diff --git a/.buildkite/bootstrap.yml b/.buildkite/bootstrap.yml new file mode 100644 index 0000000000000..b0b84616b3eb5 --- /dev/null +++ b/.buildkite/bootstrap.yml @@ -0,0 +1,31 @@ +# Uploads the latest CI workflow to Buildkite. +# https://buildkite.com/docs/pipelines/defining-steps +# +# Changes to this file must be manually edited here: +# https://buildkite.com/bun/bun/settings/steps +steps: + - if: "build.pull_request.repository.fork" + block: ":eyes:" + prompt: "Did you review the PR?" + blocked_state: "running" + + - label: ":pipeline:" + agents: + queue: "build-darwin" + command: + - ".buildkite/scripts/prepare-build.sh" + + - if: "build.branch == 'main' && !build.pull_request.repository.fork" + label: ":github:" + agents: + queue: "test-darwin" + depends_on: + - "darwin-aarch64-build-bun" + - "darwin-x64-build-bun" + - "linux-aarch64-build-bun" + - "linux-x64-build-bun" + - "linux-x64-baseline-build-bun" + - "windows-x64-build-bun" + - "windows-x64-baseline-build-bun" + command: + - ".buildkite/scripts/upload-release.sh" diff --git a/.buildkite/ci.yml b/.buildkite/ci.yml new file mode 100644 index 0000000000000..a0ab6dcf037e7 --- /dev/null +++ b/.buildkite/ci.yml @@ -0,0 +1,790 @@ +# Build and test Bun on macOS, Linux, and Windows. +# https://buildkite.com/docs/pipelines/defining-steps +# +# If a step has the `robobun: true` label, robobun will listen +# to webhooks from Buildkite and provision a VM to run the step. +# +# Changes to this file will be automatically uploaded on the next run +# for a particular commit. + +steps: + # macOS aarch64 + - key: "darwin-aarch64" + group: ":darwin: aarch64" + steps: + - key: "darwin-aarch64-build-deps" + label: ":darwin: aarch64 - build-deps" + agents: + queue: "build-darwin" + os: "darwin" + arch: "aarch64" + command: + - "bun run build:ci --target dependencies" + + - key: "darwin-aarch64-build-cpp" + label: ":darwin: aarch64 - build-cpp" + agents: + queue: "build-darwin" + os: "darwin" + arch: "aarch64" + env: + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "darwin-aarch64-build-zig" + label: ":darwin: aarch64 - build-zig" + agents: + queue: "build-zig" + command: + - "bun run build:ci --target bun-zig --toolchain darwin-aarch64" + + - key: "darwin-aarch64-build-bun" + label: ":darwin: aarch64 - build-bun" + agents: + queue: "build-darwin" + os: "darwin" + arch: "aarch64" + depends_on: + - "darwin-aarch64-build-deps" + - "darwin-aarch64-build-cpp" + - "darwin-aarch64-build-zig" + env: + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "darwin-aarch64-test-macos-14" + label: ":darwin: 14 aarch64 - test-bun" + if: "build.branch != 'main'" + parallelism: 3 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "darwin-aarch64-build-bun" + agents: + queue: "test-darwin" + os: "darwin" + arch: "aarch64" + release: "14" + command: + - "./scripts/runner.node.mjs --step darwin-aarch64-build-bun" + + - key: "darwin-aarch64-test-macos-13" + label: ":darwin: 13 aarch64 - test-bun" + if: "build.branch != 'main'" + parallelism: 3 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "darwin-aarch64-build-bun" + agents: + queue: "test-darwin" + os: "darwin" + arch: "aarch64" + release: "13" + command: + - "./scripts/runner.node.mjs --step darwin-aarch64-build-bun" + + # macOS x64 + - key: "darwin-x64" + group: ":darwin: x64" + steps: + - key: "darwin-x64-build-deps" + label: ":darwin: x64 - build-deps" + agents: + queue: "build-darwin" + os: "darwin" + arch: "x64" + command: + - "bun run build:ci --target dependencies" + + - key: "darwin-x64-build-cpp" + label: ":darwin: x64 - build-cpp" + agents: + queue: "build-darwin" + os: "darwin" + arch: "x64" + env: + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "darwin-x64-build-zig" + label: ":darwin: x64 - build-zig" + agents: + queue: "build-zig" + command: + - "bun run build:ci --target bun-zig --toolchain darwin-x64" + + - key: "darwin-x64-build-bun" + label: ":darwin: x64 - build-bun" + agents: + queue: "build-darwin" + os: "darwin" + arch: "x64" + depends_on: + - "darwin-x64-build-deps" + - "darwin-x64-build-cpp" + - "darwin-x64-build-zig" + env: + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "darwin-x64-test-macos-14" + label: ":darwin: 14 x64 - test-bun" + if: "build.branch != 'main'" + parallelism: 3 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "darwin-x64-build-bun" + agents: + queue: "test-darwin" + os: "darwin" + arch: "x64" + release: "14" + command: + - "./scripts/runner.node.mjs --step darwin-x64-build-bun" + + - key: "darwin-x64-test-macos-13" + label: ":darwin: 13 x64 - test-bun" + if: "build.branch != 'main'" + parallelism: 3 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "darwin-x64-build-bun" + agents: + queue: "test-darwin" + os: "darwin" + arch: "x64" + release: "13" + command: + - "./scripts/runner.node.mjs --step darwin-x64-build-bun" + + # Linux x64 + - key: "linux-x64" + group: ":linux: x64" + steps: + - key: "linux-x64-build-deps" + label: ":linux: x64 - build-deps" + agents: + queue: "build-linux" + os: "linux" + arch: "x64" + command: + - "bun run build:ci --target dependencies" + + - key: "linux-x64-build-cpp" + label: ":linux: x64 - build-cpp" + agents: + queue: "build-linux" + os: "linux" + arch: "x64" + env: + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "linux-x64-build-zig" + label: ":linux: x64 - build-zig" + agents: + queue: "build-zig" + command: + - "bun run build:ci --target bun-zig --toolchain linux-x64" + + - key: "linux-x64-build-bun" + label: ":linux: x64 - build-bun" + agents: + queue: "build-linux" + os: "linux" + arch: "x64" + depends_on: + - "linux-x64-build-deps" + - "linux-x64-build-cpp" + - "linux-x64-build-zig" + env: + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "linux-x64-test-debian-12" + label: ":debian: 12 x64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-x64-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "x64" + distro: "debian" + release: "12" + command: + - "./scripts/runner.node.mjs --step linux-x64-build-bun" + + - key: "linux-x64-test-ubuntu-2204" + label: ":ubuntu: 22.04 x64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-x64-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "x64" + distro: "ubuntu" + release: "22.04" + command: + - "./scripts/runner.node.mjs --step linux-x64-build-bun" + + - key: "linux-x64-test-ubuntu-2004" + label: ":ubuntu: 20.04 x64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-x64-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "x64" + distro: "ubuntu" + release: "20.04" + command: + - "./scripts/runner.node.mjs --step linux-x64-build-bun" + + # Linux x64-baseline + - key: "linux-x64-baseline" + group: ":linux: x64-baseline" + steps: + - key: "linux-x64-baseline-build-deps" + label: ":linux: x64-baseline - build-deps" + agents: + queue: "build-linux" + os: "linux" + arch: "x64" + env: + ENABLE_BASELINE: "ON" + command: + - "bun run build:ci --target dependencies" + + - key: "linux-x64-baseline-build-cpp" + label: ":linux: x64-baseline - build-cpp" + agents: + queue: "build-linux" + os: "linux" + arch: "x64" + env: + ENABLE_BASELINE: "ON" + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "linux-x64-baseline-build-zig" + label: ":linux: x64-baseline - build-zig" + agents: + queue: "build-zig" + env: + ENABLE_BASELINE: "ON" + command: + - "bun run build:ci --target bun-zig --toolchain linux-x64-baseline" + + - key: "linux-x64-baseline-build-bun" + label: ":linux: x64-baseline - build-bun" + agents: + queue: "build-linux" + os: "linux" + arch: "x64" + depends_on: + - "linux-x64-baseline-build-deps" + - "linux-x64-baseline-build-cpp" + - "linux-x64-baseline-build-zig" + env: + ENABLE_BASELINE: "ON" + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "linux-x64-baseline-test-debian-12" + label: ":debian: 12 x64-baseline - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-x64-baseline-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "x64" + distro: "debian" + release: "12" + command: + - "./scripts/runner.node.mjs --step linux-x64-baseline-build-bun" + + - key: "linux-x64-baseline-test-ubuntu-2204" + label: ":ubuntu: 22.04 x64-baseline - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-x64-baseline-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "x64" + distro: "ubuntu" + release: "22.04" + command: + - "./scripts/runner.node.mjs --step linux-x64-baseline-build-bun" + + - key: "linux-x64-baseline-test-ubuntu-2004" + label: ":ubuntu: 20.04 x64-baseline - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-x64-baseline-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "x64" + distro: "ubuntu" + release: "20.04" + command: + - "./scripts/runner.node.mjs --step linux-x64-baseline-build-bun" + + # Linux aarch64 + - key: "linux-aarch64" + group: ":linux: aarch64" + steps: + - key: "linux-aarch64-build-deps" + label: ":linux: aarch64 - build-deps" + agents: + queue: "build-linux" + os: "linux" + arch: "aarch64" + command: + - "bun run build:ci --target dependencies" + + - key: "linux-aarch64-build-cpp" + label: ":linux: aarch64 - build-cpp" + agents: + queue: "build-linux" + os: "linux" + arch: "aarch64" + env: + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "linux-aarch64-build-zig" + label: ":linux: aarch64 - build-zig" + agents: + queue: "build-zig" + command: + - "bun run build:ci --target bun-zig --toolchain linux-aarch64" + + - key: "linux-aarch64-build-bun" + label: ":linux: aarch64 - build-bun" + agents: + queue: "build-linux" + os: "linux" + arch: "aarch64" + depends_on: + - "linux-aarch64-build-deps" + - "linux-aarch64-build-cpp" + - "linux-aarch64-build-zig" + env: + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "linux-aarch64-test-debian-12" + label: ":debian: 12 aarch64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-aarch64-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "aarch64" + distro: "debian" + release: "12" + command: + - "./scripts/runner.node.mjs --step linux-aarch64-build-bun" + + - key: "linux-aarch64-test-ubuntu-2204" + label: ":ubuntu: 22.04 aarch64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-aarch64-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "aarch64" + distro: "ubuntu" + release: "22.04" + command: + - "./scripts/runner.node.mjs --step linux-aarch64-build-bun" + + - key: "linux-aarch64-test-ubuntu-2004" + label: ":ubuntu: 20.04 aarch64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 2 + retry: + automatic: + - exit_status: 1 + limit: 1 + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "linux-aarch64-build-bun" + agents: + robobun: "true" + os: "linux" + arch: "aarch64" + distro: "ubuntu" + release: "20.04" + command: + - "./scripts/runner.node.mjs --step linux-aarch64-build-bun" + + # Windows x64 + - key: "windows-x64" + group: ":windows: x64" + steps: + - key: "windows-x64-build-deps" + label: ":windows: x64 - build-deps" + agents: + queue: "build-windows" + os: "windows" + arch: "x64" + retry: + automatic: + - exit_status: 255 + limit: 5 + command: + - "bun run build:ci --target dependencies" + + - key: "windows-x64-build-cpp" + label: ":windows: x64 - build-cpp" + agents: + queue: "build-windows" + os: "windows" + arch: "x64" + retry: + automatic: + - exit_status: 255 + limit: 5 + env: + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "windows-x64-build-zig" + label: ":windows: x64 - build-zig" + agents: + queue: "build-zig" + command: + - "bun run build:ci --target bun-zig --toolchain windows-x64" + + - key: "windows-x64-build-bun" + label: ":windows: x64 - build-bun" + agents: + queue: "build-windows" + os: "windows" + arch: "x64" + depends_on: + - "windows-x64-build-deps" + - "windows-x64-build-cpp" + - "windows-x64-build-zig" + retry: + automatic: + - exit_status: 255 + limit: 5 + env: + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "windows-x64-test-bun" + label: ":windows: x64 - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 1 + retry: + automatic: + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "windows-x64-build-bun" + agents: + robobun: "true" + os: "windows" + arch: "x64" + command: + - "node .\\scripts\\runner.node.mjs --step windows-x64-build-bun" + + # Windows x64-baseline + - key: "windows-x64-baseline" + group: ":windows: x64-baseline" + steps: + - key: "windows-x64-baseline-build-deps" + label: ":windows: x64-baseline - build-deps" + agents: + queue: "build-windows" + os: "windows" + arch: "x64" + retry: + automatic: + - exit_status: 255 + limit: 5 + env: + ENABLE_BASELINE: "ON" + command: + - "bun run build:ci --target dependencies" + + - key: "windows-x64-baseline-build-cpp" + label: ":windows: x64-baseline - build-cpp" + agents: + queue: "build-windows" + os: "windows" + arch: "x64" + retry: + automatic: + - exit_status: 255 + limit: 5 + env: + ENABLE_BASELINE: "ON" + BUN_CPP_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "windows-x64-baseline-build-zig" + label: ":windows: x64-baseline - build-zig" + agents: + queue: "build-zig" + env: + ENABLE_BASELINE: "ON" + command: + - "bun run build:ci --target bun-zig --toolchain windows-x64-baseline" + + - key: "windows-x64-baseline-build-bun" + label: ":windows: x64-baseline - build-bun" + agents: + queue: "build-windows" + os: "windows" + arch: "x64" + depends_on: + - "windows-x64-baseline-build-deps" + - "windows-x64-baseline-build-cpp" + - "windows-x64-baseline-build-zig" + retry: + automatic: + - exit_status: 255 + limit: 5 + env: + ENABLE_BASELINE: "ON" + BUN_LINK_ONLY: "ON" + command: + - "bun run build:ci --target bun" + + - key: "windows-x64-baseline-test-bun" + label: ":windows: x64-baseline - test-bun" + if: "build.branch != 'main'" + parallelism: 10 + soft_fail: + - exit_status: 1 + retry: + automatic: + - exit_status: -1 + limit: 3 + - exit_status: 255 + limit: 3 + - signal_reason: agent_stop + limit: 3 + - signal: SIGTERM + limit: 3 + depends_on: + - "windows-x64-baseline-build-bun" + agents: + robobun: "true" + os: "windows" + arch: "x64" + command: + - "node .\\scripts\\runner.node.mjs --step windows-x64-baseline-build-bun" diff --git a/.buildkite/scripts/prepare-build.sh b/.buildkite/scripts/prepare-build.sh new file mode 100755 index 0000000000000..1c245d961893f --- /dev/null +++ b/.buildkite/scripts/prepare-build.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +set -eo pipefail + +function assert_build() { + if [ -z "$BUILDKITE_REPO" ]; then + echo "error: Cannot find repository for this build" + exit 1 + fi + if [ -z "$BUILDKITE_COMMIT" ]; then + echo "error: Cannot find commit for this build" + exit 1 + fi +} + +function assert_buildkite_agent() { + if ! command -v buildkite-agent &> /dev/null; then + echo "error: Cannot find buildkite-agent, please install it:" + echo "https://buildkite.com/docs/agent/v3/install" + exit 1 + fi +} + +function assert_jq() { + assert_command "jq" "jq" "https://stedolan.github.io/jq/" +} + +function assert_curl() { + assert_command "curl" "curl" "https://curl.se/download.html" +} + +function assert_command() { + local command="$1" + local package="$2" + local help_url="$3" + if ! command -v "$command" &> /dev/null; then + echo "warning: $command is not installed, installing..." + if command -v brew &> /dev/null; then + HOMEBREW_NO_AUTO_UPDATE=1 brew install "$package" + else + echo "error: Cannot install $command, please install it" + if [ -n "$help_url" ]; then + echo "" + echo "hint: See $help_url for help" + fi + exit 1 + fi + fi +} + +function assert_release() { + if [ "$RELEASE" == "1" ]; then + run_command buildkite-agent meta-data set canary "0" + fi +} + +function assert_canary() { + local canary="$(buildkite-agent meta-data get canary 2>/dev/null)" + if [ -z "$canary" ]; then + local repo=$(echo "$BUILDKITE_REPO" | sed -E 's#https://github.com/([^/]+)/([^/]+).git#\1/\2#g') + local tag="$(curl -sL "https://api.github.com/repos/$repo/releases/latest" | jq -r ".tag_name")" + if [ "$tag" == "null" ]; then + canary="1" + else + local revision=$(curl -sL "https://api.github.com/repos/$repo/compare/$tag...$BUILDKITE_COMMIT" | jq -r ".ahead_by") + if [ "$revision" == "null" ]; then + canary="1" + else + canary="$revision" + fi + fi + run_command buildkite-agent meta-data set canary "$canary" + fi +} + +function upload_buildkite_pipeline() { + local path="$1" + if [ ! -f "$path" ]; then + echo "error: Cannot find pipeline: $path" + exit 1 + fi + run_command buildkite-agent pipeline upload "$path" +} + +function run_command() { + set -x + "$@" + { set +x; } 2>/dev/null +} + +assert_build +assert_buildkite_agent +assert_jq +assert_curl +assert_release +assert_canary +upload_buildkite_pipeline ".buildkite/ci.yml" diff --git a/.buildkite/scripts/upload-release.sh b/.buildkite/scripts/upload-release.sh new file mode 100755 index 0000000000000..68b9af307cc4f --- /dev/null +++ b/.buildkite/scripts/upload-release.sh @@ -0,0 +1,220 @@ +#!/bin/bash + +set -eo pipefail + +function assert_main() { + if [ "$RELEASE" == "1" ]; then + echo "info: Skipping canary release because this is a release build" + exit 0 + fi + if [ -z "$BUILDKITE_REPO" ]; then + echo "error: Cannot find repository for this build" + exit 1 + fi + if [ -z "$BUILDKITE_COMMIT" ]; then + echo "error: Cannot find commit for this build" + exit 1 + fi + if [ -n "$BUILDKITE_PULL_REQUEST_REPO" ] && [ "$BUILDKITE_REPO" != "$BUILDKITE_PULL_REQUEST_REPO" ]; then + echo "error: Cannot upload release from a fork" + exit 1 + fi + if [ "$BUILDKITE_PULL_REQUEST" != "false" ]; then + echo "error: Cannot upload release from a pull request" + exit 1 + fi + if [ "$BUILDKITE_BRANCH" != "main" ]; then + echo "error: Cannot upload release from a branch other than main" + exit 1 + fi +} + +function assert_buildkite_agent() { + if ! command -v "buildkite-agent" &> /dev/null; then + echo "error: Cannot find buildkite-agent, please install it:" + echo "https://buildkite.com/docs/agent/v3/install" + exit 1 + fi +} + +function assert_github() { + assert_command "gh" "gh" "https://github.com/cli/cli#installation" + assert_buildkite_secret "GITHUB_TOKEN" + # gh expects the token in $GH_TOKEN + export GH_TOKEN="$GITHUB_TOKEN" +} + +function assert_aws() { + assert_command "aws" "awscli" "https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" + for secret in "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "AWS_ENDPOINT"; do + assert_buildkite_secret "$secret" + done + assert_buildkite_secret "AWS_BUCKET" --skip-redaction +} + +function assert_sentry() { + assert_command "sentry-cli" "getsentry/tools/sentry-cli" "https://docs.sentry.io/cli/installation/" + for secret in "SENTRY_AUTH_TOKEN" "SENTRY_ORG" "SENTRY_PROJECT"; do + assert_buildkite_secret "$secret" + done +} + +function run_command() { + set -x + "$@" + { set +x; } 2>/dev/null +} + +function assert_command() { + local command="$1" + local package="$2" + local help_url="$3" + if ! command -v "$command" &> /dev/null; then + echo "warning: $command is not installed, installing..." + if command -v brew &> /dev/null; then + HOMEBREW_NO_AUTO_UPDATE=1 run_command brew install "$package" + else + echo "error: Cannot install $command, please install it" + if [ -n "$help_url" ]; then + echo "" + echo "hint: See $help_url for help" + fi + exit 1 + fi + fi +} + +function assert_buildkite_secret() { + local key="$1" + local value=$(buildkite-agent secret get "$key" ${@:2}) + if [ -z "$value" ]; then + echo "error: Cannot find $key secret" + echo "" + echo "hint: Create a secret named $key with a value:" + echo "https://buildkite.com/docs/pipelines/buildkite-secrets" + exit 1 + fi + export "$key"="$value" +} + +function release_tag() { + local version="$1" + if [ "$version" == "canary" ]; then + echo "canary" + else + echo "bun-v$version" + fi +} + +function create_sentry_release() { + local version="$1" + local release="$version" + if [ "$version" == "canary" ]; then + release="$BUILDKITE_COMMIT-canary" + fi + run_command sentry-cli releases new "$release" --finalize + run_command sentry-cli releases set-commits "$release" --auto --ignore-missing + if [ "$version" == "canary" ]; then + run_command sentry-cli deploys new --env="canary" --release="$release" + fi +} + +function download_buildkite_artifact() { + local name="$1" + local dir="$2" + if [ -z "$dir" ]; then + dir="." + fi + run_command buildkite-agent artifact download "$name" "$dir" + if [ ! -f "$dir/$name" ]; then + echo "error: Cannot find Buildkite artifact: $name" + exit 1 + fi +} + +function upload_github_asset() { + local version="$1" + local tag="$(release_tag "$version")" + local file="$2" + run_command gh release upload "$tag" "$file" --clobber --repo "$BUILDKITE_REPO" + + # Sometimes the upload fails, maybe this is a race condition in the gh CLI? + while [ "$(gh release view "$tag" --repo "$BUILDKITE_REPO" | grep -c "$file")" -eq 0 ]; do + echo "warn: Uploading $file to $tag failed, retrying..." + sleep "$((RANDOM % 5 + 1))" + run_command gh release upload "$tag" "$file" --clobber --repo "$BUILDKITE_REPO" + done +} + +function update_github_release() { + local version="$1" + local tag="$(release_tag "$version")" + if [ "$tag" == "canary" ]; then + sleep 5 # There is possibly a race condition where this overwrites artifacts? + run_command gh release edit "$tag" --repo "$BUILDKITE_REPO" \ + --notes "This release of Bun corresponds to the commit: $BUILDKITE_COMMIT" + fi +} + +function upload_s3_file() { + local folder="$1" + local file="$2" + run_command aws --endpoint-url="$AWS_ENDPOINT" s3 cp "$file" "s3://$AWS_BUCKET/$folder/$file" +} + +function create_release() { + assert_main + assert_buildkite_agent + assert_github + assert_aws + assert_sentry + + local tag="$1" # 'canary' or 'x.y.z' + local artifacts=( + bun-darwin-aarch64.zip + bun-darwin-aarch64-profile.zip + bun-darwin-x64.zip + bun-darwin-x64-profile.zip + bun-linux-aarch64.zip + bun-linux-aarch64-profile.zip + bun-linux-x64.zip + bun-linux-x64-profile.zip + bun-linux-x64-baseline.zip + bun-linux-x64-baseline-profile.zip + bun-windows-x64.zip + bun-windows-x64-profile.zip + bun-windows-x64-baseline.zip + bun-windows-x64-baseline-profile.zip + ) + + function upload_artifact() { + local artifact="$1" + download_buildkite_artifact "$artifact" + if [ "$tag" == "canary" ]; then + upload_s3_file "releases/$BUILDKITE_COMMIT-canary" "$artifact" & + else + upload_s3_file "releases/$BUILDKITE_COMMIT" "$artifact" & + fi + upload_s3_file "releases/$tag" "$artifact" & + upload_github_asset "$tag" "$artifact" & + wait + } + + for artifact in "${artifacts[@]}"; do + upload_artifact "$artifact" + done + + update_github_release "$tag" + create_sentry_release "$tag" +} + +function assert_canary() { + local canary="$(buildkite-agent meta-data get canary 2>/dev/null)" + if [ -z "$canary" ] || [ "$canary" == "0" ]; then + echo "warn: Skipping release because this is not a canary build" + exit 0 + fi +} + +assert_canary +create_release "canary" diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000000..56bea1f5880f6 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,9 @@ +WarningsAsErrors: "*" +FormatStyle: webkit +Checks: > + -*, + clang-analyzer-*, + -clang-analyzer-optin.core.EnumCastOutOfRange + -clang-analyzer-webkit.UncountedLambdaCapturesChecker + -clang-analyzer-optin.core.EnumCastOutOfRange + -clang-analyzer-webkit.RefCntblBaseVirtualDtor diff --git a/.clangd b/.clangd index 35856fb41412f..f736d521d09b2 100644 --- a/.clangd +++ b/.clangd @@ -1,3 +1,5 @@ Index: Background: Skip # Disable slow background indexing of these files. +CompileFlags: + CompilationDatabase: build/debug diff --git a/.gitattributes b/.gitattributes index 6c3caa3fe5c33..7f44299cfc260 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,6 +7,7 @@ *.cpp text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 *.cc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 *.yml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.toml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 *.zig text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 *.rs text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 *.h text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 @@ -43,5 +44,8 @@ test/**/* linguist-documentation bench/**/* linguist-documentation examples/**/* linguist-documentation -src/deps/*.c linguist-vendored -src/deps/brotli/** linguist-vendored +vendor/*.c linguist-vendored +vendor/brotli/** linguist-vendored + +test/js/node/test/fixtures linguist-vendored +test/js/node/test/common linguist-vendored diff --git a/.github/ISSUE_TEMPLATE/7-install-crash-report.yml b/.github/ISSUE_TEMPLATE/7-install-crash-report.yml index 9239188ca098d..2e39becab0250 100644 --- a/.github/ISSUE_TEMPLATE/7-install-crash-report.yml +++ b/.github/ISSUE_TEMPLATE/7-install-crash-report.yml @@ -2,11 +2,18 @@ name: bun install crash report description: Report a crash in bun install labels: - npm + - crash body: - type: markdown attributes: value: | **Thank you so much** for submitting a crash report. You're helping us make Bun more reliable for everyone! + - type: textarea + id: package_json + attributes: + label: `package.json` file + description: Can you upload your `package.json` file? This helps us reproduce the crash. + render: json - type: textarea id: repro attributes: diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml index 9d04e28cc4d68..25424ce6669c2 100644 --- a/.github/actions/setup-bun/action.yml +++ b/.github/actions/setup-bun/action.yml @@ -42,7 +42,7 @@ runs: canary) release="canary";; *) release="bun-v${{ inputs.bun-version }}";; esac - curl -LO "${{ inputs.download-url }}/${release}/${target}.zip" + curl -LO "${{ inputs.download-url }}/${release}/${target}.zip" --retry 5 unzip ${target}.zip mkdir -p ${{ runner.temp }}/.bun/bin mv ${target}/bun* ${{ runner.temp }}/.bun/bin/ diff --git a/.github/workflows/build-darwin.yml b/.github/workflows/build-darwin.yml deleted file mode 100644 index 28de5ceb5991d..0000000000000 --- a/.github/workflows/build-darwin.yml +++ /dev/null @@ -1,312 +0,0 @@ -name: Build Darwin - -permissions: - contents: read - actions: write - -on: - workflow_call: - inputs: - runs-on: - type: string - default: macos-12-large - tag: - type: string - required: true - arch: - type: string - required: true - cpu: - type: string - required: true - assertions: - type: boolean - canary: - type: boolean - no-cache: - type: boolean - -env: - LLVM_VERSION: 16 - BUN_VERSION: 1.1.8 - LC_CTYPE: "en_US.UTF-8" - LC_ALL: "en_US.UTF-8" - -jobs: - build-submodules: - name: Build Submodules - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: | - .gitmodules - src/deps - scripts - - name: Hash Submodules - id: hash - run: | - print_versions() { - git submodule | grep -v WebKit - echo "LLVM_VERSION=${{ env.LLVM_VERSION }}" - cat $(echo scripts/build*.sh scripts/all-dependencies.sh | tr " " "\n" | sort) - } - echo "hash=$(print_versions | shasum)" >> $GITHUB_OUTPUT - - if: ${{ !inputs.no-cache }} - name: Restore Cache - id: cache - uses: actions/cache/restore@v4 - with: - path: ${{ runner.temp }}/bun-deps - key: bun-${{ inputs.tag }}-deps-${{ steps.hash.outputs.hash }} - # TODO: Figure out how to cache homebrew dependencies - - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - name: Install Dependencies - env: - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - run: | - brew install \ - llvm@${{ env.LLVM_VERSION }} \ - ccache \ - rust \ - pkg-config \ - coreutils \ - libtool \ - cmake \ - libiconv \ - automake \ - openssl@1.1 \ - ninja \ - golang \ - gnu-sed --force --overwrite - echo "$(brew --prefix ccache)/bin" >> $GITHUB_PATH - echo "$(brew --prefix coreutils)/libexec/gnubin" >> $GITHUB_PATH - echo "$(brew --prefix llvm@$LLVM_VERSION)/bin" >> $GITHUB_PATH - brew link --overwrite llvm@$LLVM_VERSION - - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - name: Clone Submodules - run: | - ./scripts/update-submodules.sh - - name: Build Submodules - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - env: - CPU_TARGET: ${{ inputs.cpu }} - BUN_DEPS_OUT_DIR: ${{ runner.temp }}/bun-deps - run: | - mkdir -p $BUN_DEPS_OUT_DIR - ./scripts/all-dependencies.sh - - name: Save Cache - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - uses: actions/cache/save@v4 - with: - path: ${{ runner.temp }}/bun-deps - key: ${{ steps.cache.outputs.cache-primary-key }} - - name: Upload bun-${{ inputs.tag }}-deps - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-deps - path: ${{ runner.temp }}/bun-deps - if-no-files-found: error - build-cpp: - name: Build C++ - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - # TODO: Figure out how to cache homebrew dependencies - - name: Install Dependencies - env: - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - run: | - brew install \ - llvm@${{ env.LLVM_VERSION }} \ - ccache \ - rust \ - pkg-config \ - coreutils \ - libtool \ - cmake \ - libiconv \ - automake \ - openssl@1.1 \ - ninja \ - golang \ - gnu-sed --force --overwrite - echo "$(brew --prefix ccache)/bin" >> $GITHUB_PATH - echo "$(brew --prefix coreutils)/libexec/gnubin" >> $GITHUB_PATH - echo "$(brew --prefix llvm@$LLVM_VERSION)/bin" >> $GITHUB_PATH - brew link --overwrite llvm@$LLVM_VERSION - - name: Setup Bun - uses: ./.github/actions/setup-bun - with: - bun-version: ${{ env.BUN_VERSION }} - - if: ${{ !inputs.no-cache }} - name: Restore Cache - uses: actions/cache@v4 - with: - path: ${{ runner.temp }}/ccache - key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }} - restore-keys: | - bun-${{ inputs.tag }}-cpp- - - name: Compile - env: - CPU_TARGET: ${{ inputs.cpu }} - SOURCE_DIR: ${{ github.workspace }} - OBJ_DIR: ${{ runner.temp }}/bun-cpp-obj - BUN_DEPS_OUT_DIR: ${{ runner.temp }}/bun-deps - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p $OBJ_DIR - cd $OBJ_DIR - cmake -S $SOURCE_DIR -B $OBJ_DIR \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DUSE_LTO=ON \ - -DBUN_CPP_ONLY=1 \ - -DNO_CONFIGURE_DEPENDS=1 - chmod +x compile-cpp-only.sh - ./compile-cpp-only.sh -v - - name: Upload bun-${{ inputs.tag }}-cpp - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-cpp - path: ${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a - if-no-files-found: error - build-zig: - name: Build Zig - uses: ./.github/workflows/build-zig.yml - with: - os: darwin - only-zig: true - tag: ${{ inputs.tag }} - arch: ${{ inputs.arch }} - cpu: ${{ inputs.cpu }} - assertions: ${{ inputs.assertions }} - canary: ${{ inputs.canary }} - no-cache: ${{ inputs.no-cache }} - link: - name: Link - runs-on: ${{ inputs.runs-on }} - needs: - - build-submodules - - build-cpp - - build-zig - steps: - - uses: actions/checkout@v4 - # TODO: Figure out how to cache homebrew dependencies - - name: Install Dependencies - env: - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - run: | - brew install \ - llvm@${{ env.LLVM_VERSION }} \ - ccache \ - rust \ - pkg-config \ - coreutils \ - libtool \ - cmake \ - libiconv \ - automake \ - openssl@1.1 \ - ninja \ - golang \ - gnu-sed --force --overwrite - echo "$(brew --prefix ccache)/bin" >> $GITHUB_PATH - echo "$(brew --prefix coreutils)/libexec/gnubin" >> $GITHUB_PATH - echo "$(brew --prefix llvm@$LLVM_VERSION)/bin" >> $GITHUB_PATH - brew link --overwrite llvm@$LLVM_VERSION - - name: Setup Bun - uses: ./.github/actions/setup-bun - with: - bun-version: ${{ env.BUN_VERSION }} - - name: Download bun-${{ inputs.tag }}-deps - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-deps - path: ${{ runner.temp }}/bun-deps - - name: Download bun-${{ inputs.tag }}-cpp - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-cpp - path: ${{ runner.temp }}/bun-cpp-obj - - name: Download bun-${{ inputs.tag }}-zig - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-zig - path: ${{ runner.temp }}/release - - if: ${{ !inputs.no-cache }} - name: Restore Cache - uses: actions/cache@v4 - with: - path: ${{ runner.temp }}/ccache - key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }} - restore-keys: | - bun-${{ inputs.tag }}-cpp- - - name: Link - env: - CPU_TARGET: ${{ inputs.cpu }} - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - SRC_DIR=$PWD - mkdir ${{ runner.temp }}/link-build - cd ${{ runner.temp }}/link-build - cmake $SRC_DIR \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DUSE_LTO=ON \ - -DBUN_LINK_ONLY=1 \ - -DBUN_ZIG_OBJ_DIR="${{ runner.temp }}/release" \ - -DBUN_CPP_ARCHIVE="${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a" \ - -DBUN_DEPS_OUT_DIR="${{ runner.temp }}/bun-deps" \ - -DNO_CONFIGURE_DEPENDS=1 - ninja -v - - name: Prepare - run: | - cd ${{ runner.temp }}/link-build - chmod +x bun-profile bun - mkdir -p bun-${{ inputs.tag }}-profile/ bun-${{ inputs.tag }}/ - mv bun-profile bun-${{ inputs.tag }}-profile/bun-profile - mv bun bun-${{ inputs.tag }}/bun - zip -r bun-${{ inputs.tag }}-profile.zip bun-${{ inputs.tag }}-profile - zip -r bun-${{ inputs.tag }}.zip bun-${{ inputs.tag }} - - name: Upload bun-${{ inputs.tag }} - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }} - path: ${{ runner.temp }}/link-build/bun-${{ inputs.tag }}.zip - if-no-files-found: error - - name: Upload bun-${{ inputs.tag }}-profile - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-profile - path: ${{ runner.temp }}/link-build/bun-${{ inputs.tag }}-profile.zip - if-no-files-found: error - on-failure: - if: ${{ github.repository_owner == 'oven-sh' && failure() }} - name: On Failure - needs: link - runs-on: ubuntu-latest - steps: - - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - nodetail: true - color: "#FF0000" - title: "" - description: | - ### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) - - @${{ github.actor }}, the build for bun-${{ inputs.tag }} failed. - - **[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml deleted file mode 100644 index c1bde9271cbdc..0000000000000 --- a/.github/workflows/build-linux.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Build Linux - -permissions: - contents: read - actions: write - -on: - workflow_call: - inputs: - runs-on: - type: string - required: true - tag: - type: string - required: true - arch: - type: string - required: true - cpu: - type: string - required: true - assertions: - type: boolean - zig-optimize: - type: string - canary: - type: boolean - no-cache: - type: boolean - -jobs: - build: - name: Build Linux - uses: ./.github/workflows/build-zig.yml - with: - os: linux - only-zig: false - runs-on: ${{ inputs.runs-on }} - tag: ${{ inputs.tag }} - arch: ${{ inputs.arch }} - cpu: ${{ inputs.cpu }} - assertions: ${{ inputs.assertions }} - zig-optimize: ${{ inputs.zig-optimize }} - canary: ${{ inputs.canary }} - no-cache: ${{ inputs.no-cache }} - on-failure: - if: ${{ github.repository_owner == 'oven-sh' && failure() }} - name: On Failure - needs: build - runs-on: ubuntu-latest - steps: - - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - nodetail: true - color: "#FF0000" - title: "" - description: | - ### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) - - @${{ github.actor }}, the build for bun-${{ inputs.tag }} failed. - - **[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml deleted file mode 100644 index 3ea173ea0fd80..0000000000000 --- a/.github/workflows/build-windows.yml +++ /dev/null @@ -1,344 +0,0 @@ -name: Build Windows - -permissions: - contents: read - actions: write - -on: - workflow_call: - inputs: - runs-on: - type: string - default: windows - tag: - type: string - required: true - arch: - type: string - required: true - cpu: - type: string - required: true - assertions: - type: boolean - canary: - type: boolean - no-cache: - type: boolean - bun-version: - type: string - default: 1.1.7 - -env: - # Must specify exact version of LLVM for Windows - LLVM_VERSION: 16.0.6 - BUN_VERSION: ${{ inputs.bun-version }} - BUN_GARBAGE_COLLECTOR_LEVEL: 1 - BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: 1 - CI: true - -jobs: - build-submodules: - name: Build Submodules - runs-on: ${{ inputs.runs-on }} - steps: - - name: Install VS2022 BuildTools 17.9.7 - run: choco install -y visualstudio2022buildtools --version=117.9.7.0 --params "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --installChannelUri https://aka.ms/vs/17/release/180911598_-255012421/channel" - - name: Setup Git - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: | - .gitmodules - src/deps - scripts - - name: Hash Submodules - id: hash - run: | - $data = "$(& { - git submodule | Where-Object { $_ -notmatch 'WebKit' } - echo "LLVM_VERSION=${{ env.LLVM_VERSION }}" - Get-Content -Path (Get-ChildItem -Path 'scripts/build*.ps1', 'scripts/all-dependencies.ps1', 'scripts/env.ps1' | Sort-Object -Property Name).FullName | Out-String - echo 1 - })" - $hash = ( -join ((New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($data)) | ForEach-Object { $_.ToString("x2") } )).Substring(0, 10) - echo "hash=${hash}" >> $env:GITHUB_OUTPUT - - if: ${{ !inputs.no-cache }} - name: Restore Cache - id: cache - uses: actions/cache/restore@v4 - with: - path: bun-deps - key: bun-${{ inputs.tag }}-deps-${{ steps.hash.outputs.hash }} - - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - name: Install LLVM - uses: KyleMayes/install-llvm-action@8b37482c5a2997a3ab5dbf6561f8109e2eaa7d3b - with: - version: ${{ env.LLVM_VERSION }} - - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - name: Install Ninja - run: | - choco install -y ninja - - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - name: Clone Submodules - run: | - .\scripts\update-submodules.ps1 - - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - name: Build Dependencies - env: - CPU_TARGET: ${{ inputs.cpu }} - CCACHE_DIR: ccache - run: | - .\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }} - choco install -y nasm --version=2.16.01 - $env:BUN_DEPS_OUT_DIR = (mkdir -Force "./bun-deps") - .\scripts\all-dependencies.ps1 - - name: Save Cache - if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }} - uses: actions/cache/save@v4 - with: - path: bun-deps - key: ${{ steps.cache.outputs.cache-primary-key }} - - name: Upload bun-${{ inputs.tag }}-deps - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-deps - path: bun-deps - if-no-files-found: error - codegen: - name: Codegen - runs-on: ubuntu-latest - steps: - - name: Setup Git - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Bun - uses: ./.github/actions/setup-bun - with: - bun-version: ${{ inputs.bun-version }} - - name: Codegen - run: | - ./scripts/cross-compile-codegen.sh win32 x64 - - if: ${{ inputs.canary }} - name: Calculate Revision - run: | - echo "canary_revision=$(GITHUB_TOKEN="${{ github.token }}" - bash ./scripts/calculate-canary-revision.sh --raw)" > build-codegen-win32-x64/.canary_revision - - name: Upload bun-${{ inputs.tag }}-codegen - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-codegen - path: build-codegen-win32-x64 - if-no-files-found: error - build-cpp: - name: Build C++ - needs: codegen - runs-on: ${{ inputs.runs-on }} - steps: - - name: Install VS2022 BuildTools 17.9.7 - run: choco install -y visualstudio2022buildtools --version=117.9.7.0 --params "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --installChannelUri https://aka.ms/vs/17/release/180911598_-255012421/channel" - - name: Setup Git - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install LLVM - uses: KyleMayes/install-llvm-action@8b37482c5a2997a3ab5dbf6561f8109e2eaa7d3b - with: - version: ${{ env.LLVM_VERSION }} - - name: Install Ninja - run: | - choco install -y ninja - - name: Setup Bun - uses: ./.github/actions/setup-bun - with: - bun-version: ${{ inputs.bun-version }} - - if: ${{ !inputs.no-cache }} - name: Restore Cache - uses: actions/cache@v4 - with: - path: ccache - key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }} - restore-keys: | - bun-${{ inputs.tag }}-cpp- - - name: Download bun-${{ inputs.tag }}-codegen - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-codegen - path: build - - name: Compile - env: - CPU_TARGET: ${{ inputs.cpu }} - CCACHE_DIR: ccache - run: | - # $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" } - $CANARY_REVISION = 0 - .\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }} - .\scripts\update-submodules.ps1 - .\scripts\build-libuv.ps1 -CloneOnly $True - cd build - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release ` - -DNO_CODEGEN=1 ` - -DNO_CONFIGURE_DEPENDS=1 ` - "-DCANARY=${CANARY_REVISION}" ` - -DBUN_CPP_ONLY=1 ${{ contains(inputs.tag, '-baseline') && '-DUSE_BASELINE_BUILD=1' || '' }} - if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" } - .\compile-cpp-only.ps1 -v - if ($LASTEXITCODE -ne 0) { throw "C++ compilation failed" } - - name: Upload bun-${{ inputs.tag }}-cpp - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-cpp - path: build/bun-cpp-objects.a - if-no-files-found: error - build-zig: - name: Build Zig - uses: ./.github/workflows/build-zig.yml - with: - os: windows - zig-optimize: ReleaseSafe - only-zig: true - tag: ${{ inputs.tag }} - arch: ${{ inputs.arch }} - cpu: ${{ inputs.cpu }} - assertions: ${{ inputs.assertions }} - canary: ${{ inputs.canary }} - no-cache: ${{ inputs.no-cache }} - link: - name: Link - runs-on: ${{ inputs.runs-on }} - needs: - - build-submodules - - build-cpp - - build-zig - - codegen - steps: - - name: Install VS2022 BuildTools 17.9.7 - run: choco install -y visualstudio2022buildtools --version=117.9.7.0 --params "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --installChannelUri https://aka.ms/vs/17/release/180911598_-255012421/channel" - - name: Setup Git - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install LLVM - uses: KyleMayes/install-llvm-action@8b37482c5a2997a3ab5dbf6561f8109e2eaa7d3b - with: - version: ${{ env.LLVM_VERSION }} - - name: Install Ninja - run: | - choco install -y ninja - - name: Setup Bun - uses: ./.github/actions/setup-bun - with: - bun-version: ${{ inputs.bun-version }} - - name: Download bun-${{ inputs.tag }}-deps - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-deps - path: bun-deps - - name: Download bun-${{ inputs.tag }}-cpp - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-cpp - path: bun-cpp - - name: Download bun-${{ inputs.tag }}-zig - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-zig - path: bun-zig - - name: Download bun-${{ inputs.tag }}-codegen - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }}-codegen - path: build - - if: ${{ !inputs.no-cache }} - name: Restore Cache - uses: actions/cache@v4 - with: - path: ccache - key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }} - restore-keys: | - bun-${{ inputs.tag }}-cpp- - - name: Link - env: - CPU_TARGET: ${{ inputs.cpu }} - CCACHE_DIR: ccache - run: | - .\scripts\update-submodules.ps1 - .\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }} - Set-Location build - # $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" } - $CANARY_REVISION = 0 - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release ` - -DNO_CODEGEN=1 ` - -DNO_CONFIGURE_DEPENDS=1 ` - "-DCANARY=${CANARY_REVISION}" ` - -DBUN_LINK_ONLY=1 ` - "-DBUN_DEPS_OUT_DIR=$(Resolve-Path ../bun-deps)" ` - "-DBUN_CPP_ARCHIVE=$(Resolve-Path ../bun-cpp/bun-cpp-objects.a)" ` - "-DBUN_ZIG_OBJ_DIR=$(Resolve-Path ../bun-zig)" ` - ${{ contains(inputs.tag, '-baseline') && '-DUSE_BASELINE_BUILD=1' || '' }} - if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" } - ninja -v - if ($LASTEXITCODE -ne 0) { throw "Link failed!" } - - name: Prepare - run: | - $Dist = mkdir -Force "bun-${{ inputs.tag }}" - cp -r build\bun.exe "$Dist\bun.exe" - Compress-Archive -Force "$Dist" "${Dist}.zip" - $Dist = "$Dist-profile" - MkDir -Force "$Dist" - cp -r build\bun.exe "$Dist\bun.exe" - cp -r build\bun.pdb "$Dist\bun.pdb" - Compress-Archive -Force "$Dist" "$Dist.zip" - .\build\bun.exe --print "JSON.stringify(require('bun:internal-for-testing').crash_handler.getFeatureData())" > .\features.json - - name: Upload bun-${{ inputs.tag }} - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }} - path: bun-${{ inputs.tag }}.zip - if-no-files-found: error - - name: Upload bun-${{ inputs.tag }}-profile - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-profile - path: bun-${{ inputs.tag }}-profile.zip - if-no-files-found: error - - name: Upload bun-feature-data - uses: actions/upload-artifact@v4 - with: - name: bun-feature-data - path: features.json - if-no-files-found: error - overwrite: true - on-failure: - if: ${{ github.repository_owner == 'oven-sh' && failure() }} - name: On Failure - needs: link - runs-on: ubuntu-latest - steps: - - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - nodetail: true - color: "#FF0000" - title: "" - description: | - ### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) - - @${{ github.actor }}, the build for bun-${{ inputs.tag }} failed. - - **[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** diff --git a/.github/workflows/build-zig.yml b/.github/workflows/build-zig.yml deleted file mode 100644 index 097fe082e70ba..0000000000000 --- a/.github/workflows/build-zig.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Build Zig - -permissions: - contents: read - actions: write - -on: - workflow_call: - inputs: - runs-on: - type: string - default: ${{ github.repository_owner != 'oven-sh' && 'ubuntu-latest' || inputs.only-zig && 'namespace-profile-bun-ci-linux-x64' || inputs.arch == 'x64' && 'namespace-profile-bun-ci-linux-x64' || 'namespace-profile-bun-ci-linux-aarch64' }} - tag: - type: string - required: true - os: - type: string - required: true - arch: - type: string - required: true - cpu: - type: string - required: true - assertions: - type: boolean - default: false - zig-optimize: - type: string # 'ReleaseSafe' or 'ReleaseFast' - default: ReleaseFast - canary: - type: boolean - default: ${{ github.ref == 'refs/heads/main' }} - only-zig: - type: boolean - default: true - no-cache: - type: boolean - default: false - -jobs: - build-zig: - name: ${{ inputs.only-zig && 'Build Zig' || 'Build & Link' }} - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Calculate Cache Key - id: cache - run: | - echo "key=${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }}" >> $GITHUB_OUTPUT - - if: ${{ !inputs.no-cache }} - name: Restore Cache - uses: actions/cache@v4 - with: - key: bun-${{ inputs.tag }}-docker-${{ steps.cache.outputs.key }} - restore-keys: | - bun-${{ inputs.tag }}-docker- - path: | - ${{ runner.temp }}/dockercache - - name: Setup Docker - uses: docker/setup-buildx-action@v3 - with: - install: true - platforms: | - linux/${{ runner.arch == 'X64' && 'amd64' || 'arm64' }} - - name: Build - uses: docker/build-push-action@v5 - with: - push: false - target: ${{ inputs.only-zig && 'build_release_obj' || 'artifact' }} - cache-from: | - type=local,src=${{ runner.temp }}/dockercache - cache-to: | - type=local,dest=${{ runner.temp }}/dockercache,mode=max - outputs: | - type=local,dest=${{ runner.temp }}/release - platforms: | - linux/${{ runner.arch == 'X64' && 'amd64' || 'arm64' }} - build-args: | - GIT_SHA=${{ github.event.workflow_run.head_sha || github.sha }} - TRIPLET=${{ inputs.os == 'darwin' && format('{0}-macos-none', inputs.arch == 'x64' && 'x86_64' || 'aarch64') || inputs.os == 'windows' && format('{0}-windows-msvc', inputs.arch == 'x64' && 'x86_64' || 'aarch64') || format('{0}-linux-gnu', inputs.arch == 'x64' && 'x86_64' || 'aarch64') }} - ARCH=${{ inputs.arch == 'x64' && 'x86_64' || 'aarch64' }} - BUILDARCH=${{ inputs.arch == 'x64' && 'amd64' || 'arm64' }} - BUILD_MACHINE_ARCH=${{ inputs.arch == 'x64' && 'x86_64' || 'aarch64' }} - CPU_TARGET=${{ inputs.arch == 'x64' && inputs.cpu || 'native' }} - ASSERTIONS=${{ inputs.assertions && 'ON' || 'OFF' }} - ZIG_OPTIMIZE=${{ inputs.zig-optimize }} - CANARY=${{ inputs.canary && '1' || '0' }} - - if: ${{ inputs.only-zig }} - name: Upload bun-${{ inputs.tag }}-zig - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-zig - path: ${{ runner.temp }}/release/bun-zig.o - if-no-files-found: error - - if: ${{ !inputs.only-zig }} - name: Prepare - run: | - cd ${{ runner.temp }}/release - chmod +x bun-profile bun - mkdir bun-${{ inputs.tag }}-profile - mkdir bun-${{ inputs.tag }} - strip bun - mv bun-profile bun-${{ inputs.tag }}-profile/bun-profile - mv bun bun-${{ inputs.tag }}/bun - zip -r bun-${{ inputs.tag }}-profile.zip bun-${{ inputs.tag }}-profile - zip -r bun-${{ inputs.tag }}.zip bun-${{ inputs.tag }} - - if: ${{ !inputs.only-zig }} - name: Upload bun-${{ inputs.tag }} - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }} - path: ${{ runner.temp }}/release/bun-${{ inputs.tag }}.zip - if-no-files-found: error - - if: ${{ !inputs.only-zig }} - name: Upload bun-${{ inputs.tag }}-profile - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-profile - path: ${{ runner.temp }}/release/bun-${{ inputs.tag }}-profile.zip - if-no-files-found: error diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index e7acf376821e2..0000000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,245 +0,0 @@ -name: CI - -permissions: - contents: read - actions: write - -concurrency: - group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && inputs.run-id || github.ref }} - cancel-in-progress: true - -on: - workflow_dispatch: - inputs: - run-id: - type: string - description: The workflow ID to download artifacts (skips the build step) - pull_request: - paths-ignore: - - .vscode/**/* - - docs/**/* - - examples/**/* - push: - branches: - - main - paths-ignore: - - .vscode/**/* - - docs/**/* - - examples/**/* - -jobs: - format: - if: ${{ !inputs.run-id }} - name: Format - uses: ./.github/workflows/run-format.yml - secrets: inherit - with: - zig-version: 0.13.0 - permissions: - contents: write - lint: - if: ${{ !inputs.run-id }} - name: Lint - uses: ./.github/workflows/run-lint.yml - secrets: inherit - linux-x64: - if: ${{ !inputs.run-id }} - name: Build linux-x64 - uses: ./.github/workflows/build-linux.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }} - tag: linux-x64 - arch: x64 - cpu: haswell - canary: true - no-cache: true - linux-x64-baseline: - if: ${{ !inputs.run-id }} - name: Build linux-x64-baseline - uses: ./.github/workflows/build-linux.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }} - tag: linux-x64-baseline - arch: x64 - cpu: nehalem - canary: true - no-cache: true - linux-aarch64: - if: ${{ !inputs.run-id && github.repository_owner == 'oven-sh' }} - name: Build linux-aarch64 - uses: ./.github/workflows/build-linux.yml - secrets: inherit - with: - runs-on: namespace-profile-bun-ci-linux-aarch64 - tag: linux-aarch64 - arch: aarch64 - cpu: native - canary: true - no-cache: true - darwin-x64: - if: ${{ !inputs.run-id }} - name: Build darwin-x64 - uses: ./.github/workflows/build-darwin.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }} - tag: darwin-x64 - arch: x64 - cpu: haswell - canary: true - darwin-x64-baseline: - if: ${{ !inputs.run-id }} - name: Build darwin-x64-baseline - uses: ./.github/workflows/build-darwin.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }} - tag: darwin-x64-baseline - arch: x64 - cpu: nehalem - canary: true - darwin-aarch64: - if: ${{ !inputs.run-id }} - name: Build darwin-aarch64 - uses: ./.github/workflows/build-darwin.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-darwin-aarch64' || 'macos-12' }} - tag: darwin-aarch64 - arch: aarch64 - cpu: native - canary: true - windows-x64: - if: ${{ !inputs.run-id }} - name: Build windows-x64 - uses: ./.github/workflows/build-windows.yml - secrets: inherit - with: - runs-on: windows - tag: windows-x64 - arch: x64 - cpu: haswell - canary: true - windows-x64-baseline: - if: ${{ !inputs.run-id }} - name: Build windows-x64-baseline - uses: ./.github/workflows/build-windows.yml - secrets: inherit - with: - runs-on: windows - tag: windows-x64-baseline - arch: x64 - cpu: nehalem - canary: true - linux-x64-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test linux-x64 - needs: linux-x64 - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }} - tag: linux-x64 - linux-x64-baseline-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test linux-x64-baseline - needs: linux-x64-baseline - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }} - tag: linux-x64-baseline - linux-aarch64-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' && github.repository_owner == 'oven-sh'}} - name: Test linux-aarch64 - needs: linux-aarch64 - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: namespace-profile-bun-ci-linux-aarch64 - tag: linux-aarch64 - darwin-x64-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test darwin-x64 - needs: darwin-x64 - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }} - tag: darwin-x64 - darwin-x64-baseline-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test darwin-x64-baseline - needs: darwin-x64-baseline - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }} - tag: darwin-x64-baseline - darwin-aarch64-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test darwin-aarch64 - needs: darwin-aarch64 - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-darwin-aarch64' || 'macos-12' }} - tag: darwin-aarch64 - windows-x64-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test windows-x64 - needs: windows-x64 - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: windows - tag: windows-x64 - windows-x64-baseline-test: - if: ${{ inputs.run-id || github.event_name == 'pull_request' }} - name: Test windows-x64-baseline - needs: windows-x64-baseline - uses: ./.github/workflows/run-test.yml - secrets: inherit - with: - run-id: ${{ inputs.run-id }} - pr-number: ${{ github.event.number }} - runs-on: windows - tag: windows-x64-baseline - cleanup: - if: ${{ always() }} - name: Cleanup - needs: - - linux-x64 - - linux-x64-baseline - - linux-aarch64 - - darwin-x64 - - darwin-x64-baseline - - darwin-aarch64 - - windows-x64 - - windows-x64-baseline - runs-on: ubuntu-latest - steps: - - name: Cleanup Artifacts - uses: geekyeggo/delete-artifact@v5 - with: - name: | - bun-*-cpp - bun-*-zig - bun-*-deps - bun-*-codegen diff --git a/.github/workflows/comment.yml b/.github/workflows/comment.yml deleted file mode 100644 index 3c798e8fcc041..0000000000000 --- a/.github/workflows/comment.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Comment - -permissions: - actions: read - pull-requests: write - -on: - workflow_run: - workflows: - - CI - types: - - completed - -jobs: - comment: - if: ${{ github.repository_owner == 'oven-sh' }} - name: Comment - runs-on: ubuntu-latest - steps: - - name: Download Tests - uses: actions/download-artifact@v4 - with: - path: bun - pattern: bun-*-tests - github-token: ${{ github.token }} - run-id: ${{ github.event.workflow_run.id }} - - name: Setup Environment - id: env - shell: bash - run: | - echo "pr-number=$(> $GITHUB_OUTPUT - - name: Generate Comment - run: | - cat bun/bun-*-tests/comment.md > comment.md - if [ -s comment.md ]; then - echo -e "❌ @${{ github.actor }}, your commit has failing tests :(\n\n$(cat comment.md)" > comment.md - else - echo -e "✅ @${{ github.actor }}, all tests passed!" > comment.md - fi - echo -e "\n**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }})**" >> comment.md - echo -e "" >> comment.md - - name: Find Comment - id: comment - uses: peter-evans/find-comment@v3 - with: - issue-number: ${{ steps.env.outputs.pr-number }} - comment-author: github-actions[bot] - body-includes: - - name: Write Comment - uses: peter-evans/create-or-update-comment@v4 - with: - comment-id: ${{ steps.comment.outputs.comment-id }} - issue-number: ${{ steps.env.outputs.pr-number }} - body-path: comment.md - edit-mode: replace diff --git a/.github/workflows/create-release-build.yml b/.github/workflows/create-release-build.yml deleted file mode 100644 index e9aa5796fec42..0000000000000 --- a/.github/workflows/create-release-build.yml +++ /dev/null @@ -1,183 +0,0 @@ -name: Create Release Build -run-name: Compile Bun v${{ inputs.version }} by ${{ github.actor }} - -concurrency: - group: release - cancel-in-progress: true - -permissions: - contents: write - actions: write - -on: - workflow_dispatch: - inputs: - version: - type: string - required: true - description: "Release version. Example: 1.1.4. Exclude the 'v' prefix." - tag: - type: string - required: true - description: "GitHub tag to use" - clobber: - type: boolean - required: false - default: false - description: "Overwrite existing release artifacts?" - release: - types: - - created - -jobs: - notify-start: - if: ${{ github.repository_owner == 'oven-sh' }} - name: Notify Start - runs-on: ubuntu-latest - steps: - - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_PUBLIC }} - nodetail: true - color: "#1F6FEB" - title: "Bun v${{ inputs.version }} is compiling" - description: | - ### @${{ github.actor }} started compiling Bun v${{inputs.version}} - - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.BUN_DISCORD_GITHUB_CHANNEL_WEBHOOK }} - nodetail: true - color: "#1F6FEB" - title: "Bun v${{ inputs.version }} is compiling" - description: | - ### @${{ github.actor }} started compiling Bun v${{inputs.version}} - - **[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** - linux-x64: - name: Build linux-x64 - uses: ./.github/workflows/build-linux.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }} - tag: linux-x64 - arch: x64 - cpu: haswell - canary: false - linux-x64-baseline: - name: Build linux-x64-baseline - uses: ./.github/workflows/build-linux.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }} - tag: linux-x64-baseline - arch: x64 - cpu: nehalem - canary: false - linux-aarch64: - name: Build linux-aarch64 - uses: ./.github/workflows/build-linux.yml - secrets: inherit - with: - runs-on: namespace-profile-bun-ci-linux-aarch64 - tag: linux-aarch64 - arch: aarch64 - cpu: native - canary: false - darwin-x64: - name: Build darwin-x64 - uses: ./.github/workflows/build-darwin.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }} - tag: darwin-x64 - arch: x64 - cpu: haswell - canary: false - darwin-x64-baseline: - name: Build darwin-x64-baseline - uses: ./.github/workflows/build-darwin.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }} - tag: darwin-x64-baseline - arch: x64 - cpu: nehalem - canary: false - darwin-aarch64: - name: Build darwin-aarch64 - uses: ./.github/workflows/build-darwin.yml - secrets: inherit - with: - runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-darwin-aarch64' || 'macos-12' }} - tag: darwin-aarch64 - arch: aarch64 - cpu: native - canary: false - windows-x64: - name: Build windows-x64 - uses: ./.github/workflows/build-windows.yml - secrets: inherit - with: - runs-on: windows - tag: windows-x64 - arch: x64 - cpu: haswell - canary: false - windows-x64-baseline: - name: Build windows-x64-baseline - uses: ./.github/workflows/build-windows.yml - secrets: inherit - with: - runs-on: windows - tag: windows-x64-baseline - arch: x64 - cpu: nehalem - canary: false - - upload-artifacts: - needs: - - linux-x64 - - linux-x64-baseline - - linux-aarch64 - - darwin-x64 - - darwin-x64-baseline - - darwin-aarch64 - - windows-x64 - - windows-x64-baseline - runs-on: ubuntu-latest - steps: - - name: Download Artifacts - uses: actions/download-artifact@v4 - with: - path: bun-releases - pattern: bun-* - merge-multiple: true - github-token: ${{ github.token }} - - name: Check for Artifacts - run: | - if [ ! -d "bun-releases" ] || [ -z "$(ls -A bun-releases)" ]; then - echo "Error: No artifacts were downloaded or 'bun-releases' directory does not exist." - exit 1 # Fail the job if the condition is met - else - echo "Artifacts downloaded successfully." - fi - - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - nodetail: true - color: "#FF0000" - title: "Bun v${{ inputs.version }} release artifacts uploaded" - - name: "Upload Artifacts" - env: - GH_TOKEN: ${{ github.token }} - run: | - # Unzip one level deep each artifact - cd bun-releases - for f in *.zip; do - unzip -o $f - done - cd .. - gh release upload --repo=${{ github.repository }} ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.event.release.id }} ${{ inputs.clobber && '--clobber' || '' }} bun-releases/*.zip diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000000000..78f936207ebed --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,61 @@ +name: Format + +permissions: + contents: write + +concurrency: + group: format-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && inputs.run-id || github.ref }} + cancel-in-progress: true + +on: + workflow_dispatch: + inputs: + run-id: + type: string + description: The workflow ID to download artifacts (skips the build step) + pull_request: + +jobs: + format: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: | + .prettierrc-ci + .github + .vscode + src + scripts + packages + test + bench + package.json + bun.lockb + .clang-format + - name: Setup Bun + uses: ./.github/actions/setup-bun + with: + bun-version: "1.1.25" + - name: Setup Zig + uses: mlugg/setup-zig@v1 + with: + version: 0.13.0 + - name: Install Dependencies + run: | + bun install + - name: Format + run: | + bun fmt + - name: Format Zig + run: | + bun fmt:zig + - name: Format Cpp + run: | + bun fmt:cpp + - name: Commit + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Apply formatting changes diff --git a/.github/workflows/labeled.yml b/.github/workflows/labeled.yml index 961ae7926f459..f0240aa0c3655 100644 --- a/.github/workflows/labeled.yml +++ b/.github/workflows/labeled.yml @@ -7,6 +7,42 @@ on: types: [labeled] jobs: + # on-bug: + # runs-on: ubuntu-latest + # if: github.event.label.name == 'bug' || github.event.label.name == 'crash' + # permissions: + # issues: write + # steps: + # - name: Checkout + # uses: actions/checkout@v4 + # with: + # sparse-checkout: | + # scripts + # .github + # CMakeLists.txt + # - name: Setup Bun + # uses: ./.github/actions/setup-bun + # with: + # bun-version: "1.1.24" + # - name: "categorize bug" + # id: add-labels + # env: + # GITHUB_ISSUE_BODY: ${{ github.event.issue.body }} + # GITHUB_ISSUE_TITLE: ${{ github.event.issue.title }} + # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + # shell: bash + # run: | + # echo '{"dependencies": { "@anthropic-ai/sdk": "latest" }}' > scripts/package.json && bun install --cwd=./scripts + # LABELS=$(bun scripts/label-issue.ts) + # echo "labels=$LABELS" >> $GITHUB_OUTPUT + # - name: Add labels + # uses: actions-cool/issues-helper@v3 + # if: steps.add-labels.outputs.labels != '' + # with: + # actions: "add-labels" + # token: ${{ secrets.GITHUB_TOKEN }} + # issue-number: ${{ github.event.issue.number }} + # labels: ${{ steps.add-labels.outputs.labels }} on-labeled: runs-on: ubuntu-latest if: github.event.label.name == 'crash' || github.event.label.name == 'needs repro' diff --git a/.github/workflows/lint-cpp.yml b/.github/workflows/lint-cpp.yml index 50e750029a471..5841745ac3ca8 100644 --- a/.github/workflows/lint-cpp.yml +++ b/.github/workflows/lint-cpp.yml @@ -13,11 +13,12 @@ on: run-id: type: string description: The workflow ID to download artifacts (skips the build step) - pull_request: - paths-ignore: - - .vscode/**/* - - docs/**/* - - examples/**/* + # pull_request: + # paths: + # - ".github/workflows/lint-cpp.yml" + # - "**/*.cpp" + # - "vendor/**/*" + # - "CMakeLists.txt" jobs: lint-cpp: diff --git a/.github/workflows/on-submodule-update.yml b/.github/workflows/on-submodule-update.yml new file mode 100644 index 0000000000000..fde82bbd4936e --- /dev/null +++ b/.github/workflows/on-submodule-update.yml @@ -0,0 +1,89 @@ +name: Comment on updated submodule + +on: + pull_request_target: + paths: + - "src/generated_versions_list.zig" + - ".github/workflows/on-submodule-update.yml" + +jobs: + comment: + name: Comment + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'oven-sh' }} + permissions: + contents: read + pull-requests: write + issues: write + steps: + - name: Checkout current + uses: actions/checkout@v4 + with: + sparse-checkout: | + src + - name: Hash generated versions list + id: hash + run: | + echo "hash=$(sha256sum src/generated_versions_list.zig | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT + - name: Checkout base + uses: actions/checkout@v4 + with: + ref: ${{ github.base_ref }} + sparse-checkout: | + src + - name: Hash base + id: base + run: | + echo "base=$(sha256sum src/generated_versions_list.zig | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT + - name: Compare + id: compare + run: | + if [ "${{ steps.hash.outputs.hash }}" != "${{ steps.base.outputs.base }}" ]; then + echo "changed=true" >> $GITHUB_OUTPUT + else + echo "changed=false" >> $GITHUB_OUTPUT + fi + - name: Find Comment + id: comment + uses: peter-evans/find-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: github-actions[bot] + body-includes: + - name: Write Warning Comment + uses: peter-evans/create-or-update-comment@v4 + if: steps.compare.outputs.changed == 'true' + with: + comment-id: ${{ steps.comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + ⚠️ **Warning:** @${{ github.actor }}, this PR has changes to submodule versions. + + If this change was intentional, please ignore this message. If not, please undo changes to submodules and rebase your branch. + + + - name: Add labels + uses: actions-cool/issues-helper@v3 + if: steps.compare.outputs.changed == 'true' + with: + actions: "add-labels" + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + labels: "changed-submodules" + - name: Remove labels + uses: actions-cool/issues-helper@v3 + if: steps.compare.outputs.changed == 'false' + with: + actions: "remove-labels" + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + labels: "changed-submodules" + - name: Delete outdated comment + uses: actions-cool/issues-helper@v3 + if: steps.compare.outputs.changed == 'false' && steps.comment.outputs.comment-id != '' + with: + actions: "delete-comment" + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + comment-id: ${{ steps.comment.outputs.comment-id }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e0e90fac1365..ab0bf70103261 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,6 @@ +# TODO: Move this to bash scripts intead of Github Actions +# so it can be run from Buildkite, see: .buildkite/scripts/release.sh + name: Release concurrency: release @@ -63,7 +66,7 @@ jobs: - name: Setup Bun uses: ./.github/actions/setup-bun with: - bun-version: "1.0.21" + bun-version: "1.1.20" - name: Install Dependencies run: bun install - name: Sign Release @@ -85,10 +88,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # To workaround issue + ref: main - name: Setup Bun uses: ./.github/actions/setup-bun with: - bun-version: "1.0.21" + bun-version: "1.1.20" - name: Install Dependencies run: bun install - name: Release @@ -117,7 +123,7 @@ jobs: if: ${{ env.BUN_VERSION != 'canary' }} uses: ./.github/actions/setup-bun with: - bun-version: "1.0.21" + bun-version: "1.1.20" - name: Setup Bun if: ${{ env.BUN_VERSION == 'canary' }} uses: ./.github/actions/setup-bun @@ -259,7 +265,7 @@ jobs: - name: Setup Bun uses: ./.github/actions/setup-bun with: - bun-version: "1.0.21" + bun-version: "1.1.20" - name: Install Dependencies run: bun install - name: Release @@ -270,6 +276,24 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY}} AWS_ENDPOINT: ${{ secrets.AWS_ENDPOINT }} AWS_BUCKET: bun + + notify-sentry: + name: Notify Sentry + runs-on: ubuntu-latest + needs: s3 + steps: + - name: Notify Sentry + uses: getsentry/action-release@v1.7.0 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + with: + ignore_missing: true + ignore_empty: true + version: ${{ env.BUN_VERSION }} + environment: production + bump: name: "Bump version" runs-on: ubuntu-latest diff --git a/.github/workflows/run-format.yml b/.github/workflows/run-format.yml index bef73929e2586..49432e1381b00 100644 --- a/.github/workflows/run-format.yml +++ b/.github/workflows/run-format.yml @@ -14,24 +14,28 @@ jobs: format: name: Format runs-on: ubuntu-latest - if: ${{ github.ref != 'refs/heads/main' }} steps: - name: Checkout uses: actions/checkout@v4 with: sparse-checkout: | + .prettierrc-ci .github + .vscode src scripts packages test bench + package.json + bun.lockb + .clang-format - name: Setup Bun uses: ./.github/actions/setup-bun with: - bun-version: "1.1.8" + bun-version: "1.1.25" - name: Setup Zig - uses: goto-bus-stop/setup-zig@c7b6cdd3adba8f8b96984640ff172c37c93f73ee + uses: mlugg/setup-zig@v1 with: version: ${{ inputs.zig-version }} - name: Install Dependencies @@ -43,9 +47,9 @@ jobs: - name: Format Zig run: | bun fmt:zig - - name: Generate submodule versions + - name: Format Cpp run: | - bash ./scripts/write-versions.sh + bun fmt:cpp - name: Commit uses: stefanzweifel/git-auto-commit-action@v5 with: diff --git a/.github/workflows/run-lint-cpp.yml b/.github/workflows/run-lint-cpp.yml index 12abea144b9c5..539ec62f20882 100644 --- a/.github/workflows/run-lint-cpp.yml +++ b/.github/workflows/run-lint-cpp.yml @@ -3,7 +3,7 @@ name: lint-cpp permissions: contents: read env: - LLVM_VERSION: 16 + LLVM_VERSION: 18 LC_CTYPE: "en_US.UTF-8" LC_ALL: "en_US.UTF-8" @@ -17,7 +17,7 @@ on: jobs: lint-cpp: name: Lint C++ - runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-13-xlarge' || 'macos-12' }} + runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-13-xlarge' || 'macos-13' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -26,7 +26,7 @@ jobs: - name: Setup Bun uses: ./.github/actions/setup-bun with: - bun-version: latest + bun-version: 1.1.23 - name: Install Dependencies env: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 @@ -65,17 +65,17 @@ jobs: echo "did_fail=$(cat did_fail.txt)" >> $GITHUB_OUTPUT - name: Upload format.log - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: format.log path: format.log - name: Upload PR - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: pr-number.txt path: pr-number.txt - name: Upload PR - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: did_fail.txt path: did_fail.txt diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml deleted file mode 100644 index 6efe322a54afa..0000000000000 --- a/.github/workflows/run-test.yml +++ /dev/null @@ -1,224 +0,0 @@ -name: Test - -permissions: - contents: read - actions: read - -on: - workflow_call: - inputs: - runs-on: - type: string - required: true - tag: - type: string - required: true - pr-number: - type: string - required: true - run-id: - type: string - default: ${{ github.run_id }} - -jobs: - test: - name: Tests - runs-on: ${{ inputs.runs-on }} - steps: - - if: ${{ runner.os == 'Windows' }} - name: Setup Git - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: | - package.json - bun.lockb - test - packages/bun-internal-test - packages/bun-types - - name: Setup Environment - shell: bash - run: | - echo "${{ inputs.pr-number }}" > pr-number.txt - - name: Download Bun - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }} - path: bun - github-token: ${{ github.token }} - run-id: ${{ inputs.run-id || github.run_id }} - - name: Download pnpm - uses: pnpm/action-setup@v4 - with: - version: 8 - - if: ${{ runner.os != 'Windows' }} - name: Setup Bun - shell: bash - run: | - unzip bun/bun-*.zip - cd bun-* - pwd >> $GITHUB_PATH - - if: ${{ runner.os == 'Windows' }} - name: Setup Cygwin - uses: secondlife/setup-cygwin@v3 - with: - packages: bash - - if: ${{ runner.os == 'Windows' }} - name: Setup Bun (Windows) - run: | - unzip bun/bun-*.zip - cd bun-* - pwd >> $env:GITHUB_PATH - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Install Dependencies - timeout-minutes: 5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - bun install - - name: Install Dependencies (test) - timeout-minutes: 5 - run: | - bun install --cwd test - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Install Dependencies (runner) - timeout-minutes: 5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - bun install --cwd packages/bun-internal-test - - name: Run Tests - id: test - timeout-minutes: 90 - shell: bash - env: - IS_BUN_CI: 1 - TMPDIR: ${{ runner.temp }} - BUN_TAG: ${{ inputs.tag }} - BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" - SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }} - TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} - TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} - TEST_INFO_STRIPE: ${{ secrets.TEST_INFO_STRIPE }} - TEST_INFO_AZURE_SERVICE_BUS: ${{ secrets.TEST_INFO_AZURE_SERVICE_BUS }} - SHELLOPTS: igncr - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - node packages/bun-internal-test/src/runner.node.mjs $(which bun) - - if: ${{ always() }} - name: Upload Results - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-tests - path: | - test-report.* - comment.md - pr-number.txt - if-no-files-found: error - overwrite: true - - if: ${{ always() && steps.test.outputs.failing_tests != '' && github.event.pull_request && github.repository_owner == 'oven-sh' }} - name: Send Message - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - nodetail: true - color: "#FF0000" - title: "" - description: | - ### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) - - @${{ github.actor }}, there are ${{ steps.test.outputs.failing_tests_count || 'some' }} failing tests on bun-${{ inputs.tag }}. - - ${{ steps.test.outputs.failing_tests }} - - **[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** - - name: Fail - if: ${{ failure() || always() && steps.test.outputs.failing_tests != '' }} - run: | - echo "There are ${{ steps.test.outputs.failing_tests_count || 'some' }} failing tests on bun-${{ inputs.tag }}." - exit 1 - test-node: - name: Node.js Tests - # TODO: enable when we start paying attention to the results. In the meantime, this causes CI to queue jobs wasting developer time. - if: 0 - runs-on: ${{ inputs.runs-on }} - steps: - - if: ${{ runner.os == 'Windows' }} - name: Setup Git - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: | - test/node.js - - name: Setup Environment - shell: bash - run: | - echo "${{ inputs.pr-number }}" > pr-number.txt - - name: Download Bun - uses: actions/download-artifact@v4 - with: - name: bun-${{ inputs.tag }} - path: bun - github-token: ${{ github.token }} - run-id: ${{ inputs.run-id || github.run_id }} - - if: ${{ runner.os != 'Windows' }} - name: Setup Bun - shell: bash - run: | - unzip bun/bun-*.zip - cd bun-* - pwd >> $GITHUB_PATH - - if: ${{ runner.os == 'Windows' }} - name: Setup Cygwin - uses: secondlife/setup-cygwin@v3 - with: - packages: bash - - if: ${{ runner.os == 'Windows' }} - name: Setup Bun (Windows) - run: | - unzip bun/bun-*.zip - cd bun-* - pwd >> $env:GITHUB_PATH - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Checkout Tests - shell: bash - working-directory: test/node.js - run: | - node runner.mjs --pull - - name: Install Dependencies - timeout-minutes: 5 - shell: bash - working-directory: test/node.js - run: | - bun install - - name: Run Tests - timeout-minutes: 10 # Increase when more tests are added - shell: bash - working-directory: test/node.js - env: - TMPDIR: ${{ runner.temp }} - BUN_GARBAGE_COLLECTOR_LEVEL: "0" - BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" - run: | - node runner.mjs - - name: Upload Results - uses: actions/upload-artifact@v4 - with: - name: bun-${{ inputs.tag }}-node-tests - path: | - test/node.js/summary/*.json - if-no-files-found: error - overwrite: true diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 0000000000000..a24bb2cd6b558 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,30 @@ +name: Close inactive issues +on: + # schedule: + # - cron: "15 * * * *" + workflow_dispatch: + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + days-before-issue-close: 5 + any-of-issue-labels: "needs repro,waiting-for-author" + exempt-issue-labels: "neverstale" + exempt-pr-labels: "neverstale" + remove-stale-when-updated: true + stale-issue-label: "stale" + stale-pr-label: "stale" + stale-issue-message: "This issue is stale and may be closed due to inactivity. If you're still running into this, please leave a comment." + close-issue-message: "This issue was closed because it has been inactive for 5 days since being marked as stale." + days-before-pr-stale: 30 + days-before-pr-close: 14 + stale-pr-message: "This pull request is stale and may be closed due to inactivity." + close-pr-message: "This pull request has been closed due to inactivity." + repo-token: ${{ github.token }} + operations-per-run: 1000 diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml deleted file mode 100644 index 232c449e1d12f..0000000000000 --- a/.github/workflows/upload.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Upload Artifacts -run-name: Canary release ${{github.sha}} upload - -permissions: - contents: write - -on: - workflow_run: - workflows: - - CI - types: - - completed - branches: - - main - -jobs: - upload: - if: ${{ github.repository_owner == 'oven-sh' }} - name: Upload Artifacts - runs-on: ubuntu-latest - steps: - - name: Download Artifacts - uses: actions/download-artifact@v4 - with: - path: bun - pattern: bun-* - merge-multiple: true - github-token: ${{ github.token }} - run-id: ${{ github.event.workflow_run.id }} - - name: Check for Artifacts - run: | - if [ ! -d "bun" ] || [ -z "$(ls -A bun)" ]; then - echo "Error: No artifacts were downloaded or 'bun' directory does not exist." - exit 1 # Fail the job if the condition is met - else - echo "Artifacts downloaded successfully." - fi - - name: Upload to GitHub Releases - uses: ncipollo/release-action@v1 - with: - tag: canary - name: Canary (${{ github.sha }}) - prerelease: true - body: This canary release of Bun corresponds to the commit [${{ github.sha }}] - allowUpdates: true - replacesArtifacts: true - generateReleaseNotes: true - artifactErrorsFailBuild: true - artifacts: bun/**/bun-*.zip - token: ${{ github.token }} - - name: Upload to S3 (using SHA) - uses: shallwefootball/s3-upload-action@4350529f410221787ccf424e50133cbc1b52704e - with: - endpoint: ${{ secrets.AWS_ENDPOINT }} - aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}} - aws_bucket: ${{ secrets.AWS_BUCKET }} - source_dir: bun - destination_dir: releases/${{ github.event.workflow_run.head_sha || github.sha }}-canary - - name: Upload to S3 (using tag) - uses: shallwefootball/s3-upload-action@4350529f410221787ccf424e50133cbc1b52704e - with: - endpoint: ${{ secrets.AWS_ENDPOINT }} - aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}} - aws_bucket: ${{ secrets.AWS_BUCKET }} - source_dir: bun - destination_dir: releases/canary - - name: Announce on Discord - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.BUN_DISCORD_GITHUB_CHANNEL_WEBHOOK }} - nodetail: true - color: "#1F6FEB" - title: "New Bun Canary available" - url: https://github.com/oven-sh/bun/commit/${{ github.sha }} - description: | - A new canary build of Bun has been automatically uploaded. To upgrade, run: - ```sh - bun upgrade --canary - # bun upgrade --stable <- to downgrade - ``` diff --git a/.gitignore b/.gitignore index 849c532b2bcb1..126af7cebda0c 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,6 @@ /build-*/ /bun-webkit /kcov-out -/src/deps/libuv /test-report.json /test-report.md /test.js @@ -117,11 +116,6 @@ sign.json src/bun.js/bindings-obj src/bun.js/bindings/GeneratedJS2Native.zig src/bun.js/debug-bindings-obj -src/deps/c-ares/build -src/deps/libiconv -src/deps/openssl -src/deps/PLCrashReporter/ -src/deps/s2n-tls src/deps/zig-clap/.gitattributes src/deps/zig-clap/.github src/deps/zig-clap/example @@ -145,3 +139,27 @@ zig-cache zig-out test/node.js/upstream .zig-cache +scripts/env.local +*.generated.ts + +# Dependencies +/vendor + +# Dependencies (before CMake) +# These can be removed in the far future +/src/bun.js/WebKit +/src/deps/WebKit +/src/deps/boringssl +/src/deps/brotli +/src/deps/c*ares +/src/deps/lol*html +/src/deps/libarchive +/src/deps/libdeflate +/src/deps/libuv +/src/deps/ls*hpack +/src/deps/mimalloc +/src/deps/picohttpparser +/src/deps/tinycc +/src/deps/zstd +/src/deps/zlib +/src/deps/zig diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 98845d5097cb2..0000000000000 --- a/.gitmodules +++ /dev/null @@ -1,84 +0,0 @@ -[submodule "src/javascript/jsc/WebKit"] -path = src/bun.js/WebKit -url = https://github.com/oven-sh/WebKit.git -ignore = dirty -depth = 1 -update = none -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/picohttpparser"] -path = src/deps/picohttpparser -url = https://github.com/h2o/picohttpparser.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/mimalloc"] -path = src/deps/mimalloc -url = https://github.com/Jarred-Sumner/mimalloc.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/zlib"] -path = src/deps/zlib -url = https://github.com/cloudflare/zlib.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/libarchive"] -path = src/deps/libarchive -url = https://github.com/libarchive/libarchive.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/boringssl"] -path = src/deps/boringssl -url = https://github.com/oven-sh/boringssl.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/lol-html"] -path = src/deps/lol-html -url = https://github.com/cloudflare/lol-html -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/tinycc"] -path = src/deps/tinycc -url = https://github.com/Jarred-Sumner/tinycc.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/c-ares"] -path = src/deps/c-ares -url = https://github.com/c-ares/c-ares.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/zstd"] -path = src/deps/zstd -url = https://github.com/facebook/zstd.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "src/deps/ls-hpack"] -path = src/deps/ls-hpack -url = https://github.com/litespeedtech/ls-hpack.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false -[submodule "zig"] -path = src/deps/zig -url = https://github.com/oven-sh/zig -depth = 1 -shallow = true -fetchRecurseSubmodules = false diff --git a/.lldbinit b/.lldbinit new file mode 100644 index 0000000000000..b54a4195c3309 --- /dev/null +++ b/.lldbinit @@ -0,0 +1,4 @@ +command script import vendor/zig/tools/lldb_pretty_printers.py +command script import vendor/WebKit/Tools/lldb/lldb_webkit.py + +# type summary add --summary-string "${var} | inner=${var[0-30]}, source=${var[33-64]}, tag=${var[31-32]}" "unsigned long" diff --git a/.prettierignore b/.prettierignore index d7360d9d2f2ad..da765c9c28648 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,10 @@ src/bun.js/WebKit -src/deps +vendor test/snapshots test/js/deno test/node.js src/react-refresh.js *.min.js +test/js/node/test/fixtures +test/js/node/test/common +test/snippets diff --git a/.prettierrc-ci b/.prettierrc-ci new file mode 100644 index 0000000000000..b9cd5bacb7826 --- /dev/null +++ b/.prettierrc-ci @@ -0,0 +1,31 @@ +{ + "arrowParens": "avoid", + "printWidth": 120, + "trailingComma": "all", + "useTabs": false, + "quoteProps": "preserve", + "plugins": [ + "prettier-plugin-organize-imports" + ], + "overrides": [ + { + "files": [ + ".vscode/*.json" + ], + "options": { + "parser": "jsonc", + "quoteProps": "preserve", + "singleQuote": false, + "trailingComma": "all" + } + }, + { + "files": [ + "*.md" + ], + "options": { + "printWidth": 80 + } + } + ] +} diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 3bd648a1ffd99..8e3517a2e35f0 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,18 +3,18 @@ { "name": "Debug", "forcedInclude": ["${workspaceFolder}/src/bun.js/bindings/root.h"], - "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "compileCommands": "${workspaceFolder}/build/debug/compile_commands.json", "includePath": [ "${workspaceFolder}/build/bun-webkit/include", - "${workspaceFolder}/build/codegen", + "${workspaceFolder}/build/debug/codegen", "${workspaceFolder}/src/bun.js/bindings/", "${workspaceFolder}/src/bun.js/bindings/webcore/", "${workspaceFolder}/src/bun.js/bindings/sqlite/", "${workspaceFolder}/src/bun.js/bindings/webcrypto/", "${workspaceFolder}/src/bun.js/modules/", "${workspaceFolder}/src/js/builtins/", - "${workspaceFolder}/src/deps/boringssl/include/", - "${workspaceFolder}/src/deps", + "${workspaceFolder}/vendor/boringssl/include/", + "${workspaceFolder}/vendor", "${workspaceFolder}/src/napi/*", "${workspaceFolder}/packages/bun-usockets/src", "${workspaceFolder}/packages/", @@ -26,8 +26,8 @@ "${workspaceFolder}/src/napi/*", "${workspaceFolder}/src/js/builtins/*", "${workspaceFolder}/src/bun.js/modules/*", - "${workspaceFolder}/src/deps/*", - "${workspaceFolder}/src/deps/boringssl/include/*", + "${workspaceFolder}/vendor/*", + "${workspaceFolder}/vendor/boringssl/include/*", "${workspaceFolder}/packages/bun-usockets/*", "${workspaceFolder}/packages/bun-uws/*", "${workspaceFolder}/src/napi/*", @@ -55,12 +55,12 @@ "name": "BunWithJSCDebug", "forcedInclude": ["${workspaceFolder}/src/bun.js/bindings/root.h"], "includePath": [ - "${workspaceFolder}/build/codegen", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/ICU/Headers/", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/JavaScriptCore/PrivateHeaders/", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/WTF/Headers", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/bmalloc/Headers/", + "${workspaceFolder}/build/debug/codegen", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/ICU/Headers/", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/PrivateHeaders/", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/WTF/Headers", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/bmalloc/Headers/", "${workspaceFolder}/src/bun.js/bindings/", "${workspaceFolder}/src/bun.js/bindings/webcore/", "${workspaceFolder}/src/bun.js/bindings/sqlite/", @@ -68,19 +68,19 @@ "${workspaceFolder}/src/bun.js/modules/", "${workspaceFolder}/src/js/builtins/", "${workspaceFolder}/src/js/out", - "${workspaceFolder}/src/deps/boringssl/include/", - "${workspaceFolder}/src/deps", + "${workspaceFolder}/vendor/boringssl/include/", + "${workspaceFolder}/vendor", "${workspaceFolder}/src/napi/*", "${workspaceFolder}/packages/bun-usockets/src", "${workspaceFolder}/packages/", ], "browse": { "path": [ - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/ICU/Headers/", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/JavaScriptCore/PrivateHeaders/**", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/WTF/Headers/**", - "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/bmalloc/Headers/**", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/ICU/Headers/", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/PrivateHeaders/**", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/WTF/Headers/**", + "${workspaceFolder}/vendor/WebKit/WebKitBuild/Debug/bmalloc/Headers/**", "${workspaceFolder}/src/bun.js/bindings/*", "${workspaceFolder}/src/bun.js/bindings/*", "${workspaceFolder}/src/napi/*", @@ -90,8 +90,8 @@ "${workspaceFolder}/src/js/builtins/*", "${workspaceFolder}/src/js/out/*", "${workspaceFolder}/src/bun.js/modules/*", - "${workspaceFolder}/src/deps", - "${workspaceFolder}/src/deps/boringssl/include/", + "${workspaceFolder}/vendor", + "${workspaceFolder}/vendor/boringssl/include/", "${workspaceFolder}/packages/bun-usockets/", "${workspaceFolder}/packages/bun-uws/", "${workspaceFolder}/src/napi", diff --git a/.vscode/launch.json b/.vscode/launch.json index 3fd7dbdd75069..23205ab1f13f7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,11 +12,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file]", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", + "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "1", }, "console": "internalConsole", @@ -25,14 +27,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] --only", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "--only", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "1", - "BUN_DEBUG_FileReader": "1", "BUN_DEBUG_jest": "1", }, "console": "internalConsole", @@ -47,12 +48,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] (fast)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "0", }, "console": "internalConsole", @@ -61,12 +63,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] (verbose)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "0", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -75,12 +78,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] --watch", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "--watch", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -89,12 +93,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] --hot", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "--hot", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -103,12 +108,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] --inspect", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", "BUN_INSPECT": "ws://localhost:0/?wait=1", }, @@ -123,12 +129,13 @@ "type": "lldb", "request": "launch", "name": "bun test [file] --inspect-brk", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", "BUN_INSPECT": "ws://localhost:0/?break=1", }, @@ -144,13 +151,12 @@ "type": "lldb", "request": "launch", "name": "bun run [file]", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "env": { "FORCE_COLOR": "0", "BUN_DEBUG_QUIET_LOGS": "1", - "BUN_DEBUG_EventLoop": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -159,7 +165,7 @@ "type": "lldb", "request": "launch", "name": "bun run [file] (fast)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "env": { @@ -173,7 +179,7 @@ "type": "lldb", "request": "launch", "name": "bun run [file] (verbose)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "env": { @@ -187,7 +193,7 @@ "type": "lldb", "request": "launch", "name": "bun run [file] --watch", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "--watch", "${fileBasename}"], "cwd": "${fileDirname}", "env": { @@ -201,7 +207,7 @@ "type": "lldb", "request": "launch", "name": "bun run [file] --hot", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "--hot", "${fileBasename}"], "cwd": "${fileDirname}", "env": { @@ -215,7 +221,7 @@ "type": "lldb", "request": "launch", "name": "bun run [file] --inspect", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "env": { @@ -235,7 +241,7 @@ "type": "lldb", "request": "launch", "name": "bun run [file] --inspect-brk", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "env": { @@ -256,12 +262,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...]", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -270,12 +277,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...] (fast)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "0", }, "console": "internalConsole", @@ -284,12 +292,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...] (verbose)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", - "BUN_DEBUG_QUIET_LOGS": "0", + "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -298,12 +307,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...] --watch", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "--watch", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -312,12 +322,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...] --hot", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "--hot", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", }, "console": "internalConsole", @@ -326,12 +337,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...] --inspect", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", "BUN_INSPECT": "ws://localhost:0/?wait=1", }, @@ -346,12 +358,13 @@ "type": "lldb", "request": "launch", "name": "bun test [...] --inspect-brk", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_DEBUG_jest": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", "BUN_INSPECT": "ws://localhost:0/?break=1", }, @@ -367,7 +380,7 @@ "type": "lldb", "request": "launch", "name": "bun exec [...]", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["exec", "${input:testName}"], "cwd": "${workspaceFolder}", "env": { @@ -382,7 +395,7 @@ "type": "lldb", "request": "launch", "name": "bun test [*]", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test"], "cwd": "${workspaceFolder}/test", "env": { @@ -396,7 +409,7 @@ "type": "lldb", "request": "launch", "name": "bun test [*] (fast)", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test"], "cwd": "${workspaceFolder}/test", "env": { @@ -410,7 +423,7 @@ "type": "lldb", "request": "launch", "name": "bun test [*] --inspect", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["test"], "cwd": "${workspaceFolder}/test", "env": { @@ -430,7 +443,7 @@ "type": "lldb", "request": "launch", "name": "bun install [folder]", - "program": "${workspaceFolder}/build/bun-debug", + "program": "${workspaceFolder}/build/debug/bun-debug", "args": ["install"], "cwd": "${fileDirname}", "env": { @@ -445,8 +458,13 @@ "request": "launch", "name": "bun test [*] (ci)", "program": "node", - "args": ["src/runner.node.mjs"], - "cwd": "${workspaceFolder}/packages/bun-internal-test", + "args": ["test/runner.node.mjs"], + "cwd": "${workspaceFolder}", + "env": { + "FORCE_COLOR": "1", + "BUN_DEBUG_QUIET_LOGS": "1", + "BUN_GARBAGE_COLLECTOR_LEVEL": "2", + }, "console": "internalConsole", }, // Windows: bun test [file] @@ -457,7 +475,7 @@ }, "request": "launch", "name": "Windows: bun test [file]", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -473,7 +491,6 @@ "name": "BUN_DEBUG_jest", "value": "1", }, - { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "1", @@ -487,7 +504,7 @@ }, "request": "launch", "name": "Windows: bun test --only [file]", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "--only", "${file}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -500,19 +517,7 @@ "value": "1", }, { - "name": "BUN_DEBUG_EventLoop", - "value": "1", - }, - { - "name": "BUN_DEBUG_uv", - "value": "1", - }, - { - "name": "BUN_DEBUG_SYS", - "value": "1", - }, - { - "name": "BUN_DEBUG_PipeWriter", + "name": "BUN_DEBUG_jest", "value": "1", }, { @@ -528,7 +533,7 @@ }, "request": "launch", "name": "Windows: bun test [file] (fast)", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -540,6 +545,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "0", @@ -553,7 +562,7 @@ }, "request": "launch", "name": "Windows: bun test [file] (verbose)", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -565,6 +574,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "0", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -578,7 +591,7 @@ }, "request": "launch", "name": "Windows: bun test [file] --inspect", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -590,6 +603,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -612,7 +629,7 @@ }, "request": "launch", "name": "Windows: bun test [file] --inspect-brk", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${file}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -624,6 +641,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -647,7 +668,7 @@ }, "request": "launch", "name": "Windows: bun run [file]", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "environment": [ @@ -659,6 +680,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -672,7 +697,7 @@ }, "request": "launch", "name": "Windows: bun install", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["install"], "cwd": "${fileDirname}", "environment": [ @@ -680,7 +705,10 @@ "name": "FORCE_COLOR", "value": "1", }, - + { + "name": "BUN_DEBUG_QUIET_LOGS", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "0", @@ -694,7 +722,7 @@ }, "request": "launch", "name": "Windows: bun run [file] (verbose)", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "environment": [ @@ -704,7 +732,7 @@ }, { "name": "BUN_DEBUG_QUIET_LOGS", - "value": "0", + "value": "1", }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", @@ -719,7 +747,7 @@ }, "request": "launch", "name": "Windows: bun run [file] --inspect", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "environment": [ @@ -753,7 +781,7 @@ }, "request": "launch", "name": "Windows: bun run [file] --inspect-brk", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "environment": [ @@ -788,7 +816,7 @@ }, "request": "launch", "name": "Windows: bun test [...]", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -800,6 +828,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -813,7 +845,7 @@ }, "request": "launch", "name": "Windows: bun test [...] (fast)", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -825,6 +857,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "0", @@ -838,7 +874,7 @@ }, "request": "launch", "name": "Windows: bun test [...] (verbose)", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -850,6 +886,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "0", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -863,7 +903,7 @@ }, "request": "launch", "name": "Windows: bun test [...] --watch", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "--watch", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -875,6 +915,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -888,7 +932,7 @@ }, "request": "launch", "name": "Windows: bun test [...] --hot", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "--hot", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -900,6 +944,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -913,7 +961,7 @@ }, "request": "launch", "name": "Windows: bun test [...] --inspect", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -925,6 +973,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -947,7 +999,7 @@ }, "request": "launch", "name": "Windows: bun test [...] --inspect-brk", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test", "${input:testName}"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -959,6 +1011,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "2", @@ -982,7 +1038,7 @@ }, "request": "launch", "name": "Windows: bun exec [...]", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["exec", "${input:testName}"], "cwd": "${workspaceFolder}", "environment": [ @@ -1008,7 +1064,7 @@ }, "request": "launch", "name": "Windows: bun test [*]", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -1033,7 +1089,7 @@ }, "request": "launch", "name": "Windows: bun test [*] (fast)", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -1045,6 +1101,10 @@ "name": "BUN_DEBUG_QUIET_LOGS", "value": "1", }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", "value": "0", @@ -1058,7 +1118,7 @@ }, "request": "launch", "name": "Windows: bun test [*] --inspect", - "program": "${workspaceFolder}/build/bun-debug.exe", + "program": "${workspaceFolder}/build/debug/bun-debug.exe", "args": ["test"], "cwd": "${workspaceFolder}/test", "environment": [ @@ -1068,7 +1128,11 @@ }, { "name": "BUN_DEBUG_QUIET_LOGS", - "value": "0", + "value": "1", + }, + { + "name": "BUN_DEBUG_jest", + "value": "1", }, { "name": "BUN_GARBAGE_COLLECTOR_LEVEL", @@ -1093,8 +1157,26 @@ "request": "launch", "name": "Windows: bun test [*] (ci)", "program": "node", - "args": ["src/runner.node.mjs"], - "cwd": "${workspaceFolder}/packages/bun-internal-test", + "args": ["test/runner.node.mjs"], + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "FORCE_COLOR", + "value": "1", + }, + { + "name": "BUN_DEBUG_QUIET_LOGS", + "value": "1", + }, + { + "name": "BUN_DEBUG_jest", + "value": "1", + }, + { + "name": "BUN_GARBAGE_COLLECTOR_LEVEL", + "value": "2", + }, + ], "console": "internalConsole", }, ], diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f1ebe7541ba7..58ded00a0a4f4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,9 +12,11 @@ "search.exclude": { "node_modules": true, ".git": true, - "src/bun.js/WebKit": true, - "src/deps/*/**": true, + "vendor/*/**": true, "test/node.js/upstream": true, + // This will fill up your whole search history. + "test/js/node/test/fixtures": true, + "test/js/node/test/common": true, }, "search.followSymlinks": false, "search.useIgnoreFiles": true, @@ -27,12 +29,13 @@ // Zig "zig.initialSetupDone": true, "zig.buildOption": "build", - "zig.buildArgs": ["-Dgenerated-code=./build/codegen"], + "zig.zls.zigLibPath": "${workspaceFolder}/vendor/zig/lib", + "zig.buildArgs": ["-Dgenerated-code=./build/debug/codegen"], "zig.zls.buildOnSaveStep": "check", // "zig.zls.enableBuildOnSave": true, // "zig.buildOnSave": true, "zig.buildFilePath": "${workspaceFolder}/build.zig", - "zig.path": "${workspaceFolder}/.cache/zig/zig.exe", + "zig.path": "${workspaceFolder}/vendor/zig/zig.exe", "zig.formattingProvider": "zls", "zig.zls.enableInlayHints": false, "[zig]": { @@ -41,8 +44,11 @@ "editor.defaultFormatter": "ziglang.vscode-zig", }, - // C++ + // lldb + "lldb.launch.initCommands": ["command source ${workspaceFolder}/.lldbinit"], "lldb.verboseLogging": false, + + // C++ "cmake.configureOnOpen": false, "C_Cpp.errorSquiggles": "enabled", "[cpp]": { @@ -54,6 +60,7 @@ "[h]": { "editor.defaultFormatter": "xaver.clang-format", }, + "clangd.arguments": ["-header-insertion=never"], // JavaScript "prettier.enable": true, @@ -131,6 +138,7 @@ }, "files.associations": { "*.idl": "cpp", + "array": "cpp", }, "C_Cpp.files.exclude": { "**/.vscode": true, diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f7c65e31fe81..17be34f43a351 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1452 +1,41 @@ -cmake_minimum_required(VERSION 3.22) -cmake_policy(SET CMP0091 NEW) -cmake_policy(SET CMP0067 NEW) +cmake_minimum_required(VERSION 3.24) +message(STATUS "Configuring Bun") -set(Bun_VERSION "1.1.16") -set(WEBKIT_TAG 64d04ec1a65d91326c5f2298b9c7d05b56125252) - -set(BUN_WORKDIR "${CMAKE_CURRENT_BINARY_DIR}") -message(STATUS "Configuring Bun ${Bun_VERSION} in ${BUN_WORKDIR}") - -set(CMAKE_COLOR_DIAGNOSTICS ON) -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_C_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_C_STANDARD_REQUIRED ON) - -# WebKit uses -std=gnu++20 on non-macOS non-Windows -# If we do not set this, it will crash at startup on the first memory allocation. -if(NOT WIN32 AND NOT APPLE) - set(CMAKE_CXX_EXTENSIONS ON) -endif() - -# --- Build Type --- -if(NOT CMAKE_BUILD_TYPE) - message(WARNING "No CMAKE_BUILD_TYPE value specified, defaulting to Debug.\nSet a build type with -DCMAKE_BUILD_TYPE=") - set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build (Debug, Release)" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE MATCHES "^(Debug|Release)$") - message(FATAL_ERROR - "Invalid CMAKE_BUILD_TYPE value specified: ${CMAKE_BUILD_TYPE}\n" - "CMAKE_BUILD_TYPE must be Debug or Release.") - endif() - - message(STATUS "The CMake build type is: ${CMAKE_BUILD_TYPE}") -endif() - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(DEBUG ON) - set(DEFAULT_ZIG_OPTIMIZE "Debug") - set(bun "bun-debug") - - # COMPILE_COMMANDS - set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - set(DEBUG OFF) - set(DEFAULT_ZIG_OPTIMIZE "ReleaseFast") - - if(WIN32) - # lld-link will strip it for you, so we can build directly to bun.exe - set(bun "bun") - - # TODO(@paperdave): Remove this - # it is enabled for the time being to make sure to catch more bugs in the experimental windows builds - set(DEFAULT_ZIG_OPTIMIZE "ReleaseSafe") - else() - if(ZIG_OPTIMIZE STREQUAL "Debug") - set(bun "bun-debug") - else() - set(bun "bun-profile") - endif() - endif() -endif() - -# --- MacOS SDK --- -if(APPLE AND DEFINED ENV{CI}) - set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") -endif() - -if(APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET) - execute_process(COMMAND xcrun --show-sdk-path OUTPUT_VARIABLE SDKROOT) - string(STRIP ${SDKROOT} SDKROOT) - message(STATUS "MacOS SDK path: ${SDKROOT}") - SET(CMAKE_OSX_SYSROOT ${SDKROOT}) - - execute_process(COMMAND xcrun --sdk macosx --show-sdk-version OUTPUT_VARIABLE MACOSX_DEPLOYMENT_TARGET) - string(STRIP ${MACOSX_DEPLOYMENT_TARGET} MACOSX_DEPLOYMENT_TARGET) - set(CMAKE_OSX_DEPLOYMENT_TARGET ${MACOSX_DEPLOYMENT_TARGET}) - - # Check if current version of macOS is less than the deployment target and if so, raise an error - execute_process(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOS_VERSION) - string(STRIP ${MACOS_VERSION} MACOS_VERSION) - - if(MACOS_VERSION VERSION_LESS ${MACOSX_DEPLOYMENT_TARGET}) - message(WARNING - "The current version of macOS (${MACOS_VERSION}) is less than the deployment target (${MACOSX_DEPLOYMENT_TARGET}).\n" - "The build will be incompatible with your current device due to mismatches in `icucore` versions.\n" - "To fix this, please either:\n" - " - Upgrade to at least macOS ${MACOSX_DEPLOYMENT_TARGET}\n" - " - Use `xcode-select` to switch to an SDK version <= ${MACOS_VERSION}\n" - " - Set CMAKE_OSX_DEPLOYMENT_TARGET=${MACOS_VERSION} (make sure to build all dependencies with this variable set too)" - ) - endif() -endif() - -if(APPLE) - message(STATUS "Building for macOS v${CMAKE_OSX_DEPLOYMENT_TARGET}") -endif() - -# --- LLVM --- -# This detection is a little overkill, but it ensures that the set LLVM_VERSION matches under -# any case possible. Sorry for the complexity... -# -# Bun and WebKit must be compiled with the same compiler, so we do as much as we can to ensure that -# the compiler used for the prebuilt WebKit is the same as we install as a dependency. -# -# It has to be done before project() is called, so that CMake doesnt pick a compiler for us, but even then -# we do some extra work afterwards to double-check, and we will rerun BUN_FIND_LLVM if the compiler did not match. -# -# If the user passes -DLLVM_PREFIX, most of this logic is skipped, but we still warn if invalid. -set(LLVM_VERSION 16) - -macro(BUN_FIND_LLVM) - find_program( - _LLVM_CXX_PATH - NAMES clang++-${LLVM_VERSION} clang++ - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s clang++ binary. Please pass -DLLVM_PREFIX with the path to LLVM" - ) - - if(NOT _LLVM_CXX_PATH) - message(FATAL_ERROR "Could not find LLVM ${LLVM_VERSION}, search paths: ${PLATFORM_LLVM_SEARCH_PATHS}") - endif() - - set(CMAKE_CXX_COMPILER "${_LLVM_CXX_PATH}") - find_program( - _LLVM_C_PATH - NAMES clang-${LLVM_VERSION} clang - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s clang binary. Please pass -DLLVM_PREFIX with the path to LLVM" - ) - - if(NOT _LLVM_C_PATH) - message(FATAL_ERROR "Could not find LLVM ${LLVM_VERSION}, search paths: ${PLATFORM_LLVM_SEARCH_PATHS}") - endif() - - set(CMAKE_C_COMPILER "${_LLVM_C_PATH}") - - find_program( - STRIP - NAMES llvm-strip - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-strip binary" - ) - find_program( - STRIP - NAMES strip - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-strip binary" - ) - find_program( - DSYMUTIL - NAMES dsymutil - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s dsymutil binary" - ) - find_program( - AR - NAMES llvm-ar - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-ar binary" - ) - find_program( - AR - NAMES ar - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-ar binary" - ) - find_program( - RANLIB - NAMES llvm-ranlib - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-ar binary" - ) - - execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE _tmp) - string(REGEX MATCH "version ([0-9]+)\\.([0-9]+)\\.([0-9]+)" CMAKE_CXX_COMPILER_VERSION "${_tmp}") - set(CMAKE_CXX_COMPILER_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") -endmacro() - -if(UNIX) - if(LLVM_PREFIX) - set(PLATFORM_LLVM_SEARCH_PATHS ${LLVM_PREFIX}/bin) - else() - set(PLATFORM_LLVM_SEARCH_PATHS /usr/lib/llvm-${LLVM_VERSION}/bin /usr/lib/llvm${LLVM_VERSION}/bin /usr/bin /usr/local/bin) - - if(APPLE) - set(PLATFORM_LLVM_SEARCH_PATHS /opt/homebrew/opt/llvm@${LLVM_VERSION}/bin /opt/homebrew/bin ${PLATFORM_LLVM_SEARCH_PATHS}) - endif() - endif() - - if(CMAKE_CXX_COMPILER) - set(_LLVM_CXX_PATH "${CMAKE_CXX_COMPILER}") - endif() - - if(CMAKE_C_COMPILER) - set(_LLVM_C_PATH "${CMAKE_C_COMPILER}") - endif() - - BUN_FIND_LLVM() -else() - # Windows uses Clang-CL - # TODO: good configuration in this regard. -G Ninja will pick clang-cl if possible, which should be fine for most users. - if(NOT CMAKE_C_COMPILER) - set(CMAKE_C_COMPILER "clang-cl") - endif() - - if(NOT CMAKE_CXX_COMPILER) - set(CMAKE_CXX_COMPILER "clang-cl") - endif() - - find_program( - STRIP - NAMES llvm-strip - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-strip binary" - ) - find_program( - AR - NAMES llvm-ar - PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS} - DOC "Path to LLVM ${LLVM_VERSION}'s llvm-ar binary" - ) -endif() - -project(Bun VERSION "${Bun_VERSION}") - -# if(MSVC) -# message(FATAL_ERROR "Bun does not support building with MSVC. Please use `cmake -G Ninja` with LLVM ${LLVM_VERSION} and Ninja.") -# endif() - -# More effort to prevent using the wrong C++ compiler -if(UNIX) - if((NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR(NOT CMAKE_CXX_COMPILER_VERSION MATCHES "^${LLVM_VERSION}\.")) - # Attempt to auto-correct the compiler - message(STATUS "Compiler mismatch, attempting to auto-correct") - unset(_LLVM_CXX_PATH) - BUN_FIND_LLVM() - - if((NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR(NOT CMAKE_CXX_COMPILER_VERSION MATCHES "^${LLVM_VERSION}\.")) - message(WARNING "Expected LLVM ${LLVM_VERSION} as the C++ compiler, build may fail or break at runtime.") - endif() - endif() -endif() - -message(STATUS "C++ Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} at ${CMAKE_CXX_COMPILER}") - -# --- End LLVM --- -if(NOT WIN32) - set(SHELL "bash") - set(SCRIPT_EXTENSION "sh") -else() - set(SCRIPT_EXTENSION "ps1") - - # pwsh is the new powershell, powershell is the old one. - find_program(SHELL NAMES pwsh powershell) -endif() - -set(DEFAULT_ON_UNLESS_APPLE ON) - -if(APPLE) - set(DEFAULT_ON_UNLESS_APPLE OFF) -endif() - -set(CI OFF) - -if(DEFINED ENV{CI} OR DEFINED ENV{GITHUB_ACTIONS}) - set(CI ON) -endif() - -set(DEFAULT_USE_STATIC_LIBATOMIC ON) -set(DEFAULT_USE_DEBUG_JSC, OFF) - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(DEFAULT_USE_DEBUG_JSC ON) - set(DEFAULT_LTO OFF) -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - if(CI) - set(DEFAULT_LTO ON) - else() - set(DEFAULT_LTO OFF) - endif() -endif() - -if(WIN32) - set(DEFAULT_USE_DEBUG_JSC OFF) -endif() - -if(UNIX AND NOT APPLE) - execute_process(COMMAND grep -w "NAME" /etc/os-release OUTPUT_VARIABLE LINUX_DISTRO) - - if(${LINUX_DISTRO} MATCHES "NAME=\"(Arch|Manjaro|Artix) Linux\"|NAME=\"openSUSE Tumbleweed\"\n") - set(DEFAULT_USE_STATIC_LIBATOMIC OFF) - endif() -endif() - -# -- Build Flags -- -option(USE_STATIC_SQLITE "Statically link SQLite?" ${DEFAULT_ON_UNLESS_APPLE}) -option(USE_CUSTOM_ZLIB "Use Bun's recommended version of zlib" ON) -option(USE_CUSTOM_BORINGSSL "Use Bun's recommended version of BoringSSL" ON) -option(USE_CUSTOM_LIBARCHIVE "Use Bun's recommended version of libarchive" ON) -option(USE_CUSTOM_MIMALLOC "Use Bun's recommended version of Mimalloc" ON) -option(USE_CUSTOM_ZSTD "Use Bun's recommended version of zstd" ON) -option(USE_CUSTOM_CARES "Use Bun's recommended version of c-ares" ON) -option(USE_CUSTOM_LOLHTML "Use Bun's recommended version of lolhtml" ON) -option(USE_CUSTOM_TINYCC "Use Bun's recommended version of tinycc" ON) -option(USE_CUSTOM_LIBUV "Use Bun's recommended version of libuv (Windows only)" ON) -option(USE_CUSTOM_LSHPACK "Use Bun's recommended version of ls-hpack" ON) -option(USE_BASELINE_BUILD "Build Bun for baseline (older) CPUs" OFF) -option(USE_SYSTEM_ICU "Use the system-provided libicu. May fix startup crashes when building WebKit yourself." OFF) - -option(USE_VALGRIND "Build Bun with Valgrind support (Linux only)" OFF) - -option(ZIG_OPTIMIZE "Optimization level for Zig" ${DEFAULT_ZIG_OPTIMIZE}) -option(USE_DEBUG_JSC "Enable assertions and use a debug build of JavaScriptCore" ${DEFAULT_USE_DEBUG_JSC}) -option(USE_UNIFIED_SOURCES "Use unified sources to speed up the build" OFF) -option(USE_STATIC_LIBATOMIC "Statically link libatomic, requires the presence of libatomic.a" ${DEFAULT_USE_STATIC_LIBATOMIC}) - -option(USE_LTO "Enable Link-Time Optimization" ${DEFAULT_LTO}) - -option(BUN_TIDY_ONLY "Only run clang-tidy" OFF) -option(BUN_TIDY_ONLY_EXTRA " Only run clang-tidy, with extra checks for local development" OFF) - -if(NOT ZIG_LIB_DIR) - cmake_path(SET ZIG_LIB_DIR NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/src/deps/zig/lib") -endif() - -if(USE_VALGRIND) - # Disable SIMD - set(USE_BASELINE_BUILD ON) - - if(ARCH STREQUAL "x86_64") - # This is for picohttpparser - # Valgrind cannot handle SSE4.2 instructions - add_compile_definitions("__SSE4_2__=0") - endif() -endif() - -if(NOT CANARY) - set(CANARY 0) -endif() - -if(NOT ENABLE_LOGS) - set(ENABLE_LOGS false) -endif() - -if(NOT ZIG_OPTIMIZE) - set(ZIG_OPTIMIZE ${DEFAULT_ZIG_OPTIMIZE}) -endif() - -set(ERROR_LIMIT 100 CACHE STRING "Maximum number of errors to show when compiling C++ code") - -set(ARCH x86_64) -set(HOMEBREW_PREFIX "/usr/local") - -if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|arm") - set(ARCH aarch64) - set(HOMEBREW_PREFIX "/opt/homebrew") -endif() - -if(NOT CPU_TARGET) - if(DEFINED ENV{CPU_TARGET}) - set(CPU_TARGET $ENV{CPU_TARGET}) - else() - set(CPU_TARGET "native" CACHE STRING "CPU target for the compiler" FORCE) - - if(ARCH STREQUAL "x86_64") - if(USE_BASELINE_BUILD) - set(CPU_TARGET "nehalem") - else() - set(CPU_TARGET "haswell") - endif() - endif() - endif() -endif() - -message(STATUS "Building for CPU Target: ${CPU_TARGET}") - -if(NOT ZIG_TARGET) - set(ZIG_TARGET "native") - - if(WIN32) - set(ZIG_TARGET "${ARCH}-windows-msvc") - endif() -endif() - -set(CONFIGURE_DEPENDS "CONFIGURE_DEPENDS") - -if(NO_CONFIGURE_DEPENDS) - set(CONFIGURE_DEPENDS "") -endif() - -# --- CLI Paths --- -set(REQUIRED_IF_NOT_ONLY_CPP_OR_LINK "") - -if(NOT BUN_CPP_ONLY AND NOT BUN_LINK_ONLY) - set(REQUIRED_IF_NOT_ONLY_CPP_OR_LINK "REQUIRED") -endif() - -# Zig Compiler -function(validate_zig validator_result_var item) - set(${validator_result_var} FALSE PARENT_SCOPE) - - # We will allow any valid zig compiler, as long as it contains some text from `zig zen` - # Ideally we would do a version or feature check, but that would be quite slow - execute_process(COMMAND ${item} zen OUTPUT_VARIABLE ZIG_ZEN_OUTPUT) - - if(ZIG_ZEN_OUTPUT MATCHES "Together we serve the users") - set(${validator_result_var} TRUE PARENT_SCOPE) - else() - set(${validator_result_var} FALSE PARENT_SCOPE) - endif() -endfunction() - -if(ZIG_COMPILER) - if(ZIG_COMPILER STREQUAL "system") - message(STATUS "Using system Zig compiler") - unset(ZIG_COMPILER_) - endif() - - find_program(ZIG_COMPILER_ zig ${REQUIRED_IF_NOT_ONLY_CPP_OR_LINK} DOC "Path to the Zig compiler" VALIDATOR validate_zig) - set(ZIG_COMPILER "${ZIG_COMPILER_}") - message(STATUS "Found Zig Compiler: ${ZIG_COMPILER}") -elseif(NOT BUN_CPP_ONLY AND NOT BUN_LINK_ONLY AND NOT BUN_TIDY_ONLY AND NOT BUN_TIDY_ONLY_EXTRA) - execute_process( - COMMAND "${SHELL}" - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/download-zig.${SCRIPT_EXTENSION}" - ) - set(ZIG_COMPILER "${CMAKE_CURRENT_SOURCE_DIR}/.cache/zig/zig") - - if(WIN32) - set(ZIG_COMPILER "${ZIG_COMPILER}.exe") - endif() - - if(NOT EXISTS "${ZIG_COMPILER}") - unset(ZIG_COMPILER) - message(FATAL_ERROR "Auto-installation of Zig failed. Please pass -DZIG_COMPILER=system or a path to the Zig") - endif() - - message(STATUS "Installed Zig Compiler: ${ZIG_COMPILER}") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "build.zig") -endif() - -# Bun -find_program(BUN_EXECUTABLE bun ${REQUIRED_IF_NOT_ONLY_CPP_OR_LINK} DOC "Path to an already built release of Bun") -message(STATUS "Found Bun: ${BUN_EXECUTABLE}") - -if(WIN32 AND NO_CODEGEN) - # TODO(@paperdave): remove this, see bun-windows.yml's comment. - set(BUN_EXECUTABLE "echo") -endif() - -# Prettier -find_program(PRETTIER prettier DOC "Path to prettier" PATHS ./node_modules/.bin ENV PATH) - -# Esbuild (TODO: switch these to "bun build") -find_program(ESBUILD esbuild DOC "Path to esbuild" PATHS ./node_modules/.bin ENV PATH) - -# Ruby (only needed for unified sources) -if(USE_UNIFIED_SOURCES) - # ruby 'WebKit/Source/WTF/Scripts/generate-unified-source-bundles.rb' source_list.txt --source-tree-path . --derived-sources-path build/unified-sources - find_program(RUBY ruby DOC "Path to ruby") -endif() - -# CCache -find_program(CCACHE_PROGRAM sccache) -find_program(CCACHE_PROGRAM ccache) - -if(CCACHE_PROGRAM) - set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - message(STATUS "Using ccache: ${CCACHE_PROGRAM}") -endif() - -# --- WebKit --- -# WebKit is either prebuilt and distributed via NPM, or you can pass WEBKIT_DIR to use a local build. -# We cannot include their CMake build files (TODO: explain why, for now ask @paperdave why) -# -# On Unix, this will pull from NPM the single package that is needed and use that -if(WIN32) - set(STATIC_LIB_EXT "lib") - set(libJavaScriptCore "JavaScriptCore") - set(libWTF "WTF") -else() - set(STATIC_LIB_EXT "a") - set(libJavaScriptCore "libJavaScriptCore") - set(libWTF "libWTF") -endif() - -if(NOT WEBKIT_DIR) - set(BUN_WEBKIT_PACKAGE_NAME_SUFFIX "") - set(ASSERT_ENABLED "0") - - if(USE_DEBUG_JSC) - add_compile_definitions("BUN_DEBUG=1") - set(BUN_WEBKIT_PACKAGE_NAME_SUFFIX "-debug") - set(ASSERT_ENABLED "1") - elseif(NOT DEBUG AND NOT WIN32) - # Avoid waiting for LTO in local release builds outside of CI - if(USE_LTO) - set(BUN_WEBKIT_PACKAGE_NAME_SUFFIX "-lto") - else() - set(BUN_WEBKIT_PACKAGE_NAME_SUFFIX "") - endif() - - set(ASSERT_ENABLED "0") - endif() - - if(WIN32) - set(BUN_WEBKIT_PACKAGE_PLATFORM "windows") - elseif(APPLE) - set(BUN_WEBKIT_PACKAGE_PLATFORM "macos") - else() - set(BUN_WEBKIT_PACKAGE_PLATFORM "linux") - endif() - - if(ARCH STREQUAL "x86_64") - set(BUN_WEBKIT_PACKAGE_ARCH "amd64") - elseif(ARCH MATCHES "aarch64|arm64|arm") - set(BUN_WEBKIT_PACKAGE_ARCH "arm64") - endif() - - set(BUN_WEBKIT_PACKAGE_NAME "bun-webkit-${BUN_WEBKIT_PACKAGE_PLATFORM}-${BUN_WEBKIT_PACKAGE_ARCH}${BUN_WEBKIT_PACKAGE_NAME_SUFFIX}") - - message(STATUS "Using Pre-built WebKit: ${BUN_WEBKIT_PACKAGE_NAME}") - execute_process( - COMMAND "${SHELL}" - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/download-webkit.${SCRIPT_EXTENSION}" - "${BUN_WORKDIR}/bun-webkit" - "${WEBKIT_TAG}" - "${BUN_WEBKIT_PACKAGE_NAME}" - WORKING_DIRECTORY ${BUN_WORKDIR} - ) - - if(NOT EXISTS "${BUN_WORKDIR}/bun-webkit") - message(FATAL_ERROR "Prebuilt WebKit package ${BUN_WEBKIT_PACKAGE_NAME} failed to install") - endif() - - set(WEBKIT_INCLUDE_DIR "${BUN_WORKDIR}/bun-webkit/include") - - if(APPLE) - set(ICU_INCLUDE_DIR "") - else() - set(ICU_INCLUDE_DIR "${BUN_WORKDIR}/bun-webkit/include/wtf/unicode") - endif() - - set(WEBKIT_LIB_DIR "${BUN_WORKDIR}/bun-webkit/lib") -elseif(WEBKIT_DIR STREQUAL "omit") - message(STATUS "Not using WebKit. This is only valid if you are only trying to build Zig code") -else() - # Expected to be WebKit/WebKitBuild/${CMAKE_BUILD_TYPE} - if(EXISTS "${WEBKIT_DIR}/cmakeconfig.h") - # You may need to run: - # make jsc-compile-debug jsc-copy-headers - include_directories( - "${WEBKIT_DIR}/" - "${WEBKIT_DIR}/JavaScriptCore/Headers/JavaScriptCore" - "${WEBKIT_DIR}/JavaScriptCore/PrivateHeaders" - "${WEBKIT_DIR}/bmalloc/Headers" - "${WEBKIT_DIR}/WTF/Headers" - ) - set(WEBKIT_LIB_DIR "${WEBKIT_DIR}/lib") - - if(USE_DEBUG_JSC) - add_compile_definitions("BUN_DEBUG=1") - set(ASSERT_ENABLED "1") - endif() - - message(STATUS "Using WebKit from ${WEBKIT_DIR}") - else() - if(NOT EXISTS "${WEBKIT_DIR}/lib/${libWTF}.${STATIC_LIB_EXT}" OR NOT EXISTS "${WEBKIT_DIR}/lib/${libJavaScriptCore}.${STATIC_LIB_EXT}") - if(WEBKIT_DIR MATCHES "src/bun.js/WebKit$") - message(FATAL_ERROR "WebKit directory ${WEBKIT_DIR} does not contain all the required files for Bun. Did you forget to init submodules?") - endif() - - message(FATAL_ERROR "WebKit directory ${WEBKIT_DIR} does not contain all the required files for Bun. Expected a path to the oven-sh/WebKit repository, or a path to a folder containing `include` and `lib`.") - endif() - - set(WEBKIT_INCLUDE_DIR "${WEBKIT_DIR}/include") - set(WEBKIT_LIB_DIR "${WEBKIT_DIR}/lib") - - message(STATUS "Using specified WebKit directory: ${WEBKIT_DIR}") - - set(ASSERT_ENABLED "0") - message(STATUS "WebKit assertions: OFF") - endif() -endif() - -# --- CMake Macros --- - -# Append the given dependencies to the source file -macro(WEBKIT_ADD_SOURCE_DEPENDENCIES _source _deps) - set(_tmp) - get_source_file_property(_tmp ${_source} OBJECT_DEPENDS) - - if(NOT _tmp) - set(_tmp "") - endif() - - foreach(f ${_deps}) - list(APPEND _tmp "${f}") - endforeach() - - set_source_files_properties(${_source} PROPERTIES OBJECT_DEPENDS "${_tmp}") - unset(_tmp) -endmacro() - -# --- BUILD --- -set(BUN_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src") -set(BUN_DEPS_DIR "${BUN_SRC}/deps") -set(BUN_CODEGEN_SRC "${BUN_SRC}/codegen") - -if(NOT BUN_DEPS_OUT_DIR) - set(BUN_DEPS_OUT_DIR "${BUN_DEPS_DIR}") -endif() - -set(BUN_RAW_SOURCES, "") - -file(GLOB BUN_CPP ${CONFIGURE_DEPENDS} - "${BUN_SRC}/deps/*.cpp" - "${BUN_SRC}/io/*.cpp" - "${BUN_SRC}/bun.js/modules/*.cpp" - "${BUN_SRC}/bun.js/bindings/*.cpp" - "${BUN_SRC}/bun.js/bindings/webcore/*.cpp" - "${BUN_SRC}/bun.js/bindings/sqlite/*.cpp" - "${BUN_SRC}/bun.js/bindings/webcrypto/*.cpp" - "${BUN_SRC}/bun.js/bindings/webcrypto/*/*.cpp" - "${BUN_SRC}/deps/picohttpparser/picohttpparser.c" -) -list(APPEND BUN_RAW_SOURCES ${BUN_CPP}) - -# -- Brotli -- -set(BROTLI_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/deps/brotli") -file(GLOB BROTLI_FILES ${CONFIGURE_DEPENDS} - "${BROTLI_SRC}/common/*.c" - "${BROTLI_SRC}/enc/*.c" - "${BROTLI_SRC}/dec/*.c" -) -list(APPEND BUN_RAW_SOURCES ${BROTLI_FILES}) -include_directories("${BUN_DEPS_DIR}/brotli/include") - -# -- uSockets -- -set(USOCKETS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/packages/bun-usockets/src") -file(GLOB USOCKETS_FILES ${CONFIGURE_DEPENDS} - "${USOCKETS_SRC}/*.c" - "${USOCKETS_SRC}/eventing/*.c" - "${USOCKETS_SRC}/internal/*.c" - "${USOCKETS_SRC}/crypto/*.c" - "${USOCKETS_SRC}/crypto/*.cpp" -) -list(APPEND BUN_RAW_SOURCES ${USOCKETS_FILES}) - -# --- Classes Generator --- -file(GLOB BUN_CLASSES_TS ${CONFIGURE_DEPENDS} - "${BUN_SRC}/bun.js/*.classes.ts" - "${BUN_SRC}/bun.js/api/*.classes.ts" - "${BUN_SRC}/bun.js/test/*.classes.ts" - "${BUN_SRC}/bun.js/webcore/*.classes.ts" - "${BUN_SRC}/bun.js/node/*.classes.ts" -) -add_custom_command( - OUTPUT "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.h" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.cpp" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses+lazyStructureHeader.h" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses+DOMClientIsoSubspaces.h" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses+DOMIsoSubspaces.h" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses+lazyStructureImpl.h" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.zig" - COMMAND ${BUN_EXECUTABLE} run "${BUN_CODEGEN_SRC}/generate-classes.ts" ${BUN_CLASSES_TS} "${BUN_WORKDIR}/codegen" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - MAIN_DEPENDENCY "${BUN_CODEGEN_SRC}/generate-classes.ts" - DEPENDS ${BUN_CLASSES_TS} - VERBATIM - COMMENT "Generating *.classes.ts bindings" -) -list(APPEND BUN_RAW_SOURCES "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.cpp") - -# --- JSSink Generator --- -add_custom_command( - OUTPUT "${BUN_WORKDIR}/codegen/JSSink.cpp" - "${BUN_WORKDIR}/codegen/JSSink.h" - COMMAND ${BUN_EXECUTABLE} run "src/codegen/generate-jssink.ts" "${BUN_WORKDIR}/codegen" - VERBATIM - MAIN_DEPENDENCY "src/codegen/generate-jssink.ts" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Generating JSSink" - USES_TERMINAL -) -list(APPEND BUN_RAW_SOURCES "${BUN_WORKDIR}/codegen/JSSink.cpp") - -# --- .lut.h Generator --- -set(BUN_OBJECT_LUT_SOURCES - bun.js/bindings/BunObject.cpp - bun.js/bindings/ZigGlobalObject.lut.txt - bun.js/bindings/JSBuffer.cpp - bun.js/bindings/BunProcess.cpp - bun.js/bindings/ProcessBindingConstants.cpp - bun.js/bindings/ProcessBindingNatives.cpp -) -set(BUN_OBJECT_LUT_OUTPUTS "") -set(BUN_HASH_LUT_GENERATOR "${BUN_CODEGEN_SRC}/create-hash-table.ts") - -if(NOT BUN_LINK_ONLY) - macro(GENERATE_HASH_LUT _input _output _display_name) - if(NOT NO_CODEGEN) - add_custom_command( - OUTPUT ${_output} - MAIN_DEPENDENCY ${BUN_HASH_LUT_GENERATOR} - DEPENDS ${_input} - COMMAND ${BUN_EXECUTABLE} run ${BUN_HASH_LUT_GENERATOR} ${_input} ${_output} - VERBATIM - COMMENT "Generating ${_display_name}" - ) - endif() - - list(APPEND BUN_OBJECT_LUT_OUTPUTS "${_output}") - - # list(APPEND Bun_HEADERS ${_output}) - WEBKIT_ADD_SOURCE_DEPENDENCIES(${_input} ${_output}) - endmacro() - - foreach(_file ${BUN_OBJECT_LUT_SOURCES}) - if(NOT EXISTS "${BUN_SRC}/${_file}") - message(FATAL_ERROR "Could not find ${_file} needed for LUT generation") - endif() - - get_filename_component(_name ${_file} NAME_WE) - - # workaround for ZigGlobalObject - if(_name MATCHES "ZigGlobalObject") - set(_name "ZigGlobalObject") - endif() - - GENERATE_HASH_LUT(${BUN_SRC}/${_file} ${BUN_WORKDIR}/codegen/${_name}.lut.h ${_name}.lut.h) - endforeach() - - WEBKIT_ADD_SOURCE_DEPENDENCIES(${BUN_SRC}/bun.js/bindings/ZigGlobalObject.cpp ${BUN_WORKDIR}/codegen/ZigGlobalObject.lut.h) -endif() - -# --- Identifier Cache --- -if(NOT NO_CODEGEN) - set(BUN_IDENTIFIER_CACHE_OUT - "${BUN_SRC}/js_lexer/id_continue_bitset.blob" - "${BUN_SRC}/js_lexer/id_continue_bitset.meta.blob" - "${BUN_SRC}/js_lexer/id_start_bitset.blob" - "${BUN_SRC}/js_lexer/id_start_bitset.meta.blob") - add_custom_command( - OUTPUT ${BUN_IDENTIFIER_CACHE_OUT} - MAIN_DEPENDENCY "${BUN_SRC}/js_lexer/identifier_data.zig" - DEPENDS "${BUN_SRC}/js_lexer/identifier_cache.zig" - COMMAND ${ZIG_COMPILER} run "--zig-lib-dir" "${ZIG_LIB_DIR}" "${BUN_SRC}/js_lexer/identifier_data.zig" - VERBATIM - COMMENT "Building Identifier Cache" - ) -endif() - -# --- Bundled TS/JS --- -# Note: It's not worth doing this in parallel at the CMake/Ninja level, because this bundling -# requires all the JS files to be known, but also Bun will use all cores during bundling anyways. -if(NOT NO_CODEGEN) - file(GLOB BUN_TS_MODULES ${CONFIGURE_DEPENDS} - "${BUN_SRC}/js/node/*.ts" - "${BUN_SRC}/js/node/*.js" - "${BUN_SRC}/js/bun/*.ts" - "${BUN_SRC}/js/bun/*.js" - "${BUN_SRC}/js/builtins/*.ts" - "${BUN_SRC}/js/builtins/*.js" - "${BUN_SRC}/js/thirdparty/*.js" - "${BUN_SRC}/js/thirdparty/*.ts" - "${BUN_SRC}/js/internal/*.js" - "${BUN_SRC}/js/internal/*.ts" - "${BUN_SRC}/js/node/*.js" - "${BUN_SRC}/js/node/*.ts" - "${BUN_SRC}/js/thirdparty/*.js" - "${BUN_SRC}/js/thirdparty/*.ts" - "${BUN_SRC}/js/internal-for-testing.ts" - ) - - file(GLOB CODEGEN_FILES ${CONFIGURE_DEPENDS} "${BUN_CODEGEN_SRC}/*.ts") - - add_custom_command( - OUTPUT - "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.cpp" - "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.h" - "${BUN_WORKDIR}/codegen/InternalModuleRegistryConstants.h" - "${BUN_WORKDIR}/codegen/InternalModuleRegistry+createInternalModuleById.h" - "${BUN_WORKDIR}/codegen/InternalModuleRegistry+enum.h" - "${BUN_WORKDIR}/codegen/InternalModuleRegistry+numberOfModules.h" - "${BUN_WORKDIR}/codegen/NativeModuleImpl.h" - "${BUN_WORKDIR}/codegen/ResolvedSourceTag.zig" - "${BUN_WORKDIR}/codegen/SyntheticModuleType.h" - "${BUN_WORKDIR}/codegen/GeneratedJS2Native.h" - "${BUN_SRC}/bun.js/bindings/GeneratedJS2Native.zig" - COMMAND ${BUN_EXECUTABLE} run "${BUN_SRC}/codegen/bundle-modules.ts" "--debug=${DEBUG}" "${BUN_WORKDIR}" - DEPENDS ${BUN_TS_MODULES} ${CODEGEN_FILES} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Bundling JS" - ) -endif() - -WEBKIT_ADD_SOURCE_DEPENDENCIES( - "${BUN_SRC}/bun.js/bindings/InternalModuleRegistry.cpp" - "${BUN_WORKDIR}/codegen/InternalModuleRegistryConstants.h" -) -list(APPEND BUN_RAW_SOURCES "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.cpp") - -# --- Peechy API --- -# if(NOT NO_CODEGEN) -# add_custom_command( -# OUTPUT "${BUN_SRC}/api/schema.js" -# "${BUN_SRC}/api/schema.d.ts" -# "${BUN_SRC}/api/schema.zig" -# WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" -# COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/node_modules/.bin/peechy" -# "--schema" "${BUN_SRC}/api/schema.peechy" -# "--esm" "${BUN_SRC}/api/schema.js" -# "--ts" "${BUN_SRC}/api/schema.d.ts" -# "--zig" "${BUN_SRC}/api/schema.zig" -# COMMAND "${ZIG_COMPILER}" "fmt" "src/api/schema.zig" -# COMMAND "${PRETTIER}" "--config=.prettierrc.cjs" "--write" "src/api/schema.js" "src/api/schema.d.ts" -# DEPENDS "${BUN_SRC}/api/schema.peechy" -# COMMENT "Building schema" -# ) -# add_custom_command( -# OUTPUT "${BUN_SRC}/analytics/analytics_schema.zig" -# WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" -# COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/node_modules/.bin/peechy" -# "--schema" "${BUN_SRC}/analytics/schema.peechy" -# "--zig" "${BUN_SRC}/analytics/analytics_schema.zig" -# COMMAND "${ZIG_COMPILER}" "fmt" "${BUN_SRC}/analytics/analytics_schema.zig" -# DEPENDS "${BUN_SRC}/api/schema.peechy" -# COMMENT "Building analytics_schema.zig" -# ) -# endif() - -# --- Runtime.js --- -if(NOT NO_CODEGEN) - add_custom_command( - OUTPUT "src/fallback.out.js" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND "${ESBUILD}" "--target=esnext" "--bundle" "src/fallback.ts" "--format=iife" "--platform=browser" "--minify" "--outfile=src/fallback.out.js" - DEPENDS "src/fallback.ts" - ) -endif() - -# --- Zig Object --- -file(GLOB ZIG_FILES - "${BUN_SRC}/*.zig" - "${BUN_SRC}/*/*.zig" - "${BUN_SRC}/*/*/*.zig" - "${BUN_SRC}/*/*/*/*.zig" - "${BUN_SRC}/*/*/*/*/*.zig" -) - -if(NOT BUN_ZIG_OBJ_DIR) - set(BUN_ZIG_OBJ_DIR "${BUN_WORKDIR}/CMakeFiles") -endif() - -get_filename_component(BUN_ZIG_OBJ_DIR "${BUN_ZIG_OBJ_DIR}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") - -set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o") - -set(USES_TERMINAL_NOT_IN_CI "") - -if(NOT CI) - set(USES_TERMINAL_NOT_IN_CI "USES_TERMINAL") -endif() - -if(NOT BUN_LINK_ONLY AND NOT BUN_CPP_ONLY) - add_custom_command( - OUTPUT "${BUN_ZIG_OBJ}" - COMMAND - "${ZIG_COMPILER}" "build" "obj" - "--zig-lib-dir" "${ZIG_LIB_DIR}" - "--prefix" "${BUN_ZIG_OBJ_DIR}" - "-Dgenerated-code=${BUN_WORKDIR}/codegen" - "-freference-trace=10" - "-Dversion=${Bun_VERSION}" - "-Dcanary=${CANARY}" - "-Doptimize=${ZIG_OPTIMIZE}" - "-Dcpu=${CPU_TARGET}" - "-Dtarget=${ZIG_TARGET}" - "-Denable_logs=${ENABLE_LOGS}" - DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/build.zig" - "${ZIG_FILES}" - "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.zig" - "${BUN_WORKDIR}/codegen/ResolvedSourceTag.zig" - "${BUN_IDENTIFIER_CACHE_OUT}" - "${BUN_SRC}/api/schema.zig" - "${BUN_SRC}/bun.js/bindings/GeneratedJS2Native.zig" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Building zig code" - VERBATIM - - # This is here to show Zig's progress indicator - ${USES_TERMINAL_NOT_IN_CI} - ) -endif() - -if(WIN32) - list(APPEND BUN_RAW_SOURCES "${BUN_SRC}/bun.js/bindings/windows/musl-memmem.c") - include_directories("${BUN_SRC}/bun.js/bindings/windows") -endif() - -if(NOT BUN_CPP_ARCHIVE) - # TODO: unified sources - set(BUN_SOURCES ${BUN_RAW_SOURCES}) -else() - # used by ci - set(BUN_SOURCES "") - add_link_options("${BUN_CPP_ARCHIVE}") -endif() - -# -- Windows resources (app icon) -- -if(CANARY GREATER 0) - set(Bun_VERSION_WITH_TAG "${Bun_VERSION}-canary.${CANARY}") -else() - set(Bun_VERSION_WITH_TAG "${Bun_VERSION}") -endif() - -if(WIN32) - set(BUN_ICO_PATH "${BUN_SRC}/bun.ico") - configure_file("${BUN_SRC}/windows-app-info.rc" "${BUN_WORKDIR}/CMakeFiles/windows-app-info.rc") - list(APPEND BUN_SOURCES "${BUN_WORKDIR}/CMakeFiles/windows-app-info.rc") -endif() - -# -- The Buntime™️ --- -if(BUN_TIDY_ONLY OR BUN_TIDY_ONLY_EXTRA) - add_library(${bun} OBJECT "${BUN_SOURCES}") -elseif(NOT BUN_CPP_ONLY) - add_executable(${bun} "${BUN_SOURCES}" "${BUN_ZIG_OBJ}") -else() - add_executable(${bun} "${BUN_SOURCES}") -endif() - -set_target_properties(${bun} PROPERTIES - CXX_STANDARD 20 - CXX_STANDARD_REQUIRED YES - CXX_EXTENSIONS YES - CXX_VISIBILITY_PRESET hidden - C_STANDARD 17 - C_STANDARD_REQUIRED YES - VISIBILITY_INLINES_HIDDEN YES -) - -add_compile_definitions( - - # TODO: are all of these variables strictly necessary? - "_HAS_EXCEPTIONS=0" - "LIBUS_USE_OPENSSL=1" - "UWS_HTTPRESPONSE_NO_WRITEMARK=1" - "LIBUS_USE_BORINGSSL=1" - "WITH_BORINGSSL=1" - "STATICALLY_LINKED_WITH_JavaScriptCore=1" - "STATICALLY_LINKED_WITH_WTF=1" - "STATICALLY_LINKED_WITH_BMALLOC=1" - "BUILDING_WITH_CMAKE=1" - "JSC_OBJC_API_ENABLED=0" - "BUN_SINGLE_THREADED_PER_VM_ENTRY_SCOPE=1" - "NAPI_EXPERIMENTAL=ON" - "NOMINMAX" - "IS_BUILD" - "BUILDING_JSCONLY__" - "BUN_DYNAMIC_JS_LOAD_PATH=\"${BUN_WORKDIR}/js\"" -) - -if(NOT ASSERT_ENABLED) - add_compile_definitions("NDEBUG=1") -else() - add_compile_definitions("ASSERT_ENABLED=1") -endif() - -if(ICU_INCLUDE_DIR) - include_directories(${ICU_INCLUDE_DIR}) -endif() - -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/packages/ - ${CMAKE_CURRENT_SOURCE_DIR}/packages/bun-usockets - ${CMAKE_CURRENT_SOURCE_DIR}/packages/bun-usockets/src - ${CMAKE_CURRENT_SOURCE_DIR}/src/bun.js/bindings - ${CMAKE_CURRENT_SOURCE_DIR}/src/bun.js/bindings/webcore - ${CMAKE_CURRENT_SOURCE_DIR}/src/bun.js/bindings/webcrypto - ${CMAKE_CURRENT_SOURCE_DIR}/src/bun.js/bindings/sqlite - ${CMAKE_CURRENT_SOURCE_DIR}/src/bun.js/modules - ${CMAKE_CURRENT_SOURCE_DIR}/src/js/builtins - ${CMAKE_CURRENT_SOURCE_DIR}/src/napi - ${CMAKE_CURRENT_SOURCE_DIR}/src/deps - ${CMAKE_CURRENT_SOURCE_DIR}/src/deps/picohttpparser - ${WEBKIT_INCLUDE_DIR} - "${BUN_WORKDIR}/codegen" +list(APPEND CMAKE_MODULE_PATH + ${CMAKE_SOURCE_DIR}/cmake + ${CMAKE_SOURCE_DIR}/cmake/targets + ${CMAKE_SOURCE_DIR}/cmake/tools + ${CMAKE_SOURCE_DIR}/cmake/analysis + ${CMAKE_SOURCE_DIR}/cmake/scripts ) -# -- BUN_CPP_ONLY Target -if(NOT BUN_CPP_ARCHIVE) - if(BUN_CPP_ONLY) - if(NOT WIN32) - string(REPLACE ";" ".o\n " BUN_OBJECT_LIST "${BUN_SOURCES}.o") - string(REPLACE "${BUN_WORKDIR}/" "CMakeFiles/${bun}.dir/" BUN_OBJECT_LIST "${BUN_OBJECT_LIST}") - string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "CMakeFiles/${bun}.dir/" BUN_OBJECT_LIST "${BUN_OBJECT_LIST}") - write_file("${BUN_WORKDIR}/compile-cpp-only.sh" - "#!/usr/bin/env bash\n" - "# this file is generated in CMakeLists.txt\n" - "set -ex\n" - "OBJ_LIST=(\n ${BUN_OBJECT_LIST}\n)\n" - "ninja \${OBJ_LIST[@]} $@\n" - "\"${AR}\" rcvs bun-cpp-objects.a \${OBJ_LIST[@]}\n" - "echo '-> bun-cpp-objects.a'\n" - ) - else() - string(REPLACE ";" ".obj\",\n \"" BUN_OBJECT_LIST "\"${BUN_SOURCES}.obj\"") - string(REPLACE "rc.obj" "rc.res" BUN_OBJECT_LIST "${BUN_OBJECT_LIST}") - string(REPLACE "${BUN_WORKDIR}/" "CMakeFiles/${bun}.dir/" BUN_OBJECT_LIST "${BUN_OBJECT_LIST}") - string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "CMakeFiles/${bun}.dir/" BUN_OBJECT_LIST "${BUN_OBJECT_LIST}") - write_file("${BUN_WORKDIR}/compile-cpp-only.ps1" - "# this file is generated in CMakeLists.txt\n" - "$ErrorActionPreference = \"Stop\"\n" - "$ObjectFiles=@(\n ${BUN_OBJECT_LIST}\n)\n" - "ninja @ObjectFiles @args\n" - "& \"${AR}\" rcvs bun-cpp-objects.a @ObjectFiles\n" - "Write-Host '-> bun-cpp-objects.a'\n" - ) - endif() - endif() -else() - set_target_properties(${bun} PROPERTIES LINKER_LANGUAGE CXX) -endif() - -# --- clang and linker flags --- -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - if(NOT WIN32) - target_compile_options(${bun} PUBLIC -O0 -g -g3 -ggdb -gdwarf-4 - -Werror=return-type - -Werror=return-stack-address - -Werror=implicit-function-declaration - -Werror=uninitialized - -Werror=conditional-uninitialized - -Werror=suspicious-memaccess - -Werror=move - -Werror=sometimes-uninitialized - -Werror=unused - -Wno-unused-function - -Werror - ) - else() - target_compile_options(${bun} PUBLIC /Od /Z7) - endif() - - add_compile_definitions("BUN_DEBUG=1") -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - set(LTO_FLAG "") - - if(NOT WIN32) - if(USE_LTO) - list(APPEND LTO_FLAG "-flto=full" "-emit-llvm") - endif() - - # Leave -Werror=unused off in release builds so we avoid errors from being used in ASSERT - target_compile_options(${bun} PUBLIC -O3 ${LTO_FLAG} -g1 - -Werror=return-type - -Werror=return-stack-address - -Werror=implicit-function-declaration - -Werror=uninitialized - -Werror=conditional-uninitialized - -Werror=suspicious-memaccess - -Werror=move - -Werror=sometimes-uninitialized - -Werror - ) - else() - set(LTO_LINK_FLAG "") - - if(USE_LTO) - # -emit-llvm seems to not be supported or under a different name on Windows. - list(APPEND LTO_FLAG "-flto=full") - list(APPEND LTO_LINK_FLAG "/LTCG") - endif() - - target_compile_options(${bun} PUBLIC /O2 ${LTO_FLAG}) - target_link_options(${bun} PUBLIC ${LTO_LINK_FLAG} /DEBUG:FULL) - endif() -endif() - -if(NOT CI AND NOT WIN32) - target_compile_options(${bun} PRIVATE -fdiagnostics-color=always) -endif() - -if(NOT CPU_TARGET STREQUAL "native") - # passing -march=native to clang will break older systems - # by default on x64, CPU_TARGET is set to "haswell" or "nehalem" depending on baseline - # on arm, this argument will not be passed. - target_compile_options(${bun} PUBLIC "-march=${CPU_TARGET}") -else() - if(APPLE AND ARCH STREQUAL "aarch64") - # On arm macOS, we can set it to a minimum of the M1 cpu set. this might be the default already. - target_compile_options(${bun} PUBLIC "-mcpu=apple-m1") - endif() -endif() - -target_compile_options(${bun} PUBLIC -ferror-limit=${ERROR_LIMIT}) - -if(WIN32) - add_compile_definitions( - "WIN32" - "_WINDOWS" - "WIN32_LEAN_AND_MEAN=1" - "_CRT_SECURE_NO_WARNINGS" - "BORINGSSL_NO_CXX=1" # lol - ) - - # set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") - set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") - - target_compile_options(${bun} PUBLIC "/EHsc" "/GR-") - target_link_options(${bun} PUBLIC "/STACK:0x1200000,0x100000" "/DEF:${BUN_SRC}/symbols.def") -else() - target_compile_options(${bun} PUBLIC - -fPIC - -mtune=${CPU_TARGET} - -fconstexpr-steps=2542484 - -fconstexpr-depth=54 - -fno-exceptions - -fvisibility=hidden - -fvisibility-inlines-hidden - -fno-rtti - -fno-omit-frame-pointer - -mno-omit-leaf-frame-pointer - ) -endif() - -if(APPLE) - target_link_options(${bun} PUBLIC "-dead_strip") - target_link_options(${bun} PUBLIC "-dead_strip_dylibs") - target_link_options(${bun} PUBLIC "-Wl,-stack_size,0x1200000") - target_link_options(${bun} PUBLIC "-exported_symbols_list" "${BUN_SRC}/symbols.txt") - set_target_properties(${bun} PROPERTIES LINK_DEPENDS "${BUN_SRC}/symbols.txt") - - target_link_options(${bun} PUBLIC "-fno-keep-static-consts") - target_link_libraries(${bun} PRIVATE "resolv") -endif() - -if(UNIX AND NOT APPLE) - target_link_options(${bun} PUBLIC - "-fuse-ld=lld" - "-static-libstdc++" - "-static-libgcc" - "-Wl,-z,now" - "-Wl,--as-needed" - "-Wl,--gc-sections" - "-Wl,-z,stack-size=12800000" - "-Wl,--wrap=fcntl" - "-Wl,--wrap=fcntl64" - "-Wl,--wrap=stat64" - "-Wl,--wrap=pow" - "-Wl,--wrap=exp" - "-Wl,--wrap=expf" - "-Wl,--wrap=log" - "-Wl,--wrap=log2" - "-Wl,--wrap=lstat" - "-Wl,--wrap=stat64" - "-Wl,--wrap=stat" - "-Wl,--wrap=fstat" - "-Wl,--wrap=fstatat" - "-Wl,--wrap=lstat64" - "-Wl,--wrap=fstat64" - "-Wl,--wrap=fstatat64" - "-Wl,--wrap=mknod" - "-Wl,--wrap=mknodat" - "-Wl,--wrap=statx" - "-Wl,--wrap=fmod" - "-Wl,--compress-debug-sections=zlib" - "-Bsymbolics-functions" - "-rdynamic" - "-Wl,--dynamic-list=${BUN_SRC}/symbols.dyn" - "-Wl,--version-script=${BUN_SRC}/linker.lds" - ) - - target_link_libraries(${bun} PRIVATE "c") - target_link_libraries(${bun} PRIVATE "pthread") - target_link_libraries(${bun} PRIVATE "dl") - - if(NOT USE_STATIC_LIBATOMIC) - target_link_libraries(${bun} PUBLIC "libatomic.so") - else() - target_link_libraries(${bun} PRIVATE "libatomic.a") - endif() - - if(USE_SYSTEM_ICU) - target_link_libraries(${bun} PRIVATE "libicudata.a") - target_link_libraries(${bun} PRIVATE "libicui18n.a") - target_link_libraries(${bun} PRIVATE "libicuuc.a") - else() - target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libicudata.a") - target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libicui18n.a") - target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libicuuc.a") - endif() - - set_target_properties(${bun} PROPERTIES LINK_DEPENDS "${BUN_SRC}/linker.lds") - set_target_properties(${bun} PROPERTIES LINK_DEPENDS "${BUN_SRC}/symbols.dyn") -endif() - -# --- ICU --- -if(APPLE) - target_link_libraries(${bun} PRIVATE "icucore") -endif() - -# --- Stripped Binary "bun" -if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32 AND NOT ASSERT_ENABLED) - # add_custom_command( - # TARGET ${bun} - # POST_BUILD - # COMMAND ${DSYMUTIL} -o ${BUN_WORKDIR}/bun.dSYM ${BUN_WORKDIR}/${bun} - # COMMENT "Stripping Symbols" - # ) - add_custom_command( - TARGET ${bun} - POST_BUILD - COMMAND ${STRIP} -s -x -S -o ${BUN_WORKDIR}/bun ${BUN_WORKDIR}/${bun} - COMMENT "Stripping Symbols" - ) -endif() - -if(WIN32) - # Kill all instances of bun before linking. - # This is necessary because the file is locked by the process. - add_custom_command( - TARGET ${bun} - PRE_LINK - COMMAND - "powershell" - "/C" - "Stop-Process -Name '${bun}' -Force -ErrorAction SilentlyContinue; exit 0" - ) -endif() - -# --- Dependencies --- -if(USE_CUSTOM_ZLIB) - include_directories(${BUN_DEPS_DIR}/zlib) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/zlib.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libz.a") - endif() -else() - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_DIR}/zlib_maybethisworks.lib") - else() - find_package(ZLIB REQUIRED) - target_link_libraries(${bun} PRIVATE ZLIB::ZLIB) - endif() -endif() - -if(USE_CUSTOM_BORINGSSL) - include_directories(${BUN_DEPS_DIR}/boringssl/include) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/crypto.lib") - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/ssl.lib") - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/decrepit.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libcrypto.a") - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libssl.a") - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libdecrepit.a") - endif() -else() - include(FindBoringSSL) - FindBoringSSL(${bun}) -endif() - -if(USE_CUSTOM_LIBARCHIVE) - include_directories(${BUN_DEPS_DIR}/libarchive/include) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/archive.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libarchive.a") - endif() -else() - find_package(LibArchive REQUIRED) - target_link_libraries(${bun} PRIVATE LibArchive::LibArchive) -endif() - -if(USE_CUSTOM_MIMALLOC) - include_directories(${BUN_DEPS_DIR}/mimalloc/include) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/mimalloc.lib") - elseif(APPLE) - if(USE_DEBUG_JSC OR CMAKE_BUILD_TYPE STREQUAL "Debug") - message(STATUS "Using debug mimalloc") - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc-debug.o") - else() - # Note: https://github.com/microsoft/mimalloc/issues/512 - # It may have been a bug in our code at the time. - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc.o") - endif() - else() - if(USE_DEBUG_JSC OR CMAKE_BUILD_TYPE STREQUAL "Debug") - message(STATUS "Using debug mimalloc") - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc-debug.a") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc.o") - endif() - endif() -else() - find_package(mimalloc REQUIRED) - target_link_libraries(${bun} PRIVATE mimalloc) -endif() - -if(USE_CUSTOM_ZSTD) - include_directories(${BUN_DEPS_DIR}/zstd/include) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/zstd.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libzstd.a") - endif() -else() - find_package(zstd CONFIG REQUIRED) - target_link_libraries(${bun} PRIVATE zstd::libzstd) -endif() - -if(USE_CUSTOM_CARES) - include_directories(${BUN_DEPS_DIR}/c-ares/include) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/cares.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libcares.a") - endif() -else() - find_package(c-ares CONFIG REQUIRED) - target_link_libraries(${bun} PRIVATE c-ares::cares) -endif() +include(Policies) +include(Globals) -if(USE_CUSTOM_TINYCC) - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/tcc.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libtcc.a") - endif() -else() - find_package(tinycc REQUIRED) - target_link_libraries(${bun} PRIVATE tinycc::tinycc) -endif() +# --- Compilers --- -if(USE_CUSTOM_LOLHTML) - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/lolhtml.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/liblolhtml.a") - endif() -else() - find_package(lolhtml REQUIRED) - target_link_libraries(${bun} PRIVATE lolhtml::lolhtml) +if(CMAKE_HOST_APPLE) + include(SetupMacSDK) endif() +include(SetupLLVM) +include(SetupCcache) -if(WIN32) - if(USE_CUSTOM_LIBUV) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libuv.lib") - include_directories(${bun} PRIVATE "${BUN_DEPS_DIR}/libuv/include") - else() - find_package(libuv CONFIG REQUIRED) - target_link_libraries(${bun} PRIVATE $,libuv::uv_a,libuv::uv>) - endif() -endif() +# --- Project --- -if(USE_STATIC_SQLITE) - add_library(sqlite3 STATIC src/bun.js/bindings/sqlite/sqlite3.c) - target_include_directories(sqlite3 PUBLIC src/bun.js/bindings/sqlite) - target_compile_definitions(sqlite3 PRIVATE - "SQLITE_ENABLE_COLUMN_METADATA=" - "SQLITE_MAX_VARIABLE_NUMBER=250000" - "SQLITE_ENABLE_RTREE=1" - "SQLITE_ENABLE_FTS3=1" - "SQLITE_ENABLE_FTS3_PARENTHESIS=1" - "SQLITE_ENABLE_FTS5=1" - "SQLITE_ENABLE_JSON1=1" - "SQLITE_ENABLE_MATH_FUNCTIONS=1" - ) - target_link_libraries(${bun} PRIVATE sqlite3) - message(STATUS "Using static sqlite3") - target_compile_definitions(${bun} PRIVATE "LAZY_LOAD_SQLITE=0") -else() - message(STATUS "Using dynamicly linked sqlite3") - target_compile_definitions(${bun} PRIVATE "LAZY_LOAD_SQLITE=1") -endif() +parse_package_json(VERSION_VARIABLE DEFAULT_VERSION) +optionx(VERSION STRING "The version of Bun" DEFAULT ${DEFAULT_VERSION}) +project(Bun VERSION ${VERSION}) +include(Options) +include(CompilerFlags) -if(USE_CUSTOM_LSHPACK) - include_directories(${BUN_DEPS_DIR}/ls-hpack) +# --- Tools --- - if(WIN32) - include_directories(${BUN_DEPS_DIR}/ls-hpack/compat/queue) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/lshpack.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/liblshpack.a") - endif() -else() - find_package(lshpack REQUIRED) - target_link_libraries(${bun} PRIVATE lshpack) -endif() +include(SetupBuildkite) +include(SetupBun) +include(SetupEsbuild) +include(SetupZig) +include(SetupRust) -if(NOT WIN32) - target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libWTF.a") - target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libJavaScriptCore.a") - target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libbmalloc.a") -else() - target_link_options(${bun} PRIVATE "-static") - target_link_libraries(${bun} PRIVATE - "${WEBKIT_LIB_DIR}/WTF.lib" - "${WEBKIT_LIB_DIR}/JavaScriptCore.lib" - "${WEBKIT_LIB_DIR}/sicudt.lib" - "${WEBKIT_LIB_DIR}/sicuin.lib" - "${WEBKIT_LIB_DIR}/sicuuc.lib" - winmm - bcrypt - ntdll - ucrt - userenv - dbghelp - wsock32 # ws2_32 required by TransmitFile aka sendfile on windows - ) -endif() - -if(WIN32) - # delayimp -delayload:shell32.dll -delayload:ole32.dll -endif() +# --- Targets --- -if(BUN_LINK_ONLY) - message(STATUS "NOTE: BUN_LINK_ONLY is ON, this build config will only link the Bun executable") -endif() - -if(BUN_CPP_ONLY) - message(STATUS "NOTE: BUN_CPP_ONLY is ON, this build will only work with 'compile-cpp-only.${SCRIPT_EXTENSION}'") -endif() - -if(NO_CODEGEN) - message(STATUS "NOTE: NO_CODEGEN is ON, this build expects ./codegen to exist") -endif() - -if(BUN_TIDY_ONLY) - find_program(CLANG_TIDY_EXE NAMES "clang-tidy") - set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "-checks=-*,clang-analyzer-*,-clang-analyzer-webkit.UncountedLambdaCapturesChecker" "--fix" "--fix-errors" "--format-style=webkit" "--warnings-as-errors=*") - set_target_properties(${bun} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") -endif() - -if(BUN_TIDY_ONLY_EXTRA) - find_program(CLANG_TIDY_EXE NAMES "clang-tidy") - set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "-checks=-*,clang-analyzer-*,performance-*,-clang-analyzer-webkit.UncountedLambdaCapturesChecker" "--fix" "--fix-errors" "--format-style=webkit" "--warnings-as-errors=*") - set_target_properties(${bun} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") -endif() +include(BuildBun) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05b3809feebff..6d3dbb0fd2244 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,11 @@ Configuring a development environment for Bun can take 10-30 minutes depending o If you are using Windows, please refer to [this guide](/docs/project/building-windows) +{% details summary="For Ubuntu users" %} +TL;DR: Ubuntu 22.04 is suggested. +Bun currently requires `glibc >=2.32` in development which means if you're on Ubuntu 20.04 (glibc == 2.31), you may likely meet `error: undefined symbol: __libc_single_threaded `. You need to take extra configurations. Also, according to this [issue](https://github.com/llvm/llvm-project/issues/97314), LLVM 16 is no longer maintained on Ubuntu 24.04 (noble). And instead, you might want `brew` to install LLVM 16 for your Ubuntu 24.04. +{% /details %} + ## Install Dependencies Using your system's package manager, install Bun's dependencies: @@ -58,7 +63,7 @@ Bun requires LLVM 16 (`clang` is part of LLVM). This version requirement is to m {% codetabs %} ```bash#macOS (Homebrew) -$ brew install llvm@16 +$ brew install llvm@18 ``` ```bash#Ubuntu/Debian @@ -107,7 +112,7 @@ $ export PATH="$PATH:/usr/lib/llvm16/bin" {% /codetabs %} -> ⚠️ Ubuntu distributions may require installation of the C++ standard library independently. See the [troubleshooting section](#span-file-not-found-on-ubuntu) for more information. +> ⚠️ Ubuntu distributions (<= 20.04) may require installation of the C++ standard library independently. See the [troubleshooting section](#span-file-not-found-on-ubuntu) for more information. ## Building Bun @@ -158,8 +163,8 @@ Several code generation scripts are used during Bun's build process. These are r In particular, these are: -- `./src/codegen/generate-jssink.ts` -- Generates `build/codegen/JSSink.cpp`, `build/codegen/JSSink.h` which implement various classes for interfacing with `ReadableStream`. This is internally how `FileSink`, `ArrayBufferSink`, `"type": "direct"` streams and other code related to streams works. -- `./src/codegen/generate-classes.ts` -- Generates `build/codegen/ZigGeneratedClasses*`, which generates Zig & C++ bindings for JavaScriptCore classes implemented in Zig. In `**/*.classes.ts` files, we define the interfaces for various classes, methods, prototypes, getters/setters etc which the code generator reads to generate boilerplate code implementing the JavaScript objects in C++ and wiring them up to Zig +- `./src/codegen/generate-jssink.ts` -- Generates `build/debug/codegen/JSSink.cpp`, `build/debug/codegen/JSSink.h` which implement various classes for interfacing with `ReadableStream`. This is internally how `FileSink`, `ArrayBufferSink`, `"type": "direct"` streams and other code related to streams works. +- `./src/codegen/generate-classes.ts` -- Generates `build/debug/codegen/ZigGeneratedClasses*`, which generates Zig & C++ bindings for JavaScriptCore classes implemented in Zig. In `**/*.classes.ts` files, we define the interfaces for various classes, methods, prototypes, getters/setters etc which the code generator reads to generate boilerplate code implementing the JavaScript objects in C++ and wiring them up to Zig - `./src/codegen/bundle-modules.ts` -- Bundles built-in modules like `node:fs`, `bun:ffi` into files we can include in the final binary. In development, these can be reloaded without rebuilding Zig (you still need to run `bun run build`, but it re-reads the transpiled files from disk afterwards). In release builds, these are embedded into the binary. - `./src/codegen/bundle-functions.ts` -- Bundles globally-accessible functions implemented in JavaScript/TypeScript like `ReadableStream`, `WritableStream`, and a handful more. These are used similarly to the builtin modules, but the output more closely aligns with what WebKit/Safari does for Safari's built-in functions so that we can copy-paste the implementations from WebKit as a starting point. @@ -311,3 +316,12 @@ $ bun setup -DUSE_STATIC_LIBATOMIC=OFF ``` The built version of Bun may not work on other systems if compiled this way. + +## ccache conflicts with building TinyCC on macOS + +If you run into issues with `ccache` when building TinyCC, try reinstalling ccache + +```bash +brew uninstall ccache +brew install ccache +``` diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 410cf014828f0..0000000000000 --- a/Dockerfile +++ /dev/null @@ -1,599 +0,0 @@ -# This Dockerfile is used by CI workflows to build Bun. It is not intended as a development -# environment, or to be used as a base image for other projects. -# -# You likely want this image instead: https://hub.docker.com/r/oven/bun -# -# TODO: move this file to reduce confusion -ARG DEBIAN_FRONTEND=noninteractive -ARG GITHUB_WORKSPACE=/build -ARG WEBKIT_DIR=${GITHUB_WORKSPACE}/bun-webkit -ARG BUN_RELEASE_DIR=${GITHUB_WORKSPACE}/bun-release -ARG BUN_DEPS_OUT_DIR=${GITHUB_WORKSPACE}/bun-deps -ARG BUN_DIR=${GITHUB_WORKSPACE}/bun -ARG CPU_TARGET=native -ARG ARCH=x86_64 -ARG BUILD_MACHINE_ARCH=x86_64 -ARG BUILDARCH=amd64 -ARG TRIPLET=${ARCH}-linux-gnu -ARG GIT_SHA="" -ARG BUN_VERSION="bun-v1.1.4" -ARG BUN_DOWNLOAD_URL_BASE="https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${BUN_VERSION}" -ARG CANARY=0 -ARG ASSERTIONS=OFF -ARG ZIG_OPTIMIZE=ReleaseFast -ARG CMAKE_BUILD_TYPE=Release - -ARG NODE_VERSION="20" -ARG LLVM_VERSION="16" - -ARG ZIG_VERSION="0.13.0" -ARG ZIG_VERSION_SHORT="0.13.0" - -ARG SCCACHE_BUCKET -ARG SCCACHE_REGION -ARG SCCACHE_S3_USE_SSL -ARG SCCACHE_ENDPOINT -ARG AWS_ACCESS_KEY_ID -ARG AWS_SECRET_ACCESS_KEY - -FROM bitnami/minideb:bullseye as bun-base - -ARG BUN_DOWNLOAD_URL_BASE -ARG DEBIAN_FRONTEND -ARG BUN_VERSION -ARG NODE_VERSION -ARG LLVM_VERSION -ARG BUILD_MACHINE_ARCH -ARG BUN_DIR -ARG BUN_DEPS_OUT_DIR -ARG CPU_TARGET - -ENV CI 1 -ENV CPU_TARGET=${CPU_TARGET} -ENV BUILDARCH=${BUILDARCH} -ENV BUN_DEPS_OUT_DIR=${BUN_DEPS_OUT_DIR} - -ENV CXX=clang++-${LLVM_VERSION} -ENV CC=clang-${LLVM_VERSION} -ENV AR=/usr/bin/llvm-ar-${LLVM_VERSION} -ENV LD=lld-${LLVM_VERSION} -ENV LC_CTYPE=en_US.UTF-8 -ENV LC_ALL=en_US.UTF-8 - -ENV SCCACHE_BUCKET=${SCCACHE_BUCKET} -ENV SCCACHE_REGION=${SCCACHE_REGION} -ENV SCCACHE_S3_USE_SSL=${SCCACHE_S3_USE_SSL} -ENV SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT} -ENV AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} -ENV AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - -RUN install_packages \ - ca-certificates \ - curl \ - gnupg \ - && echo "deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list \ - && echo "deb-src https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-${LLVM_VERSION} main" >> /etc/apt/sources.list.d/llvm.list \ - && curl -fsSL "https://apt.llvm.org/llvm-snapshot.gpg.key" | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ - && curl -fsSL "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" | apt-key add - \ - && echo "deb https://apt.kitware.com/ubuntu/ focal main" > /etc/apt/sources.list.d/kitware.list \ - && curl -fsSL "https://apt.kitware.com/keys/kitware-archive-latest.asc" | apt-key add - \ - && install_packages \ - wget \ - bash \ - software-properties-common \ - build-essential \ - autoconf \ - automake \ - libtool \ - pkg-config \ - clang-${LLVM_VERSION} \ - lld-${LLVM_VERSION} \ - lldb-${LLVM_VERSION} \ - clangd-${LLVM_VERSION} \ - libc++-${LLVM_VERSION}-dev \ - libc++abi-${LLVM_VERSION}-dev \ - make \ - cmake \ - ninja-build \ - file \ - libc-dev \ - libxml2 \ - libxml2-dev \ - xz-utils \ - git \ - tar \ - rsync \ - gzip \ - unzip \ - perl \ - python3 \ - ruby \ - ruby-dev \ - golang \ - nodejs && \ - for f in /usr/lib/llvm-${LLVM_VERSION}/bin/*; do ln -sf "$f" /usr/bin; done \ - && ln -sf /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang \ - && ln -sf /usr/bin/clang++-${LLVM_VERSION} /usr/bin/clang++ \ - && ln -sf /usr/bin/lld-${LLVM_VERSION} /usr/bin/lld \ - && ln -sf /usr/bin/lldb-${LLVM_VERSION} /usr/bin/lldb \ - && ln -sf /usr/bin/clangd-${LLVM_VERSION} /usr/bin/clangd \ - && ln -sf /usr/bin/llvm-ar-${LLVM_VERSION} /usr/bin/llvm-ar \ - && arch="$(dpkg --print-architecture)" \ - && case "${arch##*-}" in \ - amd64) variant="x64";; \ - arm64) variant="aarch64";; \ - *) echo "unsupported architecture: $arch"; exit 1 ;; \ - esac \ - && wget "${BUN_DOWNLOAD_URL_BASE}/bun-linux-${variant}.zip" \ - && unzip bun-linux-${variant}.zip \ - && mv bun-linux-${variant}/bun /usr/bin/bun \ - && ln -s /usr/bin/bun /usr/bin/bunx \ - && rm -rf bun-linux-${variant} bun-linux-${variant}.zip \ - && mkdir -p ${BUN_DIR} ${BUN_DEPS_OUT_DIR} -# && if [ -n "${SCCACHE_BUCKET}" ]; then \ -# echo "Setting up sccache" \ -# && wget https://github.com/mozilla/sccache/releases/download/v0.5.4/sccache-v0.5.4-${BUILD_MACHINE_ARCH}-unknown-linux-musl.tar.gz \ -# && tar xf sccache-v0.5.4-${BUILD_MACHINE_ARCH}-unknown-linux-musl.tar.gz \ -# && mv sccache-v0.5.4-${BUILD_MACHINE_ARCH}-unknown-linux-musl/sccache /usr/bin/sccache \ -# && rm -rf sccache-v0.5.4-${BUILD_MACHINE_ARCH}-unknown-linux-musl.tar.gz sccache-v0.5.4-${BUILD_MACHINE_ARCH}-unknown-linux-musl \ - -FROM bun-base as bun-base-with-zig - -ARG ZIG_VERSION -ARG ZIG_VERSION_SHORT -ARG BUILD_MACHINE_ARCH -ARG ZIG_FOLDERNAME=zig-linux-${BUILD_MACHINE_ARCH}-${ZIG_VERSION} -ARG ZIG_FILENAME=${ZIG_FOLDERNAME}.tar.xz -ARG ZIG_URL="https://ziglang.org/builds/${ZIG_FILENAME}" -ARG ZIG_LOCAL_CACHE_DIR=/zig-cache -ENV ZIG_LOCAL_CACHE_DIR=${ZIG_LOCAL_CACHE_DIR} - -WORKDIR $GITHUB_WORKSPACE - -ADD $ZIG_URL . -RUN tar xf ${ZIG_FILENAME} \ - && mv ${ZIG_FOLDERNAME}/lib /usr/lib/zig \ - && mv ${ZIG_FOLDERNAME}/zig /usr/bin/zig \ - && rm -rf ${ZIG_FILENAME} ${ZIG_FOLDERNAME} - -FROM bun-base as c-ares - -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/c-ares ${BUN_DIR}/src/deps/c-ares - -WORKDIR $BUN_DIR - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd $BUN_DIR \ - && make c-ares \ - && rm -rf ${BUN_DIR}/src/deps/c-ares ${BUN_DIR}/Makefile - -FROM bun-base as lolhtml - -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y - -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/lol-html ${BUN_DIR}/src/deps/lol-html - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - export PATH=$PATH:$HOME/.cargo/bin \ - && cd ${BUN_DIR} \ - && make lolhtml \ - && rm -rf src/deps/lol-html Makefile - -FROM bun-base as mimalloc - -ARG BUN_DIR -ARG CPU_TARGET -ARG ASSERTIONS -ENV CPU_TARGET=${CPU_TARGET} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/mimalloc ${BUN_DIR}/src/deps/mimalloc - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd ${BUN_DIR} \ - && make mimalloc \ - && rm -rf src/deps/mimalloc Makefile - -FROM bun-base as mimalloc-debug - -ARG BUN_DIR -ARG CPU_TARGET -ARG ASSERTIONS -ENV CPU_TARGET=${CPU_TARGET} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/mimalloc ${BUN_DIR}/src/deps/mimalloc - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd ${BUN_DIR} \ - && make mimalloc-debug \ - && rm -rf src/deps/mimalloc Makefile - -FROM bun-base as zlib - -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/zlib ${BUN_DIR}/src/deps/zlib - -WORKDIR $BUN_DIR - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd $BUN_DIR \ - && make zlib \ - && rm -rf src/deps/zlib Makefile - -FROM bun-base as libarchive - -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -RUN install_packages autoconf automake libtool pkg-config - -COPY scripts ${BUN_DIR}/scripts -COPY src/deps/libarchive ${BUN_DIR}/src/deps/libarchive - -WORKDIR $BUN_DIR - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd $BUN_DIR \ - && bash ./scripts/build-libarchive.sh && rm -rf src/deps/libarchive .scripts - -FROM bun-base as tinycc - -ARG BUN_DEPS_OUT_DIR -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -RUN install_packages libtcc-dev && cp /usr/lib/$(uname -m)-linux-gnu/libtcc.a ${BUN_DEPS_OUT_DIR} - -FROM bun-base as boringssl - -RUN install_packages golang - -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/boringssl ${BUN_DIR}/src/deps/boringssl - -WORKDIR $BUN_DIR - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd ${BUN_DIR} \ - && make boringssl \ - && rm -rf src/deps/boringssl Makefile - - -FROM bun-base as zstd - -ARG BUN_DIR - -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/zstd ${BUN_DIR}/src/deps/zstd - -WORKDIR $BUN_DIR - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd $BUN_DIR \ - && make zstd - -FROM bun-base as ls-hpack - -ARG BUN_DIR - -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/ls-hpack ${BUN_DIR}/src/deps/ls-hpack - -WORKDIR $BUN_DIR - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - cd $BUN_DIR \ - && make lshpack - -FROM bun-base-with-zig as bun-identifier-cache - -ARG DEBIAN_FRONTEND -ARG GITHUB_WORKSPACE -ARG CPU_TARGET -ARG BUN_DIR -ENV CPU_TARGET=${CPU_TARGET} - -WORKDIR $BUN_DIR - -COPY src/js_lexer/identifier_data.zig ${BUN_DIR}/src/js_lexer/identifier_data.zig -COPY src/js_lexer/identifier_cache.zig ${BUN_DIR}/src/js_lexer/identifier_cache.zig - -RUN --mount=type=cache,target=${ZIG_LOCAL_CACHE_DIR} \ - cd $BUN_DIR \ - && zig run src/js_lexer/identifier_data.zig - -FROM bun-base as bun-node-fallbacks - -ARG BUN_DIR - -WORKDIR $BUN_DIR - -COPY src/node-fallbacks ${BUN_DIR}/src/node-fallbacks - -RUN cd $BUN_DIR/src/node-fallbacks \ - && bun install --frozen-lockfile \ - && bun run build \ - && rm -rf src/node-fallbacks/node_modules - -FROM bun-base as bun-webkit - -ARG BUILDARCH -ARG ASSERTIONS - -COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt - -RUN mkdir ${BUN_DIR}/bun-webkit \ - && WEBKIT_TAG=$(grep 'set(WEBKIT_TAG' "${BUN_DIR}/CMakeLists.txt" | awk '{print $2}' | cut -f 1 -d ')') \ - && WEBKIT_SUFFIX=$(if [ "${ASSERTIONS}" = "ON" ]; then echo "debug"; else echo "lto"; fi) \ - && WEBKIT_URL="https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_TAG}/bun-webkit-linux-${BUILDARCH}-${WEBKIT_SUFFIX}.tar.gz" \ - && echo "Downloading ${WEBKIT_URL}" \ - && curl -fsSL "${WEBKIT_URL}" | tar -xz -C ${BUN_DIR}/bun-webkit --strip-components=1 - -FROM bun-base as bun-cpp-objects - -ARG CANARY -ARG ASSERTIONS - -COPY --from=bun-webkit ${BUN_DIR}/bun-webkit ${BUN_DIR}/bun-webkit - -COPY packages ${BUN_DIR}/packages -COPY src ${BUN_DIR}/src -COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt -COPY src/deps/boringssl/include ${BUN_DIR}/src/deps/boringssl/include - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -RUN --mount=type=cache,target=${CCACHE_DIR} mkdir ${BUN_DIR}/build \ - && cd ${BUN_DIR}/build \ - && mkdir -p tmp_modules tmp_functions js codegen \ - && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DUSE_LTO=ON -DUSE_DEBUG_JSC=${ASSERTIONS} -DBUN_CPP_ONLY=1 -DWEBKIT_DIR=/build/bun/bun-webkit -DCANARY=${CANARY} -DZIG_COMPILER=system \ - && bash compile-cpp-only.sh -v - -FROM bun-base-with-zig as bun-codegen-for-zig - -COPY package.json bun.lockb Makefile .gitmodules ${BUN_DIR}/ -COPY src/runtime ${BUN_DIR}/src/runtime -COPY src/runtime.js src/runtime.bun.js ${BUN_DIR}/src/ -COPY packages/bun-error ${BUN_DIR}/packages/bun-error -COPY packages/bun-types ${BUN_DIR}/packages/bun-types -COPY src/fallback.ts ${BUN_DIR}/src/fallback.ts -COPY src/api ${BUN_DIR}/src/api - -WORKDIR $BUN_DIR - -# TODO: move away from Makefile entirely -RUN --mount=type=cache,target=${ZIG_LOCAL_CACHE_DIR} \ - bun install --frozen-lockfile \ - && make runtime_js fallback_decoder bun_error \ - && rm -rf src/runtime src/fallback.ts node_modules bun.lockb package.json Makefile - -FROM bun-base-with-zig as bun-compile-zig-obj - -ARG ZIG_PATH -ARG TRIPLET -ARG GIT_SHA -ARG CPU_TARGET -ARG CANARY=0 -ARG ASSERTIONS=OFF -ARG ZIG_OPTIMIZE=ReleaseFast - -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} - -COPY *.zig package.json CMakeLists.txt ${BUN_DIR}/ -COPY completions ${BUN_DIR}/completions -COPY packages ${BUN_DIR}/packages -COPY src ${BUN_DIR}/src - -COPY --from=bun-identifier-cache ${BUN_DIR}/src/js_lexer/*.blob ${BUN_DIR}/src/js_lexer/ -COPY --from=bun-node-fallbacks ${BUN_DIR}/src/node-fallbacks/out ${BUN_DIR}/src/node-fallbacks/out -COPY --from=bun-codegen-for-zig ${BUN_DIR}/src/*.out.js ${BUN_DIR}/src/*.out.refresh.js ${BUN_DIR}/src/ -COPY --from=bun-codegen-for-zig ${BUN_DIR}/packages/bun-error/dist ${BUN_DIR}/packages/bun-error/dist - -WORKDIR $BUN_DIR - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - --mount=type=cache,target=${ZIG_LOCAL_CACHE_DIR} \ - mkdir -p build \ - && bun run $BUN_DIR/src/codegen/bundle-modules.ts --debug=OFF $BUN_DIR/build \ - && cd build \ - && cmake .. \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DUSE_LTO=ON \ - -DZIG_OPTIMIZE="${ZIG_OPTIMIZE}" \ - -DCPU_TARGET="${CPU_TARGET}" \ - -DZIG_TARGET="${TRIPLET}" \ - -DWEBKIT_DIR="omit" \ - -DNO_CONFIGURE_DEPENDS=1 \ - -DNO_CODEGEN=1 \ - -DBUN_ZIG_OBJ_DIR="/tmp" \ - -DCANARY="${CANARY}" \ - -DZIG_COMPILER=system \ - -DZIG_LIB_DIR=$BUN_DIR/src/deps/zig/lib \ - && ONLY_ZIG=1 ninja "/tmp/bun-zig.o" -v - -FROM scratch as build_release_obj - -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -COPY --from=bun-compile-zig-obj /tmp/bun-zig.o / - -FROM bun-base as bun-link - -ARG CPU_TARGET -ARG CANARY -ARG ASSERTIONS - -ENV CPU_TARGET=${CPU_TARGET} -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} -ARG ZIG_LOCAL_CACHE_DIR=/zig-cache -ENV ZIG_LOCAL_CACHE_DIR=${ZIG_LOCAL_CACHE_DIR} - -WORKDIR $BUN_DIR - -RUN mkdir -p build bun-webkit - -# lol -COPY src/bun.js/bindings/sqlite/sqlite3.c ${BUN_DIR}/src/bun.js/bindings/sqlite/sqlite3.c - -COPY src/symbols.dyn src/linker.lds ${BUN_DIR}/src/ - -COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt -COPY --from=zlib ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=libarchive ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=boringssl ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=lolhtml ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=mimalloc ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=zstd ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=tinycc ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=c-ares ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=ls-hpack ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=bun-compile-zig-obj /tmp/bun-zig.o ${BUN_DIR}/build/bun-zig.o -COPY --from=bun-cpp-objects ${BUN_DIR}/build/bun-cpp-objects.a ${BUN_DIR}/build/bun-cpp-objects.a -COPY --from=bun-cpp-objects ${BUN_DIR}/bun-webkit/lib ${BUN_DIR}/bun-webkit/lib - -WORKDIR $BUN_DIR/build - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - --mount=type=cache,target=${ZIG_LOCAL_CACHE_DIR} \ - cmake .. \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUN_LINK_ONLY=1 \ - -DBUN_ZIG_OBJ_DIR="${BUN_DIR}/build" \ - -DUSE_LTO=ON \ - -DUSE_DEBUG_JSC=${ASSERTIONS} \ - -DBUN_CPP_ARCHIVE="${BUN_DIR}/build/bun-cpp-objects.a" \ - -DWEBKIT_DIR="${BUN_DIR}/bun-webkit" \ - -DBUN_DEPS_OUT_DIR="${BUN_DEPS_OUT_DIR}" \ - -DCPU_TARGET="${CPU_TARGET}" \ - -DNO_CONFIGURE_DEPENDS=1 \ - -DCANARY="${CANARY}" \ - -DZIG_COMPILER=system \ - && ninja -v \ - && ./bun --revision \ - && mkdir -p /build/out \ - && mv bun bun-profile /build/out \ - && rm -rf ${BUN_DIR} ${BUN_DEPS_OUT_DIR} - -FROM scratch as artifact - -COPY --from=bun-link /build/out / - -FROM bun-base as bun-link-assertions - -ARG CPU_TARGET -ARG CANARY -ARG ASSERTIONS - -ENV CPU_TARGET=${CPU_TARGET} -ARG CCACHE_DIR=/ccache -ENV CCACHE_DIR=${CCACHE_DIR} -ARG ZIG_LOCAL_CACHE_DIR=/zig-cache -ENV ZIG_LOCAL_CACHE_DIR=${ZIG_LOCAL_CACHE_DIR} - -WORKDIR $BUN_DIR - -RUN mkdir -p build bun-webkit - -# lol -COPY src/bun.js/bindings/sqlite/sqlite3.c ${BUN_DIR}/src/bun.js/bindings/sqlite/sqlite3.c - -COPY src/symbols.dyn src/linker.lds ${BUN_DIR}/src/ - -COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt -COPY --from=zlib ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=libarchive ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=boringssl ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=lolhtml ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=mimalloc-debug ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=zstd ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=tinycc ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=c-ares ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=bun-compile-zig-obj /tmp/bun-zig.o ${BUN_DIR}/build/bun-zig.o -COPY --from=bun-cpp-objects ${BUN_DIR}/build/bun-cpp-objects.a ${BUN_DIR}/build/bun-cpp-objects.a -COPY --from=bun-cpp-objects ${BUN_DIR}/bun-webkit/lib ${BUN_DIR}/bun-webkit/lib - -WORKDIR $BUN_DIR/build - -RUN --mount=type=cache,target=${CCACHE_DIR} \ - --mount=type=cache,target=${ZIG_LOCAL_CACHE_DIR} \ - cmake .. \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUN_LINK_ONLY=1 \ - -DBUN_ZIG_OBJ_DIR="${BUN_DIR}/build" \ - -DUSE_DEBUG_JSC=ON \ - -DBUN_CPP_ARCHIVE="${BUN_DIR}/build/bun-cpp-objects.a" \ - -DWEBKIT_DIR="${BUN_DIR}/bun-webkit" \ - -DBUN_DEPS_OUT_DIR="${BUN_DEPS_OUT_DIR}" \ - -DCPU_TARGET="${CPU_TARGET}" \ - -DNO_CONFIGURE_DEPENDS=1 \ - -DCANARY="${CANARY}" \ - -DZIG_COMPILER=system \ - -DUSE_LTO=ON \ - && ninja -v \ - && ./bun --revision \ - && mkdir -p /build/out \ - && mv bun bun-profile /build/out \ - && rm -rf ${BUN_DIR} ${BUN_DEPS_OUT_DIR} - -FROM scratch as artifact-assertions - -COPY --from=bun-link-assertions /build/out / \ No newline at end of file diff --git a/LATEST b/LATEST index 8d2c87f71afd2..243e79a0735b8 100644 --- a/LATEST +++ b/LATEST @@ -1 +1 @@ -1.1.15 \ No newline at end of file +1.1.27 \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0206d2e99483b..0000000000000 --- a/LICENSE +++ /dev/null @@ -1,224 +0,0 @@ -Bun itself is MIT-licensed. - -## JavaScriptCore - -Bun statically links JavaScriptCore (and WebKit) which is LGPL-2 licensed. WebCore files from WebKit are also licensed under LGPL2. Per LGPL2: - -> (1) If you statically link against an LGPL’d library, you must also provide your application in an object (not necessarily source) format, so that a user has the opportunity to modify the library and relink the application. - -You can find the patched version of WebKit used by Bun here: . If you would like to relink Bun with changes: - -- `git submodule update --init --recursive` -- `make jsc` -- `zig build` - -This compiles JavaScriptCore, compiles Bun’s `.cpp` bindings for JavaScriptCore (which are the object files using JavaScriptCore) and outputs a new `bun` binary with your changes. - -## Linked libraries - -Bun statically links these libraries: - -{% table %} - -- Library -- License - ---- - -- [`boringssl`](https://boringssl.googlesource.com/boringssl/) -- [several licenses](https://boringssl.googlesource.com/boringssl/+/refs/heads/master/LICENSE) - ---- - ---- - -- [`brotli`](https://github.com/google/brotli) -- MIT - ---- - -- [`libarchive`](https://github.com/libarchive/libarchive) -- [several licenses](https://github.com/libarchive/libarchive/blob/master/COPYING) - ---- - -- [`lol-html`](https://github.com/cloudflare/lol-html/tree/master/c-api) -- BSD 3-Clause - ---- - -- [`mimalloc`](https://github.com/microsoft/mimalloc) -- MIT - ---- - -- [`picohttp`](https://github.com/h2o/picohttpparser) -- dual-licensed under the Perl License or the MIT License - ---- - -- [`zstd`](https://github.com/facebook/zstd) -- dual-licensed under the BSD License or GPLv2 license - ---- - -- [`simdutf`](https://github.com/simdutf/simdutf) -- Apache 2.0 - ---- - -- [`tinycc`](https://github.com/tinycc/tinycc) -- LGPL v2.1 - ---- - -- [`uSockets`](https://github.com/uNetworking/uSockets) -- Apache 2.0 - ---- - -- [`zlib-cloudflare`](https://github.com/cloudflare/zlib) -- zlib - ---- - -- [`c-ares`](https://github.com/c-ares/c-ares) -- MIT licensed - ---- - -- [`libicu`](https://github.com/unicode-org/icu) 72 -- [license here](https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE) - ---- - -- A fork of [`uWebsockets`](https://github.com/jarred-sumner/uwebsockets) -- Apache 2.0 licensed - ---- - -- Parts of [Tigerbeetle's IO code](https://github.com/tigerbeetle/tigerbeetle/blob/532c8b70b9142c17e07737ab6d3da68d7500cbca/src/io/windows.zig#L1) -- Apache 2.0 licensed - -{% /table %} - -## Polyfills - -For compatibility reasons, the following packages are embedded into Bun's binary and injected if imported. - -{% table %} - -- Package -- License - ---- - -- [`assert`](https://npmjs.com/package/assert) -- MIT - ---- - -- [`browserify-zlib`](https://npmjs.com/package/browserify-zlib) -- MIT - ---- - -- [`buffer`](https://npmjs.com/package/buffer) -- MIT - ---- - -- [`constants-browserify`](https://npmjs.com/package/constants-browserify) -- MIT - ---- - -- [`crypto-browserify`](https://npmjs.com/package/crypto-browserify) -- MIT - ---- - -- [`domain-browser`](https://npmjs.com/package/domain-browser) -- MIT - ---- - -- [`events`](https://npmjs.com/package/events) -- MIT - ---- - -- [`https-browserify`](https://npmjs.com/package/https-browserify) -- MIT - ---- - -- [`os-browserify`](https://npmjs.com/package/os-browserify) -- MIT - ---- - -- [`path-browserify`](https://npmjs.com/package/path-browserify) -- MIT - ---- - -- [`process`](https://npmjs.com/package/process) -- MIT - ---- - -- [`punycode`](https://npmjs.com/package/punycode) -- MIT - ---- - -- [`querystring-es3`](https://npmjs.com/package/querystring-es3) -- MIT - ---- - -- [`stream-browserify`](https://npmjs.com/package/stream-browserify) -- MIT - ---- - -- [`stream-http`](https://npmjs.com/package/stream-http) -- MIT - ---- - -- [`string_decoder`](https://npmjs.com/package/string_decoder) -- MIT - ---- - -- [`timers-browserify`](https://npmjs.com/package/timers-browserify) -- MIT - ---- - -- [`tty-browserify`](https://npmjs.com/package/tty-browserify) -- MIT - ---- - -- [`url`](https://npmjs.com/package/url) -- MIT - ---- - -- [`util`](https://npmjs.com/package/util) -- MIT - ---- - -- [`vm-browserify`](https://npmjs.com/package/vm-browserify) -- MIT - -{% /table %} - -## Additional credits - -- Bun's JS transpiler, CSS lexer, and Node.js module resolver source code is a Zig port of [@evanw](https://github.com/evanw)’s [esbuild](https://github.com/evanw/esbuild) project. -- Credit to [@kipply](https://github.com/kipply) for the name "Bun"! diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000000..4cc901b7bcfee --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,73 @@ +Bun itself is MIT-licensed. + +## JavaScriptCore + +Bun statically links JavaScriptCore (and WebKit) which is LGPL-2 licensed. WebCore files from WebKit are also licensed under LGPL2. Per LGPL2: + +> (1) If you statically link against an LGPL’d library, you must also provide your application in an object (not necessarily source) format, so that a user has the opportunity to modify the library and relink the application. + +You can find the patched version of WebKit used by Bun here: . If you would like to relink Bun with changes: + +- `git submodule update --init --recursive` +- `make jsc` +- `zig build` + +This compiles JavaScriptCore, compiles Bun’s `.cpp` bindings for JavaScriptCore (which are the object files using JavaScriptCore) and outputs a new `bun` binary with your changes. + +## Linked libraries + +Bun statically links these libraries: + +| Library | License | +|---------|---------| +| [`boringssl`](https://boringssl.googlesource.com/boringssl/) | [several licenses](https://boringssl.googlesource.com/boringssl/+/refs/heads/master/LICENSE) | +| [`brotli`](https://github.com/google/brotli) | MIT | +| [`libarchive`](https://github.com/libarchive/libarchive) | [several licenses](https://github.com/libarchive/libarchive/blob/master/COPYING) | +| [`lol-html`](https://github.com/cloudflare/lol-html/tree/master/c-api) | BSD 3-Clause | +| [`mimalloc`](https://github.com/microsoft/mimalloc) | MIT | +| [`picohttp`](https://github.com/h2o/picohttpparser) | dual-licensed under the Perl License or the MIT License | +| [`zstd`](https://github.com/facebook/zstd) | dual-licensed under the BSD License or GPLv2 license | +| [`simdutf`](https://github.com/simdutf/simdutf) | Apache 2.0 | +| [`tinycc`](https://github.com/tinycc/tinycc) | LGPL v2.1 | +| [`uSockets`](https://github.com/uNetworking/uSockets) | Apache 2.0 | +| [`zlib-cloudflare`](https://github.com/cloudflare/zlib) | zlib | +| [`c-ares`](https://github.com/c-ares/c-ares) | MIT licensed | +| [`libicu`](https://github.com/unicode-org/icu) 72 | [license here](https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE) | +| [`libbase64`](https://github.com/aklomp/base64/blob/master/LICENSE) | BSD 2-Clause | +| [`libuv`](https://github.com/libuv/libuv) (on Windows) | MIT | +| [`libdeflate`](https://github.com/ebiggers/libdeflate) | MIT | +| A fork of [`uWebsockets`](https://github.com/jarred-sumner/uwebsockets) | Apache 2.0 licensed | +| Parts of [Tigerbeetle's IO code](https://github.com/tigerbeetle/tigerbeetle/blob/532c8b70b9142c17e07737ab6d3da68d7500cbca/src/io/windows.zig#L1) | Apache 2.0 licensed | + +## Polyfills + +For compatibility reasons, the following packages are embedded into Bun's binary and injected if imported. + +| Package | License | +|---------|---------| +| [`assert`](https://npmjs.com/package/assert) | MIT | +| [`browserify-zlib`](https://npmjs.com/package/browserify-zlib) | MIT | +| [`buffer`](https://npmjs.com/package/buffer) | MIT | +| [`constants-browserify`](https://npmjs.com/package/constants-browserify) | MIT | +| [`crypto-browserify`](https://npmjs.com/package/crypto-browserify) | MIT | +| [`domain-browser`](https://npmjs.com/package/domain-browser) | MIT | +| [`events`](https://npmjs.com/package/events) | MIT | +| [`https-browserify`](https://npmjs.com/package/https-browserify) | MIT | +| [`os-browserify`](https://npmjs.com/package/os-browserify) | MIT | +| [`path-browserify`](https://npmjs.com/package/path-browserify) | MIT | +| [`process`](https://npmjs.com/package/process) | MIT | +| [`punycode`](https://npmjs.com/package/punycode) | MIT | +| [`querystring-es3`](https://npmjs.com/package/querystring-es3) | MIT | +| [`stream-browserify`](https://npmjs.com/package/stream-browserify) | MIT | +| [`stream-http`](https://npmjs.com/package/stream-http) | MIT | +| [`string_decoder`](https://npmjs.com/package/string_decoder) | MIT | +| [`timers-browserify`](https://npmjs.com/package/timers-browserify) | MIT | +| [`tty-browserify`](https://npmjs.com/package/tty-browserify) | MIT | +| [`url`](https://npmjs.com/package/url) | MIT | +| [`util`](https://npmjs.com/package/util) | MIT | +| [`vm-browserify`](https://npmjs.com/package/vm-browserify) | MIT | + +## Additional credits + +- Bun's JS transpiler, CSS lexer, and Node.js module resolver source code is a Zig port of [@evanw](https://github.com/evanw)’s [esbuild](https://github.com/evanw/esbuild) project. +- Credit to [@kipply](https://github.com/kipply) for the name "Bun"! \ No newline at end of file diff --git a/Makefile b/Makefile index 5ffca73ee4b87..49a040f2038e7 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,10 @@ +# ------------------------------------------------------------ +# WARNING +# ------------------------------------------------------------ +# This file is very old and will be removed soon! +# You can build Bun using `cmake` or `bun run build` +# ------------------------------------------------------------ + SHELL := $(shell which bash) # Use bash syntax to be consistent OS_NAME := $(shell uname -s | tr '[:upper:]' '[:lower:]') @@ -26,8 +33,11 @@ ifeq ($(ARCH_NAME_RAW),arm64) ARCH_NAME = aarch64 DOCKER_BUILDARCH = arm64 BREW_PREFIX_PATH = /opt/homebrew -DEFAULT_MIN_MACOS_VERSION = 11.0 +DEFAULT_MIN_MACOS_VERSION = 13.0 MARCH_NATIVE = -mtune=$(CPU_TARGET) +ifeq ($(OS_NAME),linux) +MARCH_NATIVE = -march=armv8-a+crc -mtune=ampere1 +endif else ARCH_NAME = x64 DOCKER_BUILDARCH = amd64 @@ -129,7 +139,7 @@ SED = $(shell which gsed 2>/dev/null || which sed 2>/dev/null) BUN_DIR ?= $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BUN_DEPS_DIR ?= $(shell pwd)/src/deps -BUN_DEPS_OUT_DIR ?= $(BUN_DEPS_DIR) +BUN_DEPS_OUT_DIR ?= $(shell pwd)/build/bun-deps CPU_COUNT = 2 ifeq ($(OS_NAME),darwin) CPU_COUNT = $(shell sysctl -n hw.logicalcpu) @@ -154,7 +164,12 @@ CMAKE_FLAGS_WITHOUT_RELEASE = -DCMAKE_C_COMPILER=$(CC) \ -DCMAKE_OSX_DEPLOYMENT_TARGET=$(MIN_MACOS_VERSION) \ $(CMAKE_CXX_COMPILER_LAUNCHER_FLAG) \ -DCMAKE_AR=$(AR) \ - -DCMAKE_RANLIB=$(which llvm-16-ranlib 2>/dev/null || which llvm-ranlib 2>/dev/null) + -DCMAKE_RANLIB=$(which llvm-16-ranlib 2>/dev/null || which llvm-ranlib 2>/dev/null) \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_C_STANDARD=17 \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_C_STANDARD_REQUIRED=ON \ + -DCMAKE_CXX_EXTENSIONS=ON @@ -181,8 +196,8 @@ endif OPTIMIZATION_LEVEL=-O3 $(MARCH_NATIVE) DEBUG_OPTIMIZATION_LEVEL= -O1 $(MARCH_NATIVE) -gdwarf-4 -CFLAGS_WITHOUT_MARCH = $(MACOS_MIN_FLAG) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -BUN_CFLAGS = $(MACOS_MIN_FLAG) $(MARCH_NATIVE) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden +CFLAGS_WITHOUT_MARCH = $(MACOS_MIN_FLAG) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-pie -fno-pic +BUN_CFLAGS = $(MACOS_MIN_FLAG) $(MARCH_NATIVE) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-pie -fno-pic BUN_TMP_DIR := /tmp/make-bun CFLAGS=$(CFLAGS_WITHOUT_MARCH) $(MARCH_NATIVE) @@ -358,7 +373,7 @@ ifeq ($(OS_NAME),linux) endif ifeq ($(OS_NAME),darwin) -MACOS_MIN_FLAG=-mmacosx-version-min=$(MIN_MACOS_VERSION) +MACOS_MIN_FLAG=-mmacos-version-min=$(MIN_MACOS_VERSION) POSIX_PKG_MANAGER=brew INCLUDE_DIRS += $(MAC_INCLUDE_DIRS) endif @@ -920,7 +935,7 @@ bun-codesign-release-local-debug: .PHONY: jsc jsc: jsc-build jsc-copy-headers jsc-bindings .PHONY: jsc-debug -jsc-debug: jsc-build-debug jsc-copy-headers-debug +jsc-debug: jsc-build-debug .PHONY: jsc-build jsc-build: $(JSC_BUILD_STEPS) .PHONY: jsc-build-debug @@ -1301,6 +1316,7 @@ jsc-build-mac-compile-debug: -DCMAKE_BUILD_TYPE=Debug \ -DUSE_THIN_ARCHIVES=OFF \ -DENABLE_FTL_JIT=ON \ + -DENABLE_MALLOC_HEAP_BREAKDOWN=ON \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DUSE_BUN_JSC_ADDITIONS=ON \ -DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON \ @@ -1380,10 +1396,10 @@ jsc-build-linux-compile-build-debug: jsc-build-mac: jsc-force-fastjit jsc-build-mac-compile jsc-build-copy -jsc-build-mac-debug: jsc-force-fastjit jsc-build-mac-compile-debug jsc-build-copy-debug +jsc-build-mac-debug: jsc-force-fastjit jsc-build-mac-compile-debug jsc-build-linux: jsc-build-linux-compile-config jsc-build-linux-compile-build jsc-build-copy -jsc-build-linux-debug: jsc-build-linux-compile-config-debug jsc-build-linux-compile-build-debug jsc-build-copy-debug +jsc-build-linux-debug: jsc-build-linux-compile-config-debug jsc-build-linux-compile-build-debug jsc-build-copy: cp $(WEBKIT_RELEASE_DIR)/lib/libJavaScriptCore.a $(BUN_DEPS_OUT_DIR)/libJavaScriptCore.a diff --git a/README.md b/README.md index 63c6d5e0b710e..cf407d1a25c10 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,6 @@ ## What is Bun? -> **Bun is under active development.** Use it to speed up your development workflows or run simpler production code in resource-constrained environments like serverless functions. We're working on more complete Node.js compatibility and integration with existing frameworks. Join the [Discord](https://bun.sh/discord) and watch the [GitHub repository](https://github.com/oven-sh/bun) to keep tabs on future releases. - Bun is an all-in-one toolkit for JavaScript and TypeScript apps. It ships as a single executable called `bun`. At its core is the _Bun runtime_, a fast JavaScript runtime designed as a drop-in replacement for Node.js. It's written in Zig and powered by JavaScriptCore under the hood, dramatically reducing startup times and memory usage. @@ -87,16 +85,19 @@ bun upgrade --canary ## Quick links - Intro + - [What is Bun?](https://bun.sh/docs/index) - [Installation](https://bun.sh/docs/installation) - [Quickstart](https://bun.sh/docs/quickstart) - [TypeScript](https://bun.sh/docs/typescript) - Templating + - [`bun init`](https://bun.sh/docs/cli/init) - [`bun create`](https://bun.sh/docs/cli/bun-create) - Runtime + - [`bun run`](https://bun.sh/docs/cli/run) - [File types](https://bun.sh/docs/runtime/loaders) - [TypeScript](https://bun.sh/docs/runtime/typescript) @@ -115,6 +116,7 @@ bun upgrade --canary - [Framework API](https://bun.sh/docs/runtime/framework) - Package manager + - [`bun install`](https://bun.sh/docs/cli/install) - [`bun add`](https://bun.sh/docs/cli/add) - [`bun remove`](https://bun.sh/docs/cli/remove) @@ -130,6 +132,7 @@ bun upgrade --canary - [Overrides and resolutions](https://bun.sh/docs/install/overrides) - Bundler + - [`Bun.build`](https://bun.sh/docs/bundler) - [Loaders](https://bun.sh/docs/bundler/loaders) - [Plugins](https://bun.sh/docs/bundler/plugins) @@ -137,6 +140,7 @@ bun upgrade --canary - [vs esbuild](https://bun.sh/docs/bundler/vs-esbuild) - Test runner + - [`bun test`](https://bun.sh/docs/cli/test) - [Writing tests](https://bun.sh/docs/test/writing) - [Watch mode](https://bun.sh/docs/test/hot) @@ -148,9 +152,11 @@ bun upgrade --canary - [Code coverage](https://bun.sh/docs/test/coverage) - Package runner + - [`bunx`](https://bun.sh/docs/cli/bunx) - API + - [HTTP server](https://bun.sh/docs/api/http) - [WebSockets](https://bun.sh/docs/api/websockets) - [Workers](https://bun.sh/docs/api/workers) @@ -183,9 +189,10 @@ bun upgrade --canary - [Building Windows](https://bun.sh/docs/project/building-windows) - [License](https://bun.sh/docs/project/licensing) -## Guides +## Guides + +- Binary -- Binary - [Convert a Blob to a DataView](https://bun.sh/guides/binary/blob-to-dataview) - [Convert a Blob to a ReadableStream](https://bun.sh/guides/binary/blob-to-stream) - [Convert a Blob to a string](https://bun.sh/guides/binary/blob-to-string) @@ -209,7 +216,8 @@ bun upgrade --canary - [Convert an ArrayBuffer to a Uint8Array](https://bun.sh/guides/binary/arraybuffer-to-typedarray) - [Convert an ArrayBuffer to an array of numbers](https://bun.sh/guides/binary/arraybuffer-to-array) -- Ecosystem +- Ecosystem + - [Build a frontend using Vite and Bun](https://bun.sh/guides/ecosystem/vite) - [Build an app with Astro and Bun](https://bun.sh/guides/ecosystem/astro) - [Build an app with Next.js and Bun](https://bun.sh/guides/ecosystem/nextjs) @@ -236,7 +244,8 @@ bun upgrade --canary - [Use React and JSX](https://bun.sh/guides/ecosystem/react) - [Add Sentry to a Bun app](https://bun.sh/guides/ecosystem/sentry) -- HTTP +- HTTP + - [Common HTTP server usage](https://bun.sh/guides/http/server) - [Configure TLS on an HTTP server](https://bun.sh/guides/http/tls) - [fetch with unix domain sockets in Bun](https://bun.sh/guides/http/fetch-unix) @@ -250,7 +259,8 @@ bun upgrade --canary - [Upload files via HTTP using FormData](https://bun.sh/guides/http/file-uploads) - [Write a simple HTTP server](https://bun.sh/guides/http/simple) -- Install +- Install + - [Add a dependency](https://bun.sh/guides/install/add) - [Add a development dependency](https://bun.sh/guides/install/add-dev) - [Add a Git dependency](https://bun.sh/guides/install/add-git) @@ -268,7 +278,8 @@ bun upgrade --canary - [Using bun install with an Azure Artifacts npm registry](https://bun.sh/guides/install/azure-artifacts) - [Using bun install with Artifactory](https://bun.sh/guides/install/jfrog-artifactory) -- Process +- Process + - [Get the process uptime in nanoseconds](https://bun.sh/guides/process/nanoseconds) - [Listen for CTRL+C](https://bun.sh/guides/process/ctrl-c) - [Listen to OS signals](https://bun.sh/guides/process/os-signals) @@ -279,7 +290,8 @@ bun upgrade --canary - [Spawn a child process](https://bun.sh/guides/process/spawn) - [Spawn a child process and communicate using IPC](https://bun.sh/guides/process/ipc) -- Read file +- Read file + - [Check if a file exists](https://bun.sh/guides/read-file/exists) - [Get the MIME type of a file](https://bun.sh/guides/read-file/mime) - [Read a file as a ReadableStream](https://bun.sh/guides/read-file/stream) @@ -290,7 +302,8 @@ bun upgrade --canary - [Read a JSON file](https://bun.sh/guides/read-file/json) - [Watch a directory for changes](https://bun.sh/guides/read-file/watch) -- Runtime +- Runtime + - [Debugging Bun with the VS Code extension](https://bun.sh/guides/runtime/vscode-debugger) - [Debugging Bun with the web debugger](https://bun.sh/guides/runtime/web-debugger) - [Define and replace static globals & constants](https://bun.sh/guides/runtime/define-constant) @@ -305,7 +318,8 @@ bun upgrade --canary - [Set a time zone in Bun](https://bun.sh/guides/runtime/timezone) - [Set environment variables](https://bun.sh/guides/runtime/set-env) -- Streams +- Streams + - [Convert a Node.js Readable to a Blob](https://bun.sh/guides/streams/node-readable-to-blob) - [Convert a Node.js Readable to a string](https://bun.sh/guides/streams/node-readable-to-string) - [Convert a Node.js Readable to an ArrayBuffer](https://bun.sh/guides/streams/node-readable-to-arraybuffer) @@ -318,7 +332,8 @@ bun upgrade --canary - [Convert a ReadableStream to an ArrayBuffer](https://bun.sh/guides/streams/to-arraybuffer) - [Convert a ReadableStream to JSON](https://bun.sh/guides/streams/to-json) -- Test +- Test + - [Bail early with the Bun test runner](https://bun.sh/guides/test/bail) - [Generate code coverage reports with the Bun test runner](https://bun.sh/guides/test/coverage) - [Mark a test as a "todo" with the Bun test runner](https://bun.sh/guides/test/todo-tests) @@ -336,7 +351,8 @@ bun upgrade --canary - [Use snapshot testing in `bun test`](https://bun.sh/guides/test/snapshot) - [Write browser DOM tests with Bun and happy-dom](https://bun.sh/guides/test/happy-dom) -- Util +- Util + - [Check if the current file is the entrypoint](https://bun.sh/guides/util/entrypoint) - [Check if two objects are deeply equal](https://bun.sh/guides/util/deep-equals) - [Compress and decompress data with DEFLATE](https://bun.sh/guides/util/deflate) @@ -355,13 +371,14 @@ bun upgrade --canary - [Hash a password](https://bun.sh/guides/util/hash-a-password) - [Sleep for a fixed number of milliseconds](https://bun.sh/guides/util/sleep) -- WebSocket +- WebSocket + - [Build a publish-subscribe WebSocket server](https://bun.sh/guides/websocket/pubsub) - [Build a simple WebSocket server](https://bun.sh/guides/websocket/simple) - [Enable compression for WebSocket messages](https://bun.sh/guides/websocket/compression) - [Set per-socket contextual data on a WebSocket](https://bun.sh/guides/websocket/context) -- Write file +- Write file - [Append content to a file](https://bun.sh/guides/write-file/append) - [Copy a file to another location](https://bun.sh/guides/write-file/file-cp) - [Delete a file](https://bun.sh/guides/write-file/unlink) diff --git a/bench/async/bun.js b/bench/async/bun.js index 51d0d119bbb57..e99e05a2dc22a 100644 --- a/bench/async/bun.js +++ b/bench/async/bun.js @@ -1,4 +1,4 @@ -import { run, bench } from "mitata"; +import { bench, run } from "mitata"; bench("sync", () => {}); bench("async", async () => {}); diff --git a/bench/async/deno.js b/bench/async/deno.js index 9e4347b539786..4e691a1cdb238 100644 --- a/bench/async/deno.js +++ b/bench/async/deno.js @@ -1,4 +1,4 @@ -import { run, bench } from "../node_modules/mitata/src/cli.mjs"; +import { bench, run } from "../node_modules/mitata/src/cli.mjs"; bench("sync", () => {}); bench("async", async () => {}); diff --git a/bench/async/node.mjs b/bench/async/node.mjs index 51d0d119bbb57..e99e05a2dc22a 100644 --- a/bench/async/node.mjs +++ b/bench/async/node.mjs @@ -1,4 +1,4 @@ -import { run, bench } from "mitata"; +import { bench, run } from "mitata"; bench("sync", () => {}); bench("async", async () => {}); diff --git a/bench/bun.lockb b/bench/bun.lockb index 78eea70b0ef1b..679ce8aba1293 100755 Binary files a/bench/bun.lockb and b/bench/bun.lockb differ diff --git a/bench/copyfile/node.mitata.mjs b/bench/copyfile/node.mitata.mjs index 379150487f38e..90bf6fe0f649b 100644 --- a/bench/copyfile/node.mitata.mjs +++ b/bench/copyfile/node.mitata.mjs @@ -1,5 +1,5 @@ -import { copyFileSync, writeFileSync, readFileSync, statSync } from "node:fs"; import { bench, run } from "mitata"; +import { copyFileSync, statSync, writeFileSync } from "node:fs"; function runner(ready) { for (let size of [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]) { diff --git a/bench/crypto/asymmetricCipher.js b/bench/crypto/asymmetricCipher.js new file mode 100644 index 0000000000000..4f7c623b7dd0a --- /dev/null +++ b/bench/crypto/asymmetricCipher.js @@ -0,0 +1,24 @@ +import { bench, run } from "mitata"; +const crypto = require("node:crypto"); + +const keyPair = crypto.generateKeyPairSync("rsa", { + modulusLength: 2048, + publicKeyEncoding: { + type: "spki", + format: "pem", + }, + privateKeyEncoding: { + type: "pkcs8", + format: "pem", + }, +}); + +// Max message size for 2048-bit RSA keys +const plaintext = crypto.getRandomValues(Buffer.alloc(214)); + +bench("RSA_PKCS1_OAEP_PADDING round-trip", () => { + const ciphertext = crypto.publicEncrypt(keyPair.publicKey, plaintext); + crypto.privateDecrypt(keyPair.privateKey, ciphertext); +}); + +await run(); diff --git a/bench/deepEqual/map.js b/bench/deepEqual/map.js new file mode 100644 index 0000000000000..51070e466fced --- /dev/null +++ b/bench/deepEqual/map.js @@ -0,0 +1,27 @@ +import { expect } from "bun:test"; +import { bench, run } from "mitata"; + +const MAP_SIZE = 10_000; + +function* genPairs(count) { + for (let i = 0; i < MAP_SIZE; i++) { + yield ["k" + i, "v" + i]; + } +} + +class CustomMap extends Map { + abc = 123; + constructor(iterable) { + super(iterable); + } +} + +const a = new Map(genPairs()); +const b = new Map(genPairs()); +bench("deepEqual Map", () => expect(a).toEqual(b)); + +const x = new CustomMap(genPairs()); +const y = new CustomMap(genPairs()); +bench("deepEqual CustomMap", () => expect(x).toEqual(y)); + +await run(); diff --git a/bench/deepEqual/set.js b/bench/deepEqual/set.js new file mode 100644 index 0000000000000..4deef8847bbf3 --- /dev/null +++ b/bench/deepEqual/set.js @@ -0,0 +1,27 @@ +import { expect } from "bun:test"; +import { bench, run } from "mitata"; + +const SET_SIZE = 10_000; + +function* genValues(count) { + for (let i = 0; i < SET_SIZE; i++) { + yield "v" + i; + } +} + +class CustomSet extends Set { + abc = 123; + constructor(iterable) { + super(iterable); + } +} + +const a = new Set(genValues()); +const b = new Set(genValues()); +bench("deepEqual Set", () => expect(a).toEqual(b)); + +const x = new CustomSet(genValues()); +const y = new CustomSet(genValues()); +bench("deepEqual CustomSet", () => expect(x).toEqual(y)); + +await run(); diff --git a/bench/emitter/implementations.mjs b/bench/emitter/implementations.mjs index 2050ac38e01bb..a925bdce78972 100644 --- a/bench/emitter/implementations.mjs +++ b/bench/emitter/implementations.mjs @@ -1,4 +1,3 @@ -import EventEmitter3 from "eventemitter3"; import { group } from "mitata"; import EventEmitterNative from "node:events"; diff --git a/bench/expect-to-equal/expect-to-equal.test.js b/bench/expect-to-equal/expect-to-equal.test.js index e8361596f5634..52a904382fcc3 100644 --- a/bench/expect-to-equal/expect-to-equal.test.js +++ b/bench/expect-to-equal/expect-to-equal.test.js @@ -1,5 +1,5 @@ // bun:test automatically rewrites this import to bun:test when run in bun -import { test, expect } from "@jest/globals"; +import { expect, test } from "@jest/globals"; const N = parseInt(process.env.RUN_COUNT || "10000", 10); if (!Number.isSafeInteger(N)) { diff --git a/bench/expect-to-equal/expect-to-equal.vitest.test.js b/bench/expect-to-equal/expect-to-equal.vitest.test.js index aea945180b9ec..d02b56e3e8e6c 100644 --- a/bench/expect-to-equal/expect-to-equal.vitest.test.js +++ b/bench/expect-to-equal/expect-to-equal.vitest.test.js @@ -1,4 +1,4 @@ -import { test, expect } from "vitest"; +import { expect, test } from "vitest"; const N = parseInt(process.env.RUN_COUNT || "10000", 10); if (!Number.isSafeInteger(N)) { diff --git a/bench/ffi/bun.js b/bench/ffi/bun.js index 6e83702ee0ca7..bceebc20b9559 100644 --- a/bench/ffi/bun.js +++ b/bench/ffi/bun.js @@ -1,5 +1,5 @@ -import { ptr, dlopen, CString, toBuffer } from "bun:ffi"; -import { run, bench, group } from "mitata"; +import { CString, dlopen, ptr } from "bun:ffi"; +import { bench, group, run } from "mitata"; const { napiNoop, napiHash, napiString } = require(import.meta.dir + "/src/ffi_napi_bench.node"); diff --git a/bench/ffi/deno.js b/bench/ffi/deno.js index 63ba6358c8974..72d0a849b823b 100644 --- a/bench/ffi/deno.js +++ b/bench/ffi/deno.js @@ -1,4 +1,4 @@ -import { run, bench, group } from "../node_modules/mitata/src/cli.mjs"; +import { bench, group, run } from "../node_modules/mitata/src/cli.mjs"; const extension = "darwin" !== Deno.build.os ? "so" : "dylib"; const path = new URL("src/target/release/libffi_napi_bench." + extension, import.meta.url).pathname; diff --git a/bench/ffi/node.mjs b/bench/ffi/node.mjs index 8c2d069717278..db743024b81b1 100644 --- a/bench/ffi/node.mjs +++ b/bench/ffi/node.mjs @@ -1,4 +1,4 @@ -import { run, bench, group } from "mitata"; +import { bench, group, run } from "mitata"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); diff --git a/bench/glob/braces.mjs b/bench/glob/braces.mjs index c81aeb9d98067..2e4a9c1c21e9d 100644 --- a/bench/glob/braces.mjs +++ b/bench/glob/braces.mjs @@ -1,5 +1,5 @@ import braces from "braces"; -import { group, bench, run } from "mitata"; +import { bench, group, run } from "mitata"; // const iterations = 1000; const iterations = 100; diff --git a/bench/glob/scan.mjs b/bench/glob/scan.mjs index 0d500af66870b..ce6721a16fcf8 100644 --- a/bench/glob/scan.mjs +++ b/bench/glob/scan.mjs @@ -1,6 +1,6 @@ -import { run, bench, group } from "mitata"; import fg from "fast-glob"; import { fdir } from "fdir"; +import { bench, group, run } from "mitata"; const normalPattern = "*.ts"; const recursivePattern = "**/*.ts"; diff --git a/bench/gzip/bun.js b/bench/gzip/bun.js index 1c5cdcaddd9ed..cfe8615f80438 100644 --- a/bench/gzip/bun.js +++ b/bench/gzip/bun.js @@ -1,20 +1,43 @@ -import { run, bench } from "mitata"; -import { gzipSync, gunzipSync } from "bun"; +import { gunzipSync, gzipSync } from "bun"; +import { bench, group, run } from "mitata"; -const data = new TextEncoder().encode("Hello World!".repeat(9999)); +const data = await Bun.file(require.resolve("@babel/standalone/babel.min.js")).arrayBuffer(); const compressed = gzipSync(data); -bench(`roundtrip - "Hello World!".repeat(9999))`, () => { - gunzipSync(gzipSync(data)); +const libraries = ["zlib"]; +if (Bun.semver.satisfies(Bun.version.replaceAll("-debug", ""), ">=1.1.21")) { + libraries.push("libdeflate"); +} +const options = { library: undefined }; +const benchFn = (name, fn) => { + if (libraries.length > 1) { + group(name, () => { + for (const library of libraries) { + bench(library, () => { + options.library = library; + fn(); + }); + } + }); + } else { + options.library = libraries[0]; + bench(name, () => { + fn(); + }); + } +}; + +benchFn(`roundtrip - @babel/standalone/babel.min.js`, () => { + gunzipSync(gzipSync(data, options), options); }); -bench(`gzipSync("Hello World!".repeat(9999)))`, () => { - gzipSync(data); +benchFn(`gzipSync(@babel/standalone/babel.min.js`, () => { + gzipSync(data, options); }); -bench(`gunzipSync("Hello World!".repeat(9999)))`, () => { - gunzipSync(compressed); +benchFn(`gunzipSync(@babel/standalone/babel.min.js`, () => { + gunzipSync(compressed, options); }); await run(); diff --git a/bench/gzip/bun.lockb b/bench/gzip/bun.lockb new file mode 100755 index 0000000000000..96feac42873a3 Binary files /dev/null and b/bench/gzip/bun.lockb differ diff --git a/bench/gzip/deno.js b/bench/gzip/deno.js index 66c858e55b35f..0c7f73b37cd33 100644 --- a/bench/gzip/deno.js +++ b/bench/gzip/deno.js @@ -1,4 +1,4 @@ -import { run, bench } from "../node_modules/mitata/src/cli.mjs"; +import { bench, run } from "../node_modules/mitata/src/cli.mjs"; const data = new TextEncoder().encode("Hello World!".repeat(9999)); diff --git a/bench/gzip/node.mjs b/bench/gzip/node.mjs index 0d6ea51249131..4d1125b36865b 100644 --- a/bench/gzip/node.mjs +++ b/bench/gzip/node.mjs @@ -1,19 +1,22 @@ -import { run, bench } from "mitata"; -import { gzipSync, gunzipSync } from "zlib"; +import { readFileSync } from "fs"; +import { bench, run } from "mitata"; +import { createRequire } from "module"; +import { gunzipSync, gzipSync } from "zlib"; -const data = new TextEncoder().encode("Hello World!".repeat(9999)); +const require = createRequire(import.meta.url); +const data = readFileSync(require.resolve("@babel/standalone/babel.min.js")); const compressed = gzipSync(data); -bench(`roundtrip - "Hello World!".repeat(9999))`, () => { +bench(`roundtrip - @babel/standalone/babel.min.js)`, () => { gunzipSync(gzipSync(data)); }); -bench(`gzipSync("Hello World!".repeat(9999)))`, () => { +bench(`gzipSync(@babel/standalone/babel.min.js))`, () => { gzipSync(data); }); -bench(`gunzipSync("Hello World!".repeat(9999)))`, () => { +bench(`gunzipSync(@babel/standalone/babel.min.js))`, () => { gunzipSync(compressed); }); diff --git a/bench/gzip/package.json b/bench/gzip/package.json index f5c377686b1dd..49e6c3a890f01 100644 --- a/bench/gzip/package.json +++ b/bench/gzip/package.json @@ -7,5 +7,8 @@ "bench:node": "$NODE node.mjs", "bench:deno": "$DENO run -A --unstable deno.js", "bench": "bun run bench:bun && bun run bench:node && bun run bench:deno" + }, + "dependencies": { + "@babel/standalone": "7.24.10" } } diff --git a/bench/hot-module-reloading/css-stress-test/src/index.tsx b/bench/hot-module-reloading/css-stress-test/src/index.tsx index 7ca290f48a752..5eefb430406aa 100644 --- a/bench/hot-module-reloading/css-stress-test/src/index.tsx +++ b/bench/hot-module-reloading/css-stress-test/src/index.tsx @@ -1,6 +1,5 @@ -import { Main } from "./main"; -import classNames from "classnames"; import ReactDOM from "react-dom"; +import { Main } from "./main"; const Base = ({}) => { const name = typeof location !== "undefined" ? decodeURIComponent(location.search.substring(1)) : null; diff --git a/bench/install/app/entry.server.tsx b/bench/install/app/entry.server.tsx index fbea6220e2b89..a83df79c87db3 100644 --- a/bench/install/app/entry.server.tsx +++ b/bench/install/app/entry.server.tsx @@ -4,11 +4,11 @@ * For more information, see https://remix.run/docs/en/main/file-conventions/entry.server */ -import { PassThrough } from "node:stream"; import type { EntryContext } from "@remix-run/node"; import { Response } from "@remix-run/node"; import { RemixServer } from "@remix-run/react"; import isbot from "isbot"; +import { PassThrough } from "node:stream"; import { renderToPipeableStream } from "react-dom/server"; const ABORT_DELAY = 5_000; diff --git a/bench/modules/node_os/bun.js b/bench/modules/node_os/bun.js index 217fae47dac9e..4405c9a45688d 100644 --- a/bench/modules/node_os/bun.js +++ b/bench/modules/node_os/bun.js @@ -1,21 +1,21 @@ import { bench, run } from "mitata"; import { + arch, cpus, endianness, - arch, - uptime, - networkInterfaces, - getPriority, - totalmem, freemem, + getPriority, homedir, hostname, loadavg, + networkInterfaces, platform, release, setPriority, tmpdir, + totalmem, type, + uptime, userInfo, version, } from "node:os"; diff --git a/bench/modules/node_os/node.mjs b/bench/modules/node_os/node.mjs index 217fae47dac9e..4405c9a45688d 100644 --- a/bench/modules/node_os/node.mjs +++ b/bench/modules/node_os/node.mjs @@ -1,21 +1,21 @@ import { bench, run } from "mitata"; import { + arch, cpus, endianness, - arch, - uptime, - networkInterfaces, - getPriority, - totalmem, freemem, + getPriority, homedir, hostname, loadavg, + networkInterfaces, platform, release, setPriority, tmpdir, + totalmem, type, + uptime, userInfo, version, } from "node:os"; diff --git a/bench/package.json b/bench/package.json index 761fa8936ebf1..6ea67f5c993df 100644 --- a/bench/package.json +++ b/bench/package.json @@ -3,6 +3,7 @@ "dependencies": { "@babel/core": "^7.16.10", "@babel/preset-react": "^7.16.7", + "@babel/standalone": "^7.24.7", "@swc/core": "^1.2.133", "benchmark": "^2.1.4", "braces": "^3.0.2", diff --git a/bench/react-hello-world/react-hello-world.deno.jsx b/bench/react-hello-world/react-hello-world.deno.jsx index 0bea2574a2925..08cb7b022251d 100644 --- a/bench/react-hello-world/react-hello-world.deno.jsx +++ b/bench/react-hello-world/react-hello-world.deno.jsx @@ -1,5 +1,5 @@ -import { renderToReadableStream } from "https://esm.run/react-dom/server"; import * as React from "https://esm.run/react"; +import { renderToReadableStream } from "https://esm.run/react-dom/server"; const App = () => ( diff --git a/bench/react-hello-world/react-hello-world.node.jsx b/bench/react-hello-world/react-hello-world.node.jsx index 2f3c061490089..52dd3b056612e 100644 --- a/bench/react-hello-world/react-hello-world.node.jsx +++ b/bench/react-hello-world/react-hello-world.node.jsx @@ -1,11 +1,12 @@ // react-ssr.tsx -import { renderToPipeableStream } from "react-dom/server.node"; import React from "react"; +import { renderToPipeableStream } from "react-dom/server.node"; const http = require("http"); const App = () => (

Hello World

+

This is an example.

); diff --git a/bench/scanner/remix-route.ts b/bench/scanner/remix-route.ts index dbacf3a4ca2a1..e9d0880eed02c 100644 --- a/bench/scanner/remix-route.ts +++ b/bench/scanner/remix-route.ts @@ -1,5 +1,5 @@ +import type { ActionFunction, LoaderFunction } from "remix"; import { useParams } from "remix"; -import type { LoaderFunction, ActionFunction } from "remix"; export const loader: LoaderFunction = async ({ params }) => { console.log(params.postId); diff --git a/bench/snippets/assert.mjs b/bench/snippets/assert.mjs index 3b3284e54ba18..120363aa07e86 100644 --- a/bench/snippets/assert.mjs +++ b/bench/snippets/assert.mjs @@ -1,5 +1,5 @@ -import { bench, group, run } from "./runner.mjs"; import * as assert from "assert"; +import { bench, run } from "./runner.mjs"; bench("deepEqual", () => { assert.deepEqual({ foo: "123", bar: "baz" }, { foo: "123", bar: "baz" }); diff --git a/bench/snippets/buffer-create.mjs b/bench/snippets/buffer-create.mjs index 115f8dd4aaf01..0093f6de8324d 100644 --- a/bench/snippets/buffer-create.mjs +++ b/bench/snippets/buffer-create.mjs @@ -1,7 +1,7 @@ // @runtime bun,node,deno -import { bench, run } from "./runner.mjs"; -import process from "node:process"; import { Buffer } from "node:buffer"; +import process from "node:process"; +import { bench, run } from "./runner.mjs"; const N = parseInt(process.env.RUN_COUNTER ?? "10000", 10); var isBuffer = new Buffer(0); diff --git a/bench/snippets/buffer-to-string.mjs b/bench/snippets/buffer-to-string.mjs index c4dac62081260..f59470f6fafa4 100644 --- a/bench/snippets/buffer-to-string.mjs +++ b/bench/snippets/buffer-to-string.mjs @@ -1,6 +1,6 @@ -import { bench, run } from "./runner.mjs"; import { Buffer } from "node:buffer"; import crypto from "node:crypto"; +import { bench, run } from "./runner.mjs"; const bigBuffer = Buffer.from("hello world".repeat(10000)); const converted = bigBuffer.toString("base64"); diff --git a/bench/snippets/concat.js b/bench/snippets/concat.js index 76804dae19810..85a15c28964aa 100644 --- a/bench/snippets/concat.js +++ b/bench/snippets/concat.js @@ -1,6 +1,6 @@ -import { bench, group, run } from "./runner.mjs"; -import { readFileSync } from "fs"; import { allocUnsafe } from "bun"; +import { readFileSync } from "fs"; +import { bench, group, run } from "./runner.mjs"; function polyfill(chunks) { var size = 0; diff --git a/bench/snippets/crypto.mjs b/bench/snippets/crypto.mjs index 484a4295dd611..7c49fe92ee3e2 100644 --- a/bench/snippets/crypto.mjs +++ b/bench/snippets/crypto.mjs @@ -1,6 +1,6 @@ // so it can run in environments without node module resolution -import { bench, run } from "../node_modules/mitata/src/cli.mjs"; import crypto from "node:crypto"; +import { bench, run } from "../node_modules/mitata/src/cli.mjs"; var foo = new Uint8Array(65536); bench("crypto.getRandomValues(65536)", () => { crypto.getRandomValues(foo); diff --git a/bench/snippets/deep-equals.js b/bench/snippets/deep-equals.js index 53dee81ab4641..0b6949234223a 100644 --- a/bench/snippets/deep-equals.js +++ b/bench/snippets/deep-equals.js @@ -1,5 +1,5 @@ -import { bench, group, run } from "./runner.mjs"; import fastDeepEquals from "fast-deep-equal/es6/index"; +import { bench, group, run } from "./runner.mjs"; // const Date = globalThis.Date; function func1() {} diff --git a/bench/snippets/dns.ts b/bench/snippets/dns.ts index 7eeeea689b9aa..12ecfe1198b98 100644 --- a/bench/snippets/dns.ts +++ b/bench/snippets/dns.ts @@ -1,5 +1,5 @@ import { dns } from "bun"; -import { bench, run, group } from "./runner.mjs"; +import { bench, group, run } from "./runner.mjs"; async function forEachBackend(name, fn) { group(name, () => { diff --git a/bench/snippets/encode-into.mjs b/bench/snippets/encode-into.mjs index 5275b6f108f9c..70cb242f36f83 100644 --- a/bench/snippets/encode-into.mjs +++ b/bench/snippets/encode-into.mjs @@ -1,4 +1,4 @@ -import { run, bench } from "../node_modules/mitata/src/cli.mjs"; +import { bench, run } from "../node_modules/mitata/src/cli.mjs"; const encoder = new TextEncoder(); diff --git a/bench/snippets/escapeHTML.js b/bench/snippets/escapeHTML.js index 809666d71c9d7..96da26d9738e8 100644 --- a/bench/snippets/escapeHTML.js +++ b/bench/snippets/escapeHTML.js @@ -1,7 +1,4 @@ -import { group } from "./runner.mjs"; -import { bench, run } from "./runner.mjs"; -import { encode as htmlEntityEncode } from "html-entities"; -import { escape as heEscape } from "he"; +import { bench, group, run } from "./runner.mjs"; var bunEscapeHTML = globalThis.escapeHTML || Bun.escapeHTML; diff --git a/bench/snippets/ffi-overhead.mjs b/bench/snippets/ffi-overhead.mjs index bfb92634c2cf9..fed3228574e3c 100644 --- a/bench/snippets/ffi-overhead.mjs +++ b/bench/snippets/ffi-overhead.mjs @@ -1,4 +1,4 @@ -import { viewSource, dlopen, CString, ptr, toBuffer, toArrayBuffer, FFIType, callback } from "bun:ffi"; +import { dlopen } from "bun:ffi"; import { bench, group, run } from "./runner.mjs"; const types = { diff --git a/bench/snippets/new-incomingmessage.mjs b/bench/snippets/new-incomingmessage.mjs index ae480e0311e9e..2821ee876ae4c 100644 --- a/bench/snippets/new-incomingmessage.mjs +++ b/bench/snippets/new-incomingmessage.mjs @@ -1,5 +1,5 @@ -import { bench, run } from "./runner.mjs"; import { IncomingMessage } from "node:http"; +import { bench, run } from "./runner.mjs"; const headers = { date: "Mon, 06 Nov 2023 05:12:49 GMT", diff --git a/bench/snippets/node-vm.mjs b/bench/snippets/node-vm.mjs index 6f9d6077365eb..c8c2ac41bb7f0 100644 --- a/bench/snippets/node-vm.mjs +++ b/bench/snippets/node-vm.mjs @@ -1,6 +1,6 @@ // @runtime node, bun -import { bench, run } from "./runner.mjs"; import * as vm from "node:vm"; +import { bench, run } from "./runner.mjs"; const context = { animal: "cat", diff --git a/bench/snippets/object-values.mjs b/bench/snippets/object-values.mjs index 8dc62780bf7e1..5ca6db473ba95 100644 --- a/bench/snippets/object-values.mjs +++ b/bench/snippets/object-values.mjs @@ -24,7 +24,7 @@ const obj = { w: 23, }; -import { bench, group, run } from "./runner.mjs"; +import { bench, run } from "./runner.mjs"; var val = 0; bench("Object.values(literal)", () => { diff --git a/bench/snippets/path-resolve.mjs b/bench/snippets/path-resolve.mjs new file mode 100644 index 0000000000000..1e0a40456f6f7 --- /dev/null +++ b/bench/snippets/path-resolve.mjs @@ -0,0 +1,22 @@ +import { bench, run } from "mitata"; +import { posix } from "path"; + +const pathConfigurations = [ + "", + ".", + "./", + ["", ""].join("|"), + ["./abc.js"].join("|"), + ["foo/bar", "/tmp/file/", "..", "a/../subfile"].join("|"), + ["a/b/c/", "../../.."].join("|"), +]; + +pathConfigurations.forEach(paths => { + const args = paths.split("|"); + + bench(`resolve(${args.map(a => JSON.stringify(a)).join(", ")})`, () => { + globalThis.abc = posix.resolve(...args); + }); +}); + +await run(); diff --git a/bench/snippets/pbkdf2.mjs b/bench/snippets/pbkdf2.mjs index 6c21d3d6ea68e..a4f6ac8b58f9d 100644 --- a/bench/snippets/pbkdf2.mjs +++ b/bench/snippets/pbkdf2.mjs @@ -1,4 +1,4 @@ -import { pbkdf2, pbkdf2Sync } from "node:crypto"; +import { pbkdf2 } from "node:crypto"; import { bench, run } from "./runner.mjs"; diff --git a/bench/snippets/peek-promise.mjs b/bench/snippets/peek-promise.mjs index cabb15a313202..f883d4d4c5b68 100644 --- a/bench/snippets/peek-promise.mjs +++ b/bench/snippets/peek-promise.mjs @@ -1,5 +1,5 @@ -import { bench, run } from "mitata"; import { peek } from "bun"; +import { bench, run } from "mitata"; let pending = Bun.sleep(1000); let resolved = Promise.resolve(1); diff --git a/bench/snippets/performance-now-overhead.js b/bench/snippets/performance-now-overhead.js index 442d305639af8..b5283b6ff230f 100644 --- a/bench/snippets/performance-now-overhead.js +++ b/bench/snippets/performance-now-overhead.js @@ -1,4 +1,3 @@ -import { group } from "./runner.mjs"; import { bench, run } from "./runner.mjs"; bench("performance.now x 1000", () => { for (let i = 0; i < 1000; i++) { diff --git a/bench/snippets/process-cwd.mjs b/bench/snippets/process-cwd.mjs new file mode 100644 index 0000000000000..28df045acb2cc --- /dev/null +++ b/bench/snippets/process-cwd.mjs @@ -0,0 +1,7 @@ +import { bench, run } from "mitata"; + +bench("process.cwd()", () => { + process.cwd(); +}); + +await run(); diff --git a/bench/snippets/process-info.mjs b/bench/snippets/process-info.mjs index 0366472e5aa39..13e54e50e7954 100644 --- a/bench/snippets/process-info.mjs +++ b/bench/snippets/process-info.mjs @@ -1,5 +1,5 @@ -import { bench, run } from "./runner.mjs"; import { performance } from "perf_hooks"; +import { bench, run } from "./runner.mjs"; bench("process.memoryUsage()", () => { process.memoryUsage(); diff --git a/bench/snippets/react-dom-render.bun.js b/bench/snippets/react-dom-render.bun.js index b13508d75d2d7..11485b021e3f8 100644 --- a/bench/snippets/react-dom-render.bun.js +++ b/bench/snippets/react-dom-render.bun.js @@ -1,6 +1,6 @@ -import { bench, group, run } from "./runner.mjs"; -import { renderToReadableStream } from "react-dom/server.browser"; import { renderToReadableStream as renderToReadableStreamBun } from "react-dom/server"; +import { renderToReadableStream } from "react-dom/server.browser"; +import { bench, group, run } from "./runner.mjs"; const App = () => (
diff --git a/bench/snippets/read-file-chunk.mjs b/bench/snippets/read-file-chunk.mjs index e6a33a4992ac5..fafdd76b411a7 100644 --- a/bench/snippets/read-file-chunk.mjs +++ b/bench/snippets/read-file-chunk.mjs @@ -1,7 +1,7 @@ -import { tmpdir } from "node:os"; -import { bench, group, run } from "./runner.mjs"; import { createReadStream, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; import { sep } from "node:path"; +import { bench, run } from "./runner.mjs"; if (!Promise.withResolvers) { Promise.withResolvers = function () { diff --git a/bench/snippets/readdir.mjs b/bench/snippets/readdir.mjs index 37aefe6ac82b5..4afd214438bdb 100644 --- a/bench/snippets/readdir.mjs +++ b/bench/snippets/readdir.mjs @@ -1,10 +1,10 @@ -import { readdirSync, readdir as readdirCb } from "fs"; +import { createHash } from "crypto"; +import { readdirSync } from "fs"; import { readdir } from "fs/promises"; -import { bench, run } from "./runner.mjs"; +import { relative, resolve } from "path"; import { argv } from "process"; import { fileURLToPath } from "url"; -import { relative, resolve } from "path"; -import { createHash } from "crypto"; +import { bench, run } from "./runner.mjs"; let dir = resolve(argv.length > 2 ? argv[2] : fileURLToPath(new URL("../../node_modules", import.meta.url))); if (dir.includes(process.cwd())) { diff --git a/bench/snippets/readfile-not-found.mjs b/bench/snippets/readfile-not-found.mjs index c28100ba4a3bc..65c3a30e8ccba 100644 --- a/bench/snippets/readfile-not-found.mjs +++ b/bench/snippets/readfile-not-found.mjs @@ -1,6 +1,6 @@ -import { bench, run } from "./runner.mjs"; -import { readFileSync, existsSync } from "node:fs"; +import { readFileSync } from "node:fs"; import { readFile } from "node:fs/promises"; +import { bench, run } from "./runner.mjs"; bench(`readFileSync(/tmp/404-not-found)`, () => { try { diff --git a/bench/snippets/realpath.mjs b/bench/snippets/realpath.mjs index 4793ee3d67ae1..d7fd2ec7a7a55 100644 --- a/bench/snippets/realpath.mjs +++ b/bench/snippets/realpath.mjs @@ -1,7 +1,7 @@ import { realpathSync } from "node:fs"; +import { bench, run } from "./runner.mjs"; const count = parseInt(process.env.ITERATIONS || "1", 10) || 1; const arg = process.argv[process.argv.length - 1]; -import { bench, run } from "./runner.mjs"; bench("realpathSync x " + count, () => { for (let i = 0; i < count; i++) realpathSync(arg, "utf-8"); diff --git a/bench/snippets/render.js b/bench/snippets/render.js index 9ef70bc273ff6..58aaefb1c4021 100644 --- a/bench/snippets/render.js +++ b/bench/snippets/render.js @@ -1,4 +1,4 @@ -import decoding from "./jsx-entity-decoding"; import ReactDOMServer from "react-dom/server.browser"; +import decoding from "./jsx-entity-decoding"; console.log(ReactDOMServer.renderToString(decoding)); diff --git a/bench/snippets/require-builtins.mjs b/bench/snippets/require-builtins.mjs index c458f3a356653..34c008a892161 100644 --- a/bench/snippets/require-builtins.mjs +++ b/bench/snippets/require-builtins.mjs @@ -1,7 +1,6 @@ -import { bench, run } from "./runner.mjs"; -import { builtinModules } from "node:module"; -import { writeFile } from "node:fs/promises"; import { spawnSync } from "child_process"; +import { writeFile } from "node:fs/promises"; +import { builtinModules } from "node:module"; for (let builtin of builtinModules) { const path = `/tmp/require.${builtin.replaceAll("/", "_")}.cjs`; diff --git a/bench/snippets/rmdir.mjs b/bench/snippets/rmdir.mjs index 258d69097d483..8cc7bb08fbb5a 100644 --- a/bench/snippets/rmdir.mjs +++ b/bench/snippets/rmdir.mjs @@ -1,5 +1,5 @@ +import { existsSync, mkdirSync, promises } from "node:fs"; import { tmpdir } from "node:os"; -import { promises, existsSync, mkdirSync } from "node:fs"; const count = 1024 * 12; var queue = new Array(count); diff --git a/bench/snippets/runner-entrypoint.js b/bench/snippets/runner-entrypoint.js index 77011c13177ed..cbcf0f672694d 100644 --- a/bench/snippets/runner-entrypoint.js +++ b/bench/snippets/runner-entrypoint.js @@ -1,9 +1,9 @@ // note: this isn't done yet // we look for `// @runtime` in the file to determine which runtimes to run the benchmark in import { spawnSync } from "bun"; -import { readdirSync, readFileSync } from "node:fs"; import { Database } from "bun:sqlite"; -import { extname, basename } from "path"; +import { readdirSync, readFileSync } from "node:fs"; +import { basename, extname } from "path"; const exts = [".js", ".ts", ".mjs", ".tsx"]; diff --git a/bench/snippets/runner.mjs b/bench/snippets/runner.mjs index 4f6e29fba5e9b..1b985c716b14d 100644 --- a/bench/snippets/runner.mjs +++ b/bench/snippets/runner.mjs @@ -1,5 +1,5 @@ -import * as Mitata from "../node_modules/mitata/src/cli.mjs"; import process from "node:process"; +import * as Mitata from "../node_modules/mitata/src/cli.mjs"; const asJSON = !!process?.env?.BENCHMARK_RUNNER; diff --git a/bench/snippets/serialize.mjs b/bench/snippets/serialize.mjs index 1a3646f792bcc..acd21c5c6c4e5 100644 --- a/bench/snippets/serialize.mjs +++ b/bench/snippets/serialize.mjs @@ -1,4 +1,4 @@ -import { serialize, deserialize } from "node:v8"; +import { deserialize, serialize } from "node:v8"; import { bench, run } from "./runner.mjs"; const obj = { "id": 1296269, diff --git a/bench/snippets/set-timeout.mjs b/bench/snippets/set-timeout.mjs index 47228f77ce112..a9f495a319dd7 100644 --- a/bench/snippets/set-timeout.mjs +++ b/bench/snippets/set-timeout.mjs @@ -1,5 +1,3 @@ -import { bench, run } from "../node_modules/mitata/src/cli.mjs"; - let count = 20_000_000; const batchSize = 1_000_000; console.time("Run"); diff --git a/bench/snippets/sha512.js b/bench/snippets/sha512.js index ac162dc248a77..9b3dcdd7a5494 100644 --- a/bench/snippets/sha512.js +++ b/bench/snippets/sha512.js @@ -1,5 +1,5 @@ -import { bench, run } from "./runner.mjs"; import { SHA512 } from "bun"; +import { bench, run } from "./runner.mjs"; bench('SHA512.hash("hello world")', () => { SHA512.hash("hello world"); diff --git a/bench/snippets/sha512.node.mjs b/bench/snippets/sha512.node.mjs index 3c3ac169761f1..e373c4cb3697a 100644 --- a/bench/snippets/sha512.node.mjs +++ b/bench/snippets/sha512.node.mjs @@ -1,5 +1,5 @@ -import { bench, run } from "./runner.mjs"; import { createHash } from "crypto"; +import { bench, run } from "./runner.mjs"; bench('createHash("sha256").update("hello world").digest()', () => { createHash("sha256").update("hello world").digest(); diff --git a/bench/snippets/shell-spawn.mjs b/bench/snippets/shell-spawn.mjs index aa4da66eeb0fd..eab129eae9723 100644 --- a/bench/snippets/shell-spawn.mjs +++ b/bench/snippets/shell-spawn.mjs @@ -1,6 +1,6 @@ -import { $ as zx } from "zx"; import { $ as execa$ } from "execa"; -import { bench, run, group } from "./runner.mjs"; +import { $ as zx } from "zx"; +import { bench, group, run } from "./runner.mjs"; const execa = execa$({ stdio: "ignore", cwd: import.meta.dirname }); diff --git a/bench/snippets/stat.mjs b/bench/snippets/stat.mjs index 17d6a68c83a75..f39840123f410 100644 --- a/bench/snippets/stat.mjs +++ b/bench/snippets/stat.mjs @@ -1,6 +1,6 @@ -import { readdirSync, statSync } from "fs"; -import { bench, run } from "./runner.mjs"; +import { statSync } from "fs"; import { argv } from "process"; +import { bench, run } from "./runner.mjs"; const dir = argv.length > 2 ? argv[2] : "/tmp"; diff --git a/bench/snippets/stderr.mjs b/bench/snippets/stderr.mjs index f4669905b0864..1c348a3f46375 100644 --- a/bench/snippets/stderr.mjs +++ b/bench/snippets/stderr.mjs @@ -1,4 +1,4 @@ -import { run, bench } from "./runner.mjs"; +import { bench, run } from "./runner.mjs"; var writer = globalThis.Bun ? Bun.stderr.writer() : undefined; if (writer) diff --git a/bench/snippets/string-decoder.mjs b/bench/snippets/string-decoder.mjs index b00b7b67d4df0..950bce9c569f7 100644 --- a/bench/snippets/string-decoder.mjs +++ b/bench/snippets/string-decoder.mjs @@ -1,5 +1,5 @@ -import { bench, run } from "./runner.mjs"; import { StringDecoder } from "string_decoder"; +import { bench, run } from "./runner.mjs"; var short = Buffer.from("Hello World!"); var shortUTF16 = Buffer.from("Hello World 💕💕💕"); diff --git a/bench/snippets/string-width.mjs b/bench/snippets/string-width.mjs index 03b4833a3bb35..d37046f832c5a 100644 --- a/bench/snippets/string-width.mjs +++ b/bench/snippets/string-width.mjs @@ -1,5 +1,5 @@ -import { bench, run } from "./runner.mjs"; import npmStringWidth from "string-width"; +import { bench, run } from "./runner.mjs"; const bunStringWidth = globalThis?.Bun?.stringWidth; diff --git a/bench/snippets/tcp-echo.bun.ts b/bench/snippets/tcp-echo.bun.ts index c0f227e75474e..193ce0bd2a075 100644 --- a/bench/snippets/tcp-echo.bun.ts +++ b/bench/snippets/tcp-echo.bun.ts @@ -1,4 +1,4 @@ -import { listen, connect } from "bun"; +import { connect, listen } from "bun"; var counter = 0; const msg = "Hello World!"; diff --git a/bench/snippets/text-decoder-stream.mjs b/bench/snippets/text-decoder-stream.mjs new file mode 100644 index 0000000000000..5495fdc09b3bf --- /dev/null +++ b/bench/snippets/text-decoder-stream.mjs @@ -0,0 +1,55 @@ +import { bench, run } from "./runner.mjs"; + +const latin1 = `hello hello hello!!!! `.repeat(10240); + +function create(src) { + function split(str, chunkSize) { + let chunkedHTML = []; + let html = str; + const encoder = new TextEncoder(); + while (html.length > 0) { + chunkedHTML.push(encoder.encode(html.slice(0, chunkSize))); + html = html.slice(chunkSize); + } + return chunkedHTML; + } + + async function runBench(chunks) { + const decoder = new TextDecoderStream(); + const stream = new ReadableStream({ + pull(controller) { + for (let chunk of chunks) { + controller.enqueue(chunk); + } + controller.close(); + }, + }).pipeThrough(decoder); + for (let reader = stream.getReader(); ; ) { + const { done, value } = await reader.read(); + if (done) { + break; + } + } + } + + // if (new TextDecoder().decode(await runBench(oneKB)) !== src) { + // throw new Error("Benchmark failed"); + // } + const sizes = [16 * 1024, 64 * 1024, 256 * 1024]; + for (const chunkSize of sizes) { + const text = split(src, chunkSize); + bench( + `${Math.round(src.length / 1024)} KB of text in ${Math.round(chunkSize / 1024) > 0 ? Math.round(chunkSize / 1024) : (chunkSize / 1024).toFixed(2)} KB chunks`, + async () => { + await runBench(text); + }, + ); + } +} +create(latin1); +create( + // bun's old readme was extremely long + await fetch("https://web.archive.org/web/20230119110956/https://github.com/oven-sh/bun").then(res => res.text()), +); + +await run(); diff --git a/bench/snippets/text-encoder-stream.mjs b/bench/snippets/text-encoder-stream.mjs new file mode 100644 index 0000000000000..788e3fb50b8bd --- /dev/null +++ b/bench/snippets/text-encoder-stream.mjs @@ -0,0 +1,49 @@ +import { bench, run } from "./runner.mjs"; + +const latin1 = `hello hello hello!!!! `.repeat(10240); + +function create(src) { + function split(str, chunkSize) { + let chunkedHTML = []; + let html = str; + while (html.length > 0) { + chunkedHTML.push(html.slice(0, chunkSize)); + html = html.slice(chunkSize); + } + return chunkedHTML; + } + + async function runBench(chunks) { + const encoderStream = new TextEncoderStream(); + const stream = new ReadableStream({ + pull(controller) { + for (let chunk of chunks) { + controller.enqueue(chunk); + } + controller.close(); + }, + }).pipeThrough(encoderStream); + return await new Response(stream).bytes(); + } + + // if (new TextDecoder().decode(await runBench(oneKB)) !== src) { + // throw new Error("Benchmark failed"); + // } + const sizes = [1024, 16 * 1024, 64 * 1024, 256 * 1024]; + for (const chunkSize of sizes) { + const text = split(src, chunkSize); + bench( + `${Math.round(src.length / 1024)} KB of text in ${Math.round(chunkSize / 1024) > 0 ? Math.round(chunkSize / 1024) : (chunkSize / 1024).toFixed(2)} KB chunks`, + async () => { + await runBench(text); + }, + ); + } +} +create(latin1); +create( + // bun's old readme was extremely long + await fetch("https://web.archive.org/web/20230119110956/https://github.com/oven-sh/bun").then(res => res.text()), +); + +await run(); diff --git a/bench/snippets/transpiler-2.mjs b/bench/snippets/transpiler-2.mjs new file mode 100644 index 0000000000000..702fda9d188e5 --- /dev/null +++ b/bench/snippets/transpiler-2.mjs @@ -0,0 +1,14 @@ +import { bench, run } from "mitata"; +import { join } from "path"; + +const code = require("fs").readFileSync( + process.argv[2] || join(import.meta.dir, "../node_modules/@babel/standalone/babel.min.js"), +); + +const transpiler = new Bun.Transpiler({ minify: true }); + +bench("transformSync", () => { + transpiler.transformSync(code); +}); + +await run(); diff --git a/bench/snippets/transpiler.mjs b/bench/snippets/transpiler.mjs index 3a5c57d0afa37..5423416067720 100644 --- a/bench/snippets/transpiler.mjs +++ b/bench/snippets/transpiler.mjs @@ -1,8 +1,8 @@ import { readFileSync } from "fs"; +import { createRequire } from "module"; import { dirname } from "path"; import { fileURLToPath } from "url"; -import { bench, run, group } from "./runner.mjs"; -import { createRequire } from "module"; +import { bench, group, run } from "./runner.mjs"; const require = createRequire(import.meta.url); const esbuild_ = require("esbuild/lib/main"); const swc_ = require("@swc/core"); diff --git a/bench/snippets/write-file.mjs b/bench/snippets/write-file.mjs index 4417c817cd5ca..1b054c4daa33e 100644 --- a/bench/snippets/write-file.mjs +++ b/bench/snippets/write-file.mjs @@ -1,4 +1,4 @@ -import { readFileSync, writeFileSync } from "node:fs"; +import { writeFileSync } from "node:fs"; import { bench, run } from "./runner.mjs"; var short = "Hello World!"; diff --git a/bench/snippets/write.bun.js b/bench/snippets/write.bun.js index 67fbbe3b250bd..0a747bf958726 100644 --- a/bench/snippets/write.bun.js +++ b/bench/snippets/write.bun.js @@ -1,6 +1,6 @@ -import { bench, run } from "./runner.mjs"; import { write } from "bun"; import { openSync } from "fs"; +import { bench, run } from "./runner.mjs"; bench('write(/tmp/foo.txt, "short string")', async () => { await write("/tmp/foo.txt", "short string"); diff --git a/bench/snippets/write.node.mjs b/bench/snippets/write.node.mjs index f59c98aefa96f..823648a50bebf 100644 --- a/bench/snippets/write.node.mjs +++ b/bench/snippets/write.node.mjs @@ -1,9 +1,8 @@ // @runtime node, bun, deno -import { bench, run } from "./runner.mjs"; import { Buffer } from "node:buffer"; -import { openSync } from "node:fs"; +import { openSync, writeSync as write } from "node:fs"; import { writeFile } from "node:fs/promises"; -import { writeSync as write } from "node:fs"; +import { bench, run } from "./runner.mjs"; bench("writeFile(/tmp/foo.txt, short string)", async () => { await writeFile("/tmp/foo.txt", "short string", "utf8"); diff --git a/bench/sqlite/better-sqlite3.mjs b/bench/sqlite/better-sqlite3.mjs new file mode 100644 index 0000000000000..2412d141bd249 --- /dev/null +++ b/bench/sqlite/better-sqlite3.mjs @@ -0,0 +1,31 @@ +import { bench, run } from "mitata"; +import { createRequire } from "module"; + +const require = createRequire(import.meta.url); +const db = require("better-sqlite3")("./src/northwind.sqlite"); + +{ + const sql = db.prepare(`SELECT * FROM "Order"`); + + bench('SELECT * FROM "Order"', () => { + sql.all(); + }); +} + +{ + const sql = db.prepare(`SELECT * FROM "Product"`); + + bench('SELECT * FROM "Product"', () => { + sql.all(); + }); +} + +{ + const sql = db.prepare(`SELECT * FROM "OrderDetail"`); + + bench('SELECT * FROM "OrderDetail"', () => { + sql.all(); + }); +} + +await run(); diff --git a/bench/sqlite/bun.js b/bench/sqlite/bun.js index c178981f17232..3e0bc417aeef2 100644 --- a/bench/sqlite/bun.js +++ b/bench/sqlite/bun.js @@ -1,5 +1,5 @@ -import { run, bench } from "mitata"; import { Database } from "bun:sqlite"; +import { bench, run } from "mitata"; import { join } from "path"; const db = Database.open(join(import.meta.dir, "src", "northwind.sqlite")); diff --git a/bench/sqlite/deno.js b/bench/sqlite/deno.js index 8b4b215ee8a05..f4e4cc1b3b93a 100644 --- a/bench/sqlite/deno.js +++ b/bench/sqlite/deno.js @@ -1,5 +1,5 @@ import { Database } from "https://deno.land/x/sqlite3@0.11.1/mod.ts"; -import { run, bench } from "../node_modules/mitata/src/cli.mjs"; +import { bench, run } from "../node_modules/mitata/src/cli.mjs"; const db = new Database("./src/northwind.sqlite"); diff --git a/bench/sqlite/node.mjs b/bench/sqlite/node.mjs index 9bf25105b908e..6e2fb2dc9f067 100644 --- a/bench/sqlite/node.mjs +++ b/bench/sqlite/node.mjs @@ -1,8 +1,9 @@ -import { run, bench } from "mitata"; -import { createRequire } from "module"; +// Run `node --experimental-sqlite bench/sqlite/node.mjs` to run the script. +// You will need `--experimental-sqlite` flag to run this script and node v22.5.0 or higher. +import { bench, run } from "mitata"; +import { DatabaseSync as Database } from "node:sqlite"; -const require = createRequire(import.meta.url); -const db = require("better-sqlite3")("./src/northwind.sqlite"); +const db = new Database("./src/northwind.sqlite"); { const sql = db.prepare(`SELECT * FROM "Order"`); diff --git a/bench/tsconfig.json b/bench/tsconfig.json new file mode 100644 index 0000000000000..2432a3c9d6aaa --- /dev/null +++ b/bench/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + // For the organize imports plugin + "jsx": "react" + } +} diff --git a/build.zig b/build.zig index a87737be26ebc..064f47852e660 100644 --- a/build.zig +++ b/build.zig @@ -44,10 +44,23 @@ const BunBuildOptions = struct { version: Version, canary_revision: ?u32, sha: []const u8, + /// enable debug logs in release builds enable_logs: bool = false, tracy_callstack_depth: u16, - + reported_nodejs_version: Version, + /// To make iterating on some '@embedFile's faster, we load them at runtime + /// instead of at compile time. This is disabled in release or if this flag + /// is set (to allow CI to build a portable executable). Affected files: + /// + /// - src/kit/runtime.ts (bundled) + /// - src/bun.js/api/FFI.h + /// + /// A similar technique is used in C++ code for JavaScript builtins + force_embed_code: bool = false, + + /// `./build/codegen` or equivalent generated_code_dir: []const u8, + no_llvm: bool, cached_options_module: ?*Module = null, windows_shim: ?WindowsShim = null, @@ -57,6 +70,10 @@ const BunBuildOptions = struct { !Target.x86.featureSetHas(this.target.result.cpu.features, .avx2); } + pub fn shouldEmbedCode(opts: *const BunBuildOptions) bool { + return opts.optimize != .Debug or opts.force_embed_code; + } + pub fn buildOptionsModule(this: *BunBuildOptions, b: *Build) *Module { if (this.cached_options_module) |mod| { return mod; @@ -64,12 +81,19 @@ const BunBuildOptions = struct { var opts = b.addOptions(); opts.addOption([]const u8, "base_path", b.pathFromRoot(".")); + opts.addOption([]const u8, "codegen_path", std.fs.path.resolve(b.graph.arena, &.{ + b.build_root.path.?, + this.generated_code_dir, + }) catch @panic("OOM")); + + opts.addOption(bool, "embed_code", this.shouldEmbedCode()); opts.addOption(u32, "canary_revision", this.canary_revision orelse 0); opts.addOption(bool, "is_canary", this.canary_revision != null); opts.addOption(Version, "version", this.version); opts.addOption([:0]const u8, "sha", b.allocator.dupeZ(u8, this.sha) catch @panic("OOM")); opts.addOption(bool, "baseline", this.isBaseline()); opts.addOption(bool, "enable_logs", this.enable_logs); + opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version})); const mod = opts.createModule(); this.cached_options_module = mod; @@ -86,10 +110,8 @@ const BunBuildOptions = struct { pub fn getOSVersionMin(os: OperatingSystem) ?Target.Query.OsVersion { return switch (os) { - // bun needs macOS 12 to work properly due to icucore, but we have been - // compiling everything with 11 as the minimum. .mac => .{ - .semver = .{ .major = 11, .minor = 0, .patch = 0 }, + .semver = .{ .major = 13, .minor = 0, .patch = 0 }, }, // Windows 10 1809 is the minimum supported version @@ -111,10 +133,34 @@ pub fn getOSGlibCVersion(os: OperatingSystem) ?Version { }; } +pub fn getCpuModel(os: OperatingSystem, arch: Arch) ?Target.Query.CpuModel { + // https://github.com/oven-sh/bun/issues/12076 + if (os == .linux and arch == .aarch64) { + return .{ .explicit = &Target.aarch64.cpu.cortex_a35 }; + } + + // Be explicit and ensure we do not accidentally target a newer M-series chip + if (os == .mac and arch == .aarch64) { + return .{ .explicit = &Target.aarch64.cpu.apple_m1 }; + } + + // note: x86_64 is dealt with in the CMake config and passed in. + // the reason for the explicit handling on aarch64 is due to troubles + // passing the exact target in via flags. + return null; +} + pub fn build(b: *Build) !void { - std.debug.print("zig build v{s}\n", .{builtin.zig_version_string}); + std.log.info("zig compiler v{s}", .{builtin.zig_version_string}); - b.zig_lib_dir = b.zig_lib_dir orelse b.path("src/deps/zig/lib"); + b.zig_lib_dir = b.zig_lib_dir orelse b.path("vendor/zig/lib"); + + // TODO: Upgrade path for 0.14.0 + // b.graph.zig_lib_directory = brk: { + // const sub_path = "src/deps/zig/lib"; + // const dir = try b.build_root.handle.openDir(sub_path, .{}); + // break :brk .{ .handle = dir, .path = try b.build_root.join(b.graph.arena, &.{sub_path}) }; + // }; var target_query = b.standardTargetOptionsQueryOnly(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -136,6 +182,14 @@ pub fn build(b: *Build) !void { break :brk .{ os, arch }; }; + // target must be refined to support older but very popular devices on + // aarch64, this means moving the minimum supported CPU to support certain + // raspberry PIs. there are also a number of cloud hosts that use virtual + // machines with surprisingly out of date versions of glibc. + if (getCpuModel(os, arch)) |cpu_model| { + target_query.cpu_model = cpu_model; + } + target_query.os_version_min = getOSVersionMin(os); target_query.glibc_version = getOSGlibCVersion(os); @@ -143,15 +197,20 @@ pub fn build(b: *Build) !void { const generated_code_dir = b.pathFromRoot( b.option([]const u8, "generated-code", "Set the generated code directory") orelse - "build/codegen", + "build/debug/codegen", ); const bun_version = b.option([]const u8, "version", "Value of `Bun.version`") orelse "0.0.0"; + const force_embed_js_code = b.option(bool, "force_embed_js_code", "Always embed JavaScript builtins") orelse false; b.reference_trace = ref_trace: { const trace = b.option(u32, "reference-trace", "Set the reference trace") orelse 16; break :ref_trace if (trace == 0) null else trace; }; + const obj_format = b.option(ObjectFormat, "obj_format", "Output file for object files") orelse .obj; + + const no_llvm = b.option(bool, "no_llvm", "Experiment with Zig self hosted backends. No stability guaranteed") orelse false; + var build_options = BunBuildOptions{ .target = target, .optimize = optimize, @@ -160,6 +219,8 @@ pub fn build(b: *Build) !void { .arch = arch, .generated_code_dir = generated_code_dir, + .force_embed_code = force_embed_js_code, + .no_llvm = no_llvm, .version = try Version.parse(bun_version), .canary_revision = canary: { @@ -167,6 +228,11 @@ pub fn build(b: *Build) !void { break :canary if (rev == 0) null else rev; }, + .reported_nodejs_version = try Version.parse( + b.option([]const u8, "reported_nodejs_version", "Reported Node.js version") orelse + "0.0.0-unset", + ), + .sha = sha: { const sha = b.option([]const u8, "sha", "Force the git sha") orelse b.graph.env_map.get("GITHUB_SHA") orelse @@ -211,7 +277,7 @@ pub fn build(b: *Build) !void { var step = b.step("obj", "Build Bun's Zig code as a .o file"); var bun_obj = addBunObject(b, &build_options); step.dependOn(&bun_obj.step); - step.dependOn(&b.addInstallFile(bun_obj.getEmittedBin(), "bun-zig.o").step); + step.dependOn(addInstallObjectFile(b, bun_obj, "bun-zig", obj_format)); } // zig build windows-shim @@ -239,60 +305,60 @@ pub fn build(b: *Build) !void { // zig build check-all { - var step = b.step("check-all", "Check for semantic analysis errors on all supported platforms"); - inline for (.{ + const step = b.step("check-all", "Check for semantic analysis errors on all supported platforms"); + addMultiCheck(b, step, build_options, &.{ .{ .os = .windows, .arch = .x86_64 }, .{ .os = .mac, .arch = .x86_64 }, .{ .os = .mac, .arch = .aarch64 }, .{ .os = .linux, .arch = .x86_64 }, .{ .os = .linux, .arch = .aarch64 }, - }) |check| { - inline for (.{ .Debug, .ReleaseFast }) |mode| { - const check_target = b.resolveTargetQuery(.{ - .os_tag = OperatingSystem.stdOSTag(check.os), - .cpu_arch = check.arch, - .os_version_min = getOSVersionMin(check.os), - .glibc_version = getOSGlibCVersion(check.os), - }); - - var options = BunBuildOptions{ - .target = check_target, - .os = check.os, - .arch = check_target.result.cpu.arch, - .optimize = mode, - - .canary_revision = build_options.canary_revision, - .sha = build_options.sha, - .tracy_callstack_depth = build_options.tracy_callstack_depth, - .version = build_options.version, - .generated_code_dir = build_options.generated_code_dir, - }; - var obj = addBunObject(b, &options); - obj.generated_bin = null; - step.dependOn(&obj.step); - } - } + }); } - // Running `zig build` with no arguments is almost always a mistake. - // TODO: revive this error. cannot right now since ZLS runs zig build without arguments + // zig build check-windows { - // const mistake_message = b.addSystemCommand(&.{ - // "echo", - // \\ - // \\To build Bun from source, please use `bun run setup` instead of `zig build`" - // \\For more info, see https://bun.sh/docs/project/contributing - // \\ - // \\If you want to build the zig code in isolation, run: - // \\ 'zig build obj -Dgenerated-code=./build/codegen [...opts]' - // \\ - // \\If you want to test a compile without emitting an object: - // \\ 'zig build check' - // \\ 'zig build check-all' (run linux+mac+windows) - // \\ - // }); - - // b.default_step.dependOn(&mistake_message.step); + const step = b.step("check-windows", "Check for semantic analysis errors on Windows"); + addMultiCheck(b, step, build_options, &.{ + .{ .os = .windows, .arch = .x86_64 }, + }); + } +} + +pub inline fn addMultiCheck( + b: *Build, + parent_step: *Step, + root_build_options: BunBuildOptions, + to_check: []const struct { os: OperatingSystem, arch: Arch }, +) void { + inline for (to_check) |check| { + inline for (.{ .Debug, .ReleaseFast }) |mode| { + const check_target = b.resolveTargetQuery(.{ + .os_tag = OperatingSystem.stdOSTag(check.os), + .cpu_arch = check.arch, + .cpu_model = getCpuModel(check.os, check.arch) orelse .determined_by_cpu_arch, + .os_version_min = getOSVersionMin(check.os), + .glibc_version = getOSGlibCVersion(check.os), + }); + + var options: BunBuildOptions = .{ + .target = check_target, + .os = check.os, + .arch = check_target.result.cpu.arch, + .optimize = mode, + + .canary_revision = root_build_options.canary_revision, + .sha = root_build_options.sha, + .tracy_callstack_depth = root_build_options.tracy_callstack_depth, + .version = root_build_options.version, + .reported_nodejs_version = root_build_options.reported_nodejs_version, + .generated_code_dir = root_build_options.generated_code_dir, + .no_llvm = root_build_options.no_llvm, + }; + + var obj = addBunObject(b, &options); + obj.generated_bin = null; + parent_step.dependOn(&obj.step); + } } } @@ -305,10 +371,15 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { }, .target = opts.target, .optimize = opts.optimize, + .use_llvm = !opts.no_llvm, + .use_lld = if (opts.os == .mac) false else !opts.no_llvm, + + // https://github.com/ziglang/zig/issues/17430 .pic = true, + + .omit_frame_pointer = false, .strip = false, // stripped at the end }); - obj.bundle_compiler_rt = false; obj.formatted_panics = true; obj.root_module.omit_frame_pointer = false; @@ -326,9 +397,10 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { } if (opts.os == .linux) { - obj.link_emit_relocs = true; - obj.link_eh_frame_hdr = true; + obj.link_emit_relocs = false; + obj.link_eh_frame_hdr = false; obj.link_function_sections = true; + obj.link_data_sections = true; if (opts.optimize == .Debug) { obj.root_module.valgrind = true; @@ -339,6 +411,25 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { return obj; } +const ObjectFormat = enum { + bc, + obj, +}; + +pub fn addInstallObjectFile( + b: *Build, + compile: *Compile, + name: []const u8, + out_mode: ObjectFormat, +) *Step { + // bin always needed to be computed or else the compilation will do nothing. zig build system bug? + const bin = compile.getEmittedBin(); + return &b.addInstallFile(switch (out_mode) { + .obj => bin, + .bc => compile.getEmittedLlvmBc(), + }, b.fmt("{s}.o", .{name})).step; +} + fn exists(path: []const u8) bool { const file = std.fs.openFileAbsolute(path, .{ .mode = .read_only }) catch return false; file.close(); @@ -378,17 +469,22 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void { .root_source_file = b.path(async_path), }); - const zig_generated_classes_path = b.pathJoin(&.{ opts.generated_code_dir, "ZigGeneratedClasses.zig" }); - validateGeneratedPath(zig_generated_classes_path); - obj.root_module.addAnonymousImport("ZigGeneratedClasses", .{ - .root_source_file = .{ .cwd_relative = zig_generated_classes_path }, - }); - - const resolved_source_tag_path = b.pathJoin(&.{ opts.generated_code_dir, "ResolvedSourceTag.zig" }); - validateGeneratedPath(resolved_source_tag_path); - obj.root_module.addAnonymousImport("ResolvedSourceTag", .{ - .root_source_file = .{ .cwd_relative = resolved_source_tag_path }, - }); + // Generated code exposed as individual modules. + inline for (.{ + .{ .file = "ZigGeneratedClasses.zig", .import = "ZigGeneratedClasses" }, + .{ .file = "ResolvedSourceTag.zig", .import = "ResolvedSourceTag" }, + .{ .file = "ErrorCode.zig", .import = "ErrorCode" }, + .{ .file = "kit.client.js", .import = "kit-codegen/kit.client.js", .enable = opts.shouldEmbedCode() }, + .{ .file = "kit.server.js", .import = "kit-codegen/kit.server.js", .enable = opts.shouldEmbedCode() }, + }) |entry| { + if (!@hasField(@TypeOf(entry), "enable") or entry.enable) { + const path = b.pathJoin(&.{ opts.generated_code_dir, entry.file }); + validateGeneratedPath(path); + obj.root_module.addAnonymousImport(entry.import, .{ + .root_source_file = .{ .cwd_relative = path }, + }); + } + } if (os == .windows) { obj.root_module.addAnonymousImport("bun_shim_impl.exe", .{ @@ -399,7 +495,11 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void { fn validateGeneratedPath(path: []const u8) void { if (!exists(path)) { - std.debug.panic("{s} does not exist in generated code directory!", .{std.fs.path.basename(path)}); + std.debug.panic( + \\Generated file '{s}' is missing! + \\ + \\Make sure to use CMake and Ninja, or pass a manual codegen folder with '-Dgenerated-code=...' + , .{path}); } } diff --git a/bun.lockb b/bun.lockb index d8238c763b338..9e6b62e3f28c6 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake new file mode 100644 index 0000000000000..bf8cf576abf7e --- /dev/null +++ b/cmake/CompilerFlags.cmake @@ -0,0 +1,298 @@ +# clang: https://clang.llvm.org/docs/CommandGuide/clang.html +# clang-cl: https://clang.llvm.org/docs/UsersManual.html#id11 + +# --- Macros --- + +macro(setb variable) + if(${variable}) + set(${variable} ON) + else() + set(${variable} OFF) + endif() +endmacro() + +set(targets WIN32 APPLE UNIX LINUX) + +foreach(target ${targets}) + setb(${target}) +endforeach() + +# --- CPU target --- +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64") + if(APPLE) + register_compiler_flags(-mcpu=apple-m1) + else() + register_compiler_flags(-march=armv8-a+crc -mtune=ampere1) + endif() +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64") + if(ENABLE_BASELINE) + register_compiler_flags(-march=nehalem) + else() + register_compiler_flags(-march=haswell) + endif() +else() + unsupported(CMAKE_SYSTEM_PROCESSOR) +endif() + +# --- MSVC runtime --- +if(WIN32) + register_compiler_flags( + DESCRIPTION "Use static MSVC runtime" + /MTd ${DEBUG} + /MT ${RELEASE} + /U_DLL + ) +endif() + +# --- Optimization level --- +if(DEBUG) + register_compiler_flags( + DESCRIPTION "Disable optimization" + /Od ${WIN32} + -O0 ${UNIX} + ) +elseif(ENABLE_SMOL) + register_compiler_flags( + DESCRIPTION "Optimize for size" + /Os ${WIN32} + -Os ${UNIX} + ) +else() + register_compiler_flags( + DESCRIPTION "Optimize for speed" + /O2 ${WIN32} # TODO: change to /0t (same as -O3) to match macOS and Linux? + -O3 ${UNIX} + ) +endif() + +# --- Debug level --- +if(WIN32) + register_compiler_flags( + DESCRIPTION "Enable debug symbols (.pdb)" + /Z7 + ) +elseif(APPLE) + register_compiler_flags( + DESCRIPTION "Enable debug symbols (.dSYM)" + -gdwarf-4 + ) +endif() + +if(UNIX) + register_compiler_flags( + DESCRIPTION "Enable debug symbols" + -g3 ${DEBUG} + -g1 ${RELEASE} + ) + + register_compiler_flags( + DESCRIPTION "Optimize debug symbols for LLDB" + -glldb + ) +endif() + +# TODO: consider other debug options +# -fdebug-macro # Emit debug info for macros +# -fstandalone-debug # Emit debug info for non-system libraries +# -fno-eliminate-unused-debug-types # Don't eliminate unused debug symbols + +# --- C/C++ flags --- +register_compiler_flags( + DESCRIPTION "Disable C/C++ exceptions" + -fno-exceptions ${UNIX} + /EHsc ${WIN32} # (s- disables C++, c- disables C) +) + +register_compiler_flags( + DESCRIPTION "Disable C++ static destructors" + LANGUAGES CXX + -Xclang ${WIN32} + -fno-c++-static-destructors +) + +register_compiler_flags( + DESCRIPTION "Disable runtime type information (RTTI)" + /GR- ${WIN32} + -fno-rtti ${UNIX} +) + +register_compiler_flags( + DESCRIPTION "Keep frame pointers" + /Oy- ${WIN32} + -fno-omit-frame-pointer ${UNIX} + -mno-omit-leaf-frame-pointer ${UNIX} +) + +if(UNIX) + register_compiler_flags( + DESCRIPTION "Set C/C++ visibility to hidden" + -fvisibility=hidden + -fvisibility-inlines-hidden + ) + + register_compiler_flags( + DESCRIPTION "Disable unwind tables" + -fno-unwind-tables + -fno-asynchronous-unwind-tables + ) +endif() + +register_compiler_flags( + DESCRIPTION "Place each function in its own section" + -ffunction-sections ${UNIX} + /Gy ${WIN32} +) + +register_compiler_flags( + DESCRIPTION "Place each data item in its own section" + -fdata-sections ${UNIX} + /Gw ${WIN32} +) + +# having this enabled in debug mode on macOS >=14 causes libarchive to fail to configure with the error: +# > pid_t doesn't exist on this platform? +if((DEBUG AND LINUX) OR((NOT DEBUG) AND UNIX)) + register_compiler_flags( + DESCRIPTION "Emit an address-significance table" + -faddrsig + ) +endif() + +if(WIN32) + register_compiler_flags( + DESCRIPTION "Enable string pooling" + /GF + ) + + register_compiler_flags( + DESCRIPTION "Assume thread-local variables are defined in the executable" + /GA + ) +endif() + +# --- Linker flags --- +if(LINUX) + register_linker_flags( + DESCRIPTION "Disable relocation read-only (RELRO)" + -Wl,-z,norelro + ) +endif() + +# --- Assertions --- + +# Note: This is a helpful guide about assertions: +# https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++ +if(ENABLE_ASSERTIONS) + register_compiler_flags( + DESCRIPTION "Do not eliminate null-pointer checks" + -fno-delete-null-pointer-checks + ) + + register_compiler_definitions( + DESCRIPTION "Enable libc++ assertions" + _LIBCPP_ENABLE_ASSERTIONS=1 + _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE ${RELEASE} + _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG ${DEBUG} + ) + + register_compiler_definitions( + DESCRIPTION "Enable fortified sources" + _FORTIFY_SOURCE=3 + ) + + if(LINUX) + register_compiler_definitions( + DESCRIPTION "Enable glibc++ assertions" + _GLIBCXX_ASSERTIONS=1 + ) + endif() +else() + register_compiler_definitions( + DESCRIPTION "Disable debug assertions" + NDEBUG=1 + ) + + register_compiler_definitions( + DESCRIPTION "Disable libc++ assertions" + _LIBCPP_ENABLE_ASSERTIONS=0 + _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE + ) + + if(LINUX) + register_compiler_definitions( + DESCRIPTION "Disable glibc++ assertions" + _GLIBCXX_ASSERTIONS=0 + ) + endif() +endif() + +# --- Diagnostics --- +if(UNIX) + register_compiler_flags( + DESCRIPTION "Enable color diagnostics" + -fdiagnostics-color=always + ) +endif() + +register_compiler_flags( + DESCRIPTION "Set C/C++ error limit" + -ferror-limit=${ERROR_LIMIT} +) + +# --- LTO --- +if(ENABLE_LTO) + register_compiler_flags( + DESCRIPTION "Enable link-time optimization (LTO)" + -flto=full ${UNIX} + -flto ${WIN32} + ) + + if(UNIX) + register_compiler_flags( + DESCRIPTION "Enable virtual tables" + LANGUAGES CXX + -fforce-emit-vtables + -fwhole-program-vtables + ) + + register_linker_flags( + DESCRIPTION "Enable link-time optimization (LTO)" + -flto=full + -fwhole-program-vtables + -fforce-emit-vtables + ) + endif() +endif() + +# --- Remapping --- +if(UNIX) + register_compiler_flags( + DESCRIPTION "Remap source files" + -ffile-prefix-map=${CWD}=. + -ffile-prefix-map=${VENDOR_PATH}=vendor + -ffile-prefix-map=${CACHE_PATH}=cache + ) +endif() + +# --- Features --- + +# Valgrind cannot handle SSE4.2 instructions +# This is needed for picohttpparser +if(ENABLE_VALGRIND AND ARCH STREQUAL "x64") + register_compiler_definitions(__SSE4_2__=0) +endif() + +# --- Other --- + +# Workaround for CMake and clang-cl bug. +# https://github.com/ninja-build/ninja/issues/2280 +if(WIN32 AND NOT CMAKE_CL_SHOWINCLUDES_PREFIX) + set(CMAKE_CL_SHOWINCLUDES_PREFIX "Note: including file:") +endif() + +# WebKit uses -std=gnu++20 on non-macOS non-Windows. +# If we do not set this, it will crash at startup on the first memory allocation. +if(NOT WIN32 AND NOT APPLE) + set(CMAKE_CXX_EXTENSIONS ON) + set(CMAKE_POSITION_INDEPENDENT_CODE OFF) +endif() diff --git a/cmake/Globals.cmake b/cmake/Globals.cmake new file mode 100644 index 0000000000000..1dcb5e57fa982 --- /dev/null +++ b/cmake/Globals.cmake @@ -0,0 +1,892 @@ +include(CMakeParseArguments) + +# --- Global macros --- + +# setx() +# Description: +# Sets a variable, similar to `set()`, but also prints the value. +# Arguments: +# variable string - The variable to set +# value string - The value to set the variable to +macro(setx) + set(${ARGV}) + message(STATUS "Set ${ARGV0}: ${${ARGV0}}") +endmacro() + +# optionx() +# Description: +# Defines an option, similar to `option()`, but allows for bool, string, and regex types. +# Arguments: +# variable string - The variable to set +# type string - The type of the variable +# description string - The description of the variable +# DEFAULT string - The default value of the variable +# PREVIEW string - The preview value of the variable +# REGEX string - The regex to match the value +# REQUIRED bool - Whether the variable is required +macro(optionx variable type description) + set(options REQUIRED) + set(oneValueArgs DEFAULT PREVIEW REGEX) + set(multiValueArgs) + cmake_parse_arguments(${variable} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ${type} MATCHES "^(BOOL|STRING|FILEPATH|PATH|INTERNAL)$") + set(${variable}_REGEX ${type}) + set(${variable}_TYPE STRING) + else() + set(${variable}_TYPE ${type}) + endif() + + set(${variable} ${${variable}_DEFAULT} CACHE ${${variable}_TYPE} ${description}) + set(${variable}_SOURCE "argument") + set(${variable}_PREVIEW -D${variable}) + + if(DEFINED ENV{${variable}}) + set(${variable} $ENV{${variable}} CACHE ${${variable}_TYPE} ${description} FORCE) + set(${variable}_SOURCE "environment variable") + set(${variable}_PREVIEW ${variable}) + endif() + + if(NOT ${variable} AND ${${variable}_REQUIRED}) + message(FATAL_ERROR "Required ${${variable}_SOURCE} is missing: please set, ${${variable}_PREVIEW}=<${${variable}_REGEX}>") + endif() + + if(${type} STREQUAL "BOOL") + if("${${variable}}" MATCHES "^(TRUE|true|ON|on|YES|yes|1)$") + set(${variable} ON) + elseif("${${variable}}" MATCHES "^(FALSE|false|OFF|off|NO|no|0)$") + set(${variable} OFF) + else() + message(FATAL_ERROR "Invalid ${${variable}_SOURCE}: ${${variable}_PREVIEW}=\"${${variable}}\", please use ${${variable}_PREVIEW}=") + endif() + endif() + + if(DEFINED ${variable}_REGEX AND NOT "^(${${variable}_REGEX})$" MATCHES "${${variable}}") + message(FATAL_ERROR "Invalid ${${variable}_SOURCE}: ${${variable}_PREVIEW}=\"${${variable}}\", please use ${${variable}_PREVIEW}=<${${variable}_REGEX}>") + endif() + + message(STATUS "Set ${variable}: ${${variable}}") +endmacro() + +# unsupported() +# Description: +# Prints a message that the feature is not supported. +# Arguments: +# variable string - The variable that is not supported +macro(unsupported variable) + message(FATAL_ERROR "Unsupported ${variable}: \"${${variable}}\"") +endmacro() + +# --- CMake variables --- + +setx(CMAKE_VERSION ${CMAKE_VERSION}) +setx(CMAKE_COMMAND ${CMAKE_COMMAND}) +setx(CMAKE_HOST_SYSTEM_NAME ${CMAKE_HOST_SYSTEM_NAME}) + +# In script mode, using -P, this variable is not set +if(NOT DEFINED CMAKE_HOST_SYSTEM_PROCESSOR) + cmake_host_system_information(RESULT CMAKE_HOST_SYSTEM_PROCESSOR QUERY OS_PLATFORM) +endif() +setx(CMAKE_HOST_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) + +if(CMAKE_HOST_APPLE) + set(HOST_OS "darwin") +elseif(CMAKE_HOST_WIN32) + set(HOST_OS "windows") +elseif(CMAKE_HOST_LINUX) + set(HOST_OS "linux") +else() + unsupported(CMAKE_HOST_SYSTEM_NAME) +endif() + +if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64") + set(HOST_OS "aarch64") +elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64") + set(HOST_OS "x64") +else() + unsupported(CMAKE_HOST_SYSTEM_PROCESSOR) +endif() + +setx(CMAKE_EXPORT_COMPILE_COMMANDS ON) +setx(CMAKE_COLOR_DIAGNOSTICS ON) + +cmake_host_system_information(RESULT CORE_COUNT QUERY NUMBER_OF_LOGICAL_CORES) +optionx(CMAKE_BUILD_PARALLEL_LEVEL STRING "The number of parallel build jobs" DEFAULT ${CORE_COUNT}) + +# --- Global variables --- + +setx(CWD ${CMAKE_SOURCE_DIR}) +setx(BUILD_PATH ${CMAKE_BINARY_DIR}) + +optionx(CACHE_PATH FILEPATH "The path to the cache directory" DEFAULT ${BUILD_PATH}/cache) +optionx(CACHE_STRATEGY "read-write|read-only|write-only|none" "The strategy to use for caching" DEFAULT "read-write") + +optionx(CI BOOL "If CI is enabled" DEFAULT OFF) + +if(CI) + set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor) +else() + set(DEFAULT_VENDOR_PATH ${CWD}/vendor) +endif() + +optionx(VENDOR_PATH FILEPATH "The path to the vendor directory" DEFAULT ${DEFAULT_VENDOR_PATH}) +optionx(TMP_PATH FILEPATH "The path to the temporary directory" DEFAULT ${BUILD_PATH}/tmp) + +optionx(FRESH BOOL "Set when --fresh is used" DEFAULT OFF) +optionx(CLEAN BOOL "Set when --clean is used" DEFAULT OFF) + +# --- Helper functions --- + +function(parse_semver value variable) + string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" match "${value}") + + if(NOT match) + message(FATAL_ERROR "Invalid semver: \"${value}\"") + endif() + + set(${variable}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) + set(${variable}_VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set(${variable}_VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set(${variable}_VERSION_PATCH "${CMAKE_MATCH_3}" PARENT_SCOPE) +endfunction() + +# setenv() +# Description: +# Sets an environment variable during the build step, and writes it to a .env file. +# Arguments: +# variable string - The variable to set +# value string - The value to set the variable to +function(setenv variable value) + set(ENV_PATH ${BUILD_PATH}/.env) + if(value MATCHES "/|\\\\") + file(TO_NATIVE_PATH ${value} value) + endif() + set(ENV_LINE "${variable}=${value}") + + if(EXISTS ${ENV_PATH}) + file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8) + + foreach(line ${ENV_FILE}) + if(line MATCHES "^${variable}=") + list(REMOVE_ITEM ENV_FILE ${line}) + set(ENV_MODIFIED ON) + endif() + endforeach() + + if(ENV_MODIFIED) + list(APPEND ENV_FILE "${variable}=${value}") + list(JOIN ENV_FILE "\n" ENV_FILE) + file(WRITE ${ENV_PATH} ${ENV_FILE}) + else() + file(APPEND ${ENV_PATH} "\n${variable}=${value}") + endif() + else() + file(WRITE ${ENV_PATH} ${ENV_LINE}) + endif() + + message(STATUS "Set ENV ${variable}: ${value}") +endfunction() + +# check_command() +# Description: +# Checks if a command is available, used by `find_command()` as a validator. +# Arguments: +# FOUND bool - The variable to set to true if the version is found +# CMD string - The executable to check the version of +function(check_command FOUND CMD) + set(${FOUND} OFF PARENT_SCOPE) + + if(${CMD} MATCHES "zig") + set(CHECK_COMMAND ${CMD} version) + else() + set(CHECK_COMMAND ${CMD} --version) + endif() + + execute_process( + COMMAND ${CHECK_COMMAND} + RESULT_VARIABLE RESULT + OUTPUT_VARIABLE OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(NOT RESULT EQUAL 0 OR NOT OUTPUT) + message(DEBUG "${CHECK_COMMAND}, exited with code ${RESULT}") + return() + endif() + + parse_semver(${OUTPUT} CMD) + parse_semver(${CHECK_COMMAND_VERSION} CHECK) + + if(CHECK_COMMAND_VERSION MATCHES ">=") + if(NOT CMD_VERSION VERSION_GREATER_EQUAL ${CHECK_VERSION}) + message(DEBUG "${CHECK_COMMAND}, actual: ${CMD_VERSION}, expected: ${CHECK_COMMAND_VERSION}") + return() + endif() + elseif(CHECK_COMMAND_VERSION MATCHES ">") + if(NOT CMD_VERSION VERSION_GREATER ${CHECK_VERSION}) + message(DEBUG "${CHECK_COMMAND}, actual: ${CMD_VERSION}, expected: ${CHECK_COMMAND_VERSION}") + return() + endif() + else() + if(NOT CMD_VERSION VERSION_EQUAL ${CHECK_VERSION}) + message(DEBUG "${CHECK_COMMAND}, actual: ${CMD_VERSION}, expected: =${CHECK_COMMAND_VERSION}") + return() + endif() + endif() + + set(${FOUND} TRUE PARENT_SCOPE) +endfunction() + +# find_command() +# Description: +# Finds a command, similar to `find_program()`, but allows for version checking. +# Arguments: +# VARIABLE string - The variable to set +# COMMAND string[] - The names of the command to find +# PATHS string[] - The paths to search for the command +# REQUIRED bool - If false, the command is optional +# VERSION string - The version of the command to find (e.g. "1.2.3" or ">1.2.3") +function(find_command) + set(options) + set(args VARIABLE VERSION MIN_VERSION REQUIRED) + set(multiArgs COMMAND PATHS) + cmake_parse_arguments(CMD "${options}" "${args}" "${multiArgs}" ${ARGN}) + + if(NOT CMD_VARIABLE) + message(FATAL_ERROR "find_command: VARIABLE is required") + endif() + + if(NOT CMD_COMMAND) + message(FATAL_ERROR "find_command: COMMAND is required") + endif() + + if(CMD_VERSION) + set(CHECK_COMMAND_VERSION ${CMD_VERSION}) # special global variable + set(CMD_VALIDATOR VALIDATOR check_command) + endif() + + find_program( + ${CMD_VARIABLE} + NAMES ${CMD_COMMAND} + PATHS ${CMD_PATHS} + ${CMD_VALIDATOR} + ) + + if(NOT CMD_REQUIRED STREQUAL "OFF" AND ${CMD_VARIABLE} MATCHES "NOTFOUND") + if(CMD_VERSION) + message(FATAL_ERROR "Command not found: \"${CMD_COMMAND}\" that matches version \"${CHECK_COMMAND_VERSION}\"") + endif() + message(FATAL_ERROR "Command not found: \"${CMD_COMMAND}\"") + endif() + + if(${CMD_VARIABLE} MATCHES "NOTFOUND") + unset(${CMD_VARIABLE} PARENT_SCOPE) + else() + setx(${CMD_VARIABLE} ${${CMD_VARIABLE}} PARENT_SCOPE) + endif() +endfunction() + +# register_command() +# Description: +# Registers a command, similar to `add_custom_command()`, but has more validation and features. +# Arguments: +# COMMAND string[] - The command to run +# COMMENT string - The comment to display in the log +# CWD string - The working directory to run the command in +# ENVIRONMENT string[] - The environment variables to set (e.g. "DEBUG=1") +# TARGETS string[] - The targets that this command depends on +# SOURCES string[] - The files that this command depends on +# OUTPUTS string[] - The files that this command produces +# ARTIFACTS string[] - The files that this command produces, and uploads as an artifact in CI +# ALWAYS_RUN bool - If true, the command will always run +# TARGET string - The target to register the command with +# TARGET_PHASE string - The target phase to register the command with (e.g. PRE_BUILD, PRE_LINK, POST_BUILD) +# GROUP string - The group to register the command with (e.g. similar to JOB_POOL) +function(register_command) + set(options ALWAYS_RUN) + set(args COMMENT CWD TARGET TARGET_PHASE GROUP) + set(multiArgs COMMAND ENVIRONMENT TARGETS SOURCES OUTPUTS ARTIFACTS) + cmake_parse_arguments(CMD "${options}" "${args}" "${multiArgs}" ${ARGN}) + + if(NOT CMD_COMMAND) + message(FATAL_ERROR "register_command: COMMAND is required") + endif() + + if(NOT CMD_CWD) + set(CMD_CWD ${CWD}) + endif() + + if(CMD_ENVIRONMENT) + set(CMD_COMMAND ${CMAKE_COMMAND} -E env ${CMD_ENVIRONMENT} ${CMD_COMMAND}) + endif() + + if(NOT CMD_COMMENT) + string(JOIN " " CMD_COMMENT ${CMD_COMMAND}) + endif() + + set(CMD_COMMANDS COMMAND ${CMD_COMMAND}) + set(CMD_EFFECTIVE_DEPENDS) + + list(GET CMD_COMMAND 0 CMD_EXECUTABLE) + if(CMD_EXECUTABLE MATCHES "/|\\\\") + list(APPEND CMD_EFFECTIVE_DEPENDS ${CMD_EXECUTABLE}) + endif() + + foreach(target ${CMD_TARGETS}) + if(target MATCHES "/|\\\\") + message(FATAL_ERROR "register_command: TARGETS contains \"${target}\", if it's a path add it to SOURCES instead") + endif() + if(NOT TARGET ${target}) + message(FATAL_ERROR "register_command: TARGETS contains \"${target}\", but it's not a target") + endif() + list(APPEND CMD_EFFECTIVE_DEPENDS ${target}) + endforeach() + + foreach(source ${CMD_SOURCES}) + if(NOT source MATCHES "^(${CWD}|${BUILD_PATH}|${CACHE_PATH}|${VENDOR_PATH})") + message(FATAL_ERROR "register_command: SOURCES contains \"${source}\", if it's a path, make it absolute, otherwise add it to TARGETS instead") + endif() + list(APPEND CMD_EFFECTIVE_DEPENDS ${source}) + endforeach() + + if(NOT CMD_EFFECTIVE_DEPENDS AND NOT CMD_ALWAYS_RUN) + message(FATAL_ERROR "register_command: TARGETS or SOURCES is required") + endif() + + set(CMD_EFFECTIVE_OUTPUTS) + + foreach(output ${CMD_OUTPUTS}) + if(NOT output MATCHES "^(${CWD}|${BUILD_PATH}|${CACHE_PATH}|${VENDOR_PATH})") + message(FATAL_ERROR "register_command: OUTPUTS contains \"${output}\", if it's a path, make it absolute") + endif() + list(APPEND CMD_EFFECTIVE_OUTPUTS ${output}) + endforeach() + + foreach(artifact ${CMD_ARTIFACTS}) + if(NOT artifact MATCHES "^(${CWD}|${BUILD_PATH}|${CACHE_PATH}|${VENDOR_PATH})") + message(FATAL_ERROR "register_command: ARTIFACTS contains \"${artifact}\", if it's a path, make it absolute") + endif() + list(APPEND CMD_EFFECTIVE_OUTPUTS ${artifact}) + if(BUILDKITE) + file(RELATIVE_PATH filename ${BUILD_PATH} ${artifact}) + list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} buildkite-agent artifact upload ${filename}) + endif() + endforeach() + + foreach(output ${CMD_EFFECTIVE_OUTPUTS}) + get_source_file_property(generated ${output} GENERATED) + if(generated) + list(REMOVE_ITEM CMD_EFFECTIVE_OUTPUTS ${output}) + list(APPEND CMD_EFFECTIVE_OUTPUTS ${output}.always_run) + endif() + endforeach() + + if(CMD_ALWAYS_RUN) + list(APPEND CMD_EFFECTIVE_OUTPUTS ${CMD_CWD}/.always_run) + endif() + + if(CMD_TARGET_PHASE) + if(NOT CMD_TARGET) + message(FATAL_ERROR "register_command: TARGET is required when TARGET_PHASE is set") + endif() + if(NOT TARGET ${CMD_TARGET}) + message(FATAL_ERROR "register_command: TARGET is not a valid target: ${CMD_TARGET}") + endif() + add_custom_command( + TARGET ${CMD_TARGET} ${CMD_TARGET_PHASE} + COMMENT ${CMD_COMMENT} + WORKING_DIRECTORY ${CMD_CWD} + VERBATIM ${CMD_COMMANDS} + ) + set_property(TARGET ${CMD_TARGET} PROPERTY OUTPUT ${CMD_EFFECTIVE_OUTPUTS} APPEND) + set_property(TARGET ${CMD_TARGET} PROPERTY DEPENDS ${CMD_EFFECTIVE_DEPENDS} APPEND) + return() + endif() + + if(NOT CMD_EFFECTIVE_OUTPUTS) + message(FATAL_ERROR "register_command: OUTPUTS or ARTIFACTS is required, or set ALWAYS_RUN") + endif() + + if(CMD_TARGET) + if(TARGET ${CMD_TARGET}) + message(FATAL_ERROR "register_command: TARGET is already registered: ${CMD_TARGET}") + endif() + add_custom_target(${CMD_TARGET} + COMMENT ${CMD_COMMENT} + DEPENDS ${CMD_EFFECTIVE_OUTPUTS} + JOB_POOL ${CMD_GROUP} + ) + if(TARGET clone-${CMD_TARGET}) + add_dependencies(${CMD_TARGET} clone-${CMD_TARGET}) + endif() + endif() + + add_custom_command( + VERBATIM ${CMD_COMMANDS} + WORKING_DIRECTORY ${CMD_CWD} + COMMENT ${CMD_COMMENT} + DEPENDS ${CMD_EFFECTIVE_DEPENDS} + OUTPUT ${CMD_EFFECTIVE_OUTPUTS} + JOB_POOL ${CMD_GROUP} + ) +endfunction() + +# parse_package_json() +# Description: +# Parses a package.json file. +# Arguments: +# CWD string - The directory to look for the package.json file +# VERSION_VARIABLE string - The variable to set to the package version +# NODE_MODULES_VARIABLE string - The variable to set to list of node_modules sources +function(parse_package_json) + set(args CWD VERSION_VARIABLE NODE_MODULES_VARIABLE) + cmake_parse_arguments(NPM "" "${args}" "" ${ARGN}) + + if(NOT NPM_CWD) + set(NPM_CWD ${CWD}) + endif() + + set(NPM_PACKAGE_JSON_PATH ${NPM_CWD}/package.json) + + if(NOT EXISTS ${NPM_PACKAGE_JSON_PATH}) + message(FATAL_ERROR "parse_package_json: package.json not found: ${NPM_PACKAGE_JSON_PATH}") + endif() + + file(READ ${NPM_PACKAGE_JSON_PATH} NPM_PACKAGE_JSON) + if(NOT NPM_PACKAGE_JSON) + message(FATAL_ERROR "parse_package_json: failed to read package.json: ${NPM_PACKAGE_JSON_PATH}") + endif() + + if(NPM_VERSION_VARIABLE) + string(JSON NPM_VERSION ERROR_VARIABLE error GET "${NPM_PACKAGE_JSON}" version) + if(error) + message(FATAL_ERROR "parse_package_json: failed to read 'version': ${error}") + endif() + set(${NPM_VERSION_VARIABLE} ${NPM_VERSION} PARENT_SCOPE) + endif() + + if(NPM_NODE_MODULES_VARIABLE) + set(NPM_NODE_MODULES) + set(NPM_NODE_MODULES_PATH ${NPM_CWD}/node_modules) + set(NPM_NODE_MODULES_PROPERTIES "devDependencies" "dependencies") + + foreach(property ${NPM_NODE_MODULES_PROPERTIES}) + string(JSON NPM_${property} ERROR_VARIABLE error GET "${NPM_PACKAGE_JSON}" "${property}") + if(error MATCHES "not found") + continue() + endif() + if(error) + message(FATAL_ERROR "parse_package_json: failed to read '${property}': ${error}") + endif() + + string(JSON NPM_${property}_LENGTH ERROR_VARIABLE error LENGTH "${NPM_${property}}") + if(error) + message(FATAL_ERROR "parse_package_json: failed to read '${property}' length: ${error}") + endif() + + math(EXPR NPM_${property}_MAX_INDEX "${NPM_${property}_LENGTH} - 1") + foreach(i RANGE 0 ${NPM_${property}_MAX_INDEX}) + string(JSON NPM_${property}_${i} ERROR_VARIABLE error MEMBER "${NPM_${property}}" ${i}) + if(error) + message(FATAL_ERROR "parse_package_json: failed to index '${property}' at ${i}: ${error}") + endif() + list(APPEND NPM_NODE_MODULES ${NPM_NODE_MODULES_PATH}/${NPM_${property}_${i}}/package.json) + endforeach() + endforeach() + + set(${NPM_NODE_MODULES_VARIABLE} ${NPM_NODE_MODULES} PARENT_SCOPE) + endif() +endfunction() + +# register_bun_install() +# Description: +# Registers a command to run `bun install` in a directory. +# Arguments: +# CWD string - The directory to run `bun install` +# NODE_MODULES_VARIABLE string - The variable to set to list of node_modules sources +function(register_bun_install) + set(args CWD NODE_MODULES_VARIABLE) + cmake_parse_arguments(NPM "" "${args}" "" ${ARGN}) + + if(NOT NPM_CWD) + set(NPM_CWD ${CWD}) + endif() + + if(NPM_CWD STREQUAL ${CWD}) + set(NPM_COMMENT "bun install") + else() + set(NPM_COMMENT "bun install --cwd ${NPM_CWD}") + endif() + + parse_package_json( + CWD + ${NPM_CWD} + NODE_MODULES_VARIABLE + NPM_NODE_MODULES + ) + + if(NOT NPM_NODE_MODULES) + message(FATAL_ERROR "register_bun_install: ${NPM_CWD}/package.json does not have dependencies?") + endif() + + register_command( + COMMENT + ${NPM_COMMENT} + CWD + ${NPM_CWD} + COMMAND + ${BUN_EXECUTABLE} + install + --frozen-lockfile + SOURCES + ${NPM_CWD}/package.json + OUTPUTS + ${NPM_NODE_MODULES} + ) + + set(${NPM_NODE_MODULES_VARIABLE} ${NPM_NODE_MODULES} PARENT_SCOPE) +endfunction() + +# register_repository() +# Description: +# Registers a git repository. +# Arguments: +# NAME string - The name of the repository +# REPOSITORY string - The repository to clone +# BRANCH string - The branch to clone +# TAG string - The tag to clone +# COMMIT string - The commit to clone +# PATH string - The path to clone the repository to +# OUTPUTS string - The outputs of the repository +function(register_repository) + set(args NAME REPOSITORY BRANCH TAG COMMIT PATH) + set(multiArgs OUTPUTS) + cmake_parse_arguments(GIT "" "${args}" "${multiArgs}" ${ARGN}) + + if(NOT GIT_REPOSITORY) + message(FATAL_ERROR "git_clone: REPOSITORY is required") + endif() + + if(NOT GIT_BRANCH AND NOT GIT_TAG AND NOT GIT_COMMIT) + message(FATAL_ERROR "git_clone: COMMIT, TAG, or BRANCH is required") + endif() + + if(NOT GIT_PATH) + set(GIT_PATH ${VENDOR_PATH}/${GIT_NAME}) + endif() + + if(GIT_COMMIT) + set(GIT_REF ${GIT_COMMIT}) + elseif(GIT_TAG) + set(GIT_REF refs/tags/${GIT_TAG}) + else() + set(GIT_REF refs/heads/${GIT_BRANCH}) + endif() + + set(GIT_EFFECTIVE_OUTPUTS) + foreach(output ${GIT_OUTPUTS}) + list(APPEND GIT_EFFECTIVE_OUTPUTS ${GIT_PATH}/${output}) + endforeach() + + register_command( + TARGET + clone-${GIT_NAME} + COMMENT + "Cloning ${GIT_NAME}" + COMMAND + ${CMAKE_COMMAND} + -DGIT_PATH=${GIT_PATH} + -DGIT_REPOSITORY=${GIT_REPOSITORY} + -DGIT_REF=${GIT_REF} + -DGIT_NAME=${GIT_NAME} + -P ${CWD}/cmake/scripts/GitClone.cmake + OUTPUTS + ${GIT_PATH} + ${GIT_EFFECTIVE_OUTPUTS} + ) +endfunction() + +# register_cmake_command() +# Description: +# Registers a command that builds an external CMake project. +# Arguments: +# TARGET string - The target to register the command with +# ARGS string[] - The arguments to pass to CMake (e.g. -DKEY=VALUE) +# CWD string - The directory where the CMake files are located +# BUILD_PATH string - The path to build the project to +# LIB_PATH string - The path to the libraries +# TARGETS string[] - The targets to build from CMake +# LIBRARIES string[] - The libraries that are built +# INCLUDES string[] - The include paths +function(register_cmake_command) + set(args TARGET CWD BUILD_PATH LIB_PATH) + set(multiArgs ARGS TARGETS LIBRARIES INCLUDES) + # Use "MAKE" instead of "CMAKE" to prevent conflicts with CMake's own CMAKE_* variables + cmake_parse_arguments(MAKE "" "${args}" "${multiArgs}" ${ARGN}) + + if(NOT MAKE_TARGET) + message(FATAL_ERROR "register_cmake_command: TARGET is required") + endif() + + if(TARGET ${MAKE_TARGET}) + message(FATAL_ERROR "register_cmake_command: TARGET is already a target: ${MAKE_TARGET}") + endif() + + if(NOT MAKE_CWD) + set(MAKE_CWD ${VENDOR_PATH}/${MAKE_TARGET}) + endif() + + if(NOT MAKE_BUILD_PATH) + set(MAKE_BUILD_PATH ${BUILD_PATH}/${MAKE_TARGET}) + endif() + + if(MAKE_LIB_PATH) + set(MAKE_LIB_PATH ${MAKE_BUILD_PATH}/${MAKE_LIB_PATH}) + else() + set(MAKE_LIB_PATH ${MAKE_BUILD_PATH}) + endif() + + set(MAKE_EFFECTIVE_ARGS -B${MAKE_BUILD_PATH} ${CMAKE_ARGS}) + + set(setFlags GENERATOR BUILD_TYPE) + set(appendFlags C_FLAGS CXX_FLAGS LINKER_FLAGS) + set(specialFlags POSITION_INDEPENDENT_CODE) + set(flags ${setFlags} ${appendFlags} ${specialFlags}) + + foreach(arg ${MAKE_ARGS}) + foreach(flag ${flags}) + if(arg MATCHES "-DCMAKE_${flag}=(.*)") + if(DEFINED MAKE_${flag}) + message(FATAL_ERROR "register_cmake_command: CMAKE_${flag} was already set: \"${MAKE_${flag}}\"") + endif() + set(MAKE_${flag} ${CMAKE_MATCH_1}) + set(${arg}_USED ON) + endif() + endforeach() + if(NOT ${arg}_USED) + list(APPEND MAKE_EFFECTIVE_ARGS ${arg}) + endif() + endforeach() + + foreach(flag ${setFlags}) + if(NOT DEFINED MAKE_${flag} AND DEFINED CMAKE_${flag}) + set(MAKE_${flag} ${CMAKE_${flag}}) + endif() + endforeach() + + foreach(flag ${appendFlags}) + if(MAKE_${flag}) + set(MAKE_${flag} "${CMAKE_${flag}} ${MAKE_${flag}}") + else() + set(MAKE_${flag} ${CMAKE_${flag}}) + endif() + endforeach() + + if(MAKE_POSITION_INDEPENDENT_CODE AND NOT WIN32) + set(MAKE_C_FLAGS "${MAKE_C_FLAGS} -fPIC") + set(MAKE_CXX_FLAGS "${MAKE_CXX_FLAGS} -fPIC") + elseif(APPLE) + set(MAKE_C_FLAGS "${MAKE_C_FLAGS} -fno-pic -fno-pie") + set(MAKE_CXX_FLAGS "${MAKE_CXX_FLAGS} -fno-pic -fno-pie") + endif() + + set(effectiveFlags ${setFlags} ${appendFlags}) + foreach(flag ${effectiveFlags}) + list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_${flag}=${MAKE_${flag}}") + endforeach() + + if(DEFINED FRESH) + list(APPEND MAKE_EFFECTIVE_ARGS --fresh) + endif() + + register_command( + COMMENT "Configuring ${MAKE_TARGET}" + TARGET configure-${MAKE_TARGET} + COMMAND ${CMAKE_COMMAND} ${MAKE_EFFECTIVE_ARGS} + CWD ${MAKE_CWD} + OUTPUTS ${MAKE_BUILD_PATH}/CMakeCache.txt + ) + + if(TARGET clone-${MAKE_TARGET}) + add_dependencies(configure-${MAKE_TARGET} clone-${MAKE_TARGET}) + endif() + + set(MAKE_BUILD_ARGS --build ${MAKE_BUILD_PATH} --config ${MAKE_BUILD_TYPE}) + + set(MAKE_EFFECTIVE_LIBRARIES) + set(MAKE_ARTIFACTS) + foreach(lib ${MAKE_LIBRARIES}) + if(lib MATCHES "^(WIN32|UNIX|APPLE)$") + if(${lib}) + continue() + else() + list(POP_BACK MAKE_ARTIFACTS) + endif() + else() + list(APPEND MAKE_EFFECTIVE_LIBRARIES ${lib}) + if(lib MATCHES "\\.") + list(APPEND MAKE_ARTIFACTS ${MAKE_LIB_PATH}/${lib}) + else() + list(APPEND MAKE_ARTIFACTS ${MAKE_LIB_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + endif() + endforeach() + + if(NOT MAKE_TARGETS) + set(MAKE_TARGETS ${MAKE_EFFECTIVE_LIBRARIES}) + endif() + + foreach(target ${MAKE_TARGETS}) + list(APPEND MAKE_BUILD_ARGS --target ${target}) + endforeach() + + set(MAKE_EFFECTIVE_INCLUDES) + foreach(include ${MAKE_INCLUDES}) + if(include STREQUAL ".") + list(APPEND MAKE_EFFECTIVE_INCLUDES ${MAKE_CWD}) + else() + list(APPEND MAKE_EFFECTIVE_INCLUDES ${MAKE_CWD}/${include}) + endif() + endforeach() + + register_command( + COMMENT "Building ${MAKE_TARGET}" + TARGET ${MAKE_TARGET} + TARGETS configure-${MAKE_TARGET} + COMMAND ${CMAKE_COMMAND} ${MAKE_BUILD_ARGS} + CWD ${MAKE_CWD} + ARTIFACTS ${MAKE_ARTIFACTS} + ) + + if(MAKE_EFFECTIVE_INCLUDES) + target_include_directories(${bun} PRIVATE ${MAKE_EFFECTIVE_INCLUDES}) + if(TARGET clone-${MAKE_TARGET} AND NOT BUN_LINK_ONLY) + add_dependencies(${bun} clone-${MAKE_TARGET}) + endif() + endif() + + # HACK: Workaround for duplicate symbols when linking mimalloc.o + # >| duplicate symbol '_mi_page_queue_append(mi_heap_s*, mi_page_queue_s*, mi_page_queue_s*)' in: + # >| mimalloc/CMakeFiles/mimalloc-obj.dir/src/static.c.o + # >| ld: 287 duplicate symbols for architecture arm64 + if(NOT BUN_LINK_ONLY OR NOT MAKE_ARTIFACTS MATCHES "static.c.o") + target_link_libraries(${bun} PRIVATE ${MAKE_ARTIFACTS}) + endif() + + if(BUN_LINK_ONLY) + target_sources(${bun} PRIVATE ${MAKE_ARTIFACTS}) + endif() +endfunction() + +# register_compiler_flag() +# Description: +# Registers a compiler flag, similar to `add_compile_options()`, but has more validation and features. +# Arguments: +# flags string[] - The flags to register +# DESCRIPTION string - The description of the flag +# LANGUAGES string[] - The languages to register the flag (default: C, CXX) +# TARGETS string[] - The targets to register the flag (default: all) +function(register_compiler_flags) + set(args DESCRIPTION) + set(multiArgs LANGUAGES TARGETS) + cmake_parse_arguments(COMPILER "" "${args}" "${multiArgs}" ${ARGN}) + + if(NOT COMPILER_LANGUAGES) + set(COMPILER_LANGUAGES C CXX) + endif() + + set(COMPILER_FLAGS) + foreach(flag ${COMPILER_UNPARSED_ARGUMENTS}) + if(flag STREQUAL "ON") + continue() + elseif(flag STREQUAL "OFF") + list(POP_BACK COMPILER_FLAGS) + elseif(flag MATCHES "^(-|/)") + list(APPEND COMPILER_FLAGS ${flag}) + else() + message(FATAL_ERROR "register_compiler_flags: Invalid flag: \"${flag}\"") + endif() + endforeach() + + foreach(target ${COMPILER_TARGETS}) + if(NOT TARGET ${target}) + message(FATAL_ERROR "register_compiler_flags: \"${target}\" is not a target") + endif() + endforeach() + + foreach(lang ${COMPILER_LANGUAGES}) + list(JOIN COMPILER_FLAGS " " COMPILER_FLAGS_STRING) + + if(NOT COMPILER_TARGETS) + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE) + endif() + + foreach(target ${COMPILER_TARGETS}) + set(${target}_CMAKE_${lang}_FLAGS "${${target}_CMAKE_${lang}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE) + endforeach() + endforeach() + + foreach(lang ${COMPILER_LANGUAGES}) + foreach(flag ${COMPILER_FLAGS}) + if(NOT COMPILER_TARGETS) + add_compile_options($<$:${flag}>) + endif() + + foreach(target ${COMPILER_TARGETS}) + get_target_property(type ${target} TYPE) + if(type MATCHES "EXECUTABLE|LIBRARY") + target_compile_options(${target} PRIVATE $<$:${flag}>) + endif() + endforeach() + endforeach() + endforeach() +endfunction() + +function(register_compiler_definitions) + +endfunction() + +# register_linker_flags() +# Description: +# Registers a linker flag, similar to `add_link_options()`. +# Arguments: +# flags string[] - The flags to register +# DESCRIPTION string - The description of the flag +function(register_linker_flags) + set(args DESCRIPTION) + cmake_parse_arguments(LINKER "" "${args}" "" ${ARGN}) + + foreach(flag ${LINKER_UNPARSED_ARGUMENTS}) + if(flag STREQUAL "ON") + continue() + elseif(flag STREQUAL "OFF") + list(POP_FRONT LINKER_FLAGS) + elseif(flag MATCHES "^(-|/)") + list(APPEND LINKER_FLAGS ${flag}) + else() + message(FATAL_ERROR "register_linker_flags: Invalid flag: \"${flag}\"") + endif() + endforeach() + + add_link_options(${LINKER_FLAGS}) +endfunction() + +function(print_compiler_flags) + get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) + set(languages C CXX) + foreach(target ${targets}) + get_target_property(type ${target} TYPE) + message(STATUS "Target: ${target}") + foreach(lang ${languages}) + if(${target}_CMAKE_${lang}_FLAGS) + message(STATUS " ${lang} Flags: ${${target}_CMAKE_${lang}_FLAGS}") + endif() + endforeach() + endforeach() + foreach(lang ${languages}) + message(STATUS "Language: ${lang}") + if(CMAKE_${lang}_FLAGS) + message(STATUS " Flags: ${CMAKE_${lang}_FLAGS}") + endif() + endforeach() +endfunction() diff --git a/cmake/Options.cmake b/cmake/Options.cmake new file mode 100644 index 0000000000000..44ea99042db73 --- /dev/null +++ b/cmake/Options.cmake @@ -0,0 +1,157 @@ +if(NOT CMAKE_SYSTEM_NAME OR NOT CMAKE_SYSTEM_PROCESSOR) + message(FATAL_ERROR "CMake included this file before project() was called") +endif() + +optionx(BUN_LINK_ONLY BOOL "If only the linking step should be built" DEFAULT OFF) +optionx(BUN_CPP_ONLY BOOL "If only the C++ part of Bun should be built" DEFAULT OFF) + +optionx(BUILDKITE BOOL "If Buildkite is enabled" DEFAULT OFF) +optionx(GITHUB_ACTIONS BOOL "If GitHub Actions is enabled" DEFAULT OFF) + +if(BUILDKITE) + optionx(BUILDKITE_COMMIT STRING "The commit hash") + optionx(BUILDKITE_MESSAGE STRING "The commit message") +endif() + +optionx(CMAKE_BUILD_TYPE "Debug|Release|RelWithDebInfo|MinSizeRel" "The build type to use" REQUIRED) + +if(CMAKE_BUILD_TYPE MATCHES "Release|RelWithDebInfo|MinSizeRel") + setx(RELEASE ON) +else() + setx(RELEASE OFF) +endif() + +if(CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo") + setx(DEBUG ON) +else() + setx(DEBUG OFF) +endif() + +if(CMAKE_BUILD_TYPE MATCHES "MinSizeRel") + setx(ENABLE_SMOL ON) +endif() + +if(APPLE) + setx(OS "darwin") +elseif(WIN32) + setx(OS "windows") +elseif(LINUX) + setx(OS "linux") +else() + message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}") +endif() + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|arm") + setx(ARCH "aarch64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64") + setx(ARCH "x64") +else() + message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +if(ARCH STREQUAL "x64") + optionx(ENABLE_BASELINE BOOL "If baseline features should be used for older CPUs (e.g. disables AVX, AVX2)" DEFAULT OFF) +endif() + +optionx(ENABLE_LOGS BOOL "If debug logs should be enabled" DEFAULT ${DEBUG}) +optionx(ENABLE_ASSERTIONS BOOL "If debug assertions should be enabled" DEFAULT ${DEBUG}) + +if(BUILDKITE_MESSAGE AND BUILDKITE_MESSAGE MATCHES "\\[release build\\]") + message(STATUS "Switched to release build, since commit message contains: \"[release build]\"") + set(DEFAULT_CANARY OFF) +else() + set(DEFAULT_CANARY ON) +endif() + +optionx(ENABLE_CANARY BOOL "If canary features should be enabled" DEFAULT ${DEFAULT_CANARY}) + +if(ENABLE_CANARY AND BUILDKITE) + execute_process( + COMMAND buildkite-agent meta-data get "canary" + OUTPUT_VARIABLE DEFAULT_CANARY_REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +elseif(ENABLE_CANARY) + set(DEFAULT_CANARY_REVISION "1") +else() + set(DEFAULT_CANARY_REVISION "0") +endif() + +optionx(CANARY_REVISION STRING "The canary revision of the build" DEFAULT ${DEFAULT_CANARY_REVISION}) + +if(RELEASE AND LINUX) + set(DEFAULT_LTO ON) +else() + set(DEFAULT_LTO OFF) +endif() + +optionx(ENABLE_LTO BOOL "If LTO (link-time optimization) should be used" DEFAULT ${DEFAULT_LTO}) + +if(LINUX) + optionx(ENABLE_VALGRIND BOOL "If Valgrind support should be enabled" DEFAULT OFF) +endif() + +if(USE_VALGRIND AND NOT USE_BASELINE) + message(WARNING "If valgrind is enabled, baseline must also be enabled") + setx(USE_BASELINE ON) +endif() + +if(BUILDKITE_COMMIT) + set(DEFAULT_REVISION ${BUILDKITE_COMMIT}) +else() + execute_process( + COMMAND git rev-parse HEAD + WORKING_DIRECTORY ${CWD} + OUTPUT_VARIABLE DEFAULT_REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if(NOT DEFAULT_REVISION) + set(DEFAULT_REVISION "unknown") + endif() +endif() + +optionx(REVISION STRING "The git revision of the build" DEFAULT ${DEFAULT_REVISION}) + +# Used in process.version, process.versions.node, napi, and elsewhere +optionx(NODEJS_VERSION STRING "The version of Node.js to report" DEFAULT "22.6.0") + +# Used in process.versions.modules and compared while loading V8 modules +optionx(NODEJS_ABI_VERSION STRING "The ABI version of Node.js to report" DEFAULT "127") + +if(APPLE) + set(DEFAULT_STATIC_SQLITE OFF) +else() + set(DEFAULT_STATIC_SQLITE ON) +endif() + +optionx(USE_STATIC_SQLITE BOOL "If SQLite should be statically linked" DEFAULT ${DEFAULT_STATIC_SQLITE}) + +set(DEFAULT_STATIC_LIBATOMIC ON) + +if(CMAKE_HOST_LINUX AND NOT WIN32 AND NOT APPLE) + execute_process( + COMMAND grep -w "NAME" /etc/os-release + OUTPUT_VARIABLE LINUX_DISTRO + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if(LINUX_DISTRO MATCHES "NAME=\"(Arch|Manjaro|Artix) Linux\"|NAME=\"openSUSE Tumbleweed\"") + set(DEFAULT_STATIC_LIBATOMIC OFF) + endif() +endif() + +optionx(USE_STATIC_LIBATOMIC BOOL "If libatomic should be statically linked" DEFAULT ${DEFAULT_STATIC_LIBATOMIC}) + +if(APPLE) + set(DEFAULT_WEBKIT_ICU OFF) +else() + set(DEFAULT_WEBKIT_ICU ON) +endif() + +optionx(USE_WEBKIT_ICU BOOL "Use the ICU libraries from WebKit" DEFAULT ${DEFAULT_WEBKIT_ICU}) + +optionx(ERROR_LIMIT STRING "Maximum number of errors to show when compiling C++ code" DEFAULT "100") + +list(APPEND CMAKE_ARGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON) + diff --git a/cmake/Policies.cmake b/cmake/Policies.cmake new file mode 100644 index 0000000000000..d55a4ae021a11 --- /dev/null +++ b/cmake/Policies.cmake @@ -0,0 +1,11 @@ +# Let the MSVC runtime be set using CMAKE_MSVC_RUNTIME_LIBRARY, instead of automatically. +# Since CMake 3.15. +cmake_policy(SET CMP0091 NEW) + +# If INTERPROCEDURAL_OPTIMIZATION is enabled and not supported by the compiler, throw an error. +# Since CMake 3.9. +cmake_policy(SET CMP0069 NEW) + +# Use CMAKE_{C,CXX}_STANDARD when evaluating try_compile(). +# Since CMake 3.8. +cmake_policy(SET CMP0067 NEW) diff --git a/cmake/analysis/RunClangTidy.cmake b/cmake/analysis/RunClangTidy.cmake new file mode 100644 index 0000000000000..bac6cc5e75353 --- /dev/null +++ b/cmake/analysis/RunClangTidy.cmake @@ -0,0 +1,56 @@ +# https://clang.llvm.org/extra/clang-tidy/ + +find_command( + VARIABLE + CLANG_TIDY_PROGRAM + COMMAND + clang-tidy + VERSION + ${LLVM_VERSION} + REQUIRED + OFF +) + +set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PROGRAM} ${BUN_CPP_SOURCES} + -p ${BUILD_PATH} + --config-file=${CWD}/.clang-tidy + --fix + --fix-errors + --fix-notes +) + +if(CMAKE_COLOR_DIAGNOSTICS) + list(APPEND CLANG_TIDY_COMMAND --use-color) +endif() + +# Extra clang-tidy checks that are normally disabled due to noise. +# e.g. JavaScriptCore/Lookup.h +set(CLANG_TIDY_EXTRA_COMMAND ${CLANG_TIDY_PROGRAM} + --checks=performance-* +) + +register_command( + TARGET + clang-tidy + COMMENT + "Running clang-tidy" + COMMAND + ${CLANG_TIDY_COMMAND} + CWD + ${BUILD_PATH} + TARGETS + ${bun} +) + +register_command( + TARGET + clang-tidy-extra + COMMENT + "Running clang-tidy with extra checks" + COMMAND + ${CLANG_TIDY_EXTRA_COMMAND} + CWD + ${BUILD_PATH} + TARGETS + ${bun} +) diff --git a/cmake/analysis/RunCppCheck.cmake b/cmake/analysis/RunCppCheck.cmake new file mode 100644 index 0000000000000..a384a44863fe4 --- /dev/null +++ b/cmake/analysis/RunCppCheck.cmake @@ -0,0 +1,33 @@ +# https://cppcheck.sourceforge.io/ + +find_command( + VARIABLE + CPPCHECK_EXECUTABLE + COMMAND + cppcheck + REQUIRED + OFF +) + +set(CPPCHECK_COMMAND ${CPPCHECK_EXECUTABLE} + --cppcheck-build-dir=${BUILD_PATH}/cppcheck + --project=${BUILD_PATH}/compile_commands.json + --clang=${CMAKE_CXX_COMPILER} + --std=c++${CMAKE_CXX_STANDARD} + --report-progress + --showtime=summary +) + +register_command( + TARGET + cppcheck + COMMENT + "Running cppcheck" + COMMAND + ${CMAKE_COMMAND} -E make_directory cppcheck + && ${CPPCHECK_COMMAND} + CWD + ${BUILD_PATH} + TARGETS + ${bun} +) diff --git a/cmake/analysis/RunCppLint.cmake b/cmake/analysis/RunCppLint.cmake new file mode 100644 index 0000000000000..5b9264ecf5307 --- /dev/null +++ b/cmake/analysis/RunCppLint.cmake @@ -0,0 +1,22 @@ +find_command( + VARIABLE + CPPLINT_PROGRAM + COMMAND + cpplint + REQUIRED + OFF +) + +register_command( + TARGET + cpplint + COMMENT + "Running cpplint" + COMMAND + ${CPPLINT_PROGRAM} + ${BUN_CPP_SOURCES} + CWD + ${BUILD_PATH} + TARGETS + ${bun} +) diff --git a/cmake/analysis/RunIWYU.cmake b/cmake/analysis/RunIWYU.cmake new file mode 100644 index 0000000000000..340e44f78023e --- /dev/null +++ b/cmake/analysis/RunIWYU.cmake @@ -0,0 +1,67 @@ +# IWYU = "Include What You Use" +# https://include-what-you-use.org/ + +setx(IWYU_SOURCE_PATH ${CACHE_PATH}/iwyu-${LLVM_VERSION_MAJOR}) +setx(IWYU_BUILD_PATH ${IWYU_SOURCE_PATH}/build) +setx(IWYU_PROGRAM ${IWYU_BUILD_PATH}/bin/include-what-you-use) + +register_repository( + NAME + iwyu + REPOSITORY + include-what-you-use/include-what-you-use + BRANCH + clang_${LLVM_VERSION_MAJOR} + PATH + ${IWYU_SOURCE_PATH} +) + +register_command( + TARGET + build-iwyu + COMMENT + "Building iwyu" + COMMAND + ${CMAKE_COMMAND} + -B${IWYU_BUILD_PATH} + -G${CMAKE_GENERATOR} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER} + -DIWYU_LLVM_ROOT_PATH=${LLVM_PREFIX} + && ${CMAKE_COMMAND} + --build ${IWYU_BUILD_PATH} + CWD + ${IWYU_SOURCE_PATH} + TARGETS + clone-iwyu +) + +find_command( + VARIABLE + PYTHON_EXECUTABLE + COMMAND + python3 + python + VERSION + >=3.0.0 + REQUIRED + OFF +) + +register_command( + TARGET + iwyu + COMMENT + "Running iwyu" + COMMAND + ${CMAKE_COMMAND} + -E env IWYU_BINARY=${IWYU_PROGRAM} + ${PYTHON_EXECUTABLE} + ${IWYU_SOURCE_PATH}/iwyu_tool.py + -p ${BUILD_PATH} + CWD + ${BUILD_PATH} + TARGETS + build-iwyu + ${bun} +) diff --git a/cmake/scripts/GitClone.cmake b/cmake/scripts/GitClone.cmake new file mode 100644 index 0000000000000..89f86abd76def --- /dev/null +++ b/cmake/scripts/GitClone.cmake @@ -0,0 +1,74 @@ +include(cmake/Globals.cmake) + +if(NOT GIT_PATH OR NOT GIT_REPOSITORY OR NOT GIT_REF) + message(FATAL_ERROR "git_clone: GIT_PATH, GIT_REPOSITORY, and GIT_REF are required") +endif() + +setx(GIT_PATH ${GIT_PATH}) +setx(GIT_REPOSITORY ${GIT_REPOSITORY}) +setx(GIT_REF ${GIT_REF}) + +string(REGEX MATCH "([^/]+)$" GIT_ORIGINAL_NAME ${GIT_REPOSITORY}) + +if(NOT GIT_NAME) + setx(GIT_NAME ${GIT_ORIGINAL_NAME}) +endif() + +set(GIT_REF_PATH ${GIT_PATH}/.ref) + +if(EXISTS ${GIT_REF_PATH}) + file(READ ${GIT_REF_PATH} GIT_CACHED_REF) + if(GIT_CACHED_REF STREQUAL GIT_REF) + return() + endif() +endif() + +setx(GIT_DOWNLOAD_PATH ${GIT_PATH}.tar.gz) +setx(GIT_DOWNLOAD_URL https://github.com/${GIT_REPOSITORY}/archive/${GIT_REF}.tar.gz) + +foreach(i RANGE 10) + set(GIT_DOWNLOAD_TMP_PATH ${GIT_PATH}.tmp.${i}) + file(DOWNLOAD + ${GIT_DOWNLOAD_URL} + ${GIT_DOWNLOAD_TMP_PATH} + TIMEOUT 120 + STATUS GIT_DOWNLOAD_STATUS + SHOW_PROGRESS + ) + if(GIT_DOWNLOAD_STATUS MATCHES "^0" AND EXISTS ${GIT_DOWNLOAD_TMP_PATH}) + file(RENAME ${GIT_DOWNLOAD_TMP_PATH} ${GIT_DOWNLOAD_PATH}) + break() + endif() + message(WARNING "git_clone: ${GIT_DOWNLOAD_STATUS}: ${GIT_DOWNLOAD_URL}") + file(REMOVE ${GIT_DOWNLOAD_TMP_PATH}) +endforeach() + +if(NOT EXISTS ${GIT_DOWNLOAD_PATH}) + message(FATAL_ERROR "git_clone: failed to download ${GIT_DOWNLOAD_URL}") +endif() + +file(REMOVE_RECURSE ${GIT_PATH}) +get_filename_component(GIT_PARENT_PATH ${GIT_PATH} DIRECTORY) +file(ARCHIVE_EXTRACT INPUT ${GIT_DOWNLOAD_PATH} DESTINATION ${GIT_PARENT_PATH}/tmp-${GIT_ORIGINAL_NAME} TOUCH) +file(GLOB GIT_TMP_PATH LIST_DIRECTORIES ON ${GIT_PARENT_PATH}/tmp-${GIT_ORIGINAL_NAME}/${GIT_ORIGINAL_NAME}-*) +file(RENAME ${GIT_TMP_PATH} ${GIT_PATH}) +file(REMOVE_RECURSE ${GIT_PARENT_PATH}/tmp-${GIT_ORIGINAL_NAME}) +file(REMOVE ${GIT_DOWNLOAD_PATH}) + +file(GLOB_RECURSE GIT_PATCH_PATHS ${CMAKE_SOURCE_DIR}/patches/${GIT_NAME}/*) +foreach(GIT_PATCH_PATH ${GIT_PATCH_PATHS}) + if(GIT_PATCH_PATH MATCHES "\\.patch$") + execute_process( + COMMAND git apply --ignore-whitespace --ignore-space-change --no-index --verbose ${GIT_PATCH_PATH} + WORKING_DIRECTORY ${GIT_PATH} + RESULT_VARIABLE GIT_PATCH_RESULT + ) + if(NOT GIT_PATCH_RESULT EQUAL 0) + message(FATAL_ERROR "git_clone: failed to apply patch: ${GIT_PATCH_PATH}") + endif() + else() + file(COPY ${GIT_PATCH_PATH} DESTINATION ${GIT_PATH}) + endif() +endforeach() + +file(WRITE ${GIT_REF_PATH} ${GIT_REF}) diff --git a/cmake/targets/BuildBoringSSL.cmake b/cmake/targets/BuildBoringSSL.cmake new file mode 100644 index 0000000000000..28575eb35f7b6 --- /dev/null +++ b/cmake/targets/BuildBoringSSL.cmake @@ -0,0 +1,21 @@ +register_repository( + NAME + boringssl + REPOSITORY + oven-sh/boringssl + COMMIT + 29a2cd359458c9384694b75456026e4b57e3e567 +) + +register_cmake_command( + TARGET + boringssl + LIBRARIES + crypto + ssl + decrepit + ARGS + -DBUILD_SHARED_LIBS=OFF + INCLUDES + include +) diff --git a/cmake/targets/BuildBrotli.cmake b/cmake/targets/BuildBrotli.cmake new file mode 100644 index 0000000000000..f9bc8d96019f7 --- /dev/null +++ b/cmake/targets/BuildBrotli.cmake @@ -0,0 +1,31 @@ +register_repository( + NAME + brotli + REPOSITORY + google/brotli + TAG + v1.1.0 +) + +# Tests fail with "BrotliDecompressionError" when LTO is enabled +# only on Linux x64 (non-baseline). It's a mystery. +if(LINUX AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64" AND NOT ENABLE_BASELINE) + set(BROTLI_CMAKE_ARGS "-DCMAKE_C_FLAGS=-fno-lto") +endif() + +register_cmake_command( + TARGET + brotli + LIBRARIES + brotlicommon + brotlidec + brotlienc + ARGS + -DBUILD_SHARED_LIBS=OFF + -DBROTLI_BUILD_TOOLS=OFF + -DBROTLI_EMSCRIPTEN=OFF + -DBROTLI_DISABLE_TESTS=ON + ${BROTLI_CMAKE_ARGS} + INCLUDES + c/include +) diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake new file mode 100644 index 0000000000000..b2b422f234d12 --- /dev/null +++ b/cmake/targets/BuildBun.cmake @@ -0,0 +1,1134 @@ +if(DEBUG) + set(bun bun-debug) +elseif(ENABLE_SMOL) + set(bun bun-smol-profile) + set(bunStrip bun-smol) +elseif(ENABLE_VALGRIND) + set(bun bun-valgrind) +elseif(ENABLE_ASSERTIONS) + set(bun bun-assertions) +else() + set(bun bun-profile) + set(bunStrip bun) +endif() + +set(bunExe ${bun}${CMAKE_EXECUTABLE_SUFFIX}) + +if(bunStrip) + set(bunStripExe ${bunStrip}${CMAKE_EXECUTABLE_SUFFIX}) + set(buns ${bun} ${bunStrip}) +else() + set(buns ${bun}) +endif() + +# Some commands use this path, and some do not. +# In the future, change those commands so that generated files are written to this path. +optionx(CODEGEN_PATH FILEPATH "Path to the codegen directory" DEFAULT ${BUILD_PATH}/codegen) + +if((NOT DEFINED CONFIGURE_DEPENDS AND NOT CI) OR CONFIGURE_DEPENDS) + set(CONFIGURE_DEPENDS "CONFIGURE_DEPENDS") +else() + set(CONFIGURE_DEPENDS "") +endif() + +# --- Codegen --- + +set(BUN_ZIG_IDENTIFIER_SOURCE ${CWD}/src/js_lexer) +set(BUN_ZIG_IDENTIFIER_SCRIPT ${BUN_ZIG_IDENTIFIER_SOURCE}/identifier_data.zig) + +file(GLOB BUN_ZIG_IDENTIFIER_SOURCES ${CONFIGURE_DEPENDS} + ${BUN_ZIG_IDENTIFIER_SCRIPT} + ${BUN_ZIG_IDENTIFIER_SOURCE}/*.zig +) + +set(BUN_ZIG_IDENTIFIER_OUTPUTS + ${BUN_ZIG_IDENTIFIER_SOURCE}/id_continue_bitset.blob + ${BUN_ZIG_IDENTIFIER_SOURCE}/id_continue_bitset.meta.blob + ${BUN_ZIG_IDENTIFIER_SOURCE}/id_start_bitset.blob + ${BUN_ZIG_IDENTIFIER_SOURCE}/id_start_bitset.meta.blob +) + +register_command( + TARGET + bun-identifier-data + COMMENT + "Generating src/js_lexer/*.blob" + COMMAND + ${CMAKE_ZIG_COMPILER} + run + ${CMAKE_ZIG_FLAGS} + ${BUN_ZIG_IDENTIFIER_SCRIPT} + SOURCES + ${BUN_ZIG_IDENTIFIER_SOURCES} + OUTPUTS + ${BUN_ZIG_IDENTIFIER_OUTPUTS} + TARGETS + clone-zig +) + +set(BUN_ERROR_SOURCE ${CWD}/packages/bun-error) + +file(GLOB BUN_ERROR_SOURCES ${CONFIGURE_DEPENDS} + ${BUN_ERROR_SOURCE}/*.json + ${BUN_ERROR_SOURCE}/*.ts + ${BUN_ERROR_SOURCE}/*.tsx + ${BUN_ERROR_SOURCE}/*.css + ${BUN_ERROR_SOURCE}/img/* +) + +set(BUN_ERROR_OUTPUT ${BUN_ERROR_SOURCE}/dist) +set(BUN_ERROR_OUTPUTS + ${BUN_ERROR_OUTPUT}/index.js + ${BUN_ERROR_OUTPUT}/bun-error.css +) + +register_bun_install( + CWD + ${BUN_ERROR_SOURCE} + NODE_MODULES_VARIABLE + BUN_ERROR_NODE_MODULES +) + +register_command( + TARGET + bun-error + COMMENT + "Building bun-error" + CWD + ${BUN_ERROR_SOURCE} + COMMAND + ${ESBUILD_EXECUTABLE} ${ESBUILD_ARGS} + index.tsx + bun-error.css + --outdir=${BUN_ERROR_OUTPUT} + --define:process.env.NODE_ENV=\"'production'\" + --minify + --bundle + --platform=browser + --format=esm + SOURCES + ${BUN_ERROR_SOURCES} + ${BUN_ERROR_NODE_MODULES} + OUTPUTS + ${BUN_ERROR_OUTPUTS} +) + +set(BUN_FALLBACK_DECODER_SOURCE ${CWD}/src/fallback.ts) +set(BUN_FALLBACK_DECODER_OUTPUT ${CWD}/src/fallback.out.js) + +register_command( + TARGET + bun-fallback-decoder + COMMENT + "Building src/fallback.out.js" + COMMAND + ${ESBUILD_EXECUTABLE} ${ESBUILD_ARGS} + ${BUN_FALLBACK_DECODER_SOURCE} + --outfile=${BUN_FALLBACK_DECODER_OUTPUT} + --target=esnext + --bundle + --format=iife + --platform=browser + --minify + SOURCES + ${BUN_FALLBACK_DECODER_SOURCE} + OUTPUTS + ${BUN_FALLBACK_DECODER_OUTPUT} +) + +set(BUN_RUNTIME_JS_SOURCE ${CWD}/src/runtime.bun.js) +set(BUN_RUNTIME_JS_OUTPUT ${CWD}/src/runtime.out.js) + +register_command( + TARGET + bun-runtime-js + COMMENT + "Building src/runtime.out.js" + COMMAND + ${ESBUILD_EXECUTABLE} ${ESBUILD_ARGS} + ${BUN_RUNTIME_JS_SOURCE} + --outfile=${BUN_RUNTIME_JS_OUTPUT} + --define:process.env.NODE_ENV=\"'production'\" + --target=esnext + --bundle + --format=esm + --platform=node + --minify + --external:/bun:* + SOURCES + ${BUN_RUNTIME_JS_SOURCE} + OUTPUTS + ${BUN_RUNTIME_JS_OUTPUT} +) + +set(BUN_NODE_FALLBACKS_SOURCE ${CWD}/src/node-fallbacks) + +file(GLOB BUN_NODE_FALLBACKS_SOURCES ${CONFIGURE_DEPENDS} + ${BUN_NODE_FALLBACKS_SOURCE}/*.js +) + +set(BUN_NODE_FALLBACKS_OUTPUT ${BUN_NODE_FALLBACKS_SOURCE}/out) +set(BUN_NODE_FALLBACKS_OUTPUTS) +foreach(source ${BUN_NODE_FALLBACKS_SOURCES}) + get_filename_component(filename ${source} NAME) + list(APPEND BUN_NODE_FALLBACKS_OUTPUTS ${BUN_NODE_FALLBACKS_OUTPUT}/${filename}) +endforeach() + +register_bun_install( + CWD + ${BUN_NODE_FALLBACKS_SOURCE} + NODE_MODULES_VARIABLE + BUN_NODE_FALLBACKS_NODE_MODULES +) + +# This command relies on an older version of `esbuild`, which is why +# it uses ${BUN_EXECUTABLE} x instead of ${ESBUILD_EXECUTABLE}. +register_command( + TARGET + bun-node-fallbacks + COMMENT + "Building src/node-fallbacks/*.js" + CWD + ${BUN_NODE_FALLBACKS_SOURCE} + COMMAND + ${BUN_EXECUTABLE} x + esbuild ${ESBUILD_ARGS} + ${BUN_NODE_FALLBACKS_SOURCES} + --outdir=${BUN_NODE_FALLBACKS_OUTPUT} + --format=esm + --minify + --bundle + --platform=browser + SOURCES + ${BUN_NODE_FALLBACKS_SOURCES} + ${BUN_NODE_FALLBACKS_NODE_MODULES} + OUTPUTS + ${BUN_NODE_FALLBACKS_OUTPUTS} +) + +set(BUN_ERROR_CODE_SCRIPT ${CWD}/src/codegen/generate-node-errors.ts) + +set(BUN_ERROR_CODE_SOURCES + ${BUN_ERROR_CODE_SCRIPT} + ${CWD}/src/bun.js/bindings/ErrorCode.ts + ${CWD}/src/bun.js/bindings/ErrorCode.cpp + ${CWD}/src/bun.js/bindings/ErrorCode.h +) + +set(BUN_ERROR_CODE_OUTPUTS + ${CODEGEN_PATH}/ErrorCode+List.h + ${CODEGEN_PATH}/ErrorCode+Data.h + ${CODEGEN_PATH}/ErrorCode.zig +) + +register_command( + TARGET + bun-error-code + COMMENT + "Generating ErrorCode.{zig,h}" + COMMAND + ${BUN_EXECUTABLE} + run + ${BUN_ERROR_CODE_SCRIPT} + ${CODEGEN_PATH} + SOURCES + ${BUN_ERROR_CODE_SOURCES} + OUTPUTS + ${BUN_ERROR_CODE_OUTPUTS} +) + +set(BUN_ZIG_GENERATED_CLASSES_SCRIPT ${CWD}/src/codegen/generate-classes.ts) + +file(GLOB BUN_ZIG_GENERATED_CLASSES_SOURCES ${CONFIGURE_DEPENDS} + ${CWD}/src/bun.js/*.classes.ts + ${CWD}/src/bun.js/api/*.classes.ts + ${CWD}/src/bun.js/node/*.classes.ts + ${CWD}/src/bun.js/test/*.classes.ts + ${CWD}/src/bun.js/webcore/*.classes.ts +) + +set(BUN_ZIG_GENERATED_CLASSES_OUTPUTS + ${CODEGEN_PATH}/ZigGeneratedClasses.h + ${CODEGEN_PATH}/ZigGeneratedClasses.cpp + ${CODEGEN_PATH}/ZigGeneratedClasses+lazyStructureHeader.h + ${CODEGEN_PATH}/ZigGeneratedClasses+DOMClientIsoSubspaces.h + ${CODEGEN_PATH}/ZigGeneratedClasses+DOMIsoSubspaces.h + ${CODEGEN_PATH}/ZigGeneratedClasses+lazyStructureImpl.h + ${CODEGEN_PATH}/ZigGeneratedClasses.zig +) + +register_command( + TARGET + bun-zig-generated-classes + COMMENT + "Generating ZigGeneratedClasses.{zig,cpp,h}" + COMMAND + ${BUN_EXECUTABLE} + run + ${BUN_ZIG_GENERATED_CLASSES_SCRIPT} + ${BUN_ZIG_GENERATED_CLASSES_SOURCES} + ${CODEGEN_PATH} + SOURCES + ${BUN_ZIG_GENERATED_CLASSES_SCRIPT} + ${BUN_ZIG_GENERATED_CLASSES_SOURCES} + OUTPUTS + ${BUN_ZIG_GENERATED_CLASSES_OUTPUTS} +) + +set(BUN_JAVASCRIPT_CODEGEN_SCRIPT ${CWD}/src/codegen/bundle-modules.ts) + +file(GLOB_RECURSE BUN_JAVASCRIPT_SOURCES ${CONFIGURE_DEPENDS} + ${CWD}/src/js/*.js + ${CWD}/src/js/*.ts +) + +file(GLOB BUN_JAVASCRIPT_CODEGEN_SOURCES ${CONFIGURE_DEPENDS} + ${CWD}/src/codegen/*.ts +) + +list(APPEND BUN_JAVASCRIPT_CODEGEN_SOURCES + ${CWD}/src/bun.js/bindings/InternalModuleRegistry.cpp +) + +set(BUN_JAVASCRIPT_OUTPUTS + ${CODEGEN_PATH}/WebCoreJSBuiltins.cpp + ${CODEGEN_PATH}/WebCoreJSBuiltins.h + ${CODEGEN_PATH}/InternalModuleRegistryConstants.h + ${CODEGEN_PATH}/InternalModuleRegistry+createInternalModuleById.h + ${CODEGEN_PATH}/InternalModuleRegistry+enum.h + ${CODEGEN_PATH}/InternalModuleRegistry+numberOfModules.h + ${CODEGEN_PATH}/NativeModuleImpl.h + ${CODEGEN_PATH}/ResolvedSourceTag.zig + ${CODEGEN_PATH}/SyntheticModuleType.h + ${CODEGEN_PATH}/GeneratedJS2Native.h + # Zig will complain if files are outside of the source directory + ${CWD}/src/bun.js/bindings/GeneratedJS2Native.zig +) + +register_command( + TARGET + bun-js-modules + COMMENT + "Generating JavaScript modules" + COMMAND + ${BUN_EXECUTABLE} + run + ${BUN_JAVASCRIPT_CODEGEN_SCRIPT} + --debug=${DEBUG} + ${BUILD_PATH} + SOURCES + ${BUN_JAVASCRIPT_SOURCES} + ${BUN_JAVASCRIPT_CODEGEN_SOURCES} + ${BUN_JAVASCRIPT_CODEGEN_SCRIPT} + OUTPUTS + ${BUN_JAVASCRIPT_OUTPUTS} +) + +set(BUN_KIT_RUNTIME_CODEGEN_SCRIPT ${CWD}/src/codegen/kit-codegen.ts) + +file(GLOB_RECURSE BUN_KIT_RUNTIME_SOURCES ${CONFIGURE_DEPENDS} + ${CWD}/src/kit/*.ts + ${CWD}/src/kit/*/*.ts +) + +list(APPEND BUN_KIT_RUNTIME_CODEGEN_SOURCES + ${CWD}/src/bun.js/bindings/InternalModuleRegistry.cpp +) + +set(BUN_KIT_RUNTIME_OUTPUTS + ${CODEGEN_PATH}/kit_empty_file + ${CODEGEN_PATH}/kit.client.js + ${CODEGEN_PATH}/kit.server.js +) + +register_command( + TARGET + bun-kit-codegen + COMMENT + "Bundling Kit Runtime" + COMMAND + ${BUN_EXECUTABLE} + run + ${BUN_KIT_RUNTIME_CODEGEN_SCRIPT} + --debug=${DEBUG} + --codegen_root=${CODEGEN_PATH} + SOURCES + ${BUN_KIT_RUNTIME_SOURCES} + ${BUN_KIT_RUNTIME_CODEGEN_SOURCES} + ${BUN_KIT_RUNTIME_CODEGEN_SCRIPT} + OUTPUTS + ${BUN_KIT_RUNTIME_OUTPUTS} +) + +set(BUN_JS_SINK_SCRIPT ${CWD}/src/codegen/generate-jssink.ts) + +set(BUN_JS_SINK_SOURCES + ${BUN_JS_SINK_SCRIPT} + ${CWD}/src/codegen/create-hash-table.ts +) + +set(BUN_JS_SINK_OUTPUTS + ${CODEGEN_PATH}/JSSink.cpp + ${CODEGEN_PATH}/JSSink.h + ${CODEGEN_PATH}/JSSink.lut.h +) + +register_command( + TARGET + bun-js-sink + COMMENT + "Generating JSSink.{cpp,h}" + COMMAND + ${BUN_EXECUTABLE} + run + ${BUN_JS_SINK_SCRIPT} + ${CODEGEN_PATH} + SOURCES + ${BUN_JS_SINK_SOURCES} + OUTPUTS + ${BUN_JS_SINK_OUTPUTS} +) + +set(BUN_OBJECT_LUT_SCRIPT ${CWD}/src/codegen/create-hash-table.ts) + +set(BUN_OBJECT_LUT_SOURCES + ${CWD}/src/bun.js/bindings/BunObject.cpp + ${CWD}/src/bun.js/bindings/ZigGlobalObject.lut.txt + ${CWD}/src/bun.js/bindings/JSBuffer.cpp + ${CWD}/src/bun.js/bindings/BunProcess.cpp + ${CWD}/src/bun.js/bindings/ProcessBindingConstants.cpp + ${CWD}/src/bun.js/bindings/ProcessBindingNatives.cpp +) + +set(BUN_OBJECT_LUT_OUTPUTS + ${CODEGEN_PATH}/BunObject.lut.h + ${CODEGEN_PATH}/ZigGlobalObject.lut.h + ${CODEGEN_PATH}/JSBuffer.lut.h + ${CODEGEN_PATH}/BunProcess.lut.h + ${CODEGEN_PATH}/ProcessBindingConstants.lut.h + ${CODEGEN_PATH}/ProcessBindingNatives.lut.h +) + +macro(WEBKIT_ADD_SOURCE_DEPENDENCIES _source _deps) + set(_tmp) + get_source_file_property(_tmp ${_source} OBJECT_DEPENDS) + + if(NOT _tmp) + set(_tmp "") + endif() + + foreach(f ${_deps}) + list(APPEND _tmp "${f}") + endforeach() + + set_source_files_properties(${_source} PROPERTIES OBJECT_DEPENDS "${_tmp}") + unset(_tmp) +endmacro() + +list(LENGTH BUN_OBJECT_LUT_SOURCES BUN_OBJECT_LUT_SOURCES_COUNT) +math(EXPR BUN_OBJECT_LUT_SOURCES_MAX_INDEX "${BUN_OBJECT_LUT_SOURCES_COUNT} - 1") + +foreach(i RANGE 0 ${BUN_OBJECT_LUT_SOURCES_MAX_INDEX}) + list(GET BUN_OBJECT_LUT_SOURCES ${i} BUN_OBJECT_LUT_SOURCE) + list(GET BUN_OBJECT_LUT_OUTPUTS ${i} BUN_OBJECT_LUT_OUTPUT) + + get_filename_component(filename ${BUN_OBJECT_LUT_SOURCE} NAME_WE) + register_command( + TARGET + bun-codegen-lut-${filename} + COMMENT + "Generating ${filename}.lut.h" + COMMAND + ${BUN_EXECUTABLE} + run + ${BUN_OBJECT_LUT_SCRIPT} + ${BUN_OBJECT_LUT_SOURCE} + ${BUN_OBJECT_LUT_OUTPUT} + SOURCES + ${BUN_OBJECT_LUT_SCRIPT} + ${BUN_OBJECT_LUT_SOURCE} + OUTPUTS + ${BUN_OBJECT_LUT_OUTPUT} + ) + + WEBKIT_ADD_SOURCE_DEPENDENCIES(${BUN_OBJECT_LUT_SOURCE} ${BUN_OBJECT_LUT_OUTPUT}) +endforeach() + +WEBKIT_ADD_SOURCE_DEPENDENCIES( + ${CWD}/src/bun.js/bindings/ErrorCode.cpp + ${CODEGEN_PATH}/ErrorCode+List.h +) + +WEBKIT_ADD_SOURCE_DEPENDENCIES( + ${CWD}/src/bun.js/bindings/ErrorCode.h + ${CODEGEN_PATH}/ErrorCode+Data.h +) + +WEBKIT_ADD_SOURCE_DEPENDENCIES( + ${CWD}/src/bun.js/bindings/ZigGlobalObject.cpp + ${CODEGEN_PATH}/ZigGlobalObject.lut.h +) + +WEBKIT_ADD_SOURCE_DEPENDENCIES( + ${CWD}/src/bun.js/bindings/InternalModuleRegistry.cpp + ${CODEGEN_PATH}/InternalModuleRegistryConstants.h +) + +# --- Zig --- + +file(GLOB_RECURSE BUN_ZIG_SOURCES ${CONFIGURE_DEPENDS} + ${CWD}/src/*.zig +) + +list(APPEND BUN_ZIG_SOURCES + ${CWD}/build.zig + ${CWD}/root.zig + ${CWD}/root_wasm.zig + ${BUN_ZIG_IDENTIFIER_OUTPUTS} + ${BUN_ERROR_OUTPUTS} + ${BUN_FALLBACK_DECODER_OUTPUT} + ${BUN_RUNTIME_JS_OUTPUT} + ${BUN_NODE_FALLBACKS_OUTPUTS} + ${BUN_ERROR_CODE_OUTPUTS} + ${BUN_ZIG_GENERATED_CLASSES_OUTPUTS} + ${BUN_JAVASCRIPT_OUTPUTS} +) + +# In debug builds, these are not embedded, but rather referenced at runtime. +if (DEBUG) + list(APPEND BUN_ZIG_SOURCES ${CODEGEN_PATH}/kit_empty_file) +else() + list(APPEND BUN_ZIG_SOURCES ${BUN_KIT_RUNTIME_OUTPUTS}) +endif() + +set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.o) + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64") + if(APPLE) + set(ZIG_CPU "apple_m1") + else() + set(ZIG_CPU "native") + endif() +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64") + if(ENABLE_BASELINE) + set(ZIG_CPU "nehalem") + else() + set(ZIG_CPU "haswell") + endif() +else() + unsupported(CMAKE_SYSTEM_PROCESSOR) +endif() + +register_command( + TARGET + bun-zig + GROUP + console + COMMENT + "Building src/*.zig for ${ZIG_TARGET}" + COMMAND + ${CMAKE_ZIG_COMPILER} + build obj + ${CMAKE_ZIG_FLAGS} + --prefix ${BUILD_PATH} + -Dobj_format=${ZIG_OBJECT_FORMAT} + -Dtarget=${ZIG_TARGET} + -Doptimize=${ZIG_OPTIMIZE} + -Dcpu=${ZIG_CPU} + -Denable_logs=$,true,false> + -Dversion=${VERSION} + -Dsha=${REVISION} + -Dreported_nodejs_version=${NODEJS_VERSION} + -Dcanary=${CANARY_REVISION} + -Dgenerated-code=${CODEGEN_PATH} + ARTIFACTS + ${BUN_ZIG_OUTPUT} + SOURCES + ${BUN_ZIG_SOURCES} + TARGETS + clone-zig +) + +set_property(TARGET bun-zig PROPERTY JOB_POOL compile_pool) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "build.zig") + +# --- C/C++ Sources --- + +set(BUN_USOCKETS_SOURCE ${CWD}/packages/bun-usockets) + +file(GLOB BUN_CXX_SOURCES ${CONFIGURE_DEPENDS} + ${CWD}/src/io/*.cpp + ${CWD}/src/bun.js/modules/*.cpp + ${CWD}/src/bun.js/bindings/*.cpp + ${CWD}/src/bun.js/bindings/webcore/*.cpp + ${CWD}/src/bun.js/bindings/sqlite/*.cpp + ${CWD}/src/bun.js/bindings/webcrypto/*.cpp + ${CWD}/src/bun.js/bindings/webcrypto/*/*.cpp + ${CWD}/src/bun.js/bindings/v8/*.cpp + ${CWD}/src/kit/*.cpp + ${CWD}/src/deps/*.cpp + ${BUN_USOCKETS_SOURCE}/src/crypto/*.cpp +) + +file(GLOB BUN_C_SOURCES ${CONFIGURE_DEPENDS} + ${BUN_USOCKETS_SOURCE}/src/*.c + ${BUN_USOCKETS_SOURCE}/src/eventing/*.c + ${BUN_USOCKETS_SOURCE}/src/internal/*.c + ${BUN_USOCKETS_SOURCE}/src/crypto/*.c +) + +register_repository( + NAME + picohttpparser + REPOSITORY + h2o/picohttpparser + COMMIT + 066d2b1e9ab820703db0837a7255d92d30f0c9f5 + OUTPUTS + picohttpparser.c +) + +list(APPEND BUN_C_SOURCES ${VENDOR_PATH}/picohttpparser/picohttpparser.c) + +if(WIN32) + list(APPEND BUN_C_SOURCES ${CWD}/src/bun.js/bindings/windows/musl-memmem.c) +endif() + +list(APPEND BUN_CPP_SOURCES + ${BUN_C_SOURCES} + ${BUN_CXX_SOURCES} + ${BUN_ZIG_GENERATED_CLASSES_OUTPUTS} + ${BUN_JS_SINK_OUTPUTS} + ${BUN_JAVASCRIPT_OUTPUTS} + ${BUN_OBJECT_LUT_OUTPUTS} +) + +if(WIN32) + if(ENABLE_CANARY) + set(Bun_VERSION_WITH_TAG ${VERSION}-canary.${CANARY_REVISION}) + else() + set(Bun_VERSION_WITH_TAG ${VERSION}) + endif() + set(BUN_ICO_PATH ${CWD}/src/bun.ico) + configure_file( + ${CWD}/src/windows-app-info.rc + ${CODEGEN_PATH}/windows-app-info.rc + ) + list(APPEND BUN_CPP_SOURCES ${CODEGEN_PATH}/windows-app-info.rc) +endif() + +# --- Executable --- + +set(BUN_CPP_OUTPUT ${BUILD_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${bun}${CMAKE_STATIC_LIBRARY_SUFFIX}) + +if(BUN_LINK_ONLY) + add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT}) + set_target_properties(${bun} PROPERTIES LINKER_LANGUAGE CXX) + target_link_libraries(${bun} PRIVATE ${BUN_CPP_OUTPUT}) +elseif(BUN_CPP_ONLY) + add_library(${bun} STATIC ${BUN_CPP_SOURCES}) + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Uploading ${bun}" + COMMAND + ${CMAKE_COMMAND} -E true + ARTIFACTS + ${BUN_CPP_OUTPUT} + ) +else() + add_executable(${bun} ${BUN_CPP_SOURCES}) + target_link_libraries(${bun} PRIVATE ${BUN_ZIG_OUTPUT}) +endif() + +if(NOT bun STREQUAL "bun") + add_custom_target(bun DEPENDS ${bun}) +endif() + +# --- C/C++ Properties --- + +set_target_properties(${bun} PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS YES + CXX_VISIBILITY_PRESET hidden + C_STANDARD 17 + C_STANDARD_REQUIRED YES + VISIBILITY_INLINES_HIDDEN YES +) + +# --- C/C++ Includes --- + +if(WIN32) + target_include_directories(${bun} PRIVATE ${CWD}/src/bun.js/bindings/windows) +endif() + +target_include_directories(${bun} PRIVATE + ${CWD}/packages + ${CWD}/packages/bun-usockets + ${CWD}/packages/bun-usockets/src + ${CWD}/src/bun.js/bindings + ${CWD}/src/bun.js/bindings/webcore + ${CWD}/src/bun.js/bindings/webcrypto + ${CWD}/src/bun.js/bindings/sqlite + ${CWD}/src/bun.js/modules + ${CWD}/src/js/builtins + ${CWD}/src/napi + ${CWD}/src/deps + ${CODEGEN_PATH} + ${VENDOR_PATH} + ${VENDOR_PATH}/picohttpparser +) + +# --- C/C++ Definitions --- + +if(ENABLE_ASSERTIONS) + target_compile_definitions(${bun} PRIVATE ASSERT_ENABLED=1) +endif() + +if(DEBUG) + target_compile_definitions(${bun} PRIVATE BUN_DEBUG=1) +endif() + +if(APPLE) + target_compile_definitions(${bun} PRIVATE _DARWIN_NON_CANCELABLE=1) +endif() + +if(WIN32) + target_compile_definitions(${bun} PRIVATE + WIN32 + _WINDOWS + WIN32_LEAN_AND_MEAN=1 + _CRT_SECURE_NO_WARNINGS + BORINGSSL_NO_CXX=1 # lol + ) +endif() + +target_compile_definitions(${bun} PRIVATE + _HAS_EXCEPTIONS=0 + LIBUS_USE_OPENSSL=1 + LIBUS_USE_BORINGSSL=1 + WITH_BORINGSSL=1 + STATICALLY_LINKED_WITH_JavaScriptCore=1 + STATICALLY_LINKED_WITH_BMALLOC=1 + BUILDING_WITH_CMAKE=1 + JSC_OBJC_API_ENABLED=0 + BUN_SINGLE_THREADED_PER_VM_ENTRY_SCOPE=1 + NAPI_EXPERIMENTAL=ON + NOMINMAX + IS_BUILD + BUILDING_JSCONLY__ + REPORTED_NODEJS_VERSION=\"${NODEJS_VERSION}\" + REPORTED_NODEJS_ABI_VERSION=${NODEJS_ABI_VERSION} +) + +if(DEBUG AND NOT CI) + target_compile_definitions(${bun} PRIVATE + BUN_DYNAMIC_JS_LOAD_PATH=\"${BUILD_PATH}/js\" + ) +endif() + + +# --- Compiler options --- + +if(NOT WIN32) + target_compile_options(${bun} PUBLIC + -fconstexpr-steps=2542484 + -fconstexpr-depth=54 + -fno-pic + -fno-pie + -faddrsig + ) + if(DEBUG) + target_compile_options(${bun} PUBLIC + -Werror=return-type + -Werror=return-stack-address + -Werror=implicit-function-declaration + -Werror=uninitialized + -Werror=conditional-uninitialized + -Werror=suspicious-memaccess + -Werror=int-conversion + -Werror=nonnull + -Werror=move + -Werror=sometimes-uninitialized + -Werror=unused + -Wno-unused-function + -Wno-nullability-completeness + -Werror + -fsanitize=null + -fsanitize-recover=all + -fsanitize=bounds + -fsanitize=return + -fsanitize=nullability-arg + -fsanitize=nullability-assign + -fsanitize=nullability-return + -fsanitize=returns-nonnull-attribute + -fsanitize=unreachable + ) + target_link_libraries(${bun} PRIVATE -fsanitize=null) + else() + # Leave -Werror=unused off in release builds so we avoid errors from being used in ASSERT + target_compile_options(${bun} PUBLIC ${LTO_FLAG} + -Werror=return-type + -Werror=return-stack-address + -Werror=implicit-function-declaration + -Werror=uninitialized + -Werror=conditional-uninitialized + -Werror=suspicious-memaccess + -Werror=int-conversion + -Werror=nonnull + -Werror=move + -Werror=sometimes-uninitialized + -Wno-nullability-completeness + -Werror + ) + endif() +endif() + +# --- Linker options --- + +if(WIN32) + target_link_options(${bun} PUBLIC + /STACK:0x1200000,0x100000 + /errorlimit:0 + ) + if(RELEASE) + target_link_options(${bun} PUBLIC + /LTCG + /OPT:REF + /OPT:NOICF + /DEBUG:FULL + /delayload:ole32.dll + /delayload:WINMM.dll + /delayload:dbghelp.dll + /delayload:VCRUNTIME140_1.dll + # libuv loads these two immediately, but for some reason it seems to still be slightly faster to delayload them + /delayload:WS2_32.dll + /delayload:WSOCK32.dll + /delayload:ADVAPI32.dll + /delayload:IPHLPAPI.dll + ) + endif() +elseif(APPLE) + target_link_options(${bun} PUBLIC + -dead_strip + -dead_strip_dylibs + -Wl,-stack_size,0x1200000 + -fno-keep-static-consts + ) +else() + target_link_options(${bun} PUBLIC + -fuse-ld=lld-${LLVM_VERSION_MAJOR} + -fno-pic + -static-libstdc++ + -static-libgcc + -Wl,-no-pie + -Wl,-icf=safe + -Wl,--as-needed + -Wl,--gc-sections + -Wl,-z,stack-size=12800000 + -Wl,--wrap=fcntl + -Wl,--wrap=fcntl64 + -Wl,--wrap=stat64 + -Wl,--wrap=pow + -Wl,--wrap=exp + -Wl,--wrap=expf + -Wl,--wrap=log + -Wl,--wrap=log2 + -Wl,--wrap=lstat + -Wl,--wrap=stat64 + -Wl,--wrap=stat + -Wl,--wrap=fstat + -Wl,--wrap=fstatat + -Wl,--wrap=lstat64 + -Wl,--wrap=fstat64 + -Wl,--wrap=fstatat64 + -Wl,--wrap=mknod + -Wl,--wrap=mknodat + -Wl,--wrap=statx + -Wl,--wrap=fmod + -Wl,--compress-debug-sections=zlib + -Wl,-z,lazy + -Wl,-z,norelro + ) +endif() + +# --- Symbols list --- + +if(WIN32) + set(BUN_SYMBOLS_PATH ${CWD}/src/symbols.def) + target_link_options(${bun} PUBLIC /DEF:${BUN_SYMBOLS_PATH}) +elseif(APPLE) + set(BUN_SYMBOLS_PATH ${CWD}/src/symbols.txt) + target_link_options(${bun} PUBLIC -exported_symbols_list ${BUN_SYMBOLS_PATH}) +else() + set(BUN_SYMBOLS_PATH ${CWD}/src/symbols.dyn) + set(BUN_LINKER_LDS_PATH ${CWD}/src/linker.lds) + target_link_options(${bun} PUBLIC + -Bsymbolics-functions + -rdynamic + -Wl,--dynamic-list=${BUN_SYMBOLS_PATH} + -Wl,--version-script=${BUN_LINKER_LDS_PATH} + ) + set_target_properties(${bun} PROPERTIES LINK_DEPENDS ${BUN_LINKER_LDS_PATH}) +endif() + +set_target_properties(${bun} PROPERTIES LINK_DEPENDS ${BUN_SYMBOLS_PATH}) + +# --- WebKit --- + +include(SetupWebKit) + +if(WIN32) + if(DEBUG) + target_link_libraries(${bun} PRIVATE + ${WEBKIT_LIB_PATH}/WTF.lib + ${WEBKIT_LIB_PATH}/JavaScriptCore.lib + ${WEBKIT_LIB_PATH}/sicudtd.lib + ${WEBKIT_LIB_PATH}/sicuind.lib + ${WEBKIT_LIB_PATH}/sicuucd.lib + ) + else() + target_link_libraries(${bun} PRIVATE + ${WEBKIT_LIB_PATH}/WTF.lib + ${WEBKIT_LIB_PATH}/JavaScriptCore.lib + ${WEBKIT_LIB_PATH}/sicudt.lib + ${WEBKIT_LIB_PATH}/sicuin.lib + ${WEBKIT_LIB_PATH}/sicuuc.lib + ) + endif() +else() + target_link_libraries(${bun} PRIVATE + ${WEBKIT_LIB_PATH}/libWTF.a + ${WEBKIT_LIB_PATH}/libJavaScriptCore.a + ) + if(NOT APPLE OR EXISTS ${WEBKIT_LIB_PATH}/libbmalloc.a) + target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libbmalloc.a) + endif() +endif() + +include_directories(${WEBKIT_INCLUDE_PATH}) + +if(NOT WEBKIT_LOCAL AND NOT APPLE) + include_directories(${WEBKIT_INCLUDE_PATH}/wtf/unicode) +endif() + +# --- Dependencies --- + +set(BUN_DEPENDENCIES + BoringSSL + Brotli + Cares + LibDeflate + LolHtml + Lshpack + Mimalloc + TinyCC + Zlib + LibArchive # must be loaded after zlib + Zstd +) + +if(WIN32) + list(APPEND BUN_DEPENDENCIES Libuv) +endif() + +if(USE_STATIC_SQLITE) + list(APPEND BUN_DEPENDENCIES SQLite) +endif() + +foreach(dependency ${BUN_DEPENDENCIES}) + include(Build${dependency}) +endforeach() + +list(TRANSFORM BUN_DEPENDENCIES TOLOWER OUTPUT_VARIABLE BUN_TARGETS) +add_custom_target(dependencies DEPENDS ${BUN_TARGETS}) + +if(APPLE) + target_link_libraries(${bun} PRIVATE icucore resolv) +endif() + +if(USE_STATIC_SQLITE) + target_compile_definitions(${bun} PRIVATE LAZY_LOAD_SQLITE=0) +else() + target_compile_definitions(${bun} PRIVATE LAZY_LOAD_SQLITE=1) +endif() + +if(LINUX) + target_link_libraries(${bun} PRIVATE c pthread dl) + + if(USE_STATIC_LIBATOMIC) + target_link_libraries(${bun} PRIVATE libatomic.a) + else() + target_link_libraries(${bun} PUBLIC libatomic.so) + endif() + + if(USE_SYSTEM_ICU) + target_link_libraries(${bun} PRIVATE libicudata.a) + target_link_libraries(${bun} PRIVATE libicui18n.a) + target_link_libraries(${bun} PRIVATE libicuuc.a) + else() + target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a) + target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a) + target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a) + endif() +endif() + +if(WIN32) + target_link_libraries(${bun} PRIVATE + winmm + bcrypt + ntdll + userenv + dbghelp + wsock32 # ws2_32 required by TransmitFile aka sendfile on windows + delayimp.lib + ) +endif() + +# --- Packaging --- + +if(NOT BUN_CPP_ONLY) + if(bunStrip) + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Stripping ${bun}" + COMMAND + ${CMAKE_STRIP} + ${bunExe} + --strip-all + --strip-debug + --discard-all + -o ${bunStripExe} + CWD + ${BUILD_PATH} + OUTPUTS + ${BUILD_PATH}/${bunStripExe} + ) + endif() + + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Testing ${bun}" + COMMAND + ${CMAKE_COMMAND} + -E env BUN_DEBUG_QUIET_LOGS=1 + ${BUILD_PATH}/${bunExe} + --revision + CWD + ${BUILD_PATH} + ) + + if(CI) + set(BUN_FEATURES_SCRIPT ${CWD}/scripts/features.mjs) + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Generating features.json" + COMMAND + ${CMAKE_COMMAND} + -E env + BUN_GARBAGE_COLLECTOR_LEVEL=1 + BUN_DEBUG_QUIET_LOGS=1 + BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING=1 + ${BUILD_PATH}/${bunExe} + ${BUN_FEATURES_SCRIPT} + CWD + ${BUILD_PATH} + ARTIFACTS + ${BUILD_PATH}/features.json + ) + endif() + + if(CMAKE_HOST_APPLE AND bunStrip) + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Generating ${bun}.dSYM" + COMMAND + ${CMAKE_DSYMUTIL} + ${bun} + --flat + --keep-function-for-static + --object-prefix-map .=${CWD} + -o ${bun}.dSYM + -j ${CMAKE_BUILD_PARALLEL_LEVEL} + CWD + ${BUILD_PATH} + OUTPUTS + ${BUILD_PATH}/${bun}.dSYM + ) + endif() + + if(CI) + if(ENABLE_BASELINE) + set(bunTriplet bun-${OS}-${ARCH}-baseline) + else() + set(bunTriplet bun-${OS}-${ARCH}) + endif() + string(REPLACE bun ${bunTriplet} bunPath ${bun}) + set(bunFiles ${bunExe} features.json) + if(WIN32) + list(APPEND bunFiles ${bun}.pdb) + elseif(APPLE) + list(APPEND bunFiles ${bun}.dSYM) + endif() + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Generating ${bunPath}.zip" + COMMAND + ${CMAKE_COMMAND} -E rm -rf ${bunPath} ${bunPath}.zip + && ${CMAKE_COMMAND} -E make_directory ${bunPath} + && ${CMAKE_COMMAND} -E copy ${bunFiles} ${bunPath} + && ${CMAKE_COMMAND} -E tar cfv ${bunPath}.zip --format=zip ${bunPath} + && ${CMAKE_COMMAND} -E rm -rf ${bunPath} + CWD + ${BUILD_PATH} + ARTIFACTS + ${BUILD_PATH}/${bunPath}.zip + ) + + if(bunStrip) + string(REPLACE bun ${bunTriplet} bunStripPath ${bunStrip}) + register_command( + TARGET + ${bun} + TARGET_PHASE + POST_BUILD + COMMENT + "Generating ${bunStripPath}.zip" + COMMAND + ${CMAKE_COMMAND} -E rm -rf ${bunStripPath} ${bunStripPath}.zip + && ${CMAKE_COMMAND} -E make_directory ${bunStripPath} + && ${CMAKE_COMMAND} -E copy ${bunStripExe} ${bunStripPath} + && ${CMAKE_COMMAND} -E tar cfv ${bunStripPath}.zip --format=zip ${bunStripPath} + && ${CMAKE_COMMAND} -E rm -rf ${bunStripPath} + CWD + ${BUILD_PATH} + ARTIFACTS + ${BUILD_PATH}/${bunStripPath}.zip + ) + endif() + endif() +endif() diff --git a/cmake/targets/BuildCares.cmake b/cmake/targets/BuildCares.cmake new file mode 100644 index 0000000000000..9a3f0b9ef0faf --- /dev/null +++ b/cmake/targets/BuildCares.cmake @@ -0,0 +1,27 @@ +register_repository( + NAME + cares + REPOSITORY + c-ares/c-ares + COMMIT + d1722e6e8acaf10eb73fa995798a9cd421d9f85e +) + +register_cmake_command( + TARGET + cares + TARGETS + c-ares + ARGS + -DCARES_STATIC=ON + -DCARES_STATIC_PIC=ON # FORCE_PIC was set to 1, but CARES_STATIC_PIC was set to OFF?? + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCARES_SHARED=OFF + -DCARES_BUILD_TOOLS=OFF # this was set to ON? + LIB_PATH + lib + LIBRARIES + cares + INCLUDES + include +) diff --git a/cmake/targets/BuildLibArchive.cmake b/cmake/targets/BuildLibArchive.cmake new file mode 100644 index 0000000000000..e0cffd020be57 --- /dev/null +++ b/cmake/targets/BuildLibArchive.cmake @@ -0,0 +1,53 @@ +register_repository( + NAME + libarchive + REPOSITORY + libarchive/libarchive + COMMIT + 898dc8319355b7e985f68a9819f182aaed61b53a +) + +register_cmake_command( + TARGET + libarchive + TARGETS + archive_static + ARGS + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DBUILD_SHARED_LIBS=OFF + -DENABLE_INSTALL=OFF + -DENABLE_TEST=OFF + -DENABLE_WERROR=OFF + -DENABLE_BZIP2=OFF + -DENABLE_CAT=OFF + -DENABLE_EXPAT=OFF + -DENABLE_ICONV=OFF + -DENABLE_LIBB2=OFF + -DENABLE_LibGCC=OFF + -DENABLE_LIBXML2=OFF + -DENABLE_LZ4=OFF + -DENABLE_LZMA=OFF + -DENABLE_LZO=OFF + -DENABLE_MBEDTLS=OFF + -DENABLE_NETTLE=OFF + -DENABLE_OPENSSL=OFF + -DENABLE_PCRE2POSIX=OFF + -DENABLE_PCREPOSIX=OFF + -DENABLE_ZSTD=OFF + # libarchive depends on zlib headers, otherwise it will + # spawn a processes to compress instead of using the library. + -DENABLE_ZLIB=OFF + -DHAVE_ZLIB_H=ON + -DCMAKE_C_FLAGS="-I${VENDOR_PATH}/zlib" + LIB_PATH + libarchive + LIBRARIES + archive + INCLUDES + include +) + +# Must be loaded after zlib is defined +if(TARGET clone-zlib) + add_dependencies(libarchive clone-zlib) +endif() diff --git a/cmake/targets/BuildLibDeflate.cmake b/cmake/targets/BuildLibDeflate.cmake new file mode 100644 index 0000000000000..3faf5963a7ebe --- /dev/null +++ b/cmake/targets/BuildLibDeflate.cmake @@ -0,0 +1,24 @@ +register_repository( + NAME + libdeflate + REPOSITORY + ebiggers/libdeflate + COMMIT + dc76454a39e7e83b68c3704b6e3784654f8d5ac5 +) + +register_cmake_command( + TARGET + libdeflate + TARGETS + libdeflate_static + ARGS + -DLIBDEFLATE_BUILD_STATIC_LIB=ON + -DLIBDEFLATE_BUILD_SHARED_LIB=OFF + -DLIBDEFLATE_BUILD_GZIP=OFF + LIBRARIES + deflatestatic WIN32 + deflate UNIX + INCLUDES + . +) diff --git a/cmake/targets/BuildLibuv.cmake b/cmake/targets/BuildLibuv.cmake new file mode 100644 index 0000000000000..feba612c4424a --- /dev/null +++ b/cmake/targets/BuildLibuv.cmake @@ -0,0 +1,29 @@ +register_repository( + NAME + libuv + REPOSITORY + libuv/libuv + COMMIT + da527d8d2a908b824def74382761566371439003 +) + +if(WIN32) + set(LIBUV_CMAKE_C_FLAGS "/DWIN32 /D_WINDOWS -Wno-int-conversion") +endif() + +register_cmake_command( + TARGET + libuv + TARGETS + uv_a + ARGS + -DLIBUV_BUILD_SHARED=OFF + -DLIBUV_BUILD_TESTS=OFF + -DLIBUV_BUILD_BENCH=OFF + -DCMAKE_C_FLAGS=${LIBUV_CMAKE_C_FLAGS} + LIBRARIES + libuv WIN32 + uv UNIX + INCLUDES + include +) diff --git a/cmake/targets/BuildLolHtml.cmake b/cmake/targets/BuildLolHtml.cmake new file mode 100644 index 0000000000000..9a02362723960 --- /dev/null +++ b/cmake/targets/BuildLolHtml.cmake @@ -0,0 +1,45 @@ +register_repository( + NAME + lolhtml + REPOSITORY + cloudflare/lol-html + COMMIT + 8d4c273ded322193d017042d1f48df2766b0f88b +) + +set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api) +set(LOLHTML_BUILD_PATH ${BUILD_PATH}/lolhtml) + +if(DEBUG) + set(LOLHTML_BUILD_TYPE debug) +else() + set(LOLHTML_BUILD_TYPE release) +endif() + +set(LOLHTML_LIBRARY ${LOLHTML_BUILD_PATH}/${LOLHTML_BUILD_TYPE}/${CMAKE_STATIC_LIBRARY_PREFIX}lolhtml${CMAKE_STATIC_LIBRARY_SUFFIX}) + +set(LOLHTML_BUILD_ARGS + --target-dir ${BUILD_PATH}/lolhtml +) + +if(RELEASE) + list(APPEND LOLHTML_BUILD_ARGS --release) +endif() + +register_command( + TARGET + lolhtml + CWD + ${LOLHTML_CWD} + COMMAND + ${CARGO_EXECUTABLE} + build + ${LOLHTML_BUILD_ARGS} + ARTIFACTS + ${LOLHTML_LIBRARY} +) + +target_link_libraries(${bun} PRIVATE ${LOLHTML_LIBRARY}) +if(BUN_LINK_ONLY) + target_sources(${bun} PRIVATE ${LOLHTML_LIBRARY}) +endif() diff --git a/cmake/targets/BuildLshpack.cmake b/cmake/targets/BuildLshpack.cmake new file mode 100644 index 0000000000000..c1cbb12ff5d6d --- /dev/null +++ b/cmake/targets/BuildLshpack.cmake @@ -0,0 +1,33 @@ +register_repository( + NAME + lshpack + REPOSITORY + litespeedtech/ls-hpack + COMMIT + 3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0 +) + +if(WIN32) + set(LSHPACK_INCLUDES . compat/queue) +else() + set(LSHPACK_INCLUDES .) +endif() + +register_cmake_command( + TARGET + lshpack + LIBRARIES + ls-hpack + ARGS + -DSHARED=OFF + -DLSHPACK_XXH=ON + # There are linking errors when built with non-Release + # Undefined symbols for architecture arm64: + # "___asan_handle_no_return", referenced from: + # _lshpack_enc_get_static_nameval in libls-hpack.a(lshpack.c.o) + # _lshpack_enc_get_static_name in libls-hpack.a(lshpack.c.o) + # _update_hash in libls-hpack.a(lshpack.c.o) + -DCMAKE_BUILD_TYPE=Release + INCLUDES + ${LSHPACK_INCLUDES} +) diff --git a/cmake/targets/BuildMimalloc.cmake b/cmake/targets/BuildMimalloc.cmake new file mode 100644 index 0000000000000..2161e20603624 --- /dev/null +++ b/cmake/targets/BuildMimalloc.cmake @@ -0,0 +1,60 @@ +register_repository( + NAME + mimalloc + REPOSITORY + oven-sh/mimalloc + COMMIT + 4c283af60cdae205df5a872530c77e2a6a307d43 +) + +set(MIMALLOC_CMAKE_ARGS + -DMI_BUILD_STATIC=ON + -DMI_BUILD_OBJECT=ON + -DMI_BUILD_SHARED=OFF + -DMI_BUILD_TESTS=OFF + -DMI_USE_CXX=ON + -DMI_OVERRIDE=OFF + -DMI_OSX_ZONE=OFF + -DMI_OSX_INTERPOSE=OFF + -DMI_SKIP_COLLECT_ON_EXIT=ON +) + +if(DEBUG) + list(APPEND MIMALLOC_CMAKE_ARGS -DMI_DEBUG_FULL=ON) +endif() + +if(ENABLE_VALGRIND) + list(APPEND MIMALLOC_CMAKE_ARGS -DMI_VALGRIND=ON) +endif() + +if(WIN32) + if(DEBUG) + set(MIMALLOC_LIBRARY mimalloc-static-debug) + else() + set(MIMALLOC_LIBRARY mimalloc-static) + endif() +elseif(DEBUG) + set(MIMALLOC_LIBRARY mimalloc-debug) +else() + set(MIMALLOC_LIBRARY mimalloc) +endif() + +# Workaround for linker issue on macOS and Linux x64 +# https://github.com/microsoft/mimalloc/issues/512 +if(APPLE OR (LINUX AND NOT DEBUG)) + set(MIMALLOC_LIBRARY CMakeFiles/mimalloc-obj.dir/src/static.c.o) +endif() + +register_cmake_command( + TARGET + mimalloc + TARGETS + mimalloc-static + mimalloc-obj + ARGS + ${MIMALLOC_CMAKE_ARGS} + LIBRARIES + ${MIMALLOC_LIBRARY} + INCLUDES + include +) diff --git a/cmake/targets/BuildSQLite.cmake b/cmake/targets/BuildSQLite.cmake new file mode 100644 index 0000000000000..ce4cd8da24db7 --- /dev/null +++ b/cmake/targets/BuildSQLite.cmake @@ -0,0 +1,10 @@ +register_cmake_command( + TARGET + sqlite + CWD + ${CWD}/src/bun.js/bindings/sqlite + LIBRARIES + sqlite3 + INCLUDES + . +) diff --git a/cmake/targets/BuildTinyCC.cmake b/cmake/targets/BuildTinyCC.cmake new file mode 100644 index 0000000000000..050eac46137b7 --- /dev/null +++ b/cmake/targets/BuildTinyCC.cmake @@ -0,0 +1,15 @@ +register_repository( + NAME + tinycc + REPOSITORY + oven-sh/tinycc + COMMIT + 29985a3b59898861442fa3b43f663fc1af2591d7 +) + +register_cmake_command( + TARGET + tinycc + LIBRARIES + tcc +) diff --git a/cmake/targets/BuildZlib.cmake b/cmake/targets/BuildZlib.cmake new file mode 100644 index 0000000000000..1940bb2e33907 --- /dev/null +++ b/cmake/targets/BuildZlib.cmake @@ -0,0 +1,40 @@ +register_repository( + NAME + zlib + REPOSITORY + cloudflare/zlib + COMMIT + 886098f3f339617b4243b286f5ed364b9989e245 +) + +# https://gitlab.kitware.com/cmake/cmake/-/issues/25755 +if(APPLE) + set(ZLIB_CMAKE_C_FLAGS "-fno-define-target-os-macros") + set(ZLIB_CMAKE_CXX_FLAGS "-fno-define-target-os-macros") +endif() + +if(WIN32) + if(DEBUG) + set(ZLIB_LIBRARY "zlibd") + else() + set(ZLIB_LIBRARY "zlib") + endif() +else() + set(ZLIB_LIBRARY "z") +endif() + +register_cmake_command( + TARGET + zlib + TARGETS + zlib + ARGS + -DBUILD_SHARED_LIBS=OFF + -DBUILD_EXAMPLES=OFF + "-DCMAKE_C_FLAGS=${ZLIB_CMAKE_C_FLAGS}" + "-DCMAKE_CXX_FLAGS=${ZLIB_CMAKE_CXX_FLAGS}" + LIBRARIES + ${ZLIB_LIBRARY} + INCLUDES + . +) diff --git a/cmake/targets/BuildZstd.cmake b/cmake/targets/BuildZstd.cmake new file mode 100644 index 0000000000000..f58c3793fad1a --- /dev/null +++ b/cmake/targets/BuildZstd.cmake @@ -0,0 +1,26 @@ +register_repository( + NAME + zstd + REPOSITORY + facebook/zstd + COMMIT + 794ea1b0afca0f020f4e57b6732332231fb23c70 +) + +register_cmake_command( + TARGET + zstd + TARGETS + libzstd_static + ARGS + -Sbuild/cmake + -DZSTD_BUILD_STATIC=ON + -DZSTD_BUILD_PROGRAMS=OFF + -DZSTD_BUILD_TESTS=OFF + -DZSTD_BUILD_CONTRIB=OFF + LIB_PATH + lib + LIBRARIES + zstd_static WIN32 + zstd UNIX +) diff --git a/cmake/toolchains/darwin-aarch64.cmake b/cmake/toolchains/darwin-aarch64.cmake new file mode 100644 index 0000000000000..b5a52c3fb2ef5 --- /dev/null +++ b/cmake/toolchains/darwin-aarch64.cmake @@ -0,0 +1,5 @@ +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) diff --git a/cmake/toolchains/darwin-x64.cmake b/cmake/toolchains/darwin-x64.cmake new file mode 100644 index 0000000000000..aef2c72d12d18 --- /dev/null +++ b/cmake/toolchains/darwin-x64.cmake @@ -0,0 +1,6 @@ +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR x64) +set(CMAKE_OSX_ARCHITECTURES x86_64) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) \ No newline at end of file diff --git a/cmake/toolchains/linux-aarch64.cmake b/cmake/toolchains/linux-aarch64.cmake new file mode 100644 index 0000000000000..bc23a063020be --- /dev/null +++ b/cmake/toolchains/linux-aarch64.cmake @@ -0,0 +1,5 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) \ No newline at end of file diff --git a/cmake/toolchains/linux-x64-baseline.cmake b/cmake/toolchains/linux-x64-baseline.cmake new file mode 100644 index 0000000000000..f521cfcc4afc1 --- /dev/null +++ b/cmake/toolchains/linux-x64-baseline.cmake @@ -0,0 +1,6 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR x64) +set(ENABLE_BASELINE ON) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) \ No newline at end of file diff --git a/cmake/toolchains/linux-x64.cmake b/cmake/toolchains/linux-x64.cmake new file mode 100644 index 0000000000000..66bc7a592fd0d --- /dev/null +++ b/cmake/toolchains/linux-x64.cmake @@ -0,0 +1,5 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR x64) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) diff --git a/cmake/toolchains/windows-x64-baseline.cmake b/cmake/toolchains/windows-x64-baseline.cmake new file mode 100644 index 0000000000000..fe2df9a930740 --- /dev/null +++ b/cmake/toolchains/windows-x64-baseline.cmake @@ -0,0 +1,6 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x64) +set(ENABLE_BASELINE ON) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) \ No newline at end of file diff --git a/cmake/toolchains/windows-x64.cmake b/cmake/toolchains/windows-x64.cmake new file mode 100644 index 0000000000000..bb239656dcd61 --- /dev/null +++ b/cmake/toolchains/windows-x64.cmake @@ -0,0 +1,5 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x64) + +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) \ No newline at end of file diff --git a/cmake/tools/SetupBuildkite.cmake b/cmake/tools/SetupBuildkite.cmake new file mode 100644 index 0000000000000..946ed251311ae --- /dev/null +++ b/cmake/tools/SetupBuildkite.cmake @@ -0,0 +1,175 @@ +optionx(BUILDKITE_CACHE BOOL "If the build can use Buildkite caches, even if not running in Buildkite" DEFAULT ${BUILDKITE}) + +if(NOT BUILDKITE_CACHE OR NOT BUN_LINK_ONLY) + return() +endif() + +optionx(BUILDKITE_ORGANIZATION_SLUG STRING "The organization slug to use on Buildkite" DEFAULT "bun") +optionx(BUILDKITE_PIPELINE_SLUG STRING "The pipeline slug to use on Buildkite" DEFAULT "bun") +optionx(BUILDKITE_BUILD_ID STRING "The build ID to use on Buildkite") +optionx(BUILDKITE_GROUP_ID STRING "The group ID to use on Buildkite") + +if(ENABLE_BASELINE) + set(DEFAULT_BUILDKITE_GROUP_KEY ${OS}-${ARCH}-baseline) +else() + set(DEFAULT_BUILDKITE_GROUP_KEY ${OS}-${ARCH}) +endif() + +optionx(BUILDKITE_GROUP_KEY STRING "The group key to use on Buildkite" DEFAULT ${DEFAULT_BUILDKITE_GROUP_KEY}) + +if(BUILDKITE) + optionx(BUILDKITE_BUILD_ID_OVERRIDE STRING "The build ID to use on Buildkite") + if(BUILDKITE_BUILD_ID_OVERRIDE) + setx(BUILDKITE_BUILD_ID ${BUILDKITE_BUILD_ID_OVERRIDE}) + endif() +endif() + +set(BUILDKITE_PATH ${BUILD_PATH}/buildkite) +set(BUILDKITE_BUILDS_PATH ${BUILDKITE_PATH}/builds) + +if(NOT BUILDKITE_BUILD_ID) + # TODO: find the latest build on the main branch that passed + return() +endif() + +setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_ID}) +setx(BUILDKITE_BUILD_PATH ${BUILDKITE_BUILDS_PATH}/builds/${BUILDKITE_BUILD_ID}) + +file( + DOWNLOAD ${BUILDKITE_BUILD_URL} + HTTPHEADER "Accept: application/json" + TIMEOUT 15 + STATUS BUILDKITE_BUILD_STATUS + ${BUILDKITE_BUILD_PATH}/build.json +) +if(NOT BUILDKITE_BUILD_STATUS EQUAL 0) + message(FATAL_ERROR "No build found: ${BUILDKITE_BUILD_STATUS} ${BUILDKITE_BUILD_URL}") + return() +endif() + +file(READ ${BUILDKITE_BUILD_PATH}/build.json BUILDKITE_BUILD) +string(JSON BUILDKITE_BUILD_UUID GET ${BUILDKITE_BUILD} id) +string(JSON BUILDKITE_JOBS GET ${BUILDKITE_BUILD} jobs) +string(JSON BUILDKITE_JOBS_COUNT LENGTH ${BUILDKITE_JOBS}) + +if(NOT BUILDKITE_JOBS_COUNT GREATER 0) + message(FATAL_ERROR "No jobs found: ${BUILDKITE_BUILD_URL}") + return() +endif() + +set(BUILDKITE_JOBS_FAILED) +set(BUILDKITE_JOBS_NOT_FOUND) +set(BUILDKITE_JOBS_NO_ARTIFACTS) +set(BUILDKITE_JOBS_NO_MATCH) +set(BUILDKITE_JOBS_MATCH) + +math(EXPR BUILDKITE_JOBS_MAX_INDEX "${BUILDKITE_JOBS_COUNT} - 1") +foreach(i RANGE ${BUILDKITE_JOBS_MAX_INDEX}) + string(JSON BUILDKITE_JOB GET ${BUILDKITE_JOBS} ${i}) + string(JSON BUILDKITE_JOB_ID GET ${BUILDKITE_JOB} id) + string(JSON BUILDKITE_JOB_PASSED GET ${BUILDKITE_JOB} passed) + string(JSON BUILDKITE_JOB_GROUP_ID GET ${BUILDKITE_JOB} group_uuid) + string(JSON BUILDKITE_JOB_GROUP_KEY GET ${BUILDKITE_JOB} group_identifier) + string(JSON BUILDKITE_JOB_NAME GET ${BUILDKITE_JOB} step_key) + if(NOT BUILDKITE_JOB_NAME) + string(JSON BUILDKITE_JOB_NAME GET ${BUILDKITE_JOB} name) + endif() + + if(NOT BUILDKITE_JOB_PASSED) + list(APPEND BUILDKITE_JOBS_FAILED ${BUILDKITE_JOB_NAME}) + continue() + endif() + + if(NOT (BUILDKITE_GROUP_ID AND BUILDKITE_GROUP_ID STREQUAL BUILDKITE_JOB_GROUP_ID) AND + NOT (BUILDKITE_GROUP_KEY AND BUILDKITE_GROUP_KEY STREQUAL BUILDKITE_JOB_GROUP_KEY)) + list(APPEND BUILDKITE_JOBS_NO_MATCH ${BUILDKITE_JOB_NAME}) + continue() + endif() + + set(BUILDKITE_ARTIFACTS_URL https://buildkite.com/organizations/${BUILDKITE_ORGANIZATION_SLUG}/pipelines/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_UUID}/jobs/${BUILDKITE_JOB_ID}/artifacts) + set(BUILDKITE_ARTIFACTS_PATH ${BUILDKITE_BUILD_PATH}/artifacts/${BUILDKITE_JOB_ID}.json) + + file( + DOWNLOAD ${BUILDKITE_ARTIFACTS_URL} + HTTPHEADER "Accept: application/json" + TIMEOUT 15 + STATUS BUILDKITE_ARTIFACTS_STATUS + ${BUILDKITE_ARTIFACTS_PATH} + ) + + if(NOT BUILDKITE_ARTIFACTS_STATUS EQUAL 0) + list(APPEND BUILDKITE_JOBS_NOT_FOUND ${BUILDKITE_JOB_NAME}) + continue() + endif() + + file(READ ${BUILDKITE_ARTIFACTS_PATH} BUILDKITE_ARTIFACTS) + string(JSON BUILDKITE_ARTIFACTS_LENGTH LENGTH ${BUILDKITE_ARTIFACTS}) + if(NOT BUILDKITE_ARTIFACTS_LENGTH GREATER 0) + list(APPEND BUILDKITE_JOBS_NO_ARTIFACTS ${BUILDKITE_JOB_NAME}) + continue() + endif() + + math(EXPR BUILDKITE_ARTIFACTS_MAX_INDEX "${BUILDKITE_ARTIFACTS_LENGTH} - 1") + foreach(i RANGE 0 ${BUILDKITE_ARTIFACTS_MAX_INDEX}) + string(JSON BUILDKITE_ARTIFACT GET ${BUILDKITE_ARTIFACTS} ${i}) + string(JSON BUILDKITE_ARTIFACT_ID GET ${BUILDKITE_ARTIFACT} id) + string(JSON BUILDKITE_ARTIFACT_PATH GET ${BUILDKITE_ARTIFACT} path) + + if(NOT BUILDKITE_ARTIFACT_PATH MATCHES "\\.(o|a|lib|zip|tar|gz)") + continue() + endif() + + if(BUILDKITE) + set(BUILDKITE_DOWNLOAD_COMMAND buildkite-agent artifact download ${BUILDKITE_ARTIFACT_PATH} . --build ${BUILDKITE_BUILD_UUID} --step ${BUILDKITE_JOB_ID}) + else() + set(BUILDKITE_DOWNLOAD_COMMAND curl -L -o ${BUILDKITE_ARTIFACT_PATH} ${BUILDKITE_ARTIFACTS_URL}/${BUILDKITE_ARTIFACT_ID}) + endif() + + add_custom_command( + COMMENT + "Downloading ${BUILDKITE_ARTIFACT_PATH}" + VERBATIM COMMAND + ${BUILDKITE_DOWNLOAD_COMMAND} + WORKING_DIRECTORY + ${BUILD_PATH} + OUTPUT + ${BUILD_PATH}/${BUILDKITE_ARTIFACT_PATH} + ) + endforeach() + + list(APPEND BUILDKITE_JOBS_MATCH ${BUILDKITE_JOB_NAME}) +endforeach() + +if(BUILDKITE_JOBS_FAILED) + list(SORT BUILDKITE_JOBS_FAILED COMPARE STRING) + list(JOIN BUILDKITE_JOBS_FAILED " " BUILDKITE_JOBS_FAILED) + message(WARNING "The following jobs were found, but failed: ${BUILDKITE_JOBS_FAILED}") +endif() + +if(BUILDKITE_JOBS_NOT_FOUND) + list(SORT BUILDKITE_JOBS_NOT_FOUND COMPARE STRING) + list(JOIN BUILDKITE_JOBS_NOT_FOUND " " BUILDKITE_JOBS_NOT_FOUND) + message(WARNING "The following jobs were found, but could not fetch their data: ${BUILDKITE_JOBS_NOT_FOUND}") +endif() + +if(BUILDKITE_JOBS_NO_MATCH) + list(SORT BUILDKITE_JOBS_NO_MATCH COMPARE STRING) + list(JOIN BUILDKITE_JOBS_NO_MATCH " " BUILDKITE_JOBS_NO_MATCH) + message(WARNING "The following jobs were found, but did not match the group ID: ${BUILDKITE_JOBS_NO_MATCH}") +endif() + +if(BUILDKITE_JOBS_NO_ARTIFACTS) + list(SORT BUILDKITE_JOBS_NO_ARTIFACTS COMPARE STRING) + list(JOIN BUILDKITE_JOBS_NO_ARTIFACTS " " BUILDKITE_JOBS_NO_ARTIFACTS) + message(WARNING "The following jobs were found, but had no artifacts: ${BUILDKITE_JOBS_NO_ARTIFACTS}") +endif() + +if(BUILDKITE_JOBS_MATCH) + list(SORT BUILDKITE_JOBS_MATCH COMPARE STRING) + list(JOIN BUILDKITE_JOBS_MATCH " " BUILDKITE_JOBS_MATCH) + message(STATUS "The following jobs were found, and matched the group ID: ${BUILDKITE_JOBS_MATCH}") +endif() + +if(NOT BUILDKITE_JOBS_FAILED AND NOT BUILDKITE_JOBS_NOT_FOUND AND NOT BUILDKITE_JOBS_NO_MATCH AND NOT BUILDKITE_JOBS_NO_ARTIFACTS AND NOT BUILDKITE_JOBS_MATCH) + message(FATAL_ERROR "Something went wrong with Buildkite?") +endif() diff --git a/cmake/tools/SetupBun.cmake b/cmake/tools/SetupBun.cmake new file mode 100644 index 0000000000000..5377eb1cff1a1 --- /dev/null +++ b/cmake/tools/SetupBun.cmake @@ -0,0 +1,21 @@ +find_command( + VARIABLE + BUN_EXECUTABLE + COMMAND + bun + PATHS + $ENV{HOME}/.bun/bin + VERSION + >=1.1.26 +) + +# If this is not set, some advanced features are not checked. +# https://github.com/oven-sh/bun/blob/cd7f6a1589db7f1e39dc4e3f4a17234afbe7826c/src/bun.js/javascript.zig#L1069-L1072 +setenv(BUN_GARBAGE_COLLECTOR_LEVEL 1) +setenv(BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING 1) +setenv(BUN_DEBUG_QUIET_LOGS 1) + +# FIXME: https://github.com/oven-sh/bun/issues/11250 +if(NOT WIN32) + setenv(BUN_INSTALL_CACHE_DIR ${CACHE_PATH}/bun) +endif() diff --git a/cmake/tools/SetupCcache.cmake b/cmake/tools/SetupCcache.cmake new file mode 100644 index 0000000000000..7e14cc8a41ef7 --- /dev/null +++ b/cmake/tools/SetupCcache.cmake @@ -0,0 +1,40 @@ +optionx(ENABLE_CCACHE BOOL "If ccache should be enabled" DEFAULT ON) + +if(NOT ENABLE_CCACHE OR CACHE_STRATEGY STREQUAL "none") + setenv(CCACHE_DISABLE 1) + return() +endif() + +find_command( + VARIABLE + CCACHE_PROGRAM + COMMAND + ccache + REQUIRED + ON +) + +set(CCACHE_ARGS CMAKE_C_COMPILER_LAUNCHER CMAKE_CXX_COMPILER_LAUNCHER) +foreach(arg ${CCACHE_ARGS}) + setx(${arg} ${CCACHE_PROGRAM}) + list(APPEND CMAKE_ARGS -D${arg}=${${arg}}) +endforeach() + +setenv(CCACHE_DIR ${CACHE_PATH}/ccache) +setenv(CCACHE_BASEDIR ${CWD}) +setenv(CCACHE_NOHASHDIR 1) + +if(CACHE_STRATEGY STREQUAL "read-only") + setenv(CCACHE_READONLY 1) +elseif(CACHE_STRATEGY STREQUAL "write-only") + setenv(CCACHE_RECACHE 1) +endif() + +setenv(CCACHE_FILECLONE 1) +setenv(CCACHE_STATSLOG ${BUILD_PATH}/ccache.log) + +if(CI) + setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,clang_index_store,gcno_cwd,include_file_ctime,include_file_mtime") +else() + setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,random_seed,clang_index_store,gcno_cwd") +endif() diff --git a/cmake/tools/SetupEsbuild.cmake b/cmake/tools/SetupEsbuild.cmake new file mode 100644 index 0000000000000..90989b711c79f --- /dev/null +++ b/cmake/tools/SetupEsbuild.cmake @@ -0,0 +1,20 @@ +if(CMAKE_HOST_WIN32) + setx(ESBUILD_EXECUTABLE ${CWD}/node_modules/.bin/esbuild.exe) +else() + setx(ESBUILD_EXECUTABLE ${CWD}/node_modules/.bin/esbuild) +endif() + +if(CMAKE_COLOR_DIAGNOSTICS) + set(ESBUILD_ARGS --color) +endif() + +register_command( + COMMAND + ${BUN_EXECUTABLE} + install + --frozen-lockfile + SOURCES + ${CWD}/package.json + OUTPUTS + ${ESBUILD_EXECUTABLE} +) diff --git a/cmake/tools/SetupLLVM.cmake b/cmake/tools/SetupLLVM.cmake new file mode 100644 index 0000000000000..a029ce9fce160 --- /dev/null +++ b/cmake/tools/SetupLLVM.cmake @@ -0,0 +1,65 @@ +if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE) + set(DEFAULT_LLVM_VERSION "18.1.8") +else() + set(DEFAULT_LLVM_VERSION "16.0.6") +endif() + +optionx(LLVM_VERSION STRING "The version of LLVM to use" DEFAULT ${DEFAULT_LLVM_VERSION}) +parse_semver(${LLVM_VERSION} LLVM) + +if(APPLE) + execute_process( + COMMAND brew --prefix llvm@${LLVM_VERSION_MAJOR} + OUTPUT_VARIABLE DEFAULT_LLVM_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if(NOT DEFAULT_LLVM_PREFIX) + set(DEFAULT_LLVM_PREFIX /opt/homebrew/opt/llvm) + endif() +elseif(NOT WIN32) + set(DEFAULT_LLVM_PREFIX /usr/lib/llvm-${LLVM_VERSION_MAJOR}) +else() + set(DEFAULT_LLVM_PREFIX /usr/lib) +endif() + +optionx(LLVM_PREFIX FILEPATH "The path to the LLVM installation" DEFAULT ${DEFAULT_LLVM_PREFIX}) +set(LLVM_PATH ${LLVM_PREFIX}/bin) + +macro(find_llvm_command VARIABLE COMMAND) + find_command( + VARIABLE ${VARIABLE} + COMMAND ${COMMAND} ${COMMAND}-${LLVM_VERSION_MAJOR} + PATHS ${LLVM_PATH} + VERSION ${LLVM_VERSION} + ) + list(APPEND CMAKE_ARGS -D${VARIABLE}=${${VARIABLE}}) +endmacro() + +macro(find_llvm_command_no_version VARIABLE COMMAND) + find_command( + VARIABLE ${VARIABLE} + COMMAND ${COMMAND} ${COMMAND}-${LLVM_VERSION_MAJOR} + PATHS ${LLVM_PATH} + REQUIRED ON + ) + list(APPEND CMAKE_ARGS -D${VARIABLE}=${${VARIABLE}}) +endmacro() + +if(WIN32) + find_llvm_command(CMAKE_C_COMPILER clang-cl) + find_llvm_command(CMAKE_CXX_COMPILER clang-cl) + find_llvm_command_no_version(CMAKE_LINKER lld-link) + find_llvm_command_no_version(CMAKE_AR llvm-lib) + find_llvm_command_no_version(CMAKE_STRIP llvm-strip) +else() + find_llvm_command(CMAKE_C_COMPILER clang) + find_llvm_command(CMAKE_CXX_COMPILER clang++) + find_llvm_command(CMAKE_LINKER llvm-link) + find_llvm_command(CMAKE_AR llvm-ar) + find_llvm_command(CMAKE_STRIP llvm-strip) + find_llvm_command(CMAKE_RANLIB llvm-ranlib) + if(APPLE) + find_llvm_command(CMAKE_DSYMUTIL dsymutil) + endif() +endif() diff --git a/cmake/tools/SetupMacSDK.cmake b/cmake/tools/SetupMacSDK.cmake new file mode 100644 index 0000000000000..1eff438b795c0 --- /dev/null +++ b/cmake/tools/SetupMacSDK.cmake @@ -0,0 +1,59 @@ +set(MIN_OSX_DEPLOYMENT_TARGET "13.0") + +if(DEFINED ENV{CI}) + set(DEFAULT_OSX_DEPLOYMENT_TARGET ${MIN_OSX_DEPLOYMENT_TARGET}) +else() + execute_process( + COMMAND xcrun --sdk macosx --show-sdk-version + OUTPUT_VARIABLE CURRENT_OSX_DEPLOYMENT_TARGET + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE DEFAULT_OSX_DEPLOYMENT_TARGET_ERROR + ERROR_STRIP_TRAILING_WHITESPACE + ) + if(DEFAULT_OSX_DEPLOYMENT_TARGET_ERROR) + message(WARNING "Failed to find macOS SDK version, did you run `xcode-select --install`?") + message(FATAL_ERROR ${DEFAULT_OSX_DEPLOYMENT_TARGET_ERROR}) + endif() + + string(REGEX MATCH "^[0-9]*" DEFAULT_OSX_DEPLOYMENT_TARGET ${CURRENT_OSX_DEPLOYMENT_TARGET}) +endif() + +optionx(CMAKE_OSX_DEPLOYMENT_TARGET STRING "The macOS SDK version to target" DEFAULT ${DEFAULT_OSX_DEPLOYMENT_TARGET}) + +if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS ${MIN_OSX_DEPLOYMENT_TARGET}) + message(FATAL_ERROR "The target macOS SDK version, ${CMAKE_OSX_DEPLOYMENT_TARGET}, is older than the minimum supported version, ${MIN_OSX_DEPLOYMENT_TARGET}.") +endif() + +execute_process( + COMMAND sw_vers -productVersion + OUTPUT_VARIABLE MACOS_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +if(MACOS_VERSION VERSION_LESS ${CMAKE_OSX_DEPLOYMENT_TARGET}) + message(FATAL_ERROR "Your computer is running macOS ${MACOS_VERSION}, which is older than the target macOS SDK ${CMAKE_OSX_DEPLOYMENT_TARGET}. To fix this, either:\n" + " - Upgrade your computer to macOS ${CMAKE_OSX_DEPLOYMENT_TARGET} or newer\n" + " - Download a newer version of the macOS SDK from Apple: https://developer.apple.com/download/all/?q=xcode\n" + " - Set -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOS_VERSION}\n") +endif() + +execute_process( + COMMAND xcrun --sdk macosx --show-sdk-path + OUTPUT_VARIABLE DEFAULT_CMAKE_OSX_SYSROOT + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE DEFAULT_CMAKE_OSX_SYSROOT_ERROR + ERROR_STRIP_TRAILING_WHITESPACE +) + +if(CMAKE_OSX_SYSROOT_ERROR) + message(WARNING "Failed to find macOS SDK path, did you run `xcode-select --install`?") + message(FATAL_ERROR ${CMAKE_OSX_SYSROOT_ERROR}) +endif() + +optionx(CMAKE_OSX_SYSROOT STRING "The macOS SDK path to target" DEFAULT ${DEFAULT_CMAKE_OSX_SYSROOT}) + +list(APPEND CMAKE_ARGS + -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} + -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} +) diff --git a/cmake/tools/SetupRust.cmake b/cmake/tools/SetupRust.cmake new file mode 100644 index 0000000000000..a83b28bc5ff08 --- /dev/null +++ b/cmake/tools/SetupRust.cmake @@ -0,0 +1,25 @@ +find_command( + VARIABLE + CARGO_EXECUTABLE + COMMAND + cargo + PATHS + $ENV{HOME}/.cargo/bin + REQUIRED + OFF +) + +if(EXISTS ${CARGO_EXECUTABLE}) + return() +endif() + +if(CMAKE_HOST_WIN32) + set(CARGO_INSTALL_COMMAND "choco install rust") +else() + set(CARGO_INSTALL_COMMAND "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh") +endif() + +message(FATAL_ERROR "Command not found: cargo\n" + "Do you have Rust installed? To fix this, try running:\n" + " ${CARGO_INSTALL_COMMAND}\n" +) diff --git a/cmake/tools/SetupWebKit.cmake b/cmake/tools/SetupWebKit.cmake new file mode 100644 index 0000000000000..5b31574a6acb2 --- /dev/null +++ b/cmake/tools/SetupWebKit.cmake @@ -0,0 +1,86 @@ +option(WEBKIT_VERSION "The version of WebKit to use") +option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading") + +if(NOT WEBKIT_VERSION) + set(WEBKIT_VERSION 4a2db3254a9535949a5d5380eb58cf0f77c8e15a) +endif() + +if(WEBKIT_LOCAL) + set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${CMAKE_BUILD_TYPE}) +else() + set(DEFAULT_WEBKIT_PATH ${CACHE_PATH}/webkit-${WEBKIT_VERSION}) +endif() + +option(WEBKIT_PATH "The path to the WebKit directory") + +if(NOT WEBKIT_PATH) + set(WEBKIT_PATH ${DEFAULT_WEBKIT_PATH}) +endif() + +set(WEBKIT_INCLUDE_PATH ${WEBKIT_PATH}/include) +set(WEBKIT_LIB_PATH ${WEBKIT_PATH}/lib) + +if(WEBKIT_LOCAL) + if(EXISTS ${WEBKIT_PATH}/cmakeconfig.h) + # You may need to run: + # make jsc-compile-debug jsc-copy-headers + include_directories( + ${WEBKIT_PATH} + ${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore + ${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders + ${WEBKIT_PATH}/bmalloc/Headers + ${WEBKIT_PATH}/WTF/Headers + ) + endif() + + # After this point, only prebuilt WebKit is supported + return() +endif() + +if(EXISTS ${WEBKIT_PATH}/package.json) + file(READ ${WEBKIT_PATH}/package.json WEBKIT_PACKAGE_JSON) + + if(WEBKIT_PACKAGE_JSON MATCHES ${WEBKIT_VERSION}) + return() + endif() +endif() + +if(WIN32) + set(WEBKIT_OS "windows") +elseif(APPLE) + set(WEBKIT_OS "macos") +elseif(UNIX) + set(WEBKIT_OS "linux") +else() + message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}") +endif() + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64") + set(WEBKIT_ARCH "arm64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64") + set(WEBKIT_ARCH "amd64") +else() + message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +if(DEBUG) + set(WEBKIT_SUFFIX "-debug") +elseif(ENABLE_LTO AND NOT WIN32) + set(WEBKIT_SUFFIX "-lto") +else() + set(WEBKIT_SUFFIX "") +endif() + +set(WEBKIT_NAME bun-webkit-${WEBKIT_OS}-${WEBKIT_ARCH}${WEBKIT_SUFFIX}) +set(WEBKIT_FILENAME ${WEBKIT_NAME}.tar.gz) +setx(WEBKIT_DOWNLOAD_URL https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_VERSION}/${WEBKIT_FILENAME}) + +file(DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS) +file(ARCHIVE_EXTRACT INPUT ${CACHE_PATH}/${WEBKIT_FILENAME} DESTINATION ${CACHE_PATH} TOUCH) +file(REMOVE ${CACHE_PATH}/${WEBKIT_FILENAME}) +file(REMOVE_RECURSE ${WEBKIT_PATH}) +file(RENAME ${CACHE_PATH}/bun-webkit ${WEBKIT_PATH}) + +if(APPLE) + file(REMOVE_RECURSE ${WEBKIT_INCLUDE_PATH}/unicode) +endif() diff --git a/cmake/tools/SetupZig.cmake b/cmake/tools/SetupZig.cmake new file mode 100644 index 0000000000000..005fec3597395 --- /dev/null +++ b/cmake/tools/SetupZig.cmake @@ -0,0 +1,143 @@ +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64") + set(DEFAULT_ZIG_ARCH "aarch64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64") + set(DEFAULT_ZIG_ARCH "x86_64") +else() + unsupported(CMAKE_SYSTEM_PROCESSOR) +endif() + +if(APPLE) + set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-macos-none) +elseif(WIN32) + set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-windows-msvc) +elseif(LINUX) + set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-gnu) +else() + unsupported(CMAKE_SYSTEM_NAME) +endif() + +optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET}) + +if(CMAKE_BUILD_TYPE STREQUAL "Release") + set(DEFAULT_ZIG_OPTIMIZE "ReleaseFast") +elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + set(DEFAULT_ZIG_OPTIMIZE "ReleaseSafe") +elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") + set(DEFAULT_ZIG_OPTIMIZE "ReleaseSmall") +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEFAULT_ZIG_OPTIMIZE "Debug") +else() + unsupported(CMAKE_BUILD_TYPE) +endif() + +# Since Bun 1.1, Windows has been built using ReleaseSafe. +# This is because it caught more crashes, but we can reconsider this in the future +if(WIN32 AND DEFAULT_ZIG_OPTIMIZE STREQUAL "ReleaseFast") + set(DEFAULT_ZIG_OPTIMIZE "ReleaseSafe") +endif() + +optionx(ZIG_OPTIMIZE "ReleaseFast|ReleaseSafe|ReleaseSmall|Debug" "The Zig optimize level to use" DEFAULT ${DEFAULT_ZIG_OPTIMIZE}) + +# To use LLVM bitcode from Zig, more work needs to be done. Currently, an install of +# LLVM 18.1.7 does not compatible with what bitcode Zig 0.13 outputs (has LLVM 18.1.7) +# Change to "bc" to experiment, "Invalid record" means it is not valid output. +optionx(ZIG_OBJECT_FORMAT "obj|bc" "Output file format for Zig object files" DEFAULT obj) + +optionx(ZIG_VERSION STRING "The version of zig to use" DEFAULT "0.13.0") +optionx(ZIG_LOCAL_CACHE_DIR FILEPATH "The path to local the zig cache directory" DEFAULT ${CACHE_PATH}/zig/local) +optionx(ZIG_GLOBAL_CACHE_DIR FILEPATH "The path to the global zig cache directory" DEFAULT ${CACHE_PATH}/zig/global) + +setx(ZIG_REPOSITORY_PATH ${VENDOR_PATH}/zig) +setx(ZIG_PATH ${CACHE_PATH}/zig/bin) + +register_repository( + NAME + zig + REPOSITORY + oven-sh/zig + COMMIT + 131a009ba2eb127a3447d05b9e12f710429aa5ee + PATH + ${ZIG_REPOSITORY_PATH} +) + +setenv(ZIG_LOCAL_CACHE_DIR ${ZIG_LOCAL_CACHE_DIR}) +setenv(ZIG_GLOBAL_CACHE_DIR ${ZIG_GLOBAL_CACHE_DIR}) + +set(CMAKE_ZIG_FLAGS + --cache-dir ${ZIG_LOCAL_CACHE_DIR} + --global-cache-dir ${ZIG_GLOBAL_CACHE_DIR} + --zig-lib-dir ${ZIG_REPOSITORY_PATH}/lib +) + +find_command( + VARIABLE + CMAKE_ZIG_COMPILER + COMMAND + zig + zig.exe + PATHS + ${ZIG_PATH} + VERSION + ${ZIG_VERSION} + REQUIRED + OFF +) + +if(CMAKE_ZIG_COMPILER) + return() +endif() + +if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|aarch64") + set(ZIG_HOST_ARCH "aarch64") +elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64") + set(ZIG_HOST_ARCH "x86_64") +else() + unsupported(CMAKE_HOST_SYSTEM_PROCESSOR) +endif() + +if(CMAKE_HOST_APPLE) + set(ZIG_HOST_OS "macos") +elseif(CMAKE_HOST_WIN32) + set(ZIG_HOST_OS "windows") +elseif(CMAKE_HOST_UNIX) + set(ZIG_HOST_OS "linux") +else() + unsupported(CMAKE_HOST_SYSTEM_NAME) +endif() + +set(ZIG_NAME zig-${ZIG_HOST_OS}-${ZIG_HOST_ARCH}-${ZIG_VERSION}) + +if(CMAKE_HOST_WIN32) + set(ZIG_EXE "zig.exe") + set(ZIG_FILENAME ${ZIG_NAME}.zip) +else() + set(ZIG_EXE "zig") + set(ZIG_FILENAME ${ZIG_NAME}.tar.xz) +endif() + +setx(ZIG_DOWNLOAD_URL https://ziglang.org/download/${ZIG_VERSION}/${ZIG_FILENAME}) +file(DOWNLOAD ${ZIG_DOWNLOAD_URL} ${TMP_PATH}/${ZIG_FILENAME} SHOW_PROGRESS) +file(ARCHIVE_EXTRACT INPUT ${TMP_PATH}/${ZIG_FILENAME} DESTINATION ${TMP_PATH} TOUCH) +file(REMOVE ${TMP_PATH}/${ZIG_FILENAME}) +file(COPY ${TMP_PATH}/${ZIG_NAME}/${ZIG_EXE} DESTINATION ${ZIG_PATH}) +file(CHMOD ${ZIG_PATH}/${ZIG_EXE} PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE) +setx(CMAKE_ZIG_COMPILER ${ZIG_PATH}/${ZIG_EXE}) + +if(NOT WIN32) + file(CREATE_LINK ${ZIG_PATH}/${ZIG_EXE} ${ZIG_PATH}/zig.exe SYMBOLIC) +endif() + +# Some zig commands need the executable to be in the same directory as the zig repository +register_command( + COMMENT + "Creating symlink for zig" + COMMAND + ${CMAKE_COMMAND} -E copy ${ZIG_PATH}/${ZIG_EXE} ${ZIG_REPOSITORY_PATH}/${ZIG_EXE} + && ${CMAKE_COMMAND} -E create_symlink ${ZIG_REPOSITORY_PATH}/${ZIG_EXE} ${ZIG_REPOSITORY_PATH}/zig.exe + OUTPUTS + ${ZIG_REPOSITORY_PATH}/${ZIG_EXE} + ${ZIG_REPOSITORY_PATH}/zig.exe + TARGETS + clone-zig +) diff --git a/completions/bun.bash b/completions/bun.bash index a7e0018bbe66f..ccabb1d73b61c 100644 --- a/completions/bun.bash +++ b/completions/bun.bash @@ -82,7 +82,7 @@ _bun_completions() { declare -A PACKAGE_OPTIONS; declare -A PM_OPTIONS; - local SUBCOMMANDS="dev bun create run install add remove upgrade completions discord help init pm x"; + local SUBCOMMANDS="dev bun create run install add remove upgrade completions discord help init pm x test repl update outdated link unlink build"; GLOBAL_OPTIONS[LONG_OPTIONS]="--use --cwd --bunfile --server-bunfile --config --disable-react-fast-refresh --disable-hmr --env-file --extension-order --jsx-factory --jsx-fragment --extension-order --jsx-factory --jsx-fragment --jsx-import-source --jsx-production --jsx-runtime --main-fields --no-summary --version --platform --public-dir --tsconfig-override --define --external --help --inject --loader --origin --port --dump-environment-variables --dump-limits --disable-bun-js"; GLOBAL_OPTIONS[SHORT_OPTIONS]="-c -v -d -e -h -i -l -u -p"; diff --git a/completions/bun.fish b/completions/bun.fish index 3cb9366b7d91a..a5c51aef0587c 100644 --- a/completions/bun.fish +++ b/completions/bun.fish @@ -179,6 +179,7 @@ complete -c bun -n "__fish_use_subcommand" -a "remove" -d "Remove a dependency f complete -c bun -n "__fish_use_subcommand" -a "add" -d "Add a dependency to package.json" -f complete -c bun -n "__fish_use_subcommand" -a "init" -d "Initialize a Bun project in this directory" -f complete -c bun -n "__fish_use_subcommand" -a "link" -d "Register or link a local npm package" -f -complete -c bun -n "__fish_use_subcommand" -a "link" -d "Unregister a local npm package" -f +complete -c bun -n "__fish_use_subcommand" -a "unlink" -d "Unregister a local npm package" -f complete -c bun -n "__fish_use_subcommand" -a "pm" -d "Additional package management utilities" -f complete -c bun -n "__fish_use_subcommand" -a "x" -d "Execute a package binary, installing if needed" -f +complete -c bun -n "__fish_use_subcommand" -a "outdated" -d "Display the latest versions of outdated dependencies" -f diff --git a/completions/bun.zsh b/completions/bun.zsh index 46485f39bab9f..d75f2aa2f0668 100644 --- a/completions/bun.zsh +++ b/completions/bun.zsh @@ -425,6 +425,7 @@ _bun_run_completion() { '--external[Exclude module from transpilation (can use * wildcards). ex: -e react]:external' \ '-e[Exclude module from transpilation (can use * wildcards). ex: -e react]:external' \ '--loader[Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi]:loader' \ + '--packages[Exclude dependencies from bundle, e.g. --packages external. Valid options: bundle, external]:packages' \ '-l[Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi]:loader' \ '--origin[Rewrite import URLs to start with --origin. Default: ""]:origin' \ '-u[Rewrite import URLs to start with --origin. Default: ""]:origin' \ @@ -562,6 +563,22 @@ _bun_update_completion() { esac } +_bun_outdated_completion() { + _arguments -s -C \ + '--cwd[Set a specific cwd]:cwd' \ + '--verbose[Excessively verbose logging]' \ + '--no-progress[Disable the progress bar]' \ + '--help[Print this help menu]' && + ret=0 + + case $state in + config) + _bun_list_bunfig_toml + + ;; + esac +} + _bun_test_completion() { _arguments -s -C \ '1: :->cmd1' \ @@ -668,6 +685,7 @@ _bun() { 'add\:"Add a dependency to package.json (bun a)" ' 'remove\:"Remove a dependency from package.json (bun rm)" ' 'update\:"Update outdated dependencies & save to package.json" ' + 'outdated\:"Display the latest versions of outdated dependencies" ' 'link\:"Link an npm package globally" ' 'unlink\:"Globally unlink an npm package" ' 'pm\:"More commands for managing packages" ' @@ -739,6 +757,10 @@ _bun() { update) _bun_update_completion + ;; + outdated) + _bun_outdated_completion + ;; 'test') _bun_test_completion @@ -818,6 +840,10 @@ _bun() { update) _bun_update_completion + ;; + outdated) + _bun_outdated_completion + ;; 'test') _bun_test_completion diff --git a/docs/api/binary-data.md b/docs/api/binary-data.md index 864ef9db0e6b6..e0820d44f38a3 100644 --- a/docs/api/binary-data.md +++ b/docs/api/binary-data.md @@ -61,7 +61,7 @@ To do anything interesting we need a construct known as a "view". A view is a cl The `DataView` class is a lower-level interface for reading and manipulating the data in an `ArrayBuffer`. -Below we create a new `DataView` and set the first byte to 5. +Below we create a new `DataView` and set the first byte to 3. ```ts const buf = new ArrayBuffer(4); @@ -219,6 +219,11 @@ The following classes are typed arrays, along with a description of how they int --- +- [`Float16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float16Array) +- Every two (2) bytes are interpreted as a 16-bit floating point number. Range -6.104e5 to 6.55e4. + +--- + - [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array) - Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38. @@ -377,6 +382,16 @@ Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/Ja It's worth specifically highlighting `Uint8Array`, as it represents a classic "byte array"—a sequence of 8-bit unsigned integers between 0 and 255. This is the most common typed array you'll encounter in JavaScript. +In Bun, and someday in other JavaScript engines, it has methods available for converting between byte arrays and serialized representations of those arrays as base64 or hex strings. + +```ts +new Uint8Array([1, 2, 3, 4, 5]).toBase64(); // "AQIDBA==" +Uint8Array.fromBase64("AQIDBA=="); // Uint8Array(4) [1, 2, 3, 4, 5] + +new Uint8Array([255, 254, 253, 252, 251]).toHex(); // "fffefdfcfb==" +Uint8Array.fromHex("fffefdfcfb"); // Uint8Array(5) [255, 254, 253, 252, 251] +``` + It is the return value of [`TextEncoder#encode`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder), and the input type of [`TextDecoder#decode`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder), two utility classes designed to translate strings and various binary encodings, most notably `"utf-8"`. ```ts @@ -395,7 +410,7 @@ Bun implements `Buffer`, a Node.js API for working with binary data that pre-dat ```ts const buf = Buffer.from("hello world"); -// => Buffer(16) [ 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103 ] +// => Buffer(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ] buf.length; // => 11 buf[0]; // => 104, ascii for 'h' @@ -437,6 +452,7 @@ The contents of a `Blob` can be asynchronously read in various formats. ```ts await blob.text(); // => hello +await blob.bytes(); // => Uint8Array (copies contents) await blob.arrayBuffer(); // => ArrayBuffer (copies contents) await blob.stream(); // => ReadableStream ``` @@ -506,7 +522,7 @@ for await (const chunk of stream) { } ``` -For a more complete discussion of streams in Bun, see [API > Streams](/docs/api/streams). +For a more complete discussion of streams in Bun, see [API > Streams](https://bun.sh/docs/api/streams). ## Conversion @@ -540,6 +556,7 @@ Buffer.from(buf, 0, 10); #### To `string` +As UTF-8: ```ts new TextDecoder().decode(buf); ``` @@ -620,6 +637,7 @@ Buffer.from(arr); #### To `string` +As UTF-8: ```ts new TextDecoder().decode(arr); ``` @@ -633,6 +651,7 @@ Array.from(arr); #### To `Blob` ```ts +// only if arr is a view of its entire backing TypedArray new Blob([arr.buffer], { type: "text/plain" }); ``` @@ -696,6 +715,7 @@ Buffer.from(view.buffer, view.byteOffset, view.byteLength); #### To `string` +As UTF-8: ```ts new TextDecoder().decode(view); ``` @@ -767,9 +787,18 @@ new DataView(buf.buffer, buf.byteOffset, buf.byteLength); #### To `string` +As UTF-8: ```ts buf.toString(); ``` +As base64: +```ts +buf.toString('base64'); +``` +As hex: +```ts +buf.toString('hex'); +``` #### To `number[]` @@ -829,7 +858,7 @@ await blob.arrayBuffer(); #### To `TypedArray` ```ts -new Uint8Array(await blob.arrayBuffer()); +await blob.bytes(); ``` #### To `DataView` @@ -846,6 +875,7 @@ Buffer.from(await blob.arrayBuffer()); #### To `string` +As UTF-8: ```ts await blob.text(); ``` @@ -853,7 +883,7 @@ await blob.text(); #### To `number[]` ```ts -Array.from(new Uint8Array(await blob.arrayBuffer())); +Array.from(await blob.bytes()); ``` #### To `ReadableStream` @@ -931,9 +961,10 @@ Buffer.from(Bun.readableStreamToArrayBuffer(stream)); #### To `string` +As UTF-8: ```ts // with Response -new Response(stream).text(); +await new Response(stream).text(); // with Bun function await Bun.readableStreamToText(stream); @@ -943,8 +974,8 @@ await Bun.readableStreamToText(stream); ```ts // with Response -const buf = await new Response(stream).arrayBuffer(); -Array.from(new Uint8Array(buf)); +const arr = await new Response(stream).bytes(); +Array.from(arr); // with Bun function Array.from(new Uint8Array(Bun.readableStreamToArrayBuffer(stream))); diff --git a/docs/api/cc.md b/docs/api/cc.md new file mode 100644 index 0000000000000..212b928df5404 --- /dev/null +++ b/docs/api/cc.md @@ -0,0 +1,197 @@ +`bun:ffi` has experimental support for compiling and running C from JavaScript with low overhead. + +## Usage (cc in `bun:ffi`) + +See the [introduction blog post](https://bun.sh/blog/compile-and-run-c-in-js) for more information. + +JavaScript: + +```ts#hello.js +import { cc } from "bun:ffi"; +import source from "./hello.c" with { type: "file" }; + +const { + symbols: { hello }, +} = cc({ + source, + symbols: { + hello: { + args: [], + returns: "int", + }, + }, +}); + +console.log("What is the answer to the universe?", hello()); +``` + +C source: + +```c#hello.c +int hello() { + return 42; +} +``` + +When you run `hello.js`, it will print: + +```sh +$ bun hello.js +What is the answer to the universe? 42 +``` + +Under the hood, `cc` uses [TinyCC](https://bellard.org/tcc/) to compile the C code and then link it with the JavaScript runtime, efficiently converting types in-place. + +### Primitive types + +The same `FFIType` values in [`dlopen`](/docs/api/ffi) are supported in `cc`. + +| `FFIType` | C Type | Aliases | +| ---------- | -------------- | --------------------------- | +| cstring | `char*` | | +| function | `(void*)(*)()` | `fn`, `callback` | +| ptr | `void*` | `pointer`, `void*`, `char*` | +| i8 | `int8_t` | `int8_t` | +| i16 | `int16_t` | `int16_t` | +| i32 | `int32_t` | `int32_t`, `int` | +| i64 | `int64_t` | `int64_t` | +| i64_fast | `int64_t` | | +| u8 | `uint8_t` | `uint8_t` | +| u16 | `uint16_t` | `uint16_t` | +| u32 | `uint32_t` | `uint32_t` | +| u64 | `uint64_t` | `uint64_t` | +| u64_fast | `uint64_t` | | +| f32 | `float` | `float` | +| f64 | `double` | `double` | +| bool | `bool` | | +| char | `char` | | +| napi_env | `napi_env` | | +| napi_value | `napi_value` | | + +### Strings, objects, and non-primitive types + +To make it easier to work with strings, objects, and other non-primitive types that don't map 1:1 to C types, `cc` supports N-API. + +To pass or receive a JavaScript values without any type conversions from a C function, you can use `napi_value`. + +You can also pass a `napi_env` to receive the N-API environment used to call the JavaScript function. + +#### Returning a C string to JavaScript + +For example, if you have a string in C, you can return it to JavaScript like this: + +```ts#hello.js +import { cc } from "bun:ffi"; +import source from "./hello.c" with { type: "file" }; + +const { + symbols: { hello }, +} = cc({ + source, + symbols: { + hello: { + args: ["napi_env"], + returns: "napi_value", + }, + }, +}); + +const result = hello(); +``` + +And in C: + +```c#hello.c +#include + +napi_value hello(napi_env env) { + napi_value result; + napi_create_string_utf8(env, "Hello, Napi!", NAPI_AUTO_LENGTH, &result); + return result; +} +``` + +You can also use this to return other types like objects and arrays: + +```c#hello.c +#include + +napi_value hello(napi_env env) { + napi_value result; + napi_create_object(env, &result); + return result; +} +``` + +### `cc` Reference + +#### `library: string[]` + +The `library` array is used to specify the libraries that should be linked with the C code. + +```ts +type Library = string[]; + +cc({ + source: "hello.c", + library: ["sqlite3"], +}); +``` + +#### `symbols` + +The `symbols` object is used to specify the functions and variables that should be exposed to JavaScript. + +```ts +type Symbols = { + [key: string]: { + args: FFIType[]; + returns: FFIType; + }; +}; +``` + +#### `source` + +The `source` is a file path to the C code that should be compiled and linked with the JavaScript runtime. + +```ts +type Source = string | URL | BunFile; + +cc({ + source: "hello.c", + symbols: { + hello: { + args: [], + returns: "int", + }, + }, +}); +``` + +#### `flags: string | string[]` + +The `flags` is an optional array of strings that should be passed to the TinyCC compiler. + +```ts +type Flags = string | string[]; +``` + +These are flags like `-I` for include directories and `-D` for preprocessor definitions. + +#### `defines: Record` + +The `defines` is an optional object that should be passed to the TinyCC compiler. + +```ts +type Defines = Record; + +cc({ + source: "hello.c", + defines: { + "NDEBUG": "1", + }, +}); +``` + +These are preprocessor definitions passed to the TinyCC compiler. diff --git a/docs/api/fetch.md b/docs/api/fetch.md new file mode 100644 index 0000000000000..afbf53c5c1662 --- /dev/null +++ b/docs/api/fetch.md @@ -0,0 +1,308 @@ +Bun implements the WHATWG `fetch` standard, with some extensions to meet the needs of server-side JavaScript. + +Bun also implements `node:http`, but `fetch` is generally recommended instead. + +## Sending an HTTP request + +To send an HTTP request, use `fetch` + +```ts +const response = await fetch("http://example.com"); + +console.log(response.status); // => 200 + +const text = await response.text(); // or response.json(), response.formData(), etc. +``` + +`fetch` also works with HTTPS URLs. + +```ts +const response = await fetch("https://example.com"); +``` + +You can also pass `fetch` a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. + +```ts +const request = new Request("http://example.com", { + method: "POST", + body: "Hello, world!", +}); + +const response = await fetch(request); +``` + +### Sending a POST request + +To send a POST request, pass an object with the `method` property set to `"POST"`. + +```ts +const response = await fetch("http://example.com", { + method: "POST", + body: "Hello, world!", +}); +``` + +`body` can be a string, a `FormData` object, an `ArrayBuffer`, a `Blob`, and more. See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#setting_a_body) for more information. + +### Proxying requests + +To proxy a request, pass an object with the `proxy` property set to a URL. + +```ts +const response = await fetch("http://example.com", { + proxy: "http://proxy.com", +}); +``` + +### Custom headers + +To set custom headers, pass an object with the `headers` property set to an object. + +```ts +const response = await fetch("http://example.com", { + headers: { + "X-Custom-Header": "value", + }, +}); +``` + +You can also set headers using the [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object. + +```ts +const headers = new Headers(); +headers.append("X-Custom-Header", "value"); + +const response = await fetch("http://example.com", { + headers, +}); +``` + +### Response bodies + +To read the response body, use one of the following methods: + +- `response.text(): Promise`: Returns a promise that resolves with the response body as a string. +- `response.json(): Promise`: Returns a promise that resolves with the response body as a JSON object. +- `response.formData(): Promise`: Returns a promise that resolves with the response body as a `FormData` object. +- `response.bytes(): Promise`: Returns a promise that resolves with the response body as a `Uint8Array`. +- `response.arrayBuffer(): Promise`: Returns a promise that resolves with the response body as an `ArrayBuffer`. +- `response.blob(): Promise`: Returns a promise that resolves with the response body as a `Blob`. + +#### Streaming response bodies + +You can use async iterators to stream the response body. + +```ts +const response = await fetch("http://example.com"); + +for await (const chunk of response.body) { + console.log(chunk); +} +``` + +You can also more directly access the `ReadableStream` object. + +```ts +const response = await fetch("http://example.com"); + +const stream = response.body; + +const reader = stream.getReader(); +const { value, done } = await reader.read(); +``` + +### Fetching a URL with a timeout + +To fetch a URL with a timeout, use `AbortSignal.timeout`: + +```ts +const response = await fetch("http://example.com", { + signal: AbortSignal.timeout(1000), +}); +``` + +#### Canceling a request + +To cancel a request, use an `AbortController`: + +```ts +const controller = new AbortController(); + +const response = await fetch("http://example.com", { + signal: controller.signal, +}); + +controller.abort(); +``` + +### Unix domain sockets + +To fetch a URL using a Unix domain socket, use the `unix: string` option: + +```ts +const response = await fetch("https://hostname/a/path", { + unix: "/var/run/path/to/unix.sock", + method: "POST", + body: JSON.stringify({ message: "Hello from Bun!" }), + headers: { + "Content-Type": "application/json", + }, +}); +``` + +### TLS + +To use a client certificate, use the `tls` option: + +```ts +await fetch("https://example.com", { + tls: { + key: Bun.file("/path/to/key.pem"), + cert: Bun.file("/path/to/cert.pem"), + // ca: [Bun.file("/path/to/ca.pem")], + }, +}); +``` + +#### Custom TLS Validation + +To customize the TLS validation, use the `checkServerIdentity` option in `tls` + +```ts +await fetch("https://example.com", { + tls: { + checkServerIdentity: (hostname, peerCertificate) => { + // Return an error if the certificate is invalid + }, + }, +}); +``` + +This is similar to how it works in Node's `net` module. + +## Debugging + +To help with debugging, you can pass `verbose: true` to `fetch`: + +```ts +const response = await fetch("http://example.com", { + verbose: true, +}); +``` + +This will print the request and response headers to your terminal: + +```sh +[fetch] > HTTP/1.1 GET http://example.com/ +[fetch] > Connection: keep-alive +[fetch] > User-Agent: Bun/1.1.21 +[fetch] > Accept: */* +[fetch] > Host: example.com +[fetch] > Accept-Encoding: gzip, deflate, br + +[fetch] < 200 OK +[fetch] < Content-Encoding: gzip +[fetch] < Age: 201555 +[fetch] < Cache-Control: max-age=604800 +[fetch] < Content-Type: text/html; charset=UTF-8 +[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT +[fetch] < Etag: "3147526947+gzip" +[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT +[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT +[fetch] < Server: ECAcc (sac/254F) +[fetch] < Vary: Accept-Encoding +[fetch] < X-Cache: HIT +[fetch] < Content-Length: 648 +``` + +Note: `verbose: boolean` is not part of the Web standard `fetch` API and is specific to Bun. + +## Performance + +Before an HTTP request can be sent, the DNS lookup must be performed. This can take a significant amount of time, especially if the DNS server is slow or the network connection is poor. + +After the DNS lookup, the TCP socket must be connected and the TLS handshake might need to be performed. This can also take a significant amount of time. + +After the request completes, consuming the response body can also take a significant amount of time and memory. + +At every step of the way, Bun provides APIs to help you optimize the performance of your application. + +### DNS prefetching + +To prefetch a DNS entry, you can use the `dns.prefetch` API. This API is useful when you know you'll need to connect to a host soon and want to avoid the initial DNS lookup. + +```ts +import { dns } from "bun"; + +dns.prefetch("bun.sh", 443); +``` + +#### DNS caching + +By default, Bun caches and deduplicates DNS queries in-memory for up to 30 seconds. You can see the cache stats by calling `dns.getCacheStats()`: + +To learn more about DNS caching in Bun, see the [DNS caching](https://bun.sh/docs/api/dns) documentation. + +### Preconnect to a host + +To preconnect to a host, you can use the `fetch.preconnect` API. This API is useful when you know you'll need to connect to a host soon and want to start the initial DNS lookup, TCP socket connection, and TLS handshake early. + +```ts +import { fetch } from "bun"; + +fetch.preconnect("https://bun.sh"); +``` + +Note: calling `fetch` immediately after `fetch.preconnect` will not make your request faster. Preconnecting only helps if you know you'll need to connect to a host soon, but you're not ready to make the request yet. + +#### Preconnect at startup + +To preconnect to a host at startup, you can pass `--fetch-preconnect`: + +```sh +$ bun --fetch-preconnect https://bun.sh ./my-script.ts +``` + +This is sort of like `` in HTML. + +This feature is not implemented on Windows yet. If you're interested in using this feature on Windows, please file an issue and we can implement support for it on Windows. + +### Connection pooling & HTTP keep-alive + +Bun automatically reuses connections to the same host. This is known as connection pooling. This can significantly reduce the time it takes to establish a connection. You don't need to do anything to enable this; it's automatic. + +#### Simultaneous connection limit + +By default, Bun limits the maximum number of simultaneous `fetch` requests to 256. We do this for several reasons: + +- It improves overall system stability. Operating systems have an upper limit on the number of simultaneous open TCP sockets, usually in the low thousands. Nearing this limit causes your entire computer to behave strangely. Applications hang and crash. +- It encourages HTTP Keep-Alive connection reuse. For short-lived HTTP requests, the slowest step is often the initial connection setup. Reusing connections can save a lot of time. + +When the limit is exceeded, the requests are queued and sent as soon as the next request ends. + +You can increase the maximum number of simultaneous connections via the `BUN_CONFIG_MAX_HTTP_REQUESTS` environment variable: + +```sh +$ BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts +``` + +The max value for this limit is currently set to 65,336. The maximum port number is 65,535, so it's quite difficult for any one computer to exceed this limit. + +### Response buffering + +Bun goes to great lengths to optimize the performance of reading the response body. The fastest way to read the response body is to use one of these methods: + +- `response.text(): Promise` +- `response.json(): Promise` +- `response.formData(): Promise` +- `response.bytes(): Promise` +- `response.arrayBuffer(): Promise` +- `response.blob(): Promise` + +You can also use `Bun.write` to write the response body to a file on disk: + +```ts +import { write } from "bun"; + +await write("output.txt", response); +``` diff --git a/docs/api/ffi.md b/docs/api/ffi.md index 1a276ba0354ce..d3a7fc3e58b80 100644 --- a/docs/api/ffi.md +++ b/docs/api/ffi.md @@ -1,6 +1,6 @@ Use the built-in `bun:ffi` module to efficiently call native libraries from JavaScript. It works with languages that support the C ABI (Zig, Rust, C/C++, C#, Nim, Kotlin, etc). -## Usage (`bun:ffi`) +## dlopen usage (`bun:ffi`) To print the version number of `sqlite3`: @@ -108,25 +108,27 @@ $ zig build-lib add.cpp -dynamic -lc -lc++ The following `FFIType` values are supported. -| `FFIType` | C Type | Aliases | -| --------- | -------------- | --------------------------- | -| cstring | `char*` | | -| function | `(void*)(*)()` | `fn`, `callback` | -| ptr | `void*` | `pointer`, `void*`, `char*` | -| i8 | `int8_t` | `int8_t` | -| i16 | `int16_t` | `int16_t` | -| i32 | `int32_t` | `int32_t`, `int` | -| i64 | `int64_t` | `int64_t` | -| i64_fast | `int64_t` | | -| u8 | `uint8_t` | `uint8_t` | -| u16 | `uint16_t` | `uint16_t` | -| u32 | `uint32_t` | `uint32_t` | -| u64 | `uint64_t` | `uint64_t` | -| u64_fast | `uint64_t` | | -| f32 | `float` | `float` | -| f64 | `double` | `double` | -| bool | `bool` | | -| char | `char` | | +| `FFIType` | C Type | Aliases | +| ---------- | -------------- | --------------------------- | +| cstring | `char*` | | +| function | `(void*)(*)()` | `fn`, `callback` | +| ptr | `void*` | `pointer`, `void*`, `char*` | +| i8 | `int8_t` | `int8_t` | +| i16 | `int16_t` | `int16_t` | +| i32 | `int32_t` | `int32_t`, `int` | +| i64 | `int64_t` | `int64_t` | +| i64_fast | `int64_t` | | +| u8 | `uint8_t` | `uint8_t` | +| u16 | `uint16_t` | `uint16_t` | +| u32 | `uint32_t` | `uint32_t` | +| u64 | `uint64_t` | `uint64_t` | +| u64_fast | `uint64_t` | | +| f32 | `float` | `float` | +| f64 | `double` | `double` | +| bool | `bool` | | +| char | `char` | | +| napi_env | `napi_env` | | +| napi_value | `napi_value` | | ## Strings diff --git a/docs/api/file-io.md b/docs/api/file-io.md index 206abfe475723..f9fd2368f37e2 100644 --- a/docs/api/file-io.md +++ b/docs/api/file-io.md @@ -1,8 +1,8 @@ {% callout %} - + -**Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module. +**Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](https://bun.sh/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module. {% /callout %} diff --git a/docs/api/globals.md b/docs/api/globals.md index fe7cd60c64ae2..8e5a89651a4c1 100644 --- a/docs/api/globals.md +++ b/docs/api/globals.md @@ -34,7 +34,7 @@ Bun implements the following globals. - [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) - Node.js -- See [Node.js > `Buffer`](/docs/runtime/nodejs-apis#node-buffer) +- See [Node.js > `Buffer`](https://bun.sh/docs/runtime/nodejs-apis#node-buffer) --- @@ -172,7 +172,7 @@ Bun implements the following globals. - [`global`](https://nodejs.org/api/globals.html#global) - Node.js -- See [Node.js > `global`](/docs/runtime/nodejs-apis#global). +- See [Node.js > `global`](https://bun.sh/docs/runtime/nodejs-apis#global). --- @@ -188,7 +188,7 @@ Bun implements the following globals. --- -- [`HTMLRewriter`](/docs/api/html-rewriter) +- [`HTMLRewriter`](https://bun.sh/docs/api/html-rewriter) - Cloudflare -   @@ -220,7 +220,7 @@ Bun implements the following globals. - [`process`](https://nodejs.org/api/process.html) - Node.js -- See [Node.js > `process`](/docs/runtime/nodejs-apis#node-process) +- See [Node.js > `process`](https://bun.sh/docs/runtime/nodejs-apis#node-process) --- diff --git a/docs/api/http.md b/docs/api/http.md index 1f873dbb5d012..7b5c7636d6d2d 100644 --- a/docs/api/http.md +++ b/docs/api/http.md @@ -1,7 +1,7 @@ The page primarily documents the Bun-native `Bun.serve` API. Bun also implements [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and the Node.js [`http`](https://nodejs.org/api/http.html) and [`https`](https://nodejs.org/api/https.html) modules. {% callout %} -These modules have been re-implemented to use Bun's fast internal HTTP infrastructure. Feel free to use these modules directly; frameworks like [Express](https://expressjs.com/) that depend on these modules should work out of the box. For granular compatibility information, see [Runtime > Node.js APIs](/docs/runtime/nodejs-apis). +These modules have been re-implemented to use Bun's fast internal HTTP infrastructure. Feel free to use these modules directly; frameworks like [Express](https://expressjs.com/) that depend on these modules should work out of the box. For granular compatibility information, see [Runtime > Node.js APIs](https://bun.sh/docs/runtime/nodejs-apis). {% /callout %} To start a high-performance HTTP server with a clean API, the recommended approach is [`Bun.serve`](#start-a-server-bun-serve). @@ -70,6 +70,116 @@ const server = Bun.serve({ }); ``` +### Static routes + +Use the `static` option to serve static `Response` objects by route. + +```ts +// Bun v1.1.27+ required +Bun.serve({ + static: { + // health-check endpoint + "/api/health-check": new Response("All good!"), + + // redirect from /old-link to /new-link + "/old-link": Response.redirect("/new-link", 301), + + // serve static text + "/": new Response("Hello World"), + + // serve a file by buffering it in memory + "/index.html": new Response(await Bun.file("./index.html").bytes(), { + headers: { + "Content-Type": "text/html", + }, + }), + "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { + headers: { + "Content-Type": "image/x-icon", + }, + }), + + // serve JSON + "/api/version.json": Response.json({ version: "1.0.0" }), + }, + + fetch(req) { + return new Response("404!"); + }, +}); +``` + +Static routes support headers, status code, and other `Response` options. + +```ts +Bun.serve({ + static: { + "/api/time": new Response(new Date().toISOString(), { + headers: { + "X-Custom-Header": "Bun!", + }, + }), + }, + + fetch(req) { + return new Response("404!"); + }, +}); +``` + +Static routes can serve Response bodies faster than `fetch` handlers because they don't create `Request` objects, they don't create `AbortSignal`, they don't create additional `Response` objects. The only per-request memory allocation is the TCP/TLS socket data needed for each request. + +{% note %} +`static` is experimental +{% /note %} + +Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`. + +```ts +const server = Bun.serve({ + static: { + "/api/time": new Response(new Date().toISOString()), + }, + + fetch(req) { + return new Response("404!"); + }, +}); + +// Update the time every second. +setInterval(() => { + server.reload({ + static: { + "/api/time": new Response(new Date().toISOString()), + }, + + fetch(req) { + return new Response("404!"); + }, + }); +}, 1000); +``` + +Reloading static routes only impact the next request. In-flight requests continue to use the old static routes. After in-flight requests to old static routes are finished, the old static routes are freed from memory. + +To simplify error handling, static routes do not support streaming response bodies from `ReadableStream` or an `AsyncIterator`. Fortunately, you can still buffer the response in memory first: + +```ts +const time = await fetch("https://api.example.com/v1/data"); +// Buffer the response in memory first. +const blob = await time.blob(); + +const server = Bun.serve({ + static: { + "/api/data": new Response(blob), + }, + + fetch(req) { + return new Response("404!"); + }, +}); +``` + ### Changing the `port` and `hostname` To configure which port and hostname the server will listen on, set `port` and `hostname` in the options object. @@ -326,7 +436,24 @@ Bun.serve({ }); ``` -## Object syntax +## idleTimeout + +To configure the idle timeout, set the `idleTimeout` field in Bun.serve. + +```ts +Bun.serve({ + // 10 seconds: + idleTimeout: 10, + + fetch(req) { + return new Response("Bun!"); + }, +}); +``` + +This is the maximum amount of time a connection is allowed to be idle before the server closes it. A connection is idling if there is no data sent or received. + +## export default syntax Thus far, the examples on this page have used the explicit `Bun.serve` API. Bun also supports an alternate syntax. @@ -348,7 +475,7 @@ Instead of passing the server options into `Bun.serve`, `export default` it. Thi $ bun --hot server.ts ``` --> - + ## Streaming files diff --git a/docs/api/semver.md b/docs/api/semver.md index f27ec53e6635c..dacafa95c719e 100644 --- a/docs/api/semver.md +++ b/docs/api/semver.md @@ -4,7 +4,7 @@ It's about 20x faster than `node-semver`. ![Benchmark](https://github.com/oven-sh/bun/assets/709451/94746adc-8aba-4baf-a143-3c355f8e0f78) -Currently, this API is two functions. +Currently, this API provides two functions : #### `Bun.semver.satisfies(version: string, range: string): boolean` diff --git a/docs/api/sqlite.md b/docs/api/sqlite.md index 553ca03902682..fc714678295dd 100644 --- a/docs/api/sqlite.md +++ b/docs/api/sqlite.md @@ -419,7 +419,7 @@ const results = query.all("hello", "goodbye"); sqlite supports signed 64 bit integers, but JavaScript only supports signed 52 bit integers or arbitrary precision integers with `bigint`. -`bigint` input is supported everywhere, but by default `bun:sqlite` returns integers as `number` types. If you need to handle integers larger than 2^53, set `safeInteger` option to `true` when creating a `Database` instance. This also validates that `bigint` passed to `bun:sqlite` do not exceed 64 bits. +`bigint` input is supported everywhere, but by default `bun:sqlite` returns integers as `number` types. If you need to handle integers larger than 2^53, set `safeIntegers` option to `true` when creating a `Database` instance. This also validates that `bigint` passed to `bun:sqlite` do not exceed 64 bits. By default, `bun:sqlite` returns integers as `number` types. If you need to handle integers larger than 2^53, you can use the `bigint` type. diff --git a/docs/api/test.md b/docs/api/test.md index 6704d407d6d34..d9898ffc0fed2 100644 --- a/docs/api/test.md +++ b/docs/api/test.md @@ -1 +1 @@ -See the [`bun test`](/docs/cli/test) documentation. +See the [`bun test`](https://bun.sh/docs/cli/test) documentation. diff --git a/docs/api/utils.md b/docs/api/utils.md index 665cf401a6f7e..d4e46441e0951 100644 --- a/docs/api/utils.md +++ b/docs/api/utils.md @@ -183,7 +183,7 @@ const currentFile = import.meta.url; Bun.openInEditor(currentFile); ``` -You can override this via the `debug.editor` setting in your [`bunfig.toml`](/docs/runtime/bunfig). +You can override this via the `debug.editor` setting in your [`bunfig.toml`](https://bun.sh/docs/runtime/bunfig). ```toml-diff#bunfig.toml + [debug] @@ -200,8 +200,6 @@ Bun.openInEditor(import.meta.url, { }); ``` -Bun.ArrayBufferSink; - ## `Bun.deepEquals()` Recursively checks if two objects are equivalent. This is used internally by `expect().toEqual()` in `bun:test`. diff --git a/docs/bundler/executables.md b/docs/bundler/executables.md index 2e9459279dada..7a7f05693f35f 100644 --- a/docs/bundler/executables.md +++ b/docs/bundler/executables.md @@ -106,6 +106,31 @@ The `--minify` argument optimizes the size of the transpiled output code. If you The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that errors & stacktraces point to their original locations instead of the transpiled location. Bun will automatically decompress & resolve the sourcemap when an error occurs. +## Worker + +To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments: + +```sh +$ bun build --compile ./index.ts ./my-worker.ts --outfile myapp +``` + +Then, reference the worker in your code: + +```ts +console.log("Hello from Bun!"); + +// Any of these will work: +new Worker("./my-worker.ts"); +new Worker(new URL("./my-worker.ts", import.meta.url)); +new Worker(new URL("./my-worker.ts", import.meta.url).href); +``` + +As of Bun v1.1.25, when you add multiple entrypoints to a standalone executable, they will be bundled separately into the executable. + +In the future, we may automatically detect usages of statically-known paths in `new Worker(path)` and then bundle those into the executable, but for now, you'll need to add it to the shell command manually like the above example. + +If you use a relative path to a file not included in the standalone executable, it will attempt to load that path from disk relative to the current working directory of the process (and then error if it doesn't exist). + ## SQLite You can use `bun:sqlite` imports with `bun build --compile`. @@ -179,6 +204,59 @@ console.log(addon.hello()); Unfortunately, if you're using `@mapbox/node-pre-gyp` or other similar tools, you'll need to make sure the `.node` file is directly required or it won't bundle correctly. +### Embed directories + +To embed a directory with `bun build --compile`, use a shell glob in your `bun build` command: + +```sh +$ bun build --compile ./index.ts ./public/**/*.png +``` + +Then, you can reference the files in your code: + +```ts +import icon from "./public/assets/icon.png" with { type: "file" }; +import { file } from "bun"; + +export default { + fetch(req) { + // Embedded files can be streamed from Response objects + return new Response(file(icon)); + }, +}; +``` + +This is honestly a workaround, and we expect to improve this in the future with a more direct API. + +### Listing embedded files + +To get a list of all embedded files, use `Bun.embeddedFiles`: + +```js +import "./icon.png" with { type: "file" }; +import { embeddedFiles } from "bun"; + +console.log(embeddedFiles[0].name); // `icon-${hash}.png` +``` + +`Bun.embeddedFiles` returns an array of `Blob` objects which you can use to get the size, contents, and other properties of the files. + +```ts +embeddedFiles: Blob[] +``` + +The list of embedded files excludes bundled source code like `.ts` and `.js` files. + +#### Content hash + +By default, embedded files have a content hash appended to their name. This is useful for situations where you want to serve the file from a URL or CDN and have fewer cache invalidation issues. But sometimes, this is unexpected and you might want the original name instead: + +To disable the content hash, pass `--asset-naming` to `bun build --compile` like this: + +```sh +$ bun build --compile --asset-naming="[name].[ext]" ./index.ts +``` + ## Minification To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller. diff --git a/docs/bundler/index.md b/docs/bundler/index.md index 514be87a9e7ef..a3b166e8b4ac2 100644 --- a/docs/bundler/index.md +++ b/docs/bundler/index.md @@ -146,7 +146,7 @@ $ bun build ./index.tsx --outdir ./out --watch ## Content types -Like the Bun runtime, the bundler supports an array of file types out of the box. The following table breaks down the bundler's set of standard "loaders". Refer to [Bundler > File types](/docs/runtime/loaders) for full documentation. +Like the Bun runtime, the bundler supports an array of file types out of the box. The following table breaks down the bundler's set of standard "loaders". Refer to [Bundler > File types](https://bun.sh/docs/runtime/loaders) for full documentation. {% table %} @@ -219,11 +219,11 @@ console.log(logo); The exact behavior of the file loader is also impacted by [`naming`](#naming) and [`publicPath`](#publicpath). {% /callout %} -Refer to the [Bundler > Loaders](/docs/bundler/loaders#file) page for more complete documentation on the file loader. +Refer to the [Bundler > Loaders](https://bun.sh/docs/bundler/loaders#file) page for more complete documentation on the file loader. ### Plugins -The behavior described in this table can be overridden or extended with [plugins](/docs/bundler/plugins). Refer to the [Bundler > Loaders](/docs/bundler/plugins) page for complete documentation. +The behavior described in this table can be overridden or extended with [plugins](https://bun.sh/docs/bundler/plugins). Refer to the [Bundler > Loaders](https://bun.sh/docs/bundler/plugins) page for complete documentation. ## API @@ -490,7 +490,7 @@ n/a {% /codetabs %} -Bun implements a universal plugin system for both Bun's runtime and bundler. Refer to the [plugin documentation](/docs/bundler/plugins) for complete documentation. +Bun implements a universal plugin system for both Bun's runtime and bundler. Refer to the [plugin documentation](https://bun.sh/docs/bundler/plugins) for complete documentation. - SQLite -- [`bun:sqlite`](/docs/api/sqlite) +- [`bun:sqlite`](https://bun.sh/docs/api/sqlite) --- - FFI -- [`bun:ffi`](/docs/api/ffi) +- [`bun:ffi`](https://bun.sh/docs/api/ffi) --- - Testing -- [`bun:test`](/docs/cli/test) +- [`bun:test`](https://bun.sh/docs/cli/test) --- - Node-API -- [`Node-API`](/docs/api/node-api) +- [`Node-API`](https://bun.sh/docs/api/node-api) --- - Glob -- [`Bun.Glob`](/docs/api/glob) +- [`Bun.Glob`](https://bun.sh/docs/api/glob) --- - Utilities -- [`Bun.version`](/docs/api/utils#bun-version) - [`Bun.revision`](/docs/api/utils#bun-revision) - [`Bun.env`](/docs/api/utils#bun-env) - [`Bun.main`](/docs/api/utils#bun-main) - [`Bun.sleep()`](/docs/api/utils#bun-sleep) - [`Bun.sleepSync()`](/docs/api/utils#bun-sleepsync) - [`Bun.which()`](/docs/api/utils#bun-which) - [`Bun.peek()`](/docs/api/utils#bun-peek) - [`Bun.openInEditor()`](/docs/api/utils#bun-openineditor) - [`Bun.deepEquals()`](/docs/api/utils#bun-deepequals) - [`Bun.escapeHTML()`](/docs/api/utils#bun-escapehtml) - [`Bun.fileURLToPath()`](/docs/api/utils#bun-fileurltopath) - [`Bun.pathToFileURL()`](/docs/api/utils#bun-pathtofileurl) - [`Bun.gzipSync()`](/docs/api/utils#bun-gzipsync) - [`Bun.gunzipSync()`](/docs/api/utils#bun-gunzipsync) - [`Bun.deflateSync()`](/docs/api/utils#bun-deflatesync) - [`Bun.inflateSync()`](/docs/api/utils#bun-inflatesync) - [`Bun.inspect()`](/docs/api/utils#bun-inspect) - [`Bun.nanoseconds()`](/docs/api/utils#bun-nanoseconds) - [`Bun.readableStreamTo*()`](/docs/api/utils#bun-readablestreamto) - [`Bun.resolveSync()`](/docs/api/utils#bun-resolvesync) +- [`Bun.version`](https://bun.sh/docs/api/utils#bun-version) + [`Bun.revision`](https://bun.sh/docs/api/utils#bun-revision) + [`Bun.env`](https://bun.sh/docs/api/utils#bun-env) + [`Bun.main`](https://bun.sh/docs/api/utils#bun-main) + [`Bun.sleep()`](https://bun.sh/docs/api/utils#bun-sleep) + [`Bun.sleepSync()`](https://bun.sh/docs/api/utils#bun-sleepsync) + [`Bun.which()`](https://bun.sh/docs/api/utils#bun-which) + [`Bun.peek()`](https://bun.sh/docs/api/utils#bun-peek) + [`Bun.openInEditor()`](https://bun.sh/docs/api/utils#bun-openineditor) + [`Bun.deepEquals()`](https://bun.sh/docs/api/utils#bun-deepequals) + [`Bun.escapeHTML()`](https://bun.sh/docs/api/utils#bun-escapehtml) + [`Bun.fileURLToPath()`](https://bun.sh/docs/api/utils#bun-fileurltopath) + [`Bun.pathToFileURL()`](https://bun.sh/docs/api/utils#bun-pathtofileurl) + [`Bun.gzipSync()`](https://bun.sh/docs/api/utils#bun-gzipsync) + [`Bun.gunzipSync()`](https://bun.sh/docs/api/utils#bun-gunzipsync) + [`Bun.deflateSync()`](https://bun.sh/docs/api/utils#bun-deflatesync) + [`Bun.inflateSync()`](https://bun.sh/docs/api/utils#bun-inflatesync) + [`Bun.inspect()`](https://bun.sh/docs/api/utils#bun-inspect) + [`Bun.nanoseconds()`](https://bun.sh/docs/api/utils#bun-nanoseconds) + [`Bun.readableStreamTo*()`](https://bun.sh/docs/api/utils#bun-readablestreamto) + [`Bun.resolveSync()`](https://bun.sh/docs/api/utils#bun-resolvesync) {% /table %} diff --git a/docs/runtime/env.md b/docs/runtime/env.md index c26931c8142a6..54cda70d3d866 100644 --- a/docs/runtime/env.md +++ b/docs/runtime/env.md @@ -179,7 +179,7 @@ These environment variables are read by Bun and configure aspects of its behavio --- - `BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD` -- If `BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD=1`, then `bun --watch` will not clear the console on reload +- If `BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD=true`, then `bun --watch` will not clear the console on reload --- diff --git a/docs/runtime/index.md b/docs/runtime/index.md index 4c557cab49bf6..d2dfe8e441af4 100644 --- a/docs/runtime/index.md +++ b/docs/runtime/index.md @@ -6,7 +6,7 @@ Bun is designed to start fast and run fast. Its transpiler and runtime are writt {% image src="/images/bun-run-speed.jpeg" caption="Bun vs Node.js vs Deno running Hello World" /%} - + Performance sensitive APIs like `Buffer`, `fetch`, and `Response` are heavily profiled and optimized. Under the hood Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. It starts and runs faster than V8, the engine used by Node.js and Chromium-based browsers. @@ -21,7 +21,7 @@ $ bun index.ts $ bun index.tsx ``` -Some aspects of Bun's runtime behavior are affected by the contents of your `tsconfig.json` file. Refer to [Runtime > TypeScript](/docs/runtime/typescript) page for details. +Some aspects of Bun's runtime behavior are affected by the contents of your `tsconfig.json` file. Refer to [Runtime > TypeScript](https://bun.sh/docs/runtime/typescript) page for details. @@ -122,17 +122,17 @@ $ bun run ./my-wasm-app.whatever ## Node.js compatibility -Long-term, Bun aims for complete Node.js compatibility. Most Node.js packages already work with Bun out of the box, but certain low-level APIs like `dgram` are still unimplemented. Track the current compatibility status at [Ecosystem > Node.js](/docs/runtime/nodejs-apis). +Long-term, Bun aims for complete Node.js compatibility. Most Node.js packages already work with Bun out of the box, but certain low-level APIs like `dgram` are still unimplemented. Track the current compatibility status at [Ecosystem > Node.js](https://bun.sh/docs/runtime/nodejs-apis). Bun implements the Node.js module resolution algorithm, so dependencies can still be managed with `package.json`, `node_modules`, and CommonJS-style imports. {% callout %} -**Note** — We recommend using Bun's [built-in package manager](/docs/cli/install) for a performance boost over other npm clients. +**Note** — We recommend using Bun's [built-in package manager](https://bun.sh/docs/cli/install) for a performance boost over other npm clients. {% /callout %} ## Web APIs - + Some Web APIs aren't relevant in the context of a server-first runtime like Bun, such as the [DOM API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API#html_dom_api_interfaces) or [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API). Many others, though, are broadly useful outside of the browser context; when possible, Bun implements these Web-standard APIs instead of introducing new APIs. @@ -237,67 +237,67 @@ Bun exposes a set of Bun-specific APIs on the `Bun` global object and through a --- -- [HTTP](/docs/api/http) +- [HTTP](https://bun.sh/docs/api/http) - `Bun.serve` --- -- [File I/O](/docs/api/file-io) +- [File I/O](https://bun.sh/docs/api/file-io) - `Bun.file` `Bun.write` --- -- [Processes](/docs/api/spawn) +- [Processes](https://bun.sh/docs/api/spawn) - `Bun.spawn` `Bun.spawnSync` --- -- [TCP](/docs/api/tcp) +- [TCP](https://bun.sh/docs/api/tcp) - `Bun.listen` `Bun.connect` --- -- [Transpiler](/docs/api/transpiler) +- [Transpiler](https://bun.sh/docs/api/transpiler) - `Bun.Transpiler` --- -- [Routing](/docs/api/file-system-router) +- [Routing](https://bun.sh/docs/api/file-system-router) - `Bun.FileSystemRouter` --- -- [HTMLRewriter](/docs/api/html-rewriter) +- [HTMLRewriter](https://bun.sh/docs/api/html-rewriter) - `HTMLRewriter` --- -- [Utils](/docs/api/utils) +- [Utils](https://bun.sh/docs/api/utils) - `Bun.peek` `Bun.which` --- -- [SQLite](/docs/api/sqlite) +- [SQLite](https://bun.sh/docs/api/sqlite) - `bun:sqlite` --- -- [FFI](/docs/api/ffi) +- [FFI](https://bun.sh/docs/api/ffi) - `bun:ffi` --- -- [DNS](/docs/api/dns) +- [DNS](https://bun.sh/docs/api/dns) - `bun:dns` --- -- [Testing](/docs/api/test) +- [Testing](https://bun.sh/docs/api/test) - `bun:test` --- -- [Node-API](/docs/api/node-api) +- [Node-API](https://bun.sh/docs/api/node-api) - `Node-API` --- @@ -306,4 +306,4 @@ Bun exposes a set of Bun-specific APIs on the `Bun` global object and through a ## Plugins -Support for additional file types can be implemented with plugins. Refer to [Runtime > Plugins](/docs/bundler/plugins) for full documentation. +Support for additional file types can be implemented with plugins. Refer to [Runtime > Plugins](https://bun.sh/docs/bundler/plugins) for full documentation. diff --git a/docs/runtime/jsx.md b/docs/runtime/jsx.md index 31a61652bb1fb..ab08255599704 100644 --- a/docs/runtime/jsx.md +++ b/docs/runtime/jsx.md @@ -14,7 +14,7 @@ console.log(); ## Configuration -Bun reads your `tsconfig.json` or `jsconfig.json` configuration files to determines how to perform the JSX transform internally. To avoid using either of these, the following options can also be defined in [`bunfig.toml`](/docs/runtime/bunfig). +Bun reads your `tsconfig.json` or `jsconfig.json` configuration files to determines how to perform the JSX transform internally. To avoid using either of these, the following options can also be defined in [`bunfig.toml`](https://bun.sh/docs/runtime/bunfig). The following compiler options are respected. @@ -197,7 +197,7 @@ The module from which the component factory function (`createElement`, `jsx`, `j - ```jsonc { - "jsx": "react" + "jsx": "react", // jsxImportSource is not defined // default to "react" } @@ -213,7 +213,7 @@ The module from which the component factory function (`createElement`, `jsx`, `j - ```jsonc { "jsx": "react-jsx", - "jsxImportSource": "preact" + "jsxImportSource": "preact", } ``` @@ -227,7 +227,7 @@ The module from which the component factory function (`createElement`, `jsx`, `j - ```jsonc { "jsx": "react-jsxdev", - "jsxImportSource": "preact" + "jsxImportSource": "preact", } ``` @@ -263,7 +263,7 @@ All of these values can be set on a per-file basis using _pragmas_. A pragma is - ```jsonc { - "jsxFactory": "h" + "jsxFactory": "h", } ``` @@ -274,7 +274,7 @@ All of these values can be set on a per-file basis using _pragmas_. A pragma is ``` - ```jsonc { - "jsxFragmentFactory": "MyFragment" + "jsxFragmentFactory": "MyFragment", } ``` @@ -285,7 +285,7 @@ All of these values can be set on a per-file basis using _pragmas_. A pragma is ``` - ```jsonc { - "jsxImportSource": "preact" + "jsxImportSource": "preact", } ``` diff --git a/docs/runtime/loaders.md b/docs/runtime/loaders.md index 6b226823d047d..69dd4df0cfea7 100644 --- a/docs/runtime/loaders.md +++ b/docs/runtime/loaders.md @@ -9,7 +9,7 @@ $ bun index.ts $ bun index.tsx ``` -Some aspects of Bun's runtime behavior are affected by the contents of your `tsconfig.json` file. Refer to [Runtime > TypeScript](/docs/runtime/typescript) page for details. +Some aspects of Bun's runtime behavior are affected by the contents of your `tsconfig.json` file. Refer to [Runtime > TypeScript](https://bun.sh/docs/runtime/typescript) page for details. ## JSX @@ -85,15 +85,15 @@ $ bun run ./my-wasm-app.whatever You can import sqlite databases directly into your code. Bun will automatically load the database and return a `Database` object. ```ts -import db from "./my.db" with {type: "sqlite"}; +import db from "./my.db" with { type: "sqlite" }; console.log(db.query("select * from users LIMIT 1").get()); ``` -This uses [`bun:sqlite`](/docs/api/sqlite). +This uses [`bun:sqlite`](https://bun.sh/docs/api/sqlite). ## Custom loaders -Support for additional file types can be implemented with plugins. Refer to [Runtime > Plugins](/docs/bundler/plugins) for full documentation. +Support for additional file types can be implemented with plugins. Refer to [Runtime > Plugins](https://bun.sh/docs/bundler/plugins) for full documentation. %s\n" "${bun_is_at}" - printf "\n" - printf "You should remove this binary and switch it to ./build:\n" - printf ' export PATH="$PATH:%s"\n' $(realpath "$PWD/build") - fi -else - printf "\n" - printf "You should add ./build to your path:\n" - printf ' export PATH="$PATH:%s"\n' $(realpath "$PWD/build") -fi -printf "\n" -printf "To rebuild bun, run '${C_GREEN}bun run build${C_RESET}'\n\n" diff --git a/scripts/update-submodules.ps1 b/scripts/update-submodules.ps1 deleted file mode 100755 index dff2fb292e87d..0000000000000 --- a/scripts/update-submodules.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -param( - [switch]$WebKit = $false -) - -$ErrorActionPreference = 'Stop' -$ScriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent -Push-Location (Join-Path $ScriptDir '..') -try { - $Names = Get-Content .gitmodules | Select-String 'path = (.*)' | ForEach-Object { $_.Matches.Groups[1].Value } - - # we will exclude webkit unless you explicitly clone it yourself (a huge download) - if (!($WebKit) -and (-not (Test-Path "src/bun.js/WebKit/.git"))) { - $Names = $Names | Where-Object { $_ -ne 'src/bun.js/WebKit' } - } - - git submodule update --init --recursive --progress --depth 1 --checkout @NAMES - if ($LASTEXITCODE -ne 0) { - throw "git submodule update failed" - } -} finally { Pop-Location } \ No newline at end of file diff --git a/scripts/update-submodules.sh b/scripts/update-submodules.sh deleted file mode 100755 index 82cd63b23e937..0000000000000 --- a/scripts/update-submodules.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -cd "$(dirname "${BASH_SOURCE[0]}")" -cd .. -NAMES=$(cat .gitmodules | grep 'path = ' | awk '{print $3}') - -if ! [ "$1" == '--webkit' ]; then - # we will exclude webkit unless you explicitly clone it yourself (a huge download) - if [ ! -e "src/bun.js/WebKit/.git" ]; then - NAMES=$(echo "$NAMES" | grep -v 'WebKit') - fi -fi - -set -euxo pipefail -git submodule update --init --recursive --progress --depth=1 --checkout $NAMES diff --git a/scripts/vs-shell.ps1 b/scripts/vs-shell.ps1 new file mode 100755 index 0000000000000..35694cd1f6a61 --- /dev/null +++ b/scripts/vs-shell.ps1 @@ -0,0 +1,46 @@ +# Ensures that commands run in a Visual Studio environment. +# This is required to run commands like cmake and ninja on Windows. + +$ErrorActionPreference = "Stop" + +if($env:VSINSTALLDIR -eq $null) { + Write-Host "Loading Visual Studio environment, this may take a second..." + + $vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" + if (!(Test-Path $vswhere)) { + throw "Command not found: vswhere (did you install Visual Studio?)" + } + + $vsDir = (& $vswhere -prerelease -latest -property installationPath) + if ($vsDir -eq $null) { + $vsDir = Get-ChildItem -Path "C:\Program Files\Microsoft Visual Studio\2022" -Directory + if ($vsDir -eq $null) { + throw "Visual Studio directory not found." + } + $vsDir = $vsDir.FullName + } + + Push-Location $vsDir + try { + $vsShell = (Join-Path -Path $vsDir -ChildPath "Common7\Tools\Launch-VsDevShell.ps1") + . $vsShell -Arch amd64 -HostArch amd64 + } finally { + Pop-Location + } +} + +if($env:VSCMD_ARG_TGT_ARCH -eq "x86") { + throw "Visual Studio environment is targeting 32 bit, but only 64 bit is supported." +} + +if ($args.Count -gt 0) { + $command = $args[0] + $commandArgs = @() + if ($args.Count -gt 1) { + $commandArgs = @($args[1..($args.Count - 1)] | % {$_}) + } + + Write-Host "$ $command $commandArgs" + & $command $commandArgs + exit $LASTEXITCODE +} diff --git a/scripts/write-versions.sh b/scripts/write-versions.sh index 5724ee20e5a99..389acf702c209 100755 --- a/scripts/write-versions.sh +++ b/scripts/write-versions.sh @@ -1,7 +1,7 @@ #!/bin/bash -set -euxo pipefail +set -exo pipefail -WEBKIT_VERSION=$(git rev-parse HEAD:./src/bun.js/WebKit) +WEBKIT_VERSION=$(grep 'set(WEBKIT_TAG' "CMakeLists.txt" | awk '{print $2}' | cut -f 1 -d ')') MIMALLOC_VERSION=$(git rev-parse HEAD:./src/deps/mimalloc) LIBARCHIVE_VERSION=$(git rev-parse HEAD:./src/deps/libarchive) PICOHTTPPARSER_VERSION=$(git rev-parse HEAD:./src/deps/picohttpparser) @@ -12,6 +12,7 @@ TINYCC=$(git rev-parse HEAD:./src/deps/tinycc) C_ARES=$(git rev-parse HEAD:./src/deps/c-ares) ZSTD=$(git rev-parse HEAD:./src/deps/zstd) LSHPACK=$(git rev-parse HEAD:./src/deps/ls-hpack) +LIBDEFLATE=$(git rev-parse HEAD:./src/deps/libdeflate) rm -rf src/generated_versions_list.zig echo "// AUTO-GENERATED FILE. Created via .scripts/write-versions.sh" >src/generated_versions_list.zig @@ -26,6 +27,7 @@ echo "pub const zlib = \"$ZLIB_VERSION\";" >>src/generated_versions_list.zig echo "pub const tinycc = \"$TINYCC\";" >>src/generated_versions_list.zig echo "pub const lolhtml = \"$LOLHTML\";" >>src/generated_versions_list.zig echo "pub const c_ares = \"$C_ARES\";" >>src/generated_versions_list.zig +echo "pub const libdeflate = \"$LIBDEFLATE\";" >>src/generated_versions_list.zig echo "pub const zstd = \"$ZSTD\";" >>src/generated_versions_list.zig echo "pub const lshpack = \"$LSHPACK\";" >>src/generated_versions_list.zig echo "" >>src/generated_versions_list.zig diff --git a/src/ArenaAllocator.zig b/src/ArenaAllocator.zig index fcc99ea15a3e8..4c62038cabc63 100644 --- a/src/ArenaAllocator.zig +++ b/src/ArenaAllocator.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const assert = @import("root").bun.assert; +const bun = @import("root").bun; +const assert = bun.assert; const mem = std.mem; const Allocator = std.mem.Allocator; diff --git a/src/Global.zig b/src/Global.zig index 56c0dd36cbee5..d3becfed78495 100644 --- a/src/Global.zig +++ b/src/Global.zig @@ -101,11 +101,6 @@ pub fn runExitCallbacks() void { on_exit_callbacks.items.len = 0; } -/// Flushes stdout and stderr and exits with the given code. -pub fn exit(code: u8) noreturn { - exitWide(@as(u32, code)); -} - var is_exiting = std.atomic.Value(bool).init(false); export fn bun_is_exiting() c_int { return @intFromBool(isExiting()); @@ -114,36 +109,25 @@ pub fn isExiting() bool { return is_exiting.load(.monotonic); } -pub fn exitWide(code: u32) noreturn { +/// Flushes stdout and stderr (in exit/quick_exit callback) and exits with the given code. +pub fn exit(code: u32) noreturn { is_exiting.store(true, .monotonic); - if (comptime Environment.isMac) { - std.c.exit(@bitCast(code)); - } - bun.C.quick_exit(@bitCast(code)); -} + // If we are crashing, allow the crash handler to finish it's work. + bun.crash_handler.sleepForeverIfAnotherThreadIsCrashing(); -pub fn raiseIgnoringPanicHandler(sig: anytype) noreturn { - if (comptime @TypeOf(sig) == bun.SignalCode) { - return raiseIgnoringPanicHandler(@intFromEnum(sig)); + switch (Environment.os) { + .mac => std.c.exit(@bitCast(code)), + else => bun.C.quick_exit(@bitCast(code)), } +} +pub fn raiseIgnoringPanicHandler(sig: bun.SignalCode) noreturn { Output.flush(); - - if (!Environment.isWindows) { - if (sig >= 1 and sig != std.posix.SIG.STOP and sig != std.posix.SIG.KILL) { - const act = std.posix.Sigaction{ - .handler = .{ .sigaction = @ptrCast(@alignCast(std.posix.SIG.DFL)) }, - .mask = std.posix.empty_sigset, - .flags = 0, - }; - std.posix.sigaction(@intCast(sig), &act, null) catch {}; - } - } - Output.Source.Stdio.restore(); - _ = std.c.raise(sig); + bun.crash_handler.resetSegfaultHandler(); + _ = std.c.raise(@intFromEnum(sig)); std.c.abort(); } @@ -171,22 +155,9 @@ pub inline fn configureAllocator(_: AllocatorConfiguration) void { // if (!config.long_running) Mimalloc.mi_option_set(Mimalloc.mi_option_reset_delay, 0); } -pub fn panic(comptime fmt: string, args: anytype) noreturn { - @setCold(true); - if (comptime Environment.isWasm) { - Output.printErrorln(fmt, args); - Output.flush(); - @panic(fmt); - } else { - Output.prettyErrorln(fmt, args); - Output.flush(); - std.debug.panic(fmt, args); - } -} - pub fn notimpl() noreturn { @setCold(true); - Global.panic("Not implemented yet!!!!!", .{}); + Output.panic("Not implemented yet!!!!!", .{}); } // Make sure we always print any leftover @@ -223,7 +194,6 @@ pub const BunInfo = struct { }; pub const user_agent = "Bun/" ++ Global.package_json_version; - pub export const Bun__userAgent: [*:0]const u8 = Global.user_agent; comptime { diff --git a/src/Progress.zig b/src/Progress.zig index da05df7f24b42..822feb7576b5c 100644 --- a/src/Progress.zig +++ b/src/Progress.zig @@ -18,7 +18,7 @@ const std = @import("std"); const builtin = @import("builtin"); const windows = std.os.windows; const testing = std.testing; -const assert = std.debug.assert; +const assert = (std.debug).assert; const Progress = @This(); /// `null` if the current node (and its children) should @@ -246,7 +246,7 @@ fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void { end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len; end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; } else if (builtin.os.tag == .windows) winapi: { - std.debug.assert(p.is_windows_terminal); + assert(p.is_windows_terminal); var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) { @@ -357,7 +357,7 @@ fn refreshWithHeldLock(self: *Progress) void { pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { const file = self.terminal orelse { - std.debug.print(format, args); + (std.debug).print(format, args); return; }; self.refresh(); diff --git a/src/StandaloneModuleGraph.zig b/src/StandaloneModuleGraph.zig index a7441cb75774b..9e9827aa00e6e 100644 --- a/src/StandaloneModuleGraph.zig +++ b/src/StandaloneModuleGraph.zig @@ -9,6 +9,8 @@ const Output = bun.Output; const Global = bun.Global; const Environment = bun.Environment; const Syscall = bun.sys; +const SourceMap = bun.sourcemap; +const StringPointer = bun.StringPointer; const w = std.os.windows; @@ -33,6 +35,8 @@ pub const StandaloneModuleGraph = struct { pub const base_public_path = targetBasePublicPath(Environment.os, ""); + pub const base_public_path_with_default_suffix = targetBasePublicPath(Environment.os, "root/"); + pub fn targetBasePublicPath(target: Environment.OperatingSystem, comptime suffix: [:0]const u8) [:0]const u8 { return switch (target) { .windows => "B:/~BUN/" ++ suffix, @@ -54,6 +58,11 @@ pub const StandaloneModuleGraph = struct { if (!isBunStandaloneFilePath(base_path)) { return null; } + + return this.findAssumeStandalonePath(name); + } + + pub fn findAssumeStandalonePath(this: *const StandaloneModuleGraph, name: []const u8) ?*File { if (Environment.isWindows) { var normalized_buf: bun.PathBuffer = undefined; const normalized = bun.path.platformToPosixBuf(u8, name, &normalized_buf); @@ -64,63 +73,141 @@ pub const StandaloneModuleGraph = struct { pub const CompiledModuleGraphFile = struct { name: Schema.StringPointer = .{}, - loader: bun.options.Loader = .file, contents: Schema.StringPointer = .{}, sourcemap: Schema.StringPointer = .{}, + encoding: Encoding = .latin1, + loader: bun.options.Loader = .file, + }; + + pub const Encoding = enum(u8) { + binary = 0, + + latin1 = 1, + + // Not used yet. + utf8 = 2, }; pub const File = struct { name: []const u8 = "", loader: bun.options.Loader, - contents: []const u8 = "", + contents: [:0]const u8 = "", sourcemap: LazySourceMap, - blob_: ?*bun.JSC.WebCore.Blob = null, + cached_blob: ?*bun.JSC.WebCore.Blob = null, + encoding: Encoding = .binary, + wtf_string: bun.String = bun.String.empty, + + pub fn lessThanByIndex(ctx: []const File, lhs_i: u32, rhs_i: u32) bool { + const lhs = ctx[lhs_i]; + const rhs = ctx[rhs_i]; + return bun.strings.cmpStringsAsc({}, lhs.name, rhs.name); + } + + pub fn toWTFString(this: *File) bun.String { + if (this.wtf_string.isEmpty()) { + switch (this.encoding) { + .binary, .utf8 => { + this.wtf_string = bun.String.createUTF8(this.contents); + }, + .latin1 => { + this.wtf_string = bun.String.createStaticExternal(this.contents, true); + }, + } + } + + // We don't want this to free. + return this.wtf_string.dupeRef(); + } pub fn blob(this: *File, globalObject: *bun.JSC.JSGlobalObject) *bun.JSC.WebCore.Blob { - if (this.blob_ == null) { - var store = bun.JSC.WebCore.Blob.Store.init(@constCast(this.contents), bun.default_allocator); + if (this.cached_blob == null) { + const store = bun.JSC.WebCore.Blob.Store.init(@constCast(this.contents), bun.default_allocator); // make it never free store.ref(); - var blob_ = bun.default_allocator.create(bun.JSC.WebCore.Blob) catch bun.outOfMemory(); - blob_.* = bun.JSC.WebCore.Blob.initWithStore(store, globalObject); - blob_.allocator = bun.default_allocator; + const b = bun.JSC.WebCore.Blob.initWithStore(store, globalObject).new(); + b.allocator = bun.default_allocator; if (bun.http.MimeType.byExtensionNoDefault(bun.strings.trimLeadingChar(std.fs.path.extension(this.name), '.'))) |mime| { store.mime_type = mime; - blob_.content_type = mime.value; - blob_.content_type_was_set = true; - blob_.content_type_allocated = false; + b.content_type = mime.value; + b.content_type_was_set = true; + b.content_type_allocated = false; } + // The real name goes here: store.data.bytes.stored_name = bun.PathString.init(this.name); - this.blob_ = blob_; + // The pretty name goes here: + if (strings.hasPrefixComptime(this.name, base_public_path_with_default_suffix)) { + b.name = bun.String.createUTF8(this.name[base_public_path_with_default_suffix.len..]); + } else if (this.name.len > 0) { + b.name = bun.String.createUTF8(this.name); + } + + this.cached_blob = b; } - return this.blob_.?; + return this.cached_blob.?; } }; pub const LazySourceMap = union(enum) { - compressed: []const u8, - decompressed: bun.sourcemap, - - pub fn load(this: *LazySourceMap, log: *bun.logger.Log, allocator: std.mem.Allocator) !*bun.sourcemap { - if (this.* == .decompressed) return &this.decompressed; - - var decompressed = try allocator.alloc(u8, bun.zstd.getDecompressedSize(this.compressed)); - const result = bun.zstd.decompress(decompressed, this.compressed); - if (result == .err) { - allocator.free(decompressed); - log.addError(null, bun.logger.Loc.Empty, bun.span(result.err)) catch unreachable; - return error.@"Failed to decompress sourcemap"; - } - errdefer allocator.free(decompressed); - const bytes = decompressed[0..result.success]; + serialized: SerializedSourceMap, + parsed: *SourceMap.ParsedSourceMap, + none, + + /// It probably is not possible to run two decoding jobs on the same file + var init_lock: bun.Lock = .{}; + + pub fn load(this: *LazySourceMap) ?*SourceMap.ParsedSourceMap { + init_lock.lock(); + defer init_lock.unlock(); + + return switch (this.*) { + .none => null, + .parsed => |map| map, + .serialized => |serialized| { + var stored = switch (SourceMap.Mapping.parse( + bun.default_allocator, + serialized.mappingVLQ(), + null, + std.math.maxInt(i32), + std.math.maxInt(i32), + )) { + .success => |x| x, + .fail => { + this.* = .none; + return null; + }, + }; + + const source_files = serialized.sourceFileNames(); + const slices = bun.default_allocator.alloc(?[]u8, source_files.len * 2) catch bun.outOfMemory(); + + const file_names: [][]const u8 = @ptrCast(slices[0..source_files.len]); + const decompressed_contents_slice = slices[source_files.len..][0..source_files.len]; + for (file_names, source_files) |*dest, src| { + dest.* = src.slice(serialized.bytes); + } - this.* = .{ .decompressed = try bun.sourcemap.parse(allocator, &bun.logger.Source.initPathString("sourcemap.json", bytes), log) }; - return &this.decompressed; + @memset(decompressed_contents_slice, null); + + const data = bun.new(SerializedSourceMap.Loaded, .{ + .map = serialized, + .decompressed_files = decompressed_contents_slice, + }); + + stored.external_source_names = file_names; + stored.underlying_provider = .{ .data = @truncate(@intFromPtr(data)) }; + stored.is_standalone_module_graph = true; + + const parsed = stored.new(); // allocate this on the heap + parsed.ref(); // never free + this.* = .{ .parsed = parsed }; + return parsed; + }, + }; } }; @@ -132,7 +219,7 @@ pub const StandaloneModuleGraph = struct { const trailer = "\n---- Bun! ----\n"; - pub fn fromBytes(allocator: std.mem.Allocator, raw_bytes: []const u8, offsets: Offsets) !StandaloneModuleGraph { + pub fn fromBytes(allocator: std.mem.Allocator, raw_bytes: []u8, offsets: Offsets) !StandaloneModuleGraph { if (raw_bytes.len == 0) return StandaloneModuleGraph{ .files = bun.StringArrayHashMap(File).init(allocator), }; @@ -148,18 +235,23 @@ pub const StandaloneModuleGraph = struct { try modules.ensureTotalCapacity(modules_list.len); for (modules_list) |module| { modules.putAssumeCapacity( - sliceTo(raw_bytes, module.name), + sliceToZ(raw_bytes, module.name), File{ - .name = sliceTo(raw_bytes, module.name), + .name = sliceToZ(raw_bytes, module.name), .loader = module.loader, - .contents = sliceTo(raw_bytes, module.contents), - .sourcemap = LazySourceMap{ - .compressed = sliceTo(raw_bytes, module.sourcemap), - }, + .contents = sliceToZ(raw_bytes, module.contents), + .sourcemap = if (module.sourcemap.length > 0) + .{ .serialized = .{ + .bytes = @alignCast(sliceTo(raw_bytes, module.sourcemap)), + } } + else + .none, }, ); } + modules.lockPointers(); // make the pointers stable forever + return StandaloneModuleGraph{ .bytes = raw_bytes[0..offsets.byte_count], .files = modules, @@ -173,18 +265,29 @@ pub const StandaloneModuleGraph = struct { return bytes[ptr.offset..][0..ptr.length]; } + fn sliceToZ(bytes: []const u8, ptr: bun.StringPointer) [:0]const u8 { + if (ptr.length == 0) return ""; + + return bytes[ptr.offset..][0..ptr.length :0]; + } + pub fn toBytes(allocator: std.mem.Allocator, prefix: []const u8, output_files: []const bun.options.OutputFile) ![]u8 { var serialize_trace = bun.tracy.traceNamed(@src(), "StandaloneModuleGraph.serialize"); defer serialize_trace.end(); + var entry_point_id: ?usize = null; var string_builder = bun.StringBuilder{}; var module_count: usize = 0; for (output_files, 0..) |output_file, i| { - string_builder.count(output_file.dest_path); - string_builder.count(prefix); + string_builder.countZ(output_file.dest_path); + string_builder.countZ(prefix); if (output_file.value == .buffer) { if (output_file.output_kind == .sourcemap) { - string_builder.cap += bun.zstd.compressBound(output_file.value.buffer.bytes.len); + // This is an over-estimation to ensure that we allocate + // enough memory for the source-map contents. Calculating + // the exact amount is not possible without allocating as it + // involves a JSON parser. + string_builder.cap += output_file.value.buffer.bytes.len * 2; } else { if (entry_point_id == null) { if (output_file.output_kind == .@"entry-point") { @@ -192,7 +295,7 @@ pub const StandaloneModuleGraph = struct { } } - string_builder.count(output_file.value.buffer.bytes); + string_builder.countZ(output_file.value.buffer.bytes); module_count += 1; } } @@ -203,16 +306,19 @@ pub const StandaloneModuleGraph = struct { string_builder.cap += @sizeOf(CompiledModuleGraphFile) * output_files.len; string_builder.cap += trailer.len; string_builder.cap += 16; - - { - var offsets_ = Offsets{}; - string_builder.cap += std.mem.asBytes(&offsets_).len; - } + string_builder.cap += @sizeOf(Offsets); try string_builder.allocate(allocator); var modules = try std.ArrayList(CompiledModuleGraphFile).initCapacity(allocator, module_count); + var source_map_header_list = std.ArrayList(u8).init(allocator); + defer source_map_header_list.deinit(); + var source_map_string_list = std.ArrayList(u8).init(allocator); + defer source_map_string_list.deinit(); + var source_map_arena = bun.ArenaAllocator.init(allocator); + defer source_map_arena.deinit(); + for (output_files) |output_file| { if (output_file.output_kind == .sourcemap) { continue; @@ -225,25 +331,36 @@ pub const StandaloneModuleGraph = struct { const dest_path = bun.strings.removeLeadingDotSlash(output_file.dest_path); var module = CompiledModuleGraphFile{ - .name = string_builder.fmtAppendCount("{s}{s}", .{ + .name = string_builder.fmtAppendCountZ("{s}{s}", .{ prefix, dest_path, }), .loader = output_file.loader, - .contents = string_builder.appendCount(output_file.value.buffer.bytes), + .contents = string_builder.appendCountZ(output_file.value.buffer.bytes), + .encoding = switch (output_file.loader) { + .js, .jsx, .ts, .tsx => .latin1, + else => .binary, + }, }; if (output_file.source_map_index != std.math.maxInt(u32)) { - const remaining_slice = string_builder.allocatedSlice()[string_builder.len..]; - const compressed_result = bun.zstd.compress(remaining_slice, output_files[output_file.source_map_index].value.buffer.bytes, 1); - if (compressed_result == .err) { - bun.Output.panic("Unexpected error compressing sourcemap: {s}", .{bun.span(compressed_result.err)}); - } - module.sourcemap = string_builder.add(compressed_result.success); + defer source_map_header_list.clearRetainingCapacity(); + defer source_map_string_list.clearRetainingCapacity(); + _ = source_map_arena.reset(.retain_capacity); + try serializeJsonSourceMapForStandalone( + &source_map_header_list, + &source_map_string_list, + source_map_arena.allocator(), + output_files[output_file.source_map_index].value.buffer.bytes, + ); + module.sourcemap = string_builder.addConcat(&.{ + source_map_header_list.items, + source_map_string_list.items, + }); } modules.appendAssumeCapacity(module); } - var offsets = Offsets{ + const offsets = Offsets{ .entry_point_id = @as(u32, @truncate(entry_point_id.?)), .modules_ptr = string_builder.appendCount(std.mem.sliceAsBytes(modules.items)), .byte_count = string_builder.len, @@ -252,7 +369,20 @@ pub const StandaloneModuleGraph = struct { _ = string_builder.append(std.mem.asBytes(&offsets)); _ = string_builder.append(trailer); - return string_builder.ptr.?[0..string_builder.len]; + const output_bytes = string_builder.ptr.?[0..string_builder.len]; + + if (comptime Environment.isDebug) { + // An expensive sanity check: + var graph = try fromBytes(allocator, @alignCast(output_bytes), offsets); + defer { + graph.files.unlockPointers(); + graph.files.deinit(); + } + + bun.assert_eql(graph.files.count(), modules.items.len); + } + + return output_bytes; } const page_size = if (Environment.isLinux and Environment.isAarch64) @@ -288,7 +418,7 @@ pub const StandaloneModuleGraph = struct { out_buf[zname.len] = 0; const out = out_buf[0..zname.len :0]; - bun.copyFile(in, out) catch |err| { + bun.copyFile(in, out).unwrap() catch |err| { Output.prettyErrorln("error: failed to copy bun executable into temporary file: {s}", .{@errorName(err)}); Global.exit(1); }; @@ -388,7 +518,7 @@ pub const StandaloneModuleGraph = struct { defer _ = Syscall.close(self_fd); - bun.copyFile(self_fd.cast(), fd.cast()) catch |err| { + bun.copyFile(self_fd.cast(), fd.cast()).unwrap() catch |err| { Output.prettyErrorln("error: failed to copy bun executable into temporary file: {s}", .{@errorName(err)}); cleanup(zname, fd); Global.exit(1); @@ -778,4 +908,172 @@ pub const StandaloneModuleGraph = struct { else => @compileError("TODO"), } } + + /// Source map serialization in the bundler is specially designed to be + /// loaded in memory as is. Source contents are compressed with ZSTD to + /// reduce the file size, and mappings are stored as uncompressed VLQ. + pub const SerializedSourceMap = struct { + bytes: []const u8, + + /// Following the header bytes: + /// - source_files_count number of StringPointer, file names + /// - source_files_count number of StringPointer, zstd compressed contents + /// - the mapping data, `map_vlq_length` bytes + /// - all the StringPointer contents + pub const Header = extern struct { + source_files_count: u32, + map_bytes_length: u32, + }; + + pub fn header(map: SerializedSourceMap) *align(1) const Header { + return @ptrCast(map.bytes.ptr); + } + + pub fn mappingVLQ(map: SerializedSourceMap) []const u8 { + const head = map.header(); + const start = @sizeOf(Header) + head.source_files_count * @sizeOf(StringPointer) * 2; + return map.bytes[start..][0..head.map_bytes_length]; + } + + pub fn sourceFileNames(map: SerializedSourceMap) []align(1) const StringPointer { + const head = map.header(); + return @as([*]align(1) const StringPointer, @ptrCast(map.bytes[@sizeOf(Header)..]))[0..head.source_files_count]; + } + + fn compressedSourceFiles(map: SerializedSourceMap) []align(1) const StringPointer { + const head = map.header(); + return @as([*]align(1) const StringPointer, @ptrCast(map.bytes[@sizeOf(Header)..]))[head.source_files_count..][0..head.source_files_count]; + } + + /// Once loaded, this map stores additional data for keeping track of source code. + pub const Loaded = struct { + map: SerializedSourceMap, + + /// Only decompress source code once! Once a file is decompressed, + /// it is stored here. Decompression failures are stored as an empty + /// string, which will be treated as "no contents". + decompressed_files: []?[]u8, + + pub fn sourceFileContents(this: Loaded, index: usize) ?[]const u8 { + if (this.decompressed_files[index]) |decompressed| { + return if (decompressed.len == 0) null else decompressed; + } + + const compressed_codes = this.map.compressedSourceFiles(); + const compressed_file = compressed_codes[@intCast(index)].slice(this.map.bytes); + const size = bun.zstd.getDecompressedSize(compressed_file); + + const bytes = bun.default_allocator.alloc(u8, size) catch bun.outOfMemory(); + const result = bun.zstd.decompress(bytes, compressed_file); + + if (result == .err) { + bun.Output.warn("Source map decompression error: {s}", .{result.err}); + bun.default_allocator.free(bytes); + this.decompressed_files[index] = ""; + return null; + } + + const data = bytes[0..result.success]; + this.decompressed_files[index] = data; + return data; + } + }; + }; + + pub fn serializeJsonSourceMapForStandalone( + header_list: *std.ArrayList(u8), + string_payload: *std.ArrayList(u8), + arena: std.mem.Allocator, + json_source: []const u8, + ) !void { + const out = header_list.writer(); + const json_src = bun.logger.Source.initPathString("sourcemap.json", json_source); + var log = bun.logger.Log.init(arena); + defer log.deinit(); + + // the allocator given to the JS parser is not respected for all parts + // of the parse, so we need to remember to reset the ast store + bun.JSAst.Expr.Data.Store.reset(); + bun.JSAst.Stmt.Data.Store.reset(); + defer { + bun.JSAst.Expr.Data.Store.reset(); + bun.JSAst.Stmt.Data.Store.reset(); + } + var json = bun.JSON.ParseJSON(&json_src, &log, arena) catch + return error.InvalidSourceMap; + + const mappings_str = json.get("mappings") orelse + return error.InvalidSourceMap; + if (mappings_str.data != .e_string) + return error.InvalidSourceMap; + const sources_content = switch ((json.get("sourcesContent") orelse return error.InvalidSourceMap).data) { + .e_array => |arr| arr, + else => return error.InvalidSourceMap, + }; + const sources_paths = switch ((json.get("sources") orelse return error.InvalidSourceMap).data) { + .e_array => |arr| arr, + else => return error.InvalidSourceMap, + }; + if (sources_content.items.len != sources_paths.items.len) { + return error.InvalidSourceMap; + } + + const map_vlq: []const u8 = mappings_str.data.e_string.slice(arena); + + try out.writeInt(u32, sources_paths.items.len, .little); + try out.writeInt(u32, @intCast(map_vlq.len), .little); + + const string_payload_start_location = @sizeOf(u32) + + @sizeOf(u32) + + @sizeOf(bun.StringPointer) * sources_content.items.len * 2 + // path + source + map_vlq.len; + + for (sources_paths.items.slice()) |item| { + if (item.data != .e_string) + return error.InvalidSourceMap; + + const decoded = try item.data.e_string.stringDecodedUTF8(arena); + + const offset = string_payload.items.len; + try string_payload.appendSlice(decoded); + + const slice = bun.StringPointer{ + .offset = @intCast(offset + string_payload_start_location), + .length = @intCast(string_payload.items.len - offset), + }; + try out.writeInt(u32, slice.offset, .little); + try out.writeInt(u32, slice.length, .little); + } + + for (sources_content.items.slice()) |item| { + if (item.data != .e_string) + return error.InvalidSourceMap; + + const utf8 = try item.data.e_string.stringDecodedUTF8(arena); + defer arena.free(utf8); + + const offset = string_payload.items.len; + + const bound = bun.zstd.compressBound(utf8.len); + try string_payload.ensureUnusedCapacity(bound); + + const unused = string_payload.unusedCapacitySlice(); + const compressed_result = bun.zstd.compress(unused, utf8, 1); + if (compressed_result == .err) { + bun.Output.panic("Unexpected error compressing sourcemap: {s}", .{bun.span(compressed_result.err)}); + } + string_payload.items.len += compressed_result.success; + + const slice = bun.StringPointer{ + .offset = @intCast(offset + string_payload_start_location), + .length = @intCast(string_payload.items.len - offset), + }; + try out.writeInt(u32, slice.offset, .little); + try out.writeInt(u32, slice.length, .little); + } + + try out.writeAll(map_vlq); + + bun.assert(header_list.items.len == string_payload_start_location); + } }; diff --git a/src/StaticHashMap.zig b/src/StaticHashMap.zig index 2e8dd64949338..e45a5823f7b0d 100644 --- a/src/StaticHashMap.zig +++ b/src/StaticHashMap.zig @@ -6,7 +6,8 @@ const mem = std.mem; const math = std.math; const testing = std.testing; -const assert = @import("root").bun.assert; +const bun = @import("root").bun; +const assert = bun.assert; pub fn AutoHashMap(comptime K: type, comptime V: type, comptime max_load_percentage: comptime_int) type { return HashMap(K, V, std.hash_map.AutoContext(K), max_load_percentage); diff --git a/src/allocators.zig b/src/allocators.zig index 31bc747fa6d8c..bd8217163e024 100644 --- a/src/allocators.zig +++ b/src/allocators.zig @@ -199,7 +199,7 @@ pub fn BSSList(comptime ValueType: type, comptime _count: anytype) type { const Self = @This(); allocator: Allocator, - mutex: Mutex = Mutex.init(), + mutex: Mutex = .{}, head: *OverflowBlock = undefined, tail: OverflowBlock = OverflowBlock{}, backing_buf: [count]ValueType = undefined, @@ -288,7 +288,7 @@ pub fn BSSStringList(comptime _count: usize, comptime _item_length: usize) type allocator: Allocator, slice_buf: [count][]const u8 = undefined, slice_buf_used: u16 = 0, - mutex: Mutex = Mutex.init(), + mutex: Mutex = .{}, pub var instance: Self = undefined; var loaded: bool = false; // only need the mutex on append @@ -465,7 +465,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_ index: IndexMap, overflow_list: Overflow = Overflow{}, allocator: Allocator, - mutex: Mutex = Mutex.init(), + mutex: Mutex = .{}, backing_buf: [count]ValueType = undefined, backing_buf_used: u16 = 0, diff --git a/src/analytics/analytics_thread.zig b/src/analytics/analytics_thread.zig index dc0ec59737856..72bbadf6faf4b 100644 --- a/src/analytics/analytics_thread.zig +++ b/src/analytics/analytics_thread.zig @@ -101,7 +101,6 @@ pub const Features = struct { pub var loaders: usize = 0; pub var lockfile_migration_from_package_lock: usize = 0; pub var macros: usize = 0; - pub var origin: usize = 0; pub var shell: usize = 0; pub var spawn: usize = 0; pub var standalone_shell: usize = 0; @@ -110,8 +109,13 @@ pub const Features = struct { pub var tsconfig: usize = 0; pub var virtual_modules: usize = 0; pub var WebSocket: usize = 0; - + pub var no_avx: usize = 0; + pub var no_avx2: usize = 0; + pub var binlinks: usize = 0; pub var builtin_modules = std.enums.EnumSet(bun.JSC.HardcodedModule).initEmpty(); + pub var standalone_executable: usize = 0; + pub var workers_spawned: usize = 0; + pub var workers_terminated: usize = 0; pub fn formatter() Formatter { return Formatter{}; @@ -260,45 +264,64 @@ const platform_arch = if (Environment.isAarch64) Analytics.Architecture.arm else pub const GenerateHeader = struct { pub const GeneratePlatform = struct { var osversion_name: [32]u8 = undefined; - pub fn forMac() Analytics.Platform { + fn forMac() Analytics.Platform { @memset(&osversion_name, 0); var platform = Analytics.Platform{ .os = Analytics.OperatingSystem.macos, .version = &[_]u8{}, .arch = platform_arch }; var len = osversion_name.len - 1; - if (std.c.sysctlbyname("kern.osrelease", &osversion_name, &len, null, 0) == -1) return platform; + // this previously used "kern.osrelease", which was the darwin xnu kernel version + // That is less useful than "kern.osproductversion", which is the macOS version + if (std.c.sysctlbyname("kern.osproductversion", &osversion_name, &len, null, 0) == -1) return platform; platform.version = bun.sliceTo(&osversion_name, 0); return platform; } pub var linux_os_name: std.c.utsname = undefined; - var platform_: ?Analytics.Platform = null; + var platform_: Analytics.Platform = undefined; pub const Platform = Analytics.Platform; - var linux_kernel_version: Semver.Version = undefined; + var run_once = std.once(struct { + fn run() void { + if (comptime Environment.isMac) { + platform_ = forMac(); + } else if (comptime Environment.isPosix) { + platform_ = forLinux(); + + const release = bun.sliceTo(&linux_os_name.release, 0); + const sliced_string = Semver.SlicedString.init(release, release); + const result = Semver.Version.parse(sliced_string); + linux_kernel_version = result.version.min(); + } else if (Environment.isWindows) { + platform_ = Platform{ + .os = Analytics.OperatingSystem.windows, + .version = &[_]u8{}, + .arch = platform_arch, + }; + } + } + }.run); pub fn forOS() Analytics.Platform { - if (platform_ != null) return platform_.?; - - if (comptime Environment.isMac) { - platform_ = forMac(); - return platform_.?; - } else if (comptime Environment.isPosix) { - platform_ = forLinux(); - - const release = bun.sliceTo(&linux_os_name.release, 0); - const sliced_string = Semver.SlicedString.init(release, release); - const result = Semver.Version.parse(sliced_string); - linux_kernel_version = result.version.min(); - } else { - platform_ = Platform{ - .os = Analytics.OperatingSystem.windows, - .version = &[_]u8{}, - .arch = platform_arch, - }; + run_once.call(); + return platform_; + } + + // On macOS 13, tests that use sendmsg_x or recvmsg_x hang. + var use_msgx_on_macos_14_or_later: bool = undefined; + var detectUseMsgXOnMacOS14OrLater_once = std.once(detectUseMsgXOnMacOS14OrLater); + fn detectUseMsgXOnMacOS14OrLater() void { + const version = Semver.Version.parseUTF8(forOS().version); + use_msgx_on_macos_14_or_later = version.valid and version.version.max().major >= 14; + } + pub export fn Bun__doesMacOSVersionSupportSendRecvMsgX() i32 { + if (comptime !Environment.isMac) { + // this should not be used on non-mac platforms. + return 0; } - return platform_.?; + detectUseMsgXOnMacOS14OrLater_once.call(); + return @intFromBool(use_msgx_on_macos_14_or_later); } pub fn kernelVersion() Semver.Version { @@ -307,23 +330,23 @@ pub const GenerateHeader = struct { } _ = forOS(); - // we only care about major, minor, patch so we don't care about the string return linux_kernel_version; } - pub fn forLinux() Analytics.Platform { + fn forLinux() Analytics.Platform { linux_os_name = std.mem.zeroes(@TypeOf(linux_os_name)); _ = std.c.uname(&linux_os_name); + // Confusingly, the "release" tends to contain the kernel version much more frequently than the "version" field. const release = bun.sliceTo(&linux_os_name.release, 0); - const version = std.mem.sliceTo(&linux_os_name.version, @as(u8, 0)); + // Linux DESKTOP-P4LCIEM 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux if (std.mem.indexOf(u8, release, "microsoft") != null) { - return Analytics.Platform{ .os = Analytics.OperatingSystem.wsl, .version = version, .arch = platform_arch }; + return Analytics.Platform{ .os = Analytics.OperatingSystem.wsl, .version = release, .arch = platform_arch }; } - return Analytics.Platform{ .os = Analytics.OperatingSystem.linux, .version = version, .arch = platform_arch }; + return Analytics.Platform{ .os = Analytics.OperatingSystem.linux, .version = release, .arch = platform_arch }; } }; }; diff --git a/src/api/schema.js b/src/api/schema.js index 8451955b9d74e..908a044ab1103 100644 --- a/src/api/schema.js +++ b/src/api/schema.js @@ -3434,157 +3434,159 @@ function encodeGetTestsResponse(message, bb) { } } -export { Loader }; -export { LoaderKeys }; -export { FrameworkEntryPointType }; -export { FrameworkEntryPointTypeKeys }; -export { StackFrameScope }; -export { StackFrameScopeKeys }; -export { decodeStackFrame }; -export { encodeStackFrame }; -export { decodeStackFramePosition }; -export { encodeStackFramePosition }; -export { decodeSourceLine }; -export { encodeSourceLine }; -export { decodeStackTrace }; -export { encodeStackTrace }; -export { decodeJSException }; -export { encodeJSException }; -export { FallbackStep }; -export { FallbackStepKeys }; -export { decodeProblems }; -export { encodeProblems }; -export { decodeRouter }; -export { encodeRouter }; -export { decodeFallbackMessageContainer }; -export { encodeFallbackMessageContainer }; -export { ResolveMode }; -export { ResolveModeKeys }; -export { Target }; -export { TargetKeys }; -export { CSSInJSBehavior }; -export { CSSInJSBehaviorKeys }; -export { JSXRuntime }; -export { JSXRuntimeKeys }; -export { decodeJSX }; -export { encodeJSX }; -export { decodeStringPointer }; -export { encodeStringPointer }; -export { decodeJavascriptBundledModule }; -export { encodeJavascriptBundledModule }; -export { decodeJavascriptBundledPackage }; -export { encodeJavascriptBundledPackage }; -export { decodeJavascriptBundle }; -export { encodeJavascriptBundle }; -export { decodeJavascriptBundleContainer }; -export { encodeJavascriptBundleContainer }; -export { ScanDependencyMode }; -export { ScanDependencyModeKeys }; -export { ModuleImportType }; -export { ModuleImportTypeKeys }; -export { decodeModuleImportRecord }; -export { encodeModuleImportRecord }; -export { decodeModule }; -export { encodeModule }; -export { decodeStringMap }; -export { encodeStringMap }; -export { decodeLoaderMap }; -export { encodeLoaderMap }; -export { DotEnvBehavior }; -export { DotEnvBehaviorKeys }; -export { decodeEnvConfig }; -export { encodeEnvConfig }; -export { decodeLoadedEnvConfig }; -export { encodeLoadedEnvConfig }; -export { decodeFrameworkConfig }; -export { encodeFrameworkConfig }; -export { decodeFrameworkEntryPoint }; -export { encodeFrameworkEntryPoint }; -export { decodeFrameworkEntryPointMap }; -export { encodeFrameworkEntryPointMap }; -export { decodeFrameworkEntryPointMessage }; -export { encodeFrameworkEntryPointMessage }; -export { decodeLoadedFramework }; -export { encodeLoadedFramework }; -export { decodeLoadedRouteConfig }; -export { encodeLoadedRouteConfig }; -export { decodeRouteConfig }; -export { encodeRouteConfig }; -export { decodeTransformOptions }; -export { encodeTransformOptions }; -export { SourceMapMode }; -export { SourceMapModeKeys }; -export { decodeFileHandle }; -export { encodeFileHandle }; -export { decodeTransform }; -export { encodeTransform }; -export { decodeScan }; -export { encodeScan }; -export { decodeScanResult }; -export { encodeScanResult }; -export { decodeScannedImport }; -export { encodeScannedImport }; -export { ImportKind }; -export { ImportKindKeys }; -export { TransformResponseStatus }; -export { TransformResponseStatusKeys }; -export { decodeOutputFile }; -export { encodeOutputFile }; -export { decodeTransformResponse }; -export { encodeTransformResponse }; -export { MessageLevel }; -export { MessageLevelKeys }; -export { decodeLocation }; -export { encodeLocation }; -export { decodeMessageData }; -export { encodeMessageData }; -export { decodeMessageMeta }; -export { encodeMessageMeta }; -export { decodeMessage }; -export { encodeMessage }; -export { decodeLog }; -export { encodeLog }; -export { Reloader }; -export { ReloaderKeys }; -export { WebsocketMessageKind }; -export { WebsocketMessageKindKeys }; -export { WebsocketCommandKind }; -export { WebsocketCommandKindKeys }; -export { decodeWebsocketMessage }; -export { encodeWebsocketMessage }; -export { decodeWebsocketMessageWelcome }; -export { encodeWebsocketMessageWelcome }; -export { decodeWebsocketMessageFileChangeNotification }; -export { encodeWebsocketMessageFileChangeNotification }; -export { decodeWebsocketCommand }; -export { encodeWebsocketCommand }; -export { decodeWebsocketCommandBuild }; -export { encodeWebsocketCommandBuild }; -export { decodeWebsocketCommandManifest }; -export { encodeWebsocketCommandManifest }; -export { decodeWebsocketMessageBuildSuccess }; -export { encodeWebsocketMessageBuildSuccess }; -export { decodeWebsocketMessageBuildFailure }; -export { encodeWebsocketMessageBuildFailure }; -export { decodeWebsocketCommandBuildWithFilePath }; -export { encodeWebsocketCommandBuildWithFilePath }; -export { decodeWebsocketMessageResolveID }; -export { encodeWebsocketMessageResolveID }; -export { decodeNPMRegistry }; -export { encodeNPMRegistry }; -export { decodeNPMRegistryMap }; -export { encodeNPMRegistryMap }; -export { decodeBunInstall }; -export { encodeBunInstall }; -export { decodeClientServerModule }; -export { encodeClientServerModule }; -export { decodeClientServerModuleManifest }; -export { encodeClientServerModuleManifest }; -export { decodeGetTestsRequest }; -export { encodeGetTestsRequest }; -export { TestKind }; -export { TestKindKeys }; -export { decodeTestResponseItem }; -export { encodeTestResponseItem }; -export { decodeGetTestsResponse }; -export { encodeGetTestsResponse }; +export { + CSSInJSBehavior, + CSSInJSBehaviorKeys, + DotEnvBehavior, + DotEnvBehaviorKeys, + FallbackStep, + FallbackStepKeys, + FrameworkEntryPointType, + FrameworkEntryPointTypeKeys, + ImportKind, + ImportKindKeys, + JSXRuntime, + JSXRuntimeKeys, + Loader, + LoaderKeys, + MessageLevel, + MessageLevelKeys, + ModuleImportType, + ModuleImportTypeKeys, + Reloader, + ReloaderKeys, + ResolveMode, + ResolveModeKeys, + ScanDependencyMode, + ScanDependencyModeKeys, + SourceMapMode, + SourceMapModeKeys, + StackFrameScope, + StackFrameScopeKeys, + Target, + TargetKeys, + TestKind, + TestKindKeys, + TransformResponseStatus, + TransformResponseStatusKeys, + WebsocketCommandKind, + WebsocketCommandKindKeys, + WebsocketMessageKind, + WebsocketMessageKindKeys, + decodeBunInstall, + decodeClientServerModule, + decodeClientServerModuleManifest, + decodeEnvConfig, + decodeFallbackMessageContainer, + decodeFileHandle, + decodeFrameworkConfig, + decodeFrameworkEntryPoint, + decodeFrameworkEntryPointMap, + decodeFrameworkEntryPointMessage, + decodeGetTestsRequest, + decodeGetTestsResponse, + decodeJSException, + decodeJSX, + decodeJavascriptBundle, + decodeJavascriptBundleContainer, + decodeJavascriptBundledModule, + decodeJavascriptBundledPackage, + decodeLoadedEnvConfig, + decodeLoadedFramework, + decodeLoadedRouteConfig, + decodeLoaderMap, + decodeLocation, + decodeLog, + decodeMessage, + decodeMessageData, + decodeMessageMeta, + decodeModule, + decodeModuleImportRecord, + decodeNPMRegistry, + decodeNPMRegistryMap, + decodeOutputFile, + decodeProblems, + decodeRouteConfig, + decodeRouter, + decodeScan, + decodeScanResult, + decodeScannedImport, + decodeSourceLine, + decodeStackFrame, + decodeStackFramePosition, + decodeStackTrace, + decodeStringMap, + decodeStringPointer, + decodeTestResponseItem, + decodeTransform, + decodeTransformOptions, + decodeTransformResponse, + decodeWebsocketCommand, + decodeWebsocketCommandBuild, + decodeWebsocketCommandBuildWithFilePath, + decodeWebsocketCommandManifest, + decodeWebsocketMessage, + decodeWebsocketMessageBuildFailure, + decodeWebsocketMessageBuildSuccess, + decodeWebsocketMessageFileChangeNotification, + decodeWebsocketMessageResolveID, + decodeWebsocketMessageWelcome, + encodeBunInstall, + encodeClientServerModule, + encodeClientServerModuleManifest, + encodeEnvConfig, + encodeFallbackMessageContainer, + encodeFileHandle, + encodeFrameworkConfig, + encodeFrameworkEntryPoint, + encodeFrameworkEntryPointMap, + encodeFrameworkEntryPointMessage, + encodeGetTestsRequest, + encodeGetTestsResponse, + encodeJSException, + encodeJSX, + encodeJavascriptBundle, + encodeJavascriptBundleContainer, + encodeJavascriptBundledModule, + encodeJavascriptBundledPackage, + encodeLoadedEnvConfig, + encodeLoadedFramework, + encodeLoadedRouteConfig, + encodeLoaderMap, + encodeLocation, + encodeLog, + encodeMessage, + encodeMessageData, + encodeMessageMeta, + encodeModule, + encodeModuleImportRecord, + encodeNPMRegistry, + encodeNPMRegistryMap, + encodeOutputFile, + encodeProblems, + encodeRouteConfig, + encodeRouter, + encodeScan, + encodeScanResult, + encodeScannedImport, + encodeSourceLine, + encodeStackFrame, + encodeStackFramePosition, + encodeStackTrace, + encodeStringMap, + encodeStringPointer, + encodeTestResponseItem, + encodeTransform, + encodeTransformOptions, + encodeTransformResponse, + encodeWebsocketCommand, + encodeWebsocketCommandBuild, + encodeWebsocketCommandBuildWithFilePath, + encodeWebsocketCommandManifest, + encodeWebsocketMessage, + encodeWebsocketMessageBuildFailure, + encodeWebsocketMessageBuildSuccess, + encodeWebsocketMessageFileChangeNotification, + encodeWebsocketMessageResolveID, + encodeWebsocketMessageWelcome, +}; diff --git a/src/api/schema.zig b/src/api/schema.zig index 666b9adc1f1f2..10e25bb56169a 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -1,5 +1,6 @@ const std = @import("std"); const bun = @import("root").bun; +const js_ast = bun.JSAst; pub const Reader = struct { const Self = @This(); @@ -823,13 +824,20 @@ pub const Api = struct { } }; - pub const StringPointer = packed struct { + /// Represents a slice stored within an externally stored buffer. Safe to serialize. + /// Must be an extern struct to match with `headers-handwritten.h`. + pub const StringPointer = extern struct { /// offset offset: u32 = 0, /// length length: u32 = 0, + comptime { + bun.assert(@alignOf(StringPointer) == @alignOf(u32)); + bun.assert(@sizeOf(StringPointer) == @sizeOf(u64)); + } + pub fn decode(reader: anytype) anyerror!StringPointer { var this = std.mem.zeroes(StringPointer); @@ -842,6 +850,10 @@ pub const Api = struct { try writer.writeInt(this.offset); try writer.writeInt(this.length); } + + pub fn slice(this: @This(), bytes: []const u8) []const u8 { + return bytes[this.offset .. this.offset + this.length]; + } }; pub const JavascriptBundledModule = struct { @@ -1683,6 +1695,12 @@ pub const Api = struct { /// conditions conditions: []const []const u8, + /// packages + packages: ?PackagesMode = null, + + /// ignore_dce_annotations + ignore_dce_annotations: bool, + pub fn decode(reader: anytype) anyerror!TransformOptions { var this = std.mem.zeroes(TransformOptions); @@ -1770,6 +1788,9 @@ pub const Api = struct { 26 => { this.conditions = try reader.readArray([]const u8); }, + 27 => { + this.packages = try reader.readValue(PackagesMode); + }, else => { return error.InvalidMessage; }, @@ -1885,6 +1906,11 @@ pub const Api = struct { try writer.writeArray([]const u8, conditions); } + if (this.packages) |packages| { + try writer.writeFieldID(27); + try writer.writeValue([]const u8, packages); + } + try writer.endMessage(); } }; @@ -1907,6 +1933,20 @@ pub const Api = struct { } }; + pub const PackagesMode = enum(u8) { + /// bundle + bundle, + + /// external + external, + + _, + + pub fn jsonStringify(self: @This(), writer: anytype) !void { + return try writer.write(@tagName(self)); + } + }; + pub const FileHandle = struct { /// path path: []const u8, @@ -2731,6 +2771,27 @@ pub const Api = struct { /// token token: []const u8, + pub fn dupe(this: NpmRegistry, allocator: std.mem.Allocator) NpmRegistry { + const buf = allocator.alloc(u8, this.url.len + this.username.len + this.password.len + this.token.len) catch bun.outOfMemory(); + + var out: NpmRegistry = .{ + .url = "", + .username = "", + .password = "", + .token = "", + }; + + var i: usize = 0; + inline for (std.meta.fields(NpmRegistry)) |field| { + const field_value = @field(this, field.name); + @memcpy(buf[i .. i + field_value.len], field_value); + @field(&out, field.name) = buf[i .. i + field_value.len]; + i += field_value.len; + } + + return out; + } + pub fn decode(reader: anytype) anyerror!NpmRegistry { var this = std.mem.zeroes(NpmRegistry); @@ -2747,14 +2808,101 @@ pub const Api = struct { try writer.writeValue(@TypeOf(this.password), this.password); try writer.writeValue(@TypeOf(this.token), this.token); } + + pub const Parser = struct { + log: *bun.logger.Log, + source: *const bun.logger.Source, + allocator: std.mem.Allocator, + + fn addError(this: *Parser, loc: bun.logger.Loc, comptime text: []const u8) !void { + this.log.addError(this.source, loc, text) catch unreachable; + return error.ParserError; + } + + fn expectString(this: *Parser, expr: js_ast.Expr) !void { + switch (expr.data) { + .e_string, .e_utf8_string => {}, + else => { + this.log.addErrorFmt(this.source, expr.loc, this.allocator, "expected string but received {}", .{ + @as(js_ast.Expr.Tag, expr.data), + }) catch unreachable; + return error.ParserError; + }, + } + } + + pub fn parseRegistryURLString(this: *Parser, str: *js_ast.E.String) !Api.NpmRegistry { + return try this.parseRegistryURLStringImpl(str.data); + } + + pub fn parseRegistryURLStringImpl(this: *Parser, str: []const u8) !Api.NpmRegistry { + const url = bun.URL.parse(str); + var registry = std.mem.zeroes(Api.NpmRegistry); + + // Token + if (url.username.len == 0 and url.password.len > 0) { + registry.token = url.password; + registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{}/{s}/", .{ url.displayProtocol(), url.displayHost(), std.mem.trim(u8, url.pathname, "/") }); + } else if (url.username.len > 0 and url.password.len > 0) { + registry.username = url.username; + registry.password = url.password; + + registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{}/{s}/", .{ url.displayProtocol(), url.displayHost(), std.mem.trim(u8, url.pathname, "/") }); + } else { + // Do not include a trailing slash. There might be parameters at the end. + registry.url = url.href; + } + + return registry; + } + + fn parseRegistryObject(this: *Parser, obj: *js_ast.E.Object) !Api.NpmRegistry { + var registry = std.mem.zeroes(Api.NpmRegistry); + + if (obj.get("url")) |url| { + try this.expectString(url); + const href = url.asString(this.allocator).?; + // Do not include a trailing slash. There might be parameters at the end. + registry.url = href; + } + + if (obj.get("username")) |username| { + try this.expectString(username); + registry.username = username.asString(this.allocator).?; + } + + if (obj.get("password")) |password| { + try this.expectString(password); + registry.password = password.asString(this.allocator).?; + } + + if (obj.get("token")) |token| { + try this.expectString(token); + registry.token = token.asString(this.allocator).?; + } + + return registry; + } + + pub fn parseRegistry(this: *Parser, expr: js_ast.Expr) !Api.NpmRegistry { + switch (expr.data) { + .e_string => |str| { + return this.parseRegistryURLString(str); + }, + .e_object => |obj| { + return this.parseRegistryObject(obj); + }, + else => { + try this.addError(expr.loc, "Expected registry to be a URL string or an object"); + return std.mem.zeroes(Api.NpmRegistry); + }, + } + } + }; }; pub const NpmRegistryMap = struct { - /// scopes - scopes: []const []const u8, - - /// registries - registries: []const NpmRegistry, + scopes: bun.StringArrayHashMapUnmanaged(NpmRegistry) = .{}, pub fn decode(reader: anytype) anyerror!NpmRegistryMap { var this = std.mem.zeroes(NpmRegistryMap); @@ -2765,8 +2913,8 @@ pub const Api = struct { } pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray([]const u8, this.scopes); - try writer.writeArray(NpmRegistry, this.registries); + try writer.writeArray([]const u8, this.scopes.keys()); + try writer.writeArray(NpmRegistry, this.scopes.values()); } }; diff --git a/src/ast/base.zig b/src/ast/base.zig index 738ac642556f8..26f3815e2455f 100644 --- a/src/ast/base.zig +++ b/src/ast/base.zig @@ -2,27 +2,12 @@ const std = @import("std"); const bun = @import("root").bun; const unicode = std.unicode; -pub const JavascriptString = []u16; -pub fn newJavascriptString(comptime text: []const u8) JavascriptString { - return unicode.utf8ToUtf16LeStringLiteral(text); -} +const js_ast = bun.JSAst; pub const NodeIndex = u32; pub const NodeIndexNone = 4294967293; // TODO: figure out if we actually need this -// -- original comment -- -// Files are parsed in parallel for speed. We want to allow each parser to -// generate symbol IDs that won't conflict with each other. We also want to be -// able to quickly merge symbol tables from all files into one giant symbol -// table. -// -// We can accomplish both goals by giving each symbol ID two parts: a source -// index that is unique to the parser goroutine, and an inner index that -// increments as the parser generates new symbol IDs. Then a symbol map can -// be an array of arrays indexed first by source index, then by inner index. -// The maps can be merged quickly by creating a single outer array containing -// all inner arrays from all parsed files. pub const RefHashCtx = struct { pub fn hash(_: @This(), key: Ref) u32 { @@ -44,89 +29,6 @@ pub const RefCtx = struct { } }; -/// Sets the range of bits starting at `start_bit` upto and excluding `start_bit` + `number_of_bits` -/// to be specific, if the range is N bits long, the N lower bits of `value` will be used; if any of -/// the other bits in `value` are set to 1, this function will panic. -/// -/// ```zig -/// var val: u8 = 0b10000000; -/// setBits(&val, 2, 4, 0b00001101); -/// try testing.expectEqual(@as(u8, 0b10110100), val); -/// ``` -/// -/// ## Panics -/// This method will panic if the `value` exceeds the bit range of the type of `target` -pub fn setBits( - comptime TargetType: type, - target: TargetType, - comptime start_bit: comptime_int, - comptime number_of_bits: comptime_int, - value: TargetType, -) TargetType { - const end_bit = start_bit + number_of_bits; - - comptime { - if (number_of_bits == 0) @compileError("non-zero number_of_bits must be provided"); - - if (@typeInfo(TargetType) == .Int) { - if (@typeInfo(TargetType).Int.signedness != .unsigned) { - @compileError("requires an unsigned integer, found " ++ @typeName(TargetType)); - } - if (start_bit >= @bitSizeOf(TargetType)) { - @compileError("start_bit index is out of bounds of the bit field"); - } - if (end_bit > @bitSizeOf(TargetType)) { - @compileError("start_bit + number_of_bits is out of bounds of the bit field"); - } - } else if (@typeInfo(TargetType) == .ComptimeInt) { - @compileError("comptime_int is unsupported"); - } else { - @compileError("requires an unsigned integer, found " ++ @typeName(TargetType)); - } - } - - if (comptime std.debug.runtime_safety) { - if (getBits(TargetType, value, 0, (end_bit - start_bit)) != value) @panic("value exceeds bit range"); - } - - const bitmask: TargetType = comptime blk: { - var bitmask = ~@as(TargetType, 0); - bitmask <<= (@bitSizeOf(TargetType) - end_bit); - bitmask >>= (@bitSizeOf(TargetType) - end_bit); - bitmask >>= start_bit; - bitmask <<= start_bit; - break :blk ~bitmask; - }; - - return (target & bitmask) | (value << start_bit); -} - -pub inline fn getBits(comptime TargetType: type, target: anytype, comptime start_bit: comptime_int, comptime number_of_bits: comptime_int) TargetType { - comptime { - if (number_of_bits == 0) @compileError("non-zero number_of_bits must be provided"); - - if (@typeInfo(TargetType) == .Int) { - if (@typeInfo(TargetType).Int.signedness != .unsigned) { - @compileError("requires an unsigned integer, found " ++ @typeName(TargetType)); - } - if (start_bit >= @bitSizeOf(TargetType)) { - @compileError("start_bit index is out of bounds of the bit field"); - } - if (start_bit + number_of_bits > @bitSizeOf(TargetType)) { - @compileError("start_bit + number_of_bits is out of bounds of the bit field"); - } - } else if (@typeInfo(TargetType) == .ComptimeInt) { - if (target < 0) { - @compileError("requires an unsigned integer, found " ++ @typeName(TargetType)); - } - } else { - @compileError("requires an unsigned integer, found " ++ @typeName(TargetType)); - } - } - - return @as(TargetType, @truncate(target >> start_bit)); -} - /// In some parts of Bun, we have many different IDs pointing to different things. /// It's easy for them to get mixed up, so we use this type to make sure we don't. /// @@ -186,7 +88,22 @@ pub const Index = packed struct(u32) { } }; +/// -- original comment from esbuild -- +/// +/// Files are parsed in parallel for speed. We want to allow each parser to +/// generate symbol IDs that won't conflict with each other. We also want to be +/// able to quickly merge symbol tables from all files into one giant symbol +/// table. +/// +/// We can accomplish both goals by giving each symbol ID two parts: a source +/// index that is unique to the parser goroutine, and an inner index that +/// increments as the parser generates new symbol IDs. Then a symbol map can +/// be an array of arrays indexed first by source index, then by inner index. +/// The maps can be merged quickly by creating a single outer array containing +/// all inner arrays from all parsed files. pub const Ref = packed struct(u64) { + pub const Int = u31; + inner_index: Int = 0, tag: enum(u2) { @@ -198,6 +115,13 @@ pub const Ref = packed struct(u64) { source_index: Int = 0, + /// Represents a null state without using an extra bit + pub const None = Ref{ .inner_index = 0, .source_index = 0, .tag = .invalid }; + + comptime { + bun.assert(None.isEmpty()); + } + pub inline fn isEmpty(this: Ref) bool { return this.asU64() == 0; } @@ -205,12 +129,6 @@ pub const Ref = packed struct(u64) { pub const ArrayHashCtx = RefHashCtx; pub const HashCtx = RefCtx; - pub const Int = std.meta.Int(.unsigned, (64 - 2) / 2); - - pub fn toInt(value: anytype) Int { - return @as(Int, @intCast(value)); - } - pub fn isSourceIndexNull(this: anytype) bool { return this == std.math.maxInt(Int); } @@ -222,22 +140,40 @@ pub const Ref = packed struct(u64) { pub fn format(ref: Ref, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { try std.fmt.format( writer, - "Ref[{d}, {d}, {s}]", + "Ref[inner={d}, src={d}, .{s}]", .{ - ref.sourceIndex(), ref.innerIndex(), + ref.sourceIndex(), @tagName(ref.tag), }, ); } + pub fn dump(ref: Ref, symbol_table: anytype) std.fmt.Formatter(dumpImpl) { + return .{ .data = .{ + .ref = ref, + .symbol = ref.getSymbol(symbol_table), + } }; + } + + fn dumpImpl(data: struct { ref: Ref, symbol: *js_ast.Symbol }, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + try std.fmt.format( + writer, + "Ref[inner={d}, src={d}, .{s}; original_name={s}, uses={d}]", + .{ + data.ref.inner_index, + data.ref.source_index, + @tagName(data.ref.tag), + data.symbol.original_name, + data.symbol.use_count_estimate, + }, + ); + } + pub fn isValid(this: Ref) bool { return this.tag != .invalid; } - // 2 bits of padding for whatever is the parent - pub const None = Ref{ .inner_index = 0, .source_index = 0, .tag = .invalid }; - pub inline fn sourceIndex(this: Ref) Int { return this.source_index; } @@ -253,10 +189,7 @@ pub const Ref = packed struct(u64) { pub fn init(inner_index: Int, source_index: usize, is_source_contents_slice: bool) Ref { return .{ .inner_index = inner_index, - - // if we overflow, we want a panic - .source_index = @as(Int, @intCast(source_index)), - + .source_index = @intCast(source_index), .tag = if (is_source_contents_slice) .source_contents_slice else .allocated_name, }; } @@ -267,25 +200,37 @@ pub const Ref = packed struct(u64) { } pub fn hash(key: Ref) u32 { - return @as(u32, @truncate(key.hash64())); + return @truncate(key.hash64()); } pub inline fn asU64(key: Ref) u64 { - return @as(u64, @bitCast(key)); + return @bitCast(key); } pub inline fn hash64(key: Ref) u64 { return bun.hash(&@as([8]u8, @bitCast(key.asU64()))); } - pub fn eql(ref: Ref, b: Ref) bool { - return asU64(ref) == b.asU64(); - } - pub inline fn isNull(self: Ref) bool { - return self.tag == .invalid; + pub fn eql(ref: Ref, other: Ref) bool { + return ref.asU64() == other.asU64(); } + pub const isNull = isEmpty; // deprecated + pub fn jsonStringify(self: *const Ref, writer: anytype) !void { return try writer.write([2]u32{ self.sourceIndex(), self.innerIndex() }); } + + pub fn getSymbol(ref: Ref, symbol_table: anytype) *js_ast.Symbol { + // Different parts of the bundler use different formats of the symbol table + // In the parser you only have one array, and .sourceIndex() is ignored. + // In the bundler, you have a 2D array where both parts of the ref are used. + const resolved_symbol_table = switch (@TypeOf(symbol_table)) { + *const std.ArrayList(js_ast.Symbol) => symbol_table.items, + *std.ArrayList(js_ast.Symbol) => symbol_table.items, + []js_ast.Symbol => symbol_table, + else => |T| @compileError("Unsupported type to Ref.getSymbol: " ++ @typeName(T)), + }; + return &resolved_symbol_table[ref.innerIndex()]; + } }; diff --git a/src/async/posix_event_loop.zig b/src/async/posix_event_loop.zig index aa42d1848cb31..491e09ce7b2fc 100644 --- a/src/async/posix_event_loop.zig +++ b/src/async/posix_event_loop.zig @@ -537,7 +537,7 @@ pub const FilePoll = struct { } }; - const HiveArray = bun.HiveArray(FilePoll, 128).Fallback; + const HiveArray = bun.HiveArray(FilePoll, if (bun.heap_breakdown.enabled) 0 else 128).Fallback; // We defer freeing FilePoll until the end of the next event loop iteration // This ensures that we don't free a FilePoll before the next callback is called @@ -548,9 +548,9 @@ pub const FilePoll = struct { const log = Output.scoped(.FilePoll, false); - pub fn init(allocator: std.mem.Allocator) Store { + pub fn init() Store { return .{ - .hive = HiveArray.init(allocator), + .hive = HiveArray.init(bun.typedAllocator(FilePoll)), }; } diff --git a/src/async/windows_event_loop.zig b/src/async/windows_event_loop.zig index 24a2d7647c4a2..0943d1959b193 100644 --- a/src/async/windows_event_loop.zig +++ b/src/async/windows_event_loop.zig @@ -316,9 +316,9 @@ pub const FilePoll = struct { const log = Output.scoped(.FilePoll, false); - pub fn init(allocator: std.mem.Allocator) Store { + pub fn init() Store { return .{ - .hive = HiveArray.init(allocator), + .hive = HiveArray.init(bun.typedAllocator(FilePoll)), }; } diff --git a/src/base64/base64.zig b/src/base64/base64.zig index 76a5b635d7adb..b47856fe6861e 100644 --- a/src/base64/base64.zig +++ b/src/base64/base64.zig @@ -34,6 +34,16 @@ pub fn decode(destination: []u8, source: []const u8) bun.simdutf.SIMDUTFResult { return result; } +pub fn decodeAlloc(allocator: std.mem.Allocator, input: []const u8) ![]u8 { + var dest = try allocator.alloc(u8, decodeLen(input)); + const result = decode(dest, input); + if (!result.isSuccessful()) { + allocator.free(dest); + return error.DecodingFailed; + } + return dest[0..result.count]; +} + pub fn encode(destination: []u8, source: []const u8) usize { return bun.simdutf.base64.encode(source, destination, false); } @@ -54,7 +64,11 @@ pub fn decodeLen(source: anytype) usize { } pub fn encodeLen(source: anytype) usize { - return zig_base64.standard.Encoder.calcSize(source.len); + return encodeLenFromSize(source.len); +} + +pub fn encodeLenFromSize(source: usize) usize { + return zig_base64.standard.Encoder.calcSize(source); } pub fn urlSafeEncodeLen(source: anytype) usize { diff --git a/src/bench/string-handling.zig b/src/bench/string-handling.zig deleted file mode 100644 index 8d778e35646f2..0000000000000 --- a/src/bench/string-handling.zig +++ /dev/null @@ -1,64 +0,0 @@ -const strings = bun.strings; -const std = @import("std"); - -pub fn main() anyerror!void { - const args = try std.process.argsAlloc(std.heap.c_allocator); - const filepath = args[args.len - 3]; - const find = args[args.len - 2]; - const amount = try std.fmt.parseInt(usize, args[args.len - 1], 10); - var file = try std.fs.cwd().openFile(filepath, .{ .mode = .read_only }); - var contents = try file.readToEndAlloc(std.heap.c_allocator, std.math.maxInt(usize)); - var list = try std.ArrayList(u8).initCapacity(std.heap.c_allocator, contents.len); - var duped = list.items.ptr[0..contents.len]; - { - var timer = try std.time.Timer.start(); - var index: usize = std.math.maxInt(usize); - var j: usize = 0; - var i: usize = 0; - while (j < amount) : (j += 1) { - i = 0; - strings.copy(duped, contents); - } - - if (index == std.math.maxInt(usize)) { - std.debug.print("manual [{d} byte file] {s} NOT found in {}\n", .{ contents.len, find, std.fmt.fmtDuration(timer.read()) }); - } else { - std.debug.print("manual [{d} byte file] {s} found at {d} in {}\n", .{ contents.len, find, index, std.fmt.fmtDuration(timer.read()) }); - } - } - - { - var timer = try std.time.Timer.start(); - var index: usize = std.math.maxInt(usize); - var j: usize = 0; - var i: usize = 0; - while (j < amount) : (j += 1) { - i = 0; - @memcpy(duped[0..contents.len], contents); - } - - if (index == std.math.maxInt(usize)) { - std.debug.print("memcpy [{d} byte file] {s} NOT found in {}\n", .{ contents.len, find, std.fmt.fmtDuration(timer.read()) }); - } else { - std.debug.print("memcpy [{d} byte file] {s} found at {d} in {}\n", .{ contents.len, find, index, std.fmt.fmtDuration(timer.read()) }); - } - } - - { - var timer = try std.time.Timer.start(); - var index: usize = std.math.maxInt(usize); - var j: usize = 0; - var i: usize = 0; - while (j < amount) : (j += 1) { - i = 0; - list.clearRetainingCapacity(); - list.appendSliceAssumeCapacity(contents); - } - - if (index == std.math.maxInt(usize)) { - std.debug.print("ArrayList [{d} byte file] {s} NOT found in {}\n", .{ contents.len, find, std.fmt.fmtDuration(timer.read()) }); - } else { - std.debug.print("ArrayList [{d} byte file] {s} found at {d} in {}\n", .{ contents.len, find, index, std.fmt.fmtDuration(timer.read()) }); - } - } -} diff --git a/src/bit_set.zig b/src/bit_set.zig index 1f2fc4681c7ae..245dbd09e6ed3 100644 --- a/src/bit_set.zig +++ b/src/bit_set.zig @@ -517,6 +517,16 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type { } } + /// Sets all bits + pub fn setAll(self: *Self, value: bool) void { + @memset(&self.masks, if (value) std.math.maxInt(MaskInt) else 0); + + // Zero the padding bits + if (num_masks > 0) { + self.masks[num_masks - 1] &= last_item_mask; + } + } + /// Performs a union of two bit sets, and stores the /// result in the first one. Bits in the result are /// set if the corresponding bits were set in either input. @@ -1246,6 +1256,12 @@ pub const AutoBitSet = union(enum) { }; } + pub fn setAll(this: *AutoBitSet, value: bool) void { + switch (this.*) { + inline else => |*bitset| bitset.setAll(value), + } + } + pub fn deinit(this: *AutoBitSet, allocator: std.mem.Allocator) void { switch (std.meta.activeTag(this.*)) { .static => {}, diff --git a/src/boringssl.zig b/src/boringssl.zig index 3f3877ef36348..3f8892a1aadc0 100644 --- a/src/boringssl.zig +++ b/src/boringssl.zig @@ -206,3 +206,20 @@ pub fn checkServerIdentity( } return false; } + +const JSC = bun.JSC; +pub fn ERR_toJS(globalThis: *JSC.JSGlobalObject, err_code: u32) JSC.JSValue { + var outbuf: [128 + 1 + "BoringSSL ".len]u8 = undefined; + @memset(&outbuf, 0); + outbuf[0.."BoringSSL ".len].* = "BoringSSL ".*; + const message_buf = outbuf["BoringSSL ".len..]; + + _ = boring.ERR_error_string_n(err_code, message_buf, message_buf.len); + + const error_message: []const u8 = bun.sliceTo(outbuf[0..], 0); + if (error_message.len == "BoringSSL ".len) { + return globalThis.ERR_BORINGSSL("An unknown BoringSSL error occurred: {d}", .{err_code}).toJS(); + } + + return globalThis.ERR_BORINGSSL("{s}", .{error_message}).toJS(); +} diff --git a/src/brotli.zig b/src/brotli.zig index 16bebf494f7c1..0759261d46e99 100644 --- a/src/brotli.zig +++ b/src/brotli.zig @@ -11,17 +11,17 @@ const mimalloc = bun.Mimalloc; const BrotliAllocator = struct { pub fn alloc(_: ?*anyopaque, len: usize) callconv(.C) *anyopaque { - if (comptime bun.is_heap_breakdown_enabled) { - const zone = bun.HeapBreakdown.malloc_zone_t.get(BrotliAllocator); - return zone.malloc_zone_malloc(len).?; + if (bun.heap_breakdown.enabled) { + const zone = bun.heap_breakdown.getZone("brotli"); + return zone.malloc_zone_malloc(len) orelse bun.outOfMemory(); } - return mimalloc.mi_malloc(len) orelse unreachable; + return mimalloc.mi_malloc(len) orelse bun.outOfMemory(); } pub fn free(_: ?*anyopaque, data: ?*anyopaque) callconv(.C) void { - if (comptime bun.is_heap_breakdown_enabled) { - const zone = bun.HeapBreakdown.malloc_zone_t.get(BrotliAllocator); + if (bun.heap_breakdown.enabled) { + const zone = bun.heap_breakdown.getZone("brotli"); zone.malloc_zone_free(data); return; } @@ -55,14 +55,25 @@ pub const BrotliReaderArrayList = struct { state: State = State.Uninitialized, total_out: usize = 0, total_in: usize = 0, + flushOp: BrotliEncoder.Operation, + finishFlushOp: BrotliEncoder.Operation, + fullFlushOp: BrotliEncoder.Operation, pub usingnamespace bun.New(BrotliReaderArrayList); pub fn newWithOptions(input: []const u8, list: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, options: DecoderOptions) !*BrotliReaderArrayList { - return BrotliReaderArrayList.new(try initWithOptions(input, list, allocator, options)); + return BrotliReaderArrayList.new(try initWithOptions(input, list, allocator, options, .process, .finish, .flush)); } - pub fn initWithOptions(input: []const u8, list: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, options: DecoderOptions) !BrotliReaderArrayList { + pub fn initWithOptions( + input: []const u8, + list: *std.ArrayListUnmanaged(u8), + allocator: std.mem.Allocator, + options: DecoderOptions, + flushOp: BrotliEncoder.Operation, + finishFlushOp: BrotliEncoder.Operation, + fullFlushOp: BrotliEncoder.Operation, + ) !BrotliReaderArrayList { if (!BrotliDecoder.initializeBrotli()) { return error.BrotliFailedToLoad; } @@ -81,6 +92,9 @@ pub const BrotliReaderArrayList = struct { .list = list.*, .list_allocator = allocator, .brotli = brotli, + .flushOp = flushOp, + .finishFlushOp = finishFlushOp, + .fullFlushOp = fullFlushOp, }; } @@ -181,17 +195,28 @@ pub const BrotliCompressionStream = struct { state: State = State.Inflating, total_out: usize = 0, total_in: usize = 0, - - pub fn init() !BrotliCompressionStream { + flushOp: BrotliEncoder.Operation, + finishFlushOp: BrotliEncoder.Operation, + fullFlushOp: BrotliEncoder.Operation, + + pub fn init( + flushOp: BrotliEncoder.Operation, + finishFlushOp: BrotliEncoder.Operation, + fullFlushOp: BrotliEncoder.Operation, + ) !BrotliCompressionStream { const instance = BrotliEncoder.createInstance(&BrotliAllocator.alloc, &BrotliAllocator.free, null) orelse return error.BrotliFailedToCreateInstance; return BrotliCompressionStream{ .brotli = instance, + .flushOp = flushOp, + .finishFlushOp = finishFlushOp, + .fullFlushOp = fullFlushOp, }; } pub fn writeChunk(this: *BrotliCompressionStream, input: []const u8, last: bool) ![]const u8 { - const result = this.brotli.compressStream(if (last) BrotliEncoder.Operation.finish else .process, input); + this.total_in += input.len; + const result = this.brotli.compressStream(if (last) this.finishFlushOp else this.flushOp, input); if (!result.success) { this.state = .Error; diff --git a/src/bun.js/BuildMessage.zig b/src/bun.js/BuildMessage.zig index c464f2c763baf..468dd29c427b4 100644 --- a/src/bun.js/BuildMessage.zig +++ b/src/bun.js/BuildMessage.zig @@ -22,12 +22,12 @@ pub const BuildMessage = struct { pub fn constructor( globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) ?*BuildMessage { + ) ?*BuildMessage { globalThis.throw("BuildMessage is not constructable", .{}); return null; } - pub fn getNotes(this: *BuildMessage, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getNotes(this: *BuildMessage, globalThis: *JSC.JSGlobalObject) JSC.JSValue { const notes: []const logger.Data = this.msg.notes orelse &[_]logger.Data{}; const array = JSC.JSValue.createEmptyArray(globalThis, notes.len); for (notes, 0..) |note, i| { @@ -53,7 +53,7 @@ pub const BuildMessage = struct { var str = ZigString.init(text); str.setOutputEncoding(); if (str.isUTF8()) { - const out = str.toValueGC(globalThis); + const out = str.toJS(globalThis); default_allocator.free(text); return out; } @@ -81,7 +81,7 @@ pub const BuildMessage = struct { this: *BuildMessage, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return this.toStringFn(globalThis); } @@ -89,7 +89,7 @@ pub const BuildMessage = struct { this: *BuildMessage, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const args_ = callframe.arguments(1); const args = args_.ptr[0..args_.len]; if (args.len > 0) { @@ -110,9 +110,9 @@ pub const BuildMessage = struct { this: *BuildMessage, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var object = JSC.JSValue.createEmptyObject(globalThis, 4); - object.put(globalThis, ZigString.static("name"), ZigString.init("BuildMessage").toValueGC(globalThis)); + object.put(globalThis, ZigString.static("name"), ZigString.init("BuildMessage").toJS(globalThis)); object.put(globalThis, ZigString.static("position"), this.getPosition(globalThis)); object.put(globalThis, ZigString.static("message"), this.getMessage(globalThis)); object.put(globalThis, ZigString.static("level"), this.getLevel(globalThis)); @@ -126,17 +126,17 @@ pub const BuildMessage = struct { object.put( globalThis, ZigString.static("lineText"), - ZigString.init(location.line_text orelse "").toValueGC(globalThis), + ZigString.init(location.line_text orelse "").toJS(globalThis), ); object.put( globalThis, ZigString.static("file"), - ZigString.init(location.file).toValueGC(globalThis), + ZigString.init(location.file).toJS(globalThis), ); object.put( globalThis, ZigString.static("namespace"), - ZigString.init(location.namespace).toValueGC(globalThis), + ZigString.init(location.namespace).toJS(globalThis), ); object.put( globalThis, @@ -163,7 +163,7 @@ pub const BuildMessage = struct { } // https://github.com/oven-sh/bun/issues/2375#issuecomment-2121530202 - pub fn getColumn(this: *BuildMessage, _: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getColumn(this: *BuildMessage, _: *JSC.JSGlobalObject) JSC.JSValue { if (this.msg.data.location) |location| { return JSC.JSValue.jsNumber(@max(location.column - 1, 0)); } @@ -171,7 +171,7 @@ pub const BuildMessage = struct { return JSC.JSValue.jsNumber(@as(i32, 0)); } - pub fn getLine(this: *BuildMessage, _: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getLine(this: *BuildMessage, _: *JSC.JSGlobalObject) JSC.JSValue { if (this.msg.data.location) |location| { return JSC.JSValue.jsNumber(@max(location.line - 1, 0)); } @@ -182,25 +182,25 @@ pub const BuildMessage = struct { pub fn getPosition( this: *BuildMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return BuildMessage.generatePositionObject(this.msg, globalThis); } pub fn getMessage( this: *BuildMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(this.msg.data.text).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(this.msg.data.text).toJS(globalThis); } pub fn getLevel( this: *BuildMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(this.msg.kind.string()).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(this.msg.kind.string()).toJS(globalThis); } - pub fn finalize(this: *BuildMessage) callconv(.C) void { + pub fn finalize(this: *BuildMessage) void { this.msg.deinit(bun.default_allocator); } }; diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index 587ef28799cfd..185924190a792 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -71,8 +71,8 @@ pub const MessageType = enum(u32) { _, }; -var stderr_mutex: bun.Lock = bun.Lock.init(); -var stdout_mutex: bun.Lock = bun.Lock.init(); +var stderr_mutex: bun.Lock = .{}; +var stdout_mutex: bun.Lock = .{}; threadlocal var stderr_lock_count: u16 = 0; threadlocal var stdout_lock_count: u16 = 0; @@ -87,7 +87,7 @@ pub fn messageWithTypeAndLevel( global: *JSGlobalObject, vals: [*]const JSValue, len: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { if (comptime is_bindgen) { return; } @@ -697,41 +697,37 @@ pub fn format2( }; const tag = ConsoleObject.Formatter.Tag.get(vals[0], global); - var unbuffered_writer = if (comptime Writer != RawWriter) - if (@hasDecl(@TypeOf(writer.context.unbuffered_writer.context), "quietWriter")) - writer.context.unbuffered_writer.context.quietWriter() - else - writer.context.unbuffered_writer.context.writer() - else - writer; - if (tag.tag == .String) { if (options.enable_colors) { if (level == .Error) { - unbuffered_writer.writeAll(comptime Output.prettyFmt("", true)) catch {}; + writer.writeAll(comptime Output.prettyFmt("", true)) catch {}; } fmt.format( tag, - @TypeOf(unbuffered_writer), - unbuffered_writer, + Writer, + writer, vals[0], global, true, ); if (level == .Error) { - unbuffered_writer.writeAll(comptime Output.prettyFmt("", true)) catch {}; + writer.writeAll(comptime Output.prettyFmt("", true)) catch {}; } } else { fmt.format( tag, - @TypeOf(unbuffered_writer), - unbuffered_writer, + Writer, + writer, vals[0], global, false, ); } - if (options.add_newline) _ = unbuffered_writer.write("\n") catch 0; + if (options.add_newline) { + _ = writer.write("\n") catch 0; + } + + writer.context.flush() catch {}; } else { defer { if (comptime Writer != RawWriter) { @@ -869,7 +865,6 @@ pub const Formatter = struct { pub const ZigFormatter = struct { formatter: *ConsoleObject.Formatter, - global: *JSGlobalObject, value: JSValue, pub const WriteError = error{UhOh}; @@ -878,9 +873,8 @@ pub const Formatter = struct { defer { self.formatter.remaining_values = &[_]JSValue{}; } - self.formatter.globalThis = self.global; self.formatter.format( - Tag.get(self.value, self.global), + Tag.get(self.value, self.formatter.globalThis), @TypeOf(writer), writer, self.value, @@ -1079,6 +1073,7 @@ pub const Formatter = struct { }; } } + if (globalThis.hasException()) return .{ .tag = .RevokedProxy }; } if (js_type == .DOMWrapper) { @@ -1176,6 +1171,7 @@ pub const Formatter = struct { .Uint16Array, .Int32Array, .Uint32Array, + .Float16Array, .Float32Array, .Float64Array, .BigInt64Array, @@ -1283,6 +1279,7 @@ pub const Formatter = struct { writer.writeAll(end); // then skip the second % so we dont hit it again slice = slice[@min(slice.len, i + 1)..]; + len = @truncate(slice.len); i = 0; continue; }, @@ -1295,7 +1292,7 @@ pub const Formatter = struct { slice = slice[@min(slice.len, i + 1)..]; i = 0; hit_percent = true; - len = @as(u32, @truncate(slice.len)); + len = @truncate(slice.len); const next_value = this.remaining_values[0]; this.remaining_values = this.remaining_values[1..]; @@ -1568,7 +1565,7 @@ pub const Formatter = struct { formatter: *ConsoleObject.Formatter, writer: Writer, count: usize = 0, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); if (single_line and this.count > 0) { this.formatter.printComma(Writer, this.writer, enable_ansi_colors) catch unreachable; @@ -1632,7 +1629,7 @@ pub const Formatter = struct { formatter: *ConsoleObject.Formatter, writer: Writer, is_first: bool = true, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); if (single_line) { if (!this.is_first) { @@ -2077,10 +2074,22 @@ pub const Formatter = struct { var printable = value.getName(this.globalThis); defer printable.deref(); + const proto = value.getPrototype(this.globalThis); + const func_name = proto.getName(this.globalThis); // "Function" | "AsyncFunction" | "GeneratorFunction" | "AsyncGeneratorFunction" + defer func_name.deref(); + if (printable.isEmpty()) { - writer.print(comptime Output.prettyFmt("[Function]", enable_ansi_colors), .{}); + if (func_name.isEmpty()) { + writer.print(comptime Output.prettyFmt("[Function]", enable_ansi_colors), .{}); + } else { + writer.print(comptime Output.prettyFmt("[{}]", enable_ansi_colors), .{func_name}); + } } else { - writer.print(comptime Output.prettyFmt("[Function: {}]", enable_ansi_colors), .{printable}); + if (func_name.isEmpty()) { + writer.print(comptime Output.prettyFmt("[Function: {}]", enable_ansi_colors), .{printable}); + } else { + writer.print(comptime Output.prettyFmt("[{}: {}]", enable_ansi_colors), .{ func_name, printable }); + } } }, .GetterSetter => { @@ -2110,7 +2119,7 @@ pub const Formatter = struct { } }, .Array => { - const len = @as(u32, @truncate(value.getLength(this.globalThis))); + const len = value.getLength(this.globalThis); // TODO: DerivedArray does not get passed along in JSType, and it's not clear why. // if (jsType == .DerivedArray) { @@ -2175,6 +2184,7 @@ pub const Formatter = struct { } var i: u32 = 1; + var nonempty_count: u32 = 1; while (i < len) : (i += 1) { const element = value.getDirectIndex(this.globalThis, i); @@ -2184,6 +2194,15 @@ pub const Formatter = struct { } continue; } + if (nonempty_count >= 100) { + this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; + writer.writeAll("\n"); // we want the line break to be unconditional here + this.estimated_line_length = 0; + this.writeIndent(Writer, writer_) catch unreachable; + writer.pretty("... {d} more items", enable_ansi_colors, .{len - i}); + break; + } + nonempty_count += 1; if (empty_start) |empty| { if (empty > 0) { @@ -2298,7 +2317,8 @@ pub const Formatter = struct { .Object, Writer, writer_, - toJSONFunction.callWithThis(this.globalThis, value, &.{}), + toJSONFunction.call(this.globalThis, value, &.{}) catch |err| + this.globalThis.takeException(err), .Object, enable_ansi_colors, ); @@ -2313,7 +2333,8 @@ pub const Formatter = struct { .Object, Writer, writer_, - toJSONFunction.callWithThis(this.globalThis, value, &.{}), + toJSONFunction.call(this.globalThis, value, &.{}) catch |err| + this.globalThis.takeException(err), .Object, enable_ansi_colors, ); @@ -2365,15 +2386,9 @@ pub const Formatter = struct { writer.writeAll("Promise { " ++ comptime Output.prettyFmt("", enable_ansi_colors)); switch (JSPromise.status(@as(*JSPromise, @ptrCast(value.asObjectRef().?)), this.globalThis.vm())) { - JSPromise.Status.Pending => { - writer.writeAll(""); - }, - JSPromise.Status.Fulfilled => { - writer.writeAll(""); - }, - JSPromise.Status.Rejected => { - writer.writeAll(""); - }, + .pending => writer.writeAll(""), + .fulfilled => writer.writeAll(""), + .rejected => writer.writeAll(""), } writer.writeAll(comptime Output.prettyFmt("", enable_ansi_colors) ++ " }"); @@ -2561,15 +2576,16 @@ pub const Formatter = struct { writer.writeAll("}"); }, .toJSON => { - if (value.get(this.globalThis, "toJSON")) |func| { - const result = func.callWithThis(this.globalThis, value, &.{}); - if (result.toError() == null) { - const prev_quote_keys = this.quote_keys; - this.quote_keys = true; - defer this.quote_keys = prev_quote_keys; - this.printAs(.Object, Writer, writer_, result, value.jsType(), enable_ansi_colors); - return; - } + if (value.get(this.globalThis, "toJSON")) |func| brk: { + const result = func.call(this.globalThis, value, &.{}) catch { + this.globalThis.clearException(); + break :brk; + }; + const prev_quote_keys = this.quote_keys; + this.quote_keys = true; + defer this.quote_keys = prev_quote_keys; + this.printAs(.Object, Writer, writer_, result, value.jsType(), enable_ansi_colors); + return; } writer.writeAll("{}"); @@ -2599,10 +2615,21 @@ pub const Formatter = struct { writer.print("{}", .{str}); }, .Event => { - const event_type = EventType.map.getWithEql(value.get(this.globalThis, "type").?.getZigString(this.globalThis), ZigString.eqlComptime) orelse EventType.unknown; - if (event_type != .MessageEvent and event_type != .ErrorEvent) { - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); - } + const event_type_value = brk: { + const value_ = value.get(this.globalThis, "type") orelse break :brk JSValue.undefined; + if (value_.isString()) { + break :brk value_; + } + + break :brk JSValue.undefined; + }; + + const event_type = switch (EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { + .MessageEvent, .ErrorEvent => |evt| evt, + else => { + return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + }, + }; writer.print( comptime Output.prettyFmt("{s} {{\n", enable_ansi_colors), @@ -2628,57 +2655,68 @@ pub const Formatter = struct { event_type.label(), }, ); - if (!single_line) { + }, + } + + if (value.fastGet(this.globalThis, .message)) |message_value| { + if (message_value.isString()) { + if (!this.single_line) { this.writeIndent(Writer, writer_) catch unreachable; } - }, + + writer.print( + comptime Output.prettyFmt("message: ", enable_ansi_colors), + .{}, + ); + + const tag = Tag.getAdvanced(message_value, this.globalThis, .{ .hide_global = true }); + this.format(tag, Writer, writer_, message_value, this.globalThis, enable_ansi_colors); + this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; + if (!this.single_line) { + writer.writeAll("\n"); + } + } } switch (event_type) { .MessageEvent => { + if (!this.single_line) { + this.writeIndent(Writer, writer_) catch unreachable; + } + writer.print( comptime Output.prettyFmt("data: ", enable_ansi_colors), .{}, ); - const data = value.fastGet(this.globalThis, .data).?; + const data = value.fastGet(this.globalThis, .data) orelse JSValue.undefined; const tag = Tag.getAdvanced(data, this.globalThis, .{ .hide_global = true }); - if (tag.cell.isStringLike()) { - this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); - } else { - this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); + this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); + this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; + if (!this.single_line) { + writer.writeAll("\n"); } }, .ErrorEvent => { - { - const error_value = value.get(this.globalThis, "error").?; - - if (!error_value.isEmptyOrUndefinedOrNull()) { - writer.print( - comptime Output.prettyFmt("error: ", enable_ansi_colors), - .{}, - ); - - const tag = Tag.getAdvanced(error_value, this.globalThis, .{ .hide_global = true }); - this.format(tag, Writer, writer_, error_value, this.globalThis, enable_ansi_colors); + if (value.fastGet(this.globalThis, .@"error")) |error_value| { + if (!this.single_line) { + this.writeIndent(Writer, writer_) catch unreachable; } - } - const message_value = value.get(this.globalThis, "message").?; - if (message_value.isString()) { writer.print( - comptime Output.prettyFmt("message: ", enable_ansi_colors), + comptime Output.prettyFmt("error: ", enable_ansi_colors), .{}, ); - const tag = Tag.getAdvanced(message_value, this.globalThis, .{ .hide_global = true }); - this.format(tag, Writer, writer_, message_value, this.globalThis, enable_ansi_colors); + const tag = Tag.getAdvanced(error_value, this.globalThis, .{ .hide_global = true }); + this.format(tag, Writer, writer_, error_value, this.globalThis, enable_ansi_colors); + this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; + if (!this.single_line) { + writer.writeAll("\n"); + } } }, else => unreachable, } - if (!this.single_line) { - writer.writeAll("\n"); - } } if (!this.single_line) { @@ -3043,6 +3081,13 @@ pub const Formatter = struct { @as([]align(std.meta.alignment([]u32)) u32, @alignCast(std.mem.bytesAsSlice(u32, slice))), enable_ansi_colors, ), + .Float16Array => this.writeTypedArray( + *@TypeOf(writer), + &writer, + f16, + @as([]align(std.meta.alignment([]f16)) f16, @alignCast(std.mem.bytesAsSlice(f16, slice))), + enable_ansi_colors, + ), .Float32Array => this.writeTypedArray( *@TypeOf(writer), &writer, @@ -3109,7 +3154,7 @@ pub const Formatter = struct { ", ... {d} more"; writer.print(comptime Output.prettyFmt(fmt_, enable_ansi_colors), .{ - if (@typeInfo(Number) == .Float) bun.fmt.fmtDouble(@floatCast(slice[0])) else slice[0], + if (@typeInfo(Number) == .Float) bun.fmt.double(@floatCast(slice[0])) else slice[0], }); var leftover = slice[1..]; const max = 512; @@ -3119,7 +3164,7 @@ pub const Formatter = struct { writer.space(); writer.print(comptime Output.prettyFmt(fmt_, enable_ansi_colors), .{ - if (@typeInfo(Number) == .Float) bun.fmt.fmtDouble(@floatCast(el)) else el, + if (@typeInfo(Number) == .Float) bun.fmt.double(@floatCast(el)) else el, }); } @@ -3157,7 +3202,7 @@ pub fn count( ptr: [*]const u8, // len len: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { var this = globalThis.bunVM().console; const slice = ptr[0..len]; const hash = bun.hash(slice); @@ -3183,7 +3228,7 @@ pub fn countReset( ptr: [*]const u8, // len len: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { var this = globalThis.bunVM().console; const slice = ptr[0..len]; const hash = bun.hash(slice); @@ -3203,7 +3248,7 @@ pub fn time( _: *JSGlobalObject, chars: [*]const u8, len: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { const id = bun.hash(chars[0..len]); if (!pending_time_logs_loaded) { pending_time_logs = PendingTimers.init(default_allocator); @@ -3223,7 +3268,7 @@ pub fn timeEnd( _: *JSGlobalObject, chars: [*]const u8, len: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { if (!pending_time_logs_loaded) { return; } @@ -3254,7 +3299,7 @@ pub fn timeLog( // args args: [*]JSValue, args_len: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { if (!pending_time_logs_loaded) { return; } @@ -3301,7 +3346,7 @@ pub fn profile( _: [*]const u8, // len _: usize, -) callconv(.C) void {} +) callconv(JSC.conv) void {} pub fn profileEnd( // console _: ConsoleObject.Type, @@ -3311,7 +3356,7 @@ pub fn profileEnd( _: [*]const u8, // len _: usize, -) callconv(.C) void {} +) callconv(JSC.conv) void {} pub fn takeHeapSnapshot( // console _: ConsoleObject.Type, @@ -3321,7 +3366,7 @@ pub fn takeHeapSnapshot( _: [*]const u8, // len _: usize, -) callconv(.C) void { +) callconv(JSC.conv) void { // TODO: this does an extra JSONStringify and we don't need it to! var snapshot: [1]JSValue = .{globalThis.generateHeapSnapshot()}; ConsoleObject.messageWithTypeAndLevel(undefined, MessageType.Log, MessageLevel.Debug, globalThis, &snapshot, 1); @@ -3333,7 +3378,7 @@ pub fn timeStamp( _: *JSGlobalObject, // args _: *ScriptArguments, -) callconv(.C) void {} +) callconv(JSC.conv) void {} pub fn record( // console _: ConsoleObject.Type, @@ -3341,7 +3386,7 @@ pub fn record( _: *JSGlobalObject, // args _: *ScriptArguments, -) callconv(.C) void {} +) callconv(JSC.conv) void {} pub fn recordEnd( // console _: ConsoleObject.Type, @@ -3349,7 +3394,7 @@ pub fn recordEnd( _: *JSGlobalObject, // args _: *ScriptArguments, -) callconv(.C) void {} +) callconv(JSC.conv) void {} pub fn screenshot( // console _: ConsoleObject.Type, @@ -3357,62 +3402,20 @@ pub fn screenshot( _: *JSGlobalObject, // args _: *ScriptArguments, -) callconv(.C) void {} - -pub const Export = shim.exportFunctions(.{ - .messageWithTypeAndLevel = messageWithTypeAndLevel, - .count = count, - .countReset = countReset, - .time = time, - .timeLog = timeLog, - .timeEnd = timeEnd, - .profile = profile, - .profileEnd = profileEnd, - .takeHeapSnapshot = takeHeapSnapshot, - .timeStamp = timeStamp, - .record = record, - .recordEnd = recordEnd, - .screenshot = screenshot, -}); +) callconv(JSC.conv) void {} comptime { - @export(messageWithTypeAndLevel, .{ - .name = Export[0].symbol_name, - }); - @export(count, .{ - .name = Export[1].symbol_name, - }); - @export(countReset, .{ - .name = Export[2].symbol_name, - }); - @export(time, .{ - .name = Export[3].symbol_name, - }); - @export(timeLog, .{ - .name = Export[4].symbol_name, - }); - @export(timeEnd, .{ - .name = Export[5].symbol_name, - }); - @export(profile, .{ - .name = Export[6].symbol_name, - }); - @export(profileEnd, .{ - .name = Export[7].symbol_name, - }); - @export(takeHeapSnapshot, .{ - .name = Export[8].symbol_name, - }); - @export(timeStamp, .{ - .name = Export[9].symbol_name, - }); - @export(record, .{ - .name = Export[10].symbol_name, - }); - @export(recordEnd, .{ - .name = Export[11].symbol_name, - }); - @export(screenshot, .{ - .name = Export[12].symbol_name, - }); + @export(messageWithTypeAndLevel, .{ .name = shim.symbolName("messageWithTypeAndLevel") }); + @export(count, .{ .name = shim.symbolName("count") }); + @export(countReset, .{ .name = shim.symbolName("countReset") }); + @export(time, .{ .name = shim.symbolName("time") }); + @export(timeLog, .{ .name = shim.symbolName("timeLog") }); + @export(timeEnd, .{ .name = shim.symbolName("timeEnd") }); + @export(profile, .{ .name = shim.symbolName("profile") }); + @export(profileEnd, .{ .name = shim.symbolName("profileEnd") }); + @export(takeHeapSnapshot, .{ .name = shim.symbolName("takeHeapSnapshot") }); + @export(timeStamp, .{ .name = shim.symbolName("timeStamp") }); + @export(record, .{ .name = shim.symbolName("record") }); + @export(recordEnd, .{ .name = shim.symbolName("recordEnd") }); + @export(screenshot, .{ .name = shim.symbolName("screenshot") }); } diff --git a/src/bun.js/ResolveMessage.zig b/src/bun.js/ResolveMessage.zig index 8d0f3d9498c5b..1a2f5e774536a 100644 --- a/src/bun.js/ResolveMessage.zig +++ b/src/bun.js/ResolveMessage.zig @@ -21,12 +21,12 @@ pub const ResolveMessage = struct { pub fn constructor( globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) ?*ResolveMessage { + ) ?*ResolveMessage { globalThis.throw("ResolveMessage is not constructable", .{}); return null; } - pub fn getCode(this: *ResolveMessage, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getCode(this: *ResolveMessage, globalObject: *JSC.JSGlobalObject) JSC.JSValue { switch (this.msg.metadata) { .resolve => |resolve| { const label: []const u8 = brk: { @@ -49,7 +49,7 @@ pub const ResolveMessage = struct { } // https://github.com/oven-sh/bun/issues/2375#issuecomment-2121530202 - pub fn getColumn(this: *ResolveMessage, _: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getColumn(this: *ResolveMessage, _: *JSC.JSGlobalObject) JSC.JSValue { if (this.msg.data.location) |location| { return JSC.JSValue.jsNumber(@max(location.column - 1, 0)); } @@ -57,7 +57,7 @@ pub const ResolveMessage = struct { return JSC.JSValue.jsNumber(@as(i32, 0)); } - pub fn getLine(this: *ResolveMessage, _: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getLine(this: *ResolveMessage, _: *JSC.JSGlobalObject) JSC.JSValue { if (this.msg.data.location) |location| { return JSC.JSValue.jsNumber(@max(location.line - 1, 0)); } @@ -98,7 +98,7 @@ pub const ResolveMessage = struct { var str = ZigString.init(text); str.setOutputEncoding(); if (str.isUTF8()) { - const out = str.toValueGC(globalThis); + const out = str.toJS(globalThis); default_allocator.free(text); return out; } @@ -111,7 +111,7 @@ pub const ResolveMessage = struct { this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return this.toStringFn(globalThis); } @@ -119,7 +119,7 @@ pub const ResolveMessage = struct { this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const args_ = callframe.arguments(1); const args = args_.ptr[0..args_.len]; if (args.len > 0) { @@ -140,9 +140,9 @@ pub const ResolveMessage = struct { this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var object = JSC.JSValue.createEmptyObject(globalThis, 7); - object.put(globalThis, ZigString.static("name"), ZigString.init("ResolveMessage").toValueGC(globalThis)); + object.put(globalThis, ZigString.static("name"), ZigString.init("ResolveMessage").toJS(globalThis)); object.put(globalThis, ZigString.static("position"), this.getPosition(globalThis)); object.put(globalThis, ZigString.static("message"), this.getMessage(globalThis)); object.put(globalThis, ZigString.static("level"), this.getLevel(globalThis)); @@ -170,44 +170,44 @@ pub const ResolveMessage = struct { pub fn getPosition( this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.BuildMessage.generatePositionObject(this.msg, globalThis); } pub fn getMessage( this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(this.msg.data.text).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(this.msg.data.text).toJS(globalThis); } pub fn getLevel( this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(this.msg.kind.string()).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(this.msg.kind.string()).toJS(globalThis); } pub fn getSpecifier( this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(this.msg.metadata.resolve.specifier.slice(this.msg.data.text)).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(this.msg.metadata.resolve.specifier.slice(this.msg.data.text)).toJS(globalThis); } pub fn getImportKind( this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(this.msg.metadata.resolve.import_kind.label()).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(this.msg.metadata.resolve.import_kind.label()).toJS(globalThis); } pub fn getReferrer( this: *ResolveMessage, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { if (this.referrer) |referrer| { - return ZigString.init(referrer.text).toValueGC(globalThis); + return ZigString.init(referrer.text).toJS(globalThis); } else { return JSC.JSValue.jsNull(); } diff --git a/src/bun.js/RuntimeTranspilerCache.zig b/src/bun.js/RuntimeTranspilerCache.zig index 113007648dd5c..f76ee34a4c2a0 100644 --- a/src/bun.js/RuntimeTranspilerCache.zig +++ b/src/bun.js/RuntimeTranspilerCache.zig @@ -1,6 +1,9 @@ -// ** Update the version number when any breaking changes are made to the cache format or to the JS parser ** -// Version 2 -> 3: "Infinity" becomes "1/0". -const expected_version = 3; +/// ** Update the version number when any breaking changes are made to the cache format or to the JS parser ** +/// Version 3: "Infinity" becomes "1/0". +/// Version 4: TypeScript enums are properly handled + more constant folding +/// Version 5: `require.main === module` no longer marks a module as CJS +/// Version 6: `use strict` is preserved in CommonJS modules when at the top of the file +const expected_version = 6; const bun = @import("root").bun; const std = @import("std"); @@ -200,10 +203,10 @@ pub const RuntimeTranspilerCache = struct { try metadata.encode(metadata_stream.writer()); - if (comptime bun.Environment.allow_assert) { + if (comptime bun.Environment.isDebug) { var metadata_stream2 = std.io.fixedBufferStream(metadata_buf[0..Metadata.size]); var metadata2 = Metadata{}; - metadata2.decode(metadata_stream2.reader()) catch |err| bun.Output.panic("Metadata did not rountrip encode -> decode successfully: {s}", .{@errorName(err)}); + metadata2.decode(metadata_stream2.reader()) catch |err| bun.Output.panic("Metadata did not roundtrip encode -> decode successfully: {s}", .{@errorName(err)}); bun.assert(std.meta.eql(metadata, metadata2)); } diff --git a/src/bun.js/Weak.zig b/src/bun.js/Weak.zig index f6a478ceb6ffd..c8e724f11acef 100644 --- a/src/bun.js/Weak.zig +++ b/src/bun.js/Weak.zig @@ -4,6 +4,7 @@ const JSC = bun.JSC; pub const WeakRefType = enum(u32) { None = 0, FetchResponse = 1, + PostgreSQLQueryClient = 2, }; const WeakImpl = opaque { pub fn init(globalThis: *JSC.JSGlobalObject, value: JSC.JSValue, refType: WeakRefType, ctx: ?*anyopaque) *WeakImpl { diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit deleted file mode 160000 index 64d04ec1a65d9..0000000000000 --- a/src/bun.js/WebKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 64d04ec1a65d91326c5f2298b9c7d05b56125252 diff --git a/src/bun.js/api/BunObject.classes.ts b/src/bun.js/api/BunObject.classes.ts index 8b01189f37ba4..79cb755e8ad04 100644 --- a/src/bun.js/api/BunObject.classes.ts +++ b/src/bun.js/api/BunObject.classes.ts @@ -62,6 +62,10 @@ export default [ getter: "getStdout", cache: true, }, + stderr: { + getter: "getStderr", + cache: true, + }, writable: { getter: "getStdin", cache: "stdin", @@ -70,11 +74,6 @@ export default [ getter: "getStdout", cache: "stdout", }, - stderr: { - getter: "getStderr", - cache: true, - }, - ref: { fn: "doRef", length: 0, @@ -91,20 +90,24 @@ export default [ fn: "doSend", length: 1, }, - kill: { fn: "kill", length: 1, }, + disconnect: { + fn: "disconnect", + length: 0, + }, + connected: { + getter: "getConnected", + }, "@@asyncDispose": { fn: "asyncDispose", length: 1, }, - killed: { getter: "getKilled", }, - exitCode: { getter: "getExitCode", }, diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 15e07c086afc0..41b8affc1d822 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -1,3 +1,4 @@ +const conv = std.builtin.CallingConvention.Unspecified; /// How to add a new function or property to the Bun global /// /// - Add a callback or property to the below struct @@ -8,69 +9,70 @@ /// - Run "make dev" pub const BunObject = struct { // --- Callbacks --- - pub const allocUnsafe = Bun.allocUnsafe; - pub const build = Bun.JSBundler.buildFn; - pub const connect = JSC.wrapStaticMethod(JSC.API.Listener, "connect", false); - pub const deflateSync = JSC.wrapStaticMethod(JSZlib, "deflateSync", true); - pub const file = WebCore.Blob.constructBunFile; - pub const gc = Bun.runGC; - pub const generateHeapSnapshot = Bun.generateHeapSnapshot; - pub const gunzipSync = JSC.wrapStaticMethod(JSZlib, "gunzipSync", true); - pub const gzipSync = JSC.wrapStaticMethod(JSZlib, "gzipSync", true); - pub const indexOfLine = Bun.indexOfLine; - pub const inflateSync = JSC.wrapStaticMethod(JSZlib, "inflateSync", true); - pub const jest = @import("../test/jest.zig").Jest.call; - pub const listen = JSC.wrapStaticMethod(JSC.API.Listener, "listen", false); - pub const udpSocket = JSC.wrapStaticMethod(JSC.API.UDPSocket, "udpSocket", false); - pub const mmap = Bun.mmapFile; - pub const nanoseconds = Bun.nanoseconds; - pub const openInEditor = Bun.openInEditor; - pub const registerMacro = Bun.registerMacro; - pub const resolve = Bun.resolve; - pub const resolveSync = Bun.resolveSync; - pub const serve = Bun.serve; - pub const sha = JSC.wrapStaticMethod(Crypto.SHA512_256, "hash_", true); - pub const shrink = Bun.shrink; - pub const sleepSync = Bun.sleepSync; - pub const spawn = JSC.wrapStaticMethod(JSC.Subprocess, "spawn", false); - pub const spawnSync = JSC.wrapStaticMethod(JSC.Subprocess, "spawnSync", false); - pub const which = Bun.which; - pub const write = JSC.WebCore.Blob.writeFile; - pub const stringWidth = Bun.stringWidth; - pub const braces = Bun.braces; - pub const shellEscape = Bun.shellEscape; - pub const createParsedShellScript = bun.shell.ParsedShellScript.createParsedShellScript; - pub const createShellInterpreter = bun.shell.Interpreter.createShellInterpreter; + pub const allocUnsafe = toJSCallback(Bun.allocUnsafe); + pub const build = toJSCallback(Bun.JSBundler.buildFn); + pub const connect = toJSCallback(JSC.wrapStaticMethod(JSC.API.Listener, "connect", false)); + pub const deflateSync = toJSCallback(JSZlib.deflateSync); + pub const file = toJSCallback(WebCore.Blob.constructBunFile); + pub const gc = toJSCallback(Bun.runGC); + pub const generateHeapSnapshot = toJSCallback(Bun.generateHeapSnapshot); + pub const gunzipSync = toJSCallback(JSZlib.gunzipSync); + pub const gzipSync = toJSCallback(JSZlib.gzipSync); + pub const indexOfLine = toJSCallback(Bun.indexOfLine); + pub const inflateSync = toJSCallback(JSZlib.inflateSync); + pub const jest = toJSCallback(@import("../test/jest.zig").Jest.call); + pub const listen = toJSCallback(JSC.wrapStaticMethod(JSC.API.Listener, "listen", false)); + pub const udpSocket = toJSCallback(JSC.wrapStaticMethod(JSC.API.UDPSocket, "udpSocket", false)); + pub const mmap = toJSCallback(Bun.mmapFile); + pub const nanoseconds = toJSCallback(Bun.nanoseconds); + pub const openInEditor = toJSCallback(Bun.openInEditor); + pub const registerMacro = toJSCallback(Bun.registerMacro); + pub const resolve = toJSCallback(Bun.resolve); + pub const resolveSync = toJSCallback(Bun.resolveSync); + pub const serve = toJSCallback(Bun.serve); + pub const sha = toJSCallback(JSC.wrapStaticMethod(Crypto.SHA512_256, "hash_", true)); + pub const shrink = toJSCallback(Bun.shrink); + pub const sleepSync = toJSCallback(Bun.sleepSync); + pub const spawn = toJSCallback(JSC.wrapStaticMethod(JSC.Subprocess, "spawn", false)); + pub const spawnSync = toJSCallback(JSC.wrapStaticMethod(JSC.Subprocess, "spawnSync", false)); + pub const which = toJSCallback(Bun.which); + pub const write = toJSCallback(JSC.WebCore.Blob.writeFile); + pub const stringWidth = toJSCallback(Bun.stringWidth); + pub const braces = toJSCallback(Bun.braces); + pub const shellEscape = toJSCallback(Bun.shellEscape); + pub const createParsedShellScript = toJSCallback(bun.shell.ParsedShellScript.createParsedShellScript); + pub const createShellInterpreter = toJSCallback(bun.shell.Interpreter.createShellInterpreter); // --- Callbacks --- // --- Getters --- - pub const CryptoHasher = Crypto.CryptoHasher.getter; - pub const FFI = Bun.FFIObject.getter; - pub const FileSystemRouter = Bun.getFileSystemRouter; - pub const MD4 = Crypto.MD4.getter; - pub const MD5 = Crypto.MD5.getter; - pub const SHA1 = Crypto.SHA1.getter; - pub const SHA224 = Crypto.SHA224.getter; - pub const SHA256 = Crypto.SHA256.getter; - pub const SHA384 = Crypto.SHA384.getter; - pub const SHA512 = Crypto.SHA512.getter; - pub const SHA512_256 = Crypto.SHA512_256.getter; - pub const TOML = Bun.getTOMLObject; - pub const Glob = Bun.getGlobConstructor; - pub const Transpiler = Bun.getTranspilerConstructor; - pub const argv = Bun.getArgv; - pub const assetPrefix = Bun.getAssetPrefix; - pub const cwd = Bun.getCWD; - pub const enableANSIColors = Bun.enableANSIColors; - pub const hash = Bun.getHashObject; - pub const inspect = Bun.getInspect; - pub const main = Bun.getMain; - pub const origin = Bun.getOrigin; - pub const stderr = Bun.getStderr; - pub const stdin = Bun.getStdin; - pub const stdout = Bun.getStdout; - pub const unsafe = Bun.getUnsafe; - pub const semver = Bun.getSemver; + pub const CryptoHasher = toJSGetter(Crypto.CryptoHasher.getter); + pub const FFI = toJSGetter(Bun.FFIObject.getter); + pub const FileSystemRouter = toJSGetter(Bun.getFileSystemRouter); + pub const MD4 = toJSGetter(Crypto.MD4.getter); + pub const MD5 = toJSGetter(Crypto.MD5.getter); + pub const SHA1 = toJSGetter(Crypto.SHA1.getter); + pub const SHA224 = toJSGetter(Crypto.SHA224.getter); + pub const SHA256 = toJSGetter(Crypto.SHA256.getter); + pub const SHA384 = toJSGetter(Crypto.SHA384.getter); + pub const SHA512 = toJSGetter(Crypto.SHA512.getter); + pub const SHA512_256 = toJSGetter(Crypto.SHA512_256.getter); + pub const TOML = toJSGetter(Bun.getTOMLObject); + pub const Glob = toJSGetter(Bun.getGlobConstructor); + pub const Transpiler = toJSGetter(Bun.getTranspilerConstructor); + pub const argv = toJSGetter(Bun.getArgv); + pub const assetPrefix = toJSGetter(Bun.getAssetPrefix); + pub const cwd = toJSGetter(Bun.getCWD); + pub const enableANSIColors = toJSGetter(Bun.enableANSIColors); + pub const hash = toJSGetter(Bun.getHashObject); + pub const inspect = toJSGetter(Bun.getInspect); + pub const main = toJSGetter(Bun.getMain); + pub const origin = toJSGetter(Bun.getOrigin); + pub const stderr = toJSGetter(Bun.getStderr); + pub const stdin = toJSGetter(Bun.getStdin); + pub const stdout = toJSGetter(Bun.getStdout); + pub const unsafe = toJSGetter(Bun.getUnsafe); + pub const semver = toJSGetter(Bun.getSemver); + pub const embeddedFiles = toJSGetter(Bun.getEmbeddedFiles); // --- Getters --- fn getterName(comptime baseName: anytype) [:0]const u8 { @@ -81,6 +83,18 @@ pub const BunObject = struct { return "BunObject_callback_" ++ baseName; } + const toJSCallback = JSC.toJSHostFunction; + + const LazyPropertyCallback = fn (*JSC.JSGlobalObject, *JSC.JSObject) callconv(JSC.conv) JSC.JSValue; + + fn toJSGetter(comptime getter: anytype) LazyPropertyCallback { + return struct { + pub fn callback(this: *JSC.JSGlobalObject, object: *JSC.JSObject) callconv(JSC.conv) JSC.JSValue { + return @call(.always_inline, getter, .{ this, object }); + } + }.callback; + } + pub fn exportAll() void { if (!@inComptime()) { @compileError("Must be comptime"); @@ -118,6 +132,7 @@ pub const BunObject = struct { @export(BunObject.stdout, .{ .name = getterName("stdout") }); @export(BunObject.unsafe, .{ .name = getterName("unsafe") }); @export(BunObject.semver, .{ .name = getterName("semver") }); + @export(BunObject.embeddedFiles, .{ .name = getterName("embeddedFiles") }); // --- Getters -- // -- Callbacks -- @@ -303,7 +318,7 @@ const ShellTask = struct { pub fn shell( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const Interpreter = @import("../../shell/interpreter.zig").Interpreter; // var allocator = globalThis.bunVM().allocator; @@ -314,7 +329,7 @@ pub fn shell( var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); const string_args = arguments.nextEat() orelse { globalThis.throw("shell: expected 2 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; const template_args_js = arguments.nextEat() orelse { @@ -395,7 +410,7 @@ pub fn shell( pub fn shellEscape( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(1); if (arguments.len < 1) { globalThis.throw("shell escape expected at least 1 argument", .{}); @@ -439,14 +454,14 @@ pub fn shellEscape( pub fn braces( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments_ = callframe.arguments(2); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); defer arguments.deinit(); const brace_str_js = arguments.nextEat() orelse { globalThis.throw("braces: expected at least 1 argument, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; const brace_str = brace_str_js.toBunString(globalThis); defer brace_str.deref(); @@ -546,14 +561,14 @@ pub fn braces( pub fn which( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments_ = callframe.arguments(2); var path_buf: bun.PathBuffer = undefined; var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); defer arguments.deinit(); const path_arg = arguments.nextEat() orelse { globalThis.throw("which: expected 1 argument, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; var path_str: ZigString.Slice = ZigString.Slice.empty; @@ -576,7 +591,7 @@ pub fn which( if (bin_str.len >= bun.MAX_PATH_BYTES) { globalThis.throw("bin path is too long", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; } if (bin_str.len == 0) { @@ -608,7 +623,7 @@ pub fn which( cwd_str.slice(), bin_str.slice(), )) |bin_path| { - return ZigString.init(bin_path).withEncoding().toValueGC(globalThis); + return ZigString.init(bin_path).withEncoding().toJS(globalThis); } return JSC.JSValue.jsNull(); @@ -617,7 +632,7 @@ pub fn which( pub fn inspect( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(4).slice(); if (arguments.len == 0) return bun.String.empty.toJS(globalThis); @@ -722,13 +737,13 @@ pub fn inspect( // we are going to always clone to keep things simple for now // the common case here will be stack-allocated, so it should be fine var out = ZigString.init(array.toOwnedSliceLeaky()).withEncoding(); - const ret = out.toValueGC(globalThis); + const ret = out.toJS(globalThis); array.deinit(); return ret; } -pub fn getInspect(globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject) callconv(.C) JSC.JSValue { - const fun = JSC.createCallback(globalObject, ZigString.static("inspect"), 2, &inspect); +pub fn getInspect(globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue { + const fun = JSC.createCallback(globalObject, ZigString.static("inspect"), 2, inspect); var str = ZigString.init("nodejs.util.inspect.custom"); fun.put(globalObject, ZigString.static("custom"), JSC.JSValue.symbolFor(globalObject, &str)); return fun; @@ -737,7 +752,7 @@ pub fn getInspect(globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject) callconv( pub fn registerMacro( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments_ = callframe.arguments(2); const arguments = arguments_.slice(); if (arguments.len != 2 or !arguments[0].isNumber()) { @@ -770,21 +785,21 @@ pub fn registerMacro( pub fn getCWD( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { - return ZigString.init(VirtualMachine.get().bundler.fs.top_level_dir).toValueGC(globalThis); +) JSC.JSValue { + return ZigString.init(VirtualMachine.get().bundler.fs.top_level_dir).toJS(globalThis); } pub fn getOrigin( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { - return ZigString.init(VirtualMachine.get().origin.origin).toValueGC(globalThis); +) JSC.JSValue { + return ZigString.init(VirtualMachine.get().origin.origin).toJS(globalThis); } pub fn getStdin( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { var rare_data = globalThis.bunVM().rareData(); var store = rare_data.stdin(); store.ref(); @@ -798,7 +813,7 @@ pub fn getStdin( pub fn getStderr( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { var rare_data = globalThis.bunVM().rareData(); var store = rare_data.stderr(); store.ref(); @@ -812,7 +827,7 @@ pub fn getStderr( pub fn getStdout( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { var rare_data = globalThis.bunVM().rareData(); var store = rare_data.stdout(); store.ref(); @@ -826,14 +841,14 @@ pub fn getStdout( pub fn enableANSIColors( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { _ = globalThis; return JSValue.jsBoolean(Output.enable_ansi_colors); } pub fn getMain( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const vm = globalThis.bunVM(); // Attempt to use the resolved filesystem path @@ -880,20 +895,20 @@ pub fn getMain( return vm.main_resolved_path.toJS(globalThis); } - return ZigString.init(vm.main).toValueGC(globalThis); + return ZigString.init(vm.main).toJS(globalThis); } pub fn getAssetPrefix( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { - return ZigString.init(VirtualMachine.get().bundler.options.routes.asset_prefix_path).toValueGC(globalThis); +) JSC.JSValue { + return ZigString.init(VirtualMachine.get().bundler.options.routes.asset_prefix_path).toJS(globalThis); } pub fn getArgv( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return JSC.Node.Process.getArgv(globalThis); } @@ -901,7 +916,7 @@ const Editor = @import("../../open.zig").Editor; pub fn openInEditor( globalThis: js.JSContextRef, callframe: *JSC.CallFrame, -) callconv(.C) JSValue { +) JSValue { var edit = &VirtualMachine.get().rareData().editor_context; const args = callframe.arguments(4); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), args.slice()); @@ -967,7 +982,7 @@ pub fn openInEditor( return .zero; }; - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn getPublicPath(to: string, origin: URL, comptime Writer: type, writer: Writer) void { @@ -1019,7 +1034,7 @@ pub fn getPublicPathWithAssetPrefix( } } -pub fn sleepSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn sleepSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments = callframe.arguments(1); // Expect at least one argument. We allow more than one but ignore them; this @@ -1047,16 +1062,16 @@ pub fn sleepSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) c return .undefined; } -pub fn generateHeapSnapshot(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn generateHeapSnapshot(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { return globalObject.generateHeapSnapshot(); } -pub fn runGC(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn runGC(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments_ = callframe.arguments(1); const arguments = arguments_.slice(); return globalObject.bunVM().garbageCollect(arguments.len > 0 and arguments[0].isBoolean() and arguments[0].toBoolean()); } -pub fn shrink(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn shrink(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { globalObject.vm().shrinkFootprint(); return .undefined; } @@ -1167,13 +1182,13 @@ fn doResolveWithArgs( return null; }; - return ZigString.initUTF8(arraylist.items).toValueGC(ctx); + return ZigString.initUTF8(arraylist.items).toJS(ctx); } return errorable.result.value.toJS(ctx); } -pub fn resolveSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn resolveSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; const exception = &exception_; const arguments = callframe.arguments(3); @@ -1186,7 +1201,7 @@ pub fn resolveSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) return result orelse .zero; } -pub fn resolve(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn resolve(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; const exception = &exception_; const arguments = callframe.arguments(3); @@ -1266,7 +1281,7 @@ export fn Bun__resolveSyncWithSource( }; } -pub fn getPublicPathJS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn getPublicPathJS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments = callframe.arguments(1).slice(); if (arguments.len < 1) { return bun.String.empty.toJS(globalObject); @@ -1279,20 +1294,20 @@ pub fn getPublicPathJS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFr var writer = stream.writer(); getPublicPath(to.slice(), VirtualMachine.get().origin, @TypeOf(&writer), &writer); - return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(globalObject); + return ZigString.init(stream.buffer[0..stream.pos]).toJS(globalObject); } extern fn dump_zone_malloc_stats() void; -export fn dump_mimalloc(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { +fn dump_mimalloc(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { globalObject.bunVM().arena.dumpStats(); - if (comptime bun.is_heap_breakdown_enabled) { + if (bun.heap_breakdown.enabled) { dump_zone_malloc_stats(); } return .undefined; } -pub fn indexOfLine(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn indexOfLine(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments_ = callframe.arguments(2); const arguments = arguments_.slice(); if (arguments.len == 0) { @@ -1458,6 +1473,29 @@ pub const Crypto = struct { pub const Digest = [BoringSSL.EVP_MAX_MD_SIZE]u8; + /// For usage in Zig + pub fn pbkdf2( + output: []u8, + password: []const u8, + salt: []const u8, + iteration_count: u32, + algorithm: Algorithm, + ) ?[]const u8 { + var pbk = PBKDF2{ + .algorithm = algorithm, + .password = JSC.Node.StringOrBuffer{ .encoded_slice = JSC.ZigString.Slice.fromUTF8NeverFree(password) }, + .salt = JSC.Node.StringOrBuffer{ .encoded_slice = JSC.ZigString.Slice.fromUTF8NeverFree(salt) }, + .iteration_count = iteration_count, + .length = @intCast(output.len), + }; + + if (!pbk.run(output)) { + return null; + } + + return output; + } + pub const PBKDF2 = struct { password: JSC.Node.StringOrBuffer = JSC.Node.StringOrBuffer.empty, salt: JSC.Node.StringOrBuffer = JSC.Node.StringOrBuffer.empty, @@ -1538,7 +1576,7 @@ pub const Crypto = struct { assert(output_slice.len == @as(usize, @intCast(this.pbkdf2.length))); const buffer_value = JSC.JSValue.createBuffer(globalThis, output_slice, bun.default_allocator); if (buffer_value == .zero) { - promise.reject(globalThis, globalThis.createTypeErrorInstance("Failed to create buffer", .{})); + promise.reject(globalThis, ZigString.init("Failed to create buffer").toErrorInstance(globalThis)); return; } @@ -1627,8 +1665,7 @@ pub const Crypto = struct { const slice = arguments[4].toSlice(globalThis, bun.default_allocator); defer slice.deinit(); const name = slice.slice(); - const err = globalThis.createTypeErrorInstanceWithCode(.ERR_CRYPTO_INVALID_DIGEST, "Unsupported algorithm \"{s}\"", .{name}); - globalThis.throwValue(err); + globalThis.ERR_CRYPTO_INVALID_DIGEST("Unsupported algorithm \"{s}\"", .{name}).throw(); } return null; }; @@ -1763,19 +1800,7 @@ pub const Crypto = struct { }; pub fn createCryptoError(globalThis: *JSC.JSGlobalObject, err_code: u32) JSValue { - var outbuf: [128 + 1 + "BoringSSL error: ".len]u8 = undefined; - @memset(&outbuf, 0); - outbuf[0.."BoringSSL error: ".len].* = "BoringSSL error: ".*; - const message_buf = outbuf["BoringSSL error: ".len..]; - - _ = BoringSSL.ERR_error_string_n(err_code, message_buf, message_buf.len); - - const error_message: []const u8 = bun.sliceTo(outbuf[0..], 0); - if (error_message.len == "BoringSSL error: ".len) { - return ZigString.static("Unknown BoringSSL error").toErrorInstance(globalThis); - } - - return ZigString.fromUTF8(error_message).toErrorInstance(globalThis); + return BoringSSL.ERR_toJS(globalThis, err_code); } const unknown_password_algorithm_message = "unknown algorithm, expected one of: \"bcrypt\", \"argon2id\", \"argon2d\", \"argon2i\" (default is \"argon2id\")"; @@ -2142,6 +2167,8 @@ pub const Crypto = struct { ref: Async.KeepAlive = .{}, task: JSC.WorkPoolTask = .{ .callback = &run }, + pub usingnamespace bun.New(@This()); + pub const Result = struct { value: Value, ref: Async.KeepAlive = .{}, @@ -2150,6 +2177,8 @@ pub const Crypto = struct { promise: JSC.JSPromise.Strong, global: *JSC.JSGlobalObject, + pub usingnamespace bun.New(@This()); + pub const Value = union(enum) { err: PasswordObject.HashError, hash: []const u8, @@ -2158,25 +2187,26 @@ pub const Crypto = struct { const error_code = std.fmt.allocPrint(bun.default_allocator, "PASSWORD_{}", .{PascalToUpperUnderscoreCaseFormatter{ .input = @errorName(this.err) }}) catch bun.outOfMemory(); defer bun.default_allocator.free(error_code); const instance = globalObject.createErrorInstance("Password hashing failed with error \"{s}\"", .{@errorName(this.err)}); - instance.put(globalObject, ZigString.static("code"), JSC.ZigString.init(error_code).toValueGC(globalObject)); + instance.put(globalObject, ZigString.static("code"), JSC.ZigString.init(error_code).toJS(globalObject)); return instance; } }; pub fn runFromJS(this: *Result) void { var promise = this.promise; + defer promise.deinit(); this.promise = .{}; this.ref.unref(this.global.bunVM()); const global = this.global; switch (this.value) { .err => { const error_instance = this.value.toErrorInstance(global); - bun.default_allocator.destroy(this); + this.destroy(); promise.reject(global, error_instance); }, .hash => |value| { - const js_string = JSC.ZigString.init(value).toValueGC(global); - bun.default_allocator.destroy(this); + const js_string = JSC.ZigString.init(value).toJS(global); + this.destroy(); promise.resolve(global, js_string); }, } @@ -2184,10 +2214,9 @@ pub const Crypto = struct { }; pub fn deinit(this: *HashJob) void { - this.ref = .{}; - this.promise.strong.deinit(); + this.promise.deinit(); bun.default_allocator.free(this.password); - bun.default_allocator.destroy(this); + this.destroy(); } pub fn getValue(password: []const u8, algorithm: PasswordObject.Algorithm.Value) Result.Value { @@ -2200,23 +2229,17 @@ pub const Crypto = struct { pub fn run(task: *bun.ThreadPool.Task) void { var this: *HashJob = @fieldParentPtr("task", task); - var result = bun.default_allocator.create(Result) catch bun.outOfMemory(); - result.* = Result{ + var result = Result.new(.{ .value = getValue(this.password, this.algorithm), - .task = JSC.AnyTask.New(Result, Result.runFromJS).init(result), + .task = undefined, .promise = this.promise, .global = this.global, .ref = this.ref, - }; + }); + result.task = JSC.AnyTask.New(Result, Result.runFromJS).init(result); this.ref = .{}; this.promise.strong = .{}; - - const concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch bun.outOfMemory(); - concurrent_task.* = JSC.ConcurrentTask{ - .task = JSC.Task.init(&result.task), - .auto_delete = true, - }; - this.event_loop.enqueueTaskConcurrent(concurrent_task); + this.event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.createFrom(&result.task)); this.deinit(); } }; @@ -2237,24 +2260,22 @@ pub const Crypto = struct { return .zero; }, .hash => |h| { - return JSC.ZigString.init(h).toValueGC(globalObject); + return JSC.ZigString.init(h).toJS(globalObject); }, } unreachable; } - var job = bun.default_allocator.create(HashJob) catch bun.outOfMemory(); - var promise = JSC.JSPromise.Strong.init(globalObject); + const promise = JSC.JSPromise.Strong.init(globalObject); - job.* = HashJob{ + var job = HashJob.new(.{ .algorithm = algorithm, .password = password, .promise = promise, .event_loop = globalObject.bunVM().eventLoop(), .global = globalObject, - }; - + }); job.ref.ref(globalObject.bunVM()); JSC.WorkPool.schedule(&job.task); @@ -2286,18 +2307,16 @@ pub const Crypto = struct { unreachable; } - var job = bun.default_allocator.create(VerifyJob) catch bun.outOfMemory(); var promise = JSC.JSPromise.Strong.init(globalObject); - job.* = VerifyJob{ + const job = VerifyJob.new(.{ .algorithm = algorithm, .password = password, .prev_hash = prev_hash, .promise = promise, .event_loop = globalObject.bunVM().eventLoop(), .global = globalObject, - }; - + }); job.ref.ref(globalObject.bunVM()); JSC.WorkPool.schedule(&job.task); @@ -2305,10 +2324,10 @@ pub const Crypto = struct { } // Once we have bindings generator, this should be replaced with a generated function - pub export fn JSPasswordObject__hash( + pub fn JSPasswordObject__hash( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments_ = callframe.arguments(2); const arguments = arguments_.ptr[0..arguments_.len]; @@ -2325,7 +2344,9 @@ pub const Crypto = struct { } const password_to_hash = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(globalObject, arguments[0], bun.default_allocator) catch { - globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + } return JSC.JSValue.undefined; }; @@ -2339,10 +2360,10 @@ pub const Crypto = struct { } // Once we have bindings generator, this should be replaced with a generated function - pub export fn JSPasswordObject__hashSync( + pub fn JSPasswordObject__hashSync( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments_ = callframe.arguments(2); const arguments = arguments_.ptr[0..arguments_.len]; @@ -2359,7 +2380,9 @@ pub const Crypto = struct { } var string_or_buffer = JSC.Node.StringOrBuffer.fromJS(globalObject, bun.default_allocator, arguments[0]) orelse { - globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + } return JSC.JSValue.undefined; }; @@ -2384,6 +2407,8 @@ pub const Crypto = struct { ref: Async.KeepAlive = .{}, task: JSC.WorkPoolTask = .{ .callback = &run }, + pub usingnamespace bun.New(@This()); + pub const Result = struct { value: Value, ref: Async.KeepAlive = .{}, @@ -2392,6 +2417,8 @@ pub const Crypto = struct { promise: JSC.JSPromise.Strong, global: *JSC.JSGlobalObject, + pub usingnamespace bun.New(@This()); + pub const Value = union(enum) { err: PasswordObject.HashError, pass: bool, @@ -2400,24 +2427,25 @@ pub const Crypto = struct { const error_code = std.fmt.allocPrint(bun.default_allocator, "PASSWORD{}", .{PascalToUpperUnderscoreCaseFormatter{ .input = @errorName(this.err) }}) catch bun.outOfMemory(); defer bun.default_allocator.free(error_code); const instance = globalObject.createErrorInstance("Password verification failed with error \"{s}\"", .{@errorName(this.err)}); - instance.put(globalObject, ZigString.static("code"), JSC.ZigString.init(error_code).toValueGC(globalObject)); + instance.put(globalObject, ZigString.static("code"), JSC.ZigString.init(error_code).toJS(globalObject)); return instance; } }; pub fn runFromJS(this: *Result) void { var promise = this.promise; + defer promise.deinit(); this.promise = .{}; this.ref.unref(this.global.bunVM()); const global = this.global; switch (this.value) { .err => { const error_instance = this.value.toErrorInstance(global); - bun.default_allocator.destroy(this); + this.destroy(); promise.reject(global, error_instance); }, .pass => |pass| { - bun.default_allocator.destroy(this); + this.destroy(); promise.resolve(global, JSC.JSValue.jsBoolean(pass)); }, } @@ -2425,11 +2453,10 @@ pub const Crypto = struct { }; pub fn deinit(this: *VerifyJob) void { - this.ref = .{}; - this.promise.strong.deinit(); + this.promise.deinit(); bun.default_allocator.free(this.password); bun.default_allocator.free(this.prev_hash); - bun.default_allocator.destroy(this); + this.destroy(); } pub fn getValue(password: []const u8, prev_hash: []const u8, algorithm: ?PasswordObject.Algorithm) Result.Value { @@ -2442,32 +2469,26 @@ pub const Crypto = struct { pub fn run(task: *bun.ThreadPool.Task) void { var this: *VerifyJob = @fieldParentPtr("task", task); - var result = bun.default_allocator.create(Result) catch bun.outOfMemory(); - result.* = Result{ + var result = Result.new(.{ .value = getValue(this.password, this.prev_hash, this.algorithm), - .task = JSC.AnyTask.New(Result, Result.runFromJS).init(result), + .task = undefined, .promise = this.promise, .global = this.global, .ref = this.ref, - }; + }); + result.task = JSC.AnyTask.New(Result, Result.runFromJS).init(result); this.ref = .{}; this.promise.strong = .{}; - - const concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch bun.outOfMemory(); - concurrent_task.* = JSC.ConcurrentTask{ - .task = JSC.Task.init(&result.task), - .auto_delete = true, - }; - this.event_loop.enqueueTaskConcurrent(concurrent_task); + this.event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.createFrom(&result.task)); this.deinit(); } }; // Once we have bindings generator, this should be replaced with a generated function - pub export fn JSPasswordObject__verify( + pub fn JSPasswordObject__verify( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments_ = callframe.arguments(3); const arguments = arguments_.ptr[0..arguments_.len]; @@ -2487,19 +2508,21 @@ pub const Crypto = struct { const algorithm_string = arguments[2].getZigString(globalObject); algorithm = PasswordObject.Algorithm.label.getWithEql(algorithm_string, JSC.ZigString.eqlComptime) orelse { - globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + } return JSC.JSValue.undefined; }; } const owned_password = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(globalObject, arguments[0], bun.default_allocator) catch |err| { - if (err != error.JSError) globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); + if (err != error.JSError and !globalObject.hasException()) globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); return JSC.JSValue.undefined; }; const owned_hash = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(globalObject, arguments[1], bun.default_allocator) catch |err| { bun.default_allocator.free(owned_password); - if (err != error.JSError) globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); + if (err != error.JSError and !globalObject.hasException()) globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); return JSC.JSValue.undefined; }; @@ -2517,10 +2540,10 @@ pub const Crypto = struct { } // Once we have bindings generator, this should be replaced with a generated function - pub export fn JSPasswordObject__verifySync( + pub fn JSPasswordObject__verifySync( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments_ = callframe.arguments(3); const arguments = arguments_.ptr[0..arguments_.len]; @@ -2540,19 +2563,25 @@ pub const Crypto = struct { const algorithm_string = arguments[2].getZigString(globalObject); algorithm = PasswordObject.Algorithm.label.getWithEql(algorithm_string, JSC.ZigString.eqlComptime) orelse { - globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + } return JSC.JSValue.undefined; }; } var password = JSC.Node.StringOrBuffer.fromJS(globalObject, bun.default_allocator, arguments[0]) orelse { - globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); + } return JSC.JSValue.undefined; }; var hash_ = JSC.Node.StringOrBuffer.fromJS(globalObject, bun.default_allocator, arguments[1]) orelse { password.deinit(); - globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); + } return JSC.JSValue.undefined; }; @@ -2586,7 +2615,7 @@ pub const Crypto = struct { pub fn getByteLength( this: *CryptoHasher, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsNumber(switch (this.*) { .evp => |*inner| inner.size(), .zig => |*inner| inner.digest_length, @@ -2596,9 +2625,9 @@ pub const Crypto = struct { pub fn getAlgorithm( this: *CryptoHasher, globalObject: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return switch (this.*) { - inline else => |*inner| ZigString.fromUTF8(bun.asByteSlice(@tagName(inner.algorithm))).toValueGC(globalObject), + inline else => |*inner| ZigString.fromUTF8(bun.asByteSlice(@tagName(inner.algorithm))).toJS(globalObject), }; } @@ -2606,7 +2635,7 @@ pub const Crypto = struct { globalThis_: *JSC.JSGlobalObject, _: JSValue, _: JSValue, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var values = EVP.Algorithm.names.values; return JSC.JSValue.createStringArray(globalThis_, &values, values.len, true); } @@ -2708,7 +2737,7 @@ pub const Crypto = struct { } } - pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) ?*CryptoHasher { + pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) ?*CryptoHasher { const arguments = callframe.arguments(2); if (arguments.len == 0) { globalThis.throwInvalidArguments("Expected an algorithm name as an argument", .{}); @@ -2740,17 +2769,21 @@ pub const Crypto = struct { pub fn getter( globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return CryptoHasher.getConstructor(globalObject); } - pub fn update(this: *CryptoHasher, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn update(this: *CryptoHasher, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const thisValue = callframe.this(); const arguments = callframe.arguments(2); const input = arguments.ptr[0]; + if (input.isEmptyOrUndefinedOrNull()) { + globalThis.throwInvalidArguments("expected blob, string or buffer", .{}); + return JSC.JSValue.zero; + } const encoding = arguments.ptr[1]; const buffer = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValue(globalThis, globalThis.bunVM().allocator, input, encoding) orelse { - globalThis.throwInvalidArguments("expected blob, string or buffer", .{}); + if (!globalThis.hasException()) globalThis.throwInvalidArguments("expected blob, string or buffer", .{}); return JSC.JSValue.zero; }; defer buffer.deinit(); @@ -2783,7 +2816,7 @@ pub const Crypto = struct { this: *CryptoHasher, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var new: CryptoHasher = undefined; switch (this.*) { .evp => |*inner| { @@ -2862,7 +2895,7 @@ pub const Crypto = struct { }; } - pub fn finalize(this: *CryptoHasher) callconv(.C) void { + pub fn finalize(this: *CryptoHasher) void { switch (this.*) { .evp => |*inner| { // https://github.com/oven-sh/bun/issues/3250 @@ -2959,7 +2992,7 @@ pub const Crypto = struct { } } - fn constructor(algorithm: ZigString) callconv(.C) ?*CryptoHasher { + fn constructor(algorithm: ZigString) ?*CryptoHasher { inline for (algo_map) |item| { if (bun.strings.eqlComptime(algorithm.slice(), item[0])) { return CryptoHasher.new(.{ .zig = .{ @@ -3028,7 +3061,7 @@ pub const Crypto = struct { pub fn getByteLength( _: *@This(), _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)); } @@ -3036,7 +3069,7 @@ pub const Crypto = struct { _: *JSC.JSGlobalObject, _: JSValue, _: JSValue, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)); } @@ -3123,7 +3156,7 @@ pub const Crypto = struct { } } - pub fn constructor(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*@This() { + pub fn constructor(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*@This() { const this = bun.default_allocator.create(@This()) catch return null; this.* = .{ .hashing = Hasher.init() }; @@ -3133,11 +3166,11 @@ pub const Crypto = struct { pub fn getter( globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return ThisHasher.getConstructor(globalObject); } - pub fn update(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn update(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const thisValue = callframe.this(); const input = callframe.argument(0); const buffer = JSC.Node.BlobOrStringOrBuffer.fromJS(globalThis, globalThis.bunVM().allocator, input) orelse { @@ -3224,7 +3257,7 @@ pub const Crypto = struct { return encoding.encodeWithSize(globalThis, Hasher.digest, output_digest_slice); } - pub fn finalize(this: *@This()) callconv(.C) void { + pub fn finalize(this: *@This()) void { VirtualMachine.get().allocator.destroy(this); } }; @@ -3243,7 +3276,7 @@ pub const Crypto = struct { pub fn nanoseconds( globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const ns = globalThis.bunVM().origin_timer.read(); return JSC.JSValue.jsNumberFromUint64(ns); } @@ -3251,7 +3284,7 @@ pub fn nanoseconds( pub fn serve( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(2).slice(); var config: JSC.API.ServerConfig = brk: { var exception_ = [1]JSC.JSValueRef{null}; @@ -3412,13 +3445,13 @@ pub export fn Bun__escapeHTML16(globalObject: *JSC.JSGlobalObject, input_value: assert(len > 0); const input_slice = ptr[0..len]; const escaped = strings.escapeHTMLForUTF16Input(globalObject.bunVM().allocator, input_slice) catch { - globalObject.vm().throwError(globalObject, ZigString.init("Out of memory").toValue(globalObject)); - return JSC.JSValue.jsUndefined(); + globalObject.vm().throwError(globalObject, ZigString.init("Out of memory").toJS(globalObject)); + return .undefined; }; switch (escaped) { .static => |val| { - return ZigString.init(val).toValue(globalObject); + return ZigString.init(val).toJS(globalObject); }, .original => return input_value, .allocated => |escaped_html| { @@ -3458,13 +3491,13 @@ pub export fn Bun__escapeHTML8(globalObject: *JSC.JSGlobalObject, input_value: J const allocator = if (input_slice.len <= 32) stack_allocator.get() else stack_allocator.fallback_allocator; const escaped = strings.escapeHTMLForLatin1Input(allocator, input_slice) catch { - globalObject.vm().throwError(globalObject, ZigString.init("Out of memory").toValue(globalObject)); - return JSC.JSValue.jsUndefined(); + globalObject.vm().throwError(globalObject, ZigString.init("Out of memory").toJS(globalObject)); + return .undefined; }; switch (escaped) { .static => |val| { - return ZigString.init(val).toValue(globalObject); + return ZigString.init(val).toJS(globalObject); }, .original => return input_value, .allocated => |escaped_html| { @@ -3503,7 +3536,7 @@ comptime { pub fn allocUnsafe( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(1); const size = arguments.ptr[0]; if (!size.isUInt32AsAnyInt()) { @@ -3517,7 +3550,7 @@ pub fn allocUnsafe( pub fn mmapFile( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { if (comptime Environment.isWindows) { globalThis.throwTODO("mmapFile is not supported on Windows"); return JSC.JSValue.zero; @@ -3596,33 +3629,33 @@ pub fn mmapFile( pub fn getTranspilerConstructor( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return JSC.API.JSTranspiler.getConstructor(globalThis); } pub fn getFileSystemRouter( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return JSC.API.FileSystemRouter.getConstructor(globalThis); } pub fn getHashObject( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return HashObject.create(globalThis); } const HashObject = struct { - pub const wyhash = hashWrap(std.hash.Wyhash).hash; - pub const adler32 = hashWrap(std.hash.Adler32).hash; - pub const crc32 = hashWrap(std.hash.Crc32).hash; - pub const cityHash32 = hashWrap(std.hash.CityHash32).hash; - pub const cityHash64 = hashWrap(std.hash.CityHash64).hash; - pub const murmur32v2 = hashWrap(std.hash.murmur.Murmur2_32).hash; - pub const murmur32v3 = hashWrap(std.hash.murmur.Murmur3_32).hash; - pub const murmur64v2 = hashWrap(std.hash.murmur.Murmur2_64).hash; + pub const wyhash = hashWrap(std.hash.Wyhash); + pub const adler32 = hashWrap(std.hash.Adler32); + pub const crc32 = hashWrap(std.hash.Crc32); + pub const cityHash32 = hashWrap(std.hash.CityHash32); + pub const cityHash64 = hashWrap(std.hash.CityHash64); + pub const murmur32v2 = hashWrap(std.hash.murmur.Murmur2_32); + pub const murmur32v3 = hashWrap(std.hash.murmur.Murmur3_32); + pub const murmur64v2 = hashWrap(std.hash.murmur.Murmur2_64); pub fn create(globalThis: *JSC.JSGlobalObject) JSC.JSValue { const function = JSC.createCallback(globalThis, ZigString.static("hash"), 1, &wyhash); @@ -3641,7 +3674,7 @@ const HashObject = struct { globalThis, ZigString.static(name), 1, - &@field(HashObject, name), + @field(HashObject, name), ); function.put(globalThis, comptime ZigString.static(name), value); } @@ -3649,13 +3682,13 @@ const HashObject = struct { return function; } - fn hashWrap(comptime Hasher_: anytype) type { + fn hashWrap(comptime Hasher_: anytype) JSC.JSHostFunctionType { return struct { const Hasher = Hasher_; pub fn hash( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2).slice(); var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); defer args.deinit(); @@ -3677,6 +3710,7 @@ const HashObject = struct { .Uint16Array, .Int32Array, .Uint32Array, + .Float16Array, .Float32Array, .Float64Array, .BigInt64Array, @@ -3726,35 +3760,71 @@ const HashObject = struct { return JSC.JSValue.fromUInt64NoTruncate(globalThis, value); } } - }; + }.hash; } }; pub fn getTOMLObject( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return TOMLObject.create(globalThis); } pub fn getGlobConstructor( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return JSC.API.Glob.getConstructor(globalThis); } +pub fn getEmbeddedFiles( + globalThis: *JSC.JSGlobalObject, + _: *JSC.JSObject, +) JSC.JSValue { + const vm = globalThis.bunVM(); + const graph = vm.standalone_module_graph orelse return JSC.JSValue.createEmptyArray(globalThis, 0); + + const unsorted_files = graph.files.values(); + var sort_indices = std.ArrayList(u32).initCapacity(bun.default_allocator, unsorted_files.len) catch bun.outOfMemory(); + defer sort_indices.deinit(); + for (0..unsorted_files.len) |index| { + // Some % of people using `bun build --compile` want to obscure the source code + // We don't really do that right now, but exposing the output source + // code here as an easily accessible Blob is even worse for them. + // So let's omit any source code files from the list. + if (unsorted_files[index].loader.isJavaScriptLike()) continue; + sort_indices.appendAssumeCapacity(@intCast(index)); + } + + var i: u32 = 0; + var array = JSC.JSValue.createEmptyArray(globalThis, sort_indices.items.len); + std.mem.sort(u32, sort_indices.items, unsorted_files, bun.StandaloneModuleGraph.File.lessThanByIndex); + for (sort_indices.items) |index| { + const file = &unsorted_files[index]; + // We call .dupe() on this to ensure that we don't return a blob that might get freed later. + const input_blob = file.blob(globalThis); + const blob = JSC.WebCore.Blob.new(input_blob.dupeWithContentType(true)); + blob.allocator = bun.default_allocator; + blob.name = input_blob.name.dupeRef(); + array.putIndex(globalThis, i, blob.toJS(globalThis)); + i += 1; + } + + return array; +} + pub fn getSemver( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return SemverObject.create(globalThis); } pub fn getUnsafe( globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { return UnsafeObject.create(globalThis); } @@ -3762,9 +3832,9 @@ const UnsafeObject = struct { pub fn create(globalThis: *JSC.JSGlobalObject) JSC.JSValue { const object = JSValue.createEmptyObject(globalThis, 3); const fields = comptime .{ - .gcAggressionLevel = &gcAggressionLevel, - .arrayBufferToString = &arrayBufferToString, - .mimallocDump = &dump_mimalloc, + .gcAggressionLevel = gcAggressionLevel, + .arrayBufferToString = arrayBufferToString, + .mimallocDump = dump_mimalloc, }; inline for (comptime std.meta.fieldNames(@TypeOf(fields))) |name| { object.put( @@ -3779,7 +3849,7 @@ const UnsafeObject = struct { pub fn gcAggressionLevel( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const ret = JSValue.jsNumber(@as(i32, @intFromEnum(globalThis.bunVM().aggressive_garbage_collection))); const value = callframe.arguments(1).ptr[0]; @@ -3797,7 +3867,7 @@ const UnsafeObject = struct { pub fn arrayBufferToString( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const args = callframe.arguments(2).slice(); if (args.len < 1 or !args[0].isCell() or !args[0].jsType().isTypedArray()) { globalThis.throwInvalidArguments("Expected an ArrayBuffer", .{}); @@ -3811,10 +3881,10 @@ const UnsafeObject = struct { zig_str._unsafe_ptr_do_not_use = @as([*]const u8, @ptrCast(@alignCast(array_buffer.ptr))); zig_str.len = array_buffer.len; zig_str.markUTF16(); - return zig_str.toValueGC(globalThis); + return zig_str.toJS(globalThis); }, else => { - return ZigString.init(array_buffer.slice()).toValueGC(globalThis); + return ZigString.init(array_buffer.slice()).toJS(globalThis); }, } } @@ -3832,7 +3902,7 @@ const TOMLObject = struct { globalThis, ZigString.static("parse"), 1, - &parse, + parse, ), ); @@ -3842,7 +3912,7 @@ const TOMLObject = struct { pub fn parse( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var arena = bun.ArenaAllocator.init(globalThis.allocator()); const allocator = arena.allocator(); defer arena.deinit(); @@ -3867,7 +3937,7 @@ const TOMLObject = struct { return .zero; }; var writer = js_printer.BufferPrinter.init(buffer_writer); - _ = js_printer.printJSON(*js_printer.BufferPrinter, &writer, parse_result, &source) catch { + _ = js_printer.printJSON(*js_printer.BufferPrinter, &writer, parse_result, &source, .{}) catch { globalThis.throwValue(log.toJS(globalThis, default_allocator, "Failed to print toml")); return .zero; }; @@ -3911,7 +3981,7 @@ pub const FFIObject = struct { } } - pub const dom_call = JSC.DOMCall("FFI", @This(), "ptr", f64, JSC.DOMEffect.forRead(.TypedArrayProperties)); + pub const dom_call = JSC.DOMCall("FFI", @This(), "ptr", JSC.DOMEffect.forRead(.TypedArrayProperties)); pub fn toJS(globalObject: *JSC.JSGlobalObject) JSC.JSValue { const object = JSC.JSValue.createEmptyObject(globalObject, comptime std.meta.fieldNames(@TypeOf(fields)).len + 2); @@ -3931,18 +4001,18 @@ pub const FFIObject = struct { pub const Reader = struct { pub const DOMCalls = .{ - .u8 = JSC.DOMCall("Reader", @This(), "u8", i32, JSC.DOMEffect.forRead(.World)), - .u16 = JSC.DOMCall("Reader", @This(), "u16", i32, JSC.DOMEffect.forRead(.World)), - .u32 = JSC.DOMCall("Reader", @This(), "u32", i32, JSC.DOMEffect.forRead(.World)), - .ptr = JSC.DOMCall("Reader", @This(), "ptr", i52, JSC.DOMEffect.forRead(.World)), - .i8 = JSC.DOMCall("Reader", @This(), "i8", i32, JSC.DOMEffect.forRead(.World)), - .i16 = JSC.DOMCall("Reader", @This(), "i16", i32, JSC.DOMEffect.forRead(.World)), - .i32 = JSC.DOMCall("Reader", @This(), "i32", i32, JSC.DOMEffect.forRead(.World)), - .i64 = JSC.DOMCall("Reader", @This(), "i64", i64, JSC.DOMEffect.forRead(.World)), - .u64 = JSC.DOMCall("Reader", @This(), "u64", u64, JSC.DOMEffect.forRead(.World)), - .intptr = JSC.DOMCall("Reader", @This(), "intptr", i52, JSC.DOMEffect.forRead(.World)), - .f32 = JSC.DOMCall("Reader", @This(), "f32", f64, JSC.DOMEffect.forRead(.World)), - .f64 = JSC.DOMCall("Reader", @This(), "f64", f64, JSC.DOMEffect.forRead(.World)), + .u8 = JSC.DOMCall("Reader", @This(), "u8", JSC.DOMEffect.forRead(.World)), + .u16 = JSC.DOMCall("Reader", @This(), "u16", JSC.DOMEffect.forRead(.World)), + .u32 = JSC.DOMCall("Reader", @This(), "u32", JSC.DOMEffect.forRead(.World)), + .ptr = JSC.DOMCall("Reader", @This(), "ptr", JSC.DOMEffect.forRead(.World)), + .i8 = JSC.DOMCall("Reader", @This(), "i8", JSC.DOMEffect.forRead(.World)), + .i16 = JSC.DOMCall("Reader", @This(), "i16", JSC.DOMEffect.forRead(.World)), + .i32 = JSC.DOMCall("Reader", @This(), "i32", JSC.DOMEffect.forRead(.World)), + .i64 = JSC.DOMCall("Reader", @This(), "i64", JSC.DOMEffect.forRead(.World)), + .u64 = JSC.DOMCall("Reader", @This(), "u64", JSC.DOMEffect.forRead(.World)), + .intptr = JSC.DOMCall("Reader", @This(), "intptr", JSC.DOMEffect.forRead(.World)), + .f32 = JSC.DOMCall("Reader", @This(), "f32", JSC.DOMEffect.forRead(.World)), + .f64 = JSC.DOMCall("Reader", @This(), "f64", JSC.DOMEffect.forRead(.World)), }; pub fn toJS(globalThis: *JSC.JSGlobalObject) JSC.JSValue { @@ -4121,7 +4191,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) u8, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4131,7 +4201,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) u16, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4141,7 +4211,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) u32, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4151,7 +4221,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) u64, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4161,7 +4231,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) i8, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4171,7 +4241,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) i16, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4181,7 +4251,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) i32, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4191,7 +4261,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) i64, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4202,7 +4272,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) f32, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4213,7 +4283,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) f64, @ptrFromInt(addr)).*; return JSValue.jsNumber(value); @@ -4224,7 +4294,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) u64, @ptrFromInt(addr)).*; return JSValue.fromUInt64NoTruncate(global, value); @@ -4235,7 +4305,7 @@ pub const FFIObject = struct { _: *anyopaque, raw_addr: i64, offset: i32, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset)); const value = @as(*align(1) i64, @ptrFromInt(addr)).*; return JSValue.fromInt64NoTruncate(global, value); @@ -4258,7 +4328,7 @@ pub const FFIObject = struct { _: *JSGlobalObject, _: *anyopaque, array: *JSC.JSUint8Array, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { return JSValue.fromPtrAddress(@intFromPtr(array.ptr())); } @@ -4518,15 +4588,15 @@ pub const FFIObject = struct { pub fn getter( globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return FFIObject.toJS(globalObject); } }; -fn stringWidth(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +fn stringWidth(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments = callframe.arguments(2).slice(); - const value = if (arguments.len > 0) arguments[0] else JSC.JSValue.jsUndefined(); - const options_object = if (arguments.len > 1) arguments[1] else JSC.JSValue.jsUndefined(); + const value = if (arguments.len > 0) arguments[0] else .undefined; + const options_object = if (arguments.len > 1) arguments[1] else .undefined; if (!value.isString()) { return JSC.jsNumber(0); @@ -4619,143 +4689,341 @@ pub const JSZlib = struct { reader.list.deinit(reader.allocator); reader.deinit(); } - + export fn global_deallocator(_: ?*anyopaque, ctx: ?*anyopaque) void { + comptime assert(bun.use_mimalloc); + bun.Mimalloc.mi_free(ctx); + } export fn compressor_deallocator(_: ?*anyopaque, ctx: ?*anyopaque) void { var compressor: *zlib.ZlibCompressorArrayList = bun.cast(*zlib.ZlibCompressorArrayList, ctx.?); compressor.list.deinit(compressor.allocator); compressor.deinit(); } + const Library = enum { + zlib, + libdeflate, + + pub const map = bun.ComptimeEnumMap(Library); + }; + + // This has to be `inline` due to the callframe. + inline fn getOptions(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) ?struct { JSC.Node.StringOrBuffer, ?JSValue } { + const arguments = callframe.arguments(2).slice(); + const buffer_value = if (arguments.len > 0) arguments[0] else .undefined; + const options_val: ?JSValue = + if (arguments.len > 1 and arguments[1].isObject()) + arguments[1] + else if (arguments.len > 1 and !arguments[1].isUndefined()) { + globalThis.throwInvalidArguments("Expected options to be an object", .{}); + return null; + } else null; + + if (JSC.Node.StringOrBuffer.fromJS(globalThis, bun.default_allocator, buffer_value)) |buffer| { + return .{ buffer, options_val }; + } + + globalThis.throwInvalidArguments("Expected buffer to be a string or buffer", .{}); + return null; + } + pub fn gzipSync( globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - options_val_: ?JSValue, + callframe: *JSC.CallFrame, + ) JSValue { + const buffer, const options_val = getOptions(globalThis, callframe) orelse return .zero; + defer buffer.deinit(); + return gzipOrDeflateSync(globalThis, buffer, options_val, true); + } + + pub fn inflateSync( + globalThis: *JSGlobalObject, + callframe: *JSC.CallFrame, ) JSValue { - return gzipOrDeflateSync(globalThis, buffer, options_val_, true); + const buffer, const options_val = getOptions(globalThis, callframe) orelse return .zero; + defer buffer.deinit(); + return gunzipOrInflateSync(globalThis, buffer, options_val, false); } pub fn deflateSync( globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - options_val_: ?JSValue, + callframe: *JSC.CallFrame, ) JSValue { - return gzipOrDeflateSync(globalThis, buffer, options_val_, false); + const buffer, const options_val = getOptions(globalThis, callframe) orelse return .zero; + defer buffer.deinit(); + return gzipOrDeflateSync(globalThis, buffer, options_val, false); } - pub fn gzipOrDeflateSync( + pub fn gunzipSync( + globalThis: *JSGlobalObject, + callframe: *JSC.CallFrame, + ) JSValue { + const buffer, const options_val = getOptions(globalThis, callframe) orelse return .zero; + defer buffer.deinit(); + return gunzipOrInflateSync(globalThis, buffer, options_val, true); + } + + pub fn gunzipOrInflateSync( globalThis: *JSGlobalObject, buffer: JSC.Node.StringOrBuffer, options_val_: ?JSValue, is_gzip: bool, ) JSValue { - var opts = zlib.Options{ .gzip = is_gzip }; + var opts = zlib.Options{ + .gzip = is_gzip, + .windowBits = if (is_gzip) 31 else -15, + }; + + var library: Library = .zlib; if (options_val_) |options_val| { - if (options_val.isObject()) { - if (options_val.get(globalThis, "windowBits")) |window| { - opts.windowBits = window.coerce(i32, globalThis); - } + if (options_val.get(globalThis, "windowBits")) |window| { + opts.windowBits = window.coerce(i32, globalThis); + library = .zlib; + } - if (options_val.get(globalThis, "level")) |level| { - opts.level = level.coerce(i32, globalThis); - } + if (options_val.get(globalThis, "level")) |level| { + opts.level = level.coerce(i32, globalThis); + } - if (options_val.get(globalThis, "memLevel")) |memLevel| { - opts.memLevel = memLevel.coerce(i32, globalThis); - } + if (options_val.get(globalThis, "memLevel")) |memLevel| { + opts.memLevel = memLevel.coerce(i32, globalThis); + library = .zlib; + } + + if (options_val.get(globalThis, "strategy")) |strategy| { + opts.strategy = strategy.coerce(i32, globalThis); + library = .zlib; + } - if (options_val.get(globalThis, "strategy")) |strategy| { - opts.strategy = strategy.coerce(i32, globalThis); + if (options_val.getTruthy(globalThis, "library")) |library_value| { + if (!library_value.isString()) { + globalThis.throwInvalidArguments("Expected library to be a string", .{}); + return .zero; } + + library = Library.map.fromJS(globalThis, library_value) orelse { + globalThis.throwInvalidArguments("Expected library to be one of 'zlib' or 'libdeflate'", .{}); + return .zero; + }; } } + if (globalThis.hasException()) return .zero; + const compressed = buffer.slice(); const allocator = JSC.VirtualMachine.get().allocator; - var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch unreachable; - var reader = zlib.ZlibCompressorArrayList.init(compressed, &list, allocator, opts) catch |err| { - if (err == error.InvalidArgument) { - return JSC.toInvalidArguments("Invalid buffer", .{}, globalThis); + + var list = brk: { + if (is_gzip and compressed.len > 64) { + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | CRC32 | ISIZE | + // +---+---+---+---+---+---+---+---+ + const estimated_size: u32 = @bitCast(compressed[compressed.len - 4 ..][0..4].*); + // If it's > 256 MB, let's rely on dynamic allocation to minimize the risk of OOM. + if (estimated_size > 0 and estimated_size < 256 * 1024 * 1024) { + break :brk std.ArrayListUnmanaged(u8).initCapacity(allocator, @max(estimated_size, 64)) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + } } - return JSC.toInvalidArguments("Unexpected", .{}, globalThis); + break :brk std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; }; - reader.readAll() catch { - defer reader.deinit(); - globalThis.throwValue(ZigString.init(reader.errorMessage() orelse "Zlib returned an error").toErrorInstance(globalThis)); - return .zero; - }; - reader.list = .{ .items = reader.list.toOwnedSlice(allocator) catch @panic("TODO") }; - reader.list.capacity = reader.list.items.len; - reader.list_ptr = &reader.list; + switch (library) { + .zlib => { + var reader = zlib.ZlibReaderArrayList.initWithOptions(compressed, &list, allocator, .{ + .windowBits = opts.windowBits, + .level = opts.level, + }) catch |err| { + list.deinit(allocator); + if (err == error.InvalidArgument) { + globalThis.throw("Zlib error: Invalid argument", .{}); + return .zero; + } + + globalThis.throwError(err, "Zlib error"); + return .zero; + }; + + reader.readAll() catch { + defer reader.deinit(); + globalThis.throwValue(ZigString.init(reader.errorMessage() orelse "Zlib returned an error").toErrorInstance(globalThis)); + return .zero; + }; + reader.list = .{ .items = reader.list.items }; + reader.list.capacity = reader.list.items.len; + reader.list_ptr = &reader.list; + + var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); + return array_buffer.toJSWithContext(globalThis, reader, reader_deallocator, null); + }, + .libdeflate => { + var decompressor: *bun.libdeflate.Decompressor = bun.libdeflate.Decompressor.alloc() orelse { + list.deinit(allocator); + globalThis.throwOutOfMemory(); + return .zero; + }; + defer decompressor.deinit(); + while (true) { + const result = decompressor.decompress(compressed, list.allocatedSlice(), if (is_gzip) .gzip else .deflate); + + list.items.len = result.written; + + if (result.status == .insufficient_space) { + if (list.capacity > 1024 * 1024 * 1024) { + list.deinit(allocator); + globalThis.throwOutOfMemory(); + return .zero; + } + + list.ensureTotalCapacity(allocator, list.capacity * 2) catch { + list.deinit(allocator); + globalThis.throwOutOfMemory(); + return .zero; + }; + continue; + } + + if (result.status == .success) { + list.items.len = result.written; + break; + } - var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); - return array_buffer.toJSWithContext(globalThis, reader, reader_deallocator, null); + list.deinit(allocator); + globalThis.throw("libdeflate returned an error: {s}", .{@tagName(result.status)}); + return .zero; + } + + var array_buffer = JSC.ArrayBuffer.fromBytes(list.items, .Uint8Array); + return array_buffer.toJSWithContext(globalThis, list.items.ptr, global_deallocator, null); + }, + } } - pub fn inflateSync( + pub fn gzipOrDeflateSync( globalThis: *JSGlobalObject, buffer: JSC.Node.StringOrBuffer, + options_val_: ?JSValue, + is_gzip: bool, ) JSValue { - const compressed = buffer.slice(); - const allocator = JSC.VirtualMachine.get().allocator; - var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch unreachable; - var reader = zlib.ZlibReaderArrayList.initWithOptions(compressed, &list, allocator, .{ - .windowBits = -15, - }) catch |err| { - if (err == error.InvalidArgument) { - return JSC.toInvalidArguments("Invalid buffer", .{}, globalThis); + var level: ?i32 = null; + var library: Library = .zlib; + var windowBits: i32 = 0; + + if (options_val_) |options_val| { + if (options_val.get(globalThis, "windowBits")) |window| { + windowBits = window.coerce(i32, globalThis); + library = .zlib; } - return JSC.toInvalidArguments("Unexpected", .{}, globalThis); - }; + if (options_val.getTruthy(globalThis, "library")) |library_value| { + if (!library_value.isString()) { + globalThis.throwInvalidArguments("Expected library to be a string", .{}); + return .zero; + } - reader.readAll() catch { - defer reader.deinit(); - globalThis.throwValue(ZigString.init(reader.errorMessage() orelse "Zlib returned an error").toErrorInstance(globalThis)); - return .zero; - }; - reader.list = .{ .items = reader.list.toOwnedSlice(allocator) catch @panic("TODO") }; - reader.list.capacity = reader.list.items.len; - reader.list_ptr = &reader.list; + library = Library.map.fromJS(globalThis, library_value) orelse { + globalThis.throwInvalidArguments("Expected library to be one of 'zlib' or 'libdeflate'", .{}); + return .zero; + }; + } - var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); - return array_buffer.toJSWithContext(globalThis, reader, reader_deallocator, null); - } + if (options_val.get(globalThis, "level")) |level_value| { + level = level_value.coerce(i32, globalThis); + if (globalThis.hasException()) return .zero; + } + } + + if (globalThis.hasException()) return .zero; - pub fn gunzipSync( - globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - ) JSValue { const compressed = buffer.slice(); - const allocator = JSC.VirtualMachine.get().allocator; - var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch unreachable; - var reader = zlib.ZlibReaderArrayList.init(compressed, &list, allocator) catch |err| { - if (err == error.InvalidArgument) { - return JSC.toInvalidArguments("Invalid buffer", .{}, globalThis); - } + const allocator = bun.default_allocator; + + switch (library) { + .zlib => { + var list = std.ArrayListUnmanaged(u8).initCapacity( + allocator, + if (compressed.len > 512) compressed.len else 32, + ) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; - return JSC.toInvalidArguments("Unexpected", .{}, globalThis); - }; + var reader = zlib.ZlibCompressorArrayList.init(compressed, &list, allocator, .{ + .windowBits = 15, + .gzip = is_gzip, + .level = level orelse 6, + }) catch |err| { + defer list.deinit(allocator); + if (err == error.InvalidArgument) { + globalThis.throw("Zlib error: Invalid argument", .{}); + return .zero; + } - reader.readAll() catch { - defer reader.deinit(); - globalThis.throwValue(ZigString.init(reader.errorMessage() orelse "Zlib returned an error").toErrorInstance(globalThis)); - return .zero; - }; - reader.list = .{ .items = reader.list.toOwnedSlice(allocator) catch @panic("TODO") }; - reader.list.capacity = reader.list.items.len; - reader.list_ptr = &reader.list; + globalThis.throwError(err, "Zlib error"); + return .zero; + }; + + reader.readAll() catch { + defer reader.deinit(); + globalThis.throwValue(ZigString.init(reader.errorMessage() orelse "Zlib returned an error").toErrorInstance(globalThis)); + return .zero; + }; + reader.list = .{ .items = reader.list.toOwnedSlice(allocator) catch @panic("TODO") }; + reader.list.capacity = reader.list.items.len; + reader.list_ptr = &reader.list; - var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); - return array_buffer.toJSWithContext(globalThis, reader, reader_deallocator, null); + var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); + return array_buffer.toJSWithContext(globalThis, reader, reader_deallocator, null); + }, + .libdeflate => { + var compressor: *bun.libdeflate.Compressor = bun.libdeflate.Compressor.alloc(level orelse 6) orelse { + globalThis.throwOutOfMemory(); + return .zero; + }; + const encoding: bun.libdeflate.Encoding = if (is_gzip) .gzip else .deflate; + defer compressor.deinit(); + + var list = std.ArrayListUnmanaged(u8).initCapacity( + allocator, + // This allocation size is unfortunate, but it's not clear how to avoid it with libdeflate. + compressor.maxBytesNeeded(compressed, encoding), + ) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + + while (true) { + const result = compressor.compress(compressed, list.allocatedSlice(), encoding); + + list.items.len = result.written; + + if (result.status == .success) { + list.items.len = result.written; + break; + } + + list.deinit(allocator); + globalThis.throw("libdeflate error: {s}", .{@tagName(result.status)}); + return .zero; + } + + var array_buffer = JSC.ArrayBuffer.fromBytes(list.items, .Uint8Array); + return array_buffer.toJSWithContext(globalThis, list.items.ptr, global_deallocator, null); + }, + } } }; pub usingnamespace @import("./bun/subprocess.zig"); const InternalTestingAPIs = struct { - pub fn BunInternalFunction__syntaxHighlighter(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn BunInternalFunction__syntaxHighlighter(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const args = callframe.arguments(1); if (args.len < 1) { globalThis.throwNotEnoughArguments("code", 1, 0); diff --git a/src/bun.js/api/FFI.h b/src/bun.js/api/FFI.h index effe7dc13aed2..6feeaa68bdca5 100644 --- a/src/bun.js/api/FFI.h +++ b/src/bun.js/api/FFI.h @@ -33,6 +33,37 @@ typedef _Bool bool; #define true 1 #define false 0 +#ifndef SRC_JS_NATIVE_API_TYPES_H_ +typedef struct napi_env__ *napi_env; +typedef struct napi_value__ *napi_value; +typedef enum { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_name_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_array_expected, + napi_generic_failure, + napi_pending_exception, + napi_cancelled, + napi_escape_called_twice, + napi_handle_scope_mismatch, + napi_callback_scope_mismatch, + napi_queue_full, + napi_closing, + napi_bigint_expected, + napi_date_expected, + napi_arraybuffer_expected, + napi_detachable_arraybuffer_expected, + napi_would_deadlock // unused +} napi_status; +void* NapiHandleScope__push(void* jsGlobalObject, bool detached); +void NapiHandleScope__pop(void* jsGlobalObject, void* handleScope); +#endif + #ifdef INJECT_BEFORE // #include @@ -68,6 +99,8 @@ typedef union EncodedJSValue { JSCell *ptr; #endif +napi_value asNapiValue; + #if IS_BIG_ENDIAN struct { int32_t tag; diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 4998ba299631d..6837ec90d0393 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -61,12 +61,14 @@ pub const JSBundler = struct { minify: Minify = .{}, server_components: ServerComponents = ServerComponents{}, no_macros: bool = false, - + ignore_dce_annotations: bool = false, + emit_dce_annotations: ?bool = null, names: Names = .{}, external: bun.StringSet = bun.StringSet.init(bun.default_allocator), source_map: options.SourceMapOption = .none, public_path: OwnedString = OwnedString.initEmpty(bun.default_allocator), conditions: bun.StringSet = bun.StringSet.init(bun.default_allocator), + packages: options.PackagesOption = .bundle, pub const List = bun.StringArrayHashMapUnmanaged(Config); @@ -223,6 +225,10 @@ pub const JSBundler = struct { } } + if (try config.getOptionalEnum(globalThis, "packages", options.PackagesOption)) |packages| { + this.packages = packages; + } + if (try config.getOptionalEnum(globalThis, "format", options.Format)) |format| { switch (format) { .esm => {}, @@ -278,6 +284,18 @@ pub const JSBundler = struct { return error.JSException; } + if (config.getTruthy(globalThis, "emitDCEAnnotations")) |flag| { + if (flag.coerce(bool, globalThis)) { + this.emit_dce_annotations = true; + } + } + + if (config.getTruthy(globalThis, "ignoreDCEAnnotations")) |flag| { + if (flag.coerce(bool, globalThis)) { + this.ignore_dce_annotations = true; + } + } + if (config.getTruthy(globalThis, "conditions")) |conditions_value| { if (conditions_value.isString()) { var slice = conditions_value.toSliceOrNull(globalThis) orelse { @@ -543,12 +561,12 @@ pub const JSBundler = struct { ) JSC.JSValue { if (arguments.len == 0 or !arguments[0].isObject()) { globalThis.throwInvalidArguments("Expected a config object to be passed to Bun.build", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; } var plugins: ?*Plugin = null; const config = Config.fromJS(globalThis, arguments[0], &plugins, globalThis.allocator()) catch { - return JSC.JSValue.jsUndefined(); + return .undefined; }; return bun.BundleV2.generateFromJavaScript( @@ -558,14 +576,14 @@ pub const JSBundler = struct { globalThis.bunVM().eventLoop(), bun.default_allocator, ) catch { - return JSC.JSValue.jsUndefined(); + return .undefined; }; } pub fn buildFn( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments = callframe.arguments(1); return build(globalThis, arguments.slice()); } @@ -683,16 +701,7 @@ pub const JSBundler = struct { completion.ref(); this.js_task = AnyTask.init(this); - const concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch { - completion.deref(); - this.deinit(); - return; - }; - concurrent_task.* = JSC.ConcurrentTask{ - .auto_delete = true, - .task = this.js_task.task(), - }; - completion.jsc_event_loop.enqueueTaskConcurrent(concurrent_task); + completion.jsc_event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.create(this.js_task.task())); } pub fn runOnJSThread(this: *Resolve) void { @@ -839,15 +848,7 @@ pub const JSBundler = struct { completion.ref(); this.js_task = AnyTask.init(this); - const concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch { - completion.deref(); - this.deinit(); - return; - }; - concurrent_task.* = JSC.ConcurrentTask{ - .auto_delete = true, - .task = this.js_task.task(), - }; + const concurrent_task = JSC.ConcurrentTask.createFrom(&this.js_task); completion.jsc_event_loop.enqueueTaskConcurrent(concurrent_task); } @@ -1081,7 +1082,7 @@ pub const BuildArtifact = struct { this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return @call(bun.callmod_inline, Blob.getText, .{ &this.blob, globalThis, callframe }); } @@ -1089,27 +1090,27 @@ pub const BuildArtifact = struct { this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return @call(bun.callmod_inline, Blob.getJSON, .{ &this.blob, globalThis, callframe }); } pub fn getArrayBuffer( this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { return @call(bun.callmod_inline, Blob.getArrayBuffer, .{ &this.blob, globalThis, callframe }); } pub fn getSlice( this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return @call(bun.callmod_inline, Blob.getSlice, .{ &this.blob, globalThis, callframe }); } pub fn getType( this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return @call(bun.callmod_inline, Blob.getType, .{ &this.blob, globalThis }); } @@ -1117,7 +1118,7 @@ pub const BuildArtifact = struct { this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { return @call(bun.callmod_inline, Blob.getStream, .{ &this.blob, globalThis, @@ -1128,39 +1129,39 @@ pub const BuildArtifact = struct { pub fn getPath( this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { - return ZigString.fromUTF8(this.path).toValueGC(globalThis); + ) JSValue { + return ZigString.fromUTF8(this.path).toJS(globalThis); } pub fn getLoader( this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { - return ZigString.fromUTF8(@tagName(this.loader)).toValueGC(globalThis); + ) JSValue { + return ZigString.fromUTF8(@tagName(this.loader)).toJS(globalThis); } pub fn getHash( this: *BuildArtifact, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { var buf: [512]u8 = undefined; const out = std.fmt.bufPrint(&buf, "{any}", .{options.PathTemplate.hashFormatter(this.hash)}) catch @panic("Unexpected"); - return ZigString.init(out).toValueGC(globalThis); + return ZigString.init(out).toJS(globalThis); } - pub fn getSize(this: *BuildArtifact, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getSize(this: *BuildArtifact, globalObject: *JSC.JSGlobalObject) JSValue { return @call(bun.callmod_inline, Blob.getSize, .{ &this.blob, globalObject }); } - pub fn getMimeType(this: *BuildArtifact, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getMimeType(this: *BuildArtifact, globalObject: *JSC.JSGlobalObject) JSValue { return @call(bun.callmod_inline, Blob.getType, .{ &this.blob, globalObject }); } - pub fn getOutputKind(this: *BuildArtifact, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { - return ZigString.init(@tagName(this.output_kind)).toValueGC(globalObject); + pub fn getOutputKind(this: *BuildArtifact, globalObject: *JSC.JSGlobalObject) JSValue { + return ZigString.init(@tagName(this.output_kind)).toJS(globalObject); } - pub fn getSourceMap(this: *BuildArtifact, _: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getSourceMap(this: *BuildArtifact, _: *JSC.JSGlobalObject) JSValue { if (this.sourcemap.get()) |value| { return value; } diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 9eda467a5ed87..062ade00e28fa 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -140,10 +140,9 @@ pub const TransformTask = struct { .allocator = allocator, }; ast_memory_allocator.reset(); + JSAst.Stmt.Data.Store.memory_allocator = ast_memory_allocator; JSAst.Expr.Data.Store.memory_allocator = ast_memory_allocator; - JSAst.Stmt.Data.Store.create(bun.default_allocator); - JSAst.Expr.Data.Store.create(bun.default_allocator); defer { JSAst.Stmt.Data.Store.reset(); @@ -248,6 +247,9 @@ pub const TransformTask = struct { this.log.deinit(); this.input_code.deinitAndUnprotect(); this.output_code.deref(); + if (this.tsconfig) |tsconfig| { + tsconfig.destroy(); + } this.destroy(); } @@ -524,19 +526,6 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std transpiler.runtime.allow_runtime = flag; } - if (object.getOptional(globalThis, "jsxOptimizationInline", bool) catch return transpiler) |flag| { - transpiler.runtime.jsx_optimization_inline = flag; - } - - if (object.getOptional(globalThis, "jsxOptimizationHoist", bool) catch return transpiler) |flag| { - transpiler.runtime.jsx_optimization_hoist = flag; - - if (!transpiler.runtime.jsx_optimization_inline and transpiler.runtime.jsx_optimization_hoist) { - JSC.throwInvalidArguments("jsxOptimizationHoist requires jsxOptimizationInline", .{}, globalObject, exception); - return transpiler; - } - } - if (object.getOptional(globalThis, "inline", bool) catch return transpiler) |flag| { transpiler.runtime.inlining = flag; } @@ -587,6 +576,10 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std } } + if (try object.getOptionalEnum(globalThis, "packages", options.PackagesOption)) |packages| { + transpiler.transform.packages = packages.toAPI(); + } + var tree_shaking: ?bool = null; if (object.getOptional(globalThis, "treeShaking", bool) catch return transpiler) |treeShaking| { tree_shaking = treeShaking; @@ -743,7 +736,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) ?*Transpiler { +) ?*Transpiler { var temp = bun.ArenaAllocator.init(getAllocator(globalThis)); const arguments = callframe.arguments(3); var args = JSC.Node.ArgumentsSlice.init( @@ -907,7 +900,7 @@ pub fn scan( this: *Transpiler, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { JSC.markBinding(@src()); const arguments = callframe.arguments(3); var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments.slice()); @@ -1005,7 +998,7 @@ pub fn transform( this: *Transpiler, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { JSC.markBinding(@src()); var exception_ref = [_]JSC.C.JSValueRef{null}; const exception: JSC.C.ExceptionRef = &exception_ref; @@ -1050,7 +1043,7 @@ pub fn transform( if (code == .buffer) { code_arg.unprotect(); } - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return .zero; }; task.schedule(); @@ -1061,7 +1054,7 @@ pub fn transformSync( this: *Transpiler, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { JSC.markBinding(@src()); var exception_value = [_]JSC.C.JSValueRef{null}; const exception: JSC.C.ExceptionRef = &exception_value; @@ -1191,7 +1184,7 @@ pub fn transformSync( var out = JSC.ZigString.init(buffer_writer.written); out.setOutputEncoding(); - return out.toValueGC(globalThis); + return out.toJS(globalThis); } fn namedExportsToJS(global: *JSGlobalObject, named_exports: *JSAst.Ast.NamedExports) JSC.JSValue { @@ -1234,8 +1227,8 @@ fn namedImportsToJS( if (record.is_internal) continue; array.ensureStillAlive(); - const path = JSC.ZigString.init(record.path.text).toValueGC(global); - const kind = JSC.ZigString.init(record.kind.label()).toValueGC(global); + const path = JSC.ZigString.init(record.path.text).toJS(global); + const kind = JSC.ZigString.init(record.kind.label()).toJS(global); array.putIndex(global, @as(u32, @truncate(i)), JSC.JSValue.createObject2(global, path_label, kind_label, path, kind)); } @@ -1246,7 +1239,7 @@ pub fn scanImports( this: *Transpiler, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(2); var exception_val = [_]JSC.C.JSValueRef{null}; const exception: JSC.C.ExceptionRef = &exception_val; diff --git a/src/bun.js/api/Timer.zig b/src/bun.js/api/Timer.zig index 5fda636d0b42c..3849d729ebeb6 100644 --- a/src/bun.js/api/Timer.zig +++ b/src/bun.js/api/Timer.zig @@ -550,7 +550,7 @@ pub const TimerObject = struct { return .{ timer, timer_js }; } - pub fn doRef(this: *TimerObject, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn doRef(this: *TimerObject, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const this_value = callframe.this(); this_value.ensureStillAlive(); @@ -564,7 +564,7 @@ pub const TimerObject = struct { return this_value; } - pub fn doRefresh(this: *TimerObject, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn doRefresh(this: *TimerObject, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const this_value = callframe.this(); // setImmediate does not support refreshing and we do not support refreshing after cleanup @@ -578,7 +578,7 @@ pub const TimerObject = struct { return this_value; } - pub fn doUnref(this: *TimerObject, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn doUnref(this: *TimerObject, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const this_value = callframe.this(); this_value.ensureStillAlive(); @@ -643,10 +643,10 @@ pub const TimerObject = struct { } } - pub fn hasRef(this: *TimerObject, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn hasRef(this: *TimerObject, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { return JSValue.jsBoolean(this.is_keeping_event_loop_alive); } - pub fn toPrimitive(this: *TimerObject, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toPrimitive(this: *TimerObject, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { if (!this.has_accessed_primitive) { this.has_accessed_primitive = true; const vm = VirtualMachine.get(); @@ -655,7 +655,7 @@ pub const TimerObject = struct { return JSValue.jsNumber(this.id); } - pub fn finalize(this: *TimerObject) callconv(.C) void { + pub fn finalize(this: *TimerObject) void { this.strong_this.deinit(); this.deref(); } @@ -669,7 +669,19 @@ pub const TimerObject = struct { } if (this.has_accessed_primitive) { - _ = vm.timer.maps.get(this.kind).orderedRemove(this.id); + const map = vm.timer.maps.get(this.kind); + if (map.orderedRemove(this.id)) { + // If this array gets large, let's shrink it down + // Array keys are i32 + // Values are 1 ptr + // Therefore, 12 bytes per entry + // So if you created 21,000 timers and accessed them by ID, you'd be using 252KB + const allocated_bytes = map.capacity() * @sizeOf(TimeoutMap.Data); + const used_bytes = map.count() * @sizeOf(TimeoutMap.Data); + if (allocated_bytes - used_bytes > 256 * 1024) { + map.shrinkAndFree(bun.default_allocator, map.count() + 8); + } + } } this.setEnableKeepingEventLoopAlive(vm, false); @@ -712,11 +724,30 @@ pub const EventLoopTimer = struct { tag: Tag = .TimerCallback, - pub const Tag = enum { + pub const Tag = if (Environment.isWindows) enum { + TimerCallback, + TimerObject, + TestRunner, + StatWatcherScheduler, + UpgradedDuplex, + WindowsNamedPipe, + + pub fn Type(comptime T: Tag) type { + return switch (T) { + .TimerCallback => TimerCallback, + .TimerObject => TimerObject, + .TestRunner => JSC.Jest.TestRunner, + .StatWatcherScheduler => StatWatcherScheduler, + .UpgradedDuplex => uws.UpgradedDuplex, + .WindowsNamedPipe => uws.WindowsNamedPipe, + }; + } + } else enum { TimerCallback, TimerObject, TestRunner, StatWatcherScheduler, + UpgradedDuplex, pub fn Type(comptime T: Tag) type { return switch (T) { @@ -724,6 +755,7 @@ pub const EventLoopTimer = struct { .TimerObject => TimerObject, .TestRunner => JSC.Jest.TestRunner, .StatWatcherScheduler => StatWatcherScheduler, + .UpgradedDuplex => uws.UpgradedDuplex, }; } }; @@ -784,6 +816,14 @@ pub const EventLoopTimer = struct { if (comptime t.Type() == StatWatcherScheduler) { return container.timerCallback(); } + if (comptime t.Type() == uws.UpgradedDuplex) { + return container.onTimeout(); + } + if (Environment.isWindows) { + if (comptime t.Type() == uws.WindowsNamedPipe) { + return container.onTimeout(); + } + } if (comptime t.Type() == JSC.Jest.TestRunner) { container.onTestTimeout(now, vm); diff --git a/src/bun.js/api/brotli.classes.ts b/src/bun.js/api/brotli.classes.ts deleted file mode 100644 index 2095a2c10433c..0000000000000 --- a/src/bun.js/api/brotli.classes.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { define } from "../../codegen/class-definitions"; - -export default [ - define({ - name: "BrotliEncoder", - construct: true, - noConstructor: true, - finalize: true, - configurable: false, - hasPendingActivity: true, - klass: {}, - JSType: "0b11101110", - values: ["callback"], - proto: { - encode: { - fn: "encode", - length: 2, - }, - encodeSync: { - fn: "encodeSync", - length: 2, - }, - end: { - fn: "end", - length: 2, - }, - endSync: { - fn: "endSync", - length: 2, - }, - }, - }), - define({ - name: "BrotliDecoder", - construct: true, - noConstructor: true, - finalize: true, - configurable: false, - hasPendingActivity: true, - klass: {}, - JSType: "0b11101110", - values: ["callback"], - - proto: { - decode: { - fn: "decode", - length: 2, - }, - decodeSync: { - fn: "decodeSync", - length: 2, - }, - end: { - fn: "end", - length: 2, - }, - endSync: { - fn: "endSync", - length: 2, - }, - }, - }), -]; diff --git a/src/bun.js/api/brotli.zig b/src/bun.js/api/brotli.zig deleted file mode 100644 index 9a467a81a15b2..0000000000000 --- a/src/bun.js/api/brotli.zig +++ /dev/null @@ -1,621 +0,0 @@ -const bun = @import("root").bun; -const JSC = bun.JSC; -const std = @import("std"); -const brotli = bun.brotli; - -const Queue = std.fifo.LinearFifo(JSC.Node.BlobOrStringOrBuffer, .Dynamic); - -pub const BrotliEncoder = struct { - pub usingnamespace bun.New(@This()); - pub usingnamespace JSC.Codegen.JSBrotliEncoder; - - stream: brotli.BrotliCompressionStream, - - freelist: Queue = Queue.init(bun.default_allocator), - freelist_write_lock: bun.Lock = bun.Lock.init(), - - globalThis: *JSC.JSGlobalObject, - - input: Queue = Queue.init(bun.default_allocator), - input_lock: bun.Lock = bun.Lock.init(), - - has_called_end: bool = false, - callback_value: JSC.Strong = .{}, - - output: std.ArrayListUnmanaged(u8) = .{}, - output_lock: bun.Lock = bun.Lock.init(), - - has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - pending_encode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - ref_count: u32 = 1, - write_failed: bool = false, - poll_ref: bun.Async.KeepAlive = .{}, - - pub fn hasPendingActivity(this: *BrotliEncoder) callconv(.C) bool { - return this.has_pending_activity.load(.monotonic) > 0; - } - - pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*BrotliEncoder { - globalThis.throw("BrotliEncoder is not constructable", .{}); - return null; - } - - pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(3).slice(); - - if (arguments.len < 3) { - globalThis.throwNotEnoughArguments("BrotliEncoder", 3, arguments.len); - return .zero; - } - - const opts = arguments[0]; - const callback = arguments[2]; - - var this: *BrotliEncoder = BrotliEncoder.new(.{ - .globalThis = globalThis, - .stream = brotli.BrotliCompressionStream.init() catch { - globalThis.throw("Failed to create BrotliEncoder", .{}); - return .zero; - }, - }); - - if (opts.get(globalThis, "params")) |params| { - inline for (std.meta.fields(bun.brotli.c.BrotliEncoderParameter)) |f| { - const idx = params.getIndex(globalThis, f.value); - if (!idx.isNumber()) break; - const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32()); - if (!was_set) { - globalThis.throwValue(globalThis.createErrorInstanceWithCode(.ERR_ZLIB_INITIALIZATION_FAILED, "Initialization failed", .{})); - this.deinit(); - return .zero; - } - } - } - - const out = this.toJS(globalThis); - @This().callbackSetCached(out, globalThis, callback); - this.callback_value.set(globalThis, callback); - - return out; - } - - pub fn finalize(this: *BrotliEncoder) callconv(.C) void { - this.deinit(); - } - - pub fn deinit(this: *BrotliEncoder) void { - this.callback_value.deinit(); - this.drainFreelist(); - this.stream.deinit(); - this.input.deinit(); - this.destroy(); - } - - fn drainFreelist(this: *BrotliEncoder) void { - this.freelist_write_lock.lock(); - defer this.freelist_write_lock.unlock(); - const to_free = this.freelist.readableSlice(0); - for (to_free) |*input| { - input.deinit(); - } - this.freelist.discard(to_free.len); - } - - fn collectOutputValue(this: *BrotliEncoder) JSC.JSValue { - this.output_lock.lock(); - defer this.output_lock.unlock(); - - defer this.output.clearRetainingCapacity(); - return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items); - } - - pub fn runFromJSThread(this: *BrotliEncoder) void { - this.poll_ref.unref(this.globalThis.bunVM()); - - defer _ = this.has_pending_activity.fetchSub(1, .monotonic); - this.drainFreelist(); - - const result = this.callback_value.get().?.call(this.globalThis, &.{ - if (this.write_failed) - // TODO: propagate error from brotli - this.globalThis.createErrorInstance("BrotliError", .{}) - else - JSC.JSValue.null, - this.collectOutputValue(), - }); - - if (result.toError()) |err| { - _ = this.globalThis.bunVM().uncaughtException(this.globalThis, err, false); - } - } - - // We can only run one encode job at a time - // But we don't have an idea of a serial dispatch queue - // So instead, we let you enqueue as many times as you want - // and if one is already running, we just don't do anything - const EncodeJob = struct { - task: JSC.WorkPoolTask = .{ .callback = &runTask }, - encoder: *BrotliEncoder, - is_async: bool, - - pub usingnamespace bun.New(@This()); - - pub fn runTask(this: *JSC.WorkPoolTask) void { - var job: *EncodeJob = @fieldParentPtr("task", this); - job.run(); - job.destroy(); - } - - pub fn run(this: *EncodeJob) void { - defer { - _ = this.encoder.has_pending_activity.fetchSub(1, .monotonic); - } - - var any = false; - - if (this.encoder.pending_encode_job_count.fetchAdd(1, .monotonic) >= 0) { - const is_last = this.encoder.has_called_end; - while (true) { - this.encoder.input_lock.lock(); - defer this.encoder.input_lock.unlock(); - const readable = this.encoder.input.readableSlice(0); - defer this.encoder.input.discard(readable.len); - const pending = readable; - - const Writer = struct { - encoder: *BrotliEncoder, - - pub const Error = error{OutOfMemory}; - pub fn writeAll(writer: @This(), chunk: []const u8) Error!void { - writer.encoder.output_lock.lock(); - defer writer.encoder.output_lock.unlock(); - - try writer.encoder.output.appendSlice(bun.default_allocator, chunk); - } - }; - - defer { - this.encoder.freelist_write_lock.lock(); - this.encoder.freelist.write(pending) catch unreachable; - this.encoder.freelist_write_lock.unlock(); - } - for (pending) |input| { - var writer = this.encoder.stream.writer(Writer{ .encoder = this.encoder }); - writer.writeAll(input.slice()) catch { - _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); - this.encoder.write_failed = true; - return; - }; - } - - any = any or pending.len > 0; - - if (this.encoder.pending_encode_job_count.fetchSub(1, .monotonic) == 0) - break; - } - - if (is_last and any) { - var output = &this.encoder.output; - this.encoder.output_lock.lock(); - defer this.encoder.output_lock.unlock(); - - output.appendSlice(bun.default_allocator, this.encoder.stream.end() catch { - _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); - this.encoder.write_failed = true; - return; - }) catch { - _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); - this.encoder.write_failed = true; - return; - }; - } - } - - if (this.is_async and any) { - var vm = this.encoder.globalThis.bunVMConcurrently(); - _ = this.encoder.has_pending_activity.fetchAdd(1, .monotonic); - this.encoder.poll_ref.refConcurrently(vm); - vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.encoder))); - } - } - }; - - pub fn encode(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(3); - - if (arguments.len < 2) { - globalThis.throwNotEnoughArguments("BrotliEncoder.encode", 2, arguments.len); - return .zero; - } - - if (this.has_called_end) { - globalThis.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{}); - return .zero; - } - - const input = callframe.argument(0); - const optional_encoding = callframe.argument(1); - const is_last = callframe.argument(2).toBoolean(); - - const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { - globalThis.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer"); - return .zero; - }; - - _ = this.has_pending_activity.fetchAdd(1, .monotonic); - if (is_last) - this.has_called_end = true; - - var task = EncodeJob.new(.{ - .encoder = this, - .is_async = true, - }); - - { - this.input_lock.lock(); - defer this.input_lock.unlock(); - - this.input.writeItem(input_to_queue) catch unreachable; - } - JSC.WorkPool.schedule(&task.task); - - return .undefined; - } - - pub fn encodeSync(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(3); - - if (arguments.len < 2) { - globalThis.throwNotEnoughArguments("BrotliEncoder.encode", 2, arguments.len); - return .zero; - } - - if (this.has_called_end) { - globalThis.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{}); - return .zero; - } - - const input = callframe.argument(0); - const optional_encoding = callframe.argument(1); - const is_last = callframe.argument(2).toBoolean(); - - const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { - globalThis.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer"); - return .zero; - }; - - _ = this.has_pending_activity.fetchAdd(1, .monotonic); - if (is_last) - this.has_called_end = true; - - var task: EncodeJob = .{ - .encoder = this, - .is_async = false, - }; - - { - this.input_lock.lock(); - defer this.input_lock.unlock(); - - this.input.writeItem(input_to_queue) catch unreachable; - } - task.run(); - return if (!is_last and this.output.items.len == 0) .undefined else this.collectOutputValue(); - } - - pub fn end(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - _ = this; - _ = globalThis; - _ = callframe; - - return .zero; - } - - pub fn endSync(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - _ = this; - _ = globalThis; - _ = callframe; - - return .zero; - } -}; - -pub const BrotliDecoder = struct { - pub usingnamespace bun.New(@This()); - pub usingnamespace JSC.Codegen.JSBrotliDecoder; - - globalThis: *JSC.JSGlobalObject, - stream: brotli.BrotliReaderArrayList, - - has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - ref_count: u32 = 1, - poll_ref: bun.Async.KeepAlive = .{}, - write_failed: bool = false, - callback_value: JSC.Strong = .{}, - has_called_end: bool = false, - pending_decode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - - input: Queue = Queue.init(bun.default_allocator), - input_lock: bun.Lock = bun.Lock.init(), - - output: std.ArrayListUnmanaged(u8) = .{}, - output_lock: bun.Lock = bun.Lock.init(), - - freelist: Queue = Queue.init(bun.default_allocator), - freelist_write_lock: bun.Lock = bun.Lock.init(), - - pub fn hasPendingActivity(this: *BrotliDecoder) callconv(.C) bool { - return this.has_pending_activity.load(.monotonic) > 0; - } - - pub fn deinit(this: *BrotliDecoder) void { - this.callback_value.deinit(); - this.drainFreelist(); - this.output.deinit(bun.default_allocator); - this.stream.brotli.destroyInstance(); - this.input.deinit(); - this.destroy(); - } - - pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*BrotliDecoder { - globalThis.throw("Crypto is not constructable", .{}); - return null; - } - - pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(3).slice(); - - if (arguments.len < 3) { - globalThis.throwNotEnoughArguments("BrotliDecoder", 3, arguments.len); - return .zero; - } - - const opts = arguments[0]; - const callback = arguments[2]; - - var this: *BrotliDecoder = BrotliDecoder.new(.{ - .globalThis = globalThis, - .stream = undefined, // &this.output needs to be a stable pointer - }); - this.stream = brotli.BrotliReaderArrayList.initWithOptions("", &this.output, bun.default_allocator, .{}) catch { - globalThis.throw("Failed to create BrotliDecoder", .{}); - return .zero; - }; - - if (opts.get(globalThis, "params")) |params| { - inline for (std.meta.fields(bun.brotli.c.BrotliDecoderParameter)) |f| { - const idx = params.getIndex(globalThis, f.value); - if (!idx.isNumber()) break; - const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32()); - if (!was_set) { - globalThis.throwValue(globalThis.createErrorInstanceWithCode(.ERR_ZLIB_INITIALIZATION_FAILED, "Initialization failed", .{})); - this.deinit(); - return .zero; - } - } - } - - const out = this.toJS(globalThis); - @This().callbackSetCached(out, globalThis, callback); - this.callback_value.set(globalThis, callback); - - return out; - } - - pub fn finalize(this: *BrotliDecoder) callconv(.C) void { - this.deinit(); - } - - fn collectOutputValue(this: *BrotliDecoder) JSC.JSValue { - this.output_lock.lock(); - defer this.output_lock.unlock(); - - defer this.output.clearRetainingCapacity(); - return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items); - } - - pub fn runFromJSThread(this: *BrotliDecoder) void { - this.poll_ref.unref(this.globalThis.bunVM()); - - defer _ = this.has_pending_activity.fetchSub(1, .monotonic); - this.drainFreelist(); - - const result = this.callback_value.get().?.call(this.globalThis, &.{ - if (this.write_failed) - // TODO: propagate error from brotli - this.globalThis.createErrorInstance("BrotliError", .{}) - else - JSC.JSValue.null, - this.collectOutputValue(), - }); - - if (result.toError()) |err| { - _ = this.globalThis.bunVM().uncaughtException(this.globalThis, err, false); - } - } - - fn drainFreelist(this: *BrotliDecoder) void { - this.freelist_write_lock.lock(); - defer this.freelist_write_lock.unlock(); - const to_free = this.freelist.readableSlice(0); - for (to_free) |*input| { - input.deinit(); - } - this.freelist.discard(to_free.len); - } - - pub fn decode(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(3); - - if (arguments.len < 2) { - globalThis.throwNotEnoughArguments("BrotliEncoder.decode", 2, arguments.len); - return .zero; - } - - if (this.has_called_end) { - globalThis.throw("BrotliEncoder.decode called after BrotliEncoder.end", .{}); - return .zero; - } - - const input = callframe.argument(0); - const optional_encoding = callframe.argument(1); - const is_last = callframe.argument(2).toBoolean(); - - const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { - globalThis.throwInvalidArgumentType("BrotliEncoder.decode", "input", "Blob, String, or Buffer"); - return .zero; - }; - - _ = this.has_pending_activity.fetchAdd(1, .monotonic); - if (is_last) - this.has_called_end = true; - - var task = DecodeJob.new(.{ - .decoder = this, - .is_async = true, - }); - - { - this.input_lock.lock(); - defer this.input_lock.unlock(); - - this.input.writeItem(input_to_queue) catch unreachable; - } - JSC.WorkPool.schedule(&task.task); - - return .undefined; - } - - pub fn decodeSync(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(3); - - if (arguments.len < 2) { - globalThis.throwNotEnoughArguments("BrotliEncoder.decode", 2, arguments.len); - return .zero; - } - - if (this.has_called_end) { - globalThis.throw("BrotliEncoder.decode called after BrotliEncoder.end", .{}); - return .zero; - } - - const input = callframe.argument(0); - const optional_encoding = callframe.argument(1); - const is_last = callframe.argument(2).toBoolean(); - - const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { - globalThis.throwInvalidArgumentType("BrotliEncoder.decode", "input", "Blob, String, or Buffer"); - return .zero; - }; - - _ = this.has_pending_activity.fetchAdd(1, .monotonic); - if (is_last) - this.has_called_end = true; - - var task: DecodeJob = .{ - .decoder = this, - .is_async = false, - }; - - { - this.input_lock.lock(); - defer this.input_lock.unlock(); - - this.input.writeItem(input_to_queue) catch unreachable; - } - task.run(); - return if (!is_last) .undefined else this.collectOutputValue(); - } - - // We can only run one decode job at a time - // But we don't have an idea of a serial dispatch queue - // So instead, we let you enqueue as many times as you want - // and if one is already running, we just don't do anything - const DecodeJob = struct { - task: JSC.WorkPoolTask = .{ .callback = &runTask }, - decoder: *BrotliDecoder, - is_async: bool, - - pub usingnamespace bun.New(@This()); - - pub fn runTask(this: *JSC.WorkPoolTask) void { - var job: *DecodeJob = @fieldParentPtr("task", this); - job.run(); - job.destroy(); - } - - pub fn run(this: *DecodeJob) void { - defer { - _ = this.decoder.has_pending_activity.fetchSub(1, .monotonic); - } - - var any = false; - - if (this.decoder.pending_decode_job_count.fetchAdd(1, .monotonic) >= 0) { - const is_last = this.decoder.has_called_end; - while (true) { - this.decoder.input_lock.lock(); - defer this.decoder.input_lock.unlock(); - if (!is_last) break; - const readable = this.decoder.input.readableSlice(0); - const pending = readable; - - defer { - this.decoder.freelist_write_lock.lock(); - this.decoder.freelist.write(pending) catch unreachable; - this.decoder.freelist_write_lock.unlock(); - } - - var input_list = std.ArrayListUnmanaged(u8){}; - defer input_list.deinit(bun.default_allocator); - if (pending.len > 1) { - for (pending) |input| { - input_list.appendSlice(bun.default_allocator, input.slice()) catch bun.outOfMemory(); - } - } - - { - this.decoder.output_lock.lock(); - defer this.decoder.output_lock.unlock(); - - const input = if (pending.len <= 1) pending[0].slice() else input_list.items; - this.decoder.stream.input = input; - this.decoder.stream.readAll(false) catch { - _ = this.decoder.pending_decode_job_count.fetchSub(1, .monotonic); - this.decoder.write_failed = true; - return; - }; - } - - any = any or pending.len > 0; - - if (this.decoder.pending_decode_job_count.fetchSub(1, .monotonic) == 0) - break; - } - } - - if (this.is_async and any) { - var vm = this.decoder.globalThis.bunVMConcurrently(); - _ = this.decoder.has_pending_activity.fetchAdd(1, .monotonic); - this.decoder.poll_ref.refConcurrently(vm); - vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.decoder))); - } - } - }; - - pub fn end(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - _ = this; - _ = globalThis; - _ = callframe; - - return .zero; - } - - pub fn endSync(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - _ = this; - _ = globalThis; - _ = callframe; - - return .zero; - } -}; diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index c4bc06f8aa22e..16b4f8cb8499d 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -271,6 +271,10 @@ pub fn normalizeDNSName(name: []const u8, backend: *GetAddrInfo.Backend) []const } else if (strings.isIPV6Address(name)) { backend.* = .system; } + // getaddrinfo() is inconsistent with ares_getaddrinfo() when using localhost + else if (strings.eqlComptime(name, "localhost")) { + backend.* = .system; + } } return name; @@ -488,29 +492,15 @@ pub const CAresNameInfo = struct { pub fn processResolve(this: *@This(), err_: ?c_ares.Error, _: i32, result: ?c_ares.struct_nameinfo) void { if (err_) |err| { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("lookupService failed: {s}", .{err.label()}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init(err.code()).toValueGC(globalThis), - ); - - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, err.toJS(globalThis)); this.deinit(); return; } if (result == null) { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("lookupService failed: No results", .{}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), - ); - - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, c_ares.Error.ENOTFOUND.toJS(globalThis)); this.deinit(); return; } @@ -906,29 +896,15 @@ pub const CAresReverse = struct { pub fn processResolve(this: *@This(), err_: ?c_ares.Error, _: i32, result: ?*c_ares.struct_hostent) void { if (err_) |err| { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("reverse failed: {s}", .{err.label()}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init(err.code()).toValueGC(globalThis), - ); - - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, err.toJS(globalThis)); this.deinit(); return; } if (result == null) { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("reverse failed: No results", .{}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), - ); - - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, c_ares.Error.ENOTFOUND.toJS(globalThis)); this.deinit(); return; } @@ -985,28 +961,15 @@ pub fn CAresLookup(comptime cares_type: type, comptime type_name: []const u8) ty pub fn processResolve(this: *@This(), err_: ?c_ares.Error, _: i32, result: ?*cares_type) void { if (err_) |err| { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("{s} lookup failed: {s}", .{ type_name, err.label() }); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init(err.code()).toValueGC(globalThis), - ); - - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, err.toJS(globalThis)); this.deinit(); return; } if (result == null) { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("{s} lookup failed: {s}", .{ type_name, "No results" }); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), - ); - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, c_ares.Error.ENOTFOUND.toJS(globalThis)); this.deinit(); return; } @@ -1070,19 +1033,14 @@ pub const DNSLookup = struct { log("processGetAddrInfoNative: status={d}", .{status}); if (c_ares.Error.initEAI(status)) |err| { var promise = this.promise; - var globalThis = this.globalThis; + const globalThis = this.globalThis; const error_value = brk: { if (err == .ESERVFAIL) { break :brk bun.sys.Error.fromCode(bun.C.getErrno(@as(c_int, -1)), .getaddrinfo).toJSC(globalThis); } - const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{err.label()}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init(err.code()).toValueGC(globalThis), - ); - break :brk error_value; + + break :brk err.toJS(globalThis); }; this.deinit(); @@ -1096,28 +1054,17 @@ pub const DNSLookup = struct { log("processGetAddrInfo", .{}); if (err_) |err| { var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{err.label()}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init(err.code()).toValueGC(globalThis), - ); - promise.rejectTask(globalThis, error_value); + const globalThis = this.globalThis; + promise.rejectTask(globalThis, err.toJS(globalThis)); this.deinit(); return; } if (result == null or result.?.node == null) { var promise = this.promise; - var globalThis = this.globalThis; + const globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{"No results"}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), - ); + const error_value = c_ares.Error.ENOTFOUND.toJS(globalThis); promise.rejectTask(globalThis, error_value); this.deinit(); return; @@ -1292,7 +1239,7 @@ pub const InternalDNS = struct { const GlobalCache = struct { const MAX_ENTRIES = 256; - lock: bun.Lock = bun.Lock.init(), + lock: bun.Lock = .{}, cache: [MAX_ENTRIES]*Request = undefined, len: usize = 0, @@ -1396,7 +1343,14 @@ pub const InternalDNS = struct { .addrlen = 0, .canonname = null, .family = std.c.AF.UNSPEC, - .flags = 0, + // If the system is IPv4-only or IPv6-only, then only return the corresponding address family. + // https://github.com/nodejs/node/commit/54dd7c38e507b35ee0ffadc41a716f1782b0d32f + // https://bugzilla.mozilla.org/show_bug.cgi?id=467497 + // https://github.com/adobe/chromium/blob/cfe5bf0b51b1f6b9fe239c2a3c2f2364da9967d7/net/base/host_resolver_proc.cc#L122-L241 + // https://github.com/nodejs/node/issues/33816 + // https://github.com/aio-libs/aiohttp/issues/5357 + // https://github.com/libuv/libuv/issues/2225 + .flags = if (Environment.isPosix) bun.C.netdb.AI_ADDRCONFIG else 0, .next = null, .protocol = 0, .socktype = std.c.SOCK.STREAM, @@ -1608,7 +1562,7 @@ pub const InternalDNS = struct { var dns_cache_errors: usize = 0; var getaddrinfo_calls: usize = 0; - pub fn getDNSCacheStats(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn getDNSCacheStats(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const object = JSC.JSValue.createEmptyObject(globalObject, 6); object.put(globalObject, JSC.ZigString.static("cacheHitsCompleted"), JSC.JSValue.jsNumber(@atomicLoad(usize, &dns_cache_hits_completed, .monotonic))); object.put(globalObject, JSC.ZigString.static("cacheHitsInflight"), JSC.JSValue.jsNumber(@atomicLoad(usize, &dns_cache_hits_inflight, .monotonic))); @@ -1679,7 +1633,7 @@ pub const InternalDNS = struct { return req; } - pub fn prefetchFromJS(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn prefetchFromJS(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2).slice(); if (arguments.len < 1) { @@ -2298,7 +2252,7 @@ pub const DNSResolver = struct { }); }; - pub fn resolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(3); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolve", 2, arguments.len); @@ -2383,7 +2337,7 @@ pub const DNSResolver = struct { } } - pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("reverse", 2, arguments.len); @@ -2411,13 +2365,8 @@ pub const DNSResolver = struct { var channel: *c_ares.Channel = switch (resolver.getChannel()) { .result => |res| res, .err => |err| { - const system_error = JSC.SystemError{ - .errno = -1, - .code = bun.String.static(err.code()), - .message = bun.String.static(err.label()), - }; defer ip_slice.deinit(); - globalThis.throwValue(system_error.toErrorInstance(globalThis)); + globalThis.throwValue(err.toJS(globalThis)); return .zero; }, }; @@ -2453,7 +2402,7 @@ pub const DNSResolver = struct { return promise; } - pub fn lookup(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn lookup(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("lookup", 2, arguments.len); @@ -2523,7 +2472,7 @@ pub const DNSResolver = struct { }; } - pub fn resolveSrv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveSrv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveSrv", 2, arguments.len); @@ -2554,7 +2503,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_ares_srv_reply, "srv", name.slice(), globalThis); } - pub fn resolveSoa(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveSoa(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveSoa", 2, arguments.len); @@ -2580,7 +2529,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_ares_soa_reply, "soa", name.slice(), globalThis); } - pub fn resolveCaa(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveCaa(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveCaa", 2, arguments.len); @@ -2611,7 +2560,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_ares_caa_reply, "caa", name.slice(), globalThis); } - pub fn resolveNs(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveNs(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveNs", 2, arguments.len); @@ -2637,7 +2586,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_hostent, "ns", name.slice(), globalThis); } - pub fn resolvePtr(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolvePtr(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolvePtr", 2, arguments.len); @@ -2668,7 +2617,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_hostent, "ptr", name.slice(), globalThis); } - pub fn resolveCname(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveCname(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveCname", 2, arguments.len); @@ -2699,7 +2648,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_hostent, "cname", name.slice(), globalThis); } - pub fn resolveMx(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveMx(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveMx", 2, arguments.len); @@ -2730,7 +2679,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_ares_mx_reply, "mx", name.slice(), globalThis); } - pub fn resolveNaptr(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveNaptr(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveNaptr", 2, arguments.len); @@ -2761,7 +2710,7 @@ pub const DNSResolver = struct { return resolver.doResolveCAres(c_ares.struct_ares_naptr_reply, "naptr", name.slice(), globalThis); } - pub fn resolveTxt(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn resolveTxt(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("resolveTxt", 2, arguments.len); @@ -2796,13 +2745,7 @@ pub const DNSResolver = struct { var channel: *c_ares.Channel = switch (this.getChannel()) { .result => |res| res, .err => |err| { - const system_error = JSC.SystemError{ - .errno = -1, - .code = bun.String.static(err.code()), - .message = bun.String.static(err.label()), - }; - - globalThis.throwValue(system_error.toErrorInstance(globalThis)); + globalThis.throwValue(err.toJS(globalThis)); return .zero; }, }; @@ -2888,7 +2831,7 @@ pub const DNSResolver = struct { return promise; } - pub fn getServers(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn getServers(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { _ = callframe; var vm = globalThis.bunVM(); @@ -2954,16 +2897,16 @@ pub const DNSResolver = struct { const size = bun.len(bun.cast([*:0]u8, buf[1..])) + 1; if (port == IANA_DNS_PORT) { - values.putIndex(globalThis, i, JSC.ZigString.init(buf[1..size]).withEncoding().toValueGC(globalThis)); + values.putIndex(globalThis, i, JSC.ZigString.init(buf[1..size]).withEncoding().toJS(globalThis)); } else { if (family == std.posix.AF.INET6) { buf[0] = '['; buf[size] = ']'; const port_slice = std.fmt.bufPrint(buf[size + 1 ..], ":{d}", .{port}) catch unreachable; - values.putIndex(globalThis, i, JSC.ZigString.init(buf[0 .. size + 1 + port_slice.len]).withEncoding().toValueGC(globalThis)); + values.putIndex(globalThis, i, JSC.ZigString.init(buf[0 .. size + 1 + port_slice.len]).withEncoding().toJS(globalThis)); } else { const port_slice = std.fmt.bufPrint(buf[size..], ":{d}", .{port}) catch unreachable; - values.putIndex(globalThis, i, JSC.ZigString.init(buf[1 .. size + port_slice.len]).withEncoding().toValueGC(globalThis)); + values.putIndex(globalThis, i, JSC.ZigString.init(buf[1 .. size + port_slice.len]).withEncoding().toJS(globalThis)); } } } @@ -2974,7 +2917,7 @@ pub const DNSResolver = struct { // Resolves the given address and port into a host name and service using the operating system's underlying getnameinfo implementation. // If address is not a valid IP address, a TypeError will be thrown. The port will be coerced to a number. // If it is not a legal port, a TypeError will be thrown. - pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(3); if (arguments.len < 2) { globalThis.throwNotEnoughArguments("lookupService", 3, arguments.len); diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig index e6815f0ad8800..13c5d04d89718 100644 --- a/src/bun.js/api/bun/h2_frame_parser.zig +++ b/src/bun.js/api/bun/h2_frame_parser.zig @@ -261,7 +261,7 @@ const SingleValueHeaders = bun.ComptimeStringMap(void, .{ .{"x-content-type-options"}, }); -pub fn jsGetUnpackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +fn jsGetUnpackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { JSC.markBinding(@src()); var settings: FullSettingsPayload = .{}; @@ -296,7 +296,7 @@ pub fn jsGetUnpackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC. } } -pub fn jsGetPackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { +fn jsGetPackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { var settings: FullSettingsPayload = .{}; const args_list = callframe.arguments(1); @@ -716,7 +716,7 @@ pub const H2FrameParser = struct { stream.state = .CLOSED; if (rstCode == .NO_ERROR) { - this.dispatchWithExtra(.onStreamEnd, JSC.JSValue.jsNumber(stream.id), JSC.JSValue.jsUndefined()); + this.dispatchWithExtra(.onStreamEnd, JSC.JSValue.jsNumber(stream.id), .undefined); } else { this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream.id), JSC.JSValue.jsNumber(@intFromEnum(rstCode))); } @@ -928,7 +928,7 @@ pub const H2FrameParser = struct { continue; } - const value = JSC.ZigString.fromUTF8(header.value).toValueGC(globalObject); + const value = JSC.ZigString.fromUTF8(header.value).toJS(globalObject); if (current_value.jsType().isArray()) { current_value.push(globalObject, value); @@ -943,7 +943,7 @@ pub const H2FrameParser = struct { } else { // TODO: check for well-known headers and use pre-allocated static strings (see lshpack.c) const name = JSC.ZigString.fromUTF8(header.name); - const value = JSC.ZigString.fromUTF8(header.value).toValueGC(globalObject); + const value = JSC.ZigString.fromUTF8(header.value).toJS(globalObject); headers.put(globalObject, &name, value); } @@ -1415,7 +1415,7 @@ pub const H2FrameParser = struct { } } - pub fn setEncoding(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn setEncoding(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -1428,7 +1428,7 @@ pub const H2FrameParser = struct { return .zero; }; - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn loadSettingsFromJSValue(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, options: JSC.JSValue) bool { @@ -1532,7 +1532,7 @@ pub const H2FrameParser = struct { return true; } - pub fn updateSettings(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn updateSettings(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -1544,13 +1544,13 @@ pub const H2FrameParser = struct { if (this.loadSettingsFromJSValue(globalObject, options)) { this.setSettings(this.localSettings); - return JSC.JSValue.jsUndefined(); + return .undefined; } return .zero; } - pub fn getCurrentState(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn getCurrentState(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); var result = JSValue.createEmptyObject(globalObject, 9); result.put(globalObject, JSC.ZigString.static("effectiveLocalWindowSize"), JSC.JSValue.jsNumber(this.windowSize)); @@ -1568,7 +1568,7 @@ pub const H2FrameParser = struct { result.put(globalObject, JSC.ZigString.static("outboundQueueSize"), JSC.JSValue.jsNumber(0)); return result; } - pub fn goaway(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn goaway(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(3); if (args_list.len < 1) { @@ -1608,17 +1608,17 @@ pub const H2FrameParser = struct { if (opaque_data_arg.asArrayBuffer(globalObject)) |array_buffer| { const slice = array_buffer.byteSlice(); this.sendGoAway(0, @enumFromInt(errorCode), slice, lastStreamID); - return JSC.JSValue.jsUndefined(); + return .undefined; } } } } this.sendGoAway(0, @enumFromInt(errorCode), "", lastStreamID); - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn ping(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn ping(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -1629,14 +1629,14 @@ pub const H2FrameParser = struct { if (args_list.ptr[0].asArrayBuffer(globalObject)) |array_buffer| { const slice = array_buffer.slice(); this.sendPing(false, slice); - return JSC.JSValue.jsUndefined(); + return .undefined; } globalObject.throw("Expected payload to be a Buffer", .{}); return .zero; } - pub fn getEndAfterHeaders(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn getEndAfterHeaders(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -1664,7 +1664,7 @@ pub const H2FrameParser = struct { return JSC.JSValue.jsBoolean(stream.endAfterHeaders); } - pub fn setEndAfterHeaders(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn setEndAfterHeaders(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(2); if (args_list.len < 2) { @@ -1698,7 +1698,7 @@ pub const H2FrameParser = struct { return JSC.JSValue.jsBoolean(true); } - pub fn isStreamAborted(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn isStreamAborted(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -1728,7 +1728,7 @@ pub const H2FrameParser = struct { } return JSC.JSValue.jsBoolean(true); } - pub fn getStreamState(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn getStreamState(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -1765,7 +1765,7 @@ pub const H2FrameParser = struct { return state; } - pub fn setStreamPriority(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn setStreamPriority(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(2); if (args_list.len < 2) { @@ -1859,7 +1859,7 @@ pub const H2FrameParser = struct { } return JSC.JSValue.jsBoolean(true); } - pub fn rstStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn rstStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(2); if (args_list.len < 2) { @@ -1938,7 +1938,7 @@ pub const H2FrameParser = struct { } } - pub fn sendTrailers(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn sendTrailers(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(3); if (args_list.len < 3) { @@ -1994,13 +1994,13 @@ pub const H2FrameParser = struct { const name = name_slice.slice(); if (header_name.charAt(0) == ':') { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_PSEUDOHEADER", "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_PSEUDOHEADER, "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } var js_value = headers_arg.getTruthy(globalObject, name) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2010,20 +2010,20 @@ pub const H2FrameParser = struct { var value_iter = js_value.arrayIterator(globalObject); if (SingleValueHeaders.has(name) and value_iter.len > 1) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER", "Header field \"{s}\" must only have a single value", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER, "Header field \"{s}\" must only have a single value", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } while (value_iter.next()) |item| { if (item.isEmptyOrUndefinedOrNull()) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } const value_str = item.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2038,12 +2038,12 @@ pub const H2FrameParser = struct { stream.state = .CLOSED; stream.rstCode = @intFromEnum(ErrorCode.COMPRESSION_ERROR); this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode)); - return JSC.JSValue.jsUndefined(); + return .undefined; }; } } else { const value_str = js_value.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2058,7 +2058,7 @@ pub const H2FrameParser = struct { stream.state = .CLOSED; stream.rstCode = @intFromEnum(ErrorCode.COMPRESSION_ERROR); this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode)); - return JSC.JSValue.jsUndefined(); + return .undefined; }; } } @@ -2075,9 +2075,9 @@ pub const H2FrameParser = struct { frame.write(@TypeOf(writer), writer); _ = writer.write(buffer[0..encoded_size]) catch 0; - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn writeStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn writeStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(3); if (args_list.len < 3) { @@ -2121,7 +2121,8 @@ pub const H2FrameParser = struct { const payload = zig_str.slice(); this.sendData(stream_id, payload, close and !stream.waitForTrailers); } else { - globalObject.throw("Expected data to be an ArrayBuffer or a string", .{}); + if (!globalObject.hasException()) + globalObject.throw("Expected data to be an ArrayBuffer or a string", .{}); return .zero; } @@ -2147,7 +2148,7 @@ pub const H2FrameParser = struct { return stream_id; } - pub fn request(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn request(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); // we use PADDING_STRATEGY_NONE with is default // TODO: PADDING_STRATEGY_MAX AND PADDING_STRATEGY_ALIGNED @@ -2199,7 +2200,7 @@ pub const H2FrameParser = struct { if (ignore_pseudo_headers == 1) continue; if (!ValidRequestPseudoHeaders.has(name)) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_PSEUDOHEADER", "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_PSEUDOHEADER, "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } @@ -2208,7 +2209,7 @@ pub const H2FrameParser = struct { } var js_value = headers_arg.getTruthy(globalObject, name) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2219,20 +2220,20 @@ pub const H2FrameParser = struct { var value_iter = js_value.arrayIterator(globalObject); if (SingleValueHeaders.has(name) and value_iter.len > 1) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER", "Header field \"{s}\" must only have a single value", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Header field \"{s}\" must only have a single value", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } while (value_iter.next()) |item| { if (item.isEmptyOrUndefinedOrNull()) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } const value_str = item.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2250,13 +2251,13 @@ pub const H2FrameParser = struct { stream.state = .CLOSED; stream.rstCode = @intFromEnum(ErrorCode.COMPRESSION_ERROR); this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode)); - return JSC.JSValue.jsUndefined(); + return .undefined; }; } } else { log("single header {s}", .{name}); const value_str = js_value.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2423,7 +2424,7 @@ pub const H2FrameParser = struct { return JSC.JSValue.jsNumber(stream.id); } - pub fn read(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn read(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const args_list = callframe.arguments(1); if (args_list.len < 1) { @@ -2439,13 +2440,13 @@ pub const H2FrameParser = struct { const result = this.readBytes(bytes); bytes = bytes[result..]; } - return JSC.JSValue.jsUndefined(); + return .undefined; } globalObject.throw("Expected data to be a Buffer or ArrayBuffer", .{}); return .zero; } - pub fn constructor(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) ?*H2FrameParser { + pub fn constructor(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) ?*H2FrameParser { const args_list = callframe.arguments(1); if (args_list.len < 1) { globalObject.throw("Expected 1 argument", .{}); @@ -2528,7 +2529,7 @@ pub const H2FrameParser = struct { pub fn finalize( this: *H2FrameParser, - ) callconv(.C) void { + ) void { log("finalize", .{}); this.deinit(); } diff --git a/src/bun.js/api/bun/process.zig b/src/bun.js/api/bun/process.zig index b7c81b3b6e789..fe982a202dd37 100644 --- a/src/bun.js/api/bun/process.zig +++ b/src/bun.js/api/bun/process.zig @@ -962,6 +962,7 @@ pub const PosixSpawnOptions = struct { stdin: Stdio = .ignore, stdout: Stdio = .ignore, stderr: Stdio = .ignore, + ipc: ?bun.FileDescriptor = null, extra_fds: []const Stdio = &.{}, cwd: []const u8 = "", detached: bool = false, @@ -980,6 +981,7 @@ pub const PosixSpawnOptions = struct { inherit: void, ignore: void, buffer: void, + ipc: void, pipe: bun.FileDescriptor, dup2: struct { out: bun.JSC.Subprocess.StdioKind, to: bun.JSC.Subprocess.StdioKind }, }; @@ -1030,6 +1032,7 @@ pub const WindowsSpawnOptions = struct { stdin: Stdio = .ignore, stdout: Stdio = .ignore, stderr: Stdio = .ignore, + ipc: ?bun.FileDescriptor = null, extra_fds: []const Stdio = &.{}, cwd: []const u8 = "", detached: bool = false, @@ -1049,6 +1052,7 @@ pub const WindowsSpawnOptions = struct { inherit: void, ignore: void, buffer: *bun.windows.libuv.Pipe, + ipc: *bun.windows.libuv.Pipe, pipe: bun.FileDescriptor, dup2: struct { out: bun.JSC.Subprocess.StdioKind, to: bun.JSC.Subprocess.StdioKind }, @@ -1075,6 +1079,7 @@ pub const PosixSpawnResult = struct { stdin: ?bun.FileDescriptor = null, stdout: ?bun.FileDescriptor = null, stderr: ?bun.FileDescriptor = null, + ipc: ?bun.FileDescriptor = null, extra_pipes: std.ArrayList(bun.FileDescriptor) = std.ArrayList(bun.FileDescriptor).init(bun.default_allocator), memfds: [3]bool = .{ false, false, false }, @@ -1258,6 +1263,11 @@ pub fn spawnProcessPosix( attr.set(@intCast(flags)) catch {}; attr.resetSignals() catch {}; + if (options.ipc) |ipc| { + try actions.inherit(ipc); + spawned.ipc = ipc; + } + const stdio_options: [3]PosixSpawnOptions.Stdio = .{ options.stdin, options.stdout, options.stderr }; const stdios: [3]*?bun.FileDescriptor = .{ &spawned.stdin, &spawned.stdout, &spawned.stderr }; @@ -1284,7 +1294,7 @@ pub fn spawnProcessPosix( .inherit => { try actions.inherit(fileno); }, - .ignore => { + .ipc, .ignore => { try actions.openZ(fileno, "/dev/null", flag | bun.O.CREAT, 0o664); }, .path => |path| { @@ -1301,13 +1311,8 @@ pub fn spawnProcessPosix( else => "spawn_stdio_generic", }; - // We use the linux syscall api because the glibc requirement is 2.27, which is a little close for comfort. - const rc = std.os.linux.memfd_create(label, 0); - if (bun.C.getErrno(rc) != .SUCCESS) { - break :use_memfd; - } + const fd = bun.sys.memfd_create(label, 0).unwrap() catch break :use_memfd; - const fd = bun.toFD(@as(u32, @intCast(rc))); to_close_on_error.append(fd) catch {}; to_set_cloexec.append(fd) catch {}; try actions.dup2(fd, fileno); @@ -1408,7 +1413,7 @@ pub fn spawnProcessPosix( .path => |path| { try actions.open(fileno, path, bun.O.RDWR | bun.O.CREAT, 0o664); }, - .buffer => { + .ipc, .buffer => { const fds: [2]bun.FileDescriptor = brk: { var fds_: [2]std.c.fd_t = undefined; const rc = std.c.socketpair(std.posix.AF.UNIX, std.posix.SOCK.STREAM, 0, &fds_); @@ -1518,8 +1523,10 @@ pub fn spawnProcessWindows( const allocator = stack_allocator.get(); const loop = options.windows.loop.platformEventLoop().uv_loop; - const cwd = try allocator.dupeZ(u8, options.cwd); - defer allocator.free(cwd); + var cwd_buf: bun.PathBuffer = undefined; + @memcpy(cwd_buf[0..options.cwd.len], options.cwd); + cwd_buf[options.cwd.len] = 0; + const cwd = cwd_buf[0..options.cwd.len :0]; uv_process_options.cwd = cwd.ptr; @@ -1587,6 +1594,11 @@ pub fn spawnProcessWindows( stdio.flags = uv.UV_INHERIT_FD; stdio.data.fd = fd_i; }, + .ipc => |my_pipe| { + // ipc option inside stdin, stderr or stdout are not supported + bun.default_allocator.destroy(my_pipe); + stdio.flags = uv.UV_IGNORE; + }, .ignore => { stdio.flags = uv.UV_IGNORE; }, @@ -1655,6 +1667,11 @@ pub fn spawnProcessWindows( try uv_files_to_close.append(fd); stdio.data.fd = fd; }, + .ipc => |my_pipe| { + try my_pipe.init(loop, true).unwrap(); + stdio.flags = uv.UV_CREATE_PIPE | uv.UV_WRITABLE_PIPE | uv.UV_READABLE_PIPE | uv.UV_OVERLAPPED_PIPE; + stdio.data.stream = @ptrCast(my_pipe); + }, .buffer => |my_pipe| { try my_pipe.init(loop, false).unwrap(); stdio.flags = uv.UV_CREATE_PIPE | uv.UV_WRITABLE_PIPE | uv.UV_READABLE_PIPE | uv.UV_OVERLAPPED_PIPE; @@ -1739,7 +1756,7 @@ pub fn spawnProcessWindows( for (options.extra_fds, 0..) |*input, i| { switch (input.*) { - .buffer => { + .ipc, .buffer => { result.extra_pipes.appendAssumeCapacity(.{ .buffer = @ptrCast(stdio_containers.items[3 + i].data.stream) }); }, else => { @@ -1756,6 +1773,7 @@ pub const sync = struct { stdin: Stdio = .ignore, stdout: Stdio = .inherit, stderr: Stdio = .inherit, + ipc: ?bun.FileDescriptor = null, cwd: []const u8 = "", detached: bool = false, @@ -1790,6 +1808,8 @@ pub const sync = struct { .stdin = this.stdin.toStdio(), .stdout = this.stdout.toStdio(), .stderr = this.stderr.toStdio(), + .ipc = this.ipc, + .cwd = this.cwd, .detached = this.detached, .use_execve_on_macos = this.use_execve_on_macos, @@ -2036,21 +2056,54 @@ pub const sync = struct { return spawnWithArgv(options, @ptrCast(args.items.ptr), @ptrCast(envp)); } + // Forward signals from parent to the child process. + extern "C" fn Bun__registerSignalsForForwarding() void; + extern "C" fn Bun__unregisterSignalsForForwarding() void; + + // The PID to forward signals to. + // Set to 0 when unregistering. + extern "C" var Bun__currentSyncPID: i64; + + // Race condition: a signal could be sent before spawnProcessPosix returns. + // We need to make sure to send it after the process is spawned. + extern "C" fn Bun__sendPendingSignalIfNecessary() void; + fn spawnPosix( options: *const Options, argv: [*:null]?[*:0]const u8, envp: [*:null]?[*:0]const u8, ) !Maybe(Result) { + Bun__currentSyncPID = 0; + Bun__registerSignalsForForwarding(); + defer { + Bun__unregisterSignalsForForwarding(); + bun.crash_handler.resetOnPosix(); + } const process = switch (try spawnProcessPosix(&options.toSpawnOptions(), argv, envp)) { .err => |err| return .{ .err = err }, .result => |proces| proces, }; + Bun__currentSyncPID = @intCast(process.pid); + + Bun__sendPendingSignalIfNecessary(); + var out = [2]std.ArrayList(u8){ std.ArrayList(u8).init(bun.default_allocator), std.ArrayList(u8).init(bun.default_allocator), }; var out_fds = [2]bun.FileDescriptor{ process.stdout orelse bun.invalid_fd, process.stderr orelse bun.invalid_fd }; + var success = false; defer { + // If we're going to return an error, + // let's make sure to clean up the output buffers + // and kill the process + if (!success) { + for (&out) |*array_list| { + array_list.clearAndFree(); + } + _ = std.c.kill(process.pid, 1); + } + for (out_fds) |fd| { if (fd != bun.invalid_fd) { _ = bun.sys.close(fd); @@ -2081,13 +2134,14 @@ pub const sync = struct { for (&out_fds_to_wait_for, &out, &out_fds) |*fd, *bytes, *out_fd| { if (fd.* == bun.invalid_fd) continue; while (true) { - bytes.ensureUnusedCapacity(16384) catch bun.outOfMemory(); + bytes.ensureUnusedCapacity(16384) catch { + return .{ .err = bun.sys.Error.fromCode(.NOMEM, .recv) }; + }; switch (bun.sys.recvNonBlock(fd.*, bytes.unusedCapacitySlice())) { .err => |err| { if (err.isRetry() or err.getErrno() == .PIPE) { break; } - _ = std.c.kill(process.pid, 1); return .{ .err = err }; }, .result => |bytes_read| { @@ -2156,6 +2210,7 @@ pub const sync = struct { } } + success = true; return .{ .result = Result{ .status = status, diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 8c6612693a206..3c517b8e5b0af 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -18,47 +18,7 @@ const ZigString = JSC.ZigString; const BoringSSL = bun.BoringSSL; const X509 = @import("./x509.zig"); const Async = bun.Async; - -// const Corker = struct { -// ptr: ?*[16384]u8 = null, -// holder: ?*anyopaque = null, -// list: bun.ByteList = .{}, - -// pub fn write(this: *Corker, owner: *anyopaque, bytes: []const u8) usize { -// if (this.holder != null and this.holder.? != owner) { -// return 0; -// } - -// this.holder = owner; -// if (this.ptr == null) { -// this.ptr = bun.default_allocator.alloc(u8, 16384) catch @panic("Out of memory allocating corker"); -// bun.assert(this.list.cap == 0); -// bun.assert(this.list.len == 0); -// this.list.cap = 16384; -// this.list.ptr = this.ptr.?; -// this.list.len = 0; -// } -// } - -// pub fn flushIfNecessary(this: *Corker, comptime ssl: bool, socket: uws.NewSocketHandler(ssl), owner: *anyopaque) void { -// if (this.holder == null or this.holder.? != owner) { -// return; -// } - -// if (this.ptr == null) { -// return; -// } - -// if (this.list.len == 0) { -// return; -// } - -// const bytes = ths.list.slice(); - -// this.list.len = 0; -// } -// }; - +const uv = bun.windows.libuv; noinline fn getSSLException(globalThis: *JSC.JSGlobalObject, defaultMessage: []const u8) JSValue { var zig_str: ZigString = ZigString.init(""); var output_buf: [4096]u8 = undefined; @@ -110,7 +70,7 @@ noinline fn getSSLException(globalThis: *JSC.JSGlobalObject, defaultMessage: []c if (written > 0) { const message = output_buf[0..written]; - zig_str = ZigString.init(std.fmt.allocPrint(bun.default_allocator, "OpenSSL {s}", .{message}) catch unreachable); + zig_str = ZigString.init(std.fmt.allocPrint(bun.default_allocator, "OpenSSL {s}", .{message}) catch bun.outOfMemory()); var encoded_str = zig_str.withEncoding(); encoded_str.mark(); @@ -176,21 +136,19 @@ const Handlers = struct { pub const Scope = struct { handlers: *Handlers, - socket_context: ?*uws.SocketContext, - pub fn exit(this: *Scope, ssl: bool, wrapped: WrappedType) void { + pub fn exit(this: *Scope) void { var vm = this.handlers.vm; defer vm.eventLoop().exit(); - this.handlers.markInactive(ssl, this.socket_context, wrapped); + this.handlers.markInactive(); } }; - pub fn enter(this: *Handlers, context: ?*uws.SocketContext) Scope { + pub fn enter(this: *Handlers) Scope { this.markActive(); this.vm.eventLoop().enter(); return .{ .handlers = this, - .socket_context = context, }; } @@ -219,24 +177,18 @@ const Handlers = struct { return true; } - pub fn markInactive(this: *Handlers, ssl: bool, ctx: ?*uws.SocketContext, wrapped: WrappedType) void { + pub fn markInactive(this: *Handlers) void { Listener.log("markInactive", .{}); this.active_connections -= 1; if (this.active_connections == 0) { if (this.is_server) { var listen_socket: *Listener = @fieldParentPtr("handlers", this); // allow it to be GC'd once the last connection is closed and it's not listening anymore - if (listen_socket.listener == null) { + if (listen_socket.listener == .none) { listen_socket.strong_self.clear(); } } else { this.unprotect(); - // will deinit when is not wrapped or when is the TCP wrapped connection - if (wrapped != .tls) { - if (ctx) |ctx_| { - ctx_.deinit(ssl); - } - } bun.default_allocator.destroy(this); } } @@ -258,10 +210,8 @@ const Handlers = struct { return false; } - const result = onError.callWithThis(globalObject, thisValue, err); - if (result.isAnyError()) { - _ = vm.uncaughtException(globalObject, result, false); - } + _ = onError.call(globalObject, thisValue, err) catch |e| + globalObject.reportActiveExceptionAsUnhandled(e); return true; } @@ -499,7 +449,8 @@ pub const Listener = struct { pub const log = Output.scoped(.Listener, false); handlers: Handlers, - listener: ?*uws.ListenSocket = null, + listener: ListenerType = .none, + poll_ref: Async.KeepAlive = Async.KeepAlive.init(), connection: UnixOrHost, socket_context: ?*uws.SocketContext = null, @@ -511,10 +462,16 @@ pub const Listener = struct { pub usingnamespace JSC.Codegen.JSListener; + pub const ListenerType = union(enum) { + uws: *uws.ListenSocket, + namedPipe: *WindowsNamedPipeListeningContext, + none: void, + }; + pub fn getData( this: *Listener, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getData()", .{}); return this.strong_data.get() orelse JSValue.jsUndefined(); } @@ -541,13 +498,13 @@ pub const Listener = struct { switch (this) { .unix => |u| { return .{ - .unix = (bun.default_allocator.dupe(u8, u) catch unreachable), + .unix = (bun.default_allocator.dupe(u8, u) catch bun.outOfMemory()), }; }, .host => |h| { return .{ .host = .{ - .host = (bun.default_allocator.dupe(u8, h.host) catch unreachable), + .host = (bun.default_allocator.dupe(u8, h.host) catch bun.outOfMemory()), .port = this.host.port, }, }; @@ -569,10 +526,10 @@ pub const Listener = struct { } }; - pub fn reload(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn reload(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const args = callframe.arguments(1); - if (args.len < 1 or (this.listener == null and this.handlers.active_connections == 0)) { + if (args.len < 1 or (this.listener == .none and this.handlers.active_connections == 0)) { globalObject.throw("Expected 1 argument", .{}); return .zero; } @@ -634,10 +591,59 @@ pub const Listener = struct { const ssl_enabled = ssl != null; const socket_flags: i32 = if (exclusive) 1 else 0; + defer if (ssl != null) ssl.?.deinit(); + + if (Environment.isWindows) { + if (port == null) { + // we check if the path is a named pipe otherwise we try to connect using AF_UNIX + const pipe_name = hostname_or_unix.slice(); + if (strings.startsWith(pipe_name, "\\\\.\\pipe\\") or strings.startsWith(pipe_name, "\\\\?\\pipe\\")) { + const connection: Listener.UnixOrHost = .{ .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch bun.outOfMemory()).slice() }; + if (ssl_enabled) { + if (ssl.?.protos) |p| { + protos = p[0..ssl.?.protos_len]; + } + } + var socket = Listener{ + .handlers = handlers, + .connection = connection, + .ssl = ssl_enabled, + .socket_context = null, + .listener = .none, + .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch bun.outOfMemory()) else null, + }; + + vm.eventLoop().ensureWaker(); + socket.handlers.protect(); + + if (socket_config.default_data != .zero) { + socket.strong_data = JSC.Strong.create(socket_config.default_data, globalObject); + } + + var this: *Listener = handlers.vm.allocator.create(Listener) catch bun.outOfMemory(); + this.* = socket; + //TODO: server_name is not supported on named pipes, I belive its , lets wait for someone to ask for it + + this.listener = .{ + // we need to add support for the backlog parameter on listen here we use the default value of nodejs + .namedPipe = WindowsNamedPipeListeningContext.listen(globalObject, pipe_name, 511, ssl, this) catch { + exception.* = JSC.toInvalidArguments("Failed to listen at {s}", .{pipe_name}, globalObject).asObjectRef(); + this.deinit(); + return .zero; + }, + }; + + const this_value = this.toJS(globalObject); + this.strong_self.set(globalObject, this_value); + this.poll_ref.ref(handlers.vm); + + return this_value; + } + } + } const ctx_opts: uws.us_bun_socket_context_options_t = JSC.API.ServerConfig.SSLConfig.asUSockets(ssl); - defer if (ssl != null) ssl.?.deinit(); vm.eventLoop().ensureWaker(); const socket_context = uws.us_create_bun_socket_context( @@ -656,7 +662,7 @@ pub const Listener = struct { if (errno != 0) { err.put(globalObject, ZigString.static("errno"), JSValue.jsNumber(errno)); if (bun.C.SystemErrno.init(errno)) |str| { - err.put(globalObject, ZigString.static("code"), ZigString.init(@tagName(str)).toValueGC(globalObject)); + err.put(globalObject, ZigString.static("code"), ZigString.init(@tagName(str)).toJS(globalObject)); } } @@ -705,15 +711,15 @@ pub const Listener = struct { } var connection: Listener.UnixOrHost = if (port) |port_| .{ - .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), .port = port_ }, + .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch bun.outOfMemory()).slice(), .port = port_ }, } else .{ - .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), + .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch bun.outOfMemory()).slice(), }; const listen_socket: *uws.ListenSocket = brk: { switch (connection) { .host => |c| { - const host = bun.default_allocator.dupeZ(u8, c.host) catch unreachable; + const host = bun.default_allocator.dupeZ(u8, c.host) catch bun.outOfMemory(); defer bun.default_allocator.free(host); const socket = uws.us_socket_context_listen( @@ -731,7 +737,7 @@ pub const Listener = struct { break :brk socket; }, .unix => |u| { - const host = bun.default_allocator.dupeZ(u8, u) catch unreachable; + const host = bun.default_allocator.dupeZ(u8, u) catch bun.outOfMemory(); defer bun.default_allocator.free(host); break :brk uws.us_socket_context_listen_unix(@intFromBool(ssl_enabled), socket_context, host, host.len, socket_flags, 8); }, @@ -756,7 +762,7 @@ pub const Listener = struct { if (errno != 0) { err.put(globalObject, ZigString.static("errno"), JSValue.jsNumber(errno)); if (bun.C.SystemErrno.init(errno)) |str| { - err.put(globalObject, ZigString.static("code"), ZigString.init(@tagName(str)).toValueGC(globalObject)); + err.put(globalObject, ZigString.static("code"), ZigString.init(@tagName(str)).toJS(globalObject)); } } exception.* = err.asObjectRef(); @@ -769,8 +775,8 @@ pub const Listener = struct { .connection = connection, .ssl = ssl_enabled, .socket_context = socket_context, - .listener = listen_socket, - .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch unreachable) else null, + .listener = .{ .uws = listen_socket }, + .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch bun.outOfMemory()) else null, }; socket.handlers.protect(); @@ -810,32 +816,58 @@ pub const Listener = struct { onCreate(false, socket); } - pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*Listener { + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*Listener { globalObject.throw("Cannot construct Listener", .{}); return null; } + pub fn onNamePipeCreated(comptime ssl: bool, listener: *Listener) *NewSocket(ssl) { + const Socket = NewSocket(ssl); + bun.assert(ssl == listener.ssl); + + var this_socket = Socket.new(.{ + .handlers = &listener.handlers, + .this_value = .zero, + // here we start with a detached socket and attach it later after accept + .socket = Socket.Socket.detached, + .protos = listener.protos, + .flags = .{ .owned_protos = false }, + .socket_context = null, // dont own the socket context + }); + this_socket.ref(); + if (listener.strong_data.get()) |default_data| { + const globalObject = listener.handlers.globalObject; + Socket.dataSetCached(this_socket.getThisValue(globalObject), globalObject, default_data); + } + return this_socket; + } + pub fn onCreate(comptime ssl: bool, socket: uws.NewSocketHandler(ssl)) void { JSC.markBinding(@src()); log("onCreate", .{}); + //PS: We dont reach this path when using named pipes on windows see onNamePipeCreated + var listener: *Listener = socket.context().?.ext(ssl, *Listener).?.*; const Socket = NewSocket(ssl); bun.assert(ssl == listener.ssl); - var this_socket = listener.handlers.vm.allocator.create(Socket) catch bun.outOfMemory(); - this_socket.* = Socket{ + var this_socket = Socket.new(.{ .handlers = &listener.handlers, .this_value = .zero, .socket = socket, .protos = listener.protos, - .owned_protos = false, - }; + .flags = .{ .owned_protos = false }, + .socket_context = null, // dont own the socket context + }); + this_socket.ref(); if (listener.strong_data.get()) |default_data| { const globalObject = listener.handlers.globalObject; Socket.dataSetCached(this_socket.getThisValue(globalObject), globalObject, default_data); } - socket.ext(**anyopaque).* = bun.cast(**anyopaque, this_socket); - socket.setTimeout(120000); + if (socket.ext(**anyopaque)) |ctx| { + ctx.* = bun.cast(**anyopaque, this_socket); + } + socket.setTimeout(120); } pub fn addServerName(this: *Listener, global: *JSC.JSGlobalObject, hostname: JSValue, tls: JSValue) JSValue { @@ -874,51 +906,71 @@ pub const Listener = struct { return JSValue.jsUndefined(); } - pub fn stop(this: *Listener, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn dispose(this: *Listener, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + this.doStop(true); + return .undefined; + } + + pub fn stop(this: *Listener, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const arguments = callframe.arguments(1); log("close", .{}); - var listener = this.listener orelse return JSValue.jsUndefined(); - this.listener = null; + this.doStop(if (arguments.len > 0 and arguments.ptr[0].isBoolean()) arguments.ptr[0].toBoolean() else false); + + return .undefined; + } + + fn doStop(this: *Listener, force_close: bool) void { + if (this.listener == .none) return; + const listener = this.listener; + this.listener = .none; this.poll_ref.unref(this.handlers.vm); // if we already have no active connections, we can deinit the context now if (this.handlers.active_connections == 0) { this.handlers.unprotect(); // deiniting the context will also close the listener - this.socket_context.?.deinit(this.ssl); - this.socket_context = null; + if (this.socket_context) |ctx| { + this.socket_context = null; + ctx.deinit(this.ssl); + } this.strong_self.clear(); this.strong_data.clear(); } else { - const forceClose = arguments.len > 0 and arguments.ptr[0].isBoolean() and arguments.ptr[0].toBoolean() and this.socket_context != null; - if (forceClose) { + if (force_close) { // close all connections in this context and wait for them to close - this.socket_context.?.close(this.ssl); + if (this.socket_context) |ctx| { + ctx.close(this.ssl); + } } else { // only close the listener and wait for the connections to close by it self - listener.close(this.ssl); + switch (listener) { + .uws => |socket| socket.close(this.ssl), + .namedPipe => |namedPipe| if (Environment.isWindows) namedPipe.closePipeAndDeinit(), + .none => {}, + } } } - - return JSValue.jsUndefined(); } pub fn finalize(this: *Listener) callconv(.C) void { - log("Finalize", .{}); - if (this.listener) |listener| { - this.listener = null; - listener.close(this.ssl); + log("finalize", .{}); + const listener = this.listener; + this.listener = .none; + switch (listener) { + .uws => |socket| socket.close(this.ssl), + .namedPipe => |namedPipe| if (Environment.isWindows) namedPipe.closePipeAndDeinit(), + .none => {}, } - this.deinit(); } pub fn deinit(this: *Listener) void { + log("deinit", .{}); this.strong_self.deinit(); this.strong_data.deinit(); this.poll_ref.unref(this.handlers.vm); - bun.assert(this.listener == null); + bun.assert(this.listener == .none); this.handlers.unprotect(); if (this.handlers.active_connections > 0) { @@ -940,41 +992,41 @@ pub const Listener = struct { bun.default_allocator.destroy(this); } - pub fn getConnectionsCount(this: *Listener, _: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getConnectionsCount(this: *Listener, _: *JSC.JSGlobalObject) JSValue { return JSValue.jsNumber(this.handlers.active_connections); } - pub fn getUnix(this: *Listener, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getUnix(this: *Listener, globalObject: *JSC.JSGlobalObject) JSValue { if (this.connection != .unix) { return JSValue.jsUndefined(); } - return ZigString.init(this.connection.unix).withEncoding().toValueGC(globalObject); + return ZigString.init(this.connection.unix).withEncoding().toJS(globalObject); } - pub fn getHostname(this: *Listener, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getHostname(this: *Listener, globalObject: *JSC.JSGlobalObject) JSValue { if (this.connection != .host) { return JSValue.jsUndefined(); } - return ZigString.init(this.connection.host.host).withEncoding().toValueGC(globalObject); + return ZigString.init(this.connection.host.host).withEncoding().toJS(globalObject); } - pub fn getPort(this: *Listener, _: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getPort(this: *Listener, _: *JSC.JSGlobalObject) JSValue { if (this.connection != .host) { return JSValue.jsUndefined(); } return JSValue.jsNumber(this.connection.host.port); } - pub fn ref(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn ref(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const this_value = callframe.this(); - if (this.listener == null) return JSValue.jsUndefined(); + if (this.listener == .none) return JSValue.jsUndefined(); this.poll_ref.ref(globalObject.bunVM()); this.strong_self.set(globalObject, this_value); return JSValue.jsUndefined(); } - pub fn unref(this: *Listener, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn unref(this: *Listener, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { this.poll_ref.unref(globalObject.bunVM()); if (this.handlers.active_connections == 0) { this.strong_self.clear(); @@ -1014,10 +1066,114 @@ pub const Listener = struct { const ssl_enabled = ssl != null; defer if (ssl != null) ssl.?.deinit(); - const ctx_opts: uws.us_bun_socket_context_options_t = JSC.API.ServerConfig.SSLConfig.asUSockets(socket_config.ssl); - vm.eventLoop().ensureWaker(); + var connection: Listener.UnixOrHost = blk: { + if (opts.getTruthy(globalObject, "fd")) |fd_| { + if (fd_.isNumber()) { + const fd = fd_.asFileDescriptor(); + break :blk .{ .fd = fd }; + } + } + if (port) |_| { + break :blk .{ .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch bun.outOfMemory()).slice(), .port = port.? } }; + } + + break :blk .{ .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch bun.outOfMemory()).slice() }; + }; + + if (Environment.isWindows) { + const isNamedPipe = switch (connection) { + // we check if the path is a named pipe otherwise we try to connect using AF_UNIX + .unix => |pipe_name| strings.startsWith(pipe_name, "\\\\.\\pipe\\") or strings.startsWith(pipe_name, "\\\\?\\pipe\\"), + .fd => |fd| brk: { + const uvfd = bun.uvfdcast(fd); + const fd_type = uv.uv_guess_handle(uvfd); + if (fd_type == uv.Handle.Type.named_pipe) { + break :brk true; + } + if (fd_type == uv.Handle.Type.unknown) { + // is not a libuv fd, check if it's a named pipe + const osfd: uv.uv_os_fd_t = @ptrFromInt(@as(usize, @intCast(uvfd))); + if (bun.windows.GetFileType(osfd) == bun.windows.FILE_TYPE_PIPE) { + // yay its a named pipe lets make it a libuv fd + connection.fd = bun.FDImpl.fromUV(uv.uv_open_osfhandle(osfd)).encode(); + break :brk true; + } + } + break :brk false; + }, + else => false, + }; + if (isNamedPipe) { + default_data.ensureStillAlive(); + + var handlers_ptr = handlers.vm.allocator.create(Handlers) catch bun.outOfMemory(); + handlers_ptr.* = handlers; + handlers_ptr.is_server = false; + + var promise = JSC.JSPromise.create(globalObject); + const promise_value = promise.asValue(globalObject); + handlers_ptr.promise.set(globalObject, promise_value); + + if (ssl_enabled) { + var tls = TLSSocket.new(.{ + .handlers = handlers_ptr, + .this_value = .zero, + .socket = TLSSocket.Socket.detached, + .connection = connection, + .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch bun.outOfMemory()) else null, + .server_name = server_name, + .socket_context = null, + }); + TLSSocket.dataSetCached(tls.getThisValue(globalObject), globalObject, default_data); + tls.poll_ref.ref(handlers.vm); + tls.ref(); + if (connection == .unix) { + const named_pipe = WindowsNamedPipeContext.connect(globalObject, connection.unix, ssl, .{ .tls = tls }) catch { + return promise_value; + }; + tls.socket = TLSSocket.Socket.fromNamedPipe(named_pipe); + } else { + // fd + const named_pipe = WindowsNamedPipeContext.open(globalObject, connection.fd, ssl, .{ .tls = tls }) catch { + return promise_value; + }; + tls.socket = TLSSocket.Socket.fromNamedPipe(named_pipe); + } + } else { + var tcp = TCPSocket.new(.{ + .handlers = handlers_ptr, + .this_value = .zero, + .socket = TCPSocket.Socket.detached, + .connection = null, + .protos = null, + .server_name = null, + .socket_context = null, + }); + tcp.ref(); + TCPSocket.dataSetCached(tcp.getThisValue(globalObject), globalObject, default_data); + tcp.poll_ref.ref(handlers.vm); + + if (connection == .unix) { + const named_pipe = WindowsNamedPipeContext.connect(globalObject, connection.unix, null, .{ .tcp = tcp }) catch { + return promise_value; + }; + tcp.socket = TCPSocket.Socket.fromNamedPipe(named_pipe); + } else { + // fd + const named_pipe = WindowsNamedPipeContext.open(globalObject, connection.fd, null, .{ .tcp = tcp }) catch { + return promise_value; + }; + tcp.socket = TCPSocket.Socket.fromNamedPipe(named_pipe); + } + } + return promise_value; + } + } + + const ctx_opts: uws.us_bun_socket_context_options_t = JSC.API.ServerConfig.SSLConfig.asUSockets(socket_config.ssl); + const socket_context = uws.us_create_bun_socket_context(@intFromBool(ssl_enabled), uws.Loop.get(), @sizeOf(usize), ctx_opts) orelse { const err = JSC.SystemError{ .message = bun.String.static("Failed to connect"), @@ -1026,28 +1182,16 @@ pub const Listener = struct { }; exception.* = err.toErrorInstance(globalObject).asObjectRef(); handlers.unprotect(); + connection.deinit(); return .zero; }; - const connection: Listener.UnixOrHost = blk: { - if (opts.getTruthy(globalObject, "fd")) |fd_| { - if (fd_.isNumber()) { - const fd = fd_.asFileDescriptor(); - break :blk .{ .fd = fd }; - } - } - if (port) |_| { - break :blk .{ .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), .port = port.? } }; - } - break :blk .{ .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice() }; - }; - if (ssl_enabled) { if (ssl.?.protos) |p| { protos = p[0..ssl.?.protos_len]; } if (ssl.?.server_name) |s| { - server_name = bun.default_allocator.dupe(u8, s[0..bun.len(s)]) catch unreachable; + server_name = bun.default_allocator.dupe(u8, s[0..bun.len(s)]) catch bun.outOfMemory(); } uws.NewSocketHandler(true).configure( socket_context, @@ -1093,41 +1237,39 @@ pub const Listener = struct { handlers_ptr.promise.set(globalObject, promise_value); if (ssl_enabled) { - var tls = handlers.vm.allocator.create(TLSSocket) catch bun.outOfMemory(); - - tls.* = .{ + var tls = TLSSocket.new(.{ .handlers = handlers_ptr, .this_value = .zero, - .socket = undefined, + .socket = TLSSocket.Socket.detached, .connection = connection, - .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch unreachable) else null, + .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch bun.outOfMemory()) else null, .server_name = server_name, - }; + .socket_context = socket_context, // owns the socket context + }); TLSSocket.dataSetCached(tls.getThisValue(globalObject), globalObject, default_data); - tls.doConnect(connection, socket_context) catch { + tls.doConnect(connection) catch { tls.handleConnectError(@intFromEnum(if (port == null) bun.C.SystemErrno.ENOENT else bun.C.SystemErrno.ECONNREFUSED)); return promise_value; }; + tls.poll_ref.ref(handlers.vm); return promise_value; } else { - var tcp = handlers.vm.allocator.create(TCPSocket) catch bun.outOfMemory(); - - tcp.* = .{ + var tcp = TCPSocket.new(.{ .handlers = handlers_ptr, .this_value = .zero, - .socket = undefined, + .socket = TCPSocket.Socket.detached, .connection = null, .protos = null, .server_name = null, - }; + .socket_context = socket_context, // owns the socket context + }); TCPSocket.dataSetCached(tcp.getThisValue(globalObject), globalObject, default_data); - - tcp.doConnect(connection, socket_context) catch { + tcp.doConnect(connection) catch { tcp.handleConnectError(@intFromEnum(if (port == null) bun.C.SystemErrno.ENOENT else bun.C.SystemErrno.ECONNREFUSED)); return promise_value; }; @@ -1179,27 +1321,26 @@ fn NewSocket(comptime ssl: bool) type { return struct { pub const Socket = uws.NewSocketHandler(ssl); socket: Socket, - detached: bool = false, + // if the socket owns a context it will be here + socket_context: ?*uws.SocketContext, - /// Prevent onClose from calling into JavaScript while we are finalizing - finalizing: bool = false, + flags: Flags = .{}, + ref_count: u32 = 1, wrapped: WrappedType = .none, handlers: *Handlers, this_value: JSC.JSValue = .zero, poll_ref: Async.KeepAlive = Async.KeepAlive.init(), - is_active: bool = false, last_4: [4]u8 = .{ 0, 0, 0, 0 }, - authorized: bool = false, connection: ?Listener.UnixOrHost = null, protos: ?[]const u8, - owned_protos: bool = true, server_name: ?[]const u8 = null, // TODO: switch to something that uses `visitAggregate` and have the // `Listener` keep a list of all the sockets JSValue in there // This is wasteful because it means we are keeping a JSC::Weak for every single open socket has_pending_activity: std.atomic.Value(bool) = std.atomic.Value(bool).init(true), + pub usingnamespace bun.NewRefCounted(@This(), @This().deinit); const This = @This(); const log = Output.scoped(.Socket, false); @@ -1210,7 +1351,13 @@ fn NewSocket(comptime ssl: bool) type { total: usize = 0, }, }; - + const Flags = packed struct { + is_active: bool = false, + /// Prevent onClose from calling into JavaScript while we are finalizing + finalizing: bool = false, + authorized: bool = false, + owned_protos: bool = true, + }; pub usingnamespace if (!ssl) JSC.Codegen.JSTCPSocket else @@ -1222,46 +1369,60 @@ fn NewSocket(comptime ssl: bool) type { return this.has_pending_activity.load(.acquire); } - pub fn doConnect(this: *This, connection: Listener.UnixOrHost, socket_ctx: *uws.SocketContext) !void { + pub fn doConnect(this: *This, connection: Listener.UnixOrHost) !void { + bun.assert(this.socket_context != null); switch (connection) { .host => |c| { - _ = try This.Socket.connectPtr( + this.ref(); + this.socket = try This.Socket.connectAnon( normalizeHost(c.host), c.port, - socket_ctx, - This, + this.socket_context.?, this, - "socket", ); }, .unix => |u| { - _ = try This.Socket.connectUnixPtr( + this.ref(); + + this.socket = try This.Socket.connectUnixAnon( u, - socket_ctx, - This, + this.socket_context.?, this, - "socket", ); }, .fd => |f| { - const socket = This.Socket.fromFd(socket_ctx, f, This, this, "socket") orelse return error.ConnectionFailed; + const socket = This.Socket.fromFd(this.socket_context.?, f, This, this, null) orelse return error.ConnectionFailed; this.onOpen(socket); }, } } - pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*This { + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*This { globalObject.throw("Cannot construct Socket", .{}); return null; } + pub fn handleError(this: *This, err_value: JSC.JSValue) void { + log("handleError", .{}); + const handlers = this.handlers; + var vm = handlers.vm; + if (vm.isShuttingDown()) { + return; + } + vm.eventLoop().enter(); + defer vm.eventLoop().exit(); + const globalObject = handlers.globalObject; + const this_value = this.getThisValue(globalObject); + _ = handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, err_value }); + } + pub fn onWritable( this: *This, _: Socket, ) void { JSC.markBinding(@src()); log("onWritable", .{}); - if (this.detached) return; + if (this.socket.isDetached()) return; const handlers = this.handlers; const callback = handlers.onWritable; if (callback == .zero) return; @@ -1275,48 +1436,42 @@ fn NewSocket(comptime ssl: bool) type { const globalObject = handlers.globalObject; const this_value = this.getThisValue(globalObject); - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ - this_value, - }); - - if (result.toError()) |err_value| { - _ = handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, err_value }); - } + _ = callback.call(globalObject, this_value, &.{this_value}) catch |err| { + _ = handlers.callErrorHandler(this_value, &.{ this_value, globalObject.takeException(err) }); + }; } pub fn onTimeout( this: *This, - socket: Socket, + _: Socket, ) void { JSC.markBinding(@src()); log("onTimeout", .{}); - if (this.detached) return; + if (this.socket.isDetached()) return; const handlers = this.handlers; const callback = handlers.onTimeout; - if (callback == .zero or this.finalizing) return; + if (callback == .zero or this.flags.finalizing) return; if (handlers.vm.isShuttingDown()) { return; } // the handlers must be kept alive for the duration of the function call // that way if we need to call the error handler, we can - var scope = handlers.enter(socket.context()); - defer scope.exit(ssl, this.wrapped); + var scope = handlers.enter(); + defer scope.exit(); const globalObject = handlers.globalObject; const this_value = this.getThisValue(globalObject); - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ - this_value, - }); - - if (result.toError()) |err_value| { - _ = handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, err_value }); - } + _ = callback.call(globalObject, this_value, &.{this_value}) catch |err| { + _ = handlers.callErrorHandler(this_value, &.{ this_value, globalObject.takeException(err) }); + }; } + fn handleConnectError(this: *This, errno: c_int) void { - log("onConnectError({d})", .{errno}); - if (this.detached) return; - this.detached = true; + log("onConnectError({d}, {})", .{ errno, this.ref_count }); + const needs_deref = !this.socket.isDetached(); + this.socket = Socket.detached; + defer if (needs_deref) this.deref(); defer this.markInactive(); const handlers = this.handlers; @@ -1355,10 +1510,10 @@ fn NewSocket(comptime ssl: bool) type { const this_value = this.getThisValue(globalObject); const err_value = err.toErrorInstance(globalObject); - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + const result = callback.call(globalObject, this_value, &[_]JSValue{ this_value, err_value, - }); + }) catch |e| globalObject.takeException(e); if (result.toError()) |err_val| { if (handlers.rejectPromise(err_val)) return; @@ -1378,77 +1533,82 @@ fn NewSocket(comptime ssl: bool) type { } pub fn markActive(this: *This) void { - if (!this.is_active) { + if (!this.flags.is_active) { this.handlers.markActive(); - this.is_active = true; + this.flags.is_active = true; this.has_pending_activity.store(true, .release); } } + pub fn closeAndDetach(this: *This, code: uws.CloseCode) void { + const socket = this.socket; + this.socket.detach(); + socket.close(code); + } + pub fn markInactive(this: *This) void { - if (this.is_active) { - if (!this.detached) { - // we have to close the socket before the socket context is closed - // otherwise we will get a segfault - // uSockets will defer freeing the TCP socket until the next tick - if (!this.socket.isClosed()) { - this.detached = true; - this.socket.close(.normal); - // onClose will call markInactive again - return; - } + if (this.flags.is_active) { + // we have to close the socket before the socket context is closed + // otherwise we will get a segfault + // uSockets will defer freeing the TCP socket until the next tick + if (!this.socket.isClosed()) { + this.closeAndDetach(.normal); + // onClose will call markInactive again + return; } - this.is_active = false; + + this.flags.is_active = false; const vm = this.handlers.vm; - this.handlers.markInactive(ssl, this.socket.context(), this.wrapped); + this.handlers.markInactive(); this.poll_ref.unref(vm); this.has_pending_activity.store(false, .release); } } pub fn onOpen(this: *This, socket: Socket) void { + log("onOpen {} {}", .{ this.socket.isDetached(), this.ref_count }); // update the internal socket instance to the one that was just connected + // This socket must be replaced because the previous one is a connecting socket not a uSockets socket this.socket = socket; JSC.markBinding(@src()); log("onOpen ssl: {}", .{comptime ssl}); // Add SNI support for TLS (mongodb and others requires this) if (comptime ssl) { - var ssl_ptr = this.socket.ssl(); - - if (!ssl_ptr.isInitFinished()) { - if (this.server_name) |server_name| { - const host = normalizeHost(server_name); - if (host.len > 0) { - const host__ = default_allocator.dupeZ(u8, host) catch unreachable; - defer default_allocator.free(host__); - ssl_ptr.setHostname(host__); - } - } else if (this.connection) |connection| { - if (connection == .host) { - const host = normalizeHost(connection.host.host); + if (this.socket.ssl()) |ssl_ptr| { + if (!ssl_ptr.isInitFinished()) { + if (this.server_name) |server_name| { + const host = normalizeHost(server_name); if (host.len > 0) { - const host__ = default_allocator.dupeZ(u8, host) catch unreachable; + const host__ = default_allocator.dupeZ(u8, host) catch bun.outOfMemory(); defer default_allocator.free(host__); ssl_ptr.setHostname(host__); } + } else if (this.connection) |connection| { + if (connection == .host) { + const host = normalizeHost(connection.host.host); + if (host.len > 0) { + const host__ = default_allocator.dupeZ(u8, host) catch bun.outOfMemory(); + defer default_allocator.free(host__); + ssl_ptr.setHostname(host__); + } + } } - } - if (this.protos) |protos| { - if (this.handlers.is_server) { - BoringSSL.SSL_CTX_set_alpn_select_cb(BoringSSL.SSL_get_SSL_CTX(ssl_ptr), selectALPNCallback, bun.cast(*anyopaque, this)); - } else { - _ = BoringSSL.SSL_set_alpn_protos(ssl_ptr, protos.ptr, @as(c_uint, @intCast(protos.len))); + if (this.protos) |protos| { + if (this.handlers.is_server) { + BoringSSL.SSL_CTX_set_alpn_select_cb(BoringSSL.SSL_get_SSL_CTX(ssl_ptr), selectALPNCallback, bun.cast(*anyopaque, this)); + } else { + _ = BoringSSL.SSL_set_alpn_protos(ssl_ptr, protos.ptr, @as(c_uint, @intCast(protos.len))); + } } } } } - this.detached = false; - this.socket = socket; - if (this.wrapped == .none) { - socket.ext(**anyopaque).* = bun.cast(**anyopaque, this); + if (socket.ext(**anyopaque)) |ctx| { + ctx.* = bun.cast(**anyopaque, this); + } } const handlers = this.handlers; @@ -1472,12 +1632,11 @@ fn NewSocket(comptime ssl: bool) type { const vm = handlers.vm; vm.eventLoop().enter(); defer vm.eventLoop().exit(); - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + const result = callback.call(globalObject, this_value, &[_]JSValue{ this_value, - }); + }) catch |err| globalObject.takeException(err); if (result.toError()) |err| { - this.detached = true; defer this.markInactive(); if (!this.socket.isClosed()) { log("Closing due to error", .{}); @@ -1501,10 +1660,10 @@ fn NewSocket(comptime ssl: bool) type { return this.this_value; } - pub fn onEnd(this: *This, socket: Socket) void { + pub fn onEnd(this: *This, _: Socket) void { JSC.markBinding(@src()); log("onEnd", .{}); - if (this.detached) return; + if (this.socket.isDetached()) return; const handlers = this.handlers; @@ -1519,27 +1678,23 @@ fn NewSocket(comptime ssl: bool) type { // the handlers must be kept alive for the duration of the function call // that way if we need to call the error handler, we can - var scope = handlers.enter(socket.context()); - defer scope.exit(ssl, this.wrapped); + var scope = handlers.enter(); + defer scope.exit(); const globalObject = handlers.globalObject; const this_value = this.getThisValue(globalObject); - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ - this_value, - }); - - if (result.toError()) |err_value| { - _ = handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, err_value }); - } + _ = callback.call(globalObject, this_value, &.{this_value}) catch |err| { + _ = handlers.callErrorHandler(this_value, &.{ this_value, globalObject.takeException(err) }); + }; } - pub fn onHandshake(this: *This, socket: Socket, success: i32, ssl_error: uws.us_bun_verify_error_t) void { + pub fn onHandshake(this: *This, _: Socket, success: i32, ssl_error: uws.us_bun_verify_error_t) void { log("onHandshake({d})", .{success}); JSC.markBinding(@src()); - if (this.detached) return; + if (this.socket.isDetached()) return; const authorized = if (success == 1) true else false; - this.authorized = authorized; + this.flags.authorized = authorized; const handlers = this.handlers; var callback = handlers.onHandshake; @@ -1560,8 +1715,8 @@ fn NewSocket(comptime ssl: bool) type { // the handlers must be kept alive for the duration of the function call // that way if we need to call the error handler, we can - var scope = handlers.enter(socket.context()); - defer scope.exit(ssl, this.wrapped); + var scope = handlers.enter(); + defer scope.exit(); const globalObject = handlers.globalObject; const this_value = this.getThisValue(globalObject); @@ -1570,7 +1725,7 @@ fn NewSocket(comptime ssl: bool) type { // open callback only have 1 parameters and its the socket // you should use getAuthorizationError and authorized getter to get those values in this case if (is_open) { - result = callback.callWithThis(globalObject, this_value, &[_]JSValue{this_value}); + result = callback.call(globalObject, this_value, &[_]JSValue{this_value}) catch |err| globalObject.takeException(err); // only call onOpen once for clients if (!handlers.is_server) { @@ -1597,11 +1752,11 @@ fn NewSocket(comptime ssl: bool) type { authorization_error = fallback.toErrorInstance(globalObject); } - result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + result = callback.call(globalObject, this_value, &[_]JSValue{ this_value, JSValue.jsBoolean(authorized), authorization_error, - }); + }) catch |err| globalObject.takeException(err); } if (result.toError()) |err_value| { @@ -1609,13 +1764,14 @@ fn NewSocket(comptime ssl: bool) type { } } - pub fn onClose(this: *This, socket: Socket, err: c_int, _: ?*anyopaque) void { + pub fn onClose(this: *This, _: Socket, err: c_int, _: ?*anyopaque) void { JSC.markBinding(@src()); log("onClose", .{}); - this.detached = true; + this.socket.detach(); + defer this.deref(); defer this.markInactive(); - if (this.finalizing) { + if (this.flags.finalizing) { return; } @@ -1634,29 +1790,27 @@ fn NewSocket(comptime ssl: bool) type { // the handlers must be kept alive for the duration of the function call // that way if we need to call the error handler, we can - var scope = handlers.enter(socket.context()); - defer scope.exit(ssl, this.wrapped); + var scope = handlers.enter(); + defer scope.exit(); const globalObject = handlers.globalObject; const this_value = this.getThisValue(globalObject); - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + _ = callback.call(globalObject, this_value, &[_]JSValue{ this_value, JSValue.jsNumber(@as(i32, err)), - }); - - if (result.toError()) |err_value| { - _ = handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, err_value }); - } + }) catch |e| { + _ = handlers.callErrorHandler(this_value, &.{ this_value, globalObject.takeException(e) }); + }; } - pub fn onData(this: *This, socket: Socket, data: []const u8) void { + pub fn onData(this: *This, _: Socket, data: []const u8) void { JSC.markBinding(@src()); log("onData({d})", .{data.len}); - if (this.detached) return; + if (this.socket.isDetached()) return; const handlers = this.handlers; const callback = handlers.onData; - if (callback == .zero or this.finalizing) return; + if (callback == .zero or this.flags.finalizing) return; if (handlers.vm.isShuttingDown()) { return; } @@ -1667,24 +1821,22 @@ fn NewSocket(comptime ssl: bool) type { // the handlers must be kept alive for the duration of the function call // that way if we need to call the error handler, we can - var scope = handlers.enter(socket.context()); - defer scope.exit(ssl, this.wrapped); + var scope = handlers.enter(); + defer scope.exit(); // const encoding = handlers.encoding; - const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + _ = callback.call(globalObject, this_value, &[_]JSValue{ this_value, output_value, - }); - - if (result.toError()) |err_value| { - _ = handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, err_value }); - } + }) catch |err| { + _ = handlers.callErrorHandler(this_value, &.{ this_value, globalObject.takeException(err) }); + }; } pub fn getData( _: *This, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getData()", .{}); return JSValue.jsUndefined(); } @@ -1702,8 +1854,8 @@ fn NewSocket(comptime ssl: bool) type { pub fn getListener( this: *This, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { - if (!this.handlers.is_server or this.detached) { + ) JSValue { + if (!this.handlers.is_server or this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -1714,10 +1866,10 @@ fn NewSocket(comptime ssl: bool) type { pub fn getReadyState( this: *This, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getReadyState()", .{}); - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsNumber(@as(i32, -1)); } else if (this.socket.isClosed()) { return JSValue.jsNumber(@as(i32, 0)); @@ -1733,18 +1885,18 @@ fn NewSocket(comptime ssl: bool) type { pub fn getAuthorized( this: *This, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getAuthorized()", .{}); - return JSValue.jsBoolean(this.authorized); + return JSValue.jsBoolean(this.flags.authorized); } pub fn timeout( this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); const args = callframe.arguments(1); - if (this.detached) return JSValue.jsUndefined(); + if (this.socket.isDetached()) return JSValue.jsUndefined(); if (args.len == 0) { globalObject.throw("Expected 1 argument, got 0", .{}); return .zero; @@ -1754,6 +1906,7 @@ fn NewSocket(comptime ssl: bool) type { globalObject.throw("Timeout must be a positive integer", .{}); return .zero; } + log("timeout({d})", .{t}); this.socket.setTimeout(@as(c_uint, @intCast(t))); @@ -1764,10 +1917,10 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsNull(); } @@ -1794,10 +1947,10 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsNumber(@as(i32, -1)); } @@ -1817,8 +1970,8 @@ fn NewSocket(comptime ssl: bool) type { pub fn getLocalPort( this: *This, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { - if (this.detached) { + ) JSValue { + if (this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -1828,8 +1981,8 @@ fn NewSocket(comptime ssl: bool) type { pub fn getRemoteAddress( this: *This, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { - if (this.detached) { + ) JSValue { + if (this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -1846,11 +1999,11 @@ fn NewSocket(comptime ssl: bool) type { }; const text = bun.fmt.formatIp(address, &text_buf) catch unreachable; - return ZigString.init(text).toValueGC(globalThis); + return ZigString.init(text).toJS(globalThis); } fn writeMaybeCorked(this: *This, buffer: []const u8, is_end: bool) i32 { - if (this.detached or this.socket.isShutdown() or this.socket.isClosed()) { + if (this.socket.isShutdown() or this.socket.isClosed()) { return -1; } // we don't cork yet but we might later @@ -2009,7 +2162,8 @@ fn NewSocket(comptime ssl: bool) type { }, }; } else { - globalObject.throw("Expected ArrayBufferView, a string, or a Blob", .{}); + if (!globalObject.hasException()) + globalObject.throw("Expected ArrayBufferView, a string, or a Blob", .{}); return .{ .fail = {} }; } } @@ -2018,10 +2172,9 @@ fn NewSocket(comptime ssl: bool) type { this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); - if (!this.detached) - this.socket.flush(); + this.socket.flush(); return JSValue.jsUndefined(); } @@ -2030,12 +2183,9 @@ fn NewSocket(comptime ssl: bool) type { this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); - if (!this.detached) { - this.socket.close(.failure); - } - + this.closeAndDetach(.failure); return JSValue.jsUndefined(); } @@ -2043,15 +2193,13 @@ fn NewSocket(comptime ssl: bool) type { this: *This, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); const args = callframe.arguments(1); - if (!this.detached) { - if (args.len > 0 and args.ptr[0].toBoolean()) { - this.socket.shutdownRead(); - } else { - this.socket.shutdown(); - } + if (args.len > 0 and args.ptr[0].toBoolean()) { + this.socket.shutdownRead(); + } else { + this.socket.shutdown(); } return JSValue.jsUndefined(); @@ -2061,14 +2209,14 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); const args = callframe.arguments(4); log("end({d} args)", .{args.len}); - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsNumber(@as(i32, -1)); } @@ -2085,34 +2233,25 @@ fn NewSocket(comptime ssl: bool) type { }; } - pub fn ref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn jsRef(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); - if (this.detached) return JSValue.jsUndefined(); + if (this.socket.isDetached()) return JSValue.jsUndefined(); this.poll_ref.ref(globalObject.bunVM()); return JSValue.jsUndefined(); } - pub fn unref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn jsUnref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); this.poll_ref.unref(globalObject.bunVM()); return JSValue.jsUndefined(); } - pub fn finalize(this: *This) callconv(.C) void { - log("finalize() {d}", .{@intFromPtr(this)}); - this.finalizing = true; - if (!this.detached) { - this.detached = true; - if (!this.socket.isClosed()) { - this.socket.close(.failure); - } - } - + pub fn deinit(this: *This) void { this.markInactive(); this.poll_ref.unref(JSC.VirtualMachine.get()); // need to deinit event without being attached - if (this.owned_protos) { + if (this.flags.owned_protos) { if (this.protos) |protos| { this.protos = null; default_allocator.free(protos); @@ -2128,9 +2267,24 @@ fn NewSocket(comptime ssl: bool) type { this.connection = null; connection.deinit(); } + if (this.socket_context) |socket_context| { + this.socket_context = null; + socket_context.deinit(ssl); + } + this.destroy(); } - pub fn reload(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn finalize(this: *This) void { + log("finalize() {d} {}", .{ @intFromPtr(this), this.socket_context != null }); + this.flags.finalizing = true; + if (!this.socket.isClosed()) { + this.closeAndDetach(.failure); + } + + this.deref(); + } + + pub fn reload(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const args = callframe.arguments(1); if (args.len < 1) { @@ -2138,7 +2292,7 @@ fn NewSocket(comptime ssl: bool) type { return .zero; } - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -2172,15 +2326,11 @@ fn NewSocket(comptime ssl: bool) type { this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - - const ssl_ptr = this.socket.ssl(); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); BoringSSL.SSL_set_renegotiate_mode(ssl_ptr, BoringSSL.ssl_renegotiate_never); return JSValue.jsUndefined(); } @@ -2189,11 +2339,11 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -2230,15 +2380,12 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - const ssl_ptr = this.socket.ssl(); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); BoringSSL.ERR_clear_error(); if (BoringSSL.SSL_renegotiate(ssl_ptr) != 1) { globalObject.throwValue(getSSLException(globalObject, "SSL_renegotiate error")); @@ -2250,16 +2397,12 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - - const ssl_ptr = this.socket.ssl(); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return JSValue.jsUndefined(); var ticket: [*c]const u8 = undefined; var length: usize = 0; @@ -2277,12 +2420,12 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -2323,16 +2466,12 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - - const ssl_ptr = this.socket.ssl(); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return JSValue.jsUndefined(); const size = BoringSSL.i2d_SSL_SESSION(session, null); if (size <= 0) { @@ -2351,19 +2490,15 @@ fn NewSocket(comptime ssl: bool) type { pub fn getALPNProtocol( this: *This, globalObject: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsBoolean(false); } - if (this.detached) { - return JSValue.jsBoolean(false); - } - var alpn_proto: [*c]const u8 = null; var alpn_proto_len: u32 = 0; - const ssl_ptr = this.socket.ssl(); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsBoolean(false); BoringSSL.SSL_get0_alpn_selected(ssl_ptr, &alpn_proto, &alpn_proto_len); if (alpn_proto == null or alpn_proto_len == 0) { @@ -2372,23 +2507,23 @@ fn NewSocket(comptime ssl: bool) type { const slice = alpn_proto[0..alpn_proto_len]; if (strings.eql(slice, "h2")) { - return ZigString.static("h2").toValue(globalObject); + return ZigString.static("h2").toJS(globalObject); } if (strings.eql(slice, "http/1.1")) { - return ZigString.static("http/1.1").toValue(globalObject); + return ZigString.static("http/1.1").toJS(globalObject); } - return ZigString.fromUTF8(slice).toValueGC(globalObject); + return ZigString.fromUTF8(slice).toJS(globalObject); } pub fn exportKeyingMaterial( this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { + if (this.socket.isDetached()) { return JSValue.jsUndefined(); } @@ -2422,7 +2557,7 @@ fn NewSocket(comptime ssl: bool) type { defer label.deinit(); const label_slice = label.slice(); - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); if (args.len > 2) { const context_arg = args.ptr[2]; @@ -2471,22 +2606,19 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsNull(); } - if (this.detached) { - return JSValue.jsNull(); - } - // only available for clients if (this.handlers.is_server) { return JSValue.jsNull(); } var result = JSValue.createEmptyObject(globalObject, 3); - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsNull(); + // TODO: investigate better option or compatible way to get the key // this implementation follows nodejs but for BoringSSL SSL_get_server_tmp_key will always return 0 // wich will result in a empty object @@ -2504,7 +2636,7 @@ fn NewSocket(comptime ssl: bool) type { switch (kid) { BoringSSL.EVP_PKEY_DH => { - result.put(globalObject, ZigString.static("type"), ZigString.static("DH").toValue(globalObject)); + result.put(globalObject, ZigString.static("type"), ZigString.static("DH").toJS(globalObject)); result.put(globalObject, ZigString.static("size"), JSValue.jsNumber(bits)); }, @@ -2527,8 +2659,8 @@ fn NewSocket(comptime ssl: bool) type { curve_name = ""; } } - result.put(globalObject, ZigString.static("type"), ZigString.static("ECDH").toValue(globalObject)); - result.put(globalObject, ZigString.static("name"), ZigString.fromUTF8(curve_name).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("type"), ZigString.static("ECDH").toJS(globalObject)); + result.put(globalObject, ZigString.static("name"), ZigString.fromUTF8(curve_name).toJS(globalObject)); result.put(globalObject, ZigString.static("size"), JSValue.jsNumber(bits)); }, else => {}, @@ -2540,18 +2672,15 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const cipher = BoringSSL.SSL_get_current_cipher(ssl_ptr); var result = JSValue.createEmptyObject(globalObject, 3); - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); - const cipher = BoringSSL.SSL_get_current_cipher(ssl_ptr); if (cipher == null) { result.put(globalObject, ZigString.static("name"), JSValue.jsNull()); result.put(globalObject, ZigString.static("standardName"), JSValue.jsNull()); @@ -2563,21 +2692,21 @@ fn NewSocket(comptime ssl: bool) type { if (name == null) { result.put(globalObject, ZigString.static("name"), JSValue.jsNull()); } else { - result.put(globalObject, ZigString.static("name"), ZigString.fromUTF8(name[0..bun.len(name)]).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("name"), ZigString.fromUTF8(name[0..bun.len(name)]).toJS(globalObject)); } const standard_name = BoringSSL.SSL_CIPHER_standard_name(cipher); if (standard_name == null) { result.put(globalObject, ZigString.static("standardName"), JSValue.jsNull()); } else { - result.put(globalObject, ZigString.static("standardName"), ZigString.fromUTF8(standard_name[0..bun.len(standard_name)]).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("standardName"), ZigString.fromUTF8(standard_name[0..bun.len(standard_name)]).toJS(globalObject)); } const version = BoringSSL.SSL_CIPHER_get_version(cipher); if (version == null) { result.put(globalObject, ZigString.static("version"), JSValue.jsNull()); } else { - result.put(globalObject, ZigString.static("version"), ZigString.fromUTF8(version[0..bun.len(version)]).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("version"), ZigString.fromUTF8(version[0..bun.len(version)]).toJS(globalObject)); } return result; @@ -2587,16 +2716,12 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); // We cannot just pass nullptr to SSL_get_peer_finished() // because it would further be propagated to memcpy(), // where the standard requirements as described in ISO/IEC 9899:2011 @@ -2619,16 +2744,12 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); // We cannot just pass nullptr to SSL_get_finished() // because it would further be propagated to memcpy(), // where the standard requirements as described in ISO/IEC 9899:2011 @@ -2651,16 +2772,13 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); if (comptime ssl == false) { return JSValue.jsNull(); } - if (this.detached) { - return JSValue.jsNull(); - } - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsNull(); const nsig = BoringSSL.SSL_get_shared_sigalgs(ssl_ptr, 0, null, null, null, null, null); @@ -2719,20 +2837,20 @@ fn NewSocket(comptime ssl: bool) type { if (hash_str != null) { const hash_str_len = bun.len(hash_str); const hash_slice = hash_str[0..hash_str_len]; - const buffer = bun.default_allocator.alloc(u8, sig_with_md.len + hash_str_len + 1) catch unreachable; + const buffer = bun.default_allocator.alloc(u8, sig_with_md.len + hash_str_len + 1) catch bun.outOfMemory(); defer bun.default_allocator.free(buffer); bun.copy(u8, buffer, sig_with_md); buffer[sig_with_md.len] = '+'; bun.copy(u8, buffer[sig_with_md.len + 1 ..], hash_slice); - array.putIndex(globalObject, @as(u32, @intCast(i)), JSC.ZigString.fromUTF8(buffer).toValueGC(globalObject)); + array.putIndex(globalObject, @as(u32, @intCast(i)), JSC.ZigString.fromUTF8(buffer).toJS(globalObject)); } else { - const buffer = bun.default_allocator.alloc(u8, sig_with_md.len + 6) catch unreachable; + const buffer = bun.default_allocator.alloc(u8, sig_with_md.len + 6) catch bun.outOfMemory(); defer bun.default_allocator.free(buffer); bun.copy(u8, buffer, sig_with_md); bun.copy(u8, buffer[sig_with_md.len..], "+UNDEF"); - array.putIndex(globalObject, @as(u32, @intCast(i)), JSC.ZigString.fromUTF8(buffer).toValueGC(globalObject)); + array.putIndex(globalObject, @as(u32, @intCast(i)), JSC.ZigString.fromUTF8(buffer).toJS(globalObject)); } } return array; @@ -2742,39 +2860,31 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); if (comptime ssl == false) { return JSValue.jsNull(); } - if (this.detached) { - return JSValue.jsNull(); - } - - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsNull(); const version = BoringSSL.SSL_get_version(ssl_ptr); if (version == null) return JSValue.jsNull(); const version_len = bun.len(version); if (version_len == 0) return JSValue.jsNull(); const slice = version[0..version_len]; - return ZigString.fromUTF8(slice).toValueGC(globalObject); + return ZigString.fromUTF8(slice).toJS(globalObject); } pub fn setMaxSendFragment( this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); if (comptime ssl == false) { return JSValue.jsBoolean(false); } - if (this.detached) { - return JSValue.jsBoolean(false); - } - const args = callframe.arguments(1); if (args.len < 1) { @@ -2797,23 +2907,19 @@ fn NewSocket(comptime ssl: bool) type { return .zero; } - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsBoolean(false); return JSValue.jsBoolean(BoringSSL.SSL_set_max_send_fragment(ssl_ptr, @as(usize, @intCast(size))) == 1); } pub fn getPeerCertificate( this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); if (comptime ssl == false) { return JSValue.jsUndefined(); } - if (this.detached) { - return JSValue.jsUndefined(); - } - const args = callframe.arguments(1); var abbreviated: bool = true; if (args.len > 0) { @@ -2825,7 +2931,7 @@ fn NewSocket(comptime ssl: bool) type { abbreviated = arg.toBoolean(); } - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); if (abbreviated) { if (this.handlers.is_server) { @@ -2859,16 +2965,11 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } - - if (this.detached) { - return JSValue.jsUndefined(); - } - - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); const cert = BoringSSL.SSL_get_certificate(ssl_ptr); if (cert) |x509| { @@ -2881,7 +2982,7 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } @@ -2891,13 +2992,13 @@ fn NewSocket(comptime ssl: bool) type { if (servername == null) { return JSValue.jsUndefined(); } - return ZigString.fromUTF8(servername[0..bun.len(servername)]).toValueGC(globalObject); + return ZigString.fromUTF8(servername[0..bun.len(servername)]).toJS(globalObject); } pub fn setServername( this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (comptime ssl == false) { return JSValue.jsUndefined(); } @@ -2919,7 +3020,7 @@ fn NewSocket(comptime ssl: bool) type { return .zero; } - const slice = server_name.getZigString(globalObject).toOwnedSlice(bun.default_allocator) catch unreachable; + const slice = server_name.getZigString(globalObject).toOwnedSlice(bun.default_allocator) catch bun.outOfMemory(); if (this.server_name) |old| { this.server_name = slice; default_allocator.free(old); @@ -2927,21 +3028,16 @@ fn NewSocket(comptime ssl: bool) type { this.server_name = slice; } - if (this.detached) { - // will be attached onOpen - return JSValue.jsUndefined(); - } - const host = normalizeHost(@as([]const u8, slice)); if (host.len > 0) { - var ssl_ptr = this.socket.ssl(); + var ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); if (ssl_ptr.isInitFinished()) { // match node.js exceptions globalObject.throw("Already started.", .{}); return .zero; } - const host__ = default_allocator.dupeZ(u8, host) catch unreachable; + const host__ = default_allocator.dupeZ(u8, host) catch bun.outOfMemory(); defer default_allocator.free(host__); ssl_ptr.setHostname(host__); } @@ -2956,16 +3052,16 @@ fn NewSocket(comptime ssl: bool) type { this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { JSC.markBinding(@src()); + const this_js = callframe.this(); + if (comptime ssl) { return JSValue.jsUndefined(); } - - if (this.detached) { + if (this.socket.isDetached() or this.socket.isNamedPipe()) { return JSValue.jsUndefined(); } - const args = callframe.arguments(1); if (args.len < 1) { @@ -2974,6 +3070,7 @@ fn NewSocket(comptime ssl: bool) type { } var exception: JSC.C.JSValueRef = null; + var success = false; const opts = args.ptr[0]; if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { @@ -2985,28 +3082,57 @@ fn NewSocket(comptime ssl: bool) type { globalObject.throw("Expected \"socket\" option", .{}); return .zero; }; + if (globalObject.hasException()) { + return .zero; + } + + const handlers = Handlers.fromJS(globalObject, socket_obj, &exception) orelse { + if (!globalObject.hasException() and exception != null) { + globalObject.throwValue(exception.?.value()); + } - var handlers = Handlers.fromJS(globalObject, socket_obj, &exception) orelse { - globalObject.throwValue(exception.?.value()); return .zero; }; - var ssl_opts: ?JSC.API.ServerConfig.SSLConfig = null; + if (!globalObject.hasException() and exception != null) { + globalObject.throwValue(exception.?.value()); + } - if (opts.getTruthy(globalObject, "tls")) |tls| { - if (tls.isBoolean()) { - if (tls.toBoolean()) { - ssl_opts = JSC.API.ServerConfig.SSLConfig.zero; - } + if (globalObject.hasException()) { + return .zero; + } + + var ssl_opts: ?JSC.API.ServerConfig.SSLConfig = null; + defer { + if (!success) { + if (ssl_opts) |*ssl_config| { + ssl_config.deinit(); + } + } + } + + if (opts.getTruthy(globalObject, "tls")) |tls| { + if (tls.isBoolean()) { + if (tls.toBoolean()) { + ssl_opts = JSC.API.ServerConfig.SSLConfig.zero; + } } else { if (JSC.API.ServerConfig.SSLConfig.inJS(JSC.VirtualMachine.get(), globalObject, tls, &exception)) |ssl_config| { ssl_opts = ssl_config; - } else if (exception != null) { - return .zero; } } } + if (exception != null) { + if (!globalObject.hasException()) { + globalObject.throwValue(exception.?.value()); + } + } + + if (globalObject.hasException()) { + return .zero; + } + if (ssl_opts == null) { globalObject.throw("Expected \"tls\" option", .{}); return .zero; @@ -3017,8 +3143,12 @@ fn NewSocket(comptime ssl: bool) type { default_data = default_data_value; default_data.ensureStillAlive(); } + if (globalObject.hasException()) { + return .zero; + } var socket_config = ssl_opts.?; + ssl_opts = null; defer socket_config.deinit(); const options = socket_config.asUSockets(); @@ -3028,30 +3158,29 @@ fn NewSocket(comptime ssl: bool) type { const ext_size = @sizeOf(WrappedSocket); const is_server = this.handlers.is_server; - var tls = handlers.vm.allocator.create(TLSSocket) catch bun.outOfMemory(); - var handlers_ptr = handlers.vm.allocator.create(Handlers) catch bun.outOfMemory(); + + var handlers_ptr = bun.default_allocator.create(Handlers) catch bun.outOfMemory(); handlers_ptr.* = handlers; handlers_ptr.is_server = is_server; handlers_ptr.protect(); - - tls.* = .{ + var tls = TLSSocket.new(.{ .handlers = handlers_ptr, .this_value = .zero, - .socket = undefined, + .socket = TLSSocket.Socket.detached, .connection = if (this.connection) |c| c.clone() else null, .wrapped = .tls, - .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p[0..protos_len]) catch unreachable) else null, - .server_name = if (socket_config.server_name) |server_name| (bun.default_allocator.dupe(u8, server_name[0..bun.len(server_name)]) catch unreachable) else null, - }; - - const tls_js_value = tls.getThisValue(globalObject); - TLSSocket.dataSetCached(tls_js_value, globalObject, default_data); + .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p[0..protos_len]) catch bun.outOfMemory()) else null, + .server_name = if (socket_config.server_name) |server_name| (bun.default_allocator.dupe(u8, server_name[0..bun.len(server_name)]) catch bun.outOfMemory()) else null, + .socket_context = null, // only set after the wrapTLS + .flags = .{ + .is_active = false, + }, + }); const TCPHandler = NewWrappedHandler(false); // reconfigure context to use the new wrapper handlers Socket.unsafeConfigure(this.socket.context().?, true, true, WrappedSocket, TCPHandler); - const old_context = this.socket.context(); const TLSHandler = NewWrappedHandler(true); const new_socket = this.socket.wrapTLS( options, @@ -3060,18 +3189,62 @@ fn NewSocket(comptime ssl: bool) type { WrappedSocket, TLSHandler, ) orelse { + const err = BoringSSL.ERR_get_error(); + defer { + if (err != 0) { + BoringSSL.ERR_clear_error(); + } + } + tls.wrapped = .none; + + // Reset config to TCP + uws.NewSocketHandler(false).configure( + this.socket.context().?, + true, + *TCPSocket, + struct { + pub const onOpen = NewSocket(false).onOpen; + pub const onClose = NewSocket(false).onClose; + pub const onData = NewSocket(false).onData; + pub const onWritable = NewSocket(false).onWritable; + pub const onTimeout = NewSocket(false).onTimeout; + pub const onConnectError = NewSocket(false).onConnectError; + pub const onEnd = NewSocket(false).onEnd; + pub const onHandshake = NewSocket(false).onHandshake; + }, + ); + + tls.deref(); + handlers_ptr.unprotect(); - handlers.vm.allocator.destroy(handlers_ptr); - bun.default_allocator.destroy(tls); + bun.default_allocator.destroy(handlers_ptr); + + // If BoringSSL gave us an error code, let's use it. + if (err != 0 and !globalObject.hasException()) { + globalObject.throwValue(BoringSSL.ERR_toJS(globalObject, err)); + } + + // If BoringSSL did not give us an error code, let's throw a generic error. + if (!globalObject.hasException()) { + globalObject.throw("Failed to upgrade socket from TCP -> TLS. Is the TLS config correct?", .{}); + } + return JSValue.jsUndefined(); }; + // Do not create the JS Wrapper object until _after_ we've validated the TLS config. + // Otherwise, JSC will GC it and the lifetime gets very complicated. + const tls_js_value = tls.getThisValue(globalObject); + TLSSocket.dataSetCached(tls_js_value, globalObject, default_data); + tls.socket = new_socket; + tls.socket_context = new_socket.context(); // owns the new tls context that have a ref from the old one + tls.ref(); + const vm = handlers.vm; - var raw = handlers.vm.allocator.create(TLSSocket) catch bun.outOfMemory(); - var raw_handlers_ptr = handlers.vm.allocator.create(Handlers) catch bun.outOfMemory(); + var raw_handlers_ptr = bun.default_allocator.create(Handlers) catch bun.outOfMemory(); raw_handlers_ptr.* = .{ - .vm = globalObject.bunVM(), + .vm = vm, .globalObject = globalObject, .onOpen = this.handlers.onOpen, .onClose = this.handlers.onClose, @@ -3086,21 +3259,25 @@ fn NewSocket(comptime ssl: bool) type { .is_server = is_server, }; - raw.* = .{ + raw_handlers_ptr.protect(); + + var raw = TLSSocket.new(.{ .handlers = raw_handlers_ptr, .this_value = .zero, .socket = new_socket, .connection = if (this.connection) |c| c.clone() else null, .wrapped = .tcp, .protos = null, - }; - raw_handlers_ptr.protect(); + .socket_context = null, // raw socket will dont own the context + }); + raw.ref(); const raw_js_value = raw.getThisValue(globalObject); - if (JSSocketType(ssl).dataGetCached(this.getThisValue(globalObject))) |raw_default_data| { + if (JSSocketType(ssl).dataGetCached(this_js)) |raw_default_data| { raw_default_data.ensureStillAlive(); TLSSocket.dataSetCached(raw_js_value, globalObject, raw_default_data); } + // marks both as active raw.markActive(); // this will keep tls alive until socket.open() is called to start TLS certificate and the handshake process @@ -3111,26 +3288,33 @@ fn NewSocket(comptime ssl: bool) type { tls.poll_ref.ref(this.handlers.vm); // mark both instances on socket data - new_socket.ext(WrappedSocket).* = .{ .tcp = raw, .tls = tls }; - - // start TLS handshake after we set ext - new_socket.startTLS(!this.handlers.is_server); + if (new_socket.ext(WrappedSocket)) |ctx| { + ctx.* = .{ .tcp = raw, .tls = tls }; + } - //detach and invalidate the old instance - this.detached = true; - if (this.is_active) { - const vm = this.handlers.vm; - this.is_active = false; - // will free handlers and the old_context when hits 0 active connections + if (this.flags.is_active) { + this.poll_ref.disable(); + this.flags.is_active = false; + // will free handlers when hits 0 active connections // the connection can be upgraded inside a handler call so we need to garantee that it will be still alive - this.handlers.markInactive(ssl, old_context, this.wrapped); - this.poll_ref.unref(vm); + this.handlers.markInactive(); + this.has_pending_activity.store(false, .release); } const array = JSC.JSValue.createEmptyArray(globalObject, 2); array.putIndex(globalObject, 0, raw_js_value); array.putIndex(globalObject, 1, tls_js_value); + + defer this.deref(); + + // detach and invalidate the old instance + this.socket.detach(); + + // start TLS handshake after we set extension on the socket + new_socket.startTLS(!is_server); + + success = true; return array; } }; @@ -3252,7 +3436,535 @@ pub fn NewWrappedHandler(comptime tls: bool) type { } }; } -pub fn jsAddServerName(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + +pub const DuplexUpgradeContext = struct { + upgrade: uws.UpgradedDuplex, + // We only us a tls and not a raw socket when upgrading a Duplex, Duplex dont support socketpairs + tls: ?*TLSSocket, + // task used to deinit the context in the next tick, vm is used to enqueue the task + vm: *JSC.VirtualMachine, + task: JSC.AnyTask, + task_event: EventState = .StartTLS, + ssl_config: ?JSC.API.ServerConfig.SSLConfig, + is_open: bool = false, + pub const EventState = enum(u8) { + StartTLS, + Close, + }; + + usingnamespace bun.New(DuplexUpgradeContext); + + fn onOpen(this: *DuplexUpgradeContext) void { + this.is_open = true; + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + if (this.tls) |tls| { + tls.onOpen(socket); + } + } + + fn onData(this: *DuplexUpgradeContext, decoded_data: []const u8) void { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + if (this.tls) |tls| { + tls.onData(socket, decoded_data); + } + } + + fn onHandshake(this: *DuplexUpgradeContext, success: bool, ssl_error: uws.us_bun_verify_error_t) void { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + if (this.tls) |tls| { + tls.onHandshake(socket, @intFromBool(success), ssl_error); + } + } + + fn onEnd(this: *DuplexUpgradeContext) void { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + if (this.tls) |tls| { + tls.onEnd(socket); + } + } + + fn onWritable(this: *DuplexUpgradeContext) void { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + if (this.tls) |tls| { + tls.onWritable(socket); + } + } + + fn onError(this: *DuplexUpgradeContext, err_value: JSC.JSValue) void { + if (this.is_open) { + if (this.tls) |tls| { + tls.handleError(err_value); + } + } else { + if (this.tls) |tls| { + tls.handleConnectError(@intFromEnum(bun.C.SystemErrno.ECONNREFUSED)); + } + } + } + + fn onTimeout(this: *DuplexUpgradeContext) void { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + if (this.tls) |tls| { + tls.onTimeout(socket); + } + } + + fn onClose(this: *DuplexUpgradeContext) void { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + if (this.tls) |tls| { + tls.onClose(socket, 0, null); + } + + this.deinitInNextTick(); + } + + fn runEvent(this: *DuplexUpgradeContext) void { + switch (this.task_event) { + .StartTLS => { + if (this.ssl_config) |config| { + this.upgrade.startTLS(config, true) catch |err| { + switch (err) { + error.OutOfMemory => { + bun.outOfMemory(); + }, + else => { + const errno = @intFromEnum(bun.C.SystemErrno.ECONNREFUSED); + if (this.tls) |tls| { + const socket = TLSSocket.Socket.fromDuplex(&this.upgrade); + + tls.handleConnectError(errno); + tls.onClose(socket, errno, null); + } + }, + } + }; + this.ssl_config.?.deinit(); + this.ssl_config = null; + } + }, + .Close => { + this.upgrade.close(); + }, + } + } + + fn deinitInNextTick(this: *DuplexUpgradeContext) void { + this.task_event = .Close; + this.vm.enqueueTask(JSC.Task.init(&this.task)); + } + + fn startTLS(this: *DuplexUpgradeContext) void { + this.task_event = .StartTLS; + this.vm.enqueueTask(JSC.Task.init(&this.task)); + } + + fn deinit(this: *DuplexUpgradeContext) void { + if (this.tls) |tls| { + this.tls = null; + tls.deref(); + } + this.upgrade.deinit(); + this.destroy(); + } +}; + +pub const WindowsNamedPipeListeningContext = if (Environment.isWindows) struct { + uvPipe: uv.Pipe = std.mem.zeroes(uv.Pipe), + listener: ?*Listener, + globalThis: *JSC.JSGlobalObject, + vm: *JSC.VirtualMachine, + ctx: ?*BoringSSL.SSL_CTX = null, // server reuses the same ctx + usingnamespace bun.New(WindowsNamedPipeListeningContext); + + fn onClientConnect(this: *WindowsNamedPipeListeningContext, status: uv.ReturnCode) void { + if (status != uv.ReturnCode.zero or this.vm.isShuttingDown() or this.listener == null) { + // connection dropped or vm is shutting down or we are deiniting/closing + return; + } + const listener = this.listener.?; + const socket: WindowsNamedPipeContext.SocketType = brk: { + if (this.ctx) |_| { + break :brk .{ .tls = Listener.onNamePipeCreated(true, listener) }; + } else { + break :brk .{ .tcp = Listener.onNamePipeCreated(false, listener) }; + } + }; + + const client = WindowsNamedPipeContext.create(this.globalThis, socket); + + const result = client.named_pipe.getAcceptedBy(&this.uvPipe, this.ctx); + if (result == .err) { + // connection dropped + client.deinit(); + } + } + fn onPipeClosed(pipe: *uv.Pipe) callconv(.C) void { + const this: *WindowsNamedPipeListeningContext = @ptrCast(@alignCast(pipe.data)); + this.deinit(); + } + + pub fn closePipeAndDeinit(this: *WindowsNamedPipeListeningContext) void { + this.listener = null; + this.uvPipe.data = this; + this.uvPipe.close(onPipeClosed); + } + + pub fn listen(globalThis: *JSC.JSGlobalObject, path: []const u8, backlog: i32, ssl_config: ?JSC.API.ServerConfig.SSLConfig, listener: *Listener) !*WindowsNamedPipeListeningContext { + const this = WindowsNamedPipeListeningContext.new(.{ + .globalThis = globalThis, + .vm = globalThis.bunVM(), + .listener = listener, + }); + + if (ssl_config) |ssl_options| { + BoringSSL.load(); + + const ctx_opts: uws.us_bun_socket_context_options_t = JSC.API.ServerConfig.SSLConfig.asUSockets(ssl_options); + // Create SSL context using uSockets to match behavior of node.js + const ctx = uws.create_ssl_context_from_bun_options(ctx_opts) orelse return error.InvalidOptions; // invalid options + errdefer BoringSSL.SSL_CTX_free(ctx); + this.ctx = ctx; + } + + const initResult = this.uvPipe.init(this.vm.uvLoop(), false); + if (initResult == .err) { + return error.FailedToInitPipe; + } + if (path[path.len - 1] == 0) { + // is already null terminated + const slice_z = path[0 .. path.len - 1 :0]; + this.uvPipe.listenNamedPipe(slice_z, backlog, this, onClientConnect).unwrap() catch return error.FailedToBindPipe; + } else { + var path_buf: bun.PathBuffer = undefined; + // we need to null terminate the path + const len = @min(path.len, path_buf.len - 1); + + @memcpy(path_buf[0..len], path[0..len]); + path_buf[len] = 0; + const slice_z = path_buf[0..len :0]; + this.uvPipe.listenNamedPipe(slice_z, backlog, this, onClientConnect).unwrap() catch return error.FailedToBindPipe; + } + //TODO: add readableAll and writableAll support if someone needs it + // if(uv.uv_pipe_chmod(&this.uvPipe, uv.UV_WRITABLE | uv.UV_READABLE) != 0) { + // this.closePipeAndDeinit(); + // return error.FailedChmodPipe; + //} + + return this; + } + + fn runEvent(this: *WindowsNamedPipeListeningContext) void { + switch (this.task_event) { + .deinit => { + this.deinit(); + }, + .none => @panic("Invalid event state"), + } + } + + fn deinitInNextTick(this: *WindowsNamedPipeListeningContext) void { + bun.assert(this.task_event != .deinit); + this.task_event = .deinit; + this.vm.enqueueTask(JSC.Task.init(&this.task)); + } + + fn deinit(this: *WindowsNamedPipeListeningContext) void { + this.listener = null; + if (this.ctx) |ctx| { + this.ctx = null; + BoringSSL.SSL_CTX_free(ctx); + } + this.destroy(); + } +} else void; +pub const WindowsNamedPipeContext = if (Environment.isWindows) struct { + named_pipe: uws.WindowsNamedPipe, + socket: SocketType, + + // task used to deinit the context in the next tick, vm is used to enqueue the task + vm: *JSC.VirtualMachine, + globalThis: *JSC.JSGlobalObject, + task: JSC.AnyTask, + task_event: EventState = .none, + is_open: bool = false, + pub const EventState = enum(u8) { + deinit, + none, + }; + + pub const SocketType = union(enum) { + tls: *TLSSocket, + tcp: *TCPSocket, + none: void, + }; + + usingnamespace bun.New(WindowsNamedPipeContext); + const log = Output.scoped(.WindowsNamedPipeContext, false); + + fn onOpen(this: *WindowsNamedPipeContext) void { + this.is_open = true; + switch (this.socket) { + .tls => |tls| { + const socket = TLSSocket.Socket.fromNamedPipe(&this.named_pipe); + tls.onOpen(socket); + }, + .tcp => |tcp| { + const socket = TCPSocket.Socket.fromNamedPipe(&this.named_pipe); + tcp.onOpen(socket); + }, + .none => {}, + } + } + + fn onData(this: *WindowsNamedPipeContext, decoded_data: []const u8) void { + switch (this.socket) { + .tls => |tls| { + const socket = TLSSocket.Socket.fromNamedPipe(&this.named_pipe); + tls.onData(socket, decoded_data); + }, + .tcp => |tcp| { + const socket = TCPSocket.Socket.fromNamedPipe(&this.named_pipe); + tcp.onData(socket, decoded_data); + }, + .none => {}, + } + } + + fn onHandshake(this: *WindowsNamedPipeContext, success: bool, ssl_error: uws.us_bun_verify_error_t) void { + switch (this.socket) { + .tls => |tls| { + const socket = TLSSocket.Socket.fromNamedPipe(&this.named_pipe); + tls.onHandshake(socket, @intFromBool(success), ssl_error); + }, + .tcp => |tcp| { + const socket = TCPSocket.Socket.fromNamedPipe(&this.named_pipe); + tcp.onHandshake(socket, @intFromBool(success), ssl_error); + }, + .none => {}, + } + } + + fn onEnd(this: *WindowsNamedPipeContext) void { + switch (this.socket) { + .tls => |tls| { + const socket = TLSSocket.Socket.fromNamedPipe(&this.named_pipe); + tls.onEnd(socket); + }, + .tcp => |tcp| { + const socket = TCPSocket.Socket.fromNamedPipe(&this.named_pipe); + tcp.onEnd(socket); + }, + .none => {}, + } + } + + fn onWritable(this: *WindowsNamedPipeContext) void { + switch (this.socket) { + .tls => |tls| { + const socket = TLSSocket.Socket.fromNamedPipe(&this.named_pipe); + tls.onWritable(socket); + }, + .tcp => |tcp| { + const socket = TCPSocket.Socket.fromNamedPipe(&this.named_pipe); + tcp.onWritable(socket); + }, + .none => {}, + } + } + + fn onError(this: *WindowsNamedPipeContext, err: bun.sys.Error) void { + if (this.is_open) { + if (this.vm.isShuttingDown()) { + // dont touch global just wait to close vm is shutting down + return; + } + + switch (this.socket) { + .tls => |tls| { + tls.handleError(err.toJSC(this.globalThis)); + }, + .tcp => |tcp| { + tcp.handleError(err.toJSC(this.globalThis)); + }, + else => {}, + } + } else { + switch (this.socket) { + .tls => |tls| { + tls.handleConnectError(err.errno); + }, + .tcp => |tcp| { + tcp.handleConnectError(err.errno); + }, + else => {}, + } + } + } + + fn onTimeout(this: *WindowsNamedPipeContext) void { + switch (this.socket) { + .tls => |tls| { + const socket = TLSSocket.Socket.fromNamedPipe(&this.named_pipe); + tls.onTimeout(socket); + }, + .tcp => |tcp| { + const socket = TCPSocket.Socket.fromNamedPipe(&this.named_pipe); + tcp.onTimeout(socket); + }, + .none => {}, + } + } + + fn onClose(this: *WindowsNamedPipeContext) void { + const socket = this.socket; + this.socket = .none; + switch (socket) { + .tls => |tls| { + tls.onClose(TLSSocket.Socket.fromNamedPipe(&this.named_pipe), 0, null); + tls.deref(); + }, + .tcp => |tcp| { + tcp.onClose(TCPSocket.Socket.fromNamedPipe(&this.named_pipe), 0, null); + tcp.deref(); + }, + .none => {}, + } + + this.deinitInNextTick(); + } + + fn runEvent(this: *WindowsNamedPipeContext) void { + switch (this.task_event) { + .deinit => { + this.deinit(); + }, + .none => @panic("Invalid event state"), + } + } + + fn deinitInNextTick(this: *WindowsNamedPipeContext) void { + bun.assert(this.task_event != .deinit); + this.task_event = .deinit; + this.vm.enqueueTask(JSC.Task.init(&this.task)); + } + + fn create(globalThis: *JSC.JSGlobalObject, socket: SocketType) *WindowsNamedPipeContext { + const vm = globalThis.bunVM(); + const this = WindowsNamedPipeContext.new(.{ + .vm = vm, + .globalThis = globalThis, + .task = undefined, + .socket = socket, + .named_pipe = undefined, + }); + + // named_pipe owns the pipe (PipeWriter owns the pipe and will close and deinit it) + this.named_pipe = uws.WindowsNamedPipe.from(bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory(), .{ + .ctx = this, + .onOpen = @ptrCast(&WindowsNamedPipeContext.onOpen), + .onData = @ptrCast(&WindowsNamedPipeContext.onData), + .onHandshake = @ptrCast(&WindowsNamedPipeContext.onHandshake), + .onEnd = @ptrCast(&WindowsNamedPipeContext.onEnd), + .onWritable = @ptrCast(&WindowsNamedPipeContext.onWritable), + .onError = @ptrCast(&WindowsNamedPipeContext.onError), + .onTimeout = @ptrCast(&WindowsNamedPipeContext.onTimeout), + .onClose = @ptrCast(&WindowsNamedPipeContext.onClose), + }, vm); + this.task = JSC.AnyTask.New(WindowsNamedPipeContext, WindowsNamedPipeContext.runEvent).init(this); + + switch (socket) { + .tls => |tls| { + tls.ref(); + }, + .tcp => |tcp| { + tcp.ref(); + }, + .none => {}, + } + + return this; + } + + pub fn open(globalThis: *JSC.JSGlobalObject, fd: bun.FileDescriptor, ssl_config: ?JSC.API.ServerConfig.SSLConfig, socket: SocketType) !*uws.WindowsNamedPipe { + // TODO: reuse the same context for multiple connections when possibles + + const this = WindowsNamedPipeContext.create(globalThis, socket); + + errdefer { + switch (socket) { + .tls => |tls| { + tls.handleConnectError(@intFromEnum(bun.C.SystemErrno.ENOENT)); + }, + .tcp => |tcp| { + tcp.handleConnectError(@intFromEnum(bun.C.SystemErrno.ENOENT)); + }, + .none => {}, + } + this.deinitInNextTick(); + } + try this.named_pipe.open(fd, ssl_config).unwrap(); + return &this.named_pipe; + } + + pub fn connect(globalThis: *JSC.JSGlobalObject, path: []const u8, ssl_config: ?JSC.API.ServerConfig.SSLConfig, socket: SocketType) !*uws.WindowsNamedPipe { + // TODO: reuse the same context for multiple connections when possibles + + const this = WindowsNamedPipeContext.create(globalThis, socket); + errdefer { + switch (socket) { + .tls => |tls| { + tls.handleConnectError(@intFromEnum(bun.C.SystemErrno.ENOENT)); + }, + .tcp => |tcp| { + tcp.handleConnectError(@intFromEnum(bun.C.SystemErrno.ENOENT)); + }, + .none => {}, + } + this.deinitInNextTick(); + } + + if (path[path.len - 1] == 0) { + // is already null terminated + const slice_z = path[0 .. path.len - 1 :0]; + try this.named_pipe.connect(slice_z, ssl_config).unwrap(); + } else { + var path_buf: bun.PathBuffer = undefined; + // we need to null terminate the path + const len = @min(path.len, path_buf.len - 1); + + @memcpy(path_buf[0..len], path[0..len]); + path_buf[len] = 0; + const slice_z = path_buf[0..len :0]; + try this.named_pipe.connect(slice_z, ssl_config).unwrap(); + } + return &this.named_pipe; + } + fn deinit(this: *WindowsNamedPipeContext) void { + log("deinit", .{}); + const socket = this.socket; + this.socket = .none; + switch (socket) { + .tls => |tls| { + tls.deref(); + }, + .tcp => |tcp| { + tcp.deref(); + }, + else => {}, + } + + this.named_pipe.deinit(); + this.destroy(); + } +} else void; + +pub fn jsAddServerName(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { JSC.markBinding(@src()); const arguments = callframe.arguments(3); @@ -3268,8 +3980,144 @@ pub fn jsAddServerName(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) c return .zero; } +pub fn jsUpgradeDuplexToTLS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + JSC.markBinding(@src()); + + const args = callframe.arguments(2); + if (args.len < 2) { + globalObject.throw("Expected 2 arguments", .{}); + return .zero; + } + const duplex = args.ptr[0]; + // TODO: do better type checking + if (duplex.isEmptyOrUndefinedOrNull()) { + globalObject.throw("Expected a Duplex instance", .{}); + return .zero; + } + + var exception: JSC.C.JSValueRef = null; + + const opts = args.ptr[1]; + if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { + globalObject.throw("Expected options object", .{}); + return .zero; + } + + const socket_obj = opts.get(globalObject, "socket") orelse { + globalObject.throw("Expected \"socket\" option", .{}); + return .zero; + }; + + var handlers = Handlers.fromJS(globalObject, socket_obj, &exception) orelse { + globalObject.throwValue(exception.?.value()); + return .zero; + }; + + var ssl_opts: ?JSC.API.ServerConfig.SSLConfig = null; + if (opts.getTruthy(globalObject, "tls")) |tls| { + if (tls.isBoolean()) { + if (tls.toBoolean()) { + ssl_opts = JSC.API.ServerConfig.SSLConfig.zero; + } + } else { + if (JSC.API.ServerConfig.SSLConfig.inJS(JSC.VirtualMachine.get(), globalObject, tls, &exception)) |ssl_config| { + ssl_opts = ssl_config; + } else if (exception != null) { + return .zero; + } + } + } + if (ssl_opts == null) { + globalObject.throw("Expected \"tls\" option", .{}); + return .zero; + } + + var default_data = JSValue.zero; + if (opts.fastGet(globalObject, .data)) |default_data_value| { + default_data = default_data_value; + default_data.ensureStillAlive(); + } + + const socket_config = ssl_opts.?; + + const protos = socket_config.protos; + const protos_len = socket_config.protos_len; + + const is_server = false; // A duplex socket is always handled as a client + + var handlers_ptr = handlers.vm.allocator.create(Handlers) catch bun.outOfMemory(); + handlers_ptr.* = handlers; + handlers_ptr.is_server = is_server; + handlers_ptr.protect(); + var tls = TLSSocket.new(.{ + .handlers = handlers_ptr, + .this_value = .zero, + .socket = TLSSocket.Socket.detached, + .connection = null, + .wrapped = .tls, + .protos = if (protos) |p| (bun.default_allocator.dupe(u8, p[0..protos_len]) catch bun.outOfMemory()) else null, + .server_name = if (socket_config.server_name) |server_name| (bun.default_allocator.dupe(u8, server_name[0..bun.len(server_name)]) catch bun.outOfMemory()) else null, + .socket_context = null, // only set after the wrapTLS + }); + const tls_js_value = tls.getThisValue(globalObject); + TLSSocket.dataSetCached(tls_js_value, globalObject, default_data); + + var duplexContext = DuplexUpgradeContext.new(.{ + .upgrade = undefined, + .tls = tls, + .vm = globalObject.bunVM(), + .task = undefined, + .ssl_config = socket_config, + }); + tls.ref(); + + duplexContext.task = JSC.AnyTask.New(DuplexUpgradeContext, DuplexUpgradeContext.runEvent).init(duplexContext); + duplexContext.upgrade = uws.UpgradedDuplex.from(globalObject, duplex, .{ + .onOpen = @ptrCast(&DuplexUpgradeContext.onOpen), + .onData = @ptrCast(&DuplexUpgradeContext.onData), + .onHandshake = @ptrCast(&DuplexUpgradeContext.onHandshake), + .onClose = @ptrCast(&DuplexUpgradeContext.onClose), + .onEnd = @ptrCast(&DuplexUpgradeContext.onEnd), + .onWritable = @ptrCast(&DuplexUpgradeContext.onWritable), + .onError = @ptrCast(&DuplexUpgradeContext.onError), + .onTimeout = @ptrCast(&DuplexUpgradeContext.onTimeout), + .ctx = @ptrCast(duplexContext), + }); + + tls.socket = TLSSocket.Socket.fromDuplex(&duplexContext.upgrade); + tls.markActive(); + tls.poll_ref.ref(globalObject.bunVM()); + + duplexContext.startTLS(); + + const array = JSC.JSValue.createEmptyArray(globalObject, 2); + array.putIndex(globalObject, 0, tls_js_value); + // data, end, drain and close events must be reported + array.putIndex(globalObject, 1, duplexContext.upgrade.getJSHandlers(globalObject)); + + return array; +} + +pub fn jsIsNamedPipeSocket(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + JSC.markBinding(@src()); + + const arguments = callframe.arguments(3); + if (arguments.len < 1) { + global.throwNotEnoughArguments("isNamedPipeSocket", 1, arguments.len); + return .zero; + } + const socket = arguments.ptr[0]; + if (socket.as(TCPSocket)) |this| { + return JSC.JSValue.jsBoolean(this.socket.isNamedPipe()); + } else if (socket.as(TLSSocket)) |this| { + return JSC.JSValue.jsBoolean(this.socket.isNamedPipe()); + } + return JSC.JSValue.jsBoolean(false); +} pub fn createNodeTLSBinding(global: *JSC.JSGlobalObject) JSC.JSValue { return JSC.JSArray.create(global, &.{ - JSC.JSFunction.create(global, "addServerName", jsAddServerName, 3, .{}), + JSC.JSFunction.create(global, "addServerName", JSC.toJSHostFunction(jsAddServerName), 3, .{}), + JSC.JSFunction.create(global, "upgradeDuplexToTLS", JSC.toJSHostFunction(jsUpgradeDuplexToTLS), 2, .{}), + JSC.JSFunction.create(global, "isNamedPipeSocket", JSC.toJSHostFunction(jsIsNamedPipeSocket), 1, .{}), }); } diff --git a/src/bun.js/api/bun/spawn/stdio.zig b/src/bun.js/api/bun/spawn/stdio.zig index e86c2d6a46f8a..1f4e3323442c5 100644 --- a/src/bun.js/api/bun/spawn/stdio.zig +++ b/src/bun.js/api/bun/spawn/stdio.zig @@ -27,6 +27,7 @@ pub const Stdio = union(enum) { array_buffer: JSC.ArrayBuffer.Strong, memfd: bun.FileDescriptor, pipe: void, + ipc: void, const log = bun.sys.syslog; @@ -111,20 +112,7 @@ pub const Stdio = union(enum) { else => "spawn_stdio_memory_file", }; - // We use the linux syscall api because the glibc requirement is 2.27, which is a little close for comfort. - const rc = std.c.memfd_create(label, 0); - - log("memfd_create({s}) = {d}", .{ label, rc }); - - switch (bun.C.getErrno(rc)) { - .SUCCESS => {}, - else => |errno| { - log("Failed to create memfd: {s}", .{@tagName(errno)}); - return; - }, - } - - const fd = bun.toFD(rc); + const fd = bun.sys.memfd_create(label, 0).unwrap() catch return; var remain = this.byteSlice(); @@ -210,6 +198,7 @@ pub const Stdio = union(enum) { }, .dup2 => .{ .dup2 = .{ .out = stdio.dup2.out, .to = stdio.dup2.to } }, .capture, .pipe, .array_buffer => .{ .buffer = {} }, + .ipc => .{ .ipc = {} }, .fd => |fd| .{ .pipe = fd }, .memfd => |fd| .{ .pipe = fd }, .path => |pathlike| .{ .path = pathlike.slice() }, @@ -261,6 +250,7 @@ pub const Stdio = union(enum) { break :brk .{ .buffer = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory() }; }, + .ipc => .{ .ipc = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory() }, .capture, .pipe, .array_buffer => .{ .buffer = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory() }, .fd => |fd| .{ .pipe = fd }, .dup2 => .{ .dup2 = .{ .out = stdio.dup2.out, .to = stdio.dup2.to } }, @@ -295,6 +285,7 @@ pub const Stdio = union(enum) { pub fn isPiped(self: Stdio) bool { return switch (self) { .capture, .array_buffer, .blob, .pipe => true, + .ipc => Environment.isWindows, else => false, }; } @@ -325,7 +316,7 @@ pub const Stdio = union(enum) { } else if (str.eqlComptime("pipe") or str.eqlComptime("overlapped")) { out_stdio.* = Stdio{ .pipe = {} }; } else if (str.eqlComptime("ipc")) { - out_stdio.* = Stdio{ .pipe = {} }; // TODO: + out_stdio.* = Stdio{ .ipc = {} }; } else { globalThis.throwInvalidArguments("stdio must be an array of 'inherit', 'pipe', 'ignore', Bun.file(pathOrFd), number, or null", .{}); return false; @@ -343,7 +334,7 @@ pub const Stdio = union(enum) { if (file_fd >= std.math.maxInt(i32)) { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis }; globalThis.throwInvalidArguments("file descriptor must be a valid integer, received: {}", .{ - value.toFmt(globalThis, &formatter), + value.toFmt(&formatter), }); return false; } diff --git a/src/bun.js/api/bun/ssl_wrapper.zig b/src/bun.js/api/bun/ssl_wrapper.zig new file mode 100644 index 0000000000000..c75fba25fa56a --- /dev/null +++ b/src/bun.js/api/bun/ssl_wrapper.zig @@ -0,0 +1,488 @@ +const bun = @import("root").bun; + +const BoringSSL = bun.BoringSSL; +const X509 = @import("./x509.zig"); +const JSC = bun.JSC; +const uws = bun.uws; + +/// Mimics the behavior of openssl.c in uSockets, wrapping data that can be received from any where (network, DuplexStream, etc) +pub fn SSLWrapper(comptime T: type) type { + // receiveData() is called when we receive data from the network (encrypted data that will be decrypted by SSLWrapper) + // writeData() is called when we want to send data to the network (unencrypted data that will be encrypted by SSLWrapper) + + // after init we need to call start() to start the SSL handshake + // this will trigger the onOpen callback before the handshake starts and the onHandshake callback after the handshake completes + // onData and write callbacks are triggered when we have data to read or write respectively + // onData will pass the decrypted data that we received from the network + // write will pass the encrypted data that we want to send to the network + // onClose callback is triggered when we wanna the network connection to be closed (remember to flush the data before closing the connection) + + // Notes: + // SSL_read() read unencrypted data which is stored in the input BIO. + // SSL_write() write unencrypted data into the output BIO. + // BIO_write() write encrypted data into the input BIO. + // BIO_read() read encrypted data from the output BIO. + + return struct { + const This = @This(); + const BUFFER_SIZE = 16384; + + handlers: Handlers, + ssl: ?*BoringSSL.SSL, + ctx: ?*BoringSSL.SSL_CTX, + + flags: Flags = .{}, + + pub const Flags = packed struct { + handshake_state: HandshakeState = HandshakeState.HANDSHAKE_PENDING, + received_ssl_shutdown: bool = false, + sent_ssl_shutdown: bool = false, + is_client: bool = false, + authorized: bool = false, + fatal_error: bool = false, + closed_notified: bool = false, + }; + pub const HandshakeState = enum(u2) { + HANDSHAKE_PENDING = 0, + HANDSHAKE_COMPLETED = 1, + HANDSHAKE_RENEGOTIATION_PENDING = 2, + }; + pub const Handlers = struct { + ctx: T, + onOpen: *const fn (T) void, + onHandshake: *const fn (T, bool, uws.us_bun_verify_error_t) void, + write: *const fn (T, []const u8) void, + onData: *const fn (T, []const u8) void, + onClose: *const fn (T) void, + }; + + /// Initialize the SSLWrapper with a specific SSL_CTX*, remember to call SSL_CTX_up_ref if you want to keep the SSL_CTX alive after the SSLWrapper is deinitialized + pub fn initWithCTX(ctx: *BoringSSL.SSL_CTX, is_client: bool, handlers: Handlers) !This { + BoringSSL.load(); + const ssl = BoringSSL.SSL_new(ctx) orelse return error.OutOfMemory; + errdefer BoringSSL.SSL_free(ssl); + + // OpenSSL enables TLS renegotiation by default and accepts renegotiation requests from the peer transparently. Renegotiation is an extremely problematic protocol feature, so BoringSSL rejects peer renegotiations by default. + // We explicitly set the SSL_set_renegotiate_mode so if we switch to OpenSSL we keep the same behavior + // See: https://boringssl.googlesource.com/boringssl/+/HEAD/PORTING.md#TLS-renegotiation + if (is_client) { + // Set the renegotiation mode to explicit so that we can renegotiate on the client side if needed (better performance than ssl_renegotiate_freely) + // BoringSSL: Renegotiation is only supported as a client in TLS and the HelloRequest must be received at a quiet point in the application protocol. This is sufficient to support the common use of requesting a new client certificate between an HTTP request and response in (unpipelined) HTTP/1.1. + BoringSSL.SSL_set_renegotiate_mode(ssl, BoringSSL.ssl_renegotiate_explicit); + BoringSSL.SSL_set_connect_state(ssl); + } else { + // Set the renegotiation mode to never so that we can't renegotiate on the server side (security reasons) + // BoringSSL: There is no support for renegotiation as a server. (Attempts by clients will result in a fatal alert so that ClientHello messages cannot be used to flood a server and escape higher-level limits.) + BoringSSL.SSL_set_renegotiate_mode(ssl, BoringSSL.ssl_renegotiate_never); + BoringSSL.SSL_set_accept_state(ssl); + } + const input = BoringSSL.BIO_new(BoringSSL.BIO_s_mem()) orelse return error.OutOfMemory; + errdefer _ = BoringSSL.BIO_free(input); + const output = BoringSSL.BIO_new(BoringSSL.BIO_s_mem()) orelse return error.OutOfMemory; + // Set the EOF return value to -1 so that we can detect when the BIO is empty using BIO_ctrl_pending + _ = BoringSSL.BIO_set_mem_eof_return(input, -1); + _ = BoringSSL.BIO_set_mem_eof_return(output, -1); + // Set the input and output BIOs + BoringSSL.SSL_set_bio(ssl, input, output); + + return .{ + .handlers = handlers, + .flags = .{ .is_client = is_client }, + .ctx = ctx, + .ssl = ssl, + }; + } + + pub fn init(ssl_options: JSC.API.ServerConfig.SSLConfig, is_client: bool, handlers: Handlers) !This { + BoringSSL.load(); + + const ctx_opts: uws.us_bun_socket_context_options_t = JSC.API.ServerConfig.SSLConfig.asUSockets(ssl_options); + // Create SSL context using uSockets to match behavior of node.js + const ctx = uws.create_ssl_context_from_bun_options(ctx_opts) orelse return error.InvalidOptions; // invalid options + errdefer BoringSSL.SSL_CTX_free(ctx); + return try This.initWithCTX(ctx, is_client, handlers); + } + + pub fn start(this: *This) void { + // trigger the onOpen callback so the user can configure the SSL connection before first handshake + this.handlers.onOpen(this.handlers.ctx); + // start the handshake + this.handleTraffic(); + } + + /// Shutdown the read direction of the SSL (fake it just for convenience) + pub fn shutdownRead(this: *This) void { + // We cannot shutdown read in SSL, the read direction is closed by the peer. + // So we just ignore the onData data, we still wanna to wait until we received the shutdown + const DummyReadHandler = struct { + fn onData(_: T, _: []const u8) void {} + }; + this.handlers.onData = DummyReadHandler.onData; + } + /// Shutdown the write direction of the SSL and returns if we are completed closed or not + /// We cannot assume that the read part will remain open after we sent a shutdown, the other side will probably complete the 2-step shutdown ASAP. + /// Caution: never reuse a socket if fast_shutdown = true, this will also fully close both read and write directions + pub fn shutdown(this: *This, fast_shutdown: bool) bool { + const ssl = this.ssl orelse return false; + // we already sent the ssl shutdown + if (this.flags.sent_ssl_shutdown or this.flags.fatal_error) return this.flags.received_ssl_shutdown; + + // Calling SSL_shutdown() only closes the write direction of the connection; the read direction is closed by the peer. + // Once SSL_shutdown() is called, SSL_write(3) can no longer be used, but SSL_read(3) may still be used until the peer decides to close the connection in turn. + // The peer might continue sending data for some period of time before handling the local application's shutdown indication. + // This will start a full shutdown process if fast_shutdown = false, we can assume that the other side will complete the 2-step shutdown ASAP. + const ret = BoringSSL.SSL_shutdown(ssl); + // when doing a fast shutdown we don't need to wait for the peer to send a shutdown so we just call SSL_shutdown again + if (fast_shutdown) { + // This allows for a more rapid shutdown process if the application does not wish to wait for the peer. + // This alternative "fast shutdown" approach should only be done if it is known that the peer will not send more data, otherwise there is a risk of an application exposing itself to a truncation attack. + // The full SSL_shutdown() process, in which both parties send close_notify alerts and SSL_shutdown() returns 1, provides a cryptographically authenticated indication of the end of a connection. + + // The fast shutdown approach can only be used if there is no intention to reuse the underlying connection (e.g. a TCP connection) for further communication; in this case, the full shutdown process must be performed to ensure synchronisation. + _ = BoringSSL.SSL_shutdown(ssl); + this.flags.received_ssl_shutdown = true; + // Reset pending handshake because we are closed for sure now + if (this.flags.handshake_state != HandshakeState.HANDSHAKE_COMPLETED) { + this.flags.handshake_state = HandshakeState.HANDSHAKE_COMPLETED; + this.triggerHandshakeCallback(false, this.getVerifyError()); + } + + // we need to trigger close because we are not receiving a SSL_shutdown + this.triggerCloseCallback(); + return false; + } + + // we sent the shutdown + this.flags.sent_ssl_shutdown = ret >= 0; + if (ret < 0) { + const err = BoringSSL.SSL_get_error(ssl, ret); + BoringSSL.ERR_clear_error(); + + if (err == BoringSSL.SSL_ERROR_SSL or err == BoringSSL.SSL_ERROR_SYSCALL) { + this.flags.fatal_error = true; + this.triggerCloseCallback(); + return false; + } + } + return ret == 1; // truly closed + } + + // flush buffered data and returns amount of pending data to write + pub fn flush(this: *This) usize { + const ssl = this.ssl orelse return 0; + this.handleTraffic(); + const pending = BoringSSL.BIO_ctrl_pending(BoringSSL.SSL_get_wbio(ssl)); + if (pending > 0) return @intCast(pending); + return 0; + } + + // Return if we have pending data to be read or write + pub fn hasPendingData(this: *const This) bool { + const ssl = this.ssl orelse return false; + + return BoringSSL.BIO_ctrl_pending(BoringSSL.SSL_get_wbio(ssl)) > 0 or BoringSSL.BIO_ctrl_pending(BoringSSL.SSL_get_rbio(ssl)) > 0; + } + + // We sent or received a shutdown (closing or closed) + pub fn isShutdown(this: *const This) bool { + return this.flags.closed_notified or this.flags.received_ssl_shutdown or this.flags.sent_ssl_shutdown; + } + + // We sent and received the shutdown (fully closed) + pub fn isClosed(this: *const This) bool { + return this.flags.received_ssl_shutdown and this.flags.sent_ssl_shutdown; + } + + pub fn isAuthorized(this: *This) bool { + // handshake ended we know if we are authorized or not + if (this.flags.handshake_state == HandshakeState.HANDSHAKE_COMPLETED) { + return this.flags.authorized; + } + // hanshake still in progress + return false; + } + + // Receive data from the network (encrypted data) + pub fn receiveData(this: *This, data: []const u8) void { + const ssl = this.ssl orelse return; + + const input = BoringSSL.SSL_get_rbio(ssl) orelse return; + const written = BoringSSL.BIO_write(input, data.ptr, @as(c_int, @intCast(data.len))); + if (written > -1) { + this.handleTraffic(); + } + } + + // Send data to the network (unencrypted data) + pub fn writeData(this: *This, data: []const u8) !usize { + const ssl = this.ssl orelse return error.ConnectionClosed; + + // shutdown is sent we cannot write anymore + if (this.flags.sent_ssl_shutdown) return error.ConnectionClosed; + + if (data.len == 0) { + // just cycle through internal openssl's state + this.handleTraffic(); + return 0; + } + const written = BoringSSL.SSL_write(ssl, data.ptr, @as(c_int, @intCast(data.len))); + if (written <= 0) { + const err = BoringSSL.SSL_get_error(ssl, written); + BoringSSL.ERR_clear_error(); + + if (err == BoringSSL.SSL_ERROR_WANT_READ) { + // we wanna read/write + this.handleTraffic(); + return error.WantRead; + } + if (err == BoringSSL.SSL_ERROR_WANT_WRITE) { + // we wanna read/write + this.handleTraffic(); + return error.WantWrite; + } + // some bad error happened here we must close + this.flags.fatal_error = err == BoringSSL.SSL_ERROR_SSL or err == BoringSSL.SSL_ERROR_SYSCALL; + this.triggerCloseCallback(); + return error.ConnectionClosed; + } + this.handleTraffic(); + return @intCast(written); + } + + pub fn deinit(this: *This) void { + this.flags.closed_notified = true; + if (this.ssl) |ssl| { + this.ssl = null; + // SSL_free will also free the input and output BIOs + _ = BoringSSL.SSL_free(ssl); + } + if (this.ctx) |ctx| { + this.ctx = null; + // SSL_CTX_free will free the SSL context and all the certificates + _ = BoringSSL.SSL_CTX_free(ctx); + } + } + + fn triggerHandshakeCallback(this: *This, success: bool, result: uws.us_bun_verify_error_t) void { + if (this.flags.closed_notified) return; + + this.flags.authorized = success; + // trigger the handshake callback + this.handlers.onHandshake(this.handlers.ctx, success, result); + } + + fn triggerWannaWriteCallback(this: *This, data: []const u8) void { + if (this.flags.closed_notified) return; + + // trigger the write callback + this.handlers.write(this.handlers.ctx, data); + } + + fn triggerDataCallback(this: *This, data: []const u8) void { + if (this.flags.closed_notified) return; + + // trigger the onData callback + this.handlers.onData(this.handlers.ctx, data); + } + + fn triggerCloseCallback(this: *This) void { + if (this.flags.closed_notified) return; + this.flags.closed_notified = true; + // trigger the onClose callback + this.handlers.onClose(this.handlers.ctx); + } + + fn getVerifyError(this: *This) uws.us_bun_verify_error_t { + if (this.isShutdown()) { + return .{}; + } + const ssl = this.ssl orelse return .{}; + return uws.us_ssl_socket_verify_error_from_ssl(ssl); + } + + /// Update the handshake state + /// Returns true if we can call handleReading + fn updateHandshakeState(this: *This) bool { + if (this.flags.closed_notified) return false; + const ssl = this.ssl orelse return false; + + if (BoringSSL.SSL_is_init_finished(ssl) != 0) { + // handshake already completed nothing to do here + if ((BoringSSL.SSL_get_shutdown(ssl) & BoringSSL.SSL_RECEIVED_SHUTDOWN) != 0) { + // we received a shutdown + this.flags.received_ssl_shutdown = true; + // 2-step shutdown + _ = this.shutdown(false); + this.triggerCloseCallback(); + + return false; + } + return true; + } + + if (this.flags.handshake_state == HandshakeState.HANDSHAKE_RENEGOTIATION_PENDING) { + // we are in the middle of a renegotiation need to call read/write + return true; + } + + const result = BoringSSL.SSL_do_handshake(ssl); + + if (result <= 0) { + const err = BoringSSL.SSL_get_error(ssl, result); + BoringSSL.ERR_clear_error(); + if (err == BoringSSL.SSL_ERROR_ZERO_RETURN) { + // Remotely-Initiated Shutdown + // See: https://www.openssl.org/docs/manmaster/man3/SSL_shutdown.html + this.flags.received_ssl_shutdown = true; + // 2-step shutdown + _ = this.shutdown(false); + this.handleEndOfRenegotiation(); + return false; + } + // as far as I know these are the only errors we want to handle + if (err != BoringSSL.SSL_ERROR_WANT_READ and err != BoringSSL.SSL_ERROR_WANT_WRITE) { + // clear per thread error queue if it may contain something + this.flags.fatal_error = err == BoringSSL.SSL_ERROR_SSL or err == BoringSSL.SSL_ERROR_SYSCALL; + + this.flags.handshake_state = HandshakeState.HANDSHAKE_COMPLETED; + this.triggerHandshakeCallback(false, this.getVerifyError()); + + if (this.flags.fatal_error) { + this.triggerCloseCallback(); + return false; + } + return true; + } + this.flags.handshake_state = HandshakeState.HANDSHAKE_PENDING; + return true; + } + + // handshake completed + this.flags.handshake_state = HandshakeState.HANDSHAKE_COMPLETED; + this.triggerHandshakeCallback(true, this.getVerifyError()); + + return true; + } + + /// Handle the end of a renegotiation if it was pending + /// This function is called when we receive a SSL_ERROR_ZERO_RETURN or successfully read data + fn handleEndOfRenegotiation(this: *This) void { + if (this.flags.handshake_state == HandshakeState.HANDSHAKE_RENEGOTIATION_PENDING and (this.ssl == null or BoringSSL.SSL_is_init_finished(this.ssl) != 0)) { + // renegotiation ended successfully call on_handshake + this.flags.handshake_state = HandshakeState.HANDSHAKE_COMPLETED; + this.triggerHandshakeCallback(true, this.getVerifyError()); + } + } + + /// Handle reading data + /// Returns true if we can call handleWriting + fn handleReading(this: *This, buffer: *[BUFFER_SIZE]u8) bool { + var read: usize = 0; + + // read data from the input BIO + while (true) { + const ssl = this.ssl orelse return false; + + const input = BoringSSL.SSL_get_rbio(ssl) orelse return true; + + const pending = BoringSSL.BIO_ctrl_pending(input); + if (pending <= 0) { + // no data to write + break; + } + const available = buffer[read..]; + const just_read = BoringSSL.SSL_read(ssl, available.ptr, @intCast(available.len)); + + if (just_read <= 0) { + const err = BoringSSL.SSL_get_error(ssl, just_read); + BoringSSL.ERR_clear_error(); + + if (err != BoringSSL.SSL_ERROR_WANT_READ and err != BoringSSL.SSL_ERROR_WANT_WRITE) { + if (err == BoringSSL.SSL_ERROR_WANT_RENEGOTIATE) { + this.flags.handshake_state = HandshakeState.HANDSHAKE_RENEGOTIATION_PENDING; + if (BoringSSL.SSL_renegotiate(ssl) == 0) { + this.flags.handshake_state = HandshakeState.HANDSHAKE_COMPLETED; + // we failed to renegotiate + this.triggerHandshakeCallback(false, this.getVerifyError()); + this.triggerCloseCallback(); + return false; + } + // ok, we are done here, we need to call SSL_read again + // this dont mean that we are done with the handshake renegotiation + // we need to call SSL_read again + continue; + } else if (err == BoringSSL.SSL_ERROR_ZERO_RETURN) { + // Remotely-Initiated Shutdown + // See: https://www.openssl.org/docs/manmaster/man3/SSL_shutdown.html + this.flags.received_ssl_shutdown = true; + // 2-step shutdown + _ = this.shutdown(false); + this.handleEndOfRenegotiation(); + } + this.flags.fatal_error = err == BoringSSL.SSL_ERROR_SSL or err == BoringSSL.SSL_ERROR_SYSCALL; + + // flush the reading + if (read > 0) { + this.triggerDataCallback(buffer[0..read]); + } + this.triggerCloseCallback(); + return false; + } else { + // we wanna read/write just break + break; + } + } + + this.handleEndOfRenegotiation(); + + read += @intCast(just_read); + if (read == buffer.len) { + // we filled the buffer + this.triggerDataCallback(buffer[0..read]); + read = 0; + } + } + // we finished reading + if (read > 0) { + this.triggerDataCallback(buffer[0..read]); + } + return true; + } + + fn handleWriting(this: *This, buffer: *[BUFFER_SIZE]u8) void { + while (true) { + const ssl = this.ssl orelse return; + + const output = BoringSSL.SSL_get_wbio(ssl) orelse return; + // read data from the output BIO + const pending = BoringSSL.BIO_ctrl_pending(output); + if (pending <= 0) { + // no data to write + break; + } + // limit the read to the buffer size + const len = @min(pending, buffer.len); + const pending_buffer = buffer[0..len]; + const read = BoringSSL.BIO_read(output, pending_buffer.ptr, len); + if (read > 0) { + this.triggerWannaWriteCallback(buffer[0..@intCast(read)]); + } + } + } + + fn handleTraffic(this: *This) void { + // always handle the handshake first + if (this.updateHandshakeState()) { + // shared stack buffer for reading and writing + var buffer: [BUFFER_SIZE]u8 = undefined; + // drain the input BIO first + this.handleWriting(&buffer); + // drain the output BIO + if (this.handleReading(&buffer)) { + // read data can trigger writing so we need to handle it + this.handleWriting(&buffer); + } + } + } + }; +} diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index b525e15f7387c..018b24f1ddf0d 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -20,6 +20,7 @@ const windows = bun.windows; const uv = windows.libuv; const LifecycleScriptSubprocess = bun.install.LifecycleScriptSubprocess; const Body = JSC.WebCore.Body; +const IPClog = Output.scoped(.IPC, false); const PosixSpawn = bun.posix.spawn; const Rusage = bun.posix.spawn.Rusage; @@ -43,14 +44,14 @@ pub const ResourceUsage = struct { pub fn constructor( _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) ?*Subprocess { + ) ?*Subprocess { return null; } pub fn getCPUTime( this: *ResourceUsage, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { var cpu = JSC.JSValue.createEmptyObjectWithNullPrototype(globalObject); const rusage = this.rusage; @@ -67,28 +68,28 @@ pub const ResourceUsage = struct { pub fn getMaxRSS( this: *ResourceUsage, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSC.JSValue.jsNumber(this.rusage.maxrss); } pub fn getSharedMemorySize( this: *ResourceUsage, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSC.JSValue.jsNumber(this.rusage.ixrss); } pub fn getSwapCount( this: *ResourceUsage, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSC.JSValue.jsNumber(this.rusage.nswap); } pub fn getOps( this: *ResourceUsage, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { var ops = JSC.JSValue.createEmptyObjectWithNullPrototype(globalObject); ops.put(globalObject, JSC.ZigString.static("in"), JSC.JSValue.jsNumber(this.rusage.inblock)); ops.put(globalObject, JSC.ZigString.static("out"), JSC.JSValue.jsNumber(this.rusage.oublock)); @@ -98,7 +99,7 @@ pub const ResourceUsage = struct { pub fn getMessages( this: *ResourceUsage, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { var msgs = JSC.JSValue.createEmptyObjectWithNullPrototype(globalObject); msgs.put(globalObject, JSC.ZigString.static("sent"), JSC.JSValue.jsNumber(this.rusage.msgsnd)); msgs.put(globalObject, JSC.ZigString.static("received"), JSC.JSValue.jsNumber(this.rusage.msgrcv)); @@ -108,14 +109,14 @@ pub const ResourceUsage = struct { pub fn getSignalCount( this: *ResourceUsage, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSC.JSValue.jsNumber(this.rusage.nsignals); } pub fn getContextSwitches( this: *ResourceUsage, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { var ctx = JSC.JSValue.createEmptyObjectWithNullPrototype(globalObject); ctx.put(globalObject, JSC.ZigString.static("voluntary"), JSC.JSValue.jsNumber(this.rusage.nvcsw)); ctx.put(globalObject, JSC.ZigString.static("involuntary"), JSC.JSValue.jsNumber(this.rusage.nivcsw)); @@ -128,10 +129,7 @@ pub const ResourceUsage = struct { }; pub fn appendEnvpFromJS(globalThis: *JSC.JSGlobalObject, object: JSC.JSValue, envp: *std.ArrayList(?[*:0]const u8), PATH: *[]const u8) !void { - var object_iter = JSC.JSPropertyIterator(.{ - .skip_empty_name = false, - .include_value = true, - }).init(globalThis, object); + var object_iter = JSC.JSPropertyIterator(.{ .skip_empty_name = false, .include_value = true }).init(globalThis, object); defer object_iter.deinit(); try envp.ensureTotalCapacityPrecise(object_iter.len + // +1 incase there's IPC @@ -185,6 +183,7 @@ pub const Subprocess = struct { exit_promise: JSC.Strong = .{}, on_exit_callback: JSC.Strong = .{}, + on_disconnect_callback: JSC.Strong = .{}, globalThis: *JSC.JSGlobalObject, observable_getters: std.enums.EnumSet(enum { @@ -203,6 +202,9 @@ pub const Subprocess = struct { flags: Flags = .{}, weak_file_sink_stdin_ptr: ?*JSC.WebCore.FileSink = null, + ref_count: u32 = 1, + + usingnamespace bun.NewRefCounted(@This(), Subprocess.deinit); pub const Flags = packed struct { is_sync: bool = false, @@ -227,7 +229,7 @@ pub const Subprocess = struct { this: *Subprocess, globalObject: *JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { return this.createResourceUsageObject(globalObject); } @@ -346,7 +348,7 @@ pub const Subprocess = struct { return this.has_pending_activity.load(.acquire); } - pub fn ref(this: *Subprocess) void { + pub fn jsRef(this: *Subprocess) void { this.process.enableKeepingEventLoopAlive(); if (!this.hasCalledGetter(.stdin)) { @@ -365,7 +367,7 @@ pub const Subprocess = struct { } /// This disables the keeping process alive flag on the poll and also in the stdin, stdout, and stderr - pub fn unref(this: *Subprocess) void { + pub fn jsUnref(this: *Subprocess) void { this.process.disableKeepingEventLoopAlive(); if (!this.hasCalledGetter(.stdin)) { @@ -386,7 +388,7 @@ pub const Subprocess = struct { pub fn constructor( _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) ?*Subprocess { + ) ?*Subprocess { return null; } @@ -433,11 +435,9 @@ pub const Subprocess = struct { if (Environment.isWindows) { return switch (stdio) { .inherit => Readable{ .inherit = {} }, - .ignore => Readable{ .ignore = {} }, - .path => Readable{ .ignore = {} }, + .ignore, .ipc, .path, .memfd => Readable{ .ignore = {} }, .fd => |fd| Readable{ .fd = fd }, .dup2 => |dup2| Readable{ .fd = dup2.out.toFd() }, - .memfd => Readable{ .ignore = {} }, .pipe => Readable{ .pipe = PipeReader.create(event_loop, process, result) }, .array_buffer, .blob => Output.panic("TODO: implement ArrayBuffer & Blob support in Stdio readable", .{}), .capture => Output.panic("TODO: implement capture support in Stdio readable", .{}), @@ -452,8 +452,7 @@ pub const Subprocess = struct { return switch (stdio) { .inherit => Readable{ .inherit = {} }, - .ignore => Readable{ .ignore = {} }, - .path => Readable{ .ignore = {} }, + .ignore, .ipc, .path => Readable{ .ignore = {} }, .fd => Readable{ .fd = result.? }, .memfd => Readable{ .memfd = stdio.memfd }, .pipe => Readable{ .pipe = PipeReader.create(event_loop, process, result) }, @@ -560,7 +559,7 @@ pub const Subprocess = struct { pub fn getStderr( this: *Subprocess, globalThis: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { this.observable_getters.insert(.stderr); return this.stderr.toJS(globalThis, this.hasExited()); } @@ -568,7 +567,7 @@ pub const Subprocess = struct { pub fn getStdin( this: *Subprocess, globalThis: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { this.observable_getters.insert(.stdin); return this.stdin.toJS(globalThis, this); } @@ -576,7 +575,7 @@ pub const Subprocess = struct { pub fn getStdout( this: *Subprocess, globalThis: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { this.observable_getters.insert(.stdout); return this.stdout.toJS(globalThis, this.hasExited()); } @@ -585,7 +584,7 @@ pub const Subprocess = struct { this: *Subprocess, global: *JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.process.hasExited()) { // rely on GC to clean everything up in this case return .undefined; @@ -613,7 +612,7 @@ pub const Subprocess = struct { this: *Subprocess, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { this.this_jsvalue = callframe.this(); var arguments = callframe.arguments(1); @@ -679,7 +678,6 @@ pub const Subprocess = struct { if (this.hasExited()) { return .{ .result = {} }; } - return this.process.kill(@intCast(sig)); } @@ -694,30 +692,28 @@ pub const Subprocess = struct { this.process.close(); } - pub fn doRef(this: *Subprocess, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { - this.ref(); - return JSC.JSValue.jsUndefined(); + pub fn doRef(this: *Subprocess, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + this.jsRef(); + return .undefined; } - pub fn doUnref(this: *Subprocess, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { - this.unref(); - return JSC.JSValue.jsUndefined(); + pub fn doUnref(this: *Subprocess, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + this.jsUnref(); + return .undefined; } pub fn onStdinDestroyed(this: *Subprocess) void { this.flags.has_stdin_destructor_called = true; this.weak_file_sink_stdin_ptr = null; - - if (this.flags.finalized) { - // if the process has already been garbage collected, we can free the memory now - bun.default_allocator.destroy(this); - } else { + defer this.deref(); + if (!this.flags.finalized) { // otherwise update the pending activity flag this.updateHasPendingActivity(); } } - pub fn doSend(this: *Subprocess, global: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn doSend(this: *Subprocess, global: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSValue { + IPClog("Subprocess#doSend", .{}); const ipc_data = &(this.ipc_data orelse { if (this.hasExited()) { global.throw("Subprocess.send() cannot be used after the process has exited.", .{}); @@ -737,13 +733,23 @@ pub const Subprocess = struct { const success = ipc_data.serializeAndSend(global, value); if (!success) return .zero; - return JSC.JSValue.jsUndefined(); + return .undefined; + } + pub fn disconnectIPC(this: *Subprocess, nextTick: bool) void { + const ipc_data = this.ipc() orelse return; + ipc_data.close(nextTick); + } + pub fn disconnect(this: *Subprocess, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + _ = globalThis; + _ = callframe; + this.disconnectIPC(true); + return .undefined; } - pub fn disconnect(this: *Subprocess) void { - const ipc_data = this.ipc_data orelse return; - ipc_data.socket.close(.normal); - this.ipc_data = null; + pub fn getConnected(this: *Subprocess, globalThis: *JSGlobalObject) JSValue { + _ = globalThis; + const ipc_data = this.ipc(); + return JSValue.jsBoolean(ipc_data != null and ipc_data.?.disconnected == false); } pub fn pid(this: *const Subprocess) i32 { @@ -753,21 +759,21 @@ pub const Subprocess = struct { pub fn getPid( this: *Subprocess, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSValue.jsNumber(this.pid()); } pub fn getKilled( this: *Subprocess, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSValue.jsBoolean(this.hasKilled()); } pub fn getStdio( this: *Subprocess, global: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { const array = JSValue.createEmptyArray(global, 0); array.push(global, .null); array.push(global, .null); // TODO: align this with options @@ -832,7 +838,7 @@ pub const Subprocess = struct { ref_count: u32 = 1, buffer: []const u8 = "", - pub usingnamespace bun.NewRefCounted(@This(), deinit); + pub usingnamespace bun.NewRefCounted(@This(), @This().deinit); const This = @This(); const print = bun.Output.scoped(.StaticPipeWriter, false); @@ -956,7 +962,7 @@ pub const Subprocess = struct { pub const IOReader = bun.io.BufferedReader; pub const Poll = IOReader; - pub usingnamespace bun.NewRefCounted(PipeReader, deinit); + pub usingnamespace bun.NewRefCounted(PipeReader, PipeReader.deinit); pub fn hasPendingActivity(this: *const PipeReader) bool { if (this.state == .pending) @@ -1233,6 +1239,7 @@ pub const Subprocess = struct { } pipe.writer.setParent(pipe); subprocess.weak_file_sink_stdin_ptr = pipe; + subprocess.ref(); subprocess.flags.has_stdin_destructor_called = false; return Writable{ @@ -1264,7 +1271,7 @@ pub const Subprocess = struct { .memfd, .path, .ignore => { return Writable{ .ignore = {} }; }, - .capture => { + .ipc, .capture => { return Writable{ .ignore = {} }; }, } @@ -1291,6 +1298,7 @@ pub const Subprocess = struct { } subprocess.weak_file_sink_stdin_ptr = pipe; + subprocess.ref(); subprocess.flags.has_stdin_destructor_called = false; pipe.writer.handle.poll.flags.insert(.socket); @@ -1323,7 +1331,7 @@ pub const Subprocess = struct { .path, .ignore => { return Writable{ .ignore = {} }; }, - .capture => { + .ipc, .capture => { return Writable{ .ignore = {} }; }, } @@ -1342,6 +1350,7 @@ pub const Subprocess = struct { } else { subprocess.flags.has_stdin_destructor_called = false; subprocess.weak_file_sink_stdin_ptr = pipe; + subprocess.ref(); if (@intFromPtr(pipe.signal.ptr) == @intFromPtr(subprocess)) { pipe.signal.clear(); } @@ -1410,6 +1419,8 @@ pub const Subprocess = struct { this_jsvalue.ensureStillAlive(); this.pid_rusage = rusage.*; const is_sync = this.flags.is_sync; + defer this.deref(); + defer this.disconnectIPC(true); var stdin: ?*JSC.WebCore.FileSink = this.weak_file_sink_stdin_ptr; var existing_stdin_value = JSC.JSValue.zero; @@ -1471,9 +1482,9 @@ pub const Subprocess = struct { if (status == .err) status.err.toJSC(globalThis) else - JSC.JSValue.jsUndefined(); + .undefined; - const this_value = if (this_jsvalue.isEmptyOrUndefinedOrNull()) JSC.JSValue.jsUndefined() else this_jsvalue; + const this_value = if (this_jsvalue.isEmptyOrUndefinedOrNull()) .undefined else this_jsvalue; this_value.ensureStillAlive(); const args = [_]JSValue{ @@ -1549,6 +1560,12 @@ pub const Subprocess = struct { this.exit_promise.deinit(); this.on_exit_callback.deinit(); + this.on_disconnect_callback.deinit(); + } + + pub fn deinit(this: *Subprocess) void { + log("deinit", .{}); + this.destroy(); } pub fn finalize(this: *Subprocess) callconv(.C) void { @@ -1565,16 +1582,13 @@ pub const Subprocess = struct { this.process.deref(); this.flags.finalized = true; - if (this.weak_file_sink_stdin_ptr == null) { - // if no file sink exists we can free immediately - bun.default_allocator.destroy(this); - } + this.deref(); } pub fn getExited( this: *Subprocess, globalThis: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { switch (this.process.status) { .exited => |exit| { return JSC.JSPromise.resolvedPromiseValue(globalThis, JSValue.jsNumber(exit.code)); @@ -1598,7 +1612,7 @@ pub const Subprocess = struct { pub fn getExitCode( this: *Subprocess, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.process.status == .exited) { return JSC.JSValue.jsNumber(this.process.status.exited.code); } @@ -1608,10 +1622,10 @@ pub const Subprocess = struct { pub fn getSignalCode( this: *Subprocess, global: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.process.signalCode()) |signal| { if (signal.name()) |name| - return JSC.ZigString.init(name).toValueGC(global) + return JSC.ZigString.init(name).toJS(global) else return JSC.JSValue.jsNumber(@intFromEnum(signal)); } @@ -1638,10 +1652,7 @@ pub const Subprocess = struct { var allocator = arena.allocator(); var override_env = false; - var env_array = std.ArrayListUnmanaged(?[*:0]const u8){ - .items = &.{}, - .capacity = 0, - }; + var env_array = std.ArrayListUnmanaged(?[*:0]const u8){}; var jsc_vm = globalThis.bunVM(); var cwd = jsc_vm.bundler.fs.top_level_dir; @@ -1658,6 +1669,7 @@ pub const Subprocess = struct { } var lazy = false; var on_exit_callback = JSValue.zero; + var on_disconnect_callback = JSValue.zero; var PATH = jsc_vm.bundler.env.get("PATH") orelse ""; var argv = std.ArrayList(?[*:0]const u8).init(allocator); var cmd_value = JSValue.zero; @@ -1667,6 +1679,7 @@ pub const Subprocess = struct { var ipc_callback: JSValue = .zero; var extra_fds = std.ArrayList(bun.spawn.SpawnOptions.Stdio).init(bun.default_allocator); var argv0: ?[*:0]const u8 = null; + var ipc_channel: i32 = -1; var windows_hide: bool = false; var windows_verbatim_arguments: bool = false; @@ -1701,6 +1714,17 @@ pub const Subprocess = struct { }; } } + + // need to update `cwd` before searching for executable with `Which.which` + if (args.getTruthy(globalThis, "cwd")) |cwd_| { + const cwd_str = cwd_.getZigString(globalThis); + if (cwd_str.len > 0) { + cwd = cwd_str.toOwnedSliceZ(allocator) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + } + } } { @@ -1724,7 +1748,7 @@ pub const Subprocess = struct { { var first_cmd = cmds_array.next().?; - var arg0 = first_cmd.toSlice(globalThis, allocator); + var arg0 = first_cmd.toSliceOrNullWithAllocator(globalThis, allocator) orelse return .zero; defer arg0.deinit(); if (argv0 == null) { @@ -1748,6 +1772,7 @@ pub const Subprocess = struct { return .zero; }; } + argv.appendAssumeCapacity(allocator.dupeZ(u8, arg0.slice()) catch { globalThis.throwOutOfMemory(); return .zero; @@ -1774,7 +1799,6 @@ pub const Subprocess = struct { } if (args != .zero and args.isObject()) { - // This must run before the stdio parsing happens if (!is_sync) { if (args.getTruthy(globalThis, "ipc")) |val| { @@ -1799,25 +1823,20 @@ pub const Subprocess = struct { }; ipc_callback = val.withAsyncContextIfNeeded(globalThis); - - if (Environment.isPosix) { - extra_fds.append(.{ .buffer = {} }) catch { - globalThis.throwOutOfMemory(); - return .zero; - }; - } } } } - if (args.getTruthy(globalThis, "cwd")) |cwd_| { - const cwd_str = cwd_.getZigString(globalThis); - if (cwd_str.len > 0) { - cwd = cwd_str.toOwnedSliceZ(allocator) catch { - globalThis.throwOutOfMemory(); - return .zero; - }; + if (args.getTruthy(globalThis, "onDisconnect")) |onDisconnect_| { + if (!onDisconnect_.isCell() or !onDisconnect_.isCallable(globalThis.vm())) { + globalThis.throwInvalidArguments("onDisconnect must be a function or undefined", .{}); + return .zero; } + + on_disconnect_callback = if (comptime is_sync) + onDisconnect_ + else + onDisconnect_.withAsyncContextIfNeeded(globalThis); } if (args.getTruthy(globalThis, "onExit")) |onExit_| { @@ -1848,7 +1867,6 @@ pub const Subprocess = struct { }; env_array = envp_managed.moveToUnmanaged(); } - if (args.get(globalThis, "stdio")) |stdio_val| { if (!stdio_val.isEmptyOrUndefinedOrNull()) { if (stdio_val.jsType().isArray()) { @@ -1856,7 +1874,7 @@ pub const Subprocess = struct { var i: u32 = 0; while (stdio_iter.next()) |value| : (i += 1) { if (!stdio[i].extract(globalThis, i, value)) - return JSC.JSValue.jsUndefined(); + return .undefined; if (i == 2) break; } @@ -1865,7 +1883,7 @@ pub const Subprocess = struct { while (stdio_iter.next()) |value| : (i += 1) { var new_item: Stdio = undefined; if (!new_item.extract(globalThis, i, value)) { - return JSC.JSValue.jsUndefined(); + return .undefined; } const opt = switch (new_item.asSpawnOption(i)) { @@ -1874,6 +1892,9 @@ pub const Subprocess = struct { return e.throwJS(globalThis); }, }; + if (opt == .ipc) { + ipc_channel = @intCast(extra_fds.items.len); + } extra_fds.append(opt) catch { globalThis.throwOutOfMemory(); return .zero; @@ -1954,8 +1975,8 @@ pub const Subprocess = struct { } } } - - var windows_ipc_env_buf: if (Environment.isWindows) ["NODE_CHANNEL_FD=\\\\.\\pipe\\BUN_IPC_00000000-0000-0000-0000-000000000000\x00".len]u8 else void = undefined; + //"NODE_CHANNEL_FD=" is 16 bytes long, 15 bytes for the number, and 1 byte for the null terminator should be enough/safe + var ipc_env_buf: [32]u8 = undefined; if (!is_sync) if (maybe_ipc_mode) |ipc_mode| { // IPC is currently implemented in a very limited way. // @@ -1964,25 +1985,42 @@ pub const Subprocess = struct { // // Bun currently only supports three fds: stdin, stdout, and stderr, which are all unidirectional // - // And then fd 3 is assigned specifically and only for IPC. This is quite lame, because Node.js allows - // the ipc fd to be any number and it just works. But most people only care about the default `.fork()` - // behavior, where this workaround suffices. + // And then one fd is assigned specifically and only for IPC. If the user dont specify it, we add one (default: 3). // // When Bun.spawn() is given an `.ipc` callback, it enables IPC as follows: env_array.ensureUnusedCapacity(allocator, 3) catch |err| return globalThis.handleError(err, "in Bun.spawn"); - if (Environment.isPosix) { - env_array.appendAssumeCapacity("NODE_CHANNEL_FD=3"); - } else { - const uuid = globalThis.bunVM().rareData().nextUUID(); - const pipe_env = std.fmt.bufPrintZ( - &windows_ipc_env_buf, - "NODE_CHANNEL_FD=\\\\.\\pipe\\BUN_IPC_{s}", - .{uuid}, - ) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, // upper bound for this string is known - }; - env_array.appendAssumeCapacity(pipe_env); - } + const ipc_fd: u32 = brk: { + if (ipc_channel == -1) { + // If the user didn't specify an IPC channel, we need to add one + ipc_channel = @intCast(extra_fds.items.len); + var ipc_extra_fd_default = Stdio{ .ipc = {} }; + const fd: u32 = @intCast(ipc_channel + 3); + switch (ipc_extra_fd_default.asSpawnOption(fd)) { + .result => |opt| { + extra_fds.append(opt) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + }, + .err => |e| { + return e.throwJS(globalThis); + }, + } + break :brk fd; + } else { + break :brk @intCast(ipc_channel + 3); + } + }; + + const pipe_env = std.fmt.bufPrintZ( + &ipc_env_buf, + "NODE_CHANNEL_FD={d}", + .{ipc_fd}, + ) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + env_array.appendAssumeCapacity(pipe_env); env_array.appendAssumeCapacity(switch (ipc_mode) { inline else => |t| "NODE_CHANNEL_SERIALIZATION_MODE=" ++ @tagName(t), @@ -2029,25 +2067,36 @@ pub const Subprocess = struct { } else {}, }; - const process_allocator = globalThis.allocator(); - var subprocess = process_allocator.create(Subprocess) catch { - globalThis.throwOutOfMemory(); - return .zero; - }; + var subprocess = Subprocess.new(.{ + .globalThis = globalThis, + .process = undefined, + .pid_rusage = null, + .stdin = undefined, + .stdout = undefined, + .stderr = undefined, + .stdio_pipes = .{}, + .on_exit_callback = .{}, + .on_disconnect_callback = .{}, + .ipc_data = null, + .ipc_callback = .{}, + .flags = .{ + .is_sync = is_sync, + }, + }); var spawned = switch (bun.spawn.spawnProcess( &spawn_options, @ptrCast(argv.items.ptr), @ptrCast(env_array.items.ptr), ) catch |err| { - process_allocator.destroy(subprocess); + subprocess.deref(); spawn_options.deinit(); globalThis.throwError(err, ": failed to spawn process"); return .zero; }) { .err => |err| { - process_allocator.destroy(subprocess); + subprocess.deref(); spawn_options.deinit(); globalThis.throwValue(err.toJSC(globalThis)); return .zero; @@ -2063,9 +2112,9 @@ pub const Subprocess = struct { uws.us_socket_from_fd( jsc_vm.rareData().spawnIPCContext(jsc_vm), @sizeOf(*Subprocess), - spawned.extra_pipes.items[0].cast(), + spawned.extra_pipes.items[@intCast(ipc_channel)].cast(), ) orelse { - process_allocator.destroy(subprocess); + subprocess.deref(); spawn_options.deinit(); globalThis.throw("failed to create socket pair", .{}); return .zero; @@ -2110,6 +2159,7 @@ pub const Subprocess = struct { ), .stdio_pipes = spawned.extra_pipes.moveToUnmanaged(), .on_exit_callback = if (on_exit_callback != .zero) JSC.Strong.create(on_exit_callback, globalThis) else .{}, + .on_disconnect_callback = if (on_disconnect_callback != .zero) JSC.Strong.create(on_disconnect_callback, globalThis) else .{}, .ipc_data = if (!is_sync) if (maybe_ipc_mode) |ipc_mode| if (Environment.isWindows) .{ @@ -2127,21 +2177,28 @@ pub const Subprocess = struct { .is_sync = is_sync, }, }; + subprocess.ref(); // + one ref for the process subprocess.process.setExitHandler(subprocess); if (subprocess.ipc_data) |*ipc_data| { if (Environment.isPosix) { - posix_ipc_info.ext(*Subprocess).* = subprocess; + if (posix_ipc_info.ext(*Subprocess)) |ctx| { + ctx.* = subprocess; + subprocess.ref(); // + one ref for the IPC + } } else { + subprocess.ref(); // + one ref for the IPC + if (ipc_data.configureServer( Subprocess, subprocess, - windows_ipc_env_buf["NODE_CHANNEL_FD=".len..], + subprocess.stdio_pipes.items[@intCast(ipc_channel)].buffer, ).asErr()) |err| { - process_allocator.destroy(subprocess); + subprocess.deref(); globalThis.throwValue(err.toJSC(globalThis)); return .zero; } + subprocess.stdio_pipes.items[@intCast(ipc_channel)] = .unavailable; } ipc_data.writeVersionPacket(); } @@ -2254,10 +2311,13 @@ pub const Subprocess = struct { return sync_value; } + const node_cluster_binding = @import("./../../node/node_cluster_binding.zig"); + pub fn handleIPCMessage( this: *Subprocess, message: IPC.DecodedIPCMessage, ) void { + IPClog("Subprocess#handleIPCMessage", .{}); switch (message) { // In future versions we can read this in order to detect version mismatches, // or disable future optimizations if the subprocess is old. @@ -2275,16 +2335,33 @@ pub const Subprocess = struct { ); } }, + .internal => |data| { + IPC.log("Received IPC internal message from child", .{}); + node_cluster_binding.handleInternalMessagePrimary(this.globalThis, this, data); + }, } } pub fn handleIPCClose(this: *Subprocess) void { - this.ipc_data = null; + IPClog("Subprocess#handleIPCClose", .{}); this.updateHasPendingActivity(); + defer this.deref(); + var ok = false; + if (this.ipc()) |ipc_data| { + ok = true; + ipc_data.internal_msg_queue.deinit(); + } + this.ipc_data = null; + + const this_jsvalue = this.this_jsvalue; + this_jsvalue.ensureStillAlive(); + if (this.on_disconnect_callback.trySwap()) |callback| { + this.globalThis.bunVM().eventLoop().runCallback(callback, this.globalThis, this_jsvalue, &.{JSValue.jsBoolean(ok)}); + } } - pub fn ipc(this: *Subprocess) *IPC.IPCData { - return &this.ipc_data.?; + pub fn ipc(this: *Subprocess) ?*IPC.IPCData { + return &(this.ipc_data orelse return null); } pub const IPCHandler = IPC.NewIPCHandler(Subprocess); diff --git a/src/bun.js/api/bun/udp_socket.zig b/src/bun.js/api/bun/udp_socket.zig index 3108f81304bdd..c2984b56292ca 100644 --- a/src/bun.js/api/bun/udp_socket.zig +++ b/src/bun.js/api/bun/udp_socket.zig @@ -27,7 +27,7 @@ fn onClose(socket: *uws.udp.Socket) callconv(.C) void { const this: *UDPSocket = bun.cast(*UDPSocket, socket.user().?); this.closed = true; - this.poll_ref.unref(this.globalThis.bunVM()); + this.poll_ref.disable(); _ = this.js_refcount.fetchSub(1, .monotonic); } @@ -38,10 +38,13 @@ fn onDrain(socket: *uws.udp.Socket) callconv(.C) void { const callback = this.config.on_drain; if (callback == .zero) return; - const result = callback.callWithThis(this.globalThis, this.thisValue, &[_]JSValue{this.thisValue}); - if (result.toError()) |err| { - _ = this.callErrorHandler(.zero, &[_]JSValue{err}); - } + const vm = JSC.VirtualMachine.get(); + const event_loop = vm.eventLoop(); + event_loop.enter(); + defer event_loop.exit(); + _ = callback.call(this.globalThis, this.thisValue, &.{this.thisValue}) catch |err| { + _ = this.callErrorHandler(.zero, &.{this.globalThis.takeException(err)}); + }; } fn onData(socket: *uws.udp.Socket, buf: *uws.udp.PacketBuffer, packets: c_int) callconv(.C) void { @@ -87,16 +90,14 @@ fn onData(socket: *uws.udp.Socket, buf: *uws.udp.PacketBuffer, packets: c_int) c _ = udpSocket.js_refcount.fetchAdd(1, .monotonic); defer _ = udpSocket.js_refcount.fetchSub(1, .monotonic); - const result = callback.callWithThis(globalThis, udpSocket.thisValue, &[_]JSValue{ + _ = callback.call(globalThis, udpSocket.thisValue, &.{ udpSocket.thisValue, udpSocket.config.binary_type.toJS(slice, globalThis), JSC.jsNumber(port), - JSC.ZigString.init(std.mem.span(hostname.?)).toValueAuto(globalThis), - }); - - if (result.toError()) |err| { - _ = udpSocket.callErrorHandler(.zero, &[_]JSValue{err}); - } + JSC.ZigString.init(std.mem.span(hostname.?)).toJS(globalThis), + }) catch |err| { + _ = udpSocket.callErrorHandler(.zero, &.{udpSocket.globalThis.takeException(err)}); + }; } } @@ -280,7 +281,7 @@ pub const UDPSocket = struct { pub usingnamespace JSC.Codegen.JSUDPSocket; - pub fn constructor(globalThis: *JSGlobalObject, _: *CallFrame) callconv(.C) ?*This { + pub fn constructor(globalThis: *JSGlobalObject, _: *CallFrame) ?*This { globalThis.throw("Cannot construct UDPSocket", .{}); return null; } @@ -364,15 +365,12 @@ pub const UDPSocket = struct { return false; } - const result = callback.callWithThis(globalThis, thisValue, err); - if (result.isAnyError()) { - _ = vm.uncaughtException(globalThis, result, false); - } + _ = callback.call(globalThis, thisValue, err) catch |e| globalThis.reportActiveExceptionAsUnhandled(e); return true; } - pub fn sendMany(this: *This, globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn sendMany(this: *This, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { if (this.closed) { globalThis.throw("Socket is closed", .{}); return .zero; @@ -462,7 +460,7 @@ pub const UDPSocket = struct { this: *This, globalThis: *JSGlobalObject, callframe: *CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.closed) { globalThis.throw("Socket is closed", .{}); return .zero; @@ -492,20 +490,19 @@ pub const UDPSocket = struct { }; const payload_arg = arguments.ptr[0]; - var payload = brk: { + var payload_str = JSC.ZigString.Slice.empty; + defer payload_str.deinit(); + const payload = brk: { if (payload_arg.asArrayBuffer(globalThis)) |array_buffer| { - break :brk bun.JSC.ZigString.Slice{ - .ptr = array_buffer.ptr, - .len = array_buffer.len, - }; + break :brk array_buffer.slice(); } else if (payload_arg.isString()) { - break :brk payload_arg.asString().toSlice(globalThis, bun.default_allocator); + payload_str = payload_arg.asString().toSlice(globalThis, bun.default_allocator); + break :brk payload_str.slice(); } else { globalThis.throwInvalidArguments("Expected ArrayBufferView or string as first argument", .{}); return .zero; } }; - defer payload.deinit(); var addr: std.posix.sockaddr.storage = std.mem.zeroes(std.posix.sockaddr.storage); const addr_ptr = brk: { @@ -566,7 +563,7 @@ pub const UDPSocket = struct { address: JSValue, }; - pub fn ref(this: *This, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn ref(this: *This, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { if (!this.closed) { this.poll_ref.ref(globalThis.bunVM()); } @@ -574,10 +571,8 @@ pub const UDPSocket = struct { return .undefined; } - pub fn unref(this: *This, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { - if (!this.closed) { - this.poll_ref.unref(globalThis.bunVM()); - } + pub fn unref(this: *This, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + this.poll_ref.unref(globalThis.bunVM()); return .undefined; } @@ -586,13 +581,13 @@ pub const UDPSocket = struct { this: *This, _: *JSGlobalObject, _: *CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (!this.closed) this.socket.close(); return .undefined; } - pub fn reload(this: *This, globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn reload(this: *This, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const args = callframe.arguments(1); if (args.len < 1) { @@ -613,16 +608,16 @@ pub const UDPSocket = struct { return .undefined; } - pub fn getClosed(this: *This, _: *JSGlobalObject) callconv(.C) JSValue { + pub fn getClosed(this: *This, _: *JSGlobalObject) JSValue { return JSValue.jsBoolean(this.closed); } - pub fn getHostname(this: *This, _: *JSGlobalObject) callconv(.C) JSValue { + pub fn getHostname(this: *This, _: *JSGlobalObject) JSValue { const hostname = JSC.ZigString.init(this.config.hostname); - return hostname.toValueGC(this.globalThis); + return hostname.toJS(this.globalThis); } - pub fn getPort(this: *This, _: *JSGlobalObject) callconv(.C) JSValue { + pub fn getPort(this: *This, _: *JSGlobalObject) JSValue { if (this.closed) return .undefined; return JSValue.jsNumber(this.socket.boundPort()); } @@ -639,7 +634,7 @@ pub const UDPSocket = struct { return bun.String.createLatin1(slice).toJS(globalThis); } - pub fn getAddress(this: *This, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn getAddress(this: *This, globalThis: *JSGlobalObject) JSValue { if (this.closed) return .undefined; var buf: [64]u8 = [_]u8{0} ** 64; var length: i32 = 64; @@ -655,7 +650,7 @@ pub const UDPSocket = struct { ); } - pub fn getRemoteAddress(this: *This, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getRemoteAddress(this: *This, globalThis: *JSC.JSGlobalObject) JSC.JSValue { if (this.closed) return .undefined; const connect_info = this.connect_info orelse return .undefined; var buf: [64]u8 = [_]u8{0} ** 64; @@ -674,11 +669,11 @@ pub const UDPSocket = struct { pub fn getBinaryType( this: *This, globalThis: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return switch (this.config.binary_type) { - .Buffer => JSC.ZigString.init("buffer").toValueGC(globalThis), - .Uint8Array => JSC.ZigString.init("uint8array").toValueGC(globalThis), - .ArrayBuffer => JSC.ZigString.init("arraybuffer").toValueGC(globalThis), + .Buffer => JSC.ZigString.init("buffer").toJS(globalThis), + .Uint8Array => JSC.ZigString.init("uint8array").toJS(globalThis), + .ArrayBuffer => JSC.ZigString.init("arraybuffer").toJS(globalThis), else => @panic("Invalid binary type"), }; } @@ -697,7 +692,7 @@ pub const UDPSocket = struct { this.destroy(); } - pub fn jsConnect(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsConnect(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { const args = callFrame.arguments(2); const this = callFrame.this().as(UDPSocket) orelse { @@ -747,7 +742,7 @@ pub const UDPSocket = struct { return .undefined; } - pub fn jsDisconnect(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsDisconnect(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { const this = callFrame.this().as(UDPSocket) orelse { globalObject.throwInvalidArguments("Expected UDPSocket as 'this'", .{}); return .zero; diff --git a/src/bun.js/api/bun/x509.zig b/src/bun.js/api/bun/x509.zig index 14e50d75165bd..c5c63ddc9bbed 100644 --- a/src/bun.js/api/bun/x509.zig +++ b/src/bun.js/api/bun/x509.zig @@ -56,17 +56,17 @@ fn x509GetNameObject(globalObject: *JSGlobalObject, name: ?*BoringSSL.X509_NAME) // accurately. if (result.getTruthy(globalObject, name_slice)) |value| { if (value.jsType().isArray()) { - value.push(globalObject, JSC.ZigString.fromUTF8(value_slice).toValueGC(globalObject)); + value.push(globalObject, JSC.ZigString.fromUTF8(value_slice).toJS(globalObject)); } else { const prop_name = JSC.ZigString.fromUTF8(name_slice); const array = JSValue.createEmptyArray(globalObject, 2); array.putIndex(globalObject, 0, value); - array.putIndex(globalObject, 1, JSC.ZigString.fromUTF8(value_slice).toValueGC(globalObject)); + array.putIndex(globalObject, 1, JSC.ZigString.fromUTF8(value_slice).toJS(globalObject)); result.put(globalObject, &prop_name, array); } } else { const prop_name = JSC.ZigString.fromUTF8(name_slice); - result.put(globalObject, &prop_name, JSC.ZigString.fromUTF8(value_slice).toValueGC(globalObject)); + result.put(globalObject, &prop_name, JSC.ZigString.fromUTF8(value_slice).toJS(globalObject)); } } return result; @@ -354,7 +354,7 @@ fn x509GetSubjectAltNameString(globalObject: *JSGlobalObject, bio: *BoringSSL.BI return JSValue.jsNull(); } - return JSC.ZigString.fromUTF8(bio.slice()).toValueGC(globalObject); + return JSC.ZigString.fromUTF8(bio.slice()).toJS(globalObject); } fn x509GetInfoAccessString(globalObject: *JSGlobalObject, bio: *BoringSSL.BIO, cert: *BoringSSL.X509) JSValue { @@ -368,7 +368,7 @@ fn x509GetInfoAccessString(globalObject: *JSGlobalObject, bio: *BoringSSL.BIO, c return JSValue.jsNull(); } - return JSC.ZigString.fromUTF8(bio.slice()).toValueGC(globalObject); + return JSC.ZigString.fromUTF8(bio.slice()).toJS(globalObject); } fn addFingerprintDigest(md: []const u8, mdSize: c_uint, fingerprint: []u8) usize { @@ -394,7 +394,7 @@ fn getFingerprintDigest(cert: *BoringSSL.X509, method: *const BoringSSL.EVP_MD, if (BoringSSL.X509_digest(cert, method, @as([*c]u8, @ptrCast(&md)), &md_size) != 0) { const length = addFingerprintDigest(&md, md_size, &fingerprint); - return JSC.ZigString.fromUTF8(fingerprint[0..length]).toValueGC(globalObject); + return JSC.ZigString.fromUTF8(fingerprint[0..length]).toJS(globalObject); } return JSValue.jsUndefined(); } @@ -409,7 +409,7 @@ fn getSerialNumber(cert: *BoringSSL.X509, globalObject: *JSGlobalObject) JSValue const slice = data[0..bun.len(data)]; // BoringSSL prints the hex value of the serialNumber in lower case, but we need upper case toUpper(slice); - return JSC.ZigString.fromUTF8(slice).toValueGC(globalObject); + return JSC.ZigString.fromUTF8(slice).toJS(globalObject); } } } @@ -473,13 +473,13 @@ pub fn toJS(cert: *BoringSSL.X509, globalObject: *JSGlobalObject) JSValue { const slice = bio.slice(); // BoringSSL prints the hex value of the modulus in lower case, but we need upper case toUpper(slice); - const modulus = JSC.ZigString.fromUTF8(slice).toValueGC(globalObject); + const modulus = JSC.ZigString.fromUTF8(slice).toJS(globalObject); _ = BoringSSL.BIO_reset(bio); result.put(globalObject, ZigString.static("modulus"), modulus); const exponent_word = BoringSSL.BN_get_word(e); _ = BoringSSL.BIO_printf(bio, "0x" ++ BoringSSL.BN_HEX_FMT1, exponent_word); - const exponent = JSC.ZigString.fromUTF8(bio.slice()).toValueGC(globalObject); + const exponent = JSC.ZigString.fromUTF8(bio.slice()).toJS(globalObject); _ = BoringSSL.BIO_reset(bio); result.put(globalObject, ZigString.static("exponent"), exponent); @@ -534,11 +534,11 @@ pub fn toJS(cert: *BoringSSL.X509, globalObject: *JSGlobalObject) JSValue { // Curve is well-known, get its OID and NIST nick-name (if it has one). const asn1Curve_str = BoringSSL.OBJ_nid2sn(nid); if (asn1Curve_str != null) { - result.put(globalObject, ZigString.static("asn1Curve"), JSC.ZigString.fromUTF8(asn1Curve_str[0..bun.len(asn1Curve_str)]).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("asn1Curve"), JSC.ZigString.fromUTF8(asn1Curve_str[0..bun.len(asn1Curve_str)]).toJS(globalObject)); } const nistCurve_str = BoringSSL.EC_curve_nid2nist(nid); if (nistCurve_str != null) { - result.put(globalObject, ZigString.static("nistCurve"), JSC.ZigString.fromUTF8(nistCurve_str[0..bun.len(nistCurve_str)]).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("nistCurve"), JSC.ZigString.fromUTF8(nistCurve_str[0..bun.len(nistCurve_str)]).toJS(globalObject)); } } } @@ -546,11 +546,11 @@ pub fn toJS(cert: *BoringSSL.X509, globalObject: *JSGlobalObject) JSValue { else => {}, } _ = BoringSSL.ASN1_TIME_print(bio, BoringSSL.X509_get0_notBefore(cert)); - result.put(globalObject, ZigString.static("valid_from"), JSC.ZigString.fromUTF8(bio.slice()).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("valid_from"), JSC.ZigString.fromUTF8(bio.slice()).toJS(globalObject)); _ = BoringSSL.BIO_reset(bio); _ = BoringSSL.ASN1_TIME_print(bio, BoringSSL.X509_get0_notAfter(cert)); - result.put(globalObject, ZigString.static("valid_to"), JSC.ZigString.fromUTF8(bio.slice()).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("valid_to"), JSC.ZigString.fromUTF8(bio.slice()).toJS(globalObject)); _ = BoringSSL.BIO_reset(bio); result.put(globalObject, ZigString.static("fingerprint"), getFingerprintDigest(cert, BoringSSL.EVP_sha1(), globalObject)); diff --git a/src/bun.js/api/crypto.classes.ts b/src/bun.js/api/crypto.classes.ts index 4c5e9610d87b2..163447afee54e 100644 --- a/src/bun.js/api/crypto.classes.ts +++ b/src/bun.js/api/crypto.classes.ts @@ -42,11 +42,13 @@ export default [ proto: { getRandomValues: { fn: "getRandomValues", - DOMJIT: { - returns: "JSValue", - "pure": false, - args: ["JSUint8Array"], - }, + // https://discord.com/channels/876711213126520882/1276103693665828894/1276133319033229363 + // https://discord.com/channels/876711213126520882/1276103693665828894/1276127092047609919 + // DOMJIT: { + // returns: "JSValue", + // "pure": false, + // args: ["JSUint8Array"], + // }, }, randomUUID: { fn: "randomUUID", diff --git a/src/bun.js/api/ffi-stdalign.h b/src/bun.js/api/ffi-stdalign.h new file mode 100644 index 0000000000000..03c59b5dca472 --- /dev/null +++ b/src/bun.js/api/ffi-stdalign.h @@ -0,0 +1,15 @@ +#ifndef _STDALIGN_H +#define _STDALIGN_H + +#if __STDC_VERSION__ < 201112L && (defined(__GNUC__) || defined(__TINYC__)) +#define _Alignas(t) __attribute__((__aligned__(t))) +#define _Alignof(t) __alignof__(t) +#endif + +#define alignas _Alignas +#define alignof _Alignof + +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 + +#endif /* _STDALIGN_H */ diff --git a/src/bun.js/api/ffi-stdarg.h b/src/bun.js/api/ffi-stdarg.h new file mode 100644 index 0000000000000..aa784da243026 --- /dev/null +++ b/src/bun.js/api/ffi-stdarg.h @@ -0,0 +1,14 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef __builtin_va_list va_list; +#define va_start __builtin_va_start +#define va_arg __builtin_va_arg +#define va_copy __builtin_va_copy +#define va_end __builtin_va_end + +/* fix a buggy dependency on GCC in libio.h */ +typedef va_list __gnuc_va_list; +#define _VA_LIST_DEFINED + +#endif /* _STDARG_H */ diff --git a/src/bun.js/api/ffi-stdatomic.h b/src/bun.js/api/ffi-stdatomic.h new file mode 100644 index 0000000000000..7ac662816be1e --- /dev/null +++ b/src/bun.js/api/ffi-stdatomic.h @@ -0,0 +1,180 @@ +/* This file is derived from clang's stdatomic.h */ + +/*===---- stdatomic.h - Standard header for atomic types and operations -----=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef _STDATOMIC_H +#define _STDATOMIC_H + +#include +#include +#include + +#define __ATOMIC_RELAXED 0 +#define __ATOMIC_CONSUME 1 +#define __ATOMIC_ACQUIRE 2 +#define __ATOMIC_RELEASE 3 +#define __ATOMIC_ACQ_REL 4 +#define __ATOMIC_SEQ_CST 5 + +/* Memory ordering */ +typedef enum { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST, +} memory_order; + +/* Atomic typedefs */ +typedef _Atomic(_Bool) atomic_bool; +typedef _Atomic(char) atomic_char; +typedef _Atomic(signed char) atomic_schar; +typedef _Atomic(unsigned char) atomic_uchar; +typedef _Atomic(short) atomic_short; +typedef _Atomic(unsigned short) atomic_ushort; +typedef _Atomic(int) atomic_int; +typedef _Atomic(unsigned int) atomic_uint; +typedef _Atomic(long) atomic_long; +typedef _Atomic(unsigned long) atomic_ulong; +typedef _Atomic(long long) atomic_llong; +typedef _Atomic(unsigned long long) atomic_ullong; +typedef _Atomic(uint_least16_t) atomic_char16_t; +typedef _Atomic(uint_least32_t) atomic_char32_t; +typedef _Atomic(wchar_t) atomic_wchar_t; +typedef _Atomic(int_least8_t) atomic_int_least8_t; +typedef _Atomic(uint_least8_t) atomic_uint_least8_t; +typedef _Atomic(int_least16_t) atomic_int_least16_t; +typedef _Atomic(uint_least16_t) atomic_uint_least16_t; +typedef _Atomic(int_least32_t) atomic_int_least32_t; +typedef _Atomic(uint_least32_t) atomic_uint_least32_t; +typedef _Atomic(int_least64_t) atomic_int_least64_t; +typedef _Atomic(uint_least64_t) atomic_uint_least64_t; +typedef _Atomic(int_fast8_t) atomic_int_fast8_t; +typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t; +typedef _Atomic(int_fast16_t) atomic_int_fast16_t; +typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t; +typedef _Atomic(int_fast32_t) atomic_int_fast32_t; +typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t; +typedef _Atomic(int_fast64_t) atomic_int_fast64_t; +typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t; +typedef _Atomic(intptr_t) atomic_intptr_t; +typedef _Atomic(uintptr_t) atomic_uintptr_t; +typedef _Atomic(size_t) atomic_size_t; +typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; +typedef _Atomic(intmax_t) atomic_intmax_t; +typedef _Atomic(uintmax_t) atomic_uintmax_t; + +/* Atomic flag */ +typedef struct { + atomic_bool value; +} atomic_flag; + +#define ATOMIC_FLAG_INIT {0} +#define ATOMIC_VAR_INIT(value) (value) + +#define atomic_flag_test_and_set_explicit(object, order) \ + __atomic_test_and_set((void *)(&((object)->value)), order) +#define atomic_flag_test_and_set(object) \ + atomic_flag_test_and_set_explicit(object, __ATOMIC_SEQ_CST) + +#define atomic_flag_clear_explicit(object, order) \ + __atomic_clear((bool *)(&((object)->value)), order) +#define atomic_flag_clear(object) \ + atomic_flag_clear_explicit(object, __ATOMIC_SEQ_CST) + +/* Generic routines */ +#define atomic_init(object, desired) \ + atomic_store_explicit(object, desired, __ATOMIC_RELAXED) + +#define atomic_store_explicit(object, desired, order) \ + ({ \ + __typeof__(object) ptr = (object); \ + __typeof__(*ptr) tmp = (desired); \ + __atomic_store(ptr, &tmp, (order)); \ + }) +#define atomic_store(object, desired) \ + atomic_store_explicit(object, desired, __ATOMIC_SEQ_CST) + +#define atomic_load_explicit(object, order) \ + ({ \ + __typeof__(object) ptr = (object); \ + __typeof__(*ptr) tmp; \ + __atomic_load(ptr, &tmp, (order)); \ + tmp; \ + }) +#define atomic_load(object) atomic_load_explicit(object, __ATOMIC_SEQ_CST) + +#define atomic_exchange_explicit(object, desired, order) \ + ({ \ + __typeof__(object) ptr = (object); \ + __typeof__(*ptr) val = (desired); \ + __typeof__(*ptr) tmp; \ + __atomic_exchange(ptr, &val, &tmp, (order)); \ + tmp; \ + }) +#define atomic_exchange(object, desired) \ + atomic_exchange_explicit(object, desired, __ATOMIC_SEQ_CST) + +#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ + success, failure) \ + ({ \ + __typeof__(object) ptr = (object); \ + __typeof__(*ptr) tmp = desired; \ + __atomic_compare_exchange(ptr, expected, &tmp, 0, success, failure); \ + }) +#define atomic_compare_exchange_strong(object, expected, desired) \ + atomic_compare_exchange_strong_explicit(object, expected, desired, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ + success, failure) \ + ({ \ + __typeof__(object) ptr = (object); \ + __typeof__(*ptr) tmp = desired; \ + __atomic_compare_exchange(ptr, expected, &tmp, 1, success, failure); \ + }) +#define atomic_compare_exchange_weak(object, expected, desired) \ + atomic_compare_exchange_weak_explicit(object, expected, desired, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define atomic_fetch_add(object, operand) \ + __atomic_fetch_add(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit __atomic_fetch_add + +#define atomic_fetch_sub(object, operand) \ + __atomic_fetch_sub(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub_explicit __atomic_fetch_sub + +#define atomic_fetch_or(object, operand) \ + __atomic_fetch_or(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_or_explicit __atomic_fetch_or + +#define atomic_fetch_xor(object, operand) \ + __atomic_fetch_xor(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor_explicit __atomic_fetch_xor + +#define atomic_fetch_and(object, operand) \ + __atomic_fetch_and(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_and_explicit __atomic_fetch_and + +extern void atomic_thread_fence(memory_order); +extern void __atomic_thread_fence(memory_order); +#define atomic_thread_fence(order) __atomic_thread_fence(order) +extern void atomic_signal_fence(memory_order); +extern void __atomic_signal_fence(memory_order); +#define atomic_signal_fence(order) __atomic_signal_fence(order) +extern bool __atomic_is_lock_free(size_t size, void *ptr); +#define atomic_is_lock_free(OBJ) __atomic_is_lock_free(sizeof(*(OBJ)), (OBJ)) + +extern bool __atomic_test_and_set(void *, memory_order); +extern void __atomic_clear(bool *, memory_order); + +#endif /* _STDATOMIC_H */ diff --git a/src/bun.js/api/ffi-stdbool.h b/src/bun.js/api/ffi-stdbool.h new file mode 100644 index 0000000000000..89b1643237489 --- /dev/null +++ b/src/bun.js/api/ffi-stdbool.h @@ -0,0 +1,11 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* ISOC99 boolean */ + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif /* _STDBOOL_H */ diff --git a/src/bun.js/api/ffi-stddef.h b/src/bun.js/api/ffi-stddef.h new file mode 100644 index 0000000000000..6356a5c810039 --- /dev/null +++ b/src/bun.js/api/ffi-stddef.h @@ -0,0 +1,44 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ssize_t; +typedef __WCHAR_TYPE__ wchar_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __PTRDIFF_TYPE__ intptr_t; +typedef __SIZE_TYPE__ uintptr_t; + +#if __STDC_VERSION__ >= 201112L +typedef union { + long long __ll; + long double __ld; +} max_align_t; +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#undef offsetof +#define offsetof(type, field) ((size_t) & ((type *)0)->field) + +#if defined __i386__ || defined __x86_64__ +void *alloca(size_t size); +#endif + +#endif + +/* Older glibc require a wint_t from (when requested + by __need_wint_t, as otherwise stddef.h isn't allowed to + define this type). Note that this must be outside the normal + _STDDEF_H guard, so that it works even when we've included the file + already (without requiring wint_t). Some other libs define _WINT_T + if they've already provided that type, so we can use that as guard. + TCC defines __WINT_TYPE__ for us. */ +#if defined(__need_wint_t) +#ifndef _WINT_T +#define _WINT_T +typedef __WINT_TYPE__ wint_t; +#endif +#undef __need_wint_t +#endif diff --git a/src/bun.js/api/ffi-stdnoreturn.h b/src/bun.js/api/ffi-stdnoreturn.h new file mode 100644 index 0000000000000..4d580ea5b85c1 --- /dev/null +++ b/src/bun.js/api/ffi-stdnoreturn.h @@ -0,0 +1,7 @@ +#ifndef _STDNORETURN_H +#define _STDNORETURN_H + +/* ISOC11 noreturn */ +#define noreturn _Noreturn + +#endif /* _STDNORETURN_H */ diff --git a/src/bun.js/api/ffi-tgmath.h b/src/bun.js/api/ffi-tgmath.h new file mode 100644 index 0000000000000..85d81222cb028 --- /dev/null +++ b/src/bun.js/api/ffi-tgmath.h @@ -0,0 +1,89 @@ +/* + * ISO C Standard: 7.22 Type-generic math + */ + +#ifndef _TGMATH_H +#define _TGMATH_H + +#include + +#ifndef __cplusplus +#define __tgmath_real(x, F) \ + _Generic((x), float: F##f, long double: F##l, default: F)(x) +#define __tgmath_real_2_1(x, y, F) \ + _Generic((x), float: F##f, long double: F##l, default: F)(x, y) +#define __tgmath_real_2(x, y, F) \ + _Generic((x) + (y), float: F##f, long double: F##l, default: F)(x, y) +#define __tgmath_real_3_2(x, y, z, F) \ + _Generic((x) + (y), float: F##f, long double: F##l, default: F)(x, y, z) +#define __tgmath_real_3(x, y, z, F) \ + _Generic((x) + (y) + (z), float: F##f, long double: F##l, default: F)(x, y, z) + +/* Functions defined in both and (7.22p4) */ +#define acos(z) __tgmath_real(z, acos) +#define asin(z) __tgmath_real(z, asin) +#define atan(z) __tgmath_real(z, atan) +#define acosh(z) __tgmath_real(z, acosh) +#define asinh(z) __tgmath_real(z, asinh) +#define atanh(z) __tgmath_real(z, atanh) +#define cos(z) __tgmath_real(z, cos) +#define sin(z) __tgmath_real(z, sin) +#define tan(z) __tgmath_real(z, tan) +#define cosh(z) __tgmath_real(z, cosh) +#define sinh(z) __tgmath_real(z, sinh) +#define tanh(z) __tgmath_real(z, tanh) +#define exp(z) __tgmath_real(z, exp) +#define log(z) __tgmath_real(z, log) +#define pow(z1, z2) __tgmath_real_2(z1, z2, pow) +#define sqrt(z) __tgmath_real(z, sqrt) +#define fabs(z) __tgmath_real(z, fabs) + +/* Functions defined in only (7.22p5) */ +#define atan2(x, y) __tgmath_real_2(x, y, atan2) +#define cbrt(x) __tgmath_real(x, cbrt) +#define ceil(x) __tgmath_real(x, ceil) +#define copysign(x, y) __tgmath_real_2(x, y, copysign) +#define erf(x) __tgmath_real(x, erf) +#define erfc(x) __tgmath_real(x, erfc) +#define exp2(x) __tgmath_real(x, exp2) +#define expm1(x) __tgmath_real(x, expm1) +#define fdim(x, y) __tgmath_real_2(x, y, fdim) +#define floor(x) __tgmath_real(x, floor) +#define fma(x, y, z) __tgmath_real_3(x, y, z, fma) +#define fmax(x, y) __tgmath_real_2(x, y, fmax) +#define fmin(x, y) __tgmath_real_2(x, y, fmin) +#define fmod(x, y) __tgmath_real_2(x, y, fmod) +#define frexp(x, y) __tgmath_real_2_1(x, y, frexp) +#define hypot(x, y) __tgmath_real_2(x, y, hypot) +#define ilogb(x) __tgmath_real(x, ilogb) +#define ldexp(x, y) __tgmath_real_2_1(x, y, ldexp) +#define lgamma(x) __tgmath_real(x, lgamma) +#define llrint(x) __tgmath_real(x, llrint) +#define llround(x) __tgmath_real(x, llround) +#define log10(x) __tgmath_real(x, log10) +#define log1p(x) __tgmath_real(x, log1p) +#define log2(x) __tgmath_real(x, log2) +#define logb(x) __tgmath_real(x, logb) +#define lrint(x) __tgmath_real(x, lrint) +#define lround(x) __tgmath_real(x, lround) +#define nearbyint(x) __tgmath_real(x, nearbyint) +#define nextafter(x, y) __tgmath_real_2(x, y, nextafter) +#define nexttoward(x, y) __tgmath_real_2(x, y, nexttoward) +#define remainder(x, y) __tgmath_real_2(x, y, remainder) +#define remquo(x, y, z) __tgmath_real_3_2(x, y, z, remquo) +#define rint(x) __tgmath_real(x, rint) +#define round(x) __tgmath_real(x, round) +#define scalbln(x, y) __tgmath_real_2_1(x, y, scalbln) +#define scalbn(x, y) __tgmath_real_2_1(x, y, scalbn) +#define tgamma(x) __tgmath_real(x, tgamma) +#define trunc(x) __tgmath_real(x, trunc) + +/* Functions defined in only (7.22p6) +#define carg(z) __tgmath_cplx_only(z, carg) +#define cimag(z) __tgmath_cplx_only(z, cimag) +#define conj(z) __tgmath_cplx_only(z, conj) +#define cproj(z) __tgmath_cplx_only(z, cproj) +#define creal(z) __tgmath_cplx_only(z, creal) +*/ +#endif /* __cplusplus */ +#endif /* _TGMATH_H */ diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 3736bdc615c6f..6524f10526c42 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -8,6 +8,7 @@ const Global = bun.Global; const strings = bun.strings; const string = bun.string; const Output = bun.Output; +const debug = Output.scoped(.TCC, false); const MutableString = bun.MutableString; const std = @import("std"); const Allocator = std.mem.Allocator; @@ -73,16 +74,758 @@ const VirtualMachine = JSC.VirtualMachine; const IOTask = JSC.IOTask; const TCC = @import("../../tcc.zig"); +extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void; pub const FFI = struct { dylib: ?std.DynLib = null, + relocated_bytes_to_free: ?[]u8 = null, functions: bun.StringArrayHashMapUnmanaged(Function) = .{}, closed: bool = false, + shared_state: ?*TCC.TCCState = null, pub usingnamespace JSC.Codegen.JSFFI; pub fn finalize(_: *FFI) callconv(.C) void {} + const CompileC = struct { + source: Source = .{ .file = "" }, + current_file_for_errors: [:0]const u8 = "", + + libraries: StringArray = .{}, + library_dirs: StringArray = .{}, + include_dirs: StringArray = .{}, + symbols: SymbolsMap = .{}, + define: std.ArrayListUnmanaged([2][:0]const u8) = .{}, + // Flags to replace the default flags + flags: [:0]const u8 = "", + deferred_errors: std.ArrayListUnmanaged([]const u8) = .{}, + + const Source = union(enum) { + file: [:0]const u8, + files: std.ArrayListUnmanaged([:0]const u8), + + pub fn first(this: *const Source) [:0]const u8 { + return switch (this.*) { + .file => this.file, + .files => this.files.items[0], + }; + } + + pub fn deinit(this: *Source, allocator: Allocator) void { + switch (this.*) { + .file => if (this.file.len > 0) allocator.free(this.file), + .files => { + for (this.files.items) |file| { + allocator.free(file); + } + this.files.deinit(allocator); + }, + } + + this.* = .{ .file = "" }; + } + + pub fn add(this: *Source, state: *TCC.TCCState, current_file_for_errors: *[:0]const u8) !void { + switch (this.*) { + .file => { + current_file_for_errors.* = this.file; + if (TCC.tcc_add_file(state, this.file) != 0) { + return error.CompilationError; + } + current_file_for_errors.* = ""; + }, + .files => { + for (this.files.items) |file| { + current_file_for_errors.* = file; + if (TCC.tcc_add_file(state, file) != 0) { + return error.CompilationError; + } + current_file_for_errors.* = ""; + } + }, + } + } + }; + + const stdarg = struct { + extern "C" fn ffi_vfprintf(*anyopaque, [*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_vprintf([*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_fprintf(*anyopaque, [*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_printf([*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_fscanf(*anyopaque, [*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_scanf([*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_sscanf([*:0]const u8, [*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_vsscanf([*:0]const u8, [*:0]const u8, ...) callconv(.C) c_int; + extern "C" fn ffi_fopen([*:0]const u8, [*:0]const u8) callconv(.C) *anyopaque; + extern "C" fn ffi_fclose(*anyopaque) callconv(.C) c_int; + extern "C" fn ffi_fgetc(*anyopaque) callconv(.C) c_int; + extern "C" fn ffi_fputc(c: c_int, *anyopaque) callconv(.C) c_int; + extern "C" fn ffi_feof(*anyopaque) callconv(.C) c_int; + extern "C" fn ffi_fileno(*anyopaque) callconv(.C) c_int; + extern "C" fn ffi_ungetc(c: c_int, *anyopaque) callconv(.C) c_int; + extern "C" fn ffi_ftell(*anyopaque) callconv(.C) c_long; + extern "C" fn ffi_fseek(*anyopaque, c_long, c_int) callconv(.C) c_int; + extern "C" fn ffi_fflush(*anyopaque) callconv(.C) c_int; + + extern "C" fn calloc(nmemb: usize, size: usize) callconv(.C) ?*anyopaque; + extern "C" fn perror([*:0]const u8) callconv(.C) void; + + const mac = if (Environment.isMac) struct { + var ffi_stdinp: *anyopaque = @extern(*anyopaque, .{ .name = "__stdinp" }); + var ffi_stdoutp: *anyopaque = @extern(*anyopaque, .{ .name = "__stdoutp" }); + var ffi_stderrp: *anyopaque = @extern(*anyopaque, .{ .name = "__stderrp" }); + + pub fn inject(state: *TCC.TCCState) void { + _ = TCC.tcc_add_symbol(state, "__stdinp", ffi_stdinp); + _ = TCC.tcc_add_symbol(state, "__stdoutp", ffi_stdoutp); + _ = TCC.tcc_add_symbol(state, "__stderrp", ffi_stderrp); + } + } else struct { + pub fn inject(_: *TCC.TCCState) void {} + }; + + pub fn inject(state: *TCC.TCCState) void { + _ = TCC.tcc_add_symbol(state, "vfprintf", ffi_vfprintf); + _ = TCC.tcc_add_symbol(state, "vprintf", ffi_vprintf); + _ = TCC.tcc_add_symbol(state, "fprintf", ffi_fprintf); + _ = TCC.tcc_add_symbol(state, "printf", ffi_printf); + _ = TCC.tcc_add_symbol(state, "fscanf", ffi_fscanf); + _ = TCC.tcc_add_symbol(state, "scanf", ffi_scanf); + _ = TCC.tcc_add_symbol(state, "sscanf", ffi_sscanf); + _ = TCC.tcc_add_symbol(state, "vsscanf", ffi_vsscanf); + + _ = TCC.tcc_add_symbol(state, "fopen", ffi_fopen); + _ = TCC.tcc_add_symbol(state, "fclose", ffi_fclose); + _ = TCC.tcc_add_symbol(state, "fgetc", ffi_fgetc); + _ = TCC.tcc_add_symbol(state, "fputc", ffi_fputc); + _ = TCC.tcc_add_symbol(state, "feof", ffi_feof); + _ = TCC.tcc_add_symbol(state, "fileno", ffi_fileno); + _ = TCC.tcc_add_symbol(state, "fwrite", std.c.fwrite); + _ = TCC.tcc_add_symbol(state, "ungetc", ffi_ungetc); + _ = TCC.tcc_add_symbol(state, "ftell", ffi_ftell); + _ = TCC.tcc_add_symbol(state, "fseek", ffi_fseek); + _ = TCC.tcc_add_symbol(state, "fflush", ffi_fflush); + _ = TCC.tcc_add_symbol(state, "malloc", std.c.malloc); + _ = TCC.tcc_add_symbol(state, "free", std.c.free); + _ = TCC.tcc_add_symbol(state, "fread", std.c.fread); + _ = TCC.tcc_add_symbol(state, "realloc", std.c.realloc); + _ = TCC.tcc_add_symbol(state, "calloc", calloc); + _ = TCC.tcc_add_symbol(state, "perror", perror); + + if (Environment.isPosix) { + _ = TCC.tcc_add_symbol(state, "posix_memalign", std.c.posix_memalign); + _ = TCC.tcc_add_symbol(state, "dlopen", std.c.dlopen); + _ = TCC.tcc_add_symbol(state, "dlclose", std.c.dlclose); + _ = TCC.tcc_add_symbol(state, "dlsym", std.c.dlsym); + _ = TCC.tcc_add_symbol(state, "dlerror", std.c.dlerror); + } + + mac.inject(state); + } + }; + + pub fn handleCompilationError(this: *CompileC, message: ?[*:0]const u8) callconv(.C) void { + var msg = std.mem.span(message orelse ""); + if (msg.len == 0) return; + + var offset: usize = 0; + // the message we get from TCC sometimes has garbage in it + // i think because we're doing in-memory compilation + while (offset < msg.len) : (offset += 1) { + if (msg[offset] > 0x20 and msg[offset] < 0x7f) break; + } + msg = msg[offset..]; + + this.deferred_errors.append(bun.default_allocator, bun.default_allocator.dupe(u8, msg) catch bun.outOfMemory()) catch bun.outOfMemory(); + } + + pub const default_tcc_options: [:0]const u8 = "-std=c11 -Wl,--export-all-symbols -g -O2"; + + var cached_default_system_include_dir: [:0]const u8 = ""; + var cached_default_system_library_dir: [:0]const u8 = ""; + var cached_default_system_include_dir_once = std.once(getSystemRootDirOnce); + fn getSystemRootDirOnce() void { + if (Environment.isMac) { + var which_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + + var process = bun.spawnSync(&.{ + .stdout = .buffer, + .stdin = .ignore, + .stderr = .ignore, + .argv = &.{ + bun.which(&which_buf, bun.sliceTo(std.c.getenv("PATH") orelse "", 0), Fs.FileSystem.instance.top_level_dir, "xcrun") orelse "/usr/bin/xcrun", + "-sdk", + "macosx", + "-show-sdk-path", + }, + .envp = std.c.environ, + }) catch return; + if (process == .result) { + defer process.result.deinit(); + if (process.result.isOK()) { + const stdout = process.result.stdout.items; + if (stdout.len > 0) { + cached_default_system_include_dir = bun.default_allocator.dupeZ(u8, strings.trim(stdout, "\n\r")) catch return; + } + } + } + } else if (Environment.isLinux) { + if (Environment.isX64) { + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/include/x86_64-linux-gnu").isTrue()) { + cached_default_system_include_dir = "/usr/include/x86_64-linux-gnu"; + } + + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/lib/x86_64-linux-gnu").isTrue()) { + cached_default_system_library_dir = "/usr/lib/x86_64-linux-gnu"; + } + } else if (Environment.isAarch64) { + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/include/aarch64-linux-gnu").isTrue()) { + cached_default_system_include_dir = "/usr/include/aarch64-linux-gnu"; + } + + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/lib/aarch64-linux-gnu").isTrue()) { + cached_default_system_library_dir = "/usr/lib/aarch64-linux-gnu"; + } + } + } + } + + fn getSystemIncludeDir() ?[:0]const u8 { + cached_default_system_include_dir_once.call(); + if (cached_default_system_include_dir.len == 0) return null; + return cached_default_system_include_dir; + } + + fn getSystemLibraryDir() ?[:0]const u8 { + cached_default_system_include_dir_once.call(); + if (cached_default_system_library_dir.len == 0) return null; + return cached_default_system_library_dir; + } + + pub fn compile(this: *CompileC, globalThis: *JSGlobalObject) !struct { *TCC.TCCState, []u8 } { + const state = TCC.tcc_new() orelse { + globalThis.throw("TinyCC failed to initialize", .{}); + return error.JSException; + }; + TCC.tcc_set_error_func(state, this, @ptrCast(&handleCompilationError)); + if (this.flags.len > 0) { + TCC.tcc_set_options(state, this.flags.ptr); + } else if (bun.getenvZ("BUN_TCC_OPTIONS")) |tcc_options| { + TCC.tcc_set_options(state, @ptrCast(tcc_options)); + } else { + TCC.tcc_set_options(state, default_tcc_options); + } + _ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY); + errdefer TCC.tcc_delete(state); + + var pathbuf: [bun.MAX_PATH_BYTES]u8 = undefined; + + if (CompilerRT.dir()) |compiler_rt_dir| { + if (TCC.tcc_add_sysinclude_path(state, compiler_rt_dir) == -1) { + debug("TinyCC failed to add sysinclude path", .{}); + } + } + + if (Environment.isMac) { + add_system_include_dir: { + const dirs_to_try = [_][]const u8{ + bun.getenvZ("SDKROOT") orelse "", + getSystemIncludeDir() orelse "", + }; + + for (dirs_to_try) |sdkroot| { + if (sdkroot.len > 0) { + const include_dir = bun.path.joinAbsStringBufZ(sdkroot, &pathbuf, &.{ "usr", "include" }, .auto); + if (TCC.tcc_add_sysinclude_path(state, include_dir.ptr) == -1) { + globalThis.throw("TinyCC failed to add sysinclude path", .{}); + return error.JSException; + } + + const lib_dir = bun.path.joinAbsStringBufZ(sdkroot, &pathbuf, &.{ "usr", "lib" }, .auto); + if (TCC.tcc_add_library_path(state, lib_dir.ptr) == -1) { + globalThis.throw("TinyCC failed to add library path", .{}); + return error.JSException; + } + break :add_system_include_dir; + } + } + } + + if (Environment.isAarch64) { + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/opt/homebrew/include").isTrue()) { + if (TCC.tcc_add_include_path(state, "/opt/homebrew/include") == -1) { + debug("TinyCC failed to add library path", .{}); + } + } + + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/opt/homebrew/lib").isTrue()) { + if (TCC.tcc_add_library_path(state, "/opt/homebrew/lib") == -1) { + debug("TinyCC failed to add library path", .{}); + } + } + } + } else if (Environment.isLinux) { + if (getSystemIncludeDir()) |include_dir| { + if (TCC.tcc_add_sysinclude_path(state, include_dir) == -1) { + debug("TinyCC failed to add library path", .{}); + } + } + + if (getSystemLibraryDir()) |library_dir| { + if (TCC.tcc_add_library_path(state, library_dir) == -1) { + debug("TinyCC failed to add library path", .{}); + } + } + } + + if (Environment.isPosix) { + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/local/include").isTrue()) { + if (TCC.tcc_add_sysinclude_path(state, "/usr/local/include") == -1) { + debug("TinyCC failed to add library path", .{}); + } + } + + if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/local/lib").isTrue()) { + if (TCC.tcc_add_library_path(state, "/usr/local/lib") == -1) { + debug("TinyCC failed to add library path", .{}); + } + } + } + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + for (this.include_dirs.items) |include_dir| { + if (TCC.tcc_add_include_path(state, include_dir) == -1) {} + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + } + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + CompilerRT.define(state); + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + for (this.define.items) |define| { + TCC.tcc_define_symbol(state, define[0], define[1]); + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + } + + this.source.add(state, &this.current_file_for_errors) catch { + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } else { + if (!globalThis.hasException()) { + globalThis.throw("TinyCC failed to compile", .{}); + } + return error.JSException; + } + }; + + CompilerRT.inject(state); + stdarg.inject(state); + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + for (this.library_dirs.items) |library_dir| { + if (TCC.tcc_add_library_path(state, library_dir) == -1) {} + } + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + for (this.libraries.items) |library| { + _ = TCC.tcc_add_library(state, library); + + if (this.deferred_errors.items.len > 0) { + break; + } + } + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + const relocation_size = TCC.tcc_relocate(state, null); + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + if (relocation_size < 0) { + globalThis.throw("Unexpected: tcc_relocate returned a negative value", .{}); + return error.JSException; + } + + const bytes: []u8 = try bun.default_allocator.alloc(u8, @as(usize, @intCast(relocation_size))); + // We cannot free these bytes, evidently. + + if (comptime Environment.isAarch64 and Environment.isMac) { + pthread_jit_write_protect_np(false); + } + _ = TCC.tcc_relocate(state, bytes.ptr); + if (comptime Environment.isAarch64 and Environment.isMac) { + pthread_jit_write_protect_np(true); + } + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + for (this.symbols.map.keys(), this.symbols.map.values()) |symbol, *function| { + const duped = bun.default_allocator.dupeZ(u8, symbol) catch bun.outOfMemory(); + defer bun.default_allocator.free(duped); + if (TCC.tcc_get_symbol(state, duped)) |function_ptr| { + function.symbol_from_dynamic_library = function_ptr; + } else { + globalThis.throw("{} is missing from {s}. Was it included in the source code?", .{ bun.fmt.quote(symbol), this.source.first() }); + return error.JSException; + } + } + + if (this.deferred_errors.items.len > 0) { + return error.DeferredErrors; + } + + return .{ state, bytes }; + } + + pub fn deinit(this: *CompileC) void { + this.symbols.deinit(); + + this.libraries.deinit(); + this.library_dirs.deinit(); + this.include_dirs.deinit(); + + for (this.deferred_errors.items) |deferred_error| { + bun.default_allocator.free(deferred_error); + } + this.deferred_errors.clearAndFree(bun.default_allocator); + + for (this.define.items) |define| { + bun.default_allocator.free(define[0]); + if (define[1].len > 0) bun.default_allocator.free(define[1]); + } + this.define.clearAndFree(bun.default_allocator); + + this.source.deinit(bun.default_allocator); + if (this.flags.len > 0) bun.default_allocator.free(this.flags); + this.flags = ""; + } + }; + const SymbolsMap = struct { + map: bun.StringArrayHashMapUnmanaged(Function) = .{}, + pub fn deinit(this: *SymbolsMap) void { + for (this.map.keys()) |key| { + bun.default_allocator.free(@constCast(key)); + } + this.map.clearAndFree(bun.default_allocator); + } + }; + + const StringArray = struct { + items: []const [:0]const u8 = &.{}, + pub fn deinit(this: *StringArray) void { + for (this.items) |item| { + // Attempting to free an empty null-terminated slice will crash if it was a default value + bun.debugAssert(item.len > 0); + + bun.default_allocator.free(@constCast(item)); + } + + if (this.items.len > 0) + bun.default_allocator.free(this.items); + } + + pub fn fromJSArray(globalThis: *JSC.JSGlobalObject, value: JSC.JSValue, comptime property: []const u8) StringArray { + var iter = value.arrayIterator(globalThis); + var items = std.ArrayList([:0]const u8).init(bun.default_allocator); + + while (iter.next()) |val| { + if (!val.isString()) { + for (items.items) |item| { + bun.default_allocator.free(@constCast(item)); + } + items.deinit(); + _ = globalThis.throwInvalidArgumentTypeValue(property, "array of strings", val); + return .{}; + } + const str = val.getZigString(globalThis); + if (str.isEmpty()) continue; + items.append(str.toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory()) catch bun.outOfMemory(); + } + + return .{ .items = items.items }; + } + + pub fn fromJSString(globalThis: *JSC.JSGlobalObject, value: JSC.JSValue, comptime property: []const u8) StringArray { + if (value == .undefined) return .{}; + if (!value.isString()) { + _ = globalThis.throwInvalidArgumentTypeValue(property, "array of strings", value); + return .{}; + } + const str = value.getZigString(globalThis); + if (str.isEmpty()) return .{}; + var items = std.ArrayList([:0]const u8).init(bun.default_allocator); + items.append(str.toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory()) catch bun.outOfMemory(); + return .{ .items = items.items }; + } + + pub fn fromJS(globalThis: *JSC.JSGlobalObject, value: JSC.JSValue, comptime property: []const u8) StringArray { + if (value.isArray()) { + return fromJSArray(globalThis, value, property); + } + return fromJSString(globalThis, value, property); + } + }; + + pub fn Bun__FFI__cc(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(1).slice(); + if (arguments.len == 0 or !arguments[0].isObject()) { + return JSC.toInvalidArguments("Expected object", .{}, globalThis); + } + + // Step 1. compile the user's code + + const object = arguments[0]; + + var compile_c = CompileC{}; + defer { + if (globalThis.hasException()) { + compile_c.deinit(); + } + } + + const symbols_object = object.get(globalThis, "symbols") orelse .undefined; + if (!globalThis.hasException() and (symbols_object == .zero or !symbols_object.isObject())) { + _ = globalThis.throwInvalidArgumentTypeValue("symbols", "object", symbols_object); + } + + if (globalThis.hasException()) { + return .zero; + } + + if (generateSymbols(globalThis, &compile_c.symbols.map, symbols_object) catch JSC.JSValue.zero) |val| { + if (val != .zero and !globalThis.hasException()) + globalThis.throwValue(val); + return .zero; + } + + if (compile_c.symbols.map.count() == 0) { + globalThis.throw("Expected at least one exported symbol", .{}); + return .zero; + } + + if (object.get(globalThis, "library")) |library_value| { + compile_c.libraries = StringArray.fromJS(globalThis, library_value, "library"); + } + + if (globalThis.hasException()) { + return .zero; + } + + if (object.getTruthy(globalThis, "flags")) |flags_value| { + if (flags_value.isArray()) { + var iter = flags_value.arrayIterator(globalThis); + + var flags = std.ArrayList(u8).init(bun.default_allocator); + defer flags.deinit(); + flags.appendSlice(CompileC.default_tcc_options) catch bun.outOfMemory(); + + while (iter.next()) |value| { + if (!value.isString()) { + return globalThis.throwInvalidArgumentTypeValue("flags", "array of strings", value); + } + const slice = value.toSlice(globalThis, bun.default_allocator); + if (slice.len == 0) continue; + defer slice.deinit(); + flags.append(' ') catch bun.outOfMemory(); + flags.appendSlice(slice.slice()) catch bun.outOfMemory(); + } + flags.append(0) catch bun.outOfMemory(); + compile_c.flags = flags.items[0 .. flags.items.len - 1 :0]; + flags = std.ArrayList(u8).init(bun.default_allocator); + } else { + if (!flags_value.isString()) { + return globalThis.throwInvalidArgumentTypeValue("flags", "string", flags_value); + } + + const str = flags_value.getZigString(globalThis); + if (!str.isEmpty()) { + compile_c.flags = str.toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory(); + } + } + } + + if (globalThis.hasException()) { + return .zero; + } + + if (object.getTruthy(globalThis, "define")) |define_value| { + if (define_value.isObject()) { + const Iter = JSC.JSPropertyIterator(.{ .include_value = true, .skip_empty_name = true }); + var iter = Iter.init(globalThis, define_value); + defer iter.deinit(); + while (iter.next()) |entry| { + const key = entry.toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory(); + var owned_value: [:0]const u8 = ""; + if (iter.value != .zero and iter.value != .undefined) { + if (iter.value.isString()) { + const value = iter.value.getZigString(globalThis); + if (value.len > 0) { + owned_value = value.toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory(); + } + } + } + if (globalThis.hasException()) { + bun.default_allocator.free(key); + return .zero; + } + + compile_c.define.append(bun.default_allocator, .{ key, owned_value }) catch bun.outOfMemory(); + } + } + } + + if (globalThis.hasException()) { + return .zero; + } + + if (object.getTruthy(globalThis, "include")) |include_value| { + compile_c.include_dirs = StringArray.fromJS(globalThis, include_value, "include"); + } + + if (globalThis.hasException()) { + return .zero; + } + + if (object.get(globalThis, "source")) |source_value| { + if (source_value.isArray()) { + compile_c.source = .{ .files = .{} }; + var iter = source_value.arrayIterator(globalThis); + while (iter.next()) |value| { + if (!value.isString()) { + return globalThis.throwInvalidArgumentTypeValue("source", "array of strings", value); + } + compile_c.source.files.append(bun.default_allocator, value.getZigString(globalThis).toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory()) catch bun.outOfMemory(); + } + } else if (!source_value.isString()) { + return globalThis.throwInvalidArgumentTypeValue("source", "string", source_value); + } else { + const source_path = source_value.getZigString(globalThis).toOwnedSliceZ(bun.default_allocator) catch bun.outOfMemory(); + compile_c.source.file = source_path; + } + } + + if (globalThis.hasException()) { + return .zero; + } + + // Now we compile the code with tinycc. + var tcc_state: ?*TCC.TCCState, var bytes_to_free_on_error = compile_c.compile(globalThis) catch |err| { + switch (err) { + error.DeferredErrors => { + var combined = std.ArrayList(u8).init(bun.default_allocator); + defer combined.deinit(); + var writer = combined.writer(); + writer.print("{d} errors while compiling {s}\n", .{ compile_c.deferred_errors.items.len, if (compile_c.current_file_for_errors.len > 0) compile_c.current_file_for_errors else compile_c.source.first() }) catch bun.outOfMemory(); + + for (compile_c.deferred_errors.items) |deferred_error| { + writer.print("{s}\n", .{deferred_error}) catch bun.outOfMemory(); + } + + globalThis.throw("{s}", .{combined.items}); + return .zero; + }, + error.JSException => { + return .zero; + }, + error.OutOfMemory => { + globalThis.throwOutOfMemory(); + return .zero; + }, + } + }; + defer { + if (tcc_state) |state| { + TCC.tcc_delete(state); + } + + // TODO: upgrade tinycc because they improved the way memory management works for this + // we are unable to free memory safely in certain cases here. + } + + var obj = JSC.JSValue.createEmptyObject(globalThis, compile_c.symbols.map.count()); + for (compile_c.symbols.map.values()) |*function| { + const function_name = function.base_name.?; + const allocator = bun.default_allocator; + + function.compile(allocator) catch |err| { + if (!globalThis.hasException()) { + const ret = JSC.toInvalidArguments("{s} when translating symbol \"{s}\"", .{ + @errorName(err), + function_name, + }, globalThis); + globalThis.throwValue(ret); + } + + return .zero; + }; + switch (function.step) { + .failed => |err| { + const res = ZigString.init(err.msg).toErrorInstance(globalThis); + globalThis.throwValue(res); + return .zero; + }, + .pending => { + globalThis.throw("Failed to compile (nothing happend!)", .{}); + return .zero; + }, + .compiled => |*compiled| { + const str = ZigString.init(bun.asByteSlice(function_name)); + const cb = JSC.NewRuntimeFunction( + globalThis, + &str, + @as(u32, @intCast(function.arg_types.items.len)), + bun.cast(JSC.JSHostFunctionPtr, compiled.ptr), + false, + true, + ); + compiled.js_function = cb; + obj.put(globalThis, &str, cb); + }, + } + } + + var lib = bun.default_allocator.create(FFI) catch bun.outOfMemory(); + lib.* = .{ + .dylib = null, + .shared_state = tcc_state, + .functions = compile_c.symbols.map, + .relocated_bytes_to_free = bytes_to_free_on_error, + }; + tcc_state = null; + bytes_to_free_on_error = ""; + compile_c.symbols = .{}; + + const js_object = lib.toJS(globalThis); + JSC.Codegen.JSFFI.symbolsValueSetCached(js_object, globalThis, obj); + return js_object; + } + pub fn closeCallback(globalThis: *JSGlobalObject, ctx: JSValue) JSValue { var function = ctx.asPtr(Function); function.deinit(globalThis, bun.default_allocator); @@ -145,7 +888,7 @@ pub const FFI = struct { ) callconv(.C) JSValue { JSC.markBinding(@src()); if (this.closed) { - return JSC.JSValue.jsUndefined(); + return .undefined; } this.closed = true; if (this.dylib) |*dylib| { @@ -153,14 +896,23 @@ pub const FFI = struct { this.dylib = null; } + if (this.shared_state) |state| { + this.shared_state = null; + TCC.tcc_delete(state); + } + const allocator = VirtualMachine.get().allocator; for (this.functions.values()) |*val| { val.deinit(globalThis, allocator); } this.functions.deinit(allocator); + if (this.relocated_bytes_to_free) |relocated_bytes_to_free| { + this.relocated_bytes_to_free = null; + bun.default_allocator.free(relocated_bytes_to_free); + } - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn printCallback(global: *JSGlobalObject, object: JSC.JSValue) JSValue { @@ -185,7 +937,7 @@ pub const FFI = struct { function.printCallbackSourceCode(null, null, &writer) catch { return ZigString.init("Error while printing code").toErrorInstance(global); }; - return ZigString.init(arraylist.items).toValueGC(global); + return ZigString.init(arraylist.items).toJS(global); } pub fn print(global: *JSGlobalObject, object: JSC.JSValue, is_callback_val: ?JSC.JSValue) JSValue { @@ -326,7 +1078,7 @@ pub const FFI = struct { break :brk std.DynLib.open(backup_name) catch { // Then, if that fails, report an error. const system_error = JSC.SystemError{ - .code = bun.String.createUTF8(@tagName(JSC.Node.ErrorCode.ERR_DLOPEN_FAILED)), + .code = bun.String.createUTF8(@tagName(.ERR_DLOPEN_FAILED)), .message = bun.String.createUTF8("Failed to open library. This is usually caused by a missing library or an invalid library path."), .syscall = bun.String.createUTF8("dlopen"), }; @@ -424,7 +1176,7 @@ pub const FFI = struct { return js_object; } - pub fn getSymbols(_: *FFI, _: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getSymbols(_: *FFI, _: *JSC.JSGlobalObject) JSC.JSValue { // This shouldn't be called. The cachedValue is what should be called. return .undefined; } @@ -568,7 +1320,7 @@ pub const FFI = struct { defer type_name.deinit(); abi_types.appendAssumeCapacity(ABIType.label.get(type_name.slice()) orelse { abi_types.clearAndFree(allocator); - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown type {s}", .{type_name.slice()}, global); + return JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "Unknown type {s}", .{type_name.slice()}, global); }); } } @@ -577,11 +1329,11 @@ pub const FFI = struct { var threadsafe = false; - if (value.get(global, "threadsafe")) |threadsafe_value| { + if (value.getTruthy(global, "threadsafe")) |threadsafe_value| { threadsafe = threadsafe_value.toBoolean(); } - if (value.get(global, "returns")) |ret_value| brk: { + if (value.getTruthy(global, "returns")) |ret_value| brk: { if (ret_value.isAnyInt()) { const int = ret_value.toInt32(); switch (int) { @@ -600,10 +1352,15 @@ pub const FFI = struct { defer ret_slice.deinit(); return_type = ABIType.label.get(ret_slice.slice()) orelse { abi_types.clearAndFree(allocator); - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown return type {s}", .{ret_slice.slice()}, global); + return JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "Unknown return type {s}", .{ret_slice.slice()}, global); }; } + if (return_type == ABIType.napi_env) { + abi_types.clearAndFree(allocator); + return ZigString.static("Cannot return napi_env to JavaScript").toErrorInstance(global); + } + if (function.threadsafe and return_type != ABIType.void) { abi_types.clearAndFree(allocator); return ZigString.static("Threadsafe functions must return void").toErrorInstance(global); @@ -648,7 +1405,7 @@ pub const FFI = struct { const value = symbols_iter.value; if (value.isEmptyOrUndefinedOrNull()) { - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{any}\"", .{prop}, global); + return JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{any}\"", .{prop}, global); } var function: Function = .{}; @@ -675,6 +1432,15 @@ pub const FFI = struct { pub var lib_dirZ: [*:0]const u8 = ""; + pub fn needsHandleScope(val: *const Function) bool { + for (val.arg_types.items) |arg| { + if (arg == ABIType.napi_env or arg == ABIType.napi_value) { + return true; + } + } + return val.return_type == ABIType.napi_value; + } + extern "C" fn FFICallbackFunctionWrapper_destroy(*anyopaque) void; pub fn deinit(val: *Function, globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) void { @@ -727,26 +1493,11 @@ pub const FFI = struct { }, }; - const FFI_HEADER: string = @embedFile("./FFI.h"); - pub inline fn ffiHeader() string { - if (comptime Environment.isDebug) { - const dirpath = comptime bun.Environment.base_path ++ (bun.Dirname.dirname(u8, @src().file) orelse ""); - var buf: bun.PathBuffer = undefined; - const user = bun.getUserName(&buf) orelse ""; - const dir = std.mem.replaceOwned( - u8, - default_allocator, - dirpath, - "jarred", - user, - ) catch unreachable; - const runtime_path = std.fs.path.join(default_allocator, &[_]string{ dir, "FFI.h" }) catch unreachable; - const file = std.fs.openFileAbsolute(runtime_path, .{}) catch @panic("Missing bun/src/bun.js/api/FFI.h."); - defer file.close(); - return file.readToEndAlloc(default_allocator, file.getEndPos() catch unreachable) catch unreachable; - } else { - return FFI_HEADER; - } + pub fn ffiHeader() string { + return if (Environment.embed_code) + @embedFile("./FFI.h") + else + bun.runtimeEmbedFile(.src, "bun.js/api/FFI.h"); } pub fn handleTCCError(ctx: ?*anyopaque, message: [*c]const u8) callconv(.C) void { @@ -765,24 +1516,6 @@ pub const FFI = struct { this.step = .{ .failed = .{ .msg = VirtualMachine.get().allocator.dupe(u8, msg) catch unreachable, .allocated = true } }; } - extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void; - - const MyFunctionSStructWorkAround = struct { - JSVALUE_TO_INT64: *const fn (JSValue0: JSC.JSValue) callconv(.C) i64, - JSVALUE_TO_UINT64: *const fn (JSValue0: JSC.JSValue) callconv(.C) u64, - INT64_TO_JSVALUE: *const fn (arg0: *JSC.JSGlobalObject, arg1: i64) callconv(.C) JSC.JSValue, - UINT64_TO_JSVALUE: *const fn (arg0: *JSC.JSGlobalObject, arg1: u64) callconv(.C) JSC.JSValue, - bun_call: *const @TypeOf(JSC.C.JSObjectCallAsFunction), - }; - const headers = @import("../bindings/headers.zig"); - var workaround: MyFunctionSStructWorkAround = if (!JSC.is_bindgen) .{ - .JSVALUE_TO_INT64 = headers.JSC__JSValue__toInt64, - .JSVALUE_TO_UINT64 = headers.JSC__JSValue__toUInt64NoTruncate, - .INT64_TO_JSVALUE = headers.JSC__JSValue__fromInt64NoTruncate, - .UINT64_TO_JSVALUE = headers.JSC__JSValue__fromUInt64NoTruncate, - .bun_call = &JSC.C.JSObjectCallAsFunction, - } else undefined; - const tcc_options = "-std=c11 -nostdlib -Wl,--export-all-symbols" ++ if (Environment.isDebug) " -g" else ""; pub fn compile( @@ -827,7 +1560,7 @@ pub const FFI = struct { const compilation_result = TCC.tcc_compile_string( state, - source_code.items.ptr, + @ptrCast(source_code.items.ptr), ); // did tcc report an error? if (this.step == .failed) { @@ -886,64 +1619,6 @@ pub const FFI = struct { }; return; } - const CompilerRT = struct { - noinline fn memset( - dest: [*]u8, - c: u8, - byte_count: usize, - ) callconv(.C) void { - @memset(dest[0..byte_count], c); - } - - noinline fn memcpy( - noalias dest: [*]u8, - noalias source: [*]const u8, - byte_count: usize, - ) callconv(.C) void { - @memcpy(dest[0..byte_count], source[0..byte_count]); - } - - pub fn define(state: *TCC.TCCState) void { - if (comptime Environment.isX64) { - _ = TCC.tcc_define_symbol(state, "NEEDS_COMPILER_RT_FUNCTIONS", "1"); - // there - _ = TCC.tcc_compile_string(state, @embedFile(("libtcc1.c"))); - } - } - - pub fn inject(state: *TCC.TCCState) void { - JSC.markBinding(@src()); - _ = TCC.tcc_add_symbol(state, "memset", &memset); - _ = TCC.tcc_add_symbol(state, "memcpy", &memcpy); - - _ = TCC.tcc_add_symbol( - state, - "JSVALUE_TO_INT64_SLOW", - workaround.JSVALUE_TO_INT64, - ); - _ = TCC.tcc_add_symbol( - state, - "JSVALUE_TO_UINT64_SLOW", - workaround.JSVALUE_TO_UINT64, - ); - if (!comptime JSC.is_bindgen) { - std.mem.doNotOptimizeAway(headers.JSC__JSValue__toUInt64NoTruncate); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__toInt64); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__fromInt64NoTruncate); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__fromUInt64NoTruncate); - } - _ = TCC.tcc_add_symbol( - state, - "INT64_TO_JSVALUE_SLOW", - workaround.INT64_TO_JSVALUE, - ); - _ = TCC.tcc_add_symbol( - state, - "UINT64_TO_JSVALUE_SLOW", - workaround.UINT64_TO_JSVALUE, - ); - } - }; pub fn compileCallback( this: *Function, @@ -986,7 +1661,7 @@ pub const FFI = struct { const compilation_result = TCC.tcc_compile_string( state, - source_code.items.ptr, + @ptrCast(source_code.items.ptr), ); // did tcc report an error? if (this.step == .failed) { @@ -1082,11 +1757,7 @@ pub const FFI = struct { } } - if (comptime Environment.isRelease) { - try writer.writeAll(bun.asByteSlice(FFI_HEADER)); - } else { - try writer.writeAll(ffiHeader()); - } + try writer.writeAll(ffiHeader()); // -- Generate the FFI function symbol try writer.writeAll("/* --- The Function To Call */\n"); @@ -1111,22 +1782,46 @@ pub const FFI = struct { \\ ); + if (this.needsHandleScope()) { + try writer.writeAll( + \\ void* handleScope = NapiHandleScope__push(JS_GLOBAL_OBJECT, false); + \\ + ); + } + if (this.arg_types.items.len > 0) { try writer.writeAll( \\ LOAD_ARGUMENTS_FROM_CALL_FRAME; \\ ); for (this.arg_types.items, 0..) |arg, i| { - if (arg.needsACastInC()) { + if (arg == .napi_env) { + try writer.print( + \\ napi_env arg{d} = (napi_env)JS_GLOBAL_OBJECT; + \\ argsPtr++; + \\ + , + .{ + i, + }, + ); + } else if (arg == .napi_value) { + try writer.print( + \\ EncodedJSValue arg{d} = {{ .asInt64 = *argsPtr++ }}; + \\ + , + .{ + i, + }, + ); + } else if (arg.needsACastInC()) { if (i < this.arg_types.items.len - 1) { try writer.print( - \\ EncodedJSValue arg{d}; - \\ arg{d}.asInt64 = *argsPtr++; + \\ EncodedJSValue arg{d} = {{ .asInt64 = *argsPtr++ }}; \\ , .{ i, - i, }, ); } else { @@ -1200,6 +1895,13 @@ pub const FFI = struct { try writer.writeAll(" "); + if (this.needsHandleScope()) { + try writer.writeAll( + \\ NapiHandleScope__pop(JS_GLOBAL_OBJECT, handleScope); + \\ + ); + } + try writer.writeAll("return "); if (!(this.return_type == .void)) { @@ -1252,11 +1954,7 @@ pub const FFI = struct { } } - if (comptime Environment.isRelease) { - try writer.writeAll(bun.asByteSlice(FFI_HEADER)); - } else { - try writer.writeAll(ffiHeader()); - } + try writer.writeAll(ffiHeader()); // -- Generate the FFI function symbol try writer.writeAll("\n \n/* --- The Callback Function */\n"); @@ -1364,8 +2062,10 @@ pub const FFI = struct { u64_fast = 16, function = 17, + napi_env = 18, + napi_value = 19, - pub const max = @intFromEnum(ABIType.function); + pub const max = @intFromEnum(ABIType.napi_value); /// Types that we can directly pass through as an `int64_t` pub fn needsACastInC(this: ABIType) bool { @@ -1414,6 +2114,8 @@ pub const FFI = struct { .{ "function", ABIType.function }, .{ "callback", ABIType.function }, .{ "fn", ABIType.function }, + .{ "napi_env", ABIType.napi_env }, + .{ "napi_value", ABIType.napi_value }, }; pub const label = bun.ComptimeStringMap(ABIType, map); const EnumMapFormatter = struct { @@ -1508,6 +2210,15 @@ pub const FFI = struct { try writer.writeAll("(float)"); try writer.writeAll("JSVALUE_TO_FLOAT("); }, + .napi_env => { + try writer.writeAll("(napi_env)JS_GLOBAL_OBJECT"); + return; + }, + .napi_value => { + try writer.writeAll(self.symbol); + try writer.writeAll(".asNapiValue"); + return; + }, } // if (self.fromi64) { // try writer.writeAll("EncodedJSValue{ "); @@ -1557,6 +2268,12 @@ pub const FFI = struct { .float => { try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol}); }, + .napi_env => { + try writer.writeAll("JS_GLOBAL_OBJECT"); + }, + .napi_value => { + try writer.print("((EncodedJSValue) {{.asNapiValue = {s} }} )", .{self.symbol}); + }, } } }; @@ -1599,6 +2316,8 @@ pub const FFI = struct { .float => "float", .char => "char", .void => "void", + .napi_env => "napi_env", + .napi_value => "napi_value", }; } @@ -1624,7 +2343,121 @@ pub const FFI = struct { .float => "float", .char => "char", .void => "void", + .napi_env => "napi_env", + .napi_value => "napi_value", }; } }; }; + +const CompilerRT = struct { + var compiler_rt_dir: [:0]const u8 = ""; + const compiler_rt_sources = struct { + pub const @"stdbool.h" = @embedFile("./ffi-stdbool.h"); + pub const @"stdarg.h" = @embedFile("./ffi-stdarg.h"); + pub const @"stdnoreturn.h" = @embedFile("./ffi-stdnoreturn.h"); + pub const @"stdalign.h" = @embedFile("./ffi-stdalign.h"); + pub const @"tgmath.h" = @embedFile("./ffi-tgmath.h"); + pub const @"stddef.h" = @embedFile("./ffi-stddef.h"); + pub const @"varargs.h" = "// empty"; + }; + + fn createCompilerRTDir() void { + const tmpdir = Fs.FileSystem.instance.tmpdir() catch return; + var bunCC = tmpdir.makeOpenPath("bun-cc", .{}) catch return; + defer bunCC.close(); + + inline for (comptime std.meta.declarations(compiler_rt_sources)) |decl| { + const source = @field(compiler_rt_sources, decl.name); + bunCC.writeFile(.{ + .sub_path = decl.name, + .data = source, + }) catch {}; + } + var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + compiler_rt_dir = bun.default_allocator.dupeZ(u8, bun.getFdPath(bunCC, &path_buf) catch return) catch bun.outOfMemory(); + } + var create_compiler_rt_dir_once = std.once(createCompilerRTDir); + + pub fn dir() ?[:0]const u8 { + create_compiler_rt_dir_once.call(); + if (compiler_rt_dir.len == 0) return null; + return compiler_rt_dir; + } + + const MyFunctionSStructWorkAround = struct { + JSVALUE_TO_INT64: *const fn (JSValue0: JSC.JSValue) callconv(.C) i64, + JSVALUE_TO_UINT64: *const fn (JSValue0: JSC.JSValue) callconv(.C) u64, + INT64_TO_JSVALUE: *const fn (arg0: *JSC.JSGlobalObject, arg1: i64) callconv(.C) JSC.JSValue, + UINT64_TO_JSVALUE: *const fn (arg0: *JSC.JSGlobalObject, arg1: u64) callconv(.C) JSC.JSValue, + bun_call: *const @TypeOf(JSC.C.JSObjectCallAsFunction), + }; + const headers = @import("../bindings/headers.zig"); + var workaround: MyFunctionSStructWorkAround = if (!JSC.is_bindgen) .{ + .JSVALUE_TO_INT64 = headers.JSC__JSValue__toInt64, + .JSVALUE_TO_UINT64 = headers.JSC__JSValue__toUInt64NoTruncate, + .INT64_TO_JSVALUE = headers.JSC__JSValue__fromInt64NoTruncate, + .UINT64_TO_JSVALUE = headers.JSC__JSValue__fromUInt64NoTruncate, + .bun_call = &JSC.C.JSObjectCallAsFunction, + } else undefined; + + noinline fn memset( + dest: [*]u8, + c: u8, + byte_count: usize, + ) callconv(.C) void { + @memset(dest[0..byte_count], c); + } + + noinline fn memcpy( + noalias dest: [*]u8, + noalias source: [*]const u8, + byte_count: usize, + ) callconv(.C) void { + @memcpy(dest[0..byte_count], source[0..byte_count]); + } + + pub fn define(state: *TCC.TCCState) void { + if (comptime Environment.isX64) { + _ = TCC.tcc_define_symbol(state, "NEEDS_COMPILER_RT_FUNCTIONS", "1"); + // there + _ = TCC.tcc_compile_string(state, @embedFile(("libtcc1.c"))); + } + } + + pub fn inject(state: *TCC.TCCState) void { + _ = TCC.tcc_add_symbol(state, "memset", &memset); + _ = TCC.tcc_add_symbol(state, "memcpy", &memcpy); + _ = TCC.tcc_add_symbol(state, "NapiHandleScope__push", &bun.JSC.napi.NapiHandleScope.NapiHandleScope__push); + _ = TCC.tcc_add_symbol(state, "NapiHandleScope__pop", &bun.JSC.napi.NapiHandleScope.NapiHandleScope__pop); + + _ = TCC.tcc_add_symbol( + state, + "JSVALUE_TO_INT64_SLOW", + workaround.JSVALUE_TO_INT64, + ); + _ = TCC.tcc_add_symbol( + state, + "JSVALUE_TO_UINT64_SLOW", + workaround.JSVALUE_TO_UINT64, + ); + if (!comptime JSC.is_bindgen) { + std.mem.doNotOptimizeAway(headers.JSC__JSValue__toUInt64NoTruncate); + std.mem.doNotOptimizeAway(headers.JSC__JSValue__toInt64); + std.mem.doNotOptimizeAway(headers.JSC__JSValue__fromInt64NoTruncate); + std.mem.doNotOptimizeAway(headers.JSC__JSValue__fromUInt64NoTruncate); + } + _ = TCC.tcc_add_symbol( + state, + "INT64_TO_JSVALUE_SLOW", + workaround.INT64_TO_JSVALUE, + ); + _ = TCC.tcc_add_symbol( + state, + "UINT64_TO_JSVALUE_SLOW", + workaround.UINT64_TO_JSVALUE, + ); + } +}; + +pub const Bun__FFI__cc = FFI.Bun__FFI__cc; diff --git a/src/bun.js/api/filesystem_router.zig b/src/bun.js/api/filesystem_router.zig index bd5a22c34d001..74052edbe6b23 100644 --- a/src/bun.js/api/filesystem_router.zig +++ b/src/bun.js/api/filesystem_router.zig @@ -49,7 +49,7 @@ pub const FileSystemRouter = struct { pub usingnamespace JSC.Codegen.JSFileSystemRouter; - pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) ?*FileSystemRouter { + pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) ?*FileSystemRouter { const argument_ = callframe.arguments(1); if (argument_.len == 0) { globalThis.throwInvalidArguments("Expected object", .{}); @@ -211,7 +211,7 @@ pub const FileSystemRouter = struct { return fs_router; } - pub fn reload(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn reload(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const this_value = callframe.this(); var arena = globalThis.allocator().create(bun.ArenaAllocator) catch unreachable; @@ -259,7 +259,7 @@ pub const FileSystemRouter = struct { return this_value; } - pub fn match(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn match(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { const argument_ = callframe.arguments(2); if (argument_.len == 0) { globalThis.throwInvalidArguments("Expected string, Request or Response", .{}); @@ -327,15 +327,15 @@ pub const FileSystemRouter = struct { return result.toJS(globalThis); } - pub fn getOrigin(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getOrigin(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) JSValue { if (this.origin) |origin| { - return JSC.ZigString.init(origin.slice()).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(origin.slice()).withEncoding().toJS(globalThis); } return JSValue.jsNull(); } - pub fn getRoutes(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getRoutes(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) JSValue { const paths = this.router.getEntryPoints() catch unreachable; const names = this.router.getNames() catch unreachable; var name_strings = bun.default_allocator.alloc(ZigString, names.len * 2) catch unreachable; @@ -354,13 +354,13 @@ pub const FileSystemRouter = struct { ); } - pub fn getStyle(_: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { - return ZigString.static("nextjs").toValue(globalThis); + pub fn getStyle(_: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) JSValue { + return ZigString.static("nextjs").toJS(globalThis); } - pub fn getAssetPrefix(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getAssetPrefix(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject) JSValue { if (this.asset_prefix) |asset_prefix| { - return JSC.ZigString.init(asset_prefix.slice()).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(asset_prefix.slice()).withEncoding().toJS(globalThis); } return JSValue.jsNull(); @@ -399,8 +399,8 @@ pub const MatchedRoute = struct { pub usingnamespace JSC.Codegen.JSMatchedRoute; - pub fn getName(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { - return ZigString.init(this.route.name).withEncoding().toValueGC(globalThis); + pub fn getName(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) JSValue { + return ZigString.init(this.route.name).withEncoding().toJS(globalThis); } pub fn init( @@ -466,10 +466,10 @@ pub const MatchedRoute = struct { pub fn getFilePath( this: *MatchedRoute, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return ZigString.init(this.route.file_path) .withEncoding() - .toValueGC(globalThis); + .toJS(globalThis); } pub fn finalize( @@ -478,16 +478,16 @@ pub const MatchedRoute = struct { this.deinit(); } - pub fn getPathname(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getPathname(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) JSValue { return ZigString.init(this.route.pathname) .withEncoding() - .toValueGC(globalThis); + .toJS(globalThis); } - pub fn getRoute(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getRoute(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) JSValue { return ZigString.init(this.route.name) .withEncoding() - .toValueGC(globalThis); + .toJS(globalThis); } const KindEnum = struct { @@ -510,13 +510,13 @@ pub const MatchedRoute = struct { } }; - pub fn getKind(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { - return KindEnum.init(this.route.name).toValue(globalThis); + pub fn getKind(this: *MatchedRoute, globalThis: *JSC.JSGlobalObject) JSValue { + return KindEnum.init(this.route.name).toJS(globalThis); } threadlocal var query_string_values_buf: [256]string = undefined; threadlocal var query_string_value_refs_buf: [256]ZigString = undefined; - pub fn createQueryObject(ctx: js.JSContextRef, map: *QueryStringMap) callconv(.C) JSValue { + pub fn createQueryObject(ctx: js.JSContextRef, map: *QueryStringMap) JSValue { const QueryObjectCreator = struct { query: *QueryStringMap, pub fn create(this: *@This(), obj: *JSObject, global: *JSGlobalObject) void { @@ -577,7 +577,7 @@ pub const MatchedRoute = struct { pub fn getScriptSrc( this: *MatchedRoute, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var buf: bun.PathBuffer = undefined; var stream = std.io.fixedBufferStream(&buf); var writer = stream.writer(); @@ -592,13 +592,13 @@ pub const MatchedRoute = struct { ); return ZigString.init(buf[0..writer.context.pos]) .withEncoding() - .toValueGC(globalThis); + .toJS(globalThis); } pub fn getParams( this: *MatchedRoute, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { if (this.route.params.len == 0) return JSValue.createEmptyObject(globalThis, 0); @@ -621,7 +621,7 @@ pub const MatchedRoute = struct { pub fn getQuery( this: *MatchedRoute, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { if (this.route.query_string.len == 0 and this.route.params.len == 0) { return JSValue.createEmptyObject(globalThis, 0); } else if (this.route.query_string.len == 0) { diff --git a/src/bun.js/api/glob.zig b/src/bun.js/api/glob.zig index ec1d2d58e2220..122aff963b989 100644 --- a/src/bun.js/api/glob.zig +++ b/src/bun.js/api/glob.zig @@ -174,7 +174,7 @@ pub const WalkTask = struct { pub fn toJSC(this: Err, globalThis: *JSGlobalObject) JSValue { return switch (this) { .syscall => |err| err.toJSC(globalThis), - .unknown => |err| ZigString.fromBytes(@errorName(err)).toValueGC(globalThis), + .unknown => |err| ZigString.fromBytes(@errorName(err)).toJS(globalThis), }; } }; @@ -259,7 +259,7 @@ fn makeGlobWalker( if (cwd != null) { var globWalker = alloc.create(GlobWalker) catch { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return null; }; @@ -275,7 +275,7 @@ fn makeGlobWalker( error_on_broken_symlinks, only_files, ) catch { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return null; }) { .err => |err| { @@ -287,7 +287,7 @@ fn makeGlobWalker( return globWalker; } var globWalker = alloc.create(GlobWalker) catch { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return null; }; @@ -301,7 +301,7 @@ fn makeGlobWalker( error_on_broken_symlinks, only_files, ) catch { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return null; }) { .err => |err| { @@ -317,7 +317,7 @@ fn makeGlobWalker( pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) ?*Glob { +) ?*Glob { const alloc = getAllocator(globalThis); const arguments_ = callframe.arguments(1); @@ -384,7 +384,7 @@ fn decrPendingActivityFlag(has_pending_activity: *std.atomic.Value(usize)) void _ = has_pending_activity.fetchSub(1, .seq_cst); } -pub fn __scan(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn __scan(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const alloc = getAllocator(globalThis); const arguments_ = callframe.arguments(1); @@ -400,7 +400,7 @@ pub fn __scan(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFram incrPendingActivityFlag(&this.has_pending_activity); var task = WalkTask.create(globalThis, alloc, globWalker, &this.has_pending_activity) catch { decrPendingActivityFlag(&this.has_pending_activity); - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return .undefined; }; task.schedule(); @@ -408,7 +408,7 @@ pub fn __scan(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFram return task.promise.value(); } -pub fn __scanSync(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn __scanSync(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const alloc = getAllocator(globalThis); const arguments_ = callframe.arguments(1); @@ -423,7 +423,7 @@ pub fn __scanSync(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.Call defer globWalker.deinit(true); switch (globWalker.walk() catch { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return .undefined; }) { .err => |err| { @@ -438,7 +438,7 @@ pub fn __scanSync(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.Call return matchedPaths; } -pub fn match(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn match(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const alloc = getAllocator(globalThis); var arena = Arena.init(alloc); defer arena.deinit(); @@ -448,12 +448,12 @@ pub fn match(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame defer arguments.deinit(); const str_arg = arguments.nextEat() orelse { globalThis.throw("Glob.matchString: expected 1 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; if (!str_arg.isString()) { globalThis.throw("Glob.matchString: first argument is not a string", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; } var str = str_arg.toSlice(globalThis, arena.allocator()); @@ -480,7 +480,7 @@ pub fn match(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame break :codepoints codepoints.items[0..codepoints.items.len]; }; - return JSC.JSValue.jsBoolean(globImpl.matchImpl(codepoints, str.slice())); + return if (globImpl.matchImpl(codepoints, str.slice()).matches()) .true else .false; } pub fn convertUtf8(codepoints: *std.ArrayList(u32), pattern: []const u8) !void { diff --git a/src/bun.js/api/html_rewriter.zig b/src/bun.js/api/html_rewriter.zig index d217fbcba9eb0..ef86b5083f355 100644 --- a/src/bun.js/api/html_rewriter.zig +++ b/src/bun.js/api/html_rewriter.zig @@ -465,14 +465,14 @@ pub const HTMLRewriter = struct { return switch (buffering_error) { error.StreamAlreadyUsed => { var err = JSC.SystemError{ - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_ALREADY_FINISHED))), + .code = bun.String.static("ERR_STREAM_ALREADY_FINISHED"), .message = bun.String.static("Stream already used, please create a new one"), }; return err.toErrorInstance(sink.global); }, else => { var err = JSC.SystemError{ - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE))), + .code = bun.String.static("ERR_STREAM_CANNOT_PIPE"), .message = bun.String.static("Failed to pipe stream"), }; return err.toErrorInstance(sink.global); @@ -493,7 +493,7 @@ pub const HTMLRewriter = struct { return response_js_value; } - pub fn onFinishedBuffering(ctx: *anyopaque, bytes: []const u8, js_err: ?JSC.JSValue, is_async: bool) void { + pub fn onFinishedBuffering(ctx: *anyopaque, bytes: []const u8, js_err: ?JSC.WebCore.Body.Value.ValueError, is_async: bool) void { const sink = bun.cast(*BufferOutputSink, ctx); if (js_err) |err| { if (sink.response.body.value == .Locked and @intFromPtr(sink.response.body.value.Locked.task) == @intFromPtr(sink) and @@ -510,7 +510,7 @@ pub const HTMLRewriter = struct { sink.response.body.value.Locked.task = null; } if (is_async) { - sink.response.body.value.toErrorInstance(err, sink.global); + sink.response.body.value.toErrorInstance(err.dupe(sink.global), sink.global); } else { var ret_err = throwLOLHTMLError(sink.global); ret_err.ensureStillAlive(); @@ -544,7 +544,7 @@ pub const HTMLRewriter = struct { sink.deinit(); if (is_async) { - response.body.value.toErrorInstance(throwLOLHTMLError(global), global); + response.body.value.toErrorInstance(.{ .Message = throwLOLHTMLStringError() }, global); return null; } else { @@ -558,7 +558,7 @@ pub const HTMLRewriter = struct { sink.deinit(); if (is_async) { - response.body.value.toErrorInstance(throwLOLHTMLError(global), global); + response.body.value.toErrorInstance(.{ .Message = throwLOLHTMLStringError() }, global); return null; } else { return throwLOLHTMLError(global); @@ -880,14 +880,14 @@ fn HandlerCallback( @field(zig_element, field_name) = value; defer @field(zig_element, field_name) = null; - var result = @field(this, callback_name).?.callWithThis( + const result = @field(this, callback_name).?.call( this.global, if (comptime @hasField(HandlerType, "thisObject")) @field(this, "thisObject") else JSValue.zero, &.{zig_element.toJS(this.global)}, - ); + ) catch |err| this.global.takeException(err); if (!result.isUndefinedOrNull()) { if (result.isError() or result.isAggregateError(this.global)) { @@ -896,7 +896,7 @@ fn HandlerCallback( if (result.asAnyPromise()) |promise| { this.global.bunVM().waitForPromise(promise); - const fail = promise.status(this.global.vm()) == .Rejected; + const fail = promise.status(this.global.vm()) == .rejected; if (fail) { _ = this.global.bunVM().unhandledRejection(this.global, promise.result(this.global.vm()), promise.asValue(this.global)); } @@ -1028,6 +1028,11 @@ fn throwLOLHTMLError(global: *JSGlobalObject) JSValue { defer err.deinit(); return ZigString.fromUTF8(err.slice()).toErrorInstance(global); } +fn throwLOLHTMLStringError() bun.String { + const err = LOLHTML.HTMLString.lastError(); + defer err.deinit(); + return bun.String.fromUTF8(err.slice()); +} fn htmlStringValue(input: LOLHTML.HTMLString, globalObject: *JSGlobalObject) JSValue { return input.toJS(globalObject); @@ -1040,7 +1045,7 @@ pub const TextChunk = struct { fn contentHandler(this: *TextChunk, comptime Callback: (fn (*LOLHTML.TextChunk, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { if (this.text_chunk == null) - return JSC.JSValue.jsUndefined(); + return .undefined; var content_slice = content.toSlice(bun.default_allocator); defer content_slice.deinit(); @@ -1091,7 +1096,7 @@ pub const TextChunk = struct { this: *TextChunk, _: *JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.text_chunk == null) return JSValue.jsUndefined(); this.text_chunk.?.remove(); @@ -1101,17 +1106,17 @@ pub const TextChunk = struct { pub fn getText( this: *TextChunk, global: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.text_chunk == null) return JSValue.jsUndefined(); - return ZigString.init(this.text_chunk.?.getContent().slice()).withEncoding().toValueGC(global); + return ZigString.init(this.text_chunk.?.getContent().slice()).withEncoding().toJS(global); } - pub fn removed(this: *TextChunk, _: *JSGlobalObject) callconv(.C) JSValue { + pub fn removed(this: *TextChunk, _: *JSGlobalObject) JSValue { return JSValue.jsBoolean(this.text_chunk.?.isRemoved()); } - pub fn lastInTextNode(this: *TextChunk, _: *JSGlobalObject) callconv(.C) JSValue { + pub fn lastInTextNode(this: *TextChunk, _: *JSGlobalObject) JSValue { return JSValue.jsBoolean(this.text_chunk.?.isLastInTextNode()); } @@ -1135,39 +1140,39 @@ pub const DocType = struct { pub fn name( this: *DocType, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.doctype == null) return JSValue.jsUndefined(); const str = this.doctype.?.getName().slice(); if (str.len == 0) return JSValue.jsNull(); - return ZigString.init(str).toValueGC(globalObject); + return ZigString.init(str).toJS(globalObject); } pub fn systemId( this: *DocType, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.doctype == null) return JSValue.jsUndefined(); const str = this.doctype.?.getSystemId().slice(); if (str.len == 0) return JSValue.jsNull(); - return ZigString.init(str).toValueGC(globalObject); + return ZigString.init(str).toJS(globalObject); } pub fn publicId( this: *DocType, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.doctype == null) return JSValue.jsUndefined(); const str = this.doctype.?.getPublicId().slice(); if (str.len == 0) return JSValue.jsNull(); - return ZigString.init(str).toValueGC(globalObject); + return ZigString.init(str).toJS(globalObject); } }; @@ -1273,7 +1278,7 @@ pub const Comment = struct { this: *Comment, _: *JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.comment == null) return JSValue.jsNull(); this.comment.?.remove(); @@ -1283,7 +1288,7 @@ pub const Comment = struct { pub fn getText( this: *Comment, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.comment == null) return JSValue.jsNull(); return this.comment.?.getText().toJS(globalObject); @@ -1309,7 +1314,7 @@ pub const Comment = struct { pub fn removed( this: *Comment, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.comment == null) return JSValue.jsUndefined(); return JSValue.jsBoolean(this.comment.?.isRemoved()); @@ -1395,7 +1400,7 @@ pub const EndTag = struct { this: *EndTag, _: *JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.end_tag == null) return JSValue.jsUndefined(); @@ -1406,7 +1411,7 @@ pub const EndTag = struct { pub fn getName( this: *EndTag, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.end_tag == null) return JSValue.jsUndefined(); @@ -1444,7 +1449,7 @@ pub const AttributeIterator = struct { pub usingnamespace JSC.Codegen.JSAttributeIterator; - pub fn next(this: *AttributeIterator, globalObject: *JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn next(this: *AttributeIterator, globalObject: *JSGlobalObject, _: *JSC.CallFrame) JSValue { const done_label = JSC.ZigString.static("done"); const value_label = JSC.ZigString.static("value"); @@ -1470,7 +1475,7 @@ pub const AttributeIterator = struct { )); } - pub fn getThis(_: *AttributeIterator, _: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn getThis(_: *AttributeIterator, _: *JSGlobalObject, callFrame: *JSC.CallFrame) JSValue { return callFrame.this(); } }; @@ -1493,7 +1498,7 @@ pub const Element = struct { if (this.element == null) return JSValue.jsNull(); if (function.isUndefinedOrNull() or !function.isCallable(globalObject.vm())) { - return ZigString.init("Expected a function").withEncoding().toValueGC(globalObject); + return ZigString.init("Expected a function").withEncoding().toJS(globalObject); } const end_tag_handler = bun.default_allocator.create(EndTag.Handler) catch bun.outOfMemory(); @@ -1669,7 +1674,7 @@ pub const Element = struct { this: *Element, _: *JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); @@ -1682,14 +1687,14 @@ pub const Element = struct { this: *Element, _: *JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); this.element.?.removeAndKeepContent(); return callFrame.this(); } - pub fn getTagName(this: *Element, globalObject: *JSGlobalObject) callconv(.C) JSValue { + pub fn getTagName(this: *Element, globalObject: *JSGlobalObject) JSValue { if (this.element == null) return JSValue.jsUndefined(); @@ -1700,7 +1705,7 @@ pub const Element = struct { this: *Element, global: *JSGlobalObject, value: JSValue, - ) callconv(.C) bool { + ) bool { if (this.element == null) return false; @@ -1718,7 +1723,7 @@ pub const Element = struct { pub fn getRemoved( this: *Element, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); return JSValue.jsBoolean(this.element.?.isRemoved()); @@ -1727,7 +1732,7 @@ pub const Element = struct { pub fn getSelfClosing( this: *Element, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); return JSValue.jsBoolean(this.element.?.isSelfClosing()); @@ -1736,7 +1741,7 @@ pub const Element = struct { pub fn getCanHaveContent( this: *Element, _: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); return JSValue.jsBoolean(this.element.?.canHaveContent()); @@ -1745,7 +1750,7 @@ pub const Element = struct { pub fn getNamespaceURI( this: *Element, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); var str = bun.String.createUTF8(std.mem.span(this.element.?.namespaceURI())); @@ -1756,7 +1761,7 @@ pub const Element = struct { pub fn getAttributes( this: *Element, globalObject: *JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.element == null) return JSValue.jsUndefined(); diff --git a/src/bun.js/api/js_brotli.zig b/src/bun.js/api/js_brotli.zig new file mode 100644 index 0000000000000..07bee73bdab1d --- /dev/null +++ b/src/bun.js/api/js_brotli.zig @@ -0,0 +1,811 @@ +const bun = @import("root").bun; +const JSC = bun.JSC; +const std = @import("std"); +const brotli = bun.brotli; + +const Queue = std.fifo.LinearFifo(JSC.Node.BlobOrStringOrBuffer, .Dynamic); + +// We cannot free outside the JavaScript thread. +const FreeList = struct { + write_lock: bun.Lock = .{}, + list: std.ArrayListUnmanaged(JSC.Node.BlobOrStringOrBuffer) = .{}, + + pub fn append(this: *FreeList, slice: []const JSC.Node.BlobOrStringOrBuffer) void { + this.write_lock.lock(); + defer this.write_lock.unlock(); + this.list.appendSlice(bun.default_allocator, slice) catch bun.outOfMemory(); + } + + pub fn drain(this: *FreeList) void { + this.write_lock.lock(); + defer this.write_lock.unlock(); + const out = this.list.items; + for (out) |*item| { + item.deinitAndUnprotect(); + } + this.list.clearRetainingCapacity(); + } + + pub fn deinit(this: *FreeList) void { + this.drain(); + this.list.deinit(bun.default_allocator); + } +}; + +pub const BrotliEncoder = struct { + pub usingnamespace bun.New(@This()); + pub usingnamespace JSC.Codegen.JSBrotliEncoder; + + stream: brotli.BrotliCompressionStream, + chunkSize: c_uint, + maxOutputLength: usize, + + freelist: FreeList = .{}, + + globalThis: *JSC.JSGlobalObject, + mode: u8, + + input: Queue = Queue.init(bun.default_allocator), + input_lock: bun.Lock = .{}, + + has_called_end: bool = false, + callback_value: JSC.Strong = .{}, + + output: std.ArrayListUnmanaged(u8) = .{}, + output_lock: bun.Lock = .{}, + + has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + pending_encode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + ref_count: u32 = 1, + write_failure: ?JSC.DeferredError = null, + poll_ref: bun.Async.KeepAlive = .{}, + closed: bool = false, + + pub fn hasPendingActivity(this: *BrotliEncoder) bool { + return this.has_pending_activity.load(.monotonic) > 0; + } + + pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*BrotliEncoder { + globalThis.throw("BrotliEncoder is not constructable", .{}); + return null; + } + + pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(4).slice(); + + if (arguments.len < 4) { + globalThis.throwNotEnoughArguments("BrotliEncoder", 4, arguments.len); + return .zero; + } + + const opts = arguments[0]; + const callback = arguments[2]; + const mode = arguments[3].to(u8); + + const chunkSize = globalThis.getInteger(opts, u32, 1024 * 48, .{ .min = 64, .field_name = "chunkSize" }) orelse return .zero; + const maxOutputLength = globalThis.getInteger(opts, usize, 0, .{ .max = std.math.maxInt(u52), .field_name = "maxOutputLength" }) orelse return .zero; + const flush = globalThis.getInteger(opts, u8, 0, .{ .max = 3, .field_name = "flush" }) orelse return .zero; + const finishFlush = globalThis.getInteger(opts, u8, 2, .{ .max = 3, .field_name = "finishFlush" }) orelse return .zero; + const fullFlush = globalThis.getInteger(opts, u8, 1, .{ .max = 3, .field_name = "fullFlush" }) orelse return .zero; + + var this: *BrotliEncoder = BrotliEncoder.new(.{ + .globalThis = globalThis, + .stream = brotli.BrotliCompressionStream.init(@enumFromInt(flush), @enumFromInt(finishFlush), @enumFromInt(fullFlush)) catch { + globalThis.throw("Failed to create BrotliEncoder", .{}); + return .zero; + }, + .chunkSize = chunkSize, + .maxOutputLength = maxOutputLength, + .mode = mode, + }); + + if (opts.get(globalThis, "params")) |params| { + inline for (std.meta.fields(bun.brotli.c.BrotliEncoderParameter)) |f| { + if (!params.isObject()) break; + if (params.hasOwnPropertyValue(globalThis, JSC.ZigString.static(std.fmt.comptimePrint("{d}", .{f.value})).toJS(globalThis))) { + const idx = params.getIndex(globalThis, f.value); + if (!idx.isNumber()) { + globalThis.throwValue(globalThis.ERR_INVALID_ARG_TYPE_static( + JSC.ZigString.static("options.params[key]"), + JSC.ZigString.static("number"), + idx, + )); + this.deinit(); + return .zero; + } + const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32()); + if (!was_set) { + globalThis.ERR_ZLIB_INITIALIZATION_FAILED("Initialization failed", .{}).throw(); + this.deinit(); + return .zero; + } + } + } + } + if (globalThis.hasException()) return .zero; + + const out = this.toJS(globalThis); + this.callback_value.set(globalThis, callback); + + return out; + } + + pub fn finalize(this: *BrotliEncoder) void { + this.deinit(); + } + + pub fn deinit(this: *BrotliEncoder) void { + this.callback_value.deinit(); + this.freelist.deinit(); + this.output.deinit(bun.default_allocator); + this.stream.deinit(); + this.input.deinit(); + this.destroy(); + } + + fn drainFreelist(this: *BrotliEncoder) void { + this.freelist.drain(); + } + + fn collectOutputValue(this: *BrotliEncoder) JSC.JSValue { + this.output_lock.lock(); + defer this.output_lock.unlock(); + + defer this.output.clearRetainingCapacity(); + return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items); + } + + pub fn runFromJSThread(this: *BrotliEncoder) void { + this.poll_ref.unref(this.globalThis.bunVM()); + + defer _ = this.has_pending_activity.fetchSub(1, .monotonic); + this.drainFreelist(); + + _ = this.callback_value.get().?.call( + this.globalThis, + .undefined, + if (this.write_failure != null) + &.{this.write_failure.?.toError(this.globalThis)} + else + &.{ .null, this.collectOutputValue() }, + ) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err); + } + + // We can only run one encode job at a time + // But we don't have an idea of a serial dispatch queue + // So instead, we let you enqueue as many times as you want + // and if one is already running, we just don't do anything + const EncodeJob = struct { + task: JSC.WorkPoolTask = .{ .callback = &runTask }, + encoder: *BrotliEncoder, + is_async: bool, + vm: *JSC.VirtualMachine, + + pub usingnamespace bun.New(@This()); + + pub fn runTask(this: *JSC.WorkPoolTask) void { + var job: *EncodeJob = @fieldParentPtr("task", this); + job.run(); + job.destroy(); + } + + pub fn run(this: *EncodeJob) void { + defer { + _ = this.encoder.has_pending_activity.fetchSub(1, .monotonic); + } + + var any = false; + + if (this.encoder.pending_encode_job_count.fetchAdd(1, .monotonic) >= 0) { + const is_last = this.encoder.has_called_end; + while (true) { + this.encoder.input_lock.lock(); + defer this.encoder.input_lock.unlock(); + const readable = this.encoder.input.readableSlice(0); + defer this.encoder.input.discard(readable.len); + const pending = readable; + + const Writer = struct { + encoder: *BrotliEncoder, + + pub const Error = error{OutOfMemory}; + pub fn writeAll(writer: @This(), chunk: []const u8) Error!void { + writer.encoder.output_lock.lock(); + defer writer.encoder.output_lock.unlock(); + + try writer.encoder.output.appendSlice(bun.default_allocator, chunk); + } + }; + + defer { + this.encoder.freelist.append(pending); + } + for (pending) |*input| { + var writer = this.encoder.stream.writer(Writer{ .encoder = this.encoder }); + writer.writeAll(input.slice()) catch { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + if (!this.is_async) { + this.encoder.closed = true; + this.encoder.globalThis.throw("BrotliError", .{}); + return; + } + this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error + return; + }; + if (this.encoder.output.items.len > this.encoder.maxOutputLength) { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.encoder.maxOutputLength}); + return; + } + } + + any = any or pending.len > 0; + + if (this.encoder.pending_encode_job_count.fetchSub(1, .monotonic) == 0) + break; + } + + if (is_last and any) { + var output = &this.encoder.output; + this.encoder.output_lock.lock(); + defer this.encoder.output_lock.unlock(); + + output.appendSlice(bun.default_allocator, this.encoder.stream.end() catch { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error + return; + }) catch { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error + return; + }; + if (output.items.len > this.encoder.maxOutputLength) { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.encoder.maxOutputLength}); + return; + } + } + } + + if (this.is_async and any) { + _ = this.encoder.has_pending_activity.fetchAdd(1, .monotonic); + this.vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.encoder))); + } + } + }; + + pub fn transform(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(3); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("BrotliEncoder.encode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + globalThis.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer"); + return .zero; + }; + + _ = this.has_pending_activity.fetchAdd(1, .monotonic); + if (is_last) + this.has_called_end = true; + + var task = EncodeJob.new(.{ + .encoder = this, + .is_async = true, + .vm = this.globalThis.bunVM(), + }); + + { + this.input_lock.lock(); + defer this.input_lock.unlock(); + + // need to protect because no longer on the stack. unprotected in FreeList.deinit + input_to_queue.protect(); + this.input.writeItem(input_to_queue) catch bun.outOfMemory(); + } + this.poll_ref.ref(task.vm); + JSC.WorkPool.schedule(&task.task); + + return .undefined; + } + + pub fn transformSync(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(4); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("BrotliEncoder.encode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + const optional_flushFlag = arguments.ptr[3]; + + const old_flushFlag = this.stream.flushOp; + defer this.stream.flushOp = old_flushFlag; + blk: { + if (!optional_flushFlag.isInt32()) break :blk; + const int = optional_flushFlag.asInt32(); + if (int < 0) break :blk; + if (int > 3) break :blk; + this.stream.flushOp = @enumFromInt(int); + } + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + globalThis.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer"); + return .zero; + }; + + _ = this.has_pending_activity.fetchAdd(1, .monotonic); + if (is_last) + this.has_called_end = true; + + var task: EncodeJob = .{ + .encoder = this, + .is_async = false, + .vm = this.globalThis.bunVM(), + }; + + { + this.input_lock.lock(); + defer this.input_lock.unlock(); + + // need to protect because no longer on the stack. unprotected in FreeList.deinit + input_to_queue.protect(); + this.input.writeItem(input_to_queue) catch bun.outOfMemory(); + } + task.run(); + if (!is_last and this.output.items.len == 0) { + return JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); + } + if (this.write_failure != null) { + globalThis.vm().throwError(globalThis, this.write_failure.?.toError(globalThis)); + return .zero; + } + return this.collectOutputValue(); + } + + pub fn reset(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn getBytesWritten(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.total_in); + } + + pub fn getClosed(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsBoolean(this.closed); + } + + pub fn close(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.chunkSize); + } + + pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.flushOp); + } + + pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.finishFlushOp); + } + + pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.fullFlushOp); + } + + pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.maxOutputLength); + } +}; + +pub const BrotliDecoder = struct { + pub usingnamespace bun.New(@This()); + pub usingnamespace JSC.Codegen.JSBrotliDecoder; + + globalThis: *JSC.JSGlobalObject, + stream: brotli.BrotliReaderArrayList, + chunkSize: c_uint, + maxOutputLength: usize, + mode: u8, + + has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + ref_count: u32 = 1, + poll_ref: bun.Async.KeepAlive = .{}, + write_failure: ?JSC.DeferredError = null, + callback_value: JSC.Strong = .{}, + has_called_end: bool = false, + pending_decode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + closed: bool = false, + + input: Queue = Queue.init(bun.default_allocator), + input_lock: bun.Lock = .{}, + + output: std.ArrayListUnmanaged(u8) = .{}, + output_lock: bun.Lock = .{}, + + freelist: FreeList = .{}, + + pub fn hasPendingActivity(this: *BrotliDecoder) bool { + return this.has_pending_activity.load(.monotonic) > 0; + } + + pub fn deinit(this: *BrotliDecoder) void { + this.callback_value.deinit(); + this.freelist.deinit(); + this.output.deinit(bun.default_allocator); + this.stream.brotli.destroyInstance(); + this.input.deinit(); + this.destroy(); + } + + pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*BrotliDecoder { + globalThis.throw("BrotliDecoder is not constructable", .{}); + return null; + } + + pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(4).slice(); + + if (arguments.len < 4) { + globalThis.throwNotEnoughArguments("BrotliDecoder", 4, arguments.len); + return .zero; + } + + const opts = arguments[0]; + const callback = arguments[2]; + const mode = arguments[3].to(u8); + + const chunkSize = globalThis.getInteger(opts, u32, 1024 * 48, .{ .min = 64, .field_name = "chunkSize" }) orelse return .zero; + const maxOutputLength = globalThis.getInteger(opts, usize, 0, .{ .max = std.math.maxInt(u52), .field_name = "maxOutputLength" }) orelse return .zero; + const flush = globalThis.getInteger(opts, u8, 0, .{ .max = 6, .field_name = "flush" }) orelse return .zero; + const finishFlush = globalThis.getInteger(opts, u8, 2, .{ .max = 6, .field_name = "finishFlush" }) orelse return .zero; + const fullFlush = globalThis.getInteger(opts, u8, 1, .{ .max = 6, .field_name = "fullFlush" }) orelse return .zero; + + var this: *BrotliDecoder = BrotliDecoder.new(.{ + .globalThis = globalThis, + .stream = undefined, // &this.output needs to be a stable pointer + .chunkSize = chunkSize, + .maxOutputLength = maxOutputLength, + .mode = mode, + }); + this.stream = brotli.BrotliReaderArrayList.initWithOptions("", &this.output, bun.default_allocator, .{}, @enumFromInt(flush), @enumFromInt(finishFlush), @enumFromInt(fullFlush)) catch { + globalThis.throw("Failed to create BrotliDecoder", .{}); + return .zero; + }; + + if (opts.get(globalThis, "params")) |params| { + inline for (std.meta.fields(bun.brotli.c.BrotliDecoderParameter)) |f| { + if (!params.isObject()) break; + const idx = params.getIndex(globalThis, f.value); + if (!idx.isNumber()) break; + const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32()); + if (!was_set) { + globalThis.ERR_ZLIB_INITIALIZATION_FAILED("Initialization failed", .{}).throw(); + this.deinit(); + return .zero; + } + } + } + if (globalThis.hasException()) return .zero; + + const out = this.toJS(globalThis); + this.callback_value.set(globalThis, callback); + + return out; + } + + pub fn finalize(this: *BrotliDecoder) void { + this.deinit(); + } + + fn collectOutputValue(this: *BrotliDecoder) JSC.JSValue { + this.output_lock.lock(); + defer this.output_lock.unlock(); + + defer this.output.clearRetainingCapacity(); + return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items); + } + + pub fn runFromJSThread(this: *BrotliDecoder) void { + this.poll_ref.unref(this.globalThis.bunVM()); + + defer _ = this.has_pending_activity.fetchSub(1, .monotonic); + this.drainFreelist(); + + _ = this.callback_value.get().?.call( + this.globalThis, + .undefined, + if (this.write_failure != null) + &.{this.write_failure.?.toError(this.globalThis)} + else + &.{ .null, this.collectOutputValue() }, + ) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err); + } + + fn drainFreelist(this: *BrotliDecoder) void { + this.freelist.drain(); + } + + pub fn transform(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(3); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("BrotliEncoder.decode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("BrotliEncoder.decode called after BrotliEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + globalThis.throwInvalidArgumentType("BrotliEncoder.decode", "input", "Blob, String, or Buffer"); + return .zero; + }; + + _ = this.has_pending_activity.fetchAdd(1, .monotonic); + if (is_last) + this.has_called_end = true; + + var task = DecodeJob.new(.{ + .decoder = this, + .is_async = true, + .vm = this.globalThis.bunVM(), + }); + + { + this.input_lock.lock(); + defer this.input_lock.unlock(); + + // need to protect because no longer on the stack. unprotected in FreeList.deinit + input_to_queue.protect(); + this.input.writeItem(input_to_queue) catch bun.outOfMemory(); + } + this.poll_ref.ref(task.vm); + JSC.WorkPool.schedule(&task.task); + + return .undefined; + } + + pub fn transformSync(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(4); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("BrotliEncoder.decode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("BrotliEncoder.decode called after BrotliEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + // const optional_flushFlag = arguments.ptr[3]; + + // const old_flushFlag = this.stream.flushOp; + // defer this.stream.flushOp = old_flushFlag; + // blk: { + // if (!optional_flushFlag.isInt32()) break :blk; + // const int = optional_flushFlag.asInt32(); + // if (int < 0) break :blk; + // if (int > 3) break :blk; + // this.stream.flushOp = @enumFromInt(int); + // } + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + globalThis.throwInvalidArgumentType("BrotliEncoder.decode", "input", "Blob, String, or Buffer"); + return .zero; + }; + + _ = this.has_pending_activity.fetchAdd(1, .monotonic); + if (is_last) + this.has_called_end = true; + + var task: DecodeJob = .{ + .decoder = this, + .is_async = false, + .vm = this.globalThis.bunVM(), + }; + + { + this.input_lock.lock(); + defer this.input_lock.unlock(); + + // need to protect because no longer on the stack. unprotected in FreeList.deinit + input_to_queue.protect(); + this.input.writeItem(input_to_queue) catch bun.outOfMemory(); + } + task.run(); + if (!is_last and this.output.items.len == 0) { + return JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); + } + if (this.write_failure != null) { + globalThis.throwValue(this.write_failure.?.toError(globalThis)); + return .zero; + } + return this.collectOutputValue(); + } + + // We can only run one decode job at a time + // But we don't have an idea of a serial dispatch queue + // So instead, we let you enqueue as many times as you want + // and if one is already running, we just don't do anything + const DecodeJob = struct { + task: JSC.WorkPoolTask = .{ .callback = &runTask }, + decoder: *BrotliDecoder, + is_async: bool, + vm: *JSC.VirtualMachine, + + pub usingnamespace bun.New(@This()); + + pub fn runTask(this: *JSC.WorkPoolTask) void { + var job: *DecodeJob = @fieldParentPtr("task", this); + job.run(); + job.destroy(); + } + + pub fn run(this: *DecodeJob) void { + defer { + _ = this.decoder.has_pending_activity.fetchSub(1, .monotonic); + } + + var any = false; + + if (this.decoder.pending_decode_job_count.fetchAdd(1, .monotonic) >= 0) { + const is_last = this.decoder.has_called_end; + while (true) { + this.decoder.input_lock.lock(); + defer this.decoder.input_lock.unlock(); + if (!is_last) break; + const pending = this.decoder.input.readableSlice(0); + + defer { + this.decoder.freelist.append(pending); + } + + var input_list = std.ArrayListUnmanaged(u8){}; + defer input_list.deinit(bun.default_allocator); + + if (pending.len > 1) { + var count: usize = 0; + for (pending) |input| { + count += input.slice().len; + } + + input_list.ensureTotalCapacityPrecise(bun.default_allocator, count) catch bun.outOfMemory(); + + for (pending) |*input| { + input_list.appendSliceAssumeCapacity(input.slice()); + } + } + + { + this.decoder.output_lock.lock(); + defer this.decoder.output_lock.unlock(); + + const input = if (pending.len <= 1) pending[0].slice() else input_list.items; + this.decoder.stream.input = input; + this.decoder.stream.readAll(false) catch { + any = true; + _ = this.decoder.pending_decode_job_count.fetchSub(1, .monotonic); + if (!this.is_async) { + this.decoder.closed = true; + this.decoder.globalThis.throw("BrotliError", .{}); + return; + } + this.decoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error + break; + }; + if (this.decoder.output.items.len > this.decoder.maxOutputLength) { + any = true; + _ = this.decoder.pending_decode_job_count.fetchSub(1, .monotonic); + this.decoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.decoder.maxOutputLength}); + break; + } + } + + any = any or pending.len > 0; + + if (this.decoder.pending_decode_job_count.fetchSub(1, .monotonic) == 0) + break; + } + } + + if (this.is_async and any) { + _ = this.decoder.has_pending_activity.fetchAdd(1, .monotonic); + this.vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.decoder))); + } + } + }; + + pub fn reset(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn getBytesWritten(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.total_in); + } + + pub fn getClosed(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsBoolean(this.closed); + } + + pub fn close(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.chunkSize); + } + + pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.flushOp); + } + + pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.finishFlushOp); + } + + pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.fullFlushOp); + } + + pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.maxOutputLength); + } +}; diff --git a/src/bun.js/api/js_zlib.zig b/src/bun.js/api/js_zlib.zig new file mode 100644 index 0000000000000..7b2d812e63749 --- /dev/null +++ b/src/bun.js/api/js_zlib.zig @@ -0,0 +1,1014 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Environment = bun.Environment; +const JSC = bun.JSC; +const string = bun.string; +const Output = bun.Output; +const ZigString = JSC.ZigString; +const Queue = std.fifo.LinearFifo(JSC.Node.BlobOrStringOrBuffer, .Dynamic); + +pub const ZlibEncoder = struct { + pub usingnamespace bun.New(@This()); + pub usingnamespace JSC.Codegen.JSZlibEncoder; + + globalThis: *JSC.JSGlobalObject, + stream: bun.zlib.ZlibCompressorStreaming, + maxOutputLength: usize, + + freelist: Queue = Queue.init(bun.default_allocator), + freelist_write_lock: bun.Lock = .{}, + + input: Queue = Queue.init(bun.default_allocator), + input_lock: bun.Lock = .{}, + + has_called_end: bool = false, + callback_value: JSC.Strong = .{}, + + output: std.ArrayListUnmanaged(u8) = .{}, + output_lock: bun.Lock = .{}, + + has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + pending_encode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + ref_count: u32 = 1, + write_failure: ?JSC.DeferredError = null, + poll_ref: bun.Async.KeepAlive = .{}, + closed: bool = false, + + pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) ?*@This() { + _ = callframe; + globalThis.throw("ZlibEncoder is not constructable", .{}); + return null; + } + + pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(4).slice(); + + if (arguments.len < 4) { + globalThis.throwNotEnoughArguments("ZlibEncoder", 4, arguments.len); + return .zero; + } + + const opts = arguments[0]; + const callback = arguments[2]; + const mode = arguments[3].to(bun.zlib.NodeMode); + + var options = Options.fromJS(globalThis, mode, opts) orelse return .zero; + + if (mode == .GZIP or mode == .GUNZIP) options.windowBits += 16; + if (mode == .UNZIP) options.windowBits += 32; + if (mode == .DEFLATERAW or mode == .INFLATERAW) options.windowBits *= -1; + + // In zlib v1.2.9, 8 become an invalid value for this parameter, so we gracefully fix it. + // Ref: https://github.com/nodejs/node/commit/241eb6122ee6f36de16ee4ed4a6a291510b1807f + if (mode == .DEFLATERAW and options.windowBits == -8) options.windowBits = -9; + + var this: *ZlibEncoder = ZlibEncoder.new(.{ + .globalThis = globalThis, + .maxOutputLength = options.maxOutputLength, + .stream = .{ + .mode = mode, + .chunkSize = options.chunkSize, + .flush = @enumFromInt(options.flush), + .finishFlush = @enumFromInt(options.finishFlush), + .fullFlush = @enumFromInt(options.fullFlush), + .level = options.level, + .windowBits = options.windowBits, + .memLevel = options.memLevel, + .strategy = options.strategy, + .dictionary = options.dictionary.slice(), + }, + }); + this.stream.init() catch { + globalThis.throw("Failed to create ZlibEncoder", .{}); + return .zero; + }; + + const out = this.toJS(globalThis); + this.callback_value.set(globalThis, callback); + + return out; + } + + pub fn finalize(this: *@This()) callconv(.C) void { + this.deinit(); + } + + pub fn deinit(this: *@This()) void { + this.input.deinit(); + this.output.deinit(bun.default_allocator); + this.callback_value.deinit(); + this.destroy(); + } + + pub fn transformSync(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(4); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("ZlibEncoder.encode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("ZlibEncoder.encodeSync called after ZlibEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + const optional_flushFlag = arguments.ptr[3]; + + const old_flushFlag = this.stream.flush; + defer this.stream.flush = old_flushFlag; + blk: { + if (!optional_flushFlag.isInt32()) break :blk; + const int = optional_flushFlag.asInt32(); + if (int < 0) break :blk; + if (int > 5) break :blk; + this.stream.flush = @enumFromInt(int); + } + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, false) orelse { + return globalThis.throwInvalidArgumentTypeValue("buffer", "string or an instance of Buffer, TypedArray, DataView, or ArrayBuffer", input); + }; + defer input_to_queue.deinit(); + + if (is_last) + this.has_called_end = true; + + { + this.stream.write(input_to_queue.slice(), &this.output, true) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + } + if (this.output.items.len > this.maxOutputLength) { + globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}).throw(); + return .zero; + } + if (is_last) { + this.stream.end(&this.output) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + } + if (this.output.items.len > this.maxOutputLength) { + globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}).throw(); + return .zero; + } + + if (!is_last and this.output.items.len == 0) { + return JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); + } + if (this.write_failure != null) { + globalThis.vm().throwError(globalThis, this.write_failure.?.toError(globalThis)); + return .zero; + } + return this.collectOutputValue(); + } + + pub fn transformWith(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(4); + + if (arguments.len < 4) { + globalThis.throwNotEnoughArguments("ZlibEncoder.encode", 4, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("ZlibEncoder.encodeSync called after ZlibEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const thisctx = arguments.ptr[2]; + const is_last = callframe.argument(3).toBoolean(); + + const push_fn: JSC.JSValue = thisctx.get(globalThis, "push") orelse { + globalThis.throw("are you sure this is a stream.Transform?", .{}); + return .zero; + }; + if (globalThis.hasException()) return .zero; + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, false) orelse { + return globalThis.throwInvalidArgumentTypeValue("buffer", "string or an instance of Buffer, TypedArray, DataView, or ArrayBuffer", input); + }; + defer input_to_queue.deinit(); + + if (is_last) + this.has_called_end = true; + + const err_buffer_too_large = globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}); + + { + this.stream.write(input_to_queue.slice(), &this.output, false) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + if (this.output.items.len > this.maxOutputLength) { + err_buffer_too_large.throw(); + return .zero; + } + while (true) { + const done = this.stream.doWork(&this.output, this.stream.flush) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + if (this.output.items.len > this.maxOutputLength) { + err_buffer_too_large.throw(); + return .zero; + } + if (this.output.items.len > 0) _ = push_fn.call(globalThis, thisctx, &.{this.collectOutputValue()}) catch return .zero; + if (done) break; + } + } + if (is_last) { + this.stream.end(&this.output) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + if (this.output.items.len > this.maxOutputLength) { + globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}).throw(); + return .zero; + } + if (this.output.items.len > 0) _ = push_fn.call(globalThis, thisctx, &.{this.collectOutputValue()}) catch return .zero; + } + return .undefined; + } + + pub fn transform(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(3); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("ZlibEncoder.encode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("ZlibEncoder.encode called after ZlibEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + globalThis.throwInvalidArgumentType("ZlibEncoder.encode", "input", "Blob, String, or Buffer"); + return .zero; + }; + + _ = this.has_pending_activity.fetchAdd(1, .monotonic); + if (is_last) + this.has_called_end = true; + + var task = EncodeJob.new(.{ + .encoder = this, + }); + + { + this.input_lock.lock(); + defer this.input_lock.unlock(); + + this.input.writeItem(input_to_queue) catch unreachable; + } + this.poll_ref.ref(globalThis.bunVM()); + JSC.WorkPool.schedule(&task.task); + + return .undefined; + } + + pub fn runFromJSThread(this: *@This()) void { + this.poll_ref.unref(this.globalThis.bunVM()); + + defer _ = this.has_pending_activity.fetchSub(1, .monotonic); + this.drainFreelist(); + + _ = this.callback_value.get().?.call( + this.globalThis, + .undefined, + if (this.write_failure != null) + &.{this.write_failure.?.toError(this.globalThis)} + else + &.{ .null, this.collectOutputValue() }, + ) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err); + } + + pub fn hasPendingActivity(this: *@This()) callconv(.C) bool { + return this.has_pending_activity.load(.monotonic) > 0; + } + + fn drainFreelist(this: *ZlibEncoder) void { + this.freelist_write_lock.lock(); + defer this.freelist_write_lock.unlock(); + const to_free = this.freelist.readableSlice(0); + for (to_free) |*input| { + input.deinit(); + } + this.freelist.discard(to_free.len); + } + + fn collectOutputValue(this: *ZlibEncoder) JSC.JSValue { + this.output_lock.lock(); + defer this.output_lock.unlock(); + + defer this.output.clearRetainingCapacity(); + return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items); + } + + const EncodeJob = struct { + task: JSC.WorkPoolTask = .{ .callback = &runTask }, + encoder: *ZlibEncoder, + + pub usingnamespace bun.New(@This()); + + pub fn runTask(this: *JSC.WorkPoolTask) void { + var job: *EncodeJob = @fieldParentPtr("task", this); + job.run(); + job.destroy(); + } + + pub fn run(this: *EncodeJob) void { + const vm = this.encoder.globalThis.bunVMConcurrently(); + defer this.encoder.poll_ref.unrefConcurrently(vm); + defer { + _ = this.encoder.has_pending_activity.fetchSub(1, .monotonic); + } + + var any = false; + + if (this.encoder.pending_encode_job_count.fetchAdd(1, .monotonic) >= 0) { + const is_last = this.encoder.has_called_end; + outer: while (true) { + this.encoder.input_lock.lock(); + defer this.encoder.input_lock.unlock(); + const readable = this.encoder.input.readableSlice(0); + defer this.encoder.input.discard(readable.len); + const pending = readable; + + defer { + this.encoder.freelist_write_lock.lock(); + this.encoder.freelist.write(pending) catch unreachable; + this.encoder.freelist_write_lock.unlock(); + } + for (pending) |input| { + const output = &this.encoder.output; + this.encoder.stream.write(input.slice(), output, true) catch |e| { + any = true; + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "ZlibError: {s}", .{@errorName(e)}); // TODO propagate better error + break :outer; + }; + if (this.encoder.output.items.len > this.encoder.maxOutputLength) { + any = true; + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.encoder.maxOutputLength}); + break :outer; + } + } + + any = any or pending.len > 0; + + if (this.encoder.pending_encode_job_count.fetchSub(1, .monotonic) == 0) + break; + } + + if (is_last and any) { + const output = &this.encoder.output; + this.encoder.output_lock.lock(); + defer this.encoder.output_lock.unlock(); + + this.encoder.stream.end(output) catch |e| { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "ZlibError: {s}", .{@errorName(e)}); // TODO propogate better error + return; + }; + if (this.encoder.output.items.len > this.encoder.maxOutputLength) { + _ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.encoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.encoder.maxOutputLength}); + return; + } + } + } + + if (any) { + _ = this.encoder.has_pending_activity.fetchAdd(1, .monotonic); + this.encoder.poll_ref.refConcurrently(vm); + this.encoder.poll_ref.refConcurrently(vm); + vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.encoder))); + } + } + }; + + pub fn reset(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = globalThis; + _ = callframe; + _ = bun.zlib.deflateReset(&this.stream.state); + return .undefined; + } + + pub fn getBytesWritten(this: *@This(), globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(@as(u64, this.stream.state.total_in)); + } + + pub fn getLevel(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.level); + } + + pub fn getStrategy(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.strategy); + } + + pub fn getClosed(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsBoolean(this.closed); + } + + pub fn close(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(3).ptr; + if (this.stream.mode != .DEFLATE) return .undefined; + + const level = if (arguments[0] != .zero) (globalThis.validateIntegerRange(arguments[0], i16, -1, .{ .max = 9, .min = -1, .field_name = "level" }) orelse return .zero) else this.stream.level; + const strategy = if (arguments[1] != .zero) (globalThis.validateIntegerRange(arguments[1], u8, 0, .{ .max = 4, .min = 0, .field_name = "strategy" }) orelse return .zero) else this.stream.strategy; + this.stream.params(level, strategy); + + if (arguments[2] != .zero) { + if (!arguments[2].isFunction()) { + return globalThis.throwInvalidArgumentTypeValue("callback", "function", arguments[2]); + } + this.callback_value.set(globalThis, arguments[2]); + } + + return .undefined; + } + + pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.chunkSize); + } + + pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.flush); + } + + pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.finishFlush); + } + + pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.fullFlush); + } + + pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.maxOutputLength); + } +}; + +pub const ZlibDecoder = struct { + pub usingnamespace bun.New(@This()); + pub usingnamespace JSC.Codegen.JSZlibDecoder; + + globalThis: *JSC.JSGlobalObject, + stream: bun.zlib.ZlibDecompressorStreaming, + maxOutputLength: usize, + + freelist: Queue = Queue.init(bun.default_allocator), + freelist_write_lock: bun.Lock = .{}, + + input: Queue = Queue.init(bun.default_allocator), + input_lock: bun.Lock = .{}, + + has_called_end: bool = false, + callback_value: JSC.Strong = .{}, + + output: std.ArrayListUnmanaged(u8) = .{}, + output_lock: bun.Lock = .{}, + + has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + pending_encode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + ref_count: u32 = 1, + write_failure: ?JSC.DeferredError = null, + poll_ref: bun.Async.KeepAlive = .{}, + closed: bool = false, + + pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) ?*@This() { + _ = callframe; + globalThis.throw("ZlibDecoder is not constructable", .{}); + return null; + } + + pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(4).slice(); + + if (arguments.len < 4) { + globalThis.throwNotEnoughArguments("ZlibDecoder", 4, arguments.len); + return .zero; + } + + const opts = arguments[0]; + const callback = arguments[2]; + const mode = arguments[3].to(bun.zlib.NodeMode); + + var options = Options.fromJS(globalThis, mode, opts) orelse return .zero; + + if (mode == .GZIP or mode == .GUNZIP) options.windowBits += 16; + if (mode == .UNZIP) options.windowBits += 32; + if (mode == .DEFLATERAW or mode == .INFLATERAW) options.windowBits *= -1; + + var this: *ZlibDecoder = ZlibDecoder.new(.{ + .globalThis = globalThis, + .maxOutputLength = options.maxOutputLength, + .stream = .{ + .mode = mode, + .chunkSize = options.chunkSize, + .flush = @enumFromInt(options.flush), + .finishFlush = @enumFromInt(options.finishFlush), + .fullFlush = @enumFromInt(options.fullFlush), + .windowBits = options.windowBits, + .dictionary = options.dictionary.slice(), + }, + }); + this.stream.init() catch { + globalThis.throw("Failed to create ZlibDecoder", .{}); + return .zero; + }; + + const out = this.toJS(globalThis); + this.callback_value.set(globalThis, callback); + + return out; + } + + pub fn finalize(this: *@This()) callconv(.C) void { + this.deinit(); + } + + pub fn deinit(this: *@This()) void { + this.input.deinit(); + this.output.deinit(bun.default_allocator); + this.callback_value.deinit(); + this.destroy(); + } + + pub fn transformSync(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(4); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("ZlibDecoder.encode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("ZlibDecoder.encodeSync called after ZlibDecoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + const optional_flushFlag = arguments.ptr[3]; + + const old_flushFlag = this.stream.flush; + defer this.stream.flush = old_flushFlag; + blk: { + if (!optional_flushFlag.isInt32()) break :blk; + const int = optional_flushFlag.asInt32(); + if (int < 0) break :blk; + if (int > 5) break :blk; + this.stream.flush = @enumFromInt(int); + } + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + return globalThis.throwInvalidArgumentTypeValue("buffer", "string or an instance of Buffer, TypedArray, DataView, or ArrayBuffer", input); + }; + + if (is_last) + this.has_called_end = true; + + { + this.stream.writeAll(input_to_queue.slice(), &this.output, true) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + } + if (this.output.items.len > this.maxOutputLength) { + globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}).throw(); + return .zero; + } + if (is_last) { + this.stream.end(&this.output) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + } + if (this.output.items.len > this.maxOutputLength) { + globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}).throw(); + return .zero; + } + + if (!is_last and this.output.items.len == 0) { + return JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); + } + if (this.write_failure != null) { + globalThis.vm().throwError(globalThis, this.write_failure.?.toError(globalThis)); + return .zero; + } + return this.collectOutputValue(); + } + + pub fn transformWith(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(4); + + if (arguments.len < 4) { + globalThis.throwNotEnoughArguments("ZlibEncoder.encode", 4, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("ZlibEncoder.encodeSync called after ZlibEncoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const thisctx = arguments.ptr[2]; + const is_last = callframe.argument(3).toBoolean(); + + const push_fn: JSC.JSValue = thisctx.get(globalThis, "push") orelse { + globalThis.throw("are you sure this is a stream.Transform?", .{}); + return .zero; + }; + if (globalThis.hasException()) return .zero; + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValue(globalThis, bun.default_allocator, input, optional_encoding) orelse { + return globalThis.throwInvalidArgumentTypeValue("buffer", "string or an instance of Buffer, TypedArray, DataView, or ArrayBuffer", input); + }; + defer input_to_queue.deinit(); + if (is_last) + this.has_called_end = true; + + const err_buffer_too_large = globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}); + + { + const input_slice = input_to_queue.slice(); + this.stream.writeAll(input_slice, &this.output, false) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + if (this.output.items.len > this.maxOutputLength) { + err_buffer_too_large.throw(); + return .zero; + } + while (this.stream.do_inflate_loop) { + const done = this.stream.doWork(&this.output, this.stream.flush) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + if (this.output.items.len > this.maxOutputLength) { + err_buffer_too_large.throw(); + return .zero; + } + if (this.output.items.len > 0) _ = push_fn.call(globalThis, thisctx, &.{this.collectOutputValue()}) catch return .zero; + if (done) break; + } + } + if (is_last) { + this.stream.end(&this.output) catch |err| return handleTransformSyncStreamError(err, globalThis, this.stream.err_msg, &this.closed); + if (this.output.items.len > this.maxOutputLength) { + globalThis.ERR_BUFFER_TOO_LARGE("Cannot create a Buffer larger than {d} bytes", .{this.maxOutputLength}).throw(); + return .zero; + } + if (this.output.items.len > 0) _ = push_fn.call(globalThis, thisctx, &.{this.collectOutputValue()}) catch return .zero; + } + return .undefined; + } + + pub fn transform(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(3); + + if (arguments.len < 3) { + globalThis.throwNotEnoughArguments("ZlibDecoder.encode", 3, arguments.len); + return .zero; + } + + if (this.has_called_end) { + globalThis.throw("ZlibDecoder.encode called after ZlibDecoder.end", .{}); + return .zero; + } + + const input = callframe.argument(0); + const optional_encoding = callframe.argument(1); + const is_last = callframe.argument(2).toBoolean(); + + const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse { + globalThis.throwInvalidArgumentType("ZlibDecoder.encode", "input", "Blob, String, or Buffer"); + return .zero; + }; + + _ = this.has_pending_activity.fetchAdd(1, .monotonic); + if (is_last) + this.has_called_end = true; + + var task = DecodeJob.new(.{ + .decoder = this, + }); + + { + this.input_lock.lock(); + defer this.input_lock.unlock(); + + this.input.writeItem(input_to_queue) catch unreachable; + } + this.poll_ref.ref(globalThis.bunVM()); + JSC.WorkPool.schedule(&task.task); + + return .undefined; + } + + pub fn runFromJSThread(this: *@This()) void { + this.poll_ref.unref(this.globalThis.bunVM()); + + defer _ = this.has_pending_activity.fetchSub(1, .monotonic); + this.drainFreelist(); + + _ = this.callback_value.get().?.call( + this.globalThis, + .undefined, + if (this.write_failure != null) + &.{this.write_failure.?.toError(this.globalThis)} + else + &.{ .null, this.collectOutputValue() }, + ) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err); + } + + pub fn hasPendingActivity(this: *@This()) callconv(.C) bool { + return this.has_pending_activity.load(.monotonic) > 0; + } + + fn drainFreelist(this: *ZlibDecoder) void { + this.freelist_write_lock.lock(); + defer this.freelist_write_lock.unlock(); + const to_free = this.freelist.readableSlice(0); + for (to_free) |*input| { + input.deinit(); + } + this.freelist.discard(to_free.len); + } + + fn collectOutputValue(this: *ZlibDecoder) JSC.JSValue { + this.output_lock.lock(); + defer this.output_lock.unlock(); + + defer this.output.clearRetainingCapacity(); + return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items); + } + + const DecodeJob = struct { + task: JSC.WorkPoolTask = .{ .callback = &runTask }, + decoder: *ZlibDecoder, + + pub usingnamespace bun.New(@This()); + + pub fn runTask(this: *JSC.WorkPoolTask) void { + var job: *DecodeJob = @fieldParentPtr("task", this); + job.run(); + job.destroy(); + } + + pub fn run(this: *DecodeJob) void { + const vm = this.decoder.globalThis.bunVMConcurrently(); + defer this.decoder.poll_ref.unrefConcurrently(vm); + defer { + _ = this.decoder.has_pending_activity.fetchSub(1, .monotonic); + } + + var any = false; + + if (this.decoder.pending_encode_job_count.fetchAdd(1, .monotonic) >= 0) outer: { + const is_last = this.decoder.has_called_end; + while (true) { + this.decoder.input_lock.lock(); + defer this.decoder.input_lock.unlock(); + const readable = this.decoder.input.readableSlice(0); + defer this.decoder.input.discard(readable.len); + const pending = readable; + + defer { + this.decoder.freelist_write_lock.lock(); + this.decoder.freelist.write(pending) catch unreachable; + this.decoder.freelist_write_lock.unlock(); + } + for (pending) |input| { + const output = &this.decoder.output; + this.decoder.stream.writeAll(input.slice(), output, true) catch |e| { + any = true; + _ = this.decoder.pending_encode_job_count.fetchSub(1, .monotonic); + switch (e) { + error.ZlibError => { + const message = std.mem.sliceTo(this.decoder.stream.err_msg.?, 0); + this.decoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "{s}", .{message}); + break :outer; + }, + else => {}, + } + this.decoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "ZlibError: {s}", .{@errorName(e)}); // TODO propogate better error + break :outer; + }; + } + + any = any or pending.len > 0; + + if (this.decoder.pending_encode_job_count.fetchSub(1, .monotonic) == 0) + break; + } + + if (is_last and any) { + const output = &this.decoder.output; + this.decoder.output_lock.lock(); + defer this.decoder.output_lock.unlock(); + + this.decoder.stream.end(output) catch |e| { + any = true; + _ = this.decoder.pending_encode_job_count.fetchSub(1, .monotonic); + switch (e) { + error.ZlibError => { + const message = std.mem.sliceTo(this.decoder.stream.err_msg.?, 0); + this.decoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "{s}", .{message}); + break :outer; + }, + else => { + this.decoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "ZlibError: {s}", .{@errorName(e)}); // TODO propogate better error + break :outer; + }, + } + }; + if (output.items.len > this.decoder.maxOutputLength) { + any = true; + _ = this.decoder.pending_encode_job_count.fetchSub(1, .monotonic); + this.decoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.decoder.maxOutputLength}); + break :outer; + } + } + } + + if (any) { + _ = this.decoder.has_pending_activity.fetchAdd(1, .monotonic); + this.decoder.poll_ref.refConcurrently(vm); + this.decoder.poll_ref.refConcurrently(vm); + vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.decoder))); + } + } + }; + + pub fn reset(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = globalThis; + _ = callframe; + _ = bun.zlib.inflateReset(&this.stream.state); + return .undefined; + } + + pub fn getBytesWritten(this: *@This(), globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(@as(u64, this.stream.state.total_in)); + } + + pub fn getLevel(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = this; + _ = globalObject; + return JSC.JSValue.jsUndefined(); + } + + pub fn getStrategy(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = this; + _ = globalObject; + return JSC.JSValue.jsUndefined(); + } + + pub fn getClosed(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsBoolean(this.closed); + } + + pub fn close(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + return .undefined; + } + + pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.chunkSize); + } + + pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.flush); + } + + pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.finishFlush); + } + + pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.stream.fullFlush); + } + + pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + _ = globalObject; + return JSC.JSValue.jsNumber(this.maxOutputLength); + } +}; + +const Options = struct { + chunkSize: c_uint, + level: c_int, + windowBits: c_int, + memLevel: c_int, + strategy: c_int, + dictionary: JSC.Buffer, + maxOutputLength: usize, + flush: u8, + finishFlush: u8, + fullFlush: u8, + + pub fn fromJS(globalThis: *JSC.JSGlobalObject, mode: bun.zlib.NodeMode, opts: JSC.JSValue) ?Options { + const chunkSize = globalThis.getInteger(opts, c_uint, 48 * 1024, .{ + .field_name = "chunkSize", + .min = 64, + }) orelse return null; + const level = globalThis.getInteger(opts, i16, -1, .{ .field_name = "level", .min = -1, .max = 9 }) orelse return null; + const memLevel = globalThis.getInteger(opts, u8, 8, .{ .field_name = "memLevel", .min = 8, .max = 255 }) orelse return null; + const strategy = globalThis.getInteger(opts, u8, 0, .{ .field_name = "strategy", .min = 0, .max = 4 }) orelse return null; + const maxOutputLength = globalThis.getInteger(opts, usize, std.math.maxInt(u52), .{ .field_name = "maxOutputLength", .min = 0, .max = std.math.maxInt(u52) }) orelse return null; + const flush = globalThis.getInteger(opts, u8, 0, .{ .field_name = "flush", .min = 0, .max = 5 }) orelse return null; + const finishFlush = globalThis.getInteger(opts, u8, 4, .{ .field_name = "finishFlush", .min = 0, .max = 5 }) orelse return null; + const fullFlush = globalThis.getInteger(opts, u8, 3, .{ .field_name = "fullFlush", .min = 0, .max = 5 }) orelse return null; + + const windowBits = switch (mode) { + .NONE, + .BROTLI_DECODE, + .BROTLI_ENCODE, + => unreachable, + .DEFLATE, .DEFLATERAW => globalThis.getInteger(opts, u8, 15, .{ .min = 8, .max = 15, .field_name = "windowBits" }) orelse return null, + .INFLATE, .INFLATERAW => getWindowBits(globalThis, opts, "windowBits", u8, 8, 15, 15) orelse return null, + .GZIP => globalThis.getInteger(opts, i16, 15, .{ .min = 9, .max = 15, .field_name = "windowBits" }) orelse return null, + .GUNZIP, .UNZIP => getWindowBits(globalThis, opts, "windowBits", i16, 9, 15, 15) orelse return null, + }; + + const dictionary = blk: { + var exceptionref: JSC.C.JSValueRef = null; + const value: JSC.JSValue = opts.get(globalThis, "dictionary") orelse { + if (globalThis.hasException()) return null; + break :blk JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array); + }; + if (value.isUndefined()) { + break :blk JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array); + } + const buffer = JSC.Buffer.fromJS(globalThis, value, &exceptionref) orelse { + const ty_str = value.jsTypeString(globalThis).toSlice(globalThis, bun.default_allocator); + defer ty_str.deinit(); + globalThis.ERR_INVALID_ARG_TYPE("The \"options.dictionary\" property must be an instance of Buffer, TypedArray, DataView, or ArrayBuffer. Received {s}", .{ty_str.slice()}).throw(); + return null; + }; + if (exceptionref) |ptr| { + globalThis.throwValue(JSC.JSValue.c(ptr)); + return null; + } + break :blk buffer; + }; + + return .{ + .chunkSize = chunkSize, + .level = level, + .windowBits = windowBits, + .memLevel = memLevel, + .strategy = strategy, + .dictionary = dictionary, + .maxOutputLength = maxOutputLength, + .flush = flush, + .finishFlush = finishFlush, + .fullFlush = fullFlush, + }; + } + + // Specialization of globalThis.checkRangesOrGetDefault since windowBits also allows 0 when decompressing + fn getWindowBits(this: *JSC.JSGlobalObject, obj: JSC.JSValue, comptime field_name: []const u8, comptime T: type, comptime min: T, comptime max: T, comptime default: T) ?T { + return this.getInteger(obj, T, default, .{ + .field_name = field_name, + .min = min, + .max = max, + .always_allow_zero = true, + }); + } +}; + +fn handleTransformSyncStreamError(err: anyerror, globalThis: *JSC.JSGlobalObject, err_msg: ?[*:0]const u8, closed: *bool) JSC.JSValue { + switch (err) { + error.ZlibError => { + globalThis.throw("{s}", .{std.mem.sliceTo(err_msg.?, 0)}); + }, + else => { + globalThis.throw("ZlibError: {s}", .{@errorName(err)}); + }, + } + closed.* = true; + return .zero; +} diff --git a/src/bun.js/api/postgres.classes.ts b/src/bun.js/api/postgres.classes.ts new file mode 100644 index 0000000000000..ddb27007c7d03 --- /dev/null +++ b/src/bun.js/api/postgres.classes.ts @@ -0,0 +1,65 @@ +import { define } from "../../codegen/class-definitions"; + +export default [ + define({ + name: "PostgresSQLConnection", + construct: true, + finalize: true, + hasPendingActivity: true, + configurable: false, + klass: { + // escapeString: { + // fn: "escapeString", + // }, + // escapeIdentifier: { + // fn: "escapeIdentifier", + // }, + }, + JSType: "0b11101110", + proto: { + close: { + fn: "doClose", + }, + flush: { + fn: "doFlush", + }, + connected: { + getter: "getConnected", + }, + ref: { + fn: "doRef", + }, + unref: { + fn: "doUnref", + }, + query: { + fn: "createQuery", + }, + }, + }), + define({ + name: "PostgresSQLQuery", + construct: true, + finalize: true, + configurable: false, + hasPendingActivity: true, + JSType: "0b11101110", + klass: {}, + proto: { + run: { + fn: "doRun", + length: 2, + }, + cancel: { + fn: "doCancel", + length: 0, + }, + done: { + fn: "doDone", + length: 0, + }, + }, + values: ["pendingValue", "binding"], + estimatedSize: true, + }), +]; diff --git a/src/bun.js/api/server.classes.ts b/src/bun.js/api/server.classes.ts index b2f5610df8075..9182fa809e4f8 100644 --- a/src/bun.js/api/server.classes.ts +++ b/src/bun.js/api/server.classes.ts @@ -16,6 +16,10 @@ function generate(name) { fn: "doPublish", length: 3, }, + subscriberCount: { + fn: "doSubscriberCount", + length: 1, + }, reload: { fn: "doReload", length: 2, @@ -32,6 +36,10 @@ function generate(name) { fn: "doRequestIP", length: 1, }, + timeout: { + fn: "doTimeout", + length: 2, + }, port: { getter: "getPort", }, @@ -143,14 +151,17 @@ export default [ close: { fn: "close", length: 3, + passThis: true, }, terminate: { fn: "terminate", length: 0, + passThis: true, }, cork: { fn: "cork", length: 1, + passThis: true, }, getBufferedAmount: { fn: "getBufferedAmount", diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 6d8e50568f96a..e3f3f43ae956e 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -2,7 +2,7 @@ const Bun = @This(); const default_allocator = bun.default_allocator; const bun = @import("root").bun; const Environment = bun.Environment; - +const AnyBlob = bun.JSC.WebCore.AnyBlob; const Global = bun.Global; const strings = bun.strings; const string = bun.string; @@ -89,7 +89,8 @@ const SendfileContext = struct { }; const linux = std.os.linux; const Async = bun.Async; - +const httplog = Output.scoped(.Server, false); +const ctxLog = Output.scoped(.RequestContext, false); const BlobFileContentResult = struct { data: [:0]const u8, fn init(comptime fieldname: []const u8, js_obj: JSC.JSValue, global: *JSC.JSGlobalObject, exception: JSC.C.ExceptionRef) ?BlobFileContentResult { @@ -118,6 +119,306 @@ const BlobFileContentResult = struct { } }; +fn getContentType(headers: ?*JSC.FetchHeaders, blob: *const JSC.WebCore.AnyBlob, allocator: std.mem.Allocator) struct { MimeType, bool, bool } { + var needs_content_type = true; + var content_type_needs_free = false; + + const content_type: MimeType = brk: { + if (headers) |headers_| { + if (headers_.fastGet(.ContentType)) |content| { + needs_content_type = false; + + var content_slice = content.toSlice(allocator); + defer content_slice.deinit(); + + const content_type_allocator = if (content_slice.allocator.isNull()) null else allocator; + break :brk MimeType.init(content_slice.slice(), content_type_allocator, &content_type_needs_free); + } + } + + break :brk if (blob.contentType().len > 0) + MimeType.byName(blob.contentType()) + else if (MimeType.sniff(blob.slice())) |content| + content + else if (blob.wasString()) + MimeType.text + // TODO: should we get the mime type off of the Blob.Store if it exists? + // A little wary of doing this right now due to causing some breaking change + else + MimeType.other; + }; + + return .{ content_type, needs_content_type, content_type_needs_free }; +} + +fn writeHeaders( + headers: *JSC.FetchHeaders, + comptime ssl: bool, + resp_ptr: ?*uws.NewApp(ssl).Response, +) void { + ctxLog("writeHeaders", .{}); + headers.fastRemove(.ContentLength); + headers.fastRemove(.TransferEncoding); + if (!ssl) headers.fastRemove(.StrictTransportSecurity); + if (resp_ptr) |resp| { + headers.toUWSResponse(ssl, resp); + } +} + +fn writeStatus(comptime ssl: bool, resp_ptr: ?*uws.NewApp(ssl).Response, status: u16) void { + if (resp_ptr) |resp| { + if (HTTPStatusText.get(status)) |text| { + resp.writeStatus(text); + } else { + var status_text_buf: [48]u8 = undefined; + resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{status}) catch unreachable); + } + } +} + +const StaticRoute = struct { + server: ?AnyServer = null, + status_code: u16, + blob: AnyBlob, + cached_blob_size: u64 = 0, + has_content_disposition: bool = false, + headers: Headers = .{ + .allocator = bun.default_allocator, + }, + ref_count: u32 = 1, + + const HTTPResponse = uws.AnyResponse; + const Route = @This(); + + pub usingnamespace bun.NewRefCounted(@This(), deinit); + + fn deinit(this: *Route) void { + this.blob.detach(); + this.headers.deinit(); + + this.destroy(); + } + + pub fn fromJS(globalThis: *JSC.JSGlobalObject, argument: JSC.JSValue) ?*Route { + if (argument.as(JSC.WebCore.Response)) |response| { + + // The user may want to pass in the same Response object multiple endpoints + // Let's let them do that. + response.body.value.toBlobIfPossible(); + + var blob: AnyBlob = brk: { + switch (response.body.value) { + .Used => { + globalThis.throwInvalidArguments("Response body has already been used", .{}); + return null; + }, + + else => { + globalThis.throwInvalidArguments("Body must be fully buffered before it can be used in a static route. Consider calling new Response(await response.blob()) to buffer the body.", .{}); + return null; + }, + .Null, .Empty => { + break :brk AnyBlob{ + .InternalBlob = JSC.WebCore.InternalBlob{ + .bytes = std.ArrayList(u8).init(bun.default_allocator), + }, + }; + }, + + .Blob, .InternalBlob, .WTFStringImpl => { + if (response.body.value == .Blob and response.body.value.Blob.needsToReadFile()) { + globalThis.throwTODO("TODO: support Bun.file(path) in static routes"); + return null; + } + var blob = response.body.value.use(); + blob.globalThis = globalThis; + blob.allocator = null; + response.body.value = .{ .Blob = blob.dupe() }; + + break :brk .{ .Blob = blob }; + }, + } + }; + + var has_content_disposition = false; + + if (response.init.headers) |headers| { + has_content_disposition = headers.fastHas(.ContentDisposition); + headers.fastRemove(.TransferEncoding); + headers.fastRemove(.ContentLength); + } + + const headers: Headers = if (response.init.headers) |headers| + Headers.from(headers, bun.default_allocator, .{ + .body = &blob, + }) catch { + blob.detach(); + globalThis.throwOutOfMemory(); + return null; + } + else + .{ + .allocator = bun.default_allocator, + }; + + return Route.new(.{ + .blob = blob, + .cached_blob_size = blob.size(), + .has_content_disposition = has_content_disposition, + .headers = headers, + .server = null, + .status_code = response.statusCode(), + }); + } + + globalThis.throwInvalidArguments("Expected a Response object", .{}); + return null; + } + + // HEAD requests have no body. + pub fn onHEADRequest(this: *Route, req: *uws.Request, resp: HTTPResponse) void { + req.setYield(false); + this.ref(); + if (this.server) |server| { + server.onPendingRequest(); + resp.timeout(server.config().idleTimeout); + } + resp.corked(renderMetadataAndEnd, .{ this, resp }); + this.onResponseComplete(resp); + } + + fn renderMetadataAndEnd(this: *Route, resp: HTTPResponse) void { + this.renderMetadata(resp); + resp.writeHeaderInt("Content-Length", this.cached_blob_size); + resp.endWithoutBody(resp.shouldCloseConnection()); + } + + pub fn onRequest(this: *Route, req: *uws.Request, resp: HTTPResponse) void { + req.setYield(false); + this.ref(); + if (this.server) |server| { + server.onPendingRequest(); + resp.timeout(server.config().idleTimeout); + } + var finished = false; + this.doRenderBlob(resp, &finished); + if (finished) { + this.onResponseComplete(resp); + return; + } + + this.toAsync(resp); + } + + fn toAsync(this: *Route, resp: HTTPResponse) void { + resp.onAborted(*Route, onAborted, this); + resp.onWritable(*Route, onWritableBytes, this); + } + + fn onAborted(this: *Route, resp: HTTPResponse) void { + this.onResponseComplete(resp); + } + + fn onResponseComplete(this: *Route, resp: HTTPResponse) void { + resp.clearAborted(); + resp.clearOnWritable(); + resp.clearTimeout(); + + if (this.server) |server| { + server.onStaticRequestComplete(); + } + + this.deref(); + } + + pub fn doRenderBlob(this: *Route, resp: HTTPResponse, did_finish: *bool) void { + // We are not corked + // The body is small + // Faster to do the memcpy than to do the two network calls + // We are not streaming + // This is an important performance optimization + if (this.blob.fastSize() < 16384 - 1024) { + resp.corked(doRenderBlobCorked, .{ this, resp, did_finish }); + } else { + this.doRenderBlobCorked(resp, did_finish); + } + } + + pub fn doRenderBlobCorked(this: *Route, resp: HTTPResponse, did_finish: *bool) void { + this.renderMetadata(resp); + this.renderBytes(resp, did_finish); + } + + fn onWritable(this: *Route, write_offset: u64, resp: HTTPResponse) void { + if (this.server) |server| { + resp.timeout(server.config().idleTimeout); + } + + if (!this.onWritableBytes(write_offset, resp)) { + this.toAsync(resp); + return; + } + + this.onResponseComplete(resp); + } + + fn onWritableBytes(this: *Route, write_offset: u64, resp: HTTPResponse) bool { + const blob = this.blob; + const all_bytes = blob.slice(); + + const bytes = all_bytes[@min(all_bytes.len, @as(usize, @truncate(write_offset)))..]; + + if (!resp.tryEnd( + bytes, + all_bytes.len, + resp.shouldCloseConnection(), + )) { + return false; + } + + return true; + } + + fn doWriteStatus(_: *StaticRoute, status: u16, resp: HTTPResponse) void { + switch (resp) { + .SSL => |r| writeStatus(true, r, status), + .TCP => |r| writeStatus(false, r, status), + } + } + + fn doWriteHeaders(this: *StaticRoute, resp: HTTPResponse) void { + switch (resp) { + inline .SSL, .TCP => |s| { + const entries = this.headers.entries.slice(); + const names: []const Api.StringPointer = entries.items(.name); + const values: []const Api.StringPointer = entries.items(.value); + const buf = this.headers.buf.items; + + for (names, values) |name, value| { + s.writeHeader(name.slice(buf), value.slice(buf)); + } + }, + } + } + + fn renderBytes(this: *Route, resp: HTTPResponse, did_finish: *bool) void { + did_finish.* = this.onWritableBytes(0, resp); + } + + fn renderMetadata(this: *Route, resp: HTTPResponse) void { + var status = this.status_code; + const size = this.cached_blob_size; + + status = if (status == 200 and size == 0 and !this.blob.isDetached()) + 204 + else + status; + + this.doWriteStatus(status, resp); + this.doWriteHeaders(resp); + } +}; + pub const ServerConfig = struct { address: union(enum) { tcp: struct { @@ -142,7 +443,7 @@ pub const ServerConfig = struct { } = .{ .tcp = .{}, }, - + idleTimeout: u8 = 10, //TODO: should we match websocket default idleTimeout of 120? // TODO: use webkit URL parser instead of bun's base_url: URL = URL{}, base_uri: string = "", @@ -162,6 +463,41 @@ pub const ServerConfig = struct { id: []const u8 = "", allow_hot: bool = true, + static_routes: std.ArrayList(StaticRouteEntry) = std.ArrayList(StaticRouteEntry).init(bun.default_allocator), + + pub const StaticRouteEntry = struct { + path: []const u8, + route: *StaticRoute, + + pub fn deinit(this: *StaticRouteEntry) void { + bun.default_allocator.free(this.path); + this.route.deref(); + } + }; + + pub fn applyStaticRoutes(this: *ServerConfig, comptime ssl: bool, server: AnyServer, app: *uws.NewApp(ssl)) void { + for (this.static_routes.items) |entry| { + entry.route.server = server; + const handler_wrap = struct { + pub fn handler(route: *StaticRoute, req: *uws.Request, resp: *uws.NewApp(ssl).Response) void { + route.onRequest(req, switch (comptime ssl) { + true => .{ .SSL = resp }, + false => .{ .TCP = resp }, + }); + } + + pub fn HEAD(route: *StaticRoute, req: *uws.Request, resp: *uws.NewApp(ssl).Response) void { + route.onHEADRequest(req, switch (comptime ssl) { + true => .{ .SSL = resp }, + false => .{ .TCP = resp }, + }); + } + }; + app.head(entry.path, *StaticRoute, entry.route, handler_wrap.HEAD); + app.any(entry.path, *StaticRoute, entry.route, handler_wrap.handler); + } + } + pub fn deinit(this: *ServerConfig) void { this.address.deinit(bun.default_allocator); @@ -180,6 +516,11 @@ pub const ServerConfig = struct { this.sni.?.deinitWithAllocator(bun.default_allocator); this.sni = null; } + + for (this.static_routes.items) |*entry| { + entry.deinit(); + } + this.static_routes.clearAndFree(); } pub fn computeID(this: *const ServerConfig, allocator: std.mem.Allocator) []const u8 { @@ -836,8 +1177,8 @@ pub const ServerConfig = struct { }; pub fn fromJS(global: *JSC.JSGlobalObject, arguments: *JSC.Node.ArgumentsSlice, exception: JSC.C.ExceptionRef) ServerConfig { - var env = arguments.vm.bundler.env; - const vm = JSC.VirtualMachine.get(); + const vm = arguments.vm; + const env = vm.bundler.env; var args = ServerConfig{ .address = .{ @@ -847,8 +1188,13 @@ pub const ServerConfig = struct { }, }, .development = true, + + // If this is a node:cluster child, let's default to SO_REUSEPORT. + // That way you don't have to remember to set reusePort: true in Bun.serve() when using node:cluster. + .reuse_port = env.get("NODE_UNIQUE_ID") != null, }; var has_hostname = false; + if (strings.eqlComptime(env.get("NODE_ENV") orelse "", "production")) { args.development = false; } @@ -880,12 +1226,84 @@ pub const ServerConfig = struct { args.base_uri = origin; } + defer { + if (global.hasException() or exception.* != null) { + if (args.ssl_config) |*conf| { + conf.deinit(); + args.ssl_config = null; + } + } + } + if (arguments.next()) |arg| { if (!arg.isObject()) { JSC.throwInvalidArguments("Bun.serve expects an object", .{}, global, exception); return args; } + if (arg.get(global, "static")) |static| { + if (!static.isObject()) { + JSC.throwInvalidArguments("Bun.serve expects 'static' to be an object shaped like { [pathname: string]: Response }", .{}, global, exception); + return args; + } + + var iter = JSC.JSPropertyIterator(.{ + .skip_empty_name = true, + .include_value = true, + }).init(global, static); + defer iter.deinit(); + + while (iter.next()) |key| { + const path, const is_ascii = key.toOwnedSliceReturningAllASCII(bun.default_allocator) catch bun.outOfMemory(); + + const value = iter.value; + + if (path.len == 0 or path[0] != '/') { + bun.default_allocator.free(path); + JSC.throwInvalidArguments("Invalid static route \"{s}\". path must start with '/'", .{path}, global, exception); + return args; + } + + if (!is_ascii) { + bun.default_allocator.free(path); + JSC.throwInvalidArguments("Invalid static route \"{s}\". Please encode all non-ASCII characters in the path.", .{path}, global, exception); + return args; + } + + if (StaticRoute.fromJS(global, value)) |route| { + args.static_routes.append(.{ + .path = path, + .route = route, + }) catch bun.outOfMemory(); + } else if (global.hasException()) { + bun.default_allocator.free(path); + return args; + } else { + Output.panic("Internal error: expected exception or static route", .{}); + } + } + } + + if (global.hasException()) return args; + + if (arg.get(global, "idleTimeout")) |value| { + if (!value.isUndefinedOrNull()) { + if (!value.isAnyInt()) { + JSC.throwInvalidArguments("Bun.serve expects idleTimeout to be an integer", .{}, global, exception); + + return args; + } + + const idleTimeout: u64 = @intCast(@max(value.toInt64(), 0)); + if (idleTimeout > 255) { + JSC.throwInvalidArguments("Bun.serve expects idleTimeout to be 255 or less", .{}, global, exception); + return args; + } + + args.idleTimeout = @truncate(idleTimeout); + } + } + if (arg.getTruthy(global, "webSocket") orelse arg.getTruthy(global, "websocket")) |websocket_object| { if (!websocket_object.isObject()) { JSC.throwInvalidArguments("Expected websocket to be an object", .{}, global, exception); @@ -904,6 +1322,7 @@ pub const ServerConfig = struct { return args; } } + if (global.hasException()) return args; if (arg.getTruthy(global, "port")) |port_| { args.address.tcp.port = @as( @@ -915,6 +1334,7 @@ pub const ServerConfig = struct { ); port = args.address.tcp.port; } + if (global.hasException()) return args; if (arg.getTruthy(global, "baseURI")) |baseURI| { var sliced = baseURI.toSlice(global, bun.default_allocator); @@ -924,6 +1344,7 @@ pub const ServerConfig = struct { args.base_uri = bun.default_allocator.dupe(u8, sliced.slice()) catch unreachable; } } + if (global.hasException()) return args; if (arg.getTruthy(global, "hostname") orelse arg.getTruthy(global, "host")) |host| { const host_str = host.toSlice( @@ -937,6 +1358,7 @@ pub const ServerConfig = struct { has_hostname = true; } } + if (global.hasException()) return args; if (arg.getTruthy(global, "unix")) |unix| { const unix_str = unix.toSlice( @@ -947,15 +1369,13 @@ pub const ServerConfig = struct { if (unix_str.len > 0) { if (has_hostname) { JSC.throwInvalidArguments("Cannot specify both hostname and unix", .{}, global, exception); - if (args.ssl_config) |*conf| { - conf.deinit(); - } return args; } args.address = .{ .unix = bun.default_allocator.dupeZ(u8, unix_str.slice()) catch unreachable }; } } + if (global.hasException()) return args; if (arg.get(global, "id")) |id| { if (id.isUndefinedOrNull()) { @@ -973,46 +1393,46 @@ pub const ServerConfig = struct { } } } + if (global.hasException()) return args; if (arg.get(global, "development")) |dev| { args.development = dev.coerce(bool, global); args.reuse_port = !args.development; } + if (global.hasException()) return args; if (arg.get(global, "reusePort")) |dev| { args.reuse_port = dev.coerce(bool, global); } + if (global.hasException()) return args; if (arg.get(global, "inspector")) |inspector| { args.inspector = inspector.coerce(bool, global); if (args.inspector and !args.development) { JSC.throwInvalidArguments("Cannot enable inspector in production. Please set development: true in Bun.serve()", .{}, global, exception); - if (args.ssl_config) |*conf| { - conf.deinit(); - } return args; } } + if (global.hasException()) return args; if (arg.getTruthy(global, "maxRequestBodySize")) |max_request_body_size| { if (max_request_body_size.isNumber()) { args.max_request_body_size = @as(u64, @intCast(@max(0, max_request_body_size.toInt64()))); } } + if (global.hasException()) return args; - if (arg.getTruthy(global, "error")) |onError| { + if (arg.getTruthyComptime(global, "error")) |onError| { if (!onError.isCallable(global.vm())) { JSC.throwInvalidArguments("Expected error to be a function", .{}, global, exception); - if (args.ssl_config) |*conf| { - conf.deinit(); - } return args; } const onErrorSnapshot = onError.withAsyncContextIfNeeded(global); args.onError = onErrorSnapshot; onErrorSnapshot.protect(); } + if (global.hasException()) return args; if (arg.getTruthy(global, "fetch")) |onRequest_| { if (!onRequest_.isCallable(global.vm())) { @@ -1023,10 +1443,8 @@ pub const ServerConfig = struct { JSC.C.JSValueProtect(global, onRequest.asObjectRef()); args.onRequest = onRequest; } else { + if (global.hasException()) return args; JSC.throwInvalidArguments("Expected fetch() to be a function", .{}, global, exception); - if (args.ssl_config) |*conf| { - conf.deinit(); - } return args; } @@ -1078,6 +1496,7 @@ pub const ServerConfig = struct { } } } + if (global.hasException()) return args; // @compatibility Bun v0.x - v0.2.1 // this used to be top-level, now it's "tls" object @@ -1282,6 +1701,7 @@ fn NewFlags(comptime debug_mode: bool) type { has_marked_complete: bool = false, has_marked_pending: bool = false, has_abort_handler: bool = false, + has_timeout_handler: bool = false, has_sendfile_ctx: bool = false, has_called_error_handler: bool = false, needs_content_length: bool = false, @@ -1320,6 +1740,54 @@ pub const AnyRequestContext = struct { pub fn init(request_ctx: anytype) AnyRequestContext { return .{ .tagged_pointer = Pointer.init(request_ctx) }; } + pub fn get(self: AnyRequestContext, comptime T: type) ?*T { + return self.tagged_pointer.get(T); + } + + pub fn setTimeout(self: AnyRequestContext, seconds: c_uint) bool { + if (self.tagged_pointer.isNull()) { + return false; + } + + switch (self.tagged_pointer.tag()) { + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => { + return self.tagged_pointer.as(HTTPServer.RequestContext).setTimeout(seconds); + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => { + return self.tagged_pointer.as(HTTPSServer.RequestContext).setTimeout(seconds); + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => { + return self.tagged_pointer.as(DebugHTTPServer.RequestContext).setTimeout(seconds); + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => { + return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).setTimeout(seconds); + }, + else => @panic("Unexpected AnyRequestContext tag"), + } + return false; + } + + pub fn enableTimeoutEvents(self: AnyRequestContext) void { + if (self.tagged_pointer.isNull()) { + return; + } + + switch (self.tagged_pointer.tag()) { + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => { + return self.tagged_pointer.as(HTTPServer.RequestContext).setTimeoutHandler(); + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => { + return self.tagged_pointer.as(HTTPSServer.RequestContext).setTimeoutHandler(); + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => { + return self.tagged_pointer.as(DebugHTTPServer.RequestContext).setTimeoutHandler(); + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => { + return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).setTimeoutHandler(); + }, + else => @panic("Unexpected AnyRequestContext tag"), + } + } pub fn getRemoteSocketInfo(self: AnyRequestContext) ?uws.SocketAddress { if (self.tagged_pointer.isNull()) { @@ -1343,6 +1811,27 @@ pub const AnyRequestContext = struct { } } + pub fn detachRequest(self: AnyRequestContext) void { + if (self.tagged_pointer.isNull()) { + return; + } + switch (self.tagged_pointer.tag()) { + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => { + self.tagged_pointer.as(HTTPServer.RequestContext).req = null; + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => { + self.tagged_pointer.as(HTTPSServer.RequestContext).req = null; + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => { + self.tagged_pointer.as(DebugHTTPServer.RequestContext).req = null; + }, + @field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => { + self.tagged_pointer.as(DebugHTTPSServer.RequestContext).req = null; + }, + else => @panic("Unexpected AnyRequestContext tag"), + } + } + /// Wont actually set anything if `self` is `.none` pub fn setRequest(self: AnyRequestContext, req: *uws.Request) void { if (self.tagged_pointer.isNull()) { @@ -1393,24 +1882,25 @@ pub const AnyRequestContext = struct { fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comptime ThisServer: type) type { return struct { const RequestContext = @This(); - const ctxLog = Output.scoped(.RequestContext, false); + const App = uws.NewApp(ssl_enabled); pub threadlocal var pool: ?*RequestContext.RequestContextStackAllocator = null; pub const ResponseStream = JSC.WebCore.HTTPServerWritable(ssl_enabled); // This pre-allocates up to 2,048 RequestContext structs. // It costs about 655,632 bytes. - pub const RequestContextStackAllocator = bun.HiveArray(RequestContext, 2048).Fallback; + pub const RequestContextStackAllocator = bun.HiveArray(RequestContext, if (bun.heap_breakdown.enabled) 0 else 2048).Fallback; pub const name = "HTTPRequestContext" ++ (if (debug_mode) "Debug" else "") ++ (if (ThisServer.ssl_enabled) "TLS" else ""); pub const shim = JSC.Shimmer("Bun", name, @This()); - server: *ThisServer, + server: ?*ThisServer, resp: ?*App.Response, /// thread-local default heap allocator /// this prevents an extra pthread_getspecific() call which shows up in profiling allocator: std.mem.Allocator, - req: *uws.Request, + req: ?*uws.Request, + request_weakref: Request.WeakRef = .{}, signal: ?*JSC.WebCore.AbortSignal = null, method: HTTP.Method, @@ -1421,14 +1911,15 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp /// We can only safely free once the request body promise is finalized /// and the response is rejected response_jsvalue: JSC.JSValue = JSC.JSValue.zero, - pending_promises_for_abort: u8 = 0, + ref_count: u8 = 1, response_ptr: ?*JSC.WebCore.Response = null, blob: JSC.WebCore.AnyBlob = JSC.WebCore.AnyBlob{ .Blob = .{} }, - promise: ?*JSC.JSValue = null, sendfile: SendfileContext = undefined, - request_body: ?*JSC.WebCore.BodyValueRef = null, + + request_body_readable_stream_ref: JSC.WebCore.ReadableStream.Strong = .{}, + request_body: ?*JSC.BodyValueRef = null, request_body_buf: std.ArrayListUnmanaged(u8) = .{}, request_body_content_len: usize = 0, @@ -1456,7 +1947,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp fn drainMicrotasks(this: *const RequestContext) void { if (this.isAsync()) return; - this.server.vm.drainMicrotasks(); + if (this.server) |server| server.vm.drainMicrotasks(); } pub fn setAbortHandler(this: *RequestContext) void { @@ -1467,25 +1958,24 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } } - pub fn onResolve(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn setTimeoutHandler(this: *RequestContext) void { + if (this.flags.has_timeout_handler) return; + if (this.resp) |resp| { + this.flags.has_timeout_handler = true; + resp.onTimeout(*RequestContext, RequestContext.onTimeout, this); + } + } + + pub fn onResolve(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { ctxLog("onResolve", .{}); const arguments = callframe.arguments(2); var ctx = arguments.ptr[1].asPromisePtr(@This()); + defer ctx.deref(); + const result = arguments.ptr[0]; result.ensureStillAlive(); - ctx.pending_promises_for_abort -|= 1; - if (ctx.flags.aborted) { - ctx.finalizeForAbort(); - return JSValue.jsUndefined(); - } - - if (ctx.didUpgradeWebSocket()) { - ctx.finalize(); - return JSValue.jsUndefined(); - } - handleResolve(ctx, result); return JSValue.jsUndefined(); } @@ -1493,32 +1983,43 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp fn renderMissingInvalidResponse(ctx: *RequestContext, value: JSC.JSValue) void { var class_name = value.getClassInfoName() orelse bun.String.empty; defer class_name.deref(); - const globalThis: *JSC.JSGlobalObject = ctx.server.globalThis; - Output.enableBuffering(); - var writer = Output.errorWriter(); + if (ctx.server) |server| { + const globalThis: *JSC.JSGlobalObject = server.globalThis; - if (class_name.eqlComptime("Response")) { - Output.errGeneric("Expected a native Response object, but received a polyfilled Response object. Bun.serve() only supports native Response objects.", .{}); - } else if (!value.isEmpty() and !globalThis.hasException()) { - var formatter = JSC.ConsoleObject.Formatter{ - .globalThis = globalThis, - .quote_strings = true, - }; - Output.errGeneric("Expected a Response object, but received '{}'", .{value.toFmt(formatter.globalThis, &formatter)}); - } else { - Output.errGeneric("Expected a Response object", .{}); - } + Output.enableBuffering(); + var writer = Output.errorWriter(); - Output.flush(); - if (!globalThis.hasException()) { - JSC.ConsoleObject.writeTrace(@TypeOf(&writer), &writer, globalThis); + if (class_name.eqlComptime("Response")) { + Output.errGeneric("Expected a native Response object, but received a polyfilled Response object. Bun.serve() only supports native Response objects.", .{}); + } else if (!value.isEmpty() and !globalThis.hasException()) { + var formatter = JSC.ConsoleObject.Formatter{ + .globalThis = globalThis, + .quote_strings = true, + }; + Output.errGeneric("Expected a Response object, but received '{}'", .{value.toFmt(&formatter)}); + } else { + Output.errGeneric("Expected a Response object", .{}); + } + + Output.flush(); + if (!globalThis.hasException()) { + JSC.ConsoleObject.writeTrace(@TypeOf(&writer), &writer, globalThis); + } + Output.flush(); } - Output.flush(); ctx.renderMissing(); } fn handleResolve(ctx: *RequestContext, value: JSC.JSValue) void { + if (ctx.isAbortedOrEnded() or ctx.didUpgradeWebSocket()) { + return; + } + + if (ctx.server == null) { + ctx.renderMissingInvalidResponse(value); + return; + } if (value.isEmptyOrUndefinedOrNull() or !value.isCell()) { ctx.renderMissingInvalidResponse(value); return; @@ -1531,54 +2032,124 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctx.response_jsvalue = value; assert(!ctx.flags.response_protected); ctx.flags.response_protected = true; - JSC.C.JSValueProtect(ctx.server.globalThis, value.asObjectRef()); + JSC.C.JSValueProtect(ctx.server.?.globalThis, value.asObjectRef()); ctx.render(response); } - pub fn finalizeForAbort(this: *RequestContext) void { - streamLog("finalizeForAbort", .{}); - this.pending_promises_for_abort -|= 1; - if (this.pending_promises_for_abort == 0) this.finalize(); + pub fn shouldRenderMissing(this: *RequestContext) bool { + // If we did not respond yet, we should render missing + // To allow this all the conditions above should be true: + // 1 - still has a response (not detached) + // 2 - not aborted + // 3 - not marked completed + // 4 - not marked pending + // 5 - is the only reference of the context + // 6 - is not waiting for request body + // 7 - did not call sendfile + return this.resp != null and !this.flags.aborted and !this.flags.has_marked_complete and !this.flags.has_marked_pending and this.ref_count == 1 and !this.flags.is_waiting_for_request_body and !this.flags.has_sendfile_ctx; } - pub fn onReject(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { - ctxLog("onReject", .{}); + pub fn isDeadRequest(this: *RequestContext) bool { + // check if has pending promise or extra reference (aka not the only reference) + if (this.ref_count > 1) return false; + // check if the body is Locked (streaming) + if (this.request_body) |body| { + if (body.value == .Locked) { + return false; + } + } - const arguments = callframe.arguments(2); - var ctx = arguments.ptr[1].asPromisePtr(@This()); - const err = arguments.ptr[0]; + return true; + } - ctx.pending_promises_for_abort -|= 1; + /// destroy RequestContext, should be only called by deref or if defer_deinit_until_callback_completes is ref is set to true + fn deinit(this: *RequestContext) void { + this.detachResponse(); + this.endRequestStreamingAndDrain(); + // TODO: has_marked_complete is doing something? + this.flags.has_marked_complete = true; - if (ctx.flags.aborted) { - ctx.finalizeForAbort(); - return JSValue.jsUndefined(); + if (this.defer_deinit_until_callback_completes) |defer_deinit| { + defer_deinit.* = true; + ctxLog("deferred deinit ({*})", .{this}); + return; } - handleReject(ctx, if (!err.isEmptyOrUndefinedOrNull()) err else JSC.JSValue.jsUndefined()); + + ctxLog("deinit ({*})", .{this}); + if (comptime Environment.allow_assert) + assert(this.flags.has_finalized); + + this.request_body_buf.clearAndFree(this.allocator); + this.response_buf_owned.clearAndFree(this.allocator); + + if (this.request_body) |body| { + _ = body.unref(); + this.request_body = null; + } + + if (this.server) |server| { + this.server = null; + server.request_pool_allocator.put(this); + server.onRequestComplete(); + } + } + + pub fn deref(this: *RequestContext) void { + streamLog("deref", .{}); + assert(this.ref_count > 0); + const ref_count = this.ref_count; + this.ref_count -= 1; + if (ref_count == 1) { + this.finalizeWithoutDeinit(); + this.deinit(); + } + } + + pub fn ref(this: *RequestContext) void { + streamLog("ref", .{}); + this.ref_count += 1; + } + + pub fn onReject(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + ctxLog("onReject", .{}); + + const arguments = callframe.arguments(2); + const ctx = arguments.ptr[1].asPromisePtr(@This()); + const err = arguments.ptr[0]; + defer ctx.deref(); + handleReject(ctx, if (!err.isEmptyOrUndefinedOrNull()) err else .undefined); return JSValue.jsUndefined(); } fn handleReject(ctx: *RequestContext, value: JSC.JSValue) void { - if (ctx.resp == null) { - ctx.finalizeForAbort(); + if (ctx.isAbortedOrEnded()) { return; } + const resp = ctx.resp.?; const has_responded = resp.hasResponded(); - if (!has_responded) + if (!has_responded) { + const original_state = ctx.defer_deinit_until_callback_completes; + var should_deinit_context = false; + ctx.defer_deinit_until_callback_completes = &should_deinit_context; ctx.runErrorHandler( value, ); - - if (ctx.flags.aborted) { - ctx.finalizeForAbort(); + ctx.defer_deinit_until_callback_completes = original_state; + // we try to deinit inside runErrorHandler so we just return here and let it deinit + if (should_deinit_context) { + ctx.deinit(); + return; + } + } + // check again in case it get aborted after runErrorHandler + if (ctx.isAbortedOrEnded()) { return; } // I don't think this case happens? if (ctx.didUpgradeWebSocket()) { - ctx.finalize(); return; } @@ -1592,7 +2163,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (ctx.resp) |resp| { resp.runCorkedWithType(*RequestContext, renderMissingCorked, ctx); } - ctx.finalize(); } pub fn renderMissingCorked(ctx: *RequestContext) void { @@ -1602,30 +2172,30 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp resp.writeStatus("204 No Content"); ctx.flags.has_written_status = true; ctx.end("", ctx.shouldCloseConnection()); - } else { - // avoid writing the status again and missmatching the content-length - if (ctx.flags.has_written_status) { - ctx.end("", ctx.shouldCloseConnection()); - return; - } - - if (ctx.flags.is_web_browser_navigation) { - resp.writeStatus("200 OK"); - ctx.flags.has_written_status = true; + return; + } + // avoid writing the status again and missmatching the content-length + if (ctx.flags.has_written_status) { + ctx.end("", ctx.shouldCloseConnection()); + return; + } - resp.writeHeader("content-type", MimeType.html.value); - resp.writeHeader("content-encoding", "gzip"); - resp.writeHeaderInt("content-length", welcome_page_html_gz.len); - ctx.end(welcome_page_html_gz, ctx.shouldCloseConnection()); - return; - } - const missing_content = "Welcome to Bun! To get started, return a Response object."; + if (ctx.flags.is_web_browser_navigation) { resp.writeStatus("200 OK"); - resp.writeHeader("content-type", MimeType.text.value); - resp.writeHeaderInt("content-length", missing_content.len); ctx.flags.has_written_status = true; - ctx.end(missing_content, ctx.shouldCloseConnection()); + + resp.writeHeader("content-type", MimeType.html.value); + resp.writeHeader("content-encoding", "gzip"); + resp.writeHeaderInt("content-length", welcome_page_html_gz.len); + ctx.end(welcome_page_html_gz, ctx.shouldCloseConnection()); + return; } + const missing_content = "Welcome to Bun! To get started, return a Response object."; + resp.writeStatus("200 OK"); + resp.writeHeader("content-type", MimeType.text.value); + resp.writeHeaderInt("content-length", missing_content.len); + ctx.flags.has_written_status = true; + ctx.end(missing_content, ctx.shouldCloseConnection()); } } @@ -1676,7 +2246,10 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ) catch unreachable; if (this.resp == null or this.resp.?.tryEnd(bb.items, bb.items.len, this.shouldCloseConnection())) { bb.clearAndFree(); + this.detachResponse(); + this.endRequestStreamingAndDrain(); this.finalizeWithoutDeinit(); + this.deref(); return; } @@ -1686,7 +2259,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (this.resp) |resp| { resp.onWritable(*RequestContext, onWritableCompleteResponseBuffer, this); } - this.setAbortHandler(); } pub fn renderResponseBuffer(this: *RequestContext) void { @@ -1707,18 +2279,18 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp )) { this.flags.has_marked_pending = true; resp.onWritable(*RequestContext, onWritableCompleteResponseBuffer, this); - this.setAbortHandler(); return; } } - this.finalize(); + this.detachResponse(); + this.endRequestStreamingAndDrain(); + this.deref(); } /// Drain a partial response buffer pub fn drainResponseBufferAndMetadata(this: *RequestContext) void { if (this.resp) |resp| { this.renderMetadata(); - this.setAbortHandler(); _ = resp.write( this.response_buf_owned.items, @@ -1729,64 +2301,56 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn end(this: *RequestContext, data: []const u8, closeConnection: bool) void { if (this.resp) |resp| { - if (this.flags.is_waiting_for_request_body) { - this.flags.is_waiting_for_request_body = false; - resp.clearOnData(); - } + defer this.deref(); + + this.detachResponse(); + this.endRequestStreamingAndDrain(); resp.end(data, closeConnection); - this.resp = null; } } pub fn endStream(this: *RequestContext, closeConnection: bool) void { ctxLog("endStream", .{}); if (this.resp) |resp| { - if (this.flags.is_waiting_for_request_body) { - this.flags.is_waiting_for_request_body = false; - resp.clearOnData(); - } + defer this.deref(); + this.detachResponse(); + this.endRequestStreamingAndDrain(); // This will send a terminating 0\r\n\r\n chunk to the client // We only want to do that if they're still expecting a body // We cannot call this function if the Content-Length header was previously set if (resp.state().isResponsePending()) resp.endStream(closeConnection); - - this.resp = null; } } pub fn endWithoutBody(this: *RequestContext, closeConnection: bool) void { if (this.resp) |resp| { - if (this.flags.is_waiting_for_request_body) { - this.flags.is_waiting_for_request_body = false; - resp.clearOnData(); - } + defer this.deref(); + + this.detachResponse(); + this.endRequestStreamingAndDrain(); resp.endWithoutBody(closeConnection); - this.resp = null; } } - pub fn onWritableResponseBuffer(this: *RequestContext, _: u64, resp: *App.Response) callconv(.C) bool { + pub fn onWritableResponseBuffer(this: *RequestContext, _: u64, resp: *App.Response) bool { ctxLog("onWritableResponseBuffer", .{}); assert(this.resp == resp); - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return false; } this.end("", this.shouldCloseConnection()); - this.finalize(); return false; } // TODO: should we cork? - pub fn onWritableCompleteResponseBufferAndMetadata(this: *RequestContext, write_offset: u64, resp: *App.Response) callconv(.C) bool { + pub fn onWritableCompleteResponseBufferAndMetadata(this: *RequestContext, write_offset: u64, resp: *App.Response) bool { ctxLog("onWritableCompleteResponseBufferAndMetadata", .{}); assert(this.resp == resp); - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return false; } @@ -1796,18 +2360,16 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (this.method == .HEAD) { this.end("", this.shouldCloseConnection()); - this.finalize(); return false; } return this.sendWritableBytesForCompleteResponseBuffer(this.response_buf_owned.items, write_offset, resp); } - pub fn onWritableCompleteResponseBuffer(this: *RequestContext, write_offset: u64, resp: *App.Response) callconv(.C) bool { + pub fn onWritableCompleteResponseBuffer(this: *RequestContext, write_offset: u64, resp: *App.Response) bool { ctxLog("onWritableCompleteResponseBuffer", .{}); assert(this.resp == resp); - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return false; } return this.sendWritableBytesForCompleteResponseBuffer(this.response_buf_owned.items, write_offset, resp); @@ -1825,47 +2387,68 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctxLog("create ({*})", .{this}); } - pub fn isDeadRequest(this: *RequestContext) bool { - if (this.pending_promises_for_abort > 0) return false; + pub fn onTimeout(this: *RequestContext, resp: *App.Response) void { + assert(this.resp == resp); + assert(this.server != null); - if (this.promise != null) { - return false; + var any_js_calls = false; + var vm = this.server.?.vm; + const globalThis = this.server.?.globalThis; + defer { + // This is a task in the event loop. + // If we called into JavaScript, we must drain the microtask queue + if (any_js_calls) { + vm.drainMicrotasks(); + } } - if (this.request_body) |body| { - if (body.value == .Locked) { - return false; + if (this.request_weakref.get()) |request| { + if (request.internal_event_callback.trigger(Request.InternalJSEventCallback.EventType.timeout, globalThis)) { + any_js_calls = true; } } - - return true; } pub fn onAbort(this: *RequestContext, resp: *App.Response) void { assert(this.resp == resp); assert(!this.flags.aborted); + assert(this.server != null); // mark request as aborted this.flags.aborted = true; + + this.detachResponse(); var any_js_calls = false; - var vm = this.server.vm; + var vm = this.server.?.vm; + const globalThis = this.server.?.globalThis; defer { // This is a task in the event loop. // If we called into JavaScript, we must drain the microtask queue if (any_js_calls) { vm.drainMicrotasks(); } + this.deref(); } + if (this.request_weakref.get()) |request| { + request.request_context = AnyRequestContext.Null; + if (request.internal_event_callback.trigger(Request.InternalJSEventCallback.EventType.abort, globalThis)) { + any_js_calls = true; + } + // we can already clean this strong refs + request.internal_event_callback.deinit(); + this.request_weakref.deinit(); + } // if signal is not aborted, abort the signal if (this.signal) |signal| { this.signal = null; + defer { + signal.pendingActivityUnref(); + signal.unref(); + } if (!signal.aborted()) { - const reason = JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.server.globalThis); - reason.ensureStillAlive(); - _ = signal.signal(reason); + signal.signal(globalThis, .ConnectionClosed); any_js_calls = true; } - _ = signal.unref(); } //if have sink, call onAborted on sink @@ -1877,60 +2460,32 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // if we can, free the request now. if (this.isDeadRequest()) { this.finalizeWithoutDeinit(); - this.markComplete(); - this.deinit(); } else { - this.pending_promises_for_abort = 0; - - // if we cannot, we have to reject pending promises - // first, we reject the request body promise - if (this.request_body) |body| { - // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object - // but we received nothing or the connection was aborted - - if (body.value == .Locked) { - // the promise is pending - if (body.value.Locked.action != .none or body.value.Locked.promise != null) { - this.pending_promises_for_abort += 1; - } else if (body.value.Locked.readable.get()) |readable| { - readable.abort(this.server.globalThis); - body.value.Locked.readable.deinit(); - any_js_calls = true; - } - body.value.toErrorInstance(JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis), this.server.globalThis); - } + if (this.endRequestStreaming()) { + any_js_calls = true; } if (this.response_ptr) |response| { if (response.body.value == .Locked) { - if (response.body.value.Locked.readable.get()) |readable| { - defer response.body.value.Locked.readable.deinit(); - readable.abort(this.server.globalThis); + var strong_readable = response.body.value.Locked.readable; + response.body.value.Locked.readable = .{}; + defer strong_readable.deinit(); + if (strong_readable.get()) |readable| { + readable.abort(globalThis); any_js_calls = true; } } } - - // then, we reject the response promise - if (this.promise) |promise| { - this.pending_promises_for_abort += 1; - this.promise = null; - promise.asAnyPromise().?.reject(this.server.globalThis, JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis)); - any_js_calls = true; - } } } - pub fn markComplete(this: *RequestContext) void { - if (!this.flags.has_marked_complete) this.server.onRequestComplete(); - this.flags.has_marked_complete = true; - } - // This function may be called multiple times // so it's important that we can safely do that pub fn finalizeWithoutDeinit(this: *RequestContext) void { ctxLog("finalizeWithoutDeinit ({*})", .{this}); this.blob.detach(); + assert(this.server != null); + const globalThis = this.server.?.globalThis; if (comptime Environment.allow_assert) { ctxLog("finalizeWithoutDeinit: has_finalized {any}", .{this.flags.has_finalized}); @@ -1946,41 +2501,36 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.response_jsvalue = JSC.JSValue.zero; } + this.request_body_readable_stream_ref.deinit(); + + if (this.request_weakref.get()) |request| { + request.request_context = AnyRequestContext.Null; + // we can already clean this strong refs + request.internal_event_callback.deinit(); + this.request_weakref.deinit(); + } + // if signal is not aborted, abort the signal if (this.signal) |signal| { this.signal = null; - if (this.flags.aborted and !signal.aborted()) { - const reason = JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.server.globalThis); - reason.ensureStillAlive(); - _ = signal.signal(reason); + defer { + signal.pendingActivityUnref(); + signal.unref(); } - _ = signal.unref(); - } - - if (this.request_body) |body| { - ctxLog("finalizeWithoutDeinit: request_body != null", .{}); - // Case 1: - // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object - // but we received nothing or the connection was aborted - // the promise is pending - // Case 2: - // User ignored the body and the connection was aborted or ended - // Case 3: - // Stream was not consumed and the connection was aborted or ended - if (body.value == .Locked) { - body.value.toErrorInstance(JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis), this.server.globalThis); + if (this.flags.aborted and !signal.aborted()) { + signal.signal(globalThis, .ConnectionClosed); } } - if (this.promise) |promise| { - ctxLog("finalizeWithoutDeinit: this.promise != null", .{}); - this.promise = null; - - if (promise.asAnyPromise()) |prom| { - prom.rejectAsHandled(this.server.globalThis, (JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis))); - } - JSC.C.JSValueUnprotect(this.server.globalThis, promise.asObjectRef()); - } + // Case 1: + // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object + // but we received nothing or the connection was aborted + // the promise is pending + // Case 2: + // User ignored the body and the connection was aborted or ended + // Case 3: + // Stream was not consumed and the connection was aborted or ended + _ = this.endRequestStreaming(); if (this.byte_stream) |stream| { ctxLog("finalizeWithoutDeinit: stream != null", .{}); @@ -1995,89 +2545,25 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.pathname.deref(); this.pathname = bun.String.empty; } - - // if we are waiting for the body yet and the request was not aborted we can safely clear the onData callback - if (this.resp) |resp| { - if (this.flags.is_waiting_for_request_body and this.flags.aborted == false) { - resp.clearOnData(); - this.flags.is_waiting_for_request_body = false; - } - } } - pub fn finalize(this: *RequestContext) void { - ctxLog("finalize ({*})", .{this}); - this.finalizeWithoutDeinit(); - this.markComplete(); - this.deinit(); - } - - pub fn deinit(this: *RequestContext) void { - if (!this.isDeadRequest()) { - ctxLog("deinit ({*}) waiting request", .{this}); - return; - } - if (this.defer_deinit_until_callback_completes) |defer_deinit| { - defer_deinit.* = true; - ctxLog("deferred deinit ({*})", .{this}); - return; - } - - ctxLog("deinit ({*})", .{this}); - if (comptime Environment.allow_assert) - assert(this.flags.has_finalized); - - if (comptime Environment.allow_assert) - assert(this.flags.has_marked_complete); - var server = this.server; - this.request_body_buf.clearAndFree(this.allocator); - this.response_buf_owned.clearAndFree(this.allocator); - - if (this.request_body) |body| { - _ = body.unref(); - this.request_body = null; - } - - server.request_pool_allocator.put(this); - } - - fn writeHeaders( - this: *RequestContext, - headers: *JSC.FetchHeaders, - ) void { - ctxLog("writeHeaders", .{}); - headers.fastRemove(.ContentLength); - headers.fastRemove(.TransferEncoding); - if (!ssl_enabled) headers.fastRemove(.StrictTransportSecurity); + pub fn endSendFile(this: *RequestContext, writeOffSet: usize, closeConnection: bool) void { if (this.resp) |resp| { - headers.toUWSResponse(ssl_enabled, resp); - } - } + defer this.deref(); - pub fn writeStatus(this: *RequestContext, status: u16) void { - var status_text_buf: [48]u8 = undefined; - assert(!this.flags.has_written_status); - this.flags.has_written_status = true; - - if (this.resp) |resp| { - if (HTTPStatusText.get(status)) |text| { - resp.writeStatus(text); - } else { - resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{status}) catch unreachable); - } + this.detachResponse(); + this.endRequestStreamingAndDrain(); + resp.endSendFile(writeOffSet, closeConnection); } } fn cleanupAndFinalizeAfterSendfile(this: *RequestContext) void { - if (this.resp) |resp| { - resp.overrideWriteOffset(this.sendfile.offset); - this.endWithoutBody(this.shouldCloseConnection()); - } + const sendfile = this.sendfile; + this.endSendFile(sendfile.offset, this.shouldCloseConnection()); + // use node syscall so that we don't segfault on BADF - if (this.sendfile.auto_close) - _ = bun.sys.close(this.sendfile.fd); - this.sendfile = undefined; - this.finalize(); + if (sendfile.auto_close) + _ = bun.sys.close(sendfile.fd); } const separator: string = "\r\n"; const separator_iovec = [1]std.posix.iovec_const{.{ @@ -2086,7 +2572,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp }}; pub fn onSendfile(this: *RequestContext) bool { - if (this.flags.aborted or this.resp == null) { + if (this.isAbortedOrEnded()) { this.cleanupAndFinalizeAfterSendfile(); return false; } @@ -2106,7 +2592,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.sendfile.remain -|= @as(Blob.SizeType, @intCast(this.sendfile.offset -| start)); - if (errcode != .SUCCESS or this.flags.aborted or this.sendfile.remain == 0 or val == 0) { + if (errcode != .SUCCESS or this.isAbortedOrEnded() or this.sendfile.remain == 0 or val == 0) { if (errcode != .AGAIN and errcode != .SUCCESS and errcode != .PIPE and errcode != .NOTCONN) { Output.prettyErrorln("Error: {s}", .{@tagName(errcode)}); Output.flush(); @@ -2128,7 +2614,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp const wrote = @as(Blob.SizeType, @intCast(sbytes)); this.sendfile.offset +|= wrote; this.sendfile.remain -|= wrote; - if (errcode != .AGAIN or this.flags.aborted or this.sendfile.remain == 0 or sbytes == 0) { + if (errcode != .AGAIN or this.isAbortedOrEnded() or this.sendfile.remain == 0 or sbytes == 0) { if (errcode != .AGAIN and errcode != .SUCCESS and errcode != .PIPE and errcode != .NOTCONN) { Output.prettyErrorln("Error: {s}", .{@tagName(errcode)}); Output.flush(); @@ -2144,17 +2630,15 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp resp.onWritable(*RequestContext, onWritableSendfile, this); } - this.setAbortHandler(); resp.markNeedsMore(); return true; } - pub fn onWritableBytes(this: *RequestContext, write_offset: u64, resp: *App.Response) callconv(.C) bool { + pub fn onWritableBytes(this: *RequestContext, write_offset: u64, resp: *App.Response) bool { ctxLog("onWritableBytes", .{}); assert(this.resp == resp); - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return false; } @@ -2172,7 +2656,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp const bytes = bytes_[@min(bytes_.len, @as(usize, @truncate(write_offset)))..]; if (resp.tryEnd(bytes, bytes_.len, this.shouldCloseConnection())) { - this.finalize(); + this.detachResponse(); + this.endRequestStreamingAndDrain(); + this.deref(); return true; } else { this.flags.has_marked_pending = true; @@ -2188,7 +2674,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp const bytes = bytes_[@min(bytes_.len, @as(usize, @truncate(write_offset)))..]; if (resp.tryEnd(bytes, bytes_.len, this.shouldCloseConnection())) { this.response_buf_owned.items.len = 0; - this.finalize(); + this.detachResponse(); + this.endRequestStreamingAndDrain(); + this.deref(); } else { this.flags.has_marked_pending = true; resp.onWritable(*RequestContext, onWritableCompleteResponseBuffer, this); @@ -2197,7 +2685,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return true; } - pub fn onWritableSendfile(this: *RequestContext, _: u64, _: *App.Response) callconv(.C) bool { + pub fn onWritableSendfile(this: *RequestContext, _: u64, _: *App.Response) bool { ctxLog("onWritableSendfile", .{}); return this.onSendfile(); } @@ -2205,7 +2693,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // We tried open() in another thread for this // it was not faster due to the mountain of syscalls pub fn renderSendFile(this: *RequestContext, blob: JSC.WebCore.Blob) void { - if (this.resp == null) return; + if (this.resp == null or this.server == null) return; + const globalThis = this.server.?.globalThis; const resp = this.resp.?; this.blob = .{ .Blob = blob }; @@ -2217,7 +2706,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp else switch (bun.sys.open(file.pathlike.path.sliceZ(&file_buf), bun.O.RDONLY | bun.O.NONBLOCK | bun.O.CLOEXEC, 0)) { .result => |_fd| _fd, .err => |err| return this.runErrorHandler(err.withPath(file.pathlike.path.slice()).toSystemError().toErrorInstance( - this.server.globalThis, + globalThis, )), }; @@ -2226,7 +2715,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp .result => |result| result, .err => |err| { this.runErrorHandler(err.withPathLike(file.pathlike).toSystemError().toErrorInstance( - this.server.globalThis, + globalThis, )); if (auto_close) { _ = bun.sys.close(fd); @@ -2248,7 +2737,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp var sys = err.withPathLike(file.pathlike).toSystemError(); sys.message = bun.String.static("MacOS does not support sending non-regular files"); this.runErrorHandler(sys.toErrorInstance( - this.server.globalThis, + globalThis, )); return; } @@ -2267,7 +2756,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp var sys = err.withPathLike(file.pathlike).toSystemError(); sys.message = bun.String.static("File must be regular or FIFO"); this.runErrorHandler(sys.toErrorInstance( - this.server.globalThis, + globalThis, )); return; } @@ -2287,7 +2776,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp .remain = this.blob.Blob.offset + original_size, .offset = this.blob.Blob.offset, .auto_close = auto_close, - .socket_fd = if (!this.flags.aborted) resp.getNativeHandle() else bun.invalid_fd, + .socket_fd = if (!this.isAbortedOrEnded()) resp.getNativeHandle() else bun.invalid_fd, }; // if we are sending only part of a file, include the content-range header @@ -2321,8 +2810,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn doSendfile(this: *RequestContext, blob: Blob) void { - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return; } @@ -2333,19 +2821,23 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (comptime can_sendfile) { return this.renderSendFile(blob); } - - this.setAbortHandler(); - this.blob.Blob.doReadFileInternal(*RequestContext, this, onReadFile, this.server.globalThis); + if (this.server) |server| { + this.ref(); + this.blob.Blob.doReadFileInternal(*RequestContext, this, onReadFile, server.globalThis); + } } pub fn onReadFile(this: *RequestContext, result: Blob.ReadFile.ResultType) void { - if (this.flags.aborted or this.resp == null) { - this.finalizeForAbort(); + defer this.deref(); + + if (this.isAbortedOrEnded()) { return; } if (result == .err) { - this.runErrorHandler(result.err.toErrorInstance(this.server.globalThis)); + if (this.server) |server| { + this.runErrorHandler(result.err.toErrorInstance(server.globalThis)); + } return; } @@ -2392,13 +2884,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } fn renderWithBlobFromBodyValue(this: *RequestContext) void { - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return; } if (this.blob.needsToReadFile()) { - this.req.setYield(false); if (!this.flags.has_sendfile_ctx) this.doSendfile(this.blob.Blob); return; @@ -2419,10 +2909,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctxLog("doRenderStream", .{}); var this = pair.this; var stream = pair.stream; - if (this.resp == null or this.flags.aborted) { - stream.cancel(this.server.globalThis); + assert(this.server != null); + const globalThis = this.server.?.globalThis; + + if (this.isAbortedOrEnded()) { + stream.cancel(globalThis); this.readable_stream_ref.deinit(); - this.finalizeForAbort(); return; } const resp = this.resp.?; @@ -2430,7 +2922,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp stream.value.ensureStillAlive(); var response_stream = this.allocator.create(ResponseStream.JSSink) catch unreachable; - var globalThis = this.server.globalThis; response_stream.* = ResponseStream.JSSink{ .sink = .{ .res = resp, @@ -2474,7 +2965,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (assignment_result.toError()) |err_value| { streamLog("returned an error", .{}); - if (!this.flags.aborted) resp.clearAborted(); response_stream.detach(); this.sink = null; response_stream.sink.destroy(); @@ -2482,15 +2972,13 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } if (resp.hasResponded()) { - if (!this.flags.aborted) resp.clearAborted(); streamLog("done", .{}); response_stream.detach(); this.sink = null; response_stream.sink.destroy(); - stream.done(this.server.globalThis); + stream.done(globalThis); this.readable_stream_ref.deinit(); this.endStream(this.shouldCloseConnection()); - this.finalize(); return; } @@ -2502,7 +2990,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.drainMicrotasks(); switch (promise.status(globalThis.vm())) { - .Pending => { + .pending => { streamLog("promise still Pending", .{}); if (!this.flags.has_written_status) { response_stream.sink.onFirstWrite = null; @@ -2511,14 +2999,13 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } // TODO: should this timeout? - this.setAbortHandler(); - this.pending_promises_for_abort += 1; this.response_ptr.?.body.value = .{ .Locked = .{ .readable = JSC.WebCore.ReadableStream.Strong.init(stream, globalThis), .global = globalThis, }, }; + this.ref(); assignment_result.then( globalThis, this, @@ -2528,20 +3015,24 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // the response_stream should be GC'd }, - .Fulfilled => { + .fulfilled => { streamLog("promise Fulfilled", .{}); + var readable_stream_ref = this.readable_stream_ref; + this.readable_stream_ref = .{}; defer { stream.done(globalThis); - this.readable_stream_ref.deinit(); + readable_stream_ref.deinit(); } this.handleResolveStream(); }, - .Rejected => { + .rejected => { streamLog("promise Rejected", .{}); + var readable_stream_ref = this.readable_stream_ref; + this.readable_stream_ref = .{}; defer { stream.cancel(globalThis); - this.readable_stream_ref.deinit(); + readable_stream_ref.deinit(); } this.handleRejectStream(globalThis, promise.result(globalThis.vm())); }, @@ -2550,7 +3041,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } else { // if is not a promise we treat it as Error streamLog("returned an error", .{}); - if (!this.flags.aborted) resp.clearAborted(); response_stream.detach(); this.sink = null; response_stream.sink.destroy(); @@ -2558,20 +3048,20 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } } - if (this.flags.aborted) { + if (this.isAbortedOrEnded()) { response_stream.detach(); stream.cancel(globalThis); defer this.readable_stream_ref.deinit(); response_stream.sink.markDone(); - this.finalizeForAbort(); response_stream.sink.onFirstWrite = null; response_stream.sink.finalize(); return; } - - defer this.readable_stream_ref.deinit(); + var readable_stream_ref = this.readable_stream_ref; + this.readable_stream_ref = .{}; + defer readable_stream_ref.deinit(); const is_in_progress = response_stream.sink.has_backpressure or !(response_stream.sink.wrote == 0 and response_stream.sink.buffer.len == 0); @@ -2586,7 +3076,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } } - this.setAbortHandler(); streamLog("is in progress, but did not return a Promise. Finalizing request context", .{}); response_stream.sink.onFirstWrite = null; response_stream.sink.ctx = null; @@ -2604,6 +3093,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp fn toAsyncWithoutAbortHandler(ctx: *RequestContext, req: *uws.Request, request_object: *Request) void { request_object.request_context.setRequest(req); + assert(ctx.server != null); request_object.ensureURL() catch { request_object.url = bun.String.empty; @@ -2611,12 +3101,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // we have to clone the request headers here since they will soon belong to a different request if (!request_object.hasFetchHeaders()) { - request_object.setFetchHeaders(JSC.FetchHeaders.createFromUWS(ctx.server.globalThis, req)); + request_object.setFetchHeaders(JSC.FetchHeaders.createFromUWS(req)); } // This object dies after the stack frame is popped // so we have to clear it in here too - request_object.request_context = JSC.API.AnyRequestContext.Null; + request_object.request_context.detachRequest(); } fn toAsync( @@ -2632,6 +3122,51 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctx.setAbortHandler(); } + fn endRequestStreamingAndDrain(this: *RequestContext) void { + assert(this.server != null); + + if (this.endRequestStreaming()) { + this.server.?.vm.drainMicrotasks(); + } + } + fn endRequestStreaming(this: *RequestContext) bool { + assert(this.server != null); + // if we cannot, we have to reject pending promises + // first, we reject the request body promise + if (this.request_body) |body| { + // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object + // but we received nothing or the connection was aborted + if (body.value == .Locked) { + body.value.toErrorInstance(.{ .AbortReason = .ConnectionClosed }, this.server.?.globalThis); + return true; + } + } + return false; + } + fn detachResponse(this: *RequestContext) void { + if (this.resp) |resp| { + this.resp = null; + + if (this.flags.is_waiting_for_request_body) { + this.flags.is_waiting_for_request_body = false; + resp.clearOnData(); + } + if (this.flags.has_abort_handler) { + resp.clearAborted(); + this.flags.has_abort_handler = false; + } + if (this.flags.has_timeout_handler) { + resp.clearTimeout(); + this.flags.has_timeout_handler = false; + } + } + } + + fn isAbortedOrEnded(this: *const RequestContext) bool { + // resp == null or aborted or server.stop(true) + return this.resp == null or this.flags.aborted or this.server == null or this.server.?.flags.terminated; + } + // Each HTTP request or TCP socket connection is effectively a "task". // // However, unlike the regular task queue, we don't drain the microtask @@ -2656,17 +3191,14 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp response_value.ensureStillAlive(); ctx.drainMicrotasks(); - if (ctx.flags.aborted) { - ctx.finalizeForAbort(); + if (ctx.isAbortedOrEnded()) { return; } - // if you return a Response object or a Promise // but you upgraded the connection to a WebSocket // just ignore the Response object. It doesn't do anything. // it's better to do that than to throw an error if (ctx.didUpgradeWebSocket()) { - ctx.finalize(); return; } @@ -2704,22 +3236,22 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return; } - var wait_for_promise = false; var vm = this.vm; if (response_value.asAnyPromise()) |promise| { // If we immediately have the value available, we can skip the extra event loop tick - switch (promise.status(vm.global.vm())) { - .Pending => {}, - .Fulfilled => { - const fulfilled_value = promise.result(vm.global.vm()); - + switch (promise.unwrap(vm.global.vm(), .mark_handled)) { + .pending => { + ctx.ref(); + response_value.then(this.globalThis, ctx, RequestContext.onResolve, RequestContext.onReject); + return; + }, + .fulfilled => |fulfilled_value| { // if you return a Response object or a Promise // but you upgraded the connection to a WebSocket // just ignore the Response object. It doesn't do anything. // it's better to do that than to throw an error if (ctx.didUpgradeWebSocket()) { - ctx.finalize(); return; } @@ -2754,19 +3286,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctx.render(response); return; }, - .Rejected => { - promise.setHandled(vm.global.vm()); - ctx.handleReject(promise.result(vm.global.vm())); + .rejected => |err| { + ctx.handleReject(err); return; }, } - wait_for_promise = true; - } - - if (wait_for_promise) { - ctx.pending_promises_for_abort += 1; - response_value.then(this.globalThis, ctx, RequestContext.onResolve, RequestContext.onReject); - return; } } @@ -2787,50 +3311,43 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } if (req.response_ptr) |resp| { + assert(req.server != null); + if (resp.body.value == .Locked) { if (resp.body.value.Locked.readable.get()) |stream| { - stream.done(req.server.globalThis); + stream.done(req.server.?.globalThis); } resp.body.value.Locked.readable.deinit(); resp.body.value = .{ .Used = {} }; } } - streamLog("onResolve({any})", .{wrote_anything}); - //aborted so call finalizeForAbort - if (req.flags.aborted or req.resp == null) { - req.finalizeForAbort(); + if (req.isAbortedOrEnded()) { return; } - const resp = req.resp.?; - const responded = resp.hasResponded(); - - if (!responded) { - resp.clearAborted(); - if (!req.flags.has_written_status) { - req.renderMetadata(); - } - req.endStream(req.shouldCloseConnection()); + streamLog("onResolve({any})", .{wrote_anything}); + if (!req.flags.has_written_status) { + req.renderMetadata(); } - - req.finalize(); + req.endStream(req.shouldCloseConnection()); } - pub fn onResolveStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onResolveStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { streamLog("onResolveStream", .{}); var args = callframe.arguments(2); var req: *@This() = args.ptr[args.len - 1].asPromisePtr(@This()); - req.pending_promises_for_abort -|= 1; + defer req.deref(); req.handleResolveStream(); return JSValue.jsUndefined(); } - pub fn onRejectStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onRejectStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { streamLog("onRejectStream", .{}); const args = callframe.arguments(2); var req = args.ptr[args.len - 1].asPromisePtr(@This()); - req.pending_promises_for_abort -|= 1; const err = args.ptr[0]; + defer req.deref(); + req.handleRejectStream(globalThis, err); return JSValue.jsUndefined(); } @@ -2859,8 +3376,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } // aborted so call finalizeForAbort - if (req.flags.aborted) { - req.finalizeForAbort(); + if (req.isAbortedOrEnded()) { return; } @@ -2870,15 +3386,16 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp req.renderMetadata(); } - req.endStream(true); if (comptime debug_mode) { - if (!err.isEmptyOrUndefinedOrNull()) { - var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(req.allocator); - defer exception_list.deinit(); - req.server.vm.runErrorHandler(err, &exception_list); + if (req.server) |server| { + if (!err.isEmptyOrUndefinedOrNull()) { + var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(req.allocator); + defer exception_list.deinit(); + server.vm.runErrorHandler(err, &exception_list); + } } } - req.finalize(); + req.endStream(true); } pub fn doRenderWithBody(this: *RequestContext, value: *JSC.WebCore.Body.Value) void { @@ -2887,16 +3404,14 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // If a ReadableStream can trivially be converted to a Blob, do so. // If it's a WTFStringImpl and it cannot be used as a UTF-8 string, convert it to a Blob. value.toBlobIfPossible(); - + const globalThis = this.server.?.globalThis; switch (value.*) { - .Error => { - const err = value.Error; + .Error => |*err_ref| { _ = value.use(); - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return; } - this.runErrorHandler(err); + this.runErrorHandler(err_ref.toJS(globalThis)); return; }, // .InlineBlob, @@ -2910,8 +3425,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return; }, .Locked => |*lock| { - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return; } @@ -2921,14 +3435,14 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.readable_stream_ref = lock.readable; value.* = .{ .Used = {} }; - if (stream.isLocked(this.server.globalThis)) { + if (stream.isLocked(globalThis)) { streamLog("was locked but it shouldn't be", .{}); var err = JSC.SystemError{ - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE))), + .code = bun.String.static(@tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE)), .message = bun.String.static("Stream already used, please create a new one"), }; stream.value.unprotect(); - this.runErrorHandler(err.toErrorInstance(this.server.globalThis)); + this.runErrorHandler(err.toErrorInstance(globalThis)); return; } @@ -2955,7 +3469,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp assert(this.byte_stream == null); if (this.resp == null) { // we don't have a response, so we can discard the stream - stream.done(this.server.globalThis); + stream.done(globalThis); this.readable_stream_ref.deinit(); return; } @@ -2970,7 +3484,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } byte_stream.pipe = JSC.WebCore.Pipe.New(@This(), onPipe).init(this); - this.readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(stream, this.server.globalThis); + this.readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(stream, globalThis); this.byte_stream = byte_stream; this.response_buf_owned = byte_stream.buffer.moveToUnmanaged(); @@ -2986,7 +3500,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // if we only have metadata to send, send it now resp.runCorkedWithType(*RequestContext, renderMetadata, this); } - this.setAbortHandler(); return; }, } @@ -2994,7 +3507,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (lock.onReceiveValue != null or lock.task != null) { // someone else is waiting for the stream or waiting for `onStartStreaming` - const readable = value.toReadableStream(this.server.globalThis); + const readable = value.toReadableStream(globalThis); readable.ensureStillAlive(); this.doRenderWithBody(value); return; @@ -3025,8 +3538,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } } - if (this.flags.aborted or this.resp == null) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return; } const resp = this.resp.?; @@ -3039,7 +3551,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (resp.write(chunk)) { if (stream.isDone()) { this.endStream(this.shouldCloseConnection()); - this.finalize(); } } else { // when it's the last one, we just want to know if it's done @@ -3073,8 +3584,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn doRender(this: *RequestContext) void { ctxLog("doRender", .{}); - if (this.flags.aborted) { - this.finalizeForAbort(); + if (this.isAbortedOrEnded()) { return; } var response = this.response_ptr.?; @@ -3102,7 +3612,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp }, } } - this.finalize(); } pub fn runErrorHandler( @@ -3124,8 +3633,10 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } if (!this.flags.has_abort_handler) { - try writer.writeAll(this.req.url()); - return; + if (this.req) |req| { + try writer.writeAll(req.url()); + return; + } } try writer.writeAll("/"); @@ -3138,19 +3649,21 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub inline fn shouldCloseConnection(this: *const RequestContext) bool { if (this.resp) |resp| { - return resp.state().isHttpConnectionClose(); + return resp.shouldCloseConnection(); } return false; } fn finishRunningErrorHandler(this: *RequestContext, value: JSC.JSValue, status: u16) void { - var vm: *JSC.VirtualMachine = this.server.vm; + if (this.server == null) return this.renderProductionError(status); + var vm: *JSC.VirtualMachine = this.server.?.vm; + const globalThis = this.server.?.globalThis; if (comptime debug_mode) { var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(this.allocator); defer exception_list.deinit(); const prev_exception_list = vm.onUnhandledRejectionExceptionList; vm.onUnhandledRejectionExceptionList = &exception_list; - vm.onUnhandledRejection(vm, this.server.globalThis, value); + vm.onUnhandledRejection(vm, globalThis, value); vm.onUnhandledRejectionExceptionList = prev_exception_list; this.renderDefaultError( @@ -3162,7 +3675,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ); } else { if (status != 404) { - vm.onUnhandledRejection(vm, this.server.globalThis, value); + vm.onUnhandledRejection(vm, globalThis, value); } this.renderProductionError(status); } @@ -3176,24 +3689,26 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp status: u16, ) void { JSC.markBinding(@src()); - if (!this.server.config.onError.isEmpty() and !this.flags.has_called_error_handler) { - this.flags.has_called_error_handler = true; - const result = this.server.config.onError.callWithThis( - this.server.globalThis, - this.server.thisObject, - &.{value}, - ); - defer result.ensureStillAlive(); - if (!result.isEmptyOrUndefinedOrNull()) { - if (result.toError()) |err| { - this.finishRunningErrorHandler(err, status); - return; - } else if (result.asAnyPromise()) |promise| { - this.processOnErrorPromise(result, promise, value, status); - return; - } else if (result.as(Response)) |response| { - this.render(response); - return; + if (this.server) |server| { + if (!server.config.onError.isEmpty() and !this.flags.has_called_error_handler) { + this.flags.has_called_error_handler = true; + const result = server.config.onError.call( + server.globalThis, + server.thisObject, + &.{value}, + ) catch |err| server.globalThis.takeException(err); + defer result.ensureStillAlive(); + if (!result.isEmptyOrUndefinedOrNull()) { + if (result.toError()) |err| { + this.finishRunningErrorHandler(err, status); + return; + } else if (result.asAnyPromise()) |promise| { + this.processOnErrorPromise(result, promise, value, status); + return; + } else if (result.as(Response)) |response| { + this.render(response); + return; + } } } } @@ -3208,13 +3723,21 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp value: JSC.JSValue, status: u16, ) void { - var vm = ctx.server.vm; - - switch (promise.status(vm.global.vm())) { - .Pending => {}, - .Fulfilled => { - const fulfilled_value = promise.result(vm.global.vm()); - + assert(ctx.server != null); + var vm = ctx.server.?.vm; + + switch (promise.unwrap(vm.global.vm(), .mark_handled)) { + .pending => { + ctx.flags.is_error_promise_pending = true; + ctx.ref(); + promise_js.then( + ctx.server.?.globalThis, + ctx, + RequestContext.onResolve, + RequestContext.onReject, + ); + }, + .fulfilled => |fulfilled_value| { // if you return a Response object or a Promise // but you upgraded the connection to a WebSocket // just ignore the Response object. It doesn't do anything. @@ -3250,24 +3773,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctx.render(response); return; }, - .Rejected => { - promise.setHandled(vm.global.vm()); - ctx.finishRunningErrorHandler(promise.result(vm.global.vm()), status); + .rejected => |err| { + ctx.finishRunningErrorHandler(err, status); return; }, } - - // Promise is not fulfilled yet - { - ctx.flags.is_error_promise_pending = true; - ctx.pending_promises_for_abort += 1; - promise_js.then( - ctx.server.globalThis, - ctx, - RequestContext.onResolve, - RequestContext.onReject, - ); - } } pub fn runErrorHandlerWithStatusCode( @@ -3299,33 +3809,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp else status; - var needs_content_type = true; - var content_type_needs_free = false; - - const content_type: MimeType = brk: { - if (response.init.headers) |headers_| { - if (headers_.fastGet(.ContentType)) |content| { - needs_content_type = false; - - var content_slice = content.toSlice(this.allocator); - defer content_slice.deinit(); - - const content_type_allocator = if (content_slice.allocator.isNull()) null else this.allocator; - break :brk MimeType.init(content_slice.slice(), content_type_allocator, &content_type_needs_free); - } - } - - break :brk if (this.blob.contentType().len > 0) - MimeType.byName(this.blob.contentType()) - else if (MimeType.sniff(this.blob.slice())) |content| - content - else if (this.blob.wasString()) - MimeType.text - // TODO: should we get the mime type off of the Blob.Store if it exists? - // A little wary of doing this right now due to causing some breaking change - else - MimeType.other; - }; + const content_type, const needs_content_type, const content_type_needs_free = getContentType( + response.init.headers, + &this.blob, + this.allocator, + ); defer if (content_type_needs_free) content_type.deinit(this.allocator); var has_content_disposition = false; var has_content_range = false; @@ -3337,15 +3825,15 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp status = 206; } - this.writeStatus(status); - this.writeHeaders(headers_); + this.doWriteStatus(status); + this.doWriteHeaders(headers_); response.init.headers = null; headers_.deref(); } else if (needs_content_range) { status = 206; - this.writeStatus(status); + this.doWriteStatus(status); } else { - this.writeStatus(status); + this.doWriteStatus(status); } if (needs_content_type and @@ -3396,6 +3884,17 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } } + fn doWriteStatus(this: *RequestContext, status: u16) void { + assert(!this.flags.has_written_status); + this.flags.has_written_status = true; + + writeStatus(ssl_enabled, this.resp, status); + } + + fn doWriteHeaders(this: *RequestContext, headers: *JSC.FetchHeaders) void { + writeHeaders(headers, ssl_enabled, this.resp); + } + pub fn renderBytes(this: *RequestContext) void { // copy it to stack memory to prevent aliasing issues in release builds const blob = this.blob; @@ -3408,13 +3907,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp )) { this.flags.has_marked_pending = true; resp.onWritable(*RequestContext, onWritableBytes, this); - // given a blob, we might not have set an abort handler yet - this.setAbortHandler(); return; } } - - this.finalize(); + this.detachResponse(); + this.endRequestStreamingAndDrain(); + this.deref(); } pub fn render(this: *RequestContext, response: *JSC.WebCore.Response) void { @@ -3430,51 +3928,55 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp assert(this.resp == resp); this.flags.is_waiting_for_request_body = last == false; - if (this.flags.aborted or this.flags.has_marked_complete) return; + if (this.isAbortedOrEnded() or this.flags.has_marked_complete) return; if (!last and chunk.len == 0) { // Sometimes, we get back an empty chunk // We have to ignore those chunks unless it's the last one return; } + const vm = this.server.?.vm; + const globalThis = this.server.?.globalThis; + + // After the user does request.body, + // if they then do .text(), .arrayBuffer(), etc + // we can no longer hold the strong reference from the body value ref. + if (this.request_body_readable_stream_ref.get()) |readable| { + assert(this.request_body_buf.items.len == 0); + vm.eventLoop().enter(); + defer vm.eventLoop().exit(); + + if (!last) { + readable.ptr.Bytes.onData( + .{ + .temporary = bun.ByteList.initConst(chunk), + }, + bun.default_allocator, + ); + } else { + var strong = this.request_body_readable_stream_ref; + this.request_body_readable_stream_ref = .{}; + defer strong.deinit(); + if (this.request_body) |request_body| { + _ = request_body.unref(); + this.request_body = null; + } + + readable.value.ensureStillAlive(); + readable.ptr.Bytes.onData( + .{ + .temporary_and_done = bun.ByteList.initConst(chunk), + }, + bun.default_allocator, + ); + } + + return; + } // This is the start of a task, so it's a good time to drain if (this.request_body != null) { var body = this.request_body.?; - if (body.value == .Locked) { - if (body.value.Locked.readable.get()) |readable| { - if (readable.ptr == .Bytes) { - assert(this.request_body_buf.items.len == 0); - var vm = this.server.vm; - vm.eventLoop().enter(); - defer vm.eventLoop().exit(); - - if (!last) { - readable.ptr.Bytes.onData( - .{ - .temporary = bun.ByteList.initConst(chunk), - }, - bun.default_allocator, - ); - } else { - var prev = body.value.Locked.readable; - body.value.Locked.readable = .{}; - readable.value.ensureStillAlive(); - defer prev.deinit(); - readable.value.ensureStillAlive(); - readable.ptr.Bytes.onData( - .{ - .temporary_and_done = bun.ByteList.initConst(chunk), - }, - bun.default_allocator, - ); - } - - return; - } - } - } - if (last) { var bytes = &this.request_body_buf; @@ -3493,7 +3995,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // } else { bytes.ensureTotalCapacityPrecise(this.allocator, total) catch |err| { this.request_body_buf.clearAndFree(this.allocator); - body.value.toError(err, this.server.globalThis); + body.value.toError(err, globalThis); break :getter; }; @@ -3511,11 +4013,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.request_body_buf = .{}; if (old == .Locked) { - var loop = this.server.vm.eventLoop(); + var loop = vm.eventLoop(); loop.enter(); defer loop.exit(); - old.resolve(&body.value, this.server.globalThis, null); + old.resolve(&body.value, globalThis, null); } return; } @@ -3529,7 +4031,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn onStartStreamingRequestBody(this: *RequestContext) JSC.WebCore.DrainResult { ctxLog("onStartStreamingRequestBody", .{}); - if (this.flags.aborted) { + if (this.isAbortedOrEnded()) { return JSC.WebCore.DrainResult{ .aborted = {}, }; @@ -3556,25 +4058,31 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } const max_request_body_preallocate_length = 1024 * 256; pub fn onStartBuffering(this: *RequestContext) void { - ctxLog("onStartBuffering", .{}); - // TODO: check if is someone calling onStartBuffering other than onStartBufferingCallback - // if is not, this should be removed and only keep protect + setAbortHandler - if (this.flags.is_transfer_encoding == false and this.request_body_content_len == 0) { - // no content-length or 0 content-length - // no transfer-encoding - if (this.request_body != null) { - var body = this.request_body.?; - var old = body.value; - old.Locked.onReceiveValue = null; - var new_body = .{ .Null = {} }; - old.resolve(&new_body, this.server.globalThis, null); - body.value = new_body; + if (this.server) |server| { + ctxLog("onStartBuffering", .{}); + // TODO: check if is someone calling onStartBuffering other than onStartBufferingCallback + // if is not, this should be removed and only keep protect + setAbortHandler + if (this.flags.is_transfer_encoding == false and this.request_body_content_len == 0) { + // no content-length or 0 content-length + // no transfer-encoding + if (this.request_body != null) { + var body = this.request_body.?; + var old = body.value; + old.Locked.onReceiveValue = null; + var new_body = .{ .Null = {} }; + old.resolve(&new_body, server.globalThis, null); + body.value = new_body; + } } - } else { - this.setAbortHandler(); } } + pub fn onRequestBodyReadableStreamAvailable(ptr: *anyopaque, globalThis: *JSC.JSGlobalObject, readable: JSC.WebCore.ReadableStream) void { + var this = bun.cast(*RequestContext, ptr); + bun.debugAssert(this.request_body_readable_stream_ref.held.ref == null); + this.request_body_readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(readable, globalThis); + } + pub fn onStartBufferingCallback(this: *anyopaque) void { onStartBuffering(bun.cast(*RequestContext, this)); } @@ -3587,6 +4095,27 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return (this.resp orelse return null).getRemoteSocketInfo(); } + pub fn setTimeout(this: *RequestContext, seconds: c_uint) bool { + if (this.resp) |resp| { + resp.timeout(@min(seconds, 255)); + if (seconds > 0) { + + // we only set the timeout callback if we wanna the timeout event to be triggered + // the connection will be closed so the abort handler will be called after the timeout + if (this.request_weakref.get()) |req| { + if (req.internal_event_callback.hasCallback()) { + this.setTimeoutHandler(); + } + } + } else { + // if the timeout is 0, we don't need to trigger the timeout event + resp.clearTimeout(); + } + return true; + } + return false; + } + pub const Export = shim.exportFunctions(.{ .onResolve = onResolve, .onReject = onReject, @@ -3595,20 +4124,18 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp }); comptime { - if (!JSC.is_bindgen) { - @export(onResolve, .{ - .name = Export[0].symbol_name, - }); - @export(onReject, .{ - .name = Export[1].symbol_name, - }); - @export(onResolveStream, .{ - .name = Export[2].symbol_name, - }); - @export(onRejectStream, .{ - .name = Export[3].symbol_name, - }); - } + @export(onResolve, .{ + .name = Export[0].symbol_name, + }); + @export(onReject, .{ + .name = Export[1].symbol_name, + }); + @export(onResolveStream, .{ + .name = Export[2].symbol_name, + }); + @export(onRejectStream, .{ + .name = Export[3].symbol_name, + }); } }; } @@ -3651,10 +4178,8 @@ pub const WebSocketServer = struct { pub fn runErrorCallback(this: *const Handler, vm: *JSC.VirtualMachine, globalObject: *JSC.JSGlobalObject, error_value: JSC.JSValue) void { const onError = this.onError; if (!onError.isEmptyOrUndefinedOrNull()) { - const err_ret = onError.callWithThis(globalObject, .undefined, &.{error_value}); - if (err_ret.toError()) |actual_err| { - _ = vm.uncaughtException(globalObject, actual_err, false); - } + _ = onError.call(globalObject, .undefined, &.{error_value}) catch |err| + this.globalObject.reportActiveExceptionAsUnhandled(err); return; } @@ -3667,7 +4192,7 @@ pub const WebSocketServer = struct { var valid = false; - if (object.getTruthy(globalObject, "message")) |message_| { + if (object.getTruthyComptime(globalObject, "message")) |message_| { if (!message_.isCallable(vm)) { globalObject.throwInvalidArguments("websocket expects a function for the message option", .{}); return null; @@ -3909,7 +4434,7 @@ pub const WebSocketServer = struct { globalObject.throwInvalidArguments("websocket expects idleTimeout to be 960 or less", .{}); return null; } else if (idleTimeout > 0) { - // uws does not allow idleTimeout to be between (0, 8], + // uws does not allow idleTimeout to be between (0, 8), // since its timer is not that accurate, therefore round up. idleTimeout = @max(idleTimeout, 8); } @@ -3975,10 +4500,11 @@ const Corker = struct { pub fn run(this: *Corker) void { const this_value = this.this_value; - this.result = if (this_value == .zero) - this.callback.call(this.globalObject, this.args) - else - this.callback.callWithThis(this.globalObject, this_value, this.args); + this.result = this.callback.call( + this.globalObject, + if (this_value == .zero) .undefined else this_value, + this.args, + ) catch |err| this.globalObject.takeException(err); } }; @@ -4119,7 +4645,7 @@ pub const ServerWebSocket = struct { .text => brk: { var str = ZigString.init(message); str.markUTF8(); - break :brk str.toValueGC(globalObject); + break :brk str.toJS(globalObject); }, .binary => this.binaryToJS(globalObject, message), else => unreachable, @@ -4144,7 +4670,7 @@ pub const ServerWebSocket = struct { if (result.asAnyPromise()) |promise| { switch (promise.status(globalObject.vm())) { - .Rejected => { + .rejected => { _ = promise.result(globalObject.vm()); return; }, @@ -4170,7 +4696,7 @@ pub const ServerWebSocket = struct { const globalObject = handler.globalObject; var corker = Corker{ - .args = &[_]JSC.JSValue{this.this_value}, + .args = &[_]JSC.JSValue{this.getThisValue()}, .globalObject = globalObject, .callback = handler.onDrain, }; @@ -4219,15 +4745,15 @@ pub const ServerWebSocket = struct { loop.enter(); defer loop.exit(); - const result = cb.call( + _ = cb.call( globalThis, - &[_]JSC.JSValue{ this.this_value, this.binaryToJS(globalThis, data) }, - ); - - if (result.toError()) |err| { + .undefined, + &[_]JSC.JSValue{ this.getThisValue(), this.binaryToJS(globalThis, data) }, + ) catch |e| { + const err = globalThis.takeException(e); log("onPing error", .{}); handler.runErrorCallback(vm, globalThis, err); - } + }; } pub fn onPong(this: *ServerWebSocket, _: uws.AnyWebSocket, data: []const u8) void { @@ -4247,15 +4773,15 @@ pub const ServerWebSocket = struct { loop.enter(); defer loop.exit(); - const result = cb.call( + _ = cb.call( globalThis, - &[_]JSC.JSValue{ this.this_value, this.binaryToJS(globalThis, data) }, - ); - - if (result.toError()) |err| { + .undefined, + &[_]JSC.JSValue{ this.getThisValue(), this.binaryToJS(globalThis, data) }, + ) catch |e| { + const err = globalThis.takeException(e); log("onPong error", .{}); handler.runErrorCallback(vm, globalThis, err); - } + }; } pub fn onClose(this: *ServerWebSocket, _: uws.AnyWebSocket, code: i32, message: []const u8) void { @@ -4279,15 +4805,15 @@ pub const ServerWebSocket = struct { loop.enter(); defer loop.exit(); str.markUTF8(); - const result = handler.onClose.call( + _ = handler.onClose.call( globalObject, - &[_]JSC.JSValue{ this.this_value, JSValue.jsNumber(code), str.toValueGC(globalObject) }, - ); - - if (result.toError()) |err| { + .undefined, + &[_]JSC.JSValue{ this.getThisValue(), JSValue.jsNumber(code), str.toJS(globalObject) }, + ) catch |e| { + const err = globalObject.takeException(e); log("onClose error", .{}); handler.runErrorCallback(vm, globalObject, err); - } + }; } this.this_value.unprotect(); @@ -4297,12 +4823,12 @@ pub const ServerWebSocket = struct { return uws.WebSocketBehavior.Wrap(ServerType, @This(), ssl).apply(opts); } - pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*ServerWebSocket { + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*ServerWebSocket { globalObject.throw("Cannot construct ServerWebSocket", .{}); return null; } - pub fn finalize(this: *ServerWebSocket) callconv(.C) void { + pub fn finalize(this: *ServerWebSocket) void { log("finalize", .{}); this.destroy(); } @@ -4311,9 +4837,8 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(4); - if (args.len < 1) { log("publish()", .{}); globalThis.throw("publish requires at least 1 argument", .{}); @@ -4360,7 +4885,7 @@ pub const ServerWebSocket = struct { if (message_value.asArrayBuffer(globalThis)) |array_buffer| { const buffer = array_buffer.slice(); - const result = if (!publish_to_self) + const result = if (!publish_to_self and !this.isClosed()) this.websocket().publish(topic_slice.slice(), buffer, .binary, compress) else uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .binary, compress); @@ -4378,7 +4903,7 @@ pub const ServerWebSocket = struct { const buffer = string_slice.slice(); - const result = if (!publish_to_self) + const result = if (!publish_to_self and !this.isClosed()) this.websocket().publish(topic_slice.slice(), buffer, .text, compress) else uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress); @@ -4397,7 +4922,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(4); if (args.len < 1) { @@ -4444,7 +4969,7 @@ pub const ServerWebSocket = struct { const buffer = string_slice.slice(); - const result = if (!publish_to_self) + const result = if (!publish_to_self and !this.isClosed()) this.websocket().publish(topic_slice.slice(), buffer, .text, compress) else uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress); @@ -4460,7 +4985,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(4); if (args.len < 1) { @@ -4504,13 +5029,14 @@ pub const ServerWebSocket = struct { globalThis.throw("publishBinary requires a non-empty message", .{}); return .zero; } + const array_buffer = message_value.asArrayBuffer(globalThis) orelse { globalThis.throw("publishBinary expects an ArrayBufferView", .{}); return .zero; }; const buffer = array_buffer.slice(); - const result = if (!publish_to_self) + const result = if (!publish_to_self and !this.isClosed()) this.websocket().publish(topic_slice.slice(), buffer, .binary, compress) else uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .binary, compress); @@ -4527,7 +5053,7 @@ pub const ServerWebSocket = struct { globalThis: *JSC.JSGlobalObject, topic_str: *JSC.JSString, array: *JSC.JSUint8Array, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const app = this.handler.app orelse { log("publish() closed", .{}); return JSValue.jsNumber(0); @@ -4550,7 +5076,7 @@ pub const ServerWebSocket = struct { return JSC.JSValue.jsNumber(0); } - const result = if (!publish_to_self) + const result = if (!publish_to_self and !this.isClosed()) this.websocket().publish(topic_slice.slice(), buffer, .binary, compress) else uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .binary, compress); @@ -4567,7 +5093,7 @@ pub const ServerWebSocket = struct { globalThis: *JSC.JSGlobalObject, topic_str: *JSC.JSString, str: *JSC.JSString, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const app = this.handler.app orelse { log("publish() closed", .{}); return JSValue.jsNumber(0); @@ -4592,7 +5118,8 @@ pub const ServerWebSocket = struct { if (buffer.len == 0) { return JSC.JSValue.jsNumber(0); } - const result = if (!publish_to_self) + + const result = if (!publish_to_self and !this.isClosed()) this.websocket().publish(topic_slice.slice(), buffer, .text, compress) else uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress); @@ -4608,17 +5135,21 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + // Since we're passing the `this` value to the cork function, we need to + // make sure the `this` value is up to date. + this_value: JSC.JSValue, + ) JSValue { const args = callframe.arguments(1); + this.this_value = this_value; if (args.len < 1) { - globalThis.throw("cork requires at least 1 argument", .{}); + globalThis.throwNotEnoughArguments("cork", 1, 0); return .zero; } + const callback = args.ptr[0]; if (callback.isEmptyOrUndefinedOrNull() or !callback.isCallable(globalThis.vm())) { - globalThis.throw("cork requires a function", .{}); - return .zero; + return globalThis.throwInvalidArgumentTypeValue("cork", "callback", callback); } if (this.isClosed()) { @@ -4627,16 +5158,13 @@ pub const ServerWebSocket = struct { var corker = Corker{ .globalObject = globalThis, - .this_value = this.this_value, + .this_value = this_value, .callback = callback, }; this.websocket().cork(&corker, Corker.run); const result = corker.result; - if (result.isEmptyOrUndefinedOrNull()) - return JSValue.jsUndefined(); - if (result.isAnyError()) { globalThis.throwValue(result); return JSValue.jsUndefined(); @@ -4649,7 +5177,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(2); if (args.len < 1) { @@ -4723,7 +5251,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(2); if (args.len < 1) { @@ -4777,7 +5305,7 @@ pub const ServerWebSocket = struct { globalThis: *JSC.JSGlobalObject, message_str: *JSC.JSString, compress: bool, - ) callconv(.C) JSValue { + ) JSValue { if (this.isClosed()) { log("sendText() closed", .{}); return JSValue.jsNumber(0); @@ -4807,7 +5335,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(2); if (args.len < 1) { @@ -4857,7 +5385,7 @@ pub const ServerWebSocket = struct { _: *JSC.JSGlobalObject, array_buffer: *JSC.JSUint8Array, compress: bool, - ) callconv(.C) JSValue { + ) JSValue { if (this.isClosed()) { log("sendBinary() closed", .{}); return JSValue.jsNumber(0); @@ -4885,7 +5413,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { return sendPing(this, globalThis, callframe, "ping", .ping); } @@ -4893,7 +5421,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { return sendPing(this, globalThis, callframe, "pong", .pong); } @@ -4973,7 +5501,7 @@ pub const ServerWebSocket = struct { pub fn getData( _: *ServerWebSocket, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getData()", .{}); return JSValue.jsUndefined(); } @@ -4991,7 +5519,7 @@ pub const ServerWebSocket = struct { pub fn getReadyState( this: *ServerWebSocket, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getReadyState()", .{}); if (this.isClosed()) { @@ -5005,9 +5533,12 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + // Since close() can lead to the close() callback being called, let's always ensure the `this` value is up to date. + this_value: JSC.JSValue, + ) JSValue { const args = callframe.arguments(2); log("close()", .{}); + this.this_value = this_value; if (this.isClosed()) { return .undefined; @@ -5049,12 +5580,16 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + // Since terminate() can lead to close() being called, let's always ensure the `this` value is up to date. + this_value: JSC.JSValue, + ) JSValue { _ = globalThis; const args = callframe.arguments(2); _ = args; log("terminate()", .{}); + this.this_value = this_value; + if (this.isClosed()) { return .undefined; } @@ -5069,13 +5604,13 @@ pub const ServerWebSocket = struct { pub fn getBinaryType( this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { log("getBinaryType()", .{}); return switch (this.flags.binary_type) { - .Uint8Array => ZigString.static("uint8array").toValueGC(globalThis), - .Buffer => ZigString.static("nodebuffer").toValueGC(globalThis), - .ArrayBuffer => ZigString.static("arraybuffer").toValueGC(globalThis), + .Uint8Array => ZigString.static("uint8array").toJS(globalThis), + .Buffer => ZigString.static("nodebuffer").toJS(globalThis), + .ArrayBuffer => ZigString.static("arraybuffer").toJS(globalThis), else => @panic("Invalid binary type"), }; } @@ -5105,7 +5640,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { log("getBufferedAmount()", .{}); if (this.isClosed()) { @@ -5118,7 +5653,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(1); if (args.len < 1) { globalThis.throw("subscribe requires at least 1 argument", .{}); @@ -5155,7 +5690,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(1); if (args.len < 1) { globalThis.throw("unsubscribe requires at least 1 argument", .{}); @@ -5192,7 +5727,7 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args = callframe.arguments(1); if (args.len < 1) { globalThis.throw("isSubscribed requires at least 1 argument", .{}); @@ -5229,7 +5764,7 @@ pub const ServerWebSocket = struct { pub fn getRemoteAddress( this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.isClosed()) { return JSValue.jsUndefined(); } @@ -5245,7 +5780,7 @@ pub const ServerWebSocket = struct { }; const text = bun.fmt.formatIp(address, &text_buf) catch unreachable; - return ZigString.init(text).toValueGC(globalThis); + return ZigString.init(text).toJS(globalThis); } }; @@ -5259,8 +5794,6 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub const App = uws.NewApp(ssl_enabled); - const httplog = Output.scoped(.Server, false); - listener: ?*App.ListenSocket = null, thisObject: JSC.JSValue = JSC.JSValue.zero, app: *App = undefined, @@ -5270,6 +5803,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp config: ServerConfig = ServerConfig{}, pending_requests: usize = 0, request_pool_allocator: *RequestContext.RequestContextStackAllocator = undefined, + all_closed_promise: JSC.JSPromise.Strong = .{}, listen_callback: JSC.AnyTask = undefined, allocator: std.mem.Allocator, @@ -5279,10 +5813,11 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp cached_hostname: bun.String = bun.String.empty, cached_protocol: bun.String = bun.String.empty, - flags: packed struct(u3) { + flags: packed struct(u4) { deinit_scheduled: bool = false, terminated: bool = false, has_js_deinited: bool = false, + has_handled_all_closed_promise: bool = false, } = .{}, pub const doStop = JSC.wrapInstanceMethod(ThisServer, "stopFromJS", false); @@ -5292,10 +5827,37 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub const doReload = onReload; pub const doFetch = onFetch; pub const doRequestIP = JSC.wrapInstanceMethod(ThisServer, "requestIP", false); + pub const doTimeout = JSC.wrapInstanceMethod(ThisServer, "timeout", false); + + pub fn doSubscriberCount(this: *ThisServer, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(1); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("subscriberCount", 1, 0); + return .zero; + } + + if (arguments.ptr[0].isEmptyOrUndefinedOrNull()) { + globalThis.throwInvalidArguments("subscriberCount requires a topic name as a string", .{}); + return .zero; + } + + var topic = arguments.ptr[0].toSlice(globalThis, bun.default_allocator); + defer topic.deinit(); + if (globalThis.hasException()) { + return .zero; + } + + if (topic.len == 0) { + return JSValue.jsNumber(0); + } + + return JSValue.jsNumber((this.app.num_subscribers(topic.slice()))); + } pub usingnamespace NamespaceType; + pub usingnamespace bun.New(@This()); - pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*ThisServer { + pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*ThisServer { globalThis.throw("Server() is not a constructor", .{}); return null; } @@ -5309,7 +5871,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return if (request.request_context.getRemoteSocketInfo()) |info| JSSocketAddress__create( this.globalThis, - bun.String.static(info.ip).toJS(this.globalThis), + bun.String.init(info.ip).toJS(this.globalThis), info.port, info.is_ipv6, ) @@ -5317,6 +5879,20 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp JSValue.jsNull(); } + pub fn timeout(this: *ThisServer, request: *JSC.WebCore.Request, seconds: JSValue) JSC.JSValue { + if (!seconds.isNumber()) { + this.globalThis.throw("timeout() requires a number", .{}); + return .zero; + } + const value = seconds.to(c_uint); + _ = request.request_context.setTimeout(value); + return JSValue.jsUndefined(); + } + + pub fn setIdleTimeout(this: *ThisServer, seconds: c_uint) void { + this.config.idleTimeout = @truncate(@min(seconds, 255)); + } + pub fn publish(this: *ThisServer, globalThis: *JSC.JSGlobalObject, topic: ZigString, message_value: JSValue, compress_value: ?JSValue, exception: JSC.C.ExceptionRef) JSValue { if (this.config.websocket == null) return JSValue.jsNumber(0); @@ -5382,18 +5958,16 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return JSValue.jsUndefined(); }; - if (request.upgrader == null) { - return JSC.jsBoolean(false); - } + var upgrader = request.request_context.get(RequestContext) orelse return JSC.jsBoolean(false); - var upgrader = bun.cast(*RequestContext, request.upgrader.?); - if (upgrader.flags.aborted or upgrader.resp == null) { + if (upgrader.isAbortedOrEnded()) { return JSC.jsBoolean(false); } if (upgrader.upgrade_context == null or @intFromPtr(upgrader.upgrade_context) == std.math.maxInt(usize)) { return JSC.jsBoolean(false); } + const resp = upgrader.resp.?; const ctx = upgrader.upgrade_context.?; @@ -5409,22 +5983,23 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp sec_websocket_extensions = head.fastGet(.SecWebSocketExtensions) orelse ZigString.Empty; } - if (sec_websocket_key_str.len == 0) { - sec_websocket_key_str = ZigString.init(upgrader.req.header("sec-websocket-key") orelse ""); + if (upgrader.req) |req| { + if (sec_websocket_key_str.len == 0) { + sec_websocket_key_str = ZigString.init(req.header("sec-websocket-key") orelse ""); + } + if (sec_websocket_protocol.len == 0) { + sec_websocket_protocol = ZigString.init(req.header("sec-websocket-protocol") orelse ""); + } + + if (sec_websocket_extensions.len == 0) { + sec_websocket_extensions = ZigString.init(req.header("sec-websocket-extensions") orelse ""); + } } if (sec_websocket_key_str.len == 0) { return JSC.jsBoolean(false); } - if (sec_websocket_protocol.len == 0) { - sec_websocket_protocol = ZigString.init(upgrader.req.header("sec-websocket-protocol") orelse ""); - } - - if (sec_websocket_extensions.len == 0) { - sec_websocket_extensions = ZigString.init(upgrader.req.header("sec-websocket-extensions") orelse ""); - } - if (sec_websocket_protocol.len > 0) { sec_websocket_protocol.markUTF8(); } @@ -5459,6 +6034,10 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp data_value = headers_value; } + if (globalThis.hasException()) { + return JSValue.jsUndefined(); + } + if (opts.fastGet(globalThis, .headers)) |headers_value| { if (headers_value.isEmptyOrUndefinedOrNull()) { break :getter; @@ -5473,10 +6052,16 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp } break :brk null; } orelse { - JSC.throwInvalidArguments("upgrade options.headers must be a Headers or an object", .{}, globalThis, exception); + if (!globalThis.hasException()) { + JSC.throwInvalidArguments("upgrade options.headers must be a Headers or an object", .{}, globalThis, exception); + } return JSValue.jsUndefined(); }; + if (globalThis.hasException()) { + return JSValue.jsUndefined(); + } + if (fetch_headers_to_use.fastGet(.SecWebSocketProtocol)) |protocol| { sec_websocket_protocol = protocol; } @@ -5490,6 +6075,10 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp resp.writeStatus("101 Switching Protocols"); fetch_headers_to_use.toUWSResponse(comptime ssl_enabled, resp); } + + if (globalThis.hasException()) { + return JSValue.jsUndefined(); + } } } @@ -5498,9 +6087,12 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp // obviously invalid pointer marks it as used upgrader.upgrade_context = @as(*uws.uws_socket_context_s, @ptrFromInt(std.math.maxInt(usize))); - request.upgrader = null; - - resp.clearAborted(); + // set the abort handler so we can receive onAbort to deref the context + upgrader.setAbortHandler(); + // after upgrading we should not use the response anymore + upgrader.resp = null; + request.request_context = AnyRequestContext.Null; + upgrader.request_weakref.deinit(); data_value.ensureStillAlive(); const ws = ServerWebSocket.new(.{ @@ -5529,6 +6121,8 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn onReloadFromZig(this: *ThisServer, new_config: *ServerConfig, globalThis: *JSC.JSGlobalObject) void { httplog("onReload", .{}); + this.app.clearRoutes(); + // only reload those two if (this.config.onRequest != new_config.onRequest) { this.config.onRequest.unprotect(); @@ -5544,25 +6138,27 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp if (ws.handler.onMessage != .zero or ws.handler.onOpen != .zero) { if (this.config.websocket) |old_ws| { old_ws.unprotect(); - } else { - this.app.ws("/*", this, 0, ServerWebSocket.behavior( - ThisServer, - ssl_enabled, - ws.toBehavior(), - )); } ws.globalObject = globalThis; this.config.websocket = ws.*; } // we don't remove it } + + for (this.config.static_routes.items) |*route| { + route.deinit(); + } + this.config.static_routes.deinit(); + this.config.static_routes = new_config.static_routes; + + this.setRoutes(); } pub fn onReload( this: *ThisServer, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments = callframe.arguments(1).slice(); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("reload", 1, 0); @@ -5593,7 +6189,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this: *ThisServer, ctx: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { JSC.markBinding(@src()); const arguments = callframe.arguments(2).slice(); if (arguments.len == 0) { @@ -5661,7 +6257,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp existing_request = Request.init( bun.String.createUTF8(url.href), headers, - JSC.WebCore.InitRequestBodyValue(body) catch bun.outOfMemory(), + this.vm.initRequestBodyValue(body) catch bun.outOfMemory(), method, ); } else if (first_arg.as(Request)) |request_| { @@ -5678,14 +6274,13 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return JSPromise.rejectedPromiseValue(ctx, err); } - var request = bun.default_allocator.create(Request) catch unreachable; - request.* = existing_request; + var request = Request.new(existing_request); - const response_value = this.config.onRequest.callWithThis( + const response_value = this.config.onRequest.call( this.globalThis, this.thisObject, &[_]JSC.JSValue{request.toJS(this.globalThis)}, - ); + ) catch |err| this.globalThis.takeException(err); if (response_value.isAnyError()) { return JSC.JSPromise.rejectedPromiseValue(ctx, response_value); @@ -5716,28 +6311,28 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp break :brk false; }; - JSC.C.JSValueUnprotect(this.globalThis, this.thisObject.asObjectRef()); - this.thisObject = JSC.JSValue.jsUndefined(); + this.thisObject.unprotect(); + this.thisObject = .undefined; this.stop(abrupt); } - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn disposeFromJS(this: *ThisServer) JSC.JSValue { if (this.listener != null) { - JSC.C.JSValueUnprotect(this.globalThis, this.thisObject.asObjectRef()); - this.thisObject = JSC.JSValue.jsUndefined(); + this.thisObject.unprotect(); + this.thisObject = .undefined; this.stop(true); } - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn getPort( this: *ThisServer, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { switch (this.config.address) { .unix => return .undefined, else => {}, @@ -5750,7 +6345,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn getId( this: *ThisServer, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var str = bun.String.createUTF8(this.config.id); defer str.deref(); return str.toJS(globalThis); @@ -5759,18 +6354,18 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn getPendingRequests( this: *ThisServer, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsNumber(@as(i32, @intCast(@as(u31, @truncate(this.pending_requests))))); } pub fn getPendingWebSockets( this: *ThisServer, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsNumber(@as(i32, @intCast(@as(u31, @truncate(this.activeSocketsCount()))))); } - pub fn getAddress(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getAddress(this: *ThisServer, globalThis: *JSGlobalObject) JSC.JSValue { switch (this.config.address) { .unix => |unix| { var value = bun.String.createUTF8(unix); @@ -5802,7 +6397,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp } } - pub fn getURL(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getURL(this: *ThisServer, globalThis: *JSGlobalObject) JSC.JSValue { const fmt = switch (this.config.address) { .unix => |unix| brk: { if (unix.len > 1 and unix[0] == 0) { @@ -5839,7 +6434,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return value.toJSDOMURL(globalThis); } - pub fn getHostname(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getHostname(this: *ThisServer, globalThis: *JSGlobalObject) JSC.JSValue { switch (this.config.address) { .unix => return .undefined, else => {}, @@ -5872,7 +6467,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return this.cached_hostname.toJS(globalThis); } - pub fn getProtocol(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getProtocol(this: *ThisServer, globalThis: *JSGlobalObject) JSC.JSValue { if (this.cached_protocol.isEmpty()) { this.cached_protocol = bun.String.createUTF8(if (ssl_enabled) "https" else "http"); } @@ -5883,10 +6478,15 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn getDevelopment( _: *ThisServer, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(debug_mode); } + pub fn onStaticRequestComplete(this: *ThisServer) void { + this.pending_requests -= 1; + this.deinitIfWeCan(); + } + pub fn onRequestComplete(this: *ThisServer) void { this.vm.eventLoop().processGCTimer(); @@ -5894,7 +6494,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this.deinitIfWeCan(); } - pub fn finalize(this: *ThisServer) callconv(.C) void { + pub fn finalize(this: *ThisServer) void { httplog("finalize", .{}); this.flags.has_js_deinited = true; this.deinitIfWeCan(); @@ -5911,6 +6511,24 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn deinitIfWeCan(this: *ThisServer) void { httplog("deinitIfWeCan", .{}); + + const vm = this.globalThis.bunVM(); + + if (this.pending_requests == 0 and this.listener == null and !this.hasActiveWebSockets() and !this.flags.has_handled_all_closed_promise and this.all_closed_promise.strong.has()) { + const event_loop = vm.eventLoop(); + + // use a flag here instead of `this.all_closed_promise.get().isHandled(vm)` to prevent the race condition of this block being called + // again before the task has run. + this.flags.has_handled_all_closed_promise = true; + + const task = ServerAllConnectionsClosedTask.new(.{ + .globalObject = this.globalThis, + .promise = this.all_closed_promise, + .tracker = JSC.AsyncTaskTracker.init(vm), + }); + this.all_closed_promise = .{}; + event_loop.enqueueTask(JSC.Task.init(task)); + } if (this.pending_requests == 0 and this.listener == null and this.flags.has_js_deinited and !this.hasActiveWebSockets()) { if (this.config.websocket) |*ws| { ws.handler.app = null; @@ -5932,6 +6550,9 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp if (!abrupt) { listener.close(); } else if (!this.flags.terminated) { + if (this.config.websocket) |*ws| { + ws.handler.app = null; + } this.flags.terminated = true; this.app.close(); } @@ -5971,23 +6592,26 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this.config.deinit(); this.app.destroy(); - const allocator = this.allocator; - allocator.destroy(this); + this.destroy(); } pub fn init(config: ServerConfig, globalThis: *JSGlobalObject) *ThisServer { - var server = bun.default_allocator.create(ThisServer) catch bun.outOfMemory(); - server.* = .{ + var server = ThisServer.new(.{ .globalThis = globalThis, .config = config, .base_url_string_for_joining = bun.default_allocator.dupe(u8, strings.trim(config.base_url.href, "/")) catch unreachable, .vm = JSC.VirtualMachine.get(), .allocator = Arena.getThreadlocalDefault(), - }; + }); if (RequestContext.pool == null) { RequestContext.pool = server.allocator.create(RequestContext.RequestContextStackAllocator) catch bun.outOfMemory(); - RequestContext.pool.?.* = RequestContext.RequestContextStackAllocator.init(server.allocator); + RequestContext.pool.?.* = RequestContext.RequestContextStackAllocator.init( + if (comptime bun.heap_breakdown.enabled) + bun.typedAllocator(RequestContext) + else + bun.default_allocator, + ); } server.request_pool_allocator = RequestContext.pool.?; @@ -6137,14 +6761,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this.poll_ref.unref(this.vm); } - pub fn doRef(this: *ThisServer, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doRef(this: *ThisServer, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const this_value = callframe.this(); this.ref(); return this_value; } - pub fn doUnref(this: *ThisServer, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doUnref(this: *ThisServer, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const this_value = callframe.this(); this.unref(); @@ -6168,6 +6792,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp &writer, bun.Global.BunInfo.generate(*Bundler, &JSC.VirtualMachine.get().bundler, allocator) catch unreachable, &source, + .{}, ) catch unreachable; resp.writeStatus("200 OK"); @@ -6209,13 +6834,17 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp } } + pub fn onPendingRequest(this: *ThisServer) void { + this.pending_requests += 1; + } + pub fn onRequest( this: *ThisServer, req: *uws.Request, resp: *App.Response, ) void { JSC.markBinding(@src()); - this.pending_requests += 1; + this.onPendingRequest(); if (comptime Environment.isDebug) { this.vm.eventLoop().debug.enter(); } @@ -6224,29 +6853,31 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this.vm.eventLoop().debug.exit(); } } - req.setYield(false); + resp.timeout(this.config.idleTimeout); + var ctx = this.request_pool_allocator.tryGet() catch bun.outOfMemory(); ctx.create(this, req, resp); this.vm.jsc.reportExtraMemory(@sizeOf(RequestContext)); - var request_object = this.allocator.create(JSC.WebCore.Request) catch bun.outOfMemory(); - var body = JSC.WebCore.InitRequestBodyValue(.{ .Null = {} }) catch unreachable; + var body = this.vm.initRequestBodyValue(.{ .Null = {} }) catch unreachable; ctx.request_body = body; var signal = JSC.WebCore.AbortSignal.new(this.globalThis); ctx.signal = signal; + signal.pendingActivityRef(); - request_object.* = .{ + const request_object = Request.new(.{ .method = ctx.method, .request_context = AnyRequestContext.init(ctx), .https = ssl_enabled, .signal = signal.ref(), .body = body.ref(), - }; + }); + ctx.request_weakref = Request.WeakRef.create(request_object); if (comptime debug_mode) { ctx.flags.is_web_browser_navigation = brk: { - if (ctx.req.header("sec-fetch-dest")) |fetch_dest| { + if (req.header("sec-fetch-dest")) |fetch_dest| { if (strings.eqlComptime(fetch_dest, "document")) { break :brk true; } @@ -6287,6 +6918,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp .global = this.globalThis, .onStartBuffering = RequestContext.onStartBufferingCallback, .onStartStreaming = RequestContext.onStartStreamingRequestBodyCallback, + .onReadableStreamAvailable = RequestContext.onRequestBodyReadableStreamAvailable, }, }; ctx.flags.is_waiting_for_request_body = true; @@ -6294,22 +6926,25 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp resp.onData(*RequestContext, RequestContext.onBufferedBodyChunk, ctx); } } + const js_request = request_object.toJS(this.globalThis); + js_request.ensureStillAlive(); // We keep the Request object alive for the duration of the request so that we can remove the pointer to the UWS request object. var args = [_]JSC.JSValue{ - request_object.toJS(this.globalThis), + js_request, this.thisObject, }; const request_value = args[0]; request_value.ensureStillAlive(); - const response_value = this.config.onRequest.callWithThis(this.globalThis, this.thisObject, &args); + const response_value = this.config.onRequest.call(this.globalThis, this.thisObject, &args) catch |err| + this.globalThis.takeException(err); defer { // uWS request will not live longer than this function - request_object.request_context = JSC.API.AnyRequestContext.Null; + request_object.request_context.detachRequest(); } - + const original_state = ctx.defer_deinit_until_callback_completes; var should_deinit_context = false; ctx.defer_deinit_until_callback_completes = &should_deinit_context; ctx.onResponse( @@ -6319,15 +6954,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp request_value, response_value, ); - ctx.defer_deinit_until_callback_completes = null; + ctx.defer_deinit_until_callback_completes = original_state; if (should_deinit_context) { - request_object.request_context = JSC.API.AnyRequestContext.Null; ctx.deinit(); return; } - if (!ctx.flags.has_marked_complete and !ctx.flags.has_marked_pending and ctx.pending_promises_for_abort == 0 and !ctx.flags.is_waiting_for_request_body and !ctx.flags.has_sendfile_ctx) { + if (ctx.shouldRenderMissing()) { ctx.renderMissing(); return; } @@ -6345,25 +6979,23 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp JSC.markBinding(@src()); this.pending_requests += 1; req.setYield(false); - var ctx = this.request_pool_allocator.tryGet() catch @panic("ran out of memory"); + var ctx = this.request_pool_allocator.tryGet() catch bun.outOfMemory(); ctx.create(this, req, resp); - var request_object = this.allocator.create(JSC.WebCore.Request) catch unreachable; - var body = JSC.WebCore.InitRequestBodyValue(.{ .Null = {} }) catch unreachable; + var body = this.vm.initRequestBodyValue(.{ .Null = {} }) catch unreachable; ctx.request_body = body; var signal = JSC.WebCore.AbortSignal.new(this.globalThis); ctx.signal = signal; - request_object.* = .{ + var request_object = Request.new(.{ .method = ctx.method, .request_context = AnyRequestContext.init(ctx), - .upgrader = ctx, .https = ssl_enabled, .signal = signal.ref(), .body = body.ref(), - }; + }); ctx.upgrade_context = upgrade_ctx; - + ctx.request_weakref = Request.WeakRef.create(request_object); // We keep the Request object alive for the duration of the request so that we can remove the pointer to the UWS request object. var args = [_]JSC.JSValue{ request_object.toJS(this.globalThis), @@ -6371,13 +7003,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp }; const request_value = args[0]; request_value.ensureStillAlive(); - const response_value = this.config.onRequest.callWithThis(this.globalThis, this.thisObject, &args); + const response_value = this.config.onRequest.call(this.globalThis, this.thisObject, &args) catch |err| + this.globalThis.takeException(err); defer { - if (!ctx.didUpgradeWebSocket()) {} // uWS request will not live longer than this function - request_object.request_context = JSC.API.AnyRequestContext.Null; + request_object.request_context.detachRequest(); } + const original_state = ctx.defer_deinit_until_callback_completes; var should_deinit_context = false; ctx.defer_deinit_until_callback_completes = &should_deinit_context; ctx.onResponse( @@ -6387,15 +7020,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp request_value, response_value, ); - ctx.defer_deinit_until_callback_completes = null; + ctx.defer_deinit_until_callback_completes = original_state; if (should_deinit_context) { - request_object.request_context = JSC.API.AnyRequestContext.Null; ctx.deinit(); return; } - if (!ctx.flags.has_marked_complete and !ctx.flags.has_marked_pending and ctx.pending_promises_for_abort == 0 and !ctx.flags.is_waiting_for_request_body and !ctx.flags.has_sendfile_ctx) { + if (ctx.shouldRenderMissing()) { ctx.renderMissing(); return; } @@ -6404,6 +7036,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp } fn setRoutes(this: *ThisServer) void { + if (this.config.static_routes.items.len > 0) { + this.config.applyStaticRoutes( + ssl_enabled, + AnyServer.from(this), + this.app, + ); + } + if (this.config.websocket) |*websocket| { websocket.globalObject = this.globalThis; websocket.handler.app = this.app; @@ -6510,13 +7150,112 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp }; } +pub const ServerAllConnectionsClosedTask = struct { + globalObject: *JSC.JSGlobalObject, + promise: JSC.JSPromise.Strong, + tracker: JSC.AsyncTaskTracker, + + pub usingnamespace bun.New(@This()); + + pub fn runFromJSThread(this: *ServerAllConnectionsClosedTask, vm: *JSC.VirtualMachine) void { + httplog("ServerAllConnectionsClosedTask runFromJSThread", .{}); + + const globalObject = this.globalObject; + const tracker = this.tracker; + tracker.willDispatch(globalObject); + defer tracker.didDispatch(globalObject); + + var promise = this.promise; + this.destroy(); + + if (!vm.isShuttingDown()) { + promise.resolve(globalObject, .undefined); + } else { + promise.deinit(); + } + } +}; + pub const HTTPServer = NewServer(JSC.Codegen.JSHTTPServer, false, false); pub const HTTPSServer = NewServer(JSC.Codegen.JSHTTPSServer, true, false); pub const DebugHTTPServer = NewServer(JSC.Codegen.JSDebugHTTPServer, false, true); pub const DebugHTTPSServer = NewServer(JSC.Codegen.JSDebugHTTPSServer, true, true); +const AnyServer = union(enum) { + HTTPServer: *HTTPServer, + HTTPSServer: *HTTPSServer, + DebugHTTPServer: *DebugHTTPServer, + DebugHTTPSServer: *DebugHTTPSServer, + + pub fn config(this: AnyServer) *const ServerConfig { + return switch (this) { + inline else => |server| &server.config, + }; + } + + pub fn from(server: anytype) AnyServer { + return switch (@TypeOf(server)) { + *HTTPServer => AnyServer{ .HTTPServer = server }, + *HTTPSServer => AnyServer{ .HTTPSServer = server }, + *DebugHTTPServer => AnyServer{ .DebugHTTPServer = server }, + *DebugHTTPSServer => AnyServer{ .DebugHTTPSServer = server }, + else => @compileError("Invalid server type"), + }; + } + + pub fn onPendingRequest(this: AnyServer) void { + switch (this) { + inline else => |server| server.onPendingRequest(), + } + } + + pub fn onRequestComplete(this: AnyServer) void { + switch (this) { + inline else => |server| server.onRequestComplete(), + } + } + pub fn onStaticRequestComplete(this: AnyServer) void { + switch (this) { + inline else => |server| server.onStaticRequestComplete(), + } + } +}; const welcome_page_html_gz = @embedFile("welcome-page.html.gz"); extern fn Bun__addInspector(bool, *anyopaque, *JSC.JSGlobalObject) void; const assert = bun.assert; + +pub export fn Server__setIdleTimeout( + server: JSC.JSValue, + seconds: JSC.JSValue, + globalThis: *JSC.JSGlobalObject, +) void { + if (!server.isObject()) { + globalThis.throw("Failed to set timeout: The 'this' value is not a Server.", .{}); + return; + } + + if (!seconds.isNumber()) { + globalThis.throw("Failed to set timeout: The provided value is not of type 'number'.", .{}); + return; + } + const value = seconds.to(c_uint); + if (server.as(HTTPServer)) |this| { + this.setIdleTimeout(value); + } else if (server.as(HTTPSServer)) |this| { + this.setIdleTimeout(value); + } else if (server.as(DebugHTTPServer)) |this| { + this.setIdleTimeout(value); + } else if (server.as(DebugHTTPSServer)) |this| { + this.setIdleTimeout(value); + } else { + globalThis.throw("Failed to set timeout: The 'this' value is not a Server.", .{}); + } +} + +comptime { + if (!JSC.is_bindgen) { + _ = Server__setIdleTimeout; + } +} diff --git a/src/bun.js/api/sockets.classes.ts b/src/bun.js/api/sockets.classes.ts index 192ba84665e29..dc2f4b39c8f5b 100644 --- a/src/bun.js/api/sockets.classes.ts +++ b/src/bun.js/api/sockets.classes.ts @@ -116,7 +116,7 @@ function generate(ssl) { }, "@@dispose": { - fn: "shutdown", + fn: "end", length: 0, }, @@ -126,11 +126,11 @@ function generate(ssl) { }, ref: { - fn: "ref", + fn: "jsRef", length: 0, }, unref: { - fn: "unref", + fn: "jsUnref", length: 0, }, @@ -191,7 +191,7 @@ export default [ length: 1, }, "@@dispose": { - fn: "stop", + fn: "dispose", length: 0, }, diff --git a/src/bun.js/api/streams.classes.ts b/src/bun.js/api/streams.classes.ts index e766278c26ee1..f4419c5db862d 100644 --- a/src/bun.js/api/streams.classes.ts +++ b/src/bun.js/api/streams.classes.ts @@ -39,6 +39,32 @@ function source(name) { isClosed: { getter: "getIsClosedFromJS", }, + ...(name !== "File" + ? // Buffered versions + // not implemented in File, yet. + { + text: { + fn: "textFromJS", + length: 0, + }, + json: { + fn: "jsonFromJS", + length: 0, + }, + arrayBuffer: { + fn: "arrayBufferFromJS", + length: 0, + }, + blob: { + fn: "blobFromJS", + length: 0, + }, + bytes: { + fn: "bytesFromJS", + length: 0, + }, + } + : {}), ...(name === "File" ? { setRawMode: { diff --git a/src/bun.js/api/zlib.classes.ts b/src/bun.js/api/zlib.classes.ts new file mode 100644 index 0000000000000..6b55a9f7d1fd9 --- /dev/null +++ b/src/bun.js/api/zlib.classes.ts @@ -0,0 +1,250 @@ +import { define } from "../../codegen/class-definitions"; + +export default [ + define({ + name: "BrotliEncoder", + construct: true, + noConstructor: true, + finalize: true, + configurable: false, + hasPendingActivity: true, + klass: {}, + JSType: "0b11101110", + values: ["callback"], + proto: { + transform: { + fn: "transform", + length: 2, + }, + transformSync: { + fn: "transformSync", + length: 2, + }, + reset: { + fn: "reset", + length: 0, + }, + bytesWritten: { + getter: "getBytesWritten", + }, + bytesRead: { + // deprecated + value: "bytesWritten", + }, + closed: { + getter: "getClosed", + }, + close: { + fn: "close", + length: 0, + }, + chunkSize: { + getter: "getChunkSize", + }, + flush: { + getter: "getFlush", + }, + finishFlush: { + getter: "getFinishFlush", + }, + fullFlush: { + getter: "getFullFlush", + }, + maxOutputLength: { + getter: "getMaxOutputLength", + }, + }, + }), + define({ + name: "BrotliDecoder", + construct: true, + noConstructor: true, + finalize: true, + configurable: false, + hasPendingActivity: true, + klass: {}, + JSType: "0b11101110", + values: ["callback"], + + proto: { + transform: { + fn: "transform", + length: 2, + }, + transformSync: { + fn: "transformSync", + length: 2, + }, + reset: { + fn: "reset", + length: 0, + }, + bytesWritten: { + getter: "getBytesWritten", + }, + bytesRead: { + // deprecated + value: "bytesWritten", + }, + closed: { + getter: "getClosed", + }, + close: { + fn: "close", + length: 0, + }, + chunkSize: { + getter: "getChunkSize", + }, + flush: { + getter: "getFlush", + }, + finishFlush: { + getter: "getFinishFlush", + }, + fullFlush: { + getter: "getFullFlush", + }, + maxOutputLength: { + getter: "getMaxOutputLength", + }, + }, + }), + define({ + name: "ZlibEncoder", + construct: true, + noConstructor: true, + finalize: true, + configurable: false, + hasPendingActivity: true, + klass: {}, + JSType: "0b11101110", + values: ["callback"], + proto: { + transform: { + fn: "transform", + length: 2, + }, + transformSync: { + fn: "transformSync", + length: 2, + }, + transformWith: { + fn: "transformWith", + length: 4, + }, + reset: { + fn: "reset", + length: 0, + }, + bytesWritten: { + getter: "getBytesWritten", + }, + bytesRead: { + // deprecated + value: "bytesWritten", + }, + level: { + getter: "getLevel", + }, + strategy: { + getter: "getStrategy", + }, + closed: { + getter: "getClosed", + }, + close: { + fn: "close", + length: 0, + }, + params: { + fn: "params", + length: 3, + }, + chunkSize: { + getter: "getChunkSize", + }, + flush: { + getter: "getFlush", + }, + finishFlush: { + getter: "getFinishFlush", + }, + fullFlush: { + getter: "getFullFlush", + }, + maxOutputLength: { + getter: "getMaxOutputLength", + }, + }, + }), + define({ + name: "ZlibDecoder", + construct: true, + noConstructor: true, + finalize: true, + configurable: false, + hasPendingActivity: true, + klass: {}, + JSType: "0b11101110", + values: ["callback"], + + proto: { + transform: { + fn: "transform", + length: 2, + }, + transformSync: { + fn: "transformSync", + length: 2, + }, + transformWith: { + fn: "transformWith", + length: 4, + }, + reset: { + fn: "reset", + length: 0, + }, + bytesWritten: { + getter: "getBytesWritten", + }, + bytesRead: { + // deprecated + value: "bytesWritten", + }, + level: { + getter: "getLevel", + }, + strategy: { + getter: "getStrategy", + }, + closed: { + getter: "getClosed", + }, + close: { + fn: "close", + length: 0, + }, + params: { + fn: "params", + length: 3, + }, + chunkSize: { + getter: "getChunkSize", + }, + flush: { + getter: "getFlush", + }, + finishFlush: { + getter: "getFinishFlush", + }, + fullFlush: { + getter: "getFullFlush", + }, + maxOutputLength: { + getter: "getMaxOutputLength", + }, + }, + }), +]; diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index 16ab349138cb3..8c3884a4d34ea 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -187,17 +187,7 @@ pub fn createError( } } -pub fn throwTypeError( - code: JSC.Node.ErrorCode, - comptime fmt: string, - args: anytype, - ctx: js.JSContextRef, - exception: ExceptionValueRef, -) void { - exception.* = toTypeError(code, fmt, args, ctx).asObjectRef(); -} - -pub fn toTypeErrorWithCode( +fn toTypeErrorWithCode( code: []const u8, comptime fmt: string, args: anytype, @@ -219,31 +209,31 @@ pub fn toTypeErrorWithCode( } pub fn toTypeError( - code: JSC.Node.ErrorCode, - comptime fmt: string, + code: JSC.Error, + comptime fmt: [:0]const u8, args: anytype, ctx: js.JSContextRef, ) JSC.JSValue { - return toTypeErrorWithCode(@tagName(code), fmt, args, ctx); + return code.fmt(ctx, fmt, args); } pub fn throwInvalidArguments( - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ctx: js.JSContextRef, exception: ExceptionValueRef, ) void { @setCold(true); - return throwTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, fmt, args, ctx, exception); + exception.* = JSC.Error.ERR_INVALID_ARG_TYPE.fmt(ctx, fmt, args).asObjectRef(); } pub fn toInvalidArguments( - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ctx: js.JSContextRef, ) JSC.JSValue { @setCold(true); - return toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, fmt, args, ctx); + return JSC.Error.ERR_INVALID_ARG_TYPE.fmt(ctx, fmt, args); } pub fn getAllocator(_: js.JSContextRef) std.mem.Allocator { @@ -261,9 +251,9 @@ pub const JSStringList = std.ArrayList(js.JSStringRef); pub const ArrayBuffer = extern struct { ptr: [*]u8 = undefined, - offset: u32 = 0, - len: u32 = 0, - byte_len: u32 = 0, + offset: usize = 0, + len: usize = 0, + byte_len: usize = 0, typed_array_type: JSC.JSValue.JSType = .Cell, value: JSC.JSValue = JSC.JSValue.zero, shared: bool = false, @@ -415,6 +405,21 @@ pub const ArrayBuffer = extern struct { return Bun__createUint8ArrayForCopy(globalThis, bytes.ptr, bytes.len, true); } + pub fn createUint8Array(globalThis: *JSC.JSGlobalObject, bytes: []const u8) JSValue { + JSC.markBinding(@src()); + return Bun__createUint8ArrayForCopy(globalThis, bytes.ptr, bytes.len, false); + } + + extern "C" fn Bun__allocUint8ArrayForCopy(*JSC.JSGlobalObject, usize, **anyopaque) JSValue; + pub fn allocBuffer(globalThis: *JSC.JSGlobalObject, len: usize) struct { JSValue, []u8 } { + var ptr: [*]u8 = undefined; + const buffer = Bun__allocUint8ArrayForCopy(globalThis, len, @ptrCast(&ptr)); + if (buffer.isEmpty()) { + return .{ buffer, &.{} }; + } + return .{ buffer, ptr[0..len] }; + } + extern "C" fn Bun__createUint8ArrayForCopy(*JSC.JSGlobalObject, ptr: ?*const anyopaque, len: usize, buffer: bool) JSValue; extern "C" fn Bun__createArrayBufferForCopy(*JSC.JSGlobalObject, ptr: ?*const anyopaque, len: usize) JSValue; @@ -425,6 +430,19 @@ pub const ArrayBuffer = extern struct { return out; } + extern "C" fn JSArrayBuffer__fromDefaultAllocator(*JSC.JSGlobalObject, ptr: [*]u8, len: usize) JSC.JSValue; + pub fn toJSFromDefaultAllocator(globalThis: *JSC.JSGlobalObject, bytes: []u8) JSC.JSValue { + return JSArrayBuffer__fromDefaultAllocator(globalThis, bytes.ptr, bytes.len); + } + + pub fn fromDefaultAllocator(globalThis: *JSC.JSGlobalObject, bytes: []u8, comptime typed_array_type: JSC.JSValue.JSType) JSC.JSValue { + return switch (typed_array_type) { + .ArrayBuffer => JSArrayBuffer__fromDefaultAllocator(globalThis, bytes.ptr, bytes.len), + .Uint8Array => JSC.JSUint8Array.fromBytes(globalThis, bytes), + else => @compileError("Not implemented yet"), + }; + } + pub fn fromBytes(bytes: []u8, typed_array_type: JSC.JSValue.JSType) ArrayBuffer { return ArrayBuffer{ .offset = 0, .len = @as(u32, @intCast(bytes.len)), .byte_len = @as(u32, @intCast(bytes.len)), .typed_array_type = typed_array_type, .ptr = bytes.ptr }; } @@ -629,18 +647,18 @@ pub const MarkedArrayBuffer = struct { } } - pub fn init(allocator: std.mem.Allocator, size: u32, typed_array_type: js.JSTypedArrayType) !*MarkedArrayBuffer { + pub fn init(allocator: std.mem.Allocator, size: u32, typed_array_type: JSC.JSValue.JSType) !*MarkedArrayBuffer { const bytes = try allocator.alloc(u8, size); const container = try allocator.create(MarkedArrayBuffer); container.* = MarkedArrayBuffer.fromBytes(bytes, allocator, typed_array_type); return container; } - pub fn toNodeBuffer(this: MarkedArrayBuffer, ctx: js.JSContextRef) JSC.JSValue { + pub fn toNodeBuffer(this: *const MarkedArrayBuffer, ctx: js.JSContextRef) JSC.JSValue { return JSValue.createBufferWithCtx(ctx, this.buffer.byteSlice(), this.buffer.ptr, MarkedArrayBuffer_deallocator); } - pub fn toJSObjectRef(this: MarkedArrayBuffer, ctx: js.JSContextRef, exception: js.ExceptionRef) js.JSObjectRef { + pub fn toJSObjectRef(this: *const MarkedArrayBuffer, ctx: js.JSContextRef, exception: js.ExceptionRef) js.JSObjectRef { if (!this.buffer.value.isEmptyOrUndefinedOrNull()) { return this.buffer.value.asObjectRef(); } @@ -666,7 +684,7 @@ pub const MarkedArrayBuffer = struct { } // TODO: refactor this - pub fn toJS(this: *MarkedArrayBuffer, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + pub fn toJS(this: *const MarkedArrayBuffer, globalObject: *JSC.JSGlobalObject) JSC.JSValue { var exception = [_]JSC.C.JSValueRef{null}; const obj = this.toJSObjectRef(globalObject, &exception); @@ -771,6 +789,7 @@ const TestScope = Test.TestScope; const NodeFS = JSC.Node.NodeFS; const TextEncoder = WebCore.TextEncoder; const TextDecoder = WebCore.TextDecoder; +const TextEncoderStreamEncoder = WebCore.TextEncoderStreamEncoder; const HTMLRewriter = JSC.Cloudflare.HTMLRewriter; const Element = JSC.Cloudflare.Element; const Comment = JSC.Cloudflare.Comment; @@ -931,7 +950,6 @@ pub fn DOMCall( comptime class_name: string, comptime Container: type, comptime functionName: string, - comptime ResultType: type, comptime dom_effect: DOMEffect, ) type { return extern struct { @@ -949,7 +967,7 @@ pub fn DOMCall( thisValue: JSC.JSValue, arguments_ptr: [*]const JSC.JSValue, arguments_len: usize, - ) callconv(.C) JSValue { + ) callconv(JSC.conv) JSValue { return @field(Container, functionName)( globalObject, thisValue, @@ -961,215 +979,18 @@ pub fn DOMCall( pub const Fastpath = @TypeOf(fastpath); pub const Arguments = std.meta.ArgsTuple(Fastpath); - pub const Export = shim.exportFunctions(.{ - .slowpath = slowpath, - .fastpath = fastpath, - }); - pub fn put(globalObject: *JSC.JSGlobalObject, value: JSValue) void { shim.cppFn("put", .{ globalObject, value }); } pub const effect = dom_effect; - pub fn printGenerateDOMJITSignature(comptime Writer: type, writer: Writer) !void { - const signatureName = "DOMJIT_" ++ shim.name ++ "_signature"; - const slowPathName = Export[0].symbol_name; - const fastPathName = Export[1].symbol_name; - const Fields: []const std.builtin.Type.StructField = std.meta.fields(Arguments); - - const options = .{ - .name = functionName, - .exportName = name ++ "__put", - .signatureName = signatureName, - .IDLResultName = DOMCallResultType(ResultType), - .fastPathName = fastPathName, - .slowPathName = slowPathName, - .argumentsCount = Fields.len - 2, - }; - { - const fmt = - \\extern "C" JSC_DECLARE_HOST_FUNCTION({[slowPathName]s}Wrapper); - \\extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL({[fastPathName]s}Wrapper, EncodedJSValue, (JSC::JSGlobalObject* lexicalGlobalObject, void* thisValue - ; - try writer.print(fmt, .{ .fastPathName = options.fastPathName, .slowPathName = options.slowPathName }); - } - { - switch (Fields.len - 2) { - 0 => { - try writer.writeAll("));\n"); - }, - 1 => { - try writer.writeAll(", "); - try writer.writeAll(DOMCallArgumentTypeWrapper(Fields[2].type)); - try writer.writeAll("));\n"); - }, - 2 => { - try writer.writeAll(", "); - try writer.writeAll(DOMCallArgumentTypeWrapper(Fields[2].type)); - try writer.writeAll(", "); - try writer.writeAll(DOMCallArgumentTypeWrapper(Fields[3].type)); - try writer.writeAll("));\n"); - }, - else => @compileError("Must be <= 3 arguments"), - } - } - - { - const fmt = - \\ - \\JSC_DEFINE_JIT_OPERATION({[fastPathName]s}Wrapper, EncodedJSValue, (JSC::JSGlobalObject* lexicalGlobalObject, void* thisValue - ; - try writer.print(fmt, .{ .fastPathName = options.fastPathName }); - } - { - switch (Fields.len - 2) { - 0 => { - try writer.writeAll(")) {\n"); - }, - 1 => { - try writer.writeAll(", "); - try writer.writeAll(DOMCallArgumentTypeWrapper(Fields[2].type)); - try writer.writeAll(" arg1)) {\n"); - }, - 2 => { - try writer.writeAll(", "); - try writer.writeAll(DOMCallArgumentTypeWrapper(Fields[2].type)); - try writer.writeAll(" arg1, "); - try writer.writeAll(DOMCallArgumentTypeWrapper(Fields[3].type)); - try writer.writeAll(" arg2)) {\n"); - }, - else => @compileError("Must be <= 3 arguments"), - } - { - const fmt = - \\VM& vm = JSC::getVM(lexicalGlobalObject); - \\IGNORE_WARNINGS_BEGIN("frame-address") - \\CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - \\IGNORE_WARNINGS_END - \\JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - \\return {[fastPathName]s}(lexicalGlobalObject, thisValue - ; - try writer.print(fmt, .{ .fastPathName = options.fastPathName }); - } - { - switch (Fields.len - 2) { - 0 => { - try writer.writeAll(");\n}\n"); - }, - 1 => { - try writer.writeAll(", arg1);\n}\n"); - }, - 2 => { - try writer.writeAll(", arg1, arg2);\n}\n"); - }, - else => @compileError("Must be <= 3 arguments"), - } - } - } - - { - const fmt = - \\JSC_DEFINE_HOST_FUNCTION({[slowPathName]s}Wrapper, (JSC::JSGlobalObject *globalObject, JSC::CallFrame* frame)) {{ - \\ return {[slowPathName]s}(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); - \\}} - \\ - \\extern "C" void {[exportName]s}(JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) {{ - \\ JSC::JSObject *thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - \\ static const JSC::DOMJIT::Signature {[signatureName]s}( - \\ {[fastPathName]s}Wrapper, - \\ thisObject->classInfo(), - \\ - ; - - try writer.print(fmt, .{ - .slowPathName = options.slowPathName, - .exportName = options.exportName, - .fastPathName = options.fastPathName, - .signatureName = options.signatureName, - }); - } - if (effect.isPure()) { - try writer.writeAll("JSC::DOMJIT::Effect::forPure(),\n "); - } else if (effect.writes[0] == DOMEffect.pure.writes[0]) { - try writer.print( - "JSC::DOMJIT::Effect::forReadKinds(JSC::DFG::AbstractHeapKind::{s}, JSC::DFG::AbstractHeapKind::{s}, JSC::DFG::AbstractHeapKind::{s}, JSC::DFG::AbstractHeapKind::{s}),\n ", - .{ - @tagName(effect.reads[0]), - @tagName(effect.reads[1]), - @tagName(effect.reads[2]), - @tagName(effect.reads[3]), - }, - ); - } else if (effect.reads[0] == DOMEffect.pure.reads[0]) { - try writer.print( - "JSC::DOMJIT::Effect::forWriteKinds(JSC::DFG::AbstractHeapKind::{s}, JSC::DFG::AbstractHeapKind::{s}, JSC::DFG::AbstractHeapKind::{s}, JSC::DFG::AbstractHeapKind::{s}),\n ", - .{ - @tagName(effect.writes[0]), - @tagName(effect.writes[1]), - @tagName(effect.writes[2]), - @tagName(effect.writes[3]), - }, - ); - } else { - try writer.writeAll("JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),\n "); - } - - { - try writer.writeAll(DOMCallResultType(ResultType)); - } - - switch (Fields.len - 2) { - 0 => {}, - 1 => { - try writer.writeAll(",\n "); - try writer.writeAll(DOMCallArgumentType(Fields[2].type)); - try writer.writeAll("\n "); - }, - 2 => { - try writer.writeAll(",\n "); - try writer.writeAll(DOMCallArgumentType(Fields[2].type)); - try writer.writeAll(",\n "); - try writer.writeAll(DOMCallArgumentType(Fields[3].type)); - try writer.writeAll("\n "); - }, - else => @compileError("Must be <= 3 arguments"), - } - - try writer.writeAll(");\n "); - - { - const fmt = - \\ JSFunction* function = JSFunction::create( - \\ globalObject->vm(), - \\ globalObject, - \\ {[argumentsCount]d}, - \\ String("{[name]s}"_s), - \\ {[slowPathName]s}Wrapper, ImplementationVisibility::Public, NoIntrinsic, {[slowPathName]s}Wrapper, - \\ &{[signatureName]s} - \\ ); - \\ thisObject->putDirect( - \\ globalObject->vm(), - \\ Identifier::fromString(globalObject->vm(), "{[name]s}"_s), - \\ function - \\ ); - \\}} - ; - try writer.print(fmt, .{ - .argumentsCount = options.argumentsCount, - .name = options.name, - .slowPathName = options.slowPathName, - .signatureName = options.signatureName, - }); - } - } - pub const Extern = [_][]const u8{"put"}; comptime { if (!JSC.is_bindgen) { - @export(slowpath, .{ .name = Export[0].symbol_name }); - @export(fastpath, .{ .name = Export[1].symbol_name }); + @export(slowpath, .{ .name = shim.symbolName("slowpath") }); + @export(fastpath, .{ .name = shim.symbolName("fastpath") }); } else { _ = slowpath; _ = fastpath; @@ -1179,7 +1000,7 @@ pub fn DOMCall( } pub fn InstanceMethodType(comptime Container: type) type { - return fn (instance: *Container, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue; + return fn (instance: *Container, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue; } pub fn wrapInstanceMethod( @@ -1197,7 +1018,7 @@ pub fn wrapInstanceMethod( this: *Container, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(FunctionTypeInfo.params.len); var iter = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments.slice()); var args: Args = undefined; @@ -1351,7 +1172,7 @@ pub fn wrapInstanceMethod( } } - return @call(.auto, @field(Container, name), args); + return @call(.always_inline, @field(Container, name), args); } }.method; } @@ -1370,7 +1191,7 @@ pub fn wrapStaticMethod( pub fn method( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(FunctionTypeInfo.params.len); var iter = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments.slice()); var args: Args = undefined; @@ -1504,7 +1325,7 @@ pub fn wrapStaticMethod( defer iter.deinit(); - return @call(.auto, @field(Container, name), args); + return @call(.always_inline, @field(Container, name), args); } }.method; } @@ -1545,6 +1366,7 @@ pub const BinaryType = enum(u4) { Int8Array, Int16Array, Int32Array, + Float16Array, Float32Array, Float64Array, // DataView, @@ -1555,6 +1377,7 @@ pub const BinaryType = enum(u4) { .Buffer => .Uint8Array, // .DataView => .DataView, .Float32Array => .Float32Array, + .Float16Array => .Float16Array, .Float64Array => .Float64Array, .Int16Array => .Int16Array, .Int32Array => .Int32Array, @@ -1576,6 +1399,7 @@ pub const BinaryType = enum(u4) { .{ "Buffer", .Buffer }, // .{ "DataView", .DataView }, .{ "Float32Array", .Float32Array }, + .{ "Float16Array", .Float16Array }, .{ "Float64Array", .Float64Array }, .{ "Int16Array", .Int16Array }, .{ "Int32Array", .Int32Array }, @@ -1586,6 +1410,7 @@ pub const BinaryType = enum(u4) { .{ "arraybuffer", .ArrayBuffer }, .{ "buffer", .Buffer }, // .{ "dataview", .DataView }, + .{ "float16array", .Float16Array }, .{ "float32array", .Float32Array }, .{ "float64array", .Float64Array }, .{ "int16array", .Int16Array }, @@ -1618,7 +1443,7 @@ pub const BinaryType = enum(u4) { .Uint8Array => return JSC.ArrayBuffer.create(globalThis, bytes, .Uint8Array), // These aren't documented, but they are supported - .Uint16Array, .Uint32Array, .Int8Array, .Int16Array, .Int32Array, .Float32Array, .Float64Array => { + .Uint16Array, .Uint32Array, .Int8Array, .Int16Array, .Int32Array, .Float16Array, .Float32Array, .Float64Array => { const buffer = JSC.ArrayBuffer.create(globalThis, bytes, .ArrayBuffer); return JSC.JSValue.c(JSC.C.JSObjectMakeTypedArrayWithArrayBuffer(globalThis, this.toTypedArrayType(), buffer.asObjectRef(), null)); }, diff --git a/src/bun.js/bindings/.clang-format-ignore b/src/bun.js/bindings/.clang-format-ignore new file mode 100644 index 0000000000000..2a91525822d7a --- /dev/null +++ b/src/bun.js/bindings/.clang-format-ignore @@ -0,0 +1,2 @@ +*.lut.h +sqlite/sqlite3_local.h \ No newline at end of file diff --git a/src/bun.js/bindings/AsyncContextFrame.h b/src/bun.js/bindings/AsyncContextFrame.h index 7635cb19c6b35..b853917590e14 100644 --- a/src/bun.js/bindings/AsyncContextFrame.h +++ b/src/bun.js/bindings/AsyncContextFrame.h @@ -36,7 +36,7 @@ class AsyncContextFrame : public JSC::JSNonFinalObject { { if constexpr (mode == JSC::SubspaceAccess::Concurrently) return nullptr; - return WebCore::subspaceForImpl( + return WebCore::subspaceForImpl( vm, [](auto& spaces) { return spaces.m_clientSubspaceForAsyncContextFrame.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForAsyncContextFrame = std::forward(space); }, diff --git a/src/bun.js/bindings/Base64Helpers.cpp b/src/bun.js/bindings/Base64Helpers.cpp index 58d7934331af2..ac99cf78e81f5 100644 --- a/src/bun.js/bindings/Base64Helpers.cpp +++ b/src/bun.js/bindings/Base64Helpers.cpp @@ -1,6 +1,6 @@ #include "root.h" -#include "simdutf.h" +#include "wtf/SIMDUTF.h" #include "ExceptionOr.h" diff --git a/src/bun.js/bindings/BunClientData.cpp b/src/bun.js/bindings/BunClientData.cpp index 544827ae7ebc3..ee48afd07fb41 100644 --- a/src/bun.js/bindings/BunClientData.cpp +++ b/src/bun.js/bindings/BunClientData.cpp @@ -26,6 +26,8 @@ namespace WebCore { using namespace JSC; +RefPtr createBuiltinsSourceProvider(); + JSHeapData::JSHeapData(Heap& heap) : m_heapCellTypeForJSWorkerGlobalScope(JSC::IsoHeapCellType::Args()) , m_domBuiltinConstructorSpace ISO_SUBSPACE_INIT(heap, heap.cellHeapCellType, JSDOMBuiltinConstructorBase) @@ -38,15 +40,14 @@ JSHeapData::JSHeapData(Heap& heap) #define CLIENT_ISO_SUBSPACE_INIT(subspace) subspace(m_heapData->subspace) -JSVMClientData::JSVMClientData(VM& vm) - : m_builtinFunctions(vm) - , m_builtinNames(vm) +JSVMClientData::JSVMClientData(VM& vm, RefPtr sourceProvider) + : m_builtinNames(vm) + , m_builtinFunctions(vm, sourceProvider, m_builtinNames) , m_heapData(JSHeapData::ensureHeapData(vm.heap)) , CLIENT_ISO_SUBSPACE_INIT(m_domBuiltinConstructorSpace) , CLIENT_ISO_SUBSPACE_INIT(m_domConstructorSpace) , CLIENT_ISO_SUBSPACE_INIT(m_domNamespaceObjectSpace) , m_clientSubspaces(makeUnique()) - { } @@ -72,17 +73,25 @@ JSVMClientData::~JSVMClientData() } void JSVMClientData::create(VM* vm, void* bunVM) { - JSVMClientData* clientData = new JSVMClientData(*vm); + auto provider = WebCore::createBuiltinsSourceProvider(); + JSVMClientData* clientData = new JSVMClientData(*vm, provider); clientData->bunVM = bunVM; - vm->deferredWorkTimer->onAddPendingWork = Bun::JSCTaskScheduler::onAddPendingWork; - vm->deferredWorkTimer->onScheduleWorkSoon = Bun::JSCTaskScheduler::onScheduleWorkSoon; - vm->deferredWorkTimer->onCancelPendingWork = Bun::JSCTaskScheduler::onCancelPendingWork; + vm->deferredWorkTimer->onAddPendingWork = [clientData](Ref&& ticket, JSC::DeferredWorkTimer::WorkType kind) -> void { + Bun::JSCTaskScheduler::onAddPendingWork(clientData, WTFMove(ticket), kind); + }; + vm->deferredWorkTimer->onScheduleWorkSoon = [clientData](JSC::DeferredWorkTimer::Ticket ticket, JSC::DeferredWorkTimer::Task&& task) -> void { + Bun::JSCTaskScheduler::onScheduleWorkSoon(clientData, ticket, WTFMove(task)); + }; + vm->deferredWorkTimer->onCancelPendingWork = [clientData](JSC::DeferredWorkTimer::Ticket ticket) -> void { + Bun::JSCTaskScheduler::onCancelPendingWork(clientData, ticket); + }; vm->clientData = clientData; // ~VM deletes this pointer. clientData->m_normalWorld = DOMWrapperWorld::create(*vm, DOMWrapperWorld::Type::Normal); vm->heap.addMarkingConstraint(makeUnique(*vm, clientData->heapData())); vm->m_typedArrayController = adoptRef(new WebCoreTypedArrayController(true)); + clientData->builtinFunctions().exportNames(); } } // namespace WebCore \ No newline at end of file diff --git a/src/bun.js/bindings/BunClientData.h b/src/bun.js/bindings/BunClientData.h index e6862b955325a..5b28bb5801c2a 100644 --- a/src/bun.js/bindings/BunClientData.h +++ b/src/bun.js/bindings/BunClientData.h @@ -76,7 +76,7 @@ class JSVMClientData : public JSC::VM::ClientData { WTF_MAKE_FAST_ALLOCATED; public: - explicit JSVMClientData(JSC::VM&); + explicit JSVMClientData(JSC::VM&, RefPtr); virtual ~JSVMClientData(); @@ -190,6 +190,11 @@ static inline BunBuiltinNames& builtinNames(JSC::VM& vm) } // namespace WebCore +inline void* bunVM(JSC::VM& vm) +{ + return WebCore::clientData(vm)->bunVM; +} + namespace WebCore { using JSVMClientData = WebCore::JSVMClientData; using JSHeapData = WebCore::JSHeapData; diff --git a/src/bun.js/bindings/BunGlobalScope.cpp b/src/bun.js/bindings/BunGlobalScope.cpp new file mode 100644 index 0000000000000..5594cb8309879 --- /dev/null +++ b/src/bun.js/bindings/BunGlobalScope.cpp @@ -0,0 +1,50 @@ + +#include "root.h" +#include "ZigGlobalObject.h" +#include "BunGlobalScope.h" +#include "JavaScriptCore/VM.h" +#include "JavaScriptCore/VMTraps.h" +#include "JavaScriptCore/VMTrapsInlines.h" +#include "JavaScriptCore/LazyClassStructure.h" +#include "JavaScriptCore/LazyClassStructureInlines.h" +#include "BunClientData.h" + +namespace Bun { + +using namespace JSC; + +void GlobalScope::finishCreation(JSC::VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + m_encodeIntoObjectStructure.initLater( + [](const JSC::LazyProperty::Initializer& init) { + auto& vm = init.vm; + auto& globalObject = *init.owner; + Structure* structure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), 2); + PropertyOffset offset; + auto clientData = WebCore::clientData(vm); + structure = Structure::addPropertyTransition(vm, structure, clientData->builtinNames().readPublicName(), 0, offset); + RELEASE_ASSERT(offset == 0); + structure = Structure::addPropertyTransition(vm, structure, clientData->builtinNames().writtenPublicName(), 0, offset); + RELEASE_ASSERT(offset == 1); + init.set(structure); + }); +} + +DEFINE_VISIT_CHILDREN(GlobalScope); + +template +void GlobalScope::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + GlobalScope* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + thisObject->m_encodeIntoObjectStructure.visit(visitor); +} + +const JSC::ClassInfo GlobalScope::s_info = { "GlobalScope"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(GlobalScope) }; + +} diff --git a/src/bun.js/bindings/BunGlobalScope.h b/src/bun.js/bindings/BunGlobalScope.h new file mode 100644 index 0000000000000..1fc6f4b1ce0cf --- /dev/null +++ b/src/bun.js/bindings/BunGlobalScope.h @@ -0,0 +1,45 @@ +#pragma once + +#include "root.h" +#include "JavaScriptCore/JSGlobalObject.h" + +namespace Bun { + +using namespace JSC; + +class GlobalScope : public JSC::JSGlobalObject { + using Base = JSC::JSGlobalObject; + +protected: + void finishCreation(JSC::VM& vm); + +public: + GlobalScope(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + + GlobalScope(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable) + : Base(vm, structure, methodTable) + { + } + + DECLARE_INFO; + DECLARE_VISIT_CHILDREN; + + JSC::Structure* encodeIntoObjectStructure() const { return m_encodeIntoObjectStructure.getInitializedOnMainThread(this); } + + /** + * WARNING: You must update visitChildrenImpl() if you add a new field. + * + * That informs the garbage collector that these fields exist. If you don't + * do that, the garbage collector will not know about these fields and will + * not trace them. This will lead to crashes and very strange behavior at runtime. + * + * For example, if you don't add the queueMicrotask functions to visitChildrenImpl(), + * those callbacks will eventually never be called anymore. But it'll work the first time! + */ + LazyProperty m_encodeIntoObjectStructure; +}; + +} // namespace Bun diff --git a/src/bun.js/bindings/BunInjectedScriptHost.cpp b/src/bun.js/bindings/BunInjectedScriptHost.cpp index d2091333007ff..a31aefdc2948c 100644 --- a/src/bun.js/bindings/BunInjectedScriptHost.cpp +++ b/src/bun.js/bindings/BunInjectedScriptHost.cpp @@ -33,7 +33,7 @@ JSValue BunInjectedScriptHost::subtype(JSGlobalObject* exec, JSValue value) static JSObject* constructInternalProperty(VM& vm, JSGlobalObject* exec, const String& name, JSValue value) { auto* object = constructEmptyObject(exec); - object->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, name)); + object->putDirect(vm, vm.propertyNames->name, jsString(vm, name)); object->putDirect(vm, Identifier::fromString(vm, "value"_s), value); return object; } @@ -41,7 +41,7 @@ static JSObject* constructInternalProperty(VM& vm, JSGlobalObject* exec, const S static JSObject* constructInternalProperty(VM& vm, JSGlobalObject* exec, const Identifier& name, JSValue value) { auto* object = constructEmptyObject(exec); - object->putDirect(vm, Identifier::fromString(vm, "name"_s), JSC::identifierToJSValue(vm, name)); + object->putDirect(vm, vm.propertyNames->name, JSC::identifierToJSValue(vm, name)); object->putDirect(vm, Identifier::fromString(vm, "value"_s), value); return object; } diff --git a/src/bun.js/bindings/BunObject+exports.h b/src/bun.js/bindings/BunObject+exports.h index 17bf201b60a80..8d6bd26d59176 100644 --- a/src/bun.js/bindings/BunObject+exports.h +++ b/src/bun.js/bindings/BunObject+exports.h @@ -1,3 +1,4 @@ +#pragma once // clang-format off // --- Getters --- @@ -29,6 +30,7 @@ macro(stdout) \ macro(unsafe) \ macro(semver) \ + macro(embeddedFiles) \ // --- Callbacks --- #define FOR_EACH_CALLBACK(macro) \ @@ -67,15 +69,15 @@ macro(createShellInterpreter) \ macro(createParsedShellScript) \ -#define DECLARE_ZIG_BUN_OBJECT_CALLBACK(name) extern "C" JSC::EncodedJSValue BunObject_callback_##name(JSC::JSGlobalObject*, JSC::CallFrame*); +#define DECLARE_ZIG_BUN_OBJECT_CALLBACK(name) BUN_DECLARE_HOST_FUNCTION(BunObject_callback_##name); FOR_EACH_CALLBACK(DECLARE_ZIG_BUN_OBJECT_CALLBACK); #undef DECLARE_ZIG_BUN_OBJECT_CALLBACK -#define DECLARE_ZIG_BUN_OBJECT_GETTER(name) extern "C" JSC::EncodedJSValue BunObject_getter_##name(JSC::JSGlobalObject*, JSC::JSObject*); +#define DECLARE_ZIG_BUN_OBJECT_GETTER(name) extern "C" JSC::EncodedJSValue SYSV_ABI BunObject_getter_##name(JSC::JSGlobalObject*, JSC::JSObject*); FOR_EACH_GETTER(DECLARE_ZIG_BUN_OBJECT_GETTER); #undef DECLARE_ZIG_BUN_OBJECT_GETTER -#define DEFINE_ZIG_BUN_OBJECT_GETTER_WRAPPER(name) JSC::JSValue BunObject_getter_wrap_##name(JSC::VM &vm, JSC::JSObject *object) { \ +#define DEFINE_ZIG_BUN_OBJECT_GETTER_WRAPPER(name) static JSC::JSValue BunObject_getter_wrap_##name(JSC::VM &vm, JSC::JSObject *object) { \ return JSC::JSValue::decode(BunObject_getter_##name(object->globalObject(), object)); \ } \ diff --git a/src/bun.js/bindings/BunObject.cpp b/src/bun.js/bindings/BunObject.cpp index 0f2b1f67e7567..46976c1f9b05e 100644 --- a/src/bun.js/bindings/BunObject.cpp +++ b/src/bun.js/bindings/BunObject.cpp @@ -25,18 +25,37 @@ #include "DOMJITIDLType.h" #include "DOMJITIDLTypeFilter.h" #include "Exception.h" -#include "BunObject+exports.h" #include "JSDOMException.h" #include "JSDOMConvert.h" #include "wtf/Compiler.h" #include "PathInlines.h" +#include "wtf/text/ASCIILiteral.h" +#include "BunObject+exports.h" + +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__lookup); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolve); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveSrv); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveTxt); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveSoa); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveNaptr); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveMx); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveCaa); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveNs); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolvePtr); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveCname); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getServers); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__reverse); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__lookupService); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__prefetch); +BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getCacheStats); +BUN_DECLARE_HOST_FUNCTION(Bun__fetch); +BUN_DECLARE_HOST_FUNCTION(Bun__fetchPreconnect); namespace Bun { using namespace JSC; using namespace WebCore; -extern "C" JSC::EncodedJSValue Bun__fetch(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); extern "C" bool has_bun_garbage_collector_flag_enabled; static JSValue BunObject_getter_wrap_ArrayBufferSink(VM& vm, JSObject* bunObject) @@ -66,8 +85,20 @@ static inline JSC::EncodedJSValue flattenArrayOfBuffersIntoArrayBufferOrUint8Arr } size_t arrayLength = array->length(); - if (arrayLength < 1) { + const auto returnEmptyArrayBufferView = [&]() -> EncodedJSValue { + if (asUint8Array) { + return JSValue::encode( + JSC::JSUint8Array::create( + lexicalGlobalObject, + lexicalGlobalObject->m_typedArrayUint8.get(lexicalGlobalObject), + 0)); + } + RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSArrayBuffer::create(vm, lexicalGlobalObject->arrayBufferStructure(), JSC::ArrayBuffer::create(static_cast(0), 1)))); + }; + + if (arrayLength < 1) { + return returnEmptyArrayBufferView(); } size_t byteLength = 0; @@ -122,7 +153,7 @@ static inline JSC::EncodedJSValue flattenArrayOfBuffersIntoArrayBufferOrUint8Arr byteLength = std::min(byteLength, maxLength); if (byteLength == 0) { - RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSArrayBuffer::create(vm, lexicalGlobalObject->arrayBufferStructure(), JSC::ArrayBuffer::create(static_cast(0), 1)))); + return returnEmptyArrayBufferView(); } auto buffer = JSC::ArrayBuffer::tryCreateUninitialized(byteLength, 1); @@ -210,6 +241,7 @@ JSC_DEFINE_HOST_FUNCTION(functionConcatTypedArrays, (JSGlobalObject * globalObje auto arg2 = callFrame->argument(2); if (!arg2.isUndefined()) { asUint8Array = arg2.toBoolean(globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); } return flattenArrayOfBuffersIntoArrayBufferOrUint8Array(globalObject, arrayValue, maxLength, asUint8Array); @@ -219,12 +251,12 @@ JSC_DECLARE_HOST_FUNCTION(functionConcatTypedArrays); static JSValue constructBunVersion(VM& vm, JSObject*) { - return JSC::jsString(vm, makeString(Bun__version + 1)); + return JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__version + 1))); } static JSValue constructBunRevision(VM& vm, JSObject*) { - return JSC::jsString(vm, makeString(Bun__version_sha)); + return JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__version_sha))); } static JSValue constructIsMainThread(VM&, JSObject* object) @@ -249,12 +281,23 @@ static JSValue constructPasswordObject(VM& vm, JSObject* bunObject) return JSValue::decode(JSPasswordObject__create(bunObject->globalObject())); } +JSValue constructBunFetchObject(VM& vm, JSObject* bunObject) +{ + JSFunction* fetchFn = JSFunction::create(vm, bunObject->globalObject(), 1, "fetch"_s, Bun__fetch, ImplementationVisibility::Public, NoIntrinsic); + + auto* globalObject = jsCast(bunObject->globalObject()); + fetchFn->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "preconnect"_s), 1, Bun__fetchPreconnect, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0); + + return fetchFn; +} + static JSValue constructBunShell(VM& vm, JSObject* bunObject) { auto* globalObject = jsCast(bunObject->globalObject()); JSFunction* createParsedShellScript = JSFunction::create(vm, bunObject->globalObject(), 2, "createParsedShellScript"_s, BunObject_callback_createParsedShellScript, ImplementationVisibility::Private, NoIntrinsic); JSFunction* createShellInterpreterFunction = JSFunction::create(vm, bunObject->globalObject(), 1, "createShellInterpreter"_s, BunObject_callback_createShellInterpreter, ImplementationVisibility::Private, NoIntrinsic); - JSC::JSFunction* createShellFn = JSC::JSFunction::create(vm, shellCreateBunShellTemplateFunctionCodeGenerator(vm), globalObject); + JSC::JSFunction* createShellFn = JSC::JSFunction::create(vm, globalObject, shellCreateBunShellTemplateFunctionCodeGenerator(vm), globalObject); auto scope = DECLARE_THROW_SCOPE(vm); auto args = JSC::MarkedArgumentBuffer(); @@ -275,22 +318,11 @@ static JSValue constructBunShell(VM& vm, JSObject* bunObject) return bunShell; } -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__lookup); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolve); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveSrv); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveTxt); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveSoa); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveNaptr); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveMx); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveCaa); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveNs); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolvePtr); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveCname); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getServers); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__reverse); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__lookupService); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__prefetch); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getCacheStats); +// This value currently depends on a zig feature flag +extern "C" JSC::EncodedJSValue Bun__getTemporaryDevServer(JSC::JSGlobalObject* bunObject); +static JSValue constructBunKit(VM& vm, JSObject* bunObject) { + return JSC::JSValue::decode(Bun__getTemporaryDevServer(bunObject->globalObject())); +} static JSValue constructDNSObject(VM& vm, JSObject* bunObject) { @@ -298,7 +330,7 @@ static JSValue constructDNSObject(VM& vm, JSObject* bunObject) JSC::JSObject* dnsObject = JSC::constructEmptyObject(globalObject); dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | 0); - dnsObject->putDirectNativeFunction(vm, globalObject, builtinNames(vm).resolvePublicName(), 2, Bun__DNSResolver__resolve, ImplementationVisibility::Public, NoIntrinsic, + dnsObject->putDirectNativeFunction(vm, globalObject, vm.propertyNames->resolve, 2, Bun__DNSResolver__resolve, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | 0); dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNSResolver__resolveSrv, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | 0); @@ -335,8 +367,8 @@ static JSValue constructBunPeekObject(VM& vm, JSObject* bunObject) { JSGlobalObject* globalObject = bunObject->globalObject(); JSC::Identifier identifier = JSC::Identifier::fromString(vm, "peek"_s); - JSFunction* peekFunction = JSFunction::create(vm, peekPeekCodeGenerator(vm), globalObject->globalScope()); - JSFunction* peekStatus = JSFunction::create(vm, peekPeekStatusCodeGenerator(vm), globalObject->globalScope()); + JSFunction* peekFunction = JSFunction::create(vm, globalObject, peekPeekCodeGenerator(vm), globalObject->globalScope()); + JSFunction* peekStatus = JSFunction::create(vm, globalObject, peekPeekStatusCodeGenerator(vm), globalObject->globalScope()); peekFunction->putDirect(vm, PropertyName(JSC::Identifier::fromString(vm, "status"_s)), peekStatus, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0); return peekFunction; @@ -379,23 +411,24 @@ JSC_DEFINE_HOST_FUNCTION(functionBunEscapeHTML, (JSC::JSGlobalObject * lexicalGl JSC::JSValue argument = callFrame->argument(0); if (argument.isEmpty()) return JSValue::encode(jsEmptyString(vm)); - if (argument.isNumber() || argument.isBoolean()) + if (argument.isNumber() || argument.isBoolean() || argument.isUndefined() || argument.isNull()) return JSValue::encode(argument.toString(lexicalGlobalObject)); auto scope = DECLARE_THROW_SCOPE(vm); auto string = argument.toString(lexicalGlobalObject); RETURN_IF_EXCEPTION(scope, {}); - size_t length = string->length(); - if (!length) + if (string->length() == 0) RELEASE_AND_RETURN(scope, JSValue::encode(string)); auto resolvedString = string->value(lexicalGlobalObject); + RETURN_IF_EXCEPTION(scope, {}); + JSC::EncodedJSValue encodedInput = JSValue::encode(string); - if (!resolvedString.is8Bit()) { - const auto span = resolvedString.span16(); + if (!resolvedString->is8Bit()) { + const auto span = resolvedString->span16(); RELEASE_AND_RETURN(scope, Bun__escapeHTML16(lexicalGlobalObject, encodedInput, span.data(), span.size())); } else { - const auto span = resolvedString.span8(); + const auto span = resolvedString->span8(); RELEASE_AND_RETURN(scope, Bun__escapeHTML8(lexicalGlobalObject, encodedInput, span.data(), span.size())); } } @@ -461,14 +494,13 @@ JSC_DEFINE_HOST_FUNCTION(functionBunDeepMatch, (JSGlobalObject * globalObject, J JSC_DEFINE_HOST_FUNCTION(functionBunNanoseconds, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { - auto* global = reinterpret_cast(globalObject); - uint64_t time = Bun__readOriginTimer(global->bunVM()); + uint64_t time = Bun__readOriginTimer(bunVM(globalObject)); return JSValue::encode(jsNumber(time)); } JSC_DEFINE_HOST_FUNCTION(functionPathToFileURL, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { - auto& globalObject = *reinterpret_cast(lexicalGlobalObject); + auto& globalObject = *defaultGlobalObject(lexicalGlobalObject); auto& vm = globalObject.vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); auto pathValue = callFrame->argument(0); @@ -540,6 +572,7 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj SHA512_256 BunObject_getter_wrap_SHA512_256 DontDelete|PropertyCallback TOML BunObject_getter_wrap_TOML DontDelete|PropertyCallback Transpiler BunObject_getter_wrap_Transpiler DontDelete|PropertyCallback + embeddedFiles BunObject_getter_wrap_embeddedFiles DontDelete|PropertyCallback allocUnsafe BunObject_callback_allocUnsafe DontDelete|Function 1 argv BunObject_getter_wrap_argv DontDelete|PropertyCallback build BunObject_callback_build DontDelete|Function 1 @@ -548,14 +581,14 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj cwd BunObject_getter_wrap_cwd DontEnum|DontDelete|PropertyCallback deepEquals functionBunDeepEquals DontDelete|Function 2 deepMatch functionBunDeepMatch DontDelete|Function 2 - deflateSync BunObject_callback_deflateSync DontDelete|Function 1 + deflateSync BunObject_callback_deflateSync DontDelete|Function 1 dns constructDNSObject ReadOnly|DontDelete|PropertyCallback enableANSIColors BunObject_getter_wrap_enableANSIColors DontDelete|PropertyCallback env constructEnvObject ReadOnly|DontDelete|PropertyCallback escapeHTML functionBunEscapeHTML DontDelete|Function 2 - fetch Bun__fetch ReadOnly|DontDelete|Function 1 - file BunObject_callback_file DontDelete|Function 1 - fileURLToPath functionFileURLToPath DontDelete|Function 1 + fetch constructBunFetchObject ReadOnly|DontDelete|PropertyCallback + file BunObject_callback_file DontDelete|Function 1 + fileURLToPath functionFileURLToPath DontDelete|Function 1 gc BunObject_callback_gc DontDelete|Function 1 generateHeapSnapshot BunObject_callback_generateHeapSnapshot DontDelete|Function 1 gunzipSync BunObject_callback_gunzipSync DontDelete|Function 1 @@ -604,6 +637,7 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj version constructBunVersion ReadOnly|DontDelete|PropertyCallback which BunObject_callback_which DontDelete|Function 1 write BunObject_callback_write DontDelete|Function 1 + wipDevServerDoNotUseYet constructBunKit DontEnum|ReadOnly|DontDelete|PropertyCallback @end */ diff --git a/src/bun.js/bindings/BunObject.h b/src/bun.js/bindings/BunObject.h index f5c167e2fb6b0..2ddd9ab04a95c 100644 --- a/src/bun.js/bindings/BunObject.h +++ b/src/bun.js/bindings/BunObject.h @@ -12,5 +12,7 @@ JSC_DECLARE_HOST_FUNCTION(functionBunNanoseconds); JSC_DECLARE_HOST_FUNCTION(functionPathToFileURL); JSC_DECLARE_HOST_FUNCTION(functionFileURLToPath); +JSC::JSValue constructBunFetchObject(VM& vm, JSObject* bunObject); JSC::JSObject* createBunObject(VM& vm, JSObject* globalObject); + } diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp index d30185f781fe9..8570bce362b70 100644 --- a/src/bun.js/bindings/BunPlugin.cpp +++ b/src/bun.js/bindings/BunPlugin.cpp @@ -1,5 +1,6 @@ #include "BunPlugin.h" +#include "JavaScriptCore/JSCast.h" #include "headers-handwritten.h" #include "helpers.h" #include "ZigGlobalObject.h" @@ -133,7 +134,8 @@ static EncodedJSValue jsFunctionAppendVirtualModulePluginBody(JSC::JSGlobalObjec return JSValue::encode(jsUndefined()); } - Zig::GlobalObject* global = Zig::jsCast(globalObject); + Zig::GlobalObject* global = defaultGlobalObject(globalObject); + if (global->onLoadPlugins.virtualModules == nullptr) { global->onLoadPlugins.virtualModules = new BunPlugin::VirtualModuleMap; } @@ -200,7 +202,7 @@ static JSC::EncodedJSValue jsFunctionAppendOnResolvePluginBody(JSC::JSGlobalObje static JSC::EncodedJSValue jsFunctionAppendOnResolvePluginGlobal(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target) { - Zig::GlobalObject* global = Zig::jsCast(globalObject); + Zig::GlobalObject* global = defaultGlobalObject(globalObject); auto& plugins = global->onResolvePlugins; auto callback = Bun__onDidAppendPlugin; @@ -209,7 +211,7 @@ static JSC::EncodedJSValue jsFunctionAppendOnResolvePluginGlobal(JSC::JSGlobalOb static JSC::EncodedJSValue jsFunctionAppendOnLoadPluginGlobal(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target) { - Zig::GlobalObject* global = Zig::jsCast(globalObject); + Zig::GlobalObject* global = defaultGlobalObject(globalObject); auto& plugins = global->onLoadPlugins; auto callback = Bun__onDidAppendPlugin; @@ -251,20 +253,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionAppendOnResolvePluginBrowser, (JSC::JSGlobalO return jsFunctionAppendOnResolvePluginGlobal(globalObject, callframe, BunPluginTargetBrowser); } -extern "C" JSC::EncodedJSValue jsFunctionBunPluginClear(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe) -{ - Zig::GlobalObject* global = reinterpret_cast(globalObject); - global->onLoadPlugins.fileNamespace.clear(); - global->onResolvePlugins.fileNamespace.clear(); - global->onLoadPlugins.groups.clear(); - global->onResolvePlugins.namespaces.clear(); - - delete global->onLoadPlugins.virtualModules; - - return JSValue::encode(jsUndefined()); -} - -extern "C" JSC::EncodedJSValue setupBunPlugin(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target) +static inline JSC::EncodedJSValue setupBunPlugin(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target) { JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -276,24 +265,24 @@ extern "C" JSC::EncodedJSValue setupBunPlugin(JSC::JSGlobalObject* globalObject, JSC::JSObject* obj = callframe->uncheckedArgument(0).getObject(); if (!obj) { JSC::throwTypeError(globalObject, throwScope, "plugin needs an object as first argument"_s); - return JSValue::encode(jsUndefined()); } + RETURN_IF_EXCEPTION(throwScope, {}); JSC::JSValue setupFunctionValue = obj->getIfPropertyExists(globalObject, Identifier::fromString(vm, "setup"_s)); if (!setupFunctionValue || setupFunctionValue.isUndefinedOrNull() || !setupFunctionValue.isCell() || !setupFunctionValue.isCallable()) { JSC::throwTypeError(globalObject, throwScope, "plugin needs a setup() function"_s); - return JSValue::encode(jsUndefined()); } + RETURN_IF_EXCEPTION(throwScope, {}); if (JSValue targetValue = obj->getIfPropertyExists(globalObject, Identifier::fromString(vm, "target"_s))) { if (auto* targetJSString = targetValue.toStringOrNull(globalObject)) { - auto targetString = targetJSString->value(globalObject); + String targetString = targetJSString->value(globalObject); if (!(targetString == "node"_s || targetString == "bun"_s || targetString == "browser"_s)) { JSC::throwTypeError(globalObject, throwScope, "plugin target must be one of 'node', 'bun' or 'browser'"_s); - return JSValue::encode(jsUndefined()); } } } + RETURN_IF_EXCEPTION(throwScope, {}); JSObject* builderObject = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 4); @@ -343,11 +332,6 @@ extern "C" JSC::EncodedJSValue setupBunPlugin(JSC::JSGlobalObject* globalObject, RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); } -extern "C" JSC::EncodedJSValue jsFunctionBunPlugin(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe) -{ - return setupBunPlugin(globalObject, callframe, BunPluginTargetBun); -} - void BunPlugin::Group::append(JSC::VM& vm, JSC::RegExp* filter, JSC::JSFunction* func) { filters.append(JSC::Strong { vm, filter }); @@ -382,7 +366,7 @@ JSFunction* BunPlugin::Group::find(JSC::JSGlobalObject* globalObject, String& pa void BunPlugin::OnLoad::addModuleMock(JSC::VM& vm, const String& path, JSC::JSObject* mockObject) { - Zig::GlobalObject* globalObject = Zig::jsCast(mockObject->globalObject()); + Zig::GlobalObject* globalObject = defaultGlobalObject(mockObject->globalObject()); if (globalObject->onLoadPlugins.virtualModules == nullptr) { globalObject->onLoadPlugins.virtualModules = new BunPlugin::VirtualModuleMap; @@ -488,10 +472,11 @@ JSObject* JSModuleMock::executeOnce(JSC::JSGlobalObject* lexicalGlobalObject) } extern "C" JSC::EncodedJSValue Bun__resolveSyncWithSource(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, BunString* from, bool is_esm); -extern "C" EncodedJSValue JSMock__jsModuleMock(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callframe) +BUN_DECLARE_HOST_FUNCTION(JSMock__jsModuleMock); +extern "C" JSC_DEFINE_HOST_FUNCTION(JSMock__jsModuleMock, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) { JSC::VM& vm = lexicalGlobalObject->vm(); - Zig::GlobalObject* globalObject = jsDynamicCast(lexicalGlobalObject); + Zig::GlobalObject* globalObject = defaultGlobalObject(lexicalGlobalObject); auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!globalObject)) { scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "Cannot run mock from a different global context"_s)); @@ -612,44 +597,46 @@ extern "C" EncodedJSValue JSMock__jsModuleMock(JSC::JSGlobalObject* lexicalGloba if (JSValue entryValue = esm->get(globalObject, specifierString)) { removeFromESM = true; - - if (entryValue.isObject()) { - JSObject* entry = entryValue.getObject(); + JSObject* entry = entryValue ? entryValue.getObject() : nullptr; + if (entry) { if (JSValue moduleValue = entry->getIfPropertyExists(globalObject, Identifier::fromString(vm, String("module"_s)))) { + RETURN_IF_EXCEPTION(scope, {}); if (auto* mod = jsDynamicCast(moduleValue)) { JSC::JSModuleNamespaceObject* moduleNamespaceObject = mod->getModuleNamespace(globalObject); - JSValue exportsValue = getJSValue(); RETURN_IF_EXCEPTION(scope, {}); - removeFromESM = false; - - if (exportsValue.isObject()) { - // TODO: use fast path for property iteration - auto* object = exportsValue.getObject(); - JSC::PropertyNameArray names(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); - JSObject::getOwnPropertyNames(object, globalObject, names, DontEnumPropertiesMode::Exclude); + if (moduleNamespaceObject) { + JSValue exportsValue = getJSValue(); RETURN_IF_EXCEPTION(scope, {}); - - for (auto& name : names) { - // consistent with regular esm handling code - auto catchScope = DECLARE_CATCH_SCOPE(vm); - JSValue value = object->get(globalObject, name); - if (scope.exception()) { - scope.clearException(); - value = jsUndefined(); + auto* object = exportsValue.getObject(); + removeFromESM = false; + + if (object) { + JSC::PropertyNameArray names(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); + JSObject::getOwnPropertyNames(object, globalObject, names, DontEnumPropertiesMode::Exclude); + RETURN_IF_EXCEPTION(scope, {}); + + for (auto& name : names) { + // consistent with regular esm handling code + auto catchScope = DECLARE_CATCH_SCOPE(vm); + JSValue value = object->get(globalObject, name); + if (scope.exception()) { + scope.clearException(); + value = jsUndefined(); + } + moduleNamespaceObject->overrideExportValue(globalObject, name, value); } - moduleNamespaceObject->overrideExportValue(globalObject, name, value); - } - } else { - // if it's not an object, I guess we just set the default export? - moduleNamespaceObject->overrideExportValue(globalObject, vm.propertyNames->defaultKeyword, exportsValue); - } + } else { + // if it's not an object, I guess we just set the default export? + moduleNamespaceObject->overrideExportValue(globalObject, vm.propertyNames->defaultKeyword, exportsValue); + } - RETURN_IF_EXCEPTION(scope, {}); + RETURN_IF_EXCEPTION(scope, {}); - // TODO: do we need to handle intermediate loading state here? - // entry->putDirect(vm, Identifier::fromString(vm, String("evaluated"_s)), jsBoolean(true), 0); - // entry->putDirect(vm, Identifier::fromString(vm, String("state"_s)), jsNumber(JSC::JSModuleLoader::Status::Ready), 0); + // TODO: do we need to handle intermediate loading state here? + // entry->putDirect(vm, Identifier::fromString(vm, String("evaluated"_s)), jsBoolean(true), 0); + // entry->putDirect(vm, Identifier::fromString(vm, String("state"_s)), jsNumber(JSC::JSModuleLoader::Status::Ready), 0); + } } } } @@ -657,7 +644,7 @@ extern "C" EncodedJSValue JSMock__jsModuleMock(JSC::JSGlobalObject* lexicalGloba if (auto entryValue = globalObject->requireMap()->get(globalObject, specifierString)) { removeFromCJS = true; - if (auto* moduleObject = jsDynamicCast(entryValue)) { + if (auto* moduleObject = entryValue ? jsDynamicCast(entryValue) : nullptr) { JSValue exportsValue = getJSValue(); RETURN_IF_EXCEPTION(scope, {}); @@ -929,4 +916,22 @@ JSC::JSValue runVirtualModule(Zig::GlobalObject* globalObject, BunString* specif return fallback(); } -} // namespace Bun \ No newline at end of file +} // namespace Bun + +BUN_DEFINE_HOST_FUNCTION(jsFunctionBunPluginClear, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + Zig::GlobalObject* global = reinterpret_cast(globalObject); + global->onLoadPlugins.fileNamespace.clear(); + global->onResolvePlugins.fileNamespace.clear(); + global->onLoadPlugins.groups.clear(); + global->onResolvePlugins.namespaces.clear(); + + delete global->onLoadPlugins.virtualModules; + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +BUN_DEFINE_HOST_FUNCTION(jsFunctionBunPlugin, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + return Bun::setupBunPlugin(globalObject, callframe, BunPluginTargetBun); +} \ No newline at end of file diff --git a/src/bun.js/bindings/BunPlugin.h b/src/bun.js/bindings/BunPlugin.h index 41f1956cdefda..44a606234e754 100644 --- a/src/bun.js/bindings/BunPlugin.h +++ b/src/bun.js/bindings/BunPlugin.h @@ -6,8 +6,8 @@ #include #include "helpers.h" -extern "C" JSC_DECLARE_HOST_FUNCTION(jsFunctionBunPlugin); -extern "C" JSC_DECLARE_HOST_FUNCTION(jsFunctionBunPluginClear); +BUN_DECLARE_HOST_FUNCTION(jsFunctionBunPlugin); +BUN_DECLARE_HOST_FUNCTION(jsFunctionBunPluginClear); namespace Zig { diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 870e15a9591d5..e852e2d877401 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -8,6 +8,7 @@ #include "JavaScriptCore/JSCJSValue.h" #include "JavaScriptCore/JSCast.h" #include "JavaScriptCore/JSString.h" +#include "JavaScriptCore/MathCommon.h" #include "JavaScriptCore/Protect.h" #include "JavaScriptCore/PutPropertySlot.h" #include "ScriptExecutionContext.h" @@ -30,6 +31,10 @@ #include "wtf/text/ASCIILiteral.h" #include "wtf/text/OrdinalNumber.h" +#include "AsyncContextFrame.h" + +#include "napi_handle_scope.h" + #ifndef WIN32 #include #include @@ -77,7 +82,6 @@ namespace Bun { using namespace JSC; -#define REPORTED_NODE_VERSION "22.2.0" #define processObjectBindingCodeGenerator processObjectInternalsBindingCodeGenerator #define setProcessObjectInternalsMainModuleCodeGenerator processObjectInternalsSetMainModuleCodeGenerator #define setProcessObjectMainModuleCodeGenerator setMainModuleCodeGenerator @@ -105,24 +109,27 @@ JSC_DECLARE_CUSTOM_GETTER(Process_getTitle); JSC_DECLARE_CUSTOM_GETTER(Process_getPID); JSC_DECLARE_CUSTOM_GETTER(Process_getPPID); JSC_DECLARE_HOST_FUNCTION(Process_functionCwd); +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)); static bool processIsExiting = false; extern "C" uint8_t Bun__getExitCode(void*); extern "C" uint8_t Bun__setExitCode(void*, uint8_t); -extern "C" void* Bun__getVM(); -extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); +extern "C" bool Bun__closeChildIPC(JSGlobalObject*); + extern "C" bool Bun__GlobalObject__hasIPC(JSGlobalObject*); extern "C" bool Bun__ensureProcessIPCInitialized(JSGlobalObject*); extern "C" const char* Bun__githubURL; -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__Process__send); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__Process__disconnect); +BUN_DECLARE_HOST_FUNCTION(Bun__Process__send); + +extern "C" void Process__emitDisconnectEvent(Zig::GlobalObject* global); +extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValue value); static JSValue constructArch(VM& vm, JSObject* processObject) { #if CPU(X86_64) - return JSC::jsString(vm, makeAtomString("x64")); + return JSC::jsString(vm, makeAtomString("x64"_s)); #elif CPU(ARM64) - return JSC::jsString(vm, makeAtomString("arm64")); + return JSC::jsString(vm, makeAtomString("arm64"_s)); #else #error "Unknown architecture" #endif @@ -144,58 +151,64 @@ static JSValue constructPlatform(VM& vm, JSObject* processObject) static JSValue constructVersions(VM& vm, JSObject* processObject) { auto* globalObject = processObject->globalObject(); - JSC::JSObject* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 23); + JSC::JSObject* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 24); object->putDirect(vm, JSC::Identifier::fromString(vm, "node"_s), - JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(REPORTED_NODE_VERSION)))); + JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(ASCIILiteral::fromLiteralUnsafe(REPORTED_NODEJS_VERSION))))); object->putDirect( vm, JSC::Identifier::fromString(vm, "bun"_s), - JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(Bun__version + 1 /* remove "v" prefix */)))); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__version)).substring(1)))); object->putDirect(vm, JSC::Identifier::fromString(vm, "boringssl"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_boringssl))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_boringssl)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "openssl"_s), // https://github.com/oven-sh/bun/issues/7921 // BoringSSL is a fork of OpenSSL 1.1.0, so we can report OpenSSL 1.1.0 JSC::JSValue(JSC::jsString(vm, String("1.1.0"_s), 0))); object->putDirect(vm, JSC::Identifier::fromString(vm, "libarchive"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_libarchive))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_libarchive)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "mimalloc"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_mimalloc))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_mimalloc)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "picohttpparser"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_picohttpparser))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_picohttpparser)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "uwebsockets"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_uws))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_uws)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "webkit"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_webkit))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(BUN_WEBKIT_VERSION)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "zig"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zig))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_zig)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "zlib"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zlib))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_zlib)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "tinycc"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_tinycc))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_tinycc)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "lolhtml"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_lolhtml))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_lolhtml)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "ares"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_c_ares))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_c_ares)))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "libdeflate"_s), + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_libdeflate)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "usockets"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_usockets))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_usockets)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "lshpack"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_lshpack))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_lshpack)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "zstd"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zstd))), 0); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(Bun__versions_zstd)))), 0); object->putDirect(vm, JSC::Identifier::fromString(vm, "v8"_s), JSValue(JSC::jsString(vm, makeString("12.4.254.14-node.12"_s))), 0); #if OS(WINDOWS) - object->putDirect(vm, JSC::Identifier::fromString(vm, "uv"_s), JSValue(JSC::jsString(vm, makeString(uv_version_string()))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "uv"_s), JSValue(JSC::jsString(vm, String::fromLatin1(uv_version_string()))), 0); #else object->putDirect(vm, JSC::Identifier::fromString(vm, "uv"_s), JSValue(JSC::jsString(vm, makeString("1.48.0"_s))), 0); #endif object->putDirect(vm, JSC::Identifier::fromString(vm, "napi"_s), JSValue(JSC::jsString(vm, makeString("9"_s))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "icu"_s), JSValue(JSC::jsString(vm, makeString(U_ICU_VERSION))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "unicode"_s), JSValue(JSC::jsString(vm, makeString(U_UNICODE_VERSION))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "icu"_s), JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(U_ICU_VERSION)))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "unicode"_s), JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(U_UNICODE_VERSION)))), 0); +#define STRINGIFY_IMPL(x) #x +#define STRINGIFY(x) STRINGIFY_IMPL(x) object->putDirect(vm, JSC::Identifier::fromString(vm, "modules"_s), - JSC::JSValue(JSC::jsString(vm, makeAtomString("115")))); + JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(STRINGIFY(REPORTED_NODEJS_ABI_VERSION)))))); +#undef STRINGIFY +#undef STRINGIFY_IMPL return object; } @@ -206,7 +219,7 @@ static JSValue constructProcessReleaseObject(VM& vm, JSObject* processObject) auto* release = JSC::constructEmptyObject(globalObject); // SvelteKit compatibility hack - release->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, WTF::String("node"_s)), 0); + release->putDirect(vm, vm.propertyNames->name, jsString(vm, WTF::String("node"_s)), 0); release->putDirect(vm, Identifier::fromString(vm, "lts"_s), jsBoolean(false), 0); release->putDirect(vm, Identifier::fromString(vm, "sourceUrl"_s), jsString(vm, WTF::String(std::span { Bun__githubURL, strlen(Bun__githubURL) })), 0); @@ -286,7 +299,9 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, return JSC::JSValue::encode(JSC::JSValue {}); } - globalObject->pendingNapiModule = exports; + globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, moduleObject); + globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, exports); + Strong strongExports; if (exports.isCell()) { @@ -340,7 +355,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, LPWSTR messageBuffer = nullptr; size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); - WTF::String msg = makeString("LoadLibrary failed: ", WTF::StringView(std::span { (UCHAR*)messageBuffer, size })); + WTF::String msg = makeString("LoadLibrary failed: "_s, WTF::StringView(std::span { (UCHAR*)messageBuffer, size })); LocalFree(messageBuffer); #else WTF::String msg = WTF::String::fromUTF8(dlerror()); @@ -350,9 +365,10 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, } if (callCountAtStart != globalObject->napiModuleRegisterCallCount) { - JSValue resultValue = globalObject->pendingNapiModule; - globalObject->pendingNapiModule = JSValue {}; + JSValue resultValue = globalObject->m_pendingNapiModuleAndExports[0].get(); globalObject->napiModuleRegisterCallCount = 0; + globalObject->m_pendingNapiModuleAndExports[0].clear(); + globalObject->m_pendingNapiModuleAndExports[1].clear(); RETURN_IF_EXCEPTION(scope, {}); @@ -372,6 +388,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, #define dlsym GetProcAddress #endif + // TODO(@190n) look for node_register_module_vXYZ according to BuildOptions.reported_nodejs_version + // (bun/src/env.zig:36) and the table at https://github.com/nodejs/node/blob/main/doc/abi_version_registry.json napi_register_module_v1 = reinterpret_cast( dlsym(handle, "napi_register_module_v1")); @@ -390,11 +408,16 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, return JSC::JSValue::encode(JSC::JSValue {}); } + NapiHandleScope handleScope(globalObject); + EncodedJSValue exportsValue = JSC::JSValue::encode(exports); JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(globalObject, exportsValue)); RETURN_IF_EXCEPTION(scope, {}); + globalObject->m_pendingNapiModuleAndExports[0].clear(); + globalObject->m_pendingNapiModuleAndExports[1].clear(); + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/node_api.cc#L734-L742 // https://github.com/oven-sh/bun/issues/1288 if (!resultValue.isEmpty() && !scope.exception() && (!strongExports || resultValue != strongExports.get())) { @@ -424,7 +447,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionUmask, } if (!numberValue.isAnyInt()) { - throwRangeError(globalObject, throwScope, "The \"mask\" argument must be an integer"_s); + throwNodeRangeError(globalObject, throwScope, "The \"mask\" argument must be an integer"_s); return JSValue::encode({}); } @@ -435,7 +458,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionUmask, StringBuilder messageBuilder; messageBuilder.append("The \"mask\" value must be in range [0, 4294967295]. Received value: "_s); messageBuilder.append(int52ToString(vm, newUmask, 10)->getString(globalObject)); - throwRangeError(globalObject, throwScope, messageBuilder.toString()); + throwNodeRangeError(globalObject, throwScope, messageBuilder.toString()); return JSValue::encode({}); } @@ -465,14 +488,15 @@ extern "C" void Process__dispatchOnExit(Zig::GlobalObject* globalObject, uint8_t } auto* process = jsCast(globalObject->processObject()); + if (exitCode > 0) + process->m_isExitCodeObservable = true; dispatchExitInternal(globalObject, process, exitCode); } JSC_DEFINE_HOST_FUNCTION(Process_functionUptime, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) + (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { - Zig::GlobalObject* globalObject_ = reinterpret_cast(globalObject); - double now = static_cast(Bun__readOriginTimer(globalObject_->bunVM())); + double now = static_cast(Bun__readOriginTimer(bunVM(lexicalGlobalObject))); double result = (now / 1000000.0) / 1000.0; return JSC::JSValue::encode(JSC::jsNumber(result)); } @@ -488,17 +512,17 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionExit, RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::JSValue {})); exitCode = static_cast(extiCode32); + Bun__setExitCode(bunVM(globalObject), exitCode); } else if (!arg0.isUndefinedOrNull()) { throwTypeError(globalObject, throwScope, "The \"code\" argument must be an integer"_s); return JSC::JSValue::encode(JSC::JSValue {}); } else { - exitCode = Bun__getExitCode(Bun__getVM()); + exitCode = Bun__getExitCode(bunVM(globalObject)); } - auto* zigGlobal = jsDynamicCast(globalObject); - if (UNLIKELY(!zigGlobal)) { - zigGlobal = Bun__getDefaultGlobal(); - } + auto* zigGlobal = defaultGlobalObject(globalObject); + auto process = jsCast(zigGlobal->processObject()); + process->m_isExitCodeObservable = true; Process__dispatchOnExit(zigGlobal, exitCode); Bun__Process__exit(zigGlobal, exitCode); @@ -514,10 +538,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_setUncaughtExceptionCaptureCallback, throwTypeError(globalObject, throwScope, "The \"callback\" argument must be callable or null"_s); return JSC::JSValue::encode(JSC::JSValue {}); } - auto* zigGlobal = jsDynamicCast(globalObject); - if (UNLIKELY(!zigGlobal)) { - zigGlobal = Bun__getDefaultGlobal(); - } + auto* zigGlobal = defaultGlobalObject(globalObject); jsCast(zigGlobal->processObject())->setUncaughtExceptionCaptureCallback(arg0); return JSC::JSValue::encode(jsUndefined()); } @@ -525,10 +546,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_setUncaughtExceptionCaptureCallback, JSC_DEFINE_HOST_FUNCTION(Process_hasUncaughtExceptionCaptureCallback, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { - auto* zigGlobal = jsDynamicCast(globalObject); - if (UNLIKELY(!zigGlobal)) { - zigGlobal = Bun__getDefaultGlobal(); - } + auto* zigGlobal = defaultGlobalObject(globalObject); JSValue cb = jsCast(zigGlobal->processObject())->getUncaughtExceptionCaptureCallback(); if (cb.isEmpty() || !cb.isCell()) { return JSValue::encode(jsBoolean(false)); @@ -609,7 +627,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionHRTimeBigInt, JSC_DEFINE_HOST_FUNCTION(Process_functionChdir, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); ZigString str = ZigString { nullptr, 0 }; if (callFrame->argumentCount() > 0) { @@ -617,15 +636,11 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionChdir, } JSC::JSValue result = JSC::JSValue::decode(Bun__Process__setCwd(globalObject, &str)); - JSC::JSObject* obj = result.getObject(); - if (UNLIKELY(obj != nullptr && obj->isErrorInstance())) { - scope.throwException(globalObject, obj); - return JSValue::encode(JSC::jsUndefined()); - } - - scope.release(); + RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(result); + auto* processObject = jsCast(defaultGlobalObject(globalObject)->processObject()); + processObject->setCachedCwd(vm, result.toStringOrNull(globalObject)); + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(result)); } static HashMap* signalNameToNumberMap = nullptr; @@ -687,82 +702,82 @@ static void loadSignalNumberMap() signalNameToNumberMap->add(signalNames[9], SIGKILL); signalNameToNumberMap->add(signalNames[15], SIGTERM); #else - signalNameToNumberMap->add(signalNames[0], SIGHUP); - signalNameToNumberMap->add(signalNames[1], SIGINT); - signalNameToNumberMap->add(signalNames[2], SIGQUIT); - signalNameToNumberMap->add(signalNames[3], SIGILL); + signalNameToNumberMap->add(signalNames[0], SIGHUP); + signalNameToNumberMap->add(signalNames[1], SIGINT); + signalNameToNumberMap->add(signalNames[2], SIGQUIT); + signalNameToNumberMap->add(signalNames[3], SIGILL); #ifdef SIGTRAP - signalNameToNumberMap->add(signalNames[4], SIGTRAP); + signalNameToNumberMap->add(signalNames[4], SIGTRAP); #endif - signalNameToNumberMap->add(signalNames[5], SIGABRT); + signalNameToNumberMap->add(signalNames[5], SIGABRT); #ifdef SIGIOT - signalNameToNumberMap->add(signalNames[6], SIGIOT); + signalNameToNumberMap->add(signalNames[6], SIGIOT); #endif #ifdef SIGBUS - signalNameToNumberMap->add(signalNames[7], SIGBUS); + signalNameToNumberMap->add(signalNames[7], SIGBUS); #endif - signalNameToNumberMap->add(signalNames[8], SIGFPE); - signalNameToNumberMap->add(signalNames[9], SIGKILL); + signalNameToNumberMap->add(signalNames[8], SIGFPE); + signalNameToNumberMap->add(signalNames[9], SIGKILL); #ifdef SIGUSR1 - signalNameToNumberMap->add(signalNames[10], SIGUSR1); + signalNameToNumberMap->add(signalNames[10], SIGUSR1); #endif - signalNameToNumberMap->add(signalNames[11], SIGSEGV); + signalNameToNumberMap->add(signalNames[11], SIGSEGV); #ifdef SIGUSR2 - signalNameToNumberMap->add(signalNames[12], SIGUSR2); + signalNameToNumberMap->add(signalNames[12], SIGUSR2); #endif #ifdef SIGPIPE - signalNameToNumberMap->add(signalNames[13], SIGPIPE); + signalNameToNumberMap->add(signalNames[13], SIGPIPE); #endif #ifdef SIGALRM - signalNameToNumberMap->add(signalNames[14], SIGALRM); + signalNameToNumberMap->add(signalNames[14], SIGALRM); #endif - signalNameToNumberMap->add(signalNames[15], SIGTERM); + signalNameToNumberMap->add(signalNames[15], SIGTERM); #ifdef SIGCHLD - signalNameToNumberMap->add(signalNames[16], SIGCHLD); + signalNameToNumberMap->add(signalNames[16], SIGCHLD); #endif #ifdef SIGCONT - signalNameToNumberMap->add(signalNames[17], SIGCONT); + signalNameToNumberMap->add(signalNames[17], SIGCONT); #endif #ifdef SIGSTOP - signalNameToNumberMap->add(signalNames[18], SIGSTOP); + signalNameToNumberMap->add(signalNames[18], SIGSTOP); #endif #ifdef SIGTSTP - signalNameToNumberMap->add(signalNames[19], SIGTSTP); + signalNameToNumberMap->add(signalNames[19], SIGTSTP); #endif #ifdef SIGTTIN - signalNameToNumberMap->add(signalNames[20], SIGTTIN); + signalNameToNumberMap->add(signalNames[20], SIGTTIN); #endif #ifdef SIGTTOU - signalNameToNumberMap->add(signalNames[21], SIGTTOU); + signalNameToNumberMap->add(signalNames[21], SIGTTOU); #endif #ifdef SIGURG - signalNameToNumberMap->add(signalNames[22], SIGURG); + signalNameToNumberMap->add(signalNames[22], SIGURG); #endif #ifdef SIGXCPU - signalNameToNumberMap->add(signalNames[23], SIGXCPU); + signalNameToNumberMap->add(signalNames[23], SIGXCPU); #endif #ifdef SIGXFSZ - signalNameToNumberMap->add(signalNames[24], SIGXFSZ); + signalNameToNumberMap->add(signalNames[24], SIGXFSZ); #endif #ifdef SIGVTALRM - signalNameToNumberMap->add(signalNames[25], SIGVTALRM); + signalNameToNumberMap->add(signalNames[25], SIGVTALRM); #endif #ifdef SIGPROF - signalNameToNumberMap->add(signalNames[26], SIGPROF); + signalNameToNumberMap->add(signalNames[26], SIGPROF); #endif - signalNameToNumberMap->add(signalNames[27], SIGWINCH); + signalNameToNumberMap->add(signalNames[27], SIGWINCH); #ifdef SIGIO - signalNameToNumberMap->add(signalNames[28], SIGIO); + signalNameToNumberMap->add(signalNames[28], SIGIO); #endif #ifdef SIGINFO - signalNameToNumberMap->add(signalNames[29], SIGINFO); + signalNameToNumberMap->add(signalNames[29], SIGINFO); #endif #ifndef SIGINFO - signalNameToNumberMap->add(signalNames[29], 255); + signalNameToNumberMap->add(signalNames[29], 255); #endif #ifdef SIGSYS - signalNameToNumberMap->add(signalNames[30], SIGSYS); + signalNameToNumberMap->add(signalNames[30], SIGSYS); #endif #endif }); @@ -873,22 +888,33 @@ extern "C" int Bun__handleUnhandledRejection(JSC::JSGlobalObject* lexicalGlobalO } } +extern "C" void Bun__setChannelRef(GlobalObject* globalObject, bool enabled) +{ + auto process = jsCast(globalObject->processObject()); + process->wrapped().m_hasIPCRef = enabled; + + if (enabled) { + process->scriptExecutionContext()->refEventLoop(); + } else { + process->scriptExecutionContext()->unrefEventLoop(); + } +} + static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) { if (eventEmitter.scriptExecutionContext()->isMainThread()) { // IPC handlers - if (eventName.string() == "message"_s) { + if (eventName.string() == "message"_s || eventName.string() == "disconnect"_s) { + auto* global = jsCast(eventEmitter.scriptExecutionContext()->jsGlobalObject()); if (isAdded) { - auto* global = eventEmitter.scriptExecutionContext()->jsGlobalObject(); if (Bun__GlobalObject__hasIPC(global) && eventEmitter.listenerCount(eventName) == 1) { Bun__ensureProcessIPCInitialized(global); - eventEmitter.scriptExecutionContext()->refEventLoop(); - eventEmitter.m_hasIPCRef = true; + Bun__setChannelRef(global, true); } } else { - if (eventEmitter.listenerCount(eventName) == 0 && eventEmitter.m_hasIPCRef) { - eventEmitter.scriptExecutionContext()->unrefEventLoop(); + if (eventEmitter.listenerCount(eventName) == 0) { + Bun__setChannelRef(global, false); } } return; @@ -1069,7 +1095,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_emitWarning, (JSGlobalObject * lexicalGlobalObj return createError(globalObject, str); })(); - errorInstance->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, String("warn"_s)), JSC::PropertyAttribute::DontEnum | 0); + errorInstance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("warn"_s)), JSC::PropertyAttribute::DontEnum | 0); auto ident = Identifier::fromString(vm, "warning"_s); if (process->wrapped().hasEventListeners(ident)) { @@ -1092,9 +1118,13 @@ JSC_DEFINE_CUSTOM_GETTER(processExitCode, (JSC::JSGlobalObject * lexicalGlobalOb if (!process) { return JSValue::encode(jsUndefined()); } + if (!process->m_isExitCodeObservable) { + return JSValue::encode(jsUndefined()); + } return JSValue::encode(jsNumber(Bun__getExitCode(jsCast(process->globalObject())->bunVM()))); } + JSC_DEFINE_CUSTOM_SETTER(setProcessExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName)) { Process* process = jsDynamicCast(JSValue::decode(thisValue)); @@ -1112,6 +1142,7 @@ JSC_DEFINE_CUSTOM_SETTER(setProcessExitCode, (JSC::JSGlobalObject * lexicalGloba int exitCodeInt = exitCode.toInt32(lexicalGlobalObject) % 256; RETURN_IF_EXCEPTION(throwScope, false); + process->m_isExitCodeObservable = true; void* ptr = jsCast(process->globalObject())->bunVM(); Bun__setExitCode(ptr, static_cast(exitCodeInt)); @@ -1513,7 +1544,7 @@ static JSValue constructReportObjectComplete(VM& vm, Zig::GlobalObject* globalOb } header->putDirect(vm, JSC::Identifier::fromString(vm, "commandLine"_s), JSValue::decode(Bun__Process__getExecArgv(globalObject)), 0); - header->putDirect(vm, JSC::Identifier::fromString(vm, "nodejsVersion"_s), JSC::jsString(vm, String::fromLatin1(REPORTED_NODE_VERSION)), 0); + header->putDirect(vm, JSC::Identifier::fromString(vm, "nodejsVersion"_s), JSC::jsString(vm, String::fromLatin1(REPORTED_NODEJS_VERSION)), 0); header->putDirect(vm, JSC::Identifier::fromString(vm, "wordSize"_s), JSC::jsNumber(64), 0); header->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s), constructArch(vm, header), 0); header->putDirect(vm, JSC::Identifier::fromString(vm, "platform"_s), constructPlatform(vm, header), 0); @@ -1605,7 +1636,7 @@ static JSValue constructReportObjectComplete(VM& vm, Zig::GlobalObject* globalOb auto constructJavaScriptStack = [&]() -> JSC::JSValue { JSC::JSObject* javascriptStack = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 3); - javascriptStack->putDirect(vm, JSC::Identifier::fromString(vm, "message"_s), JSC::jsString(vm, String("Error [ERR_SYNTHETIC]: JavaScript Callstack"_s)), 0); + javascriptStack->putDirect(vm, vm.propertyNames->message, JSC::jsString(vm, String("Error [ERR_SYNTHETIC]: JavaScript Callstack"_s)), 0); // TODO: allow errors as an argument { @@ -1617,7 +1648,7 @@ static JSValue constructReportObjectComplete(VM& vm, Zig::GlobalObject* globalOb OrdinalNumber column = OrdinalNumber::beforeFirst(); WTF::String sourceURL; WTF::String stackProperty = Bun::formatStackTrace( - vm, globalObject, name, message, + vm, globalObject, globalObject, name, message, line, column, sourceURL, stackFrames, nullptr); @@ -1776,6 +1807,7 @@ static JSValue constructProcessConfigObject(VM& vm, JSObject* processObject) JSC::JSObject* variables = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 1); variables->putDirect(vm, JSC::Identifier::fromString(vm, "v8_enable_i8n_support"_s), JSC::jsNumber(1), 0); + variables->putDirect(vm, JSC::Identifier::fromString(vm, "enable_lto"_s), JSC::jsBoolean(false), 0); config->putDirect(vm, JSC::Identifier::fromString(vm, "target_defaults"_s), JSC::constructEmptyObject(globalObject), 0); config->putDirect(vm, JSC::Identifier::fromString(vm, "variables"_s), variables, 0); @@ -1804,7 +1836,7 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject); + JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; args.append(JSC::jsNumber(fd)); @@ -1841,14 +1873,12 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int static JSValue constructStdout(VM& vm, JSObject* processObject) { - auto* globalObject = Bun__getDefaultGlobal(); - return constructStdioWriteStream(globalObject, 1); + return constructStdioWriteStream(processObject->globalObject(), 1); } static JSValue constructStderr(VM& vm, JSObject* processObject) { - auto* globalObject = Bun__getDefaultGlobal(); - return constructStdioWriteStream(globalObject, 2); + return constructStdioWriteStream(processObject->globalObject(), 2); } #if OS(WINDOWS) @@ -1857,9 +1887,9 @@ static JSValue constructStderr(VM& vm, JSObject* processObject) static JSValue constructStdin(VM& vm, JSObject* processObject) { - auto* globalObject = Bun__getDefaultGlobal(); + auto* globalObject = processObject->globalObject(); auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject); + JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; args.append(JSC::jsNumber(STDIN_FILENO)); @@ -1891,6 +1921,29 @@ static JSValue constructProcessSend(VM& vm, JSObject* processObject) } } +JSC_DEFINE_HOST_FUNCTION(processDisonnectFinish, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + Bun__closeChildIPC(globalObject); + return JSC::JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(Bun__Process__disconnect, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto global = jsCast(globalObject); + + if (!Bun__GlobalObject__hasIPC(globalObject)) { + Process__emitErrorEvent(global, jsFunction_ERR_IPC_DISCONNECTED(globalObject, nullptr)); + return JSC::JSValue::encode(jsUndefined()); + } + + auto finishFn = JSC::JSFunction::create(vm, globalObject, 0, String("finish"_s), processDisonnectFinish, ImplementationVisibility::Public); + auto process = jsCast(global->processObject()); + + process->queueNextTick(vm, globalObject, finishFn); + return JSC::JSValue::encode(jsUndefined()); +} + static JSValue constructProcessDisconnect(VM& vm, JSObject* processObject) { auto* globalObject = processObject->globalObject(); @@ -1901,6 +1954,36 @@ static JSValue constructProcessDisconnect(VM& vm, JSObject* processObject) } } +static JSValue constructProcessChannel(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + if (Bun__GlobalObject__hasIPC(globalObject)) { + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSC::JSFunction* getControl = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetChannelCodeGenerator(vm), globalObject); + JSC::MarkedArgumentBuffer args; + JSC::CallData callData = JSC::getCallData(getControl); + + NakedPtr returnedException = nullptr; + auto result = JSC::call(globalObject, getControl, callData, globalObject->globalThis(), args, returnedException); + RETURN_IF_EXCEPTION(scope, {}); + + if (auto* exception = returnedException.get()) { +#if BUN_DEBUG + Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception); +#endif + scope.throwException(globalObject, exception->value()); + returnedException.clear(); + return {}; + } + + return result; + } else { + return jsUndefined(); + } +} + #if OS(WINDOWS) #define getpid _getpid #endif @@ -1950,7 +2033,7 @@ static JSValue constructBrowser(VM& vm, JSObject* processObject) static JSValue constructVersion(VM& vm, JSObject* processObject) { - return JSC::jsString(vm, makeString("v", REPORTED_NODE_VERSION)); + return JSC::jsString(vm, makeString("v"_s, ASCIILiteral::fromLiteralUnsafe(REPORTED_NODEJS_VERSION))); } static JSValue constructIsBun(VM& vm, JSObject* processObject) @@ -1960,7 +2043,7 @@ static JSValue constructIsBun(VM& vm, JSObject* processObject) static JSValue constructRevision(VM& vm, JSObject* processObject) { - return JSC::jsString(vm, makeAtomString(Bun__version_sha)); + return JSC::jsString(vm, makeAtomString(ASCIILiteral::fromLiteralUnsafe(Bun__version_sha))); } static JSValue constructEnv(VM& vm, JSObject* processObject) @@ -2141,13 +2224,10 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionReallyExit, (JSGlobalObject * globalObj throwTypeError(globalObject, throwScope, "The \"code\" argument must be an integer"_s); return JSC::JSValue::encode(JSC::JSValue {}); } else { - exitCode = Bun__getExitCode(Bun__getVM()); + exitCode = Bun__getExitCode(bunVM(globalObject)); } - auto* zigGlobal = jsDynamicCast(globalObject); - if (UNLIKELY(!zigGlobal)) { - zigGlobal = Bun__getDefaultGlobal(); - } + auto* zigGlobal = defaultGlobalObject(globalObject); Bun__Process__exit(zigGlobal, exitCode); return JSC::JSValue::encode(jsUndefined()); } @@ -2159,6 +2239,9 @@ void Process::visitChildrenImpl(JSCell* cell, Visitor& visitor) ASSERT_GC_OBJECT_INHERITS(thisObject, info()); Base::visitChildren(thisObject, visitor); visitor.append(thisObject->m_uncaughtExceptionCaptureCallback); + visitor.append(thisObject->m_nextTickFunction); + visitor.append(thisObject->m_cachedCwd); + thisObject->m_cpuUsageStructure.visit(visitor); thisObject->m_memoryUsageStructure.visit(visitor); thisObject->m_bindingUV.visit(visitor); @@ -2230,11 +2313,7 @@ static Process* getProcessObject(JSC::JSGlobalObject* lexicalGlobalObject, JSVal // Handle "var memoryUsage = process.memoryUsage; memoryUsage()" if (UNLIKELY(!process)) { // Handle calling this function from inside a node:vm - Zig::GlobalObject* zigGlobalObject = jsDynamicCast(lexicalGlobalObject); - - if (UNLIKELY(!zigGlobalObject)) { - zigGlobalObject = Bun__getDefaultGlobal(); - } + Zig::GlobalObject* zigGlobalObject = defaultGlobalObject(lexicalGlobalObject); return jsCast(zigGlobalObject->processObject()); } @@ -2283,12 +2362,12 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, if (callFrame->argumentCount() > 0) { JSValue comparatorValue = callFrame->argument(0); if (!comparatorValue.isUndefined()) { - if (UNLIKELY(!comparatorValue.isObject())) { + JSC::JSObject* comparator = comparatorValue.getObject(); + if (UNLIKELY(!comparator)) { throwTypeError(globalObject, throwScope, "Expected an object as the first argument"_s); return JSC::JSValue::encode(JSC::jsUndefined()); } - JSC::JSObject* comparator = comparatorValue.getObject(); JSValue userValue; JSValue systemValue; @@ -2304,17 +2383,27 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, } if (UNLIKELY(!userValue || !userValue.isNumber())) { - throwTypeError(globalObject, throwScope, "Expected a number for the user property"_s); + throwTypeError(globalObject, throwScope, "Expected a number for the 'user' property"_s); return JSC::JSValue::encode(JSC::jsUndefined()); } if (UNLIKELY(!systemValue || !systemValue.isNumber())) { - throwTypeError(globalObject, throwScope, "Expected a number for the system property"_s); + throwTypeError(globalObject, throwScope, "Expected a number for the 'system' property"_s); + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + double userComparator = userValue.toNumber(globalObject); + double systemComparator = systemValue.toNumber(globalObject); + + if (userComparator > JSC::maxSafeInteger() || userComparator < 0 || std::isnan(userComparator)) { + throwRangeError(globalObject, throwScope, "The 'user' property must be a number between 0 and 2^53"_s); return JSC::JSValue::encode(JSC::jsUndefined()); } - double userComparator = userValue.asNumber(); - double systemComparator = systemValue.asNumber(); + if (systemComparator > JSC::maxSafeInteger() || systemComparator < 0 || std::isnan(systemComparator)) { + throwRangeError(globalObject, throwScope, "The 'system' property must be a number between 0 and 2^53"_s); + return JSC::JSValue::encode(JSC::jsUndefined()); + } user -= userComparator; system -= systemComparator; @@ -2324,8 +2413,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, JSC::JSObject* result = JSC::constructEmptyObject(vm, cpuUsageStructure); RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); - result->putDirectOffset(vm, 0, JSC::jsNumber(user)); - result->putDirectOffset(vm, 1, JSC::jsNumber(system)); + result->putDirectOffset(vm, 0, JSC::jsDoubleNumber(user)); + result->putDirectOffset(vm, 1, JSC::jsDoubleNumber(system)); RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(result)); } @@ -2477,10 +2566,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionMemoryUsageRSS, JSC_DEFINE_HOST_FUNCTION(Process_functionOpenStdin, (JSGlobalObject * globalObject, CallFrame* callFrame)) { auto& vm = globalObject->vm(); - Zig::GlobalObject* global = jsDynamicCast(globalObject); - if (UNLIKELY(!global)) { - global = Bun__getDefaultGlobal(); - } + Zig::GlobalObject* global = defaultGlobalObject(globalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); if (JSValue stdinValue = global->processObject()->getIfPropertyExists(globalObject, Identifier::fromString(vm, "stdin"_s))) { @@ -2560,10 +2646,52 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionDrainMicrotaskQueue, (JSC::JSGlobalObject * g return JSValue::encode(jsUndefined()); } -static JSValue constructProcessNextTickFn(VM& vm, JSObject* processObject) +void Process::queueNextTick(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const ArgList& args) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + if (!this->m_nextTickFunction) { + this->get(globalObject, Identifier::fromString(vm, "nextTick"_s)); + RETURN_IF_EXCEPTION(scope, void()); + } + + ASSERT(!args.isEmpty()); + JSObject* nextTickFn = this->m_nextTickFunction.get(); + AsyncContextFrame::call(globalObject, nextTickFn, jsUndefined(), args); + RELEASE_AND_RETURN(scope, void()); +} + +void Process::queueNextTick(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSValue value) +{ + ASSERT_WITH_MESSAGE(value.isCallable(), "Must be a function for us to call"); + MarkedArgumentBuffer args; + if (value != 0) + args.append(value); + this->queueNextTick(vm, globalObject, args); +} + +void Process::queueNextTick(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSValue value, JSValue arg1) +{ + ASSERT_WITH_MESSAGE(value.isCallable(), "Must be a function for us to call"); + MarkedArgumentBuffer args; + if (value != 0) { + args.append(value); + if (arg1 != 0) { + args.append(arg1); + } + } + this->queueNextTick(vm, globalObject, args); +} + +extern "C" void Bun__Process__queueNextTick1(GlobalObject* globalObject, EncodedJSValue value, EncodedJSValue arg1) +{ + auto process = jsCast(globalObject->processObject()); + auto& vm = globalObject->vm(); + process->queueNextTick(vm, globalObject, JSValue::decode(value), JSValue::decode(arg1)); +} +JSC_DECLARE_HOST_FUNCTION(Bun__Process__queueNextTick1); + +JSValue Process::constructNextTickFn(JSC::VM& vm, Zig::GlobalObject* globalObject) { - JSGlobalObject* lexicalGlobalObject = processObject->globalObject(); - Zig::GlobalObject* globalObject = jsCast(lexicalGlobalObject); JSValue nextTickQueueObject; if (!globalObject->m_nextTickQueue) { nextTickQueueObject = Bun::JSNextTickQueue::create(globalObject); @@ -2572,15 +2700,27 @@ static JSValue constructProcessNextTickFn(VM& vm, JSObject* processObject) nextTickQueueObject = jsCast(globalObject->m_nextTickQueue.get()); } - JSC::JSFunction* initializer = JSC::JSFunction::create(vm, processObjectInternalsInitializeNextTickQueueCodeGenerator(vm), lexicalGlobalObject); + JSC::JSFunction* initializer = JSC::JSFunction::create(vm, globalObject, processObjectInternalsInitializeNextTickQueueCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; - args.append(processObject); + args.append(this); args.append(nextTickQueueObject); args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionDrainMicrotaskQueue, ImplementationVisibility::Private)); args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionReportUncaughtException, ImplementationVisibility::Private)); - return JSC::call(globalObject, initializer, JSC::getCallData(initializer), globalObject->globalThis(), args); + JSValue nextTickFunction = JSC::call(globalObject, initializer, JSC::getCallData(initializer), globalObject->globalThis(), args); + if (nextTickFunction && nextTickFunction.isObject()) { + this->m_nextTickFunction.set(vm, this, nextTickFunction.getObject()); + } + + return nextTickFunction; +} + +static JSValue constructProcessNextTickFn(VM& vm, JSObject* processObject) +{ + JSGlobalObject* lexicalGlobalObject = processObject->globalObject(); + Zig::GlobalObject* globalObject = jsCast(lexicalGlobalObject); + return jsCast(processObject)->constructNextTickFn(globalObject->vm(), globalObject); } static JSValue constructFeatures(VM& vm, JSObject* processObject) @@ -2638,7 +2778,7 @@ JSC_DEFINE_CUSTOM_SETTER(setProcessDebugPort, JSValue value = JSValue::decode(encodedValue); if (!value.isInt32AsAnyInt()) { - throwRangeError(globalObject, scope, "debugPort must be 0 or in range 1024 to 65535"_s); + throwNodeRangeError(globalObject, scope, "debugPort must be 0 or in range 1024 to 65535"_s); return false; } @@ -2646,7 +2786,7 @@ JSC_DEFINE_CUSTOM_SETTER(setProcessDebugPort, if (port != 0) { if (port < 1024 || port > 65535) { - throwRangeError(globalObject, scope, "debugPort must be 0 or in range 1024 to 65535"_s); + throwNodeRangeError(globalObject, scope, "debugPort must be 0 or in range 1024 to 65535"_s); return false; } } @@ -2695,18 +2835,33 @@ JSC_DEFINE_CUSTOM_SETTER(setProcessTitle, #endif } -JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +static inline JSValue getCachedCwd(JSC::JSGlobalObject* globalObject) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::JSValue result = JSC::JSValue::decode(Bun__Process__getCwd(globalObject)); - JSC::JSObject* obj = result.getObject(); - if (UNLIKELY(obj != nullptr && obj->isErrorInstance())) { - scope.throwException(globalObject, obj); - return JSValue::encode(JSC::jsUndefined()); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/bootstrap/switches/does_own_process_state.js#L142-L146 + auto* processObject = jsCast(defaultGlobalObject(globalObject)->processObject()); + if (auto* cached = processObject->cachedCwd()) { + return cached; } - return JSC::JSValue::encode(result); + auto cwd = Bun__Process__getCwd(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + JSString* cwdStr = jsCast(JSValue::decode(cwd)); + processObject->setCachedCwd(vm, cwdStr); + RELEASE_AND_RETURN(scope, cwdStr); +} + +extern "C" EncodedJSValue Process__getCachedCwd(JSC::JSGlobalObject* globalObject) +{ + return JSValue::encode(getCachedCwd(globalObject)); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, + (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + return JSValue::encode(getCachedCwd(globalObject)); } JSC_DEFINE_HOST_FUNCTION(Process_functionReallyKill, @@ -2714,6 +2869,11 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionReallyKill, { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + if (callFrame->argumentCount() < 2) { + throwVMError(globalObject, scope, "Not enough arguments"_s); + return {}; + } + int pid = callFrame->argument(0).toInt32(globalObject); RETURN_IF_EXCEPTION(scope, {}); @@ -2722,28 +2882,25 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionReallyKill, #if !OS(WINDOWS) int result = kill(pid, signal); + if (result < 0) + result = errno; #else int result = uv_kill(pid, signal); #endif - if (result < 0) { - throwSystemError(scope, globalObject, "kill"_s, errno); - } - - RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined())); + RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(result))); } + JSC_DEFINE_HOST_FUNCTION(Process_functionKill, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - int pid = callFrame->argument(0).toInt32(globalObject); RETURN_IF_EXCEPTION(scope, {}); if (pid < 0) { - throwRangeError(globalObject, scope, "pid must be a positive integer"_s); + throwNodeRangeError(globalObject, scope, "pid must be a positive integer"_s); return JSValue::encode(jsUndefined()); } - JSC::JSValue signalValue = callFrame->argument(1); int signal = SIGTERM; if (signalValue.isNumber()) { @@ -2755,25 +2912,42 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill, signal = num; RETURN_IF_EXCEPTION(scope, {}); } else { - throwRangeError(globalObject, scope, "Unknown signal name"_s); + throwNodeRangeError(globalObject, scope, "Unknown signal name"_s); return JSValue::encode(jsUndefined()); } - RETURN_IF_EXCEPTION(scope, {}); } else if (!signalValue.isUndefinedOrNull()) { throwTypeError(globalObject, scope, "signal must be a string or number"_s); return JSValue::encode(jsUndefined()); } -#if OS(WINDOWS) - int result = uv_kill(pid, signal); -#else - int result = kill(pid, signal); -#endif + auto global = jsCast(globalObject); + auto& vm = global->vm(); + JSValue _killFn = global->processObject()->get(globalObject, Identifier::fromString(vm, "_kill"_s)); + RETURN_IF_EXCEPTION(scope, {}); + if (!_killFn.isCallable()) { + throwTypeError(globalObject, scope, "process._kill is not a function"_s); + return JSValue::encode({}); + } - if (result < 0) { - throwSystemError(scope, globalObject, "kill"_s, errno); - return JSValue::encode(jsUndefined()); + JSC::MarkedArgumentBuffer args; + args.append(jsNumber(pid)); + args.append(jsNumber(signal)); + JSC::CallData callData = JSC::getCallData(_killFn); + + NakedPtr returnedException = nullptr; + auto result = JSC::call(globalObject, _killFn, callData, globalObject->globalThis(), args, returnedException); + RETURN_IF_EXCEPTION(scope, {}); + + if (auto* exception = returnedException.get()) { + scope.throwException(globalObject, exception->value()); + returnedException.clear(); + return {}; + } + auto err = result.toInt32(globalObject); + if (err) { + throwSystemError(scope, globalObject, "kill"_s, err); + return {}; } return JSValue::encode(jsBoolean(true)); @@ -2784,7 +2958,7 @@ extern "C" void Process__emitMessageEvent(Zig::GlobalObject* global, EncodedJSVa auto* process = static_cast(global->processObject()); auto& vm = global->vm(); - auto ident = Identifier::fromString(vm, "message"_s); + auto ident = vm.propertyNames->message; if (process->wrapped().hasEventListeners(ident)) { JSC::MarkedArgumentBuffer args; args.append(JSValue::decode(value)); @@ -2803,6 +2977,17 @@ extern "C" void Process__emitDisconnectEvent(Zig::GlobalObject* global) } } +extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValue value) +{ + auto* process = static_cast(global->processObject()); + auto& vm = global->vm(); + if (process->wrapped().hasEventListeners(vm.propertyNames->error)) { + JSC::MarkedArgumentBuffer args; + args.append(JSValue::decode(value)); + process->wrapped().emit(vm.propertyNames->error, args); + } +} + /* Source for Process.lut.h @begin processObjectTable abort Process_functionAbort Function 1 @@ -2814,6 +2999,7 @@ extern "C" void Process__emitDisconnectEvent(Zig::GlobalObject* global) binding Process_functionBinding Function 1 browser constructBrowser PropertyCallback chdir Process_functionChdir Function 1 + channel constructProcessChannel PropertyCallback config constructProcessConfigObject PropertyCallback connected processConnected CustomAccessor constrainedMemory Process_functionConstrainedMemory Function 0 @@ -2906,6 +3092,7 @@ void Process::finishCreation(JSC::VM& vm) }); putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("process"_s)), 0); + putDirect(vm, Identifier::fromString(vm, "_exiting"_s), jsBoolean(false), 0); } } // namespace Bun diff --git a/src/bun.js/bindings/BunProcess.h b/src/bun.js/bindings/BunProcess.h index 653f4d54a4fb6..718369852521b 100644 --- a/src/bun.js/bindings/BunProcess.h +++ b/src/bun.js/bindings/BunProcess.h @@ -6,6 +6,10 @@ #include "BunClientData.h" #include "JSEventEmitter.h" +namespace Zig { +class GlobalObject; +} + namespace Bun { // TODO: find a better place for this @@ -21,6 +25,9 @@ class Process : public WebCore::JSEventEmitter { LazyProperty m_bindingUV; LazyProperty m_bindingNatives; WriteBarrier m_uncaughtExceptionCaptureCallback; + WriteBarrier m_nextTickFunction; + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/bootstrap/switches/does_own_process_state.js#L113-L116 + WriteBarrier m_cachedCwd; public: Process(JSC::Structure* structure, WebCore::JSDOMGlobalObject& globalObject, Ref&& impl) @@ -37,8 +44,18 @@ class Process : public WebCore::JSEventEmitter { ~Process(); + bool m_isExitCodeObservable = false; + static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + JSValue constructNextTickFn(JSC::VM& vm, Zig::GlobalObject* globalObject); + void queueNextTick(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const ArgList& args); + void queueNextTick(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSValue); + void queueNextTick(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSValue, JSValue); + + JSString* cachedCwd() { return m_cachedCwd.get(); } + void setCachedCwd(JSC::VM& vm, JSString* cwd) { m_cachedCwd.set(vm, this, cwd); } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { @@ -71,11 +88,13 @@ class Process : public WebCore::JSEventEmitter { void finishCreation(JSC::VM& vm); - inline void setUncaughtExceptionCaptureCallback(JSC::JSValue callback) { + inline void setUncaughtExceptionCaptureCallback(JSC::JSValue callback) + { m_uncaughtExceptionCaptureCallback.set(vm(), this, callback); } - inline JSC::JSValue getUncaughtExceptionCaptureCallback() { + inline JSC::JSValue getUncaughtExceptionCaptureCallback() + { return m_uncaughtExceptionCaptureCallback.get(); } diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index bf8013ee7309a..8afcbb1ff164b 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -7,7 +7,7 @@ #include "JavaScriptCore/JSCJSValue.h" #include "JavaScriptCore/PutPropertySlot.h" -#include "simdutf.h" +#include "wtf/SIMDUTF.h" #include "JSDOMURL.h" #include "DOMURL.h" #include "ZigGlobalObject.h" @@ -61,6 +61,14 @@ extern "C" bool BunString__fromJS(JSC::JSGlobalObject* globalObject, JSC::Encode return bunString->tag != BunStringTag::Dead; } +extern "C" bool BunString__fromJSRef(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue encodedValue, BunString* bunString) +{ + + JSC::JSValue value = JSC::JSValue::decode(encodedValue); + *bunString = Bun::toStringRef(globalObject, value); + return bunString->tag != BunStringTag::Dead; +} + extern "C" BunString BunString__createAtom(const char* bytes, size_t length) { ASSERT(simdutf::validate_ascii(bytes, length)); @@ -197,7 +205,8 @@ BunString toStringRef(WTF::StringImpl* wtfString) return { BunStringTag::WTFStringImpl, { .wtf = wtfString } }; } -BunString toStringView(StringView view) { +BunString toStringView(StringView view) +{ return { BunStringTag::ZigString, { .zig = toZigString(view) } @@ -215,16 +224,22 @@ extern "C" BunString BunString__fromUTF16Unitialized(size_t length) { ASSERT(length > 0); UChar* ptr; - auto impl = WTF::StringImpl::createUninitialized(length, ptr); - return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; + auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr); + if (UNLIKELY(!impl)) { + return { .tag = BunStringTag::Dead }; + } + return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } }; } extern "C" BunString BunString__fromLatin1Unitialized(size_t length) { ASSERT(length > 0); LChar* ptr; - auto impl = WTF::StringImpl::createUninitialized(length, ptr); - return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; + auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr); + if (UNLIKELY(!impl)) { + return { .tag = BunStringTag::Dead }; + } + return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } }; } extern "C" BunString BunString__fromUTF8(const char* bytes, size_t length) @@ -233,13 +248,19 @@ extern "C" BunString BunString__fromUTF8(const char* bytes, size_t length) if (simdutf::validate_utf8(bytes, length)) { size_t u16Length = simdutf::utf16_length_from_utf8(bytes, length); UChar* ptr; - auto impl = WTF::StringImpl::createUninitialized(static_cast(u16Length), ptr); + auto impl = WTF::StringImpl::tryCreateUninitialized(static_cast(u16Length), ptr); + if (UNLIKELY(!impl)) { + return { .tag = BunStringTag::Dead }; + } RELEASE_ASSERT(simdutf::convert_utf8_to_utf16(bytes, length, ptr) == u16Length); impl->ref(); - return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; + return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } }; } auto str = WTF::String::fromUTF8ReplacingInvalidSequences(std::span { reinterpret_cast(bytes), length }); + if (UNLIKELY(str.isNull())) { + return { .tag = BunStringTag::Dead }; + } str.impl()->ref(); return Bun::toString(str); } @@ -247,7 +268,14 @@ extern "C" BunString BunString__fromUTF8(const char* bytes, size_t length) extern "C" BunString BunString__fromLatin1(const char* bytes, size_t length) { ASSERT(length > 0); - return { BunStringTag::WTFStringImpl, { .wtf = &WTF::StringImpl::create(std::span { bytes, length }).leakRef() } }; + LChar* ptr; + auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr); + if (UNLIKELY(!impl)) { + return { .tag = BunStringTag::Dead }; + } + memcpy(ptr, bytes, length); + + return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } }; } extern "C" BunString BunString__fromUTF16ToLatin1(const char16_t* bytes, size_t length) @@ -256,20 +284,26 @@ extern "C" BunString BunString__fromUTF16ToLatin1(const char16_t* bytes, size_t ASSERT_WITH_MESSAGE(simdutf::validate_utf16le(bytes, length), "This function only accepts ascii UTF16 strings"); size_t outLength = simdutf::latin1_length_from_utf16(length); LChar* ptr = nullptr; - auto impl = WTF::StringImpl::createUninitialized(outLength, ptr); - if (!ptr) { + auto impl = WTF::StringImpl::tryCreateUninitialized(outLength, ptr); + if (UNLIKELY(!impl)) { return { BunStringTag::Dead }; } size_t latin1_length = simdutf::convert_valid_utf16le_to_latin1(bytes, length, reinterpret_cast(ptr)); ASSERT_WITH_MESSAGE(latin1_length == outLength, "Failed to convert UTF16 to Latin1"); - return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; + return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } }; } extern "C" BunString BunString__fromUTF16(const char16_t* bytes, size_t length) { ASSERT(length > 0); - return { BunStringTag::WTFStringImpl, { .wtf = &WTF::StringImpl::create(std::span { bytes, length }).leakRef() } }; + UChar* ptr; + auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr); + if (UNLIKELY(!impl)) { + return { .tag = BunStringTag::Dead }; + } + memcpy(ptr, bytes, length * sizeof(char16_t)); + return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } }; } extern "C" BunString BunString__fromBytes(const char* bytes, size_t length) @@ -282,6 +316,15 @@ extern "C" BunString BunString__fromBytes(const char* bytes, size_t length) return BunString__fromUTF8(bytes, length); } +extern "C" BunString BunString__createStaticExternal(const char* bytes, size_t length, bool isLatin1) +{ + Ref impl = isLatin1 ? WTF::ExternalStringImpl::createStatic({ reinterpret_cast(bytes), length }) : + + WTF::ExternalStringImpl::createStatic({ reinterpret_cast(bytes), length }); + + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + extern "C" BunString BunString__createExternal(const char* bytes, size_t length, bool isLatin1, void* ctx, void (*callback)(void* arg0, void* arg1, size_t arg2)) { Ref impl = isLatin1 ? WTF::ExternalStringImpl::create({ reinterpret_cast(bytes), length }, ctx, callback) : @@ -295,12 +338,15 @@ extern "C" JSC::EncodedJSValue BunString__toJSON( JSC::JSGlobalObject* globalObject, BunString* bunString) { + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::JSValue result = JSC::JSONParse(globalObject, bunString->toWTFString()); - if (!result) { - result = JSC::JSValue(JSC::createSyntaxError(globalObject, "Failed to parse JSON"_s)); + if (!result && !scope.exception()) { + scope.throwException(globalObject, createSyntaxError(globalObject, "Failed to parse JSON"_s)); } + RETURN_IF_EXCEPTION(scope, {}); + return JSC::JSValue::encode(result); } diff --git a/src/bun.js/bindings/BunWorkerGlobalScope.cpp b/src/bun.js/bindings/BunWorkerGlobalScope.cpp index f78111633885e..3bac99269b094 100644 --- a/src/bun.js/bindings/BunWorkerGlobalScope.cpp +++ b/src/bun.js/bindings/BunWorkerGlobalScope.cpp @@ -5,17 +5,17 @@ namespace WebCore { -WTF_MAKE_ISO_ALLOCATED_IMPL(GlobalScope); +WTF_MAKE_ISO_ALLOCATED_IMPL(WorkerGlobalScope); -MessagePortChannelProvider& GlobalScope::messagePortChannelProvider() +MessagePortChannelProvider& WorkerGlobalScope::messagePortChannelProvider() { return *reinterpret_cast(&MessagePortChannelProviderImpl::singleton()); } -void GlobalScope::onDidChangeListenerImpl(EventTarget& self, const AtomString& eventType, OnDidChangeListenerKind kind) +void WorkerGlobalScope::onDidChangeListenerImpl(EventTarget& self, const AtomString& eventType, OnDidChangeListenerKind kind) { if (eventType == eventNames().messageEvent) { - auto& global = static_cast(self); + auto& global = static_cast(self); switch (kind) { case Add: if (global.m_messageEventCount == 0) { diff --git a/src/bun.js/bindings/BunWorkerGlobalScope.h b/src/bun.js/bindings/BunWorkerGlobalScope.h index e1ac3636c679b..eb9a2bac8a371 100644 --- a/src/bun.js/bindings/BunWorkerGlobalScope.h +++ b/src/bun.js/bindings/BunWorkerGlobalScope.h @@ -15,15 +15,15 @@ namespace WebCore { class MessagePortChannelProvider; class MessagePortChannelProviderImpl; -class GlobalScope : public RefCounted, public EventTargetWithInlineData { - WTF_MAKE_ISO_ALLOCATED(GlobalScope); +class WorkerGlobalScope : public RefCounted, public EventTargetWithInlineData { + WTF_MAKE_ISO_ALLOCATED(WorkerGlobalScope); - uint32_t m_messageEventCount{0}; + uint32_t m_messageEventCount { 0 }; static void onDidChangeListenerImpl(EventTarget&, const AtomString&, OnDidChangeListenerKind); public: - GlobalScope(ScriptExecutionContext* context) + WorkerGlobalScope(ScriptExecutionContext* context) : EventTargetWithInlineData() , m_context(context) { @@ -33,12 +33,12 @@ class GlobalScope : public RefCounted, public EventTargetWithInline using RefCounted::deref; using RefCounted::ref; - static Ref create(ScriptExecutionContext* context) + static Ref create(ScriptExecutionContext* context) { - return adoptRef(*new GlobalScope(context)); + return adoptRef(*new WorkerGlobalScope(context)); } - ~GlobalScope() = default; + ~WorkerGlobalScope() = default; EventTargetInterface eventTargetInterface() const final { return EventTargetInterface::DOMWindowEventTargetInterfaceType; } ScriptExecutionContext* scriptExecutionContext() const final { return m_context; } diff --git a/src/bun.js/bindings/CPUFeatures.cpp b/src/bun.js/bindings/CPUFeatures.cpp new file mode 100644 index 0000000000000..861de32dc1be4 --- /dev/null +++ b/src/bun.js/bindings/CPUFeatures.cpp @@ -0,0 +1,110 @@ +#include "root.h" + +enum class X86CPUFeature : uint8_t { + sse42 = 1, + popcnt = 2, + avx = 3, + avx2 = 4, + avx512 = 5, +}; + +enum class AArch64CPUFeature : uint8_t { + neon = 1, + fp = 2, + aes = 3, + crc32 = 4, + atomics = 5, + sve = 6, +}; + +#if CPU(X86_64) + +#if OS(WINDOWS) + +#include + +#endif + +static uint8_t x86_cpu_features() +{ + uint8_t features = 0; + +#if OS(WINDOWS) + if (IsProcessorFeaturePresent(PF_SSE4_2_INSTRUCTIONS_AVAILABLE)) + features |= 1 << static_cast(X86CPUFeature::sse42); + + if (IsProcessorFeaturePresent(PF_AVX_INSTRUCTIONS_AVAILABLE)) + features |= 1 << static_cast(X86CPUFeature::avx); + + if (IsProcessorFeaturePresent(PF_AVX2_INSTRUCTIONS_AVAILABLE)) + features |= 1 << static_cast(X86CPUFeature::avx2); + + if (IsProcessorFeaturePresent(PF_AVX512F_INSTRUCTIONS_AVAILABLE)) + features |= 1 << static_cast(X86CPUFeature::avx512); + +#else + +#if __has_builtin(__builtin_cpu_supports) + __builtin_cpu_init(); + + if (__builtin_cpu_supports("sse4.2")) + features |= 1 << static_cast(X86CPUFeature::sse42); + if (__builtin_cpu_supports("popcnt")) + features |= 1 << static_cast(X86CPUFeature::popcnt); + if (__builtin_cpu_supports("avx")) + features |= 1 << static_cast(X86CPUFeature::avx); + if (__builtin_cpu_supports("avx2")) + features |= 1 << static_cast(X86CPUFeature::avx2); + if (__builtin_cpu_supports("avx512f")) + features |= 1 << static_cast(X86CPUFeature::avx512); +#endif + +#endif + + return features; +} + +#endif + +#if CPU(ARM64) + +static uint8_t aarch64_cpu_features() +{ + uint8_t features = 0; + +#if OS(WINDOWS) +#pragma error "TODO: Implement AArch64 CPU features for Windows" +#endif + +#if __has_builtin(__builtin_cpu_supports) + __builtin_cpu_init(); + + if (__builtin_cpu_supports("neon")) + features |= 1 << static_cast(AArch64CPUFeature::neon); + if (__builtin_cpu_supports("crypto")) + features |= 1 << static_cast(AArch64CPUFeature::fp); + if (__builtin_cpu_supports("aes")) + features |= 1 << static_cast(AArch64CPUFeature::aes); + if (__builtin_cpu_supports("crc32")) + features |= 1 << static_cast(AArch64CPUFeature::crc32); + if (__builtin_cpu_supports("atomics")) + features |= 1 << static_cast(AArch64CPUFeature::atomics); + if (__builtin_cpu_supports("sve")) + features |= 1 << static_cast(AArch64CPUFeature::sve); +#endif + + return features; +} + +#endif + +extern "C" uint8_t bun_cpu_features() +{ +#if CPU(X86_64) + return x86_cpu_features(); +#elif CPU(ARM64) + return aarch64_cpu_features(); +#else + return 0; +#endif +} \ No newline at end of file diff --git a/src/bun.js/bindings/CPUFeatures.zig b/src/bun.js/bindings/CPUFeatures.zig new file mode 100644 index 0000000000000..a5f6f444912d7 --- /dev/null +++ b/src/bun.js/bindings/CPUFeatures.zig @@ -0,0 +1,89 @@ +const bun = @import("root").bun; +const std = @import("std"); + +fn Impl(comptime T: type) type { + return struct { + pub fn format(this: T, comptime _: []const u8, _: anytype, writer: anytype) !void { + var is_first = true; + inline for (comptime std.meta.fieldNames(T)) |fieldName| { + if (comptime bun.strings.eqlComptime(fieldName, "padding") or bun.strings.eqlComptime(fieldName, "none")) + continue; + + const value = @field(this, fieldName); + if (value) { + if (!is_first) + try writer.writeAll(" "); + is_first = false; + try writer.writeAll(fieldName); + } + } + } + + pub fn isEmpty(this: T) bool { + return @as(u8, @bitCast(this)) == 0; + } + + pub fn get() T { + const this: T = @bitCast(bun_cpu_features()); + + // sanity check + assert(this.none == false and this.padding == 0); + + if (bun.Environment.isX64) { + bun.analytics.Features.no_avx += @as(usize, @intFromBool(!this.avx)); + bun.analytics.Features.no_avx2 += @as(usize, @intFromBool(!this.avx2)); + } + + return this; + } + }; +} + +const X86CPUFeatures = packed struct(u8) { + none: bool = false, + + sse42: bool = false, + popcnt: bool = false, + avx: bool = false, + avx2: bool = false, + avx512: bool = false, + + padding: u2 = 0, + + pub usingnamespace Impl(@This()); +}; +const AArch64CPUFeatures = packed struct(u8) { + none: bool = false, + + neon: bool = false, + fp: bool = false, + aes: bool = false, + crc32: bool = false, + atomics: bool = false, + sve: bool = false, + + padding: u1 = 0, + + pub usingnamespace Impl(@This()); +}; + +pub const CPUFeatures = if (bun.Environment.isX64) + X86CPUFeatures +else if (bun.Environment.isAarch64) + AArch64CPUFeatures +else + struct { + pub fn get() @This() { + return .{}; + } + + pub fn format(_: @This(), comptime _: []const u8, _: anytype, _: anytype) !void {} + + pub fn isEmpty(_: @This()) bool { + return true; + } + }; + +extern "C" fn bun_cpu_features() u8; + +const assert = bun.debugAssert; diff --git a/src/bun.js/bindings/CallSite.cpp b/src/bun.js/bindings/CallSite.cpp index 85a41a17be3ec..f1a51812655a5 100644 --- a/src/bun.js/bindings/CallSite.cpp +++ b/src/bun.js/bindings/CallSite.cpp @@ -126,11 +126,21 @@ void CallSite::formatAsString(JSC::VM& vm, JSC::JSGlobalObject* globalObject, WT if (isNative()) { sb.append("native"_s); } else { - sb.append(mySourceURL->getString(globalObject)); - sb.append(":"_s); - sb.append(myLineNumber->getString(globalObject)); - sb.append(":"_s); - sb.append(myColumnNumber->getString(globalObject)); + if (mySourceURL->length() == 0) { + sb.append("unknown"_s); + } else { + sb.append(mySourceURL->getString(globalObject)); + } + + if (myLineNumber->length() > 0 && myColumnNumber->length() > 0) { + sb.append(":"_s); + sb.append(myLineNumber->getString(globalObject)); + sb.append(":"_s); + sb.append(myColumnNumber->getString(globalObject)); + } else if (myLineNumber->length() > 0) { + sb.append(":"_s); + sb.append(myLineNumber->getString(globalObject)); + } } sb.append(")"_s); } diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index 3ed8339213122..5fa1d281de884 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -30,6 +30,8 @@ */ #include "headers.h" + +#include "JavaScriptCore/Synchronousness.h" #include "JavaScriptCore/JSCast.h" #include #include "root.h" @@ -72,6 +74,7 @@ #include "PathInlines.h" #include "wtf/NakedPtr.h" #include "wtf/URL.h" +#include "wtf/text/StringImpl.h" extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const BunString*); @@ -152,7 +155,6 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj JSValue fnValue = JSC::evaluate(globalObject, code, jsUndefined(), exception); if (UNLIKELY(exception.get() || fnValue.isEmpty())) { - return false; } @@ -171,6 +173,13 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj args.append(Zig::ImportMetaObject::create(globalObject, filename)); } + // Clear the source code as early as possible. + code = {}; + + // Call the CommonJS module wrapper function. + // + // fn(exports, require, module, __filename, __dirname) { /* code */ }(exports, require, module, __filename, __dirname) + // JSC::call(globalObject, fn, callData, moduleObject, args, exception); return exception.get() == nullptr; @@ -290,7 +299,7 @@ RequireFunctionPrototype* RequireFunctionPrototype::create( RequireFunctionPrototype* prototype = new (NotNull, JSC::allocateCell(vm)) RequireFunctionPrototype(vm, structure); prototype->finishCreation(vm); - prototype->putDirect(vm, builtinNames(vm).resolvePublicName(), jsCast(globalObject)->requireResolveFunctionUnbound(), 0); + prototype->putDirect(vm, vm.propertyNames->resolve, jsCast(globalObject)->requireResolveFunctionUnbound(), 0); return prototype; } @@ -299,20 +308,22 @@ void RequireFunctionPrototype::finishCreation(JSC::VM& vm) { Base::finishCreation(vm); ASSERT(inherits(info())); + auto* globalObject = this->globalObject(); reifyStaticProperties(vm, info(), RequireFunctionPrototypeValues, *this); JSC::JSFunction* requireDotMainFunction = JSFunction::create( vm, + globalObject, moduleMainCodeGenerator(vm), - globalObject()->globalScope()); + globalObject->globalScope()); this->putDirectAccessor( - globalObject(), + globalObject, JSC::Identifier::fromString(vm, "main"_s), - JSC::GetterSetter::create(vm, globalObject(), requireDotMainFunction, requireDotMainFunction), + JSC::GetterSetter::create(vm, globalObject, requireDotMainFunction, requireDotMainFunction), PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | 0); - auto extensions = constructEmptyObject(globalObject()); + auto extensions = constructEmptyObject(globalObject); extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".js"_s), jsBoolean(true), 0); extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".json"_s), jsBoolean(true), 0); extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".node"_s), jsBoolean(true), 0); @@ -352,9 +363,15 @@ JSC_DEFINE_CUSTOM_GETTER(getterParent, (JSC::JSGlobalObject * globalObject, JSC: if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } - auto v = thisObject->m_parent.get(); - if (v) - return JSValue::encode(thisObject->m_parent.get()); + + if (thisObject->m_overridenParent) { + return JSValue::encode(thisObject->m_overridenParent.get()); + } + + if (thisObject->m_parent) { + auto* parent = thisObject->m_parent.get(); + return JSValue::encode(parent); + } // initialize parent by checking if it is the main module. we do this lazily because most people // dont need `module.parent` and creating commonjs module records is done a ton. @@ -363,12 +380,11 @@ JSC_DEFINE_CUSTOM_GETTER(getterParent, (JSC::JSGlobalObject * globalObject, JSC: auto id = idValue->value(globalObject); auto idStr = Bun::toString(id); if (Bun__isBunMain(globalObject, &idStr)) { - thisObject->m_parent.set(globalObject->vm(), thisObject, jsNull()); + thisObject->m_overridenParent.set(globalObject->vm(), thisObject, jsNull()); return JSValue::encode(jsNull()); } } - thisObject->m_parent.set(globalObject->vm(), thisObject, jsUndefined()); return JSValue::encode(jsUndefined()); } @@ -454,7 +470,15 @@ JSC_DEFINE_CUSTOM_SETTER(setterParent, if (!thisObject) return false; - thisObject->m_parent.set(globalObject->vm(), thisObject, JSValue::decode(value)); + JSValue decodedValue = JSValue::decode(value); + + if (auto* parent = jsDynamicCast(decodedValue)) { + thisObject->m_parent = parent; + thisObject->m_overridenParent.clear(); + } else { + thisObject->m_parent = {}; + thisObject->m_overridenParent.set(globalObject->vm(), thisObject, JSValue::decode(value)); + } return true; } @@ -640,7 +664,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionCreateCommonJSModule, (JSGlobalObject * globa { RELEASE_ASSERT(callframe->argumentCount() == 4); - auto id = callframe->uncheckedArgument(0).toWTFString(globalObject); + auto id = callframe->uncheckedArgument(0).toString(globalObject); JSValue object = callframe->uncheckedArgument(1); JSValue hasEvaluated = callframe->uncheckedArgument(2); ASSERT(hasEvaluated.isBoolean()); @@ -651,15 +675,14 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionCreateCommonJSModule, (JSGlobalObject * globa JSCommonJSModule* JSCommonJSModule::create( Zig::GlobalObject* globalObject, - const WTF::String& key, + JSC::JSString* requireMapKey, JSValue exportsObject, bool hasEvaluated, JSValue parent) { auto& vm = globalObject->vm(); - JSString* requireMapKey = JSC::jsStringWithCache(vm, key); - - auto index = key.reverseFind(PLATFORM_SEP, key.length()); + auto key = requireMapKey->value(globalObject); + auto index = key->reverseFind(PLATFORM_SEP, key->length()); JSString* dirname; if (index != WTF::notFound) { @@ -679,11 +702,31 @@ JSCommonJSModule* JSCommonJSModule::create( exportsObject, 0); out->hasEvaluated = hasEvaluated; - out->m_parent.set(vm, out, parent); + if (parent && parent.isCell()) { + if (auto* parentModule = jsDynamicCast(parent)) { + out->m_parent = JSC::Weak(parentModule); + } else { + out->m_overridenParent.set(vm, out, parent); + } + } else if (parent) { + out->m_overridenParent.set(vm, out, parent); + } return out; } +JSCommonJSModule* JSCommonJSModule::create( + Zig::GlobalObject* globalObject, + const WTF::String& key, + JSValue exportsObject, + bool hasEvaluated, + JSValue parent) +{ + auto& vm = globalObject->vm(); + auto* requireMapKey = JSC::jsStringWithCache(vm, key); + return JSCommonJSModule::create(globalObject, requireMapKey, exportsObject, hasEvaluated, parent); +} + void JSCommonJSModule::destroy(JSC::JSCell* cell) { static_cast(cell)->JSCommonJSModule::~JSCommonJSModule(); @@ -771,12 +814,13 @@ void populateESMExports( if (canPerformFastEnumeration(structure)) { exports->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { auto key = entry.key(); - if (key->isSymbol() || entry.attributes() & PropertyAttribute::DontEnum || key == esModuleMarker) + if (key->isSymbol() || key == esModuleMarker) return true; needsToAssignDefault = needsToAssignDefault && key != vm.propertyNames->defaultKeyword; JSValue value = exports->getDirect(entry.offset()); + exportNames.append(Identifier::fromUid(vm, key)); exportValues.append(value); return true; @@ -801,6 +845,14 @@ void populateESMExports( if (!exports->getPropertySlot(globalObject, property, slot)) continue; + // Allow DontEnum properties which are not getter/setters + // https://github.com/oven-sh/bun/issues/4432 + if (slot.attributes() & PropertyAttribute::DontEnum) { + if (!(slot.isValue() || slot.isCustom())) { + continue; + } + } + exportNames.append(property); JSValue getterResult = slot.getValue(globalObject, property); @@ -821,7 +873,7 @@ void populateESMExports( } else if (canPerformFastEnumeration(structure)) { exports->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { auto key = entry.key(); - if (key->isSymbol() || entry.attributes() & PropertyAttribute::DontEnum || key == vm.propertyNames->defaultKeyword) + if (key->isSymbol() || key == vm.propertyNames->defaultKeyword) return true; JSValue value = exports->getDirect(entry.offset()); @@ -832,7 +884,7 @@ void populateESMExports( }); } else { JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude); - exports->methodTable()->getOwnPropertyNames(exports, globalObject, properties, DontEnumPropertiesMode::Exclude); + exports->methodTable()->getOwnPropertyNames(exports, globalObject, properties, DontEnumPropertiesMode::Include); if (catchScope.exception()) { catchScope.clearExceptionExceptTermination(); return; @@ -850,6 +902,14 @@ void populateESMExports( if (!exports->getPropertySlot(globalObject, property, slot)) continue; + if (slot.attributes() & PropertyAttribute::DontEnum) { + // Allow DontEnum properties which are not getter/setters + // https://github.com/oven-sh/bun/issues/4432 + if (!(slot.isValue() || slot.isCustom())) { + continue; + } + } + exportNames.append(property); JSValue getterResult = slot.getValue(globalObject, property); @@ -908,6 +968,7 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_filename); visitor.append(thisObject->m_dirname); visitor.append(thisObject->m_paths); + visitor.append(thisObject->m_overridenParent); } DEFINE_VISIT_CHILDREN(JSCommonJSModule); @@ -1014,7 +1075,6 @@ bool JSCommonJSModule::evaluate( this->sourceCode = JSC::SourceCode(WTFMove(sourceProvider)); WTF::NakedPtr exception; - evaluateCommonJSModuleOnce(vm, globalObject, this, this->m_dirname.get(), this->m_filename.get(), exception); if (exception.get()) { diff --git a/src/bun.js/bindings/CommonJSModuleRecord.h b/src/bun.js/bindings/CommonJSModuleRecord.h index 8e8459836c8f7..d3f0fde00a1da 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.h +++ b/src/bun.js/bindings/CommonJSModuleRecord.h @@ -1,6 +1,8 @@ #pragma once -#include "JavaScriptCore/JSGlobalObject.h" #include "root.h" + +#include "JavaScriptCore/JSGlobalObject.h" +#include "JavaScriptCore/JSString.h" #include "headers-handwritten.h" #include "wtf/NakedPtr.h" @@ -35,7 +37,19 @@ class JSCommonJSModule final : public JSC::JSDestructibleObject { mutable JSC::WriteBarrier m_filename; mutable JSC::WriteBarrier m_dirname; mutable JSC::WriteBarrier m_paths; - mutable JSC::WriteBarrier m_parent; + + // Visited by the GC. When the module is assigned a non-JSCommonJSModule + // parent, it is assigned to this field. + // + // module.parent = parent; + // + mutable JSC::WriteBarrier m_overridenParent; + + // Not visited by the GC. + // When the module is assigned a JSCommonJSModule parent, it is assigned to this field. + // This is the normal state. + JSC::Weak m_parent {}; + bool ignoreESModuleAnnotation { false }; JSC::SourceCode sourceCode = JSC::SourceCode(); @@ -70,6 +84,11 @@ class JSCommonJSModule final : public JSC::JSDestructibleObject { const WTF::String& key, JSValue exportsObject, bool hasEvaluated, JSValue parent); + static JSCommonJSModule* create( + Zig::GlobalObject* globalObject, + JSC::JSString* key, + JSValue exportsObject, bool hasEvaluated, JSValue parent); + static JSCommonJSModule* create( Zig::GlobalObject* globalObject, const WTF::String& key, diff --git a/src/bun.js/bindings/DOMFormData.cpp b/src/bun.js/bindings/DOMFormData.cpp index 026ba0317edfa..456fe5da54fdf 100644 --- a/src/bun.js/bindings/DOMFormData.cpp +++ b/src/bun.js/bindings/DOMFormData.cpp @@ -30,10 +30,13 @@ #include "config.h" #include "DOMFormData.h" +#include "wtf/DebugHeap.h" #include namespace WebCore { +DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(DOMFormData); + DOMFormData::DOMFormData(ScriptExecutionContext* context) : ContextDestructionObserver(context) { diff --git a/src/bun.js/bindings/DOMFormData.h b/src/bun.js/bindings/DOMFormData.h index 8ca84a29afc83..447be4e927566 100644 --- a/src/bun.js/bindings/DOMFormData.h +++ b/src/bun.js/bindings/DOMFormData.h @@ -35,13 +35,19 @@ #include #include #include "blob.h" + namespace WebCore { +class ScriptExecutionContext; + template class ExceptionOr; class HTMLElement; class HTMLFormElement; +DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(DOMFormData); class DOMFormData : public RefCounted, public ContextDestructionObserver { + WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DOMFormData); + public: using FormDataEntryValue = std::variant>; @@ -81,6 +87,7 @@ class DOMFormData : public RefCounted, public ContextDestructionObs size_t m_index { 0 }; }; Iterator createIterator() { return Iterator { *this }; } + Iterator createIterator(const ScriptExecutionContext* context) { return Iterator { *this }; } private: // explicit DOMFormData(ScriptExecutionContext*, const PAL::TextEncoding& = PAL::UTF8Encoding()); diff --git a/src/bun.js/bindings/DOMURL.cpp b/src/bun.js/bindings/DOMURL.cpp index 638111df9dc00..7c59c949501e0 100644 --- a/src/bun.js/bindings/DOMURL.cpp +++ b/src/bun.js/bindings/DOMURL.cpp @@ -65,7 +65,7 @@ ExceptionOr> DOMURL::create(const String& url) { URL completeURL { url }; if (!completeURL.isValid()) - return Exception { TypeError, makeString(redact(url), " cannot be parsed as a URL.") }; + return Exception { InvalidURLError, makeString(redact(url), " cannot be parsed as a URL."_s) }; return adoptRef(*new DOMURL(WTFMove(completeURL))); } @@ -74,7 +74,7 @@ ExceptionOr> DOMURL::create(const String& url, const URL& base) ASSERT(base.isValid() || base.isNull()); URL completeURL { base, url }; if (!completeURL.isValid()) - return Exception { TypeError, makeString(redact(url), " cannot be parsed as a URL.") }; + return Exception { InvalidURLError, makeString(redact(url), " cannot be parsed as a URL."_s) }; return adoptRef(*new DOMURL(WTFMove(completeURL))); } @@ -82,7 +82,7 @@ ExceptionOr> DOMURL::create(const String& url, const String& base) { URL baseURL { base }; if (!base.isNull() && !baseURL.isValid()) - return Exception { TypeError, makeString(redact(url), " cannot be parsed as a URL against "_s, redact(base)) }; + return Exception { InvalidURLError, makeString(redact(url), " cannot be parsed as a URL against "_s, redact(base)) }; return create(url, baseURL); } @@ -114,7 +114,7 @@ ExceptionOr DOMURL::setHref(const String& url) URL completeURL { URL {}, url }; if (!completeURL.isValid()) { - return Exception { TypeError, makeString(redact(url), " cannot be parsed as a URL.") }; + return Exception { InvalidURLError, makeString(redact(url), " cannot be parsed as a URL."_s) }; } m_url = WTFMove(completeURL); if (m_searchParams) diff --git a/src/bun.js/bindings/DeleteCallbackDataTask.h b/src/bun.js/bindings/DeleteCallbackDataTask.h new file mode 100644 index 0000000000000..4c3a5b05d3e98 --- /dev/null +++ b/src/bun.js/bindings/DeleteCallbackDataTask.h @@ -0,0 +1,16 @@ +#include "root.h" +#include "EventLoopTask.h" + +namespace WebCore { +class DeleteCallbackDataTask : public EventLoopTask { +public: + template + explicit DeleteCallbackDataTask(CallbackDataType* data) + : EventLoopTask(EventLoopTask::CleanupTask, [data](ScriptExecutionContext&) mutable { + delete data; + }) + { + } +}; + +} \ No newline at end of file diff --git a/src/bun.js/bindings/DoubleFormatter.cpp b/src/bun.js/bindings/DoubleFormatter.cpp index 019476f0f1b94..c1a94d55bfa54 100644 --- a/src/bun.js/bindings/DoubleFormatter.cpp +++ b/src/bun.js/bindings/DoubleFormatter.cpp @@ -1,11 +1,21 @@ #include "root.h" #include "wtf/dtoa.h" +#include "wtf/text/StringView.h" +#include "JavaScriptCore/JSGlobalObjectFunctions.h" #include /// Must be called with a buffer of exactly 124 /// Find the length by scanning for the 0 -extern "C" void WTF__dtoa(char* buf_124_bytes, double number) +extern "C" size_t WTF__dtoa(char* buf_124_bytes, double number) { NumberToStringBuffer& buf = *reinterpret_cast(buf_124_bytes); - WTF::numberToString(number, buf); + return WTF::numberToStringAndSize(number, buf); +} + +/// This is the equivalent of the unary '+' operator on a JS string +/// See https://262.ecma-international.org/14.0/#sec-stringtonumber +/// Grammar: https://262.ecma-international.org/14.0/#prod-StringNumericLiteral +extern "C" double JSC__jsToNumber(char* latin1_ptr, size_t len) +{ + return JSC::jsToNumber(WTF::StringView(latin1_ptr, len, true)); } diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp new file mode 100644 index 0000000000000..a962c516df069 --- /dev/null +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -0,0 +1,586 @@ + +#include "root.h" + +#include "ZigGlobalObject.h" +#include "DOMException.h" +#include "JavaScriptCore/Error.h" +#include "JavaScriptCore/ErrorType.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "JavaScriptCore/WriteBarrier.h" +#include "root.h" +#include "headers-handwritten.h" +#include "BunClientData.h" +#include "helpers.h" +#include "JavaScriptCore/JSCJSValue.h" +#include "JavaScriptCore/ErrorInstance.h" +#include "JavaScriptCore/JSString.h" +#include "JavaScriptCore/JSType.h" +#include "JavaScriptCore/Symbol.h" +#include "wtf/Assertions.h" +#include "wtf/text/ASCIIFastPath.h" +#include "wtf/text/ASCIILiteral.h" +#include "wtf/text/MakeString.h" +#include "wtf/text/WTFString.h" +#include "AbortSignal.h" +#include "JavaScriptCore/ErrorInstanceInlines.h" +#include "JavaScriptCore/JSInternalFieldObjectImplInlines.h" +#include "JSDOMException.h" + +#include "ErrorCode.h" + +static JSC::JSObject* createErrorPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code, bool isDOMExceptionPrototype = false) +{ + JSC::JSObject* prototype; + + // Inherit from DOMException + // But preserve the error.stack property. + if (isDOMExceptionPrototype) { + auto* domGlobalObject = defaultGlobalObject(globalObject); + prototype = JSC::constructEmptyObject(globalObject, WebCore::JSDOMException::prototype(vm, *domGlobalObject)); + } else { + switch (type) { + case JSC::ErrorType::TypeError: + prototype = JSC::constructEmptyObject(globalObject, globalObject->m_typeErrorStructure.prototype(globalObject)); + break; + case JSC::ErrorType::RangeError: + prototype = JSC::constructEmptyObject(globalObject, globalObject->m_rangeErrorStructure.prototype(globalObject)); + break; + case JSC::ErrorType::Error: + prototype = JSC::constructEmptyObject(globalObject, globalObject->errorPrototype()); + break; + default: { + RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("TODO: Add support for more error types"); + break; + } + } + } + + prototype->putDirect(vm, vm.propertyNames->name, jsString(vm, String(name)), 0); + prototype->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), jsString(vm, String(code)), 0); + + return prototype; +} + +extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value); +extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE_static(JSC::JSGlobalObject* globalObject, const ZigString* val_arg_name, const ZigString* val_expected_type, JSC::EncodedJSValue val_actual_value); +extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3); +extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS_static(JSC::JSGlobalObject* globalObject, const ZigString* arg1, const ZigString* arg2, const ZigString* arg3); +extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject); + +// clang-format on + +#define EXPECT_ARG_COUNT(count__) \ + do { \ + auto argCount = callFrame->argumentCount(); \ + if (argCount < count__) { \ + JSC::throwTypeError(globalObject, scope, "requires " #count__ " arguments"_s); \ + return {}; \ + } \ + } while (false) + +namespace Bun { + +using namespace JSC; + +#include "ErrorCode+Data.h" + +const ClassInfo ErrorCodeCache::s_info = { "ErrorCodeCache"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ErrorCodeCache) }; + +ErrorCodeCache::ErrorCodeCache(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +template +void ErrorCodeCache::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +DEFINE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE, ErrorCodeCache); + +Structure* ErrorCodeCache::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + return Structure::create(vm, globalObject, jsNull(), TypeInfo(InternalFieldTupleType, StructureFlags), info(), 0, 0); +} + +ErrorCodeCache* ErrorCodeCache::create(VM& vm, Structure* structure) +{ + ErrorCodeCache* object = new (NotNull, allocateCell(vm)) ErrorCodeCache(vm, structure); + object->finishCreation(vm); + return object; +} + +void ErrorCodeCache::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + for (unsigned i = 0; i < NODE_ERROR_COUNT; i++) { + this->internalField(i).clear(); + } +} + +static ErrorCodeCache* errorCache(Zig::GlobalObject* globalObject) +{ + return static_cast(globalObject->nodeErrorCache()); +} + +// clang-format on +static Structure* createErrorStructure(JSC::VM& vm, JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code, bool isDOMExceptionPrototype = false) +{ + auto* prototype = createErrorPrototype(vm, globalObject, type, name, code, isDOMExceptionPrototype); + return ErrorInstance::createStructure(vm, globalObject, prototype); +} + +JSObject* ErrorCodeCache::createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options) +{ + auto* cache = errorCache(globalObject); + const auto& data = errors[static_cast(code)]; + if (!cache->internalField(static_cast(code))) { + auto* structure = createErrorStructure(vm, globalObject, data.type, data.name, data.code, code == ErrorCode::ABORT_ERR); + cache->internalField(static_cast(code)).set(vm, cache, structure); + } + + auto* structure = jsCast(cache->internalField(static_cast(code)).get()); + return JSC::ErrorInstance::create(globalObject, structure, message, options, nullptr, JSC::RuntimeType::TypeNothing, data.type, true); +} + +JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, const String& message) +{ + return errorCache(globalObject)->createError(vm, globalObject, code, jsString(vm, message), jsUndefined()); +} + +JSObject* createError(VM& vm, JSC::JSGlobalObject* globalObject, ErrorCode code, JSValue message) +{ + if (auto* zigGlobalObject = jsDynamicCast(globalObject)) + return createError(vm, zigGlobalObject, code, message, jsUndefined()); + + auto* structure = createErrorStructure(vm, globalObject, errors[static_cast(code)].type, errors[static_cast(code)].name, errors[static_cast(code)].code); + return JSC::ErrorInstance::create(globalObject, structure, message, jsUndefined(), nullptr, JSC::RuntimeType::TypeNothing, errors[static_cast(code)].type, true); +} + +JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options) +{ + return errorCache(globalObject)->createError(vm, globalObject, code, message, options); +} + +JSObject* createError(JSC::JSGlobalObject* globalObject, ErrorCode code, const String& message) +{ + auto& vm = globalObject->vm(); + return createError(vm, globalObject, code, jsString(vm, message)); +} + +JSObject* createError(Zig::JSGlobalObject* globalObject, ErrorCode code, JSC::JSValue message) +{ + auto& vm = globalObject->vm(); + return createError(vm, globalObject, code, message); +} + +WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) +{ + ASSERT(!arg.isEmpty()); + if (!arg.isCell()) + return arg.toWTFStringForConsole(globalObject); + + auto cell = arg.asCell(); + switch (cell->type()) { + case JSC::JSType::StringType: { + return arg.toWTFStringForConsole(globalObject); + } + case JSC::JSType::SymbolType: { + + auto symbol = jsCast(cell); + auto result = symbol->tryGetDescriptiveString(); + if (result.has_value()) + return result.value(); + return "Symbol"_s; + } + case JSC::JSType::InternalFunctionType: + case JSC::JSType::JSFunctionType: { + auto& vm = globalObject->vm(); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + auto name = JSC::getCalculatedDisplayName(vm, cell->getObject()); + if (catchScope.exception()) { + catchScope.clearException(); + name = "Function"_s; + } + + if (!name.isNull() && name.length() > 0) { + return makeString("[Function: "_s, name, ']'); + } + + return "Function"_s; + break; + } + + default: { + break; + } + } + + return arg.toWTFStringForConsole(globalObject); +} + +namespace Message { + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, const StringView& expected_type, JSValue actual_value) +{ + + auto actual_value_string = JSValueToStringSafe(globalObject, actual_value); + RETURN_IF_EXCEPTION(scope, {}); + + return makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received: "_s, actual_value_string); +} + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, ArgList expected_types, JSValue actual_value) +{ + WTF::StringBuilder result; + + auto actual_value_string = JSValueToStringSafe(globalObject, actual_value); + RETURN_IF_EXCEPTION(scope, {}); + + result.append("The \""_s, arg_name, "\" argument must be of type "_s); + + unsigned length = expected_types.size(); + if (length == 1) { + result.append(expected_types.at(0).toWTFString(globalObject)); + } else if (length == 2) { + result.append(expected_types.at(0).toWTFString(globalObject)); + result.append(" or "_s); + result.append(expected_types.at(1).toWTFString(globalObject)); + } else { + for (unsigned i = 0; i < length - 1; i++) { + if (i > 0) + result.append(", "_s); + JSValue expected_type = expected_types.at(i); + result.append(expected_type.toWTFString(globalObject)); + } + result.append(" or "_s); + result.append(expected_types.at(length - 1).toWTFString(globalObject)); + } + + result.append(". Received: "_s, actual_value_string); + + return result.toString(); +} + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const ZigString* arg_name_string, const ZigString* expected_type_string, JSValue actual_value) +{ + auto arg_name = std::span(arg_name_string->ptr, arg_name_string->len); + ASSERT(WTF::charactersAreAllASCII(arg_name)); + + auto expected_type = std::span(expected_type_string->ptr, expected_type_string->len); + ASSERT(WTF::charactersAreAllASCII(expected_type)); + + return ERR_INVALID_ARG_TYPE(scope, globalObject, arg_name, expected_type, actual_value); +} + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue val_arg_name, JSValue val_expected_type, JSValue val_actual_value) +{ + + auto arg_name = val_arg_name.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto expected_type = val_expected_type.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + return ERR_INVALID_ARG_TYPE(scope, globalObject, arg_name, expected_type, val_actual_value); +} + +WTF::String ERR_OUT_OF_RANGE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue val_arg_name, JSValue val_range, JSValue val_input) +{ + auto arg_name = val_arg_name.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto range = val_range.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto input = JSValueToStringSafe(globalObject, val_input); + RETURN_IF_EXCEPTION(scope, {}); + + return makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received: \""_s, input, '"'); +} +} + +static JSC::JSValue ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue arg0, JSValue arg1, JSValue arg2) +{ + + if (auto* array = jsDynamicCast(arg1)) { + const WTF::String argName = arg0.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + MarkedArgumentBuffer expected_types; + for (unsigned i = 0, length = array->length(); i < length; i++) { + expected_types.append(array->getDirectIndex(globalObject, i)); + RETURN_IF_EXCEPTION(scope, {}); + } + + const auto msg = Bun::Message::ERR_INVALID_ARG_TYPE(scope, globalObject, argName, expected_types, arg2); + return createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, msg); + } + + const auto msg = Bun::Message::ERR_INVALID_ARG_TYPE(scope, globalObject, arg0, arg1, arg2); + return createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, msg); +} + +extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto message = Message::ERR_INVALID_ARG_TYPE(scope, globalObject, JSValue::decode(val_arg_name), JSValue::decode(val_expected_type), JSValue::decode(val_actual_value)); + RETURN_IF_EXCEPTION(scope, {}); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); +} +extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE_static(JSC::JSGlobalObject* globalObject, const ZigString* val_arg_name, const ZigString* val_expected_type, JSC::EncodedJSValue val_actual_value) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + WTF::String message = Message::ERR_INVALID_ARG_TYPE(scope, globalObject, val_arg_name, val_expected_type, JSValue::decode(val_actual_value)); + RETURN_IF_EXCEPTION(scope, {}); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + EXPECT_ARG_COUNT(3); + + auto message = Message::ERR_OUT_OF_RANGE(scope, globalObject, callFrame->argument(0), callFrame->argument(1), callFrame->argument(2)); + RETURN_IF_EXCEPTION(scope, {}); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_OUT_OF_RANGE, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_IPC_DISCONNECTED, "IPC channel is already disconnected"_s)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_SERVER_NOT_RUNNING, "Server is not running."_s)); +} + +extern "C" JSC::EncodedJSValue Bun__createErrorWithCode(JSC::JSGlobalObject* globalObject, ErrorCode code, BunString* message) +{ + return JSValue::encode(createError(globalObject, code, message->toWTFString(BunString::ZeroCopy))); +} + +extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (arg1 == 0) { + JSC::throwTypeError(globalObject, scope, "requires at least 1 argument"_s); + return {}; + } + + auto name1 = JSValue::decode(arg1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (arg2 == 0) { + // 1 arg name passed + auto message = makeString("The \""_s, name1, "\" argument must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); + } + + auto name2 = JSValue::decode(arg2).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (arg3 == 0) { + // 2 arg names passed + auto message = makeString("The \""_s, name1, "\" and \""_s, name2, "\" arguments must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); + } + + auto name3 = JSValue::decode(arg3).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + // 3 arg names passed + auto message = makeString("The \""_s, name1, "\", \""_s, name2, "\", and \""_s, name3, "\" arguments must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); +} +extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS_static(JSC::JSGlobalObject* globalObject, const ZigString* arg1, const ZigString* arg2, const ZigString* arg3) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (arg1 == nullptr) { + JSC::throwTypeError(globalObject, scope, "requires at least 1 argument"_s); + return {}; + } + + auto name1 = std::span(arg1->ptr, arg1->len); + ASSERT(WTF::charactersAreAllASCII(name1)); + + if (arg2 == nullptr) { + // 1 arg name passed + auto message = makeString("The \""_s, name1, "\" argument must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); + } + + auto name2 = std::span(arg2->ptr, arg2->len); + ASSERT(WTF::charactersAreAllASCII(name2)); + + if (arg3 == nullptr) { + // 2 arg names passed + auto message = makeString("The \""_s, name1, "\" and \""_s, name2, "\" arguments must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); + } + + auto name3 = std::span(arg3->ptr, arg3->len); + ASSERT(WTF::charactersAreAllASCII(name3)); + + // 3 arg names passed + auto message = makeString("The \""_s, name1, "\", \""_s, name2, "\", and \""_s, name3, "\" arguments must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return Bun__ERR_IPC_CHANNEL_CLOSED(globalObject); +} +extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_IPC_CHANNEL_CLOSED, "Channel closed."_s)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_SOCKET_BAD_TYPE, "Bad socket type specified. Valid types are: udp4, udp6"_s)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_PROTOCOL, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + EXPECT_ARG_COUNT(2); + + auto actual = callFrame->argument(0).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto expected = callFrame->argument(1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto message = makeString("Protocol \""_s, actual, "\" not supported. Expected \""_s, expected, "\""_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_PROTOCOL, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + EXPECT_ARG_COUNT(3); + + auto arg_name = callFrame->argument(0); + auto expected_type = callFrame->argument(1); + auto actual_value = callFrame->argument(2); + return JSValue::encode(ERR_INVALID_ARG_TYPE(scope, globalObject, arg_name, expected_type, actual_value)); +} + +} // namespace Bun + +JSC::JSValue WebCore::toJS(JSC::JSGlobalObject* globalObject, CommonAbortReason abortReason) +{ + switch (abortReason) { + case CommonAbortReason::Timeout: { + return createError(globalObject, Bun::ErrorCode::ABORT_ERR, "The operation timed out"_s); + } + case CommonAbortReason::UserAbort: { + // This message is a standardized error message. We cannot change it. + // https://webidl.spec.whatwg.org/#idl-DOMException:~:text=The%20operation%20was%20aborted. + return createError(globalObject, Bun::ErrorCode::ABORT_ERR, "The operation was aborted."_s); + } + case CommonAbortReason::ConnectionClosed: { + return createError(globalObject, Bun::ErrorCode::ABORT_ERR, "The connection was closed"_s); + } + default: { + break; + } + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +extern "C" JSC::EncodedJSValue WebCore__CommonAbortReason__toJS(JSC::JSGlobalObject* globalObject, WebCore::CommonAbortReason abortReason) +{ + return JSC::JSValue::encode(WebCore::toJS(globalObject, abortReason)); +} + +JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, const String& message) +{ + return Bun::createError(globalObject, Bun::ErrorCode::ERR_INVALID_THIS, message); +} + +JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, JSC::JSValue thisValue, const ASCIILiteral typeName) +{ + if (thisValue.isEmpty() || thisValue.isUndefined()) { + return Bun::createError(globalObject, Bun::ErrorCode::ERR_INVALID_THIS, makeString("Expected this to be instanceof "_s, typeName)); + } + + // Pathological case: the this value returns a string which is extremely long or causes an out of memory error. + const auto& typeString = thisValue.isString() ? String("a string"_s) : JSC::errorDescriptionForValue(globalObject, thisValue); + return Bun::createError(globalObject, Bun::ErrorCode::ERR_INVALID_THIS, makeString("Expected this to be instanceof "_s, typeName, ", but received "_s, typeString)); +} + +JSC::EncodedJSValue Bun::throwError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, Bun::ErrorCode code, const WTF::String& message) +{ + return JSC::JSValue::encode(scope.throwException(globalObject, createError(globalObject, code, message))); +} + +JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + EXPECT_ARG_COUNT(2); + + JSC::JSValue codeValue = callFrame->argument(0); + RETURN_IF_EXCEPTION(scope, {}); + +#if BUN_DEBUG + if (!codeValue.isNumber()) { + JSC::throwTypeError(globalObject, scope, "First argument to $ERR_ must be a number"_s); + return {}; + } +#endif + + int code = codeValue.toInt32(globalObject); + +#if BUN_DEBUG + if (code > Bun::NODE_ERROR_COUNT - 1 || code < 0) { + JSC::throwTypeError(globalObject, scope, "Invalid error code. Use $ERR_* constants"_s); + return {}; + } +#endif + + Bun::ErrorCode error = static_cast(code); + + switch (error) { + case Bun::ErrorCode::ERR_INVALID_ARG_TYPE: { + JSValue arg0 = callFrame->argument(1); + JSValue arg1 = callFrame->argument(2); + JSValue arg2 = callFrame->argument(3); + + return JSValue::encode(ERR_INVALID_ARG_TYPE(scope, globalObject, arg0, arg1, arg2)); + } + + default: { + break; + } + } + + auto message = callFrame->argument(1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + return JSC::JSValue::encode(createError(globalObject, error, message)); +} diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h new file mode 100644 index 0000000000000..4e2d1d60108df --- /dev/null +++ b/src/bun.js/bindings/ErrorCode.h @@ -0,0 +1,67 @@ +#pragma once + +#include "ZigGlobalObject.h" +#include "root.h" +#include +#include +#include "BunClientData.h" +#include "ErrorCode+List.h" + +namespace Bun { + +class ErrorCodeCache : public JSC::JSInternalFieldObjectImpl { +public: + using Base = JSInternalFieldObjectImpl; + using Field = ErrorCode; + + DECLARE_EXPORT_INFO; + + static size_t allocationSize(Checked inlineCapacity) + { + ASSERT_UNUSED(inlineCapacity, inlineCapacity == 0U); + return sizeof(ErrorCodeCache); + } + + template + static GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForErrorCodeCache.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForErrorCodeCache = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForErrorCodeCache.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForErrorCodeCache = std::forward(space); }); + } + + static ErrorCodeCache* create(VM& vm, Structure* structure); + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject); + + JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options); + +private: + JS_EXPORT_PRIVATE ErrorCodeCache(VM&, Structure*); + DECLARE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE); + void finishCreation(VM&); +}; + +JSC::EncodedJSValue throwError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, ErrorCode code, const WTF::String& message); +JSC::JSObject* createError(Zig::GlobalObject* globalObject, ErrorCode code, const WTF::String& message); +JSC::JSObject* createError(JSC::JSGlobalObject* globalObject, ErrorCode code, const WTF::String& message); +JSC::JSObject* createError(Zig::GlobalObject* globalObject, ErrorCode code, JSC::JSValue message); +JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options = jsUndefined()); +JSC::JSValue toJS(JSC::JSGlobalObject*, ErrorCode); +JSObject* createInvalidThisError(JSGlobalObject* globalObject, JSValue thisValue, const ASCIILiteral typeName); +JSObject* createInvalidThisError(JSGlobalObject* globalObject, const String& message); + +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_INVALID_PROTOCOL); +JSC_DECLARE_HOST_FUNCTION(jsFunctionMakeErrorWithCode); + +} diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts new file mode 100644 index 0000000000000..c8519af883efb --- /dev/null +++ b/src/bun.js/bindings/ErrorCode.ts @@ -0,0 +1,51 @@ +type ErrorCodeMapping = Array< + [ + /** error.code */ + string, + /** Constructor **/ + typeof TypeError | typeof RangeError | typeof Error | typeof SyntaxError, + /** error.name */ + string, + ] +>; + +export default [ + ["ABORT_ERR", Error, "AbortError"], + ["ERR_CRYPTO_INVALID_DIGEST", TypeError, "TypeError"], + ["ERR_ENCODING_INVALID_ENCODED_DATA", TypeError, "TypeError"], + ["ERR_HTTP2_INVALID_HEADER_VALUE", TypeError, "TypeError"], + ["ERR_HTTP2_INVALID_PSEUDOHEADER", TypeError, "TypeError"], + ["ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER", TypeError, "TypeError"], + ["ERR_INVALID_ARG_TYPE", TypeError, "TypeError"], + ["ERR_INVALID_ARG_VALUE", TypeError, "TypeError"], + ["ERR_INVALID_PROTOCOL", TypeError, "TypeError"], + ["ERR_INVALID_THIS", TypeError, "TypeError"], + ["ERR_IPC_CHANNEL_CLOSED", Error, "Error"], + ["ERR_IPC_DISCONNECTED", Error, "Error"], + ["ERR_MISSING_ARGS", TypeError, "TypeError"], + ["ERR_OUT_OF_RANGE", RangeError, "RangeError"], + ["ERR_PARSE_ARGS_INVALID_OPTION_VALUE", TypeError, "TypeError"], + ["ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL", TypeError, "TypeError"], + ["ERR_PARSE_ARGS_UNKNOWN_OPTION", TypeError, "TypeError"], + ["ERR_SERVER_NOT_RUNNING", Error, "Error"], + ["ERR_SOCKET_BAD_TYPE", TypeError, "TypeError"], + ["ERR_STREAM_ALREADY_FINISHED", TypeError, "TypeError"], + ["ERR_STREAM_CANNOT_PIPE", TypeError, "TypeError"], + ["ERR_STREAM_DESTROYED", TypeError, "TypeError"], + ["ERR_STREAM_NULL_VALUES", TypeError, "TypeError"], + ["ERR_STREAM_WRITE_AFTER_END", TypeError, "TypeError"], + ["ERR_ZLIB_INITIALIZATION_FAILED", Error, "Error"], + ["ERR_STRING_TOO_LONG", Error, "Error"], + ["ERR_CRYPTO_SCRYPT_INVALID_PARAMETER", Error, "Error"], + ["ERR_CRYPTO_INVALID_SCRYPT_PARAMS", RangeError, "RangeError"], + ["MODULE_NOT_FOUND", Error, "Error"], + ["ERR_ILLEGAL_CONSTRUCTOR", TypeError, "TypeError"], + ["ERR_INVALID_URL", TypeError, "TypeError"], + ["ERR_BUFFER_TOO_LARGE", RangeError, "RangeError"], + + // Bun-specific + ["ERR_FORMDATA_PARSE_ERROR", TypeError, "TypeError"], + ["ERR_BODY_ALREADY_USED", Error, "Error"], + ["ERR_STREAM_WRAP", Error, "Error"], + ["ERR_BORINGSSL", Error, "Error"], +] as ErrorCodeMapping; diff --git a/src/bun.js/bindings/EventLoopTask.h b/src/bun.js/bindings/EventLoopTask.h new file mode 100644 index 0000000000000..965a36adc05d8 --- /dev/null +++ b/src/bun.js/bindings/EventLoopTask.h @@ -0,0 +1,44 @@ +#include "root.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +class EventLoopTask { + WTF_MAKE_ISO_ALLOCATED(EventLoopTask); + +public: + enum CleanupTaskTag { CleanupTask }; + + template::value && std::is_convertible>::value>::type> + EventLoopTask(T task) + : m_task(WTFMove(task)) + , m_isCleanupTask(false) + { + } + + EventLoopTask(Function&& task) + : m_task([task = WTFMove(task)](ScriptExecutionContext&) { task(); }) + , m_isCleanupTask(false) + { + } + + template>::value>::type> + EventLoopTask(CleanupTaskTag, T task) + : m_task(WTFMove(task)) + , m_isCleanupTask(true) + { + } + + void performTask(ScriptExecutionContext& context) + { + m_task(context); + delete this; + } + bool isCleanupTask() const { return m_isCleanupTask; } + +protected: + Function m_task; + bool m_isCleanupTask; +}; + +} \ No newline at end of file diff --git a/src/bun.js/bindings/ExceptionCode.h b/src/bun.js/bindings/ExceptionCode.h index d63691db1cb40..ebbb9501bcef4 100644 --- a/src/bun.js/bindings/ExceptionCode.h +++ b/src/bun.js/bindings/ExceptionCode.h @@ -71,6 +71,9 @@ enum ExceptionCode { // Used to indicate to the bindings that a JS exception was thrown below and it should be propagated. ExistingExceptionError, + + InvalidThisError, + InvalidURLError, }; } // namespace WebCore @@ -116,7 +119,9 @@ template<> struct EnumTraits { WebCore::ExceptionCode::TypeError, WebCore::ExceptionCode::JSSyntaxError, WebCore::ExceptionCode::StackOverflowError, - WebCore::ExceptionCode::ExistingExceptionError>; + WebCore::ExceptionCode::ExistingExceptionError, + WebCore::ExceptionCode::InvalidThisError, + WebCore::ExceptionCode::InvalidURLError>; }; } // namespace WTF diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index fa93fc9f672fd..19d4cab3243d2 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -102,7 +102,7 @@ static JSC::EncodedJSValue functionRequireResolve(JSC::JSGlobalObject* globalObj // require.resolve also supports a paths array // we only support a single path if (!fromValue.isUndefinedOrNull() && fromValue.isObject()) { - if (auto pathsObject = fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s))) { + if (auto pathsObject = fromValue.getObject()->getIfPropertyExists(globalObject, builtinNames(vm).pathsPublicName())) { if (pathsObject.isCell() && pathsObject.asCell()->type() == JSC::JSType::ArrayType) { auto pathsArray = JSC::jsCast(pathsObject); if (pathsArray->length() > 0) { @@ -209,7 +209,7 @@ extern "C" JSC::EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObje if (!fromValue.isUndefinedOrNull() && fromValue.isObject()) { - if (auto pathsObject = fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s))) { + if (auto pathsObject = fromValue.getObject()->getIfPropertyExists(globalObject, builtinNames(vm).pathsPublicName())) { if (pathsObject.isCell() && pathsObject.asCell()->type() == JSC::JSType::ArrayType) { auto pathsArray = JSC::jsCast(pathsObject); if (pathsArray->length() > 0) { @@ -358,7 +358,7 @@ JSC_DEFINE_HOST_FUNCTION(functionImportMeta__resolve, JSValue fromValue = callFrame->uncheckedArgument(1); if (!fromValue.isUndefinedOrNull() && fromValue.isObject()) { - if (JSValue pathsObject = fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s))) { + if (JSValue pathsObject = fromValue.getObject()->getIfPropertyExists(globalObject, builtinNames(vm).pathsPublicName())) { if (pathsObject.isCell() && pathsObject.asCell()->type() == JSC::JSType::ArrayType) { auto* pathsArray = JSC::jsCast(pathsObject); if (pathsArray->length() > 0) { @@ -535,7 +535,7 @@ class ImportMetaObjectPrototype final : public JSC::JSNonFinalObject { reifyStaticProperties(vm, ImportMetaObject::info(), ImportMetaObjectPrototypeValues, *this); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); - auto mainGetter = JSFunction::create(vm, importMetaObjectMainCodeGenerator(vm), globalObject); + auto mainGetter = JSFunction::create(vm, globalObject, importMetaObjectMainCodeGenerator(vm), globalObject); this->putDirectAccessor( this->globalObject(), @@ -666,7 +666,7 @@ void ImportMetaObject::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) { // if (void* wrapped = thisObject->wrapped()) { // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + // analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); // } Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/InternalModuleRegistry.cpp b/src/bun.js/bindings/InternalModuleRegistry.cpp index d83ad266beb19..3043fd0e9d19f 100644 --- a/src/bun.js/bindings/InternalModuleRegistry.cpp +++ b/src/bun.js/bindings/InternalModuleRegistry.cpp @@ -1,5 +1,4 @@ #include "InternalModuleRegistry.h" - #include "ZigGlobalObject.h" #include #include @@ -11,6 +10,7 @@ #include #include "InternalModuleRegistryConstants.h" +#include "wtf/Forward.h" namespace Bun { @@ -41,7 +41,7 @@ static void maybeAddCodeCoverage(JSC::VM& vm, const JSC::SourceCode& code) maybeAddCodeCoverage(vm, source); \ JSFunction* func \ = JSFunction::create( \ - vm, \ + vm, globalObject, \ createBuiltinExecutable( \ vm, source, \ Identifier(), \ @@ -78,7 +78,7 @@ JSValue initializeInternalModuleFromDisk( WTF::String fileBase, const WTF::String& urlString) { - WTF::String file = makeString(BUN_DYNAMIC_JS_LOAD_PATH, "/"_s, WTFMove(fileBase)); + WTF::String file = makeString(ASCIILiteral::fromLiteralUnsafe(BUN_DYNAMIC_JS_LOAD_PATH), "/"_s, WTFMove(fileBase)); if (auto contents = WTF::FileSystemImpl::readEntireFile(file)) { auto string = WTF::String::fromUTF8(contents.value()); INTERNAL_MODULE_REGISTRY_GENERATE_(globalObject, vm, string, moduleName, urlString); diff --git a/src/bun.js/bindings/JS2Native.cpp b/src/bun.js/bindings/JS2Native.cpp index f0a89109b324f..61b16fefb19b3 100644 --- a/src/bun.js/bindings/JS2Native.cpp +++ b/src/bun.js/bindings/JS2Native.cpp @@ -1,3 +1,4 @@ +#include "root.h" #include "JS2Native.h" #include @@ -5,6 +6,7 @@ #include #include "ZigGlobalObject.h" + #include "GeneratedJS2Native.h" #include "wtf/Assertions.h" diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index 55ad01ddba7fb..da9a2e7c99555 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -1,6 +1,15 @@ #include "root.h" +#include "JavaScriptCore/ExceptionHelpers.h" +#include "JavaScriptCore/JSString.h" +#include "JavaScriptCore/Error.h" +#include "JavaScriptCore/JSArrayBufferView.h" +#include "JavaScriptCore/JSCell.h" +#include "JavaScriptCore/JSGlobalObject.h" +#include "BufferEncodingType.h" +#include "JavaScriptCore/JSCJSValue.h" + #include "JSBuffer.h" #include "JavaScriptCore/ArgList.h" @@ -44,6 +53,7 @@ #include "JSBufferEncodingType.h" #include "wtf/Assertions.h" +#include "wtf/Forward.h" #include #if ENABLE(MEDIA_SOURCE) #include "BufferMediaSource.h" @@ -69,6 +79,7 @@ using namespace JSC; using namespace WebCore; JSC_DECLARE_HOST_FUNCTION(constructJSBuffer); +JSC_DECLARE_HOST_FUNCTION(callJSBuffer); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" @@ -96,6 +107,86 @@ static JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_toString); static JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_write); #pragma clang diagnostic pop +namespace Bun { + +// Use a JSString* here to avoid unnecessarily joining the rope string. +// If we're only getting the length property, it won't join the rope string. +std::optional byteLength(JSC::JSString* str, WebCore::BufferEncodingType encoding) +{ + if (str->length() == 0) + return 0; + + switch (encoding) { + + case WebCore::BufferEncodingType::ucs2: + case WebCore::BufferEncodingType::utf16le: { + // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L600 + return str->length() * 2; + } + + case WebCore::BufferEncodingType::latin1: + case WebCore::BufferEncodingType::ascii: { + // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L627 + return str->length(); + } + + case WebCore::BufferEncodingType::base64: + case WebCore::BufferEncodingType::base64url: { + int64_t length = str->length(); + const auto& view = str->tryGetValue(true); + if (UNLIKELY(view->isNull())) { + return std::nullopt; + } + + if (view->is8Bit()) { + const auto span = view->span8(); + if (span.data()[length - 1] == 0x3D) { + length--; + + if (length > 1 && span.data()[length - 1] == '=') + length--; + } + } else { + const auto span = view->span16(); + if (span.data()[length - 1] == 0x3D) { + length--; + + if (length > 1 && span.data()[length - 1] == '=') + length--; + } + } + + // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L579 + return static_cast((length * 3) >> 2); + } + + case WebCore::BufferEncodingType::hex: { + return str->length() >> 1; + } + + case WebCore::BufferEncodingType::utf8: { + const auto& view = str->tryGetValue(true); + if (UNLIKELY(view->isNull())) { + return std::nullopt; + } + + if (view->is8Bit()) { + const auto span = view->span8(); + return Bun__encoding__byteLengthLatin1(span.data(), span.size(), static_cast(encoding)); + } else { + const auto span = view->span16(); + return Bun__encoding__byteLengthUTF16(span.data(), span.size(), static_cast(encoding)); + } + } + default: { + RELEASE_ASSERT_NOT_REACHED(); + } + } + + return std::nullopt; +} +} + static JSUint8Array* allocBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t byteLength) { #if ASSERT_ENABLED @@ -160,7 +251,7 @@ static inline uint32_t parseIndex(JSC::JSGlobalObject* lexicalGlobalObject, JSC: return num.value(); if (arg.isNumber()) - throwRangeError(lexicalGlobalObject, scope, "Invalid array length"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "Invalid array length"_s); else throwTypeError(lexicalGlobalObject, scope, "Expected number"_s); @@ -222,7 +313,7 @@ JSC::EncodedJSValue JSBuffer__bufferFromPointerAndLengthAndDeinit(JSC::JSGlobalO auto* subclassStructure = globalObject->JSBufferSubclassStructure(); if (LIKELY(length > 0)) { - auto buffer = ArrayBuffer::createFromBytes(ptr, length, createSharedTask([ctx, bytesDeallocator](void* p) { + auto buffer = ArrayBuffer::createFromBytes({ reinterpret_cast(ptr), length }, createSharedTask([ctx, bytesDeallocator](void* p) { if (bytesDeallocator) bytesDeallocator(p, ctx); })); @@ -243,7 +334,11 @@ static inline JSC::EncodedJSValue writeToBuffer(JSC::JSGlobalObject* lexicalGlob if (UNLIKELY(str->length() == 0)) return JSC::JSValue::encode(JSC::jsNumber(0)); - const auto& view = str->tryGetValue(lexicalGlobalObject); + const auto& view = str->value(lexicalGlobalObject); + if (view->isNull()) { + return {}; + } + size_t written = 0; switch (encoding) { @@ -256,11 +351,11 @@ static inline JSC::EncodedJSValue writeToBuffer(JSC::JSGlobalObject* lexicalGlob case WebCore::BufferEncodingType::base64url: case WebCore::BufferEncodingType::hex: { - if (view.is8Bit()) { - const auto span = view.span8(); + if (view->is8Bit()) { + const auto span = view->span8(); written = Bun__encoding__writeLatin1(span.data(), span.size(), reinterpret_cast(castedThis->vector()) + offset, length, static_cast(encoding)); } else { - const auto span = view.span16(); + const auto span = view->span16(); written = Bun__encoding__writeUTF16(span.data(), span.size(), reinterpret_cast(castedThis->vector()) + offset, length, static_cast(encoding)); } break; @@ -316,7 +411,7 @@ static inline JSC::JSUint8Array* JSBuffer__bufferFromLengthAsArray(JSC::JSGlobal auto throwScope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm()); if (UNLIKELY(length < 0)) { - throwRangeError(lexicalGlobalObject, throwScope, "Invalid array length"_s); + throwNodeRangeError(lexicalGlobalObject, throwScope, "Invalid array length"_s); return nullptr; } @@ -351,7 +446,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocUnsafeBody(JS double lengthDouble = lengthValue.toIntegerWithTruncation(lexicalGlobalObject); if (UNLIKELY(lengthDouble < 0 || lengthDouble > UINT_MAX)) { - throwRangeError(lexicalGlobalObject, throwScope, "Buffer size must be 0...4294967295"_s); + throwNodeRangeError(lexicalGlobalObject, throwScope, "Buffer size must be 0...4294967295"_s); return {}; } @@ -374,8 +469,8 @@ static JSC::EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalOb const auto& view = str->tryGetValue(lexicalGlobalObject); JSC::EncodedJSValue result; - if (view.is8Bit()) { - const auto span = view.span8(); + if (view->is8Bit()) { + const auto span = view->span8(); switch (encoding) { case WebCore::BufferEncodingType::utf8: @@ -399,7 +494,7 @@ static JSC::EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalOb } } } else { - const auto span = view.span16(); + const auto span = view->span16(); switch (encoding) { case WebCore::BufferEncodingType::utf8: case WebCore::BufferEncodingType::base64: @@ -479,7 +574,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG double lengthDouble = lengthValue.toIntegerWithTruncation(lexicalGlobalObject); if (UNLIKELY(lengthDouble < 0 || lengthDouble > UINT_MAX)) { - throwRangeError(lexicalGlobalObject, scope, "Buffer size must be 0...4294967295"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "Buffer size must be 0...4294967295"_s); return {}; } @@ -568,7 +663,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocUnsafeSlowBod } // new SlowBuffer(size) -EncodedJSValue constructSlowBuffer(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(constructSlowBuffer, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { return jsBufferConstructorFunction_allocUnsafeSlowBody(lexicalGlobalObject, callFrame); } @@ -581,73 +676,14 @@ static inline JSC::EncodedJSValue jsBufferByteLengthFromStringAndEncoding(JSC::J return JSC::JSValue::encode(jsUndefined()); } - if (str->length() == 0) - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(0))); - - int64_t written = 0; - - switch (encoding) { - - case WebCore::BufferEncodingType::ucs2: - case WebCore::BufferEncodingType::utf16le: { - // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L600 - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(str->length() * 2))); + if (auto length = Bun::byteLength(str, encoding)) { + return JSValue::encode(jsNumber(*length)); } - - case WebCore::BufferEncodingType::latin1: - case WebCore::BufferEncodingType::ascii: { - // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L627 - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(str->length()))); + if (!scope.exception()) { + throwOutOfMemoryError(lexicalGlobalObject, scope); } - case WebCore::BufferEncodingType::base64: - case WebCore::BufferEncodingType::base64url: { - int64_t length = str->length(); - const auto& view = str->tryGetValue(lexicalGlobalObject); - - if (view.is8Bit()) { - const auto span = view.span8(); - if (span.data()[length - 1] == 0x3D) { - length--; - - if (length > 1 && span.data()[length - 1] == '=') - length--; - } - } else { - const auto span = view.span16(); - if (span.data()[length - 1] == 0x3D) { - length--; - - if (length > 1 && span.data()[length - 1] == '=') - length--; - } - } - - // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L579 - return JSValue::encode(jsNumber(static_cast((length * 3) >> 2))); - } - - case WebCore::BufferEncodingType::hex: { - return JSValue::encode(jsNumber(str->length() >> 1)); - } - - case WebCore::BufferEncodingType::utf8: { - const auto& view = str->tryGetValue(lexicalGlobalObject); - if (view.is8Bit()) { - const auto span = view.span8(); - written = Bun__encoding__byteLengthLatin1(span.data(), span.size(), static_cast(encoding)); - } else { - const auto span = view.span16(); - written = Bun__encoding__byteLengthUTF16(span.data(), span.size(), static_cast(encoding)); - } - break; - } - default: { - break; - } - } - - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(written))); + return {}; } static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) { @@ -1179,7 +1215,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob } if (UNLIKELY(end > limit)) { - throwRangeError(lexicalGlobalObject, scope, "end out of range"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "end out of range"_s); return JSC::JSValue::encode(jsUndefined()); } @@ -1389,7 +1425,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap16Body(JSC::JSGl constexpr int elemSize = 2; int64_t length = static_cast(castedThis->byteLength()); if (length % elemSize != 0) { - throwRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 16-bits"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 16-bits"_s); return JSC::JSValue::encode(jsUndefined()); } @@ -1418,7 +1454,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap32Body(JSC::JSGl constexpr int elemSize = 4; int64_t length = static_cast(castedThis->byteLength()); if (length % elemSize != 0) { - throwRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 32-bits"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 32-bits"_s); return JSC::JSValue::encode(jsUndefined()); } @@ -1452,7 +1488,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGl constexpr size_t elemSize = 8; int64_t length = static_cast(castedThis->byteLength()); if (length % elemSize != 0) { - throwRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 64-bits"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 64-bits"_s); return JSC::JSValue::encode(jsUndefined()); } @@ -1479,7 +1515,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGl return JSC::JSValue::encode(castedThis); } -static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSUint8Array* castedThis, size_t offset, size_t length, WebCore::BufferEncodingType encoding) +static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSArrayBufferView* castedThis, size_t offset, size_t length, WebCore::BufferEncodingType encoding) { auto scope = DECLARE_THROW_SCOPE(vm); @@ -1493,7 +1529,7 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj case WebCore::BufferEncodingType::latin1: { LChar* data = nullptr; auto str = String::createUninitialized(length, data); - memcpy(data, reinterpret_cast(castedThis->typedVector() + offset), length); + memcpy(data, reinterpret_cast(castedThis->vector()) + offset, length); return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str))); } @@ -1505,7 +1541,7 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj return JSC::JSValue::encode(JSC::jsEmptyString(vm)); } else { auto str = String::createUninitialized(u16length, data); - memcpy(reinterpret_cast(data), reinterpret_cast(castedThis->typedVector() + offset), u16length * 2); + memcpy(reinterpret_cast(data), reinterpret_cast(castedThis->vector()) + offset, u16length * 2); return JSC::JSValue::encode(JSC::jsString(vm, str)); } @@ -1517,7 +1553,7 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj // so we might as well allocate upfront LChar* data = nullptr; auto str = String::createUninitialized(length, data); - Bun__encoding__writeLatin1(castedThis->typedVector() + offset, length, data, length, static_cast(encoding)); + Bun__encoding__writeLatin1(reinterpret_cast(castedThis->vector()) + offset, length, data, length, static_cast(encoding)); return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str))); } @@ -1526,7 +1562,7 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj case WebCore::BufferEncodingType::base64: case WebCore::BufferEncodingType::base64url: case WebCore::BufferEncodingType::hex: { - ret = Bun__encoding__toString(castedThis->typedVector() + offset, length, lexicalGlobalObject, static_cast(encoding)); + ret = Bun__encoding__toString(reinterpret_cast(castedThis->vector()) + offset, length, lexicalGlobalObject, static_cast(encoding)); break; } default: { @@ -1544,12 +1580,31 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj RELEASE_AND_RETURN(scope, JSC::JSValue::encode(retValue)); } +// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/node_buffer.cc#L208-L233 +bool inline parseArrayIndex(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, size_t& out, ASCIILiteral errorMessage) +{ + if (value.isUndefined()) { + return true; + } + + int64_t index = static_cast(value.toIntegerWithTruncation(globalObject)); + RETURN_IF_EXCEPTION(scope, false); + + if (index < 0) { + throwNodeRangeError(globalObject, scope, errorMessage); + return false; + } + + out = static_cast(index); + return true; +} + static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); auto scope = DECLARE_THROW_SCOPE(vm); uint32_t start = 0; - uint32_t end = castedThis->length(); + uint32_t end = castedThis->byteLength(); uint32_t byteLength = end; WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; @@ -1591,6 +1646,48 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS return jsBufferToString(vm, lexicalGlobalObject, castedThis, start, end > start ? end - start : 0, encoding); } +// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/node_buffer.cc#L544 +template +static inline JSC::EncodedJSValue jsBufferPrototypeFunction_SliceWithEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + auto* castedThis = JSC::jsDynamicCast(callFrame->thisValue()); + const JSValue startValue = callFrame->argument(0); + const JSValue endValue = callFrame->argument(1); + + if (UNLIKELY(!castedThis)) { + throwTypeError(lexicalGlobalObject, scope, "Expected ArrayBufferView"_s); + return {}; + } + + const size_t length = castedThis->byteLength(); + if (UNLIKELY(length == 0)) { + return JSC::JSValue::encode(JSC::jsEmptyString(vm)); + } + + size_t start = 0; + size_t end = length; + + if (UNLIKELY(!parseArrayIndex(scope, lexicalGlobalObject, startValue, start, "start must be a positive integer"_s))) { + return {}; + } + + if (UNLIKELY(!parseArrayIndex(scope, lexicalGlobalObject, endValue, end, "end must be a positive integer"_s))) { + return {}; + } + + if (end < start) + end = start; + + if (!(end <= length)) { + throwNodeRangeError(lexicalGlobalObject, scope, "end out of range"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + return jsBufferToString(vm, lexicalGlobalObject, castedThis, start, end - start, encoding); +} + // DOMJIT makes it slower! TODO: investigate why // JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsBufferPrototypeToStringWithoutTypeChecks, JSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::JSUint8Array* thisValue, JSC::JSString* encodingValue)); @@ -1615,6 +1712,59 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS // return JSValue::decode(jsBufferToString(vm, lexicalGlobalObject, thisValue, 0, thisValue->byteLength(), encoding)); // } +// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/node_buffer.cc#L711 +template +static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeEncodingBody(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSArrayBufferView* castedThis, JSString* str, JSValue offsetValue, JSValue lengthValue) +{ + size_t offset = 0; + size_t length = castedThis->byteLength(); + size_t max = length; + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(!parseArrayIndex(scope, lexicalGlobalObject, offsetValue, offset, "offset must be > 0"_s))) { + return {}; + } + + if (UNLIKELY(offset > max)) { + throwNodeRangeError(lexicalGlobalObject, scope, "offset is out of bounds"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + if (UNLIKELY(!parseArrayIndex(scope, lexicalGlobalObject, lengthValue, max, "length must be > 0"_s))) { + return {}; + } + + size_t max_length = std::min(length - offset, max); + + RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, max_length, encoding)); +} + +template +static inline JSC::EncodedJSValue jsBufferPrototypeFunctionWriteWithEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + auto* castedThis = JSC::jsDynamicCast(callFrame->thisValue()); + + JSString* text = callFrame->argument(0).toStringOrNull(lexicalGlobalObject); + RETURN_IF_EXCEPTION(scope, {}); + + JSValue offsetValue = callFrame->argument(1); + JSValue lengthValue = callFrame->argument(2); + + if (UNLIKELY(!castedThis)) { + throwTypeError(lexicalGlobalObject, scope, "Expected ArrayBufferView"_s); + return {}; + } + + if (UNLIKELY(castedThis->isDetached())) { + throwTypeError(lexicalGlobalObject, scope, "ArrayBufferView is detached"_s); + return {}; + } + + return jsBufferPrototypeFunction_writeEncodingBody(vm, lexicalGlobalObject, castedThis, text, offsetValue, lengthValue); +} + static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); @@ -1682,7 +1832,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlo int32_t userOffset = offsetValue.toInt32(lexicalGlobalObject); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); if (userOffset < 0 || userOffset > max) { - throwRangeError(lexicalGlobalObject, scope, "Offset is out of bounds"_s); + throwNodeRangeError(lexicalGlobalObject, scope, "Offset is out of bounds"_s); return JSC::JSValue::encode(jsUndefined()); } offset = static_cast(userOffset); @@ -1715,7 +1865,7 @@ extern "C" JSC::EncodedJSValue JSBuffer__fromMmap(Zig::GlobalObject* globalObjec auto* structure = globalObject->JSBufferSubclassStructure(); - auto buffer = ArrayBuffer::createFromBytes(ptr, length, createSharedTask([length](void* p) { + auto buffer = ArrayBuffer::createFromBytes({ static_cast(ptr), length }, createSharedTask([length](void* p) { #if !OS(WINDOWS) munmap(p, length); #else @@ -1779,7 +1929,7 @@ class JSBufferConstructor final : public JSC::InternalFunction { private: JSBufferConstructor(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) - : Base(vm, structure, constructJSBuffer, constructJSBuffer) + : Base(vm, structure, callJSBuffer, constructJSBuffer) { } @@ -1814,7 +1964,7 @@ JSC_DEFINE_JIT_OPERATION(jsBufferConstructorAllocWithoutTypeChecks, JSUint8Array CallFrame* callFrame = DECLARE_CALL_FRAME(vm); IGNORE_WARNINGS_END JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return allocBuffer(lexicalGlobalObject, byteLength); + return { allocBuffer(lexicalGlobalObject, byteLength) }; } JSC_DEFINE_JIT_OPERATION(jsBufferConstructorAllocUnsafeWithoutTypeChecks, JSUint8Array*, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int byteLength)) @@ -1824,7 +1974,7 @@ JSC_DEFINE_JIT_OPERATION(jsBufferConstructorAllocUnsafeWithoutTypeChecks, JSUint CallFrame* callFrame = DECLARE_CALL_FRAME(vm); IGNORE_WARNINGS_END JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return allocBufferUnsafe(lexicalGlobalObject, byteLength); + return { allocBufferUnsafe(lexicalGlobalObject, byteLength) }; } JSC_DEFINE_JIT_OPERATION(jsBufferConstructorAllocUnsafeSlowWithoutTypeChecks, JSUint8Array*, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int byteLength)) @@ -1834,7 +1984,7 @@ JSC_DEFINE_JIT_OPERATION(jsBufferConstructorAllocUnsafeSlowWithoutTypeChecks, JS CallFrame* callFrame = DECLARE_CALL_FRAME(vm); IGNORE_WARNINGS_END JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return allocBufferUnsafe(lexicalGlobalObject, byteLength); + return { allocBufferUnsafe(lexicalGlobalObject, byteLength) }; } JSC_ANNOTATE_HOST_FUNCTION(JSBufferConstructorConstruct, JSBufferConstructor::construct); @@ -1903,30 +2053,94 @@ JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_write, (JSGlobalObject * lexi return IDLOperation::call(*lexicalGlobalObject, *callFrame, "write"); } +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_utf16leWrite, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_utf8Write, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_latin1Write, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_asciiWrite, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_base64Write, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_base64urlWrite, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_hexWrite, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunctionWriteWithEncoding(lexicalGlobalObject, callFrame); +} + +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_utf8Slice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_utf16leSlice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_latin1Slice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_asciiSlice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_base64Slice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_base64urlSlice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} +JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_hexSlice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return jsBufferPrototypeFunction_SliceWithEncoding(lexicalGlobalObject, callFrame); +} + /* */ /* Hash table for prototype */ static const HashTableValue JSBufferPrototypeTableValues[] = { - { "asciiSlice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeAsciiSliceCodeGenerator, 2 } }, - { "asciiWrite"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeAsciiWriteCodeGenerator, 1 } }, - { "base64Slice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeBase64SliceCodeGenerator, 2 } }, - { "base64Write"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeBase64WriteCodeGenerator, 1 } }, - { "base64urlSlice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeBase64urlSliceCodeGenerator, 2 } }, - { "base64urlWrite"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeBase64urlWriteCodeGenerator, 1 } }, + { "asciiSlice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_asciiSlice, 2 } }, + { "asciiWrite"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_asciiWrite, 3 } }, + { "base64Slice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_base64Slice, 2 } }, + { "base64Write"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_base64Write, 3 } }, + { "base64urlSlice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_base64urlSlice, 2 } }, + { "base64urlWrite"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_base64urlWrite, 3 } }, { "compare"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_compare, 5 } }, { "copy"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_copy, 4 } }, { "equals"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_equals, 1 } }, { "fill"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_fill, 4 } }, - { "hexSlice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeHexSliceCodeGenerator, 2 } }, - { "hexWrite"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeHexWriteCodeGenerator, 1 } }, + { "hexSlice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_hexSlice, 2 } }, + { "hexWrite"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_hexWrite, 3 } }, { "includes"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_includes, 3 } }, { "indexOf"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_indexOf, 3 } }, { "inspect"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeInspectCodeGenerator, 2 } }, { "lastIndexOf"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_lastIndexOf, 3 } }, - { "latin1Slice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeLatin1SliceCodeGenerator, 2 } }, - { "latin1Write"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeLatin1WriteCodeGenerator, 1 } }, + { "latin1Slice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_latin1Slice, 2 } }, + { "latin1Write"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_latin1Write, 3 } }, { "offset"_s, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinAccessorType, jsBufferPrototypeOffsetCodeGenerator, 0 } }, { "parent"_s, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinAccessorType, jsBufferPrototypeParentCodeGenerator, 0 } }, { "readBigInt64"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadBigInt64LECodeGenerator, 1 } }, @@ -1976,12 +2190,12 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "toJSON"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeToJSONCodeGenerator, 1 } }, { "toLocaleString"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } }, { "toString"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } }, - { "ucs2Slice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUcs2SliceCodeGenerator, 2 } }, - { "ucs2Write"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUcs2WriteCodeGenerator, 1 } }, - { "utf16leSlice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUtf16leSliceCodeGenerator, 2 } }, - { "utf16leWrite"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUtf16leWriteCodeGenerator, 1 } }, - { "utf8Slice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUtf8SliceCodeGenerator, 2 } }, - { "utf8Write"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUtf8WriteCodeGenerator, 1 } }, + { "ucs2Slice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf16leSlice, 2 } }, + { "ucs2Write"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf16leWrite, 3 } }, + { "utf16leSlice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf16leSlice, 2 } }, + { "utf16leWrite"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf16leWrite, 3 } }, + { "utf8Slice"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf8Slice, 2 } }, + { "utf8Write"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf8Write, 3 } }, { "write"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_write, 4 } }, { "writeBigInt64BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteBigInt64BECodeGenerator, 1 } }, { "writeBigInt64LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteBigInt64LECodeGenerator, 1 } }, @@ -2082,16 +2296,16 @@ JSC::JSObject* createBufferConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalO } // namespace WebCore -JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexicalGlobalObject, JSValue newTarget, ArgList args) { VM& vm = lexicalGlobalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - size_t argsCount = callFrame->argumentCount(); + size_t argsCount = args.size(); if (argsCount == 0) { RELEASE_AND_RETURN(throwScope, constructBufferEmpty(lexicalGlobalObject)); } - JSValue distinguishingArg = callFrame->uncheckedArgument(0); - JSValue encodingArg = argsCount > 1 ? callFrame->uncheckedArgument(1) : JSValue(); + JSValue distinguishingArg = args.at(0); + JSValue encodingArg = argsCount > 1 ? args.at(1) : JSValue(); auto* globalObject = reinterpret_cast(lexicalGlobalObject); if (distinguishingArg.isAnyInt()) { @@ -2113,6 +2327,7 @@ JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobal case Int8ArrayType: case Int16ArrayType: case Int32ArrayType: + case Float16ArrayType: case Float32ArrayType: case Float64ArrayType: case BigInt64ArrayType: @@ -2167,14 +2382,14 @@ JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobal std::optional length; if (argsCount > 1) { - offset = callFrame->uncheckedArgument(1).toTypedArrayIndex(globalObject, "byteOffset"_s); + offset = args.at(1).toTypedArrayIndex(globalObject, "byteOffset"_s); // TOOD: return Node.js error RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); if (argsCount > 2) { // If the length value is present but undefined, treat it as missing. - JSValue lengthValue = callFrame->uncheckedArgument(2); + JSValue lengthValue = args.at(2); if (!lengthValue.isUndefined()) { length = lengthValue.toTypedArrayIndex(globalObject, "length"_s); @@ -2197,7 +2412,7 @@ JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobal if (buffer->isResizableOrGrowableShared()) { if (UNLIKELY(offset > byteLength)) { // TOOD: return Node.js error - throwRangeError(globalObject, throwScope, "byteOffset exceeds source ArrayBuffer byteLength"_s); + throwNodeRangeError(globalObject, throwScope, "byteOffset exceeds source ArrayBuffer byteLength"_s); return JSValue::encode({}); } } else { @@ -2222,13 +2437,13 @@ JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobal JSC::JSObject* constructor = lexicalGlobalObject->m_typedArrayUint8.constructor(lexicalGlobalObject); - MarkedArgumentBuffer args; - args.append(distinguishingArg); + MarkedArgumentBuffer argsBuffer; + argsBuffer.append(distinguishingArg); for (size_t i = 1; i < argsCount; ++i) - args.append(callFrame->uncheckedArgument(i)); + argsBuffer.append(args.at(i)); - JSValue target = callFrame->newTarget(); - if (!target) { + JSValue target = newTarget; + if (!target || !target.isCell()) { target = globalObject->JSBufferConstructor(); } @@ -2240,6 +2455,16 @@ JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobal RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(object)); } +JSC_DEFINE_HOST_FUNCTION(callJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ + return createJSBufferFromJS(lexicalGlobalObject, callFrame->thisValue(), ArgList(callFrame)); +} + +JSC_DEFINE_HOST_FUNCTION(constructJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ + return createJSBufferFromJS(lexicalGlobalObject, callFrame->newTarget(), ArgList(callFrame)); +} + bool JSBuffer__isBuffer(JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value) { JSC::VM& vm = lexicalGlobalObject->vm(); diff --git a/src/bun.js/bindings/JSBuffer.h b/src/bun.js/bindings/JSBuffer.h index 0e18b1513be2f..6910043790745 100644 --- a/src/bun.js/bindings/JSBuffer.h +++ b/src/bun.js/bindings/JSBuffer.h @@ -35,6 +35,12 @@ extern "C" JSC::EncodedJSValue Bun__encoding__toStringUTF8(const uint8_t* input, extern "C" bool Bun__Buffer_fill(ZigString*, void*, size_t, WebCore::BufferEncodingType); extern "C" bool JSBuffer__isBuffer(JSC::JSGlobalObject*, JSC::EncodedJSValue); +namespace Bun { + +std::optional byteLength(JSC::JSString* str, WebCore::BufferEncodingType encoding); + +} + namespace WebCore { JSC::JSUint8Array* createUninitializedBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t length); @@ -44,7 +50,7 @@ JSC::JSUint8Array* createBuffer(JSC::JSGlobalObject* lexicalGlobalObject, const JSC::JSUint8Array* createBuffer(JSC::JSGlobalObject* lexicalGlobalObject, const char* ptr, size_t length); JSC::JSUint8Array* createEmptyBuffer(JSC::JSGlobalObject* lexicalGlobalObject); -JSC::EncodedJSValue constructSlowBuffer(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(constructSlowBuffer); JSC::JSObject* createBufferPrototype(JSC::VM&, JSC::JSGlobalObject*); JSC::Structure* createBufferStructure(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue prototype); JSC::JSObject* createBufferConstructor(JSC::VM&, JSC::JSGlobalObject*, JSC::JSObject* bufferPrototype); diff --git a/src/bun.js/bindings/JSBufferEncodingType.cpp b/src/bun.js/bindings/JSBufferEncodingType.cpp index 8645ee6914cd0..336d7cfdda2fd 100644 --- a/src/bun.js/bindings/JSBufferEncodingType.cpp +++ b/src/bun.js/bindings/JSBufferEncodingType.cpp @@ -20,6 +20,7 @@ #include "config.h" #include "JSBufferEncodingType.h" +#include "wtf/Forward.h" #include #include @@ -59,7 +60,7 @@ template<> std::optional parseEnumerationvalue(&lexicalGlobalObject); + String encoding = str->value(&lexicalGlobalObject); switch (encoding.length()) { case 0: { return BufferEncodingType::utf8; @@ -128,9 +129,9 @@ template<> std::optional parseEnumeration const char* expectedEnumerationValues() +template<> ASCIILiteral expectedEnumerationValues() { - return "\"utf8\", \"ucs2\", \"utf16le\", \"latin1\", \"ascii\", \"base64\", \"base64url\", \"hex\""; + return "\"utf8\", \"ucs2\", \"utf16le\", \"latin1\", \"ascii\", \"base64\", \"base64url\", \"hex\""_s; } } // namespace WebCore diff --git a/src/bun.js/bindings/JSBufferEncodingType.h b/src/bun.js/bindings/JSBufferEncodingType.h index ee788006240a4..76b3aea30132c 100644 --- a/src/bun.js/bindings/JSBufferEncodingType.h +++ b/src/bun.js/bindings/JSBufferEncodingType.h @@ -8,6 +8,6 @@ String convertEnumerationToString(BufferEncodingType); template<> JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, BufferEncodingType); template<> std::optional parseEnumeration(JSC::JSGlobalObject&, JSC::JSValue); -template<> const char* expectedEnumerationValues(); +template<> WTF::ASCIILiteral expectedEnumerationValues(); } // namespace WebCore \ No newline at end of file diff --git a/src/bun.js/bindings/JSBufferList.cpp b/src/bun.js/bindings/JSBufferList.cpp index 9a4e9aa7c0601..4741b88afcee9 100644 --- a/src/bun.js/bindings/JSBufferList.cpp +++ b/src/bun.js/bindings/JSBufferList.cpp @@ -27,10 +27,6 @@ static JSC_DEFINE_CUSTOM_GETTER(JSBufferList_getLength, (JSC::JSGlobalObject * g void JSBufferList::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { Base::finishCreation(vm); - - putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "length"_s), - JSC::CustomGetterSetter::create(vm, JSBufferList_getLength, nullptr), - JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); } JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, size_t n) @@ -49,7 +45,8 @@ JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGloba return throwTypeError(lexicalGlobalObject, throwScope, "concat can only be called when all buffers are Uint8Array"_s); } if (UNLIKELY(array->byteLength() > n)) { - return throwRangeError(lexicalGlobalObject, throwScope, "specified size too small to fit all buffers"_s); + throwNodeRangeError(lexicalGlobalObject, throwScope, "specified size too small to fit all buffers"_s); + return {}; } RELEASE_AND_RETURN(throwScope, array); } @@ -68,7 +65,8 @@ JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGloba } const size_t length = array->byteLength(); if (UNLIKELY(i + length > n)) { - return throwRangeError(lexicalGlobalObject, throwScope, "specified size too small to fit all buffers"_s); + throwNodeRangeError(lexicalGlobalObject, throwScope, "specified size too small to fit all buffers"_s); + return {}; } if (UNLIKELY(!uint8Array->setFromTypedArray(lexicalGlobalObject, i, array, 0, length, JSC::CopyType::Unobservable))) { return throwOutOfMemoryError(lexicalGlobalObject, throwScope); @@ -126,7 +124,7 @@ JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalG size_t n = total; if (n == len) { - m_deque.removeFirst(); + this->removeFirst(); RELEASE_AND_RETURN(throwScope, str); } if (n < len) { @@ -152,7 +150,7 @@ JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalG } if (!ropeBuilder.append(str)) return throwOutOfMemoryError(lexicalGlobalObject, throwScope); - m_deque.removeFirst(); + this->removeFirst(); if (n == len) break; n -= len; @@ -178,7 +176,7 @@ JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalG size_t n = total; if (n == len) { - m_deque.removeFirst(); + this->removeFirst(); RELEASE_AND_RETURN(throwScope, array); } if (n < len) { @@ -219,7 +217,7 @@ JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalG if (UNLIKELY(!uint8Array->setFromTypedArray(lexicalGlobalObject, offset, array, 0, len, JSC::CopyType::Unobservable))) { return throwOutOfMemoryError(lexicalGlobalObject, throwScope); } - m_deque.removeFirst(); + this->removeFirst(); if (n == len) { offset += len; break; @@ -253,8 +251,10 @@ void JSBufferList::visitChildrenImpl(JSCell* cell, Visitor& visitor) JSBufferList* buffer = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(buffer, info()); Base::visitChildren(buffer, visitor); + buffer->lock(); for (auto& val : buffer->m_deque) visitor.append(val); + buffer->unlock(); } DEFINE_VISIT_CHILDREN(JSBufferList); @@ -409,6 +409,7 @@ static const HashTableValue JSBufferListPrototypeTableValues[] { "concat"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferListPrototypeFunction_concat, 1 } }, { "join"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferListPrototypeFunction_join, 1 } }, { "consume"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferListPrototypeFunction_consume, 2 } }, + { "length"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, JSBufferList_getLength, 0 } }, }; void JSBufferListPrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalThis) @@ -448,7 +449,6 @@ void JSBufferListConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* const ClassInfo JSBufferListConstructor::s_info = { "BufferList"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBufferListConstructor) }; - JSValue getBufferList(Zig::GlobalObject* globalObject) { return reinterpret_cast(globalObject)->JSBufferList(); diff --git a/src/bun.js/bindings/JSBufferList.h b/src/bun.js/bindings/JSBufferList.h index 93fe66092fad2..78b6ddf60e7a7 100644 --- a/src/bun.js/bindings/JSBufferList.h +++ b/src/bun.js/bindings/JSBufferList.h @@ -50,25 +50,43 @@ class JSBufferList : public JSC::JSNonFinalObject { inline size_t length() { return m_deque.size(); } void push(JSC::VM& vm, JSC::JSValue v) { + this->lock(); m_deque.append(WriteBarrier()); + this->unlock(); + m_deque.last().set(vm, this, v); } void unshift(JSC::VM& vm, JSC::JSValue v) { + this->lock(); m_deque.prepend(WriteBarrier()); + this->unlock(); + m_deque.first().set(vm, this, v); } + void removeFirst() + { + this->lock(); + m_deque.removeFirst(); + this->unlock(); + } JSC::JSValue shift() { if (UNLIKELY(length() == 0)) return JSC::jsUndefined(); auto v = m_deque.first().get(); + + this->lock(); m_deque.removeFirst(); + this->unlock(); + return v; } void clear() { + this->lock(); m_deque.clear(); + this->unlock(); } JSC::JSValue first() { @@ -83,8 +101,12 @@ class JSBufferList : public JSC::JSNonFinalObject { JSC::JSValue _getBuffer(JSC::VM&, JSC::JSGlobalObject*, size_t); JSC::JSValue _getString(JSC::VM&, JSC::JSGlobalObject*, size_t); + void lock() { m_lock.lock(); } + void unlock() { m_lock.unlock(); } + private: Deque> m_deque; + WTF::Lock m_lock; }; class JSBufferListPrototype : public JSC::JSNonFinalObject { diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index f87be457f21be..2d5f0e7fce9c1 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -259,7 +259,7 @@ void JSBundlerPlugin::finishCreation(JSC::VM& vm) auto* globalObject = init.owner->globalObject(); init.set( - JSC::JSFunction::create(vm, WebCore::bundlerPluginRunOnLoadPluginsCodeGenerator(vm), globalObject)); + JSC::JSFunction::create(vm, globalObject, WebCore::bundlerPluginRunOnLoadPluginsCodeGenerator(vm), globalObject)); }); this->onResolveFunction.initLater( @@ -268,7 +268,7 @@ void JSBundlerPlugin::finishCreation(JSC::VM& vm) auto* globalObject = init.owner->globalObject(); init.set( - JSC::JSFunction::create(vm, WebCore::bundlerPluginRunOnResolvePluginsCodeGenerator(vm), globalObject)); + JSC::JSFunction::create(vm, globalObject, WebCore::bundlerPluginRunOnResolvePluginsCodeGenerator(vm), globalObject)); }); this->setupFunction.initLater( @@ -277,7 +277,7 @@ void JSBundlerPlugin::finishCreation(JSC::VM& vm) auto* globalObject = init.owner->globalObject(); init.set( - JSC::JSFunction::create(vm, WebCore::bundlerPluginRunSetupFunctionCodeGenerator(vm), globalObject)); + JSC::JSFunction::create(vm, globalObject, WebCore::bundlerPluginRunSetupFunctionCodeGenerator(vm), globalObject)); }); this->putDirect(vm, Identifier::fromString(vm, String("onLoad"_s)), jsUndefined(), 0); diff --git a/src/bun.js/bindings/JSCTaskScheduler.cpp b/src/bun.js/bindings/JSCTaskScheduler.cpp index 9f280e061c8de..22125a19bf3ca 100644 --- a/src/bun.js/bindings/JSCTaskScheduler.cpp +++ b/src/bun.js/bindings/JSCTaskScheduler.cpp @@ -15,61 +15,64 @@ extern "C" void Bun__eventLoop__incrementRefConcurrently(void* bunVM, int delta) class JSCDeferredWorkTask { public: - JSCDeferredWorkTask(Ticket ticket, Task&& task) - : ticket(ticket) + JSCDeferredWorkTask(Ref ticket, Task&& task) + : ticket(WTFMove(ticket)) , task(WTFMove(task)) { } - Ticket ticket; + Ref ticket; Task task; + ~JSCDeferredWorkTask() + { + } + + JSC::VM& vm() const { return ticket->scriptExecutionOwner()->vm(); } WTF_MAKE_ISO_ALLOCATED(JSCDeferredWorkTask); }; WTF_MAKE_ISO_ALLOCATED_IMPL(JSCDeferredWorkTask); -static JSC::VM& getVM(Ticket ticket) +static JSC::VM& getVM(Ticket& ticket) { - return ticket->scriptExecutionOwner.get()->vm(); + return ticket->scriptExecutionOwner()->vm(); } -void JSCTaskScheduler::onAddPendingWork(std::unique_ptr ticket, JSC::DeferredWorkTimer::WorkKind kind) +void JSCTaskScheduler::onAddPendingWork(WebCore::JSVMClientData* clientData, Ref&& ticket, JSC::DeferredWorkTimer::WorkType kind) { - JSC::VM& vm = getVM(ticket.get()); - auto clientData = WebCore::clientData(vm); auto& scheduler = clientData->deferredWorkTimer; Locker holder { scheduler.m_lock }; - if (kind != DeferredWorkTimer::WorkKind::Other) { - + if (kind == DeferredWorkTimer::WorkType::ImminentlyScheduled) { Bun__eventLoop__incrementRefConcurrently(clientData->bunVM, 1); scheduler.m_pendingTicketsKeepingEventLoopAlive.add(WTFMove(ticket)); } else { scheduler.m_pendingTicketsOther.add(WTFMove(ticket)); } } -void JSCTaskScheduler::onScheduleWorkSoon(Ticket ticket, Task&& task) +void JSCTaskScheduler::onScheduleWorkSoon(WebCore::JSVMClientData* clientData, Ticket ticket, Task&& task) { - auto* job = new JSCDeferredWorkTask(ticket, WTFMove(task)); - Bun__queueJSCDeferredWorkTaskConcurrently(WebCore::clientData(getVM(ticket))->bunVM, job); + auto* job = new JSCDeferredWorkTask(*ticket, WTFMove(task)); + Bun__queueJSCDeferredWorkTaskConcurrently(clientData->bunVM, job); } -void JSCTaskScheduler::onCancelPendingWork(Ticket ticket) +void JSCTaskScheduler::onCancelPendingWork(WebCore::JSVMClientData* clientData, Ticket ticket) { - auto& scheduler = WebCore::clientData(getVM(ticket))->deferredWorkTimer; + auto* bunVM = clientData->bunVM; + auto& scheduler = clientData->deferredWorkTimer; Locker holder { scheduler.m_lock }; - bool isKeepingEventLoopAlive = scheduler.m_pendingTicketsKeepingEventLoopAlive.removeIf([ticket](const auto& pendingTicket) { - return pendingTicket.get() == ticket; + bool isKeepingEventLoopAlive = scheduler.m_pendingTicketsKeepingEventLoopAlive.removeIf([ticket](auto pendingTicket) { + return pendingTicket.ptr() == ticket; }); + // -- At this point, ticket may be an invalid pointer. if (isKeepingEventLoopAlive) { holder.unlockEarly(); - JSC::VM& vm = getVM(ticket); - Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(vm)->bunVM, -1); + Bun__eventLoop__incrementRefConcurrently(bunVM, -1); } else { - scheduler.m_pendingTicketsOther.removeIf([ticket](const auto& pendingTicket) { - return pendingTicket.get() == ticket; + scheduler.m_pendingTicketsOther.removeIf([ticket](auto pendingTicket) { + return pendingTicket.ptr() == ticket; }); } } @@ -86,7 +89,7 @@ static void runPendingWork(void* bunVM, Bun::JSCTaskScheduler& scheduler, JSCDef holder.unlockEarly(); if (pendingTicket && !pendingTicket->isCancelled()) { - job->task(job->ticket); + job->task(job->ticket.ptr()); } delete job; @@ -94,7 +97,7 @@ static void runPendingWork(void* bunVM, Bun::JSCTaskScheduler& scheduler, JSCDef extern "C" void Bun__runDeferredWork(Bun::JSCDeferredWorkTask* job) { - auto& vm = getVM(job->ticket); + auto& vm = job->vm(); auto clientData = WebCore::clientData(vm); runPendingWork(clientData->bunVM, clientData->deferredWorkTimer, job); diff --git a/src/bun.js/bindings/JSCTaskScheduler.h b/src/bun.js/bindings/JSCTaskScheduler.h index 3257eb9c88ad3..45a6d3888de2f 100644 --- a/src/bun.js/bindings/JSCTaskScheduler.h +++ b/src/bun.js/bindings/JSCTaskScheduler.h @@ -1,5 +1,9 @@ #pragma once +namespace WebCore { +class JSVMClientData; +} + #include namespace Bun { @@ -12,14 +16,14 @@ class JSCTaskScheduler { { } - static void onAddPendingWork(std::unique_ptr ticket, JSC::DeferredWorkTimer::WorkKind kind); - static void onScheduleWorkSoon(JSC::DeferredWorkTimer::Ticket ticket, JSC::DeferredWorkTimer::Task&& task); - static void onCancelPendingWork(JSC::DeferredWorkTimer::Ticket ticket); + static void onAddPendingWork(WebCore::JSVMClientData* clientData, Ref&& ticket, JSC::DeferredWorkTimer::WorkType kind); + static void onScheduleWorkSoon(WebCore::JSVMClientData* clientData, JSC::DeferredWorkTimer::Ticket ticket, JSC::DeferredWorkTimer::Task&& task); + static void onCancelPendingWork(WebCore::JSVMClientData* clientData, JSC::DeferredWorkTimer::Ticket ticket); public: Lock m_lock; - HashSet> m_pendingTicketsKeepingEventLoopAlive; - HashSet> m_pendingTicketsOther; + HashSet> m_pendingTicketsKeepingEventLoopAlive; + HashSet> m_pendingTicketsOther; }; } \ No newline at end of file diff --git a/src/bun.js/bindings/JSDOMExceptionHandling.cpp b/src/bun.js/bindings/JSDOMExceptionHandling.cpp index e3f99710637ce..9531f9594c96b 100644 --- a/src/bun.js/bindings/JSDOMExceptionHandling.cpp +++ b/src/bun.js/bindings/JSDOMExceptionHandling.cpp @@ -21,6 +21,8 @@ #include "root.h" +#include "ErrorCode.h" + #include "DOMException.h" #include "JSDOMException.h" #include "JSDOMExceptionHandling.h" @@ -148,37 +150,43 @@ JSValue createDOMException(JSGlobalObject* lexicalGlobalObject, ExceptionCode ec return jsUndefined(); switch (ec) { - case ExistingExceptionError: + case ExceptionCode::ExistingExceptionError: return jsUndefined(); // FIXME: Handle other WebIDL exception types. - case TypeError: + case ExceptionCode::TypeError: if (message.isEmpty()) return createTypeError(lexicalGlobalObject); return createTypeError(lexicalGlobalObject, message); - case RangeError: + case ExceptionCode::RangeError: if (message.isEmpty()) return createRangeError(lexicalGlobalObject, "Bad value"_s); return createRangeError(lexicalGlobalObject, message); - case JSSyntaxError: + case ExceptionCode::JSSyntaxError: if (message.isEmpty()) return createSyntaxError(lexicalGlobalObject); return createSyntaxError(lexicalGlobalObject, message); - case StackOverflowError: + case ExceptionCode::StackOverflowError: return createStackOverflowError(lexicalGlobalObject); - case OutOfMemoryError: + case ExceptionCode::OutOfMemoryError: return createOutOfMemoryError(lexicalGlobalObject); + case ExceptionCode::InvalidThisError: + return Bun::createInvalidThisError(lexicalGlobalObject, message.isEmpty() ? "Expected this to be of a different type"_s : message); + + case ExceptionCode::InvalidURLError: + return Bun::createError(lexicalGlobalObject, Bun::ErrorCode::ERR_INVALID_URL, message.isEmpty() ? "Invalid URL"_s : message); + default: { // FIXME: All callers to createDOMException need to pass in the correct global object. // For now, we're going to assume the lexicalGlobalObject. Which is wrong in cases like this: // frames[0].document.createElement(null, null); // throws an exception which should have the subframe's prototypes. // https://bugs.webkit.org/show_bug.cgi?id=222229 - JSDOMGlobalObject* globalObject = JSC::jsCast(lexicalGlobalObject); + JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(lexicalGlobalObject); JSValue errorObject = toJS(lexicalGlobalObject, globalObject, DOMException::create(ec, message)); ASSERT(errorObject); @@ -200,75 +208,75 @@ void propagateExceptionSlowPath(JSC::JSGlobalObject& lexicalGlobalObject, JSC::T throwException(&lexicalGlobalObject, throwScope, createDOMException(lexicalGlobalObject, WTFMove(exception))); } -static JSC::EncodedJSValue throwTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const String& errorMessage) +static EncodedJSValue throwTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const String& errorMessage) { return throwVMTypeError(&lexicalGlobalObject, scope, errorMessage); } -template static String makeArgumentTypeErrorMessage(unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName, StringTypes... strings) +template static String makeArgumentTypeErrorMessage(unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral interfaceName, ASCIILiteral functionName, StringTypes... strings) { return makeString( - "Argument ", argumentIndex + 1, " ('", argumentName, "') to ", - functionName ? std::make_tuple(interfaceName, ".", functionName) : std::make_tuple("the ", interfaceName, " constructor"), - " must be ", strings...); + "Argument "_s, argumentIndex + 1, " ('"_s, argumentName, "') to "_s, + functionName ? std::make_tuple(interfaceName, "."_s, functionName) : std::make_tuple("the "_s, interfaceName, " constructor"_s), + " must be "_s, strings...); } void throwNotSupportedError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral message) { scope.assertNoExceptionExceptTermination(); - throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, NotSupportedError, message)); + throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, ExceptionCode::NotSupportedError, message)); } void throwInvalidStateError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral message) { scope.assertNoExceptionExceptTermination(); - throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, InvalidStateError, message)); + throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, ExceptionCode::InvalidStateError, message)); } void throwSecurityError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const String& message) { scope.assertNoExceptionExceptTermination(); - throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, SecurityError, message)); + throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, ExceptionCode::SecurityError, message)); } -JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedValues) +JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedValues) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "one of: ", expectedValues)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "one of: "_s, expectedValues)); } -JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName) +JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral interfaceName, ASCIILiteral functionName) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "a function")); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "a function"_s)); } -JSC::EncodedJSValue throwArgumentMustBeObjectError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName) +JSC::EncodedJSValue throwArgumentMustBeObjectError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral interfaceName, ASCIILiteral functionName) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "an object")); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "an object"_s)); } -JSC::EncodedJSValue throwArgumentTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedType) +JSC::EncodedJSValue throwArgumentTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedType) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "an instance of ", expectedType)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "an instance of "_s, expectedType)); } -void throwAttributeTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const char* interfaceName, const char* attributeName, const char* expectedType) +void throwAttributeTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral interfaceName, ASCIILiteral attributeName, ASCIILiteral expectedType) { - throwTypeError(lexicalGlobalObject, scope, makeString("The ", interfaceName, '.', attributeName, " attribute must be an instance of ", expectedType)); + throwTypeError(lexicalGlobalObject, scope, makeString("The "_s, interfaceName, '.', attributeName, " attribute must be an instance of "_s, expectedType)); } -JSC::EncodedJSValue throwRequiredMemberTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const char* memberName, const char* dictionaryName, const char* expectedType) +JSC::EncodedJSValue throwRequiredMemberTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral memberName, ASCIILiteral dictionaryName, ASCIILiteral expectedType) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeString("Member ", dictionaryName, '.', memberName, " is required and must be an instance of ", expectedType)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeString("Member "_s, dictionaryName, '.', memberName, " is required and must be an instance of "_s, expectedType)); } -JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const char* interfaceName) +JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral interfaceName) { - return throwVMError(&lexicalGlobalObject, scope, createReferenceError(&lexicalGlobalObject, makeString(interfaceName, " constructor associated execution context is unavailable"))); + return throwVMError(&lexicalGlobalObject, scope, createReferenceError(&lexicalGlobalObject, makeString(interfaceName, " constructor associated execution context is unavailable"_s))); } void throwSequenceTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { - throwTypeError(lexicalGlobalObject, scope, "Value is not a sequence"_s); + Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Value is not a sequence"_s); } void throwNonFiniteTypeError(JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) @@ -283,22 +291,23 @@ JSC::EncodedJSValue rejectPromiseWithGetterTypeError(JSC::JSGlobalObject& lexica String makeThisTypeErrorMessage(const char* interfaceName, const char* functionName) { - return makeString("Can only call ", interfaceName, '.', functionName, " on instances of ", interfaceName); + auto interfaceNameSpan = span(interfaceName); + return makeString("Can only call "_s, interfaceNameSpan, '.', span(functionName), " on instances of "_s, interfaceNameSpan); } -String makeUnsupportedIndexedSetterErrorMessage(const char* interfaceName) +String makeUnsupportedIndexedSetterErrorMessage(ASCIILiteral interfaceName) { - return makeString("Failed to set an indexed property on ", interfaceName, ": Indexed property setter is not supported."); + return makeString("Failed to set an indexed property on "_s, interfaceName, ": Indexed property setter is not supported."_s); } EncodedJSValue throwThisTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const char* interfaceName, const char* functionName) { - return throwTypeError(lexicalGlobalObject, scope, makeThisTypeErrorMessage(interfaceName, functionName)); + return JSValue::encode(scope.throwException(&lexicalGlobalObject, Bun::createInvalidThisError(&lexicalGlobalObject, makeThisTypeErrorMessage(interfaceName, functionName)))); } JSC::EncodedJSValue rejectPromiseWithThisTypeError(DeferredPromise& promise, const char* interfaceName, const char* methodName) { - promise.reject(TypeError, makeThisTypeErrorMessage(interfaceName, methodName)); + promise.reject(ExceptionCode::InvalidThisError, makeThisTypeErrorMessage(interfaceName, methodName)); return JSValue::encode(jsUndefined()); } @@ -310,13 +319,13 @@ JSC::EncodedJSValue rejectPromiseWithThisTypeError(JSC::JSGlobalObject& lexicalG void throwDOMSyntaxError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral message) { scope.assertNoExceptionExceptTermination(); - throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, SyntaxError, message)); + throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, ExceptionCode::SyntaxError, message)); } void throwDataCloneError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { scope.assertNoExceptionExceptTermination(); - throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, DataCloneError)); + throwException(&lexicalGlobalObject, scope, createDOMException(&lexicalGlobalObject, ExceptionCode::DataCloneError)); } } // namespace WebCore diff --git a/src/bun.js/bindings/JSDOMExceptionHandling.h b/src/bun.js/bindings/JSDOMExceptionHandling.h index 97153d5aa9fd4..d4a3c7cd2c7f7 100644 --- a/src/bun.js/bindings/JSDOMExceptionHandling.h +++ b/src/bun.js/bindings/JSDOMExceptionHandling.h @@ -23,8 +23,6 @@ #pragma once -#include "root.h" - #include "ExceptionDetails.h" #include "ExceptionOr.h" #include @@ -38,7 +36,7 @@ namespace WebCore { class CachedScript; class DeferredPromise; -void throwAttributeTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, const char* interfaceName, const char* attributeName, const char* expectedType); +void throwAttributeTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral interfaceName, ASCIILiteral attributeName, ASCIILiteral expectedType); void throwDataCloneError(JSC::JSGlobalObject&, JSC::ThrowScope&); void throwDOMSyntaxError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral); // Not the same as a JavaScript syntax error. @@ -48,15 +46,15 @@ void throwNotSupportedError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral void throwSecurityError(JSC::JSGlobalObject&, JSC::ThrowScope&, const String& message); WEBCORE_EXPORT void throwSequenceTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&); -WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedValues); -WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName); -WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeObjectError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName); -WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedType); -WEBCORE_EXPORT JSC::EncodedJSValue throwRequiredMemberTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, const char* memberName, const char* dictionaryName, const char* expectedType); -JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC::JSGlobalObject&, JSC::ThrowScope&, const char* interfaceName); +WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedValues); +WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName); +WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeObjectError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName); +WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedType); +WEBCORE_EXPORT JSC::EncodedJSValue throwRequiredMemberTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral memberName, ASCIILiteral dictionaryName, ASCIILiteral expectedType); +JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral interfaceName); String makeThisTypeErrorMessage(const char* interfaceName, const char* attributeName); -String makeUnsupportedIndexedSetterErrorMessage(const char* interfaceName); +String makeUnsupportedIndexedSetterErrorMessage(ASCIILiteral interfaceName); WEBCORE_EXPORT JSC::EncodedJSValue throwThisTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, const char* interfaceName, const char* functionName); @@ -68,6 +66,7 @@ String retrieveErrorMessageWithoutName(JSC::JSGlobalObject&, JSC::VM&, JSC::JSVa String retrieveErrorMessage(JSC::JSGlobalObject&, JSC::VM&, JSC::JSValue exception, JSC::CatchScope&); WEBCORE_EXPORT void reportException(JSC::JSGlobalObject*, JSC::JSValue exception, CachedScript* = nullptr, bool = false); WEBCORE_EXPORT void reportException(JSC::JSGlobalObject*, JSC::Exception*, CachedScript* = nullptr, bool = false, ExceptionDetails* = nullptr); +WEBCORE_EXPORT void reportExceptionIfJSDOMWindow(JSC::JSGlobalObject*, JSC::JSValue exception); void reportCurrentException(JSC::JSGlobalObject*); JSC::JSValue createDOMException(JSC::JSGlobalObject&, Exception&&); diff --git a/src/bun.js/bindings/JSDOMFile.cpp b/src/bun.js/bindings/JSDOMFile.cpp index 37536729d5445..212cbeb0c09bd 100644 --- a/src/bun.js/bindings/JSDOMFile.cpp +++ b/src/bun.js/bindings/JSDOMFile.cpp @@ -7,8 +7,8 @@ using namespace JSC; -extern "C" void* JSDOMFile__construct(JSC::JSGlobalObject*, JSC::CallFrame* callframe); -extern "C" bool JSDOMFile__hasInstance(EncodedJSValue, JSC::JSGlobalObject*, EncodedJSValue); +extern "C" SYSV_ABI void* JSDOMFile__construct(JSC::JSGlobalObject*, JSC::CallFrame* callframe); +extern "C" SYSV_ABI bool JSDOMFile__hasInstance(EncodedJSValue, JSC::JSGlobalObject*, EncodedJSValue); // TODO: make this inehrit from JSBlob instead of InternalFunction // That will let us remove this hack for [Symbol.hasInstance] and fix the prototype chain. @@ -63,7 +63,7 @@ class JSDOMFile : public JSC::InternalFunction { return JSDOMFile__hasInstance(JSValue::encode(object), globalObject, JSValue::encode(value)); } - static JSC::EncodedJSValue construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) + static JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) { Zig::GlobalObject* globalObject = reinterpret_cast(lexicalGlobalObject); JSC::VM& vm = globalObject->vm(); @@ -93,7 +93,7 @@ class JSDOMFile : public JSC::InternalFunction { WebCore::JSBlob::create(vm, globalObject, structure, ptr)); } - static EncodedJSValue call(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) + static JSC_HOST_CALL_ATTRIBUTES EncodedJSValue call(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) { auto scope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm()); throwTypeError(lexicalGlobalObject, scope, "Class constructor File cannot be invoked without 'new'"_s); diff --git a/src/bun.js/bindings/JSDOMWrapper.h b/src/bun.js/bindings/JSDOMWrapper.h index 4698f86d856ba..bff0179a4586a 100644 --- a/src/bun.js/bindings/JSDOMWrapper.h +++ b/src/bun.js/bindings/JSDOMWrapper.h @@ -84,6 +84,7 @@ class JSDOMWrapper : public JSDOMObject { using DOMWrapped = ImplementationClass; ImplementationClass& wrapped() const { return m_wrapped; } + Ref protectedWrapped() const { return m_wrapped; } static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSDOMWrapper, m_wrapped); } constexpr static bool hasCustomPtrTraits() { return !std::is_same_v>; }; diff --git a/src/bun.js/bindings/JSDOMWrapperCache.h b/src/bun.js/bindings/JSDOMWrapperCache.h index e06f2607d893c..7fa9a8d3e7dbd 100644 --- a/src/bun.js/bindings/JSDOMWrapperCache.h +++ b/src/bun.js/bindings/JSDOMWrapperCache.h @@ -221,7 +221,7 @@ template inline void setSubclassStructureIfNeeded(JSC::JSGlob auto* functionGlobalObject = JSC::getFunctionRealm(lexicalGlobalObject, newTarget); RETURN_IF_EXCEPTION(scope, void()); - auto* newTargetGlobalObject = JSC::jsCast(functionGlobalObject); + auto* newTargetGlobalObject = defaultGlobalObject(functionGlobalObject); auto* baseStructure = getDOMStructure(vm, *newTargetGlobalObject); auto* subclassStructure = JSC::InternalFunction::createSubclassStructure(lexicalGlobalObject, newTarget, baseStructure); RETURN_IF_EXCEPTION(scope, void()); diff --git a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp index 3f991ba5825ca..84e19282dc07d 100644 --- a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp +++ b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp @@ -348,7 +348,7 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject) Identifier::fromString(vm, BUN_CONFIG_VERBOSE_FETCH), JSC::CustomGetterSetter::create(vm, jsBunConfigVerboseFetchGetter, jsBunConfigVerboseFetchSetter), BUN_CONFIG_VERBOSE_FETCH_Attrs); #if OS(WINDOWS) - JSC::JSFunction* getSourceEvent = JSC::JSFunction::create(vm, processObjectInternalsWindowsEnvCodeGenerator(vm), globalObject); + JSC::JSFunction* getSourceEvent = JSC::JSFunction::create(vm, globalObject, processObjectInternalsWindowsEnvCodeGenerator(vm), globalObject); RETURN_IF_EXCEPTION(scope, {}); JSC::MarkedArgumentBuffer args; args.append(object); diff --git a/src/bun.js/bindings/JSFFIFunction.cpp b/src/bun.js/bindings/JSFFIFunction.cpp index ac4120c2d5e53..6f5d9dcf4c3b8 100644 --- a/src/bun.js/bindings/JSFFIFunction.cpp +++ b/src/bun.js/bindings/JSFFIFunction.cpp @@ -115,15 +115,18 @@ extern "C" void Bun__untrackFFIFunction(Zig::GlobalObject* globalObject, JSC::En } extern "C" JSC::EncodedJSValue Bun__CreateFFIFunctionValue(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong, bool addPtrField) { - auto* function = Bun__CreateFFIFunction(globalObject, symbolName, argCount, functionPointer, strong); if (addPtrField) { + auto* function = Zig::JSFFIFunction::createForFFI(globalObject->vm(), globalObject, argCount, symbolName != nullptr ? Zig::toStringCopy(*symbolName) : String(), reinterpret_cast(functionPointer)); auto& vm = globalObject->vm(); // We should only expose the "ptr" field when it's a JSCallback for bun:ffi. // Not for internal usages of this function type. // We should also consider a separate JSFunction type for our usage to not have this branch in the first place... function->putDirect(vm, JSC::Identifier::fromString(vm, String(MAKE_STATIC_STRING_IMPL("ptr"))), JSC::jsNumber(bitwise_cast(functionPointer)), JSC::PropertyAttribute::ReadOnly | 0); + + return JSC::JSValue::encode(function); } - return JSC::JSValue::encode(function); + + return Bun__CreateFFIFunctionWithDataValue(globalObject, symbolName, argCount, functionPointer, strong, nullptr); } namespace Zig { @@ -131,7 +134,7 @@ using namespace JSC; const ClassInfo JSFFIFunction::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSFFIFunction) }; -JSFFIFunction::JSFFIFunction(VM& vm, NativeExecutable* executable, JSGlobalObject* globalObject, Structure* structure, FFIFunction&& function) +JSFFIFunction::JSFFIFunction(VM& vm, NativeExecutable* executable, JSGlobalObject* globalObject, Structure* structure, CFFIFunction&& function) : Base(vm, executable, globalObject, structure) , m_function(WTFMove(function)) { @@ -157,11 +160,32 @@ void JSFFIFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigne JSFFIFunction* JSFFIFunction::create(VM& vm, Zig::GlobalObject* globalObject, unsigned length, const String& name, FFIFunction FFIFunction, Intrinsic intrinsic, NativeFunction nativeConstructor) { - NativeExecutable* executable = vm.getHostFunction(FFIFunction, ImplementationVisibility::Public, intrinsic, FFIFunction, nullptr, name); + Structure* structure = globalObject->FFIFunctionStructure(); + JSFFIFunction* function = new (NotNull, allocateCell(vm)) JSFFIFunction(vm, executable, globalObject, structure, reinterpret_cast(WTFMove(FFIFunction))); + function->finishCreation(vm, executable, length, name); + return function; +} +#if OS(WINDOWS) + +JSC_DEFINE_HOST_FUNCTION(JSFFIFunction::trampoline, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + const auto* function = jsCast(callFrame->jsCallee()); + return function->function()(globalObject, callFrame); +} + +#endif + +JSFFIFunction* JSFFIFunction::createForFFI(VM& vm, Zig::GlobalObject* globalObject, unsigned length, const String& name, CFFIFunction FFIFunction) +{ +#if OS(WINDOWS) + NativeExecutable* executable = vm.getHostFunction(trampoline, ImplementationVisibility::Public, NoIntrinsic, trampoline, nullptr, name); +#else + NativeExecutable* executable = vm.getHostFunction(FFIFunction, ImplementationVisibility::Public, NoIntrinsic, FFIFunction, nullptr, name); +#endif Structure* structure = globalObject->FFIFunctionStructure(); - JSFFIFunction* function = new (NotNull, allocateCell(vm)) JSFFIFunction(vm, executable, globalObject, structure, WTFMove(FFIFunction)); + JSFFIFunction* function = new (NotNull, allocateCell(vm)) JSFFIFunction(vm, executable, globalObject, structure, reinterpret_cast(WTFMove(FFIFunction))); function->finishCreation(vm, executable, length, name); return function; } diff --git a/src/bun.js/bindings/JSFFIFunction.h b/src/bun.js/bindings/JSFFIFunction.h index e186c730362da..078cb8073b240 100644 --- a/src/bun.js/bindings/JSFFIFunction.h +++ b/src/bun.js/bindings/JSFFIFunction.h @@ -20,7 +20,13 @@ namespace Zig { using namespace JSC; -using FFIFunction = JSC::EncodedJSValue (*)(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); +using FFIFunction = SYSV_ABI JSC::EncodedJSValue (*)(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); + +#if OS(WINDOWS) +using CFFIFunction = JSC::EncodedJSValue __attribute__((cdecl)) (*)(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); +#else +using CFFIFunction = FFIFunction; +#endif /** * Call a C function with low overhead, modeled after JSC::JSNativeStdFunction @@ -64,6 +70,7 @@ class JSFFIFunction final : public JSC::JSFunction { DECLARE_EXPORT_INFO; JS_EXPORT_PRIVATE static JSFFIFunction* create(VM&, Zig::GlobalObject*, unsigned length, const String& name, FFIFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor); + JS_EXPORT_PRIVATE static JSFFIFunction* createForFFI(VM&, Zig::GlobalObject*, unsigned length, const String& name, CFFIFunction); static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) { @@ -71,16 +78,22 @@ class JSFFIFunction final : public JSC::JSFunction { return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info()); } - const FFIFunction function() { return m_function; } + const CFFIFunction function() const { return m_function; } + +#if OS(WINDOWS) + + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES trampoline(JSGlobalObject* globalObject, CallFrame* callFrame); + +#endif void* dataPtr; private: - JSFFIFunction(VM&, NativeExecutable*, JSGlobalObject*, Structure*, FFIFunction&&); + JSFFIFunction(VM&, NativeExecutable*, JSGlobalObject*, Structure*, CFFIFunction&&); void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name); DECLARE_VISIT_CHILDREN; - FFIFunction m_function; + CFFIFunction m_function; }; } // namespace JSC diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index ec2229765d40f..699f695a451cf 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -1,5 +1,7 @@ #include "root.h" +#include "ErrorCode+List.h" +#include "JavaScriptCore/Error.h" #include "JSMockFunction.h" #include #include "ZigGlobalObject.h" @@ -23,6 +25,24 @@ #include #include #include "BunPlugin.h" +#include "AsyncContextFrame.h" +#include "ErrorCode.h" + +BUN_DECLARE_HOST_FUNCTION(JSMock__jsUseFakeTimers); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsUseRealTimers); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsNow); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsSetSystemTime); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsRestoreAllMocks); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsClearAllMocks); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsSpyOn); +BUN_DECLARE_HOST_FUNCTION(JSMock__jsMockFn); + +#define CHECK_IS_MOCK_FUNCTION(thisValue) \ + if (UNLIKELY(!thisObject)) { \ + scope.throwException(globalObject, createInvalidThisError(globalObject, thisValue, "Mock"_s)); \ + return {}; \ + } + namespace Bun { /** @@ -46,6 +66,26 @@ inline To tryJSDynamicCast(JSValue from) return jsDynamicCast(from.asCell()); } +/** + * intended to be used in an if statement as an abstraction over this double if statement + * + * if(jsValue) { + * if(auto value = jsDynamicCast(jsValue)) { + * ... + * } + * } + * + * the reason this is needed is because jsDynamicCast will segfault if given a zero JSValue + */ +template +inline To tryJSDynamicCast(JSC::WriteBarrier& from) +{ + if (UNLIKELY(!from)) + return nullptr; + + return jsDynamicCast(from.get()); +} + JSC_DECLARE_HOST_FUNCTION(jsMockFunctionCall); JSC_DECLARE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl); JSC_DECLARE_CUSTOM_GETTER(jsMockFunctionGetter_mock); @@ -68,41 +108,6 @@ JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementationCleanup); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementation); -// This is a stub. Exists so that the same code can be run in Jest -extern "C" JSC::EncodedJSValue JSMock__jsUseFakeTimers(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -{ - return JSValue::encode(callFrame->thisValue()); -} - -extern "C" JSC::EncodedJSValue JSMock__jsUseRealTimers(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -{ - globalObject->overridenDateNow = -1; - return JSValue::encode(callFrame->thisValue()); -} - -extern "C" JSC::EncodedJSValue JSMock__jsNow(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -{ - return JSValue::encode(jsNumber(globalObject->jsDateNow())); -} -extern "C" JSC::EncodedJSValue JSMock__jsSetSystemTime(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -{ - JSValue argument0 = callFrame->argument(0); - - if (auto* dateInstance = jsDynamicCast(argument0)) { - if (std::isnormal(dateInstance->internalNumber())) { - globalObject->overridenDateNow = dateInstance->internalNumber(); - } - return JSValue::encode(callFrame->thisValue()); - } - - if (argument0.isNumber() && argument0.asNumber() > 0) { - globalObject->overridenDateNow = argument0.asNumber(); - } - - globalObject->overridenDateNow = -1; - return JSValue::encode(callFrame->thisValue()); -} - uint64_t JSMockModule::s_nextInvocationId = 0; // This is taken from JSWeakSet @@ -244,7 +249,10 @@ class JSMockFunction : public JSC::InternalFunction { } DECLARE_INFO; + DECLARE_VISIT_CHILDREN; + template void visitAdditionalChildren(Visitor&); + DECLARE_VISIT_OUTPUT_CONSTRAINTS; JSC::LazyProperty mock; // three pointers to implementation objects @@ -268,6 +276,11 @@ class JSMockFunction : public JSC::InternalFunction { static constexpr unsigned SpyAttributeESModuleNamespace = 1 << 30; + JSString* jsName() + { + return m_originalName.get(); + } + void setName(const WTF::String& name) { auto& vm = this->vm(); @@ -290,7 +303,10 @@ class JSMockFunction : public JSC::InternalFunction { this->putDirect(vm, vm.propertyNames->length, (lengthJSValue), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly); } } else if (auto* fn = jsDynamicCast(value)) { - nameToUse = fn->get(global, vm.propertyNames->name).toWTFString(global); + JSValue nameValue = fn->get(global, vm.propertyNames->name); + if (!catcher.exception()) { + nameToUse = nameValue.toWTFString(global); + } } else if (auto* fn = jsDynamicCast(value)) { nameToUse = fn->name(); } else { @@ -326,6 +342,7 @@ class JSMockFunction : public JSC::InternalFunction { this->instances.clear(); this->returnValues.clear(); this->contexts.clear(); + this->invocationCallOrder.clear(); if (this->mock.isInitialized()) { this->initMock(); @@ -352,8 +369,9 @@ class JSMockFunction : public JSC::InternalFunction { // Reset the spy back to the original value. if (this->spyAttributes & SpyAttributeESModuleNamespace) { - auto* moduleNamespaceObject = jsCast(target); - moduleNamespaceObject->overrideExportValue(moduleNamespaceObject->globalObject(), this->spyIdentifier, implValue); + if (auto* moduleNamespaceObject = tryJSDynamicCast(target)) { + moduleNamespaceObject->overrideExportValue(moduleNamespaceObject->globalObject(), this->spyIdentifier, implValue); + } } else { target->putDirect(this->vm(), this->spyIdentifier, implValue, this->spyAttributes); } @@ -431,11 +449,10 @@ class JSMockFunction : public JSC::InternalFunction { }; template -void JSMockFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor) +void JSMockFunction::visitAdditionalChildren(Visitor& visitor) { - JSMockFunction* fn = jsCast(cell); + JSMockFunction* fn = this; ASSERT_GC_OBJECT_INHERITS(fn, info()); - Base::visitChildren(fn, visitor); visitor.append(fn->implementation); visitor.append(fn->tail); @@ -448,14 +465,34 @@ void JSMockFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(fn->spyOriginal); fn->mock.visit(visitor); } + +template +void JSMockFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSMockFunction* fn = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(fn, info()); + Base::visitChildren(fn, visitor); + fn->visitAdditionalChildren(visitor); +} + +template +void JSMockFunction::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor) +{ + JSMockFunction* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + thisObject->visitAdditionalChildren(visitor); +} + DEFINE_VISIT_CHILDREN(JSMockFunction); +DEFINE_VISIT_ADDITIONAL_CHILDREN(JSMockFunction); +DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSMockFunction); static void pushImpl(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSMockImplementation::Kind kind, JSValue value) { Zig::GlobalObject* globalObject = jsCast(jsGlobalObject); auto& vm = globalObject->vm(); - if (auto* current = tryJSDynamicCast(fn->fallbackImplmentation.get())) { + if (auto* current = tryJSDynamicCast(fn->fallbackImplmentation)) { current->underlyingValue.set(vm, current, value); current->kind = kind; return; @@ -463,7 +500,7 @@ static void pushImpl(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSMockI JSMockImplementation* impl = JSMockImplementation::create(globalObject, globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), kind, value, false); fn->fallbackImplmentation.set(vm, fn, impl); - if (auto* tail = tryJSDynamicCast(fn->tail.get())) { + if (auto* tail = tryJSDynamicCast(fn->tail)) { tail->nextValueOrSentinel.set(vm, tail, impl); } else { fn->implementation.set(vm, fn, impl); @@ -477,10 +514,10 @@ static void pushImplOnce(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSM JSMockImplementation* impl = JSMockImplementation::create(globalObject, globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), kind, value, true); - if (!fn->implementation.get()) { + if (!fn->implementation) { fn->implementation.set(vm, fn, impl); } - if (auto* tail = tryJSDynamicCast(fn->tail.get())) { + if (auto* tail = tryJSDynamicCast(fn->tail)) { tail->nextValueOrSentinel.set(vm, tail, impl); } else { fn->implementation.set(vm, fn, impl); @@ -582,12 +619,6 @@ extern "C" void JSMock__resetSpies(Zig::GlobalObject* globalObject) globalObject->mockModule.activeSpies.clear(); } -extern "C" JSC::EncodedJSValue JSMock__jsRestoreAllMocks(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe) -{ - JSMock__resetSpies(jsCast(globalObject)); - return JSValue::encode(jsUndefined()); -} - extern "C" void JSMock__clearAllMocks(Zig::GlobalObject* globalObject) { if (!globalObject->mockModule.activeMocks) { @@ -612,131 +643,6 @@ extern "C" void JSMock__clearAllMocks(Zig::GlobalObject* globalObject) } } -extern "C" JSC::EncodedJSValue JSMock__jsClearAllMocks(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe) -{ - JSMock__clearAllMocks(jsCast(globalObject)); - return JSValue::encode(jsUndefined()); -} - -extern "C" JSC::EncodedJSValue JSMock__jsSpyOn(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callframe) -{ - auto& vm = lexicalGlobalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* globalObject = jsDynamicCast(lexicalGlobalObject); - if (UNLIKELY(!globalObject)) { - throwVMError(globalObject, scope, "Cannot run spyOn from a different global context"_s); - return {}; - } - - JSValue objectValue = callframe->argument(0); - JSValue propertyKeyValue = callframe->argument(1); - - if (callframe->argumentCount() < 2 || !objectValue.isObject()) { - throwVMError(globalObject, scope, "spyOn(target, prop) expects a target object and a property key"_s); - return {}; - } - - PropertyName propertyKey = propertyKeyValue.toPropertyKey(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - if (propertyKey.isNull()) { - throwVMError(globalObject, scope, "spyOn(target, prop) expects a property key"_s); - return {}; - } - - JSC::JSObject* object = objectValue.getObject(); - if (object->type() == JSC::JSType::GlobalProxyType) - object = jsCast(object)->target(); - - JSC::PropertySlot slot(object, JSC::PropertySlot::InternalMethodType::HasProperty); - bool hasValue = object->getPropertySlot(globalObject, propertyKey, slot); - - // easymode: regular property or missing property - if (!hasValue || slot.isValue()) { - JSValue value = jsUndefined(); - if (hasValue) { - if (UNLIKELY(slot.isTaintedByOpaqueObject())) { - // if it's a Proxy or JSModuleNamespaceObject - value = object->get(globalObject, propertyKey); - } else { - value = slot.getValue(globalObject, propertyKey); - } - - if (jsDynamicCast(value)) { - return JSValue::encode(value); - } - } - - auto* mock = JSMockFunction::create(vm, globalObject, globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), CallbackKind::GetterSetter); - mock->spyTarget = JSC::Weak(object, &weakValueHandleOwner(), nullptr); - mock->spyIdentifier = propertyKey.isSymbol() ? Identifier::fromUid(vm, propertyKey.uid()) : Identifier::fromString(vm, propertyKey.publicName()); - mock->spyAttributes = hasValue ? slot.attributes() : 0; - unsigned attributes = 0; - - if (hasValue && ((slot.attributes() & PropertyAttribute::Function) != 0 || (value.isCell() && value.isCallable()))) { - if (hasValue) - attributes = slot.attributes(); - - mock->copyNameAndLength(vm, globalObject, value); - - if (JSModuleNamespaceObject* moduleNamespaceObject = jsDynamicCast(object)) { - moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock); - mock->spyAttributes |= JSMockFunction::SpyAttributeESModuleNamespace; - } else { - object->putDirect(vm, propertyKey, mock, attributes); - } - - RETURN_IF_EXCEPTION(scope, {}); - - pushImpl(mock, globalObject, JSMockImplementation::Kind::Call, value); - } else { - if (hasValue) - attributes = slot.attributes(); - - attributes |= PropertyAttribute::Accessor; - - if (JSModuleNamespaceObject* moduleNamespaceObject = jsDynamicCast(object)) { - moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock); - mock->spyAttributes |= JSMockFunction::SpyAttributeESModuleNamespace; - } else { - object->putDirectAccessor(globalObject, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes); - } - - // mock->setName(propertyKey.publicName()); - RETURN_IF_EXCEPTION(scope, {}); - - pushImpl(mock, globalObject, JSMockImplementation::Kind::ReturnValue, value); - } - - mock->spyOriginal.set(vm, mock, value); - - { - if (!globalObject->mockModule.activeSpies) { - ActiveSpySet* activeSpies = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); - globalObject->mockModule.activeSpies.set(vm, activeSpies); - } - ActiveSpySet* activeSpies = jsCast(globalObject->mockModule.activeSpies.get()); - activeSpies->add(vm, mock, mock); - } - - { - if (!globalObject->mockModule.activeMocks) { - ActiveSpySet* activeMocks = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); - globalObject->mockModule.activeMocks.set(vm, activeMocks); - } - ActiveSpySet* activeMocks = jsCast(globalObject->mockModule.activeMocks.get()); - activeMocks->add(vm, mock, mock); - } - - return JSValue::encode(mock); - } - - // hardmode: accessor property - throwVMError(globalObject, scope, "spyOn(target, prop) does not support accessor properties yet"_s); - return {}; -} - JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject) { JSMockModule mock; @@ -867,14 +773,14 @@ extern Structure* createMockResultStructure(JSC::VM& vm, JSC::JSGlobalObject* gl structure = structure->addPropertyTransition( vm, structure, - JSC::Identifier::fromString(vm, "type"_s), + vm.propertyNames->type, 0, offset); structure = structure->addPropertyTransition( vm, structure, - JSC::Identifier::fromString(vm, "value"_s), + vm.propertyNames->value, 0, offset); return structure; } @@ -970,7 +876,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje } }; - if (auto* impl = tryJSDynamicCast(fn->implementation.get())) { + if (auto* impl = tryJSDynamicCast(fn->implementation)) { if (impl->isOnce()) { auto next = impl->nextValueOrSentinel.get(); fn->implementation.set(vm, fn, next); @@ -990,14 +896,15 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje setReturnValue(createMockResult(vm, globalObject, "incomplete"_s, jsUndefined())); - WTF::NakedPtr exception; + auto catchScope = DECLARE_CATCH_SCOPE(vm); - JSValue returnValue = call(globalObject, result, callData, thisValue, args, exception); + JSValue returnValue = Bun::call(globalObject, result, callData, thisValue, args); - if (auto* exc = exception.get()) { + if (auto* exc = catchScope.exception()) { if (auto* returnValuesArray = fn->returnValues.get()) { returnValuesArray->putDirectIndex(globalObject, returnValueIndex, createMockResult(vm, globalObject, "throw"_s, exc->value())); fn->returnValues.set(vm, fn, returnValuesArray); + catchScope.clearException(); JSC::throwException(globalObject, scope, exc); return {}; } @@ -1044,14 +951,14 @@ void JSMockFunctionPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* g JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockImplementation, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - } + CHECK_IS_MOCK_FUNCTION(thisValue); - if (auto* implementation = tryJSDynamicCast(thisObject->implementation.get())) { + if (auto* implementation = tryJSDynamicCast(thisObject->implementation)) { if (implementation->kind == JSMockImplementation::Kind::Call) { RELEASE_AND_RETURN(scope, JSValue::encode(implementation->underlyingValue.get())); } @@ -1064,10 +971,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsMockFunctionGetter_mock, (JSC::JSGlobalObject * globa { Bun::JSMockFunction* thisObject = jsDynamicCast(JSValue::decode(thisValue)); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(JSValue::decode(thisValue)) return JSValue::encode(thisObject->mock.getInitializedOnMainThread(thisObject)); } @@ -1076,61 +980,19 @@ JSC_DEFINE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl, (JSC::JSGlobalObject * { Bun::JSMockFunction* thisObject = jsDynamicCast(JSValue::decode(thisValue)); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(JSValue::decode(thisValue)) - if (auto* impl = tryJSDynamicCast(thisObject->implementation.get())) { + if (auto* impl = tryJSDynamicCast(thisObject->implementation)) { if (impl->kind == JSMockImplementation::Kind::Call) { - return JSValue::encode(impl->underlyingValue.get()); + if (impl->underlyingValue) { + return JSValue::encode(impl->underlyingValue.get()); + } } } return JSValue::encode(jsUndefined()); } -extern "C" JSC::EncodedJSValue JSMock__jsMockFn(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callframe) -{ - auto& vm = lexicalGlobalObject->vm(); - auto* globalObject = jsCast(lexicalGlobalObject); - auto scope = DECLARE_THROW_SCOPE(vm); - - JSMockFunction* thisObject = JSMockFunction::create( - vm, - globalObject, - globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject)); - - if (UNLIKELY(!thisObject)) { - throwOutOfMemoryError(globalObject, scope); - return {}; - } - - if (callframe->argumentCount() > 0) { - JSValue value = callframe->argument(0); - if (value.isCallable()) { - thisObject->copyNameAndLength(vm, lexicalGlobalObject, value); - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); - } else { - // jest doesn't support doing `jest.fn(10)`, but we support it. - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); - thisObject->setName("mockConstructor"_s); - } - } else { - thisObject->setName("mockConstructor"_s); - } - - if (!globalObject->mockModule.activeMocks) { - ActiveSpySet* activeMocks = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); - globalObject->mockModule.activeMocks.set(vm, activeMocks); - } - - ActiveSpySet* activeMocks = jsCast(globalObject->mockModule.activeMocks.get()); - activeMocks->add(vm, thisObject, thisObject); - - return JSValue::encode(thisObject); -} - extern "C" JSC::EncodedJSValue JSMockFunction__getCalls(EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); @@ -1152,36 +1014,28 @@ extern "C" JSC::EncodedJSValue JSMockFunction__getReturns(EncodedJSValue encoded JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - } - - if (auto* impl = tryJSDynamicCast(thisObject->implementation.get())) { - if (impl->kind == JSMockImplementation::Kind::Call) { - if (JSValue underlyingValue = impl->underlyingValue.get()) { - JSObject* object = underlyingValue.asCell()->getObject(); - if (auto nameValue = object->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->name))) { - RELEASE_AND_RETURN(scope, JSValue::encode(nameValue)); - } + CHECK_IS_MOCK_FUNCTION(thisValue) - RETURN_IF_EXCEPTION(scope, {}); - } - } + auto* jsName = thisObject->jsName(); + if (!jsName) { + return JSValue::encode(jsEmptyString(vm)); } - RELEASE_AND_RETURN(scope, JSValue::encode(jsEmptyString(vm))); + RELEASE_AND_RETURN(scope, JSValue::encode(jsName)); } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockClear, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - } + CHECK_IS_MOCK_FUNCTION(thisValue); thisObject->clear(); @@ -1189,12 +1043,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockClear, (JSC::JSGlobalObject * globalO } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReset, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - } + CHECK_IS_MOCK_FUNCTION(thisValue); thisObject->reset(); @@ -1202,12 +1056,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReset, (JSC::JSGlobalObject * globalO } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRestore, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - } + CHECK_IS_MOCK_FUNCTION(thisValue); thisObject->clearSpy(); @@ -1217,13 +1071,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementation, (JSC::JSGlobalObject { auto& vm = lexicalGlobalObject->vm(); auto* globalObject = jsCast(lexicalGlobalObject); + + JSValue thisValue = callframe->thisValue(); auto scope = DECLARE_THROW_SCOPE(vm); - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSMockFunction* thisObject = jsDynamicCast(thisValue); - if (UNLIKELY(!thisObject)) { - throwOutOfMemoryError(globalObject, scope); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); JSValue value = callframe->argument(0); @@ -1240,13 +1093,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce, (JSC::JSGlobalObj { auto& vm = lexicalGlobalObject->vm(); auto* globalObject = jsCast(lexicalGlobalObject); + + JSValue thisValue = callframe->thisValue(); auto scope = DECLARE_THROW_SCOPE(vm); - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSMockFunction* thisObject = jsDynamicCast(thisValue); - if (UNLIKELY(!thisObject)) { - throwOutOfMemoryError(globalObject, scope); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); JSValue value = callframe->argument(0); @@ -1261,34 +1113,33 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce, (JSC::JSGlobalObj } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } - - if (callframe->argumentCount() > 0) { - auto* newName = callframe->argument(0).toStringOrNull(globalObject); - if (UNLIKELY(!newName)) { - return {}; - } - - thisObject->putDirect(vm, vm.propertyNames->name, newName, 0); - RELEASE_AND_RETURN(scope, JSValue::encode(newName)); + CHECK_IS_MOCK_FUNCTION(thisValue); + + // https://github.com/jestjs/jest/blob/bd1c6db7c15c23788ca3e09c919138e48dd3b28a/packages/jest-mock/src/index.ts#L849-L856 + if (callframe->argument(0).toBoolean(globalObject)) { + RETURN_IF_EXCEPTION(scope, {}); + WTF::String name = callframe->argument(0).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + thisObject->setName(name); + } else { + RETURN_IF_EXCEPTION(scope, {}); } - RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, thisObject->calculatedDisplayName(vm)))); + RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnThis, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnThis, jsUndefined()); @@ -1296,13 +1147,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnThis, (JSC::JSGlobalObject * gl } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0)); @@ -1310,13 +1160,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValue, (JSC::JSGlobalObject * g } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0)); @@ -1324,13 +1173,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce, (JSC::JSGlobalObject } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); @@ -1338,13 +1186,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); @@ -1352,13 +1199,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce, (JSC::JSGlobalObje } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::rejectedPromise(globalObject, callframe->argument(0))); @@ -1366,13 +1212,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValue, (JSC::JSGlobalObject * } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::rejectedPromise(globalObject, callframe->argument(0))); @@ -1380,11 +1225,14 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce, (JSC::JSGlobalObje } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetter_mockGetLastCall, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { + auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); JSValue thisObject = callframe->thisValue(); if (UNLIKELY(!thisObject.isObject())) { return JSValue::encode(jsUndefined()); } - JSValue callsValue = thisObject.get(globalObject, Identifier::fromString(globalObject->vm(), "calls"_s)); + JSValue callsValue = thisObject.get(globalObject, Identifier::fromString(vm, "calls"_s)); + RETURN_IF_EXCEPTION(throwScope, {}); if (auto callsArray = jsDynamicCast(callsValue)) { auto len = callsArray->length(); @@ -1468,13 +1316,12 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementation, (JSC::JSGlobalObject { Zig::GlobalObject* globalObject = jsCast(jsGlobalObject); - JSMockFunction* thisObject = jsDynamicCast(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + JSValue thisValue = callframe->thisValue(); + JSMockFunction* thisObject = jsDynamicCast(thisValue); + auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - return {}; - } + CHECK_IS_MOCK_FUNCTION(thisValue); JSValue tempImplValue = callframe->argument(0); JSValue callback = callframe->argument(1); @@ -1529,3 +1376,213 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementation, (JSC::JSGlobalObject return JSC::JSValue::encode(jsUndefined()); } } // namespace Bun + +using namespace Bun; +using namespace JSC; + +// This is a stub. Exists so that the same code can be run in Jest +BUN_DEFINE_HOST_FUNCTION(JSMock__jsUseFakeTimers, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + return JSValue::encode(callframe->thisValue()); +} + +BUN_DEFINE_HOST_FUNCTION(JSMock__jsUseRealTimers, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + globalObject->overridenDateNow = -1; + return JSValue::encode(callframe->thisValue()); +} + +BUN_DEFINE_HOST_FUNCTION(JSMock__jsNow, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + return JSValue::encode(jsNumber(globalObject->jsDateNow())); +} +BUN_DEFINE_HOST_FUNCTION(JSMock__jsSetSystemTime, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + JSValue argument0 = callframe->argument(0); + + if (auto* dateInstance = jsDynamicCast(argument0)) { + if (std::isnormal(dateInstance->internalNumber())) { + globalObject->overridenDateNow = dateInstance->internalNumber(); + } + return JSValue::encode(callframe->thisValue()); + } + // number > 0 is a valid date otherwise it's invalid and we should reset the time (set to -1) + globalObject->overridenDateNow = (argument0.isNumber() && argument0.asNumber() >= 0) ? argument0.asNumber() : -1; + + return JSValue::encode(callframe->thisValue()); +} + +BUN_DEFINE_HOST_FUNCTION(JSMock__jsRestoreAllMocks, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + JSMock__resetSpies(jsCast(globalObject)); + return JSValue::encode(jsUndefined()); +} + +BUN_DEFINE_HOST_FUNCTION(JSMock__jsClearAllMocks, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + JSMock__clearAllMocks(jsCast(globalObject)); + return JSValue::encode(jsUndefined()); +} + +BUN_DEFINE_HOST_FUNCTION(JSMock__jsSpyOn, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* globalObject = jsDynamicCast(lexicalGlobalObject); + if (UNLIKELY(!globalObject)) { + throwVMError(globalObject, scope, "Cannot run spyOn from a different global context"_s); + return {}; + } + + JSValue objectValue = callframe->argument(0); + JSValue propertyKeyValue = callframe->argument(1); + + if (callframe->argumentCount() < 2 || !objectValue.isObject()) { + throwVMError(globalObject, scope, "spyOn(target, prop) expects a target object and a property key"_s); + return {}; + } + + PropertyName propertyKey = propertyKeyValue.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (propertyKey.isNull()) { + throwVMError(globalObject, scope, "spyOn(target, prop) expects a property key"_s); + return {}; + } + + JSC::JSObject* object = objectValue.getObject(); + if (object->type() == JSC::JSType::GlobalProxyType) + object = jsCast(object)->target(); + + JSC::PropertySlot slot(object, JSC::PropertySlot::InternalMethodType::HasProperty); + bool hasValue = object->getPropertySlot(globalObject, propertyKey, slot); + + // easymode: regular property or missing property + if (!hasValue || slot.isValue()) { + JSValue value = jsUndefined(); + if (hasValue) { + if (UNLIKELY(slot.isTaintedByOpaqueObject())) { + // if it's a Proxy or JSModuleNamespaceObject + value = object->get(globalObject, propertyKey); + } else { + value = slot.getValue(globalObject, propertyKey); + } + + if (jsDynamicCast(value)) { + return JSValue::encode(value); + } + } + + auto* mock = JSMockFunction::create(vm, globalObject, globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), CallbackKind::GetterSetter); + mock->spyTarget = JSC::Weak(object, &weakValueHandleOwner(), nullptr); + mock->spyIdentifier = propertyKey.isSymbol() ? Identifier::fromUid(vm, propertyKey.uid()) : Identifier::fromString(vm, propertyKey.publicName()); + mock->spyAttributes = hasValue ? slot.attributes() : 0; + unsigned attributes = 0; + + if (hasValue && ((slot.attributes() & PropertyAttribute::Function) != 0 || (value.isCell() && value.isCallable()))) { + if (hasValue) + attributes = slot.attributes(); + + mock->copyNameAndLength(vm, globalObject, value); + + if (JSModuleNamespaceObject* moduleNamespaceObject = tryJSDynamicCast(object)) { + moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock); + mock->spyAttributes |= JSMockFunction::SpyAttributeESModuleNamespace; + } else { + object->putDirect(vm, propertyKey, mock, attributes); + } + + RETURN_IF_EXCEPTION(scope, {}); + + pushImpl(mock, globalObject, JSMockImplementation::Kind::Call, value); + } else { + if (hasValue) + attributes = slot.attributes(); + + attributes |= PropertyAttribute::Accessor; + + if (JSModuleNamespaceObject* moduleNamespaceObject = tryJSDynamicCast(object)) { + moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock); + mock->spyAttributes |= JSMockFunction::SpyAttributeESModuleNamespace; + } else { + object->putDirectAccessor(globalObject, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes); + } + + // mock->setName(propertyKey.publicName()); + RETURN_IF_EXCEPTION(scope, {}); + + pushImpl(mock, globalObject, JSMockImplementation::Kind::ReturnValue, value); + } + + mock->spyOriginal.set(vm, mock, value); + + { + if (!globalObject->mockModule.activeSpies) { + ActiveSpySet* activeSpies = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); + globalObject->mockModule.activeSpies.set(vm, activeSpies); + } + ActiveSpySet* activeSpies = jsCast(globalObject->mockModule.activeSpies.get()); + activeSpies->add(vm, mock, mock); + } + + { + if (!globalObject->mockModule.activeMocks) { + ActiveSpySet* activeMocks = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); + globalObject->mockModule.activeMocks.set(vm, activeMocks); + } + ActiveSpySet* activeMocks = jsCast(globalObject->mockModule.activeMocks.get()); + activeMocks->add(vm, mock, mock); + } + + return JSValue::encode(mock); + } + + // hardmode: accessor property + throwVMError(globalObject, scope, "spyOn(target, prop) does not support accessor properties yet"_s); + return {}; +} + +BUN_DEFINE_HOST_FUNCTION(JSMock__jsMockFn, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto* globalObject = jsCast(lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSMockFunction* thisObject = JSMockFunction::create( + vm, + globalObject, + globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject)); + + if (UNLIKELY(!thisObject)) { + throwOutOfMemoryError(globalObject, scope); + return {}; + } + + if (callframe->argumentCount() > 0) { + JSValue value = callframe->argument(0); + if (value.isCallable()) { + thisObject->copyNameAndLength(vm, lexicalGlobalObject, value); + RETURN_IF_EXCEPTION(scope, {}); + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); + } else { + // jest doesn't support doing `jest.fn(10)`, but we support it. + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); + thisObject->setName("mockConstructor"_s); + } + } else { + thisObject->setName("mockConstructor"_s); + } + + if (!globalObject->mockModule.activeMocks) { + ActiveSpySet* activeMocks = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); + globalObject->mockModule.activeMocks.set(vm, activeMocks); + } + + ActiveSpySet* activeMocks = jsCast(globalObject->mockModule.activeMocks.get()); + activeMocks->add(vm, thisObject, thisObject); + + return JSValue::encode(thisObject); +} + +#undef CHECK_IS_MOCK_FUNCTION \ No newline at end of file diff --git a/src/bun.js/bindings/JSNextTickQueue.cpp b/src/bun.js/bindings/JSNextTickQueue.cpp index 18963645cb4be..6c3557177bd3b 100644 --- a/src/bun.js/bindings/JSNextTickQueue.cpp +++ b/src/bun.js/bindings/JSNextTickQueue.cpp @@ -87,7 +87,6 @@ void JSNextTickQueue::drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject) globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined()); } auto* drainFn = internalField(2).get().getObject(); - auto throwScope = DECLARE_THROW_SCOPE(vm); MarkedArgumentBuffer drainArgs; JSC::call(globalObject, drainFn, drainArgs, "Failed to drain next tick queue"_s); diff --git a/src/bun.js/bindings/JSPropertyIterator.cpp b/src/bun.js/bindings/JSPropertyIterator.cpp index 4b3afe3002397..e386c6cc5a2c8 100644 --- a/src/bun.js/bindings/JSPropertyIterator.cpp +++ b/src/bun.js/bindings/JSPropertyIterator.cpp @@ -8,6 +8,7 @@ #include "JavaScriptCore/JSCJSValue.h" #include "JavaScriptCore/JSGlobalObject.h" #include "JavaScriptCore/PropertyNameArray.h" +#include "wtf/Assertions.h" #include "wtf/FastMalloc.h" #include "headers-handwritten.h" @@ -23,7 +24,7 @@ class JSPropertyIterator { } RefPtr properties; - JSC::VM& vm; + Ref vm; static JSPropertyIterator* create(JSC::VM& vm, RefPtr data) { return new JSPropertyIterator(vm, data); @@ -75,4 +76,4 @@ extern "C" void Bun__JSPropertyIterator__deinit(JSPropertyIterator* iter) delete iter; } -} \ No newline at end of file +} diff --git a/src/bun.js/bindings/JSStringDecoder.cpp b/src/bun.js/bindings/JSStringDecoder.cpp index 70feb31b72740..27969cb8954cf 100644 --- a/src/bun.js/bindings/JSStringDecoder.cpp +++ b/src/bun.js/bindings/JSStringDecoder.cpp @@ -458,7 +458,7 @@ static JSC_DEFINE_CUSTOM_GETTER(jsStringDecoder_lastChar, (JSGlobalObject * lexi } JSStringDecoder* thisObject = jsCast(stringDecoderValue); auto throwScope = DECLARE_THROW_SCOPE(vm); - auto buffer = ArrayBuffer::createFromBytes(thisObject->m_lastChar, 4, nullptr); + auto buffer = ArrayBuffer::create({ thisObject->m_lastChar, 4 }); auto* globalObject = reinterpret_cast(lexicalGlobalObject); JSC::JSUint8Array* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, globalObject->JSBufferSubclassStructure(), WTFMove(buffer), 0, 4); RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array)); diff --git a/src/bun.js/bindings/JSWrappingFunction.h b/src/bun.js/bindings/JSWrappingFunction.h index 39b00f2faf228..ed3a925583441 100644 --- a/src/bun.js/bindings/JSWrappingFunction.h +++ b/src/bun.js/bindings/JSWrappingFunction.h @@ -18,7 +18,7 @@ class JSGlobalObject; namespace Zig { -using NativeFunctionPtr = JSC::EncodedJSValue (*)(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); +using NativeFunctionPtr = SYSV_ABI JSC::EncodedJSValue (*)(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); /** * Subclass of JSC::JSFunction that holds an additional single native JSFunction as property. diff --git a/src/bun.js/bindings/KeyObject.cpp b/src/bun.js/bindings/KeyObject.cpp index b96b2de7dd22a..901e3256026dc 100644 --- a/src/bun.js/bindings/KeyObject.cpp +++ b/src/bun.js/bindings/KeyObject.cpp @@ -21,6 +21,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. +#include "ErrorCode.h" #include "KeyObject.h" #include "JavaScriptCore/JSArrayBufferView.h" #include "JavaScriptCore/JSCJSValue.h" @@ -47,18 +48,19 @@ #include "JSBuffer.h" #include "CryptoAlgorithmHMAC.h" #include "CryptoAlgorithmEd25519.h" +#include "CryptoAlgorithmRSA_OAEP.h" #include "CryptoAlgorithmRSA_PSS.h" #include "CryptoAlgorithmRSASSA_PKCS1_v1_5.h" #include "CryptoAlgorithmECDSA.h" #include "CryptoAlgorithmEcdsaParams.h" +#include "CryptoAlgorithmRsaOaepParams.h" #include "CryptoAlgorithmRsaPssParams.h" #include "CryptoAlgorithmRegistry.h" #include "wtf/ForbidHeapAllocation.h" #include "wtf/Noncopyable.h" using namespace JSC; using namespace Bun; -using JSGlobalObject - = JSC::JSGlobalObject; +using JSGlobalObject = JSC::JSGlobalObject; using Exception = JSC::Exception; using JSValue = JSC::JSValue; using JSString = JSC::JSString; @@ -69,6 +71,21 @@ using SourceOrigin = JSC::SourceOrigin; using JSObject = JSC::JSObject; using JSNonFinalObject = JSC::JSNonFinalObject; +JSC_DECLARE_HOST_FUNCTION(KeyObject__AsymmetricKeyType); +JSC_DECLARE_HOST_FUNCTION(KeyObject_AsymmetricKeyDetails); +JSC_DECLARE_HOST_FUNCTION(KeyObject__SymmetricKeySize); +JSC_DECLARE_HOST_FUNCTION(KeyObject__Equals); +JSC_DECLARE_HOST_FUNCTION(KeyObject__Exports); +JSC_DECLARE_HOST_FUNCTION(KeyObject__createSecretKey); +JSC_DECLARE_HOST_FUNCTION(KeyObject__createPublicKey); +JSC_DECLARE_HOST_FUNCTION(KeyObject__createPrivateKey); +JSC_DECLARE_HOST_FUNCTION(KeyObject__generateKeySync); +JSC_DECLARE_HOST_FUNCTION(KeyObject__generateKeyPairSync); +JSC_DECLARE_HOST_FUNCTION(KeyObject__Sign); +JSC_DECLARE_HOST_FUNCTION(KeyObject__Verify); +JSC_DECLARE_HOST_FUNCTION(KeyObject__publicEncrypt); +JSC_DECLARE_HOST_FUNCTION(KeyObject__privateDecrypt); + namespace WebCore { static bool KeyObject__IsASN1Sequence(const unsigned char* data, size_t size, @@ -304,7 +321,7 @@ AsymmetricKeyValueWithDER KeyObject__ParsePublicKeyPEM(const char* key_pem, return result; } -JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__createPrivateKey, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); @@ -364,6 +381,7 @@ JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* globalObjec case Int8ArrayType: case Int16ArrayType: case Int32ArrayType: + case Float16ArrayType: case Float32ArrayType: case Float64ArrayType: case BigInt64ArrayType: @@ -537,7 +555,7 @@ JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* globalObjec } } if (format == "der"_s) { - JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "type"_s))); + JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->type)); WTF::String type = "pkcs8"_s; if (!typeJSValue.isUndefinedOrNull() && !typeJSValue.isEmpty()) { if (!typeJSValue.isString()) { @@ -557,7 +575,7 @@ JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* globalObjec return JSValue::encode(JSC::jsUndefined()); } auto pKeyID = EVP_PKEY_id(pkey.get()); - auto impl = CryptoKeyRSA::create(pKeyID == EVP_PKEY_RSA_PSS ? CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5 : CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5, CryptoAlgorithmIdentifier::SHA_1, false, CryptoKeyType::Private, WTFMove(pkey), true, CryptoKeyUsageDecrypt); + auto impl = CryptoKeyRSA::create(pKeyID == EVP_PKEY_RSA_PSS ? CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5 : CryptoAlgorithmIdentifier::RSA_OAEP, CryptoAlgorithmIdentifier::SHA_1, false, CryptoKeyType::Private, WTFMove(pkey), true, CryptoKeyUsageDecrypt); return JSC::JSValue::encode(JSCryptoKey::create(structure, zigGlobalObject, WTFMove(impl))); } else if (type == "pkcs8"_s) { @@ -766,14 +784,14 @@ static JSC::EncodedJSValue KeyObject__createOKPFromPrivate(JSC::JSGlobalObject* auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - uint8_t public_key[ED25519_PUBLIC_KEY_LEN]; + Vector public_key(ED25519_PUBLIC_KEY_LEN); if (namedCurve == CryptoKeyOKP::NamedCurve::Ed25519) { - memcpy(public_key, keyData.data() + ED25519_PRIVATE_KEY_LEN, ED25519_PUBLIC_KEY_LEN); + memcpy(public_key.data(), keyData.data() + ED25519_PRIVATE_KEY_LEN, ED25519_PUBLIC_KEY_LEN); } else { - X25519_public_from_private(public_key, keyData.data()); + X25519_public_from_private(public_key.data(), keyData.data()); } - auto result = CryptoKeyOKP::create(alg, namedCurve, CryptoKeyType::Public, Vector(public_key), true, CryptoKeyUsageVerify); + auto result = CryptoKeyOKP::create(alg, namedCurve, CryptoKeyType::Public, WTFMove(public_key), true, CryptoKeyUsageVerify); if (UNLIKELY(result == nullptr)) { JSC::throwTypeError(globalObject, scope, "ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE: Failed to create a public key from private"_s); return JSValue::encode(JSC::jsUndefined()); @@ -841,7 +859,7 @@ static JSC::EncodedJSValue KeyObject__createPublicFromPrivate(JSC::JSGlobalObjec } } -JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__createPublicKey, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); @@ -926,6 +944,7 @@ JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* globalObject case Int8ArrayType: case Int16ArrayType: case Int32ArrayType: + case Float16ArrayType: case Float32ArrayType: case Float64ArrayType: case BigInt64ArrayType: @@ -1125,7 +1144,7 @@ JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* globalObject } } if (format == "der"_s) { - JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "type"_s))); + JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->type)); WTF::String type = "spki"_s; if (!typeJSValue.isUndefinedOrNull() && !typeJSValue.isEmpty()) { if (!typeJSValue.isString()) { @@ -1150,11 +1169,11 @@ JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* globalObject } auto pKeyID = EVP_PKEY_id(pkey.get()); - return KeyObject__createRSAFromPrivate(globalObject, pkey.get(), pKeyID == EVP_PKEY_RSA_PSS ? CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5 : CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5); + return KeyObject__createRSAFromPrivate(globalObject, pkey.get(), pKeyID == EVP_PKEY_RSA_PSS ? CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5 : CryptoAlgorithmIdentifier::RSA_OAEP); } auto pKeyID = EVP_PKEY_id(pkey.get()); - auto impl = CryptoKeyRSA::create(pKeyID == EVP_PKEY_RSA_PSS ? CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5 : CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5, CryptoAlgorithmIdentifier::SHA_1, false, CryptoKeyType::Public, WTFMove(pkey), true, CryptoKeyUsageEncrypt); + auto impl = CryptoKeyRSA::create(pKeyID == EVP_PKEY_RSA_PSS ? CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5 : CryptoAlgorithmIdentifier::RSA_OAEP, CryptoAlgorithmIdentifier::SHA_1, false, CryptoKeyType::Public, WTFMove(pkey), true, CryptoKeyUsageEncrypt); return JSC::JSValue::encode(JSCryptoKey::create(structure, zigGlobalObject, WTFMove(impl))); } else if (type == "spki"_s) { // We use d2i_PUBKEY() to import a public key. @@ -1234,7 +1253,7 @@ JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* globalObject return JSValue::encode(JSC::jsUndefined()); } -JSC::EncodedJSValue KeyObject__createSecretKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__createSecretKey, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { JSValue bufferArg = callFrame->uncheckedArgument(0); auto& vm = lexicalGlobalObject->vm(); @@ -1259,6 +1278,7 @@ JSC::EncodedJSValue KeyObject__createSecretKey(JSC::JSGlobalObject* lexicalGloba case Int8ArrayType: case Int16ArrayType: case Int32ArrayType: + case Float16ArrayType: case Float32ArrayType: case Float64ArrayType: case BigInt64ArrayType: @@ -1315,6 +1335,7 @@ static ExceptionOr> KeyObject__GetBuffer(JSValue bufferArg) case Int8ArrayType: case Int16ArrayType: case Int32ArrayType: + case Float16ArrayType: case Float32ArrayType: case Float64ArrayType: case BigInt64ArrayType: @@ -1347,7 +1368,7 @@ static ExceptionOr> KeyObject__GetBuffer(JSValue bufferArg) } return Exception { OperationError }; } -JSC::EncodedJSValue KeyObject__Sign(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__Sign, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); auto& vm = globalObject->vm(); @@ -1556,7 +1577,7 @@ JSC::EncodedJSValue KeyObject__Sign(JSC::JSGlobalObject* globalObject, JSC::Call } } -JSC::EncodedJSValue KeyObject__Verify(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__Verify, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); auto& vm = globalObject->vm(); @@ -1763,7 +1784,7 @@ JSC::EncodedJSValue KeyObject__Verify(JSC::JSGlobalObject* globalObject, JSC::Ca } } -JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__Exports, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); @@ -1787,7 +1808,7 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C auto id = wrapped.keyClass(); if (auto* options = jsDynamicCast(callFrame->argument(1))) { JSValue formatJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "format"_s))); - JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "type"_s))); + JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->type)); JSValue passphraseJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "passphrase"_s))); KeyPassphrase passphrase(passphraseJSValue, globalObject, scope); RETURN_IF_EXCEPTION(scope, encodedJSValue()); @@ -2348,7 +2369,7 @@ static char* bignum_to_string(const BIGNUM* bn) return ret; } -JSC::EncodedJSValue KeyObject_AsymmetricKeyDetails(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject_AsymmetricKeyDetails, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { if (auto* key = jsDynamicCast(callFrame->argument(0))) { @@ -2473,7 +2494,7 @@ JSC::EncodedJSValue KeyObject_AsymmetricKeyDetails(JSC::JSGlobalObject* lexicalG return JSC::JSValue::encode(JSC::jsUndefined()); } -JSC::EncodedJSValue KeyObject__generateKeyPairSync(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__generateKeyPairSync, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); auto& vm = lexicalGlobalObject->vm(); @@ -2695,7 +2716,7 @@ JSC::EncodedJSValue KeyObject__generateKeyPairSync(JSC::JSGlobalObject* lexicalG } return JSValue::encode(JSC::jsUndefined()); } -JSC::EncodedJSValue KeyObject__generateKeySync(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__generateKeySync, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { auto count = callFrame->argumentCount(); auto& vm = lexicalGlobalObject->vm(); @@ -2757,7 +2778,7 @@ JSC::EncodedJSValue KeyObject__generateKeySync(JSC::JSGlobalObject* lexicalGloba } } -JSC::EncodedJSValue KeyObject__AsymmetricKeyType(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__AsymmetricKeyType, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { static const NeverDestroyed values[] = { MAKE_STATIC_STRING_IMPL("rsa"), @@ -2842,7 +2863,7 @@ static AsymmetricKeyValue GetInternalAsymmetricKey(WebCore::CryptoKey& key) } } -JSC::EncodedJSValue KeyObject__Equals(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__Equals, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { if (auto* key = jsDynamicCast(callFrame->argument(0))) { if (auto* key2 = jsDynamicCast(callFrame->argument(1))) { @@ -2886,7 +2907,7 @@ JSC::EncodedJSValue KeyObject__Equals(JSC::JSGlobalObject* lexicalGlobalObject, return JSC::JSValue::encode(jsBoolean(false)); } -JSC::EncodedJSValue KeyObject__SymmetricKeySize(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSC_DEFINE_HOST_FUNCTION(KeyObject__SymmetricKeySize, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { if (auto* key = jsDynamicCast(callFrame->argument(0))) { auto& wrapped = key->wrapped(); @@ -2926,6 +2947,149 @@ JSC::EncodedJSValue KeyObject__SymmetricKeySize(JSC::JSGlobalObject* globalObjec return JSC::JSValue::encode(JSC::jsUndefined()); } +static EncodedJSValue doAsymmetricCipher(JSGlobalObject* globalObject, CallFrame* callFrame, bool encrypt) +{ + auto count = callFrame->argumentCount(); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (count != 2) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_MISSING_ARGS, + "expected object as first argument"_s); + } + + auto* jsKey = jsDynamicCast(callFrame->uncheckedArgument(0)); + if (!jsKey) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected object as first argument"_s); + } + + auto jsCryptoKeyValue = jsKey->getIfPropertyExists( + globalObject, PropertyName(Identifier::fromString(vm, "key"_s))); + if (jsCryptoKeyValue.isUndefinedOrNull() || jsCryptoKeyValue.isEmpty()) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected key property in key object"_s); + } + auto* jsCryptoKey = jsDynamicCast(jsCryptoKeyValue); + + auto& cryptoKey = jsCryptoKey->wrapped(); + // We should only encrypt to public keys, and decrypt with private keys. + if ((encrypt && cryptoKey.type() != CryptoKeyType::Public) + || (!encrypt && cryptoKey.type() != CryptoKeyType::Private) + // RSA-OAEP is the modern alternative to RSAES-PKCS1-v1_5, which is vulnerable to + // known-ciphertext attacks. Node.js does not support it either. + || cryptoKey.algorithmIdentifier() != CryptoAlgorithmIdentifier::RSA_OAEP) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_VALUE, + "unsupported key type for asymmetric encryption"_s); + } + + bool setCustomHash = false; + auto oaepHash = WebCore::CryptoAlgorithmIdentifier::SHA_1; + auto jsOaepHash = jsKey->getIfPropertyExists( + globalObject, PropertyName(Identifier::fromString(vm, "oaepHash"_s))); + if (!jsOaepHash.isUndefined() && !jsOaepHash.isEmpty()) { + if (UNLIKELY(!jsOaepHash.isString())) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected string for oaepHash"_s); + } + auto oaepHashStr = jsOaepHash.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + auto oaepHashId = CryptoAlgorithmRegistry::singleton().identifier(oaepHashStr); + if (UNLIKELY(!oaepHashId)) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_CRYPTO_INVALID_DIGEST, + "unsupported digest for oaepHash"_s); + } + switch (*oaepHashId) { + case WebCore::CryptoAlgorithmIdentifier::SHA_1: + case WebCore::CryptoAlgorithmIdentifier::SHA_224: + case WebCore::CryptoAlgorithmIdentifier::SHA_256: + case WebCore::CryptoAlgorithmIdentifier::SHA_384: + case WebCore::CryptoAlgorithmIdentifier::SHA_512: { + setCustomHash = true; + oaepHash = *oaepHashId; + break; + } + default: { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_CRYPTO_INVALID_DIGEST, + "unsupported digest for oaepHash"_s); + } + } + } + + std::optional oaepLabel = std::nullopt; + auto jsOaepLabel = jsKey->getIfPropertyExists( + globalObject, PropertyName(Identifier::fromString(vm, "oaepLabel"_s))); + if (!jsOaepLabel.isUndefined() && !jsOaepLabel.isEmpty()) { + if (UNLIKELY(!jsOaepLabel.isCell())) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected Buffer or array-like object for oaepLabel"_s); + } + auto jsOaepLabelCell = jsOaepLabel.asCell(); + auto jsOaepLabelType = jsOaepLabelCell->type(); + + if (isTypedArrayTypeIncludingDataView(jsOaepLabelType)) { + auto* jsBufferView = jsCast(jsOaepLabelCell); + oaepLabel = std::optional{jsBufferView->unsharedImpl()}; + } else if (jsOaepLabelType == ArrayBufferType) { + auto* jsBuffer = jsDynamicCast(jsOaepLabelCell); + oaepLabel = std::optional{jsBuffer->impl()}; + } else { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected Buffer or array-like object for oaepLabel"_s); + } + } + + auto padding = RSA_PKCS1_OAEP_PADDING; + auto jsPadding = jsKey->getIfPropertyExists( + globalObject, PropertyName(Identifier::fromString(vm, "padding"_s))); + if (!jsPadding.isUndefinedOrNull() && !jsPadding.isEmpty()) { + if (UNLIKELY(!jsPadding.isNumber())) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected number for padding"_s); + } + padding = jsPadding.toUInt32(globalObject); + if (padding == RSA_PKCS1_PADDING && !encrypt) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_VALUE, + "RSA_PKCS1_PADDING is no longer supported for private decryption"_s); + } + if (padding != RSA_PKCS1_OAEP_PADDING && (setCustomHash || oaepLabel.has_value())) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_VALUE, + "oaepHash/oaepLabel cannot be set without RSA_PKCS1_OAEP_PADDING"_s); + } + } + + auto jsBuffer = KeyObject__GetBuffer(callFrame->uncheckedArgument(1)); + if (jsBuffer.hasException()) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "expected Buffer or array-like object as second argument"_s); + } + auto buffer = jsBuffer.releaseReturnValue(); + + auto params = CryptoAlgorithmRsaOaepParams{}; + params.label = oaepLabel; + params.padding = padding; + const auto& rsaKey = downcast(cryptoKey); + auto operation = encrypt ? CryptoAlgorithmRSA_OAEP::platformEncryptWithHash : CryptoAlgorithmRSA_OAEP::platformDecryptWithHash; + auto result = operation(params, rsaKey, buffer, oaepHash); + if (result.hasException()) { + WebCore::propagateException(*globalObject, scope, result.releaseException()); + return encodedJSUndefined(); + } + auto outBuffer = result.releaseReturnValue(); + return JSValue::encode(WebCore::createBuffer(globalObject, outBuffer)); +} + +JSC_DEFINE_HOST_FUNCTION(KeyObject__publicEncrypt, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + return doAsymmetricCipher(globalObject, callFrame, true); +} + +JSC_DEFINE_HOST_FUNCTION(KeyObject__privateDecrypt, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + return doAsymmetricCipher(globalObject, callFrame, false); +} + JSValue createNodeCryptoBinding(Zig::GlobalObject* globalObject) { VM& vm = globalObject->vm(); @@ -2957,6 +3121,11 @@ JSValue createNodeCryptoBinding(Zig::GlobalObject* globalObject) obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "sign"_s)), JSC::JSFunction::create(vm, globalObject, 3, "sign"_s, KeyObject__Sign, ImplementationVisibility::Public, NoIntrinsic), 0); obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "verify"_s)), JSC::JSFunction::create(vm, globalObject, 4, "verify"_s, KeyObject__Verify, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect(vm, PropertyName(Identifier::fromString(vm, "publicEncrypt"_s)), + JSFunction::create(vm, globalObject, 2, "publicEncrypt"_s, KeyObject__publicEncrypt, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect(vm, PropertyName(Identifier::fromString(vm, "privateDecrypt"_s)), + JSFunction::create(vm, globalObject, 2, "privateDecrypt"_s, KeyObject__privateDecrypt, ImplementationVisibility::Public, NoIntrinsic), 0); + return obj; } diff --git a/src/bun.js/bindings/KeyObject.h b/src/bun.js/bindings/KeyObject.h index d70ba80d5c114..924ba9223fc6e 100644 --- a/src/bun.js/bindings/KeyObject.h +++ b/src/bun.js/bindings/KeyObject.h @@ -6,19 +6,6 @@ namespace WebCore { -JSC::EncodedJSValue KeyObject__AsymmetricKeyType(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject_AsymmetricKeyDetails(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__SymmetricKeySize(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__Equals(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__createSecretKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__generateKeySync(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__generateKeyPairSync(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__Sign(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); -JSC::EncodedJSValue KeyObject__Verify(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); - JSC::JSValue createNodeCryptoBinding(Zig::GlobalObject* globalObject); } // namespace WebCore diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index dba0e8457318b..6d3d8b3e244c9 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -42,6 +42,24 @@ using namespace JSC; using namespace Zig; using namespace WebCore; +class ResolvedSourceCodeHolder { +public: + ResolvedSourceCodeHolder(ErrorableResolvedSource* res_) + : res(res_) + { + } + + ~ResolvedSourceCodeHolder() + { + if (res->success && res->result.value.source_code.tag == BunStringTag::WTFStringImpl && res->result.value.needsDeref) { + res->result.value.needsDeref = false; + res->result.value.source_code.impl.wtf->deref(); + } + } + + ErrorableResolvedSource* res; +}; + extern "C" BunLoaderType Bun__getDefaultLoader(JSC::JSGlobalObject*, BunString* specifier); static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value) @@ -289,13 +307,7 @@ static JSValue handleVirtualModuleResult( auto onLoadResult = handleOnLoadResult(globalObject, virtualModuleResult, specifier, wasModuleMock); JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - WTF::String sourceCodeStringForDeref; - const auto getSourceCodeStringForDeref = [&]() { - if (res->success && res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) { - res->result.value.needsDeref = false; - sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf); - } - }; + ResolvedSourceCodeHolder sourceCodeHolder(res); const auto reject = [&](JSC::JSValue exception) -> JSValue { if constexpr (allowPromise) { @@ -342,7 +354,6 @@ static JSValue handleVirtualModuleResult( if (!res->success) { return reject(JSValue::decode(reinterpret_cast(res->result.err.ptr))); } - getSourceCodeStringForDeref(); auto provider = Zig::SourceProvider::create(globalObject, res->result.value); return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); @@ -396,13 +407,7 @@ extern "C" void Bun__onFulfillAsyncModule( BunString* specifier, BunString* referrer) { - WTF::String sourceCodeStringForDeref; - const auto getSourceCodeStringForDeref = [&]() { - if (res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) { - res->result.value.needsDeref = false; - sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf); - } - }; + ResolvedSourceCodeHolder sourceCodeHolder(res); auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSInternalPromise* promise = jsCast(JSC::JSValue::decode(encodedPromiseValue)); @@ -414,21 +419,23 @@ extern "C" void Bun__onFulfillAsyncModule( return promise->reject(globalObject, exception); } - getSourceCodeStringForDeref(); auto specifierValue = Bun::toJS(globalObject, *specifier); if (auto entry = globalObject->esmRegistryMap()->get(globalObject, specifierValue)) { - if (res->result.value.commonJSExportsLen) { - if (entry.isObject()) { - if (auto isEvaluated = entry.getObject()->getIfPropertyExists(globalObject, Bun::builtinNames(vm).evaluatedPublicName())) { - if (isEvaluated.isTrue()) { - // it's a race! we lost. - // https://github.com/oven-sh/bun/issues/6946 - return; - } + if (entry.isObject()) { + + auto* object = entry.getObject(); + if (auto state = object->getIfPropertyExists(globalObject, Bun::builtinNames(vm).statePublicName())) { + if (state.toInt32(globalObject) > JSC::JSModuleLoader::Status::Fetch) { + // it's a race! we lost. + // https://github.com/oven-sh/bun/issues/6946 + // https://github.com/oven-sh/bun/issues/12910 + return; } } + } + if (res->result.value.isCommonJSModule) { auto created = Bun::createCommonJSModule(jsCast(globalObject), specifierValue, res->result.value); if (created.has_value()) { JSSourceCode* code = JSSourceCode::create(vm, WTFMove(created.value())); @@ -465,13 +472,7 @@ JSValue fetchCommonJSModule( memset(&resValue, 0, sizeof(ErrorableResolvedSource)); ErrorableResolvedSource* res = &resValue; - WTF::String sourceCodeStringForDeref; - const auto getSourceCodeStringForDeref = [&]() { - if (res->success && res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) { - res->result.value.needsDeref = false; - sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf); - } - }; + ResolvedSourceCodeHolder sourceCodeHolder(res); auto& builtinNames = WebCore::clientData(vm)->builtinNames(); bool wasModuleMock = false; @@ -598,9 +599,7 @@ JSValue fetchCommonJSModule( } Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false); - getSourceCodeStringForDeref(); - - if (res->success && res->result.value.commonJSExportsLen) { + if (res->success && res->result.value.isCommonJSModule) { target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), res->result.value); RETURN_IF_EXCEPTION(scope, {}); RELEASE_AND_RETURN(scope, target); @@ -667,6 +666,7 @@ static JSValue fetchESMSourceCode( void* bunVM = globalObject->bunVM(); auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); + ResolvedSourceCodeHolder sourceCodeHolder(res); const auto reject = [&](JSC::JSValue exception) -> JSValue { if constexpr (allowPromise) { @@ -736,7 +736,7 @@ static JSValue fetchESMSourceCode( default: { if (tag & SyntheticModuleType::InternalModuleRegistryFlag) { constexpr auto mask = (SyntheticModuleType::InternalModuleRegistryFlag - 1); - auto source = JSC::SourceCode(JSC::SyntheticSourceProvider::create(generateInternalModuleSourceCode(globalObject, static_cast(tag & mask)), JSC::SourceOrigin(URL(makeString("builtins://", moduleKey))), moduleKey)); + auto source = JSC::SourceCode(JSC::SyntheticSourceProvider::create(generateInternalModuleSourceCode(globalObject, static_cast(tag & mask)), JSC::SourceOrigin(URL(makeString("builtins://"_s, moduleKey))), moduleKey)); return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source))); } else { auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value, JSC::SourceProviderSourceType::Module, true); @@ -753,26 +753,16 @@ static JSValue fetchESMSourceCode( } } - WTF::String sourceCodeStringForDeref; - const auto getSourceCodeStringForDeref = [&]() { - if (res->success && res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) { - res->result.value.needsDeref = false; - sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf); - } - }; - if constexpr (allowPromise) { auto* pendingCtx = Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, true); - getSourceCodeStringForDeref(); if (pendingCtx) { return pendingCtx; } } else { Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false); - getSourceCodeStringForDeref(); } - if (res->success && res->result.value.commonJSExportsLen) { + if (res->success && res->result.value.isCommonJSModule) { auto created = Bun::createCommonJSModule(globalObject, specifierJS, res->result.value); if (created.has_value()) { @@ -844,7 +834,32 @@ static JSValue fetchESMSourceCode( JSC::SourceCode(Zig::SourceProvider::create(globalObject, res->result.value)))); } -extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +JSValue fetchESMSourceCodeSync( + Zig::GlobalObject* globalObject, + JSC::JSValue specifierJS, + ErrorableResolvedSource* res, + BunString* specifier, + BunString* referrer, + BunString* typeAttribute) +{ + return fetchESMSourceCode(globalObject, specifierJS, res, specifier, referrer, typeAttribute); +} + +JSValue fetchESMSourceCodeAsync( + Zig::GlobalObject* globalObject, + JSC::JSValue specifierJS, + ErrorableResolvedSource* res, + BunString* specifier, + BunString* referrer, + BunString* typeAttribute) +{ + return fetchESMSourceCode(globalObject, specifierJS, res, specifier, referrer, typeAttribute); +} +} + +using namespace Bun; + +BUN_DEFINE_HOST_FUNCTION(jsFunctionOnLoadObjectResultResolve, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { JSC::VM& vm = globalObject->vm(); ErrorableResolvedSource res = {}; @@ -882,7 +897,7 @@ extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobal return JSValue::encode(jsUndefined()); } -extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultReject(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +BUN_DEFINE_HOST_FUNCTION(jsFunctionOnLoadObjectResultReject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { JSC::VM& vm = globalObject->vm(); JSC::JSValue reason = callFrame->argument(0); @@ -896,26 +911,3 @@ extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultReject(JSC::JSGlobalO return JSValue::encode(reason); } - -JSValue fetchESMSourceCodeSync( - Zig::GlobalObject* globalObject, - JSC::JSValue specifierJS, - ErrorableResolvedSource* res, - BunString* specifier, - BunString* referrer, - BunString* typeAttribute) -{ - return fetchESMSourceCode(globalObject, specifierJS, res, specifier, referrer, typeAttribute); -} - -JSValue fetchESMSourceCodeAsync( - Zig::GlobalObject* globalObject, - JSC::JSValue specifierJS, - ErrorableResolvedSource* res, - BunString* specifier, - BunString* referrer, - BunString* typeAttribute) -{ - return fetchESMSourceCode(globalObject, specifierJS, res, specifier, referrer, typeAttribute); -} -} diff --git a/src/bun.js/bindings/ModuleLoader.h b/src/bun.js/bindings/ModuleLoader.h index 2212bab10cf60..6816c0fd0b8fb 100644 --- a/src/bun.js/bindings/ModuleLoader.h +++ b/src/bun.js/bindings/ModuleLoader.h @@ -1,11 +1,13 @@ +#pragma once + #include "root.h" #include "headers-handwritten.h" #include #include "BunClientData.h" -extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); -extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultReject(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); +BUN_DECLARE_HOST_FUNCTION(jsFunctionOnLoadObjectResultResolve); +BUN_DECLARE_HOST_FUNCTION(jsFunctionOnLoadObjectResultReject); namespace Zig { class GlobalObject; diff --git a/src/bun.js/bindings/NodeFetch.cpp b/src/bun.js/bindings/NodeFetch.cpp new file mode 100644 index 0000000000000..1b25af677dad7 --- /dev/null +++ b/src/bun.js/bindings/NodeFetch.cpp @@ -0,0 +1,50 @@ +#include "root.h" +#include "JSDOMGlobalObjectInlines.h" +#include "ZigGlobalObject.h" + +#include "JSFetchHeaders.h" +#include "JSDOMFormData.h" +#include "JavaScriptCore/ObjectConstructor.h" + +#include "helpers.h" +#include "BunClientData.h" + +#include "JavaScriptCore/AggregateError.h" +#include "JavaScriptCore/JSFunction.h" +#include "JSDOMFile.h" + +namespace Bun { + +using namespace JSC; +using namespace WebCore; + +// Ensure overriding globals doesn't impact usages. +JSC::JSValue createNodeFetchInternalBinding(Zig::GlobalObject* globalObject) +{ + auto& vm = globalObject->vm(); + + auto* obj = constructEmptyObject(globalObject); + obj->putDirectIndex( + globalObject, 0, + globalObject->JSResponseConstructor()); + obj->putDirectIndex( + globalObject, 1, + globalObject->JSRequestConstructor()); + obj->putDirectIndex( + globalObject, 2, + globalObject->JSBlobConstructor()); + obj->putDirectIndex( + globalObject, 3, + WebCore::JSFetchHeaders::getConstructor(vm, globalObject)); + + obj->putDirectIndex( + globalObject, 4, + WebCore::JSDOMFormData::getConstructor(vm, globalObject)); + obj->putDirectIndex( + globalObject, 5, + globalObject->JSDOMFileConstructor()); + + return obj; +} + +} \ No newline at end of file diff --git a/src/bun.js/bindings/NodeFetch.h b/src/bun.js/bindings/NodeFetch.h new file mode 100644 index 0000000000000..2b20c2d0beb91 --- /dev/null +++ b/src/bun.js/bindings/NodeFetch.h @@ -0,0 +1,7 @@ +#include "config.h" + +namespace Bun { + +JSC::JSValue createNodeFetchInternalBinding(Zig::GlobalObject*); + +} \ No newline at end of file diff --git a/src/bun.js/bindings/NodeHTTP.cpp b/src/bun.js/bindings/NodeHTTP.cpp index a49e17adcd144..8a7b04ad5e357 100644 --- a/src/bun.js/bindings/NodeHTTP.cpp +++ b/src/bun.js/bindings/NodeHTTP.cpp @@ -1,4 +1,5 @@ #include "root.h" +#include "JSDOMGlobalObjectInlines.h" #include "ZigGlobalObject.h" #include #include "helpers.h" @@ -21,7 +22,9 @@ using namespace JSC; using namespace WebCore; extern "C" uWS::HttpRequest* Request__getUWSRequest(void*); - +extern "C" void Request__setInternalEventCallback(void*, EncodedJSValue, JSC::JSGlobalObject*); +extern "C" void Request__setTimeout(void*, EncodedJSValue, JSC::JSGlobalObject*); +extern "C" void Server__setIdleTimeout(EncodedJSValue, EncodedJSValue, JSC::JSGlobalObject*); static EncodedJSValue assignHeadersFromFetchHeaders(FetchHeaders& impl, JSObject* prototype, JSObject* objectValue, JSC::InternalFieldTuple* tuple, JSC::JSGlobalObject* globalObject, JSC::VM& vm) { auto scope = DECLARE_THROW_SCOPE(vm); @@ -259,10 +262,11 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPAssignHeaders, (JSGlobalObject * globalObject, Ca auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - JSValue requestValue = callFrame->argument(0); - JSObject* objectValue = callFrame->argument(1).getObject(); - - JSC::InternalFieldTuple* tuple = JSC::InternalFieldTuple::create(vm, globalObject->m_internalFieldTupleStructure.get()); + // This is an internal binding. + JSValue requestValue = callFrame->uncheckedArgument(0); + JSObject* objectValue = callFrame->uncheckedArgument(1).getObject(); + JSC::InternalFieldTuple* tuple = jsCast(callFrame->uncheckedArgument(2)); + ASSERT(callFrame->argumentCount() == 3); JSValue headersValue = JSValue(); JSValue urlValue = JSValue(); @@ -320,6 +324,57 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPAssignHeaders, (JSGlobalObject * globalObject, Ca return JSValue::encode(jsNull()); } +JSC_DEFINE_HOST_FUNCTION(jsHTTPAssignEventCallback, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // This is an internal binding. + JSValue requestValue = callFrame->uncheckedArgument(0); + JSValue callback = callFrame->uncheckedArgument(1); + + ASSERT(callFrame->argumentCount() == 2); + + if (auto* jsRequest = jsDynamicCast(requestValue)) { + Request__setInternalEventCallback(jsRequest->wrapped(), JSValue::encode(callback), globalObject); + } + + return JSValue::encode(jsNull()); +} + +JSC_DEFINE_HOST_FUNCTION(jsHTTPSetTimeout, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // This is an internal binding. + JSValue requestValue = callFrame->uncheckedArgument(0); + JSValue seconds = callFrame->uncheckedArgument(1); + + ASSERT(callFrame->argumentCount() == 2); + + if (auto* jsRequest = jsDynamicCast(requestValue)) { + Request__setTimeout(jsRequest->wrapped(), JSValue::encode(seconds), globalObject); + } + + return JSValue::encode(jsUndefined()); +} +JSC_DEFINE_HOST_FUNCTION(jsHTTPSetServerIdleTimeout, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // This is an internal binding. + JSValue serverValue = callFrame->uncheckedArgument(0); + JSValue seconds = callFrame->uncheckedArgument(1); + + ASSERT(callFrame->argumentCount() == 2); + + Server__setIdleTimeout(JSValue::encode(serverValue), JSValue::encode(seconds), globalObject); + + return JSValue::encode(jsUndefined()); +} + JSC_DEFINE_HOST_FUNCTION(jsHTTPGetHeader, (JSGlobalObject * globalObject, CallFrame* callFrame)) { auto& vm = globalObject->vm(); @@ -409,13 +464,39 @@ JSValue createNodeHTTPInternalBinding(Zig::GlobalObject* globalObject) VM& vm = globalObject->vm(); obj->putDirect( vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setHeader"_s)), - JSC::JSFunction::create(vm, globalObject, 3, "setHeader"_s, jsHTTPSetHeader, ImplementationVisibility::Public), NoIntrinsic); + JSC::JSFunction::create(vm, globalObject, 3, "setHeader"_s, jsHTTPSetHeader, ImplementationVisibility::Public), 0); obj->putDirect( vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getHeader"_s)), - JSC::JSFunction::create(vm, globalObject, 2, "getHeader"_s, jsHTTPGetHeader, ImplementationVisibility::Public), NoIntrinsic); + JSC::JSFunction::create(vm, globalObject, 2, "getHeader"_s, jsHTTPGetHeader, ImplementationVisibility::Public), 0); obj->putDirect( vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "assignHeaders"_s)), - JSC::JSFunction::create(vm, globalObject, 2, "assignHeaders"_s, jsHTTPAssignHeaders, ImplementationVisibility::Public), NoIntrinsic); + JSC::JSFunction::create(vm, globalObject, 2, "assignHeaders"_s, jsHTTPAssignHeaders, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "assignEventCallback"_s)), + JSC::JSFunction::create(vm, globalObject, 2, "assignEventCallback"_s, jsHTTPAssignEventCallback, ImplementationVisibility::Public), 0); + + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setRequestTimeout"_s)), + JSC::JSFunction::create(vm, globalObject, 2, "setRequestTimeout"_s, jsHTTPSetTimeout, ImplementationVisibility::Public), 0); + + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setServerIdleTimeout"_s)), + JSC::JSFunction::create(vm, globalObject, 2, "setServerIdleTimeout"_s, jsHTTPSetServerIdleTimeout, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Response"_s)), + globalObject->JSResponseConstructor(), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Request"_s)), + globalObject->JSRequestConstructor(), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Blob"_s)), + globalObject->JSBlobConstructor(), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Headers"_s)), + WebCore::JSFetchHeaders::getConstructor(vm, globalObject), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "headersTuple"_s)), + JSC::InternalFieldTuple::create(vm, globalObject->m_internalFieldTupleStructure.get()), 0); return obj; } diff --git a/src/bun.js/bindings/NodeTLS.cpp b/src/bun.js/bindings/NodeTLS.cpp index e908a55088588..f1c5efa37d095 100644 --- a/src/bun.js/bindings/NodeTLS.cpp +++ b/src/bun.js/bindings/NodeTLS.cpp @@ -11,7 +11,7 @@ namespace Bun { using namespace JSC; -extern "C" JSC::EncodedJSValue Bun__canonicalizeIP(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +BUN_DECLARE_HOST_FUNCTION(Bun__canonicalizeIP); JSC::JSValue createNodeTLSBinding(Zig::GlobalObject* globalObject) { diff --git a/src/bun.js/bindings/NodeTimerObject.cpp b/src/bun.js/bindings/NodeTimerObject.cpp index 4177a40aae128..d68786eac1a40 100644 --- a/src/bun.js/bindings/NodeTimerObject.cpp +++ b/src/bun.js/bindings/NodeTimerObject.cpp @@ -27,10 +27,10 @@ extern "C" void Bun__JSTimeout__call(JSC::EncodedJSValue encodedTimeoutValue, JS WebCore::JSTimeout* timeout = jsCast(JSC::JSValue::decode(encodedTimeoutValue)); JSCell* callbackCell = timeout->m_callback.get().asCell(); - JSValue restoreAsyncContext{}; + JSValue restoreAsyncContext {}; JSC::InternalFieldTuple* asyncContextData = nullptr; - if (auto *wrapper = jsDynamicCast(callbackCell)) { + if (auto* wrapper = jsDynamicCast(callbackCell)) { callbackCell = wrapper->callback.get().asCell(); asyncContextData = globalObject->m_asyncContextData.get(); restoreAsyncContext = asyncContextData->getInternalField(0); @@ -64,7 +64,7 @@ extern "C" void Bun__JSTimeout__call(JSC::EncodedJSValue encodedTimeoutValue, JS } } - JSC::profiledCall(globalObject, ProfilingReason::API, JSValue(callbackCell), JSC::getCallData(callbackCell), timeout, ArgList(args)); + JSC::profiledCall(globalObject, ProfilingReason::API, JSValue(callbackCell), JSC::getCallData(callbackCell), timeout, ArgList(args)); break; } } diff --git a/src/bun.js/bindings/NodeVM.cpp b/src/bun.js/bindings/NodeVM.cpp index 1090752fc252b..b53f07ecc0a44 100644 --- a/src/bun.js/bindings/NodeVM.cpp +++ b/src/bun.js/bindings/NodeVM.cpp @@ -1,5 +1,7 @@ #include "root.h" +#include "JavaScriptCore/ExecutableInfo.h" +#include "BunClientData.h" #include "NodeVM.h" #include "JavaScriptCore/JSObjectInlines.h" #include "wtf/text/ExternalStringImpl.h" @@ -24,10 +26,6 @@ #include "JSBuffer.h" #include -#include "DOMJITIDLConvert.h" -#include "DOMJITIDLType.h" -#include "DOMJITIDLTypeFilter.h" -#include "DOMJITHelpers.h" #include #include @@ -47,17 +45,22 @@ class ScriptOptions { { auto& vm = globalObject->vm(); ScriptOptions opts; + JSObject* options; bool any = false; if (!optionsArg.isUndefined()) { - if (!optionsArg.isObject()) { + if (optionsArg.isObject()) { + options = asObject(optionsArg); + } else if (optionsArg.isString()) { + options = constructEmptyObject(globalObject); + options->putDirect(vm, Identifier::fromString(vm, "filename"_s), optionsArg); + } else { auto scope = DECLARE_THROW_SCOPE(vm); - throwVMTypeError(globalObject, scope, "options must be an object"_s); + throwVMTypeError(globalObject, scope, "options must be an object or a string"_s); failed = true; return std::nullopt; } - JSObject* options = asObject(optionsArg); - if (JSValue filenameOpt = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "filename"_s))) { + if (JSValue filenameOpt = options->getIfPropertyExists(globalObject, builtinNames(vm).filenamePublicName())) { if (filenameOpt.isString()) { opts.filename = filenameOpt.toWTFString(globalObject); any = true; @@ -144,8 +147,8 @@ static JSC::EncodedJSValue runInContext(JSGlobalObject* globalObject, NodeVMScri if (executable == nullptr) { // Note: it accepts a JSGlobalObject, but it just reads stuff from JSC::VM. executable = JSC::DirectEvalExecutable::create( - globalObject, script->source(), DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, - false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy()); + globalObject, script->source(), NoLexicallyScopedFeatures, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, + false, false, EvalContextType::None, nullptr, nullptr); RETURN_IF_EXCEPTION(throwScope, {}); script->m_cachedDirectExecutable.set(vm, script, executable); } @@ -270,12 +273,12 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInNewContext, (JSGlobalObject * globalObject auto* zigGlobal = reinterpret_cast(globalObject); JSObject* context = asObject(contextObjectValue); - auto* targetContext = JSC::JSGlobalObject::create( - vm, zigGlobal->globalObjectStructure()); + auto* targetContext = NodeVMGlobalObject::create( + vm, zigGlobal->NodeVMGlobalObjectStructure()); auto* executable = JSC::DirectEvalExecutable::create( - targetContext, source, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, - false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy()); + targetContext, source, NoLexicallyScopedFeatures, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, + false, false, EvalContextType::None, nullptr, nullptr); RETURN_IF_EXCEPTION(throwScope, {}); auto proxyStructure = JSGlobalProxy::createStructure(vm, globalObject, JSC::jsNull()); @@ -302,8 +305,7 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInThisContext, (JSGlobalObject * globalObjec { auto& vm = globalObject->vm(); auto sourceStringValue = callFrame->argument(0); - JSValue contextObjectValue = callFrame->argument(1); - JSValue optionsObjectValue = callFrame->argument(2); + JSValue optionsObjectValue = callFrame->argument(1); auto throwScope = DECLARE_THROW_SCOPE(vm); if (!sourceStringValue.isString()) { @@ -313,15 +315,6 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInThisContext, (JSGlobalObject * globalObjec auto sourceString = sourceStringValue.toWTFString(globalObject); - if (!contextObjectValue || contextObjectValue.isUndefinedOrNull()) { - contextObjectValue = JSC::constructEmptyObject(globalObject); - } - - if (UNLIKELY(!contextObjectValue || !contextObjectValue.isObject())) { - throwTypeError(globalObject, throwScope, "Context must be an object"_s); - return JSValue::encode({}); - } - ScriptOptions options; { bool didThrow = false; @@ -336,19 +329,13 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInThisContext, (JSGlobalObject * globalObjec SourceCode source( JSC::StringSourceProvider::create(sourceString, JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(options.filename)), options.filename, JSC::SourceTaintedOrigin::Untainted, TextPosition(options.lineOffset, options.columnOffset)), options.lineOffset.zeroBasedInt(), options.columnOffset.zeroBasedInt()); - auto* zigGlobal = reinterpret_cast(globalObject); - JSObject* context = asObject(contextObjectValue); - - auto proxyStructure = zigGlobal->globalProxyStructure(); - auto proxy = JSGlobalProxy::create(vm, proxyStructure); - proxy->setTarget(vm, globalObject); - context->setPrototypeDirect(vm, proxy); auto* executable = JSC::DirectEvalExecutable::create( - globalObject, source, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, - false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy()); + globalObject, source, NoLexicallyScopedFeatures, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, + false, false, EvalContextType::None, nullptr, nullptr); RETURN_IF_EXCEPTION(throwScope, {}); + JSObject* context = asObject(JSC::constructEmptyObject(globalObject)); JSScope* contextScope = JSWithScope::create(vm, globalObject, globalObject->globalScope(), context); auto catchScope = DECLARE_CATCH_SCOPE(vm); JSValue result = vm.interpreter.executeEval(executable, globalObject, contextScope); @@ -392,13 +379,13 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInNewContext, (JSGlobalObject * globalObject, auto* zigGlobal = reinterpret_cast(globalObject); JSObject* context = asObject(contextObjectValue); - auto* targetContext = JSC::JSGlobalObject::create( - vm, zigGlobal->globalObjectStructure()); + auto* targetContext = NodeVMGlobalObject::create( + vm, zigGlobal->NodeVMGlobalObjectStructure()); - // auto proxyStructure = JSGlobalProxy::createStructure(vm, globalObject, JSC::jsNull()); - // auto proxy = JSGlobalProxy::create(vm, proxyStructure); - // proxy->setTarget(vm, targetContext); - // context->setPrototypeDirect(vm, proxy); + auto proxyStructure = JSGlobalProxy::createStructure(vm, globalObject, JSC::jsNull()); + auto proxy = JSGlobalProxy::create(vm, proxyStructure); + proxy->setTarget(vm, targetContext); + context->setPrototypeDirect(vm, proxy); JSScope* contextScope = JSWithScope::create(vm, targetContext, targetContext->globalScope(), context); return runInContext(globalObject, script, targetContext, contextScope, callFrame->argument(0)); @@ -409,21 +396,14 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject, JSValue thisValue = callFrame->thisValue(); auto* script = jsDynamicCast(thisValue); auto throwScope = DECLARE_THROW_SCOPE(vm); + // TODO: options + // JSValue optionsObjectValue = callFrame->argument(0); if (UNLIKELY(!script)) { return throwVMTypeError(globalObject, throwScope, "Script.prototype.runInThisContext can only be called on a Script object"_s); } - JSValue contextArg = callFrame->argument(0); - if (!contextArg || contextArg.isUndefinedOrNull()) { - contextArg = JSC::constructEmptyObject(globalObject); - } - - if (!contextArg.isObject()) { - return throwVMTypeError(globalObject, throwScope, "context must be an object"_s); - } - - JSObject* context = asObject(contextArg); + JSObject* context = asObject(JSC::constructEmptyObject(globalObject)); JSWithScope* contextScope = JSWithScope::create(vm, globalObject, globalObject->globalScope(), context); return runInContext(globalObject, script, globalObject->globalThis(), contextScope, callFrame->argument(1)); @@ -444,6 +424,11 @@ JSC_DEFINE_CUSTOM_GETTER(scriptGetSourceMapURL, (JSGlobalObject * globalObject, return JSValue::encode(jsString(vm, url)); } +Structure* createNodeVMGlobalObjectStructure(JSC::VM& vm) +{ + return NodeVMGlobalObject::createStructure(vm, jsNull()); +} + JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject * globalObject, CallFrame* callFrame)) { auto& vm = globalObject->vm(); @@ -460,8 +445,8 @@ JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject * globalObject, } JSObject* context = asObject(contextArg); auto* zigGlobalObject = reinterpret_cast(globalObject); - auto* targetContext = JSC::JSGlobalObject::create( - vm, zigGlobalObject->globalObjectStructure()); + auto* targetContext = NodeVMGlobalObject::create( + vm, zigGlobalObject->NodeVMGlobalObjectStructure()); auto proxyStructure = zigGlobalObject->globalProxyStructure(); auto proxy = JSGlobalProxy::create(vm, proxyStructure); @@ -534,6 +519,7 @@ static const struct HashTableValue scriptPrototypeTableValues[] = { const ClassInfo NodeVMScriptPrototype::s_info = { "Script"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMScriptPrototype) }; const ClassInfo NodeVMScript::s_info = { "Script"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMScript) }; const ClassInfo NodeVMScriptConstructor::s_info = { "Script"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMScriptConstructor) }; +const ClassInfo NodeVMGlobalObject::s_info = { "NodeVMGlobalObject"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMGlobalObject) }; DEFINE_VISIT_CHILDREN(NodeVMScript); diff --git a/src/bun.js/bindings/NodeVM.h b/src/bun.js/bindings/NodeVM.h index becaf7b4b754f..4cee946713c1d 100644 --- a/src/bun.js/bindings/NodeVM.h +++ b/src/bun.js/bindings/NodeVM.h @@ -12,6 +12,8 @@ namespace WebCore { +Structure* createNodeVMGlobalObjectStructure(JSC::VM&); + class NodeVMScriptConstructor final : public JSC::InternalFunction { public: using Base = JSC::InternalFunction; @@ -76,6 +78,18 @@ class NodeVMScript final : public JSC::JSDestructibleObject { void finishCreation(JSC::VM&); }; +class NodeVMGlobalObject final : public Bun::GlobalScope { + using Base = Bun::GlobalScope; + +public: + NodeVMGlobalObject(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + + DECLARE_INFO; +}; + JSC_DECLARE_HOST_FUNCTION(vmModule_createContext); JSC_DECLARE_HOST_FUNCTION(vmModule_isContext); JSC_DECLARE_HOST_FUNCTION(vmModuleRunInNewContext); diff --git a/src/bun.js/bindings/OsBinding.cpp b/src/bun.js/bindings/OsBinding.cpp new file mode 100644 index 0000000000000..c994fe8d9866d --- /dev/null +++ b/src/bun.js/bindings/OsBinding.cpp @@ -0,0 +1,25 @@ +#include "root.h" + +#if OS(DARWIN) +#include +#include +#include +#include +#include +#include + +// Adapted from libuv darwin uv_get_free_memory, MIT +extern "C" uint64_t Bun__Os__getFreeMemory(void) +{ + vm_statistics_data_t info; + mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); + + if (host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count) + != KERN_SUCCESS) { + return 0; + } + + return (uint64_t)info.free_count * sysconf(_SC_PAGESIZE); +} +#endif diff --git a/src/bun.js/bindings/Path.cpp b/src/bun.js/bindings/Path.cpp index 516b4da73a20b..e0d1e308a136e 100644 --- a/src/bun.js/bindings/Path.cpp +++ b/src/bun.js/bindings/Path.cpp @@ -28,150 +28,84 @@ namespace JSCastingHelpers = JSC::JSCastingHelpers; using namespace JSC; -// clang-format off -#define DEFINE_CALLBACK_FUNCTION_BODY(ZigFunction) JSC::VM& vm = globalObject->vm(); \ - auto* thisObject = JSC::jsDynamicCast(callFrame->thisValue()); \ - if (!thisObject) { \ - auto scope = DECLARE_THROW_SCOPE(vm); \ - return throwVMTypeError(globalObject, scope); \ - } \ - auto argCount = static_cast(callFrame->argumentCount()); \ - WTF::Vector arguments; \ - arguments.reserveInitialCapacity(argCount); \ - if (argCount) { \ - for (uint16_t i = 0; i < argCount; ++i) { \ - arguments.unsafeAppendWithoutCapacityCheck(JSC::JSValue::encode(callFrame->uncheckedArgument(i))); \ - } \ - } \ - auto isWindows = thisObject->get(globalObject, WebCore::clientData(vm)->builtinNames().isWindowsPrivateName()); \ - return ZigFunction(globalObject, isWindows.asBoolean(), reinterpret_cast(arguments.data()), argCount); - -// clang-format on - -JSC_DECLARE_HOST_FUNCTION(Path_functionBasename); -JSC_DEFINE_HOST_FUNCTION(Path_functionBasename, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__basename); -} - -JSC_DECLARE_HOST_FUNCTION(Path_functionDirname); -JSC_DEFINE_HOST_FUNCTION(Path_functionDirname, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__dirname); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionExtname, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__extname); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionFormat, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__format); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionIsAbsolute, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__isAbsolute); -} +using PathFunction = JSC::EncodedJSValue (*SYSV_ABI)(JSGlobalObject*, bool, EncodedJSValue*, uint16_t len); -JSC_DEFINE_HOST_FUNCTION(Path_functionJoin, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +template +static inline JSC::EncodedJSValue createZigFunction(JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__join); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + MarkedArgumentBufferWithSize<8> args = MarkedArgumentBufferWithSize<8>(); + for (unsigned i = 0, size = callFrame->argumentCount(); i < size; ++i) { + args.append(callFrame->argument(i)); + } + const auto result = Function(globalObject, isWindows, args.data(), args.size()); + RETURN_IF_EXCEPTION(scope, {}); + return result; } -JSC_DEFINE_HOST_FUNCTION(Path_functionNormalize, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__normalize); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionParse, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__parse); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionRelative, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__relative); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionResolve, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__resolve); -} - -JSC_DEFINE_HOST_FUNCTION(Path_functionToNamespacedPath, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - DEFINE_CALLBACK_FUNCTION_BODY(Bun__Path__toNamespacedPath); -} +#define DEFINE_PATH_FUNCTION(jsFunctionName, Function, isWindows) \ + JSC_DEFINE_HOST_FUNCTION(jsFunctionName, \ + (JSC::JSGlobalObject * globalObject, JSC::CallFrame * callFrame)) \ + { \ + return createZigFunction(globalObject, callFrame); \ + } + +DEFINE_PATH_FUNCTION(jsFunctionPath_basenamePosix, Bun__Path__basename, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_dirnamePosix, Bun__Path__dirname, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_extnamePosix, Bun__Path__extname, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_formatPosix, Bun__Path__format, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_isAbsolutePosix, Bun__Path__isAbsolute, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_joinPosix, Bun__Path__join, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_normalizePosix, Bun__Path__normalize, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_parsePosix, Bun__Path__parse, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_relativePosix, Bun__Path__relative, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_resolvePosix, Bun__Path__resolve, false) +DEFINE_PATH_FUNCTION(jsFunctionPath_toNamespacedPathPosix, Bun__Path__toNamespacedPath, false) + +DEFINE_PATH_FUNCTION(jsFunctionPath_basenameWindows, Bun__Path__basename, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_dirnameWindows, Bun__Path__dirname, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_extnameWindows, Bun__Path__extname, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_formatWindows, Bun__Path__format, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_isAbsoluteWindows, Bun__Path__isAbsolute, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_joinWindows, Bun__Path__join, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_normalizeWindows, Bun__Path__normalize, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_parseWindows, Bun__Path__parse, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_relativeWindows, Bun__Path__relative, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_resolveWindows, Bun__Path__resolve, true) +DEFINE_PATH_FUNCTION(jsFunctionPath_toNamespacedPathWindows, Bun__Path__toNamespacedPath, true) static JSC::JSObject* createPath(JSGlobalObject* globalThis, bool isWindows) { JSC::VM& vm = globalThis->vm(); - JSC::Structure* plainObjectStructure = JSC::JSFinalObject::createStructure(vm, globalThis, globalThis->objectPrototype(), 0); - JSC::JSObject* path = JSC::JSFinalObject::create(vm, plainObjectStructure); - auto clientData = WebCore::clientData(vm); - - path->putDirect(vm, clientData->builtinNames().isWindowsPrivateName(), - JSC::jsBoolean(isWindows), 0); - - path->putDirect(vm, clientData->builtinNames().basenamePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "basename"_s, Path_functionBasename, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().dirnamePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "dirname"_s, Path_functionDirname, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().extnamePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "extname"_s, Path_functionExtname, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().formatPublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "format"_s, Path_functionFormat, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().isAbsolutePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "isAbsolute"_s, Path_functionIsAbsolute, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().joinPublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "join"_s, Path_functionJoin, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().normalizePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "normalize"_s, Path_functionNormalize, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().parsePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "parse"_s, Path_functionParse, ImplementationVisibility::Public), - 0); - path->putDirect(vm, clientData->builtinNames().relativePublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "relative"_s, Path_functionRelative, ImplementationVisibility::Public), - 0); - path->putDirect(vm, vm.propertyNames->resolve, - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "resolve"_s, Path_functionResolve, ImplementationVisibility::Public), - 0); - - path->putDirect(vm, clientData->builtinNames().toNamespacedPathPublicName(), - JSC::JSFunction::create(vm, JSC::jsCast(globalThis), 0, - "toNamespacedPath"_s, - Path_functionToNamespacedPath, ImplementationVisibility::Public), - 0); + auto* path = JSC::constructEmptyObject(globalThis); + auto builtinNames = WebCore::builtinNames(vm); + + if (!isWindows) { + path->putDirectNativeFunction(vm, globalThis, builtinNames.basenamePublicName(), 1, jsFunctionPath_basenamePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.dirnamePublicName(), 1, jsFunctionPath_dirnamePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.extnamePublicName(), 1, jsFunctionPath_extnamePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.formatPublicName(), 1, jsFunctionPath_formatPosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.isAbsolutePublicName(), 1, jsFunctionPath_isAbsolutePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.joinPublicName(), 1, jsFunctionPath_joinPosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.normalizePublicName(), 1, jsFunctionPath_normalizePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.parsePublicName(), 1, jsFunctionPath_parsePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.relativePublicName(), 1, jsFunctionPath_relativePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.resolvePublicName(), 1, jsFunctionPath_resolvePosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.toNamespacedPathPublicName(), 1, jsFunctionPath_toNamespacedPathPosix, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + } else { + path->putDirectNativeFunction(vm, globalThis, builtinNames.basenamePublicName(), 1, jsFunctionPath_basenameWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.dirnamePublicName(), 1, jsFunctionPath_dirnameWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.extnamePublicName(), 1, jsFunctionPath_extnameWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.formatPublicName(), 1, jsFunctionPath_formatWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.isAbsolutePublicName(), 1, jsFunctionPath_isAbsoluteWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.joinPublicName(), 1, jsFunctionPath_joinWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.normalizePublicName(), 1, jsFunctionPath_normalizeWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.parsePublicName(), 1, jsFunctionPath_parseWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.relativePublicName(), 1, jsFunctionPath_relativeWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.resolvePublicName(), 1, jsFunctionPath_resolveWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + path->putDirectNativeFunction(vm, globalThis, builtinNames.toNamespacedPathPublicName(), 1, jsFunctionPath_toNamespacedPathWindows, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0); + } return path; } @@ -183,16 +117,14 @@ namespace Bun { JSC::JSValue createNodePathBinding(Zig::GlobalObject* globalObject) { auto binding = constructEmptyArray(globalObject, nullptr, 2); - binding->putByIndexInline( + binding->putDirectIndex( globalObject, (unsigned)0, - Zig::createPath(globalObject, false), - false); - binding->putByIndexInline( + Zig::createPath(globalObject, false)); + binding->putDirectIndex( globalObject, (unsigned)1, - Zig::createPath(globalObject, true), - false); + Zig::createPath(globalObject, true)); return binding; } diff --git a/src/bun.js/bindings/ProcessBindingUV.cpp b/src/bun.js/bindings/ProcessBindingUV.cpp index d62eefa3befe6..c6151ffefe9a1 100644 --- a/src/bun.js/bindings/ProcessBindingUV.cpp +++ b/src/bun.js/bindings/ProcessBindingUV.cpp @@ -132,14 +132,15 @@ JSC_DEFINE_HOST_FUNCTION(jsGetErrorMap, (JSGlobalObject * globalObject, JSC::Cal auto& vm = globalObject->vm(); auto map = JSC::JSMap::create(vm, globalObject->mapStructure()); -#define PUT_PROPERTY(name, value, desc) \ - { \ - auto arr = JSC::constructEmptyArray(globalObject, static_cast(nullptr), 2); \ - arr->putDirectIndex(globalObject, 0, JSC::jsString(vm, String(#name##_s))); \ - arr->putDirectIndex(globalObject, 1, JSC::jsString(vm, String(desc##_s))); \ - map->set(globalObject, JSC::jsNumber(value), arr); \ - } - + // Inlining each of these via macros costs like 300 KB. + const auto putProperty = [](JSC::VM& vm, JSC::JSMap* map, JSC::JSGlobalObject* globalObject, ASCIILiteral name, int value, ASCIILiteral desc) -> void { + auto arr = JSC::constructEmptyArray(globalObject, static_cast(nullptr), 2); + arr->putDirectIndex(globalObject, 0, JSC::jsString(vm, String(name))); + arr->putDirectIndex(globalObject, 1, JSC::jsString(vm, String(desc))); + map->set(globalObject, JSC::jsNumber(value), arr); + }; + +#define PUT_PROPERTY(name, value, desc) putProperty(vm, map, globalObject, #name##_s, value, desc##_s); BUN_UV_ERRNO_MAP(PUT_PROPERTY) #undef PUT_PROPERTY @@ -152,9 +153,15 @@ JSObject* create(VM& vm, JSGlobalObject* globalObject) EnsureStillAliveScope ensureStillAlive(bindingObject); bindingObject->putDirect(vm, JSC::Identifier::fromString(vm, "errname"_s), JSC::JSFunction::create(vm, globalObject, 1, "errname"_s, jsErrname, ImplementationVisibility::Public)); -#define PUT_PROPERTY(name, value, desc) \ - bindingObject->putDirect(vm, JSC::Identifier::fromString(vm, "UV_" #name##_s), JSC::jsNumber(value)); + // Inlining each of these via macros costs like 300 KB. + // Before: 96305608 + // After: 95973832 + const auto putNamedProperty = [](JSC::VM& vm, JSObject* bindingObject, const ASCIILiteral name, int value) -> void { + bindingObject->putDirect(vm, JSC::Identifier::fromString(vm, makeString("UV_"_s, name)), JSC::jsNumber(value)); + }; +#define PUT_PROPERTY(name, value, desc) \ + putNamedProperty(vm, bindingObject, #name##_s, value); BUN_UV_ERRNO_MAP(PUT_PROPERTY) #undef PUT_PROPERTY diff --git a/src/bun.js/bindings/ProcessIdentifier.cpp b/src/bun.js/bindings/ProcessIdentifier.cpp index 5e82f4fe9326c..03e73426430ca 100644 --- a/src/bun.js/bindings/ProcessIdentifier.cpp +++ b/src/bun.js/bindings/ProcessIdentifier.cpp @@ -29,7 +29,7 @@ #include namespace WebCore { -namespace ProcessIdent { +namespace Process { static std::optional globalIdentifier; diff --git a/src/bun.js/bindings/ProcessIdentifier.h b/src/bun.js/bindings/ProcessIdentifier.h index e6141bdc0508d..1950d8cf0eb79 100644 --- a/src/bun.js/bindings/ProcessIdentifier.h +++ b/src/bun.js/bindings/ProcessIdentifier.h @@ -29,13 +29,13 @@ namespace WebCore { -enum ProcessIdentifierType {}; -using ProcessIdentifier = ObjectIdentifier; +enum class ProcessIdentifierType {}; +using ProcessIdentifier = LegacyNullableObjectIdentifier; -namespace ProcessIdent { +namespace Process { WEBCORE_EXPORT void setIdentifier(ProcessIdentifier); WEBCORE_EXPORT ProcessIdentifier identifier(); -} // namespace ProcessIdent +} // namespace Process } // namespace WebCore diff --git a/src/bun.js/bindings/SQLClient.cpp b/src/bun.js/bindings/SQLClient.cpp new file mode 100644 index 0000000000000..807fffffa3208 --- /dev/null +++ b/src/bun.js/bindings/SQLClient.cpp @@ -0,0 +1,317 @@ +#include "root.h" + +#include "JavaScriptCore/JSGlobalObject.h" +#include +#include +#include "headers-handwritten.h" +#include +#include +#include +#include +#include +#include "ZigGlobalObject.h" +#include +#include +#include "GCDefferalContext.h" +#include "wtf/Assertions.h" + +#include "JavaScriptCore/ArgList.h" +#include "JavaScriptCore/ArrayAllocationProfile.h" +#include "JavaScriptCore/ExceptionScope.h" +#include "JavaScriptCore/JSArrayBufferView.h" +#include "JavaScriptCore/JSCast.h" +#include "JavaScriptCore/JSGlobalObjectInlines.h" +#include "JavaScriptCore/JSType.h" +#include "JavaScriptCore/TypedArrayAdaptersForwardDeclarations.h" + +namespace Bun { +using namespace JSC; + +typedef struct DataCellArray { + struct DataCell* cells; + unsigned length; +} DataCellArray; + +typedef struct TypedArrayDataCell { + void* headPtr; + void* data; + unsigned length; + unsigned byteLength; + JSC::JSType type; +} TypedArrayDataCell; + +typedef union DataCellValue { + uint8_t null_value; + WTF::StringImpl* string; + double number; + int32_t integer; + int64_t bigint; + uint8_t boolean; + double date; + size_t bytea[2]; + WTF::StringImpl* json; + DataCellArray array; + TypedArrayDataCell typed_array; +} DataCellValue; + +enum class DataCellTag : uint8_t { + Null = 0, + String = 1, + Double = 2, + Integer = 3, + Bigint = 4, + Boolean = 5, + Date = 6, + Bytea = 7, + Json = 8, + Array = 9, + TypedArray = 10, +}; + +typedef struct DataCell { + DataCellTag tag; + DataCellValue value; + uint8_t freeValue; +} DataCell; + +static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCell& cell) +{ + switch (cell.tag) { + case DataCellTag::Null: + return jsNull(); + break; + case DataCellTag::String: { + return jsString(vm, WTF::String(cell.value.string)); + break; + } + case DataCellTag::Double: + return jsDoubleNumber(cell.value.number); + break; + case DataCellTag::Integer: + return jsNumber(cell.value.integer); + break; + case DataCellTag::Bigint: + return JSC::JSBigInt::createFrom(globalObject, cell.value.bigint); + break; + case DataCellTag::Boolean: + return jsBoolean(cell.value.boolean); + break; + case DataCellTag::Date: + return JSC::DateInstance::create(vm, globalObject->dateStructure(), cell.value.date); + break; + case DataCellTag::Bytea: { + Zig::GlobalObject* zigGlobal = jsCast(globalObject); + auto* subclassStructure = zigGlobal->JSBufferSubclassStructure(); + auto* uint8Array = JSC::JSUint8Array::createUninitialized(globalObject, subclassStructure, cell.value.bytea[1]); + if (UNLIKELY(uint8Array == nullptr)) { + return {}; + } + + if (cell.value.bytea[1] > 0) { + memcpy(uint8Array->vector(), reinterpret_cast(cell.value.bytea[0]), cell.value.bytea[1]); + } + return uint8Array; + } + case DataCellTag::Json: { + auto str = WTF::String(cell.value.string); + JSC::JSValue json = JSC::JSONParse(globalObject, str); + return json; + break; + } + case DataCellTag::Array: { + MarkedArgumentBuffer args; + unsigned length = cell.value.array.length; + for (unsigned i = 0; i < length; i++) { + JSValue result = toJS(vm, globalObject, cell.value.array.cells[i]); + if (UNLIKELY(result.isEmpty())) { + return {}; + } + + args.append(result); + } + + return JSC::constructArray(globalObject, static_cast(nullptr), args); + } + case DataCellTag::TypedArray: { + JSC::JSType type = static_cast(cell.value.typed_array.type); + unsigned length = cell.value.typed_array.length; + switch (type) { + case JSC::JSType::Int32ArrayType: { + JSC::JSInt32Array* array = JSC::JSInt32Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeInt32, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * sizeof(int32_t)); + } + + return array; + } + case JSC::JSType::Uint32ArrayType: { + JSC::JSUint32Array* array = JSC::JSUint32Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeUint32, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * sizeof(uint32_t)); + } + return array; + } + case JSC::JSType::Int16ArrayType: { + JSC::JSInt16Array* array = JSC::JSInt16Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeInt16, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * sizeof(int16_t)); + } + + return array; + } + case JSC::JSType::Uint16ArrayType: { + JSC::JSUint16Array* array = JSC::JSUint16Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeUint16, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * sizeof(uint16_t)); + } + return array; + } + case JSC::JSType::Float16ArrayType: { + JSC::JSFloat16Array* array = JSC::JSFloat16Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeFloat16, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * 2); // sizeof(float16_t) + } + return array; + } + case JSC::JSType::Float32ArrayType: { + JSC::JSFloat32Array* array = JSC::JSFloat32Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeFloat32, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * sizeof(float)); + } + return array; + } + case JSC::JSType::Float64ArrayType: { + JSC::JSFloat64Array* array = JSC::JSFloat64Array::createUninitialized(globalObject, globalObject->typedArrayStructure(TypedArrayType::TypeFloat64, false), length); + if (UNLIKELY(array == nullptr)) { + return {}; + } + + if (length > 0) { + memcpy(array->vector(), reinterpret_cast(cell.value.typed_array.data), length * sizeof(double)); + } + return array; + } + default: { + RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("TODO: implement this typed array type"); + } + } + } + default: { + RELEASE_ASSERT_NOT_REACHED(); + } + } +} + +static JSC::JSValue toJS(JSC::Structure* structure, DataCell* cells, unsigned count, JSC::JSGlobalObject* globalObject) +{ + auto& vm = globalObject->vm(); + auto* object = JSC::constructEmptyObject(vm, structure); + auto scope = DECLARE_THROW_SCOPE(vm); + + for (unsigned i = 0; i < count; i++) { + auto& cell = cells[i]; + JSValue value = toJS(vm, globalObject, cell); + RETURN_IF_EXCEPTION(scope, {}); + object->putDirectOffset(vm, i, value); + } + + return object; +} + +static JSC::JSValue toJS(JSC::JSArray* array, JSC::Structure* structure, DataCell* cells, unsigned count, JSC::JSGlobalObject* globalObject) +{ + JSValue value = toJS(structure, cells, count, globalObject); + if (value.isEmpty()) + return {}; + + if (array) { + array->push(globalObject, value); + return array; + } + + auto* newArray = JSC::constructEmptyArray(globalObject, static_cast(nullptr), 1); + if (!newArray) + return {}; + + newArray->putDirectIndex(globalObject, 0, value); + return newArray; +} + +extern "C" EncodedJSValue JSC__constructObjectFromDataCell( + JSC::JSGlobalObject* globalObject, + EncodedJSValue encodedArrayValue, + EncodedJSValue encodedStructureValue, DataCell* cells, unsigned count) +{ + JSValue arrayValue = JSValue::decode(encodedArrayValue); + JSValue structureValue = JSValue::decode(encodedStructureValue); + auto* array = arrayValue ? jsDynamicCast(arrayValue) : nullptr; + auto* structure = jsDynamicCast(structureValue); + + return JSValue::encode(toJS(array, structure, cells, count, globalObject)); +} + +extern "C" EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject, JSC::JSCell* owner, unsigned int inlineCapacity, BunString* names) +{ + auto& vm = globalObject->vm(); + Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), inlineCapacity); + if (owner) { + vm.writeBarrier(owner, structure); + } else { + vm.writeBarrier(structure); + } + ensureStillAliveHere(structure); + + PropertyNameArray propertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); + for (unsigned i = 0; i < inlineCapacity; i++) { + propertyNames.add(Identifier::fromString(vm, names[i].toWTFString())); + } + + PropertyOffset offset = 0; + for (unsigned i = 0; i < inlineCapacity; i++) { + structure = structure->addPropertyTransition(vm, structure, propertyNames[i], 0, offset); + } + + return JSValue::encode(structure); +} + +extern "C" EncodedJSValue JSC__createEmptyObjectWithStructure(JSC::JSGlobalObject* globalObject, JSC::Structure* structure) +{ + auto& vm = globalObject->vm(); + auto* object = JSC::constructEmptyObject(vm, structure); + + ensureStillAliveHere(object); + vm.writeBarrier(object); + + return JSValue::encode(object); +} + +extern "C" void JSC__putDirectOffset(JSC::VM* vm, JSC::EncodedJSValue object, unsigned int offset, JSC::EncodedJSValue value) +{ + JSValue::decode(object).getObject()->putDirectOffset(*vm, offset, JSValue::decode(value)); +} + +} diff --git a/src/bun.js/bindings/ScriptExecutionContext.cpp b/src/bun.js/bindings/ScriptExecutionContext.cpp index 87de0125a0dda..06e5b7ddba82c 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.cpp +++ b/src/bun.js/bindings/ScriptExecutionContext.cpp @@ -3,10 +3,10 @@ #include "ScriptExecutionContext.h" #include "MessagePort.h" -#include "webcore/WebSocket.h" #include "libusockets.h" #include "_libusockets.h" #include "BunClientData.h" +#include "EventLoopTask.h" extern "C" void Bun__startLoop(us_loop_t* loop); diff --git a/src/bun.js/bindings/ScriptExecutionContext.h b/src/bun.js/bindings/ScriptExecutionContext.h index 4b60323594133..e2cf419111e64 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.h +++ b/src/bun.js/bindings/ScriptExecutionContext.h @@ -29,44 +29,7 @@ class WebSocket; class MessagePort; class ScriptExecutionContext; - -class EventLoopTask { - WTF_MAKE_ISO_ALLOCATED(EventLoopTask); - -public: - enum CleanupTaskTag { CleanupTask }; - - template::value && std::is_convertible>::value>::type> - EventLoopTask(T task) - : m_task(WTFMove(task)) - , m_isCleanupTask(false) - { - } - - EventLoopTask(Function&& task) - : m_task([task = WTFMove(task)](ScriptExecutionContext&) { task(); }) - , m_isCleanupTask(false) - { - } - - template>::value>::type> - EventLoopTask(CleanupTaskTag, T task) - : m_task(WTFMove(task)) - , m_isCleanupTask(true) - { - } - - void performTask(ScriptExecutionContext& context) - { - m_task(context); - delete this; - } - bool isCleanupTask() const { return m_isCleanupTask; } - -protected: - Function m_task; - bool m_isCleanupTask; -}; +class EventLoopTask; using ScriptExecutionContextIdentifier = uint32_t; diff --git a/src/bun.js/bindings/Serialization.cpp b/src/bun.js/bindings/Serialization.cpp index ad9e254da7cfb..fafb08b3736e6 100644 --- a/src/bun.js/bindings/Serialization.cpp +++ b/src/bun.js/bindings/Serialization.cpp @@ -12,7 +12,7 @@ using namespace WebCore; struct SerializedValueSlice { const uint8_t* bytes; size_t size; - WebCore::SerializedScriptValue* value; + WebCore::SerializedScriptValue* value; // NOLINT }; /// Returns a "slice" that also contains a pointer to the SerializedScriptValue. Must be freed by the caller diff --git a/src/bun.js/bindings/Strong.cpp b/src/bun.js/bindings/Strong.cpp index 045b4484ac309..20728656e673e 100644 --- a/src/bun.js/bindings/Strong.cpp +++ b/src/bun.js/bindings/Strong.cpp @@ -1,10 +1,17 @@ #include "root.h" #include #include "BunClientData.h" +#include "wtf/DebugHeap.h" #include "Strong.h" namespace Bun { +DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(StrongRef); + +#if ENABLE(MALLOC_BREAKDOWN) +WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(StrongRef); +#else WTF_MAKE_ISO_ALLOCATED_IMPL(StrongRef); +#endif } diff --git a/src/bun.js/bindings/Strong.h b/src/bun.js/bindings/Strong.h index fe7bfa9e81098..8da881a282374 100644 --- a/src/bun.js/bindings/Strong.h +++ b/src/bun.js/bindings/Strong.h @@ -1,14 +1,21 @@ #pragma once #include "root.h" +#include "wtf/DebugHeap.h" +#include "wtf/IsoMalloc.h" #include namespace Bun { +DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(StrongRef); // We tried to pool these // But it was very complicated class StrongRef { +#if ENABLE(MALLOC_BREAKDOWN) + WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(StrongRef); +#else WTF_MAKE_ISO_ALLOCATED(StrongRef); +#endif public: StrongRef(JSC::VM& vm, JSC::JSValue value) diff --git a/src/bun.js/bindings/URLSearchParams.h b/src/bun.js/bindings/URLSearchParams.h index 02258c82cc8e3..65400d3672227 100644 --- a/src/bun.js/bindings/URLSearchParams.h +++ b/src/bun.js/bindings/URLSearchParams.h @@ -34,6 +34,7 @@ namespace WebCore { class DOMURL; +class ScriptExecutionContext; class URLSearchParams : public RefCounted { public: @@ -66,6 +67,7 @@ class URLSearchParams : public RefCounted { size_t m_index { 0 }; }; Iterator createIterator() { return Iterator { *this }; } + Iterator createIterator(const ScriptExecutionContext* context) { return Iterator { *this }; } private: const Vector>& pairs() const { return m_pairs; } diff --git a/src/bun.js/bindings/Uint8Array.cpp b/src/bun.js/bindings/Uint8Array.cpp new file mode 100644 index 0000000000000..697bf61a5ac46 --- /dev/null +++ b/src/bun.js/bindings/Uint8Array.cpp @@ -0,0 +1,44 @@ +#include "root.h" + +#include "JavaScriptCore/JSArrayBuffer.h" +#include "JavaScriptCore/TypedArrayType.h" +#include "mimalloc.h" + +namespace Bun { + +extern "C" JSC::EncodedJSValue JSUint8Array__fromDefaultAllocator(JSC::JSGlobalObject* lexicalGlobalObject, uint8_t* ptr, size_t length) +{ + JSC::JSUint8Array* uint8Array; + + if (LIKELY(length > 0)) { + auto buffer = ArrayBuffer::createFromBytes({ ptr, length }, createSharedTask([](void* p) { + mi_free(p); + })); + + uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, lexicalGlobalObject->typedArrayStructure(JSC::TypeUint8, false), WTFMove(buffer), 0, length); + } else { + uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, lexicalGlobalObject->typedArrayStructure(JSC::TypeUint8, false), 0); + } + + return JSC::JSValue::encode(uint8Array); +} + +extern "C" JSC::EncodedJSValue JSArrayBuffer__fromDefaultAllocator(JSC::JSGlobalObject* lexicalGlobalObject, uint8_t* ptr, size_t length) +{ + + JSC::JSArrayBuffer* arrayBuffer; + + if (LIKELY(length > 0)) { + RefPtr buffer = ArrayBuffer::createFromBytes({ ptr, length }, createSharedTask([](void* p) { + mi_free(p); + })); + + arrayBuffer = JSC::JSArrayBuffer::create(lexicalGlobalObject->vm(), lexicalGlobalObject->arrayBufferStructure(), WTFMove(buffer)); + } else { + arrayBuffer = JSC::JSArrayBuffer::create(lexicalGlobalObject->vm(), lexicalGlobalObject->arrayBufferStructure(), nullptr); + } + + return JSC::JSValue::encode(arrayBuffer); +} + +} \ No newline at end of file diff --git a/src/bun.js/bindings/Undici.cpp b/src/bun.js/bindings/Undici.cpp new file mode 100644 index 0000000000000..879b9f4fe25bf --- /dev/null +++ b/src/bun.js/bindings/Undici.cpp @@ -0,0 +1,59 @@ +#include "root.h" + +#include "JSDOMURL.h" +#include "JSURLSearchParams.h" +#include "JSAbortSignal.h" +#include "JSDOMGlobalObjectInlines.h" +#include "ZigGlobalObject.h" + +#include "JSFetchHeaders.h" +#include "JSDOMFormData.h" +#include "JavaScriptCore/ObjectConstructor.h" + +#include "helpers.h" +#include "BunClientData.h" + +#include "JavaScriptCore/AggregateError.h" +#include "JavaScriptCore/JSFunction.h" +#include "JSDOMFile.h" + +namespace Bun { + +using namespace JSC; +using namespace WebCore; + +// Ensure overriding globals doesn't impact usages. +JSC::JSValue createUndiciInternalBinding(Zig::GlobalObject* globalObject) +{ + auto& vm = globalObject->vm(); + + auto* obj = constructEmptyObject(globalObject); + obj->putDirectIndex( + globalObject, 0, + globalObject->JSResponseConstructor()); + obj->putDirectIndex( + globalObject, 1, + globalObject->JSRequestConstructor()); + obj->putDirectIndex( + globalObject, 2, + WebCore::JSFetchHeaders::getConstructor(vm, globalObject)); + obj->putDirectIndex( + globalObject, 3, + WebCore::JSDOMFormData::getConstructor(vm, globalObject)); + obj->putDirectIndex( + globalObject, 4, + globalObject->JSDOMFileConstructor()); + obj->putDirectIndex( + globalObject, 5, + JSDOMURL::getConstructor(vm, globalObject)); + obj->putDirectIndex( + globalObject, 6, + JSAbortSignal::getConstructor(vm, globalObject)); + obj->putDirectIndex( + globalObject, 7, + JSURLSearchParams::getConstructor(vm, globalObject)); + + return obj; +} + +} \ No newline at end of file diff --git a/src/bun.js/bindings/Undici.h b/src/bun.js/bindings/Undici.h new file mode 100644 index 0000000000000..5bc89e5a29124 --- /dev/null +++ b/src/bun.js/bindings/Undici.h @@ -0,0 +1,6 @@ + +namespace Bun { + +JSC::JSValue createUndiciInternalBinding(Zig::GlobalObject* globalObject); + +} \ No newline at end of file diff --git a/src/bun.js/bindings/Weak.cpp b/src/bun.js/bindings/Weak.cpp index c308b389d9006..90d4e9839101e 100644 --- a/src/bun.js/bindings/Weak.cpp +++ b/src/bun.js/bindings/Weak.cpp @@ -10,12 +10,17 @@ namespace Bun { enum class WeakRefType : uint32_t { None = 0, FetchResponse = 1, + PostgreSQLQueryClient = 2, }; typedef void (*WeakRefFinalizeFn)(void* context); +// clang-format off #define FOR_EACH_WEAK_REF_TYPE(macro) \ - macro(FetchResponse) + macro(FetchResponse) \ + macro(PostgreSQLQueryClient) + +// clang-format on #define DECLARE_WEAK_REF_OWNER(X) \ extern "C" void Bun__##X##_finalize(void* context); @@ -32,6 +37,9 @@ class WeakRefOwner : public JSC::WeakHandleOwner { case WeakRefType::FetchResponse: Bun__FetchResponse_finalize(context); break; + case WeakRefType::PostgreSQLQueryClient: + // Bun__PostgreSQLQueryClient_finalize(context); + break; default: break; } @@ -52,6 +60,9 @@ static JSC::WeakHandleOwner* getWeakRefOwner(WeakRefType type) case WeakRefType::FetchResponse: { return getWeakRefOwner(); } + case WeakRefType::PostgreSQLQueryClient: { + return getWeakRefOwner(); + } default: { RELEASE_ASSERT_NOT_REACHED(); } @@ -107,4 +118,4 @@ extern "C" JSC::EncodedJSValue Bun__WeakRef__get(Bun::WeakRef* weakRef) return JSC::JSValue::encode(cell); } return JSC::encodedJSValue(); -} +} \ No newline at end of file diff --git a/src/bun.js/bindings/ZigGeneratedCode.cpp b/src/bun.js/bindings/ZigGeneratedCode.cpp index 777c6455ea3f9..45d939c024cb7 100644 --- a/src/bun.js/bindings/ZigGeneratedCode.cpp +++ b/src/bun.js/bindings/ZigGeneratedCode.cpp @@ -15,18 +15,18 @@ using namespace WebCore; /* -- BEGIN DOMCall DEFINITIONS -- */ -extern "C" JSC_DECLARE_HOST_FUNCTION(FFI__ptr__slowpathWrapper); -extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(FFI__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, JSC::JSUint8Array*)); - -JSC_DEFINE_JIT_OPERATION(FFI__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, JSC::JSUint8Array* arg1)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return FFI__ptr__fastpath(lexicalGlobalObject, thisValue, arg1); -} +// BUN_DECLARE_HOST_FUNCTION(FFI__ptr__slowpathWrapper); +// extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(FFI__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, JSC::JSUint8Array*)); + +// JSC_DEFINE_JIT_OPERATION(FFI__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, JSC::JSUint8Array* arg1)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { FFI__ptr__fastpath(lexicalGlobalObject, thisValue, arg1) }; +// } JSC_DEFINE_HOST_FUNCTION(FFI__ptr__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return FFI__ptr__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -35,37 +35,45 @@ JSC_DEFINE_HOST_FUNCTION(FFI__ptr__slowpathWrapper, (JSC::JSGlobalObject * globa extern "C" void FFI__ptr__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_ptr_signature( - FFI__ptr__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecDoubleReal, - JSC::SpecUint8Array); + // static const JSC::DOMJIT::Signature DOMJIT_ptr_signature( + // FFI__ptr__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecDoubleReal, + // JSC::SpecUint8Array); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 1, + // String("ptr"_s), + // FFI__ptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, FFI__ptr__slowpathWrapper, + // &DOMJIT_ptr_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 1, String("ptr"_s), - FFI__ptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, FFI__ptr__slowpathWrapper, - &DOMJIT_ptr_signature); + FFI__ptr__slowpathWrapper, + ImplementationVisibility::Public, + NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "ptr"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__u8__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__u8__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__u8__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__u8__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__u8__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__u8__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__u8__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__u8__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__u8__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -74,38 +82,47 @@ JSC_DEFINE_HOST_FUNCTION(Reader__u8__slowpathWrapper, (JSC::JSGlobalObject * glo extern "C" void Reader__u8__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_u8_signature( - Reader__u8__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt32Only, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_u8_signature( + // Reader__u8__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt32Only, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("u8"_s), + // Reader__u8__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u8__slowpathWrapper, + // &DOMJIT_u8_signature); + JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("u8"_s), - Reader__u8__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u8__slowpathWrapper, - &DOMJIT_u8_signature); + Reader__u8__slowpathWrapper, + ImplementationVisibility::Public, + NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "u8"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__u16__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__u16__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__u16__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__u16__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__u16__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__u16__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__u16__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__u16__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__u16__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -114,38 +131,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__u16__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__u16__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_u16_signature( - Reader__u16__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt32Only, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_u16_signature( + // Reader__u16__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt32Only, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("u16"_s), + // Reader__u16__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u16__slowpathWrapper, + // &DOMJIT_u16_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("u16"_s), - Reader__u16__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u16__slowpathWrapper, - &DOMJIT_u16_signature); + Reader__u16__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "u16"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__u32__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__u32__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__u32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__u32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__u32__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__u32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__u32__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__u32__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__u32__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -154,38 +177,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__u32__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__u32__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_u32_signature( - Reader__u32__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt32Only, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_u32_signature( + // Reader__u32__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt32Only, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("u32"_s), + // Reader__u32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u32__slowpathWrapper, + // &DOMJIT_u32_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("u32"_s), - Reader__u32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u32__slowpathWrapper, - &DOMJIT_u32_signature); + Reader__u32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "u32"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__ptr__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__ptr__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__ptr__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__ptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__ptr__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__ptr__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__ptr__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -194,38 +223,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__ptr__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__ptr__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_ptr_signature( - Reader__ptr__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt52Any, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_ptr_signature( + // Reader__ptr__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt52Any, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("ptr"_s), + // Reader__ptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__ptr__slowpathWrapper, + // &DOMJIT_ptr_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("ptr"_s), - Reader__ptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__ptr__slowpathWrapper, - &DOMJIT_ptr_signature); + Reader__ptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "ptr"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__i8__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__i8__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__i8__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__i8__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__i8__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__i8__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__i8__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__i8__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__i8__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -234,38 +269,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__i8__slowpathWrapper, (JSC::JSGlobalObject * glo extern "C" void Reader__i8__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_i8_signature( - Reader__i8__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt32Only, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_i8_signature( + // Reader__i8__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt32Only, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("i8"_s), + // Reader__i8__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i8__slowpathWrapper, + // &DOMJIT_i8_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("i8"_s), - Reader__i8__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i8__slowpathWrapper, - &DOMJIT_i8_signature); + Reader__i8__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "i8"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__i16__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__i16__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__i16__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__i16__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__i16__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__i16__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__i16__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__i16__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__i16__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -274,38 +315,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__i16__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__i16__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_i16_signature( - Reader__i16__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt32Only, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_i16_signature( + // Reader__i16__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt32Only, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("i16"_s), + // Reader__i16__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i16__slowpathWrapper, + // &DOMJIT_i16_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("i16"_s), - Reader__i16__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i16__slowpathWrapper, - &DOMJIT_i16_signature); + Reader__i16__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "i16"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__i32__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__i32__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__i32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__i32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__i32__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__i32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__i32__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__i32__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__i32__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -314,38 +361,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__i32__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__i32__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_i32_signature( - Reader__i32__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt32Only, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_i32_signature( + // Reader__i32__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt32Only, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("i32"_s), + // Reader__i32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i32__slowpathWrapper, + // &DOMJIT_i32_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("i32"_s), - Reader__i32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i32__slowpathWrapper, - &DOMJIT_i32_signature); + Reader__i32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "i32"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__i64__slowpathWrapper); +BUN_DECLARE_HOST_FUNCTION(Reader__i64__slowpathWrapper); extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__i64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__i64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__i64__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__i64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__i64__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__i64__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__i64__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -354,38 +407,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__i64__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__i64__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_i64_signature( - Reader__i64__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecHeapTop, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_i64_signature( + // Reader__i64__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecHeapTop, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("i64"_s), + // Reader__i64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i64__slowpathWrapper, + // &DOMJIT_i64_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("i64"_s), - Reader__i64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__i64__slowpathWrapper, - &DOMJIT_i64_signature); + Reader__i64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "i64"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__u64__slowpathWrapper); -extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__u64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); +BUN_DECLARE_HOST_FUNCTION(Reader__u64__slowpathWrapper); +// extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__u64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__u64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__u64__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__u64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__u64__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__u64__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__u64__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -394,38 +453,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__u64__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__u64__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_u64_signature( - Reader__u64__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecHeapTop, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_u64_signature( + // Reader__u64__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecHeapTop, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("u64"_s), + // Reader__u64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u64__slowpathWrapper, + // &DOMJIT_u64_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("u64"_s), - Reader__u64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__u64__slowpathWrapper, - &DOMJIT_u64_signature); + Reader__u64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "u64"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__intptr__slowpathWrapper); -extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__intptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); +BUN_DECLARE_HOST_FUNCTION(Reader__intptr__slowpathWrapper); +// extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__intptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__intptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__intptr__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__intptr__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__intptr__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__intptr__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__intptr__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -434,38 +499,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__intptr__slowpathWrapper, (JSC::JSGlobalObject * extern "C" void Reader__intptr__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_intptr_signature( - Reader__intptr__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecInt52Any, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_intptr_signature( + // Reader__intptr__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecInt52Any, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("intptr"_s), + // Reader__intptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__intptr__slowpathWrapper, + // &DOMJIT_intptr_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("intptr"_s), - Reader__intptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__intptr__slowpathWrapper, - &DOMJIT_intptr_signature); + Reader__intptr__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "intptr"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__f32__slowpathWrapper); -extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__f32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); +BUN_DECLARE_HOST_FUNCTION(Reader__f32__slowpathWrapper); +// extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__f32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__f32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__f32__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__f32__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__f32__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__f32__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__f32__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -474,38 +545,44 @@ JSC_DEFINE_HOST_FUNCTION(Reader__f32__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__f32__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_f32_signature( - Reader__f32__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecDoubleReal, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_f32_signature( + // Reader__f32__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecDoubleReal, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("f32"_s), + // Reader__f32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__f32__slowpathWrapper, + // &DOMJIT_f32_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("f32"_s), - Reader__f32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__f32__slowpathWrapper, - &DOMJIT_f32_signature); + Reader__f32__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "f32"_s), function); } -extern "C" JSC_DECLARE_HOST_FUNCTION(Reader__f64__slowpathWrapper); -extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__f64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); +BUN_DECLARE_HOST_FUNCTION(Reader__f64__slowpathWrapper); +// extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(Reader__f64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t, int32_t)); -JSC_DEFINE_JIT_OPERATION(Reader__f64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return Reader__f64__fastpath(lexicalGlobalObject, thisValue, arg1, arg2); -} +// JSC_DEFINE_JIT_OPERATION(Reader__f64__fastpathWrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue, int64_t arg1, int32_t arg2)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// return { Reader__f64__fastpath(lexicalGlobalObject, thisValue, arg1, arg2) }; +// } JSC_DEFINE_HOST_FUNCTION(Reader__f64__slowpathWrapper, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* frame)) { return Reader__f64__slowpath(globalObject, JSValue::encode(frame->thisValue()), reinterpret_cast(frame->addressOfArgumentsStart()), frame->argumentCount()); @@ -514,20 +591,26 @@ JSC_DEFINE_HOST_FUNCTION(Reader__f64__slowpathWrapper, (JSC::JSGlobalObject * gl extern "C" void Reader__f64__put(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) { JSC::JSObject* thisObject = JSC::jsCast(JSC::JSValue::decode(value)); - static const JSC::DOMJIT::Signature DOMJIT_f64_signature( - Reader__f64__fastpathWrapper, - thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecDoubleReal, - JSC::SpecInt52Any, - JSC::SpecInt32Only); + // static const JSC::DOMJIT::Signature DOMJIT_f64_signature( + // Reader__f64__fastpathWrapper, + // thisObject->classInfo(), + // JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), + // JSC::SpecDoubleReal, + // JSC::SpecInt52Any, + // JSC::SpecInt32Only); + // JSFunction* function = JSFunction::create( + // globalObject->vm(), + // globalObject, + // 2, + // String("f64"_s), + // Reader__f64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__f64__slowpathWrapper, + // &DOMJIT_f64_signature); JSFunction* function = JSFunction::create( globalObject->vm(), globalObject, 2, String("f64"_s), - Reader__f64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic, Reader__f64__slowpathWrapper, - &DOMJIT_f64_signature); + Reader__f64__slowpathWrapper, ImplementationVisibility::Public, NoIntrinsic); thisObject->putDirect( globalObject->vm(), Identifier::fromString(globalObject->vm(), "f64"_s), diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 42c2c056a3c22..b6a34291ebd51 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1,8 +1,7 @@ - #include "root.h" #include "ZigGlobalObject.h" #include "helpers.h" - +#include "JavaScriptCore/ArgList.h" #include "JavaScriptCore/JSImmutableButterfly.h" #include "wtf/text/Base64.h" #include "JavaScriptCore/BuiltinNames.h" @@ -20,8 +19,6 @@ #include "JavaScriptCore/FunctionPrototype.h" #include "JavaScriptCore/GetterSetter.h" #include "JavaScriptCore/GlobalObjectMethodTable.h" -#include "JavaScriptCore/HashMapImpl.h" -#include "JavaScriptCore/HashMapImplInlines.h" #include "JavaScriptCore/Heap.h" #include "JavaScriptCore/Identifier.h" #include "JavaScriptCore/InitializeThreading.h" @@ -56,11 +53,9 @@ #include "JavaScriptCore/StackFrame.h" #include "JavaScriptCore/StackVisitor.h" #include "JavaScriptCore/VM.h" - #include "AddEventListenerOptions.h" #include "AsyncContextFrame.h" #include "BunClientData.h" -#include "BunClientData.h" #include "BunObject.h" #include "BunPlugin.h" #include "BunProcess.h" @@ -119,6 +114,8 @@ #include "JSSQLStatement.h" #include "JSStringDecoder.h" #include "JSTextEncoder.h" +#include "JSTextEncoderStream.h" +#include "JSTextDecoderStream.h" #include "JSTransformStream.h" #include "JSTransformStreamDefaultController.h" #include "JSURLSearchParams.h" @@ -130,6 +127,7 @@ #include "libusockets.h" #include "ModuleLoader.h" #include "napi_external.h" +#include "napi_handle_scope.h" #include "napi.h" #include "NodeHTTP.h" #include "NodeVM.h" @@ -148,6 +146,9 @@ #include "UtilInspect.h" #include "Base64Helpers.h" #include "wtf/text/OrdinalNumber.h" +#include "ErrorCode.h" +#include "v8/V8GlobalInternals.h" +#include "EventLoopTask.h" #if ENABLE(REMOTE_INSPECTOR) #include "JavaScriptCore/RemoteInspectorServer.h" @@ -166,14 +167,11 @@ using namespace Bun; -extern "C" JSC__JSValue Bun__NodeUtil__jsParseArgs(JSC::JSGlobalObject*, JSC::CallFrame*); +BUN_DECLARE_HOST_FUNCTION(Bun__NodeUtil__jsParseArgs); +BUN_DECLARE_HOST_FUNCTION(BUN__HTTP2__getUnpackedSettings); +BUN_DECLARE_HOST_FUNCTION(BUN__HTTP2_getPackedSettings); -extern "C" JSC::EncodedJSValue Bun__fetch(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -extern "C" JSC::EncodedJSValue H2FrameParser__getConstructor(Zig::GlobalObject* globalObject); -extern "C" JSC::EncodedJSValue BUN__HTTP2__getUnpackedSettings(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -extern "C" JSC::EncodedJSValue BUN__HTTP2_getPackedSettings(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -using JSGlobalObject - = JSC::JSGlobalObject; +using JSGlobalObject = JSC::JSGlobalObject; using Exception = JSC::Exception; using JSValue = JSC::JSValue; using JSString = JSC::JSString; @@ -193,7 +191,7 @@ static bool has_loaded_jsc = false; Structure* createMemoryFootprintStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject); extern "C" WebCore::Worker* WebWorker__getParentWorker(void*); -extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(const char* ptr, size_t length)) +extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(const char* ptr, size_t length), bool evalMode) { // NOLINTBEGIN if (has_loaded_jsc) @@ -210,23 +208,24 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c JSC::Options::useConcurrentJIT() = true; // JSC::Options::useSigillCrashAnalyzer() = true; - JSC::Options::useWebAssembly() = true; + JSC::Options::useWasm() = true; JSC::Options::useSourceProviderCache() = true; // JSC::Options::useUnlinkedCodeBlockJettisoning() = false; JSC::Options::exposeInternalModuleLoader() = true; JSC::Options::useSharedArrayBuffer() = true; JSC::Options::useJIT() = true; JSC::Options::useBBQJIT() = true; + JSC::Options::useUint8ArrayBase64Methods() = true; JSC::Options::useJITCage() = false; JSC::Options::useShadowRealm() = true; - JSC::Options::useResizableArrayBuffer() = true; - JSC::Options::usePromiseWithResolversMethod() = true; JSC::Options::useV8DateParser() = true; + JSC::Options::evalMode() = evalMode; + JSC::Options::usePromiseTryMethod() = true; + JSC::Options::useRegExpEscape() = true; #ifdef BUN_DEBUG JSC::Options::showPrivateScriptsInStackTraces() = true; #endif - JSC::Options::useSetMethods() = true; if (LIKELY(envc > 0)) { while (envc--) { @@ -249,7 +248,8 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c } extern "C" void* Bun__getVM(); -extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); + +extern "C" void Bun__setDefaultGlobalObject(Zig::GlobalObject* globalObject); // Error.captureStackTrace may cause computeErrorInfo to be called twice // Rather than figure out the plumbing in JSC, we just skip the next call @@ -280,7 +280,7 @@ static JSValue formatStackTraceToJSValue(JSC::VM& vm, Zig::GlobalObject* globalO auto* str = errorMessage.toString(lexicalGlobalObject); if (str->length() > 0) { sb.append("Error: "_s); - sb.append(str->value(lexicalGlobalObject)); + sb.append(str->value(lexicalGlobalObject).data); } else { sb.append("Error"_s); } @@ -348,7 +348,8 @@ static JSValue formatStackTraceToJSValue(JSC::VM& vm, Zig::GlobalObject* globalO WTF::String Bun::formatStackTrace( JSC::VM& vm, - JSC::JSGlobalObject* globalObject, + Zig::GlobalObject* globalObject, + JSC::JSGlobalObject* lexicalGlobalObject, const WTF::String& name, const WTF::String& message, OrdinalNumber& line, @@ -394,15 +395,15 @@ WTF::String Bun::formatStackTrace( // If it's not a Zig::GlobalObject, don't bother source-mapping it. if (globalObject && !sourceURLForFrame.isEmpty()) { + // https://github.com/oven-sh/bun/issues/3595 if (!sourceURLForFrame.isEmpty()) { - remappedFrame.source_url = Bun::toString(sourceURLForFrame); - } else { - // https://github.com/oven-sh/bun/issues/3595 - remappedFrame.source_url = BunStringEmpty; - } + remappedFrame.source_url = Bun::toStringRef(sourceURLForFrame); - // This ensures the lifetime of the sourceURL is accounted for correctly - Bun__remapStackFramePositions(globalObject, &remappedFrame, 1); + // This ensures the lifetime of the sourceURL is accounted for correctly + Bun__remapStackFramePositions(globalObject, &remappedFrame, 1); + + sourceURLForFrame = remappedFrame.source_url.toWTFString(); + } } // there is always a newline before each stack frame line, ensuring that the name + message @@ -461,7 +462,7 @@ WTF::String Bun::formatStackTrace( if (callee->isObject()) { JSValue functionNameValue = callee->getObject()->getDirect(vm, vm.propertyNames->name); if (functionNameValue && functionNameValue.isString()) { - functionName = functionNameValue.toWTFString(globalObject); + functionName = functionNameValue.toWTFString(lexicalGlobalObject); } } } @@ -495,17 +496,40 @@ WTF::String Bun::formatStackTrace( String sourceURLForFrame = frame.sourceURL(vm); + // Sometimes, the sourceURL is empty. + // For example, pages in Next.js. + if (sourceURLForFrame.isEmpty()) { + // hasLineAndColumnInfo() checks codeBlock(), so this is safe to access here. + const auto& source = frame.codeBlock()->source(); + + // source.isNull() is true when the SourceProvider is a null pointer. + if (!source.isNull()) { + auto* provider = source.provider(); + // I'm not 100% sure we should show sourceURLDirective here. + if (!provider->sourceURLDirective().isEmpty()) { + sourceURLForFrame = provider->sourceURLDirective(); + } else if (!provider->sourceURL().isEmpty()) { + sourceURLForFrame = provider->sourceURL(); + } else { + const auto& origin = provider->sourceOrigin(); + if (!origin.isNull()) { + sourceURLForFrame = origin.string(); + } + } + } + } + // If it's not a Zig::GlobalObject, don't bother source-mapping it. - if (globalObject) { + if (globalObject == lexicalGlobalObject && globalObject) { + // https://github.com/oven-sh/bun/issues/3595 if (!sourceURLForFrame.isEmpty()) { - remappedFrame.source_url = Bun::toString(sourceURLForFrame); - } else { - // https://github.com/oven-sh/bun/issues/3595 - remappedFrame.source_url = BunStringEmpty; - } + remappedFrame.source_url = Bun::toStringRef(sourceURLForFrame); - // This ensures the lifetime of the sourceURL is accounted for correctly - Bun__remapStackFramePositions(globalObject, &remappedFrame, 1); + // This ensures the lifetime of the sourceURL is accounted for correctly + Bun__remapStackFramePositions(globalObject, &remappedFrame, 1); + + sourceURLForFrame = remappedFrame.source_url.toWTFString(); + } } if (!hasSet) { @@ -545,6 +569,7 @@ WTF::String Bun::formatStackTrace( static String computeErrorInfoWithoutPrepareStackTrace( JSC::VM& vm, Zig::GlobalObject* globalObject, + JSC::JSGlobalObject* lexicalGlobalObject, Vector& stackTrace, OrdinalNumber& line, OrdinalNumber& column, @@ -558,17 +583,19 @@ static String computeErrorInfoWithoutPrepareStackTrace( if (errorInstance) { // Note that we are not allowed to allocate memory in here. It's called inside a finalizer. if (auto* instance = jsDynamicCast(errorInstance)) { - auto* lexicalGlobalObject = errorInstance->globalObject(); + if (!lexicalGlobalObject) { + lexicalGlobalObject = errorInstance->globalObject(); + } name = instance->sanitizedNameString(lexicalGlobalObject); message = instance->sanitizedMessageString(lexicalGlobalObject); } } if (UNLIKELY(!globalObject)) { - globalObject = Bun__getDefaultGlobal(); + globalObject = defaultGlobalObject(); } - return Bun::formatStackTrace(vm, globalObject, name, message, line, column, sourceURL, stackTrace, errorInstance); + return Bun::formatStackTrace(vm, globalObject, lexicalGlobalObject, name, message, line, column, sourceURL, stackTrace, errorInstance); } static String computeErrorInfoWithPrepareStackTrace(JSC::VM& vm, Zig::GlobalObject* globalObject, JSC::JSGlobalObject* lexicalGlobalObject, Vector& stackFrames, OrdinalNumber& line, OrdinalNumber& column, String& sourceURL, JSObject* errorObject, JSObject* prepareStackTrace) @@ -588,10 +615,11 @@ static String computeErrorInfoWithPrepareStackTrace(JSC::VM& vm, Zig::GlobalObje // We need to sourcemap it if it's a GlobalObject. if (globalObject == lexicalGlobalObject) { size_t framesCount = stackTrace.size(); - ZigStackFrame remappedFrames[framesCount]; + ZigStackFrame remappedFrames[64]; + framesCount = framesCount > 64 ? 64 : framesCount; for (int i = 0; i < framesCount; i++) { remappedFrames[i] = {}; - remappedFrames[i].source_url = Bun::toString(lexicalGlobalObject, stackTrace.at(i).sourceURL()); + remappedFrames[i].source_url = Bun::toStringRef(lexicalGlobalObject, stackTrace.at(i).sourceURL()); if (JSCStackFrame::SourcePositions* sourcePositions = stackTrace.at(i).getSourcePositions()) { remappedFrames[i].position.line_zero_based = sourcePositions->line.zeroBasedInt(); remappedFrames[i].position.column_zero_based = sourcePositions->column.zeroBasedInt(); @@ -635,15 +663,16 @@ static String computeErrorInfo(JSC::VM& vm, Vector& stackTrace, Ordi } Zig::GlobalObject* globalObject = nullptr; + JSC::JSGlobalObject* lexicalGlobalObject = nullptr; if (errorInstance) { - auto* lexicalGlobalObject = errorInstance->globalObject(); + lexicalGlobalObject = errorInstance->globalObject(); globalObject = jsDynamicCast(lexicalGlobalObject); // Error.prepareStackTrace - https://v8.dev/docs/stack-trace-api#customizing-stack-traces if (!globalObject) { // node:vm will use a different JSGlobalObject - globalObject = Bun__getDefaultGlobal(); + globalObject = defaultGlobalObject(); auto* errorConstructor = lexicalGlobalObject->m_errorStructure.constructor(lexicalGlobalObject); if (JSValue prepareStackTrace = errorConstructor->getIfPropertyExists(lexicalGlobalObject, Identifier::fromString(vm, "prepareStackTrace"_s))) { @@ -660,10 +689,10 @@ static String computeErrorInfo(JSC::VM& vm, Vector& stackTrace, Ordi } } - return computeErrorInfoWithoutPrepareStackTrace(vm, globalObject, stackTrace, line, column, sourceURL, errorInstance); + return computeErrorInfoWithoutPrepareStackTrace(vm, globalObject, lexicalGlobalObject, stackTrace, line, column, sourceURL, errorInstance); } -// TODO: @paperdave: remove this wrapper and make the WTF::Function from JavaScriptCore expeect OrdinalNumber instead of unsigned. +// TODO: @paperdave: remove this wrapper and make the WTF::Function from JavaScriptCore expect OrdinalNumber instead of unsigned. static String computeErrorInfoWrapper(JSC::VM& vm, Vector& stackTrace, unsigned int& line_in, unsigned int& column_in, String& sourceURL, JSObject* errorInstance) { OrdinalNumber line = OrdinalNumber::fromOneBasedInt(line_in); @@ -679,7 +708,7 @@ static String computeErrorInfoWrapper(JSC::VM& vm, Vector& stackTrac static void checkIfNextTickWasCalledDuringMicrotask(JSC::VM& vm) { - auto* globalObject = Bun__getDefaultGlobal(); + auto* globalObject = defaultGlobalObject(); if (auto nextTickQueueValue = globalObject->m_nextTickQueue.get()) { auto* queue = jsCast(nextTickQueueValue); globalObject->resetOnEachMicrotaskTick(); @@ -689,7 +718,7 @@ static void checkIfNextTickWasCalledDuringMicrotask(JSC::VM& vm) static void cleanupAsyncHooksData(JSC::VM& vm) { - auto* globalObject = Bun__getDefaultGlobal(); + auto* globalObject = defaultGlobalObject(); globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined()); globalObject->asyncHooksNeedsCleanup = false; if (!globalObject->m_nextTickQueue) { @@ -716,7 +745,6 @@ void Zig::GlobalObject::resetOnEachMicrotaskTick() extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, int32_t executionContextId, bool miniMode, bool evalMode, void* worker_ptr) { - auto heapSize = miniMode ? JSC::HeapType::Small : JSC::HeapType::Large; JSC::VM& vm = JSC::VM::create(heapSize).leakRef(); // This must happen before JSVMClientData::create @@ -725,55 +753,51 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, WebCore::JSVMClientData::create(&vm, Bun__getVM()); - Zig::GlobalObject* globalObject; - - if (UNLIKELY(executionContextId > -1)) { - globalObject = Zig::GlobalObject::create( - vm, - Zig::GlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), JSC::jsNull()), - static_cast(executionContextId)); - - if (auto* worker = static_cast(worker_ptr)) { - auto& options = worker->options(); - - // ensure remote termination works. - vm.ensureTerminationException(); - vm.forbidExecutionOnTermination(); + const auto createGlobalObject = [&]() -> Zig::GlobalObject* { + if (UNLIKELY(executionContextId > -1)) { + auto* structure = Zig::GlobalObject::createStructure(vm); + if (UNLIKELY(!structure)) { + return nullptr; + } + return Zig::GlobalObject::create( + vm, + structure, + static_cast(executionContextId)); + } else if (evalMode) { + auto* structure = Zig::EvalGlobalObject::createStructure(vm); + if (UNLIKELY(!structure)) { + return nullptr; + } + return Zig::EvalGlobalObject::create( + vm, + structure, + &Zig::EvalGlobalObject::s_globalObjectMethodTable); - if (options.bun.env) { - auto map = WTFMove(options.bun.env); - auto size = map->size(); - auto env = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), size >= JSFinalObject::maxInlineCapacity ? JSFinalObject::maxInlineCapacity : size); - for (auto k : *map) { - env->putDirect(vm, JSC::Identifier::fromString(vm, WTFMove(k.key)), JSC::jsString(vm, WTFMove(k.value))); - } - map->clear(); - globalObject->m_processEnvObject.set(vm, globalObject, env); + } else { + auto* structure = Zig::GlobalObject::createStructure(vm); + if (UNLIKELY(!structure)) { + return nullptr; } + return Zig::GlobalObject::create( + vm, + structure); } - } else if (evalMode) { - globalObject = Zig::EvalGlobalObject::create( - vm, - Zig::EvalGlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), - JSC::jsNull()), - &Zig::EvalGlobalObject::s_globalObjectMethodTable); + }; - } else { - globalObject = Zig::GlobalObject::create( - vm, - Zig::GlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), - JSC::jsNull())); + auto* globalObject = createGlobalObject(); + if (UNLIKELY(!globalObject)) { + BUN_PANIC("Failed to allocate JavaScript global object. Did your computer run out of memory?"); } globalObject->setConsole(console_client); globalObject->isThreadLocalDefaultGlobalObject = true; globalObject->setStackTraceLimit(DEFAULT_ERROR_STACK_TRACE_LIMIT); // Node.js defaults to 10 - vm.setOnComputeErrorInfo(computeErrorInfoWrapper); - + Bun__setDefaultGlobalObject(globalObject); JSC::gcProtect(globalObject); + vm.setOnComputeErrorInfo(computeErrorInfoWrapper); vm.setOnEachMicrotaskTick([](JSC::VM& vm) -> void { - auto* globalObject = Bun__getDefaultGlobal(); + auto* globalObject = defaultGlobalObject(); if (auto nextTickQueue = globalObject->m_nextTickQueue.get()) { globalObject->resetOnEachMicrotaskTick(); Bun::JSNextTickQueue* queue = jsCast(nextTickQueue); @@ -782,6 +806,43 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, } }); + if (executionContextId > -1) { + const auto initializeWorker = [&](WebCore::Worker& worker) -> void { + auto& options = worker.options(); + + if (options.bun.env) { + auto map = WTFMove(options.bun.env); + auto size = map->size(); + + // In theory, a GC could happen before we finish putting all the properties on the object. + // So we use a MarkedArgumentBuffer to ensure that the strings are not collected and we immediately put them on the object. + MarkedArgumentBuffer strings; + strings.ensureCapacity(map->size()); + for (const auto& value : map->values()) { + strings.append(jsString(vm, value)); + } + + auto env = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), size >= JSFinalObject::maxInlineCapacity ? JSFinalObject::maxInlineCapacity : size); + size_t i = 0; + for (auto k : *map) { + // They can have environment variables with numbers as keys. + // So we must use putDirectMayBeIndex to handle that. + env->putDirectMayBeIndex(globalObject, JSC::Identifier::fromString(vm, WTFMove(k.key)), strings.at(i++)); + } + map->clear(); + globalObject->m_processEnvObject.set(vm, globalObject, env); + } + + // ensure remote termination works. + vm.ensureTerminationException(); + vm.forbidExecutionOnTermination(); + }; + + if (auto* worker = static_cast(worker_ptr)) { + initializeWorker(*worker); + } + } + return globalObject; } @@ -859,7 +920,7 @@ extern "C" bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject* g // vm.deleteAllLinkedCode(JSC::DeleteAllCodeEffort::DeleteAllCodeIfNotCollecting); // JSC::Heap::PreventCollectionScope(vm.heap); - oldMap->clear(vm); + oldMap->clear(globalObject); JSC::gcUnprotect(oldMap); // vm.heap.completeAllJITPlans(); @@ -897,7 +958,7 @@ extern "C" bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject* g String GlobalObject::defaultAgentClusterID() { - return makeString(ProcessIdent::identifier().toUInt64(), "-default"_s); + return makeString(WebCore::Process::identifier().toUInt64(), "-default"_s); } String GlobalObject::agentClusterID() const @@ -915,7 +976,7 @@ using namespace WebCore; static JSGlobalObject* deriveShadowRealmGlobalObject(JSGlobalObject* globalObject) { auto& vm = globalObject->vm(); - Zig::GlobalObject* shadow = Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), JSC::jsNull())); + Zig::GlobalObject* shadow = Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm)); shadow->setConsole(shadow); return shadow; @@ -957,7 +1018,9 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { nullptr, // defaultLanguage nullptr, // compileStreaming nullptr, // instantiateStreaming - &Zig::deriveShadowRealmGlobalObject + &Zig::deriveShadowRealmGlobalObject, + nullptr, // codeForEval + nullptr, // canCompileStrings }; const JSC::GlobalObjectMethodTable EvalGlobalObject::s_globalObjectMethodTable = { @@ -980,18 +1043,20 @@ const JSC::GlobalObjectMethodTable EvalGlobalObject::s_globalObjectMethodTable = nullptr, // defaultLanguage nullptr, // compileStreaming nullptr, // instantiateStreaming - &Zig::deriveShadowRealmGlobalObject + &Zig::deriveShadowRealmGlobalObject, + nullptr, // codeForEval + nullptr, // canCompileStrings }; GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable) - : JSC::JSGlobalObject(vm, structure, methodTable) + : Base(vm, structure, methodTable) , m_bunVM(Bun__getVM()) , m_constructors(makeUnique()) , m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal)) , m_worldIsNormal(true) , m_builtinInternalFunctions(vm) , m_scriptExecutionContext(new WebCore::ScriptExecutionContext(&vm, this)) - , globalEventScope(*new Bun::GlobalScope(m_scriptExecutionContext)) + , globalEventScope(*new Bun::WorkerGlobalScope(m_scriptExecutionContext)) { // m_scriptExecutionContext = globalEventScope.m_context; mockModule = Bun::JSMockModule::create(this); @@ -1001,14 +1066,14 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, const JSC::Gl } GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::ScriptExecutionContextIdentifier contextId, const JSC::GlobalObjectMethodTable* methodTable) - : JSC::JSGlobalObject(vm, structure, methodTable) + : Base(vm, structure, methodTable) , m_bunVM(Bun__getVM()) , m_constructors(makeUnique()) , m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal)) , m_worldIsNormal(true) , m_builtinInternalFunctions(vm) , m_scriptExecutionContext(new WebCore::ScriptExecutionContext(&vm, this, contextId)) - , globalEventScope(*new Bun::GlobalScope(m_scriptExecutionContext)) + , globalEventScope(*new Bun::WorkerGlobalScope(m_scriptExecutionContext)) { // m_scriptExecutionContext = globalEventScope.m_context; mockModule = Bun::JSMockModule::create(this); @@ -1203,6 +1268,8 @@ WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableStreamDefaultController) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableStreamDefaultReader) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(SubtleCrypto); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoder); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoderStream); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextDecoderStream); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStream) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStreamDefaultController) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(URLSearchParams); @@ -1521,6 +1588,10 @@ JSC_DEFINE_HOST_FUNCTION(functionBTOA, LChar* ptr; unsigned length = encodedString.length(); auto dest = WTF::String::createUninitialized(length, ptr); + if (UNLIKELY(dest.isNull())) { + throwOutOfMemoryError(globalObject, throwScope); + return JSC::JSValue::encode(JSC::JSValue {}); + } WTF::StringImpl::copyCharacters(ptr, encodedString.span16()); encodedString = WTFMove(dest); } @@ -1585,7 +1656,7 @@ extern "C" JSC__JSValue ArrayBuffer__fromSharedMemfd(int64_t fd, JSC::JSGlobalOb return JSC::JSValue::encode(JSC::JSValue {}); } - auto buffer = ArrayBuffer::createFromBytes(reinterpret_cast(ptr) + byteOffset, byteLength, createSharedTask([ptr, totalLength](void* p) { + auto buffer = ArrayBuffer::createFromBytes({ reinterpret_cast(reinterpret_cast(ptr) + byteOffset), byteLength }, createSharedTask([ptr, totalLength](void* p) { munmap(ptr, totalLength); })); @@ -1627,12 +1698,30 @@ extern "C" JSC__JSValue Bun__createArrayBufferForCopy(JSC::JSGlobalObject* globa RELEASE_AND_RETURN(scope, JSValue::encode(JSC::JSArrayBuffer::create(globalObject->vm(), globalObject->arrayBufferStructure(JSC::ArrayBufferSharingMode::Default), WTFMove(arrayBuffer)))); } -extern "C" JSC__JSValue Bun__createUint8ArrayForCopy(JSC::JSGlobalObject* globalObject, const void* ptr, size_t len, bool isBuffer) +extern "C" JSC__JSValue Bun__allocUint8ArrayForCopy(JSC::JSGlobalObject* globalObject, size_t len, void** ptr) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + + JSC::JSUint8Array* array = JSC::JSUint8Array::createUninitialized(globalObject, globalObject->m_typedArrayUint8.get(globalObject), len); + + if (UNLIKELY(!array)) { + JSC::throwOutOfMemoryError(globalObject, scope); + return encodedJSValue(); + } + + *ptr = array->vector(); + + return JSValue::encode(array); +} + +extern "C" JSC__JSValue Bun__createUint8ArrayForCopy(JSC::JSGlobalObject* globalObject, const void* ptr, size_t len, bool isBuffer) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSC::JSUint8Array* array = JSC::JSUint8Array::createUninitialized( globalObject, - isBuffer ? reinterpret_cast(globalObject)->JSBufferSubclassStructure() : globalObject->m_typedArrayUint8.get(globalObject), + isBuffer ? reinterpret_cast(globalObject)->JSBufferSubclassStructure() : globalObject->typedArrayStructure(TypeUint8, false), len); if (UNLIKELY(!array)) { @@ -1699,7 +1788,7 @@ static inline JSC::EncodedJSValue jsFunctionAddEventListenerBody(JSC::JSGlobalOb auto type = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventTarget", "addEventListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener"_s, "EventTarget"_s, "addEventListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->argument(2); auto options = argument2.value().isUndefined() ? false : convert, IDLBoolean>>(*lexicalGlobalObject, argument2.value()); @@ -1728,7 +1817,7 @@ static inline JSC::EncodedJSValue jsFunctionRemoveEventListenerBody(JSC::JSGloba auto type = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventTarget", "removeEventListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener"_s, "EventTarget"_s, "removeEventListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->argument(2); auto options = argument2.value().isUndefined() ? false : convert, IDLBoolean>>(*lexicalGlobalObject, argument2.value()); @@ -1754,7 +1843,7 @@ static inline JSC::EncodedJSValue jsFunctionDispatchEventBody(JSC::JSGlobalObjec if (UNLIKELY(callFrame->argumentCount() < 1)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto event = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "event", "EventTarget", "dispatchEvent", "Event"); }); + auto event = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "event"_s, "EventTarget"_s, "dispatchEvent"_s, "Event"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(WebCore::toJS(*lexicalGlobalObject, throwScope, impl.dispatchEventForBindings(*event)))); } @@ -1902,8 +1991,53 @@ JSC_DEFINE_HOST_FUNCTION(isAbortSignal, (JSGlobalObject*, CallFrame* callFrame)) ASSERT(callFrame->argumentCount() == 1); return JSValue::encode(jsBoolean(callFrame->uncheckedArgument(0).inherits())); } +static inline std::optional invokeReadableStreamFunction(JSC::JSGlobalObject& lexicalGlobalObject, const JSC::Identifier& identifier, JSC::JSValue thisValue, const JSC::MarkedArgumentBuffer& arguments) +{ + JSC::VM& vm = lexicalGlobalObject.vm(); + JSC::JSLockHolder lock(vm); + + auto function = lexicalGlobalObject.get(&lexicalGlobalObject, identifier); + ASSERT(function.isCallable()); + + auto scope = DECLARE_CATCH_SCOPE(vm); + auto callData = JSC::getCallData(function); + auto result = call(&lexicalGlobalObject, function, callData, thisValue, arguments); +#if BUN_DEBUG + if (scope.exception()) { + Bun__reportError(&lexicalGlobalObject, JSValue::encode(scope.exception())); + } +#endif + EXCEPTION_ASSERT(!scope.exception() || vm.hasPendingTerminationException()); + if (scope.exception()) + return {}; + return result; +} +extern "C" bool ReadableStream__tee(JSC__JSValue possibleReadableStream, Zig::GlobalObject* globalObject, JSC__JSValue* possibleReadableStream1, JSC__JSValue* possibleReadableStream2) +{ + auto* readableStream = jsDynamicCast(JSC::JSValue::decode(possibleReadableStream)); + if (UNLIKELY(!readableStream)) + return false; + + auto& lexicalGlobalObject = *globalObject; + auto* clientData = static_cast(lexicalGlobalObject.vm().clientData); + auto& privateName = clientData->builtinFunctions().readableStreamInternalsBuiltins().readableStreamTeePrivateName(); + + MarkedArgumentBuffer arguments; + arguments.append(readableStream); + arguments.append(JSC::jsBoolean(true)); + ASSERT(!arguments.hasOverflowed()); + auto returnedValue = invokeReadableStreamFunction(lexicalGlobalObject, privateName, JSC::jsUndefined(), arguments); + if (!returnedValue) + return false; + + auto results = Detail::SequenceConverter::convert(lexicalGlobalObject, *returnedValue); + + ASSERT(results.size() == 2); + *possibleReadableStream1 = JSValue::encode(results[0]); + *possibleReadableStream2 = JSValue::encode(results[1]); + return true; +} -extern "C" void ReadableStream__cancel(JSC__JSValue possibleReadableStream, Zig::GlobalObject* globalObject); extern "C" void ReadableStream__cancel(JSC__JSValue possibleReadableStream, Zig::GlobalObject* globalObject) { auto* readableStream = jsDynamicCast(JSC::JSValue::decode(possibleReadableStream)); @@ -2069,7 +2203,7 @@ static inline JSC__JSValue ZigGlobalObject__readableStreamToArrayBufferBody(Zig: auto* function = globalObject->m_readableStreamToArrayBuffer.get(); if (!function) { - function = JSFunction::create(vm, static_cast(readableStreamReadableStreamToArrayBufferCodeGenerator(vm)), globalObject); + function = JSFunction::create(vm, globalObject, static_cast(readableStreamReadableStreamToArrayBufferCodeGenerator(vm)), globalObject); globalObject->m_readableStreamToArrayBuffer.set(vm, globalObject, function); } @@ -2115,7 +2249,7 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToBytes(Zig::GlobalObject auto* function = globalObject->m_readableStreamToBytes.get(); if (!function) { - function = JSFunction::create(vm, static_cast(readableStreamReadableStreamToBytesCodeGenerator(vm)), globalObject); + function = JSFunction::create(vm, globalObject, static_cast(readableStreamReadableStreamToBytesCodeGenerator(vm)), globalObject); globalObject->m_readableStreamToBytes.set(vm, globalObject, function); } @@ -2155,7 +2289,7 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToText(Zig::GlobalObject* if (auto readableStreamToText = globalObject->m_readableStreamToText.get()) { function = readableStreamToText; } else { - function = JSFunction::create(vm, static_cast(readableStreamReadableStreamToTextCodeGenerator(vm)), globalObject); + function = JSFunction::create(vm, globalObject, static_cast(readableStreamReadableStreamToTextCodeGenerator(vm)), globalObject); globalObject->m_readableStreamToText.set(vm, globalObject, function); } @@ -2175,7 +2309,7 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToFormData(Zig::GlobalObj if (auto readableStreamToFormData = globalObject->m_readableStreamToFormData.get()) { function = readableStreamToFormData; } else { - function = JSFunction::create(vm, static_cast(readableStreamReadableStreamToFormDataCodeGenerator(vm)), globalObject); + function = JSFunction::create(vm, globalObject, static_cast(readableStreamReadableStreamToFormDataCodeGenerator(vm)), globalObject); globalObject->m_readableStreamToFormData.set(vm, globalObject, function); } @@ -2197,7 +2331,7 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToJSON(Zig::GlobalObject* if (auto readableStreamToJSON = globalObject->m_readableStreamToJSON.get()) { function = readableStreamToJSON; } else { - function = JSFunction::create(vm, static_cast(readableStreamReadableStreamToJSONCodeGenerator(vm)), globalObject); + function = JSFunction::create(vm, globalObject, static_cast(readableStreamReadableStreamToJSONCodeGenerator(vm)), globalObject); globalObject->m_readableStreamToJSON.set(vm, globalObject, function); } @@ -2218,7 +2352,7 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToBlob(Zig::GlobalObject* if (auto readableStreamToBlob = globalObject->m_readableStreamToBlob.get()) { function = readableStreamToBlob; } else { - function = JSFunction::create(vm, static_cast(readableStreamReadableStreamToBlobCodeGenerator(vm)), globalObject); + function = JSFunction::create(vm, globalObject, static_cast(readableStreamReadableStreamToBlobCodeGenerator(vm)), globalObject); globalObject->m_readableStreamToBlob.set(vm, globalObject, function); } @@ -2431,12 +2565,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionDefaultErrorPrepareStackTrace, (JSGlobalObjec { auto& vm = lexicalGlobalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - Zig::GlobalObject* globalObject = jsDynamicCast(lexicalGlobalObject); - - if (!globalObject) { - // node:vm will use a different JSGlobalObject - globalObject = Bun__getDefaultGlobal(); - } + auto* globalObject = defaultGlobalObject(lexicalGlobalObject); auto errorObject = jsDynamicCast(callFrame->argument(0)); auto callSites = jsDynamicCast(callFrame->argument(1)); @@ -2493,7 +2622,7 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb for (int i = 0; i < framesCount; i++) { memset(remappedFrames + i, 0, sizeof(ZigStackFrame)); - remappedFrames[i].source_url = Bun::toString(lexicalGlobalObject, stackTrace.at(i).sourceURL()); + remappedFrames[i].source_url = Bun::toStringRef(lexicalGlobalObject, stackTrace.at(i).sourceURL()); if (JSCStackFrame::SourcePositions* sourcePositions = stackTrace.at(i).getSourcePositions()) { remappedFrames[i].position.line_zero_based = sourcePositions->line.zeroBasedInt(); remappedFrames[i].position.column_zero_based = sourcePositions->column.zeroBasedInt(); @@ -2557,7 +2686,7 @@ void GlobalObject::finishCreation(VM& vm) JSC::VM& vm = init.vm; JSC::JSGlobalObject* globalObject = init.owner; - auto* function = JSFunction::create(vm, static_cast(importMetaObjectCreateRequireCacheCodeGenerator(vm)), globalObject); + auto* function = JSFunction::create(vm, globalObject, static_cast(importMetaObjectCreateRequireCacheCodeGenerator(vm)), globalObject); NakedPtr returnedException = nullptr; auto result = JSC::call(globalObject, function, JSC::getCallData(function), globalObject, ArgList(), returnedException); @@ -2596,6 +2725,15 @@ void GlobalObject::finishCreation(VM& vm) init.set(WebCore::createJSSQLStatementStructure(init.owner)); }); + m_V8GlobalInternals.initLater( + [](const JSC::LazyProperty::Initializer& init) { + init.set( + v8::GlobalInternals::create( + init.vm, + v8::GlobalInternals::createStructure(init.vm, init.owner), + jsDynamicCast(init.owner))); + }); + m_memoryFootprintStructure.initLater( [](const JSC::LazyProperty::Initializer& init) { init.set( @@ -2654,13 +2792,22 @@ void GlobalObject::finishCreation(VM& vm) init.set(Bun::createUtilInspectOptionsStructure(init.vm, init.owner)); }); + m_nodeErrorCache.initLater( + [](const Initializer& init) { + auto* structure = ErrorCodeCache::createStructure( + init.vm, + init.owner); + + init.set(ErrorCodeCache::create(init.vm, structure)); + }); + m_utilInspectStylizeColorFunction.initLater( [](const Initializer& init) { JSC::MarkedArgumentBuffer args; args.append(jsCast(init.owner)->utilInspectFunction()); auto scope = DECLARE_THROW_SCOPE(init.vm); - JSC::JSFunction* getStylize = JSC::JSFunction::create(init.vm, utilInspectGetStylizeWithColorCodeGenerator(init.vm), init.owner); + JSC::JSFunction* getStylize = JSC::JSFunction::create(init.vm, init.owner, utilInspectGetStylizeWithColorCodeGenerator(init.vm), init.owner); // RETURN_IF_EXCEPTION(scope, {}); JSC::CallData callData = JSC::getCallData(getStylize); @@ -2678,7 +2825,7 @@ void GlobalObject::finishCreation(VM& vm) m_utilInspectStylizeNoColorFunction.initLater( [](const Initializer& init) { - init.set(JSC::JSFunction::create(init.vm, utilInspectStylizeWithNoColorCodeGenerator(init.vm), init.owner)); + init.set(JSC::JSFunction::create(init.vm, init.owner, utilInspectStylizeWithNoColorCodeGenerator(init.vm), init.owner)); }); m_nativeMicrotaskTrampoline.initLater( @@ -2754,10 +2901,13 @@ void GlobalObject::finishCreation(VM& vm) Bun::NapiPrototype::createStructure(init.vm, init.owner, init.owner->objectPrototype())); }); - m_cachedGlobalObjectStructure.initLater( + m_NapiHandleScopeImplStructure.initLater([](const JSC::LazyProperty::Initializer& init) { + init.set(Bun::NapiHandleScopeImpl::createStructure(init.vm, init.owner)); + }); + + m_cachedNodeVMGlobalObjectStructure.initLater( [](const JSC::LazyProperty::Initializer& init) { - init.set( - JSC::JSGlobalObject::createStructure(init.vm, JSC::jsNull())); + init.set(WebCore::createNodeVMGlobalObjectStructure(init.vm)); }); m_cachedGlobalProxyStructure.initLater( @@ -2856,25 +3006,12 @@ void GlobalObject::finishCreation(VM& vm) init.set(registry); }); - m_encodeIntoObjectStructure.initLater( - [](const JSC::LazyProperty::Initializer& init) { - auto& vm = init.vm; - auto& globalObject = *init.owner; - Structure* structure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), 2); - PropertyOffset offset; - auto clientData = WebCore::clientData(vm); - structure = Structure::addPropertyTransition(vm, structure, clientData->builtinNames().readPublicName(), 0, offset); - RELEASE_ASSERT(offset == 0); - structure = Structure::addPropertyTransition(vm, structure, clientData->builtinNames().writtenPublicName(), 0, offset); - RELEASE_ASSERT(offset == 1); - init.set(structure); - }); - m_requireFunctionUnbound.initLater( [](const JSC::LazyProperty::Initializer& init) { init.set( JSFunction::create( init.vm, + init.owner, moduleRequireCodeGenerator(init.vm), init.owner->globalScope(), JSFunction::createStructure(init.vm, init.owner, RequireFunctionPrototype::create(init.owner)))); @@ -2885,6 +3022,7 @@ void GlobalObject::finishCreation(VM& vm) init.set( JSFunction::create( init.vm, + init.owner, moduleRequireResolveCodeGenerator(init.vm), init.owner->globalScope(), JSFunction::createStructure(init.vm, init.owner, RequireResolveFunctionPrototype::create(init.owner)))); @@ -3115,7 +3253,7 @@ JSValue getEventSourceConstructor(VM& vm, JSObject* thisObject) auto globalObject = jsCast(thisObject); auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSFunction* getSourceEvent = JSC::JSFunction::create(vm, eventSourceGetEventSourceCodeGenerator(vm), globalObject); + JSC::JSFunction* getSourceEvent = JSC::JSFunction::create(vm, globalObject, eventSourceGetEventSourceCodeGenerator(vm), globalObject); RETURN_IF_EXCEPTION(scope, {}); JSC::MarkedArgumentBuffer args; @@ -3139,7 +3277,7 @@ JSC_DEFINE_CUSTOM_GETTER(getConsoleConstructor, (JSGlobalObject * globalObject, { auto& vm = globalObject->vm(); auto console = JSValue::decode(thisValue).getObject(); - JSC::JSFunction* createConsoleConstructor = JSC::JSFunction::create(vm, consoleObjectCreateConsoleConstructorCodeGenerator(vm), globalObject); + JSC::JSFunction* createConsoleConstructor = JSC::JSFunction::create(vm, globalObject, consoleObjectCreateConsoleConstructorCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; args.append(console); JSC::CallData callData = JSC::getCallData(createConsoleConstructor); @@ -3203,7 +3341,7 @@ EncodedJSValue GlobalObject::assignToStream(JSValue stream, JSValue controller) JSC::VM& vm = this->vm(); JSC::JSFunction* function = this->m_assignToStream.get(); if (!function) { - function = JSFunction::create(vm, static_cast(readableStreamInternalsAssignToStreamCodeGenerator(vm)), this); + function = JSFunction::create(vm, this, static_cast(readableStreamInternalsAssignToStreamCodeGenerator(vm)), this); this->m_assignToStream.set(vm, this, function); } @@ -3245,9 +3383,9 @@ JSC::GCClient::IsoSubspace* GlobalObject::subspaceForImpl(JSC::VM& vm) [](auto& server) -> JSC::HeapCellType& { return server.m_heapCellTypeForJSWorkerGlobalScope; }); } -extern "C" JSC::EncodedJSValue WebCore__alert(JSC::JSGlobalObject*, JSC::CallFrame*); -extern "C" JSC::EncodedJSValue WebCore__prompt(JSC::JSGlobalObject*, JSC::CallFrame*); -extern "C" JSC::EncodedJSValue WebCore__confirm(JSC::JSGlobalObject*, JSC::CallFrame*); +BUN_DECLARE_HOST_FUNCTION(WebCore__alert); +BUN_DECLARE_HOST_FUNCTION(WebCore__prompt); +BUN_DECLARE_HOST_FUNCTION(WebCore__confirm); JSValue GlobalObject_getPerformanceObject(VM& vm, JSObject* globalObject) { @@ -3297,6 +3435,8 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) GlobalPropertyInfo(builtinNames.internalModuleRegistryPrivateName(), this->internalModuleRegistry(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.processBindingConstantsPrivateName(), this->processBindingConstants(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.requireMapPrivateName(), this->requireMap(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0), + GlobalPropertyInfo(builtinNames.TextEncoderStreamEncoderPrivateName(), JSTextEncoderStreamEncoderConstructor(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0), + GlobalPropertyInfo(builtinNames.makeErrorWithCodePrivateName(), JSFunction::create(vm, this, 2, String(), jsFunctionMakeErrorWithCode, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), }; addStaticGlobals(staticGlobals, std::size(staticGlobals)); @@ -3444,14 +3584,18 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_nextTickQueue); visitor.append(thisObject->m_errorConstructorPrepareStackTraceValue); + visitor.append(thisObject->m_pendingNapiModuleAndExports[0]); + visitor.append(thisObject->m_pendingNapiModuleAndExports[1]); + + visitor.append(thisObject->m_currentNapiHandleScopeImpl); + thisObject->m_asyncBoundFunctionStructure.visit(visitor); thisObject->m_bunObject.visit(visitor); - thisObject->m_cachedGlobalObjectStructure.visit(visitor); + thisObject->m_cachedNodeVMGlobalObjectStructure.visit(visitor); thisObject->m_cachedGlobalProxyStructure.visit(visitor); thisObject->m_callSiteStructure.visit(visitor); thisObject->m_commonJSModuleObjectStructure.visit(visitor); thisObject->m_cryptoObject.visit(visitor); - thisObject->m_encodeIntoObjectStructure.visit(visitor); thisObject->m_errorConstructorPrepareStackTraceInternalValue.visit(visitor); thisObject->m_esmRegistryMap.visit(visitor); thisObject->m_importMetaObjectStructure.visit(visitor); @@ -3472,6 +3616,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_JSHTTPSResponseSinkClassStructure.visit(visitor); thisObject->m_JSSocketAddressStructure.visit(visitor); thisObject->m_JSSQLStatementStructure.visit(visitor); + thisObject->m_V8GlobalInternals.visit(visitor); thisObject->m_JSStringDecoderClassStructure.visit(visitor); thisObject->m_lazyPreloadTestModuleObject.visit(visitor); thisObject->m_lazyReadableStreamPrototypeMap.visit(visitor); @@ -3482,6 +3627,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_NapiExternalStructure.visit(visitor); thisObject->m_NAPIFunctionStructure.visit(visitor); thisObject->m_NapiPrototypeStructure.visit(visitor); + thisObject->m_NapiHandleScopeImplStructure.visit(visitor); thisObject->m_nativeMicrotaskTrampoline.visit(visitor); thisObject->m_navigatorObject.visit(visitor); thisObject->m_NodeVMScriptClassStructure.visit(visitor); @@ -3510,6 +3656,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->mockModule.mockWithImplementationCleanupDataStructure.visit(visitor); thisObject->mockModule.withImplementationCleanupFunction.visit(visitor); + thisObject->m_nodeErrorCache.visit(visitor); + for (auto& barrier : thisObject->m_thenables) { visitor.append(barrier); } @@ -3653,12 +3801,13 @@ template void GlobalObject::visitOutputConstraints(JSCell*, SlotVisitor&); void GlobalObject::reload() { JSModuleLoader* moduleLoader = this->moduleLoader(); + auto& vm = this->vm(); JSC::JSMap* registry = jsCast(moduleLoader->get( this, - Identifier::fromString(this->vm(), "registry"_s))); + Identifier::fromString(vm, "registry"_s))); - registry->clear(this->vm()); - this->requireMap()->clear(this->vm()); + registry->clear(this); + this->requireMap()->clear(this); // If we run the GC every time, we will never get the SourceProvider cache hit. // So we run the GC every other time. @@ -3693,7 +3842,7 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* jsGlobalObject BunString keyZ; if (key.isString()) { auto moduleName = jsCast(key)->value(globalObject); - if (moduleName.startsWith("file://"_s)) { + if (moduleName->startsWith("file://"_s)) { auto url = WTF::URL(moduleName); if (url.isValid() && !url.isEmpty()) { keyZ = Bun::toStringRef(url.fileSystemPath()); @@ -3745,9 +3894,6 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - auto* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); - RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); - if (globalObject->onLoadPlugins.hasVirtualModules()) { auto keyString = moduleNameValue->value(globalObject); if (auto resolution = globalObject->onLoadPlugins.resolveVirtualModule(keyString, sourceOrigin.url().protocolIsFile() ? sourceOrigin.url().fileSystemPath() : String())) { @@ -3755,8 +3901,10 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j auto result = JSC::importModule(globalObject, resolvedIdentifier, JSC::jsUndefined(), parameters, JSC::jsUndefined()); - - RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); + if (scope.exception()) { + auto* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); + return promise->rejectWithCaughtException(globalObject, scope); + } return result; } } @@ -3765,7 +3913,7 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j ErrorableString resolved; BunString moduleNameZ; - auto moduleName = moduleNameValue->value(globalObject); + String moduleName = moduleNameValue->value(globalObject); #if BUN_DEBUG auto startRefCount = moduleName.impl()->refCount(); #endif @@ -3794,7 +3942,7 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j ASSERT(startRefCount == moduleName.impl()->refCount()); if (!resolved.success) { throwException(scope, resolved.result.err, globalObject); - return promise->rejectWithCaughtException(globalObject, scope); + return JSC::JSInternalPromise::rejectedPromiseWithCaughtException(globalObject, scope); } JSC::Identifier resolvedIdentifier; @@ -3823,8 +3971,11 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j auto result = JSC::importModule(globalObject, resolvedIdentifier, JSC::jsUndefined(), parameters, jsUndefined()); - RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); + if (scope.exception()) { + return JSC::JSInternalPromise::rejectedPromiseWithCaughtException(globalObject, scope); + } + ASSERT(result); return result; } @@ -3945,7 +4096,7 @@ JSC::JSValue EvalGlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGloba return result; } -GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)) +GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction handler) { if (handler == Bun__HTTPRequestContext__onReject) { return GlobalObject::PromiseFunctions::Bun__HTTPRequestContext__onReject; diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 242b1917f00c6..aa94f5abdecaf 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -21,7 +21,7 @@ class ScriptExecutionContext; class DOMGuardedObject; class EventLoopTask; class DOMWrapperWorld; -class GlobalScope; +class WorkerGlobalScope; class SubtleCrypto; class EventTarget; class Performance; @@ -29,8 +29,13 @@ class Performance; namespace Bun { class InternalModuleRegistry; +class NapiHandleScopeImpl; } // namespace Bun +namespace v8 { +class GlobalInternals; +} // namespace v8 + #include "root.h" #include "headers-handwritten.h" #include @@ -44,9 +49,10 @@ class InternalModuleRegistry; #include "WebCoreJSBuiltins.h" #include "headers-handwritten.h" #include "BunCommonStrings.h" +#include "BunGlobalScope.h" namespace WebCore { -class GlobalScope; +class WorkerGlobalScope; class SubtleCrypto; class EventTarget; } @@ -68,10 +74,13 @@ using DOMGuardedObjectSet = HashSet; #define ZIG_GLOBAL_OBJECT_DEFINED -class GlobalObject : public JSC::JSGlobalObject { - using Base = JSC::JSGlobalObject; +class GlobalObject : public Bun::GlobalScope { + using Base = Bun::GlobalScope; public: + // Move this to the front for better cache locality. + void* m_bunVM; + static const JSC::ClassInfo s_info; static const JSC::GlobalObjectMethodTable s_globalObjectMethodTable; @@ -88,9 +97,9 @@ class GlobalObject : public JSC::JSGlobalObject { static constexpr const JSC::ClassInfo* info() { return &s_info; } - static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* global, JSC::JSValue prototype) + static JSC::Structure* createStructure(JSC::VM& vm) { - return JSC::Structure::create(vm, global, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags & ~IsImmutablePrototypeExoticObject), info()); + return JSC::Structure::create(vm, nullptr, jsNull(), JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags & ~IsImmutablePrototypeExoticObject), info()); } // Make binding code generation easier. @@ -240,7 +249,6 @@ class GlobalObject : public JSC::JSGlobalObject { JSC::JSMap* readableStreamNativeMap() const { return m_lazyReadableStreamPrototypeMap.getInitializedOnMainThread(this); } JSC::JSMap* requireMap() const { return m_requireMap.getInitializedOnMainThread(this); } JSC::JSMap* esmRegistryMap() const { return m_esmRegistryMap.getInitializedOnMainThread(this); } - JSC::Structure* encodeIntoObjectStructure() const { return m_encodeIntoObjectStructure.getInitializedOnMainThread(this); } JSC::Structure* callSiteStructure() const { return m_callSiteStructure.getInitializedOnMainThread(this); } @@ -262,7 +270,7 @@ class GlobalObject : public JSC::JSGlobalObject { JSObject* lazyRequireCacheObject() const { return m_lazyRequireCacheObject.getInitializedOnMainThread(this); } - Structure* globalObjectStructure() const { return m_cachedGlobalObjectStructure.getInitializedOnMainThread(this); } + Structure* NodeVMGlobalObjectStructure() const { return m_cachedNodeVMGlobalObjectStructure.getInitializedOnMainThread(this); } Structure* globalProxyStructure() const { return m_cachedGlobalProxyStructure.getInitializedOnMainThread(this); } JSObject* lazyTestModuleObject() const { return m_lazyTestModuleObject.getInitializedOnMainThread(this); } JSObject* lazyPreloadTestModuleObject() const { return m_lazyPreloadTestModuleObject.getInitializedOnMainThread(this); } @@ -277,9 +285,12 @@ class GlobalObject : public JSC::JSGlobalObject { Structure* NapiExternalStructure() const { return m_NapiExternalStructure.getInitializedOnMainThread(this); } Structure* NapiPrototypeStructure() const { return m_NapiPrototypeStructure.getInitializedOnMainThread(this); } Structure* NAPIFunctionStructure() const { return m_NAPIFunctionStructure.getInitializedOnMainThread(this); } + Structure* NapiHandleScopeImplStructure() const { return m_NapiHandleScopeImplStructure.getInitializedOnMainThread(this); } Structure* JSSQLStatementStructure() const { return m_JSSQLStatementStructure.getInitializedOnMainThread(this); } + v8::GlobalInternals* V8GlobalInternals() const { return m_V8GlobalInternals.getInitializedOnMainThread(this); } + bool hasProcessObject() const { return m_processObject.isInitialized(); } RefPtr performance(); @@ -312,7 +323,7 @@ class GlobalObject : public JSC::JSGlobalObject { WebCore::EventTarget& eventTarget(); WebCore::ScriptExecutionContext* m_scriptExecutionContext; - Bun::GlobalScope& globalEventScope; + Bun::WorkerGlobalScope& globalEventScope; void resetOnEachMicrotaskTick(); @@ -344,9 +355,9 @@ class GlobalObject : public JSC::JSGlobalObject { }; static constexpr size_t promiseFunctionsSize = 24; - static PromiseFunctions promiseHandlerID(EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)); + static PromiseFunctions promiseHandlerID(SYSV_ABI EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)); - JSFunction* thenable(EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)) + JSFunction* thenable(SYSV_ABI EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)) { auto& barrier = this->m_thenables[static_cast(GlobalObject::promiseHandlerID(handler))]; if (JSFunction* func = barrier.get()) { @@ -394,14 +405,25 @@ class GlobalObject : public JSC::JSGlobalObject { // Error.prepareStackTrace mutable WriteBarrier m_errorConstructorPrepareStackTraceValue; + // When a napi module initializes on dlopen, we need to know what the value is + mutable JSC::WriteBarrier m_pendingNapiModuleAndExports[2]; + + // The handle scope where all new NAPI values will be created. You must not pass any napi_values + // back to a NAPI function without putting them in the handle scope, as the NAPI function may + // move them off the stack which will cause them to get collected if not in the handle scope. + JSC::WriteBarrier m_currentNapiHandleScopeImpl; + // The original, unmodified Error.prepareStackTrace. // - // We set a default value for this to mimick Node.js behavior It is a + // We set a default value for this to mimic Node.js behavior It is a // separate from the user-facing value so that we can tell if the user // really set it or if it's just the default value. // LazyProperty m_errorConstructorPrepareStackTraceInternalValue; + LazyProperty m_nodeErrorCache; + JSObject* nodeErrorCache() const { return m_nodeErrorCache.getInitializedOnMainThread(this); } + Structure* memoryFootprintStructure() { return m_memoryFootprintStructure.getInitializedOnMainThread(this); @@ -442,9 +464,6 @@ class GlobalObject : public JSC::JSGlobalObject { JSC::Structure* pendingVirtualModuleResultStructure() { return m_pendingVirtualModuleResultStructure.get(this); } - // When a napi module initializes on dlopen, we need to know what the value is - // This value is not observed by GC. It should be extremely ephemeral. - JSValue pendingNapiModule = JSValue {}; // We need to know if the napi module registered itself or we registered it. // To do that, we count the number of times we register a module. int napiModuleRegisterCallCount = 0; @@ -466,10 +485,11 @@ class GlobalObject : public JSC::JSGlobalObject { #include "ZigGeneratedClasses+lazyStructureHeader.h" + void finishCreation(JSC::VM&); + private: void addBuiltinGlobals(JSC::VM&); - void finishCreation(JSC::VM&); friend void WebCore::JSBuiltinInternalFunctions::initialize(Zig::GlobalObject&); WebCore::JSBuiltinInternalFunctions m_builtinInternalFunctions; std::unique_ptr m_constructors; @@ -526,7 +546,6 @@ class GlobalObject : public JSC::JSGlobalObject { LazyProperty m_lazyReadableStreamPrototypeMap; LazyProperty m_requireMap; LazyProperty m_esmRegistryMap; - LazyProperty m_encodeIntoObjectStructure; LazyProperty m_JSArrayBufferControllerPrototype; LazyProperty m_JSHTTPSResponseControllerPrototype; LazyProperty m_JSFileSinkControllerPrototype; @@ -538,7 +557,7 @@ class GlobalObject : public JSC::JSGlobalObject { LazyProperty m_lazyTestModuleObject; LazyProperty m_lazyPreloadTestModuleObject; LazyProperty m_testMatcherUtilsObject; - LazyProperty m_cachedGlobalObjectStructure; + LazyProperty m_cachedNodeVMGlobalObjectStructure; LazyProperty m_cachedGlobalProxyStructure; LazyProperty m_commonJSModuleObjectStructure; LazyProperty m_JSSocketAddressStructure; @@ -554,7 +573,10 @@ class GlobalObject : public JSC::JSGlobalObject { LazyProperty m_NapiExternalStructure; LazyProperty m_NapiPrototypeStructure; LazyProperty m_NAPIFunctionStructure; + LazyProperty m_NapiHandleScopeImplStructure; + LazyProperty m_JSSQLStatementStructure; + LazyProperty m_V8GlobalInternals; LazyProperty m_bunObject; LazyProperty m_cryptoObject; @@ -564,8 +586,6 @@ class GlobalObject : public JSC::JSGlobalObject { private: DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock); - void* m_bunVM; - WebCore::SubtleCrypto* m_subtleCrypto = nullptr; WTF::Vector> m_aboutToBeNotifiedRejectedPromises; @@ -588,7 +608,22 @@ class EvalGlobalObject : public GlobalObject { // TODO: move this namespace Bun { -String formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const WTF::String& name, const WTF::String& message, OrdinalNumber& line, OrdinalNumber& column, WTF::String& sourceURL, Vector& stackTrace, JSC::JSObject* errorInstance); +String formatStackTrace(JSC::VM& vm, Zig::GlobalObject* globalObject, JSC::JSGlobalObject* lexicalGlobalObject, const WTF::String& name, const WTF::String& message, OrdinalNumber& line, OrdinalNumber& column, WTF::String& sourceURL, Vector& stackTrace, JSC::JSObject* errorInstance); + +ALWAYS_INLINE void* vm(Zig::GlobalObject* globalObject) +{ + return globalObject->bunVM(); +} + +ALWAYS_INLINE void* vm(JSC::VM& vm) +{ + return WebCore::clientData(vm)->bunVM; +} + +ALWAYS_INLINE void* vm(JSC::JSGlobalObject* lexicalGlobalObject) +{ + return WebCore::clientData(lexicalGlobalObject->vm())->bunVM; +} } @@ -599,4 +634,40 @@ using JSDOMGlobalObject = Zig::GlobalObject; } #endif +// Do not use this directly. +namespace ___private___ { +extern "C" Zig::GlobalObject* Bun__getDefaultGlobalObject(); +inline Zig::GlobalObject* getDefaultGlobalObject() +{ + return Bun__getDefaultGlobalObject(); +} +} + +inline Zig::GlobalObject* defaultGlobalObject(JSC::JSGlobalObject* lexicalGlobalObject) +{ + auto* globalObject = jsDynamicCast(lexicalGlobalObject); + if (!globalObject) { + return ___private___::getDefaultGlobalObject(); + } + return globalObject; +} +inline Zig::GlobalObject* defaultGlobalObject() +{ + return ___private___::getDefaultGlobalObject(); +} + +inline void* bunVM(JSC::JSGlobalObject* lexicalGlobalObject) +{ + if (auto* globalObject = jsDynamicCast(lexicalGlobalObject)) { + return globalObject->bunVM(); + } + + return WebCore::clientData(lexicalGlobalObject->vm())->bunVM; +} + +inline void* bunVM(Zig::GlobalObject* globalObject) +{ + return globalObject->bunVM(); +} + #endif diff --git a/src/bun.js/bindings/ZigGlobalObject.lut.txt b/src/bun.js/bindings/ZigGlobalObject.lut.txt index 389ceb03a59cc..106143b94aaf5 100644 --- a/src/bun.js/bindings/ZigGlobalObject.lut.txt +++ b/src/bun.js/bindings/ZigGlobalObject.lut.txt @@ -1,88 +1,90 @@ -// In a separate file because processing ZigGlobalObject.cpp takes 15+ seconds - -/* Source for ZigGlobalObject.lut.h -@begin bunGlobalObjectTable - addEventListener jsFunctionAddEventListener Function 2 - alert WebCore__alert Function 1 - atob functionATOB Function 1 - btoa functionBTOA Function 1 - clearImmediate functionClearTimeout Function 1 - clearInterval functionClearInterval Function 1 - clearTimeout functionClearTimeout Function 1 - confirm WebCore__confirm Function 1 - dispatchEvent jsFunctionDispatchEvent Function 1 - fetch Bun__fetch Function 2 - postMessage jsFunctionPostMessage Function 1 - prompt WebCore__prompt Function 1 - queueMicrotask functionQueueMicrotask Function 2 - removeEventListener jsFunctionRemoveEventListener Function 2 - reportError functionReportError Function 1 - setImmediate functionSetImmediate Function 1 - setInterval functionSetInterval Function 1 - setTimeout functionSetTimeout Function 1 - structuredClone functionStructuredClone Function 2 - - global GlobalObject_getGlobalThis PropertyCallback - EventSource getEventSourceConstructor PropertyCallback - - Bun GlobalObject::m_bunObject CellProperty|DontDelete|ReadOnly - File GlobalObject::m_JSDOMFileConstructor CellProperty - crypto GlobalObject::m_cryptoObject CellProperty - navigator GlobalObject::m_navigatorObject CellProperty - performance GlobalObject::m_performanceObject CellProperty - process GlobalObject::m_processObject CellProperty - - Blob GlobalObject::m_JSBlob ClassStructure - Buffer GlobalObject::m_JSBufferClassStructure ClassStructure - BuildError GlobalObject::m_JSBuildMessage ClassStructure - BuildMessage GlobalObject::m_JSBuildMessage ClassStructure - Crypto GlobalObject::m_JSCrypto ClassStructure - HTMLRewriter GlobalObject::m_JSHTMLRewriter ClassStructure - Request GlobalObject::m_JSRequest ClassStructure - ResolveError GlobalObject::m_JSResolveMessage ClassStructure - ResolveMessage GlobalObject::m_JSResolveMessage ClassStructure - Response GlobalObject::m_JSResponse ClassStructure - TextDecoder GlobalObject::m_JSTextDecoder ClassStructure - - AbortController AbortControllerConstructorCallback PropertyCallback - AbortSignal AbortSignalConstructorCallback PropertyCallback - BroadcastChannel BroadcastChannelConstructorCallback PropertyCallback - ByteLengthQueuingStrategy ByteLengthQueuingStrategyConstructorCallback PropertyCallback - CloseEvent CloseEventConstructorCallback PropertyCallback - CountQueuingStrategy CountQueuingStrategyConstructorCallback PropertyCallback - CryptoKey CryptoKeyConstructorCallback PropertyCallback - CustomEvent CustomEventConstructorCallback PropertyCallback - DOMException DOMExceptionConstructorCallback PropertyCallback - ErrorEvent ErrorEventConstructorCallback PropertyCallback - Event EventConstructorCallback PropertyCallback - EventTarget EventTargetConstructorCallback PropertyCallback - FormData DOMFormDataConstructorCallback PropertyCallback - Headers FetchHeadersConstructorCallback PropertyCallback - MessageChannel MessageChannelConstructorCallback PropertyCallback - MessageEvent MessageEventConstructorCallback PropertyCallback - MessagePort MessagePortConstructorCallback PropertyCallback - Performance PerformanceConstructorCallback PropertyCallback - PerformanceEntry PerformanceEntryConstructorCallback PropertyCallback - PerformanceMark PerformanceMarkConstructorCallback PropertyCallback - PerformanceMeasure PerformanceMeasureConstructorCallback PropertyCallback - PerformanceObserver PerformanceObserverConstructorCallback PropertyCallback - PerformanceObserverEntryList PerformanceObserverEntryListConstructorCallback PropertyCallback - ReadableByteStreamController ReadableByteStreamControllerConstructorCallback PropertyCallback - ReadableStream ReadableStreamConstructorCallback PropertyCallback - ReadableStreamBYOBReader ReadableStreamBYOBReaderConstructorCallback PropertyCallback - ReadableStreamBYOBRequest ReadableStreamBYOBRequestConstructorCallback PropertyCallback - ReadableStreamDefaultController ReadableStreamDefaultControllerConstructorCallback PropertyCallback - ReadableStreamDefaultReader ReadableStreamDefaultReaderConstructorCallback PropertyCallback - SubtleCrypto SubtleCryptoConstructorCallback PropertyCallback - TextEncoder TextEncoderConstructorCallback PropertyCallback - TransformStream TransformStreamConstructorCallback PropertyCallback - TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback - URL DOMURLConstructorCallback PropertyCallback - URLSearchParams URLSearchParamsConstructorCallback PropertyCallback - WebSocket WebSocketConstructorCallback PropertyCallback - Worker WorkerConstructorCallback PropertyCallback - WritableStream WritableStreamConstructorCallback PropertyCallback - WritableStreamDefaultController WritableStreamDefaultControllerConstructorCallback PropertyCallback - WritableStreamDefaultWriter WritableStreamDefaultWriterConstructorCallback PropertyCallback -@end -*/ +// In a separate file because processing ZigGlobalObject.cpp takes 15+ seconds + +/* Source for ZigGlobalObject.lut.h +@begin bunGlobalObjectTable + addEventListener jsFunctionAddEventListener Function 2 + alert WebCore__alert Function 1 + atob functionATOB Function 1 + btoa functionBTOA Function 1 + clearImmediate functionClearTimeout Function 1 + clearInterval functionClearInterval Function 1 + clearTimeout functionClearTimeout Function 1 + confirm WebCore__confirm Function 1 + dispatchEvent jsFunctionDispatchEvent Function 1 + fetch constructBunFetchObject PropertyCallback + postMessage jsFunctionPostMessage Function 1 + prompt WebCore__prompt Function 1 + queueMicrotask functionQueueMicrotask Function 2 + removeEventListener jsFunctionRemoveEventListener Function 2 + reportError functionReportError Function 1 + setImmediate functionSetImmediate Function 1 + setInterval functionSetInterval Function 1 + setTimeout functionSetTimeout Function 1 + structuredClone functionStructuredClone Function 2 + + global GlobalObject_getGlobalThis PropertyCallback + EventSource getEventSourceConstructor PropertyCallback + + Bun GlobalObject::m_bunObject CellProperty|DontDelete|ReadOnly + File GlobalObject::m_JSDOMFileConstructor CellProperty + crypto GlobalObject::m_cryptoObject CellProperty + navigator GlobalObject::m_navigatorObject CellProperty + performance GlobalObject::m_performanceObject CellProperty + process GlobalObject::m_processObject CellProperty + + Blob GlobalObject::m_JSBlob ClassStructure + Buffer GlobalObject::m_JSBufferClassStructure ClassStructure + BuildError GlobalObject::m_JSBuildMessage ClassStructure + BuildMessage GlobalObject::m_JSBuildMessage ClassStructure + Crypto GlobalObject::m_JSCrypto ClassStructure + HTMLRewriter GlobalObject::m_JSHTMLRewriter ClassStructure + Request GlobalObject::m_JSRequest ClassStructure + ResolveError GlobalObject::m_JSResolveMessage ClassStructure + ResolveMessage GlobalObject::m_JSResolveMessage ClassStructure + Response GlobalObject::m_JSResponse ClassStructure + TextDecoder GlobalObject::m_JSTextDecoder ClassStructure + + AbortController AbortControllerConstructorCallback PropertyCallback + AbortSignal AbortSignalConstructorCallback PropertyCallback + BroadcastChannel BroadcastChannelConstructorCallback PropertyCallback + ByteLengthQueuingStrategy ByteLengthQueuingStrategyConstructorCallback PropertyCallback + CloseEvent CloseEventConstructorCallback PropertyCallback + CountQueuingStrategy CountQueuingStrategyConstructorCallback PropertyCallback + CryptoKey CryptoKeyConstructorCallback PropertyCallback + CustomEvent CustomEventConstructorCallback PropertyCallback + DOMException DOMExceptionConstructorCallback PropertyCallback + ErrorEvent ErrorEventConstructorCallback PropertyCallback + Event EventConstructorCallback PropertyCallback + EventTarget EventTargetConstructorCallback PropertyCallback + FormData DOMFormDataConstructorCallback PropertyCallback + Headers FetchHeadersConstructorCallback PropertyCallback + MessageChannel MessageChannelConstructorCallback PropertyCallback + MessageEvent MessageEventConstructorCallback PropertyCallback + MessagePort MessagePortConstructorCallback PropertyCallback + Performance PerformanceConstructorCallback PropertyCallback + PerformanceEntry PerformanceEntryConstructorCallback PropertyCallback + PerformanceMark PerformanceMarkConstructorCallback PropertyCallback + PerformanceMeasure PerformanceMeasureConstructorCallback PropertyCallback + PerformanceObserver PerformanceObserverConstructorCallback PropertyCallback + PerformanceObserverEntryList PerformanceObserverEntryListConstructorCallback PropertyCallback + ReadableByteStreamController ReadableByteStreamControllerConstructorCallback PropertyCallback + ReadableStream ReadableStreamConstructorCallback PropertyCallback + ReadableStreamBYOBReader ReadableStreamBYOBReaderConstructorCallback PropertyCallback + ReadableStreamBYOBRequest ReadableStreamBYOBRequestConstructorCallback PropertyCallback + ReadableStreamDefaultController ReadableStreamDefaultControllerConstructorCallback PropertyCallback + ReadableStreamDefaultReader ReadableStreamDefaultReaderConstructorCallback PropertyCallback + SubtleCrypto SubtleCryptoConstructorCallback PropertyCallback + TextEncoder TextEncoderConstructorCallback PropertyCallback + TextEncoderStream TextEncoderStreamConstructorCallback PropertyCallback + TextDecoderStream TextDecoderStreamConstructorCallback PropertyCallback + TransformStream TransformStreamConstructorCallback PropertyCallback + TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback + URL DOMURLConstructorCallback PropertyCallback + URLSearchParams URLSearchParamsConstructorCallback PropertyCallback + WebSocket WebSocketConstructorCallback PropertyCallback + Worker WorkerConstructorCallback PropertyCallback + WritableStream WritableStreamConstructorCallback PropertyCallback + WritableStreamDefaultController WritableStreamDefaultControllerConstructorCallback PropertyCallback + WritableStreamDefaultWriter WritableStreamDefaultWriterConstructorCallback PropertyCallback +@end +*/ diff --git a/src/bun.js/bindings/ZigSourceProvider.cpp b/src/bun.js/bindings/ZigSourceProvider.cpp index 48ddef08ea6bb..3d1c137bbedb1 100644 --- a/src/bun.js/bindings/ZigSourceProvider.cpp +++ b/src/bun.js/bindings/ZigSourceProvider.cpp @@ -35,11 +35,11 @@ SourceOrigin toSourceOrigin(const String& sourceURL, bool isBuiltin) if (isBuiltin) { if (sourceURL.startsWith("node:"_s)) { - return SourceOrigin(WTF::URL(makeString("builtin://node/", sourceURL.substring(5)))); + return SourceOrigin(WTF::URL(makeString("builtin://node/"_s, sourceURL.substring(5)))); } else if (sourceURL.startsWith("bun:"_s)) { - return SourceOrigin(WTF::URL(makeString("builtin://bun/", sourceURL.substring(4)))); + return SourceOrigin(WTF::URL(makeString("builtin://bun/"_s, sourceURL.substring(4)))); } else { - return SourceOrigin(WTF::URL(makeString("builtin://", sourceURL))); + return SourceOrigin(WTF::URL(makeString("builtin://"_s, sourceURL))); } } @@ -73,8 +73,8 @@ Ref SourceProvider::create( Zig::GlobalObject* globalObject, ResolvedSource& resolvedSource, JSC::SourceProviderSourceType sourceType, - bool isBuiltin -) { + bool isBuiltin) +{ auto string = resolvedSource.source_code.toWTFString(BunString::ZeroCopy); auto sourceURLString = resolvedSource.source_url.toWTFString(BunString::ZeroCopy); @@ -111,8 +111,9 @@ Ref SourceProvider::create( return provider; } -SourceProvider::~SourceProvider() { - if(m_resolvedSource.already_bundled) { +SourceProvider::~SourceProvider() +{ + if (m_resolvedSource.already_bundled) { BunString str = Bun::toString(sourceURL()); Bun__removeSourceProviderSourceMap(m_globalObject->bunVM(), this, &str); } @@ -156,7 +157,6 @@ void SourceProvider::cacheBytecode(const BytecodeCacheGenerator& generator) m_cachedBytecode->addGlobalUpdate(*update); } - void SourceProvider::commitCachedBytecode() { // if (!m_resolvedSource.bytecodecache_fd || !m_cachedBytecode || !m_cachedBytecode->hasUpdates()) @@ -245,7 +245,8 @@ int SourceProvider::readCache(JSC::VM& vm, const JSC::SourceCode& sourceCode) // } } -extern "C" BunString ZigSourceProvider__getSourceSlice(SourceProvider* provider) { +extern "C" BunString ZigSourceProvider__getSourceSlice(SourceProvider* provider) +{ return Bun::toStringView(provider->source()); } diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 389a049535609..e8addc06b1b80 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -1,9 +1,19 @@ #include "root.h" +#include "JavaScriptCore/Exception.h" +#include "ErrorCode+List.h" +#include "ErrorCode.h" +#include "JavaScriptCore/ThrowScope.h" + +#include "JavaScriptCore/JSCast.h" +#include "JavaScriptCore/JSType.h" +#include "JavaScriptCore/NumberObject.h" #include "JavaScriptCore/JSCJSValue.h" #include "JavaScriptCore/JSGlobalObject.h" +#include "JavaScriptCore/JSPromiseConstructor.h" #include "JavaScriptCore/DeleteAllCodeEffort.h" - +#include "JavaScriptCore/BooleanObject.h" +#include "JSFFIFunction.h" #include "headers.h" #include "BunClientData.h" @@ -29,12 +39,14 @@ #include "JavaScriptCore/JSClassRef.h" #include "JavaScriptCore/JSInternalPromise.h" #include "JavaScriptCore/JSMap.h" +#include "JavaScriptCore/JSMapIterator.h" #include "JavaScriptCore/JSModuleLoader.h" #include "JavaScriptCore/JSModuleRecord.h" #include "JavaScriptCore/JSNativeStdFunction.h" #include "JavaScriptCore/JSONObject.h" #include "JavaScriptCore/JSObject.h" #include "JavaScriptCore/JSSet.h" +#include "JavaScriptCore/JSSetIterator.h" #include "JavaScriptCore/JSString.h" #include "JavaScriptCore/ProxyObject.h" #include "JavaScriptCore/Microtask.h" @@ -92,8 +104,6 @@ #include "JavaScriptCore/DateInstance.h" #include "JavaScriptCore/RegExpObject.h" #include "JavaScriptCore/PropertyNameArray.h" -#include "JavaScriptCore/HashMapImpl.h" -#include "JavaScriptCore/HashMapImplInlines.h" #include "webcore/JSAbortSignal.h" #include "JSAbortAlgorithm.h" @@ -114,6 +124,13 @@ #include "ErrorStackFrame.h" +#if OS(DARWIN) +#if BUN_DEBUG +#include +#define IS_MALLOC_DEBUGGING_ENABLED 1 +#endif +#endif + static WTF::StringView StringView_slice(WTF::StringView sv, unsigned start, unsigned end) { return sv.substring(start, end - start); @@ -195,7 +212,6 @@ static constexpr int FLAG_NOT = (1 << 2); #pragma clang diagnostic pop -extern "C" bool Expect_readFlagsAndProcessPromise(JSC__JSValue instanceValue, JSC__JSGlobalObject* globalObject, ExpectFlags* flags, JSC__JSValue* value); extern "C" bool ExpectCustomAsymmetricMatcher__execute(void* self, JSC__JSValue thisValue, JSC__JSGlobalObject* globalObject, JSC__JSValue leftValue); enum class AsymmetricMatcherResult : uint8_t { @@ -204,10 +220,91 @@ enum class AsymmetricMatcherResult : uint8_t { NOT_MATCHER, }; -bool readFlagsAndProcessPromise(JSValue& instanceValue, ExpectFlags& flags, JSGlobalObject* globalObject, JSValue& value) +enum class AsymmetricMatcherConstructorType : uint8_t { + none = 0, + Symbol = 1, + String = 2, + Object = 3, + Array = 4, + BigInt = 5, + Boolean = 6, + Number = 7, + Promise = 8, + InstanceOf = 9, +}; + +#if ASSERT_ENABLED +#define ASSERT_NO_PENDING_EXCEPTION(globalObject) DECLARE_THROW_SCOPE(globalObject->vm()).assertNoExceptionExceptTermination() +#else +#define ASSERT_NO_PENDING_EXCEPTION(globalObject) void() +#endif + +extern "C" bool Expect_readFlagsAndProcessPromise(JSC__JSValue instanceValue, JSC__JSGlobalObject* globalObject, ExpectFlags* flags, JSC__JSValue* value, AsymmetricMatcherConstructorType* constructorType); + +extern "C" uint8_t AsymmetricMatcherConstructorType__fromJS(JSC__JSGlobalObject* globalObject, JSC__JSValue encodedValue) +{ + JSValue value = JSValue::decode(encodedValue); + if (value.isObject()) { + JSObject* object = value.getObject(); + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (globalObject->numberObjectConstructor() == object) { + return static_cast(AsymmetricMatcherConstructorType::Number); + } + + if (globalObject->booleanObjectConstructor() == object) { + return static_cast(AsymmetricMatcherConstructorType::Boolean); + } + + auto stringConstructorValue = globalObject->stringPrototype()->getIfPropertyExists(globalObject, vm.propertyNames->constructor); + RETURN_IF_EXCEPTION(scope, static_cast(AsymmetricMatcherConstructorType::none)); + + if (stringConstructorValue == object) { + return static_cast(AsymmetricMatcherConstructorType::String); + } + + auto symbolConstructorValue = globalObject->symbolPrototype()->getIfPropertyExists(globalObject, vm.propertyNames->constructor); + RETURN_IF_EXCEPTION(scope, static_cast(AsymmetricMatcherConstructorType::none)); + + if (symbolConstructorValue == object) { + return static_cast(AsymmetricMatcherConstructorType::Symbol); + } + + auto bigIntConstructorValue = globalObject->bigIntPrototype()->getIfPropertyExists(globalObject, vm.propertyNames->constructor); + RETURN_IF_EXCEPTION(scope, static_cast(AsymmetricMatcherConstructorType::none)); + + if (bigIntConstructorValue == object) { + return static_cast(AsymmetricMatcherConstructorType::BigInt); + } + + JSObject* promiseConstructor = globalObject->promiseConstructor(); + + if (promiseConstructor == object) { + return static_cast(AsymmetricMatcherConstructorType::Promise); + } + + JSObject* array = globalObject->arrayConstructor(); + + if (array == object) { + return static_cast(AsymmetricMatcherConstructorType::Array); + } + + JSObject* obj = globalObject->objectConstructor(); + + if (obj == object) { + return static_cast(AsymmetricMatcherConstructorType::Object); + } + + return static_cast(AsymmetricMatcherConstructorType::InstanceOf); + } + + return static_cast(AsymmetricMatcherConstructorType::none); +} + +bool readFlagsAndProcessPromise(JSValue& instanceValue, ExpectFlags& flags, JSGlobalObject* globalObject, JSValue& value, AsymmetricMatcherConstructorType& constructorType) { JSC::EncodedJSValue valueEncoded = JSValue::encode(value); - if (Expect_readFlagsAndProcessPromise(JSValue::encode(instanceValue), globalObject, &flags, &valueEncoded)) { + if (Expect_readFlagsAndProcessPromise(JSValue::encode(instanceValue), globalObject, &flags, &valueEncoded, &constructorType)) { value = JSValue::decode(valueEncoded); return true; } @@ -216,11 +313,11 @@ bool readFlagsAndProcessPromise(JSValue& instanceValue, ExpectFlags& flags, JSGl AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* globalObject, JSValue matcherProp, JSValue otherProp, ThrowScope* throwScope, ExpectFlags& flags) { - VM& vm = globalObject->vm(); JSCell* matcherPropCell = matcherProp.asCell(); + AsymmetricMatcherConstructorType constructorType = AsymmetricMatcherConstructorType::none; if (auto* expectAnything = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; if (otherProp.isUndefinedOrNull()) { @@ -229,46 +326,95 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return AsymmetricMatcherResult::PASS; } else if (auto* expectAny = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; JSValue constructorValue = expectAny->m_constructorValue.get(); JSObject* constructorObject = constructorValue.getObject(); - if (otherProp.isPrimitive()) { - if (otherProp.isNumber() && globalObject->numberObjectConstructor() == constructorObject) { - return AsymmetricMatcherResult::PASS; - } else if (otherProp.isBoolean() && globalObject->booleanObjectConstructor() == constructorObject) { - return AsymmetricMatcherResult::PASS; - } else if (otherProp.isSymbol() && globalObject->symbolObjectConstructor() == constructorObject) { + switch (constructorType) { + case AsymmetricMatcherConstructorType::Symbol: { + if (otherProp.isSymbol()) { return AsymmetricMatcherResult::PASS; - } else if (otherProp.isString()) { - if (auto* constructorFunction = jsDynamicCast(constructorObject)) { - String name = constructorFunction->name(vm); - if (name == "String"_s) { - return AsymmetricMatcherResult::PASS; - } - } else if (auto* internalConstructorFunction = jsDynamicCast(constructorObject)) { - String name = internalConstructorFunction->name(); - if (name == "String"_s) { - return AsymmetricMatcherResult::PASS; - } + } + break; + } + case AsymmetricMatcherConstructorType::String: { + if (otherProp.isCell()) { + JSCell* cell = otherProp.asCell(); + switch (cell->type()) { + case JSC::StringType: + case JSC::StringObjectType: + case JSC::DerivedStringObjectType: { + return AsymmetricMatcherResult::PASS; + } + default: { + break; } - } else if (otherProp.isBigInt()) { - if (auto* constructorFunction = jsDynamicCast(constructorObject)) { - String name = constructorFunction->name(vm); - if (name == "BigInt"_s) { - return AsymmetricMatcherResult::PASS; - } - } else if (auto* internalConstructorFunction = jsDynamicCast(constructorObject)) { - String name = internalConstructorFunction->name(); - if (name == "BigInt"_s) { - return AsymmetricMatcherResult::PASS; - } } } + break; + } - return AsymmetricMatcherResult::FAIL; + case AsymmetricMatcherConstructorType::BigInt: { + if (otherProp.isBigInt()) { + return AsymmetricMatcherResult::PASS; + } + break; + } + + case AsymmetricMatcherConstructorType::Boolean: { + if (otherProp.isBoolean()) { + return AsymmetricMatcherResult::PASS; + } + + if (auto* booleanObject = jsDynamicCast(otherProp)) { + return AsymmetricMatcherResult::PASS; + } + + break; + } + + case AsymmetricMatcherConstructorType::Number: { + if (otherProp.isNumber()) { + return AsymmetricMatcherResult::PASS; + } + + if (auto* numberObject = jsDynamicCast(otherProp)) { + return AsymmetricMatcherResult::PASS; + } + + break; + } + + case AsymmetricMatcherConstructorType::Promise: { + if (otherProp.isCell() && otherProp.asCell()->type() == JSPromiseType) { + return AsymmetricMatcherResult::PASS; + } + break; + } + + case AsymmetricMatcherConstructorType::Array: { + if (JSC::isArray(globalObject, otherProp)) { + return AsymmetricMatcherResult::PASS; + } + break; + } + + case AsymmetricMatcherConstructorType::Object: { + if (otherProp.isObject()) { + return AsymmetricMatcherResult::PASS; + } + break; + } + + case AsymmetricMatcherConstructorType::InstanceOf: { + break; + } + case AsymmetricMatcherConstructorType::none: { + ASSERT_NOT_REACHED_WITH_MESSAGE("Invalid constructor type"); + break; + } } if (constructorObject->hasInstance(globalObject, otherProp)) { @@ -277,7 +423,7 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return AsymmetricMatcherResult::FAIL; } else if (auto* expectStringContaining = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; JSValue expectedSubstring = expectStringContaining->m_stringValue.get(); @@ -296,7 +442,7 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return AsymmetricMatcherResult::FAIL; } else if (auto* expectStringMatching = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; JSValue expectedTestValue = expectStringMatching->m_testValue.get(); @@ -324,7 +470,7 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return AsymmetricMatcherResult::FAIL; } else if (auto* expectArrayContaining = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; JSValue expectedArrayValue = expectArrayContaining->m_arrayValue.get(); @@ -369,7 +515,7 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return AsymmetricMatcherResult::FAIL; } else if (auto* expectObjectContaining = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; JSValue patternObject = expectObjectContaining->m_objectValue.get(); @@ -384,7 +530,7 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return AsymmetricMatcherResult::FAIL; } else if (auto* expectCloseTo = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; if (!otherProp.isNumber()) { @@ -412,7 +558,7 @@ AsymmetricMatcherResult matchAsymmetricMatcherAndGetFlags(JSGlobalObject* global return isClose ? AsymmetricMatcherResult::PASS : AsymmetricMatcherResult::FAIL; } } else if (auto* customMatcher = jsDynamicCast(matcherPropCell)) { - if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp)) + if (!readFlagsAndProcessPromise(matcherProp, flags, globalObject, otherProp, constructorType)) return AsymmetricMatcherResult::FAIL; // ignore the "not" flag here, because the custom matchers handle it themselves (accessing this.isNot) @@ -437,7 +583,7 @@ AsymmetricMatcherResult matchAsymmetricMatcher(JSGlobalObject* globalObject, JSV } template -static void handlePromise(PromiseType* promise, JSC__JSGlobalObject* globalObject, JSC::EncodedJSValue ctx, JSC__JSValue (*resolverFunction)(JSC__JSGlobalObject* arg0, JSC__CallFrame* callFrame), JSC__JSValue (*rejecterFunction)(JSC__JSGlobalObject* arg0, JSC__CallFrame* callFrame)) +static void handlePromise(PromiseType* promise, JSC__JSGlobalObject* globalObject, JSC::EncodedJSValue ctx, Zig::FFIFunction resolverFunction, Zig::FFIFunction rejecterFunction) { auto globalThis = reinterpret_cast(globalObject); @@ -590,99 +736,30 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, return false; } - bool canPerformFastSet = JSSet::isAddFastAndNonObservable(set1->structure()) && JSSet::isAddFastAndNonObservable(set2->structure()); - - // This code is loosely based on - // https://github.com/oven-sh/WebKit/blob/657558d4d4c9c33f41b9670e72d96a5a39fe546e/Source/JavaScriptCore/runtime/HashMapImplInlines.h#L203-L211 - if (canPerformFastSet && set1->isIteratorProtocolFastAndNonObservable() && set2->isIteratorProtocolFastAndNonObservable()) { - auto* bucket = set1->head(); - while (bucket) { - if (!bucket->deleted()) { - auto key = bucket->key(); - RETURN_IF_EXCEPTION(*scope, false); - auto** bucket2ptr = set2->findBucket(globalObject, key); - - if (bucket2ptr && (*bucket2ptr)->deleted()) { - bucket2ptr = nullptr; - } - - if (!bucket2ptr) { - auto findDeepEqualKey = [&]() -> bool { - auto* bucket = set2->head(); - while (bucket) { - if (!bucket->deleted()) { - auto key2 = bucket->key(); - if (Bun__deepEquals(globalObject, key, key2, gcBuffer, stack, scope, false)) { - return true; - } - } - bucket = bucket->next(); - } - - return false; - }; - - if (!findDeepEqualKey()) { - return false; - } - } - } - bucket = bucket->next(); - } - - return true; - } - - // This code path can be triggered when it is a class that extends from Set. - // - // class MySet extends Set {} - // - IterationRecord iterationRecord1 = iteratorForIterable(globalObject, v1); - bool isEqual = true; - - // https://github.com/oven-sh/bun/issues/7736 - DeferGC deferGC(vm); - - while (true) { - JSValue next1 = iteratorStep(globalObject, iterationRecord1); - if (next1.isFalse()) { - break; + auto iter1 = JSSetIterator::create(globalObject, set1->structure(), set1, IterationKind::Keys); + JSValue key1; + while (iter1->next(globalObject, key1)) { + if (set2->has(globalObject, key1)) { + continue; } - JSValue nextValue1 = iteratorValue(globalObject, next1); - RETURN_IF_EXCEPTION(*scope, false); - - bool found = false; - IterationRecord iterationRecord2 = iteratorForIterable(globalObject, v2); - while (true) { - JSValue next2 = iteratorStep(globalObject, iterationRecord2); - if (UNLIKELY(next2.isFalse())) { - break; - } - - JSValue nextValue2 = iteratorValue(globalObject, next2); - RETURN_IF_EXCEPTION(*scope, false); - - // set has unique values, no need to count - if (Bun__deepEquals(globalObject, nextValue1, nextValue2, gcBuffer, stack, scope, false)) { - found = true; - if (!nextValue1.isPrimitive()) { - stack.append({ nextValue1, nextValue2 }); - } + // We couldn't find the key in the second set. This may be a false positive due to how + // JSValues are represented in JSC, so we need to fall back to a linear search to be sure. + auto iter2 = JSSetIterator::create(globalObject, set2->structure(), set2, IterationKind::Keys); + JSValue key2; + bool foundMatchingKey = false; + while (iter2->next(globalObject, key2)) { + if (Bun__deepEquals(globalObject, key1, key2, gcBuffer, stack, scope, false)) { + foundMatchingKey = true; break; } } - if (!found) { - isEqual = false; - break; + if (!foundMatchingKey) { + return false; } } - if (!isEqual) { - return false; - } - return true; } case JSMapType: { @@ -698,129 +775,36 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, return false; } - bool canPerformFastSet = JSMap::isSetFastAndNonObservable(map1->structure()) && JSMap::isSetFastAndNonObservable(map2->structure()); - - // This code is loosely based on - // https://github.com/oven-sh/WebKit/blob/657558d4d4c9c33f41b9670e72d96a5a39fe546e/Source/JavaScriptCore/runtime/HashMapImplInlines.h#L203-L211 - if (canPerformFastSet && map1->isIteratorProtocolFastAndNonObservable() && map2->isIteratorProtocolFastAndNonObservable()) { - auto* bucket = map1->head(); - while (bucket) { - if (!bucket->deleted()) { - auto key = bucket->key(); - auto value = bucket->value(); - RETURN_IF_EXCEPTION(*scope, false); - auto** bucket2ptr = map2->findBucket(globalObject, key); - JSMap::BucketType* bucket2 = nullptr; - - if (bucket2ptr) { - bucket2 = *bucket2ptr; - - if (bucket2->deleted()) { - bucket2 = nullptr; - } - } - - if (!bucket2) { - auto findDeepEqualKey = [&]() -> JSMap::BucketType* { - auto* bucket = map2->head(); - while (bucket) { - if (!bucket->deleted()) { - auto key2 = bucket->key(); - if (Bun__deepEquals(globalObject, key, key2, gcBuffer, stack, scope, false)) { - return bucket; - } - } - bucket = bucket->next(); - } - - return nullptr; - }; - - bucket2 = findDeepEqualKey(); - } - - if (!bucket2 || !Bun__deepEquals(globalObject, value, bucket2->value(), gcBuffer, stack, scope, false)) { - return false; + auto iter1 = JSMapIterator::create(globalObject, map1->structure(), map1, IterationKind::Entries); + JSValue key1, value1; + while (iter1->nextKeyValue(globalObject, key1, value1)) { + JSValue value2 = map2->get(globalObject, key1); + if (value2.isUndefined()) { + // We couldn't find the key in the second map. This may be a false positive due to + // how JSValues are represented in JSC, so we need to fall back to a linear search + // to be sure. + auto iter2 = JSMapIterator::create(globalObject, map2->structure(), map2, IterationKind::Entries); + JSValue key2; + bool foundMatchingKey = false; + while (iter2->nextKeyValue(globalObject, key2, value2)) { + if (Bun__deepEquals(globalObject, key1, key2, gcBuffer, stack, scope, false)) { + foundMatchingKey = true; + break; } } - bucket = bucket->next(); - } - return true; - } - - // This code path can be triggered when it is a class that extends from Map. - // - // class MyMap extends Map {} - // - IterationRecord iterationRecord1 = iteratorForIterable(globalObject, v1); - bool isEqual = true; - - // https://github.com/oven-sh/bun/issues/7736 - DeferGC deferGC(vm); - - while (true) { - JSValue next1 = iteratorStep(globalObject, iterationRecord1); - if (next1.isFalse()) { - break; - } - - JSValue nextValue1 = iteratorValue(globalObject, next1); - RETURN_IF_EXCEPTION(*scope, false); - - if (UNLIKELY(!nextValue1.isObject())) { - return false; - } - - JSObject* nextValueObject1 = asObject(nextValue1); - - JSValue key1 = nextValueObject1->getIndex(globalObject, static_cast(0)); - RETURN_IF_EXCEPTION(*scope, false); - - bool found = false; - - IterationRecord iterationRecord2 = iteratorForIterable(globalObject, v2); - - while (true) { - - JSValue next2 = iteratorStep(globalObject, iterationRecord2); - if (UNLIKELY(next2.isFalse())) { - break; - } - - JSValue nextValue2 = iteratorValue(globalObject, next2); - RETURN_IF_EXCEPTION(*scope, false); - - if (UNLIKELY(!nextValue2.isObject())) { + if (!foundMatchingKey) { return false; } - JSObject* nextValueObject2 = asObject(nextValue2); - - JSValue key2 = nextValueObject2->getIndex(globalObject, static_cast(0)); - RETURN_IF_EXCEPTION(*scope, false); - - if (Bun__deepEquals(globalObject, key1, key2, gcBuffer, stack, scope, false)) { - if (Bun__deepEquals(globalObject, nextValue1, nextValue2, gcBuffer, stack, scope, false)) { - found = true; - if (!nextValue1.isPrimitive()) { - stack.append({ nextValue1, nextValue2 }); - } - break; - } - } + // Compare both values below. } - if (!found) { - isEqual = false; - break; + if (!Bun__deepEquals(globalObject, value1, value2, gcBuffer, stack, scope, false)) { + return false; } } - if (!isEqual) { - return false; - } - return true; } case ArrayBufferType: { @@ -904,6 +888,7 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, case Uint16ArrayType: case Int32ArrayType: case Uint32ArrayType: + case Float16ArrayType: case Float32ArrayType: case Float64ArrayType: case BigInt64ArrayType: @@ -1411,29 +1396,61 @@ WebCore__FetchHeaders* WebCore__FetchHeaders__createFromJS(JSC__JSGlobalObject* EnsureStillAliveScope argument0 = JSC::JSValue::decode(argument0_); auto throwScope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm()); + throwScope.assertNoException(); + // Note that we use IDLDOMString here rather than IDLByteString: while headers // should be ASCII only, we want the headers->fill implementation to discover // and error on invalid names and values using TargetType = IDLUnion>, IDLRecord>; using Converter = std::optional::ReturnType>; + auto init = argument0.value().isUndefined() ? Converter() : Converter(convert(*lexicalGlobalObject, argument0.value())); RETURN_IF_EXCEPTION(throwScope, nullptr); + // if the headers are empty, return null + if (!init) { + return nullptr; + } + + // [["", ""]] should be considered empty and return null + if (std::holds_alternative>>(init.value())) { + const auto& sequence = std::get>>(init.value()); + + if (sequence.size() == 0) { + return nullptr; + } + } else { + // {} should be considered empty and return null + const auto& record = std::get>>(init.value()); + if (record.size() == 0) { + return nullptr; + } + } + auto* headers = new WebCore::FetchHeaders({ WebCore::FetchHeaders::Guard::None, {} }); headers->relaxAdoptionRequirement(); - if (init) { - // `fill` doesn't set an exception on the VM if it fails, it returns an - // ExceptionOr. So we need to check for the exception and, if set, - // translate it to JSValue and throw it. - WebCore::propagateException(*lexicalGlobalObject, throwScope, - headers->fill(WTFMove(init.value()))); + + // `fill` doesn't set an exception on the VM if it fails, it returns an + // ExceptionOr. So we need to check for the exception and, if set, + // translate it to JSValue and throw it. + WebCore::propagateException(*lexicalGlobalObject, throwScope, + headers->fill(WTFMove(init.value()))); + + // If there's an exception, it will be thrown by the above call to fill(). + // in that case, let's also free the headers to make memory leaks harder. + if (throwScope.exception()) { + headers->deref(); + return nullptr; } + return headers; } JSC__JSValue WebCore__FetchHeaders__toJS(WebCore__FetchHeaders* headers, JSC__JSGlobalObject* lexicalGlobalObject) { Zig::GlobalObject* globalObject = reinterpret_cast(lexicalGlobalObject); + ASSERT_NO_PENDING_EXCEPTION(globalObject); + bool needsMemoryCost = headers->hasOneRef(); JSValue value = WebCore::toJS(lexicalGlobalObject, globalObject, headers); @@ -1445,6 +1462,7 @@ JSC__JSValue WebCore__FetchHeaders__toJS(WebCore__FetchHeaders* headers, JSC__JS return JSC::JSValue::encode(value); } + JSC__JSValue WebCore__FetchHeaders__clone(WebCore__FetchHeaders* headers, JSC__JSGlobalObject* arg1) { auto throwScope = DECLARE_THROW_SCOPE(arg1->vm()); @@ -1587,7 +1605,7 @@ WebCore::FetchHeaders* WebCore__FetchHeaders__createFromPicoHeaders_(const void* } return headers; } -WebCore::FetchHeaders* WebCore__FetchHeaders__createFromUWS(JSC__JSGlobalObject* arg0, void* arg1) +WebCore::FetchHeaders* WebCore__FetchHeaders__createFromUWS(void* arg1) { uWS::HttpRequest req = *reinterpret_cast(arg1); @@ -1660,11 +1678,12 @@ bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* headers, const ZigString* } else return result.releaseReturnValue(); } -void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* headers, const ZigString* arg1, const ZigString* arg2, JSC__JSGlobalObject* global) +extern "C" void WebCore__FetchHeaders__put(WebCore__FetchHeaders* headers, HTTPHeaderName name, const ZigString* arg2, JSC__JSGlobalObject* global) { auto throwScope = DECLARE_THROW_SCOPE(global->vm()); + throwScope.assertNoException(); // can't throw an exception when there's already one. WebCore::propagateException(*global, throwScope, - headers->set(Zig::toString(*arg1), Zig::toStringCopy(*arg2))); + headers->set(name, Zig::toStringCopy(*arg2))); } void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* headers, const ZigString* arg1, JSC__JSGlobalObject* global) { @@ -1717,29 +1736,49 @@ BunString WebCore__DOMURL__fileSystemPath(WebCore__DOMURL* arg0) extern "C" JSC__JSValue ZigString__toJSONObject(const ZigString* strPtr, JSC::JSGlobalObject* globalObject) { + ASSERT_NO_PENDING_EXCEPTION(globalObject); auto str = Zig::toString(*strPtr); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + if (str.isNull()) { + // isNull() will be true for empty strings and for strings which are too long. + // So we need to check the length is plausibly due to a long string. + if (strPtr->len > Bun__stringSyntheticAllocationLimit) { + scope.throwException(globalObject, Bun::createError(globalObject, Bun::ErrorCode::ERR_STRING_TOO_LONG, "Cannot parse a JSON string longer than 2^32-1 characters"_s)); + return {}; + } + } + + auto catchScope = DECLARE_CATCH_SCOPE(globalObject->vm()); // JSONParseWithException does not propagate exceptions as expected. See #5859 JSValue result = JSONParse(globalObject, str); - if (!result && !scope.exception()) { + if (!result && !catchScope.exception()) scope.throwException(globalObject, createSyntaxError(globalObject, "Failed to parse JSON"_s)); - } - if (scope.exception()) { - auto* exception = scope.exception(); - scope.clearException(); - return JSC::JSValue::encode(exception); + if (catchScope.exception()) { + auto* exception = catchScope.exception(); + catchScope.clearExceptionExceptTermination(); + return JSC::JSValue::encode(exception->value()); } return JSValue::encode(result); } +// We used to just throw "Out of memory" as a regular Error with that string. +// +// But JSC has some different handling for out of memory errors. So we should +// make it look like what JSC does. +void JSGlobalObject__throwOutOfMemoryError(JSC::JSGlobalObject* globalObject) +{ + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + throwOutOfMemoryError(globalObject, scope); +} + JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, JSC__JSGlobalObject* globalObject) { - + ASSERT_NO_PENDING_EXCEPTION(globalObject); SystemError err = *arg0; JSC::VM& vm = globalObject->vm(); @@ -1858,6 +1897,7 @@ double JSC__JSValue__getLengthIfPropertyExistsInternal(JSC__JSValue value, JSC__ case JSC::JSType::Uint16ArrayType: case JSC::JSType::Int32ArrayType: case JSC::JSType::Uint32ArrayType: + case JSC::JSType::Float16ArrayType: case JSC::JSType::Float32ArrayType: case JSC::JSType::Float64ArrayType: case JSC::JSType::BigInt64ArrayType: @@ -2007,6 +2047,7 @@ JSC__JSPromise* JSC__JSValue__asPromise(JSC__JSValue JSValue0) JSC::JSValue value = JSC::JSValue::decode(JSValue0); return JSC::jsDynamicCast(value); } + JSC__JSValue JSC__JSValue__createInternalPromise(JSC__JSGlobalObject* globalObject) { JSC::VM& vm = globalObject->vm(); @@ -2039,6 +2080,7 @@ bool JSC__JSFunction__getSourceCode(JSC__JSValue JSValue0, ZigString* outSourceC void JSC__JSValue__jsonStringify(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, uint32_t arg2, BunString* arg3) { + ASSERT_NO_PENDING_EXCEPTION(arg1); JSC::JSValue value = JSC::JSValue::decode(JSValue0); WTF::String str = JSC::JSONStringify(arg1, value, (unsigned)arg2); *arg3 = Bun::toStringRef(str); @@ -2067,13 +2109,14 @@ JSC__JSValue JSC__JSPromise__asValue(JSC__JSPromise* arg0, JSC__JSGlobalObject* ASSERT_WITH_MESSAGE(value.inherits(), "JSPromise::asValue() called on a non-promise object"); return JSC::JSValue::encode(value); } + JSC__JSPromise* JSC__JSPromise__create(JSC__JSGlobalObject* arg0) { return JSC::JSPromise::create(arg0->vm(), arg0->promiseStructure()); } // TODO: prevent this from allocating so much memory -void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue arg2, JSC__JSValue (*ArgFn3)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1), JSC__JSValue (*ArgFn4)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)) +void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue arg2, Zig::FFIFunction ArgFn3, Zig::FFIFunction ArgFn4) { auto* cell = JSC::JSValue::decode(JSValue0).asCell(); @@ -2107,6 +2150,7 @@ JSC__JSValue JSC__JSGlobalObject__getCachedObject(JSC__JSGlobalObject* globalObj JSC::JSValue result = globalObject->getIfPropertyExists(globalObject, ident); return JSC::JSValue::encode(result); } + JSC__JSValue JSC__JSGlobalObject__putCachedObject(JSC__JSGlobalObject* globalObject, const ZigString* arg1, JSC__JSValue JSValue2) { JSC::VM& vm = globalObject->vm(); @@ -2179,6 +2223,7 @@ bool JSC__JSValue__isSameValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1, bool JSC__JSValue__deepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject) { + ASSERT_NO_PENDING_EXCEPTION(globalObject); JSValue v1 = JSValue::decode(JSValue0); JSValue v2 = JSValue::decode(JSValue1); @@ -2242,19 +2287,15 @@ bool JSC__JSValue__jestDeepMatch(JSC__JSValue JSValue0, JSC__JSValue JSValue1, J return Bun__deepMatch(obj, subset, globalObject, &scope, replacePropsWithAsymmetricMatchers, false); } -// This is the same as the C API version, except it returns a JSValue which may be a *Exception -// We want that so we can return stack traces. -extern "C" JSC__JSValue JSObjectCallAsFunctionReturnValue(JSContextRef ctx, JSC__JSValue object, +extern "C" JSC__JSValue Bun__JSValue__call(JSContextRef ctx, JSC__JSValue object, JSC__JSValue thisObject, size_t argumentCount, const JSValueRef* arguments) { JSC::JSGlobalObject* globalObject = toJS(ctx); JSC::VM& vm = globalObject->vm(); -#if BUN_DEBUG // This is a redundant check, but we add it to make the error message clearer. ASSERT_WITH_MESSAGE(!vm.isCollectorBusyOnCurrentThread(), "Cannot call function inside a finalizer or while GC is running on same thread."); -#endif if (UNLIKELY(!object)) return JSC::JSValue::encode(JSC::JSValue()); @@ -2289,8 +2330,10 @@ extern "C" JSC__JSValue JSObjectCallAsFunctionReturnValue(JSContextRef ctx, JSC_ asyncContextData->putInternalField(vm, 0, restoreAsyncContext); } + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); if (returnedException.get()) { - return JSC::JSValue::encode(JSC::JSValue(returnedException.get())); + scope.throwException(globalObject, returnedException.get()); + return JSC::JSValue::encode({}); } return JSC::JSValue::encode(result); @@ -2348,6 +2391,7 @@ JSC__Exception* JSC__Exception__create(JSC__JSGlobalObject* arg0, JSC__JSObject* ? JSC::Exception::StackCaptureAction::CaptureStack : JSC::Exception::StackCaptureAction::DoNotCaptureStack); } + JSC__JSValue JSC__Exception__value(JSC__Exception* arg0) { return JSC::JSValue::encode(arg0->value()); @@ -2360,17 +2404,21 @@ JSC__JSValue JSC__Exception__value(JSC__Exception* arg0) // JSC__PropertyNameArray__next(JSC__PropertyNameArray* arg0, size_t arg1); // CPP_DECL void JSC__PropertyNameArray__release(JSC__PropertyNameArray* arg0); size_t JSC__JSObject__getArrayLength(JSC__JSObject* arg0) { return arg0->getArrayLength(); } + JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue jsValue, JSC__JSGlobalObject* arg1, uint32_t arg3) { + ASSERT_NO_PENDING_EXCEPTION(arg1); return JSC::JSValue::encode(JSC::JSValue::decode(jsValue).toObject(arg1)->getIndex(arg1, arg3)); } + JSC__JSValue JSC__JSValue__getDirectIndex(JSC__JSValue jsValue, JSC__JSGlobalObject* arg1, uint32_t arg3) { JSC::JSObject* object = JSC::JSValue::decode(jsValue).getObject(); return JSC::JSValue::encode(object->getDirectIndex(arg1, arg3)); } + JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, const ZigString* arg2) { @@ -2405,6 +2453,7 @@ bool JSC__JSString__eql(const JSC__JSString* arg0, JSC__JSGlobalObject* obj, JSC } bool JSC__JSString__is8Bit(const JSC__JSString* arg0) { return arg0->is8Bit(); }; size_t JSC__JSString__length(const JSC__JSString* arg0) { return arg0->length(); } + JSC__JSObject* JSC__JSString__toObject(JSC__JSString* arg0, JSC__JSGlobalObject* arg1) { return arg0->toObject(arg1); @@ -2426,6 +2475,7 @@ extern "C" JSC::JSInternalPromise* JSModuleLoader__import(JSC::JSGlobalObject* g RETURN_IF_EXCEPTION(scope, nullptr); return promise; } + JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* globalObject, const unsigned char* arg1, size_t arg2, const unsigned char* originUrlPtr, size_t originURLLen, const unsigned char* referrerUrlPtr, size_t referrerUrlLen, JSC__JSValue JSValue5, JSC__JSValue* arg6) @@ -2500,6 +2550,7 @@ JSC__JSValue JSC__JSValue__createRangeError(const ZigString* message, const ZigS return JSC::JSValue::encode(rangeError); } + JSC__JSValue JSC__JSValue__createTypeError(const ZigString* message, const ZigString* arg1, JSC__JSGlobalObject* globalObject) { @@ -2570,7 +2621,7 @@ JSC__JSValue JSC__JSValue__values(JSC__JSGlobalObject* globalObject, JSC__JSValu bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, Bun__ArrayBuffer* arg2) { - + ASSERT_NO_PENDING_EXCEPTION(arg1); JSC::JSValue value = JSC::JSValue::decode(JSValue0); if (UNLIKELY(!value) || !value.isCell()) { return false; @@ -2587,6 +2638,7 @@ bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* ar case JSC::JSType::Uint16ArrayType: case JSC::JSType::Int32ArrayType: case JSC::JSType::Uint32ArrayType: + case JSC::JSType::Float16ArrayType: case JSC::JSType::Float32ArrayType: case JSC::JSType::Float64ArrayType: case JSC::JSType::BigInt64ArrayType: @@ -2706,7 +2758,7 @@ JSC__JSValue JSC__JSValue__createStringArray(JSC__JSGlobalObject* globalObject, } JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject* globalObject, - void** errors, uint16_t errors_count, + const JSValue* errors, size_t errors_count, const ZigString* arg3) { JSC::VM& vm = globalObject->vm(); @@ -2722,9 +2774,8 @@ JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject* glob globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), errors_count))) { - for (uint16_t i = 0; i < errors_count; ++i) { - array->initializeIndexWithoutBarrier( - initializationScope, i, JSC::JSValue(reinterpret_cast(errors[i]))); + for (size_t i = 0; i < errors_count; ++i) { + array->initializeIndexWithoutBarrier(initializationScope, i, errors[i]); } } } @@ -2741,11 +2792,6 @@ JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject* glob // static JSC::JSNativeStdFunction* rejecterFunction; // static bool resolverFunctionInitialized = false; -JSC__JSValue ZigString__toValue(const ZigString* arg0, JSC__JSGlobalObject* arg1) -{ - return JSC::JSValue::encode(JSC::JSValue(JSC::jsOwnedString(arg1->vm(), Zig::toString(*arg0)))); -} - JSC__JSValue ZigString__toAtomicValue(const ZigString* arg0, JSC__JSGlobalObject* arg1) { if (arg0->len == 0) { @@ -2806,7 +2852,7 @@ JSC__JSValue ZigString__toExternalValue(const ZigString* arg0, JSC__JSGlobalObje VirtualMachine* JSC__JSGlobalObject__bunVM(JSC__JSGlobalObject* arg0) { - return reinterpret_cast(reinterpret_cast(arg0)->bunVM()); + return reinterpret_cast(WebCore::clientData(arg0->vm())->bunVM); } JSC__JSValue ZigString__toValueGC(const ZigString* arg0, JSC__JSGlobalObject* arg1) @@ -2834,13 +2880,13 @@ void JSC__JSValue__toZigString(JSC__JSValue JSValue0, ZigString* arg1, JSC__JSGl auto str = strValue->value(arg2); - if (str.is8Bit()) { - arg1->ptr = str.span8().data(); + if (str->is8Bit()) { + arg1->ptr = str->span8().data(); } else { - arg1->ptr = Zig::taggedUTF16Ptr(str.span16().data()); + arg1->ptr = Zig::taggedUTF16Ptr(str->span16().data()); } - arg1->len = str.length(); + arg1->len = str->length(); } JSC__JSValue ZigString__external(const ZigString* arg0, JSC__JSGlobalObject* arg1, void* arg2, void (*ArgFn3)(void* arg0, void* arg1, size_t arg2)) @@ -2932,10 +2978,86 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, } #pragma mark - JSC::JSPromise +void JSC__AnyPromise__wrap(JSC__JSGlobalObject* globalObject, EncodedJSValue encodedPromise, void* ctx, JSC__JSValue (*func)(void*, JSC__JSGlobalObject*)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + + JSValue promiseValue = JSC::JSValue::decode(encodedPromise); + ASSERT(!promiseValue.isEmpty()); + + JSValue result = JSC::JSValue::decode(func(ctx, globalObject)); + if (scope.exception()) { + auto* exception = scope.exception(); + scope.clearException(); + + if (auto* promise = jsDynamicCast(promiseValue)) { + promise->reject(globalObject, exception->value()); + return; + } + + if (auto* promise = jsDynamicCast(promiseValue)) { + promise->reject(globalObject, exception->value()); + return; + } + + ASSERT_NOT_REACHED_WITH_MESSAGE("Non-promise value passed to AnyPromise.wrap"); + } + + if (auto* errorInstance = jsDynamicCast(result)) { + if (auto* promise = jsDynamicCast(promiseValue)) { + promise->reject(globalObject, errorInstance); + return; + } + + if (auto* promise = jsDynamicCast(promiseValue)) { + promise->reject(globalObject, errorInstance); + return; + } + + ASSERT_NOT_REACHED_WITH_MESSAGE("Non-promise value passed to AnyPromise.wrap"); + } + + if (auto* promise = jsDynamicCast(promiseValue)) { + promise->resolve(globalObject, result); + return; + } + if (auto* promise = jsDynamicCast(promiseValue)) { + promise->resolve(globalObject, result); + return; + } + + ASSERT_NOT_REACHED_WITH_MESSAGE("Non-promise value passed to AnyPromise.wrap"); +} + +JSC__JSValue JSC__JSPromise__wrap(JSC__JSGlobalObject* globalObject, void* ctx, JSC__JSValue (*func)(void*, JSC__JSGlobalObject*)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + + JSValue result = JSC::JSValue::decode(func(ctx, globalObject)); + if (scope.exception()) { + auto* exception = scope.exception(); + scope.clearException(); + return JSValue::encode(JSC::JSPromise::rejectedPromise(globalObject, exception->value())); + } + + if (auto* promise = jsDynamicCast(result)) { + return JSValue::encode(promise); + } + + if (JSC::ErrorInstance* err = jsDynamicCast(result)) { + return JSValue::encode(JSC::JSPromise::rejectedPromise(globalObject, err)); + } + + return JSValue::encode(JSC::JSPromise::resolvedPromise(globalObject, result)); +} + void JSC__JSPromise__reject(JSC__JSPromise* arg0, JSC__JSGlobalObject* globalObject, JSC__JSValue JSValue2) { JSValue value = JSC::JSValue::decode(JSValue2); + ASSERT_WITH_MESSAGE(!value.isEmpty(), "Promise.reject cannot be called with a empty JSValue"); auto& vm = globalObject->vm(); ASSERT_WITH_MESSAGE(arg0->inherits(), "Argument is not a promise"); ASSERT_WITH_MESSAGE(arg0->status(vm) == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); @@ -2962,6 +3084,7 @@ void JSC__JSPromise__rejectAsHandledException(JSC__JSPromise* arg0, JSC__JSGloba { arg0->rejectAsHandled(arg1, arg2); } + JSC__JSPromise* JSC__JSPromise__rejectedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) { return JSC::JSPromise::rejectedPromise(arg0, JSC::JSValue::decode(JSValue1)); @@ -3027,6 +3150,7 @@ void JSC__JSPromise__rejectOnNextTickWithHandled(JSC__JSPromise* promise, JSC__J RETURN_IF_EXCEPTION(scope, void()); } } + JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* globalObject, JSC__JSValue JSValue1) { JSC::VM& vm = globalObject->vm(); @@ -3115,6 +3239,7 @@ void JSC__JSInternalPromise__rejectAsHandledException(JSC__JSInternalPromise* ar { arg0->rejectAsHandled(arg1, arg2); } + JSC__JSInternalPromise* JSC__JSInternalPromise__rejectedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) { @@ -3127,6 +3252,7 @@ void JSC__JSInternalPromise__resolve(JSC__JSInternalPromise* arg0, JSC__JSGlobal { arg0->resolve(arg1, JSC::JSValue::decode(JSValue2)); } + JSC__JSInternalPromise* JSC__JSInternalPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) { @@ -3372,6 +3498,7 @@ bool JSC__JSValue__isUndefinedOrNull(JSC__JSValue JSValue0) { return JSC::JSValue::decode(JSValue0).isUndefinedOrNull(); } + JSC__JSValue JSC__JSValue__jsBoolean(bool arg0) { return JSC::JSValue::encode(JSC::jsBoolean(arg0)); @@ -3380,6 +3507,7 @@ JSC__JSValue JSC__JSValue__jsDoubleNumber(double arg0) { return JSC::JSValue::encode(JSC::jsNumber(arg0)); } + JSC__JSValue JSC__JSValue__jsEmptyString(JSC__JSGlobalObject* arg0) { return JSC::JSValue::encode(JSC::jsEmptyString(arg0->vm())); @@ -3566,21 +3694,26 @@ JSC__JSValue JSC__JSValue__getIfPropertyExistsImpl(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, const unsigned char* arg1, uint32_t arg2) { - + ASSERT_NO_PENDING_EXCEPTION(globalObject); JSValue value = JSC::JSValue::decode(JSValue0); - if (UNLIKELY(!value.isObject())) - return JSValue::encode({}); + ASSERT_WITH_MESSAGE(!value.isEmpty(), "get() must not be called on empty value"); JSC::VM& vm = globalObject->vm(); JSC::JSObject* object = value.getObject(); - auto identifier = JSC::Identifier::fromString(vm, String(StringImpl::createWithoutCopying({ arg1, arg2 }))); - auto property = JSC::PropertyName(identifier); + if (UNLIKELY(!object)) + return JSValue::encode({}); + + // Since Identifier might not ref' the string, we need to enusre it doesn't get deref'd until this function returns + const auto propertyString = String(StringImpl::createWithoutCopying({ arg1, arg2 })); + const auto identifier = JSC::Identifier::fromString(vm, propertyString); + const auto property = JSC::PropertyName(identifier); return JSC::JSValue::encode(object->getIfPropertyExists(globalObject, property)); } extern "C" JSC__JSValue JSC__JSValue__getIfPropertyExistsImplString(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, BunString* propertyName) { + ASSERT_NO_PENDING_EXCEPTION(globalObject); JSValue value = JSC::JSValue::decode(JSValue0); if (UNLIKELY(!value.isObject())) return JSValue::encode({}); @@ -3596,6 +3729,8 @@ extern "C" JSC__JSValue JSC__JSValue__getIfPropertyExistsImplString(JSC__JSValue extern "C" JSC__JSValue JSC__JSValue__getOwn(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, BunString* propertyName) { + ASSERT_NO_PENDING_EXCEPTION(globalObject); + VM& vm = globalObject->vm(); JSValue value = JSC::JSValue::decode(JSValue0); WTF::String propertyNameString = propertyName->tag == BunStringTag::Empty ? WTF::String(""_s) : propertyName->toWTFString(BunString::ZeroCopy); @@ -3610,6 +3745,7 @@ extern "C" JSC__JSValue JSC__JSValue__getOwn(JSC__JSValue JSValue0, JSC__JSGloba JSC__JSValue JSC__JSValue__getIfPropertyExistsFromPath(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, JSC__JSValue arg1) { + ASSERT_NO_PENDING_EXCEPTION(globalObject); VM& vm = globalObject->vm(); ThrowScope scope = DECLARE_THROW_SCOPE(vm); JSValue value = JSValue::decode(JSValue0); @@ -3779,6 +3915,7 @@ int32_t JSC__JSValue__toInt32(JSC__JSValue JSValue0) CPP_DECL double JSC__JSValue__coerceToDouble(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1) { + ASSERT_NO_PENDING_EXCEPTION(arg1); JSC::JSValue value = JSC::JSValue::decode(JSValue0); auto catchScope = DECLARE_CATCH_SCOPE(arg1->vm()); double result = value.toNumber(arg1); @@ -3846,6 +3983,7 @@ JSC__JSString* JSC__JSValue__toStringOrNull(JSC__JSValue JSValue0, JSC__JSGlobal bool JSC__JSValue__toMatch(JSC__JSValue regexValue, JSC__JSGlobalObject* global, JSC__JSValue value) { + ASSERT_NO_PENDING_EXCEPTION(global); JSC::JSValue regex = JSC::JSValue::decode(regexValue); JSC::JSValue str = JSC::JSValue::decode(value); if (regex.asCell()->type() != RegExpObjectType || !str.isString()) { @@ -3873,13 +4011,24 @@ bool JSC__JSValue__stringIncludes(JSC__JSValue value, JSC__JSGlobalObject* globa static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stackFrame, ZigStackFrame* frame) { - frame->source_url = Bun::toStringRef(stackFrame->sourceURL(vm)); - if (stackFrame->isWasmFrame()) { frame->code_type = ZigStackFrameCodeWasm; + + auto name = stackFrame->functionName(vm); + if (!name.isEmpty()) { + frame->function_name = Bun::toStringRef(name); + } + + auto sourceURL = stackFrame->sourceURL(vm); + if (sourceURL != "[wasm code]"_s) { + // [wasm code] is a useless source URL, so we don't bother to set it. + // It is the default value JSC returns. + frame->source_url = Bun::toStringRef(sourceURL); + } return; } + frame->source_url = Bun::toStringRef(stackFrame->sourceURL(vm)); auto m_codeBlock = stackFrame->codeBlock(); if (m_codeBlock) { switch (m_codeBlock->codeType()) { @@ -3921,7 +4070,7 @@ static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stack static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunString* source_lines, OrdinalNumber* source_line_numbers, uint8_t source_lines_count, - ZigStackFramePosition* position) + ZigStackFramePosition* position, JSC::SourceProvider** referenced_source_provider) { auto code = stackFrame->codeBlock(); if (!code) @@ -3935,15 +4084,20 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr WTF::StringView sourceString = provider->source(); if (UNLIKELY(sourceString.isNull())) return; + if (!stackFrame->hasBytecodeIndex()) { - auto lineColumn = stackFrame->computeLineAndColumn(); - position->line_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.line).zeroBasedInt(); - position->column_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.column).zeroBasedInt(); + if (stackFrame->hasLineAndColumnInfo()) { + auto lineColumn = stackFrame->computeLineAndColumn(); + position->line_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.line).zeroBasedInt(); + position->column_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.column).zeroBasedInt(); + } + position->byte_position = -1; return; } auto location = Bun::getAdjustedPositionForBytecode(code, stackFrame->bytecodeIndex()); + *position = location; if (source_lines_count > 1 && source_lines != nullptr && sourceString.is8Bit()) { // Search for the beginning of the line @@ -3961,8 +4115,14 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr const unsigned char* bytes = sourceString.span8().data(); - // Most of the time, when you look at a stack trace, you want a couple lines above - source_lines[0] = Bun::toStringRef(sourceString.substring(lineStart, lineEnd - lineStart).toStringWithoutCopying()); + // Most of the time, when you look at a stack trace, you want a couple lines above. + + // It is key to not clone this data because source code strings are large. + // Usage of toStringView (non-owning) is safe as we ref the provider. + provider->ref(); + ASSERT(*referenced_source_provider == nullptr); + *referenced_source_provider = provider; + source_lines[0] = Bun::toStringView(sourceString.substring(lineStart, lineEnd - lineStart)); source_line_numbers[0] = location.line(); if (lineStart > 0) { @@ -3988,9 +4148,7 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr } // We are at the beginning of the line - source_lines[source_line_i] = Bun::toStringRef( - sourceString.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1) - .toStringWithoutCopying()); + source_lines[source_line_i] = Bun::toStringView(sourceString.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1)); source_line_numbers[source_line_i] = location.line().fromZeroBasedInt(location.line().zeroBasedInt() - source_line_i); source_line_i++; @@ -4001,17 +4159,15 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr } } } - - *position = location; } static void populateStackFrame(JSC::VM& vm, ZigStackTrace* trace, const JSC::StackFrame* stackFrame, - ZigStackFrame* frame, bool is_top) + ZigStackFrame* frame, bool is_top, JSC::SourceProvider** referenced_source_provider) { populateStackFrameMetadata(vm, stackFrame, frame); populateStackFramePosition(stackFrame, is_top ? trace->source_lines_ptr : nullptr, is_top ? trace->source_lines_numbers : nullptr, - is_top ? trace->source_lines_to_collect : 0, &frame->position); + is_top ? trace->source_lines_to_collect : 0, &frame->position, referenced_source_provider); } class V8StackTraceIterator { @@ -4064,18 +4220,18 @@ class V8StackTraceIterator { offset = end; // the proper singular spelling is parenthesis - auto openingParenthese = line.reverseFind('('); - auto closingParenthese = line.reverseFind(')'); + auto openingParentheses = line.reverseFind('('); + auto closingParentheses = line.reverseFind(')'); - if (openingParenthese > closingParenthese) - openingParenthese = WTF::notFound; + if (openingParentheses > closingParentheses) + openingParentheses = WTF::notFound; - if (closingParenthese == WTF::notFound || closingParenthese == WTF::notFound) { + if (closingParentheses == WTF::notFound || closingParentheses == WTF::notFound) { offset = stack.length(); return false; } - auto lineInner = StringView_slice(line, openingParenthese + 1, closingParenthese); + auto lineInner = StringView_slice(line, openingParentheses + 1, closingParentheses); { auto marker1 = 0; @@ -4148,7 +4304,7 @@ class V8StackTraceIterator { } done_block: - StringView functionName = line.substring(0, openingParenthese - 1); + StringView functionName = line.substring(0, openingParentheses - 1); if (functionName == ""_s) { functionName = StringView(); @@ -4190,14 +4346,14 @@ static void populateStackTrace(JSC::VM& vm, const WTF::Vector& while (frame_i < frame_count && stack_frame_i < total_frame_count) { // Skip native frames - while (stack_frame_i < total_frame_count && !(&frames.at(stack_frame_i))->codeBlock() && !(&frames.at(stack_frame_i))->isWasmFrame()) { + while (stack_frame_i < total_frame_count && !(&frames.at(stack_frame_i))->hasLineAndColumnInfo() && !(&frames.at(stack_frame_i))->isWasmFrame()) { stack_frame_i++; } if (stack_frame_i >= total_frame_count) break; ZigStackFrame* frame = &trace->frames_ptr[frame_i]; - populateStackFrame(vm, trace, &frames[stack_frame_i], frame, frame_i == 0); + populateStackFrame(vm, trace, &frames[stack_frame_i], frame, frame_i == 0, &trace->referenced_source_provider); stack_frame_i++; frame_i++; } @@ -4619,10 +4775,9 @@ JSC__JSValue JSC__JSValue__toError_(JSC__JSValue JSValue0) return JSC::JSValue::encode({}); } -void JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, - ZigException* exception) +void JSC__JSValue__toZigException(JSC__JSValue jsException, JSC__JSGlobalObject* global, ZigException* exception) { - JSC::JSValue value = JSC::JSValue::decode(JSValue0); + JSC::JSValue value = JSC::JSValue::decode(jsException); if (value == JSC::JSValue {}) { exception->code = JSErrorCodeError; exception->name = Bun::toStringRef("Error"_s); @@ -4632,17 +4787,17 @@ void JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject* ar if (JSC::Exception* jscException = JSC::jsDynamicCast(value)) { if (JSC::ErrorInstance* error = JSC::jsDynamicCast(jscException->value())) { - fromErrorInstance(exception, arg1, error, &jscException->stack(), value); + fromErrorInstance(exception, global, error, &jscException->stack(), value); return; } } if (JSC::ErrorInstance* error = JSC::jsDynamicCast(value)) { - fromErrorInstance(exception, arg1, error, nullptr, value); + fromErrorInstance(exception, global, error, nullptr, value); return; } - exceptionFromString(exception, value, arg1); + exceptionFromString(exception, value, global); } void JSC__Exception__getStackTrace(JSC__Exception* arg0, ZigStackTrace* trace) @@ -4656,6 +4811,12 @@ JSC__JSValue JSC__VM__runGC(JSC__VM* vm, bool sync) { JSC::JSLockHolder lock(vm); +#if IS_MALLOC_DEBUGGING_ENABLED && OS(DARWIN) + if (!malloc_zone_check(nullptr)) { + BUN_PANIC("Heap corruption detected!!"); + } +#endif + vm->finalizeSynchronousJSExecution(); WTF::releaseFastMallocFreeMemory(); @@ -4663,6 +4824,9 @@ JSC__JSValue JSC__VM__runGC(JSC__VM* vm, bool sync) vm->clearSourceProviderCaches(); vm->heap.deleteAllUnlinkedCodeBlocks(JSC::PreventCollectionAndDeleteAllCode); vm->heap.collectNow(JSC::Sync, JSC::CollectionScope::Full); +#if IS_MALLOC_DEBUGGING_ENABLED && OS(DARWIN) + malloc_zone_pressure_relief(nullptr, 0); +#endif } else { vm->heap.deleteAllUnlinkedCodeBlocks(JSC::DeleteAllCodeIfNotCollecting); vm->heap.collectSync(JSC::CollectionScope::Full); @@ -4670,6 +4834,12 @@ JSC__JSValue JSC__VM__runGC(JSC__VM* vm, bool sync) vm->finalizeSynchronousJSExecution(); +#if IS_MALLOC_DEBUGGING_ENABLED && OS(DARWIN) + if (!malloc_zone_check(nullptr)) { + BUN_PANIC("Heap corruption detected after GC!!"); + } +#endif + return JSC::JSValue::encode(JSC::jsNumber(vm->heap.sizeAfterLastFullCollection())); } @@ -4703,6 +4873,23 @@ void JSC__VM__holdAPILock(JSC__VM* arg0, void* ctx, void (*callback)(void* arg0) callback(ctx); } +// The following two functions are copied 1:1 from JSLockHolder to provide a +// new, more ergonomic binding for interacting with the lock from Zig +// https://github.com/WebKit/WebKit/blob/main/Source/JavaScriptCore/runtime/JSLock.cpp + +extern "C" void JSC__VM__getAPILock(JSC::VM* vm) +{ + // https://github.com/WebKit/WebKit/blob/6cb5017d237ef7cb898582a22f05acca22322845/Source/JavaScriptCore/runtime/JSLock.cpp#L67 + vm->apiLock().lock(); +} + +extern "C" void JSC__VM__releaseAPILock(JSC::VM* vm) +{ + // https://github.com/WebKit/WebKit/blob/6cb5017d237ef7cb898582a22f05acca22322845/Source/JavaScriptCore/runtime/JSLock.cpp#L72 + RefPtr apiLock(&vm->apiLock()); + apiLock->unlock(); +} + void JSC__JSString__iterator(JSC__JSString* arg0, JSC__JSGlobalObject* arg1, void* arg2) { jsstring_iterator* iter = (jsstring_iterator*)arg2; @@ -4759,13 +4946,26 @@ void JSC__VM__notifyNeedDebuggerBreak(JSC__VM* arg0) { (*arg0).notifyNeedDebugge void JSC__VM__notifyNeedShellTimeoutCheck(JSC__VM* arg0) { (*arg0).notifyNeedShellTimeoutCheck(); } void JSC__VM__notifyNeedWatchdogCheck(JSC__VM* arg0) { (*arg0).notifyNeedWatchdogCheck(); } -void JSC__VM__throwError(JSC__VM* vm_, JSC__JSGlobalObject* arg1, JSC__JSValue value) +void JSC__VM__throwError(JSC__VM* vm_, JSC__JSGlobalObject* arg1, JSC__JSValue encodedValue) { JSC::VM& vm = *reinterpret_cast(vm_); - auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSObject* error = JSC::JSValue::decode(value).getObject(); - JSC::Exception* exception = JSC::Exception::create(vm, error); + JSValue value = JSValue::decode(encodedValue); + scope.assertNoException(); // can't throw an exception when there's already one. + ASSERT(!value.isEmpty()); // can't throw an empty value. + + // This case can happen if we did not call .toError() on a JSValue. + if (value.isCell()) { + JSC::JSCell* cell = value.asCell(); + if (cell->type() == JSC::CellType && cell->inherits()) { + scope.throwException(arg1, jsCast(value)); + return; + } + } + + // Do not call .getObject() on it. + // https://github.com/oven-sh/bun/issues/13311 + JSC::Exception* exception = JSC::Exception::create(vm, value); scope.throwException(arg1, exception); } @@ -4780,6 +4980,7 @@ JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject* globalObj JSC::ensureStillAliveHere(JSC::JSValue::decode(JSValue1)); return JSC::JSValue::encode(promise); } + JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject* globalObject, JSC__JSValue JSValue1) { @@ -4814,9 +5015,12 @@ enum class BuiltinNamesMap : uint8_t { path, stream, asyncIterator, + name, + message, + error, }; -static JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigned char name) +static const JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigned char name) { auto& vm = globalObject->vm(); auto clientData = WebCore::clientData(vm); @@ -4863,6 +5067,15 @@ static JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigne case BuiltinNamesMap::asyncIterator: { return vm.propertyNames->asyncIteratorSymbol; } + case BuiltinNamesMap::name: { + return vm.propertyNames->name; + } + case BuiltinNamesMap::message: { + return vm.propertyNames->message; + } + case BuiltinNamesMap::error: { + return vm.propertyNames->error; + } default: { ASSERT_NOT_REACHED(); return Identifier(); @@ -4892,6 +5105,7 @@ bool JSC__JSValue__toBooleanSlow(JSC__JSValue JSValue0, JSC__JSGlobalObject* glo template static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, void* arg2, void (*iter)(JSC__JSGlobalObject* arg0, void* ctx, ZigString* arg2, JSC__JSValue JSValue3, bool isSymbol, bool isPrivateSymbol)) { + ASSERT_NO_PENDING_EXCEPTION(globalObject); JSC::JSValue value = JSC::JSValue::decode(JSValue0); JSC::JSObject* object = value.getObject(); if (!object) @@ -4918,8 +5132,8 @@ static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlob } } } - - auto* clientData = WebCore::clientData(vm); + auto* propertyNames = vm.propertyNames; + auto& builtinNames = WebCore::builtinNames(vm); WTF::Vector visitedProperties; restart: @@ -4932,12 +5146,12 @@ static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlob } auto* prop = entry.key(); - if (prop == vm.propertyNames->constructor - || prop == vm.propertyNames->underscoreProto - || prop == vm.propertyNames->toStringTagSymbol) + if (prop == propertyNames->constructor + || prop == propertyNames->underscoreProto + || prop == propertyNames->toStringTagSymbol) return true; - if (clientData->builtinNames().bunNativePtrPrivateName() == prop) + if (builtinNames.bunNativePtrPrivateName() == prop) return true; if (visitedProperties.contains(Identifier::fromUid(vm, prop))) { @@ -5016,11 +5230,11 @@ static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlob continue; // ignore constructor - if (property == vm.propertyNames->constructor || clientData->builtinNames().bunNativePtrPrivateName() == property) + if (property == propertyNames->constructor || builtinNames.bunNativePtrPrivateName() == property) continue; if constexpr (nonIndexedOnly) { - if (property == vm.propertyNames->length) { + if (property == propertyNames->length) { continue; } } @@ -5030,8 +5244,8 @@ static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlob continue; if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) { - if (property == vm.propertyNames->underscoreProto - || property == vm.propertyNames->toStringTagSymbol) + if (property == propertyNames->underscoreProto + || property == propertyNames->toStringTagSymbol) continue; } @@ -5048,7 +5262,12 @@ static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlob if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) { if ((slot.attributes() & PropertyAttribute::Accessor) != 0) { - propertyValue = slot.getPureResult(); + // If we can't use getPureResult, let's at least say it was a [Getter] + if (!slot.isCacheableGetter()) { + propertyValue = slot.getterSetter(); + } else { + propertyValue = slot.getPureResult(); + } } else if (slot.attributes() & PropertyAttribute::BuiltinOrFunction) { propertyValue = slot.getValue(globalObject, property); } else if (slot.isCustom()) { @@ -5058,8 +5277,13 @@ static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlob } else if (object->getOwnPropertySlot(object, globalObject, property, slot)) { propertyValue = slot.getValue(globalObject, property); } - } else if ((slot.attributes() & PropertyAttribute::Accessor) != 0) { - propertyValue = slot.getPureResult(); + } else if (slot.isAccessor()) { + // If we can't use getPureResult, let's at least say it was a [Getter] + if (!slot.isCacheableGetter()) { + propertyValue = slot.getterSetter(); + } else { + propertyValue = slot.getPureResult(); + } } else { propertyValue = slot.getValue(globalObject, property); } @@ -5294,14 +5518,43 @@ extern "C" JSC__JSValue WebCore__AbortSignal__toJS(WebCore__AbortSignal* arg0, J return JSValue::encode(toJS>(*globalObject, *jsCast(globalObject), *abortSignal)); } -extern "C" WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSValue JSValue1) +extern "C" void WebCore__AbortSignal__incrementPendingActivity(WebCore__AbortSignal* arg0) +{ + WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); + abortSignal->incrementPendingActivityCount(); +} + +extern "C" void WebCore__AbortSignal__decrementPendingActivity(WebCore__AbortSignal* arg0) +{ + WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); + abortSignal->decrementPendingActivityCount(); +} + +extern "C" WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC::JSGlobalObject* globalObject, uint8_t reason) { WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); - abortSignal->signalAbort(JSC::JSValue::decode(JSValue1)); + abortSignal->signalAbort( + globalObject, + static_cast(reason)); + ; return arg0; } +extern "C" JSC__JSValue WebCore__AbortSignal__reasonIfAborted(WebCore::AbortSignal* signal, JSC::JSGlobalObject* globalObject, CommonAbortReason* reason) +{ + if (signal->aborted()) { + *reason = signal->commonReason(); + if (signal->commonReason() != WebCore::CommonAbortReason::None) { + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(signal->jsReason(*globalObject)); + } + + return JSValue::encode({}); +} + extern "C" bool WebCore__AbortSignal__aborted(WebCore__AbortSignal* arg0) { WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); @@ -5321,12 +5574,12 @@ extern "C" WebCore__AbortSignal* WebCore__AbortSignal__ref(WebCore__AbortSignal* return arg0; } -extern "C" WebCore__AbortSignal* WebCore__AbortSignal__unref(WebCore__AbortSignal* arg0) +extern "C" void WebCore__AbortSignal__unref(WebCore__AbortSignal* arg0) { WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); abortSignal->deref(); - return arg0; } + extern "C" void WebCore__AbortSignal__cleanNativeBindings(WebCore__AbortSignal* arg0, void* arg1) { WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); @@ -5357,58 +5610,66 @@ extern "C" WebCore__AbortSignal* WebCore__AbortSignal__fromJS(JSC__JSValue value return reinterpret_cast(&object->wrapped()); } -static auto ABORT_ERROR_NAME = MAKE_STATIC_STRING_IMPL("AbortError"); -extern "C" JSC__JSValue WebCore__AbortSignal__createAbortError(const ZigString* message, const ZigString* arg1, - JSC__JSGlobalObject* globalObject) + +CPP_DECL double JSC__JSValue__getUnixTimestamp(JSC__JSValue timeValue) { - JSC::VM& vm = globalObject->vm(); - ZigString code = *arg1; - JSC::JSObject* error = Zig::getErrorInstance(message, globalObject).asCell()->getObject(); + JSC::JSValue decodedValue = JSC::JSValue::decode(timeValue); + JSC::DateInstance* date = JSC::jsDynamicCast(decodedValue); + if (!date) + return PNaN; - error->putDirect( - vm, vm.propertyNames->name, - JSC::JSValue(JSC::jsOwnedString(vm, ABORT_ERROR_NAME)), - 0); + return date->internalNumber(); +} - if (code.len > 0) { - auto clientData = WebCore::clientData(vm); - JSC::JSValue codeValue = Zig::toJSStringValue(code, globalObject); - error->putDirect(vm, clientData->builtinNames().codePublicName(), codeValue, 0); - } +extern "C" double Bun__parseDate(JSC::JSGlobalObject* globalObject, BunString* str) +{ + auto& vm = globalObject->vm(); + return vm.dateCache.parseDate(globalObject, vm, str->toWTFString()); +} + +extern "C" EncodedJSValue JSC__JSValue__dateInstanceFromNullTerminatedString(JSC::JSGlobalObject* globalObject, const LChar* nullTerminatedChars) +{ + double dateSeconds = WTF::parseDate(std::span(nullTerminatedChars, strlen(reinterpret_cast(nullTerminatedChars)))); + JSC::DateInstance* date = JSC::DateInstance::create(globalObject->vm(), globalObject->dateStructure(), dateSeconds); - return JSC::JSValue::encode(error); + return JSValue::encode(date); } -static auto TIMEOUT_ERROR_NAME = MAKE_STATIC_STRING_IMPL("TimeoutError"); -extern "C" JSC__JSValue WebCore__AbortSignal__createTimeoutError(const ZigString* message, const ZigString* arg1, - JSC__JSGlobalObject* globalObject) +// this is largely copied from dateProtoFuncToISOString +extern "C" int JSC__JSValue__toISOString(JSC::JSGlobalObject* globalObject, EncodedJSValue dateValue, char* buf) { - JSC::VM& vm = globalObject->vm(); - ZigString code = *arg1; - JSC::JSObject* error = Zig::getErrorInstance(message, globalObject).asCell()->getObject(); + char buffer[29]; + JSC::DateInstance* thisDateObj = JSC::jsDynamicCast(JSC::JSValue::decode(dateValue)); + if (!thisDateObj) + return -1; - error->putDirect( - vm, vm.propertyNames->name, - JSC::JSValue(JSC::jsOwnedString(vm, TIMEOUT_ERROR_NAME)), - 0); + if (!std::isfinite(thisDateObj->internalNumber())) + return -1; - if (code.len > 0) { - auto clientData = WebCore::clientData(vm); - JSC::JSValue codeValue = Zig::toJSStringValue(code, globalObject); - error->putDirect(vm, clientData->builtinNames().codePublicName(), codeValue, 0); - } + auto& vm = globalObject->vm(); - return JSC::JSValue::encode(error); -} + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(vm.dateCache); + if (!gregorianDateTime) + return -1; -CPP_DECL double JSC__JSValue__getUnixTimestamp(JSC__JSValue timeValue) -{ - JSC::JSValue decodedValue = JSC::JSValue::decode(timeValue); - JSC::DateInstance* date = JSC::jsDynamicCast(decodedValue); - if (!date) - return PNaN; + // If the year is outside the bounds of 0 and 9999 inclusive we want to use the extended year format (ES 15.9.1.15.1). + int ms = static_cast(fmod(thisDateObj->internalNumber(), msPerSecond)); + if (ms < 0) + ms += msPerSecond; - return date->internalNumber(); + int charactersWritten; + if (gregorianDateTime->year() > 9999 || gregorianDateTime->year() < 0) + charactersWritten = snprintf(buffer, sizeof(buffer), "%+07d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime->year(), gregorianDateTime->month() + 1, gregorianDateTime->monthDay(), gregorianDateTime->hour(), gregorianDateTime->minute(), gregorianDateTime->second(), ms); + else + charactersWritten = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime->year(), gregorianDateTime->month() + 1, gregorianDateTime->monthDay(), gregorianDateTime->hour(), gregorianDateTime->minute(), gregorianDateTime->second(), ms); + + memcpy(buf, buffer, charactersWritten); + + ASSERT(charactersWritten > 0 && static_cast(charactersWritten) < sizeof(buffer)); + if (static_cast(charactersWritten) >= sizeof(buffer)) + return -1; + + return charactersWritten; } #pragma mark - WebCore::DOMFormData @@ -5497,7 +5758,17 @@ CPP_DECL void JSC__VM__setControlFlowProfiler(JSC__VM* vm, bool isEnabled) extern "C" EncodedJSValue JSC__createError(JSC::JSGlobalObject* globalObject, const BunString* str) { - return JSValue::encode(JSC::createError(globalObject, str->toWTFString())); + return JSValue::encode(JSC::createError(globalObject, str->toWTFString(BunString::ZeroCopy))); +} + +extern "C" EncodedJSValue JSC__createTypeError(JSC::JSGlobalObject* globalObject, const BunString* str) +{ + return JSValue::encode(JSC::createTypeError(globalObject, str->toWTFString(BunString::ZeroCopy))); +} + +extern "C" EncodedJSValue JSC__createRangeError(JSC::JSGlobalObject* globalObject, const BunString* str) +{ + return JSValue::encode(JSC::createRangeError(globalObject, str->toWTFString(BunString::ZeroCopy))); } extern "C" EncodedJSValue ExpectMatcherUtils__getSingleton(JSC::JSGlobalObject* globalObject_) @@ -5548,11 +5819,35 @@ extern "C" EncodedJSValue JSArray__constructArray( JSC::constructArray(global, (ArrayAllocationProfile*)nullptr, values, values_len)); } +extern "C" EncodedJSValue JSArray__constructEmptyArray( + JSC::JSGlobalObject* global, + size_t len) +{ + return JSValue::encode(JSC::constructEmptyArray(global, (ArrayAllocationProfile*)nullptr, len)); +} + extern "C" bool JSGlobalObject__hasException(JSC::JSGlobalObject* globalObject) { return DECLARE_CATCH_SCOPE(globalObject->vm()).exception() != 0; } +extern "C" void JSGlobalObject__clearException(JSC::JSGlobalObject* globalObject) +{ + DECLARE_CATCH_SCOPE(globalObject->vm()).clearException(); +} + +extern "C" JSC::EncodedJSValue JSGlobalObject__tryTakeException(JSC::JSGlobalObject* globalObject) +{ + auto scope = DECLARE_CATCH_SCOPE(globalObject->vm()); + + if (auto exception = scope.exception()) { + scope.clearException(); + return JSC::JSValue::encode(exception); + } + + return {}; +} + CPP_DECL bool JSC__GetterSetter__isGetterNull(JSC__GetterSetter* gettersetter) { return gettersetter->isGetterNull(); @@ -5577,3 +5872,8 @@ CPP_DECL JSC__JSValue Bun__ProxyObject__getInternalField(JSC__JSValue value, uin { return JSValue::encode(jsCast(JSValue::decode(value))->internalField((ProxyObject::Field)id).get()); } + +CPP_DECL void JSC__SourceProvider__deref(JSC::SourceProvider* provider) +{ + provider->deref(); +} diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 99b74e577e744..bbdc4a4fbc60d 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -35,20 +35,35 @@ pub const JSObject = extern struct { }); } - const InitializeCallback = *const fn (ctx: ?*anyopaque, obj: [*c]JSObject, global: [*c]JSGlobalObject) callconv(.C) void; + const InitializeCallback = *const fn (ctx: *anyopaque, obj: *JSObject, global: *JSGlobalObject) callconv(.C) void; + extern fn JSC__JSObject__create(global_object: *JSGlobalObject, length: usize, ctx: *anyopaque, initializer: InitializeCallback) JSValue; pub fn create(global_object: *JSGlobalObject, length: usize, ctx: *anyopaque, initializer: InitializeCallback) JSValue { - return cppFn("create", .{ - global_object, - length, - ctx, - initializer, - }); + return JSC__JSObject__create(global_object, length, ctx, initializer); + } + + extern fn JSC__createStructure(*JSC.JSGlobalObject, *JSC.JSCell, u32, names: [*]bun.String) JSC.JSValue; + extern fn JSC__createEmptyObjectWithStructure(*JSC.JSGlobalObject, *anyopaque) JSC.JSValue; + extern fn JSC__putDirectOffset(*JSC.VM, JSC.JSValue, offset: u32, JSC.JSValue) void; + + pub fn createStructure(global: *JSGlobalObject, owner: JSC.JSValue, length: u32, names: [*]bun.String) JSValue { + JSC.markBinding(@src()); + return JSC__createStructure(global, owner.asCell(), length, names); + } + + pub fn uninitialized(global: *JSGlobalObject, structure: JSC.JSValue) JSValue { + JSC.markBinding(@src()); + return JSC__createEmptyObjectWithStructure(global, structure.asCell()); + } + + pub fn putDirectOffset(this: JSValue, vm: *VM, offset: u32, value: JSValue) void { + JSC.markBinding(@src()); + return JSC__putDirectOffset(vm, this, offset, value); } pub fn Initializer(comptime Ctx: type, comptime func: fn (*Ctx, obj: *JSObject, global: *JSGlobalObject) void) type { return struct { - pub fn call(this: ?*anyopaque, obj: [*c]JSObject, global: [*c]JSGlobalObject) callconv(.C) void { - @call(bun.callmod_inline, func, .{ @as(*Ctx, @ptrCast(@alignCast(this.?))), obj.?, global.? }); + pub fn call(this: *anyopaque, obj: *JSObject, global: *JSGlobalObject) callconv(.C) void { + @call(bun.callmod_inline, func, .{ @as(*Ctx, @ptrCast(@alignCast(this))), obj, global }); } }; } @@ -80,7 +95,6 @@ pub const JSObject = extern struct { pub const Extern = [_][]const u8{ "putRecord", - "create", "getArrayLength", "getIndex", "putAtIndex", @@ -123,7 +137,7 @@ pub const ZigString = extern struct { pub fn dupeForJS(utf8: []const u8, allocator: std.mem.Allocator) !ZigString { if (try strings.toUTF16Alloc(allocator, utf8, false, false)) |utf16| { - var out = ZigString.init16(utf16); + var out = ZigString.initUTF16(utf16); out.mark(); out.markUTF16(); return out; @@ -134,12 +148,13 @@ pub const ZigString = extern struct { } } - pub fn toJS(this: ZigString, ctx: *JSC.JSGlobalObject) JSValue { + extern fn ZigString__toValueGC(arg0: *const ZigString, arg1: *JSGlobalObject) JSC.JSValue; + pub fn toJS(this: *const ZigString, ctx: *JSC.JSGlobalObject) JSValue { if (this.isGloballyAllocated()) { return this.toExternalValue(ctx); } - return this.toValueAuto(ctx); + return ZigString__toValueGC(this, ctx); } /// This function is not optimized! @@ -587,12 +602,13 @@ pub const ZigString = extern struct { return out; } - pub fn static(comptime slice_: []const u8) *const ZigString { + pub fn static(comptime slice_: [:0]const u8) *const ZigString { const Holder = struct { - pub const value = ZigString{ ._unsafe_ptr_do_not_use = slice_.ptr, .len = slice_.len }; + const null_terminated_ascii_literal = slice_; + pub const value = &ZigString{ ._unsafe_ptr_do_not_use = null_terminated_ascii_literal.ptr, .len = null_terminated_ascii_literal.len }; }; - return &Holder.value; + return Holder.value; } pub const GithubActionFormatter = struct { @@ -613,8 +629,8 @@ pub const ZigString = extern struct { return shim.cppFn("toAtomicValue", .{ this, globalThis }); } - pub fn init16(slice_: []const u16) ZigString { - var out = ZigString{ ._unsafe_ptr_do_not_use = std.mem.sliceAsBytes(slice_).ptr, .len = slice_.len }; + pub fn initUTF16(items: []const u16) ZigString { + var out = ZigString{ ._unsafe_ptr_do_not_use = @ptrCast(items), .len = items.len }; out.markUTF16(); return out; } @@ -661,6 +677,11 @@ pub const ZigString = extern struct { } pub fn toExternalU16(ptr: [*]const u16, len: usize, global: *JSGlobalObject) JSValue { + if (len > String.max_length()) { + bun.default_allocator.free(ptr[0..len]); + global.ERR_STRING_TOO_LONG("Cannot create a string longer than 2^32-1 characters", .{}).throw(); + return JSValue.zero; + } return shim.cppFn("toExternalU16", .{ ptr, len, global }); } @@ -710,7 +731,7 @@ pub const ZigString = extern struct { } pub inline fn toRef(slice_: []const u8, global: *JSGlobalObject) C_API.JSValueRef { - return init(slice_).toValue(global).asRef(); + return init(slice_).toJS(global).asRef(); } pub const Empty = ZigString{ ._unsafe_ptr_do_not_use = "", .len = 0 }; @@ -824,14 +845,6 @@ pub const ZigString = extern struct { return strings.trim(this.full(), " \r\n"); } - pub fn toValueAuto(this: *const ZigString, global: *JSGlobalObject) JSValue { - if (!this.is16Bit()) { - return this.toValue(global); - } else { - return this.to16BitValue(global); - } - } - inline fn assertGlobalIfNeeded(this: *const ZigString) void { if (comptime bun.Environment.allow_assert) { if (this.isGloballyAllocated()) { @@ -848,13 +861,13 @@ pub const ZigString = extern struct { } } - pub fn toValue(this: *const ZigString, global: *JSGlobalObject) JSValue { - this.assertGlobalIfNeeded(); - return shim.cppFn("toValue", .{ this, global }); - } - pub fn toExternalValue(this: *const ZigString, global: *JSGlobalObject) JSValue { this.assertGlobal(); + if (this.len > String.max_length()) { + bun.default_allocator.free(@constCast(this.byteSlice())); + global.ERR_STRING_TOO_LONG("Cannot create a string longer than 2^32-1 characters", .{}).throw(); + return .zero; + } return shim.cppFn("toExternalValue", .{ this, global }); } @@ -872,6 +885,12 @@ pub const ZigString = extern struct { ctx: ?*anyopaque, callback: *const fn (ctx: ?*anyopaque, ptr: ?*anyopaque, len: usize) callconv(.C) void, ) JSValue { + if (this.len > String.max_length()) { + callback(ctx, @constCast(@ptrCast(this.byteSlice().ptr)), this.len); + global.ERR_STRING_TOO_LONG("Cannot create a string longer than 2^32-1 characters", .{}).throw(); + return .zero; + } + return shim.cppFn("external", .{ this, global, ctx, callback }); } @@ -880,10 +899,6 @@ pub const ZigString = extern struct { return shim.cppFn("to16BitValue", .{ this, global }); } - pub fn toValueGC(this: *const ZigString, global: *JSGlobalObject) JSValue { - return shim.cppFn("toValueGC", .{ this, global }); - } - pub fn withEncoding(this: *const ZigString) ZigString { var out = this.*; out.setOutputEncoding(); @@ -919,10 +934,8 @@ pub const ZigString = extern struct { pub const Extern = [_][]const u8{ "toAtomicValue", - "toValue", "toExternalValue", "to16BitValue", - "toValueGC", "toErrorInstance", "toExternalU16", "toExternalValueWithCallback", @@ -1132,6 +1145,7 @@ pub const DOMFormData = opaque { "createFromURLQuery", }; }; + pub const FetchHeaders = opaque { pub const shim = Shimmer("WebCore", "FetchHeaders", @This()); @@ -1157,22 +1171,29 @@ pub const FetchHeaders = opaque { }); } + extern "C" fn WebCore__FetchHeaders__createFromJS(*JSC.JSGlobalObject, JSValue) ?*FetchHeaders; + /// Construct a `Headers` object from a JSValue. + /// + /// This can be: + /// - Array<[String, String]> + /// - Record. + /// + /// Throws an exception if invalid. + /// + /// If empty, returns null. pub fn createFromJS( global: *JSGlobalObject, value: JSValue, ) ?*FetchHeaders { - return shim.cppFn("createFromJS", .{ - global, - value, - }); + return WebCore__FetchHeaders__createFromJS(global, value); } - pub fn putDefault(this: *FetchHeaders, name_: []const u8, value: []const u8, global: *JSGlobalObject) void { - if (this.has(&ZigString.init(name_), global)) { + pub fn putDefault(this: *FetchHeaders, name_: HTTPHeaderName, value: []const u8, global: *JSGlobalObject) void { + if (this.fastHas(name_)) { return; } - this.put_(&ZigString.init(name_), &ZigString.init(value), global); + this.put(name_, value, global); } pub fn from( @@ -1198,11 +1219,9 @@ pub const FetchHeaders = opaque { } pub fn createFromUWS( - global: *JSGlobalObject, uws_request: *anyopaque, ) *FetchHeaders { return shim.cppFn("createFromUWS", .{ - global, uws_request, }); } @@ -1260,27 +1279,15 @@ pub const FetchHeaders = opaque { }); } - pub fn put_( - this: *FetchHeaders, - name_: *const ZigString, - value: *const ZigString, - global: *JSGlobalObject, - ) void { - return shim.cppFn("put_", .{ - this, - name_, - value, - global, - }); - } + extern fn WebCore__FetchHeaders__put(this: *FetchHeaders, name_: HTTPHeaderName, value: *const ZigString, global: *JSGlobalObject) void; pub fn put( this: *FetchHeaders, - name_: []const u8, + name_: HTTPHeaderName, value: []const u8, global: *JSGlobalObject, ) void { - this.put_(&ZigString.init(name_), &ZigString.init(value), global); + WebCore__FetchHeaders__put(this, name_, &ZigString.init(value), global); } pub fn get_( @@ -1692,6 +1699,21 @@ pub const JSUint8Array = opaque { pub fn slice(this: *JSUint8Array) []u8 { return this.ptr()[0..this.len()]; } + + extern fn JSUint8Array__fromDefaultAllocator(*JSC.JSGlobalObject, ptr: [*]u8, len: usize) JSC.JSValue; + /// *bytes* must come from bun.default_allocator + pub fn fromBytes(globalThis: *JSGlobalObject, bytes: []u8) JSC.JSValue { + return JSUint8Array__fromDefaultAllocator(globalThis, bytes.ptr, bytes.len); + } + + extern fn Bun__createUint8ArrayForCopy(*JSC.JSGlobalObject, ptr: ?*const anyopaque, len: usize, buffer: bool) JSValue; + pub fn fromBytesCopy(globalThis: *JSGlobalObject, bytes: []const u8) JSValue { + return Bun__createUint8ArrayForCopy(globalThis, bytes.ptr, bytes.len, false); + } + + pub fn createEmpty(globalThis: *JSGlobalObject) JSValue { + return Bun__createUint8ArrayForCopy(globalThis, null, 0, false); + } }; pub const JSCell = extern struct { @@ -1987,6 +2009,18 @@ pub fn PromiseCallback(comptime Type: type, comptime CallbackFunction: fn (*Type }.callback; } +pub const CommonAbortReason = enum(u8) { + Timeout = 1, + UserAbort = 2, + ConnectionClosed = 3, + + pub fn toJS(this: CommonAbortReason, global: *JSGlobalObject) JSValue { + return WebCore__CommonAbortReason__toJS(global, this); + } + + extern fn WebCore__CommonAbortReason__toJS(*JSGlobalObject, CommonAbortReason) JSValue; +}; + pub const AbortSignal = extern opaque { pub const shim = Shimmer("WebCore", "AbortSignal", @This()); const cppFn = shim.cppFn; @@ -2026,12 +2060,26 @@ pub const AbortSignal = extern opaque { return cppFn("cleanNativeBindings", .{ this, ctx }); } + extern fn WebCore__AbortSignal__signal(*AbortSignal, *JSC.JSGlobalObject, CommonAbortReason) void; + pub fn signal( this: *AbortSignal, - reason: JSValue, - ) *AbortSignal { + globalObject: *JSC.JSGlobalObject, + reason: CommonAbortReason, + ) void { bun.Analytics.Features.abort_signal += 1; - return cppFn("signal", .{ this, reason }); + return WebCore__AbortSignal__signal(this, globalObject, reason); + } + + extern fn WebCore__AbortSignal__incrementPendingActivity(*AbortSignal) void; + extern fn WebCore__AbortSignal__decrementPendingActivity(*AbortSignal) void; + + pub fn pendingActivityRef(this: *AbortSignal) void { + return WebCore__AbortSignal__incrementPendingActivity(this); + } + + pub fn pendingActivityUnref(this: *AbortSignal) void { + return WebCore__AbortSignal__decrementPendingActivity(this); } /// This function is not threadsafe. aborted is a boolean, not an atomic! @@ -2044,6 +2092,35 @@ pub const AbortSignal = extern opaque { return cppFn("abortReason", .{this}); } + extern fn WebCore__AbortSignal__reasonIfAborted(*AbortSignal, *JSC.JSGlobalObject, *u8) JSValue; + + pub const AbortReason = union(enum) { + CommonAbortReason: CommonAbortReason, + JSValue: JSValue, + + pub fn toBodyValueError(this: AbortReason, globalObject: *JSC.JSGlobalObject) JSC.WebCore.Body.Value.ValueError { + return switch (this) { + .CommonAbortReason => |reason| .{ .AbortReason = reason }, + .JSValue => |value| .{ .JSValue = JSC.Strong.create(value, globalObject) }, + }; + } + }; + + pub fn reasonIfAborted(this: *AbortSignal, global: *JSC.JSGlobalObject) ?AbortReason { + var reason: u8 = 0; + const js_reason = WebCore__AbortSignal__reasonIfAborted(this, global, &reason); + if (reason > 0) { + bun.debugAssert(js_reason == .undefined); + return AbortReason{ .CommonAbortReason = @enumFromInt(reason) }; + } + + if (js_reason == .zero) { + return null; + } + + return AbortReason{ .JSValue = js_reason }; + } + pub fn ref( this: *AbortSignal, ) *AbortSignal { @@ -2052,13 +2129,13 @@ pub const AbortSignal = extern opaque { pub fn unref( this: *AbortSignal, - ) *AbortSignal { - return cppFn("unref", .{this}); + ) void { + cppFn("unref", .{this}); } pub fn detach(this: *AbortSignal, ctx: ?*anyopaque) void { this.cleanNativeBindings(ctx); - _ = this.unref(); + this.unref(); } pub fn fromJS(value: JSValue) ?*AbortSignal { @@ -2079,15 +2156,7 @@ pub const AbortSignal = extern opaque { return WebCore__AbortSignal__new(global); } - pub fn createAbortError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue { - return cppFn("createAbortError", .{ message, code, global }); - } - - pub fn createTimeoutError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue { - return cppFn("createTimeoutError", .{ message, code, global }); - } - - pub const Extern = [_][]const u8{ "createAbortError", "createTimeoutError", "create", "ref", "unref", "signal", "abortReason", "aborted", "addListener", "fromJS", "toJS", "cleanNativeBindings" }; + pub const Extern = [_][]const u8{ "create", "ref", "unref", "signal", "abortReason", "aborted", "addListener", "fromJS", "toJS", "cleanNativeBindings" }; }; pub const JSPromise = extern struct { @@ -2099,9 +2168,9 @@ pub const JSPromise = extern struct { pub const namespace = "JSC"; pub const Status = enum(u32) { - Pending = 0, // Making this as 0, so that, we can change the status from Pending to others without masking. - Fulfilled = 1, - Rejected = 2, + pending = 0, // Making this as 0, so that, we can change the status from Pending to others without masking. + fulfilled = 1, + rejected = 2, }; pub fn Weak(comptime T: type) type { @@ -2255,10 +2324,28 @@ pub const JSPromise = extern struct { } }; + extern fn JSC__JSPromise__wrap(*JSC.JSGlobalObject, *anyopaque, *const fn (*anyopaque, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue) JSC.JSValue; + pub fn wrap( globalObject: *JSGlobalObject, - value: JSValue, + comptime Function: anytype, + args: std.meta.ArgsTuple(@TypeOf(Function)), ) JSValue { + const Args = std.meta.ArgsTuple(@TypeOf(Function)); + const Fn = Function; + const Wrapper = struct { + args: Args, + + pub fn call(this: *@This(), _: *JSC.JSGlobalObject) JSC.JSValue { + return @call(.auto, Fn, this.args); + } + }; + + var ctx = Wrapper{ .args = args }; + return JSC__JSPromise__wrap(globalObject, &ctx, @ptrCast(&Wrapper.call)); + } + + pub fn wrapValue(globalObject: *JSGlobalObject, value: JSValue) JSValue { if (value.isEmpty()) { return resolvedPromiseValue(globalObject, JSValue.jsUndefined()); } else if (value.isEmptyOrUndefinedOrNull() or !value.isCell()) { @@ -2384,6 +2471,25 @@ pub const JSPromise = extern struct { "status", // "rejectException", }; + + pub const Unwrapped = union(enum) { + pending, + fulfilled: JSValue, + rejected: JSValue, + }; + + pub const UnwrapMode = enum { mark_handled, leave_unhandled }; + + pub fn unwrap(promise: *JSPromise, vm: *VM, mode: UnwrapMode) Unwrapped { + return switch (promise.status(vm)) { + .pending => .pending, + .fulfilled => .{ .fulfilled = promise.result(vm) }, + .rejected => { + if (mode == .mark_handled) promise.setHandled(vm); + return .{ .rejected = promise.result(vm) }; + }, + }; + } }; pub const JSInternalPromise = extern struct { @@ -2407,6 +2513,17 @@ pub const JSInternalPromise = extern struct { cppFn("setHandled", .{ this, vm }); } + pub fn unwrap(promise: *JSInternalPromise, vm: *VM, mode: JSPromise.UnwrapMode) JSPromise.Unwrapped { + return switch (promise.status(vm)) { + .pending => .pending, + .fulfilled => .{ .fulfilled = promise.result(vm) }, + .rejected => { + if (mode == .mark_handled) promise.setHandled(vm); + return .{ .rejected = promise.result(vm) }; + }, + }; + } + pub fn resolvedPromise(globalThis: *JSGlobalObject, value: JSValue) *JSInternalPromise { return cppFn("resolvedPromise", .{ globalThis, value }); } @@ -2602,9 +2719,14 @@ pub const JSInternalPromise = extern struct { }; pub const AnyPromise = union(enum) { - Normal: *JSPromise, - Internal: *JSInternalPromise, + normal: *JSPromise, + internal: *JSInternalPromise, + pub fn unwrap(this: AnyPromise, vm: *VM, mode: JSPromise.UnwrapMode) JSPromise.Unwrapped { + return switch (this) { + inline else => |promise| promise.unwrap(vm, mode), + }; + } pub fn status(this: AnyPromise, vm: *VM) JSPromise.Status { return switch (this) { inline else => |promise| promise.status(vm), @@ -2631,26 +2753,52 @@ pub const AnyPromise = union(enum) { inline else => |promise| promise.resolve(globalThis, value), } } + pub fn reject(this: AnyPromise, globalThis: *JSGlobalObject, value: JSValue) void { switch (this) { inline else => |promise| promise.reject(globalThis, value), } } + pub fn rejectAsHandled(this: AnyPromise, globalThis: *JSGlobalObject, value: JSValue) void { switch (this) { inline else => |promise| promise.rejectAsHandled(globalThis, value), } } + pub fn rejectAsHandledException(this: AnyPromise, globalThis: *JSGlobalObject, value: *Exception) void { switch (this) { inline else => |promise| promise.rejectAsHandledException(globalThis, value), } } + pub fn asValue(this: AnyPromise, globalThis: *JSGlobalObject) JSValue { return switch (this) { - .Normal => |promise| promise.asValue(globalThis), - .Internal => |promise| promise.asValue(), + .normal => |promise| promise.asValue(globalThis), + .internal => |promise| promise.asValue(), + }; + } + + extern fn JSC__AnyPromise__wrap(*JSC.JSGlobalObject, JSValue, *anyopaque, *const fn (*anyopaque, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue) void; + + pub fn wrap( + this: AnyPromise, + globalObject: *JSGlobalObject, + comptime Function: anytype, + args: std.meta.ArgsTuple(@TypeOf(Function)), + ) void { + const Args = std.meta.ArgsTuple(@TypeOf(Function)); + const Fn = Function; + const Wrapper = struct { + args: Args, + + pub fn call(wrap_: *@This(), _: *JSC.JSGlobalObject) JSC.JSValue { + return @call(.auto, Fn, wrap_.args); + } }; + + var ctx = Wrapper{ .args = args }; + JSC__AnyPromise__wrap(globalObject, this.asValue(globalObject), &ctx, @ptrCast(&Wrapper.call)); } }; @@ -2702,7 +2850,7 @@ pub const JSFunction = extern struct { pub fn create( global: *JSGlobalObject, fn_name: anytype, - implementation: *const JSHostFunctionType, + comptime implementation: JSHostFunctionType, function_length: u32, options: CreateJSFunctionOptions, ) JSValue { @@ -2740,20 +2888,14 @@ pub const JSFunction = extern struct { }; }; -pub const JSGlobalObject = extern struct { - pub const shim = Shimmer("JSC", "JSGlobalObject", @This()); - bytes: shim.Bytes, - - pub const include = "JavaScriptCore/JSGlobalObject.h"; - pub const name = "JSC::JSGlobalObject"; - pub const namespace = "JSC"; - +pub const JSGlobalObject = opaque { pub fn allocator(this: *JSGlobalObject) std.mem.Allocator { return this.bunVM().allocator; } + extern fn JSGlobalObject__throwOutOfMemoryError(this: *JSGlobalObject) void; pub fn throwOutOfMemory(this: *JSGlobalObject) void { - this.throwValue(this.createErrorInstance("Out of memory", .{})); + JSGlobalObject__throwOutOfMemoryError(this); } pub fn throwTODO(this: *JSGlobalObject, msg: []const u8) void { @@ -2762,11 +2904,8 @@ pub const JSGlobalObject = extern struct { this.throwValue(err); } - extern fn JSGlobalObject__clearTerminationException(this: *JSGlobalObject) void; - extern fn JSGlobalObject__throwTerminationException(this: *JSGlobalObject) void; pub const throwTerminationException = JSGlobalObject__throwTerminationException; pub const clearTerminationException = JSGlobalObject__clearTerminationException; - extern fn JSGlobalObject__setTimeZone(this: *JSGlobalObject, timeZone: *const ZigString) bool; pub fn setTimeZone(this: *JSGlobalObject, timeZone: *const ZigString) bool { return JSGlobalObject__setTimeZone(this, timeZone); @@ -2778,7 +2917,7 @@ pub const JSGlobalObject = extern struct { pub fn throwInvalidArguments( this: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) void { const err = JSC.toInvalidArguments(fmt, args, this); @@ -2791,13 +2930,7 @@ pub const JSGlobalObject = extern struct { comptime field: []const u8, comptime typename: []const u8, ) JSC.JSValue { - return JSC.JSValue.createTypeError( - ZigString.static( - comptime std.fmt.comptimePrint("Expected {s} to be a {s} for '{s}'.", .{ field, typename, name_ }), - ), - ZigString.static("ERR_INVALID_ARG_TYPE"), - this, - ); + return this.ERR_INVALID_ARG_TYPE(comptime std.fmt.comptimePrint("Expected {s} to be a {s} for '{s}'.", .{ field, typename, name_ }), .{}).toJS(); } pub fn toJS(this: *JSC.JSGlobalObject, value: anytype, comptime lifetime: JSC.Lifetime) JSC.JSValue { @@ -2814,6 +2947,27 @@ pub const JSGlobalObject = extern struct { } pub fn throwInvalidArgumentTypeValue( + this: *JSGlobalObject, + argname: []const u8, + typename: []const u8, + value: JSValue, + ) JSValue { + const ty_str = value.jsTypeString(this).toSlice(this, bun.default_allocator); + defer ty_str.deinit(); + this.ERR_INVALID_ARG_TYPE("The \"{s}\" argument must be of type {s}. Received {}", .{ argname, typename, bun.fmt.quote(ty_str.slice()) }).throw(); + return .zero; + } + pub fn throwInvalidArgumentRangeValue( + this: *JSGlobalObject, + argname: []const u8, + typename: []const u8, + value: i64, + ) JSValue { + this.ERR_OUT_OF_RANGE("The \"{s}\" is out of range. {s}. Received {}", .{ argname, typename, value }).throw(); + return .zero; + } + + pub fn throwInvalidPropertyTypeValue( this: *JSGlobalObject, field: []const u8, typename: []const u8, @@ -2821,7 +2975,8 @@ pub const JSGlobalObject = extern struct { ) JSValue { const ty_str = value.jsTypeString(this).toSlice(this, bun.default_allocator); defer ty_str.deinit(); - return this.throwValueRet(this.createTypeErrorInstanceWithCode(.ERR_INVALID_ARG_TYPE, "The \"{s}\" argument must be of type {s}. Received {s}", .{ field, typename, ty_str.slice() })); + this.ERR_INVALID_ARG_TYPE("The \"{s}\" property must be of type {s}. Received {s}", .{ field, typename, ty_str.slice() }).throw(); + return .zero; } pub fn createNotEnoughArguments( @@ -2830,12 +2985,7 @@ pub const JSGlobalObject = extern struct { comptime expected: usize, got: usize, ) JSC.JSValue { - return JSC.toTypeErrorWithCode( - @tagName(JSC.Node.ErrorCode.ERR_MISSING_ARGS), - "Not enough arguments to '" ++ name_ ++ "'. Expected {d}, got {d}.", - .{ expected, got }, - this, - ); + return JSC.toTypeError(.ERR_MISSING_ARGS, "Not enough arguments to '" ++ name_ ++ "'. Expected {d}, got {d}.", .{ expected, got }, this); } pub fn throwNotEnoughArguments( @@ -2847,11 +2997,12 @@ pub const JSGlobalObject = extern struct { this.throwValue(this.createNotEnoughArguments(name_, expected, got)); } + extern fn JSC__JSGlobalObject__reload(JSC__JSGlobalObject__ptr: *JSGlobalObject) void; pub fn reload(this: *JSC.JSGlobalObject) void { this.vm().drainMicrotasks(); this.vm().collectAsync(); - return cppFn("reload", .{this}); + JSC__JSGlobalObject__reload(this); } pub const BunPluginTarget = enum(u8) { @@ -2883,25 +3034,7 @@ pub const JSGlobalObject = extern struct { return result; } - pub fn createSyntheticModule_(this: *JSGlobalObject, export_names: [*]const ZigString, export_len: usize, value_ptrs: [*]const JSValue, values_len: usize) void { - shim.cppFn("createSyntheticModule_", .{ this, export_names, export_len, value_ptrs, values_len }); - } - - pub fn createSyntheticModule(this: *JSGlobalObject, comptime module: anytype) void { - const names = comptime std.meta.fieldNames(@TypeOf(module)); - var export_names: [names.len]ZigString = undefined; - var export_values: [names.len]JSValue = undefined; - inline for (comptime names, 0..) |export_name, i| { - export_names[i] = ZigString.init(export_name); - const function = @field(module, export_name).@"0"; - const len = @field(module, export_name).@"1"; - export_values[i] = JSC.NewFunction(this, &export_names[i], len, function, true); - } - - createSyntheticModule_(this, &export_names, names.len, &export_values, names.len); - } - - pub fn createErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2910,24 +3043,21 @@ pub const JSGlobalObject = extern struct { writer.print(fmt, args) catch // if an exception occurs in the middle of formatting the error message, it's better to just return the formatting string than an error about an error return ZigString.static(fmt).toErrorInstance(this); - var str = ZigString.fromUTF8(buf.toOwnedSliceLeaky()); + + // Ensure we clone it. + var str = ZigString.initUTF8(buf.toOwnedSliceLeaky()); + return str.toErrorInstance(this); } else { if (comptime strings.isAllASCII(fmt)) { - return ZigString.static(fmt).toErrorInstance(this); + return String.static(fmt).toErrorInstance(this); } else { return ZigString.initUTF8(fmt).toErrorInstance(this); } } } - pub fn createErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: string, args: anytype) JSValue { - var err = this.createErrorInstance(fmt, args); - err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toValue(this)); - return err; - } - - pub fn createTypeErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createTypeErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2941,13 +3071,7 @@ pub const JSGlobalObject = extern struct { } } - pub fn createTypeErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: string, args: anytype) JSValue { - var err = this.createTypeErrorInstance(fmt, args); - err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toValue(this)); - return err; - } - - pub fn createSyntaxErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createSyntaxErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2961,7 +3085,7 @@ pub const JSGlobalObject = extern struct { } } - pub fn createRangeErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createRangeErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2975,22 +3099,14 @@ pub const JSGlobalObject = extern struct { } } - pub fn createRangeErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: string, args: anytype) JSValue { - var err = this.createRangeErrorInstance(fmt, args); - err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toValue(this)); - return err; - } - - pub fn createRangeError(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createRangeError(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { const err = createErrorInstance(this, fmt, args); - err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_OUT_OF_RANGE)).toValue(this)); + err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_OUT_OF_RANGE)).toJS(this)); return err; } - pub fn createInvalidArgs(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { - const err = createErrorInstance(this, fmt, args); - err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE)).toValue(this)); - return err; + pub fn createInvalidArgs(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { + return JSC.Error.ERR_INVALID_ARG_TYPE.fmt(this, fmt, args); } pub fn createError( @@ -3001,14 +3117,14 @@ pub const JSGlobalObject = extern struct { args: anytype, ) JSValue { const err = createErrorInstance(this, message, args); - err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toValue(this)); - err.put(this, ZigString.static("name"), ZigString.init(error_name).toValue(this)); + err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toJS(this)); + err.put(this, ZigString.static("name"), ZigString.init(error_name).toJS(this)); return err; } pub fn throw( this: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) void { const instance = this.createErrorInstance(fmt, args); @@ -3018,7 +3134,7 @@ pub const JSGlobalObject = extern struct { pub fn throwPretty( this: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) void { const instance = switch (Output.enable_ansi_colors) { @@ -3058,18 +3174,14 @@ pub const JSGlobalObject = extern struct { ); } + extern fn JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject__ptr: *JSGlobalObject, JSValue, JSValue, JSValue) void; pub fn queueMicrotaskJob( this: *JSGlobalObject, function: JSValue, first: JSValue, second: JSValue, ) void { - shim.cppFn("queueMicrotaskJob", .{ - this, - function, - first, - second, - }); + JSC__JSGlobalObject__queueMicrotaskJob(this, function, first, second); } pub fn throwValue( @@ -3091,7 +3203,7 @@ pub const JSGlobalObject = extern struct { pub fn throwError( this: *JSGlobalObject, err: anyerror, - comptime fmt: string, + comptime fmt: [:0]const u8, ) void { var str = ZigString.init(std.fmt.allocPrint(this.bunVM().allocator, "{s} " ++ fmt, .{@errorName(err)}) catch return); str.markUTF8(); @@ -3103,28 +3215,12 @@ pub const JSGlobalObject = extern struct { pub fn handleError( this: *JSGlobalObject, err: anyerror, - comptime fmt: string, + comptime fmt: [:0]const u8, ) JSValue { this.throwError(err, fmt); return JSValue.jsUndefined(); } - // pub fn createError(globalObject: *JSGlobalObject, error_type: ErrorType, message: *String) *JSObject { - // return cppFn("createError", .{ globalObject, error_type, message }); - // } - - // pub fn throwError( - // globalObject: *JSGlobalObject, - // err: *JSObject, - // ) *JSObject { - // return cppFn("throwError", .{ - // globalObject, - // err, - // }); - // } - - const cppFn = shim.cppFn; - pub fn ref(this: *JSGlobalObject) C_API.JSContextRef { return @as(C_API.JSContextRef, @ptrCast(this)); } @@ -3134,37 +3230,68 @@ pub const JSGlobalObject = extern struct { return this; } - pub fn createAggregateError(globalObject: *JSGlobalObject, errors: [*]*anyopaque, errors_len: u16, message: *const ZigString) JSValue { - return cppFn("createAggregateError", .{ globalObject, errors, errors_len, message }); + extern fn JSC__JSGlobalObject__createAggregateError(*JSGlobalObject, [*]const JSValue, usize, *const ZigString) JSValue; + pub fn createAggregateError(globalObject: *JSGlobalObject, errors: []const JSValue, message: *const ZigString) JSValue { + return JSC__JSGlobalObject__createAggregateError(globalObject, errors.ptr, errors.len, message); } + extern fn JSC__JSGlobalObject__generateHeapSnapshot(*JSGlobalObject) JSValue; pub fn generateHeapSnapshot(this: *JSGlobalObject) JSValue { - return cppFn("generateHeapSnapshot", .{this}); + return JSC__JSGlobalObject__generateHeapSnapshot(this); } - pub fn putCachedObject(this: *JSGlobalObject, key: *const ZigString, value: JSValue) JSValue { - return cppFn("putCachedObject", .{ this, key, value }); + pub fn hasException(this: *JSGlobalObject) bool { + return JSGlobalObject__hasException(this); } - pub fn getCachedObject(this: *JSGlobalObject, key: *const ZigString) JSValue { - return cppFn("getCachedObject", .{ this, key }); + pub fn clearException(this: *JSGlobalObject) void { + return JSGlobalObject__clearException(this); } - extern fn JSGlobalObject__hasException(*JSGlobalObject) bool; - pub fn hasException(this: *JSGlobalObject) bool { - return JSGlobalObject__hasException(this); + /// Clears the current exception and returns that value. Requires compile-time + /// proof of an exception via `error.JSError` + pub fn takeException(this: *JSGlobalObject, proof: bun.JSError) JSValue { + switch (proof) { + error.JSError => {}, + } + + if (bun.Environment.allow_assert) + bun.assert(this.hasException()); + + return this.tryTakeException() orelse { + bun.assert(false); + return .zero; + }; + } + + pub fn tryTakeException(this: *JSGlobalObject) ?JSValue { + const value = JSGlobalObject__tryTakeException(this); + if (value == .zero) return null; + return value; + } + + /// This is for the common scenario you are calling into JavaScript, but there is + /// no logical way to handle a thrown exception other than to treat it as unhandled. + /// + /// The pattern: + /// + /// const result = value.call(...) catch |err| + /// return global.reportActiveExceptionAsUnhandled(err); + /// + pub fn reportActiveExceptionAsUnhandled(this: *JSGlobalObject, err: bun.JSError) void { + _ = this.bunVM().uncaughtException(this, this.takeException(err), false); } pub fn vm(this: *JSGlobalObject) *VM { - return cppFn("vm", .{this}); + return JSC__JSGlobalObject__vm(this); } pub fn deleteModuleRegistryEntry(this: *JSGlobalObject, name_: *ZigString) void { - return cppFn("deleteModuleRegistryEntry", .{ this, name_ }); + return JSC__JSGlobalObject__deleteModuleRegistryEntry(this, name_); } fn bunVMUnsafe(this: *JSGlobalObject) *anyopaque { - return cppFn("bunVM", .{this}); + return JSC__JSGlobalObject__bunVM(this); } pub fn bunVM(this: *JSGlobalObject) *JSC.VirtualMachine { @@ -3185,12 +3312,9 @@ pub const JSGlobalObject = extern struct { return @as(*JSC.VirtualMachine, @ptrCast(@alignCast(this.bunVMUnsafe()))); } + extern fn JSC__JSGlobalObject__handleRejectedPromises(*JSGlobalObject) void; pub fn handleRejectedPromises(this: *JSGlobalObject) void { - return cppFn("handleRejectedPromises", .{this}); - } - - pub fn startRemoteInspector(this: *JSGlobalObject, host: [:0]const u8, port: u16) bool { - return cppFn("startRemoteInspector", .{ this, host, port }); + return JSC__JSGlobalObject__handleRejectedPromises(this); } extern fn ZigGlobalObject__readableStreamToArrayBuffer(*JSGlobalObject, JSValue) JSValue; @@ -3201,32 +3325,26 @@ pub const JSGlobalObject = extern struct { extern fn ZigGlobalObject__readableStreamToBlob(*JSGlobalObject, JSValue) JSValue; pub fn readableStreamToArrayBuffer(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToArrayBuffer(this, value); } pub fn readableStreamToBytes(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToBytes(this, value); } pub fn readableStreamToText(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToText(this, value); } pub fn readableStreamToJSON(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToJSON(this, value); } pub fn readableStreamToBlob(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToBlob(this, value); } pub fn readableStreamToFormData(this: *JSGlobalObject, value: JSValue, content_type: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToFormData(this, value, content_type); } @@ -3234,27 +3352,136 @@ pub const JSGlobalObject = extern struct { if (bun.Environment.allow_assert) this.bunVM().assertOnJSThread(); } - pub const Extern = [_][]const u8{ - "reload", - "bunVM", - "putCachedObject", - "getCachedObject", - "createAggregateError", + // returns false if it throws + pub fn validateObject( + this: *JSGlobalObject, + comptime arg_name: [:0]const u8, + value: JSValue, + opts: struct { + allowArray: bool = false, + allowFunction: bool = false, + nullable: bool = false, + }, + ) bool { + if ((!opts.nullable and value.isNull()) or + (!opts.allowArray and value.isArray()) or + (!value.isObject() and (!opts.allowFunction or !value.isFunction()))) + { + this.throwValue(this.ERR_INVALID_ARG_TYPE_static( + ZigString.static(arg_name), + ZigString.static("object"), + value, + )); + return false; + } + return true; + } - "deleteModuleRegistryEntry", + extern fn Bun__ERR_INVALID_ARG_TYPE_static(*JSGlobalObject, *const ZigString, *const ZigString, JSValue) JSValue; + /// Caller asserts 'arg_name' and 'etype' are utf-8 literals. + pub fn ERR_INVALID_ARG_TYPE_static(this: *JSGlobalObject, arg_name: *const ZigString, etype: *const ZigString, atype: JSValue) JSValue { + return Bun__ERR_INVALID_ARG_TYPE_static(this, arg_name, etype, atype); + } - "vm", - "generateHeapSnapshot", - "startRemoteInspector", - "handleRejectedPromises", - "createSyntheticModule_", - "queueMicrotaskJob", - // "createError", - // "throwError", + pub fn throwRangeError(this: *JSGlobalObject, value: anytype, options: bun.fmt.OutOfRangeOptions) void { + // This works around a Zig compiler bug + // when using this.ERR_OUT_OF_RANGE. + JSC.Error.ERR_OUT_OF_RANGE.throw(this, "{}", .{bun.fmt.outOfRange(value, options)}); + } + + pub const IntegerRange = struct { + min: comptime_int = JSC.MIN_SAFE_INTEGER, + max: comptime_int = JSC.MAX_SAFE_INTEGER, + field_name: []const u8 = "", + always_allow_zero: bool = false, }; + + pub fn validateIntegerRange(this: *JSGlobalObject, value: JSValue, comptime T: type, default: T, comptime range: IntegerRange) ?T { + if (value == .undefined or value == .zero) { + return default; + } + + const min_t = comptime @max(range.min, std.math.minInt(T), JSC.MIN_SAFE_INTEGER); + const max_t = comptime @min(range.max, std.math.maxInt(T), JSC.MAX_SAFE_INTEGER); + + comptime { + if (min_t > max_t) { + @compileError("max must be less than min"); + } + + if (max_t < min_t) { + @compileError("max must be less than min"); + } + } + const field_name = comptime range.field_name; + const always_allow_zero = comptime range.always_allow_zero; + const min = range.min; + const max = range.max; + + if (value.isInt32()) { + const int = value.toInt32(); + if (always_allow_zero and int == 0) { + return 0; + } + if (int < min_t or int > max_t) { + this.throwRangeError(int, .{ .field_name = field_name, .min = min, .max = max }); + return null; + } + return @intCast(int); + } + + if (!value.isNumber()) { + _ = this.throwInvalidPropertyTypeValue(field_name, "number", value); + return null; + } + const f64_val = value.asNumber(); + if (always_allow_zero and f64_val == 0) { + return 0; + } + + if (std.math.isNan(f64_val)) { + // node treats NaN as default + return default; + } + if (@floor(f64_val) != f64_val) { + _ = this.throwInvalidPropertyTypeValue(field_name, "integer", value); + return null; + } + if (f64_val < min_t or f64_val > max_t) { + this.throwRangeError(f64_val, .{ .field_name = comptime field_name, .min = min, .max = max }); + return null; + } + + return @intFromFloat(f64_val); + } + + pub fn getInteger(this: *JSGlobalObject, obj: JSValue, comptime T: type, default: T, comptime range: IntegerRange) ?T { + if (obj.get(this, range.field_name)) |val| { + return this.validateIntegerRange(val, T, default, range); + } + if (this.hasException()) return null; + return default; + } + + extern fn Bun__ERR_MISSING_ARGS_static(*JSGlobalObject, *const ZigString, ?*const ZigString, ?*const ZigString) JSValue; + pub fn ERR_MISSING_ARGS_static(this: *JSGlobalObject, arg1: *const ZigString, arg2: ?*const ZigString, arg3: ?*const ZigString) JSValue { + return Bun__ERR_MISSING_ARGS_static(this, arg1, arg2, arg3); + } + + pub usingnamespace @import("ErrorCode").JSGlobalObjectExtensions; + + extern fn JSC__JSGlobalObject__bunVM(*JSGlobalObject) *VM; + extern fn JSC__JSGlobalObject__vm(*JSGlobalObject) *VM; + extern fn JSC__JSGlobalObject__deleteModuleRegistryEntry(*JSGlobalObject, *const ZigString) void; + extern fn JSGlobalObject__clearException(*JSGlobalObject) void; + extern fn JSGlobalObject__clearTerminationException(this: *JSGlobalObject) void; + extern fn JSGlobalObject__hasException(*JSGlobalObject) bool; + extern fn JSGlobalObject__setTimeZone(this: *JSGlobalObject, timeZone: *const ZigString) bool; + extern fn JSGlobalObject__tryTakeException(*JSGlobalObject) JSValue; + extern fn JSGlobalObject__throwTerminationException(this: *JSGlobalObject) void; }; -pub const JSNativeFn = *const fn (*JSGlobalObject, *CallFrame) callconv(.C) JSValue; +pub const JSNativeFn = JSHostFunctionPtr; pub const JSArrayIterator = struct { i: u32 = 0, @@ -3335,6 +3562,7 @@ pub const JSMap = opaque { }; pub const JSValueReprInt = i64; + pub const JSValue = enum(JSValueReprInt) { zero = 0, undefined = 0xa, @@ -3354,117 +3582,89 @@ pub const JSValue = enum(JSValueReprInt) { pub const name = "JSC::JSValue"; pub const namespace = "JSC"; pub const JSType = enum(u8) { - // The Cell value must come before any JS that is a JSCell. - Cell, - Structure, - String, - HeapBigInt, - Symbol, - - GetterSetter, - CustomGetterSetter, - /// For 32-bit architectures, this wraps a 64-bit JSValue - APIValueWrapper, - - NativeExecutable, - - ProgramExecutable, - ModuleProgramExecutable, - EvalExecutable, - FunctionExecutable, - - UnlinkedFunctionExecutable, - - UnlinkedProgramCodeBlock, - UnlinkedModuleProgramCodeBlock, - UnlinkedEvalCodeBlock, - UnlinkedFunctionCodeBlock, - - CodeBlock, - - JSImmutableButterfly, - JSSourceCode, - JSScriptFetcher, - JSScriptFetchParameters, - - // The Object value must come before any JS that is a subclass of JSObject. - Object, - FinalObject, - JSCallee, - JSFunction, - InternalFunction, - NullSetterFunction, - BooleanObject, - NumberObject, - ErrorInstance, - GlobalProxy, - DirectArguments, - ScopedArguments, - ClonedArguments, - - // Start JSArray s. - Array, - DerivedArray, - // End JSArray s. - - ArrayBuffer, - - // Start JSArrayBufferView s. Keep in sync with the order of FOR_EACH_D_ARRAY__EXCLUDING_DATA_VIEW. - Int8Array, - Uint8Array, - Uint8ClampedArray, - Int16Array, - Uint16Array, - Int32Array, - Uint32Array, - Float32Array, - Float64Array, - BigInt64Array, - BigUint64Array, - DataView, - // End JSArrayBufferView s. - - // JSScope <- JSWithScope - // <- StrictEvalActivation - // <- JSSymbolTableObject <- JSLexicalEnvironment <- JSModuleEnvironment - // <- JSSegmentedVariableObject <- JSGlobalLexicalEnvironment - // <- JSGlobalObject - // Start JSScope s. - // Start environment record s. - GlobalObject, - GlobalLexicalEnvironment, - LexicalEnvironment, - ModuleEnvironment, - StrictEvalActivation, - // End environment record s. - WithScope, - // End JSScope s. - - ModuleNamespaceObject, - ShadowRealm, - RegExpObject, - JSDate, - ProxyObject, - JSGenerator, - JSAsyncGenerator, - JSArrayIterator, - JSMapIterator, - JSSetIterator, - JSStringIterator, - JSPromise, - JSMap, - JSSet, - JSWeakMap, - JSWeakSet, - WebAssemblyModule, - WebAssemblyInstance, - WebAssemblyGCObject, - // Start StringObject s. - StringObject, - DerivedStringObject, - // End StringObject s. - - InternalFieldTuple, + Cell = 0, + Structure = 1, + String = 2, + HeapBigInt = 3, + Symbol = 4, + GetterSetter = 5, + CustomGetterSetter = 6, + APIValueWrapper = 7, + NativeExecutable = 8, + ProgramExecutable = 9, + ModuleProgramExecutable = 10, + EvalExecutable = 11, + FunctionExecutable = 12, + UnlinkedFunctionExecutable = 13, + UnlinkedProgramCodeBlock = 14, + UnlinkedModuleProgramCodeBlock = 15, + UnlinkedEvalCodeBlock = 16, + UnlinkedFunctionCodeBlock = 17, + CodeBlock = 18, + JSImmutableButterfly = 19, + JSSourceCode = 20, + JSScriptFetcher = 21, + JSScriptFetchParameters = 22, + Object = 23, + FinalObject = 24, + JSCallee = 25, + JSFunction = 26, + InternalFunction = 27, + NullSetterFunction = 28, + BooleanObject = 29, + NumberObject = 30, + ErrorInstance = 31, + GlobalProxy = 32, + DirectArguments = 33, + ScopedArguments = 34, + ClonedArguments = 35, + Array = 36, + DerivedArray = 37, + ArrayBuffer = 38, + Int8Array = 39, + Uint8Array = 40, + Uint8ClampedArray = 41, + Int16Array = 42, + Uint16Array = 43, + Int32Array = 44, + Uint32Array = 45, + Float16Array = 46, + Float32Array = 47, + Float64Array = 48, + BigInt64Array = 49, + BigUint64Array = 50, + DataView = 51, + GlobalObject = 52, + GlobalLexicalEnvironment = 53, + LexicalEnvironment = 54, + ModuleEnvironment = 55, + StrictEvalActivation = 56, + WithScope = 57, + ModuleNamespaceObject = 58, + ShadowRealm = 59, + RegExpObject = 60, + JSDate = 61, + ProxyObject = 62, + JSGenerator = 63, + JSAsyncGenerator = 64, + JSArrayIterator = 65, + JSIterator = 66, + JSMapIterator = 67, + JSSetIterator = 68, + JSStringIterator = 69, + JSWrapForValidIterator = 70, + JSRegExpStringIterator = 71, + JSPromise = 72, + JSMap = 73, + JSSet = 74, + JSWeakMap = 75, + JSWeakSet = 76, + WebAssemblyModule = 77, + WebAssemblyInstance = 78, + WebAssemblyGCObject = 79, + StringObject = 80, + DerivedStringObject = 81, + InternalFieldTuple = 82, MaxJS = 0b11111111, Event = 0b11101111, @@ -3491,6 +3691,7 @@ pub const JSValue = enum(JSValueReprInt) { .Event, .FinalObject, .Float32Array, + .Float16Array, .Float64Array, .GlobalObject, .Int16Array, @@ -3547,6 +3748,7 @@ pub const JSValue = enum(JSValueReprInt) { .BigInt64Array, .BigUint64Array, .Float32Array, + .Float16Array, .Float64Array, .Int16Array, .Int32Array, @@ -3642,6 +3844,7 @@ pub const JSValue = enum(JSValueReprInt) { .BigInt64Array, .BigUint64Array, .Float32Array, + .Float16Array, .Float64Array, .Int16Array, .Int32Array, @@ -3683,6 +3886,7 @@ pub const JSValue = enum(JSValueReprInt) { .BigInt64Array, .BigUint64Array, .Float32Array, + .Float16Array, .Float64Array, .Int16Array, .Int32Array, @@ -3797,6 +4001,10 @@ pub const JSValue = enum(JSValueReprInt) { /// This does not call [Symbol.toPrimitive] or [Symbol.toStringTag]. /// This is only safe when you don't want to do conversions across non-primitive types. pub fn to(this: JSValue, comptime T: type) T { + if (@typeInfo(T) == .Enum) { + const Int = @typeInfo(T).Enum.tag_type; + return @enumFromInt(this.to(Int)); + } return switch (comptime T) { u32 => toU32(this), u16 => toU16(this), @@ -3823,46 +4031,38 @@ pub const JSValue = enum(JSValueReprInt) { return cppFn("isInstanceOf", .{ this, global, constructor }); } - pub fn call(this: JSValue, globalThis: *JSGlobalObject, args: []const JSC.JSValue) JSC.JSValue { - return callWithThis(this, globalThis, JSC.JSValue.jsUndefined(), args); + pub fn callWithGlobalThis(this: JSValue, globalThis: *JSGlobalObject, args: []const JSC.JSValue) !JSC.JSValue { + return this.call(globalThis, globalThis.toJSValue(), args); } - pub fn callWithGlobalThis(this: JSValue, globalThis: *JSGlobalObject, args: []const JSC.JSValue) JSC.JSValue { - JSC.markBinding(@src()); - if (comptime bun.Environment.isDebug) { - const loop = JSC.VirtualMachine.get().eventLoop(); - loop.debug.js_call_count_outside_tick_queue += @as(usize, @intFromBool(!loop.debug.is_inside_tick_queue)); - if (loop.debug.track_last_fn_name and !loop.debug.is_inside_tick_queue) { - loop.debug.last_fn_name.deref(); - loop.debug.last_fn_name = this.getName(globalThis); - } - } - return JSC.C.JSObjectCallAsFunctionReturnValue( - globalThis, - this, - globalThis.toJSValue(), - args.len, - @as(?[*]const JSC.C.JSValueRef, @ptrCast(args.ptr)), - ); - } + pub extern "c" fn Bun__JSValue__call( + ctx: *JSGlobalObject, + object: JSValue, + thisObject: JSValue, + argumentCount: usize, + arguments: [*]const JSValue, + ) JSValue; - pub fn callWithThis(this: JSValue, globalThis: *JSGlobalObject, thisValue: JSC.JSValue, args: []const JSC.JSValue) JSC.JSValue { + pub fn call(function: JSValue, global: *JSGlobalObject, thisValue: JSC.JSValue, args: []const JSC.JSValue) bun.JSError!JSC.JSValue { JSC.markBinding(@src()); if (comptime bun.Environment.isDebug) { const loop = JSC.VirtualMachine.get().eventLoop(); loop.debug.js_call_count_outside_tick_queue += @as(usize, @intFromBool(!loop.debug.is_inside_tick_queue)); if (loop.debug.track_last_fn_name and !loop.debug.is_inside_tick_queue) { loop.debug.last_fn_name.deref(); - loop.debug.last_fn_name = this.getName(globalThis); + loop.debug.last_fn_name = function.getName(global); } } - return JSC.C.JSObjectCallAsFunctionReturnValue( - globalThis, - this, + + const value = Bun__JSValue__call( + global, + function, thisValue, args.len, - @as(?[*]const JSC.C.JSValueRef, @ptrCast(args.ptr)), + args.ptr, ); + if (value == .zero) return error.JSError; + return value; } /// The value cannot be empty. Check `!this.isEmpty()` before calling this function @@ -3929,6 +4129,8 @@ pub const JSValue = enum(JSValueReprInt) { putZigString(value, global, key, result); } else if (Elem == bun.String) { putBunString(value, global, key, result); + } else if (std.meta.Elem(Key) == u8) { + putZigString(value, global, &ZigString.init(key), result); } else { @compileError("Unsupported key type in put(). Expected ZigString or bun.String, got " ++ @typeName(Elem)); } @@ -3958,12 +4160,22 @@ pub const JSValue = enum(JSValueReprInt) { cppFn("push", .{ value, globalObject, out }); } + extern fn JSC__JSValue__toISOString(*JSC.JSGlobalObject, JSC.JSValue, *[28]u8) c_int; + pub fn toISOString(this: JSValue, globalObject: *JSC.JSGlobalObject, buf: *[28]u8) []const u8 { + const count = JSC__JSValue__toISOString(globalObject, this, buf); + if (count < 0) { + return ""; + } + + return buf[0..@as(usize, @intCast(count))]; + } + /// Return the pointer to the wrapped object only if it is a direct instance of the type. /// If the object does not match the type, return null. /// If the object is a subclass of the type or has mutated the structure, return null. /// Note: this may return null for direct instances of the type if the user adds properties to the object. pub fn asDirect(value: JSValue, comptime ZigType: type) ?*ZigType { - bun.assert(value.isCell()); // you must have already checked this. + bun.debugAssert(value.isCell()); // you must have already checked this. return ZigType.fromJSDirect(value); } @@ -4011,6 +4223,12 @@ pub const JSValue = enum(JSValueReprInt) { return JSC.GetJSPrivateData(ZigType, value.asObjectRef()); } + extern fn JSC__JSValue__dateInstanceFromNullTerminatedString(*JSGlobalObject, [*:0]const u8) JSValue; + pub fn fromDateString(globalObject: *JSGlobalObject, str: [*:0]const u8) JSValue { + JSC.markBinding(@src()); + return JSC__JSValue__dateInstanceFromNullTerminatedString(globalObject, str); + } + extern fn JSBuffer__isBuffer(*JSGlobalObject, JSValue) bool; pub fn isBuffer(value: JSValue, global: *JSGlobalObject) bool { JSC.markBinding(@src()); @@ -4187,6 +4405,9 @@ pub const JSValue = enum(JSValueReprInt) { extern fn JSBuffer__bufferFromPointerAndLengthAndDeinit(*JSGlobalObject, [*]u8, usize, ?*anyopaque, JSC.C.JSTypedArrayBytesDeallocator) JSValue; pub fn jsNumberWithType(comptime Number: type, number: Number) JSValue { + if (@typeInfo(Number) == .Enum) { + return jsNumberWithType(@typeInfo(Number).Enum.tag_type, @intFromEnum(number)); + } return switch (comptime Number) { JSValue => number, u0 => jsNumberFromInt32(0), @@ -4230,12 +4451,12 @@ pub const JSValue = enum(JSValueReprInt) { if (value.isEmptyOrUndefinedOrNull()) return null; if (value.asInternalPromise()) |promise| { return AnyPromise{ - .Internal = promise, + .internal = promise, }; } if (value.asPromise()) |promise| { return AnyPromise{ - .Normal = promise, + .normal = promise, }; } return null; @@ -4380,7 +4601,7 @@ pub const JSValue = enum(JSValueReprInt) { } pub fn jsNumberFromInt64(i: i64) JSValue { - if (i <= std.math.maxInt(i32)) { + if (i <= std.math.maxInt(i32) and i >= std.math.minInt(i32)) { return jsNumberFromInt32(@as(i32, @intCast(i))); } @@ -4458,10 +4679,10 @@ pub const JSValue = enum(JSValueReprInt) { } pub inline fn isUndefined(this: JSValue) bool { - return @intFromEnum(this) == 0xa; + return this == .undefined; } pub inline fn isNull(this: JSValue) bool { - return @intFromEnum(this) == 0x2; + return this == .null; } pub inline fn isEmptyOrUndefinedOrNull(this: JSValue) bool { return switch (@intFromEnum(this)) { @@ -4597,6 +4818,12 @@ pub const JSValue = enum(JSValueReprInt) { pub inline fn isObject(this: JSValue) bool { return this.isCell() and this.jsType().isObject(); } + pub inline fn isArray(this: JSValue) bool { + return this.isCell() and this.jsType().isArray(); + } + pub inline fn isFunction(this: JSValue) bool { + return this.isCell() and this.jsType().isFunction(); + } pub fn isObjectEmpty(this: JSValue, globalObject: *JSGlobalObject) bool { const type_of_value = this.jsType(); // https://github.com/jestjs/jest/blob/main/packages/jest-get-type/src/index.ts#L26 @@ -4643,6 +4870,10 @@ pub const JSValue = enum(JSValueReprInt) { }; } + pub fn toJSString(globalObject: *JSC.JSGlobalObject, slice_: []const u8) JSC.JSValue { + return JSC.ZigString.init(slice_).withEncoding().toJS(globalObject); + } + pub fn asCell(this: JSValue) *JSCell { return cppFn("asCell", .{this}); } @@ -4776,6 +5007,14 @@ pub const JSValue = enum(JSValueReprInt) { return str.toUTF8(bun.default_allocator); } + /// Call `toString()` on the JSValue and clone the result. + /// On exception, this returns null. + pub fn toSliceOrNullWithAllocator(this: JSValue, globalThis: *JSGlobalObject, allocator: std.mem.Allocator) ?ZigString.Slice { + const str = bun.String.tryFromJS(this, globalThis) orelse return null; + defer str.deref(); + return str.toUTF8(allocator); + } + /// Call `toString()` on the JSValue and clone the result. /// On exception or out of memory, this returns null. /// @@ -4784,6 +5023,15 @@ pub const JSValue = enum(JSValueReprInt) { return this.toSliceCloneWithAllocator(globalThis, bun.default_allocator); } + /// Call `toString()` on the JSValue and clone the result. + /// On exception or out of memory, this returns null. + /// + /// Remember that `Symbol` throws an exception when you call `toString()`. + pub fn toSliceCloneZ(this: JSValue, globalThis: *JSGlobalObject) ?[:0]u8 { + var str = bun.String.tryFromJS(this, globalThis) orelse return null; + return str.toOwnedSliceZ(bun.default_allocator) catch return null; + } + /// On exception or out of memory, this returns null, to make exception checks clearer. pub fn toSliceCloneWithAllocator( this: JSValue, @@ -4828,6 +5076,9 @@ pub const JSValue = enum(JSValueReprInt) { path, stream, asyncIterator, + name, + message, + @"error", pub fn has(property: []const u8) bool { return bun.ComptimeEnumMap(BuiltinName).has(property); @@ -4912,6 +5163,8 @@ pub const JSValue = enum(JSValueReprInt) { return zig_str; } + /// Equivalent to `obj.property` in JavaScript. + /// Reminder: `undefined` is a value! pub fn get(this: JSValue, global: *JSGlobalObject, property: []const u8) ?JSValue { if (comptime bun.Environment.isDebug) { if (BuiltinName.has(property)) { @@ -4920,7 +5173,7 @@ pub const JSValue = enum(JSValueReprInt) { } const value = getIfPropertyExistsImpl(this, global, property.ptr, @as(u32, @intCast(property.len))); - return if (@intFromEnum(value) != 0) value else return null; + return if (value.isEmpty()) null else value; } extern fn JSC__JSValue__getIfPropertyExistsImplString(value: JSValue, globalObject: *JSGlobalObject, propertyName: [*c]const bun.String) JSValue; @@ -5210,7 +5463,6 @@ pub const JSValue = enum(JSValueReprInt) { pub fn toFmt( this: JSValue, - global: *JSGlobalObject, formatter: *Exports.ConsoleObject.Formatter, ) Exports.ConsoleObject.Formatter.ZigFormatter { formatter.remaining_values = &[_]JSValue{}; @@ -5222,7 +5474,6 @@ pub const JSValue = enum(JSValueReprInt) { return Exports.ConsoleObject.Formatter.ZigFormatter{ .formatter = formatter, .value = this, - .global = global, }; } @@ -5606,7 +5857,7 @@ pub const JSValue = enum(JSValueReprInt) { "jestDeepMatch", }; - // For any callback JSValue created in JS that you will not call *immediatly*, you must wrap it + // For any callback JSValue created in JS that you will not call *immediately*, you must wrap it // in an AsyncContextFrame with this function. This allows AsyncLocalStorage to work by // snapshotting it's state and restoring it when called. // - If there is no current context, this returns the callback as-is. @@ -5618,11 +5869,11 @@ pub const JSValue = enum(JSValueReprInt) { return AsyncContextFrame__withAsyncContextIfNeeded(global, this); } - extern "c" fn Bun__JSValue__deserialize(global: *JSGlobalObject, data: [*]const u8, len: isize) JSValue; + extern "c" fn Bun__JSValue__deserialize(global: *JSGlobalObject, data: [*]const u8, len: usize) JSValue; /// Deserializes a JSValue from a serialized buffer. Zig version of `import('bun:jsc').deserialize` pub inline fn deserialize(bytes: []const u8, global: *JSGlobalObject) JSValue { - return Bun__JSValue__deserialize(global, bytes.ptr, @intCast(bytes.len)); + return Bun__JSValue__deserialize(global, bytes.ptr, bytes.len); } extern fn Bun__serializeJSValue(global: *JSC.JSGlobalObject, value: JSValue) SerializedScriptValue.External; @@ -5748,10 +5999,27 @@ pub const VM = extern struct { return cppFn("isJITEnabled", .{}); } + /// deprecated in favor of getAPILock to avoid an annoying callback wrapper pub fn holdAPILock(this: *VM, ctx: ?*anyopaque, callback: *const fn (ctx: ?*anyopaque) callconv(.C) void) void { cppFn("holdAPILock", .{ this, ctx, callback }); } + extern fn JSC__VM__getAPILock(vm: *VM) void; + extern fn JSC__VM__releaseAPILock(vm: *VM) void; + + /// See `JSLock.h` in WebKit for more detail on how the API lock prevents races. + pub fn getAPILock(vm: *VM) Lock { + JSC__VM__getAPILock(vm); + return .{ .vm = vm }; + } + + pub const Lock = struct { + vm: *VM, + pub fn release(lock: Lock) void { + JSC__VM__releaseAPILock(lock.vm); + } + }; + pub fn deferGC(this: *VM, ctx: ?*anyopaque, callback: *const fn (ctx: ?*anyopaque) callconv(.C) void) void { cppFn("deferGC", .{ this, ctx, callback }); } @@ -5990,6 +6258,34 @@ pub const CallFrame = opaque { pub const name = "JSC::CallFrame"; + pub fn format(frame: *CallFrame, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + const args = frame.argumentsPtr()[0..frame.argumentsCount()]; + + for (args[0..@min(args.len, 4)], 0..) |arg, i| { + if (i != 0) { + try writer.writeAll(", "); + } + switch (arg) { + .zero => try writer.writeAll(""), + .undefined => try writer.writeAll("undefined"), + .null => try writer.writeAll("null"), + .true => try writer.writeAll("true"), + .false => try writer.writeAll("false"), + else => { + if (arg.isNumber()) { + try writer.writeAll("number"); + } else { + try writer.writeAll(@tagName(arg.jsType())); + } + }, + } + } + + if (args.len > 4) { + try writer.print(", ... {d} more", .{args.len - 4}); + } + } + pub fn argumentsPtr(self: *const CallFrame) [*]const JSC.JSValue { return @as([*]align(alignment) const JSC.JSValue, @ptrCast(@alignCast(self))) + Sizes.Bun_CallFrame__firstArgument; } @@ -6004,7 +6300,7 @@ pub const CallFrame = opaque { len: usize, pub inline fn init(comptime i: usize, ptr: [*]const JSC.JSValue) @This() { var args: [max]JSC.JSValue = std.mem.zeroes([max]JSC.JSValue); - args[0..comptime i].* = ptr[0..i].*; + args[0..i].* = ptr[0..i].*; return @This(){ .ptr = args, @@ -6023,7 +6319,7 @@ pub const CallFrame = opaque { const ptr = self.argumentsPtr(); return switch (@as(u4, @min(len, max))) { 0 => .{ .ptr = undefined, .len = 0 }, - inline 1...8 => |count| Arguments(max).init(comptime @min(count, max), ptr), + inline 1...9 => |count| Arguments(max).init(comptime @min(count, max), ptr), else => unreachable, }; } @@ -6074,8 +6370,44 @@ pub const EncodedJSValue = extern union { asPtr: ?*anyopaque, asDouble: f64, }; -pub const JSHostFunctionType = fn (*JSGlobalObject, *CallFrame) callconv(.C) JSValue; +pub const JSHostFunctionType = fn (*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; +pub const JSHostFunctionTypeWithCCallConvForAssertions = fn (*JSGlobalObject, *CallFrame) callconv(.C) JSValue; pub const JSHostFunctionPtr = *const JSHostFunctionType; +pub fn toJSHostFunction(comptime Function: anytype) JSC.JSHostFunctionType { + if (comptime @TypeOf(Function) == JSHostFunctionType) { + return Function; + } + + if (@TypeOf(Function) == fn (*JSGlobalObject, *CallFrame) JSValue) { + // These may coerce to both, but we want to force it to be this kind. + } else if (@TypeOf(Function) == *const fn (*JSGlobalObject, *CallFrame) JSValue) { + @compileLog(Function, "use JSC.toJSHostFunction(Function) instead of JSC.toJSHostFunction(&Function)"); + } + + return struct { + pub fn function( + globalThis: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(JSC.conv) JSC.JSValue { + comptime { + const Fn = @TypeOf(Function); + var FnTypeInfo = @typeInfo(Fn); + if (FnTypeInfo == .Pointer) { + FnTypeInfo = @typeInfo(std.meta.Child(Fn)); + } + + if (bun.Environment.isWindows) { + if (FnTypeInfo.Fn.calling_convention == .C) { + @compileLog(Function, "use callconv(JSC.conv) instead of callconv(.C), or don't set a callconv on the function."); + } + } + } + + return @call(.always_inline, Function, .{ globalThis, callframe }); + } + }.function; +} + const DeinitFunction = *const fn (ctx: *anyopaque, buffer: [*]u8, len: usize) callconv(.C) void; pub const JSArray = opaque { @@ -6086,6 +6418,12 @@ pub const JSArray = opaque { return JSArray__constructArray(global, items.ptr, items.len); } + extern fn JSArray__constructEmptyArray(*JSGlobalObject, usize) JSValue; + + pub fn createEmpty(global: *JSGlobalObject, len: usize) JSValue { + return JSArray__constructEmptyArray(global, len); + } + pub fn iterator(array: *JSArray, global: *JSGlobalObject) JSArrayIterator { return JSValue.fromCell(array).arrayIterator(global); } @@ -6096,7 +6434,7 @@ const private = struct { *JSGlobalObject, ?*const ZigString, argCount: u32, - function: *const anyopaque, + function: JSHostFunctionPtr, strong: bool, data: *anyopaque, ) JSValue; @@ -6104,7 +6442,7 @@ const private = struct { globalObject: *JSGlobalObject, symbolName: ?*const ZigString, argCount: u32, - functionPointer: *const anyopaque, + functionPointer: JSHostFunctionPtr, strong: bool, ) *anyopaque; @@ -6126,28 +6464,23 @@ const private = struct { pub extern fn Bun__FFIFunction_setDataPtr(JSValue, ?*anyopaque) void; }; -pub fn NewFunctionPtr(globalObject: *JSGlobalObject, symbolName: ?*const ZigString, argCount: u32, comptime functionPointer: anytype, strong: bool) *anyopaque { - JSC.markBinding(@src()); - return private.Bun__CreateFFIFunction(globalObject, symbolName, argCount, @as(*const anyopaque, @ptrCast(&functionPointer)), strong); -} - pub fn NewFunction( globalObject: *JSGlobalObject, symbolName: ?*const ZigString, argCount: u32, - functionPointer: JSHostFunctionPtr, + comptime functionPointer: anytype, strong: bool, ) JSValue { - return NewRuntimeFunction(globalObject, symbolName, argCount, functionPointer, strong, false); + return NewRuntimeFunction(globalObject, symbolName, argCount, toJSHostFunction(functionPointer), strong, false); } pub fn createCallback( globalObject: *JSGlobalObject, symbolName: ?*const ZigString, argCount: u32, - functionPointer: JSHostFunctionPtr, + comptime functionPointer: anytype, ) JSValue { - return NewRuntimeFunction(globalObject, symbolName, argCount, functionPointer, false, false); + return NewRuntimeFunction(globalObject, symbolName, argCount, toJSHostFunction(functionPointer), false, false); } pub fn NewRuntimeFunction( @@ -6185,7 +6518,7 @@ pub fn NewFunctionWithData( globalObject, symbolName, argCount, - @as(*const anyopaque, @ptrCast(&functionPointer)), + toJSHostFunction(functionPointer), strong, data, ); @@ -6373,43 +6706,6 @@ pub const WTF = struct { } }; -pub const Callback = struct { - // zig: Value, -}; - -pub fn Thenable(comptime name: []const u8, comptime Then: type, comptime onResolve: fn (*Then, globalThis: *JSGlobalObject, result: JSValue) void, comptime onReject: fn (*Then, globalThis: *JSGlobalObject, result: JSValue) void) type { - return struct { - pub fn resolve( - globalThis: [*c]JSGlobalObject, - callframe: ?*JSC.CallFrame, - ) callconv(.C) void { - @setRuntimeSafety(false); - const args_list = callframe.?.arguments(8); - onResolve(@as(*Then, @ptrCast(@alignCast(args_list.ptr[args_list.len - 1].asEncoded().asPtr))), globalThis, args_list.ptr[0]); - } - - pub fn reject( - globalThis: [*c]JSGlobalObject, - callframe: ?*JSC.CallFrame, - ) callconv(.C) void { - @setRuntimeSafety(false); - const args_list = callframe.?.arguments(8); - onReject(@as(*Then, @ptrCast(@alignCast(args_list.ptr[args_list.len - 1].asEncoded().asPtr))), globalThis, args_list.ptr[0]); - } - - pub fn then(ctx: *Then, this: JSValue, globalThis: *JSGlobalObject) void { - this._then(globalThis, ctx, resolve, reject); - } - - comptime { - if (!JSC.is_bindgen) { - @export(resolve, name ++ "__resolve"); - @export(reject, name ++ "__reject"); - } - } - }; -} - pub usingnamespace @import("./JSPropertyIterator.zig"); // DOMCall Fields @@ -6432,8 +6728,8 @@ pub const DOMCalls = &.{ Bun.FFIObject.Reader.DOMCalls, }; -extern "c" fn JSCInitialize(env: [*]const [*:0]u8, count: usize, cb: *const fn ([*]const u8, len: usize) callconv(.C) void) void; -pub fn initialize() void { +extern "c" fn JSCInitialize(env: [*]const [*:0]u8, count: usize, cb: *const fn ([*]const u8, len: usize) callconv(.C) void, eval_mode: bool) void; +pub fn initialize(eval_mode: bool) void { JSC.markBinding(@src()); bun.analytics.Features.jsc += 1; JSCInitialize( @@ -6459,6 +6755,7 @@ pub fn initialize() void { bun.Global.exit(1); } }.callback, + eval_mode, ); } @@ -6469,7 +6766,35 @@ pub const ScriptExecutionStatus = enum(i32) { }; comptime { - // this file is gennerated, but cant be placed in the build/codegen folder + // this file is gennerated, but cant be placed in the build/debug/codegen folder // because zig will complain about outside-of-module stuff _ = @import("./GeneratedJS2Native.zig"); } + +// Error's cannot be created off of the main thread. So we use this to store the +// information until its ready to be materialized later. +pub const DeferredError = struct { + kind: Kind, + code: JSC.Node.ErrorCode, + msg: bun.String, + + pub const Kind = enum { plainerror, typeerror, rangeerror }; + + pub fn from(kind: Kind, code: JSC.Node.ErrorCode, comptime fmt: [:0]const u8, args: anytype) DeferredError { + return .{ + .kind = kind, + .code = code, + .msg = bun.String.createFormat(fmt, args) catch bun.outOfMemory(), + }; + } + + pub fn toError(this: *const DeferredError, globalThis: *JSGlobalObject) JSValue { + const err = switch (this.kind) { + .plainerror => this.msg.toErrorInstance(globalThis), + .typeerror => this.msg.toTypeErrorInstance(globalThis), + .rangeerror => this.msg.toRangeErrorInstance(globalThis), + }; + err.put(globalThis, ZigString.static("code"), ZigString.init(@tagName(this.code)).toJS(globalThis)); + return err; + } +}; diff --git a/src/bun.js/bindings/blob.cpp b/src/bun.js/bindings/blob.cpp index dfd98d45ba2e7..bfbdc1fd49946 100644 --- a/src/bun.js/bindings/blob.cpp +++ b/src/bun.js/bindings/blob.cpp @@ -1,7 +1,7 @@ #include "blob.h" #include "ZigGeneratedClasses.h" -extern "C" JSC::EncodedJSValue Blob__create(JSC::JSGlobalObject* globalObject, void* impl); +extern "C" JSC::EncodedJSValue SYSV_ABI Blob__create(JSC::JSGlobalObject* globalObject, void* impl); extern "C" void* Blob__setAsFile(void* impl, BunString* filename); namespace WebCore { diff --git a/src/bun.js/bindings/bun-simdutf.cpp b/src/bun.js/bindings/bun-simdutf.cpp index 4909be19aab35..ea6ff06416509 100644 --- a/src/bun.js/bindings/bun-simdutf.cpp +++ b/src/bun.js/bindings/bun-simdutf.cpp @@ -1,4 +1,4 @@ -#include "simdutf.h" +#include "wtf/SIMDUTF.h" typedef struct SIMDUTFResult { int error; @@ -264,6 +264,10 @@ size_t simdutf__convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, { return simdutf::convert_valid_utf16be_to_utf32(buf, len, utf32_buffer); } +size_t simdutf__convert_latin1_to_utf8(const char* input, size_t length, char* utf8_buffer) +{ + return simdutf::convert_latin1_to_utf8(input, length, utf8_buffer); +} void simdutf__change_endianness_utf16(const char16_t* buf, size_t length, char16_t* output) { @@ -325,6 +329,11 @@ size_t simdutf__utf32_length_from_utf8(const char* input, size_t length) return simdutf::utf32_length_from_utf8(input, length); } +size_t simdutf__utf8_length_from_latin1(const char* input, size_t length) +{ + return simdutf::utf8_length_from_latin1(input, length); +} + size_t simdutf__base64_encode(const char* input, size_t length, char* output, int is_urlsafe) { return simdutf::binary_to_base64(input, length, output, is_urlsafe ? simdutf::base64_url : simdutf::base64_default); diff --git a/src/bun.js/bindings/bun-simdutf.zig b/src/bun.js/bindings/bun-simdutf.zig index 98f426cd76b7b..7ef948dfc22c1 100644 --- a/src/bun.js/bindings/bun-simdutf.zig +++ b/src/bun.js/bindings/bun-simdutf.zig @@ -81,6 +81,7 @@ pub extern fn simdutf__convert_utf16le_to_utf32_with_errors(buf: [*]const u16, l pub extern fn simdutf__convert_utf16be_to_utf32_with_errors(buf: [*]const u16, len: usize, utf32_buffer: [*]u32) SIMDUTFResult; pub extern fn simdutf__convert_valid_utf16le_to_utf32(buf: [*]const u16, len: usize, utf32_buffer: [*]u32) usize; pub extern fn simdutf__convert_valid_utf16be_to_utf32(buf: [*]const u16, len: usize, utf32_buffer: [*]u32) usize; +pub extern fn simdutf__convert_latin1_to_utf8(buf: [*]const u8, len: usize, utf8_buffer: [*]u8) usize; pub extern fn simdutf__change_endianness_utf16(buf: [*]const u16, length: usize, output: [*]u16) void; pub extern fn simdutf__count_utf16le(buf: [*]const u16, length: usize) usize; pub extern fn simdutf__count_utf16be(buf: [*]const u16, length: usize) usize; @@ -93,6 +94,7 @@ pub extern fn simdutf__utf16_length_from_utf8(input: [*]const u8, length: usize) pub extern fn simdutf__utf8_length_from_utf32(input: [*c]const c_uint, length: usize) usize; pub extern fn simdutf__utf16_length_from_utf32(input: [*c]const c_uint, length: usize) usize; pub extern fn simdutf__utf32_length_from_utf8(input: [*]const u8, length: usize) usize; +pub extern fn simdutf__utf8_length_from_latin1(input: [*]const u8, length: usize) usize; pub const validate = struct { pub const with_errors = struct { @@ -126,6 +128,14 @@ pub const validate = struct { }; pub const convert = struct { + pub const latin1 = struct { + pub const to = struct { + pub fn utf8(input: []const u8, output: []u8) usize { + return simdutf__convert_latin1_to_utf8(input.ptr, input.len, output.ptr); + } + }; + }; + pub const utf8 = struct { pub const to = struct { pub const utf16 = struct { @@ -261,6 +271,10 @@ pub const length = struct { } }; + pub fn latin1(input: []const u8) usize { + return simdutf__utf8_length_from_latin1(input.ptr, input.len); + } + pub fn utf32(input: []const u32) usize { JSC.markBinding(@src()); return simdutf__utf8_length_from_utf32(input.ptr, input.len); diff --git a/src/bun.js/bindings/c-bindings.cpp b/src/bun.js/bindings/c-bindings.cpp index c0358a8c2c5cc..9357a1c84c6a8 100644 --- a/src/bun.js/bindings/c-bindings.cpp +++ b/src/bun.js/bindings/c-bindings.cpp @@ -1,5 +1,6 @@ // when we don't want to use @cInclude, we can just stick wrapper functions here #include "root.h" +#include #if !OS(WINDOWS) #include @@ -599,6 +600,12 @@ extern "C" int32_t open_as_nonblocking_tty(int32_t fd, int32_t mode) #endif +extern "C" size_t Bun__ramSize() +{ + // This value is cached internally. + return WTF::ramSize(); +} + #if !OS(WINDOWS) extern "C" void Bun__disableSOLinger(int fd) @@ -617,4 +624,231 @@ extern "C" void Bun__disableSOLinger(SOCKET fd) setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); } -#endif \ No newline at end of file +#endif + +extern "C" int ffi_vprintf(const char* fmt, va_list ap) +{ + int ret = vfprintf(stderr, fmt, ap); + fflush(stderr); + return ret; +} + +extern "C" int ffi_vfprintf(FILE* stream, const char* fmt, va_list ap) +{ + int ret = vfprintf(stream, fmt, ap); + fflush(stream); + return ret; +} + +extern "C" int ffi_printf(const char* __restrict fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vprintf(fmt, ap); + va_end(ap); + fflush(stdout); + return r; +} + +extern "C" int ffi_fprintf(FILE* stream, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vfprintf(stream, fmt, ap); + va_end(ap); + fflush(stream); + return r; +} + +extern "C" int ffi_scanf(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vscanf(fmt, ap); + va_end(ap); + return r; +} + +extern "C" int ffi_fscanf(FILE* stream, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vfscanf(stream, fmt, ap); + va_end(ap); + return r; +} + +extern "C" int ffi_vsscanf(const char* str, const char* fmt, va_list ap) +{ + return vsscanf(str, fmt, ap); +} + +extern "C" int ffi_sscanf(const char* str, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vsscanf(str, fmt, ap); + va_end(ap); + return r; +} + +extern "C" FILE* ffi_fopen(const char* path, const char* mode) +{ + return fopen(path, mode); +} + +extern "C" int ffi_fclose(FILE* file) +{ + return fclose(file); +} + +extern "C" int ffi_fgetc(FILE* file) +{ + return fgetc(file); +} + +extern "C" int ffi_fputc(int c, FILE* file) +{ + return fputc(c, file); +} + +extern "C" int ffi_ungetc(int c, FILE* file) +{ + return ungetc(c, file); +} + +extern "C" int ffi_feof(FILE* file) +{ + return feof(file); +} + +extern "C" int ffi_fseek(FILE* file, long offset, int whence) +{ + return fseek(file, offset, whence); +} + +extern "C" long ffi_ftell(FILE* file) +{ + return ftell(file); +} + +extern "C" int ffi_fflush(FILE* file) +{ + return fflush(file); +} + +extern "C" int ffi_fileno(FILE* file) +{ + return fileno(file); +} + +// Handle signals in bun.spawnSync. +// If we receive a signal, we want to forward the signal to the child process. +#if OS(LINUX) || OS(DARWIN) +#include +#include + +// Note: We only ever use bun.spawnSync on the main thread. +extern "C" int64_t Bun__currentSyncPID = 0; +static int Bun__pendingSignalToSend = 0; +static struct sigaction previous_actions[NSIG]; + +// This list of signals is copied from npm. +// https://github.com/npm/cli/blob/fefd509992a05c2dfddbe7bc46931c42f1da69d7/workspaces/arborist/lib/signals.js#L26-L57 +#define FOR_EACH_POSIX_SIGNAL(M) \ + M(SIGABRT); \ + M(SIGALRM); \ + M(SIGHUP); \ + M(SIGINT); \ + M(SIGTERM); \ + M(SIGVTALRM); \ + M(SIGXCPU); \ + M(SIGXFSZ); \ + M(SIGUSR2); \ + M(SIGTRAP); \ + M(SIGSYS); \ + M(SIGQUIT); \ + M(SIGIOT); \ + M(SIGIO); + +#if OS(LINUX) +#define FOR_EACH_LINUX_ONLY_SIGNAL(M) \ + M(SIGPOLL); \ + M(SIGPWR); \ + M(SIGSTKFLT); + +#endif + +#if OS(DARWIN) +#define FOR_EACH_SIGNAL(M) FOR_EACH_POSIX_SIGNAL(M) +#endif + +#if OS(LINUX) +#define FOR_EACH_SIGNAL(M) \ + FOR_EACH_POSIX_SIGNAL(M) \ + FOR_EACH_LINUX_ONLY_SIGNAL(M) +#endif + +static void Bun__forwardSignalFromParentToChildAndRestorePreviousAction(pid_t pid, int sig) +{ + sigset_t restore_mask; + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, sig); + sigemptyset(&restore_mask); + sigaddset(&restore_mask, sig); + pthread_sigmask(SIG_BLOCK, &mask, &restore_mask); + kill(pid, sig); + pthread_sigmask(SIG_UNBLOCK, &restore_mask, nullptr); +} + +extern "C" void Bun__sendPendingSignalIfNecessary() +{ + int sig = Bun__pendingSignalToSend; + Bun__pendingSignalToSend = 0; + int pid = Bun__currentSyncPID; + if (sig == 0 || pid == 0) + return; + + Bun__forwardSignalFromParentToChildAndRestorePreviousAction(pid, sig); +} + +extern "C" void Bun__registerSignalsForForwarding() +{ + Bun__pendingSignalToSend = 0; + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; + sa.sa_handler = [](int sig) { + if (Bun__currentSyncPID == 0) { + Bun__pendingSignalToSend = sig; + return; + } + + Bun__forwardSignalFromParentToChildAndRestorePreviousAction(Bun__currentSyncPID, sig); + }; + +#define REGISTER_SIGNAL(SIG) \ + if (sigaction(SIG, &sa, &previous_actions[SIG]) == -1) { \ + } + + FOR_EACH_SIGNAL(REGISTER_SIGNAL) + +#undef REGISTER_SIGNAL +} + +extern "C" void Bun__unregisterSignalsForForwarding() +{ + Bun__currentSyncPID = 0; + +#define UNREGISTER_SIGNAL(SIG) \ + if (sigaction(SIG, &previous_actions[SIG], NULL) == -1) { \ + } + + FOR_EACH_SIGNAL(UNREGISTER_SIGNAL) + memset(previous_actions, 0, sizeof(previous_actions)); +#undef UNREGISTER_SIGNAL +} + +#endif diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 1d3eab92581f3..27d1e91655f0f 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -202,11 +202,7 @@ pub const ResolvedSource = extern struct { /// source_url is eventually deref'd on success source_url: bun.String = bun.String.empty, - // this pointer is unused and shouldn't exist - commonjs_exports: ?[*]ZigString = null, - - // This field is used to indicate whether it's a CommonJS module or ESM - commonjs_exports_len: u32 = 0, + is_commonjs_module: bool = false, hash: u32 = 0, @@ -223,6 +219,13 @@ pub const ResolvedSource = extern struct { pub const Tag = @import("ResolvedSourceTag").ResolvedSourceTag; }; +pub const SourceProvider = opaque { + extern fn JSC__SourceProvider__deref(*SourceProvider) void; + pub fn deref(provider: *SourceProvider) void { + JSC__SourceProvider__deref(provider); + } +}; + const Mimalloc = @import("../../allocators/mimalloc.zig"); export fn ZigString__free(raw: [*]const u8, len: usize, allocator_: ?*anyopaque) void { @@ -360,7 +363,7 @@ pub const Process = extern struct { // TODO: https://github.com/nodejs/node/blob/master/deps/uv/src/unix/darwin-proctitle.c pub fn setTitle(globalObject: *JSGlobalObject, _: *ZigString) callconv(.C) JSValue { - return ZigString.init(_bun).toValue(globalObject); + return ZigString.init(_bun).toJS(globalObject); } pub const getArgv = JSC.Node.Process.getArgv; @@ -426,6 +429,10 @@ pub const ZigStackTrace = extern struct { frames_ptr: [*]ZigStackFrame, frames_len: u8, + /// Non-null if `source_lines_*` points into data owned by a JSC::SourceProvider. + /// If so, then .deref must be called on it to release the memory. + referenced_source_provider: ?*JSC.SourceProvider = null, + pub fn toAPI( this: *const ZigStackTrace, allocator: std.mem.Allocator, @@ -596,9 +603,16 @@ pub const ZigStackFrame = extern struct { } try writer.writeAll(source_slice); + if (source_slice.len > 0 and (this.position.line.isValid() or this.position.column.isValid())) { + if (this.enable_color) { + try writer.writeAll(comptime Output.prettyFmt(":", true)); + } else { + try writer.writeAll(":"); + } + } if (this.enable_color) { - if (this.position.line.isValid()) { + if (this.position.line.isValid() or this.position.column.isValid()) { try writer.writeAll(comptime Output.prettyFmt("", true)); } else { try writer.writeAll(comptime Output.prettyFmt("", true)); @@ -610,11 +624,11 @@ pub const ZigStackFrame = extern struct { if (this.enable_color) { try std.fmt.format( writer, - comptime Output.prettyFmt(":{d}:{d}", true), + comptime Output.prettyFmt("{d}:{d}", true), .{ this.position.line.oneBased(), this.position.column.oneBased() }, ); } else { - try std.fmt.format(writer, ":{d}:{d}", .{ + try std.fmt.format(writer, "{d}:{d}", .{ this.position.line.oneBased(), this.position.column.oneBased(), }); @@ -623,13 +637,13 @@ pub const ZigStackFrame = extern struct { if (this.enable_color) { try std.fmt.format( writer, - comptime Output.prettyFmt(":{d}", true), + comptime Output.prettyFmt("{d}", true), .{ this.position.line.oneBased(), }, ); } else { - try std.fmt.format(writer, ":{d}", .{ + try std.fmt.format(writer, "{d}", .{ this.position.line.oneBased(), }); } @@ -670,7 +684,11 @@ pub const ZigStackFrame = extern struct { } }, .Wasm => { - try std.fmt.format(writer, "WASM {}", .{name}); + if (!name.isEmpty()) { + try std.fmt.format(writer, "{}", .{name}); + } else { + try writer.writeAll("WASM"); + } }, .Constructor => { try std.fmt.format(writer, "new {}", .{name}); @@ -775,6 +793,10 @@ pub const ZigException = extern struct { for (this.stack.frames_ptr[0..this.stack.frames_len]) |*frame| { frame.deinit(); } + + if (this.stack.referenced_source_provider) |source| { + source.deref(); + } } pub const shim = Shimmer("Zig", "Exception", @This()); @@ -817,7 +839,9 @@ pub const ZigException = extern struct { } pub fn deinit(this: *Holder, vm: *JSC.VirtualMachine) void { - this.zigException().deinit(); + if (this.loaded) { + this.zig_exception.deinit(); + } if (this.need_to_clear_parser_arena_on_deinit) { vm.module_loader.resetArena(vm); } @@ -954,6 +978,10 @@ comptime { /// Using characters16() does not seem to always have the sentinel. or something else /// broke when I just used it. Not sure. ... but this works! pub export fn Bun__LoadLibraryBunString(str: *bun.String) ?*anyopaque { + if (comptime !Environment.isWindows) { + unreachable; + } + var buf: bun.WPathBuffer = undefined; const data = switch (str.encoding()) { .utf8 => bun.strings.convertUTF8toUTF16InBuffer(&buf, str.utf8()), @@ -993,7 +1021,10 @@ pub export fn NodeModuleModule__findPath( const found = if (paths_maybe) |paths| found: { var iter = paths.iterator(global); while (iter.next()) |path| { - const cur_path = bun.String.tryFromJS(path, global) orelse continue; + const cur_path = bun.String.tryFromJS(path, global) orelse { + if (global.hasException()) return .zero; + continue; + }; defer cur_path.deref(); if (findPathInner(request_bun_str, cur_path, global)) |found| { diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig index 28595f2675ecc..7550e84910e8b 100644 --- a/src/bun.js/bindings/generated_classes_list.zig +++ b/src/bun.js/bindings/generated_classes_list.zig @@ -71,6 +71,11 @@ pub const Classes = struct { pub const FileInternalReadableStreamSource = JSC.WebCore.FileReader.Source; pub const BlobInternalReadableStreamSource = JSC.WebCore.ByteBlobLoader.Source; pub const BytesInternalReadableStreamSource = JSC.WebCore.ByteStream.Source; + pub const PostgresSQLConnection = JSC.Postgres.PostgresSQLConnection; + pub const PostgresSQLQuery = JSC.Postgres.PostgresSQLQuery; pub const BrotliEncoder = JSC.API.BrotliEncoder; pub const BrotliDecoder = JSC.API.BrotliDecoder; + pub const TextEncoderStreamEncoder = JSC.WebCore.TextEncoderStreamEncoder; + pub const ZlibEncoder = JSC.API.ZlibEncoder; + pub const ZlibDecoder = JSC.API.ZlibDecoder; }; diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index e8068957f4b6b..54efa66cfcdeb 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -94,8 +94,7 @@ typedef struct ResolvedSource { BunString specifier; BunString source_code; BunString source_url; - ZigString* commonJSExports; - uint32_t commonJSExportsLen; + bool isCommonJSModule; uint32_t hash; void* allocator; JSC::EncodedJSValue jsvalue_for_export; @@ -139,6 +138,9 @@ const ZigStackFrameCode ZigStackFrameCodeGlobal = 4; const ZigStackFrameCode ZigStackFrameCodeWasm = 5; const ZigStackFrameCode ZigStackFrameCodeConstructor = 6; +extern "C" void __attribute((__noreturn__)) Bun__panic(const char* message, size_t length); +#define BUN_PANIC(message) Bun__panic(message, sizeof(message) - 1) + typedef struct ZigStackFramePosition { int32_t line_zero_based; int32_t column_zero_based; @@ -169,6 +171,7 @@ typedef struct ZigStackTrace { uint8_t source_lines_to_collect; ZigStackFrame* frames_ptr; uint8_t frames_len; + JSC::SourceProvider* referenced_source_provider; } ZigStackTrace; typedef struct ZigException { @@ -295,9 +298,9 @@ using Uint8Array_alias = JSC::JSUint8Array; typedef struct { char* ptr; - uint32_t offset; - uint32_t len; - uint32_t byte_len; + size_t offset; + size_t len; + size_t byte_len; uint8_t cell_type; int64_t _value; bool shared; @@ -348,6 +351,7 @@ extern "C" const char* Bun__versions_mimalloc; extern "C" const char* Bun__versions_picohttpparser; extern "C" const char* Bun__versions_uws; extern "C" const char* Bun__versions_webkit; +extern "C" const char* Bun__versions_libdeflate; extern "C" const char* Bun__versions_zig; extern "C" const char* Bun__versions_zlib; extern "C" const char* Bun__versions_tinycc; diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 6b9aff353a0a9..6d5efdb701743 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -164,7 +164,6 @@ CPP_DECL JSC__JSValue ZigString__toExternalValueWithCallback(const ZigString* ar CPP_DECL JSC__JSValue ZigString__toRangeErrorInstance(const ZigString* arg0, JSC__JSGlobalObject* arg1); CPP_DECL JSC__JSValue ZigString__toSyntaxErrorInstance(const ZigString* arg0, JSC__JSGlobalObject* arg1); CPP_DECL JSC__JSValue ZigString__toTypeErrorInstance(const ZigString* arg0, JSC__JSGlobalObject* arg1); -CPP_DECL JSC__JSValue ZigString__toValue(const ZigString* arg0, JSC__JSGlobalObject* arg1); CPP_DECL JSC__JSValue ZigString__toValueGC(const ZigString* arg0, JSC__JSGlobalObject* arg1); CPP_DECL WebCore__DOMURL* WebCore__DOMURL__cast_(JSC__JSValue JSValue0, JSC__VM* arg1); CPP_DECL BunString WebCore__DOMURL__fileSystemPath(WebCore__DOMURL* arg0); @@ -191,7 +190,7 @@ CPP_DECL void WebCore__FetchHeaders__count(WebCore__FetchHeaders* arg0, uint32_t CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__createEmpty(); CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__createFromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__createFromPicoHeaders_(const void* arg0); -CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__createFromUWS(JSC__JSGlobalObject* arg0, void* arg1); +CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__createFromUWS(void* arg1); CPP_DECL JSC__JSValue WebCore__FetchHeaders__createValue(JSC__JSGlobalObject* arg0, StringPointer* arg1, StringPointer* arg2, const ZigString* arg3, uint32_t arg4); CPP_DECL void WebCore__FetchHeaders__deref(WebCore__FetchHeaders* arg0); CPP_DECL void WebCore__FetchHeaders__fastGet_(WebCore__FetchHeaders* arg0, unsigned char arg1, ZigString* arg2); @@ -200,7 +199,6 @@ CPP_DECL void WebCore__FetchHeaders__fastRemove_(WebCore__FetchHeaders* arg0, un CPP_DECL void WebCore__FetchHeaders__get_(WebCore__FetchHeaders* arg0, const ZigString* arg1, ZigString* arg2, JSC__JSGlobalObject* arg3); CPP_DECL bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); CPP_DECL bool WebCore__FetchHeaders__isEmpty(WebCore__FetchHeaders* arg0); -CPP_DECL void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2, JSC__JSGlobalObject* arg3); CPP_DECL void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); CPP_DECL JSC__JSValue WebCore__FetchHeaders__toJS(WebCore__FetchHeaders* arg0, JSC__JSGlobalObject* arg1); CPP_DECL void WebCore__FetchHeaders__toUWSResponse(WebCore__FetchHeaders* arg0, bool arg1, void* arg2); @@ -232,13 +230,11 @@ CPP_DECL JSC__JSValue WebCore__AbortSignal__abortReason(WebCore__AbortSignal* ar CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__addListener(WebCore__AbortSignal* arg0, void* arg1, void(* ArgFn2)(void* arg0, JSC__JSValue JSValue1)); CPP_DECL void WebCore__AbortSignal__cleanNativeBindings(WebCore__AbortSignal* arg0, void* arg1); CPP_DECL JSC__JSValue WebCore__AbortSignal__create(JSC__JSGlobalObject* arg0); -CPP_DECL JSC__JSValue WebCore__AbortSignal__createAbortError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); -CPP_DECL JSC__JSValue WebCore__AbortSignal__createTimeoutError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__fromJS(JSC__JSValue JSValue0); CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__ref(WebCore__AbortSignal* arg0); -CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSValue JSValue1); +CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSGlobalObject*, uint8_t abortReason); CPP_DECL JSC__JSValue WebCore__AbortSignal__toJS(WebCore__AbortSignal* arg0, JSC__JSGlobalObject* arg1); -CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__unref(WebCore__AbortSignal* arg0); +CPP_DECL void WebCore__AbortSignal__unref(WebCore__AbortSignal* arg0); #pragma mark - JSC::JSPromise @@ -280,7 +276,7 @@ CPP_DECL void JSC__JSFunction__optimizeSoon(JSC__JSValue JSValue0); #pragma mark - JSC::JSGlobalObject CPP_DECL VirtualMachine* JSC__JSGlobalObject__bunVM(JSC__JSGlobalObject* arg0); -CPP_DECL JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject* arg0, void** arg1, uint16_t arg2, const ZigString* arg3); +CPP_DECL JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject* arg0, const JSC::JSValue* arg1, size_t arg2, const ZigString* arg3); CPP_DECL void JSC__JSGlobalObject__createSyntheticModule_(JSC__JSGlobalObject* arg0, ZigString* arg1, size_t arg2, JSC__JSValue* arg3, size_t arg4); CPP_DECL void JSC__JSGlobalObject__deleteModuleRegistryEntry(JSC__JSGlobalObject* arg0, ZigString* arg1); CPP_DECL JSC__JSValue JSC__JSGlobalObject__generateHeapSnapshot(JSC__JSGlobalObject* arg0); @@ -302,7 +298,7 @@ CPP_DECL void JSC__JSMap__set(JSC__JSMap* arg0, JSC__JSGlobalObject* arg1, JSC__ #pragma mark - JSC::JSValue -CPP_DECL void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2, JSC__JSValue(* ArgFn3)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1), JSC__JSValue(* ArgFn4)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)); +CPP_DECL void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2, SYSV_ABI JSC__JSValue(* ArgFn3)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1), SYSV_ABI JSC__JSValue(* ArgFn4)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1)); CPP_DECL bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, Bun__ArrayBuffer* arg2); CPP_DECL unsigned char JSC__JSValue__asBigIntCompare(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL JSC__JSCell* JSC__JSValue__asCell(JSC__JSValue JSValue0); @@ -462,104 +458,104 @@ CPP_DECL void FFI__ptr__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue FFI__ptr__fastpath(JSC__JSGlobalObject* arg0, void* arg1, Uint8Array_alias* arg2); -ZIG_DECL JSC__JSValue FFI__ptr__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI FFI__ptr__fastpath(JSC__JSGlobalObject* arg0, void* arg1, Uint8Array_alias* arg2); +extern "C" JSC__JSValue SYSV_ABI FFI__ptr__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__u8__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__u8__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__u8__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u8__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u8__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__u16__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__u16__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__u16__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u16__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u16__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__u32__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__u32__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__u32__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u32__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u32__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__ptr__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__ptr__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__ptr__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__ptr__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__ptr__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__i8__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__i8__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__i8__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i8__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i8__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__i16__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__i16__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__i16__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i16__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i16__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__i32__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__i32__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__i32__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i32__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i32__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__f32__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__f32__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__f32__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__f32__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__f32__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__f64__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__f64__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__f64__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__f64__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__f64__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__i64__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__i64__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__i64__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i64__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__i64__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__u64__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__u64__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__u64__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u64__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__u64__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif CPP_DECL void Reader__intptr__put(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); #ifdef __cplusplus -ZIG_DECL JSC__JSValue Reader__intptr__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); -ZIG_DECL JSC__JSValue Reader__intptr__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__intptr__fastpath(JSC__JSGlobalObject* arg0, void* arg1, int64_t arg2, int32_t arg3); +extern "C" JSC__JSValue SYSV_ABI Reader__intptr__slowpath(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue* arg2, size_t arg3); #endif @@ -582,17 +578,17 @@ ZIG_DECL void Zig__GlobalObject__resolve(ErrorableString* arg0, JSC__JSGlobalObj #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__Path__basename(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__dirname(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__extname(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__format(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__isAbsolute(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__join(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__normalize(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__parse(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__relative(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__resolve(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); -ZIG_DECL JSC__JSValue Bun__Path__toNamespacedPath(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__basename(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__dirname(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__extname(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__format(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__isAbsolute(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__join(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__normalize(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__parse(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__relative(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__resolve(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); +extern "C" JSC__JSValue SYSV_ABI Bun__Path__toNamespacedPath(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); #endif @@ -606,14 +602,14 @@ CPP_DECL void ArrayBufferSink__onReady(JSC__JSValue JSValue0, JSC__JSValue JSVal #ifdef __cplusplus ZIG_DECL JSC__JSValue ArrayBufferSink__close(JSC__JSGlobalObject* arg0, void* arg1); -ZIG_DECL JSC__JSValue ArrayBufferSink__construct(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue ArrayBufferSink__end(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue ArrayBufferSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); +BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__construct); +BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__end); +ZIG_DECL JSC__JSValue SYSV_ABI ArrayBufferSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); ZIG_DECL void ArrayBufferSink__finalize(void* arg0); -ZIG_DECL JSC__JSValue ArrayBufferSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue ArrayBufferSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__flush); +BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__start); ZIG_DECL void ArrayBufferSink__updateRef(void* arg0, bool arg1); -ZIG_DECL JSC__JSValue ArrayBufferSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__write); #endif CPP_DECL JSC__JSValue HTTPSResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); @@ -626,14 +622,14 @@ CPP_DECL void HTTPSResponseSink__onReady(JSC__JSValue JSValue0, JSC__JSValue JSV #ifdef __cplusplus ZIG_DECL JSC__JSValue HTTPSResponseSink__close(JSC__JSGlobalObject* arg0, void* arg1); -ZIG_DECL JSC__JSValue HTTPSResponseSink__construct(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue HTTPSResponseSink__end(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue HTTPSResponseSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); +BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__construct); +BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__end); +ZIG_DECL JSC__JSValue SYSV_ABI HTTPSResponseSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); ZIG_DECL void HTTPSResponseSink__finalize(void* arg0); -ZIG_DECL JSC__JSValue HTTPSResponseSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue HTTPSResponseSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__flush); +BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__start); ZIG_DECL void HTTPSResponseSink__updateRef(void* arg0, bool arg1); -ZIG_DECL JSC__JSValue HTTPSResponseSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__write); #endif CPP_DECL JSC__JSValue HTTPResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); @@ -646,14 +642,14 @@ CPP_DECL void HTTPResponseSink__onReady(JSC__JSValue JSValue0, JSC__JSValue JSVa #ifdef __cplusplus ZIG_DECL JSC__JSValue HTTPResponseSink__close(JSC__JSGlobalObject* arg0, void* arg1); -ZIG_DECL JSC__JSValue HTTPResponseSink__construct(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue HTTPResponseSink__end(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue HTTPResponseSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); +BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__construct); +BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__end); +ZIG_DECL JSC__JSValue SYSV_ABI SYSV_ABI HTTPResponseSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); ZIG_DECL void HTTPResponseSink__finalize(void* arg0); -ZIG_DECL JSC__JSValue HTTPResponseSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue HTTPResponseSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__flush); +BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__start); ZIG_DECL void HTTPResponseSink__updateRef(void* arg0, bool arg1); -ZIG_DECL JSC__JSValue HTTPResponseSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__write); #endif CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); @@ -666,14 +662,14 @@ CPP_DECL void FileSink__onReady(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JS #ifdef __cplusplus ZIG_DECL JSC__JSValue FileSink__close(JSC__JSGlobalObject* arg0, void* arg1); -ZIG_DECL JSC__JSValue FileSink__construct(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue FileSink__end(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue FileSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); +BUN_DECLARE_HOST_FUNCTION(FileSink__construct); +BUN_DECLARE_HOST_FUNCTION(FileSink__end); +ZIG_DECL JSC__JSValue SYSV_ABI FileSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); ZIG_DECL void FileSink__finalize(void* arg0); -ZIG_DECL JSC__JSValue FileSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue FileSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(FileSink__flush); +BUN_DECLARE_HOST_FUNCTION(FileSink__start); ZIG_DECL void FileSink__updateRef(void* arg0, bool arg1); -ZIG_DECL JSC__JSValue FileSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(FileSink__write); #endif @@ -687,14 +683,14 @@ CPP_DECL void FileSink__onReady(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JS #ifdef __cplusplus ZIG_DECL JSC__JSValue FileSink__close(JSC__JSGlobalObject* arg0, void* arg1); -ZIG_DECL JSC__JSValue FileSink__construct(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue FileSink__end(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue FileSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); +BUN_DECLARE_HOST_FUNCTION(FileSink__construct); +BUN_DECLARE_HOST_FUNCTION(FileSink__end); +ZIG_DECL JSC__JSValue SYSV_ABI FileSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg1); ZIG_DECL void FileSink__finalize(void* arg0); -ZIG_DECL JSC__JSValue FileSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue FileSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(FileSink__flush); +BUN_DECLARE_HOST_FUNCTION(FileSink__start); ZIG_DECL void FileSink__updateRef(void* arg0, bool arg1); -ZIG_DECL JSC__JSValue FileSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(FileSink__write); #endif @@ -758,19 +754,19 @@ CPP_DECL ZigException ZigException__fromException(JSC__Exception* arg0); #ifdef __cplusplus -ZIG_DECL void Bun__ConsoleObject__count(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__countReset(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__messageWithTypeAndLevel(void* arg0, uint32_t MessageType1, uint32_t MessageLevel2, JSC__JSGlobalObject* arg3, JSC__JSValue* arg4, size_t arg5); -ZIG_DECL void Bun__ConsoleObject__profile(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__profileEnd(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__record(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); -ZIG_DECL void Bun__ConsoleObject__recordEnd(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); -ZIG_DECL void Bun__ConsoleObject__screenshot(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); -ZIG_DECL void Bun__ConsoleObject__takeHeapSnapshot(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__time(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__timeEnd(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); -ZIG_DECL void Bun__ConsoleObject__timeLog(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3, JSC__JSValue* arg4, size_t arg5); -ZIG_DECL void Bun__ConsoleObject__timeStamp(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); +extern "C" SYSV_ABI void Bun__ConsoleObject__count(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__countReset(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__messageWithTypeAndLevel(void* arg0, uint32_t MessageType1, uint32_t MessageLevel2, JSC__JSGlobalObject* arg3, JSC__JSValue* arg4, size_t arg5); +extern "C" SYSV_ABI void Bun__ConsoleObject__profile(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__profileEnd(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__record(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); +extern "C" SYSV_ABI void Bun__ConsoleObject__recordEnd(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); +extern "C" SYSV_ABI void Bun__ConsoleObject__screenshot(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); +extern "C" SYSV_ABI void Bun__ConsoleObject__takeHeapSnapshot(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__time(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__timeEnd(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3); +extern "C" SYSV_ABI void Bun__ConsoleObject__timeLog(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3, JSC__JSValue* arg4, size_t arg5); +extern "C" SYSV_ABI void Bun__ConsoleObject__timeStamp(void* arg0, JSC__JSGlobalObject* arg1, ScriptArguments* arg2); #endif @@ -789,37 +785,37 @@ ZIG_DECL JSC__JSValue Bun__Timer__setTimeout(JSC__JSGlobalObject* arg0, JSC__JSV #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__HTTPRequestContext__onReject(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContext__onRejectStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContext__onResolve(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContext__onResolveStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContext__onReject); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContext__onRejectStream); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContext__onResolve); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContext__onResolveStream); #endif #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextTLS__onReject(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextTLS__onRejectStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextTLS__onResolve(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextTLS__onResolveStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextTLS__onReject); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextTLS__onRejectStream); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextTLS__onResolve); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextTLS__onResolveStream); #endif #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebug__onReject(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebug__onRejectStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebug__onResolve(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebug__onResolveStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebug__onReject); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebug__onRejectStream); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebug__onResolve); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebug__onResolveStream); #endif #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebugTLS__onReject(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebugTLS__onRejectStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebugTLS__onResolve(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebugTLS__onResolveStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebugTLS__onReject); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebugTLS__onRejectStream); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebugTLS__onResolve); +BUN_DECLARE_HOST_FUNCTION(Bun__HTTPRequestContextDebugTLS__onResolveStream); #endif @@ -828,15 +824,15 @@ ZIG_DECL JSC__JSValue Bun__HTTPRequestContextDebugTLS__onResolveStream(JSC__JSGl #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__BodyValueBufferer__onRejectStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__BodyValueBufferer__onResolveStream(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__BodyValueBufferer__onRejectStream); +BUN_DECLARE_HOST_FUNCTION(Bun__BodyValueBufferer__onResolveStream); #endif #ifdef __cplusplus -ZIG_DECL JSC__JSValue Bun__TestScope__onReject(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -ZIG_DECL JSC__JSValue Bun__TestScope__onResolve(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__TestScope__onReject); +BUN_DECLARE_HOST_FUNCTION(Bun__TestScope__onResolve); #endif @@ -854,7 +850,7 @@ CPP_DECL bool JSC__CustomGetterSetter__isSetterNull(JSC__CustomGetterSetter *arg #ifdef __cplusplus -CPP_DECL JSC__JSValue Bun__onResolveEntryPointResult(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); -CPP_DECL JSC__JSValue Bun__onRejectEntryPointResult(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +BUN_DECLARE_HOST_FUNCTION(Bun__onResolveEntryPointResult); +BUN_DECLARE_HOST_FUNCTION(Bun__onRejectEntryPointResult); #endif \ No newline at end of file diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index 448276de2e219..3b64ff69051ff 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -95,8 +95,6 @@ pub extern fn ZigString__toExternalValueWithCallback(arg0: [*c]const ZigString, pub extern fn ZigString__toRangeErrorInstance(arg0: [*c]const ZigString, arg1: *bindings.JSGlobalObject) JSC__JSValue; pub extern fn ZigString__toSyntaxErrorInstance(arg0: [*c]const ZigString, arg1: *bindings.JSGlobalObject) JSC__JSValue; pub extern fn ZigString__toTypeErrorInstance(arg0: [*c]const ZigString, arg1: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn ZigString__toValue(arg0: [*c]const ZigString, arg1: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn ZigString__toValueGC(arg0: [*c]const ZigString, arg1: *bindings.JSGlobalObject) JSC__JSValue; pub extern fn WebCore__DOMURL__cast_(JSValue0: JSC__JSValue, arg1: *bindings.VM) ?*bindings.DOMURL; pub extern fn WebCore__DOMURL__fileSystemPath(arg0: ?*bindings.DOMURL) BunString; pub extern fn WebCore__DOMURL__href_(arg0: ?*bindings.DOMURL, arg1: [*c]ZigString) void; @@ -114,9 +112,8 @@ pub extern fn WebCore__FetchHeaders__cloneThis(arg0: ?*bindings.FetchHeaders, ar pub extern fn WebCore__FetchHeaders__copyTo(arg0: ?*bindings.FetchHeaders, arg1: [*c]StringPointer, arg2: [*c]StringPointer, arg3: [*c]u8) void; pub extern fn WebCore__FetchHeaders__count(arg0: ?*bindings.FetchHeaders, arg1: [*c]u32, arg2: [*c]u32) void; pub extern fn WebCore__FetchHeaders__createEmpty(...) ?*bindings.FetchHeaders; -pub extern fn WebCore__FetchHeaders__createFromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*bindings.FetchHeaders; pub extern fn WebCore__FetchHeaders__createFromPicoHeaders_(arg0: ?*const anyopaque) ?*bindings.FetchHeaders; -pub extern fn WebCore__FetchHeaders__createFromUWS(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) ?*bindings.FetchHeaders; +pub extern fn WebCore__FetchHeaders__createFromUWS(arg1: ?*anyopaque) ?*bindings.FetchHeaders; pub extern fn WebCore__FetchHeaders__createValue(arg0: *bindings.JSGlobalObject, arg1: [*c]StringPointer, arg2: [*c]StringPointer, arg3: [*c]const ZigString, arg4: u32) JSC__JSValue; pub extern fn WebCore__FetchHeaders__deref(arg0: ?*bindings.FetchHeaders) void; pub extern fn WebCore__FetchHeaders__fastGet_(arg0: ?*bindings.FetchHeaders, arg1: u8, arg2: [*c]ZigString) void; @@ -145,13 +142,11 @@ pub extern fn WebCore__AbortSignal__abortReason(arg0: ?*bindings.AbortSignal) JS pub extern fn WebCore__AbortSignal__addListener(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque, ArgFn2: ?*const fn (?*anyopaque, JSC__JSValue) callconv(.C) void) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__cleanNativeBindings(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque) void; pub extern fn WebCore__AbortSignal__create(arg0: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn WebCore__AbortSignal__createAbortError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn WebCore__AbortSignal__createTimeoutError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue; pub extern fn WebCore__AbortSignal__fromJS(JSValue0: JSC__JSValue) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__ref(arg0: ?*bindings.AbortSignal) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__signal(arg0: ?*bindings.AbortSignal, JSValue1: JSC__JSValue) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__toJS(arg0: ?*bindings.AbortSignal, arg1: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn WebCore__AbortSignal__unref(arg0: ?*bindings.AbortSignal) ?*bindings.AbortSignal; +pub extern fn WebCore__AbortSignal__unref(arg0: ?*bindings.AbortSignal) void; pub extern fn JSC__JSPromise__asValue(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject) JSC__JSValue; pub extern fn JSC__JSPromise__create(arg0: *bindings.JSGlobalObject) ?*bindings.JSPromise; pub extern fn JSC__JSPromise__isHandled(arg0: [*c]const JSC__JSPromise, arg1: *bindings.VM) bool; @@ -181,7 +176,6 @@ pub extern fn JSC__JSInternalPromise__setHandled(arg0: [*c]bindings.JSInternalPr pub extern fn JSC__JSInternalPromise__status(arg0: [*c]const JSC__JSInternalPromise, arg1: *bindings.VM) u32; pub extern fn JSC__JSFunction__optimizeSoon(JSValue0: JSC__JSValue) void; pub extern fn JSC__JSGlobalObject__bunVM(arg0: *bindings.JSGlobalObject) ?*bindings.VirtualMachine; -pub extern fn JSC__JSGlobalObject__createAggregateError(arg0: *bindings.JSGlobalObject, arg1: [*c]*anyopaque, arg2: u16, arg3: [*c]const ZigString) JSC__JSValue; pub extern fn JSC__JSGlobalObject__createSyntheticModule_(arg0: *bindings.JSGlobalObject, arg1: [*c]ZigString, arg2: usize, arg3: [*c]bindings.JSValue, arg4: usize) void; pub extern fn JSC__JSGlobalObject__deleteModuleRegistryEntry(arg0: *bindings.JSGlobalObject, arg1: [*c]ZigString) void; pub extern fn JSC__JSGlobalObject__generateHeapSnapshot(arg0: *bindings.JSGlobalObject) JSC__JSValue; @@ -197,7 +191,7 @@ pub extern fn JSC__JSMap__get_(arg0: ?*bindings.JSMap, arg1: *bindings.JSGlobalO pub extern fn JSC__JSMap__has(arg0: ?*bindings.JSMap, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) bool; pub extern fn JSC__JSMap__remove(arg0: ?*bindings.JSMap, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) bool; pub extern fn JSC__JSMap__set(arg0: ?*bindings.JSMap, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue, JSValue3: JSC__JSValue) void; -pub extern fn JSC__JSValue___then(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue, ArgFn3: ?*const fn (*bindings.JSGlobalObject, *bindings.CallFrame) callconv(.C) JSC__JSValue, ArgFn4: ?*const fn (*bindings.JSGlobalObject, *bindings.CallFrame) callconv(.C) JSC__JSValue) void; +pub extern fn JSC__JSValue___then(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue, ArgFn3: ?bun.JSC.JSHostFunctionPtr, ArgFn4: ?bun.JSC.JSHostFunctionPtr) void; pub extern fn JSC__JSValue__asArrayBuffer_(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: ?*Bun__ArrayBuffer) bool; pub extern fn JSC__JSValue__asBigIntCompare(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) u8; pub extern fn JSC__JSValue__asCell(JSValue0: JSC__JSValue) [*c]bindings.JSCell; diff --git a/src/bun.js/bindings/helpers.h b/src/bun.js/bindings/helpers.h index e8c0e4072466c..ee00db842f23c 100644 --- a/src/bun.js/bindings/helpers.h +++ b/src/bun.js/bindings/helpers.h @@ -1,6 +1,7 @@ #pragma once #include "root.h" +#include "wtf/text/ASCIILiteral.h" #include #include @@ -9,6 +10,7 @@ #include #include #include +#include using JSC__JSGlobalObject = JSC::JSGlobalObject; using JSC__JSValue = JSC::EncodedJSValue; @@ -23,6 +25,8 @@ class GlobalObject; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" +extern "C" size_t Bun__stringSyntheticAllocationLimit; + namespace Zig { // 8 bit byte @@ -45,15 +49,6 @@ static void* untagVoid(const char16_t* ptr) return untagVoid(reinterpret_cast(ptr)); } -static const JSC::Identifier toIdentifier(ZigString str, JSC::JSGlobalObject* global) -{ - if (str.len == 0 || str.ptr == nullptr) { - return JSC::Identifier::EmptyIdentifier; - } - - return JSC::Identifier::fromString(global->vm(), { untag(str.ptr), str.len }); -} - static bool isTaggedUTF16Ptr(const unsigned char* ptr) { return (reinterpret_cast(ptr) & (static_cast(1) << 63)) != 0; @@ -90,16 +85,27 @@ static const WTF::String toString(ZigString str) } if (UNLIKELY(isTaggedExternalPtr(str.ptr))) { + // This will fail if the string is too long. Let's make it explicit instead of an ASSERT. + if (UNLIKELY(str.len > Bun__stringSyntheticAllocationLimit)) { + free_global_string(nullptr, reinterpret_cast(const_cast(untag(str.ptr))), static_cast(str.len)); + return {}; + } + return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::ExternalStringImpl::create({ untag(str.ptr), str.len }, untagVoid(str.ptr), free_global_string)) : WTF::String(WTF::ExternalStringImpl::create( - { reinterpret_cast(untag(str.ptr)), str.len }, untagVoid(str.ptr), free_global_string)); + { reinterpret_cast(untag(str.ptr)), str.len }, untagVoid(str.ptr), free_global_string)); + } + + // This will fail if the string is too long. Let's make it explicit instead of an ASSERT. + if (UNLIKELY(str.len > Bun__stringSyntheticAllocationLimit)) { + return {}; } return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::StringImpl::createWithoutCopying({ untag(str.ptr), str.len })) : WTF::String(WTF::StringImpl::createWithoutCopying( - { reinterpret_cast(untag(str.ptr)), str.len })); + { reinterpret_cast(untag(str.ptr)), str.len })); } static WTF::AtomString toAtomString(ZigString str) @@ -121,10 +127,15 @@ static const WTF::String toString(ZigString str, StringPointer ptr) return WTF::String::fromUTF8ReplacingInvalidSequences(std::span { &untag(str.ptr)[ptr.off], ptr.len }); } + // This will fail if the string is too long. Let's make it explicit instead of an ASSERT. + if (UNLIKELY(str.len > Bun__stringSyntheticAllocationLimit)) { + return {}; + } + return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::StringImpl::createWithoutCopying({ &untag(str.ptr)[ptr.off], ptr.len })) : WTF::String(WTF::StringImpl::createWithoutCopying( - { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); + { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); } static const WTF::String toStringCopy(ZigString str, StringPointer ptr) @@ -136,10 +147,15 @@ static const WTF::String toStringCopy(ZigString str, StringPointer ptr) return WTF::String::fromUTF8ReplacingInvalidSequences(std::span { &untag(str.ptr)[ptr.off], ptr.len }); } + // This will fail if the string is too long. Let's make it explicit instead of an ASSERT. + if (UNLIKELY(str.len > Bun__stringSyntheticAllocationLimit)) { + return {}; + } + return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::StringImpl::create(std::span { &untag(str.ptr)[ptr.off], ptr.len })) : WTF::String(WTF::StringImpl::create( - std::span { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); + std::span { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); } static const WTF::String toStringCopy(ZigString str) @@ -293,13 +309,15 @@ static const WTF::String toStringStatic(ZigString str) return WTF::String(AtomStringImpl::add(std::span { reinterpret_cast(untag(str.ptr)), str.len })); } - return WTF::String(AtomStringImpl::add( - std::span { reinterpret_cast(untag(str.ptr)), str.len })); + auto* untagged = untag(str.ptr); + ASSERT(untagged[str.len] == 0); + ASCIILiteral ascii = ASCIILiteral::fromLiteralUnsafe(reinterpret_cast(untagged)); + return WTF::String(ascii); } static JSC::JSValue getErrorInstance(const ZigString* str, JSC__JSGlobalObject* globalObject) { - WTF::String message = toStringCopy(*str); + WTF::String message = toString(*str); if (UNLIKELY(message.isNull() && str->len > 0)) { // pending exception while creating an error. return JSC::JSValue(); @@ -335,6 +353,16 @@ static JSC::JSValue getRangeErrorInstance(const ZigString* str, JSC__JSGlobalObj return JSC::JSValue(result); } +static const JSC::Identifier toIdentifier(ZigString str, JSC::JSGlobalObject* global) +{ + if (str.len == 0 || str.ptr == nullptr) { + return JSC::Identifier::EmptyIdentifier; + } + WTF::String wtfstr = Zig::isTaggedExternalPtr(str.ptr) ? toString(str) : Zig::toStringCopy(str); + JSC::Identifier id = JSC::Identifier::fromString(global->vm(), wtfstr); + return id; +} + }; // namespace Zig JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral message, ASCIILiteral syscall, int err); @@ -362,4 +390,4 @@ OutType* WebCoreCast(JSC__JSValue JSValue0) return reinterpret_cast(&jsdomURL->wrapped()); } -#pragma clang diagnostic pop \ No newline at end of file +#pragma clang diagnostic pop diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index 69d77263e0788..58c164909298a 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -7,6 +7,7 @@ #include "JavaScriptCore/JSGlobalObject.h" #include "JavaScriptCore/SourceCode.h" #include "js_native_api_types.h" +#include "napi_handle_scope.h" #include "helpers.h" #include @@ -33,8 +34,6 @@ #include #include #include -#include -#include #include #include #include @@ -46,7 +45,6 @@ #include #include "JSFFIFunction.h" #include -#include #include "napi.h" #include #include @@ -64,6 +62,8 @@ #include #include #include "CommonJSModuleRecord.h" +#include "wtf/text/ASCIIFastPath.h" +#include "JavaScriptCore/WeakInlines.h" // #include using namespace JSC; @@ -115,6 +115,15 @@ JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSOb // #include #define NAPI_OBJECT_EXPECTED napi_object_expected +static inline Zig::GlobalObject* defaultGlobalObject(napi_env env) +{ + if (env) { + return defaultGlobalObject(toJS(env)); + } + + return defaultGlobalObject(); +} + class NapiRefWeakHandleOwner final : public JSC::WeakHandleOwner { public: void finalize(JSC::Handle, void* context) final @@ -139,7 +148,7 @@ void NapiFinalizer::call(JSC::JSGlobalObject* globalObject, void* data) { if (this->finalize_cb) { NAPI_PREMABLE - this->finalize_cb(reinterpret_cast(globalObject), data, this->finalize_hint); + this->finalize_cb(toNapi(globalObject), data, this->finalize_hint); } } @@ -148,13 +157,8 @@ void NapiRef::ref() ++refCount; if (refCount == 1 && !weakValueRef.isClear()) { auto& vm = globalObject.get()->vm(); - if (weakValueRef.isString()) { - strongRef.set(vm, JSC::JSValue(weakValueRef.string())); - } else if (weakValueRef.isObject()) { - strongRef.set(vm, JSC::JSValue(weakValueRef.object())); - } else { - strongRef.set(vm, weakValueRef.primitive()); - } + strongRef.set(vm, weakValueRef.get()); + // isSet() will return always true after being set once // We cannot rely on isSet() to check if the value is set we need to use isClear() // .setString/.setObject/.setPrimitive will assert fail if called more than once (even after clear()) @@ -186,8 +190,6 @@ void NapiRef::clear() // class Reference // } -extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); - WTF_MAKE_ISO_ALLOCATED_IMPL(NapiRef); static uint32_t getPropertyAttributes(napi_property_attributes attributes_) @@ -220,6 +222,104 @@ static uint32_t getPropertyAttributes(napi_property_descriptor prop) return result; } +NapiWeakValue::~NapiWeakValue() +{ + clear(); +} + +void NapiWeakValue::clear() +{ + switch (m_tag) { + case WeakTypeTag::Cell: { + m_value.cell.clear(); + break; + } + case WeakTypeTag::String: { + m_value.string.clear(); + break; + } + default: { + break; + } + } + + m_tag = WeakTypeTag::NotSet; +} + +bool NapiWeakValue::isClear() const +{ + return m_tag == WeakTypeTag::NotSet; +} + +void NapiWeakValue::setPrimitive(JSValue value) +{ + switch (m_tag) { + case WeakTypeTag::Cell: { + m_value.cell.clear(); + break; + } + case WeakTypeTag::String: { + m_value.string.clear(); + break; + } + default: { + break; + } + } + m_tag = WeakTypeTag::Primitive; + m_value.primitive = value; +} + +void NapiWeakValue::set(JSValue value, WeakHandleOwner& owner, void* context) +{ + if (value.isCell()) { + auto* cell = value.asCell(); + if (cell->isString()) { + setString(jsCast(cell), owner, context); + } else { + setCell(cell, owner, context); + } + } else { + setPrimitive(value); + } +} + +void NapiWeakValue::setCell(JSCell* cell, WeakHandleOwner& owner, void* context) +{ + switch (m_tag) { + case WeakTypeTag::Cell: { + m_value.cell.clear(); + break; + } + case WeakTypeTag::String: { + m_value.string.clear(); + break; + } + default: { + break; + } + } + + m_value.cell = JSC::Weak(cell, &owner, context); + m_tag = WeakTypeTag::Cell; +} + +void NapiWeakValue::setString(JSString* string, WeakHandleOwner& owner, void* context) +{ + switch (m_tag) { + case WeakTypeTag::Cell: { + m_value.cell.clear(); + break; + } + default: { + break; + } + } + + m_value.string = JSC::Weak(string, &owner, context); + m_tag = WeakTypeTag::String; +} + class NAPICallFrame { public: NAPICallFrame(const JSC::ArgList args, void* dataPtr) @@ -275,10 +375,10 @@ class NAPICallFrame { // and receives the actual count of args. napi_value* argv, // [out] Array of values napi_value* this_arg, // [out] Receives the JS 'this' arg for the call - void** data) + void** data, Zig::GlobalObject* globalObject) { if (this_arg != nullptr) { - *this_arg = toNapi(callframe.thisValue()); + *this_arg = toNapi(callframe.thisValue(), globalObject); } if (data != nullptr) { @@ -304,7 +404,7 @@ class NAPICallFrame { if (overflow > 0) { while (overflow--) { - *argv = toNapi(jsUndefined()); + *argv = toNapi(jsUndefined(), globalObject); argv++; } } @@ -326,12 +426,12 @@ class NAPIFunction : public JSC::JSFunction { using Base = JSC::JSFunction; static constexpr unsigned StructureFlags = Base::StructureFlags; - static JSC::EncodedJSValue call(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe) + static JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue call(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe) { ASSERT(jsCast(callframe->jsCallee())); auto* function = static_cast(callframe->jsCallee()); auto* env = toNapi(globalObject); - auto* callback = reinterpret_cast(function->m_method.get()); + auto* callback = reinterpret_cast(function->m_method); JSC::VM& vm = globalObject->vm(); MarkedArgumentBufferWithSize<12> args; @@ -342,20 +442,21 @@ class NAPIFunction : public JSC::JSFunction { NAPICallFrame frame(JSC::ArgList(args), function->m_dataPtr); auto scope = DECLARE_THROW_SCOPE(vm); + Bun::NapiHandleScope handleScope(jsCast(globalObject)); auto result = callback(env, NAPICallFrame::toNapiCallbackInfo(frame)); RELEASE_AND_RETURN(scope, JSC::JSValue::encode(toJS(result))); } - NAPIFunction(JSC::VM& vm, JSC::NativeExecutable* exec, JSGlobalObject* globalObject, Structure* structure, JSC::NativeFunction method, void* dataPtr) + NAPIFunction(JSC::VM& vm, JSC::NativeExecutable* exec, JSGlobalObject* globalObject, Structure* structure, Zig::CFFIFunction method, void* dataPtr) : Base(vm, exec, globalObject, structure) , m_method(method) , m_dataPtr(dataPtr) { } - static NAPIFunction* create(JSC::VM& vm, Zig::GlobalObject* globalObject, unsigned length, const WTF::String& name, JSC::NativeFunction method, void* dataPtr) + static NAPIFunction* create(JSC::VM& vm, Zig::GlobalObject* globalObject, unsigned length, const WTF::String& name, Zig::CFFIFunction method, void* dataPtr) { auto* structure = globalObject->NAPIFunctionStructure(); @@ -366,7 +467,7 @@ class NAPIFunction : public JSC::JSFunction { } void* m_dataPtr = nullptr; - JSC::NativeFunction m_method = nullptr; + Zig::CFFIFunction m_method = nullptr; template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { @@ -430,7 +531,7 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t if (property.method) { JSC::JSValue value; - auto method = reinterpret_cast(property.method); + auto method = reinterpret_cast(property.method); auto* function = NAPIFunction::create(vm, globalObject, 1, propertyName.isSymbol() ? String() : propertyName.string(), method, dataPtr); value = JSC::JSValue(function); @@ -443,8 +544,8 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t JSC::JSObject* getter = nullptr; JSC::JSObject* setter = nullptr; - auto getterProperty = reinterpret_cast(property.getter); - auto setterProperty = reinterpret_cast(property.setter); + auto getterProperty = reinterpret_cast(property.getter); + auto setterProperty = reinterpret_cast(property.setter); if (getterProperty) { getter = NAPIFunction::create(vm, globalObject, 0, makeString("get "_s, propertyName.isSymbol() ? String() : propertyName.string()), getterProperty, dataPtr); @@ -580,10 +681,14 @@ extern "C" napi_status napi_get_property(napi_env env, napi_value object, { NAPI_PREMABLE - if (UNLIKELY(!result)) { + if (UNLIKELY(!result || !env)) { return napi_invalid_arg; } + if (UNLIKELY(!object)) { + return napi_object_expected; + } + auto globalObject = toJS(env); auto& vm = globalObject->vm(); @@ -596,7 +701,7 @@ extern "C" napi_status napi_get_property(napi_env env, napi_value object, auto keyProp = toJS(key); JSC::EnsureStillAliveScope ensureAlive2(keyProp); auto scope = DECLARE_CATCH_SCOPE(vm); - *result = toNapi(target->getIfPropertyExists(globalObject, keyProp.toPropertyKey(globalObject))); + *result = toNapi(target->getIfPropertyExists(globalObject, keyProp.toPropertyKey(globalObject)), globalObject); RETURN_IF_EXCEPTION(scope, napi_generic_failure); scope.clearException(); @@ -622,7 +727,7 @@ extern "C" napi_status napi_delete_property(napi_env env, napi_value object, RETURN_IF_EXCEPTION(scope, napi_generic_failure); if (LIKELY(result)) { - *result = toNapi(deleteResult); + *result = deleteResult; } scope.clearException(); @@ -647,7 +752,7 @@ extern "C" napi_status napi_has_own_property(napi_env env, napi_value object, auto keyProp = toJS(key); auto scope = DECLARE_CATCH_SCOPE(vm); - *result = toNapi(target->hasOwnProperty(globalObject, JSC::PropertyName(keyProp.toPropertyKey(globalObject)))); + *result = target->hasOwnProperty(globalObject, JSC::PropertyName(keyProp.toPropertyKey(globalObject))); RETURN_IF_EXCEPTION(scope, napi_generic_failure); scope.clearException(); @@ -667,7 +772,7 @@ extern "C" napi_status napi_set_named_property(napi_env env, napi_value object, return napi_object_expected; } - if (UNLIKELY(utf8name == nullptr || !*utf8name)) { + if (UNLIKELY(utf8name == nullptr || !*utf8name || !value)) { return napi_invalid_arg; } @@ -694,7 +799,7 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env, { NAPI_PREMABLE - JSC::JSGlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = toJS(env); if (UNLIKELY(!globalObject || !result)) { return napi_invalid_arg; } @@ -717,7 +822,7 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env, if (LIKELY(data && jsArrayBuffer->impl())) { *data = jsArrayBuffer->impl()->data(); } - *result = toNapi(jsArrayBuffer); + *result = toNapi(jsArrayBuffer, globalObject); return napi_ok; } @@ -725,12 +830,15 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env, // it doesn't copy the string // but it's only safe to use if we are not setting a property // because we can't guarantee the lifetime of it -#define PROPERTY_NAME_FROM_UTF8(identifierName) \ - size_t utf8Len = strlen(utf8name); \ - JSC::PropertyName identifierName = LIKELY(charactersAreAllASCII(std::span { reinterpret_cast(utf8name), utf8Len })) ? JSC::PropertyName(JSC::Identifier::fromString(vm, WTF::String(WTF::StringImpl::createWithoutCopying({ utf8name, utf8Len })))) : JSC::PropertyName(JSC::Identifier::fromString(vm, WTF::String::fromUTF8(utf8name))); +#define PROPERTY_NAME_FROM_UTF8(identifierName) \ + size_t utf8Len = strlen(utf8Name); \ + WTF::String nameString = LIKELY(WTF::charactersAreAllASCII(std::span { reinterpret_cast(utf8Name), utf8Len })) \ + ? WTF::String(WTF::StringImpl::createWithoutCopying({ utf8Name, utf8Len })) \ + : WTF::String::fromUTF8(utf8Name); \ + JSC::PropertyName identifierName = JSC::Identifier::fromString(vm, nameString); extern "C" napi_status napi_has_named_property(napi_env env, napi_value object, - const char* utf8name, + const char* utf8Name, bool* result) { NAPI_PREMABLE @@ -742,7 +850,7 @@ extern "C" napi_status napi_has_named_property(napi_env env, napi_value object, auto globalObject = toJS(env); auto& vm = globalObject->vm(); - auto* target = toJS(object).getObject(); + JSObject* target = toJS(object).getObject(); if (UNLIKELY(!target)) { return napi_object_expected; } @@ -750,14 +858,15 @@ extern "C" napi_status napi_has_named_property(napi_env env, napi_value object, PROPERTY_NAME_FROM_UTF8(name); auto scope = DECLARE_CATCH_SCOPE(vm); - *result = !!target->getIfPropertyExists(globalObject, name); + PropertySlot slot(target, PropertySlot::InternalMethodType::HasProperty); + *result = target->getPropertySlot(globalObject, name, slot); RETURN_IF_EXCEPTION(scope, napi_generic_failure); scope.clearException(); return napi_ok; } extern "C" napi_status napi_get_named_property(napi_env env, napi_value object, - const char* utf8name, + const char* utf8Name, napi_value* result) { NAPI_PREMABLE @@ -769,7 +878,7 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object, auto globalObject = toJS(env); auto& vm = globalObject->vm(); - auto* target = toJS(object).getObject(); + JSObject* target = toJS(object).getObject(); if (UNLIKELY(!target)) { return napi_object_expected; } @@ -777,7 +886,7 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object, PROPERTY_NAME_FROM_UTF8(name); auto scope = DECLARE_CATCH_SCOPE(vm); - *result = toNapi(target->getIfPropertyExists(globalObject, name)); + *result = toNapi(target->getIfPropertyExists(globalObject, name), globalObject); RETURN_IF_EXCEPTION(scope, napi_generic_failure); scope.clearException(); @@ -807,18 +916,14 @@ node_api_create_external_string_latin1(napi_env env, #if NAPI_VERBOSE printf("[napi] string finalize_callback\n"); #endif - finalize_callback(reinterpret_cast(Bun__getDefaultGlobal()), nullptr, hint); + finalize_callback(toNapi(defaultGlobalObject()), nullptr, hint); } }); - JSGlobalObject* globalObject = toJS(env); - // globalObject is allowed to be null here - if (UNLIKELY(!globalObject)) { - globalObject = Bun__getDefaultGlobal(); - } + Zig::GlobalObject* globalObject = toJS(env); JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl)); ensureStillAliveHere(out); - *result = toNapi(out); + *result = toNapi(out, globalObject); ensureStillAliveHere(out); return napi_ok; @@ -849,18 +954,14 @@ node_api_create_external_string_utf16(napi_env env, #endif if (finalize_callback) { - finalize_callback(reinterpret_cast(Bun__getDefaultGlobal()), nullptr, hint); + finalize_callback(toNapi(defaultGlobalObject()), nullptr, hint); } }); - JSGlobalObject* globalObject = toJS(env); - // globalObject is allowed to be null here - if (UNLIKELY(!globalObject)) { - globalObject = Bun__getDefaultGlobal(); - } + Zig::GlobalObject* globalObject = toJS(env); JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl)); ensureStillAliveHere(out); - *result = toNapi(out); + *result = toNapi(out, globalObject); ensureStillAliveHere(out); return napi_ok; @@ -868,11 +969,11 @@ node_api_create_external_string_utf16(napi_env env, extern "C" void napi_module_register(napi_module* mod) { - auto* globalObject = Bun__getDefaultGlobal(); + auto* globalObject = defaultGlobalObject(); JSC::VM& vm = globalObject->vm(); auto keyStr = WTF::String::fromUTF8(mod->nm_modname); globalObject->napiModuleRegisterCallCount++; - JSValue pendingNapiModule = globalObject->pendingNapiModule; + JSValue pendingNapiModule = globalObject->m_pendingNapiModuleAndExports[0].get(); JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject() : nullptr; @@ -886,7 +987,6 @@ extern "C" void napi_module_register(napi_module* mod) object = Bun::JSCommonJSModule::create(globalObject, keyStr, exportsObject, false, jsUndefined()); strongExportsObject = { vm, exportsObject }; } else { - globalObject->pendingNapiModule = JSC::JSValue(); JSValue exportsObject = object->getIfPropertyExists(globalObject, WebCore::builtinNames(vm).exportsPublicName()); RETURN_IF_EXCEPTION(scope, void()); @@ -897,23 +997,20 @@ extern "C" void napi_module_register(napi_module* mod) JSC::Strong strongObject = { vm, object }; - JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object))); + Bun::NapiHandleScope handleScope(globalObject); + JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object, globalObject))); RETURN_IF_EXCEPTION(scope, void()); if (resultValue.isEmpty()) { JSValue errorInstance = createError(globalObject, makeString("Node-API module \""_s, keyStr, "\" returned an error"_s)); - globalObject->pendingNapiModule = errorInstance; - vm.writeBarrier(globalObject, errorInstance); - EnsureStillAliveScope ensureAlive(globalObject->pendingNapiModule); + globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance); return; } if (!resultValue.isObject()) { JSValue errorInstance = createError(globalObject, makeString("Expected Node-API module \""_s, keyStr, "\" to return an exports object"_s)); - globalObject->pendingNapiModule = errorInstance; - vm.writeBarrier(globalObject, errorInstance); - EnsureStillAliveScope ensureAlive(globalObject->pendingNapiModule); + globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance); return; } @@ -924,7 +1021,7 @@ extern "C" void napi_module_register(napi_module* mod) strongObject->put(strongObject.get(), globalObject, WebCore::builtinNames(vm).exportsPublicName(), resultValue, slot); } - globalObject->pendingNapiModule = object; + globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, object); } extern "C" napi_status napi_wrap(napi_env env, @@ -967,7 +1064,8 @@ extern "C" napi_status napi_wrap(napi_env env, } auto* ref = new NapiRef(globalObject, 0); - ref->weakValueRef.setObject(value.getObject(), weakValueHandleOwner(), ref); + + ref->weakValueRef.set(value, weakValueHandleOwner(), ref); if (finalize_cb) { ref->finalizer.finalize_cb = finalize_cb; @@ -1069,11 +1167,11 @@ extern "C" napi_status napi_create_function(napi_env env, const char* utf8name, name = WTF::String::fromUTF8({ utf8name, length == NAPI_AUTO_LENGTH ? strlen(utf8name) : length }); } - auto method = reinterpret_cast(cb); + auto method = reinterpret_cast(cb); auto* function = NAPIFunction::create(vm, globalObject, length, name, method, data); ASSERT(function->isCallable()); - *result = toNapi(JSC::JSValue(function)); + *result = toNapi(JSC::JSValue(function), globalObject); return napi_ok; } @@ -1090,9 +1188,10 @@ extern "C" napi_status napi_get_cb_info( NAPI_PREMABLE JSC::CallFrame* callFrame = reinterpret_cast(cbinfo); + Zig::GlobalObject* globalObject = toJS(env); if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) { - NAPICallFrame::extract(*frame, argc, argv, this_arg, data); + NAPICallFrame::extract(*frame, argc, argv, this_arg, data, globalObject); return napi_ok; } @@ -1107,14 +1206,14 @@ extern "C" napi_status napi_get_cb_info( memcpy(argv, callFrame->addressOfArgumentsStart(), argsToCopy * sizeof(JSC::JSValue)); for (size_t i = outputArgsCount; i < inputArgsCount; i++) { - argv[i] = toNapi(JSC::jsUndefined()); + argv[i] = toNapi(JSC::jsUndefined(), globalObject); } } JSC::JSValue thisValue = callFrame->thisValue(); if (this_arg != nullptr) { - *this_arg = toNapi(thisValue); + *this_arg = toNapi(thisValue, globalObject); } if (data != nullptr) { @@ -1201,6 +1300,54 @@ napi_define_properties(napi_env env, napi_value object, size_t property_count, return napi_ok; } +static void throwErrorWithCode(JSC::JSGlobalObject* globalObject, const char* msg_utf8, const char* code_utf8, const WTF::Function& createError) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto message = msg_utf8 ? WTF::String::fromUTF8(msg_utf8) : String(); + auto code = msg_utf8 ? WTF::String::fromUTF8(code_utf8) : String(); + + auto* error = createError(globalObject, message); + if (!code.isEmpty()) { + error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), JSC::jsString(vm, code), 0); + } + + scope.throwException(globalObject, Exception::create(vm, error)); +} + +static JSValue createErrorForNapi(napi_env env, napi_value code, napi_value msg, const WTF::Function& constructor) +{ + auto* globalObject = toJS(env); + JSC::VM& vm = globalObject->vm(); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + + JSValue codeValue = toJS(code); + WTF::String message; + + if (msg) { + JSValue messageValue = toJS(msg); + message = messageValue.toWTFString(globalObject); + if (catchScope.exception()) { + catchScope.clearException(); + return {}; + } + } + + auto* error = constructor(globalObject, message); + + if (codeValue && error) { + error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0); + } + + if (catchScope.exception()) { + catchScope.clearException(); + return {}; + } + + return error; +} + extern "C" napi_status napi_throw_error(napi_env env, const char* code, const char* msg) @@ -1208,12 +1355,10 @@ extern "C" napi_status napi_throw_error(napi_env env, NAPI_PREMABLE Zig::GlobalObject* globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); + throwErrorWithCode(globalObject, msg, code, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + return JSC::createError(globalObject, message); + }); - auto message = msg != nullptr ? WTF::String::fromUTF8(msg) : "Error"_s; - auto error = JSC::createError(globalObject, message); - JSC::throwException(globalObject, throwScope, error); return napi_ok; } @@ -1223,13 +1368,13 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value, { NAPI_PREMABLE - if (UNLIKELY(!result)) { + if (UNLIKELY(!result || !env)) { return napi_invalid_arg; } JSC::JSValue val = toJS(value); - if (!val || !val.isObject()) { + if (!val || !val.isCell()) { return napi_object_expected; } @@ -1239,14 +1384,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value, if (initial_refcount > 0) { ref->strongRef.set(globalObject->vm(), val); } - // we dont have a finalizer but we can use the weak value to re-ref again or get the value until the GC is called - if (val.isString()) { - ref->weakValueRef.setString(val.toString(globalObject), weakValueHandleOwner(), ref); - } else if (val.isObject()) { - ref->weakValueRef.setObject(val.getObject(), weakValueHandleOwner(), ref); - } else { - ref->weakValueRef.setPrimitive(val); - } + ref->weakValueRef.set(val, weakValueHandleOwner(), ref); *result = toNapi(ref); @@ -1271,6 +1409,9 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object, napi_ref* result) { NAPI_PREMABLE + if (UNLIKELY(!env)) { + return napi_invalid_arg; + } Zig::GlobalObject* globalObject = toJS(env); JSC::VM& vm = globalObject->vm(); @@ -1294,6 +1435,10 @@ extern "C" napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) { NAPI_PREMABLE + if (UNLIKELY(!result || !env || !ref)) { + return napi_invalid_arg; + } + NapiRef* napiRef = toJS(ref); napiRef->unref(); if (LIKELY(result)) { @@ -1309,11 +1454,11 @@ extern "C" napi_status napi_get_reference_value(napi_env env, napi_ref ref, napi_value* result) { NAPI_PREMABLE - if (UNLIKELY(!result)) { + if (UNLIKELY(!result || !env || !ref)) { return napi_invalid_arg; } NapiRef* napiRef = toJS(ref); - *result = toNapi(napiRef->value()); + *result = toNapi(napiRef->value(), toJS(env)); return napi_ok; } @@ -1327,6 +1472,9 @@ extern "C" JSC__JSValue napi_get_reference_value_internal(NapiRef* napiRef) extern "C" napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) { + if (UNLIKELY(!result || !env || !ref)) { + return napi_invalid_arg; + } NAPI_PREMABLE NapiRef* napiRef = toJS(ref); napiRef->ref(); @@ -1339,6 +1487,9 @@ extern "C" napi_status napi_reference_ref(napi_env env, napi_ref ref, extern "C" napi_status napi_delete_reference(napi_env env, napi_ref ref) { NAPI_PREMABLE + if (UNLIKELY(!env || !ref)) { + return napi_invalid_arg; + } NapiRef* napiRef = toJS(ref); delete napiRef; return napi_ok; @@ -1367,7 +1518,6 @@ extern "C" napi_status napi_is_detached_arraybuffer(napi_env env, } auto arrayBuffer = jsArrayBuffer->impl(); - *result = arrayBuffer->isDetached(); return napi_ok; } @@ -1434,7 +1584,7 @@ extern "C" napi_status napi_get_and_clear_last_exception(napi_env env, } auto globalObject = toJS(env); - *result = toNapi(JSC::JSValue(globalObject->vm().lastException())); + *result = toNapi(JSC::JSValue(globalObject->vm().lastException()), globalObject); globalObject->vm().clearLastException(); return napi_ok; } @@ -1484,7 +1634,7 @@ extern "C" napi_status node_api_symbol_for(napi_env env, } auto description = WTF::String::fromUTF8({ utf8description, length == NAPI_AUTO_LENGTH ? strlen(utf8description) : length }); - *result = toNapi(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(description))); + *result = toNapi(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(description)), globalObject); return napi_ok; } @@ -1499,16 +1649,15 @@ extern "C" napi_status node_api_create_syntax_error(napi_env env, return napi_invalid_arg; } - JSValue messageValue = toJS(msg); - JSValue codeValue = toJS(code); - auto globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); - auto* err = messageValue && !messageValue.isUndefinedOrNull() ? createSyntaxError(globalObject, messageValue.toWTFString(globalObject)) : createSyntaxError(globalObject); - if (codeValue && !codeValue.isUndefinedOrNull()) { - err->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0); + auto err = createErrorForNapi(env, code, msg, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + return JSC::createSyntaxError(globalObject, message); + }); + + if (UNLIKELY(!err)) { + return napi_generic_failure; } - *result = reinterpret_cast(JSC::JSValue::encode(err)); + *result = toNapi(err, toJS(env)); return napi_ok; } @@ -1518,16 +1667,12 @@ extern "C" napi_status node_api_throw_syntax_error(napi_env env, { NAPI_PREMABLE - auto message = msg ? WTF::String::fromUTF8(msg) : String(); auto globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); - auto* err = createSyntaxError(globalObject, message); - if (code) { - err->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), JSC::jsString(vm, String::fromUTF8(code)), 0); - } - auto scope = DECLARE_THROW_SCOPE(vm); - scope.throwException(globalObject, err); + throwErrorWithCode(globalObject, msg, code, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + return JSC::createSyntaxError(globalObject, message); + }); + return napi_ok; } @@ -1537,12 +1682,10 @@ extern "C" napi_status napi_throw_type_error(napi_env env, const char* code, NAPI_PREMABLE Zig::GlobalObject* globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); + throwErrorWithCode(globalObject, msg, code, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + return JSC::createTypeError(globalObject, message); + }); - auto message = WTF::String::fromUTF8(msg); - auto error = JSC::createTypeError(globalObject, message); - JSC::throwException(globalObject, throwScope, error); return napi_ok; } @@ -1550,18 +1693,23 @@ extern "C" napi_status napi_create_type_error(napi_env env, napi_value code, napi_value msg, napi_value* result) { - Zig::GlobalObject* globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); + if (UNLIKELY(!result || !env)) { + return napi_invalid_arg; + } - JSC::JSValue codeValue = toJS(code); - JSC::JSValue messageValue = toJS(msg); + auto err = createErrorForNapi(env, code, msg, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + if (message.isEmpty()) { + return JSC::createTypeError(globalObject); + } - auto error = JSC::createTypeError(globalObject, messageValue.toWTFString(globalObject)); - if (codeValue) { - error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0); + return JSC::createTypeError(globalObject, message); + }); + + if (UNLIKELY(!err)) { + return napi_generic_failure; } - *result = reinterpret_cast(JSC::JSValue::encode(error)); + *result = toNapi(err, toJS(env)); return napi_ok; } @@ -1575,23 +1723,19 @@ extern "C" napi_status napi_create_error(napi_env env, napi_value code, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); - - JSC::JSValue codeValue = toJS(code); - JSC::JSValue messageValue = toJS(msg); + auto err = createErrorForNapi(env, code, msg, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + if (message.isEmpty()) { + return JSC::createError(globalObject, String("Error"_s)); + } - WTF::String message = messageValue.toWTFString(globalObject); - if (message.isEmpty()) { - message = "Error"_s; - } + return JSC::createError(globalObject, message); + }); - auto* error = JSC::createError(globalObject, message); - if (codeValue) { - error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0); + if (UNLIKELY(!err)) { + return napi_generic_failure; } - *result = reinterpret_cast(JSC::JSValue::encode(error)); + *result = toNapi(err, toJS(env)); return napi_ok; } extern "C" napi_status napi_throw_range_error(napi_env env, const char* code, @@ -1600,25 +1744,25 @@ extern "C" napi_status napi_throw_range_error(napi_env env, const char* code, NAPI_PREMABLE Zig::GlobalObject* globalObject = toJS(env); - JSC::VM& vm = globalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); + throwErrorWithCode(globalObject, msg, code, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + return JSC::createRangeError(globalObject, message); + }); - auto message = WTF::String::fromUTF8(msg); - auto error = JSC::createRangeError(globalObject, message); - JSC::throwException(globalObject, throwScope, error); return napi_ok; } extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value) { NAPI_PREMABLE + if (UNLIKELY(!env || !object_value)) { + return napi_invalid_arg; + } Zig::GlobalObject* globalObject = toJS(env); JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - JSC::EncodedJSValue encodedValue = reinterpret_cast(object_value); - JSC::JSValue value = JSC::JSValue::decode(encodedValue); + JSC::JSValue value = toJS(object_value); if (!value.isObject()) { return NAPI_OBJECT_EXPECTED; } @@ -1637,8 +1781,7 @@ extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value) JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - JSC::EncodedJSValue encodedValue = reinterpret_cast(object_value); - JSC::JSValue value = JSC::JSValue::decode(encodedValue); + JSC::JSValue value = toJS(object_value); if (UNLIKELY(!value.isObject())) { return NAPI_OBJECT_EXPECTED; @@ -1661,7 +1804,7 @@ extern "C" napi_status napi_get_global(napi_env env, napi_value* result) } Zig::GlobalObject* globalObject = toJS(env); - *result = reinterpret_cast(globalObject->globalThis()); + *result = toNapi(globalObject->globalThis(), globalObject); return napi_ok; } @@ -1675,16 +1818,18 @@ extern "C" napi_status napi_create_range_error(napi_env env, napi_value code, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + auto err = createErrorForNapi(env, code, msg, [](JSC::JSGlobalObject* globalObject, const WTF::String& message) { + if (message.isEmpty()) { + return JSC::createRangeError(globalObject, String("Range error"_s)); + } - JSC::JSValue codeValue = toJS(code); - JSC::JSValue messageValue = toJS(msg); + return JSC::createRangeError(globalObject, message); + }); - auto error = JSC::createRangeError(globalObject, messageValue.toWTFString(globalObject)); - if (codeValue) { - error->putDirect(globalObject->vm(), WebCore::builtinNames(globalObject->vm()).codePublicName(), codeValue, 0); + if (UNLIKELY(!err)) { + return napi_generic_failure; } - *result = reinterpret_cast(error); + *result = toNapi(err, toJS(env)); return napi_ok; } @@ -1703,12 +1848,12 @@ extern "C" napi_status napi_get_new_target(napi_env env, CallFrame* callFrame = reinterpret_cast(cbinfo); if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) { - *result = toNapi(frame->newTarget); + *result = toNapi(frame->newTarget, toJS(env)); return napi_ok; } JSC::JSValue newTarget = callFrame->newTarget(); - *result = reinterpret_cast(JSC::JSValue::encode(newTarget)); + *result = toNapi(newTarget, toJS(env)); return napi_ok; } @@ -1727,14 +1872,14 @@ extern "C" napi_status napi_create_dataview(napi_env env, size_t length, JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - JSC::EncodedJSValue encodedArraybuffer = reinterpret_cast(arraybuffer); - auto arraybufferValue = JSC::jsDynamicCast(JSC::JSValue::decode(encodedArraybuffer)); - if (!arraybufferValue) { + JSC::JSValue arraybufferValue = toJS(arraybuffer); + auto arraybufferPtr = JSC::jsDynamicCast(arraybufferValue); + if (!arraybufferPtr) { return napi_arraybuffer_expected; } - auto dataView = JSC::DataView::create(arraybufferValue->impl(), byte_offset, length); + auto dataView = JSC::DataView::create(arraybufferPtr->impl(), byte_offset, length); - *result = reinterpret_cast(dataView->wrap(globalObject, globalObject)); + *result = toNapi(dataView->wrap(globalObject, globalObject), globalObject); return napi_ok; } @@ -1787,6 +1932,7 @@ JSC_DEFINE_HOST_FUNCTION(NapiClass_ConstructorFunction, }); NAPICallFrame frame(JSC::ArgList(args), nullptr); frame.newTarget = newTarget; + Bun::NapiHandleScope handleScope(jsCast(globalObject)); napi->constructor()(globalObject, reinterpret_cast(NAPICallFrame::toNapiCallbackInfo(frame))); RETURN_IF_EXCEPTION(scope, {}); @@ -1815,7 +1961,7 @@ void NapiClass::finishCreation(VM& vm, NativeExecutable* executable, unsigned le { Base::finishCreation(vm, executable, length, name); ASSERT(inherits(info())); - this->m_constructor = reinterpret_cast(constructor); + this->m_constructor = reinterpret_cast(constructor); auto globalObject = reinterpret_cast(this->globalObject()); this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | 0); @@ -1874,7 +2020,7 @@ extern "C" napi_status napi_get_all_property_names( JSC::JSArray* exportKeys = ownPropertyKeys(globalObject, object, jsc_property_mode, jsc_key_mode); // TODO: filter - *result = toNapi(JSC::JSValue::encode(exportKeys)); + *result = toNapi(JSC::JSValue(exportKeys), globalObject); return napi_ok; } @@ -1946,7 +2092,7 @@ extern "C" napi_status napi_define_class(napi_env env, napiClass->dataPtr = data; } - *result = toNapi(value); + *result = toNapi(value, globalObject); return napi_ok; } @@ -1968,10 +2114,10 @@ extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value, // .toString() can throw JSC::JSValue resultValue = JSC::JSValue(jsValue.toString(globalObject)); JSC::EnsureStillAliveScope ensureStillAlive1(resultValue); - *result = toNapi(resultValue); + *result = toNapi(resultValue, globalObject); if (UNLIKELY(scope.exception())) { - *result = reinterpret_cast(JSC::JSValue::encode(JSC::jsUndefined())); + *result = toNapi(JSC::jsUndefined(), globalObject); return napi_generic_failure; } scope.clearException(); @@ -1999,13 +2145,13 @@ extern "C" napi_status napi_get_property_names(napi_env env, napi_value object, JSC::EnsureStillAliveScope ensureStillAlive(jsValue); JSC::JSValue value = JSC::ownPropertyKeys(globalObject, jsValue.getObject(), PropertyNameMode::Strings, DontEnumPropertiesMode::Include); if (UNLIKELY(scope.exception())) { - *result = reinterpret_cast(JSC::JSValue::encode(JSC::jsUndefined())); + *result = toNapi(JSC::jsUndefined(), globalObject); return napi_generic_failure; } scope.clearException(); JSC::EnsureStillAliveScope ensureStillAlive1(value); - *result = toNapi(value); + *result = toNapi(value, globalObject); return napi_ok; } @@ -2023,7 +2169,7 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length, Zig::GlobalObject* globalObject = toJS(env); - auto arrayBuffer = ArrayBuffer::createFromBytes(data, length, createSharedTask([globalObject, finalize_hint, finalize_cb](void* p) { + auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(data), length }, createSharedTask([globalObject, finalize_hint, finalize_cb](void* p) { #if NAPI_VERBOSE printf("[napi] buffer finalize_callback\n"); #endif @@ -2035,7 +2181,7 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length, auto* buffer = JSC::JSUint8Array::create(globalObject, subclassStructure, WTFMove(arrayBuffer), 0, length); - *result = toNapi(buffer); + *result = toNapi(buffer, globalObject); return napi_ok; } @@ -2051,7 +2197,7 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte Zig::GlobalObject* globalObject = toJS(env); JSC::VM& vm = globalObject->vm(); - auto arrayBuffer = ArrayBuffer::createFromBytes(external_data, byte_length, createSharedTask([globalObject, finalize_hint, finalize_cb](void* p) { + auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(external_data), byte_length }, createSharedTask([globalObject, finalize_hint, finalize_cb](void* p) { #if NAPI_VERBOSE printf("[napi] arraybuffer finalize_callback\n"); #endif @@ -2062,7 +2208,7 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte auto* buffer = JSC::JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(ArrayBufferSharingMode::Shared), WTFMove(arrayBuffer)); - *result = toNapi(buffer); + *result = toNapi(buffer, globalObject); return napi_ok; } @@ -2075,7 +2221,7 @@ extern "C" napi_status napi_create_double(napi_env env, double value, return napi_invalid_arg; } - *result = toNapi(jsDoubleNumber(value)); + *result = toNapi(jsDoubleNumber(value), toJS(env)); return napi_ok; } @@ -2127,8 +2273,7 @@ extern "C" napi_status napi_get_value_string_utf8(napi_env env, } size_t length = jsString->length(); - auto viewWithUnderlyingString = jsString->viewWithUnderlyingString(globalObject); - auto view = viewWithUnderlyingString.view; + String view = jsString->value(globalObject); if (buf == nullptr) { if (writtenPtr != nullptr) { @@ -2192,7 +2337,7 @@ extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue, JSValue element = object->getIndex(toJS(env), index); RETURN_IF_EXCEPTION(scope, napi_generic_failure); - *result = toNapi(element); + *result = toNapi(element, toJS(env)); return napi_ok; } @@ -2231,7 +2376,7 @@ extern "C" napi_status napi_create_object(napi_env env, napi_value* result) JSValue value = JSValue(NapiPrototype::create(vm, globalObject->NapiPrototypeStructure())); - *result = toNapi(value); + *result = toNapi(value, globalObject); JSC::EnsureStillAliveScope ensureStillAlive(value); return napi_ok; @@ -2252,7 +2397,7 @@ extern "C" napi_status napi_create_external(napi_env env, void* data, auto* structure = globalObject->NapiExternalStructure(); JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, reinterpret_cast(finalize_cb)); JSC::EnsureStillAliveScope ensureStillAlive(value); - *result = toNapi(value); + *result = toNapi(value, globalObject); return napi_ok; } @@ -2427,7 +2572,7 @@ extern "C" napi_status napi_run_script(napi_env env, napi_value script, { NAPI_PREMABLE - JSC::JSGlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = toJS(env); if (UNLIKELY(result == nullptr)) { return napi_invalid_arg; } @@ -2455,7 +2600,7 @@ extern "C" napi_status napi_run_script(napi_env env, napi_value script, } if (result != nullptr) { - *result = toNapi(value); + *result = toNapi(value, globalObject); } RELEASE_AND_RETURN(throwScope, napi_ok); @@ -2469,8 +2614,7 @@ extern "C" napi_status napi_set_instance_data(napi_env env, NAPI_PREMABLE Zig::GlobalObject* globalObject = toJS(env); - if (data) - globalObject->napiInstanceData = data; + globalObject->napiInstanceData = data; globalObject->napiInstanceDataFinalizer = reinterpret_cast(finalize_cb); globalObject->napiInstanceDataFinalizerHint = finalize_hint; @@ -2509,7 +2653,7 @@ extern "C" napi_status napi_create_bigint_words(napi_env env, } } - *result = toNapi(bigint); + *result = toNapi(bigint, globalObject); return napi_ok; } @@ -2537,15 +2681,52 @@ extern "C" napi_status napi_create_symbol(napi_env env, napi_value description, } if (descriptionString->length() > 0) { - *result = toNapi(JSC::Symbol::createWithDescription(vm, descriptionString->value(globalObject))); + *result = toNapi(JSC::Symbol::createWithDescription(vm, descriptionString->value(globalObject)), + globalObject); return napi_ok; } } - *result = toNapi(JSC::Symbol::create(vm)); + *result = toNapi(JSC::Symbol::create(vm), globalObject); return napi_ok; } +// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/js_native_api_v8.cc#L2904-L2930 +extern "C" napi_status napi_new_instance(napi_env env, napi_value constructor, + size_t argc, const napi_value* argv, + napi_value* result) +{ + NAPI_PREMABLE + if (UNLIKELY(!result)) { + return napi_invalid_arg; + } + + Zig::GlobalObject* globalObject = toJS(env); + JSC::VM& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSC::JSValue constructorValue = toJS(constructor); + if (UNLIKELY(!constructorValue.isObject())) { + return napi_function_expected; + } + + JSC::JSObject* constructorObject = constructorValue.getObject(); + JSC::CallData constructData = getConstructData(constructorObject); + if (UNLIKELY(constructData.type == JSC::CallData::Type::None)) { + return napi_function_expected; + } + + JSC::MarkedArgumentBuffer args; + args.fill(vm, argc, [&](auto* slot) { + memcpy(slot, argv, sizeof(JSC::JSValue) * argc); + }); + + auto value = construct(globalObject, constructorObject, constructData, args); + RETURN_IF_EXCEPTION(throwScope, napi_pending_exception); + *result = toNapi(value, globalObject); + + RELEASE_AND_RETURN(throwScope, napi_ok); +} extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi, napi_value func_napi, size_t argc, const napi_value* argv, @@ -2582,9 +2763,9 @@ extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi, if (result_ptr) { if (result.isEmpty()) { - *result_ptr = toNapi(JSC::jsUndefined()); + *result_ptr = toNapi(JSC::jsUndefined(), globalObject); } else { - *result_ptr = toNapi(result); + *result_ptr = toNapi(result, globalObject); } } diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index c0116537382cc..e78badd495945 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -1,9 +1,5 @@ #pragma once -namespace Zig { -class GlobalObject; -} - #include "root.h" #include #include @@ -14,6 +10,8 @@ class GlobalObject; #include "js_native_api_types.h" #include #include "JSFFIFunction.h" +#include "ZigGlobalObject.h" +#include "napi_handle_scope.h" namespace JSC { class JSGlobalObject; @@ -38,14 +36,14 @@ static inline Zig::GlobalObject* toJS(napi_env val) return reinterpret_cast(val); } -static inline napi_value toNapi(JSC::EncodedJSValue val) -{ - return reinterpret_cast(val); -} - -static inline napi_value toNapi(JSC::JSValue val) +static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObject) { - return toNapi(JSC::JSValue::encode(val)); + if (val.isCell()) { + if (auto* scope = globalObject->m_currentNapiHandleScopeImpl.get()) { + scope->append(val); + } + } + return reinterpret_cast(JSC::JSValue::encode(val)); } static inline napi_env toNapi(JSC::JSGlobalObject* val) @@ -61,6 +59,86 @@ class NapiFinalizer { void call(JSC::JSGlobalObject* globalObject, void* data); }; +// This is essentially JSC::JSWeakValue, except with a JSCell* instead of a +// JSObject*. Sometimes, a napi embedder might want to store a JSC::Exception, a +// JSC::HeapBigInt, JSC::Symbol, etc inside of a NapiRef. So we can't limit it +// to just JSObject*. It has to be JSCell*. It's not clear that we benefit from +// not simply making this JSC::Unknown. +class NapiWeakValue { +public: + NapiWeakValue() = default; + ~NapiWeakValue(); + + void clear(); + bool isClear() const; + + bool isSet() const { return m_tag != WeakTypeTag::NotSet; } + bool isPrimitive() const { return m_tag == WeakTypeTag::Primitive; } + bool isCell() const { return m_tag == WeakTypeTag::Cell; } + bool isString() const { return m_tag == WeakTypeTag::String; } + + void setPrimitive(JSValue); + void setCell(JSCell*, WeakHandleOwner&, void* context); + void setString(JSString*, WeakHandleOwner&, void* context); + void set(JSValue, WeakHandleOwner&, void* context); + + JSValue get() const + { + switch (m_tag) { + case WeakTypeTag::Primitive: + return m_value.primitive; + case WeakTypeTag::Cell: + return JSC::JSValue(m_value.cell.get()); + case WeakTypeTag::String: + return JSC::JSValue(m_value.string.get()); + default: + return JSC::JSValue(); + } + } + + JSCell* cell() const + { + ASSERT(isCell()); + return m_value.cell.get(); + } + + JSValue primitive() const + { + ASSERT(isPrimitive()); + return m_value.primitive; + } + + JSString* string() const + { + ASSERT(isString()); + return m_value.string.get(); + } + +private: + enum class WeakTypeTag { NotSet, + Primitive, + Cell, + String }; + + WeakTypeTag m_tag { WeakTypeTag::NotSet }; + + union WeakValueUnion { + WeakValueUnion() + : primitive(JSValue()) + { + } + + ~WeakValueUnion() + { + ASSERT(!primitive); + } + + JSValue primitive; + JSC::Weak cell; + JSC::Weak string; + } m_value; +}; + class NapiRef { WTF_MAKE_ISO_ALLOCATED(NapiRef); @@ -80,22 +158,7 @@ class NapiRef { JSC::JSValue value() const { if (refCount == 0) { - // isSet() can return true even if the value was cleared - // so we must check if the value is clear - // if the value is unset, isClear() will return true - if (weakValueRef.isClear()) { - return JSC::JSValue {}; - } - - if (weakValueRef.isString()) { - return JSC::JSValue(weakValueRef.string()); - } - - if (weakValueRef.isObject()) { - return JSC::JSValue(weakValueRef.object()); - } - - return weakValueRef.primitive(); + return weakValueRef.get(); } return strongRef.get(); @@ -110,7 +173,7 @@ class NapiRef { } JSC::Weak globalObject; - JSC::JSWeakValue weakValueRef; + NapiWeakValue weakValueRef; JSC::Strong strongRef; NapiFinalizer finalizer; void* data = nullptr; @@ -160,13 +223,13 @@ class NapiClass final : public JSC::JSFunction { return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info()); } - FFIFunction constructor() + CFFIFunction constructor() { return m_constructor; } void* dataPtr = nullptr; - FFIFunction m_constructor = nullptr; + CFFIFunction m_constructor = nullptr; NapiRef* napiRef = nullptr; private: @@ -247,4 +310,4 @@ static inline NapiRef* toJS(napi_ref val) Structure* createNAPIFunctionStructure(VM& vm, JSC::JSGlobalObject* globalObject); -} \ No newline at end of file +} diff --git a/src/bun.js/bindings/napi_external.cpp b/src/bun.js/bindings/napi_external.cpp index bffa96cf6dcad..3780ebc210cf2 100644 --- a/src/bun.js/bindings/napi_external.cpp +++ b/src/bun.js/bindings/napi_external.cpp @@ -6,7 +6,9 @@ namespace Bun { NapiExternal::~NapiExternal() { if (finalizer) { - reinterpret_cast(finalizer)(toNapi(globalObject()), m_value, m_finalizerHint); + // We cannot call globalObject() here because it is in a finalizer. + // https://github.com/oven-sh/bun/issues/13001#issuecomment-2290022312 + reinterpret_cast(finalizer)(toNapi(this->napi_env), m_value, m_finalizerHint); } } diff --git a/src/bun.js/bindings/napi_external.h b/src/bun.js/bindings/napi_external.h index c82e7f597f0bd..a9e38676dca7e 100644 --- a/src/bun.js/bindings/napi_external.h +++ b/src/bun.js/bindings/napi_external.h @@ -80,6 +80,7 @@ class NapiExternal : public JSC::JSDestructibleObject { Base::finishCreation(vm); m_value = value; m_finalizerHint = finalizer_hint; + napi_env = this->globalObject(); this->finalizer = finalizer; } @@ -90,6 +91,7 @@ class NapiExternal : public JSC::JSDestructibleObject { void* m_value; void* m_finalizerHint; void* finalizer; + JSGlobalObject* napi_env; #if BUN_DEBUG String sourceOriginURL = String(); diff --git a/src/bun.js/bindings/napi_handle_scope.cpp b/src/bun.js/bindings/napi_handle_scope.cpp new file mode 100644 index 0000000000000..b23d8bdfdece2 --- /dev/null +++ b/src/bun.js/bindings/napi_handle_scope.cpp @@ -0,0 +1,146 @@ +#include "napi_handle_scope.h" + +#include "ZigGlobalObject.h" + +namespace Bun { + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const JSC::ClassInfo NapiHandleScopeImpl::s_info = { + "NapiHandleScopeImpl"_s, + nullptr, + nullptr, + nullptr, + CREATE_METHOD_TABLE(NapiHandleScopeImpl) +}; + +NapiHandleScopeImpl::NapiHandleScopeImpl(JSC::VM& vm, JSC::Structure* structure, NapiHandleScopeImpl* parent, bool escapable) + : Base(vm, structure) + , m_parent(parent) + , m_escapeSlot(nullptr) +{ + if (escapable) { + m_escapeSlot = parent->reserveSlot(); + } +} + +NapiHandleScopeImpl* NapiHandleScopeImpl::create(JSC::VM& vm, + JSC::Structure* structure, + NapiHandleScopeImpl* parent, + bool escapable) +{ + NapiHandleScopeImpl* buffer = new (NotNull, JSC::allocateCell(vm)) + NapiHandleScopeImpl(vm, structure, parent, escapable); + buffer->finishCreation(vm); + return buffer; +} + +template +void NapiHandleScopeImpl::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + NapiHandleScopeImpl* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + WTF::Locker locker { thisObject->cellLock() }; + + for (auto& handle : thisObject->m_storage) { + visitor.append(handle); + } + + if (thisObject->m_parent) { + visitor.appendUnbarriered(thisObject->m_parent); + } +} + +DEFINE_VISIT_CHILDREN(NapiHandleScopeImpl); + +void NapiHandleScopeImpl::append(JSC::JSValue val) +{ + m_storage.append(Slot(vm(), this, val)); +} + +bool NapiHandleScopeImpl::escape(JSC::JSValue val) +{ + if (!m_escapeSlot) { + return false; + } + + m_escapeSlot->set(vm(), m_parent, val); + m_escapeSlot = nullptr; + return true; +} + +NapiHandleScopeImpl::Slot* NapiHandleScopeImpl::reserveSlot() +{ + m_storage.append(Slot()); + return &m_storage.last(); +} + +NapiHandleScopeImpl* NapiHandleScope::push(Zig::GlobalObject* globalObject, bool escapable) +{ + auto& vm = globalObject->vm(); + // Do not create a new handle scope while a finalizer is in progress + // This state is possible because we call napi finalizers immediately + // so a finalizer can be called while an allocation is in progress. + // An example where this happens: + // 1. Use the `sqlite3` package + // 2. Do an allocation in a hot code path + // 3. the napi_ref finalizer is called while the constructor is running + // 4. The finalizer creates a new handle scope (yes, it should not do that. No, we can't change that.) + if (vm.heap.mutatorState() == JSC::MutatorState::Sweeping) { + return nullptr; + } + + auto* impl = NapiHandleScopeImpl::create(vm, + globalObject->NapiHandleScopeImplStructure(), + globalObject->m_currentNapiHandleScopeImpl.get(), + escapable); + globalObject->m_currentNapiHandleScopeImpl.set(vm, globalObject, impl); + return impl; +} + +void NapiHandleScope::pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current) +{ + RELEASE_ASSERT_WITH_MESSAGE(current == globalObject->m_currentNapiHandleScopeImpl.get(), + "Unbalanced napi_handle_scope opens and closes"); + if (auto* parent = current->parent()) { + globalObject->m_currentNapiHandleScopeImpl.set(globalObject->vm(), globalObject, parent); + } else { + globalObject->m_currentNapiHandleScopeImpl.clear(); + } +} + +NapiHandleScope::NapiHandleScope(Zig::GlobalObject* globalObject) + : m_globalObject(globalObject) + , m_impl(NapiHandleScope::push(globalObject, false)) +{ +} + +NapiHandleScope::~NapiHandleScope() +{ + NapiHandleScope::pop(m_globalObject, m_impl); +} + +extern "C" NapiHandleScopeImpl* NapiHandleScope__push(Zig::GlobalObject* globalObject, bool escapable) +{ + return NapiHandleScope::push(globalObject, escapable); +} + +extern "C" void NapiHandleScope__pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current) +{ + return NapiHandleScope::pop(globalObject, current); +} + +extern "C" void NapiHandleScope__append(Zig::GlobalObject* globalObject, JSC::EncodedJSValue value) +{ + globalObject->m_currentNapiHandleScopeImpl.get()->append(JSC::JSValue::decode(value)); +} + +extern "C" bool NapiHandleScope__escape(NapiHandleScopeImpl* handleScope, JSC::EncodedJSValue value) +{ + return handleScope->escape(JSC::JSValue::decode(value)); +} + +} // namespace Bun diff --git a/src/bun.js/bindings/napi_handle_scope.h b/src/bun.js/bindings/napi_handle_scope.h new file mode 100644 index 0000000000000..891dbbb9de424 --- /dev/null +++ b/src/bun.js/bindings/napi_handle_scope.h @@ -0,0 +1,97 @@ +#pragma once + +#include "BunClientData.h" +#include "root.h" + +namespace Bun { + +// An array of write barriers (so that newly-added objects are not lost by GC) to JSValues. Unlike +// the V8 version, pointer stability is not required (because napi_values don't point into this +// structure) so we can use a regular WTF::Vector +// +// Don't use this directly, use NapiHandleScope. Most NAPI functions won't even need to use that as +// a handle scope is created before calling a native function. +class NapiHandleScopeImpl : public JSC::JSCell { +public: + using Base = JSC::JSCell; + + static NapiHandleScopeImpl* create( + JSC::VM& vm, + JSC::Structure* structure, + NapiHandleScopeImpl* parent, + bool escapable = false); + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) + { + return JSC::Structure::create(vm, globalObject, JSC::jsNull(), JSC::TypeInfo(JSC::CellType, StructureFlags), info(), 0, 0); + } + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForNapiHandleScopeImpl.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNapiHandleScopeImpl = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForNapiHandleScopeImpl.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForNapiHandleScopeImpl = std::forward(space); }); + } + + DECLARE_INFO; + DECLARE_VISIT_CHILDREN; + + // Store val in the handle scope + void append(JSC::JSValue val); + NapiHandleScopeImpl* parent() const { return m_parent; } + // Returns false if this handle scope is not escapable or if it is but escape() has already + // been called + bool escape(JSC::JSValue val); + +private: + using Slot = JSC::WriteBarrier; + + NapiHandleScopeImpl* m_parent; + WTF::Vector m_storage; + Slot* m_escapeSlot; + + Slot* reserveSlot(); + + NapiHandleScopeImpl(JSC::VM& vm, JSC::Structure* structure, NapiHandleScopeImpl* parent, bool escapable); +}; + +// Wrapper class used to push a new handle scope and pop it when this instance goes out of scope +class NapiHandleScope { +public: + NapiHandleScope(Zig::GlobalObject* globalObject); + ~NapiHandleScope(); + + // Create a new handle scope in the given environment + static NapiHandleScopeImpl* push(Zig::GlobalObject* globalObject, bool escapable); + + // Pop the most recently created handle scope in the given environment and restore the old one. + // Asserts that `current` is the active handle scope. + static void pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current); + +private: + NapiHandleScopeImpl* m_impl; + Zig::GlobalObject* m_globalObject; +}; + +// Create a new handle scope in the given environment +extern "C" NapiHandleScopeImpl* NapiHandleScope__push(Zig::GlobalObject* globalObject, bool escapable); + +// Pop the most recently created handle scope in the given environment and restore the old one. +// Asserts that `current` is the active handle scope. +extern "C" void NapiHandleScope__pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current); + +// Store a value in the active handle scope in the given environment +extern "C" void NapiHandleScope__append(Zig::GlobalObject* globalObject, JSC::EncodedJSValue value); + +// Put a value from the current handle scope into its escape slot reserved in the outer handle +// scope. Returns false if the current handle scope is not escapable or if escape has already been +// called on it. +extern "C" bool NapiHandleScope__escape(NapiHandleScopeImpl* handle_scope, JSC::EncodedJSValue value); + +} // namespace Bun diff --git a/src/bun.js/bindings/root.h b/src/bun.js/bindings/root.h index 2bb8253a81616..0fb85524cab44 100644 --- a/src/bun.js/bindings/root.h +++ b/src/bun.js/bindings/root.h @@ -8,6 +8,12 @@ #error "root.h must be included before any other WebCore or JavaScriptCore headers" #endif +#if defined(WIN32) || defined(_WIN32) +#define BUN_EXPORT __declspec(dllexport) +#else +#define BUN_EXPORT JS_EXPORT +#endif + /* * Copyright (C) 2006-2021 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig "sam.weinig@gmail.com" @@ -69,6 +75,7 @@ #include #include +#include #include #include #include @@ -78,4 +85,12 @@ #define USE_OPENSSL 1 #define HAVE_RSA_PSS 1 -#endif \ No newline at end of file +#if OS(WINDOWS) +#define BUN_DECLARE_HOST_FUNCTION(name) extern "C" __attribute__((visibility("default"))) JSC_DECLARE_HOST_FUNCTION(name) +#define BUN_DEFINE_HOST_FUNCTION(name, args) extern "C" __attribute__((visibility("default"))) JSC_DEFINE_HOST_FUNCTION(name, args) +#else +#define BUN_DECLARE_HOST_FUNCTION(name) extern "C" JSC_DECLARE_HOST_FUNCTION(name) +#define BUN_DEFINE_HOST_FUNCTION(name, args) extern "C" JSC_DEFINE_HOST_FUNCTION(name, args) +#endif + +#endif diff --git a/src/bun.js/bindings/shimmer.zig b/src/bun.js/bindings/shimmer.zig index 1c83268481997..307294918ef41 100644 --- a/src/bun.js/bindings/shimmer.zig +++ b/src/bun.js/bindings/shimmer.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const bun = @import("root").bun; const StaticExport = @import("./static_export.zig"); const Sizes = @import("./sizes.zig"); pub const is_bindgen: bool = false; @@ -15,6 +16,14 @@ pub fn Shimmer(comptime _namespace: []const u8, comptime _name: []const u8, comp pub const namespace = _namespace; pub const name = _name; + pub fn assertJSFunction(comptime funcs: anytype) void { + inline for (funcs) |func| { + if (@typeInfo(@TypeOf(func)) != .Fn) { + @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(func) ++ " to be a function but received " ++ @tagName(@typeInfo(@TypeOf(func)))); + } + } + } + pub fn ref() void { if (comptime @hasDecl(Parent, "Export")) { inline for (Parent.Export) |exp| { @@ -111,7 +120,9 @@ pub fn Shimmer(comptime _namespace: []const u8, comptime _name: []const u8, comp @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(Function) ++ " to be a function but received " ++ @tagName(@typeInfo(Function))); } const Fn: std.builtin.Type.Fn = @typeInfo(Function).Fn; - if (Fn.calling_convention != .C) { + if (Function == bun.JSC.JSHostFunctionTypeWithCCallConvForAssertions and bun.JSC.conv != .C) { + @compileError("Expected " ++ bun.meta.typeName(Function) ++ " to have a JSC.conv Calling Convention."); + } else if (Function == bun.JSC.JSHostFunctionType) {} else if (Fn.calling_convention != .C) { @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(Function) ++ " to have a C Calling Convention."); } diff --git a/src/bun.js/bindings/simdutf.cpp b/src/bun.js/bindings/simdutf.cpp deleted file mode 100644 index e4650b90b25ee..0000000000000 --- a/src/bun.js/bindings/simdutf.cpp +++ /dev/null @@ -1,38583 +0,0 @@ -// clang-format off -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wall" -/* auto-generated on 2024-05-07 22:33:11 -0400. Do not edit! */ -/* begin file src/simdutf.cpp */ -#include "simdutf.h" -// We include base64_tables once. -/* begin file src/tables/base64_tables.h */ -#ifndef SIMDUTF_BASE64_TABLES_H -#define SIMDUTF_BASE64_TABLES_H -#include -#include - -namespace simdutf { -namespace { -namespace tables { -namespace base64 { -namespace base64_default { - -const char e0[256] = { - 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', - 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', - 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', - 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', - 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', - 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', - 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', - 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', - 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', - 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', - 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', - 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', - 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', - 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', - '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', - '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', - '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', '+', '+', '/', '/', '/', - '/'}; - -const char e1[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', - '/'}; - -const char e2[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', - '/'}; - -const uint32_t d0[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, - 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, - 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, - 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, - 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, - 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, - 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, - 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, - 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, - 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, - 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; - -const uint32_t d1[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, - 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, - 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, - 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, - 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, - 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, - 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, - 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, - 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, - 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, - 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; - -const uint32_t d2[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, - 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, - 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, - 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, - 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, - 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, - 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, - 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, - 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, - 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, - 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; - -const uint32_t d3[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, - 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, - 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, - 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, - 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, - 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, - 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, - 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, - 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, - 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, - 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -} // namespace base64_default - -namespace base64_url { - -const char e0[256] = { - 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', - 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', - 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', - 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', - 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', - 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', - 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', - 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', - 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', - 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', - 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', - 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', - 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', - 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', - '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', - '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', - '8', '8', '8', '8', '9', '9', '9', '9', '-', '-', '-', '-', '_', '_', '_', - '_'}; - -const char e1[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', - '_'}; - -const char e2[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', - '_'}; - -const uint32_t d0[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, - 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, - 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, - 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, - 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, - 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, - 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, - 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, - 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, - 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, - 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, - 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d1[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, - 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, - 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, - 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, - 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, - 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, - 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, - 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, - 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, - 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, - 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, - 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d2[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, - 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, - 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, - 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, - 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, - 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, - 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, - 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, - 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, - 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, - 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, - 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d3[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, - 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, - 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, - 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, - 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, - 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, - 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, - 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, - 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, - 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, - 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, - 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -} // namespace base64_url -const uint64_t thintable_epi8[256] = { - 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, - 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, - 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, - 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, - 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, - 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, - 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, - 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, - 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, - 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, - 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, - 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, - 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, - 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, - 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, - 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, - 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, - 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, - 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, - 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, - 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, - 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, - 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, - 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, - 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, - 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, - 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, - 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, - 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, - 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, - 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, - 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, - 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, - 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, - 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, - 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, - 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, - 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, - 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, - 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, - 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, - 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, - 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, - 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, - 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, - 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, - 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, - 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, - 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, - 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, - 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, - 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, - 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, - 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, - 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, - 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, - 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, - 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, - 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, - 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, - 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, - 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, - 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, - 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, - 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, - 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, - 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, - 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, - 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, - 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, - 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, - 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, - 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, - 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, - 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, - 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, - 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, - 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, - 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, - 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, - 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, - 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, - 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, - 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, - 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, - 0x0000000000000000, -}; - -const uint8_t pshufb_combine_table[272] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -const unsigned char BitsSetTable256mul2[256] = { - 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, - 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, - 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, - 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, - 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, - 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, - 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, - 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, - 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, - 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, - 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, - 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, - 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, - 14, 10, 12, 12, 14, 12, 14, 14, 16}; - -constexpr uint8_t to_base64_value[] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, - 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; - -constexpr uint8_t to_base64_url_value[] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; -static_assert(sizeof(to_base64_value) == 256, "to_base64_value must have 256 elements"); -static_assert(sizeof(to_base64_url_value) == 256, "to_base64_url_value must have 256 elements"); -static_assert(to_base64_value[uint8_t(' ')] == 64, "space must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t(' ')] == 64, "space must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\t')] == 64, "tab must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\t')] == 64, "tab must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\r')] == 64, "cr must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\r')] == 64, "cr must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\n')] == 64, "lf must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\n')] == 64, "lf must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\f')] == 64, "ff must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\f')] == 64, "ff must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('+')] == 62, "+ must be == 62 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('-')] == 62, "- must be == 62 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('/')] == 63, "/ must be == 62 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('_')] == 63, "_ must be == 62 in to_base64_url_value"); -} // namespace base64 -} // namespace tables -} // unnamed namespace -} // namespace simdutf - -#endif // SIMDUTF_BASE64_TABLES_H -/* end file src/tables/base64_tables.h */ -/* begin file src/implementation.cpp */ -#include -#include -#include - -// Useful for debugging purposes -namespace simdutf { -namespace { - -template -std::string toBinaryString(T b) { - std::string binary = ""; - T mask = T(1) << (sizeof(T) * CHAR_BIT - 1); - while (mask > 0) { - binary += ((b & mask) == 0) ? '0' : '1'; - mask >>= 1; - } - return binary; -} -} -} - -// Implementations -// The best choice should always come first! -/* begin file src/simdutf/arm64.h */ -#ifndef SIMDUTF_ARM64_H -#define SIMDUTF_ARM64_H - -#ifdef SIMDUTF_FALLBACK_H -#error "arm64.h must be included before fallback.h" -#endif - - -#ifndef SIMDUTF_IMPLEMENTATION_ARM64 -#define SIMDUTF_IMPLEMENTATION_ARM64 (SIMDUTF_IS_ARM64) -#endif -#if SIMDUTF_IMPLEMENTATION_ARM64 && SIMDUTF_IS_ARM64 -#define SIMDUTF_CAN_ALWAYS_RUN_ARM64 1 -#else -#define SIMDUTF_CAN_ALWAYS_RUN_ARM64 0 -#endif - - - -#if SIMDUTF_IMPLEMENTATION_ARM64 - -namespace simdutf { -/** - * Implementation for NEON (ARMv8). - */ -namespace arm64 { -} // namespace arm64 -} // namespace simdutf - -/* begin file src/simdutf/arm64/implementation.h */ -#ifndef SIMDUTF_ARM64_IMPLEMENTATION_H -#define SIMDUTF_ARM64_IMPLEMENTATION_H - - -namespace simdutf { -namespace arm64 { - -namespace { -using namespace simdutf; -} - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() : simdutf::implementation("arm64", "ARM NEON", internal::instruction_set::NEON) {} - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -}; - -} // namespace arm64 -} // namespace simdutf - -#endif // SIMDUTF_ARM64_IMPLEMENTATION_H -/* end file src/simdutf/arm64/implementation.h */ - -/* begin file src/simdutf/arm64/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "arm64" -// #define SIMDUTF_IMPLEMENTATION arm64 -/* end file src/simdutf/arm64/begin.h */ - -// Declarations -/* begin file src/simdutf/arm64/intrinsics.h */ -#ifndef SIMDUTF_ARM64_INTRINSICS_H -#define SIMDUTF_ARM64_INTRINSICS_H - - -// This should be the correct header whether -// you use visual studio or other compilers. -#include - -#endif // SIMDUTF_ARM64_INTRINSICS_H -/* end file src/simdutf/arm64/intrinsics.h */ -/* begin file src/simdutf/arm64/bitmanipulation.h */ -#ifndef SIMDUTF_ARM64_BITMANIPULATION_H -#define SIMDUTF_ARM64_BITMANIPULATION_H - -namespace simdutf { -namespace arm64 { -namespace { - -/* result might be undefined when input_num is zero */ -simdutf_really_inline int count_ones(uint64_t input_num) { - return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); -} - -#if SIMDUTF_NEED_TRAILING_ZEROES -simdutf_really_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - unsigned long ret; - // Search the mask data from least significant bit (LSB) - // to the most significant bit (MSB) for a set bit (1). - _BitScanForward64(&ret, input_num); - return (int)ret; -#else // SIMDUTF_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDUTF_REGULAR_VISUAL_STUDIO -} -#endif - -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf - -#endif // SIMDUTF_ARM64_BITMANIPULATION_H -/* end file src/simdutf/arm64/bitmanipulation.h */ -/* begin file src/simdutf/arm64/simd.h */ -#ifndef SIMDUTF_ARM64_SIMD_H -#define SIMDUTF_ARM64_SIMD_H - -#include - - -namespace simdutf { -namespace arm64 { -namespace { -namespace simd { - -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO -namespace { -// Start of private section with Visual Studio workaround - -#ifndef simdutf_make_uint8x16_t -#define simdutf_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \ - x13, x14, x15, x16) \ - ([=]() { \ - uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \ - x9, x10, x11, x12, x13, x14, x15, x16}; \ - return vld1q_u8(array); \ - }()) -#endif -#ifndef simdutf_make_int8x16_t -#define simdutf_make_int8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \ - x13, x14, x15, x16) \ - ([=]() { \ - int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \ - x9, x10, x11, x12, x13, x14, x15, x16}; \ - return vld1q_s8(array); \ - }()) -#endif - -#ifndef simdutf_make_uint8x8_t -#define simdutf_make_uint8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \ - ([=]() { \ - uint8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \ - return vld1_u8(array); \ - }()) -#endif -#ifndef simdutf_make_int8x8_t -#define simdutf_make_int8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \ - ([=]() { \ - int8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \ - return vld1_s8(array); \ - }()) -#endif -#ifndef simdutf_make_uint16x8_t -#define simdutf_make_uint16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \ - ([=]() { \ - uint16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \ - return vld1q_u16(array); \ - }()) -#endif -#ifndef simdutf_make_int16x8_t -#define simdutf_make_int16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \ - ([=]() { \ - int16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \ - return vld1q_s16(array); \ - }()) -#endif - - -// End of private section with Visual Studio workaround -} // namespace -#endif // SIMDUTF_REGULAR_VISUAL_STUDIO - - - template - struct simd8; - - // - // Base class of simd8 and simd8, both of which use uint8x16_t internally. - // - template> - struct base_u8 { - uint8x16_t value; - static const int SIZE = sizeof(value); - - // Conversion from/to SIMD register - simdutf_really_inline base_u8(const uint8x16_t _value) : value(_value) {} - simdutf_really_inline operator const uint8x16_t&() const { return this->value; } - simdutf_really_inline operator uint8x16_t&() { return this->value; } - simdutf_really_inline T first() const { return vgetq_lane_u8(*this,0); } - simdutf_really_inline T last() const { return vgetq_lane_u8(*this,15); } - - // Bit operations - simdutf_really_inline simd8 operator|(const simd8 other) const { return vorrq_u8(*this, other); } - simdutf_really_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } - simdutf_really_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } - simdutf_really_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } - simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdutf_really_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdutf_really_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdutf_really_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - - friend simdutf_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - - template - simdutf_really_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - static simdutf_really_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - - simdutf_really_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // False constructor - simdutf_really_inline simd8() : simd8(vdupq_n_u8(0)) {} - // Splat constructor - simdutf_really_inline simd8(bool _value) : simd8(splat(_value)) {} - simdutf_really_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - - // We return uint32_t instead of uint16_t because that seems to be more efficient for most - // purposes (cutting it down to uint16_t costs performance in some compilers). - simdutf_really_inline uint32_t to_bitmask() const { -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = simdutf_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); -#else - const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; -#endif - auto minput = *this & bit_mask; - uint8x16_t tmp = vpaddq_u8(minput, minput); - tmp = vpaddq_u8(tmp, tmp); - 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. - // This method is expected to be faster than none() and is equivalent - // when the vector register is the result of a comparison, with byte - // values 0xff and 0x00. - simdutf_really_inline uint64_t to_bitmask64() const { - return vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); - } - - simdutf_really_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } - simdutf_really_inline bool none() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) == 0; } - simdutf_really_inline bool all() const { return vminvq_u32(vreinterpretq_u32_u8(*this)) == 0xFFFFF; } - - - }; - - // Unsigned bytes - template<> - struct simd8: base_u8 { - static simdutf_really_inline simd8 splat(uint8_t _value) { return vmovq_n_u8(_value); } - static simdutf_really_inline simd8 zero() { return vdupq_n_u8(0); } - static simdutf_really_inline simd8 load(const uint8_t* values) { return vld1q_u8(values); } - simdutf_really_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // Zero constructor - simdutf_really_inline simd8() : simd8(zero()) {} - // Array constructor - simdutf_really_inline simd8(const uint8_t values[16]) : simd8(load(values)) {} - // Splat constructor - simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Member-by-member initialization -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - simdutf_really_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) : simd8(simdutf_make_uint8x16_t( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} -#else - simdutf_really_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) : simd8(uint8x16_t{ - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - }) {} -#endif - - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Store to array - simdutf_really_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - - // Saturated math - simdutf_really_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } - simdutf_really_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } - simdutf_really_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } - simdutf_really_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdutf_really_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - - // Order-specific operations - simdutf_really_inline uint8_t max_val() const { return vmaxvq_u8(*this); } - simdutf_really_inline uint8_t min_val() const { return vminvq_u8(*this); } - simdutf_really_inline simd8 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } - simdutf_really_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } - simdutf_really_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } - simdutf_really_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } - simdutf_really_inline simd8 operator>(const simd8 other) const { return vcgtq_u8(*this, other); } - // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's. - simdutf_really_inline simd8 gt_bits(const simd8 other) const { return simd8(*this > other); } - // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's. - simdutf_really_inline simd8 lt_bits(const simd8 other) const { return simd8(*this < other); } - - // Bit-specific operations - simdutf_really_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdutf_really_inline bool is_ascii() const { return this->max_val() < 0b10000000u; } - - simdutf_really_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } - simdutf_really_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } - template - simdutf_really_inline simd8 shr() const { return vshrq_n_u8(*this, N); } - template - simdutf_really_inline simd8 shl() const { return vshlq_n_u8(*this, N); } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - - - template - simdutf_really_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - - template - simdutf_really_inline simd8 apply_lookup_16_to(const simd8 original) const { - return vqtbl1q_u8(*this, simd8(original)); - } - }; - - // Signed bytes - template<> - struct simd8 { - int8x16_t value; - - static simdutf_really_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } - static simdutf_really_inline simd8 zero() { return vdupq_n_s8(0); } - static simdutf_really_inline simd8 load(const int8_t values[16]) { return vld1q_s8(values); } - - // Use ST2 instead of UXTL+UXTL2 to interleave zeroes. UXTL is actually a USHLL #0, - // and shifting in NEON is actually quite slow. - // - // While this needs the registers to be in a specific order, bigger cores can interleave - // these with no overhead, and it still performs decently on little cores. - // movi v1.3d, #0 - // mov v0.16b, value[0] - // st2 {v0.16b, v1.16b}, [ptr], #32 - // mov v0.16b, value[1] - // st2 {v0.16b, v1.16b}, [ptr], #32 - // ... - template - simdutf_really_inline void store_ascii_as_utf16(char16_t * p) const { - int8x16x2_t pair = match_system(big_endian) - ? int8x16x2_t{{this->value, vmovq_n_s8(0)}} - : int8x16x2_t{{vmovq_n_s8(0), this->value}}; - vst2q_s8(reinterpret_cast(p), pair); - } - - // currently unused - // Technically this could be done with ST4 like in store_ascii_as_utf16, but it is - // very much not worth it, as explicitly mentioned in the ARM Cortex-X1 Core Software - // Optimization Guide: - // 4.18 Complex ASIMD instructions - // The bandwidth of [ST4 with element size less than 64b] is limited by decode - // constraints and it is advisable to avoid them when high performing code is desired. - // Instead, it is better to use ZIP1+ZIP2 and two ST2. - simdutf_really_inline void store_ascii_as_utf32(char32_t * p) const { - const uint16x8_t low = vreinterpretq_u16_s8(vzip1q_s8(this->value, vmovq_n_s8(0))); - const uint16x8_t high = vreinterpretq_u16_s8(vzip2q_s8(this->value, vmovq_n_s8(0))); - const uint16x8x2_t low_pair{{ low, vmovq_n_u16(0) }}; - vst2q_u16(reinterpret_cast(p), low_pair); - const uint16x8x2_t high_pair{{ high, vmovq_n_u16(0) }}; - vst2q_u16(reinterpret_cast(p + 8), high_pair); - } - - // In places where the table can be reused, which is most uses in simdutf, it is worth it to do - // 4 table lookups, as there is no direct zero extension from u8 to u32. - simdutf_really_inline void store_ascii_as_utf32_tbl(char32_t * p) const { - const simd8 tb1{ 0,255,255,255, 1,255,255,255, 2,255,255,255, 3,255,255,255 }; - const simd8 tb2{ 4,255,255,255, 5,255,255,255, 6,255,255,255, 7,255,255,255 }; - const simd8 tb3{ 8,255,255,255, 9,255,255,255, 10,255,255,255, 11,255,255,255 }; - const simd8 tb4{ 12,255,255,255, 13,255,255,255, 14,255,255,255, 15,255,255,255 }; - - // encourage store pairing and interleaving - const auto shuf1 = this->apply_lookup_16_to(tb1); - const auto shuf2 = this->apply_lookup_16_to(tb2); - shuf1.store(reinterpret_cast(p)); - shuf2.store(reinterpret_cast(p + 4)); - - const auto shuf3 = this->apply_lookup_16_to(tb3); - const auto shuf4 = this->apply_lookup_16_to(tb4); - shuf3.store(reinterpret_cast(p + 8)); - shuf4.store(reinterpret_cast(p + 12)); - } - // Conversion from/to SIMD register - simdutf_really_inline simd8(const int8x16_t _value) : value{_value} {} - simdutf_really_inline operator const int8x16_t&() const { return this->value; } -#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO - simdutf_really_inline operator const uint8x16_t() const { return vreinterpretq_u8_s8(this->value); } -#endif - simdutf_really_inline operator int8x16_t&() { return this->value; } - - // Zero constructor - simdutf_really_inline simd8() : simd8(zero()) {} - // Splat constructor - simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const int8_t* values) : simd8(load(values)) {} - // Member-by-member initialization -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - simdutf_really_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) : simd8(simdutf_make_int8x16_t( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} -#else - simdutf_really_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) : simd8(int8x16_t{ - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - }) {} -#endif - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Store to array - simdutf_really_inline void store(int8_t dst[16]) const { return vst1q_s8(dst, value); } - // Explicit conversion to/from unsigned - // - // Under Visual Studio/ARM64 uint8x16_t and int8x16_t are apparently the same type. - // In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14 - // and relatively ugly and hard to read. -#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO - simdutf_really_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {} -#endif - simdutf_really_inline operator simd8() const { return vreinterpretq_u8_s8(this->value); } - - simdutf_really_inline simd8 operator|(const simd8 other) const { return vorrq_s8(value, other.value); } - simdutf_really_inline simd8 operator&(const simd8 other) const { return vandq_s8(value, other.value); } - simdutf_really_inline simd8 operator^(const simd8 other) const { return veorq_s8(value, other.value); } - simdutf_really_inline simd8 bit_andnot(const simd8 other) const { return vbicq_s8(value, other.value); } - - // Math - simdutf_really_inline simd8 operator+(const simd8 other) const { return vaddq_s8(value, other.value); } - simdutf_really_inline simd8 operator-(const simd8 other) const { return vsubq_s8(value, other.value); } - simdutf_really_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdutf_really_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - - simdutf_really_inline int8_t max_val() const { return vmaxvq_s8(value); } - simdutf_really_inline int8_t min_val() const { return vminvq_s8(value); } - simdutf_really_inline bool is_ascii() const { return this->min_val() >= 0; } - - // Order-sensitive comparisons - simdutf_really_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(value, other.value); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return vminq_s8(value, other.value); } - simdutf_really_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(value, other.value); } - simdutf_really_inline simd8 operator<(const simd8 other) const { return vcltq_s8(value, other.value); } - simdutf_really_inline simd8 operator==(const simd8 other) const { return vceqq_s8(value, other.value); } - - template - simdutf_really_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } - - // Perform a lookup assuming no value is larger than 16 - template - simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - template - simdutf_really_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - - template - simdutf_really_inline simd8 apply_lookup_16_to(const simd8 original) const { - return vqtbl1q_s8(*this, simd8(original)); - } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8 other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8::load(ptr), simd8::load(ptr+sizeof(simd8)/sizeof(T)), simd8::load(ptr+2*sizeof(simd8)/sizeof(T)), simd8::load(ptr+3*sizeof(simd8)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr+sizeof(simd8)*0/sizeof(T)); - this->chunks[1].store(ptr+sizeof(simd8)*1/sizeof(T)); - this->chunks[2].store(ptr+sizeof(simd8)*2/sizeof(T)); - this->chunks[3].store(ptr+sizeof(simd8)*3/sizeof(T)); - } - - - simdutf_really_inline simd8x64& operator |=(const simd8x64 &other) { - this->chunks[0] |= other.chunks[0]; - this->chunks[1] |= other.chunks[1]; - this->chunks[2] |= other.chunks[2]; - this->chunks[3] |= other.chunks[3]; - return *this; - } - - simdutf_really_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } - - simdutf_really_inline bool is_ascii() const { - return reduce_or().is_ascii(); - } - - template - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - this->chunks[0].template store_ascii_as_utf16(ptr+sizeof(simd8)*0); - this->chunks[1].template store_ascii_as_utf16(ptr+sizeof(simd8)*1); - this->chunks[2].template store_ascii_as_utf16(ptr+sizeof(simd8)*2); - this->chunks[3].template store_ascii_as_utf16(ptr+sizeof(simd8)*3); - } - - simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const { - this->chunks[0].store_ascii_as_utf32_tbl(ptr+sizeof(simd8)*0); - this->chunks[1].store_ascii_as_utf32_tbl(ptr+sizeof(simd8)*1); - this->chunks[2].store_ascii_as_utf32_tbl(ptr+sizeof(simd8)*2); - this->chunks[3].store_ascii_as_utf32_tbl(ptr+sizeof(simd8)*3); - } - - simdutf_really_inline uint64_t to_bitmask() const { -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = simdutf_make_uint8x16_t( - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - ); -#else - const uint8x16_t bit_mask = { - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - }; -#endif - // Add each of the elements next to each other, successively, to stuff each 8 byte mask into one. - uint8x16_t sum0 = vpaddq_u8(vandq_u8(uint8x16_t(this->chunks[0]), bit_mask), vandq_u8(uint8x16_t(this->chunks[1]), bit_mask)); - uint8x16_t sum1 = vpaddq_u8(vandq_u8(uint8x16_t(this->chunks[2]), bit_mask), vandq_u8(uint8x16_t(this->chunks[3]), bit_mask)); - sum0 = vpaddq_u8(sum0, sum1); - sum0 = vpaddq_u8(sum0, sum0); - return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - - return simd8x64( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low), - (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low), - (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - return simd8x64( - (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low), - (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low), - (this->chunks[2] > mask_high) | (this->chunks[2] < mask_low), - (this->chunks[3] > mask_high) | (this->chunks[3] < mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] < mask, - this->chunks[1] < mask, - this->chunks[2] < mask, - this->chunks[3] < mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] > mask, - this->chunks[1] > mask, - this->chunks[2] > mask, - this->chunks[3] > mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] >= mask, - this->chunks[1] >= mask, - this->chunks[2] >= mask, - this->chunks[3] >= mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - simd8(uint8x16_t(this->chunks[0])) >= mask, - simd8(uint8x16_t(this->chunks[1])) >= mask, - simd8(uint8x16_t(this->chunks[2])) >= mask, - simd8(uint8x16_t(this->chunks[3])) >= mask - ).to_bitmask(); - } - }; // struct simd8x64 -/* begin file src/simdutf/arm64/simd16-inl.h */ -template -struct simd16; - - template> - struct base_u16 { - uint16x8_t value; - static const int SIZE = sizeof(value); - - // Conversion from/to SIMD register - simdutf_really_inline base_u16() = default; - simdutf_really_inline base_u16(const uint16x8_t _value) : value(_value) {} - simdutf_really_inline operator const uint16x8_t&() const { return this->value; } - simdutf_really_inline operator uint16x8_t&() { return this->value; } - // Bit operations - simdutf_really_inline simd16 operator|(const simd16 other) const { return vorrq_u16(*this, other); } - simdutf_really_inline simd16 operator&(const simd16 other) const { return vandq_u16(*this, other); } - simdutf_really_inline simd16 operator^(const simd16 other) const { return veorq_u16(*this, other); } - simdutf_really_inline simd16 bit_andnot(const simd16 other) const { return vbicq_u16(*this, other); } - simdutf_really_inline simd16 operator~() const { return *this ^ 0xFFu; } - simdutf_really_inline simd16& operator|=(const simd16 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdutf_really_inline simd16& operator&=(const simd16 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdutf_really_inline simd16& operator^=(const simd16 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - - friend simdutf_really_inline Mask operator==(const simd16 lhs, const simd16 rhs) { return vceqq_u16(lhs, rhs); } - - template - simdutf_really_inline simd16 prev(const simd16 prev_chunk) const { - return vextq_u18(prev_chunk, *this, 8 - N); - } - }; - -template> -struct base16: base_u16 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - simdutf_really_inline base16() : base_u16() {} - simdutf_really_inline base16(const uint16x8_t _value) : base_u16(_value) {} - template - simdutf_really_inline base16(const Pointer* ptr) : base16(vld1q_u16(ptr)) {} - - static const int SIZE = sizeof(base_u16::value); - - template - simdutf_really_inline simd16 prev(const simd16 prev_chunk) const { - return vextq_u18(prev_chunk, *this, 8 - N); - } -}; - -// SIMD byte mask type (returned by things like eq and gt) -template<> -struct simd16: base16 { - static simdutf_really_inline simd16 splat(bool _value) { return vmovq_n_u16(uint16_t(-(!!_value))); } - - simdutf_really_inline simd16() : base16() {} - simdutf_really_inline simd16(const uint16x8_t _value) : base16(_value) {} - // Splat constructor - simdutf_really_inline simd16(bool _value) : base16(splat(_value)) {} - -}; - -template -struct base16_numeric: base16 { - static simdutf_really_inline simd16 splat(T _value) { return vmovq_n_u16(_value); } - static simdutf_really_inline simd16 zero() { return vdupq_n_u16(0); } - static simdutf_really_inline simd16 load(const T values[8]) { - return vld1q_u16(reinterpret_cast(values)); - } - - simdutf_really_inline base16_numeric() : base16() {} - simdutf_really_inline base16_numeric(const uint16x8_t _value) : base16(_value) {} - - // Store to array - simdutf_really_inline void store(T dst[8]) const { return vst1q_u16(dst, *this); } - - // Override to distinguish from bool version - simdutf_really_inline simd16 operator~() const { return *this ^ 0xFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd16 operator+(const simd16 other) const { return vaddq_u8(*this, other); } - simdutf_really_inline simd16 operator-(const simd16 other) const { return vsubq_u8(*this, other); } - simdutf_really_inline simd16& operator+=(const simd16 other) { *this = *this + other; return *static_cast*>(this); } - simdutf_really_inline simd16& operator-=(const simd16 other) { *this = *this - other; return *static_cast*>(this); } -}; - -// Signed code units -template<> -struct simd16 : base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} -#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO - simdutf_really_inline simd16(const uint16x8_t _value) : base16_numeric(_value) {} -#endif - simdutf_really_inline simd16(const int16x8_t _value) : base16_numeric(vreinterpretq_u16_s16(_value)) {} - - // Splat constructor - simdutf_really_inline simd16(int16_t _value) : simd16(splat(_value)) {} - // Array constructor - simdutf_really_inline simd16(const int16_t* values) : simd16(load(values)) {} - simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast(values))) {} - simdutf_really_inline operator simd16() const; - simdutf_really_inline operator const uint16x8_t&() const { return this->value; } - simdutf_really_inline operator const int16x8_t() const { return vreinterpretq_s16_u16(this->value); } - - simdutf_really_inline int16_t max_val() const { return vmaxvq_s16(vreinterpretq_s16_u16(this->value)); } - simdutf_really_inline int16_t min_val() const { return vminvq_s16(vreinterpretq_s16_u16(this->value)); } - // Order-sensitive comparisons - simdutf_really_inline simd16 max_val(const simd16 other) const { return vmaxq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); } - simdutf_really_inline simd16 min_val(const simd16 other) const { return vmaxq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); } - simdutf_really_inline simd16 operator>(const simd16 other) const { return vcgtq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); } - simdutf_really_inline simd16 operator<(const simd16 other) const { return vcltq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); } -}; - - - - -// Unsigned code units -template<> -struct simd16: base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} - simdutf_really_inline simd16(const uint16x8_t _value) : base16_numeric(_value) {} - - // Splat constructor - simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {} - // Array constructor - simdutf_really_inline simd16(const uint16_t* values) : simd16(load(values)) {} - simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast(values))) {} - - - simdutf_really_inline int16_t max_val() const { return vmaxvq_u16(*this); } - simdutf_really_inline int16_t min_val() const { return vminvq_u16(*this); } - // Saturated math - simdutf_really_inline simd16 saturating_add(const simd16 other) const { return vqaddq_u16(*this, other); } - simdutf_really_inline simd16 saturating_sub(const simd16 other) const { return vqsubq_u16(*this, other); } - - // Order-specific operations - simdutf_really_inline simd16 max_val(const simd16 other) const { return vmaxq_u16(*this, other); } - simdutf_really_inline simd16 min_val(const simd16 other) const { return vminq_u16(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd16 gt_bits(const simd16 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd16 lt_bits(const simd16 other) const { return other.saturating_sub(*this); } - simdutf_really_inline simd16 operator<=(const simd16 other) const { return vcleq_u16(*this, other); } - simdutf_really_inline simd16 operator>=(const simd16 other) const { return vcgeq_u16(*this, other); } - simdutf_really_inline simd16 operator>(const simd16 other) const { return vcgtq_u16(*this, other); } - simdutf_really_inline simd16 operator<(const simd16 other) const { return vcltq_u16(*this, other); } - - // Bit-specific operations - simdutf_really_inline simd16 bits_not_set() const { return *this == uint16_t(0); } - template - simdutf_really_inline simd16 shr() const { return simd16(vshrq_n_u16(*this, N)); } - template - simdutf_really_inline simd16 shl() const { return simd16(vshlq_n_u16(*this, N)); } - - // logical operations - simdutf_really_inline simd16 operator|(const simd16 other) const { return vorrq_u16(*this, other); } - simdutf_really_inline simd16 operator&(const simd16 other) const { return vandq_u16(*this, other); } - simdutf_really_inline simd16 operator^(const simd16 other) const { return veorq_u16(*this, other); } - - // Pack with the unsigned saturation of two uint16_t code units into single uint8_t vector - static simdutf_really_inline simd8 pack(const simd16& v0, const simd16& v1) { - return vqmovn_high_u16(vqmovn_u16(v0), v1); - } - - // Change the endianness - simdutf_really_inline simd16 swap_bytes() const { - return vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(*this))); - } -}; -simdutf_really_inline simd16::operator simd16() const { return this->value; } - - - template - struct simd16x32 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd16); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - simd16 chunks[NUM_CHUNKS]; - - simd16x32(const simd16x32& o) = delete; // no copy allowed - simd16x32& operator=(const simd16 other) = delete; // no assignment allowed - simd16x32() = delete; // no default constructor allowed - - simdutf_really_inline simd16x32(const simd16 chunk0, const simd16 chunk1, const simd16 chunk2, const simd16 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdutf_really_inline simd16x32(const T* ptr) : chunks{simd16::load(ptr), simd16::load(ptr+sizeof(simd16)/sizeof(T)), simd16::load(ptr+2*sizeof(simd16)/sizeof(T)), simd16::load(ptr+3*sizeof(simd16)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr+sizeof(simd16)*0/sizeof(T)); - this->chunks[1].store(ptr+sizeof(simd16)*1/sizeof(T)); - this->chunks[2].store(ptr+sizeof(simd16)*2/sizeof(T)); - this->chunks[3].store(ptr+sizeof(simd16)*3/sizeof(T)); - } - - simdutf_really_inline simd16 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } - - simdutf_really_inline bool is_ascii() const { - return reduce_or().is_ascii(); - } - - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - this->chunks[0].store_ascii_as_utf16(ptr+sizeof(simd16)*0); - this->chunks[1].store_ascii_as_utf16(ptr+sizeof(simd16)*1); - this->chunks[2].store_ascii_as_utf16(ptr+sizeof(simd16)*2); - this->chunks[3].store_ascii_as_utf16(ptr+sizeof(simd16)*3); - } - - simdutf_really_inline uint64_t to_bitmask() const { -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = simdutf_make_uint8x16_t( - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - ); -#else - const uint8x16_t bit_mask = { - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - }; -#endif - // Add each of the elements next to each other, successively, to stuff each 8 byte mask into one. - uint8x16_t sum0 = vpaddq_u8(vreinterpretq_u8_u16(this->chunks[0] & vreinterpretq_u16_u8(bit_mask)), vreinterpretq_u8_u16(this->chunks[1] & vreinterpretq_u16_u8(bit_mask))); - uint8x16_t sum1 = vpaddq_u8(vreinterpretq_u8_u16(this->chunks[2] & vreinterpretq_u16_u8(bit_mask)), vreinterpretq_u8_u16(this->chunks[3] & vreinterpretq_u16_u8(bit_mask))); - sum0 = vpaddq_u8(sum0, sum1); - sum0 = vpaddq_u8(sum0, sum0); - return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0); - } - - simdutf_really_inline void swap_bytes() { - this->chunks[0] = this->chunks[0].swap_bytes(); - this->chunks[1] = this->chunks[1].swap_bytes(); - this->chunks[2] = this->chunks[2].swap_bytes(); - this->chunks[3] = this->chunks[3].swap_bytes(); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd16 mask_low = simd16::splat(low); - const simd16 mask_high = simd16::splat(high); - - return simd16x32( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low), - (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low), - (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd16 mask_low = simd16::splat(low); - const simd16 mask_high = simd16::splat(high); - return simd16x32( - (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low), - (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low), - (this->chunks[2] > mask_high) | (this->chunks[2] < mask_low), - (this->chunks[3] > mask_high) | (this->chunks[3] < mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] < mask, - this->chunks[1] < mask, - this->chunks[2] < mask, - this->chunks[3] < mask - ).to_bitmask(); - } - - }; // struct simd16x32 - template<> - simdutf_really_inline uint64_t simd16x32::not_in_range(const uint16_t low, const uint16_t high) const { - const simd16 mask_low = simd16::splat(low); - const simd16 mask_high = simd16::splat(high); - simd16x32 x( - simd16((this->chunks[0] > mask_high) | (this->chunks[0] < mask_low)), - simd16((this->chunks[1] > mask_high) | (this->chunks[1] < mask_low)), - simd16((this->chunks[2] > mask_high) | (this->chunks[2] < mask_low)), - simd16((this->chunks[3] > mask_high) | (this->chunks[3] < mask_low)) - ); - return x.to_bitmask(); - } -/* end file src/simdutf/arm64/simd16-inl.h */ -} // namespace simd -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf - -#endif // SIMDUTF_ARM64_SIMD_H -/* end file src/simdutf/arm64/simd.h */ - -/* begin file src/simdutf/arm64/end.h */ -/* end file src/simdutf/arm64/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_ARM64 - -#endif // SIMDUTF_ARM64_H -/* end file src/simdutf/arm64.h */ -/* begin file src/simdutf/icelake.h */ -#ifndef SIMDUTF_ICELAKE_H -#define SIMDUTF_ICELAKE_H - - - -#ifdef __has_include -// How do we detect that a compiler supports vbmi2? -// For sure if the following header is found, we are ok? -#if __has_include() -#define SIMDUTF_COMPILER_SUPPORTS_VBMI2 1 -#endif -#endif - -#ifdef _MSC_VER -#if _MSC_VER >= 1930 -// Visual Studio 2022 and up support VBMI2 under x64 even if the header -// avx512vbmi2intrin.h is not found. -// Visual Studio 2019 technically supports VBMI2, but the implementation -// might be unreliable. Search for visualstudio2019icelakeissue in our -// tests. -#define SIMDUTF_COMPILER_SUPPORTS_VBMI2 1 -#endif -#endif - -// We allow icelake on x64 as long as the compiler is known to support VBMI2. -#ifndef SIMDUTF_IMPLEMENTATION_ICELAKE -#define SIMDUTF_IMPLEMENTATION_ICELAKE ((SIMDUTF_IS_X86_64) && (SIMDUTF_COMPILER_SUPPORTS_VBMI2)) -#endif - -// To see why (__BMI__) && (__LZCNT__) are not part of this next line, see -// https://github.com/simdutf/simdutf/issues/1247 -#if ((SIMDUTF_IMPLEMENTATION_ICELAKE) && (SIMDUTF_IS_X86_64) && (__AVX2__) && (SIMDUTF_HAS_AVX512F && \ - SIMDUTF_HAS_AVX512DQ && \ - SIMDUTF_HAS_AVX512VL && \ - SIMDUTF_HAS_AVX512VBMI2) && (!SIMDUTF_IS_32BITS)) -#define SIMDUTF_CAN_ALWAYS_RUN_ICELAKE 1 -#else -#define SIMDUTF_CAN_ALWAYS_RUN_ICELAKE 0 -#endif - -#if SIMDUTF_IMPLEMENTATION_ICELAKE -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE -#define SIMDUTF_TARGET_ICELAKE -#else -#define SIMDUTF_TARGET_ICELAKE SIMDUTF_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,bmi2,pclmul,lzcnt,popcnt,avx512vpopcntdq") -#endif - -namespace simdutf { -namespace icelake { -} // namespace icelake -} // namespace simdutf - - - -// -// These two need to be included outside SIMDUTF_TARGET_REGION -// -/* begin file src/simdutf/icelake/intrinsics.h */ -#ifndef SIMDUTF_ICELAKE_INTRINSICS_H -#define SIMDUTF_ICELAKE_INTRINSICS_H - - -#ifdef SIMDUTF_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#include -#else - -#if SIMDUTF_GCC11ORMORE -// We should not get warnings while including yet we do -// under some versions of GCC. -// If the x86intrin.h header has uninitialized values that are problematic, -// it is a GCC issue, we want to ignore these warnings. -SIMDUTF_DISABLE_GCC_WARNING(-Wuninitialized) -#endif - -#include // elsewhere - - -#if SIMDUTF_GCC11ORMORE -// cancels the suppression of the -Wuninitialized -SIMDUTF_POP_DISABLE_WARNINGS -#endif - -#ifndef _tzcnt_u64 -#define _tzcnt_u64(x) __tzcnt_u64(x) -#endif // _tzcnt_u64 -#endif // SIMDUTF_VISUAL_STUDIO - -#ifdef SIMDUTF_CLANG_VISUAL_STUDIO -/** - * You are not supposed, normally, to include these - * headers directly. Instead you should either include intrin.h - * or x86intrin.h. However, when compiling with clang - * under Windows (i.e., when _MSC_VER is set), these headers - * only get included *if* the corresponding features are detected - * from macros: - * e.g., if __AVX2__ is set... in turn, we normally set these - * macros by compiling against the corresponding architecture - * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole - * software with these advanced instructions. In simdutf, we - * want to compile the whole program for a generic target, - * and only target our specific kernels. As a workaround, - * we directly include the needed headers. These headers would - * normally guard against such usage, but we carefully included - * (or ) before, so the headers - * are fooled. - */ -#include // for _blsr_u64 -#include // for _pext_u64, _pdep_u64 -#include // for __lzcnt64 -#include // for most things (AVX2, AVX512, _popcnt64) -#include -#include -#include -#include -// Important: we need the AVX-512 headers: -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// unfortunately, we may not get _blsr_u64, but, thankfully, clang -// has it as a macro. -#ifndef _blsr_u64 -// we roll our own -#define _blsr_u64(n) ((n - 1) & n) -#endif // _blsr_u64 -#endif // SIMDUTF_CLANG_VISUAL_STUDIO - - - -#if defined(__GNUC__) && !defined(__clang__) - -#if __GNUC__ == 8 -#define SIMDUTF_GCC8 1 -#elif __GNUC__ == 9 -#define SIMDUTF_GCC9 1 -#endif // __GNUC__ == 8 || __GNUC__ == 9 - -#endif // defined(__GNUC__) && !defined(__clang__) - -#if SIMDUTF_GCC8 -#pragma GCC push_options -#pragma GCC target("avx512f") -/** - * GCC 8 fails to provide _mm512_set_epi8. We roll our own. - */ -inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) { - return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56), - uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56), - uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56), - uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56), - uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56), - uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56), - uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56), - uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56)); -} -#pragma GCC pop_options -#endif // SIMDUTF_GCC8 - -#endif // SIMDUTF_HASWELL_INTRINSICS_H -/* end file src/simdutf/icelake/intrinsics.h */ -/* begin file src/simdutf/icelake/implementation.h */ -#ifndef SIMDUTF_ICELAKE_IMPLEMENTATION_H -#define SIMDUTF_ICELAKE_IMPLEMENTATION_H - - -namespace simdutf { -namespace icelake { - -namespace { -using namespace simdutf; -} - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() : simdutf::implementation( - "icelake", - "Intel AVX512 (AVX-512BW, AVX-512CD, AVX-512VL, AVX-512VBMI2 extensions)", - internal::instruction_set::AVX2 | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 | internal::instruction_set::AVX512BW | internal::instruction_set::AVX512CD | internal::instruction_set::AVX512VL | internal::instruction_set::AVX512VBMI2 | internal::instruction_set::AVX512VPOPCNTDQ ) {} - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -}; - -} // namespace icelake -} // namespace simdutf - -#endif // SIMDUTF_ICELAKE_IMPLEMENTATION_H -/* end file src/simdutf/icelake/implementation.h */ - -// -// The rest need to be inside the region -// -/* begin file src/simdutf/icelake/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "icelake" -// #define SIMDUTF_IMPLEMENTATION icelake - -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE -// nothing needed. -#else -SIMDUTF_TARGET_ICELAKE -#endif - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) -#endif // end of workaround -/* end file src/simdutf/icelake/begin.h */ -// Declarations -/* begin file src/simdutf/icelake/bitmanipulation.h */ -#ifndef SIMDUTF_ICELAKE_BITMANIPULATION_H -#define SIMDUTF_ICELAKE_BITMANIPULATION_H - -namespace simdutf { -namespace icelake { -namespace { - -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO -simdutf_really_inline unsigned __int64 count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num);// Visual Studio wants two underscores -} -#else -simdutf_really_inline long long int count_ones(uint64_t input_num) { - return _popcnt64(input_num); -} -#endif - -#if SIMDUTF_NEED_TRAILING_ZEROES -simdutf_really_inline int trailing_zeroes(uint64_t input_num) { -#if SIMDUTF_REGULAR_VISUAL_STUDIO - return (int)_tzcnt_u64(input_num); -#else // SIMDUTF_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDUTF_REGULAR_VISUAL_STUDIO -} -#endif - -} // unnamed namespace -} // namespace icelake -} // namespace simdutf - -#endif // SIMDUTF_ICELAKE_BITMANIPULATION_H -/* end file src/simdutf/icelake/bitmanipulation.h */ -/* begin file src/simdutf/icelake/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_POP_DISABLE_WARNINGS -#endif // end of workaround -/* end file src/simdutf/icelake/end.h */ - - - -#endif // SIMDUTF_IMPLEMENTATION_ICELAKE -#endif // SIMDUTF_ICELAKE_H -/* end file src/simdutf/icelake.h */ -/* begin file src/simdutf/haswell.h */ -#ifndef SIMDUTF_HASWELL_H -#define SIMDUTF_HASWELL_H - -#ifdef SIMDUTF_WESTMERE_H -#error "haswell.h must be included before westmere.h" -#endif -#ifdef SIMDUTF_FALLBACK_H -#error "haswell.h must be included before fallback.h" -#endif - - -// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected -// at runtime. -#ifndef SIMDUTF_IMPLEMENTATION_HASWELL -// -// You do not want to restrict it like so: SIMDUTF_IS_X86_64 && __AVX2__ -// because we want to rely on *runtime dispatch*. -// -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE -#define SIMDUTF_IMPLEMENTATION_HASWELL 0 -#else -#define SIMDUTF_IMPLEMENTATION_HASWELL (SIMDUTF_IS_X86_64) -#endif - -#endif -// To see why (__BMI__) && (__LZCNT__) are not part of this next line, see -// https://github.com/simdutf/simdutf/issues/1247 -#if ((SIMDUTF_IMPLEMENTATION_HASWELL) && (SIMDUTF_IS_X86_64) && (__AVX2__)) -#define SIMDUTF_CAN_ALWAYS_RUN_HASWELL 1 -#else -#define SIMDUTF_CAN_ALWAYS_RUN_HASWELL 0 -#endif - -#if SIMDUTF_IMPLEMENTATION_HASWELL - -#define SIMDUTF_TARGET_HASWELL SIMDUTF_TARGET_REGION("avx2,bmi,lzcnt,popcnt") - -namespace simdutf { -/** - * Implementation for Haswell (Intel AVX2). - */ -namespace haswell { -} // namespace haswell -} // namespace simdutf - -// -// These two need to be included outside SIMDUTF_TARGET_REGION -// -/* begin file src/simdutf/haswell/implementation.h */ -#ifndef SIMDUTF_HASWELL_IMPLEMENTATION_H -#define SIMDUTF_HASWELL_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDUTF_TARGET_REGION -namespace simdutf { -namespace haswell { - -using namespace simdutf; - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() : simdutf::implementation( - "haswell", - "Intel/AMD AVX2", - internal::instruction_set::AVX2 | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 - ) {} - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept; - simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused virtual result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused virtual result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused virtual size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -}; - -} // namespace haswell -} // namespace simdutf - -#endif // SIMDUTF_HASWELL_IMPLEMENTATION_H -/* end file src/simdutf/haswell/implementation.h */ -/* begin file src/simdutf/haswell/intrinsics.h */ -#ifndef SIMDUTF_HASWELL_INTRINSICS_H -#define SIMDUTF_HASWELL_INTRINSICS_H - - -#ifdef SIMDUTF_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else - -#if SIMDUTF_GCC11ORMORE -// We should not get warnings while including yet we do -// under some versions of GCC. -// If the x86intrin.h header has uninitialized values that are problematic, -// it is a GCC issue, we want to ignore these warnings. -SIMDUTF_DISABLE_GCC_WARNING(-Wuninitialized) -#endif - -#include // elsewhere - - -#if SIMDUTF_GCC11ORMORE -// cancels the suppression of the -Wuninitialized -SIMDUTF_POP_DISABLE_WARNINGS -#endif - -#endif // SIMDUTF_VISUAL_STUDIO - -#ifdef SIMDUTF_CLANG_VISUAL_STUDIO -/** - * You are not supposed, normally, to include these - * headers directly. Instead you should either include intrin.h - * or x86intrin.h. However, when compiling with clang - * under Windows (i.e., when _MSC_VER is set), these headers - * only get included *if* the corresponding features are detected - * from macros: - * e.g., if __AVX2__ is set... in turn, we normally set these - * macros by compiling against the corresponding architecture - * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole - * software with these advanced instructions. In simdutf, we - * want to compile the whole program for a generic target, - * and only target our specific kernels. As a workaround, - * we directly include the needed headers. These headers would - * normally guard against such usage, but we carefully included - * (or ) before, so the headers - * are fooled. - */ -#include // for _blsr_u64 -#include // for __lzcnt64 -#include // for most things (AVX2, AVX512, _popcnt64) -#include -#include -#include -#include -// unfortunately, we may not get _blsr_u64, but, thankfully, clang -// has it as a macro. -#ifndef _blsr_u64 -// we roll our own -#define _blsr_u64(n) ((n - 1) & n) -#endif // _blsr_u64 -#endif // SIMDUTF_CLANG_VISUAL_STUDIO - -#endif // SIMDUTF_HASWELL_INTRINSICS_H -/* end file src/simdutf/haswell/intrinsics.h */ - -// -// The rest need to be inside the region -// -/* begin file src/simdutf/haswell/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "haswell" -// #define SIMDUTF_IMPLEMENTATION haswell - -#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL -// nothing needed. -#else -SIMDUTF_TARGET_HASWELL -#endif - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) -#endif // end of workaround -/* end file src/simdutf/haswell/begin.h */ -// Declarations -/* begin file src/simdutf/haswell/bitmanipulation.h */ -#ifndef SIMDUTF_HASWELL_BITMANIPULATION_H -#define SIMDUTF_HASWELL_BITMANIPULATION_H - -namespace simdutf { -namespace haswell { -namespace { - -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO -simdutf_really_inline unsigned __int64 count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num);// Visual Studio wants two underscores -} -#else -simdutf_really_inline long long int count_ones(uint64_t input_num) { - return _popcnt64(input_num); -} -#endif - -#if SIMDUTF_NEED_TRAILING_ZEROES -simdutf_inline int trailing_zeroes(uint64_t input_num) { -#if SIMDUTF_REGULAR_VISUAL_STUDIO - return (int)_tzcnt_u64(input_num); -#else // SIMDUTF_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDUTF_REGULAR_VISUAL_STUDIO -} -#endif - -} // unnamed namespace -} // namespace haswell -} // namespace simdutf - -#endif // SIMDUTF_HASWELL_BITMANIPULATION_H -/* end file src/simdutf/haswell/bitmanipulation.h */ -/* begin file src/simdutf/haswell/simd.h */ -#ifndef SIMDUTF_HASWELL_SIMD_H -#define SIMDUTF_HASWELL_SIMD_H - - -namespace simdutf { -namespace haswell { -namespace { -namespace simd { - - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; - - // Zero constructor - simdutf_really_inline base() : value{__m256i()} {} - - // Conversion from SIMD register - simdutf_really_inline base(const __m256i _value) : value(_value) {} - // Conversion to SIMD register - simdutf_really_inline operator const __m256i&() const { return this->value; } - simdutf_really_inline operator __m256i&() { return this->value; } - template - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - __m256i first = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(*this)); - __m256i second = _mm256_cvtepu8_epi16(_mm256_extractf128_si256(*this,1)); - if (big_endian) { - const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - first = _mm256_shuffle_epi8(first, swap); - second = _mm256_shuffle_epi8(second, swap); - } - _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), first); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), second); - } - simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const { - _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), _mm256_cvtepu8_epi32(_mm256_castsi256_si128(*this))); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr+8), _mm256_cvtepu8_epi32(_mm256_castsi256_si128(_mm256_srli_si256(*this,8)))); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), _mm256_cvtepu8_epi32(_mm256_extractf128_si256(*this,1))); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 24), _mm256_cvtepu8_epi32(_mm_srli_si128(_mm256_extractf128_si256(*this,1),8))); - } - // Bit operations - simdutf_really_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); } - simdutf_really_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); } - simdutf_really_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); } - simdutf_really_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); } - simdutf_really_inline Child& operator|=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast | other; return *this_cast; } - simdutf_really_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdutf_really_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8: base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; - - simdutf_really_inline base8() : base>() {} - simdutf_really_inline base8(const __m256i _value) : base>(_value) {} - simdutf_really_inline T first() const { return _mm256_extract_epi8(*this,0); } - simdutf_really_inline T last() const { return _mm256_extract_epi8(*this,31); } - friend simdutf_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } - - static const int SIZE = sizeof(base::value); - - template - simdutf_really_inline simd8 prev(const simd8 prev_chunk) const { - return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N); - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base8 { - static simdutf_really_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } - - simdutf_really_inline simd8() : base8() {} - simdutf_really_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdutf_really_inline simd8(bool _value) : base8(splat(_value)) {} - - simdutf_really_inline uint32_t to_bitmask() const { return uint32_t(_mm256_movemask_epi8(*this)); } - simdutf_really_inline bool any() const { return !_mm256_testz_si256(*this, *this); } - simdutf_really_inline bool none() const { return _mm256_testz_si256(*this, *this); } - simdutf_really_inline bool all() const { return static_cast(_mm256_movemask_epi8(*this)) == 0xFFFFFFFF; } - simdutf_really_inline simd8 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric: base8 { - static simdutf_really_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } - static simdutf_really_inline simd8 zero() { return _mm256_setzero_si256(); } - static simdutf_really_inline simd8 load(const T values[32]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdutf_really_inline simd8 repeat_16( - T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7, - T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - simdutf_really_inline base8_numeric() : base8() {} - simdutf_really_inline base8_numeric(const __m256i _value) : base8(_value) {} - - // Store to array - simdutf_really_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd8 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } - simdutf_really_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } - simdutf_really_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdutf_really_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Override to distinguish from bool version - simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm256_shuffle_epi8(lookup_table, *this); - } - - template - simdutf_really_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m256i _value) : base8_numeric(_value) {} - - // Splat constructor - simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const int8_t values[32]) : simd8(load(values)) {} - simdutf_really_inline operator simd8() const; - // Member-by-member initialization - simdutf_really_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15, - int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23, - int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31 - ) : simd8(_mm256_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v16,v17,v18,v19,v20,v21,v22,v23, - v24,v25,v26,v27,v28,v29,v30,v31 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - simdutf_really_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; } - // Order-sensitive comparisons - simdutf_really_inline simd8 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } - simdutf_really_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } - simdutf_really_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } - }; - - // Unsigned bytes - template<> - struct simd8: base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m256i _value) : base8_numeric(_value) {} - // Splat constructor - simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15, - uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23, - uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31 - ) : simd8(_mm256_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v16,v17,v18,v19,v20,v21,v22,v23, - v24,v25,v26,v27,v28,v29,v30,v31 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - - // Saturated math - simdutf_really_inline simd8 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } - simdutf_really_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } - - // Order-specific operations - simdutf_really_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdutf_really_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdutf_really_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdutf_really_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdutf_really_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdutf_really_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdutf_really_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdutf_really_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdutf_really_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - simdutf_really_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; } - simdutf_really_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); } - simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdutf_really_inline bool bits_not_set_anywhere(simd8 bits) const { return _mm256_testz_si256(*this, bits); } - simdutf_really_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdutf_really_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdutf_really_inline simd8 shl() const { return simd8(_mm256_slli_epi16(*this, N)) & uint8_t(0xFFu << N); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdutf_really_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } - }; - simdutf_really_inline simd8::operator simd8() const { return this->value; } - - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8 other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} - simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8::load(ptr), simd8::load(ptr+sizeof(simd8)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr+sizeof(simd8)*0/sizeof(T)); - this->chunks[1].store(ptr+sizeof(simd8)*1/sizeof(T)); - } - - simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r_hi = this->chunks[1].to_bitmask(); - return r_lo | (r_hi << 32); - } - - simdutf_really_inline simd8x64& operator|=(const simd8x64 &other) { - this->chunks[0] |= other.chunks[0]; - this->chunks[1] |= other.chunks[1]; - return *this; - } - - simdutf_really_inline simd8 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } - - simdutf_really_inline bool is_ascii() const { - return this->reduce_or().is_ascii(); - } - - template - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - this->chunks[0].template store_ascii_as_utf16(ptr+sizeof(simd8)*0); - this->chunks[1].template store_ascii_as_utf16(ptr+sizeof(simd8)*1); - } - - simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const { - this->chunks[0].store_ascii_as_utf32(ptr+sizeof(simd8)*0); - this->chunks[1].store_ascii_as_utf32(ptr+sizeof(simd8)*1); - } - - simdutf_really_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t eq(const simd8x64 &other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - - return simd8x64( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - return simd8x64( - (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low), - (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] < mask, - this->chunks[1] < mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t gt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] > mask, - this->chunks[1] > mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] >= mask, - this->chunks[1] >= mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - (simd8(__m256i(this->chunks[0])) >= mask), - (simd8(__m256i(this->chunks[1])) >= mask) - ).to_bitmask(); - } - }; // struct simd8x64 - -/* begin file src/simdutf/haswell/simd16-inl.h */ -#ifdef __GNUC__ -#if __GNUC__ < 8 -#define _mm256_set_m128i(xmm1, xmm2) _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1), _mm256_castsi128_si256(xmm2), 2) -#define _mm256_setr_m128i(xmm2, xmm1) _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1), _mm256_castsi128_si256(xmm2), 2) -#endif -#endif - -template -struct simd16; - -template> -struct base16: base> { - using bitmask_type = uint32_t; - - simdutf_really_inline base16() : base>() {} - simdutf_really_inline base16(const __m256i _value) : base>(_value) {} - template - simdutf_really_inline base16(const Pointer* ptr) : base16(_mm256_loadu_si256(reinterpret_cast(ptr))) {} - friend simdutf_really_inline Mask operator==(const simd16 lhs, const simd16 rhs) { return _mm256_cmpeq_epi16(lhs, rhs); } - - /// the size of vector in bytes - static const int SIZE = sizeof(base>::value); - - /// the number of elements of type T a vector can hold - static const int ELEMENTS = SIZE / sizeof(T); - - template - simdutf_really_inline simd16 prev(const simd16 prev_chunk) const { - return _mm256_alignr_epi8(*this, prev_chunk, 16 - N); - } -}; - -// SIMD byte mask type (returned by things like eq and gt) -template<> -struct simd16: base16 { - static simdutf_really_inline simd16 splat(bool _value) { return _mm256_set1_epi16(uint16_t(-(!!_value))); } - - simdutf_really_inline simd16() : base16() {} - simdutf_really_inline simd16(const __m256i _value) : base16(_value) {} - // Splat constructor - simdutf_really_inline simd16(bool _value) : base16(splat(_value)) {} - - simdutf_really_inline bitmask_type to_bitmask() const { return _mm256_movemask_epi8(*this); } - simdutf_really_inline bool any() const { return !_mm256_testz_si256(*this, *this); } - simdutf_really_inline simd16 operator~() const { return *this ^ true; } -}; - -template -struct base16_numeric: base16 { - static simdutf_really_inline simd16 splat(T _value) { return _mm256_set1_epi16(_value); } - static simdutf_really_inline simd16 zero() { return _mm256_setzero_si256(); } - static simdutf_really_inline simd16 load(const T values[8]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - - simdutf_really_inline base16_numeric() : base16() {} - simdutf_really_inline base16_numeric(const __m256i _value) : base16(_value) {} - - // Store to array - simdutf_really_inline void store(T dst[8]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); } - - // Override to distinguish from bool version - simdutf_really_inline simd16 operator~() const { return *this ^ 0xFFFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd16 operator+(const simd16 other) const { return _mm256_add_epi16(*this, other); } - simdutf_really_inline simd16 operator-(const simd16 other) const { return _mm256_sub_epi16(*this, other); } - simdutf_really_inline simd16& operator+=(const simd16 other) { *this = *this + other; return *static_cast*>(this); } - simdutf_really_inline simd16& operator-=(const simd16 other) { *this = *this - other; return *static_cast*>(this); } -}; - -// Signed code units -template<> -struct simd16 : base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} - simdutf_really_inline simd16(const __m256i _value) : base16_numeric(_value) {} - // Splat constructor - simdutf_really_inline simd16(int16_t _value) : simd16(splat(_value)) {} - // Array constructor - simdutf_really_inline simd16(const int16_t* values) : simd16(load(values)) {} - simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast(values))) {} - // Order-sensitive comparisons - simdutf_really_inline simd16 max_val(const simd16 other) const { return _mm256_max_epi16(*this, other); } - simdutf_really_inline simd16 min_val(const simd16 other) const { return _mm256_min_epi16(*this, other); } - simdutf_really_inline simd16 operator>(const simd16 other) const { return _mm256_cmpgt_epi16(*this, other); } - simdutf_really_inline simd16 operator<(const simd16 other) const { return _mm256_cmpgt_epi16(other, *this); } -}; - -// Unsigned code units -template<> -struct simd16: base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} - simdutf_really_inline simd16(const __m256i _value) : base16_numeric(_value) {} - - // Splat constructor - simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {} - // Array constructor - simdutf_really_inline simd16(const uint16_t* values) : simd16(load(values)) {} - simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast(values))) {} - - // Saturated math - simdutf_really_inline simd16 saturating_add(const simd16 other) const { return _mm256_adds_epu16(*this, other); } - simdutf_really_inline simd16 saturating_sub(const simd16 other) const { return _mm256_subs_epu16(*this, other); } - - // Order-specific operations - simdutf_really_inline simd16 max_val(const simd16 other) const { return _mm256_max_epu16(*this, other); } - simdutf_really_inline simd16 min_val(const simd16 other) const { return _mm256_min_epu16(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd16 gt_bits(const simd16 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd16 lt_bits(const simd16 other) const { return other.saturating_sub(*this); } - simdutf_really_inline simd16 operator<=(const simd16 other) const { return other.max_val(*this) == other; } - simdutf_really_inline simd16 operator>=(const simd16 other) const { return other.min_val(*this) == other; } - simdutf_really_inline simd16 operator>(const simd16 other) const { return this->gt_bits(other).any_bits_set(); } - simdutf_really_inline simd16 operator<(const simd16 other) const { return this->gt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdutf_really_inline simd16 bits_not_set() const { return *this == uint16_t(0); } - simdutf_really_inline simd16 bits_not_set(simd16 bits) const { return (*this & bits).bits_not_set(); } - simdutf_really_inline simd16 any_bits_set() const { return ~this->bits_not_set(); } - simdutf_really_inline simd16 any_bits_set(simd16 bits) const { return ~this->bits_not_set(bits); } - - simdutf_really_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); } - simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdutf_really_inline bool bits_not_set_anywhere(simd16 bits) const { return _mm256_testz_si256(*this, bits); } - simdutf_really_inline bool any_bits_set_anywhere(simd16 bits) const { return !bits_not_set_anywhere(bits); } - template - simdutf_really_inline simd16 shr() const { return simd16(_mm256_srli_epi16(*this, N)); } - template - simdutf_really_inline simd16 shl() const { return simd16(_mm256_slli_epi16(*this, N)); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdutf_really_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 15-N)); } - - // Change the endianness - simdutf_really_inline simd16 swap_bytes() const { - const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - return _mm256_shuffle_epi8(*this, swap); - } - - // Pack with the unsigned saturation of two uint16_t code units into single uint8_t vector - static simdutf_really_inline simd8 pack(const simd16& v0, const simd16& v1) { - // Note: the AVX2 variant of pack operates on 128-bit lanes, thus - // we have to shuffle lanes in order to produce bytes in the - // correct order. - - // get the 0th lanes - const __m128i lo_0 = _mm256_extracti128_si256(v0, 0); - const __m128i lo_1 = _mm256_extracti128_si256(v1, 0); - - // get the 1st lanes - const __m128i hi_0 = _mm256_extracti128_si256(v0, 1); - const __m128i hi_1 = _mm256_extracti128_si256(v1, 1); - - // build new vectors (shuffle lanes) - const __m256i t0 = _mm256_set_m128i(lo_1, lo_0); - const __m256i t1 = _mm256_set_m128i(hi_1, hi_0); - - // pack code units in linear order from v0 and v1 - return _mm256_packus_epi16(t0, t1); - } -}; - - - template - struct simd16x32 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd16); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - simd16 chunks[NUM_CHUNKS]; - - simd16x32(const simd16x32& o) = delete; // no copy allowed - simd16x32& operator=(const simd16 other) = delete; // no assignment allowed - simd16x32() = delete; // no default constructor allowed - - simdutf_really_inline simd16x32(const simd16 chunk0, const simd16 chunk1) : chunks{chunk0, chunk1} {} - simdutf_really_inline simd16x32(const T* ptr) : chunks{simd16::load(ptr), simd16::load(ptr+sizeof(simd16)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr+sizeof(simd16)*0/sizeof(T)); - this->chunks[1].store(ptr+sizeof(simd16)*1/sizeof(T)); - } - - simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r_hi = this->chunks[1].to_bitmask(); - return r_lo | (r_hi << 32); - } - - simdutf_really_inline simd16 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } - - simdutf_really_inline bool is_ascii() const { - return this->reduce_or().is_ascii(); - } - - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - this->chunks[0].store_ascii_as_utf16(ptr+sizeof(simd16)*0); - this->chunks[1].store_ascii_as_utf16(ptr+sizeof(simd16)); - } - - simdutf_really_inline simd16x32 bit_or(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } - - simdutf_really_inline void swap_bytes() { - this->chunks[0] = this->chunks[0].swap_bytes(); - this->chunks[1] = this->chunks[1].swap_bytes(); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t eq(const simd16x32 &other) const { - return simd16x32( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd16 mask_low = simd16::splat(low); - const simd16 mask_high = simd16::splat(high); - - return simd16x32( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd16 mask_low = simd16::splat(static_cast(low-1)); - const simd16 mask_high = simd16::splat(static_cast(high+1)); - return simd16x32( - (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low), - (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] < mask, - this->chunks[1] < mask - ).to_bitmask(); - } - }; // struct simd16x32 -/* end file src/simdutf/haswell/simd16-inl.h */ - -} // namespace simd - -} // unnamed namespace -} // namespace haswell -} // namespace simdutf - -#endif // SIMDUTF_HASWELL_SIMD_H -/* end file src/simdutf/haswell/simd.h */ - -/* begin file src/simdutf/haswell/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_POP_DISABLE_WARNINGS -#endif // end of workaround -/* end file src/simdutf/haswell/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_HASWELL -#endif // SIMDUTF_HASWELL_COMMON_H -/* end file src/simdutf/haswell.h */ -/* begin file src/simdutf/westmere.h */ -#ifndef SIMDUTF_WESTMERE_H -#define SIMDUTF_WESTMERE_H - -#ifdef SIMDUTF_FALLBACK_H -#error "westmere.h must be included before fallback.h" -#endif - - -// Default Westmere to on if this is x86-64, unless we'll always select Haswell. -#ifndef SIMDUTF_IMPLEMENTATION_WESTMERE -// -// You do not want to set it to (SIMDUTF_IS_X86_64 && !SIMDUTF_REQUIRES_HASWELL) -// because you want to rely on runtime dispatch! -// -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE || SIMDUTF_CAN_ALWAYS_RUN_HASWELL -#define SIMDUTF_IMPLEMENTATION_WESTMERE 0 -#else -#define SIMDUTF_IMPLEMENTATION_WESTMERE (SIMDUTF_IS_X86_64) -#endif - -#endif - -#if (SIMDUTF_IMPLEMENTATION_WESTMERE && SIMDUTF_IS_X86_64 && __SSE4_2__) -#define SIMDUTF_CAN_ALWAYS_RUN_WESTMERE 1 -#else -#define SIMDUTF_CAN_ALWAYS_RUN_WESTMERE 0 -#endif - -#if SIMDUTF_IMPLEMENTATION_WESTMERE - -#define SIMDUTF_TARGET_WESTMERE SIMDUTF_TARGET_REGION("sse4.2,popcnt") - -namespace simdutf { -/** - * Implementation for Westmere (Intel SSE4.2). - */ -namespace westmere { -} // namespace westmere -} // namespace simdutf - -// -// These two need to be included outside SIMDUTF_TARGET_REGION -// -/* begin file src/simdutf/westmere/implementation.h */ -#ifndef SIMDUTF_WESTMERE_IMPLEMENTATION_H -#define SIMDUTF_WESTMERE_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDUTF_TARGET_REGION -namespace simdutf { -namespace westmere { - -namespace { -using namespace simdutf; -} - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() : simdutf::implementation("westmere", "Intel/AMD SSE4.2", internal::instruction_set::SSE42) {} - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -}; - -} // namespace westmere -} // namespace simdutf - -#endif // SIMDUTF_WESTMERE_IMPLEMENTATION_H -/* end file src/simdutf/westmere/implementation.h */ -/* begin file src/simdutf/westmere/intrinsics.h */ -#ifndef SIMDUTF_WESTMERE_INTRINSICS_H -#define SIMDUTF_WESTMERE_INTRINSICS_H - -#ifdef SIMDUTF_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else - -#if SIMDUTF_GCC11ORMORE -// We should not get warnings while including yet we do -// under some versions of GCC. -// If the x86intrin.h header has uninitialized values that are problematic, -// it is a GCC issue, we want to ignore these warnings. -SIMDUTF_DISABLE_GCC_WARNING(-Wuninitialized) -#endif - -#include // elsewhere - - -#if SIMDUTF_GCC11ORMORE -// cancels the suppression of the -Wuninitialized -SIMDUTF_POP_DISABLE_WARNINGS -#endif - -#endif // SIMDUTF_VISUAL_STUDIO - - -#ifdef SIMDUTF_CLANG_VISUAL_STUDIO -/** - * You are not supposed, normally, to include these - * headers directly. Instead you should either include intrin.h - * or x86intrin.h. However, when compiling with clang - * under Windows (i.e., when _MSC_VER is set), these headers - * only get included *if* the corresponding features are detected - * from macros: - */ -#include // for _mm_alignr_epi8 -#endif - - - -#endif // SIMDUTF_WESTMERE_INTRINSICS_H -/* end file src/simdutf/westmere/intrinsics.h */ - -// -// The rest need to be inside the region -// -/* begin file src/simdutf/westmere/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "westmere" -// #define SIMDUTF_IMPLEMENTATION westmere - -#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE -// nothing needed. -#else -SIMDUTF_TARGET_WESTMERE -#endif -/* end file src/simdutf/westmere/begin.h */ - -// Declarations -/* begin file src/simdutf/westmere/bitmanipulation.h */ -#ifndef SIMDUTF_WESTMERE_BITMANIPULATION_H -#define SIMDUTF_WESTMERE_BITMANIPULATION_H - -namespace simdutf { -namespace westmere { -namespace { - -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO -simdutf_really_inline unsigned __int64 count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num);// Visual Studio wants two underscores -} -#else -simdutf_really_inline long long int count_ones(uint64_t input_num) { - return _popcnt64(input_num); -} -#endif - -#if SIMDUTF_NEED_TRAILING_ZEROES -simdutf_really_inline int trailing_zeroes(uint64_t input_num) { -#if SIMDUTF_REGULAR_VISUAL_STUDIO - unsigned long ret; - _BitScanForward64(&ret, input_num); - return (int)ret; -#else // SIMDUTF_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDUTF_REGULAR_VISUAL_STUDIO -} -#endif - -} // unnamed namespace -} // namespace westmere -} // namespace simdutf - -#endif // SIMDUTF_WESTMERE_BITMANIPULATION_H -/* end file src/simdutf/westmere/bitmanipulation.h */ -/* begin file src/simdutf/westmere/simd.h */ -#ifndef SIMDUTF_WESTMERE_SIMD_H -#define SIMDUTF_WESTMERE_SIMD_H - -namespace simdutf { -namespace westmere { -namespace { -namespace simd { - - template - struct base { - __m128i value; - - // Zero constructor - simdutf_really_inline base() : value{__m128i()} {} - - // Conversion from SIMD register - simdutf_really_inline base(const __m128i _value) : value(_value) {} - // Conversion to SIMD register - simdutf_really_inline operator const __m128i&() const { return this->value; } - simdutf_really_inline operator __m128i&() { return this->value; } - template - simdutf_really_inline void store_ascii_as_utf16(char16_t * p) const { - __m128i first = _mm_cvtepu8_epi16(*this); - __m128i second = _mm_cvtepu8_epi16(_mm_srli_si128(*this,8)); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - first = _mm_shuffle_epi8(first, swap); - second = _mm_shuffle_epi8(second, swap); - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(p), first); - _mm_storeu_si128(reinterpret_cast<__m128i *>(p+8), second); - } - simdutf_really_inline void store_ascii_as_utf32(char32_t * p) const { - _mm_storeu_si128(reinterpret_cast<__m128i *>(p), _mm_cvtepu8_epi32(*this)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(p+4), _mm_cvtepu8_epi32(_mm_srli_si128(*this,4))); - _mm_storeu_si128(reinterpret_cast<__m128i *>(p+8), _mm_cvtepu8_epi32(_mm_srli_si128(*this,8))); - _mm_storeu_si128(reinterpret_cast<__m128i *>(p+12), _mm_cvtepu8_epi32(_mm_srli_si128(*this,12))); - } - // Bit operations - simdutf_really_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); } - simdutf_really_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); } - simdutf_really_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); } - simdutf_really_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); } - simdutf_really_inline Child& operator|=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast | other; return *this_cast; } - simdutf_really_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdutf_really_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8: base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - simdutf_really_inline T first() const { return _mm_extract_epi8(*this,0); } - simdutf_really_inline T last() const { return _mm_extract_epi8(*this,15); } - simdutf_really_inline base8() : base>() {} - simdutf_really_inline base8(const __m128i _value) : base>(_value) {} - - friend simdutf_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - - static const int SIZE = sizeof(base>::value); - - template - simdutf_really_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base8 { - static simdutf_really_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - - simdutf_really_inline simd8() : base8() {} - simdutf_really_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdutf_really_inline simd8(bool _value) : base8(splat(_value)) {} - - simdutf_really_inline int to_bitmask() const { return _mm_movemask_epi8(*this); } - simdutf_really_inline bool any() const { return !_mm_testz_si128(*this, *this); } - simdutf_really_inline bool none() const { return _mm_testz_si128(*this, *this); } - simdutf_really_inline bool all() const { return _mm_movemask_epi8(*this) == 0xFFFF; } - simdutf_really_inline simd8 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric: base8 { - static simdutf_really_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdutf_really_inline simd8 zero() { return _mm_setzero_si128(); } - static simdutf_really_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdutf_really_inline simd8 repeat_16( - T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7, - T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - simdutf_really_inline base8_numeric() : base8() {} - simdutf_really_inline base8_numeric(const __m128i _value) : base8(_value) {} - - // Store to array - simdutf_really_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } - - // Override to distinguish from bool version - simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdutf_really_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdutf_really_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdutf_really_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } - - template - simdutf_really_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m128i _value) : base8_numeric(_value) {} - // Splat constructor - simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const int8_t* values) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) : simd8(_mm_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - simdutf_really_inline operator simd8() const; - simdutf_really_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; } - - // Order-sensitive comparisons - simdutf_really_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdutf_really_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdutf_really_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; - - // Unsigned bytes - template<> - struct simd8: base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m128i _value) : base8_numeric(_value) {} - - // Splat constructor - simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const uint8_t* values) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) : simd8(_mm_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Saturated math - simdutf_really_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdutf_really_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - - // Order-specific operations - simdutf_really_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdutf_really_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdutf_really_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdutf_really_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdutf_really_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdutf_really_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdutf_really_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdutf_really_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdutf_really_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - simdutf_really_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; } - - simdutf_really_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); } - simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdutf_really_inline bool bits_not_set_anywhere(simd8 bits) const { return _mm_testz_si128(*this, bits); } - simdutf_really_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdutf_really_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdutf_really_inline simd8 shl() const { return simd8(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdutf_really_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } - }; - simdutf_really_inline simd8::operator simd8() const { return this->value; } - - // Unsigned bytes - template<> - struct simd8: base { - static simdutf_really_inline simd8 splat(uint16_t _value) { return _mm_set1_epi16(_value); } - static simdutf_really_inline simd8 load(const uint16_t values[8]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - - simdutf_really_inline simd8() : base() {} - simdutf_really_inline simd8(const __m128i _value) : base(_value) {} - // Splat constructor - simdutf_really_inline simd8(uint16_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const uint16_t* values) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline simd8( - uint16_t v0, uint16_t v1, uint16_t v2, uint16_t v3, uint16_t v4, uint16_t v5, uint16_t v6, uint16_t v7 - ) : simd8(_mm_setr_epi16( - v0, v1, v2, v3, v4, v5, v6, v7 - )) {} - - // Saturated math - simdutf_really_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu16(*this, other); } - simdutf_really_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu16(*this, other); } - - // Order-specific operations - simdutf_really_inline simd8 max_val(const simd8 other) const { return _mm_max_epu16(*this, other); } - simdutf_really_inline simd8 min_val(const simd8 other) const { return _mm_min_epu16(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdutf_really_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdutf_really_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdutf_really_inline simd8 operator==(const simd8 other) const { return _mm_cmpeq_epi16(*this, other); } - simdutf_really_inline simd8 operator&(const simd8 other) const { return _mm_and_si128(*this, other); } - simdutf_really_inline simd8 operator|(const simd8 other) const { return _mm_or_si128(*this, other); } - - // Bit-specific operations - simdutf_really_inline simd8 bits_not_set() const { return *this == uint16_t(0); } - simdutf_really_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - - simdutf_really_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); } - simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdutf_really_inline bool bits_not_set_anywhere(simd8 bits) const { return _mm_testz_si128(*this, bits); } - simdutf_really_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8 other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8::load(ptr), simd8::load(ptr+sizeof(simd8)/sizeof(T)), simd8::load(ptr+2*sizeof(simd8)/sizeof(T)), simd8::load(ptr+3*sizeof(simd8)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr+sizeof(simd8)*0/sizeof(T)); - this->chunks[1].store(ptr+sizeof(simd8)*1/sizeof(T)); - this->chunks[2].store(ptr+sizeof(simd8)*2/sizeof(T)); - this->chunks[3].store(ptr+sizeof(simd8)*3/sizeof(T)); - } - - simdutf_really_inline simd8x64& operator |=(const simd8x64 &other) { - this->chunks[0] |= other.chunks[0]; - this->chunks[1] |= other.chunks[1]; - this->chunks[2] |= other.chunks[2]; - this->chunks[3] |= other.chunks[3]; - return *this; - } - - simdutf_really_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } - - simdutf_really_inline bool is_ascii() const { - return this->reduce_or().is_ascii(); - } - - template - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - this->chunks[0].template store_ascii_as_utf16(ptr+sizeof(simd8)*0); - this->chunks[1].template store_ascii_as_utf16(ptr+sizeof(simd8)*1); - this->chunks[2].template store_ascii_as_utf16(ptr+sizeof(simd8)*2); - this->chunks[3].template store_ascii_as_utf16(ptr+sizeof(simd8)*3); - } - - simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const { - this->chunks[0].store_ascii_as_utf32(ptr+sizeof(simd8)*0); - this->chunks[1].store_ascii_as_utf32(ptr+sizeof(simd8)*1); - this->chunks[2].store_ascii_as_utf32(ptr+sizeof(simd8)*2); - this->chunks[3].store_ascii_as_utf32(ptr+sizeof(simd8)*3); - } - - simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r0 = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r1 = this->chunks[1].to_bitmask(); - uint64_t r2 = this->chunks[2].to_bitmask(); - uint64_t r3 = this->chunks[3].to_bitmask(); - return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t eq(const simd8x64 &other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1], - this->chunks[2] == other.chunks[2], - this->chunks[3] == other.chunks[3] - ).to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - - return simd8x64( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low), - (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low), - (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low-1); - const simd8 mask_high = simd8::splat(high+1); - return simd8x64( - (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low), - (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low), - (this->chunks[2] >= mask_high) | (this->chunks[2] <= mask_low), - (this->chunks[3] >= mask_high) | (this->chunks[3] <= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] < mask, - this->chunks[1] < mask, - this->chunks[2] < mask, - this->chunks[3] < mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t gt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] > mask, - this->chunks[1] > mask, - this->chunks[2] > mask, - this->chunks[3] > mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] >= mask, - this->chunks[1] >= mask, - this->chunks[2] >= mask, - this->chunks[3] >= mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - simd8(__m128i(this->chunks[0])) >= mask, - simd8(__m128i(this->chunks[1])) >= mask, - simd8(__m128i(this->chunks[2])) >= mask, - simd8(__m128i(this->chunks[3])) >= mask - ).to_bitmask(); - } - }; // struct simd8x64 - -/* begin file src/simdutf/westmere/simd16-inl.h */ -template -struct simd16; - -template> -struct base16: base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - simdutf_really_inline base16() : base>() {} - simdutf_really_inline base16(const __m128i _value) : base>(_value) {} - template - simdutf_really_inline base16(const Pointer* ptr) : base16(_mm_loadu_si128(reinterpret_cast(ptr))) {} - - friend simdutf_really_inline Mask operator==(const simd16 lhs, const simd16 rhs) { return _mm_cmpeq_epi16(lhs, rhs); } - - static const int SIZE = sizeof(base>::value); - - template - simdutf_really_inline simd16 prev(const simd16 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } -}; - -// SIMD byte mask type (returned by things like eq and gt) -template<> -struct simd16: base16 { - static simdutf_really_inline simd16 splat(bool _value) { return _mm_set1_epi16(uint16_t(-(!!_value))); } - - simdutf_really_inline simd16() : base16() {} - simdutf_really_inline simd16(const __m128i _value) : base16(_value) {} - // Splat constructor - simdutf_really_inline simd16(bool _value) : base16(splat(_value)) {} - - simdutf_really_inline int to_bitmask() const { return _mm_movemask_epi8(*this); } - simdutf_really_inline bool any() const { return !_mm_testz_si128(*this, *this); } - simdutf_really_inline simd16 operator~() const { return *this ^ true; } -}; - -template -struct base16_numeric: base16 { - static simdutf_really_inline simd16 splat(T _value) { return _mm_set1_epi16(_value); } - static simdutf_really_inline simd16 zero() { return _mm_setzero_si128(); } - static simdutf_really_inline simd16 load(const T values[8]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - - simdutf_really_inline base16_numeric() : base16() {} - simdutf_really_inline base16_numeric(const __m128i _value) : base16(_value) {} - - // Store to array - simdutf_really_inline void store(T dst[8]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } - - // Override to distinguish from bool version - simdutf_really_inline simd16 operator~() const { return *this ^ 0xFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd16 operator+(const simd16 other) const { return _mm_add_epi16(*this, other); } - simdutf_really_inline simd16 operator-(const simd16 other) const { return _mm_sub_epi16(*this, other); } - simdutf_really_inline simd16& operator+=(const simd16 other) { *this = *this + other; return *static_cast*>(this); } - simdutf_really_inline simd16& operator-=(const simd16 other) { *this = *this - other; return *static_cast*>(this); } -}; - -// Signed code units -template<> -struct simd16 : base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} - simdutf_really_inline simd16(const __m128i _value) : base16_numeric(_value) {} - // Splat constructor - simdutf_really_inline simd16(int16_t _value) : simd16(splat(_value)) {} - // Array constructor - simdutf_really_inline simd16(const int16_t* values) : simd16(load(values)) {} - simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast(values))) {} - // Member-by-member initialization - simdutf_really_inline simd16( - int16_t v0, int16_t v1, int16_t v2, int16_t v3, int16_t v4, int16_t v5, int16_t v6, int16_t v7) - : simd16(_mm_setr_epi16(v0, v1, v2, v3, v4, v5, v6, v7)) {} - simdutf_really_inline operator simd16() const; - - // Order-sensitive comparisons - simdutf_really_inline simd16 max_val(const simd16 other) const { return _mm_max_epi16(*this, other); } - simdutf_really_inline simd16 min_val(const simd16 other) const { return _mm_min_epi16(*this, other); } - simdutf_really_inline simd16 operator>(const simd16 other) const { return _mm_cmpgt_epi16(*this, other); } - simdutf_really_inline simd16 operator<(const simd16 other) const { return _mm_cmpgt_epi16(other, *this); } -}; - -// Unsigned code units -template<> -struct simd16: base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} - simdutf_really_inline simd16(const __m128i _value) : base16_numeric(_value) {} - - // Splat constructor - simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {} - // Array constructor - simdutf_really_inline simd16(const uint16_t* values) : simd16(load(values)) {} - simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast(values))) {} - // Member-by-member initialization - simdutf_really_inline simd16( - uint16_t v0, uint16_t v1, uint16_t v2, uint16_t v3, uint16_t v4, uint16_t v5, uint16_t v6, uint16_t v7) - : simd16(_mm_setr_epi16(v0, v1, v2, v3, v4, v5, v6, v7)) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd16 repeat_16( - uint16_t v0, uint16_t v1, uint16_t v2, uint16_t v3, uint16_t v4, uint16_t v5, uint16_t v6, uint16_t v7 - ) { - return simd16(v0, v1, v2, v3, v4, v5, v6, v7); - } - - // Saturated math - simdutf_really_inline simd16 saturating_add(const simd16 other) const { return _mm_adds_epu16(*this, other); } - simdutf_really_inline simd16 saturating_sub(const simd16 other) const { return _mm_subs_epu16(*this, other); } - - // Order-specific operations - simdutf_really_inline simd16 max_val(const simd16 other) const { return _mm_max_epu16(*this, other); } - simdutf_really_inline simd16 min_val(const simd16 other) const { return _mm_min_epu16(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd16 gt_bits(const simd16 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd16 lt_bits(const simd16 other) const { return other.saturating_sub(*this); } - simdutf_really_inline simd16 operator<=(const simd16 other) const { return other.max_val(*this) == other; } - simdutf_really_inline simd16 operator>=(const simd16 other) const { return other.min_val(*this) == other; } - simdutf_really_inline simd16 operator>(const simd16 other) const { return this->gt_bits(other).any_bits_set(); } - simdutf_really_inline simd16 operator<(const simd16 other) const { return this->gt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdutf_really_inline simd16 bits_not_set() const { return *this == uint16_t(0); } - simdutf_really_inline simd16 bits_not_set(simd16 bits) const { return (*this & bits).bits_not_set(); } - simdutf_really_inline simd16 any_bits_set() const { return ~this->bits_not_set(); } - simdutf_really_inline simd16 any_bits_set(simd16 bits) const { return ~this->bits_not_set(bits); } - - simdutf_really_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); } - simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdutf_really_inline bool bits_not_set_anywhere(simd16 bits) const { return _mm_testz_si128(*this, bits); } - simdutf_really_inline bool any_bits_set_anywhere(simd16 bits) const { return !bits_not_set_anywhere(bits); } - template - simdutf_really_inline simd16 shr() const { return simd16(_mm_srli_epi16(*this, N)); } - template - simdutf_really_inline simd16 shl() const { return simd16(_mm_slli_epi16(*this, N)); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdutf_really_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } - - // Change the endianness - simdutf_really_inline simd16 swap_bytes() const { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - return _mm_shuffle_epi8(*this, swap); - } - - // Pack with the unsigned saturation of two uint16_t code units into single uint8_t vector - static simdutf_really_inline simd8 pack(const simd16& v0, const simd16& v1) { - return _mm_packus_epi16(v0, v1); - } -}; -simdutf_really_inline simd16::operator simd16() const { return this->value; } - -template - struct simd16x32 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd16); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - simd16 chunks[NUM_CHUNKS]; - - simd16x32(const simd16x32& o) = delete; // no copy allowed - simd16x32& operator=(const simd16 other) = delete; // no assignment allowed - simd16x32() = delete; // no default constructor allowed - - simdutf_really_inline simd16x32(const simd16 chunk0, const simd16 chunk1, const simd16 chunk2, const simd16 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdutf_really_inline simd16x32(const T* ptr) : chunks{simd16::load(ptr), simd16::load(ptr+sizeof(simd16)/sizeof(T)), simd16::load(ptr+2*sizeof(simd16)/sizeof(T)), simd16::load(ptr+3*sizeof(simd16)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr+sizeof(simd16)*0/sizeof(T)); - this->chunks[1].store(ptr+sizeof(simd16)*1/sizeof(T)); - this->chunks[2].store(ptr+sizeof(simd16)*2/sizeof(T)); - this->chunks[3].store(ptr+sizeof(simd16)*3/sizeof(T)); - } - - simdutf_really_inline simd16 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } - - simdutf_really_inline bool is_ascii() const { - return this->reduce_or().is_ascii(); - } - - simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const { - this->chunks[0].store_ascii_as_utf16(ptr+sizeof(simd16)*0); - this->chunks[1].store_ascii_as_utf16(ptr+sizeof(simd16)*1); - this->chunks[2].store_ascii_as_utf16(ptr+sizeof(simd16)*2); - this->chunks[3].store_ascii_as_utf16(ptr+sizeof(simd16)*3); - } - - simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r0 = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r1 = this->chunks[1].to_bitmask(); - uint64_t r2 = this->chunks[2].to_bitmask(); - uint64_t r3 = this->chunks[3].to_bitmask(); - return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); - } - - simdutf_really_inline void swap_bytes() { - this->chunks[0] = this->chunks[0].swap_bytes(); - this->chunks[1] = this->chunks[1].swap_bytes(); - this->chunks[2] = this->chunks[2].swap_bytes(); - this->chunks[3] = this->chunks[3].swap_bytes(); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t eq(const simd16x32 &other) const { - return simd16x32( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1], - this->chunks[2] == other.chunks[2], - this->chunks[3] == other.chunks[3] - ).to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd16 mask_low = simd16::splat(low); - const simd16 mask_high = simd16::splat(high); - - return simd16x32( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low), - (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low), - (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd16 mask_low = simd16::splat(static_cast(low-1)); - const simd16 mask_high = simd16::splat(static_cast(high+1)); - return simd16x32( - (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low), - (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low), - (this->chunks[2] >= mask_high) | (this->chunks[2] <= mask_low), - (this->chunks[3] >= mask_high) | (this->chunks[3] <= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd16 mask = simd16::splat(m); - return simd16x32( - this->chunks[0] < mask, - this->chunks[1] < mask, - this->chunks[2] < mask, - this->chunks[3] < mask - ).to_bitmask(); - } - }; // struct simd16x32 -/* end file src/simdutf/westmere/simd16-inl.h */ - -} // namespace simd -} // unnamed namespace -} // namespace westmere -} // namespace simdutf - -#endif // SIMDUTF_WESTMERE_SIMD_INPUT_H -/* end file src/simdutf/westmere/simd.h */ - -/* begin file src/simdutf/westmere/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - -/* end file src/simdutf/westmere/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_WESTMERE -#endif // SIMDUTF_WESTMERE_COMMON_H -/* end file src/simdutf/westmere.h */ -/* begin file src/simdutf/ppc64.h */ -#ifndef SIMDUTF_PPC64_H -#define SIMDUTF_PPC64_H - -#ifdef SIMDUTF_FALLBACK_H -#error "ppc64.h must be included before fallback.h" -#endif - - -#ifndef SIMDUTF_IMPLEMENTATION_PPC64 -#define SIMDUTF_IMPLEMENTATION_PPC64 (SIMDUTF_IS_PPC64) -#endif -#define SIMDUTF_CAN_ALWAYS_RUN_PPC64 SIMDUTF_IMPLEMENTATION_PPC64 && SIMDUTF_IS_PPC64 - - - -#if SIMDUTF_IMPLEMENTATION_PPC64 - -namespace simdutf { -/** - * Implementation for ALTIVEC (PPC64). - */ -namespace ppc64 { -} // namespace ppc64 -} // namespace simdutf - -/* begin file src/simdutf/ppc64/implementation.h */ -#ifndef SIMDUTF_PPC64_IMPLEMENTATION_H -#define SIMDUTF_PPC64_IMPLEMENTATION_H - - -namespace simdutf { -namespace ppc64 { - -namespace { -using namespace simdutf; -} // namespace - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() - : simdutf::implementation("ppc64", "PPC64 ALTIVEC", - internal::instruction_set::ALTIVEC) {} - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -}; - -} // namespace ppc64 -} // namespace simdutf - -#endif // SIMDUTF_PPC64_IMPLEMENTATION_H -/* end file src/simdutf/ppc64/implementation.h */ - -/* begin file src/simdutf/ppc64/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "ppc64" -// #define SIMDUTF_IMPLEMENTATION ppc64 -/* end file src/simdutf/ppc64/begin.h */ - -// Declarations -/* begin file src/simdutf/ppc64/intrinsics.h */ -#ifndef SIMDUTF_PPC64_INTRINSICS_H -#define SIMDUTF_PPC64_INTRINSICS_H - - -// This should be the correct header whether -// you use visual studio or other compilers. -#include - -// These are defined by altivec.h in GCC toolchain, it is safe to undef them. -#ifdef bool -#undef bool -#endif - -#ifdef vector -#undef vector -#endif - -#endif // SIMDUTF_PPC64_INTRINSICS_H -/* end file src/simdutf/ppc64/intrinsics.h */ -/* begin file src/simdutf/ppc64/bitmanipulation.h */ -#ifndef SIMDUTF_PPC64_BITMANIPULATION_H -#define SIMDUTF_PPC64_BITMANIPULATION_H - -namespace simdutf { -namespace ppc64 { -namespace { - -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO -simdutf_really_inline int count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num); // Visual Studio wants two underscores -} -#else -simdutf_really_inline int count_ones(uint64_t input_num) { - return __builtin_popcountll(input_num); -} -#endif - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf - -#endif // SIMDUTF_PPC64_BITMANIPULATION_H -/* end file src/simdutf/ppc64/bitmanipulation.h */ -/* begin file src/simdutf/ppc64/simd.h */ -#ifndef SIMDUTF_PPC64_SIMD_H -#define SIMDUTF_PPC64_SIMD_H - -#include - -namespace simdutf { -namespace ppc64 { -namespace { -namespace simd { - -using __m128i = __vector unsigned char; - -template struct base { - __m128i value; - - // Zero constructor - simdutf_really_inline base() : value{__m128i()} {} - - // Conversion from SIMD register - simdutf_really_inline base(const __m128i _value) : value(_value) {} - - // Conversion to SIMD register - simdutf_really_inline operator const __m128i &() const { - return this->value; - } - simdutf_really_inline operator __m128i &() { return this->value; } - - // Bit operations - simdutf_really_inline Child operator|(const Child other) const { - return vec_or(this->value, (__m128i)other); - } - simdutf_really_inline Child operator&(const Child other) const { - return vec_and(this->value, (__m128i)other); - } - simdutf_really_inline Child operator^(const Child other) const { - return vec_xor(this->value, (__m128i)other); - } - simdutf_really_inline Child bit_andnot(const Child other) const { - return vec_andc(this->value, (__m128i)other); - } - simdutf_really_inline Child &operator|=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast | other; - return *this_cast; - } - simdutf_really_inline Child &operator&=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast & other; - return *this_cast; - } - simdutf_really_inline Child &operator^=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast ^ other; - return *this_cast; - } -}; - -// Forward-declared so they can be used by splat and friends. -template struct simd8; - -template > -struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - simdutf_really_inline base8() : base>() {} - simdutf_really_inline base8(const __m128i _value) : base>(_value) {} - - friend simdutf_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } - - static const int SIZE = sizeof(base>::value); - - template - simdutf_really_inline simd8 prev(simd8 prev_chunk) const { - __m128i chunk = this->value; -#ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve(this->value); - prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk); -#endif - chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N); -#ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve((__m128i)chunk); -#endif - return chunk; - } -}; - -// SIMD byte mask type (returned by things like eq and gt) -template <> struct simd8 : base8 { - static simdutf_really_inline simd8 splat(bool _value) { - return (__m128i)vec_splats((unsigned char)(-(!!_value))); - } - - simdutf_really_inline simd8() : base8() {} - simdutf_really_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdutf_really_inline simd8(bool _value) - : base8(splat(_value)) {} - - simdutf_really_inline int to_bitmask() const { - __vector unsigned long long result; - const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, - 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00}; - - result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value, - (__m128i)perm_mask)); -#ifdef __LITTLE_ENDIAN__ - return static_cast(result[1]); -#else - return static_cast(result[0]); -#endif - } - simdutf_really_inline bool any() const { - return !vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdutf_really_inline simd8 operator~() const { - return this->value ^ (__m128i)splat(true); - } -}; - -template struct base8_numeric : base8 { - static simdutf_really_inline simd8 splat(T value) { - (void)value; - return (__m128i)vec_splats(value); - } - static simdutf_really_inline simd8 zero() { return splat(0); } - static simdutf_really_inline simd8 load(const T values[16]) { - return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdutf_really_inline simd8 repeat_16(T v0, T v1, T v2, T v3, T v4, - T v5, T v6, T v7, T v8, T v9, - T v10, T v11, T v12, T v13, - T v14, T v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15); - } - - simdutf_really_inline base8_numeric() : base8() {} - simdutf_really_inline base8_numeric(const __m128i _value) - : base8(_value) {} - - // Store to array - simdutf_really_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst)); - } - - // Override to distinguish from bool version - simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdutf_really_inline simd8 operator+(const simd8 other) const { - return (__m128i)((__m128i)this->value + (__m128i)other); - } - simdutf_really_inline simd8 operator-(const simd8 other) const { - return (__m128i)((__m128i)this->value - (__m128i)other); - } - simdutf_really_inline simd8 &operator+=(const simd8 other) { - *this = *this + other; - return *static_cast *>(this); - } - simdutf_really_inline simd8 &operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); - } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior - // for out of range values) - template - simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value); - } - - template - simdutf_really_inline simd8 - lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4, - L replace5, L replace6, L replace7, L replace8, L replace9, - L replace10, L replace11, L replace12, L replace13, L replace14, - L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, replace4, replace5, replace6, - replace7, replace8, replace9, replace10, replace11, replace12, - replace13, replace14, replace15)); - } -}; - -// Signed bytes -template <> struct simd8 : base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m128i _value) - : base8_numeric(_value) {} - - // Splat constructor - simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const int8_t *values) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline simd8(int8_t v0, int8_t v1, int8_t v2, int8_t v3, - int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, - int8_t v12, int8_t v13, int8_t v14, int8_t v15) - : simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, - v15}) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 - repeat_16(int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, - int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, - int8_t v12, int8_t v13, int8_t v14, int8_t v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } - - // Order-sensitive comparisons - simdutf_really_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdutf_really_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdutf_really_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdutf_really_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } -}; - -// Unsigned bytes -template <> struct simd8 : base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m128i _value) - : base8_numeric(_value) {} - // Splat constructor - simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const uint8_t *values) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline - simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, - uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, - uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15) - : simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15}) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 - repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, - uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, - uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, - uint8_t v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } - - // Saturated math - simdutf_really_inline simd8 - saturating_add(const simd8 other) const { - return (__m128i)vec_adds(this->value, (__m128i)other); - } - simdutf_really_inline simd8 - saturating_sub(const simd8 other) const { - return (__m128i)vec_subs(this->value, (__m128i)other); - } - - // Order-specific operations - simdutf_really_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max(this->value, (__m128i)other); - } - simdutf_really_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min(this->value, (__m128i)other); - } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 - lt_bits(const simd8 other) const { - return other.saturating_sub(*this); - } - simdutf_really_inline simd8 - operator<=(const simd8 other) const { - return other.max_val(*this) == other; - } - simdutf_really_inline simd8 - operator>=(const simd8 other) const { - return other.min_val(*this) == other; - } - simdutf_really_inline simd8 - operator>(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - simdutf_really_inline simd8 - operator<(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - - // Bit-specific operations - simdutf_really_inline simd8 bits_not_set() const { - return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); - } - simdutf_really_inline simd8 bits_not_set(simd8 bits) const { - return (*this & bits).bits_not_set(); - } - simdutf_really_inline simd8 any_bits_set() const { - return ~this->bits_not_set(); - } - simdutf_really_inline simd8 any_bits_set(simd8 bits) const { - return ~this->bits_not_set(bits); - } - - simdutf_really_inline bool is_ascii() const { - return this->saturating_sub(0b01111111u).bits_not_set_anywhere(); - } - - simdutf_really_inline bool bits_not_set_anywhere() const { - return vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdutf_really_inline bool any_bits_set_anywhere() const { - return !bits_not_set_anywhere(); - } - simdutf_really_inline bool bits_not_set_anywhere(simd8 bits) const { - return vec_all_eq(vec_and(this->value, (__m128i)bits), - (__m128i)vec_splats(0)); - } - simdutf_really_inline bool any_bits_set_anywhere(simd8 bits) const { - return !bits_not_set_anywhere(bits); - } - template simdutf_really_inline simd8 shr() const { - return simd8( - (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); - } - template simdutf_really_inline simd8 shl() const { - return simd8( - (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); - } -}; - -template struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, - "PPC64 kernel should use four registers per 64-byte block."); - simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64 &o) = delete; // no copy allowed - simd8x64 & - operator=(const simd8 other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{chunk0, chunk1, chunk2, chunk3} {} - - simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8::load(ptr), simd8::load(ptr+sizeof(simd8)/sizeof(T)), simd8::load(ptr+2*sizeof(simd8)/sizeof(T)), simd8::load(ptr+3*sizeof(simd8)/sizeof(T))} {} - - simdutf_really_inline void store(T* ptr) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0/sizeof(T)); - this->chunks[1].store(ptr + sizeof(simd8) * 1/sizeof(T)); - this->chunks[2].store(ptr + sizeof(simd8) * 2/sizeof(T)); - this->chunks[3].store(ptr + sizeof(simd8) * 3/sizeof(T)); - } - - - simdutf_really_inline simd8x64& operator |=(const simd8x64 &other) { - this->chunks[0] |= other.chunks[0]; - this->chunks[1] |= other.chunks[1]; - this->chunks[2] |= other.chunks[2]; - this->chunks[3] |= other.chunks[3]; - return *this; - } - - simdutf_really_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } - - - simdutf_really_inline bool is_ascii() const { - return input.reduce_or().is_ascii(); - } - - simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r0 = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r1 = this->chunks[1].to_bitmask(); - uint64_t r2 = this->chunks[2].to_bitmask(); - uint64_t r3 = this->chunks[3].to_bitmask(); - return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); - } - - simdutf_really_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, - this->chunks[2] == mask, this->chunks[3] == mask) - .to_bitmask(); - } - - simdutf_really_inline uint64_t eq(const simd8x64 &other) const { - return simd8x64(this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1], - this->chunks[2] == other.chunks[2], - this->chunks[3] == other.chunks[3]) - .to_bitmask(); - } - - simdutf_really_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) - .to_bitmask(); - } - - simdutf_really_inline uint64_t in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - - return simd8x64( - (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low), - (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low), - (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low), - (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t not_in_range(const T low, const T high) const { - const simd8 mask_low = simd8::splat(low); - const simd8 mask_high = simd8::splat(high); - return simd8x64( - (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low), - (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low), - (this->chunks[2] > mask_high) | (this->chunks[2] < mask_low), - (this->chunks[3] > mask_high) | (this->chunks[3] < mask_low) - ).to_bitmask(); - } - simdutf_really_inline uint64_t lt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] < mask, this->chunks[1] < mask, - this->chunks[2] < mask, this->chunks[3] < mask) - .to_bitmask(); - } - - simdutf_really_inline uint64_t gt(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] > mask, - this->chunks[1] > mask, - this->chunks[2] > mask, - this->chunks[3] > mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] >= mask, - this->chunks[1] >= mask, - this->chunks[2] >= mask, - this->chunks[3] >= mask - ).to_bitmask(); - } - simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - simd8(this->chunks[0]) >= mask, - simd8(this->chunks[1]) >= mask, - simd8(this->chunks[2]) >= mask, - simd8(this->chunks[3]) >= mask - ).to_bitmask(); - } -}; // struct simd8x64 - -} // namespace simd -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf - -#endif // SIMDUTF_PPC64_SIMD_INPUT_H -/* end file src/simdutf/ppc64/simd.h */ - -/* begin file src/simdutf/ppc64/end.h */ -/* end file src/simdutf/ppc64/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_PPC64 - -#endif // SIMDUTF_PPC64_H -/* end file src/simdutf/ppc64.h */ -/* begin file src/simdutf/rvv.h */ -#ifndef SIMDUTF_RVV_H -#define SIMDUTF_RVV_H - -#ifdef SIMDUTF_FALLBACK_H -#error "rvv.h must be included before fallback.h" -#endif - - -#define SIMDUTF_CAN_ALWAYS_RUN_RVV SIMDUTF_IS_RVV - -#ifndef SIMDUTF_IMPLEMENTATION_RVV -#define SIMDUTF_IMPLEMENTATION_RVV (SIMDUTF_CAN_ALWAYS_RUN_RVV || (SIMDUTF_IS_RISCV64 && SIMDUTF_HAS_RVV_INTRINSICS && SIMDUTF_HAS_RVV_TARGET_REGION)) -#endif - -#if SIMDUTF_IMPLEMENTATION_RVV - -#if SIMDUTF_CAN_ALWAYS_RUN_RVV -#define SIMDUTF_TARGET_RVV -#else -#define SIMDUTF_TARGET_RVV SIMDUTF_TARGET_REGION("arch=+v") -#endif -#if !SIMDUTF_IS_ZVBB && SIMDUTF_HAS_ZVBB_INTRINSICS -#define SIMDUTF_TARGET_ZVBB SIMDUTF_TARGET_REGION("arch=+v,+zvbb") -#endif - -namespace simdutf { -namespace rvv { -} // namespace rvv -} // namespace simdutf - -/* begin file src/simdutf/rvv/implementation.h */ -#ifndef SIMDUTF_RVV_IMPLEMENTATION_H -#define SIMDUTF_RVV_IMPLEMENTATION_H - - -namespace simdutf { -namespace rvv { - -namespace { -using namespace simdutf; -} // namespace - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() - : simdutf::implementation("rvv", "RISC-V Vector Extension", - internal::instruction_set::RVV) - , _supports_zvbb(internal::detect_supported_architectures() & internal::instruction_set::ZVBB) - {} - simdutf_warn_unused int detect_encodings(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf8(const char *buf, size_t len, char *utf8_output) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf32(const char *buf, size_t len, char32_t *utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_latin1(const char *buf, size_t len, char *latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char *buf, size_t len, char *latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char *buf, size_t len, char16_t *utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char *buf, size_t len, char16_t *utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char *buf, size_t len, char16_t *utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char *buf, size_t len, char16_t *utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char *buf, size_t len, char32_t *utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char *buf, size_t len, char32_t *utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t *buf, size_t len, char *latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len, char *latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t *buf, size_t len, char *latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t *buf, size_t len, char16_t *output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t count_utf8(const char *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *buf, size_t len) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char *buf, size_t len) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf8(const char *buf, size_t len) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf16(size_t len) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf32(size_t len) const noexcept; - simdutf_warn_unused size_t utf32_length_from_latin1(size_t len) const noexcept; - simdutf_warn_unused size_t utf16_length_from_latin1(size_t len) const noexcept; - simdutf_warn_unused size_t utf8_length_from_latin1(const char *buf, size_t len) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -private: - const bool _supports_zvbb; - -#if SIMDUTF_IS_ZVBB - bool supports_zvbb() const { return true; } -#elif SIMDUTF_HAS_ZVBB_INTRINSICS - bool supports_zvbb() const { return _supports_zvbb; } -#else - bool supports_zvbb() const { return false; } -#endif -}; - -} // namespace rvv -} // namespace simdutf - -#endif // SIMDUTF_RVV_IMPLEMENTATION_H -/* end file src/simdutf/rvv/implementation.h */ -/* begin file src/simdutf/rvv/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "rvv" -// #define SIMDUTF_IMPLEMENTATION rvv - -#if SIMDUTF_CAN_ALWAYS_RUN_RVV -// nothing needed. -#else -SIMDUTF_TARGET_RVV -#endif -/* end file src/simdutf/rvv/begin.h */ -/* begin file src/simdutf/rvv/intrinsics.h */ -#ifndef SIMDUTF_RVV_INTRINSICS_H -#define SIMDUTF_RVV_INTRINSICS_H - - -#include - -#if __riscv_v_intrinsic >= 1000000 || __GCC__ >= 14 -#define simdutf_vrgather_u8m1x2(tbl, idx) __riscv_vcreate_v_u8m1_u8m2( \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), __riscv_vsetvlmax_e8m1()), \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), __riscv_vsetvlmax_e8m1())); - -#define simdutf_vrgather_u8m1x4(tbl, idx) __riscv_vcreate_v_u8m1_u8m4( \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), __riscv_vsetvlmax_e8m1()), \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), __riscv_vsetvlmax_e8m1()), \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), __riscv_vsetvlmax_e8m1()), \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), __riscv_vsetvlmax_e8m1())); -#else -// This has worse codegen on gcc -#define simdutf_vrgather_u8m1x2(tbl, idx) \ - __riscv_vset_v_u8m1_u8m2(__riscv_vlmul_ext_v_u8m1_u8m2( \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), __riscv_vsetvlmax_e8m1())), 1, \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), __riscv_vsetvlmax_e8m1())) - -#define simdutf_vrgather_u8m1x4(tbl, idx) \ - __riscv_vset_v_u8m1_u8m4(__riscv_vset_v_u8m1_u8m4(\ - __riscv_vset_v_u8m1_u8m4(__riscv_vlmul_ext_v_u8m1_u8m4( \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), __riscv_vsetvlmax_e8m1())), 1, \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), __riscv_vsetvlmax_e8m1())), 2, \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), __riscv_vsetvlmax_e8m1())), 3, \ - __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), __riscv_vsetvlmax_e8m1())) -#endif - -/* Zvbb adds dedicated support for endianness swaps with vrev8, but if we can't - * use that, we have to emulate it with the standard V extension. - * Using LMUL=1 vrgathers could be faster than the srl+macc variant, but that - * would increase register pressure, and vrgather implementations performance - * varies a lot. */ -enum class simdutf_ByteFlip { NONE, V, ZVBB }; - -template -simdutf_really_inline static uint16_t simdutf_byteflip(uint16_t v) { - if (method != simdutf_ByteFlip::NONE) - return (uint16_t)((v*1u) << 8 | (v*1u) >> 8); - return v; -} - -#ifdef SIMDUTF_TARGET_ZVBB -SIMDUTF_UNTARGET_REGION -SIMDUTF_TARGET_ZVBB -#endif - -template -simdutf_really_inline static vuint16m1_t simdutf_byteflip(vuint16m1_t v, size_t vl) { -#if SIMDUTF_HAS_ZVBB_INTRINSICS - if (method == simdutf_ByteFlip::ZVBB) - return __riscv_vrev8_v_u16m1(v, vl); -#endif - if (method == simdutf_ByteFlip::V) - return __riscv_vmacc_vx_u16m1(__riscv_vsrl_vx_u16m1(v, 8, vl), 0x100, v, vl); - return v; -} - -template -simdutf_really_inline static vuint16m2_t simdutf_byteflip(vuint16m2_t v, size_t vl) { -#if SIMDUTF_HAS_ZVBB_INTRINSICS - if (method == simdutf_ByteFlip::ZVBB) - return __riscv_vrev8_v_u16m2(v, vl); -#endif - if (method == simdutf_ByteFlip::V) - return __riscv_vmacc_vx_u16m2(__riscv_vsrl_vx_u16m2(v, 8, vl), 0x100, v, vl); - return v; -} - -template -simdutf_really_inline static vuint16m4_t simdutf_byteflip(vuint16m4_t v, size_t vl) { -#if SIMDUTF_HAS_ZVBB_INTRINSICS - if (method == simdutf_ByteFlip::ZVBB) - return __riscv_vrev8_v_u16m4(v, vl); -#endif - if (method == simdutf_ByteFlip::V) - return __riscv_vmacc_vx_u16m4(__riscv_vsrl_vx_u16m4(v, 8, vl), 0x100, v, vl); - return v; -} - -template -simdutf_really_inline static vuint16m8_t simdutf_byteflip(vuint16m8_t v, size_t vl) { -#if SIMDUTF_HAS_ZVBB_INTRINSICS - if (method == simdutf_ByteFlip::ZVBB) - return __riscv_vrev8_v_u16m8(v, vl); -#endif - if (method == simdutf_ByteFlip::V) - return __riscv_vmacc_vx_u16m8(__riscv_vsrl_vx_u16m8(v, 8, vl), 0x100, v, vl); - return v; -} - -#ifdef SIMDUTF_TARGET_ZVBB -SIMDUTF_UNTARGET_REGION -SIMDUTF_TARGET_RVV -#endif - -#endif // SIMDUTF_RVV_INTRINSICS_H -/* end file src/simdutf/rvv/intrinsics.h */ -/* begin file src/simdutf/rvv/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_RVV -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - -/* end file src/simdutf/rvv/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_RVV - -#endif // SIMDUTF_RVV_H -/* end file src/simdutf/rvv.h */ -/* begin file src/simdutf/fallback.h */ -#ifndef SIMDUTF_FALLBACK_H -#define SIMDUTF_FALLBACK_H - - -// Note that fallback.h is always imported last. - -// Default Fallback to on unless a builtin implementation has already been selected. -#ifndef SIMDUTF_IMPLEMENTATION_FALLBACK -#if SIMDUTF_CAN_ALWAYS_RUN_ARM64 || SIMDUTF_CAN_ALWAYS_RUN_ICELAKE || SIMDUTF_CAN_ALWAYS_RUN_HASWELL || SIMDUTF_CAN_ALWAYS_RUN_WESTMERE || SIMDUTF_CAN_ALWAYS_RUN_PPC64 || SIMDUTF_CAN_ALWAYS_RUN_RVV -#define SIMDUTF_IMPLEMENTATION_FALLBACK 0 -#else -#define SIMDUTF_IMPLEMENTATION_FALLBACK 1 -#endif -#endif - -#define SIMDUTF_CAN_ALWAYS_RUN_FALLBACK (SIMDUTF_IMPLEMENTATION_FALLBACK) - -#if SIMDUTF_IMPLEMENTATION_FALLBACK - -namespace simdutf { -/** - * Fallback implementation (runs on any machine). - */ -namespace fallback { -} // namespace fallback -} // namespace simdutf - -/* begin file src/simdutf/fallback/implementation.h */ -#ifndef SIMDUTF_FALLBACK_IMPLEMENTATION_H -#define SIMDUTF_FALLBACK_IMPLEMENTATION_H - - -namespace simdutf { -namespace fallback { - -namespace { -using namespace simdutf; -} - -class implementation final : public simdutf::implementation { -public: - simdutf_really_inline implementation() : simdutf::implementation( - "fallback", - "Generic fallback implementation", - 0 - ) {} - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final; - simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final; - simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final; - void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final; - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept; - simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept; - simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept; - simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept; - simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept; - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept; - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept; - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept; -}; -} // namespace fallback -} // namespace simdutf - -#endif // SIMDUTF_FALLBACK_IMPLEMENTATION_H -/* end file src/simdutf/fallback/implementation.h */ - -/* begin file src/simdutf/fallback/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "fallback" -// #define SIMDUTF_IMPLEMENTATION fallback -/* end file src/simdutf/fallback/begin.h */ - -// Declarations -/* begin file src/simdutf/fallback/bitmanipulation.h */ -#ifndef SIMDUTF_FALLBACK_BITMANIPULATION_H -#define SIMDUTF_FALLBACK_BITMANIPULATION_H - -#include - -namespace simdutf { -namespace fallback { -namespace { - -} // unnamed namespace -} // namespace fallback -} // namespace simdutf - -#endif // SIMDUTF_FALLBACK_BITMANIPULATION_H -/* end file src/simdutf/fallback/bitmanipulation.h */ - -/* begin file src/simdutf/fallback/end.h */ -/* end file src/simdutf/fallback/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_FALLBACK -#endif // SIMDUTF_FALLBACK_H -/* end file src/simdutf/fallback.h */ - -/* begin file src/scalar/utf8.h */ -#ifndef SIMDUTF_UTF8_H -#define SIMDUTF_UTF8_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8 { -#if SIMDUTF_IMPLEMENTATION_FALLBACK || SIMDUTF_IMPLEMENTATION_RVV -// only used by the fallback kernel. -// credit: based on code from Google Fuchsia (Apache Licensed) -inline simdutf_warn_unused bool validate(const char *buf, size_t len) noexcept { - const uint8_t *data = reinterpret_cast(buf); - uint64_t pos = 0; - uint32_t code_point = 0; - while (pos < len) { - // check of the next 16 bytes are ascii. - uint64_t next_pos = pos + 16; - if (next_pos <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - std::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - pos = next_pos; - continue; - } - } - unsigned char byte = data[pos]; - - while (byte < 0b10000000) { - if (++pos == len) { return true; } - byte = data[pos]; - } - - if ((byte & 0b11100000) == 0b11000000) { - next_pos = pos + 2; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return false; } - // range check - code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); - if ((code_point < 0x80) || (0x7ff < code_point)) { return false; } - } else if ((byte & 0b11110000) == 0b11100000) { - next_pos = pos + 3; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return false; } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return false; } - // range check - code_point = (byte & 0b00001111) << 12 | - (data[pos + 1] & 0b00111111) << 6 | - (data[pos + 2] & 0b00111111); - if ((code_point < 0x800) || (0xffff < code_point) || - (0xd7ff < code_point && code_point < 0xe000)) { - return false; - } - } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000 - next_pos = pos + 4; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return false; } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return false; } - if ((data[pos + 3] & 0b11000000) != 0b10000000) { return false; } - // range check - code_point = - (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | - (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); - if (code_point <= 0xffff || 0x10ffff < code_point) { return false; } - } else { - // we may have a continuation - return false; - } - pos = next_pos; - } - return true; -} -#endif - -inline simdutf_warn_unused result validate_with_errors(const char *buf, size_t len) noexcept { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - uint32_t code_point = 0; - while (pos < len) { - // check of the next 16 bytes are ascii. - size_t next_pos = pos + 16; - if (next_pos <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - std::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - pos = next_pos; - continue; - } - } - unsigned char byte = data[pos]; - - while (byte < 0b10000000) { - if (++pos == len) { return result(error_code::SUCCESS, len); } - byte = data[pos]; - } - - if ((byte & 0b11100000) == 0b11000000) { - next_pos = pos + 2; - if (next_pos > len) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); - if ((code_point < 0x80) || (0x7ff < code_point)) { return result(error_code::OVERLONG, pos); } - } else if ((byte & 0b11110000) == 0b11100000) { - next_pos = pos + 3; - if (next_pos > len) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - code_point = (byte & 0b00001111) << 12 | - (data[pos + 1] & 0b00111111) << 6 | - (data[pos + 2] & 0b00111111); - if ((code_point < 0x800) || (0xffff < code_point)) { return result(error_code::OVERLONG, pos);} - if (0xd7ff < code_point && code_point < 0xe000) { return result(error_code::SURROGATE, pos); } - } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000 - next_pos = pos + 4; - if (next_pos > len) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 3] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - code_point = - (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | - (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); - if (code_point <= 0xffff) { return result(error_code::OVERLONG, pos); } - if (0x10ffff < code_point) { return result(error_code::TOO_LARGE, pos); } - } else { - // we either have too many continuation bytes or an invalid leading byte - if ((byte & 0b11000000) == 0b10000000) { return result(error_code::TOO_LONG, pos); } - else { return result(error_code::HEADER_BITS, pos); } - } - pos = next_pos; - } - return result(error_code::SUCCESS, len); -} - -// Finds the previous leading byte starting backward from buf and validates with errors from there -// Used to pinpoint the location of an error when an invalid chunk is detected -// We assume that the stream starts with a leading byte, and to check that it is the case, we -// ask that you pass a pointer to the start of the stream (start). -inline simdutf_warn_unused result rewind_and_validate_with_errors(const char *start, const char *buf, size_t len) noexcept { - // First check that we start with a leading byte - if ((*start & 0b11000000) == 0b10000000) { - return result(error_code::TOO_LONG, 0); - } - size_t extra_len{0}; - // A leading byte cannot be further than 4 bytes away - for(int i = 0; i < 5; i++) { - unsigned char byte = *buf; - if ((byte & 0b11000000) != 0b10000000) { - break; - } else { - buf--; - extra_len++; - } - } - - result res = validate_with_errors(buf, len + extra_len); - res.count -= extra_len; - return res; -} - -inline size_t count_code_points(const char* buf, size_t len) { - const int8_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - // -65 is 0b10111111, anything larger in two-complement's should start a new code point. - if(p[i] > -65) { counter++; } - } - return counter; -} - -inline size_t utf16_length_from_utf8(const char* buf, size_t len) { - const int8_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - if(p[i] > -65) { counter++; } - if(uint8_t(p[i]) >= 240) { counter++; } - } - return counter; -} - -simdutf_warn_unused inline size_t trim_partial_utf8(const char *input, size_t length) { - if (length < 3) { - switch (length) { - case 2: - if (uint8_t(input[length-1]) >= 0xc0) { return length-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (uint8_t(input[length-2]) >= 0xe0) { return length-2; } // 3- and 4-byte characters with only 2 bytes left - return length; - case 1: - if (uint8_t(input[length-1]) >= 0xc0) { return length-1; } // 2-, 3- and 4-byte characters with only 1 byte left - return length; - case 0: - return length; - } - } - if (uint8_t(input[length-1]) >= 0xc0) { return length-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (uint8_t(input[length-2]) >= 0xe0) { return length-2; } // 3- and 4-byte characters with only 1 byte left - if (uint8_t(input[length-3]) >= 0xf0) { return length-3; } // 4-byte characters with only 3 bytes left - return length; -} - -} // utf8 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8.h */ -/* begin file src/scalar/utf16.h */ -#ifndef SIMDUTF_UTF16_H -#define SIMDUTF_UTF16_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16 { - -inline simdutf_warn_unused uint16_t swap_bytes(const uint16_t word) { - return uint16_t((word >> 8) | (word << 8)); -} - -template -inline simdutf_warn_unused bool validate(const char16_t *buf, size_t len) noexcept { - const uint16_t *data = reinterpret_cast(buf); - uint64_t pos = 0; - while (pos < len) { - uint16_t word = !match_system(big_endian) ? swap_bytes(data[pos]) : data[pos]; - if((word &0xF800) == 0xD800) { - if(pos + 1 >= len) { return false; } - uint16_t diff = uint16_t(word - 0xD800); - if(diff > 0x3FF) { return false; } - uint16_t next_word = !match_system(big_endian) ? swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if(diff2 > 0x3FF) { return false; } - pos += 2; - } else { - pos++; - } - } - return true; -} - -template -inline simdutf_warn_unused result validate_with_errors(const char16_t *buf, size_t len) noexcept { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - while (pos < len) { - uint16_t word = !match_system(big_endian) ? swap_bytes(data[pos]) : data[pos]; - if((word & 0xF800) == 0xD800) { - if(pos + 1 >= len) { return result(error_code::SURROGATE, pos); } - uint16_t diff = uint16_t(word - 0xD800); - if(diff > 0x3FF) { return result(error_code::SURROGATE, pos); } - uint16_t next_word = !match_system(big_endian) ? swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if(diff2 > 0x3FF) { return result(error_code::SURROGATE, pos); } - pos += 2; - } else { - pos++; - } - } - return result(error_code::SUCCESS, pos); -} - -template -inline size_t count_code_points(const char16_t* buf, size_t len) { - // We are not BOM aware. - const uint16_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - uint16_t word = !match_system(big_endian) ? swap_bytes(p[i]) : p[i]; - counter += ((word & 0xFC00) != 0xDC00); - } - return counter; -} - -template -inline size_t utf8_length_from_utf16(const char16_t* buf, size_t len) { - // We are not BOM aware. - const uint16_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - uint16_t word = !match_system(big_endian) ? swap_bytes(p[i]) : p[i]; - counter++; // ASCII - counter += static_cast(word > 0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes - counter += static_cast((word > 0x7FF && word <= 0xD7FF) || (word >= 0xE000)); // three-byte - } - return counter; -} - -template -inline size_t utf32_length_from_utf16(const char16_t* buf, size_t len) { - // We are not BOM aware. - const uint16_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - uint16_t word = !match_system(big_endian) ? swap_bytes(p[i]) : p[i]; - counter += ((word & 0xFC00) != 0xDC00); - } - return counter; -} - - -inline size_t latin1_length_from_utf16(size_t len) { - return len; -} - -simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* out) { - const uint16_t * input = reinterpret_cast(in); - uint16_t * output = reinterpret_cast(out); - for (size_t i = 0; i < size; i++) { - *output++ = uint16_t(input[i] >> 8 | input[i] << 8); - } -} - - -template -simdutf_warn_unused inline size_t trim_partial_utf16(const char16_t* input, size_t length) { - if (length <= 1) { - return length; - } - uint16_t last_word = uint16_t(input[length-1]); - last_word = !match_system(big_endian) ? swap_bytes(last_word) : last_word; - length -= ((last_word & 0xFC00) == 0xD800); - return length; -} - -} // utf16 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16.h */ -/* begin file src/scalar/utf32.h */ -#ifndef SIMDUTF_UTF32_H -#define SIMDUTF_UTF32_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32 { - -inline simdutf_warn_unused bool validate(const char32_t *buf, size_t len) noexcept { - const uint32_t *data = reinterpret_cast(buf); - uint64_t pos = 0; - for(;pos < len; pos++) { - uint32_t word = data[pos]; - if(word > 0x10FFFF || (word >= 0xD800 && word <= 0xDFFF)) { - return false; - } - } - return true; -} - -inline simdutf_warn_unused result validate_with_errors(const char32_t *buf, size_t len) noexcept { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - for(;pos < len; pos++) { - uint32_t word = data[pos]; - if(word > 0x10FFFF) { - return result(error_code::TOO_LARGE, pos); - } - if(word >= 0xD800 && word <= 0xDFFF) { - return result(error_code::SURROGATE, pos); - } - } - return result(error_code::SUCCESS, pos); -} - -inline size_t utf8_length_from_utf32(const char32_t* buf, size_t len) { - // We are not BOM aware. - const uint32_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - // credit: @ttsugriy for the vectorizable approach - counter++; // ASCII - counter += static_cast(p[i] > 0x7F); // two-byte - counter += static_cast(p[i] > 0x7FF); // three-byte - counter += static_cast(p[i] > 0xFFFF); // four-bytes - } - return counter; -} - -inline size_t utf16_length_from_utf32(const char32_t* buf, size_t len) { - // We are not BOM aware. - const uint32_t * p = reinterpret_cast(buf); - size_t counter{0}; - for(size_t i = 0; i < len; i++) { - counter++; // non-surrogate word - counter += static_cast(p[i] > 0xFFFF); // surrogate pair - } - return counter; -} - -inline size_t latin1_length_from_utf32(size_t len) { - // We are not BOM aware. - return len; // a utf32 codepoint will always represent 1 latin1 character -} - -inline simdutf_warn_unused uint32_t swap_bytes(const uint32_t word) { - return ((word >> 24) & 0xff) | // move byte 3 to byte 0 - ((word << 8) & 0xff0000) | // move byte 1 to byte 2 - ((word >> 8) & 0xff00) | // move byte 2 to byte 1 - ((word << 24) & 0xff000000); // byte 0 to byte 3 -} - -} // utf32 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32.h */ -/* begin file src/scalar/base64.h */ -#ifndef SIMDUTF_BASE64_H -#define SIMDUTF_BASE64_H - -#include -#include -#include -namespace simdutf { -namespace scalar { -namespace { -namespace base64 { - -// This function is not expected to be fast. Do not use in long loops. -template -bool is_ascii_white_space(char_type c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; -} - -// Returns true upon success. The destination buffer must be large enough. -// This functions assumes that the padding (=) has been removed. -template -result base64_tail_decode(char *dst, const char_type *src, size_t length, base64_options options) { - // This looks like 5 branches, but we expect the compiler to resolve this to a single branch: - const uint8_t *to_base64 = (options & base64_url) ? tables::base64::to_base64_url_value : tables::base64::to_base64_value; - const uint32_t *d0 = (options & base64_url) ? tables::base64::base64_url::d0 : tables::base64::base64_default::d0; - const uint32_t *d1 = (options & base64_url) ? tables::base64::base64_url::d1 : tables::base64::base64_default::d1; - const uint32_t *d2 = (options & base64_url) ? tables::base64::base64_url::d2 : tables::base64::base64_default::d2; - const uint32_t *d3 = (options & base64_url) ? tables::base64::base64_url::d3 : tables::base64::base64_default::d3; - - const char_type *srcend = src + length; - const char_type *srcinit = src; - const char *dstinit = dst; - - uint32_t x; - size_t idx; - uint8_t buffer[4]; - while (true) { - while (src + 4 <= srcend && - (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] | - d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) { - if(match_system(endianness::BIG)) { - x = scalar::utf32::swap_bytes(x); - } - std::memcpy(dst, &x, 3); // optimization opportunity: copy 4 bytes - dst += 3; - src += 4; - } - idx = 0; - // we need at least four characters. - while (idx < 4 && src < srcend) { - char_type c = *src; - uint8_t code = to_base64[uint8_t(c)]; - buffer[idx] = uint8_t(code); - if (code <= 63) { - idx++; - } else if (code > 64) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } else { - // We have a space or a newline. We ignore it. - } - src++; - } - if (idx != 4) { - if (idx == 2) { - uint32_t triple = - (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6); - if(match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 1); - } else { - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - } - dst += 1; - - } else if (idx == 3) { - uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) + - (uint32_t(buffer[1]) << 2 * 6) + - (uint32_t(buffer[2]) << 1 * 6); - if(match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 2); - } else { - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 2); - } - dst += 2; - } else if (idx == 1) { - return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)}; - } - return {SUCCESS, size_t(dst - dstinit)}; - } - - uint32_t triple = - (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) + - (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6); - if(match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 3); - } else { - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 3); - } - dst += 3; - } -} - -// like base64_tail_decode, but it will not write past the end of the ouput buffer. -// outlen is modified to reflect the number of bytes written. -// This functions assumes that the padding (=) has been removed. -template -result base64_tail_decode_safe(char *dst, size_t& outlen, const char_type *src, size_t length, base64_options options) { - // This looks like 5 branches, but we expect the compiler to resolve this to a single branch: - const uint8_t *to_base64 = (options & base64_url) ? tables::base64::to_base64_url_value : tables::base64::to_base64_value; - const uint32_t *d0 = (options & base64_url) ? tables::base64::base64_url::d0 : tables::base64::base64_default::d0; - const uint32_t *d1 = (options & base64_url) ? tables::base64::base64_url::d1 : tables::base64::base64_default::d1; - const uint32_t *d2 = (options & base64_url) ? tables::base64::base64_url::d2 : tables::base64::base64_default::d2; - const uint32_t *d3 = (options & base64_url) ? tables::base64::base64_url::d3 : tables::base64::base64_default::d3; - - const char_type *srcend = src + length; - const char_type *srcinit = src; - const char *dstinit = dst; - const char *dstend = dst + outlen; - - uint32_t x; - size_t idx; - uint8_t buffer[4]; - while (true) { - while (src + 4 <= srcend && - (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] | - d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) { - if(match_system(endianness::BIG)) { - x = scalar::utf32::swap_bytes(x); - } - if(dst + 3 > dstend) { - outlen = size_t(dst - dstinit); - return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit)}; - } - std::memcpy(dst, &x, 3); // optimization opportunity: copy 4 bytes - dst += 3; - src += 4; - } - idx = 0; - const char_type *srccur = src; - - // we need at least four characters. - while (idx < 4 && src < srcend) { - char_type c = *src; - uint8_t code = to_base64[uint8_t(c)]; - buffer[idx] = uint8_t(code); - if (code <= 63) { - idx++; - } else if (code > 64) { - outlen = size_t(dst - dstinit); - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } else { - // We have a space or a newline. We ignore it. - } - src++; - } - if (idx != 4) { - if (idx == 2) { - if(dst == dstend) { - outlen = size_t(dst - dstinit); - return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit)}; - } - uint32_t triple = - (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6); - if(match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 1); - } else { - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - } - dst += 1; - - } else if (idx == 3) { - if(dst + 2 >= dstend) { - outlen = size_t(dst - dstinit); - return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit)}; - } - uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) + - (uint32_t(buffer[1]) << 2 * 6) + - (uint32_t(buffer[2]) << 1 * 6); - if(match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 2); - } else { - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 2); - } - dst += 2; - } else if (idx == 1) { - outlen = size_t(dst - dstinit); - return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)}; - } - outlen = size_t(dst - dstinit); - return {SUCCESS, size_t(dst - dstinit)}; - } - if(dst + 3 >= dstend) { - outlen = size_t(dst - dstinit); - return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit)}; - } - uint32_t triple = - (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) + - (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6); - if(match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 3); - } else { - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 3); - } - dst += 3; - } -} - -// Returns the number of bytes written. The destination buffer must be large -// enough. It will add padding (=) if needed. -size_t tail_encode_base64(char *dst, const char *src, size_t srclen, base64_options options) { - // This looks like 3 branches, but we expect the compiler to resolve this to a single branch: - const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0 : tables::base64::base64_default::e0; - const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1 : tables::base64::base64_default::e1; - const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2 : tables::base64::base64_default::e2; - char *out = dst; - size_t i = 0; - uint8_t t1, t2, t3; - for (; i + 2 < srclen; i += 3) { - t1 = uint8_t(src[i]); - t2 = uint8_t(src[i + 1]); - t3 = uint8_t(src[i + 2]); - *out++ = e0[t1]; - *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; - *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; - *out++ = e2[t3]; - } - switch (srclen - i) { - case 0: - break; - case 1: - t1 = uint8_t(src[i]); - *out++ = e0[t1]; - *out++ = e1[(t1 & 0x03) << 4]; - if((options & base64_url) == 0) { - *out++ = '='; - *out++ = '='; - } - break; - default: /* case 2 */ - t1 = uint8_t(src[i]); - t2 = uint8_t(src[i + 1]); - *out++ = e0[t1]; - *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; - *out++ = e2[(t2 & 0x0F) << 2]; - if((options & base64_url) == 0) { - *out++ = '='; - } - } - return (size_t)(out - dst); -} - -template -simdutf_warn_unused size_t maximal_binary_length_from_base64(const char_type * input, size_t length) noexcept { - // We follow https://infra.spec.whatwg.org/#forgiving-base64-decode - size_t padding = 0; - if(length > 0) { - if(input[length - 1] == '=') { - padding++; - if(length > 1 && input[length - 2] == '=') { - padding++; - } - } - } - size_t actual_length = length - padding; - if(actual_length % 4 <= 1) { - return actual_length / 4 * 3; - } - // if we have a valid input, then the remainder must be 2 or 3 adding one or two extra bytes. - return actual_length / 4 * 3 + (actual_length %4) - 1; -} - -simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) noexcept { - if(options & base64_url) { - return length/3 * 4 + ((length % 3) ? (length % 3) + 1 : 0); - } - return (length + 2)/3 * 4; // We use padding to make the length a multiple of 4. -} - -} // namespace base64 -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/base64.h */ - -namespace simdutf { -bool implementation::supported_by_runtime_system() const { - uint32_t required_instruction_sets = this->required_instruction_sets(); - uint32_t supported_instruction_sets = internal::detect_supported_architectures(); - return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets); -} - -simdutf_warn_unused encoding_type implementation::autodetect_encoding(const char * input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - // UTF8 is common, it includes ASCII, and is commonly represented - // without a BOM, so if it fits, go with that. Note that it is still - // possible to get it wrong, we are only 'guessing'. If some has UTF-16 - // data without a BOM, it could pass as UTF-8. - // - // An interesting twist might be to check for UTF-16 ASCII first (every - // other byte is zero). - if(validate_utf8(input, length)) { return encoding_type::UTF8; } - // The next most common encoding that might appear without BOM is probably - // UTF-16LE, so try that next. - if((length % 2) == 0) { - // important: we need to divide by two - if(validate_utf16le(reinterpret_cast(input), length/2)) { return encoding_type::UTF16_LE; } - } - if((length % 4) == 0) { - if(validate_utf32(reinterpret_cast(input), length/4)) { return encoding_type::UTF32_LE; } - } - return encoding_type::unspecified; -} - -namespace internal { -// When there is a single implementation, we should not pay a price - // for dispatching to the best implementation. We should just use the - // one we have. This is a compile-time check. - #define SIMDUTF_SINGLE_IMPLEMENTATION (SIMDUTF_IMPLEMENTATION_ICELAKE \ - + SIMDUTF_IMPLEMENTATION_HASWELL + SIMDUTF_IMPLEMENTATION_WESTMERE \ - + SIMDUTF_IMPLEMENTATION_ARM64 + SIMDUTF_IMPLEMENTATION_PPC64 \ - + SIMDUTF_IMPLEMENTATION_FALLBACK == 1) - -// Static array of known implementations. We're hoping these get baked into the executable -// without requiring a static initializer. - - -#if SIMDUTF_IMPLEMENTATION_ICELAKE -static const icelake::implementation* get_icelake_singleton() { - static const icelake::implementation icelake_singleton{}; - return &icelake_singleton; -} -#endif -#if SIMDUTF_IMPLEMENTATION_HASWELL -static const haswell::implementation* get_haswell_singleton() { - static const haswell::implementation haswell_singleton{}; - return &haswell_singleton; -} -#endif -#if SIMDUTF_IMPLEMENTATION_WESTMERE -static const westmere::implementation* get_westmere_singleton() { - static const westmere::implementation westmere_singleton{}; - return &westmere_singleton; -} -#endif -#if SIMDUTF_IMPLEMENTATION_ARM64 -static const arm64::implementation* get_arm64_singleton() { - static const arm64::implementation arm64_singleton{}; - return &arm64_singleton; -} -#endif -#if SIMDUTF_IMPLEMENTATION_PPC64 -static const ppc64::implementation* get_ppc64_singleton() { - static const ppc64::implementation ppc64_singleton{}; - return &ppc64_singleton; -} -#endif -#if SIMDUTF_IMPLEMENTATION_RVV -static const rvv::implementation* get_rvv_singleton() { - static const rvv::implementation rvv_singleton{}; - return &rvv_singleton; -} -#endif -#if SIMDUTF_IMPLEMENTATION_FALLBACK -static const fallback::implementation* get_fallback_singleton() { - static const fallback::implementation fallback_singleton{}; - return &fallback_singleton; -} -#endif - -#if SIMDUTF_SINGLE_IMPLEMENTATION -static const implementation* get_single_implementation() { - return -#if SIMDUTF_IMPLEMENTATION_ICELAKE - get_icelake_singleton(); -#endif -#if SIMDUTF_IMPLEMENTATION_HASWELL - get_haswell_singleton(); -#endif -#if SIMDUTF_IMPLEMENTATION_WESTMERE - get_westmere_singleton(); -#endif -#if SIMDUTF_IMPLEMENTATION_ARM64 - get_arm64_singleton(); -#endif -#if SIMDUTF_IMPLEMENTATION_PPC64 - get_ppc64_singleton(); -#endif -#if SIMDUTF_IMPLEMENTATION_FALLBACK - get_fallback_singleton(); -#endif -} -#endif - -/** - * @private Detects best supported implementation on first use, and sets it - */ -class detect_best_supported_implementation_on_first_use final : public implementation { -public: - std::string name() const noexcept final { return set_best()->name(); } - std::string description() const noexcept final { return set_best()->description(); } - uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); } - - simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept override { - return set_best()->detect_encodings(input, length); - } - - simdutf_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override { - return set_best()->validate_utf8(buf, len); - } - - simdutf_warn_unused result validate_utf8_with_errors(const char * buf, size_t len) const noexcept final override { - return set_best()->validate_utf8_with_errors(buf, len); - } - - simdutf_warn_unused bool validate_ascii(const char * buf, size_t len) const noexcept final override { - return set_best()->validate_ascii(buf, len); - } - - simdutf_warn_unused result validate_ascii_with_errors(const char * buf, size_t len) const noexcept final override { - return set_best()->validate_ascii_with_errors(buf, len); - } - - simdutf_warn_unused bool validate_utf16le(const char16_t * buf, size_t len) const noexcept final override { - return set_best()->validate_utf16le(buf, len); - } - - simdutf_warn_unused bool validate_utf16be(const char16_t * buf, size_t len) const noexcept final override { - return set_best()->validate_utf16be(buf, len); - } - - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t * buf, size_t len) const noexcept final override { - return set_best()->validate_utf16le_with_errors(buf, len); - } - - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t * buf, size_t len) const noexcept final override { - return set_best()->validate_utf16be_with_errors(buf, len); - } - - simdutf_warn_unused bool validate_utf32(const char32_t * buf, size_t len) const noexcept final override { - return set_best()->validate_utf32(buf, len); - } - - simdutf_warn_unused result validate_utf32_with_errors(const char32_t * buf, size_t len) const noexcept final override { - return set_best()->validate_utf32_with_errors(buf, len); - } - - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_latin1_to_utf8(buf, len,utf8_output); - } - - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_latin1_to_utf16le(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_latin1_to_utf16be(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t * latin1_output) const noexcept final override { - return set_best()->convert_latin1_to_utf32(buf, len,latin1_output); - } - - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf8_to_latin1(buf, len,latin1_output); - } - - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf8_to_latin1_with_errors(buf, len, latin1_output); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_valid_utf8_to_latin1(buf, len,latin1_output); - } - - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf8_to_utf16le(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf8_to_utf16be(buf, len, utf16_output); - } - - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf8_to_utf16le_with_errors(buf, len, utf16_output); - } - - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf8_to_utf16be_with_errors(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_valid_utf8_to_utf16le(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_valid_utf8_to_utf16be(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_utf8_to_utf32(buf, len, utf32_output); - } - - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_utf8_to_utf32_with_errors(buf, len, utf32_output); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_valid_utf8_to_utf32(buf, len, utf32_output); - } - - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf16le_to_latin1(buf, len, latin1_output); - } - - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf16be_to_latin1(buf, len, latin1_output); - } - - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf16le_to_latin1_with_errors(buf, len, latin1_output); - } - - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf16be_to_latin1_with_errors(buf, len, latin1_output); - } - - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_valid_utf16le_to_latin1(buf, len, latin1_output); - } - - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_valid_utf16be_to_latin1(buf, len, latin1_output); - } - - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_utf16le_to_utf8(buf, len, utf8_output); - } - - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_utf16be_to_utf8(buf, len, utf8_output); - } - - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_utf16le_to_utf8_with_errors(buf, len, utf8_output); - } - - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_utf16be_to_utf8_with_errors(buf, len, utf8_output); - } - - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_valid_utf16le_to_utf8(buf, len, utf8_output); - } - - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_valid_utf16be_to_utf8(buf, len, utf8_output); - } - - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf32_to_latin1(buf, len,latin1_output); - } - - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf32_to_latin1_with_errors(buf, len,latin1_output); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final override { - return set_best()->convert_utf32_to_latin1(buf, len,latin1_output); - } - - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_utf32_to_utf8(buf, len, utf8_output); - } - - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_utf32_to_utf8_with_errors(buf, len, utf8_output); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_output) const noexcept final override { - return set_best()->convert_valid_utf32_to_utf8(buf, len, utf8_output); - } - - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf32_to_utf16le(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf32_to_utf16be(buf, len, utf16_output); - } - - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf32_to_utf16le_with_errors(buf, len, utf16_output); - } - - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_utf32_to_utf16be_with_errors(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_valid_utf32_to_utf16le(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override { - return set_best()->convert_valid_utf32_to_utf16be(buf, len, utf16_output); - } - - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_utf16le_to_utf32(buf, len, utf32_output); - } - - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_utf16be_to_utf32(buf, len, utf32_output); - } - - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_utf16le_to_utf32_with_errors(buf, len, utf32_output); - } - - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_utf16be_to_utf32_with_errors(buf, len, utf32_output); - } - - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_valid_utf16le_to_utf32(buf, len, utf32_output); - } - - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override { - return set_best()->convert_valid_utf16be_to_utf32(buf, len, utf32_output); - } - - void change_endianness_utf16(const char16_t * buf, size_t len, char16_t * output) const noexcept final override { - set_best()->change_endianness_utf16(buf, len, output); - } - - simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t len) const noexcept final override { - return set_best()->count_utf16le(buf, len); - } - - simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t len) const noexcept final override { - return set_best()->count_utf16be(buf, len); - } - - simdutf_warn_unused size_t count_utf8(const char * buf, size_t len) const noexcept final override { - return set_best()->count_utf8(buf, len); - } - - simdutf_warn_unused size_t latin1_length_from_utf8(const char * buf, size_t len) const noexcept override { - return set_best()->latin1_length_from_utf8(buf, len); - } - - simdutf_warn_unused size_t latin1_length_from_utf16(size_t len) const noexcept override { - return set_best()->latin1_length_from_utf16(len); - } - - simdutf_warn_unused size_t latin1_length_from_utf32(size_t len) const noexcept override { - return set_best()->latin1_length_from_utf32(len); - } - - simdutf_warn_unused size_t utf8_length_from_latin1(const char * buf, size_t len) const noexcept override { - return set_best()->utf8_length_from_latin1(buf, len); - } - - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * buf, size_t len) const noexcept override { - return set_best()->utf8_length_from_utf16le(buf, len); - } - - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * buf, size_t len) const noexcept override { - return set_best()->utf8_length_from_utf16be(buf, len); - } - - simdutf_warn_unused size_t utf16_length_from_latin1(size_t len) const noexcept override { - return set_best()->utf16_length_from_latin1(len); - } - - simdutf_warn_unused size_t utf32_length_from_latin1(size_t len) const noexcept override { - return set_best()->utf32_length_from_latin1(len); - } - - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * buf, size_t len) const noexcept override { - return set_best()->utf32_length_from_utf16le(buf, len); - } - - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * buf, size_t len) const noexcept override { - return set_best()->utf32_length_from_utf16be(buf, len); - } - - simdutf_warn_unused size_t utf16_length_from_utf8(const char * buf, size_t len) const noexcept override { - return set_best()->utf16_length_from_utf8(buf, len); - } - - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * buf, size_t len) const noexcept override { - return set_best()->utf8_length_from_utf32(buf, len); - } - - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * buf, size_t len) const noexcept override { - return set_best()->utf16_length_from_utf32(buf, len); - } - - simdutf_warn_unused size_t utf32_length_from_utf8(const char * buf, size_t len) const noexcept override { - return set_best()->utf32_length_from_utf8(buf, len); - } - - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept override { - return set_best()->maximal_binary_length_from_base64(input, length); - } - - simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept override { - return set_best()->base64_to_binary(input, length, output, options); - } - - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept override { - return set_best()->maximal_binary_length_from_base64(input, length); - } - - simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept override { - return set_best()->base64_to_binary(input, length, output, options); - } - - simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept override { - return set_best()->base64_length_from_binary(length, options); - } - - size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept override { - return set_best()->binary_to_base64(input, length, output, options); - } - - simdutf_really_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {} - -private: - const implementation *set_best() const noexcept; -}; - -static_assert(std::is_trivially_destructible::value, "detect_best_supported_implementation_on_first_use should be trivially destructible"); - -static const std::initializer_list& get_available_implementation_pointers() { - static const std::initializer_list available_implementation_pointers { -#if SIMDUTF_IMPLEMENTATION_ICELAKE - get_icelake_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_HASWELL - get_haswell_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_WESTMERE - get_westmere_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_ARM64 - get_arm64_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_PPC64 - get_ppc64_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_RVV - get_rvv_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_FALLBACK - get_fallback_singleton(), -#endif - }; // available_implementation_pointers - return available_implementation_pointers; -} - -// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support -class unsupported_implementation final : public implementation { -public: - simdutf_warn_unused int detect_encodings(const char *, size_t) const noexcept override { - return encoding_type::unspecified; - } - - simdutf_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override { - return false; // Just refuse to validate. Given that we have a fallback implementation - // it seems unlikely that unsupported_implementation will ever be used. If it is used, - // then it will flag all strings as invalid. The alternative is to return an error_code - // from which the user has to figure out whether the string is valid UTF-8... which seems - // like a lot of work just to handle the very unlikely case that we have an unsupported - // implementation. And, when it does happen (that we have an unsupported implementation), - // what are the chances that the programmer has a fallback? Given that *we* provide the - // fallback, it implies that the programmer would need a fallback for our fallback. - } - - simdutf_warn_unused result validate_utf8_with_errors(const char *, size_t) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused bool validate_ascii(const char *, size_t) const noexcept final override { - return false; - } - - simdutf_warn_unused result validate_ascii_with_errors(const char *, size_t) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused bool validate_utf16le(const char16_t*, size_t) const noexcept final override { - return false; - } - - simdutf_warn_unused bool validate_utf16be(const char16_t*, size_t) const noexcept final override { - return false; - } - - simdutf_warn_unused result validate_utf16le_with_errors(const char16_t*, size_t) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused result validate_utf16be_with_errors(const char16_t*, size_t) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused bool validate_utf32(const char32_t*, size_t) const noexcept final override { - return false; - } - - simdutf_warn_unused result validate_utf32_with_errors(const char32_t*, size_t) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_latin1_to_utf8(const char*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_latin1_to_utf32(const char*, size_t, char32_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf8_to_latin1(const char*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char*, size_t, char*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf8_to_utf16le(const char*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf8_to_utf16be(const char*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char*, size_t, char16_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char*, size_t, char16_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf8_to_utf32(const char*, size_t, char32_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char*, size_t, char32_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char*, size_t, char32_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t*, size_t, char*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t*, size_t, char*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t*, size_t, char*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t*, size_t, char*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t *, size_t, char* ) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t *, size_t, char* ) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t *, size_t, char* ) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t*, size_t, char*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t*, size_t, char*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t*, size_t, char16_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t*, size_t, char16_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t*, size_t, char16_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t*, size_t, char32_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t*, size_t, char32_t*) const noexcept final override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override { - return 0; - } - - void change_endianness_utf16(const char16_t *, size_t, char16_t *) const noexcept final override { - - } - - simdutf_warn_unused size_t count_utf16le(const char16_t *, size_t) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t count_utf16be(const char16_t *, size_t) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t count_utf8(const char *, size_t) const noexcept final override { - return 0; - } - - simdutf_warn_unused size_t latin1_length_from_utf8(const char *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t latin1_length_from_utf16(size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t latin1_length_from_utf32(size_t) const noexcept override { - return 0; - } - simdutf_warn_unused size_t utf8_length_from_latin1(const char *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf32_length_from_latin1(size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf16_length_from_utf8(const char *, size_t) const noexcept override { - return 0; - } - simdutf_warn_unused size_t utf16_length_from_latin1(size_t) const noexcept override { - return 0; - } - simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t utf32_length_from_utf8(const char *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused result base64_to_binary(const char *, size_t, char*, base64_options) const noexcept override { - return result(error_code::OTHER, 0); - } - - simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t *, size_t) const noexcept override { - return 0; - } - - simdutf_warn_unused result base64_to_binary(const char16_t *, size_t, char*, base64_options) const noexcept override { - return result(error_code::OTHER, 0); - } - - - simdutf_warn_unused size_t base64_length_from_binary(size_t, base64_options) const noexcept override { - return 0; - } - - size_t binary_to_base64(const char *, size_t, char*, base64_options) const noexcept override { - return 0; - } - - unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {} -}; - -const unsupported_implementation* get_unsupported_singleton() { - static const unsupported_implementation unsupported_singleton{}; - return &unsupported_singleton; -} -static_assert(std::is_trivially_destructible::value, "unsupported_singleton should be trivially destructible"); - -size_t available_implementation_list::size() const noexcept { - return internal::get_available_implementation_pointers().size(); -} -const implementation * const *available_implementation_list::begin() const noexcept { - return internal::get_available_implementation_pointers().begin(); -} -const implementation * const *available_implementation_list::end() const noexcept { - return internal::get_available_implementation_pointers().end(); -} -const implementation *available_implementation_list::detect_best_supported() const noexcept { - // They are prelisted in priority order, so we just go down the list - uint32_t supported_instruction_sets = internal::detect_supported_architectures(); - for (const implementation *impl : internal::get_available_implementation_pointers()) { - uint32_t required_instruction_sets = impl->required_instruction_sets(); - if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; } - } - return get_unsupported_singleton(); // this should never happen? -} - -const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept { - SIMDUTF_PUSH_DISABLE_WARNINGS - SIMDUTF_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe - char *force_implementation_name = getenv("SIMDUTF_FORCE_IMPLEMENTATION"); - SIMDUTF_POP_DISABLE_WARNINGS - - if (force_implementation_name) { - auto force_implementation = get_available_implementations()[force_implementation_name]; - if (force_implementation) { - return get_active_implementation() = force_implementation; - } else { - // Note: abort() and stderr usage within the library is forbidden. - return get_active_implementation() = get_unsupported_singleton(); - } - } - return get_active_implementation() = get_available_implementations().detect_best_supported(); -} - -} // namespace internal - - - -/** - * The list of available implementations compiled into simdutf. - */ -SIMDUTF_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { - static const internal::available_implementation_list available_implementations{}; - return available_implementations; -} - -/** - * The active implementation. - */ -SIMDUTF_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { -#if SIMDUTF_SINGLE_IMPLEMENTATION - // skip runtime detection - static internal::atomic_ptr active_implementation{internal::get_single_implementation()}; - return active_implementation; -#else - static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; - static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; - return active_implementation; -#endif -} - - -#if SIMDUTF_SINGLE_IMPLEMENTATION -const implementation * get_default_implementation() { - return internal::get_single_implementation(); -} -#else -internal::atomic_ptr& get_default_implementation() { - return get_active_implementation(); -} -#endif -#define SIMDUTF_GET_CURRENT_IMPLEMENTION - -simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { - return get_default_implementation()->validate_utf8(buf, len); -} -simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) noexcept { - return get_default_implementation()->validate_utf8_with_errors(buf, len); -} -simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept { - return get_default_implementation()->validate_ascii(buf, len); -} -simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) noexcept { - return get_default_implementation()->validate_ascii_with_errors(buf, len); -} -simdutf_warn_unused size_t convert_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_output) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf8_to_utf16be(input, length, utf16_output); - #else - return convert_utf8_to_utf16le(input, length, utf16_output); - #endif -} -simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) noexcept { - return get_default_implementation()->convert_latin1_to_utf8(buf, len,utf8_output); -} -simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) noexcept { - return get_default_implementation()->convert_latin1_to_utf16le(buf, len, utf16_output); -} -simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) noexcept{ - return get_default_implementation()->convert_latin1_to_utf16be(buf, len, utf16_output); -} -simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t * latin1_output) noexcept { - return get_default_implementation()->convert_latin1_to_utf32(buf, len,latin1_output); -} -simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) noexcept { - return get_default_implementation()->convert_utf8_to_latin1(buf, len,latin1_output); -} -simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) noexcept { - return get_default_implementation()->convert_utf8_to_latin1_with_errors(buf, len, latin1_output); -} -simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) noexcept { - return get_default_implementation()->convert_valid_utf8_to_latin1(buf, len,latin1_output); -} -simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_output) noexcept { - return get_default_implementation()->convert_utf8_to_utf16le(input, length, utf16_output); -} -simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_output) noexcept { - return get_default_implementation()->convert_utf8_to_utf16be(input, length, utf16_output); -} -simdutf_warn_unused result convert_utf8_to_utf16_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf8_to_utf16be_with_errors(input, length, utf16_output); - #else - return convert_utf8_to_utf16le_with_errors(input, length, utf16_output); - #endif -} -simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept { - return get_default_implementation()->convert_utf8_to_utf16le_with_errors(input, length, utf16_output); -} -simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept { - return get_default_implementation()->convert_utf8_to_utf16be_with_errors(input, length, utf16_output); -} -simdutf_warn_unused size_t convert_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_output) noexcept { - return get_default_implementation()->convert_utf8_to_utf32(input, length, utf32_output); -} -simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * input, size_t length, char32_t* utf32_output) noexcept { - return get_default_implementation()->convert_utf8_to_utf32_with_errors(input, length, utf32_output); -} -simdutf_warn_unused bool validate_utf16(const char16_t * buf, size_t len) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return validate_utf16be(buf, len); - #else - return validate_utf16le(buf, len); - #endif -} -simdutf_warn_unused bool validate_utf16le(const char16_t * buf, size_t len) noexcept { - return get_default_implementation()->validate_utf16le(buf, len); -} -simdutf_warn_unused bool validate_utf16be(const char16_t * buf, size_t len) noexcept { - return get_default_implementation()->validate_utf16be(buf, len); -} -simdutf_warn_unused result validate_utf16_with_errors(const char16_t * buf, size_t len) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return validate_utf16be_with_errors(buf, len); - #else - return validate_utf16le_with_errors(buf, len); - #endif -} -simdutf_warn_unused result validate_utf16le_with_errors(const char16_t * buf, size_t len) noexcept { - return get_default_implementation()->validate_utf16le_with_errors(buf, len); -} -simdutf_warn_unused result validate_utf16be_with_errors(const char16_t * buf, size_t len) noexcept { - return get_default_implementation()->validate_utf16be_with_errors(buf, len); -} -simdutf_warn_unused bool validate_utf32(const char32_t * buf, size_t len) noexcept { - return get_default_implementation()->validate_utf32(buf, len); -} -simdutf_warn_unused result validate_utf32_with_errors(const char32_t * buf, size_t len) noexcept { - return get_default_implementation()->validate_utf32_with_errors(buf, len); -} -simdutf_warn_unused size_t convert_valid_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_valid_utf8_to_utf16be(input, length, utf16_buffer); - #else - return convert_valid_utf8_to_utf16le(input, length, utf16_buffer); - #endif -} -simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_valid_utf8_to_utf16le(input, length, utf16_buffer); -} -simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_valid_utf8_to_utf16be(input, length, utf16_buffer); -} -simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_valid_utf8_to_utf32(input, length, utf32_buffer); -} -simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf16be_to_utf8(buf, len, utf8_buffer); - #else - return convert_utf16le_to_utf8(buf, len, utf8_buffer); - #endif -} -simdutf_warn_unused size_t convert_utf16_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf16be_to_latin1(buf, len, latin1_buffer); - #else - return convert_utf16le_to_latin1(buf, len, latin1_buffer); - #endif -} -simdutf_warn_unused size_t convert_latin1_to_utf16(const char * buf, size_t len, char16_t* utf16_output) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_latin1_to_utf16be(buf, len, utf16_output); - #else - return convert_latin1_to_utf16le(buf, len, utf16_output); - #endif -} -simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - return get_default_implementation()->convert_utf16be_to_latin1(buf, len, latin1_buffer); -} -simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - return get_default_implementation()->convert_utf16le_to_latin1(buf, len, latin1_buffer); -} -simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - return get_default_implementation()->convert_valid_utf16be_to_latin1(buf, len, latin1_buffer); -} -simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - return get_default_implementation()->convert_valid_utf16le_to_latin1(buf, len, latin1_buffer); -} -simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - return get_default_implementation()->convert_utf16le_to_latin1_with_errors(buf, len, latin1_buffer); -} -simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - return get_default_implementation()->convert_utf16be_to_latin1_with_errors(buf, len, latin1_buffer); -} -simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_utf16le_to_utf8(buf, len, utf8_buffer); -} -simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_utf16be_to_utf8(buf, len, utf8_buffer); -} -simdutf_warn_unused result convert_utf16_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf16be_to_utf8_with_errors(buf, len, utf8_buffer); - #else - return convert_utf16le_to_utf8_with_errors(buf, len, utf8_buffer); - #endif -} -simdutf_warn_unused result convert_utf16_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf16be_to_latin1_with_errors(buf, len, latin1_buffer); - #else - return convert_utf16le_to_latin1_with_errors(buf, len, latin1_buffer); - #endif -} -simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_utf16le_to_utf8_with_errors(buf, len, utf8_buffer); -} -simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_utf16be_to_utf8_with_errors(buf, len, utf8_buffer); -} -simdutf_warn_unused size_t convert_valid_utf16_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_valid_utf16be_to_utf8(buf, len, utf8_buffer); - #else - return convert_valid_utf16le_to_utf8(buf, len, utf8_buffer); - #endif -} -simdutf_warn_unused size_t convert_valid_utf16_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_valid_utf16be_to_latin1(buf, len, latin1_buffer); - #else - return convert_valid_utf16le_to_latin1(buf, len, latin1_buffer); - #endif -} -simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_valid_utf16le_to_utf8(buf, len, utf8_buffer); -} -simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_valid_utf16be_to_utf8(buf, len, utf8_buffer); -} -simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_utf32_to_utf8(buf, len, utf8_buffer); -} -simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_utf32_to_utf8_with_errors(buf, len, utf8_buffer); -} -simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) noexcept { - return get_default_implementation()->convert_valid_utf32_to_utf8(buf, len, utf8_buffer); -} -simdutf_warn_unused size_t convert_utf32_to_utf16(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf32_to_utf16be(buf, len, utf16_buffer); - #else - return convert_utf32_to_utf16le(buf, len, utf16_buffer); - #endif -} -simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_output) noexcept { - return get_default_implementation()->convert_utf32_to_latin1(input, length, latin1_output); -} -simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_utf32_to_utf16le(buf, len, utf16_buffer); -} -simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_utf32_to_utf16be(buf, len, utf16_buffer); -} -simdutf_warn_unused result convert_utf32_to_utf16_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf32_to_utf16be_with_errors(buf, len, utf16_buffer); - #else - return convert_utf32_to_utf16le_with_errors(buf, len, utf16_buffer); - #endif -} -simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_utf32_to_utf16le_with_errors(buf, len, utf16_buffer); -} -simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_utf32_to_utf16be_with_errors(buf, len, utf16_buffer); -} -simdutf_warn_unused size_t convert_valid_utf32_to_utf16(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_valid_utf32_to_utf16be(buf, len, utf16_buffer); - #else - return convert_valid_utf32_to_utf16le(buf, len, utf16_buffer); - #endif -} -simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_valid_utf32_to_utf16le(buf, len, utf16_buffer); -} -simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept { - return get_default_implementation()->convert_valid_utf32_to_utf16be(buf, len, utf16_buffer); -} -simdutf_warn_unused size_t convert_utf16_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf16be_to_utf32(buf, len, utf32_buffer); - #else - return convert_utf16le_to_utf32(buf, len, utf32_buffer); - #endif -} -simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_utf16le_to_utf32(buf, len, utf32_buffer); -} -simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_utf16be_to_utf32(buf, len, utf32_buffer); -} -simdutf_warn_unused result convert_utf16_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_utf16be_to_utf32_with_errors(buf, len, utf32_buffer); - #else - return convert_utf16le_to_utf32_with_errors(buf, len, utf32_buffer); - #endif -} -simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_utf16le_to_utf32_with_errors(buf, len, utf32_buffer); -} -simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_utf16be_to_utf32_with_errors(buf, len, utf32_buffer); -} -simdutf_warn_unused size_t convert_valid_utf16_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return convert_valid_utf16be_to_utf32(buf, len, utf32_buffer); - #else - return convert_valid_utf16le_to_utf32(buf, len, utf32_buffer); - #endif -} -simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_valid_utf16le_to_utf32(buf, len, utf32_buffer); -} -simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept { - return get_default_implementation()->convert_valid_utf16be_to_utf32(buf, len, utf32_buffer); -} -void change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) noexcept { - get_default_implementation()->change_endianness_utf16(input, length, output); -} -simdutf_warn_unused size_t count_utf16(const char16_t * input, size_t length) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return count_utf16be(input, length); - #else - return count_utf16le(input, length); - #endif -} -simdutf_warn_unused size_t count_utf16le(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->count_utf16le(input, length); -} -simdutf_warn_unused size_t count_utf16be(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->count_utf16be(input, length); -} -simdutf_warn_unused size_t count_utf8(const char * input, size_t length) noexcept { - return get_default_implementation()->count_utf8(input, length); -} -simdutf_warn_unused size_t latin1_length_from_utf8(const char * buf, size_t len) noexcept { - return get_default_implementation()->latin1_length_from_utf8(buf, len); -} -simdutf_warn_unused size_t latin1_length_from_utf16(size_t len) noexcept { - return get_default_implementation()->latin1_length_from_utf16(len); -} -simdutf_warn_unused size_t latin1_length_from_utf32(size_t len) noexcept { - return get_default_implementation()->latin1_length_from_utf32(len); -} -simdutf_warn_unused size_t utf8_length_from_latin1(const char * buf, size_t len) noexcept { - return get_default_implementation()->utf8_length_from_latin1(buf, len); -} -simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t * input, size_t length) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return utf8_length_from_utf16be(input, length); - #else - return utf8_length_from_utf16le(input, length); - #endif -} -simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->utf8_length_from_utf16le(input, length); -} -simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->utf8_length_from_utf16be(input, length); -} -simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t * input, size_t length) noexcept { - #if SIMDUTF_IS_BIG_ENDIAN - return utf32_length_from_utf16be(input, length); - #else - return utf32_length_from_utf16le(input, length); - #endif -} -simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->utf32_length_from_utf16le(input, length); -} -simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->utf32_length_from_utf16be(input, length); -} -simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) noexcept { - return get_default_implementation()->utf16_length_from_utf8(input, length); -} -simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) noexcept { - return get_default_implementation()->utf16_length_from_latin1(length); -} -simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) noexcept { - return get_default_implementation()->utf8_length_from_utf32(input, length); -} -simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) noexcept { - return get_default_implementation()->utf16_length_from_utf32(input, length); -} -simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) noexcept { - return get_default_implementation()->utf32_length_from_utf8(input, length); -} - -simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) noexcept { - return get_default_implementation()->maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) noexcept { - return get_default_implementation()->base64_to_binary(input, length, output, options); -} - -simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) noexcept { - return get_default_implementation()->maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) noexcept { - return get_default_implementation()->base64_to_binary(input, length, output, options); -} - -template -simdutf_warn_unused result base64_to_binary_safe_impl(const chartype * input, size_t length, char* output, size_t& outlen, base64_options options) noexcept { - static_assert(std::is_same::value || std::is_same::value, "Only char and char16_t are supported."); - // The implementation could be nicer, but we expect that most times, the user - // will provide us with a buffer that is large enough. - size_t max_length = maximal_binary_length_from_base64(input, length); - if(outlen >= max_length) { - // fast path - result r = base64_to_binary(input, length, output, options); - if(r.error != error_code::INVALID_BASE64_CHARACTER) { outlen = r.count; r.count = length; } - return r; - } - // The output buffer is maybe too small. We will decode a truncated version of the input. - size_t outlen3 = outlen / 3 * 3; // round down to multiple of 3 - size_t safe_input = base64_length_from_binary(outlen3, options); - result r = base64_to_binary(input, safe_input, output, options); - if(r.error == error_code::INVALID_BASE64_CHARACTER) { return r; } - size_t offset = (r.error == error_code::BASE64_INPUT_REMAINDER) ? 1 : - ((r.count % 3) == 0 ? 0 : (r.count % 3) + 1); - size_t output_index = r.count - (r.count % 3); - size_t input_index = safe_input; - // offset is a value that is no larger than 3. We backtrack - // by up to offset characters + an undetermined number of - // white space characters. It is expected that the next loop - // runs at most 3 times + the number of white space characters - // in between them, so we are not worried about performance. - while(offset > 0 && input_index > 0) { - chartype c = input[--input_index]; - if(scalar::base64::is_ascii_white_space(c)){ - // skipping - } else { - offset--; - } - } - size_t remaining_out = outlen - output_index; - const chartype * tail_input = input + input_index; - size_t tail_length = length - input_index; - while(tail_length > 0 && scalar::base64::is_ascii_white_space(tail_input[tail_length - 1])) { - tail_length--; - } - size_t padding_characts = 0; - if(tail_length > 0 && tail_input[tail_length - 1] == '=') { - tail_length--; - padding_characts++; - while(tail_length > 0 && scalar::base64::is_ascii_white_space(tail_input[tail_length - 1])) { - tail_length--; - } - if(tail_length > 0 && tail_input[tail_length - 1] == '=') { - tail_length--; - padding_characts++; - } - } - r = scalar::base64::base64_tail_decode_safe(output + output_index, remaining_out, tail_input, tail_length, options); - outlen = output_index + remaining_out; - if(r.error == error_code::SUCCESS && padding_characts > 0) { - // additional checks - if((outlen % 3 == 0) || ((outlen % 3) + 1 + padding_characts != 4)) { - r.error = error_code::INVALID_BASE64_CHARACTER; - } - } - r.count += input_index; - return r; -} - - -simdutf_warn_unused result base64_to_binary_safe(const char * input, size_t length, char* output, size_t& outlen, base64_options options) noexcept { - return base64_to_binary_safe_impl(input, length, output, outlen, options); -} -simdutf_warn_unused result base64_to_binary_safe(const char16_t * input, size_t length, char* output, size_t& outlen, base64_options options) noexcept { - return base64_to_binary_safe_impl(input, length, output, outlen, options); -} - -simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) noexcept { - return get_default_implementation()->base64_length_from_binary(length, options); -} - -size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) noexcept { - return get_default_implementation()->binary_to_base64(input, length, output, options); -} - -simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const char * buf, size_t length) noexcept { - return get_default_implementation()->autodetect_encoding(buf, length); -} -simdutf_warn_unused int detect_encodings(const char * buf, size_t length) noexcept { - return get_default_implementation()->detect_encodings(buf, length); -} -const implementation * builtin_implementation() { - static const implementation * builtin_impl = get_available_implementations()[SIMDUTF_STRINGIFY(SIMDUTF_BUILTIN_IMPLEMENTATION)]; - return builtin_impl; -} - -simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length) { - return scalar::utf8::trim_partial_utf8(input, length); -} - -simdutf_warn_unused size_t trim_partial_utf16be(const char16_t* input, size_t length) { - return scalar::utf16::trim_partial_utf16(input, length); -} - -simdutf_warn_unused size_t trim_partial_utf16le(const char16_t* input, size_t length) { - return scalar::utf16::trim_partial_utf16(input, length); -} - -simdutf_warn_unused size_t trim_partial_utf16(const char16_t* input, size_t length) { - #if SIMDUTF_IS_BIG_ENDIAN - return trim_partial_utf16be(input, length); - #else - return trim_partial_utf16le(input, length); - #endif -} - -} // namespace simdutf - -/* end file src/implementation.cpp */ -/* begin file src/encoding_types.cpp */ - -namespace simdutf { -bool match_system(endianness e) { -#if SIMDUTF_IS_BIG_ENDIAN - return e == endianness::BIG; -#else - return e == endianness::LITTLE; -#endif -} - -std::string to_string(encoding_type bom) { - switch (bom) { - case UTF16_LE: return "UTF16 little-endian"; - case UTF16_BE: return "UTF16 big-endian"; - case UTF32_LE: return "UTF32 little-endian"; - case UTF32_BE: return "UTF32 big-endian"; - case UTF8: return "UTF8"; - case unspecified: return "unknown"; - default: return "error"; - } -} - -namespace BOM { -// Note that BOM for UTF8 is discouraged. -encoding_type check_bom(const uint8_t* byte, size_t length) { - if (length >= 2 && byte[0] == 0xff and byte[1] == 0xfe) { - if (length >= 4 && byte[2] == 0x00 and byte[3] == 0x0) { - return encoding_type::UTF32_LE; - } else { - return encoding_type::UTF16_LE; - } - } else if (length >= 2 && byte[0] == 0xfe and byte[1] == 0xff) { - return encoding_type::UTF16_BE; - } else if (length >= 4 && byte[0] == 0x00 and byte[1] == 0x00 and byte[2] == 0xfe and byte[3] == 0xff) { - return encoding_type::UTF32_BE; - } else if (length >= 4 && byte[0] == 0xef and byte[1] == 0xbb and byte[2] == 0xbf) { - return encoding_type::UTF8; - } - return encoding_type::unspecified; - } - -encoding_type check_bom(const char* byte, size_t length) { - return check_bom(reinterpret_cast(byte), length); - } - - size_t bom_byte_size(encoding_type bom) { - switch (bom) { - case UTF16_LE: return 2; - case UTF16_BE: return 2; - case UTF32_LE: return 4; - case UTF32_BE: return 4; - case UTF8: return 3; - case unspecified: return 0; - default: return 0; - } -} - -} -} -/* end file src/encoding_types.cpp */ -/* begin file src/error.cpp */ -namespace simdutf { -// deliberately empty -} -/* end file src/error.cpp */ -// The large tables should be included once and they -// should not depend on a kernel. -/* begin file src/tables/utf8_to_utf16_tables.h */ -#ifndef SIMDUTF_UTF8_TO_UTF16_TABLES_H -#define SIMDUTF_UTF8_TO_UTF16_TABLES_H -#include - -namespace simdutf { -namespace { -namespace tables { -namespace utf8_to_utf16 { -/** - * utf8bigindex uses about 8 kB - * shufutf8 uses about 3344 B - * - * So we use a bit over 11 kB. It would be - * easy to save about 4 kB by only - * storing the index in utf8bigindex, and - * deriving the consumed bytes otherwise. - * However, this may come at a significant (10% to 20%) - * performance penalty. - */ - -const uint8_t shufutf8[209][16] = -{ {0, 255, 1, 255, 2, 255, 3, 255, 4, 255, 5, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 3, 255, 4, 255, 6, 5, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 3, 255, 5, 4, 6, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 3, 255, 5, 4, 7, 6, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 4, 3, 5, 255, 6, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 4, 3, 5, 255, 7, 6, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 4, 3, 6, 5, 7, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 2, 255, 4, 3, 6, 5, 8, 7, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 4, 255, 5, 255, 6, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 4, 255, 5, 255, 7, 6, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 4, 255, 6, 5, 7, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 4, 255, 6, 5, 8, 7, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 5, 4, 6, 255, 7, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 5, 4, 6, 255, 8, 7, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 5, 4, 7, 6, 8, 255, 0, 0, 0, 0}, - {0, 255, 1, 255, 3, 2, 5, 4, 7, 6, 9, 8, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 4, 255, 5, 255, 6, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 4, 255, 5, 255, 7, 6, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 4, 255, 6, 5, 7, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 4, 255, 6, 5, 8, 7, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 5, 4, 6, 255, 7, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 5, 4, 6, 255, 8, 7, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 5, 4, 7, 6, 8, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 3, 255, 5, 4, 7, 6, 9, 8, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 5, 255, 6, 255, 7, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 5, 255, 6, 255, 8, 7, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 5, 255, 7, 6, 8, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 5, 255, 7, 6, 9, 8, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 6, 5, 7, 255, 8, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 6, 5, 7, 255, 9, 8, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 6, 5, 8, 7, 9, 255, 0, 0, 0, 0}, - {0, 255, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 4, 255, 5, 255, 6, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 4, 255, 5, 255, 7, 6, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 4, 255, 6, 5, 7, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 4, 255, 6, 5, 8, 7, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 5, 4, 6, 255, 7, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 5, 4, 6, 255, 8, 7, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 5, 4, 7, 6, 8, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 3, 255, 5, 4, 7, 6, 9, 8, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 5, 255, 6, 255, 7, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 5, 255, 6, 255, 8, 7, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 5, 255, 7, 6, 8, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 5, 255, 7, 6, 9, 8, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 6, 5, 7, 255, 8, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 6, 5, 7, 255, 9, 8, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 6, 5, 8, 7, 9, 255, 0, 0, 0, 0}, - {1, 0, 2, 255, 4, 3, 6, 5, 8, 7, 10, 9, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 5, 255, 6, 255, 7, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 5, 255, 6, 255, 8, 7, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 5, 255, 7, 6, 8, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 5, 255, 7, 6, 9, 8, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 6, 5, 7, 255, 8, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 6, 5, 7, 255, 9, 8, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 6, 5, 8, 7, 9, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 4, 255, 6, 5, 8, 7, 10, 9, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 6, 255, 7, 255, 8, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 6, 255, 7, 255, 9, 8, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 6, 255, 8, 7, 9, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 6, 255, 8, 7, 10, 9, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 7, 6, 8, 255, 9, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 7, 6, 8, 255, 10, 9, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 10, 255, 0, 0, 0, 0}, - {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 0, 0, 0, 0}, - {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 4, 255, 255, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 5, 4, 255, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 6, 5, 4, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 5, 255, 255, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 6, 5, 255, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 7, 6, 5, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 6, 255, 255, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 7, 6, 255, 255}, - {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 8, 7, 6, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 5, 255, 255, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 6, 5, 255, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 7, 6, 5, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 6, 255, 255, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 7, 6, 255, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 8, 7, 6, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 7, 255, 255, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 8, 7, 255, 255}, - {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 9, 8, 7, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 4, 255, 255, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 5, 4, 255, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 6, 5, 4, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 5, 255, 255, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 6, 5, 255, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 7, 6, 5, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 6, 255, 255, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 7, 6, 255, 255}, - {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 8, 7, 6, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 5, 255, 255, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 6, 5, 255, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 7, 6, 5, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 6, 255, 255, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 7, 6, 255, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 8, 7, 6, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 7, 255, 255, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 8, 7, 255, 255}, - {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 9, 8, 7, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 6, 255, 255, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 7, 6, 255, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 8, 7, 6, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 7, 255, 255, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 8, 7, 255, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 9, 8, 7, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 8, 255, 255, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 9, 8, 255, 255}, - {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 10, 9, 8, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 5, 255, 255, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 6, 5, 255, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 7, 6, 5, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 6, 255, 255, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 7, 6, 255, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 8, 7, 6, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 7, 255, 255, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 8, 7, 255, 255}, - {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 9, 8, 7, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 6, 255, 255, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 7, 6, 255, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 8, 7, 6, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 7, 255, 255, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 8, 7, 255, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 9, 8, 7, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 8, 255, 255, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 9, 8, 255, 255}, - {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 10, 9, 8, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 7, 255, 255, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 8, 7, 255, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 9, 8, 7, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 8, 255, 255, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 9, 8, 255, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 10, 9, 8, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 9, 255, 255, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 10, 9, 255, 255}, - {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 11, 10, 9, 255}, - {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 1, 255, 255, 255, 5, 4, 3, 2, 0, 0, 0, 0}, - {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 2, 1, 255, 255, 6, 5, 4, 3, 0, 0, 0, 0}, - {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 3, 2, 1, 255, 7, 6, 5, 4, 0, 0, 0, 0}, - {0, 255, 255, 255, 4, 3, 2, 1, 5, 255, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 4, 3, 2, 1, 6, 5, 255, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 4, 3, 2, 1, 7, 6, 5, 255, 0, 0, 0, 0}, - {0, 255, 255, 255, 4, 3, 2, 1, 8, 7, 6, 5, 0, 0, 0, 0}, - {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 2, 255, 255, 255, 6, 5, 4, 3, 0, 0, 0, 0}, - {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 3, 2, 255, 255, 7, 6, 5, 4, 0, 0, 0, 0}, - {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 4, 3, 2, 255, 8, 7, 6, 5, 0, 0, 0, 0}, - {1, 0, 255, 255, 5, 4, 3, 2, 6, 255, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 5, 4, 3, 2, 7, 6, 255, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 5, 4, 3, 2, 8, 7, 6, 255, 0, 0, 0, 0}, - {1, 0, 255, 255, 5, 4, 3, 2, 9, 8, 7, 6, 0, 0, 0, 0}, - {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 3, 255, 255, 255, 7, 6, 5, 4, 0, 0, 0, 0}, - {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 4, 3, 255, 255, 8, 7, 6, 5, 0, 0, 0, 0}, - {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 5, 4, 3, 255, 9, 8, 7, 6, 0, 0, 0, 0}, - {2, 1, 0, 255, 6, 5, 4, 3, 7, 255, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 6, 5, 4, 3, 8, 7, 255, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 6, 5, 4, 3, 9, 8, 7, 255, 0, 0, 0, 0}, - {2, 1, 0, 255, 6, 5, 4, 3, 10, 9, 8, 7, 0, 0, 0, 0}, - {3, 2, 1, 0, 4, 255, 255, 255, 5, 255, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 4, 255, 255, 255, 6, 5, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 4, 255, 255, 255, 7, 6, 5, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 4, 255, 255, 255, 8, 7, 6, 5, 0, 0, 0, 0}, - {3, 2, 1, 0, 5, 4, 255, 255, 6, 255, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 5, 4, 255, 255, 7, 6, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 5, 4, 255, 255, 8, 7, 6, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 5, 4, 255, 255, 9, 8, 7, 6, 0, 0, 0, 0}, - {3, 2, 1, 0, 6, 5, 4, 255, 7, 255, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 6, 5, 4, 255, 8, 7, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 6, 5, 4, 255, 9, 8, 7, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 6, 5, 4, 255, 10, 9, 8, 7, 0, 0, 0, 0}, - {3, 2, 1, 0, 7, 6, 5, 4, 8, 255, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 7, 6, 5, 4, 9, 8, 255, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 7, 6, 5, 4, 10, 9, 8, 255, 0, 0, 0, 0}, - {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 0, 0, 0, 0}}; -/* number of two bytes : 64 */ -/* number of two + three bytes : 145 */ -/* number of two + three + four bytes : 209 */ -const uint8_t utf8bigindex[4096][2] = -{ {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {148, 6}, - {209, 12}, - {151, 6}, - {163, 6}, - {66, 6}, - {209, 12}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {152, 7}, - {164, 7}, - {145, 3}, - {209, 12}, - {155, 7}, - {167, 7}, - {69, 7}, - {179, 7}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {170, 7}, - {71, 7}, - {182, 7}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {185, 7}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {171, 8}, - {72, 8}, - {183, 8}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {186, 8}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {68, 6}, - {122, 8}, - {74, 6}, - {92, 6}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {76, 6}, - {94, 6}, - {5, 8}, - {193, 6}, - {82, 6}, - {100, 6}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {77, 7}, - {95, 7}, - {6, 8}, - {194, 7}, - {83, 7}, - {101, 7}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {187, 9}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {77, 7}, - {95, 7}, - {7, 9}, - {194, 7}, - {83, 7}, - {101, 7}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {176, 10}, - {148, 6}, - {188, 10}, - {151, 6}, - {163, 6}, - {66, 6}, - {200, 10}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {191, 10}, - {152, 7}, - {164, 7}, - {145, 3}, - {203, 10}, - {90, 10}, - {108, 10}, - {69, 7}, - {126, 10}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {114, 10}, - {71, 7}, - {132, 10}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {138, 10}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {206, 10}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {116, 10}, - {72, 8}, - {134, 10}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {140, 10}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {15, 10}, - {122, 8}, - {23, 10}, - {39, 10}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {27, 10}, - {43, 10}, - {5, 8}, - {193, 6}, - {82, 6}, - {51, 10}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {29, 10}, - {45, 10}, - {6, 8}, - {194, 7}, - {83, 7}, - {53, 10}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {57, 10}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {142, 10}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {30, 10}, - {46, 10}, - {7, 9}, - {194, 7}, - {83, 7}, - {54, 10}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {58, 10}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {60, 10}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {148, 6}, - {209, 12}, - {151, 6}, - {163, 6}, - {66, 6}, - {209, 12}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {192, 11}, - {152, 7}, - {164, 7}, - {145, 3}, - {204, 11}, - {155, 7}, - {167, 7}, - {69, 7}, - {179, 7}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {170, 7}, - {71, 7}, - {182, 7}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {185, 7}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {207, 11}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {117, 11}, - {72, 8}, - {135, 11}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {141, 11}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {68, 6}, - {122, 8}, - {74, 6}, - {92, 6}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {76, 6}, - {94, 6}, - {5, 8}, - {193, 6}, - {82, 6}, - {100, 6}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {77, 7}, - {95, 7}, - {6, 8}, - {194, 7}, - {83, 7}, - {101, 7}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {143, 11}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {31, 11}, - {47, 11}, - {7, 9}, - {194, 7}, - {83, 7}, - {55, 11}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {59, 11}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {61, 11}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {176, 10}, - {148, 6}, - {188, 10}, - {151, 6}, - {163, 6}, - {66, 6}, - {200, 10}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {191, 10}, - {152, 7}, - {164, 7}, - {145, 3}, - {203, 10}, - {90, 10}, - {108, 10}, - {69, 7}, - {126, 10}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {114, 10}, - {71, 7}, - {132, 10}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {138, 10}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {206, 10}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {116, 10}, - {72, 8}, - {134, 10}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {140, 10}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {62, 11}, - {15, 10}, - {122, 8}, - {23, 10}, - {39, 10}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {27, 10}, - {43, 10}, - {5, 8}, - {193, 6}, - {82, 6}, - {51, 10}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {29, 10}, - {45, 10}, - {6, 8}, - {194, 7}, - {83, 7}, - {53, 10}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {57, 10}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {142, 10}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {30, 10}, - {46, 10}, - {7, 9}, - {194, 7}, - {83, 7}, - {54, 10}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {58, 10}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {60, 10}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {148, 6}, - {209, 12}, - {151, 6}, - {163, 6}, - {66, 6}, - {209, 12}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {152, 7}, - {164, 7}, - {145, 3}, - {209, 12}, - {155, 7}, - {167, 7}, - {69, 7}, - {179, 7}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {170, 7}, - {71, 7}, - {182, 7}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {185, 7}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {208, 12}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {171, 8}, - {72, 8}, - {183, 8}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {186, 8}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {68, 6}, - {122, 8}, - {74, 6}, - {92, 6}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {76, 6}, - {94, 6}, - {5, 8}, - {193, 6}, - {82, 6}, - {100, 6}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {77, 7}, - {95, 7}, - {6, 8}, - {194, 7}, - {83, 7}, - {101, 7}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {144, 12}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {77, 7}, - {95, 7}, - {7, 9}, - {194, 7}, - {83, 7}, - {101, 7}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {176, 10}, - {148, 6}, - {188, 10}, - {151, 6}, - {163, 6}, - {66, 6}, - {200, 10}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {191, 10}, - {152, 7}, - {164, 7}, - {145, 3}, - {203, 10}, - {90, 10}, - {108, 10}, - {69, 7}, - {126, 10}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {114, 10}, - {71, 7}, - {132, 10}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {138, 10}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {206, 10}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {116, 10}, - {72, 8}, - {134, 10}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {140, 10}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {63, 12}, - {15, 10}, - {122, 8}, - {23, 10}, - {39, 10}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {27, 10}, - {43, 10}, - {5, 8}, - {193, 6}, - {82, 6}, - {51, 10}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {29, 10}, - {45, 10}, - {6, 8}, - {194, 7}, - {83, 7}, - {53, 10}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {57, 10}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {142, 10}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {30, 10}, - {46, 10}, - {7, 9}, - {194, 7}, - {83, 7}, - {54, 10}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {58, 10}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {60, 10}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {148, 6}, - {209, 12}, - {151, 6}, - {163, 6}, - {66, 6}, - {209, 12}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {192, 11}, - {152, 7}, - {164, 7}, - {145, 3}, - {204, 11}, - {155, 7}, - {167, 7}, - {69, 7}, - {179, 7}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {170, 7}, - {71, 7}, - {182, 7}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {185, 7}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {207, 11}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {117, 11}, - {72, 8}, - {135, 11}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {141, 11}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {104, 8}, - {68, 6}, - {122, 8}, - {74, 6}, - {92, 6}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {76, 6}, - {94, 6}, - {5, 8}, - {193, 6}, - {82, 6}, - {100, 6}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {77, 7}, - {95, 7}, - {6, 8}, - {194, 7}, - {83, 7}, - {101, 7}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {143, 11}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {31, 11}, - {47, 11}, - {7, 9}, - {194, 7}, - {83, 7}, - {55, 11}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {59, 11}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {61, 11}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {209, 12}, - {209, 12}, - {147, 5}, - {209, 12}, - {150, 5}, - {162, 5}, - {65, 5}, - {209, 12}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {176, 10}, - {148, 6}, - {188, 10}, - {151, 6}, - {163, 6}, - {66, 6}, - {200, 10}, - {154, 6}, - {166, 6}, - {68, 6}, - {178, 6}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {169, 6}, - {70, 6}, - {181, 6}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {191, 10}, - {152, 7}, - {164, 7}, - {145, 3}, - {203, 10}, - {90, 10}, - {108, 10}, - {69, 7}, - {126, 10}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {114, 10}, - {71, 7}, - {132, 10}, - {77, 7}, - {95, 7}, - {65, 5}, - {194, 7}, - {83, 7}, - {101, 7}, - {67, 5}, - {119, 7}, - {73, 5}, - {91, 5}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {138, 10}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {103, 7}, - {68, 6}, - {121, 7}, - {74, 6}, - {92, 6}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {76, 6}, - {94, 6}, - {4, 7}, - {193, 6}, - {82, 6}, - {100, 6}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {206, 10}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {116, 10}, - {72, 8}, - {134, 10}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {140, 10}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {62, 11}, - {15, 10}, - {122, 8}, - {23, 10}, - {39, 10}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {27, 10}, - {43, 10}, - {5, 8}, - {193, 6}, - {82, 6}, - {51, 10}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {29, 10}, - {45, 10}, - {6, 8}, - {194, 7}, - {83, 7}, - {53, 10}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {57, 10}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {209, 12}, - {209, 12}, - {209, 12}, - {146, 4}, - {209, 12}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {160, 9}, - {172, 9}, - {147, 5}, - {184, 9}, - {150, 5}, - {162, 5}, - {65, 5}, - {196, 9}, - {153, 5}, - {165, 5}, - {67, 5}, - {177, 5}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {175, 9}, - {148, 6}, - {142, 10}, - {81, 9}, - {99, 9}, - {66, 6}, - {199, 9}, - {87, 9}, - {105, 9}, - {68, 6}, - {123, 9}, - {74, 6}, - {92, 6}, - {64, 4}, - {209, 12}, - {157, 6}, - {111, 9}, - {70, 6}, - {129, 9}, - {76, 6}, - {94, 6}, - {65, 5}, - {193, 6}, - {82, 6}, - {100, 6}, - {67, 5}, - {118, 6}, - {73, 5}, - {91, 5}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {190, 9}, - {152, 7}, - {164, 7}, - {145, 3}, - {202, 9}, - {89, 9}, - {107, 9}, - {69, 7}, - {125, 9}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {113, 9}, - {71, 7}, - {131, 9}, - {30, 10}, - {46, 10}, - {7, 9}, - {194, 7}, - {83, 7}, - {54, 10}, - {11, 9}, - {119, 7}, - {19, 9}, - {35, 9}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {137, 9}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {58, 10}, - {13, 9}, - {121, 7}, - {21, 9}, - {37, 9}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {25, 9}, - {41, 9}, - {4, 7}, - {193, 6}, - {82, 6}, - {49, 9}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {145, 3}, - {205, 9}, - {156, 8}, - {168, 8}, - {146, 4}, - {180, 8}, - {149, 4}, - {161, 4}, - {64, 4}, - {209, 12}, - {159, 8}, - {115, 9}, - {72, 8}, - {133, 9}, - {78, 8}, - {96, 8}, - {65, 5}, - {195, 8}, - {84, 8}, - {102, 8}, - {67, 5}, - {120, 8}, - {73, 5}, - {91, 5}, - {64, 4}, - {209, 12}, - {209, 12}, - {174, 8}, - {148, 6}, - {139, 9}, - {80, 8}, - {98, 8}, - {66, 6}, - {198, 8}, - {86, 8}, - {60, 10}, - {14, 9}, - {122, 8}, - {22, 9}, - {38, 9}, - {3, 8}, - {209, 12}, - {157, 6}, - {110, 8}, - {70, 6}, - {128, 8}, - {26, 9}, - {42, 9}, - {5, 8}, - {193, 6}, - {82, 6}, - {50, 9}, - {9, 8}, - {118, 6}, - {17, 8}, - {33, 8}, - {0, 6}, - {209, 12}, - {209, 12}, - {209, 12}, - {209, 12}, - {189, 8}, - {152, 7}, - {164, 7}, - {145, 3}, - {201, 8}, - {88, 8}, - {106, 8}, - {69, 7}, - {124, 8}, - {75, 7}, - {93, 7}, - {64, 4}, - {209, 12}, - {158, 7}, - {112, 8}, - {71, 7}, - {130, 8}, - {28, 9}, - {44, 9}, - {6, 8}, - {194, 7}, - {83, 7}, - {52, 9}, - {10, 8}, - {119, 7}, - {18, 8}, - {34, 8}, - {1, 7}, - {209, 12}, - {209, 12}, - {173, 7}, - {148, 6}, - {136, 8}, - {79, 7}, - {97, 7}, - {66, 6}, - {197, 7}, - {85, 7}, - {56, 9}, - {12, 8}, - {121, 7}, - {20, 8}, - {36, 8}, - {2, 7}, - {209, 12}, - {157, 6}, - {109, 7}, - {70, 6}, - {127, 7}, - {24, 8}, - {40, 8}, - {4, 7}, - {193, 6}, - {82, 6}, - {48, 8}, - {8, 7}, - {118, 6}, - {16, 7}, - {32, 7}, - {0, 6}}; -} // utf8_to_utf16 namespace -} // tables namespace -} // unnamed namespace -} // namespace simdutf - -#endif // SIMDUTF_UTF8_TO_UTF16_TABLES_H -/* end file src/tables/utf8_to_utf16_tables.h */ -/* begin file src/tables/utf16_to_utf8_tables.h */ -// file generated by scripts/sse_convert_utf16_to_utf8.py -#ifndef SIMDUTF_UTF16_TO_UTF8_TABLES_H -#define SIMDUTF_UTF16_TO_UTF8_TABLES_H - -namespace simdutf { -namespace { -namespace tables { -namespace utf16_to_utf8 { - - // 1 byte for length, 16 bytes for mask - const uint8_t pack_1_2_utf8_bytes[256][17] = { - {16,1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14}, - {15,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14,0x80}, - {15,1,0,3,2,5,4,7,6,8,11,10,13,12,15,14,0x80}, - {14,0,3,2,5,4,7,6,8,11,10,13,12,15,14,0x80,0x80}, - {15,1,0,2,5,4,7,6,9,8,11,10,13,12,15,14,0x80}, - {14,0,2,5,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80}, - {14,1,0,2,5,4,7,6,8,11,10,13,12,15,14,0x80,0x80}, - {13,0,2,5,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {15,1,0,3,2,5,4,7,6,9,8,10,13,12,15,14,0x80}, - {14,0,3,2,5,4,7,6,9,8,10,13,12,15,14,0x80,0x80}, - {14,1,0,3,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80}, - {13,0,3,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80}, - {14,1,0,2,5,4,7,6,9,8,10,13,12,15,14,0x80,0x80}, - {13,0,2,5,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80}, - {13,1,0,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {15,1,0,3,2,4,7,6,9,8,11,10,13,12,15,14,0x80}, - {14,0,3,2,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80}, - {14,1,0,3,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80}, - {13,0,3,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {14,1,0,2,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80}, - {13,0,2,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {13,1,0,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80}, - {13,0,3,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80}, - {13,1,0,3,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {15,1,0,3,2,5,4,7,6,9,8,11,10,12,15,14,0x80}, - {14,0,3,2,5,4,7,6,9,8,11,10,12,15,14,0x80,0x80}, - {14,1,0,3,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80}, - {13,0,3,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80}, - {14,1,0,2,5,4,7,6,9,8,11,10,12,15,14,0x80,0x80}, - {13,0,2,5,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80}, - {13,1,0,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80}, - {12,0,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80}, - {13,0,3,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80}, - {12,0,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80}, - {13,0,3,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80}, - {13,1,0,3,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80}, - {12,0,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {15,1,0,3,2,5,4,6,9,8,11,10,13,12,15,14,0x80}, - {14,0,3,2,5,4,6,9,8,11,10,13,12,15,14,0x80,0x80}, - {14,1,0,3,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80}, - {13,0,3,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {14,1,0,2,5,4,6,9,8,11,10,13,12,15,14,0x80,0x80}, - {13,0,2,5,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {13,1,0,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80}, - {13,0,3,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80}, - {13,0,3,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {13,1,0,3,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80}, - {13,0,3,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80}, - {12,0,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80}, - {12,0,3,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,3,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,3,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {15,1,0,3,2,5,4,7,6,9,8,11,10,13,12,14,0x80}, - {14,0,3,2,5,4,7,6,9,8,11,10,13,12,14,0x80,0x80}, - {14,1,0,3,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80}, - {13,0,3,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80}, - {14,1,0,2,5,4,7,6,9,8,11,10,13,12,14,0x80,0x80}, - {13,0,2,5,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80}, - {13,1,0,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80}, - {12,0,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80}, - {13,0,3,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80}, - {12,0,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80}, - {13,0,3,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80}, - {13,1,0,3,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80}, - {12,0,3,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80}, - {12,0,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80}, - {12,0,3,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80}, - {13,0,3,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80}, - {12,0,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80}, - {12,0,3,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,3,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,3,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {14,1,0,3,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80}, - {13,0,3,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {13,1,0,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80}, - {12,0,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80}, - {12,0,3,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,3,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,3,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {13,1,0,3,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80}, - {12,0,3,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,3,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,3,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {12,1,0,3,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80}, - {11,0,3,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,3,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,3,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,1,0,3,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80}, - {10,0,3,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,3,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,3,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,1,0,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,1,0,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80} - }; - - // 1 byte for length, 16 bytes for mask - const uint8_t pack_1_2_3_utf8_bytes[256][17] = { - {12,2,3,1,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80}, - {9,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,3,1,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80}, - {10,0,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,2,3,1,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80}, - {8,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,3,1,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,2,3,1,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,3,1,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,2,3,1,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80}, - {8,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,3,1,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,2,3,1,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,3,1,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,2,3,1,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,3,1,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,2,3,1,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,3,1,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {1,0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,2,3,1,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,3,1,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,0,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,2,3,1,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {1,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,3,1,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,0,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,2,3,1,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,3,1,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,0,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,2,3,1,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {1,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,3,1,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,0,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,2,3,1,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,3,1,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,0,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {11,2,3,1,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80}, - {8,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,3,1,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,0,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,2,3,1,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,3,1,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,2,3,1,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,3,1,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,0,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,2,3,1,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,3,1,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {10,2,3,1,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,3,1,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,0,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,2,3,1,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {1,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,3,1,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,0,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,2,3,1,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,3,1,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,0,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {9,2,3,1,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,3,1,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,0,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {8,2,3,1,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,3,1,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,0,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,2,3,1,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {2,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,3,1,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,0,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {7,2,3,1,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,3,1,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,0,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {6,2,3,1,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {3,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {5,3,1,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, - {4,0,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80} - }; - -} // utf16_to_utf8 namespace -} // tables namespace -} // unnamed namespace -} // namespace simdutf - -#endif // SIMDUTF_UTF16_TO_UTF8_TABLES_H -/* end file src/tables/utf16_to_utf8_tables.h */ -// End of tables. - -// The scalar routines should be included once. -/* begin file src/scalar/ascii.h */ -#ifndef SIMDUTF_ASCII_H -#define SIMDUTF_ASCII_H - -namespace simdutf { -namespace scalar { -namespace { -namespace ascii { -#if SIMDUTF_IMPLEMENTATION_FALLBACK -// Only used by the fallback kernel. -inline simdutf_warn_unused bool validate(const char *buf, size_t len) noexcept { - const uint8_t *data = reinterpret_cast(buf); - uint64_t pos = 0; - // process in blocks of 16 bytes when possible - for (;pos + 16 <= len; pos += 16) { - uint64_t v1; - std::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) != 0) { return false; } - } - // process the tail byte-by-byte - for (;pos < len; pos ++) { - if (data[pos] >= 0b10000000) { return false; } - } - return true; -} -#endif - -inline simdutf_warn_unused result validate_with_errors(const char *buf, size_t len) noexcept { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - // process in blocks of 16 bytes when possible - for (;pos + 16 <= len; pos += 16) { - uint64_t v1; - std::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) != 0) { - for (;pos < len; pos ++) { - if (data[pos] >= 0b10000000) { return result(error_code::TOO_LARGE, pos); } - } - } - } - // process the tail byte-by-byte - for (;pos < len; pos ++) { - if (data[pos] >= 0b10000000) { return result(error_code::TOO_LARGE, pos); } - } - return result(error_code::SUCCESS, pos); -} - -} // ascii namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/ascii.h */ -/* begin file src/scalar/latin1.h */ -#ifndef SIMDUTF_LATIN1_H -#define SIMDUTF_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace latin1 { - -inline size_t utf32_length_from_latin1(size_t len) { - // We are not BOM aware. - return len; // a utf32 unit will always represent 1 latin1 character -} - -inline size_t utf8_length_from_latin1(const char *buf, size_t len) { - const uint8_t * c = reinterpret_cast(buf); - size_t answer = 0; - for(size_t i = 0; i>7)) { answer++; } - } - return answer + len; -} - -inline size_t utf16_length_from_latin1(size_t len) { - return len; -} - -} // utf32 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/latin1.h */ - -/* begin file src/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */ -#ifndef SIMDUTF_VALID_UTF32_TO_UTF8_H -#define SIMDUTF_VALID_UTF32_TO_UTF8_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32_to_utf8 { - -#if SIMDUTF_IMPLEMENTATION_FALLBACK || SIMDUTF_IMPLEMENTATION_PPC64 -// only used by the fallback and POWER kernel -inline size_t convert_valid(const char32_t* buf, size_t len, char* utf8_output) { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 2 ASCII characters - if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0xFFFFFF80FFFFFF80) == 0) { - *utf8_output++ = char(buf[pos]); - *utf8_output++ = char(buf[pos+1]); - pos += 2; - continue; - } - } - uint32_t word = data[pos]; - if((word & 0xFFFFFF80)==0) { - // will generate one UTF-8 bytes - *utf8_output++ = char(word); - pos++; - } else if((word & 0xFFFFF800)==0) { - // will generate two UTF-8 bytes - // we have 0b110XXXXX 0b10XXXXXX - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else if((word & 0xFFFF0000)==0) { - // will generate three UTF-8 bytes - // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else { - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos ++; - } - } - return utf8_output - start; -} -#endif // SIMDUTF_IMPLEMENTATION_FALLBACK || SIMDUTF_IMPLEMENTATION_PPC64 - -} // utf32_to_utf8 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */ -/* begin file src/scalar/utf32_to_utf8/utf32_to_utf8.h */ -#ifndef SIMDUTF_UTF32_TO_UTF8_H -#define SIMDUTF_UTF32_TO_UTF8_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32_to_utf8 { - -inline size_t convert(const char32_t* buf, size_t len, char* utf8_output) { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 2 ASCII characters - if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0xFFFFFF80FFFFFF80) == 0) { - *utf8_output++ = char(buf[pos]); - *utf8_output++ = char(buf[pos+1]); - pos += 2; - continue; - } - } - uint32_t word = data[pos]; - if((word & 0xFFFFFF80)==0) { - // will generate one UTF-8 bytes - *utf8_output++ = char(word); - pos++; - } else if((word & 0xFFFFF800)==0) { - // will generate two UTF-8 bytes - // we have 0b110XXXXX 0b10XXXXXX - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else if((word & 0xFFFF0000)==0) { - // will generate three UTF-8 bytes - // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX - if (word >= 0xD800 && word <= 0xDFFF) { return 0; } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else { - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - if (word > 0x10FFFF) { return 0; } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos ++; - } - } - return utf8_output - start; -} - -inline result convert_with_errors(const char32_t* buf, size_t len, char* utf8_output) { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 2 ASCII characters - if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0xFFFFFF80FFFFFF80) == 0) { - *utf8_output++ = char(buf[pos]); - *utf8_output++ = char(buf[pos+1]); - pos += 2; - continue; - } - } - uint32_t word = data[pos]; - if((word & 0xFFFFFF80)==0) { - // will generate one UTF-8 bytes - *utf8_output++ = char(word); - pos++; - } else if((word & 0xFFFFF800)==0) { - // will generate two UTF-8 bytes - // we have 0b110XXXXX 0b10XXXXXX - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else if((word & 0xFFFF0000)==0) { - // will generate three UTF-8 bytes - // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX - if (word >= 0xD800 && word <= 0xDFFF) { return result(error_code::SURROGATE, pos); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else { - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - if (word > 0x10FFFF) { return result(error_code::TOO_LARGE, pos); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos ++; - } - } - return result(error_code::SUCCESS, utf8_output - start); -} - -} // utf32_to_utf8 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32_to_utf8/utf32_to_utf8.h */ - -/* begin file src/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */ -#ifndef SIMDUTF_VALID_UTF32_TO_UTF16_H -#define SIMDUTF_VALID_UTF32_TO_UTF16_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32_to_utf16 { - -template -inline size_t convert_valid(const char32_t* buf, size_t len, char16_t* utf16_output) { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{utf16_output}; - while (pos < len) { - uint32_t word = data[pos]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(uint16_t(word))) : char16_t(word); - pos++; - } else { - // will generate a surrogate pair - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = utf16::swap_bytes(high_surrogate); - low_surrogate = utf16::swap_bytes(low_surrogate); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - pos++; - } - } - return utf16_output - start; -} - -} // utf32_to_utf16 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */ -/* begin file src/scalar/utf32_to_utf16/utf32_to_utf16.h */ -#ifndef SIMDUTF_UTF32_TO_UTF16_H -#define SIMDUTF_UTF32_TO_UTF16_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32_to_utf16 { - -template -inline size_t convert(const char32_t* buf, size_t len, char16_t* utf16_output) { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{utf16_output}; - while (pos < len) { - uint32_t word = data[pos]; - if((word & 0xFFFF0000)==0) { - if (word >= 0xD800 && word <= 0xDFFF) { return 0; } - // will not generate a surrogate pair - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(uint16_t(word))) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return 0; } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = utf16::swap_bytes(high_surrogate); - low_surrogate = utf16::swap_bytes(low_surrogate); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - pos++; - } - return utf16_output - start; -} - -template -inline result convert_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) { - const uint32_t *data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{utf16_output}; - while (pos < len) { - uint32_t word = data[pos]; - if((word & 0xFFFF0000)==0) { - if (word >= 0xD800 && word <= 0xDFFF) { return result(error_code::SURROGATE, pos); } - // will not generate a surrogate pair - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(uint16_t(word))) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return result(error_code::TOO_LARGE, pos); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = utf16::swap_bytes(high_surrogate); - low_surrogate = utf16::swap_bytes(low_surrogate); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - pos++; - } - return result(error_code::SUCCESS, utf16_output - start); -} - -} // utf32_to_utf16 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32_to_utf16/utf32_to_utf16.h */ - -/* begin file src/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */ -#ifndef SIMDUTF_VALID_UTF16_TO_UTF8_H -#define SIMDUTF_VALID_UTF16_TO_UTF8_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16_to_utf8 { - -template -inline size_t convert_valid(const char16_t* buf, size_t len, char* utf8_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 4 ASCII characters - if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if (!match_system(big_endian)) { v = (v >> 8) | (v << (64 - 8)); } - if ((v & 0xFF80FF80FF80FF80) == 0) { - size_t final_pos = pos + 4; - while(pos < final_pos) { - *utf8_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(buf[pos])) : char(buf[pos]); - pos++; - } - continue; - } - } - - uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word & 0xFF80)==0) { - // will generate one UTF-8 bytes - *utf8_output++ = char(word); - pos++; - } else if((word & 0xF800)==0) { - // will generate two UTF-8 bytes - // we have 0b110XXXXX 0b10XXXXXX - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else if((word &0xF800 ) != 0xD800) { - // will generate three UTF-8 bytes - // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - if(pos + 1 >= len) { return 0; } // minimal bound checking - uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - uint32_t value = (diff << 10) + diff2 + 0x10000; - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - pos += 2; - } - } - return utf8_output - start; -} - -} // utf16_to_utf8 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */ -/* begin file src/scalar/utf16_to_utf8/utf16_to_utf8.h */ -#ifndef SIMDUTF_UTF16_TO_UTF8_H -#define SIMDUTF_UTF16_TO_UTF8_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16_to_utf8 { - -template -inline size_t convert(const char16_t* buf, size_t len, char* utf8_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 8 bytes - if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if (!match_system(big_endian)) { v = (v >> 8) | (v << (64 - 8)); } - if ((v & 0xFF80FF80FF80FF80) == 0) { - size_t final_pos = pos + 4; - while(pos < final_pos) { - *utf8_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(buf[pos])) : char(buf[pos]); - pos++; - } - continue; - } - } - uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word & 0xFF80)==0) { - // will generate one UTF-8 bytes - *utf8_output++ = char(word); - pos++; - } else if((word & 0xF800)==0) { - // will generate two UTF-8 bytes - // we have 0b110XXXXX 0b10XXXXXX - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else if((word &0xF800 ) != 0xD800) { - // will generate three UTF-8 bytes - // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else { - // must be a surrogate pair - if(pos + 1 >= len) { return 0; } - uint16_t diff = uint16_t(word - 0xD800); - if(diff > 0x3FF) { return 0; } - uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if(diff2 > 0x3FF) { return 0; } - uint32_t value = (diff << 10) + diff2 + 0x10000; - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - pos += 2; - } - } - return utf8_output - start; -} - -template -inline result convert_with_errors(const char16_t* buf, size_t len, char* utf8_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 8 bytes - if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if (!match_system(big_endian)) v = (v >> 8) | (v << (64 - 8)); - if ((v & 0xFF80FF80FF80FF80) == 0) { - size_t final_pos = pos + 4; - while(pos < final_pos) { - *utf8_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(buf[pos])) : char(buf[pos]); - pos++; - } - continue; - } - } - uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word & 0xFF80)==0) { - // will generate one UTF-8 bytes - *utf8_output++ = char(word); - pos++; - } else if((word & 0xF800)==0) { - // will generate two UTF-8 bytes - // we have 0b110XXXXX 0b10XXXXXX - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else if((word &0xF800 ) != 0xD800) { - // will generate three UTF-8 bytes - // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - pos++; - } else { - // must be a surrogate pair - if(pos + 1 >= len) { return result(error_code::SURROGATE, pos); } - uint16_t diff = uint16_t(word - 0xD800); - if(diff > 0x3FF) { return result(error_code::SURROGATE, pos); } - uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if(diff2 > 0x3FF) { return result(error_code::SURROGATE, pos); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - pos += 2; - } - } - return result(error_code::SUCCESS, utf8_output - start); -} - -} // utf16_to_utf8 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16_to_utf8/utf16_to_utf8.h */ - -/* begin file src/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */ -#ifndef SIMDUTF_VALID_UTF16_TO_UTF32_H -#define SIMDUTF_VALID_UTF16_TO_UTF32_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16_to_utf32 { - -template -inline size_t convert_valid(const char16_t* buf, size_t len, char32_t* utf32_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char32_t* start{utf32_output}; - while (pos < len) { - uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word &0xF800 ) != 0xD800) { - // No surrogate pair, extend 16-bit word to 32-bit word - *utf32_output++ = char32_t(word); - pos++; - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - if(pos + 1 >= len) { return 0; } // minimal bound checking - uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - pos += 2; - } - } - return utf32_output - start; -} - -} // utf16_to_utf32 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */ -/* begin file src/scalar/utf16_to_utf32/utf16_to_utf32.h */ -#ifndef SIMDUTF_UTF16_TO_UTF32_H -#define SIMDUTF_UTF16_TO_UTF32_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16_to_utf32 { - -template -inline size_t convert(const char16_t* buf, size_t len, char32_t* utf32_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char32_t* start{utf32_output}; - while (pos < len) { - uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word &0xF800 ) != 0xD800) { - // No surrogate pair, extend 16-bit word to 32-bit word - *utf32_output++ = char32_t(word); - pos++; - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - if(diff > 0x3FF) { return 0; } - if(pos + 1 >= len) { return 0; } // minimal bound checking - uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if(diff2 > 0x3FF) { return 0; } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - pos += 2; - } - } - return utf32_output - start; -} - -template -inline result convert_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char32_t* start{utf32_output}; - while (pos < len) { - uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word &0xF800 ) != 0xD800) { - // No surrogate pair, extend 16-bit word to 32-bit word - *utf32_output++ = char32_t(word); - pos++; - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - if(diff > 0x3FF) { return result(error_code::SURROGATE, pos); } - if(pos + 1 >= len) { return result(error_code::SURROGATE, pos); } // minimal bound checking - uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1]; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if(diff2 > 0x3FF) { return result(error_code::SURROGATE, pos); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - pos += 2; - } - } - return result(error_code::SUCCESS, utf32_output - start); -} - -} // utf16_to_utf32 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16_to_utf32/utf16_to_utf32.h */ - -/* begin file src/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */ -#ifndef SIMDUTF_VALID_UTF8_TO_UTF16_H -#define SIMDUTF_VALID_UTF8_TO_UTF16_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8_to_utf16 { - -template -inline size_t convert_valid(const char* buf, size_t len, char16_t* utf16_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{utf16_output}; - while (pos < len) { - // try to convert the next block of 8 ASCII bytes - if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0x8080808080808080) == 0) { - size_t final_pos = pos + 8; - while(pos < final_pos) { - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(buf[pos])) : char16_t(buf[pos]); - pos++; - } - continue; - } - } - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(leading_byte)) : char16_t(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { - // We have a two-byte UTF-8, it should become - // a single UTF-16 word. - if(pos + 1 >= len) { break; } // minimal bound checking - uint16_t code_point = uint16_t(((leading_byte &0b00011111) << 6) | (data[pos + 1] &0b00111111)); - if (!match_system(big_endian)) { - code_point = utf16::swap_bytes(uint16_t(code_point)); - } - *utf16_output++ = char16_t(code_point); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8, it should become - // a single UTF-16 word. - if(pos + 2 >= len) { break; } // minimal bound checking - uint16_t code_point = uint16_t(((leading_byte &0b00001111) << 12) | ((data[pos + 1] &0b00111111) << 6) | (data[pos + 2] &0b00111111)); - if (!match_system(big_endian)) { - code_point = utf16::swap_bytes(uint16_t(code_point)); - } - *utf16_output++ = char16_t(code_point); - pos += 3; - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - if(pos + 3 >= len) { break; } // minimal bound checking - uint32_t code_point = ((leading_byte & 0b00000111) << 18 )| ((data[pos + 1] &0b00111111) << 12) - | ((data[pos + 2] &0b00111111) << 6) | (data[pos + 3] &0b00111111); - code_point -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = utf16::swap_bytes(high_surrogate); - low_surrogate = utf16::swap_bytes(low_surrogate); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - pos += 4; - } else { - // we may have a continuation but we do not do error checking - return 0; - } - } - return utf16_output - start; -} - - -} // namespace utf8_to_utf16 -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */ -/* begin file src/scalar/utf8_to_utf16/utf8_to_utf16.h */ -#ifndef SIMDUTF_UTF8_TO_UTF16_H -#define SIMDUTF_UTF8_TO_UTF16_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8_to_utf16 { - -template -inline size_t convert(const char* buf, size_t len, char16_t* utf16_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{utf16_output}; - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - size_t final_pos = pos + 16; - while(pos < final_pos) { - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(buf[pos])) : char16_t(buf[pos]); - pos++; - } - continue; - } - } - - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(leading_byte)): char16_t(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { - // We have a two-byte UTF-8, it should become - // a single UTF-16 word. - if(pos + 1 >= len) { return 0; } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } - // range check - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); - if (code_point < 0x80 || 0x7ff < code_point) { return 0; } - if (!match_system(big_endian)) { - code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point))); - } - *utf16_output++ = char16_t(code_point); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8, it should become - // a single UTF-16 word. - if(pos + 2 >= len) { return 0; } // minimal bound checking - - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; } - // range check - uint32_t code_point = (leading_byte & 0b00001111) << 12 | - (data[pos + 1] & 0b00111111) << 6 | - (data[pos + 2] & 0b00111111); - if (code_point < 0x800 || 0xffff < code_point || - (0xd7ff < code_point && code_point < 0xe000)) { - return 0; - } - if (!match_system(big_endian)) { - code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point))); - } - *utf16_output++ = char16_t(code_point); - pos += 3; - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - if(pos + 3 >= len) { return 0; } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; } - if ((data[pos + 3] & 0b11000000) != 0b10000000) { return 0; } - - // range check - uint32_t code_point = - (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | - (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); - if (code_point <= 0xffff || 0x10ffff < code_point) { return 0; } - code_point -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = utf16::swap_bytes(high_surrogate); - low_surrogate = utf16::swap_bytes(low_surrogate); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - pos += 4; - } else { - return 0; - } - } - return utf16_output - start; -} - -template -inline result convert_with_errors(const char* buf, size_t len, char16_t* utf16_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{utf16_output}; - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - size_t final_pos = pos + 16; - while(pos < final_pos) { - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(buf[pos])) : char16_t(buf[pos]); - pos++; - } - continue; - } - } - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(leading_byte)): char16_t(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { - // We have a two-byte UTF-8, it should become - // a single UTF-16 word. - if(pos + 1 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); - if (code_point < 0x80 || 0x7ff < code_point) { return result(error_code::OVERLONG, pos); } - if (!match_system(big_endian)) { - code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point))); - } - *utf16_output++ = char16_t(code_point); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8, it should become - // a single UTF-16 word. - if(pos + 2 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking - - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - uint32_t code_point = (leading_byte & 0b00001111) << 12 | - (data[pos + 1] & 0b00111111) << 6 | - (data[pos + 2] & 0b00111111); - if ((code_point < 0x800) || (0xffff < code_point)) { return result(error_code::OVERLONG, pos);} - if (0xd7ff < code_point && code_point < 0xe000) { return result(error_code::SURROGATE, pos); } - if (!match_system(big_endian)) { - code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point))); - } - *utf16_output++ = char16_t(code_point); - pos += 3; - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - if(pos + 3 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 3] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - - // range check - uint32_t code_point = - (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | - (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); - if (code_point <= 0xffff) { return result(error_code::OVERLONG, pos); } - if (0x10ffff < code_point) { return result(error_code::TOO_LARGE, pos); } - code_point -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = utf16::swap_bytes(high_surrogate); - low_surrogate = utf16::swap_bytes(low_surrogate); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - pos += 4; - } else { - // we either have too many continuation bytes or an invalid leading byte - if ((leading_byte & 0b11000000) == 0b10000000) { return result(error_code::TOO_LONG, pos); } - else { return result(error_code::HEADER_BITS, pos); } - } - } - return result(error_code::SUCCESS, utf16_output - start); -} - -/** - * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and we have - * up to len input bytes left, and we encountered some error. It is possible that - * the error is at 'buf' exactly, but it could also be in the previous bytes (up to 3 bytes back). - * - * prior_bytes indicates how many bytes, prior to 'buf' may belong to the current memory section - * and can be safely accessed. We prior_bytes to access safely up to three bytes before 'buf'. - * - * The caller is responsible to ensure that len > 0. - * - * If the error is believed to have occurred prior to 'buf', the count value contain in the result - * will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3. - */ -template -inline result rewind_and_convert_with_errors(size_t prior_bytes, const char* buf, size_t len, char16_t* utf16_output) { - size_t extra_len{0}; - // We potentially need to go back in time and find a leading byte. - // In theory '3' would be sufficient, but sometimes the error can go back quite far. - size_t how_far_back = prior_bytes; - // size_t how_far_back = 3; // 3 bytes in the past + current position - // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; } - bool found_leading_bytes{false}; - // important: it is i <= how_far_back and not 'i < how_far_back'. - for(size_t i = 0; i <= how_far_back; i++) { - unsigned char byte = buf[0-i]; - found_leading_bytes = ((byte & 0b11000000) != 0b10000000); - if(found_leading_bytes) { - buf -= i; - extra_len = i; - break; - } - } - // - // It is possible for this function to return a negative count in its result. - // C++ Standard Section 18.1 defines size_t is in which is described in C Standard as . - // C Standard Section 4.1.5 defines size_t as an unsigned integral type of the result of the sizeof operator - // - // An unsigned type will simply wrap round arithmetically (well defined). - // - if(!found_leading_bytes) { - // If how_far_back == 3, we may have four consecutive continuation bytes!!! - // [....] [continuation] [continuation] [continuation] | [buf is continuation] - // Or we possibly have a stream that does not start with a leading byte. - return result(error_code::TOO_LONG, 0-how_far_back); - } - result res = convert_with_errors(buf, len + extra_len, utf16_output); - if (res.error) { - res.count -= extra_len; - } - return res; -} - -} // utf8_to_utf16 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8_to_utf16/utf8_to_utf16.h */ - -/* begin file src/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */ -#ifndef SIMDUTF_VALID_UTF8_TO_UTF32_H -#define SIMDUTF_VALID_UTF8_TO_UTF32_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8_to_utf32 { - -inline size_t convert_valid(const char* buf, size_t len, char32_t* utf32_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char32_t* start{utf32_output}; - while (pos < len) { - // try to convert the next block of 8 ASCII bytes - if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0x8080808080808080) == 0) { - size_t final_pos = pos + 8; - while(pos < final_pos) { - *utf32_output++ = char32_t(buf[pos]); - pos++; - } - continue; - } - } - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *utf32_output++ = char32_t(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { - // We have a two-byte UTF-8 - if(pos + 1 >= len) { break; } // minimal bound checking - *utf32_output++ = char32_t(((leading_byte &0b00011111) << 6) | (data[pos + 1] &0b00111111)); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8 - if(pos + 2 >= len) { break; } // minimal bound checking - *utf32_output++ = char32_t(((leading_byte &0b00001111) << 12) | ((data[pos + 1] &0b00111111) << 6) | (data[pos + 2] &0b00111111)); - pos += 3; - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - if(pos + 3 >= len) { break; } // minimal bound checking - uint32_t code_word = ((leading_byte & 0b00000111) << 18 )| ((data[pos + 1] &0b00111111) << 12) - | ((data[pos + 2] &0b00111111) << 6) | (data[pos + 3] &0b00111111); - *utf32_output++ = char32_t(code_word); - pos += 4; - } else { - // we may have a continuation but we do not do error checking - return 0; - } - } - return utf32_output - start; -} - - -} // namespace utf8_to_utf32 -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */ -/* begin file src/scalar/utf8_to_utf32/utf8_to_utf32.h */ -#ifndef SIMDUTF_UTF8_TO_UTF32_H -#define SIMDUTF_UTF8_TO_UTF32_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8_to_utf32 { - -inline size_t convert(const char* buf, size_t len, char32_t* utf32_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char32_t* start{utf32_output}; - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - size_t final_pos = pos + 16; - while(pos < final_pos) { - *utf32_output++ = char32_t(buf[pos]); - pos++; - } - continue; - } - } - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *utf32_output++ = char32_t(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { - // We have a two-byte UTF-8 - if(pos + 1 >= len) { return 0; } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } - // range check - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); - if (code_point < 0x80 || 0x7ff < code_point) { return 0; } - *utf32_output++ = char32_t(code_point); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8 - if(pos + 2 >= len) { return 0; } // minimal bound checking - - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; } - // range check - uint32_t code_point = (leading_byte & 0b00001111) << 12 | - (data[pos + 1] & 0b00111111) << 6 | - (data[pos + 2] & 0b00111111); - if (code_point < 0x800 || 0xffff < code_point || - (0xd7ff < code_point && code_point < 0xe000)) { - return 0; - } - *utf32_output++ = char32_t(code_point); - pos += 3; - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - if(pos + 3 >= len) { return 0; } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; } - if ((data[pos + 3] & 0b11000000) != 0b10000000) { return 0; } - - // range check - uint32_t code_point = - (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | - (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); - if (code_point <= 0xffff || 0x10ffff < code_point) { return 0; } - *utf32_output++ = char32_t(code_point); - pos += 4; - } else { - return 0; - } - } - return utf32_output - start; -} - -inline result convert_with_errors(const char* buf, size_t len, char32_t* utf32_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char32_t* start{utf32_output}; - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - size_t final_pos = pos + 16; - while(pos < final_pos) { - *utf32_output++ = char32_t(buf[pos]); - pos++; - } - continue; - } - } - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *utf32_output++ = char32_t(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { - // We have a two-byte UTF-8 - if(pos + 1 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); - if (code_point < 0x80 || 0x7ff < code_point) { return result(error_code::OVERLONG, pos); } - *utf32_output++ = char32_t(code_point); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8 - if(pos + 2 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking - - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - // range check - uint32_t code_point = (leading_byte & 0b00001111) << 12 | - (data[pos + 1] & 0b00111111) << 6 | - (data[pos + 2] & 0b00111111); - if (code_point < 0x800 || 0xffff < code_point) { return result(error_code::OVERLONG, pos); } - if (0xd7ff < code_point && code_point < 0xe000) { return result(error_code::SURROGATE, pos); } - *utf32_output++ = char32_t(code_point); - pos += 3; - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - if(pos + 3 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos);} - if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - if ((data[pos + 3] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); } - - // range check - uint32_t code_point = - (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | - (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); - if (code_point <= 0xffff) { return result(error_code::OVERLONG, pos); } - if (0x10ffff < code_point) { return result(error_code::TOO_LARGE, pos); } - *utf32_output++ = char32_t(code_point); - pos += 4; - } else { - // we either have too many continuation bytes or an invalid leading byte - if ((leading_byte & 0b11000000) == 0b10000000) { return result(error_code::TOO_LONG, pos); } - else { return result(error_code::HEADER_BITS, pos); } - } - } - return result(error_code::SUCCESS, utf32_output - start); -} - -/** - * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and we have - * up to len input bytes left, and we encountered some error. It is possible that - * the error is at 'buf' exactly, but it could also be in the previous bytes location (up to 3 bytes back). - * - * prior_bytes indicates how many bytes, prior to 'buf' may belong to the current memory section - * and can be safely accessed. We prior_bytes to access safely up to three bytes before 'buf'. - * - * The caller is responsible to ensure that len > 0. - * - * If the error is believed to have occurred prior to 'buf', the count value contain in the result - * will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3. - */ -inline result rewind_and_convert_with_errors(size_t prior_bytes, const char* buf, size_t len, char32_t* utf32_output) { - size_t extra_len{0}; - // We potentially need to go back in time and find a leading byte. - size_t how_far_back = 3; // 3 bytes in the past + current position - if(how_far_back > prior_bytes) { how_far_back = prior_bytes; } - bool found_leading_bytes{false}; - // important: it is i <= how_far_back and not 'i < how_far_back'. - for(size_t i = 0; i <= how_far_back; i++) { - unsigned char byte = buf[0-i]; - found_leading_bytes = ((byte & 0b11000000) != 0b10000000); - if(found_leading_bytes) { - buf -= i; - extra_len = i; - break; - } - } - // - // It is possible for this function to return a negative count in its result. - // C++ Standard Section 18.1 defines size_t is in which is described in C Standard as . - // C Standard Section 4.1.5 defines size_t as an unsigned integral type of the result of the sizeof operator - // - // An unsigned type will simply wrap round arithmetically (well defined). - // - if(!found_leading_bytes) { - // If how_far_back == 3, we may have four consecutive continuation bytes!!! - // [....] [continuation] [continuation] [continuation] | [buf is continuation] - // Or we possibly have a stream that does not start with a leading byte. - return result(error_code::TOO_LONG, 0-how_far_back); - } - - result res = convert_with_errors(buf, len + extra_len, utf32_output); - if (res.error) { - res.count -= extra_len; - } - return res; -} - -} // utf8_to_utf32 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8_to_utf32/utf8_to_utf32.h */ - -/* begin file src/scalar/latin1_to_utf8/latin1_to_utf8.h */ -#ifndef SIMDUTF_LATIN1_TO_UTF8_H -#define SIMDUTF_LATIN1_TO_UTF8_H - -namespace simdutf { -namespace scalar { -namespace { -namespace latin1_to_utf8 { - -inline size_t convert(const char* buf, size_t len, char* utf8_output) { - const unsigned char *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{utf8_output}; - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000, so it makes sense to concatenate everything - if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII - size_t final_pos = pos + 16; - while(pos < final_pos) { - *utf8_output++ = char(buf[pos]); - pos++; - } - continue; - } - } - - unsigned char byte = data[pos]; - if((byte & 0x80) == 0) { // if ASCII - // will generate one UTF-8 bytes - *utf8_output++ = char(byte); - pos++; - } else { - // will generate two UTF-8 bytes - *utf8_output++ = char((byte>>6) | 0b11000000); - *utf8_output++ = char((byte & 0b111111) | 0b10000000); - pos++; - } - } - return utf8_output - start; -} - -} // latin1_to_utf8 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/latin1_to_utf8/latin1_to_utf8.h */ -/* begin file src/scalar/latin1_to_utf16/latin1_to_utf16.h */ -#ifndef SIMDUTF_LATIN1_TO_UTF16_H -#define SIMDUTF_LATIN1_TO_UTF16_H - -namespace simdutf { -namespace scalar { -namespace { -namespace latin1_to_utf16 { - -template -inline size_t convert(const char* buf, size_t len, char16_t* utf16_output) { - const uint8_t* data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{ utf16_output }; - - while (pos < len) { - uint16_t word = uint16_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point - *utf16_output++ = char16_t(match_system(big_endian) ? word : utf16::swap_bytes(word)); - pos++; - } - - return utf16_output - start; -} - -template -inline result convert_with_errors(const char* buf, size_t len, char16_t* utf16_output) { - const uint8_t* data = reinterpret_cast(buf); - size_t pos = 0; - char16_t* start{ utf16_output }; - - while (pos < len) { - uint16_t word = uint16_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point - *utf16_output++ = char16_t(match_system(big_endian) ? word : utf16::swap_bytes(word)); - pos++; - } - - return result(error_code::SUCCESS, utf16_output - start); -} - -} // latin1_to_utf16 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/latin1_to_utf16/latin1_to_utf16.h */ -/* begin file src/scalar/latin1_to_utf32/latin1_to_utf32.h */ -#ifndef SIMDUTF_LATIN1_TO_UTF32_H -#define SIMDUTF_LATIN1_TO_UTF32_H - -namespace simdutf { -namespace scalar { -namespace { -namespace latin1_to_utf32 { - - -inline size_t convert(const char *buf, size_t len, char32_t *utf32_output) { - const unsigned char *data = reinterpret_cast(buf); - char32_t* start{utf32_output}; - for (size_t i = 0; i < len; i++) { - *utf32_output++ = (char32_t)data[i]; - } - return utf32_output - start; -} - -} // latin1_to_utf32 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/latin1_to_utf32/latin1_to_utf32.h */ - -/* begin file src/scalar/utf8_to_latin1/utf8_to_latin1.h */ -#ifndef SIMDUTF_UTF8_TO_LATIN1_H -#define SIMDUTF_UTF8_TO_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8_to_latin1 { - -inline size_t convert(const char* buf, size_t len, char* latin_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{latin_output}; - - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000 .... etc - if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII - size_t final_pos = pos + 16; - while(pos < final_pos) { - *latin_output++ = char(buf[pos]); - pos++; - } - continue; - } - } - - // suppose it is not an all ASCII byte sequence - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *latin_output++ = char(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { // the first three bits indicate: - // We have a two-byte UTF-8 - if(pos + 1 >= len) { - return 0; - } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } // checks if the next byte is a valid continuation byte in UTF-8. A valid continuation byte starts with 10. - // range check - - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); // assembles the Unicode code point from the two bytes. It does this by discarding the leading 110 and 10 bits from the two bytes, shifting the remaining bits of the first byte, and then combining the results with a bitwise OR operation. - if (code_point < 0x80 || 0xFF < code_point) { - return 0; // We only care about the range 129-255 which is Non-ASCII latin1 characters. A code_point beneath 0x80 is invalid as it's already covered by bytes whose leading bit is zero. - } - *latin_output++ = char(code_point); - pos += 2; - } else { - return 0; - } - } - return latin_output - start; -} - -inline result convert_with_errors(const char* buf, size_t len, char* latin_output) { - const uint8_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{latin_output}; - - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000...etc - if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII - size_t final_pos = pos + 16; - while(pos < final_pos) { - *latin_output++ = char(buf[pos]); - pos++; - } - continue; - } - } - // suppose it is not an all ASCII byte sequence - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *latin_output++ = char(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { // the first three bits indicate: - // We have a two-byte UTF-8 - if(pos + 1 >= len) { - return result(error_code::TOO_SHORT, pos); } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { - return result(error_code::TOO_SHORT, pos); } // checks if the next byte is a valid continuation byte in UTF-8. A valid continuation byte starts with 10. - // range check - - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); // assembles the Unicode code point from the two bytes. It does this by discarding the leading 110 and 10 bits from the two bytes, shifting the remaining bits of the first byte, and then combining the results with a bitwise OR operation. - if (code_point < 0x80) { - return result(error_code::OVERLONG, pos); - } - if (0xFF < code_point) { - return result(error_code::TOO_LARGE, pos); - } // We only care about the range 129-255 which is Non-ASCII latin1 characters - *latin_output++ = char(code_point); - pos += 2; - } else if ((leading_byte & 0b11110000) == 0b11100000) { - // We have a three-byte UTF-8 - return result(error_code::TOO_LARGE, pos); - } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 - // we have a 4-byte UTF-8 word. - return result(error_code::TOO_LARGE, pos); - } else { - // we either have too many continuation bytes or an invalid leading byte - if ((leading_byte & 0b11000000) == 0b10000000) { - return result(error_code::TOO_LONG, pos); - } - - return result(error_code::HEADER_BITS, pos); - - } - } - return result(error_code::SUCCESS, latin_output - start); -} - - -inline result rewind_and_convert_with_errors(size_t prior_bytes, const char* buf, size_t len, char* latin1_output) { - size_t extra_len{0}; - // We potentially need to go back in time and find a leading byte. - // In theory '3' would be sufficient, but sometimes the error can go back quite far. - size_t how_far_back = prior_bytes; - // size_t how_far_back = 3; // 3 bytes in the past + current position - // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; } - bool found_leading_bytes{false}; - // important: it is i <= how_far_back and not 'i < how_far_back'. - for(size_t i = 0; i <= how_far_back; i++) { - unsigned char byte = buf[0-i]; - found_leading_bytes = ((byte & 0b11000000) != 0b10000000); - if(found_leading_bytes) { - buf -= i; - extra_len = i; - break; - } - } - // - // It is possible for this function to return a negative count in its result. - // C++ Standard Section 18.1 defines size_t is in which is described in C Standard as . - // C Standard Section 4.1.5 defines size_t as an unsigned integral type of the result of the sizeof operator - // - // An unsigned type will simply wrap round arithmetically (well defined). - // - if(!found_leading_bytes) { - // If how_far_back == 3, we may have four consecutive continuation bytes!!! - // [....] [continuation] [continuation] [continuation] | [buf is continuation] - // Or we possibly have a stream that does not start with a leading byte. - return result(error_code::TOO_LONG, 0-how_far_back); - } - result res = convert_with_errors(buf, len + extra_len, latin1_output); - if (res.error) { - res.count -= extra_len; - } - return res; -} - - -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8_to_latin1/utf8_to_latin1.h */ -/* begin file src/scalar/utf16_to_latin1/utf16_to_latin1.h */ -#ifndef SIMDUTF_UTF16_TO_LATIN1_H -#define SIMDUTF_UTF16_TO_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16_to_latin1 { - -#include // for std::memcpy - -template -inline size_t convert(const char16_t* buf, size_t len, char* latin_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - std::vector temp_output(len); - char* current_write = temp_output.data(); - uint16_t word = 0; - uint16_t too_large = 0; - - while (pos < len) { - word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - too_large |= word; - *current_write++ = char(word & 0xFF); - pos++; - } - if((too_large & 0xFF00) != 0) { return 0; } - - // Only copy to latin_output if there were no errors - std::memcpy(latin_output, temp_output.data(), len); - - return current_write - temp_output.data(); -} - -template -inline result convert_with_errors(const char16_t* buf, size_t len, char* latin_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{latin_output}; - uint16_t word; - - while (pos < len) { - if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that they are Latin1 - uint64_t v1, v2, v3, v4; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - ::memcpy(&v2, data + pos + 4, sizeof(uint64_t)); - ::memcpy(&v3, data + pos + 8, sizeof(uint64_t)); - ::memcpy(&v4, data + pos + 12, sizeof(uint64_t)); - - if (!match_system(big_endian)) { v1 = (v1 >> 8) | (v1 << (64 - 8)); } - if (!match_system(big_endian)) { v2 = (v2 >> 8) | (v2 << (64 - 8)); } - if (!match_system(big_endian)) { v3 = (v3 >> 8) | (v3 << (64 - 8)); } - if (!match_system(big_endian)) { v4 = (v1 >> 8) | (v4 << (64 - 8)); } - - if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) { - size_t final_pos = pos + 16; - while(pos < final_pos) { - *latin_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(data[pos])) : char(data[pos]); - pos++; - } - continue; - } - } - word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - if((word & 0xFF00 ) == 0) { - *latin_output++ = char(word & 0xFF); - pos++; - } else { return result(error_code::TOO_LARGE, pos); } - } - return result(error_code::SUCCESS,latin_output - start); -} - - -} // utf16_to_latin1 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16_to_latin1/utf16_to_latin1.h */ -/* begin file src/scalar/utf32_to_latin1/utf32_to_latin1.h */ -#ifndef SIMDUTF_UTF32_TO_LATIN1_H -#define SIMDUTF_UTF32_TO_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32_to_latin1 { - -inline size_t convert(const char32_t *buf, size_t len, char *latin1_output) { - const uint32_t *data = reinterpret_cast(buf); - char* start = latin1_output; - uint32_t utf32_char; - size_t pos = 0; - uint32_t too_large = 0; - - while (pos < len) { - utf32_char = (uint32_t)data[pos]; - too_large |= utf32_char; - *latin1_output++ = (char)(utf32_char & 0xFF); - pos++; - } - if((too_large & 0xFFFFFF00) != 0) { return 0; } - return latin1_output - start; -} - -inline result convert_with_errors(const char32_t *buf, size_t len, char *latin1_output) { - const uint32_t *data = reinterpret_cast(buf); - char* start{latin1_output}; - size_t pos = 0; - while (pos < len) { - if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are Latin1 - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0xFFFFFF00FFFFFF00) == 0) { - *latin1_output++ = char(buf[pos]); - *latin1_output++ = char(buf[pos+1]); - pos += 2; - continue; - } - } - uint32_t utf32_char = data[pos]; - if ((utf32_char & 0xFFFFFF00) == 0) { // Check if the character can be represented in Latin-1 - *latin1_output++ = (char)(utf32_char & 0xFF); - pos++; - } else { return result(error_code::TOO_LARGE, pos); }; - } - return result(error_code::SUCCESS, latin1_output - start); -} - -} // utf32_to_latin1 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32_to_latin1/utf32_to_latin1.h */ - -/* begin file src/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */ -#ifndef SIMDUTF_VALID_UTF8_TO_LATIN1_H -#define SIMDUTF_VALID_UTF8_TO_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf8_to_latin1 { - -inline size_t convert_valid(const char* buf, size_t len, char* latin_output) { - const uint8_t *data = reinterpret_cast(buf); - - size_t pos = 0; - char* start{latin_output}; - - while (pos < len) { - // try to convert the next block of 16 ASCII bytes - if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii - uint64_t v1; - ::memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000, so it makes sense to concatenate everything - if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII - size_t final_pos = pos + 16; - while(pos < final_pos) { - *latin_output++ = char(buf[pos]); - pos++; - } - continue; - } - } - - // suppose it is not an all ASCII byte sequence - uint8_t leading_byte = data[pos]; // leading byte - if (leading_byte < 0b10000000) { - // converting one ASCII byte !!! - *latin_output++ = char(leading_byte); - pos++; - } else if ((leading_byte & 0b11100000) == 0b11000000) { // the first three bits indicate: - // We have a two-byte UTF-8 - if(pos + 1 >= len) { break; } // minimal bound checking - if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } // checks if the next byte is a valid continuation byte in UTF-8. A valid continuation byte starts with 10. - // range check - - uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); // assembles the Unicode code point from the two bytes. It does this by discarding the leading 110 and 10 bits from the two bytes, shifting the remaining bits of the first byte, and then combining the results with a bitwise OR operation. - *latin_output++ = char(code_point); - pos += 2; - } else { - // we may have a continuation but we do not do error checking - return 0; - } - } - return latin_output - start; -} - -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */ -/* begin file src/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */ -#ifndef SIMDUTF_VALID_UTF16_TO_LATIN1_H -#define SIMDUTF_VALID_UTF16_TO_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf16_to_latin1 { - -template -inline size_t convert_valid(const char16_t* buf, size_t len, char* latin_output) { - const uint16_t *data = reinterpret_cast(buf); - size_t pos = 0; - char* start{latin_output}; - uint16_t word = 0; - - while (pos < len) { - word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos]; - *latin_output++ = char(word); - pos++; - } - - return latin_output - start; -} - -} // utf16_to_latin1 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */ -/* begin file src/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */ -#ifndef SIMDUTF_VALID_UTF32_TO_LATIN1_H -#define SIMDUTF_VALID_UTF32_TO_LATIN1_H - -namespace simdutf { -namespace scalar { -namespace { -namespace utf32_to_latin1 { - -inline size_t convert_valid(const char32_t *buf, size_t len, char *latin1_output) { - const uint32_t *data = reinterpret_cast(buf); - char* start = latin1_output; - uint32_t utf32_char; - size_t pos = 0; - - while (pos < len) { - utf32_char = (uint32_t)data[pos]; - - if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are Latin1 - uint64_t v; - ::memcpy(&v, data + pos, sizeof(uint64_t)); - if ((v & 0xFFFFFF00FFFFFF00) == 0) { - *latin1_output++ = char(buf[pos]); - *latin1_output++ = char(buf[pos+1]); - pos += 2; - continue; - } - } - *latin1_output++ = (char)(utf32_char & 0xFF); - pos++; - - } - return latin1_output - start; -} - - -} // utf32_to_latin1 namespace -} // unnamed namespace -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */ - - - -SIMDUTF_PUSH_DISABLE_WARNINGS -SIMDUTF_DISABLE_UNDESIRED_WARNINGS - - -#if SIMDUTF_IMPLEMENTATION_ARM64 -/* begin file src/arm64/implementation.cpp */ -/* begin file src/simdutf/arm64/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "arm64" -// #define SIMDUTF_IMPLEMENTATION arm64 -/* end file src/simdutf/arm64/begin.h */ -namespace simdutf { -namespace arm64 { -namespace { -#ifndef SIMDUTF_ARM64_H -#error "arm64.h must be included" -#endif -using namespace simd; - -simdutf_really_inline bool is_ascii(const simd8x64& input) { - simd8 bits = input.reduce_or(); - return bits.max_val() < 0b10000000u; -} - -simdutf_unused simdutf_really_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1 >= uint8_t(0b11000000u); - simd8 is_third_byte = prev2 >= uint8_t(0b11100000u); - simd8 is_fourth_byte = prev3 >= uint8_t(0b11110000u); - // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well. - // This will work fine because we only have to report errors for cases with 0-1 lead bytes. - // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is - // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character. - // The error will be detected there. - return is_second_byte ^ is_third_byte ^ is_fourth_byte; -} - -simdutf_really_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2 >= uint8_t(0b11100000u); - simd8 is_fourth_byte = prev3 >= uint8_t(0b11110000u); - return is_third_byte ^ is_fourth_byte; -} - -// common functions for utf8 conversions -simdutf_really_inline uint16x4_t convert_utf8_3_byte_to_utf16(uint8x16_t in) { - // Low half contains 10cccccc|1110aaaa - // High half contains 10bbbbbb|10bbbbbb -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint8x16_t sh = simdutf_make_uint8x16_t(0, 2, 3, 5, 6, 8, 9, 11, 1, 1, 4, 4, 7, 7, 10, 10); -#else - const uint8x16_t sh = {0, 2, 3, 5, 6, 8, 9, 11, 1, 1, 4, 4, 7, 7, 10, 10}; -#endif - uint8x16_t perm = vqtbl1q_u8(in, sh); - // Split into half vectors. - // 10cccccc|1110aaaa - uint8x8_t perm_low = vget_low_u8(perm); // no-op - // 10bbbbbb|10bbbbbb - uint8x8_t perm_high = vget_high_u8(perm); - // xxxxxxxx 10bbbbbb - uint16x4_t mid = vreinterpret_u16_u8(perm_high); // no-op - // xxxxxxxx 1110aaaa - uint16x4_t high = vreinterpret_u16_u8(perm_low); // no-op - // Assemble with shift left insert. - // xxxxxxaa aabbbbbb - uint16x4_t mid_high = vsli_n_u16(mid, high, 6); - // (perm_low << 8) | (perm_low >> 8) - // xxxxxxxx 10cccccc - uint16x4_t low = vreinterpret_u16_u8(vrev16_u8(perm_low)); - // Shift left insert into the low bits - // aaaabbbb bbcccccc - uint16x4_t composed = vsli_n_u16(low, mid_high, 6); - return composed; -} - -simdutf_really_inline uint16x8_t convert_utf8_2_byte_to_utf16(uint8x16_t in) { - // Converts 6 2 byte UTF-8 characters to 6 UTF-16 characters. - // Technically this calculates 8, but 6 does better and happens more often - // (The languages which use these codepoints use ASCII spaces so 8 would need to be - // in the middle of a very long word). - - // 10bbbbbb 110aaaaa - uint16x8_t upper = vreinterpretq_u16_u8(in); - // (in << 8) | (in >> 8) - // 110aaaaa 10bbbbbb - uint16x8_t lower = vreinterpretq_u16_u8(vrev16q_u8(in)); - // 00000000 000aaaaa - uint16x8_t upper_masked = vandq_u16(upper, vmovq_n_u16(0x1F)); - // Assemble with shift left insert. - // 00000aaa aabbbbbb - uint16x8_t composed = vsliq_n_u16(lower, upper_masked, 6); - return composed; -} - -simdutf_really_inline uint16x8_t convert_utf8_1_to_2_byte_to_utf16(uint8x16_t in, size_t shufutf8_idx) { - // Converts 6 1-2 byte UTF-8 characters to 6 UTF-16 characters. - // This is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. - uint8x16_t sh = vld1q_u8(reinterpret_cast(simdutf::tables::utf8_to_utf16::shufutf8[shufutf8_idx])); - // Shuffle - // 1 byte: 00000000 0bbbbbbb - // 2 byte: 110aaaaa 10bbbbbb - uint16x8_t perm = vreinterpretq_u16_u8(vqtbl1q_u8(in, sh)); - // Mask - // 1 byte: 00000000 0bbbbbbb - // 2 byte: 00000000 00bbbbbb - uint16x8_t ascii = vandq_u16(perm, vmovq_n_u16(0x7f)); // 6 or 7 bits - // 1 byte: 00000000 00000000 - // 2 byte: 000aaaaa 00000000 - uint16x8_t highbyte = vandq_u16(perm, vmovq_n_u16(0x1f00)); // 5 bits - // Combine with a shift right accumulate - // 1 byte: 00000000 0bbbbbbb - // 2 byte: 00000aaa aabbbbbb - uint16x8_t composed = vsraq_n_u16(ascii, highbyte, 2); - return composed; -} - -/* begin file src/arm64/arm_detect_encodings.cpp */ -template -// len is known to be a multiple of 2 when this is called -int arm_detect_encodings(const char * buf, size_t len) { - const char* start = buf; - const char* end = buf + len; - - bool is_utf8 = true; - bool is_utf16 = true; - bool is_utf32 = true; - - int out = 0; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - - uint32x4_t currentmax = vmovq_n_u32(0x0); - - checker check{}; - - while(buf + 64 <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - uint16x8_t secondin = vld1q_u16(reinterpret_cast(buf) + simd16::SIZE / sizeof(char16_t)); - uint16x8_t thirdin = vld1q_u16(reinterpret_cast(buf) + 2*simd16::SIZE / sizeof(char16_t)); - uint16x8_t fourthin = vld1q_u16(reinterpret_cast(buf) + 3*simd16::SIZE / sizeof(char16_t)); - - const auto u0 = simd16(in); - const auto u1 = simd16(secondin); - const auto u2 = simd16(thirdin); - const auto u3 = simd16(fourthin); - - const auto v0 = u0.shr<8>(); - const auto v1 = u1.shr<8>(); - const auto v2 = u2.shr<8>(); - const auto v3 = u3.shr<8>(); - - const auto in16 = simd16::pack(v0, v1); - const auto nextin16 = simd16::pack(v2, v3); - - const uint64_t surrogates_wordmask0 = ((in16 & v_f8) == v_d8).to_bitmask64(); - const uint64_t surrogates_wordmask1 = ((nextin16 & v_f8) == v_d8).to_bitmask64(); - - // Check for surrogates - if (surrogates_wordmask0 != 0 || surrogates_wordmask1 != 0) { - // Cannot be UTF8 - is_utf8 = false; - // Can still be either UTF-16LE or UTF-32 depending on the positions of the surrogates - // To be valid UTF-32, a surrogate cannot be in the two most significant bytes of any 32-bit word. - // On the other hand, to be valid UTF-16LE, at least one surrogate must be in the two most significant - // bytes of a 32-bit word since they always come in pairs in UTF-16LE. - // Note that we always proceed in multiple of 4 before this point so there is no offset in 32-bit code units. - - if (((surrogates_wordmask0 | surrogates_wordmask1) & 0xf0f0f0f0f0f0f0f0) != 0) { - is_utf32 = false; - // Code from arm_validate_utf16le.cpp - // Not efficient, we do not process surrogates_wordmask1 - const char16_t * input = reinterpret_cast(buf); - const char16_t* end16 = reinterpret_cast(start) + len/2; - - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - const uint64_t V0 = ~surrogates_wordmask0; - - const auto vH0 = ((in16 & v_fc) == v_dc); - const uint64_t H0 = vH0.to_bitmask64(); - - const uint64_t L0 = ~H0 & surrogates_wordmask0; - - const uint64_t a0 = L0 & (H0 >> 4); - - const uint64_t b0 = a0 << 4; - - const uint64_t c0 = V0 | a0 | b0; - if (c0 == ~0ull) { - input += 16; - } else if (c0 == 0xfffffffffffffffull) { - input += 15; - } else { - is_utf16 = false; - break; - } - - while (input + 16 < end16) { - const auto in0 = simd16(input); - const auto in1 = simd16(input + simd16::SIZE / sizeof(char16_t)); - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - const simd8 in_16 = simd16::pack(t0, t1); - - const uint64_t surrogates_wordmask = ((in_16 & v_f8) == v_d8).to_bitmask64(); - if(surrogates_wordmask == 0) { - input += 16; - } else { - const uint64_t V = ~surrogates_wordmask; - - const auto vH = ((in_16 & v_fc) == v_dc); - const uint64_t H = vH.to_bitmask64(); - - const uint64_t L = ~H & surrogates_wordmask; - - const uint64_t a = L & (H >> 4); - - const uint64_t b = a << 4; - - const uint64_t c = V | a | b; - if (c == ~0ull) { - input += 16; - } else if (c == 0xfffffffffffffffull) { - input += 15; - } else { - is_utf16 = false; - break; - } - } - } - } else { - is_utf16 = false; - // Check for UTF-32 - if (len % 4 == 0) { - const char32_t * input = reinterpret_cast(buf); - const char32_t* end32 = reinterpret_cast(start) + len/4; - - // Must start checking for surrogates - uint32x4_t currentoffsetmax = vmovq_n_u32(0x0); - const uint32x4_t offset = vmovq_n_u32(0xffff2000); - const uint32x4_t standardoffsetmax = vmovq_n_u32(0xfffff7ff); - - const uint32x4_t in32 = vreinterpretq_u32_u16(in); - const uint32x4_t secondin32 = vreinterpretq_u32_u16(secondin); - const uint32x4_t thirdin32 = vreinterpretq_u32_u16(thirdin); - const uint32x4_t fourthin32 = vreinterpretq_u32_u16(fourthin); - - currentmax = vmaxq_u32(in32,currentmax); - currentmax = vmaxq_u32(secondin32,currentmax); - currentmax = vmaxq_u32(thirdin32,currentmax); - currentmax = vmaxq_u32(fourthin32,currentmax); - - currentoffsetmax = vmaxq_u32(vaddq_u32(in32, offset), currentoffsetmax); - currentoffsetmax = vmaxq_u32(vaddq_u32(secondin32, offset), currentoffsetmax); - currentoffsetmax = vmaxq_u32(vaddq_u32(thirdin32, offset), currentoffsetmax); - currentoffsetmax = vmaxq_u32(vaddq_u32(fourthin32, offset), currentoffsetmax); - - while (input + 4 < end32) { - const uint32x4_t in_32 = vld1q_u32(reinterpret_cast(input)); - currentmax = vmaxq_u32(in_32,currentmax); - currentoffsetmax = vmaxq_u32(vaddq_u32(in_32, offset), currentoffsetmax); - input += 4; - } - - uint32x4_t forbidden_words = veorq_u32(vmaxq_u32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(vmaxvq_u32(forbidden_words) != 0) { - is_utf32 = false; - } - } else { - is_utf32 = false; - } - } - break; - } - // If no surrogate, validate under other encodings as well - - // UTF-32 validation - currentmax = vmaxq_u32(vreinterpretq_u32_u16(in),currentmax); - currentmax = vmaxq_u32(vreinterpretq_u32_u16(secondin),currentmax); - currentmax = vmaxq_u32(vreinterpretq_u32_u16(thirdin),currentmax); - currentmax = vmaxq_u32(vreinterpretq_u32_u16(fourthin),currentmax); - - // UTF-8 validation - // Relies on ../generic/utf8_validation/utf8_lookup4_algorithm.h - simd::simd8x64 in8(vreinterpretq_u8_u16(in), vreinterpretq_u8_u16(secondin), vreinterpretq_u8_u16(thirdin), vreinterpretq_u8_u16(fourthin)); - check.check_next_input(in8); - - buf += 64; - } - - // Check which encodings are possible - - if (is_utf8) { - if (static_cast(buf - start) != len) { - uint8_t block[64]{}; - std::memset(block, 0x20, 64); - std::memcpy(block, buf, len - (buf - start)); - simd::simd8x64 in(block); - check.check_next_input(in); - } - if (!check.errors()) { - out |= simdutf::encoding_type::UTF8; - } - } - - if (is_utf16 && scalar::utf16::validate(reinterpret_cast(buf), (len - (buf - start))/2)) { - out |= simdutf::encoding_type::UTF16_LE; - } - - if (is_utf32 && (len % 4 == 0)) { - const uint32x4_t standardmax = vmovq_n_u32(0x10ffff); - uint32x4_t is_zero = veorq_u32(vmaxq_u32(currentmax, standardmax), standardmax); - if (vmaxvq_u32(is_zero) == 0 && scalar::utf32::validate(reinterpret_cast(buf), (len - (buf - start))/4)) { - out |= simdutf::encoding_type::UTF32_LE; - } - } - - return out; -} -/* end file src/arm64/arm_detect_encodings.cpp */ - -/* begin file src/arm64/arm_validate_utf16.cpp */ -template -const char16_t* arm_validate_utf16(const char16_t* input, size_t size) { - const char16_t* end = input + size; - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - while (input + 16 < end) { - // 0. Load data: since the validation takes into account only higher - // byte of each word, we compress the two vectors into one which - // consists only the higher bytes. - auto in0 = simd16(input); - auto in1 = simd16(input + simd16::SIZE / sizeof(char16_t)); - if (!match_system(big_endian)) { - in0 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in0))); - in1 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in1))); - } - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - const simd8 in = simd16::pack(t0, t1); - // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy). - const uint64_t surrogates_wordmask = ((in & v_f8) == v_d8).to_bitmask64(); - if(surrogates_wordmask == 0) { - input += 16; - } else { - // 2. We have some surrogates that have to be distinguished: - // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF) - // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF) - // - // Fact: high surrogate has 11th bit set (3rd bit in the higher word) - - // V - non-surrogate code units - // V = not surrogates_wordmask - const uint64_t V = ~surrogates_wordmask; - - // H - word-mask for high surrogates: the six highest bits are 0b1101'11 - const auto vH = ((in & v_fc) == v_dc); - const uint64_t H = vH.to_bitmask64(); - - // L - word mask for low surrogates - // L = not H and surrogates_wordmask - const uint64_t L = ~H & surrogates_wordmask; - - const uint64_t a = L & (H >> 4); // A low surrogate must be followed by high one. - // (A low surrogate placed in the 7th register's word - // is an exception we handle.) - const uint64_t b = a << 4; // Just mark that the opposite fact is hold, - // thanks to that we have only two masks for valid case. - const uint64_t c = V | a | b; // Combine all the masks into the final one. - if (c == ~0ull) { - // The whole input register contains valid UTF-16, i.e., - // either single code units or proper surrogate pairs. - input += 16; - } else if (c == 0xfffffffffffffffull) { - // The 15 lower code units of the input register contains valid UTF-16. - // The 15th word may be either a low or high surrogate. It the next - // iteration we 1) check if the low surrogate is followed by a high - // one, 2) reject sole high surrogate. - input += 15; - } else { - return nullptr; - } - } - } - return input; -} - - -template -const result arm_validate_utf16_with_errors(const char16_t* input, size_t size) { - const char16_t* start = input; - const char16_t* end = input + size; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - while (input + 16 < end) { - // 0. Load data: since the validation takes into account only higher - // byte of each word, we compress the two vectors into one which - // consists only the higher bytes. - auto in0 = simd16(input); - auto in1 = simd16(input + simd16::SIZE / sizeof(char16_t)); - - if (!match_system(big_endian)) { - in0 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in0))); - in1 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in1))); - } - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - const simd8 in = simd16::pack(t0, t1); - // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy). - const uint64_t surrogates_wordmask = ((in & v_f8) == v_d8).to_bitmask64(); - if(surrogates_wordmask == 0) { - input += 16; - } else { - // 2. We have some surrogates that have to be distinguished: - // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF) - // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF) - // - // Fact: high surrogate has 11th bit set (3rd bit in the higher word) - - // V - non-surrogate code units - // V = not surrogates_wordmask - const uint64_t V = ~surrogates_wordmask; - - // H - word-mask for high surrogates: the six highest bits are 0b1101'11 - const auto vH = ((in & v_fc) == v_dc); - const uint64_t H = vH.to_bitmask64(); - - // L - word mask for low surrogates - // L = not H and surrogates_wordmask - const uint64_t L = ~H & surrogates_wordmask; - - const uint64_t a = L & (H >> 4); // A low surrogate must be followed by high one. - // (A low surrogate placed in the 7th register's word - // is an exception we handle.) - const uint64_t b = a << 4; // Just mark that the opposite fact is hold, - // thanks to that we have only two masks for valid case. - const uint64_t c = V | a | b; // Combine all the masks into the final one. - if (c == ~0ull) { - // The whole input register contains valid UTF-16, i.e., - // either single code units or proper surrogate pairs. - input += 16; - } else if (c == 0xfffffffffffffffull) { - // The 15 lower code units of the input register contains valid UTF-16. - // The 15th word may be either a low or high surrogate. It the next - // iteration we 1) check if the low surrogate is followed by a high - // one, 2) reject sole high surrogate. - input += 15; - } else { - return result(error_code::SURROGATE, input - start); - } - } - } - return result(error_code::SUCCESS, input - start); -} -/* end file src/arm64/arm_validate_utf16.cpp */ -/* begin file src/arm64/arm_validate_utf32le.cpp */ - -const char32_t* arm_validate_utf32le(const char32_t* input, size_t size) { - const char32_t* end = input + size; - - const uint32x4_t standardmax = vmovq_n_u32(0x10ffff); - const uint32x4_t offset = vmovq_n_u32(0xffff2000); - const uint32x4_t standardoffsetmax = vmovq_n_u32(0xfffff7ff); - uint32x4_t currentmax = vmovq_n_u32(0x0); - uint32x4_t currentoffsetmax = vmovq_n_u32(0x0); - - while (input + 4 < end) { - const uint32x4_t in = vld1q_u32(reinterpret_cast(input)); - currentmax = vmaxq_u32(in,currentmax); - currentoffsetmax = vmaxq_u32(vaddq_u32(in, offset), currentoffsetmax); - input += 4; - } - - uint32x4_t is_zero = veorq_u32(vmaxq_u32(currentmax, standardmax), standardmax); - if(vmaxvq_u32(is_zero) != 0) { - return nullptr; - } - - is_zero = veorq_u32(vmaxq_u32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(vmaxvq_u32(is_zero) != 0) { - return nullptr; - } - - return input; -} - - -const result arm_validate_utf32le_with_errors(const char32_t* input, size_t size) { - const char32_t* start = input; - const char32_t* end = input + size; - - const uint32x4_t standardmax = vmovq_n_u32(0x10ffff); - const uint32x4_t offset = vmovq_n_u32(0xffff2000); - const uint32x4_t standardoffsetmax = vmovq_n_u32(0xfffff7ff); - uint32x4_t currentmax = vmovq_n_u32(0x0); - uint32x4_t currentoffsetmax = vmovq_n_u32(0x0); - - while (input + 4 < end) { - const uint32x4_t in = vld1q_u32(reinterpret_cast(input)); - currentmax = vmaxq_u32(in,currentmax); - currentoffsetmax = vmaxq_u32(vaddq_u32(in, offset), currentoffsetmax); - - uint32x4_t is_zero = veorq_u32(vmaxq_u32(currentmax, standardmax), standardmax); - if(vmaxvq_u32(is_zero) != 0) { - return result(error_code::TOO_LARGE, input - start); - } - - is_zero = veorq_u32(vmaxq_u32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(vmaxvq_u32(is_zero) != 0) { - return result(error_code::SURROGATE, input - start); - } - - input += 4; - } - - return result(error_code::SUCCESS, input - start); -} -/* end file src/arm64/arm_validate_utf32le.cpp */ - -/* begin file src/arm64/arm_convert_latin1_to_utf8.cpp */ -/* - Returns a pair: the first unprocessed byte from buf and utf8_output - A scalar routing should carry on the conversion of the tail. -*/ -std::pair -arm_convert_latin1_to_utf8(const char *latin1_input, size_t len, - char *utf8_out) { - uint8_t *utf8_output = reinterpret_cast(utf8_out); - const char *end = latin1_input + len; - const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080); - // We always write 16 bytes, of which more than the first 8 bytes - // are valid. A safety margin of 8 is more than sufficient. - while (latin1_input + 16 + 8 <= end) { - uint8x16_t in8 = vld1q_u8(reinterpret_cast(latin1_input)); - if (vmaxvq_u8(in8) <= 0x7F) { // ASCII fast path!!!! - vst1q_u8(utf8_output, in8); - utf8_output += 16; - latin1_input += 16; - continue; - } - - // We just fallback on UTF-16 code. This could be optimized/simplified - // further. - uint16x8_t in16 = vmovl_u8(vget_low_u8(in8)); - // 1. prepare 2-byte values - // input 8-bit word : [aabb|bbbb] x 8 - // expected output : [1100|00aa|10bb|bbbb] x 8 - const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00); - const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f); - - // t0 = [0000|00aa|bbbb|bb00] - const uint16x8_t t0 = vshlq_n_u16(in16, 2); - // t1 = [0000|00aa|0000|0000] - const uint16x8_t t1 = vandq_u16(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const uint16x8_t t2 = vandq_u16(in16, v_003f); - // t3 = [0000|00aa|00bb|bbbb] - const uint16x8_t t3 = vorrq_u16(t1, t2); - // t4 = [1100|00aa|10bb|bbbb] - const uint16x8_t t4 = vorrq_u16(t3, v_c080); - // 2. merge ASCII and 2-byte codewords - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(in16, v_007f); - const uint8x16_t utf8_unpacked = - vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, in16, t4)); - // 3. prepare bitmask for 8-bit lookup -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004, 0x0010, 0x0040, - 0x0002, 0x0008, 0x0020, 0x0080); -#else - const uint16x8_t mask = {0x0001, 0x0004, 0x0010, 0x0040, - 0x0002, 0x0008, 0x0020, 0x0080}; -#endif - uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask)); - // 4. pack the bytes - const uint8_t *row = - &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const uint8x16_t shuffle = vld1q_u8(row + 1); - const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle); - - // 5. store bytes - vst1q_u8(utf8_output, utf8_packed); - // 6. adjust pointers - latin1_input += 8; - utf8_output += row[0]; - - } // while - - return std::make_pair(latin1_input, reinterpret_cast(utf8_output)); -} -/* end file src/arm64/arm_convert_latin1_to_utf8.cpp */ -/* begin file src/arm64/arm_convert_latin1_to_utf16.cpp */ -template -std::pair arm_convert_latin1_to_utf16(const char* buf, size_t len, char16_t* utf16_output) { - const char* end = buf + len; - - while (buf + 16 <= end) { - uint8x16_t in8 = vld1q_u8(reinterpret_cast(buf)); - uint16x8_t inlow = vmovl_u8(vget_low_u8(in8)); - if (!match_system(big_endian)) { inlow = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(inlow))); } - vst1q_u16(reinterpret_cast(utf16_output), inlow); - uint16x8_t inhigh = vmovl_u8(vget_high_u8(in8)); - if (!match_system(big_endian)) { inhigh = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(inhigh))); } - vst1q_u16(reinterpret_cast(utf16_output+8), inhigh); - utf16_output += 16; - buf += 16; - } - - return std::make_pair(buf, utf16_output); -} -/* end file src/arm64/arm_convert_latin1_to_utf16.cpp */ -/* begin file src/arm64/arm_convert_latin1_to_utf32.cpp */ -std::pair arm_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { - const char* end = buf + len; - - while (buf + 16 <= end) { - uint8x16_t in8 = vld1q_u8(reinterpret_cast(buf)); - uint16x8_t in8low = vmovl_u8(vget_low_u8(in8)); - uint32x4_t in16lowlow = vmovl_u16(vget_low_u16(in8low)); - uint32x4_t in16lowhigh = vmovl_u16(vget_high_u16(in8low)); - uint16x8_t in8high = vmovl_u8(vget_high_u8(in8)); - uint32x4_t in8highlow = vmovl_u16(vget_low_u16(in8high)); - uint32x4_t in8highhigh = vmovl_u16(vget_high_u16(in8high)); - vst1q_u32(reinterpret_cast(utf32_output), in16lowlow); - vst1q_u32(reinterpret_cast(utf32_output+4), in16lowhigh); - vst1q_u32(reinterpret_cast(utf32_output+8), in8highlow); - vst1q_u32(reinterpret_cast(utf32_output+12), in8highhigh); - - utf32_output += 16; - buf += 16; - } - - return std::make_pair(buf, utf32_output); -} -/* end file src/arm64/arm_convert_latin1_to_utf32.cpp */ - -/* begin file src/arm64/arm_convert_utf8_to_utf16.cpp */ -// Convert up to 16 bytes from utf8 to utf16 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 16, usually 12). -template -size_t convert_masked_utf8_to_utf16(const char *input, - uint64_t utf8_end_of_code_point_mask, - char16_t *&utf16_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - uint8x16_t in = vld1q_u8(reinterpret_cast(input)); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - - // We first try a few fast paths. - // The obvious first test is ASCII, which actually consumes the full 16. - if((utf8_end_of_code_point_mask & 0xFFFF) == 0xffff) { - // We process in chunks of 16 bytes - // The routine in simd.h is reused. - simd8 temp{vreinterpretq_s8_u8(in)}; - temp.store_ascii_as_utf16(utf16_output); - utf16_output += 16; // We wrote 16 16-bit characters. - return 16; // We consumed 16 bytes. - } - - // 3 byte sequences are the next most common, as seen in CJK, which has long sequences - // of these. - if (input_utf8_end_of_code_point_mask == 0x924) { - // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte UTF-16 code units. - uint16x4_t composed = convert_utf8_3_byte_to_utf16(in); - // Byte swap if necessary - if (!match_system(big_endian)) { - composed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(composed))); - } - vst1_u16(reinterpret_cast(utf16_output), composed); - utf16_output += 4; // We wrote 4 16-bit characters. - return 12; // We consumed 12 bytes. - } - - // 2 byte sequences occur in short bursts in languages like Greek and Russian. - if ((utf8_end_of_code_point_mask & 0xFFF) == 0xaaa) { - // We want to take 6 2-byte UTF-8 code units and turn them into 6 2-byte UTF-16 code units. - uint16x8_t composed = convert_utf8_2_byte_to_utf16(in); - // Byte swap if necessary - if (!match_system(big_endian)) { - composed = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(composed))); - } - vst1q_u16(reinterpret_cast(utf16_output), composed); - - utf16_output += 6; // We wrote 6 16-bit characters. - return 12; // We consumed 12 bytes. - } - - /// We do not have a fast path available, or the fast path is unimportant, so we fallback. - const uint8_t idx = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - - const uint8_t consumed = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - - if (idx < 64) { - // SIX (6) input code-code units - // Convert to UTF-16 - uint16x8_t composed = convert_utf8_1_to_2_byte_to_utf16(in, idx); - // Byte swap if necessary - if (!match_system(big_endian)) { - composed = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(composed))); - } - // Store - vst1q_u16(reinterpret_cast(utf16_output), composed); - utf16_output += 6; // We wrote 6 16-bit characters. - return consumed; - } else if (idx < 145) { - // FOUR (4) input code-code units - // UTF-16 and UTF-32 use similar algorithms, but UTF-32 skips the narrowing. - uint8x16_t sh = vld1q_u8(reinterpret_cast(simdutf::tables::utf8_to_utf16::shufutf8[idx])); - // XXX: depending on the system scalar instructions might be faster. - // 1 byte: 00000000 00000000 0ccccccc - // 2 byte: 00000000 110bbbbb 10cccccc - // 3 byte: 1110aaaa 10bbbbbb 10cccccc - uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh)); - // 1 byte: 00000000 0ccccccc - // 2 byte: xx0bbbbb x0cccccc - // 3 byte: xxbbbbbb x0cccccc - uint16x4_t lowperm = vmovn_u32(perm); - // Partially mask with bic (doesn't require a temporary register unlike and) - // The shift left insert below will clear the top bits. - // 1 byte: 00000000 00000000 - // 2 byte: xx0bbbbb 00000000 - // 3 byte: xxbbbbbb 00000000 - uint16x4_t middlebyte = vbic_u16(lowperm, vmov_n_u16(uint16_t(~0xFF00))); - // ASCII - // 1 byte: 00000000 0ccccccc - // 2+byte: 00000000 00cccccc - uint16x4_t ascii = vand_u16(lowperm, vmov_n_u16(0x7F)); - // Split into narrow vectors. - // 2 byte: 00000000 00000000 - // 3 byte: 00000000 xxxxaaaa - uint16x4_t highperm = vshrn_n_u32(perm, 16); - // Shift right accumulate the middle byte - // 1 byte: 00000000 0ccccccc - // 2 byte: 00xx0bbb bbcccccc - // 3 byte: 00xxbbbb bbcccccc - uint16x4_t middlelow = vsra_n_u16(ascii, middlebyte, 2); - // Shift left and insert the top 4 bits, overwriting the garbage - // 1 byte: 00000000 0ccccccc - // 2 byte: 00000bbb bbcccccc - // 3 byte: aaaabbbb bbcccccc - uint16x4_t composed = vsli_n_u16(middlelow, highperm, 12); - // Byte swap if necessary - if (!match_system(big_endian)) { - composed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(composed))); - } - vst1_u16(reinterpret_cast(utf16_output), composed); - - utf16_output += 4; // We wrote 4 16-bit codepoints - return consumed; - } else if (idx < 209) { - // THREE (3) input code-code units - if (input_utf8_end_of_code_point_mask == 0x888) { - // We want to take 3 4-byte UTF-8 code units and turn them into 3 4-byte UTF-16 pairs. - // Generating surrogate pairs is a little tricky though, but it is easier when we - // can assume they are all pairs. - // This version does not use the LUT, but 4 byte sequences are less common and the - // overhead of the extra memory access is less important than the early branch overhead - // in shorter sequences. - - // Swap byte pairs - // 10dddddd 10cccccc|10bbbbbb 11110aaa - // 10cccccc 10dddddd|11110aaa 10bbbbbb - uint8x16_t swap = vrev16q_u8(in); - // Shift left 2 bits - // cccccc00 dddddd00 xxxxxxxx bbbbbb00 - uint32x4_t shift = vreinterpretq_u32_u8(vshlq_n_u8(swap, 2)); - // Create a magic number containing the low 2 bits of the trail surrogate and all the - // corrections needed to create the pair. - // UTF-8 4b prefix = -0x0000|0xF000 - // surrogate offset = -0x0000|0x0040 (0x10000 << 6) - // surrogate high = +0x0000|0xD800 - // surrogate low = +0xDC00|0x0000 - // ------------------------------- - // = +0xDC00|0xE7C0 - uint32x4_t magic = vmovq_n_u32(0xDC00E7C0); - // Generate unadjusted trail surrogate minus lowest 2 bits - // xxxxxxxx xxxxxxxx|11110aaa bbbbbb00 - uint32x4_t trail = vbslq_u32(vmovq_n_u32(0x0000FF00), vreinterpretq_u32_u8(swap), shift); - // Insert low 2 bits of trail surrogate to magic number for later - // 11011100 00000000 11100111 110000cc - uint16x8_t magic_with_low_2 = vreinterpretq_u16_u32(vsraq_n_u32(magic, shift, 30)); - // Generate lead surrogate - // xxxxcccc ccdddddd|xxxxxxxx xxxxxxxx - uint32x4_t lead = vreinterpretq_u32_u16(vsliq_n_u16(vreinterpretq_u16_u8(swap), vreinterpretq_u16_u8(in), 6)); - // Mask out lead - // 000000cc ccdddddd|xxxxxxxx xxxxxxxx - lead = vbicq_u32(lead, vmovq_n_u32(uint32_t(~0x03FFFFFF))); - // Blend pairs - // 000000cc ccdddddd|11110aaa bbbbbb00 - uint16x8_t blend = vreinterpretq_u16_u32(vbslq_u32(vmovq_n_u32(0x0000FFFF), trail, lead)); - // Add magic number to finish the result - // 110111CC CCDDDDDD|110110AA BBBBBBCC - uint16x8_t composed = vaddq_u16(blend, magic_with_low_2); - // Byte swap if necessary - if (!match_system(big_endian)) { - composed = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(composed))); - } - vst1q_u16(reinterpret_cast(utf16_output), composed); - utf16_output += 6; // We 3 32-bit surrogate pairs. - return 12; // We consumed 12 bytes. - } - // 3 1-4 byte sequences - uint8x16_t sh = vld1q_u8(reinterpret_cast(simdutf::tables::utf8_to_utf16::shufutf8[idx])); - - // 1 byte: 00000000 00000000 00000000 0ddddddd - // 3 byte: 00000000 00000000 110ccccc 10dddddd - // 3 byte: 00000000 1110bbbb 10cccccc 10dddddd - // 4 byte: 11110aaa 10bbbbbb 10cccccc 10dddddd - uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh)); - // Mask the low and middle bytes - // 00000000 00000000 00000000 0ddddddd - uint32x4_t ascii = vandq_u32(perm, vmovq_n_u32(0x7f)); - // Because the surrogates need more work, the high surrogate is computed first. - uint32x4_t middlehigh = vshlq_n_u32(perm, 2); - // 00000000 00000000 00cccccc 00000000 - uint32x4_t middlebyte = vandq_u32(perm, vmovq_n_u32(0x3F00)); - // Start assembling the sequence. Since the 4th byte is in the same position as it - // would be in a surrogate and there is no dependency, shift left instead of right. - // 3 byte: 00000000 10bbbbxx xxxxxxxx xxxxxxxx - // 4 byte: 11110aaa bbbbbbxx xxxxxxxx xxxxxxxx - uint32x4_t ab = vbslq_u32(vmovq_n_u32(0xFF000000), perm, middlehigh); - // Top 16 bits contains the high ten bits of the surrogate pair before correction - // 3 byte: 00000000 10bbbbcc|cccc0000 00000000 - // 4 byte: 11110aaa bbbbbbcc|cccc0000 00000000 - high 10 bits correct w/o correction - uint32x4_t abc = vbslq_u32(vmovq_n_u32(0xFFFC0000), ab, vshlq_n_u32(middlebyte, 4)); - // Combine the low 6 or 7 bits by a shift right accumulate - // 3 byte: 00000000 00000010|bbbbcccc ccdddddd - low 16 bits correct - // 4 byte: 00000011 110aaabb|bbbbcccc ccdddddd - low 10 bits correct w/o correction - uint32x4_t composed = vsraq_n_u32(ascii, abc, 6); - // After this is for surrogates - // Blend the low and high surrogates - // 4 byte: 11110aaa bbbbbbcc|bbbbcccc ccdddddd - uint32x4_t mixed = vbslq_u32(vmovq_n_u32(0xFFFF0000), abc, composed); - // Clear the upper 6 bits of the low surrogate. Don't clear the upper bits yet as - // 0x10000 was not subtracted from the codepoint yet. - // 4 byte: 11110aaa bbbbbbcc|000000cc ccdddddd - uint16x8_t masked_pair = - vreinterpretq_u16_u32(vbicq_u32(mixed, vmovq_n_u32(uint32_t(~0xFFFF03FF)))); - // Correct the remaining UTF-8 prefix, surrogate offset, and add the surrogate prefixes - // in one magic 16-bit addition. - // similar magic number but without the continue byte adjust and halfword swapped - // UTF-8 4b prefix = -0xF000|0x0000 - // surrogate offset = -0x0040|0x0000 (0x10000 << 6) - // surrogate high = +0xD800|0x0000 - // surrogate low = +0x0000|0xDC00 - // ----------------------------------- - // = +0xE7C0|0xDC00 - uint16x8_t magic = vreinterpretq_u16_u32(vmovq_n_u32(0xE7C0DC00)); - // 4 byte: 110110AA BBBBBBCC|110111CC CCDDDDDD - surrogate pair complete - uint32x4_t surrogates = vreinterpretq_u32_u16(vaddq_u16(masked_pair, magic)); - // If the high bit is 1 (s32 less than zero), this needs a surrogate pair - uint32x4_t is_pair = vcltzq_s32(vreinterpretq_s32_u32(perm)); - - // Select either the 4 byte surrogate pair or the 2 byte solo codepoint - // 3 byte: 0xxxxxxx xxxxxxxx|bbbbcccc ccdddddd - // 4 byte: 110110AA BBBBBBCC|110111CC CCDDDDDD - uint32x4_t selected = vbslq_u32(is_pair, surrogates, composed); - // Byte swap if necessary - if (!match_system(big_endian)) { - selected = vreinterpretq_u32_u8(vrev16q_u8(vreinterpretq_u8_u32(selected))); - } - // Attempting to shuffle and store would be complex, just scalarize. - uint32_t buffer[4]; - vst1q_u32(buffer, selected); - // Test for the top bit of the surrogate mask. - const uint32_t SURROGATE_MASK = match_system(big_endian) ? 0x80000000 : 0x00800000; - for (size_t i = 0; i < 3; i++) { - // Surrogate - if (buffer[i] & SURROGATE_MASK) { - utf16_output[0] = uint16_t(buffer[i] >> 16); - utf16_output[1] = uint16_t(buffer[i] & 0xFFFF); - utf16_output += 2; - } else { - utf16_output[0] = uint16_t(buffer[i] & 0xFFFF); - utf16_output++; - } - } - return consumed; - } else { - // here we know that there is an error but we do not handle errors - return 12; - } -} - -/* end file src/arm64/arm_convert_utf8_to_utf16.cpp */ -/* begin file src/arm64/arm_convert_utf8_to_utf32.cpp */ -// Convert up to 12 bytes from utf8 to utf32 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -size_t convert_masked_utf8_to_utf32(const char *input, - uint64_t utf8_end_of_code_point_mask, - char32_t *&utf32_out) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - uint32_t*& utf32_output = reinterpret_cast(utf32_out); - uint8x16_t in = vld1q_u8(reinterpret_cast(input)); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xFFF; - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - // We first try a few fast paths. - if((utf8_end_of_code_point_mask & 0xffff) == 0xffff) { - // We process in chunks of 16 bytes. - // use fast implementation in src/simdutf/arm64/simd.h - // Ideally the compiler can keep the tables in registers. - simd8 temp{vreinterpretq_s8_u8(in)}; - temp.store_ascii_as_utf32_tbl(utf32_out); - utf32_output += 16; // We wrote 16 32-bit characters. - return 16; // We consumed 16 bytes. - } - if(input_utf8_end_of_code_point_mask == 0x924) { - // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte UTF-32 code units. - // Convert to UTF-16 - uint16x4_t composed_utf16 = convert_utf8_3_byte_to_utf16(in); - // Zero extend and store via ST2 with a zero. - uint16x4x2_t interleaver = {{ composed_utf16, vmov_n_u16(0) }}; - vst2_u16(reinterpret_cast(utf32_output), interleaver); - utf32_output += 4; // We wrote 4 32-bit characters. - return 12; // We consumed 12 bytes. - } - - // 2 byte sequences occur in short bursts in languages like Greek and Russian. - if(input_utf8_end_of_code_point_mask == 0xaaa) { - // We want to take 6 2-byte UTF-8 code units and turn them into 6 4-byte UTF-32 code units. - // Convert to UTF-16 - uint16x8_t composed_utf16 = convert_utf8_2_byte_to_utf16(in); - // Zero extend and store via ST2 with a zero. - uint16x8x2_t interleaver = {{ composed_utf16, vmovq_n_u16(0) }}; - vst2q_u16(reinterpret_cast(utf32_output), interleaver); - utf32_output += 6; // We wrote 6 32-bit characters. - return 12; // We consumed 12 bytes. - } - /// Either no fast path or an unimportant fast path. - - const uint8_t idx = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - - - if (idx < 64) { - // SIX (6) input code-code units - // Convert to UTF-16 - uint16x8_t composed_utf16 = convert_utf8_1_to_2_byte_to_utf16(in, idx); - // Zero extend and store with ST2 and zero - uint16x8x2_t interleaver = {{ composed_utf16, vmovq_n_u16(0) }}; - vst2q_u16(reinterpret_cast(utf32_output), interleaver); - utf32_output += 6; // We wrote 6 32-bit characters. - return consumed; - } else if (idx < 145) { - // FOUR (4) input code-code units - // UTF-16 and UTF-32 use similar algorithms, but UTF-32 skips the narrowing. - uint8x16_t sh = vld1q_u8(reinterpret_cast(simdutf::tables::utf8_to_utf16::shufutf8[idx])); - // Shuffle - // 1 byte: 00000000 00000000 0ccccccc - // 2 byte: 00000000 110bbbbb 10cccccc - // 3 byte: 1110aaaa 10bbbbbb 10cccccc - uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh)); - // Split - // 00000000 00000000 0ccccccc - uint32x4_t ascii = vandq_u32(perm, vmovq_n_u32(0x7F)); // 6 or 7 bits - // Note: unmasked - // xxxxxxxx aaaaxxxx xxxxxxxx - uint32x4_t high = vshrq_n_u32(perm, 4); // 4 bits - // Use 16 bit bic instead of and. - // The top bits will be corrected later in the bsl - // 00000000 10bbbbbb 00000000 - uint32x4_t middle = - vreinterpretq_u32_u16(vbicq_u16(vreinterpretq_u16_u32(perm), vmovq_n_u16(uint16_t(~0xff00)))); // 5 or 6 bits - // Combine low and middle with shift right accumulate - // 00000000 00xxbbbb bbcccccc - uint32x4_t lowmid = vsraq_n_u32(ascii, middle, 2); - // Insert top 4 bits from high byte with bitwise select - // 00000000 aaaabbbb bbcccccc - uint32x4_t composed = vbslq_u32(vmovq_n_u32(0x0000F000), high, lowmid); - vst1q_u32(utf32_output, composed); - utf32_output += 4; // We wrote 4 32-bit characters. - return consumed; - } else if (idx < 209) { - // THREE (3) input code-code units - if (input_utf8_end_of_code_point_mask == 0x888) { - // We want to take 3 4-byte UTF-8 code units and turn them into 3 4-byte UTF-32 code units. - // This uses the same method as the fixed 3 byte version, reversing and shift left insert. - // However, there is no need for a shuffle mask now, just rev16 and rev32. - // - // This version does not use the LUT, but 4 byte sequences are less common and the - // overhead of the extra memory access is less important than the early branch overhead - // in shorter sequences, so it comes last. - - // Swap pairs of bytes - // 10dddddd|10cccccc|10bbbbbb|11110aaa - // 10cccccc 10dddddd|11110aaa 10bbbbbb - uint16x8_t swap1 = vreinterpretq_u16_u8(vrev16q_u8(in)); - // Shift left and insert - // xxxxcccc ccdddddd|xxxxxxxa aabbbbbb - uint16x8_t merge1 = vsliq_n_u16(swap1, vreinterpretq_u16_u8(in), 6); - // Swap 16-bit lanes - // xxxxcccc ccdddddd xxxxxxxa aabbbbbb - // xxxxxxxa aabbbbbb xxxxcccc ccdddddd - uint32x4_t swap2 = vreinterpretq_u32_u16(vrev32q_u16(merge1)); - // Shift insert again - // xxxxxxxx xxxaaabb bbbbcccc ccdddddd - uint32x4_t merge2 = vsliq_n_u32(swap2, vreinterpretq_u32_u16(merge1), 12); - // Clear the garbage - // 00000000 000aaabb bbbbcccc ccdddddd - uint32x4_t composed = vandq_u32(merge2, vmovq_n_u32(0x1FFFFF)); - // Store - vst1q_u32(utf32_output, composed); - - utf32_output += 3; // We wrote 3 32-bit characters. - return 12; // We consumed 12 bytes. - } - // Unlike UTF-16, doing a fast codepath doesn't have nearly as much benefit due to - // surrogates no longer being involved. - uint8x16_t sh = vld1q_u8(reinterpret_cast(simdutf::tables::utf8_to_utf16::shufutf8[idx])); - // 1 byte: 00000000 00000000 00000000 0ddddddd - // 2 byte: 00000000 00000000 110ccccc 10dddddd - // 3 byte: 00000000 1110bbbb 10cccccc 10dddddd - // 4 byte: 11110aaa 10bbbbbb 10cccccc 10dddddd - uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh)); - // Ascii - uint32x4_t ascii = vandq_u32(perm, vmovq_n_u32(0x7F)); - uint32x4_t middle = vandq_u32(perm, vmovq_n_u32(0x3f00)); - // When converting the way we do, the 3 byte prefix will be interpreted as the - // 18th bit being set, since the code would interpret the lead byte (0b1110bbbb) - // as a continuation byte (0b10bbbbbb). To fix this, we can either xor or do an - // 8 bit add of the 6th bit shifted right by 1. Since NEON has shift right accumulate, - // we use that. - // 4 byte 3 byte - // 10bbbbbb 1110bbbb - // 00000000 01000000 6th bit - // 00000000 00100000 shift right - // 10bbbbbb 0000bbbb add - // 00bbbbbb 0000bbbb mask - uint8x16_t correction = - vreinterpretq_u8_u32(vandq_u32(perm, vmovq_n_u32(0x00400000))); - uint32x4_t corrected = - vreinterpretq_u32_u8(vsraq_n_u8(vreinterpretq_u8_u32(perm), correction, 1)); - // 00000000 00000000 0000cccc ccdddddd - uint32x4_t cd = vsraq_n_u32(ascii, middle, 2); - // Insert twice - // xxxxxxxx xxxaaabb bbbbxxxx xxxxxxxx - uint32x4_t ab = vbslq_u32(vmovq_n_u32(0x01C0000), vshrq_n_u32(corrected, 6), vshrq_n_u32(corrected, 4)); - // 00000000 000aaabb bbbbcccc ccdddddd - uint32x4_t composed = vbslq_u32(vmovq_n_u32(0xFFE00FFF), cd, ab); - // Store - vst1q_u32(utf32_output, composed); - utf32_output += 3; // We wrote 3 32-bit characters. - return consumed; - } else { - // here we know that there is an error but we do not handle errors - return 12; - } -} -/* end file src/arm64/arm_convert_utf8_to_utf32.cpp */ -/* begin file src/arm64/arm_convert_utf8_to_latin1.cpp */ -// Convert up to 16 bytes from utf8 to utf16 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 16, usually 12). -size_t convert_masked_utf8_to_latin1(const char *input, - uint64_t utf8_end_of_code_point_mask, - char *&latin1_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - uint8x16_t in = vld1q_u8(reinterpret_cast(input)); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - - // We first try a few fast paths. - // The obvious first test is ASCII, which actually consumes the full 16. - if((utf8_end_of_code_point_mask & 0xFFFF) == 0xffff) { - // We process in chunks of 16 bytes - vst1q_u8(reinterpret_cast(latin1_output), in); - latin1_output += 16; // We wrote 16 18-bit characters. - return 16; // We consumed 16 bytes. - } - /// We do not have a fast path available, or the fast path is unimportant, so we fallback. - const uint8_t idx = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - - const uint8_t consumed = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - // this indicates an invalid input: - if(idx >= 64) { return consumed; } - // Here we should have (idx < 64), if not, there is a bug in the validation or elsewhere. - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. - // Converts 6 1-2 byte UTF-8 characters to 6 UTF-16 characters. - // This is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. - uint8x16_t sh = vld1q_u8(reinterpret_cast(simdutf::tables::utf8_to_utf16::shufutf8[idx])); - // Shuffle - // 1 byte: 00000000 0bbbbbbb - // 2 byte: 110aaaaa 10bbbbbb - uint16x8_t perm = vreinterpretq_u16_u8(vqtbl1q_u8(in, sh)); - // Mask - // 1 byte: 00000000 0bbbbbbb - // 2 byte: 00000000 00bbbbbb - uint16x8_t ascii = vandq_u16(perm, vmovq_n_u16(0x7f)); // 6 or 7 bits - // 1 byte: 00000000 00000000 - // 2 byte: 000aaaaa 00000000 - uint16x8_t highbyte = vandq_u16(perm, vmovq_n_u16(0x1f00)); // 5 bits - // Combine with a shift right accumulate - // 1 byte: 00000000 0bbbbbbb - // 2 byte: 00000aaa aabbbbbb - uint16x8_t composed = vsraq_n_u16(ascii, highbyte, 2); - // writing 8 bytes even though we only care about the first 6 bytes. - uint8x8_t latin1_packed = vmovn_u16(composed); - vst1_u8(reinterpret_cast(latin1_output), latin1_packed); - latin1_output += 6; // We wrote 6 bytes. - return consumed; -} - -/* end file src/arm64/arm_convert_utf8_to_latin1.cpp */ - -/* begin file src/arm64/arm_convert_utf16_to_latin1.cpp */ - -template -std::pair arm_convert_utf16_to_latin1(const char16_t* buf, size_t len, char* latin1_output) { - const char16_t* end = buf + len; - while (buf + 8 <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); } - if (vmaxvq_u16(in) <= 0xff) { - // 1. pack the bytes - uint8x8_t latin1_packed = vmovn_u16(in); - // 2. store (8 bytes) - vst1_u8(reinterpret_cast(latin1_output), latin1_packed); - // 3. adjust pointers - buf += 8; - latin1_output += 8; - } else { - return std::make_pair(nullptr, reinterpret_cast(latin1_output)); - } - } // while - return std::make_pair(buf, latin1_output); -} - -template -std::pair arm_convert_utf16_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) { - const char16_t* start = buf; - const char16_t* end = buf + len; - while (buf + 8 <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); } - if (vmaxvq_u16(in) <= 0xff) { - // 1. pack the bytes - uint8x8_t latin1_packed = vmovn_u16(in); - // 2. store (8 bytes) - vst1_u8(reinterpret_cast(latin1_output), latin1_packed); - // 3. adjust pointers - buf += 8; - latin1_output += 8; - } else { - // Let us do a scalar fallback. - for(int k = 0; k < 8; k++) { - uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if(word <= 0xff) { - *latin1_output++ = char(word); - } else { - return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), latin1_output); - } - } - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output); -} -/* end file src/arm64/arm_convert_utf16_to_latin1.cpp */ -/* begin file src/arm64/arm_convert_utf16_to_utf8.cpp */ -/* - The vectorized algorithm works on single SSE register i.e., it - loads eight 16-bit code units. - - We consider three cases: - 1. an input register contains no surrogates and each value - is in range 0x0000 .. 0x07ff. - 2. an input register contains no surrogates and values are - is in range 0x0000 .. 0xffff. - 3. an input register contains surrogates --- i.e. codepoints - can have 16 or 32 bits. - - Ad 1. - - When values are less than 0x0800, it means that a 16-bit code unit - can be converted into: 1) single UTF8 byte (when it's an ASCII - char) or 2) two UTF8 bytes. - - For this case we do only some shuffle to obtain these 2-byte - codes and finally compress the whole SSE register with a single - shuffle. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - Ad 2. - - When values fit in 16-bit code units, but are above 0x07ff, then - a single word may produce one, two or three UTF8 bytes. - - We prepare data for all these three cases in two registers. - The first register contains lower two UTF8 bytes (used in all - cases), while the second one contains just the third byte for - the three-UTF8-bytes case. - - Finally these two registers are interleaved forming eight-element - array of 32-bit values. The array spans two SSE registers. - The bytes from the registers are compressed using two shuffles. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - - To summarize: - - We need two 256-entry tables that have 8704 bytes in total. -*/ -/* - Returns a pair: the first unprocessed byte from buf and utf8_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::pair arm_convert_utf16_to_utf8(const char16_t* buf, size_t len, char* utf8_out) { - uint8_t * utf8_output = reinterpret_cast(utf8_out); - const char16_t* end = buf + len; - - const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800); - const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800); - const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - while (buf + 16 + safety_margin <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); } - if(vmaxvq_u16(in) <= 0x7F) { // ASCII fast path!!!! - // It is common enough that we have sequences of 16 consecutive ASCII characters. - uint16x8_t nextin = vld1q_u16(reinterpret_cast(buf) + 8); - if (!match_system(big_endian)) { nextin = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(nextin))); } - if(vmaxvq_u16(nextin) > 0x7F) { - // 1. pack the bytes - // obviously suboptimal. - uint8x8_t utf8_packed = vmovn_u16(in); - // 2. store (8 bytes) - vst1_u8(utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - in = nextin; - } else { - // 1. pack the bytes - // obviously suboptimal. - uint8x16_t utf8_packed = vmovn_high_u16(vmovn_u16(in), nextin); - // 2. store (16 bytes) - vst1q_u8(utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - } - - if (vmaxvq_u16(in) <= 0x7FF) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00); - const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const uint16x8_t t0 = vshlq_n_u16(in, 2); - // t1 = [000a|aaaa|0000|0000] - const uint16x8_t t1 = vandq_u16(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const uint16x8_t t2 = vandq_u16(in, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const uint16x8_t t3 = vorrq_u16(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const uint16x8_t t4 = vorrq_u16(t3, v_c080); - // 2. merge ASCII and 2-byte codewords - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f); - const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, in, t4)); - // 3. prepare bitmask for 8-bit lookup -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080); -#else - const uint16x8_t mask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080 }; -#endif - uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask)); - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const uint8x16_t shuffle = vld1q_u8(row + 1); - const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle); - - // 5. store bytes - vst1q_u8(utf8_output, utf8_packed); - - // 6. adjust pointers - buf += 8; - utf8_output += row[0]; - continue; - - } - const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (vmaxvq_u16(surrogates_bytemask) == 0) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); -#else - const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e}; -#endif - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) vmovq_n_u16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(in), vreinterpretq_u8_u16(dup_even))); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000)); - - // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa] - const uint16x8_t s0 = vshrq_n_u16(in, 12); - // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000] - const uint16x8_t s1 = vandq_u16(in, simdutf_vec(0b0000111111000000)); - // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000] - const uint16x8_t s1s = vshlq_n_u16(s1, 2); - // [00bb|bbbb|0000|aaaa] - const uint16x8_t s2 = vorrq_u16(s0, s1s); - // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000)); - const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF); - const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(in, v_07ff); - const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask); - const uint16x8_t s4 = veorq_u16(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4)); - const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4)); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f); -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 ); - const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 ); -#else - const uint16x8_t onemask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 }; - const uint16x8_t twomask = { 0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 }; -#endif - const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask)); - const uint16_t mask = vaddvq_u16(combined); - // The following fast path may or may not be beneficial. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0}; - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle); - vst1q_u8(utf8_output, utf8_0); - utf8_output += 12; - vst1q_u8(utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const uint8x16_t shuffle0 = vld1q_u8(row0 + 1); - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const uint8x16_t shuffle1 = vld1q_u8(row1 + 1); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1); - - vst1q_u8(utf8_output, utf8_0); - utf8_output += row0[0]; - vst1q_u8(utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word & 0xFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xF800 ) != 0xD800) { - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, reinterpret_cast(utf8_output)); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(buf, reinterpret_cast(utf8_output)); -} - - -/* - Returns a pair: a result struct and utf8_output. - If there is an error, the count field of the result is the position of the error. - Otherwise, it is the position of the first unprocessed byte in buf (even if finished). - A scalar routing should carry on the conversion of the tail if needed. -*/ -template -std::pair arm_convert_utf16_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_out) { - uint8_t * utf8_output = reinterpret_cast(utf8_out); - const char16_t* start = buf; - const char16_t* end = buf + len; - - const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800); - const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800); - const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); } - if(vmaxvq_u16(in) <= 0x7F) { // ASCII fast path!!!! - // It is common enough that we have sequences of 16 consecutive ASCII characters. - uint16x8_t nextin = vld1q_u16(reinterpret_cast(buf) + 8); - if (!match_system(big_endian)) { nextin = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(nextin))); } - if(vmaxvq_u16(nextin) > 0x7F) { - // 1. pack the bytes - // obviously suboptimal. - uint8x8_t utf8_packed = vmovn_u16(in); - // 2. store (8 bytes) - vst1_u8(utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - in = nextin; - } else { - // 1. pack the bytes - // obviously suboptimal. - uint8x16_t utf8_packed = vmovn_high_u16(vmovn_u16(in), nextin); - // 2. store (16 bytes) - vst1q_u8(utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - } - - if (vmaxvq_u16(in) <= 0x7FF) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00); - const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const uint16x8_t t0 = vshlq_n_u16(in, 2); - // t1 = [000a|aaaa|0000|0000] - const uint16x8_t t1 = vandq_u16(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const uint16x8_t t2 = vandq_u16(in, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const uint16x8_t t3 = vorrq_u16(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const uint16x8_t t4 = vorrq_u16(t3, v_c080); - // 2. merge ASCII and 2-byte codewords - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f); - const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, in, t4)); - // 3. prepare bitmask for 8-bit lookup -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080); -#else - const uint16x8_t mask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080 }; -#endif - uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask)); - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const uint8x16_t shuffle = vld1q_u8(row + 1); - const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle); - - // 5. store bytes - vst1q_u8(utf8_output, utf8_packed); - - // 6. adjust pointers - buf += 8; - utf8_output += row[0]; - continue; - - } - const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (vmaxvq_u16(surrogates_bytemask) == 0) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); -#else - const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e}; -#endif - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) vmovq_n_u16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(in), vreinterpretq_u8_u16(dup_even))); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000)); - - // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa] - const uint16x8_t s0 = vshrq_n_u16(in, 12); - // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000] - const uint16x8_t s1 = vandq_u16(in, simdutf_vec(0b0000111111000000)); - // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000] - const uint16x8_t s1s = vshlq_n_u16(s1, 2); - // [00bb|bbbb|0000|aaaa] - const uint16x8_t s2 = vorrq_u16(s0, s1s); - // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000)); - const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF); - const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(in, v_07ff); - const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask); - const uint16x8_t s4 = veorq_u16(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4)); - const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4)); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f); -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 ); - const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 ); -#else - const uint16x8_t onemask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 }; - const uint16x8_t twomask = { 0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 }; -#endif - const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask)); - const uint16_t mask = vaddvq_u16(combined); - // The following fast path may or may not be beneficial. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0}; - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle); - vst1q_u8(utf8_output, utf8_0); - utf8_output += 12; - vst1q_u8(utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const uint8x16_t shuffle0 = vld1q_u8(row0 + 1); - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const uint8x16_t shuffle1 = vld1q_u8(row1 + 1); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1); - - vst1q_u8(utf8_output, utf8_0); - utf8_output += row0[0]; - vst1q_u8(utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word & 0xFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xF800 ) != 0xD800) { - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), reinterpret_cast(utf8_output)); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast(utf8_output)); -} -/* end file src/arm64/arm_convert_utf16_to_utf8.cpp */ -/* begin file src/arm64/arm_convert_utf16_to_utf32.cpp */ -/* - The vectorized algorithm works on single SSE register i.e., it - loads eight 16-bit code units. - - We consider three cases: - 1. an input register contains no surrogates and each value - is in range 0x0000 .. 0x07ff. - 2. an input register contains no surrogates and values are - is in range 0x0000 .. 0xffff. - 3. an input register contains surrogates --- i.e. codepoints - can have 16 or 32 bits. - - Ad 1. - - When values are less than 0x0800, it means that a 16-bit code unit - can be converted into: 1) single UTF8 byte (when it's an ASCII - char) or 2) two UTF8 bytes. - - For this case we do only some shuffle to obtain these 2-byte - codes and finally compress the whole SSE register with a single - shuffle. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - Ad 2. - - When values fit in 16-bit code units, but are above 0x07ff, then - a single word may produce one, two or three UTF8 bytes. - - We prepare data for all these three cases in two registers. - The first register contains lower two UTF8 bytes (used in all - cases), while the second one contains just the third byte for - the three-UTF8-bytes case. - - Finally these two registers are interleaved forming eight-element - array of 32-bit values. The array spans two SSE registers. - The bytes from the registers are compressed using two shuffles. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - - To summarize: - - We need two 256-entry tables that have 8704 bytes in total. -*/ -/* - Returns a pair: the first unprocessed byte from buf and utf8_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::pair arm_convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_out) { - uint32_t * utf32_output = reinterpret_cast(utf32_out); - const char16_t* end = buf + len; - - const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800); - const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800); - - while (buf + 8 <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); } - - const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (vmaxvq_u16(surrogates_bytemask) == 0) { - // case: no surrogate pairs, extend all 16-bit code units to 32-bit code units - vst1q_u32(utf32_output, vmovl_u16(vget_low_u16(in))); - vst1q_u32(utf32_output+4, vmovl_high_u16(in)); - utf32_output += 8; - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word &0xF800 ) != 0xD800) { - *utf32_output++ = char32_t(word); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, reinterpret_cast(utf32_output)); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - } - } - buf += k; - } - } // while - return std::make_pair(buf, reinterpret_cast(utf32_output)); -} - - -/* - Returns a pair: a result struct and utf8_output. - If there is an error, the count field of the result is the position of the error. - Otherwise, it is the position of the first unprocessed byte in buf (even if finished). - A scalar routing should carry on the conversion of the tail if needed. -*/ -template -std::pair arm_convert_utf16_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_out) { - uint32_t * utf32_output = reinterpret_cast(utf32_out); - const char16_t* start = buf; - const char16_t* end = buf + len; - - const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800); - const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800); - - while (buf + 8 <= end) { - uint16x8_t in = vld1q_u16(reinterpret_cast(buf)); - if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); } - - const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (vmaxvq_u16(surrogates_bytemask) == 0) { - // case: no surrogate pairs, extend all 16-bit code units to 32-bit code units - vst1q_u32(utf32_output, vmovl_u16(vget_low_u16(in))); - vst1q_u32(utf32_output+4, vmovl_high_u16(in)); - utf32_output += 8; - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word &0xF800 ) != 0xD800) { - *utf32_output++ = char32_t(word); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), reinterpret_cast(utf32_output)); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - } - } - buf += k; - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast(utf32_output)); -} -/* end file src/arm64/arm_convert_utf16_to_utf32.cpp */ - -/* begin file src/arm64/arm_convert_utf32_to_latin1.cpp */ -std::pair arm_convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) { - const char32_t* end = buf + len; - while (buf + 8 <= end) { - uint32x4_t in1 = vld1q_u32(reinterpret_cast(buf)); - uint32x4_t in2 = vld1q_u32(reinterpret_cast(buf+4)); - - uint16x8_t utf16_packed = vcombine_u16(vqmovn_u32(in1), vqmovn_u32(in2)); - if (vmaxvq_u16(utf16_packed) <= 0xff) { - // 1. pack the bytes - uint8x8_t latin1_packed = vmovn_u16(utf16_packed); - // 2. store (8 bytes) - vst1_u8(reinterpret_cast(latin1_output), latin1_packed); - // 3. adjust pointers - buf += 8; - latin1_output += 8; - } else { - return std::make_pair(nullptr, reinterpret_cast(latin1_output)); - } - } // while - return std::make_pair(buf, latin1_output); -} - - -std::pair arm_convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) { - const char32_t* start = buf; - const char32_t* end = buf + len; - - while (buf + 8 <= end) { - uint32x4_t in1 = vld1q_u32(reinterpret_cast(buf)); - uint32x4_t in2 = vld1q_u32(reinterpret_cast(buf+4)); - - uint16x8_t utf16_packed = vcombine_u16(vqmovn_u32(in1), vqmovn_u32(in2)); - - if (vmaxvq_u16(utf16_packed) <= 0xff) { - // 1. pack the bytes - uint8x8_t latin1_packed = vmovn_u16(utf16_packed); - // 2. store (8 bytes) - vst1_u8(reinterpret_cast(latin1_output), latin1_packed); - // 3. adjust pointers - buf += 8; - latin1_output += 8; - } else { - // Let us do a scalar fallback. - for(int k = 0; k < 8; k++) { - uint32_t word = buf[k]; - if(word <= 0xff) { - *latin1_output++ = char(word); - } else { - return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), latin1_output); - } - } - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output); -} -/* end file src/arm64/arm_convert_utf32_to_latin1.cpp */ -/* begin file src/arm64/arm_convert_utf32_to_utf8.cpp */ -std::pair arm_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_out) { - uint8_t * utf8_output = reinterpret_cast(utf8_out); - const char32_t* end = buf + len; - - const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080); - - uint16x8_t forbidden_bytemask = vmovq_n_u16(0x0); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin < end) { - uint32x4_t in = vld1q_u32(reinterpret_cast(buf)); - uint32x4_t nextin = vld1q_u32(reinterpret_cast(buf+4)); - - // Check if no bits set above 16th - if(vmaxvq_u32(vorrq_u32(in, nextin)) <= 0xFFFF) { - // Pack UTF-32 to UTF-16 safely (without surrogate pairs) - // Apply UTF-16 => UTF-8 routine (arm_convert_utf16_to_utf8.cpp) - uint16x8_t utf16_packed = vcombine_u16(vmovn_u32(in), vmovn_u32(nextin)); - if(vmaxvq_u16(utf16_packed) <= 0x7F) { // ASCII fast path!!!! - // 1. pack the bytes - // obviously suboptimal. - uint8x8_t utf8_packed = vmovn_u16(utf16_packed); - // 2. store (8 bytes) - vst1_u8(utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - continue; // we are done for this round! - } - - if (vmaxvq_u16(utf16_packed) <= 0x7FF) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00); - const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const uint16x8_t t0 = vshlq_n_u16(utf16_packed, 2); - // t1 = [000a|aaaa|0000|0000] - const uint16x8_t t1 = vandq_u16(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const uint16x8_t t2 = vandq_u16(utf16_packed, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const uint16x8_t t3 = vorrq_u16(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const uint16x8_t t4 = vorrq_u16(t3, v_c080); - // 2. merge ASCII and 2-byte codewords - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f); - const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, utf16_packed, t4)); - // 3. prepare bitmask for 8-bit lookup - #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080); - #else - const uint16x8_t mask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080 }; - #endif - uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask)); - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const uint8x16_t shuffle = vld1q_u8(row + 1); - const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle); - - // 5. store bytes - vst1q_u8(utf8_output, utf8_packed); - - // 6. adjust pointers - buf += 8; - utf8_output += row[0]; - continue; - } else { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800); - const uint16x8_t v_dfff = vmovq_n_u16((uint16_t)0xdfff); - forbidden_bytemask = vorrq_u16(vandq_u16(vcleq_u16(utf16_packed, v_dfff), vcgeq_u16(utf16_packed, v_d800)), forbidden_bytemask); - - #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - #else - const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e}; - #endif - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ - #define simdutf_vec(x) vmovq_n_u16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(utf16_packed), vreinterpretq_u8_u16(dup_even))); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000)); - - // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa] - const uint16x8_t s0 = vshrq_n_u16(utf16_packed, 12); - // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000] - const uint16x8_t s1 = vandq_u16(utf16_packed, simdutf_vec(0b0000111111000000)); - // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000] - const uint16x8_t s1s = vshlq_n_u16(s1, 2); - // [00bb|bbbb|0000|aaaa] - const uint16x8_t s2 = vorrq_u16(s0, s1s); - // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000)); - const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF); - const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(utf16_packed, v_07ff); - const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask); - const uint16x8_t s4 = veorq_u16(s3, m0); - #undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4)); - const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4)); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f); - #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 ); - const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 ); - #else - const uint16x8_t onemask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 }; - const uint16x8_t twomask = { 0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 }; - #endif - const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask)); - const uint16_t mask = vaddvq_u16(combined); - // The following fast path may or may not be beneficial. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0}; - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle); - vst1q_u8(utf8_output, utf8_0); - utf8_output += 12; - vst1q_u8(utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const uint8x16_t shuffle0 = vld1q_u8(row0 + 1); - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const uint8x16_t shuffle1 = vld1q_u8(row1 + 1); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1); - - vst1q_u8(utf8_output, utf8_0); - utf8_output += row0[0]; - vst1q_u8(utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - } - // At least one 32-bit word will produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes. - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word & 0xFFFF0000)==0) { - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, reinterpret_cast(utf8_output)); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - if (word > 0x10FFFF) { return std::make_pair(nullptr, reinterpret_cast(utf8_output)); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - // check for invalid input - if (vmaxvq_u16(forbidden_bytemask) != 0) { - return std::make_pair(nullptr, reinterpret_cast(utf8_output)); - } - return std::make_pair(buf, reinterpret_cast(utf8_output)); -} - - -std::pair arm_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_out) { - uint8_t * utf8_output = reinterpret_cast(utf8_out); - const char32_t* start = buf; - const char32_t* end = buf + len; - - const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin < end) { - uint32x4_t in = vld1q_u32(reinterpret_cast(buf)); - uint32x4_t nextin = vld1q_u32(reinterpret_cast(buf+4)); - - // Check if no bits set above 16th - if(vmaxvq_u32(vorrq_u32(in, nextin)) <= 0xFFFF) { - // Pack UTF-32 to UTF-16 safely (without surrogate pairs) - // Apply UTF-16 => UTF-8 routine (arm_convert_utf16_to_utf8.cpp) - uint16x8_t utf16_packed = vcombine_u16(vmovn_u32(in), vmovn_u32(nextin)); - if(vmaxvq_u16(utf16_packed) <= 0x7F) { // ASCII fast path!!!! - // 1. pack the bytes - // obviously suboptimal. - uint8x8_t utf8_packed = vmovn_u16(utf16_packed); - // 2. store (8 bytes) - vst1_u8(utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - continue; // we are done for this round! - } - - if (vmaxvq_u16(utf16_packed) <= 0x7FF) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00); - const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const uint16x8_t t0 = vshlq_n_u16(utf16_packed, 2); - // t1 = [000a|aaaa|0000|0000] - const uint16x8_t t1 = vandq_u16(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const uint16x8_t t2 = vandq_u16(utf16_packed, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const uint16x8_t t3 = vorrq_u16(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const uint16x8_t t4 = vorrq_u16(t3, v_c080); - // 2. merge ASCII and 2-byte codewords - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f); - const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, utf16_packed, t4)); - // 3. prepare bitmask for 8-bit lookup - #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080); - #else - const uint16x8_t mask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0002, 0x0008, - 0x0020, 0x0080 }; - #endif - uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask)); - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const uint8x16_t shuffle = vld1q_u8(row + 1); - const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle); - - // 5. store bytes - vst1q_u8(utf8_output, utf8_packed); - - // 6. adjust pointers - buf += 8; - utf8_output += row[0]; - continue; - } else { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - - // check for invalid input - const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800); - const uint16x8_t v_dfff = vmovq_n_u16((uint16_t)0xdfff); - const uint16x8_t forbidden_bytemask = vandq_u16(vcleq_u16(utf16_packed, v_dfff), vcgeq_u16(utf16_packed, v_d800)); - if (vmaxvq_u16(forbidden_bytemask) != 0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), reinterpret_cast(utf8_output)); - } - - #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - #else - const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e}; - #endif - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ - #define simdutf_vec(x) vmovq_n_u16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(utf16_packed), vreinterpretq_u8_u16(dup_even))); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000)); - - // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa] - const uint16x8_t s0 = vshrq_n_u16(utf16_packed, 12); - // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000] - const uint16x8_t s1 = vandq_u16(utf16_packed, simdutf_vec(0b0000111111000000)); - // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000] - const uint16x8_t s1s = vshlq_n_u16(s1, 2); - // [00bb|bbbb|0000|aaaa] - const uint16x8_t s2 = vorrq_u16(s0, s1s); - // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000)); - const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF); - const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(utf16_packed, v_07ff); - const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask); - const uint16x8_t s4 = veorq_u16(s3, m0); - #undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4)); - const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4)); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F); - const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f); - #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 ); - const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 ); - #else - const uint16x8_t onemask = { 0x0001, 0x0004, - 0x0010, 0x0040, - 0x0100, 0x0400, - 0x1000, 0x4000 }; - const uint16x8_t twomask = { 0x0002, 0x0008, - 0x0020, 0x0080, - 0x0200, 0x0800, - 0x2000, 0x8000 }; - #endif - const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask)); - const uint16_t mask = vaddvq_u16(combined); - // The following fast path may or may not be beneficial. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0}; - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle); - vst1q_u8(utf8_output, utf8_0); - utf8_output += 12; - vst1q_u8(utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const uint8x16_t shuffle0 = vld1q_u8(row0 + 1); - const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const uint8x16_t shuffle1 = vld1q_u8(row1 + 1); - const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1); - - vst1q_u8(utf8_output, utf8_0); - utf8_output += row0[0]; - vst1q_u8(utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - } - // At least one 32-bit word will produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes. - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word & 0xFFFF0000)==0) { - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), reinterpret_cast(utf8_output)); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), reinterpret_cast(utf8_output)); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast(utf8_output)); -} -/* end file src/arm64/arm_convert_utf32_to_utf8.cpp */ -/* begin file src/arm64/arm_convert_utf32_to_utf16.cpp */ -template -std::pair arm_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_out) { - uint16_t * utf16_output = reinterpret_cast(utf16_out); - const char32_t* end = buf + len; - - uint16x4_t forbidden_bytemask = vmov_n_u16(0x0); - - while(buf + 4 <= end) { - uint32x4_t in = vld1q_u32(reinterpret_cast(buf)); - - // Check if no bits set above 16th - if(vmaxvq_u32(in) <= 0xFFFF) { - uint16x4_t utf16_packed = vmovn_u32(in); - - const uint16x4_t v_d800 = vmov_n_u16((uint16_t)0xd800); - const uint16x4_t v_dfff = vmov_n_u16((uint16_t)0xdfff); - forbidden_bytemask = vorr_u16(vand_u16(vcle_u16(utf16_packed, v_dfff), vcge_u16(utf16_packed, v_d800)), forbidden_bytemask); - - if (!match_system(big_endian)) { utf16_packed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(utf16_packed))); } - vst1_u16(utf16_output, utf16_packed); - utf16_output += 4; - buf += 4; - } else { - size_t forward = 3; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, reinterpret_cast(utf16_output)); } - *utf16_output++ = !match_system(big_endian) ? char16_t(word >> 8 | word << 8) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(nullptr, reinterpret_cast(utf16_output)); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = uint16_t(high_surrogate >> 8 | high_surrogate << 8); - low_surrogate = uint16_t(low_surrogate << 8 | low_surrogate >> 8); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - // check for invalid input - if (vmaxv_u16(forbidden_bytemask) != 0) { - return std::make_pair(nullptr, reinterpret_cast(utf16_output)); - } - - return std::make_pair(buf, reinterpret_cast(utf16_output)); -} - - -template -std::pair arm_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_out) { - uint16_t * utf16_output = reinterpret_cast(utf16_out); - const char32_t* start = buf; - const char32_t* end = buf + len; - - while(buf + 4 <= end) { - uint32x4_t in = vld1q_u32(reinterpret_cast(buf)); - - // Check if no bits set above 16th - if(vmaxvq_u32(in) <= 0xFFFF) { - uint16x4_t utf16_packed = vmovn_u32(in); - - const uint16x4_t v_d800 = vmov_n_u16((uint16_t)0xd800); - const uint16x4_t v_dfff = vmov_n_u16((uint16_t)0xdfff); - const uint16x4_t forbidden_bytemask = vand_u16(vcle_u16(utf16_packed, v_dfff), vcge_u16(utf16_packed, v_d800)); - if (vmaxv_u16(forbidden_bytemask) != 0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), reinterpret_cast(utf16_output)); - } - - if (!match_system(big_endian)) { utf16_packed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(utf16_packed))); } - vst1_u16(utf16_output, utf16_packed); - utf16_output += 4; - buf += 4; - } else { - size_t forward = 3; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), reinterpret_cast(utf16_output)); } - *utf16_output++ = !match_system(big_endian) ? char16_t(word >> 8 | word << 8) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), reinterpret_cast(utf16_output)); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (!match_system(big_endian)) { - high_surrogate = uint16_t(high_surrogate >> 8 | high_surrogate << 8); - low_surrogate = uint16_t(low_surrogate << 8 | low_surrogate >> 8); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast(utf16_output)); -} -/* end file src/arm64/arm_convert_utf32_to_utf16.cpp */ -/* begin file src/arm64/arm_base64.cpp */ -/** - * References and further reading: - * - * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the - * speed of a memory copy, Software: Practice and Experience 50 (2), 2020. - * https://arxiv.org/abs/1910.05109 - * - * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 - * Instructions, ACM Transactions on the Web 12 (3), 2018. - * https://arxiv.org/abs/1704.00605 - * - * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings. - * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force, - * Request for Comments: 4648. - * - * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization. - * http://www.alfredklomp.com/programming/sse-base64/. (2014). - * - * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD - * acceleration. https://github.com/aklomp/base64. (2014). - * - * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014). - * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/ - * - * Nick Kopp. 2013. Base64 Encoding on a GPU. - * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). - */ - -size_t encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { - // credit: Wojciech Muła - uint8_t *out = (uint8_t *)dst; - constexpr static uint8_t source_table[64] = { - 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x', 'C', 'S', 'i', 'y', 'D', - 'T', 'j', 'z', 'E', 'U', 'k', '0', 'F', 'V', 'l', '1', 'G', 'W', - 'm', '2', 'H', 'X', 'n', '3', 'I', 'Y', 'o', '4', 'J', 'Z', 'p', - '5', 'K', 'a', 'q', '6', 'L', 'b', 'r', '7', 'M', 'c', 's', '8', - 'N', 'd', 't', '9', 'O', 'e', 'u', '+', 'P', 'f', 'v', '/', - }; - constexpr static uint8_t source_table_url[64] = { - 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x', 'C', 'S', 'i', 'y', 'D', - 'T', 'j', 'z', 'E', 'U', 'k', '0', 'F', 'V', 'l', '1', 'G', 'W', - 'm', '2', 'H', 'X', 'n', '3', 'I', 'Y', 'o', '4', 'J', 'Z', 'p', - '5', 'K', 'a', 'q', '6', 'L', 'b', 'r', '7', 'M', 'c', 's', '8', - 'N', 'd', 't', '9', 'O', 'e', 'u', '-', 'P', 'f', 'v', '_', - }; - const uint8x16_t v3f = vdupq_n_u8(0x3f); -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - // When trying to load a uint8_t array, Visual Studio might - // error with: error C2664: '__n128x4 neon_ld4m_q8(const char *)': - // cannot convert argument 1 from 'const uint8_t [64]' to 'const char * - const uint8x16x4_t table = - vld4q_u8((reinterpret_cast( - options & base64_url) ? source_table_url : source_table)); -#else - const uint8x16x4_t table = - vld4q_u8((options & base64_url) ? source_table_url : source_table); -#endif - size_t i = 0; - for (; i + 16 * 3 <= srclen; i += 16 * 3) { - const uint8x16x3_t in = vld3q_u8((const uint8_t *)src + i); - uint8x16x4_t result; - result.val[0] = vshrq_n_u8(in.val[0], 2); - result.val[1] = - vandq_u8(vsliq_n_u8(vshrq_n_u8(in.val[1], 4), in.val[0], 4), v3f); - result.val[2] = - vandq_u8(vsliq_n_u8(vshrq_n_u8(in.val[2], 6), in.val[1], 2), v3f); - result.val[3] = vandq_u8(in.val[2], v3f); - result.val[0] = vqtbl4q_u8(table, result.val[0]); - result.val[1] = vqtbl4q_u8(table, result.val[1]); - result.val[2] = vqtbl4q_u8(table, result.val[2]); - result.val[3] = vqtbl4q_u8(table, result.val[3]); - vst4q_u8(out, result); - out += 64; - } - out += scalar::base64::tail_encode_base64((char *)out, src + i, srclen - i, - options); - - return size_t((char *)out - dst); -} - -static inline void compress(uint8x16_t data, uint16_t mask, char *output) { - if (mask == 0) { - vst1q_u8((uint8_t *)output, data); - return; - } - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - uint64x2_t compactmasku64 = {tables::base64::thintable_epi8[mask1], - tables::base64::thintable_epi8[mask2]}; - uint8x16_t compactmask = vreinterpretq_u8_u64(compactmasku64); -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint8x16_t off = - simdutf_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8); -#else - const uint8x16_t off = {0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8}; -#endif - - compactmask = vaddq_u8(compactmask, off); - uint8x16_t pruned = vqtbl1q_u8(data, compactmask); - - int pop1 = tables::base64::BitsSetTable256mul2[mask1]; - // then load the corresponding mask, what it does is to write - // only the first pop1 bytes from the first 8 bytes, and then - // it fills in with the bytes from the second 8 bytes + some filling - // at the end. - compactmask = vld1q_u8(tables::base64::pshufb_combine_table + pop1 * 8); - uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); - vst1q_u8((uint8_t *)output, answer); -} - -struct block64 { - uint8x16_t chunks[4]; -}; -static_assert(sizeof(block64) == 64, "block64 is not 64 bytes"); -template uint64_t to_base64_mask(block64 *b, bool *error) { - uint8x16_t v0f = vdupq_n_u8(0xf); - - uint8x16_t underscore0, underscore1, underscore2, underscore3; - if (base64_url) { - underscore0 = vceqq_u8(b->chunks[0], vdupq_n_u8(0x5f)); - underscore1 = vceqq_u8(b->chunks[1], vdupq_n_u8(0x5f)); - underscore2 = vceqq_u8(b->chunks[2], vdupq_n_u8(0x5f)); - underscore3 = vceqq_u8(b->chunks[3], vdupq_n_u8(0x5f)); - } else { - (void)underscore0; - (void)underscore1; - (void)underscore2; - (void)underscore3; - } - - uint8x16_t lo_nibbles0 = vandq_u8(b->chunks[0], v0f); - uint8x16_t lo_nibbles1 = vandq_u8(b->chunks[1], v0f); - uint8x16_t lo_nibbles2 = vandq_u8(b->chunks[2], v0f); - uint8x16_t lo_nibbles3 = vandq_u8(b->chunks[3], v0f); - // Needed by the decoding step. - uint8x16_t hi_nibbles0 = vshrq_n_u8(b->chunks[0], 4); - uint8x16_t hi_nibbles1 = vshrq_n_u8(b->chunks[1], 4); - uint8x16_t hi_nibbles2 = vshrq_n_u8(b->chunks[2], 4); - uint8x16_t hi_nibbles3 = vshrq_n_u8(b->chunks[3], 4); - uint8x16_t lut_lo; -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - if (base64_url) { - lut_lo = - simdutf_make_uint8x16_t(0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, - 0x70, 0x61, 0xe1, 0xf4, 0xf5, 0xa5, 0xf4, 0xf4); - } else { - lut_lo = - simdutf_make_uint8x16_t(0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, - 0x70, 0x61, 0xe1, 0xb4, 0xf5, 0xe5, 0xf4, 0xb4); - } -#else - if (base64_url) { - lut_lo = uint8x16_t{0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, - 0x70, 0x61, 0xe1, 0xf4, 0xf5, 0xa5, 0xf4, 0xf4}; - } else { - lut_lo = uint8x16_t{0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, - 0x70, 0x61, 0xe1, 0xb4, 0xf5, 0xe5, 0xf4, 0xb4}; - } -#endif - uint8x16_t lo0 = vqtbl1q_u8(lut_lo, lo_nibbles0); - uint8x16_t lo1 = vqtbl1q_u8(lut_lo, lo_nibbles1); - uint8x16_t lo2 = vqtbl1q_u8(lut_lo, lo_nibbles2); - uint8x16_t lo3 = vqtbl1q_u8(lut_lo, lo_nibbles3); - uint8x16_t lut_hi; -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - if (base64_url) { - lut_hi = - simdutf_make_uint8x16_t(0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20); - } else { - lut_hi = - simdutf_make_uint8x16_t(0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20); - } -#else - if (base64_url) { - lut_hi = uint8x16_t{0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; - } else { - lut_hi = uint8x16_t{0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; - } -#endif - uint8x16_t hi0 = vqtbl1q_u8(lut_hi, hi_nibbles0); - uint8x16_t hi1 = vqtbl1q_u8(lut_hi, hi_nibbles1); - uint8x16_t hi2 = vqtbl1q_u8(lut_hi, hi_nibbles2); - uint8x16_t hi3 = vqtbl1q_u8(lut_hi, hi_nibbles3); - - if (base64_url) { - hi0 = vbicq_u8(hi0, underscore0); - hi1 = vbicq_u8(hi1, underscore1); - hi2 = vbicq_u8(hi2, underscore2); - hi3 = vbicq_u8(hi3, underscore3); - } - - uint8_t checks = - vmaxvq_u8(vorrq_u8(vorrq_u8(vandq_u8(lo0, hi0), vandq_u8(lo1, hi1)), - vorrq_u8(vandq_u8(lo2, hi2), vandq_u8(lo3, hi3)))); -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = - simdutf_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); -#else - const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; -#endif - uint64_t badcharmask = 0; - *error = checks > 0x3; - if (checks) { - // Add each of the elements next to each other, successively, to stuff each - // 8 byte mask into one. - uint8x16_t test0 = vtstq_u8(lo0, hi0); - uint8x16_t test1 = vtstq_u8(lo1, hi1); - uint8x16_t test2 = vtstq_u8(lo2, hi2); - uint8x16_t test3 = vtstq_u8(lo3, hi3); - uint8x16_t sum0 = - vpaddq_u8(vandq_u8(test0, bit_mask), vandq_u8(test1, bit_mask)); - uint8x16_t sum1 = - vpaddq_u8(vandq_u8(test2, bit_mask), vandq_u8(test3, bit_mask)); - sum0 = vpaddq_u8(sum0, sum1); - sum0 = vpaddq_u8(sum0, sum0); - badcharmask = vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0); - } - // This is the transformation step that can be done while we are waiting for - // sum0 - uint8x16_t roll_lut; -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO - if (base64_url) { - roll_lut = - simdutf_make_uint8x16_t(0xe0, 0x11, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0); - } else { - roll_lut = - simdutf_make_uint8x16_t(0x0, 0x10, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0); - } -#else - if (base64_url) { - roll_lut = uint8x16_t{0xe0, 0x11, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; - } else { - roll_lut = uint8x16_t{0x0, 0x10, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; - } -#endif - uint8x16_t vsecond_last = base64_url ? vdupq_n_u8(0x2d) : vdupq_n_u8(0x2f); - if (base64_url) { - hi_nibbles0 = vbicq_u8(hi_nibbles0, underscore0); - hi_nibbles1 = vbicq_u8(hi_nibbles1, underscore1); - hi_nibbles2 = vbicq_u8(hi_nibbles2, underscore2); - hi_nibbles3 = vbicq_u8(hi_nibbles3, underscore3); - } - uint8x16_t roll0 = vqtbl1q_u8( - roll_lut, vaddq_u8(vceqq_u8(b->chunks[0], vsecond_last), hi_nibbles0)); - uint8x16_t roll1 = vqtbl1q_u8( - roll_lut, vaddq_u8(vceqq_u8(b->chunks[1], vsecond_last), hi_nibbles1)); - uint8x16_t roll2 = vqtbl1q_u8( - roll_lut, vaddq_u8(vceqq_u8(b->chunks[2], vsecond_last), hi_nibbles2)); - uint8x16_t roll3 = vqtbl1q_u8( - roll_lut, vaddq_u8(vceqq_u8(b->chunks[3], vsecond_last), hi_nibbles3)); - b->chunks[0] = vaddq_u8(b->chunks[0], roll0); - b->chunks[1] = vaddq_u8(b->chunks[1], roll1); - b->chunks[2] = vaddq_u8(b->chunks[2], roll2); - b->chunks[3] = vaddq_u8(b->chunks[3], roll3); - return badcharmask; -} - -void copy_block(block64 *b, char *output) { - vst1q_u8((uint8_t *)output, b->chunks[0]); - vst1q_u8((uint8_t *)output + 16, b->chunks[1]); - vst1q_u8((uint8_t *)output + 32, b->chunks[2]); - vst1q_u8((uint8_t *)output + 48, b->chunks[3]); -} - -uint64_t compress_block(block64 *b, uint64_t mask, char *output) { - uint64_t popcounts = - vget_lane_u64(vreinterpret_u64_u8(vcnt_u8(vcreate_u8(~mask))), 0); - uint64_t offsets = popcounts * 0x0101010101010101; - compress(b->chunks[0], uint16_t(mask), output); - compress(b->chunks[1], uint16_t(mask >> 16), &output[(offsets >> 8) & 0xFF]); - compress(b->chunks[2], uint16_t(mask >> 32), &output[(offsets >> 24) & 0xFF]); - compress(b->chunks[3], uint16_t(mask >> 48), &output[(offsets >> 40) & 0xFF]); - return offsets >> 56; -} - -// The caller of this function is responsible to ensure that there are 64 bytes available -// from reading at src. The data is read into a block64 structure. -void load_block(block64 *b, const char *src) { - b->chunks[0] = vld1q_u8(reinterpret_cast(src)); - b->chunks[1] = vld1q_u8(reinterpret_cast(src) + 16); - b->chunks[2] = vld1q_u8(reinterpret_cast(src) + 32); - b->chunks[3] = vld1q_u8(reinterpret_cast(src) + 48); -} - -// The caller of this function is responsible to ensure that there are 32 bytes available -// from reading at data. It returns a 16-byte value, narrowing with saturation the 16-bit words. -inline uint8x16_t load_satured(const uint16_t *data) { - uint16x8_t in1 = vld1q_u16(data); - uint16x8_t in2 = vld1q_u16(data + 8); - return vqmovn_high_u16(vqmovn_u16(in1), in2); -} - -// The caller of this function is responsible to ensure that there are 128 bytes available -// from reading at src. The data is read into a block64 structure. -void load_block(block64 *b, const char16_t *src) { - b->chunks[0] = load_satured(reinterpret_cast(src)); - b->chunks[1] = load_satured(reinterpret_cast(src) + 16); - b->chunks[2] = load_satured(reinterpret_cast(src) + 32); - b->chunks[3] = load_satured(reinterpret_cast(src) + 48); -} - -// decode 64 bytes and output 48 bytes -void base64_decode_block(char *out, const char *src) { - uint8x16x4_t str = vld4q_u8((uint8_t *)src); - uint8x16x3_t outvec; - outvec.val[0] = - vorrq_u8(vshlq_n_u8(str.val[0], 2), vshrq_n_u8(str.val[1], 4)); - outvec.val[1] = - vorrq_u8(vshlq_n_u8(str.val[1], 4), vshrq_n_u8(str.val[2], 2)); - outvec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]); - vst3q_u8((uint8_t *)out, outvec); -} - -template -result compress_decode_base64(char *dst, const char_type *src, size_t srclen, - base64_options options) { - const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - size_t equalsigns = 0; - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 1; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 2; - } - } - const char_type *const srcinit = src; - const char *const dstinit = dst; - const char_type *const srcend = src + srclen; - - constexpr size_t block_size = 10; - char buffer[block_size * 64]; - char *bufferptr = buffer; - if (srclen >= 64) { - const char_type *const srcend64 = src + srclen - 64; - while (src <= srcend64) { - block64 b; - load_block(&b, src); - src += 64; - bool error = false; - uint64_t badcharmask = to_base64_mask(&b, &error); - if(badcharmask) - if (error) { - src -= 64; - - while (src < srcend && to_base64[uint8_t(*src)] <= 64) { - src++; - } - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - - if (badcharmask != 0) { - // optimization opportunity: check for simple masks like those made of - // continuous 1s followed by continuous 0s. And masks containing a - // single bad character. - - bufferptr += compress_block(&b, badcharmask, bufferptr); - } else { - // optimization opportunity: if bufferptr == buffer and mask == 0, we - // can avoid the call to compress_block and decode directly. - copy_block(&b, bufferptr); - bufferptr += 64; - // base64_decode_block(dst, &b); - // dst += 48; - } - if (bufferptr >= (block_size - 1) * 64 + buffer) { - for (size_t i = 0; i < (block_size - 1); i++) { - base64_decode_block(dst, buffer + i * 64); - dst += 48; - } - std::memcpy(buffer, buffer + (block_size - 1) * 64, - 64); // 64 might be too much - bufferptr -= (block_size - 1) * 64; - } - } - } - char *buffer_start = buffer; - // Optimization note: if this is almost full, then it is worth our - // time, otherwise, we should just decode directly. - int last_block = (int)((bufferptr - buffer_start) % 64); - if (last_block != 0 && srcend - src + last_block >= 64) { - while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - *bufferptr = char(val); - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - bufferptr += (val <= 63); - src++; - } - } - - for (; buffer_start + 64 <= bufferptr; buffer_start += 64) { - base64_decode_block(dst, buffer_start); - dst += 48; - } - if ((bufferptr - buffer_start) % 64 != 0) { - while (buffer_start + 4 < bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 4); - - dst += 3; - buffer_start += 4; - } - if (buffer_start + 4 <= bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - - dst += 3; - buffer_start += 4; - } - // we may have 1, 2 or 3 bytes left and we need to decode them so let us - // bring in src content - int leftover = int(bufferptr - buffer_start); - if (leftover > 0) { - while (leftover < 4 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - buffer_start[leftover] = char(val); - leftover += (val <= 63); - src++; - } - - if (leftover == 1) { - return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)}; - } - if (leftover == 2) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - dst += 1; - } else if (leftover == 3) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6) + - (uint32_t(buffer_start[2]) << 1 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - - std::memcpy(dst, &triple, 2); - dst += 2; - } else { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - dst += 3; - } - } - } - if (src < srcend + equalsigns) { - result r = - scalar::base64::base64_tail_decode(dst, src, srcend - src, options); - if (r.error == error_code::INVALID_BASE64_CHARACTER) { - r.count += size_t(src - srcinit); - return r; - } else { - r.count += size_t(dst - dstinit); - } - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - r.error = error_code::INVALID_BASE64_CHARACTER; - } - } - return r; - } - if(equalsigns > 0) { - if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)}; - } - } - return {SUCCESS, size_t(dst - dstinit)}; -} -/* end file src/arm64/arm_base64.cpp */ - -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* begin file src/generic/buf_block_reader.h */ -namespace simdutf { -namespace arm64 { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdutf_really_inline size_t block_index(); - simdutf_really_inline bool has_full_block() const; - simdutf_really_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdutf_really_inline size_t get_remainder(uint8_t *dst) const; - simdutf_really_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text_64(const uint8_t *text) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text(const simd8x64& in) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char * format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdutf_really_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdutf_really_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdutf_really_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdutf_really_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdutf_really_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/buf_block_reader.h */ -/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdutf_really_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1 - }; - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdutf_really_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdutf_really_inline void check_next_input(const simd8x64& input) { - if(simdutf_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - - } - } - - // do not forget to call check_eof! - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -/* begin file src/generic/utf8_validation/utf8_validator.h */ -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_validation { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return !c.errors(); -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -/** - * Validates that the string is actual UTF-8 and stops on errors. - */ -template -result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - if(c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input + count), length - count); - res.count += count; - return res; - } - reader.advance(); - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - if (c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input) + count, length - count); - res.count += count; - return res; - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_utf8_with_errors(const char * input, size_t length) { - return generic_validate_utf8_with_errors(reinterpret_cast(input),length); -} - -template -bool generic_validate_ascii(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - uint8_t blocks[64]{}; - simd::simd8x64 running_or(blocks); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - running_or |= in; - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - running_or |= in; - return running_or.is_ascii(); -} - -bool generic_validate_ascii(const char * input, size_t length) { - return generic_validate_ascii(reinterpret_cast(input),length); -} - -template -result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } - reader.advance(); - - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_ascii_with_errors(const char * input, size_t length) { - return generic_validate_ascii_with_errors(reinterpret_cast(input),length); -} - -} // namespace utf8_validation -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_validator.h */ -// transcoding from UTF-8 to UTF-16 -/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ - - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_to_utf16 { - -using namespace simd; - -template -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char16_t* utf16_output) noexcept { - // The implementation is not specific to haswell and should be moved to the generic directory. - size_t pos = 0; - char16_t* start{utf16_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - // this loop could be unrolled further. For example, we could process the mask - // far more than 64 bytes. - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // Slow path. We hope that the compiler will recognize that this is a slow path. - // Anything that is not a continuation mask is a 'leading byte', that is, the - // start of a new code point. - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - // The *start* of code points is not so useful, rather, we want the *end* of code points. - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times when using solely - // the slow/regular path, and at least four times if there are fast paths. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - // - // Thus we may allow convert_masked_utf8_to_utf16 to process - // more bytes at a time under a fast-path mode where 16 bytes - // are consumed at once (e.g., when encountering ASCII). - size_t consumed = convert_masked_utf8_to_utf16(input + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - utf16_output += scalar::utf8_to_utf16::convert_valid(input + pos, size - pos, utf16_output); - return utf16_output - start; -} - -} // namespace utf8_to_utf16 -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ -/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */ - - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_to_utf16 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - template - simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf16::convert(in + pos, size - pos, utf16_output); - if(howmany == 0) { return 0; } - utf16_output += howmany; - } - return utf16_output - start; - } - - template - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf16_output += res.count; - } - } - return result(error_code::SUCCESS, utf16_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf16 namespace -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */ -// transcoding from UTF-8 to UTF-32 -/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_to_utf32 { - -using namespace simd; - - -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char32_t* utf32_output) noexcept { - size_t pos = 0; - char32_t* start{utf32_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - size_t max_starting_point = (pos + 64) - 12; - while(pos < max_starting_point) { - size_t consumed = convert_masked_utf8_to_utf32(input + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - } - } - utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output); - return utf32_output - start; -} - - -} // namespace utf8_to_utf32 -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ -/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */ - - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_to_utf32 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - - simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output); - if(howmany == 0) { return 0; } - utf32_output += howmany; - } - return utf32_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - if(pos < size) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf32_output += res.count; - } - } - return result(error_code::SUCCESS, utf32_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf32 namespace -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */ -// other functions -/* begin file src/generic/utf8.h */ - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8 { - -using namespace simd; - -simdutf_really_inline size_t count_code_points(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.gt(-65); - count += count_ones(utf8_continuation_mask); - } - return count + scalar::utf8::count_code_points(in + pos, size - pos); -} - -simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - // We count one word for anything that is not a continuation (so - // leading bytes). - count += 64 - count_ones(utf8_continuation_mask); - int64_t utf8_4byte = input.gteq_unsigned(240); - count += count_ones(utf8_4byte); - } - return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos); -} -} // utf8 namespace -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8.h */ -/* begin file src/generic/utf16.h */ -namespace simdutf { -namespace arm64 { -namespace { -namespace utf16 { - -template -simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF); - count += count_ones(not_pair) / 2; - } - return count + scalar::utf16::count_code_points(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t ascii_mask = input.lteq(0x7F); - uint64_t twobyte_mask = input.lteq(0x7FF); - uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF); - - size_t ascii_count = count_ones(ascii_mask) / 2; - size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2; - size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2; - size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2; - count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count; - } - return count + scalar::utf16::utf8_length_from_utf16(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) { - return count_code_points(in, size); -} - -simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) { - size_t pos = 0; - - while (pos < size/32*32) { - simd16x32 input(reinterpret_cast(in + pos)); - input.swap_bytes(); - input.store(reinterpret_cast(output)); - pos += 32; - output += 32; - } - - scalar::utf16::change_endianness_utf16(in + pos, size - pos, output); -} - -} // utf16 -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf16.h */ -// transcoding from UTF-8 to Latin 1 -/* begin file src/generic/utf8_to_latin1/utf8_to_latin1.h */ - - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_to_latin1 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// For UTF-8 to Latin 1, we can allow any ASCII character, and any continuation byte, -// but the non-ASCII leading bytes must be 0b11000011 or 0b11000010 and nothing else. -// -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - constexpr const uint8_t FORBIDDEN = 0xff; - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - FORBIDDEN, - // 1110____ ________ - FORBIDDEN, - // 1111____ ________ - FORBIDDEN - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - FORBIDDEN, - // ____0101 ________ - FORBIDDEN, - // ____011_ ________ - FORBIDDEN, - FORBIDDEN, - - // ____1___ ________ - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - // ____1101 ________ - FORBIDDEN, - FORBIDDEN, - FORBIDDEN - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - this->error |= check_special_cases(input, prev1); - } - - - simdutf_really_inline size_t convert(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ... - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for. - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output); - if(howmany == 0) { return 0; } - latin1_output += howmany; - } - return latin1_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - latin1_output += res.count; - } - } - return result(error_code::SUCCESS, latin1_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace arm64 -} // namespace simdutf -/* end file src/generic/utf8_to_latin1/utf8_to_latin1.h */ -/* begin file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */ - - -namespace simdutf { -namespace arm64 { -namespace { -namespace utf8_to_latin1 { -using namespace simd; - - - simdutf_really_inline size_t convert_valid(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ... - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for. - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(pos < size) { - size_t howmany = scalar::utf8_to_latin1::convert_valid(in + pos, size - pos, latin1_output); - latin1_output += howmany; - } - return latin1_output - start; - } - - } -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace arm64 - // namespace simdutf -/* end file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */ - -// placeholder scalars - -// -// Implementation-specific overrides -// -namespace simdutf { -namespace arm64 { - -simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - if (length % 2 == 0) { - return arm_detect_encodings(input, length); - } else { - if (implementation::validate_utf8(input, length)) { - return simdutf::encoding_type::UTF8; - } else { - return simdutf::encoding_type::unspecified; - } - } -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return arm64::utf8_validation::generic_validate_utf8(buf,len); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept { - return arm64::utf8_validation::generic_validate_utf8_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept { - return arm64::utf8_validation::generic_validate_ascii(buf,len); -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept { - return arm64::utf8_validation::generic_validate_ascii_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept { - const char16_t* tail = arm_validate_utf16(buf, len); - if (tail) { - return scalar::utf16::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept { - const char16_t* tail = arm_validate_utf16(buf, len); - if (tail) { - return scalar::utf16::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept { - result res = arm_validate_utf16_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf16::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept { - result res = arm_validate_utf16_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf16::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept { - const char32_t* tail = arm_validate_utf32le(buf, len); - if (tail) { - return scalar::utf32::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept { - result res = arm_validate_utf32le_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf32::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = arm_convert_latin1_to_utf8(buf, len, utf8_output); - size_t converted_chars = ret.second - utf8_output; - - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = arm_convert_latin1_to_utf16(buf, len, utf16_output); - size_t converted_chars = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = arm_convert_latin1_to_utf16(buf, len, utf16_output); - size_t converted_chars = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = arm_convert_latin1_to_utf32(buf, len, utf32_output); - size_t converted_chars = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - utf8_to_latin1::validating_transcoder converter; - return converter.convert(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept { - utf8_to_latin1::validating_transcoder converter; - return converter.convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - return arm64::utf8_to_latin1::convert_valid(buf,len,latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* input, size_t size, - char16_t* utf16_output) const noexcept { - return utf8_to_utf16::convert_valid(input, size, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* input, size_t size, - char16_t* utf16_output) const noexcept { - return utf8_to_utf16::convert_valid(input, size, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - utf8_to_utf32::validating_transcoder converter; - return converter.convert(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - utf8_to_utf32::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size, - char32_t* utf32_output) const noexcept { - return utf8_to_utf32::convert_valid(input, size, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf16_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf16_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf16_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf16_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: implement a custom function. - return convert_utf16be_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: implement a custom function. - return convert_utf16le_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = arm_convert_utf16_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = arm_convert_utf16_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf16_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf16_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16le_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16be_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = arm_convert_utf32_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf32_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = arm_convert_utf16_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = arm_convert_utf16_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf16_to_utf32_with_errors(buf, len, utf32_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf16_to_utf32_with_errors(buf, len, utf32_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf32_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf32_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf32_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = arm_convert_utf32_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert_valid( - ret.first, len - (ret.first - buf), ret.second); - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - // optimization opportunity: implement a custom function. - return convert_utf32_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = arm_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = arm_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = arm_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16le(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16be(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return convert_utf16le_to_utf32(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return convert_utf16be_to_utf32(buf, len, utf32_output); -} - -void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept { - utf16::change_endianness_utf16(input, length, output); -} - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept { - return count_utf8(buf,len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept { - return scalar::utf16::latin1_length_from_utf16(length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept { - return scalar::utf32::latin1_length_from_utf32(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t length) const noexcept { - // See https://lemire.me/blog/2023/05/15/computing-the-utf-8-size-of-a-latin-1-string-quickly-arm-neon-edition/ - // credit to Pete Cawley - const uint8_t *data = reinterpret_cast(input); - uint64_t result = 0; - const int lanes = sizeof(uint8x16_t); - uint8_t rem = length % lanes; - const uint8_t *simd_end = data + (length / lanes) * lanes; - const uint8x16_t threshold = vdupq_n_u8(0x80); - for (; data < simd_end; data += lanes) { - // load 16 bytes - uint8x16_t input_vec = vld1q_u8(data); - // compare to threshold (0x80) - uint8x16_t withhighbit = vcgeq_u8(input_vec, threshold); - // vertical addition - result -= vaddvq_s8(vreinterpretq_s8_u8(withhighbit)); - } - return result + (length / lanes) * lanes + scalar::latin1::utf8_length_from_latin1((const char*)simd_end, rem); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::utf8_length_from_utf16(input, length); -} - - -simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf16_length_from_latin1(length); -} - - -simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf32_length_from_latin1(length); -} - - - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept { - return utf8::utf16_length_from_utf8(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const uint32x4_t v_7f = vmovq_n_u32((uint32_t)0x7f); - const uint32x4_t v_7ff = vmovq_n_u32((uint32_t)0x7ff); - const uint32x4_t v_ffff = vmovq_n_u32((uint32_t)0xffff); - const uint32x4_t v_1 = vmovq_n_u32((uint32_t)0x1); - size_t pos = 0; - size_t count = 0; - for(;pos + 4 <= length; pos += 4) { - uint32x4_t in = vld1q_u32(reinterpret_cast(input + pos)); - const uint32x4_t ascii_bytes_bytemask = vcleq_u32(in, v_7f); - const uint32x4_t one_two_bytes_bytemask = vcleq_u32(in, v_7ff); - const uint32x4_t two_bytes_bytemask = veorq_u32(one_two_bytes_bytemask, ascii_bytes_bytemask); - const uint32x4_t three_bytes_bytemask = veorq_u32(vcleq_u32(in, v_ffff), one_two_bytes_bytemask); - - const uint16x8_t reduced_ascii_bytes_bytemask = vreinterpretq_u16_u32(vandq_u32(ascii_bytes_bytemask, v_1)); - const uint16x8_t reduced_two_bytes_bytemask = vreinterpretq_u16_u32(vandq_u32(two_bytes_bytemask, v_1)); - const uint16x8_t reduced_three_bytes_bytemask = vreinterpretq_u16_u32(vandq_u32(three_bytes_bytemask, v_1)); - - const uint16x8_t compressed_bytemask0 = vpaddq_u16(reduced_ascii_bytes_bytemask, reduced_two_bytes_bytemask); - const uint16x8_t compressed_bytemask1 = vpaddq_u16(reduced_three_bytes_bytemask, reduced_three_bytes_bytemask); - - size_t ascii_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask0), 0)); - size_t two_bytes_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask0), 1)); - size_t three_bytes_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask1), 0)); - - count += 16 - 3*ascii_count - 2*two_bytes_count - three_bytes_count; - } - return count + scalar::utf32::utf8_length_from_utf32(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const uint32x4_t v_ffff = vmovq_n_u32((uint32_t)0xffff); - const uint32x4_t v_1 = vmovq_n_u32((uint32_t)0x1); - size_t pos = 0; - size_t count = 0; - for(;pos + 4 <= length; pos += 4) { - uint32x4_t in = vld1q_u32(reinterpret_cast(input + pos)); - const uint32x4_t surrogate_bytemask = vcgtq_u32(in, v_ffff); - const uint16x8_t reduced_bytemask = vreinterpretq_u16_u32(vandq_u32(surrogate_bytemask, v_1)); - const uint16x8_t compressed_bytemask = vpaddq_u16(reduced_bytemask, reduced_bytemask); - size_t surrogate_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask), 0)); - count += 4 + surrogate_count; - } - return count + scalar::utf32::utf16_length_from_utf32(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - return encode_base64(output, input, length, options); -} - - -} // namespace arm64 -} // namespace simdutf - -/* begin file src/simdutf/arm64/end.h */ -/* end file src/simdutf/arm64/end.h */ -/* end file src/arm64/implementation.cpp */ -#endif -#if SIMDUTF_IMPLEMENTATION_FALLBACK -/* begin file src/fallback/implementation.cpp */ -/* begin file src/simdutf/fallback/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "fallback" -// #define SIMDUTF_IMPLEMENTATION fallback -/* end file src/simdutf/fallback/begin.h */ - - - - - - - - - -namespace simdutf { -namespace fallback { - -simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - int out = 0; - if(validate_utf8(input, length)) { out |= encoding_type::UTF8; } - if((length % 2) == 0) { - if(validate_utf16le(reinterpret_cast(input), length/2)) { out |= encoding_type::UTF16_LE; } - } - if((length % 4) == 0) { - if(validate_utf32(reinterpret_cast(input), length/4)) { out |= encoding_type::UTF32_LE; } - } - - return out; -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return scalar::utf8::validate(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept { - return scalar::utf8::validate_with_errors(buf, len); -} - -simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept { - return scalar::ascii::validate(buf, len); -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept { - return scalar::ascii::validate_with_errors(buf, len); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate(buf, len); -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate_with_errors(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate_with_errors(buf, len); -} - -simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept { - return scalar::utf32::validate(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept { - return scalar::utf32::validate_with_errors(buf, len); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept { - return scalar::latin1_to_utf8::convert(buf,len,utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::latin1_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::latin1_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::latin1_to_utf32::convert(buf,len,utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf8_to_latin1::convert(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf8_to_latin1::convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf8_to_latin1::convert_valid(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf8_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf8_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf8_to_utf16::convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf8_to_utf16::convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf8_to_utf16::convert_valid(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf8_to_utf16::convert_valid(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf8_to_utf32::convert(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf8_to_utf32::convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size, - char32_t* utf32_output) const noexcept { - return scalar::utf8_to_utf32::convert_valid(input, size, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf16_to_latin1::convert(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf16_to_latin1::convert(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf16_to_latin1::convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf16_to_latin1::convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf16_to_latin1::convert_valid(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf16_to_latin1::convert_valid(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert(buf, len, utf8_output); -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_with_errors(buf, len, utf8_output); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_with_errors(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_valid(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_valid(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf32_to_latin1::convert(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf32_to_latin1::convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return scalar::utf32_to_latin1::convert_valid(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf32_to_utf8::convert(buf, len, utf8_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf32_to_utf8::convert_with_errors(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf32_to_utf8::convert_valid(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_valid(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_valid(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_valid(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_valid(buf, len, utf32_output); -} - -void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept { - scalar::utf16::change_endianness_utf16(input, length, output); -} - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept { - return scalar::utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept { - return scalar::utf8::count_code_points(buf,len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept { - return scalar::utf16::latin1_length_from_utf16(length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept { - return length; -} - -simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t length) const noexcept { - return scalar::latin1::utf8_length_from_latin1(input,length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf16_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept { - return scalar::utf8::utf16_length_from_utf8(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept { - return scalar::utf32::utf8_length_from_utf32(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept { - return scalar::utf32::utf16_length_from_utf32(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf32_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept { - return scalar::utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - size_t equallocation = length; // location of the first padding character if any - size_t equalsigns = 0; - if(length > 0 && input[length - 1] == '=') { - length -= 1; - equalsigns++; - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - if(length > 0 && input[length - 1] == '=') { - equalsigns++; - length -= 1; - } - } - if(length == 0) { - if(equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - return {SUCCESS, 0}; - } - result r = scalar::base64::base64_tail_decode(output, input, length, options); - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - } - return r; -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - size_t equallocation = length; // location of the first padding character if any - size_t equalsigns = 0; - if(length > 0 && input[length - 1] == '=') { - length -= 1; - equalsigns++; - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - if(length > 0 && input[length - 1] == '=') { - equalsigns++; - length -= 1; - } - } - if(length == 0) { - if(equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - return {SUCCESS, 0}; - } - result r = scalar::base64::base64_tail_decode(output, input, length, options); - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - } - return r; -} - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - return scalar::base64::tail_encode_base64(output, input, length, options); -} -} // namespace fallback -} // namespace simdutf - -/* begin file src/simdutf/fallback/end.h */ -/* end file src/simdutf/fallback/end.h */ -/* end file src/fallback/implementation.cpp */ -#endif -#if SIMDUTF_IMPLEMENTATION_ICELAKE -/* begin file src/icelake/implementation.cpp */ - - -/* begin file src/simdutf/icelake/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "icelake" -// #define SIMDUTF_IMPLEMENTATION icelake - -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE -// nothing needed. -#else -SIMDUTF_TARGET_ICELAKE -#endif - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) -#endif // end of workaround -/* end file src/simdutf/icelake/begin.h */ -namespace simdutf { -namespace icelake { -namespace { -#ifndef SIMDUTF_ICELAKE_H -#error "icelake.h must be included" -#endif -/* begin file src/icelake/icelake_utf8_common.inl.cpp */ -// Common procedures for both validating and non-validating conversions from UTF-8. -enum block_processing_mode { SIMDUTF_FULL, SIMDUTF_TAIL}; - -using utf8_to_utf16_result = std::pair; -using utf8_to_utf32_result = std::pair; - -/* - process_block_utf8_to_utf16 converts up to 64 bytes from 'in' from UTF-8 - to UTF-16. When tail = SIMDUTF_FULL, then the full input buffer (64 bytes) - might be used. When tail = SIMDUTF_TAIL, we take into account 'gap' which - indicates how many input bytes are relevant. - - Returns true when the result is correct, otherwise it returns false. - - The provided in and out pointers are advanced according to how many input - bytes have been processed, upon success. -*/ -template -simdutf_really_inline bool process_block_utf8_to_utf16(const char *&in, char16_t *&out, size_t gap) { - // constants - __m512i mask_identity = _mm512_set_epi8(63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); - __m512i mask_c0c0c0c0 = _mm512_set1_epi32(0xc0c0c0c0); - __m512i mask_80808080 = _mm512_set1_epi32(0x80808080); - __m512i mask_f0f0f0f0 = _mm512_set1_epi32(0xf0f0f0f0); - __m512i mask_dfdfdfdf_tail = _mm512_set_epi64(0xffffdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf); - __m512i mask_c2c2c2c2 = _mm512_set1_epi32(0xc2c2c2c2); - __m512i mask_ffffffff = _mm512_set1_epi32(0xffffffff); - __m512i mask_d7c0d7c0 = _mm512_set1_epi32(0xd7c0d7c0); - __m512i mask_dc00dc00 = _mm512_set1_epi32(0xdc00dc00); - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - // Note that 'tail' is a compile-time constant ! - __mmask64 b = (tail == SIMDUTF_FULL) ? 0xFFFFFFFFFFFFFFFF : (uint64_t(1) << gap) - 1; - __m512i input = (tail == SIMDUTF_FULL) ? _mm512_loadu_si512(in) : _mm512_maskz_loadu_epi8(b, in); - __mmask64 m1 = (tail == SIMDUTF_FULL) ? _mm512_cmplt_epu8_mask(input, mask_80808080) : _mm512_mask_cmplt_epu8_mask(b, input, mask_80808080); - if(_ktestc_mask64_u8(m1, b)) {// NOT(m1) AND b -- if all zeroes, then all ASCII - // alternatively, we could do 'if (m1 == b) { ' - if (tail == SIMDUTF_FULL) { - in += 64; // consumed 64 bytes - // we convert a full 64-byte block, writing 128 bytes. - __m512i input1 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(input)); - if(big_endian) { input1 = _mm512_shuffle_epi8(input1, byteflip); } - _mm512_storeu_si512(out, input1); - out += 32; - __m512i input2 = _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(input, 1)); - if(big_endian) { input2 = _mm512_shuffle_epi8(input2, byteflip); } - _mm512_storeu_si512(out, input2); - out += 32; - return true; // we are done - } else { - in += gap; - if (gap <= 32) { - __m512i input1 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(input)); - if(big_endian) { input1 = _mm512_shuffle_epi8(input1, byteflip); } - _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << (gap)) - 1), input1); - out += gap; - } else { - __m512i input1 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(input)); - if(big_endian) { input1 = _mm512_shuffle_epi8(input1, byteflip); } - _mm512_storeu_si512(out, input1); - out += 32; - __m512i input2 = _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(input, 1)); - if(big_endian) { input2 = _mm512_shuffle_epi8(input2, byteflip); } - _mm512_mask_storeu_epi16(out, __mmask32((uint32_t(1) << (gap - 32)) - 1), input2); - out += gap - 32; - } - return true; // we are done - } - } - // classify characters further - __mmask64 m234 = _mm512_cmp_epu8_mask(mask_c0c0c0c0, input, - _MM_CMPINT_LE); // 0xc0 <= input, 2, 3, or 4 leading byte - __mmask64 m34 = _mm512_cmp_epu8_mask(mask_dfdfdfdf_tail, input, - _MM_CMPINT_LT); // 0xdf < input, 3 or 4 leading byte - - __mmask64 milltwobytes = _mm512_mask_cmp_epu8_mask(m234, input, mask_c2c2c2c2, - _MM_CMPINT_LT); // 0xc0 <= input < 0xc2 (illegal two byte sequence) - // Overlong 2-byte sequence - if (_ktestz_mask64_u8(milltwobytes, milltwobytes) == 0) { - // Overlong 2-byte sequence - return false; - } - if (_ktestz_mask64_u8(m34, m34) == 0) { - // We have a 3-byte sequence and/or a 2-byte sequence, or possibly even a 4-byte sequence! - __mmask64 m4 = _mm512_cmp_epu8_mask(input, mask_f0f0f0f0, - _MM_CMPINT_NLT); // 0xf0 <= zmm0 (4 byte start bytes) - - __mmask64 mask_not_ascii = (tail == SIMDUTF_FULL) ? _knot_mask64(m1) : _kand_mask64(_knot_mask64(m1), b); - - __mmask64 mp1 = _kshiftli_mask64(m234, 1); - __mmask64 mp2 = _kshiftli_mask64(m34, 2); - // We could do it as follows... - // if (_kortestz_mask64_u8(m4,m4)) { // compute the bitwise OR of the 64-bit masks a and b and return 1 if all zeroes - // but GCC generates better code when we do: - if (m4 == 0) { // compute the bitwise OR of the 64-bit masks a and b and return 1 if all zeroes - // Fast path with 1,2,3 bytes - __mmask64 mc = _kor_mask64(mp1, mp2); // expected continuation bytes - __mmask64 m1234 = _kor_mask64(m1, m234); - // mismatched continuation bytes: - if (tail == SIMDUTF_FULL) { - __mmask64 xnormcm1234 = _kxnor_mask64(mc, m1234); // XNOR of mc and m1234 should be all zero if they differ - // the presence of a 1 bit indicates that they overlap. - // _kortestz_mask64_u8: compute the bitwise OR of 64-bit masksand return 1 if all zeroes. - if (!_kortestz_mask64_u8(xnormcm1234, xnormcm1234)) { return false; } - } else { - __mmask64 bxorm1234 = _kxor_mask64(b, m1234); - if (mc != bxorm1234) { return false; } - } - // mend: identifying the last bytes of each sequence to be decoded - __mmask64 mend = _kshiftri_mask64(m1234, 1); - if (tail != SIMDUTF_FULL) { - mend = _kor_mask64(mend, (uint64_t(1) << (gap - 1))); - } - - - __m512i last_and_third = _mm512_maskz_compress_epi8(mend, mask_identity); - __m512i last_and_thirdu16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(last_and_third)); - - __m512i nonasciitags = _mm512_maskz_mov_epi8(mask_not_ascii, mask_c0c0c0c0); // ASCII: 00000000 other: 11000000 - __m512i clearedbytes = _mm512_andnot_si512(nonasciitags, input); // high two bits cleared where not ASCII - __m512i lastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, last_and_thirdu16, - clearedbytes); // the last byte of each character - - __mmask64 mask_before_non_ascii = _kshiftri_mask64(mask_not_ascii, 1); // bytes that precede non-ASCII bytes - __m512i indexofsecondlastbytes = _mm512_add_epi16(mask_ffffffff, last_and_thirdu16); // indices of the second last bytes - __m512i beforeasciibytes = _mm512_maskz_mov_epi8(mask_before_non_ascii, clearedbytes); - __m512i secondlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofsecondlastbytes, - beforeasciibytes); // the second last bytes (of two, three byte seq, - // surrogates) - secondlastbytes = _mm512_slli_epi16(secondlastbytes, 6); // shifted into position - - __m512i indexofthirdlastbytes = _mm512_add_epi16(mask_ffffffff, - indexofsecondlastbytes); // indices of the second last bytes - __m512i thirdlastbyte = _mm512_maskz_mov_epi8(m34, - clearedbytes); // only those that are the third last byte of a sequence - __m512i thirdlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofthirdlastbytes, - thirdlastbyte); // the third last bytes (of three byte sequences, hi - // surrogate) - thirdlastbytes = _mm512_slli_epi16(thirdlastbytes, 12); // shifted into position - __m512i Wout = _mm512_ternarylogic_epi32(lastbytes, secondlastbytes, thirdlastbytes, 254); - // the elements of Wout excluding the last element if it happens to be a high surrogate: - - __mmask64 mprocessed = (tail == SIMDUTF_FULL) ? _pdep_u64(0xFFFFFFFF, mend) : _pdep_u64(0xFFFFFFFF, _kand_mask64(mend, b)); // we adjust mend at the end of the output. - - - // Encodings out of range... - { - // the location of 3-byte sequence start bytes in the input - __mmask64 m3 = m34 & (b ^ m4); - // code units in Wout corresponding to 3-byte sequences. - __mmask32 M3 = __mmask32(_pext_u64(m3 << 2, mend)); - __m512i mask_08000800 = _mm512_set1_epi32(0x08000800); - __mmask32 Msmall800 = _mm512_mask_cmplt_epu16_mask(M3, Wout, mask_08000800); - __m512i mask_d800d800 = _mm512_set1_epi32(0xd800d800); - __m512i Moutminusd800 = _mm512_sub_epi16(Wout, mask_d800d800); - __mmask32 M3s = _mm512_mask_cmplt_epu16_mask(M3, Moutminusd800, mask_08000800); - if (_kor_mask32(Msmall800, M3s)) { return false; } - } - int64_t nout = _mm_popcnt_u64(mprocessed); - in += 64 - _lzcnt_u64(mprocessed); - if(big_endian) { Wout = _mm512_shuffle_epi8(Wout, byteflip); } - _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), Wout); - out += nout; - return true; // ok - } - // - // We have a 4-byte sequence, this is the general case. - // Slow! - __mmask64 mp3 = _kshiftli_mask64(m4, 3); - __mmask64 mc = _kor_mask64(_kor_mask64(mp1, mp2), mp3); // expected continuation bytes - __mmask64 m1234 = _kor_mask64(m1, m234); - - // mend: identifying the last bytes of each sequence to be decoded - __mmask64 mend = _kor_mask64(_kshiftri_mask64(_kor_mask64(mp3, m1234), 1), mp3); - if (tail != SIMDUTF_FULL) { - mend = _kor_mask64(mend, __mmask64(uint64_t(1) << (gap - 1))); - } - __m512i last_and_third = _mm512_maskz_compress_epi8(mend, mask_identity); - __m512i last_and_thirdu16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(last_and_third)); - - __m512i nonasciitags = _mm512_maskz_mov_epi8(mask_not_ascii, mask_c0c0c0c0); // ASCII: 00000000 other: 11000000 - __m512i clearedbytes = _mm512_andnot_si512(nonasciitags, input); // high two bits cleared where not ASCII - __m512i lastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, last_and_thirdu16, - clearedbytes); // the last byte of each character - - __mmask64 mask_before_non_ascii = _kshiftri_mask64(mask_not_ascii, 1); // bytes that precede non-ASCII bytes - __m512i indexofsecondlastbytes = _mm512_add_epi16(mask_ffffffff, last_and_thirdu16); // indices of the second last bytes - __m512i beforeasciibytes = _mm512_maskz_mov_epi8(mask_before_non_ascii, clearedbytes); - __m512i secondlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofsecondlastbytes, - beforeasciibytes); // the second last bytes (of two, three byte seq, - // surrogates) - secondlastbytes = _mm512_slli_epi16(secondlastbytes, 6); // shifted into position - - __m512i indexofthirdlastbytes = _mm512_add_epi16(mask_ffffffff, - indexofsecondlastbytes); // indices of the second last bytes - __m512i thirdlastbyte = _mm512_maskz_mov_epi8(m34, - clearedbytes); // only those that are the third last byte of a sequence - __m512i thirdlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofthirdlastbytes, - thirdlastbyte); // the third last bytes (of three byte sequences, hi - // surrogate) - thirdlastbytes = _mm512_slli_epi16(thirdlastbytes, 12); // shifted into position - __m512i thirdsecondandlastbytes = _mm512_ternarylogic_epi32(lastbytes, secondlastbytes, thirdlastbytes, 254); - uint64_t Mlo_uint64 = _pext_u64(mp3, mend); - __mmask32 Mlo = __mmask32(Mlo_uint64); - __mmask32 Mhi = __mmask32(Mlo_uint64 >> 1); - __m512i lo_surr_mask = _mm512_maskz_mov_epi16(Mlo, - mask_dc00dc00); // lo surr: 1101110000000000, other: 0000000000000000 - __m512i shifted4_thirdsecondandlastbytes = _mm512_srli_epi16(thirdsecondandlastbytes, - 4); // hi surr: 00000WVUTSRQPNML vuts = WVUTS - 1 - __m512i tagged_lo_surrogates = _mm512_or_si512(thirdsecondandlastbytes, - lo_surr_mask); // lo surr: 110111KJHGFEDCBA, other: unchanged - __m512i Wout = _mm512_mask_add_epi16(tagged_lo_surrogates, Mhi, shifted4_thirdsecondandlastbytes, - mask_d7c0d7c0); // hi sur: 110110vutsRQPNML, other: unchanged - // the elements of Wout excluding the last element if it happens to be a high surrogate: - __mmask32 Mout = ~(Mhi & 0x80000000); - __mmask64 mprocessed = (tail == SIMDUTF_FULL) ? _pdep_u64(Mout, mend) : _pdep_u64(Mout, _kand_mask64(mend, b)); // we adjust mend at the end of the output. - - - // mismatched continuation bytes: - if (tail == SIMDUTF_FULL) { - __mmask64 xnormcm1234 = _kxnor_mask64(mc, m1234); // XNOR of mc and m1234 should be all zero if they differ - // the presence of a 1 bit indicates that they overlap. - // _kortestz_mask64_u8: compute the bitwise OR of 64-bit masksand return 1 if all zeroes. - if (!_kortestz_mask64_u8(xnormcm1234, xnormcm1234)) { return false; } - } else { - __mmask64 bxorm1234 = _kxor_mask64(b, m1234); - if (mc != bxorm1234) { return false; } - } - // Encodings out of range... - { - // the location of 3-byte sequence start bytes in the input - __mmask64 m3 = m34 & (b ^ m4); - // code units in Wout corresponding to 3-byte sequences. - __mmask32 M3 = __mmask32(_pext_u64(m3 << 2, mend)); - __m512i mask_08000800 = _mm512_set1_epi32(0x08000800); - __mmask32 Msmall800 = _mm512_mask_cmplt_epu16_mask(M3, Wout, mask_08000800); - __m512i mask_d800d800 = _mm512_set1_epi32(0xd800d800); - __m512i Moutminusd800 = _mm512_sub_epi16(Wout, mask_d800d800); - __mmask32 M3s = _mm512_mask_cmplt_epu16_mask(M3, Moutminusd800, mask_08000800); - __m512i mask_04000400 = _mm512_set1_epi32(0x04000400); - __mmask32 M4s = _mm512_mask_cmpge_epu16_mask(Mhi, Moutminusd800, mask_04000400); - if (!_kortestz_mask32_u8(M4s, _kor_mask32(Msmall800, M3s))) { return false; } - } - in += 64 - _lzcnt_u64(mprocessed); - int64_t nout = _mm_popcnt_u64(mprocessed); - if(big_endian) { Wout = _mm512_shuffle_epi8(Wout, byteflip); } - _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), Wout); - out += nout; - return true; // ok - } - // Fast path 2: all ASCII or 2 byte - __mmask64 continuation_or_ascii = (tail == SIMDUTF_FULL) ? _knot_mask64(m234) : _kand_mask64(_knot_mask64(m234), b); - // on top of -0xc0 we subtract -2 which we get back later of the - // continuation byte tags - __m512i leading2byte = _mm512_maskz_sub_epi8(m234, input, mask_c2c2c2c2); - __mmask64 leading = tail == (tail == SIMDUTF_FULL) ? _kor_mask64(m1, m234) : _kand_mask64(_kor_mask64(m1, m234), b); // first bytes of each sequence - if (tail == SIMDUTF_FULL) { - __mmask64 xnor234leading = _kxnor_mask64(_kshiftli_mask64(m234, 1), leading); - if (!_kortestz_mask64_u8(xnor234leading, xnor234leading)) { return false; } - } else { - __mmask64 bxorleading = _kxor_mask64(b, leading); - if (_kshiftli_mask64(m234, 1) != bxorleading) { return false; } - } - // - if (tail == SIMDUTF_FULL) { - // In the two-byte/ASCII scenario, we are easily latency bound, so we want - // to increment the input buffer as quickly as possible. - // We process 32 bytes unless the byte at index 32 is a continuation byte, - // in which case we include it as well for a total of 33 bytes. - // Note that if x is an ASCII byte, then the following is false: - // int8_t(x) <= int8_t(0xc0) under two's complement. - in += 32; - if(int8_t(*in) <= int8_t(0xc0)) in++; - // The alternative is to do - // in += 64 - _lzcnt_u64(_pdep_u64(0xFFFFFFFF, continuation_or_ascii)); - // but it requires loading the input, doing the mask computation, and converting - // back the mask to a general register. It just takes too long, leaving the - // processor likely to be idle. - } else { - in += 64 - _lzcnt_u64(_pdep_u64(0xFFFFFFFF, continuation_or_ascii)); - } - __m512i lead = _mm512_maskz_compress_epi8(leading, leading2byte); // will contain zero for ascii, and the data - lead = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(lead)); // ... zero extended into code units - __m512i follow = _mm512_maskz_compress_epi8(continuation_or_ascii, input); // the last bytes of each sequence - follow = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(follow)); // ... zero extended into code units - lead = _mm512_slli_epi16(lead, 6); // shifted into position - __m512i final = _mm512_add_epi16(follow, lead); // combining lead and follow - - if(big_endian) { final = _mm512_shuffle_epi8(final, byteflip); } - if (tail == SIMDUTF_FULL) { - // Next part is UTF-16 specific and can be generalized to UTF-32. - int nout = _mm_popcnt_u32(uint32_t(leading)); - _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), final); - out += nout; // UTF-8 to UTF-16 is only expansionary in this case. - } else { - int nout = int(_mm_popcnt_u64(_pdep_u64(0xFFFFFFFF, leading))); - _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), final); - out += nout; // UTF-8 to UTF-16 is only expansionary in this case. - } - - return true; // we are fine. -} - - - - -/* - utf32_to_utf16_masked converts `count` lower UTF-32 code units - from input `utf32` into UTF-16. It differs from utf32_to_utf16 - in that it 'masks' the writes. - - Returns how many 16-bit code units were stored. - - byteflip is used for flipping 16-bit code units, and it should be - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - We pass it to the (always inlined) function to encourage the compiler to - keep the value in a (constant) register. -*/ -template -simdutf_really_inline size_t utf32_to_utf16_masked(const __m512i byteflip, __m512i utf32, unsigned int count, char16_t* output) { - - const __mmask16 valid = uint16_t((1 << count) - 1); - // 1. check if we have any surrogate pairs - const __m512i v_0000_ffff = _mm512_set1_epi32(0x0000ffff); - const __mmask16 sp_mask = _mm512_mask_cmpgt_epu32_mask(valid, utf32, v_0000_ffff); - - if (sp_mask == 0) { - if(big_endian) { - _mm256_mask_storeu_epi16((__m256i*)output, valid, _mm256_shuffle_epi8(_mm512_cvtepi32_epi16(utf32), _mm512_castsi512_si256(byteflip))); - - } else { - _mm256_mask_storeu_epi16((__m256i*)output, valid, _mm512_cvtepi32_epi16(utf32)); - } - return count; - } - - { - // build surrogate pair code units in 32-bit lanes - - // t0 = 8 x [000000000000aaaa|aaaaaabbbbbbbbbb] - const __m512i v_0001_0000 = _mm512_set1_epi32(0x00010000); - const __m512i t0 = _mm512_sub_epi32(utf32, v_0001_0000); - - // t1 = 8 x [000000aaaaaaaaaa|bbbbbbbbbb000000] - const __m512i t1 = _mm512_slli_epi32(t0, 6); - - // t2 = 8 x [000000aaaaaaaaaa|aaaaaabbbbbbbbbb] -- copy hi word from t1 to t0 - // 0xe4 = (t1 and v_ffff_0000) or (t0 and not v_ffff_0000) - const __m512i v_ffff_0000 = _mm512_set1_epi32(0xffff0000); - const __m512i t2 = _mm512_ternarylogic_epi32(t1, t0, v_ffff_0000, 0xe4); - - // t2 = 8 x [110110aaaaaaaaaa|110111bbbbbbbbbb] -- copy hi word from t1 to t0 - // 0xba = (t2 and not v_fc00_fc000) or v_d800_dc00 - const __m512i v_fc00_fc00 = _mm512_set1_epi32(0xfc00fc00); - const __m512i v_d800_dc00 = _mm512_set1_epi32(0xd800dc00); - const __m512i t3 = _mm512_ternarylogic_epi32(t2, v_fc00_fc00, v_d800_dc00, 0xba); - const __m512i t4 = _mm512_mask_blend_epi32(sp_mask, utf32, t3); - __m512i t5 = _mm512_ror_epi32(t4, 16); - // Here we want to trim all of the upper 16-bit code units from the 2-byte - // characters represented as 4-byte values. We can compute it from - // sp_mask or the following... It can be more optimized! - const __mmask32 nonzero = _kor_mask32(0xaaaaaaaa,_mm512_cmpneq_epi16_mask(t5, _mm512_setzero_si512())); - const __mmask32 nonzero_masked = _kand_mask32(nonzero, __mmask32((uint64_t(1) << (2*count)) - 1)); - if(big_endian) { t5 = _mm512_shuffle_epi8(t5, byteflip); } - // we deliberately avoid _mm512_mask_compressstoreu_epi16 for portability (zen4) - __m512i compressed = _mm512_maskz_compress_epi16(nonzero_masked, t5); - _mm512_mask_storeu_epi16(output, (1<<(count + static_cast(count_ones(sp_mask)))) - 1, compressed); - //_mm512_mask_compressstoreu_epi16(output, nonzero_masked, t5); - } - - return count + static_cast(count_ones(sp_mask)); -} - -/* - utf32_to_utf16 converts `count` lower UTF-32 code units - from input `utf32` into UTF-16. It may overflow. - - Returns how many 16-bit code units were stored. - - byteflip is used for flipping 16-bit code units, and it should be - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - We pass it to the (always inlined) function to encourage the compiler to - keep the value in a (constant) register. -*/ -template -simdutf_really_inline size_t utf32_to_utf16(const __m512i byteflip, __m512i utf32, unsigned int count, char16_t* output) { - // check if we have any surrogate pairs - const __m512i v_0000_ffff = _mm512_set1_epi32(0x0000ffff); - const __mmask16 sp_mask = _mm512_cmpgt_epu32_mask(utf32, v_0000_ffff); - - if (sp_mask == 0) { - // technically, it should be _mm256_storeu_epi16 - if(big_endian) { - _mm256_storeu_si256((__m256i*)output, _mm256_shuffle_epi8(_mm512_cvtepi32_epi16(utf32),_mm512_castsi512_si256(byteflip))); - } else { - _mm256_storeu_si256((__m256i*)output, _mm512_cvtepi32_epi16(utf32)); - } - return count; - } - - { - // build surrogate pair code units in 32-bit lanes - - // t0 = 8 x [000000000000aaaa|aaaaaabbbbbbbbbb] - const __m512i v_0001_0000 = _mm512_set1_epi32(0x00010000); - const __m512i t0 = _mm512_sub_epi32(utf32, v_0001_0000); - - // t1 = 8 x [000000aaaaaaaaaa|bbbbbbbbbb000000] - const __m512i t1 = _mm512_slli_epi32(t0, 6); - - // t2 = 8 x [000000aaaaaaaaaa|aaaaaabbbbbbbbbb] -- copy hi word from t1 to t0 - // 0xe4 = (t1 and v_ffff_0000) or (t0 and not v_ffff_0000) - const __m512i v_ffff_0000 = _mm512_set1_epi32(0xffff0000); - const __m512i t2 = _mm512_ternarylogic_epi32(t1, t0, v_ffff_0000, 0xe4); - - // t2 = 8 x [110110aaaaaaaaaa|110111bbbbbbbbbb] -- copy hi word from t1 to t0 - // 0xba = (t2 and not v_fc00_fc000) or v_d800_dc00 - const __m512i v_fc00_fc00 = _mm512_set1_epi32(0xfc00fc00); - const __m512i v_d800_dc00 = _mm512_set1_epi32(0xd800dc00); - const __m512i t3 = _mm512_ternarylogic_epi32(t2, v_fc00_fc00, v_d800_dc00, 0xba); - const __m512i t4 = _mm512_mask_blend_epi32(sp_mask, utf32, t3); - __m512i t5 = _mm512_ror_epi32(t4, 16); - const __mmask32 nonzero = _kor_mask32(0xaaaaaaaa,_mm512_cmpneq_epi16_mask(t5, _mm512_setzero_si512())); - if(big_endian) { t5 = _mm512_shuffle_epi8(t5, byteflip); } - // we deliberately avoid _mm512_mask_compressstoreu_epi16 for portability (zen4) - __m512i compressed = _mm512_maskz_compress_epi16(nonzero, t5); - _mm512_mask_storeu_epi16(output, (1<<(count + static_cast(count_ones(sp_mask)))) - 1, compressed); - //_mm512_mask_compressstoreu_epi16(output, nonzero, t5); - } - - return count + static_cast(count_ones(sp_mask)); -} - -/** - * Store the last N bytes of previous followed by 512-N bytes from input. - */ -template -__m512i prev(__m512i input, __m512i previous) { - static_assert(N<=32, "N must be no larger than 32"); - const __m512i movemask = _mm512_setr_epi32(28,29,30,31,0,1,2,3,4,5,6,7,8,9,10,11); - const __m512i rotated = _mm512_permutex2var_epi32(input, movemask, previous); -#if SIMDUTF_GCC8 || SIMDUTF_GCC9 - constexpr int shift = 16-N; // workaround for GCC8,9 - return _mm512_alignr_epi8(input, rotated, shift); -#else - return _mm512_alignr_epi8(input, rotated, 16-N); -#endif // SIMDUTF_GCC8 || SIMDUTF_GCC9 -} - -template -__m512i shuffle_epi128(__m512i v) { - static_assert((idx0 >= 0 && idx0 <= 3), "idx0 must be in range 0..3"); - static_assert((idx1 >= 0 && idx1 <= 3), "idx1 must be in range 0..3"); - static_assert((idx2 >= 0 && idx2 <= 3), "idx2 must be in range 0..3"); - static_assert((idx3 >= 0 && idx3 <= 3), "idx3 must be in range 0..3"); - - constexpr unsigned shuffle = idx0 | (idx1 << 2) | (idx2 << 4) | (idx3 << 6); - return _mm512_shuffle_i32x4(v, v, shuffle); -} - -template -constexpr __m512i broadcast_epi128(__m512i v) { - return shuffle_epi128(v); -} - -/** - * Current unused. - */ -template -__m512i rotate_by_N_epi8(const __m512i input) { - - // lanes order: 1, 2, 3, 0 => 0b00_11_10_01 - const __m512i permuted = _mm512_shuffle_i32x4(input, input, 0x39); - - return _mm512_alignr_epi8(permuted, input, N); -} - -/* - expanded_utf8_to_utf32 converts expanded UTF-8 characters (`utf8`) - stored at separate 32-bit lanes. - - For each lane we have also a character class (`char_class), given in form - 0x8080800N, where N is 4 highest bits from the leading byte; 0x80 resets - corresponding bytes during pshufb. -*/ -simdutf_really_inline __m512i expanded_utf8_to_utf32(__m512i char_class, __m512i utf8) { - /* - Input: - - utf8: bytes stored at separate 32-bit code units - - valid: which code units have valid UTF-8 characters - - Bit layout of single word. We show 4 cases for each possible - UTF-8 character encoding. The `?` denotes bits we must not - assume their value. - - |10dd.dddd|10cc.cccc|10bb.bbbb|1111.0aaa| 4-byte char - |????.????|10cc.cccc|10bb.bbbb|1110.aaaa| 3-byte char - |????.????|????.????|10bb.bbbb|110a.aaaa| 2-byte char - |????.????|????.????|????.????|0aaa.aaaa| ASCII char - byte 3 byte 2 byte 1 byte 0 - */ - - /* 1. Reset control bits of continuation bytes and the MSB - of the leading byte; this makes all bytes unsigned (and - does not alter ASCII char). - - |00dd.dddd|00cc.cccc|00bb.bbbb|0111.0aaa| 4-byte char - |00??.????|00cc.cccc|00bb.bbbb|0110.aaaa| 3-byte char - |00??.????|00??.????|00bb.bbbb|010a.aaaa| 2-byte char - |00??.????|00??.????|00??.????|0aaa.aaaa| ASCII char - ^^ ^^ ^^ ^ - */ - __m512i values; - const __m512i v_3f3f_3f7f = _mm512_set1_epi32(0x3f3f3f7f); - values = _mm512_and_si512(utf8, v_3f3f_3f7f); - - /* 2. Swap and join fields A-B and C-D - - |0000.cccc|ccdd.dddd|0001.110a|aabb.bbbb| 4-byte char - |0000.cccc|cc??.????|0001.10aa|aabb.bbbb| 3-byte char - |0000.????|????.????|0001.0aaa|aabb.bbbb| 2-byte char - |0000.????|????.????|000a.aaaa|aa??.????| ASCII char */ - const __m512i v_0140_0140 = _mm512_set1_epi32(0x01400140); - values = _mm512_maddubs_epi16(values, v_0140_0140); - - /* 3. Swap and join fields AB & CD - - |0000.0001|110a.aabb|bbbb.cccc|ccdd.dddd| 4-byte char - |0000.0001|10aa.aabb|bbbb.cccc|cc??.????| 3-byte char - |0000.0001|0aaa.aabb|bbbb.????|????.????| 2-byte char - |0000.000a|aaaa.aa??|????.????|????.????| ASCII char */ - const __m512i v_0001_1000 = _mm512_set1_epi32(0x00011000); - values = _mm512_madd_epi16(values, v_0001_1000); - - /* 4. Shift left the values by variable amounts to reset highest UTF-8 bits - |aaab.bbbb|bccc.cccd|dddd.d000|0000.0000| 4-byte char -- by 11 - |aaaa.bbbb|bbcc.cccc|????.??00|0000.0000| 3-byte char -- by 10 - |aaaa.abbb|bbb?.????|????.???0|0000.0000| 2-byte char -- by 9 - |aaaa.aaa?|????.????|????.????|?000.0000| ASCII char -- by 7 */ - { - /** pshufb - - continuation = 0 - ascii = 7 - _2_bytes = 9 - _3_bytes = 10 - _4_bytes = 11 - - shift_left_v3 = 4 * [ - ascii, # 0000 - ascii, # 0001 - ascii, # 0010 - ascii, # 0011 - ascii, # 0100 - ascii, # 0101 - ascii, # 0110 - ascii, # 0111 - continuation, # 1000 - continuation, # 1001 - continuation, # 1010 - continuation, # 1011 - _2_bytes, # 1100 - _2_bytes, # 1101 - _3_bytes, # 1110 - _4_bytes, # 1111 - ] */ - const __m512i shift_left_v3 = _mm512_setr_epi64( - 0x0707070707070707, - 0x0b0a090900000000, - 0x0707070707070707, - 0x0b0a090900000000, - 0x0707070707070707, - 0x0b0a090900000000, - 0x0707070707070707, - 0x0b0a090900000000 - ); - - const __m512i shift = _mm512_shuffle_epi8(shift_left_v3, char_class); - values = _mm512_sllv_epi32(values, shift); - } - - /* 5. Shift right the values by variable amounts to reset lowest bits - |0000.0000|000a.aabb|bbbb.cccc|ccdd.dddd| 4-byte char -- by 11 - |0000.0000|0000.0000|aaaa.bbbb|bbcc.cccc| 3-byte char -- by 16 - |0000.0000|0000.0000|0000.0aaa|aabb.bbbb| 2-byte char -- by 21 - |0000.0000|0000.0000|0000.0000|0aaa.aaaa| ASCII char -- by 25 */ - { - // 4 * [25, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 21, 21, 16, 11] - const __m512i shift_right = _mm512_setr_epi64( - 0x1919191919191919, - 0x0b10151500000000, - 0x1919191919191919, - 0x0b10151500000000, - 0x1919191919191919, - 0x0b10151500000000, - 0x1919191919191919, - 0x0b10151500000000 - ); - - const __m512i shift = _mm512_shuffle_epi8(shift_right, char_class); - values = _mm512_srlv_epi32(values, shift); - } - - return values; -} - - -simdutf_really_inline __m512i expand_and_identify(__m512i lane0, __m512i lane1, int &count) { - const __m512i merged = _mm512_mask_mov_epi32(lane0, 0x1000, lane1); - const __m512i expand_ver2 = _mm512_setr_epi64( - 0x0403020103020100, - 0x0605040305040302, - 0x0807060507060504, - 0x0a09080709080706, - 0x0c0b0a090b0a0908, - 0x0e0d0c0b0d0c0b0a, - 0x000f0e0d0f0e0d0c, - 0x0201000f01000f0e - ); - const __m512i input = _mm512_shuffle_epi8(merged, expand_ver2); - const __m512i v_0000_00c0 = _mm512_set1_epi32(0xc0); - const __m512i t0 = _mm512_and_si512(input, v_0000_00c0); - const __m512i v_0000_0080 = _mm512_set1_epi32(0x80); - const __mmask16 leading_bytes = _mm512_cmpneq_epu32_mask(t0, v_0000_0080); - count = static_cast(count_ones(leading_bytes)); - return _mm512_mask_compress_epi32(_mm512_setzero_si512(), leading_bytes, input); -} - -simdutf_really_inline __m512i expand_utf8_to_utf32(__m512i input) { - __m512i char_class = _mm512_srli_epi32(input, 4); - /* char_class = ((input >> 4) & 0x0f) | 0x80808000 */ - const __m512i v_0000_000f = _mm512_set1_epi32(0x0f); - const __m512i v_8080_8000 = _mm512_set1_epi32(0x80808000); - char_class = _mm512_ternarylogic_epi32(char_class, v_0000_000f, v_8080_8000, 0xea); - return expanded_utf8_to_utf32(char_class, input); -} -/* end file src/icelake/icelake_utf8_common.inl.cpp */ -/* begin file src/icelake/icelake_macros.inl.cpp */ - -/* - This upcoming macro (SIMDUTF_ICELAKE_TRANSCODE16) takes 16 + 4 bytes (of a UTF-8 string) - and loads all possible 4-byte substring into an AVX512 register. - - For example if we have bytes abcdefgh... we create following 32-bit lanes - - [abcd|bcde|cdef|defg|efgh|...] - ^ ^ - byte 0 of reg byte 63 of reg -*/ -/** pshufb - # lane{0,1,2} have got bytes: [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15] - # lane3 has got bytes: [ 16, 17, 18, 19, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15] - - expand_ver2 = [ - # lane 0: - 0, 1, 2, 3, - 1, 2, 3, 4, - 2, 3, 4, 5, - 3, 4, 5, 6, - - # lane 1: - 4, 5, 6, 7, - 5, 6, 7, 8, - 6, 7, 8, 9, - 7, 8, 9, 10, - - # lane 2: - 8, 9, 10, 11, - 9, 10, 11, 12, - 10, 11, 12, 13, - 11, 12, 13, 14, - - # lane 3 order: 13, 14, 15, 16 14, 15, 16, 17, 15, 16, 17, 18, 16, 17, 18, 19 - 12, 13, 14, 15, - 13, 14, 15, 0, - 14, 15, 0, 1, - 15, 0, 1, 2, - ] -*/ - -#define SIMDUTF_ICELAKE_TRANSCODE16(LANE0, LANE1, MASKED) \ - { \ - const __m512i merged = _mm512_mask_mov_epi32(LANE0, 0x1000, LANE1); \ - const __m512i expand_ver2 = _mm512_setr_epi64( \ - 0x0403020103020100, \ - 0x0605040305040302, \ - 0x0807060507060504, \ - 0x0a09080709080706, \ - 0x0c0b0a090b0a0908, \ - 0x0e0d0c0b0d0c0b0a, \ - 0x000f0e0d0f0e0d0c, \ - 0x0201000f01000f0e \ - ); \ - const __m512i input = _mm512_shuffle_epi8(merged, expand_ver2); \ - \ - __mmask16 leading_bytes; \ - const __m512i v_0000_00c0 = _mm512_set1_epi32(0xc0); \ - const __m512i t0 = _mm512_and_si512(input, v_0000_00c0); \ - const __m512i v_0000_0080 = _mm512_set1_epi32(0x80); \ - leading_bytes = _mm512_cmpneq_epu32_mask(t0, v_0000_0080); \ - \ - __m512i char_class; \ - char_class = _mm512_srli_epi32(input, 4); \ - /* char_class = ((input >> 4) & 0x0f) | 0x80808000 */ \ - const __m512i v_0000_000f = _mm512_set1_epi32(0x0f); \ - const __m512i v_8080_8000 = _mm512_set1_epi32(0x80808000); \ - char_class = _mm512_ternarylogic_epi32(char_class, v_0000_000f, v_8080_8000, 0xea); \ - \ - const int valid_count = static_cast(count_ones(leading_bytes)); \ - const __m512i utf32 = expanded_utf8_to_utf32(char_class, input); \ - \ - const __m512i out = _mm512_mask_compress_epi32(_mm512_setzero_si512(), leading_bytes, utf32); \ - \ - if (UTF32) { \ - if(MASKED) { \ - const __mmask16 valid = uint16_t((1 << valid_count) - 1); \ - _mm512_mask_storeu_epi32((__m512i*)output, valid, out); \ - } else { \ - _mm512_storeu_si512((__m512i*)output, out); \ - } \ - output += valid_count; \ - } else { \ - if(MASKED) { \ - output += utf32_to_utf16_masked(byteflip, out, valid_count, reinterpret_cast(output)); \ - } else { \ - output += utf32_to_utf16(byteflip, out, valid_count, reinterpret_cast(output)); \ - } \ - } \ - } - -#define SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(INPUT, VALID_COUNT, MASKED) \ -{ \ - if (UTF32) { \ - if(MASKED) { \ - const __mmask16 valid_mask = uint16_t((1 << VALID_COUNT) - 1); \ - _mm512_mask_storeu_epi32((__m512i*)output, valid_mask, INPUT); \ - } else { \ - _mm512_storeu_si512((__m512i*)output, INPUT); \ - } \ - output += VALID_COUNT; \ - } else { \ - if(MASKED) { \ - output += utf32_to_utf16_masked(byteflip, INPUT, VALID_COUNT, reinterpret_cast(output)); \ - } else { \ - output += utf32_to_utf16(byteflip, INPUT, VALID_COUNT, reinterpret_cast(output)); \ - } \ - } \ -} - - -#define SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output) \ - if (UTF32) { \ - const __m128i t0 = _mm512_castsi512_si128(utf8); \ - const __m128i t1 = _mm512_extracti32x4_epi32(utf8, 1); \ - const __m128i t2 = _mm512_extracti32x4_epi32(utf8, 2); \ - const __m128i t3 = _mm512_extracti32x4_epi32(utf8, 3); \ - _mm512_storeu_si512((__m512i*)(output + 0*16), _mm512_cvtepu8_epi32(t0)); \ - _mm512_storeu_si512((__m512i*)(output + 1*16), _mm512_cvtepu8_epi32(t1)); \ - _mm512_storeu_si512((__m512i*)(output + 2*16), _mm512_cvtepu8_epi32(t2)); \ - _mm512_storeu_si512((__m512i*)(output + 3*16), _mm512_cvtepu8_epi32(t3)); \ - } else { \ - const __m256i h0 = _mm512_castsi512_si256(utf8); \ - const __m256i h1 = _mm512_extracti64x4_epi64(utf8, 1); \ - if(big_endian) { \ - _mm512_storeu_si512((__m512i*)(output + 0*16), _mm512_shuffle_epi8(_mm512_cvtepu8_epi16(h0), byteflip)); \ - _mm512_storeu_si512((__m512i*)(output + 2*16), _mm512_shuffle_epi8(_mm512_cvtepu8_epi16(h1), byteflip)); \ - } else { \ - _mm512_storeu_si512((__m512i*)(output + 0*16), _mm512_cvtepu8_epi16(h0)); \ - _mm512_storeu_si512((__m512i*)(output + 2*16), _mm512_cvtepu8_epi16(h1)); \ - } \ - } -/* end file src/icelake/icelake_macros.inl.cpp */ -/* begin file src/icelake/icelake_from_valid_utf8.inl.cpp */ -// file included directly - -// File contains conversion procedure from VALID UTF-8 strings. - -/* - valid_utf8_to_fixed_length converts a valid UTF-8 string into UTF-32. - - The `OUTPUT` template type decides what to do with UTF-32: store - it directly or convert into UTF-16 (with AVX512). - - Input: - - str - valid UTF-8 string - - len - string length - - out_buffer - output buffer - - Result: - - pair.first - the first unprocessed input byte - - pair.second - the first unprocessed output word -*/ -template -std::pair valid_utf8_to_fixed_length(const char* str, size_t len, OUTPUT* dwords) { - constexpr bool UTF32 = std::is_same::value; - constexpr bool UTF16 = std::is_same::value; - static_assert(UTF32 or UTF16, "output type has to be uint32_t (for UTF-32) or char16_t (for UTF-16)"); - static_assert(!(UTF32 and big_endian), "we do not currently support big-endian UTF-32"); - - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - const char* ptr = str; - const char* end = ptr + len; - - OUTPUT* output = dwords; - /** - * In the main loop, we consume 64 bytes per iteration, - * but we access 64 + 4 bytes. - * We check for ptr + 64 + 64 <= end because - * we want to be do maskless writes without overruns. - */ - while (ptr + 64 + 64 <= end) { - const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr); - const __m512i v_80 = _mm512_set1_epi8(char(0x80)); - const __mmask64 ascii = _mm512_test_epi8_mask(utf8, v_80); - if(ascii == 0) { - SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output) - output += 64; - ptr += 64; - continue; - } - - const __m512i lane0 = broadcast_epi128<0>(utf8); - const __m512i lane1 = broadcast_epi128<1>(utf8); - int valid_count0; - __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0); - const __m512i lane2 = broadcast_epi128<2>(utf8); - int valid_count1; - __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1); - if(valid_count0 + valid_count1 <= 16) { - vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<(utf8); - int valid_count2; - __m512i vec2 = expand_and_identify(lane2, lane3, valid_count2); - uint32_t tmp1; - ::memcpy(&tmp1, ptr + 64, sizeof(tmp1)); - const __m512i lane4 = _mm512_set1_epi32(tmp1); - int valid_count3; - __m512i vec3 = expand_and_identify(lane3, lane4, valid_count3); - if(valid_count2 + valid_count3 <= 16) { - vec2 = _mm512_mask_expand_epi32(vec2, __mmask16(((1<(utf8); - const __m512i lane1 = broadcast_epi128<1>(utf8); - int valid_count0; - __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0); - const __m512i lane2 = broadcast_epi128<2>(utf8); - int valid_count1; - __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1); - if(valid_count0 + valid_count1 <= 16) { - vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<(utf8); - SIMDUTF_ICELAKE_TRANSCODE16(lane2, lane3, true) - - ptr += 3*16; - } - } - return {ptr, output}; -} - - -using utf8_to_utf16_result = std::pair; -/* end file src/icelake/icelake_from_valid_utf8.inl.cpp */ -/* begin file src/icelake/icelake_utf8_validation.inl.cpp */ -// file included directly - - -simdutf_really_inline __m512i check_special_cases(__m512i input, const __m512i prev1) { - __m512i mask1 = _mm512_setr_epi64( - 0x0202020202020202, - 0x4915012180808080, - 0x0202020202020202, - 0x4915012180808080, - 0x0202020202020202, - 0x4915012180808080, - 0x0202020202020202, - 0x4915012180808080); - const __m512i v_0f = _mm512_set1_epi8(0x0f); - __m512i index1 = _mm512_and_si512(_mm512_srli_epi16(prev1, 4), v_0f); - - __m512i byte_1_high = _mm512_shuffle_epi8(mask1, index1); - __m512i mask2 = _mm512_setr_epi64( - 0xcbcbcb8b8383a3e7, - 0xcbcbdbcbcbcbcbcb, - 0xcbcbcb8b8383a3e7, - 0xcbcbdbcbcbcbcbcb, - 0xcbcbcb8b8383a3e7, - 0xcbcbdbcbcbcbcbcb, - 0xcbcbcb8b8383a3e7, - 0xcbcbdbcbcbcbcbcb); - __m512i index2 = _mm512_and_si512(prev1, v_0f); - - __m512i byte_1_low = _mm512_shuffle_epi8(mask2, index2); - __m512i mask3 = _mm512_setr_epi64( - 0x101010101010101, - 0x1010101babaaee6, - 0x101010101010101, - 0x1010101babaaee6, - 0x101010101010101, - 0x1010101babaaee6, - 0x101010101010101, - 0x1010101babaaee6 - ); - __m512i index3 = _mm512_and_si512(_mm512_srli_epi16(input, 4), v_0f); - __m512i byte_2_high = _mm512_shuffle_epi8(mask3, index3); - return _mm512_ternarylogic_epi64(byte_1_high, byte_1_low, byte_2_high, 128); - } - - simdutf_really_inline __m512i check_multibyte_lengths(const __m512i input, - const __m512i prev_input, const __m512i sc) { - __m512i prev2 = prev<2>(input, prev_input); - __m512i prev3 = prev<3>(input, prev_input); - __m512i is_third_byte = _mm512_subs_epu8(prev2, _mm512_set1_epi8(0b11100000u-1)); // Only 111_____ will be > 0 - __m512i is_fourth_byte = _mm512_subs_epu8(prev3, _mm512_set1_epi8(0b11110000u-1)); // Only 1111____ will be > 0 - __m512i is_third_or_fourth_byte = _mm512_or_si512(is_third_byte, is_fourth_byte); - const __m512i v_7f = _mm512_set1_epi8(char(0x7f)); - is_third_or_fourth_byte = _mm512_adds_epu8(v_7f, is_third_or_fourth_byte); - // We want to compute (is_third_or_fourth_byte AND v80) XOR sc. - const __m512i v_80 = _mm512_set1_epi8(char(0x80)); - return _mm512_ternarylogic_epi32(is_third_or_fourth_byte, v_80, sc, 0b1101010); - //__m512i is_third_or_fourth_byte_mask = _mm512_and_si512(is_third_or_fourth_byte, v_80); - //return _mm512_xor_si512(is_third_or_fourth_byte_mask, sc); - } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdutf_really_inline __m512i is_incomplete(const __m512i input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ - __m512i max_value = _mm512_setr_epi64( - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xbfdfefffffffffff); - return _mm512_subs_epu8(input, max_value); - } - - struct avx512_utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - __m512i error{}; - - // The last input we received - __m512i prev_input_block{}; - // Whether the last input we received was incomplete (used for ASCII fast path) - __m512i prev_incomplete{}; - - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const __m512i input, const __m512i prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - __m512i prev1 = prev<1>(input, prev_input); - __m512i sc = check_special_cases(input, prev1); - this->error = _mm512_or_si512(check_multibyte_lengths(input, prev_input, sc), this->error); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdutf_really_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error = _mm512_or_si512(this->error, this->prev_incomplete); - } - - // returns true if ASCII. - simdutf_really_inline bool check_next_input(const __m512i input) { - const __m512i v_80 = _mm512_set1_epi8(char(0x80)); - const __mmask64 ascii = _mm512_test_epi8_mask(input, v_80); - if(ascii == 0) { - this->error = _mm512_or_si512(this->error, this->prev_incomplete); - return true; - } else { - this->check_utf8_bytes(input, this->prev_input_block); - this->prev_incomplete = is_incomplete(input); - this->prev_input_block = input; - return false; - } - } - // do not forget to call check_eof! - simdutf_really_inline bool errors() const { - return _mm512_test_epi8_mask(this->error, this->error) != 0; - } - - }; // struct avx512_utf8_checker -/* end file src/icelake/icelake_utf8_validation.inl.cpp */ -/* begin file src/icelake/icelake_from_utf8.inl.cpp */ -// file included directly - -// File contains conversion procedure from possibly invalid UTF-8 strings. - -/** - * Attempts to convert up to len 1-byte code units from in (in UTF-8 format) to - * out. - * Returns the position of the input and output after the processing is - * completed. Upon error, the output is set to null. - */ - -template -utf8_to_utf16_result fast_avx512_convert_utf8_to_utf16(const char *in, size_t len, char16_t *out) { - const char *const final_in = in + len; - bool result = true; - while (result) { - if (in + 64 <= final_in) { - result = process_block_utf8_to_utf16(in, out, final_in - in); - } else if(in < final_in) { - result = process_block_utf8_to_utf16(in, out, final_in - in); - } else { break; } - } - if(!result) { out = nullptr; } - return std::make_pair(in, out); -} - -template -simdutf::result fast_avx512_convert_utf8_to_utf16_with_errors(const char *in, size_t len, char16_t *out) { - const char *const init_in = in; - const char16_t *const init_out = out; - const char *const final_in = in + len; - bool result = true; - while (result) { - if (in + 64 <= final_in) { - result = process_block_utf8_to_utf16(in, out, final_in - in); - } else if(in < final_in) { - result = process_block_utf8_to_utf16(in, out, final_in - in); - } else { break; } - } - if(!result) { - // rewind_and_convert_with_errors will seek a potential error from in onward, - // with the ability to go back up to in - init_in bytes, and read final_in - in bytes forward. - simdutf::result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(in - init_in, in, final_in - in, out); - res.count += (in - init_in); - return res; - } else { - return simdutf::result(error_code::SUCCESS,out - init_out); - } -} - - -template -std::pair validating_utf8_to_fixed_length(const char* str, size_t len, OUTPUT* dwords) { - constexpr bool UTF32 = std::is_same::value; - constexpr bool UTF16 = std::is_same::value; - static_assert(UTF32 or UTF16, "output type has to be uint32_t (for UTF-32) or char16_t (for UTF-16)"); - static_assert(!(UTF32 and big_endian), "we do not currently support big-endian UTF-32"); - - const char* ptr = str; - const char* end = ptr + len; - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - OUTPUT* output = dwords; - avx512_utf8_checker checker{}; - /** - * In the main loop, we consume 64 bytes per iteration, - * but we access 64 + 4 bytes. - * We check for ptr + 64 + 64 <= end because - * we want to be do maskless writes without overruns. - */ - while (ptr + 64 + 64 <= end) { - const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr); - if(checker.check_next_input(utf8)) { - SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output) - output += 64; - ptr += 64; - continue; - } - const __m512i lane0 = broadcast_epi128<0>(utf8); - const __m512i lane1 = broadcast_epi128<1>(utf8); - int valid_count0; - __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0); - const __m512i lane2 = broadcast_epi128<2>(utf8); - int valid_count1; - __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1); - if(valid_count0 + valid_count1 <= 16) { - vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<(utf8); - int valid_count2; - __m512i vec2 = expand_and_identify(lane2, lane3, valid_count2); - uint32_t tmp1; - ::memcpy(&tmp1, ptr + 64, sizeof(tmp1)); - const __m512i lane4 = _mm512_set1_epi32(tmp1); - int valid_count3; - __m512i vec3 = expand_and_identify(lane3, lane4, valid_count3); - if(valid_count2 + valid_count3 <= 16) { - vec2 = _mm512_mask_expand_epi32(vec2, __mmask16(((1<(utf8); - const __m512i lane1 = broadcast_epi128<1>(utf8); - int valid_count0; - __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0); - const __m512i lane2 = broadcast_epi128<2>(utf8); - int valid_count1; - __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1); - if(valid_count0 + valid_count1 <= 16) { - vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<(utf8); - SIMDUTF_ICELAKE_TRANSCODE16(lane2, lane3, true) - - ptr += 3*16; - } - validatedptr += 4*16; - } - { - const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - validatedptr))-1, (const __m512i*)validatedptr); - checker.check_next_input(utf8); - } - checker.check_eof(); - if(checker.errors()) { - return {ptr, nullptr}; // We found an error. - } - return {ptr, output}; -} - -// Like validating_utf8_to_fixed_length but returns as soon as an error is identified -template -std::tuple validating_utf8_to_fixed_length_with_constant_checks(const char* str, size_t len, OUTPUT* dwords) { - constexpr bool UTF32 = std::is_same::value; - constexpr bool UTF16 = std::is_same::value; - static_assert(UTF32 or UTF16, "output type has to be uint32_t (for UTF-32) or char16_t (for UTF-16)"); - static_assert(!(UTF32 and big_endian), "we do not currently support big-endian UTF-32"); - - const char* ptr = str; - const char* end = ptr + len; - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - OUTPUT* output = dwords; - avx512_utf8_checker checker{}; - /** - * In the main loop, we consume 64 bytes per iteration, - * but we access 64 + 4 bytes. - * We check for ptr + 64 + 64 <= end because - * we want to be do maskless writes without overruns. - */ - while (ptr + 64 + 64 <= end) { - const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr); - if(checker.check_next_input(utf8)) { - SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output) - output += 64; - ptr += 64; - continue; - } - if(checker.errors()) { - return {ptr, output, false}; // We found an error. - } - const __m512i lane0 = broadcast_epi128<0>(utf8); - const __m512i lane1 = broadcast_epi128<1>(utf8); - int valid_count0; - __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0); - const __m512i lane2 = broadcast_epi128<2>(utf8); - int valid_count1; - __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1); - if(valid_count0 + valid_count1 <= 16) { - vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<(utf8); - int valid_count2; - __m512i vec2 = expand_and_identify(lane2, lane3, valid_count2); - uint32_t tmp1; - ::memcpy(&tmp1, ptr + 64, sizeof(tmp1)); - const __m512i lane4 = _mm512_set1_epi32(tmp1); - int valid_count3; - __m512i vec3 = expand_and_identify(lane3, lane4, valid_count3); - if(valid_count2 + valid_count3 <= 16) { - vec2 = _mm512_mask_expand_epi32(vec2, __mmask16(((1<(utf8); - const __m512i lane1 = broadcast_epi128<1>(utf8); - int valid_count0; - __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0); - const __m512i lane2 = broadcast_epi128<2>(utf8); - int valid_count1; - __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1); - if(valid_count0 + valid_count1 <= 16) { - vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<(utf8); - SIMDUTF_ICELAKE_TRANSCODE16(lane2, lane3, true) - - ptr += 3*16; - } - validatedptr += 4*16; - } - { - const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - validatedptr))-1, (const __m512i*)validatedptr); - checker.check_next_input(utf8); - } - checker.check_eof(); - if(checker.errors()) { - return {ptr, output, false}; // We found an error. - } - return {ptr, output, true}; -} -/* end file src/icelake/icelake_from_utf8.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf8_to_latin1.inl.cpp */ -// file included directly - -// File contains conversion procedure from possibly invalid UTF-8 strings. - -// template -template -simdutf_really_inline size_t process_block_from_utf8_to_latin1(const char *buf, size_t len, - char *latin_output, __m512i minus64, - __m512i one, - __mmask64 *next_leading_ptr, - __mmask64 *next_bit6_ptr) { - __mmask64 load_mask = - is_remaining ? _bzhi_u64(~0ULL, (unsigned int)len) : ~0ULL; - __m512i input = _mm512_maskz_loadu_epi8(load_mask, (__m512i *)buf); - __mmask64 nonascii = _mm512_movepi8_mask(input); - - if (nonascii == 0) { - is_remaining - ? _mm512_mask_storeu_epi8((__m512i *)latin_output, load_mask, input) - : _mm512_storeu_si512((__m512i *)latin_output, input); - return len; - } - - __mmask64 leading = _mm512_cmpge_epu8_mask(input, minus64); - - __m512i highbits = _mm512_xor_si512(input, _mm512_set1_epi8(-62)); - __mmask64 invalid_leading_bytes = - _mm512_mask_cmpgt_epu8_mask(leading, highbits, one); - - if (invalid_leading_bytes) { - return 0; // Indicates error - } - - __mmask64 leading_shift = (leading << 1) | *next_leading_ptr; - *next_leading_ptr = leading >> 63; - - if ((nonascii ^ leading) != leading_shift) { - return 0; // Indicates error - } - - __mmask64 bit6 = _mm512_cmpeq_epi8_mask(highbits, one); - input = - _mm512_mask_sub_epi8(input, (bit6 << 1) | *next_bit6_ptr, input, minus64); - *next_bit6_ptr = bit6 >> 63; - - __mmask64 retain = ~leading & load_mask; - __m512i output = _mm512_maskz_compress_epi8(retain, input); - int64_t written_out = count_ones(retain); - __mmask64 store_mask = (1ULL << written_out) - 1; - - // *************************** - // Possible optimization? (Nick Nuon) - // This commented out line is 5% faster but sadly it'll also write past - // memory bounds for latin1_output: is_remaining ? - // _mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output) : - // _mm512_storeu_si512((__m512i *)latin_output, output); I tried using - // _mm512_storeu_si512 and have the next process_block start from the - // "written_out" point but the compiler shuffles memory in such a way that it - // is significantly slower... - // **************************** - _mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output); - - return written_out; -} - -size_t utf8_to_latin1_avx512(const char *buf, size_t len, char *latin_output) { - char *start = latin_output; - size_t pos = 0; - __m512i minus64 = _mm512_set1_epi8(-64); // 11111111111 ... 1100 0000 - __m512i one = _mm512_set1_epi8(1); - __mmask64 next_leading = 0; - __mmask64 next_bit6 = 0; - - while (pos + 64 <= len) { - size_t written = process_block_from_utf8_to_latin1(buf + pos, 64, latin_output, minus64, - one, &next_leading, &next_bit6); - if (written == 0) { - return 0; // Indicates error - } - latin_output += written; - pos += 64; - } - - if (pos < len) { - size_t remaining = len - pos; - size_t written = - process_block_from_utf8_to_latin1(buf + pos, remaining, latin_output, minus64, one, - &next_leading, &next_bit6); - if (written == 0) { - return 0; // Indicates error - } - latin_output += written; - } - - return (size_t)(latin_output - start); -} -/* end file src/icelake/icelake_convert_utf8_to_latin1.inl.cpp */ -/* begin file src/icelake/icelake_convert_valid_utf8_to_latin1.inl.cpp */ -// file included directly - -// File contains conversion procedure from valid UTF-8 strings. - -template -simdutf_really_inline size_t process_valid_block_from_utf8_to_latin1(const char *buf, size_t len, - char *latin_output, - __m512i minus64, __m512i one, - __mmask64 *next_leading_ptr, - __mmask64 *next_bit6_ptr) { - __mmask64 load_mask = - is_remaining ? _bzhi_u64(~0ULL, (unsigned int)len) : ~0ULL; - __m512i input = _mm512_maskz_loadu_epi8(load_mask, (__m512i *)buf); - __mmask64 nonascii = _mm512_movepi8_mask(input); - - if (nonascii == 0) { - is_remaining - ? _mm512_mask_storeu_epi8((__m512i *)latin_output, load_mask, input) - : _mm512_storeu_si512((__m512i *)latin_output, input); - return len; - } - - __mmask64 leading = _mm512_cmpge_epu8_mask(input, minus64); - - __m512i highbits = _mm512_xor_si512(input, _mm512_set1_epi8(-62)); - - *next_leading_ptr = leading >> 63; - - __mmask64 bit6 = _mm512_cmpeq_epi8_mask(highbits, one); - input = - _mm512_mask_sub_epi8(input, (bit6 << 1) | *next_bit6_ptr, input, minus64); - *next_bit6_ptr = bit6 >> 63; - - __mmask64 retain = ~leading & load_mask; - __m512i output = _mm512_maskz_compress_epi8(retain, input); - int64_t written_out = count_ones(retain); - __mmask64 store_mask = (1ULL << written_out) - 1; - // Optimization opportunity: sometimes, masked writes are not needed. - _mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output); - return written_out; -} - -size_t valid_utf8_to_latin1_avx512(const char *buf, size_t len, - char *latin_output) { - char *start = latin_output; - size_t pos = 0; - __m512i minus64 = _mm512_set1_epi8(-64); // 11111111111 ... 1100 0000 - __m512i one = _mm512_set1_epi8(1); - __mmask64 next_leading = 0; - __mmask64 next_bit6 = 0; - - while (pos + 64 <= len) { - size_t written = process_valid_block_from_utf8_to_latin1( - buf + pos, 64, latin_output, minus64, one, &next_leading, &next_bit6); - latin_output += written; - pos += 64; - } - - if (pos < len) { - size_t remaining = len - pos; - size_t written = - process_valid_block_from_utf8_to_latin1(buf + pos, remaining, latin_output, minus64, - one, &next_leading, &next_bit6); - latin_output += written; - } - - return (size_t)(latin_output - start); -} -/* end file src/icelake/icelake_convert_valid_utf8_to_latin1.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf16_to_latin1.inl.cpp */ -// file included directly -template -size_t icelake_convert_utf16_to_latin1(const char16_t *buf, size_t len, - char *latin1_output) { - const char16_t *end = buf + len; - __m512i v_0xFF = _mm512_set1_epi16(0xff); - __m512i byteflip = _mm512_setr_epi64(0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809); - __m512i shufmask = _mm512_set_epi8( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, - 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); - while (buf + 32 <= end) { - __m512i in = _mm512_loadu_si512((__m512i *)buf); - if (big_endian) { - in = _mm512_shuffle_epi8(in, byteflip); - } - if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) { - return 0; - } - _mm256_storeu_si256( - (__m256i *)latin1_output, - _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in))); - latin1_output += 32; - buf += 32; - } - if (buf < end) { - uint32_t mask(uint32_t(1 << (end - buf)) - 1); - __m512i in = _mm512_maskz_loadu_epi16(mask, buf); - if (big_endian) { - in = _mm512_shuffle_epi8(in, byteflip); - } - if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) { - return 0; - } - _mm256_mask_storeu_epi8( - latin1_output, mask, - _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in))); - } - return len; -} - -template -std::pair -icelake_convert_utf16_to_latin1_with_errors(const char16_t *buf, size_t len, - char *latin1_output) { - const char16_t *end = buf + len; - const char16_t *start = buf; - __m512i byteflip = _mm512_setr_epi64(0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809); - __m512i v_0xFF = _mm512_set1_epi16(0xff); - __m512i shufmask = _mm512_set_epi8( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, - 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); - while (buf + 32 <= end) { - __m512i in = _mm512_loadu_si512((__m512i *)buf); - if (big_endian) { - in = _mm512_shuffle_epi8(in, byteflip); - } - if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) { - uint16_t word; - while ((word = (big_endian ? scalar::utf16::swap_bytes(uint16_t(*buf)) - : uint16_t(*buf))) <= 0xff) { - *latin1_output++ = uint8_t(word); - buf++; - } - return std::make_pair(result(error_code::TOO_LARGE, buf - start), - latin1_output); - } - _mm256_storeu_si256( - (__m256i *)latin1_output, - _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in))); - latin1_output += 32; - buf += 32; - } - if (buf < end) { - uint32_t mask(uint32_t(1 << (end - buf)) - 1); - __m512i in = _mm512_maskz_loadu_epi16(mask, buf); - if (big_endian) { - in = _mm512_shuffle_epi8(in, byteflip); - } - if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) { - - uint16_t word; - while ((word = (big_endian ? scalar::utf16::swap_bytes(uint16_t(*buf)) - : uint16_t(*buf))) <= 0xff) { - *latin1_output++ = uint8_t(word); - buf++; - } - return std::make_pair(result(error_code::TOO_LARGE, buf - start), - latin1_output); - } - _mm256_mask_storeu_epi8( - latin1_output, mask, - _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in))); - } - return std::make_pair(result(error_code::SUCCESS, len), latin1_output); -} -/* end file src/icelake/icelake_convert_utf16_to_latin1.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf16_to_utf8.inl.cpp */ -// file included directly - -/** - * This function converts the input (inbuf, inlen), assumed to be valid - * UTF16 (little endian) into UTF-8 (to outbuf). The number of code units written - * is written to 'outlen' and the function reports the number of input word - * consumed. - */ -template -size_t utf16_to_utf8_avx512i(const char16_t *inbuf, size_t inlen, - unsigned char *outbuf, size_t *outlen) { - __m512i in; - __mmask32 inmask = _cvtu32_mask32(0x7fffffff); - __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - const char16_t * const inbuf_orig = inbuf; - const unsigned char * const outbuf_orig = outbuf; - size_t adjust = 0; - int carry = 0; - - while (inlen >= 32) { - in = _mm512_loadu_si512(inbuf); - if(big_endian) { in = _mm512_shuffle_epi8(in, byteflip); } - inlen -= 31; - lastiteration: - inbuf += 31; - - failiteration: - const __mmask32 is234byte = _mm512_mask_cmp_epu16_mask( - inmask, in, _mm512_set1_epi16(0x0080), _MM_CMPINT_NLT); - - if (_ktestz_mask32_u8(inmask, is234byte)) { - // fast path for ASCII only - _mm512_mask_cvtepi16_storeu_epi8(outbuf, inmask, in); - outbuf += 31; - carry = 0; - - if (inlen < 32) { - goto tail; - } else { - continue; - } - } - - const __mmask32 is12byte = - _mm512_cmp_epu16_mask(in, _mm512_set1_epi16(0x0800), _MM_CMPINT_LT); - - if (_ktestc_mask32_u8(is12byte, inmask)) { - // fast path for 1 and 2 byte only - - const __m512i twobytes = _mm512_ternarylogic_epi32( - _mm512_slli_epi16(in, 8), _mm512_srli_epi16(in, 6), - _mm512_set1_epi16(0x3f3f), 0xa8); // (A|B)&C - in = _mm512_mask_add_epi16(in, is234byte, twobytes, - _mm512_set1_epi16(int16_t(0x80c0))); - const __m512i cmpmask = - _mm512_mask_blend_epi16(inmask, _mm512_set1_epi16(int16_t(0xffff)), - _mm512_set1_epi16(0x0800)); - const __mmask64 smoosh = _mm512_cmp_epu8_mask(in, cmpmask, _MM_CMPINT_NLT); - const __m512i out = _mm512_maskz_compress_epi8(smoosh, in); - _mm512_mask_storeu_epi8(outbuf, _cvtu64_mask64(_pext_u64(_cvtmask64_u64(smoosh), _cvtmask64_u64(smoosh))), - out); - outbuf += 31 + _mm_popcnt_u32(_cvtmask32_u32(is234byte)); - carry = 0; - - if (inlen < 32) { - goto tail; - } else { - continue; - } - } - __m512i lo = _mm512_cvtepu16_epi32(_mm512_castsi512_si256(in)); - __m512i hi = _mm512_cvtepu16_epi32(_mm512_extracti32x8_epi32(in, 1)); - - - __m512i taglo = _mm512_set1_epi32(0x8080e000); - __m512i taghi = taglo; - - const __m512i fc00masked = _mm512_and_epi32(in, _mm512_set1_epi16(int16_t(0xfc00))); - const __mmask32 hisurr = _mm512_mask_cmp_epu16_mask( - inmask, fc00masked, _mm512_set1_epi16(int16_t(0xd800)), _MM_CMPINT_EQ); - const __mmask32 losurr = _mm512_cmp_epu16_mask( - fc00masked, _mm512_set1_epi16(int16_t(0xdc00)), _MM_CMPINT_EQ); - - int carryout = 0; - if (!_kortestz_mask32_u8(hisurr, losurr)) { - // handle surrogates - - __m512i los = _mm512_alignr_epi32(hi, lo, 1); - __m512i his = _mm512_alignr_epi32(lo, hi, 1); - - const __mmask32 hisurrhi = _kshiftri_mask32(hisurr, 16); - taglo = - _mm512_mask_mov_epi32(taglo,__mmask16(hisurr), _mm512_set1_epi32(0x808080f0)); - taghi = - _mm512_mask_mov_epi32(taghi, __mmask16(hisurrhi), _mm512_set1_epi32(0x808080f0)); - - lo = _mm512_mask_slli_epi32(lo, __mmask16(hisurr), lo, 10); - hi = _mm512_mask_slli_epi32(hi, __mmask16(hisurrhi), hi, 10); - los = _mm512_add_epi32(los, _mm512_set1_epi32(0xfca02400)); - his = _mm512_add_epi32(his, _mm512_set1_epi32(0xfca02400)); - lo = _mm512_mask_add_epi32(lo, __mmask16(hisurr), lo, los); - hi = _mm512_mask_add_epi32(hi, __mmask16(hisurrhi), hi, his); - - carryout = _cvtu32_mask32(_kshiftri_mask32(hisurr, 30)); - - const uint32_t h = _cvtmask32_u32(hisurr); - const uint32_t l = _cvtmask32_u32(losurr); - // check for mismatched surrogates - if ((h + h + carry) ^ l) { - const uint32_t lonohi = l & ~(h + h + carry); - const uint32_t hinolo = h & ~(l >> 1); - inlen = _tzcnt_u32(hinolo | lonohi); - inmask = __mmask32(0x7fffffff & ((1 << inlen) - 1)); - in = _mm512_maskz_mov_epi16(inmask, in); - adjust = (int)inlen - 31; - inlen = 0; - goto failiteration; - } - } - - hi = _mm512_maskz_mov_epi32(_cvtu32_mask16(0x7fff),hi); - carry = carryout; - - __m512i mslo = - _mm512_multishift_epi64_epi8(_mm512_set1_epi64(0x20262c3200060c12), lo); - - __m512i mshi = - _mm512_multishift_epi64_epi8(_mm512_set1_epi64(0x20262c3200060c12), hi); - - const __mmask32 outmask = __mmask32(_kandn_mask64(losurr, inmask)); - const __mmask64 outmhi = _kshiftri_mask64(outmask, 16); - - const __mmask32 is1byte = __mmask32(_knot_mask64(is234byte)); - const __mmask64 is1bhi = _kshiftri_mask64(is1byte, 16); - const __mmask64 is12bhi = _kshiftri_mask64(is12byte, 16); - - taglo = - _mm512_mask_mov_epi32(taglo, __mmask16(is12byte), _mm512_set1_epi32(0x80c00000)); - taghi = - _mm512_mask_mov_epi32(taghi, __mmask16(is12bhi), _mm512_set1_epi32(0x80c00000)); - __m512i magiclo = _mm512_mask_blend_epi32(__mmask16(outmask), _mm512_set1_epi32(0xffffffff), - _mm512_set1_epi32(0x00010101)); - __m512i magichi = _mm512_mask_blend_epi32(__mmask16(outmhi), _mm512_set1_epi32(0xffffffff), - _mm512_set1_epi32(0x00010101)); - - - magiclo = _mm512_mask_blend_epi32(__mmask16(outmask), _mm512_set1_epi32(0xffffffff), - _mm512_set1_epi32(0x00010101)); - magichi = _mm512_mask_blend_epi32(__mmask16(outmhi), _mm512_set1_epi32(0xffffffff), - _mm512_set1_epi32(0x00010101)); - - mslo = _mm512_ternarylogic_epi32(mslo, _mm512_set1_epi32(0x3f3f3f3f), taglo, - 0xea); // A&B|C - mshi = _mm512_ternarylogic_epi32(mshi, _mm512_set1_epi32(0x3f3f3f3f), taghi, - 0xea); - mslo = _mm512_mask_slli_epi32(mslo, __mmask16(is1byte), lo, 24); - - mshi = _mm512_mask_slli_epi32(mshi, __mmask16(is1bhi), hi, 24); - - const __mmask64 wantlo = _mm512_cmp_epu8_mask(mslo, magiclo, _MM_CMPINT_NLT); - const __mmask64 wanthi = _mm512_cmp_epu8_mask(mshi, magichi, _MM_CMPINT_NLT); - const __m512i outlo = _mm512_maskz_compress_epi8(wantlo, mslo); - const __m512i outhi = _mm512_maskz_compress_epi8(wanthi, mshi); - const uint64_t wantlo_uint64 = _cvtmask64_u64(wantlo); - const uint64_t wanthi_uint64 = _cvtmask64_u64(wanthi); - - uint64_t advlo = _mm_popcnt_u64(wantlo_uint64); - uint64_t advhi = _mm_popcnt_u64(wanthi_uint64); - - _mm512_mask_storeu_epi8(outbuf, _cvtu64_mask64(_pext_u64(wantlo_uint64, wantlo_uint64)), outlo); - _mm512_mask_storeu_epi8(outbuf + advlo, _cvtu64_mask64(_pext_u64(wanthi_uint64, wanthi_uint64)), outhi); - outbuf += advlo + advhi; - } - outbuf -= adjust; - -tail: - if (inlen != 0) { - // We must have inlen < 31. - inmask = _cvtu32_mask32((1 << inlen) - 1); - in = _mm512_maskz_loadu_epi16(inmask, inbuf); - if(big_endian) { in = _mm512_shuffle_epi8(in, byteflip); } - adjust = inlen - 31; - inlen = 0; - goto lastiteration; - } - *outlen = (outbuf - outbuf_orig) + adjust; - return ((inbuf - inbuf_orig) + adjust); -} -/* end file src/icelake/icelake_convert_utf16_to_utf8.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf16_to_utf32.inl.cpp */ -// file included directly - -/* - Returns a pair: the first unprocessed byte from buf and utf32_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::tuple convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) { - const char16_t* end = buf + len; - const __m512i v_fc00 = _mm512_set1_epi16((uint16_t)0xfc00); - const __m512i v_d800 = _mm512_set1_epi16((uint16_t)0xd800); - const __m512i v_dc00 = _mm512_set1_epi16((uint16_t)0xdc00); - __mmask32 carry{0}; - const __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - while (std::distance(buf,end) >= 32) { - // Always safe because buf + 32 <= end so that end - buf >= 32 bytes: - __m512i in = _mm512_loadu_si512((__m512i*)buf); - if(big_endian) { in = _mm512_shuffle_epi8(in, byteflip); } - - // H - bitmask for high surrogates - const __mmask32 H = _mm512_cmpeq_epi16_mask(_mm512_and_si512(in, v_fc00), v_d800); - // H - bitmask for low surrogates - const __mmask32 L = _mm512_cmpeq_epi16_mask(_mm512_and_si512(in, v_fc00), v_dc00); - - if ((H|L)) { - // surrogate pair(s) in a register - const __mmask32 V = (L ^ (carry | (H << 1))); // A high surrogate must be followed by low one and a low one must be preceded by a high one. - // If valid, V should be equal to 0 - - if(V == 0) { - // valid case - /* - Input surrogate pair: - |1101.11aa.aaaa.aaaa|1101.10bb.bbbb.bbbb| - low surrogate high surrogate - */ - /* 1. Expand all code units to 32-bit code units - in |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0000.0000.0000.1101.10bb.bbbb.bbbb| - */ - const __m512i first = _mm512_cvtepu16_epi32(_mm512_castsi512_si256(in)); - const __m512i second = _mm512_cvtepu16_epi32(_mm512_extracti32x8_epi32(in,1)); - - /* 2. Shift by one 16-bit word to align low surrogates with high surrogates - in |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0000.0000.0000.1101.10bb.bbbb.bbbb| - shifted |????.????.????.????.????.????.????.????|0000.0000.0000.0000.1101.11aa.aaaa.aaaa| - */ - const __m512i shifted_first = _mm512_alignr_epi32(second, first, 1); - const __m512i shifted_second = _mm512_alignr_epi32(_mm512_setzero_si512(), second, 1); - - /* 3. Align all high surrogates in first and second by shifting to the left by 10 bits - |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0011.0110.bbbb.bbbb.bb00.0000.0000| - */ - const __m512i aligned_first = _mm512_mask_slli_epi32(first, (__mmask16)H, first, 10); - const __m512i aligned_second = _mm512_mask_slli_epi32(second, (__mmask16)(H>>16), second, 10); - - /* 4. Remove surrogate prefixes and add offset 0x10000 by adding in, shifted and constant - in |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0011.0110.bbbb.bbbb.bb00.0000.0000| - shifted |????.????.????.????.????.????.????.????|0000.0000.0000.0000.1101.11aa.aaaa.aaaa| - constant|1111.1100.1010.0000.0010.0100.0000.0000|1111.1100.1010.0000.0010.0100.0000.0000| - */ - const __m512i constant = _mm512_set1_epi32((uint32_t)0xfca02400); - const __m512i added_first = _mm512_mask_add_epi32(aligned_first, (__mmask16)H, aligned_first, shifted_first); - const __m512i utf32_first = _mm512_mask_add_epi32(added_first, (__mmask16)H, added_first, constant); - - const __m512i added_second = _mm512_mask_add_epi32(aligned_second, (__mmask16)(H>>16), aligned_second, shifted_second); - const __m512i utf32_second = _mm512_mask_add_epi32(added_second, (__mmask16)(H>>16), added_second, constant); - - // 5. Store all valid UTF-32 code units (low surrogate positions and 32nd word are invalid) - const __mmask32 valid = ~L & 0x7fffffff; - // We deliberately do a _mm512_maskz_compress_epi32 followed by storeu_epi32 - // to ease performance portability to Zen 4. - const __m512i compressed_first = _mm512_maskz_compress_epi32((__mmask16)(valid), utf32_first); - const size_t howmany1 = count_ones((uint16_t)(valid)); - _mm512_storeu_si512((__m512i *) utf32_output, compressed_first); - utf32_output += howmany1; - const __m512i compressed_second = _mm512_maskz_compress_epi32((__mmask16)(valid >> 16), utf32_second); - const size_t howmany2 = count_ones((uint16_t)(valid >> 16)); - // The following could be unsafe in some cases? - //_mm512_storeu_epi32((__m512i *) utf32_output, compressed_second); - _mm512_mask_storeu_epi32((__m512i *) utf32_output, __mmask16((1<> 30) & 0x1; - } else { - // invalid case - return std::make_tuple(buf+carry, utf32_output, false); - } - } else { - // no surrogates - // extend all thirty-two 16-bit code units to thirty-two 32-bit code units - _mm512_storeu_si512((__m512i *)(utf32_output), _mm512_cvtepu16_epi32(_mm512_castsi512_si256(in))); - _mm512_storeu_si512((__m512i *)(utf32_output) + 1, _mm512_cvtepu16_epi32(_mm512_extracti32x8_epi32(in,1))); - utf32_output += 32; - buf += 32; - carry = 0; - } - } // while - return std::make_tuple(buf+carry, utf32_output, true); -} -/* end file src/icelake/icelake_convert_utf16_to_utf32.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf32_to_latin1.inl.cpp */ -// file included directly -size_t icelake_convert_utf32_to_latin1(const char32_t *buf, size_t len, - char *latin1_output) { - const char32_t *end = buf + len; - __m512i v_0xFF = _mm512_set1_epi32(0xff); - __m512i shufmask = _mm512_set_epi8( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, - 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0); - while (buf + 16 <= end) { - __m512i in = _mm512_loadu_si512((__m512i *)buf); - if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) { - return 0; - } - _mm_storeu_si128((__m128i *)latin1_output, - _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in))); - latin1_output += 16; - buf += 16; - } - if (buf < end) { - uint16_t mask = uint16_t((1 << (end - buf)) - 1); - __m512i in = _mm512_maskz_loadu_epi32(mask, buf); - if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) { - return 0; - } - _mm_mask_storeu_epi8( - latin1_output, mask, - _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in))); - } - return len; -} - -std::pair -icelake_convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len, - char *latin1_output) { - const char32_t *end = buf + len; - const char32_t *start = buf; - __m512i v_0xFF = _mm512_set1_epi32(0xff); - __m512i shufmask = _mm512_set_epi8( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, - 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0); - while (buf + 16 <= end) { - __m512i in = _mm512_loadu_si512((__m512i *)buf); - if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) { - while (uint32_t(*buf) <= 0xff) { - *latin1_output++ = uint8_t(*buf++); - } - return std::make_pair(result(error_code::TOO_LARGE, buf - start), - latin1_output); - } - _mm_storeu_si128((__m128i *)latin1_output, - _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in))); - latin1_output += 16; - buf += 16; - } - if (buf < end) { - uint16_t mask = uint16_t((1 << (end - buf)) - 1); - __m512i in = _mm512_maskz_loadu_epi32(mask, buf); - if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) { - while (uint32_t(*buf) <= 0xff) { - *latin1_output++ = uint8_t(*buf++); - } - return std::make_pair(result(error_code::TOO_LARGE, buf - start), - latin1_output); - } - _mm_mask_storeu_epi8( - latin1_output, mask, - _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in))); - } - return std::make_pair(result(error_code::SUCCESS, len), latin1_output); -} -/* end file src/icelake/icelake_convert_utf32_to_latin1.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf32_to_utf8.inl.cpp */ -// file included directly - -// Todo: currently, this is just the haswell code, optimize for icelake kernel. -std::pair avx512_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { - const char32_t* end = buf + len; - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000); - const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80); - const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800); - const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080); - const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff); - __m256i running_max = _mm256_setzero_si256(); - __m256i forbidden_bytemask = _mm256_setzero_si256(); - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1); - running_max = _mm256_max_epu32(_mm256_max_epu32(in, running_max), nextin); - - // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation - __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff)); - in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000); - - // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp) - - if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!! - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1)); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000); - const uint32_t one_or_two_bytes_bitmask = static_cast(_mm256_movemask_epi8(one_or_two_bytes_bytemask)); - if (one_or_two_bytes_bitmask == 0xffffffff) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in_16, 2); - // t1 = [000a|aaaa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in_16, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - buf += 16; - continue; - } - // Must check for overflow in packing - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - if (saturation_bitmask == 0xffffffff) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800); - forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800)); - - const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e, - 0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm256_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m256i s0 = _mm256_srli_epi16(in_16, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000)); - const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m256i s4 = _mm256_xor_si256(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m256i out0 = _mm256_unpacklo_epi16(t2, s4); - const __m256i out1 = _mm256_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint32_t mask = (one_byte_bitmask & 0x55555555) | - (one_or_two_bytes_bitmask & 0xaaaaaaaa); - // Due to the wider registers, the following path is less likely to be useful. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle); - const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1)); - utf8_output += 12; - buf += 16; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1); - - const uint8_t mask2 = static_cast(mask >> 16); - const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0]; - const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1)); - const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2); - - - const uint8_t mask3 = static_cast(mask >> 24); - const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0]; - const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1)); - const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_2); - utf8_output += row2[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_3); - utf8_output += row3[0]; - buf += 16; - } else { - // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes. - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // may require large, non-trivial tables? - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII) - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { // 2-byte - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word & 0xFFFF0000 )==0) { // 3-byte - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf8_output); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { // 4-byte - if (word > 0x10FFFF) { return std::make_pair(nullptr, utf8_output); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - // check for invalid input - const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff); - if(static_cast(_mm256_movemask_epi8(_mm256_cmpeq_epi32(_mm256_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffffffff) { - return std::make_pair(nullptr, utf8_output); - } - - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf8_output); } - - return std::make_pair(buf, utf8_output); -} - -// Todo: currently, this is just the haswell code, optimize for icelake kernel. -std::pair avx512_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) { - const char32_t* end = buf + len; - const char32_t* start = buf; - - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000); - const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80); - const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800); - const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080); - const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff); - const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff); - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1); - // Check for too large input - const __m256i max_input = _mm256_max_epu32(_mm256_max_epu32(in, nextin), v_10ffff); - if(static_cast(_mm256_movemask_epi8(_mm256_cmpeq_epi32(max_input, v_10ffff))) != 0xffffffff) { - return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output); - } - - // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation - __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff)); - in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000); - - // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp) - - if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!! - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1)); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000); - const uint32_t one_or_two_bytes_bitmask = static_cast(_mm256_movemask_epi8(one_or_two_bytes_bytemask)); - if (one_or_two_bytes_bitmask == 0xffffffff) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in_16, 2); - // t1 = [000a|aaaa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in_16, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - buf += 16; - continue; - } - // Must check for overflow in packing - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - if (saturation_bitmask == 0xffffffff) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - - // Check for illegal surrogate code units - const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800); - const __m256i forbidden_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800); - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), utf8_output); - } - - const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e, - 0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm256_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m256i s0 = _mm256_srli_epi16(in_16, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000)); - const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m256i s4 = _mm256_xor_si256(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m256i out0 = _mm256_unpacklo_epi16(t2, s4); - const __m256i out1 = _mm256_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint32_t mask = (one_byte_bitmask & 0x55555555) | - (one_or_two_bytes_bitmask & 0xaaaaaaaa); - // Due to the wider registers, the following path is less likely to be useful. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle); - const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1)); - utf8_output += 12; - buf += 16; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1); - - const uint8_t mask2 = static_cast(mask >> 16); - const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0]; - const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1)); - const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2); - - - const uint8_t mask3 = static_cast(mask >> 24); - const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0]; - const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1)); - const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_2); - utf8_output += row2[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_3); - utf8_output += row3[0]; - buf += 16; - } else { - // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes. - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // may require large, non-trivial tables? - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII) - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { // 2-byte - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word & 0xFFFF0000 )==0) { // 3-byte - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf8_output); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { // 4-byte - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf8_output); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output); -} -/* end file src/icelake/icelake_convert_utf32_to_utf8.inl.cpp */ -/* begin file src/icelake/icelake_convert_utf32_to_utf16.inl.cpp */ -// file included directly - -// Todo: currently, this is just the haswell code, optimize for icelake kernel. -template -std::pair avx512_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_output) { - const char32_t* end = buf + len; - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - __m256i forbidden_bytemask = _mm256_setzero_si256(); - - - while (buf + 8 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - - const __m256i v_00000000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000); - - // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - - if (saturation_bitmask == 0xffffffff) { - const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800); - forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800)); - - __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1)); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - utf16_packed = _mm_shuffle_epi8(utf16_packed, swap); - } - _mm_storeu_si128((__m128i*)utf16_output, utf16_packed); - utf16_output += 8; - buf += 8; - } else { - size_t forward = 7; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf16_output); } - *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(nullptr, utf16_output); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (big_endian) { - high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8)); - low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8)); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - // check for invalid input - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf16_output); } - - return std::make_pair(buf, utf16_output); -} - -// Todo: currently, this is just the haswell code, optimize for icelake kernel. -template -std::pair avx512_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) { - const char32_t* start = buf; - const char32_t* end = buf + len; - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 8 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - - const __m256i v_00000000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000); - - // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - - if (saturation_bitmask == 0xffffffff) { - const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800); - const __m256i forbidden_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800); - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), utf16_output); - } - - __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1)); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - utf16_packed = _mm_shuffle_epi8(utf16_packed, swap); - } - _mm_storeu_si128((__m128i*)utf16_output, utf16_packed); - utf16_output += 8; - buf += 8; - } else { - size_t forward = 7; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf16_output); } - *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf16_output); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (big_endian) { - high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8)); - low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8)); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output); -} -/* end file src/icelake/icelake_convert_utf32_to_utf16.inl.cpp */ -/* begin file src/icelake/icelake_ascii_validation.inl.cpp */ -// file included directly - -bool validate_ascii(const char* buf, size_t len) { - const char* end = buf + len; - const __m512i ascii = _mm512_set1_epi8((uint8_t)0x80); - __m512i running_or = _mm512_setzero_si512(); - for (; buf + 64 <= end; buf += 64) { - const __m512i utf8 = _mm512_loadu_si512((const __m512i*)buf); - running_or = _mm512_ternarylogic_epi32(running_or, utf8, ascii, 0xf8); // running_or | (utf8 & ascii) - } - if(buf < end) { - const __m512i utf8 = _mm512_maskz_loadu_epi8((uint64_t(1) << (end-buf)) - 1,(const __m512i*)buf); - running_or = _mm512_ternarylogic_epi32(running_or, utf8, ascii, 0xf8); // running_or | (utf8 & ascii) - } - return (_mm512_test_epi8_mask(running_or, running_or) == 0); -} -/* end file src/icelake/icelake_ascii_validation.inl.cpp */ -/* begin file src/icelake/icelake_utf32_validation.inl.cpp */ -// file included directly - -const char32_t* validate_utf32(const char32_t* buf, size_t len) { - const char32_t* end = len >= 16 ? buf + len - 16 : nullptr; - - const __m512i offset = _mm512_set1_epi32((uint32_t)0xffff2000); - __m512i currentmax = _mm512_setzero_si512(); - __m512i currentoffsetmax = _mm512_setzero_si512(); - - while (buf <= end) { - __m512i utf32 = _mm512_loadu_si512((const __m512i*)buf); - buf += 16; - currentoffsetmax = _mm512_max_epu32(_mm512_add_epi32(utf32, offset), currentoffsetmax); - currentmax = _mm512_max_epu32(utf32, currentmax); - } - - const __m512i standardmax = _mm512_set1_epi32((uint32_t)0x10ffff); - const __m512i standardoffsetmax = _mm512_set1_epi32((uint32_t)0xfffff7ff); - __m512i is_zero = _mm512_xor_si512(_mm512_max_epu32(currentmax, standardmax), standardmax); - if (_mm512_test_epi8_mask(is_zero, is_zero) != 0) { - return nullptr; - } - is_zero = _mm512_xor_si512(_mm512_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if (_mm512_test_epi8_mask(is_zero, is_zero) != 0) { - return nullptr; - } - - return buf; -} -/* end file src/icelake/icelake_utf32_validation.inl.cpp */ -/* begin file src/icelake/icelake_convert_latin1_to_utf8.inl.cpp */ -// file included directly - -static inline size_t latin1_to_utf8_avx512_vec(__m512i input, size_t input_len, char *utf8_output, int mask_output) { - __mmask64 nonascii = _mm512_movepi8_mask(input); - size_t output_size = input_len + (size_t)count_ones(nonascii); - - // Mask to denote whether the byte is a leading byte that is not ascii - __mmask64 sixth = - _mm512_cmpge_epu8_mask(input, _mm512_set1_epi8(-64)); //binary representation of -64: 1100 0000 - - const uint64_t alternate_bits = UINT64_C(0x5555555555555555); - uint64_t ascii = ~nonascii; - // the bits in ascii are inverted and zeros are interspersed in between them - uint64_t maskA = ~_pdep_u64(ascii, alternate_bits); - uint64_t maskB = ~_pdep_u64(ascii>>32, alternate_bits); - - // interleave bytes from top and bottom halves (abcd...ABCD -> aAbBcCdD) - __m512i input_interleaved = _mm512_permutexvar_epi8(_mm512_set_epi32( - 0x3f1f3e1e, 0x3d1d3c1c, 0x3b1b3a1a, 0x39193818, - 0x37173616, 0x35153414, 0x33133212, 0x31113010, - 0x2f0f2e0e, 0x2d0d2c0c, 0x2b0b2a0a, 0x29092808, - 0x27072606, 0x25052404, 0x23032202, 0x21012000 - ), input); - - // double size of each byte, and insert the leading byte 1100 0010 - -/* -upscale the bytes to 16-bit value, adding the 0b11000000 leading byte in the process. -We adjust for the bytes that have their two most significant bits. This takes care of the first 32 bytes, assuming we interleaved the bytes. */ - __m512i outputA = _mm512_shldi_epi16(input_interleaved, _mm512_set1_epi8(-62), 8); - outputA = _mm512_mask_add_epi16( - outputA, - (__mmask32)sixth, - outputA, - _mm512_set1_epi16(1 - 0x4000)); // 1- 0x4000 = 1100 0000 0000 0001???? - - // in the second 32-bit half, set first or second option based on whether original input is leading byte (second case) or not (first case) - __m512i leadingB = _mm512_mask_blend_epi16( - (__mmask32)(sixth>>32), - _mm512_set1_epi16(0x00c2), // 0000 0000 1101 0010 - _mm512_set1_epi16(0x40c3));// 0100 0000 1100 0011 - __m512i outputB = _mm512_ternarylogic_epi32( - input_interleaved, - leadingB, - _mm512_set1_epi16((short)0xff00), - (240 & 170) ^ 204); // (input_interleaved & 0xff00) ^ leadingB - - // prune redundant bytes - outputA = _mm512_maskz_compress_epi8(maskA, outputA); - outputB = _mm512_maskz_compress_epi8(maskB, outputB); - - - size_t output_sizeA = (size_t)count_ones((uint32_t)nonascii) + 32; - - if(mask_output) { - if(input_len > 32) { // is the second half of the input vector used? - __mmask64 write_mask = _bzhi_u64(~0ULL, (unsigned int)output_sizeA); - _mm512_mask_storeu_epi8(utf8_output, write_mask, outputA); - utf8_output += output_sizeA; - write_mask = _bzhi_u64(~0ULL, (unsigned int)(output_size - output_sizeA)); - _mm512_mask_storeu_epi8(utf8_output, write_mask, outputB); - } else { - __mmask64 write_mask = _bzhi_u64(~0ULL, (unsigned int)output_size); - _mm512_mask_storeu_epi8(utf8_output, write_mask, outputA); - } - } else { - _mm512_storeu_si512(utf8_output, outputA); - utf8_output += output_sizeA; - _mm512_storeu_si512(utf8_output, outputB); - } - return output_size; -} - -static inline size_t latin1_to_utf8_avx512_branch(__m512i input, char *utf8_output) { - __mmask64 nonascii = _mm512_movepi8_mask(input); - if(nonascii) { - return latin1_to_utf8_avx512_vec(input, 64, utf8_output, 0); - } else { - _mm512_storeu_si512(utf8_output, input); - return 64; - } -} - -size_t latin1_to_utf8_avx512_start(const char *buf, size_t len, char *utf8_output) { - char *start = utf8_output; - size_t pos = 0; - // if there's at least 128 bytes remaining, we don't need to mask the output - for (; pos + 128 <= len; pos += 64) { - __m512i input = _mm512_loadu_si512((__m512i *)(buf + pos)); - utf8_output += latin1_to_utf8_avx512_branch(input, utf8_output); - } - // in the last 128 bytes, the first 64 may require masking the output - if (pos + 64 <= len) { - __m512i input = _mm512_loadu_si512((__m512i *)(buf + pos)); - utf8_output += latin1_to_utf8_avx512_vec(input, 64, utf8_output, 1); - pos += 64; - } - // with the last 64 bytes, the input also needs to be masked - if (pos < len) { - __mmask64 load_mask = _bzhi_u64(~0ULL, (unsigned int)(len - pos)); - __m512i input = _mm512_maskz_loadu_epi8(load_mask, (__m512i *)(buf + pos)); - utf8_output += latin1_to_utf8_avx512_vec(input, len - pos, utf8_output, 1); - } - return (size_t)(utf8_output - start); -} -/* end file src/icelake/icelake_convert_latin1_to_utf8.inl.cpp */ -/* begin file src/icelake/icelake_convert_latin1_to_utf16.inl.cpp */ -// file included directly -template -size_t icelake_convert_latin1_to_utf16(const char *latin1_input, size_t len, - char16_t *utf16_output) { - size_t rounded_len = len & ~0x1F; // Round down to nearest multiple of 32 - - __m512i byteflip = _mm512_setr_epi64(0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809, - 0x0607040502030001, 0x0e0f0c0d0a0b0809); - for (size_t i = 0; i < rounded_len; i += 32) { - // Load 32 Latin1 characters into a 256-bit register - __m256i in = _mm256_loadu_si256((__m256i *)&latin1_input[i]); - // Zero extend each set of 8 Latin1 characters to 32 16-bit integers - __m512i out = _mm512_cvtepu8_epi16(in); - if (big_endian) { - out = _mm512_shuffle_epi8(out, byteflip); - } - // Store the results back to memory - _mm512_storeu_si512((__m512i *)&utf16_output[i], out); - } - if (rounded_len != len) { - uint32_t mask = uint32_t(1 << (len - rounded_len)) - 1; - __m256i in = _mm256_maskz_loadu_epi8(mask, latin1_input + rounded_len); - - // Zero extend each set of 8 Latin1 characters to 32 16-bit integers - __m512i out = _mm512_cvtepu8_epi16(in); - if (big_endian) { - out = _mm512_shuffle_epi8(out, byteflip); - } - // Store the results back to memory - _mm512_mask_storeu_epi16(utf16_output + rounded_len, mask, out); - } - - return len; -} -/* end file src/icelake/icelake_convert_latin1_to_utf16.inl.cpp */ -/* begin file src/icelake/icelake_convert_latin1_to_utf32.inl.cpp */ -std::pair avx512_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { - size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16 - - for (size_t i = 0; i < rounded_len; i += 16) { - // Load 16 Latin1 characters into a 128-bit register - __m128i in = _mm_loadu_si128((__m128i*)&buf[i]); - - // Zero extend each set of 8 Latin1 characters to 16 32-bit integers using vpmovzxbd - __m512i out = _mm512_cvtepu8_epi32(in); - - // Store the results back to memory - _mm512_storeu_si512((__m512i*)&utf32_output[i], out); - } - - // Return pointers pointing to where we left off - return std::make_pair(buf + rounded_len, utf32_output + rounded_len); -} -/* end file src/icelake/icelake_convert_latin1_to_utf32.inl.cpp */ -/* begin file src/icelake/icelake_base64.inl.cpp */ -// file included directly -/** - * References and further reading: - * - * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the - * speed of a memory copy, Software: Practice and Experience 50 (2), 2020. - * https://arxiv.org/abs/1910.05109 - * - * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 - * Instructions, ACM Transactions on the Web 12 (3), 2018. - * https://arxiv.org/abs/1704.00605 - * - * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings. - * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force, - * Request for Comments: 4648. - * - * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization. - * http://www.alfredklomp.com/programming/sse-base64/. (2014). - * - * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD - * acceleration. https://github.com/aklomp/base64. (2014). - * - * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014). - * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/ - * - * Nick Kopp. 2013. Base64 Encoding on a GPU. - * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). - */ - -struct block64 { - __m512i chunks[1]; -}; - -template -size_t encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { - // credit: Wojciech Muła - const uint8_t *input = (const uint8_t *)src; - - uint8_t *out = (uint8_t *)dst; - static const char *lookup_tbl = - base64_url - ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" - : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - const __m512i shuffle_input = _mm512_setr_epi32( - 0x01020001, 0x04050304, 0x07080607, 0x0a0b090a, 0x0d0e0c0d, 0x10110f10, - 0x13141213, 0x16171516, 0x191a1819, 0x1c1d1b1c, 0x1f201e1f, 0x22232122, - 0x25262425, 0x28292728, 0x2b2c2a2b, 0x2e2f2d2e); - const __m512i lookup = - _mm512_loadu_si512(reinterpret_cast(lookup_tbl)); - const __m512i multi_shifts = _mm512_set1_epi64(UINT64_C(0x3036242a1016040a)); - size_t i = 0; - for (; i + 64 <= srclen; i += 48) { - const __m512i v = - _mm512_loadu_si512(reinterpret_cast(input + i)); - const __m512i in = _mm512_permutexvar_epi8(shuffle_input, v); - const __m512i indices = _mm512_multishift_epi64_epi8(multi_shifts, in); - const __m512i result = _mm512_permutexvar_epi8(indices, lookup); - _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), result); - out += 64; - } - return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, - srclen - i, options); -} - -template -static inline uint64_t to_base64_mask(block64 *b, bool *error) { - __m512i input = b->chunks[0]; - const __m512i ascii_space_tbl = _mm512_set_epi8( - 0, 0, 13, 12, 0, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 13, 12, 0, 10, 9, - 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 13, 12, 0, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 0, 0, 13, 12, 0, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 32); - __m512i lookup0; - if (base64_url) { - lookup0 = _mm512_set_epi8( - -128, -128, -128, -128, -128, -128, 61, 60, 59, 58, 57, 56, 55, 54, 53, - 52, -128, -128, 62, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -1, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -1, - -128, -128, -1, -1, -128, -128, -128, -128, -128, -128, -128, -128, -1); - } else { - lookup0 = _mm512_set_epi8( - -128, -128, -128, -128, -128, -128, 61, 60, 59, 58, 57, 56, 55, 54, 53, - 52, 63, -128, -128, -128, 62, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -1, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -1, -128, - -128, -1, -1, -128, -128, -128, -128, -128, -128, -128, -128, -128); - } - __m512i lookup1; - if (base64_url) { - lookup1 = _mm512_set_epi8( - -128, -128, -128, -128, -128, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, - 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, -128, - 63, -128, -128, -128, -128, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, - 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -128); - } else { - lookup1 = _mm512_set_epi8( - -128, -128, -128, -128, -128, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, - 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, -128, - -128, -128, -128, -128, -128, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, - 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -128); - } - - const __m512i translated = _mm512_permutex2var_epi8(lookup0, input, lookup1); - const __m512i combined = _mm512_or_si512(translated, input); - const __mmask64 mask = _mm512_movepi8_mask(combined); - if (mask) { - const __mmask64 spaces = _mm512_cmpeq_epi8_mask( - _mm512_shuffle_epi8(ascii_space_tbl, input), input); - *error |= (mask != spaces); - } - b->chunks[0] = translated; - - return mask; -} - -static inline void copy_block(block64 *b, char *output) { - _mm512_storeu_si512(reinterpret_cast<__m512i *>(output), b->chunks[0]); -} - -static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { - uint64_t nmask = ~mask; - __m512i c = _mm512_maskz_compress_epi8(nmask, b->chunks[0]); - _mm512_storeu_si512(reinterpret_cast<__m512i *>(output), c); - return _mm_popcnt_u64(nmask); -} - -// The caller of this function is responsible to ensure that there are 64 bytes available -// from reading at src. The data is read into a block64 structure. -static inline void load_block(block64 *b, const char *src) { - b->chunks[0] = _mm512_loadu_si512(reinterpret_cast(src)); -} - -// The caller of this function is responsible to ensure that there are 128 bytes available -// from reading at src. The data is read into a block64 structure. -static inline void load_block(block64 *b, const char16_t *src) { - __m512i m1 = _mm512_loadu_si512(reinterpret_cast(src)); - __m512i m2 = _mm512_loadu_si512(reinterpret_cast(src + 32)); - __m512i p = _mm512_packus_epi16(m1, m2); - b->chunks[0] = - _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), p); -} - -static inline void base64_decode(char *out, __m512i str) { - const __m512i merge_ab_and_bc = - _mm512_maddubs_epi16(str, _mm512_set1_epi32(0x01400140)); - const __m512i merged = - _mm512_madd_epi16(merge_ab_and_bc, _mm512_set1_epi32(0x00011000)); - const __m512i pack = _mm512_set_epi8( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 61, 62, 56, 57, 58, - 52, 53, 54, 48, 49, 50, 44, 45, 46, 40, 41, 42, 36, 37, 38, 32, 33, 34, - 28, 29, 30, 24, 25, 26, 20, 21, 22, 16, 17, 18, 12, 13, 14, 8, 9, 10, 4, - 5, 6, 0, 1, 2); - const __m512i shuffled = _mm512_permutexvar_epi8(pack, merged); - _mm512_mask_storeu_epi8( - (__m512i *)out, 0xffffffffffff, - shuffled); // mask would be 0xffffffffffff since we write 48 bytes. -} -// decode 64 bytes and output 48 bytes -static inline void base64_decode_block(char *out, const char *src) { - base64_decode(out, - _mm512_loadu_si512(reinterpret_cast(src))); -} -static inline void base64_decode_block(char *out, block64 *b) { - base64_decode(out, b->chunks[0]); -} - -template -result compress_decode_base64(char *dst, const chartype *src, size_t srclen, - base64_options options) { - const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value; - size_t equalsigns = 0; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 1; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 2; - } - } - const chartype *const srcinit = src; - const char *const dstinit = dst; - const chartype *const srcend = src + srclen; - - // figure out why block_size == 2 is sometimes best??? - constexpr size_t block_size = 6; - char buffer[block_size * 64]; - char *bufferptr = buffer; - if (srclen >= 64) { - const chartype *const srcend64 = src + srclen - 64; - while (src <= srcend64) { - block64 b; - load_block(&b, src); - src += 64; - bool error = false; - uint64_t badcharmask = to_base64_mask(&b, &error); - if (error) { - src -= 64; - while (src < srcend && to_base64[uint8_t(*src)] <= 64) { - src++; - } - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - if (badcharmask != 0) { - // optimization opportunity: check for simple masks like those made of - // continuous 1s followed by continuous 0s. And masks containing a - // single bad character. - bufferptr += compress_block(&b, badcharmask, bufferptr); - } else if (bufferptr != buffer) { - copy_block(&b, bufferptr); - bufferptr += 64; - } else { - base64_decode_block(dst, &b); - dst += 48; - } - if (bufferptr >= (block_size - 1) * 64 + buffer) { - for (size_t i = 0; i < (block_size - 1); i++) { - base64_decode_block(dst, buffer + i * 64); - dst += 48; - } - std::memcpy(buffer, buffer + (block_size - 1) * 64, - 64); // 64 might be too much - bufferptr -= (block_size - 1) * 64; - } - } - } - - char *buffer_start = buffer; - // Optimization note: if this is almost full, then it is worth our - // time, otherwise, we should just decode directly. - int last_block = (int)((bufferptr - buffer_start) % 64); - if (last_block != 0 && srcend - src + last_block >= 64) { - - while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - *bufferptr = char(val); - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - bufferptr += (val <= 63); - src++; - } - } - - for (; buffer_start + 64 <= bufferptr; buffer_start += 64) { - base64_decode_block(dst, buffer_start); - dst += 48; - } - if ((bufferptr - buffer_start) % 64 != 0) { - while (buffer_start + 4 < bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 4); - dst += 3; - buffer_start += 4; - } - if (buffer_start + 4 <= bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - dst += 3; - buffer_start += 4; - } - // we may have 1, 2 or 3 bytes left and we need to decode them so let us - // bring in src content - int leftover = int(bufferptr - buffer_start); - if (leftover > 0) { - while (leftover < 4 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - buffer_start[leftover] = char(val); - leftover += (val <= 63); - src++; - } - - if (leftover == 1) { - return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)}; - } - if (leftover == 2) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - dst += 1; - } else if (leftover == 3) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6) + - (uint32_t(buffer_start[2]) << 1 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - - std::memcpy(dst, &triple, 2); - dst += 2; - } else { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - dst += 3; - } - } - } - if (src < srcend + equalsigns) { - result r = - scalar::base64::base64_tail_decode(dst, src, srcend - src, options); - if (r.error == error_code::INVALID_BASE64_CHARACTER) { - r.count += size_t(src - srcinit); - return r; - } else { - r.count += size_t(dst - dstinit); - } - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - r.error = error_code::INVALID_BASE64_CHARACTER; - } - } - return r; - } - if(equalsigns > 0) { - if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)}; - } - } - return {SUCCESS, size_t(dst - dstinit)}; -} -/* end file src/icelake/icelake_base64.inl.cpp */ - - -#include - -} // namespace -} // namespace icelake -} // namespace simdutf - -namespace simdutf { -namespace icelake { - -simdutf_warn_unused int -implementation::detect_encodings(const char *input, - size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - if (length % 2 == 0) { - const char *buf = input; - - const char *start = buf; - const char *end = input + length; - - bool is_utf8 = true; - bool is_utf16 = true; - bool is_utf32 = true; - - int out = 0; - - avx512_utf8_checker checker{}; - __m512i currentmax = _mm512_setzero_si512(); - while (buf + 64 <= end) { - __m512i in = _mm512_loadu_si512((__m512i *)buf); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = - _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if (surrogates) { - is_utf8 = false; - - // Can still be either UTF-16LE or UTF-32 depending on the positions - // of the surrogates To be valid UTF-32, a surrogate cannot be in the - // two most significant bytes of any 32-bit word. On the other hand, to - // be valid UTF-16LE, at least one surrogate must be in the two most - // significant bytes of a 32-bit word since they always come in pairs in - // UTF-16LE. Note that we always proceed in multiple of 4 before this - // point so there is no offset in 32-bit code units. - - if ((surrogates & 0xaaaaaaaa) != 0) { - is_utf32 = false; - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask( - diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - return simdutf::encoding_type::unspecified; - } - - bool ends_with_high = ((highsurrogates & 0x80000000) != 0); - if (ends_with_high) { - buf += - 31 * - sizeof(char16_t); // advance only by 31 code units so that we start - // with the high surrogate on the next round. - } else { - buf += 32 * sizeof(char16_t); - } - is_utf16 = validate_utf16le(reinterpret_cast(buf), - (end - buf) / sizeof(char16_t)); - if (!is_utf16) { - return simdutf::encoding_type::unspecified; - - } else { - return simdutf::encoding_type::UTF16_LE; - } - - } else { - is_utf16 = false; - // Check for UTF-32 - if (length % 4 == 0) { - const char32_t *input32 = reinterpret_cast(buf); - const char32_t *end32 = - reinterpret_cast(start) + length / 4; - if (validate_utf32(input32, end32 - input32)) { - return simdutf::encoding_type::UTF32_LE; - } - } - return simdutf::encoding_type::unspecified; - } - } - // If no surrogate, validate under other encodings as well - - // UTF-32 validation - currentmax = _mm512_max_epu32(in, currentmax); - - // UTF-8 validation - checker.check_next_input(in); - - buf += 64; - } - - // Check which encodings are possible - - if (is_utf8) { - size_t current_length = static_cast(buf - start); - if (current_length != length) { - const __m512i utf8 = _mm512_maskz_loadu_epi8( - (1ULL << (length - current_length)) - 1, (const __m512i *)buf); - checker.check_next_input(utf8); - } - checker.check_eof(); - if (!checker.errors()) { - out |= simdutf::encoding_type::UTF8; - } - } - - if (is_utf16 && scalar::utf16::validate( - reinterpret_cast(buf), - (length - (buf - start)) / 2)) { - out |= simdutf::encoding_type::UTF16_LE; - } - - if (is_utf32 && (length % 4 == 0)) { - currentmax = _mm512_max_epu32( - _mm512_maskz_loadu_epi8( - (1ULL << (length - static_cast(buf - start))) - 1, - (const __m512i *)buf), - currentmax); - __mmask16 outside_range = _mm512_cmp_epu32_mask(currentmax, _mm512_set1_epi32(0x10ffff), - _MM_CMPINT_GT); - if (outside_range == 0) { - out |= simdutf::encoding_type::UTF32_LE; - } - } - - return out; - } else if (implementation::validate_utf8(input, length)) { - return simdutf::encoding_type::UTF8; - } else { - return simdutf::encoding_type::unspecified; - } -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - avx512_utf8_checker checker{}; - const char* ptr = buf; - const char* end = ptr + len; - for (; ptr + 64 <= end; ptr += 64) { - const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr); - checker.check_next_input(utf8); - } - { - const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - ptr))-1, (const __m512i*)ptr); - checker.check_next_input(utf8); - } - checker.check_eof(); - return ! checker.errors(); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept { - avx512_utf8_checker checker{}; - const char* ptr = buf; - const char* end = ptr + len; - size_t count{0}; - for (; ptr + 64 <= end; ptr += 64) { - const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr); - checker.check_next_input(utf8); - if(checker.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(buf), reinterpret_cast(buf + count), len - count); - res.count += count; - return res; - } - count += 64; - } - { - const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - ptr))-1, (const __m512i*)ptr); - checker.check_next_input(utf8); - if(checker.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(buf), reinterpret_cast(buf + count), len - count); - res.count += count; - return res; - } else { - return result(error_code::SUCCESS, len); - } - } -} - -simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept { - return icelake::validate_ascii(buf, len); -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept { - const char* buf_orig = buf; - const char* end = buf + len; - const __m512i ascii = _mm512_set1_epi8((uint8_t)0x80); - for (; buf + 64 <= end; buf += 64) { - const __m512i input = _mm512_loadu_si512((const __m512i*)buf); - __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT); - if(notascii) { - return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u64(notascii)); - } - } - { - const __m512i input = _mm512_maskz_loadu_epi8((1ULL<<(end - buf))-1, (const __m512i*)buf); - __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT); - if(notascii) { - return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u64(notascii)); - } - } - return result(error_code::SUCCESS, len); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept { - const char16_t *end = buf + len; - - for(;buf + 32 <= end; ) { - __m512i in = _mm512_loadu_si512((__m512i*)buf); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - return false; - } - bool ends_with_high = ((highsurrogates & 0x80000000) != 0); - if(ends_with_high) { - buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round. - } else { - buf += 32; - } - } else { - buf += 32; - } - } - if(buf < end) { - __m512i in = _mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - return false; - } - } - } - return true; -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept { - const char16_t *end = buf + len; - const __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - for(;buf + 32 <= end; ) { - __m512i in = _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i*)buf), byteflip); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - return false; - } - bool ends_with_high = ((highsurrogates & 0x80000000) != 0); - if(ends_with_high) { - buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round. - } else { - buf += 32; - } - } else { - buf += 32; - } - } - if(buf < end) { - __m512i in = _mm512_shuffle_epi8(_mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf), byteflip); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - return false; - } - } - } - return true; -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept { - const char16_t *start_buf = buf; - const char16_t *end = buf + len; - for(;buf + 32 <= end; ) { - __m512i in = _mm512_loadu_si512((__m512i*)buf); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1)); - uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1)); - return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high)); - } - bool ends_with_high = ((highsurrogates & 0x80000000) != 0); - if(ends_with_high) { - buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round. - } else { - buf += 32; - } - } else { - buf += 32; - } - } - if(buf < end) { - __m512i in = _mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1)); - uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1)); - return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high)); - } - } - } - return result(error_code::SUCCESS, len); -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept { - const char16_t *start_buf = buf; - const char16_t *end = buf + len; - const __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - for(;buf + 32 <= end; ) { - __m512i in = _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i*)buf), byteflip); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1)); - uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1)); - return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high)); - } - bool ends_with_high = ((highsurrogates & 0x80000000) != 0); - if(ends_with_high) { - buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round. - } else { - buf += 32; - } - } else { - buf += 32; - } - } - if(buf < end) { - __m512i in = _mm512_shuffle_epi8(_mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf), byteflip); - __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800))); - __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); - if(surrogates) { - __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); - __mmask32 lowsurrogates = surrogates ^ highsurrogates; - // high must be followed by low - if ((highsurrogates << 1) != lowsurrogates) { - uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1)); - uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1)); - return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high)); - } - } - } - return result(error_code::SUCCESS, len); -} - -simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept { - const char32_t * tail = icelake::validate_utf32(buf, len); - if (tail) { - return scalar::utf32::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept { - - const char32_t* end = len >= 16 ? buf + len - 16 : nullptr; - const char32_t* buf_orig = buf; - while (buf <= end) { - __m512i utf32 = _mm512_loadu_si512((const __m512i*)buf); - __mmask16 outside_range = _mm512_cmp_epu32_mask(utf32, _mm512_set1_epi32(0x10ffff), - _MM_CMPINT_GT); - if (outside_range) { - return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u32(outside_range)); - } - - __m512i utf32_off = _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000)); - - __mmask16 surrogate_range = _mm512_cmp_epu32_mask(utf32_off, _mm512_set1_epi32(0xfffff7ff), - _MM_CMPINT_GT); - if (surrogate_range) { - return result(error_code::SURROGATE, buf - buf_orig + _tzcnt_u32(surrogate_range)); - } - buf += 16; - } - if(buf < buf_orig + len) { - __m512i utf32 = _mm512_maskz_loadu_epi32(__mmask16((1<<(buf_orig + len - buf))-1),(const __m512i*)buf); - __mmask16 outside_range = _mm512_cmp_epu32_mask(utf32, _mm512_set1_epi32(0x10ffff), - _MM_CMPINT_GT); - if (outside_range) { - return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u32(outside_range)); - } - __m512i utf32_off = _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000)); - - __mmask16 surrogate_range = _mm512_cmp_epu32_mask(utf32_off, _mm512_set1_epi32(0xfffff7ff), - _MM_CMPINT_GT); - if (surrogate_range) { - return result(error_code::SURROGATE, buf - buf_orig + _tzcnt_u32(surrogate_range)); - } - } - - return result(error_code::SUCCESS, len); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept { - return icelake::latin1_to_utf8_avx512_start(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return icelake_convert_latin1_to_utf16(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return icelake_convert_latin1_to_utf16(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = avx512_convert_latin1_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - return icelake::utf8_to_latin1_avx512(buf, len, latin1_output); -} - - -simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept { - // Initialize output length and input length counters - size_t inlen = 0; - - // First, try to convert as much as possible using the SIMD implementation. - inlen = icelake::utf8_to_latin1_avx512(buf, len, latin1_output); - - // If we have completely converted the string - if(inlen == len) { - return {simdutf::SUCCESS, len}; - } - - // Else if there are remaining bytes, use the scalar function to process them. - // Note: This is assuming scalar::utf8_to_latin1::convert_with_errors is a function that takes - // the input buffer, length, and output buffer, and returns a result object with an error code - // and the number of characters processed. - result res = scalar::utf8_to_latin1::convert_with_errors(buf + inlen, len - inlen, latin1_output + inlen); - res.count += inlen; // Add the number of characters processed by the SIMD implementation - - return res; -} - - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - return icelake::valid_utf8_to_latin1_avx512(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16_result ret = fast_avx512_convert_utf8_to_utf16(buf, len, utf16_output); - if (ret.second == nullptr) { - return 0; - } - return ret.second - utf16_output; -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16_result ret = fast_avx512_convert_utf8_to_utf16(buf, len, utf16_output); - if (ret.second == nullptr) { - return 0; - } - return ret.second - utf16_output; -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return fast_avx512_convert_utf8_to_utf16_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - return fast_avx512_convert_utf8_to_utf16_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16_result ret = icelake::valid_utf8_to_fixed_length(buf, len, utf16_output); - size_t saved_bytes = ret.second - utf16_output; - const char* end = buf + len; - if (ret.first == end) { - return saved_bytes; - } - - // Note: AVX512 procedure looks up 4 bytes forward, and - // correctly converts multi-byte chars even if their - // continuation bytes lie outsiede 16-byte window. - // It meas, we have to skip continuation bytes from - // the beginning ret.first, as they were already consumed. - while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) { - ret.first += 1; - } - - if (ret.first != end) { - const size_t scalar_saved_bytes = scalar::utf8_to_utf16::convert_valid( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16_result ret = icelake::valid_utf8_to_fixed_length(buf, len, utf16_output); - size_t saved_bytes = ret.second - utf16_output; - const char* end = buf + len; - if (ret.first == end) { - return saved_bytes; - } - - // Note: AVX512 procedure looks up 4 bytes forward, and - // correctly converts multi-byte chars even if their - // continuation bytes lie outsiede 16-byte window. - // It meas, we have to skip continuation bytes from - // the beginning ret.first, as they were already consumed. - while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) { - ret.first += 1; - } - - if (ret.first != end) { - const size_t scalar_saved_bytes = scalar::utf8_to_utf16::convert_valid( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - - return saved_bytes; -} - - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_out) const noexcept { - uint32_t * utf32_output = reinterpret_cast(utf32_out); - utf8_to_utf32_result ret = icelake::validating_utf8_to_fixed_length(buf, len, utf32_output); - if (ret.second == nullptr) - return 0; - - size_t saved_bytes = ret.second - utf32_output; - const char* end = buf + len; - if (ret.first == end) { - return saved_bytes; - } - - // Note: the AVX512 procedure looks up 4 bytes forward, and - // correctly converts multi-byte chars even if their - // continuation bytes lie outside 16-byte window. - // It means, we have to skip continuation bytes from - // the beginning ret.first, as they were already consumed. - while (ret.first != end and ((uint8_t(*ret.first) & 0xc0) == 0x80)) { - ret.first += 1; - } - - if (ret.first != end) { - const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert( - ret.first, len - (ret.first - buf), utf32_out + saved_bytes); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32) const noexcept { - uint32_t * utf32_output = reinterpret_cast(utf32); - auto ret = icelake::validating_utf8_to_fixed_length_with_constant_checks(buf, len, utf32_output); - if (!std::get<2>(ret)) { - auto new_buf = std::get<0>(ret); - // rewind_and_convert_with_errors will seek a potential error from new_buf onward, - // with the ability to go back up to new_buf - buf bytes, and read len - (new_buf - buf) bytes forward. - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(new_buf - buf, new_buf, len - (new_buf - buf), reinterpret_cast(std::get<1>(ret))); - res.count += (std::get<0>(ret) - buf); - return res; - } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - const char* end = buf + len; - if (std::get<0>(ret) == end) { - return {simdutf::SUCCESS, saved_bytes}; - } - - // Note: the AVX512 procedure looks up 4 bytes forward, and - // correctly converts multi-byte chars even if their - // continuation bytes lie outside 16-byte window. - // It means, we have to skip continuation bytes from - // the beginning ret.first, as they were already consumed. - while (std::get<0>(ret) != end and ((uint8_t(*std::get<0>(ret)) & 0xc0) == 0x80)) { - std::get<0>(ret) += 1; - } - - if (std::get<0>(ret) != end) { - auto scalar_result = scalar::utf8_to_utf32::convert_with_errors( - std::get<0>(ret), len - (std::get<0>(ret) - buf), reinterpret_cast(utf32_output) + saved_bytes); - if (scalar_result.error != simdutf::SUCCESS) { - scalar_result.count += (std::get<0>(ret) - buf); - } else { - scalar_result.count += saved_bytes; - } - return scalar_result; - } - - return {simdutf::SUCCESS, size_t(std::get<1>(ret) - utf32_output)}; -} - - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_out) const noexcept { - uint32_t * utf32_output = reinterpret_cast(utf32_out); - utf8_to_utf32_result ret = icelake::valid_utf8_to_fixed_length(buf, len, utf32_output); - size_t saved_bytes = ret.second - utf32_output; - const char* end = buf + len; - if (ret.first == end) { - return saved_bytes; - } - - // Note: AVX512 procedure looks up 4 bytes forward, and - // correctly converts multi-byte chars even if their - // continuation bytes lie outsiede 16-byte window. - // It meas, we have to skip continuation bytes from - // the beginning ret.first, as they were already consumed. - while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) { - ret.first += 1; - } - - if (ret.first != end) { - const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert_valid( - ret.first, len - (ret.first - buf), utf32_out + saved_bytes); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - - return saved_bytes; -} - - -simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf16_to_latin1(buf,len,latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf16_to_latin1(buf,len,latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf16_to_latin1_with_errors(buf,len,latin1_output).first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf16_to_latin1_with_errors(buf,len,latin1_output).first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: implement custom function - return convert_utf16be_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: implement custom function - return convert_utf16le_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - size_t outlen; - size_t inlen = utf16_to_utf8_avx512i(buf, len, (unsigned char*)utf8_output, &outlen); - if(inlen != len) { return 0; } - return outlen; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - size_t outlen; - size_t inlen = utf16_to_utf8_avx512i(buf, len, (unsigned char*)utf8_output, &outlen); - if(inlen != len) { return 0; } - return outlen; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - size_t outlen; - size_t inlen = utf16_to_utf8_avx512i(buf, len, (unsigned char*)utf8_output, &outlen); - if(inlen != len) { - result res = scalar::utf16_to_utf8::convert_with_errors(buf + inlen, len - outlen, utf8_output + outlen); - res.count += inlen; - return res; - } - return {simdutf::SUCCESS, outlen}; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - size_t outlen; - size_t inlen = utf16_to_utf8_avx512i(buf, len, (unsigned char*)utf8_output, &outlen); - if(inlen != len) { - result res = scalar::utf16_to_utf8::convert_with_errors(buf + inlen, len - outlen, utf8_output + outlen); - res.count += inlen; - return res; - } - return {simdutf::SUCCESS, outlen}; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16le_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16be_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf32_to_latin1(buf,len,latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf32_to_latin1_with_errors(buf,len,latin1_output).first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return icelake_convert_utf32_to_latin1(buf,len,latin1_output); -} - - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = avx512_convert_utf32_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = icelake::avx512_convert_utf32_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf32_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = avx512_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = avx512_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = avx512_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = avx512_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16le(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16be(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::tuple ret = icelake::convert_utf16_to_utf32(buf, len, utf32_output); - if (!std::get<2>(ret)) { return 0; } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - if (std::get<0>(ret) != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::tuple ret = icelake::convert_utf16_to_utf32(buf, len, utf32_output); - if (!std::get<2>(ret)) { return 0; } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - if (std::get<0>(ret) != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::tuple ret = icelake::convert_utf16_to_utf32(buf, len, utf32_output); - if (!std::get<2>(ret)) { - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - scalar_res.count += (std::get<0>(ret) - buf); - return scalar_res; - } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - if (std::get<0>(ret) != buf + len) { - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - if (scalar_res.error) { - scalar_res.count += (std::get<0>(ret) - buf); - return scalar_res; - } else { - scalar_res.count += saved_bytes; - return scalar_res; - } - } - return simdutf::result(simdutf::SUCCESS, saved_bytes); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::tuple ret = icelake::convert_utf16_to_utf32(buf, len, utf32_output); - if (!std::get<2>(ret)) { - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - scalar_res.count += (std::get<0>(ret) - buf); - return scalar_res; - } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - if (std::get<0>(ret) != buf + len) { - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - if (scalar_res.error) { - scalar_res.count += (std::get<0>(ret) - buf); - return scalar_res; - } else { - scalar_res.count += saved_bytes; - return scalar_res; - } - } - return simdutf::result(simdutf::SUCCESS, saved_bytes); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::tuple ret = icelake::convert_utf16_to_utf32(buf, len, utf32_output); - if (!std::get<2>(ret)) { return 0; } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - if (std::get<0>(ret) != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::tuple ret = icelake::convert_utf16_to_utf32(buf, len, utf32_output); - if (!std::get<2>(ret)) { return 0; } - size_t saved_bytes = std::get<1>(ret) - utf32_output; - if (std::get<0>(ret) != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret)); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept { - size_t pos = 0; - const __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - while (pos + 32 <= length) { - __m512i utf16 = _mm512_loadu_si512((const __m512i*)(input + pos)); - utf16 = _mm512_shuffle_epi8(utf16, byteflip); - _mm512_storeu_si512(output + pos, utf16); - pos += 32; - } - if(pos < length) { - __mmask32 m((1<< (length - pos))-1); - __m512i utf16 = _mm512_maskz_loadu_epi16(m, (const __m512i*)(input + pos)); - utf16 = _mm512_shuffle_epi8(utf16, byteflip); - _mm512_mask_storeu_epi16(output + pos, m, utf16); - } -} - - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept { - const char16_t* end = length >= 32 ? input + length - 32 : nullptr; - const char16_t* ptr = input; - - const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00); - const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff); - - size_t count{0}; - - while (ptr <= end) { - __m512i utf16 = _mm512_loadu_si512((const __m512i*)ptr); - ptr += 32; - uint64_t not_high_surrogate = static_cast(_mm512_cmpgt_epu16_mask(utf16, high) | _mm512_cmplt_epu16_mask(utf16, low)); - count += count_ones(not_high_surrogate); - } - - return count + scalar::utf16::count_code_points(ptr, length - (ptr - input)); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept { - const char16_t* end = length >= 32 ? input + length - 32 : nullptr; - const char16_t* ptr = input; - - const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00); - const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff); - - size_t count{0}; - const __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - while (ptr <= end) { - __m512i utf16 = _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i*)ptr), byteflip); - ptr += 32; - uint64_t not_high_surrogate = static_cast(_mm512_cmpgt_epu16_mask(utf16, high) | _mm512_cmplt_epu16_mask(utf16, low)); - count += count_ones(not_high_surrogate); - } - - return count + scalar::utf16::count_code_points(ptr, length - (ptr - input)); -} - - -simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept { - const uint8_t *str = reinterpret_cast(input); - size_t answer = length / sizeof(__m512i) * sizeof(__m512i); // Number of 512-bit chunks that fits into the length. - size_t i = 0; - __m512i unrolled_popcount{0}; - - const __m512i continuation = _mm512_set1_epi8(char(0b10111111)); - - while (i + sizeof(__m512i) <= length) { - size_t iterations = (length - i) / sizeof(__m512i); - - size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i); - for (; i + 8*sizeof(__m512i) <= max_i; i += 8*sizeof(__m512i)) { - __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i)); - __m512i input2 = _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i))); - __m512i input3 = _mm512_loadu_si512((const __m512i *)(str + i + 2*sizeof(__m512i))); - __m512i input4 = _mm512_loadu_si512((const __m512i *)(str + i + 3*sizeof(__m512i))); - __m512i input5 = _mm512_loadu_si512((const __m512i *)(str + i + 4*sizeof(__m512i))); - __m512i input6 = _mm512_loadu_si512((const __m512i *)(str + i + 5*sizeof(__m512i))); - __m512i input7 = _mm512_loadu_si512((const __m512i *)(str + i + 6*sizeof(__m512i))); - __m512i input8 = _mm512_loadu_si512((const __m512i *)(str + i + 7*sizeof(__m512i))); - - - __mmask64 mask1 = _mm512_cmple_epi8_mask(input1, continuation); - __mmask64 mask2 = _mm512_cmple_epi8_mask(input2, continuation); - __mmask64 mask3 = _mm512_cmple_epi8_mask(input3, continuation); - __mmask64 mask4 = _mm512_cmple_epi8_mask(input4, continuation); - __mmask64 mask5 = _mm512_cmple_epi8_mask(input5, continuation); - __mmask64 mask6 = _mm512_cmple_epi8_mask(input6, continuation); - __mmask64 mask7 = _mm512_cmple_epi8_mask(input7, continuation); - __mmask64 mask8 = _mm512_cmple_epi8_mask(input8, continuation); - - __m512i mask_register = _mm512_set_epi64(mask8, mask7, mask6, mask5, mask4, mask3, mask2, mask1); - - - unrolled_popcount = _mm512_add_epi64(unrolled_popcount, _mm512_popcnt_epi64(mask_register)); - } - - for (; i <= max_i; i += sizeof(__m512i)) { - __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i)); - uint64_t continuation_bitmask = static_cast(_mm512_cmple_epi8_mask(more_input, continuation)); - answer -= count_ones(continuation_bitmask); - } - } - - __m256i first_half = _mm512_extracti64x4_epi64(unrolled_popcount, 0); - __m256i second_half = _mm512_extracti64x4_epi64(unrolled_popcount, 1); - answer -= (size_t)_mm256_extract_epi64(first_half, 0) + - (size_t)_mm256_extract_epi64(first_half, 1) + - (size_t)_mm256_extract_epi64(first_half, 2) + - (size_t)_mm256_extract_epi64(first_half, 3) + - (size_t)_mm256_extract_epi64(second_half, 0) + - (size_t)_mm256_extract_epi64(second_half, 1) + - (size_t)_mm256_extract_epi64(second_half, 2) + - (size_t)_mm256_extract_epi64(second_half, 3); - - return answer + scalar::utf8::count_code_points(reinterpret_cast(str + i), length - i); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept { - return count_utf8(buf,len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept { - return scalar::utf16::latin1_length_from_utf16(length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept { - return scalar::utf32::latin1_length_from_utf32(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - const char16_t* end = length >= 32 ? input + length - 32 : nullptr; - const char16_t* ptr = input; - - const __m512i v_007f = _mm512_set1_epi16((uint16_t)0x007f); - const __m512i v_07ff = _mm512_set1_epi16((uint16_t)0x07ff); - const __m512i v_dfff = _mm512_set1_epi16((uint16_t)0xdfff); - const __m512i v_d800 = _mm512_set1_epi16((uint16_t)0xd800); - - size_t count{0}; - - while (ptr <= end) { - __m512i utf16 = _mm512_loadu_si512((const __m512i*)ptr); - ptr += 32; - __mmask32 ascii_bitmask = _mm512_cmple_epu16_mask(utf16, v_007f); - __mmask32 two_bytes_bitmask = _mm512_mask_cmple_epu16_mask(~ascii_bitmask, utf16, v_07ff); - __mmask32 not_one_two_bytes = ~(ascii_bitmask | two_bytes_bitmask); - __mmask32 surrogates_bitmask = _mm512_mask_cmple_epu16_mask(not_one_two_bytes, utf16, v_dfff) & _mm512_mask_cmpge_epu16_mask(not_one_two_bytes, utf16, v_d800); - - size_t ascii_count = count_ones(ascii_bitmask); - size_t two_bytes_count = count_ones(two_bytes_bitmask); - size_t surrogate_bytes_count = count_ones(surrogates_bitmask); - size_t three_bytes_count = 32 - ascii_count - two_bytes_count - surrogate_bytes_count; - - count += ascii_count + 2*two_bytes_count + 3*three_bytes_count + 2*surrogate_bytes_count; - } - - return count + scalar::utf16::utf8_length_from_utf16(ptr, length - (ptr - input)); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - const char16_t* end = length >= 32 ? input + length - 32 : nullptr; - const char16_t* ptr = input; - - const __m512i v_007f = _mm512_set1_epi16((uint16_t)0x007f); - const __m512i v_07ff = _mm512_set1_epi16((uint16_t)0x07ff); - const __m512i v_dfff = _mm512_set1_epi16((uint16_t)0xdfff); - const __m512i v_d800 = _mm512_set1_epi16((uint16_t)0xd800); - - size_t count{0}; - const __m512i byteflip = _mm512_setr_epi64( - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x0607040502030001, - 0x0e0f0c0d0a0b0809 - ); - while (ptr <= end) { - __m512i utf16 = _mm512_loadu_si512((const __m512i*)ptr); - utf16 = _mm512_shuffle_epi8(utf16, byteflip); - ptr += 32; - __mmask32 ascii_bitmask = _mm512_cmple_epu16_mask(utf16, v_007f); - __mmask32 two_bytes_bitmask = _mm512_mask_cmple_epu16_mask(~ascii_bitmask, utf16, v_07ff); - __mmask32 not_one_two_bytes = ~(ascii_bitmask | two_bytes_bitmask); - __mmask32 surrogates_bitmask = _mm512_mask_cmple_epu16_mask(not_one_two_bytes, utf16, v_dfff) & _mm512_mask_cmpge_epu16_mask(not_one_two_bytes, utf16, v_d800); - - size_t ascii_count = count_ones(ascii_bitmask); - size_t two_bytes_count = count_ones(two_bytes_bitmask); - size_t surrogate_bytes_count = count_ones(surrogates_bitmask); - size_t three_bytes_count = 32 - ascii_count - two_bytes_count - surrogate_bytes_count; - count += ascii_count + 2*two_bytes_count + 3*three_bytes_count + 2*surrogate_bytes_count; - } - - return count + scalar::utf16::utf8_length_from_utf16(ptr, length - (ptr - input)); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return implementation::count_utf16le(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return implementation::count_utf16be(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf16_length_from_latin1(length); -} - - -simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf32_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t length) const noexcept { - const uint8_t *str = reinterpret_cast(input); - size_t answer = length / sizeof(__m512i) * sizeof(__m512i); - size_t i = 0; - unsigned char v_0xFF = 0xff; - __m512i eight_64bits = _mm512_setzero_si512(); - while (i + sizeof(__m512i) <= length) { - __m512i runner = _mm512_setzero_si512(); - size_t iterations = (length - i) / sizeof(__m512i); - if (iterations > 255) { - iterations = 255; - } - size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i); - for (; i + 4*sizeof(__m512i) <= max_i; i += 4*sizeof(__m512i)) { - // Load four __m512i vectors - __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i)); - __m512i input2 = _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i))); - __m512i input3 = _mm512_loadu_si512((const __m512i *)(str + i + 2*sizeof(__m512i))); - __m512i input4 = _mm512_loadu_si512((const __m512i *)(str + i + 3*sizeof(__m512i))); - - // Generate four masks - __mmask64 mask1 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input1); - __mmask64 mask2 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input2); - __mmask64 mask3 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input3); - __mmask64 mask4 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input4); - // Apply the masks and subtract from the runner - __m512i not_ascii1 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask1, v_0xFF); - __m512i not_ascii2 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask2, v_0xFF); - __m512i not_ascii3 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask3, v_0xFF); - __m512i not_ascii4 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask4, v_0xFF); - - runner = _mm512_sub_epi8(runner, not_ascii1); - runner = _mm512_sub_epi8(runner, not_ascii2); - runner = _mm512_sub_epi8(runner, not_ascii3); - runner = _mm512_sub_epi8(runner, not_ascii4); - } - - for (; i <= max_i; i += sizeof(__m512i)) { - __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i)); - - __mmask64 mask = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), more_input); - __m512i not_ascii = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask, v_0xFF); - runner = _mm512_sub_epi8(runner, not_ascii); - } - - eight_64bits = _mm512_add_epi64(eight_64bits, _mm512_sad_epu8(runner, _mm512_setzero_si512())); - } - - __m256i first_half = _mm512_extracti64x4_epi64(eight_64bits, 0); - __m256i second_half = _mm512_extracti64x4_epi64(eight_64bits, 1); - answer += (size_t)_mm256_extract_epi64(first_half, 0) + - (size_t)_mm256_extract_epi64(first_half, 1) + - (size_t)_mm256_extract_epi64(first_half, 2) + - (size_t)_mm256_extract_epi64(first_half, 3) + - (size_t)_mm256_extract_epi64(second_half, 0) + - (size_t)_mm256_extract_epi64(second_half, 1) + - (size_t)_mm256_extract_epi64(second_half, 2) + - (size_t)_mm256_extract_epi64(second_half, 3); - return answer + scalar::latin1::utf8_length_from_latin1(reinterpret_cast(str + i), length - i); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos + 64 <= length; pos += 64) { - __m512i utf8 = _mm512_loadu_si512((const __m512i*)(input+pos)); - uint64_t utf8_continuation_mask = _mm512_cmple_epi8_mask(utf8, _mm512_set1_epi8(-65+1)); - // We count one word for anything that is not a continuation (so - // leading bytes). - count += 64 - count_ones(utf8_continuation_mask); - uint64_t utf8_4byte = _mm512_cmpge_epu8_mask(utf8, _mm512_set1_epi8(int8_t(240))); - count += count_ones(utf8_4byte); - } - return count + scalar::utf8::utf16_length_from_utf8(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const char32_t* end = length >= 16 ? input + length - 16 : nullptr; - const char32_t* ptr = input; - - const __m512i v_0000_007f = _mm512_set1_epi32((uint32_t)0x7f); - const __m512i v_0000_07ff = _mm512_set1_epi32((uint32_t)0x7ff); - const __m512i v_0000_ffff = _mm512_set1_epi32((uint32_t)0x0000ffff); - - size_t count{0}; - - while (ptr <= end) { - __m512i utf32 = _mm512_loadu_si512((const __m512i*)ptr); - ptr += 16; - __mmask16 ascii_bitmask = _mm512_cmple_epu32_mask(utf32, v_0000_007f); - __mmask16 two_bytes_bitmask = _mm512_mask_cmple_epu32_mask(_knot_mask16(ascii_bitmask), utf32, v_0000_07ff); - __mmask16 three_bytes_bitmask = _mm512_mask_cmple_epu32_mask(_knot_mask16(_mm512_kor(ascii_bitmask, two_bytes_bitmask)), utf32, v_0000_ffff); - - size_t ascii_count = count_ones(ascii_bitmask); - size_t two_bytes_count = count_ones(two_bytes_bitmask); - size_t three_bytes_count = count_ones(three_bytes_bitmask); - size_t four_bytes_count = 16 - ascii_count - two_bytes_count - three_bytes_count; - count += ascii_count + 2*two_bytes_count + 3*three_bytes_count + 4*four_bytes_count; - } - - return count + scalar::utf32::utf8_length_from_utf32(ptr, length - (ptr - input)); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const char32_t* end = length >= 16 ? input + length - 16 : nullptr; - const char32_t* ptr = input; - - const __m512i v_0000_ffff = _mm512_set1_epi32((uint32_t)0x0000ffff); - - size_t count{0}; - - while (ptr <= end) { - __m512i utf32 = _mm512_loadu_si512((const __m512i*)ptr); - ptr += 16; - __mmask16 surrogates_bitmask = _mm512_cmpgt_epu32_mask(utf32, v_0000_ffff); - - count += 16 + count_ones(surrogates_bitmask); - } - - return count + scalar::utf32::utf16_length_from_utf32(ptr, length - (ptr - input)); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept { - return implementation::count_utf8(input, length); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - if(options & base64_url) { - return encode_base64(output, input, length, options); - } else { - return encode_base64(output, input, length, options); - } -} - -} // namespace icelake -} // namespace simdutf - -/* begin file src/simdutf/icelake/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_POP_DISABLE_WARNINGS -#endif // end of workaround -/* end file src/simdutf/icelake/end.h */ -/* end file src/icelake/implementation.cpp */ -#endif -#if SIMDUTF_IMPLEMENTATION_HASWELL -/* begin file src/haswell/implementation.cpp */ - -/* begin file src/simdutf/haswell/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "haswell" -// #define SIMDUTF_IMPLEMENTATION haswell - -#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL -// nothing needed. -#else -SIMDUTF_TARGET_HASWELL -#endif - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) -#endif // end of workaround -/* end file src/simdutf/haswell/begin.h */ -namespace simdutf { -namespace haswell { -namespace { -#ifndef SIMDUTF_HASWELL_H -#error "haswell.h must be included" -#endif -using namespace simd; - - -simdutf_really_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} - -simdutf_unused simdutf_really_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0b11000000u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0b11100000u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0b11110000u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdutf_really_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be > 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be > 0x80 - return simd8(is_third_byte | is_fourth_byte); -} - -/* begin file src/haswell/avx2_detect_encodings.cpp */ -template -// len is known to be a multiple of 2 when this is called -int avx2_detect_encodings(const char * buf, size_t len) { - const char* start = buf; - const char* end = buf + len; - - bool is_utf8 = true; - bool is_utf16 = true; - bool is_utf32 = true; - - int out = 0; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - - __m256i currentmax = _mm256_setzero_si256(); - - checker check{}; - - while(buf + 64 <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1); - - const auto u0 = simd16(in); - const auto u1 = simd16(nextin); - - const auto v0 = u0.shr<8>(); - const auto v1 = u1.shr<8>(); - - const auto in16 = simd16::pack(v0, v1); - - const auto surrogates_wordmask0 = (in16 & v_f8) == v_d8; - uint32_t surrogates_bitmask0 = surrogates_wordmask0.to_bitmask(); - - // Check for surrogates - if (surrogates_bitmask0 != 0x0) { - // Cannot be UTF8 - is_utf8 = false; - // Can still be either UTF-16LE or UTF-32 depending on the positions of the surrogates - // To be valid UTF-32, a surrogate cannot be in the two most significant bytes of any 32-bit word. - // On the other hand, to be valid UTF-16LE, at least one surrogate must be in the two most significant - // bytes of a 32-bit word since they always come in pairs in UTF-16LE. - // Note that we always proceed in multiple of 4 before this point so there is no offset in 32-bit code units. - - if ((surrogates_bitmask0 & 0xaaaaaaaa) != 0) { - is_utf32 = false; - // Code from avx2_validate_utf16le.cpp - const char16_t * input = reinterpret_cast(buf); - const char16_t* end16 = reinterpret_cast(start) + len/2; - - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - const uint32_t V0 = ~surrogates_bitmask0; - - const auto vH0 = (in16 & v_fc) == v_dc; - const uint32_t H0 = vH0.to_bitmask(); - - const uint32_t L0 = ~H0 & surrogates_bitmask0; - - const uint32_t a0 = L0 & (H0 >> 1); - const uint32_t b0 = a0 << 1; - const uint32_t c0 = V0 | a0 | b0; - - if (c0 == 0xffffffff) { - input += simd16::ELEMENTS * 2; - } else if (c0 == 0x7fffffff) { - input += simd16::ELEMENTS * 2 - 1; - } else { - return simdutf::encoding_type::unspecified; - } - - while (input + simd16::ELEMENTS * 2 < end16) { - const auto in0 = simd16(input); - const auto in1 = simd16(input + simd16::ELEMENTS); - - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - - const auto in_16 = simd16::pack(t0, t1); - - const auto surrogates_wordmask = (in_16 & v_f8) == v_d8; - const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask(); - if (surrogates_bitmask == 0x0) { - input += simd16::ELEMENTS * 2; - } else { - const uint32_t V = ~surrogates_bitmask; - - const auto vH = (in_16 & v_fc) == v_dc; - const uint32_t H = vH.to_bitmask(); - - const uint32_t L = ~H & surrogates_bitmask; - - const uint32_t a = L & (H >> 1); - - const uint32_t b = a << 1; - - const uint32_t c = V | a | b; - - if (c == 0xffffffff) { - input += simd16::ELEMENTS * 2; - } else if (c == 0x7fffffff) { - input += simd16::ELEMENTS * 2 - 1; - } else { - return simdutf::encoding_type::unspecified; - } - } - } - } else { - is_utf16 = false; - // Check for UTF-32 - if (len % 4 == 0) { - const char32_t * input = reinterpret_cast(buf); - const char32_t* end32 = reinterpret_cast(start) + len/4; - - // Must start checking for surrogates - __m256i currentoffsetmax = _mm256_setzero_si256(); - const __m256i offset = _mm256_set1_epi32(0xffff2000); - const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff); - - currentmax = _mm256_max_epu32(in, currentmax); - currentmax = _mm256_max_epu32(nextin, currentmax); - - currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in, offset), currentoffsetmax); - currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(nextin, offset), currentoffsetmax); - - while (input + 8 < end32) { - const __m256i in32 = _mm256_loadu_si256((__m256i *)input); - currentmax = _mm256_max_epu32(in32,currentmax); - currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in32, offset), currentoffsetmax); - input += 8; - } - - __m256i forbidden_words = _mm256_xor_si256(_mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(_mm256_testz_si256(forbidden_words, forbidden_words) == 0) { - return simdutf::encoding_type::unspecified; - } - } else { - return simdutf::encoding_type::unspecified; - } - } - break; - } - // If no surrogate, validate under other encodings as well - - // UTF-32 validation - currentmax = _mm256_max_epu32(in, currentmax); - currentmax = _mm256_max_epu32(nextin, currentmax); - - // UTF-8 validation - // Relies on ../generic/utf8_validation/utf8_lookup4_algorithm.h - simd::simd8x64 in8(in, nextin); - check.check_next_input(in8); - - buf += 64; - } - - // Check which encodings are possible - - if (is_utf8) { - if (static_cast(buf - start) != len) { - uint8_t block[64]{}; - std::memset(block, 0x20, 64); - std::memcpy(block, buf, len - (buf - start)); - simd::simd8x64 in(block); - check.check_next_input(in); - } - if (!check.errors()) { - out |= simdutf::encoding_type::UTF8; - } - } - - if (is_utf16 && scalar::utf16::validate(reinterpret_cast(buf), (len - (buf - start))/2)) { - out |= simdutf::encoding_type::UTF16_LE; - } - - if (is_utf32 && (len % 4 == 0)) { - const __m256i standardmax = _mm256_set1_epi32(0x10ffff); - __m256i is_zero = _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax); - if (_mm256_testz_si256(is_zero, is_zero) == 1 && scalar::utf32::validate(reinterpret_cast(buf), (len - (buf - start))/4)) { - out |= simdutf::encoding_type::UTF32_LE; - } - } - - return out; -} -/* end file src/haswell/avx2_detect_encodings.cpp */ - -/* begin file src/haswell/avx2_validate_utf16.cpp */ -/* - In UTF-16 code units in range 0xD800 to 0xDFFF have special meaning. - - In a vectorized algorithm we want to examine the most significant - nibble in order to select a fast path. If none of highest nibbles - are 0xD (13), than we are sure that UTF-16 chunk in a vector - register is valid. - - Let us analyze what we need to check if the nibble is 0xD. The - value of the preceding nibble determines what we have: - - 0xd000 .. 0xd7ff - a valid word - 0xd800 .. 0xdbff - low surrogate - 0xdc00 .. 0xdfff - high surrogate - - Other constraints we have to consider: - - there must not be two consecutive low surrogates (0xd800 .. 0xdbff) - - there must not be two consecutive high surrogates (0xdc00 .. 0xdfff) - - there must not be sole low surrogate nor high surrogate - - We're going to build three bitmasks based on the 3rd nibble: - - V = valid word, - - L = low surrogate (0xd800 .. 0xdbff) - - H = high surrogate (0xdc00 .. 0xdfff) - - 0 1 2 3 4 5 6 7 <--- word index - [ V | L | H | L | H | V | V | L ] - 1 0 0 0 0 1 1 0 - V = valid masks - 0 1 0 1 0 0 0 1 - L = low surrogate - 0 0 1 0 1 0 0 0 - H high surrogate - - - 1 0 0 0 0 1 1 0 V = valid masks - 0 1 0 1 0 0 0 0 a = L & (H >> 1) - 0 0 1 0 1 0 0 0 b = a << 1 - 1 1 1 1 1 1 1 0 c = V | a | b - ^ - the last bit can be zero, we just consume 7 code units - and recheck this word in the next iteration -*/ - -/* Returns: - - pointer to the last unprocessed character (a scalar fallback should check the rest); - - nullptr if an error was detected. -*/ -template -const char16_t* avx2_validate_utf16(const char16_t* input, size_t size) { - const char16_t* end = input + size; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - while (input + simd16::ELEMENTS * 2 < end) { - // 0. Load data: since the validation takes into account only higher - // byte of each word, we compress the two vectors into one which - // consists only the higher bytes. - auto in0 = simd16(input); - auto in1 = simd16(input + simd16::ELEMENTS); - - if (big_endian) { - in0 = in0.swap_bytes(); - in1 = in1.swap_bytes(); - } - - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - - const auto in = simd16::pack(t0, t1); - - // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy). - const auto surrogates_wordmask = (in & v_f8) == v_d8; - const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask(); - if (surrogates_bitmask == 0x0) { - input += simd16::ELEMENTS * 2; - } else { - // 2. We have some surrogates that have to be distinguished: - // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF) - // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF) - // - // Fact: high surrogate has 11th bit set (3rd bit in the higher word) - - // V - non-surrogate code units - // V = not surrogates_wordmask - const uint32_t V = ~surrogates_bitmask; - - // H - word-mask for high surrogates: the six highest bits are 0b1101'11 - const auto vH = (in & v_fc) == v_dc; - const uint32_t H = vH.to_bitmask(); - - // L - word mask for low surrogates - // L = not H and surrogates_wordmask - const uint32_t L = ~H & surrogates_bitmask; - - const uint32_t a = L & (H >> 1); // A low surrogate must be followed by high one. - // (A low surrogate placed in the 7th register's word - // is an exception we handle.) - const uint32_t b = a << 1; // Just mark that the opposite fact is hold, - // thanks to that we have only two masks for valid case. - const uint32_t c = V | a | b; // Combine all the masks into the final one. - - if (c == 0xffffffff) { - // The whole input register contains valid UTF-16, i.e., - // either single code units or proper surrogate pairs. - input += simd16::ELEMENTS * 2; - } else if (c == 0x7fffffff) { - // The 31 lower code units of the input register contains valid UTF-16. - // The 31 word may be either a low or high surrogate. It the next - // iteration we 1) check if the low surrogate is followed by a high - // one, 2) reject sole high surrogate. - input += simd16::ELEMENTS * 2 - 1; - } else { - return nullptr; - } - } - } - - return input; -} - - -template -const result avx2_validate_utf16_with_errors(const char16_t* input, size_t size) { - const char16_t* start = input; - const char16_t* end = input + size; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - while (input + simd16::ELEMENTS * 2 < end) { - // 0. Load data: since the validation takes into account only higher - // byte of each word, we compress the two vectors into one which - // consists only the higher bytes. - auto in0 = simd16(input); - auto in1 = simd16(input + simd16::ELEMENTS); - - if (big_endian) { - in0 = in0.swap_bytes(); - in1 = in1.swap_bytes(); - } - - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - - const auto in = simd16::pack(t0, t1); - - // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy). - const auto surrogates_wordmask = (in & v_f8) == v_d8; - const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask(); - if (surrogates_bitmask == 0x0) { - input += simd16::ELEMENTS * 2; - } else { - // 2. We have some surrogates that have to be distinguished: - // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF) - // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF) - // - // Fact: high surrogate has 11th bit set (3rd bit in the higher word) - - // V - non-surrogate code units - // V = not surrogates_wordmask - const uint32_t V = ~surrogates_bitmask; - - // H - word-mask for high surrogates: the six highest bits are 0b1101'11 - const auto vH = (in & v_fc) == v_dc; - const uint32_t H = vH.to_bitmask(); - - // L - word mask for low surrogates - // L = not H and surrogates_wordmask - const uint32_t L = ~H & surrogates_bitmask; - - const uint32_t a = L & (H >> 1); // A low surrogate must be followed by high one. - // (A low surrogate placed in the 7th register's word - // is an exception we handle.) - const uint32_t b = a << 1; // Just mark that the opposite fact is hold, - // thanks to that we have only two masks for valid case. - const uint32_t c = V | a | b; // Combine all the masks into the final one. - - if (c == 0xffffffff) { - // The whole input register contains valid UTF-16, i.e., - // either single code units or proper surrogate pairs. - input += simd16::ELEMENTS * 2; - } else if (c == 0x7fffffff) { - // The 31 lower code units of the input register contains valid UTF-16. - // The 31 word may be either a low or high surrogate. It the next - // iteration we 1) check if the low surrogate is followed by a high - // one, 2) reject sole high surrogate. - input += simd16::ELEMENTS * 2 - 1; - } else { - return result(error_code::SURROGATE, input - start); - } - } - } - - return result(error_code::SUCCESS, input - start); -} -/* end file src/haswell/avx2_validate_utf16.cpp */ -/* begin file src/haswell/avx2_validate_utf32le.cpp */ -/* Returns: - - pointer to the last unprocessed character (a scalar fallback should check the rest); - - nullptr if an error was detected. -*/ -const char32_t* avx2_validate_utf32le(const char32_t* input, size_t size) { - const char32_t* end = input + size; - - const __m256i standardmax = _mm256_set1_epi32(0x10ffff); - const __m256i offset = _mm256_set1_epi32(0xffff2000); - const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff); - __m256i currentmax = _mm256_setzero_si256(); - __m256i currentoffsetmax = _mm256_setzero_si256(); - - while (input + 8 < end) { - const __m256i in = _mm256_loadu_si256((__m256i *)input); - currentmax = _mm256_max_epu32(in,currentmax); - currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in, offset), currentoffsetmax); - input += 8; - } - __m256i is_zero = _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax); - if(_mm256_testz_si256(is_zero, is_zero) == 0) { - return nullptr; - } - - is_zero = _mm256_xor_si256(_mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(_mm256_testz_si256(is_zero, is_zero) == 0) { - return nullptr; - } - - return input; -} - - -const result avx2_validate_utf32le_with_errors(const char32_t* input, size_t size) { - const char32_t* start = input; - const char32_t* end = input + size; - - const __m256i standardmax = _mm256_set1_epi32(0x10ffff); - const __m256i offset = _mm256_set1_epi32(0xffff2000); - const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff); - __m256i currentmax = _mm256_setzero_si256(); - __m256i currentoffsetmax = _mm256_setzero_si256(); - - while (input + 8 < end) { - const __m256i in = _mm256_loadu_si256((__m256i *)input); - currentmax = _mm256_max_epu32(in,currentmax); - currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in, offset), currentoffsetmax); - - __m256i is_zero = _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax); - if(_mm256_testz_si256(is_zero, is_zero) == 0) { - return result(error_code::TOO_LARGE, input - start); - } - - is_zero = _mm256_xor_si256(_mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(_mm256_testz_si256(is_zero, is_zero) == 0) { - return result(error_code::SURROGATE, input - start); - } - input += 8; - } - - return result(error_code::SUCCESS, input - start); -} -/* end file src/haswell/avx2_validate_utf32le.cpp */ - -/* begin file src/haswell/avx2_convert_latin1_to_utf8.cpp */ -std::pair avx2_convert_latin1_to_utf8(const char *latin1_input, size_t len, - char *utf8_output) { - const char *end = latin1_input + len; - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080); - const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80); - const size_t safety_margin = 12; - - while (latin1_input + 16 + safety_margin <= end) { - __m128i in8 = _mm_loadu_si128((__m128i *)latin1_input); - // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes - const __m128i v_80 = _mm_set1_epi8((char)0x80); - if (_mm_testz_si128(in8, v_80)) { // ASCII fast path!!!! - // 1. store (16 bytes) - _mm_storeu_si128((__m128i *)utf8_output, in8); - // 2. adjust pointers - latin1_input += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // We proceed only with the first 16 bytes. - const __m256i in = _mm256_cvtepu8_epi16((in8)); - - // 1. prepare 2-byte values - // input 16-bit word : [0000|0000|aabb|bbbb] x 8 - // expected output : [1100|00aa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [0000|00aa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in, 2); - // t1 = [0000|00aa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [1100|00aa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t *row = - &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t *row_2 = - &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >> 16)] - [0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8( - utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i *)utf8_output, - _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i *)utf8_output, - _mm256_extractf128_si256(utf8_packed, 1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - latin1_input += 16; - continue; - - } // while - return std::make_pair(latin1_input, utf8_output); -} -/* end file src/haswell/avx2_convert_latin1_to_utf8.cpp */ -/* begin file src/haswell/avx2_convert_latin1_to_utf16.cpp */ -template -std::pair avx2_convert_latin1_to_utf16(const char* latin1_input, size_t len, char16_t* utf16_output) { - size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 32 - - size_t i = 0; - for (; i < rounded_len; i += 16) { - // Load 16 bytes from the address (input + i) into a xmm register - __m128i xmm0 = _mm_loadu_si128(reinterpret_cast(latin1_input + i)); - - // Zero extend each byte in xmm0 to word and put it in another xmm register - __m128i xmm1 = _mm_cvtepu8_epi16(xmm0); - - // Shift xmm0 to the right by 8 bytes - xmm0 = _mm_srli_si128(xmm0, 8); - - // Zero extend each byte in the shifted xmm0 to word in xmm0 - xmm0 = _mm_cvtepu8_epi16(xmm0); - - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - xmm0 = _mm_shuffle_epi8(xmm0, swap); - xmm1 = _mm_shuffle_epi8(xmm1, swap); - } - - // Store the contents of xmm1 into the address pointed by (output + i) - _mm_storeu_si128(reinterpret_cast<__m128i*>(utf16_output + i), xmm1); - - // Store the contents of xmm0 into the address pointed by (output + i + 8) - _mm_storeu_si128(reinterpret_cast<__m128i*>(utf16_output + i + 8), xmm0); - } - - return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len); - -} -/* end file src/haswell/avx2_convert_latin1_to_utf16.cpp */ -/* begin file src/haswell/avx2_convert_latin1_to_utf32.cpp */ -std::pair avx2_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { - size_t rounded_len = ((len | 7) ^ 7); // Round down to nearest multiple of 8 - - for (size_t i = 0; i < rounded_len; i += 8) { - // Load 8 Latin1 characters into a 64-bit register - __m128i in = _mm_loadl_epi64((__m128i*)&buf[i]); - - // Zero extend each set of 8 Latin1 characters to 8 32-bit integers using vpmovzxbd - __m256i out = _mm256_cvtepu8_epi32(in); - - // Store the results back to memory - _mm256_storeu_si256((__m256i*)&utf32_output[i], out); - } - - // return pointers pointing to where we left off - return std::make_pair(buf + rounded_len, utf32_output + rounded_len); -} - -/* end file src/haswell/avx2_convert_latin1_to_utf32.cpp */ - -/* begin file src/haswell/avx2_convert_utf8_to_utf16.cpp */ -// depends on "tables/utf8_to_utf16_tables.h" - - -// Convert up to 12 bytes from utf8 to utf16 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -template -size_t convert_masked_utf8_to_utf16(const char *input, - uint64_t utf8_end_of_code_point_mask, - char16_t *&utf16_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - // We first try a few fast paths. - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - const __m128i in = _mm_loadu_si128((__m128i *)input); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; - if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) { - // We process the data in chunks of 16 bytes. - __m256i ascii = _mm256_cvtepu8_epi16(in); - if (big_endian) { - const __m256i swap256 = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - ascii = _mm256_shuffle_epi8(ascii, swap256); - } - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output), ascii); - utf16_output += 16; // We wrote 16 16-bit characters. - return 16; // We consumed 16 bytes. - } - if(((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) { - // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte UTF-16 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - if (big_endian) composed = _mm_shuffle_epi8(composed, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed); - utf16_output += 8; // We wrote 16 bytes, 8 code points. - return 16; - } - if(input_utf8_end_of_code_point_mask == 0x924) { - // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte UTF-16 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - __m128i composed_repacked = _mm_packus_epi32(composed, composed); - if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed_repacked); - utf16_output += 4; - return 12; - } - - const uint8_t idx = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - if (idx < 64) { - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. On processors - // where pdep/pext is fast, we might be able to use a small lookup table. - const __m128i sh = - _mm_loadu_si128((const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - if (big_endian) composed = _mm_shuffle_epi8(composed, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed); - utf16_output += 6; // We wrote 12 bytes, 6 code points. There is a potential overflow of 4 bytes. - } else if (idx < 145) { - // FOUR (4) input code-code units - const __m128i sh = - _mm_loadu_si128((const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - __m128i composed_repacked = _mm_packus_epi32(composed, composed); - if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed_repacked); - utf16_output += 4; // Here we overflow by 8 bytes. - } else if (idx < 209) { - // TWO (2) input code-code units - ////////////// - // There might be garbage inputs where a leading byte mascarades as a four-byte - // leading byte (by being followed by 3 continuation byte), but is not greater than - // 0xf0. This could trigger a buffer overflow if we only counted leading - // bytes of the form 0xf0 as generating surrogate pairs, without further UTF-8 validation. - // Thus we must be careful to ensure that only leading bytes at least as large as 0xf0 generate surrogate pairs. - // We do as at the cost of an extra mask. - ///////////// - const __m128i sh = - _mm_loadu_si128((const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f)); - const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000)); - // correct for spurious high bit - const __m128i correct = - _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1); - middlehighbyte = _mm_xor_si128(correct, middlehighbyte); - const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4); - // We deliberately carry the leading four bits in highbyte if they are present, - // we remove them later when computing hightenbits. - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000)); - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6); - // When we need to generate a surrogate pair (leading byte > 0xF0), then - // the corresponding 32-bit value in 'composed' will be greater than - // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the - // location of the surrogate pairs. - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), - _mm_or_si128(highbyte_shifted, middlehighbyte_shifted)); - const __m128i composedminus = - _mm_sub_epi32(composed, _mm_set1_epi32(0x10000)); - const __m128i lowtenbits = - _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff)); - // Notice the 0x3ff mask: - const __m128i hightenbits = _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff)); - const __m128i lowtenbitsadd = - _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00)); - const __m128i hightenbitsadd = - _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800)); - const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16); - __m128i surrogates = - _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted); - uint32_t basic_buffer[4]; - uint32_t basic_buffer_swap[4]; - if (big_endian) { - _mm_storeu_si128((__m128i *)basic_buffer_swap, _mm_shuffle_epi8(composed, swap)); - surrogates = _mm_shuffle_epi8(surrogates, swap); - } - _mm_storeu_si128((__m128i *)basic_buffer, composed); - uint32_t surrogate_buffer[4]; - _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates); - for (size_t i = 0; i < 3; i++) { - if(basic_buffer[i] > 0x3c00000) { - utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff); - utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16); - utf16_output += 2; - } else { - utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i]) : uint16_t(basic_buffer[i]); - utf16_output++; - } - } - } else { - // here we know that there is an error but we do not handle errors - } - return consumed; -} -/* end file src/haswell/avx2_convert_utf8_to_utf16.cpp */ -/* begin file src/haswell/avx2_convert_utf8_to_utf32.cpp */ -// depends on "tables/utf8_to_utf16_tables.h" - - -// Convert up to 12 bytes from utf8 to utf32 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -size_t convert_masked_utf8_to_utf32(const char *input, - uint64_t utf8_end_of_code_point_mask, - char32_t *&utf32_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - // We first try a few fast paths. - const __m128i in = _mm_loadu_si128((__m128i *)input); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; - if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) { - // We process the data in chunks of 16 bytes. - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output), _mm256_cvtepu8_epi32(in)); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output+8), _mm256_cvtepu8_epi32(_mm_srli_si128(in,8))); - utf32_output += 16; // We wrote 16 32-bit characters. - return 16; // We consumed 16 bytes. - } - if(((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) { - // We want to take 8 2-byte UTF-8 code units and turn them into 8 4-byte UTF-32 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - _mm256_storeu_si256((__m256i *)utf32_output, _mm256_cvtepu16_epi32(composed)); - utf32_output += 8; // We wrote 16 bytes, 8 code points. - return 16; - } - if(input_utf8_end_of_code_point_mask == 0x924) { - // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte UTF-32 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - _mm_storeu_si128((__m128i *)utf32_output, composed); - utf32_output += 4; - return 12; - } - /// We do not have a fast path available, so we fallback. - - const uint8_t idx = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - if (idx < 64) { - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. On processors - // where pdep/pext is fast, we might be able to use a small lookup table. - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - _mm256_storeu_si256((__m256i *)utf32_output, _mm256_cvtepu16_epi32(composed)); - utf32_output += 6; // We wrote 24 bytes, 6 code points. There is a potential - // overflow of 32 - 24 = 8 bytes. - } else if (idx < 145) { - // FOUR (4) input code-code units - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - _mm_storeu_si128((__m128i *)utf32_output, composed); - utf32_output += 4; - } else if (idx < 209) { - // TWO (2) input code-code units - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f)); - const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000)); - // correct for spurious high bit - const __m128i correct = - _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1); - middlehighbyte = _mm_xor_si128(correct, middlehighbyte); - const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000)); - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), - _mm_or_si128(highbyte_shifted, middlehighbyte_shifted)); - _mm_storeu_si128((__m128i *)utf32_output, composed); - utf32_output += 3; // We wrote 3 * 4 bytes, there is a potential overflow of 4 bytes. - } else { - // here we know that there is an error but we do not handle errors - } - return consumed; -} -/* end file src/haswell/avx2_convert_utf8_to_utf32.cpp */ - -/* begin file src/haswell/avx2_convert_utf16_to_latin1.cpp */ -template -std::pair -avx2_convert_utf16_to_latin1(const char16_t *buf, size_t len, - char *latin1_output) { - const char16_t *end = buf + len; - while (buf + 16 <= end) { - // Load 16 UTF-16 characters into 256-bit AVX2 register - __m256i in = _mm256_loadu_si256(reinterpret_cast(buf)); - - if (!match_system(big_endian)) { - const __m256i swap = _mm256_setr_epi8( - 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, - 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - in = _mm256_shuffle_epi8(in, swap); - } - - __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00); - if (_mm256_testz_si256(in, high_byte_mask)) { - // Pack 16-bit characters into 8-bit and store in latin1_output - __m128i lo = _mm256_extractf128_si256(in, 0); - __m128i hi = _mm256_extractf128_si256(in, 1); - __m128i latin1_packed_lo = _mm_packus_epi16(lo, lo); - __m128i latin1_packed_hi = _mm_packus_epi16(hi, hi); - _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output), - latin1_packed_lo); - _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output + 8), - latin1_packed_hi); - // Adjust pointers for next iteration - buf += 16; - latin1_output += 16; - } else { - return std::make_pair(nullptr, reinterpret_cast(latin1_output)); - } - } // while - return std::make_pair(buf, latin1_output); -} - -template -std::pair -avx2_convert_utf16_to_latin1_with_errors(const char16_t *buf, size_t len, - char *latin1_output) { - const char16_t *start = buf; - const char16_t *end = buf + len; - while (buf + 16 <= end) { - __m256i in = _mm256_loadu_si256(reinterpret_cast(buf)); - - if (!big_endian) { - const __m256i swap = _mm256_setr_epi8( - 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, - 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - in = _mm256_shuffle_epi8(in, swap); - } - - __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00); - if (_mm256_testz_si256(in, high_byte_mask)) { - __m128i lo = _mm256_extractf128_si256(in, 0); - __m128i hi = _mm256_extractf128_si256(in, 1); - __m128i latin1_packed_lo = _mm_packus_epi16(lo, lo); - __m128i latin1_packed_hi = _mm_packus_epi16(hi, hi); - _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output), - latin1_packed_lo); - _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output + 8), - latin1_packed_hi); - buf += 16; - latin1_output += 16; - } else { - // Fallback to scalar code for handling errors - for (int k = 0; k < 16; k++) { - uint16_t word = !match_system(big_endian) - ? scalar::utf16::swap_bytes(buf[k]) - : buf[k]; - if (word <= 0xff) { - *latin1_output++ = char(word); - } else { - return std::make_pair( - result{error_code::TOO_LARGE, (size_t)(buf - start + k)}, - latin1_output); - } - } - buf += 16; - } - } // while - return std::make_pair(result{error_code::SUCCESS, (size_t)(buf - start)}, - latin1_output); -} -/* end file src/haswell/avx2_convert_utf16_to_latin1.cpp */ -/* begin file src/haswell/avx2_convert_utf16_to_utf8.cpp */ -/* - The vectorized algorithm works on single SSE register i.e., it - loads eight 16-bit code units. - - We consider three cases: - 1. an input register contains no surrogates and each value - is in range 0x0000 .. 0x07ff. - 2. an input register contains no surrogates and values are - is in range 0x0000 .. 0xffff. - 3. an input register contains surrogates --- i.e. codepoints - can have 16 or 32 bits. - - Ad 1. - - When values are less than 0x0800, it means that a 16-bit code unit - can be converted into: 1) single UTF8 byte (when it's an ASCII - char) or 2) two UTF8 bytes. - - For this case we do only some shuffle to obtain these 2-byte - codes and finally compress the whole SSE register with a single - shuffle. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - Ad 2. - - When values fit in 16-bit code units, but are above 0x07ff, then - a single word may produce one, two or three UTF8 bytes. - - We prepare data for all these three cases in two registers. - The first register contains lower two UTF8 bytes (used in all - cases), while the second one contains just the third byte for - the three-UTF8-bytes case. - - Finally these two registers are interleaved forming eight-element - array of 32-bit values. The array spans two SSE registers. - The bytes from the registers are compressed using two shuffles. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - - To summarize: - - We need two 256-entry tables that have 8704 bytes in total. -*/ - - -/* - Returns a pair: the first unprocessed byte from buf and utf8_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::pair avx2_convert_utf16_to_utf8(const char16_t* buf, size_t len, char* utf8_output) { - const char16_t* end = buf + len; - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800); - const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - if (big_endian) { - const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - in = _mm256_shuffle_epi8(in, swap); - } - // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes - const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80); - if(_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!! - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1)); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000); - const uint32_t one_or_two_bytes_bitmask = static_cast(_mm256_movemask_epi8(one_or_two_bytes_bytemask)); - if (one_or_two_bytes_bitmask == 0xffffffff) { - - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in, 2); - // t1 = [000a|aaaa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - buf += 16; - continue; - } - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint32_t surrogates_bitmask = static_cast(_mm256_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x00000000) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e, - 0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm256_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m256i t0 = _mm256_shuffle_epi8(in, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m256i s0 = _mm256_srli_epi16(in, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000)); - const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m256i s4 = _mm256_xor_si256(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m256i out0 = _mm256_unpacklo_epi16(t2, s4); - const __m256i out1 = _mm256_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint32_t mask = (one_byte_bitmask & 0x55555555) | - (one_or_two_bytes_bitmask & 0xaaaaaaaa); - // Due to the wider registers, the following path is less likely to be useful. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle); - const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1)); - utf8_output += 12; - buf += 16; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1); - - const uint8_t mask2 = static_cast(mask >> 16); - const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0]; - const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1)); - const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2); - - - const uint8_t mask3 = static_cast(mask >> 24); - const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0]; - const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1)); - const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_2); - utf8_output += row2[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_3); - utf8_output += row3[0]; - buf += 16; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word & 0xFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xF800 ) != 0xD800) { - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf8_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - return std::make_pair(buf, utf8_output); -} - - -/* - Returns a pair: a result struct and utf8_output. - If there is an error, the count field of the result is the position of the error. - Otherwise, it is the position of the first unprocessed byte in buf (even if finished). - A scalar routing should carry on the conversion of the tail if needed. -*/ -template -std::pair avx2_convert_utf16_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) { - const char16_t* start = buf; - const char16_t* end = buf + len; - - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800); - const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - if (big_endian) { - const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - in = _mm256_shuffle_epi8(in, swap); - } - // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes - const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80); - if(_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!! - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1)); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000); - const uint32_t one_or_two_bytes_bitmask = static_cast(_mm256_movemask_epi8(one_or_two_bytes_bytemask)); - if (one_or_two_bytes_bitmask == 0xffffffff) { - - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in, 2); - // t1 = [000a|aaaa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - buf += 16; - continue; - } - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint32_t surrogates_bitmask = static_cast(_mm256_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x00000000) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e, - 0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm256_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m256i t0 = _mm256_shuffle_epi8(in, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m256i s0 = _mm256_srli_epi16(in, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000)); - const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m256i s4 = _mm256_xor_si256(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m256i out0 = _mm256_unpacklo_epi16(t2, s4); - const __m256i out1 = _mm256_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint32_t mask = (one_byte_bitmask & 0x55555555) | - (one_or_two_bytes_bitmask & 0xaaaaaaaa); - // Due to the wider registers, the following path is less likely to be useful. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle); - const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1)); - utf8_output += 12; - buf += 16; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1); - - const uint8_t mask2 = static_cast(mask >> 16); - const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0]; - const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1)); - const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2); - - - const uint8_t mask3 = static_cast(mask >> 24); - const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0]; - const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1)); - const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_2); - utf8_output += row2[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_3); - utf8_output += row3[0]; - buf += 16; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word & 0xFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xF800 ) != 0xD800) { - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf8_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output); -} -/* end file src/haswell/avx2_convert_utf16_to_utf8.cpp */ -/* begin file src/haswell/avx2_convert_utf16_to_utf32.cpp */ -/* - The vectorized algorithm works on single SSE register i.e., it - loads eight 16-bit code units. - - We consider three cases: - 1. an input register contains no surrogates and each value - is in range 0x0000 .. 0x07ff. - 2. an input register contains no surrogates and values are - in range 0x0000 .. 0xffff. - 3. an input register contains surrogates --- i.e. codepoints - can have 16 or 32 bits. - - Ad 1. - - When values are less than 0x0800, it means that a 16-bit code unit - can be converted into: 1) single UTF8 byte (when it's an ASCII - char) or 2) two UTF8 bytes. - - For this case we do only some shuffle to obtain these 2-byte - codes and finally compress the whole SSE register with a single - shuffle. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - Ad 2. - - When values fit in 16-bit code units, but are above 0x07ff, then - a single word may produce one, two or three UTF8 bytes. - - We prepare data for all these three cases in two registers. - The first register contains lower two UTF8 bytes (used in all - cases), while the second one contains just the third byte for - the three-UTF8-bytes case. - - Finally these two registers are interleaved forming eight-element - array of 32-bit values. The array spans two SSE registers. - The bytes from the registers are compressed using two shuffles. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - - To summarize: - - We need two 256-entry tables that have 8704 bytes in total. -*/ - - -/* - Returns a pair: the first unprocessed byte from buf and utf32_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::pair avx2_convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) { - const char16_t* end = buf + len; - const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800); - - while (buf + 16 <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - if (big_endian) { - const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - in = _mm256_shuffle_epi8(in, swap); - } - - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint32_t surrogates_bitmask = static_cast(_mm256_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x00000000) { - // case: we extend all sixteen 16-bit code units to sixteen 32-bit code units - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output), _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in))); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output + 8), _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in,1))); - utf32_output += 16; - buf += 16; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word &0xF800 ) != 0xD800) { - // No surrogate pair - *utf32_output++ = char32_t(word); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf32_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - } - } - buf += k; - } - } // while - return std::make_pair(buf, utf32_output); -} - - -/* - Returns a pair: a result struct and utf8_output. - If there is an error, the count field of the result is the position of the error. - Otherwise, it is the position of the first unprocessed byte in buf (even if finished). - A scalar routing should carry on the conversion of the tail if needed. -*/ -template -std::pair avx2_convert_utf16_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) { - const char16_t* start = buf; - const char16_t* end = buf + len; - const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800); - - while (buf + 16 <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - if (big_endian) { - const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, - 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30); - in = _mm256_shuffle_epi8(in, swap); - } - - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint32_t surrogates_bitmask = static_cast(_mm256_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x00000000) { - // case: we extend all sixteen 16-bit code units to sixteen 32-bit code units - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output), _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in))); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output + 8), _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in,1))); - utf32_output += 16; - buf += 16; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word &0xF800 ) != 0xD800) { - // No surrogate pair - *utf32_output++ = char32_t(word); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf32_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - } - } - buf += k; - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output); -} -/* end file src/haswell/avx2_convert_utf16_to_utf32.cpp */ - -/* begin file src/haswell/avx2_convert_utf32_to_latin1.cpp */ -std::pair -avx2_convert_utf32_to_latin1(const char32_t *buf, size_t len, - char *latin1_output) { - const size_t rounded_len = - len & ~0x1F; // Round down to nearest multiple of 32 - - __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00); - - __m256i shufmask = _mm256_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 12, 8, 4, 0, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 12, 8, 4, 0); - - for (size_t i = 0; i < rounded_len; i += 16) { - __m256i in1 = _mm256_loadu_si256((__m256i *)buf); - __m256i in2 = _mm256_loadu_si256((__m256i *)(buf + 8)); - - __m256i check_combined = _mm256_or_si256(in1, in2); - - if (!_mm256_testz_si256(check_combined, high_bytes_mask)) { - return std::make_pair(nullptr, latin1_output); - } - - //Turn UTF32 bytes into latin 1 bytes - __m256i shuffled1 = _mm256_shuffle_epi8(in1, shufmask); - __m256i shuffled2 = _mm256_shuffle_epi8(in2, shufmask); - - //move Latin1 bytes to their correct spot - __m256i idx1 = _mm256_set_epi32(-1, -1,-1,-1,-1,-1,4,0); - __m256i idx2 = _mm256_set_epi32(-1, -1,-1,-1,4,0,-1,-1); - __m256i reshuffled1 = _mm256_permutevar8x32_epi32(shuffled1, idx1); - __m256i reshuffled2 = _mm256_permutevar8x32_epi32(shuffled2, idx2); - - __m256i result = _mm256_or_si256(reshuffled1, reshuffled2); - _mm_storeu_si128((__m128i *)latin1_output, - _mm256_castsi256_si128(result)); - - latin1_output += 16; - buf += 16; - } - - return std::make_pair(buf, latin1_output); -} -std::pair -avx2_convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len, - char *latin1_output) { - const size_t rounded_len = - len & ~0x1F; // Round down to nearest multiple of 32 - - __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00); - __m256i shufmask = _mm256_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 12, 8, 4, 0, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 12, 8, 4, 0); - - const char32_t *start = buf; - - for (size_t i = 0; i < rounded_len; i += 16) { - __m256i in1 = _mm256_loadu_si256((__m256i *)buf); - __m256i in2 = _mm256_loadu_si256((__m256i *)(buf + 8)); - - __m256i check_combined = _mm256_or_si256(in1, in2); - - if (!_mm256_testz_si256(check_combined, high_bytes_mask)) { - // Fallback to scalar code for handling errors - for (int k = 0; k < 8; k++) { - char32_t codepoint = buf[k]; - if (codepoint <= 0xFF) { - *latin1_output++ = static_cast(codepoint); - } else { - return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), - latin1_output); - } - } - buf += 8; - } else { - __m256i shuffled1 = _mm256_shuffle_epi8(in1, shufmask); - __m256i shuffled2 = _mm256_shuffle_epi8(in2, shufmask); - - __m256i idx1 = _mm256_set_epi32(-1, -1, -1, -1, -1, -1, 4, 0); - __m256i idx2 = _mm256_set_epi32(-1, -1, -1, -1, 4, 0, -1, -1); - __m256i reshuffled1 = _mm256_permutevar8x32_epi32(shuffled1, idx1); - __m256i reshuffled2 = _mm256_permutevar8x32_epi32(shuffled2, idx2); - - __m256i result = _mm256_or_si256(reshuffled1, reshuffled2); - _mm_storeu_si128((__m128i *)latin1_output, _mm256_castsi256_si128(result)); - - latin1_output += 16; - buf += 16; - } - } - - return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output); -} -/* end file src/haswell/avx2_convert_utf32_to_latin1.cpp */ -/* begin file src/haswell/avx2_convert_utf32_to_utf8.cpp */ -std::pair avx2_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { - const char32_t* end = buf + len; - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000); - const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80); - const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800); - const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080); - const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff); - __m256i running_max = _mm256_setzero_si256(); - __m256i forbidden_bytemask = _mm256_setzero_si256(); - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1); - running_max = _mm256_max_epu32(_mm256_max_epu32(in, running_max), nextin); - - // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation - __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff)); - in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000); - - // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp) - - if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!! - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1)); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000); - const uint32_t one_or_two_bytes_bitmask = static_cast(_mm256_movemask_epi8(one_or_two_bytes_bytemask)); - if (one_or_two_bytes_bitmask == 0xffffffff) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in_16, 2); - // t1 = [000a|aaaa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in_16, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - buf += 16; - continue; - } - // Must check for overflow in packing - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - if (saturation_bitmask == 0xffffffff) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800); - forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800)); - - const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e, - 0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm256_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m256i s0 = _mm256_srli_epi16(in_16, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000)); - const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m256i s4 = _mm256_xor_si256(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m256i out0 = _mm256_unpacklo_epi16(t2, s4); - const __m256i out1 = _mm256_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint32_t mask = (one_byte_bitmask & 0x55555555) | - (one_or_two_bytes_bitmask & 0xaaaaaaaa); - // Due to the wider registers, the following path is less likely to be useful. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle); - const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1)); - utf8_output += 12; - buf += 16; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1); - - const uint8_t mask2 = static_cast(mask >> 16); - const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0]; - const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1)); - const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2); - - - const uint8_t mask3 = static_cast(mask >> 24); - const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0]; - const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1)); - const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_2); - utf8_output += row2[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_3); - utf8_output += row3[0]; - buf += 16; - } else { - // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes. - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // may require large, non-trivial tables? - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII) - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { // 2-byte - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word & 0xFFFF0000 )==0) { // 3-byte - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf8_output); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { // 4-byte - if (word > 0x10FFFF) { return std::make_pair(nullptr, utf8_output); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - // check for invalid input - const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff); - if(static_cast(_mm256_movemask_epi8(_mm256_cmpeq_epi32(_mm256_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffffffff) { - return std::make_pair(nullptr, utf8_output); - } - - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf8_output); } - - return std::make_pair(buf, utf8_output); -} - - -std::pair avx2_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) { - const char32_t* end = buf + len; - const char32_t* start = buf; - - const __m256i v_0000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000); - const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80); - const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800); - const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080); - const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff); - const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff); - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1); - // Check for too large input - const __m256i max_input = _mm256_max_epu32(_mm256_max_epu32(in, nextin), v_10ffff); - if(static_cast(_mm256_movemask_epi8(_mm256_cmpeq_epi32(max_input, v_10ffff))) != 0xffffffff) { - return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output); - } - - // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation - __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff)); - in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000); - - // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp) - - if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!! - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1)); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - // no bits set above 7th bit - const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000); - const uint32_t one_byte_bitmask = static_cast(_mm256_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000); - const uint32_t one_or_two_bytes_bitmask = static_cast(_mm256_movemask_epi8(one_or_two_bytes_bytemask)); - if (one_or_two_bytes_bitmask == 0xffffffff) { - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00); - const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m256i t0 = _mm256_slli_epi16(in_16, 2); - // t1 = [000a|aaaa|0000|0000] - const __m256i t1 = _mm256_and_si256(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m256i t2 = _mm256_and_si256(in_16, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m256i t3 = _mm256_or_si256(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m256i t4 = _mm256_or_si256(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - const uint32_t M0 = one_byte_bitmask & 0x55555555; - const uint32_t M1 = M0 >> 7; - const uint32_t M2 = (M1 | M0) & 0x00ff00ff; - // 4. pack the bytes - - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0]; - const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0]; - - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1)); - - const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2)); - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed)); - utf8_output += row[0]; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1)); - utf8_output += row_2[0]; - - // 6. adjust pointers - buf += 16; - continue; - } - // Must check for overflow in packing - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - if (saturation_bitmask == 0xffffffff) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - - // Check for illegal surrogate code units - const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800); - const __m256i forbidden_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800); - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), utf8_output); - } - - const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e, - 0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm256_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m256i s0 = _mm256_srli_epi16(in_16, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000)); - const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m256i s4 = _mm256_xor_si256(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m256i out0 = _mm256_unpacklo_epi16(t2, s4); - const __m256i out1 = _mm256_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint32_t mask = (one_byte_bitmask & 0x55555555) | - (one_or_two_bytes_bitmask & 0xaaaaaaaa); - // Due to the wider registers, the following path is less likely to be useful. - /*if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle); - const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1)); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1)); - utf8_output += 12; - buf += 16; - continue; - }*/ - const uint8_t mask0 = uint8_t(mask); - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1); - - const uint8_t mask2 = static_cast(mask >> 16); - const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0]; - const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1)); - const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2); - - - const uint8_t mask3 = static_cast(mask >> 24); - const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0]; - const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1)); - const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_2); - utf8_output += row2[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_3); - utf8_output += row3[0]; - buf += 16; - } else { - // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes. - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // may require large, non-trivial tables? - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII) - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { // 2-byte - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word & 0xFFFF0000 )==0) { // 3-byte - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf8_output); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { // 4-byte - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf8_output); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output); -} -/* end file src/haswell/avx2_convert_utf32_to_utf8.cpp */ -/* begin file src/haswell/avx2_convert_utf32_to_utf16.cpp */ -template -std::pair avx2_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_output) { - const char32_t* end = buf + len; - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - __m256i forbidden_bytemask = _mm256_setzero_si256(); - - - while (buf + 8 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - - const __m256i v_00000000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000); - - // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - - if (saturation_bitmask == 0xffffffff) { - const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800); - forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800)); - - __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1)); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - utf16_packed = _mm_shuffle_epi8(utf16_packed, swap); - } - _mm_storeu_si128((__m128i*)utf16_output, utf16_packed); - utf16_output += 8; - buf += 8; - } else { - size_t forward = 7; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf16_output); } - *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(nullptr, utf16_output); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (big_endian) { - high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8)); - low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8)); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - // check for invalid input - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf16_output); } - - return std::make_pair(buf, utf16_output); -} - - -template -std::pair avx2_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) { - const char32_t* start = buf; - const char32_t* end = buf + len; - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 8 + safety_margin <= end) { - __m256i in = _mm256_loadu_si256((__m256i*)buf); - - const __m256i v_00000000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000); - - // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs - const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000); - const uint32_t saturation_bitmask = static_cast(_mm256_movemask_epi8(saturation_bytemask)); - - if (saturation_bitmask == 0xffffffff) { - const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800); - const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800); - const __m256i forbidden_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800); - if (static_cast(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), utf16_output); - } - - __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1)); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - utf16_packed = _mm_shuffle_epi8(utf16_packed, swap); - } - _mm_storeu_si128((__m128i*)utf16_output, utf16_packed); - utf16_output += 8; - buf += 8; - } else { - size_t forward = 7; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf16_output); } - *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf16_output); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (big_endian) { - high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8)); - low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8)); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output); -} -/* end file src/haswell/avx2_convert_utf32_to_utf16.cpp */ - -/* begin file src/haswell/avx2_convert_utf8_to_latin1.cpp */ -// depends on "tables/utf8_to_utf16_tables.h" - -// Convert up to 12 bytes from utf8 to latin1 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -size_t convert_masked_utf8_to_latin1(const char *input, - uint64_t utf8_end_of_code_point_mask, - char *&latin1_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - const __m128i in = _mm_loadu_si128((__m128i *)input); - const __m128i in_second_half = _mm_loadu_si128((__m128i *)(input + 16)); - - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; //we're only processing 12 bytes in case it`s not all ASCII - - if((input_utf8_end_of_code_point_mask & 0xffffffff) == 0xffffffff) { - // Load the next 128 bits. - - // Combine the two 128-bit registers into a single 256-bit register. - __m256i in_combined = _mm256_set_m128i(in_second_half, in); - - // We process the data in chunks of 32 bytes. - _mm256_storeu_si256(reinterpret_cast<__m256i *>(latin1_output), in_combined); - - latin1_output += 32; // We wrote 32 characters. - return 32; // We consumed 32 bytes. - } - - - if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) { - // We process the data in chunks of 16 bytes. - _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in); - latin1_output += 16; // We wrote 16 characters. - return 16; // We consumed 16 bytes. - } - /// We do not have a fast path available, so we fallback. - const uint8_t idx = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - // this indicates an invalid input: - if(idx >= 64) { return consumed; } - // Here we should have (idx < 64), if not, there is a bug in the validation or elsewhere. - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. On processors - // where pdep/pext is fast, we might be able to use a small lookup table. - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - const __m128i latin1_packed = _mm_packus_epi16(composed,composed); - // writing 8 bytes even though we only care about the first 6 bytes. - // performance note: it would be faster to use _mm_storeu_si128, we should investigate. - _mm_storel_epi64((__m128i *)latin1_output, latin1_packed); - latin1_output += 6; // We wrote 6 bytes. - return consumed; -} -/* end file src/haswell/avx2_convert_utf8_to_latin1.cpp */ - -/* begin file src/haswell/avx2_base64.cpp */ -/** - * References and further reading: - * - * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the - * speed of a memory copy, Software: Practice and Experience 50 (2), 2020. - * https://arxiv.org/abs/1910.05109 - * - * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 - * Instructions, ACM Transactions on the Web 12 (3), 2018. - * https://arxiv.org/abs/1704.00605 - * - * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings. - * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force, - * Request for Comments: 4648. - * - * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization. - * http://www.alfredklomp.com/programming/sse-base64/. (2014). - * - * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD - * acceleration. https://github.com/aklomp/base64. (2014). - * - * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014). - * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/ - * - * Nick Kopp. 2013. Base64 Encoding on a GPU. - * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). - */ - -template -simdutf_really_inline __m256i lookup_pshufb_improved(const __m256i input) { - // credit: Wojciech Muła - __m256i result = _mm256_subs_epu8(input, _mm256_set1_epi8(51)); - const __m256i less = _mm256_cmpgt_epi8(_mm256_set1_epi8(26), input); - result = - _mm256_or_si256(result, _mm256_and_si256(less, _mm256_set1_epi8(13))); - __m256i shift_LUT; - if (base64_url) { - shift_LUT = _mm256_setr_epi8( - 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '0' - 52, '0' - 52, '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0, - - 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '0' - 52, '0' - 52, '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0); - } else { - shift_LUT = _mm256_setr_epi8( - 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '0' - 52, '0' - 52, '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0, - - 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '0' - 52, '0' - 52, '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0); - } - - result = _mm256_shuffle_epi8(shift_LUT, result); - return _mm256_add_epi8(result, input); -} - -template -size_t encode_base64(char *dst, const char *src, size_t srclen) { - // credit: Wojciech Muła - const uint8_t *input = (const uint8_t *)src; - - uint8_t *out = (uint8_t *)dst; - const __m256i shuf = - _mm256_set_epi8(10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1, - - 10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1); - size_t i = 0; - for (; i + 100 <= srclen; i += 96) { - const __m128i lo0 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 0)); - const __m128i hi0 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 1)); - const __m128i lo1 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 2)); - const __m128i hi1 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 3)); - const __m128i lo2 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 4)); - const __m128i hi2 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 5)); - const __m128i lo3 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 6)); - const __m128i hi3 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 7)); - - __m256i in0 = _mm256_shuffle_epi8(_mm256_set_m128i(hi0, lo0), shuf); - __m256i in1 = _mm256_shuffle_epi8(_mm256_set_m128i(hi1, lo1), shuf); - __m256i in2 = _mm256_shuffle_epi8(_mm256_set_m128i(hi2, lo2), shuf); - __m256i in3 = _mm256_shuffle_epi8(_mm256_set_m128i(hi3, lo3), shuf); - - const __m256i t0_0 = _mm256_and_si256(in0, _mm256_set1_epi32(0x0fc0fc00)); - const __m256i t0_1 = _mm256_and_si256(in1, _mm256_set1_epi32(0x0fc0fc00)); - const __m256i t0_2 = _mm256_and_si256(in2, _mm256_set1_epi32(0x0fc0fc00)); - const __m256i t0_3 = _mm256_and_si256(in3, _mm256_set1_epi32(0x0fc0fc00)); - - const __m256i t1_0 = - _mm256_mulhi_epu16(t0_0, _mm256_set1_epi32(0x04000040)); - const __m256i t1_1 = - _mm256_mulhi_epu16(t0_1, _mm256_set1_epi32(0x04000040)); - const __m256i t1_2 = - _mm256_mulhi_epu16(t0_2, _mm256_set1_epi32(0x04000040)); - const __m256i t1_3 = - _mm256_mulhi_epu16(t0_3, _mm256_set1_epi32(0x04000040)); - - const __m256i t2_0 = _mm256_and_si256(in0, _mm256_set1_epi32(0x003f03f0)); - const __m256i t2_1 = _mm256_and_si256(in1, _mm256_set1_epi32(0x003f03f0)); - const __m256i t2_2 = _mm256_and_si256(in2, _mm256_set1_epi32(0x003f03f0)); - const __m256i t2_3 = _mm256_and_si256(in3, _mm256_set1_epi32(0x003f03f0)); - - const __m256i t3_0 = - _mm256_mullo_epi16(t2_0, _mm256_set1_epi32(0x01000010)); - const __m256i t3_1 = - _mm256_mullo_epi16(t2_1, _mm256_set1_epi32(0x01000010)); - const __m256i t3_2 = - _mm256_mullo_epi16(t2_2, _mm256_set1_epi32(0x01000010)); - const __m256i t3_3 = - _mm256_mullo_epi16(t2_3, _mm256_set1_epi32(0x01000010)); - - const __m256i input0 = _mm256_or_si256(t1_0, t3_0); - const __m256i input1 = _mm256_or_si256(t1_1, t3_1); - const __m256i input2 = _mm256_or_si256(t1_2, t3_2); - const __m256i input3 = _mm256_or_si256(t1_3, t3_3); - - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input0)); - out += 32; - - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input1)); - out += 32; - - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input2)); - out += 32; - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input3)); - out += 32; - } - for (; i + 28 <= srclen; i += 24) { - // lo = [xxxx|DDDC|CCBB|BAAA] - // hi = [xxxx|HHHG|GGFF|FEEE] - const __m128i lo = - _mm_loadu_si128(reinterpret_cast(input + i)); - const __m128i hi = - _mm_loadu_si128(reinterpret_cast(input + i + 4 * 3)); - - // bytes from groups A, B and C are needed in separate 32-bit lanes - // in = [0HHH|0GGG|0FFF|0EEE[0DDD|0CCC|0BBB|0AAA] - __m256i in = _mm256_shuffle_epi8(_mm256_set_m128i(hi, lo), shuf); - - // this part is well commented in encode.sse.cpp - - const __m256i t0 = _mm256_and_si256(in, _mm256_set1_epi32(0x0fc0fc00)); - const __m256i t1 = _mm256_mulhi_epu16(t0, _mm256_set1_epi32(0x04000040)); - const __m256i t2 = _mm256_and_si256(in, _mm256_set1_epi32(0x003f03f0)); - const __m256i t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010)); - const __m256i indices = _mm256_or_si256(t1, t3); - - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(indices)); - out += 32; - } - return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, - srclen - i, options); -} - -static inline void compress(__m128i data, uint16_t mask, char *output) { - if (mask == 0) { - _mm_storeu_si128(reinterpret_cast<__m128i *>(output), data); - return; - } - // this particular implementation was inspired by work done by @animetosho - // we do it in two steps, first 8 bytes and then second 8 bytes - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - // next line just loads the 64-bit values thintable_epi8[mask1] and - // thintable_epi8[mask2] into a 128-bit register, using only - // two instructions on most compilers. - - __m128i shufmask = _mm_set_epi64x(tables::base64::thintable_epi8[mask2], - tables::base64::thintable_epi8[mask1]); - // we increment by 0x08 the second half of the mask - shufmask = - _mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0)); - // this is the version "nearly pruned" - __m128i pruned = _mm_shuffle_epi8(data, shufmask); - // we still need to put the two halves together. - // we compute the popcount of the first half: - int pop1 = tables::base64::BitsSetTable256mul2[mask1]; - // then load the corresponding mask, what it does is to write - // only the first pop1 bytes from the first 8 bytes, and then - // it fills in with the bytes from the second 8 bytes + some filling - // at the end. - __m128i compactmask = _mm_loadu_si128(reinterpret_cast( - tables::base64::pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - - _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); -} - -static inline void compress(__m256i data, uint32_t mask, char *output) { - if (mask == 0) { - _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), data); - return; - } - compress(_mm256_castsi256_si128(data), uint16_t(mask), output); - compress(_mm256_extracti128_si256(data, 1), uint16_t(mask >> 16), - output + _mm_popcnt_u32(~mask & 0xFFFF)); -} - -struct block64 { - __m256i chunks[2]; -}; - -template -static inline uint32_t to_base64_mask(__m256i *src, bool *error) { - const __m256i ascii_space_tbl = - _mm256_setr_epi8(0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xa, - 0x0, 0xc, 0xd, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0); - // credit: aqrit - __m256i delta_asso; - if (base64_url) { - delta_asso = - _mm256_setr_epi8(0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0xF, 0x0, 0xF, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF); - } else { - delta_asso = _mm256_setr_epi8( - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0F, 0x00, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F); - } - - __m256i delta_values; - if (base64_url) { - delta_values = _mm256_setr_epi8( - 0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF), uint8_t(0xBF), uint8_t(0xB9), - uint8_t(0xB9), 0x0, 0x11, uint8_t(0xC3), uint8_t(0xBF), uint8_t(0xE0), - uint8_t(0xB9), uint8_t(0xB9), 0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF), - uint8_t(0xBF), uint8_t(0xB9), uint8_t(0xB9), 0x0, 0x11, uint8_t(0xC3), - uint8_t(0xBF), uint8_t(0xE0), uint8_t(0xB9), uint8_t(0xB9)); - } else { - delta_values = _mm256_setr_epi8( - int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), int8_t(0x04), - int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9), int8_t(0x00), - int8_t(0x10), int8_t(0xC3), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9), - int8_t(0x00), int8_t(0x10), int8_t(0xC3), int8_t(0xBF), int8_t(0xBF), - int8_t(0xB9), int8_t(0xB9)); - } - __m256i check_asso; - - if (base64_url) { - check_asso = - _mm256_setr_epi8(0xD, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, - 0x7, 0xB, 0x6, 0xB, 0x12, 0xD, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x1, 0x1, 0x3, 0x7, 0xB, 0x6, 0xB, 0x12); - } else { - - check_asso = _mm256_setr_epi8( - 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, - 0x0B, 0x0B, 0x0B, 0x0F, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F); - } - __m256i check_values; - if (base64_url) { - check_values = _mm256_setr_epi8( - 0x0, uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), uint8_t(0xCF), - uint8_t(0xBF), uint8_t(0xD3), uint8_t(0xA6), uint8_t(0xB5), - uint8_t(0x86), uint8_t(0xD0), uint8_t(0x80), uint8_t(0xB0), - uint8_t(0x80), 0x0, 0x0, 0x0, uint8_t(0x80), uint8_t(0x80), - uint8_t(0x80), uint8_t(0xCF), uint8_t(0xBF), uint8_t(0xD3), - uint8_t(0xA6), uint8_t(0xB5), uint8_t(0x86), uint8_t(0xD0), - uint8_t(0x80), uint8_t(0xB0), uint8_t(0x80), 0x0, 0x0); - } else { - check_values = _mm256_setr_epi8( - int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0xCF), - int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), int8_t(0xB5), int8_t(0x86), - int8_t(0xD1), int8_t(0x80), int8_t(0xB1), int8_t(0x80), int8_t(0x91), - int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), int8_t(0xB5), - int8_t(0x86), int8_t(0xD1), int8_t(0x80), int8_t(0xB1), int8_t(0x80), - int8_t(0x91), int8_t(0x80)); - } - const __m256i shifted = _mm256_srli_epi32(*src, 3); - const __m256i delta_hash = - _mm256_avg_epu8(_mm256_shuffle_epi8(delta_asso, *src), shifted); - const __m256i check_hash = - _mm256_avg_epu8(_mm256_shuffle_epi8(check_asso, *src), shifted); - const __m256i out = - _mm256_adds_epi8(_mm256_shuffle_epi8(delta_values, delta_hash), *src); - const __m256i chk = - _mm256_adds_epi8(_mm256_shuffle_epi8(check_values, check_hash), *src); - const int mask = _mm256_movemask_epi8(chk); - if (mask) { - __m256i ascii_space = - _mm256_cmpeq_epi8(_mm256_shuffle_epi8(ascii_space_tbl, *src), *src); - *error |= (mask != _mm256_movemask_epi8(ascii_space)); - } - *src = out; - return (uint32_t)mask; -} - -template -static inline uint64_t to_base64_mask(block64 *b, bool *error) { - *error = 0; - uint64_t m0 = to_base64_mask(&b->chunks[0], error); - uint64_t m1 = to_base64_mask(&b->chunks[1], error); - return m0 | (m1 << 32); -} - -static inline void copy_block(block64 *b, char *output) { - _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), b->chunks[0]); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(output + 32), b->chunks[1]); -} - -static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { - uint64_t nmask = ~mask; - compress(b->chunks[0], uint32_t(mask), output); - compress(b->chunks[1], uint32_t(mask >> 32), - output + _mm_popcnt_u64(nmask & 0xFFFFFFFF)); - return _mm_popcnt_u64(nmask); -} - -// The caller of this function is responsible to ensure that there are 64 bytes available -// from reading at src. The data is read into a block64 structure. -static inline void load_block(block64 *b, const char *src) { - b->chunks[0] = _mm256_loadu_si256(reinterpret_cast(src)); - b->chunks[1] = - _mm256_loadu_si256(reinterpret_cast(src + 32)); -} - -// The caller of this function is responsible to ensure that there are 128 bytes available -// from reading at src. The data is read into a block64 structure. -static inline void load_block(block64 *b, const char16_t *src) { - __m256i m1 = _mm256_loadu_si256(reinterpret_cast(src)); - __m256i m2 = _mm256_loadu_si256(reinterpret_cast(src + 16)); - __m256i m3 = _mm256_loadu_si256(reinterpret_cast(src + 32)); - __m256i m4 = _mm256_loadu_si256(reinterpret_cast(src + 48)); - __m256i m1p = _mm256_permute2x128_si256(m1, m2, 0x20); - __m256i m2p = _mm256_permute2x128_si256(m1, m2, 0x31); - __m256i m3p = _mm256_permute2x128_si256(m3, m4, 0x20); - __m256i m4p = _mm256_permute2x128_si256(m3, m4, 0x31); - b->chunks[0] = _mm256_packus_epi16(m1p, m2p); - b->chunks[1] = _mm256_packus_epi16(m3p, m4p); -} - -static inline void base64_decode(char *out, __m256i str) { - // credit: aqrit - const __m256i pack_shuffle = - _mm256_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1, - 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1); - const __m256i t0 = _mm256_maddubs_epi16(str, _mm256_set1_epi32(0x01400140)); - const __m256i t1 = _mm256_madd_epi16(t0, _mm256_set1_epi32(0x00011000)); - const __m256i t2 = _mm256_shuffle_epi8(t1, pack_shuffle); - - // Store the output: - _mm_storeu_si128((__m128i *)out, _mm256_castsi256_si128(t2)); - _mm_storeu_si128((__m128i *)(out + 12), _mm256_extracti128_si256(t2, 1)); -} -// decode 64 bytes and output 48 bytes -static inline void base64_decode_block(char *out, const char *src) { - base64_decode(out, - _mm256_loadu_si256(reinterpret_cast(src))); - base64_decode(out + 24, _mm256_loadu_si256( - reinterpret_cast(src + 32))); -} -static inline void base64_decode_block_safe(char *out, const char *src) { - base64_decode(out, - _mm256_loadu_si256(reinterpret_cast(src))); - char buffer[32]; // We enforce safety with a buffer. - base64_decode( - buffer, _mm256_loadu_si256(reinterpret_cast(src + 32))); - std::memcpy(out + 24, buffer, 24); -} -static inline void base64_decode_block(char *out, block64 *b) { - base64_decode(out, b->chunks[0]); - base64_decode(out + 24, b->chunks[1]); -} -static inline void base64_decode_block_safe(char *out, block64 *b) { - base64_decode(out, b->chunks[0]); - char buffer[32]; // We enforce safety with a buffer. - base64_decode(buffer, b->chunks[1]); - std::memcpy(out + 24, buffer, 24); -} - -template -result compress_decode_base64(char *dst, const chartype *src, size_t srclen, - base64_options options) { - const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - size_t equalsigns = 0; - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 1; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 2; - } - } - char *end_of_safe_64byte_zone = - (srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63 : dst; - - const chartype *const srcinit = src; - const char *const dstinit = dst; - const chartype *const srcend = src + srclen; - - constexpr size_t block_size = 6; - static_assert(block_size >= 2, "block_size must be at least two"); - char buffer[block_size * 64]; - char *bufferptr = buffer; - if (srclen >= 64) { - const chartype *const srcend64 = src + srclen - 64; - while (src <= srcend64) { - block64 b; - load_block(&b, src); - src += 64; - bool error = false; - uint64_t badcharmask = to_base64_mask(&b, &error); - if (error) { - src -= 64; - while (src < srcend && to_base64[uint8_t(*src)] <= 64) { - src++; - } - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - if (badcharmask != 0) { - // optimization opportunity: check for simple masks like those made of - // continuous 1s followed by continuous 0s. And masks containing a - // single bad character. - bufferptr += compress_block(&b, badcharmask, bufferptr); - } else if (bufferptr != buffer) { - copy_block(&b, bufferptr); - bufferptr += 64; - } else { - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, &b); - } else { - base64_decode_block(dst, &b); - } - dst += 48; - } - if (bufferptr >= (block_size - 1) * 64 + buffer) { - for (size_t i = 0; i < (block_size - 2); i++) { - base64_decode_block(dst, buffer + i * 64); - dst += 48; - } - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, buffer + (block_size - 2) * 64); - } else { - base64_decode_block(dst, buffer + (block_size - 2) * 64); - } - dst += 48; - std::memcpy(buffer, buffer + (block_size - 1) * 64, - 64); // 64 might be too much - bufferptr -= (block_size - 1) * 64; - } - } - } - - char *buffer_start = buffer; - // Optimization note: if this is almost full, then it is worth our - // time, otherwise, we should just decode directly. - int last_block = (int)((bufferptr - buffer_start) % 64); - if (last_block != 0 && srcend - src + last_block >= 64) { - - while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - *bufferptr = char(val); - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - bufferptr += (val <= 63); - src++; - } - } - - for (; buffer_start + 64 <= bufferptr; buffer_start += 64) { - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, buffer_start); - } else { - base64_decode_block(dst, buffer_start); - } - dst += 48; - } - if ((bufferptr - buffer_start) % 64 != 0) { - while (buffer_start + 4 < bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 4); - - dst += 3; - buffer_start += 4; - } - if (buffer_start + 4 <= bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - - dst += 3; - buffer_start += 4; - } - // we may have 1, 2 or 3 bytes left and we need to decode them so let us - // bring in src content - int leftover = int(bufferptr - buffer_start); - if (leftover > 0) { - while (leftover < 4 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - buffer_start[leftover] = char(val); - leftover += (val <= 63); - src++; - } - - if (leftover == 1) { - return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)}; - } - if (leftover == 2) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - dst += 1; - } else if (leftover == 3) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6) + - (uint32_t(buffer_start[2]) << 1 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 2); - dst += 2; - } else { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - dst += 3; - } - } - } - if (src < srcend + equalsigns) { - result r = - scalar::base64::base64_tail_decode(dst, src, srcend - src, options); - if (r.error == error_code::INVALID_BASE64_CHARACTER) { - r.count += size_t(src - srcinit); - return r; - } else { - r.count += size_t(dst - dstinit); - } - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - r.error = error_code::INVALID_BASE64_CHARACTER; - } - } - return r; - } - if(equalsigns > 0) { - if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)}; - } - } - return {SUCCESS, size_t(dst - dstinit)}; -} -/* end file src/haswell/avx2_base64.cpp */ - -} // unnamed namespace -} // namespace haswell -} // namespace simdutf - -/* begin file src/generic/buf_block_reader.h */ -namespace simdutf { -namespace haswell { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdutf_really_inline size_t block_index(); - simdutf_really_inline bool has_full_block() const; - simdutf_really_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdutf_really_inline size_t get_remainder(uint8_t *dst) const; - simdutf_really_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text_64(const uint8_t *text) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text(const simd8x64& in) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char * format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdutf_really_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdutf_really_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdutf_really_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdutf_really_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdutf_really_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/buf_block_reader.h */ -/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdutf_really_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1 - }; - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdutf_really_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdutf_really_inline void check_next_input(const simd8x64& input) { - if(simdutf_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - - } - } - - // do not forget to call check_eof! - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -/* begin file src/generic/utf8_validation/utf8_validator.h */ -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_validation { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return !c.errors(); -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -/** - * Validates that the string is actual UTF-8 and stops on errors. - */ -template -result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - if(c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input + count), length - count); - res.count += count; - return res; - } - reader.advance(); - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - if (c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input) + count, length - count); - res.count += count; - return res; - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_utf8_with_errors(const char * input, size_t length) { - return generic_validate_utf8_with_errors(reinterpret_cast(input),length); -} - -template -bool generic_validate_ascii(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - uint8_t blocks[64]{}; - simd::simd8x64 running_or(blocks); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - running_or |= in; - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - running_or |= in; - return running_or.is_ascii(); -} - -bool generic_validate_ascii(const char * input, size_t length) { - return generic_validate_ascii(reinterpret_cast(input),length); -} - -template -result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } - reader.advance(); - - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_ascii_with_errors(const char * input, size_t length) { - return generic_validate_ascii_with_errors(reinterpret_cast(input),length); -} - -} // namespace utf8_validation -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_validator.h */ -// transcoding from UTF-8 to UTF-16 -/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ - - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_to_utf16 { - -using namespace simd; - -template -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char16_t* utf16_output) noexcept { - // The implementation is not specific to haswell and should be moved to the generic directory. - size_t pos = 0; - char16_t* start{utf16_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - // this loop could be unrolled further. For example, we could process the mask - // far more than 64 bytes. - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // Slow path. We hope that the compiler will recognize that this is a slow path. - // Anything that is not a continuation mask is a 'leading byte', that is, the - // start of a new code point. - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - // The *start* of code points is not so useful, rather, we want the *end* of code points. - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times when using solely - // the slow/regular path, and at least four times if there are fast paths. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - // - // Thus we may allow convert_masked_utf8_to_utf16 to process - // more bytes at a time under a fast-path mode where 16 bytes - // are consumed at once (e.g., when encountering ASCII). - size_t consumed = convert_masked_utf8_to_utf16(input + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - utf16_output += scalar::utf8_to_utf16::convert_valid(input + pos, size - pos, utf16_output); - return utf16_output - start; -} - -} // namespace utf8_to_utf16 -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ -/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */ - - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_to_utf16 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - template - simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf16::convert(in + pos, size - pos, utf16_output); - if(howmany == 0) { return 0; } - utf16_output += howmany; - } - return utf16_output - start; - } - - template - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf16_output += res.count; - } - } - return result(error_code::SUCCESS, utf16_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf16 namespace -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */ -// transcoding from UTF-8 to UTF-32 -/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_to_utf32 { - -using namespace simd; - - -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char32_t* utf32_output) noexcept { - size_t pos = 0; - char32_t* start{utf32_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - size_t max_starting_point = (pos + 64) - 12; - while(pos < max_starting_point) { - size_t consumed = convert_masked_utf8_to_utf32(input + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - } - } - utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output); - return utf32_output - start; -} - - -} // namespace utf8_to_utf32 -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ -/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */ - - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_to_utf32 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - - simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output); - if(howmany == 0) { return 0; } - utf32_output += howmany; - } - return utf32_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - if(pos < size) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf32_output += res.count; - } - } - return result(error_code::SUCCESS, utf32_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf32 namespace -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */ -// other functions -/* begin file src/generic/utf8.h */ - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8 { - -using namespace simd; - -simdutf_really_inline size_t count_code_points(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.gt(-65); - count += count_ones(utf8_continuation_mask); - } - return count + scalar::utf8::count_code_points(in + pos, size - pos); -} - -simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - // We count one word for anything that is not a continuation (so - // leading bytes). - count += 64 - count_ones(utf8_continuation_mask); - int64_t utf8_4byte = input.gteq_unsigned(240); - count += count_ones(utf8_4byte); - } - return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos); -} -} // utf8 namespace -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8.h */ -/* begin file src/generic/utf16.h */ -namespace simdutf { -namespace haswell { -namespace { -namespace utf16 { - -template -simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF); - count += count_ones(not_pair) / 2; - } - return count + scalar::utf16::count_code_points(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t ascii_mask = input.lteq(0x7F); - uint64_t twobyte_mask = input.lteq(0x7FF); - uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF); - - size_t ascii_count = count_ones(ascii_mask) / 2; - size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2; - size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2; - size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2; - count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count; - } - return count + scalar::utf16::utf8_length_from_utf16(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) { - return count_code_points(in, size); -} - -simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) { - size_t pos = 0; - - while (pos < size/32*32) { - simd16x32 input(reinterpret_cast(in + pos)); - input.swap_bytes(); - input.store(reinterpret_cast(output)); - pos += 32; - output += 32; - } - - scalar::utf16::change_endianness_utf16(in + pos, size - pos, output); -} - -} // utf16 -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf16.h */ - - -// transcoding from UTF-8 to Latin 1 -/* begin file src/generic/utf8_to_latin1/utf8_to_latin1.h */ - - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_to_latin1 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// For UTF-8 to Latin 1, we can allow any ASCII character, and any continuation byte, -// but the non-ASCII leading bytes must be 0b11000011 or 0b11000010 and nothing else. -// -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - constexpr const uint8_t FORBIDDEN = 0xff; - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - FORBIDDEN, - // 1110____ ________ - FORBIDDEN, - // 1111____ ________ - FORBIDDEN - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - FORBIDDEN, - // ____0101 ________ - FORBIDDEN, - // ____011_ ________ - FORBIDDEN, - FORBIDDEN, - - // ____1___ ________ - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - // ____1101 ________ - FORBIDDEN, - FORBIDDEN, - FORBIDDEN - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - this->error |= check_special_cases(input, prev1); - } - - - simdutf_really_inline size_t convert(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ... - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for. - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output); - if(howmany == 0) { return 0; } - latin1_output += howmany; - } - return latin1_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - latin1_output += res.count; - } - } - return result(error_code::SUCCESS, latin1_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace haswell -} // namespace simdutf -/* end file src/generic/utf8_to_latin1/utf8_to_latin1.h */ -/* begin file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */ - - -namespace simdutf { -namespace haswell { -namespace { -namespace utf8_to_latin1 { -using namespace simd; - - - simdutf_really_inline size_t convert_valid(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ... - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for. - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(pos < size) { - size_t howmany = scalar::utf8_to_latin1::convert_valid(in + pos, size - pos, latin1_output); - latin1_output += howmany; - } - return latin1_output - start; - } - - } -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace haswell - // namespace simdutf -/* end file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */ - -namespace simdutf { -namespace haswell { - -simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - if (length % 2 == 0) { - return avx2_detect_encodings(input, length); - } else { - if (implementation::validate_utf8(input, length)) { - return simdutf::encoding_type::UTF8; - } else { - return simdutf::encoding_type::unspecified; - } - } -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return haswell::utf8_validation::generic_validate_utf8(buf,len); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept { - return haswell::utf8_validation::generic_validate_utf8_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept { - return haswell::utf8_validation::generic_validate_ascii(buf,len); -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept { - return haswell::utf8_validation::generic_validate_ascii_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept { - const char16_t* tail = avx2_validate_utf16(buf, len); - if (tail) { - return scalar::utf16::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept { - const char16_t* tail = avx2_validate_utf16(buf, len); - if (tail) { - return scalar::utf16::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept { - result res = avx2_validate_utf16_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf16::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept { - result res = avx2_validate_utf16_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf16::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept { - const char32_t* tail = avx2_validate_utf32le(buf, len); - if (tail) { - return scalar::utf32::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept { - result res = avx2_validate_utf32le_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf32::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = avx2_convert_latin1_to_utf8(buf, len, utf8_output); - size_t converted_chars = ret.second - utf8_output; - - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - converted_chars += scalar_converted_chars; - } - - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = avx2_convert_latin1_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = avx2_convert_latin1_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = avx2_convert_latin1_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - utf8_to_latin1::validating_transcoder converter; - return converter.convert(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept { - utf8_to_latin1::validating_transcoder converter; - return converter.convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* input, size_t size, - char* latin1_output) const noexcept { - return utf8_to_latin1::convert_valid(input, size, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* input, size_t size, - char16_t* utf16_output) const noexcept { - return utf8_to_utf16::convert_valid(input, size, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* input, size_t size, - char16_t* utf16_output) const noexcept { - return utf8_to_utf16::convert_valid(input, size, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - utf8_to_utf32::validating_transcoder converter; - return converter.convert(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - utf8_to_utf32::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size, - char32_t* utf32_output) const noexcept { - return utf8_to_utf32::convert_valid(input, size, utf32_output); -} - - -simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = haswell::avx2_convert_utf16_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = haswell::avx2_convert_utf16_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = avx2_convert_utf16_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = avx2_convert_utf16_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: implement a custom function - return convert_utf16be_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: implement a custom function - return convert_utf16le_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = haswell::avx2_convert_utf16_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = haswell::avx2_convert_utf16_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf16_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf16_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16le_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16be_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = avx2_convert_utf32_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = avx2_convert_utf32_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = avx2_convert_utf32_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - return convert_utf32_to_latin1(buf,len,latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf32_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = haswell::avx2_convert_utf16_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = haswell::avx2_convert_utf16_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf16_to_utf32_with_errors(buf, len, utf32_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf16_to_utf32_with_errors(buf, len, utf32_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf32_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = avx2_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = avx2_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = haswell::avx2_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16le(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16be(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return convert_utf16le_to_utf32(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return convert_utf16be_to_utf32(buf, len, utf32_output); -} - -void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept { - utf16::change_endianness_utf16(input, length, output); -} - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept { - return count_utf8(buf,len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept { - return scalar::utf16::latin1_length_from_utf16(length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept { - return scalar::utf32::latin1_length_from_utf32(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::utf32_length_from_utf16(input, length); -} - - -simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf16_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept { - return utf8::utf16_length_from_utf8(input, length); -} - - -simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf32_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char *input, size_t len) const noexcept { - const uint8_t *data = reinterpret_cast(input); - size_t answer = len / sizeof(__m256i) * sizeof(__m256i); - size_t i = 0; - __m256i four_64bits = _mm256_setzero_si256(); - while (i + sizeof(__m256i) <= len) { - __m256i runner = _mm256_setzero_si256(); - // We can do up to 255 loops without overflow. - size_t iterations = (len - i) / sizeof(__m256i); - if (iterations > 255) { - iterations = 255; - } - size_t max_i = i + iterations * sizeof(__m256i) - sizeof(__m256i); - for (; i + 4*sizeof(__m256i) <= max_i; i += 4*sizeof(__m256i)) { - __m256i input1 = _mm256_loadu_si256((const __m256i *)(data + i)); - __m256i input2 = _mm256_loadu_si256((const __m256i *)(data + i + sizeof(__m256i))); - __m256i input3 = _mm256_loadu_si256((const __m256i *)(data + i + 2*sizeof(__m256i))); - __m256i input4 = _mm256_loadu_si256((const __m256i *)(data + i + 3*sizeof(__m256i))); - __m256i input12 = _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input1), - _mm256_cmpgt_epi8(_mm256_setzero_si256(), input2)); - __m256i input23 = _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input3), - _mm256_cmpgt_epi8(_mm256_setzero_si256(), input4)); - __m256i input1234 = _mm256_add_epi8(input12, input23); - runner = _mm256_sub_epi8( - runner, input1234); - } - for (; i <= max_i; i += sizeof(__m256i)) { - __m256i input_256_chunk = _mm256_loadu_si256((const __m256i *)(data + i)); - runner = _mm256_sub_epi8( - runner, _mm256_cmpgt_epi8(_mm256_setzero_si256(), input_256_chunk)); - } - four_64bits = _mm256_add_epi64( - four_64bits, _mm256_sad_epu8(runner, _mm256_setzero_si256())); - } - answer += _mm256_extract_epi64(four_64bits, 0) + - _mm256_extract_epi64(four_64bits, 1) + - _mm256_extract_epi64(four_64bits, 2) + - _mm256_extract_epi64(four_64bits, 3); - return answer + scalar::latin1::utf8_length_from_latin1(reinterpret_cast(data + i), len - i); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const __m256i v_00000000 = _mm256_setzero_si256(); - const __m256i v_ffffff80 = _mm256_set1_epi32((uint32_t)0xffffff80); - const __m256i v_fffff800 = _mm256_set1_epi32((uint32_t)0xfffff800); - const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000); - size_t pos = 0; - size_t count = 0; - for(;pos + 8 <= length; pos += 8) { - __m256i in = _mm256_loadu_si256((__m256i*)(input + pos)); - const __m256i ascii_bytes_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffffff80), v_00000000); - const __m256i one_two_bytes_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_fffff800), v_00000000); - const __m256i two_bytes_bytemask = _mm256_xor_si256(one_two_bytes_bytemask, ascii_bytes_bytemask); - const __m256i one_two_three_bytes_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000); - const __m256i three_bytes_bytemask = _mm256_xor_si256(one_two_three_bytes_bytemask, one_two_bytes_bytemask); - const uint32_t ascii_bytes_bitmask = static_cast(_mm256_movemask_epi8(ascii_bytes_bytemask)); - const uint32_t two_bytes_bitmask = static_cast(_mm256_movemask_epi8(two_bytes_bytemask)); - const uint32_t three_bytes_bitmask = static_cast(_mm256_movemask_epi8(three_bytes_bytemask)); - - size_t ascii_count = count_ones(ascii_bytes_bitmask) / 4; - size_t two_bytes_count = count_ones(two_bytes_bitmask) / 4; - size_t three_bytes_count = count_ones(three_bytes_bitmask) / 4; - count += 32 - 3*ascii_count - 2*two_bytes_count - three_bytes_count; - } - return count + scalar::utf32::utf8_length_from_utf32(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const __m256i v_00000000 = _mm256_setzero_si256(); - const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000); - size_t pos = 0; - size_t count = 0; - for(;pos + 8 <= length; pos += 8) { - __m256i in = _mm256_loadu_si256((__m256i*)(input + pos)); - const __m256i surrogate_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000); - const uint32_t surrogate_bitmask = static_cast(_mm256_movemask_epi8(surrogate_bytemask)); - size_t surrogate_count = (32-count_ones(surrogate_bitmask))/4; - count += 8 + surrogate_count; - } - return count + scalar::utf32::utf16_length_from_utf32(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - if(options & base64_url) { - return encode_base64(output, input, length); - } else { - return encode_base64(output, input, length); - } -} -} // namespace haswell -} // namespace simdutf - -/* begin file src/simdutf/haswell/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - - -#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593 -SIMDUTF_POP_DISABLE_WARNINGS -#endif // end of workaround -/* end file src/simdutf/haswell/end.h */ -/* end file src/haswell/implementation.cpp */ -#endif -#if SIMDUTF_IMPLEMENTATION_PPC64 -/* begin file src/ppc64/implementation.cpp */ - - - - - -/* begin file src/simdutf/ppc64/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "ppc64" -// #define SIMDUTF_IMPLEMENTATION ppc64 -/* end file src/simdutf/ppc64/begin.h */ -namespace simdutf { -namespace ppc64 { -namespace { -#ifndef SIMDUTF_PPC64_H -#error "ppc64.h must be included" -#endif -using namespace simd; - - -simdutf_really_inline bool is_ascii(const simd8x64& input) { - // careful: 0x80 is not ascii. - return input.reduce_or().saturating_sub(0b01111111u).bits_not_set_anywhere(); -} - -simdutf_unused simdutf_really_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0b11000000u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0b11100000u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0b11110000u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdutf_really_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_third_byte | is_fourth_byte); -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf - -/* begin file src/generic/buf_block_reader.h */ -namespace simdutf { -namespace ppc64 { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdutf_really_inline size_t block_index(); - simdutf_really_inline bool has_full_block() const; - simdutf_really_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdutf_really_inline size_t get_remainder(uint8_t *dst) const; - simdutf_really_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text_64(const uint8_t *text) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text(const simd8x64& in) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char * format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdutf_really_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdutf_really_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdutf_really_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdutf_really_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdutf_really_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/buf_block_reader.h */ -/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdutf_really_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1 - }; - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdutf_really_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdutf_really_inline void check_next_input(const simd8x64& input) { - if(simdutf_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - - } - } - - // do not forget to call check_eof! - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -/* begin file src/generic/utf8_validation/utf8_validator.h */ -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8_validation { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return !c.errors(); -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -/** - * Validates that the string is actual UTF-8 and stops on errors. - */ -template -result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - if(c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input + count), length - count); - res.count += count; - return res; - } - reader.advance(); - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - if (c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input) + count, length - count); - res.count += count; - return res; - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_utf8_with_errors(const char * input, size_t length) { - return generic_validate_utf8_with_errors(reinterpret_cast(input),length); -} - -template -bool generic_validate_ascii(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - uint8_t blocks[64]{}; - simd::simd8x64 running_or(blocks); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - running_or |= in; - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - running_or |= in; - return running_or.is_ascii(); -} - -bool generic_validate_ascii(const char * input, size_t length) { - return generic_validate_ascii(reinterpret_cast(input),length); -} - -template -result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } - reader.advance(); - - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_ascii_with_errors(const char * input, size_t length) { - return generic_validate_ascii_with_errors(reinterpret_cast(input),length); -} - -} // namespace utf8_validation -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_validator.h */ -// transcoding from UTF-8 to UTF-16 -/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ - - -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8_to_utf16 { - -using namespace simd; - -template -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char16_t* utf16_output) noexcept { - // The implementation is not specific to haswell and should be moved to the generic directory. - size_t pos = 0; - char16_t* start{utf16_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - // this loop could be unrolled further. For example, we could process the mask - // far more than 64 bytes. - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // Slow path. We hope that the compiler will recognize that this is a slow path. - // Anything that is not a continuation mask is a 'leading byte', that is, the - // start of a new code point. - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - // The *start* of code points is not so useful, rather, we want the *end* of code points. - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times when using solely - // the slow/regular path, and at least four times if there are fast paths. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - // - // Thus we may allow convert_masked_utf8_to_utf16 to process - // more bytes at a time under a fast-path mode where 16 bytes - // are consumed at once (e.g., when encountering ASCII). - size_t consumed = convert_masked_utf8_to_utf16(input + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - utf16_output += scalar::utf8_to_utf16::convert_valid(input + pos, size - pos, utf16_output); - return utf16_output - start; -} - -} // namespace utf8_to_utf16 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ -/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */ - - -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8_to_utf16 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - template - simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf16::convert(in + pos, size - pos, utf16_output); - if(howmany == 0) { return 0; } - utf16_output += howmany; - } - return utf16_output - start; - } - - template - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf16_output += res.count; - } - } - return result(error_code::SUCCESS, utf16_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf16 namespace -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */ -// transcoding from UTF-8 to UTF-32 -/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ - -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8_to_utf32 { - -using namespace simd; - - -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char32_t* utf32_output) noexcept { - size_t pos = 0; - char32_t* start{utf32_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - size_t max_starting_point = (pos + 64) - 12; - while(pos < max_starting_point) { - size_t consumed = convert_masked_utf8_to_utf32(input + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - } - } - utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output); - return utf32_output - start; -} - - -} // namespace utf8_to_utf32 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ -/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */ - - -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8_to_utf32 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - - simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output); - if(howmany == 0) { return 0; } - utf32_output += howmany; - } - return utf32_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - if(pos < size) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf32_output += res.count; - } - } - return result(error_code::SUCCESS, utf32_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf32 namespace -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */ -// other functions -/* begin file src/generic/utf8.h */ - -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf8 { - -using namespace simd; - -simdutf_really_inline size_t count_code_points(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.gt(-65); - count += count_ones(utf8_continuation_mask); - } - return count + scalar::utf8::count_code_points(in + pos, size - pos); -} - -simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - // We count one word for anything that is not a continuation (so - // leading bytes). - count += 64 - count_ones(utf8_continuation_mask); - int64_t utf8_4byte = input.gteq_unsigned(240); - count += count_ones(utf8_4byte); - } - return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos); -} -} // utf8 namespace -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf8.h */ -/* begin file src/generic/utf16.h */ -namespace simdutf { -namespace ppc64 { -namespace { -namespace utf16 { - -template -simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF); - count += count_ones(not_pair) / 2; - } - return count + scalar::utf16::count_code_points(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t ascii_mask = input.lteq(0x7F); - uint64_t twobyte_mask = input.lteq(0x7FF); - uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF); - - size_t ascii_count = count_ones(ascii_mask) / 2; - size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2; - size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2; - size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2; - count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count; - } - return count + scalar::utf16::utf8_length_from_utf16(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) { - return count_code_points(in, size); -} - -simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) { - size_t pos = 0; - - while (pos < size/32*32) { - simd16x32 input(reinterpret_cast(in + pos)); - input.swap_bytes(); - input.store(reinterpret_cast(output)); - pos += 32; - output += 32; - } - - scalar::utf16::change_endianness_utf16(in + pos, size - pos, output); -} - -} // utf16 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdutf -/* end file src/generic/utf16.h */ - -// -// Implementation-specific overrides -// -namespace simdutf { -namespace ppc64 { - -simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - int out = 0; - if(validate_utf8(input, length)) { out |= encoding_type::UTF8; } - if((length % 2) == 0) { - if(validate_utf16(reinterpret_cast(input), length/2)) { out |= encoding_type::UTF16_LE; } - } - if((length % 4) == 0) { - if(validate_utf32(reinterpret_cast(input), length/4)) { out |= encoding_type::UTF32_LE; } - } - - return out; -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return ppc64::utf8_validation::generic_validate_utf8(buf,len); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept { - return ppc64::utf8_validation::generic_validate_utf8_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept { - return ppc64::utf8_validation::generic_validate_ascii(buf,len); -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept { - return ppc64::utf8_validation::generic_validate_ascii_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate(buf, len); -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate_with_errors(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept { - return scalar::utf16::validate_with_errors(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept { - return scalar::utf32::validate_with_errors(buf, len); -} - -simdutf_warn_unused bool implementation::validate_utf32(const char16_t *buf, size_t len) const noexcept { - return scalar::utf32::validate(buf, len); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept { - return 0; // stub -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept { - return 0; // stub -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept { - return result(error_code::OTHER, 0); // stub -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept { - return result(error_code::OTHER, 0); // stub -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept { - return 0; // stub -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept { - return 0; // stub -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* /*buf*/, size_t /*len*/, char32_t* /*utf16_output*/) const noexcept { - return 0; // stub -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* /*buf*/, size_t /*len*/, char32_t* /*utf16_output*/) const noexcept { - return result(error_code::OTHER, 0); // stub -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* /*buf*/, size_t /*len*/, char32_t* /*utf16_output*/) const noexcept { - return 0; // stub -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert(buf, len, utf8_output); -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_with_errors(buf, len, utf8_output); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_with_errors(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_valid(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf16_to_utf8::convert_valid(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf32_to_utf8::convert(buf, len, utf8_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf32_to_utf8::convert_with_errors(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return scalar::utf32_to_utf8::convert_valid(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_valid(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return scalar::utf32_to_utf16::convert_valid(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_valid(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return scalar::utf16_to_utf32::convert_valid(buf, len, utf32_output); -} - -void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept { - scalar::utf16::change_endianness_utf16(input, length, output); -} - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return scalar::utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept { - return scalar::utf8::utf16_length_from_utf8(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept { - return scalar::utf32::utf8_length_from_utf32(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept { - return scalar::utf32::utf16_length_from_utf32(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept { - return scalar::utf8::count_code_points(input, length); -} - - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - // skip trailing spaces - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - size_t equallocation = length; // location of the first padding character if any - size_t equalsigns = 0; - if(length > 0 && input[length - 1] == '=') { - length -= 1; - equalsigns++; - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - if(length > 0 && input[length - 1] == '=') { - equalsigns++; - length -= 1; - } - } - if(length == 0) { - if(equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation};; - } - return {SUCCESS, 0}; - } - result r = scalar::base64::base64_tail_decode(output, input, length, options); - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - } - return r; -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - // skip trailing spaces - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - size_t equallocation = length; // location of the first padding character if any - size_t equalsigns = 0; - if(length > 0 && input[length - 1] == '=') { - length -= 1; - equalsigns++; - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - if(length > 0 && input[length - 1] == '=') { - equalsigns++; - length -= 1; - } - } - if(length == 0) { - if(equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - return {SUCCESS, 0}; - } - result r = scalar::base64::base64_tail_decode(output, input, length, options); - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - } - return r; -} - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - return scalar::base64::binary_to_base64(input, length, output, options); -} -} // namespace ppc64 -} // namespace simdutf - -/* begin file src/simdutf/ppc64/end.h */ -/* end file src/simdutf/ppc64/end.h */ -/* end file src/ppc64/implementation.cpp */ -#endif -#if SIMDUTF_IMPLEMENTATION_RVV -/* begin file src/rvv/implementation.cpp */ - - - - - -/* begin file src/simdutf/rvv/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "rvv" -// #define SIMDUTF_IMPLEMENTATION rvv - -#if SIMDUTF_CAN_ALWAYS_RUN_RVV -// nothing needed. -#else -SIMDUTF_TARGET_RVV -#endif -/* end file src/simdutf/rvv/begin.h */ -namespace simdutf { -namespace rvv { -namespace { -#ifndef SIMDUTF_RVV_H -#error "rvv.h must be included" -#endif - -} // unnamed namespace -} // namespace rvv -} // namespace simdutf - -// -// Implementation-specific overrides -// -namespace simdutf { -namespace rvv { - -/* begin file src/rvv/rvv_length_from.inl.cpp */ - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t *src, size_t len) const noexcept { - return utf32_length_from_utf16le(src, len); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t *src, size_t len) const noexcept { - return utf32_length_from_utf16be(src, len); -} - -simdutf_warn_unused size_t implementation::count_utf8(const char *src, size_t len) const noexcept { - return utf32_length_from_utf8(src, len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char *src, size_t len) const noexcept { - return utf32_length_from_utf8(src, len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t len) const noexcept { - return len; -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t len) const noexcept { - return len; -} - -simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t len) const noexcept { - return len; -} - -simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t len) const noexcept { - return len; -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char *src, size_t len) const noexcept { - size_t count = 0; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e8m8(len); - vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl); - vbool1_t mask = __riscv_vmsgt_vx_i8m8_b1(v, -65, vl); - count += __riscv_vcpop_m_b1(mask, vl); - } - return count; -} - -template -simdutf_really_inline static size_t rvv_utf32_length_from_utf16(const char16_t *src, size_t len) { - size_t count = 0; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl); - v = simdutf_byteflip(v, vl); - vbool2_t notHigh = __riscv_vmor_mm_b2( - __riscv_vmsgtu_vx_u16m8_b2(v, 0xDFFF, vl), - __riscv_vmsltu_vx_u16m8_b2(v, 0xDC00, vl), vl); - count += __riscv_vcpop_m_b2(notHigh, vl); - } - return count; -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t *src, size_t len) const noexcept { - return rvv_utf32_length_from_utf16(src, len); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t *src, size_t len) const noexcept { - if (supports_zvbb()) - return rvv_utf32_length_from_utf16(src, len); - else - return rvv_utf32_length_from_utf16(src, len); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char *src, size_t len) const noexcept { - size_t count = len; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e8m8(len); - vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl); - count += __riscv_vcpop_m_b1(__riscv_vmslt_vx_i8m8_b1(v, 0, vl), vl); - } - return count; -} - -template -simdutf_really_inline static size_t rvv_utf8_length_from_utf16(const char16_t *src, size_t len) { - size_t count = 0; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl); - v = simdutf_byteflip(v, vl); - vbool2_t m234 = __riscv_vmsgtu_vx_u16m8_b2(v, 0x7F, vl); - vbool2_t m34 = __riscv_vmsgtu_vx_u16m8_b2(v, 0x7FF, vl); - vbool2_t notSur = __riscv_vmor_mm_b2( - __riscv_vmsltu_vx_u16m8_b2(v, 0xD800, vl), - __riscv_vmsgtu_vx_u16m8_b2(v, 0xDFFF, vl), vl); - vbool2_t m3 = __riscv_vmand_mm_b2(m34, notSur, vl); - count += vl + __riscv_vcpop_m_b2(m234, vl) + __riscv_vcpop_m_b2(m3, vl); - } - return count; -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t *src, size_t len) const noexcept { - return rvv_utf8_length_from_utf16(src, len); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t *src, size_t len) const noexcept { - if (supports_zvbb()) - return rvv_utf8_length_from_utf16(src, len); - else - return rvv_utf8_length_from_utf16(src, len); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t *src, size_t len) const noexcept { - size_t count = 0; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e32m8(len); - vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl); - vbool4_t m234 = __riscv_vmsgtu_vx_u32m8_b4(v, 0x7F, vl); - vbool4_t m34 = __riscv_vmsgtu_vx_u32m8_b4(v, 0x7FF, vl); - vbool4_t m4 = __riscv_vmsgtu_vx_u32m8_b4(v, 0xFFFF, vl); - count += vl + __riscv_vcpop_m_b4(m234, vl) + __riscv_vcpop_m_b4(m34, vl) + __riscv_vcpop_m_b4(m4, vl); - } - return count; -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char *src, size_t len) const noexcept { - size_t count = 0; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e8m8(len); - vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl); - vbool1_t m1234 = __riscv_vmsgt_vx_i8m8_b1(v, -65, vl); - vbool1_t m4 = __riscv_vmsgtu_vx_u8m8_b1( - __riscv_vreinterpret_u8m8(v), (uint8_t)0b11101111, vl); - count += __riscv_vcpop_m_b1(m1234, vl) + __riscv_vcpop_m_b1(m4, vl); - } - return count; -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t *src, size_t len) const noexcept { - size_t count = 0; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e32m8(len); - vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl); - vbool4_t m4 = __riscv_vmsgtu_vx_u32m8_b4(v, 0xFFFF, vl); - count += vl + __riscv_vcpop_m_b4(m4, vl); - } - return count; -} - -/* end file src/rvv/rvv_length_from.inl.cpp */ -/* begin file src/rvv/rvv_validate.inl.cpp */ - - -simdutf_warn_unused bool implementation::validate_ascii(const char *src, size_t len) const noexcept { - size_t vlmax = __riscv_vsetvlmax_e8m8(); - vint8m8_t mask = __riscv_vmv_v_x_i8m8(0, vlmax); - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e8m8(len); - vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl); - mask = __riscv_vor_vv_i8m8_tu(mask, mask, v, vl); - } - return __riscv_vfirst_m_b1(__riscv_vmslt_vx_i8m8_b1(mask, 0, vlmax), vlmax) < 0; -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *src, size_t len) const noexcept { - const char *beg = src; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e8m8(len); - vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl); - long idx = __riscv_vfirst_m_b1(__riscv_vmslt_vx_i8m8_b1(v, 0, vl), vl); - if (idx >= 0) return result(error_code::TOO_LARGE, src - beg + idx); - } - return result(error_code::SUCCESS, src - beg); -} - -/* Returns a close estimation of the number of valid UTF-8 bytes up to the - * first invalid one, but never overestimating. */ -simdutf_really_inline static size_t rvv_count_valid_utf8(const char *src, size_t len) { - const char *beg = src; - if (len < 32) return 0; - - /* validate first three bytes */ - { - size_t idx = 3; - while (idx < len && (src[idx] >> 6) == 0b10) - ++idx; - if (idx > 3+3 || !scalar::utf8::validate(src, idx)) - return 0; - } - - static const uint64_t err1m[] = { 0x0202020202020202, 0x4915012180808080 }; - static const uint64_t err2m[] = { 0xCBCBCB8B8383A3E7, 0xCBCBDBCBCBCBCBCB }; - static const uint64_t err3m[] = { 0x0101010101010101, 0X01010101BABAAEE6 }; - - const vuint8m1_t err1tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err1m, 2)); - const vuint8m1_t err2tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err2m, 2)); - const vuint8m1_t err3tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err3m, 2)); - - size_t tail = 3; - size_t n = len - tail; - - for (size_t vl; n > 0; n -= vl, src += vl) { - vl = __riscv_vsetvl_e8m4(n); - vuint8m4_t v0 = __riscv_vle8_v_u8m4((uint8_t const*)src, vl); - - uint8_t next0 = src[vl+0]; - uint8_t next1 = src[vl+1]; - uint8_t next2 = src[vl+2]; - - /* fast path: ASCII */ - if (__riscv_vfirst_m_b2(__riscv_vmsgtu_vx_u8m4_b2(v0, 0b01111111, vl), vl) < 0 && (next0|next1|next2) < 0b10000000) - continue; - - /* see "Validating UTF-8 In Less Than One Instruction Per Byte" - * https://arxiv.org/abs/2010.03090 */ - vuint8m4_t v1 = __riscv_vslide1down_vx_u8m4(v0, next0, vl); - vuint8m4_t v2 = __riscv_vslide1down_vx_u8m4(v1, next1, vl); - vuint8m4_t v3 = __riscv_vslide1down_vx_u8m4(v2, next2, vl); - - vuint8m4_t s1 = __riscv_vreinterpret_v_u16m4_u8m4(__riscv_vsrl_vx_u16m4(__riscv_vreinterpret_v_u8m4_u16m4(v2), 4, __riscv_vsetvlmax_e16m4())); - vuint8m4_t s3 = __riscv_vreinterpret_v_u16m4_u8m4(__riscv_vsrl_vx_u16m4(__riscv_vreinterpret_v_u8m4_u16m4(v3), 4, __riscv_vsetvlmax_e16m4())); - - vuint8m4_t idx2 = __riscv_vand_vx_u8m4(v2, 0xF, vl); - vuint8m4_t idx1 = __riscv_vand_vx_u8m4(s1, 0xF, vl); - vuint8m4_t idx3 = __riscv_vand_vx_u8m4(s3, 0xF, vl); - - vuint8m4_t err1 = simdutf_vrgather_u8m1x4(err1tbl, idx1); - vuint8m4_t err2 = simdutf_vrgather_u8m1x4(err2tbl, idx2); - vuint8m4_t err3 = simdutf_vrgather_u8m1x4(err3tbl, idx3); - vint8m4_t errs = __riscv_vreinterpret_v_u8m4_i8m4(__riscv_vand_vv_u8m4(__riscv_vand_vv_u8m4(err1, err2, vl), err3, vl)); - - vbool2_t is_3 = __riscv_vmsgtu_vx_u8m4_b2(v1, 0b11100000-1, vl); - vbool2_t is_4 = __riscv_vmsgtu_vx_u8m4_b2(v0, 0b11110000-1, vl); - vbool2_t is_34 = __riscv_vmor_mm_b2(is_3, is_4, vl); - vbool2_t err34 = __riscv_vmxor_mm_b2(is_34, __riscv_vmslt_vx_i8m4_b2(errs, 0, vl), vl); - vbool2_t errm = __riscv_vmor_mm_b2(__riscv_vmsgt_vx_i8m4_b2(errs, 0, vl), err34, vl); - if (__riscv_vfirst_m_b2(errm , vl) >= 0) - break; - } - - /* we need to validate the last character */ - while (tail < len && (src[0] >> 6) == 0b10) --src, ++tail; - return src - beg; -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *src, size_t len) const noexcept { - size_t count = rvv_count_valid_utf8(src, len); - return scalar::utf8::validate(src + count, len - count); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *src, size_t len) const noexcept { - size_t count = rvv_count_valid_utf8(src, len); - result res = scalar::utf8::validate_with_errors(src + count, len - count); - return result(res.error, count + res.count); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *src, size_t len) const noexcept { - return validate_utf16le_with_errors(src, len).error == error_code::SUCCESS; -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *src, size_t len) const noexcept { - return validate_utf16be_with_errors(src, len).error == error_code::SUCCESS; -} - -template -simdutf_really_inline static result rvv_validate_utf16_with_errors(const char16_t *src, size_t len) { - const char16_t *beg = src; - uint16_t last = 0; - for (size_t vl; len > 0; len -= vl, src += vl, last = simdutf_byteflip(src[-1])) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v1 = __riscv_vle16_v_u16m8((const uint16_t*)src, vl); - v1 = simdutf_byteflip(v1, vl); - vuint16m8_t v0 = __riscv_vslide1up_vx_u16m8(v1, last, vl); - - vbool2_t surhi = __riscv_vmseq_vx_u16m8_b2(__riscv_vand_vx_u16m8(v0, 0xFC00, vl), 0xD800, vl); - vbool2_t surlo = __riscv_vmseq_vx_u16m8_b2(__riscv_vand_vx_u16m8(v1, 0xFC00, vl), 0xDC00, vl); - - long idx = __riscv_vfirst_m_b2(__riscv_vmxor_mm_b2(surhi, surlo, vl), vl); - if (idx >= 0) { - last = idx > 0 ? simdutf_byteflip(src[idx-1]) : last; - return result(error_code::SURROGATE, src - beg + idx - (last - 0xD800u < 0x400u)); - break; - } - } - if (last - 0xD800u < 0x400u) - return result(error_code::SURROGATE, src - beg - 1); /* end on high surrogate */ - else - return result(error_code::SUCCESS, src - beg); -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *src, size_t len) const noexcept { - return rvv_validate_utf16_with_errors(src, len); -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *src, size_t len) const noexcept { - if (supports_zvbb()) - return rvv_validate_utf16_with_errors(src, len); - else - return rvv_validate_utf16_with_errors(src, len); -} - -simdutf_warn_unused bool implementation::validate_utf32(const char32_t *src, size_t len) const noexcept { - size_t vlmax = __riscv_vsetvlmax_e32m8(); - vuint32m8_t max = __riscv_vmv_v_x_u32m8(0x10FFFF, vlmax); - vuint32m8_t maxOff = __riscv_vmv_v_x_u32m8(0xFFFFF7FF, vlmax); - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e32m8(len); - vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl); - vuint32m8_t off = __riscv_vadd_vx_u32m8(v, 0xFFFF2000, vl); - max = __riscv_vmaxu_vv_u32m8_tu(max, max, v, vl); - maxOff = __riscv_vmaxu_vv_u32m8_tu(maxOff, maxOff, off, vl); - } - return __riscv_vfirst_m_b4(__riscv_vmor_mm_b4( - __riscv_vmsne_vx_u32m8_b4(max, 0x10FFFF, vlmax), - __riscv_vmsne_vx_u32m8_b4(maxOff, 0xFFFFF7FF, vlmax), vlmax), vlmax) < 0; -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *src, size_t len) const noexcept { - const char32_t *beg = src; - for (size_t vl; len > 0; len -= vl, src += vl) { - vl = __riscv_vsetvl_e32m8(len); - vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl); - vuint32m8_t off = __riscv_vadd_vx_u32m8(v, 0xFFFF2000, vl); - long idx; - idx = __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(v, 0x10FFFF, vl), vl); - if (idx >= 0) return result(error_code::TOO_LARGE, src - beg + idx); - idx = __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(off, 0xFFFFF7FF, vl), vl); - if (idx >= 0) return result(error_code::SURROGATE, src - beg + idx); - } - return result(error_code::SUCCESS, src - beg); -} - -/* end file src/rvv/rvv_validate.inl.cpp */ - -/* begin file src/rvv/rvv_latin1_to.inl.cpp */ - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char *src, size_t len, char *dst) const noexcept { - char *beg = dst; - for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut) { - vl = __riscv_vsetvl_e8m2(len); - vuint8m2_t v1 = __riscv_vle8_v_u8m2((uint8_t*)src, vl); - vbool4_t nascii = __riscv_vmslt_vx_i8m2_b4(__riscv_vreinterpret_v_u8m2_i8m2(v1), 0, vl); - size_t cnt = __riscv_vcpop_m_b4(nascii, vl); - vlOut = vl + cnt; - if (cnt == 0) { - __riscv_vse8_v_u8m2((uint8_t*)dst, v1, vlOut); - continue; - } - - vuint8m2_t v0 = __riscv_vor_vx_u8m2(__riscv_vsrl_vx_u8m2(v1, 6, vl), 0b11000000, vl); - v1 = __riscv_vand_vx_u8m2_mu(nascii, v1, v1, 0b10111111, vl); - - vuint8m4_t wide = __riscv_vreinterpret_v_u16m4_u8m4(__riscv_vwmaccu_vx_u16m4(__riscv_vwaddu_vv_u16m4(v0, v1, vl), 0xFF, v1, vl)); - vbool2_t mask = __riscv_vmsgtu_vx_u8m4_b2(__riscv_vsub_vx_u8m4(wide, 0b11000000, vl*2), 1, vl*2); - vuint8m4_t comp = __riscv_vcompress_vm_u8m4(wide, mask, vl*2); - - __riscv_vse8_v_u8m4((uint8_t*)dst, comp, vlOut); - } - return dst - beg; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char *src, size_t len, char16_t *dst) const noexcept { - char16_t *beg = dst; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e8m4(len); - vuint8m4_t v = __riscv_vle8_v_u8m4((uint8_t*)src, vl); - __riscv_vse16_v_u16m8((uint16_t*)dst, __riscv_vzext_vf2_u16m8(v, vl), vl); - } - return dst - beg; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char *src, size_t len, char16_t *dst) const noexcept { - char16_t *beg = dst; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e8m4(len); - vuint8m4_t v = __riscv_vle8_v_u8m4((uint8_t*)src, vl); - __riscv_vse16_v_u16m8((uint16_t*)dst, __riscv_vsll_vx_u16m8(__riscv_vzext_vf2_u16m8(v, vl), 8, vl), vl); - } - return dst - beg; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char *src, size_t len, char32_t *dst) const noexcept { - char32_t *beg = dst; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e8m2(len); - vuint8m2_t v = __riscv_vle8_v_u8m2((uint8_t*)src, vl); - __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf4_u32m8(v, vl), vl); - } - return dst - beg; -} - -/* end file src/rvv/rvv_latin1_to.inl.cpp */ -/* begin file src/rvv/rvv_utf8_to.inl.cpp */ -template -simdutf_really_inline static size_t rvv_utf32_store_utf16_m4(uint16_t *dst, vuint32m4_t utf32, size_t vl, vbool4_t m4even) { - /* convert [000000000000aaaa|aaaaaabbbbbbbbbb] - * to [110111bbbbbbbbbb|110110aaaaaaaaaa] */ - vuint32m4_t sur = __riscv_vsub_vx_u32m4(utf32, 0x10000, vl); - sur = __riscv_vor_vv_u32m4(__riscv_vsll_vx_u32m4(sur, 16, vl), - __riscv_vsrl_vx_u32m4(sur, 10, vl), vl); - sur = __riscv_vand_vx_u32m4(sur, 0x3FF03FF, vl); - sur = __riscv_vor_vx_u32m4(sur, 0xDC00D800, vl); - /* merge 1 byte utf32 and 2 byte sur */ - vbool8_t m4 = __riscv_vmsgtu_vx_u32m4_b8(utf32, 0xFFFF, vl); - vuint16m4_t utf32_16 = __riscv_vreinterpret_v_u32m4_u16m4(__riscv_vmerge_vvm_u32m4(utf32, sur, m4, vl)); - /* compress and store */ - vbool4_t mOut = __riscv_vmor_mm_b4(__riscv_vmsne_vx_u16m4_b4(utf32_16, 0, vl*2), m4even, vl*2); - vuint16m4_t vout = __riscv_vcompress_vm_u16m4(utf32_16, mOut, vl*2); - vl = __riscv_vcpop_m_b4(mOut, vl*2); - __riscv_vse16_v_u16m4(dst, simdutf_byteflip(vout, vl), vl); - return vl; -}; - -template -simdutf_really_inline static size_t rvv_utf8_to_common(char const *src, size_t len, Tdst *dst) { - static_assert(std::is_same() || std::is_same(), "invalid type"); - constexpr bool is16 = std::is_same(); - constexpr endianness endian = bflip == simdutf_ByteFlip::NONE ? endianness::LITTLE : endianness::BIG; - const auto scalar = [](char const *in, size_t count, Tdst *out) { - return is16 ? scalar::utf8_to_utf16::convert(in, count, (char16_t*)out) - : scalar::utf8_to_utf32::convert(in, count, (char32_t*)out); - }; - - if (len < 32) return scalar(src, len, dst); - - /* validate first three bytes */ - if (validate) { - size_t idx = 3; - while (idx < len && (src[idx] >> 6) == 0b10) - ++idx; - if (idx > 3+3 || !scalar::utf8::validate(src, idx)) - return 0; - } - - size_t tail = 3; - size_t n = len - tail; - Tdst *beg = dst; - - static const uint64_t err1m[] = { 0x0202020202020202, 0x4915012180808080 }; - static const uint64_t err2m[] = { 0xCBCBCB8B8383A3E7, 0xCBCBDBCBCBCBCBCB }; - static const uint64_t err3m[] = { 0x0101010101010101, 0X01010101BABAAEE6 }; - - const vuint8m1_t err1tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err1m, 2)); - const vuint8m1_t err2tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err2m, 2)); - const vuint8m1_t err3tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err3m, 2)); - - size_t vl8m2 = __riscv_vsetvlmax_e8m2(); - vbool4_t m4even = __riscv_vmseq_vx_u8m2_b4(__riscv_vand_vx_u8m2(__riscv_vid_v_u8m2(vl8m2), 1, vl8m2), 0, vl8m2); - - for (size_t vl, vlOut; n > 0; n -= vl, src += vl, dst += vlOut) { - vl = __riscv_vsetvl_e8m2(n); - - vuint8m2_t v0 = __riscv_vle8_v_u8m2((uint8_t const*)src, vl); - uint64_t max = __riscv_vmv_x_s_u8m1_u8(__riscv_vredmaxu_vs_u8m2_u8m1(v0, __riscv_vmv_s_x_u8m1(0, vl), vl)); - - uint8_t next0 = src[vl+0]; - uint8_t next1 = src[vl+1]; - uint8_t next2 = src[vl+2]; - - /* fast path: ASCII */ - if ((max|next0|next1|next2) < 0b10000000) { - vlOut = vl; - if (is16) __riscv_vse16_v_u16m4((uint16_t*)dst, simdutf_byteflip(__riscv_vzext_vf2_u16m4(v0, vlOut), vlOut), vlOut); - else __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf4_u32m8(v0, vlOut), vlOut); - continue; - } - - /* see "Validating UTF-8 In Less Than One Instruction Per Byte" - * https://arxiv.org/abs/2010.03090 */ - vuint8m2_t v1 = __riscv_vslide1down_vx_u8m2(v0, next0, vl); - vuint8m2_t v2 = __riscv_vslide1down_vx_u8m2(v1, next1, vl); - vuint8m2_t v3 = __riscv_vslide1down_vx_u8m2(v2, next2, vl); - - if (validate) { - vuint8m2_t s1 = __riscv_vreinterpret_v_u16m2_u8m2(__riscv_vsrl_vx_u16m2(__riscv_vreinterpret_v_u8m2_u16m2(v2), 4, __riscv_vsetvlmax_e16m2())); - vuint8m2_t s3 = __riscv_vreinterpret_v_u16m2_u8m2(__riscv_vsrl_vx_u16m2(__riscv_vreinterpret_v_u8m2_u16m2(v3), 4, __riscv_vsetvlmax_e16m2())); - - vuint8m2_t idx2 = __riscv_vand_vx_u8m2(v2, 0xF, vl); - vuint8m2_t idx1 = __riscv_vand_vx_u8m2(s1, 0xF, vl); - vuint8m2_t idx3 = __riscv_vand_vx_u8m2(s3, 0xF, vl); - - vuint8m2_t err1 = simdutf_vrgather_u8m1x2(err1tbl, idx1); - vuint8m2_t err2 = simdutf_vrgather_u8m1x2(err2tbl, idx2); - vuint8m2_t err3 = simdutf_vrgather_u8m1x2(err3tbl, idx3); - vint8m2_t errs = __riscv_vreinterpret_v_u8m2_i8m2(__riscv_vand_vv_u8m2(__riscv_vand_vv_u8m2(err1, err2, vl), err3, vl)); - - vbool4_t is_3 = __riscv_vmsgtu_vx_u8m2_b4(v1, 0b11100000-1, vl); - vbool4_t is_4 = __riscv_vmsgtu_vx_u8m2_b4(v0, 0b11110000-1, vl); - vbool4_t is_34 = __riscv_vmor_mm_b4(is_3, is_4, vl); - vbool4_t err34 = __riscv_vmxor_mm_b4(is_34, __riscv_vmslt_vx_i8m2_b4(errs, 0, vl), vl); - vbool4_t errm = __riscv_vmor_mm_b4(__riscv_vmsgt_vx_i8m2_b4(errs, 0, vl), err34, vl); - if (__riscv_vfirst_m_b4(errm , vl) >= 0) - return 0; - } - - /* decoding */ - - /* mask of non continuation bytes */ - vbool4_t m = __riscv_vmsgt_vx_i8m2_b4(__riscv_vreinterpret_v_u8m2_i8m2(v0), -65, vl); - vlOut = __riscv_vcpop_m_b4(m, vl); - - /* extract first and second bytes */ - vuint8m2_t b1 = __riscv_vcompress_vm_u8m2(v0, m, vl); - vuint8m2_t b2 = __riscv_vcompress_vm_u8m2(v1, m, vl); - - /* fast path: one and two byte */ - if (max < 0b11100000) { - b2 = __riscv_vand_vx_u8m2(b2, 0b00111111, vlOut); - - vbool4_t m1 = __riscv_vmsgtu_vx_u8m2_b4(b1, 0b10111111, vlOut); - b1 = __riscv_vand_vx_u8m2_mu(m1, b1, b1, 63, vlOut); - - vuint16m4_t b12 = __riscv_vwmulu_vv_u16m4(b1, __riscv_vmerge_vxm_u8m2(__riscv_vmv_v_x_u8m2(1, vlOut), 1<<6, m1, vlOut), vlOut); - b12 = __riscv_vwaddu_wv_u16m4_mu(m1, b12, b12, b2, vlOut); - if (is16) __riscv_vse16_v_u16m4((uint16_t*)dst, simdutf_byteflip(b12, vlOut), vlOut); - else __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf2_u32m8(b12, vlOut), vlOut); - continue; - } - - /* fast path: one, two and three byte */ - if (max < 0b11110000) { - vuint8m2_t b3 = __riscv_vcompress_vm_u8m2(v2, m, vl); - - b2 = __riscv_vand_vx_u8m2(b2, 0b00111111, vlOut); - b3 = __riscv_vand_vx_u8m2(b3, 0b00111111, vlOut); - - vbool4_t m1 = __riscv_vmsgtu_vx_u8m2_b4(b1, 0b10111111, vlOut); - vbool4_t m3 = __riscv_vmsgtu_vx_u8m2_b4(b1, 0b11011111, vlOut); - - vuint8m2_t t1 = __riscv_vand_vx_u8m2_mu(m1, b1, b1, 63, vlOut); - b1 = __riscv_vand_vx_u8m2_mu(m3, t1, b1, 15, vlOut); - - vuint16m4_t b12 = __riscv_vwmulu_vv_u16m4(b1, __riscv_vmerge_vxm_u8m2(__riscv_vmv_v_x_u8m2(1, vlOut), 1<<6, m1, vlOut), vlOut); - b12 = __riscv_vwaddu_wv_u16m4_mu(m1, b12, b12, b2, vlOut); - vuint16m4_t b123 = __riscv_vwaddu_wv_u16m4_mu(m3, b12, __riscv_vsll_vx_u16m4_mu(m3, b12, b12, 6, vlOut), b3, vlOut); - if (is16) __riscv_vse16_v_u16m4((uint16_t*)dst, simdutf_byteflip(b123, vlOut), vlOut); - else __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf2_u32m8(b123, vlOut), vlOut); - continue; - } - - /* extract third and fourth bytes */ - vuint8m2_t b3 = __riscv_vcompress_vm_u8m2(v2, m, vl); - vuint8m2_t b4 = __riscv_vcompress_vm_u8m2(v3, m, vl); - - #define SIMDUTF_RVV_UTF8_TO_COMMON_M1(idx) \ - vuint8m1_t c1 = __riscv_vget_v_u8m2_u8m1(b1, idx); \ - vuint8m1_t c2 = __riscv_vget_v_u8m2_u8m1(b2, idx); \ - vuint8m1_t c3 = __riscv_vget_v_u8m2_u8m1(b3, idx); \ - vuint8m1_t c4 = __riscv_vget_v_u8m2_u8m1(b4, idx); \ - /* remove prefix from trailing bytes */ \ - c2 = __riscv_vand_vx_u8m1(c2, 0b00111111, vlOut); \ - c3 = __riscv_vand_vx_u8m1(c3, 0b00111111, vlOut); \ - c4 = __riscv_vand_vx_u8m1(c4, 0b00111111, vlOut); \ - /* remove prefix from leading bytes - * - * We could also use vrgather here, but it increases register pressure, - * and its performance varies widely on current platforms. It might be - * worth reconsidering, though, once there is more hardware available. - * Same goes for the __riscv_vsrl_vv_u32m4 correction step. - * - * We shift left and then right by the number of bytes in the prefix, - * which can be calculated as follows: - * x max(x-10, 0) - * 0xxx -> 0000-0111 -> sift by 0 or 1 -> 0 - * 10xx -> 1000-1011 -> don't care - * 110x -> 1100,1101 -> sift by 3 -> 2,3 - * 1110 -> 1110 -> sift by 4 -> 4 - * 1111 -> 1111 -> sift by 5 -> 5 - * - * vssubu.vx v, 10, (max(x-10, 0)) almost gives us what we want, we - * just need to manually detect and handle the one special case: - */ \ - vuint8m1_t shift = __riscv_vsrl_vx_u8m1(c1, 4, vlOut); \ - shift = __riscv_vmerge_vxm_u8m1(__riscv_vssubu_vx_u8m1(shift, 10, vlOut), 3, __riscv_vmseq_vx_u8m1_b8(shift, 12, vlOut), vlOut); \ - c1 = __riscv_vsll_vv_u8m1(c1, shift, vlOut); \ - c1 = __riscv_vsrl_vv_u8m1(c1, shift, vlOut); \ - /* unconditionally widen and combine to c1234 */ \ - vuint16m2_t c34 = __riscv_vwaddu_wv_u16m2(__riscv_vwmulu_vx_u16m2(c3, 1<<6, vlOut), c4, vlOut); \ - vuint16m2_t c12 = __riscv_vwaddu_wv_u16m2(__riscv_vwmulu_vx_u16m2(c1, 1<<6, vlOut), c2, vlOut); \ - vuint32m4_t c1234 = __riscv_vwaddu_wv_u32m4(__riscv_vwmulu_vx_u32m4(c12, 1 << 12, vlOut), c34, vlOut); \ - /* derive required right-shift amount from `shift` to reduce - * c1234 to the required number of bytes */ \ - c1234 = __riscv_vsrl_vv_u32m4(c1234, __riscv_vzext_vf4_u32m4(__riscv_vmul_vx_u8m1( \ - __riscv_vrsub_vx_u8m1( __riscv_vssubu_vx_u8m1(shift, 2, vlOut), 3, vlOut), 6, vlOut), vlOut), vlOut); \ - /* store result in desired format */ \ - if (is16) vlDst = rvv_utf32_store_utf16_m4((uint16_t*)dst, c1234, vlOut, m4even); \ - else vlDst = vlOut, __riscv_vse32_v_u32m4((uint32_t*)dst, c1234, vlOut); - - /* Unrolling this manually reduces register pressure and allows - * us to terminate early. */ - { - size_t vlOutm2 = vlOut, vlDst; - vlOut = __riscv_vsetvl_e8m1(vlOut); - SIMDUTF_RVV_UTF8_TO_COMMON_M1(0) - if (vlOutm2 == vlOut) { - vlOut = vlDst; - continue; - } - - dst += vlDst; - vlOut = vlOutm2 - vlOut; - } - { - size_t vlDst; - SIMDUTF_RVV_UTF8_TO_COMMON_M1(1) - vlOut = vlDst; - } - -#undef SIMDUTF_RVV_UTF8_TO_COMMON_M1 - } - - /* validate the last character and reparse it + tail */ - if (len > tail) { - if ((src[0] >> 6) == 0b10) - --dst; - while ((src[0] >> 6) == 0b10 && tail < len) - --src, ++tail; - if (is16) { - /* go back one more, when on high surrogate */ - if (simdutf_byteflip((uint16_t)dst[-1]) >= 0xD800 && simdutf_byteflip((uint16_t)dst[-1]) <= 0xDBFF) - --dst; - } - } - size_t ret = scalar(src, tail, dst); - if (ret == 0) return 0; - return (size_t)(dst - beg) + ret; -} - - -simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char *src, size_t len, char *dst) const noexcept { - const char *beg = dst; - uint8_t last = 0b10000000; - for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut, last = src[-1]) { - vl = __riscv_vsetvl_e8m2(len); - vuint8m2_t v1 = __riscv_vle8_v_u8m2((uint8_t*)src, vl); - vbool4_t m = __riscv_vmsltu_vx_u8m2_b4(v1, 0b11000000, vl); - vlOut = __riscv_vcpop_m_b4(m, vl); - if (vlOut != vl || last > 0b01111111) { - vuint8m2_t v0 = __riscv_vslide1up_vx_u8m2(v1, last, vl); - - vbool4_t leading0 = __riscv_vmsgtu_vx_u8m2_b4(v0, 0b10111111, vl); - vbool4_t trailing1 = __riscv_vmslt_vx_i8m2_b4(__riscv_vreinterpret_v_u8m2_i8m2(v1), (uint8_t)0b11000000, vl); - vbool4_t tobig = __riscv_vmand_mm_b4(leading0, __riscv_vmsgtu_vx_u8m2_b4(__riscv_vxor_vx_u8m2(v0, (uint8_t)-62, vl), 1, vl), vl); - if (__riscv_vfirst_m_b4(__riscv_vmor_mm_b4(tobig, __riscv_vmxor_mm_b4(leading0, trailing1, vl), vl), vl) >= 0) - return 0; - - v1 = __riscv_vor_vx_u8m2_mu(__riscv_vmseq_vx_u8m2_b4(v0, 0b11000011, vl), v1, v1, 0b01000000, vl); - v1 = __riscv_vcompress_vm_u8m2(v1, m, vl); - } - __riscv_vse8_v_u8m2((uint8_t*)dst, v1, vlOut); - } - if (last > 0b10111111) - return 0; - return dst - beg; -} - -simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char *src, size_t len, char *dst) const noexcept { - size_t res = convert_utf8_to_latin1(src, len, dst); - if (res) return result(error_code::SUCCESS, res); - return scalar::utf8_to_latin1::convert_with_errors(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char *src, size_t len, char *dst) const noexcept { - const char *beg = dst; - uint8_t last = 0b11000000; - for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut, last = src[-1]) { - vl = __riscv_vsetvl_e8m2(len); - vuint8m2_t v1 = __riscv_vle8_v_u8m2((uint8_t*)src, vl); - vbool4_t m = __riscv_vmsltu_vx_u8m2_b4(v1, 0b11000000, vl); - vlOut = __riscv_vcpop_m_b4(m, vl); - if (vlOut != vl || last > 0b01111111) { - vuint8m2_t v0 = __riscv_vslide1up_vx_u8m2(v1, last, vl); - v1 = __riscv_vor_vx_u8m2_mu(__riscv_vmseq_vx_u8m2_b4(v0, 0b11000011, vl), v1, v1, 0b01000000, vl); - v1 = __riscv_vcompress_vm_u8m2(v1, m, vl); - } - __riscv_vse8_v_u8m2((uint8_t*)dst, v1, vlOut); - } - return dst - beg; -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char *src, size_t len, char16_t *dst) const noexcept { - return rvv_utf8_to_common(src, len, (uint16_t*)dst); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char *src, size_t len, char16_t *dst) const noexcept { - if (supports_zvbb()) - return rvv_utf8_to_common(src, len, (uint16_t*)dst); - else - return rvv_utf8_to_common(src, len, (uint16_t*)dst); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char *src, size_t len, char16_t *dst) const noexcept { - size_t res = convert_utf8_to_utf16le(src, len, dst); - if (res) return result(error_code::SUCCESS, res); - return scalar::utf8_to_utf16::convert_with_errors(src, len, dst); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char *src, size_t len, char16_t *dst) const noexcept { - size_t res = convert_utf8_to_utf16be(src, len, dst); - if (res) return result(error_code::SUCCESS, res); - return scalar::utf8_to_utf16::convert_with_errors(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char *src, size_t len, char16_t *dst) const noexcept { - return rvv_utf8_to_common(src, len, (uint16_t*)dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char *src, size_t len, char16_t *dst) const noexcept { - if (supports_zvbb()) - return rvv_utf8_to_common(src, len, (uint16_t*)dst); - else - return rvv_utf8_to_common(src, len, (uint16_t*)dst); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char *src, size_t len, char32_t *dst) const noexcept { - return rvv_utf8_to_common(src, len, (uint32_t*)dst); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char *src, size_t len, char32_t *dst) const noexcept { - size_t res = convert_utf8_to_utf32(src, len, dst); - if (res) return result(error_code::SUCCESS, res); - return scalar::utf8_to_utf32::convert_with_errors(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char *src, size_t len, char32_t *dst) const noexcept { - return rvv_utf8_to_common(src, len, (uint32_t*)dst); -} - -/* end file src/rvv/rvv_utf8_to.inl.cpp */ -/* begin file src/rvv/rvv_utf16_to.inl.cpp */ -#include - -template -simdutf_really_inline static result rvv_utf16_to_latin1_with_errors(const char16_t *src, size_t len, char *dst) { - const char16_t *const beg = src; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl); - v = simdutf_byteflip(v, vl); - long idx = __riscv_vfirst_m_b2(__riscv_vmsgtu_vx_u16m8_b2(v, 255, vl), vl); - if (idx >= 0) - return result(error_code::TOO_LARGE, beg - src + idx); - __riscv_vse8_v_u8m4((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m4(v, vl), vl); - } - return result(error_code::SUCCESS, src - beg); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept { - result res = convert_utf16le_to_latin1_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept { - result res = convert_utf16be_to_latin1_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t *src, size_t len, char *dst) const noexcept { - return rvv_utf16_to_latin1_with_errors(src, len, dst); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t *src, size_t len, char *dst) const noexcept { - if (supports_zvbb()) - return rvv_utf16_to_latin1_with_errors(src, len, dst); - else - return rvv_utf16_to_latin1_with_errors(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept { - const char16_t *const beg = src; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl); - __riscv_vse8_v_u8m4((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m4(v, vl), vl); - } - return src - beg; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept { - const char16_t *const beg = src; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl); - __riscv_vse8_v_u8m4((uint8_t*)dst, __riscv_vnsrl_wx_u8m4(v, 8, vl), vl); - } - return src - beg; -} - -template -simdutf_really_inline static result rvv_utf16_to_utf8_with_errors(const char16_t *src, size_t len, char *dst) { - size_t n = len; - const char16_t *srcBeg = src; - const char *dstBeg = dst; - size_t vl8m4 = __riscv_vsetvlmax_e8m4(); - vbool2_t m4mulp2 = __riscv_vmseq_vx_u8m4_b2(__riscv_vand_vx_u8m4(__riscv_vid_v_u8m4(vl8m4), 3, vl8m4), 2, vl8m4); - - for (size_t vl, vlOut; n > 0; ) { - vl = __riscv_vsetvl_e16m2(n); - - vuint16m2_t v = __riscv_vle16_v_u16m2((uint16_t const*)src, vl); - v = simdutf_byteflip(v, vl); - vbool8_t m234 = __riscv_vmsgtu_vx_u16m2_b8(v, 0x80-1, vl); - - if (__riscv_vfirst_m_b8(m234,vl) < 0) { /* 1 byte utf8 */ - vlOut = vl; - __riscv_vse8_v_u8m1((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m1(v, vlOut), vlOut); - n -= vl, src += vl, dst += vlOut; - continue; - } - - vbool8_t m34 = __riscv_vmsgtu_vx_u16m2_b8(v, 0x800-1, vl); - - if (__riscv_vfirst_m_b8(m34,vl) < 0) { /* 1/2 byte utf8 */ - /* 0: [ aaa|aabbbbbb] - * 1: [aabbbbbb| ] vsll 8 - * 2: [ | aaaaa] vsrl 6 - * 3: [00111111|00011111] - * 4: [ bbbbbb|000aaaaa] (1|2)&3 - * 5: [11000000|11000000] - * 6: [10bbbbbb|110aaaaa] 4|5 */ - vuint16m2_t twoByte = - __riscv_vand_vx_u16m2(__riscv_vor_vv_u16m2( - __riscv_vsll_vx_u16m2(v, 8, vl), - __riscv_vsrl_vx_u16m2(v, 6, vl), - vl), 0b0011111100011111, vl); - vuint16m2_t vout16 = __riscv_vor_vx_u16m2_mu(m234, v, twoByte, 0b1000000011000000, vl); - vuint8m2_t vout = __riscv_vreinterpret_v_u16m2_u8m2(vout16); - - /* Every high byte that is zero should be compressed - * low bytes should never be compressed, so we set them - * to all ones, and then create a non-zero bytes mask */ - vbool4_t mcomp = __riscv_vmsne_vx_u8m2_b4(__riscv_vreinterpret_v_u16m2_u8m2(__riscv_vor_vx_u16m2(vout16, 0xFF, vl)), 0, vl*2); - vlOut = __riscv_vcpop_m_b4(mcomp, vl*2); - - vout = __riscv_vcompress_vm_u8m2(vout, mcomp, vl*2); - __riscv_vse8_v_u8m2((uint8_t*)dst, vout, vlOut); - - n -= vl, src += vl, dst += vlOut; - continue; - } - - vbool8_t sur = __riscv_vmseq_vx_u16m2_b8(__riscv_vand_vx_u16m2(v, 0xF800, vl), 0xD800, vl); - long first = __riscv_vfirst_m_b8(sur, vl); - size_t tail = vl - first; - vl = first < 0 ? vl : first; - - if (vl > 0) { /* 1/2/3 byte utf8 */ - /* in: [aaaabbbb|bbcccccc] - * v1: [0bcccccc| ] vsll 8 - * v1: [10cccccc| ] vsll 8 & 0b00111111 | 0b10000000 - * v2: [ |110bbbbb] vsrl 6 & 0b00111111 | 0b11000000 - * v2: [ |10bbbbbb] vsrl 6 & 0b00111111 | 0b10000000 - * v3: [ |1110aaaa] vsrl 12 | 0b11100000 - * 1: [00000000|0bcccccc|00000000|00000000] => [0bcccccc] - * 2: [00000000|10cccccc|110bbbbb|00000000] => [110bbbbb] [10cccccc] - * 3: [00000000|10cccccc|10bbbbbb|1110aaaa] => [1110aaaa] [10bbbbbb] [10cccccc] - */ - vuint16m2_t v1, v2, v3, v12; - v1 = __riscv_vor_vx_u16m2_mu(m234, v, __riscv_vand_vx_u16m2(v, 0b00111111, vl), 0b10000000, vl); - v1 = __riscv_vsll_vx_u16m2(v1, 8, vl); - - v2 = __riscv_vor_vx_u16m2(__riscv_vand_vx_u16m2(__riscv_vsrl_vx_u16m2(v, 6, vl), 0b00111111, vl), 0b10000000, vl); - v2 = __riscv_vor_vx_u16m2_mu(__riscv_vmnot_m_b8(m34,vl), v2, v2, 0b01000000, vl); - v3 = __riscv_vor_vx_u16m2(__riscv_vsrl_vx_u16m2(v, 12, vl), 0b11100000, vl); - v12 = __riscv_vor_vv_u16m2_mu(m234, v1, v1, v2, vl); - - vuint32m4_t w12 = __riscv_vwmulu_vx_u32m4(v12, 1<<8, vl); - vuint32m4_t w123 = __riscv_vwaddu_wv_u32m4_mu(m34, w12, w12, v3, vl); - vuint8m4_t vout = __riscv_vreinterpret_v_u32m4_u8m4(w123); - - vbool2_t mcomp = __riscv_vmor_mm_b2(m4mulp2, __riscv_vmsne_vx_u8m4_b2(vout, 0, vl*4), vl*4); - vlOut = __riscv_vcpop_m_b2(mcomp, vl*4); - - vout = __riscv_vcompress_vm_u8m4(vout, mcomp, vl*4); - __riscv_vse8_v_u8m4((uint8_t*)dst, vout, vlOut); - - n -= vl, src += vl, dst += vlOut; - } - - if (tail) while (n) { - uint16_t word = simdutf_byteflip(src[0]); - if((word & 0xFF80)==0) { - break; - } else if((word & 0xF800)==0) { - break; - } else if ((word & 0xF800) != 0xD800) { - break; - } else { - // must be a surrogate pair - if (n <= 1) return result(error_code::SURROGATE, src - srcBeg); - uint16_t diff = word - 0xD800; - if (diff > 0x3FF) return result(error_code::SURROGATE, src - srcBeg); - uint16_t diff2 = simdutf_byteflip(src[1]) - 0xDC00; - if (diff2 > 0x3FF) return result(error_code::SURROGATE, src - srcBeg); - - uint32_t value = ((diff + 0x40) << 10) + diff2 ; - - // will generate four UTF-8 bytes - // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX - *dst++ = (char)( (value>>18) | 0b11110000); - *dst++ = (char)(((value>>12) & 0b111111) | 0b10000000); - *dst++ = (char)(((value>> 6) & 0b111111) | 0b10000000); - *dst++ = (char)(( value & 0b111111) | 0b10000000); - src += 2; - n -= 2; - } - } - } - - return result(error_code::SUCCESS, dst - dstBeg); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept { - result res = convert_utf16le_to_utf8_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept { - result res = convert_utf16be_to_utf8_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t *src, size_t len, char *dst) const noexcept { - return rvv_utf16_to_utf8_with_errors(src, len, dst); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t *src, size_t len, char *dst) const noexcept { - if (supports_zvbb()) - return rvv_utf16_to_utf8_with_errors(src, len, dst); - else - return rvv_utf16_to_utf8_with_errors(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept { - return convert_utf16le_to_utf8(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept { - return convert_utf16be_to_utf8(src, len, dst); -} - -template -simdutf_really_inline static result rvv_utf16_to_utf32_with_errors(const char16_t *src, size_t len, char32_t *dst) { - const char16_t *const srcBeg = src; - char32_t *const dstBeg = dst; - - constexpr const uint16_t ANY_SURROGATE_MASK = 0xf800; - constexpr const uint16_t ANY_SURROGATE_VALUE = 0xd800; - constexpr const uint16_t LO_SURROGATE_MASK = 0xfc00; - constexpr const uint16_t LO_SURROGATE_VALUE = 0xdc00; - constexpr const uint16_t HI_SURROGATE_MASK = 0xfc00; - constexpr const uint16_t HI_SURROGATE_VALUE = 0xd800; - - uint16_t last = 0; - while (len > 0) { - size_t vl = __riscv_vsetvl_e16m2(len); - vuint16m2_t v0 = __riscv_vle16_v_u16m2((uint16_t const*)src, vl); - v0 = simdutf_byteflip(v0, vl); - - { // check fast-path - const vuint16m2_t v = __riscv_vand_vx_u16m2(v0, ANY_SURROGATE_MASK, vl); - const vbool8_t any_surrogate = __riscv_vmseq_vx_u16m2_b8(v, ANY_SURROGATE_VALUE, vl); - if (__riscv_vfirst_m_b8(any_surrogate, vl) < 0) { - /* no surrogates */ - __riscv_vse32_v_u32m4((uint32_t*)dst, __riscv_vzext_vf2_u32m4(v0, vl), vl); - len -= vl; - src += vl; - dst += vl; - continue; - } - } - - if ((simdutf_byteflip(src[0]) & LO_SURROGATE_MASK) == LO_SURROGATE_VALUE) { - return result(error_code::SURROGATE, src - srcBeg); - } - - // decode surrogates - vuint16m2_t v1 = __riscv_vslide1down_vx_u16m2(v0, 0, vl); - vl = __riscv_vsetvl_e16m2(vl - 1); - if (vl == 0) { - return result(error_code::SURROGATE, src - srcBeg); - } - - const vbool8_t surhi = __riscv_vmseq_vx_u16m2_b8(__riscv_vand_vx_u16m2(v0, HI_SURROGATE_MASK, vl), HI_SURROGATE_VALUE, vl); - const vbool8_t surlo = __riscv_vmseq_vx_u16m2_b8(__riscv_vand_vx_u16m2(v1, LO_SURROGATE_MASK, vl), LO_SURROGATE_VALUE, vl); - - // compress everything but lo surrogates - const vbool8_t compress = __riscv_vmsne_vx_u16m2_b8(__riscv_vand_vx_u16m2(v0, LO_SURROGATE_MASK, vl), LO_SURROGATE_VALUE, vl); - - { - const vbool8_t diff = __riscv_vmxor_mm_b8(surhi, surlo, vl); - const long idx = __riscv_vfirst_m_b8(diff, vl); - if (idx >= 0) { - return result(error_code::SURROGATE, src - srcBeg + idx + 1); - } - } - - last = simdutf_byteflip(src[vl]); - vuint32m4_t utf32 = __riscv_vzext_vf2_u32m4(v0, vl); - - // v0 = 110110yyyyyyyyyy (0xd800 + yyyyyyyyyy) --- hi surrogate - // v1 = 110111xxxxxxxxxx (0xdc00 + xxxxxxxxxx) --- lo surrogate - - // t0 = u16( 0000_00yy_yyyy_yyyy) - const vuint32m4_t t0 = __riscv_vzext_vf2_u32m4(__riscv_vand_vx_u16m2(v0, 0x03ff, vl), vl); - // t1 = u32(0000_0000_0000_yyyy_yyyy_yy00_0000_0000) - const vuint32m4_t t1 = __riscv_vsll_vx_u32m4(t0, 10, vl); - - // t2 = u32(0000_0000_0000_0000_0000_00xx_xxxx_xxxx) - const vuint32m4_t t2 = __riscv_vzext_vf2_u32m4(__riscv_vand_vx_u16m2(v1, 0x03ff, vl), vl); - - // t3 = u32(0000_0000_0000_yyyy_yyyy_yyxx_xxxx_xxxx) - const vuint32m4_t t3 = __riscv_vor_vv_u32m4(t1, t2, vl); - - // t4 = utf32 from surrogate pairs - const vuint32m4_t t4 = __riscv_vadd_vx_u32m4(t3, 0x10000, vl); - - const vuint32m4_t result = __riscv_vmerge_vvm_u32m4(utf32, t4, surhi, vl); - - const vuint32m4_t comp = __riscv_vcompress_vm_u32m4(result, compress, vl); - const size_t vlOut = __riscv_vcpop_m_b8(compress, vl); - __riscv_vse32_v_u32m4((uint32_t*)dst, comp, vlOut); - - len -= vl; - src += vl; - dst += vlOut; - - if ((last & LO_SURROGATE_MASK) == LO_SURROGATE_VALUE) { - // last item is lo surrogate and got already consumed - len -= 1; - src += 1; - } - } - - return result(error_code::SUCCESS, dst - dstBeg); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept { - result res = convert_utf16le_to_utf32_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept { - result res = convert_utf16be_to_utf32_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t *src, size_t len, char32_t *dst) const noexcept { - return rvv_utf16_to_utf32_with_errors(src, len, dst); -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t *src, size_t len, char32_t *dst) const noexcept { - if (supports_zvbb()) - return rvv_utf16_to_utf32_with_errors(src, len, dst); - else - return rvv_utf16_to_utf32_with_errors(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept { - return convert_utf16le_to_utf32(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept { - return convert_utf16be_to_utf32(src, len, dst); -} -/* end file src/rvv/rvv_utf16_to.inl.cpp */ -/* begin file src/rvv/rvv_utf32_to.inl.cpp */ - -simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t *src, size_t len, char *dst) const noexcept { - result res = convert_utf32_to_latin1_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t *src, size_t len, char *dst) const noexcept { - const char32_t *const beg = src; - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e32m8(len); - vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl); - long idx = __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(v, 255, vl), vl); - if (idx >= 0) - return result(error_code::TOO_LARGE, src - beg + idx); - /* We don't use vcompress here, because its performance varies widely on current platforms. - * This might be worth reconsidering once there is more hardware available. */ - __riscv_vse8_v_u8m2((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m2(__riscv_vncvt_x_x_w_u16m4(v, vl), vl), vl); - } - return result(error_code::SUCCESS, src - beg); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t *src, size_t len, char *dst) const noexcept { - return convert_utf32_to_latin1(src, len, dst); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t *src, size_t len, char *dst) const noexcept { - size_t n = len; - const char32_t *srcBeg = src; - const char *dstBeg = dst; - size_t vl8m4 = __riscv_vsetvlmax_e8m4(); - vbool2_t m4mulp2 = __riscv_vmseq_vx_u8m4_b2(__riscv_vand_vx_u8m4(__riscv_vid_v_u8m4(vl8m4), 3, vl8m4), 2, vl8m4); - - for (size_t vl, vlOut; n > 0; ) { - vl = __riscv_vsetvl_e32m4(n); - - vuint32m4_t v = __riscv_vle32_v_u32m4((uint32_t const*)src, vl); - vbool8_t m234 = __riscv_vmsgtu_vx_u32m4_b8(v, 0x80-1, vl); - vuint16m2_t vn = __riscv_vncvt_x_x_w_u16m2(v, vl); - - if (__riscv_vfirst_m_b8(m234, vl) < 0) { /* 1 byte utf8 */ - vlOut = vl; - __riscv_vse8_v_u8m1((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m1(vn, vlOut), vlOut); - n -= vl, src += vl, dst += vlOut; - continue; - } - - vbool8_t m34 = __riscv_vmsgtu_vx_u32m4_b8(v, 0x800-1, vl); - - if (__riscv_vfirst_m_b8(m34,vl) < 0) { /* 1/2 byte utf8 */ - /* 0: [ aaa|aabbbbbb] - * 1: [aabbbbbb| ] vsll 8 - * 2: [ | aaaaa] vsrl 6 - * 3: [00111111|00111111] - * 4: [ bbbbbb|000aaaaa] (1|2)&3 - * 5: [10000000|11000000] - * 6: [10bbbbbb|110aaaaa] 4|5 */ - vuint16m2_t twoByte = - __riscv_vand_vx_u16m2(__riscv_vor_vv_u16m2( - __riscv_vsll_vx_u16m2(vn, 8, vl), - __riscv_vsrl_vx_u16m2(vn, 6, vl), - vl), 0b0011111100111111, vl); - vuint16m2_t vout16 = __riscv_vor_vx_u16m2_mu(m234, vn, twoByte, 0b1000000011000000, vl); - vuint8m2_t vout = __riscv_vreinterpret_v_u16m2_u8m2(vout16); - - /* Every high byte that is zero should be compressed - * low bytes should never be compressed, so we set them - * to all ones, and then create a non-zero bytes mask */ - vbool4_t mcomp = __riscv_vmsne_vx_u8m2_b4(__riscv_vreinterpret_v_u16m2_u8m2(__riscv_vor_vx_u16m2(vout16, 0xFF, vl)), 0, vl*2); - vlOut = __riscv_vcpop_m_b4(mcomp, vl*2); - - vout = __riscv_vcompress_vm_u8m2(vout, mcomp, vl*2); - __riscv_vse8_v_u8m2((uint8_t*)dst, vout, vlOut); - - n -= vl, src += vl, dst += vlOut; - continue; - } - - vbool8_t sur = __riscv_vmseq_vx_u32m4_b8(__riscv_vand_vx_u32m4(v, 0xFFFFF800, vl), 0xD800, vl); - long idx = __riscv_vfirst_m_b8(sur, vl); - if (idx >= 0) return result(error_code::SURROGATE, src - srcBeg + idx); - - vbool8_t m4 = __riscv_vmsgtu_vx_u32m4_b8(v, 0x10000-1, vl); - long first = __riscv_vfirst_m_b8(m4, vl); - size_t tail = vl - first; - vl = first < 0 ? vl : first; - - if (vl > 0) { /* 1/2/3 byte utf8 */ - /* vn: [aaaabbbb|bbcccccc] - * v1: [0bcccccc| ] vsll 8 - * v1: [10cccccc| ] vsll 8 & 0b00111111 | 0b10000000 - * v2: [ |110bbbbb] vsrl 6 & 0b00111111 | 0b11000000 - * v2: [ |10bbbbbb] vsrl 6 & 0b00111111 | 0b10000000 - * v3: [ |1110aaaa] vsrl 12 | 0b11100000 - * 1: [00000000|0bcccccc|00000000|00000000] => [0bcccccc] - * 2: [00000000|10cccccc|110bbbbb|00000000] => [110bbbbb] [10cccccc] - * 3: [00000000|10cccccc|10bbbbbb|1110aaaa] => [1110aaaa] [10bbbbbb] [10cccccc] - */ - vuint16m2_t v1, v2, v3, v12; - v1 = __riscv_vor_vx_u16m2_mu(m234, vn, __riscv_vand_vx_u16m2(vn, 0b00111111, vl), 0b10000000, vl); - v1 = __riscv_vsll_vx_u16m2(v1, 8, vl); - - v2 = __riscv_vor_vx_u16m2(__riscv_vand_vx_u16m2(__riscv_vsrl_vx_u16m2(vn, 6, vl), 0b00111111, vl), 0b10000000, vl); - v2 = __riscv_vor_vx_u16m2_mu(__riscv_vmnot_m_b8(m34,vl), v2, v2, 0b01000000, vl); - v3 = __riscv_vor_vx_u16m2(__riscv_vsrl_vx_u16m2(vn, 12, vl), 0b11100000, vl); - v12 = __riscv_vor_vv_u16m2_mu(m234, v1, v1, v2, vl); - - vuint32m4_t w12 = __riscv_vwmulu_vx_u32m4(v12, 1<<8, vl); - vuint32m4_t w123 = __riscv_vwaddu_wv_u32m4_mu(m34, w12, w12, v3, vl); - vuint8m4_t vout = __riscv_vreinterpret_v_u32m4_u8m4(w123); - - vbool2_t mcomp = __riscv_vmor_mm_b2(m4mulp2, __riscv_vmsne_vx_u8m4_b2(vout, 0, vl*4), vl*4); - vlOut = __riscv_vcpop_m_b2(mcomp, vl*4); - - vout = __riscv_vcompress_vm_u8m4(vout, mcomp, vl*4); - __riscv_vse8_v_u8m4((uint8_t*)dst, vout, vlOut); - - n -= vl, src += vl, dst += vlOut; - } - - if (tail) while (n) { - uint32_t word = src[0]; - if (word < 0x10000) break; - if (word > 0x10FFFF) return result(error_code::TOO_LARGE, src - srcBeg); - *dst++ = (uint8_t)(( word>>18) | 0b11110000); - *dst++ = (uint8_t)(((word>>12) & 0b111111) | 0b10000000); - *dst++ = (uint8_t)(((word>> 6) & 0b111111) | 0b10000000); - *dst++ = (uint8_t)(( word & 0b111111) | 0b10000000); - ++src; - --n; - } - } - - return result(error_code::SUCCESS, dst - dstBeg); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t *src, size_t len, char *dst) const noexcept { - result res = convert_utf32_to_utf8_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t *src, size_t len, char *dst) const noexcept { - return convert_utf32_to_utf8(src, len, dst); -} - -template -simdutf_really_inline static result rvv_convert_utf32_to_utf16_with_errors(const char32_t *src, size_t len, char16_t *dst) { - size_t vl8m2 = __riscv_vsetvlmax_e8m2(); - vbool4_t m4even = __riscv_vmseq_vx_u8m2_b4(__riscv_vand_vx_u8m2(__riscv_vid_v_u8m2(vl8m2), 1, vl8m2), 0, vl8m2); - const char16_t *dstBeg = dst; - const char32_t *srcBeg = src; - for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut) { - vl = __riscv_vsetvl_e32m4(len); - vuint32m4_t v = __riscv_vle32_v_u32m4((uint32_t*)src, vl); - vuint32m4_t off = __riscv_vadd_vx_u32m4(v, 0xFFFF2000, vl); - long idx; - idx = __riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(off, 0xFFFFF7FF, vl), vl); - if (idx >= 0) return result(error_code::SURROGATE, src - srcBeg + idx); - idx = __riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(v, 0xFFFF, vl), vl); - if (idx < 0) { - vlOut = vl; - vuint16m2_t n = simdutf_byteflip(__riscv_vncvt_x_x_w_u16m2(v, vlOut), vlOut); - __riscv_vse16_v_u16m2((uint16_t*)dst, n, vlOut); - continue; - } - idx = __riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(v, 0x10FFFF, vl), vl); - if (idx >= 0) return result(error_code::TOO_LARGE, src - srcBeg + idx); - vlOut = rvv_utf32_store_utf16_m4((uint16_t*)dst, v, vl, m4even); - } - return result(error_code::SUCCESS, dst - dstBeg); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t *src, size_t len, char16_t *dst) const noexcept { - result res = convert_utf32_to_utf16le_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t *src, size_t len, char16_t *dst) const noexcept { - result res = convert_utf32_to_utf16be_with_errors(src, len, dst); - return res.error == error_code::SUCCESS ? res.count : 0; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t *src, size_t len, char16_t *dst) const noexcept { - return rvv_convert_utf32_to_utf16_with_errors(src, len, dst); -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t *src, size_t len, char16_t *dst) const noexcept { - if (supports_zvbb()) - return rvv_convert_utf32_to_utf16_with_errors(src, len, dst); - else - return rvv_convert_utf32_to_utf16_with_errors(src, len, dst); -} - -template -simdutf_really_inline static size_t rvv_convert_valid_utf32_to_utf16(const char32_t *src, size_t len, char16_t *dst) { - size_t vl8m2 = __riscv_vsetvlmax_e8m2(); - vbool4_t m4even = __riscv_vmseq_vx_u8m2_b4(__riscv_vand_vx_u8m2(__riscv_vid_v_u8m2(vl8m2), 1, vl8m2), 0, vl8m2); - char16_t *dstBeg = dst; - for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut) { - vl = __riscv_vsetvl_e32m4(len); - vuint32m4_t v = __riscv_vle32_v_u32m4((uint32_t*)src, vl); - if (__riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(v, 0xFFFF, vl), vl) < 0) { - vlOut = vl; - vuint16m2_t n = simdutf_byteflip(__riscv_vncvt_x_x_w_u16m2(v, vlOut), vlOut); - __riscv_vse16_v_u16m2((uint16_t*)dst, n, vlOut); - continue; - } - vlOut = rvv_utf32_store_utf16_m4((uint16_t*)dst, v, vl, m4even); - } - return dst - dstBeg; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t *src, size_t len, char16_t *dst) const noexcept { - return rvv_convert_valid_utf32_to_utf16(src, len, dst); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t *src, size_t len, char16_t *dst) const noexcept { - if (supports_zvbb()) - return rvv_convert_valid_utf32_to_utf16(src, len, dst); - else - return rvv_convert_valid_utf32_to_utf16(src, len, dst); -} -/* end file src/rvv/rvv_utf32_to.inl.cpp */ - -simdutf_warn_unused int implementation::detect_encodings(const char *input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if (bom_encoding != encoding_type::unspecified) - return bom_encoding; - int out = 0; - if (validate_utf8(input, length)) - out |= encoding_type::UTF8; - if (length % 2 == 0) { - if (validate_utf16(reinterpret_cast(input), length/2)) - out |= encoding_type::UTF16_LE; - } - if (length % 4 == 0) { - if (validate_utf32(reinterpret_cast(input), length/4)) - out |= encoding_type::UTF32_LE; - } - - return out; -} - -template -simdutf_really_inline static void rvv_change_endianness_utf16(const char16_t *src, size_t len, char16_t *dst) { - for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) { - vl = __riscv_vsetvl_e16m8(len); - vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl); - __riscv_vse16_v_u16m8((uint16_t *)dst, simdutf_byteflip(v, vl), vl); - } -} - -void implementation::change_endianness_utf16(const char16_t *src, size_t len, char16_t *dst) const noexcept { - if (supports_zvbb()) - return rvv_change_endianness_utf16(src, len, dst); - else - return rvv_change_endianness_utf16(src, len, dst); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - size_t equallocation = length; // location of the first padding character if any - size_t equalsigns = 0; - if(length > 0 && input[length - 1] == '=') { - length -= 1; - equalsigns++; - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - if(length > 0 && input[length - 1] == '=') { - equalsigns++; - length -= 1; - } - } - if(length == 0) { - if(equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - return {SUCCESS, 0}; - } - result r = scalar::base64::base64_tail_decode(output, input, length, options); - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - } - return r; -} - - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - size_t equallocation = length; // location of the first padding character if any - auto equalsigns = 0; - if(length > 0 && input[length - 1] == '=') { - length -= 1; - equalsigns++; - while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) { - length--; - } - if(length > 0 && input[length - 1] == '=') { - equalsigns++; - length -= 1; - } - } - if(length == 0) { - if(equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - return {SUCCESS, 0}; - } - result r = scalar::base64::base64_tail_decode(output, input, length, options); - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - } - return r; -} - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - return scalar::base64::tail_encode_base64(output, input, length, options); -} -} // namespace rvv -} // namespace simdutf - -/* begin file src/simdutf/rvv/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_RVV -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - -/* end file src/simdutf/rvv/end.h */ -/* end file src/rvv/implementation.cpp */ -#endif -#if SIMDUTF_IMPLEMENTATION_WESTMERE -/* begin file src/westmere/implementation.cpp */ -/* begin file src/simdutf/westmere/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "westmere" -// #define SIMDUTF_IMPLEMENTATION westmere - -#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE -// nothing needed. -#else -SIMDUTF_TARGET_WESTMERE -#endif -/* end file src/simdutf/westmere/begin.h */ -namespace simdutf { -namespace westmere { -namespace { -#ifndef SIMDUTF_WESTMERE_H -#error "westmere.h must be included" -#endif -using namespace simd; - -simdutf_really_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} - -simdutf_unused simdutf_really_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0b11000000u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0b11100000u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0b11110000u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdutf_really_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 - return simd8(is_third_byte | is_fourth_byte); -} - -/* begin file src/westmere/internal/loader.cpp */ -namespace internal { -namespace westmere { - -/* begin file src/westmere/internal/write_v_u16_11bits_to_utf8.cpp */ -/* -* reads a vector of uint16 values -* bits after 11th are ignored -* first 11 bits are encoded into utf8 -* !important! utf8_output must have at least 16 writable bytes -*/ - -inline void write_v_u16_11bits_to_utf8( - const __m128i v_u16, - char*& utf8_output, - const __m128i one_byte_bytemask, - const uint16_t one_byte_bitmask -) { - // 0b1100_0000_1000_0000 - const __m128i v_c080 = _mm_set1_epi16((int16_t)0xc080); - // 0b0001_1111_0000_0000 - const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00); - // 0b0000_0000_0011_1111 - const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f); - - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - - // t0 = [000a|aaaa|bbbb|bb00] - const __m128i t0 = _mm_slli_epi16(v_u16, 2); - // t1 = [000a|aaaa|0000|0000] - const __m128i t1 = _mm_and_si128(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m128i t2 = _mm_and_si128(v_u16, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m128i t3 = _mm_or_si128(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m128i t4 = _mm_or_si128(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m128i utf8_unpacked = _mm_blendv_epi8(t4, v_u16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - // one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a - LSB) - const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a - const uint16_t m1 = static_cast(m0 >> 7); // m1 = 00000000h0g0f0e0 - const uint8_t m2 = static_cast((m0 | m1) & 0xff); // m2 = hdgcfbea - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle); - - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - - // 6. adjust pointers - utf8_output += row[0]; -} - -inline void write_v_u16_11bits_to_utf8( - const __m128i v_u16, - char*& utf8_output, - const __m128i v_0000, - const __m128i v_ff80 -) { - // no bits set above 7th bit - const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(v_u16, v_ff80), v_0000); - const uint16_t one_byte_bitmask = static_cast(_mm_movemask_epi8(one_byte_bytemask)); - - write_v_u16_11bits_to_utf8( - v_u16, utf8_output, one_byte_bytemask, one_byte_bitmask); -} -/* end file src/westmere/internal/write_v_u16_11bits_to_utf8.cpp */ - -} // namespace westmere -} // namespace internal -/* end file src/westmere/internal/loader.cpp */ -/* begin file src/westmere/sse_detect_encodings.cpp */ -template -// len is known to be a multiple of 2 when this is called -int sse_detect_encodings(const char * buf, size_t len) { - const char* start = buf; - const char* end = buf + len; - - bool is_utf8 = true; - bool is_utf16 = true; - bool is_utf32 = true; - - int out = 0; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - - __m128i currentmax = _mm_setzero_si128(); - - checker check{}; - - while(buf + 64 <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - __m128i secondin = _mm_loadu_si128((__m128i*)buf+1); - __m128i thirdin = _mm_loadu_si128((__m128i*)buf+2); - __m128i fourthin = _mm_loadu_si128((__m128i*)buf+3); - - const auto u0 = simd16(in); - const auto u1 = simd16(secondin); - const auto u2 = simd16(thirdin); - const auto u3 = simd16(fourthin); - - const auto v0 = u0.shr<8>(); - const auto v1 = u1.shr<8>(); - const auto v2 = u2.shr<8>(); - const auto v3 = u3.shr<8>(); - - const auto in16 = simd16::pack(v0, v1); - const auto nextin16 = simd16::pack(v2, v3); - - const auto surrogates_wordmask0 = (in16 & v_f8) == v_d8; - const auto surrogates_wordmask1 = (nextin16 & v_f8) == v_d8; - uint16_t surrogates_bitmask0 = static_cast(surrogates_wordmask0.to_bitmask()); - uint16_t surrogates_bitmask1 = static_cast(surrogates_wordmask1.to_bitmask()); - - // Check for surrogates - if (surrogates_bitmask0 != 0x0 || surrogates_bitmask1 != 0x0) { - // Cannot be UTF8 - is_utf8 = false; - // Can still be either UTF-16LE or UTF-32 depending on the positions of the surrogates - // To be valid UTF-32, a surrogate cannot be in the two most significant bytes of any 32-bit word. - // On the other hand, to be valid UTF-16LE, at least one surrogate must be in the two most significant - // bytes of a 32-bit word since they always come in pairs in UTF-16LE. - // Note that we always proceed in multiple of 4 before this point so there is no offset in 32-bit code units. - - if (((surrogates_bitmask0 | surrogates_bitmask1) & 0xaaaa) != 0) { - is_utf32 = false; - // Code from sse_validate_utf16le.cpp - // Not efficient, we do not process surrogates_bitmask1 - const char16_t * input = reinterpret_cast(buf); - const char16_t* end16 = reinterpret_cast(start) + len/2; - - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - const uint16_t V0 = static_cast(~surrogates_bitmask0); - - const auto vH0 = (in16 & v_fc) == v_dc; - const uint16_t H0 = static_cast(vH0.to_bitmask()); - - const uint16_t L0 = static_cast(~H0 & surrogates_bitmask0); - - const uint16_t a0 = static_cast(L0 & (H0 >> 1)); - - const uint16_t b0 = static_cast(a0 << 1); - - const uint16_t c0 = static_cast(V0 | a0 | b0); - - if (c0 == 0xffff) { - input += 16; - } else if (c0 == 0x7fff) { - input += 15; - } else { - is_utf16 = false; - break; - } - - while (input + simd16::SIZE * 2 < end16) { - const auto in0 = simd16(input); - const auto in1 = simd16(input + simd16::SIZE / sizeof(char16_t)); - - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - - const auto in_16 = simd16::pack(t0, t1); - - const auto surrogates_wordmask = (in_16 & v_f8) == v_d8; - const uint16_t surrogates_bitmask = static_cast(surrogates_wordmask.to_bitmask()); - if (surrogates_bitmask == 0x0) { - input += 16; - } else { - const uint16_t V = static_cast(~surrogates_bitmask); - - const auto vH = (in_16 & v_fc) == v_dc; - const uint16_t H = static_cast(vH.to_bitmask()); - - const uint16_t L = static_cast(~H & surrogates_bitmask); - - const uint16_t a = static_cast(L & (H >> 1)); - - const uint16_t b = static_cast(a << 1); - - const uint16_t c = static_cast(V | a | b); - - if (c == 0xffff) { - input += 16; - } else if (c == 0x7fff) { - input += 15; - } else { - is_utf16 = false; - break; - } - } - } - } else { - is_utf16 = false; - // Check for UTF-32 - if (len % 4 == 0) { - const char32_t * input = reinterpret_cast(buf); - const char32_t* end32 = reinterpret_cast(start) + len/4; - - // Must start checking for surrogates - __m128i currentoffsetmax = _mm_setzero_si128(); - const __m128i offset = _mm_set1_epi32(0xffff2000); - const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff); - - currentmax = _mm_max_epu32(in, currentmax); - currentmax = _mm_max_epu32(secondin, currentmax); - currentmax = _mm_max_epu32(thirdin, currentmax); - currentmax = _mm_max_epu32(fourthin, currentmax); - - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in, offset), currentoffsetmax); - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(secondin, offset), currentoffsetmax); - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(thirdin, offset), currentoffsetmax); - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(fourthin, offset), currentoffsetmax); - - while (input + 4 < end32) { - const __m128i in32 = _mm_loadu_si128((__m128i *)input); - currentmax = _mm_max_epu32(in32,currentmax); - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in32, offset), currentoffsetmax); - input += 4; - } - - __m128i forbidden_words = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(_mm_testz_si128(forbidden_words, forbidden_words) == 0) { - is_utf32 = false; - } - } else { - is_utf32 = false; - } - } - break; - } - // If no surrogate, validate under other encodings as well - - // UTF-32 validation - currentmax = _mm_max_epu32(in, currentmax); - currentmax = _mm_max_epu32(secondin, currentmax); - currentmax = _mm_max_epu32(thirdin, currentmax); - currentmax = _mm_max_epu32(fourthin, currentmax); - - // UTF-8 validation - // Relies on ../generic/utf8_validation/utf8_lookup4_algorithm.h - simd::simd8x64 in8(in, secondin, thirdin, fourthin); - check.check_next_input(in8); - - buf += 64; - } - - // Check which encodings are possible - - if (is_utf8) { - if (static_cast(buf - start) != len) { - uint8_t block[64]{}; - std::memset(block, 0x20, 64); - std::memcpy(block, buf, len - (buf - start)); - simd::simd8x64 in(block); - check.check_next_input(in); - } - if (!check.errors()) { - out |= simdutf::encoding_type::UTF8; - } - } - - if (is_utf16 && scalar::utf16::validate(reinterpret_cast(buf), (len - (buf - start))/2)) { - out |= simdutf::encoding_type::UTF16_LE; - } - - if (is_utf32 && (len % 4 == 0)) { - const __m128i standardmax = _mm_set1_epi32(0x10ffff); - __m128i is_zero = _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax); - if (_mm_testz_si128(is_zero, is_zero) == 1 && scalar::utf32::validate(reinterpret_cast(buf), (len - (buf - start))/4)) { - out |= simdutf::encoding_type::UTF32_LE; - } - } - - return out; -} -/* end file src/westmere/sse_detect_encodings.cpp */ - -/* begin file src/westmere/sse_validate_utf16.cpp */ -/* - In UTF-16 code units in range 0xD800 to 0xDFFF have special meaning. - - In a vectorized algorithm we want to examine the most significant - nibble in order to select a fast path. If none of highest nibbles - are 0xD (13), than we are sure that UTF-16 chunk in a vector - register is valid. - - Let us analyze what we need to check if the nibble is 0xD. The - value of the preceding nibble determines what we have: - - 0xd000 .. 0xd7ff - a valid word - 0xd800 .. 0xdbff - low surrogate - 0xdc00 .. 0xdfff - high surrogate - - Other constraints we have to consider: - - there must not be two consecutive low surrogates (0xd800 .. 0xdbff) - - there must not be two consecutive high surrogates (0xdc00 .. 0xdfff) - - there must not be sole low surrogate nor high surrogate - - We're going to build three bitmasks based on the 3rd nibble: - - V = valid word, - - L = low surrogate (0xd800 .. 0xdbff) - - H = high surrogate (0xdc00 .. 0xdfff) - - 0 1 2 3 4 5 6 7 <--- word index - [ V | L | H | L | H | V | V | L ] - 1 0 0 0 0 1 1 0 - V = valid masks - 0 1 0 1 0 0 0 1 - L = low surrogate - 0 0 1 0 1 0 0 0 - H high surrogate - - - 1 0 0 0 0 1 1 0 V = valid masks - 0 1 0 1 0 0 0 0 a = L & (H >> 1) - 0 0 1 0 1 0 0 0 b = a << 1 - 1 1 1 1 1 1 1 0 c = V | a | b - ^ - the last bit can be zero, we just consume 7 code units - and recheck this word in the next iteration -*/ - -/* Returns: - - pointer to the last unprocessed character (a scalar fallback should check the rest); - - nullptr if an error was detected. -*/ -template -const char16_t* sse_validate_utf16(const char16_t* input, size_t size) { - const char16_t* end = input + size; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - while (input + simd16::SIZE * 2 < end) { - // 0. Load data: since the validation takes into account only higher - // byte of each word, we compress the two vectors into one which - // consists only the higher bytes. - auto in0 = simd16(input); - auto in1 = simd16(input + simd16::SIZE / sizeof(char16_t)); - if (big_endian) { - in0 = in0.swap_bytes(); - in1 = in1.swap_bytes(); - } - - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - - const auto in = simd16::pack(t0, t1); - - // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy). - const auto surrogates_wordmask = (in & v_f8) == v_d8; - const uint16_t surrogates_bitmask = static_cast(surrogates_wordmask.to_bitmask()); - if (surrogates_bitmask == 0x0000) { - input += 16; - } else { - // 2. We have some surrogates that have to be distinguished: - // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF) - // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF) - // - // Fact: high surrogate has 11th bit set (3rd bit in the higher word) - - // V - non-surrogate code units - // V = not surrogates_wordmask - const uint16_t V = static_cast(~surrogates_bitmask); - - // H - word-mask for high surrogates: the six highest bits are 0b1101'11 - const auto vH = (in & v_fc) == v_dc; - const uint16_t H = static_cast(vH.to_bitmask()); - - // L - word mask for low surrogates - // L = not H and surrogates_wordmask - const uint16_t L = static_cast(~H & surrogates_bitmask); - - const uint16_t a = static_cast(L & (H >> 1)); // A low surrogate must be followed by high one. - // (A low surrogate placed in the 7th register's word - // is an exception we handle.) - const uint16_t b = static_cast(a << 1); // Just mark that the opinput - startite fact is hold, - // thanks to that we have only two masks for valid case. - const uint16_t c = static_cast(V | a | b); // Combine all the masks into the final one. - - if (c == 0xffff) { - // The whole input register contains valid UTF-16, i.e., - // either single code units or proper surrogate pairs. - input += 16; - } else if (c == 0x7fff) { - // The 15 lower code units of the input register contains valid UTF-16. - // The 15th word may be either a low or high surrogate. It the next - // iteration we 1) check if the low surrogate is followed by a high - // one, 2) reject sole high surrogate. - input += 15; - } else { - return nullptr; - } - } - } - - return input; -} - - -template -const result sse_validate_utf16_with_errors(const char16_t* input, size_t size) { - const char16_t* start = input; - const char16_t* end = input + size; - - const auto v_d8 = simd8::splat(0xd8); - const auto v_f8 = simd8::splat(0xf8); - const auto v_fc = simd8::splat(0xfc); - const auto v_dc = simd8::splat(0xdc); - - while (input + simd16::SIZE * 2 < end) { - // 0. Load data: since the validation takes into account only higher - // byte of each word, we compress the two vectors into one which - // consists only the higher bytes. - auto in0 = simd16(input); - auto in1 = simd16(input + simd16::SIZE / sizeof(char16_t)); - - if (big_endian) { - in0 = in0.swap_bytes(); - in1 = in1.swap_bytes(); - } - - const auto t0 = in0.shr<8>(); - const auto t1 = in1.shr<8>(); - - const auto in = simd16::pack(t0, t1); - - // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy). - const auto surrogates_wordmask = (in & v_f8) == v_d8; - const uint16_t surrogates_bitmask = static_cast(surrogates_wordmask.to_bitmask()); - if (surrogates_bitmask == 0x0000) { - input += 16; - } else { - // 2. We have some surrogates that have to be distinguished: - // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF) - // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF) - // - // Fact: high surrogate has 11th bit set (3rd bit in the higher word) - - // V - non-surrogate code units - // V = not surrogates_wordmask - const uint16_t V = static_cast(~surrogates_bitmask); - - // H - word-mask for high surrogates: the six highest bits are 0b1101'11 - const auto vH = (in & v_fc) == v_dc; - const uint16_t H = static_cast(vH.to_bitmask()); - - // L - word mask for low surrogates - // L = not H and surrogates_wordmask - const uint16_t L = static_cast(~H & surrogates_bitmask); - - const uint16_t a = static_cast(L & (H >> 1)); // A low surrogate must be followed by high one. - // (A low surrogate placed in the 7th register's word - // is an exception we handle.) - const uint16_t b = static_cast(a << 1); // Just mark that the opinput - startite fact is hold, - // thanks to that we have only two masks for valid case. - const uint16_t c = static_cast(V | a | b); // Combine all the masks into the final one. - - if (c == 0xffff) { - // The whole input register contains valid UTF-16, i.e., - // either single code units or proper surrogate pairs. - input += 16; - } else if (c == 0x7fff) { - // The 15 lower code units of the input register contains valid UTF-16. - // The 15th word may be either a low or high surrogate. It the next - // iteration we 1) check if the low surrogate is followed by a high - // one, 2) reject sole high surrogate. - input += 15; - } else { - return result(error_code::SURROGATE, input - start); - } - } - } - - return result(error_code::SUCCESS, input - start); -} -/* end file src/westmere/sse_validate_utf16.cpp */ -/* begin file src/westmere/sse_validate_utf32le.cpp */ -/* Returns: - - pointer to the last unprocessed character (a scalar fallback should check the rest); - - nullptr if an error was detected. -*/ -const char32_t* sse_validate_utf32le(const char32_t* input, size_t size) { - const char32_t* end = input + size; - - const __m128i standardmax = _mm_set1_epi32(0x10ffff); - const __m128i offset = _mm_set1_epi32(0xffff2000); - const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff); - __m128i currentmax = _mm_setzero_si128(); - __m128i currentoffsetmax = _mm_setzero_si128(); - - while (input + 4 < end) { - const __m128i in = _mm_loadu_si128((__m128i *)input); - currentmax = _mm_max_epu32(in,currentmax); - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in, offset), currentoffsetmax); - input += 4; - } - __m128i is_zero = _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax); - if(_mm_test_all_zeros(is_zero, is_zero) == 0) { - return nullptr; - } - - is_zero = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(_mm_test_all_zeros(is_zero, is_zero) == 0) { - return nullptr; - } - - return input; -} - - -const result sse_validate_utf32le_with_errors(const char32_t* input, size_t size) { - const char32_t* start = input; - const char32_t* end = input + size; - - const __m128i standardmax = _mm_set1_epi32(0x10ffff); - const __m128i offset = _mm_set1_epi32(0xffff2000); - const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff); - __m128i currentmax = _mm_setzero_si128(); - __m128i currentoffsetmax = _mm_setzero_si128(); - - while (input + 4 < end) { - const __m128i in = _mm_loadu_si128((__m128i *)input); - currentmax = _mm_max_epu32(in,currentmax); - currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in, offset), currentoffsetmax); - - __m128i is_zero = _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax); - if(_mm_test_all_zeros(is_zero, is_zero) == 0) { - return result(error_code::TOO_LARGE, input - start); - } - - is_zero = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); - if(_mm_test_all_zeros(is_zero, is_zero) == 0) { - return result(error_code::SURROGATE, input - start); - } - input += 4; - } - - return result(error_code::SUCCESS, input - start); -} -/* end file src/westmere/sse_validate_utf32le.cpp */ - -/* begin file src/westmere/sse_convert_latin1_to_utf8.cpp */ -std::pair sse_convert_latin1_to_utf8( - const char* latin_input, - const size_t latin_input_length, - char* utf8_output) { - const char* end = latin_input + latin_input_length; - - const __m128i v_0000 = _mm_setzero_si128(); - // 0b1000_0000 - const __m128i v_80 = _mm_set1_epi8((uint8_t)0x80); - // 0b1111_1111_1000_0000 - const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80); - - const __m128i latin_1_half_into_u16_byte_mask = _mm_setr_epi8( - 0, '\x80', - 1, '\x80', - 2, '\x80', - 3, '\x80', - 4, '\x80', - 5, '\x80', - 6, '\x80', - 7, '\x80' - ); - - const __m128i latin_2_half_into_u16_byte_mask = _mm_setr_epi8( - 8, '\x80', - 9, '\x80', - 10, '\x80', - 11, '\x80', - 12, '\x80', - 13, '\x80', - 14, '\x80', - 15, '\x80' - ); - - // each latin1 takes 1-2 utf8 bytes - // slow path writes useful 8-15 bytes twice (eagerly writes 16 bytes and then adjust the pointer) - // so the last write can exceed the utf8_output size by 8-1 bytes - // by reserving 8 extra input bytes, we expect the output to have 8-16 bytes free - while (latin_input + 16 + 8 <= end) { - // Load 16 Latin1 characters (16 bytes) into a 128-bit register - __m128i v_latin = _mm_loadu_si128((__m128i*)latin_input); - - - if (_mm_testz_si128(v_latin, v_80)) {// ASCII fast path!!!! - _mm_storeu_si128((__m128i*)utf8_output, v_latin); - latin_input += 16; - utf8_output += 16; - continue; - } - - - // assuming a/b are bytes and A/B are uint16 of the same value - // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA - __m128i v_u16_latin_1_half = _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask); - // aaaa_aaaa_bbbb_bbbb -> BBBB_BBBB - __m128i v_u16_latin_2_half = _mm_shuffle_epi8(v_latin, latin_2_half_into_u16_byte_mask); - - - internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_1_half, utf8_output, v_0000, v_ff80); - internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_2_half, utf8_output, v_0000, v_ff80); - latin_input += 16; - } - - if (latin_input + 16 <= end) { - // Load 16 Latin1 characters (16 bytes) into a 128-bit register - __m128i v_latin = _mm_loadu_si128((__m128i*)latin_input); - - if (_mm_testz_si128(v_latin, v_80)) {// ASCII fast path!!!! - _mm_storeu_si128((__m128i*)utf8_output, v_latin); - latin_input += 16; - utf8_output += 16; - } else { - // assuming a/b are bytes and A/B are uint16 of the same value - // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA - __m128i v_u16_latin_1_half = _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask); - internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_1_half, utf8_output, v_0000, v_ff80); - latin_input += 8; - } - } - - return std::make_pair(latin_input, utf8_output); -} -/* end file src/westmere/sse_convert_latin1_to_utf8.cpp */ -/* begin file src/westmere/sse_convert_latin1_to_utf16.cpp */ -template -std::pair sse_convert_latin1_to_utf16(const char *latin1_input, size_t len, - char16_t *utf16_output) { - size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16 - for (size_t i = 0; i < rounded_len; i += 16) { - // Load 16 Latin1 characters into a 128-bit register - __m128i in = _mm_loadu_si128(reinterpret_cast(&latin1_input[i])); - __m128i out1 = big_endian ? _mm_unpacklo_epi8(_mm_setzero_si128(), in) - : _mm_unpacklo_epi8(in, _mm_setzero_si128()); - __m128i out2 = big_endian ? _mm_unpackhi_epi8(_mm_setzero_si128(), in) - : _mm_unpackhi_epi8(in, _mm_setzero_si128()); - // Zero extend each Latin1 character to 16-bit integers and store the results back to memory - _mm_storeu_si128(reinterpret_cast<__m128i*>(&utf16_output[i]), out1); - _mm_storeu_si128(reinterpret_cast<__m128i*>(&utf16_output[i + 8]), out2); - } - // return pointers pointing to where we left off - return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len); -} -/* end file src/westmere/sse_convert_latin1_to_utf16.cpp */ -/* begin file src/westmere/sse_convert_latin1_to_utf32.cpp */ -std::pair sse_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { - const char* end = buf + len; - - while (buf + 16 <= end) { - // Load 16 Latin1 characters (16 bytes) into a 128-bit register - __m128i in = _mm_loadu_si128((__m128i*)buf); - - // Shift input to process next 4 bytes - __m128i in_shifted1 = _mm_srli_si128(in, 4); - __m128i in_shifted2 = _mm_srli_si128(in, 8); - __m128i in_shifted3 = _mm_srli_si128(in, 12); - - // expand 8-bit to 32-bit unit - __m128i out1 = _mm_cvtepu8_epi32(in); - __m128i out2 = _mm_cvtepu8_epi32(in_shifted1); - __m128i out3 = _mm_cvtepu8_epi32(in_shifted2); - __m128i out4 = _mm_cvtepu8_epi32(in_shifted3); - - _mm_storeu_si128((__m128i*)utf32_output, out1); - _mm_storeu_si128((__m128i*)(utf32_output + 4), out2); - _mm_storeu_si128((__m128i*)(utf32_output + 8), out3); - _mm_storeu_si128((__m128i*)(utf32_output + 12), out4); - - utf32_output += 16; - buf += 16; - } - - return std::make_pair(buf, utf32_output); -} - -/* end file src/westmere/sse_convert_latin1_to_utf32.cpp */ - - -/* begin file src/westmere/sse_convert_utf8_to_utf16.cpp */ -// depends on "tables/utf8_to_utf16_tables.h" - - -// Convert up to 12 bytes from utf8 to utf16 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -template -size_t convert_masked_utf8_to_utf16(const char *input, - uint64_t utf8_end_of_code_point_mask, - char16_t *&utf16_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - // We first try a few fast paths. - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - const __m128i in = _mm_loadu_si128((__m128i *)input); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; - if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) { - // We process the data in chunks of 16 bytes. - __m128i ascii_first = _mm_cvtepu8_epi16(in); - __m128i ascii_second = _mm_cvtepu8_epi16(_mm_srli_si128(in,8)); - if (big_endian) { - ascii_first = _mm_shuffle_epi8(ascii_first, swap); - ascii_second = _mm_shuffle_epi8(ascii_second, swap); - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output), ascii_first); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output + 8), ascii_second); - utf16_output += 16; // We wrote 16 16-bit characters. - return 16; // We consumed 16 bytes. - } - if(((utf8_end_of_code_point_mask & 0xFFFF) == 0xaaaa)) { - // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte UTF-16 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - if (big_endian) composed = _mm_shuffle_epi8(composed, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed); - utf16_output += 8; // We wrote 16 bytes, 8 code points. - return 16; - } - if(input_utf8_end_of_code_point_mask == 0x924) { - // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte UTF-16 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - __m128i composed_repacked = _mm_packus_epi32(composed, composed); - if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed_repacked); - utf16_output += 4; - return 12; - } - /// We do not have a fast path available, so we fallback. - - const uint8_t idx = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - if (idx < 64) { - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. On processors - // where pdep/pext is fast, we might be able to use a small lookup table. - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - if (big_endian) composed = _mm_shuffle_epi8(composed, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed); - utf16_output += 6; // We wrote 12 bytes, 6 code points. - } else if (idx < 145) { - // FOUR (4) input code-code units - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - __m128i composed_repacked = _mm_packus_epi32(composed, composed); - if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap); - _mm_storeu_si128((__m128i *)utf16_output, composed_repacked); - utf16_output += 4; - } else if (idx < 209) { - // TWO (2) input code-code units - ////////////// - // There might be garbage inputs where a leading byte mascarades as a four-byte - // leading byte (by being followed by 3 continuation byte), but is not greater than - // 0xf0. This could trigger a buffer overflow if we only counted leading - // bytes of the form 0xf0 as generating surrogate pairs, without further UTF-8 validation. - // Thus we must be careful to ensure that only leading bytes at least as large as 0xf0 generate surrogate pairs. - // We do as at the cost of an extra mask. - ///////////// - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f)); - const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000)); - // correct for spurious high bit - const __m128i correct = - _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1); - middlehighbyte = _mm_xor_si128(correct, middlehighbyte); - const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4); - // We deliberately carry the leading four bits in highbyte if they are present, - // we remove them later when computing hightenbits. - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000)); - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6); - // When we need to generate a surrogate pair (leading byte > 0xF0), then - // the corresponding 32-bit value in 'composed' will be greater than - // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the - // location of the surrogate pairs. - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), - _mm_or_si128(highbyte_shifted, middlehighbyte_shifted)); - const __m128i composedminus = - _mm_sub_epi32(composed, _mm_set1_epi32(0x10000)); - const __m128i lowtenbits = - _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff)); - // Notice the 0x3ff mask: - const __m128i hightenbits = _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff)); - const __m128i lowtenbitsadd = - _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00)); - const __m128i hightenbitsadd = - _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800)); - const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16); - __m128i surrogates = - _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted); - uint32_t basic_buffer[4]; - uint32_t basic_buffer_swap[4]; - if (big_endian) { - _mm_storeu_si128((__m128i *)basic_buffer_swap, _mm_shuffle_epi8(composed, swap)); - surrogates = _mm_shuffle_epi8(surrogates, swap); - } - _mm_storeu_si128((__m128i *)basic_buffer, composed); - uint32_t surrogate_buffer[4]; - _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates); - for (size_t i = 0; i < 3; i++) { - if(basic_buffer[i] > 0x3c00000) { - utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff); - utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16); - utf16_output += 2; - } else { - utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i]) : uint16_t(basic_buffer[i]); - utf16_output++; - } - } - } else { - // here we know that there is an error but we do not handle errors - } - return consumed; -} -/* end file src/westmere/sse_convert_utf8_to_utf16.cpp */ -/* begin file src/westmere/sse_convert_utf8_to_utf32.cpp */ -// depends on "tables/utf8_to_utf16_tables.h" - - -// Convert up to 12 bytes from utf8 to utf32 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -size_t convert_masked_utf8_to_utf32(const char *input, - uint64_t utf8_end_of_code_point_mask, - char32_t *&utf32_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - // We first try a few fast paths. - const __m128i in = _mm_loadu_si128((__m128i *)input); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; - if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) { - // We process the data in chunks of 16 bytes. - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu8_epi32(in)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu8_epi32(_mm_srli_si128(in,4))); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+8), _mm_cvtepu8_epi32(_mm_srli_si128(in,8))); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+12), _mm_cvtepu8_epi32(_mm_srli_si128(in,12))); - utf32_output += 16; // We wrote 16 32-bit characters. - return 16; // We consumed 16 bytes. - } - if(((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) { - // We want to take 8 2-byte UTF-8 code units and turn them into 8 4-byte UTF-32 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(composed)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(composed,8))); - utf32_output += 8; // We wrote 32 bytes, 8 code points. - return 16; - } - if(input_utf8_end_of_code_point_mask == 0x924) { - // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte UTF-32 code units. - // There is probably a more efficient sequence, but the following might do. - const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - _mm_storeu_si128((__m128i *)utf32_output, composed); - utf32_output += 4; - return 12; - } - /// We do not have a fast path available, so we fallback. - - const uint8_t idx = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - if (idx < 64) { - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. On processors - // where pdep/pext is fast, we might be able to use a small lookup table. - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(composed)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(composed,8))); - utf32_output += 6; // We wrote 12 bytes, 6 code points. - } else if (idx < 145) { - // FOUR (4) input code-code units - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = - _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits - const __m128i middlebyte = - _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - const __m128i highbyte = - _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted); - _mm_storeu_si128((__m128i *)utf32_output, composed); - utf32_output += 4; - } else if (idx < 209) { - // TWO (2) input code-code units - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f)); - const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); - const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2); - __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000)); - // correct for spurious high bit - const __m128i correct = - _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1); - middlehighbyte = _mm_xor_si128(correct, middlehighbyte); - const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000)); - const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6); - const __m128i composed = - _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), - _mm_or_si128(highbyte_shifted, middlehighbyte_shifted)); - _mm_storeu_si128((__m128i *)utf32_output, composed); - utf32_output += 3; - } else { - // here we know that there is an error but we do not handle errors - } - return consumed; -} -/* end file src/westmere/sse_convert_utf8_to_utf32.cpp */ -/* begin file src/westmere/sse_convert_utf8_to_latin1.cpp */ -// depends on "tables/utf8_to_utf16_tables.h" - - -// Convert up to 12 bytes from utf8 to latin1 using a mask indicating the -// end of the code points. Only the least significant 12 bits of the mask -// are accessed. -// It returns how many bytes were consumed (up to 12). -size_t convert_masked_utf8_to_latin1(const char *input, - uint64_t utf8_end_of_code_point_mask, - char *&latin1_output) { - // we use an approach where we try to process up to 12 input bytes. - // Why 12 input bytes and not 16? Because we are concerned with the size of - // the lookup tables. Also 12 is nicely divisible by two and three. - // - // - // Optimization note: our main path below is load-latency dependent. Thus it is maybe - // beneficial to have fast paths that depend on branch prediction but have less latency. - // This results in more instructions but, potentially, also higher speeds. - // - const __m128i in = _mm_loadu_si128((__m128i *)input); - const uint16_t input_utf8_end_of_code_point_mask = - utf8_end_of_code_point_mask & 0xfff; //we're only processing 12 bytes in case it`s not all ASCII - if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) { - // We process the data in chunks of 16 bytes. - _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in); - latin1_output += 16; // We wrote 16 characters. - return 16; // We consumed 16 bytes. - } - /// We do not have a fast path available, so we fallback. - const uint8_t idx = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0]; - const uint8_t consumed = - tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1]; - // this indicates an invalid input: - if(idx >= 64) { return consumed; } - // Here we should have (idx < 64), if not, there is a bug in the validation or elsewhere. - // SIX (6) input code-code units - // this is a relatively easy scenario - // we process SIX (6) input code-code units. The max length in bytes of six code - // code units spanning between 1 and 2 bytes each is 12 bytes. On processors - // where pdep/pext is fast, we might be able to use a small lookup table. - const __m128i sh = - _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]); - const __m128i perm = _mm_shuffle_epi8(in, sh); - const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f)); - const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00)); - __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2)); - const __m128i latin1_packed = _mm_packus_epi16(composed,composed); - // writing 8 bytes even though we only care about the first 6 bytes. - // performance note: it would be faster to use _mm_storeu_si128, we should investigate. - _mm_storel_epi64((__m128i *)latin1_output, latin1_packed); - latin1_output += 6; // We wrote 6 bytes. - return consumed; -} -/* end file src/westmere/sse_convert_utf8_to_latin1.cpp */ - -/* begin file src/westmere/sse_convert_utf16_to_latin1.cpp */ -template -std::pair sse_convert_utf16_to_latin1(const char16_t* buf, size_t len, char* latin1_output) { - const char16_t* end = buf + len; - while (buf + 8 <= end) { - // Load 8 UTF-16 characters into 128-bit SSE register - __m128i in = _mm_loadu_si128(reinterpret_cast(buf)); - - if (!match_system(big_endian)) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - in = _mm_shuffle_epi8(in, swap); - } - - __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00); - if (_mm_testz_si128(in, high_byte_mask)) { - // Pack 16-bit characters into 8-bit and store in latin1_output - __m128i latin1_packed = _mm_packus_epi16(in, in); - _mm_storel_epi64(reinterpret_cast<__m128i*>(latin1_output), latin1_packed); - // Adjust pointers for next iteration - buf += 8; - latin1_output += 8; - } else { - return std::make_pair(nullptr, reinterpret_cast(latin1_output)); - } - } // while - return std::make_pair(buf, latin1_output); -} - -template -std::pair sse_convert_utf16_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) { - const char16_t* start = buf; - const char16_t* end = buf + len; - while (buf + 8 <= end) { - __m128i in = _mm_loadu_si128(reinterpret_cast(buf)); - - if (!big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - in = _mm_shuffle_epi8(in, swap); - } - - __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00); - if (_mm_testz_si128(in, high_byte_mask)) { - __m128i latin1_packed = _mm_packus_epi16(in, in); - _mm_storel_epi64(reinterpret_cast<__m128i*>(latin1_output), latin1_packed); - buf += 8; - latin1_output += 8; - } else { - // Fallback to scalar code for handling errors - for(int k = 0; k < 8; k++) { - uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if(word <= 0xff) { - *latin1_output++ = char(word); - } else { - return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), latin1_output); - } - } - buf += 8; - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output); -} -/* end file src/westmere/sse_convert_utf16_to_latin1.cpp */ -/* begin file src/westmere/sse_convert_utf16_to_utf8.cpp */ -/* - The vectorized algorithm works on single SSE register i.e., it - loads eight 16-bit code units. - - We consider three cases: - 1. an input register contains no surrogates and each value - is in range 0x0000 .. 0x07ff. - 2. an input register contains no surrogates and values are - is in range 0x0000 .. 0xffff. - 3. an input register contains surrogates --- i.e. codepoints - can have 16 or 32 bits. - - Ad 1. - - When values are less than 0x0800, it means that a 16-bit code unit - can be converted into: 1) single UTF8 byte (when it's an ASCII - char) or 2) two UTF8 bytes. - - For this case we do only some shuffle to obtain these 2-byte - codes and finally compress the whole SSE register with a single - shuffle. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - Ad 2. - - When values fit in 16-bit code units, but are above 0x07ff, then - a single word may produce one, two or three UTF8 bytes. - - We prepare data for all these three cases in two registers. - The first register contains lower two UTF8 bytes (used in all - cases), while the second one contains just the third byte for - the three-UTF8-bytes case. - - Finally these two registers are interleaved forming eight-element - array of 32-bit values. The array spans two SSE registers. - The bytes from the registers are compressed using two shuffles. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - - To summarize: - - We need two 256-entry tables that have 8704 bytes in total. -*/ - -/* - Returns a pair: the first unprocessed byte from buf and utf8_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::pair sse_convert_utf16_to_utf8(const char16_t* buf, size_t len, char* utf8_output) { - - const char16_t* end = buf + len; - - const __m128i v_0000 = _mm_setzero_si128(); - const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800); - const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - in = _mm_shuffle_epi8(in, swap); - } - // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes - const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80); - if(_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!! - __m128i nextin = _mm_loadu_si128((__m128i*)buf+1); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - nextin = _mm_shuffle_epi8(nextin, swap); - } - if(!_mm_testz_si128(nextin, v_ff80)) { - // 1. pack the bytes - // obviously suboptimal. - const __m128i utf8_packed = _mm_packus_epi16(in,in); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - in = nextin; - } else { - // 1. pack the bytes - // obviously suboptimal. - const __m128i utf8_packed = _mm_packus_epi16(in,nextin); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - } - - // no bits set above 7th bit - const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000); - const uint16_t one_byte_bitmask = static_cast(_mm_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000); - const uint16_t one_or_two_bytes_bitmask = static_cast(_mm_movemask_epi8(one_or_two_bytes_bytemask)); - - if (one_or_two_bytes_bitmask == 0xffff) { - internal::westmere::write_v_u16_11bits_to_utf8(in, utf8_output, one_byte_bytemask, one_byte_bitmask); - buf += 8; - continue; - } - - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint16_t surrogates_bitmask = static_cast(_mm_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x0000) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m128i t0 = _mm_shuffle_epi8(in, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m128i s0 = _mm_srli_epi16(in, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000)); - const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m128i s4 = _mm_xor_si128(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m128i out0 = _mm_unpacklo_epi16(t2, s4); - const __m128i out1 = _mm_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16_t mask = (one_byte_bitmask & 0x5555) | - (one_or_two_bytes_bitmask & 0xaaaa); - if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - } - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word & 0xFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xF800 ) != 0xD800) { - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf8_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(buf, utf8_output); -} - - -/* - Returns a pair: a result struct and utf8_output. - If there is an error, the count field of the result is the position of the error. - Otherwise, it is the position of the first unprocessed byte in buf (even if finished). - A scalar routing should carry on the conversion of the tail if needed. -*/ -template -std::pair sse_convert_utf16_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) { - const char16_t* start = buf; - const char16_t* end = buf + len; - - const __m128i v_0000 = _mm_setzero_si128(); - const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800); - const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - in = _mm_shuffle_epi8(in, swap); - } - // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes - const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80); - if(_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!! - __m128i nextin = _mm_loadu_si128((__m128i*)buf+1); - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - nextin = _mm_shuffle_epi8(nextin, swap); - } - if(!_mm_testz_si128(nextin, v_ff80)) { - // 1. pack the bytes - // obviously suboptimal. - const __m128i utf8_packed = _mm_packus_epi16(in,in); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - in = nextin; - } else { - // 1. pack the bytes - // obviously suboptimal. - const __m128i utf8_packed = _mm_packus_epi16(in,nextin); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - } - - // no bits set above 7th bit - const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000); - const uint16_t one_byte_bitmask = static_cast(_mm_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000); - const uint16_t one_or_two_bytes_bitmask = static_cast(_mm_movemask_epi8(one_or_two_bytes_bytemask)); - - if (one_or_two_bytes_bitmask == 0xffff) { - internal::westmere::write_v_u16_11bits_to_utf8(in, utf8_output, one_byte_bytemask, one_byte_bitmask); - buf += 8; - continue; - } - - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint16_t surrogates_bitmask = static_cast(_mm_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x0000) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m128i t0 = _mm_shuffle_epi8(in, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m128i s0 = _mm_srli_epi16(in, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000)); - const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m128i s4 = _mm_xor_si128(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m128i out0 = _mm_unpacklo_epi16(t2, s4); - const __m128i out1 = _mm_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16_t mask = (one_byte_bitmask & 0x5555) | - (one_or_two_bytes_bitmask & 0xaaaa); - if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - } - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word & 0xFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xF800 ) != 0xD800) { - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf8_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf8_output++ = char((value>>18) | 0b11110000); - *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((value & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output); -} -/* end file src/westmere/sse_convert_utf16_to_utf8.cpp */ -/* begin file src/westmere/sse_convert_utf16_to_utf32.cpp */ -/* - The vectorized algorithm works on single SSE register i.e., it - loads eight 16-bit code units. - - We consider three cases: - 1. an input register contains no surrogates and each value - is in range 0x0000 .. 0x07ff. - 2. an input register contains no surrogates and values are - is in range 0x0000 .. 0xffff. - 3. an input register contains surrogates --- i.e. codepoints - can have 16 or 32 bits. - - Ad 1. - - When values are less than 0x0800, it means that a 16-bit code unit - can be converted into: 1) single UTF8 byte (when it's an ASCII - char) or 2) two UTF8 bytes. - - For this case we do only some shuffle to obtain these 2-byte - codes and finally compress the whole SSE register with a single - shuffle. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - Ad 2. - - When values fit in 16-bit code units, but are above 0x07ff, then - a single word may produce one, two or three UTF8 bytes. - - We prepare data for all these three cases in two registers. - The first register contains lower two UTF8 bytes (used in all - cases), while the second one contains just the third byte for - the three-UTF8-bytes case. - - Finally these two registers are interleaved forming eight-element - array of 32-bit values. The array spans two SSE registers. - The bytes from the registers are compressed using two shuffles. - - We need 256-entry lookup table to get a compression pattern - and the number of output bytes in the compressed vector register. - Each entry occupies 17 bytes. - - - To summarize: - - We need two 256-entry tables that have 8704 bytes in total. -*/ - -/* - Returns a pair: the first unprocessed byte from buf and utf8_output - A scalar routing should carry on the conversion of the tail. -*/ -template -std::pair sse_convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) { - const char16_t* end = buf + len; - - const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800); - const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800); - - while (buf + 8 <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - in = _mm_shuffle_epi8(in, swap); - } - - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint16_t surrogates_bitmask = static_cast(_mm_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x0000) { - // case: no surrogate pair, extend 16-bit code units to 32-bit code units - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(in)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(in,8))); - utf32_output += 8; - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word &0xF800 ) != 0xD800) { - *utf32_output++ = char32_t(word); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf32_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - } - } - buf += k; - } - } // while - return std::make_pair(buf, utf32_output); -} - - -/* - Returns a pair: a result struct and utf8_output. - If there is an error, the count field of the result is the position of the error. - Otherwise, it is the position of the first unprocessed byte in buf (even if finished). - A scalar routing should carry on the conversion of the tail if needed. -*/ -template -std::pair sse_convert_utf16_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) { - const char16_t* start = buf; - const char16_t* end = buf + len; - - const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800); - const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800); - - while (buf + 8 <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - in = _mm_shuffle_epi8(in, swap); - } - - // 1. Check if there are any surrogate word in the input chunk. - // We have also deal with situation when there is a surrogate word - // at the end of a chunk. - const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800); - - // bitmask = 0x0000 if there are no surrogates - // = 0xc000 if the last word is a surrogate - const uint16_t surrogates_bitmask = static_cast(_mm_movemask_epi8(surrogates_bytemask)); - // It might seem like checking for surrogates_bitmask == 0xc000 could help. However, - // it is likely an uncommon occurrence. - if (surrogates_bitmask == 0x0000) { - // case: no surrogate pair, extend 16-bit code units to 32-bit code units - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(in)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(in,8))); - utf32_output += 8; - buf += 8; - // surrogate pair(s) in a register - } else { - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k]; - if((word &0xF800 ) != 0xD800) { - *utf32_output++ = char32_t(word); - } else { - // must be a surrogate pair - uint16_t diff = uint16_t(word - 0xD800); - uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1]; - k++; - uint16_t diff2 = uint16_t(next_word - 0xDC00); - if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf32_output); } - uint32_t value = (diff << 10) + diff2 + 0x10000; - *utf32_output++ = char32_t(value); - } - } - buf += k; - } - } // while - return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output); -} -/* end file src/westmere/sse_convert_utf16_to_utf32.cpp */ - -/* begin file src/westmere/sse_convert_utf32_to_latin1.cpp */ -std::pair -sse_convert_utf32_to_latin1(const char32_t *buf, size_t len, - char *latin1_output) { - const size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16 - - __m128i high_bytes_mask = _mm_set1_epi32(0xFFFFFF00); - __m128i shufmask = - _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 8, 4, 0); - - for (size_t i = 0; i < rounded_len; i += 16) { - __m128i in1 = _mm_loadu_si128((__m128i *)buf); - __m128i in2 = _mm_loadu_si128((__m128i *)(buf + 4)); - __m128i in3 = _mm_loadu_si128((__m128i *)(buf + 8)); - __m128i in4 = _mm_loadu_si128((__m128i *)(buf + 12)); - - __m128i check_combined = _mm_or_si128(in1, in2); - check_combined = _mm_or_si128(check_combined, in3); - check_combined = _mm_or_si128(check_combined, in4); - - if (!_mm_testz_si128(check_combined, high_bytes_mask)) { - return std::make_pair(nullptr, latin1_output); - } - __m128i pack1 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in1, shufmask), _mm_shuffle_epi8(in2, shufmask)); - __m128i pack2 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in3, shufmask), _mm_shuffle_epi8(in4, shufmask)); - __m128i pack = _mm_unpacklo_epi64(pack1, pack2); - _mm_storeu_si128((__m128i *)latin1_output, pack); - latin1_output += 16; - buf += 16; - } - - return std::make_pair(buf, latin1_output); -} - -std::pair -sse_convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len, - char *latin1_output) { - const char32_t *start = buf; - const size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16 - - __m128i high_bytes_mask = _mm_set1_epi32(0xFFFFFF00); - __m128i shufmask = - _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 8, 4, 0); - - for (size_t i = 0; i < rounded_len; i += 16) { - __m128i in1 = _mm_loadu_si128((__m128i *)buf); - __m128i in2 = _mm_loadu_si128((__m128i *)(buf + 4)); - __m128i in3 = _mm_loadu_si128((__m128i *)(buf + 8)); - __m128i in4 = _mm_loadu_si128((__m128i *)(buf + 12)); - - __m128i check_combined = _mm_or_si128(in1, in2); - check_combined = _mm_or_si128(check_combined, in3); - check_combined = _mm_or_si128(check_combined, in4); - - if (!_mm_testz_si128(check_combined, high_bytes_mask)) { - // Fallback to scalar code for handling errors - for (int k = 0; k < 16; k++) { - char32_t codepoint = buf[k]; - if (codepoint <= 0xff) { - *latin1_output++ = char(codepoint); - } else { - return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), - latin1_output); - } - } - buf += 16; - continue; - } - __m128i pack1 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in1, shufmask), _mm_shuffle_epi8(in2, shufmask)); - __m128i pack2 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in3, shufmask), _mm_shuffle_epi8(in4, shufmask)); - __m128i pack = _mm_unpacklo_epi64(pack1, pack2); - _mm_storeu_si128((__m128i *)latin1_output, pack); - latin1_output += 16; - buf += 16; - } - - return std::make_pair(result(error_code::SUCCESS, buf - start), - latin1_output); -} -/* end file src/westmere/sse_convert_utf32_to_latin1.cpp */ -/* begin file src/westmere/sse_convert_utf32_to_utf8.cpp */ -std::pair sse_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { - const char32_t* end = buf + len; - - const __m128i v_0000 = _mm_setzero_si128();//__m128 = 128 bits - const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); //1111 1000 0000 0000 - const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080); //1100 0000 1000 0000 - const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80); //1111 1111 1000 0000 - const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000); //1111 1111 1111 1111 0000 0000 0000 0000 - const __m128i v_7fffffff = _mm_set1_epi32((uint32_t)0x7fffffff); //0111 1111 1111 1111 1111 1111 1111 1111 - __m128i running_max = _mm_setzero_si128(); - __m128i forbidden_bytemask = _mm_setzero_si128(); - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { //buf is a char32_t pointer, each char32_t has 4 bytes or 32 bits, thus buf + 16 * char_32t = 512 bits = 64 bytes - // We load two 16 bytes registers for a total of 32 bytes or 16 characters. - __m128i in = _mm_loadu_si128((__m128i*)buf); - __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);//These two values can hold only 8 UTF32 chars - running_max = _mm_max_epu32( - _mm_max_epu32(in, running_max), //take element-wise max char32_t from in and running_max vector - nextin); //and take element-wise max element from nextin and running_max vector - - // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation - __m128i in_16 = _mm_packus_epi32( - _mm_and_si128(in, v_7fffffff), - _mm_and_si128(nextin, v_7fffffff) - );//in this context pack the two __m128 into a single - //By ensuring the highest bit is set to 0(&v_7fffffff), we're making sure all values are interpreted as non-negative, or specifically, the values are within the range of valid Unicode code points. - //remember : having leading byte 0 means a positive number by the two complements system. Unicode is well beneath the range where you'll start getting issues so that's OK. - - // Try to apply UTF-16 => UTF-8 from ./sse_convert_utf16_to_utf8.cpp - - // Check for ASCII fast path - - // ASCII fast path!!!! - // We eagerly load another 32 bytes, hoping that they will be ASCII too. - // The intuition is that we try to collect 16 ASCII characters which requires - // a total of 64 bytes of input. If we fail, we just pass thirdin and fourthin - // as our new inputs. - if(_mm_testz_si128(in_16, v_ff80)) { //if the first two blocks are ASCII - __m128i thirdin = _mm_loadu_si128((__m128i*)buf+2); - __m128i fourthin = _mm_loadu_si128((__m128i*)buf+3); - running_max = _mm_max_epu32(_mm_max_epu32(thirdin, running_max), fourthin);//take the running max of all 4 vectors thus far - __m128i nextin_16 = _mm_packus_epi32(_mm_and_si128(thirdin, v_7fffffff), _mm_and_si128(fourthin, v_7fffffff));//pack into 1 vector, now you have two - if(!_mm_testz_si128(nextin_16, v_ff80)) { //checks if the second packed vector is ASCII, if not: - // 1. pack the bytes - // obviously suboptimal. - const __m128i utf8_packed = _mm_packus_epi16(in_16,in_16); //creates two copy of in_16 in 1 vector - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); //put them into the output - // 3. adjust pointers - buf += 8; //the char32_t buffer pointer goes up 8 char32_t chars* 32 bits = 256 bits - utf8_output += 8; //same with output, e.g. lift the first two blocks alone. - // Proceed with next input - in_16 = nextin_16; - // We need to update in and nextin because they are used later. - in = thirdin; - nextin = fourthin; - } else { - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(in_16, nextin_16); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - } - - // no bits set above 7th bit -- find out all the ASCII characters - const __m128i one_byte_bytemask = _mm_cmpeq_epi16( // this takes four bytes at a time and compares: - _mm_and_si128(in_16, v_ff80), // the vector that get only the first 9 bits of each 16-bit/2-byte units - v_0000 // - ); // they should be all zero if they are ASCII. E.g. ASCII in UTF32 is of format 0000 0000 0000 0XXX XXXX - // _mm_cmpeq_epi16 should now return a 1111 1111 1111 1111 for equals, and 0000 0000 0000 0000 if not for each 16-bit/2-byte units - const uint16_t one_byte_bitmask = static_cast(_mm_movemask_epi8(one_byte_bytemask)); // collect the MSB from previous vector and put them into uint16_t mas - - // no bits set above 11th bit - const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000); - const uint16_t one_or_two_bytes_bitmask = static_cast(_mm_movemask_epi8(one_or_two_bytes_bytemask)); - - if (one_or_two_bytes_bitmask == 0xffff) { - // case: all code units either produce 1 or 2 UTF-8 bytes (at least one produces 2 bytes) - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00); // 0001 1111 0000 0000 - const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f); // 0000 0000 0011 1111 - - // t0 = [000a|aaaa|bbbb|bb00] - const __m128i t0 = _mm_slli_epi16(in_16, 2); // shift packed vector by two - // t1 = [000a|aaaa|0000|0000] - const __m128i t1 = _mm_and_si128(t0, v_1f00); // potentital first utf8 byte - // t2 = [0000|0000|00bb|bbbb] - const __m128i t2 = _mm_and_si128(in_16, v_003f);// potential second utf8 byte - // t3 = [000a|aaaa|00bb|bbbb] - const __m128i t3 = _mm_or_si128(t1, t2); // first and second potential utf8 byte together - // t4 = [110a|aaaa|10bb|bbbb] - const __m128i t4 = _mm_or_si128(t3, v_c080); // t3 | 1100 0000 1000 0000 = full potential 2-byte utf8 unit - - // 2. merge ASCII and 2-byte codewords - const __m128i utf8_unpacked = _mm_blendv_epi8(t4, in_16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - // one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a - LSB) - const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a - const uint16_t m1 = static_cast(m0 >> 7); // m1 = 00000000h0g0f0e0 - const uint8_t m2 = static_cast((m0 | m1) & 0xff); // m2 = hdgcfbea - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle); - - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - - // 6. adjust pointers - buf += 8; - utf8_output += row[0]; - continue; - } - - // Check for overflow in packing - - const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm_movemask_epi8(saturation_bytemask)); - if (saturation_bitmask == 0xffff) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800); - forbidden_bytemask = _mm_or_si128(forbidden_bytemask, _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800)); - - const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m128i s0 = _mm_srli_epi16(in_16, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000)); - const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m128i s4 = _mm_xor_si128(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m128i out0 = _mm_unpacklo_epi16(t2, s4); - const __m128i out1 = _mm_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16_t mask = (one_byte_bitmask & 0x5555) | - (one_or_two_bytes_bitmask & 0xaaaa); - if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - } - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - } else { - // case: at least one 32-bit word produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xFFFF0000 )==0) { - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf8_output); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - if (word > 0x10FFFF) { return std::make_pair(nullptr, utf8_output); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - // check for invalid input - const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff); - if(static_cast(_mm_movemask_epi8(_mm_cmpeq_epi32(_mm_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffff) { - return std::make_pair(nullptr, utf8_output); - } - - if (static_cast(_mm_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf8_output); } - - return std::make_pair(buf, utf8_output); -} - - -std::pair sse_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) { - - const char32_t* end = buf + len; - const char32_t* start = buf; - - const __m128i v_0000 = _mm_setzero_si128(); - const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); - const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080); - const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80); - const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000); - const __m128i v_7fffffff = _mm_set1_epi32((uint32_t)0x7fffffff); - const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff); - - const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92 - - while (buf + 16 + safety_margin <= end) { - // We load two 16 bytes registers for a total of 32 bytes or 16 characters. - __m128i in = _mm_loadu_si128((__m128i*)buf); - __m128i nextin = _mm_loadu_si128((__m128i*)buf+1); - - // Check for too large input - __m128i max_input = _mm_max_epu32(_mm_max_epu32(in, nextin), v_10ffff); - if(static_cast(_mm_movemask_epi8(_mm_cmpeq_epi32(max_input, v_10ffff))) != 0xffff) { - return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output); - } - - // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation - __m128i in_16 = _mm_packus_epi32(_mm_and_si128(in, v_7fffffff), _mm_and_si128(nextin, v_7fffffff)); - - // Try to apply UTF-16 => UTF-8 from ./sse_convert_utf16_to_utf8.cpp - - // Check for ASCII fast path - if(_mm_testz_si128(in_16, v_ff80)) { // ASCII fast path!!!! - // We eagerly load another 32 bytes, hoping that they will be ASCII too. - // The intuition is that we try to collect 16 ASCII characters which requires - // a total of 64 bytes of input. If we fail, we just pass thirdin and fourthin - // as our new inputs. - __m128i thirdin = _mm_loadu_si128((__m128i*)buf+2); - __m128i fourthin = _mm_loadu_si128((__m128i*)buf+3); - __m128i nextin_16 = _mm_packus_epi32(_mm_and_si128(thirdin, v_7fffffff), _mm_and_si128(fourthin, v_7fffffff)); - if(!_mm_testz_si128(nextin_16, v_ff80)) { - // 1. pack the bytes - // obviously suboptimal. - const __m128i utf8_packed = _mm_packus_epi16(in_16,in_16); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 8; - utf8_output += 8; - // Proceed with next input - in_16 = nextin_16; - __m128i next_max_input = _mm_max_epu32(_mm_max_epu32(thirdin, fourthin), v_10ffff); - if(static_cast(_mm_movemask_epi8(_mm_cmpeq_epi32(next_max_input, v_10ffff))) != 0xffff) { - return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output); - } - // We need to update in and nextin because they are used later. - in = thirdin; - nextin = fourthin; - } else { - // 1. pack the bytes - const __m128i utf8_packed = _mm_packus_epi16(in_16, nextin_16); - // 2. store (16 bytes) - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - // 3. adjust pointers - buf += 16; - utf8_output += 16; - continue; // we are done for this round! - } - } - - // no bits set above 7th bit - const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_ff80), v_0000); - const uint16_t one_byte_bitmask = static_cast(_mm_movemask_epi8(one_byte_bytemask)); - - // no bits set above 11th bit - const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000); - const uint16_t one_or_two_bytes_bitmask = static_cast(_mm_movemask_epi8(one_or_two_bytes_bytemask)); - - if (one_or_two_bytes_bitmask == 0xffff) { - // case: all code units either produce 1 or 2 UTF-8 bytes (at least one produces 2 bytes) - // 1. prepare 2-byte values - // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8 - // expected output : [110a|aaaa|10bb|bbbb] x 8 - const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00); - const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f); - - // t0 = [000a|aaaa|bbbb|bb00] - const __m128i t0 = _mm_slli_epi16(in_16, 2); - // t1 = [000a|aaaa|0000|0000] - const __m128i t1 = _mm_and_si128(t0, v_1f00); - // t2 = [0000|0000|00bb|bbbb] - const __m128i t2 = _mm_and_si128(in_16, v_003f); - // t3 = [000a|aaaa|00bb|bbbb] - const __m128i t3 = _mm_or_si128(t1, t2); - // t4 = [110a|aaaa|10bb|bbbb] - const __m128i t4 = _mm_or_si128(t3, v_c080); - - // 2. merge ASCII and 2-byte codewords - const __m128i utf8_unpacked = _mm_blendv_epi8(t4, in_16, one_byte_bytemask); - - // 3. prepare bitmask for 8-bit lookup - // one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a - LSB) - const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a - const uint16_t m1 = static_cast(m0 >> 7); // m1 = 00000000h0g0f0e0 - const uint8_t m2 = static_cast((m0 | m1) & 0xff); // m2 = hdgcfbea - // 4. pack the bytes - const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0]; - const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1)); - const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle); - - // 5. store bytes - _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); - - // 6. adjust pointers - buf += 8; - utf8_output += row[0]; - continue; - } - - - // Check for overflow in packing - const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm_movemask_epi8(saturation_bytemask)); - - if (saturation_bitmask == 0xffff) { - // case: code units from register produce either 1, 2 or 3 UTF-8 bytes - - // Check for illegal surrogate code units - const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800); - const __m128i forbidden_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800); - if (static_cast(_mm_movemask_epi8(forbidden_bytemask)) != 0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), utf8_output); - } - - const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606, - 0x0808, 0x0a0a, 0x0c0c, 0x0e0e); - - /* In this branch we handle three cases: - 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte - 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes - 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes - - We expand the input word (16-bit) into two code units (32-bit), thus - we have room for four bytes. However, we need five distinct bit - layouts. Note that the last byte in cases #2 and #3 is the same. - - We precompute byte 1 for case #1 and the common byte for cases #2 & #3 - in register t2. - - We precompute byte 1 for case #3 and -- **conditionally** -- precompute - either byte 1 for case #2 or byte 2 for case #3. Note that they - differ by exactly one bit. - - Finally from these two code units we build proper UTF-8 sequence, taking - into account the case (i.e, the number of bytes to write). - */ - /** - * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: - * t2 => [0ccc|cccc] [10cc|cccc] - * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) - */ -#define simdutf_vec(x) _mm_set1_epi16(static_cast(x)) - // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] - const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even); - // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] - const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111)); - // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] - const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000)); - - // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc] - const __m128i s0 = _mm_srli_epi16(in_16, 4); - // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00] - const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100)); - // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa] - const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140)); - // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa] - const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000)); - const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000)); - const __m128i s4 = _mm_xor_si128(s3, m0); -#undef simdutf_vec - - // 4. expand code units 16-bit => 32-bit - const __m128i out0 = _mm_unpacklo_epi16(t2, s4); - const __m128i out1 = _mm_unpackhi_epi16(t2, s4); - - // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle - const uint16_t mask = (one_byte_bitmask & 0x5555) | - (one_or_two_bytes_bitmask & 0xaaaa); - if(mask == 0) { - // We only have three-byte code units. Use fast path. - const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle); - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += 12; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += 12; - buf += 8; - continue; - } - const uint8_t mask0 = uint8_t(mask); - - const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; - const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1)); - const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0); - - const uint8_t mask1 = static_cast(mask >> 8); - - const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; - const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1)); - const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1); - - _mm_storeu_si128((__m128i*)utf8_output, utf8_0); - utf8_output += row0[0]; - _mm_storeu_si128((__m128i*)utf8_output, utf8_1); - utf8_output += row1[0]; - - buf += 8; - } else { - // case: at least one 32-bit word produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes - // Let us do a scalar fallback. - // It may seem wasteful to use scalar code, but being efficient with SIMD - // in the presence of surrogate pairs may require non-trivial tables. - size_t forward = 15; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFFFF80)==0) { - *utf8_output++ = char(word); - } else if((word & 0xFFFFF800)==0) { - *utf8_output++ = char((word>>6) | 0b11000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else if((word &0xFFFF0000 )==0) { - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf8_output); } - *utf8_output++ = char((word>>12) | 0b11100000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } else { - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf- start + k), utf8_output); } - *utf8_output++ = char((word>>18) | 0b11110000); - *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000); - *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000); - *utf8_output++ = char((word & 0b111111) | 0b10000000); - } - } - buf += k; - } - } // while - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output); -} -/* end file src/westmere/sse_convert_utf32_to_utf8.cpp */ -/* begin file src/westmere/sse_convert_utf32_to_utf16.cpp */ -template -std::pair sse_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_output) { - - const char32_t* end = buf + len; - - const __m128i v_0000 = _mm_setzero_si128(); - const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000); - __m128i forbidden_bytemask = _mm_setzero_si128(); - - while (buf + 8 <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - __m128i nextin = _mm_loadu_si128((__m128i*)buf+1); - const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm_movemask_epi8(saturation_bytemask)); - - // Check if no bits set above 16th - if (saturation_bitmask == 0xffff) { - // Pack UTF-32 to UTF-16 - __m128i utf16_packed = _mm_packus_epi32(in, nextin); - - const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); - const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800); - forbidden_bytemask = _mm_or_si128(forbidden_bytemask, _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800)); - - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - utf16_packed = _mm_shuffle_epi8(utf16_packed, swap); - } - - _mm_storeu_si128((__m128i*)utf16_output, utf16_packed); - utf16_output += 8; - buf += 8; - } else { - size_t forward = 7; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf16_output); } - *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(nullptr, utf16_output); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (big_endian) { - high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8)); - low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8)); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - // check for invalid input - if (static_cast(_mm_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf16_output); } - - return std::make_pair(buf, utf16_output); -} - - -template -std::pair sse_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) { - const char32_t* start = buf; - const char32_t* end = buf + len; - - const __m128i v_0000 = _mm_setzero_si128(); - const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000); - - while (buf + 8 <= end) { - __m128i in = _mm_loadu_si128((__m128i*)buf); - __m128i nextin = _mm_loadu_si128((__m128i*)buf+1); - const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000); - const uint32_t saturation_bitmask = static_cast(_mm_movemask_epi8(saturation_bytemask)); - - // Check if no bits set above 16th - if (saturation_bitmask == 0xffff) { - // Pack UTF-32 to UTF-16 - __m128i utf16_packed = _mm_packus_epi32(in, nextin); - - const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); - const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800); - const __m128i forbidden_bytemask = _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800); - if (static_cast(_mm_movemask_epi8(forbidden_bytemask)) != 0) { - return std::make_pair(result(error_code::SURROGATE, buf - start), utf16_output); - } - - if (big_endian) { - const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - utf16_packed = _mm_shuffle_epi8(utf16_packed, swap); - } - - _mm_storeu_si128((__m128i*)utf16_output, utf16_packed); - utf16_output += 8; - buf += 8; - } else { - size_t forward = 7; - size_t k = 0; - if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);} - for(; k < forward; k++) { - uint32_t word = buf[k]; - if((word & 0xFFFF0000)==0) { - // will not generate a surrogate pair - if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf16_output); } - *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word); - } else { - // will generate a surrogate pair - if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf16_output); } - word -= 0x10000; - uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); - uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); - if (big_endian) { - high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8)); - low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8)); - } - *utf16_output++ = char16_t(high_surrogate); - *utf16_output++ = char16_t(low_surrogate); - } - } - buf += k; - } - } - - return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output); -} -/* end file src/westmere/sse_convert_utf32_to_utf16.cpp */ -/* begin file src/westmere/sse_base64.cpp */ -/** - * References and further reading: - * - * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the - * speed of a memory copy, Software: Practice and Experience 50 (2), 2020. - * https://arxiv.org/abs/1910.05109 - * - * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 - * Instructions, ACM Transactions on the Web 12 (3), 2018. - * https://arxiv.org/abs/1704.00605 - * - * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings. - * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force, - * Request for Comments: 4648. - * - * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization. - * http://www.alfredklomp.com/programming/sse-base64/. (2014). - * - * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD - * acceleration. https://github.com/aklomp/base64. (2014). - * - * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014). - * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/ - * - * Nick Kopp. 2013. Base64 Encoding on a GPU. - * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). - */ -template __m128i lookup_pshufb_improved(const __m128i input) { - // credit: Wojciech Muła - // reduce 0..51 -> 0 - // 52..61 -> 1 .. 10 - // 62 -> 11 - // 63 -> 12 - __m128i result = _mm_subs_epu8(input, _mm_set1_epi8(51)); - - // distinguish between ranges 0..25 and 26..51: - // 0 .. 25 -> remains 0 - // 26 .. 51 -> becomes 13 - const __m128i less = _mm_cmpgt_epi8(_mm_set1_epi8(26), input); - result = _mm_or_si128(result, _mm_and_si128(less, _mm_set1_epi8(13))); - - __m128i shift_LUT; - if (base64_url) { - shift_LUT = _mm_setr_epi8('a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0); - } else { - shift_LUT = _mm_setr_epi8('a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, - '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0); - } - - // read shift - result = _mm_shuffle_epi8(shift_LUT, result); - - return _mm_add_epi8(result, input); -} - -template -size_t encode_base64(char *dst, const char *src, size_t srclen) { - // credit: Wojciech Muła - // SSE (lookup: pshufb improved unrolled) - const uint8_t *input = (const uint8_t *)src; - - uint8_t *out = (uint8_t *)dst; - const __m128i shuf = - _mm_set_epi8(10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1); - - size_t i = 0; - for (; i + 52 <= srclen; i += 48) { - __m128i in0 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 0)); - __m128i in1 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 1)); - __m128i in2 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 2)); - __m128i in3 = _mm_loadu_si128( - reinterpret_cast(input + i + 4 * 3 * 3)); - - in0 = _mm_shuffle_epi8(in0, shuf); - in1 = _mm_shuffle_epi8(in1, shuf); - in2 = _mm_shuffle_epi8(in2, shuf); - in3 = _mm_shuffle_epi8(in3, shuf); - - const __m128i t0_0 = _mm_and_si128(in0, _mm_set1_epi32(0x0fc0fc00)); - const __m128i t0_1 = _mm_and_si128(in1, _mm_set1_epi32(0x0fc0fc00)); - const __m128i t0_2 = _mm_and_si128(in2, _mm_set1_epi32(0x0fc0fc00)); - const __m128i t0_3 = _mm_and_si128(in3, _mm_set1_epi32(0x0fc0fc00)); - - const __m128i t1_0 = _mm_mulhi_epu16(t0_0, _mm_set1_epi32(0x04000040)); - const __m128i t1_1 = _mm_mulhi_epu16(t0_1, _mm_set1_epi32(0x04000040)); - const __m128i t1_2 = _mm_mulhi_epu16(t0_2, _mm_set1_epi32(0x04000040)); - const __m128i t1_3 = _mm_mulhi_epu16(t0_3, _mm_set1_epi32(0x04000040)); - - const __m128i t2_0 = _mm_and_si128(in0, _mm_set1_epi32(0x003f03f0)); - const __m128i t2_1 = _mm_and_si128(in1, _mm_set1_epi32(0x003f03f0)); - const __m128i t2_2 = _mm_and_si128(in2, _mm_set1_epi32(0x003f03f0)); - const __m128i t2_3 = _mm_and_si128(in3, _mm_set1_epi32(0x003f03f0)); - - const __m128i t3_0 = _mm_mullo_epi16(t2_0, _mm_set1_epi32(0x01000010)); - const __m128i t3_1 = _mm_mullo_epi16(t2_1, _mm_set1_epi32(0x01000010)); - const __m128i t3_2 = _mm_mullo_epi16(t2_2, _mm_set1_epi32(0x01000010)); - const __m128i t3_3 = _mm_mullo_epi16(t2_3, _mm_set1_epi32(0x01000010)); - - const __m128i input0 = _mm_or_si128(t1_0, t3_0); - const __m128i input1 = _mm_or_si128(t1_1, t3_1); - const __m128i input2 = _mm_or_si128(t1_2, t3_2); - const __m128i input3 = _mm_or_si128(t1_3, t3_3); - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input0)); - out += 16; - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input1)); - out += 16; - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input2)); - out += 16; - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input3)); - out += 16; - } - for (; i + 16 <= srclen; i += 12) { - - __m128i in = _mm_loadu_si128(reinterpret_cast(input + i)); - - // bytes from groups A, B and C are needed in separate 32-bit lanes - // in = [DDDD|CCCC|BBBB|AAAA] - // - // an input triplet has layout - // [????????|ccdddddd|bbbbcccc|aaaaaabb] - // byte 3 byte 2 byte 1 byte 0 -- byte 3 comes from the next - // triplet - // - // shuffling changes the order of bytes: 1, 0, 2, 1 - // [bbbbcccc|ccdddddd|aaaaaabb|bbbbcccc] - // ^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^ - // processed bits - in = _mm_shuffle_epi8(in, shuf); - - // unpacking - - // t0 = [0000cccc|cc000000|aaaaaa00|00000000] - const __m128i t0 = _mm_and_si128(in, _mm_set1_epi32(0x0fc0fc00)); - // t1 = [00000000|00cccccc|00000000|00aaaaaa] - // (c * (1 << 10), a * (1 << 6)) >> 16 (note: an unsigned - // multiplication) - const __m128i t1 = _mm_mulhi_epu16(t0, _mm_set1_epi32(0x04000040)); - - // t2 = [00000000|00dddddd|000000bb|bbbb0000] - const __m128i t2 = _mm_and_si128(in, _mm_set1_epi32(0x003f03f0)); - // t3 = [00dddddd|00000000|00bbbbbb|00000000]( - // (d * (1 << 8), b * (1 << 4)) - const __m128i t3 = _mm_mullo_epi16(t2, _mm_set1_epi32(0x01000010)); - - // res = [00dddddd|00cccccc|00bbbbbb|00aaaaaa] = t1 | t3 - const __m128i indices = _mm_or_si128(t1, t3); - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(indices)); - out += 16; - } - - return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, - srclen - i, options); -} -static inline void compress(__m128i data, uint16_t mask, char *output) { - if (mask == 0) { - _mm_storeu_si128(reinterpret_cast<__m128i *>(output), data); - return; - } - - // this particular implementation was inspired by work done by @animetosho - // we do it in two steps, first 8 bytes and then second 8 bytes - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - // next line just loads the 64-bit values thintable_epi8[mask1] and - // thintable_epi8[mask2] into a 128-bit register, using only - // two instructions on most compilers. - - __m128i shufmask = _mm_set_epi64x(tables::base64::thintable_epi8[mask2], - tables::base64::thintable_epi8[mask1]); - // we increment by 0x08 the second half of the mask - shufmask = - _mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0)); - // this is the version "nearly pruned" - __m128i pruned = _mm_shuffle_epi8(data, shufmask); - // we still need to put the two halves together. - // we compute the popcount of the first half: - int pop1 = tables::base64::BitsSetTable256mul2[mask1]; - // then load the corresponding mask, what it does is to write - // only the first pop1 bytes from the first 8 bytes, and then - // it fills in with the bytes from the second 8 bytes + some filling - // at the end. - __m128i compactmask = _mm_loadu_si128(reinterpret_cast( - tables::base64::pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); -} - -struct block64 { - __m128i chunks[4]; -}; - -template -static inline uint16_t to_base64_mask(__m128i *src, bool *error) { - const __m128i ascii_space_tbl = - _mm_setr_epi8(0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xa, 0x0, - 0xc, 0xd, 0x0, 0x0); - // credit: aqrit - __m128i delta_asso; - if (base64_url) { - delta_asso = _mm_setr_epi8(0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0xF, 0x0, 0xF); - } else { - - delta_asso = _mm_setr_epi8(0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F); - } - __m128i delta_values; - if (base64_url) { - delta_values = _mm_setr_epi8(0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF), - uint8_t(0xBF), uint8_t(0xB9), uint8_t(0xB9), - 0x0, 0x11, uint8_t(0xC3), uint8_t(0xBF), - uint8_t(0xE0), uint8_t(0xB9), uint8_t(0xB9)); - } else { - - delta_values = - _mm_setr_epi8(int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0x10), int8_t(0xC3), - int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9)); - } - __m128i check_asso; - if (base64_url) { - check_asso = _mm_setr_epi8(0xD, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x3, 0x7, 0xB, 0x6, 0xB, 0x12); - } else { - - check_asso = _mm_setr_epi8(0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F); - } - __m128i check_values; - if (base64_url) { - check_values = _mm_setr_epi8(0x0, uint8_t(0x80), uint8_t(0x80), - uint8_t(0x80), uint8_t(0xCF), uint8_t(0xBF), - uint8_t(0xD3), uint8_t(0xA6), uint8_t(0xB5), - uint8_t(0x86), uint8_t(0xD0), uint8_t(0x80), - uint8_t(0xB0), uint8_t(0x80), 0x0, 0x0); - } else { - - check_values = - _mm_setr_epi8(int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), - int8_t(0xB5), int8_t(0x86), int8_t(0xD1), int8_t(0x80), - int8_t(0xB1), int8_t(0x80), int8_t(0x91), int8_t(0x80)); - } - const __m128i shifted = _mm_srli_epi32(*src, 3); - - const __m128i delta_hash = - _mm_avg_epu8(_mm_shuffle_epi8(delta_asso, *src), shifted); - const __m128i check_hash = - _mm_avg_epu8(_mm_shuffle_epi8(check_asso, *src), shifted); - - const __m128i out = - _mm_adds_epi8(_mm_shuffle_epi8(delta_values, delta_hash), *src); - const __m128i chk = - _mm_adds_epi8(_mm_shuffle_epi8(check_values, check_hash), *src); - const int mask = _mm_movemask_epi8(chk); - if (mask) { - __m128i ascii_space = - _mm_cmpeq_epi8(_mm_shuffle_epi8(ascii_space_tbl, *src), *src); - *error |= (mask != _mm_movemask_epi8(ascii_space)); - } - *src = out; - return (uint16_t)mask; -} - -template -static inline uint64_t to_base64_mask(block64 *b, bool *error) { - *error = 0; - uint64_t m0 = to_base64_mask(&b->chunks[0], error); - uint64_t m1 = to_base64_mask(&b->chunks[1], error); - uint64_t m2 = to_base64_mask(&b->chunks[2], error); - uint64_t m3 = to_base64_mask(&b->chunks[3], error); - return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48); -} - -static inline void copy_block(block64 *b, char *output) { - _mm_storeu_si128(reinterpret_cast<__m128i *>(output), b->chunks[0]); - _mm_storeu_si128(reinterpret_cast<__m128i *>(output + 16), b->chunks[1]); - _mm_storeu_si128(reinterpret_cast<__m128i *>(output + 32), b->chunks[2]); - _mm_storeu_si128(reinterpret_cast<__m128i *>(output + 48), b->chunks[3]); -} - -static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { - uint64_t nmask = ~mask; - compress(b->chunks[0], uint16_t(mask), output); - compress(b->chunks[1], uint16_t(mask >> 16), - output + _mm_popcnt_u64(nmask & 0xFFFF)); - compress(b->chunks[2], uint16_t(mask >> 32), - output + _mm_popcnt_u64(nmask & 0xFFFFFFFF)); - compress(b->chunks[3], uint16_t(mask >> 48), - output + _mm_popcnt_u64(nmask & 0xFFFFFFFFFFFFULL)); - return _mm_popcnt_u64(nmask); -} - -// The caller of this function is responsible to ensure that there are 64 bytes available -// from reading at src. The data is read into a block64 structure. -static inline void load_block(block64 *b, const char *src) { - b->chunks[0] = _mm_loadu_si128(reinterpret_cast(src)); - b->chunks[1] = _mm_loadu_si128(reinterpret_cast(src + 16)); - b->chunks[2] = _mm_loadu_si128(reinterpret_cast(src + 32)); - b->chunks[3] = _mm_loadu_si128(reinterpret_cast(src + 48)); -} - -// The caller of this function is responsible to ensure that there are 128 bytes available -// from reading at src. The data is read into a block64 structure. -static inline void load_block(block64 *b, const char16_t *src) { - __m128i m1 = _mm_loadu_si128(reinterpret_cast(src)); - __m128i m2 = _mm_loadu_si128(reinterpret_cast(src + 8)); - __m128i m3 = _mm_loadu_si128(reinterpret_cast(src + 16)); - __m128i m4 = _mm_loadu_si128(reinterpret_cast(src + 24)); - __m128i m5 = _mm_loadu_si128(reinterpret_cast(src + 32)); - __m128i m6 = _mm_loadu_si128(reinterpret_cast(src + 40)); - __m128i m7 = _mm_loadu_si128(reinterpret_cast(src + 48)); - __m128i m8 = _mm_loadu_si128(reinterpret_cast(src + 56)); - b->chunks[0] = _mm_packus_epi16(m1, m2); - b->chunks[1] = _mm_packus_epi16(m3, m4); - b->chunks[2] = _mm_packus_epi16(m5, m6); - b->chunks[3] = _mm_packus_epi16(m7, m8); -} - -static inline void base64_decode(char *out, __m128i str) { - // credit: aqrit - - const __m128i pack_shuffle = - _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1); - - const __m128i t0 = _mm_maddubs_epi16(str, _mm_set1_epi32(0x01400140)); - const __m128i t1 = _mm_madd_epi16(t0, _mm_set1_epi32(0x00011000)); - const __m128i t2 = _mm_shuffle_epi8(t1, pack_shuffle); - // Store the output: - // this writes 16 bytes, but we only need 12. - _mm_storeu_si128((__m128i *)out, t2); -} -// decode 64 bytes and output 48 bytes -static inline void base64_decode_block(char *out, const char *src) { - base64_decode(out, _mm_loadu_si128(reinterpret_cast(src))); - base64_decode(out + 12, - _mm_loadu_si128(reinterpret_cast(src + 16))); - base64_decode(out + 24, - _mm_loadu_si128(reinterpret_cast(src + 32))); - base64_decode(out + 36, - _mm_loadu_si128(reinterpret_cast(src + 48))); -} -static inline void base64_decode_block_safe(char *out, const char *src) { - base64_decode(out, _mm_loadu_si128(reinterpret_cast(src))); - base64_decode(out + 12, - _mm_loadu_si128(reinterpret_cast(src + 16))); - base64_decode(out + 24, - _mm_loadu_si128(reinterpret_cast(src + 32))); - char buffer[16]; - base64_decode(buffer, - _mm_loadu_si128(reinterpret_cast(src + 48))); - std::memcpy(out + 36, buffer, 12); -} -static inline void base64_decode_block(char *out, block64 *b) { - base64_decode(out, b->chunks[0]); - base64_decode(out + 12, b->chunks[1]); - base64_decode(out + 24, b->chunks[2]); - base64_decode(out + 36, b->chunks[3]); -} -static inline void base64_decode_block_safe(char *out, block64 *b) { - base64_decode(out, b->chunks[0]); - base64_decode(out + 12, b->chunks[1]); - base64_decode(out + 24, b->chunks[2]); - char buffer[16]; - base64_decode(buffer, b->chunks[3]); - std::memcpy(out + 36, buffer, 12); -} - -template -result compress_decode_base64(char *dst, const chartype *src, size_t srclen, - base64_options options) { - const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - size_t equalsigns = 0; - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 1; - // skip trailing spaces - while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - if (srclen > 0 && src[srclen - 1] == '=') { - srclen--; - equalsigns = 2; - } - } - char *end_of_safe_64byte_zone = - (srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63 : dst; - - const chartype *const srcinit = src; - const char *const dstinit = dst; - const chartype *const srcend = src + srclen; - - constexpr size_t block_size = 6; - static_assert(block_size >= 2, "block should of size 2 or more"); - char buffer[block_size * 64]; - char *bufferptr = buffer; - if (srclen >= 64) { - const chartype *const srcend64 = src + srclen - 64; - while (src <= srcend64) { - block64 b; - load_block(&b, src); - src += 64; - bool error = false; - uint64_t badcharmask = to_base64_mask(&b, &error); - if (error) { - src -= 64; - while (src < srcend && to_base64[uint8_t(*src)] <= 64) { - src++; - } - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - if (badcharmask != 0) { - // optimization opportunity: check for simple masks like those made of - // continuous 1s followed by continuous 0s. And masks containing a - // single bad character. - bufferptr += compress_block(&b, badcharmask, bufferptr); - } else if (bufferptr != buffer) { - copy_block(&b, bufferptr); - bufferptr += 64; - } else { - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, &b); - } else { - base64_decode_block(dst, &b); - } - dst += 48; - } - if (bufferptr >= (block_size - 1) * 64 + buffer) { - for (size_t i = 0; i < (block_size - 2); i++) { - base64_decode_block(dst, buffer + i * 64); - dst += 48; - } - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, buffer + (block_size - 2) * 64); - } else { - base64_decode_block(dst, buffer + (block_size - 2) * 64); - } - dst += 48; - std::memcpy(buffer, buffer + (block_size - 1) * 64, - 64); // 64 might be too much - bufferptr -= (block_size - 1) * 64; - } - } - } - - char *buffer_start = buffer; - // Optimization note: if this is almost full, then it is worth our - // time, otherwise, we should just decode directly. - int last_block = (int)((bufferptr - buffer_start) % 64); - if (last_block != 0 && srcend - src + last_block >= 64) { - while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - *bufferptr = char(val); - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - bufferptr += (val <= 63); - src++; - } - } - - for (; buffer_start + 64 <= bufferptr; buffer_start += 64) { - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, buffer_start); - } else { - base64_decode_block(dst, buffer_start); - } - dst += 48; - } - if ((bufferptr - buffer_start) % 64 != 0) { - while (buffer_start + 4 < bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 4); - - dst += 3; - buffer_start += 4; - } - if (buffer_start + 4 <= bufferptr) { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - - dst += 3; - buffer_start += 4; - } - // we may have 1, 2 or 3 bytes left and we need to decode them so let us - // bring in src content - int leftover = int(bufferptr - buffer_start); - if (leftover > 0) { - while (leftover < 4 && src < srcend) { - uint8_t val = to_base64[uint8_t(*src)]; - if (val > 64) { - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)}; - } - buffer_start[leftover] = char(val); - leftover += (val <= 63); - src++; - } - - if (leftover == 1) { - return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)}; - } - if (leftover == 2) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6); - triple = scalar::utf32::swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - dst += 1; - } else if (leftover == 3) { - uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) + - (uint32_t(buffer_start[1]) << 2 * 6) + - (uint32_t(buffer_start[2]) << 1 * 6); - triple = scalar::utf32::swap_bytes(triple); - - triple >>= 8; - - std::memcpy(dst, &triple, 2); - dst += 2; - } else { - uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) + - (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) + - (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + - (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) - << 8; - triple = scalar::utf32::swap_bytes(triple); - std::memcpy(dst, &triple, 3); - dst += 3; - } - } - } - if (src < srcend + equalsigns) { - result r = - scalar::base64::base64_tail_decode(dst, src, srcend - src, options); - if (r.error == error_code::INVALID_BASE64_CHARACTER) { - r.count += size_t(src - srcinit); - return r; - } else { - r.count += size_t(dst - dstinit); - } - if(r.error == error_code::SUCCESS && equalsigns > 0) { - // additional checks - if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) { - r.error = error_code::INVALID_BASE64_CHARACTER; - } - } - return r; - } - if(equalsigns > 0) { - if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)}; - } - } - return {SUCCESS, size_t(dst - dstinit)}; -} -/* end file src/westmere/sse_base64.cpp */ - -} // unnamed namespace -} // namespace westmere -} // namespace simdutf - -/* begin file src/generic/buf_block_reader.h */ -namespace simdutf { -namespace westmere { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdutf_really_inline size_t block_index(); - simdutf_really_inline bool has_full_block() const; - simdutf_really_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdutf_really_inline size_t get_remainder(uint8_t *dst) const; - simdutf_really_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text_64(const uint8_t *text) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char * format_input_text(const simd8x64& in) { - static char *buf = reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char * format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdutf_really_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdutf_really_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdutf_really_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdutf_really_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdutf_really_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/buf_block_reader.h */ -/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdutf_really_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1 - }; - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdutf_really_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdutf_really_inline void check_next_input(const simd8x64& input) { - if(simdutf_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - - } - } - - // do not forget to call check_eof! - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */ -/* begin file src/generic/utf8_validation/utf8_validator.h */ -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_validation { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return !c.errors(); -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -/** - * Validates that the string is actual UTF-8 and stops on errors. - */ -template -result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - if(c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input + count), length - count); - res.count += count; - return res; - } - reader.advance(); - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - if (c.errors()) { - if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk - result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast(input), reinterpret_cast(input) + count, length - count); - res.count += count; - return res; - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_utf8_with_errors(const char * input, size_t length) { - return generic_validate_utf8_with_errors(reinterpret_cast(input),length); -} - -template -bool generic_validate_ascii(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - uint8_t blocks[64]{}; - simd::simd8x64 running_or(blocks); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - running_or |= in; - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - running_or |= in; - return running_or.is_ascii(); -} - -bool generic_validate_ascii(const char * input, size_t length) { - return generic_validate_ascii(reinterpret_cast(input),length); -} - -template -result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) { - buf_block_reader<64> reader(input, length); - size_t count{0}; - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } - reader.advance(); - - count += 64; - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - if (!in.is_ascii()) { - result res = scalar::ascii::validate_with_errors(reinterpret_cast(input + count), length - count); - return result(res.error, count + res.count); - } else { - return result(error_code::SUCCESS, length); - } -} - -result generic_validate_ascii_with_errors(const char * input, size_t length) { - return generic_validate_ascii_with_errors(reinterpret_cast(input),length); -} - -} // namespace utf8_validation -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_validation/utf8_validator.h */ -// transcoding from UTF-8 to UTF-16 -/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ - - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_to_utf16 { - -using namespace simd; - -template -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char16_t* utf16_output) noexcept { - // The implementation is not specific to haswell and should be moved to the generic directory. - size_t pos = 0; - char16_t* start{utf16_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - // this loop could be unrolled further. For example, we could process the mask - // far more than 64 bytes. - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // Slow path. We hope that the compiler will recognize that this is a slow path. - // Anything that is not a continuation mask is a 'leading byte', that is, the - // start of a new code point. - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - // The *start* of code points is not so useful, rather, we want the *end* of code points. - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times when using solely - // the slow/regular path, and at least four times if there are fast paths. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - // - // Thus we may allow convert_masked_utf8_to_utf16 to process - // more bytes at a time under a fast-path mode where 16 bytes - // are consumed at once (e.g., when encountering ASCII). - size_t consumed = convert_masked_utf8_to_utf16(input + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - utf16_output += scalar::utf8_to_utf16::convert_valid(input + pos, size - pos, utf16_output); - return utf16_output - start; -} - -} // namespace utf8_to_utf16 -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */ -/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */ - - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_to_utf16 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - template - simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf16::convert(in + pos, size - pos, utf16_output); - if(howmany == 0) { return 0; } - utf16_output += howmany; - } - return utf16_output - start; - } - - template - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) { - size_t pos = 0; - char16_t* start{utf16_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf16(utf16_output); - utf16_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf16(in + pos, - utf8_end_of_code_point_mask, utf16_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf16_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf16_output += res.count; - } - } - return result(error_code::SUCCESS, utf16_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf16 namespace -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */ -// transcoding from UTF-8 to UTF-32 -/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_to_utf32 { - -using namespace simd; - - -simdutf_warn_unused size_t convert_valid(const char* input, size_t size, - char32_t* utf32_output) noexcept { - size_t pos = 0; - char32_t* start{utf32_output}; - const size_t safety_margin = 16; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 in(reinterpret_cast(input + pos)); - if(in.is_ascii()) { - in.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // -65 is 0b10111111 in two-complement's, so largest possible continuation byte - uint64_t utf8_continuation_mask = in.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - size_t max_starting_point = (pos + 64) - 12; - while(pos < max_starting_point) { - size_t consumed = convert_masked_utf8_to_utf32(input + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - } - } - utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output); - return utf32_output - start; -} - - -} // namespace utf8_to_utf32 -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */ -/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */ - - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_to_utf32 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdutf_really_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - - - simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output); - if(howmany == 0) { return 0; } - utf32_output += howmany; - } - return utf32_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) { - size_t pos = 0; - char32_t* start{utf32_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the fourth last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store_ascii_as_utf32(utf32_output); - utf32_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_utf32(in + pos, - utf8_end_of_code_point_mask, utf32_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - res.count += pos; - return res; - } - if(pos < size) { - result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - utf32_output += res.count; - } - } - return result(error_code::SUCCESS, utf32_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_utf32 namespace -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */ -// other functions -/* begin file src/generic/utf8.h */ - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8 { - -using namespace simd; - -simdutf_really_inline size_t count_code_points(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.gt(-65); - count += count_ones(utf8_continuation_mask); - } - return count + scalar::utf8::count_code_points(in + pos, size - pos); -} - -simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos + 64 <= size; pos += 64) { - simd8x64 input(reinterpret_cast(in + pos)); - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - // We count one word for anything that is not a continuation (so - // leading bytes). - count += 64 - count_ones(utf8_continuation_mask); - int64_t utf8_4byte = input.gteq_unsigned(240); - count += count_ones(utf8_4byte); - } - return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos); -} -} // utf8 namespace -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8.h */ -/* begin file src/generic/utf16.h */ -namespace simdutf { -namespace westmere { -namespace { -namespace utf16 { - -template -simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF); - count += count_ones(not_pair) / 2; - } - return count + scalar::utf16::count_code_points(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) { - size_t pos = 0; - size_t count = 0; - // This algorithm could no doubt be improved! - for(;pos < size/32*32; pos += 32) { - simd16x32 input(reinterpret_cast(in + pos)); - if (!match_system(big_endian)) { input.swap_bytes(); } - uint64_t ascii_mask = input.lteq(0x7F); - uint64_t twobyte_mask = input.lteq(0x7FF); - uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF); - - size_t ascii_count = count_ones(ascii_mask) / 2; - size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2; - size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2; - size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2; - count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count; - } - return count + scalar::utf16::utf8_length_from_utf16(in + pos, size - pos); -} - -template -simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) { - return count_code_points(in, size); -} - -simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) { - size_t pos = 0; - - while (pos < size/32*32) { - simd16x32 input(reinterpret_cast(in + pos)); - input.swap_bytes(); - input.store(reinterpret_cast(output)); - pos += 32; - output += 32; - } - - scalar::utf16::change_endianness_utf16(in + pos, size - pos, output); -} - -} // utf16 -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf16.h */ -// transcoding from UTF-8 to Latin 1 -/* begin file src/generic/utf8_to_latin1/utf8_to_latin1.h */ - - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_to_latin1 { -using namespace simd; - - - simdutf_really_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// For UTF-8 to Latin 1, we can allow any ASCII character, and any continuation byte, -// but the non-ASCII leading bytes must be 0b11000011 or 0b11000010 and nothing else. -// -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - constexpr const uint8_t FORBIDDEN = 0xff; - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - FORBIDDEN, - // 1110____ ________ - FORBIDDEN, - // 1111____ ________ - FORBIDDEN - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - FORBIDDEN, - // ____0101 ________ - FORBIDDEN, - // ____011_ ________ - FORBIDDEN, - FORBIDDEN, - - // ____1___ ________ - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - FORBIDDEN, - // ____1101 ________ - FORBIDDEN, - FORBIDDEN, - FORBIDDEN - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - - struct validating_transcoder { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - - validating_transcoder() : error(uint8_t(0)) {} - // - // Check whether the current bytes are valid UTF-8. - // - simdutf_really_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - this->error |= check_special_cases(input, prev1); - } - - - simdutf_really_inline size_t convert(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ... - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for. - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { return 0; } - if(pos < size) { - size_t howmany = scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output); - if(howmany == 0) { return 0; } - latin1_output += howmany; - } - return latin1_output - start; - } - - simdutf_really_inline result convert_with_errors(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 2) || (simd8x64::NUM_CHUNKS == 4), - "We support either two or four chunks per 64-byte block."); - auto zero = simd8{uint8_t(0)}; - if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], zero); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - if (errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - res.count += pos; - return res; - } - uint64_t utf8_continuation_mask = input.lt(-65 + 1); - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(errors()) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - res.count += pos; - return res; - } - if(pos < size) { - // rewind_and_convert_with_errors will seek a potential error from in+pos onward, - // with the ability to go back up to pos bytes, and read size-pos bytes forward. - result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output); - if (res.error) { // In case of error, we want the error position - res.count += pos; - return res; - } else { // In case of success, we want the number of word written - latin1_output += res.count; - } - } - return result(error_code::SUCCESS, latin1_output - start); - } - - simdutf_really_inline bool errors() const { - return this->error.any_bits_set_anywhere(); - } - - }; // struct utf8_checker -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace westmere -} // namespace simdutf -/* end file src/generic/utf8_to_latin1/utf8_to_latin1.h */ -/* begin file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */ - - -namespace simdutf { -namespace westmere { -namespace { -namespace utf8_to_latin1 { -using namespace simd; - - - simdutf_really_inline size_t convert_valid(const char* in, size_t size, char* latin1_output) { - size_t pos = 0; - char* start{latin1_output}; - // In the worst case, we have the haswell kernel which can cause an overflow of - // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes, - // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate - // much more than 8 bytes. However, you cannot generally assume that you have valid - // UTF-8 input, so we are going to go back from the end counting 8 leading bytes, - // to give us a good margin. - size_t leading_byte = 0; - size_t margin = size; - for(; margin > 0 && leading_byte < 8; margin--) { - leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ... - } - // If the input is long enough, then we have that margin-1 is the eight last leading byte. - const size_t safety_margin = size - margin + 1; // to avoid overruns! - while(pos + 64 + safety_margin <= size) { - simd8x64 input(reinterpret_cast(in + pos)); - if(input.is_ascii()) { - input.store((int8_t*)latin1_output); - latin1_output += 64; - pos += 64; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for. - uint64_t utf8_leading_mask = ~utf8_continuation_mask; - uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1; - // We process in blocks of up to 12 bytes except possibly - // for fast paths which may process up to 16 bytes. For the - // slow path to work, we should have at least 12 input bytes left. - size_t max_starting_point = (pos + 64) - 12; - // Next loop is going to run at least five times. - while(pos < max_starting_point) { - // Performance note: our ability to compute 'consumed' and - // then shift and recompute is critical. If there is a - // latency of, say, 4 cycles on getting 'consumed', then - // the inner loop might have a total latency of about 6 cycles. - // Yet we process between 6 to 12 inputs bytes, thus we get - // a speed limit between 1 cycle/byte and 0.5 cycle/byte - // for this section of the code. Hence, there is a limit - // to how much we can further increase this latency before - // it seriously harms performance. - size_t consumed = convert_masked_utf8_to_latin1(in + pos, - utf8_end_of_code_point_mask, latin1_output); - pos += consumed; - utf8_end_of_code_point_mask >>= consumed; - } - // At this point there may remain between 0 and 12 bytes in the - // 64-byte block. These bytes will be processed again. So we have an - // 80% efficiency (in the worst case). In practice we expect an - // 85% to 90% efficiency. - } - } - if(pos < size) { - size_t howmany = scalar::utf8_to_latin1::convert_valid(in + pos, size - pos, latin1_output); - latin1_output += howmany; - } - return latin1_output - start; - } - - } -} // utf8_to_latin1 namespace -} // unnamed namespace -} // namespace westmere - // namespace simdutf -/* end file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */ - - -// -// Implementation-specific overrides -// - -namespace simdutf { -namespace westmere { - -simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept { - // If there is a BOM, then we trust it. - auto bom_encoding = simdutf::BOM::check_bom(input, length); - if(bom_encoding != encoding_type::unspecified) { return bom_encoding; } - if (length % 2 == 0) { - return sse_detect_encodings(input, length); - } else { - if (implementation::validate_utf8(input, length)) { - return simdutf::encoding_type::UTF8; - } else { - return simdutf::encoding_type::unspecified; - } - } -} - -simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return westmere::utf8_validation::generic_validate_utf8(buf, len); -} - -simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept { - return westmere::utf8_validation::generic_validate_utf8_with_errors(buf, len); -} - -simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept { - return westmere::utf8_validation::generic_validate_ascii(buf, len); -} - -simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept { - return westmere::utf8_validation::generic_validate_ascii_with_errors(buf,len); -} - -simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept { - const char16_t* tail = sse_validate_utf16(buf, len); - if (tail) { - return scalar::utf16::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept { - const char16_t* tail = sse_validate_utf16(buf, len); - if (tail) { - return scalar::utf16::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept { - result res = sse_validate_utf16_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf16::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept { - result res = sse_validate_utf16_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf16::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept { - const char32_t* tail = sse_validate_utf32le(buf, len); - if (tail) { - return scalar::utf32::validate(tail, len - (tail - buf)); - } else { - return false; - } -} - -simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept { - result res = sse_validate_utf32le_with_errors(buf, len); - if (res.count != len) { - result scalar_res = scalar::utf32::validate_with_errors(buf + res.count, len - res.count); - return result(scalar_res.error, res.count + scalar_res.count); - } else { - return res; - } -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept { - - std::pair ret = sse_convert_latin1_to_utf8(buf, len, utf8_output); - size_t converted_chars = ret.second - utf8_output; - - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - converted_chars += scalar_converted_chars; - } - - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = sse_convert_latin1_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = sse_convert_latin1_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - -simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = sse_convert_latin1_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t converted_chars = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_converted_chars == 0) { return 0; } - converted_chars += scalar_converted_chars; - } - return converted_chars; -} - - -simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - utf8_to_latin1::validating_transcoder converter; - return converter.convert(buf, len, latin1_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept { - utf8_to_latin1::validating_transcoder converter; - return converter.convert_with_errors(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept { - return westmere::utf8_to_latin1::convert_valid(buf,len,latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf16_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept { - utf8_to_utf16::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf16_output); -} - - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* input, size_t size, - char16_t* utf16_output) const noexcept { - return utf8_to_utf16::convert_valid(input, size, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* input, size_t size, - char16_t* utf16_output) const noexcept { - return utf8_to_utf16::convert_valid(input, size, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - utf8_to_utf32::validating_transcoder converter; - return converter.convert(buf, len, utf32_output); -} - -simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept { - utf8_to_utf32::validating_transcoder converter; - return converter.convert_with_errors(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size, - char32_t* utf32_output) const noexcept { - return utf8_to_utf32::convert_valid(input, size, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = sse_convert_utf16_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = sse_convert_utf16_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = sse_convert_utf16_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = sse_convert_utf16_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: we could provide an optimized function. - return convert_utf16be_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: we could provide an optimized function. - return convert_utf16le_to_latin1(buf, len, latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = sse_convert_utf16_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = sse_convert_utf16_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf16_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf16_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16le_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf16be_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - std::pair ret = sse_convert_utf32_to_latin1(buf, len, latin1_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - latin1_output; - // if (ret.first != buf + len) { - if (ret.first < buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - - -simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf32_to_latin1_with_errors(buf, len, latin1_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_latin1::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept { - // optimization opportunity: we could provide an optimized function. - return convert_utf32_to_latin1(buf,len,latin1_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - std::pair ret = sse_convert_utf32_to_utf8(buf, len, utf8_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf8_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf32_to_utf8_with_errors(buf, len, utf8_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf8::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = sse_convert_utf16_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - std::pair ret = sse_convert_utf16_to_utf32(buf, len, utf32_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf32_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf16_to_utf32_with_errors(buf, len, utf32_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf16_to_utf32_with_errors(buf, len, utf32_output); - if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count - if (ret.first.count != len) { // All good so far, but not finished - result scalar_res = scalar::utf16_to_utf32::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept { - return convert_utf32_to_utf8(buf, len, utf8_output); -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = sse_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - std::pair ret = sse_convert_utf32_to_utf16(buf, len, utf16_output); - if (ret.first == nullptr) { return 0; } - size_t saved_bytes = ret.second - utf16_output; - if (ret.first != buf + len) { - const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert( - ret.first, len - (ret.first - buf), ret.second); - if (scalar_saved_bytes == 0) { return 0; } - saved_bytes += scalar_saved_bytes; - } - return saved_bytes; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - // ret.first.count is always the position in the buffer, not the number of code units written even if finished - std::pair ret = westmere::sse_convert_utf32_to_utf16_with_errors(buf, len, utf16_output); - if (ret.first.count != len) { - result scalar_res = scalar::utf32_to_utf16::convert_with_errors( - buf + ret.first.count, len - ret.first.count, ret.second); - if (scalar_res.error) { - scalar_res.count += ret.first.count; - return scalar_res; - } else { - ret.second += scalar_res.count; - } - } - ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written - return ret.first; -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16le(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept { - return convert_utf32_to_utf16be(buf, len, utf16_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return convert_utf16le_to_utf32(buf, len, utf32_output); -} - -simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept { - return convert_utf16be_to_utf32(buf, len, utf32_output); -} - -void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept { - utf16::change_endianness_utf16(input, length, output); -} - -simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept { - return count_utf8(buf,len); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept { - return scalar::utf16::latin1_length_from_utf16(length); -} - -simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept { - return scalar::utf32::latin1_length_from_utf32(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::utf8_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf16_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept { - return scalar::latin1::utf32_length_from_latin1(length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t len) const noexcept { - const uint8_t *str = reinterpret_cast(input); - size_t answer = len / sizeof(__m128i) * sizeof(__m128i); - size_t i = 0; - __m128i two_64bits = _mm_setzero_si128(); - while (i + sizeof(__m128i) <= len) { - __m128i runner = _mm_setzero_si128(); - size_t iterations = (len - i) / sizeof(__m128i); - if (iterations > 255) { - iterations = 255; - } - size_t max_i = i + iterations * sizeof(__m128i) - sizeof(__m128i); - for (; i + 4*sizeof(__m128i) <= max_i; i += 4*sizeof(__m128i)) { - __m128i input1 = _mm_loadu_si128((const __m128i *)(str + i)); - __m128i input2 = _mm_loadu_si128((const __m128i *)(str + i + sizeof(__m128i))); - __m128i input3 = _mm_loadu_si128((const __m128i *)(str + i + 2*sizeof(__m128i))); - __m128i input4 = _mm_loadu_si128((const __m128i *)(str + i + 3*sizeof(__m128i))); - __m128i input12 = _mm_add_epi8( - _mm_cmpgt_epi8( - _mm_setzero_si128(), - input1), - _mm_cmpgt_epi8( - _mm_setzero_si128(), - input2)); - __m128i input34 = _mm_add_epi8( - _mm_cmpgt_epi8( - _mm_setzero_si128(), - input3), - _mm_cmpgt_epi8( - _mm_setzero_si128(), - input4)); - __m128i input1234 = _mm_add_epi8(input12, input34); - runner = _mm_sub_epi8(runner, input1234); - } - for (; i <= max_i; i += sizeof(__m128i)) { - __m128i more_input = _mm_loadu_si128((const __m128i *)(str + i)); - runner = _mm_sub_epi8( - runner, _mm_cmpgt_epi8(_mm_setzero_si128(), more_input)); - } - two_64bits = _mm_add_epi64( - two_64bits, _mm_sad_epu8(runner, _mm_setzero_si128())); - } - answer += _mm_extract_epi64(two_64bits, 0) + - _mm_extract_epi64(two_64bits, 1); - return answer + scalar::latin1::utf8_length_from_latin1(reinterpret_cast(str + i), len - i); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept { - return utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept { - return utf16::utf32_length_from_utf16(input, length); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept { - return utf8::utf16_length_from_utf8(input, length); -} - -simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const __m128i v_00000000 = _mm_setzero_si128(); - const __m128i v_ffffff80 = _mm_set1_epi32((uint32_t)0xffffff80); - const __m128i v_fffff800 = _mm_set1_epi32((uint32_t)0xfffff800); - const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000); - size_t pos = 0; - size_t count = 0; - for(;pos + 4 <= length; pos += 4) { - __m128i in = _mm_loadu_si128((__m128i*)(input + pos)); - const __m128i ascii_bytes_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_ffffff80), v_00000000); - const __m128i one_two_bytes_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_fffff800), v_00000000); - const __m128i two_bytes_bytemask = _mm_xor_si128(one_two_bytes_bytemask, ascii_bytes_bytemask); - const __m128i one_two_three_bytes_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_ffff0000), v_00000000); - const __m128i three_bytes_bytemask = _mm_xor_si128(one_two_three_bytes_bytemask, one_two_bytes_bytemask); - const uint16_t ascii_bytes_bitmask = static_cast(_mm_movemask_epi8(ascii_bytes_bytemask)); - const uint16_t two_bytes_bitmask = static_cast(_mm_movemask_epi8(two_bytes_bytemask)); - const uint16_t three_bytes_bitmask = static_cast(_mm_movemask_epi8(three_bytes_bytemask)); - - size_t ascii_count = count_ones(ascii_bytes_bitmask) / 4; - size_t two_bytes_count = count_ones(two_bytes_bitmask) / 4; - size_t three_bytes_count = count_ones(three_bytes_bitmask) / 4; - count += 16 - 3*ascii_count - 2*two_bytes_count - three_bytes_count; - } - return count + scalar::utf32::utf8_length_from_utf32(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept { - const __m128i v_00000000 = _mm_setzero_si128(); - const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000); - size_t pos = 0; - size_t count = 0; - for(;pos + 4 <= length; pos += 4) { - __m128i in = _mm_loadu_si128((__m128i*)(input + pos)); - const __m128i surrogate_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_ffff0000), v_00000000); - const uint16_t surrogate_bitmask = static_cast(_mm_movemask_epi8(surrogate_bytemask)); - size_t surrogate_count = (16-count_ones(surrogate_bitmask))/4; - count += 4 + surrogate_count; - } - return count + scalar::utf32::utf16_length_from_utf32(input + pos, length - pos); -} - -simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept { - return utf8::count_code_points(input, length); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept { - return scalar::base64::maximal_binary_length_from_base64(input, length); -} - -simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept { - return (options & base64_url) ? compress_decode_base64(output, input, length, options) : compress_decode_base64(output, input, length, options); -} - -simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept { - return scalar::base64::base64_length_from_binary(length, options); -} - -size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept { - if(options == base64_url) { - return encode_base64(output, input, length); - } else { - return encode_base64(output, input, length); - } -} -} // namespace westmere -} // namespace simdutf - -/* begin file src/simdutf/westmere/end.h */ -#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE -// nothing needed. -#else -SIMDUTF_UNTARGET_REGION -#endif - -/* end file src/simdutf/westmere/end.h */ -/* end file src/westmere/implementation.cpp */ -#endif - -SIMDUTF_POP_DISABLE_WARNINGS -/* end file src/simdutf.cpp */ -#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/bun.js/bindings/simdutf.h b/src/bun.js/bindings/simdutf.h deleted file mode 100644 index 63c8f31d44490..0000000000000 --- a/src/bun.js/bindings/simdutf.h +++ /dev/null @@ -1,3758 +0,0 @@ -// clang-format off -/* auto-generated on 2024-05-07 22:33:11 -0400. Do not edit! */ -/* begin file include/simdutf.h */ -#ifndef SIMDUTF_H -#define SIMDUTF_H -#include - -/* begin file include/simdutf/compiler_check.h */ -#ifndef SIMDUTF_COMPILER_CHECK_H -#define SIMDUTF_COMPILER_CHECK_H - -#ifndef __cplusplus -#error simdutf requires a C++ compiler -#endif - -#ifndef SIMDUTF_CPLUSPLUS -#if defined(_MSVC_LANG) && !defined(__clang__) -#define SIMDUTF_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) -#else -#define SIMDUTF_CPLUSPLUS __cplusplus -#endif -#endif - -// C++ 17 -#if !defined(SIMDUTF_CPLUSPLUS17) && (SIMDUTF_CPLUSPLUS >= 201703L) -#define SIMDUTF_CPLUSPLUS17 1 -#endif - -// C++ 14 -#if !defined(SIMDUTF_CPLUSPLUS14) && (SIMDUTF_CPLUSPLUS >= 201402L) -#define SIMDUTF_CPLUSPLUS14 1 -#endif - -// C++ 11 -#if !defined(SIMDUTF_CPLUSPLUS11) && (SIMDUTF_CPLUSPLUS >= 201103L) -#define SIMDUTF_CPLUSPLUS11 1 -#endif - -#ifndef SIMDUTF_CPLUSPLUS11 -#error simdutf requires a compiler compliant with the C++11 standard -#endif - -#endif // SIMDUTF_COMPILER_CHECK_H -/* end file include/simdutf/compiler_check.h */ -/* begin file include/simdutf/common_defs.h */ -#ifndef SIMDUTF_COMMON_DEFS_H -#define SIMDUTF_COMMON_DEFS_H - -#include -/* begin file include/simdutf/portability.h */ -#ifndef SIMDUTF_PORTABILITY_H -#define SIMDUTF_PORTABILITY_H - -#include -#include -#include -#include -#include -#ifndef _WIN32 -// strcasecmp, strncasecmp -#include -#endif - -/** - * We want to check that it is actually a little endian system at - * compile-time. - */ - -#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) -#define SIMDUTF_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#elif defined(_WIN32) -#define SIMDUTF_IS_BIG_ENDIAN 0 -#else -#if defined(__APPLE__) || defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ -#include -#elif defined(sun) || defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__) -#include -#else // defined(__APPLE__) || defined(__FreeBSD__) - -#ifdef __has_include -#if __has_include() -#include -#endif //__has_include() -#endif //__has_include - -#endif // defined(__APPLE__) || defined(__FreeBSD__) - - -#ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) -#define SIMDUTF_IS_BIG_ENDIAN 0 -#endif - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define SIMDUTF_IS_BIG_ENDIAN 0 -#else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define SIMDUTF_IS_BIG_ENDIAN 1 -#endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - -#endif // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ - - -/** - * At this point in time, SIMDUTF_IS_BIG_ENDIAN is defined. - */ - -#ifdef _MSC_VER -#define SIMDUTF_VISUAL_STUDIO 1 -/** - * We want to differentiate carefully between - * clang under visual studio and regular visual - * studio. - * - * Under clang for Windows, we enable: - * * target pragmas so that part and only part of the - * code gets compiled for advanced instructions. - * - */ -#ifdef __clang__ -// clang under visual studio -#define SIMDUTF_CLANG_VISUAL_STUDIO 1 -#else -// just regular visual studio (best guess) -#define SIMDUTF_REGULAR_VISUAL_STUDIO 1 -#endif // __clang__ -#endif // _MSC_VER - -#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO -// https://en.wikipedia.org/wiki/C_alternative_tokens -// This header should have no effect, except maybe -// under Visual Studio. -#include -#endif - -#if (defined(__x86_64__) || defined(_M_AMD64)) && !defined(_M_ARM64EC) -#define SIMDUTF_IS_X86_64 1 -#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) -#define SIMDUTF_IS_ARM64 1 -#elif defined(__PPC64__) || defined(_M_PPC64) -//#define SIMDUTF_IS_PPC64 1 -// The simdutf library does yet support SIMD acceleration under -// POWER processors. Please see https://github.com/lemire/simdutf/issues/51 -#elif defined(__s390__) -// s390 IBM system. Big endian. -#elif (defined(__riscv) || defined(__riscv__)) && __riscv_xlen == 64 -// RISC-V 64-bit -#define SIMDUTF_IS_RISCV64 1 - -#if __clang_major__ >= 19 -// Does the compiler support target regions for RISC-V -#define SIMDUTF_HAS_RVV_TARGET_REGION 1 -#endif - -#if __riscv_v_intrinsic >= 11000 -#define SIMDUTF_HAS_RVV_INTRINSICS 1 -#endif - -#define SIMDUTF_HAS_ZVBB_INTRINSICS 0 // there is currently no way to detect this - -#if SIMDUTF_HAS_RVV_INTRINSICS && __riscv_vector && __riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64 -// RISC-V V extension -#define SIMDUTF_IS_RVV 1 -#if SIMDUTF_HAS_ZVBB_INTRINSICS && __riscv_zvbb >= 1000000 -// RISC-V Vector Basic Bit-manipulation -#define SIMDUTF_IS_ZVBB 1 -#endif -#endif - -#elif defined(__loongarch_lp64) -// LoongArch 64-bit -#else -// The simdutf library is designed -// for 64-bit processors and it seems that you are not -// compiling for a known 64-bit platform. Please -// use a 64-bit target such as x64 or 64-bit ARM for best performance. -#define SIMDUTF_IS_32BITS 1 - -// We do not support 32-bit platforms, but it can be -// handy to identify them. -#if defined(_M_IX86) || defined(__i386__) -#define SIMDUTF_IS_X86_32BITS 1 -#elif defined(__arm__) || defined(_M_ARM) -#define SIMDUTF_IS_ARM_32BITS 1 -#elif defined(__PPC__) || defined(_M_PPC) -#define SIMDUTF_IS_PPC_32BITS 1 -#endif - -#endif // defined(__x86_64__) || defined(_M_AMD64) - -#ifdef SIMDUTF_IS_32BITS -#ifndef SIMDUTF_NO_PORTABILITY_WARNING -// In the future, we may want to warn users of 32-bit systems that -// the simdutf does not support accelerated kernels for such systems. -#endif // SIMDUTF_NO_PORTABILITY_WARNING -#endif // SIMDUTF_IS_32BITS - -// this is almost standard? -#define SIMDUTF_STRINGIFY_IMPLEMENTATION_(a) #a -#define SIMDUTF_STRINGIFY(a) SIMDUTF_STRINGIFY_IMPLEMENTATION_(a) - -// Our fast kernels require 64-bit systems. -// -// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions. -// Furthermore, the number of SIMD registers is reduced. -// -// On 32-bit ARM, we would have smaller registers. -// -// The simdutf users should still have the fallback kernel. It is -// slower, but it should run everywhere. - -// -// Enable valid runtime implementations, and select SIMDUTF_BUILTIN_IMPLEMENTATION -// - -// We are going to use runtime dispatch. -#ifdef SIMDUTF_IS_X86_64 -#ifdef __clang__ -// clang does not have GCC push pop -// warning: clang attribute push can't be used within a namespace in clang up -// til 8.0 so SIMDUTF_TARGET_REGION and SIMDUTF_UNTARGET_REGION must be *outside* of a -// namespace. -#define SIMDUTF_TARGET_REGION(T) \ - _Pragma(SIMDUTF_STRINGIFY( \ - clang attribute push(__attribute__((target(T))), apply_to = function))) -#define SIMDUTF_UNTARGET_REGION _Pragma("clang attribute pop") -#elif defined(__GNUC__) -// GCC is easier -#define SIMDUTF_TARGET_REGION(T) \ - _Pragma("GCC push_options") _Pragma(SIMDUTF_STRINGIFY(GCC target(T))) -#define SIMDUTF_UNTARGET_REGION _Pragma("GCC pop_options") -#endif // clang then gcc - -#endif // x86 - -// Default target region macros don't do anything. -#ifndef SIMDUTF_TARGET_REGION -#define SIMDUTF_TARGET_REGION(T) -#define SIMDUTF_UNTARGET_REGION -#endif - -// Is threading enabled? -#if defined(_REENTRANT) || defined(_MT) -#ifndef SIMDUTF_THREADS_ENABLED -#define SIMDUTF_THREADS_ENABLED -#endif -#endif - -// workaround for large stack sizes under -O0. -// https://github.com/simdutf/simdutf/issues/691 -#ifdef __APPLE__ -#ifndef __OPTIMIZE__ -// Apple systems have small stack sizes in secondary threads. -// Lack of compiler optimization may generate high stack usage. -// Users may want to disable threads for safety, but only when -// in debug mode which we detect by the fact that the __OPTIMIZE__ -// macro is not defined. -#undef SIMDUTF_THREADS_ENABLED -#endif -#endif - -#ifdef SIMDUTF_VISUAL_STUDIO -// This is one case where we do not distinguish between -// regular visual studio and clang under visual studio. -// clang under Windows has _stricmp (like visual studio) but not strcasecmp (as clang normally has) -#define simdutf_strcasecmp _stricmp -#define simdutf_strncasecmp _strnicmp -#else -// The strcasecmp, strncasecmp, and strcasestr functions do not work with multibyte strings (e.g. UTF-8). -// So they are only useful for ASCII in our context. -// https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings -#define simdutf_strcasecmp strcasecmp -#define simdutf_strncasecmp strncasecmp -#endif - -#ifdef NDEBUG - -#ifdef SIMDUTF_VISUAL_STUDIO -#define SIMDUTF_UNREACHABLE() __assume(0) -#define SIMDUTF_ASSUME(COND) __assume(COND) -#else -#define SIMDUTF_UNREACHABLE() __builtin_unreachable(); -#define SIMDUTF_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) -#endif - -#else // NDEBUG - -#define SIMDUTF_UNREACHABLE() assert(0); -#define SIMDUTF_ASSUME(COND) assert(COND) - -#endif - - -#if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ >= 11 -#define SIMDUTF_GCC11ORMORE 1 -#endif // __GNUC__ >= 11 -#endif // defined(__GNUC__) && !defined(__clang__) - - -#endif // SIMDUTF_PORTABILITY_H -/* end file include/simdutf/portability.h */ -/* begin file include/simdutf/avx512.h */ -#ifndef SIMDUTF_AVX512_H_ -#define SIMDUTF_AVX512_H_ - -/* - It's possible to override AVX512 settings with cmake DCMAKE_CXX_FLAGS. - - All preprocessor directives has form `SIMDUTF_HAS_AVX512{feature}`, - where a feature is a code name for extensions. - - Please see the listing below to find which are supported. -*/ - -#ifndef SIMDUTF_HAS_AVX512F -# if defined(__AVX512F__) && __AVX512F__ == 1 -# define SIMDUTF_HAS_AVX512F 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512DQ -# if defined(__AVX512DQ__) && __AVX512DQ__ == 1 -# define SIMDUTF_HAS_AVX512DQ 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512IFMA -# if defined(__AVX512IFMA__) && __AVX512IFMA__ == 1 -# define SIMDUTF_HAS_AVX512IFMA 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512CD -# if defined(__AVX512CD__) && __AVX512CD__ == 1 -# define SIMDUTF_HAS_AVX512CD 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512BW -# if defined(__AVX512BW__) && __AVX512BW__ == 1 -# define SIMDUTF_HAS_AVX512BW 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512VL -# if defined(__AVX512VL__) && __AVX512VL__ == 1 -# define SIMDUTF_HAS_AVX512VL 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512VBMI -# if defined(__AVX512VBMI__) && __AVX512VBMI__ == 1 -# define SIMDUTF_HAS_AVX512VBMI 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512VBMI2 -# if defined(__AVX512VBMI2__) && __AVX512VBMI2__ == 1 -# define SIMDUTF_HAS_AVX512VBMI2 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512VNNI -# if defined(__AVX512VNNI__) && __AVX512VNNI__ == 1 -# define SIMDUTF_HAS_AVX512VNNI 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512BITALG -# if defined(__AVX512BITALG__) && __AVX512BITALG__ == 1 -# define SIMDUTF_HAS_AVX512BITALG 1 -# endif -#endif - -#ifndef SIMDUTF_HAS_AVX512VPOPCNTDQ -# if defined(__AVX512VPOPCNTDQ__) && __AVX512VPOPCNTDQ__ == 1 -# define SIMDUTF_HAS_AVX512VPOPCNTDQ 1 -# endif -#endif - -#endif // SIMDUTF_AVX512_H_ -/* end file include/simdutf/avx512.h */ - - -#if defined(__GNUC__) - // Marks a block with a name so that MCA analysis can see it. - #define SIMDUTF_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name); - #define SIMDUTF_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name); - #define SIMDUTF_DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name); -#else - #define SIMDUTF_BEGIN_DEBUG_BLOCK(name) - #define SIMDUTF_END_DEBUG_BLOCK(name) - #define SIMDUTF_DEBUG_BLOCK(name, block) -#endif - -// Align to N-byte boundary -#define SIMDUTF_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1)) -#define SIMDUTF_ROUNDDOWN_N(a, n) ((a) & ~((n)-1)) - -#define SIMDUTF_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0) - -#if defined(SIMDUTF_REGULAR_VISUAL_STUDIO) - - #define simdutf_really_inline __forceinline - #define simdutf_never_inline __declspec(noinline) - - #define simdutf_unused - #define simdutf_warn_unused - - #ifndef simdutf_likely - #define simdutf_likely(x) x - #endif - #ifndef simdutf_unlikely - #define simdutf_unlikely(x) x - #endif - - #define SIMDUTF_PUSH_DISABLE_WARNINGS __pragma(warning( push )) - #define SIMDUTF_PUSH_DISABLE_ALL_WARNINGS __pragma(warning( push, 0 )) - #define SIMDUTF_DISABLE_VS_WARNING(WARNING_NUMBER) __pragma(warning( disable : WARNING_NUMBER )) - // Get rid of Intellisense-only warnings (Code Analysis) - // Though __has_include is C++17, it is supported in Visual Studio 2017 or better (_MSC_VER>=1910). - #ifdef __has_include - #if __has_include() - #include - #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS SIMDUTF_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS) - #endif - #endif - - #ifndef SIMDUTF_DISABLE_UNDESIRED_WARNINGS - #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS - #endif - - #define SIMDUTF_DISABLE_DEPRECATED_WARNING SIMDUTF_DISABLE_VS_WARNING(4996) - #define SIMDUTF_DISABLE_STRICT_OVERFLOW_WARNING - #define SIMDUTF_POP_DISABLE_WARNINGS __pragma(warning( pop )) - -#else // SIMDUTF_REGULAR_VISUAL_STUDIO - - #define simdutf_really_inline inline __attribute__((always_inline)) - #define simdutf_never_inline inline __attribute__((noinline)) - - #define simdutf_unused __attribute__((unused)) - #define simdutf_warn_unused __attribute__((warn_unused_result)) - - #ifndef simdutf_likely - #define simdutf_likely(x) __builtin_expect(!!(x), 1) - #endif - #ifndef simdutf_unlikely - #define simdutf_unlikely(x) __builtin_expect(!!(x), 0) - #endif - - #define SIMDUTF_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push") - // gcc doesn't seem to disable all warnings with all and extra, add warnings here as necessary - #define SIMDUTF_PUSH_DISABLE_ALL_WARNINGS SIMDUTF_PUSH_DISABLE_WARNINGS \ - SIMDUTF_DISABLE_GCC_WARNING(-Weffc++) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wall) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wconversion) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wextra) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wattributes) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wreturn-type) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wshadow) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wunused-parameter) \ - SIMDUTF_DISABLE_GCC_WARNING(-Wunused-variable) - #define SIMDUTF_PRAGMA(P) _Pragma(#P) - #define SIMDUTF_DISABLE_GCC_WARNING(WARNING) SIMDUTF_PRAGMA(GCC diagnostic ignored #WARNING) - #if defined(SIMDUTF_CLANG_VISUAL_STUDIO) - #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS SIMDUTF_DISABLE_GCC_WARNING(-Wmicrosoft-include) - #else - #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS - #endif - #define SIMDUTF_DISABLE_DEPRECATED_WARNING SIMDUTF_DISABLE_GCC_WARNING(-Wdeprecated-declarations) - #define SIMDUTF_DISABLE_STRICT_OVERFLOW_WARNING SIMDUTF_DISABLE_GCC_WARNING(-Wstrict-overflow) - #define SIMDUTF_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop") - - - -#endif // MSC_VER - -#ifndef SIMDUTF_DLLIMPORTEXPORT - #if defined(SIMDUTF_VISUAL_STUDIO) - /** - * It does not matter here whether you are using - * the regular visual studio or clang under visual - * studio. - */ - #if SIMDUTF_USING_LIBRARY - #define SIMDUTF_DLLIMPORTEXPORT __declspec(dllimport) - #else - #define SIMDUTF_DLLIMPORTEXPORT __declspec(dllexport) - #endif - #else - #define SIMDUTF_DLLIMPORTEXPORT - #endif -#endif - -/// If EXPR is an error, returns it. -#define SIMDUTF_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } } - - -#endif // SIMDUTF_COMMON_DEFS_H -/* end file include/simdutf/common_defs.h */ -/* begin file include/simdutf/encoding_types.h */ -#include - -namespace simdutf { - -enum encoding_type { - UTF8 = 1, // BOM 0xef 0xbb 0xbf - UTF16_LE = 2, // BOM 0xff 0xfe - UTF16_BE = 4, // BOM 0xfe 0xff - UTF32_LE = 8, // BOM 0xff 0xfe 0x00 0x00 - UTF32_BE = 16, // BOM 0x00 0x00 0xfe 0xff - Latin1 = 32, - - unspecified = 0 -}; - -enum endianness { - LITTLE = 0, - BIG = 1 -}; - -bool match_system(endianness e); - -std::string to_string(encoding_type bom); - -// Note that BOM for UTF8 is discouraged. -namespace BOM { - -/** - * Checks for a BOM. If not, returns unspecified - * @param input the string to process - * @param length the length of the string in code units - * @return the corresponding encoding - */ - -encoding_type check_bom(const uint8_t* byte, size_t length); -encoding_type check_bom(const char* byte, size_t length); -/** - * Returns the size, in bytes, of the BOM for a given encoding type. - * Note that UTF8 BOM are discouraged. - * @param bom the encoding type - * @return the size in bytes of the corresponding BOM - */ -size_t bom_byte_size(encoding_type bom); - -} // BOM namespace -} // simdutf namespace -/* end file include/simdutf/encoding_types.h */ -/* begin file include/simdutf/error.h */ -#ifndef SIMDUTF_ERROR_H -#define SIMDUTF_ERROR_H -namespace simdutf { - -enum error_code { - SUCCESS = 0, - HEADER_BITS, // Any byte must have fewer than 5 header bits. - TOO_SHORT, // The leading byte must be followed by N-1 continuation bytes, where N is the UTF-8 character length - // This is also the error when the input is truncated. - TOO_LONG, // We either have too many consecutive continuation bytes or the string starts with a continuation byte. - OVERLONG, // The decoded character must be above U+7F for two-byte characters, U+7FF for three-byte characters, - // and U+FFFF for four-byte characters. - TOO_LARGE, // The decoded character must be less than or equal to U+10FFFF,less than or equal than U+7F for ASCII OR less than equal than U+FF for Latin1 - SURROGATE, // The decoded character must be not be in U+D800...DFFF (UTF-8 or UTF-32) OR - // a high surrogate must be followed by a low surrogate and a low surrogate must be preceded by a high surrogate (UTF-16) OR - // there must be no surrogate at all (Latin1) - INVALID_BASE64_CHARACTER, // Found a character that cannot be part of a valid base64 string. - BASE64_INPUT_REMAINDER, // The base64 input terminates with a single character, excluding padding (=). - OUTPUT_BUFFER_TOO_SMALL, // The provided buffer is too small. - OTHER // Not related to validation/transcoding. -}; - -struct result { - error_code error; - size_t count; // In case of error, indicates the position of the error. In case of success, indicates the number of code units validated/written. - - simdutf_really_inline result() : error{error_code::SUCCESS}, count{0} {} - - simdutf_really_inline result(error_code _err, size_t _pos) : error{_err}, count{_pos} {} -}; - -} -#endif -/* end file include/simdutf/error.h */ - -SIMDUTF_PUSH_DISABLE_WARNINGS -SIMDUTF_DISABLE_UNDESIRED_WARNINGS - -// Public API -/* begin file include/simdutf/simdutf_version.h */ -// /include/simdutf/simdutf_version.h automatically generated by release.py, -// do not change by hand -#ifndef SIMDUTF_SIMDUTF_VERSION_H -#define SIMDUTF_SIMDUTF_VERSION_H - -/** The version of simdutf being used (major.minor.revision) */ -#define SIMDUTF_VERSION "5.2.8" - -namespace simdutf { -enum { - /** - * The major version (MAJOR.minor.revision) of simdutf being used. - */ - SIMDUTF_VERSION_MAJOR = 5, - /** - * The minor version (major.MINOR.revision) of simdutf being used. - */ - SIMDUTF_VERSION_MINOR = 2, - /** - * The revision (major.minor.REVISION) of simdutf being used. - */ - SIMDUTF_VERSION_REVISION = 8 -}; -} // namespace simdutf - -#endif // SIMDUTF_SIMDUTF_VERSION_H -/* end file include/simdutf/simdutf_version.h */ -/* begin file include/simdutf/implementation.h */ -#ifndef SIMDUTF_IMPLEMENTATION_H -#define SIMDUTF_IMPLEMENTATION_H -#include -#if !defined(SIMDUTF_NO_THREADS) -#include -#endif -#include -#include -/* begin file include/simdutf/internal/isadetection.h */ -/* From -https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h -Highly modified. - -Copyright (c) 2016- Facebook, Inc (Adam Paszke) -Copyright (c) 2014- Facebook, Inc (Soumith Chintala) -Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) -Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) -Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) -Copyright (c) 2011-2013 NYU (Clement Farabet) -Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, -Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute -(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, -Samy Bengio, Johnny Mariethoz) - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories -America and IDIAP Research Institute nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef SIMDutf_INTERNAL_ISADETECTION_H -#define SIMDutf_INTERNAL_ISADETECTION_H - -#include -#include -#if defined(_MSC_VER) -#include -#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) -#include -#endif - - -// RISC-V ISA detection utilities -#if SIMDUTF_IS_RISCV64 && defined(__linux__) -#include // for syscall -// We define these ourselves, for backwards compatibility -struct simdutf_riscv_hwprobe { int64_t key; uint64_t value; }; -#define simdutf_riscv_hwprobe(...) syscall(258, __VA_ARGS__) -#define SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0 4 -#define SIMDUTF_RISCV_HWPROBE_IMA_V (1 << 2) -#define SIMDUTF_RISCV_HWPROBE_EXT_ZVBB (1 << 17) -#endif // SIMDUTF_IS_RISCV64 && defined(__linux__) - -namespace simdutf { -namespace internal { - -enum instruction_set { - DEFAULT = 0x0, - NEON = 0x1, - AVX2 = 0x4, - SSE42 = 0x8, - PCLMULQDQ = 0x10, - BMI1 = 0x20, - BMI2 = 0x40, - ALTIVEC = 0x80, - AVX512F = 0x100, - AVX512DQ = 0x200, - AVX512IFMA = 0x400, - AVX512PF = 0x800, - AVX512ER = 0x1000, - AVX512CD = 0x2000, - AVX512BW = 0x4000, - AVX512VL = 0x8000, - AVX512VBMI2 = 0x10000, - AVX512VPOPCNTDQ = 0x2000, - RVV = 0x4000, - ZVBB = 0x8000, -}; - -#if defined(__PPC64__) - -static inline uint32_t detect_supported_architectures() { - return instruction_set::ALTIVEC; -} - -#elif SIMDUTF_IS_RISCV64 - -static inline uint32_t detect_supported_architectures() { - uint32_t host_isa = instruction_set::DEFAULT; -#if SIMDUTF_IS_RVV - host_isa |= instruction_set::RVV; -#endif -#if SIMDUTF_IS_ZVBB - host_isa |= instruction_set::ZVBB; -#endif -#if defined(__linux__) - simdutf_riscv_hwprobe probes[] = { { SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0, 0 } }; - long ret = simdutf_riscv_hwprobe(&probes, sizeof probes/sizeof *probes, 0, nullptr, 0); - if (ret == 0) { - uint64_t extensions = probes[0].value; - if (extensions & SIMDUTF_RISCV_HWPROBE_IMA_V) - host_isa |= instruction_set::RVV; - if (extensions & SIMDUTF_RISCV_HWPROBE_EXT_ZVBB) - host_isa |= instruction_set::ZVBB; - } -#endif -#if defined(RUN_IN_SPIKE_SIMULATOR) - // Proxy Kernel does not implement yet hwprobe syscall - host_isa |= instruction_set::RVV; -#endif - return host_isa; -} - -#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) - -static inline uint32_t detect_supported_architectures() { - return instruction_set::NEON; -} - -#elif defined(__x86_64__) || defined(_M_AMD64) // x64 - - -namespace { -namespace cpuid_bit { - // Can be found on Intel ISA Reference for CPUID - - // EAX = 0x01 - constexpr uint32_t pclmulqdq = uint32_t(1) << 1; ///< @private bit 1 of ECX for EAX=0x1 - constexpr uint32_t sse42 = uint32_t(1) << 20; ///< @private bit 20 of ECX for EAX=0x1 - constexpr uint32_t osxsave = (uint32_t(1) << 26) | (uint32_t(1) << 27); ///< @private bits 26+27 of ECX for EAX=0x1 - - // EAX = 0x7f (Structured Extended Feature Flags), ECX = 0x00 (Sub-leaf) - // See: "Table 3-8. Information Returned by CPUID Instruction" - namespace ebx { - constexpr uint32_t bmi1 = uint32_t(1) << 3; - constexpr uint32_t avx2 = uint32_t(1) << 5; - constexpr uint32_t bmi2 = uint32_t(1) << 8; - constexpr uint32_t avx512f = uint32_t(1) << 16; - constexpr uint32_t avx512dq = uint32_t(1) << 17; - constexpr uint32_t avx512ifma = uint32_t(1) << 21; - constexpr uint32_t avx512cd = uint32_t(1) << 28; - constexpr uint32_t avx512bw = uint32_t(1) << 30; - constexpr uint32_t avx512vl = uint32_t(1) << 31; - } - - namespace ecx { - constexpr uint32_t avx512vbmi = uint32_t(1) << 1; - constexpr uint32_t avx512vbmi2 = uint32_t(1) << 6; - constexpr uint32_t avx512vnni = uint32_t(1) << 11; - constexpr uint32_t avx512bitalg = uint32_t(1) << 12; - constexpr uint32_t avx512vpopcnt = uint32_t(1) << 14; - } - namespace edx { - constexpr uint32_t avx512vp2intersect = uint32_t(1) << 8; - } - namespace xcr0_bit { - constexpr uint64_t avx256_saved = uint64_t(1) << 2; ///< @private bit 2 = AVX - constexpr uint64_t avx512_saved = uint64_t(7) << 5; ///< @private bits 5,6,7 = opmask, ZMM_hi256, hi16_ZMM - } - } -} - - - -static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, - uint32_t *edx) { -#if defined(_MSC_VER) - int cpu_info[4]; - __cpuidex(cpu_info, *eax, *ecx); - *eax = cpu_info[0]; - *ebx = cpu_info[1]; - *ecx = cpu_info[2]; - *edx = cpu_info[3]; -#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) - uint32_t level = *eax; - __get_cpuid(level, eax, ebx, ecx, edx); -#else - uint32_t a = *eax, b, c = *ecx, d; - asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d)); - *eax = a; - *ebx = b; - *ecx = c; - *edx = d; -#endif -} - -static inline uint64_t xgetbv() { - #if defined(_MSC_VER) - return _xgetbv(0); - #else - uint32_t xcr0_lo, xcr0_hi; - asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0)); - return xcr0_lo | ((uint64_t)xcr0_hi << 32); - #endif - } - -static inline uint32_t detect_supported_architectures() { - uint32_t eax; - uint32_t ebx = 0; - uint32_t ecx = 0; - uint32_t edx = 0; - uint32_t host_isa = 0x0; - - // EBX for EAX=0x1 - eax = 0x1; - cpuid(&eax, &ebx, &ecx, &edx); - - if (ecx & cpuid_bit::sse42) { - host_isa |= instruction_set::SSE42; - } - - if (ecx & cpuid_bit::pclmulqdq) { - host_isa |= instruction_set::PCLMULQDQ; - } - - if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) { - return host_isa; - } - - // xgetbv for checking if the OS saves registers - uint64_t xcr0 = xgetbv(); - - if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) { - return host_isa; - } - // ECX for EAX=0x7 - eax = 0x7; - ecx = 0x0; // Sub-leaf = 0 - cpuid(&eax, &ebx, &ecx, &edx); - if (ebx & cpuid_bit::ebx::avx2) { - host_isa |= instruction_set::AVX2; - } - if (ebx & cpuid_bit::ebx::bmi1) { - host_isa |= instruction_set::BMI1; - } - if (ebx & cpuid_bit::ebx::bmi2) { - host_isa |= instruction_set::BMI2; - } - if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) == cpuid_bit::xcr0_bit::avx512_saved)) { - return host_isa; - } - if (ebx & cpuid_bit::ebx::avx512f) { - host_isa |= instruction_set::AVX512F; - } - if (ebx & cpuid_bit::ebx::avx512bw) { - host_isa |= instruction_set::AVX512BW; - } - if (ebx & cpuid_bit::ebx::avx512cd) { - host_isa |= instruction_set::AVX512CD; - } - if (ebx & cpuid_bit::ebx::avx512dq) { - host_isa |= instruction_set::AVX512DQ; - } - if (ebx & cpuid_bit::ebx::avx512vl) { - host_isa |= instruction_set::AVX512VL; - } - if (ecx & cpuid_bit::ecx::avx512vbmi2) { - host_isa |= instruction_set::AVX512VBMI2; - } - if (ecx & cpuid_bit::ecx::avx512vpopcnt) { - host_isa |= instruction_set::AVX512VPOPCNTDQ; - } - return host_isa; -} -#else // fallback - -// includes 32-bit ARM. -static inline uint32_t detect_supported_architectures() { - return instruction_set::DEFAULT; -} - - -#endif // end SIMD extension detection code - -} // namespace internal -} // namespace simdutf - -#endif // SIMDutf_INTERNAL_ISADETECTION_H -/* end file include/simdutf/internal/isadetection.h */ - - -namespace simdutf { - -/** - * Autodetect the encoding of the input, a single encoding is recommended. - * E.g., the function might return simdutf::encoding_type::UTF8, - * simdutf::encoding_type::UTF16_LE, simdutf::encoding_type::UTF16_BE, or - * simdutf::encoding_type::UTF32_LE. - * - * @param input the string to analyze. - * @param length the length of the string in bytes. - * @return the detected encoding type - */ -simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const char * input, size_t length) noexcept; -simdutf_really_inline simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const uint8_t * input, size_t length) noexcept { - return autodetect_encoding(reinterpret_cast(input), length); -} - -/** - * Autodetect the possible encodings of the input in one pass. - * E.g., if the input might be UTF-16LE or UTF-8, this function returns - * the value (simdutf::encoding_type::UTF8 | simdutf::encoding_type::UTF16_LE). - * - * Overridden by each implementation. - * - * @param input the string to analyze. - * @param length the length of the string in bytes. - * @return the detected encoding type - */ -simdutf_warn_unused int detect_encodings(const char * input, size_t length) noexcept; -simdutf_really_inline simdutf_warn_unused int detect_encodings(const uint8_t * input, size_t length) noexcept { - return detect_encodings(reinterpret_cast(input), length); -} - -/** - * Validate the UTF-8 string. This function may be best when you expect - * the input to be almost always valid. Otherwise, consider using - * validate_utf8_with_errors. - * - * Overridden by each implementation. - * - * @param buf the UTF-8 string to validate. - * @param len the length of the string in bytes. - * @return true if and only if the string is valid UTF-8. - */ -simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept; - -/** - * Validate the UTF-8 string and stop on error. - * - * Overridden by each implementation. - * - * @param buf the UTF-8 string to validate. - * @param len the length of the string in bytes. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ -simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) noexcept; - -/** - * Validate the ASCII string. - * - * Overridden by each implementation. - * - * @param buf the ASCII string to validate. - * @param len the length of the string in bytes. - * @return true if and only if the string is valid ASCII. - */ -simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept; - -/** - * Validate the ASCII string and stop on error. It might be faster than - * validate_utf8 when an error is expected to occur early. - * - * Overridden by each implementation. - * - * @param buf the ASCII string to validate. - * @param len the length of the string in bytes. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ -simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) noexcept; - -/** - * Using native endianness; Validate the UTF-16 string. - * This function may be best when you expect the input to be almost always valid. - * Otherwise, consider using validate_utf16_with_errors. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16 string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return true if and only if the string is valid UTF-16. - */ -simdutf_warn_unused bool validate_utf16(const char16_t *buf, size_t len) noexcept; - -/** - * Validate the UTF-16LE string. This function may be best when you expect - * the input to be almost always valid. Otherwise, consider using - * validate_utf16le_with_errors. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16LE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return true if and only if the string is valid UTF-16LE. - */ -simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) noexcept; - -/** - * Validate the UTF-16BE string. This function may be best when you expect - * the input to be almost always valid. Otherwise, consider using - * validate_utf16be_with_errors. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16BE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return true if and only if the string is valid UTF-16BE. - */ -simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) noexcept; - -/** - * Using native endianness; Validate the UTF-16 string and stop on error. - * It might be faster than validate_utf16 when an error is expected to occur early. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16 string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ -simdutf_warn_unused result validate_utf16_with_errors(const char16_t *buf, size_t len) noexcept; - -/** - * Validate the UTF-16LE string and stop on error. It might be faster than - * validate_utf16le when an error is expected to occur early. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16LE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ -simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) noexcept; - -/** - * Validate the UTF-16BE string and stop on error. It might be faster than - * validate_utf16be when an error is expected to occur early. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16BE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ -simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) noexcept; - -/** - * Validate the UTF-32 string. This function may be best when you expect - * the input to be almost always valid. Otherwise, consider using - * validate_utf32_with_errors. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-32 string to validate. - * @param len the length of the string in number of 4-byte code units (char32_t). - * @return true if and only if the string is valid UTF-32. - */ -simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) noexcept; - -/** - * Validate the UTF-32 string and stop on error. It might be faster than - * validate_utf32 when an error is expected to occur early. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-32 string to validate. - * @param len the length of the string in number of 4-byte code units (char32_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ -simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) noexcept; - - /** - * Convert Latin1 string into UTF8 string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return the number of written char; 0 if conversion is not possible - */ - simdutf_warn_unused size_t convert_latin1_to_utf8(const char * input, size_t length, char* utf8_output) noexcept; - - - /** - * Convert possibly Latin1 string into UTF-16LE string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if conversion is not possible - */ - simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * input, size_t length, char16_t* utf16_output) noexcept; - - /** - * Convert Latin1 string into UTF-16BE string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if conversion is not possible - */ - simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * input, size_t length, char16_t* utf16_output) noexcept; - - /** - * Convert Latin1 string into UTF-32 string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return the number of written char32_t; 0 if conversion is not possible - */ - simdutf_warn_unused size_t convert_latin1_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) noexcept; - - /** - * Convert possibly broken UTF-8 string into latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return the number of written char; 0 if the input was not valid UTF-8 string or if it cannot be represented as Latin1 - */ - simdutf_warn_unused size_t convert_utf8_to_latin1(const char * input, size_t length, char* latin1_output) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-8 string into a UTF-16 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if the input was not valid UTF-8 string - */ -simdutf_warn_unused size_t convert_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_output) noexcept; - - -/** - * Using native endianness, convert a Latin1 string into a UTF-16 string. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t. - */ -simdutf_warn_unused size_t convert_latin1_to_utf16(const char * input, size_t length, char16_t* utf16_output) noexcept; - -/** - * Convert possibly broken UTF-8 string into UTF-16LE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if the input was not valid UTF-8 string - */ -simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_output) noexcept; - -/** - * Convert possibly broken UTF-8 string into UTF-16BE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if the input was not valid UTF-8 string - */ -simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_output) noexcept; - - - /** - * Convert possibly broken UTF-8 string into latin1 string with errors. - * If the string cannot be represented as Latin1, an error - * code is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * input, size_t length, char* latin1_output) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-8 string into UTF-16 - * string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ -simdutf_warn_unused result convert_utf8_to_utf16_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept; - -/** - * Convert possibly broken UTF-8 string into UTF-16LE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ -simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept; - -/** - * Convert possibly broken UTF-8 string into UTF-16BE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ -simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept; - -/** - * Convert possibly broken UTF-8 string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return the number of written char32_t; 0 if the input was not valid UTF-8 string - */ -simdutf_warn_unused size_t convert_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_output) noexcept; - -/** - * Convert possibly broken UTF-8 string into UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ -simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * input, size_t length, char32_t* utf32_output) noexcept; - - /** - * Convert valid UTF-8 string into latin1 string. - * - * This function assumes that the input string is valid UTF-8. - * - * This function is not BOM-aware. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return the number of written char; 0 if the input was not valid UTF-8 string - */ - simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * input, size_t length, char* latin1_output) noexcept; - - -/** - * Using native endianness, convert valid UTF-8 string into a UTF-16 string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t - */ -simdutf_warn_unused size_t convert_valid_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert valid UTF-8 string into UTF-16LE string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t - */ -simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert valid UTF-8 string into UTF-16BE string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t - */ -simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert valid UTF-8 string into UTF-32 string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return the number of written char32_t - */ -simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) noexcept; - - -/** - * Return the number of bytes that this Latin1 string would require in UTF-8 format. - * - * @param input the Latin1 string to convert - * @param length the length of the string bytes - * @return the number of bytes required to encode the Latin1 string as UTF-8 - */ -simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) noexcept; - -/** - * Compute the number of bytes that this UTF-8 string would require in Latin1 format. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in byte - * @return the number of bytes required to encode the UTF-8 string as Latin1 - */ -simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) noexcept; - -/** - * Compute the number of 2-byte code units that this UTF-8 string would require in UTF-16LE format. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return the number of char16_t code units required to encode the UTF-8 string as UTF-16LE - */ -simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) noexcept; - -/** - * Compute the number of 4-byte code units that this UTF-8 string would require in UTF-32 format. - * - * This function is equivalent to count_utf8 - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return the number of char32_t code units required to encode the UTF-8 string as UTF-32 - */ -simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-16 string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ -simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - - - -/** - * Using native endianness, convert possibly broken UTF-16 string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16 string or if it cannot be represented as Latin1 - */ -simdutf_warn_unused size_t convert_utf16_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert possibly broken UTF-16LE string into Latin1 string. - * If the string cannot be represented as Latin1, an error - * is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string or if it cannot be represented as Latin1 - */ -simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert possibly broken UTF-16BE string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16BE string or if it cannot be represented as Latin1 - */ -simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - - -/** - * Convert possibly broken UTF-16LE string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ -simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Convert possibly broken UTF-16BE string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ -simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-16 string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf16_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert possibly broken UTF-16LE string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert possibly broken UTF-16BE string into Latin1 string. - * If the string cannot be represented as Latin1, an error - * is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - - -/** - * Using native endianness, convert possibly broken UTF-16 string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf16_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Convert possibly broken UTF-16LE string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Convert possibly broken UTF-16BE string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Using native endianness, convert valid UTF-16 string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - - -/** - * Using native endianness, convert UTF-16 string into Latin1 string. - * - * This function assumes that the input string is valid UTF-8. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert valid UTF-16LE string into Latin1 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert valid UTF-16BE string into Latin1 string. - * - * This function assumes that the input string is valid UTF-16BE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept; - - -/** - * Convert valid UTF-16LE string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Convert valid UTF-16BE string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-16BE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-16 string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ -simdutf_warn_unused size_t convert_utf16_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Convert possibly broken UTF-16LE string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ -simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Convert possibly broken UTF-16BE string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ -simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-16 string into - * UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ -simdutf_warn_unused result convert_utf16_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Convert possibly broken UTF-16LE string into UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ -simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Convert possibly broken UTF-16BE string into UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ -simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Using native endianness, convert valid UTF-16 string into UTF-32 string. - * - * This function assumes that the input string is valid UTF-16 (native endianness). - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Convert valid UTF-16LE string into UTF-32 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - -/** - * Convert valid UTF-16BE string into UTF-32 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept; - - -/* - * Compute the number of bytes that this UTF-16LE/BE string would require in Latin1 format. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as Latin1 - */ -simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) noexcept; - - -/** - * Using native endianness; Compute the number of bytes that this UTF-16 - * string would require in UTF-8 format. - * - * This function does not validate the input. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as UTF-8 - */ -simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t * input, size_t length) noexcept; - -/** - * Compute the number of bytes that this UTF-16LE string would require in UTF-8 format. - * - * This function does not validate the input. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as UTF-8 - */ -simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) noexcept; - -/** - * Compute the number of bytes that this UTF-16BE string would require in UTF-8 format. - * - * This function does not validate the input. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16BE string as UTF-8 - */ -simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) noexcept; - -/** - * Convert possibly broken UTF-32 string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ -simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Convert possibly broken UTF-32 string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Convert valid UTF-32 string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-32 string into a UTF-16 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ -simdutf_warn_unused size_t convert_utf32_to_utf16(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert possibly broken UTF-32 string into UTF-16LE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ -simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert possibly broken UTF-32 string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string or if it cannot be represented as Latin1 - */ -simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) noexcept; - - -/** - * Convert possibly broken UTF-32 string into Latin1 string and stop on error. - * If the string cannot be represented as Latin1, an error is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ -simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert valid UTF-32 string into Latin1 string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param latin1_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) noexcept; - -/** - * Convert possibly broken UTF-32 string into UTF-16BE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ -simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Using native endianness, convert possibly broken UTF-32 string into UTF-16 - * string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ -simdutf_warn_unused result convert_utf32_to_utf16_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert possibly broken UTF-32 string into UTF-16LE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ -simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert possibly broken UTF-32 string into UTF-16BE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ -simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Using native endianness, convert valid UTF-32 string into a UTF-16 string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf32_to_utf16(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert valid UTF-32 string into UTF-16LE string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Convert valid UTF-32 string into UTF-16BE string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ -simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept; - -/** - * Change the endianness of the input. Can be used to go from UTF-16LE to UTF-16BE or - * from UTF-16BE to UTF-16LE. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @param output the pointer to buffer that can hold the conversion result - */ -void change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) noexcept; - -/** - * Compute the number of bytes that this UTF-32 string would require in UTF-8 format. - * - * This function does not validate the input. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @return the number of bytes required to encode the UTF-32 string as UTF-8 - */ -simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) noexcept; - -/** - * Compute the number of two-byte code units that this UTF-32 string would require in UTF-16 format. - * - * This function does not validate the input. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @return the number of bytes required to encode the UTF-32 string as UTF-16 - */ -simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) noexcept; - -/** - * Using native endianness; Compute the number of bytes that this UTF-16 - * string would require in UTF-32 format. - * - * This function is equivalent to count_utf16. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as UTF-32 - */ -simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t * input, size_t length) noexcept; - -/** - * Compute the number of bytes that this UTF-16LE string would require in UTF-32 format. - * - * This function is equivalent to count_utf16le. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as UTF-32 - */ -simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) noexcept; - -/** - * Compute the number of bytes that this UTF-16BE string would require in UTF-32 format. - * - * This function is equivalent to count_utf16be. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16BE string as UTF-32 - */ -simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) noexcept; - -/** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-16 (native endianness). - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @return number of code points - */ -simdutf_warn_unused size_t count_utf16(const char16_t * input, size_t length) noexcept; - -/** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @return number of code points - */ -simdutf_warn_unused size_t count_utf16le(const char16_t * input, size_t length) noexcept; - -/** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-16BE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @return number of code points - */ -simdutf_warn_unused size_t count_utf16be(const char16_t * input, size_t length) noexcept; - -/** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return number of code points - */ -simdutf_warn_unused size_t count_utf8(const char * input, size_t length) noexcept; - -/** - * Given a valid UTF-8 string having a possibly truncated last character, - * this function checks the end of string. If the last character is truncated (or partial), - * then it returns a shorter length (shorter by 1 to 3 bytes) so that the short UTF-8 - * strings only contain complete characters. If there is no truncated character, - * the original length is returned. - * - * This function assumes that the input string is valid UTF-8, but possibly truncated. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return the length of the string in bytes, possibly shorter by 1 to 3 bytes - */ -simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length); - -/** - * Given a valid UTF-16BE string having a possibly truncated last character, - * this function checks the end of string. If the last character is truncated (or partial), - * then it returns a shorter length (shorter by 1 unit) so that the short UTF-16BE - * strings only contain complete characters. If there is no truncated character, - * the original length is returned. - * - * This function assumes that the input string is valid UTF-16BE, but possibly truncated. - * - * @param input the UTF-16BE string to process - * @param length the length of the string in bytes - * @return the length of the string in bytes, possibly shorter by 1 unit - */ -simdutf_warn_unused size_t trim_partial_utf16be(const char16_t* input, size_t length); - -/** - * Given a valid UTF-16LE string having a possibly truncated last character, - * this function checks the end of string. If the last character is truncated (or partial), - * then it returns a shorter length (shorter by 1 unit) so that the short UTF-16LE - * strings only contain complete characters. If there is no truncated character, - * the original length is returned. - * - * This function assumes that the input string is valid UTF-16LE, but possibly truncated. - * - * @param input the UTF-16LE string to process - * @param length the length of the string in bytes - * @return the length of the string in unit, possibly shorter by 1 unit - */ -simdutf_warn_unused size_t trim_partial_utf16le(const char16_t* input, size_t length); - - -/** - * Given a valid UTF-16 string having a possibly truncated last character, - * this function checks the end of string. If the last character is truncated (or partial), - * then it returns a shorter length (shorter by 1 unit) so that the short UTF-16 - * strings only contain complete characters. If there is no truncated character, - * the original length is returned. - * - * This function assumes that the input string is valid UTF-16, but possibly truncated. - * We use the native endianness. - * - * @param input the UTF-16 string to process - * @param length the length of the string in bytes - * @return the length of the string in unit, possibly shorter by 1 unit - */ -simdutf_warn_unused size_t trim_partial_utf16(const char16_t* input, size_t length); - -// base64_options are used to specify the base64 encoding options. -using base64_options = uint64_t; -enum : base64_options { - base64_default = 0, /* standard base64 format */ - base64_url = 1 /* base64url format*/ -}; - -/** - * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less than - * the maximum length. - * - * @param input the base64 input to process - * @param length the length of the base64 input in bytes - * @return maximum number of binary bytes - */ -simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) noexcept; - -/** - * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less than - * the maximum length. - * - * @param input the base64 input to process, in ASCII stored as 16-bit units - * @param length the length of the base64 input in 16-bit units - * @return maximal number of binary bytes - */ -simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) noexcept; - -/** - * Convert a base64 input to a binary ouput. - * - * This function follows the WHATWG forgiving-base64 format, which means that it will - * ignore any ASCII spaces in the input. You may provide a padded input (with one or two - * equal signs at the end) or an unpadded input (without any equal signs at the end). - * - * See https://infra.spec.whatwg.org/#forgiving-base64-decode - * - * This function will fail in case of invalid input. There are two possible reasons for - * failure: the input contains a number of base64 characters that when divided by 4, leaves - * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character - * that is not a valid base64 character (INVALID_BASE64_CHARACTER). - * - * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the input - * where the invalid character was found. When the error is BASE64_INPUT_REMAINDER, then - * r.count contains the number of bytes decoded. - * - * You should call this function with a buffer that is at least maximal_binary_length_from_base64(input, length) bytes long. - * If you fail to provide that much space, the function may cause a buffer overflow. - * - * @param input the base64 string to process - * @param length the length of the string in bytes - * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long). - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in bytes) if any, or the number of bytes written if successful. - */ -simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options = base64_default) noexcept; - -/** - * Provide the base64 length in bytes given the length of a binary input. - * - * @param length the length of the input in bytes - * @return number of base64 bytes - */ -simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options = base64_default) noexcept; - -/** - * Convert a binary input to a base64 ouput. The output is always padded with equal signs so that it is - * a multiple of 4 bytes long. - * - * This function always succeeds. - * - * @param input the binary to process - * @param length the length of the input in bytes - * @param output the pointer to buffer that can hold the conversion result (should be at least base64_length_from_binary(length) bytes long) - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return number of written bytes, will be equal to base64_length_from_binary(length, options) - */ -size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options = base64_default) noexcept; - -/** - * Convert a base64 input to a binary ouput. - * - * This function follows the WHATWG forgiving-base64 format, which means that it will - * ignore any ASCII spaces in the input. You may provide a padded input (with one or two - * equal signs at the end) or an unpadded input (without any equal signs at the end). - * - * See https://infra.spec.whatwg.org/#forgiving-base64-decode - * - * This function will fail in case of invalid input. There are two possible reasons for - * failure: the input contains a number of base64 characters that when divided by 4, leaves - * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character - * that is not a valid base64 character (INVALID_BASE64_CHARACTER). - * - * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the input - * where the invalid character was found. When the error is BASE64_INPUT_REMAINDER, then - * r.count contains the number of bytes decoded. - * - * You should call this function with a buffer that is at least maximal_binary_length_from_utf6_base64(input, length) bytes long. - * If you fail to provide that much space, the function may cause a buffer overflow. - * - * @param input the base64 string to process, in ASCII stored as 16-bit units - * @param length the length of the string in 16-bit units - * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long). - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and position of the INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number of bytes written if successful. - */ -simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options = base64_default) noexcept; - -/** - * Convert a base64 input to a binary ouput. - * - * This function follows the WHATWG forgiving-base64 format, which means that it will - * ignore any ASCII spaces in the input. You may provide a padded input (with one or two - * equal signs at the end) or an unpadded input (without any equal signs at the end). - * - * See https://infra.spec.whatwg.org/#forgiving-base64-decode - * - * This function will fail in case of invalid input. There are three possible reasons for - * failure: the input contains a number of base64 characters that when divided by 4, leaves - * a single remainder character (BASE64_INPUT_REMAINDER), the input contains a character - * that is not a valid base64 character (INVALID_BASE64_CHARACTER), or the output buffer - * is too small (OUTPUT_BUFFER_TOO_SMALL). - * - * When OUTPUT_BUFFER_TOO_SMALL, we return both the number of bytes written - * and the number of units processed, see description of the parameters and returned value. - * - * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the input - * where the invalid character was found. When the error is BASE64_INPUT_REMAINDER, then - * r.count contains the number of bytes decoded. - * - * The INVALID_BASE64_CHARACTER cases are considered fatal and you are expected to discard - * the output. - * - * @param input the base64 string to process, in ASCII stored as 8-bit or 16-bit units - * @param length the length of the string in 8-bit or 16-bit units. - * @param output the pointer to buffer that can hold the conversion result. - * @param outlen the number of bytes that can be written in the output buffer. Upon return, it is modified to reflect how many bytes were written. - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and position of the INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number of units processed if successful. - */ -simdutf_warn_unused result base64_to_binary_safe(const char * input, size_t length, char* output, size_t& outlen, base64_options options = base64_default) noexcept; -simdutf_warn_unused result base64_to_binary_safe(const char16_t * input, size_t length, char* output, size_t& outlen, base64_options options = base64_default) noexcept; - -/** - * An implementation of simdutf for a particular CPU architecture. - * - * Also used to maintain the currently active implementation. The active implementation is - * automatically initialized on first use to the most advanced implementation supported by the host. - */ -class implementation { -public: - - /** - * The name of this implementation. - * - * const implementation *impl = simdutf::active_implementation; - * cout << "simdutf is optimized for " << impl->name() << "(" << impl->description() << ")" << endl; - * - * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" - */ - virtual std::string name() const { return std::string(_name); } - - /** - * The description of this implementation. - * - * const implementation *impl = simdutf::active_implementation; - * cout << "simdutf is optimized for " << impl->name() << "(" << impl->description() << ")" << endl; - * - * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" - */ - virtual std::string description() const { return std::string(_description); } - - /** - * The instruction sets this implementation is compiled against - * and the current CPU match. This function may poll the current CPU/system - * and should therefore not be called too often if performance is a concern. - * - * - * @return true if the implementation can be safely used on the current system (determined at runtime) - */ - bool supported_by_runtime_system() const; - - /** - * This function will try to detect the encoding - * @param input the string to identify - * @param length the length of the string in bytes. - * @return the encoding type detected - */ - virtual encoding_type autodetect_encoding(const char * input, size_t length) const noexcept; - - /** - * This function will try to detect the possible encodings in one pass - * @param input the string to identify - * @param length the length of the string in bytes. - * @return the encoding type detected - */ - virtual int detect_encodings(const char * input, size_t length) const noexcept = 0; - - /** - * @private For internal implementation use - * - * The instruction sets this implementation is compiled against. - * - * @return a mask of all required `internal::instruction_set::` values - */ - virtual uint32_t required_instruction_sets() const { return _required_instruction_sets; } - - - /** - * Validate the UTF-8 string. - * - * Overridden by each implementation. - * - * @param buf the UTF-8 string to validate. - * @param len the length of the string in bytes. - * @return true if and only if the string is valid UTF-8. - */ - simdutf_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-8 string and stop on errors. - * - * Overridden by each implementation. - * - * @param buf the UTF-8 string to validate. - * @param len the length of the string in bytes. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result validate_utf8_with_errors(const char *buf, size_t len) const noexcept = 0; - - /** - * Validate the ASCII string. - * - * Overridden by each implementation. - * - * @param buf the ASCII string to validate. - * @param len the length of the string in bytes. - * @return true if and only if the string is valid ASCII. - */ - simdutf_warn_unused virtual bool validate_ascii(const char *buf, size_t len) const noexcept = 0; - - /** - * Validate the ASCII string and stop on error. - * - * Overridden by each implementation. - * - * @param buf the ASCII string to validate. - * @param len the length of the string in bytes. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result validate_ascii_with_errors(const char *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-16LE string.This function may be best when you expect - * the input to be almost always valid. Otherwise, consider using - * validate_utf16le_with_errors. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16LE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return true if and only if the string is valid UTF-16LE. - */ - simdutf_warn_unused virtual bool validate_utf16le(const char16_t *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-16BE string. This function may be best when you expect - * the input to be almost always valid. Otherwise, consider using - * validate_utf16be_with_errors. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16BE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return true if and only if the string is valid UTF-16BE. - */ - simdutf_warn_unused virtual bool validate_utf16be(const char16_t *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-16LE string and stop on error. It might be faster than - * validate_utf16le when an error is expected to occur early. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16LE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-16BE string and stop on error. It might be faster than - * validate_utf16be when an error is expected to occur early. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-16BE string to validate. - * @param len the length of the string in number of 2-byte code units (char16_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-32 string. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-32 string to validate. - * @param len the length of the string in number of 4-byte code units (char32_t). - * @return true if and only if the string is valid UTF-32. - */ - simdutf_warn_unused virtual bool validate_utf32(const char32_t *buf, size_t len) const noexcept = 0; - - /** - * Validate the UTF-32 string and stop on error. - * - * Overridden by each implementation. - * - * This function is not BOM-aware. - * - * @param buf the UTF-32 string to validate. - * @param len the length of the string in number of 4-byte code units (char32_t). - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept = 0; - - /** - * Convert Latin1 string into UTF8 string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return the number of written char; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_latin1_to_utf8(const char * input, size_t length, char* utf8_output) const noexcept = 0; - - - /** - * Convert possibly Latin1 string into UTF-16LE string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_latin1_to_utf16le(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0; - - /** - * Convert Latin1 string into UTF-16BE string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_latin1_to_utf16be(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0; - - /** - * Convert Latin1 string into UTF-32 string. - * - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the Latin1 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return the number of written char32_t; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_latin1_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return the number of written char; 0 if the input was not valid UTF-8 string or if it cannot be represented as Latin1 - */ - simdutf_warn_unused virtual size_t convert_utf8_to_latin1(const char * input, size_t length, char* latin1_output) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into latin1 string with errors. - * If the string cannot be represented as Latin1, an error - * code is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result convert_utf8_to_latin1_with_errors(const char * input, size_t length, char* latin1_output) const noexcept = 0; - - /** - * Convert valid UTF-8 string into latin1 string. - * - * This function assumes that the input string is valid UTF-8. - * - * This function is not BOM-aware. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param latin1_output the pointer to buffer that can hold conversion result - * @return the number of written char; 0 if the input was not valid UTF-8 string - */ - simdutf_warn_unused virtual size_t convert_valid_utf8_to_latin1(const char * input, size_t length, char* latin1_output) const noexcept = 0; - - - /** - * Convert possibly broken UTF-8 string into UTF-16LE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if the input was not valid UTF-8 string - */ - simdutf_warn_unused virtual size_t convert_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into UTF-16BE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if the input was not valid UTF-8 string - */ - simdutf_warn_unused virtual size_t convert_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into UTF-16LE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result convert_utf8_to_utf16le_with_errors(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into UTF-16BE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful. - */ - simdutf_warn_unused virtual result convert_utf8_to_utf16be_with_errors(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t; 0 if the input was not valid UTF-8 string - */ - simdutf_warn_unused virtual size_t convert_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_output) const noexcept = 0; - - /** - * Convert possibly broken UTF-8 string into UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ - simdutf_warn_unused virtual result convert_utf8_to_utf32_with_errors(const char * input, size_t length, char32_t* utf32_output) const noexcept = 0; - - /** - * Convert valid UTF-8 string into UTF-16LE string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t - */ - simdutf_warn_unused virtual size_t convert_valid_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - -/** - * Convert valid UTF-8 string into UTF-16BE string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char16_t - */ - simdutf_warn_unused virtual size_t convert_valid_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Convert valid UTF-8 string into UTF-32 string. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in bytes - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return the number of written char32_t - */ - simdutf_warn_unused virtual size_t convert_valid_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Compute the number of 2-byte code units that this UTF-8 string would require in UTF-16LE format. - * - * This function does not validate the input. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return the number of char16_t code units required to encode the UTF-8 string as UTF-16LE - */ - simdutf_warn_unused virtual size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept = 0; - - /** - * Compute the number of 4-byte code units that this UTF-8 string would require in UTF-32 format. - * - * This function is equivalent to count_utf8. - * - * This function does not validate the input. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return the number of char32_t code units required to encode the UTF-8 string as UTF-32 - */ - simdutf_warn_unused virtual size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept = 0; - - /** - * Convert possibly broken UTF-16LE string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string or if it cannot be represented as Latin1 - */ - simdutf_warn_unused virtual size_t convert_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16BE string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16BE string or if it cannot be represented as Latin1 - */ - simdutf_warn_unused virtual size_t convert_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16LE string into Latin1 string. - * If the string cannot be represented as Latin1, an error - * is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ - simdutf_warn_unused virtual result convert_utf16le_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16BE string into Latin1 string. - * If the string cannot be represented as Latin1, an error - * is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ - simdutf_warn_unused virtual result convert_utf16be_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert valid UTF-16LE string into Latin1 string. - * - * This function assumes that the input string is valid UTF-8. - - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert valid UTF-16BE string into Latin1 string. - * - * This function assumes that the input string is valid UTF-8. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16LE string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ - simdutf_warn_unused virtual size_t convert_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16BE string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16BE string - */ - simdutf_warn_unused virtual size_t convert_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16LE string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ - simdutf_warn_unused virtual result convert_utf16le_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16BE string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ - simdutf_warn_unused virtual result convert_utf16be_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert valid UTF-16LE string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert valid UTF-16BE string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-16BE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16LE string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16LE string - */ - simdutf_warn_unused virtual size_t convert_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16BE string into UTF-32 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-16BE string - */ - simdutf_warn_unused virtual size_t convert_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16LE string into UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ - simdutf_warn_unused virtual result convert_utf16le_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-16BE string into UTF-32 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful. - */ - simdutf_warn_unused virtual result convert_utf16be_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Convert valid UTF-16LE string into UTF-32 string. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Convert valid UTF-16LE string into UTF-32BE string. - * - * This function assumes that the input string is valid UTF-16BE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @param utf32_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0; - - /** - * Compute the number of bytes that this UTF-16LE string would require in UTF-8 format. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as UTF-8 - */ - simdutf_warn_unused virtual size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept = 0; - - /** - * Compute the number of bytes that this UTF-16BE string would require in UTF-8 format. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16BE string as UTF-8 - */ - simdutf_warn_unused virtual size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into Latin1 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ - - simdutf_warn_unused virtual size_t convert_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into Latin1 string and stop on error. - * If the string cannot be represented as Latin1, an error is returned. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param latin1_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ - simdutf_warn_unused virtual result convert_utf32_to_latin1_with_errors(const char32_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert valid UTF-32 string into Latin1 string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param latin1_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into UTF-8 string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ - simdutf_warn_unused virtual size_t convert_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into UTF-8 string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf8_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful. - */ - simdutf_warn_unused virtual result convert_utf32_to_utf8_with_errors(const char32_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - /** - * Convert valid UTF-32 string into UTF-8 string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf8_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) const noexcept = 0; - - - /** - * Return the number of bytes that this UTF-16 string would require in Latin1 format. - * - * - * @param input the UTF-16 string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16 string as Latin1 - */ - simdutf_warn_unused virtual size_t utf16_length_from_latin1(size_t length) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into UTF-16LE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ - simdutf_warn_unused virtual size_t convert_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into UTF-16BE string. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return number of written code units; 0 if input is not a valid UTF-32 string - */ - simdutf_warn_unused virtual size_t convert_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into UTF-16LE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ - simdutf_warn_unused virtual result convert_utf32_to_utf16le_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Convert possibly broken UTF-32 string into UTF-16BE string and stop on error. - * - * During the conversion also validation of the input string is done. - * This function is suitable to work with inputs from untrusted sources. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold conversion result - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful. - */ - simdutf_warn_unused virtual result convert_utf32_to_utf16be_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Convert valid UTF-32 string into UTF-16LE string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Convert valid UTF-32 string into UTF-16BE string. - * - * This function assumes that the input string is valid UTF-32. - * - * This function is not BOM-aware. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @param utf16_buffer the pointer to buffer that can hold the conversion result - * @return number of written code units; 0 if conversion is not possible - */ - simdutf_warn_unused virtual size_t convert_valid_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0; - - /** - * Change the endianness of the input. Can be used to go from UTF-16LE to UTF-16BE or - * from UTF-16BE to UTF-16LE. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16 string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @param output the pointer to buffer that can hold the conversion result - */ - virtual void change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept = 0; - - /** - * Return the number of bytes that this Latin1 string would require in UTF-8 format. - * - * @param input the Latin1 string to convert - * @param length the length of the string bytes - * @return the number of bytes required to encode the Latin1 string as UTF-8 - */ - simdutf_warn_unused virtual size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept = 0; - - /** - * Compute the number of bytes that this UTF-32 string would require in UTF-8 format. - * - * This function does not validate the input. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @return the number of bytes required to encode the UTF-32 string as UTF-8 - */ - simdutf_warn_unused virtual size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept = 0; - - /** - * Compute the number of bytes that this UTF-32 string would require in Latin1 format. - * - * This function does not validate the input. - * - * @param length the length of the string in 4-byte code units (char32_t) - * @return the number of bytes required to encode the UTF-32 string as Latin1 - */ - simdutf_warn_unused virtual size_t latin1_length_from_utf32(size_t length) const noexcept = 0; - - /** - * Compute the number of bytes that this UTF-8 string would require in Latin1 format. - * - * This function does not validate the input. - * - * @param input the UTF-8 string to convert - * @param length the length of the string in byte - * @return the number of bytes required to encode the UTF-8 string as Latin1 - */ - simdutf_warn_unused virtual size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept = 0; - - /* - * Compute the number of bytes that this UTF-16LE/BE string would require in Latin1 format. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as Latin1 - */ - simdutf_warn_unused virtual size_t latin1_length_from_utf16(size_t length) const noexcept = 0; - - /** - * Compute the number of two-byte code units that this UTF-32 string would require in UTF-16 format. - * - * This function does not validate the input. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @return the number of bytes required to encode the UTF-32 string as UTF-16 - */ - simdutf_warn_unused virtual size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept = 0; - - - /** - * Return the number of bytes that this UTF-32 string would require in Latin1 format. - * - * This function does not validate the input. - * - * @param input the UTF-32 string to convert - * @param length the length of the string in 4-byte code units (char32_t) - * @return the number of bytes required to encode the UTF-32 string as Latin1 - */ - simdutf_warn_unused virtual size_t utf32_length_from_latin1(size_t length) const noexcept = 0; - - /* - * Compute the number of bytes that this UTF-16LE string would require in UTF-32 format. - * - * This function is equivalent to count_utf16le. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16LE string as UTF-32 - */ - simdutf_warn_unused virtual size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept = 0; - - /* - * Compute the number of bytes that this UTF-16BE string would require in UTF-32 format. - * - * This function is equivalent to count_utf16be. - * - * This function does not validate the input. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to convert - * @param length the length of the string in 2-byte code units (char16_t) - * @return the number of bytes required to encode the UTF-16BE string as UTF-32 - */ - simdutf_warn_unused virtual size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept = 0; - - /** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-16LE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16LE string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @return number of code points - */ - simdutf_warn_unused virtual size_t count_utf16le(const char16_t * input, size_t length) const noexcept = 0; - - /** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-16BE. - * - * This function is not BOM-aware. - * - * @param input the UTF-16BE string to process - * @param length the length of the string in 2-byte code units (char16_t) - * @return number of code points - */ - simdutf_warn_unused virtual size_t count_utf16be(const char16_t * input, size_t length) const noexcept = 0; - - - /** - * Count the number of code points (characters) in the string assuming that - * it is valid. - * - * This function assumes that the input string is valid UTF-8. - * - * @param input the UTF-8 string to process - * @param length the length of the string in bytes - * @return number of code points - */ - simdutf_warn_unused virtual size_t count_utf8(const char * input, size_t length) const noexcept = 0; - - /** - * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less than - * the maximum length. - * - * @param input the base64 input to process - * @param length the length of the base64 input in bytes - * @return maximal number of binary bytes - */ - simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept = 0; - - /** - * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less than - * the maximum length. - * - * @param input the base64 input to process, in ASCII stored as 16-bit units - * @param length the length of the base64 input in 16-bit units - * @return maximal number of binary bytes - */ - simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept = 0; - - /** - * Convert a base64 input to a binary ouput. - * - * This function follows the WHATWG forgiving-base64 format, which means that it will - * ignore any ASCII spaces in the input. You may provide a padded input (with one or two - * equal signs at the end) or an unpadded input (without any equal signs at the end). - * - * See https://infra.spec.whatwg.org/#forgiving-base64-decode - * - * This function will fail in case of invalid input. There are two possible reasons for - * failure: the input contains a number of base64 characters that when divided by 4, leaves - * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character - * that is not a valid base64 character (INVALID_BASE64_CHARACTER). - * - * You should call this function with a buffer that is at least maximal_binary_length_from_base64(input, length) bytes long. - * If you fail to provide that much space, the function may cause a buffer overflow. - * - * @param input the base64 string to process - * @param length the length of the string in bytes - * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long). - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in bytes) if any, or the number of bytes written if successful. - */ - simdutf_warn_unused virtual result base64_to_binary(const char * input, size_t length, char* output, base64_options options = base64_default) const noexcept = 0; - - /** - * Convert a base64 input to a binary ouput. - * - * This function follows the WHATWG forgiving-base64 format, which means that it will - * ignore any ASCII spaces in the input. You may provide a padded input (with one or two - * equal signs at the end) or an unpadded input (without any equal signs at the end). - * - * See https://infra.spec.whatwg.org/#forgiving-base64-decode - * - * This function will fail in case of invalid input. There are two possible reasons for - * failure: the input contains a number of base64 characters that when divided by 4, leaves - * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character - * that is not a valid base64 character (INVALID_BASE64_CHARACTER). - * - * You should call this function with a buffer that is at least maximal_binary_length_from_utf6_base64(input, length) bytes long. - * If you fail to provide that much space, the function may cause a buffer overflow. - * - * @param input the base64 string to process, in ASCII stored as 16-bit units - * @param length the length of the string in 16-bit units - * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long). - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and position of the INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number of bytes written if successful. - */ - simdutf_warn_unused virtual result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options = base64_default) const noexcept = 0; - - /** - * Provide the base64 length in bytes given the length of a binary input. - * - * @param length the length of the input in bytes - * @parem options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return number of base64 bytes - */ - simdutf_warn_unused virtual size_t base64_length_from_binary(size_t length, base64_options options = base64_default) const noexcept = 0; - - /** - * Convert a binary input to a base64 ouput. The output is always padded with equal signs so that it is - * a multiple of 4 bytes long. - * - * This function always succeeds. - * - * @param input the binary to process - * @param length the length of the input in bytes - * @param output the pointer to buffer that can hold the conversion result (should be at least base64_length_from_binary(length) bytes long) - * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default. - * @return number of written bytes, will be equal to base64_length_from_binary(length, options) - */ - virtual size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options = base64_default) const noexcept = 0; - - -protected: - /** @private Construct an implementation with the given name and description. For subclasses. */ - simdutf_really_inline implementation( - const char* name, - const char* description, - uint32_t required_instruction_sets - ) : - _name(name), - _description(description), - _required_instruction_sets(required_instruction_sets) - { - } -protected: - ~implementation() = default; -private: - /** - * The name of this implementation. - */ - const char* _name; - - /** - * The description of this implementation. - */ - const char* _description; - - /** - * Instruction sets required for this implementation. - */ - const uint32_t _required_instruction_sets; -}; - -/** @private */ -namespace internal { - -/** - * The list of available implementations compiled into simdutf. - */ -class available_implementation_list { -public: - /** Get the list of available implementations compiled into simdutf */ - simdutf_really_inline available_implementation_list() {} - /** Number of implementations */ - size_t size() const noexcept; - /** STL const begin() iterator */ - const implementation * const *begin() const noexcept; - /** STL const end() iterator */ - const implementation * const *end() const noexcept; - - /** - * Get the implementation with the given name. - * - * Case sensitive. - * - * const implementation *impl = simdutf::available_implementations["westmere"]; - * if (!impl) { exit(1); } - * if (!imp->supported_by_runtime_system()) { exit(1); } - * simdutf::active_implementation = impl; - * - * @param name the implementation to find, e.g. "westmere", "haswell", "arm64" - * @return the implementation, or nullptr if the parse failed. - */ - const implementation * operator[](const std::string &name) const noexcept { - for (const implementation * impl : *this) { - if (impl->name() == name) { return impl; } - } - return nullptr; - } - - /** - * Detect the most advanced implementation supported by the current host. - * - * This is used to initialize the implementation on startup. - * - * const implementation *impl = simdutf::available_implementation::detect_best_supported(); - * simdutf::active_implementation = impl; - * - * @return the most advanced supported implementation for the current host, or an - * implementation that returns UNSUPPORTED_ARCHITECTURE if there is no supported - * implementation. Will never return nullptr. - */ - const implementation *detect_best_supported() const noexcept; -}; - -template -class atomic_ptr { -public: - atomic_ptr(T *_ptr) : ptr{_ptr} {} - -#if defined(SIMDUTF_NO_THREADS) - operator const T*() const { return ptr; } - const T& operator*() const { return *ptr; } - const T* operator->() const { return ptr; } - - operator T*() { return ptr; } - T& operator*() { return *ptr; } - T* operator->() { return ptr; } - atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } - -#else - operator const T*() const { return ptr.load(); } - const T& operator*() const { return *ptr; } - const T* operator->() const { return ptr.load(); } - - operator T*() { return ptr.load(); } - T& operator*() { return *ptr; } - T* operator->() { return ptr.load(); } - atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } - -#endif - -private: -#if defined(SIMDUTF_NO_THREADS) - T* ptr; -#else - std::atomic ptr; -#endif -}; - -class detect_best_supported_implementation_on_first_use; - -} // namespace internal - -/** - * The list of available implementations compiled into simdutf. - */ -extern SIMDUTF_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations(); - -/** - * The active implementation. - * - * Automatically initialized on first use to the most advanced implementation supported by this hardware. - */ -extern SIMDUTF_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation(); - - -} // namespace simdutf - -#endif // SIMDUTF_IMPLEMENTATION_H -/* end file include/simdutf/implementation.h */ - - -// Implementation-internal files (must be included before the implementations themselves, to keep -// amalgamation working--otherwise, the first time a file is included, it might be put inside the -// #ifdef SIMDUTF_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't -// compile unless that implementation is turned on). - - -SIMDUTF_POP_DISABLE_WARNINGS - -#endif // SIMDUTF_H -/* end file include/simdutf.h */ diff --git a/src/bun.js/bindings/sqlite/CMakeLists.txt b/src/bun.js/bindings/sqlite/CMakeLists.txt new file mode 100644 index 0000000000000..9609feb3d38f4 --- /dev/null +++ b/src/bun.js/bindings/sqlite/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.28) +project(sqlite3 C) + +add_library(sqlite3 STATIC sqlite3.c) +target_include_directories(sqlite3 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(sqlite3 PRIVATE + "SQLITE_ENABLE_COLUMN_METADATA=" + "SQLITE_MAX_VARIABLE_NUMBER=250000" + "SQLITE_ENABLE_RTREE=1" + "SQLITE_ENABLE_FTS3=1" + "SQLITE_ENABLE_FTS3_PARENTHESIS=1" + "SQLITE_ENABLE_FTS5=1" + "SQLITE_ENABLE_JSON1=1" + "SQLITE_ENABLE_MATH_FUNCTIONS=1" +) + +if(WIN32) + target_compile_options(sqlite3 PRIVATE /MT /U_DLL) +endif() diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp index d330c3d7ef220..8c5ab8a0aa9f5 100644 --- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp +++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp @@ -38,7 +38,7 @@ #include "DOMJITIDLTypeFilter.h" #include "DOMJITHelpers.h" #include -#include "simdutf.h" +#include "wtf/SIMDUTF.h" #include #include "BunBuiltinNames.h" #include "sqlite3_error_codes.h" @@ -193,17 +193,44 @@ static inline JSC::JSValue jsBigIntFromSQLite(JSC::JSGlobalObject* globalObject, return JSValue::encode(jsUndefined()); \ } +#define CHECK_PREPARED_JIT \ + if (UNLIKELY(castedThis->stmt == nullptr || castedThis->version_db == nullptr)) { \ + throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Statement has finalized"_s)); \ + return {}; \ + } + +DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(VersionSqlite3); + class VersionSqlite3 { + WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(VersionSqlite3); + public: explicit VersionSqlite3(sqlite3* db) : db(db) , version(0) + , reference_count(1) { } sqlite3* db; std::atomic version; + size_t reference_count; + + void release() + { + ASSERT(reference_count > 0); + --reference_count; + if (reference_count == 0) { + if (!db) { + return; + } + sqlite3_close_v2(db); + db = nullptr; + } + }; }; +DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(VersionSqlite3); + class SQLiteSingleton { public: Vector databases; @@ -394,6 +421,9 @@ class JSSQLStatement : public JSC::JSDestructibleObject { { Structure* structure = globalObject->JSSQLStatementStructure(); JSSQLStatement* ptr = new (NotNull, JSC::allocateCell(globalObject->vm())) JSSQLStatement(structure, *globalObject, stmt, version_db, memorySizeChange); + if (version_db) { + ++version_db->reference_count; + } ptr->finishCreation(globalObject->vm()); return ptr; } @@ -502,21 +532,10 @@ static JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, sqlite3_stmt return jsNull(); } -extern "C" { -static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsSQLStatementExecuteStatementFunctionGetWithoutTypeChecking, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSSQLStatement* castedThis)); -} - -static const JSC::DOMJIT::Signature DOMJITSignatureForjsSQLStatementExecuteStatementFunctionGet( - jsSQLStatementExecuteStatementFunctionGetWithoutTypeChecking, - JSSQLStatement::info(), - // We use HeapRange::top() because MiscFields and SideState and HeapObjectIdentity were not enough to tell the compiler that it cannot skip calling the function. - // https://github.com/oven-sh/bun/issues/7694 - JSC::DOMJIT::Effect::forDef(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), - JSC::SpecFinalObject); static const HashTableValue JSSQLStatementPrototypeTableValues[] = { { "run"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsSQLStatementExecuteStatementFunctionRun, 1 } }, - { "get"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DOMJITFunction), NoIntrinsic, { HashTableValue::DOMJITFunctionType, jsSQLStatementExecuteStatementFunctionGet, &DOMJITSignatureForjsSQLStatementExecuteStatementFunctionGet } }, + { "get"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsSQLStatementExecuteStatementFunctionGet, 1 } }, { "all"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsSQLStatementExecuteStatementFunctionAll, 1 } }, { "as"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsSQLStatementSetPrototypeFunction, 1 } }, { "values"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsSQLStatementExecuteStatementFunctionRows, 1 } }, @@ -764,7 +783,7 @@ static inline bool rebindValue(JSC::JSGlobalObject* lexicalGlobalObject, sqlite3 return false; } - auto roped = str->tryGetValue(lexicalGlobalObject); + String roped = str->tryGetValue(lexicalGlobalObject); if (UNLIKELY(!roped)) { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Out of memory :("_s)); return false; @@ -863,7 +882,7 @@ static JSC::JSValue rebindObject(JSC::JSGlobalObject* globalObject, SQLiteBindin JSValue value = getValue(name, i); if (!value && !scope.exception()) { if (throwOnMissing) { - throwException(globalObject, scope, createError(globalObject, makeString("Missing parameter \""_s, name, "\""_s))); + throwException(globalObject, scope, createError(globalObject, makeString("Missing parameter \""_s, reinterpret_cast(name), "\""_s))); } else { continue; } @@ -1610,12 +1629,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementOpenStatementFunction, (JSC::JSGlobalObje databases().append(new VersionSqlite3(db)); if (finalizationTarget.isObject()) { vm.heap.addFinalizer(finalizationTarget.getObject(), [index](JSC::JSCell* ptr) -> void { - auto* db = databases()[index]; - if (!db->db) { - return; - } - sqlite3_close_v2(db->db); - databases()[index]->db = nullptr; + databases()[index]->release(); }); } RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(index))); @@ -2068,53 +2082,6 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionGet, (JSC::JSGlob } } -JSC_DEFINE_JIT_OPERATION(jsSQLStatementExecuteStatementFunctionGetWithoutTypeChecking, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSSQLStatement* castedThis)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* stmt = castedThis->stmt; - CHECK_PREPARED - - int statusCode = sqlite3_reset(stmt); - if (UNLIKELY(statusCode != SQLITE_OK)) { - throwException(lexicalGlobalObject, scope, createSQLiteError(lexicalGlobalObject, castedThis->version_db->db)); - return JSValue::encode(jsUndefined()); - } - - int status = sqlite3_step(stmt); - if (!sqlite3_stmt_readonly(stmt)) { - castedThis->version_db->version++; - } - - if (!castedThis->hasExecuted || castedThis->need_update()) { - initializeColumnNames(lexicalGlobalObject, castedThis); - } - - JSValue result = jsNull(); - if (status == SQLITE_ROW) { - bool useBigInt64 = castedThis->useBigInt64; - - result = useBigInt64 ? constructResultObject(lexicalGlobalObject, castedThis) - : constructResultObject(lexicalGlobalObject, castedThis); - while (status == SQLITE_ROW) { - status = sqlite3_step(stmt); - } - } - - if (status == SQLITE_DONE || status == SQLITE_OK) { - RELEASE_AND_RETURN(scope, JSValue::encode(result)); - } else { - throwException(lexicalGlobalObject, scope, createSQLiteError(lexicalGlobalObject, castedThis->version_db->db)); - sqlite3_reset(stmt); - return JSValue::encode(jsUndefined()); - } -} - JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) { @@ -2219,6 +2186,11 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRun, (JSC::JSGlob DO_REBIND(arg0); } + if (UNLIKELY(!castedThis->version_db->db)) { + throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Database has closed"_s)); + return JSValue::encode(JSC::jsUndefined()); + } + int total_changes_before = sqlite3_total_changes(castedThis->version_db->db); int status = sqlite3_step(stmt); @@ -2387,6 +2359,10 @@ JSSQLStatement::~JSSQLStatement() columnNames->releaseData(); this->columnNames = nullptr; } + + if (this->version_db) { + this->version_db->release(); + } } void JSSQLStatement::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) diff --git a/src/bun.js/bindings/sqlite/sqlite3_local.h b/src/bun.js/bindings/sqlite/sqlite3_local.h index c2d2456dde6f3..1d77374a2ab09 100644 --- a/src/bun.js/bindings/sqlite/sqlite3_local.h +++ b/src/bun.js/bindings/sqlite/sqlite3_local.h @@ -1,3 +1,4 @@ +// clang-format off /* ** 2001-09-15 ** diff --git a/src/bun.js/bindings/uv-posix-polyfills.cpp b/src/bun.js/bindings/uv-posix-polyfills.cpp new file mode 100644 index 0000000000000..6431dfa84b73e --- /dev/null +++ b/src/bun.js/bindings/uv-posix-polyfills.cpp @@ -0,0 +1,15 @@ +#include "uv-posix-polyfills.h" + +#if OS(LINUX) || OS(DARWIN) + +uv_pid_t uv_os_getpid() +{ + return getpid(); +} + +uv_pid_t uv_os_getppid() +{ + return getppid(); +} + +#endif diff --git a/src/bun.js/bindings/uv-posix-polyfills.h b/src/bun.js/bindings/uv-posix-polyfills.h new file mode 100644 index 0000000000000..8c2779ff949e3 --- /dev/null +++ b/src/bun.js/bindings/uv-posix-polyfills.h @@ -0,0 +1,15 @@ +#pragma once + +#include "root.h" + +#if OS(LINUX) || OS(DARWIN) + +typedef int uv_pid_t; + +// Returns the current process ID. +extern "C" BUN_EXPORT uv_pid_t uv_os_getpid(); + +// Returns the parent process ID. +extern "C" BUN_EXPORT uv_pid_t uv_os_getppid(); + +#endif diff --git a/src/bun.js/bindings/v8.cpp b/src/bun.js/bindings/v8.cpp deleted file mode 100644 index b939552c9534b..0000000000000 --- a/src/bun.js/bindings/v8.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// This file implements the v8 and node C++ APIs -// -// If you have issues linking this file, you probably have to update -// the code in `napi.zig` at `const V8API` -#include "root.h" -#include "ZigGlobalObject.h" - -#if defined(WIN32) || defined(_WIN32) -#define BUN_EXPORT __declspec(dllexport) -#else -#define BUN_EXPORT JS_EXPORT -#endif - -extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); - -namespace v8 { - -using Context = JSC::JSGlobalObject; - -template -class Local final { -public: - T* ptr; -}; - -// This currently is just a pointer to a Zig::GlobalObject* -// We do that so that we can recover the context and the VM from the "Isolate" -class Isolate final { -public: - Isolate() = default; - - // Returns the isolate inside which the current thread is running or nullptr. - BUN_EXPORT static Isolate* TryGetCurrent(); - - // Returns the isolate inside which the current thread is running. - BUN_EXPORT static Isolate* GetCurrent(); - - BUN_EXPORT Local GetCurrentContext(); - - Zig::GlobalObject* globalObject() { return reinterpret_cast(this); } - JSC::VM& vm() { return globalObject()->vm(); } -}; - -// Returns the isolate inside which the current thread is running or nullptr. -Isolate* Isolate::TryGetCurrent() -{ - auto* global = Bun__getDefaultGlobal(); - - return global ? reinterpret_cast(global) : nullptr; -} - -// Returns the isolate inside which the current thread is running. -Isolate* Isolate::GetCurrent() -{ - auto* global = Bun__getDefaultGlobal(); - - return global ? reinterpret_cast(global) : nullptr; -} - -Local Isolate::GetCurrentContext() -{ - return Local { reinterpret_cast(this) }; -} - -} - -namespace node { - -BUN_EXPORT void AddEnvironmentCleanupHook(v8::Isolate* isolate, - void (*fun)(void* arg), - void* arg) -{ - // TODO -} - -BUN_EXPORT void RemoveEnvironmentCleanupHook(v8::Isolate* isolate, - void (*fun)(void* arg), - void* arg) -{ - // TODO -} - -} diff --git a/src/bun.js/bindings/v8/V8Array.cpp b/src/bun.js/bindings/v8/V8Array.cpp new file mode 100644 index 0000000000000..97dd9cf31925d --- /dev/null +++ b/src/bun.js/bindings/v8/V8Array.cpp @@ -0,0 +1,23 @@ +#include "V8Array.h" + +#include "V8HandleScope.h" + +using JSC::ArrayAllocationProfile; +using JSC::JSArray; +using JSC::JSValue; + +namespace v8 { + +Local Array::New(Isolate* isolate, Local* elements, size_t length) +{ + V8_UNIMPLEMENTED(); + // TODO fix for v8 layout + Zig::GlobalObject* globalObject = isolate->globalObject(); + JSArray* array = JSC::constructArray(globalObject, + static_cast(nullptr), + reinterpret_cast(elements), + (unsigned int)length); + return isolate->currentHandleScope()->createLocal(isolate->vm(), array); +} + +} diff --git a/src/bun.js/bindings/v8/V8Array.h b/src/bun.js/bindings/v8/V8Array.h new file mode 100644 index 0000000000000..d88418894e899 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Array.h @@ -0,0 +1,16 @@ +#pragma once + +#include "v8.h" +#include "V8Object.h" +#include "V8Local.h" +#include "V8Isolate.h" +#include "V8Value.h" + +namespace v8 { + +class Array : public Object { +public: + BUN_EXPORT static Local New(Isolate* isolate, Local* elements, size_t length); +}; + +} diff --git a/src/bun.js/bindings/v8/V8Boolean.cpp b/src/bun.js/bindings/v8/V8Boolean.cpp new file mode 100644 index 0000000000000..3bc95e043c978 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Boolean.cpp @@ -0,0 +1,16 @@ +#include "V8Boolean.h" +#include "V8HandleScope.h" + +namespace v8 { + +bool Boolean::Value() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).asBoolean(); +} + +Local Boolean::New(Isolate* isolate, bool value) +{ + return isolate->currentHandleScope()->createLocal(isolate->vm(), JSC::jsBoolean(value)); +} + +} diff --git a/src/bun.js/bindings/v8/V8Boolean.h b/src/bun.js/bindings/v8/V8Boolean.h new file mode 100644 index 0000000000000..9dd76f124a575 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Boolean.h @@ -0,0 +1,15 @@ +#pragma once + +#include "V8Primitive.h" +#include "V8Isolate.h" + +namespace v8 { + +class Boolean : public Primitive { +public: + BUN_EXPORT bool Value() const; + // usually inlined + BUN_EXPORT static Local New(Isolate* isolate, bool value); +}; + +} diff --git a/src/bun.js/bindings/v8/V8Context.cpp b/src/bun.js/bindings/v8/V8Context.cpp new file mode 100644 index 0000000000000..1fe001f237048 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Context.cpp @@ -0,0 +1,10 @@ +#include "V8Context.h" + +namespace v8 { + +Isolate* Context::GetIsolate() +{ + return reinterpret_cast(localToPointer()); +} + +} diff --git a/src/bun.js/bindings/v8/V8Context.h b/src/bun.js/bindings/v8/V8Context.h new file mode 100644 index 0000000000000..2293a22516ff2 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Context.h @@ -0,0 +1,38 @@ +#pragma once + +#include "ZigGlobalObject.h" +#include "V8GlobalInternals.h" +#include "V8Data.h" + +namespace v8 { + +class Isolate; + +// Context is always a reinterpret pointer to V8::Roots, so that inlined V8 functions can find +// values they expect to find at fixed offsets +class Context : public Data { +public: + BUN_EXPORT Isolate* GetIsolate(); + + JSC::VM& vm() const + { + return globalObject()->vm(); + } + + const Zig::GlobalObject* globalObject() const + { + return reinterpret_cast(localToCell())->parent->globalObject; + } + + Zig::GlobalObject* globalObject() + { + return reinterpret_cast(localToCell())->parent->globalObject; + } + + HandleScope* currentHandleScope() const + { + return globalObject()->V8GlobalInternals()->currentHandleScope(); + }; +}; + +} diff --git a/src/bun.js/bindings/v8/V8Data.h b/src/bun.js/bindings/v8/V8Data.h new file mode 100644 index 0000000000000..aded85f5c9bba --- /dev/null +++ b/src/bun.js/bindings/v8/V8Data.h @@ -0,0 +1,106 @@ +#pragma once + +#include "root.h" +#include "V8TaggedPointer.h" +#include "V8Handle.h" +#include "V8GlobalInternals.h" + +namespace v8 { + +class Data { +public: + // Functions beginning with "localTo" must only be used when "this" comes from a v8::Local (i.e. + // in public V8 functions), as they make assumptions about how V8 lays out local handles. They + // will segfault or worse otherwise. + + // Recover an opaque pointer out of a v8::Local which is not a number + void* localToPointer() + { + TaggedPointer tagged = localToTagged(); + RELEASE_ASSERT(tagged.type() != TaggedPointer::Type::Smi); + return tagged.getPtr(); + } + + // Recover a JSCell pointer out of a v8::Local + JSC::JSCell* localToCell() + { + return reinterpret_cast(localToPointer()); + } + + // Recover a pointer to a JSCell subclass out of a v8::Local + template + T* localToObjectPointer() + { + static_assert(std::is_base_of::value, "localToObjectPointer can only be used when T is a JSCell subclass"); + return JSC::jsDynamicCast(localToCell()); + } + + // Get this as a JSValue when this is a v8::Local + JSC::JSValue localToJSValue(GlobalInternals* globalInternals) const + { + TaggedPointer root = *reinterpret_cast(this); + if (root.type() == TaggedPointer::Type::Smi) { + return JSC::jsNumber(root.getSmiUnchecked()); + } else { + void* raw_ptr = root.getPtr(); + // check if this pointer is identical to the fixed locations where these primitive + // values are stored + if (raw_ptr == globalInternals->undefinedSlot()->getPtr()) { + return JSC::jsUndefined(); + } else if (raw_ptr == globalInternals->nullSlot()->getPtr()) { + return JSC::jsNull(); + } else if (raw_ptr == globalInternals->trueSlot()->getPtr()) { + return JSC::jsBoolean(true); + } else if (raw_ptr == globalInternals->falseSlot()->getPtr()) { + return JSC::jsBoolean(false); + } + + ObjectLayout* v8_object = reinterpret_cast(raw_ptr); + if (v8_object->map()->instance_type == InstanceType::HeapNumber) { + return JSC::jsDoubleNumber(v8_object->asDouble()); + } else { + return JSC::JSValue(v8_object->asCell()); + } + } + } + + // Recover an opaque pointer out of a v8::Local which is not a number + const void* localToPointer() const + { + TaggedPointer tagged = localToTagged(); + RELEASE_ASSERT(tagged.type() != TaggedPointer::Type::Smi); + return tagged.getPtr(); + } + + // Recover a JSCell pointer out of a v8::Local + const JSC::JSCell* localToCell() const + { + return reinterpret_cast(localToPointer()); + } + + // Recover a pointer to a JSCell subclass out of a v8::Local + template + const T* localToObjectPointer() const + { + static_assert(std::is_base_of::value, "localToObjectPointer can only be used when T is a JSCell subclass"); + return JSC::jsDynamicCast(localToCell()); + } + +private: + // Convert the local handle into either a smi or a pointer to some non-V8 type. + TaggedPointer localToTagged() const + { + TaggedPointer root = *reinterpret_cast(this); + if (root.type() == TaggedPointer::Type::Smi) { + return root; + } else { + // root points to the V8 object. The first field of the V8 object is the map, and the + // second is a pointer to some object we have stored. So we ignore the map and recover + // the object pointer. + ObjectLayout* v8_object = root.getPtr(); + return TaggedPointer(v8_object->asRaw()); + } + } +}; + +} diff --git a/src/bun.js/bindings/v8/V8EscapableHandleScope.cpp b/src/bun.js/bindings/v8/V8EscapableHandleScope.cpp new file mode 100644 index 0000000000000..c5e7bcb9134b7 --- /dev/null +++ b/src/bun.js/bindings/v8/V8EscapableHandleScope.cpp @@ -0,0 +1,15 @@ +#include "V8EscapableHandleScope.h" + +namespace v8 { + +EscapableHandleScope::EscapableHandleScope(Isolate* isolate) + : EscapableHandleScopeBase(isolate) +{ +} + +EscapableHandleScope::~EscapableHandleScope() +{ + EscapableHandleScopeBase::~EscapableHandleScopeBase(); +} + +} diff --git a/src/bun.js/bindings/v8/V8EscapableHandleScope.h b/src/bun.js/bindings/v8/V8EscapableHandleScope.h new file mode 100644 index 0000000000000..ad5f39ebd7752 --- /dev/null +++ b/src/bun.js/bindings/v8/V8EscapableHandleScope.h @@ -0,0 +1,13 @@ +#pragma once + +#include "V8EscapableHandleScopeBase.h" + +namespace v8 { + +class EscapableHandleScope : public EscapableHandleScopeBase { +public: + BUN_EXPORT EscapableHandleScope(Isolate* isolate); + BUN_EXPORT ~EscapableHandleScope(); +}; + +} diff --git a/src/bun.js/bindings/v8/V8EscapableHandleScopeBase.cpp b/src/bun.js/bindings/v8/V8EscapableHandleScopeBase.cpp new file mode 100644 index 0000000000000..9bc31b58b5040 --- /dev/null +++ b/src/bun.js/bindings/v8/V8EscapableHandleScopeBase.cpp @@ -0,0 +1,22 @@ +#include "V8EscapableHandleScopeBase.h" + +namespace v8 { + +EscapableHandleScopeBase::EscapableHandleScopeBase(Isolate* isolate) + : HandleScope(isolate) +{ + // at this point isolate->currentHandleScope() would just be this, so instead we have to get the + // previous one + auto& handle = prev->buffer->createEmptyHandle(); + escape_slot = &handle; +} + +// Store the handle escape_value in the escape slot that we have allocated from the parent +// HandleScope, and return the escape slot +uintptr_t* EscapableHandleScopeBase::EscapeSlot(uintptr_t* escape_value) +{ + *escape_slot = *reinterpret_cast(escape_value); + return &escape_slot->to_v8_object.value; +} + +} diff --git a/src/bun.js/bindings/v8/V8EscapableHandleScopeBase.h b/src/bun.js/bindings/v8/V8EscapableHandleScopeBase.h new file mode 100644 index 0000000000000..1b82748dc7ab8 --- /dev/null +++ b/src/bun.js/bindings/v8/V8EscapableHandleScopeBase.h @@ -0,0 +1,20 @@ +#pragma once + +#include "v8.h" +#include "V8HandleScope.h" +#include "V8Isolate.h" + +namespace v8 { + +class EscapableHandleScopeBase : public HandleScope { +public: + BUN_EXPORT EscapableHandleScopeBase(Isolate* isolate); + +protected: + BUN_EXPORT uintptr_t* EscapeSlot(uintptr_t* escape_value); + +private: + Handle* escape_slot; +}; + +} diff --git a/src/bun.js/bindings/v8/V8External.cpp b/src/bun.js/bindings/v8/V8External.cpp new file mode 100644 index 0000000000000..64a437490a080 --- /dev/null +++ b/src/bun.js/bindings/v8/V8External.cpp @@ -0,0 +1,26 @@ +#include "V8External.h" +#include "V8HandleScope.h" + +#include "napi_external.h" + +namespace v8 { + +Local External::New(Isolate* isolate, void* value) +{ + auto globalObject = isolate->globalObject(); + auto& vm = globalObject->vm(); + auto structure = globalObject->NapiExternalStructure(); + Bun::NapiExternal* val = Bun::NapiExternal::create(vm, structure, value, nullptr, nullptr); + return isolate->currentHandleScope()->createLocal(vm, val); +} + +void* External::Value() const +{ + auto* external = localToObjectPointer(); + if (!external) { + return nullptr; + } + return external->value(); +} + +} diff --git a/src/bun.js/bindings/v8/V8External.h b/src/bun.js/bindings/v8/V8External.h new file mode 100644 index 0000000000000..8c01c3db66a9e --- /dev/null +++ b/src/bun.js/bindings/v8/V8External.h @@ -0,0 +1,16 @@ +#pragma once + +#include "v8.h" +#include "V8Value.h" +#include "V8MaybeLocal.h" +#include "V8Isolate.h" + +namespace v8 { + +class External : public Value { +public: + BUN_EXPORT static Local New(Isolate* isolate, void* value); + BUN_EXPORT void* Value() const; +}; + +} diff --git a/src/bun.js/bindings/v8/V8Function.cpp b/src/bun.js/bindings/v8/V8Function.cpp new file mode 100644 index 0000000000000..3ff1dc4ae1719 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Function.cpp @@ -0,0 +1,64 @@ +#include "V8Function.h" + +#include "V8FunctionTemplate.h" + +#include "JavaScriptCore/FunctionPrototype.h" + +using JSC::Structure; +using JSC::VM; + +namespace v8 { + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const JSC::ClassInfo Function::s_info = { + "Function"_s, + &Base::s_info, + nullptr, + nullptr, + CREATE_METHOD_TABLE(Function) +}; + +Structure* Function::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + return Structure::create( + vm, + globalObject, + globalObject->functionPrototype(), + JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), + info()); +} + +template +void Function::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + Function* fn = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(fn, info()); + Base::visitChildren(fn, visitor); + + visitor.append(fn->__internals.functionTemplate); +} + +DEFINE_VISIT_CHILDREN(Function); + +Function* Function::create(VM& vm, Structure* structure, FunctionTemplate* functionTemplate) +{ + auto* function = new (NotNull, JSC::allocateCell(vm)) Function(vm, structure); + function->finishCreation(vm, functionTemplate); + return function; +} + +void Function::finishCreation(VM& vm, FunctionTemplate* functionTemplate) +{ + Base::finishCreation(vm, 0, "Function"_s); + __internals.functionTemplate.set(vm, this, functionTemplate); +} + +void Function::SetName(Local name) +{ + auto* thisObj = localToObjectPointer(); + thisObj->m_originalName.set(Isolate::GetCurrent()->vm(), thisObj, name->localToJSString()); +} + +} diff --git a/src/bun.js/bindings/v8/V8Function.h b/src/bun.js/bindings/v8/V8Function.h new file mode 100644 index 0000000000000..9b2d1f552eabb --- /dev/null +++ b/src/bun.js/bindings/v8/V8Function.h @@ -0,0 +1,76 @@ +#pragma once + +#include "V8Object.h" +#include "V8FunctionTemplate.h" +#include "V8Local.h" +#include "V8String.h" + +namespace v8 { + +// If this inherited Object like it does in V8, the layout would be wrong for JSC HeapCell. +// Inheritance shouldn't matter for the ABI. +class Function : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + + static Function* create(JSC::VM& vm, JSC::Structure* structure, FunctionTemplate* functionTemplate); + + DECLARE_INFO; + DECLARE_VISIT_CHILDREN; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject); + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForV8Function.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForV8Function = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForV8Function.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForV8Function = std::forward(space); }); + } + + BUN_EXPORT void SetName(Local name); + + FunctionTemplate* functionTemplate() const + { + return __internals.functionTemplate.get(); + } + +private: + class Internals { + private: + JSC::WriteBarrier functionTemplate; + friend class Function; + friend class FunctionTemplate; + }; + + Internals __internals; + + Function(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure, FunctionTemplate::functionCall) + { + } + + Function* localToObjectPointer() + { + return reinterpret_cast(this)->localToObjectPointer(); + } + + const Function* localToObjectPointer() const + { + return reinterpret_cast(this)->localToObjectPointer(); + } + + Internals& internals() + { + return localToObjectPointer()->__internals; + } + + void finishCreation(JSC::VM& vm, FunctionTemplate* functionTemplate); +}; + +} diff --git a/src/bun.js/bindings/v8/V8FunctionTemplate.cpp b/src/bun.js/bindings/v8/V8FunctionTemplate.cpp new file mode 100644 index 0000000000000..b83d5c6fbf6c4 --- /dev/null +++ b/src/bun.js/bindings/v8/V8FunctionTemplate.cpp @@ -0,0 +1,137 @@ +#include "V8FunctionTemplate.h" +#include "V8Function.h" +#include "V8HandleScope.h" + +#include "JavaScriptCore/FunctionPrototype.h" + +using JSC::JSCell; +using JSC::JSValue; +using JSC::Structure; + +namespace v8 { + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const JSC::ClassInfo FunctionTemplate::s_info = { + "FunctionTemplate"_s, + &Base::s_info, + nullptr, + nullptr, + CREATE_METHOD_TABLE(FunctionTemplate) +}; + +Local FunctionTemplate::New( + Isolate* isolate, + FunctionCallback callback, + Local data, + Local signature, + int length, + ConstructorBehavior behavior, + SideEffectType side_effect_type, + const CFunction* c_function, + uint16_t instance_type, + uint16_t allowed_receiver_instance_type_range_start, + uint16_t allowed_receiver_instance_type_range_end) +{ + // only handling simpler cases for now + // (pass most of these into v8::Function / JSC::InternalFunction) + RELEASE_ASSERT(signature.IsEmpty()); + RELEASE_ASSERT(length == 0); + RELEASE_ASSERT(behavior == ConstructorBehavior::kAllow); + RELEASE_ASSERT(side_effect_type == SideEffectType::kHasSideEffect); + RELEASE_ASSERT(c_function == nullptr); + RELEASE_ASSERT(instance_type == 0); + RELEASE_ASSERT(allowed_receiver_instance_type_range_start == 0); + RELEASE_ASSERT(allowed_receiver_instance_type_range_end == 0); + + auto globalObject = isolate->globalObject(); + auto& vm = globalObject->vm(); + auto* globalInternals = globalObject->V8GlobalInternals(); + JSValue jsc_data = data.IsEmpty() ? JSC::jsUndefined() : data->localToJSValue(globalInternals); + + Structure* structure = globalInternals->functionTemplateStructure(globalObject); + auto* functionTemplate = new (NotNull, JSC::allocateCell(vm)) FunctionTemplate( + vm, structure, callback, jsc_data); + functionTemplate->finishCreation(vm); + + return globalInternals->currentHandleScope()->createLocal(vm, functionTemplate); +} + +MaybeLocal FunctionTemplate::GetFunction(Local context) +{ + auto& vm = context->vm(); + auto* globalObject = context->globalObject(); + auto* globalInternals = globalObject->V8GlobalInternals(); + auto* f = Function::create(vm, globalInternals->v8FunctionStructure(globalObject), localToObjectPointer()); + + return globalInternals->currentHandleScope()->createLocal(vm, f); +} + +Structure* FunctionTemplate::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + return Structure::create( + vm, + globalObject, + globalObject->functionPrototype(), + JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), + info()); +} + +template +void FunctionTemplate::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + FunctionTemplate* fn = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(fn, info()); + Base::visitChildren(fn, visitor); + + visitor.append(fn->__internals.data); +} + +DEFINE_VISIT_CHILDREN(FunctionTemplate); + +JSC::EncodedJSValue FunctionTemplate::functionCall(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +{ + auto* callee = JSC::jsDynamicCast(callFrame->jsCallee()); + auto* functionTemplate = callee->functionTemplate(); + auto* isolate = Isolate::fromGlobalObject(JSC::jsDynamicCast(globalObject)); + auto& vm = globalObject->vm(); + + WTF::Vector args(callFrame->argumentCount() + 1); + + HandleScope hs(isolate); + Local thisValue = hs.createLocal(vm, callFrame->thisValue()); + args[0] = thisValue.tagged(); + + for (size_t i = 0; i < callFrame->argumentCount(); i++) { + Local argValue = hs.createLocal(vm, callFrame->argument(i)); + args[i + 1] = argValue.tagged(); + } + + Local data = hs.createLocal(vm, functionTemplate->__internals.data.get()); + + ImplicitArgs implicit_args = { + .holder = nullptr, + .isolate = isolate, + .context = reinterpret_cast(isolate), + .return_value = TaggedPointer(), + // data may be an object + // put it in the handle scope so that it has a map ptr + .target = data.tagged(), + .new_target = nullptr, + }; + + FunctionCallbackInfo info(&implicit_args, args.data() + 1, callFrame->argumentCount()); + + functionTemplate->__internals.callback(info); + + if (implicit_args.return_value.type() != TaggedPointer::Type::Smi && implicit_args.return_value.getPtr() == nullptr) { + // callback forgot to set a return value, so return undefined + return JSValue::encode(JSC::jsUndefined()); + } else { + Local local_ret(&implicit_args.return_value); + return JSValue::encode(local_ret->localToJSValue(isolate->globalInternals())); + } +} + +} diff --git a/src/bun.js/bindings/v8/V8FunctionTemplate.h b/src/bun.js/bindings/v8/V8FunctionTemplate.h new file mode 100644 index 0000000000000..3d3fe261c188c --- /dev/null +++ b/src/bun.js/bindings/v8/V8FunctionTemplate.h @@ -0,0 +1,155 @@ +#pragma once + +#include "v8.h" +#include "V8Context.h" +#include "V8Isolate.h" +#include "V8Local.h" +#include "V8MaybeLocal.h" +#include "V8Value.h" +#include "V8Signature.h" + +namespace v8 { + +class Function; + +struct ImplicitArgs { + // v8-function-callback.h:168 + void* holder; + Isolate* isolate; + Context* context; + // overwritten by the callback + TaggedPointer return_value; + // holds the value passed for data in FunctionTemplate::New + TaggedPointer target; + void* new_target; +}; + +// T = return value +template +class FunctionCallbackInfo { + // V8 treats this as an array of pointers + ImplicitArgs* implicit_args; + // index -1 is this + TaggedPointer* values; + int length; + +public: + FunctionCallbackInfo(ImplicitArgs* implicit_args_, TaggedPointer* values_, int length_) + : implicit_args(implicit_args_) + , values(values_) + , length(length_) + { + } +}; + +using FunctionCallback = void (*)(const FunctionCallbackInfo&); + +enum class ConstructorBehavior { + kThrow, + kAllow, +}; + +enum class SideEffectType { + kHasSideEffect, + kHasNoSideEffect, + kHasSideEffectToReceiver, +}; + +class CFunction { +private: + const void* address; + const void* type_info; +}; + +// If this inherited Template like it does in V8, the layout would be wrong for JSC HeapCell. +// Inheritance shouldn't matter for the ABI. +class FunctionTemplate : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + + BUN_EXPORT static Local New( + Isolate* isolate, + FunctionCallback callback = nullptr, + Local data = Local(), + Local signature = Local(), + int length = 0, + ConstructorBehavior behavior = ConstructorBehavior::kAllow, + SideEffectType side_effect_type = SideEffectType::kHasSideEffect, + const CFunction* c_function = nullptr, + uint16_t instance_type = 0, + uint16_t allowed_receiver_instance_type_range_start = 0, + uint16_t allowed_receiver_instance_type_range_end = 0); + + BUN_EXPORT MaybeLocal GetFunction(Local context); + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject); + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForFunctionTemplate.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForFunctionTemplate = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForFunctionTemplate.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForFunctionTemplate = std::forward(space); }); + } + + DECLARE_INFO; + DECLARE_VISIT_CHILDREN; + + friend class Function; + + FunctionCallback callback() const + { + return __internals.callback; + } + +private: + class Internals { + private: + FunctionCallback callback; + JSC::WriteBarrier data; + + Internals(FunctionCallback callback_, JSC::VM& vm, FunctionTemplate* owner, JSC::JSValue data_) + : callback(callback_) + , data(vm, owner, data_) + { + } + + friend class FunctionTemplate; + }; + + // only use from functions called directly on FunctionTemplate + Internals __internals; + + FunctionTemplate* localToObjectPointer() + { + return reinterpret_cast(this)->localToObjectPointer(); + } + + const FunctionTemplate* localToObjectPointer() const + { + return reinterpret_cast(this)->localToObjectPointer(); + } + + // only use from functions called on Local + Internals& internals() + { + return localToObjectPointer()->__internals; + } + + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES functionCall(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); + + FunctionTemplate(JSC::VM& vm, JSC::Structure* structure, FunctionCallback callback, JSC::JSValue data) + : __internals(callback, vm, this, data) + , Base(vm, structure, functionCall, JSC::callHostFunctionAsConstructor) + { + } + + // some kind of static trampoline +}; + +} diff --git a/src/bun.js/bindings/v8/V8GlobalInternals.cpp b/src/bun.js/bindings/v8/V8GlobalInternals.cpp new file mode 100644 index 0000000000000..6a5a36fa87293 --- /dev/null +++ b/src/bun.js/bindings/v8/V8GlobalInternals.cpp @@ -0,0 +1,70 @@ +#include "V8GlobalInternals.h" + +#include "V8ObjectTemplate.h" +#include "V8InternalFieldObject.h" +#include "V8HandleScopeBuffer.h" +#include "V8FunctionTemplate.h" +#include "V8Function.h" + +#include "JavaScriptCore/FunctionPrototype.h" +#include "JavaScriptCore/LazyClassStructureInlines.h" +#include "JavaScriptCore/VMTrapsInlines.h" + +using JSC::ClassInfo; +using JSC::LazyClassStructure; +using JSC::LazyProperty; +using JSC::Structure; +using JSC::VM; + +namespace v8 { + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const ClassInfo GlobalInternals::s_info = { "GlobalInternals"_s, nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(GlobalInternals) }; + +GlobalInternals* GlobalInternals::create(VM& vm, Structure* structure, Zig::GlobalObject* globalObject) +{ + GlobalInternals* internals = new (NotNull, JSC::allocateCell(vm)) GlobalInternals(vm, structure, globalObject); + internals->finishCreation(vm); + return internals; +} + +void GlobalInternals::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + m_ObjectTemplateStructure.initLater([](LazyClassStructure::Initializer& init) { + init.setStructure(ObjectTemplate::createStructure(init.vm, init.global, init.global->functionPrototype())); + }); + m_HandleScopeBufferStructure.initLater([](LazyClassStructure::Initializer& init) { + init.setStructure(HandleScopeBuffer::createStructure(init.vm, init.global)); + }); + m_FunctionTemplateStructure.initLater([](LazyClassStructure::Initializer& init) { + init.setStructure(FunctionTemplate::createStructure(init.vm, init.global)); + }); + m_V8FunctionStructure.initLater([](LazyClassStructure::Initializer& init) { + init.setStructure(Function::createStructure(init.vm, init.global)); + }); + m_GlobalHandles.initLater([](const LazyProperty::Initializer& init) { + init.set(HandleScopeBuffer::create(init.vm, + init.owner->handleScopeBufferStructure(init.owner->globalObject))); + }); +} + +template +void GlobalInternals::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + GlobalInternals* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + thisObject->m_ObjectTemplateStructure.visit(visitor); + thisObject->m_HandleScopeBufferStructure.visit(visitor); + thisObject->m_FunctionTemplateStructure.visit(visitor); + thisObject->m_V8FunctionStructure.visit(visitor); + thisObject->m_GlobalHandles.visit(visitor); +} + +DEFINE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE, GlobalInternals); + +} diff --git a/src/bun.js/bindings/v8/V8GlobalInternals.h b/src/bun.js/bindings/v8/V8GlobalInternals.h new file mode 100644 index 0000000000000..bdb2bbc936ce3 --- /dev/null +++ b/src/bun.js/bindings/v8/V8GlobalInternals.h @@ -0,0 +1,108 @@ +#pragma once + +#include "BunClientData.h" + +#include "V8Roots.h" +#include "V8Oddball.h" + +namespace v8 { + +class HandleScope; +class HandleScopeBuffer; + +class GlobalInternals : public JSC::JSCell { +public: + using Base = JSC::JSCell; + + static GlobalInternals* create(JSC::VM& vm, JSC::Structure* structure, Zig::GlobalObject* globalObject); + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) + { + return JSC::Structure::create(vm, globalObject, JSC::jsNull(), JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), 0, 0); + } + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForV8GlobalInternals.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForV8GlobalInternals = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForV8GlobalInternals.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForV8GlobalInternals = std::forward(space); }); + } + + JSC::Structure* objectTemplateStructure(JSC::JSGlobalObject* globalObject) const + { + return m_ObjectTemplateStructure.getInitializedOnMainThread(globalObject); + } + + JSC::Structure* handleScopeBufferStructure(JSC::JSGlobalObject* globalObject) const + { + return m_HandleScopeBufferStructure.getInitializedOnMainThread(globalObject); + } + + JSC::Structure* functionTemplateStructure(JSC::JSGlobalObject* globalObject) const + { + return m_FunctionTemplateStructure.getInitializedOnMainThread(globalObject); + } + + JSC::Structure* v8FunctionStructure(JSC::JSGlobalObject* globalObject) const + { + return m_V8FunctionStructure.getInitializedOnMainThread(globalObject); + } + + HandleScopeBuffer* globalHandles() const { return m_GlobalHandles.getInitializedOnMainThread(this); } + + HandleScope* currentHandleScope() const { return m_CurrentHandleScope; } + + void setCurrentHandleScope(HandleScope* handleScope) { m_CurrentHandleScope = handleScope; } + + TaggedPointer* undefinedSlot() { return &roots.roots[Roots::kUndefinedValueRootIndex]; } + + TaggedPointer* nullSlot() { return &roots.roots[Roots::kNullValueRootIndex]; } + + TaggedPointer* trueSlot() { return &roots.roots[Roots::kTrueValueRootIndex]; } + + TaggedPointer* falseSlot() { return &roots.roots[Roots::kFalseValueRootIndex]; } + + DECLARE_INFO; + DECLARE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE); + + friend struct Roots; + friend class Isolate; + friend class Context; + +private: + Zig::GlobalObject* globalObject; + JSC::LazyClassStructure m_ObjectTemplateStructure; + JSC::LazyClassStructure m_HandleScopeBufferStructure; + JSC::LazyClassStructure m_FunctionTemplateStructure; + JSC::LazyClassStructure m_V8FunctionStructure; + HandleScope* m_CurrentHandleScope; + JSC::LazyProperty m_GlobalHandles; + + Oddball undefinedValue; + Oddball nullValue; + Oddball trueValue; + Oddball falseValue; + + Roots roots; + + void finishCreation(JSC::VM& vm); + GlobalInternals(JSC::VM& vm, JSC::Structure* structure, Zig::GlobalObject* globalObject_) + : Base(vm, structure) + , m_CurrentHandleScope(nullptr) + , undefinedValue(Oddball::Kind::kUndefined) + , nullValue(Oddball::Kind::kNull) + , trueValue(Oddball::Kind::kTrue, &Map::boolean_map) + , falseValue(Oddball::Kind::kFalse, &Map::boolean_map) + , roots(this) + , globalObject(globalObject_) + { + } +}; + +} diff --git a/src/bun.js/bindings/v8/V8Handle.h b/src/bun.js/bindings/v8/V8Handle.h new file mode 100644 index 0000000000000..709481e95e4bb --- /dev/null +++ b/src/bun.js/bindings/v8/V8Handle.h @@ -0,0 +1,149 @@ +#pragma once + +#include "V8Map.h" + +namespace v8 { + +class ObjectLayout { +private: + // these two fields are laid out so that V8 can find the map + TaggedPointer tagged_map; + union { + JSC::WriteBarrier cell; + double number; + void* raw; + } contents; + +public: + ObjectLayout() + // using a smi value for map is most likely to catch bugs as almost every access will expect + // map to be a pointer (and even if the assertion is bypassed, it'll be a null pointer) + : tagged_map(0) + , contents({ .raw = nullptr }) + { + } + + ObjectLayout(const Map* map_ptr, JSC::JSCell* cell, JSC::VM& vm, const JSC::JSCell* owner) + : tagged_map(const_cast(map_ptr)) + , contents({ .cell = JSC::WriteBarrier(vm, owner, cell) }) + { + } + + ObjectLayout(double number) + : tagged_map(const_cast(&Map::heap_number_map)) + , contents({ .number = number }) + { + } + + ObjectLayout(void* raw) + : tagged_map(const_cast(&Map::raw_ptr_map)) + , contents({ .raw = raw }) + { + } + + const Map* map() const { return tagged_map.getPtr(); } + + double asDouble() const { return contents.number; } + + JSC::JSCell* asCell() const { return contents.cell.get(); } + + void* asRaw() const { return contents.raw; } + + friend class Handle; + friend class HandleScopeBuffer; +}; + +// A handle stored in a HandleScope with layout suitable for V8's inlined functions: +// - The first field is a V8 tagged pointer. If it's a SMI (int32), it holds the numeric value +// directly and the other fields don't matter. +// - Otherwise, if the first field is a pointer value, V8 treats that as a pointer to an object with +// V8 layout. V8 objects have a tagged pointer to their map (which describes their structure) as +// the first field. Therefore, in the object case, the first field is a pointer to the second +// field. +// - V8 will inspect the instance type of the map to determine if it can take fast paths for some +// functions (notably, Value::IsUndefined()/IsNull() and Object::GetInternalField()). For objects, +// we use a map with an instance type that makes V8 think it must call SlowGetInternalField(), +// which we can control. That function (and all other functions that are called on Locals) uses +// the third field to get the actual object (either a JSCell* or a void*, depending on whether map +// points to Map::object_map or Map::raw_ptr_map). +struct Handle { + static_assert(offsetof(ObjectLayout, tagged_map) == 0, "ObjectLayout is wrong"); + static_assert(offsetof(ObjectLayout, contents) == 8, "ObjectLayout is wrong"); + static_assert(sizeof(ObjectLayout) == 16, "ObjectLayout is wrong"); + + Handle(const Map* map, JSC::JSCell* cell, JSC::VM& vm, const JSC::JSCell* owner) + : to_v8_object(&this->object) + , object(map, cell, vm, owner) + { + } + + Handle(double number) + : to_v8_object(&this->object) + , object(number) + { + } + + Handle(void* raw) + : to_v8_object(&this->object) + , object(raw) + { + } + + Handle(int32_t smi) + : to_v8_object(smi) + , object() + { + } + + Handle(const Handle& that) + { + *this = that; + } + + Handle(const ObjectLayout* that) + : to_v8_object(&this->object) + { + object = *that; + } + + Handle& operator=(const Handle& that) + { + object = that.object; + if (that.to_v8_object.type() == TaggedPointer::Type::Smi) { + to_v8_object = that.to_v8_object; + } else { + to_v8_object = &this->object; + } + return *this; + } + + Handle() + : to_v8_object(0) + , object() + { + } + + bool isCell() const + { + if (to_v8_object.type() == TaggedPointer::Type::Smi) { + return false; + } + const Map* map_ptr = object.map(); + // TODO(@190n) exhaustively switch on InstanceType + if (map_ptr == &Map::object_map || map_ptr == &Map::string_map) { + return true; + } else if (map_ptr == &Map::map_map || map_ptr == &Map::raw_ptr_map || map_ptr == &Map::oddball_map + || map_ptr == &Map::boolean_map || map_ptr == &Map::heap_number_map) { + return false; + } else { + RELEASE_ASSERT_NOT_REACHED("unknown Map at %p with instance type %" PRIx16, + map_ptr, map_ptr->instance_type); + } + } + + // if not SMI, holds &this->map so that V8 can see what kind of object this is + TaggedPointer to_v8_object; + ObjectLayout object; +}; + +} diff --git a/src/bun.js/bindings/v8/V8HandleScope.cpp b/src/bun.js/bindings/v8/V8HandleScope.cpp new file mode 100644 index 0000000000000..f7d20b76d2cf1 --- /dev/null +++ b/src/bun.js/bindings/v8/V8HandleScope.cpp @@ -0,0 +1,27 @@ +#include "V8HandleScope.h" + +#include "V8GlobalInternals.h" + +namespace v8 { + +HandleScope::HandleScope(Isolate* isolate_) + : isolate(isolate_) + , prev(isolate->globalInternals()->currentHandleScope()) + , buffer(HandleScopeBuffer::create(isolate_->vm(), isolate_->globalInternals()->handleScopeBufferStructure(isolate_->globalObject()))) +{ + isolate->globalInternals()->setCurrentHandleScope(this); +} + +HandleScope::~HandleScope() +{ + isolate->globalInternals()->setCurrentHandleScope(prev); + buffer = nullptr; +} + +uintptr_t* HandleScope::CreateHandle(internal::Isolate* isolate, uintptr_t value) +{ + auto* handleScope = reinterpret_cast(isolate)->globalInternals()->currentHandleScope(); + return &handleScope->buffer->createHandleFromExistingHandle(TaggedPointer::fromRaw(value))->value; +} + +} diff --git a/src/bun.js/bindings/v8/V8HandleScope.h b/src/bun.js/bindings/v8/V8HandleScope.h new file mode 100644 index 0000000000000..81f73e08ee1a8 --- /dev/null +++ b/src/bun.js/bindings/v8/V8HandleScope.h @@ -0,0 +1,62 @@ +#pragma once + +#include "v8.h" +#include "V8Isolate.h" +#include "v8_internal.h" +#include "V8HandleScopeBuffer.h" + +namespace v8 { + +class Number; + +class HandleScope { +public: + BUN_EXPORT HandleScope(Isolate* isolate); + BUN_EXPORT ~HandleScope(); + + template Local createLocal(JSC::VM& vm, JSC::JSValue value) + { + // TODO(@190n) handle more types + if (value.isString()) { + return Local(buffer->createHandle(value.asCell(), &Map::string_map, vm)); + } else if (value.isCell()) { + return Local(buffer->createHandle(value.asCell(), &Map::object_map, vm)); + } else if (value.isInt32()) { + return Local(buffer->createSmiHandle(value.asInt32())); + } else if (value.isNumber()) { + return Local(buffer->createDoubleHandle(value.asNumber())); + } else if (value.isUndefined()) { + return Local(isolate->globalInternals()->undefinedSlot()); + } else if (value.isNull()) { + return Local(isolate->globalInternals()->nullSlot()); + } else if (value.isTrue()) { + return Local(isolate->globalInternals()->trueSlot()); + } else if (value.isFalse()) { + return Local(isolate->globalInternals()->falseSlot()); + } else { + V8_UNIMPLEMENTED(); + return Local(); + } + } + + template Local createRawLocal(void* ptr) + { + TaggedPointer* handle = buffer->createRawHandle(ptr); + return Local(handle); + } + + friend class EscapableHandleScopeBase; + +protected: + // must be 24 bytes to match V8 layout + Isolate* isolate; + HandleScope* prev; + HandleScopeBuffer* buffer; + + // is protected in v8, which matters on windows + BUN_EXPORT static uintptr_t* CreateHandle(internal::Isolate* isolate, uintptr_t value); +}; + +static_assert(sizeof(HandleScope) == 24, "HandleScope has wrong layout"); + +} diff --git a/src/bun.js/bindings/v8/V8HandleScopeBuffer.cpp b/src/bun.js/bindings/v8/V8HandleScopeBuffer.cpp new file mode 100644 index 0000000000000..8468cc64674d5 --- /dev/null +++ b/src/bun.js/bindings/v8/V8HandleScopeBuffer.cpp @@ -0,0 +1,89 @@ +#include "V8HandleScopeBuffer.h" + +namespace v8 { + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const JSC::ClassInfo HandleScopeBuffer::s_info = { + "HandleScopeBuffer"_s, + nullptr, + nullptr, + nullptr, + CREATE_METHOD_TABLE(HandleScopeBuffer) +}; + +HandleScopeBuffer* HandleScopeBuffer::create(JSC::VM& vm, JSC::Structure* structure) +{ + HandleScopeBuffer* buffer = new (NotNull, JSC::allocateCell(vm)) HandleScopeBuffer(vm, structure); + buffer->finishCreation(vm); + return buffer; +} + +template +void HandleScopeBuffer::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + HandleScopeBuffer* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + WTF::Locker locker { thisObject->gc_lock }; + + for (auto& handle : thisObject->storage) { + if (handle.isCell()) { + visitor.append(handle.object.contents.cell); + } + } +} + +DEFINE_VISIT_CHILDREN(HandleScopeBuffer); + +Handle& HandleScopeBuffer::createEmptyHandle() +{ + WTF::Locker locker { gc_lock }; + storage.append(Handle {}); + return storage.last(); +} + +TaggedPointer* HandleScopeBuffer::createHandle(JSCell* ptr, const Map* map, JSC::VM& vm) +{ + auto& handle = createEmptyHandle(); + handle = Handle(map, ptr, vm, this); + return &handle.to_v8_object; +} + +TaggedPointer* HandleScopeBuffer::createRawHandle(void* ptr) +{ + auto& handle = createEmptyHandle(); + handle = Handle(ptr); + return &handle.to_v8_object; +} + +TaggedPointer* HandleScopeBuffer::createSmiHandle(int32_t smi) +{ + auto& handle = createEmptyHandle(); + handle = Handle(smi); + return &handle.to_v8_object; +} + +TaggedPointer* HandleScopeBuffer::createDoubleHandle(double value) +{ + auto& handle = createEmptyHandle(); + handle = Handle(value); + return &handle.to_v8_object; +} + +TaggedPointer* HandleScopeBuffer::createHandleFromExistingHandle(TaggedPointer address) +{ + auto& handle = createEmptyHandle(); + int32_t smi; + if (address.getSmi(smi)) { + handle = Handle(smi); + } else { + auto* v8_object = address.getPtr(); + handle = Handle(v8_object); + } + return &handle.to_v8_object; +} + +} diff --git a/src/bun.js/bindings/v8/V8HandleScopeBuffer.h b/src/bun.js/bindings/v8/V8HandleScopeBuffer.h new file mode 100644 index 0000000000000..a045951436973 --- /dev/null +++ b/src/bun.js/bindings/v8/V8HandleScopeBuffer.h @@ -0,0 +1,59 @@ +#pragma once + +#include "v8.h" +#include "V8TaggedPointer.h" +#include "V8Map.h" +#include "V8Handle.h" + +namespace v8 { + +// An array used by HandleScope to store the items. Must keep pointer stability when resized, since +// v8::Locals point inside this array. +class HandleScopeBuffer : public JSC::JSCell { +public: + using Base = JSC::JSCell; + + static HandleScopeBuffer* create(JSC::VM& vm, JSC::Structure* structure); + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) + { + return JSC::Structure::create(vm, globalObject, JSC::jsNull(), JSC::TypeInfo(JSC::CellType, StructureFlags), info(), 0, 0); + } + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForHandleScopeBuffer.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForHandleScopeBuffer = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForHandleScopeBuffer.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForHandleScopeBuffer = std::forward(space); }); + } + + TaggedPointer* createHandle(JSC::JSCell* object, const Map* map, JSC::VM& vm); + TaggedPointer* createRawHandle(void* ptr); + TaggedPointer* createSmiHandle(int32_t smi); + TaggedPointer* createDoubleHandle(double value); + TaggedPointer* createHandleFromExistingHandle(TaggedPointer address); + + DECLARE_INFO; + DECLARE_VISIT_CHILDREN; + + friend class EscapableHandleScopeBase; + +private: + WTF::Lock gc_lock; + WTF::SegmentedVector storage; + + Handle& createEmptyHandle(); + + HandleScopeBuffer(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } +}; + +} diff --git a/src/bun.js/bindings/v8/V8InternalFieldObject.cpp b/src/bun.js/bindings/v8/V8InternalFieldObject.cpp new file mode 100644 index 0000000000000..69dd3878da145 --- /dev/null +++ b/src/bun.js/bindings/v8/V8InternalFieldObject.cpp @@ -0,0 +1,39 @@ +#include "V8InternalFieldObject.h" + +namespace v8 { + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const JSC::ClassInfo InternalFieldObject::s_info = { + "InternalFieldObject"_s, + &Base::s_info, + nullptr, + nullptr, + CREATE_METHOD_TABLE(InternalFieldObject) +}; + +InternalFieldObject* InternalFieldObject::create(JSC::VM& vm, JSC::Structure* structure, Local objectTemplate) +{ + // TODO figure out how this works with __internals + // maybe pass a Local + auto object = new (NotNull, JSC::allocateCell(vm)) InternalFieldObject(vm, structure, objectTemplate->InternalFieldCount()); + object->finishCreation(vm); + return object; +} + +template +void InternalFieldObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + InternalFieldObject* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + for (auto& value : thisObject->fields) { + visitor.append(value); + } +} + +DEFINE_VISIT_CHILDREN(InternalFieldObject); + +} diff --git a/src/bun.js/bindings/v8/V8InternalFieldObject.h b/src/bun.js/bindings/v8/V8InternalFieldObject.h new file mode 100644 index 0000000000000..6a118786bf178 --- /dev/null +++ b/src/bun.js/bindings/v8/V8InternalFieldObject.h @@ -0,0 +1,46 @@ +#pragma once + +#include "V8ObjectTemplate.h" + +namespace v8 { + +class InternalFieldObject : public JSC::JSDestructibleObject { +public: + using Base = JSC::JSDestructibleObject; + + DECLARE_INFO; + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForInternalFieldObject.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForInternalFieldObject = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForInternalFieldObject.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForInternalFieldObject = std::forward(space); }); + } + + // never changes size + using FieldContainer = WTF::FixedVector>; + + FieldContainer* internalFields() { return &fields; } + static InternalFieldObject* create(JSC::VM& vm, JSC::Structure* structure, Local objectTemplate); + + DECLARE_VISIT_CHILDREN; + +protected: + InternalFieldObject(JSC::VM& vm, JSC::Structure* structure, int internalFieldCount) + : Base(vm, structure) + , fields(internalFieldCount, JSC::WriteBarrier(vm, this, JSC::jsUndefined())) + { + } + +private: + // TODO(@190n) use template with fixed size array for small counts + FieldContainer fields; +}; + +} diff --git a/src/bun.js/bindings/v8/V8Isolate.cpp b/src/bun.js/bindings/v8/V8Isolate.cpp new file mode 100644 index 0000000000000..7ddca0beeefcb --- /dev/null +++ b/src/bun.js/bindings/v8/V8Isolate.cpp @@ -0,0 +1,28 @@ +#include "V8Isolate.h" +#include "V8HandleScope.h" +#include "ZigGlobalObject.h" + +namespace v8 { + +// Returns the isolate inside which the current thread is running or nullptr. +Isolate* Isolate::TryGetCurrent() +{ + auto* global = defaultGlobalObject(); + + return global ? reinterpret_cast(&global->V8GlobalInternals()->roots) : nullptr; +} + +// Returns the isolate inside which the current thread is running. +Isolate* Isolate::GetCurrent() +{ + auto* global = defaultGlobalObject(); + + return global ? reinterpret_cast(&global->V8GlobalInternals()->roots) : nullptr; +} + +Local Isolate::GetCurrentContext() +{ + return currentHandleScope()->createRawLocal(this); +} + +} diff --git a/src/bun.js/bindings/v8/V8Isolate.h b/src/bun.js/bindings/v8/V8Isolate.h new file mode 100644 index 0000000000000..4616c22060bd3 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Isolate.h @@ -0,0 +1,34 @@ +#pragma once + +#include "v8.h" +#include "V8Context.h" +#include "V8Local.h" +#include "V8GlobalInternals.h" + +namespace v8 { + +class HandleScope; + +// This currently is just a pointer to a v8::Roots +// We do that so that we can recover the context and the VM from the "Isolate," and so that inlined +// V8 functions can find values at certain fixed offsets from the Isolate +class Isolate final { +public: + Isolate() = default; + + // Returns the isolate inside which the current thread is running or nullptr. + BUN_EXPORT static Isolate* TryGetCurrent(); + + // Returns the isolate inside which the current thread is running. + BUN_EXPORT static Isolate* GetCurrent(); + + BUN_EXPORT Local GetCurrentContext(); + + static Isolate* fromGlobalObject(Zig::GlobalObject* globalObject) { return reinterpret_cast(&globalObject->V8GlobalInternals()->roots); } + Zig::GlobalObject* globalObject() { return reinterpret_cast(this)->parent->globalObject; } + JSC::VM& vm() { return globalObject()->vm(); } + GlobalInternals* globalInternals() { return globalObject()->V8GlobalInternals(); } + HandleScope* currentHandleScope() { return globalInternals()->currentHandleScope(); } +}; + +} diff --git a/src/bun.js/bindings/v8/V8Local.h b/src/bun.js/bindings/v8/V8Local.h new file mode 100644 index 0000000000000..a2f23ec1b496a --- /dev/null +++ b/src/bun.js/bindings/v8/V8Local.h @@ -0,0 +1,42 @@ +#pragma once + +#include "root.h" + +#include "V8TaggedPointer.h" + +namespace v8 { + +template +class Local final { +public: + Local() + : location(nullptr) + { + } + + Local(TaggedPointer* slot) + : location(slot) + { + } + + bool IsEmpty() const { return location == nullptr; } + + T* operator*() const { return reinterpret_cast(location); } + T* operator->() const { return reinterpret_cast(location); } + + template + Local reinterpret() const + { + return Local(location); + } + + TaggedPointer tagged() const + { + return *location; + } + +private: + TaggedPointer* location; +}; + +} diff --git a/src/bun.js/bindings/v8/V8Map.cpp b/src/bun.js/bindings/v8/V8Map.cpp new file mode 100644 index 0000000000000..300c242958c2b --- /dev/null +++ b/src/bun.js/bindings/v8/V8Map.cpp @@ -0,0 +1,14 @@ +#include "V8Map.h" + +namespace v8 { + +// TODO give these more appropriate instance types +const Map Map::map_map(InstanceType::Object); +const Map Map::object_map(InstanceType::Object); +const Map Map::raw_ptr_map(InstanceType::Object); +const Map Map::oddball_map(InstanceType::Oddball); +const Map Map::boolean_map(InstanceType::Oddball); +const Map Map::string_map(InstanceType::String); +const Map Map::heap_number_map(InstanceType::HeapNumber); + +} diff --git a/src/bun.js/bindings/v8/V8Map.h b/src/bun.js/bindings/v8/V8Map.h new file mode 100644 index 0000000000000..4448c235aaa75 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Map.h @@ -0,0 +1,60 @@ +#pragma once + +#include "V8TaggedPointer.h" + +namespace v8 { + +enum class InstanceType : uint16_t { + // v8-internal.h:787, kFirstNonstringType is 0x80 + String = 0x7f, + // "Oddball" in V8 means undefined or null + // v8-internal.h:788 + Oddball = 0x83, + // v8-internal.h:1016 kFirstNonstringType + // this cannot be kJSObjectType (or anything in the range [kJSObjectType, kLastJSApiObjectType]) + // because then V8 will try to access internal fields directly instead of calling + // SlowGetInternalField + Object = 0x80, + // a number that doesn't fit in int32_t and is stored on the heap (for us, in the + // HandleScopeBuffer) + HeapNumber = 0x82, +}; + +// V8's description of the structure of an object +struct Map { + // the structure of the map itself (always points to map_map) + TaggedPointer meta_map; + // TBD whether we need to put anything here to please inlined V8 functions + uint32_t unused; + // describes which kind of object this is. we shouldn't actually need to create very many + // instance types -- only ones for primitives, and one to make sure V8 thinks it cannot take the + // fast path when accessing internal fields + // (v8::internal::Internals::CanHaveInternalField, in v8-internal.h) + InstanceType instance_type; + + // the map used by maps + static const Map map_map; + // the map used by objects inheriting JSCell + static const Map object_map; + // the map used by pointers to non-JSCell objects stored in handles + static const Map raw_ptr_map; + // the map used by oddballs (null, undefined) + static const Map oddball_map; + // the map used by booleans + static const Map boolean_map; + // the map used by strings + static const Map string_map; + // the map used by heap numbers + static const Map heap_number_map; + + Map(InstanceType instance_type_) + : meta_map(const_cast(&map_map)) + , unused(0xaaaaaaaa) + , instance_type(instance_type_) + { + } +}; + +static_assert(sizeof(Map) == 16, "Map has wrong layout"); + +} diff --git a/src/bun.js/bindings/v8/V8Maybe.h b/src/bun.js/bindings/v8/V8Maybe.h new file mode 100644 index 0000000000000..6ad41688adb8f --- /dev/null +++ b/src/bun.js/bindings/v8/V8Maybe.h @@ -0,0 +1,33 @@ +#pragma once + +namespace v8 { + +template +class Maybe { +public: + Maybe() + : has_value(false) + { + } + explicit Maybe(const T& t) + : has_value(true) + , value(t) + { + } + bool has_value; + T value; +}; + +template +inline Maybe Nothing() +{ + return Maybe(); +} + +template +inline Maybe Just(const T& t) +{ + return Maybe(t); +} + +} diff --git a/src/bun.js/bindings/v8/V8MaybeLocal.h b/src/bun.js/bindings/v8/V8MaybeLocal.h new file mode 100644 index 0000000000000..f53a6feedbf94 --- /dev/null +++ b/src/bun.js/bindings/v8/V8MaybeLocal.h @@ -0,0 +1,24 @@ +#pragma once + +#include "V8Local.h" + +namespace v8 { + +template +class MaybeLocal { +public: + MaybeLocal() + : local_(Local()) {}; + + template MaybeLocal(Local that) + : local_(that) + { + } + + bool IsEmpty() const { return local_.IsEmpty(); } + +private: + Local local_; +}; + +} diff --git a/src/bun.js/bindings/v8/V8Number.cpp b/src/bun.js/bindings/v8/V8Number.cpp new file mode 100644 index 0000000000000..31c6744f2fc0a --- /dev/null +++ b/src/bun.js/bindings/v8/V8Number.cpp @@ -0,0 +1,16 @@ +#include "V8Number.h" +#include "V8HandleScope.h" + +namespace v8 { + +Local Number::New(Isolate* isolate, double value) +{ + return isolate->currentHandleScope()->createLocal(isolate->vm(), JSC::jsNumber(value)); +} + +double Number::Value() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).asNumber(); +} + +} diff --git a/src/bun.js/bindings/v8/V8Number.h b/src/bun.js/bindings/v8/V8Number.h new file mode 100644 index 0000000000000..7ec8e6d54cd51 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Number.h @@ -0,0 +1,17 @@ +#pragma once + +#include "v8.h" +#include "V8Primitive.h" +#include "V8Local.h" +#include "V8Isolate.h" + +namespace v8 { + +class Number : public Primitive { +public: + BUN_EXPORT static Local New(Isolate* isolate, double value); + + BUN_EXPORT double Value() const; +}; + +} diff --git a/src/bun.js/bindings/v8/V8Object.cpp b/src/bun.js/bindings/v8/V8Object.cpp new file mode 100644 index 0000000000000..cb6d5215eab23 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Object.cpp @@ -0,0 +1,91 @@ +#include "V8Object.h" +#include "V8InternalFieldObject.h" +#include "V8HandleScope.h" + +#include "JavaScriptCore/ConstructData.h" +#include "JavaScriptCore/ObjectConstructor.h" + +using JSC::Identifier; +using JSC::JSFinalObject; +using JSC::JSGlobalObject; +using JSC::JSObject; +using JSC::JSValue; +using JSC::PutPropertySlot; + +namespace v8 { + +using FieldContainer = InternalFieldObject::FieldContainer; + +static FieldContainer* getInternalFieldsContainer(Object* object) +{ + JSObject* js_object = object->localToObjectPointer(); + + // TODO(@190n): do we need to unwrap proxies like node-jsc did? + + if (auto ifo = JSC::jsDynamicCast(js_object)) { + return ifo->internalFields(); + } + + return nullptr; +} + +Local Object::New(Isolate* isolate) +{ + JSFinalObject* object = JSC::constructEmptyObject(isolate->globalObject()); + return isolate->currentHandleScope()->createLocal(isolate->vm(), object); +} + +Maybe Object::Set(Local context, Local key, Local value) +{ + Zig::GlobalObject* globalObject = context->globalObject(); + JSObject* object = localToObjectPointer(); + JSValue k = key->localToJSValue(globalObject->V8GlobalInternals()); + JSValue v = value->localToJSValue(globalObject->V8GlobalInternals()); + auto& vm = globalObject->vm(); + + auto scope = DECLARE_CATCH_SCOPE(vm); + PutPropertySlot slot(object, false); + + Identifier identifier = k.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, Nothing()); + + if (!object->put(object, globalObject, identifier, v, slot)) { + scope.clearExceptionExceptTermination(); + return Nothing(); + } + if (scope.exception()) { + scope.clearException(); + return Nothing(); + } + return Just(true); +} + +void Object::SetInternalField(int index, Local data) +{ + auto* fields = getInternalFieldsContainer(this); + RELEASE_ASSERT(fields, "object has no internal fields"); + RELEASE_ASSERT(index >= 0 && index < fields->size(), "internal field index is out of bounds"); + JSObject* js_object = localToObjectPointer(); + auto* globalObject = JSC::jsDynamicCast(js_object->globalObject()); + fields->at(index).set(globalObject->vm(), localToCell(), data->localToJSValue(globalObject->V8GlobalInternals())); +} + +Local Object::GetInternalField(int index) +{ + return SlowGetInternalField(index); +} + +Local Object::SlowGetInternalField(int index) +{ + auto* fields = getInternalFieldsContainer(this); + JSObject* js_object = localToObjectPointer(); + auto* globalObject = JSC::jsDynamicCast(js_object->globalObject()); + HandleScope* handleScope = Isolate::fromGlobalObject(globalObject)->currentHandleScope(); + if (fields && index >= 0 && index < fields->size()) { + auto& field = fields->at(index); + return handleScope->createLocal(globalObject->vm(), field.get()); + } + return handleScope->createLocal(globalObject->vm(), JSC::jsUndefined()); +} + +} diff --git a/src/bun.js/bindings/v8/V8Object.h b/src/bun.js/bindings/v8/V8Object.h new file mode 100644 index 0000000000000..6c74b0f035949 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Object.h @@ -0,0 +1,25 @@ +#pragma once + +#include "v8.h" +#include "V8Value.h" +#include "V8Local.h" +#include "V8Isolate.h" +#include "V8Maybe.h" +#include "V8Context.h" +#include "V8Data.h" + +namespace v8 { + +class Object : public Value { +public: + BUN_EXPORT static Local New(Isolate* isolate); + BUN_EXPORT Maybe Set(Local context, Local key, Local value); + BUN_EXPORT void SetInternalField(int index, Local data); + // usually inlined + BUN_EXPORT Local GetInternalField(int index); + +private: + BUN_EXPORT Local SlowGetInternalField(int index); +}; + +} diff --git a/src/bun.js/bindings/v8/V8ObjectTemplate.cpp b/src/bun.js/bindings/v8/V8ObjectTemplate.cpp new file mode 100644 index 0000000000000..07b32031f13fe --- /dev/null +++ b/src/bun.js/bindings/v8/V8ObjectTemplate.cpp @@ -0,0 +1,108 @@ +#include "V8ObjectTemplate.h" +#include "V8InternalFieldObject.h" +#include "V8GlobalInternals.h" +#include "V8HandleScope.h" + +#include "JavaScriptCore/FunctionPrototype.h" +#include "JavaScriptCore/LazyPropertyInlines.h" +#include "JavaScriptCore/VMTrapsInlines.h" + +using JSC::JSGlobalObject; +using JSC::JSValue; +using JSC::LazyProperty; +using JSC::Structure; + +namespace v8 { + +void ObjectTemplate::finishCreation(JSC::VM& vm) +{ + Base::finishCreation(vm); + __internals.objectStructure.initLater([](const LazyProperty::Initializer& init) { + init.set(JSC::Structure::create( + init.vm, + init.owner->globalObject(), + init.owner->globalObject()->objectPrototype(), + JSC::TypeInfo(JSC::ObjectType, InternalFieldObject::StructureFlags), + InternalFieldObject::info())); + }); +} + +// for CREATE_METHOD_TABLE +namespace JSCastingHelpers = JSC::JSCastingHelpers; + +const JSC::ClassInfo ObjectTemplate::s_info = { + "ObjectTemplate"_s, + &Base::s_info, + nullptr, + nullptr, + CREATE_METHOD_TABLE(ObjectTemplate) +}; + +Local ObjectTemplate::New(Isolate* isolate, Local constructor) +{ + RELEASE_ASSERT(constructor.IsEmpty()); + auto* globalObject = isolate->globalObject(); + auto& vm = globalObject->vm(); + auto* globalInternals = globalObject->V8GlobalInternals(); + Structure* structure = globalInternals->objectTemplateStructure(globalObject); + auto* objectTemplate = new (NotNull, JSC::allocateCell(vm)) ObjectTemplate(vm, structure); + // TODO pass constructor + objectTemplate->finishCreation(vm); + return globalInternals->currentHandleScope()->createLocal(vm, objectTemplate); +} + +MaybeLocal ObjectTemplate::NewInstance(Local context) +{ + // TODO handle constructor + // TODO handle interceptors? + + auto& vm = context->vm(); + auto thisObj = localToObjectPointer(); + + // get a structure + // must take thisObj because JSC needs the native pointer + auto structure = internals().objectStructure.get(thisObj); + + // create object from it + // InternalFieldObject needs a Local, which we can create using the `this` + // pointer as we know this method itself was called through a Local + auto newInstance = InternalFieldObject::create(vm, structure, Local(reinterpret_cast(this))); + + // todo: apply properties + + return MaybeLocal(context->currentHandleScope()->createLocal(vm, newInstance)); +} + +template +void ObjectTemplate::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + ObjectTemplate* fn = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(fn, info()); + Base::visitChildren(fn, visitor); + + fn->__internals.objectStructure.visit(visitor); +} + +DEFINE_VISIT_CHILDREN(ObjectTemplate); + +void ObjectTemplate::SetInternalFieldCount(int value) +{ + internals().internalFieldCount = value; +} + +int ObjectTemplate::InternalFieldCount() const +{ + return internals().internalFieldCount; +} + +Structure* ObjectTemplate::createStructure(JSC::VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, + globalObject, + prototype, + JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), + info()); +} + +} diff --git a/src/bun.js/bindings/v8/V8ObjectTemplate.h b/src/bun.js/bindings/v8/V8ObjectTemplate.h new file mode 100644 index 0000000000000..b8d92118f7f73 --- /dev/null +++ b/src/bun.js/bindings/v8/V8ObjectTemplate.h @@ -0,0 +1,83 @@ +#pragma once + +#include "JavaScriptCore/SubspaceAccess.h" +#include "v8.h" +#include "V8Context.h" +#include "V8Local.h" +#include "V8Isolate.h" +#include "V8FunctionTemplate.h" +#include "V8MaybeLocal.h" +#include "V8Object.h" +#include "V8Template.h" + +namespace v8 { + +// If this inherited Template like it does in V8, the layout would be wrong for JSC HeapCell. +// Inheritance shouldn't matter for the ABI. +class ObjectTemplate : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + + DECLARE_INFO; + + BUN_EXPORT static Local New(Isolate* isolate, Local constructor = Local()); + BUN_EXPORT MaybeLocal NewInstance(Local context); + BUN_EXPORT void SetInternalFieldCount(int value); + BUN_EXPORT int InternalFieldCount() const; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype); + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForObjectTemplate.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForObjectTemplate = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForObjectTemplate.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForObjectTemplate = std::forward(space); }); + } + + DECLARE_VISIT_CHILDREN; + +private: + class Internals { + int internalFieldCount = 0; + JSC::LazyProperty objectStructure; + friend class ObjectTemplate; + }; + + // do not use directly inside exported V8 functions, use internals() + Internals __internals; + + ObjectTemplate* localToObjectPointer() + { + return reinterpret_cast(this)->localToObjectPointer(); + } + + const ObjectTemplate* localToObjectPointer() const + { + return reinterpret_cast(this)->localToObjectPointer(); + } + + Internals& internals() + { + return localToObjectPointer()->__internals; + } + + const Internals& internals() const + { + return localToObjectPointer()->__internals; + } + + ObjectTemplate(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure, Template::DummyCallback, Template::DummyCallback) + { + } + + void finishCreation(JSC::VM& vm); +}; + +} diff --git a/src/bun.js/bindings/v8/V8Oddball.h b/src/bun.js/bindings/v8/V8Oddball.h new file mode 100644 index 0000000000000..c58f40c149753 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Oddball.h @@ -0,0 +1,28 @@ +#pragma once + +#include "V8TaggedPointer.h" +#include "V8Map.h" + +namespace v8 { + +struct Oddball { + enum class Kind : int { + kUndefined = 4, + kNull = 3, + kInvalid = 255, + kTrue = 1, + kFalse = 0, + }; + + TaggedPointer map; + uintptr_t unused[4]; + TaggedPointer kind; + + Oddball(Kind kind_, const Map* map_ = &Map::oddball_map) + : map(const_cast(map_)) + , kind(TaggedPointer(static_cast(kind_))) + { + } +}; + +} diff --git a/src/bun.js/bindings/v8/V8Primitive.h b/src/bun.js/bindings/v8/V8Primitive.h new file mode 100644 index 0000000000000..afb9565fe62c7 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Primitive.h @@ -0,0 +1,9 @@ +#pragma once + +#include "V8Value.h" + +namespace v8 { + +class Primitive : public Value {}; + +} diff --git a/src/bun.js/bindings/v8/V8Roots.cpp b/src/bun.js/bindings/v8/V8Roots.cpp new file mode 100644 index 0000000000000..025f68a714cf0 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Roots.cpp @@ -0,0 +1,15 @@ +#include "V8Roots.h" +#include "V8GlobalInternals.h" + +namespace v8 { + +Roots::Roots(GlobalInternals* parent_) + : parent(parent_) +{ + roots[kUndefinedValueRootIndex] = TaggedPointer(&parent->undefinedValue); + roots[kNullValueRootIndex] = TaggedPointer(&parent->nullValue); + roots[kTrueValueRootIndex] = TaggedPointer(&parent->trueValue); + roots[kFalseValueRootIndex] = TaggedPointer(&parent->falseValue); +} + +} diff --git a/src/bun.js/bindings/v8/V8Roots.h b/src/bun.js/bindings/v8/V8Roots.h new file mode 100644 index 0000000000000..1a538388e4416 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Roots.h @@ -0,0 +1,32 @@ +#pragma once + +#include "V8TaggedPointer.h" + +namespace v8 { + +class GlobalInternals; + +// Container for some data that V8 expects to find at certain offsets. Isolate and Context pointers +// actually point to this object. It is a separate struct so that we can use offsetof() to make sure +// the layout is correct. +struct Roots { + // v8-internal.h:775 + static const int kUndefinedValueRootIndex = 4; + static const int kTheHoleValueRootIndex = 5; + static const int kNullValueRootIndex = 6; + static const int kTrueValueRootIndex = 7; + static const int kFalseValueRootIndex = 8; + + GlobalInternals* parent; + + uintptr_t padding[73]; + + TaggedPointer roots[9]; + + Roots(GlobalInternals* parent); +}; + +// kIsolateRootsOffset at v8-internal.h:744 +static_assert(offsetof(Roots, roots) == 592, "Roots does not match V8 layout"); + +} diff --git a/src/bun.js/bindings/v8/V8Signature.h b/src/bun.js/bindings/v8/V8Signature.h new file mode 100644 index 0000000000000..91387756535a1 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Signature.h @@ -0,0 +1,9 @@ +#pragma once + +#include "V8Data.h" + +namespace v8 { + +class Signature : public Data {}; + +} diff --git a/src/bun.js/bindings/v8/V8String.cpp b/src/bun.js/bindings/v8/V8String.cpp new file mode 100644 index 0000000000000..7e6d9965d91d1 --- /dev/null +++ b/src/bun.js/bindings/v8/V8String.cpp @@ -0,0 +1,167 @@ +#include "V8String.h" + +#include "V8HandleScope.h" +#include "wtf/SIMDUTF.h" + +using JSC::JSString; + +namespace v8 { + +MaybeLocal String::NewFromUtf8(Isolate* isolate, char const* data, NewStringType type, int signed_length) +{ + // TODO(@190n) maybe use JSC::AtomString instead of ignoring type + (void)type; + size_t length = 0; + if (signed_length < 0) { + length = strlen(data); + } else { + length = static_cast(signed_length); + } + + if (length > JSString::MaxLength) { + // empty + return MaybeLocal(); + } + + auto& vm = isolate->vm(); + std::span span(reinterpret_cast(data), length); + // ReplacingInvalidSequences matches how v8 behaves here + auto string = WTF::String::fromUTF8ReplacingInvalidSequences(span); + JSString* jsString = JSC::jsString(vm, string); + return MaybeLocal(isolate->currentHandleScope()->createLocal(vm, jsString)); +} + +MaybeLocal String::NewFromOneByte(Isolate* isolate, const uint8_t* data, NewStringType type, int signed_length) +{ + (void)type; + size_t length = 0; + if (signed_length < 0) { + length = strlen(reinterpret_cast(data)); + } else { + length = static_cast(signed_length); + } + + if (length > JSString::MaxLength) { + // empty + return MaybeLocal(); + } + + auto& vm = isolate->vm(); + std::span span(data, length); + WTF::String string(span); + JSString* jsString = JSC::jsString(vm, string); + return MaybeLocal(isolate->currentHandleScope()->createLocal(vm, jsString)); +} + +int String::Utf8Length(Isolate* isolate) const +{ + auto jsString = localToObjectPointer(); + if (jsString->length() == 0) { + return 0; + } + + auto str = jsString->view(isolate->globalObject()); + if (str->is8Bit()) { + const auto span = str->span8(); + size_t len = simdutf::utf8_length_from_latin1(reinterpret_cast(span.data()), span.size()); + return static_cast(len); + } else { + const auto span = str->span16(); + size_t len = simdutf::utf8_length_from_utf16(span.data(), span.size()); + return static_cast(len); + } +} + +bool String::IsOneByte() const +{ + auto jsString = localToObjectPointer(); + if (jsString->length() == 0) { + return true; + } + auto impl = jsString->tryGetValue(); + return impl->is8Bit(); +} + +bool String::ContainsOnlyOneByte() const +{ + auto jsString = localToObjectPointer(); + if (jsString->length() == 0) { + return true; + } + auto impl = jsString->tryGetValue(); + return impl->containsOnlyLatin1(); +} + +bool String::IsExternal() const +{ + auto jsString = localToObjectPointer(); + if (jsString->length() == 0) { + return false; + } + auto impl = jsString->tryGetValue(); + return !impl->isNull() && impl->impl()->isExternal(); +} + +bool String::IsExternalTwoByte() const +{ + auto jsString = localToObjectPointer(); + if (jsString->length() == 0) { + return false; + } + auto impl = jsString->tryGetValue(); + return !impl->isNull() && impl->impl()->isExternal() && !impl->is8Bit(); +} + +bool String::IsExternalOneByte() const +{ + auto jsString = localToObjectPointer(); + if (jsString->length() == 0) { + return false; + } + auto impl = jsString->tryGetValue(); + return !impl->isNull() && impl->impl()->isExternal() && impl->is8Bit(); +} + +extern "C" size_t TextEncoder__encodeInto8(const LChar* stringPtr, size_t stringLen, void* ptr, size_t len); +extern "C" size_t TextEncoder__encodeInto16(const UChar* stringPtr, size_t stringLen, void* ptr, size_t len); + +int String::WriteUtf8(Isolate* isolate, char* buffer, int length, int* nchars_ref, int options) const +{ + RELEASE_ASSERT(options == 0); + auto jsString = localToObjectPointer(); + WTF::String string = jsString->getString(isolate->globalObject()); + + size_t unsigned_length = length < 0 ? SIZE_MAX : length; + + uint64_t result = string.is8Bit() ? TextEncoder__encodeInto8(string.span8().data(), string.span8().size(), buffer, unsigned_length) + : TextEncoder__encodeInto16(string.span16().data(), string.span16().size(), buffer, unsigned_length); + uint32_t read = static_cast(result); + uint32_t written = static_cast(result >> 32); + + if (written < length && read == string.length()) { + buffer[written] = 0; + written++; + } + if (read < string.length() && U16_IS_SURROGATE(string[read]) && written + 3 <= length) { + // encode unpaired surrogate + UChar surrogate = string[read]; + buffer[written + 0] = 0xe0 | (surrogate >> 12); + buffer[written + 1] = 0x80 | ((surrogate >> 6) & 0x3f); + buffer[written + 2] = 0x80 | (surrogate & 0x3f); + written += 3; + read += 1; + } + if (nchars_ref) { + *nchars_ref = read; + } + + return written; +} + +int String::Length() const +{ + auto jsString = localToObjectPointer(); + return static_cast(jsString->length()); +} + +} diff --git a/src/bun.js/bindings/v8/V8String.h b/src/bun.js/bindings/v8/V8String.h new file mode 100644 index 0000000000000..4ec76762c7614 --- /dev/null +++ b/src/bun.js/bindings/v8/V8String.h @@ -0,0 +1,79 @@ +#pragma once + +#include "v8.h" +#include "V8Primitive.h" +#include "V8MaybeLocal.h" +#include "V8Isolate.h" + +namespace v8 { + +enum class NewStringType { + kNormal, + kInternalized, +}; + +class String : Primitive { +public: + enum WriteOptions { + NO_OPTIONS = 0, + HINT_MANY_WRITES_EXPECTED = 1, + NO_NULL_TERMINATION = 2, + PRESERVE_ONE_BYTE_NULL = 4, + REPLACE_INVALID_UTF8 = 8, + }; + + BUN_EXPORT static MaybeLocal NewFromUtf8(Isolate* isolate, char const* data, NewStringType type, int length = -1); + BUN_EXPORT static MaybeLocal NewFromOneByte(Isolate* isolate, const uint8_t* data, NewStringType type, int length); + + // length: number of bytes in buffer (if negative, assume it is large enough) + // nchars_ref: store number of code units written here + // return: number of bytes copied including null terminator + // + // if string ends in a surrogate pair, but buffer is one byte too small to store it, instead + // endcode the unpaired lead surrogate with WTF-8 + BUN_EXPORT int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, int* nchars_ref = nullptr, int options = NO_OPTIONS) const; + BUN_EXPORT int Length() const; + + /** + * Returns the number of bytes in the UTF-8 encoded + * representation of this string. + */ + BUN_EXPORT int Utf8Length(Isolate* isolate) const; + + /** + * Returns whether this string is known to contain only one byte data, + * i.e. ISO-8859-1 code points. + * Does not read the string. + * False negatives are possible. + */ + BUN_EXPORT bool IsOneByte() const; + + /** + * Returns whether this string contain only one byte data, + * i.e. ISO-8859-1 code points. + * Will read the entire string in some cases. + */ + BUN_EXPORT bool ContainsOnlyOneByte() const; + + /** + * Returns true if the string is external. + */ + BUN_EXPORT bool IsExternal() const; + + /** + * Returns true if the string is both external and two-byte. + */ + BUN_EXPORT bool IsExternalTwoByte() const; + + /** + * Returns true if the string is both external and one-byte. + */ + BUN_EXPORT bool IsExternalOneByte() const; + + JSC::JSString* localToJSString() + { + return localToObjectPointer(); + } +}; + +} diff --git a/src/bun.js/bindings/v8/V8TaggedPointer.h b/src/bun.js/bindings/v8/V8TaggedPointer.h new file mode 100644 index 0000000000000..910adb2845a14 --- /dev/null +++ b/src/bun.js/bindings/v8/V8TaggedPointer.h @@ -0,0 +1,92 @@ +#pragma once + +#include "v8.h" + +namespace v8 { + +struct TaggedPointer { + uintptr_t value; + + enum class Type : uint8_t { + Smi, + StrongPointer, + WeakPointer, + }; + + TaggedPointer() + : TaggedPointer(nullptr) {}; + TaggedPointer(const TaggedPointer&) = default; + TaggedPointer& operator=(const TaggedPointer&) = default; + bool operator==(const TaggedPointer& other) const { return value == other.value; } + + TaggedPointer(void* ptr, bool weak) + : value(reinterpret_cast(ptr) | (weak ? 3 : 1)) + { + RELEASE_ASSERT((reinterpret_cast(ptr) & 3) == 0); + } + + TaggedPointer(void* ptr) + : TaggedPointer(ptr, false) + { + } + + TaggedPointer(int32_t smi) + : value(static_cast(smi) << 32) + { + } + + static TaggedPointer fromRaw(uintptr_t raw) + { + TaggedPointer tagged; + tagged.value = raw; + return tagged; + } + + Type type() const + { + switch (value & 3) { + case 0: + return Type::Smi; + case 1: + return Type::StrongPointer; + case 3: + return Type::WeakPointer; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + + template T* getPtr() const + { + if (type() == Type::Smi) { + return nullptr; + } + return reinterpret_cast(value & ~3ull); + } + + bool getSmi(int32_t& smi) const + { + if (type() != Type::Smi) { + return false; + } + smi = static_cast(value >> 32); + return true; + } + + int32_t getSmiUnchecked() const + { + ASSERT(type() == Type::Smi); + return static_cast(value >> 32); + } + + JSC::JSValue getJSValue() const + { + int32_t smi; + if (getSmi(smi)) { + return JSC::jsNumber(smi); + } + return getPtr(); + } +}; + +} diff --git a/src/bun.js/bindings/v8/V8Template.cpp b/src/bun.js/bindings/v8/V8Template.cpp new file mode 100644 index 0000000000000..5993d74d41098 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Template.cpp @@ -0,0 +1,11 @@ +#include "V8Template.h" + +namespace v8 { + +JSC::EncodedJSValue Template::DummyCallback(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +{ + ASSERT_NOT_REACHED(); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +} diff --git a/src/bun.js/bindings/v8/V8Template.h b/src/bun.js/bindings/v8/V8Template.h new file mode 100644 index 0000000000000..d6cbdbc66f20c --- /dev/null +++ b/src/bun.js/bindings/v8/V8Template.h @@ -0,0 +1,13 @@ +#pragma once + +#include "V8Data.h" + +namespace v8 { + +// matches V8 class hierarchy +class Template : public Data { +public: + static JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue DummyCallback(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); +}; + +} diff --git a/src/bun.js/bindings/v8/V8Value.cpp b/src/bun.js/bindings/v8/V8Value.cpp new file mode 100644 index 0000000000000..f0dcdf0d7c946 --- /dev/null +++ b/src/bun.js/bindings/v8/V8Value.cpp @@ -0,0 +1,76 @@ +#include "V8Value.h" +#include "V8Isolate.h" + +namespace v8 { + +bool Value::IsBoolean() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isBoolean(); +} + +bool Value::IsObject() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isObject(); +} + +bool Value::IsNumber() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isNumber(); +} + +bool Value::IsUint32() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isUInt32(); +} + +bool Value::IsUndefined() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isUndefined(); +} + +bool Value::IsNull() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isNull(); +} + +bool Value::IsNullOrUndefined() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isUndefinedOrNull(); +} + +bool Value::IsTrue() const +{ + return FullIsTrue(); +} + +bool Value::IsFalse() const +{ + return FullIsFalse(); +} + +bool Value::IsString() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isString(); +} + +Maybe Value::Uint32Value(Local context) const +{ + auto js_value = localToJSValue(context->globalObject()->V8GlobalInternals()); + uint32_t value; + if (js_value.getUInt32(value)) { + return Just(value); + } + return Nothing(); +} + +bool Value::FullIsTrue() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isTrue(); +} + +bool Value::FullIsFalse() const +{ + return localToJSValue(Isolate::GetCurrent()->globalInternals()).isFalse(); +} + +} diff --git a/src/bun.js/bindings/v8/V8Value.h b/src/bun.js/bindings/v8/V8Value.h new file mode 100644 index 0000000000000..fe3b7c9f7e6ac --- /dev/null +++ b/src/bun.js/bindings/v8/V8Value.h @@ -0,0 +1,32 @@ +#pragma once + +#include "V8Data.h" +#include "V8Maybe.h" +#include "V8Local.h" +#include "V8Context.h" + +namespace v8 { + +class Value : public Data { +public: + BUN_EXPORT bool IsBoolean() const; + BUN_EXPORT bool IsObject() const; + BUN_EXPORT bool IsNumber() const; + BUN_EXPORT bool IsUint32() const; + BUN_EXPORT Maybe Uint32Value(Local context) const; + + // usually inlined: + BUN_EXPORT bool IsUndefined() const; + BUN_EXPORT bool IsNull() const; + BUN_EXPORT bool IsNullOrUndefined() const; + BUN_EXPORT bool IsTrue() const; + BUN_EXPORT bool IsFalse() const; + BUN_EXPORT bool IsString() const; + +private: + // non-inlined versions of these + BUN_EXPORT bool FullIsTrue() const; + BUN_EXPORT bool FullIsFalse() const; +}; + +} diff --git a/src/bun.js/bindings/v8/node.cpp b/src/bun.js/bindings/v8/node.cpp new file mode 100644 index 0000000000000..f8a5d23ae47f2 --- /dev/null +++ b/src/bun.js/bindings/v8/node.cpp @@ -0,0 +1,110 @@ +#include "node.h" +#include "V8HandleScope.h" + +#include "JavaScriptCore/ObjectConstructor.h" +#include "CommonJSModuleRecord.h" +#include + +using v8::Context; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::Value; + +using JSC::JSObject; +using JSC::jsUndefined; +using JSC::JSValue; + +namespace node { + +void AddEnvironmentCleanupHook(v8::Isolate* isolate, + void (*fun)(void* arg), + void* arg) +{ + // TODO +} + +void RemoveEnvironmentCleanupHook(v8::Isolate* isolate, + void (*fun)(void* arg), + void* arg) +{ + // TODO +} + +void node_module_register(void* opaque_mod) +{ + // TODO unify this with napi_module_register + auto* globalObject = defaultGlobalObject(); + auto& vm = globalObject->vm(); + auto* mod = reinterpret_cast(opaque_mod); + // Error: The module '/Users/ben/code/bun/test/v8/v8-module/build/Release/v8tests.node' + // was compiled against a different Node.js version using + // NODE_MODULE_VERSION 127. This version of Node.js requires + // NODE_MODULE_VERSION 108. Please try re-compiling or re-installing + // the module (for instance, using `npm rebuild` or `npm install`). + + if (mod->nm_version != REPORTED_NODEJS_ABI_VERSION) { + } + + auto keyStr = WTF::String::fromUTF8(mod->nm_modname); + globalObject->napiModuleRegisterCallCount++; + JSValue pendingNapiModule = globalObject->m_pendingNapiModuleAndExports[0].get(); + JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject() + : nullptr; + + auto scope = DECLARE_THROW_SCOPE(vm); + JSC::Strong strongExportsObject; + + if (mod->nm_version != REPORTED_NODEJS_ABI_VERSION) { + auto* error = JSC::createError(globalObject, + WTF::makeString("The module '"_s, + keyStr, + "' was compiled against a different Node.js ABI version using NODE_MODULE_VERSION "_s, + mod->nm_version, + ". This version of Bun requires NODE_MODULE_VERSION "_s, + REPORTED_NODEJS_ABI_VERSION, + ". Please try re-compiling or re-installing the module."_s)); + globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, error); + return; + } + + if (!object) { + auto* exportsObject = JSC::constructEmptyObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + object = Bun::JSCommonJSModule::create(globalObject, keyStr, exportsObject, false, jsUndefined()); + strongExportsObject = { vm, exportsObject }; + } else { + JSValue exportsObject = object->getIfPropertyExists(globalObject, WebCore::builtinNames(vm).exportsPublicName()); + RETURN_IF_EXCEPTION(scope, void()); + + if (exportsObject && exportsObject.isObject()) { + strongExportsObject = { vm, exportsObject.getObject() }; + } + } + + JSC::Strong strongObject = { vm, object }; + + HandleScope hs(Isolate::fromGlobalObject(globalObject)); + + // exports, module + Local exports = hs.createLocal(vm, *strongExportsObject); + Local module = hs.createLocal(vm, object); + Local context = Isolate::fromGlobalObject(globalObject)->GetCurrentContext(); + if (mod->nm_context_register_func) { + mod->nm_context_register_func(exports, module, context, mod->nm_priv); + } else if (mod->nm_register_func) { + mod->nm_register_func(exports, module, mod->nm_priv); + } else { + auto* error = JSC::createError(globalObject, WTF::makeString("The module '"_s, keyStr, "' has no declared entry point."_s)); + globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, error); + return; + } + + RETURN_IF_EXCEPTION(scope, void()); + + globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, object); +} + +} diff --git a/src/bun.js/bindings/v8/node.h b/src/bun.js/bindings/v8/node.h new file mode 100644 index 0000000000000..d1b791f7ea3d0 --- /dev/null +++ b/src/bun.js/bindings/v8/node.h @@ -0,0 +1,44 @@ +#pragma once + +#include "v8.h" +#include "V8Local.h" +#include "V8Isolate.h" +#include "V8Object.h" +#include "V8Value.h" + +namespace node { + +BUN_EXPORT void AddEnvironmentCleanupHook(v8::Isolate* isolate, + void (*fun)(void* arg), + void* arg); + +BUN_EXPORT void RemoveEnvironmentCleanupHook(v8::Isolate* isolate, + void (*fun)(void* arg), + void* arg); + +typedef void (*addon_register_func)( + v8::Local exports, + v8::Local module, + void* priv); + +typedef void (*addon_context_register_func)( + v8::Local exports, + v8::Local module, + v8::Local context, + void* priv); + +struct node_module { + int nm_version; + unsigned int nm_flags; + void* nm_dso_handle; + const char* nm_filename; + node::addon_register_func nm_register_func; + node::addon_context_register_func nm_context_register_func; + const char* nm_modname; + void* nm_priv; + struct node_module* nm_link; +}; + +extern "C" BUN_EXPORT void node_module_register(void* mod); + +} diff --git a/src/bun.js/bindings/v8/v8.h b/src/bun.js/bindings/v8/v8.h new file mode 100644 index 0000000000000..fd8d672e04f8e --- /dev/null +++ b/src/bun.js/bindings/v8/v8.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ZigGlobalObject.h" + +#define V8_UNIMPLEMENTED() \ + do { \ + const auto str = WTF::makeString( \ + "You're using a module which calls a V8 function \""_s, \ + WTF::ASCIILiteral::fromLiteralUnsafe(__PRETTY_FUNCTION__), \ + "\" that Bun does not yet implement. Track progress at https://github.com/oven-sh/bun/issues/4290."_s); \ + auto utf8 = str.utf8(); \ + Bun__panic(utf8.data(), utf8.length()); \ + } while (0) + +namespace v8 { +} diff --git a/src/bun.js/bindings/v8/v8_api_internal.cpp b/src/bun.js/bindings/v8/v8_api_internal.cpp new file mode 100644 index 0000000000000..f610051696143 --- /dev/null +++ b/src/bun.js/bindings/v8/v8_api_internal.cpp @@ -0,0 +1,26 @@ +#include "v8_api_internal.h" +#include "V8Isolate.h" +#include "V8HandleScopeBuffer.h" + +namespace v8 { +namespace api_internal { + +void ToLocalEmpty() +{ + BUN_PANIC("Attempt to unwrap an empty v8::MaybeLocal"); +} + +uintptr_t* GlobalizeReference(v8::internal::Isolate* isolate, uintptr_t address) +{ + auto* globalHandles = reinterpret_cast(isolate)->globalInternals()->globalHandles(); + return &globalHandles->createHandleFromExistingHandle(TaggedPointer::fromRaw(address))->value; +} + +void DisposeGlobal(uintptr_t* location) +{ + // TODO free up a slot in the handle scope + (void)location; +} + +} +} diff --git a/src/bun.js/bindings/v8/v8_api_internal.h b/src/bun.js/bindings/v8/v8_api_internal.h new file mode 100644 index 0000000000000..ec3fa7347b5f7 --- /dev/null +++ b/src/bun.js/bindings/v8/v8_api_internal.h @@ -0,0 +1,14 @@ +#pragma once + +#include "v8.h" +#include "v8_internal.h" + +namespace v8 { +namespace api_internal { + +BUN_EXPORT void ToLocalEmpty(); +BUN_EXPORT uintptr_t* GlobalizeReference(v8::internal::Isolate* isolate, uintptr_t address); +BUN_EXPORT void DisposeGlobal(uintptr_t* location); + +} +} diff --git a/src/bun.js/bindings/v8/v8_internal.cpp b/src/bun.js/bindings/v8/v8_internal.cpp new file mode 100644 index 0000000000000..4f4ce03da4565 --- /dev/null +++ b/src/bun.js/bindings/v8/v8_internal.cpp @@ -0,0 +1,13 @@ +#include "v8_internal.h" + +namespace v8 { +namespace internal { + +Isolate* IsolateFromNeverReadOnlySpaceObject(uintptr_t obj) +{ + V8_UNIMPLEMENTED(); + return nullptr; +} + +} +} diff --git a/src/bun.js/bindings/v8/v8_internal.h b/src/bun.js/bindings/v8/v8_internal.h new file mode 100644 index 0000000000000..da9be2b59bded --- /dev/null +++ b/src/bun.js/bindings/v8/v8_internal.h @@ -0,0 +1,13 @@ +#pragma once + +#include "v8.h" + +namespace v8 { +namespace internal { + +class Isolate {}; + +BUN_EXPORT Isolate* IsolateFromNeverReadOnlySpaceObject(uintptr_t obj); + +} +} diff --git a/src/bun.js/bindings/webcore/AbortController.cpp b/src/bun.js/bindings/webcore/AbortController.cpp index 46dffc7c196bc..8fa6b496abca3 100644 --- a/src/bun.js/bindings/webcore/AbortController.cpp +++ b/src/bun.js/bindings/webcore/AbortController.cpp @@ -57,10 +57,11 @@ AbortSignal& AbortController::signal() void AbortController::abort(JSDOMGlobalObject& globalObject, JSC::JSValue reason) { ASSERT(reason); - if (reason.isUndefined()) - reason = toJS(&globalObject, &globalObject, DOMException::create(ExceptionCode::AbortError)); - - protectedSignal()->signalAbort(reason); + if (reason.isUndefined()) { + protectedSignal()->signalAbort(&globalObject, CommonAbortReason::UserAbort); + } else { + protectedSignal()->signalAbort(reason); + } } WebCoreOpaqueRoot AbortController::opaqueRoot() diff --git a/src/bun.js/bindings/webcore/AbortSignal.cpp b/src/bun.js/bindings/webcore/AbortSignal.cpp index 9b1c28930883c..d26816c01e866 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.cpp +++ b/src/bun.js/bindings/webcore/AbortSignal.cpp @@ -32,15 +32,18 @@ #include "Event.h" #include "EventNames.h" #include "JSDOMException.h" +#include "JavaScriptCore/JSCJSValue.h" #include "ScriptExecutionContext.h" #include "WebCoreOpaqueRoot.h" +#include "wtf/DebugHeap.h" +#include "wtf/FastMalloc.h" #include #include #include namespace WebCore { -WTF_MAKE_ISO_ALLOCATED_IMPL(AbortSignal); +DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(AbortSignal); Ref AbortSignal::create(ScriptExecutionContext* context) { @@ -110,6 +113,20 @@ AbortSignal::AbortSignal(ScriptExecutionContext* context, Aborted aborted, JSC:: AbortSignal::~AbortSignal() = default; +JSValue AbortSignal::jsReason(JSC::JSGlobalObject& globalObject) +{ + JSValue existingValue = m_reason.getValue(jsUndefined()); + if (existingValue.isUndefined()) { + if (m_commonReason != CommonAbortReason::None) { + existingValue = toJS(&globalObject, m_commonReason); + m_commonReason = CommonAbortReason::None; + m_reason.setWeakly(existingValue); + } + } + + return existingValue; +} + void AbortSignal::addSourceSignal(AbortSignal& signal) { if (signal.isDependent()) { @@ -162,6 +179,16 @@ void AbortSignal::signalAbort(JSC::JSValue reason) dependentSignal->signalAbort(reason); } +void AbortSignal::signalAbort(JSC::JSGlobalObject* globalObject, CommonAbortReason reason) +{ + // 1. If signal's aborted flag is set, then return. + if (m_aborted) + return; + + m_commonReason = reason; + signalAbort(toJS(globalObject, reason)); +} + void AbortSignal::cleanNativeBindings(void* ref) { auto callbacks = std::exchange(m_native_callbacks, {}); @@ -181,7 +208,7 @@ void AbortSignal::signalFollow(AbortSignal& signal) return; if (signal.aborted()) { - signalAbort(signal.reason().getValue()); + signalAbort(signal.jsReason(*scriptExecutionContext()->jsGlobalObject())); return; } @@ -201,7 +228,8 @@ void AbortSignal::eventListenersDidChange() uint32_t AbortSignal::addAbortAlgorithmToSignal(AbortSignal& signal, Ref&& algorithm) { if (signal.aborted()) { - algorithm->handleEvent(signal.m_reason.getValue()); + // TODO: Null check. + algorithm->handleEvent(signal.jsReason(*signal.scriptExecutionContext()->jsGlobalObject())); return 0; } return signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue value) mutable { diff --git a/src/bun.js/bindings/webcore/AbortSignal.h b/src/bun.js/bindings/webcore/AbortSignal.h index e5fe1369cba4a..788d1c6c94dbe 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.h +++ b/src/bun.js/bindings/webcore/AbortSignal.h @@ -30,6 +30,10 @@ #include "ContextDestructionObserver.h" #include "EventTarget.h" #include "JSValueInWrappedObject.h" +#include "JavaScriptCore/JSGlobalObject.h" +#include "ZigGlobalObject.h" +#include "wtf/DebugHeap.h" +#include "wtf/FastMalloc.h" #include #include #include @@ -42,8 +46,19 @@ class AbortAlgorithm; class ScriptExecutionContext; class WebCoreOpaqueRoot; +DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(AbortSignal); + +enum class CommonAbortReason : uint8_t { + None, + Timeout, + UserAbort, + ConnectionClosed, +}; + +JSC::JSValue toJS(JSC::JSGlobalObject*, CommonAbortReason); + class AbortSignal final : public RefCounted, public EventTargetWithInlineData, private ContextDestructionObserver { - WTF_MAKE_ISO_ALLOCATED_EXPORT(AbortSignal, WEBCORE_EXPORT); + WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(AbortSignal); public: static Ref create(ScriptExecutionContext*); @@ -57,11 +72,14 @@ class AbortSignal final : public RefCounted, public EventTargetWith static uint32_t addAbortAlgorithmToSignal(AbortSignal&, Ref&&); static void removeAbortAlgorithmFromSignal(AbortSignal&, uint32_t algorithmIdentifier); + void signalAbort(JSC::JSGlobalObject* globalObject, CommonAbortReason reason); void signalAbort(JSC::JSValue reason); void signalFollow(AbortSignal&); bool aborted() const { return m_aborted; } const JSValueInWrappedObject& reason() const { return m_reason; } + JSValue jsReason(JSC::JSGlobalObject& globalObject); + CommonAbortReason commonReason() const { return m_commonReason; } void cleanNativeBindings(void* ref); void addNativeCallback(NativeCallbackTuple callback) { m_native_callbacks.append(callback); } @@ -84,9 +102,16 @@ class AbortSignal final : public RefCounted, public EventTargetWith const AbortSignalSet& sourceSignals() const { return m_sourceSignals; } AbortSignalSet& sourceSignals() { return m_sourceSignals; } + // https://github.com/oven-sh/bun/issues/4517 + void incrementPendingActivityCount() { ++pendingActivityCount; } + void decrementPendingActivityCount() { --pendingActivityCount; } + bool hasPendingActivity() const { return pendingActivityCount > 0; } + private: - enum class Aborted : bool { No, - Yes }; + enum class Aborted : bool { + No, + Yes + }; explicit AbortSignal(ScriptExecutionContext*, Aborted = Aborted::No, JSC::JSValue reason = JSC::jsUndefined()); void setHasActiveTimeoutTimer(bool hasActiveTimeoutTimer) { m_hasActiveTimeoutTimer = hasActiveTimeoutTimer; } @@ -108,12 +133,14 @@ class AbortSignal final : public RefCounted, public EventTargetWith AbortSignalSet m_sourceSignals; AbortSignalSet m_dependentSignals; JSValueInWrappedObject m_reason; + CommonAbortReason m_commonReason { CommonAbortReason::None }; Vector m_native_callbacks; + std::atomic pendingActivityCount { 0 }; uint32_t m_algorithmIdentifier { 0 }; - bool m_aborted { false }; - bool m_hasActiveTimeoutTimer { false }; - bool m_hasAbortEventListener { false }; - bool m_isDependent { false }; + bool m_aborted : 1 = false; + bool m_hasActiveTimeoutTimer : 1 = false; + bool m_hasAbortEventListener : 1 = false; + bool m_isDependent : 1 = false; }; WebCoreOpaqueRoot root(AbortSignal*); diff --git a/src/bun.js/bindings/webcore/BroadcastChannel.cpp b/src/bun.js/bindings/webcore/BroadcastChannel.cpp index 69b66ac42fe6d..dbd4df5ff41ae 100644 --- a/src/bun.js/bindings/webcore/BroadcastChannel.cpp +++ b/src/bun.js/bindings/webcore/BroadcastChannel.cpp @@ -88,6 +88,8 @@ class BroadcastChannel::MainThreadBridge : public ThreadSafeRefCounted BufferSource::decode(Decoder& decoder) const uint8_t* data = decoder.decodeFixedLengthReference(dataSize, alignof(uint8_t)); if (!data) return std::nullopt; - return BufferSource(JSC::ArrayBuffer::tryCreate(static_cast(data), dataSize.value())); + return BufferSource(JSC::ArrayBuffer::tryCreate({ static_cast(data), dataSize.value() })); } inline BufferSource toBufferSource(const uint8_t* data, size_t length) { - return BufferSource(JSC::ArrayBuffer::tryCreate(data, length)); + return BufferSource(JSC::ArrayBuffer::tryCreate({ data, length })); } } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/CallbackResult.h b/src/bun.js/bindings/webcore/CallbackResult.h index 878fe4221940c..abedf5a2ff20b 100644 --- a/src/bun.js/bindings/webcore/CallbackResult.h +++ b/src/bun.js/bindings/webcore/CallbackResult.h @@ -58,7 +58,6 @@ template<> class CallbackResult { CallbackResultType m_type = CallbackResultType::Success; }; - template inline CallbackResult::CallbackResult(CallbackResultType type) : m_value(makeUnexpected(type)) { @@ -80,7 +79,6 @@ template inline auto CallbackResult::releaseRet return WTFMove(m_value.value()); } - // Void specialization inline CallbackResult::CallbackResult(CallbackResultType type) diff --git a/src/bun.js/bindings/webcore/CommonAtomStrings.cpp b/src/bun.js/bindings/webcore/CommonAtomStrings.cpp index 57cdc3d246213..bf1cdcff08e66 100644 --- a/src/bun.js/bindings/webcore/CommonAtomStrings.cpp +++ b/src/bun.js/bindings/webcore/CommonAtomStrings.cpp @@ -28,12 +28,14 @@ namespace WebCore { +// clang-format off #define DEFINE_COMMON_ATOM(atomName, atomValue) \ MainThreadLazyNeverDestroyed atomName ## AtomData; #define INITIALIZE_COMMON_ATOM(atomName, atomValue) \ atomName ## AtomData.constructWithoutAccessCheck(atomValue ## _s); WEBCORE_COMMON_ATOM_STRINGS_FOR_EACH_KEYWORD(DEFINE_COMMON_ATOM) +// clang-format on void initializeCommonAtomStrings() { diff --git a/src/bun.js/bindings/webcore/CommonAtomStrings.h b/src/bun.js/bindings/webcore/CommonAtomStrings.h index 0385f82224e04..8581023cd5e45 100644 --- a/src/bun.js/bindings/webcore/CommonAtomStrings.h +++ b/src/bun.js/bindings/webcore/CommonAtomStrings.h @@ -30,6 +30,7 @@ namespace WebCore { +// clang-format off #define WEBCORE_COMMON_ATOM_STRINGS_FOR_EACH_KEYWORD(macro) \ macro(alternative, "alternative") \ macro(auto, "auto") \ @@ -66,6 +67,7 @@ namespace WebCore { WEBCORE_COMMON_ATOM_STRINGS_FOR_EACH_KEYWORD(DECLARE_COMMON_ATOM) #undef DECLARE_COMMON_ATOM +// clang-format on WEBCORE_EXPORT void initializeCommonAtomStrings(); diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index d4dc018135fe2..79da6f30f0c2c 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -44,10 +44,12 @@ class DOMClientIsoSubspaces { std::unique_ptr m_clientSubspaceForMockWithImplementationCleanupData; std::unique_ptr m_clientSubspaceForProcessObject; std::unique_ptr m_clientSubspaceForInternalModuleRegistry; + std::unique_ptr m_clientSubspaceForErrorCodeCache; std::unique_ptr m_clientSubspaceForBunInspectorConnection; std::unique_ptr m_clientSubspaceForJSNextTickQueue; std::unique_ptr m_clientSubspaceForNAPIFunction; std::unique_ptr m_clientSubspaceForTTYWrapObject; + std::unique_ptr m_clientSubspaceForNapiHandleScopeImpl; #include "ZigGeneratedClasses+DOMClientIsoSubspaces.h" /* --- bun --- */ @@ -475,10 +477,10 @@ class DOMClientIsoSubspaces { // std::unique_ptr m_clientSubspaceForStaticRange; // std::unique_ptr m_clientSubspaceForText; // std::unique_ptr m_clientSubspaceForTextDecoder; - // std::unique_ptr m_clientSubspaceForTextDecoderStream; + std::unique_ptr m_clientSubspaceForTextDecoderStream; // std::unique_ptr m_clientSubspaceForTextDecoderStreamDecoder; std::unique_ptr m_clientSubspaceForTextEncoder; - // std::unique_ptr m_clientSubspaceForTextEncoderStream; + std::unique_ptr m_clientSubspaceForTextEncoderStream; // std::unique_ptr m_clientSubspaceForTextEncoderStreamEncoder; // std::unique_ptr m_clientSubspaceForTextEvent; // std::unique_ptr m_clientSubspaceForTransitionEvent; @@ -898,6 +900,8 @@ class DOMClientIsoSubspaces { // std::unique_ptr m_clientSubspaceForXPathResult; // std::unique_ptr m_clientSubspaceForXSLTProcessor; + std::unique_ptr m_clientSubspaceForKitGlobalScope; + std::unique_ptr m_clientSubspaceForAbortController; std::unique_ptr m_clientSubspaceForAbortSignal; std::unique_ptr m_clientSubspaceForErrorEvent; @@ -905,5 +909,12 @@ class DOMClientIsoSubspaces { std::unique_ptr m_clientSubspaceForEventListener; std::unique_ptr m_clientSubspaceForEventTarget; std::unique_ptr m_clientSubspaceForEventEmitter; + // todo(@190n) move these up or move these elsewhere + std::unique_ptr m_clientSubspaceForObjectTemplate; + std::unique_ptr m_clientSubspaceForInternalFieldObject; + std::unique_ptr m_clientSubspaceForV8GlobalInternals; + std::unique_ptr m_clientSubspaceForHandleScopeBuffer; + std::unique_ptr m_clientSubspaceForFunctionTemplate; + std::unique_ptr m_clientSubspaceForV8Function; }; } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index f2a25be521682..27bd00eab7ad2 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -44,10 +44,12 @@ class DOMIsoSubspaces { std::unique_ptr m_subspaceForMockWithImplementationCleanupData; std::unique_ptr m_subspaceForProcessObject; std::unique_ptr m_subspaceForInternalModuleRegistry; + std::unique_ptr m_subspaceForErrorCodeCache; std::unique_ptr m_subspaceForBunInspectorConnection; std::unique_ptr m_subspaceForJSNextTickQueue; std::unique_ptr m_subspaceForNAPIFunction; std::unique_ptr m_subspaceForTTYWrapObject; + std::unique_ptr m_subspaceForNapiHandleScopeImpl; #include "ZigGeneratedClasses+DOMIsoSubspaces.h" /*-- BUN --*/ @@ -468,10 +470,10 @@ class DOMIsoSubspaces { // std::unique_ptr m_subspaceForStaticRange; // std::unique_ptr m_subspaceForText; // std::unique_ptr m_subspaceForTextDecoder; - // std::unique_ptr m_subspaceForTextDecoderStream; + std::unique_ptr m_subspaceForTextDecoderStream; // std::unique_ptr m_subspaceForTextDecoderStreamDecoder; std::unique_ptr m_subspaceForTextEncoder; - // std::unique_ptr m_subspaceForTextEncoderStream; + std::unique_ptr m_subspaceForTextEncoderStream; // std::unique_ptr m_subspaceForTextEncoderStreamEncoder; // std::unique_ptr m_subspaceForTextEvent; // std::unique_ptr m_subspaceForTransitionEvent; @@ -890,6 +892,8 @@ class DOMIsoSubspaces { // std::unique_ptr m_subspaceForXPathNSResolver; // std::unique_ptr m_subspaceForXPathResult; // std::unique_ptr m_subspaceForXSLTProcessor; + + std::unique_ptr m_subspaceForKitGlobalScope; std::unique_ptr m_subspaceForAbortController; std::unique_ptr m_subspaceForAbortSignal; @@ -909,6 +913,13 @@ class DOMIsoSubspaces { // std::unique_ptr m_subspaceForDOMFormData; // std::unique_ptr m_subspaceForDOMFormDataIterator; std::unique_ptr m_subspaceForDOMURL; + // todo(@190n) move up + std::unique_ptr m_subspaceForObjectTemplate; + std::unique_ptr m_subspaceForInternalFieldObject; + std::unique_ptr m_subspaceForV8GlobalInternals; + std::unique_ptr m_subspaceForHandleScopeBuffer; + std::unique_ptr m_subspaceForFunctionTemplate; + std::unique_ptr m_subspaceForV8Function; }; } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/DOMJITIDLConvert.h b/src/bun.js/bindings/webcore/DOMJITIDLConvert.h index 81395e125bd00..fbc95f7ebbfc9 100644 --- a/src/bun.js/bindings/webcore/DOMJITIDLConvert.h +++ b/src/bun.js/bindings/webcore/DOMJITIDLConvert.h @@ -27,7 +27,8 @@ #include "IDLTypes.h" -namespace WebCore { namespace DOMJIT { +namespace WebCore { +namespace DOMJIT { template struct DirectConverter; @@ -56,4 +57,5 @@ struct DirectConverter> { } }; -} } +} +} diff --git a/src/bun.js/bindings/webcore/DOMJITIDLTypeFilter.h b/src/bun.js/bindings/webcore/DOMJITIDLTypeFilter.h index 7728db7d29cbf..b7130336e6de3 100644 --- a/src/bun.js/bindings/webcore/DOMJITIDLTypeFilter.h +++ b/src/bun.js/bindings/webcore/DOMJITIDLTypeFilter.h @@ -28,53 +28,116 @@ #include "IDLTypes.h" #include -namespace WebCore { namespace DOMJIT { +namespace WebCore { +namespace DOMJIT { template struct IDLArgumentTypeFilter; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBoolean; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLArgumentTypeFilter> { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLArgumentTypeFilter> { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLArgumentTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecUint8Array; }; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBoolean; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLArgumentTypeFilter> { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLArgumentTypeFilter> { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLArgumentTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecUint8Array; +}; template struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecFullTop; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecHeapTop; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBoolean; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLResultTypeFilter> { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLResultTypeFilter> { static const constexpr JSC::SpeculatedType value = JSC::SpecString; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecUint8Array; }; -template<> struct IDLResultTypeFilter { static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeTop; }; - +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecHeapTop; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBoolean; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecInt32Only; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeNumber; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLResultTypeFilter> { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLResultTypeFilter> { + static const constexpr JSC::SpeculatedType value = JSC::SpecString; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecUint8Array; +}; +template<> struct IDLResultTypeFilter { + static const constexpr JSC::SpeculatedType value = JSC::SpecBytecodeTop; +}; template struct IDLResultTypeFilter> { static const constexpr JSC::SpeculatedType value = JSC::SpecOther | IDLResultTypeFilter::value; }; -} } +} +} diff --git a/src/bun.js/bindings/webcore/DOMPromiseProxy.h b/src/bun.js/bindings/webcore/DOMPromiseProxy.h index 2b8706e76e47d..70d42122b7377 100644 --- a/src/bun.js/bindings/webcore/DOMPromiseProxy.h +++ b/src/bun.js/bindings/webcore/DOMPromiseProxy.h @@ -36,6 +36,7 @@ namespace WebCore { template class DOMPromiseProxy { WTF_MAKE_FAST_ALLOCATED; + public: using Value = typename IDLType::StorageType; @@ -51,7 +52,7 @@ class DOMPromiseProxy { void resolve(typename IDLType::StorageType); void resolveWithNewlyCreated(typename IDLType::StorageType); void reject(Exception, RejectAsHandled = RejectAsHandled::No); - + private: JSC::JSValue resolvePromise(JSC::JSGlobalObject&, JSDOMGlobalObject&, const Function&); @@ -62,6 +63,7 @@ class DOMPromiseProxy { template<> class DOMPromiseProxy { WTF_MAKE_FAST_ALLOCATED; + public: DOMPromiseProxy() = default; ~DOMPromiseProxy() = default; @@ -87,10 +89,11 @@ class DOMPromiseProxy { template class DOMPromiseProxyWithResolveCallback { WTF_MAKE_FAST_ALLOCATED; + public: using ResolveCallback = Function; - template + template DOMPromiseProxyWithResolveCallback(Class&, typename IDLType::ParameterType (BaseClass::*)()); DOMPromiseProxyWithResolveCallback(ResolveCallback&&); ~DOMPromiseProxyWithResolveCallback() = default; @@ -104,7 +107,7 @@ class DOMPromiseProxyWithResolveCallback { void resolve(typename IDLType::ParameterType); void resolveWithNewlyCreated(typename IDLType::ParameterType); void reject(Exception, RejectAsHandled = RejectAsHandled::No); - + private: ResolveCallback m_resolveCallback; std::optional> m_valueOrException; @@ -208,7 +211,6 @@ inline void DOMPromiseProxy::reject(Exception exception, RejectAsHandle deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); } - // MARK: - DOMPromiseProxy specialization inline JSC::JSValue DOMPromiseProxy::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) @@ -250,7 +252,7 @@ inline bool DOMPromiseProxy::isFulfilled() const inline void DOMPromiseProxy::resolve() { ASSERT(!m_valueOrException); - m_valueOrException = ExceptionOr { }; + m_valueOrException = ExceptionOr {}; for (auto& deferredPromise : m_deferredPromises) deferredPromise->resolve(); } @@ -266,7 +268,7 @@ inline void DOMPromiseProxy::reject(Exception exception, RejectAsH // MARK: - DOMPromiseProxyWithResolveCallback implementation template -template +template inline DOMPromiseProxyWithResolveCallback::DOMPromiseProxyWithResolveCallback(Class& object, typename IDLType::ParameterType (BaseClass::*function)()) : m_resolveCallback(std::bind(function, &object)) { @@ -322,7 +324,7 @@ inline void DOMPromiseProxyWithResolveCallback::resolve(typename IDLTyp { ASSERT(!m_valueOrException); - m_valueOrException = ExceptionOr { }; + m_valueOrException = ExceptionOr {}; for (auto& deferredPromise : m_deferredPromises) deferredPromise->template resolve(value); } @@ -332,7 +334,7 @@ inline void DOMPromiseProxyWithResolveCallback::resolveWithNewlyCreated { ASSERT(!m_valueOrException); - m_valueOrException = ExceptionOr { }; + m_valueOrException = ExceptionOr {}; for (auto& deferredPromise : m_deferredPromises) deferredPromise->template resolveWithNewlyCreated(value); } diff --git a/src/bun.js/bindings/webcore/ErrorCallback.cpp b/src/bun.js/bindings/webcore/ErrorCallback.cpp index 5b81eea06586a..5c9aab344c7e1 100644 --- a/src/bun.js/bindings/webcore/ErrorCallback.cpp +++ b/src/bun.js/bindings/webcore/ErrorCallback.cpp @@ -33,7 +33,7 @@ namespace WebCore { void ErrorCallback::scheduleCallback(ScriptExecutionContext& context, Ref&& exception) { - context.postTask([protectedThis = Ref { *this }, exception = WTFMove(exception)] (ScriptExecutionContext&) { + context.postTask([protectedThis = Ref { *this }, exception = WTFMove(exception)](ScriptExecutionContext&) { protectedThis->handleEvent(exception); }); } diff --git a/src/bun.js/bindings/webcore/Event.cpp b/src/bun.js/bindings/webcore/Event.cpp index f488f010662d9..e68c829d92473 100644 --- a/src/bun.js/bindings/webcore/Event.cpp +++ b/src/bun.js/bindings/webcore/Event.cpp @@ -188,7 +188,7 @@ void Event::resetAfterDispatch() String Event::debugDescription() const { - return makeString(type(), " phase ", eventPhase(), bubbles() ? " bubbles " : " ", cancelable() ? "cancelable " : " ", "0x"_s, hex(reinterpret_cast(this), Lowercase)); + return makeString(type(), " phase "_s, eventPhase(), bubbles() ? " bubbles "_s : " "_s, cancelable() ? "cancelable "_s : " "_s, "0x"_s, hex(reinterpret_cast(this), Lowercase)); } TextStream& operator<<(TextStream& ts, const Event& event) diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp index f5822895590f6..021edf1fcaabe 100644 --- a/src/bun.js/bindings/webcore/EventEmitter.cpp +++ b/src/bun.js/bindings/webcore/EventEmitter.cpp @@ -249,7 +249,7 @@ void EventEmitter::innerInvokeEventListeners(const Identifier& eventType, Simple auto* exception = exceptionPtr.get(); if (UNLIKELY(exception)) { - auto errorIdentifier = JSC::Identifier::fromString(vm, eventNames().errorEvent); + auto errorIdentifier = vm.propertyNames->error; auto hasErrorListener = this->hasActiveEventListeners(errorIdentifier); if (!hasErrorListener || eventType == errorIdentifier) { // If the event type is error, report the exception to the console. diff --git a/src/bun.js/bindings/webcore/EventListener.h b/src/bun.js/bindings/webcore/EventListener.h index 7ceb38b1e6a32..c164c0f170f16 100644 --- a/src/bun.js/bindings/webcore/EventListener.h +++ b/src/bun.js/bindings/webcore/EventListener.h @@ -53,17 +53,22 @@ class EventListener : public RefCounted, public CanMakeWeakPtr class EventSender { - WTF_MAKE_NONCOPYABLE(EventSender); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(EventSender); + WTF_MAKE_FAST_ALLOCATED; + public: explicit EventSender(const AtomString& eventType); @@ -98,7 +100,7 @@ template void EventSender::dispatchPendingEvents(Page* page) m_dispatchSoonList.checkConsistency(); - m_dispatchingList = std::exchange(m_dispatchSoonList, { }); + m_dispatchingList = std::exchange(m_dispatchSoonList, {}); for (auto& event : m_dispatchingList) { if (auto sender = event.get()) { event = nullptr; diff --git a/src/bun.js/bindings/webcore/EventTarget.cpp b/src/bun.js/bindings/webcore/EventTarget.cpp index 67bfb57cb5dd6..1a859de05f15a 100644 --- a/src/bun.js/bindings/webcore/EventTarget.cpp +++ b/src/bun.js/bindings/webcore/EventTarget.cpp @@ -101,9 +101,9 @@ bool EventTarget::addEventListener(const AtomString& eventType, RefaddAlgorithm([weakThis = WeakPtr { *this }, eventType, listener = WeakPtr { listener }, capture = options.capture](JSC::JSValue value) { + options.signal->addAlgorithm([weakThis = WeakPtr { *this }, eventType, listener = WeakPtr { listener }, capture = options.capture](JSC::JSValue) { if (weakThis && listener) - weakThis->removeEventListener(eventType, *listener, capture); + Ref { *weakThis } -> removeEventListener(eventType, *listener, capture); }); } diff --git a/src/bun.js/bindings/webcore/FetchHeaders.cpp b/src/bun.js/bindings/webcore/FetchHeaders.cpp index 02dbf7d57215c..127f6d29a19d8 100644 --- a/src/bun.js/bindings/webcore/FetchHeaders.cpp +++ b/src/bun.js/bindings/webcore/FetchHeaders.cpp @@ -31,9 +31,12 @@ #include "HTTPHeaderNames.h" #include "HTTPParsers.h" +#include "wtf/DebugHeap.h" namespace WebCore { +DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(FetchHeaders); + // https://fetch.spec.whatwg.org/#concept-headers-remove-privileged-no-cors-request-headers static void removePrivilegedNoCORSRequestHeaders(HTTPHeaderMap& headers) { @@ -44,7 +47,7 @@ static ExceptionOr canWriteHeader(const HTTPHeaderName name, const String& { ASSERT(value.isEmpty() || (!isHTTPSpace(value[0]) && !isHTTPSpace(value[value.length() - 1]))); if (!isValidHTTPHeaderValue((value))) - return Exception { TypeError, makeString("Header '", name, "' has invalid value: '", value, "'") }; + return Exception { TypeError, makeString("Header '"_s, name, "' has invalid value: '"_s, value, "'"_s) }; if (guard == FetchHeaders::Guard::Immutable) return Exception { TypeError, "Headers object's guard is 'immutable'"_s }; return true; @@ -53,10 +56,10 @@ static ExceptionOr canWriteHeader(const HTTPHeaderName name, const String& static ExceptionOr canWriteHeader(const String& name, const String& value, const String& combinedValue, FetchHeaders::Guard guard) { if (!isValidHTTPToken(name)) - return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; + return Exception { TypeError, makeString("Invalid header name: '"_s, name, "'"_s) }; ASSERT(value.isEmpty() || (!isHTTPSpace(value[0]) && !isHTTPSpace(value[value.length() - 1]))); if (!isValidHTTPHeaderValue((value))) - return Exception { TypeError, makeString("Header '", name, "' has invalid value: '", value, "'") }; + return Exception { TypeError, makeString("Header '"_s, name, "' has invalid value: '"_s, value, "'"_s) }; if (guard == FetchHeaders::Guard::Immutable) return Exception { TypeError, "Headers object's guard is 'immutable'"_s }; return true; @@ -68,10 +71,16 @@ static ExceptionOr appendToHeaderMap(const String& name, const String& val String combinedValue = normalizedValue; HTTPHeaderName headerName; if (findHTTPHeaderName(name, headerName)) { + auto index = headers.indexOf(headerName); if (headerName != HTTPHeaderName::SetCookie) { - if (headers.contains(headerName)) { - combinedValue = makeString(headers.get(headerName), ", ", normalizedValue); + if (index.isValid()) { + auto existing = headers.getIndex(index); + if (headerName == HTTPHeaderName::Cookie) { + combinedValue = makeString(existing, "; "_s, normalizedValue); + } else { + combinedValue = makeString(existing, ", "_s, normalizedValue); + } } } @@ -83,22 +92,26 @@ static ExceptionOr appendToHeaderMap(const String& name, const String& val return {}; if (headerName != HTTPHeaderName::SetCookie) { - headers.set(headerName, combinedValue); + if (!headers.setIndex(index, combinedValue)) + headers.set(headerName, combinedValue); } else { headers.add(headerName, normalizedValue); } return {}; } - - if (headers.contains(name)) - combinedValue = makeString(headers.get(name), ", ", normalizedValue); + auto index = headers.indexOf(name); + if (index.isValid()) { + combinedValue = makeString(headers.getIndex(index), ", "_s, normalizedValue); + } auto canWriteResult = canWriteHeader(name, normalizedValue, combinedValue, guard); if (canWriteResult.hasException()) return canWriteResult.releaseException(); if (!canWriteResult.releaseReturnValue()) return {}; - headers.set(name, combinedValue); + + if (!headers.setIndex(index, combinedValue)) + headers.set(name, combinedValue); // if (guard == FetchHeaders::Guard::RequestNoCors) // removePrivilegedNoCORSRequestHeaders(headers); @@ -195,7 +208,7 @@ ExceptionOr FetchHeaders::append(const String& name, const String& value) ExceptionOr FetchHeaders::remove(const String& name) { if (!isValidHTTPToken(name)) - return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; + return Exception { TypeError, makeString("Invalid header name: '"_s, name, "'"_s) }; if (m_guard == FetchHeaders::Guard::Immutable) return Exception { TypeError, "Headers object's guard is 'immutable'"_s }; if (m_guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name)) @@ -222,17 +235,35 @@ size_t FetchHeaders::memoryCost() const ExceptionOr FetchHeaders::get(const String& name) const { if (!isValidHTTPToken(name)) - return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; + return Exception { TypeError, makeString("Invalid header name: '"_s, name, "'"_s) }; return m_headers.get(name); } ExceptionOr FetchHeaders::has(const String& name) const { if (!isValidHTTPToken(name)) - return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; + return Exception { TypeError, makeString("Invalid header name: '"_s, name, '"') }; return m_headers.contains(name); } +ExceptionOr FetchHeaders::set(const HTTPHeaderName name, const String& value) +{ + String normalizedValue = value.trim(isHTTPSpace); + auto canWriteResult = canWriteHeader(name, normalizedValue, normalizedValue, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return {}; + + ++m_updateCounter; + m_headers.set(name, normalizedValue); + + if (m_guard == FetchHeaders::Guard::RequestNoCors) + removePrivilegedNoCORSRequestHeaders(m_headers); + + return {}; +} + ExceptionOr FetchHeaders::set(const String& name, const String& value) { String normalizedValue = value.trim(isHTTPSpace); diff --git a/src/bun.js/bindings/webcore/FetchHeaders.h b/src/bun.js/bindings/webcore/FetchHeaders.h index c4f607c927636..8e3713539b578 100644 --- a/src/bun.js/bindings/webcore/FetchHeaders.h +++ b/src/bun.js/bindings/webcore/FetchHeaders.h @@ -36,7 +36,13 @@ namespace WebCore { +class ScriptExecutionContext; + +DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(FetchHeaders); + class FetchHeaders : public RefCounted { + WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FetchHeaders); + public: enum class Guard { None, @@ -57,6 +63,7 @@ class FetchHeaders : public RefCounted { ExceptionOr get(const String&) const; ExceptionOr has(const String&) const; ExceptionOr set(const String& name, const String& value); + ExceptionOr set(const HTTPHeaderName name, const String& value); ExceptionOr fill(const Init&); ExceptionOr fill(const FetchHeaders&); @@ -100,6 +107,11 @@ class FetchHeaders : public RefCounted { return Iterator(*this, lowerCaseKeys); } + Iterator createIterator(const ScriptExecutionContext* context) + { + return Iterator(*this, true); + } + void setInternalHeaders(HTTPHeaderMap&& headers) { m_headers = WTFMove(headers); } const HTTPHeaderMap& internalHeaders() const { return m_headers; } diff --git a/src/bun.js/bindings/webcore/HTTPHeaderMap.cpp b/src/bun.js/bindings/webcore/HTTPHeaderMap.cpp index c1a44ecb23674..d2765a7b8f612 100644 --- a/src/bun.js/bindings/webcore/HTTPHeaderMap.cpp +++ b/src/bun.js/bindings/webcore/HTTPHeaderMap.cpp @@ -174,7 +174,7 @@ void HTTPHeaderMap::add(const String& name, const String& value) if (index == notFound) m_uncommonHeaders.append(UncommonHeader { name, value }); else - m_uncommonHeaders[index].value = makeString(m_uncommonHeaders[index].value, ", ", value); + m_uncommonHeaders[index].value = makeString(m_uncommonHeaders[index].value, ", "_s, value); } void HTTPHeaderMap::append(const String& name, const String& value) @@ -252,6 +252,30 @@ String HTTPHeaderMap::get(HTTPHeaderName name) const return index != notFound ? m_commonHeaders[index].value : String(); } +HTTPHeaderMap::HeaderIndex HTTPHeaderMap::indexOf(HTTPHeaderName name) const +{ + auto index = m_commonHeaders.findIf([&](auto& header) { + return header.key == name; + }); + return (HeaderIndex) { .index = index, .isCommon = true }; +} + +HTTPHeaderMap::HeaderIndex HTTPHeaderMap::indexOf(const String& name) const +{ + auto index = m_uncommonHeaders.findIf([&](auto& header) { + return equalIgnoringASCIICase(header.key, name); + }); + return (HeaderIndex) { .index = index, .isCommon = false }; +} + +String HTTPHeaderMap::getIndex(HTTPHeaderMap::HeaderIndex index) const +{ + if (index.index == notFound) + return String(); + if (index.isCommon) + return m_commonHeaders[index.index].value; + return m_uncommonHeaders[index.index].value; +} void HTTPHeaderMap::set(HTTPHeaderName name, const String& value) { if (name == HTTPHeaderName::SetCookie) { @@ -269,6 +293,19 @@ void HTTPHeaderMap::set(HTTPHeaderName name, const String& value) m_commonHeaders[index].value = value; } +bool HTTPHeaderMap::setIndex(HTTPHeaderMap::HeaderIndex index, const String& value) +{ + if (!index.isValid()) + return false; + + if (index.isCommon) { + m_commonHeaders[index.index].value = value; + } else { + m_uncommonHeaders[index.index].value = value; + } + return true; +} + bool HTTPHeaderMap::contains(HTTPHeaderName name) const { if (name == HTTPHeaderName::SetCookie) @@ -303,7 +340,7 @@ void HTTPHeaderMap::add(HTTPHeaderName name, const String& value) return header.key == name; }); if (index != notFound) - m_commonHeaders[index].value = makeString(m_commonHeaders[index].value, ", ", value); + m_commonHeaders[index].value = makeString(m_commonHeaders[index].value, name == HTTPHeaderName::Cookie ? "; "_s : ", "_s, value); else m_commonHeaders.append(CommonHeader { name, value }); } diff --git a/src/bun.js/bindings/webcore/HTTPHeaderMap.h b/src/bun.js/bindings/webcore/HTTPHeaderMap.h index ca506a0e10ee5..589d2945cf242 100644 --- a/src/bun.js/bindings/webcore/HTTPHeaderMap.h +++ b/src/bun.js/bindings/webcore/HTTPHeaderMap.h @@ -48,6 +48,13 @@ class HTTPHeaderMap { bool operator==(const CommonHeader &other) const { return key == other.key && value == other.value; } }; + struct HeaderIndex { + size_t index; + bool isCommon; + + bool isValid() const { return index != notFound; } + }; + struct UncommonHeader { String key; String value; @@ -180,8 +187,14 @@ class HTTPHeaderMap { WEBCORE_EXPORT void add(const String &name, const String &value); WEBCORE_EXPORT void append(const String &name, const String &value); WEBCORE_EXPORT bool contains(const String &) const; + WEBCORE_EXPORT int64_t indexOf(String &name) const; WEBCORE_EXPORT bool remove(const String &); + WEBCORE_EXPORT String getIndex(HeaderIndex index) const; + WEBCORE_EXPORT bool setIndex(HeaderIndex index, const String &value); + HeaderIndex indexOf(const String &name) const; + HeaderIndex indexOf(HTTPHeaderName name) const; + #if USE(CF) void set(CFStringRef name, const String &value); #ifdef __OBJC__ diff --git a/src/bun.js/bindings/webcore/HTTPParsers.cpp b/src/bun.js/bindings/webcore/HTTPParsers.cpp index 7721f96e94983..2bef4f365e561 100644 --- a/src/bun.js/bindings/webcore/HTTPParsers.cpp +++ b/src/bun.js/bindings/webcore/HTTPParsers.cpp @@ -741,13 +741,13 @@ size_t parseHTTPHeader(const uint8_t* start, size_t length, String& failureReaso if (name.isEmpty()) { if (p + 1 < end && *(p + 1) == '\n') return (p + 2) - start; - failureReason = makeString("CR doesn't follow LF in header name at ", trimInputSample(p, end - p)); + failureReason = makeString("CR doesn't follow LF in header name at "_s, trimInputSample(p, end - p)); return 0; } - failureReason = makeString("Unexpected CR in header name at ", trimInputSample(name.data(), name.size())); + failureReason = makeString("Unexpected CR in header name at "_s, trimInputSample(name.data(), name.size())); return 0; case '\n': - failureReason = makeString("Unexpected LF in header name at ", trimInputSample(name.data(), name.size())); + failureReason = makeString("Unexpected LF in header name at "_s, trimInputSample(name.data(), name.size())); return 0; case ':': break; @@ -756,7 +756,7 @@ size_t parseHTTPHeader(const uint8_t* start, size_t length, String& failureReaso if (name.size() < 1) failureReason = "Unexpected start character in header name"_s; else - failureReason = makeString("Unexpected character in header name at ", trimInputSample(name.data(), name.size())); + failureReason = makeString("Unexpected character in header name at "_s, trimInputSample(name.data(), name.size())); return 0; } name.append(*p); @@ -784,7 +784,7 @@ size_t parseHTTPHeader(const uint8_t* start, size_t length, String& failureReaso break; case '\n': if (strict) { - failureReason = makeString("Unexpected LF in header value at ", trimInputSample(value.data(), value.size())); + failureReason = makeString("Unexpected LF in header value at "_s, trimInputSample(value.data(), value.size())); return 0; } break; @@ -797,7 +797,7 @@ size_t parseHTTPHeader(const uint8_t* start, size_t length, String& failureReaso } } if (p >= end || (strict && *p != '\n')) { - failureReason = makeString("CR doesn't follow LF after header value at ", trimInputSample(p, end - p)); + failureReason = makeString("CR doesn't follow LF after header value at "_s, trimInputSample(p, end - p)); return 0; } valueStr = String::fromUTF8({ value.data(), value.size() }); diff --git a/src/bun.js/bindings/webcore/InternalWritableStream.cpp b/src/bun.js/bindings/webcore/InternalWritableStream.cpp index d3988c9084dee..d63acc71f57c2 100644 --- a/src/bun.js/bindings/webcore/InternalWritableStream.cpp +++ b/src/bun.js/bindings/webcore/InternalWritableStream.cpp @@ -127,7 +127,7 @@ JSC::JSValue InternalWritableStream::abort(JSC::JSGlobalObject& globalObject, JS auto result = invokeWritableStreamFunction(globalObject, privateName, arguments); if (result.hasException()) - return { }; + return {}; return result.returnValue(); } @@ -143,7 +143,7 @@ JSC::JSValue InternalWritableStream::close(JSC::JSGlobalObject& globalObject) auto result = invokeWritableStreamFunction(globalObject, privateName, arguments); if (result.hasException()) - return { }; + return {}; return result.returnValue(); } @@ -159,7 +159,7 @@ JSC::JSValue InternalWritableStream::getWriter(JSC::JSGlobalObject& globalObject auto result = invokeWritableStreamFunction(globalObject, privateName, arguments); if (result.hasException()) - return { }; + return {}; return result.returnValue(); } diff --git a/src/bun.js/bindings/webcore/JSAbortAlgorithm.cpp b/src/bun.js/bindings/webcore/JSAbortAlgorithm.cpp index d3e2fadfd03a6..26e5f57f54ef9 100644 --- a/src/bun.js/bindings/webcore/JSAbortAlgorithm.cpp +++ b/src/bun.js/bindings/webcore/JSAbortAlgorithm.cpp @@ -24,6 +24,7 @@ #include "JSDOMConvertBase.h" #include "JSDOMExceptionHandling.h" #include "ScriptExecutionContext.h" +#include "DeleteCallbackDataTask.h" namespace WebCore { using namespace JSC; diff --git a/src/bun.js/bindings/webcore/JSAbortController.cpp b/src/bun.js/bindings/webcore/JSAbortController.cpp index 4685d34ea9f3d..81464aa295612 100644 --- a/src/bun.js/bindings/webcore/JSAbortController.cpp +++ b/src/bun.js/bindings/webcore/JSAbortController.cpp @@ -104,7 +104,7 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSAbortControllerDOMConstruct ASSERT(castedThis); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "AbortController"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "AbortController"_s); auto object = AbortController::create(*context); if constexpr (IsExceptionOr) RETURN_IF_EXCEPTION(throwScope, {}); @@ -251,7 +251,7 @@ void JSAbortController::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -294,18 +294,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7AbortController@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore15AbortControllerE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore15AbortControllerE[2]; #endif // If you hit this assertion you either have a use after free bug, or // AbortController has subclasses. If AbortController has subclasses that get passed // to toJS() we currently require AbortController you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSAbortSignal.cpp b/src/bun.js/bindings/webcore/JSAbortSignal.cpp index 62ad867a3894d..b724b1c4199d2 100644 --- a/src/bun.js/bindings/webcore/JSAbortSignal.cpp +++ b/src/bun.js/bindings/webcore/JSAbortSignal.cpp @@ -225,7 +225,7 @@ static inline JSValue jsAbortSignal_reasonGetter(JSGlobalObject& lexicalGlobalOb auto& vm = JSC::getVM(&lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); auto& impl = thisObject.wrapped(); - RELEASE_AND_RETURN(throwScope, (toJS(lexicalGlobalObject, throwScope, impl.reason()))); + RELEASE_AND_RETURN(throwScope, (toJS(lexicalGlobalObject, throwScope, impl.jsReason(lexicalGlobalObject)))); } JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_reason, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) @@ -361,7 +361,7 @@ void JSAbortSignal::visitChildrenImpl(JSCell* cell, Visitor& visitor) DEFINE_VISIT_CHILDREN(JSAbortSignal); template -void JSAbortSignal::visitOutputConstraints(JSCell* cell, Visitor& visitor) +void JSAbortSignal::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor) { auto* thisObject = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); @@ -369,14 +369,14 @@ void JSAbortSignal::visitOutputConstraints(JSCell* cell, Visitor& visitor) thisObject->visitAdditionalChildren(visitor); } -template void JSAbortSignal::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); -template void JSAbortSignal::visitOutputConstraints(JSCell*, SlotVisitor&); +DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSAbortSignal); + void JSAbortSignal::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) { auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -405,18 +405,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7AbortSignal@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore11AbortSignalE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore11AbortSignalE[2]; #endif // If you hit this assertion you either have a use after free bug, or // AbortSignal has subclasses. If AbortSignal has subclasses that get passed // to toJS() we currently require AbortSignal you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSAbortSignal.h b/src/bun.js/bindings/webcore/JSAbortSignal.h index 1343961d9872b..d52af70bb0c49 100644 --- a/src/bun.js/bindings/webcore/JSAbortSignal.h +++ b/src/bun.js/bindings/webcore/JSAbortSignal.h @@ -59,9 +59,8 @@ class JSAbortSignal : public JSEventTarget { } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); DECLARE_VISIT_CHILDREN; + DECLARE_VISIT_OUTPUT_CONSTRAINTS; template void visitAdditionalChildren(Visitor&); - - template static void visitOutputConstraints(JSCell*, Visitor&); static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); AbortSignal& wrapped() const { diff --git a/src/bun.js/bindings/webcore/JSAbortSignalCustom.cpp b/src/bun.js/bindings/webcore/JSAbortSignalCustom.cpp index 20fdae80a2d1f..01cee8eecf192 100644 --- a/src/bun.js/bindings/webcore/JSAbortSignalCustom.cpp +++ b/src/bun.js/bindings/webcore/JSAbortSignalCustom.cpp @@ -42,11 +42,31 @@ bool JSAbortSignalOwner::isReachableFromOpaqueRoots(JSC::Handle ha if (abortSignal.aborted()) return false; - if (abortSignal.isFollowingSignal()) + if (abortSignal.isFollowingSignal()) { + if (UNLIKELY(reason)) + *reason = "Is Following Signal"_s; return true; + } - if (abortSignal.hasAbortEventListener() && abortSignal.hasActiveTimeoutTimer()) - return true; + if (abortSignal.hasAbortEventListener()) { + if (abortSignal.hasActiveTimeoutTimer()) { + if (UNLIKELY(reason)) + *reason = "Has Timeout And Abort Event Listener"_s; + return true; + } + if (!abortSignal.sourceSignals().isEmptyIgnoringNullReferences()) { + if (UNLIKELY(reason)) + *reason = "Has Source Signals And Abort Event Listener"_s; + return true; + } + + // https://github.com/oven-sh/bun/issues/4517 + if (abortSignal.hasPendingActivity()) { + if (UNLIKELY(reason)) + *reason = "Has Pending Activity"_s; + return true; + } + } return visitor.containsOpaqueRoot(&abortSignal); } @@ -54,6 +74,7 @@ bool JSAbortSignalOwner::isReachableFromOpaqueRoots(JSC::Handle ha template void JSAbortSignal::visitAdditionalChildren(Visitor& visitor) { + wrapped().reason().visit(visitor); } DEFINE_VISIT_ADDITIONAL_CHILDREN(JSAbortSignal); diff --git a/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp b/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp index feffce6477c5f..980dfa0050575 100644 --- a/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp +++ b/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp @@ -112,7 +112,7 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSBroadcastChannelDOMCon return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "BroadcastChannel"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "BroadcastChannel"_s); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); auto name = convert(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -351,7 +351,7 @@ void JSBroadcastChannel::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -394,18 +394,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7BroadcastChannel@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore16BroadcastChannelE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore16BroadcastChannelE[2]; #endif // If you hit this assertion you either have a use after free bug, or // BroadcastChannel has subclasses. If BroadcastChannel has subclasses that get passed // to toJS() we currently require BroadcastChannel you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.h b/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.h index 75d52881454f8..20fb28e915ab6 100644 --- a/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.h +++ b/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.h @@ -53,12 +53,11 @@ class JSByteLengthQueuingStrategy : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSByteLengthQueuingStrategy(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSCallbackData.cpp b/src/bun.js/bindings/webcore/JSCallbackData.cpp index f634b48917e9e..4b607fcb9c55f 100644 --- a/src/bun.js/bindings/webcore/JSCallbackData.cpp +++ b/src/bun.js/bindings/webcore/JSCallbackData.cpp @@ -73,7 +73,7 @@ JSValue JSCallbackData::invokeCallback(VM& vm, JSObject* callback, JSValue thisV callData = getCallData(function); if (callData.type == CallData::Type::None) { - returnedException = JSC::Exception::create(vm, createTypeError(lexicalGlobalObject, makeString("'", String(functionName.uid()), "' property of callback interface should be callable"))); + returnedException = JSC::Exception::create(vm, createTypeError(lexicalGlobalObject, makeString("'"_s, String(functionName.uid()), "' property of callback interface should be callable"_s))); return JSValue(); } diff --git a/src/bun.js/bindings/webcore/JSCallbackData.h b/src/bun.js/bindings/webcore/JSCallbackData.h index ec62cb049fcc7..d688a82c2ac64 100644 --- a/src/bun.js/bindings/webcore/JSCallbackData.h +++ b/src/bun.js/bindings/webcore/JSCallbackData.h @@ -109,15 +109,4 @@ class JSCallbackDataWeak : public JSCallbackData { JSC::Weak m_callback; }; -class DeleteCallbackDataTask : public EventLoopTask { -public: - template - explicit DeleteCallbackDataTask(CallbackDataType* data) - : EventLoopTask(EventLoopTask::CleanupTask, [data](ScriptExecutionContext&) mutable { - delete data; - }) - { - } -}; - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSCloseEvent.cpp b/src/bun.js/bindings/webcore/JSCloseEvent.cpp index a302dcdfb35aa..4b297e6e4173b 100644 --- a/src/bun.js/bindings/webcore/JSCloseEvent.cpp +++ b/src/bun.js/bindings/webcore/JSCloseEvent.cpp @@ -330,7 +330,7 @@ void JSCloseEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -352,18 +352,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7CloseEvent@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore10CloseEventE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore10CloseEventE[2]; #endif // If you hit this assertion you either have a use after free bug, or // CloseEvent has subclasses. If CloseEvent has subclasses that get passed // to toJS() we currently require CloseEvent you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSCloseEvent.h b/src/bun.js/bindings/webcore/JSCloseEvent.h index d7f25493b0438..13dc230087839 100644 --- a/src/bun.js/bindings/webcore/JSCloseEvent.h +++ b/src/bun.js/bindings/webcore/JSCloseEvent.h @@ -61,6 +61,7 @@ class JSCloseEvent : public JSEvent { { return static_cast(Base::wrapped()); } + protected: JSCloseEvent(JSC::Structure*, JSDOMGlobalObject&, Ref&&); @@ -78,5 +79,4 @@ template<> struct JSDOMWrapperConverterTraits { }; template<> CloseEvent::Init convertDictionary(JSC::JSGlobalObject&, JSC::JSValue); - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSCountQueuingStrategy.h b/src/bun.js/bindings/webcore/JSCountQueuingStrategy.h index 0cd89d4fc7bdf..0e582615eaf19 100644 --- a/src/bun.js/bindings/webcore/JSCountQueuingStrategy.h +++ b/src/bun.js/bindings/webcore/JSCountQueuingStrategy.h @@ -53,12 +53,11 @@ class JSCountQueuingStrategy : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSCountQueuingStrategy(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSCustomEvent.cpp b/src/bun.js/bindings/webcore/JSCustomEvent.cpp index 2d5e3b9da3d43..1a884481829fa 100644 --- a/src/bun.js/bindings/webcore/JSCustomEvent.cpp +++ b/src/bun.js/bindings/webcore/JSCustomEvent.cpp @@ -333,7 +333,7 @@ void JSCustomEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -355,18 +355,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj // if constexpr (std::is_polymorphic_v) { // #if ENABLE(BINDING_INTEGRITY) - // const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // // const void* actualVTablePointer = getVTablePointer(impl.ptr()); // #if PLATFORM(WIN) // void* expectedVTablePointer = __identifier("??_7CustomEvent@WebCore@@6B@"); // #else - // void* expectedVTablePointer = &_ZTVN7WebCore11CustomEventE[2]; + // // void* expectedVTablePointer = &_ZTVN7WebCore11CustomEventE[2]; // #endif // // If you hit this assertion you either have a use after free bug, or // // CustomEvent has subclasses. If CustomEvent has subclasses that get passed // // to toJS() we currently require CustomEvent you to opt out of binding hardening // // by adding the SkipVTableValidation attribute to the interface IDL definition - // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); // #endif // } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSCustomEvent.h b/src/bun.js/bindings/webcore/JSCustomEvent.h index 309b4a4a85f82..d32bf98e45fde 100644 --- a/src/bun.js/bindings/webcore/JSCustomEvent.h +++ b/src/bun.js/bindings/webcore/JSCustomEvent.h @@ -68,6 +68,7 @@ class JSCustomEvent : public JSEvent { { return static_cast(Base::wrapped()); } + protected: JSCustomEvent(JSC::Structure*, JSDOMGlobalObject&, Ref&&); @@ -85,5 +86,4 @@ template<> struct JSDOMWrapperConverterTraits { }; template<> CustomEvent::Init convertDictionary(JSC::JSGlobalObject&, JSC::JSValue); - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSDOMAttribute.h b/src/bun.js/bindings/webcore/JSDOMAttribute.h index 17b0f588c7952..19923b76c720c 100644 --- a/src/bun.js/bindings/webcore/JSDOMAttribute.h +++ b/src/bun.js/bindings/webcore/JSDOMAttribute.h @@ -76,7 +76,7 @@ class IDLAttribute { { return setter(lexicalGlobalObject, JSC::JSValue::decode(encodedValue)); } - + template static JSC::EncodedJSValue get(JSC::JSGlobalObject& lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName attributeName) { diff --git a/src/bun.js/bindings/webcore/JSDOMBuiltinConstructor.h b/src/bun.js/bindings/webcore/JSDOMBuiltinConstructor.h index 733a880f30584..afd728ec6c1ab 100644 --- a/src/bun.js/bindings/webcore/JSDOMBuiltinConstructor.h +++ b/src/bun.js/bindings/webcore/JSDOMBuiltinConstructor.h @@ -38,10 +38,11 @@ template class JSDOMBuiltinConstructor final : public JSDOMBui static JSC::JSValue prototypeForStructure(JSC::VM&, const JSDOMGlobalObject&); static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*); private: JSDOMBuiltinConstructor(JSC::VM& vm, JSC::Structure* structure) - : Base(vm, structure, construct) + : Base(vm, structure, construct, call) { } @@ -71,7 +72,7 @@ template inline void JSDOMBuiltinConstructor::finishC { Base::finishCreation(vm); ASSERT(inherits(info())); - setInitializeFunction(vm, *JSC::JSFunction::create(vm, initializeExecutable(vm), &globalObject)); + setInitializeFunction(vm, *JSC::JSFunction::create(vm, &globalObject, initializeExecutable(vm), &globalObject)); initializeProperties(vm, globalObject); } @@ -89,6 +90,25 @@ template inline JSC::Structure* JSDOMBuiltinConstructor inline JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMBuiltinConstructor::call(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + ASSERT(callFrame); + auto* castedThis = JSC::jsCast(callFrame->jsCallee()); + auto& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (callFrame->thisValue() != castedThis) { + throwTypeError(lexicalGlobalObject, scope, "Constructor called as a function"_s); + return {}; + } + auto* structure = castedThis->getDOMStructureForJSObject(lexicalGlobalObject, asObject(callFrame->thisValue())); + if (UNLIKELY(!structure)) + return {}; + + auto* jsObject = JSClass::create(structure, castedThis->globalObject()); + JSC::call(lexicalGlobalObject, castedThis->initializeFunction(), jsObject, JSC::ArgList(callFrame), "This error should never occur: initialize function is guaranteed to be callable."_s); + return JSC::JSValue::encode(jsObject); +} + template inline JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMBuiltinConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) { ASSERT(callFrame); diff --git a/src/bun.js/bindings/webcore/JSDOMBuiltinConstructorBase.h b/src/bun.js/bindings/webcore/JSDOMBuiltinConstructorBase.h index 9593d99c1c4d4..8bcb43af7deb4 100644 --- a/src/bun.js/bindings/webcore/JSDOMBuiltinConstructorBase.h +++ b/src/bun.js/bindings/webcore/JSDOMBuiltinConstructorBase.h @@ -37,8 +37,8 @@ class JSDOMBuiltinConstructorBase : public JSDOMConstructorBase { } protected: - JSDOMBuiltinConstructorBase(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction functionForConstruct) - : Base(vm, structure, functionForConstruct) + JSDOMBuiltinConstructorBase(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction functionForConstruct, JSC::NativeFunction functionForCall = nullptr) + : Base(vm, structure, functionForConstruct, functionForCall) { } diff --git a/src/bun.js/bindings/webcore/JSDOMConstructorBase.cpp b/src/bun.js/bindings/webcore/JSDOMConstructorBase.cpp index 76e5bed57d253..09556cf5c9280 100644 --- a/src/bun.js/bindings/webcore/JSDOMConstructorBase.cpp +++ b/src/bun.js/bindings/webcore/JSDOMConstructorBase.cpp @@ -20,29 +20,27 @@ */ #include "config.h" + #include "JSDOMConstructor.h" #include "WebCoreJSClientData.h" +#include "ErrorCode.h" namespace WebCore { using namespace JSC; STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSDOMConstructorBase); -JSC_DEFINE_HOST_FUNCTION(callThrowTypeErrorForJSDOMConstructor, (JSGlobalObject * globalObject, CallFrame*)) +JSC_DEFINE_HOST_FUNCTION(callThrowTypeErrorForJSDOMConstructor, (JSGlobalObject * globalObject, CallFrame* callframe)) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - throwTypeError(globalObject, scope, "Constructor requires 'new' operator"_s); - return JSValue::encode(jsNull()); -} - -JSC_DEFINE_HOST_FUNCTION(callThrowTypeErrorForJSDOMConstructorNotConstructable, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSC::throwTypeError(globalObject, scope, "Illegal constructor"_s); - return JSC::JSValue::encode(JSC::jsNull()); + auto* callee = callframe->jsCallee(); + auto* constructor = jsDynamicCast(callee); + const auto& name = constructor->name(); + RETURN_IF_EXCEPTION(scope, {}); + Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_ILLEGAL_CONSTRUCTOR, makeString("Use `new "_s, name, "(...)` instead of `"_s, name, "(...)`"_s)); + return {}; } JSC::GCClient::IsoSubspace* JSDOMConstructorBase::subspaceForImpl(JSC::VM& vm) diff --git a/src/bun.js/bindings/webcore/JSDOMConstructorBase.h b/src/bun.js/bindings/webcore/JSDOMConstructorBase.h index 117ea9d39cb32..44c60e69ced8c 100644 --- a/src/bun.js/bindings/webcore/JSDOMConstructorBase.h +++ b/src/bun.js/bindings/webcore/JSDOMConstructorBase.h @@ -27,7 +27,6 @@ namespace WebCore { JSC_DECLARE_HOST_FUNCTION(callThrowTypeErrorForJSDOMConstructor); -JSC_DECLARE_HOST_FUNCTION(callThrowTypeErrorForJSDOMConstructorNotConstructable); // Base class for all callable constructor objects in the JSC bindings. class JSDOMConstructorBase : public JSC::InternalFunction { @@ -52,10 +51,10 @@ class JSDOMConstructorBase : public JSC::InternalFunction { ScriptExecutionContext* scriptExecutionContext() const { return globalObject()->scriptExecutionContext(); } protected: - JSDOMConstructorBase(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction functionForConstruct) + JSDOMConstructorBase(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction functionForConstruct, JSC::NativeFunction functionForCall = nullptr) : Base(vm, structure, - functionForConstruct ? functionForConstruct : callThrowTypeErrorForJSDOMConstructorNotConstructable, - functionForConstruct ? functionForConstruct : callThrowTypeErrorForJSDOMConstructorNotConstructable) + functionForCall ? functionForCall : callThrowTypeErrorForJSDOMConstructor, + functionForConstruct ? functionForConstruct : callThrowTypeErrorForJSDOMConstructor) { } }; diff --git a/src/bun.js/bindings/webcore/JSDOMConvertBase.h b/src/bun.js/bindings/webcore/JSDOMConvertBase.h index d1b994f09004e..737b65bc120c0 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertBase.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertBase.h @@ -38,10 +38,10 @@ template struct Converter; namespace Detail { -template inline T* getPtrOrRef(const T* p) { return const_cast(p); } -template inline T& getPtrOrRef(const T& p) { return const_cast(p); } -template inline T* getPtrOrRef(const RefPtr& p) { return p.get(); } -template inline T& getPtrOrRef(const Ref& p) { return p.get(); } +template inline T* getPtrOrRef(const T* p) { return const_cast(p); } +template inline T& getPtrOrRef(const T& p) { return const_cast(p); } +template inline T* getPtrOrRef(const RefPtr& p) { return p.get(); } +template inline T& getPtrOrRef(const Ref& p) { return p.get(); } } @@ -89,7 +89,6 @@ template inline typename Converter::Re return Converter::convert(lexicalGlobalObject, value, globalObject, std::forward(exceptionThrower)); } - // Conversion from Implementation -> JSValue template struct JSConverter; @@ -171,7 +170,7 @@ template inline JSC::JSValue toJS(JSC::JSGlobalObject& l auto result = valueOrFunctor(); if (UNLIKELY(result.hasException())) { propagateException(lexicalGlobalObject, throwScope, result.releaseException()); - return { }; + return {}; } return JSC::jsUndefined(); } else @@ -180,7 +179,7 @@ template inline JSC::JSValue toJS(JSC::JSGlobalObject& l if constexpr (IsExceptionOr) { if (UNLIKELY(valueOrFunctor.hasException())) { propagateException(lexicalGlobalObject, throwScope, valueOrFunctor.releaseException()); - return { }; + return {}; } return toJS(lexicalGlobalObject, valueOrFunctor.releaseReturnValue()); @@ -201,7 +200,7 @@ template inline JSC::JSValue toJS(JSC::JSGlobalObject& l auto result = valueOrFunctor(); if (UNLIKELY(result.hasException())) { propagateException(lexicalGlobalObject, throwScope, result.releaseException()); - return { }; + return {}; } return JSC::jsUndefined(); } else @@ -210,7 +209,7 @@ template inline JSC::JSValue toJS(JSC::JSGlobalObject& l if constexpr (IsExceptionOr) { if (UNLIKELY(valueOrFunctor.hasException())) { propagateException(lexicalGlobalObject, throwScope, valueOrFunctor.releaseException()); - return { }; + return {}; } return toJS(lexicalGlobalObject, globalObject, valueOrFunctor.releaseReturnValue()); @@ -231,7 +230,7 @@ template inline JSC::JSValue toJSNewlyCreated(JSC::JSGlo auto result = valueOrFunctor(); if (UNLIKELY(result.hasException())) { propagateException(lexicalGlobalObject, throwScope, result.releaseException()); - return { }; + return {}; } return JSC::jsUndefined(); } else @@ -241,7 +240,7 @@ template inline JSC::JSValue toJSNewlyCreated(JSC::JSGlo if constexpr (IsExceptionOr) { if (UNLIKELY(valueOrFunctor.hasException())) { propagateException(lexicalGlobalObject, throwScope, valueOrFunctor.releaseException()); - return { }; + return {}; } return toJSNewlyCreated(lexicalGlobalObject, globalObject, valueOrFunctor.releaseReturnValue()); diff --git a/src/bun.js/bindings/webcore/JSDOMConvertBufferSource.h b/src/bun.js/bindings/webcore/JSDOMConvertBufferSource.h index 211ba841158aa..0a451c5a09c41 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertBufferSource.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertBufferSource.h @@ -47,6 +47,8 @@ struct IDLUint32Array : IDLTypedArray { }; struct IDLUint8ClampedArray : IDLTypedArray { }; +struct IDLFloat16Array : IDLTypedArray { +}; struct IDLFloat32Array : IDLTypedArray { }; struct IDLFloat64Array : IDLTypedArray { @@ -63,6 +65,7 @@ inline RefPtr toPossiblySharedUint8Array(JSC::VM& vm, JSC::JSVa inline RefPtr toPossiblySharedUint8ClampedArray(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } inline RefPtr toPossiblySharedUint16Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } inline RefPtr toPossiblySharedUint32Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } +inline RefPtr toPossiblySharedFloat16Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } inline RefPtr toPossiblySharedFloat32Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } inline RefPtr toPossiblySharedFloat64Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } inline RefPtr toPossiblySharedBigInt64Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toPossiblySharedNativeTypedView(vm, value); } @@ -75,6 +78,7 @@ inline RefPtr toUnsharedUint8Array(JSC::VM& vm, JSC::JSValue va inline RefPtr toUnsharedUint8ClampedArray(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } inline RefPtr toUnsharedUint16Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } inline RefPtr toUnsharedUint32Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } +inline RefPtr toUnsharedFloat16Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } inline RefPtr toUnsharedFloat32Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } inline RefPtr toUnsharedFloat64Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } inline RefPtr toUnsharedBigInt64Array(JSC::VM& vm, JSC::JSValue value) { return JSC::toUnsharedNativeTypedView(vm, value); } @@ -355,6 +359,28 @@ template<> struct JSConverter { } }; +template<> struct Converter : DefaultConverter { + using WrapperType = JSC::JSFloat16Array; + using ReturnType = RefPtr; + + template + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, ExceptionThrower&& exceptionThrower = ExceptionThrower()) + { + return Detail::BufferSourceConverter::convert(lexicalGlobalObject, value, std::forward(exceptionThrower)); + } +}; + +template<> struct JSConverter { + static constexpr bool needsState = true; + static constexpr bool needsGlobalObject = true; + + template + static JSC::JSValue convert(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const U& value) + { + return toJS(&lexicalGlobalObject, &globalObject, Detail::getPtrOrRef(value)); + } +}; + template<> struct Converter : DefaultConverter { using WrapperType = JSC::JSFloat32Array; using ReturnType = RefPtr; diff --git a/src/bun.js/bindings/webcore/JSDOMConvertCallbacks.h b/src/bun.js/bindings/webcore/JSDOMConvertCallbacks.h index eb49f2357e4aa..05dcc37ae1a51 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertCallbacks.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertCallbacks.h @@ -68,7 +68,7 @@ template struct JSConverter> { static constexpr bool needsState = false; static constexpr bool needsGlobalObject = false; - template + template static JSC::JSValue convert(const U& value) { return toJS(Detail::getPtrOrRef(value)); @@ -81,7 +81,6 @@ template struct JSConverter> { } }; - template struct Converter> : DefaultConverter> { template static RefPtr convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, JSDOMGlobalObject& globalObject, ExceptionThrower&& exceptionThrower = ExceptionThrower()) @@ -102,7 +101,7 @@ template struct JSConverter> { static constexpr bool needsState = false; static constexpr bool needsGlobalObject = false; - template + template static JSC::JSValue convert(const U& value) { return toJS(Detail::getPtrOrRef(value)); diff --git a/src/bun.js/bindings/webcore/JSDOMConvertDictionary.h b/src/bun.js/bindings/webcore/JSDOMConvertDictionary.h index 94173b16fdb98..87f1a1c4684f8 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertDictionary.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertDictionary.h @@ -33,7 +33,6 @@ namespace WebCore { // Specialized by generated code for IDL dictionary conversion. template T convertDictionary(JSC::JSGlobalObject&, JSC::JSValue); - template struct Converter> : DefaultConverter> { using ReturnType = T; diff --git a/src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h b/src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h index d458e4aa8cb3e..224922f1b80ee 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h @@ -33,12 +33,11 @@ namespace WebCore { // Specialized by generated code for IDL enumeration conversion. template std::optional parseEnumeration(JSC::JSGlobalObject&, JSC::JSValue); -template const char* expectedEnumerationValues(); +template ASCIILiteral expectedEnumerationValues(); // Specialized by generated code for IDL enumeration conversion. template JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, T); - template struct Converter> : DefaultConverter> { template static T convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, ExceptionThrower&& exceptionThrower = ExceptionThrower()) @@ -47,11 +46,11 @@ template struct Converter> : DefaultConverter(lexicalGlobalObject, value); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); if (UNLIKELY(!result)) { exceptionThrower(lexicalGlobalObject, throwScope); - return { }; + return {}; } return result.value(); } diff --git a/src/bun.js/bindings/webcore/JSDOMConvertNullable.h b/src/bun.js/bindings/webcore/JSDOMConvertNullable.h index a3e4b5cc41a55..549d126bb4689 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertNullable.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertNullable.h @@ -38,7 +38,7 @@ namespace Detail { template struct NullableConversionType; -template +template struct NullableConversionType { using Type = typename IDLNullable::ImplementationType; }; @@ -57,7 +57,7 @@ struct NullableConversionType { template struct Converter> : DefaultConverter> { using ReturnType = typename Detail::NullableConversionType::Type; - + // 1. If Type(V) is not Object, and the conversion to an IDL value is being performed // due to V being assigned to an attribute whose type is a nullable callback function // that is annotated with [LegacyTreatNonObjectAsNull], then return the IDL nullable @@ -146,5 +146,4 @@ template struct JSConverter> { } }; - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSDOMConvertNumbers.cpp b/src/bun.js/bindings/webcore/JSDOMConvertNumbers.cpp index c417a37efd259..d1c20179f3514 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertNumbers.cpp +++ b/src/bun.js/bindings/webcore/JSDOMConvertNumbers.cpp @@ -43,7 +43,7 @@ static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, larges static String rangeErrorString(double value, double min, double max) { - return makeString("Value ", value, " is outside the range [", min, ", ", max, ']'); + return makeString("Value "_s, value, " is outside the range ["_s, min, ", "_s, max, ']'); } static double enforceRange(JSGlobalObject& lexicalGlobalObject, double x, double minimum, double maximum) diff --git a/src/bun.js/bindings/webcore/JSDOMConvertNumbers.h b/src/bun.js/bindings/webcore/JSDOMConvertNumbers.h index 29413d8d98969..9d8bd929eeb4a 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertNumbers.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertNumbers.h @@ -251,7 +251,6 @@ template struct JSConverter> { } }; - template struct Converter> : DefaultConverter> { using ReturnType = typename IDLEnforceRangeAdaptor::ImplementationType; @@ -273,7 +272,6 @@ template struct JSConverter> { } }; - // MARK: - // MARK: Floating point types diff --git a/src/bun.js/bindings/webcore/JSDOMConvertWebGL.cpp b/src/bun.js/bindings/webcore/JSDOMConvertWebGL.cpp index 7d45766614706..bfcf892fe6969 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertWebGL.cpp +++ b/src/bun.js/bindings/webcore/JSDOMConvertWebGL.cpp @@ -122,6 +122,9 @@ JSValue convertToJSValue(JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& RELEASE_ASSERT(!list.hasOverflowed()); return constructArray(&globalObject, static_cast(nullptr), list); }, + [&](const RefPtr& array) { + return toJS(&lexicalGlobalObject, &globalObject, array.get()); + }, [&](const RefPtr& array) { return toJS(&lexicalGlobalObject, &globalObject, array.get()); }, diff --git a/src/bun.js/bindings/webcore/JSDOMConvertWebGL.h b/src/bun.js/bindings/webcore/JSDOMConvertWebGL.h index ae8f7e688d9f5..0f899215ac728 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertWebGL.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertWebGL.h @@ -56,7 +56,7 @@ template<> struct JSConverter { static constexpr bool needsState = true; static constexpr bool needsGlobalObject = true; - template + template static JSC::JSValue convert(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const T& value) { return convertToJSValue(lexicalGlobalObject, globalObject, Detail::getPtrOrRef(value)); diff --git a/src/bun.js/bindings/webcore/JSDOMException.cpp b/src/bun.js/bindings/webcore/JSDOMException.cpp index 5176577550b0e..0d6ca98497e0c 100644 --- a/src/bun.js/bindings/webcore/JSDOMException.cpp +++ b/src/bun.js/bindings/webcore/JSDOMException.cpp @@ -306,7 +306,7 @@ void JSDOMException::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + // analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -343,18 +343,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj // if constexpr (std::is_polymorphic_v) { // #if ENABLE(BINDING_INTEGRITY) - // const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // // const void* actualVTablePointer = getVTablePointer(impl.ptr()); // #if PLATFORM(WIN) // void* expectedVTablePointer = __identifier("??_7DOMException@WebCore@@6B@"); // #else - // void* expectedVTablePointer = &_ZTVN7WebCore12DOMExceptionE[2]; + // // void* expectedVTablePointer = &_ZTVN7WebCore12DOMExceptionE[2]; // #endif // // If you hit this assertion you either have a use after free bug, or // // DOMException has subclasses. If DOMException has subclasses that get passed // // to toJS() we currently require DOMException you to opt out of binding hardening // // by adding the SkipVTableValidation attribute to the interface IDL definition - // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); // #endif // } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.cpp b/src/bun.js/bindings/webcore/JSDOMFormData.cpp index 2bb7b0ff49b54..755944a96b778 100644 --- a/src/bun.js/bindings/webcore/JSDOMFormData.cpp +++ b/src/bun.js/bindings/webcore/JSDOMFormData.cpp @@ -184,7 +184,7 @@ template<> JSValue JSDOMFormDataDOMConstructor::prototypeForStructure(JSC::VM& v return globalObject.functionPrototype(); } -extern "C" JSC::EncodedJSValue FormData__jsFunctionFromMultipartData(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe); +BUN_DECLARE_HOST_FUNCTION(FormData__jsFunctionFromMultipartData); template<> void JSDOMFormDataDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) { @@ -461,10 +461,7 @@ static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_set2Body(JSC::J auto name = convert(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - EnsureStillAliveScope argument2 = callFrame->argument(2); - auto filename = argument2.value().isUndefined() ? String() : convert(*lexicalGlobalObject, argument2.value()); - RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RefPtr blobValue = nullptr; if (argument1.value().inherits()) { @@ -473,8 +470,11 @@ static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_set2Body(JSC::J if (!blobValue) { throwTypeError(lexicalGlobalObject, throwScope, "Expected argument to be a Blob."_s); - RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); } + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + auto filename = argument2.value().isUndefined() ? Blob__getFileNameString(blobValue->impl()).toWTFString(BunString::ZeroCopy) : convert(*lexicalGlobalObject, argument2.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.set(WTFMove(name), WTFMove(blobValue), WTFMove(filename)); }))); } @@ -695,7 +695,7 @@ void JSDOMFormData::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -732,18 +732,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7DOMFormData@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore11DOMFormDataE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore11DOMFormDataE[2]; #endif // If you hit this assertion you either have a use after free bug, or // DOMFormData has subclasses. If DOMFormData has subclasses that get passed // to toJS() we currently require DOMFormData you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSDOMIterator.h b/src/bun.js/bindings/webcore/JSDOMIterator.h index f0f8efa91ab11..4f60337af0891 100644 --- a/src/bun.js/bindings/webcore/JSDOMIterator.h +++ b/src/bun.js/bindings/webcore/JSDOMIterator.h @@ -27,10 +27,11 @@ #pragma once #include "JSDOMConvert.h" -#include +#include #include #include - +#include "ErrorCode.h" +#include "JavaScriptCore/Interpreter.h" namespace WebCore { void addValueIterableMethods(JSC::JSGlobalObject&, JSC::JSObject&); @@ -101,7 +102,9 @@ template class JSDOMIteratorBase : static Prototype* createPrototype(JSC::VM& vm, JSC::JSGlobalObject& globalObject) { - return Prototype::create(vm, &globalObject, Prototype::createStructure(vm, &globalObject, globalObject.iteratorPrototype())); + auto* structure = Prototype::createStructure(vm, &globalObject, globalObject.iteratorPrototype()); + structure->setMayBePrototype(true); + return Prototype::create(vm, &globalObject, structure); } JSC::JSValue next(JSC::JSGlobalObject&); @@ -111,7 +114,7 @@ template class JSDOMIteratorBase : protected: JSDOMIteratorBase(JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) : Base(structure, *iteratedObject.globalObject()) - , m_iterator(iteratedObject.wrapped().createIterator()) + , m_iterator(iteratedObject.wrapped().createIterator(iteratedObject.globalObject()->scriptExecutionContext())) , m_kind(kind) { } @@ -211,10 +214,12 @@ template JSC::JSValue iteratorForEach(JSC::JSGlobalObject& JSC::JSValue thisValue = callFrame.argument(1); auto callData = JSC::getCallData(callback); - if (callData.type == JSC::CallData::Type::None) - return throwTypeError(&lexicalGlobalObject, scope, "Cannot call callback"_s); + if (callData.type == JSC::CallData::Type::None) { + Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Cannot call callback on a non-function"_s); + return {}; + } - auto iterator = thisObject.wrapped().createIterator(); + auto iterator = thisObject.wrapped().createIterator(JSC::jsCast(&lexicalGlobalObject)->scriptExecutionContext()); while (auto value = iterator.next()) { JSC::MarkedArgumentBuffer arguments; appendForEachArguments(lexicalGlobalObject, *thisObject.globalObject(), arguments, value); @@ -256,8 +261,9 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMIteratorPrototype*>(callFrame->thisValue()); - if (!iterator) - return JSC::JSValue::encode(throwTypeError(globalObject, scope, "Cannot call next() on a non-Iterator object"_s)); + if (!iterator) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_THIS, "Cannot call next() on a non-Iterator object"_s); + } return JSC::JSValue::encode(iterator->next(*globalObject)); } @@ -268,8 +274,8 @@ void JSDOMIteratorPrototype::finishCreation(JSC::VM& Base::finishCreation(vm); ASSERT(inherits(info())); - JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, ImplementationVisibility::Public, JSC::NoIntrinsic); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, JSC::ImplementationVisibility::Public); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } -} \ No newline at end of file +} diff --git a/src/bun.js/bindings/webcore/JSDOMOperation.cpp b/src/bun.js/bindings/webcore/JSDOMOperation.cpp index 0e824fe29b6a7..1c1b40e951586 100644 --- a/src/bun.js/bindings/webcore/JSDOMOperation.cpp +++ b/src/bun.js/bindings/webcore/JSDOMOperation.cpp @@ -19,4 +19,27 @@ JSC::JSObject* createNotEnoughArgumentsErrorBun(JSC::JSGlobalObject* globalObjec return error; } + +void throwNodeRangeError(JSGlobalObject* lexicalGlobalObject, ThrowScope& scope, const String& message) +{ + auto* error = createRangeError(lexicalGlobalObject, message); + if (LIKELY(error)) { + auto& vm = getVM(lexicalGlobalObject); + auto& builtinNames = Bun::builtinNames(vm); + error->putDirect(vm, builtinNames.codePublicName(), jsString(vm, String("ERR_OUT_OF_RANGE"_s))); + scope.throwException(lexicalGlobalObject, error); + } +} + +void throwNodeRangeError(JSGlobalObject* lexicalGlobalObject, ThrowScope& scope, ASCIILiteral message) +{ + auto* error = createRangeError(lexicalGlobalObject, message); + if (LIKELY(error)) { + auto& vm = getVM(lexicalGlobalObject); + auto& builtinNames = Bun::builtinNames(vm); + error->putDirect(vm, builtinNames.codePublicName(), jsString(vm, String("ERR_OUT_OF_RANGE"_s))); + scope.throwException(lexicalGlobalObject, error); + } +} + } \ No newline at end of file diff --git a/src/bun.js/bindings/webcore/JSDOMOperation.h b/src/bun.js/bindings/webcore/JSDOMOperation.h index afae4b89865af..aa834053b1aea 100644 --- a/src/bun.js/bindings/webcore/JSDOMOperation.h +++ b/src/bun.js/bindings/webcore/JSDOMOperation.h @@ -79,4 +79,7 @@ JSC::JSObject* createNotEnoughArgumentsErrorBun(JSGlobalObject* globalObject); #define createNotEnoughArgumentsError WebCore::createNotEnoughArgumentsErrorBun #endif +void throwNodeRangeError(JSGlobalObject* lexicalGlobalObject, ThrowScope& scope, ASCIILiteral message); +void throwNodeRangeError(JSGlobalObject* lexicalGlobalObject, ThrowScope& scope, const String& message); + } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSDOMOperationReturningPromise.h b/src/bun.js/bindings/webcore/JSDOMOperationReturningPromise.h index c4d1513ad5c41..c90c5f2536958 100644 --- a/src/bun.js/bindings/webcore/JSDOMOperationReturningPromise.h +++ b/src/bun.js/bindings/webcore/JSDOMOperationReturningPromise.h @@ -38,7 +38,7 @@ class IDLOperationReturningPromise { template static JSC::EncodedJSValue call(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, const char* operationName) { - return JSC::JSValue::encode(callPromiseFunction(lexicalGlobalObject, callFrame, [&operationName] (JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, Ref&& promise) { + return JSC::JSValue::encode(callPromiseFunction(lexicalGlobalObject, callFrame, [&operationName](JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, Ref&& promise) { auto* thisObject = IDLOperation::cast(lexicalGlobalObject, callFrame); if constexpr (shouldThrow != CastedThisErrorBehavior::Assert) { if (UNLIKELY(!thisObject)) @@ -47,7 +47,7 @@ class IDLOperationReturningPromise { ASSERT(thisObject); ASSERT_GC_OBJECT_INHERITS(thisObject, JSClass::info()); - + // FIXME: We should refactor the binding generated code to use references for lexicalGlobalObject and thisObject. return operation(&lexicalGlobalObject, &callFrame, thisObject, WTFMove(promise)); })); @@ -74,7 +74,7 @@ class IDLOperationReturningPromise { template static JSC::EncodedJSValue callStatic(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, const char*) { - return JSC::JSValue::encode(callPromiseFunction(lexicalGlobalObject, callFrame, [] (JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, Ref&& promise) { + return JSC::JSValue::encode(callPromiseFunction(lexicalGlobalObject, callFrame, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, Ref&& promise) { // FIXME: We should refactor the binding generated code to use references for lexicalGlobalObject. return operation(&lexicalGlobalObject, &callFrame, WTFMove(promise)); })); diff --git a/src/bun.js/bindings/webcore/JSDOMPromise.h b/src/bun.js/bindings/webcore/JSDOMPromise.h index 97ebafa747d8b..742ecbc392549 100644 --- a/src/bun.js/bindings/webcore/JSDOMPromise.h +++ b/src/bun.js/bindings/webcore/JSDOMPromise.h @@ -44,11 +44,14 @@ class DOMPromise : public DOMGuarded { return guarded(); } - enum class IsCallbackRegistered { No, Yes }; + enum class IsCallbackRegistered { No, + Yes }; IsCallbackRegistered whenSettled(std::function&&); JSC::JSValue result() const; - enum class Status { Pending, Fulfilled, Rejected }; + enum class Status { Pending, + Fulfilled, + Rejected }; Status status() const; static IsCallbackRegistered whenPromiseIsSettled(JSDOMGlobalObject*, JSC::JSObject* promise, Function&&); diff --git a/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp b/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp index 1c63149944fbd..a1399773c38ab 100644 --- a/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp +++ b/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp @@ -38,6 +38,8 @@ #include #include #include +#include "ErrorCode.h" +#include "JavaScriptCore/ErrorInstance.h" namespace WebCore { using namespace JSC; @@ -241,7 +243,7 @@ JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::JSGlobalObject& lexi auto promiseConstructor = globalObject.promiseConstructor(); auto rejectFunction = promiseConstructor->get(&lexicalGlobalObject, vm.propertyNames->builtinNames().rejectPrivateName()); RETURN_IF_EXCEPTION(scope, {}); - auto* rejectionValue = static_cast(createTypeError(&lexicalGlobalObject, errorMessage)); + ErrorInstance* rejectionValue = static_cast(cause == RejectedPromiseWithTypeErrorCause::InvalidThis ? Bun::createInvalidThisError(&lexicalGlobalObject, errorMessage) : createTypeError(&lexicalGlobalObject, errorMessage)); if (cause == RejectedPromiseWithTypeErrorCause::NativeGetter) rejectionValue->setNativeGetterTypeError(); @@ -281,7 +283,7 @@ void fulfillPromiseWithArrayBuffer(Ref&& promise, ArrayBuffer* void fulfillPromiseWithArrayBuffer(Ref&& promise, const void* data, size_t length) { - fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get()); + fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate({ reinterpret_cast(data), length }).get()); } bool DeferredPromise::handleTerminationExceptionIfNeeded(CatchScope& scope, JSDOMGlobalObject& lexicalGlobalObject) diff --git a/src/bun.js/bindings/webcore/JSDOMURL.cpp b/src/bun.js/bindings/webcore/JSDOMURL.cpp index 755ab1bfe6683..5f0ca50b09483 100755 --- a/src/bun.js/bindings/webcore/JSDOMURL.cpp +++ b/src/bun.js/bindings/webcore/JSDOMURL.cpp @@ -69,8 +69,8 @@ static JSC_DECLARE_HOST_FUNCTION(jsDOMURLConstructorFunction_createObjectURL); static JSC_DECLARE_HOST_FUNCTION(jsDOMURLConstructorFunction_revokeObjectURL); static JSC_DECLARE_HOST_FUNCTION(jsDOMURLPrototypeFunction_toString); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__createObjectURL); -extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__revokeObjectURL); +BUN_DECLARE_HOST_FUNCTION(Bun__createObjectURL); +BUN_DECLARE_HOST_FUNCTION(Bun__revokeObjectURL); // Attributes @@ -789,7 +789,7 @@ void JSDOMURL::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -830,18 +830,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7DOMURL@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore6DOMURLE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore6DOMURLE[2]; #endif // If you hit this assertion you either have a use after free bug, or // DOMURL has subclasses. If DOMURL has subclasses that get passed // to toJS() we currently require DOMURL you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSErrorCallback.cpp b/src/bun.js/bindings/webcore/JSErrorCallback.cpp index de924d857d6ec..d1b2821e1d875 100644 --- a/src/bun.js/bindings/webcore/JSErrorCallback.cpp +++ b/src/bun.js/bindings/webcore/JSErrorCallback.cpp @@ -27,6 +27,7 @@ #include "JSDOMExceptionHandling.h" #include "JSDOMGlobalObject.h" #include "ScriptExecutionContext.h" +#include "DeleteCallbackDataTask.h" namespace WebCore { using namespace JSC; diff --git a/src/bun.js/bindings/webcore/JSErrorEvent.cpp b/src/bun.js/bindings/webcore/JSErrorEvent.cpp index 6dbf1ce8efe63..60515f2074451 100644 --- a/src/bun.js/bindings/webcore/JSErrorEvent.cpp +++ b/src/bun.js/bindings/webcore/JSErrorEvent.cpp @@ -114,7 +114,7 @@ template<> ErrorEvent::Init convertDictionary(JSGlobalObject& if (isNullOrUndefined) errorValue = jsUndefined(); else { - errorValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "error"_s)); + errorValue = object->get(&lexicalGlobalObject, vm.propertyNames->error); RETURN_IF_EXCEPTION(throwScope, {}); } if (!errorValue.isUndefined()) { @@ -150,7 +150,7 @@ template<> ErrorEvent::Init convertDictionary(JSGlobalObject& if (isNullOrUndefined) messageValue = jsUndefined(); else { - messageValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "message"_s)); + messageValue = object->get(&lexicalGlobalObject, vm.propertyNames->message); RETURN_IF_EXCEPTION(throwScope, {}); } if (!messageValue.isUndefined()) { @@ -409,7 +409,7 @@ void JSErrorEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -431,18 +431,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj // if constexpr (std::is_polymorphic_v) { // #if ENABLE(BINDING_INTEGRITY) - // const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // // const void* actualVTablePointer = getVTablePointer(impl.ptr()); // #if PLATFORM(WIN) // void* expectedVTablePointer = __identifier("??_7ErrorEvent@WebCore@@6B@"); // #else - // void* expectedVTablePointer = &_ZTVN7WebCore10ErrorEventE[2]; + // // void* expectedVTablePointer = &_ZTVN7WebCore10ErrorEventE[2]; // #endif // // If you hit this assertion you either have a use after free bug, or // // ErrorEvent has subclasses. If ErrorEvent has subclasses that get passed // // to toJS() we currently require ErrorEvent you to opt out of binding hardening // // by adding the SkipVTableValidation attribute to the interface IDL definition - // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); // #endif return createWrapper(globalObject, WTFMove(impl)); } diff --git a/src/bun.js/bindings/webcore/JSEvent.cpp b/src/bun.js/bindings/webcore/JSEvent.cpp index 9acd2a25c5c19..988f87f73e6ee 100644 --- a/src/bun.js/bindings/webcore/JSEvent.cpp +++ b/src/bun.js/bindings/webcore/JSEvent.cpp @@ -595,7 +595,7 @@ void JSEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/JSEventCustom.cpp b/src/bun.js/bindings/webcore/JSEventCustom.cpp index 2f8a9b04d5db0..8df99f03c5f43 100644 --- a/src/bun.js/bindings/webcore/JSEventCustom.cpp +++ b/src/bun.js/bindings/webcore/JSEventCustom.cpp @@ -6,13 +6,13 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED diff --git a/src/bun.js/bindings/webcore/JSEventDOMJIT.cpp b/src/bun.js/bindings/webcore/JSEventDOMJIT.cpp index f6c6b77d8093f..e6b70181d1ff6 100644 --- a/src/bun.js/bindings/webcore/JSEventDOMJIT.cpp +++ b/src/bun.js/bindings/webcore/JSEventDOMJIT.cpp @@ -30,7 +30,6 @@ #include "DOMJITCheckDOM.h" - namespace WebCore { using namespace JSC; diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp index 01d6b36ff06db..964c7146d0c3d 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitter.cpp +++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp @@ -104,7 +104,7 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSEventEmitterDOMConstru ASSERT(castedThis); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "EventEmitter"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "EventEmitter"_s); auto object = EventEmitter::create(*context); if constexpr (IsExceptionOr) RETURN_IF_EXCEPTION(throwScope, {}); @@ -246,7 +246,7 @@ inline JSC::EncodedJSValue JSEventEmitter::addListener(JSC::JSGlobalObject* lexi RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); // then, add the listener - auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventEmitter", "addListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener"_s, "EventEmitter"_s, "addListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.addListenerForBindings(WTFMove(eventType), WTFMove(listener), once, prepend); })); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -352,7 +352,7 @@ inline JSC::EncodedJSValue JSEventEmitter::removeListener(JSC::JSGlobalObject* l } EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventEmitter", "removeListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener"_s, "EventEmitter"_s, "removeListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeListenerForBindings(WTFMove(eventType), WTFMove(listener)); })); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -506,7 +506,7 @@ void JSEventEmitter::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -587,7 +587,7 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionOnce, auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); - auto listener = convert>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener", "EventEmitter", "removeListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener"_s, "EventEmitter"_s, "removeListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); vm.writeBarrier(argument0, argument2.value()); @@ -611,7 +611,7 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionOn, auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); - auto listener = convert>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener", "EventEmitter", "removeListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener"_s, "EventEmitter"_s, "removeListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); auto result = JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.addListenerForBindings(WTFMove(eventType), WTFMove(listener), false, false); })); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); diff --git a/src/bun.js/bindings/webcore/JSEventTarget.cpp b/src/bun.js/bindings/webcore/JSEventTarget.cpp index 34f2c5f82c034..6a23f00f776c4 100644 --- a/src/bun.js/bindings/webcore/JSEventTarget.cpp +++ b/src/bun.js/bindings/webcore/JSEventTarget.cpp @@ -108,7 +108,7 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSEventTargetDOMConstruc ASSERT(castedThis); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "EventTarget"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "EventTarget"_s); auto object = EventTarget::create(*context); if constexpr (IsExceptionOr) RETURN_IF_EXCEPTION(throwScope, {}); @@ -218,7 +218,7 @@ static inline JSC::EncodedJSValue jsEventTargetPrototypeFunction_addEventListene auto type = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventTarget", "addEventListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener"_s, "EventTarget"_s, "addEventListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->argument(2); auto options = argument2.value().isUndefined() ? false : convert, IDLBoolean>>(*lexicalGlobalObject, argument2.value()); @@ -247,7 +247,7 @@ static inline JSC::EncodedJSValue jsEventTargetPrototypeFunction_removeEventList auto type = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventTarget", "removeEventListener"); }); + auto listener = convert>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener"_s, "EventTarget"_s, "removeEventListener"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->argument(2); auto options = argument2.value().isUndefined() ? false : convert, IDLBoolean>>(*lexicalGlobalObject, argument2.value()); @@ -273,7 +273,7 @@ static inline JSC::EncodedJSValue jsEventTargetPrototypeFunction_dispatchEventBo if (UNLIKELY(callFrame->argumentCount() < 1)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto event = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "event", "EventTarget", "dispatchEvent", "Event"); }); + auto event = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "event"_s, "EventTarget"_s, "dispatchEvent"_s, "Event"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, impl.dispatchEventForBindings(*event)))); } @@ -320,7 +320,7 @@ void JSEventTarget::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/JSEventTargetNode.cpp b/src/bun.js/bindings/webcore/JSEventTargetNode.cpp new file mode 100644 index 0000000000000..a2cf67c370916 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSEventTargetNode.cpp @@ -0,0 +1,47 @@ + + +#include "root.h" + +#include "JavaScriptCore/JSGlobalObject.h" +#include "JavaScriptCore/ArgList.h" +#include "JavaScriptCore/JSGlobalObjectInlines.h" +#include "JSEventTarget.h" +#include "JavaScriptCore/JSArray.h" +#include "wtf/text/MakeString.h" + +namespace Bun { + +using namespace JSC; +using namespace WebCore; +JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeEventsGetEventListeners, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + if (callFrame->argumentCount() < 2) { + throwTypeError(globalObject, throwScope, "getEventListeners needs 2 arguments"_s); + return JSValue::encode(jsUndefined()); + } + + JSValue thisValue = callFrame->argument(0); + auto* thisObject = jsDynamicCast(thisValue); + RETURN_IF_EXCEPTION(throwScope, {}); + auto eventType = callFrame->argument(1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + + if (UNLIKELY(!thisObject)) + return JSValue::encode(constructEmptyArray(globalObject, nullptr, 0)); + + MarkedArgumentBuffer values; + auto& listeners = thisObject->wrapped().eventListeners(WTF::makeAtomString(eventType)); + for (auto& listener : listeners) { + auto* function = listener->callback().jsFunction(); + if (function) { + values.append(function); + } + } + + return JSValue::encode(constructArray(globalObject, static_cast(nullptr), values)); +} + +} \ No newline at end of file diff --git a/src/bun.js/bindings/webcore/JSEventTargetNode.h b/src/bun.js/bindings/webcore/JSEventTargetNode.h new file mode 100644 index 0000000000000..c404c1e908753 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSEventTargetNode.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Bun { + +JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeEventsGetEventListeners); + +} \ No newline at end of file diff --git a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp index 0d163a0c28341..f0b1cc7f5ae5e 100644 --- a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp +++ b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp @@ -40,6 +40,7 @@ #include "JSDOMIterator.h" #include "JSDOMOperation.h" #include "JSDOMWrapperCache.h" +#include "JavaScriptCore/JSCJSValue.h" #include "ScriptExecutionContext.h" #include "WebCoreJSClientData.h" #include @@ -251,7 +252,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_getAll, (JSGlobalObject return JSValue::encode(array); } -JSC_DEFINE_HOST_FUNCTION(fetchHeadersGetSetCookie, (JSC::JSGlobalObject * lexicalGlobalObject, VM& vm, WebCore::FetchHeaders* impl)) +JSC::EncodedJSValue fetchHeadersGetSetCookie(JSC::JSGlobalObject* lexicalGlobalObject, VM& vm, WebCore::FetchHeaders* impl) { auto scope = DECLARE_THROW_SCOPE(vm); @@ -598,7 +599,7 @@ void JSFetchHeaders::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -703,18 +704,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj { if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7FetchHeaders@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore12FetchHeadersE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore12FetchHeadersE[2]; #endif // If you hit this assertion you either have a use after free bug, or // FetchHeaders has subclasses. If FetchHeaders has subclasses that get passed // to toJS() we currently require FetchHeaders you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSMessageChannel.cpp b/src/bun.js/bindings/webcore/JSMessageChannel.cpp index fec8265084c00..a5c8ff719702a 100644 --- a/src/bun.js/bindings/webcore/JSMessageChannel.cpp +++ b/src/bun.js/bindings/webcore/JSMessageChannel.cpp @@ -97,7 +97,7 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSMessageChannelDOMConst ASSERT(castedThis); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "MessageChannel"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "MessageChannel"_s); auto object = MessageChannel::create(*context); if constexpr (IsExceptionOr) RETURN_IF_EXCEPTION(throwScope, {}); @@ -250,7 +250,7 @@ void JSMessageChannel::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -287,18 +287,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7MessageChannel@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore14MessageChannelE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore14MessageChannelE[2]; #endif // If you hit this assertion you either have a use after free bug, or // MessageChannel has subclasses. If MessageChannel has subclasses that get passed // to toJS() we currently require MessageChannel you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSMessageEvent.cpp b/src/bun.js/bindings/webcore/JSMessageEvent.cpp index 7d34c32ddded4..caf32b36ac542 100644 --- a/src/bun.js/bindings/webcore/JSMessageEvent.cpp +++ b/src/bun.js/bindings/webcore/JSMessageEvent.cpp @@ -470,7 +470,7 @@ void JSMessageEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -492,18 +492,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7MessageEvent@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore12MessageEventE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore12MessageEventE[2]; #endif // If you hit this assertion you either have a use after free bug, or // MessageEvent has subclasses. If MessageEvent has subclasses that get passed // to toJS() we currently require MessageEvent you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSMessagePort.cpp b/src/bun.js/bindings/webcore/JSMessagePort.cpp index 5c4845d4ce658..576baae759834 100644 --- a/src/bun.js/bindings/webcore/JSMessagePort.cpp +++ b/src/bun.js/bindings/webcore/JSMessagePort.cpp @@ -419,7 +419,7 @@ void JSMessagePort::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -464,18 +464,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7MessagePort@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore11MessagePortE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore11MessagePortE[2]; #endif // If you hit this assertion you either have a use after free bug, or // MessagePort has subclasses. If MessagePort has subclasses that get passed // to toJS() we currently require MessagePort you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSPerformance.cpp b/src/bun.js/bindings/webcore/JSPerformance.cpp index 4029dccae343a..fb301cd730e37 100644 --- a/src/bun.js/bindings/webcore/JSPerformance.cpp +++ b/src/bun.js/bindings/webcore/JSPerformance.cpp @@ -113,11 +113,10 @@ static JSC_DECLARE_HOST_FUNCTION(functionPerformanceNow); static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(functionPerformanceNowWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject*, JSPerformance*)); } -static inline JSC::EncodedJSValue functionPerformanceNowBody(JSGlobalObject* globalObject) +static inline JSC::EncodedJSValue functionPerformanceNowBody(VM& vm) { - auto* global = reinterpret_cast(globalObject); // nanoseconds to seconds - double time = static_cast(Bun__readOriginTimer(global->bunVM())); + double time = static_cast(Bun__readOriginTimer(Bun::vm(vm))); double result = time / 1000000.0; // https://github.com/oven-sh/bun/issues/5604 @@ -126,7 +125,7 @@ static inline JSC::EncodedJSValue functionPerformanceNowBody(JSGlobalObject* glo JSC_DEFINE_HOST_FUNCTION(functionPerformanceNow, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { - return functionPerformanceNowBody(globalObject); + return functionPerformanceNowBody(globalObject->vm()); } JSC_DEFINE_JIT_OPERATION(functionPerformanceNowWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSPerformance* castedThis)) @@ -136,7 +135,7 @@ JSC_DEFINE_JIT_OPERATION(functionPerformanceNowWithoutTypeCheck, JSC::EncodedJSV CallFrame* callFrame = DECLARE_CALL_FRAME(vm); IGNORE_WARNINGS_END JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return functionPerformanceNowBody(lexicalGlobalObject); + return { functionPerformanceNowBody(vm) }; } // -- end copied -- @@ -601,7 +600,7 @@ void JSPerformance::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -641,18 +640,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7Performance@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore11PerformanceE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore11PerformanceE[2]; #endif // If you hit this assertion you either have a use after free bug, or // Performance has subclasses. If Performance has subclasses that get passed // to toJS() we currently require Performance you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSPerformanceEntry.cpp b/src/bun.js/bindings/webcore/JSPerformanceEntry.cpp index 428a42fe8305f..924cc6f7fb87e 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceEntry.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceEntry.cpp @@ -235,7 +235,7 @@ static inline EncodedJSValue jsPerformanceEntryPrototypeFunction_toJSONBody(JSGl auto* result = constructEmptyObject(lexicalGlobalObject); auto nameValue = toJS(*lexicalGlobalObject, throwScope, impl.name()); RETURN_IF_EXCEPTION(throwScope, {}); - result->putDirect(vm, Identifier::fromString(vm, "name"_s), nameValue); + result->putDirect(vm, vm.propertyNames->name, nameValue); auto entryTypeValue = toJS(*lexicalGlobalObject, throwScope, impl.entryType()); RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, Identifier::fromString(vm, "entryType"_s), entryTypeValue); @@ -268,7 +268,7 @@ void JSPerformanceEntry::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/JSPerformanceMark.cpp b/src/bun.js/bindings/webcore/JSPerformanceMark.cpp index 0976a04e8bf2c..b0a193269c24d 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceMark.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceMark.cpp @@ -46,7 +46,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -99,7 +98,7 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSPerformanceMarkDOMConstruct return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "PerformanceMark"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "PerformanceMark"_s); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); auto markName = convert(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -108,13 +107,13 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSPerformanceMarkDOMConstruct RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); auto object = PerformanceMark::create(*castedThis->globalObject(), *context, WTFMove(markName), WTFMove(markOptions)); if constexpr (IsExceptionOr) - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); static_assert(TypeOrExceptionOrUnderlyingType::isRef); auto jsValue = toJSNewlyCreated>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object)); if constexpr (IsExceptionOr) - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); setSubclassStructureIfNeeded(lexicalGlobalObject, callFrame, asObject(jsValue)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); return JSValue::encode(jsValue); } JSC_ANNOTATE_HOST_FUNCTION(JSPerformanceMarkDOMConstructorConstruct, JSPerformanceMarkDOMConstructor::construct); @@ -137,8 +136,7 @@ template<> void JSPerformanceMarkDOMConstructor::initializeProperties(VM& vm, JS /* Hash table for prototype */ -static const HashTableValue JSPerformanceMarkPrototypeTableValues[] = -{ +static const HashTableValue JSPerformanceMarkPrototypeTableValues[] = { { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsPerformanceMarkConstructor, 0 } }, { "detail"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsPerformanceMark_detail, 0 } }, }; @@ -178,7 +176,7 @@ JSValue JSPerformanceMark::getConstructor(VM& vm, const JSGlobalObject* globalOb return getDOMConstructor(vm, *jsCast(globalObject)); } -JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMarkConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMarkConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) { VM& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -196,24 +194,24 @@ static inline JSValue jsPerformanceMark_detailGetter(JSGlobalObject& lexicalGlob return cachedValue; auto& impl = thisObject.wrapped(); JSValue result = toJS(lexicalGlobalObject, throwScope, impl.detail(*jsCast(&lexicalGlobalObject))); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); thisObject.m_detail.set(JSC::getVM(&lexicalGlobalObject), &thisObject, result); return result; } -JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMark_detail, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMark_detail, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::get(*lexicalGlobalObject, thisValue, attributeName); } JSC::GCClient::IsoSubspace* JSPerformanceMark::subspaceForImpl(JSC::VM& vm) { - return WebCore::subspaceForImpl(vm, - [] (auto& spaces) { return spaces.m_clientSubspaceForPerformanceMark.get(); }, - [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForPerformanceMark = std::forward(space); }, - [] (auto& spaces) { return spaces.m_subspaceForPerformanceMark.get(); }, - [] (auto& spaces, auto&& space) { spaces.m_subspaceForPerformanceMark = std::forward(space); } - ); + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForPerformanceMark.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForPerformanceMark = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForPerformanceMark.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForPerformanceMark = std::forward(space); }); } template @@ -232,16 +230,20 @@ void JSPerformanceMark::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } #if ENABLE(BINDING_INTEGRITY) #if PLATFORM(WIN) -#pragma warning(disable: 4483) -extern "C" { extern void (*const __identifier("??_7PerformanceMark@WebCore@@6B@")[])(); } +#pragma warning(disable : 4483) +extern "C" { +extern void (*const __identifier("??_7PerformanceMark@WebCore@@6B@")[])(); +} #else -extern "C" { extern void* _ZTVN7WebCore15PerformanceMarkE[]; } +extern "C" { +extern void* _ZTVN7WebCore15PerformanceMarkE[]; +} #endif #endif @@ -250,18 +252,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7PerformanceMark@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore15PerformanceMarkE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore15PerformanceMarkE[2]; #endif // If you hit this assertion you either have a use after free bug, or // PerformanceMark has subclasses. If PerformanceMark has subclasses that get passed // to toJS() we currently require PerformanceMark you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); @@ -272,5 +274,4 @@ JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* g return wrap(lexicalGlobalObject, globalObject, impl); } - } diff --git a/src/bun.js/bindings/webcore/JSPerformanceMark.h b/src/bun.js/bindings/webcore/JSPerformanceMark.h index dd838da31c389..00c92883eff86 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceMark.h +++ b/src/bun.js/bindings/webcore/JSPerformanceMark.h @@ -63,6 +63,7 @@ class JSPerformanceMark : public JSPerformanceEntry { { return static_cast(Base::wrapped()); } + protected: JSPerformanceMark(JSC::Structure*, JSDOMGlobalObject&, Ref&&); diff --git a/src/bun.js/bindings/webcore/JSPerformanceMarkOptions.cpp b/src/bun.js/bindings/webcore/JSPerformanceMarkOptions.cpp index e638624f012a8..1a7ff1fbe3fa1 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceMarkOptions.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceMarkOptions.cpp @@ -25,7 +25,6 @@ #include "JSDOMConvertNumbers.h" #include - namespace WebCore { using namespace JSC; @@ -37,7 +36,7 @@ template<> PerformanceMarkOptions convertDictionary(JSGl auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } PerformanceMarkOptions result; JSValue detailValue; @@ -45,11 +44,11 @@ template<> PerformanceMarkOptions convertDictionary(JSGl detailValue = jsUndefined(); else { detailValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "detail"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!detailValue.isUndefined()) { result.detail = convert(lexicalGlobalObject, detailValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else result.detail = jsUndefined(); JSValue startTimeValue; @@ -57,11 +56,11 @@ template<> PerformanceMarkOptions convertDictionary(JSGl startTimeValue = jsUndefined(); else { startTimeValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "startTime"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!startTimeValue.isUndefined()) { result.startTime = convert(lexicalGlobalObject, startTimeValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } diff --git a/src/bun.js/bindings/webcore/JSPerformanceMeasure.cpp b/src/bun.js/bindings/webcore/JSPerformanceMeasure.cpp index b495cc503757a..824e3a3ac2225 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceMeasure.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceMeasure.cpp @@ -42,7 +42,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -103,8 +102,7 @@ template<> void JSPerformanceMeasureDOMConstructor::initializeProperties(VM& vm, /* Hash table for prototype */ -static const HashTableValue JSPerformanceMeasurePrototypeTableValues[] = -{ +static const HashTableValue JSPerformanceMeasurePrototypeTableValues[] = { { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsPerformanceMeasureConstructor, 0 } }, { "detail"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsPerformanceMeasure_detail, 0 } }, }; @@ -144,7 +142,7 @@ JSValue JSPerformanceMeasure::getConstructor(VM& vm, const JSGlobalObject* globa return getDOMConstructor(vm, *jsCast(globalObject)); } -JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMeasureConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMeasureConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) { VM& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -162,24 +160,24 @@ static inline JSValue jsPerformanceMeasure_detailGetter(JSGlobalObject& lexicalG return cachedValue; auto& impl = thisObject.wrapped(); JSValue result = toJS(lexicalGlobalObject, throwScope, impl.detail(*jsCast(&lexicalGlobalObject))); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); thisObject.m_detail.set(JSC::getVM(&lexicalGlobalObject), &thisObject, result); return result; } -JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMeasure_detail, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsPerformanceMeasure_detail, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::get(*lexicalGlobalObject, thisValue, attributeName); } JSC::GCClient::IsoSubspace* JSPerformanceMeasure::subspaceForImpl(JSC::VM& vm) { - return WebCore::subspaceForImpl(vm, - [] (auto& spaces) { return spaces.m_clientSubspaceForPerformanceMeasure.get(); }, - [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForPerformanceMeasure = std::forward(space); }, - [] (auto& spaces) { return spaces.m_subspaceForPerformanceMeasure.get(); }, - [] (auto& spaces, auto&& space) { spaces.m_subspaceForPerformanceMeasure = std::forward(space); } - ); + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForPerformanceMeasure.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForPerformanceMeasure = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForPerformanceMeasure.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForPerformanceMeasure = std::forward(space); }); } template @@ -198,9 +196,8 @@ void JSPerformanceMeasure::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } - } diff --git a/src/bun.js/bindings/webcore/JSPerformanceMeasure.h b/src/bun.js/bindings/webcore/JSPerformanceMeasure.h index 6f31644b014ff..15134c1891d2b 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceMeasure.h +++ b/src/bun.js/bindings/webcore/JSPerformanceMeasure.h @@ -63,13 +63,13 @@ class JSPerformanceMeasure : public JSPerformanceEntry { { return static_cast(Base::wrapped()); } + protected: JSPerformanceMeasure(JSC::Structure*, JSDOMGlobalObject&, Ref&&); DECLARE_DEFAULT_FINISH_CREATION; }; - template<> struct JSDOMWrapperConverterTraits { using WrapperClass = JSPerformanceMeasure; using ToWrappedReturnType = PerformanceMeasure*; diff --git a/src/bun.js/bindings/webcore/JSPerformanceMeasureOptions.cpp b/src/bun.js/bindings/webcore/JSPerformanceMeasureOptions.cpp index cb5b4d6c25adb..8eaa6dd9cbc94 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceMeasureOptions.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceMeasureOptions.cpp @@ -28,7 +28,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -40,7 +39,7 @@ template<> PerformanceMeasureOptions convertDictionary PerformanceMeasureOptions convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "detail"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!detailValue.isUndefined()) { result.detail = convert(lexicalGlobalObject, detailValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else result.detail = jsUndefined(); JSValue durationValue; @@ -60,33 +59,33 @@ template<> PerformanceMeasureOptions convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "duration"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!durationValue.isUndefined()) { result.duration = convert(lexicalGlobalObject, durationValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } JSValue endValue; if (isNullOrUndefined) endValue = jsUndefined(); else { endValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "end"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!endValue.isUndefined()) { result.end = convert>(lexicalGlobalObject, endValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } JSValue startValue; if (isNullOrUndefined) startValue = jsUndefined(); else { startValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "start"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!startValue.isUndefined()) { result.start = convert>(lexicalGlobalObject, startValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } diff --git a/src/bun.js/bindings/webcore/JSPerformanceObserver.cpp b/src/bun.js/bindings/webcore/JSPerformanceObserver.cpp index d266ff1e5033b..a9ce7b6c8ef03 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceObserver.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceObserver.cpp @@ -54,7 +54,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -66,7 +65,7 @@ template<> PerformanceObserver::Init convertDictionary PerformanceObserver::Init convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "buffered"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!bufferedValue.isUndefined()) { result.buffered = convert(lexicalGlobalObject, bufferedValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else result.buffered = false; JSValue entryTypesValue; @@ -86,22 +85,22 @@ template<> PerformanceObserver::Init convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "entryTypes"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!entryTypesValue.isUndefined()) { result.entryTypes = convert>(lexicalGlobalObject, entryTypesValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } JSValue typeValue; if (isNullOrUndefined) typeValue = jsUndefined(); else { - typeValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "type"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + typeValue = object->get(&lexicalGlobalObject, vm.propertyNames->type); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!typeValue.isUndefined()) { result.type = convert(lexicalGlobalObject, typeValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } @@ -153,8 +152,7 @@ using JSPerformanceObserverDOMConstructor = JSDOMConstructor(JSC::PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::GetterSetterType, jsPerformanceObserverConstructor_supportedEntryTypes, 0 } }, }; @@ -168,26 +166,25 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSPerformanceObserverDOMConst return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "PerformanceObserver"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "PerformanceObserver"_s); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); auto callback = convert>( - *lexicalGlobalObject, - argument0.value(), - *castedThis->globalObject(), - [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { - throwArgumentMustBeFunctionError(lexicalGlobalObject, scope, 0, "callback", "PerformanceObserver", nullptr); - } - ); + *lexicalGlobalObject, + argument0.value(), + *castedThis->globalObject(), + [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { + throwArgumentMustBeFunctionError(lexicalGlobalObject, scope, 0, "callback"_s, "PerformanceObserver"_s, nullptr); + }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); auto object = PerformanceObserver::create(*context, callback.releaseNonNull()); if constexpr (IsExceptionOr) - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); static_assert(TypeOrExceptionOrUnderlyingType::isRef); auto jsValue = toJSNewlyCreated>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object)); if constexpr (IsExceptionOr) - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); setSubclassStructureIfNeeded(lexicalGlobalObject, callFrame, asObject(jsValue)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); return JSValue::encode(jsValue); } JSC_ANNOTATE_HOST_FUNCTION(JSPerformanceObserverDOMConstructorConstruct, JSPerformanceObserverDOMConstructor::construct); @@ -212,8 +209,7 @@ template<> void JSPerformanceObserverDOMConstructor::initializeProperties(VM& vm /* Hash table for prototype */ -static const HashTableValue JSPerformanceObserverPrototypeTableValues[] = -{ +static const HashTableValue JSPerformanceObserverPrototypeTableValues[] = { { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsPerformanceObserverConstructor, 0 } }, { "observe"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsPerformanceObserverPrototypeFunction_observe, 0 } }, { "disconnect"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsPerformanceObserverPrototypeFunction_disconnect, 0 } }, @@ -259,7 +255,7 @@ void JSPerformanceObserver::destroy(JSC::JSCell* cell) thisObject->JSPerformanceObserver::~JSPerformanceObserver(); } -JSC_DEFINE_CUSTOM_GETTER(jsPerformanceObserverConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +JSC_DEFINE_CUSTOM_GETTER(jsPerformanceObserverConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) { VM& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -279,7 +275,7 @@ static inline JSValue jsPerformanceObserverConstructor_supportedEntryTypesGetter RELEASE_AND_RETURN(throwScope, (toJS>(lexicalGlobalObject, *jsCast(&lexicalGlobalObject), throwScope, PerformanceObserver::supportedEntryTypes(*context)))); } -JSC_DEFINE_CUSTOM_GETTER(jsPerformanceObserverConstructor_supportedEntryTypes, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsPerformanceObserverConstructor_supportedEntryTypes, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::getStatic(*lexicalGlobalObject, thisValue, attributeName); } @@ -297,7 +293,7 @@ static inline JSC::EncodedJSValue jsPerformanceObserverPrototypeFunction_observe RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.observe(WTFMove(options)); }))); } -JSC_DEFINE_HOST_FUNCTION(jsPerformanceObserverPrototypeFunction_observe, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(jsPerformanceObserverPrototypeFunction_observe, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { return IDLOperation::call(*lexicalGlobalObject, *callFrame, "observe"); } @@ -312,7 +308,7 @@ static inline JSC::EncodedJSValue jsPerformanceObserverPrototypeFunction_disconn RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.disconnect(); }))); } -JSC_DEFINE_HOST_FUNCTION(jsPerformanceObserverPrototypeFunction_disconnect, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(jsPerformanceObserverPrototypeFunction_disconnect, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { return IDLOperation::call(*lexicalGlobalObject, *callFrame, "disconnect"); } @@ -327,19 +323,19 @@ static inline JSC::EncodedJSValue jsPerformanceObserverPrototypeFunction_takeRec RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, impl.takeRecords()))); } -JSC_DEFINE_HOST_FUNCTION(jsPerformanceObserverPrototypeFunction_takeRecords, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(jsPerformanceObserverPrototypeFunction_takeRecords, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { return IDLOperation::call(*lexicalGlobalObject, *callFrame, "takeRecords"); } JSC::GCClient::IsoSubspace* JSPerformanceObserver::subspaceForImpl(JSC::VM& vm) { - return WebCore::subspaceForImpl(vm, - [] (auto& spaces) { return spaces.m_clientSubspaceForPerformanceObserver.get(); }, - [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForPerformanceObserver = std::forward(space); }, - [] (auto& spaces) { return spaces.m_subspaceForPerformanceObserver.get(); }, - [] (auto& spaces, auto&& space) { spaces.m_subspaceForPerformanceObserver = std::forward(space); } - ); + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForPerformanceObserver.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForPerformanceObserver = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForPerformanceObserver.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForPerformanceObserver = std::forward(space); }); } template @@ -369,7 +365,7 @@ void JSPerformanceObserver::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -382,10 +378,14 @@ void JSPerformanceObserverOwner::finalize(JSC::Handle handle, void #if ENABLE(BINDING_INTEGRITY) #if PLATFORM(WIN) -#pragma warning(disable: 4483) -extern "C" { extern void (*const __identifier("??_7PerformanceObserver@WebCore@@6B@")[])(); } +#pragma warning(disable : 4483) +extern "C" { +extern void (*const __identifier("??_7PerformanceObserver@WebCore@@6B@")[])(); +} #else -extern "C" { extern void* _ZTVN7WebCore19PerformanceObserverE[]; } +extern "C" { +extern void* _ZTVN7WebCore19PerformanceObserverE[]; +} #endif #endif @@ -394,18 +394,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7PerformanceObserver@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore19PerformanceObserverE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore19PerformanceObserverE[2]; #endif // If you hit this assertion you either have a use after free bug, or // PerformanceObserver has subclasses. If PerformanceObserver has subclasses that get passed // to toJS() we currently require PerformanceObserver you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSPerformanceObserverEntryList.cpp b/src/bun.js/bindings/webcore/JSPerformanceObserverEntryList.cpp index 69d9f998de439..80a4a0e24ceec 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceObserverEntryList.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceObserverEntryList.cpp @@ -245,7 +245,7 @@ void JSPerformanceObserverEntryList::analyzeHeap(JSCell* cell, HeapAnalyzer& ana auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -282,18 +282,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7PerformanceObserverEntryList@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore28PerformanceObserverEntryListE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore28PerformanceObserverEntryListE[2]; #endif // If you hit this assertion you either have a use after free bug, or // PerformanceObserverEntryList has subclasses. If PerformanceObserverEntryList has subclasses that get passed // to toJS() we currently require PerformanceObserverEntryList you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSPerformanceTiming.cpp b/src/bun.js/bindings/webcore/JSPerformanceTiming.cpp index 2e1c194ef98fe..6e5fcc303b898 100644 --- a/src/bun.js/bindings/webcore/JSPerformanceTiming.cpp +++ b/src/bun.js/bindings/webcore/JSPerformanceTiming.cpp @@ -573,7 +573,7 @@ void JSPerformanceTiming::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -610,18 +610,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7PerformanceTiming@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore17PerformanceTimingE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore17PerformanceTimingE[2]; #endif // If you hit this assertion you either have a use after free bug, or // PerformanceTiming has subclasses. If PerformanceTiming has subclasses that get passed // to toJS() we currently require PerformanceTiming you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSReadableByteStreamController.h b/src/bun.js/bindings/webcore/JSReadableByteStreamController.h index 6d512aaed66a8..6fbe0488f53e4 100644 --- a/src/bun.js/bindings/webcore/JSReadableByteStreamController.h +++ b/src/bun.js/bindings/webcore/JSReadableByteStreamController.h @@ -53,12 +53,11 @@ class JSReadableByteStreamController : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSReadableByteStreamController(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSReadableStream.cpp b/src/bun.js/bindings/webcore/JSReadableStream.cpp index e7e87202a4153..8f79cf3c799b2 100644 --- a/src/bun.js/bindings/webcore/JSReadableStream.cpp +++ b/src/bun.js/bindings/webcore/JSReadableStream.cpp @@ -39,6 +39,7 @@ #include #include #include "ZigGeneratedClasses.h" +#include "JavaScriptCore/BuiltinNames.h" namespace WebCore { using namespace JSC; @@ -132,6 +133,12 @@ static JSC_DEFINE_CUSTOM_SETTER(JSReadableStreamPrototype__nativePtrSetterWrap, static JSC_DEFINE_CUSTOM_GETTER(JSReadableStreamPrototype__nativePtrGetterWrap, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue encodedThisValue, JSC::PropertyName)) { JSReadableStream* thisObject = jsCast(JSValue::decode(encodedThisValue)); + + // Force it to be locked, even though the value is still really there. + if (thisObject->isNativeTypeTransferred()) { + return JSValue::encode(jsNumber(-1)); + } + return JSValue::encode(thisObject->nativePtr()); } @@ -172,7 +179,7 @@ void JSReadableStreamPrototype::finishCreation(VM& vm) reifyStaticProperties(vm, JSReadableStream::info(), JSReadableStreamPrototypeTableValues, *this); this->putDirectBuiltinFunction(vm, globalObject(), vm.propertyNames->asyncIteratorSymbol, readableStreamLazyAsyncIteratorCodeGenerator(vm), JSC::PropertyAttribute::DontDelete | 0); - this->putDirectBuiltinFunction(vm, globalObject(), JSC::Identifier::fromString(vm, "values"_s), readableStreamValuesCodeGenerator(vm), JSC::PropertyAttribute::DontDelete | 0); + this->putDirectBuiltinFunction(vm, globalObject(), vm.propertyNames->builtinNames().valuesPublicName(), readableStreamValuesCodeGenerator(vm), JSC::PropertyAttribute::DontDelete | 0); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } @@ -191,7 +198,6 @@ void JSReadableStream::finishCreation(VM& vm) void JSReadableStream::setNativePtr(JSC::VM& vm, JSC::JSValue value) { - this->m_nativePtr.set(vm, this, value); } diff --git a/src/bun.js/bindings/webcore/JSReadableStream.h b/src/bun.js/bindings/webcore/JSReadableStream.h index c1022315c56b8..45c14a9a7b6a2 100644 --- a/src/bun.js/bindings/webcore/JSReadableStream.h +++ b/src/bun.js/bindings/webcore/JSReadableStream.h @@ -57,6 +57,11 @@ class JSReadableStream : public JSDOMObject { int nativeType() const { return this->m_nativeType; } bool disturbed() const { return this->m_disturbed; } + bool isNativeTypeTransferred() const { return this->m_transferred; } + void setTransferred() + { + this->m_transferred = true; + } JSC::JSValue nativePtr() { return this->m_nativePtr.get(); @@ -80,6 +85,7 @@ class JSReadableStream : public JSDOMObject { mutable JSC::WriteBarrier m_nativePtr; int m_nativeType { 0 }; bool m_disturbed = false; + bool m_transferred = false; JSReadableStream(JSC::Structure*, JSDOMGlobalObject&); diff --git a/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.h b/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.h index 8d69390aede0f..b206a3beed126 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.h +++ b/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.h @@ -53,12 +53,11 @@ class JSReadableStreamBYOBReader : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSReadableStreamBYOBReader(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.h b/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.h index 39f18c2291ad1..94bb293b442f7 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.h +++ b/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.h @@ -53,12 +53,11 @@ class JSReadableStreamBYOBRequest : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSReadableStreamBYOBRequest(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.h b/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.h index a38c95daf9010..4279e712f1e58 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.h +++ b/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.h @@ -53,12 +53,11 @@ class JSReadableStreamDefaultController : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSReadableStreamDefaultController(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.h b/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.h index 61a4d13366048..4178cf2be986f 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.h +++ b/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.h @@ -53,12 +53,11 @@ class JSReadableStreamDefaultReader : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSReadableStreamDefaultReader(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSReadableStreamSink.cpp b/src/bun.js/bindings/webcore/JSReadableStreamSink.cpp index bd50b028d3246..26e726660cd24 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamSink.cpp +++ b/src/bun.js/bindings/webcore/JSReadableStreamSink.cpp @@ -206,7 +206,7 @@ void JSReadableStreamSink::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/JSReadableStreamSource.cpp b/src/bun.js/bindings/webcore/JSReadableStreamSource.cpp index 860e5a770a56b..ff6aa2c30f547 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamSource.cpp +++ b/src/bun.js/bindings/webcore/JSReadableStreamSource.cpp @@ -231,7 +231,7 @@ void JSReadableStreamSource::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/JSReadableStreamSourceCustom.cpp b/src/bun.js/bindings/webcore/JSReadableStreamSourceCustom.cpp index 476c0f8528d07..7082fd6f293c0 100644 --- a/src/bun.js/bindings/webcore/JSReadableStreamSourceCustom.cpp +++ b/src/bun.js/bindings/webcore/JSReadableStreamSourceCustom.cpp @@ -37,7 +37,7 @@ using namespace JSC; JSValue JSReadableStreamSource::start(JSGlobalObject& lexicalGlobalObject, CallFrame& callFrame, Ref&& promise) { VM& vm = lexicalGlobalObject.vm(); - + // FIXME: Why is it ok to ASSERT the argument count here? ASSERT(callFrame.argumentCount()); JSReadableStreamDefaultController* controller = jsDynamicCast(callFrame.uncheckedArgument(0)); diff --git a/src/bun.js/bindings/webcore/JSTextDecoderStream.cpp b/src/bun.js/bindings/webcore/JSTextDecoderStream.cpp new file mode 100644 index 0000000000000..d9890faa4e7b8 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSTextDecoderStream.cpp @@ -0,0 +1,171 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSTextDecoderStream.h" + +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMBuiltinConstructor.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +// #include "TextDecoderStreamBuiltins.h" +#include "WebCoreJSClientData.h" +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { +using namespace JSC; + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsTextDecoderStreamConstructor); + +class JSTextDecoderStreamPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSTextDecoderStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSTextDecoderStreamPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSTextDecoderStreamPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTextDecoderStreamPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSTextDecoderStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTextDecoderStreamPrototype, JSTextDecoderStreamPrototype::Base); + +using JSTextDecoderStreamDOMConstructor = JSDOMBuiltinConstructor; + +template<> const ClassInfo JSTextDecoderStreamDOMConstructor::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStreamDOMConstructor) }; + +template<> JSValue JSTextDecoderStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSTextDecoderStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "TextDecoderStream"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSTextDecoderStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +template<> FunctionExecutable* JSTextDecoderStreamDOMConstructor::initializeExecutable(VM& vm) +{ + return textDecoderStreamInitializeTextDecoderStreamCodeGenerator(vm); +} + +/* Hash table for prototype */ + +static const HashTableValue JSTextDecoderStreamPrototypeTableValues[] = { + { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsTextDecoderStreamConstructor, 0 } }, + { "encoding"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamEncodingCodeGenerator, 0 } }, + { "fatal"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamFatalCodeGenerator, 0 } }, + { "ignoreBOM"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamIgnoreBOMCodeGenerator, 0 } }, + { "readable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamReadableCodeGenerator, 0 } }, + { "writable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamWritableCodeGenerator, 0 } }, +}; + +const ClassInfo JSTextDecoderStreamPrototype::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStreamPrototype) }; + +void JSTextDecoderStreamPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSTextDecoderStream::info(), JSTextDecoderStreamPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSTextDecoderStream::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStream) }; + +JSTextDecoderStream::JSTextDecoderStream(Structure* structure, JSDOMGlobalObject& globalObject) + : JSDOMObject(structure, globalObject) +{ +} + +JSObject* JSTextDecoderStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSTextDecoderStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()); + structure->setMayBePrototype(true); + return JSTextDecoderStreamPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSTextDecoderStream::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype(vm, globalObject); +} + +JSValue JSTextDecoderStream::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor(vm, *jsCast(globalObject)); +} + +void JSTextDecoderStream::destroy(JSC::JSCell* cell) +{ + JSTextDecoderStream* thisObject = static_cast(cell); + thisObject->JSTextDecoderStream::~JSTextDecoderStream(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsTextDecoderStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSTextDecoderStream::getConstructor(vm, prototype->globalObject())); +} + +JSC::GCClient::IsoSubspace* JSTextDecoderStream::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl( + vm, [](auto& spaces) { return spaces.m_clientSubspaceForTextDecoderStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTextDecoderStream = std::forward(space); }, [](auto& spaces) { return spaces.m_subspaceForTextDecoderStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_subspaceForTextDecoderStream = std::forward(space); }); +} + +} diff --git a/src/bun.js/bindings/webcore/JSTextDecoderStream.h b/src/bun.js/bindings/webcore/JSTextDecoderStream.h new file mode 100644 index 0000000000000..eecb62bdc8a27 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSTextDecoderStream.h @@ -0,0 +1,64 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" + +namespace WebCore { + +class JSTextDecoderStream : public JSDOMObject { +public: + using Base = JSDOMObject; + static JSTextDecoderStream* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject) + { + auto& vm = globalObject->vm(); + JSTextDecoderStream* ptr = new (NotNull, JSC::allocateCell(vm)) JSTextDecoderStream(structure, *globalObject); + ptr->finishCreation(vm); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + +protected: + JSTextDecoderStream(JSC::Structure*, JSDOMGlobalObject&); + + DECLARE_DEFAULT_FINISH_CREATION; +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSTextEncoder.cpp b/src/bun.js/bindings/webcore/JSTextEncoder.cpp index d35e4637557f0..e03033b3d4bdb 100644 --- a/src/bun.js/bindings/webcore/JSTextEncoder.cpp +++ b/src/bun.js/bindings/webcore/JSTextEncoder.cpp @@ -73,10 +73,10 @@ extern "C" size_t TextEncoder__encodeInto8(const LChar* stringPtr, size_t string extern "C" size_t TextEncoder__encodeInto16(const UChar* stringPtr, size_t stringLen, void* ptr, size_t len); extern "C" JSC::EncodedJSValue TextEncoder__encodeRopeString(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSString* str); -extern "C" { -static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsTextEncoderEncodeWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject*, JSTextEncoder*, DOMJIT::IDLJSArgumentType)); -static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsTextEncoderPrototypeFunction_encodeIntoWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSTextEncoder* castedThis, DOMJIT::IDLJSArgumentType source, DOMJIT::IDLJSArgumentType destination)); -} +// extern "C" { +// static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsTextEncoderEncodeWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject*, JSTextEncoder*, DOMJIT::IDLJSArgumentType)); +// static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsTextEncoderPrototypeFunction_encodeIntoWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSTextEncoder* castedThis, DOMJIT::IDLJSArgumentType source, DOMJIT::IDLJSArgumentType destination)); +// } template<> TextEncoder::EncodeIntoResult convertDictionary(JSGlobalObject& lexicalGlobalObject, JSValue value) { @@ -214,90 +214,92 @@ template<> void JSTextEncoderDOMConstructor::initializeProperties(VM& vm, JSDOMG putDirect(vm, vm.propertyNames->prototype, JSTextEncoder::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); } -static const JSC::DOMJIT::Signature DOMJITSignatureForJSTextEncoderEncodeWithoutTypeCheck( - jsTextEncoderEncodeWithoutTypeCheck, - JSTextEncoder::info(), - // https://github.com/oven-sh/bun/issues/9226 - // It's not totally clear what the correct side effects are for this function, so we just make it conservative for now. - JSC::DOMJIT::Effect {}, - DOMJIT::IDLResultTypeFilter::value, - DOMJIT::IDLArgumentTypeFilter::value); - -static const JSC::DOMJIT::Signature DOMJITSignatureForJSTextEncoderEncodeIntoWithoutTypeCheck( - jsTextEncoderPrototypeFunction_encodeIntoWithoutTypeCheck, - JSTextEncoder::info(), - - JSC::DOMJIT::Effect {}, - // JSC::DOMJIT::Effect::forReadWriteKinds(encodeIntoRead, encodeIntoWrite), - DOMJIT::IDLResultTypeFilter::value, - DOMJIT::IDLArgumentTypeFilter::value, - DOMJIT::IDLArgumentTypeFilter::value); +// static const JSC::DOMJIT::Signature DOMJITSignatureForJSTextEncoderEncodeWithoutTypeCheck( +// jsTextEncoderEncodeWithoutTypeCheck, +// JSTextEncoder::info(), +// // https://github.com/oven-sh/bun/issues/9226 +// // It's not totally clear what the correct side effects are for this function, so we just make it conservative for now. +// JSC::DOMJIT::Effect {}, +// DOMJIT::IDLResultTypeFilter::value, +// DOMJIT::IDLArgumentTypeFilter::value); + +// static const JSC::DOMJIT::Signature DOMJITSignatureForJSTextEncoderEncodeIntoWithoutTypeCheck( +// jsTextEncoderPrototypeFunction_encodeIntoWithoutTypeCheck, +// JSTextEncoder::info(), + +// JSC::DOMJIT::Effect {}, +// // JSC::DOMJIT::Effect::forReadWriteKinds(encodeIntoRead, encodeIntoWrite), +// DOMJIT::IDLResultTypeFilter::value, +// DOMJIT::IDLArgumentTypeFilter::value, +// DOMJIT::IDLArgumentTypeFilter::value); /* Hash table for prototype */ static const HashTableValue JSTextEncoderPrototypeTableValues[] = { { "constructor"_s, static_cast(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsTextEncoderConstructor, 0 } }, { "encoding"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsTextEncoder_encoding, 0 } }, - { "encode"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DOMJITFunction), NoIntrinsic, { HashTableValue::DOMJITFunctionType, jsTextEncoderPrototypeFunction_encode, &DOMJITSignatureForJSTextEncoderEncodeWithoutTypeCheck } }, - { "encodeInto"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DOMJITFunction), NoIntrinsic, { HashTableValue::DOMJITFunctionType, jsTextEncoderPrototypeFunction_encodeInto, &DOMJITSignatureForJSTextEncoderEncodeIntoWithoutTypeCheck } }, + // { "encode"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DOMJITFunction), NoIntrinsic, { HashTableValue::DOMJITFunctionType, jsTextEncoderPrototypeFunction_encode, &DOMJITSignatureForJSTextEncoderEncodeWithoutTypeCheck } }, + // { "encodeInto"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DOMJITFunction), NoIntrinsic, { HashTableValue::DOMJITFunctionType, jsTextEncoderPrototypeFunction_encodeInto, &DOMJITSignatureForJSTextEncoderEncodeIntoWithoutTypeCheck } }, + { "encode"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsTextEncoderPrototypeFunction_encode, 1 } }, + { "encodeInto"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsTextEncoderPrototypeFunction_encodeInto, 2 } }, }; -JSC_DEFINE_JIT_OPERATION(jsTextEncoderEncodeWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSTextEncoder* castedThis, DOMJIT::IDLJSArgumentType input)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - auto throwScope = DECLARE_THROW_SCOPE(vm); - JSC::EncodedJSValue res; - String str; - if (input->is8Bit()) { - if (input->isRope()) { - GCDeferralContext gcDeferralContext(vm); - auto encodedValue = TextEncoder__encodeRopeString(lexicalGlobalObject, input); - if (!JSC::JSValue::decode(encodedValue).isUndefined()) { - RELEASE_AND_RETURN(throwScope, encodedValue); - } - } - - str = input->value(lexicalGlobalObject); - res = TextEncoder__encode8(lexicalGlobalObject, str.span8().data(), str.length()); - } else { - str = input->value(lexicalGlobalObject); - res = TextEncoder__encode16(lexicalGlobalObject, str.span16().data(), str.length()); - } - - if (UNLIKELY(JSC::JSValue::decode(res).isObject() && JSC::JSValue::decode(res).getObject()->isErrorInstance())) { - throwScope.throwException(lexicalGlobalObject, JSC::JSValue::decode(res)); - return encodedJSValue(); - } - - RELEASE_AND_RETURN(throwScope, res); -} - -JSC_DEFINE_JIT_OPERATION(jsTextEncoderPrototypeFunction_encodeIntoWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSTextEncoder* castedThis, DOMJIT::IDLJSArgumentType sourceStr, DOMJIT::IDLJSArgumentType destination)) -{ - VM& vm = JSC::getVM(lexicalGlobalObject); - IGNORE_WARNINGS_BEGIN("frame-address") - CallFrame* callFrame = DECLARE_CALL_FRAME(vm); - IGNORE_WARNINGS_END - JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - auto source = sourceStr->value(lexicalGlobalObject); - size_t res = 0; - if (!source.is8Bit()) { - res = TextEncoder__encodeInto16(source.span16().data(), source.length(), destination->vector(), destination->byteLength()); - } else { - res = TextEncoder__encodeInto8(source.span8().data(), source.length(), destination->vector(), destination->byteLength()); - } - - Zig::GlobalObject* globalObject = reinterpret_cast(lexicalGlobalObject); - auto* result = JSC::constructEmptyObject(vm, globalObject->encodeIntoObjectStructure()); - result->putDirectOffset(vm, 0, JSC::jsNumber(static_cast(res))); - result->putDirectOffset(vm, 1, JSC::jsNumber(static_cast(res >> 32))); - - return JSValue::encode(result); -} +// JSC_DEFINE_JIT_OPERATION(jsTextEncoderEncodeWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSTextEncoder* castedThis, DOMJIT::IDLJSArgumentType input)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// auto throwScope = DECLARE_THROW_SCOPE(vm); +// JSC::EncodedJSValue res; +// String str; +// if (input->is8Bit()) { +// if (input->isRope()) { +// GCDeferralContext gcDeferralContext(vm); +// auto encodedValue = TextEncoder__encodeRopeString(lexicalGlobalObject, input); +// if (!JSC::JSValue::decode(encodedValue).isUndefined()) { +// RELEASE_AND_RETURN(throwScope, { encodedValue }); +// } +// } + +// str = input->value(lexicalGlobalObject); +// res = TextEncoder__encode8(lexicalGlobalObject, str.span8().data(), str.length()); +// } else { +// str = input->value(lexicalGlobalObject); +// res = TextEncoder__encode16(lexicalGlobalObject, str.span16().data(), str.length()); +// } + +// if (UNLIKELY(JSC::JSValue::decode(res).isObject() && JSC::JSValue::decode(res).getObject()->isErrorInstance())) { +// throwScope.throwException(lexicalGlobalObject, JSC::JSValue::decode(res)); +// return { encodedJSValue() }; +// } + +// RELEASE_AND_RETURN(throwScope, { res }); +// } + +// JSC_DEFINE_JIT_OPERATION(jsTextEncoderPrototypeFunction_encodeIntoWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSTextEncoder* castedThis, DOMJIT::IDLJSArgumentType sourceStr, DOMJIT::IDLJSArgumentType destination)) +// { +// VM& vm = JSC::getVM(lexicalGlobalObject); +// IGNORE_WARNINGS_BEGIN("frame-address") +// CallFrame* callFrame = DECLARE_CALL_FRAME(vm); +// IGNORE_WARNINGS_END +// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); +// String source = sourceStr->value(lexicalGlobalObject); +// size_t res = 0; +// if (!source.is8Bit()) { +// res = TextEncoder__encodeInto16(source.span16().data(), source.length(), destination->vector(), destination->byteLength()); +// } else { +// res = TextEncoder__encodeInto8(source.span8().data(), source.length(), destination->vector(), destination->byteLength()); +// } + +// Bun::GlobalScope* globalScope = reinterpret_cast(lexicalGlobalObject); +// auto* result = JSC::constructEmptyObject(vm, globalScope->encodeIntoObjectStructure()); +// result->putDirectOffset(vm, 0, JSC::jsNumber(static_cast(res))); +// result->putDirectOffset(vm, 1, JSC::jsNumber(static_cast(res >> 32))); + +// return { JSValue::encode(result) }; +// } const ClassInfo JSTextEncoderPrototype::s_info = { "TextEncoder"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextEncoderPrototype) }; @@ -433,9 +435,8 @@ static inline JSC::EncodedJSValue jsTextEncoderPrototypeFunction_encodeIntoBody( res = TextEncoder__encodeInto8(span.data(), span.size(), destination->vector(), destination->byteLength()); } - Zig::GlobalObject* globalObject = reinterpret_cast(lexicalGlobalObject); - - auto* result = JSC::constructEmptyObject(vm, globalObject->encodeIntoObjectStructure()); + Bun::GlobalScope* globalScope = reinterpret_cast(lexicalGlobalObject); + auto* result = JSC::constructEmptyObject(vm, globalScope->encodeIntoObjectStructure()); result->putDirectOffset(vm, 0, JSC::jsNumber(static_cast(res))); result->putDirectOffset(vm, 1, JSC::jsNumber(static_cast(res >> 32))); @@ -462,7 +463,7 @@ void JSTextEncoder::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -499,18 +500,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7TextEncoder@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore11TextEncoderE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore11TextEncoderE[2]; #endif // If you hit this assertion you either have a use after free bug, or // TextEncoder has subclasses. If TextEncoder has subclasses that get passed // to toJS() we currently require TextEncoder you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSTextEncoderStream.cpp b/src/bun.js/bindings/webcore/JSTextEncoderStream.cpp new file mode 100644 index 0000000000000..caed2ea0767d6 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSTextEncoderStream.cpp @@ -0,0 +1,169 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSTextEncoderStream.h" + +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMBuiltinConstructor.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +// #include "TextEncoderStreamBuiltins.h" +#include "WebCoreJSClientData.h" +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { +using namespace JSC; + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsTextEncoderStreamConstructor); + +class JSTextEncoderStreamPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSTextEncoderStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSTextEncoderStreamPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSTextEncoderStreamPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTextEncoderStreamPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSTextEncoderStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTextEncoderStreamPrototype, JSTextEncoderStreamPrototype::Base); + +using JSTextEncoderStreamDOMConstructor = JSDOMBuiltinConstructor; + +template<> const ClassInfo JSTextEncoderStreamDOMConstructor::s_info = { "TextEncoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextEncoderStreamDOMConstructor) }; + +template<> JSValue JSTextEncoderStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSTextEncoderStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "TextEncoderStream"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSTextEncoderStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +template<> FunctionExecutable* JSTextEncoderStreamDOMConstructor::initializeExecutable(VM& vm) +{ + return textEncoderStreamInitializeTextEncoderStreamCodeGenerator(vm); +} + +/* Hash table for prototype */ + +static const HashTableValue JSTextEncoderStreamPrototypeTableValues[] = { + { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsTextEncoderStreamConstructor, 0 } }, + { "encoding"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textEncoderStreamEncodingCodeGenerator, 0 } }, + { "readable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textEncoderStreamReadableCodeGenerator, 0 } }, + { "writable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textEncoderStreamWritableCodeGenerator, 0 } }, +}; + +const ClassInfo JSTextEncoderStreamPrototype::s_info = { "TextEncoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextEncoderStreamPrototype) }; + +void JSTextEncoderStreamPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSTextEncoderStream::info(), JSTextEncoderStreamPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSTextEncoderStream::s_info = { "TextEncoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextEncoderStream) }; + +JSTextEncoderStream::JSTextEncoderStream(Structure* structure, JSDOMGlobalObject& globalObject) + : JSDOMObject(structure, globalObject) +{ +} + +JSObject* JSTextEncoderStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSTextEncoderStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()); + structure->setMayBePrototype(true); + return JSTextEncoderStreamPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSTextEncoderStream::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype(vm, globalObject); +} + +JSValue JSTextEncoderStream::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor(vm, *jsCast(globalObject)); +} + +void JSTextEncoderStream::destroy(JSC::JSCell* cell) +{ + JSTextEncoderStream* thisObject = static_cast(cell); + thisObject->JSTextEncoderStream::~JSTextEncoderStream(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsTextEncoderStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSTextEncoderStream::getConstructor(vm, prototype->globalObject())); +} + +JSC::GCClient::IsoSubspace* JSTextEncoderStream::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl( + vm, [](auto& spaces) { return spaces.m_clientSubspaceForTextEncoderStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTextEncoderStream = std::forward(space); }, [](auto& spaces) { return spaces.m_subspaceForTextEncoderStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_subspaceForTextEncoderStream = std::forward(space); }); +} + +} diff --git a/src/bun.js/bindings/webcore/JSTextEncoderStream.h b/src/bun.js/bindings/webcore/JSTextEncoderStream.h new file mode 100644 index 0000000000000..4db3bbbbd022b --- /dev/null +++ b/src/bun.js/bindings/webcore/JSTextEncoderStream.h @@ -0,0 +1,64 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" + +namespace WebCore { + +class JSTextEncoderStream : public JSDOMObject { +public: + using Base = JSDOMObject; + static JSTextEncoderStream* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject) + { + auto& vm = globalObject->vm(); + JSTextEncoderStream* ptr = new (NotNull, JSC::allocateCell(vm)) JSTextEncoderStream(structure, *globalObject); + ptr->finishCreation(vm); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + +protected: + JSTextEncoderStream(JSC::Structure*, JSDOMGlobalObject&); + + DECLARE_DEFAULT_FINISH_CREATION; +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSTransformStream.h b/src/bun.js/bindings/webcore/JSTransformStream.h index 26646df0304f2..a68e7da4761d1 100644 --- a/src/bun.js/bindings/webcore/JSTransformStream.h +++ b/src/bun.js/bindings/webcore/JSTransformStream.h @@ -53,12 +53,11 @@ class JSTransformStream : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSTransformStream(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.h b/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.h index e63fdd7c41391..9fe4c0568fe37 100644 --- a/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.h +++ b/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.h @@ -53,12 +53,11 @@ class JSTransformStreamDefaultController : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSTransformStreamDefaultController(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSURLSearchParams.cpp b/src/bun.js/bindings/webcore/JSURLSearchParams.cpp index 28ce146097aa1..5189a2cfabfe8 100644 --- a/src/bun.js/bindings/webcore/JSURLSearchParams.cpp +++ b/src/bun.js/bindings/webcore/JSURLSearchParams.cpp @@ -591,7 +591,7 @@ void JSURLSearchParams::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + // analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -628,18 +628,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj // if constexpr (std::is_polymorphic_v) { // #if ENABLE(BINDING_INTEGRITY) - // const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // // const void* actualVTablePointer = getVTablePointer(impl.ptr()); // #if PLATFORM(WIN) // void* expectedVTablePointer = __identifier("??_7URLSearchParams@WebCore@@6B@"); // #else - // void* expectedVTablePointer = &_ZTVN7WebCore15URLSearchParamsE[2]; + // // void* expectedVTablePointer = &_ZTVN7WebCore15URLSearchParamsE[2]; // #endif // // If you hit this assertion you either have a use after free bug, or // // URLSearchParams has subclasses. If URLSearchParams has subclasses that get passed // // to toJS() we currently require URLSearchParams you to opt out of binding hardening // // by adding the SkipVTableValidation attribute to the interface IDL definition - // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); // #endif // } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSWebSocket.cpp b/src/bun.js/bindings/webcore/JSWebSocket.cpp index fe5bd0edf9c80..8d9150036b909 100644 --- a/src/bun.js/bindings/webcore/JSWebSocket.cpp +++ b/src/bun.js/bindings/webcore/JSWebSocket.cpp @@ -151,7 +151,7 @@ static inline JSC::EncodedJSValue constructJSWebSocket1(JSGlobalObject* lexicalG ASSERT(castedThis); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket"_s); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); auto url = convert(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -178,7 +178,7 @@ static inline JSC::EncodedJSValue constructJSWebSocket2(JSGlobalObject* lexicalG ASSERT(castedThis); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket"_s); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); auto url = convert(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -204,7 +204,7 @@ static inline JSC::EncodedJSValue constructJSWebSocket3(JSGlobalObject* lexicalG auto* globalObject = jsCast(lexicalGlobalObject); auto* context = globalObject->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket"_s); auto url = convert(*lexicalGlobalObject, urlValue); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -602,7 +602,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send1Body(JSC::JS UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "send", "ArrayBuffer"); }); + auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "send"_s, "ArrayBuffer"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(*data); }))); } @@ -615,7 +615,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send2Body(JSC::JS UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "send", "ArrayBufferView"); }); + auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "send"_s, "ArrayBufferView"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(data.releaseNonNull()); }))); } @@ -628,7 +628,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send2Body(JSC::JS // UNUSED_PARAM(callFrame); // auto& impl = castedThis->wrapped(); // EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); -// auto data = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "send", "Blob"); }); +// auto data = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "send"_s, "Blob"_s); }); // RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); // RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(*data); }))); // } @@ -710,7 +710,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_ping2Body(JSC::JS UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "ping", "ArrayBuffer"); }); + auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "ping"_s, "ArrayBuffer"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.ping(*data); }))); } @@ -723,7 +723,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_ping3Body(JSC::JS UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "ping", "ArrayBufferView"); }); + auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "ping"_s, "ArrayBufferView"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.ping(data.releaseNonNull()); }))); } @@ -736,7 +736,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_ping3Body(JSC::JS // UNUSED_PARAM(callFrame); // auto& impl = castedThis->wrapped(); // EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); -// auto data = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "ping", "Blob"); }); +// auto data = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "ping"_s, "Blob"_s); }); // RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); // RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.ping(*data); }))); // } @@ -799,7 +799,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_pong2Body(JSC::JS UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "pong", "ArrayBuffer"); }); + auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "pong"_s, "ArrayBuffer"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.pong(*data); }))); } @@ -812,7 +812,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_pong3Body(JSC::JS UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "pong", "ArrayBufferView"); }); + auto data = convert(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "pong"_s, "ArrayBufferView"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.pong(data.releaseNonNull()); }))); } @@ -825,7 +825,7 @@ static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_pong3Body(JSC::JS // UNUSED_PARAM(callFrame); // auto& impl = castedThis->wrapped(); // EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); -// auto data = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "pong", "Blob"); }); +// auto data = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data"_s, "WebSocket"_s, "pong"_s, "Blob"_s); }); // RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); // RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.pong(*data); }))); // } @@ -900,7 +900,7 @@ void JSWebSocket::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -948,18 +948,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7WebSocket@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore9WebSocketE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore9WebSocketE[2]; #endif // If you hit this assertion you either have a use after free bug, or // WebSocket has subclasses. If WebSocket has subclasses that get passed // to toJS() we currently require WebSocket you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSWorker.cpp b/src/bun.js/bindings/webcore/JSWorker.cpp index c3e7ee16c21e8..fe7ed06872dfb 100644 --- a/src/bun.js/bindings/webcore/JSWorker.cpp +++ b/src/bun.js/bindings/webcore/JSWorker.cpp @@ -121,7 +121,7 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWorkerDOMConstructor:: return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); auto* context = castedThis->scriptExecutionContext(); if (UNLIKELY(!context)) - return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "Worker"); + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "Worker"_s); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); auto scriptUrl = convert(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -131,7 +131,7 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWorkerDOMConstructor:: options.bun.unref = false; if (JSObject* optionsObject = JSC::jsDynamicCast(argument1.value())) { - if (auto nameValue = optionsObject->getIfPropertyExists(lexicalGlobalObject, Identifier::fromString(vm, "name"_s))) { + if (auto nameValue = optionsObject->getIfPropertyExists(lexicalGlobalObject, vm.propertyNames->name)) { if (nameValue.isString()) { options.name = nameValue.toWTFString(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); @@ -574,7 +574,7 @@ void JSWorker::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -617,18 +617,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7Worker@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore6WorkerE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore6WorkerE[2]; #endif // If you hit this assertion you either have a use after free bug, or // Worker has subclasses. If Worker has subclasses that get passed // to toJS() we currently require Worker you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcore/JSWorkerOptions.cpp b/src/bun.js/bindings/webcore/JSWorkerOptions.cpp index 6bee7e290561c..8103d23053cf9 100644 --- a/src/bun.js/bindings/webcore/JSWorkerOptions.cpp +++ b/src/bun.js/bindings/webcore/JSWorkerOptions.cpp @@ -57,7 +57,7 @@ template<> WorkerOptions convertDictionary(JSGlobalObject& lexica if (isNullOrUndefined) nameValue = jsUndefined(); else { - nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { diff --git a/src/bun.js/bindings/webcore/JSWritableStream.cpp b/src/bun.js/bindings/webcore/JSWritableStream.cpp index 7950c895e979a..96fa7326e9299 100644 --- a/src/bun.js/bindings/webcore/JSWritableStream.cpp +++ b/src/bun.js/bindings/webcore/JSWritableStream.cpp @@ -274,7 +274,7 @@ void JSWritableStream::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.h b/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.h index 8da0be568ff1e..c695f439b9484 100644 --- a/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.h +++ b/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.h @@ -53,12 +53,11 @@ class JSWritableStreamDefaultController : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSWritableStreamDefaultController(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.h b/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.h index bc13fb8c1b3e7..3434df33a2e0b 100644 --- a/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.h +++ b/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.h @@ -53,12 +53,11 @@ class JSWritableStreamDefaultWriter : public JSDOMObject { return subspaceForImpl(vm); } static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + protected: JSWritableStreamDefaultWriter(JSC::Structure*, JSDOMGlobalObject&); void finishCreation(JSC::VM&); }; - - } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSWritableStreamSink.cpp b/src/bun.js/bindings/webcore/JSWritableStreamSink.cpp index d77165916de0e..617fa9b3f50b9 100644 --- a/src/bun.js/bindings/webcore/JSWritableStreamSink.cpp +++ b/src/bun.js/bindings/webcore/JSWritableStreamSink.cpp @@ -211,7 +211,7 @@ void JSWritableStreamSink::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcore/MessageChannel.cpp b/src/bun.js/bindings/webcore/MessageChannel.cpp index 67407b9c0417f..e8928a382873a 100644 --- a/src/bun.js/bindings/webcore/MessageChannel.cpp +++ b/src/bun.js/bindings/webcore/MessageChannel.cpp @@ -35,8 +35,8 @@ namespace WebCore { static std::pair, Ref> generateMessagePorts(ScriptExecutionContext& context) { - MessagePortIdentifier id1 = { ProcessIdent::identifier(), PortIdentifier::generate() }; - MessagePortIdentifier id2 = { ProcessIdent::identifier(), PortIdentifier::generate() }; + MessagePortIdentifier id1 = { WebCore::Process::identifier(), PortIdentifier::generate() }; + MessagePortIdentifier id2 = { WebCore::Process::identifier(), PortIdentifier::generate() }; return { MessagePort::create(context, id1, id2), MessagePort::create(context, id2, id1) }; } diff --git a/src/bun.js/bindings/webcore/MessagePort.cpp b/src/bun.js/bindings/webcore/MessagePort.cpp index a69728ad7febf..aa05ebbbbb11d 100644 --- a/src/bun.js/bindings/webcore/MessagePort.cpp +++ b/src/bun.js/bindings/webcore/MessagePort.cpp @@ -141,7 +141,7 @@ MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext, const M , m_identifier(local) , m_remoteIdentifier(remote) { - // LOG(MessagePorts, "Created MessagePort %s (%p) in process %" PRIu64, m_identifier.logString().utf8().data(), this, ProcessIdent::identifier().toUInt64()); + // LOG(MessagePorts, "Created MessagePort %s (%p) in process %" PRIu64, m_identifier.logString().utf8().data(), this, WebCore::Process::identifier().toUInt64()); Locker locker { allMessagePortsLock }; allMessagePorts().set(m_identifier, this); @@ -157,7 +157,7 @@ MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext, const M MessagePort::~MessagePort() { - // LOG(MessagePorts, "Destroyed MessagePort %s (%p) in process %" PRIu64, m_identifier.logString().utf8().data(), this, ProcessIdent::identifier().toUInt64()); + // LOG(MessagePorts, "Destroyed MessagePort %s (%p) in process %" PRIu64, m_identifier.logString().utf8().data(), this, WebCore::Process::identifier().toUInt64()); ASSERT(allMessagePortsLock.isLocked()); diff --git a/src/bun.js/bindings/webcore/MessagePortChannel.h b/src/bun.js/bindings/webcore/MessagePortChannel.h index ebe27fd5893be..d50105badb3cc 100644 --- a/src/bun.js/bindings/webcore/MessagePortChannel.h +++ b/src/bun.js/bindings/webcore/MessagePortChannel.h @@ -63,7 +63,7 @@ class MessagePortChannel : public RefCounted { #if !LOG_DISABLED String logString() const { - return makeString(m_ports[0].logString(), ":", m_ports[1].logString()); + return makeString(m_ports[0].logString(), ":"_s, m_ports[1].logString()); } #endif diff --git a/src/bun.js/bindings/webcore/MessagePortChannelProviderImpl.cpp b/src/bun.js/bindings/webcore/MessagePortChannelProviderImpl.cpp index 0a21936a30ed5..b569c0d18de23 100644 --- a/src/bun.js/bindings/webcore/MessagePortChannelProviderImpl.cpp +++ b/src/bun.js/bindings/webcore/MessagePortChannelProviderImpl.cpp @@ -48,7 +48,7 @@ void MessagePortChannelProviderImpl::createNewMessagePortChannel(const MessagePo void MessagePortChannelProviderImpl::entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) { - m_registry.didEntangleLocalToRemote(local, remote, ProcessIdent::identifier()); + m_registry.didEntangleLocalToRemote(local, remote, WebCore::Process::identifier()); } void MessagePortChannelProviderImpl::messagePortDisentangled(const MessagePortIdentifier& local) diff --git a/src/bun.js/bindings/webcore/MessagePortIdentifier.h b/src/bun.js/bindings/webcore/MessagePortIdentifier.h index eac8546de6ac2..d7c70b627c607 100644 --- a/src/bun.js/bindings/webcore/MessagePortIdentifier.h +++ b/src/bun.js/bindings/webcore/MessagePortIdentifier.h @@ -28,7 +28,7 @@ #include "PortIdentifier.h" #include "ProcessIdentifier.h" #include -#include +#include namespace WebCore { @@ -36,6 +36,8 @@ struct MessagePortIdentifier { ProcessIdentifier processIdentifier; PortIdentifier portIdentifier; + friend bool operator==(const MessagePortIdentifier&, const MessagePortIdentifier&) = default; + #if !LOG_DISABLED String logString() const; #endif @@ -46,11 +48,6 @@ inline void add(Hasher& hasher, const MessagePortIdentifier& identifier) add(hasher, identifier.processIdentifier, identifier.portIdentifier); } -inline bool operator==(const MessagePortIdentifier& a, const MessagePortIdentifier& b) -{ - return a.processIdentifier == b.processIdentifier && a.portIdentifier == b.portIdentifier; -} - #if !LOG_DISABLED inline String MessagePortIdentifier::logString() const @@ -78,6 +75,7 @@ template<> struct HashTraits : GenericHashTraits static bool isDeletedValue(const WebCore::MessagePortIdentifier& slot) { return slot.processIdentifier.isHashTableDeletedValue(); } }; -template<> struct DefaultHash : MessagePortIdentifierHash {}; +template<> struct DefaultHash : MessagePortIdentifierHash { +}; } // namespace WTF diff --git a/src/bun.js/bindings/webcore/Performance.cpp b/src/bun.js/bindings/webcore/Performance.cpp index 5961997b90dec..b1866eb7b7284 100644 --- a/src/bun.js/bindings/webcore/Performance.cpp +++ b/src/bun.js/bindings/webcore/Performance.cpp @@ -52,6 +52,7 @@ // #include "ResourceResponse.h" #include "ScriptExecutionContext.h" #include +#include "BunClientData.h" namespace WebCore { @@ -78,9 +79,7 @@ void Performance::contextDestroyed() DOMHighResTimeStamp Performance::now() const { - auto* globalObject = scriptExecutionContext()->globalObject(); - auto* bunVM = jsCast(globalObject)->bunVM(); - auto nowNano = Bun__readOriginTimer(bunVM); + auto nowNano = Bun__readOriginTimer(bunVM(scriptExecutionContext()->vm())); return static_cast(nowNano) / 1000000.0; } @@ -172,7 +171,7 @@ Vector> Performance::getEntriesByType(const String& ent // if (m_navigationTiming && entryType == "navigation"_s) // entries.append(m_navigationTiming); - + // if (entryType == "resource"_s) // entries.appendVector(m_resourceTimingBuffer); @@ -453,7 +452,7 @@ void Performance::scheduleTaskIfNeeded() return; m_hasScheduledTimingBufferDeliveryTask = true; - context->postTask([protectedThis = Ref { *this }, this] (ScriptExecutionContext& context) { + context->postTask([protectedThis = Ref { *this }, this](ScriptExecutionContext& context) { m_hasScheduledTimingBufferDeliveryTask = false; for (auto& observer : copyToVector(m_observers)) observer->deliver(); diff --git a/src/bun.js/bindings/webcore/Performance.h b/src/bun.js/bindings/webcore/Performance.h index 70fc7b212c294..ce3549af1fcbe 100644 --- a/src/bun.js/bindings/webcore/Performance.h +++ b/src/bun.js/bindings/webcore/Performance.h @@ -78,6 +78,7 @@ struct PerformanceMeasureOptions; class Performance final : public RefCounted, public ContextDestructionObserver, public EventTarget { WTF_MAKE_ISO_ALLOCATED(Performance); + public: static Ref create(ScriptExecutionContext* context, MonotonicTime timeOrigin) { return adoptRef(*new Performance(context, timeOrigin)); } ~Performance(); @@ -123,14 +124,13 @@ class Performance final : public RefCounted, public ContextDestruct ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); } - using RefCounted::ref; using RefCounted::deref; + using RefCounted::ref; // void scheduleNavigationObservationTaskIfNeeded(); // PerformanceNavigationTiming* navigationTiming() { return m_navigationTiming.get(); } - // EventTargetData* eventTargetData() override; // EventTargetData* eventTargetDataConcurrently() override; // EventTargetData& ensureEventTargetData() override; @@ -174,7 +174,6 @@ class Performance final : public RefCounted, public ContextDestruct ListHashSet> m_observers; - EventTargetData* eventTargetData() final { return &m_eventTargetData; } EventTargetData* eventTargetDataConcurrently() final { return &m_eventTargetData; } EventTargetData& ensureEventTargetData() final { return m_eventTargetData; } diff --git a/src/bun.js/bindings/webcore/PerformanceMeasure.cpp b/src/bun.js/bindings/webcore/PerformanceMeasure.cpp index 6c9e0fa62fade..5c24b08dea35f 100644 --- a/src/bun.js/bindings/webcore/PerformanceMeasure.cpp +++ b/src/bun.js/bindings/webcore/PerformanceMeasure.cpp @@ -48,7 +48,7 @@ JSC::JSValue PerformanceMeasure::detail(JSC::JSGlobalObject& globalObject) if (!m_serializedDetail) { return JSC::jsNull(); } - + return m_serializedDetail->deserialize(globalObject, &globalObject); } diff --git a/src/bun.js/bindings/webcore/PerformanceObserver.cpp b/src/bun.js/bindings/webcore/PerformanceObserver.cpp index 5aa7c7d8af514..75fa7d7b40d3a 100644 --- a/src/bun.js/bindings/webcore/PerformanceObserver.cpp +++ b/src/bun.js/bindings/webcore/PerformanceObserver.cpp @@ -48,7 +48,6 @@ PerformanceObserver::PerformanceObserver(ScriptExecutionContext& scriptExecution // } else // ASSERT_NOT_REACHED(); m_performance = jsCast(scriptExecutionContext.globalObject())->performance(); - } void PerformanceObserver::disassociate() @@ -74,7 +73,7 @@ ExceptionOr PerformanceObserver::observe(Init&& init) filter.add(*type); } if (filter.isEmpty()) - return { }; + return {}; m_typeFilter = filter; } else { if (!init.type) @@ -85,7 +84,7 @@ ExceptionOr PerformanceObserver::observe(Init&& init) if (auto type = PerformanceEntry::parseEntryTypeString(*init.type)) filter.add(*type); else - return { }; + return {}; if (init.buffered) { isBuffered = true; auto oldSize = m_entriesToDeliver.size(); @@ -106,12 +105,12 @@ ExceptionOr PerformanceObserver::observe(Init&& init) if (isBuffered) deliver(); - return { }; + return {}; } Vector> PerformanceObserver::takeRecords() { - return std::exchange(m_entriesToDeliver, { }); + return std::exchange(m_entriesToDeliver, {}); } void PerformanceObserver::disconnect() @@ -121,7 +120,7 @@ void PerformanceObserver::disconnect() m_registered = false; m_entriesToDeliver.clear(); - m_typeFilter = { }; + m_typeFilter = {}; } void PerformanceObserver::queueEntry(PerformanceEntry& entry) @@ -138,7 +137,7 @@ void PerformanceObserver::deliver() if (!context) return; - Vector> entries = std::exchange(m_entriesToDeliver, { }); + Vector> entries = std::exchange(m_entriesToDeliver, {}); auto list = PerformanceObserverEntryList::create(WTFMove(entries)); // InspectorInstrumentation::willFireObserverCallback(*context, "PerformanceObserver"_s); diff --git a/src/bun.js/bindings/webcore/PerformanceObserverEntryList.h b/src/bun.js/bindings/webcore/PerformanceObserverEntryList.h index 6d7658780d70f..61f615c888e1b 100644 --- a/src/bun.js/bindings/webcore/PerformanceObserverEntryList.h +++ b/src/bun.js/bindings/webcore/PerformanceObserverEntryList.h @@ -35,6 +35,7 @@ class PerformanceEntry; class PerformanceObserverEntryList : public RefCounted { WTF_MAKE_FAST_ALLOCATED; + public: static Ref create(Vector>&& entries); diff --git a/src/bun.js/bindings/webcore/PerformanceTiming.cpp b/src/bun.js/bindings/webcore/PerformanceTiming.cpp index 3e770a7617489..a4081d6338ac5 100644 --- a/src/bun.js/bindings/webcore/PerformanceTiming.cpp +++ b/src/bun.js/bindings/webcore/PerformanceTiming.cpp @@ -77,77 +77,77 @@ unsigned long long PerformanceTiming::fetchStart() const unsigned long long PerformanceTiming::domainLookupStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::domainLookupEnd() const { -return 0; + return 0; } unsigned long long PerformanceTiming::connectStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::connectEnd() const { -return 0; + return 0; } unsigned long long PerformanceTiming::secureConnectionStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::requestStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::responseStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::responseEnd() const { -return 0; + return 0; } unsigned long long PerformanceTiming::domLoading() const { -return 0; + return 0; } unsigned long long PerformanceTiming::domInteractive() const { -return 0; + return 0; } unsigned long long PerformanceTiming::domContentLoadedEventStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::domContentLoadedEventEnd() const { -return 0; + return 0; } unsigned long long PerformanceTiming::domComplete() const { -return 0; + return 0; } unsigned long long PerformanceTiming::loadEventStart() const { -return 0; + return 0; } unsigned long long PerformanceTiming::loadEventEnd() const { -return 0; + return 0; } unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(MonotonicTime timeStamp) const diff --git a/src/bun.js/bindings/webcore/PerformanceUserTiming.cpp b/src/bun.js/bindings/webcore/PerformanceUserTiming.cpp index a375085bc47d5..0ea5967ec1f4a 100644 --- a/src/bun.js/bindings/webcore/PerformanceUserTiming.cpp +++ b/src/bun.js/bindings/webcore/PerformanceUserTiming.cpp @@ -149,7 +149,7 @@ ExceptionOr PerformanceUserTiming::convertMarkToTimestamp(const String& if (iterator != m_marksMap.end()) return iterator->value.last()->startTime(); - return Exception { SyntaxError, makeString("No mark named '", mark, "' exists") }; + return Exception { SyntaxError, makeString("No mark named '"_s, mark, "' exists"_s) }; } ExceptionOr PerformanceUserTiming::convertMarkToTimestamp(double mark) const diff --git a/src/bun.js/bindings/webcore/PortIdentifier.h b/src/bun.js/bindings/webcore/PortIdentifier.h index 8b77e792ebc93..17e9516d02205 100644 --- a/src/bun.js/bindings/webcore/PortIdentifier.h +++ b/src/bun.js/bindings/webcore/PortIdentifier.h @@ -29,7 +29,7 @@ namespace WebCore { -enum PortIdentifierType {}; -using PortIdentifier = AtomicObjectIdentifier; +enum class PortIdentifierType {}; +using PortIdentifier = LegacyNullableAtomicObjectIdentifier; } diff --git a/src/bun.js/bindings/webcore/ReadableStream.cpp b/src/bun.js/bindings/webcore/ReadableStream.cpp index a6f22d0f55525..36828aaf156f4 100644 --- a/src/bun.js/bindings/webcore/ReadableStream.cpp +++ b/src/bun.js/bindings/webcore/ReadableStream.cpp @@ -240,4 +240,15 @@ bool ReadableStream::isDisturbed() const return readableStream()->disturbed(); } +JSC_DEFINE_HOST_FUNCTION(jsFunctionTransferToNativeReadableStream, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + auto* readableStream = jsDynamicCast(callFrame->argument(0)); + readableStream->setTransferred(); + readableStream->setDisturbed(true); + return JSValue::encode(jsUndefined()); +} + } diff --git a/src/bun.js/bindings/webcore/ReadableStream.h b/src/bun.js/bindings/webcore/ReadableStream.h index ce460e52c7e53..3bd9463918a3d 100644 --- a/src/bun.js/bindings/webcore/ReadableStream.h +++ b/src/bun.js/bindings/webcore/ReadableStream.h @@ -102,4 +102,6 @@ inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, R return stream->readableStream(); } +JSC_DECLARE_HOST_FUNCTION(jsFunctionTransferToNativeReadableStream); + } diff --git a/src/bun.js/bindings/webcore/RegisteredEventListener.h b/src/bun.js/bindings/webcore/RegisteredEventListener.h index 1b2f48af49802..a52890ef42067 100644 --- a/src/bun.js/bindings/webcore/RegisteredEventListener.h +++ b/src/bun.js/bindings/webcore/RegisteredEventListener.h @@ -36,7 +36,8 @@ class RegisteredEventListener : public RefCounted { : capture(capture) , passive(passive) , once(once) - { } + { + } bool capture; bool passive; diff --git a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp index 6d77a4cabc368..823f2a46edf35 100644 --- a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp +++ b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp @@ -250,6 +250,7 @@ enum ArrayBufferViewSubtag { Float64ArrayTag = 9, BigInt64ArrayTag = 10, BigUint64ArrayTag = 11, + Float16ArrayTag = 12, }; // static bool isTypeExposedToGlobalObject(JSC::JSGlobalObject& globalObject, SerializationTag tag) @@ -351,6 +352,7 @@ static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag) return 1; case Int16ArrayTag: case Uint16ArrayTag: + case Float16ArrayTag: return 2; case Int32ArrayTag: case Uint32ArrayTag: @@ -1289,6 +1291,8 @@ class CloneSerializer : CloneBase { write(Int32ArrayTag); else if (obj->inherits()) write(Uint32ArrayTag); + else if (obj->inherits()) + write(Float16ArrayTag); else if (obj->inherits()) write(Float32ArrayTag); else if (obj->inherits()) @@ -2259,6 +2263,10 @@ class CloneSerializer : CloneBase { case CryptoAlgorithmIdentifier::Ed25519: write(CryptoAlgorithmIdentifierTag::ED25519); break; + case CryptoAlgorithmIdentifier::None: { + RELEASE_ASSERT_NOT_REACHED(); + break; + } } } @@ -2420,7 +2428,7 @@ class CloneSerializer : CloneBase { SerializationForStorage m_forStorage; }; -void SerializedScriptValue::writeBytesForBun(CloneSerializer* ctx, const uint8_t* data, uint32_t size) +SYSV_ABI void SerializedScriptValue::writeBytesForBun(CloneSerializer* ctx, const uint8_t* data, uint32_t size) { ctx->write(data, size); } @@ -2573,7 +2581,9 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) JSMap* inMap = jsCast(inValue); if (!startMap(inMap)) break; - JSMapIterator* iterator = JSMapIterator::create(vm, m_lexicalGlobalObject->mapIteratorStructure(), inMap, IterationKind::Entries); + JSMapIterator* iterator = JSMapIterator::create(m_lexicalGlobalObject, m_lexicalGlobalObject->mapIteratorStructure(), inMap, IterationKind::Entries); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; m_gcBuffer.appendWithCrashOnOverflow(inMap); m_gcBuffer.appendWithCrashOnOverflow(iterator); mapIteratorStack.append(iterator); @@ -2619,7 +2629,9 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) JSSet* inSet = jsCast(inValue); if (!startSet(inSet)) break; - JSSetIterator* iterator = JSSetIterator::create(vm, m_lexicalGlobalObject->setIteratorStructure(), inSet, IterationKind::Keys); + JSSetIterator* iterator = JSSetIterator::create(m_lexicalGlobalObject, m_lexicalGlobalObject->setIteratorStructure(), inSet, IterationKind::Keys); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; m_gcBuffer.appendWithCrashOnOverflow(inSet); m_gcBuffer.appendWithCrashOnOverflow(iterator); setIteratorStack.append(iterator); @@ -3406,7 +3418,7 @@ class CloneDeserializer : CloneBase { return false; if (m_ptr + length > m_end) return false; - arrayBuffer = ArrayBuffer::tryCreate(m_ptr, length); + arrayBuffer = ArrayBuffer::tryCreate({ m_ptr, length }); if (!arrayBuffer) return false; m_ptr += length; @@ -3502,6 +3514,9 @@ class CloneDeserializer : CloneBase { case Uint32ArrayTag: arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint32Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get()); return true; + case Float16ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float16Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get()); + return true; case Float32ArrayTag: arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float32Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get()); return true; @@ -4729,14 +4744,7 @@ class CloneDeserializer : CloneBase { fail(); return JSValue(); } - auto scope = DECLARE_THROW_SCOPE(m_lexicalGlobalObject->vm()); - JSValue result = JSC::JSWebAssemblyModule::createStub(m_lexicalGlobalObject->vm(), m_lexicalGlobalObject, m_globalObject->webAssemblyModuleStructure(), m_wasmModules->at(index)); - // Since we are cloning a JSWebAssemblyModule, it's impossible for that - // module to not have been a valid module. Therefore, createStub should - // not throw. - scope.releaseAssertNoException(); - m_gcBuffer.appendWithCrashOnOverflow(result); - return result; + return JSC::JSWebAssemblyModule::create(m_lexicalGlobalObject->vm(), m_globalObject->webAssemblyModuleStructure(), Ref { *m_wasmModules->at(index) }); } case WasmMemoryTag: { if (m_version >= 12) { @@ -4769,10 +4777,10 @@ class CloneDeserializer : CloneBase { fail(); return JSValue(); } - memory = Wasm::Memory::create(contents.releaseNonNull(), WTFMove(handler)); + memory = Wasm::Memory::create(vm, contents.releaseNonNull(), WTFMove(handler)); } else { // zero size & max-size. - memory = Wasm::Memory::createZeroSized(JSC::MemorySharingMode::Shared, WTFMove(handler)); + memory = Wasm::Memory::createZeroSized(vm, JSC::MemorySharingMode::Shared, WTFMove(handler)); } result->adopt(memory.releaseNonNull()); @@ -5686,7 +5694,7 @@ Ref SerializedScriptValue::toArrayBuffer() this->ref(); auto arrayBuffer = ArrayBuffer::createFromBytes( - this->m_data.data(), this->m_data.size(), createSharedTask([protectedThis = Ref { *this }](void* p) { + { this->m_data.data(), this->m_data.size() }, createSharedTask([protectedThis = Ref { *this }](void* p) { protectedThis->deref(); })); diff --git a/src/bun.js/bindings/webcore/SerializedScriptValue.h b/src/bun.js/bindings/webcore/SerializedScriptValue.h index 5d5e93854ba24..305debcdb6306 100644 --- a/src/bun.js/bindings/webcore/SerializedScriptValue.h +++ b/src/bun.js/bindings/webcore/SerializedScriptValue.h @@ -26,6 +26,7 @@ #pragma once +#include "root.h" // #include "Blob.h" // #include "DetachedRTCDataChannel.h" #include "ExceptionOr.h" @@ -85,7 +86,7 @@ class SerializedScriptValue : public ThreadSafeRefCounted WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(SerializedScriptValue); public: - static void writeBytesForBun(CloneSerializer*, const uint8_t*, uint32_t); + static SYSV_ABI void writeBytesForBun(CloneSerializer*, const uint8_t*, uint32_t); WEBCORE_EXPORT static ExceptionOr> create(JSC::JSGlobalObject&, JSC::JSValue, Vector>&& transfer, Vector>&, SerializationForStorage = SerializationForStorage::No, SerializationContext = SerializationContext::Default); // WEBCORE_EXPORT static ExceptionOr> create(JSC::JSGlobalObject&, JSC::JSValue, Vector>&& transfer, SerializationForStorage = SerializationForStorage::No, SerializationContext = SerializationContext::Default); diff --git a/src/bun.js/bindings/webcore/SharedBuffer.cpp b/src/bun.js/bindings/webcore/SharedBuffer.cpp index 587ed1993d0b1..ca3614f8ffc80 100644 --- a/src/bun.js/bindings/webcore/SharedBuffer.cpp +++ b/src/bun.js/bindings/webcore/SharedBuffer.cpp @@ -550,7 +550,7 @@ const uint8_t* DataSegment::data() const #if USE(GSTREAMER) [](const RefPtr& data) -> const uint8_t* { return data->data(); }, #endif - [](const FileSystem::MappedFileData& data) -> const uint8_t* { return static_cast(data.data()); }, + [](const FileSystem::MappedFileData& data) -> const uint8_t* { return static_cast(data.span().data()); }, [](const Provider& provider) -> const uint8_t* { return provider.data(); }); return std::visit(visitor, m_immutableData); } @@ -573,7 +573,7 @@ size_t DataSegment::size() const #if USE(GSTREAMER) [](const RefPtr& data) -> size_t { return data->size(); }, #endif - [](const FileSystem::MappedFileData& data) -> size_t { return data.size(); }, + [](const FileSystem::MappedFileData& data) -> size_t { return data.span().size(); }, [](const Provider& provider) -> size_t { return provider.size(); }); return std::visit(visitor, m_immutableData); } @@ -610,7 +610,7 @@ void SharedBufferBuilder::initialize(Ref&& buffer) RefPtr SharedBufferBuilder::tryCreateArrayBuffer() const { - return m_buffer ? m_buffer->tryCreateArrayBuffer() : ArrayBuffer::tryCreate(nullptr, 0); + return m_buffer ? m_buffer->tryCreateArrayBuffer() : ArrayBuffer::tryCreate({}); } Ref SharedBufferBuilder::take() @@ -626,7 +626,7 @@ Ref SharedBufferBuilder::takeAsContiguous() RefPtr SharedBufferBuilder::takeAsArrayBuffer() { if (!m_buffer) - return ArrayBuffer::tryCreate(nullptr, 0); + return ArrayBuffer::tryCreate({}); return take()->tryCreateArrayBuffer(); } diff --git a/src/bun.js/bindings/webcore/StructuredClone.cpp b/src/bun.js/bindings/webcore/StructuredClone.cpp index ec4119bb3d36c..f0f8691bd23ba 100644 --- a/src/bun.js/bindings/webcore/StructuredClone.cpp +++ b/src/bun.js/bindings/webcore/StructuredClone.cpp @@ -51,7 +51,7 @@ static JSC::EncodedJSValue cloneArrayBufferImpl(JSGlobalObject* lexicalGlobalObj if (!buffer) { auto scope = DECLARE_THROW_SCOPE(vm); throwDataCloneError(*lexicalGlobalObject, scope); - return { }; + return {}; } if (mode == CloneMode::Partial) { ASSERT(callFrame->argumentCount() == 3); @@ -59,15 +59,15 @@ static JSC::EncodedJSValue cloneArrayBufferImpl(JSGlobalObject* lexicalGlobalObj int srcLength = static_cast(callFrame->uncheckedArgument(2).toNumber(lexicalGlobalObject)); return JSValue::encode(JSArrayBuffer::create(lexicalGlobalObject->vm(), lexicalGlobalObject->arrayBufferStructure(ArrayBufferSharingMode::Default), buffer->slice(srcByteOffset, srcByteOffset + srcLength))); } - return JSValue::encode(JSArrayBuffer::create(lexicalGlobalObject->vm(), lexicalGlobalObject->arrayBufferStructure(ArrayBufferSharingMode::Default), ArrayBuffer::tryCreate(buffer->data(), buffer->byteLength()))); + return JSValue::encode(JSArrayBuffer::create(lexicalGlobalObject->vm(), lexicalGlobalObject->arrayBufferStructure(ArrayBufferSharingMode::Default), buffer->slice(0))); } -JSC_DEFINE_HOST_FUNCTION(cloneArrayBuffer, (JSGlobalObject* globalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(cloneArrayBuffer, (JSGlobalObject * globalObject, CallFrame* callFrame)) { return cloneArrayBufferImpl(globalObject, callFrame, CloneMode::Partial); } -JSC_DEFINE_HOST_FUNCTION(structuredCloneForStream, (JSGlobalObject* globalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(structuredCloneForStream, (JSGlobalObject * globalObject, CallFrame* callFrame)) { ASSERT(callFrame); ASSERT(callFrame->argumentCount()); @@ -77,6 +77,10 @@ JSC_DEFINE_HOST_FUNCTION(structuredCloneForStream, (JSGlobalObject* globalObject JSValue value = callFrame->uncheckedArgument(0); + if (value.isPrimitive()) { + return JSValue::encode(value); + } + if (value.inherits()) RELEASE_AND_RETURN(scope, cloneArrayBufferImpl(globalObject, callFrame, CloneMode::Full)); @@ -87,16 +91,16 @@ JSC_DEFINE_HOST_FUNCTION(structuredCloneForStream, (JSGlobalObject* globalObject auto* buffer = bufferView->unsharedBuffer(); if (!buffer) { throwDataCloneError(*globalObject, scope); - return { }; + return {}; } - auto bufferClone = ArrayBuffer::tryCreate(buffer->data(), buffer->byteLength()); + auto bufferClone = buffer->slice(0); Structure* structure = bufferView->structure(); -#define CLONE_TYPED_ARRAY(name) \ - do { \ - if (bufferView->inherits()) \ - RELEASE_AND_RETURN(scope, JSValue::encode(JS##name##Array::create(globalObject, structure, WTFMove(bufferClone), bufferView->byteOffset(), bufferView->length()))); \ - } while (0); +#define CLONE_TYPED_ARRAY(name) \ + do { \ + if (bufferView->inherits()) \ + RELEASE_AND_RETURN(scope, JSValue::encode(JS##name##Array::create(globalObject, structure, WTFMove(bufferClone), bufferView->byteOffset(), bufferView->length()))); \ + } while (0); FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(CLONE_TYPED_ARRAY) @@ -107,7 +111,7 @@ JSC_DEFINE_HOST_FUNCTION(structuredCloneForStream, (JSGlobalObject* globalObject } throwTypeError(globalObject, scope, "structuredClone not implemented for non-ArrayBuffer / non-ArrayBufferView"_s); - return { }; + return {}; } } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/WebCoreTypedArrayController.cpp b/src/bun.js/bindings/webcore/WebCoreTypedArrayController.cpp index e9049b91e1503..a5d3a8daaa6a5 100644 --- a/src/bun.js/bindings/webcore/WebCoreTypedArrayController.cpp +++ b/src/bun.js/bindings/webcore/WebCoreTypedArrayController.cpp @@ -35,14 +35,9 @@ #include -extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); static inline WebCore::JSDOMGlobalObject* getDefaultGlobal(JSC::JSGlobalObject* lexicalGlobalObject) { - if (auto* global = jsDynamicCast(lexicalGlobalObject)) { - return global; - } - - return Bun__getDefaultGlobal(); + return defaultGlobalObject(lexicalGlobalObject); } namespace WebCore { diff --git a/src/bun.js/bindings/webcore/WebSocket.cpp b/src/bun.js/bindings/webcore/WebSocket.cpp index 235805605983c..58b2d1049415b 100644 --- a/src/bun.js/bindings/webcore/WebSocket.cpp +++ b/src/bun.js/bindings/webcore/WebSocket.cpp @@ -125,7 +125,7 @@ static String encodeProtocolString(const String& protocol) StringBuilder builder; for (size_t i = 0; i < protocol.length(); i++) { if (protocol[i] < 0x20 || protocol[i] > 0x7E) - builder.append("\\u", hex(protocol[i], 4)); + builder.append("\\u"_s, hex(protocol[i], 4)); else if (protocol[i] == 0x5c) builder.append("\\\\"_s); else @@ -265,7 +265,7 @@ static String resourceName(const URL& url) auto path = url.path(); auto result = makeString( path, - path.isEmpty() ? "/" : "", + path.isEmpty() ? "/"_s : ""_s, url.queryWithLeadingQuestionMark()); ASSERT(!result.isEmpty()); ASSERT(!result.contains(' ')); @@ -1067,13 +1067,13 @@ void WebSocket::didReceiveBinaryData(const AtomString& eventName, const std::spa if (this->hasEventListeners(eventName)) { // the main reason for dispatching on a separate tick is to handle when you haven't yet attached an event listener this->incPendingActivityCount(); - dispatchEvent(MessageEvent::create(eventName, ArrayBuffer::create(binaryData.data(), binaryData.size()), m_url.string())); + dispatchEvent(MessageEvent::create(eventName, ArrayBuffer::create(binaryData), m_url.string())); this->decPendingActivityCount(); return; } if (auto* context = scriptExecutionContext()) { - auto arrayBuffer = JSC::ArrayBuffer::create(binaryData.data(), binaryData.size()); + auto arrayBuffer = JSC::ArrayBuffer::create(binaryData); this->incPendingActivityCount(); context->postTask([this, name = eventName, buffer = WTFMove(arrayBuffer), protectedThis = Ref { *this }](ScriptExecutionContext& context) { ASSERT(scriptExecutionContext()); @@ -1113,7 +1113,7 @@ void WebSocket::didReceiveBinaryData(const AtomString& eventName, const std::spa } if (auto* context = scriptExecutionContext()) { - auto arrayBuffer = JSC::ArrayBuffer::tryCreate(binaryData.data(), binaryData.size()); + auto arrayBuffer = JSC::ArrayBuffer::tryCreate(binaryData); this->incPendingActivityCount(); @@ -1155,7 +1155,14 @@ void WebSocket::didReceiveClose(CleanStatus wasClean, unsigned short code, WTF:: if (auto* context = scriptExecutionContext()) { this->incPendingActivityCount(); if (wasConnecting && isConnectionError) { - dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No)); + ErrorEvent::Init eventInit = {}; + eventInit.message = makeString("WebSocket connection to '"_s, m_url.stringCenterEllipsizedToLength(), "' failed: "_s, reason); + eventInit.filename = String(); + eventInit.bubbles = false; + eventInit.cancelable = false; + eventInit.colno = 0; + eventInit.error = {}; + dispatchEvent(ErrorEvent::create(eventNames().errorEvent, eventInit, EventIsTrusted::Yes)); } // https://html.spec.whatwg.org/multipage/web-sockets.html#feedback-from-the-protocol:concept-websocket-closed, we should synchronously fire a close event. dispatchEvent(CloseEvent::create(wasClean == CleanStatus::Clean, code, reason)); diff --git a/src/bun.js/bindings/webcore/WebSocketIdentifier.h b/src/bun.js/bindings/webcore/WebSocketIdentifier.h index 97884abd8e301..ebbd804f51d44 100644 --- a/src/bun.js/bindings/webcore/WebSocketIdentifier.h +++ b/src/bun.js/bindings/webcore/WebSocketIdentifier.h @@ -29,7 +29,7 @@ namespace WebCore { -enum WebSocketIdentifierType { }; +enum WebSocketIdentifierType {}; using WebSocketIdentifier = ObjectIdentifier; } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/Worker.cpp b/src/bun.js/bindings/webcore/Worker.cpp index 1c224373912f4..e0d2c09de09ef 100644 --- a/src/bun.js/bindings/webcore/Worker.cpp +++ b/src/bun.js/bindings/webcore/Worker.cpp @@ -59,7 +59,6 @@ #include #include #include "MessageEvent.h" -#include #include "BunWorkerGlobalScope.h" #include "CloseEvent.h" #include "JSMessagePort.h" @@ -95,7 +94,7 @@ Worker::Worker(ScriptExecutionContext& context, WorkerOptions&& options) : EventTargetWithInlineData() , ContextDestructionObserver(&context) , m_options(WTFMove(options)) - , m_identifier("worker:" + Inspector::IdentifiersFactory::createIdentifier()) + , m_identifier(makeString("worker:"_s, Inspector::IdentifiersFactory::createIdentifier())) , m_clientIdentifier(ScriptExecutionContext::generateIdentifier()) { // static bool addedListener; @@ -400,10 +399,12 @@ extern "C" void WebWorker__dispatchExit(Zig::GlobalObject* globalObject, Worker* JSC::VM& vm = globalObject->vm(); vm.setHasTerminationRequest(); - while (!vm.hasOneRef()) - vm.deref(); + // clang-tidy is smart enough to realize that deref() leads to freeing + // but it's not smart enough to realize that `hasOneRef()` ensures its safety + while (!vm.hasOneRef()) // NOLINT + vm.deref(); // NOLINT - vm.deref(); + vm.deref(); // NOLINT } } extern "C" void WebWorker__dispatchOnline(Worker* worker, Zig::GlobalObject* globalObject) @@ -459,7 +460,7 @@ JSValue createNodeWorkerThreadsBinding(Zig::GlobalObject* globalObject) VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSValue workerData = jsUndefined(); + JSValue workerData = jsNull(); JSValue threadId = jsNumber(0); if (auto* worker = WebWorker__getParentWorker(globalObject->bunVM())) { @@ -476,11 +477,11 @@ JSValue createNodeWorkerThreadsBinding(Zig::GlobalObject* globalObject) threadId = jsNumber(worker->clientIdentifier() - 1); } - JSArray* array = constructEmptyArray(globalObject, nullptr, 3); + JSObject* array = constructEmptyObject(globalObject, globalObject->objectPrototype(), 3); - array->putByIndexInline(globalObject, (unsigned)0, workerData, false); - array->putByIndexInline(globalObject, (unsigned)1, threadId, false); - array->putByIndexInline(globalObject, (unsigned)2, JSFunction::create(vm, globalObject, 1, "receiveMessageOnPort"_s, jsReceiveMessageOnPort, ImplementationVisibility::Public, NoIntrinsic), false); + array->putDirectIndex(globalObject, 0, workerData); + array->putDirectIndex(globalObject, 1, threadId); + array->putDirectIndex(globalObject, 2, JSFunction::create(vm, globalObject, 1, "receiveMessageOnPort"_s, jsReceiveMessageOnPort, ImplementationVisibility::Public, NoIntrinsic)); return array; } diff --git a/src/bun.js/bindings/webcore/WritableStreamSink.h b/src/bun.js/bindings/webcore/WritableStreamSink.h index 2caa86c395d3b..b15e34d91eced 100644 --- a/src/bun.js/bindings/webcore/WritableStreamSink.h +++ b/src/bun.js/bindings/webcore/WritableStreamSink.h @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #pragma once #include "JSDOMPromiseDeferred.h" @@ -54,8 +53,8 @@ class SimpleWritableStreamSink : public WritableStreamSink { explicit SimpleWritableStreamSink(WriteCallback&&); void write(ScriptExecutionContext&, JSC::JSValue, DOMPromiseDeferred&&) final; - void close() final { } - void error(String&&) final { } + void close() final {} + void error(String&&) final {} WriteCallback m_writeCallback; }; diff --git a/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h b/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h index 2f991740fd7c3..b64362a1298b7 100644 --- a/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h +++ b/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h @@ -41,7 +41,7 @@ static const unsigned char IntegerMark = 0x02; static const unsigned char OctetStringMark = 0x04; static const unsigned char SequenceMark = 0x30; // Version 0. Per https://tools.ietf.org/html/rfc5208#section-5 -static const unsigned char Version[] = {0x02, 0x01, 0x00}; +static const unsigned char Version[] = { 0x02, 0x01, 0x00 }; static const unsigned char InitialOctet = 0x00; static const size_t MaxLengthInOneByte = 128; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CFB.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CFB.cpp index 88e8075b624f2..c5d802cfd16c9 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CFB.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CFB.cpp @@ -110,7 +110,7 @@ void CryptoAlgorithmAES_CFB::generateKey(const CryptoAlgorithmParameters& parame void CryptoAlgorithmAES_CFB::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback) { using namespace CryptoAlgorithmAES_CFBInternal; - + if (usagesAreInvalidForCryptoAlgorithmAES_CFB(usages)) { exceptionCallback(SyntaxError); return; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.cpp index 9b2648e8a110d..09e397a9bb3c4 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.cpp @@ -292,7 +292,7 @@ auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator~() c return { ~m_hi, ~m_lo }; } -auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator <<=(unsigned shift) -> CounterBlockBits& +auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator<<=(unsigned shift) -> CounterBlockBits& { if (shift < 64) { m_hi = (m_hi << shift) | m_lo >> (64 - shift); @@ -308,7 +308,7 @@ auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator <<=( return *this; } -auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator &=(const CounterBlockBits& rhs) -> CounterBlockBits& +auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator&=(const CounterBlockBits& rhs) -> CounterBlockBits& { m_hi &= rhs.m_hi; m_lo &= rhs.m_lo; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.h index 8bd39339dc360..c3b41b2538390 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.h @@ -53,8 +53,8 @@ class CryptoAlgorithmAES_CTR final : public CryptoAlgorithm { CounterBlockBits operator&(const CounterBlockBits&) const; CounterBlockBits operator~() const; - CounterBlockBits& operator <<=(unsigned); - CounterBlockBits& operator &=(const CounterBlockBits&); + CounterBlockBits& operator<<=(unsigned); + CounterBlockBits& operator&=(const CounterBlockBits&); uint64_t m_hi { 0 }; uint64_t m_lo { 0 }; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_GCMOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_GCMOpenSSL.cpp index 688bfa76eb18b..7b2c35f2510d0 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_GCMOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_GCMOpenSSL.cpp @@ -56,7 +56,7 @@ static std::optional> cryptEncrypt(const Vector& key, c return std::nullopt; EvpCipherCtxPtr ctx; - int len; + int len = 0; Vector cipherText(plainText.size() + tagLength); size_t tagOffset = plainText.size(); @@ -88,11 +88,14 @@ static std::optional> cryptEncrypt(const Vector& key, c } // Provide the message to be encrypted, and obtain the encrypted output - if (1 != EVP_EncryptUpdate(ctx.get(), cipherText.data(), &len, plainText.data(), plainText.size())) - return std::nullopt; + if (plainText.size() > 0) { + if (1 != EVP_EncryptUpdate(ctx.get(), cipherText.data(), &len, plainText.data(), plainText.size())) + return std::nullopt; + } // Finalize the encryption. Normally ciphertext bytes may be written at - // this stage, but this does not occur in GCM mode + // this stage, but this does not occur in GCM mode since it is not padded. + // We're still required to call it however to signal that the tag should be written next. if (1 != EVP_EncryptFinal_ex(ctx.get(), cipherText.data() + len, &len)) return std::nullopt; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAesKeyParams.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAesKeyParams.h index c86fdcc7b09a5..49059559518db 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmAesKeyParams.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmAesKeyParams.h @@ -33,7 +33,7 @@ namespace WebCore { class CryptoAlgorithmAesKeyParams final : public CryptoAlgorithmParameters { public: - unsigned short length; + unsigned short length = 0; Class parametersClass() const final { return Class::AesKeyParams; } }; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSA.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSA.h index 00687cd9a9d88..75efa7046f1e4 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSA.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSA.h @@ -42,6 +42,7 @@ class CryptoAlgorithmECDSA final : public CryptoAlgorithm { static ExceptionOr> platformSign(const CryptoAlgorithmEcdsaParams&, const CryptoKeyEC&, const Vector&); static ExceptionOr platformVerify(const CryptoAlgorithmEcdsaParams&, const CryptoKeyEC&, const Vector&, const Vector&); + private: CryptoAlgorithmECDSA() = default; CryptoAlgorithmIdentifier identifier() const final; @@ -51,7 +52,6 @@ class CryptoAlgorithmECDSA final : public CryptoAlgorithm { void generateKey(const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyOrKeyPairCallback&&, ExceptionCallback&&, ScriptExecutionContext&) final; void importKey(CryptoKeyFormat, KeyData&&, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyCallback&&, ExceptionCallback&&) final; void exportKey(CryptoKeyFormat, Ref&&, KeyDataCallback&&, ExceptionCallback&&) final; - }; } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSAOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSAOpenSSL.cpp index de6f773f56011..fa114bca31e54 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSAOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSAOpenSSL.cpp @@ -62,7 +62,7 @@ ExceptionOr> CryptoAlgorithmECDSA::platformSign(const CryptoAlgo return Exception { OperationError }; Vector signature(derSigLength); uint8_t* p = signature.data(); - if(i2d_ECDSA_SIG(sig.get(), &p) != derSigLength) + if (i2d_ECDSA_SIG(sig.get(), &p) != derSigLength) return Exception { OperationError }; return signature; } else { @@ -107,7 +107,7 @@ ExceptionOr CryptoAlgorithmECDSA::platformVerify(const CryptoAlgorithmEcds // Bail if the signature size isn't double the key size (i.e. concatenated r and s components). if (signature.size() != keySizeInBytes * 2) return false; - + auto sig = ECDSASigPtr(ECDSA_SIG_new()); auto r = BN_bin2bn(signature.data(), keySizeInBytes, nullptr); auto s = BN_bin2bn(signature.data() + keySizeInBytes, keySizeInBytes, nullptr); diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h index 9b2eb8cc64462..65dcd0664442d 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h @@ -39,11 +39,12 @@ class CryptoAlgorithmEd25519 final : public CryptoAlgorithm { static ExceptionOr> platformSign(const CryptoKeyOKP&, const Vector&); static ExceptionOr platformVerify(const CryptoKeyOKP&, const Vector&, const Vector&); + private: CryptoAlgorithmEd25519() = default; CryptoAlgorithmIdentifier identifier() const final; - void generateKey(const CryptoAlgorithmParameters& , bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& , ExceptionCallback&& , ScriptExecutionContext&); + void generateKey(const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&&, ExceptionCallback&&, ScriptExecutionContext&); void sign(const CryptoAlgorithmParameters&, Ref&&, Vector&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; void verify(const CryptoAlgorithmParameters&, Ref&&, Vector&& signature, Vector&&, BoolCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; void importKey(CryptoKeyFormat, KeyData&&, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyCallback&&, ExceptionCallback&&) final; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHKDF.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHKDF.cpp index 5c7a2f4c5315b..b45e6aa8a2176 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHKDF.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHKDF.cpp @@ -80,7 +80,7 @@ ExceptionOr CryptoAlgorithmHKDF::getKeyLength(const CryptoAlgorithmParam { return 0; } - + } // namespace WebCore #endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.h index 22064797ae8ca..eb69939b8bc60 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.h @@ -45,7 +45,6 @@ class CryptoAlgorithmHMAC final : public CryptoAlgorithm { static ExceptionOr platformVerify(const CryptoKeyHMAC&, const Vector&, const Vector&); static ExceptionOr platformVerifyWithAlgorithm(const CryptoKeyHMAC&, CryptoAlgorithmIdentifier, const Vector&, const Vector&); - private: CryptoAlgorithmHMAC() = default; CryptoAlgorithmIdentifier identifier() const final; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp index c71751b3608eb..2caf8149e852f 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp @@ -59,8 +59,9 @@ static std::optional> calculateSignature(const EVP_MD* algorithm return cipherText; } -ExceptionOr> CryptoAlgorithmHMAC::platformSignWithAlgorithm(const CryptoKeyHMAC& key, CryptoAlgorithmIdentifier algorithmIdentifier, const Vector& data) { - +ExceptionOr> CryptoAlgorithmHMAC::platformSignWithAlgorithm(const CryptoKeyHMAC& key, CryptoAlgorithmIdentifier algorithmIdentifier, const Vector& data) +{ + auto algorithm = digestAlgorithm(algorithmIdentifier); if (!algorithm) return Exception { OperationError }; @@ -83,7 +84,8 @@ ExceptionOr> CryptoAlgorithmHMAC::platformSign(const CryptoKeyHM return WTFMove(*result); } -ExceptionOr CryptoAlgorithmHMAC::platformVerifyWithAlgorithm(const CryptoKeyHMAC& key, CryptoAlgorithmIdentifier algorithmIdentifier, const Vector& signature, const Vector& data) { +ExceptionOr CryptoAlgorithmHMAC::platformVerifyWithAlgorithm(const CryptoKeyHMAC& key, CryptoAlgorithmIdentifier algorithmIdentifier, const Vector& signature, const Vector& data) +{ auto algorithm = digestAlgorithm(algorithmIdentifier); if (!algorithm) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h index a0c07a78a0f66..d6a112ecb8abc 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h @@ -29,7 +29,8 @@ namespace WebCore { -enum class CryptoAlgorithmIdentifier { +enum class CryptoAlgorithmIdentifier : uint8_t { + None = 0, RSAES_PKCS1_v1_5 = 1, RSASSA_PKCS1_v1_5, RSA_PSS, diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmParameters.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmParameters.h index 8398404b8e33e..2b398bc29504c 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmParameters.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmParameters.h @@ -35,6 +35,7 @@ namespace WebCore { class CryptoAlgorithmParameters { WTF_MAKE_FAST_ALLOCATED; + public: enum class Class { None, @@ -57,7 +58,7 @@ class CryptoAlgorithmParameters { // FIXME: Consider merging name and identifier. String name; - CryptoAlgorithmIdentifier identifier; + CryptoAlgorithmIdentifier identifier { CryptoAlgorithmIdentifier::None }; virtual ~CryptoAlgorithmParameters() = default; @@ -66,9 +67,13 @@ class CryptoAlgorithmParameters { } // namespace WebCore +// clang-format off + #define SPECIALIZE_TYPE_TRAITS_CRYPTO_ALGORITHM_PARAMETERS(ToClassName) \ SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::CryptoAlgorithm##ToClassName) \ static bool isType(const WebCore::CryptoAlgorithmParameters& parameters) { return parameters.parametersClass() == WebCore::CryptoAlgorithmParameters::Class::ToClassName; } \ SPECIALIZE_TYPE_TRAITS_END() +// clang-format on + #endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.h index a08f08854a1a6..bf9419a01e317 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.h @@ -45,7 +45,6 @@ class CryptoAlgorithmRSASSA_PKCS1_v1_5 final : public CryptoAlgorithm { static ExceptionOr platformVerify(const CryptoKeyRSA&, const Vector&, const Vector&); static ExceptionOr platformVerifyWithAlgorithm(const CryptoKeyRSA&, CryptoAlgorithmIdentifier, const Vector&, const Vector&); - private: CryptoAlgorithmRSASSA_PKCS1_v1_5() = default; CryptoAlgorithmIdentifier identifier() const final; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5OpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5OpenSSL.cpp index f31585a3a44a1..8d7f74168cfbb 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5OpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5OpenSSL.cpp @@ -33,8 +33,8 @@ namespace WebCore { - -static ExceptionOr> signWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD* md, const Vector& data) { +static ExceptionOr> signWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD* md, const Vector& data) +{ std::optional> digest = calculateDigest(md, data); if (!digest) @@ -65,7 +65,8 @@ static ExceptionOr> signWithEVP_MD(const CryptoKeyRSA& key, con return signature; } -ExceptionOr> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSignWithAlgorithm(const CryptoKeyRSA& key, CryptoAlgorithmIdentifier algorithm, const Vector& data) { +ExceptionOr> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSignWithAlgorithm(const CryptoKeyRSA& key, CryptoAlgorithmIdentifier algorithm, const Vector& data) +{ const EVP_MD* md = digestAlgorithm(algorithm); if (!md) @@ -82,8 +83,8 @@ ExceptionOr> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(cons return signWithEVP_MD(key, md, data); } - -static ExceptionOr verifyWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD* md, const Vector& signature, const Vector& data) { +static ExceptionOr verifyWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD* md, const Vector& signature, const Vector& data) +{ std::optional> digest = calculateDigest(md, data); if (!digest) return Exception { OperationError }; @@ -106,7 +107,8 @@ static ExceptionOr verifyWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD return ret == 1; } -ExceptionOr CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerifyWithAlgorithm(const CryptoKeyRSA& key, CryptoAlgorithmIdentifier algorithm, const Vector& signature, const Vector& data) { +ExceptionOr CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerifyWithAlgorithm(const CryptoKeyRSA& key, CryptoAlgorithmIdentifier algorithm, const Vector& signature, const Vector& data) +{ const EVP_MD* md = digestAlgorithm(algorithm); if (!md) return Exception { NotSupportedError }; @@ -114,7 +116,6 @@ ExceptionOr CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerifyWithAlgorithm( return verifyWithEVP_MD(key, md, signature, data); } - ExceptionOr CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoKeyRSA& key, const Vector& signature, const Vector& data) { const EVP_MD* md = digestAlgorithm(key.hashAlgorithmIdentifier()); diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEP.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEP.h index dba7ea9d5d722..582e3e44041af 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEP.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEP.h @@ -40,6 +40,9 @@ class CryptoAlgorithmRSA_OAEP final : public CryptoAlgorithm { static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::RSA_OAEP; static Ref create(); + static ExceptionOr> platformEncryptWithHash(const CryptoAlgorithmRsaOaepParams&, const CryptoKeyRSA&, const Vector&, CryptoAlgorithmIdentifier hashIdentifier); + static ExceptionOr> platformDecryptWithHash(const CryptoAlgorithmRsaOaepParams&, const CryptoKeyRSA&, const Vector&, CryptoAlgorithmIdentifier hashIdentifier); + private: CryptoAlgorithmRSA_OAEP() = default; CryptoAlgorithmIdentifier identifier() const final; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEPOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEPOpenSSL.cpp index 3095000a61516..681aaf9ec1f1e 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEPOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEPOpenSSL.cpp @@ -38,11 +38,11 @@ namespace WebCore { ExceptionOr> CryptoAlgorithmRSA_OAEP::platformEncrypt(const CryptoAlgorithmRsaOaepParams& parameters, const CryptoKeyRSA& key, const Vector& plainText) { -#if 1 // defined(EVP_PKEY_CTX_set_rsa_oaep_md) && defined(EVP_PKEY_CTX_set_rsa_mgf1_md) && defined(EVP_PKEY_CTX_set0_rsa_oaep_label) - const EVP_MD* md = digestAlgorithm(key.hashAlgorithmIdentifier()); - if (!md) - return Exception { NotSupportedError }; + return CryptoAlgorithmRSA_OAEP::platformEncryptWithHash(parameters, key, plainText, key.hashAlgorithmIdentifier()); +} +ExceptionOr> CryptoAlgorithmRSA_OAEP::platformEncryptWithHash(const CryptoAlgorithmRsaOaepParams& parameters, const CryptoKeyRSA& key, const Vector& plainText, CryptoAlgorithmIdentifier hashIdentifier) +{ auto ctx = EvpPKeyCtxPtr(EVP_PKEY_CTX_new(key.platformKey(), nullptr)); if (!ctx) return Exception { OperationError }; @@ -50,14 +50,25 @@ ExceptionOr> CryptoAlgorithmRSA_OAEP::platformEncrypt(const Cryp if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) return Exception { OperationError }; - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) - return Exception { OperationError }; + auto padding = parameters.padding; + if (padding == 0) { + padding = RSA_PKCS1_OAEP_PADDING; + } - if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md) <= 0) + if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) return Exception { OperationError }; - if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), md) <= 0) - return Exception { OperationError }; + if (padding == RSA_PKCS1_OAEP_PADDING) { + const EVP_MD* md = digestAlgorithm(hashIdentifier); + if (!md) + return Exception { NotSupportedError }; + + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md) <= 0) + return Exception { OperationError }; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), md) <= 0) + return Exception { OperationError }; + } if (!parameters.labelVector().isEmpty()) { size_t labelSize = parameters.labelVector().size(); @@ -80,18 +91,15 @@ ExceptionOr> CryptoAlgorithmRSA_OAEP::platformEncrypt(const Cryp cipherText.shrink(cipherTextLen); return cipherText; -#else - return Exception { NotSupportedError }; -#endif } ExceptionOr> CryptoAlgorithmRSA_OAEP::platformDecrypt(const CryptoAlgorithmRsaOaepParams& parameters, const CryptoKeyRSA& key, const Vector& cipherText) { -#if 1 // defined(EVP_PKEY_CTX_set_rsa_oaep_md) && defined(EVP_PKEY_CTX_set_rsa_mgf1_md) && defined(EVP_PKEY_CTX_set0_rsa_oaep_label) - const EVP_MD* md = digestAlgorithm(key.hashAlgorithmIdentifier()); - if (!md) - return Exception { NotSupportedError }; + return CryptoAlgorithmRSA_OAEP::platformDecryptWithHash(parameters, key, cipherText, key.hashAlgorithmIdentifier()); +} +ExceptionOr> CryptoAlgorithmRSA_OAEP::platformDecryptWithHash(const CryptoAlgorithmRsaOaepParams& parameters, const CryptoKeyRSA& key, const Vector& cipherText, CryptoAlgorithmIdentifier hashIdentifier) +{ auto ctx = EvpPKeyCtxPtr(EVP_PKEY_CTX_new(key.platformKey(), nullptr)); if (!ctx) return Exception { OperationError }; @@ -99,14 +107,25 @@ ExceptionOr> CryptoAlgorithmRSA_OAEP::platformDecrypt(const Cryp if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) return Exception { OperationError }; - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) - return Exception { OperationError }; + auto padding = parameters.padding; + if (padding == 0) { + padding = RSA_PKCS1_OAEP_PADDING; + } - if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md) <= 0) + if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) return Exception { OperationError }; - if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), md) <= 0) - return Exception { OperationError }; + if (padding == RSA_PKCS1_OAEP_PADDING) { + const EVP_MD* md = digestAlgorithm(hashIdentifier); + if (!md) + return Exception { NotSupportedError }; + + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md) <= 0) + return Exception { OperationError }; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), md) <= 0) + return Exception { OperationError }; + } if (!parameters.labelVector().isEmpty()) { size_t labelSize = parameters.labelVector().size(); @@ -129,9 +148,6 @@ ExceptionOr> CryptoAlgorithmRSA_OAEP::platformDecrypt(const Cryp plainText.shrink(plainTextLen); return plainText; -#else - return Exception { NotSupportedError }; -#endif } } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.h index 5b79811796dd9..d87f04565bbd3 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.h @@ -46,7 +46,6 @@ class CryptoAlgorithmRSA_PSS final : public CryptoAlgorithm { static ExceptionOr platformVerify(const CryptoAlgorithmRsaPssParams&, const CryptoKeyRSA&, const Vector&, const Vector&); static ExceptionOr platformVerifyWithAlgorithm(const CryptoAlgorithmRsaPssParams&, CryptoAlgorithmIdentifier, const CryptoKeyRSA&, const Vector&, const Vector&); - private: CryptoAlgorithmRSA_PSS() = default; CryptoAlgorithmIdentifier identifier() const final; @@ -56,7 +55,6 @@ class CryptoAlgorithmRSA_PSS final : public CryptoAlgorithm { void generateKey(const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyOrKeyPairCallback&&, ExceptionCallback&&, ScriptExecutionContext&) final; void importKey(CryptoKeyFormat, KeyData&&, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyCallback&&, ExceptionCallback&&) final; void exportKey(CryptoKeyFormat, Ref&&, KeyDataCallback&&, ExceptionCallback&&) final; - }; } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSSOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSSOpenSSL.cpp index c768bc519c619..5aa3eb09ce0e0 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSSOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSSOpenSSL.cpp @@ -37,7 +37,7 @@ namespace WebCore { static ExceptionOr> signWithMD(const CryptoAlgorithmRsaPssParams& parameters, const CryptoKeyRSA& key, const Vector& data, const EVP_MD* md) { auto padding = parameters.padding; - if(padding == 0) { + if (padding == 0) { padding = RSA_PKCS1_PSS_PADDING; } std::optional> digest = calculateDigest(md, data); @@ -54,7 +54,7 @@ static ExceptionOr> signWithMD(const CryptoAlgorithmRsaPssParams if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) return Exception { OperationError }; - if(padding == RSA_PKCS1_PSS_PADDING) { + if (padding == RSA_PKCS1_PSS_PADDING) { if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx.get(), parameters.saltLength) <= 0) return Exception { OperationError }; } @@ -89,7 +89,6 @@ ExceptionOr> CryptoAlgorithmRSA_PSS::platformSignWithAlgorithm(c #endif } - ExceptionOr> CryptoAlgorithmRSA_PSS::platformSign(const CryptoAlgorithmRsaPssParams& parameters, const CryptoKeyRSA& key, const Vector& data) { #if 1 // defined(EVP_PKEY_CTX_set_rsa_pss_saltlen) && defined(EVP_PKEY_CTX_set_rsa_mgf1_md) @@ -106,7 +105,7 @@ ExceptionOr> CryptoAlgorithmRSA_PSS::platformSign(const CryptoAl static ExceptionOr verifyWithMD(const CryptoAlgorithmRsaPssParams& parameters, const CryptoKeyRSA& key, const Vector& signature, const Vector& data, const EVP_MD* md) { auto padding = parameters.padding; - if(padding == 0) { + if (padding == 0) { padding = RSA_PKCS1_PSS_PADDING; } @@ -120,7 +119,7 @@ static ExceptionOr verifyWithMD(const CryptoAlgorithmRsaPssParams& paramet if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) return Exception { OperationError }; - if(padding == RSA_PKCS1_PSS_PADDING) { + if (padding == RSA_PKCS1_PSS_PADDING) { if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx.get(), parameters.saltLength) <= 0) return Exception { OperationError }; } @@ -146,7 +145,6 @@ ExceptionOr CryptoAlgorithmRSA_PSS::platformVerifyWithAlgorithm(const Cryp return Exception { NotSupportedError }; return verifyWithMD(parameters, key, signature, data, md); - } ExceptionOr CryptoAlgorithmRSA_PSS::platformVerify(const CryptoAlgorithmRsaPssParams& parameters, const CryptoKeyRSA& key, const Vector& signature, const Vector& data) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistry.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistry.cpp index d8192bc9201da..db6bf6cf4347e 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistry.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistry.cpp @@ -69,7 +69,7 @@ String CryptoAlgorithmRegistry::name(CryptoAlgorithmIdentifier identifier) auto contructor = m_constructors.find(static_cast(identifier)); if (contructor == m_constructors.end()) - return { }; + return {}; return contructor->value.first.isolatedCopy(); } @@ -97,7 +97,6 @@ void CryptoAlgorithmRegistry::registerAlgorithm(const String& name, CryptoAlgori m_constructors.add(static_cast(identifier), std::make_pair(name, constructor)); } - } // namespace WebCore #endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRsaOaepParams.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRsaOaepParams.h index f960a38c67bbb..4b58f7bb679d0 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRsaOaepParams.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRsaOaepParams.h @@ -37,6 +37,7 @@ class CryptoAlgorithmRsaOaepParams final : public CryptoAlgorithmParameters { public: // Use labelVector() instead of label. The label will be gone once labelVector() is called. mutable std::optional label; + size_t padding = 0; // 0 represents the default value of the API Class parametersClass() const final { return Class::RsaOaepParams; } diff --git a/src/bun.js/bindings/webcrypto/CryptoEcKeyAlgorithm.h b/src/bun.js/bindings/webcrypto/CryptoEcKeyAlgorithm.h index dc6e67683d084..10782d5db6b44 100644 --- a/src/bun.js/bindings/webcrypto/CryptoEcKeyAlgorithm.h +++ b/src/bun.js/bindings/webcrypto/CryptoEcKeyAlgorithm.h @@ -35,4 +35,3 @@ struct CryptoEcKeyAlgorithm : CryptoKeyAlgorithm { }; } - diff --git a/src/bun.js/bindings/webcrypto/CryptoHmacKeyAlgorithm.h b/src/bun.js/bindings/webcrypto/CryptoHmacKeyAlgorithm.h index 92aa132ba6d01..89549a4e057f5 100644 --- a/src/bun.js/bindings/webcrypto/CryptoHmacKeyAlgorithm.h +++ b/src/bun.js/bindings/webcrypto/CryptoHmacKeyAlgorithm.h @@ -37,5 +37,3 @@ struct CryptoHmacKeyAlgorithm : CryptoKeyAlgorithm { }; } - - diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.h b/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.h index 714888019b6b4..0ef4ad090043c 100644 --- a/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.h +++ b/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.h @@ -43,7 +43,7 @@ class CryptoKeyHMAC final : public CryptoKey { { return adoptRef(*new CryptoKeyHMAC(key, hash, extractable, usage)); } - + virtual ~CryptoKeyHMAC(); static RefPtr generate(size_t lengthBits, CryptoAlgorithmIdentifier hash, bool extractable, CryptoKeyUsageBitmap); diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp index 462f97140a4d2..5d5e3c2ff35c0 100644 --- a/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp @@ -46,19 +46,19 @@ std::optional CryptoKeyOKP::platformGeneratePair(CryptoAlgorithmI if (namedCurve != NamedCurve::Ed25519) return {}; - uint8_t public_key[ED25519_PUBLIC_KEY_LEN], private_key[ED25519_PRIVATE_KEY_LEN]; + Vector public_key(ED25519_PUBLIC_KEY_LEN), private_key(ED25519_PRIVATE_KEY_LEN); bool isEd25519 = identifier == CryptoAlgorithmIdentifier::Ed25519; if (isEd25519) { - ED25519_keypair(public_key, private_key); + ED25519_keypair(public_key.data(), private_key.data()); } else { - X25519_keypair(public_key, private_key); + X25519_keypair(public_key.data(), private_key.data()); } bool isPublicKeyExtractable = true; - auto publicKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Public, Vector(public_key), isPublicKeyExtractable, usages); + auto publicKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Public, WTFMove(public_key), isPublicKeyExtractable, usages); ASSERT(publicKey); - auto privateKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Private, Vector(std::span { private_key, isEd25519 ? (unsigned int)ED25519_PRIVATE_KEY_LEN : (unsigned int)X25519_PRIVATE_KEY_LEN }), extractable, usages); + auto privateKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Private, Vector(std::span { private_key.data(), isEd25519 ? (unsigned int)ED25519_PRIVATE_KEY_LEN : (unsigned int)X25519_PRIVATE_KEY_LEN }), extractable, usages); ASSERT(privateKey); return CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) }; } diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyRSAOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyRSAOpenSSL.cpp index ef4794e3616eb..111cd081c6b98 100644 --- a/src/bun.js/bindings/webcrypto/CryptoKeyRSAOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoKeyRSAOpenSSL.cpp @@ -395,7 +395,7 @@ std::unique_ptr CryptoKeyRSA::exportData() const return CryptoKeyRSAComponents::createPrivateWithAdditionalData( convertToBytes(n), convertToBytes(e), convertToBytes(d), - WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector { }); + WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector {}); } default: ASSERT_NOT_REACHED(); diff --git a/src/bun.js/bindings/webcrypto/CryptoRsaHashedKeyAlgorithm.h b/src/bun.js/bindings/webcrypto/CryptoRsaHashedKeyAlgorithm.h index 30109aa907fe9..39a2f0d025de5 100644 --- a/src/bun.js/bindings/webcrypto/CryptoRsaHashedKeyAlgorithm.h +++ b/src/bun.js/bindings/webcrypto/CryptoRsaHashedKeyAlgorithm.h @@ -35,4 +35,3 @@ struct CryptoRsaHashedKeyAlgorithm : CryptoRsaKeyAlgorithm { }; } - diff --git a/src/bun.js/bindings/webcrypto/CryptoRsaKeyAlgorithm.h b/src/bun.js/bindings/webcrypto/CryptoRsaKeyAlgorithm.h index 7723d09f4f89f..4cc84c20ff66d 100644 --- a/src/bun.js/bindings/webcrypto/CryptoRsaKeyAlgorithm.h +++ b/src/bun.js/bindings/webcrypto/CryptoRsaKeyAlgorithm.h @@ -38,4 +38,3 @@ struct CryptoRsaKeyAlgorithm : CryptoKeyAlgorithm { }; } - diff --git a/src/bun.js/bindings/webcrypto/JSAesCbcCfbParams.cpp b/src/bun.js/bindings/webcrypto/JSAesCbcCfbParams.cpp index af0ddfda7de71..b9fd88ae77291 100644 --- a/src/bun.js/bindings/webcrypto/JSAesCbcCfbParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSAesCbcCfbParams.cpp @@ -30,7 +30,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -44,36 +43,36 @@ template<> CryptoAlgorithmAesCbcCfbParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "AesCbcCfbParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "AesCbcCfbParams"_s, "DOMString"_s); + return {}; } JSValue ivValue; if (isNullOrUndefined) ivValue = jsUndefined(); else { ivValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "iv"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!ivValue.isUndefined()) { result.iv = convert>(lexicalGlobalObject, ivValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "iv", "AesCbcCfbParams", "(ArrayBufferView or ArrayBuffer)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "iv"_s, "AesCbcCfbParams"_s, "(ArrayBufferView or ArrayBuffer)"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSAesCtrParams.cpp b/src/bun.js/bindings/webcrypto/JSAesCtrParams.cpp index 97ce6322e42b6..fd21f0d58c325 100644 --- a/src/bun.js/bindings/webcrypto/JSAesCtrParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSAesCtrParams.cpp @@ -31,7 +31,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -45,50 +44,50 @@ template<> CryptoAlgorithmAesCtrParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "AesCtrParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "AesCtrParams"_s, "DOMString"_s); + return {}; } JSValue counterValue; if (isNullOrUndefined) counterValue = jsUndefined(); else { counterValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "counter"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!counterValue.isUndefined()) { result.counter = convert>(lexicalGlobalObject, counterValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "counter", "AesCtrParams", "(ArrayBufferView or ArrayBuffer)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "counter"_s, "AesCtrParams"_s, "(ArrayBufferView or ArrayBuffer)"_s); + return {}; } JSValue lengthValue; if (isNullOrUndefined) lengthValue = jsUndefined(); else { lengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "length"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!lengthValue.isUndefined()) { result.length = convert>(lexicalGlobalObject, lengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length", "AesCtrParams", "octet"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length"_s, "AesCtrParams"_s, "octet"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSAesGcmParams.cpp b/src/bun.js/bindings/webcrypto/JSAesGcmParams.cpp index 7e13f82e9733b..94c8c6cc18045 100644 --- a/src/bun.js/bindings/webcrypto/JSAesGcmParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSAesGcmParams.cpp @@ -31,7 +31,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -45,58 +44,58 @@ template<> CryptoAlgorithmAesGcmParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "AesGcmParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "AesGcmParams"_s, "DOMString"_s); + return {}; } JSValue additionalDataValue; if (isNullOrUndefined) additionalDataValue = jsUndefined(); else { additionalDataValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "additionalData"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!additionalDataValue.isUndefined()) { result.additionalData = convert>(lexicalGlobalObject, additionalDataValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } JSValue ivValue; if (isNullOrUndefined) ivValue = jsUndefined(); else { ivValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "iv"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!ivValue.isUndefined()) { result.iv = convert>(lexicalGlobalObject, ivValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "iv", "AesGcmParams", "(ArrayBufferView or ArrayBuffer)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "iv"_s, "AesGcmParams"_s, "(ArrayBufferView or ArrayBuffer)"_s); + return {}; } JSValue tagLengthValue; if (isNullOrUndefined) tagLengthValue = jsUndefined(); else { tagLengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "tagLength"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!tagLengthValue.isUndefined()) { result.tagLength = convert>(lexicalGlobalObject, tagLengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSAesKeyParams.cpp b/src/bun.js/bindings/webcrypto/JSAesKeyParams.cpp index 2437d14ea81d0..b7be2bca4c545 100644 --- a/src/bun.js/bindings/webcrypto/JSAesKeyParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSAesKeyParams.cpp @@ -28,7 +28,6 @@ #include "JSDOMConvertStrings.h" #include - namespace WebCore { using namespace JSC; @@ -42,36 +41,36 @@ template<> CryptoAlgorithmAesKeyParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "AesKeyParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "AesKeyParams"_s, "DOMString"_s); + return {}; } JSValue lengthValue; if (isNullOrUndefined) lengthValue = jsUndefined(); else { lengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "length"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!lengthValue.isUndefined()) { result.length = convert>(lexicalGlobalObject, lengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length", "AesKeyParams", "unsigned short"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length"_s, "AesKeyParams"_s, "unsigned short"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoAesKeyAlgorithm.cpp b/src/bun.js/bindings/webcrypto/JSCryptoAesKeyAlgorithm.cpp index 3f7568947b695..ba48c3da96c4d 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoAesKeyAlgorithm.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoAesKeyAlgorithm.cpp @@ -30,7 +30,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -44,36 +43,36 @@ template<> CryptoAesKeyAlgorithm convertDictionary(JSGlob auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } CryptoAesKeyAlgorithm result; JSValue nameValue; if (isNullOrUndefined) nameValue = jsUndefined(); else { - nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoAesKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoAesKeyAlgorithm"_s, "DOMString"_s); + return {}; } JSValue lengthValue; if (isNullOrUndefined) lengthValue = jsUndefined(); else { lengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "length"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!lengthValue.isUndefined()) { result.length = convert(lexicalGlobalObject, lengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length", "CryptoAesKeyAlgorithm", "unsigned short"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length"_s, "CryptoAesKeyAlgorithm"_s, "unsigned short"_s); + return {}; } return result; } @@ -86,11 +85,11 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto nameValue = toJS(lexicalGlobalObject, throwScope, dictionary.name); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "name"_s), nameValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->name, nameValue); auto lengthValue = toJS(lexicalGlobalObject, throwScope, dictionary.length); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "length"_s), lengthValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->length, lengthValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoAlgorithmParameters.cpp b/src/bun.js/bindings/webcrypto/JSCryptoAlgorithmParameters.cpp index 2ac46f90e710a..12ccfe8b0b2bc 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoAlgorithmParameters.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoAlgorithmParameters.cpp @@ -47,14 +47,14 @@ template<> CryptoAlgorithmParameters convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoAlgorithmParameters", "DOMString"); + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoAlgorithmParameters"_s, "DOMString"_s); return {}; } return result; diff --git a/src/bun.js/bindings/webcrypto/JSCryptoEcKeyAlgorithm.cpp b/src/bun.js/bindings/webcrypto/JSCryptoEcKeyAlgorithm.cpp index e63283c97e45b..f86d63c3f1f85 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoEcKeyAlgorithm.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoEcKeyAlgorithm.cpp @@ -29,7 +29,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -43,36 +42,36 @@ template<> CryptoEcKeyAlgorithm convertDictionary(JSGlobal auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } CryptoEcKeyAlgorithm result; JSValue nameValue; if (isNullOrUndefined) nameValue = jsUndefined(); else { - nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoEcKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoEcKeyAlgorithm"_s, "DOMString"_s); + return {}; } JSValue namedCurveValue; if (isNullOrUndefined) namedCurveValue = jsUndefined(); else { namedCurveValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "namedCurve"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!namedCurveValue.isUndefined()) { result.namedCurve = convert(lexicalGlobalObject, namedCurveValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "namedCurve", "CryptoEcKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "namedCurve"_s, "CryptoEcKeyAlgorithm"_s, "DOMString"_s); + return {}; } return result; } @@ -85,10 +84,10 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto nameValue = toJS(lexicalGlobalObject, throwScope, dictionary.name); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "name"_s), nameValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->name, nameValue); auto namedCurveValue = toJS(lexicalGlobalObject, throwScope, dictionary.namedCurve); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "namedCurve"_s), namedCurveValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoHmacKeyAlgorithm.cpp b/src/bun.js/bindings/webcrypto/JSCryptoHmacKeyAlgorithm.cpp index 850eeb2aa958a..fd0a85740e159 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoHmacKeyAlgorithm.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoHmacKeyAlgorithm.cpp @@ -31,7 +31,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -45,50 +44,50 @@ template<> CryptoHmacKeyAlgorithm convertDictionary(JSGl auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } CryptoHmacKeyAlgorithm result; JSValue nameValue; if (isNullOrUndefined) nameValue = jsUndefined(); else { - nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoHmacKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoHmacKeyAlgorithm"_s, "DOMString"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "CryptoHmacKeyAlgorithm", "CryptoKeyAlgorithm"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "CryptoHmacKeyAlgorithm"_s, "CryptoKeyAlgorithm"_s); + return {}; } JSValue lengthValue; if (isNullOrUndefined) lengthValue = jsUndefined(); else { lengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "length"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!lengthValue.isUndefined()) { result.length = convert(lexicalGlobalObject, lengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length", "CryptoHmacKeyAlgorithm", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "length"_s, "CryptoHmacKeyAlgorithm"_s, "unsigned long"_s); + return {}; } return result; } @@ -101,14 +100,14 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto nameValue = toJS(lexicalGlobalObject, throwScope, dictionary.name); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "name"_s), nameValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->name, nameValue); auto hashValue = toJS>(lexicalGlobalObject, globalObject, throwScope, dictionary.hash); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "hash"_s), hashValue); auto lengthValue = toJS(lexicalGlobalObject, throwScope, dictionary.length); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "length"_s), lengthValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->length, lengthValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoKey.cpp b/src/bun.js/bindings/webcrypto/JSCryptoKey.cpp index cacea23e7900d..1d3d32e6683bb 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoKey.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoKey.cpp @@ -98,9 +98,9 @@ template<> std::optional parseEnumeration(JSGl return std::nullopt; } -template<> const char* expectedEnumerationValues() +template<> ASCIILiteral expectedEnumerationValues() { - return "\"public\", \"private\", \"secret\""; + return "\"public\", \"private\", \"secret\""_s; } // Attributes @@ -318,7 +318,7 @@ void JSCryptoKey::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoKey.h b/src/bun.js/bindings/webcrypto/JSCryptoKey.h index 0a977f6f9003f..af09c01381a2e 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoKey.h +++ b/src/bun.js/bindings/webcrypto/JSCryptoKey.h @@ -101,7 +101,7 @@ String convertEnumerationToString(CryptoKey::Type); template<> JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, CryptoKey::Type); template<> std::optional parseEnumeration(JSC::JSGlobalObject&, JSC::JSValue); -template<> const char* expectedEnumerationValues(); +template<> ASCIILiteral expectedEnumerationValues(); } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/JSCryptoKeyAlgorithm.cpp b/src/bun.js/bindings/webcrypto/JSCryptoKeyAlgorithm.cpp index c7009a16dadde..aee7a47e8cced 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoKeyAlgorithm.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoKeyAlgorithm.cpp @@ -29,7 +29,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -43,22 +42,22 @@ template<> CryptoKeyAlgorithm convertDictionary(JSGlobalObje auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } CryptoKeyAlgorithm result; JSValue nameValue; if (isNullOrUndefined) nameValue = jsUndefined(); else { - nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoKeyAlgorithm"_s, "DOMString"_s); + return {}; } return result; } @@ -71,8 +70,8 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto nameValue = toJS(lexicalGlobalObject, throwScope, dictionary.name); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "name"_s), nameValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->name, nameValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoKeyPair.cpp b/src/bun.js/bindings/webcrypto/JSCryptoKeyPair.cpp index 82947e907f416..b0b7ae2fe451f 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoKeyPair.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoKeyPair.cpp @@ -30,7 +30,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -44,7 +43,7 @@ template<> CryptoKeyPair convertDictionary(JSGlobalObject& lexica auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } CryptoKeyPair result; JSValue privateKeyValue; @@ -52,22 +51,22 @@ template<> CryptoKeyPair convertDictionary(JSGlobalObject& lexica privateKeyValue = jsUndefined(); else { privateKeyValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "privateKey"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!privateKeyValue.isUndefined()) { result.privateKey = convert>(lexicalGlobalObject, privateKeyValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } JSValue publicKeyValue; if (isNullOrUndefined) publicKeyValue = jsUndefined(); else { publicKeyValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "publicKey"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!publicKeyValue.isUndefined()) { result.publicKey = convert>(lexicalGlobalObject, publicKeyValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } @@ -81,12 +80,12 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J if (!IDLInterface::isNullValue(dictionary.privateKey)) { auto privateKeyValue = toJS>(lexicalGlobalObject, globalObject, throwScope, IDLInterface::extractValueFromNullable(dictionary.privateKey)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "privateKey"_s), privateKeyValue); } if (!IDLInterface::isNullValue(dictionary.publicKey)) { auto publicKeyValue = toJS>(lexicalGlobalObject, globalObject, throwScope, IDLInterface::extractValueFromNullable(dictionary.publicKey)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "publicKey"_s), publicKeyValue); } return result; diff --git a/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.cpp b/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.cpp index 2e5e36cae522f..d749d879c2f0d 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.cpp @@ -80,9 +80,9 @@ template<> std::optional parseEnumeration(JSGlob return std::nullopt; } -template<> const char* expectedEnumerationValues() +template<> ASCIILiteral expectedEnumerationValues() { - return "\"encrypt\", \"decrypt\", \"sign\", \"verify\", \"deriveKey\", \"deriveBits\", \"wrapKey\", \"unwrapKey\""; + return "\"encrypt\", \"decrypt\", \"sign\", \"verify\", \"deriveKey\", \"deriveBits\", \"wrapKey\", \"unwrapKey\""_s; } } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.h b/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.h index 7119cf1d125a7..1c2d280943a44 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.h +++ b/src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.h @@ -31,7 +31,7 @@ String convertEnumerationToString(CryptoKeyUsage); template<> JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, CryptoKeyUsage); template<> std::optional parseEnumeration(JSC::JSGlobalObject&, JSC::JSValue); -template<> const char* expectedEnumerationValues(); +template<> ASCIILiteral expectedEnumerationValues(); } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/JSCryptoRsaHashedKeyAlgorithm.cpp b/src/bun.js/bindings/webcrypto/JSCryptoRsaHashedKeyAlgorithm.cpp index 2a2cdb8a23d49..4b6393f9800d4 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoRsaHashedKeyAlgorithm.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoRsaHashedKeyAlgorithm.cpp @@ -32,7 +32,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -46,64 +45,64 @@ template<> CryptoRsaHashedKeyAlgorithm convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoRsaHashedKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoRsaHashedKeyAlgorithm"_s, "DOMString"_s); + return {}; } JSValue modulusLengthValue; if (isNullOrUndefined) modulusLengthValue = jsUndefined(); else { modulusLengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "modulusLength"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!modulusLengthValue.isUndefined()) { result.modulusLength = convert(lexicalGlobalObject, modulusLengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength", "CryptoRsaHashedKeyAlgorithm", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength"_s, "CryptoRsaHashedKeyAlgorithm"_s, "unsigned long"_s); + return {}; } JSValue publicExponentValue; if (isNullOrUndefined) publicExponentValue = jsUndefined(); else { publicExponentValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "publicExponent"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!publicExponentValue.isUndefined()) { result.publicExponent = convert(lexicalGlobalObject, publicExponentValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent", "CryptoRsaHashedKeyAlgorithm", "Uint8Array"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent"_s, "CryptoRsaHashedKeyAlgorithm"_s, "Uint8Array"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "CryptoRsaHashedKeyAlgorithm", "CryptoKeyAlgorithm"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "CryptoRsaHashedKeyAlgorithm"_s, "CryptoKeyAlgorithm"_s); + return {}; } return result; } @@ -116,16 +115,16 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto nameValue = toJS(lexicalGlobalObject, throwScope, dictionary.name); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "name"_s), nameValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->name, nameValue); auto modulusLengthValue = toJS(lexicalGlobalObject, throwScope, dictionary.modulusLength); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "modulusLength"_s), modulusLengthValue); auto publicExponentValue = toJS(lexicalGlobalObject, globalObject, throwScope, dictionary.publicExponent); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "publicExponent"_s), publicExponentValue); auto hashValue = toJS>(lexicalGlobalObject, globalObject, throwScope, dictionary.hash); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "hash"_s), hashValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSCryptoRsaKeyAlgorithm.cpp b/src/bun.js/bindings/webcrypto/JSCryptoRsaKeyAlgorithm.cpp index 79407e27fb09d..a75d16791cad4 100644 --- a/src/bun.js/bindings/webcrypto/JSCryptoRsaKeyAlgorithm.cpp +++ b/src/bun.js/bindings/webcrypto/JSCryptoRsaKeyAlgorithm.cpp @@ -31,7 +31,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -45,50 +44,50 @@ template<> CryptoRsaKeyAlgorithm convertDictionary(JSGlob auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } CryptoRsaKeyAlgorithm result; JSValue nameValue; if (isNullOrUndefined) nameValue = jsUndefined(); else { - nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "CryptoRsaKeyAlgorithm", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "CryptoRsaKeyAlgorithm"_s, "DOMString"_s); + return {}; } JSValue modulusLengthValue; if (isNullOrUndefined) modulusLengthValue = jsUndefined(); else { modulusLengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "modulusLength"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!modulusLengthValue.isUndefined()) { result.modulusLength = convert(lexicalGlobalObject, modulusLengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength", "CryptoRsaKeyAlgorithm", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength"_s, "CryptoRsaKeyAlgorithm"_s, "unsigned long"_s); + return {}; } JSValue publicExponentValue; if (isNullOrUndefined) publicExponentValue = jsUndefined(); else { publicExponentValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "publicExponent"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!publicExponentValue.isUndefined()) { result.publicExponent = convert(lexicalGlobalObject, publicExponentValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent", "CryptoRsaKeyAlgorithm", "Uint8Array"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent"_s, "CryptoRsaKeyAlgorithm"_s, "Uint8Array"_s); + return {}; } return result; } @@ -101,13 +100,13 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto nameValue = toJS(lexicalGlobalObject, throwScope, dictionary.name); - RETURN_IF_EXCEPTION(throwScope, { }); - result->putDirect(vm, JSC::Identifier::fromString(vm, "name"_s), nameValue); + RETURN_IF_EXCEPTION(throwScope, {}); + result->putDirect(vm, vm.propertyNames->name, nameValue); auto modulusLengthValue = toJS(lexicalGlobalObject, throwScope, dictionary.modulusLength); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "modulusLength"_s), modulusLengthValue); auto publicExponentValue = toJS(lexicalGlobalObject, globalObject, throwScope, dictionary.publicExponent); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "publicExponent"_s), publicExponentValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSEcKeyParams.cpp b/src/bun.js/bindings/webcrypto/JSEcKeyParams.cpp index 51fcfb966d499..7dfb4c136ca6d 100644 --- a/src/bun.js/bindings/webcrypto/JSEcKeyParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSEcKeyParams.cpp @@ -27,7 +27,6 @@ #include "JSDOMConvertStrings.h" #include - namespace WebCore { using namespace JSC; @@ -41,36 +40,36 @@ template<> CryptoAlgorithmEcKeyParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "EcKeyParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "EcKeyParams"_s, "DOMString"_s); + return {}; } JSValue namedCurveValue; if (isNullOrUndefined) namedCurveValue = jsUndefined(); else { namedCurveValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "namedCurve"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!namedCurveValue.isUndefined()) { result.namedCurve = convert(lexicalGlobalObject, namedCurveValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "namedCurve", "EcKeyParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "namedCurve"_s, "EcKeyParams"_s, "DOMString"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSEcdhKeyDeriveParams.cpp b/src/bun.js/bindings/webcrypto/JSEcdhKeyDeriveParams.cpp index 9005a34ff15d4..cae7acd814ed2 100644 --- a/src/bun.js/bindings/webcrypto/JSEcdhKeyDeriveParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSEcdhKeyDeriveParams.cpp @@ -29,7 +29,6 @@ #include "JSDOMConvertStrings.h" #include - namespace WebCore { using namespace JSC; @@ -43,36 +42,36 @@ template<> CryptoAlgorithmEcdhKeyDeriveParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "EcdhKeyDeriveParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "EcdhKeyDeriveParams"_s, "DOMString"_s); + return {}; } JSValue publicKeyValue; if (isNullOrUndefined) publicKeyValue = jsUndefined(); else { publicKeyValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "publicKey"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!publicKeyValue.isUndefined()) { result.publicKey = convert>(lexicalGlobalObject, publicKeyValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicKey", "EcdhKeyDeriveParams", "CryptoKey"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicKey"_s, "EcdhKeyDeriveParams"_s, "CryptoKey"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSEcdsaParams.cpp b/src/bun.js/bindings/webcrypto/JSEcdsaParams.cpp index 700b30e6eeb4a..b31e0bea2ca22 100644 --- a/src/bun.js/bindings/webcrypto/JSEcdsaParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSEcdsaParams.cpp @@ -30,7 +30,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -44,36 +43,36 @@ template<> CryptoAlgorithmEcdsaParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "EcdsaParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "EcdsaParams"_s, "DOMString"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "EcdsaParams", "(object or DOMString)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "EcdsaParams"_s, "(object or DOMString)"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSHkdfParams.cpp b/src/bun.js/bindings/webcrypto/JSHkdfParams.cpp index 3b68fa3de4b09..01d5856320ff9 100644 --- a/src/bun.js/bindings/webcrypto/JSHkdfParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSHkdfParams.cpp @@ -31,7 +31,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -45,64 +44,64 @@ template<> CryptoAlgorithmHkdfParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "HkdfParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "HkdfParams"_s, "DOMString"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "HkdfParams", "(object or DOMString)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "HkdfParams"_s, "(object or DOMString)"_s); + return {}; } JSValue infoValue; if (isNullOrUndefined) infoValue = jsUndefined(); else { infoValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "info"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!infoValue.isUndefined()) { result.info = convert>(lexicalGlobalObject, infoValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "info", "HkdfParams", "(ArrayBufferView or ArrayBuffer)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "info"_s, "HkdfParams"_s, "(ArrayBufferView or ArrayBuffer)"_s); + return {}; } JSValue saltValue; if (isNullOrUndefined) saltValue = jsUndefined(); else { saltValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "salt"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!saltValue.isUndefined()) { result.salt = convert>(lexicalGlobalObject, saltValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "salt", "HkdfParams", "(ArrayBufferView or ArrayBuffer)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "salt"_s, "HkdfParams"_s, "(ArrayBufferView or ArrayBuffer)"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp b/src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp index 2debe240913e0..48bd232531e6b 100644 --- a/src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp @@ -31,7 +31,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -45,47 +44,47 @@ template<> CryptoAlgorithmHmacKeyParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "HmacKeyParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "HmacKeyParams"_s, "DOMString"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "HmacKeyParams", "(object or DOMString)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "HmacKeyParams"_s, "(object or DOMString)"_s); + return {}; } JSValue lengthValue; if (isNullOrUndefined) lengthValue = jsUndefined(); else { lengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "length"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!lengthValue.isUndefined()) { result.length = convert>(lexicalGlobalObject, lengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp b/src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp index 9bb2644b582ef..1f7847fc9dc63 100644 --- a/src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp +++ b/src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp @@ -161,7 +161,7 @@ template<> JsonWebKey convertDictionary(JSGlobalObject& lexicalGloba result.kty = convert(lexicalGlobalObject, ktyValue); RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "kty", "JsonWebKey", "DOMString"); + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "kty"_s, "JsonWebKey"_s, "DOMString"_s); return {}; } JSValue nValue; diff --git a/src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp b/src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp index 871d8ffd9799d..157b2685e35b6 100644 --- a/src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp +++ b/src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp @@ -32,7 +32,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -46,64 +45,64 @@ template<> CryptoAlgorithmPbkdf2Params convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "Pbkdf2Params", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "Pbkdf2Params"_s, "DOMString"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "Pbkdf2Params", "(object or DOMString)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "Pbkdf2Params"_s, "(object or DOMString)"_s); + return {}; } JSValue iterationsValue; if (isNullOrUndefined) iterationsValue = jsUndefined(); else { iterationsValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "iterations"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!iterationsValue.isUndefined()) { result.iterations = convert>(lexicalGlobalObject, iterationsValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "iterations", "Pbkdf2Params", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "iterations"_s, "Pbkdf2Params"_s, "unsigned long"_s); + return {}; } JSValue saltValue; if (isNullOrUndefined) saltValue = jsUndefined(); else { saltValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "salt"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!saltValue.isUndefined()) { result.salt = convert>(lexicalGlobalObject, saltValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "salt", "Pbkdf2Params", "(ArrayBufferView or ArrayBuffer)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "salt"_s, "Pbkdf2Params"_s, "(ArrayBufferView or ArrayBuffer)"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp b/src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp index e48fba0fdf34e..25170eacc6d80 100644 --- a/src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp @@ -30,7 +30,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -44,36 +43,36 @@ template<> CryptoAlgorithmRsaHashedImportParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "RsaHashedImportParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "RsaHashedImportParams"_s, "DOMString"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "RsaHashedImportParams", "(object or DOMString)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "RsaHashedImportParams"_s, "(object or DOMString)"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp b/src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp index c5abfe52a3622..59875582cf90e 100644 --- a/src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp @@ -32,7 +32,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -46,64 +45,64 @@ template<> CryptoAlgorithmRsaHashedKeyGenParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "RsaHashedKeyGenParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "RsaHashedKeyGenParams"_s, "DOMString"_s); + return {}; } JSValue modulusLengthValue; if (isNullOrUndefined) modulusLengthValue = jsUndefined(); else { modulusLengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "modulusLength"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!modulusLengthValue.isUndefined()) { result.modulusLength = convert>(lexicalGlobalObject, modulusLengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength", "RsaHashedKeyGenParams", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength"_s, "RsaHashedKeyGenParams"_s, "unsigned long"_s); + return {}; } JSValue publicExponentValue; if (isNullOrUndefined) publicExponentValue = jsUndefined(); else { publicExponentValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "publicExponent"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!publicExponentValue.isUndefined()) { result.publicExponent = convert(lexicalGlobalObject, publicExponentValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent", "RsaHashedKeyGenParams", "Uint8Array"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent"_s, "RsaHashedKeyGenParams"_s, "Uint8Array"_s); + return {}; } JSValue hashValue; if (isNullOrUndefined) hashValue = jsUndefined(); else { hashValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "hash"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!hashValue.isUndefined()) { result.hash = convert>(lexicalGlobalObject, hashValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash", "RsaHashedKeyGenParams", "(object or DOMString)"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "hash"_s, "RsaHashedKeyGenParams"_s, "(object or DOMString)"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp b/src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp index 54644978ace66..4f338bf9d16fb 100644 --- a/src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp @@ -29,7 +29,6 @@ #include "JSDOMConvertStrings.h" #include - namespace WebCore { using namespace JSC; @@ -43,50 +42,50 @@ template<> CryptoAlgorithmRsaKeyGenParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "RsaKeyGenParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "RsaKeyGenParams"_s, "DOMString"_s); + return {}; } JSValue modulusLengthValue; if (isNullOrUndefined) modulusLengthValue = jsUndefined(); else { modulusLengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "modulusLength"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!modulusLengthValue.isUndefined()) { result.modulusLength = convert>(lexicalGlobalObject, modulusLengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength", "RsaKeyGenParams", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "modulusLength"_s, "RsaKeyGenParams"_s, "unsigned long"_s); + return {}; } JSValue publicExponentValue; if (isNullOrUndefined) publicExponentValue = jsUndefined(); else { publicExponentValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "publicExponent"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!publicExponentValue.isUndefined()) { result.publicExponent = convert(lexicalGlobalObject, publicExponentValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent", "RsaKeyGenParams", "Uint8Array"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "publicExponent"_s, "RsaKeyGenParams"_s, "Uint8Array"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp b/src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp index 16264b7d9cf65..e0d461f1f9534 100644 --- a/src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp @@ -30,7 +30,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -44,33 +43,33 @@ template<> CryptoAlgorithmRsaOaepParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "RsaOaepParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "RsaOaepParams"_s, "DOMString"_s); + return {}; } JSValue labelValue; if (isNullOrUndefined) labelValue = jsUndefined(); else { labelValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "label"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!labelValue.isUndefined()) { result.label = convert>(lexicalGlobalObject, labelValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp b/src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp index 420e17fb48105..ce10023e6cbcd 100644 --- a/src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp +++ b/src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp @@ -29,7 +29,6 @@ #include #include - namespace WebCore { using namespace JSC; @@ -43,7 +42,7 @@ template<> RsaOtherPrimesInfo convertDictionary(JSGlobalObje auto* object = isNullOrUndefined ? nullptr : value.getObject(); if (UNLIKELY(!isNullOrUndefined && !object)) { throwTypeError(&lexicalGlobalObject, throwScope); - return { }; + return {}; } RsaOtherPrimesInfo result; JSValue dValue; @@ -51,42 +50,42 @@ template<> RsaOtherPrimesInfo convertDictionary(JSGlobalObje dValue = jsUndefined(); else { dValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "d"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!dValue.isUndefined()) { result.d = convert(lexicalGlobalObject, dValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "d", "RsaOtherPrimesInfo", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "d"_s, "RsaOtherPrimesInfo"_s, "DOMString"_s); + return {}; } JSValue rValue; if (isNullOrUndefined) rValue = jsUndefined(); else { rValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "r"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!rValue.isUndefined()) { result.r = convert(lexicalGlobalObject, rValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "r", "RsaOtherPrimesInfo", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "r"_s, "RsaOtherPrimesInfo"_s, "DOMString"_s); + return {}; } JSValue tValue; if (isNullOrUndefined) tValue = jsUndefined(); else { tValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "t"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!tValue.isUndefined()) { result.t = convert(lexicalGlobalObject, tValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "t", "RsaOtherPrimesInfo", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "t"_s, "RsaOtherPrimesInfo"_s, "DOMString"_s); + return {}; } return result; } @@ -99,13 +98,13 @@ JSC::JSObject* convertDictionaryToJS(JSC::JSGlobalObject& lexicalGlobalObject, J auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype()); auto dValue = toJS(lexicalGlobalObject, throwScope, dictionary.d); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "d"_s), dValue); auto rValue = toJS(lexicalGlobalObject, throwScope, dictionary.r); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "r"_s), rValue); auto tValue = toJS(lexicalGlobalObject, throwScope, dictionary.t); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); result->putDirect(vm, JSC::Identifier::fromString(vm, "t"_s), tValue); return result; } diff --git a/src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp b/src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp index 3498ec0e90943..b93dfa2ffad42 100644 --- a/src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp +++ b/src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp @@ -28,7 +28,6 @@ #include "JSDOMConvertStrings.h" #include - namespace WebCore { using namespace JSC; @@ -42,36 +41,36 @@ template<> CryptoAlgorithmRsaPssParams convertDictionaryget(&lexicalGlobalObject, Identifier::fromString(vm, "name"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + nameValue = object->get(&lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!nameValue.isUndefined()) { result.name = convert(lexicalGlobalObject, nameValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name", "RsaPssParams", "DOMString"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "name"_s, "RsaPssParams"_s, "DOMString"_s); + return {}; } JSValue saltLengthValue; if (isNullOrUndefined) saltLengthValue = jsUndefined(); else { saltLengthValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "saltLength"_s)); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } if (!saltLengthValue.isUndefined()) { result.saltLength = convert>(lexicalGlobalObject, saltLengthValue); - RETURN_IF_EXCEPTION(throwScope, { }); + RETURN_IF_EXCEPTION(throwScope, {}); } else { - throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "saltLength", "RsaPssParams", "unsigned long"); - return { }; + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "saltLength"_s, "RsaPssParams"_s, "unsigned long"_s); + return {}; } return result; } diff --git a/src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp b/src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp index de04487afc5a4..d497ea8c9b59c 100644 --- a/src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp +++ b/src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp @@ -19,6 +19,7 @@ */ #include "config.h" +#include "wtf/Forward.h" #if ENABLE(WEB_CRYPTO) @@ -106,9 +107,9 @@ template<> std::optional parseEnumeration const char* expectedEnumerationValues() +template<> ASCIILiteral expectedEnumerationValues() { - return "\"raw\", \"spki\", \"pkcs8\", \"jwk\""; + return "\"raw\", \"spki\", \"pkcs8\", \"jwk\""_s; } // Functions @@ -269,7 +270,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_encryptBody(JS auto algorithm = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key", "SubtleCrypto", "encrypt", "CryptoKey"); }); + auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key"_s, "SubtleCrypto"_s, "encrypt"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); auto data = convert>(*lexicalGlobalObject, argument2.value()); @@ -295,7 +296,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_decryptBody(JS auto algorithm = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key", "SubtleCrypto", "decrypt", "CryptoKey"); }); + auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key"_s, "SubtleCrypto"_s, "decrypt"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); auto data = convert>(*lexicalGlobalObject, argument2.value()); @@ -321,7 +322,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_signBody(JSC:: auto algorithm = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key", "SubtleCrypto", "sign", "CryptoKey"); }); + auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key"_s, "SubtleCrypto"_s, "sign"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); auto data = convert>(*lexicalGlobalObject, argument2.value()); @@ -347,7 +348,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_verifyBody(JSC auto algorithm = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key", "SubtleCrypto", "verify", "CryptoKey"); }); + auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key"_s, "SubtleCrypto"_s, "verify"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); auto signature = convert>(*lexicalGlobalObject, argument2.value()); @@ -425,7 +426,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_deriveKeyBody( auto algorithm = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto baseKey = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "baseKey", "SubtleCrypto", "deriveKey", "CryptoKey"); }); + auto baseKey = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "baseKey"_s, "SubtleCrypto"_s, "deriveKey"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); auto derivedKeyType = convert>(*lexicalGlobalObject, argument2.value()); @@ -457,7 +458,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_deriveBitsBody auto algorithm = convert>(*lexicalGlobalObject, argument0.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto baseKey = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "baseKey", "SubtleCrypto", "deriveBits", "CryptoKey"); }); + auto baseKey = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "baseKey"_s, "SubtleCrypto"_s, "deriveBits"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); auto length = convert(*lexicalGlobalObject, argument2.value()); @@ -480,7 +481,7 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_importKeyBody( if (UNLIKELY(callFrame->argumentCount() < 5)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format", "SubtleCrypto", "importKey", expectedEnumerationValues()); }); + auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format"_s, "SubtleCrypto"_s, "importKey"_s, expectedEnumerationValues()); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); auto keyData = convert>>(*lexicalGlobalObject, argument1.value()); @@ -512,10 +513,10 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_exportKeyBody( if (UNLIKELY(callFrame->argumentCount() < 2)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format", "SubtleCrypto", "exportKey", expectedEnumerationValues()); }); + auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format"_s, "SubtleCrypto"_s, "exportKey"_s, expectedEnumerationValues()); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key", "SubtleCrypto", "exportKey", "CryptoKey"); }); + auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key"_s, "SubtleCrypto"_s, "exportKey"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, [&]() -> decltype(auto) { return impl.exportKey(WTFMove(format), *key, WTFMove(promise)); @@ -537,13 +538,13 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_wrapKeyBody(JS if (UNLIKELY(callFrame->argumentCount() < 4)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format", "SubtleCrypto", "wrapKey", expectedEnumerationValues()); }); + auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format"_s, "SubtleCrypto"_s, "wrapKey"_s, expectedEnumerationValues()); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key", "SubtleCrypto", "wrapKey", "CryptoKey"); }); + auto key = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 1, "key"_s, "SubtleCrypto"_s, "wrapKey"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); - auto wrappingKey = convert>(*lexicalGlobalObject, argument2.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 2, "wrappingKey", "SubtleCrypto", "wrapKey", "CryptoKey"); }); + auto wrappingKey = convert>(*lexicalGlobalObject, argument2.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 2, "wrappingKey"_s, "SubtleCrypto"_s, "wrapKey"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument3 = callFrame->uncheckedArgument(3); auto wrapAlgorithm = convert>(*lexicalGlobalObject, argument3.value()); @@ -566,13 +567,13 @@ static inline JSC::EncodedJSValue jsSubtleCryptoPrototypeFunction_unwrapKeyBody( if (UNLIKELY(callFrame->argumentCount() < 7)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format", "SubtleCrypto", "unwrapKey", expectedEnumerationValues()); }); + auto format = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeEnumError(lexicalGlobalObject, scope, 0, "format"_s, "SubtleCrypto"_s, "unwrapKey"_s, expectedEnumerationValues()); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); auto wrappedKey = convert>(*lexicalGlobalObject, argument1.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2); - auto unwrappingKey = convert>(*lexicalGlobalObject, argument2.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 2, "unwrappingKey", "SubtleCrypto", "unwrapKey", "CryptoKey"); }); + auto unwrappingKey = convert>(*lexicalGlobalObject, argument2.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 2, "unwrappingKey"_s, "SubtleCrypto"_s, "unwrapKey"_s, "CryptoKey"_s); }); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument3 = callFrame->uncheckedArgument(3); auto unwrapAlgorithm = convert>(*lexicalGlobalObject, argument3.value()); @@ -609,7 +610,7 @@ void JSSubtleCrypto::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, makeString("url "_s, thisObject->scriptExecutionContext()->url().string())); Base::analyzeHeap(cell, analyzer); } @@ -649,18 +650,18 @@ JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObj if constexpr (std::is_polymorphic_v) { #if ENABLE(BINDING_INTEGRITY) - const void* actualVTablePointer = getVTablePointer(impl.ptr()); + // const void* actualVTablePointer = getVTablePointer(impl.ptr()); #if PLATFORM(WIN) void* expectedVTablePointer = __identifier("??_7SubtleCrypto@WebCore@@6B@"); #else - void* expectedVTablePointer = &_ZTVN7WebCore12SubtleCryptoE[2]; + // void* expectedVTablePointer = &_ZTVN7WebCore12SubtleCryptoE[2]; #endif // If you hit this assertion you either have a use after free bug, or // SubtleCrypto has subclasses. If SubtleCrypto has subclasses that get passed // to toJS() we currently require SubtleCrypto you to opt out of binding hardening // by adding the SkipVTableValidation attribute to the interface IDL definition - RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); + // RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif } return createWrapper(globalObject, WTFMove(impl)); diff --git a/src/bun.js/bindings/webcrypto/JSSubtleCrypto.h b/src/bun.js/bindings/webcrypto/JSSubtleCrypto.h index a4057c957eeea..b3a26c8727a4c 100644 --- a/src/bun.js/bindings/webcrypto/JSSubtleCrypto.h +++ b/src/bun.js/bindings/webcrypto/JSSubtleCrypto.h @@ -97,7 +97,7 @@ String convertEnumerationToString(SubtleCrypto::KeyFormat); template<> JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, SubtleCrypto::KeyFormat); template<> std::optional parseEnumeration(JSC::JSGlobalObject&, JSC::JSValue); -template<> const char* expectedEnumerationValues(); +template<> ASCIILiteral expectedEnumerationValues(); } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/OpenSSLCryptoUniquePtr.h b/src/bun.js/bindings/webcrypto/OpenSSLCryptoUniquePtr.h index b87aa561a1744..0ce30dee6b265 100644 --- a/src/bun.js/bindings/webcrypto/OpenSSLCryptoUniquePtr.h +++ b/src/bun.js/bindings/webcrypto/OpenSSLCryptoUniquePtr.h @@ -37,14 +37,14 @@ namespace WebCore { -template +template struct OpenSSLCryptoPtrDeleter { void operator()(T* ptr) const = delete; }; -template +template using OpenSSLCryptoPtr = std::unique_ptr>; - +// clang-format off #define DEFINE_OPENSSL_CRYPTO_PTR_FULL(alias, typeName, deleterFunc) \ template<> struct OpenSSLCryptoPtrDeleter { \ void operator()(typeName* ptr) const { \ @@ -55,7 +55,7 @@ using OpenSSLCryptoPtr = std::unique_ptr>; #define DEFINE_OPENSSL_CRYPTO_PTR(alias, typeName, deleterFunc) \ DEFINE_OPENSSL_CRYPTO_PTR_FULL(alias, typeName, deleterFunc(ptr)) - +// clang-format on DEFINE_OPENSSL_CRYPTO_PTR(EvpCipherCtxPtr, EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) DEFINE_OPENSSL_CRYPTO_PTR(EvpDigestCtxPtr, EVP_MD_CTX, EVP_MD_CTX_free) DEFINE_OPENSSL_CRYPTO_PTR(EvpPKeyPtr, EVP_PKEY, EVP_PKEY_free) diff --git a/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp b/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp index 2996cae2ab431..f37b50334e8c9 100644 --- a/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp +++ b/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp @@ -84,7 +84,7 @@ Vector convertToBytesExpand(const BIGNUM* bignum, size_t minimumBufferS { int length = BN_num_bytes(bignum); if (length < 0) - return { }; + return {}; size_t bufferSize = std::max(length, minimumBufferSize); @@ -134,5 +134,4 @@ AESKey::~AESKey() } // namespace WebCore - #endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/OpenSSLUtilities.h b/src/bun.js/bindings/webcrypto/OpenSSLUtilities.h index 53d884c582915..6adffe4b7964a 100644 --- a/src/bun.js/bindings/webcrypto/OpenSSLUtilities.h +++ b/src/bun.js/bindings/webcrypto/OpenSSLUtilities.h @@ -49,6 +49,7 @@ BIGNUMPtr convertToBigNumber(const Vector& bytes); class AESKey { WTF_MAKE_NONCOPYABLE(AESKey); + public: AESKey() = default; ~AESKey(); @@ -56,6 +57,7 @@ class AESKey { bool setKey(const Vector& key, int enc /* AES_ENCRYPT or AES_DECRYPT */); AES_KEY* key() { return &m_key; } + private: AES_KEY m_key; }; diff --git a/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp b/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp index 8a1010b997ac3..961b714f65bce 100644 --- a/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp +++ b/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp @@ -111,7 +111,7 @@ static ExceptionOr> normalizeCryptoAl if (std::holds_alternative(algorithmIdentifier)) { auto newParams = Strong(vm, constructEmptyObject(&state)); - newParams->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, std::get(algorithmIdentifier))); + newParams->putDirect(vm, vm.propertyNames->name, jsString(vm, std::get(algorithmIdentifier))); return normalizeCryptoAlgorithmParameters(state, newParams, operation); } @@ -268,10 +268,10 @@ static ExceptionOr> normalizeCryptoAl switch (*identifier) { case CryptoAlgorithmIdentifier::ECDH: { // Remove this hack once https://bugs.webkit.org/show_bug.cgi?id=169333 is fixed. - JSValue nameValue = value.get()->get(&state, Identifier::fromString(vm, "name"_s)); + JSValue nameValue = value.get()->get(&state, vm.propertyNames->name); JSValue publicValue = value.get()->get(&state, Identifier::fromString(vm, "public"_s)); JSObject* newValue = constructEmptyObject(&state); - newValue->putDirect(vm, Identifier::fromString(vm, "name"_s), nameValue); + newValue->putDirect(vm, vm.propertyNames->name, nameValue); newValue->putDirect(vm, Identifier::fromString(vm, "publicKey"_s), publicValue); auto params = convertDictionary(state, newValue); @@ -357,7 +357,10 @@ static ExceptionOr> normalizeCryptoAl case CryptoAlgorithmIdentifier::SHA_384: case CryptoAlgorithmIdentifier::SHA_512: return Exception { NotSupportedError }; + case CryptoAlgorithmIdentifier::None: + return Exception { NotSupportedError }; } + break; case Operations::WrapKey: case Operations::UnwrapKey: diff --git a/src/bun.js/bindings/webcrypto/SubtleCrypto.h b/src/bun.js/bindings/webcrypto/SubtleCrypto.h index e9ef23e6a5b60..dbfee4649bf5f 100644 --- a/src/bun.js/bindings/webcrypto/SubtleCrypto.h +++ b/src/bun.js/bindings/webcrypto/SubtleCrypto.h @@ -50,7 +50,7 @@ class BufferSource; class CryptoKey; class DeferredPromise; -enum class CryptoAlgorithmIdentifier; +enum class CryptoAlgorithmIdentifier : uint8_t; enum class CryptoKeyUsage; class SubtleCrypto : public ContextDestructionObserver, public RefCounted, public CanMakeWeakPtr { diff --git a/src/bun.js/bindings/windows/musl-memmem.h b/src/bun.js/bindings/windows/musl-memmem.h index c036e9e934c05..b0d2f38fecaed 100644 --- a/src/bun.js/bindings/windows/musl-memmem.h +++ b/src/bun.js/bindings/windows/musl-memmem.h @@ -1 +1 @@ -extern "C" void *memmem(const void *h0, size_t k, const void *n0, size_t l); \ No newline at end of file +extern "C" void* memmem(const void* h0, size_t k, const void* n0, size_t l); \ No newline at end of file diff --git a/src/bun.js/bindings/workaround-missing-symbols.cpp b/src/bun.js/bindings/workaround-missing-symbols.cpp index 5db3610ca4955..21cb1964391db 100644 --- a/src/bun.js/bindings/workaround-missing-symbols.cpp +++ b/src/bun.js/bindings/workaround-missing-symbols.cpp @@ -1,4 +1,5 @@ + #if defined(WIN32) #include @@ -266,96 +267,28 @@ extern "C" int __wrap_mknodat(int dirfd, const char* path, __mode_t mode, __dev_ // macOS #if defined(__APPLE__) +#include #include #include +#include +#include +#include "headers.h" -extern "C" int pthread_self_is_exiting_np() -{ - static void* pthread_self_is_exiting_np_ptr = nullptr; - static bool pthread_self_is_exiting_np_ptr_initialized = false; - if (UNLIKELY(!pthread_self_is_exiting_np_ptr_initialized)) { - pthread_self_is_exiting_np_ptr_initialized = true; - pthread_self_is_exiting_np_ptr = dlsym(RTLD_DEFAULT, "pthread_self_is_exiting_np"); - } - - if (UNLIKELY(pthread_self_is_exiting_np_ptr == nullptr)) - return 0; - - return ((int (*)())pthread_self_is_exiting_np_ptr)(); -} - -extern "C" int posix_spawn_file_actions_addchdir_np( - void* file_actions, - const char* path) +void std::__libcpp_verbose_abort(char const* format, ...) { - static void* posix_spawn_file_actions_addchdir_np_ptr = nullptr; - static bool posix_spawn_file_actions_addchdir_np_ptr_initialized = false; - if (UNLIKELY(!posix_spawn_file_actions_addchdir_np_ptr_initialized)) { - posix_spawn_file_actions_addchdir_np_ptr_initialized = true; - posix_spawn_file_actions_addchdir_np_ptr = dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np"); - } - - if (UNLIKELY(posix_spawn_file_actions_addchdir_np_ptr == nullptr)) - return 0; - - return ((int (*)(void*, const char*))posix_spawn_file_actions_addchdir_np_ptr)(file_actions, path); -} - -extern "C" int posix_spawn_file_actions_addinherit_np(void* ptr, - int status) -{ - static void* posix_spawn_file_actions_addinherit_np_ptr = nullptr; - static bool posix_spawn_file_actions_addinherit_np_ptr_initialized = false; - if (UNLIKELY(!posix_spawn_file_actions_addinherit_np_ptr_initialized)) { - posix_spawn_file_actions_addinherit_np_ptr_initialized = true; - posix_spawn_file_actions_addinherit_np_ptr = dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addinherit_np"); - } - - if (UNLIKELY(posix_spawn_file_actions_addinherit_np_ptr == nullptr)) - return 0; + va_list list; + va_start(list, format); + char buffer[1024]; + size_t len = vsnprintf(buffer, sizeof(buffer), format, list); + va_end(list); - return ((int (*)(void*, int))posix_spawn_file_actions_addinherit_np_ptr)(ptr, status); + Bun__panic(buffer, len); } -extern "C" int posix_spawn_file_actions_addfchdir_np(void* ptr, - int fd) -{ - static void* posix_spawn_file_actions_addfchdir_np_ptr = nullptr; - static bool posix_spawn_file_actions_addfchdir_np_ptr_initialized = false; - if (UNLIKELY(!posix_spawn_file_actions_addfchdir_np_ptr_initialized)) { - posix_spawn_file_actions_addfchdir_np_ptr_initialized = true; - posix_spawn_file_actions_addfchdir_np_ptr = dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addfchdir_np"); - } - - if (UNLIKELY(posix_spawn_file_actions_addfchdir_np_ptr == nullptr)) - return 0; - - return ((int (*)(void*, int))posix_spawn_file_actions_addfchdir_np_ptr)(ptr, fd); -} - -extern "C" int __ulock_wait(uint32_t operation, void* addr, uint64_t value, - uint32_t timeout_microseconds); /* timeout is specified in microseconds */ - -// https://github.com/oven-sh/bun/pull/2426#issuecomment-1532343394 -extern "C" int __ulock_wait2(uint32_t operation, void* addr, uint64_t value, - uint64_t timeout_ns, uint64_t value2) -{ - static void* __ulock_wait2_ptr = nullptr; - static bool __ulock_wait2_ptr_initialized = false; - if (UNLIKELY(!__ulock_wait2_ptr_initialized)) { - __ulock_wait2_ptr_initialized = true; - __ulock_wait2_ptr = dlsym(RTLD_DEFAULT, "__ulock_wait2"); - } - - if (UNLIKELY(__ulock_wait2_ptr == nullptr)) { - uint64_t timeout = timeout_ns / 1000; - uint32_t timeout_us = static_cast(timeout > UINT32_MAX ? UINT32_MAX : timeout); - return __ulock_wait(operation, addr, value, timeout_us); - } - - return ((int (*)(uint32_t, void*, uint64_t, uint64_t, uint64_t))__ulock_wait2_ptr)(operation, addr, value, timeout_ns, value2); -} +#endif +#ifndef U_SHOW_CPLUSPLUS_API +#define U_SHOW_CPLUSPLUS_API 0 #endif #include diff --git a/src/bun.js/bindings/wtf-bindings.cpp b/src/bun.js/bindings/wtf-bindings.cpp index 5bb26fbe55162..848e7d143d1e7 100644 --- a/src/bun.js/bindings/wtf-bindings.cpp +++ b/src/bun.js/bindings/wtf-bindings.cpp @@ -5,7 +5,7 @@ #include #include -#include "simdutf.h" +#include "wtf/SIMDUTF.h" #if OS(WINDOWS) #include #endif @@ -217,9 +217,9 @@ size_t toISOString(JSC::VM& vm, double date, char in[64]) GregorianDateTime gregorianDateTime; vm.dateCache.msToGregorianDateTime(date, WTF::TimeType::UTCTime, gregorianDateTime); - // Maximum amount of space we need in buffer: 7 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) - // 6 for formatting and one for null termination = 28. We add one extra character to allow us to force null termination. - char buffer[28]; + // Maximum amount of space we need in buffer: 8 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) + // 6 for formatting and one for null termination = 29. + char buffer[29]; // If the year is outside the bounds of 0 and 9999 inclusive we want to use the extended year format (ES 15.9.1.15.1). int ms = static_cast(fmod(date, msPerSecond)); if (ms < 0) diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 0b51c8a32fff7..ca50477234545 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -82,7 +82,7 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { } pub fn deinit(this: *This) void { - this.promise.strong.deinit(); + this.promise.deinit(); this.destroy(); } }; @@ -319,8 +319,11 @@ pub const JSCScheduler = struct { export fn Bun__queueJSCDeferredWorkTaskConcurrently(jsc_vm: *VirtualMachine, task: *JSCScheduler.JSCDeferredWorkTask) void { JSC.markBinding(@src()); var loop = jsc_vm.eventLoop(); - var concurrent_task = bun.default_allocator.create(ConcurrentTask) catch bun.outOfMemory(); - loop.enqueueTaskConcurrent(concurrent_task.from(task, .auto_deinit)); + loop.enqueueTaskConcurrent(ConcurrentTask.new(.{ + .task = Task.init(task), + .next = null, + .auto_delete = true, + })); } comptime { @@ -379,6 +382,8 @@ const Lchown = JSC.Node.Async.lchown; const Unlink = JSC.Node.Async.unlink; const BrotliDecoder = JSC.API.BrotliDecoder; const BrotliEncoder = JSC.API.BrotliEncoder; +const ZlibDecoder = JSC.API.ZlibDecoder; +const ZlibEncoder = JSC.API.ZlibEncoder; const ShellGlobTask = bun.shell.interpret.Interpreter.Expansion.ShellGlobTask; const ShellRmTask = bun.shell.Interpreter.Builtin.Rm.ShellRmTask; @@ -399,6 +404,8 @@ const ProcessWaiterThreadTask = if (Environment.isPosix) bun.spawn.WaiterThread. const ProcessMiniEventLoopWaiterThreadTask = if (Environment.isPosix) bun.spawn.WaiterThread.ProcessMiniEventLoopQueue.ResultTask else opaque {}; const ShellAsyncSubprocessDone = bun.shell.Interpreter.Cmd.ShellAsyncSubprocessDone; const RuntimeTranspilerStore = JSC.RuntimeTranspilerStore; +const ServerAllConnectionsClosedTask = @import("./api/server.zig").ServerAllConnectionsClosedTask; + // Task.get(ReadFileTask) -> ?ReadFileTask pub const Task = TaggedPointerUnion(.{ FetchTasklet, @@ -461,6 +468,8 @@ pub const Task = TaggedPointerUnion(.{ Unlink, BrotliEncoder, BrotliDecoder, + ZlibEncoder, + ZlibDecoder, ShellGlobTask, ShellRmTask, ShellRmDirTask, @@ -476,8 +485,12 @@ pub const Task = TaggedPointerUnion(.{ TimerObject, bun.shell.Interpreter.Builtin.Yes.YesTask, + bun.kit.DevServer.BundleTask, + bun.kit.DevServer.HotReloadTask, + ProcessWaiterThreadTask, RuntimeTranspilerStore, + ServerAllConnectionsClosedTask, }); const UnboundedQueue = @import("./unbounded_queue.zig").UnboundedQueue; pub const ConcurrentTask = struct { @@ -486,19 +499,18 @@ pub const ConcurrentTask = struct { auto_delete: bool = false, pub const Queue = UnboundedQueue(ConcurrentTask, .next); + pub usingnamespace bun.New(@This()); pub const AutoDeinit = enum { manual_deinit, auto_deinit, }; pub fn create(task: Task) *ConcurrentTask { - const created = bun.default_allocator.create(ConcurrentTask) catch bun.outOfMemory(); - created.* = .{ + return ConcurrentTask.new(.{ .task = task, .next = null, .auto_delete = true, - }; - return created; + }); } pub fn createFrom(task: anytype) *ConcurrentTask { @@ -769,7 +781,7 @@ pub const EventLoop = struct { waker: ?Waker = null, forever_timer: ?*uws.Timer = null, deferred_tasks: DeferredTaskQueue = .{}, - uws_loop: if (Environment.isWindows) *uws.Loop else void = undefined, + uws_loop: if (Environment.isWindows) ?*uws.Loop else void = if (Environment.isWindows) null else {}, debug: Debug = .{}, entered_event_loop_count: isize = 0, @@ -869,14 +881,11 @@ pub const EventLoop = struct { this.enter(); defer this.exit(); - const result = callback.callWithThis(globalObject, thisValue, arguments); - - if (result.toError()) |err| { - _ = this.virtual_machine.uncaughtException(globalObject, err, false); - } + _ = callback.call(globalObject, thisValue, arguments) catch |err| + globalObject.reportActiveExceptionAsUnhandled(err); } - pub fn tickQueueWithCount(this: *EventLoop, comptime queue_name: []const u8) u32 { + fn tickQueueWithCount(this: *EventLoop, virtual_machine: *VirtualMachine, comptime queue_name: []const u8) u32 { var global = this.global; const global_vm = global.vm(); var counter: usize = 0; @@ -1021,6 +1030,13 @@ pub const EventLoop = struct { // special case: we return return 0; }, + @field(Task.Tag, @typeName(bun.kit.DevServer.HotReloadTask)) => { + const transform_task = task.get(bun.kit.DevServer.HotReloadTask).?; + transform_task.*.run(); + transform_task.deinit(); + // special case: we return + return 0; + }, @field(Task.Tag, typeBaseName(@typeName(FSWatchTask))) => { var transform_task: *FSWatchTask = task.get(FSWatchTask).?; transform_task.*.run(); @@ -1039,7 +1055,7 @@ pub const EventLoop = struct { any.run(global); }, @field(Task.Tag, typeBaseName(@typeName(PollPendingModulesTask))) => { - this.virtual_machine.modules.onPoll(); + virtual_machine.modules.onPoll(); }, @field(Task.Tag, typeBaseName(@typeName(GetAddrInfoRequestTask))) => { if (Environment.os == .windows) @panic("This should not be reachable on Windows"); @@ -1216,6 +1232,14 @@ pub const EventLoop = struct { var any: *BrotliDecoder = task.get(BrotliDecoder).?; any.runFromJSThread(); }, + @field(Task.Tag, typeBaseName(@typeName(ZlibEncoder))) => { + var any: *ZlibEncoder = task.get(ZlibEncoder).?; + any.runFromJSThread(); + }, + @field(Task.Tag, typeBaseName(@typeName(ZlibDecoder))) => { + var any: *ZlibDecoder = task.get(ZlibDecoder).?; + any.runFromJSThread(); + }, @field(Task.Tag, typeBaseName(@typeName(ProcessWaiterThreadTask))) => { bun.markPosixOnly(); var any: *ProcessWaiterThreadTask = task.get(ProcessWaiterThreadTask).?; @@ -1227,7 +1251,14 @@ pub const EventLoop = struct { }, @field(Task.Tag, typeBaseName(@typeName(TimerObject))) => { var any: *TimerObject = task.get(TimerObject).?; - any.runImmediateTask(this.virtual_machine); + any.runImmediateTask(virtual_machine); + }, + @field(Task.Tag, typeBaseName(@typeName(ServerAllConnectionsClosedTask))) => { + var any: *ServerAllConnectionsClosedTask = task.get(ServerAllConnectionsClosedTask).?; + any.runFromJSThread(virtual_machine); + }, + @field(Task.Tag, typeBaseName(@typeName(bun.kit.DevServer.BundleTask))) => { + task.get(bun.kit.DevServer.BundleTask).?.completeOnMainThread(); }, else => if (Environment.allow_assert) { @@ -1245,20 +1276,19 @@ pub const EventLoop = struct { return @as(u32, @truncate(counter)); } - pub fn tickWithCount(this: *EventLoop) u32 { - return this.tickQueueWithCount("tasks"); + fn tickWithCount(this: *EventLoop, virtual_machine: *VirtualMachine) u32 { + return this.tickQueueWithCount(virtual_machine, "tasks"); } - pub fn tickImmediateTasks(this: *EventLoop) void { - _ = this.tickQueueWithCount("immediate_tasks"); + pub fn tickImmediateTasks(this: *EventLoop, virtual_machine: *VirtualMachine) void { + _ = this.tickQueueWithCount(virtual_machine, "immediate_tasks"); } - pub fn tickConcurrent(this: *EventLoop) void { + fn tickConcurrent(this: *EventLoop) void { _ = this.tickConcurrentWithCount(); } - pub fn tickConcurrentWithCount(this: *EventLoop) usize { - JSC.markBinding(@src()); + fn updateCounts(this: *EventLoop) void { const delta = this.concurrent_ref.swap(0, .monotonic); const loop = this.virtual_machine.event_loop_handle.?; if (comptime Environment.isWindows) { @@ -1276,6 +1306,10 @@ pub const EventLoop = struct { loop.active -= @intCast(-delta); } } + } + + pub fn tickConcurrentWithCount(this: *EventLoop) usize { + this.updateCounts(); var concurrent = this.concurrent_tasks.popBatch(); const count = concurrent.count; @@ -1296,8 +1330,8 @@ pub const EventLoop = struct { while (iter.next()) |task| { if (to_destroy) |dest| { - bun.default_allocator.destroy(dest); to_destroy = null; + dest.destroy(); } if (task.auto_delete) { @@ -1311,7 +1345,7 @@ pub const EventLoop = struct { } if (to_destroy) |dest| { - bun.default_allocator.destroy(dest); + dest.destroy(); } return this.tasks.count - start_count; @@ -1319,7 +1353,7 @@ pub const EventLoop = struct { inline fn usocketsLoop(this: *const EventLoop) *uws.Loop { if (comptime Environment.isWindows) { - return this.uws_loop; + return this.uws_loop.?; } return this.virtual_machine.event_loop_handle.?; @@ -1330,7 +1364,7 @@ pub const EventLoop = struct { var loop = this.usocketsLoop(); this.flushImmediateQueue(); - this.tickImmediateTasks(); + this.tickImmediateTasks(ctx); if (comptime Environment.isPosix) { // Some tasks need to keep the event loop alive for one more tick. @@ -1421,7 +1455,7 @@ pub const EventLoop = struct { var loop = this.usocketsLoop(); var ctx = this.virtual_machine; this.flushImmediateQueue(); - this.tickImmediateTasks(); + this.tickImmediateTasks(ctx); if (comptime Environment.isPosix) { const pending_unref = ctx.pending_unref_counter; @@ -1470,7 +1504,7 @@ pub const EventLoop = struct { const global_vm = ctx.jsc; while (true) { - while (this.tickWithCount() > 0) : (this.global.handleRejectedPromises()) { + while (this.tickWithCount(ctx) > 0) : (this.global.handleRejectedPromises()) { this.tickConcurrent(); } else { this.drainMicrotasksWithGlobal(global, global_vm); @@ -1480,7 +1514,7 @@ pub const EventLoop = struct { break; } - while (this.tickWithCount() > 0) { + while (this.tickWithCount(ctx) > 0) { this.tickConcurrent(); } @@ -1490,11 +1524,11 @@ pub const EventLoop = struct { pub fn waitForPromise(this: *EventLoop, promise: JSC.AnyPromise) void { switch (promise.status(this.virtual_machine.jsc)) { - JSC.JSPromise.Status.Pending => { - while (promise.status(this.virtual_machine.jsc) == .Pending) { + .pending => { + while (promise.status(this.virtual_machine.jsc) == .pending) { this.tick(); - if (promise.status(this.virtual_machine.jsc) == .Pending) { + if (promise.status(this.virtual_machine.jsc) == .pending) { this.autoTick(); } } @@ -1506,11 +1540,11 @@ pub const EventLoop = struct { pub fn waitForPromiseWithTermination(this: *EventLoop, promise: JSC.AnyPromise) void { const worker = this.virtual_machine.worker orelse @panic("EventLoop.waitForPromiseWithTermination: worker is not initialized"); switch (promise.status(this.virtual_machine.jsc)) { - JSC.JSPromise.Status.Pending => { - while (!worker.hasRequestedTerminate() and promise.status(this.virtual_machine.jsc) == .Pending) { + .pending => { + while (!worker.hasRequestedTerminate() and promise.status(this.virtual_machine.jsc) == .pending) { this.tick(); - if (!worker.hasRequestedTerminate() and promise.status(this.virtual_machine.jsc) == .Pending) { + if (!worker.hasRequestedTerminate() and promise.status(this.virtual_machine.jsc) == .pending) { this.autoTick(); } } @@ -1567,7 +1601,9 @@ pub const EventLoop = struct { pub fn wakeup(this: *EventLoop) void { if (comptime Environment.isWindows) { - this.uws_loop.wakeup(); + if (this.uws_loop) |loop| { + loop.wakeup(); + } return; } @@ -1576,25 +1612,31 @@ pub const EventLoop = struct { } } pub fn enqueueTaskConcurrent(this: *EventLoop, task: *ConcurrentTask) void { - JSC.markBinding(@src()); if (comptime Environment.allow_assert) { if (this.virtual_machine.has_terminated) { @panic("EventLoop.enqueueTaskConcurrent: VM has terminated"); } } + if (comptime Environment.isDebug) { + log("enqueueTaskConcurrent({s})", .{task.task.typeName() orelse "[unknown]"}); + } + this.concurrent_tasks.push(task); this.wakeup(); } pub fn enqueueTaskConcurrentBatch(this: *EventLoop, batch: ConcurrentTask.Queue.Batch) void { - JSC.markBinding(@src()); if (comptime Environment.allow_assert) { if (this.virtual_machine.has_terminated) { @panic("EventLoop.enqueueTaskConcurrent: VM has terminated"); } } + if (comptime Environment.isDebug) { + log("enqueueTaskConcurrentBatch({d})", .{batch.count}); + } + this.concurrent_tasks.pushBatch(batch.front.?, batch.last.?, batch.count); this.wakeup(); } @@ -1789,7 +1831,7 @@ pub const MiniEventLoop = struct { pub fn filePolls(this: *MiniEventLoop) *Async.FilePoll.Store { return this.file_polls_ orelse { this.file_polls_ = this.allocator.create(Async.FilePoll.Store) catch bun.outOfMemory(); - this.file_polls_.?.* = Async.FilePoll.Store.init(this.allocator); + this.file_polls_.?.* = Async.FilePoll.Store.init(); return this.file_polls_.?; }; } @@ -1922,9 +1964,8 @@ pub const MiniEventLoop = struct { pub fn stderr(this: *MiniEventLoop) *JSC.WebCore.Blob.Store { return this.stderr_store orelse brk: { - const store = bun.default_allocator.create(JSC.WebCore.Blob.Store) catch bun.outOfMemory(); var mode: bun.Mode = 0; - const fd = if (comptime Environment.isWindows) bun.FDImpl.fromUV(2).encode() else bun.STDERR_FD; + const fd = if (Environment.isWindows) bun.FDImpl.fromUV(2).encode() else bun.STDERR_FD; switch (bun.sys.fstat(fd)) { .result => |stat| { @@ -1933,7 +1974,7 @@ pub const MiniEventLoop = struct { .err => {}, } - store.* = JSC.WebCore.Blob.Store{ + const store = JSC.WebCore.Blob.Store.new(.{ .ref_count = std.atomic.Value(u32).init(2), .allocator = bun.default_allocator, .data = .{ @@ -1945,7 +1986,7 @@ pub const MiniEventLoop = struct { .mode = mode, }, }, - }; + }); this.stderr_store = store; break :brk store; @@ -1954,7 +1995,6 @@ pub const MiniEventLoop = struct { pub fn stdout(this: *MiniEventLoop) *JSC.WebCore.Blob.Store { return this.stdout_store orelse brk: { - const store = bun.default_allocator.create(JSC.WebCore.Blob.Store) catch bun.outOfMemory(); var mode: bun.Mode = 0; const fd = if (Environment.isWindows) bun.FDImpl.fromUV(1).encode() else bun.STDOUT_FD; @@ -1965,7 +2005,7 @@ pub const MiniEventLoop = struct { .err => {}, } - store.* = JSC.WebCore.Blob.Store{ + const store = JSC.WebCore.Blob.Store.new(.{ .ref_count = std.atomic.Value(u32).init(2), .allocator = bun.default_allocator, .data = .{ @@ -1977,7 +2017,7 @@ pub const MiniEventLoop = struct { .mode = mode, }, }, - }; + }); this.stdout_store = store; break :brk store; diff --git a/src/bun.js/ipc.zig b/src/bun.js/ipc.zig index 1816ddca5f6d0..cf8d53a869697 100644 --- a/src/bun.js/ipc.zig +++ b/src/bun.js/ipc.zig @@ -12,6 +12,8 @@ const JSC = bun.JSC; const JSValue = JSC.JSValue; const JSGlobalObject = JSC.JSGlobalObject; +const node_cluster_binding = @import("./node/node_cluster_binding.zig"); + pub const log = Output.scoped(.IPC, false); /// Mode of Inter-Process Communication. @@ -23,7 +25,7 @@ pub const Mode = enum { /// This must match the behavior of node.js, and supports bun <--> node.js/etc communication. json, - const Map = std.StaticStringMap(Mode).initComptime(.{ + const Map = bun.ComptimeStringMap(Mode, .{ .{ "advanced", .advanced }, .{ "json", .json }, }); @@ -36,6 +38,7 @@ pub const Mode = enum { pub const DecodedIPCMessage = union(enum) { version: u32, data: JSValue, + internal: JSValue, }; pub const DecodeIPCMessageResult = struct { @@ -64,6 +67,7 @@ const advanced = struct { pub const IPCMessageType = enum(u8) { Version = 1, SerializedMessage = 2, + SerializedInternalMessage = 3, _, }; @@ -83,7 +87,7 @@ const advanced = struct { log("Received IPC message type {d} ({s}) len {d}", .{ @intFromEnum(message_type), - std.enums.tagName(IPCMessageType, message_type) orelse "unknown", + bun.tagName(IPCMessageType, message_type) orelse "unknown", message_len, }); @@ -112,7 +116,25 @@ const advanced = struct { .message = .{ .data = deserialized }, }; }, - else => { + .SerializedInternalMessage => { + if (data.len < (header_length + message_len)) { + log("Not enough bytes to decode IPC message body of len {d}, have {d} bytes", .{ message_len, data.len }); + return IPCDecodeError.NotEnoughBytes; + } + + const message = data[header_length .. header_length + message_len]; + const deserialized = JSValue.deserialize(message, global); + + if (deserialized == .zero) { + return IPCDecodeError.InvalidFormat; + } + + return .{ + .bytes_consumed = header_length + message_len, + .message = .{ .internal = deserialized }, + }; + }, + _ => { return IPCDecodeError.InvalidFormat; }, } @@ -139,6 +161,24 @@ const advanced = struct { return payload_length; } + + pub fn serializeInternal(_: *IPCData, writer: anytype, global: *JSC.JSGlobalObject, value: JSValue) !usize { + const serialized = value.serialize(global) orelse + return IPCSerializationError.SerializationFailed; + defer serialized.deinit(); + + const size: u32 = @intCast(serialized.data.len); + + const payload_length: usize = @sizeOf(IPCMessageType) + @sizeOf(u32) + size; + + try writer.ensureUnusedCapacity(payload_length); + + writer.writeTypeAsBytesAssumeCapacity(IPCMessageType, .SerializedInternalMessage); + writer.writeTypeAsBytesAssumeCapacity(u32, size); + writer.writeAssumeCapacity(serialized.data); + + return payload_length; + } }; const json = struct { @@ -150,12 +190,28 @@ const json = struct { return &.{}; } + // In order to not have to do a property lookup json messages sent from Bun will have a single u8 prepended to them + // to be able to distinguish whether it is a regular json message or an internal one for cluster ipc communication. + // 1 is regular + // 2 is internal + pub fn decodeIPCMessage( data: []const u8, globalThis: *JSC.JSGlobalObject, ) IPCDecodeError!DecodeIPCMessageResult { if (bun.strings.indexOfChar(data, '\n')) |idx| { - const json_data = data[0..idx]; + var kind = data[0]; + var json_data = data[1..idx]; + + switch (kind) { + 1, 2 => {}, + else => { + // if the message being recieved is from a node process then it wont have the leading marker byte + // assume full message will be json + kind = 1; + json_data = data[0..idx]; + }, + } const is_ascii = bun.strings.isAllASCII(json_data); var was_ascii_string_freed = false; @@ -176,9 +232,16 @@ const json = struct { const deserialized = str.toJSByParseJSON(globalThis); - return .{ - .bytes_consumed = idx + 1, - .message = .{ .data = deserialized }, + return switch (kind) { + 1 => .{ + .bytes_consumed = idx + 1, + .message = .{ .data = deserialized }, + }, + 2 => .{ + .bytes_consumed = idx + 1, + .message = .{ .internal = deserialized }, + }, + else => @panic("invalid ipc json message kind this is a bug in Bun."), }; } return IPCDecodeError.NotEnoughBytes; @@ -197,12 +260,35 @@ const json = struct { const slice = str.slice(); - try writer.ensureUnusedCapacity(slice.len + 1); + try writer.ensureUnusedCapacity(1 + slice.len + 1); + writer.writeAssumeCapacity(&.{1}); writer.writeAssumeCapacity(slice); writer.writeAssumeCapacity("\n"); - return slice.len + 1; + return 1 + slice.len + 1; + } + + pub fn serializeInternal(_: *IPCData, writer: anytype, global: *JSC.JSGlobalObject, value: JSValue) !usize { + var out: bun.String = undefined; + value.jsonStringify(global, 0, &out); + defer out.deref(); + + if (out.tag == .Dead) return IPCSerializationError.SerializationFailed; + + // TODO: it would be cool to have a 'toUTF8Into' which can write directly into 'ipc_data.outgoing.list' + const str = out.toUTF8(bun.default_allocator); + defer str.deinit(); + + const slice = str.slice(); + + try writer.ensureUnusedCapacity(1 + slice.len + 1); + + writer.writeAssumeCapacity(&.{2}); + writer.writeAssumeCapacity(slice); + writer.writeAssumeCapacity("\n"); + + return 1 + slice.len + 1; } }; @@ -228,6 +314,14 @@ pub fn serialize(data: *IPCData, writer: anytype, global: *JSC.JSGlobalObject, v }; } +/// Given a writer interface, serialize and write a value. +/// Returns true if the value was written, false if it was not. +pub fn serializeInternal(data: *IPCData, writer: anytype, global: *JSC.JSGlobalObject, value: JSValue) !usize { + return switch (data.mode) { + inline else => |t| @field(@This(), @tagName(t)).serializeInternal(data, writer, global, value), + }; +} + pub const Socket = uws.NewSocketHandler(false); /// Used on POSIX @@ -237,9 +331,10 @@ const SocketIPCData = struct { incoming: bun.ByteList = .{}, // Maybe we should use StreamBuffer here as well outgoing: bun.io.StreamBuffer = .{}, - has_written_version: if (Environment.allow_assert) u1 else u0 = 0, - + internal_msg_queue: node_cluster_binding.InternalMsgHolder = .{}, + disconnected: bool = false, + is_server: bool = false, pub fn writeVersionPacket(this: *SocketIPCData) void { if (Environment.allow_assert) { bun.assert(this.has_written_version == 0); @@ -264,8 +359,32 @@ const SocketIPCData = struct { // TODO: probably we should not direct access ipc_data.outgoing.list.items here const start_offset = ipc_data.outgoing.list.items.len; - const payload_length = serialize(ipc_data, &ipc_data.outgoing, global, value) catch - return false; + const payload_length = serialize(ipc_data, &ipc_data.outgoing, global, value) catch return false; + + bun.assert(ipc_data.outgoing.list.items.len == start_offset + payload_length); + + if (start_offset == 0) { + bun.assert(ipc_data.outgoing.cursor == 0); + const n = ipc_data.socket.write(ipc_data.outgoing.list.items.ptr[start_offset..payload_length], false); + if (n == payload_length) { + ipc_data.outgoing.reset(); + } else if (n > 0) { + ipc_data.outgoing.cursor = @intCast(n); + } + } + + return true; + } + + pub fn serializeAndSendInternal(ipc_data: *SocketIPCData, global: *JSGlobalObject, value: JSValue) bool { + if (Environment.allow_assert) { + bun.assert(ipc_data.has_written_version == 1); + } + + // TODO: probably we should not direct access ipc_data.outgoing.list.items here + const start_offset = ipc_data.outgoing.list.items.len; + + const payload_length = serializeInternal(ipc_data, &ipc_data.outgoing, global, value) catch return false; bun.assert(ipc_data.outgoing.list.items.len == start_offset + payload_length); @@ -281,6 +400,24 @@ const SocketIPCData = struct { return true; } + + pub fn close(this: *SocketIPCData, nextTick: bool) void { + log("SocketIPCData#close", .{}); + if (this.disconnected) return; + this.disconnected = true; + if (nextTick) { + JSC.VirtualMachine.get().enqueueTask(JSC.ManagedTask.New(SocketIPCData, closeTask).init(this)); + } else { + this.closeTask(); + } + } + + pub fn closeTask(this: *SocketIPCData) void { + log("SocketIPCData#closeTask", .{}); + if (this.disconnected) { + this.socket.close(.normal); + } + } }; /// Used on Windows @@ -290,57 +427,72 @@ const NamedPipeIPCData = struct { mode: Mode, // we will use writer pipe as Duplex - writer: bun.io.StreamingWriter(NamedPipeIPCData, onWrite, onError, null, onClientClose) = .{}, + writer: bun.io.StreamingWriter(NamedPipeIPCData, onWrite, onError, null, onPipeClose) = .{}, incoming: bun.ByteList = .{}, // Maybe we should use IPCBuffer here as well - connected: bool = false, + disconnected: bool = false, + is_server: bool = false, connect_req: uv.uv_connect_t = std.mem.zeroes(uv.uv_connect_t), - server: ?*uv.Pipe = null, onClose: ?CloseHandler = null, - has_written_version: if (Environment.allow_assert) u1 else u0 = 0, + internal_msg_queue: node_cluster_binding.InternalMsgHolder = .{}, const CloseHandler = struct { callback: *const fn (*anyopaque) void, context: *anyopaque, }; - fn onWrite(_: *NamedPipeIPCData, amount: usize, status: bun.io.WriteStatus) void { - log("onWrite {d} {}", .{ amount, status }); + fn onServerPipeClose(this: *uv.Pipe) callconv(.C) void { + // safely free the pipes + bun.default_allocator.destroy(this); } - fn onError(_: *NamedPipeIPCData, err: bun.sys.Error) void { - log("Failed to write outgoing data {}", .{err}); + fn detach(this: *NamedPipeIPCData) void { + log("NamedPipeIPCData#detach: is_server {}", .{this.is_server}); + const source = this.writer.source.?; + // unref because we are closing the pipe + source.pipe.unref(); + this.writer.source = null; + + if (this.is_server) { + source.pipe.data = source.pipe; + source.pipe.close(onServerPipeClose); + this.onPipeClose(); + return; + } + // server will be destroyed by the process that created it + defer bun.default_allocator.destroy(source.pipe); + this.writer.source = null; + this.onPipeClose(); } - fn onClientClose(this: *NamedPipeIPCData) void { - log("onClisentClose", .{}); - this.connected = false; - if (this.server) |server| { - // we must close the server too - server.close(onServerClose); - } else { - if (this.onClose) |handler| { - // deinit dont free the instance of IPCData we should call it before the onClose callback actually frees it - this.deinit(); - handler.callback(handler.context); - } + fn onWrite(this: *NamedPipeIPCData, amount: usize, status: bun.io.WriteStatus) void { + log("onWrite {d} {}", .{ amount, status }); + + switch (status) { + .pending => {}, + .drained => { + // unref after sending all data + this.writer.source.?.pipe.unref(); + }, + .end_of_file => { + this.detach(); + }, } } - fn onServerClose(pipe: *uv.Pipe) callconv(.C) void { - log("onServerClose", .{}); - const this = bun.cast(*NamedPipeIPCData, pipe.data); - this.server = null; - if (this.connected) { - // close and deinit client if connected - this.writer.close(); - return; - } + fn onError(this: *NamedPipeIPCData, err: bun.sys.Error) void { + log("Failed to write outgoing data {}", .{err}); + this.detach(); + } + + fn onPipeClose(this: *NamedPipeIPCData) void { + log("onPipeClose", .{}); if (this.onClose) |handler| { + this.onClose = null; + handler.callback(handler.context); // deinit dont free the instance of IPCData we should call it before the onClose callback actually frees it this.deinit(); - handler.callback(handler.context); } } @@ -350,11 +502,11 @@ const NamedPipeIPCData = struct { } const bytes = getVersionPacket(this.mode); if (bytes.len > 0) { - if (this.connected) { - _ = this.writer.write(bytes); - } else { + if (this.disconnected) { // enqueue to be sent after connecting this.writer.outgoing.write(bytes) catch bun.outOfMemory(); + } else { + _ = this.writer.write(bytes); } } if (Environment.allow_assert) { @@ -366,86 +518,141 @@ const NamedPipeIPCData = struct { if (Environment.allow_assert) { bun.assert(this.has_written_version == 1); } - + if (this.disconnected) { + return false; + } + // ref because we have pending data + this.writer.source.?.pipe.ref(); const start_offset = this.writer.outgoing.list.items.len; - const payload_length: usize = serialize(this, &this.writer.outgoing, global, value) catch + const payload_length: usize = serialize(this, &this.writer.outgoing, global, value) catch return false; + + bun.assert(this.writer.outgoing.list.items.len == start_offset + payload_length); + + if (start_offset == 0) { + bun.assert(this.writer.outgoing.cursor == 0); + _ = this.writer.flush(); + } + + return true; + } + + pub fn serializeAndSendInternal(this: *NamedPipeIPCData, global: *JSGlobalObject, value: JSValue) bool { + if (Environment.allow_assert) { + bun.assert(this.has_written_version == 1); + } + if (this.disconnected) { return false; + } + // ref because we have pending data + this.writer.source.?.pipe.ref(); + const start_offset = this.writer.outgoing.list.items.len; + + const payload_length: usize = serializeInternal(this, &this.writer.outgoing, global, value) catch return false; bun.assert(this.writer.outgoing.list.items.len == start_offset + payload_length); if (start_offset == 0) { bun.assert(this.writer.outgoing.cursor == 0); - if (this.connected) { - _ = this.writer.flush(); - } + _ = this.writer.flush(); } return true; } - pub fn close(this: *NamedPipeIPCData) void { - if (this.server) |server| { - server.close(onServerClose); + pub fn close(this: *NamedPipeIPCData, nextTick: bool) void { + log("NamedPipeIPCData#close", .{}); + if (this.disconnected) return; + this.disconnected = true; + if (nextTick) { + JSC.VirtualMachine.get().enqueueTask(JSC.ManagedTask.New(NamedPipeIPCData, closeTask).init(this)); } else { - this.writer.close(); + this.closeTask(); } } - pub fn configureServer(this: *NamedPipeIPCData, comptime Context: type, instance: *Context, named_pipe: []const u8) JSC.Maybe(void) { - log("configureServer", .{}); - const ipc_pipe = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory(); - this.server = ipc_pipe; - ipc_pipe.data = this; - if (ipc_pipe.init(uv.Loop.get(), false).asErr()) |err| { - bun.default_allocator.destroy(ipc_pipe); - this.server = null; - return .{ .err = err }; + pub fn closeTask(this: *NamedPipeIPCData) void { + log("NamedPipeIPCData#closeTask is_server {}", .{this.is_server}); + if (this.disconnected) { + _ = this.writer.flush(); + this.writer.end(); + if (this.writer.getStream()) |stream| { + stream.readStop(); + } + if (!this.writer.hasPendingData()) { + this.detach(); + } } + } + + pub fn configureServer(this: *NamedPipeIPCData, comptime Context: type, instance: *Context, ipc_pipe: *uv.Pipe) JSC.Maybe(void) { + log("configureServer", .{}); ipc_pipe.data = @ptrCast(instance); this.onClose = .{ .callback = @ptrCast(&NewNamedPipeIPCHandler(Context).onClose), .context = @ptrCast(instance), }; - if (ipc_pipe.listenNamedPipe(named_pipe, 0, instance, NewNamedPipeIPCHandler(Context).onNewClientConnect).asErr()) |err| { - bun.default_allocator.destroy(ipc_pipe); - this.server = null; - return .{ .err = err }; + ipc_pipe.unref(); + this.is_server = true; + this.writer.setParent(this); + this.writer.owns_fd = false; + const startPipeResult = this.writer.startWithPipe(ipc_pipe); + if (startPipeResult == .err) { + this.close(false); + return startPipeResult; } - ipc_pipe.setPendingInstancesCount(1); - - ipc_pipe.unref(); + const stream = this.writer.getStream() orelse { + this.close(false); + return JSC.Maybe(void).errno(bun.C.E.PIPE, .pipe); + }; + const readStartResult = stream.readStart(instance, NewNamedPipeIPCHandler(Context).onReadAlloc, NewNamedPipeIPCHandler(Context).onReadError, NewNamedPipeIPCHandler(Context).onRead); + if (readStartResult == .err) { + this.close(false); + return readStartResult; + } return .{ .result = {} }; } - pub fn configureClient(this: *NamedPipeIPCData, comptime Context: type, instance: *Context, named_pipe: []const u8) !void { + pub fn configureClient(this: *NamedPipeIPCData, comptime Context: type, instance: *Context, pipe_fd: bun.FileDescriptor) !void { log("configureClient", .{}); const ipc_pipe = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory(); ipc_pipe.init(uv.Loop.get(), true).unwrap() catch |err| { bun.default_allocator.destroy(ipc_pipe); return err; }; - this.writer.startWithPipe(ipc_pipe).unwrap() catch |err| { + ipc_pipe.open(pipe_fd).unwrap() catch |err| { bun.default_allocator.destroy(ipc_pipe); return err; }; + ipc_pipe.unref(); + this.writer.owns_fd = false; + this.writer.setParent(this); + this.writer.startWithPipe(ipc_pipe).unwrap() catch |err| { + this.close(false); + return err; + }; this.connect_req.data = @ptrCast(instance); this.onClose = .{ .callback = @ptrCast(&NewNamedPipeIPCHandler(Context).onClose), .context = @ptrCast(instance), }; - try ipc_pipe.connect(&this.connect_req, named_pipe, instance, NewNamedPipeIPCHandler(Context).onConnect).unwrap(); + + const stream = this.writer.getStream() orelse { + this.close(false); + return error.FailedToConnectIPC; + }; + + stream.readStart(instance, NewNamedPipeIPCHandler(Context).onReadAlloc, NewNamedPipeIPCHandler(Context).onReadError, NewNamedPipeIPCHandler(Context).onRead).unwrap() catch |err| { + this.close(false); + return err; + }; } fn deinit(this: *NamedPipeIPCData) void { log("deinit", .{}); this.writer.deinit(); - if (this.server) |server| { - this.server = null; - bun.default_allocator.destroy(server); - } this.incoming.deinitWithAllocator(bun.default_allocator); } }; @@ -475,7 +682,7 @@ fn NewSocketIPCHandler(comptime Context: type) type { _: ?*anyopaque, ) void { // Note: uSockets has already freed the underlying socket, so calling Socket.close() can segfault - log("onClose\n", .{}); + log("NewSocketIPCHandler#onClose\n", .{}); this.handleIPCClose(); } @@ -485,7 +692,7 @@ fn NewSocketIPCHandler(comptime Context: type) type { all_data: []const u8, ) void { var data = all_data; - const ipc = this.ipc(); + const ipc = this.ipc() orelse return; log("onData {}", .{std.fmt.fmtSliceHexLower(data)}); // In the VirtualMachine case, `globalThis` is an optional, in case @@ -567,18 +774,17 @@ fn NewSocketIPCHandler(comptime Context: type) type { context: *Context, socket: Socket, ) void { - const to_write = context.ipc().outgoing.slice(); + const ipc = context.ipc() orelse return; + const to_write = ipc.outgoing.slice(); if (to_write.len == 0) { - context.ipc().outgoing.reset(); - context.ipc().outgoing.reset(); + ipc.outgoing.reset(); return; } const n = socket.write(to_write, false); if (n == to_write.len) { - context.ipc().outgoing.reset(); - context.ipc().outgoing.reset(); + ipc.outgoing.reset(); } else if (n > 0) { - context.ipc().outgoing.cursor += @intCast(n); + ipc.outgoing.cursor += @intCast(n); } } @@ -609,28 +815,29 @@ fn NewSocketIPCHandler(comptime Context: type) type { /// Used on Windows fn NewNamedPipeIPCHandler(comptime Context: type) type { - const uv = bun.windows.libuv; return struct { fn onReadAlloc(this: *Context, suggested_size: usize) []u8 { - const ipc = this.ipc(); + const ipc = this.ipc() orelse return ""; var available = ipc.incoming.available(); if (available.len < suggested_size) { ipc.incoming.ensureUnusedCapacity(bun.default_allocator, suggested_size) catch bun.outOfMemory(); available = ipc.incoming.available(); } - log("onReadAlloc {d}", .{suggested_size}); + log("NewNamedPipeIPCHandler#onReadAlloc {d}", .{suggested_size}); return available.ptr[0..suggested_size]; } fn onReadError(this: *Context, err: bun.C.E) void { - log("onReadError {}", .{err}); - this.ipc().close(); + log("NewNamedPipeIPCHandler#onReadError {}", .{err}); + if (this.ipc()) |ipc_data| { + ipc_data.close(true); + } } fn onRead(this: *Context, buffer: []const u8) void { - const ipc = this.ipc(); + const ipc = this.ipc() orelse return; - log("onRead {d}", .{buffer.len}); + log("NewNamedPipeIPCHandler#onRead {d}", .{buffer.len}); ipc.incoming.len += @as(u32, @truncate(buffer.len)); var slice = ipc.incoming.slice(); @@ -643,7 +850,7 @@ fn NewNamedPipeIPCHandler(comptime Context: type) type { if (this.globalThis) |global| { break :brk global; } - ipc.close(); + ipc.close(true); return; }, else => @panic("Unexpected globalThis type: " ++ @typeName(@TypeOf(this.globalThis))), @@ -659,7 +866,7 @@ fn NewNamedPipeIPCHandler(comptime Context: type) type { }, error.InvalidFormat => { Output.printErrorln("InvalidFormatError during IPC message handling", .{}); - ipc.close(); + ipc.close(false); return; }, }; @@ -676,74 +883,10 @@ fn NewNamedPipeIPCHandler(comptime Context: type) type { } } - pub fn onNewClientConnect(this: *Context, status: uv.ReturnCode) void { - const ipc = this.ipc(); - log("onNewClientConnect {d}", .{status.int()}); - if (status.errEnum()) |_| { - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - } - const server = ipc.server orelse { - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - }; - var client = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory(); - client.init(uv.Loop.get(), true).unwrap() catch { - bun.default_allocator.destroy(client); - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - }; - - ipc.writer.startWithPipe(client).unwrap() catch { - bun.default_allocator.destroy(client); - Output.printErrorln("Failed to start IPC pipe", .{}); - return; - }; - - switch (server.accept(client)) { - .err => { - ipc.close(); - return; - }, - .result => { - ipc.connected = true; - client.readStart(this, onReadAlloc, onReadError, onRead).unwrap() catch { - ipc.close(); - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - }; - _ = ipc.writer.flush(); - }, - } - } - pub fn onClose(this: *Context) void { + log("NewNamedPipeIPCHandler#onClose\n", .{}); this.handleIPCClose(); } - - fn onConnect(this: *Context, status: uv.ReturnCode) void { - const ipc = this.ipc(); - - log("onConnect {d}", .{status.int()}); - ipc.connected = true; - - if (status.errEnum()) |_| { - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - } - const stream = ipc.writer.getStream() orelse { - ipc.close(); - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - }; - - stream.readStart(this, onReadAlloc, onReadError, onRead).unwrap() catch { - ipc.close(); - Output.printErrorln("Failed to connect IPC pipe", .{}); - return; - }; - _ = ipc.writer.flush(); - } }; } @@ -753,7 +896,7 @@ fn NewNamedPipeIPCHandler(comptime Context: type) type { /// struct { /// globalThis: ?*JSGlobalObject, /// -/// fn ipc(*Context) *IPCData, +/// fn ipc(*Context) ?*IPCData, /// fn handleIPCMessage(*Context, DecodedIPCMessage) void /// fn handleIPCClose(*Context) void /// } diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index c94fb86d55d67..94c07d4d2410d 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -114,7 +114,7 @@ pub const bun_file_import_path = "/node_modules.server.bun"; export var has_bun_garbage_collector_flag_enabled = false; const SourceMap = @import("../sourcemap/sourcemap.zig"); -const ParsedSourceMap = SourceMap.Mapping.ParsedSourceMap; +const ParsedSourceMap = SourceMap.ParsedSourceMap; const MappingList = SourceMap.Mapping.List; const SourceProviderMap = SourceMap.SourceProviderMap; @@ -123,10 +123,29 @@ const uv = bun.windows.libuv; pub const SavedSourceMap = struct { /// This is a pointer to the map located on the VirtualMachine struct map: *HashTable, - mutex: bun.Lock = bun.Lock.init(), + mutex: bun.Lock = .{}, pub const vlq_offset = 24; + pub fn init(this: *SavedSourceMap, map: *HashTable) void { + this.* = .{ + .map = map, + .mutex = .{}, + }; + + this.map.lockPointers(); + } + + pub inline fn lock(map: *SavedSourceMap) void { + map.mutex.lock(); + map.map.unlockPointers(); + } + + pub inline fn unlock(map: *SavedSourceMap) void { + map.map.lockPointers(); + map.mutex.unlock(); + } + // For the runtime, we store the number of mappings and how many bytes the final list is at the beginning of the array // The first 8 bytes are the length of the array // The second 8 bytes are the number of mappings @@ -209,8 +228,8 @@ pub const SavedSourceMap = struct { } pub fn removeZigSourceProvider(this: *SavedSourceMap, opaque_source_provider: *anyopaque, path: []const u8) void { - this.mutex.lock(); - defer this.mutex.unlock(); + this.lock(); + defer this.unlock(); const entry = this.map.getEntry(bun.hash(path)) orelse return; const old_value = Value.from(entry.value_ptr.*); @@ -222,8 +241,8 @@ pub const SavedSourceMap = struct { } else if (old_value.get(ParsedSourceMap)) |map| { if (map.underlying_provider.provider()) |prov| { if (@intFromPtr(prov) == @intFromPtr(opaque_source_provider)) { - map.deinit(default_allocator); this.map.removeByPtr(entry.key_ptr); + map.deref(); } } } @@ -239,15 +258,14 @@ pub const SavedSourceMap = struct { pub fn deinit(this: *SavedSourceMap) void { { - this.mutex.lock(); - defer this.mutex.unlock(); + this.lock(); + defer this.unlock(); var iter = this.map.valueIterator(); while (iter.next()) |val| { var value = Value.from(val.*); - if (value.get(ParsedSourceMap)) |source_map_| { - var source_map: *ParsedSourceMap = source_map_; - source_map.deinit(default_allocator); + if (value.get(ParsedSourceMap)) |source_map| { + source_map.deref(); } else if (value.get(SavedMappings)) |saved_mappings| { var saved = SavedMappings{ .data = @as([*]u8, @ptrCast(saved_mappings)) }; saved.deinit(); @@ -257,6 +275,7 @@ pub const SavedSourceMap = struct { } } + this.map.unlockPointers(); this.map.deinit(); } @@ -265,14 +284,15 @@ pub const SavedSourceMap = struct { } fn putValue(this: *SavedSourceMap, path: []const u8, value: Value) !void { - this.mutex.lock(); - defer this.mutex.unlock(); + this.lock(); + defer this.unlock(); + const entry = try this.map.getOrPut(bun.hash(path)); if (entry.found_existing) { var old_value = Value.from(entry.value_ptr.*); if (old_value.get(ParsedSourceMap)) |parsed_source_map| { var source_map: *ParsedSourceMap = parsed_source_map; - source_map.deinit(default_allocator); + source_map.deref(); } else if (old_value.get(SavedMappings)) |saved_mappings| { var saved = SavedMappings{ .data = @as([*]u8, @ptrCast(saved_mappings)) }; saved.deinit(); @@ -283,37 +303,59 @@ pub const SavedSourceMap = struct { entry.value_ptr.* = value.ptr(); } - pub fn getWithContent( + fn getWithContent( this: *SavedSourceMap, path: string, hint: SourceMap.ParseUrlResultHint, ) SourceMap.ParseUrl { const hash = bun.hash(path); - const mapping = this.map.getEntry(hash) orelse return .{}; + + // This lock is for the hash table + this.lock(); + + // This mapping entry is only valid while the mutex is locked + const mapping = this.map.getEntry(hash) orelse { + this.unlock(); + return .{}; + }; + switch (Value.from(mapping.value_ptr.*).tag()) { Value.Tag.ParsedSourceMap => { - return .{ .map = Value.from(mapping.value_ptr.*).as(ParsedSourceMap) }; + defer this.unlock(); + const map = Value.from(mapping.value_ptr.*).as(ParsedSourceMap); + map.ref(); + return .{ .map = map }; }, Value.Tag.SavedMappings => { + defer this.unlock(); var saved = SavedMappings{ .data = @as([*]u8, @ptrCast(Value.from(mapping.value_ptr.*).as(ParsedSourceMap))) }; defer saved.deinit(); - const result = default_allocator.create(ParsedSourceMap) catch unreachable; - result.* = saved.toMapping(default_allocator, path) catch { + const result = ParsedSourceMap.new(saved.toMapping(default_allocator, path) catch { _ = this.map.remove(mapping.key_ptr.*); return .{}; - }; + }); mapping.value_ptr.* = Value.init(result).ptr(); + result.ref(); + return .{ .map = result }; }, Value.Tag.SourceProviderMap => { - var ptr = Value.from(mapping.value_ptr.*).as(SourceProviderMap); + const ptr: *SourceProviderMap = Value.from(mapping.value_ptr.*).as(SourceProviderMap); + this.unlock(); - if (ptr.getSourceMap(path, .none, hint)) |parse| + // Do not lock the mutex while we're parsing JSON! + if (ptr.getSourceMap(path, .none, hint)) |parse| { if (parse.map) |map| { - mapping.value_ptr.* = Value.init(map).ptr(); + map.ref(); + // The mutex is not locked. We have to check the hash table again. + this.putValue(path, Value.init(map)) catch bun.outOfMemory(); + return parse; - }; + } + } + this.lock(); + defer this.unlock(); // does not have a valid source map. let's not try again _ = this.map.remove(hash); @@ -327,6 +369,7 @@ pub const SavedSourceMap = struct { if (Environment.allow_assert) { @panic("Corrupt pointer tag"); } + this.unlock(); return .{}; }, } @@ -343,14 +386,12 @@ pub const SavedSourceMap = struct { column: i32, source_handling: SourceMap.SourceContentHandling, ) ?SourceMap.Mapping.Lookup { - this.mutex.lock(); - defer this.mutex.unlock(); - const parse = this.getWithContent(path, switch (source_handling) { .no_source_contents => .mappings_only, .source_contents => .{ .all = .{ .line = line, .column = column } }, }); const map = parse.map orelse return null; + const mapping = parse.mapping orelse SourceMap.Mapping.find(map.mappings, line, column) orelse return null; @@ -364,10 +405,6 @@ pub const SavedSourceMap = struct { }; const uws = bun.uws; -pub export fn Bun__getDefaultGlobal() *JSGlobalObject { - return JSC.VirtualMachine.get().global; -} - pub export fn Bun__getVM() *JSC.VirtualMachine { return JSC.VirtualMachine.get(); } @@ -389,39 +426,88 @@ pub export fn Bun__GlobalObject__hasIPC(global: *JSC.JSGlobalObject) bool { return global.bunVM().ipc != null; } +extern fn Bun__Process__queueNextTick1(*JSC.ZigGlobalObject, JSC.JSValue, JSC.JSValue) void; + pub export fn Bun__Process__send( globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame, -) JSValue { +) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); - if (callFrame.argumentsCount() < 1) { - globalObject.throwInvalidArguments("process.send requires at least one argument", .{}); - return .zero; + var message, var handle, var options_, var callback = callFrame.arguments(4).ptr; + + if (message == .zero) message = .undefined; + if (handle == .zero) handle = .undefined; + if (options_ == .zero) options_ = .undefined; + if (callback == .zero) callback = .undefined; + + if (handle.isFunction()) { + callback = handle; + handle = .undefined; + options_ = .undefined; + } else if (options_.isFunction()) { + callback = options_; + options_ = .undefined; + } else if (!options_.isUndefined()) { + if (!globalObject.validateObject("options", options_, .{})) return .zero; } + + const S = struct { + fn impl(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments_ = callframe.arguments(1).slice(); + const ex = arguments_[0]; + VirtualMachine.Process__emitErrorEvent(globalThis, ex); + return .undefined; + } + }; + const vm = globalObject.bunVM(); - if (vm.getIPCInstance()) |ipc_instance| { - const success = ipc_instance.data.serializeAndSend(globalObject, callFrame.argument(0)); - return if (success) .undefined else .zero; + const zigGlobal: *JSC.ZigGlobalObject = @ptrCast(globalObject); + const ipc_instance = vm.getIPCInstance() orelse { + const ex = globalObject.ERR_IPC_CHANNEL_CLOSED("Channel closed.", .{}).toJS(); + if (callback.isFunction()) { + Bun__Process__queueNextTick1(zigGlobal, callback, ex); + } else { + const fnvalue = JSC.JSFunction.create(globalObject, "", S.impl, 1, .{}); + Bun__Process__queueNextTick1(zigGlobal, fnvalue, ex); + } + return .false; + }; + + if (message.isUndefined()) { + return globalObject.throwValueRet(globalObject.ERR_MISSING_ARGS_static(ZigString.static("message"), null, null)); + } + if (!message.isString() and !message.isObject() and !message.isNumber() and !message.isBoolean()) { + return globalObject.throwValueRet(globalObject.ERR_INVALID_ARG_TYPE_static( + ZigString.static("message"), + ZigString.static("string, object, number, or boolean"), + message, + )); + } + + const good = ipc_instance.data.serializeAndSend(globalObject, message); + + if (good) { + if (callback.isFunction()) { + Bun__Process__queueNextTick1(zigGlobal, callback, .zero); + } } else { - globalObject.throw("IPC Socket is no longer open.", .{}); - return .zero; + const ex = globalObject.createTypeErrorInstance("process.send() failed", .{}); + ex.put(globalObject, ZigString.static("syscall"), ZigString.static("write").toJS(globalObject)); + if (callback.isFunction()) { + Bun__Process__queueNextTick1(zigGlobal, callback, ex); + } else { + const fnvalue = JSC.JSFunction.create(globalObject, "", S.impl, 1, .{}); + Bun__Process__queueNextTick1(zigGlobal, fnvalue, ex); + } } + + return .true; } pub export fn Bun__isBunMain(globalObject: *JSGlobalObject, str: *const bun.String) bool { return str.eqlUTF8(globalObject.bunVM().main); } -pub export fn Bun__Process__disconnect( - globalObject: *JSGlobalObject, - callFrame: *JSC.CallFrame, -) JSValue { - JSC.markBinding(@src()); - _ = callFrame; - _ = globalObject; - return .undefined; -} - /// When IPC environment variables are passed, the socket is not immediately opened, /// but rather we wait for process.on('message') or process.send() to be called, THEN /// we open the socket. This is to avoid missing messages at the start of the program. @@ -451,7 +537,7 @@ pub export fn Bun__reportUnhandledError(globalObject: *JSGlobalObject, value: JS // See the crash in https://github.com/oven-sh/bun/issues/9778 const jsc_vm = JSC.VirtualMachine.get(); _ = jsc_vm.uncaughtException(globalObject, value, false); - return JSC.JSValue.jsUndefined(); + return .undefined; } /// This function is called on another thread @@ -460,12 +546,9 @@ pub export fn Bun__reportUnhandledError(globalObject: *JSGlobalObject, value: JS pub export fn Bun__queueTaskConcurrently(global: *JSGlobalObject, task: *JSC.CppTask) void { JSC.markBinding(@src()); - const concurrent = bun.default_allocator.create(JSC.ConcurrentTask) catch unreachable; - concurrent.* = JSC.ConcurrentTask{ - .task = Task.init(task), - .auto_delete = true, - }; - global.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(concurrent); + global.bunVMConcurrently().eventLoop().enqueueTaskConcurrent( + JSC.ConcurrentTask.create(Task.init(task)), + ); } pub export fn Bun__handleRejectedPromise(global: *JSGlobalObject, promise: *JSC.JSPromise) void { @@ -613,6 +696,10 @@ export fn Bun__getVerboseFetchValue() i32 { }; } +const body_value_pool_size = if (bun.heap_breakdown.enabled) 0 else 256; +pub const BodyValueRef = bun.HiveRef(JSC.WebCore.Body.Value, body_value_pool_size); +const BodyValueHiveAllocator = bun.HiveArray(BodyValueRef, body_value_pool_size).Fallback; + /// TODO: rename this to ScriptExecutionContext /// This is the shared global state for a single JS instance execution /// Today, Bun is one VM per thread, so the name "VirtualMachine" sort of makes sense @@ -658,11 +745,12 @@ pub const VirtualMachine = struct { default_tls_reject_unauthorized: ?bool = null, default_verbose_fetch: ?bun.http.HTTPVerboseLevel = null, - /// Do not access this field directly - /// It exists in the VirtualMachine struct so that - /// we don't accidentally make a stack copy of it - /// only use it through - /// source_mappings + /// Do not access this field directly! + /// + /// It exists in the VirtualMachine struct so that we don't accidentally + /// make a stack copy of it only use it through source_mappings. + /// + /// This proposal could let us safely move it back https://github.com/ziglang/zig/issues/7769 saved_source_map_table: SavedSourceMap.HashTable = undefined, source_mappings: SavedSourceMap = undefined, @@ -703,7 +791,7 @@ pub const VirtualMachine = struct { /// ["baz", "--bar"] /// "bun foo /// [] - argv: []const []const u8 = &[_][]const u8{"bun"}, + argv: []const []const u8 = &[_][]const u8{}, origin_timer: std.time.Timer = undefined, origin_timestamp: u64 = 0, @@ -747,10 +835,16 @@ pub const VirtualMachine = struct { debug_thread_id: if (Environment.allow_assert) std.Thread.Id else void, + body_value_hive_allocator: BodyValueHiveAllocator = undefined, + pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; pub const OnException = fn (*ZigException) void; + pub fn initRequestBodyValue(this: *VirtualMachine, body: JSC.WebCore.Body.Value) !*BodyValueRef { + return BodyValueRef.init(body, &this.body_value_hive_allocator); + } + pub fn uwsLoop(this: *const VirtualMachine) *uws.Loop { if (comptime Environment.isPosix) { if (Environment.allow_assert) { @@ -801,8 +895,25 @@ pub const VirtualMachine = struct { }; } - const VMHolder = struct { + pub const VMHolder = struct { pub threadlocal var vm: ?*VirtualMachine = null; + pub threadlocal var cached_global_object: ?*JSGlobalObject = null; + pub export fn Bun__setDefaultGlobalObject(global: *JSGlobalObject) void { + if (vm) |vm_instance| { + vm_instance.global = global; + } + + cached_global_object = global; + } + + pub export fn Bun__getDefaultGlobalObject() ?*JSGlobalObject { + return cached_global_object orelse { + if (vm) |vm_instance| { + cached_global_object = vm_instance.global; + } + return null; + }; + } }; pub inline fn get() *VirtualMachine { @@ -917,27 +1028,41 @@ pub const VirtualMachine = struct { }; } - pub fn loadExtraEnv(this: *VirtualMachine) void { + fn ensureSourceCodePrinter(this: *VirtualMachine) void { + if (source_code_printer == null) { + const allocator = if (bun.heap_breakdown.enabled) bun.heap_breakdown.namedAllocator("SourceCode") else this.allocator; + const writer = try js_printer.BufferWriter.init(allocator); + source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable; + source_code_printer.?.* = js_printer.BufferPrinter.init(writer); + source_code_printer.?.ctx.append_null_byte = false; + } + } + + pub fn loadExtraEnvAndSourceCodePrinter(this: *VirtualMachine) void { var map = this.bundler.env.map; + ensureSourceCodePrinter(this); + if (map.get("BUN_SHOW_BUN_STACKFRAMES") != null) { this.hide_bun_stackframes = false; } + if (bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER")) { + this.transpiler_store.enabled = false; + } + if (map.map.fetchSwapRemove("NODE_CHANNEL_FD")) |kv| { + const fd_s = kv.value.value; const mode = if (map.map.fetchSwapRemove("NODE_CHANNEL_SERIALIZATION_MODE")) |mode_kv| IPC.Mode.fromString(mode_kv.value.value) orelse .json else .json; - IPC.log("IPC environment variables: NODE_CHANNEL_FD={d}, NODE_CHANNEL_SERIALIZATION_MODE={s}", .{ kv.value.value, @tagName(mode) }); - if (Environment.isWindows) { - this.initIPCInstance(kv.value.value, mode); - } else { - if (std.fmt.parseInt(i32, kv.value.value, 10)) |fd| { - this.initIPCInstance(bun.toFD(fd), mode); - } else |_| { - Output.warn("Failed to parse IPC channel number '{s}'", .{kv.value.value}); - } + + IPC.log("IPC environment variables: NODE_CHANNEL_FD={s}, NODE_CHANNEL_SERIALIZATION_MODE={s}", .{ fd_s, @tagName(mode) }); + if (std.fmt.parseInt(i32, fd_s, 10)) |fd| { + this.initIPCInstance(bun.toFD(fd), mode); + } else |_| { + Output.warn("Failed to parse IPC channel number '{s}'", .{fd_s}); } } @@ -961,6 +1086,15 @@ pub const VirtualMachine = struct { this.aggressive_garbage_collection = .aggressive; has_bun_garbage_collector_flag_enabled = true; } + + if (map.get("BUN_FEATURE_FLAG_SYNTHETIC_MEMORY_LIMIT")) |value| { + if (std.fmt.parseInt(usize, value, 10)) |limit| { + synthetic_allocation_limit = limit; + string_allocation_limit = limit; + } else |_| { + Output.panic("BUN_FEATURE_FLAG_SYNTHETIC_MEMORY_LIMIT must be a positive integer", .{}); + } + } } } @@ -1002,7 +1136,7 @@ pub const VirtualMachine = struct { if (this.is_handling_uncaught_exception) { this.runErrorHandler(err, null); - Bun__Process__exit(globalObject, 1); + Bun__Process__exit(globalObject, 7); @panic("Uncaught exception while handling uncaught exception"); } this.is_handling_uncaught_exception = true; @@ -1170,14 +1304,18 @@ pub const VirtualMachine = struct { this.exit_handler.dispatchOnExit(); const rare_data = this.rare_data orelse return; - var hook = rare_data.cleanup_hook orelse return; - hook.execute(); - while (hook.next) |next| { - next.execute(); - hook = next; + var hooks = rare_data.cleanup_hooks; + defer if (!is_main_thread_vm) hooks.clearAndFree(bun.default_allocator); + rare_data.cleanup_hooks = .{}; + for (hooks.items) |hook| { + hook.execute(); } } + pub fn globalExit(this: *VirtualMachine) noreturn { + bun.Global.exit(this.exit_handler.exit_code); + } + pub fn nextAsyncTaskID(this: *VirtualMachine) u64 { var debugger: *Debugger = &(this.debugger orelse return 0); debugger.next_debugger_id +%= 1; @@ -1368,6 +1506,7 @@ pub const VirtualMachine = struct { this.macro_event_loop.global = this.global; this.macro_event_loop.virtual_machine = this; this.macro_event_loop.concurrent_tasks = .{}; + ensureSourceCodePrinter(this); } this.bundler.options.target = .bun_macro; @@ -1430,7 +1569,7 @@ pub const VirtualMachine = struct { vm.* = VirtualMachine{ .global = undefined, - .transpiler_store = RuntimeTranspilerStore.init(allocator), + .transpiler_store = RuntimeTranspilerStore.init(), .allocator = allocator, .entry_point = ServerEntryPoint{}, .bundler = bundler, @@ -1445,11 +1584,11 @@ pub const VirtualMachine = struct { .origin_timer = std.time.Timer.start() catch @panic("Timers are not supported on this system."), .origin_timestamp = getOriginTimestamp(), .ref_strings = JSC.RefString.Map.init(allocator), - .ref_strings_mutex = Lock.init(), + .ref_strings_mutex = .{}, .standalone_module_graph = opts.graph.?, .debug_thread_id = if (Environment.allow_assert) std.Thread.getCurrentId() else {}, }; - vm.source_mappings = .{ .map = &vm.saved_source_map_table }; + vm.source_mappings.init(&vm.saved_source_map_table); vm.regular_event_loop.tasks = EventLoop.Queue.init( default_allocator, ); @@ -1492,14 +1631,8 @@ pub const VirtualMachine = struct { vm.regular_event_loop.virtual_machine = vm; vm.jsc = vm.global.vm(); - if (source_code_printer == null) { - const writer = try js_printer.BufferWriter.init(allocator); - source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable; - source_code_printer.?.* = js_printer.BufferPrinter.init(writer); - source_code_printer.?.ctx.append_null_byte = false; - } - vm.configureDebugger(opts.debugger); + vm.body_value_hive_allocator = BodyValueHiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value)); return vm; } @@ -1545,7 +1678,7 @@ pub const VirtualMachine = struct { vm.* = VirtualMachine{ .global = undefined, - .transpiler_store = RuntimeTranspilerStore.init(allocator), + .transpiler_store = RuntimeTranspilerStore.init(), .allocator = allocator, .entry_point = ServerEntryPoint{}, .bundler = bundler, @@ -1560,10 +1693,10 @@ pub const VirtualMachine = struct { .origin_timer = std.time.Timer.start() catch @panic("Please don't mess with timers."), .origin_timestamp = getOriginTimestamp(), .ref_strings = JSC.RefString.Map.init(allocator), - .ref_strings_mutex = Lock.init(), + .ref_strings_mutex = .{}, .debug_thread_id = if (Environment.allow_assert) std.Thread.getCurrentId() else {}, }; - vm.source_mappings = .{ .map = &vm.saved_source_map_table }; + vm.source_mappings.init(&vm.saved_source_map_table); vm.regular_event_loop.tasks = EventLoop.Queue.init( default_allocator, ); @@ -1611,14 +1744,8 @@ pub const VirtualMachine = struct { if (opts.smol) is_smol_mode = opts.smol; - if (source_code_printer == null) { - const writer = try js_printer.BufferWriter.init(allocator); - source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable; - source_code_printer.?.* = js_printer.BufferPrinter.init(writer); - source_code_printer.?.ctx.append_null_byte = false; - } - vm.configureDebugger(opts.debugger); + vm.body_value_hive_allocator = BodyValueHiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value)); return vm; } @@ -1693,7 +1820,7 @@ pub const VirtualMachine = struct { vm.* = VirtualMachine{ .global = undefined, .allocator = allocator, - .transpiler_store = RuntimeTranspilerStore.init(allocator), + .transpiler_store = RuntimeTranspilerStore.init(), .entry_point = ServerEntryPoint{}, .bundler = bundler, .console = console, @@ -1707,12 +1834,12 @@ pub const VirtualMachine = struct { .origin_timer = std.time.Timer.start() catch @panic("Please don't mess with timers."), .origin_timestamp = getOriginTimestamp(), .ref_strings = JSC.RefString.Map.init(allocator), - .ref_strings_mutex = Lock.init(), + .ref_strings_mutex = .{}, .standalone_module_graph = worker.parent.standalone_module_graph, .worker = worker, .debug_thread_id = if (Environment.allow_assert) std.Thread.getCurrentId() else {}, }; - vm.source_mappings = .{ .map = &vm.saved_source_map_table }; + vm.source_mappings.init(&vm.saved_source_map_table); vm.regular_event_loop.tasks = EventLoop.Queue.init( default_allocator, ); @@ -1734,8 +1861,14 @@ pub const VirtualMachine = struct { .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, }; + vm.bundler.resolver.standalone_module_graph = opts.graph; + + if (opts.graph == null) { + vm.bundler.configureLinker(); + } else { + vm.bundler.configureLinkerWithAutoJSX(false); + } - vm.bundler.configureLinker(); try vm.bundler.configureFramework(false); vm.smol = opts.smol; vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); @@ -1755,13 +1888,95 @@ pub const VirtualMachine = struct { vm.regular_event_loop.virtual_machine = vm; vm.jsc = vm.global.vm(); vm.bundler.setAllocator(allocator); - if (source_code_printer == null) { - const writer = try js_printer.BufferWriter.init(allocator); - source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable; - source_code_printer.?.* = js_printer.BufferPrinter.init(writer); - source_code_printer.?.ctx.append_null_byte = false; + vm.body_value_hive_allocator = BodyValueHiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value)); + + return vm; + } + + pub fn initKit(opts: Options) anyerror!*VirtualMachine { + JSC.markBinding(@src()); + const allocator = opts.allocator; + var log: *logger.Log = undefined; + if (opts.log) |__log| { + log = __log; + } else { + log = try allocator.create(logger.Log); + log.* = logger.Log.init(allocator); } + VMHolder.vm = try allocator.create(VirtualMachine); + const console = try allocator.create(ConsoleObject); + console.* = ConsoleObject.init(Output.errorWriter(), Output.writer()); + const bundler = try Bundler.init( + allocator, + log, + try Config.configureTransformOptionsForBunVM(allocator, opts.args), + opts.env_loader, + ); + var vm = VMHolder.vm.?; + + vm.* = VirtualMachine{ + .global = undefined, + .transpiler_store = RuntimeTranspilerStore.init(), + .allocator = allocator, + .entry_point = ServerEntryPoint{}, + .bundler = bundler, + .console = console, + .log = log, + .flush_list = std.ArrayList(string).init(allocator), + .origin = bundler.options.origin, + .saved_source_map_table = SavedSourceMap.HashTable.init(bun.default_allocator), + .source_mappings = undefined, + .macros = MacroMap.init(allocator), + .macro_entry_points = @TypeOf(vm.macro_entry_points).init(allocator), + .origin_timer = std.time.Timer.start() catch @panic("Please don't mess with timers."), + .origin_timestamp = getOriginTimestamp(), + .ref_strings = JSC.RefString.Map.init(allocator), + .ref_strings_mutex = .{}, + .debug_thread_id = if (Environment.allow_assert) std.Thread.getCurrentId() else {}, + }; + vm.source_mappings.init(&vm.saved_source_map_table); + vm.regular_event_loop.tasks = EventLoop.Queue.init( + default_allocator, + ); + vm.regular_event_loop.immediate_tasks = EventLoop.Queue.init( + default_allocator, + ); + vm.regular_event_loop.next_immediate_tasks = EventLoop.Queue.init( + default_allocator, + ); + vm.regular_event_loop.tasks.ensureUnusedCapacity(64) catch unreachable; + vm.regular_event_loop.concurrent_tasks = .{}; + vm.event_loop = &vm.regular_event_loop; + + vm.bundler.macro_context = null; + vm.bundler.resolver.store_fd = opts.store_fd; + vm.bundler.resolver.prefer_module_field = false; + + vm.bundler.resolver.onWakePackageManager = .{ + .context = &vm.modules, + .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, + .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, + }; + + vm.bundler.configureLinker(); + try vm.bundler.configureFramework(false); + + vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); + + if (opts.args.serve orelse false) { + vm.bundler.linker.onImportCSS = Bun.onImportCSS; + } + + vm.regular_event_loop.virtual_machine = vm; + vm.smol = opts.smol; + + if (opts.smol) + is_smol_mode = opts.smol; + + vm.configureDebugger(opts.debugger); + vm.body_value_hive_allocator = BodyValueHiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value)); + return vm; } @@ -1777,7 +1992,7 @@ pub const VirtualMachine = struct { return ResolvedSource{ .source_code = bun.String.init(""), .specifier = specifier, - .source_url = bun.String.init(source_url), + .source_url = specifier.createIfDifferent(source_url), .hash = 0, .allocator = null, .source_code_needs_deref = false, @@ -1792,7 +2007,7 @@ pub const VirtualMachine = struct { return ResolvedSource{ .source_code = bun.String.init(source.impl), .specifier = specifier, - .source_url = bun.String.init(source_url), + .source_url = specifier.createIfDifferent(source_url), .hash = source.hash, .allocator = source, .source_code_needs_deref = false, @@ -2278,16 +2493,6 @@ pub const VirtualMachine = struct { res.* = ErrorableString.ok(bun.String.init(result.path)); } - // // This double prints - // pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, _: JSPromiseRejectionOperation) callconv(.C) JSValue { - // const result = promise.result(global.vm()); - // if (@intFromEnum(VirtualMachine.get().last_error_jsvalue) != @intFromEnum(result)) { - // VirtualMachine.get().runErrorHandler(result, null); - // } - - // return JSValue.jsUndefined(); - // } - pub const main_file_name: string = "bun:main"; pub fn drainMicrotasks(this: *VirtualMachine) void { @@ -2332,7 +2537,7 @@ pub const VirtualMachine = struct { return; }, else => { - var errors_stack: [256]*anyopaque = undefined; + var errors_stack: [256]JSValue = undefined; const len = @min(log.msgs.items.len, errors_stack.len); const errors = errors_stack[0..len]; @@ -2340,21 +2545,20 @@ pub const VirtualMachine = struct { for (logs, errors) |msg, *current| { current.* = switch (msg.metadata) { - .build => BuildMessage.create(globalThis, globalThis.allocator(), msg).asVoid(), + .build => BuildMessage.create(globalThis, globalThis.allocator(), msg), .resolve => ResolveMessage.create( globalThis, globalThis.allocator(), msg, referrer.toUTF8(bun.default_allocator).slice(), - ).asVoid(), + ), }; } ret.* = ErrorableResolvedSource.err( err, globalThis.createAggregateError( - errors.ptr, - @as(u16, @intCast(errors.len)), + errors, &ZigString.init( std.fmt.allocPrint(globalThis.allocator(), "{d} errors building \"{}\"", .{ errors.len, @@ -2369,7 +2573,14 @@ pub const VirtualMachine = struct { // TODO: pub fn deinit(this: *VirtualMachine) void { + if (source_code_printer) |print| { + print.getMutableBuffer().deinit(); + print.ctx.written = &.{}; + } this.source_mappings.deinit(); + if (this.rare_data) |rare_data| { + rare_data.deinit(); + } this.has_terminated = true; } @@ -2494,11 +2705,11 @@ pub const VirtualMachine = struct { if (this.isWatcherEnabled()) { this.eventLoop().performGC(); switch (this.pending_internal_promise.status(this.global.vm())) { - JSC.JSPromise.Status.Pending => { - while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + .pending => { + while (this.pending_internal_promise.status(this.global.vm()) == .pending) { this.eventLoop().tick(); - if (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + if (this.pending_internal_promise.status(this.global.vm()) == .pending) { this.eventLoop().autoTick(); } } @@ -2508,11 +2719,11 @@ pub const VirtualMachine = struct { } else { this.eventLoop().performGC(); this.waitForPromise(JSC.AnyPromise{ - .Internal = promise, + .internal = promise, }); } - if (promise.status(this.global.vm()) == .Rejected) + if (promise.status(this.global.vm()) == .rejected) return promise; } @@ -2593,7 +2804,7 @@ pub const VirtualMachine = struct { const promise = try this.reloadEntryPoint(entry_path); this.eventLoop().performGC(); this.eventLoop().waitForPromiseWithTermination(JSC.AnyPromise{ - .Internal = promise, + .internal = promise, }); if (this.worker) |worker| { if (worker.hasRequestedTerminate()) { @@ -2610,11 +2821,11 @@ pub const VirtualMachine = struct { if (this.isWatcherEnabled()) { this.eventLoop().performGC(); switch (this.pending_internal_promise.status(this.global.vm())) { - JSC.JSPromise.Status.Pending => { - while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + .pending => { + while (this.pending_internal_promise.status(this.global.vm()) == .pending) { this.eventLoop().tick(); - if (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + if (this.pending_internal_promise.status(this.global.vm()) == .pending) { this.eventLoop().autoTick(); } } @@ -2622,14 +2833,12 @@ pub const VirtualMachine = struct { else => {}, } } else { - if (promise.status(this.global.vm()) == .Rejected) { + if (promise.status(this.global.vm()) == .rejected) { return promise; } this.eventLoop().performGC(); - this.waitForPromise(JSC.AnyPromise{ - .Internal = promise, - }); + this.waitForPromise(.{ .internal = promise }); } this.eventLoop().autoTick(); @@ -2644,11 +2853,11 @@ pub const VirtualMachine = struct { if (this.isWatcherEnabled()) { this.eventLoop().performGC(); switch (this.pending_internal_promise.status(this.global.vm())) { - JSC.JSPromise.Status.Pending => { - while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + .pending => { + while (this.pending_internal_promise.status(this.global.vm()) == .pending) { this.eventLoop().tick(); - if (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + if (this.pending_internal_promise.status(this.global.vm()) == .pending) { this.eventLoop().autoTick(); } } @@ -2656,14 +2865,12 @@ pub const VirtualMachine = struct { else => {}, } } else { - if (promise.status(this.global.vm()) == .Rejected) { + if (promise.status(this.global.vm()) == .rejected) { return promise; } this.eventLoop().performGC(); - this.waitForPromise(JSC.AnyPromise{ - .Internal = promise, - }); + this.waitForPromise(.{ .internal = promise }); } return this.pending_internal_promise; @@ -2724,12 +2931,22 @@ pub const VirtualMachine = struct { promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(entry_path)) orelse return null; this.waitForPromise(JSC.AnyPromise{ - .Internal = promise, + .internal = promise, }); return promise; } + pub fn printErrorLikeObjectSimple(this: *VirtualMachine, value: JSValue, writer: anytype, comptime escape_codes: bool) void { + this.printErrorlikeObject(value, null, null, @TypeOf(writer), writer, escape_codes, false); + } + + pub fn printErrorLikeObjectToConsole(this: *VirtualMachine, value: JSValue) void { + switch (Output.enable_ansi_colors_stderr) { + inline else => |colors| this.printErrorLikeObjectSimple(value, Output.errorWriter(), colors), + } + } + // When the Error-like object is one of our own, it's best to rely on the object directly instead of serializing it to a ZigException. // This is for: // - BuildMessage @@ -2780,13 +2997,13 @@ pub const VirtualMachine = struct { writer: Writer, current_exception_list: ?*ExceptionList = null, - pub fn iteratorWithColor(_vm: [*c]VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn iteratorWithColor(_vm: [*c]VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { iterator(_vm, globalObject, nextValue, ctx.?, true); } - pub fn iteratorWithOutColor(_vm: [*c]VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn iteratorWithOutColor(_vm: [*c]VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { iterator(_vm, globalObject, nextValue, ctx.?, false); } - inline fn iterator(_: [*c]VM, _: [*c]JSGlobalObject, nextValue: JSValue, ctx: ?*anyopaque, comptime color: bool) void { + inline fn iterator(_: [*c]VM, _: *JSGlobalObject, nextValue: JSValue, ctx: ?*anyopaque, comptime color: bool) void { const this_ = @as(*@This(), @ptrFromInt(@intFromPtr(ctx))); VirtualMachine.get().printErrorlikeObject(nextValue, null, this_.current_exception_list, Writer, this_.writer, color, allow_side_effects); } @@ -2880,7 +3097,7 @@ pub const VirtualMachine = struct { pub fn reportUncaughtException(globalObject: *JSGlobalObject, exception: *JSC.Exception) JSValue { var jsc_vm = globalObject.bunVM(); _ = jsc_vm.uncaughtException(globalObject, exception.value(), false); - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn printStackTrace(comptime Writer: type, writer: Writer, trace: ZigStackTrace, comptime allow_ansi_colors: bool) !void { @@ -2903,7 +3120,7 @@ pub const VirtualMachine = struct { const has_name = std.fmt.count("{}", .{frame.nameFormatter(false)}) > 0; - if (has_name) { + if (has_name and !frame.position.isInvalid()) { try writer.print( comptime Output.prettyFmt( " at {} ({})\n", @@ -2921,7 +3138,7 @@ pub const VirtualMachine = struct { ), }, ); - } else { + } else if (!frame.position.isInvalid()) { try writer.print( comptime Output.prettyFmt( " at {}\n", @@ -2936,6 +3153,33 @@ pub const VirtualMachine = struct { ), }, ); + } else if (has_name) { + try writer.print( + comptime Output.prettyFmt( + " at {}\n", + allow_ansi_colors, + ), + .{ + frame.nameFormatter( + allow_ansi_colors, + ), + }, + ); + } else { + try writer.print( + comptime Output.prettyFmt( + " at {}\n", + allow_ansi_colors, + ), + .{ + frame.sourceURLFormatter( + dir, + origin, + false, + allow_ansi_colors, + ), + }, + ); } } } @@ -2951,12 +3195,14 @@ pub const VirtualMachine = struct { var sourceURL = frame.source_url.toUTF8(bun.default_allocator); defer sourceURL.deinit(); - if (this.source_mappings.resolveMapping( + if (this.resolveSourceMapping( sourceURL.slice(), @max(frame.position.line.zeroBased(), 0), @max(frame.position.column.zeroBased(), 0), .no_source_contents, )) |lookup| { + const source_map = lookup.source_map; + defer if (source_map) |map| map.deref(); if (lookup.displaySourceURLIfNeeded(sourceURL.slice())) |source_url| { frame.source_url.deref(); frame.source_url = source_url; @@ -3068,12 +3314,11 @@ pub const VirtualMachine = struct { }, .source_index = 0, }, - // undefined is fine, because these two values are never read if `top.remapped == true` - .source_map = undefined, - .prefetched_source_code = undefined, + .source_map = null, + .prefetched_source_code = null, } else - this.source_mappings.resolveMapping( + this.resolveSourceMapping( top_source_url.slice(), @max(top.position.line.zeroBased(), 0), @max(top.position.column.zeroBased(), 0), @@ -3082,6 +3327,8 @@ pub const VirtualMachine = struct { if (maybe_lookup) |lookup| { const mapping = lookup.mapping; + const source_map = lookup.source_map; + defer if (source_map) |map| map.deref(); if (!top.remapped) { if (lookup.displaySourceURLIfNeeded(top_source_url.slice())) |src| { @@ -3091,7 +3338,8 @@ pub const VirtualMachine = struct { } const code = code: { - if (!top.remapped and lookup.source_map.isExternal()) { + if (bun.getRuntimeFeatureFlag("BUN_DISABLE_SOURCE_CODE_PREVIEW") or bun.getRuntimeFeatureFlag("BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW")) break :code ZigString.Slice.empty; + if (!top.remapped and lookup.source_map != null and lookup.source_map.?.isExternal()) { if (lookup.getSourceCode(top_source_url.slice())) |src| { break :code src; } @@ -3142,12 +3390,13 @@ pub const VirtualMachine = struct { if (frame == top or frame.position.isInvalid()) continue; const source_url = frame.source_url.toUTF8(bun.default_allocator); defer source_url.deinit(); - if (this.source_mappings.resolveMapping( + if (this.resolveSourceMapping( source_url.slice(), @max(frame.position.line.zeroBased(), 0), @max(frame.position.column.zeroBased(), 0), .no_source_contents, )) |lookup| { + defer if (lookup.source_map) |map| map.deref(); if (lookup.displaySourceURLIfNeeded(source_url.slice())) |src| { frame.source_url.deref(); frame.source_url = src; @@ -3166,6 +3415,9 @@ pub const VirtualMachine = struct { var exception = exception_holder.zigException(); defer exception_holder.deinit(this); + // The ZigException structure stores substrings of the source code, in + // which we need the lifetime of this data to outlive the inner call to + // remapZigException, but still get freed. var source_code_slice: ?ZigString.Slice = null; defer if (source_code_slice) |slice| slice.deinit(); @@ -3588,8 +3840,40 @@ pub const VirtualMachine = struct { writer.print("\n", .{}) catch {}; } + pub fn resolveSourceMapping( + this: *VirtualMachine, + path: []const u8, + line: i32, + column: i32, + source_handling: SourceMap.SourceContentHandling, + ) ?SourceMap.Mapping.Lookup { + return this.source_mappings.resolveMapping(path, line, column, source_handling) orelse { + if (this.standalone_module_graph) |graph| { + const file = graph.find(path) orelse return null; + const map = file.sourcemap.load() orelse return null; + + map.ref(); + + this.source_mappings.putValue(path, SavedSourceMap.Value.init(map)) catch + bun.outOfMemory(); + + const mapping = SourceMap.Mapping.find(map.mappings, line, column) orelse + return null; + + return .{ + .mapping = mapping, + .source_map = map, + .prefetched_source_code = null, + }; + } + + return null; + }; + } + extern fn Process__emitMessageEvent(global: *JSGlobalObject, value: JSValue) void; extern fn Process__emitDisconnectEvent(global: *JSGlobalObject) void; + extern fn Process__emitErrorEvent(global: *JSGlobalObject, value: JSValue) void; pub const IPCInstanceUnion = union(enum) { /// IPC is put in this "enabled but not started" state when IPC is detected @@ -3603,20 +3887,23 @@ pub const VirtualMachine = struct { pub const IPCInstance = struct { globalThis: ?*JSGlobalObject, - context: if (Environment.isPosix) *uws.SocketContext else u0, + context: if (Environment.isPosix) *uws.SocketContext else void, data: IPC.IPCData, + has_disconnect_called: bool = false, pub usingnamespace bun.New(@This()); - pub fn ipc(this: *IPCInstance) *IPC.IPCData { + const node_cluster_binding = @import("./node/node_cluster_binding.zig"); + + pub fn ipc(this: *IPCInstance) ?*IPC.IPCData { return &this.data; } - pub fn handleIPCMessage( - this: *IPCInstance, - message: IPC.DecodedIPCMessage, - ) void { + pub fn handleIPCMessage(this: *IPCInstance, message: IPC.DecodedIPCMessage) void { JSC.markBinding(@src()); + const globalThis = this.globalThis orelse return; + const event_loop = JSC.VirtualMachine.get().eventLoop(); + switch (message) { // In future versions we can read this in order to detect version mismatches, // or disable future optimizations if the subprocess is old. @@ -3625,34 +3912,54 @@ pub const VirtualMachine = struct { }, .data => |data| { IPC.log("Received IPC message from parent", .{}); - if (this.globalThis) |global| { - Process__emitMessageEvent(global, data); - } + event_loop.enter(); + defer event_loop.exit(); + Process__emitMessageEvent(globalThis, data); + }, + .internal => |data| { + IPC.log("Received IPC internal message from parent", .{}); + event_loop.enter(); + defer event_loop.exit(); + node_cluster_binding.handleInternalMessageChild(globalThis, data); }, } } pub fn handleIPCClose(this: *IPCInstance) void { - if (this.globalThis) |global| { - var vm = global.bunVM(); - vm.ipc = null; - Process__emitDisconnectEvent(global); - } + IPC.log("IPCInstance#handleIPCClose", .{}); + var vm = VirtualMachine.get(); + vm.ipc = null; + const event_loop = vm.eventLoop(); + node_cluster_binding.child_singleton.deinit(); + event_loop.enter(); + Process__emitDisconnectEvent(vm.global); + event_loop.exit(); if (Environment.isPosix) { uws.us_socket_context_free(0, this.context); } this.destroy(); } + extern fn Bun__setChannelRef(*JSC.JSGlobalObject, bool) void; + + export fn Bun__closeChildIPC(global: *JSGlobalObject) void { + if (global.bunVM().ipc) |*current_ipc| { + switch (current_ipc.*) { + .initialized => |instance| { + instance.data.close(true); + }, + .waiting => {}, + } + } + } + pub const Handlers = IPC.NewIPCHandler(IPCInstance); }; - const IPCInfoType = if (Environment.isWindows) []const u8 else bun.FileDescriptor; + const IPCInfoType = bun.FileDescriptor; pub fn initIPCInstance(this: *VirtualMachine, info: IPCInfoType, mode: IPC.Mode) void { - IPC.log("initIPCInstance {" ++ (if (Environment.isWindows) "s" else "") ++ "}", .{info}); - this.ipc = .{ - .waiting = .{ .info = info, .mode = mode }, - }; + IPC.log("initIPCInstance {}", .{info}); + this.ipc = .{ .waiting = .{ .info = info, .mode = mode } }; } pub fn getIPCInstance(this: *VirtualMachine) ?*IPCInstance { @@ -3660,7 +3967,7 @@ pub const VirtualMachine = struct { if (this.ipc.? != .waiting) return this.ipc.?.initialized; const opts = this.ipc.?.waiting; - IPC.log("getIPCInstance {" ++ (if (Environment.isWindows) "s" else "") ++ "}", .{opts.info}); + IPC.log("getIPCInstance {}", .{opts.info}); this.event_loop.ensureWaker(); @@ -3675,6 +3982,8 @@ pub const VirtualMachine = struct { .data = undefined, }); + this.ipc = .{ .initialized = instance }; + const socket = IPC.Socket.fromFd(context, opts.info, IPCInstance, instance, null) orelse { instance.destroy(); this.ipc = null; @@ -3690,14 +3999,16 @@ pub const VirtualMachine = struct { .windows => instance: { var instance = IPCInstance.new(.{ .globalThis = this.global, - .context = 0, + .context = {}, .data = .{ .mode = opts.mode }, }); + this.ipc = .{ .initialized = instance }; + instance.data.configureClient(IPCInstance, instance, opts.info) catch { instance.destroy(); this.ipc = null; - Output.warn("Unable to start IPC pipe '{s}'", .{opts.info}); + Output.warn("Unable to start IPC pipe '{}'", .{opts.info}); return null; }; @@ -3705,8 +4016,6 @@ pub const VirtualMachine = struct { }, }; - this.ipc = .{ .initialized = instance }; - instance.data.writeVersionPacket(); return instance; @@ -3728,11 +4037,38 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime pub const Watcher = GenericWatcher.NewWatcher(*@This()); const Reloader = @This(); - onAccept: std.ArrayHashMapUnmanaged(GenericWatcher.HashType, bun.BabyList(OnAcceptCallback), bun.ArrayIdentityContext, false) = .{}, ctx: *Ctx, verbose: bool = false, + pending_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + + tombstones: bun.StringHashMapUnmanaged(*bun.fs.FileSystem.RealFS.EntriesOption) = .{}, + + pub fn init(ctx: *Ctx, fs: *bun.fs.FileSystem, verbose: bool, clear_screen_flag: bool) *@This().Watcher { + const reloader = bun.default_allocator.create(Reloader) catch bun.outOfMemory(); + reloader.* = .{ + .ctx = ctx, + .verbose = Environment.enable_logs or verbose, + }; - tombstones: std.StringHashMapUnmanaged(*bun.fs.FileSystem.RealFS.EntriesOption) = .{}, + clear_screen = clear_screen_flag; + const watcher = @This().Watcher.init(reloader, fs, bun.default_allocator) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + Output.panic("Failed to enable File Watcher: {s}", .{@errorName(err)}); + }; + watcher.start() catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + Output.panic("Failed to start File Watcher: {s}", .{@errorName(err)}); + }; + return watcher; + } + + fn debug(comptime fmt: string, args: anytype) void { + if (Environment.enable_logs) { + Output.scoped(.hot_reloader, false)(fmt, args); + } else { + Output.prettyErrorln("watcher: " ++ fmt, args); + } + } pub fn eventLoop(this: @This()) *EventLoopType { return this.ctx.eventLoop(); @@ -3768,7 +4104,18 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime } pub fn run(this: *HotReloadTask) void { - this.reloader.ctx.reload(); + // Since we rely on the event loop for hot reloads, there can be + // a delay before the next reload begins. In the time between the + // last reload and the next one, we shouldn't schedule any more + // hot reloads. Since we reload literally everything, we don't + // need to worry about missing any changes. + // + // Note that we set the count _before_ we reload, so that if we + // get another hot reload request while we're reloading, we'll + // still enqueue it. + while (this.reloader.pending_count.swap(0, .monotonic) > 0) { + this.reloader.ctx.reload(); + } } pub fn enqueue(this: *HotReloadTask) void { @@ -3779,12 +4126,15 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime if (comptime reload_immediately) { Output.flush(); if (comptime Ctx == ImportWatcher) { - this.reloader.ctx.rareData().closeAllListenSocketsForWatchMode(); + if (this.reloader.ctx.rare_data) |rare| + rare.closeAllListenSocketsForWatchMode(); } bun.reloadProcess(bun.default_allocator, clear_screen, false); unreachable; } + _ = this.reloader.pending_count.fetchAdd(1, .monotonic); + BunDebugger__willHotReload(); var that = bun.default_allocator.create(HotReloadTask) catch unreachable; @@ -3799,21 +4149,6 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime } }; - fn NewCallback(comptime FunctionSignature: type) type { - return union(enum) { - javascript_callback: JSC.Strong, - zig_callback: struct { - ptr: *anyopaque, - function: *const FunctionSignature, - }, - }; - } - - pub const OnAcceptCallback = NewCallback(fn ( - vm: *JSC.VirtualMachine, - specifier: []const u8, - ) void); - pub fn enableHotModuleReloading(this: *Ctx) void { if (comptime @TypeOf(this.bun_watcher) == ImportWatcher) { if (this.bun_watcher != .none) @@ -3826,7 +4161,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime var reloader = bun.default_allocator.create(Reloader) catch bun.outOfMemory(); reloader.* = .{ .ctx = this, - .verbose = if (@hasField(Ctx, "log")) this.log.level.atLeast(.info) else false, + .verbose = Environment.enable_logs or if (@hasField(Ctx, "log")) this.log.level.atLeast(.info) else false, }; if (comptime @TypeOf(this.bun_watcher) == ImportWatcher) { @@ -3905,8 +4240,10 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime } else { return this.ctx.bun_watcher.hot; } - } else { + } else if (@typeInfo(@TypeOf(this.ctx.bun_watcher)) == .Optional) { return this.ctx.bun_watcher.?; + } else { + return this.ctx.bun_watcher; } } @@ -3916,18 +4253,18 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime changed_files: []?[:0]u8, watchlist: GenericWatcher.WatchList, ) void { - var slice = watchlist.slice(); + const slice = watchlist.slice(); const file_paths = slice.items(.file_path); - var counts = slice.items(.count); + const counts = slice.items(.count); const kinds = slice.items(.kind); const hashes = slice.items(.hash); const parents = slice.items(.parent_hash); const file_descriptors = slice.items(.fd); - var ctx = this.getContext(); + const ctx = this.getContext(); defer ctx.flushEvictions(); defer Output.flush(); - var bundler = if (@TypeOf(this.ctx.bundler) == *bun.Bundler) + const bundler = if (@TypeOf(this.ctx.bundler) == *bun.Bundler) this.ctx.bundler else &this.ctx.bundler; @@ -3953,9 +4290,8 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime // const path = Fs.PathName.init(file_path); const id = hashes[event.index]; - if (comptime Environment.isDebug) { - Output.prettyErrorln("[watch] {s} ({s}, {})", .{ file_path, @tagName(kind), event.op }); - } + if (this.verbose) + debug("onFileUpdate {s} ({s}, {})", .{ file_path, @tagName(kind), event.op }); switch (kind) { .file => { @@ -3969,7 +4305,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime } if (this.verbose) - Output.prettyErrorln("File changed: {s}", .{fs.relativeTo(file_path)}); + debug("File changed: {s}", .{fs.relativeTo(file_path)}); if (event.op.write or event.op.delete or event.op.rename) { current_task.append(id); @@ -4091,13 +4427,13 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime last_file_hash = file_hash; if (this.verbose) - Output.prettyErrorln(" File change: {s}", .{fs.relativeTo(abs_path)}); + debug("File change: {s}", .{fs.relativeTo(abs_path)}); } } } if (this.verbose) { - Output.prettyErrorln(" Dir change: {s}", .{fs.relativeTo(file_path)}); + debug("Dir change: {s}", .{fs.relativeTo(file_path)}); } }, } @@ -4121,3 +4457,31 @@ export fn Bun__removeSourceProviderSourceMap(vm: *VirtualMachine, opaque_source_ } pub export var isBunTest: bool = false; + +// TODO: evaluate if this has any measurable performance impact. +pub var synthetic_allocation_limit: usize = std.math.maxInt(u32); +pub var string_allocation_limit: usize = std.math.maxInt(u32); + +comptime { + @export(synthetic_allocation_limit, .{ .name = "Bun__syntheticAllocationLimit" }); + @export(string_allocation_limit, .{ .name = "Bun__stringSyntheticAllocationLimit" }); +} + +pub export fn Bun__setSyntheticAllocationLimitForTesting(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + const args = callframe.arguments(1).slice(); + if (args.len < 1) { + globalObject.throwNotEnoughArguments("setSyntheticAllocationLimitForTesting", 1, args.len); + return JSValue.zero; + } + + if (!args[0].isNumber()) { + globalObject.throwInvalidArguments("setSyntheticAllocationLimitForTesting expects a number", .{}); + return JSValue.zero; + } + + const limit: usize = @intCast(@max(args[0].coerceToInt64(globalObject), 1024 * 1024)); + const prev = synthetic_allocation_limit; + synthetic_allocation_limit = limit; + string_allocation_limit = limit; + return JSValue.jsNumber(prev); +} diff --git a/src/bun.js/javascript_core_c_api.zig b/src/bun.js/javascript_core_c_api.zig index b4faa3f722efc..3bba44b331c7e 100644 --- a/src/bun.js/javascript_core_c_api.zig +++ b/src/bun.js/javascript_core_c_api.zig @@ -163,7 +163,6 @@ pub const OpaqueJSPropertyNameAccumulator = struct_OpaqueJSPropertyNameAccumulat // This is a workaround for not receiving a JSException* object // This function lets us use the C API but returns a plain old JSValue // allowing us to have exceptions that include stack traces -pub extern "c" fn JSObjectCallAsFunctionReturnValue(ctx: JSContextRef, object: cpp.JSValue, thisObject: cpp.JSValue, argumentCount: usize, arguments: [*c]const JSValueRef) cpp.JSValue; pub extern "c" fn JSObjectCallAsFunctionReturnValueHoldingAPILock(ctx: JSContextRef, object: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef) cpp.JSValue; pub extern fn JSRemoteInspectorDisableAutoStart() void; @@ -173,101 +172,4 @@ pub extern fn JSRemoteInspectorSetLogToSystemConsole(enabled: bool) void; pub extern fn JSRemoteInspectorGetInspectionEnabledByDefault(void) bool; pub extern fn JSRemoteInspectorSetInspectionEnabledByDefault(enabled: bool) void; -// -- Manual -- - -const size_t = usize; - -pub const CellType = enum(u8) { - pub const LastMaybeFalsyCellPrimitive = 2; - pub const LastJSCObjectType = 73; - - CellType = 0, - StringType = 1, - HeapBigIntType = 2, - - SymbolType = 3, - GetterSetterType = 4, - CustomGetterSetterType = 5, - APIValueWrapperType = 6, - NativeExecutableType = 7, - ProgramExecutableType = 8, - ModuleProgramExecutableType = 9, - EvalExecutableType = 10, - FunctionExecutableType = 11, - UnlinkedFunctionExecutableType = 12, - UnlinkedProgramCodeBlockType = 13, - UnlinkedModuleProgramCodeBlockType = 14, - UnlinkedEvalCodeBlockType = 15, - UnlinkedFunctionCodeBlockType = 16, - CodeBlockType = 17, - JSImmutableButterflyType = 18, - JSSourceCodeType = 19, - JSScriptFetcherType = 20, - JSScriptFetchParametersType = 21, - ObjectType = 22, - FinalObjectType = 23, - JSCalleeType = 24, - JSFunctionType = 25, - InternalFunctionType = 26, - NullSetterFunctionType = 27, - BooleanObjectType = 28, - NumberObjectType = 29, - ErrorInstanceType = 30, - GlobalProxyType = 31, - DirectArgumentsType = 32, - ScopedArgumentsType = 33, - ClonedArgumentsType = 34, - ArrayType = 35, - DerivedArrayType = 36, - ArrayBufferType = 37, - Int8ArrayType = 38, - Uint8ArrayType = 39, - Uint8ClampedArrayType = 40, - Int16ArrayType = 41, - Uint16ArrayType = 42, - Int32ArrayType = 43, - Uint32ArrayType = 44, - Float32ArrayType = 45, - Float64ArrayType = 46, - BigInt64ArrayType = 47, - BigUint64ArrayType = 48, - DataViewType = 49, - GlobalObjectType = 50, - GlobalLexicalEnvironmentType = 51, - LexicalEnvironmentType = 52, - ModuleEnvironmentType = 53, - StrictEvalActivationType = 54, - WithScopeType = 55, - ModuleNamespaceObjectType = 56, - RegExpObjectType = 57, - JSDateType = 58, - ProxyObjectType = 59, - JSGeneratorType = 60, - JSAsyncGeneratorType = 61, - JSArrayIteratorType = 62, - JSMapIteratorType = 63, - JSSetIteratorType = 64, - JSStringIteratorType = 65, - JSPromiseType = 66, - JSMapType = 67, - JSSetType = 68, - JSWeakMapType = 69, - JSWeakSetType = 70, - WebAssemblyModuleType = 71, - WebAssemblyInstanceType = 72, - WebAssemblyGCObjectType = 73, - StringObjectType = 74, - DerivedStringObjectType = 75, - - MaxJSType = 255, - _, - - pub fn isString(this: CellType) bool { - return switch (this) { - .StringType => true, - else => false, - }; - } -}; - pub extern "c" fn JSObjectGetProxyTarget(JSObjectRef) JSObjectRef; diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 9de1852925859..d6a5e0daeaa80 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -88,56 +88,6 @@ const String = bun.String; const debug = Output.scoped(.ModuleLoader, true); -// Setting BUN_OVERRIDE_MODULE_PATH to the path to the bun repo will make it so modules are loaded -// from there instead of the ones embedded into the binary. -// In debug mode, this is set automatically for you, using the path relative to this file. -fn jsModuleFromFile(from_path: string, comptime input: string) string { - // `modules_dev` is not minified or committed. Later we could also try loading source maps for it too. - const moduleFolder = if (comptime Environment.isDebug) "modules_dev" else "modules"; - - const Holder = struct { - pub const file = @embedFile("../js/out/" ++ moduleFolder ++ "/" ++ input); - }; - - if ((comptime !Environment.allow_assert) and from_path.len == 0) { - return Holder.file; - } - - var file: std.fs.File = undefined; - if ((comptime Environment.allow_assert) and from_path.len == 0) { - const absolute_path = comptime (Environment.base_path ++ (std.fs.path.dirname(std.fs.path.dirname(@src().file).?).?) ++ "/js/out/" ++ moduleFolder ++ "/" ++ input); - file = std.fs.openFileAbsoluteZ(absolute_path, .{ .mode = .read_only }) catch { - const WarnOnce = struct { - pub var warned = false; - }; - if (!WarnOnce.warned) { - WarnOnce.warned = true; - Output.prettyErrorln("Could not find file: " ++ absolute_path ++ " - using embedded version", .{}); - } - return Holder.file; - }; - } else { - var parts = [_]string{ from_path, "src/js/out/" ++ moduleFolder ++ "/" ++ input }; - var buf: bun.PathBuffer = undefined; - var absolute_path_to_use = Fs.FileSystem.instance.absBuf(&parts, &buf); - buf[absolute_path_to_use.len] = 0; - file = std.fs.openFileAbsoluteZ(absolute_path_to_use[0..absolute_path_to_use.len :0], .{ .mode = .read_only }) catch { - const WarnOnce = struct { - pub var warned = false; - }; - if (!WarnOnce.warned) { - WarnOnce.warned = true; - Output.prettyErrorln("Could not find file: {s}, so using embedded version", .{absolute_path_to_use}); - } - return Holder.file; - }; - } - - const contents = file.readToEndAlloc(bun.default_allocator, std.math.maxInt(usize)) catch @panic("Cannot read file " ++ input); - file.close(); - return contents; -} - inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag, specifier: String) ResolvedSource { return ResolvedSource{ .allocator = null, @@ -166,10 +116,11 @@ fn dumpSourceString(vm: *VirtualMachine, specifier: string, written: []const u8) fn dumpSourceStringFailiable(vm: *VirtualMachine, specifier: string, written: []const u8) !void { if (!Environment.isDebug) return; + if (bun.getRuntimeFeatureFlag("BUN_DEBUG_NO_DUMP")) return; const BunDebugHolder = struct { pub var dir: ?std.fs.Dir = null; - pub var lock: bun.Lock = bun.Lock.init(); + pub var lock: bun.Lock = .{}; }; BunDebugHolder.lock.lock(); @@ -208,6 +159,7 @@ fn dumpSourceStringFailiable(vm: *VirtualMachine, specifier: string, written: [] return; }; if (vm.source_mappings.get(specifier)) |mappings| { + defer mappings.deref(); const map_path = std.mem.concat(bun.default_allocator, u8, &.{ std.fs.path.basename(specifier), ".map" }) catch bun.outOfMemory(); defer bun.default_allocator.free(map_path); const file = try parent.createFile(map_path, .{}); @@ -264,9 +216,9 @@ pub const RuntimeTranspilerStore = struct { pub const Queue = bun.UnboundedQueue(TranspilerJob, .next); - pub fn init(allocator: std.mem.Allocator) RuntimeTranspilerStore { + pub fn init() RuntimeTranspilerStore { return RuntimeTranspilerStore{ - .store = TranspilerJob.Store.init(allocator), + .store = TranspilerJob.Store.init(bun.typedAllocator(TranspilerJob)), }; } @@ -338,7 +290,7 @@ pub const RuntimeTranspilerStore = struct { work_task: JSC.WorkPoolTask = .{ .callback = runFromWorkerThread }, next: ?*TranspilerJob = null, - pub const Store = bun.HiveArray(TranspilerJob, 64).Fallback; + pub const Store = bun.HiveArray(TranspilerJob, if (bun.heap_breakdown.enabled) 0 else 64).Fallback; pub const Fetcher = union(enum) { virtual_module: bun.String, @@ -391,7 +343,7 @@ pub const RuntimeTranspilerStore = struct { }; resolved_source.tag = brk: { - if (resolved_source.commonjs_exports_len > 0) { + if (resolved_source.is_commonjs_module) { const actual_package_json: *PackageJSON = brk2: { // this should already be cached virtually always so it's fine to do this const dir_info = (vm.bundler.resolver.readDirInfo(this.path.name.dir) catch null) orelse @@ -409,12 +361,10 @@ pub const RuntimeTranspilerStore = struct { }; const parse_error = this.parse_error; - if (!vm.transpiler_store.store.hive.in(this)) { - this.promise.deinit(); - } + this.promise.deinit(); this.deinit(); - _ = vm.transpiler_store.store.hive.put(this); + _ = vm.transpiler_store.store.put(this); ModuleLoader.AsyncModule.fulfill(globalThis, promise, resolved_source, parse_error, specifier, referrer, &log); } @@ -617,7 +567,7 @@ pub const RuntimeTranspilerStore = struct { .specifier = duped, .source_url = duped.createIfDifferent(path.text), .hash = 0, - .commonjs_exports_len = if (entry.metadata.module_type == .cjs) std.math.maxInt(u32) else 0, + .is_commonjs_module = entry.metadata.module_type == .cjs, }; return; @@ -731,11 +681,7 @@ pub const RuntimeTranspilerStore = struct { .source_code = source_code, .specifier = duped, .source_url = duped.createIfDifferent(path.text), - .commonjs_exports = null, - .commonjs_exports_len = if (parse_result.ast.exports_kind == .cjs) - std.math.maxInt(u32) - else - 0, + .is_commonjs_module = parse_result.ast.has_commonjs_export_names or parse_result.ast.exports_kind == .cjs, .hash = 0, }; } @@ -807,8 +753,6 @@ pub const ModuleLoader = struct { // This is all the state used by the printer to print the module parse_result: ParseResult, - // stmt_blocks: []*js_ast.Stmt.Data.Store.All.Block = &[_]*js_ast.Stmt.Data.Store.All.Block{}, - // expr_blocks: []*js_ast.Expr.Data.Store.All.Block = &[_]*js_ast.Expr.Data.Store.All.Block{}, promise: JSC.Strong = .{}, path: Fs.Path, specifier: string = "", @@ -895,12 +839,7 @@ pub const ModuleLoader = struct { pub fn onWakeHandler(ctx: *anyopaque, _: *PackageManager) void { debug("onWake", .{}); var this = bun.cast(*Queue, ctx); - const concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch bun.outOfMemory(); - concurrent_task.* = .{ - .task = JSC.Task.init(this), - .auto_delete = true, - }; - this.vm().enqueueTaskConcurrent(concurrent_task); + this.vm().enqueueTaskConcurrent(JSC.ConcurrentTask.createFrom(this)); } pub fn onPoll(this: *Queue) void { @@ -1230,6 +1169,8 @@ pub const ModuleLoader = struct { } log.deinit(); + debug("fulfill: {any}", .{specifier}); + Bun__onFulfillAsyncModule( globalThis, promise, @@ -1309,19 +1250,19 @@ pub const ModuleLoader = struct { var error_instance = ZigString.init(msg).withEncoding().toErrorInstance(globalThis); if (result.url.len > 0) - error_instance.put(globalThis, ZigString.static("url"), ZigString.init(result.url).withEncoding().toValueGC(globalThis)); - error_instance.put(globalThis, ZigString.static("name"), ZigString.init(name).withEncoding().toValueGC(globalThis)); - error_instance.put(globalThis, ZigString.static("pkg"), ZigString.init(result.name).withEncoding().toValueGC(globalThis)); - error_instance.put(globalThis, ZigString.static("specifier"), ZigString.init(this.specifier).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("url"), ZigString.init(result.url).withEncoding().toJS(globalThis)); + error_instance.put(globalThis, ZigString.static("name"), ZigString.init(name).withEncoding().toJS(globalThis)); + error_instance.put(globalThis, ZigString.static("pkg"), ZigString.init(result.name).withEncoding().toJS(globalThis)); + error_instance.put(globalThis, ZigString.static("specifier"), ZigString.init(this.specifier).withEncoding().toJS(globalThis)); const location = logger.rangeData(&this.parse_result.source, this.parse_result.ast.import_records.at(import_record_id).range, "").location.?; - error_instance.put(globalThis, ZigString.static("sourceURL"), ZigString.init(this.parse_result.source.path.text).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("sourceURL"), ZigString.init(this.parse_result.source.path.text).withEncoding().toJS(globalThis)); error_instance.put(globalThis, ZigString.static("line"), JSValue.jsNumber(location.line)); if (location.line_text) |line_text| { - error_instance.put(globalThis, ZigString.static("lineText"), ZigString.init(line_text).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("lineText"), ZigString.init(line_text).withEncoding().toJS(globalThis)); } error_instance.put(globalThis, ZigString.static("column"), JSValue.jsNumber(location.column)); if (this.referrer.len > 0 and !strings.eqlComptime(this.referrer, "undefined")) { - error_instance.put(globalThis, ZigString.static("referrer"), ZigString.init(this.referrer).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("referrer"), ZigString.init(this.referrer).withEncoding().toJS(globalThis)); } const promise_value = this.promise.swap(); @@ -1400,21 +1341,21 @@ pub const ModuleLoader = struct { var error_instance = ZigString.init(msg).withEncoding().toErrorInstance(globalThis); if (result.url.len > 0) - error_instance.put(globalThis, ZigString.static("url"), ZigString.init(result.url).withEncoding().toValueGC(globalThis)); - error_instance.put(globalThis, ZigString.static("name"), ZigString.init(name).withEncoding().toValueGC(globalThis)); - error_instance.put(globalThis, ZigString.static("pkg"), ZigString.init(result.name).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("url"), ZigString.init(result.url).withEncoding().toJS(globalThis)); + error_instance.put(globalThis, ZigString.static("name"), ZigString.init(name).withEncoding().toJS(globalThis)); + error_instance.put(globalThis, ZigString.static("pkg"), ZigString.init(result.name).withEncoding().toJS(globalThis)); if (this.specifier.len > 0 and !strings.eqlComptime(this.specifier, "undefined")) { - error_instance.put(globalThis, ZigString.static("referrer"), ZigString.init(this.specifier).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("referrer"), ZigString.init(this.specifier).withEncoding().toJS(globalThis)); } const location = logger.rangeData(&this.parse_result.source, this.parse_result.ast.import_records.at(import_record_id).range, "").location.?; error_instance.put(globalThis, ZigString.static("specifier"), ZigString.init( this.parse_result.ast.import_records.at(import_record_id).path.text, - ).withEncoding().toValueGC(globalThis)); - error_instance.put(globalThis, ZigString.static("sourceURL"), ZigString.init(this.parse_result.source.path.text).withEncoding().toValueGC(globalThis)); + ).withEncoding().toJS(globalThis)); + error_instance.put(globalThis, ZigString.static("sourceURL"), ZigString.init(this.parse_result.source.path.text).withEncoding().toJS(globalThis)); error_instance.put(globalThis, ZigString.static("line"), JSValue.jsNumber(location.line)); if (location.line_text) |line_text| { - error_instance.put(globalThis, ZigString.static("lineText"), ZigString.init(line_text).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("lineText"), ZigString.init(line_text).withEncoding().toJS(globalThis)); } error_instance.put(globalThis, ZigString.static("column"), JSValue.jsNumber(location.column)); @@ -1476,11 +1417,6 @@ pub const ModuleLoader = struct { dumpSource(jsc_vm, specifier, &printer); } - const commonjs_exports = try bun.default_allocator.alloc(ZigString, parse_result.ast.commonjs_export_names.len); - for (parse_result.ast.commonjs_export_names, commonjs_exports) |name, *out| { - out.* = ZigString.fromUTF8(name); - } - if (jsc_vm.isWatcherEnabled()) { var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, bun.String.init(specifier), path.text, null, false); @@ -1498,16 +1434,7 @@ pub const ModuleLoader = struct { } } - resolved_source.commonjs_exports = if (commonjs_exports.len > 0) - commonjs_exports.ptr - else - null; - resolved_source.commonjs_exports_len = if (commonjs_exports.len > 0) - @as(u32, @truncate(commonjs_exports.len)) - else if (parse_result.ast.exports_kind == .cjs) - std.math.maxInt(u32) - else - 0; + resolved_source.is_commonjs_module = parse_result.ast.has_commonjs_export_names or parse_result.ast.exports_kind == .cjs; return resolved_source; } @@ -1517,16 +1444,7 @@ pub const ModuleLoader = struct { .source_code = bun.String.createLatin1(printer.ctx.getWritten()), .specifier = String.init(specifier), .source_url = String.init(path.text), - .commonjs_exports = if (commonjs_exports.len > 0) - commonjs_exports.ptr - else - null, - .commonjs_exports_len = if (commonjs_exports.len > 0) - @as(u32, @truncate(commonjs_exports.len)) - else if (parse_result.ast.exports_kind == .cjs) - std.math.maxInt(u32) - else - 0, + .is_commonjs_module = parse_result.ast.has_commonjs_export_names or parse_result.ast.exports_kind == .cjs, .hash = 0, }; @@ -1719,7 +1637,7 @@ pub const ModuleLoader = struct { } } - var parse_result = switch (disable_transpilying or + var parse_result: ParseResult = switch (disable_transpilying or (loader == .json and !path.isJSONCFile())) { inline else => |return_file_only| brk: { break :brk jsc_vm.bundler.parseMaybeReturnFileOnly( @@ -1811,7 +1729,7 @@ pub const ModuleLoader = struct { .allocator = null, .source_code = switch (comptime flags) { .print_source_and_clone => bun.String.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), - .print_source => bun.String.static(parse_result.source.contents), + .print_source => bun.String.init(parse_result.source.contents), else => @compileError("unreachable"), }, .specifier = input_specifier, @@ -1821,12 +1739,23 @@ pub const ModuleLoader = struct { } if (loader == .json or loader == .toml) { + if (parse_result.empty) { + return ResolvedSource{ + .allocator = null, + .specifier = input_specifier, + .source_url = input_specifier.createIfDifferent(path.text), + .hash = 0, + .jsvalue_for_export = JSC.JSValue.createEmptyObject(jsc_vm.global, 0), + .tag = .exports_object, + }; + } + return ResolvedSource{ .allocator = null, .specifier = input_specifier, .source_url = input_specifier.createIfDifferent(path.text), .hash = 0, - .jsvalue_for_export = parse_result.ast.parts.@"[0]"().stmts[0].data.s_expr.value.toJS(allocator, globalObject orelse jsc_vm.global) catch @panic("Unexpected JS error"), + .jsvalue_for_export = parse_result.ast.parts.@"[0]"().stmts[0].data.s_expr.value.toJS(allocator, globalObject orelse jsc_vm.global, .{}) catch @panic("Unexpected JS error"), .tag = .exports_object, }; } @@ -1866,7 +1795,7 @@ pub const ModuleLoader = struct { .specifier = input_specifier, .source_url = input_specifier.createIfDifferent(path.text), .hash = 0, - .commonjs_exports_len = if (entry.metadata.module_type == .cjs) std.math.maxInt(u32) else 0, + .is_commonjs_module = entry.metadata.module_type == .cjs, .tag = brk: { if (entry.metadata.module_type == .cjs and parse_result.source.path.isFile()) { const actual_package_json: *PackageJSON = package_json orelse brk2: { @@ -1937,10 +1866,10 @@ pub const ModuleLoader = struct { var printer = source_code_printer.*; printer.ctx.reset(); - + defer source_code_printer.* = printer; _ = brk: { var mapper = jsc_vm.sourceMapHandler(&printer); - defer source_code_printer.* = printer; + break :brk try jsc_vm.bundler.printWithSourceMap( parse_result, @TypeOf(&printer), @@ -1954,11 +1883,6 @@ pub const ModuleLoader = struct { dumpSource(jsc_vm, specifier, &printer); } - const commonjs_exports = try bun.default_allocator.alloc(ZigString, parse_result.ast.commonjs_export_names.len); - for (parse_result.ast.commonjs_export_names, commonjs_exports) |name, *out| { - out.* = ZigString.fromUTF8(name); - } - defer { if (is_main) { jsc_vm.has_loaded = true; @@ -1967,17 +1891,7 @@ pub const ModuleLoader = struct { if (jsc_vm.isWatcherEnabled()) { var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, input_specifier, path.text, null, false); - - resolved_source.commonjs_exports = if (commonjs_exports.len > 0) - commonjs_exports.ptr - else - null; - resolved_source.commonjs_exports_len = if (commonjs_exports.len > 0) - @as(u32, @truncate(commonjs_exports.len)) - else if (parse_result.ast.exports_kind == .cjs) - std.math.maxInt(u32) - else - 0; + resolved_source.is_commonjs_module = parse_result.ast.has_commonjs_export_names or parse_result.ast.exports_kind == .cjs; return resolved_source; } @@ -1986,7 +1900,7 @@ pub const ModuleLoader = struct { if (parse_result.ast.exports_kind == .cjs and parse_result.source.path.isFile()) { const actual_package_json: *PackageJSON = package_json orelse brk2: { // this should already be cached virtually always so it's fine to do this - const dir_info = (jsc_vm.bundler.resolver.readDirInfo(parse_result.source.path.name.dir) catch null) orelse + const dir_info = (jsc_vm.bundler.resolver.readDirInfo(parse_result.source.path.name.dirOrDot()) catch null) orelse break :brk .javascript; break :brk2 dir_info.package_json orelse dir_info.enclosing_package_json; @@ -2008,25 +1922,14 @@ pub const ModuleLoader = struct { if (written.len > 1024 * 1024 * 2 or jsc_vm.smol) { printer.ctx.buffer.deinit(); - source_code_printer.* = printer; } break :brk result; }, .specifier = input_specifier, .source_url = input_specifier.createIfDifferent(path.text), - .commonjs_exports = if (commonjs_exports.len > 0) - commonjs_exports.ptr - else - null, - .commonjs_exports_len = if (commonjs_exports.len > 0) - @as(u32, @truncate(commonjs_exports.len)) - else if (parse_result.ast.exports_kind == .cjs) - std.math.maxInt(u32) - else - 0, + .is_commonjs_module = parse_result.ast.has_commonjs_export_names or parse_result.ast.exports_kind == .cjs, .hash = 0, - .tag = tag, }; }, @@ -2160,6 +2063,58 @@ pub const ModuleLoader = struct { }, else => { + if (virtual_source == null) { + if (comptime !disable_transpilying) { + if (jsc_vm.isWatcherEnabled()) auto_watch: { + if (std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { + const input_fd: bun.StoredFileDescriptorType = brk: { + // on macOS, we need a file descriptor to receive event notifications on it. + // so we use O_EVTONLY to open the file descriptor without asking any additional permissions. + if (comptime Environment.isMac) { + switch (bun.sys.open( + &(std.posix.toPosixPath(path.text) catch break :auto_watch), + bun.C.O_EVTONLY, + 0, + )) { + .err => break :auto_watch, + .result => |fd| break :brk @enumFromInt(fd.cast()), + } + } else { + // Otherwise, don't even bother opening it. + break :brk .zero; + } + }; + const hash = JSC.GenericWatcher.getHash(path.text); + switch (jsc_vm.bun_watcher.addFile( + input_fd, + path.text, + hash, + loader, + .zero, + null, + true, + )) { + .err => { + if (comptime Environment.isMac) { + // If any error occurs and we just + // opened the file descriptor to + // receive event notifications on + // it, we should close it. + if (input_fd != .zero) { + _ = bun.sys.close(bun.toFD(input_fd)); + } + } + + // we don't consider it a failure if we cannot watch the file + // they didn't open the file + }, + .result => {}, + } + } + } + } + } + var stack_buf = std.heap.stackFallback(4096, jsc_vm.allocator); const allocator = stack_buf.get(); var buf = MutableString.init2048(allocator) catch bun.outOfMemory(); @@ -2339,7 +2294,7 @@ pub const ModuleLoader = struct { virtual_source = &virtual_source_to_use.?; } } else { - ret.* = ErrorableResolvedSource.err(error.JSErrorObject, globalObject.createErrorInstanceWithCode(.MODULE_NOT_FOUND, "Blob not found", .{}).asVoid()); + ret.* = ErrorableResolvedSource.err(error.JSErrorObject, globalObject.MODULE_NOT_FOUND("Blob not found", .{}).toJS().asVoid()); return null; } } @@ -2513,6 +2468,14 @@ pub const ModuleLoader = struct { // These are defined in src/js/* .@"bun:ffi" => return jsSyntheticModule(.@"bun:ffi", specifier), + .@"bun:sql" => { + if (!Environment.isDebug) { + if (!is_allowed_to_use_internal_testing_apis and !bun.FeatureFlags.postgresql) + return null; + } + + return jsSyntheticModule(.@"bun:sql", specifier); + }, .@"bun:sqlite" => return jsSyntheticModule(.@"bun:sqlite", specifier), .@"detect-libc" => return jsSyntheticModule(if (Environment.isLinux) .@"detect-libc/linux" else .@"detect-libc", specifier), .@"node:assert" => return jsSyntheticModule(.@"node:assert", specifier), @@ -2583,7 +2546,7 @@ pub const ModuleLoader = struct { } else if (jsc_vm.standalone_module_graph) |graph| { const specifier_utf8 = specifier.toUTF8(bun.default_allocator); defer specifier_utf8.deinit(); - if (graph.files.get(specifier_utf8.slice())) |file| { + if (graph.files.getPtr(specifier_utf8.slice())) |file| { if (file.loader == .sqlite or file.loader == .sqlite_embedded) { const code = \\/* Generated code */ @@ -2596,7 +2559,7 @@ pub const ModuleLoader = struct { ; return ResolvedSource{ .allocator = null, - .source_code = bun.String.init(code), + .source_code = bun.String.static(code), .specifier = specifier, .source_url = specifier.dupeRef(), .hash = 0, @@ -2606,7 +2569,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, - .source_code = bun.String.static(file.contents), + .source_code = file.toWTFString(), .specifier = specifier, .source_url = specifier.dupeRef(), .hash = 0, @@ -2711,6 +2674,7 @@ pub const HardcodedModule = enum { @"bun:jsc", @"bun:main", @"bun:test", // usually replaced by the transpiler but `await import("bun:" + "test")` has to work + @"bun:sql", @"bun:sqlite", @"bun:internal-for-testing", @"detect-libc", @@ -2788,6 +2752,7 @@ pub const HardcodedModule = enum { .{ "bun:test", HardcodedModule.@"bun:test" }, .{ "bun:sqlite", HardcodedModule.@"bun:sqlite" }, .{ "bun:internal-for-testing", HardcodedModule.@"bun:internal-for-testing" }, + .{ "bun:sql", HardcodedModule.@"bun:sql" }, .{ "detect-libc", HardcodedModule.@"detect-libc" }, .{ "node-fetch", HardcodedModule.@"node-fetch" }, .{ "isomorphic-fetch", HardcodedModule.@"isomorphic-fetch" }, @@ -3000,6 +2965,7 @@ pub const HardcodedModule = enum { .{ "bun:ffi", .{ .path = "bun:ffi" } }, .{ "bun:jsc", .{ .path = "bun:jsc" } }, .{ "bun:sqlite", .{ .path = "bun:sqlite" } }, + .{ "bun:sql", .{ .path = "bun:sql" } }, .{ "bun:wrap", .{ .path = "bun:wrap" } }, .{ "bun:internal-for-testing", .{ .path = "bun:internal-for-testing" } }, .{ "ffi", .{ .path = "bun:ffi" } }, diff --git a/src/bun.js/modules/BunJSCModule.h b/src/bun.js/modules/BunJSCModule.h index 994ccac390e28..56a04cf0bc474 100644 --- a/src/bun.js/modules/BunJSCModule.h +++ b/src/bun.js/modules/BunJSCModule.h @@ -1,6 +1,11 @@ #include "_NativeModule.h" #include "ExceptionOr.h" +#include "JavaScriptCore/ArgList.h" +#include "JavaScriptCore/ExceptionScope.h" +#include "JavaScriptCore/JSCJSValue.h" +#include "JavaScriptCore/JSGlobalObject.h" +#include "JavaScriptCore/JSNativeStdFunction.h" #include "MessagePort.h" #include "SerializedScriptValue.h" #include @@ -18,11 +23,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -39,6 +47,13 @@ #include +#if OS(DARWIN) +#if BUN_DEBUG +#include +#define IS_MALLOC_DEBUGGING_ENABLED 1 +#endif +#endif + using namespace JSC; using namespace WTF; using namespace WebCore; @@ -91,8 +106,10 @@ JSC_DEFINE_HOST_FUNCTION(functionStartRemoteDebugger, if (!server.start(reinterpret_cast(host), port)) { throwVMError( globalObject, scope, - createError(globalObject, "Failed to start server \""_s + host + ":"_s + - port + "\". Is port already in use?"_s)); + createError(globalObject, + makeString("Failed to start server \""_s, + reinterpret_cast(host), + ":"_s, port, "\". Is port already in use?"_s))); return JSC::JSValue::encode(JSC::jsUndefined()); } @@ -195,30 +212,142 @@ JSC_DEFINE_HOST_FUNCTION(functionMemoryUsageStatistics, auto &vm = globalObject->vm(); - // this is a C API function - auto *stats = toJS(JSGetMemoryUsageStatistics(toRef(globalObject))); + if (vm.heap.size() == 0) { + vm.heap.collectNow(Sync, CollectionScope::Full); + JSC::DisallowGC disallowGC; + } - if (JSValue heapSizeValue = - stats->getDirect(vm, Identifier::fromString(vm, "heapSize"_s))) { - ASSERT(heapSizeValue.isNumber()); - if (heapSizeValue.toInt32(globalObject) == 0) { - vm.heap.collectNow(Sync, CollectionScope::Full); - JSC::DisallowGC disallowGC; - stats = toJS(JSGetMemoryUsageStatistics(toRef(globalObject))); + const auto createdSortedTypeCounts = + [&](JSC::TypeCountSet *typeCounts) -> JSC::JSValue { + WTF::Vector> counts; + counts.reserveInitialCapacity(typeCounts->size()); + for (auto &it : *typeCounts) { + if (it.value > 0) + counts.append( + std::make_pair(Identifier::fromLatin1(vm, it.key), it.value)); + } + + // Sort by count first, then by name. + std::sort(counts.begin(), counts.end(), + [](const std::pair &a, + const std::pair &b) { + if (a.second == b.second) { + WTF::StringView left = a.first.string(); + WTF::StringView right = b.first.string(); + unsigned originalLeftLength = left.length(); + unsigned originalRightLength = right.length(); + unsigned size = std::min(left.length(), right.length()); + left = left.substring(0, size); + right = right.substring(0, size); + int result = WTF::codePointCompare(right, left); + if (result == 0) { + return originalLeftLength > originalRightLength; + } + + return result > 0; + } + + return a.second > b.second; + }); + + auto *objectTypeCounts = constructEmptyObject(globalObject); + for (auto &it : counts) { + objectTypeCounts->putDirect(vm, it.first, jsNumber(it.second)); + } + return objectTypeCounts; + }; + + JSValue objectTypeCounts = + createdSortedTypeCounts(vm.heap.objectTypeCounts().get()); + JSValue protectedCounts = + createdSortedTypeCounts(vm.heap.protectedObjectTypeCounts().get()); + + JSObject *object = constructEmptyObject(globalObject); + object->putDirect(vm, Identifier::fromString(vm, "objectTypeCounts"_s), + objectTypeCounts); + + object->putDirect(vm, + Identifier::fromLatin1(vm, "protectedObjectTypeCounts"_s), + protectedCounts); + object->putDirect(vm, Identifier::fromString(vm, "heapSize"_s), + jsNumber(vm.heap.size())); + object->putDirect(vm, Identifier::fromString(vm, "heapCapacity"_s), + jsNumber(vm.heap.capacity())); + object->putDirect(vm, Identifier::fromString(vm, "extraMemorySize"_s), + jsNumber(vm.heap.extraMemorySize())); + object->putDirect(vm, Identifier::fromString(vm, "objectCount"_s), + jsNumber(vm.heap.objectCount())); + object->putDirect(vm, Identifier::fromString(vm, "protectedObjectCount"_s), + jsNumber(vm.heap.protectedObjectCount())); + object->putDirect(vm, Identifier::fromString(vm, "globalObjectCount"_s), + jsNumber(vm.heap.globalObjectCount())); + object->putDirect(vm, + Identifier::fromString(vm, "protectedGlobalObjectCount"_s), + jsNumber(vm.heap.protectedGlobalObjectCount())); + +#if IS_MALLOC_DEBUGGING_ENABLED +#if OS(DARWIN) + { + vm_address_t *zones; + unsigned count; + + // Zero out the structures in case a zone is missing + malloc_statistics_t zone_stats; + zone_stats.blocks_in_use = 0; + zone_stats.size_in_use = 0; + zone_stats.max_size_in_use = 0; + zone_stats.size_allocated = 0; + + malloc_zone_pressure_relief(nullptr, 0); + malloc_get_all_zones(mach_task_self(), 0, &zones, &count); + Vector> zoneSizes; + zoneSizes.reserveInitialCapacity(count); + for (unsigned i = 0; i < count; i++) { + auto zone = reinterpret_cast(zones[i]); + if (const char *name = malloc_get_zone_name(zone)) { + malloc_zone_statistics(reinterpret_cast(zones[i]), + &zone_stats); + zoneSizes.append( + std::make_pair(Identifier::fromString(vm, String::fromUTF8(name)), + zone_stats.size_in_use)); + } } - } - // This is missing from the C API - JSC::JSObject *protectedCounts = constructEmptyObject(globalObject); - auto typeCounts = *vm.heap.protectedObjectTypeCounts(); - for (auto &it : typeCounts) - protectedCounts->putDirect(vm, Identifier::fromLatin1(vm, it.key), - jsNumber(it.value)); + std::sort(zoneSizes.begin(), zoneSizes.end(), + [](const std::pair &a, + const std::pair &b) { + // Sort by name if the sizes are the same. + if (a.second == b.second) { + WTF::StringView left = a.first.string(); + WTF::StringView right = b.first.string(); + unsigned originalLeftLength = left.length(); + unsigned originalRightLength = right.length(); + unsigned size = std::min(left.length(), right.length()); + left = left.substring(0, size); + right = right.substring(0, size); + int result = WTF::codePointCompare(right, left); + if (result == 0) { + return originalLeftLength > originalRightLength; + } + + return result > 0; + } + + return a.second > b.second; + }); + + auto *zoneSizesObject = constructEmptyObject(globalObject); + for (auto &it : zoneSizes) { + zoneSizesObject->putDirect(vm, it.first, jsDoubleNumber(it.second)); + } + + object->putDirect(vm, Identifier::fromString(vm, "zones"_s), + zoneSizesObject); + } +#endif +#endif - stats->putDirect(vm, - Identifier::fromLatin1(vm, "protectedObjectTypeCounts"_s), - protectedCounts); - return JSValue::encode(stats); + return JSValue::encode(object); } JSC_DECLARE_HOST_FUNCTION(functionCreateMemoryFootprint); @@ -484,6 +613,18 @@ JSC_DEFINE_HOST_FUNCTION(functionRunProfiler, (JSGlobalObject * globalObject, vm.ensureSamplingProfiler(WTF::Stopwatch::create()); JSC::JSValue callbackValue = callFrame->argument(0); + JSC::JSValue sampleValue = callFrame->argument(1); + + MarkedArgumentBuffer args; + + if (callFrame->argumentCount() > 2) { + size_t count = callFrame->argumentCount(); + args.ensureCapacity(count - 2); + for (size_t i = 2; i < count; i++) { + args.append(callFrame->argument(i)); + } + } + auto throwScope = DECLARE_THROW_SCOPE(vm); if (callbackValue.isUndefinedOrNull() || !callbackValue.isCallable()) { throwException( @@ -494,48 +635,87 @@ JSC_DEFINE_HOST_FUNCTION(functionRunProfiler, (JSGlobalObject * globalObject, JSC::JSFunction *function = jsCast(callbackValue); - JSC::JSValue sampleValue = callFrame->argument(1); if (sampleValue.isNumber()) { unsigned sampleInterval = sampleValue.toUInt32(globalObject); samplingProfiler.setTimingInterval( Seconds::fromMicroseconds(sampleInterval)); } - JSC::CallData callData = JSC::getCallData(function); - MarkedArgumentBuffer args; + const auto report = [](JSC::VM &vm, + JSC::JSGlobalObject *globalObject) -> JSC::JSValue { + auto throwScope = DECLARE_THROW_SCOPE(vm); + + auto &samplingProfiler = *vm.samplingProfiler(); + StringPrintStream topFunctions; + samplingProfiler.reportTopFunctions(topFunctions); + + StringPrintStream byteCodes; + samplingProfiler.reportTopBytecodes(byteCodes); + + JSValue stackTraces = JSONParse( + globalObject, samplingProfiler.stackTracesAsJSON()->toJSONString()); - samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(); - samplingProfiler.start(); - JSC::call(globalObject, function, callData, JSC::jsUndefined(), args); - samplingProfiler.pause(); - if (throwScope.exception()) { samplingProfiler.shutdown(); - samplingProfiler.clearData(); - return JSValue::encode(JSValue{}); - } + RETURN_IF_EXCEPTION(throwScope, {}); + + JSObject *result = + constructEmptyObject(globalObject, globalObject->objectPrototype(), 3); + result->putDirect(vm, Identifier::fromString(vm, "functions"_s), + jsString(vm, topFunctions.toString())); + result->putDirect(vm, Identifier::fromString(vm, "bytecodes"_s), + jsString(vm, byteCodes.toString())); + result->putDirect(vm, Identifier::fromString(vm, "stackTraces"_s), + stackTraces); + + return result; + }; + const auto reportFailure = [](JSC::VM &vm) -> JSC::JSValue { + if (auto *samplingProfiler = vm.samplingProfiler()) { + samplingProfiler->pause(); + samplingProfiler->shutdown(); + samplingProfiler->clearData(); + } - StringPrintStream topFunctions; - samplingProfiler.reportTopFunctions(topFunctions); + return {}; + }; - StringPrintStream byteCodes; - samplingProfiler.reportTopBytecodes(byteCodes); + JSC::CallData callData = JSC::getCallData(function); - JSValue stackTraces = JSONParse( - globalObject, samplingProfiler.stackTracesAsJSON()->toJSONString()); + samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(); + samplingProfiler.start(); + JSValue returnValue = + JSC::call(globalObject, function, callData, JSC::jsUndefined(), args); - samplingProfiler.shutdown(); - samplingProfiler.clearData(); + if (returnValue.isEmpty() || throwScope.exception()) { + return JSValue::encode(reportFailure(vm)); + } - JSObject *result = - constructEmptyObject(globalObject, globalObject->objectPrototype(), 3); - result->putDirect(vm, Identifier::fromString(vm, "functions"_s), - jsString(vm, topFunctions.toString())); - result->putDirect(vm, Identifier::fromString(vm, "bytecodes"_s), - jsString(vm, byteCodes.toString())); - result->putDirect(vm, Identifier::fromString(vm, "stackTraces"_s), - stackTraces); + if (auto *promise = jsDynamicCast(returnValue)) { + auto afterOngoingPromiseCapability = + JSC::JSPromise::create(vm, globalObject->promiseStructure()); + RETURN_IF_EXCEPTION(throwScope, {}); + + JSNativeStdFunction *resolve = JSNativeStdFunction::create( + vm, globalObject, 0, "resolve"_s, + [report](JSGlobalObject *globalObject, CallFrame *callFrame) { + return JSValue::encode(JSPromise::resolvedPromise( + globalObject, report(globalObject->vm(), globalObject))); + }); + JSNativeStdFunction *reject = JSNativeStdFunction::create( + vm, globalObject, 0, "reject"_s, + [reportFailure](JSGlobalObject *globalObject, CallFrame *callFrame) { + EnsureStillAliveScope error = callFrame->argument(0); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + reportFailure(globalObject->vm()); + throwException(globalObject, scope, error.value()); + return JSValue::encode(jsUndefined()); + }); + promise->performPromiseThen(globalObject, resolve, reject, + afterOngoingPromiseCapability); + return JSValue::encode(afterOngoingPromiseCapability); + } - return JSValue::encode(result); + return JSValue::encode(report(vm, globalObject)); } JSC_DECLARE_HOST_FUNCTION(functionGenerateHeapSnapshotForDebugging); diff --git a/src/bun.js/modules/NodeBufferModule.h b/src/bun.js/modules/NodeBufferModule.h index 5eea9c099c747..b35dec34708b9 100644 --- a/src/bun.js/modules/NodeBufferModule.h +++ b/src/bun.js/modules/NodeBufferModule.h @@ -1,6 +1,13 @@ +#pragma once + +#include "root.h" + #include "../bindings/JSBuffer.h" +#include "ErrorCode.h" +#include "JavaScriptCore/PageCount.h" #include "_NativeModule.h" -#include "simdutf.h" +#include "wtf/SIMDUTF.h" +#include namespace Zig { using namespace WebCore; @@ -52,10 +59,9 @@ JSC_DEFINE_HOST_FUNCTION(jsBufferConstructorFunction_isUtf8, ptr = reinterpret_cast(impl->data()); } else { - throwVMError( - lexicalGlobalObject, throwScope, - createTypeError(lexicalGlobalObject, - "First argument must be an ArrayBufferView"_s)); + Bun::throwError(lexicalGlobalObject, throwScope, + Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "First argument must be an ArrayBufferView"_s); return JSValue::encode({}); } @@ -109,10 +115,9 @@ JSC_DEFINE_HOST_FUNCTION(jsBufferConstructorFunction_isAscii, ptr = reinterpret_cast(impl->data()); } else { - throwVMError( - lexicalGlobalObject, throwScope, - createTypeError(lexicalGlobalObject, - "First argument must be an ArrayBufferView"_s)); + Bun::throwError(lexicalGlobalObject, throwScope, + Bun::ErrorCode::ERR_INVALID_ARG_TYPE, + "First argument must be an ArrayBufferView"_s); return JSValue::encode({}); } @@ -121,6 +126,8 @@ JSC_DEFINE_HOST_FUNCTION(jsBufferConstructorFunction_isAscii, JSValue::encode(jsBoolean(simdutf::validate_ascii(ptr, byteLength)))); } +BUN_DECLARE_HOST_FUNCTION(jsFunctionResolveObjectURL); + JSC_DEFINE_HOST_FUNCTION(jsFunctionNotImplemented, (JSGlobalObject * globalObject, CallFrame *callFrame)) { @@ -159,18 +166,18 @@ DEFINE_NATIVE_MODULE(NodeBuffer) { JSC::jsNumber(50)); put(JSC::Identifier::fromString(vm, "kMaxLength"_s), - JSC::jsNumber(4294967296LL)); + JSC::jsNumber(MAX_ARRAY_BUFFER_SIZE)); put(JSC::Identifier::fromString(vm, "kStringMaxLength"_s), - JSC::jsNumber(536870888)); + JSC::jsNumber(std::numeric_limits().max())); JSC::JSObject *constants = JSC::constructEmptyObject( lexicalGlobalObject, globalObject->objectPrototype(), 2); constants->putDirect(vm, JSC::Identifier::fromString(vm, "MAX_LENGTH"_s), - JSC::jsNumber(4294967296LL)); + JSC::jsNumber(MAX_ARRAY_BUFFER_SIZE)); constants->putDirect(vm, JSC::Identifier::fromString(vm, "MAX_STRING_LENGTH"_s), - JSC::jsNumber(536870888)); + JSC::jsNumber(std::numeric_limits().max())); put(JSC::Identifier::fromString(vm, "constants"_s), constants); @@ -192,7 +199,8 @@ DEFINE_NATIVE_MODULE(NodeBuffer) { auto *resolveObjectURL = InternalFunction::createFunctionThatMasqueradesAsUndefined( - vm, globalObject, 1, "resolveObjectURL"_s, jsFunctionNotImplemented); + vm, globalObject, 1, "resolveObjectURL"_s, + jsFunctionResolveObjectURL); put(JSC::Identifier::fromString(vm, "resolveObjectURL"_s), resolveObjectURL); diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index d8fecc60df412..d1994e4d0eb5e 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -3,6 +3,8 @@ #include "CommonJSModuleRecord.h" #include "ImportMetaObject.h" +#include "JavaScriptCore/ArgList.h" +#include "JavaScriptCore/JSGlobalObjectInlines.h" #include "_NativeModule.h" #include "isBuiltinModule.h" #include @@ -120,7 +122,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleModuleConstructor, (JSC::JSGlobalOb idString = idValue.toString(globalObject); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined())); - auto index = idString->tryGetValue().reverseFind('/', idString->length()); + auto index = idString->tryGetValue()->reverseFind('/', idString->length()); if (index != WTF::notFound) { dirname = JSC::jsSubstring(globalObject, idString, 0, index); @@ -208,7 +210,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, scope, JSValue::encode(Bun::JSCommonJSModule::createBoundRequireFunction( vm, globalObject, val))); } -extern "C" JSC::EncodedJSValue Resolver__nodeModulePathsForJS(JSGlobalObject *, CallFrame *); +BUN_DECLARE_HOST_FUNCTION(Resolver__nodeModulePathsForJS); JSC_DEFINE_HOST_FUNCTION(jsFunctionFindSourceMap, (JSGlobalObject * globalObject, CallFrame *callFrame)) { auto &vm = globalObject->vm(); @@ -265,7 +267,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, (JSC::JSGlobalObject * globa // weird thing. (fromValue.isObject()) { - if (auto idValue = fromValue.getObject()->getIfPropertyExists(globalObject, Identifier::fromString(vm, "filename"_s))) { + if (auto idValue = fromValue.getObject()->getIfPropertyExists(globalObject, builtinNames(vm).filenamePublicName())) { if (idValue.isString()) { fromValue = idValue; } @@ -332,21 +334,25 @@ struct Parent { Parent getParent(VM&vm, JSGlobalObject* global, JSValue maybe_parent) { Parent value { nullptr, nullptr }; - if (!maybe_parent.isCell()) { + + if (!maybe_parent) { return value; } - if (!maybe_parent.isObject()) { + + auto parent = maybe_parent.getObject(); + if (!parent) { return value; } - auto parent = maybe_parent.getObject(); + auto scope = DECLARE_THROW_SCOPE(vm); - JSValue paths = parent->get(global, Identifier::fromString(vm, "paths"_s)); + const auto& builtinNames = Bun::builtinNames(vm); + JSValue paths = parent->get(global, builtinNames.pathsPublicName()); RETURN_IF_EXCEPTION(scope, value); if (paths.isCell()) { value.paths = jsDynamicCast(paths); } - JSValue filename = parent->get(global, Identifier::fromString(vm, "filename"_s)); + JSValue filename = parent->get(global, builtinNames.filenamePublicName()); RETURN_IF_EXCEPTION(scope, value); if (filename.isString()) { value.filename = filename.toString(global); @@ -515,7 +521,7 @@ DEFINE_NATIVE_MODULE(NodeModule) { putNativeFn(Identifier::fromString(vm, "_resolveLookupPaths"_s), jsFunctionResolveLookupPaths); putNativeFn(Identifier::fromString(vm, "createRequire"_s), jsFunctionNodeModuleCreateRequire); - putNativeFn(Identifier::fromString(vm, "paths"_s), Resolver__nodeModulePathsForJS); + putNativeFn(builtinNames(vm).pathsPublicName(), Resolver__nodeModulePathsForJS); putNativeFn(Identifier::fromString(vm, "findSourceMap"_s), jsFunctionFindSourceMap); putNativeFn(Identifier::fromString(vm, "syncBuiltinExports"_s), jsFunctionSyncBuiltinExports); putNativeFn(Identifier::fromString(vm, "SourceMap"_s), jsFunctionSourceMap); @@ -539,17 +545,15 @@ DEFINE_NATIVE_MODULE(NodeModule) { defaultObject->putDirect(vm, vm.propertyNames->prototype, prototype); - JSC::JSArray *builtinModules = JSC::JSArray::create( - vm, - globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), - countof(builtinModuleNames) - ); + MarkedArgumentBuffer args; + args.ensureCapacity(countof(builtinModuleNames)); for (unsigned i = 0; i < countof(builtinModuleNames); ++i) { - builtinModules->putDirectIndex(globalObject, i, JSC::jsString(vm, String(builtinModuleNames[i]))); + args.append(JSC::jsOwnedString(vm, String(builtinModuleNames[i]))); } - put(JSC::Identifier::fromString(vm, "builtinModules"_s), builtinModules); + + put(JSC::Identifier::fromString(vm, "builtinModules"_s), JSC::constructArray(globalObject, static_cast(nullptr), JSC::ArgList(args))); } } // namespace Zig diff --git a/src/bun.js/modules/NodeUtilTypesModule.h b/src/bun.js/modules/NodeUtilTypesModule.h index 854ee30e0c0ce..a90fd7e6815f7 100644 --- a/src/bun.js/modules/NodeUtilTypesModule.h +++ b/src/bun.js/modules/NodeUtilTypesModule.h @@ -357,6 +357,12 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsInt32Array, GET_FIRST_CELL return JSValue::encode(jsBoolean(cell->type() == Int32ArrayType)); } +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsFloat16Array, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callframe)) { + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Float16ArrayType)); +} JSC_DEFINE_HOST_FUNCTION(jsFunctionIsFloat32Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callframe)) { @@ -418,7 +424,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsCryptoKey, namespace Zig { DEFINE_NATIVE_MODULE(NodeUtilTypes) { - INIT_NATIVE_MODULE(42); + INIT_NATIVE_MODULE(43); putNativeFn(Identifier::fromString(vm, "isExternal"_s), jsFunctionIsExternal); putNativeFn(Identifier::fromString(vm, "isDate"_s), jsFunctionIsDate); @@ -482,6 +488,8 @@ DEFINE_NATIVE_MODULE(NodeUtilTypes) { jsFunctionIsInt16Array); putNativeFn(Identifier::fromString(vm, "isInt32Array"_s), jsFunctionIsInt32Array); + putNativeFn(Identifier::fromString(vm, "isFloat16Array"_s), + jsFunctionIsFloat16Array); putNativeFn(Identifier::fromString(vm, "isFloat32Array"_s), jsFunctionIsFloat32Array); putNativeFn(Identifier::fromString(vm, "isFloat64Array"_s), diff --git a/src/bun.js/node/dir_iterator.zig b/src/bun.js/node/dir_iterator.zig index 381e7fe9525f7..7b6b89286ed16 100644 --- a/src/bun.js/node/dir_iterator.zig +++ b/src/bun.js/node/dir_iterator.zig @@ -56,6 +56,7 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)), index: usize, end_index: usize, + received_eof: bool = false, const Self = @This(); @@ -77,6 +78,22 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { fn nextDarwin(self: *Self) Result { start_over: while (true) { if (self.index >= self.end_index) { + if (self.received_eof) { + return .{ .result = null }; + } + + // getdirentries64() writes to the last 4 bytes of the + // buffer to indicate EOF. If that value is not zero, we + // have reached the end of the directory and we can skip + // the extra syscall. + // https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/vfs/vfs_syscalls.c#L10444-L10470 + const GETDIRENTRIES64_EXTENDED_BUFSIZE = 1024; + comptime bun.assert(@sizeOf(@TypeOf(self.buf)) >= GETDIRENTRIES64_EXTENDED_BUFSIZE); + self.received_eof = false; + // Always zero the bytes where the flag will be written + // so we don't confuse garbage with EOF. + self.buf[self.buf.len - 4 ..][0..4].* = .{ 0, 0, 0, 0 }; + const rc = posix.system.__getdirentries64( self.dir.fd, &self.buf, @@ -85,7 +102,11 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { ); if (rc < 1) { - if (rc == 0) return Result{ .result = null }; + if (rc == 0) { + self.received_eof = true; + return Result{ .result = null }; + } + if (Result.errnoSys(rc, .getdirentries64)) |err| { return err; } @@ -93,6 +114,7 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { self.index = 0; self.end_index = @as(usize, @intCast(rc)); + self.received_eof = self.end_index <= (self.buf.len - 4) and @as(u32, @bitCast(self.buf[self.buf.len - 4 ..][0..4].*)) == 1; } const darwin_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); const next_index = self.index + darwin_entry.reclen; diff --git a/src/bun.js/node/fs_events.zig b/src/bun.js/node/fs_events.zig index ed1e6100a0324..fd019882fbdfe 100644 --- a/src/bun.js/node/fs_events.zig +++ b/src/bun.js/node/fs_events.zig @@ -107,8 +107,8 @@ pub const kFSEventsSystem: c_int = kFSEventStreamEventFlagUnmount | kFSEventStreamEventFlagRootChanged; -var fsevents_mutex: Mutex = Mutex.init(); -var fsevents_default_loop_mutex: Mutex = Mutex.init(); +var fsevents_mutex: Mutex = .{}; +var fsevents_default_loop_mutex: Mutex = .{}; var fsevents_default_loop: ?*FSEventsLoop = null; fn dlsym(handle: ?*anyopaque, comptime Type: type, comptime symbol: [:0]const u8) ?Type { @@ -331,7 +331,7 @@ pub const FSEventsLoop = struct { return error.FailedToCreateCoreFoudationSourceLoop; } - const fs_loop = FSEventsLoop{ .sem = Semaphore.init(0), .mutex = Mutex.init(), .signal_source = signal_source }; + const fs_loop = FSEventsLoop{ .sem = Semaphore.init(0), .mutex = .{}, .signal_source = signal_source }; this.* = fs_loop; this.thread = try std.Thread.spawn(.{}, FSEventsLoop.CFThreadLoop, .{this}); diff --git a/src/bun.js/node/node_cluster_binding.zig b/src/bun.js/node/node_cluster_binding.zig new file mode 100644 index 0000000000000..61644da41de26 --- /dev/null +++ b/src/bun.js/node/node_cluster_binding.zig @@ -0,0 +1,280 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Environment = bun.Environment; +const JSC = bun.JSC; +const string = bun.string; +const Output = bun.Output; +const ZigString = JSC.ZigString; +const log = Output.scoped(.IPC, false); + +extern fn Bun__Process__queueNextTick1(*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue) void; +extern fn Process__emitErrorEvent(global: *JSC.JSGlobalObject, value: JSC.JSValue) void; + +pub var child_singleton: InternalMsgHolder = .{}; + +pub fn sendHelperChild(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + log("sendHelperChild", .{}); + + const arguments = callframe.arguments(3).ptr; + const message = arguments[0]; + const handle = arguments[1]; + const callback = arguments[2]; + + const vm = globalThis.bunVM(); + + if (vm.ipc == null) { + return .false; + } + if (message.isUndefined()) { + return globalThis.throwValueRet(globalThis.ERR_MISSING_ARGS_static(ZigString.static("message"), null, null)); + } + if (!handle.isNull()) { + globalThis.throw("passing 'handle' not implemented yet", .{}); + return .zero; + } + if (!message.isObject()) { + return globalThis.throwValueRet(globalThis.ERR_INVALID_ARG_TYPE_static( + ZigString.static("message"), + ZigString.static("object"), + message, + )); + } + if (callback.isFunction()) { + child_singleton.callbacks.put(bun.default_allocator, child_singleton.seq, JSC.Strong.create(callback, globalThis)) catch bun.outOfMemory(); + } + + // sequence number for InternalMsgHolder + message.put(globalThis, ZigString.static("seq"), JSC.JSValue.jsNumber(child_singleton.seq)); + child_singleton.seq +%= 1; + + // similar code as Bun__Process__send + var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis }; + if (Environment.isDebug) log("child: {}", .{message.toFmt(&formatter)}); + + const ipc_instance = vm.getIPCInstance().?; + + const S = struct { + fn impl(globalThis_: *JSC.JSGlobalObject, callframe_: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments_ = callframe_.arguments(1).slice(); + const ex = arguments_[0]; + Process__emitErrorEvent(globalThis_, ex); + return .undefined; + } + }; + + const good = ipc_instance.data.serializeAndSendInternal(globalThis, message); + + if (!good) { + const ex = globalThis.createTypeErrorInstance("sendInternal() failed", .{}); + ex.put(globalThis, ZigString.static("syscall"), ZigString.static("write").toJS(globalThis)); + const fnvalue = JSC.JSFunction.create(globalThis, "", S.impl, 1, .{}); + Bun__Process__queueNextTick1(globalThis, fnvalue, ex); + return .false; + } + + return .true; +} + +pub fn onInternalMessageChild(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + log("onInternalMessageChild", .{}); + const arguments = callframe.arguments(2).ptr; + child_singleton.worker = JSC.Strong.create(arguments[0], globalThis); + child_singleton.cb = JSC.Strong.create(arguments[1], globalThis); + child_singleton.flush(globalThis); + return .undefined; +} + +pub fn handleInternalMessageChild(globalThis: *JSC.JSGlobalObject, message: JSC.JSValue) void { + log("handleInternalMessageChild", .{}); + + child_singleton.dispatch(message, globalThis); +} + +// +// +// + +/// Queue for messages sent between parent and child processes in an IPC environment. node:cluster sends json serialized messages +/// to describe different events it performs. It will send a message with an incrementing sequence number and then call a callback +/// when a message is recieved with an 'ack' property of the same sequence number. +pub const InternalMsgHolder = struct { + seq: i32 = 0, + callbacks: std.AutoArrayHashMapUnmanaged(i32, JSC.Strong) = .{}, + + worker: JSC.Strong = .{}, + cb: JSC.Strong = .{}, + messages: std.ArrayListUnmanaged(JSC.Strong) = .{}, + + pub fn isReady(this: *InternalMsgHolder) bool { + return this.worker.has() and this.cb.has(); + } + + pub fn enqueue(this: *InternalMsgHolder, message: JSC.JSValue, globalThis: *JSC.JSGlobalObject) void { + //TODO: .addOne is workaround for .append causing crash/ dependency loop in zig compiler + const new_item_ptr = this.messages.addOne(bun.default_allocator) catch bun.outOfMemory(); + new_item_ptr.* = JSC.Strong.create(message, globalThis); + } + + pub fn dispatch(this: *InternalMsgHolder, message: JSC.JSValue, globalThis: *JSC.JSGlobalObject) void { + if (!this.isReady()) { + this.enqueue(message, globalThis); + return; + } + this.dispatchUnsafe(message, globalThis); + } + + fn dispatchUnsafe(this: *InternalMsgHolder, message: JSC.JSValue, globalThis: *JSC.JSGlobalObject) void { + const cb = this.cb.get().?; + const worker = this.worker.get().?; + + const event_loop = globalThis.bunVM().eventLoop(); + + if (message.get(globalThis, "ack")) |p| { + if (!p.isUndefined()) { + const ack = p.toInt32(); + if (this.callbacks.getEntry(ack)) |entry| { + var cbstrong = entry.value_ptr.*; + if (cbstrong.get()) |callback| { + defer cbstrong.deinit(); + _ = this.callbacks.swapRemove(ack); + event_loop.runCallback(callback, globalThis, this.worker.get().?, &.{ + message, + .null, // handle + }); + return; + } + return; + } + } + } + event_loop.runCallback(cb, globalThis, worker, &.{ + message, + .null, // handle + }); + } + + pub fn flush(this: *InternalMsgHolder, globalThis: *JSC.JSGlobalObject) void { + bun.assert(this.isReady()); + var messages = this.messages; + this.messages = .{}; + for (messages.items) |*strong| { + if (strong.get()) |message| { + this.dispatchUnsafe(message, globalThis); + } + strong.deinit(); + } + messages.deinit(bun.default_allocator); + } + + pub fn deinit(this: *InternalMsgHolder) void { + for (this.callbacks.values()) |*strong| strong.deinit(); + this.callbacks.deinit(bun.default_allocator); + this.worker.deinit(); + this.cb.deinit(); + for (this.messages.items) |*strong| strong.deinit(); + this.messages.deinit(bun.default_allocator); + } +}; + +pub fn sendHelperPrimary(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + log("sendHelperPrimary", .{}); + + const arguments = callframe.arguments(4).ptr; + const subprocess = arguments[0].as(bun.JSC.Subprocess).?; + const message = arguments[1]; + const handle = arguments[2]; + const callback = arguments[3]; + + const ipc_data = subprocess.ipc() orelse return .false; + + if (message.isUndefined()) { + return globalThis.throwValueRet(globalThis.ERR_MISSING_ARGS_static(ZigString.static("message"), null, null)); + } + if (!message.isObject()) { + return globalThis.throwValueRet(globalThis.ERR_INVALID_ARG_TYPE_static( + ZigString.static("message"), + ZigString.static("object"), + message, + )); + } + if (callback.isFunction()) { + ipc_data.internal_msg_queue.callbacks.put(bun.default_allocator, ipc_data.internal_msg_queue.seq, JSC.Strong.create(callback, globalThis)) catch bun.outOfMemory(); + } + + // sequence number for InternalMsgHolder + message.put(globalThis, ZigString.static("seq"), JSC.JSValue.jsNumber(ipc_data.internal_msg_queue.seq)); + ipc_data.internal_msg_queue.seq +%= 1; + + // similar code as bun.JSC.Subprocess.doSend + var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis }; + if (Environment.isDebug) log("primary: {}", .{message.toFmt(&formatter)}); + + _ = handle; + const success = ipc_data.serializeAndSendInternal(globalThis, message); + if (!success) return .false; + + return .true; +} + +pub fn onInternalMessagePrimary(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(3).ptr; + const subprocess = arguments[0].as(bun.JSC.Subprocess).?; + const ipc_data = subprocess.ipc() orelse return .undefined; + ipc_data.internal_msg_queue.worker = JSC.Strong.create(arguments[1], globalThis); + ipc_data.internal_msg_queue.cb = JSC.Strong.create(arguments[2], globalThis); + return .undefined; +} + +pub fn handleInternalMessagePrimary(globalThis: *JSC.JSGlobalObject, subprocess: *JSC.Subprocess, message: JSC.JSValue) void { + const ipc_data = subprocess.ipc() orelse return; + + const event_loop = globalThis.bunVM().eventLoop(); + + if (message.get(globalThis, "ack")) |p| { + if (!p.isUndefined()) { + const ack = p.toInt32(); + if (ipc_data.internal_msg_queue.callbacks.getEntry(ack)) |entry| { + var cbstrong = entry.value_ptr.*; + defer cbstrong.clear(); + _ = ipc_data.internal_msg_queue.callbacks.swapRemove(ack); + const cb = cbstrong.get().?; + event_loop.runCallback(cb, globalThis, ipc_data.internal_msg_queue.worker.get().?, &.{ + message, + .null, // handle + }); + return; + } + } + } + const cb = ipc_data.internal_msg_queue.cb.get().?; + event_loop.runCallback(cb, globalThis, ipc_data.internal_msg_queue.worker.get().?, &.{ + message, + .null, // handle + }); + return; +} + +// +// +// + +extern fn Bun__setChannelRef(*JSC.JSGlobalObject, bool) void; + +pub fn setRef(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(1).ptr; + + if (arguments.len == 0) { + return globalObject.throwValueRet(globalObject.ERR_MISSING_ARGS_1(ZigString.static("enabled").toJS(globalObject))); + } + if (!arguments[0].isBoolean()) { + return globalObject.throwValueRet(globalObject.ERR_INVALID_ARG_TYPE_static( + ZigString.static("enabled"), + ZigString.static("boolean"), + arguments[0], + )); + } + + const enabled = arguments[0].toBoolean(); + Bun__setChannelRef(globalObject, enabled); + return .undefined; +} diff --git a/src/bun.js/node/node_crypto_binding.zig b/src/bun.js/node/node_crypto_binding.zig index aad68932c16ca..026450dc45c47 100644 --- a/src/bun.js/node/node_crypto_binding.zig +++ b/src/bun.js/node/node_crypto_binding.zig @@ -11,31 +11,37 @@ const assert = bun.assert; const EVP = Crypto.EVP; const PBKDF2 = EVP.PBKDF2; const JSValue = JSC.JSValue; +const validators = @import("./util/validators.zig"); +fn randomInt(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(2).slice(); -pub fn randomInt(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - const S = struct { - fn cb(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(2).slice(); + //min, max + if (!arguments[0].isNumber()) return globalThis.throwInvalidArgumentTypeValue("min", "safe integer", arguments[0]); + if (!arguments[1].isNumber()) return globalThis.throwInvalidArgumentTypeValue("max", "safe integer", arguments[1]); + const min = arguments[0].to(i64); + const max = arguments[1].to(i64); - var at_least: u52 = 0; - var at_most: u52 = std.math.maxInt(u52); - - //min, max - if (!arguments[0].isNumber()) return globalThis.throwInvalidArgumentTypeValue("min", "safe integer", arguments[0]); - if (!arguments[1].isNumber()) return globalThis.throwInvalidArgumentTypeValue("max", "safe integer", arguments[1]); - at_least = arguments[0].to(u52); - at_most = arguments[1].to(u52); + if (min > validators.NUMBER__MAX_SAFE_INTEGER or min < validators.NUMBER__MIN_SAFE_INTEGER) { + return globalThis.throwInvalidArgumentRangeValue("min", "It must be a safe integer type number", min); + } + if (max > validators.NUMBER__MAX_SAFE_INTEGER) { + return globalThis.throwInvalidArgumentRangeValue("max", "It must be a safe integer type number", max); + } + if (min >= max) { + return globalThis.throwInvalidArgumentRangeValue("max", "should be greater than min", max); + } + const diff = max - min; + if (diff > 281474976710655) { + return globalThis.throwInvalidArgumentRangeValue("max - min", "It must be <= 281474976710655", diff); + } - return JSC.JSValue.jsNumberFromUint64(std.crypto.random.intRangeLessThan(u52, at_least, at_most)); - } - }; - return JSC.JSFunction.create(global, "randomInt", &S.cb, 2, .{}); + return JSC.JSValue.jsNumberFromInt64(std.crypto.random.intRangeLessThan(i64, min, max)); } -pub fn pbkdf2( +fn pbkdf2( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(5); const data = PBKDF2.fromJS(globalThis, arguments.slice(), true) orelse { @@ -47,10 +53,10 @@ pub fn pbkdf2( return job.promise.value(); } -pub fn pbkdf2Sync( +fn pbkdf2Sync( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) JSC.JSValue { const arguments = callframe.arguments(5); var data = PBKDF2.fromJS(globalThis, arguments.slice(), false) orelse { @@ -80,12 +86,16 @@ pub fn pbkdf2Sync( return out_arraybuffer; } -pub fn createNodeCryptoBindingZig(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { +const jsPbkdf2 = JSC.toJSHostFunction(pbkdf2); +const jsPbkdf2Sync = JSC.toJSHostFunction(pbkdf2Sync); +const jsRandomInt = JSC.toJSHostFunction(randomInt); + +pub fn createNodeCryptoBindingZig(global: *JSC.JSGlobalObject) JSC.JSValue { const crypto = JSC.JSValue.createEmptyObject(global, 3); - crypto.put(global, bun.String.init("pbkdf2"), JSC.JSFunction.create(global, "pbkdf2", &pbkdf2, 5, .{})); - crypto.put(global, bun.String.init("pbkdf2Sync"), JSC.JSFunction.create(global, "pbkdf2Sync", &pbkdf2Sync, 5, .{})); - crypto.put(global, bun.String.init("randomInt"), randomInt(global)); + crypto.put(global, bun.String.init("pbkdf2"), JSC.JSFunction.create(global, "pbkdf2", jsPbkdf2, 5, .{})); + crypto.put(global, bun.String.init("pbkdf2Sync"), JSC.JSFunction.create(global, "pbkdf2Sync", jsPbkdf2Sync, 5, .{})); + crypto.put(global, bun.String.init("randomInt"), JSC.JSFunction.create(global, "randomInt", jsRandomInt, 2, .{})); return crypto; } diff --git a/src/bun.js/node/node_error_binding.zig b/src/bun.js/node/node_error_binding.zig new file mode 100644 index 0000000000000..8c6de402a1471 --- /dev/null +++ b/src/bun.js/node/node_error_binding.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Environment = bun.Environment; +const JSC = bun.JSC; +const string = bun.string; +const Output = bun.Output; +const ZigString = JSC.ZigString; +const createTypeError = JSC.JSGlobalObject.createTypeErrorInstanceWithCode; +const createError = JSC.JSGlobalObject.createErrorInstanceWithCode; +const createRangeError = JSC.JSGlobalObject.createRangeErrorInstanceWithCode; + +pub const ERR_INVALID_HANDLE_TYPE = createSimpleError(createTypeError, .ERR_INVALID_HANDLE_TYPE, "This handle type cannot be sent"); +pub const ERR_CHILD_CLOSED_BEFORE_REPLY = createSimpleError(createError, .ERR_CHILD_CLOSED_BEFORE_REPLY, "Child closed before reply received"); + +fn createSimpleError(comptime createFn: anytype, comptime code: JSC.Node.ErrorCode, comptime message: string) JSC.JS2NativeFunctionType { + const R = struct { + pub fn cbb(global: *JSC.JSGlobalObject) callconv(JSC.conv) JSC.JSValue { + const S = struct { + fn cb(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + _ = callframe; + return createFn(globalThis, code, message, .{}); + } + }; + return JSC.JSFunction.create(global, @tagName(code), S.cb, 0, .{}); + } + }; + return R.cbb; +} diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 5bcd62a5574fe..c8468290fb5d3 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -39,6 +39,9 @@ const E = C.E; const uid_t = if (Environment.isPosix) std.posix.uid_t else bun.windows.libuv.uv_uid_t; const gid_t = if (Environment.isPosix) std.posix.gid_t else bun.windows.libuv.uv_gid_t; const ReadPosition = i64; +const StringOrBuffer = JSC.Node.StringOrBuffer; +const NodeFSFunctionEnum = std.meta.DeclEnum(JSC.Node.NodeFS); +const UvFsCallback = fn (*uv.fs_t) callconv(.C) void; const Stats = JSC.Node.Stats; const Dirent = JSC.Node.Dirent; @@ -54,7 +57,6 @@ else // TODO: 0; -const StringOrBuffer = JSC.Node.StringOrBuffer; const ArrayBuffer = JSC.MarkedArrayBuffer; const Buffer = JSC.Buffer; const FileSystemFlags = JSC.Node.FileSystemFlags; @@ -63,7 +65,7 @@ pub const Async = struct { pub const appendFile = NewAsyncFSTask(Return.AppendFile, Arguments.AppendFile, NodeFS.appendFile); pub const chmod = NewAsyncFSTask(Return.Chmod, Arguments.Chmod, NodeFS.chmod); pub const chown = NewAsyncFSTask(Return.Chown, Arguments.Chown, NodeFS.chown); - pub const close = NewAsyncFSTask(Return.Close, Arguments.Close, NodeFS.close); + pub const close = NewUVFSRequest(Return.Close, Arguments.Close, .close); pub const copyFile = NewAsyncFSTask(Return.CopyFile, Arguments.CopyFile, NodeFS.copyFile); pub const exists = NewAsyncFSTask(Return.Exists, Arguments.Exists, NodeFS.exists); pub const fchmod = NewAsyncFSTask(Return.Fchmod, Arguments.FChmod, NodeFS.fchmod); @@ -80,12 +82,12 @@ pub const Async = struct { pub const lutimes = NewAsyncFSTask(Return.Lutimes, Arguments.Lutimes, NodeFS.lutimes); pub const mkdir = NewAsyncFSTask(Return.Mkdir, Arguments.Mkdir, NodeFS.mkdir); pub const mkdtemp = NewAsyncFSTask(Return.Mkdtemp, Arguments.MkdirTemp, NodeFS.mkdtemp); - pub const open = NewAsyncFSTask(Return.Open, Arguments.Open, NodeFS.open); - pub const read = NewAsyncFSTask(Return.Read, Arguments.Read, NodeFS.read); + pub const open = NewUVFSRequest(Return.Open, Arguments.Open, .open); + pub const read = NewUVFSRequest(Return.Read, Arguments.Read, .read); pub const readdir = NewAsyncFSTask(Return.Readdir, Arguments.Readdir, NodeFS.readdir); pub const readFile = NewAsyncFSTask(Return.ReadFile, Arguments.ReadFile, NodeFS.readFile); pub const readlink = NewAsyncFSTask(Return.Readlink, Arguments.Readlink, NodeFS.readlink); - pub const readv = NewAsyncFSTask(Return.Readv, Arguments.Readv, NodeFS.readv); + pub const readv = NewUVFSRequest(Return.Readv, Arguments.Readv, .readv); pub const realpath = NewAsyncFSTask(Return.Realpath, Arguments.Realpath, NodeFS.realpath); pub const rename = NewAsyncFSTask(Return.Rename, Arguments.Rename, NodeFS.rename); pub const rm = NewAsyncFSTask(Return.Rm, Arguments.Rm, NodeFS.rm); @@ -95,9 +97,9 @@ pub const Async = struct { pub const truncate = NewAsyncFSTask(Return.Truncate, Arguments.Truncate, NodeFS.truncate); pub const unlink = NewAsyncFSTask(Return.Unlink, Arguments.Unlink, NodeFS.unlink); pub const utimes = NewAsyncFSTask(Return.Utimes, Arguments.Utimes, NodeFS.utimes); - pub const write = NewAsyncFSTask(Return.Write, Arguments.Write, NodeFS.write); + pub const write = NewUVFSRequest(Return.Write, Arguments.Write, .write); pub const writeFile = NewAsyncFSTask(Return.WriteFile, Arguments.WriteFile, NodeFS.writeFile); - pub const writev = NewAsyncFSTask(Return.Writev, Arguments.Writev, NodeFS.writev); + pub const writev = NewUVFSRequest(Return.Writev, Arguments.Writev, .writev); pub const cp = AsyncCpTask; @@ -141,6 +143,195 @@ pub const Async = struct { } }; + fn NewUVFSRequest(comptime ReturnType: type, comptime ArgumentType: type, comptime FunctionEnum: NodeFSFunctionEnum) type { + if (!Environment.isWindows) { + return NewAsyncFSTask(ReturnType, ArgumentType, @field(NodeFS, @tagName(FunctionEnum))); + } + switch (FunctionEnum) { + .open, + .close, + .read, + .write, + .readv, + .writev, + => {}, + else => return NewAsyncFSTask(ReturnType, ArgumentType, @field(NodeFS, @tagName(FunctionEnum))), + } + + comptime bun.assert(Environment.isWindows); + return struct { + promise: JSC.JSPromise.Strong, + args: ArgumentType, + globalObject: *JSC.JSGlobalObject, + req: uv.fs_t = std.mem.zeroes(uv.fs_t), + result: JSC.Maybe(ReturnType), + ref: bun.Async.KeepAlive = .{}, + tracker: JSC.AsyncTaskTracker, + + pub const Task = @This(); + + pub const heap_label = "Async" ++ bun.meta.typeBaseName(@typeName(ArgumentType)) ++ "UvTask"; + + pub usingnamespace bun.New(@This()); + + pub fn create(globalObject: *JSC.JSGlobalObject, this: *JSC.Node.NodeJSFS, args: ArgumentType, vm: *JSC.VirtualMachine) JSC.JSValue { + var task = Task.new(.{ + .promise = JSC.JSPromise.Strong.init(globalObject), + .args = args, + .result = undefined, + .globalObject = globalObject, + .tracker = JSC.AsyncTaskTracker.init(vm), + }); + task.ref.ref(vm); + task.args.toThreadSafe(); + + task.tracker.didSchedule(globalObject); + + const log = bun.sys.syslog; + const loop = uv.Loop.get(); + task.req.data = task; + switch (comptime FunctionEnum) { + .open => { + const args_: Arguments.Open = task.args; + const path = if (bun.strings.eqlComptime(args_.path.slice(), "/dev/null")) "\\\\.\\NUL" else args_.path.sliceZ(&this.node_fs.sync_error_buf); + + var flags: c_int = @intFromEnum(args_.flags); + flags = uv.O.fromBunO(flags); + + var mode: c_int = args_.mode; + if (mode == 0) mode = 0o644; + + const rc = uv.uv_fs_open(loop, &task.req, path.ptr, flags, mode, &uv_callback); + bun.debugAssert(rc == .zero); + log("uv open({s}, {d}, {d}) = ~~", .{ path, flags, mode }); + }, + .close => { + const args_: Arguments.Close = task.args; + const fd = args_.fd.impl().uv(); + + if (fd == 1 or fd == 2) { + log("uv close({}) SKIPPED", .{fd}); + task.result = Maybe(Return.Close).success; + task.globalObject.bunVM().eventLoop().enqueueTask(JSC.Task.init(task)); + return task.promise.value(); + } + + const rc = uv.uv_fs_close(loop, &task.req, fd, &uv_callback); + bun.debugAssert(rc == .zero); + log("uv close({d}) = ~~", .{fd}); + }, + .read => { + const args_: Arguments.Read = task.args; + const B = uv.uv_buf_t.init; + const fd = args_.fd.impl().uv(); + + const rc = uv.uv_fs_read(loop, &task.req, fd, &.{B(args_.buffer.slice()[args_.offset..])}, 1, args_.position orelse -1, &uv_callback); + bun.debugAssert(rc == .zero); + log("uv read({d}) = ~~", .{fd}); + }, + .write => { + const args_: Arguments.Write = task.args; + const B = uv.uv_buf_t.init; + const fd = args_.fd.impl().uv(); + + const rc = uv.uv_fs_write(loop, &task.req, fd, &.{B(args_.buffer.slice()[args_.offset..])}, 1, args_.position orelse -1, &uv_callback); + bun.debugAssert(rc == .zero); + log("uv write({d}) = ~~", .{fd}); + }, + .readv => { + const args_: Arguments.Readv = task.args; + const fd = args_.fd.impl().uv(); + const bufs = args_.buffers.buffers.items; + const pos: i64 = args_.position orelse -1; + + var sum: u64 = 0; + for (bufs) |b| sum += b.slice().len; + + const rc = uv.uv_fs_read(loop, &task.req, fd, bufs.ptr, @intCast(bufs.len), pos, &uv_callback); + bun.debugAssert(rc == .zero); + log("uv readv({d}, {*}, {d}, {d}, {d} total bytes) = ~~", .{ fd, bufs.ptr, bufs.len, pos, sum }); + }, + .writev => { + const args_: Arguments.Writev = task.args; + const fd = args_.fd.impl().uv(); + const bufs = args_.buffers.buffers.items; + const pos: i64 = args_.position orelse -1; + + var sum: u64 = 0; + for (bufs) |b| sum += b.slice().len; + + const rc = uv.uv_fs_write(loop, &task.req, fd, bufs.ptr, @intCast(bufs.len), pos, &uv_callback); + bun.debugAssert(rc == .zero); + log("uv writev({d}, {*}, {d}, {d}, {d} total bytes) = ~~", .{ fd, bufs.ptr, bufs.len, pos, sum }); + }, + else => comptime unreachable, + } + + return task.promise.value(); + } + + fn uv_callback(req: *uv.fs_t) callconv(.C) void { + defer uv.uv_fs_req_cleanup(req); + const this: *Task = @ptrCast(@alignCast(req.data.?)); + var node_fs = NodeFS{}; + this.result = @field(NodeFS, "uv_" ++ @tagName(FunctionEnum))(&node_fs, this.args, @intFromEnum(req.result)); + + if (this.result == .err) { + this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch ""; + std.mem.doNotOptimizeAway(&node_fs); + } + + this.globalObject.bunVM().eventLoop().enqueueTask(JSC.Task.init(this)); + } + + pub fn runFromJSThread(this: *Task) void { + const globalObject = this.globalObject; + var success = @as(JSC.Maybe(ReturnType).Tag, this.result) == .result; + const result = switch (this.result) { + .err => |err| err.toJSC(globalObject), + .result => |*res| brk: { + const out = globalObject.toJS(res, .temporary); + success = out != .zero; + + break :brk out; + }, + }; + var promise_value = this.promise.value(); + var promise = this.promise.get(); + promise_value.ensureStillAlive(); + + const tracker = this.tracker; + tracker.willDispatch(globalObject); + defer tracker.didDispatch(globalObject); + + this.deinit(); + switch (success) { + false => { + promise.reject(globalObject, result); + }, + true => { + promise.resolve(globalObject, result); + }, + } + } + + pub fn deinit(this: *Task) void { + if (this.result == .err) { + bun.default_allocator.free(this.result.err.path); + } + + this.ref.unref(this.globalObject.bunVM()); + if (@hasDecl(ArgumentType, "deinitAndUnprotect")) { + this.args.deinitAndUnprotect(); + } else { + this.args.deinit(); + } + this.promise.deinit(); + this.destroy(); + } + }; + } + fn NewAsyncFSTask(comptime ReturnType: type, comptime ArgumentType: type, comptime Function: anytype) type { return struct { promise: JSC.JSPromise.Strong, @@ -157,6 +348,7 @@ pub const Async = struct { pub fn create( globalObject: *JSC.JSGlobalObject, + _: *JSC.Node.NodeJSFS, args: ArgumentType, vm: *JSC.VirtualMachine, ) JSC.JSValue { @@ -189,7 +381,7 @@ pub const Async = struct { std.mem.doNotOptimizeAway(&node_fs); } - this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this))); + this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.createFrom(this)); } pub fn runFromJSThread(this: *Task) void { @@ -234,7 +426,7 @@ pub const Async = struct { } else { this.args.deinit(); } - this.promise.strong.deinit(); + this.promise.deinit(); bun.destroy(this); } }; @@ -355,6 +547,7 @@ pub fn NewAsyncCpTask(comptime is_shell: bool) type { pub fn create( globalObject: *JSC.JSGlobalObject, + _: *JSC.Node.NodeJSFS, cp_args: Arguments.Cp, vm: *JSC.VirtualMachine, arena: bun.ArenaAllocator, @@ -496,7 +689,7 @@ pub fn NewAsyncCpTask(comptime is_shell: bool) type { this.deinitialized = true; if (comptime !is_shell) this.ref.unref(this.evtloop); this.args.deinit(); - this.promise.strong.deinit(); + this.promise.deinit(); this.arena.deinit(); bun.destroy(this); } @@ -757,7 +950,7 @@ pub const AsyncReaddirRecursiveTask = struct { root_path: PathString = PathString.empty, pending_err: ?Syscall.Error = null, - pending_err_mutex: bun.Lock = bun.Lock.init(), + pending_err_mutex: bun.Lock = .{}, pub usingnamespace bun.New(@This()); @@ -1059,7 +1252,7 @@ pub const AsyncReaddirRecursiveTask = struct { this.args.deinit(); bun.default_allocator.free(this.root_path.slice()); this.clearResultList(); - this.promise.strong.deinit(); + this.promise.deinit(); this.destroy(); } }; @@ -1444,6 +1637,14 @@ pub const Arguments = struct { }; arguments.eat(); + if (!uid_value.isNumber()) { + ctx.throwValue(ctx.ERR_INVALID_ARG_TYPE_static( + JSC.ZigString.static("uid"), + JSC.ZigString.static("number"), + uid_value, + )); + return null; + } break :brk @as(uid_t, @intCast(uid_value.toInt32())); }; @@ -1461,6 +1662,14 @@ pub const Arguments = struct { }; arguments.eat(); + if (!gid_value.isNumber()) { + ctx.throwValue(ctx.ERR_INVALID_ARG_TYPE_static( + JSC.ZigString.static("gid"), + JSC.ZigString.static("number"), + gid_value, + )); + return null; + } break :brk @as(gid_t, @intCast(gid_value.toInt32())); }; @@ -2291,6 +2500,7 @@ pub const Arguments = struct { if (val.getIfPropertyExists(ctx.ptr(), "mode")) |mode_| { mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse mode; + if (exception.* != null) return null; } } } @@ -2304,7 +2514,7 @@ pub const Arguments = struct { }; const MkdirTemp = struct { - prefix: JSC.Node.StringOrBuffer = .{ .buffer = .{ .buffer = JSC.ArrayBuffer.empty } }, + prefix: StringOrBuffer = .{ .buffer = .{ .buffer = JSC.ArrayBuffer.empty } }, encoding: Encoding = Encoding.utf8, pub fn deinit(this: MkdirTemp) void { @@ -2322,7 +2532,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?MkdirTemp { const prefix_value = arguments.next() orelse return MkdirTemp{}; - const prefix = JSC.Node.StringOrBuffer.fromJS(ctx, bun.default_allocator, prefix_value) orelse { + const prefix = StringOrBuffer.fromJS(ctx, bun.default_allocator, prefix_value) orelse { if (exception.* == null) { JSC.throwInvalidArguments( "prefix must be a string or TypedArray", @@ -2527,18 +2737,23 @@ pub const Arguments = struct { if (val.isObject()) { if (val.getTruthy(ctx.ptr(), "flags")) |flags_| { flags = FileSystemFlags.fromJS(ctx, flags_, exception) orelse flags; + if (exception.* != null) return null; } if (val.getTruthy(ctx.ptr(), "mode")) |mode_| { mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse mode; + if (exception.* != null) return null; } } else if (!val.isEmpty()) { - if (!val.isUndefinedOrNull()) + if (!val.isUndefinedOrNull()) { // error is handled below flags = FileSystemFlags.fromJS(ctx, val, exception) orelse flags; + if (exception.* != null) return null; + } if (arguments.nextEat()) |next| { mode = JSC.Node.modeFromJS(ctx, next, exception) orelse mode; + if (exception.* != null) return null; } } } @@ -2680,7 +2895,7 @@ pub const Arguments = struct { /// pub const Write = struct { fd: FileDescriptor, - buffer: JSC.Node.StringOrBuffer, + buffer: StringOrBuffer, // buffer_val: JSC.JSValue = JSC.JSValue.zero, offset: u64 = 0, length: u64 = std.math.maxInt(u64), @@ -2944,6 +3159,7 @@ pub const Arguments = struct { offset: JSC.WebCore.Blob.SizeType = 0, max_size: ?JSC.WebCore.Blob.SizeType = null, + limit_size_for_javascript: bool = false, flag: FileSystemFlags = FileSystemFlags.r, @@ -3029,6 +3245,7 @@ pub const Arguments = struct { .path = path, .encoding = encoding, .flag = flag, + .limit_size_for_javascript = true, }; } }; @@ -3270,6 +3487,7 @@ pub const Arguments = struct { }; } }; + pub const Exists = struct { path: ?PathLike, @@ -3831,8 +4049,11 @@ pub const Arguments = struct { }; pub const UnwatchFile = void; + pub const Watch = JSC.Node.FSWatcher.Arguments; + pub const WatchFile = JSC.Node.StatWatcher.Arguments; + pub const Fsync = struct { fd: FileDescriptor, @@ -3952,7 +4173,6 @@ const Return = struct { ); } }; - pub const WritePromise = struct { bytes_written: u52, buffer: StringOrBuffer, @@ -3990,7 +4210,6 @@ const Return = struct { return JSC.JSValue.jsNumberFromUint64(this.bytes_written); } }; - pub const Readdir = union(Tag) { with_file_types: []Dirent, buffers: []Buffer, @@ -4019,19 +4238,18 @@ const Return = struct { } } }; - pub const ReadFile = JSC.Node.StringOrBuffer; + pub const ReadFile = StringOrBuffer; pub const ReadFileWithOptions = union(enum) { string: string, buffer: JSC.Node.Buffer, null_terminated: [:0]const u8, }; - pub const Readlink = JSC.Node.StringOrBuffer; - pub const Realpath = JSC.Node.StringOrBuffer; + pub const Readlink = StringOrBuffer; + pub const Realpath = StringOrBuffer; pub const RealpathNative = Realpath; pub const Rename = void; pub const Rmdir = void; pub const Stat = StatOrNotFound; - pub const Symlink = void; pub const Truncate = void; pub const Unlink = void; @@ -4039,10 +4257,8 @@ const Return = struct { pub const Watch = JSC.JSValue; pub const WatchFile = JSC.JSValue; pub const Utimes = void; - pub const Chown = void; pub const Lutimes = void; - pub const Writev = Write; }; @@ -4115,6 +4331,17 @@ pub const NodeFS = struct { return if (Syscall.close(args.fd)) |err| .{ .err = err } else Maybe(Return.Close).success; } + pub fn uv_close(_: *NodeFS, args: Arguments.Close, rc: i64) Maybe(Return.Close) { + if (rc < 0) { + return Maybe(Return.Close){ .err = .{ + .errno = @intCast(-rc), + .syscall = .close, + .fd = args.fd, + } }; + } + return Maybe(Return.Close).success; + } + // since we use a 64 KB stack buffer, we should not let this function get inlined pub noinline fn copyFileUsingReadWriteLoop(src: [:0]const u8, dest: [:0]const u8, src_fd: FileDescriptor, dest_fd: FileDescriptor, stat_size: usize, wrote: *u64) Maybe(Return.CopyFile) { var stack_buf: [64 * 1024]u8 = undefined; @@ -4606,6 +4833,7 @@ pub const NodeFS = struct { pub fn mkdir(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) { return if (args.recursive) mkdirRecursive(this, args, flavor) else mkdirNonRecursive(this, args, flavor); } + // Node doesn't absolute the path so we don't have to either pub fn mkdirNonRecursive(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) { _ = flavor; @@ -4823,6 +5051,7 @@ pub const NodeFS = struct { if (Environment.isWindows) { var req: uv.fs_t = uv.fs_t.uninitialized; + defer req.deinit(); const rc = uv.uv_fs_mkdtemp(bun.Async.Loop.get(), &req, @ptrCast(prefix_buf.ptr), null); if (rc.errno()) |errno| { return .{ .err = .{ .errno = errno, .syscall = .mkdtemp, .path = prefix_buf[0 .. len + 6] } }; @@ -4865,6 +5094,18 @@ pub const NodeFS = struct { }; } + pub fn uv_open(this: *NodeFS, args: Arguments.Open, rc: i64) Maybe(Return.Open) { + _ = this; + if (rc < 0) { + return Maybe(Return.Open){ .err = .{ + .errno = @intCast(-rc), + .syscall = .open, + .path = args.path.slice(), + } }; + } + return Maybe(Return.Open).initResult(FDImpl.decode(bun.toFD(@as(u32, @intCast(rc))))); + } + pub fn openDir(_: *NodeFS, _: Arguments.OpenDir, comptime _: Flavor) Maybe(Return.OpenDir) { return Maybe(Return.OpenDir).todo(); } @@ -4918,6 +5159,30 @@ pub const NodeFS = struct { ); } + pub fn uv_read(this: *NodeFS, args: Arguments.Read, rc: i64) Maybe(Return.Read) { + _ = this; + if (rc < 0) { + return Maybe(Return.Read){ .err = .{ + .errno = @intCast(-rc), + .syscall = .read, + .fd = args.fd, + } }; + } + return Maybe(Return.Read).initResult(.{ .bytes_read = @intCast(rc) }); + } + + pub fn uv_readv(this: *NodeFS, args: Arguments.Readv, rc: i64) Maybe(Return.Readv) { + _ = this; + if (rc < 0) { + return Maybe(Return.Readv){ .err = .{ + .errno = @intCast(-rc), + .syscall = .readv, + .fd = args.fd, + } }; + } + return Maybe(Return.Readv).initResult(.{ .bytes_read = @intCast(rc) }); + } + pub fn readv(this: *NodeFS, args: Arguments.Readv, comptime flavor: Flavor) Maybe(Return.Readv) { return if (args.position != null) _preadv(this, args, flavor) else _readv(this, args, flavor); } @@ -4930,6 +5195,30 @@ pub const NodeFS = struct { return if (args.position != null) _pwrite(this, args, flavor) else _write(this, args, flavor); } + pub fn uv_write(this: *NodeFS, args: Arguments.Write, rc: i64) Maybe(Return.Write) { + _ = this; + if (rc < 0) { + return Maybe(Return.Write){ .err = .{ + .errno = @intCast(-rc), + .syscall = .write, + .fd = args.fd, + } }; + } + return Maybe(Return.Write).initResult(.{ .bytes_written = @intCast(rc) }); + } + + pub fn uv_writev(this: *NodeFS, args: Arguments.Writev, rc: i64) Maybe(Return.Writev) { + _ = this; + if (rc < 0) { + return Maybe(Return.Writev){ .err = .{ + .errno = @intCast(-rc), + .syscall = .writev, + .fd = args.fd, + } }; + } + return Maybe(Return.Writev).initResult(.{ .bytes_written = @intCast(rc) }); + } + fn _write(_: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) { _ = flavor; @@ -5044,7 +5333,10 @@ pub const NodeFS = struct { const dir = fd.asDir(); const is_u16 = comptime Environment.isWindows and (ExpectedType == bun.String or ExpectedType == Dirent); - var dirent_path: ?bun.String = null; + var dirent_path: bun.String = bun.String.dead; + defer { + dirent_path.deref(); + } var iterator = DirIterator.iterate(dir, comptime if (is_u16) .u16 else .u8); var entry = iterator.next(); @@ -5075,7 +5367,7 @@ pub const NodeFS = struct { .result => |ent| ent, }) |current| : (entry = iterator.next()) { if (ExpectedType == Dirent) { - if (dirent_path == null) { + if (dirent_path.isEmpty()) { dirent_path = bun.String.createUTF8(basename); } } @@ -5083,10 +5375,10 @@ pub const NodeFS = struct { const utf8_name = current.name.slice(); switch (ExpectedType) { Dirent => { - dirent_path.?.ref(); + dirent_path.ref(); entries.append(.{ .name = bun.String.createUTF8(utf8_name), - .path = dirent_path.?, + .path = dirent_path, .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -5102,10 +5394,10 @@ pub const NodeFS = struct { const utf16_name = current.name.slice(); switch (ExpectedType) { Dirent => { - dirent_path.?.ref(); + dirent_path.ref(); entries.append(.{ .name = bun.String.createUTF16(utf16_name), - .path = dirent_path.?, + .path = dirent_path, .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -5116,9 +5408,6 @@ pub const NodeFS = struct { } } } - if (dirent_path) |*p| { - p.deref(); - } return Maybe(void).success; } @@ -5177,7 +5466,10 @@ pub const NodeFS = struct { var iterator = DirIterator.iterate(fd.asDir(), .u8); var entry = iterator.next(); - var dirent_path_prev: ?bun.String = null; + var dirent_path_prev: bun.String = bun.String.empty; + defer { + dirent_path_prev.deref(); + } while (switch (entry) { .err => |err| { @@ -5231,13 +5523,15 @@ pub const NodeFS = struct { switch (comptime ExpectedType) { Dirent => { const path_u8 = bun.path.dirname(bun.path.join(&[_]string{ root_basename, name_to_copy }, .auto), .auto); - if (dirent_path_prev == null or bun.strings.eql(dirent_path_prev.?.byteSlice(), path_u8)) { + if (dirent_path_prev.isEmpty() or !bun.strings.eql(dirent_path_prev.byteSlice(), path_u8)) { + dirent_path_prev.deref(); dirent_path_prev = bun.String.createUTF8(path_u8); } - dirent_path_prev.?.ref(); + dirent_path_prev.ref(); + entries.append(.{ .name = bun.String.createUTF8(utf8_name), - .path = dirent_path_prev.?, + .path = dirent_path_prev, .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -5250,9 +5544,6 @@ pub const NodeFS = struct { else => bun.outOfMemory(), } } - if (dirent_path_prev) |*p| { - p.deref(); - } return Maybe(void).success; } @@ -5330,7 +5621,10 @@ pub const NodeFS = struct { var iterator = DirIterator.iterate(fd.asDir(), .u8); var entry = iterator.next(); - var dirent_path_prev: ?bun.String = null; + var dirent_path_prev: bun.String = bun.String.dead; + defer { + dirent_path_prev.deref(); + } while (switch (entry) { .err => |err| { @@ -5370,13 +5664,14 @@ pub const NodeFS = struct { switch (comptime ExpectedType) { Dirent => { const path_u8 = bun.path.dirname(bun.path.join(&[_]string{ root_basename, name_to_copy }, .auto), .auto); - if (dirent_path_prev == null or bun.strings.eql(dirent_path_prev.?.byteSlice(), path_u8)) { + if (dirent_path_prev.isEmpty() or !bun.strings.eql(dirent_path_prev.byteSlice(), path_u8)) { + dirent_path_prev.deref(); dirent_path_prev = bun.String.createUTF8(path_u8); } - dirent_path_prev.?.ref(); + dirent_path_prev.ref(); entries.append(.{ .name = bun.String.createUTF8(utf8_name), - .path = dirent_path_prev.?, + .path = dirent_path_prev, .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -5389,14 +5684,34 @@ pub const NodeFS = struct { else => @compileError("Impossible"), } } - if (dirent_path_prev) |*p| { - p.deref(); - } } return Maybe(void).success; } + fn shouldThrowOutOfMemoryEarlyForJavaScript(encoding: Encoding, size: usize, syscall: Syscall.Tag) ?Syscall.Error { + // Strings & typed arrays max out at 4.7 GB. + // But, it's **string length** + // So you can load an 8 GB hex string, for example, it should be fine. + const adjusted_size = switch (encoding) { + .utf16le, .ucs2, .utf8 => size / 4 -| 1, + .hex => size / 2 -| 1, + .base64, .base64url => size / 3 -| 1, + .ascii, .latin1, .buffer => size, + }; + + if ( + // Typed arrays in JavaScript are limited to 4.7 GB. + adjusted_size > JSC.synthetic_allocation_limit or + // If they do not have enough memory to open the file and they're on Linux, let's throw an error instead of dealing with the OOM killer. + (Environment.isLinux and size >= bun.getTotalMemorySize())) + { + return Syscall.Error.fromCode(.NOMEM, syscall); + } + + return null; + } + fn _readdir( buf: *bun.PathBuffer, args: Arguments.Readdir, @@ -5486,7 +5801,15 @@ pub const NodeFS = struct { .buffer = ret.result.buffer, }, }, - .string => .{ .result = .{ .string = bun.SliceWithUnderlyingString.transcodeFromOwnedSlice(@constCast(ret.result.string), args.encoding) } }, + .string => brk: { + const str = bun.SliceWithUnderlyingString.transcodeFromOwnedSlice(@constCast(ret.result.string), args.encoding); + + if (str.underlying.tag == .Dead and str.utf8.len == 0) { + return .{ .err = Syscall.Error.fromCode(.NOMEM, .read).withPathLike(args.path) }; + } + + break :brk .{ .result = .{ .string = str } }; + }, else => unreachable, }, }; @@ -5569,58 +5892,95 @@ pub const NodeFS = struct { } // For certain files, the size might be 0 but the file might still have contents. // https://github.com/oven-sh/bun/issues/1220 + const max_size = args.max_size orelse std.math.maxInt(JSC.WebCore.Blob.SizeType); + const has_max_size = args.max_size != null; + const size = @as( u64, @max( @min( stat_.size, // Only used in DOMFormData - args.max_size orelse std.math.maxInt(JSC.WebCore.Blob.SizeType), + max_size, ), 0, ), ) + @intFromBool(comptime string_type == .null_terminated); + if (args.limit_size_for_javascript and + // assume that anything more than 40 bits is not trustworthy. + (size < std.math.maxInt(u40))) + { + if (shouldThrowOutOfMemoryEarlyForJavaScript(args.encoding, size, .read)) |err| { + return .{ .err = err.withPathLike(args.path) }; + } + } + + var did_succeed = false; var buf = std.ArrayList(u8).init(bun.default_allocator); - buf.ensureTotalCapacityPrecise(size + 16) catch unreachable; + defer if (!did_succeed) buf.clearAndFree(); + buf.ensureTotalCapacityPrecise(size + 16) catch return .{ + .err = Syscall.Error.fromCode(.NOMEM, .read).withPathLike(args.path), + }; buf.expandToCapacity(); var total: usize = 0; while (total < size) { - switch (Syscall.read(fd, buf.items.ptr[total..buf.capacity])) { + switch (Syscall.read(fd, buf.items.ptr[total..@min(buf.capacity, max_size)])) { .err => |err| return .{ .err = err, }, .result => |amt| { total += amt; + + if (args.limit_size_for_javascript) { + if (shouldThrowOutOfMemoryEarlyForJavaScript(args.encoding, total, .read)) |err| { + return .{ + .err = err.withPathLike(args.path), + }; + } + } + // There are cases where stat()'s size is wrong or out of date - if (total > size and amt != 0) { - buf.ensureUnusedCapacity(8192) catch unreachable; - buf.expandToCapacity(); + if (total > size and amt != 0 and !has_max_size) { + buf.items.len = total; + buf.ensureUnusedCapacity(8192) catch { + return .{ .err = Syscall.Error.fromCode(.NOMEM, .read).withPathLike(args.path) }; + }; continue; } if (amt == 0) { + did_succeed = true; break; } }, } } else { while (true) { - switch (Syscall.read(fd, buf.items.ptr[total..buf.capacity])) { + switch (Syscall.read(fd, buf.items.ptr[total..@min(buf.capacity, max_size)])) { .err => |err| return .{ .err = err, }, .result => |amt| { total += amt; - // There are cases where stat()'s size is wrong or out of date - if (total > size and amt != 0) { - buf.ensureUnusedCapacity(8192) catch unreachable; - buf.expandToCapacity(); + + if (args.limit_size_for_javascript) { + if (shouldThrowOutOfMemoryEarlyForJavaScript(args.encoding, total, .read)) |err| { + return .{ .err = err.withPathLike(args.path) }; + } + } + + if (total > size and amt != 0 and !has_max_size) { + buf.items.len = total; + buf.ensureUnusedCapacity(8192) catch { + return .{ .err = Syscall.Error.fromCode(.NOMEM, .read).withPathLike(args.path) }; + }; continue; } if (amt == 0) { + did_succeed = true; break; } }, @@ -5630,7 +5990,7 @@ pub const NodeFS = struct { buf.items.len = if (comptime string_type == .null_terminated) total + 1 else total; if (total == 0) { - buf.deinit(); + buf.clearAndFree(); return switch (args.encoding) { .buffer => .{ .result = .{ @@ -5671,7 +6031,10 @@ pub const NodeFS = struct { } else { break :brk .{ .result = .{ - .null_terminated = buf.toOwnedSliceSentinel(0) catch unreachable, + .null_terminated = buf.toOwnedSliceSentinel(0) catch return .{ + // Since we are expecting a null-terminated string, we can't just ignore the resize failure. + .err = Syscall.Error.fromCode(.NOMEM, .read).withPathLike(args.path), + }, }, }; } @@ -5755,26 +6118,15 @@ pub const NodeFS = struct { } } - if (Environment.isWindows) { - if (args.flag == .a) { - return Maybe(Return.WriteFile).success; - } - - const rc = std.os.windows.kernel32.SetEndOfFile(fd.cast()); - if (rc == 0) { - return .{ - .err = Syscall.Error{ - .errno = @intFromEnum(std.os.windows.kernel32.GetLastError()), - .syscall = .SetEndOfFile, - .fd = fd, - }, - }; - } - } else { - // https://github.com/oven-sh/bun/issues/2931 - // https://github.com/oven-sh/bun/issues/10222 - // only truncate if we're not appending and writing to a path - if ((@intFromEnum(args.flag) & bun.O.APPEND) == 0 and args.file != .fd) { + // https://github.com/oven-sh/bun/issues/2931 + // https://github.com/oven-sh/bun/issues/10222 + // Only truncate if we're not appending and writing to a path + if ((@intFromEnum(args.flag) & bun.O.APPEND) == 0 and args.file != .fd) { + // If this errors, we silently ignore it. + // Not all files are seekable (and thus, not all files can be truncated). + if (Environment.isWindows) { + _ = std.os.windows.kernel32.SetEndOfFile(fd.cast()); + } else { _ = ftruncateSync(.{ .fd = fd, .len = @as(JSC.WebCore.Blob.SizeType, @truncate(written)) }); } } @@ -6381,18 +6733,20 @@ pub const NodeFS = struct { }; } - if (comptime Environment.isMac) { + if (comptime Environment.isMac) try_with_clonefile: { if (Maybe(Return.Cp).errnoSysP(C.clonefile(src, dest, 0), .clonefile, src)) |err| { switch (err.getErrno()) { - .ACCES, - .NAMETOOLONG, - .ROFS, - .PERM, - .INVAL, - => { + .NAMETOOLONG, .ROFS, .INVAL, .ACCES, .PERM => |errno| { + if (errno == .ACCES or errno == .PERM) { + if (args.flags.force) { + break :try_with_clonefile; + } + } + @memcpy(this.sync_error_buf[0..src.len], src); return .{ .err = err.err.withPath(this.sync_error_buf[0..src.len]) }; }, + // Other errors may be due to clonefile() not being supported // We'll fall back to other implementations else => {}, diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig index 9f7bca8c4437d..4f10e96b8afd9 100644 --- a/src/bun.js/node/node_fs_binding.zig +++ b/src/bun.js/node/node_fs_binding.zig @@ -14,7 +14,7 @@ const NodeFSFunction = fn ( this: *JSC.Node.NodeJSFS, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue; +) JSC.JSValue; const NodeFSFunctionEnum = std.meta.DeclEnum(JSC.Node.NodeFS); @@ -34,7 +34,7 @@ fn callSync(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction { this: *JSC.Node.NodeJSFS, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var exceptionref: JSC.C.JSValueRef = null; var arguments = callframe.arguments(8); @@ -89,11 +89,7 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction { comptime if (function.params.len != 3) @compileError("Expected 3 arguments"); const Arguments = comptime function.params[1].type.?; const NodeBindingClosure = struct { - pub fn bind( - _: *JSC.Node.NodeJSFS, - globalObject: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + pub fn bind(this: *JSC.Node.NodeJSFS, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { var arguments = callframe.arguments(8); var slice = ArgumentsSlice.init(globalObject.bunVM(), arguments.slice()); @@ -123,7 +119,7 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction { const Task = @field(JSC.Node.Async, @tagName(FunctionEnum)); if (comptime FunctionEnum == .cp) { - return Task.create(globalObject, args, globalObject.bunVM(), slice.arena); + return Task.create(globalObject, this, args, globalObject.bunVM(), slice.arena); } else { if (comptime FunctionEnum == .readdir) { if (args.recursive) { @@ -131,7 +127,7 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction { } } - return Task.create(globalObject, args, globalObject.bunVM()); + return Task.create(globalObject, this, args, globalObject.bunVM()); } } }; @@ -144,12 +140,12 @@ pub const NodeJSFS = struct { pub usingnamespace JSC.Codegen.JSNodeJSFS; pub usingnamespace bun.New(@This()); - pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*@This() { + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*@This() { globalObject.throw("Not a constructor", .{}); return null; } - pub fn finalize(this: *JSC.Node.NodeJSFS) callconv(.C) void { + pub fn finalize(this: *JSC.Node.NodeJSFS) void { if (this.node_fs.vm) |vm| { if (vm.node_fs == &this.node_fs) { return; @@ -241,11 +237,11 @@ pub const NodeJSFS = struct { pub const fdatasyncSync = callSync(.fdatasync); pub const fdatasync = call(.fdatasync); - pub fn getDirent(_: *NodeJSFS, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getDirent(_: *NodeJSFS, globalThis: *JSC.JSGlobalObject) JSC.JSValue { return JSC.Node.Dirent.getConstructor(globalThis); } - pub fn getStats(_: *NodeJSFS, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getStats(_: *NodeJSFS, globalThis: *JSC.JSGlobalObject) JSC.JSValue { return JSC.Node.StatsSmall.getConstructor(globalThis); } @@ -260,8 +256,7 @@ pub const NodeJSFS = struct { }; pub fn createBinding(globalObject: *JSC.JSGlobalObject) JSC.JSValue { - var module = globalObject.allocator().create(NodeJSFS) catch bun.outOfMemory(); - module.* = .{}; + const module = NodeJSFS.new(.{}); const vm = globalObject.bunVM(); if (vm.standalone_module_graph != null) @@ -269,3 +264,28 @@ pub fn createBinding(globalObject: *JSC.JSGlobalObject) JSC.JSValue { return module.toJS(globalObject); } + +pub fn createMemfdForTesting(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { + const arguments = callFrame.arguments(1); + + if (arguments.len < 1) { + return .undefined; + } + + if (comptime !bun.Environment.isLinux) { + globalObject.throw("memfd_create is not implemented on this platform", .{}); + return .zero; + } + + const size = arguments.ptr[0].toInt64(); + switch (bun.sys.memfd_create("my_memfd", std.os.linux.MFD.CLOEXEC)) { + .result => |fd| { + _ = bun.sys.ftruncate(fd, size); + return JSC.JSValue.jsNumber(fd.cast()); + }, + .err => |err| { + globalObject.throwValue(err.toJSC(globalObject)); + return .zero; + }, + } +} diff --git a/src/bun.js/node/node_fs_stat_watcher.zig b/src/bun.js/node/node_fs_stat_watcher.zig index 74f6717b4993a..5f197a0c1b022 100644 --- a/src/bun.js/node/node_fs_stat_watcher.zig +++ b/src/bun.js/node/node_fs_stat_watcher.zig @@ -26,9 +26,9 @@ const log = bun.Output.scoped(.StatWatcher, false); fn statToJSStats(globalThis: *JSC.JSGlobalObject, stats: bun.Stat, bigint: bool) JSC.JSValue { if (bigint) { - return bun.new(StatsBig, StatsBig.init(stats)).toJS(globalThis); + return StatsBig.new(StatsBig.init(stats)).toJS(globalThis); } else { - return bun.new(StatsSmall, StatsSmall.init(stats)).toJS(globalThis); + return StatsSmall.new(StatsSmall.init(stats)).toJS(globalThis); } } @@ -305,27 +305,27 @@ pub const StatWatcher = struct { if (obj.js_this != .zero) { return obj.js_this; } - return JSC.JSValue.jsUndefined(); + return .undefined; } }; - pub fn doRef(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doRef(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { if (!this.closed and !this.persistent) { this.persistent = true; this.poll_ref.ref(this.ctx); } - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn doUnref(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doUnref(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { if (this.persistent) { this.persistent = false; this.poll_ref.unref(this.ctx); } - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn hasPendingActivity(this: *StatWatcher) callconv(.C) bool { + pub fn hasPendingActivity(this: *StatWatcher) bool { @fence(.acquire); return this.used_by_scheduler_thread.load(.acquire); @@ -343,13 +343,13 @@ pub const StatWatcher = struct { this.last_jsvalue.clear(); } - pub fn doClose(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doClose(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { this.close(); - return JSC.JSValue.jsUndefined(); + return .undefined; } /// If the scheduler is not using this, free instantly, otherwise mark for being freed. - pub fn finalize(this: *StatWatcher) callconv(.C) void { + pub fn finalize(this: *StatWatcher) void { log("Finalize\n", .{}); this.deinit(); } @@ -415,18 +415,16 @@ pub const StatWatcher = struct { const jsvalue = statToJSStats(this.globalThis, this.last_stat, this.bigint); this.last_jsvalue = JSC.Strong.create(jsvalue, this.globalThis); - const result = StatWatcher.listenerGetCached(this.js_this).?.call( + const vm = this.globalThis.bunVM(); + + _ = StatWatcher.listenerGetCached(this.js_this).?.call( this.globalThis, + .undefined, &[2]JSC.JSValue{ jsvalue, jsvalue, }, - ); - - const vm = this.globalThis.bunVM(); - if (result.isAnyError()) { - _ = vm.uncaughtException(this.globalThis, result, false); - } + ) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err); vm.rareData().nodeFSStatWatcherScheduler(vm).append(this); } @@ -452,17 +450,14 @@ pub const StatWatcher = struct { const current_jsvalue = statToJSStats(this.globalThis, this.last_stat, this.bigint); this.last_jsvalue.set(this.globalThis, current_jsvalue); - const result = StatWatcher.listenerGetCached(this.js_this).?.call( + _ = StatWatcher.listenerGetCached(this.js_this).?.call( this.globalThis, + .undefined, &[2]JSC.JSValue{ current_jsvalue, prev_jsvalue, }, - ); - if (result.isAnyError()) { - const vm = this.globalThis.bunVM(); - _ = vm.uncaughtException(this.globalThis, result, false); - } + ) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err); } pub fn onTimerInterval(timer: *uws.Timer) callconv(.C) void { diff --git a/src/bun.js/node/node_fs_watcher.zig b/src/bun.js/node/node_fs_watcher.zig index fc785f10dc480..209d4c85f9f00 100644 --- a/src/bun.js/node/node_fs_watcher.zig +++ b/src/bun.js/node/node_fs_watcher.zig @@ -535,12 +535,12 @@ pub const FSWatcher = struct { listener.ensureStillAlive(); var args = [_]JSC.JSValue{ EventType.@"error".toJS(this.globalThis), - if (err.isEmptyOrUndefinedOrNull()) JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.globalThis) else err, + if (err.isEmptyOrUndefinedOrNull()) JSC.CommonAbortReason.UserAbort.toJS(this.globalThis) else err, }; _ = listener.callWithGlobalThis( this.globalThis, &args, - ); + ) catch this.globalThis.clearException(); } } } @@ -561,7 +561,7 @@ pub const FSWatcher = struct { _ = listener.callWithGlobalThis( globalObject, &args, - ); + ) catch |e| this.globalThis.reportActiveExceptionAsUnhandled(e); } } } @@ -579,12 +579,12 @@ pub const FSWatcher = struct { if (js_this == .zero) return; const listener = FSWatcher.listenerGetCached(js_this) orelse return; const globalObject = this.globalThis; - var filename: JSC.JSValue = JSC.JSValue.jsUndefined(); + var filename: JSC.JSValue = .undefined; if (file_name.len > 0) { if (this.encoding == .buffer) filename = JSC.ArrayBuffer.createBuffer(globalObject, file_name) else if (this.encoding == .utf8) { - filename = JSC.ZigString.fromUTF8(file_name).toValueGC(globalObject); + filename = JSC.ZigString.fromUTF8(file_name).toJS(globalObject); } else { // convert to desired encoding filename = Encoder.toStringAtRuntime(file_name.ptr, file_name.len, globalObject, this.encoding); @@ -600,33 +600,29 @@ pub const FSWatcher = struct { filename, }; - const err = listener.callWithGlobalThis( + _ = listener.callWithGlobalThis( globalObject, &args, - ); - - if (err.toError()) |value| { - _ = JSC.VirtualMachine.get().uncaughtException(globalObject, value, false); - } + ) catch |err| globalObject.reportActiveExceptionAsUnhandled(err); } - pub fn doRef(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doRef(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { if (!this.closed and !this.persistent) { this.persistent = true; this.poll_ref.ref(this.ctx); } - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn doUnref(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doUnref(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { if (this.persistent) { this.persistent = false; this.poll_ref.unref(this.ctx); } - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn hasRef(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn hasRef(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { return JSC.JSValue.jsBoolean(this.persistent); } @@ -641,7 +637,7 @@ pub const FSWatcher = struct { return true; } - pub fn hasPendingActivity(this: *FSWatcher) callconv(.C) bool { + pub fn hasPendingActivity(this: *FSWatcher) bool { @fence(.acquire); return this.pending_activity_count.load(.acquire) > 0; } @@ -696,12 +692,12 @@ pub const FSWatcher = struct { this.js_this = .zero; } - pub fn doClose(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doClose(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { this.close(); - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn finalize(this: *FSWatcher) callconv(.C) void { + pub fn finalize(this: *FSWatcher) void { this.deinit(); } @@ -741,7 +737,7 @@ pub const FSWatcher = struct { .ctx = undefined, .count = 0, }, - .mutex = Mutex.init(), + .mutex = .{}, .signal = if (args.signal) |s| s.ref() else null, .persistent = args.persistent, .path_watcher = null, diff --git a/src/bun.js/node/node_http_binding.zig b/src/bun.js/node/node_http_binding.zig new file mode 100644 index 0000000000000..7ad2be8088e53 --- /dev/null +++ b/src/bun.js/node/node_http_binding.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Environment = bun.Environment; +const JSC = bun.JSC; +const string = bun.string; +const Output = bun.Output; +const ZigString = JSC.ZigString; +const uv = bun.windows.libuv; + +pub fn getBunServerAllClosedPromise(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(1).slice(); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("getBunServerAllClosePromise", 1, arguments.len); + return .zero; + } + + const value = arguments[0]; + + inline for ([_]type{ + JSC.API.HTTPServer, + JSC.API.HTTPSServer, + JSC.API.DebugHTTPServer, + JSC.API.DebugHTTPSServer, + }) |Server| { + if (value.as(Server)) |server| { + if (server.listener == null and server.pending_requests == 0) { + return JSC.JSPromise.resolvedPromise(globalThis, .undefined).asValue(globalThis); + } + const prom = &server.all_closed_promise; + if (prom.strong.has()) { + return prom.value(); + } + prom.* = JSC.JSPromise.Strong.init(globalThis); + return prom.value(); + } + } + + return globalThis.throwInvalidArgumentTypeValue("server", "bun.Server", value); +} + +pub fn getMaxHTTPHeaderSize(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + _ = globalThis; // autofix + _ = callframe; // autofix + return JSC.JSValue.jsNumber(bun.http.max_http_header_size); +} + +pub fn setMaxHTTPHeaderSize(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(1).slice(); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("setMaxHTTPHeaderSize", 1, arguments.len); + return .zero; + } + const value = arguments[0]; + const num = value.coerceToInt64(globalThis); + if (num <= 0) { + return globalThis.throwInvalidArgumentTypeValue("maxHeaderSize", "non-negative integer", value); + } + bun.http.max_http_header_size = @intCast(num); + return JSC.JSValue.jsNumber(bun.http.max_http_header_size); +} diff --git a/src/bun.js/node/node_net_binding.zig b/src/bun.js/node/node_net_binding.zig index ce38d71c44795..8a4854261fa7f 100644 --- a/src/bun.js/node/node_net_binding.zig +++ b/src/bun.js/node/node_net_binding.zig @@ -11,9 +11,9 @@ const ZigString = JSC.ZigString; pub var autoSelectFamilyDefault: bool = true; -pub fn getDefaultAutoSelectFamily(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { +pub fn getDefaultAutoSelectFamily(global: *JSC.JSGlobalObject) JSC.JSValue { return JSC.JSFunction.create(global, "getDefaultAutoSelectFamily", (struct { - fn getter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + fn getter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { _ = globalThis; _ = callframe; return JSC.jsBoolean(autoSelectFamilyDefault); @@ -21,9 +21,9 @@ pub fn getDefaultAutoSelectFamily(global: *JSC.JSGlobalObject) callconv(.C) JSC. }).getter, 0, .{}); } -pub fn setDefaultAutoSelectFamily(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { +pub fn setDefaultAutoSelectFamily(global: *JSC.JSGlobalObject) JSC.JSValue { return JSC.JSFunction.create(global, "setDefaultAutoSelectFamily", (struct { - fn setter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + fn setter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(1); if (arguments.len < 1) { globalThis.throw("missing argument", .{}); @@ -46,9 +46,9 @@ pub fn setDefaultAutoSelectFamily(global: *JSC.JSGlobalObject) callconv(.C) JSC. pub var autoSelectFamilyAttemptTimeoutDefault: u32 = 250; -pub fn getDefaultAutoSelectFamilyAttemptTimeout(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { +pub fn getDefaultAutoSelectFamilyAttemptTimeout(global: *JSC.JSGlobalObject) JSC.JSValue { return JSC.JSFunction.create(global, "getDefaultAutoSelectFamilyAttemptTimeout", (struct { - fn getter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + fn getter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { _ = globalThis; _ = callframe; return JSC.jsNumber(autoSelectFamilyAttemptTimeoutDefault); @@ -56,9 +56,9 @@ pub fn getDefaultAutoSelectFamilyAttemptTimeout(global: *JSC.JSGlobalObject) cal }).getter, 0, .{}); } -pub fn setDefaultAutoSelectFamilyAttemptTimeout(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { +pub fn setDefaultAutoSelectFamilyAttemptTimeout(global: *JSC.JSGlobalObject) JSC.JSValue { return JSC.JSFunction.create(global, "setDefaultAutoSelectFamilyAttemptTimeout", (struct { - fn setter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + fn setter(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(1); if (arguments.len < 1) { globalThis.throw("missing argument", .{}); diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index 54e2ae5379244..0c4dd970e081e 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -11,7 +11,7 @@ const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false; const libuv = bun.windows.libuv; pub const OS = struct { - pub fn create(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn create(globalObject: *JSC.JSGlobalObject) JSC.JSValue { const module = JSC.JSValue.createEmptyObject(globalObject, 16); module.put(globalObject, JSC.ZigString.static("cpus"), JSC.NewFunction(globalObject, JSC.ZigString.static("cpus"), 0, cpus, true)); @@ -51,7 +51,7 @@ pub const OS = struct { } }; - pub fn cpus(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn cpus(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); return switch (Environment.os) { @@ -62,11 +62,11 @@ pub const OS = struct { } catch { const err = JSC.SystemError{ .message = bun.String.static("Failed to get cpu information"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static(@tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR)), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; }; } @@ -140,7 +140,7 @@ pub const OS = struct { if (strings.hasPrefixComptime(line, key_processor)) { if (!has_model_name) { const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toValue(globalThis)); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); } // If this line starts a new processor, parse the index from the line const digits = std.mem.trim(u8, line[key_processor.len..], " \t\n"); @@ -151,19 +151,19 @@ pub const OS = struct { // If this is the model name, extract it and store on the current cpu const model_name = line[key_model_name.len..]; const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(model_name).withEncoding().toValueGC(globalThis)); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(model_name).withEncoding().toJS(globalThis)); has_model_name = true; } } if (!has_model_name) { const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toValue(globalThis)); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); } } else |_| { // Initialize model name to "unknown" var it = values.arrayIterator(globalThis); while (it.next()) |cpu| { - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toValue(globalThis)); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); } } @@ -224,7 +224,7 @@ pub const OS = struct { //NOTE: sysctlbyname doesn't update len if it was large enough, so we // still have to find the null terminator. All cpus can share the same // model name. - const model_name = JSC.ZigString.init(std.mem.sliceTo(&model_name_buf, 0)).withEncoding().toValueGC(globalThis); + const model_name = JSC.ZigString.init(std.mem.sliceTo(&model_name_buf, 0)).withEncoding().toJS(globalThis); // Get CPU speed var speed: u64 = 0; @@ -284,7 +284,7 @@ pub const OS = struct { }; const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(bun.span(cpu_info.model)).withEncoding().toValueGC(globalThis)); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(bun.span(cpu_info.model)).withEncoding().toJS(globalThis)); cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(cpu_info.speed)); cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); @@ -294,33 +294,30 @@ pub const OS = struct { return values; } - pub fn endianness(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn endianness(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); - return JSC.ZigString.init("LE").withEncoding().toValue(globalThis); + return JSC.ZigString.init("LE").withEncoding().toJS(globalThis); } - pub fn freemem(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn freemem(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); return JSC.JSValue.jsNumberFromUint64(C.getFreeMemory()); } - pub fn getPriority(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn getPriority(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); var args_ = callframe.arguments(1); const arguments: []const JSC.JSValue = args_.ptr[0..args_.len]; if (arguments.len > 0 and !arguments[0].isNumber()) { - const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + globalThis.ERR_INVALID_ARG_TYPE( "getPriority() expects a number", .{}, - globalThis, - ); - globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + ).throw(); + return .undefined; } const pid = if (arguments.len > 0) arguments[0].asInt32() else 0; @@ -335,20 +332,20 @@ pub const OS = struct { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: uv_os_getpriority returned ESRCH (no such process)"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), //.info = info, .errno = -3, .syscall = bun.String.static("uv_os_getpriority"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; } return JSC.JSValue.jsNumberFromInt32(priority); } - pub fn homedir(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn homedir(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); const dir: []const u8 = brk: { @@ -363,10 +360,10 @@ pub const OS = struct { break :brk "unknown"; }; - return JSC.ZigString.init(dir).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(dir).withEncoding().toJS(globalThis); } - pub fn hostname(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn hostname(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); if (comptime Environment.isWindows) { @@ -386,15 +383,15 @@ pub const OS = struct { } } - return JSC.ZigString.init("unknown").withEncoding().toValueGC(globalThis); + return JSC.ZigString.init("unknown").withEncoding().toJS(globalThis); } var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; - return JSC.ZigString.init(std.posix.gethostname(&name_buffer) catch "unknown").withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(std.posix.gethostname(&name_buffer) catch "unknown").withEncoding().toJS(globalThis); } - pub fn loadavg(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn loadavg(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); const result = C.getSystemLoadavg(); @@ -405,7 +402,7 @@ pub const OS = struct { }); } - pub fn networkInterfaces(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn networkInterfaces(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { return switch (Environment.os) { .windows => networkInterfacesWindows(globalThis), else => networkInterfacesPosix(globalThis), @@ -419,13 +416,13 @@ pub const OS = struct { if (rc != 0) { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: getifaddrs returned an error"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), .errno = @intFromEnum(std.posix.errno(rc)), .syscall = bun.String.static("getifaddrs"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; } defer C.freeifaddrs(interface_start); @@ -506,10 +503,10 @@ pub const OS = struct { const suffix_str = std.fmt.bufPrint(buf[start + addr_str.len ..], "/{}", .{suffix}) catch unreachable; // The full cidr value is the address + the suffix const cidr_str = buf[start .. start + addr_str.len + suffix_str.len]; - cidr = JSC.ZigString.init(cidr_str).withEncoding().toValueGC(globalThis); + cidr = JSC.ZigString.init(cidr_str).withEncoding().toJS(globalThis); } - interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toJS(globalThis)); interface.put(globalThis, JSC.ZigString.static("cidr"), cidr); } @@ -517,7 +514,7 @@ pub const OS = struct { { var buf: [64]u8 = undefined; const str = bun.fmt.formatIp(netmask, &buf) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toJS(globalThis)); } // family Either IPv4 or IPv6 @@ -525,7 +522,7 @@ pub const OS = struct { std.posix.AF.INET => JSC.ZigString.static("IPv4"), std.posix.AF.INET6 => JSC.ZigString.static("IPv6"), else => JSC.ZigString.static("unknown"), - }).toValueGC(globalThis)); + }).toJS(globalThis)); // mac The MAC address of the network interface { @@ -556,17 +553,17 @@ pub const OS = struct { const addr_data = if (comptime Environment.isLinux) ll_addr.addr else if (comptime Environment.isMac) ll_addr.sdl_data[ll_addr.sdl_nlen..] else @compileError("unreachable"); if (addr_data.len < 6) { const mac = "00:00:00:00:00:00"; - interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); } else { const mac = std.fmt.bufPrint(&mac_buf, "{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}", .{ addr_data[0], addr_data[1], addr_data[2], addr_data[3], addr_data[4], addr_data[5], }) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); } } else { const mac = "00:00:00:00:00:00"; - interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); } } @@ -602,7 +599,7 @@ pub const OS = struct { if (err != 0) { const sys_err = JSC.SystemError{ .message = bun.String.static("uv_interface_addresses failed"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), //.info = info, .errno = err, .syscall = bun.String.static("uv_interface_addresses"), @@ -648,10 +645,10 @@ pub const OS = struct { const suffix_str = std.fmt.bufPrint(ip_buf[start + addr_str.len ..], "/{}", .{suffix}) catch unreachable; // The full cidr value is the address + the suffix const cidr_str = ip_buf[start .. start + addr_str.len + suffix_str.len]; - cidr = JSC.ZigString.init(cidr_str).withEncoding().toValueGC(globalThis); + cidr = JSC.ZigString.init(cidr_str).withEncoding().toJS(globalThis); } - interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toJS(globalThis)); } // netmask @@ -661,14 +658,14 @@ pub const OS = struct { std.net.Address.initPosix(@ptrCast(&iface.netmask.netmask4)), &ip_buf, ) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toJS(globalThis)); } // family interface.put(globalThis, JSC.ZigString.static("family"), (switch (iface.address.address4.family) { std.posix.AF.INET => JSC.ZigString.static("IPv4"), std.posix.AF.INET6 => JSC.ZigString.static("IPv6"), else => JSC.ZigString.static("unknown"), - }).toValueGC(globalThis)); + }).toJS(globalThis)); // mac { @@ -676,7 +673,7 @@ pub const OS = struct { const mac = std.fmt.bufPrint(&mac_buf, "{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}", .{ phys[0], phys[1], phys[2], phys[3], phys[4], phys[5], }) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toValueGC(globalThis)); + interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); } // internal @@ -710,19 +707,19 @@ pub const OS = struct { return ret; } - pub fn platform(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn platform(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); - return JSC.ZigString.init(Global.os_name).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(Global.os_name).withEncoding().toJS(globalThis); } - pub fn release(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn release(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; - return JSC.ZigString.init(C.getRelease(&name_buffer)).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(C.getRelease(&name_buffer)).withEncoding().toJS(globalThis); } - pub fn setPriority(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setPriority(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); var args_ = callframe.arguments(2); @@ -730,13 +727,13 @@ pub const OS = struct { if (arguments.len == 0) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + .ERR_INVALID_ARG_TYPE, "The \"priority\" argument must be of type number. Received undefined", .{}, globalThis, ); globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } const pid = if (arguments.len == 2) arguments[0].coerce(i32, globalThis) else 0; @@ -744,13 +741,13 @@ pub const OS = struct { if (priority < -20 or priority > 19) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_OUT_OF_RANGE, + .ERR_OUT_OF_RANGE, "The value of \"priority\" is out of range. It must be >= -20 && <= 19", .{}, globalThis, ); globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } const errcode = C.setProcessPriority(pid, priority); @@ -758,60 +755,60 @@ pub const OS = struct { .SRCH => { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: uv_os_setpriority returned ESRCH (no such process)"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static(@tagName(.ERR_SYSTEM_ERROR)), //.info = info, .errno = -3, .syscall = bun.String.static("uv_os_setpriority"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; }, .ACCES => { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: uv_os_setpriority returned EACCESS (permission denied)"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static(@tagName(.ERR_SYSTEM_ERROR)), //.info = info, .errno = -13, .syscall = bun.String.static("uv_os_setpriority"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; }, else => {}, } - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn totalmem(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn totalmem(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); return JSC.JSValue.jsNumberFromUint64(C.getTotalMemory()); } - pub fn @"type"(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn @"type"(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); if (comptime Environment.isWindows) - return JSC.ZigString.static("Windows_NT").toValue(globalThis) + return JSC.ZigString.static("Windows_NT").toJS(globalThis) else if (comptime Environment.isMac) - return JSC.ZigString.static("Darwin").toValue(globalThis) + return JSC.ZigString.static("Darwin").toJS(globalThis) else if (comptime Environment.isLinux) - return JSC.ZigString.static("Linux").toValue(globalThis); + return JSC.ZigString.static("Linux").toJS(globalThis); - return JSC.ZigString.init(Global.os_name).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(Global.os_name).withEncoding().toJS(globalThis); } - pub fn uptime(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn uptime(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { if (Environment.isWindows) { var uptime_value: f64 = undefined; const err = libuv.uv_uptime(&uptime_value); if (err != 0) { const sys_err = JSC.SystemError{ .message = bun.String.static("failed to get system uptime"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), .errno = err, .syscall = bun.String.static("uv_uptime"), }; @@ -824,21 +821,21 @@ pub const OS = struct { return JSC.JSValue.jsNumberFromUint64(C.getSystemUptime()); } - pub fn userInfo(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn userInfo(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const result = JSC.JSValue.createEmptyObject(globalThis, 5); result.put(globalThis, JSC.ZigString.static("homedir"), homedir(globalThis, callframe)); if (comptime Environment.isWindows) { - result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(bun.getenvZ("USERNAME") orelse "unknown").withEncoding().toValueGC(globalThis)); + result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(bun.getenvZ("USERNAME") orelse "unknown").withEncoding().toJS(globalThis)); result.put(globalThis, JSC.ZigString.static("uid"), JSC.JSValue.jsNumber(-1)); result.put(globalThis, JSC.ZigString.static("gid"), JSC.JSValue.jsNumber(-1)); result.put(globalThis, JSC.ZigString.static("shell"), JSC.JSValue.jsNull()); } else { const username = bun.getenvZ("USER") orelse "unknown"; - result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(username).withEncoding().toValueGC(globalThis)); - result.put(globalThis, JSC.ZigString.static("shell"), JSC.ZigString.init(bun.getenvZ("SHELL") orelse "unknown").withEncoding().toValueGC(globalThis)); + result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(username).withEncoding().toJS(globalThis)); + result.put(globalThis, JSC.ZigString.static("shell"), JSC.ZigString.init(bun.getenvZ("SHELL") orelse "unknown").withEncoding().toJS(globalThis)); result.put(globalThis, JSC.ZigString.static("uid"), JSC.JSValue.jsNumber(C.getuid())); result.put(globalThis, JSC.ZigString.static("gid"), JSC.JSValue.jsNumber(C.getgid())); @@ -847,13 +844,13 @@ pub const OS = struct { return result; } - pub fn version(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn version(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; - return JSC.ZigString.init(C.getVersion(&name_buffer)).withEncoding().toValueGC(globalThis); + return JSC.ZigString.init(C.getVersion(&name_buffer)).withEncoding().toJS(globalThis); } - inline fn getMachineName() []const u8 { + inline fn getMachineName() [:0]const u8 { return switch (@import("builtin").target.cpu.arch) { .arm => "arm", .aarch64 => "arm64", @@ -868,9 +865,9 @@ pub const OS = struct { }; } - pub fn machine(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn machine(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); - return JSC.ZigString.static(comptime getMachineName()).toValue(globalThis); + return JSC.ZigString.static(comptime getMachineName()).toJS(globalThis); } }; diff --git a/src/bun.js/node/node_util_binding.zig b/src/bun.js/node/node_util_binding.zig new file mode 100644 index 0000000000000..d58bac22db85b --- /dev/null +++ b/src/bun.js/node/node_util_binding.zig @@ -0,0 +1,108 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Environment = bun.Environment; +const JSC = bun.JSC; +const string = bun.string; +const Output = bun.Output; +const ZigString = JSC.ZigString; +const uv = bun.windows.libuv; + +pub fn internalErrorName(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(1).slice(); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("internalErrorName", 1, arguments.len); + return .zero; + } + + const err_value = arguments[0]; + const err_int = err_value.toInt32(); + + if (err_int == -4095) return ZigString.static("EOF").toJS(globalThis); + if (err_int == -4094) return ZigString.static("UNKNOWN").toJS(globalThis); + if (err_int == -3000) return ZigString.static("EAI_ADDRFAMILY").toJS(globalThis); + if (err_int == -3001) return ZigString.static("EAI_AGAIN").toJS(globalThis); + if (err_int == -3002) return ZigString.static("EAI_BADFLAGS").toJS(globalThis); + if (err_int == -3003) return ZigString.static("EAI_CANCELED").toJS(globalThis); + if (err_int == -3004) return ZigString.static("EAI_FAIL").toJS(globalThis); + if (err_int == -3005) return ZigString.static("EAI_FAMILY").toJS(globalThis); + if (err_int == -3006) return ZigString.static("EAI_MEMORY").toJS(globalThis); + if (err_int == -3007) return ZigString.static("EAI_NODATA").toJS(globalThis); + if (err_int == -3008) return ZigString.static("EAI_NONAME").toJS(globalThis); + if (err_int == -3009) return ZigString.static("EAI_OVERFLOW").toJS(globalThis); + if (err_int == -3010) return ZigString.static("EAI_SERVICE").toJS(globalThis); + if (err_int == -3011) return ZigString.static("EAI_SOCKTYPE").toJS(globalThis); + if (err_int == -3013) return ZigString.static("EAI_BADHINTS").toJS(globalThis); + if (err_int == -3014) return ZigString.static("EAI_PROTOCOL").toJS(globalThis); + + if (err_int == -bun.C.UV_E2BIG) return ZigString.static("E2BIG").toJS(globalThis); + if (err_int == -bun.C.UV_EACCES) return ZigString.static("EACCES").toJS(globalThis); + if (err_int == -bun.C.UV_EADDRINUSE) return ZigString.static("EADDRINUSE").toJS(globalThis); + if (err_int == -bun.C.UV_EADDRNOTAVAIL) return ZigString.static("EADDRNOTAVAIL").toJS(globalThis); + if (err_int == -bun.C.UV_EAFNOSUPPORT) return ZigString.static("EAFNOSUPPORT").toJS(globalThis); + if (err_int == -bun.C.UV_EAGAIN) return ZigString.static("EAGAIN").toJS(globalThis); + if (err_int == -bun.C.UV_EALREADY) return ZigString.static("EALREADY").toJS(globalThis); + if (err_int == -bun.C.UV_EBADF) return ZigString.static("EBADF").toJS(globalThis); + if (err_int == -bun.C.UV_EBUSY) return ZigString.static("EBUSY").toJS(globalThis); + if (err_int == -bun.C.UV_ECANCELED) return ZigString.static("ECANCELED").toJS(globalThis); + if (err_int == -bun.C.UV_ECHARSET) return ZigString.static("ECHARSET").toJS(globalThis); + if (err_int == -bun.C.UV_ECONNABORTED) return ZigString.static("ECONNABORTED").toJS(globalThis); + if (err_int == -bun.C.UV_ECONNREFUSED) return ZigString.static("ECONNREFUSED").toJS(globalThis); + if (err_int == -bun.C.UV_ECONNRESET) return ZigString.static("ECONNRESET").toJS(globalThis); + if (err_int == -bun.C.UV_EDESTADDRREQ) return ZigString.static("EDESTADDRREQ").toJS(globalThis); + if (err_int == -bun.C.UV_EEXIST) return ZigString.static("EEXIST").toJS(globalThis); + if (err_int == -bun.C.UV_EFAULT) return ZigString.static("EFAULT").toJS(globalThis); + if (err_int == -bun.C.UV_EHOSTUNREACH) return ZigString.static("EHOSTUNREACH").toJS(globalThis); + if (err_int == -bun.C.UV_EINTR) return ZigString.static("EINTR").toJS(globalThis); + if (err_int == -bun.C.UV_EINVAL) return ZigString.static("EINVAL").toJS(globalThis); + if (err_int == -bun.C.UV_EIO) return ZigString.static("EIO").toJS(globalThis); + if (err_int == -bun.C.UV_EISCONN) return ZigString.static("EISCONN").toJS(globalThis); + if (err_int == -bun.C.UV_EISDIR) return ZigString.static("EISDIR").toJS(globalThis); + if (err_int == -bun.C.UV_ELOOP) return ZigString.static("ELOOP").toJS(globalThis); + if (err_int == -bun.C.UV_EMFILE) return ZigString.static("EMFILE").toJS(globalThis); + if (err_int == -bun.C.UV_EMSGSIZE) return ZigString.static("EMSGSIZE").toJS(globalThis); + if (err_int == -bun.C.UV_ENAMETOOLONG) return ZigString.static("ENAMETOOLONG").toJS(globalThis); + if (err_int == -bun.C.UV_ENETDOWN) return ZigString.static("ENETDOWN").toJS(globalThis); + if (err_int == -bun.C.UV_ENETUNREACH) return ZigString.static("ENETUNREACH").toJS(globalThis); + if (err_int == -bun.C.UV_ENFILE) return ZigString.static("ENFILE").toJS(globalThis); + if (err_int == -bun.C.UV_ENOBUFS) return ZigString.static("ENOBUFS").toJS(globalThis); + if (err_int == -bun.C.UV_ENODEV) return ZigString.static("ENODEV").toJS(globalThis); + if (err_int == -bun.C.UV_ENOENT) return ZigString.static("ENOENT").toJS(globalThis); + if (err_int == -bun.C.UV_ENOMEM) return ZigString.static("ENOMEM").toJS(globalThis); + if (err_int == -bun.C.UV_ENONET) return ZigString.static("ENONET").toJS(globalThis); + if (err_int == -bun.C.UV_ENOSPC) return ZigString.static("ENOSPC").toJS(globalThis); + if (err_int == -bun.C.UV_ENOSYS) return ZigString.static("ENOSYS").toJS(globalThis); + if (err_int == -bun.C.UV_ENOTCONN) return ZigString.static("ENOTCONN").toJS(globalThis); + if (err_int == -bun.C.UV_ENOTDIR) return ZigString.static("ENOTDIR").toJS(globalThis); + if (err_int == -bun.C.UV_ENOTEMPTY) return ZigString.static("ENOTEMPTY").toJS(globalThis); + if (err_int == -bun.C.UV_ENOTSOCK) return ZigString.static("ENOTSOCK").toJS(globalThis); + if (err_int == -bun.C.UV_ENOTSUP) return ZigString.static("ENOTSUP").toJS(globalThis); + if (err_int == -bun.C.UV_EPERM) return ZigString.static("EPERM").toJS(globalThis); + if (err_int == -bun.C.UV_EPIPE) return ZigString.static("EPIPE").toJS(globalThis); + if (err_int == -bun.C.UV_EPROTO) return ZigString.static("EPROTO").toJS(globalThis); + if (err_int == -bun.C.UV_EPROTONOSUPPORT) return ZigString.static("EPROTONOSUPPORT").toJS(globalThis); + if (err_int == -bun.C.UV_EPROTOTYPE) return ZigString.static("EPROTOTYPE").toJS(globalThis); + if (err_int == -bun.C.UV_EROFS) return ZigString.static("EROFS").toJS(globalThis); + if (err_int == -bun.C.UV_ESHUTDOWN) return ZigString.static("ESHUTDOWN").toJS(globalThis); + if (err_int == -bun.C.UV_ESPIPE) return ZigString.static("ESPIPE").toJS(globalThis); + if (err_int == -bun.C.UV_ESRCH) return ZigString.static("ESRCH").toJS(globalThis); + if (err_int == -bun.C.UV_ETIMEDOUT) return ZigString.static("ETIMEDOUT").toJS(globalThis); + if (err_int == -bun.C.UV_ETXTBSY) return ZigString.static("ETXTBSY").toJS(globalThis); + if (err_int == -bun.C.UV_EXDEV) return ZigString.static("EXDEV").toJS(globalThis); + if (err_int == -bun.C.UV_EFBIG) return ZigString.static("EFBIG").toJS(globalThis); + if (err_int == -bun.C.UV_ENOPROTOOPT) return ZigString.static("ENOPROTOOPT").toJS(globalThis); + if (err_int == -bun.C.UV_ERANGE) return ZigString.static("ERANGE").toJS(globalThis); + if (err_int == -bun.C.UV_ENXIO) return ZigString.static("ENXIO").toJS(globalThis); + if (err_int == -bun.C.UV_EMLINK) return ZigString.static("EMLINK").toJS(globalThis); + if (err_int == -bun.C.UV_EHOSTDOWN) return ZigString.static("EHOSTDOWN").toJS(globalThis); + if (err_int == -bun.C.UV_EREMOTEIO) return ZigString.static("EREMOTEIO").toJS(globalThis); + if (err_int == -bun.C.UV_ENOTTY) return ZigString.static("ENOTTY").toJS(globalThis); + if (err_int == -bun.C.UV_EFTYPE) return ZigString.static("EFTYPE").toJS(globalThis); + if (err_int == -bun.C.UV_EILSEQ) return ZigString.static("EILSEQ").toJS(globalThis); + if (err_int == -bun.C.UV_EOVERFLOW) return ZigString.static("EOVERFLOW").toJS(globalThis); + if (err_int == -bun.C.UV_ESOCKTNOSUPPORT) return ZigString.static("ESOCKTNOSUPPORT").toJS(globalThis); + if (err_int == -bun.C.UV_ENODATA) return ZigString.static("ENODATA").toJS(globalThis); + if (err_int == -bun.C.UV_EUNATCH) return ZigString.static("EUNATCH").toJS(globalThis); + + const fmtstring = bun.String.createFormat("Unknown system error {d}", .{err_int}) catch bun.outOfMemory(); + return fmtstring.toJS(globalThis); +} diff --git a/src/bun.js/node/node_zlib_binding.zig b/src/bun.js/node/node_zlib_binding.zig index ee511d5167bc6..066174d695390 100644 --- a/src/bun.js/node/node_zlib_binding.zig +++ b/src/bun.js/node/node_zlib_binding.zig @@ -6,10 +6,65 @@ const string = bun.string; const Output = bun.Output; const ZigString = JSC.ZigString; -pub fn createBrotliEncoder(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - return JSC.JSFunction.create(global, "createBrotliEncoder", bun.JSC.API.BrotliEncoder.create, 3, .{}); -} +pub const createBrotliEncoder = bun.JSC.API.BrotliEncoder.create; + +pub const createBrotliDecoder = bun.JSC.API.BrotliDecoder.create; + +pub const createZlibEncoder = bun.JSC.API.ZlibEncoder.create; + +pub const createZlibDecoder = bun.JSC.API.ZlibDecoder.create; + +pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2).ptr; + + const data: ZigString.Slice = blk: { + const data: JSC.JSValue = arguments[0]; + var exceptionref: JSC.C.JSValueRef = null; + + if (data == .zero) { + return globalThis.throwInvalidArgumentTypeValue("data", "string or an instance of Buffer, TypedArray, or DataView", .undefined); + } + if (data.isString()) { + break :blk data.asString().toSlice(globalThis, bun.default_allocator); + } + const buffer: JSC.Buffer = JSC.Buffer.fromJS(globalThis, data, &exceptionref) orelse { + const ty_str = data.jsTypeString(globalThis).toSlice(globalThis, bun.default_allocator); + defer ty_str.deinit(); + globalThis.ERR_INVALID_ARG_TYPE("The \"data\" property must be an instance of Buffer, TypedArray, DataView, or ArrayBuffer. Received {s}", .{ty_str.slice()}).throw(); + return .zero; + }; + if (exceptionref) |ptr| { + globalThis.throwValue(JSC.JSValue.c(ptr)); + return .zero; + } + break :blk ZigString.Slice.fromUTF8NeverFree(buffer.slice()); + }; + defer data.deinit(); + + const value: u32 = blk: { + const value: JSC.JSValue = arguments[1]; + if (value == .zero) { + break :blk 0; + } + if (!value.isNumber()) { + return globalThis.throwInvalidArgumentTypeValue("value", "number", value); + } + const valuef = value.asNumber(); + const min = 0; + const max = std.math.maxInt(u32); + + if (@floor(valuef) != valuef) { + globalThis.ERR_OUT_OF_RANGE("The value of \"{s}\" is out of range. It must be an integer. Received {}", .{ "value", valuef }).throw(); + return .zero; + } + if (valuef < min or valuef > max) { + globalThis.ERR_OUT_OF_RANGE("The value of \"{s}\" is out of range. It must be >= {d} and <= {d}. Received {d}", .{ "value", min, max, valuef }).throw(); + return .zero; + } + break :blk @intFromFloat(valuef); + }; -pub fn createBrotliDecoder(global: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - return JSC.JSFunction.create(global, "createBrotliDecoder", bun.JSC.API.BrotliDecoder.create, 3, .{}); + // crc32 returns a u64 but the data will always be within a u32 range so the outer @intCast is always safe. + const slice_u8 = data.slice(); + return JSC.JSValue.jsNumber(@as(u32, @intCast(bun.zlib.crc32(value, slice_u8.ptr, @intCast(slice_u8.len))))); } diff --git a/src/bun.js/node/path.zig b/src/bun.js/node/path.zig new file mode 100644 index 0000000000000..ae3c5238cba4c --- /dev/null +++ b/src/bun.js/node/path.zig @@ -0,0 +1,2961 @@ +const bun = @import("root").bun; +const JSC = bun.JSC; +const std = @import("std"); +const windows = bun.windows; + +const Path = @This(); +const typeBaseNameT = bun.meta.typeBaseNameT; +const validators = @import("./util/validators.zig"); +const validateObject = validators.validateObject; +const validateString = validators.validateString; +// Allow on the stack: +// - 8 string slices +// - 3 path buffers +// - extra padding +const stack_fallback_size_large = 8 * @sizeOf([]const u8) + ((stack_fallback_size_small * 3) + 64); +const Syscall = bun.sys; +const strings = bun.strings; +const L = strings.literal; +const string = bun.string; +const Environment = bun.Environment; + +const PATH_MIN_WIDE = 4096; // 4 KB +const stack_fallback_size_small = switch (Environment.os) { + // Up to 4 KB, instead of MAX_PATH_BYTES which is 96 KB on Windows, ouch! + .windows => PATH_MIN_WIDE, + else => bun.MAX_PATH_BYTES, +}; + +/// Taken from Zig 0.11.0 zig/src/resinator/rc.zig +/// https://github.com/ziglang/zig/blob/776cd673f206099012d789fd5d05d49dd72b9faa/src/resinator/rc.zig#L266 +/// +/// Compares ASCII values case-insensitively, non-ASCII values are compared directly +fn eqlIgnoreCaseT(comptime T: type, a: []const T, b: []const T) bool { + if (T != u16) { + return bun.strings.eqlCaseInsensitiveASCII(a, b, true); + } +} + +/// Taken from Zig 0.11.0 zig/src/resinator/rc.zig +/// https://github.com/ziglang/zig/blob/776cd673f206099012d789fd5d05d49dd72b9faa/src/resinator/rc.zig#L266 +/// +/// Lowers ASCII values, non-ASCII values are returned directly +inline fn toLowerT(comptime T: type, a_c: T) T { + if (T != u16) { + return std.ascii.toLower(a_c); + } + return if (a_c < 128) @intCast(std.ascii.toLower(@intCast(a_c))) else a_c; +} + +fn MaybeBuf(comptime T: type) type { + return JSC.Node.Maybe([]T, Syscall.Error); +} + +fn MaybeSlice(comptime T: type) type { + return JSC.Node.Maybe([]const T, Syscall.Error); +} + +fn validatePathT(comptime T: type, comptime methodName: []const u8) void { + comptime switch (T) { + u8, u16 => return, + else => @compileError("Unsupported type for " ++ methodName ++ ": " ++ typeBaseNameT(T)), + }; +} + +const CHAR_BACKWARD_SLASH = '\\'; +const CHAR_COLON = ':'; +const CHAR_DOT = '.'; +const CHAR_FORWARD_SLASH = '/'; +const CHAR_QUESTION_MARK = '?'; + +const CHAR_STR_BACKWARD_SLASH = "\\"; +const CHAR_STR_FORWARD_SLASH = "/"; +const CHAR_STR_DOT = "."; + +const StringBuilder = @import("../../string_builder.zig"); + +const toJSString = JSC.JSValue.toJSString; + +/// Based on Node v21.6.1 path.parse: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L919 +/// The structs returned by parse methods. +fn PathParsed(comptime T: type) type { + return struct { + root: []const T = "", + dir: []const T = "", + base: []const T = "", + ext: []const T = "", + name: []const T = "", + pub fn toJSObject(this: @This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + var jsObject = JSC.JSValue.createEmptyObject(globalObject, 5); + jsObject.put(globalObject, JSC.ZigString.static("root"), toJSString(globalObject, this.root)); + jsObject.put(globalObject, JSC.ZigString.static("dir"), toJSString(globalObject, this.dir)); + jsObject.put(globalObject, JSC.ZigString.static("base"), toJSString(globalObject, this.base)); + jsObject.put(globalObject, JSC.ZigString.static("ext"), toJSString(globalObject, this.ext)); + jsObject.put(globalObject, JSC.ZigString.static("name"), toJSString(globalObject, this.name)); + return jsObject; + } + }; +} + +pub fn MAX_PATH_SIZE(comptime T: type) usize { + return if (T == u16) windows.PATH_MAX_WIDE else bun.MAX_PATH_BYTES; +} + +pub fn PATH_SIZE(comptime T: type) usize { + return if (T == u16) PATH_MIN_WIDE else bun.MAX_PATH_BYTES; +} + +const Shimmer = @import("../bindings/shimmer.zig").Shimmer; +pub const shim = Shimmer("Bun", "Path", @This()); +pub const name = "Bun__Path"; +pub const include = "Path.h"; +pub const namespace = shim.namespace; +pub const sep_posix = CHAR_FORWARD_SLASH; +pub const sep_windows = CHAR_BACKWARD_SLASH; +pub const sep_str_posix = CHAR_STR_FORWARD_SLASH; +pub const sep_str_windows = CHAR_STR_BACKWARD_SLASH; + +/// Based on Node v21.6.1 private helper formatExt: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L130C10-L130C19 +inline fn formatExtT(comptime T: type, ext: []const T, buf: []T) []const T { + const len = ext.len; + if (len == 0) { + return &.{}; + } + if (ext[0] == CHAR_DOT) { + return ext; + } + const bufSize = len + 1; + buf[0] = CHAR_DOT; + bun.memmove(buf[1..bufSize], ext); + return buf[0..bufSize]; +} + +/// Based on Node v21.6.1 private helper posixCwd: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1074 +inline fn posixCwdT(comptime T: type, buf: []T) MaybeBuf(T) { + const cwd = switch (getCwdT(T, buf)) { + .result => |r| r, + .err => |e| return MaybeBuf(T){ .err = e }, + }; + const len = cwd.len; + if (len == 0) { + return MaybeBuf(T){ .result = cwd }; + } + if (comptime Environment.isWindows) { + // Converts Windows' backslash path separators to POSIX forward slashes + // and truncates any drive indicator + + // Translated from the following JS code: + // const cwd = StringPrototypeReplace(process.cwd(), regexp, '/'); + for (0..len) |i| { + if (cwd[i] == CHAR_BACKWARD_SLASH) { + buf[i] = CHAR_FORWARD_SLASH; + } else { + buf[i] = cwd[i]; + } + } + var normalizedCwd = buf[0..len]; + + // Translated from the following JS code: + // return StringPrototypeSlice(cwd, StringPrototypeIndexOf(cwd, '/')); + const index = std.mem.indexOfScalar(T, normalizedCwd, CHAR_FORWARD_SLASH); + // Account for the -1 case of String#slice in JS land + if (index) |_index| { + return MaybeBuf(T){ .result = normalizedCwd[_index..len] }; + } + return MaybeBuf(T){ .result = normalizedCwd[len - 1 .. len] }; + } + + // We're already on POSIX, no need for any transformations + return MaybeBuf(T){ .result = cwd }; +} + +pub fn getCwdWindowsU8(buf: []u8) MaybeBuf(u8) { + const u16Buf: bun.WPathBuffer = undefined; + switch (getCwdWindowsU16(&u16Buf)) { + .result => |r| { + // Handles conversion from UTF-16 to UTF-8 including surrogates ;) + const result = strings.convertUTF16ToUTF8InBuffer(&buf, r) catch { + return MaybeBuf(u8).errnoSys(0, Syscall.Tag.getcwd).?; + }; + return MaybeBuf(u8){ .result = result }; + }, + .err => |e| return MaybeBuf(u8){ .err = e }, + } +} + +pub fn getCwdWindowsU16(buf: []u16) MaybeBuf(u16) { + const len: u32 = windows.GetCurrentDirectoryW(buf.len, &buf); + if (len == 0) { + // Indirectly calls std.os.windows.kernel32.GetLastError(). + return MaybeBuf(u16).errnoSys(0, Syscall.Tag.getcwd).?; + } + return MaybeBuf(u16){ .result = buf[0..len] }; +} + +pub fn getCwdWindowsT(comptime T: type, buf: []T) MaybeBuf(T) { + comptime validatePathT(T, "getCwdWindowsT"); + return if (T == u16) + getCwdWindowsU16(buf) + else + getCwdWindowsU8(buf); +} + +pub fn getCwdU8(buf: []u8) MaybeBuf(u8) { + const cached_cwd = strings.withoutTrailingSlash(bun.fs.FileSystem.instance.top_level_dir); + @memcpy(buf[0..cached_cwd.len], cached_cwd); + return MaybeBuf(u8){ .result = buf[0..cached_cwd.len] }; +} + +pub fn getCwdU16(buf: []u16) MaybeBuf(u16) { + if (comptime Environment.isWindows) { + return getCwdWindowsU16(&buf); + } + const u8Buf: bun.PathBuffer = undefined; + const result = strings.convertUTF8toUTF16InBuffer(&buf, bun.getcwd(strings.convertUTF16ToUTF8InBuffer(&u8Buf, buf))) catch { + return MaybeBuf(u16).errnoSys(0, Syscall.Tag.getcwd).?; + }; + return MaybeBuf(u16){ .result = result }; +} + +pub fn getCwdT(comptime T: type, buf: []T) MaybeBuf(T) { + comptime validatePathT(T, "getCwdT"); + return if (T == u16) + getCwdU16(buf) + else + getCwdU8(buf); +} + +// Alias for naming consistency. +pub const getCwd = getCwdU8; + +/// Based on Node v21.6.1 path.posix.basename: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1309 +pub fn basenamePosixT(comptime T: type, path: []const T, suffix: ?[]const T) []const T { + comptime validatePathT(T, "basenamePosixT"); + + // validateString of `path` is performed in pub fn basename. + const len = path.len; + // Exit early for easier number type use. + if (len == 0) { + return &.{}; + } + var start: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash: bool = true; + + const _suffix = if (suffix) |_s| _s else &.{}; + const _suffixLen = _suffix.len; + if (suffix != null and _suffixLen > 0 and _suffixLen <= len) { + if (std.mem.eql(T, _suffix, path)) { + return &.{}; + } + // We use an optional value instead of -1, as in Node code, for easier number type use. + var extIdx: ?usize = _suffixLen - 1; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var firstNonSlashEnd: ?usize = null; + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 >= start) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (byte == CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd == null) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx) |_extIx| { + // Try to match the explicit extension + if (byte == _suffix[_extIx]) { + if (_extIx == 0) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + extIdx = null; + } else { + extIdx = _extIx - 1; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = null; + end = firstNonSlashEnd; + } + } + } + } + + if (end) |_end| { + if (start == _end) { + return path[start..firstNonSlashEnd.?]; + } else { + return path[start.._end]; + } + } + return path[start..len]; + } + + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 > -1) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (byte == CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end == null) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + return if (end) |_end| + path[start.._end] + else + &.{}; +} + +/// Based on Node v21.6.1 path.win32.basename: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L753 +pub fn basenameWindowsT(comptime T: type, path: []const T, suffix: ?[]const T) []const T { + comptime validatePathT(T, "basenameWindowsT"); + + // validateString of `path` is performed in pub fn basename. + const len = path.len; + // Exit early for easier number type use. + if (len == 0) { + return &.{}; + } + + const isSepT = isSepWindowsT; + + var start: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash: bool = true; + + // Check for a drive letter prefix so as not to mistake the following + // path separator as an extra separator at the end of the path that can be + // disregarded + if (len >= 2 and isWindowsDeviceRootT(T, path[0]) and path[1] == CHAR_COLON) { + start = 2; + } + + const _suffix = if (suffix) |_s| _s else &.{}; + const _suffixLen = _suffix.len; + if (suffix != null and _suffixLen > 0 and _suffixLen <= len) { + if (std.mem.eql(T, _suffix, path)) { + return &.{}; + } + // We use an optional value instead of -1, as in Node code, for easier number type use. + var extIdx: ?usize = _suffixLen - 1; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var firstNonSlashEnd: ?usize = null; + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 >= start) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (isSepT(T, byte)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd == null) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx) |_extIx| { + // Try to match the explicit extension + if (byte == _suffix[_extIx]) { + if (_extIx == 0) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + extIdx = null; + } else { + extIdx = _extIx - 1; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = null; + end = firstNonSlashEnd; + } + } + } + } + + if (end) |_end| { + if (start == _end) { + return path[start..firstNonSlashEnd.?]; + } else { + return path[start.._end]; + } + } + return path[start..len]; + } + + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 >= start) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (isSepT(T, byte)) { + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end == null) { + matchedSlash = false; + end = i + 1; + } + } + + return if (end) |_end| + path[start.._end] + else + &.{}; +} + +pub inline fn basenamePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, suffix: ?[]const T) JSC.JSValue { + return toJSString(globalObject, basenamePosixT(T, path, suffix)); +} + +pub inline fn basenameWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, suffix: ?[]const T) JSC.JSValue { + return toJSString(globalObject, basenameWindowsT(T, path, suffix)); +} + +pub inline fn basenameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T, suffix: ?[]const T) JSC.JSValue { + return if (isWindows) + basenameWindowsJS_T(T, globalObject, path, suffix) + else + basenamePosixJS_T(T, globalObject, path, suffix); +} + +pub fn basename(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const suffix_ptr: ?JSC.JSValue = if (args_len > 1 and args_ptr[1] != .undefined) args_ptr[1] else null; + + if (suffix_ptr) |_suffix_ptr| { + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, _suffix_ptr, "ext", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + } + + const path_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "path", .{}) catch { + return .zero; + }; + + const pathZStr = path_ptr.getZigString(globalObject); + if (pathZStr.len == 0) return path_ptr; + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + const pathZSlice = pathZStr.toSlice(allocator); + defer pathZSlice.deinit(); + + var suffixZSlice: ?JSC.ZigString.Slice = null; + if (suffix_ptr) |_suffix_ptr| { + const suffixZStr = _suffix_ptr.getZigString(globalObject); + if (suffixZStr.len > 0 and suffixZStr.len <= pathZStr.len) { + suffixZSlice = suffixZStr.toSlice(allocator); + } + } + defer if (suffixZSlice) |_s| _s.deinit(); + return basenameJS_T(u8, globalObject, isWindows, pathZSlice.slice(), if (suffixZSlice) |_s| _s.slice() else null); +} + +pub fn create(globalObject: *JSC.JSGlobalObject, isWindows: bool) callconv(JSC.conv) JSC.JSValue { + return shim.cppFn("create", .{ globalObject, isWindows }); +} + +/// Based on Node v21.6.1 path.posix.dirname: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1278 +pub fn dirnamePosixT(comptime T: type, path: []const T) []const T { + comptime validatePathT(T, "dirnamePosixT"); + + // validateString of `path` is performed in pub fn dirname. + const len = path.len; + if (len == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + const hasRoot = path[0] == CHAR_FORWARD_SLASH; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash: bool = true; + var i: usize = len - 1; + while (i >= 1) : (i -= 1) { + if (path[i] == CHAR_FORWARD_SLASH) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end) |_end| { + return if (hasRoot and _end == 1) + comptime L(T, "//") + else + path[0.._end]; + } + return if (hasRoot) + comptime L(T, CHAR_STR_FORWARD_SLASH) + else + comptime L(T, CHAR_STR_DOT); +} + +/// Based on Node v21.6.1 path.win32.dirname: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L657 +pub fn dirnameWindowsT(comptime T: type, path: []const T) []const T { + comptime validatePathT(T, "dirnameWindowsT"); + + // validateString of `path` is performed in pub fn dirname. + const len = path.len; + if (len == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + const isSepT = isSepWindowsT; + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var rootEnd: ?usize = null; + var offset: usize = 0; + const byte0 = path[0]; + + if (len == 1) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work or a dot. + return if (isSepT(T, byte0)) path else comptime L(T, CHAR_STR_DOT); + } + + // Try to match a root + if (isSepT(T, byte0)) { + // Possible UNC root + + rootEnd = 1; + offset = 1; + + if (isSepT(T, path[1])) { + // Matched double path separator at the beginning + var j: usize = 2; + var last: usize = j; + + // Match 1 or more non-path separators + while (j < len and !isSepT(T, path[j])) { + j += 1; + } + + if (j < len and j != last) { + // Matched! + last = j; + + // Match 1 or more path separators + while (j < len and isSepT(T, path[j])) { + j += 1; + } + + if (j < len and j != last) { + // Matched! + last = j; + + // Match 1 or more non-path separators + while (j < len and !isSepT(T, path[j])) { + j += 1; + } + + if (j == len) { + // We matched a UNC root only + return path; + } + + if (j != last) { + // We matched a UNC root with leftovers + + // Offset by 1 to include the separator after the UNC root to + // treat it as a "normal root" on top of a (UNC) root + offset = j + 1; + rootEnd = offset; + } + } + } + } + // Possible device root + } else if (isWindowsDeviceRootT(T, byte0) and path[1] == CHAR_COLON) { + offset = if (len > 2 and isSepT(T, path[2])) 3 else 2; + rootEnd = offset; + } + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash: bool = true; + + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 >= offset) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + if (isSepT(T, path[i])) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end) |_end| { + return path[0.._end]; + } + + return if (rootEnd) |_rootEnd| + path[0.._rootEnd] + else + comptime L(T, CHAR_STR_DOT); +} + +pub inline fn dirnamePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { + return toJSString(globalObject, dirnamePosixT(T, path)); +} + +pub inline fn dirnameWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { + return toJSString(globalObject, dirnameWindowsT(T, path)); +} + +pub inline fn dirnameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T) JSC.JSValue { + return if (isWindows) + dirnameWindowsJS_T(T, globalObject, path) + else + dirnamePosixJS_T(T, globalObject, path); +} + +pub fn dirname(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const path_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "path", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + + const pathZStr = path_ptr.getZigString(globalObject); + if (pathZStr.len == 0) return toJSString(globalObject, CHAR_STR_DOT); + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + const pathZSlice = pathZStr.toSlice(allocator); + defer pathZSlice.deinit(); + return dirnameJS_T(u8, globalObject, isWindows, pathZSlice.slice()); +} + +/// Based on Node v21.6.1 path.posix.extname: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1278 +pub fn extnamePosixT(comptime T: type, path: []const T) []const T { + comptime validatePathT(T, "extnamePosixT"); + + // validateString of `path` is performed in pub fn extname. + const len = path.len; + // Exit early for easier number type use. + if (len == 0) { + return &.{}; + } + // We use an optional value instead of -1, as in Node code, for easier number type use. + var startDot: ?usize = null; + var startPart: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash: bool = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var preDotState: ?usize = 0; + + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 > -1) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (byte == CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + + if (end == null) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + + if (byte == CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot == null) { + startDot = i; + } else if (preDotState != null and preDotState.? != 1) { + preDotState = 1; + } + } else if (startDot != null) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = null; + } + } + + const _end = if (end) |_e| _e else 0; + const _preDotState = if (preDotState) |_p| _p else 0; + const _startDot = if (startDot) |_s| _s else 0; + if (startDot == null or + end == null or + // We saw a non-dot character immediately before the dot + (preDotState != null and _preDotState == 0) or + // The (right-most) trimmed path component is exactly '..' + (_preDotState == 1 and + _startDot == _end - 1 and + _startDot == startPart + 1)) + { + return &.{}; + } + + return path[_startDot.._end]; +} + +/// Based on Node v21.6.1 path.win32.extname: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L840 +pub fn extnameWindowsT(comptime T: type, path: []const T) []const T { + comptime validatePathT(T, "extnameWindowsT"); + + // validateString of `path` is performed in pub fn extname. + const len = path.len; + // Exit early for easier number type use. + if (len == 0) { + return &.{}; + } + var start: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var startDot: ?usize = null; + var startPart: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash: bool = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var preDotState: ?usize = 0; + + // Check for a drive letter prefix so as not to mistake the following + // path separator as an extra separator at the end of the path that can be + // disregarded + + if (len >= 2 and + path[1] == CHAR_COLON and + isWindowsDeviceRootT(T, path[0])) + { + start = 2; + startPart = start; + } + + var i_i64 = @as(i64, @intCast(len - 1)); + while (i_i64 >= start) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (isSepWindowsT(T, byte)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end == null) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (byte == CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot == null) { + startDot = i; + } else if (preDotState) |_preDotState| { + if (_preDotState != 1) { + preDotState = 1; + } + } + } else if (startDot != null) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = null; + } + } + + const _end = if (end) |_e| _e else 0; + const _preDotState = if (preDotState) |_p| _p else 0; + const _startDot = if (startDot) |_s| _s else 0; + if (startDot == null or + end == null or + // We saw a non-dot character immediately before the dot + (preDotState != null and _preDotState == 0) or + // The (right-most) trimmed path component is exactly '..' + (_preDotState == 1 and + _startDot == _end - 1 and + _startDot == startPart + 1)) + { + return &.{}; + } + + return path[_startDot.._end]; +} + +pub inline fn extnamePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { + return toJSString(globalObject, extnamePosixT(T, path)); +} + +pub inline fn extnameWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { + return toJSString(globalObject, extnameWindowsT(T, path)); +} + +pub inline fn extnameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T) JSC.JSValue { + return if (isWindows) + extnameWindowsJS_T(T, globalObject, path) + else + extnamePosixJS_T(T, globalObject, path); +} + +pub fn extname(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const path_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "path", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + + const pathZStr = path_ptr.getZigString(globalObject); + if (pathZStr.len == 0) return path_ptr; + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + const pathZSlice = pathZStr.toSlice(allocator); + defer pathZSlice.deinit(); + return extnameJS_T(u8, globalObject, isWindows, pathZSlice.slice()); +} + +/// Based on Node v21.6.1 private helper _format: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L145 +fn _formatT(comptime T: type, pathObject: PathParsed(T), sep: T, buf: []T) []const T { + comptime validatePathT(T, "_formatT"); + + // validateObject of `pathObject` is performed in pub fn format. + const root = pathObject.root; + const dir = pathObject.dir; + const base = pathObject.base; + const ext = pathObject.ext; + // Prefix with _ to avoid shadowing the identifier in the outer scope. + const _name = pathObject.name; + + // Translated from the following JS code: + // const dir = pathObject.dir || pathObject.root; + const dirIsRoot = dir.len == 0 or std.mem.eql(u8, dir, root); + const dirOrRoot = if (dirIsRoot) root else dir; + const dirLen = dirOrRoot.len; + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + // Translated from the following JS code: + // const base = pathObject.base || + // `${pathObject.name || ''}${formatExt(pathObject.ext)}`; + var baseLen = base.len; + var baseOrNameExt = base; + if (baseLen > 0) { + bun.memmove(buf[0..baseLen], base); + } else { + const formattedExt = formatExtT(T, ext, buf); + const nameLen = _name.len; + const extLen = formattedExt.len; + bufOffset = nameLen; + bufSize = bufOffset + extLen; + if (extLen > 0) { + // Move all bytes to the right by _name.len. + // Use bun.copy because formattedExt and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], formattedExt); + } + if (nameLen > 0) { + bun.memmove(buf[0..nameLen], _name); + } + if (bufSize > 0) { + baseOrNameExt = buf[0..bufSize]; + } + } + + // Translated from the following JS code: + // if (!dir) { + // return base; + // } + if (dirLen == 0) { + return baseOrNameExt; + } + + // Translated from the following JS code: + // return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; + baseLen = baseOrNameExt.len; + if (baseLen > 0) { + bufOffset = if (dirIsRoot) dirLen else dirLen + 1; + bufSize = bufOffset + baseLen; + // Move all bytes to the right by dirLen + (maybe 1 for the separator). + // Use bun.copy because baseOrNameExt and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], baseOrNameExt); + } + bun.memmove(buf[0..dirLen], dirOrRoot); + bufSize = dirLen + baseLen; + if (!dirIsRoot) { + bufSize += 1; + buf[dirLen] = sep; + } + return buf[0..bufSize]; +} + +pub inline fn formatPosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, pathObject: PathParsed(T), buf: []T) JSC.JSValue { + return toJSString(globalObject, _formatT(T, pathObject, CHAR_FORWARD_SLASH, buf)); +} + +pub inline fn formatWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, pathObject: PathParsed(T), buf: []T) JSC.JSValue { + return toJSString(globalObject, _formatT(T, pathObject, CHAR_BACKWARD_SLASH, buf)); +} + +pub fn formatJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, pathObject: PathParsed(T)) JSC.JSValue { + const baseLen = pathObject.base.len; + const dirLen = pathObject.dir.len; + // Add one for the possible separator. + const bufLen: usize = @max(1 + + (if (dirLen > 0) dirLen else pathObject.root.len) + + (if (baseLen > 0) baseLen else pathObject.name.len + pathObject.ext.len), PATH_SIZE(T)); + const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf); + return if (isWindows) formatWindowsJS_T(T, globalObject, pathObject, buf) else formatPosixJS_T(T, globalObject, pathObject, buf); +} + +pub fn format(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const pathObject_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateObject(globalObject, pathObject_ptr, "pathObject", .{}, .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + var root: []const u8 = ""; + if (pathObject_ptr.getTruthy(globalObject, "root")) |jsValue| { + root = jsValue.toSlice(globalObject, allocator).slice(); + } + var dir: []const u8 = ""; + if (pathObject_ptr.getTruthy(globalObject, "dir")) |jsValue| { + dir = jsValue.toSlice(globalObject, allocator).slice(); + } + var base: []const u8 = ""; + if (pathObject_ptr.getTruthy(globalObject, "base")) |jsValue| { + base = jsValue.toSlice(globalObject, allocator).slice(); + } + // Prefix with _ to avoid shadowing the identifier in the outer scope. + var _name: []const u8 = ""; + if (pathObject_ptr.getTruthy(globalObject, "name")) |jsValue| { + _name = jsValue.toSlice(globalObject, allocator).slice(); + } + var ext: []const u8 = ""; + if (pathObject_ptr.getTruthy(globalObject, "ext")) |jsValue| { + ext = jsValue.toSlice(globalObject, allocator).slice(); + } + return formatJS_T(u8, globalObject, allocator, isWindows, .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }); +} + +/// Based on Node v21.6.1 path.posix.isAbsolute: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1159 +pub inline fn isAbsolutePosixT(comptime T: type, path: []const T) bool { + // validateString of `path` is performed in pub fn isAbsolute. + return path.len > 0 and path[0] == CHAR_FORWARD_SLASH; +} + +/// Based on Node v21.6.1 path.win32.isAbsolute: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L406 +pub fn isAbsoluteWindowsT(comptime T: type, path: []const T) bool { + // validateString of `path` is performed in pub fn isAbsolute. + const len = path.len; + if (len == 0) + return false; + + const byte0 = path[0]; + return isSepWindowsT(T, byte0) or + // Possible device root + (len > 2 and + isWindowsDeviceRootT(T, byte0) and + path[1] == CHAR_COLON and + isSepWindowsT(T, path[2])); +} + +pub fn isAbsolutePosixZigString(pathZStr: JSC.ZigString) bool { + const pathZStrTrunc = pathZStr.trunc(1); + return if (pathZStrTrunc.len > 0 and pathZStrTrunc.is16Bit()) + isAbsolutePosixT(u16, pathZStrTrunc.utf16SliceAligned()) + else + isAbsolutePosixT(u8, pathZStrTrunc.slice()); +} + +pub fn isAbsoluteWindowsZigString(pathZStr: JSC.ZigString) bool { + return if (pathZStr.len > 0 and pathZStr.is16Bit()) + isAbsoluteWindowsT(u16, @alignCast(pathZStr.utf16Slice())) + else + isAbsoluteWindowsT(u8, pathZStr.slice()); +} + +pub fn isAbsolute(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const path_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "path", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + + const pathZStr = path_ptr.getZigString(globalObject); + if (pathZStr.len == 0) return JSC.JSValue.jsBoolean(false); + if (isWindows) return JSC.JSValue.jsBoolean(isAbsoluteWindowsZigString(pathZStr)); + return JSC.JSValue.jsBoolean(isAbsolutePosixZigString(pathZStr)); +} + +pub inline fn isSepPosixT(comptime T: type, byte: T) bool { + return byte == CHAR_FORWARD_SLASH; +} + +pub inline fn isSepWindowsT(comptime T: type, byte: T) bool { + return byte == CHAR_FORWARD_SLASH or byte == CHAR_BACKWARD_SLASH; +} + +/// Based on Node v21.6.1 private helper isWindowsDeviceRoot: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L60C10-L60C29 +pub inline fn isWindowsDeviceRootT(comptime T: type, byte: T) bool { + return (byte >= 'A' and byte <= 'Z') or (byte >= 'a' and byte <= 'z'); +} + +/// Based on Node v21.6.1 path.posix.join: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1169 +pub inline fn joinPosixT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) []const T { + comptime validatePathT(T, "joinPosixT"); + + if (paths.len == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + var bufSize: usize = 0; + var bufOffset: usize = 0; + + // Back joined by expandable buf2 in case it is long. + var joined: []const T = &.{}; + + for (paths) |path| { + // validateString of `path is performed in pub fn join. + // Back our virtual "joined" string by expandable buf2 in + // case it is long. + const len = path.len; + if (len > 0) { + // Translated from the following JS code: + // if (joined === undefined) + // joined = arg; + // else + // joined += `/${arg}`; + if (bufSize != 0) { + bufOffset = bufSize; + bufSize += 1; + buf2[bufOffset] = CHAR_FORWARD_SLASH; + } + bufOffset = bufSize; + bufSize += len; + bun.memmove(buf2[bufOffset..bufSize], path); + + joined = buf2[0..bufSize]; + } + } + if (bufSize == 0) { + return comptime L(T, CHAR_STR_DOT); + } + return normalizePosixT(T, joined, buf); +} + +/// Based on Node v21.6.1 path.win32.join: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L425 +pub fn joinWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) []const T { + comptime validatePathT(T, "joinWindowsT"); + + if (paths.len == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + const isSepT = isSepWindowsT; + + var bufSize: usize = 0; + var bufOffset: usize = 0; + + // Backed by expandable buf2 in case it is long. + var joined: []const T = &.{}; + var firstPart: []const T = &.{}; + + for (paths) |path| { + // validateString of `path` is performed in pub fn join. + const len = path.len; + if (len > 0) { + // Translated from the following JS code: + // if (joined === undefined) + // joined = firstPart = arg; + // else + // joined += `\\${arg}`; + bufOffset = bufSize; + if (bufSize == 0) { + bufSize = len; + bun.memmove(buf2[0..bufSize], path); + + joined = buf2[0..bufSize]; + firstPart = joined; + } else { + bufOffset = bufSize; + bufSize += 1; + buf2[bufOffset] = CHAR_BACKWARD_SLASH; + bufOffset = bufSize; + bufSize += len; + bun.memmove(buf2[bufOffset..bufSize], path); + + joined = buf2[0..bufSize]; + } + } + } + if (bufSize == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + // Make sure that the joined path doesn't start with two slashes, because + // normalize() will mistake it for a UNC path then. + // + // This step is skipped when it is very clear that the user actually + // intended to point at a UNC path. This is assumed when the first + // non-empty string arguments starts with exactly two slashes followed by + // at least one more non-slash character. + // + // Note that for normalize() to treat a path as a UNC path it needs to + // have at least 2 components, so we don't filter for that here. + // This means that the user can use join to construct UNC paths from + // a server name and a share name; for example: + // path.join('//server', 'share') -> '\\\\server\\share\\') + var needsReplace: bool = true; + var slashCount: usize = 0; + if (isSepT(T, firstPart[0])) { + slashCount += 1; + const firstLen = firstPart.len; + if (firstLen > 1 and + isSepT(T, firstPart[1])) + { + slashCount += 1; + if (firstLen > 2) { + if (isSepT(T, firstPart[2])) { + slashCount += 1; + } else { + // We matched a UNC path in the first part + needsReplace = false; + } + } + } + } + if (needsReplace) { + // Find any more consecutive slashes we need to replace + while (slashCount < bufSize and + isSepT(T, joined[slashCount])) + { + slashCount += 1; + } + // Replace the slashes if needed + if (slashCount >= 2) { + // Translated from the following JS code: + // joined = `\\${StringPrototypeSlice(joined, slashCount)}`; + bufOffset = 1; + bufSize = bufOffset + (bufSize - slashCount); + // Move all bytes to the right by slashCount - 1. + // Use bun.copy because joined and buf2 overlap. + bun.copy(u8, buf2[bufOffset..bufSize], joined[slashCount..]); + // Prepend the separator. + buf2[0] = CHAR_BACKWARD_SLASH; + + joined = buf2[0..bufSize]; + } + } + return normalizeWindowsT(T, joined, buf); +} + +pub inline fn joinPosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { + return toJSString(globalObject, joinPosixT(T, paths, buf, buf2)); +} + +pub inline fn joinWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { + return toJSString(globalObject, joinWindowsT(T, paths, buf, buf2)); +} + +pub fn joinJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, paths: []const []const T) JSC.JSValue { + // Adding 8 bytes when Windows for the possible UNC root. + var bufLen: usize = if (isWindows) 8 else 0; + for (paths) |path| bufLen += if (bufLen > 0 and path.len > 0) path.len + 1 else path.len; + bufLen = @max(bufLen, PATH_SIZE(T)); + const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf); + const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf2); + return if (isWindows) joinWindowsJS_T(T, globalObject, paths, buf, buf2) else joinPosixJS_T(T, globalObject, paths, buf, buf2); +} + +pub fn join(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + if (args_len == 0) return toJSString(globalObject, CHAR_STR_DOT); + + var arena = bun.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_large, arena.allocator()); + const allocator = stack_fallback.get(); + + var paths = allocator.alloc(string, args_len) catch bun.outOfMemory(); + defer allocator.free(paths); + + for (0..args_len, args_ptr) |i, path_ptr| { + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "paths[{d}]", .{i}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + const pathZStr = path_ptr.getZigString(globalObject); + paths[i] = if (pathZStr.len > 0) pathZStr.toSlice(allocator).slice() else ""; + } + return joinJS_T(u8, globalObject, allocator, isWindows, paths); +} + +/// Based on Node v21.6.1 private helper normalizeString: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L65C1-L66C77 +/// +/// Resolves . and .. elements in a path with directory names +fn normalizeStringT(comptime T: type, path: []const T, allowAboveRoot: bool, separator: T, comptime platform: bun.path.Platform, buf: []T) []const T { + const len = path.len; + const isSepT = + if (platform == .posix) + isSepPosixT + else + isSepWindowsT; + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + var res: []const T = &.{}; + var lastSegmentLength: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var lastSlash: ?usize = null; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var dots: ?usize = 0; + var byte: T = 0; + + var i: usize = 0; + while (i <= len) : (i += 1) { + if (i < len) { + byte = path[i]; + } else if (isSepT(T, byte)) { + break; + } else { + byte = CHAR_FORWARD_SLASH; + } + + if (isSepT(T, byte)) { + // Translated from the following JS code: + // if (lastSlash === i - 1 || dots === 1) { + if ((lastSlash == null and i == 0) or + (lastSlash != null and i > 0 and lastSlash.? == i - 1) or + (dots != null and dots.? == 1)) + { + // NOOP + } else if (dots != null and dots.? == 2) { + if (bufSize < 2 or + lastSegmentLength != 2 or + buf[bufSize - 1] != CHAR_DOT or + buf[bufSize - 2] != CHAR_DOT) + { + if (bufSize > 2) { + const lastSlashIndex = std.mem.lastIndexOfScalar(T, buf[0..bufSize], separator); + if (lastSlashIndex == null) { + res = &.{}; + bufSize = 0; + lastSegmentLength = 0; + } else { + bufSize = lastSlashIndex.?; + res = buf[0..bufSize]; + // Translated from the following JS code: + // lastSegmentLength = + // res.length - 1 - StringPrototypeLastIndexOf(res, separator); + const lastIndexOfSep = std.mem.lastIndexOfScalar(T, buf[0..bufSize], separator); + if (lastIndexOfSep == null) { + // Yes (>ლ), Node relies on the -1 result of + // StringPrototypeLastIndexOf(res, separator). + // A - -1 is a positive 1. + // So the code becomes + // lastSegmentLength = res.length - 1 + 1; + // or + // lastSegmentLength = res.length; + lastSegmentLength = bufSize; + } else { + lastSegmentLength = bufSize - 1 - lastIndexOfSep.?; + } + } + lastSlash = i; + dots = 0; + continue; + } else if (bufSize != 0) { + res = &.{}; + bufSize = 0; + lastSegmentLength = 0; + lastSlash = i; + dots = 0; + continue; + } + } + if (allowAboveRoot) { + // Translated from the following JS code: + // res += res.length > 0 ? `${separator}..` : '..'; + if (bufSize > 0) { + bufOffset = bufSize; + bufSize += 1; + buf[bufOffset] = separator; + bufOffset = bufSize; + bufSize += 2; + buf[bufOffset] = CHAR_DOT; + buf[bufOffset + 1] = CHAR_DOT; + } else { + bufSize = 2; + buf[0] = CHAR_DOT; + buf[1] = CHAR_DOT; + } + + res = buf[0..bufSize]; + lastSegmentLength = 2; + } + } else { + // Translated from the following JS code: + // if (res.length > 0) + // res += `${separator}${StringPrototypeSlice(path, lastSlash + 1, i)}`; + // else + // res = StringPrototypeSlice(path, lastSlash + 1, i); + if (bufSize > 0) { + bufOffset = bufSize; + bufSize += 1; + buf[bufOffset] = separator; + } + const sliceStart = if (lastSlash != null) lastSlash.? + 1 else 0; + const slice = path[sliceStart..i]; + + bufOffset = bufSize; + bufSize += slice.len; + bun.memmove(buf[bufOffset..bufSize], slice); + + res = buf[0..bufSize]; + + // Translated from the following JS code: + // lastSegmentLength = i - lastSlash - 1; + const subtract = if (lastSlash != null) lastSlash.? + 1 else 2; + lastSegmentLength = if (i >= subtract) i - subtract else 0; + } + lastSlash = i; + dots = 0; + continue; + } else if (byte == CHAR_DOT and dots != null) { + dots = if (dots != null) dots.? + 1 else 0; + continue; + } else { + dots = null; + } + } + + return res; +} + +/// Based on Node v21.6.1 path.posix.normalize +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1130 +pub fn normalizePosixT(comptime T: type, path: []const T, buf: []T) []const T { + comptime validatePathT(T, "normalizePosixT"); + + // validateString of `path` is performed in pub fn normalize. + const len = path.len; + if (len == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + // Prefix with _ to avoid shadowing the identifier in the outer scope. + const _isAbsolute = path[0] == CHAR_FORWARD_SLASH; + const trailingSeparator = path[len - 1] == CHAR_FORWARD_SLASH; + + // Normalize the path + var normalizedPath = normalizeStringT(T, path, !_isAbsolute, CHAR_FORWARD_SLASH, .posix, buf); + + var bufSize: usize = normalizedPath.len; + if (bufSize == 0) { + if (_isAbsolute) { + return comptime L(T, CHAR_STR_FORWARD_SLASH); + } + return if (trailingSeparator) + comptime L(T, "./") + else + comptime L(T, CHAR_STR_DOT); + } + + var bufOffset: usize = 0; + + // Translated from the following JS code: + // if (trailingSeparator) + // path += '/'; + if (trailingSeparator) { + bufOffset = bufSize; + bufSize += 1; + buf[bufOffset] = CHAR_FORWARD_SLASH; + normalizedPath = buf[0..bufSize]; + } + + // Translated from the following JS code: + // return isAbsolute ? `/${path}` : path; + if (_isAbsolute) { + bufOffset = 1; + bufSize += 1; + // Move all bytes to the right by 1 for the separator. + // Use bun.copy because normalizedPath and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], normalizedPath); + // Prepend the separator. + buf[0] = CHAR_FORWARD_SLASH; + normalizedPath = buf[0..bufSize]; + } + return normalizedPath[0..bufSize]; +} + +/// Based on Node v21.6.1 path.win32.normalize +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L308 +pub fn normalizeWindowsT(comptime T: type, path: []const T, buf: []T) []const T { + comptime validatePathT(T, "normalizeWindowsT"); + + // validateString of `path` is performed in pub fn normalize. + const len = path.len; + if (len == 0) { + return comptime L(T, CHAR_STR_DOT); + } + + const isSepT = isSepWindowsT; + + // Moved `rootEnd`, `device`, and `_isAbsolute` initialization after + // the `if (len == 1)` check. + const byte0: T = path[0]; + + // Try to match a root + if (len == 1) { + // `path` contains just a single char, exit early to avoid + // unnecessary work + return if (isSepT(T, byte0)) comptime L(T, CHAR_STR_BACKWARD_SLASH) else path; + } + + var rootEnd: usize = 0; + // Backed by buf. + var device: ?[]const T = null; + // Prefix with _ to avoid shadowing the identifier in the outer scope. + var _isAbsolute: bool = false; + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + if (isSepT(T, byte0)) { + // Possible UNC root + + // If we started with a separator, we know we at least have an absolute + // path of some kind (UNC or otherwise) + _isAbsolute = true; + + if (isSepT(T, path[1])) { + // Matched double path separator at beginning + var j: usize = 2; + var last: usize = j; + // Match 1 or more non-path separators + while (j < len and + !isSepT(T, path[j])) + { + j += 1; + } + if (j < len and j != last) { + const firstPart: []const u8 = path[last..j]; + // Matched! + last = j; + // Match 1 or more path separators + while (j < len and + isSepT(T, path[j])) + { + j += 1; + } + if (j < len and j != last) { + // Matched! + last = j; + // Match 1 or more non-path separators + while (j < len and + !isSepT(T, path[j])) + { + j += 1; + } + if (j == len) { + // We matched a UNC root only + // Return the normalized version of the UNC root since there + // is nothing left to process + + // Translated from the following JS code: + // return `\\\\${firstPart}\\${StringPrototypeSlice(path, last)}\\`; + bufSize = 2; + buf[0] = CHAR_BACKWARD_SLASH; + buf[1] = CHAR_BACKWARD_SLASH; + bufOffset = bufSize; + bufSize += firstPart.len; + bun.memmove(buf[bufOffset..bufSize], firstPart); + bufOffset = bufSize; + bufSize += 1; + buf[bufOffset] = CHAR_BACKWARD_SLASH; + bufOffset = bufSize; + bufSize += len - last; + bun.memmove(buf[bufOffset..bufSize], path[last..len]); + bufOffset = bufSize; + bufSize += 1; + buf[bufOffset] = CHAR_BACKWARD_SLASH; + return buf[0..bufSize]; + } + if (j != last) { + // We matched a UNC root with leftovers + + // Translated from the following JS code: + // device = + // `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; + // rootEnd = j; + bufSize = 2; + buf[0] = CHAR_BACKWARD_SLASH; + buf[1] = CHAR_BACKWARD_SLASH; + bufOffset = bufSize; + bufSize += firstPart.len; + bun.memmove(buf[bufOffset..bufSize], firstPart); + bufOffset = bufSize; + bufSize += 1; + buf[bufOffset] = CHAR_BACKWARD_SLASH; + bufOffset = bufSize; + bufSize += j - last; + bun.memmove(buf[bufOffset..bufSize], path[last..j]); + + device = buf[0..bufSize]; + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRootT(T, byte0) and + path[1] == CHAR_COLON) + { + // Possible device root + buf[0] = byte0; + buf[1] = CHAR_COLON; + device = buf[0..2]; + rootEnd = 2; + if (len > 2 and isSepT(T, path[2])) { + // Treat separator following drive name as an absolute path + // indicator + _isAbsolute = true; + rootEnd = 3; + } + } + + bufOffset = (if (device) |_d| _d.len else 0) + @intFromBool(_isAbsolute); + // Backed by buf at an offset of device.len + 1 if _isAbsolute is true. + var tailLen = if (rootEnd < len) normalizeStringT(T, path[rootEnd..len], !_isAbsolute, CHAR_BACKWARD_SLASH, .windows, buf[bufOffset..]).len else 0; + if (tailLen == 0 and !_isAbsolute) { + buf[bufOffset] = CHAR_DOT; + tailLen = 1; + } + + if (tailLen > 0 and + isSepT(T, path[len - 1])) + { + // Translated from the following JS code: + // tail += '\\'; + buf[bufOffset + tailLen] = CHAR_BACKWARD_SLASH; + tailLen += 1; + } + + bufSize = bufOffset + tailLen; + // Translated from the following JS code: + // if (device === undefined) { + // return isAbsolute ? `\\${tail}` : tail; + // } + // return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; + if (_isAbsolute) { + bufOffset -= 1; + // Prepend the separator. + buf[bufOffset] = CHAR_BACKWARD_SLASH; + } + return buf[0..bufSize]; +} + +pub fn normalizeT(comptime T: type, path: []const T, buf: []T) []const T { + return switch (Environment.os) { + .windows => normalizeWindowsT(T, path, buf), + else => normalizePosixT(T, path, buf), + }; +} + +pub inline fn normalizePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, buf: []T) JSC.JSValue { + return toJSString(globalObject, normalizePosixT(T, path, buf)); +} + +pub inline fn normalizeWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, buf: []T) JSC.JSValue { + return toJSString(globalObject, normalizeWindowsT(T, path, buf)); +} + +pub fn normalizeJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, path: []const T) JSC.JSValue { + const bufLen = @max(path.len, PATH_SIZE(T)); + const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf); + return if (isWindows) normalizeWindowsJS_T(T, globalObject, path, buf) else normalizePosixJS_T(T, globalObject, path, buf); +} + +pub fn normalize(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const path_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "path", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + const pathZStr = path_ptr.getZigString(globalObject); + const len = pathZStr.len; + if (len == 0) return toJSString(globalObject, CHAR_STR_DOT); + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + const pathZSlice = pathZStr.toSlice(allocator); + defer pathZSlice.deinit(); + return normalizeJS_T(u8, globalObject, allocator, isWindows, pathZSlice.slice()); +} + +// Based on Node v21.6.1 path.posix.parse +// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1452 +pub fn parsePosixT(comptime T: type, path: []const T) PathParsed(T) { + comptime validatePathT(T, "parsePosixT"); + + // validateString of `path` is performed in pub fn parse. + const len = path.len; + if (len == 0) { + return .{}; + } + + var root: []const T = &.{}; + var dir: []const T = &.{}; + var base: []const T = &.{}; + var ext: []const T = &.{}; + // Prefix with _ to avoid shadowing the identifier in the outer scope. + var _name: []const T = &.{}; + // Prefix with _ to avoid shadowing the identifier in the outer scope. + const _isAbsolute = path[0] == CHAR_FORWARD_SLASH; + var start: usize = 0; + if (_isAbsolute) { + root = comptime L(T, CHAR_STR_FORWARD_SLASH); + start = 1; + } + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var startDot: ?usize = null; + var startPart: usize = 0; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash = true; + var i_i64 = @as(i64, @intCast(len - 1)); + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var preDotState: ?usize = 0; + + // Get non-dir info + while (i_i64 >= start) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + const byte = path[i]; + if (byte == CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end == null) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (byte == CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot == null) { + startDot = i; + } else if (preDotState) |_preDotState| { + if (_preDotState != 1) { + preDotState = 1; + } + } + } else if (startDot != null) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = null; + } + } + + if (end) |_end| { + const _preDotState = if (preDotState) |_p| _p else 0; + const _startDot = if (startDot) |_s| _s else 0; + start = if (startPart == 0 and _isAbsolute) 1 else startPart; + if (startDot == null or + // We saw a non-dot character immediately before the dot + (preDotState != null and _preDotState == 0) or + // The (right-most) trimmed path component is exactly '..' + (_preDotState == 1 and + _startDot == _end - 1 and + _startDot == startPart + 1)) + { + _name = path[start.._end]; + base = _name; + } else { + _name = path[start.._startDot]; + base = path[start.._end]; + ext = path[_startDot.._end]; + } + } + + if (startPart > 0) { + dir = path[0..(startPart - 1)]; + } else if (_isAbsolute) { + dir = comptime L(T, CHAR_STR_FORWARD_SLASH); + } + + return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; +} + +// Based on Node v21.6.1 path.win32.parse +// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L916 +pub fn parseWindowsT(comptime T: type, path: []const T) PathParsed(T) { + comptime validatePathT(T, "parseWindowsT"); + + // validateString of `path` is performed in pub fn parse. + var root: []const T = &.{}; + var dir: []const T = &.{}; + var base: []const T = &.{}; + var ext: []const T = &.{}; + // Prefix with _ to avoid shadowing the identifier in the outer scope. + var _name: []const T = &.{}; + + const len = path.len; + if (len == 0) { + return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; + } + + const isSepT = isSepWindowsT; + + var rootEnd: usize = 0; + var byte = path[0]; + + if (len == 1) { + if (isSepT(T, byte)) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work + root = path; + dir = path; + } else { + base = path; + _name = path; + } + return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; + } + + // Try to match a root + if (isSepT(T, byte)) { + // Possible UNC root + + rootEnd = 1; + if (isSepT(T, path[1])) { + // Matched double path separator at the beginning + var j: usize = 2; + var last: usize = j; + // Match 1 or more non-path separators + while (j < len and + !isSepT(T, path[j])) + { + j += 1; + } + if (j < len and j != last) { + // Matched! + last = j; + // Match 1 or more path separators + while (j < len and + isSepT(T, path[j])) + { + j += 1; + } + if (j < len and j != last) { + // Matched! + last = j; + // Match 1 or more non-path separators + while (j < len and + !isSepT(T, path[j])) + { + j += 1; + } + if (j == len) { + // We matched a UNC root only + rootEnd = j; + } else if (j != last) { + // We matched a UNC root with leftovers + rootEnd = j + 1; + } + } + } + } + } else if (isWindowsDeviceRootT(T, byte) and + path[1] == CHAR_COLON) + { + // Possible device root + if (len <= 2) { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + root = path; + dir = path; + return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; + } + rootEnd = 2; + if (isSepT(T, path[2])) { + if (len == 3) { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + root = path; + dir = path; + return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; + } + rootEnd = 3; + } + } + if (rootEnd > 0) { + root = path[0..rootEnd]; + } + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var startDot: ?usize = null; + var startPart = rootEnd; + // We use an optional value instead of -1, as in Node code, for easier number type use. + var end: ?usize = null; + var matchedSlash = true; + var i_i64 = @as(i64, @intCast(len - 1)); + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + + // We use an optional value instead of -1, as in Node code, for easier number type use. + var preDotState: ?usize = 0; + + // Get non-dir info + while (i_i64 >= rootEnd) : (i_i64 -= 1) { + const i = @as(usize, @intCast(i_i64)); + byte = path[i]; + if (isSepT(T, byte)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end == null) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (byte == CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot == null) { + startDot = i; + } else if (preDotState) |_preDotState| { + if (_preDotState != 1) { + preDotState = 1; + } + } + } else if (startDot != null) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = null; + } + } + + if (end) |_end| { + const _preDotState = if (preDotState) |_p| _p else 0; + const _startDot = if (startDot) |_s| _s else 0; + if (startDot == null or + // We saw a non-dot character immediately before the dot + (preDotState != null and _preDotState == 0) or + // The (right-most) trimmed path component is exactly '..' + (_preDotState == 1 and + _startDot == _end - 1 and + _startDot == startPart + 1)) + { + // Prefix with _ to avoid shadowing the identifier in the outer scope. + _name = path[startPart.._end]; + base = _name; + } else { + _name = path[startPart.._startDot]; + base = path[startPart.._end]; + ext = path[_startDot.._end]; + } + } + + // If the directory is the root, use the entire root as the `dir` including + // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the + // trailing slash (`C:\abc\def` -> `C:\abc`). + if (startPart > 0 and startPart != rootEnd) { + dir = path[0..(startPart - 1)]; + } else { + dir = root; + } + + return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; +} + +pub inline fn parsePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { + return parsePosixT(T, path).toJSObject(globalObject); +} + +pub inline fn parseWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { + return parseWindowsT(T, path).toJSObject(globalObject); +} + +pub inline fn parseJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T) JSC.JSValue { + return if (isWindows) parseWindowsJS_T(T, globalObject, path) else parsePosixJS_T(T, globalObject, path); +} + +pub fn parse(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const path_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "path", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + + const pathZStr = path_ptr.getZigString(globalObject); + if (pathZStr.len == 0) return (PathParsed(u8){}).toJSObject(globalObject); + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + const pathZSlice = pathZStr.toSlice(allocator); + defer pathZSlice.deinit(); + return parseJS_T(u8, globalObject, isWindows, pathZSlice.slice()); +} + +/// Based on Node v21.6.1 path.posix.relative: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1193 +pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) MaybeSlice(T) { + comptime validatePathT(T, "relativePosixT"); + + // validateString of `from` and `to` are performed in pub fn relative. + if (std.mem.eql(T, from, to)) { + return MaybeSlice(T){ .result = &.{} }; + } + + // Trim leading forward slashes. + // Backed by expandable buf2 because fromOrig may be long. + const fromOrig = switch (resolvePosixT(T, &.{from}, buf2, buf3)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + const fromOrigLen = fromOrig.len; + // Backed by buf. + const toOrig = switch (resolvePosixT(T, &.{to}, buf, buf3)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + + if (std.mem.eql(T, fromOrig, toOrig)) { + return MaybeSlice(T){ .result = &.{} }; + } + + const fromStart = 1; + const fromEnd = fromOrigLen; + const fromLen = fromEnd - fromStart; + const toOrigLen = toOrig.len; + var toStart: usize = 1; + const toLen = toOrigLen - toStart; + + // Compare paths to find the longest common path from root + const smallestLength = @min(fromLen, toLen); + // We use an optional value instead of -1, as in Node code, for easier number type use. + var lastCommonSep: ?usize = null; + + var matchesAllOfSmallest = false; + // Add a block to isolate `i`. + { + var i: usize = 0; + while (i < smallestLength) : (i += 1) { + const fromByte = fromOrig[fromStart + i]; + if (fromByte != toOrig[toStart + i]) { + break; + } else if (fromByte == CHAR_FORWARD_SLASH) { + lastCommonSep = i; + } + } + matchesAllOfSmallest = i == smallestLength; + } + if (matchesAllOfSmallest) { + if (toLen > smallestLength) { + if (toOrig[toStart + smallestLength] == CHAR_FORWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='/foo/bar'; to='/foo/bar/baz' + return MaybeSlice(T){ .result = toOrig[toStart + smallestLength + 1 .. toOrigLen] }; + } + if (smallestLength == 0) { + // We get here if `from` is the root + // For example: from='/'; to='/foo' + return MaybeSlice(T){ .result = toOrig[toStart + smallestLength .. toOrigLen] }; + } + } else if (fromLen > smallestLength) { + if (fromOrig[fromStart + smallestLength] == CHAR_FORWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='/foo/bar/baz'; to='/foo/bar' + lastCommonSep = smallestLength; + } else if (smallestLength == 0) { + // We get here if `to` is the root. + // For example: from='/foo/bar'; to='/' + lastCommonSep = 0; + } + } + } + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + // Backed by buf3. + var out: []const T = &.{}; + // Add a block to isolate `i`. + { + // Generate the relative path based on the path difference between `to` + // and `from`. + + // Translated from the following JS code: + // for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { + var i: usize = fromStart + (if (lastCommonSep != null) lastCommonSep.? + 1 else 0); + while (i <= fromEnd) : (i += 1) { + if (i == fromEnd or fromOrig[i] == CHAR_FORWARD_SLASH) { + // Translated from the following JS code: + // out += out.length === 0 ? '..' : '/..'; + if (out.len > 0) { + bufOffset = bufSize; + bufSize += 3; + buf3[bufOffset] = CHAR_FORWARD_SLASH; + buf3[bufOffset + 1] = CHAR_DOT; + buf3[bufOffset + 2] = CHAR_DOT; + } else { + bufSize = 2; + buf3[0] = CHAR_DOT; + buf3[1] = CHAR_DOT; + } + out = buf3[0..bufSize]; + } + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts. + + // Translated from the following JS code: + // return `${out}${StringPrototypeSlice(to, toStart + lastCommonSep)}`; + toStart = if (lastCommonSep != null) toStart + lastCommonSep.? else 0; + const sliceSize = toOrigLen - toStart; + const outLen = out.len; + bufSize = outLen; + if (sliceSize > 0) { + bufOffset = bufSize; + bufSize += sliceSize; + // Use bun.copy because toOrig and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], toOrig[toStart..toOrigLen]); + } + if (outLen > 0) { + bun.memmove(buf[0..outLen], out); + } + return MaybeSlice(T){ .result = buf[0..bufSize] }; +} + +/// Based on Node v21.6.1 path.win32.relative: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L500 +pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) MaybeSlice(T) { + comptime validatePathT(T, "relativeWindowsT"); + + // validateString of `from` and `to` are performed in pub fn relative. + if (std.mem.eql(T, from, to)) { + return MaybeSlice(T){ .result = &.{} }; + } + + // Backed by expandable buf2 because fromOrig may be long. + const fromOrig = switch (resolveWindowsT(T, &.{from}, buf2, buf3)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + const fromOrigLen = fromOrig.len; + // Backed by buf. + const toOrig = switch (resolveWindowsT(T, &.{to}, buf, buf3)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + + if (std.mem.eql(T, fromOrig, toOrig) or + eqlIgnoreCaseT(T, fromOrig, toOrig)) + { + return MaybeSlice(T){ .result = &.{} }; + } + + const toOrigLen = toOrig.len; + + // Trim leading backslashes + var fromStart: usize = 0; + while (fromStart < fromOrigLen and + fromOrig[fromStart] == CHAR_BACKWARD_SLASH) + { + fromStart += 1; + } + + // Trim trailing backslashes (applicable to UNC paths only) + var fromEnd = fromOrigLen; + while (fromEnd - 1 > fromStart and + fromOrig[fromEnd - 1] == CHAR_BACKWARD_SLASH) + { + fromEnd -= 1; + } + + const fromLen = fromEnd - fromStart; + + // Trim leading backslashes + var toStart: usize = 0; + while (toStart < toOrigLen and + toOrig[toStart] == CHAR_BACKWARD_SLASH) + { + toStart = toStart + 1; + } + + // Trim trailing backslashes (applicable to UNC paths only) + var toEnd = toOrigLen; + while (toEnd - 1 > toStart and + toOrig[toEnd - 1] == CHAR_BACKWARD_SLASH) + { + toEnd -= 1; + } + + const toLen = toEnd - toStart; + + // Compare paths to find the longest common path from root + const smallestLength = @min(fromLen, toLen); + // We use an optional value instead of -1, as in Node code, for easier number type use. + var lastCommonSep: ?usize = null; + + var matchesAllOfSmallest = false; + // Add a block to isolate `i`. + { + var i: usize = 0; + while (i < smallestLength) : (i += 1) { + const fromByte = fromOrig[fromStart + i]; + if (toLowerT(T, fromByte) != toLowerT(T, toOrig[toStart + i])) { + break; + } else if (fromByte == CHAR_BACKWARD_SLASH) { + lastCommonSep = i; + } + } + matchesAllOfSmallest = i == smallestLength; + } + + // We found a mismatch before the first common path separator was seen, so + // return the original `to`. + if (!matchesAllOfSmallest) { + if (lastCommonSep == null) { + return MaybeSlice(T){ .result = toOrig }; + } + } else { + if (toLen > smallestLength) { + if (toOrig[toStart + smallestLength] == CHAR_BACKWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='C:\foo\bar'; to='C:\foo\bar\baz' + return MaybeSlice(T){ .result = toOrig[toStart + smallestLength + 1 .. toOrigLen] }; + } + if (smallestLength == 2) { + // We get here if `from` is the device root. + // For example: from='C:\'; to='C:\foo' + return MaybeSlice(T){ .result = toOrig[toStart + smallestLength .. toOrigLen] }; + } + } + if (fromLen > smallestLength) { + if (fromOrig[fromStart + smallestLength] == CHAR_BACKWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='C:\foo\bar'; to='C:\foo' + lastCommonSep = smallestLength; + } else if (smallestLength == 2) { + // We get here if `to` is the device root. + // For example: from='C:\foo\bar'; to='C:\' + lastCommonSep = 3; + } + } + if (lastCommonSep == null) { + lastCommonSep = 0; + } + } + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + // Backed by buf3. + var out: []const T = &.{}; + // Add a block to isolate `i`. + { + // Generate the relative path based on the path difference between `to` + // and `from`. + var i: usize = fromStart + (if (lastCommonSep != null) lastCommonSep.? + 1 else 0); + while (i <= fromEnd) : (i += 1) { + if (i == fromEnd or fromOrig[i] == CHAR_BACKWARD_SLASH) { + // Translated from the following JS code: + // out += out.length === 0 ? '..' : '\\..'; + if (out.len > 0) { + bufOffset = bufSize; + bufSize += 3; + buf3[bufOffset] = CHAR_BACKWARD_SLASH; + buf3[bufOffset + 1] = CHAR_DOT; + buf3[bufOffset + 2] = CHAR_DOT; + } else { + bufSize = 2; + buf3[0] = CHAR_DOT; + buf3[1] = CHAR_DOT; + } + out = buf3[0..bufSize]; + } + } + } + + // Translated from the following JS code: + // toStart += lastCommonSep; + if (lastCommonSep == null) { + // If toStart would go negative make it toOrigLen - 1 to + // mimic String#slice with a negative start. + toStart = if (toStart > 0) toStart - 1 else toOrigLen - 1; + } else { + toStart += lastCommonSep.?; + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts + const outLen = out.len; + if (outLen > 0) { + const sliceSize = toEnd - toStart; + bufSize = outLen; + if (sliceSize > 0) { + bufOffset = bufSize; + bufSize += sliceSize; + // Use bun.copy because toOrig and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], toOrig[toStart..toEnd]); + } + bun.memmove(buf[0..outLen], out); + return MaybeSlice(T){ .result = buf[0..bufSize] }; + } + + if (toOrig[toStart] == CHAR_BACKWARD_SLASH) { + toStart += 1; + } + return MaybeSlice(T){ .result = toOrig[toStart..toEnd] }; +} + +pub inline fn relativePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) JSC.JSValue { + return switch (relativePosixT(T, from, to, buf, buf2, buf3)) { + .result => |r| toJSString(globalObject, r), + .err => |e| e.toJSC(globalObject), + }; +} + +pub inline fn relativeWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) JSC.JSValue { + return switch (relativeWindowsT(T, from, to, buf, buf2, buf3)) { + .result => |r| toJSString(globalObject, r), + .err => |e| e.toJSC(globalObject), + }; +} + +pub fn relativeJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, from: []const T, to: []const T) JSC.JSValue { + const bufLen = @max(from.len + to.len, PATH_SIZE(T)); + const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf); + const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf2); + const buf3 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf3); + return if (isWindows) relativeWindowsJS_T(T, globalObject, from, to, buf, buf2, buf3) else relativePosixJS_T(T, globalObject, from, to, buf, buf2, buf3); +} + +pub fn relative(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + const from_ptr = if (args_len > 0) args_ptr[0] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, from_ptr, "from", .{}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + const to_ptr = if (args_len > 1) args_ptr[1] else .undefined; + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, to_ptr, "to", .{}) catch { + return .zero; + }; + + const fromZigStr = from_ptr.getZigString(globalObject); + const toZigStr = to_ptr.getZigString(globalObject); + if ((fromZigStr.len + toZigStr.len) == 0) return from_ptr; + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + var fromZigSlice = fromZigStr.toSlice(allocator); + defer fromZigSlice.deinit(); + var toZigSlice = toZigStr.toSlice(allocator); + defer toZigSlice.deinit(); + return relativeJS_T(u8, globalObject, allocator, isWindows, fromZigSlice.slice(), toZigSlice.slice()); +} + +/// Based on Node v21.6.1 path.posix.resolve: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1095 +pub fn resolvePosixT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) MaybeSlice(T) { + comptime validatePathT(T, "resolvePosixT"); + + // Backed by expandable buf2 because resolvedPath may be long. + // We use buf2 here because resolvePosixT is called by other methods and using + // buf2 here avoids stepping on others' toes. + var resolvedPath: []const T = &.{}; + var resolvedPathLen: usize = 0; + var resolvedAbsolute: bool = false; + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + var i_i64: i64 = if (paths.len == 0) -1 else @as(i64, @intCast(paths.len - 1)); + while (i_i64 > -2 and !resolvedAbsolute) : (i_i64 -= 1) { + var path: []const T = &.{}; + if (i_i64 >= 0) { + path = paths[@as(usize, @intCast(i_i64))]; + } else { + // cwd is limited to MAX_PATH_BYTES. + var tmpBuf: [MAX_PATH_SIZE(T)]T = undefined; + path = switch (posixCwdT(T, &tmpBuf)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + } + // validateString of `path` is performed in pub fn resolve. + const len = path.len; + + // Skip empty paths. + if (len == 0) { + continue; + } + + // Translated from the following JS code: + // resolvedPath = `${path}/${resolvedPath}`; + if (resolvedPathLen > 0) { + bufOffset = len + 1; + bufSize = bufOffset + resolvedPathLen; + // Move all bytes to the right by path.len + 1 for the separator. + // Use bun.copy because resolvedPath and buf2 overlap. + bun.copy(u8, buf2[bufOffset..bufSize], resolvedPath); + } + bufSize = len; + bun.memmove(buf2[0..bufSize], path); + bufSize += 1; + buf2[len] = CHAR_FORWARD_SLASH; + bufSize += resolvedPathLen; + + resolvedPath = buf2[0..bufSize]; + resolvedPathLen = bufSize; + resolvedAbsolute = path[0] == CHAR_FORWARD_SLASH; + } + + // Exit early for empty path. + if (resolvedPathLen == 0) { + return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) }; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeStringT(T, resolvedPath, !resolvedAbsolute, CHAR_FORWARD_SLASH, .posix, buf); + // resolvedPath is now backed by buf. + resolvedPathLen = resolvedPath.len; + + // Translated from the following JS code: + // if (resolvedAbsolute) { + // return `/${resolvedPath}`; + // } + if (resolvedAbsolute) { + bufSize = resolvedPathLen + 1; + // Use bun.copy because resolvedPath and buf overlap. + bun.copy(T, buf[1..bufSize], resolvedPath); + buf[0] = CHAR_FORWARD_SLASH; + return MaybeSlice(T){ .result = buf[0..bufSize] }; + } + // Translated from the following JS code: + // return resolvedPath.length > 0 ? resolvedPath : '.'; + return MaybeSlice(T){ .result = if (resolvedPathLen > 0) resolvedPath else comptime L(T, CHAR_STR_DOT) }; +} + +/// Based on Node v21.6.1 path.win32.resolve: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L162 +pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) MaybeSlice(T) { + comptime validatePathT(T, "resolveWindowsT"); + + const isSepT = isSepWindowsT; + var tmpBuf: [MAX_PATH_SIZE(T)]T = undefined; + + // Backed by tmpBuf. + var resolvedDevice: []const T = &.{}; + var resolvedDeviceLen: usize = 0; + // Backed by expandable buf2 because resolvedTail may be long. + // We use buf2 here because resolvePosixT is called by other methods and using + // buf2 here avoids stepping on others' toes. + var resolvedTail: []const T = &.{}; + var resolvedTailLen: usize = 0; + var resolvedAbsolute: bool = false; + + var bufOffset: usize = 0; + var bufSize: usize = 0; + var envPath: ?[]const T = null; + + var i_i64: i64 = if (paths.len == 0) -1 else @as(i64, @intCast(paths.len - 1)); + while (i_i64 > -2) : (i_i64 -= 1) { + // Backed by expandable buf2, to not conflict with buf2 backed resolvedTail, + // because path may be long. + var path: []const T = &.{}; + if (i_i64 >= 0) { + path = paths[@as(usize, @intCast(i_i64))]; + // validateString of `path` is performed in pub fn resolve. + + // Skip empty paths. + if (path.len == 0) { + continue; + } + } else if (resolvedDeviceLen == 0) { + // cwd is limited to MAX_PATH_BYTES. + path = switch (getCwdT(T, &tmpBuf)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + } else { + // Translated from the following JS code: + // path = process.env[`=${resolvedDevice}`] || process.cwd(); + if (comptime Environment.isWindows) { + var u16Buf: bun.WPathBuffer = undefined; + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive, or the process cwd if + // the drive cwd is not available. We're sure the device is not + // a UNC path at this points, because UNC paths are always absolute. + + // Translated from the following JS code: + // process.env[`=${resolvedDevice}`] + const key_w: [*:0]const u16 = brk: { + if (resolvedDeviceLen == 2 and resolvedDevice[1] == CHAR_COLON) { + // Fast path for device roots + break :brk &[3:0]u16{ '=', resolvedDevice[0], CHAR_COLON }; + } + bufSize = 1; + // Reuse buf2 for the env key because it's used to get the path. + buf2[0] = '='; + bufOffset = bufSize; + bufSize += resolvedDeviceLen; + bun.memmove(buf2[bufOffset..bufSize], resolvedDevice); + if (T == u16) { + break :brk buf2[0..bufSize]; + } else { + bufSize = std.unicode.wtf16LeToWtf8(buf2[0..bufSize], &u16Buf); + break :brk u16Buf[0..bufSize :0]; + } + }; + // Zig's std.posix.getenvW has logic to support keys like `=${resolvedDevice}`: + // https://github.com/ziglang/zig/blob/7bd8b35a3dfe61e59ffea39d464e84fbcdead29a/lib/std/os.zig#L2126-L2130 + // + // TODO: Enable test once spawnResult.stdout works on Windows. + // test/js/node/path/resolve.test.js + if (std.process.getenvW(key_w)) |r| { + if (T == u16) { + bufSize = r.len; + bun.memmove(buf2[0..bufSize], r); + } else { + // Reuse buf2 because it's used for path. + bufSize = std.unicode.wtf16LeToWtf8(buf2, r); + } + envPath = buf2[0..bufSize]; + } + } + if (envPath) |_envPath| { + path = _envPath; + } else { + // cwd is limited to MAX_PATH_BYTES. + path = switch (getCwdT(T, &tmpBuf)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + // We must set envPath here so that it doesn't hit the null check just below. + envPath = path; + } + + // Verify that a cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + + // Translated from the following JS code: + // if (path === undefined || + // (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !== + // StringPrototypeToLowerCase(resolvedDevice) && + // StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) { + if (envPath == null or + (path[2] == CHAR_BACKWARD_SLASH and + !eqlIgnoreCaseT(T, path[0..2], resolvedDevice))) + { + // Translated from the following JS code: + // path = `${resolvedDevice}\\`; + bufSize = resolvedDeviceLen; + bun.memmove(buf2[0..bufSize], resolvedDevice); + bufOffset = bufSize; + bufSize += 1; + buf2[bufOffset] = CHAR_BACKWARD_SLASH; + path = buf2[0..bufSize]; + } + } + + const len = path.len; + var rootEnd: usize = 0; + // Backed by tmpBuf or an anonymous buffer. + var device: []const T = &.{}; + // Prefix with _ to avoid shadowing the identifier in the outer scope. + var _isAbsolute: bool = false; + const byte0 = if (len > 0) path[0] else 0; + + // Try to match a root + if (len == 1) { + if (isSepT(T, byte0)) { + // `path` contains just a path separator + rootEnd = 1; + _isAbsolute = true; + } + } else if (isSepT(T, byte0)) { + // Possible UNC root + + // If we started with a separator, we know we at least have an + // absolute path of some kind (UNC or otherwise) + _isAbsolute = true; + + if (isSepT(T, path[1])) { + // Matched double path separator at the beginning + var j: usize = 2; + var last: usize = j; + // Match 1 or more non-path separators + while (j < len and + !isSepT(T, path[j])) + { + j += 1; + } + if (j < len and j != last) { + const firstPart = path[last..j]; + // Matched! + last = j; + // Match 1 or more path separators + while (j < len and + isSepT(T, path[j])) + { + j += 1; + } + if (j < len and j != last) { + // Matched! + last = j; + // Match 1 or more non-path separators + while (j < len and + !isSepT(T, path[j])) + { + j += 1; + } + if (j == len or j != last) { + // We matched a UNC root + + // Translated from the following JS code: + // device = + // `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; + // rootEnd = j; + bufSize = 2; + tmpBuf[0] = CHAR_BACKWARD_SLASH; + tmpBuf[1] = CHAR_BACKWARD_SLASH; + bufOffset = bufSize; + bufSize += firstPart.len; + bun.memmove(tmpBuf[bufOffset..bufSize], firstPart); + bufOffset = bufSize; + bufSize += 1; + tmpBuf[bufOffset] = CHAR_BACKWARD_SLASH; + const slice = path[last..j]; + bufOffset = bufSize; + bufSize += slice.len; + bun.memmove(tmpBuf[bufOffset..bufSize], slice); + + device = tmpBuf[0..bufSize]; + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRootT(T, byte0) and + path[1] == CHAR_COLON) + { + // Possible device root + device = &[2]T{ byte0, CHAR_COLON }; + rootEnd = 2; + if (len > 2 and isSepT(T, path[2])) { + // Treat separator following the drive name as an absolute path + // indicator + _isAbsolute = true; + rootEnd = 3; + } + } + + const deviceLen = device.len; + if (deviceLen > 0) { + if (resolvedDeviceLen > 0) { + // Translated from the following JS code: + // if (StringPrototypeToLowerCase(device) !== + // StringPrototypeToLowerCase(resolvedDevice)) + if (!eqlIgnoreCaseT(T, device, resolvedDevice)) { + // This path points to another device, so it is not applicable + continue; + } + } else { + // Translated from the following JS code: + // resolvedDevice = device; + bufSize = device.len; + // Copy device over if it's backed by an anonymous buffer. + if (device.ptr != tmpBuf[0..].ptr) { + bun.memmove(tmpBuf[0..bufSize], device); + } + resolvedDevice = tmpBuf[0..bufSize]; + resolvedDeviceLen = bufSize; + } + } + + if (resolvedAbsolute) { + if (resolvedDeviceLen > 0) { + break; + } + } else { + // Translated from the following JS code: + // resolvedTail = `${StringPrototypeSlice(path, rootEnd)}\\${resolvedTail}`; + const sliceLen = len - rootEnd; + if (resolvedTailLen > 0) { + bufOffset = sliceLen + 1; + bufSize = bufOffset + resolvedTailLen; + // Move all bytes to the right by path slice.len + 1 for the separator + // Use bun.copy because resolvedTail and buf2 overlap. + bun.copy(u8, buf2[bufOffset..bufSize], resolvedTail); + } + bufSize = sliceLen; + if (sliceLen > 0) { + bun.memmove(buf2[0..bufSize], path[rootEnd..len]); + } + bufOffset = bufSize; + bufSize += 1; + buf2[bufOffset] = CHAR_BACKWARD_SLASH; + bufSize += resolvedTailLen; + + resolvedTail = buf2[0..bufSize]; + resolvedTailLen = bufSize; + resolvedAbsolute = _isAbsolute; + + if (_isAbsolute and resolvedDeviceLen > 0) { + break; + } + } + } + + // Exit early for empty path. + if (resolvedTailLen == 0) { + return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) }; + } + + // At this point, the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when std.process.cwdAlloc() + // fails) + + // Normalize the tail path + resolvedTail = normalizeStringT(T, resolvedTail, !resolvedAbsolute, CHAR_BACKWARD_SLASH, .windows, buf); + // resolvedTail is now backed by buf. + resolvedTailLen = resolvedTail.len; + + // Translated from the following JS code: + // resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` + if (resolvedAbsolute) { + bufOffset = resolvedDeviceLen + 1; + bufSize = bufOffset + resolvedTailLen; + // Use bun.copy because resolvedTail and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], resolvedTail); + buf[resolvedDeviceLen] = CHAR_BACKWARD_SLASH; + bun.memmove(buf[0..resolvedDeviceLen], resolvedDevice); + return MaybeSlice(T){ .result = buf[0..bufSize] }; + } + // Translated from the following JS code: + // : `${resolvedDevice}${resolvedTail}` || '.' + if ((resolvedDeviceLen + resolvedTailLen) > 0) { + bufOffset = resolvedDeviceLen; + bufSize = bufOffset + resolvedTailLen; + // Use bun.copy because resolvedTail and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], resolvedTail); + bun.memmove(buf[0..resolvedDeviceLen], resolvedDevice); + return MaybeSlice(T){ .result = buf[0..bufSize] }; + } + return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) }; +} + +pub inline fn resolvePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { + return switch (resolvePosixT(T, paths, buf, buf2)) { + .result => |r| toJSString(globalObject, r), + .err => |e| e.toJSC(globalObject), + }; +} + +pub inline fn resolveWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { + return switch (resolveWindowsT(T, paths, buf, buf2)) { + .result => |r| toJSString(globalObject, r), + .err => |e| e.toJSC(globalObject), + }; +} + +pub fn resolveJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, paths: []const []const T) JSC.JSValue { + // Adding 8 bytes when Windows for the possible UNC root. + var bufLen: usize = if (isWindows) 8 else 0; + for (paths) |path| bufLen += if (bufLen > 0 and path.len > 0) path.len + 1 else path.len; + bufLen = @max(bufLen, PATH_SIZE(T)); + const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf); + const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf2); + return if (isWindows) resolveWindowsJS_T(T, globalObject, paths, buf, buf2) else resolvePosixJS_T(T, globalObject, paths, buf, buf2); +} + +extern "C" fn Process__getCachedCwd(*JSC.JSGlobalObject) JSC.JSValue; + +pub fn resolve(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + var arena = bun.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_large, arena.allocator()); + const allocator = stack_fallback.get(); + + var paths = allocator.alloc(string, args_len) catch bun.outOfMemory(); + defer allocator.free(paths); + var path_count: usize = 0; + + for (0..args_len, args_ptr) |i, path_ptr| { + // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. + validateString(globalObject, path_ptr, "paths[{d}]", .{i}) catch { + // Returning .zero translates to a nullprt JSC.JSValue. + return .zero; + }; + const pathZStr = path_ptr.getZigString(globalObject); + if (pathZStr.len > 0) { + paths[path_count] = pathZStr.toSlice(allocator).slice(); + path_count += 1; + } + } + + if (comptime Environment.isPosix) { + if (!isWindows) { + // Micro-optimization #1: avoid creating a new string when passing no arguments or only empty strings. + if (path_count == 0) { + return Process__getCachedCwd(globalObject); + } + + // Micro-optimization #2: path.resolve(".") and path.resolve("./") === process.cwd() + else if (path_count == 1 and (strings.eqlComptime(paths[0], ".") or strings.eqlComptime(paths[0], "./"))) { + return Process__getCachedCwd(globalObject); + } + } + } + + return resolveJS_T(u8, globalObject, allocator, isWindows, paths[0..path_count]); +} + +/// Based on Node v21.6.1 path.win32.toNamespacedPath: +/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L622 +pub fn toNamespacedPathWindowsT(comptime T: type, path: []const T, buf: []T, buf2: []T) MaybeSlice(T) { + comptime validatePathT(T, "toNamespacedPathWindowsT"); + + // validateString of `path` is performed in pub fn toNamespacedPath. + // Backed by buf. + const resolvedPath = switch (resolveWindowsT(T, &.{path}, buf, buf2)) { + .result => |r| r, + .err => |e| return MaybeSlice(T){ .err = e }, + }; + + const len = resolvedPath.len; + if (len <= 2) { + return MaybeSlice(T){ .result = path }; + } + + var bufOffset: usize = 0; + var bufSize: usize = 0; + + const byte0 = resolvedPath[0]; + if (byte0 == CHAR_BACKWARD_SLASH) { + // Possible UNC root + if (resolvedPath[1] == CHAR_BACKWARD_SLASH) { + const byte2 = resolvedPath[2]; + if (byte2 != CHAR_QUESTION_MARK and byte2 != CHAR_DOT) { + // Matched non-long UNC root, convert the path to a long UNC path + + // Translated from the following JS code: + // return `\\\\?\\UNC\\${StringPrototypeSlice(resolvedPath, 2)}`; + bufOffset = 6; + bufSize = len + 6; + // Move all bytes to the right by 6 so that the first two bytes are + // overwritten by "\\\\?\\UNC\\" which is 8 bytes long. + // Use bun.copy because resolvedPath and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], resolvedPath); + // Equiv to std.os.windows.NamespacePrefix.verbatim + // https://github.com/ziglang/zig/blob/dcaf43674e35372e1d28ab12c4c4ff9af9f3d646/lib/std/os/windows.zig#L2358-L2374 + buf[0] = CHAR_BACKWARD_SLASH; + buf[1] = CHAR_BACKWARD_SLASH; + buf[2] = CHAR_QUESTION_MARK; + buf[3] = CHAR_BACKWARD_SLASH; + buf[4] = 'U'; + buf[5] = 'N'; + buf[6] = 'C'; + buf[7] = CHAR_BACKWARD_SLASH; + return MaybeSlice(T){ .result = buf[0..bufSize] }; + } + } + } else if (isWindowsDeviceRootT(T, byte0) and + resolvedPath[1] == CHAR_COLON and + resolvedPath[2] == CHAR_BACKWARD_SLASH) + { + // Matched device root, convert the path to a long UNC path + + // Translated from the following JS code: + // return `\\\\?\\${resolvedPath}` + bufOffset = 4; + bufSize = len + 4; + // Move all bytes to the right by 4 + // Use bun.copy because resolvedPath and buf overlap. + bun.copy(T, buf[bufOffset..bufSize], resolvedPath); + // Equiv to std.os.windows.NamespacePrefix.verbatim + // https://github.com/ziglang/zig/blob/dcaf43674e35372e1d28ab12c4c4ff9af9f3d646/lib/std/os/windows.zig#L2358-L2374 + buf[0] = CHAR_BACKWARD_SLASH; + buf[1] = CHAR_BACKWARD_SLASH; + buf[2] = CHAR_QUESTION_MARK; + buf[3] = CHAR_BACKWARD_SLASH; + return MaybeSlice(T){ .result = buf[0..bufSize] }; + } + return MaybeSlice(T){ .result = resolvedPath }; +} + +pub inline fn toNamespacedPathWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, buf: []T, buf2: []T) JSC.JSValue { + return switch (toNamespacedPathWindowsT(T, path, buf, buf2)) { + .result => |r| toJSString(globalObject, r), + .err => |e| e.toJSC(globalObject), + }; +} + +pub fn toNamespacedPathJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, path: []const T) JSC.JSValue { + if (!isWindows or path.len == 0) return toJSString(globalObject, path); + const bufLen = @max(path.len, PATH_SIZE(T)); + const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf); + const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); + defer allocator.free(buf2); + return toNamespacedPathWindowsJS_T(T, globalObject, path, buf, buf2); +} + +pub fn toNamespacedPath(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { + if (args_len == 0) return .undefined; + var path_ptr = args_ptr[0]; + + // Based on Node v21.6.1 path.win32.toNamespacedPath and path.posix.toNamespacedPath: + // https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L624 + // https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1269 + // + // Act as an identity function for non-string values and non-Windows platforms. + if (!isWindows or !path_ptr.isString()) return path_ptr; + const pathZStr = path_ptr.getZigString(globalObject); + const len = pathZStr.len; + if (len == 0) return path_ptr; + + var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); + const allocator = stack_fallback.get(); + + const pathZSlice = pathZStr.toSlice(allocator); + defer pathZSlice.deinit(); + return toNamespacedPathJS_T(u8, globalObject, allocator, isWindows, pathZSlice.slice()); +} + +pub const Extern = [_][]const u8{"create"}; + +comptime { + @export(Path.basename, .{ .name = shim.symbolName("basename") }); + @export(Path.dirname, .{ .name = shim.symbolName("dirname") }); + @export(Path.extname, .{ .name = shim.symbolName("extname") }); + @export(Path.format, .{ .name = shim.symbolName("format") }); + @export(Path.isAbsolute, .{ .name = shim.symbolName("isAbsolute") }); + @export(Path.join, .{ .name = shim.symbolName("join") }); + @export(Path.normalize, .{ .name = shim.symbolName("normalize") }); + @export(Path.parse, .{ .name = shim.symbolName("parse") }); + @export(Path.relative, .{ .name = shim.symbolName("relative") }); + @export(Path.resolve, .{ .name = shim.symbolName("resolve") }); + @export(Path.toNamespacedPath, .{ .name = shim.symbolName("toNamespacedPath") }); +} diff --git a/src/bun.js/node/path_watcher.zig b/src/bun.js/node/path_watcher.zig index f3fda8461b529..3203d7365d008 100644 --- a/src/bun.js/node/path_watcher.zig +++ b/src/bun.js/node/path_watcher.zig @@ -18,7 +18,7 @@ const GenericWatcher = @import("../../watcher.zig"); const sync = @import("../../sync.zig"); const Semaphore = sync.Semaphore; -var default_manager_mutex: Mutex = Mutex.init(); +var default_manager_mutex: Mutex = .{}; var default_manager: ?*PathWatcherManager = null; const FSWatcher = bun.JSC.Node.FSWatcher; @@ -154,7 +154,7 @@ pub const PathWatcherManager = struct { ), .vm = vm, .watcher_count = 0, - .mutex = Mutex.init(), + .mutex = .{}, }; this.* = manager; @@ -795,7 +795,7 @@ pub const PathWatcher = struct { .flushCallback = updateEndCallback, .file_paths = .{}, .ctx = ctx, - .mutex = Mutex.init(), + .mutex = .{}, }; errdefer this.deinit(); @@ -815,7 +815,7 @@ pub const PathWatcher = struct { .recursive = recursive, .flushCallback = updateEndCallback, .ctx = ctx, - .mutex = Mutex.init(), + .mutex = .{}, .file_paths = bun.BabyList([:0]const u8).initCapacity(bun.default_allocator, 1) catch |err| { bun.default_allocator.destroy(this); return err; diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index c265593b09074..8a09cd2ff0104 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -11,9 +11,6 @@ const posix = std.posix; const path_handler = bun.path; const strings = bun.strings; const string = bun.string; -const validators = @import("./util/validators.zig"); -const validateObject = validators.validateObject; -const validateString = validators.validateString; const C = bun.C; const L = strings.literal; @@ -27,68 +24,12 @@ const Syscall = bun.sys; const URL = @import("../../url.zig").URL; const Value = std.json.Value; -const PATH_MIN_WIDE = 4096; // 4 KB - -const stack_fallback_size_small = switch (Environment.os) { - // Up to 4 KB, instead of MAX_PATH_BYTES which is 96 KB on Windows, ouch! - .windows => PATH_MIN_WIDE, - else => bun.MAX_PATH_BYTES, -}; - -const stack_fallback_size_large = 32 * @sizeOf(string); // up to 32 strings on the stack - -/// Taken from Zig 0.11.0 zig/src/resinator/rc.zig -/// https://github.com/ziglang/zig/blob/776cd673f206099012d789fd5d05d49dd72b9faa/src/resinator/rc.zig#L266 -/// -/// Compares ASCII values case-insensitively, non-ASCII values are compared directly -fn eqlIgnoreCaseT(comptime T: type, a: []const T, b: []const T) bool { - if (T != u16) { - return std.ascii.eqlIgnoreCase(a, b); - } - if (a.len != b.len) return false; - for (a, b) |a_c, b_c| { - if (a_c < 128) { - if (std.ascii.toLower(@intCast(a_c)) != std.ascii.toLower(@intCast(b_c))) return false; - } else { - if (a_c != b_c) return false; - } - } - return true; -} - -/// Taken from Zig 0.11.0 zig/src/resinator/rc.zig -/// https://github.com/ziglang/zig/blob/776cd673f206099012d789fd5d05d49dd72b9faa/src/resinator/rc.zig#L266 -/// -/// Lowers ASCII values, non-ASCII values are returned directly -inline fn toLowerT(comptime T: type, a_c: T) T { - if (T != u16) { - return std.ascii.toLower(a_c); - } - return if (a_c < 128) @intCast(std.ascii.toLower(@intCast(a_c))) else a_c; -} - -inline fn toJSString(globalObject: *JSC.JSGlobalObject, slice: []const u8) JSC.JSValue { - return if (slice.len > 0) - JSC.ZigString.init(slice).withEncoding().toValueGC(globalObject) - else - JSC.JSValue.jsEmptyString(globalObject); -} - -inline fn toUTF8JSString(globalObject: *JSC.JSGlobalObject, slice: []const u8) JSC.JSValue { - return JSC.ZigString.initUTF8(slice).toValueGC(globalObject); -} +pub const Path = @import("./path.zig"); fn typeBaseNameT(comptime T: type) []const u8 { return meta.typeBaseName(@typeName(T)); } -fn validatePathT(comptime T: type, comptime methodName: []const u8) void { - comptime switch (T) { - inline u8, u16 => return, - else => @compileError("Unsupported type for " ++ methodName ++ ": " ++ typeBaseNameT(T)), - }; -} - pub const Buffer = JSC.MarkedArrayBuffer; /// On windows, this is what libuv expects @@ -158,6 +99,14 @@ pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type { return .{ .err = ErrorType{} }; } + pub fn isTrue(this: @This()) bool { + if (comptime ReturnType != bool) @compileError("This function can only be called on bool"); + return switch (this) { + .result => |r| r, + else => false, + }; + } + pub fn unwrap(this: @This()) !ReturnType { return switch (this) { .result => |r| r, @@ -202,7 +151,7 @@ pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type { .Struct, .Enum, .Opaque, .Union => r.toJS(globalObject), .Pointer => { if (bun.trait.isZigString(ReturnType)) - JSC.ZigString.init(bun.asByteSlice(r)).withEncoding().toValueAuto(globalObject); + JSC.ZigString.init(bun.asByteSlice(r)).withEncoding().toJS(globalObject); return r.toJS(globalObject); }, @@ -297,14 +246,6 @@ pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type { }; } -inline fn MaybeBuf(comptime T: type) type { - return Maybe([]T, Syscall.Error); -} - -inline fn MaybeSlice(comptime T: type) type { - return Maybe([]const T, Syscall.Error); -} - fn translateToErrInt(err: anytype) bun.sys.Error.Int { return switch (@TypeOf(err)) { bun.windows.NTSTATUS => @intFromEnum(bun.windows.translateNTStatusToErrno(err)), @@ -336,6 +277,26 @@ pub const BlobOrStringOrBuffer = union(enum) { }; } + pub fn protect(this: *const BlobOrStringOrBuffer) void { + switch (this.*) { + .string_or_buffer => |sob| { + sob.protect(); + }, + else => {}, + } + } + + pub fn deinitAndUnprotect(this: *BlobOrStringOrBuffer) void { + switch (this.*) { + .string_or_buffer => |sob| { + sob.deinitAndUnprotect(); + }, + .blob => |*blob| { + blob.deinit(); + }, + } + } + pub fn fromJS(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue) ?BlobOrStringOrBuffer { if (value.as(JSC.WebCore.Blob)) |blob| { if (blob.store) |store| { @@ -383,6 +344,15 @@ pub const StringOrBuffer = union(enum) { } } + pub fn protect(this: *const StringOrBuffer) void { + switch (this.*) { + .buffer => |buf| { + buf.buffer.value.protect(); + }, + else => {}, + } + } + pub fn fromJSToOwnedSlice(globalObject: *JSC.JSGlobalObject, value: JSC.JSValue, allocator: std.mem.Allocator) ![]u8 { if (value.asArrayBuffer(globalObject)) |array_buffer| { defer globalObject.vm().reportExtraMemory(array_buffer.len); @@ -490,6 +460,7 @@ pub const StringOrBuffer = union(enum) { .Int32Array, .Uint32Array, .Float32Array, + .Float16Array, .Float64Array, .BigInt64Array, .BigUint64Array, @@ -611,18 +582,18 @@ pub const Encoding = enum(u8) { .base64 => { var buf: [std.base64.standard.Encoder.calcSize(size)]u8 = undefined; const len = bun.base64.encode(&buf, input); - return JSC.ZigString.init(buf[0..len]).toValueGC(globalObject); + return JSC.ZigString.init(buf[0..len]).toJS(globalObject); }, .base64url => { var buf: [std.base64.url_safe_no_pad.Encoder.calcSize(size)]u8 = undefined; const encoded = std.base64.url_safe_no_pad.Encoder.encode(&buf, input); - return JSC.ZigString.init(buf[0..encoded.len]).toValueGC(globalObject); + return JSC.ZigString.init(buf[0..encoded.len]).toJS(globalObject); }, .hex => { var buf: [size * 4]u8 = undefined; const out = std.fmt.bufPrint(&buf, "{}", .{std.fmt.fmtSliceHexLower(input)}) catch bun.outOfMemory(); - const result = JSC.ZigString.init(out).toValueGC(globalObject); + const result = JSC.ZigString.init(out).toJS(globalObject); return result; }, .buffer => { @@ -654,12 +625,12 @@ pub const Encoding = enum(u8) { var buf: [std.base64.url_safe_no_pad.Encoder.calcSize(max_size * 4)]u8 = undefined; const encoded = std.base64.url_safe_no_pad.Encoder.encode(&buf, input); - return JSC.ZigString.init(buf[0..encoded.len]).toValueGC(globalObject); + return JSC.ZigString.init(buf[0..encoded.len]).toJS(globalObject); }, .hex => { var buf: [max_size * 4]u8 = undefined; const out = std.fmt.bufPrint(&buf, "{}", .{std.fmt.fmtSliceHexLower(input)}) catch bun.outOfMemory(); - const result = JSC.ZigString.init(out).toValueGC(globalObject); + const result = JSC.ZigString.init(out).toJS(globalObject); return result; }, .buffer => { @@ -938,6 +909,13 @@ pub const Valid = struct { return false; } + const fd_t = if (Environment.isWindows) bun.windows.libuv.uv_file else bun.FileDescriptorInt; + + if (fd > std.math.maxInt(fd_t)) { + JSC.throwInvalidArguments("Invalid file descriptor, must not be greater than {d}", .{std.math.maxInt(fd_t)}, ctx, exception); + return false; + } + return true; } @@ -1013,7 +991,7 @@ pub const VectorArrayBuffer = struct { var bufferlist = std.ArrayList(bun.PlatformIOVec).init(allocator); var i: usize = 0; const len = val.getLength(globalObject); - bufferlist.ensureTotalCapacityPrecise(len) catch @panic("Failed to allocate memory for ArrayBuffer[]"); + bufferlist.ensureTotalCapacityPrecise(len) catch bun.outOfMemory(); while (i < len) { const element = val.getIndex(globalObject, @as(u32, @truncate(i))); @@ -1029,7 +1007,7 @@ pub const VectorArrayBuffer = struct { }; const buf = array_buffer.byteSlice(); - bufferlist.append(bun.platformIOVecCreate(buf)) catch @panic("Failed to allocate memory for ArrayBuffer[]"); + bufferlist.append(bun.platformIOVecCreate(buf)) catch bun.outOfMemory(); i += 1; } @@ -1169,9 +1147,13 @@ pub fn timeLikeFromJS(globalObject: *JSC.JSGlobalObject, value: JSC.JSValue, _: } pub fn modeFromJS(ctx: JSC.C.JSContextRef, value: JSC.JSValue, exception: JSC.C.ExceptionRef) ?Mode { - const mode_int = if (value.isNumber()) - @as(Mode, @truncate(value.to(Mode))) - else brk: { + const mode_int = if (value.isNumber()) brk: { + if (!value.isUInt32AsAnyInt()) { + exception.* = ctx.ERR_OUT_OF_RANGE("The value of \"mode\" is out of range. It must be an integer. Received {d}", .{value.asNumber()}).toJS().asObjectRef(); + return null; + } + break :brk @as(Mode, @truncate(value.to(Mode))); + } else brk: { if (value.isUndefinedOrNull()) return null; // An easier method of constructing the mode is to use a sequence of @@ -1372,6 +1354,10 @@ pub const FileSystemFlags = enum(Mode) { pub fn fromJS(ctx: JSC.C.JSContextRef, val: JSC.JSValue, exception: JSC.C.ExceptionRef) ?FileSystemFlags { if (val.isNumber()) { + if (!val.isInt32()) { + exception.* = ctx.ERR_OUT_OF_RANGE("The value of \"flags\" is out of range. It must be an integer. Received {d}", .{val.asNumber()}).toJS().asObjectRef(); + return null; + } const number = val.coerce(i32, ctx); return @as(FileSystemFlags, @enumFromInt(@as(Mode, @intCast(@max(number, 0))))); } @@ -1453,11 +1439,12 @@ pub fn StatType(comptime Big: bool) type { return extern struct { pub usingnamespace if (Big) JSC.Codegen.JSBigIntStats else JSC.Codegen.JSStats; + pub usingnamespace bun.New(@This()); // Stats stores these as i32, but BigIntStats stores all of these as i64 // On windows, these two need to be u64 as the numbers are often very large. - dev: if (Environment.isWindows) u64 else Int, - ino: if (Environment.isWindows) u64 else Int, + dev: u64, + ino: u64, mode: Int, nlink: Int, uid: Int, @@ -1501,23 +1488,29 @@ pub fn StatType(comptime Big: bool) type { } } - const PropertyGetter = fn (this: *This, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; + const PropertyGetter = fn (this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue; fn getter(comptime field: meta.FieldEnum(This)) PropertyGetter { return struct { - pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue { const value = @field(this, @tagName(field)); - if (comptime (Big and @typeInfo(@TypeOf(value)) == .Int)) { - return JSC.JSValue.fromInt64NoTruncate(globalObject, @intCast(value)); + const Type = @TypeOf(value); + if (comptime Big and @typeInfo(Type) == .Int) { + if (Type == u64) { + return JSC.JSValue.fromUInt64NoTruncate(globalObject, value); + } + + return JSC.JSValue.fromInt64NoTruncate(globalObject, value); } - return globalObject.toJS(value, .temporary); + + return JSC.JSValue.jsNumber(value); } }.callback; } fn dateGetter(comptime field: meta.FieldEnum(This)) PropertyGetter { return struct { - pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue { const value = @field(this, @tagName(field)); // Doing `Date{ ... }` here shouldn't actually change the memory layout of `value` // but it will tell comptime code how to convert the i64/f64 to a JS Date. @@ -1545,13 +1538,13 @@ pub fn StatType(comptime Big: bool) type { const DOMCallFn = fn ( *This, *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue; + ) callconv(JSC.conv) JSC.JSValue; fn domCall(comptime decl: meta.DeclEnum(This)) DOMCallFn { return struct { pub fn run( this: *This, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { return @field(This, @tagName(decl))(this); } }.run; @@ -1622,7 +1615,7 @@ pub fn StatType(comptime Big: bool) type { // TODO: BigIntStats includes a `_checkModeProperty` but I dont think anyone actually uses it. pub fn finalize(this: *This) callconv(.C) void { - bun.destroy(this); + this.destroy(); } pub fn init(stat_: bun.Stat) This { @@ -1631,8 +1624,8 @@ pub fn StatType(comptime Big: bool) type { const cTime = stat_.ctime(); return .{ - .dev = if (Environment.isWindows) stat_.dev else @truncate(@as(i64, @intCast(stat_.dev))), - .ino = if (Environment.isWindows) stat_.ino else @truncate(@as(i64, @intCast(stat_.ino))), + .dev = @intCast(@max(stat_.dev, 0)), + .ino = @intCast(@max(stat_.ino, 0)), .mode = @truncate(@as(i64, @intCast(stat_.mode))), .nlink = @truncate(@as(i64, @intCast(stat_.nlink))), .uid = @truncate(@as(i64, @intCast(stat_.uid))), @@ -1655,13 +1648,7 @@ pub fn StatType(comptime Big: bool) type { }; } - pub fn initWithAllocator(allocator: std.mem.Allocator, stat: bun.Stat) *This { - const this = allocator.create(This) catch bun.outOfMemory(); - this.* = init(stat); - return this; - } - - pub fn constructor(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) ?*This { + pub fn constructor(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(JSC.conv) ?*This { if (Big) { globalObject.throwInvalidArguments("BigIntStats is not a constructor", .{}); return null; @@ -1675,7 +1662,7 @@ pub fn StatType(comptime Big: bool) type { const ctime_ms: f64 = if (args.len > 12 and args[12].isNumber()) args[12].asNumber() else 0; const birthtime_ms: f64 = if (args.len > 13 and args[13].isNumber()) args[13].asNumber() else 0; - const this = bun.new(This, .{ + const this = This.new(.{ .dev = if (args.len > 0 and args[0].isNumber()) @intCast(args[0].toInt32()) else 0, .mode = if (args.len > 1 and args[1].isNumber()) args[1].toInt32() else 0, .nlink = if (args.len > 2 and args[2].isNumber()) args[2].toInt32() else 0, @@ -1725,8 +1712,8 @@ pub const Stats = union(enum) { pub fn toJSNewlyCreated(this: *const Stats, globalObject: *JSC.JSGlobalObject) JSC.JSValue { return switch (this.*) { - .big => bun.new(StatsBig, this.big).toJS(globalObject), - .small => bun.new(StatsSmall, this.small).toJS(globalObject), + .big => StatsBig.new(this.big).toJS(globalObject), + .small => StatsSmall.new(this.small).toJS(globalObject), }; } @@ -1765,22 +1752,33 @@ pub const Dirent = struct { pub const Kind = std.fs.File.Kind; pub usingnamespace JSC.Codegen.JSDirent; + pub usingnamespace bun.New(@This()); - pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*Dirent { + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) ?*Dirent { globalObject.throw("Dirent is not a constructor", .{}); return null; } + pub fn toJS(this: *Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + const as_js = Dirent.toJSUnchecked(globalObject, this); + + // Immediately create JSString* objects for the name and path + // So that the GC is aware of them and can collect them if necessary + Dirent.nameSetCached(as_js, globalObject, this.name.toJS(globalObject)); + Dirent.pathSetCached(as_js, globalObject, this.path.toJS(globalObject)); + + return as_js; + } + pub fn toJSNewlyCreated(this: *const Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue { - var out = bun.new(Dirent, this.*); - return out.toJS(globalObject); + return toJS(Dirent.new(this.*), globalObject); } - pub fn getName(this: *Dirent, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getName(this: *Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue { return this.name.toJS(globalObject); } - pub fn getPath(this: *Dirent, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getPath(this: *Dirent, globalThis: *JSC.JSGlobalObject) JSC.JSValue { return this.path.toJS(globalThis); } @@ -1788,49 +1786,49 @@ pub const Dirent = struct { this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.block_device); } pub fn isCharacterDevice( this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.character_device); } pub fn isDirectory( this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.directory); } pub fn isFIFO( this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.named_pipe or this.kind == std.fs.File.Kind.event_port); } pub fn isFile( this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.file); } pub fn isSocket( this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.unix_domain_socket); } pub fn isSymbolicLink( this: *Dirent, _: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.sym_link); } @@ -1839,3207 +1837,180 @@ pub const Dirent = struct { this.path.deref(); } - pub fn finalize(this: *Dirent) callconv(.C) void { + pub fn finalize(this: *Dirent) void { this.deref(); - bun.destroy(this); + this.destroy(); } }; -pub const Emitter = struct { - pub const Listener = struct { - once: bool = false, - callback: JSC.JSValue, - - pub const List = struct { - pub const ArrayList = std.MultiArrayList(Listener); - list: ArrayList = ArrayList{}, - once_count: u32 = 0, - - pub fn append(this: *List, allocator: std.mem.Allocator, ctx: JSC.C.JSContextRef, listener: Listener) !void { - JSC.C.JSValueProtect(ctx, listener.callback.asObjectRef()); - try this.list.append(allocator, listener); - this.once_count +|= @as(u32, @intFromBool(listener.once)); - } - - pub fn prepend(this: *List, allocator: std.mem.Allocator, ctx: JSC.C.JSContextRef, listener: Listener) !void { - JSC.C.JSValueProtect(ctx, listener.callback.asObjectRef()); - try this.list.ensureUnusedCapacity(allocator, 1); - this.list.insertAssumeCapacity(0, listener); - this.once_count +|= @as(u32, @intFromBool(listener.once)); - } +pub const Process = struct { + pub fn getArgv0(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + return JSC.ZigString.fromUTF8(bun.argv[0]).toJS(globalObject); + } - // removeListener() will remove, at most, one instance of a listener from the - // listener array. If any single listener has been added multiple times to the - // listener array for the specified eventName, then removeListener() must be - // called multiple times to remove each instance. - pub fn remove(this: *List, ctx: JSC.C.JSContextRef, callback: JSC.JSValue) bool { - const callbacks = this.list.items(.callback); - - for (callbacks, 0..) |item, i| { - if (callback.eqlValue(item)) { - JSC.C.JSValueUnprotect(ctx, callback.asObjectRef()); - this.once_count -|= @as(u32, @intFromBool(this.list.items(.once)[i])); - this.list.orderedRemove(i); - return true; - } - } + pub fn getExecPath(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + const out = bun.selfExePath() catch { + // if for any reason we are unable to get the executable path, we just return argv[0] + return getArgv0(globalObject); + }; - return false; - } + return JSC.ZigString.fromUTF8(out).toJS(globalObject); + } - pub fn emit(this: *List, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { - var i: usize = 0; - outer: while (true) { - var slice = this.list.slice(); - var callbacks = slice.items(.callback); - var once = slice.items(.once); - while (i < callbacks.len) : (i += 1) { - const callback = callbacks[i]; - - globalObject.enqueueMicrotask1( - callback, - value, - ); - - if (once[i]) { - this.once_count -= 1; - JSC.C.JSValueUnprotect(globalObject, callback.asObjectRef()); - this.list.orderedRemove(i); - slice = this.list.slice(); - callbacks = slice.items(.callback); - once = slice.items(.once); - continue :outer; - } - } + pub fn getExecArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + var sfb = std.heap.stackFallback(4096, globalObject.allocator()); + const temp_alloc = sfb.get(); + const vm = globalObject.bunVM(); - return; + if (vm.worker) |worker| { + // was explicitly overridden for the worker? + if (worker.execArgv) |execArgv| { + const array = JSC.JSValue.createEmptyArray(globalObject, execArgv.len); + for (0..execArgv.len) |i| { + array.putIndex(globalObject, @intCast(i), bun.String.init(execArgv[i]).toJS(globalObject)); } + return array; } - }; - }; - - pub fn New(comptime EventType: type) type { - return struct { - const EventEmitter = @This(); - pub const Map = std.enums.EnumArray(EventType, Listener.List); - listeners: Map = Map.initFill(Listener.List{}), + } - pub fn addListener(this: *EventEmitter, ctx: JSC.C.JSContextRef, event: EventType, listener: Emitter.Listener) !void { - try this.listeners.getPtr(event).append(bun.default_allocator, ctx, listener); - } + var args = std.ArrayList(bun.String).initCapacity(temp_alloc, bun.argv.len - 1) catch bun.outOfMemory(); + defer args.deinit(); - pub fn prependListener(this: *EventEmitter, ctx: JSC.C.JSContextRef, event: EventType, listener: Emitter.Listener) !void { - try this.listeners.getPtr(event).prepend(bun.default_allocator, ctx, listener); - } + var seen_run = false; + var prev: ?[]const u8 = null; - pub fn emit(this: *EventEmitter, event: EventType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { - this.listeners.getPtr(event).emit(globalObject, value); - } + // we re-parse the process argv to extract execArgv, since this is a very uncommon operation + // it isn't worth doing this as a part of the CLI + for (bun.argv[@min(1, bun.argv.len)..]) |arg| { + defer prev = arg; - pub fn removeListener(this: *EventEmitter, ctx: JSC.C.JSContextRef, event: EventType, callback: JSC.JSValue) bool { - return this.listeners.getPtr(event).remove(ctx, callback); + if (arg.len >= 1 and arg[0] == '-') { + args.append(bun.String.createUTF8(arg)) catch bun.outOfMemory(); + continue; } - }; - } -}; -pub const Path = struct { - const CHAR_BACKWARD_SLASH = '\\'; - const CHAR_COLON = ':'; - const CHAR_DOT = '.'; - const CHAR_FORWARD_SLASH = '/'; - const CHAR_QUESTION_MARK = '?'; - - const CHAR_STR_BACKWARD_SLASH = "\\"; - const CHAR_STR_FORWARD_SLASH = "/"; - const CHAR_STR_DOT = "."; - - const StringBuilder = @import("../../string_builder.zig"); - - /// Based on Node v21.6.1 path.parse: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L919 - /// The structs returned by parse methods. - fn PathParsed(comptime T: type) type { - return struct { - root: []const T = "", - dir: []const T = "", - base: []const T = "", - ext: []const T = "", - name: []const T = "", - pub fn toJSObject(this: @This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { - var jsObject = JSC.JSValue.createEmptyObject(globalObject, 5); - jsObject.put(globalObject, JSC.ZigString.static("root"), toJSString(globalObject, this.root)); - jsObject.put(globalObject, JSC.ZigString.static("dir"), toJSString(globalObject, this.dir)); - jsObject.put(globalObject, JSC.ZigString.static("base"), toJSString(globalObject, this.base)); - jsObject.put(globalObject, JSC.ZigString.static("ext"), toJSString(globalObject, this.ext)); - jsObject.put(globalObject, JSC.ZigString.static("name"), toJSString(globalObject, this.name)); - return jsObject; + if (!seen_run and bun.strings.eqlComptime(arg, "run")) { + seen_run = true; + continue; } - }; - } - - pub fn MAX_PATH_SIZE(comptime T: type) usize { - return if (T == u16) windows.PATH_MAX_WIDE else bun.MAX_PATH_BYTES; - } - - pub fn PATH_SIZE(comptime T: type) usize { - return if (T == u16) PATH_MIN_WIDE else bun.MAX_PATH_BYTES; - } - pub const shim = Shimmer("Bun", "Path", @This()); - pub const name = "Bun__Path"; - pub const include = "Path.h"; - pub const namespace = shim.namespace; - pub const sep_posix = CHAR_FORWARD_SLASH; - pub const sep_windows = CHAR_BACKWARD_SLASH; - pub const sep_str_posix = CHAR_STR_FORWARD_SLASH; - pub const sep_str_windows = CHAR_STR_BACKWARD_SLASH; - - /// Based on Node v21.6.1 private helper formatExt: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L130C10-L130C19 - inline fn formatExtT(comptime T: type, ext: []const T, buf: []T) []const T { - const len = ext.len; - if (len == 0) { - return comptime L(T, ""); - } - if (ext[0] == CHAR_DOT) { - return ext; - } - const bufSize = len + 1; - buf[0] = CHAR_DOT; - @memcpy(buf[1..bufSize], ext); - return buf[0..bufSize]; - } - - /// Based on Node v21.6.1 private helper posixCwd: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1074 - inline fn posixCwdT(comptime T: type, buf: []T) MaybeBuf(T) { - const cwd = switch (getCwdT(T, buf)) { - .result => |r| r, - .err => |e| return MaybeBuf(T){ .err = e }, - }; - const len = cwd.len; - if (len == 0) { - return MaybeBuf(T){ .result = cwd }; - } - if (comptime Environment.isWindows) { - // Converts Windows' backslash path separators to POSIX forward slashes - // and truncates any drive indicator - - // Translated from the following JS code: - // const cwd = StringPrototypeReplace(process.cwd(), regexp, '/'); - for (0..len) |i| { - if (cwd[i] == CHAR_BACKWARD_SLASH) { - buf[i] = CHAR_FORWARD_SLASH; - } else { - buf[i] = cwd[i]; + // A set of execArgv args consume an extra argument, so we do not want to + // confuse these with script names. + const map = bun.ComptimeStringMap(void, comptime brk: { + const auto_params = bun.CLI.Arguments.auto_params; + const KV = struct { []const u8, void }; + var entries: [auto_params.len]KV = undefined; + var i = 0; + for (auto_params) |param| { + if (param.takes_value != .none) { + if (param.names.long) |name| { + entries[i] = .{ "--" ++ name, {} }; + i += 1; + } + if (param.names.short) |name| { + entries[i] = .{ &[_]u8{ '-', name }, {} }; + i += 1; + } + } } - } - var normalizedCwd = buf[0..len]; - - // Translated from the following JS code: - // return StringPrototypeSlice(cwd, StringPrototypeIndexOf(cwd, '/')); - const index = std.mem.indexOfScalar(T, normalizedCwd, CHAR_FORWARD_SLASH); - // Account for the -1 case of String#slice in JS land - if (index) |_index| { - return MaybeBuf(T){ .result = normalizedCwd[_index..len] }; - } - return MaybeBuf(T){ .result = normalizedCwd[len - 1 .. len] }; - } - // We're already on POSIX, no need for any transformations - return MaybeBuf(T){ .result = cwd }; - } + var result: [i]KV = undefined; + @memcpy(&result, entries[0..i]); + break :brk result; + }); - pub fn getCwdWindowsU8(buf: []u8) MaybeBuf(u8) { - const u16Buf: bun.WPathBuffer = undefined; - switch (getCwdWindowsU16(&u16Buf)) { - .result => |r| { - // Handles conversion from UTF-16 to UTF-8 including surrogates ;) - const result = strings.convertUTF16ToUTF8InBuffer(&buf, r) catch { - return MaybeBuf(u8).errnoSys(0, Syscall.Tag.getcwd).?; - }; - return MaybeBuf(u8){ .result = result }; - }, - .err => |e| return MaybeBuf(u8){ .err = e }, - } - } + if (prev) |p| if (map.has(p)) { + args.append(bun.String.createUTF8(arg)) catch @panic("OOM"); + continue; + }; - pub fn getCwdWindowsU16(buf: []u16) MaybeBuf(u16) { - const len: u32 = kernel32.GetCurrentDirectoryW(buf.len, &buf); - if (len == 0) { - // Indirectly calls std.os.windows.kernel32.GetLastError(). - return MaybeBuf(u16).errnoSys(0, Syscall.Tag.getcwd).?; + // we hit the script name + break; } - return MaybeBuf(u16){ .result = buf[0..len] }; - } - pub fn getCwdWindowsT(comptime T: type, buf: []T) MaybeBuf(T) { - comptime validatePathT(T, "getCwdWindowsT"); - return if (T == u16) - getCwdWindowsU16(buf) - else - getCwdWindowsU8(buf); + return bun.String.toJSArray(globalObject, args.items); } - pub fn getCwdU8(buf: []u8) MaybeBuf(u8) { - const result = bun.getcwd(buf) catch { - return MaybeBuf(u8).errnoSys( - @as(c_int, 0), - Syscall.Tag.getcwd, - ).?; - }; - return MaybeBuf(u8){ .result = result }; - } + pub fn getArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + const vm = globalObject.bunVM(); - pub fn getCwdU16(buf: []u16) MaybeBuf(u16) { - if (comptime Environment.isWindows) { - return getCwdWindowsU16(&buf); + // Allocate up to 32 strings in stack + var stack_fallback_allocator = std.heap.stackFallback( + 32 * @sizeOf(JSC.ZigString) + (bun.MAX_PATH_BYTES + 1) + 32, + heap_allocator, + ); + const allocator = stack_fallback_allocator.get(); + + var args_count: usize = vm.argv.len; + if (vm.worker) |worker| { + args_count = if (worker.argv) |argv| argv.len else 0; } - const u8Buf: bun.PathBuffer = undefined; - const result = strings.convertUTF8toUTF16InBuffer(&buf, bun.getcwd(strings.convertUTF16ToUTF8InBuffer(&u8Buf, buf))) catch { - return MaybeBuf(u16).errnoSys(0, Syscall.Tag.getcwd).?; - }; - return MaybeBuf(u16){ .result = result }; - } - pub fn getCwdT(comptime T: type, buf: []T) MaybeBuf(T) { - comptime validatePathT(T, "getCwdT"); - return if (T == u16) - getCwdU16(buf) - else - getCwdU8(buf); - } + const args = allocator.alloc( + bun.String, + // argv omits "bun" because it could be "bun run" or "bun" and it's kind of ambiguous + // argv also omits the script name + args_count + 2, + ) catch bun.outOfMemory(); + var args_list = std.ArrayListUnmanaged(bun.String){ .items = args, .capacity = args.len }; + args_list.items.len = 0; - // Alias for naming consistency. - pub const getCwd = getCwdU8; + if (vm.standalone_module_graph != null) { + // Don't break user's code because they did process.argv.slice(2) + // Even if they didn't type "bun", we still want to add it as argv[0] + args_list.appendAssumeCapacity( + bun.String.static("bun"), + ); + } else { + const exe_path = bun.selfExePath() catch null; + args_list.appendAssumeCapacity( + if (exe_path) |str| bun.String.fromUTF8(str) else bun.String.static("bun"), + ); + } - /// Based on Node v21.6.1 path.posix.basename: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1309 - pub fn basenamePosixT(comptime T: type, path: []const T, suffix: ?[]const T) []const T { - comptime validatePathT(T, "basenamePosixT"); + if (vm.main.len > 0) + args_list.appendAssumeCapacity(bun.String.fromUTF8(vm.main)); - // validateString of `path` is performed in pub fn basename. - const len = path.len; - // Exit early for easier number type use. - if (len == 0) { - return comptime L(T, ""); - } - var start: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash: bool = true; - - const _suffix = if (suffix) |_s| _s else comptime L(T, ""); - const _suffixLen = _suffix.len; - if (suffix != null and _suffixLen > 0 and _suffixLen <= len) { - if (std.mem.eql(T, _suffix, path)) { - return comptime L(T, ""); - } - // We use an optional value instead of -1, as in Node code, for easier number type use. - var extIdx: ?usize = _suffixLen - 1; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var firstNonSlashEnd: ?usize = null; - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 >= start) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (byte == CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else { - if (firstNonSlashEnd == null) { - // We saw the first non-path separator, remember this index in case - // we need it if the extension ends up not matching - matchedSlash = false; - firstNonSlashEnd = i + 1; - } - if (extIdx) |_extIx| { - // Try to match the explicit extension - if (byte == _suffix[_extIx]) { - if (_extIx == 0) { - // We matched the extension, so mark this as the end of our path - // component - end = i; - extIdx = null; - } else { - extIdx = _extIx - 1; - } - } else { - // Extension does not match, so our result is the entire path - // component - extIdx = null; - end = firstNonSlashEnd; - } - } - } - } + defer allocator.free(args); - if (end) |_end| { - if (start == _end) { - return path[start..firstNonSlashEnd.?]; - } else { - return path[start.._end]; + if (vm.worker) |worker| { + if (worker.argv) |argv| { + for (argv) |arg| { + args_list.appendAssumeCapacity(bun.String.init(arg)); } } - return path[start..len]; - } - - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 > -1) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (byte == CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else if (end == null) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; + } else { + for (vm.argv) |arg| { + const str = bun.String.fromUTF8(arg); + // https://github.com/yargs/yargs/blob/adb0d11e02c613af3d9427b3028cc192703a3869/lib/utils/process-argv.ts#L1 + args_list.appendAssumeCapacity(str); } } - return if (end) |_end| - path[start.._end] - else - comptime L(T, ""); + return bun.String.toJSArray(globalObject, args_list.items); } - /// Based on Node v21.6.1 path.win32.basename: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L753 - pub fn basenameWindowsT(comptime T: type, path: []const T, suffix: ?[]const T) []const T { - comptime validatePathT(T, "basenameWindowsT"); - - // validateString of `path` is performed in pub fn basename. - const len = path.len; - // Exit early for easier number type use. - if (len == 0) { - return comptime L(T, ""); + pub fn getCwd(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + var buf: bun.PathBuffer = undefined; + switch (Path.getCwd(&buf)) { + .result => |r| return JSC.ZigString.init(r).withEncoding().toJS(globalObject), + .err => |e| { + globalObject.throwValue(e.toJSC(globalObject)); + return .zero; + }, } + } - const isSepT = isSepWindowsT; - - var start: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash: bool = true; - - // Check for a drive letter prefix so as not to mistake the following - // path separator as an extra separator at the end of the path that can be - // disregarded - if (len >= 2 and isWindowsDeviceRootT(T, path[0]) and path[1] == CHAR_COLON) { - start = 2; - } - - const _suffix = if (suffix) |_s| _s else comptime L(T, ""); - const _suffixLen = _suffix.len; - if (suffix != null and _suffixLen > 0 and _suffixLen <= len) { - if (std.mem.eql(T, _suffix, path)) { - return comptime L(T, ""); - } - // We use an optional value instead of -1, as in Node code, for easier number type use. - var extIdx: ?usize = _suffixLen - 1; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var firstNonSlashEnd: ?usize = null; - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 >= start) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (isSepT(T, byte)) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else { - if (firstNonSlashEnd == null) { - // We saw the first non-path separator, remember this index in case - // we need it if the extension ends up not matching - matchedSlash = false; - firstNonSlashEnd = i + 1; - } - if (extIdx) |_extIx| { - // Try to match the explicit extension - if (byte == _suffix[_extIx]) { - if (_extIx == 0) { - // We matched the extension, so mark this as the end of our path - // component - end = i; - extIdx = null; - } else { - extIdx = _extIx - 1; - } - } else { - // Extension does not match, so our result is the entire path - // component - extIdx = null; - end = firstNonSlashEnd; - } - } - } - } - - if (end) |_end| { - if (start == _end) { - return path[start..firstNonSlashEnd.?]; - } else { - return path[start.._end]; - } - } - return path[start..len]; - } - - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 >= start) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (isSepT(T, byte)) { - if (!matchedSlash) { - start = i + 1; - break; - } - } else if (end == null) { - matchedSlash = false; - end = i + 1; - } - } - - return if (end) |_end| - path[start.._end] - else - comptime L(T, ""); - } - - pub inline fn basenamePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, suffix: ?[]const T) JSC.JSValue { - return toJSString(globalObject, basenamePosixT(T, path, suffix)); - } - - pub inline fn basenameWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, suffix: ?[]const T) JSC.JSValue { - return toJSString(globalObject, basenameWindowsT(T, path, suffix)); - } - - pub inline fn basenameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T, suffix: ?[]const T) JSC.JSValue { - return if (isWindows) - basenameWindowsJS_T(T, globalObject, path, suffix) - else - basenamePosixJS_T(T, globalObject, path, suffix); - } - - pub fn basename(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const suffix_ptr: ?JSC.JSValue = if (args_len > 1) args_ptr[1] else null; - - if (suffix_ptr) |_suffix_ptr| { - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, _suffix_ptr, "ext", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - } - - const path_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "path", .{}) catch { - return .zero; - }; - - const pathZStr = path_ptr.getZigString(globalObject); - if (pathZStr.len == 0) return path_ptr; - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - const pathZSlice = pathZStr.toSlice(allocator); - defer pathZSlice.deinit(); - - var suffixZSlice: ?JSC.ZigString.Slice = null; - if (suffix_ptr) |_suffix_ptr| { - const suffixZStr = _suffix_ptr.getZigString(globalObject); - if (suffixZStr.len > 0 and suffixZStr.len <= pathZStr.len) { - suffixZSlice = suffixZStr.toSlice(allocator); - } - } - defer if (suffixZSlice) |_s| _s.deinit(); - return basenameJS_T(u8, globalObject, isWindows, pathZSlice.slice(), if (suffixZSlice) |_s| _s.slice() else null); - } - - pub fn create(globalObject: *JSC.JSGlobalObject, isWindows: bool) callconv(.C) JSC.JSValue { - return shim.cppFn("create", .{ globalObject, isWindows }); - } - - /// Based on Node v21.6.1 path.posix.dirname: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1278 - pub fn dirnamePosixT(comptime T: type, path: []const T) []const T { - comptime validatePathT(T, "dirnamePosixT"); - - // validateString of `path` is performed in pub fn dirname. - const len = path.len; - if (len == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - const hasRoot = path[0] == CHAR_FORWARD_SLASH; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash: bool = true; - var i: usize = len - 1; - while (i >= 1) : (i -= 1) { - if (path[i] == CHAR_FORWARD_SLASH) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } - } - - if (end) |_end| { - return if (hasRoot and _end == 1) - comptime L(T, "//") - else - path[0.._end]; - } - return if (hasRoot) - comptime L(T, CHAR_STR_FORWARD_SLASH) - else - comptime L(T, CHAR_STR_DOT); - } - - /// Based on Node v21.6.1 path.win32.dirname: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L657 - pub fn dirnameWindowsT(comptime T: type, path: []const T) []const T { - comptime validatePathT(T, "dirnameWindowsT"); - - // validateString of `path` is performed in pub fn dirname. - const len = path.len; - if (len == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - const isSepT = isSepWindowsT; - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var rootEnd: ?usize = null; - var offset: usize = 0; - const byte0 = path[0]; - - if (len == 1) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work or a dot. - return if (isSepT(T, byte0)) path else comptime L(T, CHAR_STR_DOT); - } - - // Try to match a root - if (isSepT(T, byte0)) { - // Possible UNC root - - rootEnd = 1; - offset = 1; - - if (isSepT(T, path[1])) { - // Matched double path separator at the beginning - var j: usize = 2; - var last: usize = j; - - // Match 1 or more non-path separators - while (j < len and !isSepT(T, path[j])) { - j += 1; - } - - if (j < len and j != last) { - // Matched! - last = j; - - // Match 1 or more path separators - while (j < len and isSepT(T, path[j])) { - j += 1; - } - - if (j < len and j != last) { - // Matched! - last = j; - - // Match 1 or more non-path separators - while (j < len and !isSepT(T, path[j])) { - j += 1; - } - - if (j == len) { - // We matched a UNC root only - return path; - } - - if (j != last) { - // We matched a UNC root with leftovers - - // Offset by 1 to include the separator after the UNC root to - // treat it as a "normal root" on top of a (UNC) root - offset = j + 1; - rootEnd = offset; - } - } - } - } - // Possible device root - } else if (isWindowsDeviceRootT(T, byte0) and path[1] == CHAR_COLON) { - offset = if (len > 2 and isSepT(T, path[2])) 3 else 2; - rootEnd = offset; - } - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash: bool = true; - - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 >= offset) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - if (isSepT(T, path[i])) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } - } - - if (end) |_end| { - return path[0.._end]; - } - - return if (rootEnd) |_rootEnd| - path[0.._rootEnd] - else - comptime L(T, CHAR_STR_DOT); - } - - pub inline fn dirnamePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { - return toJSString(globalObject, dirnamePosixT(T, path)); - } - - pub inline fn dirnameWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { - return toJSString(globalObject, dirnameWindowsT(T, path)); - } - - pub inline fn dirnameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T) JSC.JSValue { - return if (isWindows) - dirnameWindowsJS_T(T, globalObject, path) - else - dirnamePosixJS_T(T, globalObject, path); - } - - pub fn dirname(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const path_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "path", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - - const pathZStr = path_ptr.getZigString(globalObject); - if (pathZStr.len == 0) return toUTF8JSString(globalObject, CHAR_STR_DOT); - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - const pathZSlice = pathZStr.toSlice(allocator); - defer pathZSlice.deinit(); - return dirnameJS_T(u8, globalObject, isWindows, pathZSlice.slice()); - } - - /// Based on Node v21.6.1 path.posix.extname: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1278 - pub fn extnamePosixT(comptime T: type, path: []const T) []const T { - comptime validatePathT(T, "extnamePosixT"); - - // validateString of `path` is performed in pub fn extname. - const len = path.len; - // Exit early for easier number type use. - if (len == 0) { - return comptime L(T, ""); - } - // We use an optional value instead of -1, as in Node code, for easier number type use. - var startDot: ?usize = null; - var startPart: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash: bool = true; - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var preDotState: ?usize = 0; - - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 > -1) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (byte == CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - - if (end == null) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - - if (byte == CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot == null) { - startDot = i; - } else if (preDotState != null and preDotState.? != 1) { - preDotState = 1; - } - } else if (startDot != null) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = null; - } - } - - const _end = if (end) |_e| _e else 0; - const _preDotState = if (preDotState) |_p| _p else 0; - const _startDot = if (startDot) |_s| _s else 0; - if (startDot == null or - end == null or - // We saw a non-dot character immediately before the dot - (preDotState != null and _preDotState == 0) or - // The (right-most) trimmed path component is exactly '..' - (_preDotState == 1 and - _startDot == _end - 1 and - _startDot == startPart + 1)) - { - return comptime L(T, ""); - } - - return path[_startDot.._end]; - } - - /// Based on Node v21.6.1 path.win32.extname: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L840 - pub fn extnameWindowsT(comptime T: type, path: []const T) []const T { - comptime validatePathT(T, "extnameWindowsT"); - - // validateString of `path` is performed in pub fn extname. - const len = path.len; - // Exit early for easier number type use. - if (len == 0) { - return comptime L(T, ""); - } - var start: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var startDot: ?usize = null; - var startPart: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash: bool = true; - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var preDotState: ?usize = 0; - - // Check for a drive letter prefix so as not to mistake the following - // path separator as an extra separator at the end of the path that can be - // disregarded - - if (len >= 2 and - path[1] == CHAR_COLON and - isWindowsDeviceRootT(T, path[0])) - { - start = 2; - startPart = start; - } - - var i_i64 = @as(i64, @intCast(len - 1)); - while (i_i64 >= start) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (isSepWindowsT(T, byte)) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end == null) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (byte == CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot == null) { - startDot = i; - } else if (preDotState) |_preDotState| { - if (_preDotState != 1) { - preDotState = 1; - } - } - } else if (startDot != null) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = null; - } - } - - const _end = if (end) |_e| _e else 0; - const _preDotState = if (preDotState) |_p| _p else 0; - const _startDot = if (startDot) |_s| _s else 0; - if (startDot == null or - end == null or - // We saw a non-dot character immediately before the dot - (preDotState != null and _preDotState == 0) or - // The (right-most) trimmed path component is exactly '..' - (_preDotState == 1 and - _startDot == _end - 1 and - _startDot == startPart + 1)) - { - return comptime L(T, ""); - } - - return path[_startDot.._end]; - } - - pub inline fn extnamePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { - return toJSString(globalObject, extnamePosixT(T, path)); - } - - pub inline fn extnameWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { - return toJSString(globalObject, extnameWindowsT(T, path)); - } - - pub inline fn extnameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T) JSC.JSValue { - return if (isWindows) - extnameWindowsJS_T(T, globalObject, path) - else - extnamePosixJS_T(T, globalObject, path); - } - - pub fn extname(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const path_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "path", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - - const pathZStr = path_ptr.getZigString(globalObject); - if (pathZStr.len == 0) return path_ptr; - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - const pathZSlice = pathZStr.toSlice(allocator); - defer pathZSlice.deinit(); - return extnameJS_T(u8, globalObject, isWindows, pathZSlice.slice()); - } - - /// Based on Node v21.6.1 private helper _format: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L145 - fn _formatT(comptime T: type, pathObject: PathParsed(T), sep: T, buf: []T) []const T { - comptime validatePathT(T, "_formatT"); - - // validateObject of `pathObject` is performed in pub fn format. - const root = pathObject.root; - const dir = pathObject.dir; - const base = pathObject.base; - const ext = pathObject.ext; - // Prefix with _ to avoid shadowing the identifier in the outer scope. - const _name = pathObject.name; - - // Translated from the following JS code: - // const dir = pathObject.dir || pathObject.root; - const dirIsRoot = dir.len == 0 or std.mem.eql(u8, dir, root); - const dirOrRoot = if (dirIsRoot) root else dir; - const dirLen = dirOrRoot.len; - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - // Translated from the following JS code: - // const base = pathObject.base || - // `${pathObject.name || ''}${formatExt(pathObject.ext)}`; - var baseLen = base.len; - var baseOrNameExt = base; - if (baseLen > 0) { - @memcpy(buf[0..baseLen], base); - } else { - const formattedExt = formatExtT(T, ext, buf); - const nameLen = _name.len; - const extLen = formattedExt.len; - bufOffset = nameLen; - bufSize = bufOffset + extLen; - if (extLen > 0) { - // Move all bytes to the right by _name.len. - // Use bun.copy because formattedExt and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], formattedExt); - } - if (nameLen > 0) { - @memcpy(buf[0..nameLen], _name); - } - if (bufSize > 0) { - baseOrNameExt = buf[0..bufSize]; - } - } - - // Translated from the following JS code: - // if (!dir) { - // return base; - // } - if (dirLen == 0) { - return baseOrNameExt; - } - - // Translated from the following JS code: - // return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; - baseLen = baseOrNameExt.len; - if (baseLen > 0) { - bufOffset = if (dirIsRoot) dirLen else dirLen + 1; - bufSize = bufOffset + baseLen; - // Move all bytes to the right by dirLen + (maybe 1 for the separator). - // Use bun.copy because baseOrNameExt and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], baseOrNameExt); - } - @memcpy(buf[0..dirLen], dirOrRoot); - bufSize = dirLen + baseLen; - if (!dirIsRoot) { - bufSize += 1; - buf[dirLen] = sep; - } - return buf[0..bufSize]; - } - - pub inline fn formatPosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, pathObject: PathParsed(T), buf: []T) JSC.JSValue { - return toJSString(globalObject, _formatT(T, pathObject, CHAR_FORWARD_SLASH, buf)); - } - - pub inline fn formatWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, pathObject: PathParsed(T), buf: []T) JSC.JSValue { - return toJSString(globalObject, _formatT(T, pathObject, CHAR_BACKWARD_SLASH, buf)); - } - - pub fn formatJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, pathObject: PathParsed(T)) JSC.JSValue { - const baseLen = pathObject.base.len; - const dirLen = pathObject.dir.len; - // Add one for the possible separator. - const bufLen: usize = @max(1 + - (if (dirLen > 0) dirLen else pathObject.root.len) + - (if (baseLen > 0) baseLen else pathObject.name.len + pathObject.ext.len), PATH_SIZE(T)); - const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf); - return if (isWindows) formatWindowsJS_T(T, globalObject, pathObject, buf) else formatPosixJS_T(T, globalObject, pathObject, buf); - } - - pub fn format(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const pathObject_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateObject(globalObject, pathObject_ptr, "pathObject", .{}, .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - var root: []const u8 = ""; - if (pathObject_ptr.getTruthy(globalObject, "root")) |jsValue| { - root = jsValue.toSlice(globalObject, allocator).slice(); - } - var dir: []const u8 = ""; - if (pathObject_ptr.getTruthy(globalObject, "dir")) |jsValue| { - dir = jsValue.toSlice(globalObject, allocator).slice(); - } - var base: []const u8 = ""; - if (pathObject_ptr.getTruthy(globalObject, "base")) |jsValue| { - base = jsValue.toSlice(globalObject, allocator).slice(); - } - // Prefix with _ to avoid shadowing the identifier in the outer scope. - var _name: []const u8 = ""; - if (pathObject_ptr.getTruthy(globalObject, "name")) |jsValue| { - _name = jsValue.toSlice(globalObject, allocator).slice(); - } - var ext: []const u8 = ""; - if (pathObject_ptr.getTruthy(globalObject, "ext")) |jsValue| { - ext = jsValue.toSlice(globalObject, allocator).slice(); - } - return formatJS_T(u8, globalObject, allocator, isWindows, .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }); - } - - /// Based on Node v21.6.1 path.posix.isAbsolute: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1159 - pub inline fn isAbsolutePosixT(comptime T: type, path: []const T) bool { - // validateString of `path` is performed in pub fn isAbsolute. - return path.len > 0 and path[0] == CHAR_FORWARD_SLASH; - } - - /// Based on Node v21.6.1 path.win32.isAbsolute: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L406 - pub fn isAbsoluteWindowsT(comptime T: type, path: []const T) bool { - // validateString of `path` is performed in pub fn isAbsolute. - const len = path.len; - if (len == 0) - return false; - - const byte0 = path[0]; - return isSepWindowsT(T, byte0) or - // Possible device root - (len > 2 and - isWindowsDeviceRootT(T, byte0) and - path[1] == CHAR_COLON and - isSepWindowsT(T, path[2])); - } - - pub fn isAbsolutePosixZigString(pathZStr: JSC.ZigString) bool { - const pathZStrTrunc = pathZStr.trunc(1); - return if (pathZStrTrunc.len > 0 and pathZStrTrunc.is16Bit()) - isAbsolutePosixT(u16, pathZStrTrunc.utf16SliceAligned()) - else - isAbsolutePosixT(u8, pathZStrTrunc.slice()); - } - - pub fn isAbsoluteWindowsZigString(pathZStr: JSC.ZigString) bool { - return if (pathZStr.len > 0 and pathZStr.is16Bit()) - isAbsoluteWindowsT(u16, @alignCast(pathZStr.utf16Slice())) - else - isAbsoluteWindowsT(u8, pathZStr.slice()); - } - - pub fn isAbsolute(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const path_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "path", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - - const pathZStr = path_ptr.getZigString(globalObject); - if (pathZStr.len == 0) return JSC.JSValue.jsBoolean(false); - if (isWindows) return JSC.JSValue.jsBoolean(isAbsoluteWindowsZigString(pathZStr)); - return JSC.JSValue.jsBoolean(isAbsolutePosixZigString(pathZStr)); - } - - pub inline fn isSepPosixT(comptime T: type, byte: T) bool { - return byte == CHAR_FORWARD_SLASH; - } - - pub inline fn isSepWindowsT(comptime T: type, byte: T) bool { - return byte == CHAR_FORWARD_SLASH or byte == CHAR_BACKWARD_SLASH; - } - - /// Based on Node v21.6.1 private helper isWindowsDeviceRoot: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L60C10-L60C29 - pub inline fn isWindowsDeviceRootT(comptime T: type, byte: T) bool { - return (byte >= 'A' and byte <= 'Z') or (byte >= 'a' and byte <= 'z'); - } - - /// Based on Node v21.6.1 path.posix.join: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1169 - pub inline fn joinPosixT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) []const T { - comptime validatePathT(T, "joinPosixT"); - - if (paths.len == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - var bufSize: usize = 0; - var bufOffset: usize = 0; - - // Back joined by expandable buf2 in case it is long. - var joined: []const T = comptime L(T, ""); - - for (paths) |path| { - // validateString of `path is performed in pub fn join. - // Back our virtual "joined" string by expandable buf2 in - // case it is long. - const len = path.len; - if (len > 0) { - // Translated from the following JS code: - // if (joined === undefined) - // joined = arg; - // else - // joined += `/${arg}`; - if (bufSize != 0) { - bufOffset = bufSize; - bufSize += 1; - buf2[bufOffset] = CHAR_FORWARD_SLASH; - } - bufOffset = bufSize; - bufSize += len; - @memcpy(buf2[bufOffset..bufSize], path); - - joined = buf2[0..bufSize]; - } - } - if (bufSize == 0) { - return comptime L(T, CHAR_STR_DOT); - } - return normalizePosixT(T, joined, buf); - } - - /// Based on Node v21.6.1 path.win32.join: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L425 - pub fn joinWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) []const T { - comptime validatePathT(T, "joinWindowsT"); - - if (paths.len == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - const isSepT = isSepWindowsT; - - var bufSize: usize = 0; - var bufOffset: usize = 0; - - // Backed by expandable buf2 in case it is long. - var joined: []const T = comptime L(T, ""); - var firstPart: []const T = comptime L(T, ""); - - for (paths) |path| { - // validateString of `path` is performed in pub fn join. - const len = path.len; - if (len > 0) { - // Translated from the following JS code: - // if (joined === undefined) - // joined = firstPart = arg; - // else - // joined += `\\${arg}`; - bufOffset = bufSize; - if (bufSize == 0) { - bufSize = len; - @memcpy(buf2[0..bufSize], path); - - joined = buf2[0..bufSize]; - firstPart = joined; - } else { - bufOffset = bufSize; - bufSize += 1; - buf2[bufOffset] = CHAR_BACKWARD_SLASH; - bufOffset = bufSize; - bufSize += len; - @memcpy(buf2[bufOffset..bufSize], path); - - joined = buf2[0..bufSize]; - } - } - } - if (bufSize == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - // Make sure that the joined path doesn't start with two slashes, because - // normalize() will mistake it for a UNC path then. - // - // This step is skipped when it is very clear that the user actually - // intended to point at a UNC path. This is assumed when the first - // non-empty string arguments starts with exactly two slashes followed by - // at least one more non-slash character. - // - // Note that for normalize() to treat a path as a UNC path it needs to - // have at least 2 components, so we don't filter for that here. - // This means that the user can use join to construct UNC paths from - // a server name and a share name; for example: - // path.join('//server', 'share') -> '\\\\server\\share\\') - var needsReplace: bool = true; - var slashCount: usize = 0; - if (isSepT(T, firstPart[0])) { - slashCount += 1; - const firstLen = firstPart.len; - if (firstLen > 1 and - isSepT(T, firstPart[1])) - { - slashCount += 1; - if (firstLen > 2) { - if (isSepT(T, firstPart[2])) { - slashCount += 1; - } else { - // We matched a UNC path in the first part - needsReplace = false; - } - } - } - } - if (needsReplace) { - // Find any more consecutive slashes we need to replace - while (slashCount < bufSize and - isSepT(T, joined[slashCount])) - { - slashCount += 1; - } - // Replace the slashes if needed - if (slashCount >= 2) { - // Translated from the following JS code: - // joined = `\\${StringPrototypeSlice(joined, slashCount)}`; - bufOffset = 1; - bufSize = bufOffset + (bufSize - slashCount); - // Move all bytes to the right by slashCount - 1. - // Use bun.copy because joined and buf2 overlap. - bun.copy(u8, buf2[bufOffset..bufSize], joined[slashCount..]); - // Prepend the separator. - buf2[0] = CHAR_BACKWARD_SLASH; - - joined = buf2[0..bufSize]; - } - } - return normalizeWindowsT(T, joined, buf); - } - - pub inline fn joinPosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { - return toJSString(globalObject, joinPosixT(T, paths, buf, buf2)); - } - - pub inline fn joinWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { - return toJSString(globalObject, joinWindowsT(T, paths, buf, buf2)); - } - - pub fn joinJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, paths: []const []const T) JSC.JSValue { - // Adding 8 bytes when Windows for the possible UNC root. - var bufLen: usize = if (isWindows) 8 else 0; - for (paths) |path| bufLen += if (bufLen > 0 and path.len > 0) path.len + 1 else path.len; - bufLen = @max(bufLen, PATH_SIZE(T)); - const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf); - const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf2); - return if (isWindows) joinWindowsJS_T(T, globalObject, paths, buf, buf2) else joinPosixJS_T(T, globalObject, paths, buf, buf2); - } - - pub fn join(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - if (args_len == 0) return toUTF8JSString(globalObject, CHAR_STR_DOT); - - var arena = bun.ArenaAllocator.init(heap_allocator); - defer arena.deinit(); - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_large, arena.allocator()); - const allocator = stack_fallback.get(); - - var paths = allocator.alloc(string, args_len) catch bun.outOfMemory(); - defer allocator.free(paths); - - for (0..args_len, args_ptr) |i, path_ptr| { - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "paths[{d}]", .{i}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - const pathZStr = path_ptr.getZigString(globalObject); - paths[i] = if (pathZStr.len > 0) pathZStr.toSlice(allocator).slice() else ""; - } - return joinJS_T(u8, globalObject, allocator, isWindows, paths); - } - - /// Based on Node v21.6.1 private helper normalizeString: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L65C1-L66C77 - /// - /// Resolves . and .. elements in a path with directory names - fn normalizeStringT(comptime T: type, path: []const T, allowAboveRoot: bool, separator: T, comptime platform: path_handler.Platform, buf: []T) []const T { - const len = path.len; - const isSepT = - if (platform == .posix) - isSepPosixT - else - isSepWindowsT; - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - var res: []const T = comptime L(T, ""); - var lastSegmentLength: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var lastSlash: ?usize = null; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var dots: ?usize = 0; - var byte: T = 0; - - var i: usize = 0; - while (i <= len) : (i += 1) { - if (i < len) { - byte = path[i]; - } else if (isSepT(T, byte)) { - break; - } else { - byte = CHAR_FORWARD_SLASH; - } - - if (isSepT(T, byte)) { - // Translated from the following JS code: - // if (lastSlash === i - 1 || dots === 1) { - if ((lastSlash == null and i == 0) or - (lastSlash != null and i > 0 and lastSlash.? == i - 1) or - (dots != null and dots.? == 1)) - { - // NOOP - } else if (dots != null and dots.? == 2) { - if (bufSize < 2 or - lastSegmentLength != 2 or - buf[bufSize - 1] != CHAR_DOT or - buf[bufSize - 2] != CHAR_DOT) - { - if (bufSize > 2) { - const lastSlashIndex = std.mem.lastIndexOfScalar(T, buf[0..bufSize], separator); - if (lastSlashIndex == null) { - res = comptime L(T, ""); - bufSize = 0; - lastSegmentLength = 0; - } else { - bufSize = lastSlashIndex.?; - res = buf[0..bufSize]; - // Translated from the following JS code: - // lastSegmentLength = - // res.length - 1 - StringPrototypeLastIndexOf(res, separator); - const lastIndexOfSep = std.mem.lastIndexOfScalar(T, buf[0..bufSize], separator); - if (lastIndexOfSep == null) { - // Yes (>ლ), Node relies on the -1 result of - // StringPrototypeLastIndexOf(res, separator). - // A - -1 is a positive 1. - // So the code becomes - // lastSegmentLength = res.length - 1 + 1; - // or - // lastSegmentLength = res.length; - lastSegmentLength = bufSize; - } else { - lastSegmentLength = bufSize - 1 - lastIndexOfSep.?; - } - } - lastSlash = i; - dots = 0; - continue; - } else if (bufSize != 0) { - res = comptime L(T, ""); - bufSize = 0; - lastSegmentLength = 0; - lastSlash = i; - dots = 0; - continue; - } - } - if (allowAboveRoot) { - // Translated from the following JS code: - // res += res.length > 0 ? `${separator}..` : '..'; - if (bufSize > 0) { - bufOffset = bufSize; - bufSize += 1; - buf[bufOffset] = separator; - bufOffset = bufSize; - bufSize += 2; - buf[bufOffset] = CHAR_DOT; - buf[bufOffset + 1] = CHAR_DOT; - } else { - bufSize = 2; - buf[0] = CHAR_DOT; - buf[1] = CHAR_DOT; - } - - res = buf[0..bufSize]; - lastSegmentLength = 2; - } - } else { - // Translated from the following JS code: - // if (res.length > 0) - // res += `${separator}${StringPrototypeSlice(path, lastSlash + 1, i)}`; - // else - // res = StringPrototypeSlice(path, lastSlash + 1, i); - if (bufSize > 0) { - bufOffset = bufSize; - bufSize += 1; - buf[bufOffset] = separator; - } - const sliceStart = if (lastSlash != null) lastSlash.? + 1 else 0; - const slice = path[sliceStart..i]; - - bufOffset = bufSize; - bufSize += slice.len; - @memcpy(buf[bufOffset..bufSize], slice); - - res = buf[0..bufSize]; - - // Translated from the following JS code: - // lastSegmentLength = i - lastSlash - 1; - const subtract = if (lastSlash != null) lastSlash.? + 1 else 2; - lastSegmentLength = if (i >= subtract) i - subtract else 0; - } - lastSlash = i; - dots = 0; - continue; - } else if (byte == CHAR_DOT and dots != null) { - dots = if (dots != null) dots.? + 1 else 0; - continue; - } else { - dots = null; - } - } - - return res; - } - - /// Based on Node v21.6.1 path.posix.normalize - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1130 - pub fn normalizePosixT(comptime T: type, path: []const T, buf: []T) []const T { - comptime validatePathT(T, "normalizePosixT"); - - // validateString of `path` is performed in pub fn normalize. - const len = path.len; - if (len == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - // Prefix with _ to avoid shadowing the identifier in the outer scope. - const _isAbsolute = path[0] == CHAR_FORWARD_SLASH; - const trailingSeparator = path[len - 1] == CHAR_FORWARD_SLASH; - - // Normalize the path - var normalizedPath = normalizeStringT(T, path, !_isAbsolute, CHAR_FORWARD_SLASH, .posix, buf); - - var bufSize: usize = normalizedPath.len; - if (bufSize == 0) { - if (_isAbsolute) { - return comptime L(T, CHAR_STR_FORWARD_SLASH); - } - return if (trailingSeparator) - comptime L(T, "./") - else - comptime L(T, CHAR_STR_DOT); - } - - var bufOffset: usize = 0; - - // Translated from the following JS code: - // if (trailingSeparator) - // path += '/'; - if (trailingSeparator) { - bufOffset = bufSize; - bufSize += 1; - buf[bufOffset] = CHAR_FORWARD_SLASH; - normalizedPath = buf[0..bufSize]; - } - - // Translated from the following JS code: - // return isAbsolute ? `/${path}` : path; - if (_isAbsolute) { - bufOffset = 1; - bufSize += 1; - // Move all bytes to the right by 1 for the separator. - // Use bun.copy because normalizedPath and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], normalizedPath); - // Prepend the separator. - buf[0] = CHAR_FORWARD_SLASH; - normalizedPath = buf[0..bufSize]; - } - return normalizedPath[0..bufSize]; - } - - /// Based on Node v21.6.1 path.win32.normalize - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L308 - pub fn normalizeWindowsT(comptime T: type, path: []const T, buf: []T) []const T { - comptime validatePathT(T, "normalizeWindowsT"); - - // validateString of `path` is performed in pub fn normalize. - const len = path.len; - if (len == 0) { - return comptime L(T, CHAR_STR_DOT); - } - - const isSepT = isSepWindowsT; - - // Moved `rootEnd`, `device`, and `_isAbsolute` initialization after - // the `if (len == 1)` check. - const byte0: T = path[0]; - - // Try to match a root - if (len == 1) { - // `path` contains just a single char, exit early to avoid - // unnecessary work - return if (isSepT(T, byte0)) comptime L(T, CHAR_STR_BACKWARD_SLASH) else path; - } - - var rootEnd: usize = 0; - // Backed by buf. - var device: ?[]const T = null; - // Prefix with _ to avoid shadowing the identifier in the outer scope. - var _isAbsolute: bool = false; - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - if (isSepT(T, byte0)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an absolute - // path of some kind (UNC or otherwise) - _isAbsolute = true; - - if (isSepT(T, path[1])) { - // Matched double path separator at beginning - var j: usize = 2; - var last: usize = j; - // Match 1 or more non-path separators - while (j < len and - !isSepT(T, path[j])) - { - j += 1; - } - if (j < len and j != last) { - const firstPart: []const u8 = path[last..j]; - // Matched! - last = j; - // Match 1 or more path separators - while (j < len and - isSepT(T, path[j])) - { - j += 1; - } - if (j < len and j != last) { - // Matched! - last = j; - // Match 1 or more non-path separators - while (j < len and - !isSepT(T, path[j])) - { - j += 1; - } - if (j == len) { - // We matched a UNC root only - // Return the normalized version of the UNC root since there - // is nothing left to process - - // Translated from the following JS code: - // return `\\\\${firstPart}\\${StringPrototypeSlice(path, last)}\\`; - bufSize = 2; - buf[0] = CHAR_BACKWARD_SLASH; - buf[1] = CHAR_BACKWARD_SLASH; - bufOffset = bufSize; - bufSize += firstPart.len; - @memcpy(buf[bufOffset..bufSize], firstPart); - bufOffset = bufSize; - bufSize += 1; - buf[bufOffset] = CHAR_BACKWARD_SLASH; - bufOffset = bufSize; - bufSize += len - last; - @memcpy(buf[bufOffset..bufSize], path[last..len]); - bufOffset = bufSize; - bufSize += 1; - buf[bufOffset] = CHAR_BACKWARD_SLASH; - return buf[0..bufSize]; - } - if (j != last) { - // We matched a UNC root with leftovers - - // Translated from the following JS code: - // device = - // `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; - // rootEnd = j; - bufSize = 2; - buf[0] = CHAR_BACKWARD_SLASH; - buf[1] = CHAR_BACKWARD_SLASH; - bufOffset = bufSize; - bufSize += firstPart.len; - @memcpy(buf[bufOffset..bufSize], firstPart); - bufOffset = bufSize; - bufSize += 1; - buf[bufOffset] = CHAR_BACKWARD_SLASH; - bufOffset = bufSize; - bufSize += j - last; - @memcpy(buf[bufOffset..bufSize], path[last..j]); - - device = buf[0..bufSize]; - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRootT(T, byte0) and - path[1] == CHAR_COLON) - { - // Possible device root - buf[0] = byte0; - buf[1] = CHAR_COLON; - device = buf[0..2]; - rootEnd = 2; - if (len > 2 and isSepT(T, path[2])) { - // Treat separator following drive name as an absolute path - // indicator - _isAbsolute = true; - rootEnd = 3; - } - } - - bufOffset = (if (device) |_d| _d.len else 0) + @intFromBool(_isAbsolute); - // Backed by buf at an offset of device.len + 1 if _isAbsolute is true. - var tailLen = if (rootEnd < len) normalizeStringT(T, path[rootEnd..len], !_isAbsolute, CHAR_BACKWARD_SLASH, .windows, buf[bufOffset..]).len else 0; - if (tailLen == 0 and !_isAbsolute) { - buf[bufOffset] = CHAR_DOT; - tailLen = 1; - } - - if (tailLen > 0 and - isSepT(T, path[len - 1])) - { - // Translated from the following JS code: - // tail += '\\'; - buf[bufOffset + tailLen] = CHAR_BACKWARD_SLASH; - tailLen += 1; - } - - bufSize = bufOffset + tailLen; - // Translated from the following JS code: - // if (device === undefined) { - // return isAbsolute ? `\\${tail}` : tail; - // } - // return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; - if (_isAbsolute) { - bufOffset -= 1; - // Prepend the separator. - buf[bufOffset] = CHAR_BACKWARD_SLASH; - } - return buf[0..bufSize]; - } - - pub fn normalizeT(comptime T: type, path: []const T, buf: []T) []const T { - return switch (Environment.os) { - .windows => normalizeWindowsT(T, path, buf), - else => normalizePosixT(T, path, buf), - }; - } - - pub inline fn normalizePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, buf: []T) JSC.JSValue { - return toJSString(globalObject, normalizePosixT(T, path, buf)); - } - - pub inline fn normalizeWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, buf: []T) JSC.JSValue { - return toJSString(globalObject, normalizeWindowsT(T, path, buf)); - } - - pub fn normalizeJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, path: []const T) JSC.JSValue { - const bufLen = @max(path.len, PATH_SIZE(T)); - const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf); - return if (isWindows) normalizeWindowsJS_T(T, globalObject, path, buf) else normalizePosixJS_T(T, globalObject, path, buf); - } - - pub fn normalize(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const path_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "path", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - const pathZStr = path_ptr.getZigString(globalObject); - const len = pathZStr.len; - if (len == 0) return toUTF8JSString(globalObject, CHAR_STR_DOT); - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - const pathZSlice = pathZStr.toSlice(allocator); - defer pathZSlice.deinit(); - return normalizeJS_T(u8, globalObject, allocator, isWindows, pathZSlice.slice()); - } - - // Based on Node v21.6.1 path.posix.parse - // https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1452 - pub fn parsePosixT(comptime T: type, path: []const T) PathParsed(T) { - comptime validatePathT(T, "parsePosixT"); - - // validateString of `path` is performed in pub fn parse. - const len = path.len; - if (len == 0) { - return .{}; - } - - var root: []const T = comptime L(T, ""); - var dir: []const T = comptime L(T, ""); - var base: []const T = comptime L(T, ""); - var ext: []const T = comptime L(T, ""); - // Prefix with _ to avoid shadowing the identifier in the outer scope. - var _name: []const T = comptime L(T, ""); - // Prefix with _ to avoid shadowing the identifier in the outer scope. - const _isAbsolute = path[0] == CHAR_FORWARD_SLASH; - var start: usize = 0; - if (_isAbsolute) { - root = comptime L(T, CHAR_STR_FORWARD_SLASH); - start = 1; - } - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var startDot: ?usize = null; - var startPart: usize = 0; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash = true; - var i_i64 = @as(i64, @intCast(len - 1)); - - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var preDotState: ?usize = 0; - - // Get non-dir info - while (i_i64 >= start) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - const byte = path[i]; - if (byte == CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end == null) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (byte == CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot == null) { - startDot = i; - } else if (preDotState) |_preDotState| { - if (_preDotState != 1) { - preDotState = 1; - } - } - } else if (startDot != null) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = null; - } - } - - if (end) |_end| { - const _preDotState = if (preDotState) |_p| _p else 0; - const _startDot = if (startDot) |_s| _s else 0; - start = if (startPart == 0 and _isAbsolute) 1 else startPart; - if (startDot == null or - // We saw a non-dot character immediately before the dot - (preDotState != null and _preDotState == 0) or - // The (right-most) trimmed path component is exactly '..' - (_preDotState == 1 and - _startDot == _end - 1 and - _startDot == startPart + 1)) - { - _name = path[start.._end]; - base = _name; - } else { - _name = path[start.._startDot]; - base = path[start.._end]; - ext = path[_startDot.._end]; - } - } - - if (startPart > 0) { - dir = path[0..(startPart - 1)]; - } else if (_isAbsolute) { - dir = comptime L(T, CHAR_STR_FORWARD_SLASH); - } - - return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; - } - - // Based on Node v21.6.1 path.win32.parse - // https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L916 - pub fn parseWindowsT(comptime T: type, path: []const T) PathParsed(T) { - comptime validatePathT(T, "parseWindowsT"); - - // validateString of `path` is performed in pub fn parse. - var root: []const T = comptime L(T, ""); - var dir: []const T = comptime L(T, ""); - var base: []const T = comptime L(T, ""); - var ext: []const T = comptime L(T, ""); - // Prefix with _ to avoid shadowing the identifier in the outer scope. - var _name: []const T = comptime L(T, ""); - - const len = path.len; - if (len == 0) { - return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; - } - - const isSepT = isSepWindowsT; - - var rootEnd: usize = 0; - var byte = path[0]; - - if (len == 1) { - if (isSepT(T, byte)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - root = path; - dir = path; - } else { - base = path; - _name = path; - } - return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; - } - - // Try to match a root - if (isSepT(T, byte)) { - // Possible UNC root - - rootEnd = 1; - if (isSepT(T, path[1])) { - // Matched double path separator at the beginning - var j: usize = 2; - var last: usize = j; - // Match 1 or more non-path separators - while (j < len and - !isSepT(T, path[j])) - { - j += 1; - } - if (j < len and j != last) { - // Matched! - last = j; - // Match 1 or more path separators - while (j < len and - isSepT(T, path[j])) - { - j += 1; - } - if (j < len and j != last) { - // Matched! - last = j; - // Match 1 or more non-path separators - while (j < len and - !isSepT(T, path[j])) - { - j += 1; - } - if (j == len) { - // We matched a UNC root only - rootEnd = j; - } else if (j != last) { - // We matched a UNC root with leftovers - rootEnd = j + 1; - } - } - } - } - } else if (isWindowsDeviceRootT(T, byte) and - path[1] == CHAR_COLON) - { - // Possible device root - if (len <= 2) { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - root = path; - dir = path; - return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; - } - rootEnd = 2; - if (isSepT(T, path[2])) { - if (len == 3) { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - root = path; - dir = path; - return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; - } - rootEnd = 3; - } - } - if (rootEnd > 0) { - root = path[0..rootEnd]; - } - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var startDot: ?usize = null; - var startPart = rootEnd; - // We use an optional value instead of -1, as in Node code, for easier number type use. - var end: ?usize = null; - var matchedSlash = true; - var i_i64 = @as(i64, @intCast(len - 1)); - - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - - // We use an optional value instead of -1, as in Node code, for easier number type use. - var preDotState: ?usize = 0; - - // Get non-dir info - while (i_i64 >= rootEnd) : (i_i64 -= 1) { - const i = @as(usize, @intCast(i_i64)); - byte = path[i]; - if (isSepT(T, byte)) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end == null) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (byte == CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot == null) { - startDot = i; - } else if (preDotState) |_preDotState| { - if (_preDotState != 1) { - preDotState = 1; - } - } - } else if (startDot != null) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = null; - } - } - - if (end) |_end| { - const _preDotState = if (preDotState) |_p| _p else 0; - const _startDot = if (startDot) |_s| _s else 0; - if (startDot == null or - // We saw a non-dot character immediately before the dot - (preDotState != null and _preDotState == 0) or - // The (right-most) trimmed path component is exactly '..' - (_preDotState == 1 and - _startDot == _end - 1 and - _startDot == startPart + 1)) - { - // Prefix with _ to avoid shadowing the identifier in the outer scope. - _name = path[startPart.._end]; - base = _name; - } else { - _name = path[startPart.._startDot]; - base = path[startPart.._end]; - ext = path[_startDot.._end]; - } - } - - // If the directory is the root, use the entire root as the `dir` including - // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the - // trailing slash (`C:\abc\def` -> `C:\abc`). - if (startPart > 0 and startPart != rootEnd) { - dir = path[0..(startPart - 1)]; - } else { - dir = root; - } - - return .{ .root = root, .dir = dir, .base = base, .ext = ext, .name = _name }; - } - - pub inline fn parsePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { - return parsePosixT(T, path).toJSObject(globalObject); - } - - pub inline fn parseWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T) JSC.JSValue { - return parseWindowsT(T, path).toJSObject(globalObject); - } - - pub inline fn parseJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isWindows: bool, path: []const T) JSC.JSValue { - return if (isWindows) parseWindowsJS_T(T, globalObject, path) else parsePosixJS_T(T, globalObject, path); - } - - pub fn parse(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const path_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "path", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - - const pathZStr = path_ptr.getZigString(globalObject); - if (pathZStr.len == 0) return (PathParsed(u8){}).toJSObject(globalObject); - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - const pathZSlice = pathZStr.toSlice(allocator); - defer pathZSlice.deinit(); - return parseJS_T(u8, globalObject, isWindows, pathZSlice.slice()); - } - - /// Based on Node v21.6.1 path.posix.relative: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1193 - pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) MaybeSlice(T) { - comptime validatePathT(T, "relativePosixT"); - - // validateString of `from` and `to` are performed in pub fn relative. - if (std.mem.eql(T, from, to)) { - return MaybeSlice(T){ .result = comptime L(T, "") }; - } - - // Trim leading forward slashes. - // Backed by expandable buf2 because fromOrig may be long. - const fromOrig = switch (resolvePosixT(T, &.{from}, buf2, buf3)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - const fromOrigLen = fromOrig.len; - // Backed by buf. - const toOrig = switch (resolvePosixT(T, &.{to}, buf, buf3)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - - if (std.mem.eql(T, fromOrig, toOrig)) { - return MaybeSlice(T){ .result = comptime L(T, "") }; - } - - const fromStart = 1; - const fromEnd = fromOrigLen; - const fromLen = fromEnd - fromStart; - const toOrigLen = toOrig.len; - var toStart: usize = 1; - const toLen = toOrigLen - toStart; - - // Compare paths to find the longest common path from root - const smallestLength = @min(fromLen, toLen); - // We use an optional value instead of -1, as in Node code, for easier number type use. - var lastCommonSep: ?usize = null; - - var matchesAllOfSmallest = false; - // Add a block to isolate `i`. - { - var i: usize = 0; - while (i < smallestLength) : (i += 1) { - const fromByte = fromOrig[fromStart + i]; - if (fromByte != toOrig[toStart + i]) { - break; - } else if (fromByte == CHAR_FORWARD_SLASH) { - lastCommonSep = i; - } - } - matchesAllOfSmallest = i == smallestLength; - } - if (matchesAllOfSmallest) { - if (toLen > smallestLength) { - if (toOrig[toStart + smallestLength] == CHAR_FORWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='/foo/bar'; to='/foo/bar/baz' - return MaybeSlice(T){ .result = toOrig[toStart + smallestLength + 1 .. toOrigLen] }; - } - if (smallestLength == 0) { - // We get here if `from` is the root - // For example: from='/'; to='/foo' - return MaybeSlice(T){ .result = toOrig[toStart + smallestLength .. toOrigLen] }; - } - } else if (fromLen > smallestLength) { - if (fromOrig[fromStart + smallestLength] == CHAR_FORWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='/foo/bar/baz'; to='/foo/bar' - lastCommonSep = smallestLength; - } else if (smallestLength == 0) { - // We get here if `to` is the root. - // For example: from='/foo/bar'; to='/' - lastCommonSep = 0; - } - } - } - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - // Backed by buf3. - var out: []const T = comptime L(T, ""); - // Add a block to isolate `i`. - { - // Generate the relative path based on the path difference between `to` - // and `from`. - - // Translated from the following JS code: - // for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { - var i: usize = fromStart + (if (lastCommonSep != null) lastCommonSep.? + 1 else 0); - while (i <= fromEnd) : (i += 1) { - if (i == fromEnd or fromOrig[i] == CHAR_FORWARD_SLASH) { - // Translated from the following JS code: - // out += out.length === 0 ? '..' : '/..'; - if (out.len > 0) { - bufOffset = bufSize; - bufSize += 3; - buf3[bufOffset] = CHAR_FORWARD_SLASH; - buf3[bufOffset + 1] = CHAR_DOT; - buf3[bufOffset + 2] = CHAR_DOT; - } else { - bufSize = 2; - buf3[0] = CHAR_DOT; - buf3[1] = CHAR_DOT; - } - out = buf3[0..bufSize]; - } - } - } - - // Lastly, append the rest of the destination (`to`) path that comes after - // the common path parts. - - // Translated from the following JS code: - // return `${out}${StringPrototypeSlice(to, toStart + lastCommonSep)}`; - toStart = if (lastCommonSep != null) toStart + lastCommonSep.? else 0; - const sliceSize = toOrigLen - toStart; - const outLen = out.len; - bufSize = outLen; - if (sliceSize > 0) { - bufOffset = bufSize; - bufSize += sliceSize; - // Use bun.copy because toOrig and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], toOrig[toStart..toOrigLen]); - } - if (outLen > 0) { - @memcpy(buf[0..outLen], out); - } - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - - /// Based on Node v21.6.1 path.win32.relative: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L500 - pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) MaybeSlice(T) { - comptime validatePathT(T, "relativeWindowsT"); - - // validateString of `from` and `to` are performed in pub fn relative. - if (std.mem.eql(T, from, to)) { - return MaybeSlice(T){ .result = comptime L(T, "") }; - } - - // Backed by expandable buf2 because fromOrig may be long. - const fromOrig = switch (resolveWindowsT(T, &.{from}, buf2, buf3)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - const fromOrigLen = fromOrig.len; - // Backed by buf. - const toOrig = switch (resolveWindowsT(T, &.{to}, buf, buf3)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - - if (std.mem.eql(T, fromOrig, toOrig) or - eqlIgnoreCaseT(T, fromOrig, toOrig)) - { - return MaybeSlice(T){ .result = comptime L(T, "") }; - } - - const toOrigLen = toOrig.len; - - // Trim leading backslashes - var fromStart: usize = 0; - while (fromStart < fromOrigLen and - fromOrig[fromStart] == CHAR_BACKWARD_SLASH) - { - fromStart += 1; - } - - // Trim trailing backslashes (applicable to UNC paths only) - var fromEnd = fromOrigLen; - while (fromEnd - 1 > fromStart and - fromOrig[fromEnd - 1] == CHAR_BACKWARD_SLASH) - { - fromEnd -= 1; - } - - const fromLen = fromEnd - fromStart; - - // Trim leading backslashes - var toStart: usize = 0; - while (toStart < toOrigLen and - toOrig[toStart] == CHAR_BACKWARD_SLASH) - { - toStart = toStart + 1; - } - - // Trim trailing backslashes (applicable to UNC paths only) - var toEnd = toOrigLen; - while (toEnd - 1 > toStart and - toOrig[toEnd - 1] == CHAR_BACKWARD_SLASH) - { - toEnd -= 1; - } - - const toLen = toEnd - toStart; - - // Compare paths to find the longest common path from root - const smallestLength = @min(fromLen, toLen); - // We use an optional value instead of -1, as in Node code, for easier number type use. - var lastCommonSep: ?usize = null; - - var matchesAllOfSmallest = false; - // Add a block to isolate `i`. - { - var i: usize = 0; - while (i < smallestLength) : (i += 1) { - const fromByte = fromOrig[fromStart + i]; - if (toLowerT(T, fromByte) != toLowerT(T, toOrig[toStart + i])) { - break; - } else if (fromByte == CHAR_BACKWARD_SLASH) { - lastCommonSep = i; - } - } - matchesAllOfSmallest = i == smallestLength; - } - - // We found a mismatch before the first common path separator was seen, so - // return the original `to`. - if (!matchesAllOfSmallest) { - if (lastCommonSep == null) { - return MaybeSlice(T){ .result = toOrig }; - } - } else { - if (toLen > smallestLength) { - if (toOrig[toStart + smallestLength] == CHAR_BACKWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='C:\foo\bar'; to='C:\foo\bar\baz' - return MaybeSlice(T){ .result = toOrig[toStart + smallestLength + 1 .. toOrigLen] }; - } - if (smallestLength == 2) { - // We get here if `from` is the device root. - // For example: from='C:\'; to='C:\foo' - return MaybeSlice(T){ .result = toOrig[toStart + smallestLength .. toOrigLen] }; - } - } - if (fromLen > smallestLength) { - if (fromOrig[fromStart + smallestLength] == CHAR_BACKWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='C:\foo\bar'; to='C:\foo' - lastCommonSep = smallestLength; - } else if (smallestLength == 2) { - // We get here if `to` is the device root. - // For example: from='C:\foo\bar'; to='C:\' - lastCommonSep = 3; - } - } - if (lastCommonSep == null) { - lastCommonSep = 0; - } - } - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - // Backed by buf3. - var out: []const T = comptime L(T, ""); - // Add a block to isolate `i`. - { - // Generate the relative path based on the path difference between `to` - // and `from`. - var i: usize = fromStart + (if (lastCommonSep != null) lastCommonSep.? + 1 else 0); - while (i <= fromEnd) : (i += 1) { - if (i == fromEnd or fromOrig[i] == CHAR_BACKWARD_SLASH) { - // Translated from the following JS code: - // out += out.length === 0 ? '..' : '\\..'; - if (out.len > 0) { - bufOffset = bufSize; - bufSize += 3; - buf3[bufOffset] = CHAR_BACKWARD_SLASH; - buf3[bufOffset + 1] = CHAR_DOT; - buf3[bufOffset + 2] = CHAR_DOT; - } else { - bufSize = 2; - buf3[0] = CHAR_DOT; - buf3[1] = CHAR_DOT; - } - out = buf3[0..bufSize]; - } - } - } - - // Translated from the following JS code: - // toStart += lastCommonSep; - if (lastCommonSep == null) { - // If toStart would go negative make it toOrigLen - 1 to - // mimic String#slice with a negative start. - toStart = if (toStart > 0) toStart - 1 else toOrigLen - 1; - } else { - toStart += lastCommonSep.?; - } - - // Lastly, append the rest of the destination (`to`) path that comes after - // the common path parts - const outLen = out.len; - if (outLen > 0) { - const sliceSize = toEnd - toStart; - bufSize = outLen; - if (sliceSize > 0) { - bufOffset = bufSize; - bufSize += sliceSize; - // Use bun.copy because toOrig and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], toOrig[toStart..toEnd]); - } - @memcpy(buf[0..outLen], out); - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - - if (toOrig[toStart] == CHAR_BACKWARD_SLASH) { - toStart += 1; - } - return MaybeSlice(T){ .result = toOrig[toStart..toEnd] }; - } - - pub inline fn relativePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) JSC.JSValue { - return switch (relativePosixT(T, from, to, buf, buf2, buf3)) { - .result => |r| toJSString(globalObject, r), - .err => |e| e.toJSC(globalObject), - }; - } - - pub inline fn relativeWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) JSC.JSValue { - return switch (relativeWindowsT(T, from, to, buf, buf2, buf3)) { - .result => |r| toJSString(globalObject, r), - .err => |e| e.toJSC(globalObject), - }; - } - - pub fn relativeJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, from: []const T, to: []const T) JSC.JSValue { - const bufLen = @max(from.len + to.len, PATH_SIZE(T)); - const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf); - const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf2); - const buf3 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf3); - return if (isWindows) relativeWindowsJS_T(T, globalObject, from, to, buf, buf2, buf3) else relativePosixJS_T(T, globalObject, from, to, buf, buf2, buf3); - } - - pub fn relative(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - const from_ptr = if (args_len > 0) args_ptr[0] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, from_ptr, "from", .{}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - const to_ptr = if (args_len > 1) args_ptr[1] else JSC.JSValue.jsUndefined(); - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, to_ptr, "to", .{}) catch { - return .zero; - }; - - const fromZigStr = from_ptr.getZigString(globalObject); - const toZigStr = to_ptr.getZigString(globalObject); - if ((fromZigStr.len + toZigStr.len) == 0) return from_ptr; - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - var fromZigSlice = fromZigStr.toSlice(allocator); - defer fromZigSlice.deinit(); - var toZigSlice = toZigStr.toSlice(allocator); - defer toZigSlice.deinit(); - return relativeJS_T(u8, globalObject, allocator, isWindows, fromZigSlice.slice(), toZigSlice.slice()); - } - - /// Based on Node v21.6.1 path.posix.resolve: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1095 - pub fn resolvePosixT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) MaybeSlice(T) { - comptime validatePathT(T, "resolvePosixT"); - - // Backed by expandable buf2 because resolvedPath may be long. - // We use buf2 here because resolvePosixT is called by other methods and using - // buf2 here avoids stepping on others' toes. - var resolvedPath: []const T = comptime L(T, ""); - var resolvedPathLen: usize = 0; - var resolvedAbsolute: bool = false; - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - var i_i64: i64 = if (paths.len == 0) -1 else @as(i64, @intCast(paths.len - 1)); - while (i_i64 > -2 and !resolvedAbsolute) : (i_i64 -= 1) { - var path: []const T = comptime L(T, ""); - if (i_i64 >= 0) { - path = paths[@as(usize, @intCast(i_i64))]; - } else { - // cwd is limited to MAX_PATH_BYTES. - var tmpBuf: [MAX_PATH_SIZE(T)]T = undefined; - path = switch (posixCwdT(T, &tmpBuf)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - } - // validateString of `path` is performed in pub fn resolve. - const len = path.len; - - // Skip empty paths. - if (len == 0) { - continue; - } - - // Translated from the following JS code: - // resolvedPath = `${path}/${resolvedPath}`; - if (resolvedPathLen > 0) { - bufOffset = len + 1; - bufSize = bufOffset + resolvedPathLen; - // Move all bytes to the right by path.len + 1 for the separator. - // Use bun.copy because resolvedPath and buf2 overlap. - bun.copy(u8, buf2[bufOffset..bufSize], resolvedPath); - } - bufSize = len; - @memcpy(buf2[0..bufSize], path); - bufSize += 1; - buf2[len] = CHAR_FORWARD_SLASH; - bufSize += resolvedPathLen; - - resolvedPath = buf2[0..bufSize]; - resolvedPathLen = bufSize; - resolvedAbsolute = path[0] == CHAR_FORWARD_SLASH; - } - - // Exit early for empty path. - if (resolvedPathLen == 0) { - return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) }; - } - - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.cwd() fails) - - // Normalize the path - resolvedPath = normalizeStringT(T, resolvedPath, !resolvedAbsolute, CHAR_FORWARD_SLASH, .posix, buf); - // resolvedPath is now backed by buf. - resolvedPathLen = resolvedPath.len; - - // Translated from the following JS code: - // if (resolvedAbsolute) { - // return `/${resolvedPath}`; - // } - if (resolvedAbsolute) { - bufSize = resolvedPathLen + 1; - // Use bun.copy because resolvedPath and buf overlap. - bun.copy(T, buf[1..bufSize], resolvedPath); - buf[0] = CHAR_FORWARD_SLASH; - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - // Translated from the following JS code: - // return resolvedPath.length > 0 ? resolvedPath : '.'; - return MaybeSlice(T){ .result = if (resolvedPathLen > 0) resolvedPath else comptime L(T, CHAR_STR_DOT) }; - } - - /// Based on Node v21.6.1 path.win32.resolve: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L162 - pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) MaybeSlice(T) { - comptime validatePathT(T, "resolveWindowsT"); - - const isSepT = isSepWindowsT; - var tmpBuf: [MAX_PATH_SIZE(T)]T = undefined; - - // Backed by tmpBuf. - var resolvedDevice: []const T = comptime L(T, ""); - var resolvedDeviceLen: usize = 0; - // Backed by expandable buf2 because resolvedTail may be long. - // We use buf2 here because resolvePosixT is called by other methods and using - // buf2 here avoids stepping on others' toes. - var resolvedTail: []const T = comptime L(T, ""); - var resolvedTailLen: usize = 0; - var resolvedAbsolute: bool = false; - - var bufOffset: usize = 0; - var bufSize: usize = 0; - var envPath: ?[]const T = null; - - var i_i64: i64 = if (paths.len == 0) -1 else @as(i64, @intCast(paths.len - 1)); - while (i_i64 > -2) : (i_i64 -= 1) { - // Backed by expandable buf2, to not conflict with buf2 backed resolvedTail, - // because path may be long. - var path: []const T = comptime L(T, ""); - if (i_i64 >= 0) { - path = paths[@as(usize, @intCast(i_i64))]; - // validateString of `path` is performed in pub fn resolve. - - // Skip empty paths. - if (path.len == 0) { - continue; - } - } else if (resolvedDeviceLen == 0) { - // cwd is limited to MAX_PATH_BYTES. - path = switch (getCwdT(T, &tmpBuf)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - } else { - // Translated from the following JS code: - // path = process.env[`=${resolvedDevice}`] || process.cwd(); - if (comptime Environment.isWindows) { - // Windows has the concept of drive-specific current working - // directories. If we've resolved a drive letter but not yet an - // absolute path, get cwd for that drive, or the process cwd if - // the drive cwd is not available. We're sure the device is not - // a UNC path at this points, because UNC paths are always absolute. - - // Translated from the following JS code: - // process.env[`=${resolvedDevice}`] - const key_w: [*:0]const u16 = brk: { - if (resolvedDeviceLen == 2 and resolvedDevice[1] == CHAR_COLON) { - // Fast path for device roots - break :brk &[3:0]u16{ '=', resolvedDevice[0], CHAR_COLON }; - } - bufSize = 1; - // Reuse buf2 for the env key because it's used to get the path. - buf2[0] = '='; - bufOffset = bufSize; - bufSize += resolvedDeviceLen; - @memcpy(buf2[bufOffset..bufSize], resolvedDevice); - if (T == u16) { - break :brk buf2[0..bufSize]; - } else { - var u16Buf: bun.WPathBuffer = undefined; - bufSize = std.unicode.utf8ToUtf16Le(&u16Buf, buf2[0..bufSize]) catch { - return MaybeSlice(T).errnoSys(0, Syscall.Tag.getenv).?; - }; - break :brk u16Buf[0..bufSize :0]; - } - }; - // Zig's std.posix.getenvW has logic to support keys like `=${resolvedDevice}`: - // https://github.com/ziglang/zig/blob/7bd8b35a3dfe61e59ffea39d464e84fbcdead29a/lib/std/os.zig#L2126-L2130 - // - // TODO: Enable test once spawnResult.stdout works on Windows. - // test/js/node/path/resolve.test.js - if (std.process.getenvW(key_w)) |r| { - if (T == u16) { - bufSize = r.len; - @memcpy(buf2[0..bufSize], r); - } else { - // Reuse buf2 because it's used for path. - bufSize = std.unicode.utf16leToUtf8(buf2, r) catch { - return MaybeSlice(T).errnoSys(0, Syscall.Tag.getcwd).?; - }; - } - envPath = buf2[0..bufSize]; - } - } - if (envPath) |_envPath| { - path = _envPath; - } else { - // cwd is limited to MAX_PATH_BYTES. - path = switch (getCwdT(T, &tmpBuf)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - // We must set envPath here so that it doesn't hit the null check just below. - envPath = path; - } - - // Verify that a cwd was found and that it actually points - // to our drive. If not, default to the drive's root. - - // Translated from the following JS code: - // if (path === undefined || - // (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !== - // StringPrototypeToLowerCase(resolvedDevice) && - // StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) { - if (envPath == null or - (path[2] == CHAR_BACKWARD_SLASH and - !eqlIgnoreCaseT(T, path[0..2], resolvedDevice))) - { - // Translated from the following JS code: - // path = `${resolvedDevice}\\`; - bufSize = resolvedDeviceLen; - @memcpy(buf2[0..bufSize], resolvedDevice); - bufOffset = bufSize; - bufSize += 1; - buf2[bufOffset] = CHAR_BACKWARD_SLASH; - path = buf2[0..bufSize]; - } - } - - const len = path.len; - var rootEnd: usize = 0; - // Backed by tmpBuf or an anonymous buffer. - var device: []const T = comptime L(T, ""); - // Prefix with _ to avoid shadowing the identifier in the outer scope. - var _isAbsolute: bool = false; - const byte0 = if (len > 0) path[0] else 0; - - // Try to match a root - if (len == 1) { - if (isSepT(T, byte0)) { - // `path` contains just a path separator - rootEnd = 1; - _isAbsolute = true; - } - } else if (isSepT(T, byte0)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an - // absolute path of some kind (UNC or otherwise) - _isAbsolute = true; - - if (isSepT(T, path[1])) { - // Matched double path separator at the beginning - var j: usize = 2; - var last: usize = j; - // Match 1 or more non-path separators - while (j < len and - !isSepT(T, path[j])) - { - j += 1; - } - if (j < len and j != last) { - const firstPart = path[last..j]; - // Matched! - last = j; - // Match 1 or more path separators - while (j < len and - isSepT(T, path[j])) - { - j += 1; - } - if (j < len and j != last) { - // Matched! - last = j; - // Match 1 or more non-path separators - while (j < len and - !isSepT(T, path[j])) - { - j += 1; - } - if (j == len or j != last) { - // We matched a UNC root - - // Translated from the following JS code: - // device = - // `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; - // rootEnd = j; - bufSize = 2; - tmpBuf[0] = CHAR_BACKWARD_SLASH; - tmpBuf[1] = CHAR_BACKWARD_SLASH; - bufOffset = bufSize; - bufSize += firstPart.len; - @memcpy(tmpBuf[bufOffset..bufSize], firstPart); - bufOffset = bufSize; - bufSize += 1; - tmpBuf[bufOffset] = CHAR_BACKWARD_SLASH; - const slice = path[last..j]; - bufOffset = bufSize; - bufSize += slice.len; - @memcpy(tmpBuf[bufOffset..bufSize], slice); - - device = tmpBuf[0..bufSize]; - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRootT(T, byte0) and - path[1] == CHAR_COLON) - { - // Possible device root - device = &[2]T{ byte0, CHAR_COLON }; - rootEnd = 2; - if (len > 2 and isSepT(T, path[2])) { - // Treat separator following the drive name as an absolute path - // indicator - _isAbsolute = true; - rootEnd = 3; - } - } - - const deviceLen = device.len; - if (deviceLen > 0) { - if (resolvedDeviceLen > 0) { - // Translated from the following JS code: - // if (StringPrototypeToLowerCase(device) !== - // StringPrototypeToLowerCase(resolvedDevice)) - if (!eqlIgnoreCaseT(T, device, resolvedDevice)) { - // This path points to another device, so it is not applicable - continue; - } - } else { - // Translated from the following JS code: - // resolvedDevice = device; - bufSize = device.len; - // Copy device over if it's backed by an anonymous buffer. - if (device.ptr != tmpBuf[0..].ptr) { - @memcpy(tmpBuf[0..bufSize], device); - } - resolvedDevice = tmpBuf[0..bufSize]; - resolvedDeviceLen = bufSize; - } - } - - if (resolvedAbsolute) { - if (resolvedDeviceLen > 0) { - break; - } - } else { - // Translated from the following JS code: - // resolvedTail = `${StringPrototypeSlice(path, rootEnd)}\\${resolvedTail}`; - const sliceLen = len - rootEnd; - if (resolvedTailLen > 0) { - bufOffset = sliceLen + 1; - bufSize = bufOffset + resolvedTailLen; - // Move all bytes to the right by path slice.len + 1 for the separator - // Use bun.copy because resolvedTail and buf2 overlap. - bun.copy(u8, buf2[bufOffset..bufSize], resolvedTail); - } - bufSize = sliceLen; - if (sliceLen > 0) { - @memcpy(buf2[0..bufSize], path[rootEnd..len]); - } - bufOffset = bufSize; - bufSize += 1; - buf2[bufOffset] = CHAR_BACKWARD_SLASH; - bufSize += resolvedTailLen; - - resolvedTail = buf2[0..bufSize]; - resolvedTailLen = bufSize; - resolvedAbsolute = _isAbsolute; - - if (_isAbsolute and resolvedDeviceLen > 0) { - break; - } - } - } - - // Exit early for empty path. - if (resolvedTailLen == 0) { - return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) }; - } - - // At this point, the path should be resolved to a full absolute path, - // but handle relative paths to be safe (might happen when std.process.cwdAlloc() - // fails) - - // Normalize the tail path - resolvedTail = normalizeStringT(T, resolvedTail, !resolvedAbsolute, CHAR_BACKWARD_SLASH, .windows, buf); - // resolvedTail is now backed by buf. - resolvedTailLen = resolvedTail.len; - - // Translated from the following JS code: - // resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` - if (resolvedAbsolute) { - bufOffset = resolvedDeviceLen + 1; - bufSize = bufOffset + resolvedTailLen; - // Use bun.copy because resolvedTail and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], resolvedTail); - buf[resolvedDeviceLen] = CHAR_BACKWARD_SLASH; - @memcpy(buf[0..resolvedDeviceLen], resolvedDevice); - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - // Translated from the following JS code: - // : `${resolvedDevice}${resolvedTail}` || '.' - if ((resolvedDeviceLen + resolvedTailLen) > 0) { - bufOffset = resolvedDeviceLen; - bufSize = bufOffset + resolvedTailLen; - // Use bun.copy because resolvedTail and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], resolvedTail); - @memcpy(buf[0..resolvedDeviceLen], resolvedDevice); - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) }; - } - - pub inline fn resolvePosixJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { - return switch (resolvePosixT(T, paths, buf, buf2)) { - .result => |r| toJSString(globalObject, r), - .err => |e| e.toJSC(globalObject), - }; - } - - pub inline fn resolveWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) JSC.JSValue { - return switch (resolveWindowsT(T, paths, buf, buf2)) { - .result => |r| toJSString(globalObject, r), - .err => |e| e.toJSC(globalObject), - }; - } - - pub fn resolveJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, paths: []const []const T) JSC.JSValue { - // Adding 8 bytes when Windows for the possible UNC root. - var bufLen: usize = if (isWindows) 8 else 0; - for (paths) |path| bufLen += if (bufLen > 0 and path.len > 0) path.len + 1 else path.len; - bufLen = @max(bufLen, PATH_SIZE(T)); - const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf); - const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf2); - return if (isWindows) resolveWindowsJS_T(T, globalObject, paths, buf, buf2) else resolvePosixJS_T(T, globalObject, paths, buf, buf2); - } - - pub fn resolve(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - - var arena = bun.ArenaAllocator.init(heap_allocator); - defer arena.deinit(); - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_large, arena.allocator()); - const allocator = stack_fallback.get(); - - var paths = allocator.alloc(string, args_len) catch bun.outOfMemory(); - defer allocator.free(paths); - - for (0..args_len, args_ptr) |i, path_ptr| { - // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. - validateString(globalObject, path_ptr, "paths[{d}]", .{i}) catch { - // Returning .zero translates to a nullprt JSC.JSValue. - return .zero; - }; - const pathZStr = path_ptr.getZigString(globalObject); - paths[i] = if (pathZStr.len > 0) pathZStr.toSlice(allocator).slice() else ""; - } - return resolveJS_T(u8, globalObject, allocator, isWindows, paths); - } - - /// Based on Node v21.6.1 path.win32.toNamespacedPath: - /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L622 - pub fn toNamespacedPathWindowsT(comptime T: type, path: []const T, buf: []T, buf2: []T) MaybeSlice(T) { - comptime validatePathT(T, "toNamespacedPathWindowsT"); - - // validateString of `path` is performed in pub fn toNamespacedPath. - // Backed by buf. - const resolvedPath = switch (resolveWindowsT(T, &.{path}, buf, buf2)) { - .result => |r| r, - .err => |e| return MaybeSlice(T){ .err = e }, - }; - - const len = resolvedPath.len; - if (len <= 2) { - return MaybeSlice(T){ .result = path }; - } - - var bufOffset: usize = 0; - var bufSize: usize = 0; - - const byte0 = resolvedPath[0]; - if (byte0 == CHAR_BACKWARD_SLASH) { - // Possible UNC root - if (resolvedPath[1] == CHAR_BACKWARD_SLASH) { - const byte2 = resolvedPath[2]; - if (byte2 != CHAR_QUESTION_MARK and byte2 != CHAR_DOT) { - // Matched non-long UNC root, convert the path to a long UNC path - - // Translated from the following JS code: - // return `\\\\?\\UNC\\${StringPrototypeSlice(resolvedPath, 2)}`; - bufOffset = 6; - bufSize = len + 6; - // Move all bytes to the right by 6 so that the first two bytes are - // overwritten by "\\\\?\\UNC\\" which is 8 bytes long. - // Use bun.copy because resolvedPath and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], resolvedPath); - // Equiv to std.os.windows.NamespacePrefix.verbatim - // https://github.com/ziglang/zig/blob/dcaf43674e35372e1d28ab12c4c4ff9af9f3d646/lib/std/os/windows.zig#L2358-L2374 - buf[0] = CHAR_BACKWARD_SLASH; - buf[1] = CHAR_BACKWARD_SLASH; - buf[2] = CHAR_QUESTION_MARK; - buf[3] = CHAR_BACKWARD_SLASH; - buf[4] = 'U'; - buf[5] = 'N'; - buf[6] = 'C'; - buf[7] = CHAR_BACKWARD_SLASH; - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - } - } else if (isWindowsDeviceRootT(T, byte0) and - resolvedPath[1] == CHAR_COLON and - resolvedPath[2] == CHAR_BACKWARD_SLASH) - { - // Matched device root, convert the path to a long UNC path - - // Translated from the following JS code: - // return `\\\\?\\${resolvedPath}` - bufOffset = 4; - bufSize = len + 4; - // Move all bytes to the right by 4 - // Use bun.copy because resolvedPath and buf overlap. - bun.copy(T, buf[bufOffset..bufSize], resolvedPath); - // Equiv to std.os.windows.NamespacePrefix.verbatim - // https://github.com/ziglang/zig/blob/dcaf43674e35372e1d28ab12c4c4ff9af9f3d646/lib/std/os/windows.zig#L2358-L2374 - buf[0] = CHAR_BACKWARD_SLASH; - buf[1] = CHAR_BACKWARD_SLASH; - buf[2] = CHAR_QUESTION_MARK; - buf[3] = CHAR_BACKWARD_SLASH; - return MaybeSlice(T){ .result = buf[0..bufSize] }; - } - return MaybeSlice(T){ .result = resolvedPath }; - } - - pub inline fn toNamespacedPathWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, path: []const T, buf: []T, buf2: []T) JSC.JSValue { - return switch (toNamespacedPathWindowsT(T, path, buf, buf2)) { - .result => |r| toJSString(globalObject, r), - .err => |e| e.toJSC(globalObject), - }; - } - - pub fn toNamespacedPathJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, path: []const T) JSC.JSValue { - if (!isWindows or path.len == 0) return toJSString(globalObject, path); - const bufLen = @max(path.len, PATH_SIZE(T)); - const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf); - const buf2 = allocator.alloc(T, bufLen) catch bun.outOfMemory(); - defer allocator.free(buf2); - return toNamespacedPathWindowsJS_T(T, globalObject, path, buf, buf2); - } - - pub fn toNamespacedPath(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { - if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); - if (args_len == 0) return JSC.JSValue.jsUndefined(); - var path_ptr = args_ptr[0]; - - // Based on Node v21.6.1 path.win32.toNamespacedPath and path.posix.toNamespacedPath: - // https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L624 - // https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1269 - // - // Act as an identity function for non-string values and non-Windows platforms. - if (!isWindows or !path_ptr.isString()) return path_ptr; - const pathZStr = path_ptr.getZigString(globalObject); - const len = pathZStr.len; - if (len == 0) return path_ptr; - - var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, JSC.getAllocator(globalObject)); - const allocator = stack_fallback.get(); - - const pathZSlice = pathZStr.toSlice(allocator); - defer pathZSlice.deinit(); - return toNamespacedPathJS_T(u8, globalObject, allocator, isWindows, pathZSlice.slice()); - } - - pub const Export = shim.exportFunctions(.{ - .basename = basename, - .dirname = dirname, - .extname = extname, - .format = format, - .isAbsolute = isAbsolute, - .join = join, - .normalize = normalize, - .parse = parse, - .relative = relative, - .resolve = resolve, - .toNamespacedPath = toNamespacedPath, - }); - - pub const Extern = [_][]const u8{"create"}; - - comptime { - if (!is_bindgen) { - @export(Path.basename, .{ - .name = Export[0].symbol_name, - }); - @export(Path.dirname, .{ - .name = Export[1].symbol_name, - }); - @export(Path.extname, .{ - .name = Export[2].symbol_name, - }); - @export(Path.format, .{ - .name = Export[3].symbol_name, - }); - @export(Path.isAbsolute, .{ - .name = Export[4].symbol_name, - }); - @export(Path.join, .{ - .name = Export[5].symbol_name, - }); - @export(Path.normalize, .{ - .name = Export[6].symbol_name, - }); - @export(Path.parse, .{ - .name = Export[7].symbol_name, - }); - @export(Path.relative, .{ - .name = Export[8].symbol_name, - }); - @export(Path.resolve, .{ - .name = Export[9].symbol_name, - }); - @export(Path.toNamespacedPath, .{ - .name = Export[10].symbol_name, - }); - } - } -}; - -pub const Process = struct { - pub fn getArgv0(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - return JSC.ZigString.fromUTF8(bun.argv[0]).toValueGC(globalObject); - } - - pub fn getExecPath(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - const out = bun.selfExePath() catch { - // if for any reason we are unable to get the executable path, we just return argv[0] - return getArgv0(globalObject); - }; - - return JSC.ZigString.fromUTF8(out).toValueGC(globalObject); - } - - pub fn getExecArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - var sfb = std.heap.stackFallback(4096, globalObject.allocator()); - const temp_alloc = sfb.get(); - const vm = globalObject.bunVM(); - - if (vm.worker) |worker| { - // was explicitly overridden for the worker? - if (worker.execArgv) |execArgv| { - const array = JSC.JSValue.createEmptyArray(globalObject, execArgv.len); - for (0..execArgv.len) |i| { - array.putIndex(globalObject, @intCast(i), bun.String.init(execArgv[i]).toJS(globalObject)); - } - return array; - } - } - - var args = std.ArrayList(bun.String).initCapacity(temp_alloc, bun.argv.len - 1) catch bun.outOfMemory(); - defer args.deinit(); - - var seen_run = false; - var prev: ?[]const u8 = null; - - // we re-parse the process argv to extract execArgv, since this is a very uncommon operation - // it isn't worth doing this as a part of the CLI - for (bun.argv[@min(1, bun.argv.len)..]) |arg| { - defer prev = arg; - - if (arg.len >= 1 and arg[0] == '-') { - args.append(bun.String.createUTF8(arg)) catch bun.outOfMemory(); - continue; - } - - if (!seen_run and bun.strings.eqlComptime(arg, "run")) { - seen_run = true; - continue; - } - - // A set of execArgv args consume an extra argument, so we do not want to - // confuse these with script names. - const map = bun.ComptimeStringMap(void, comptime brk: { - const auto_params = bun.CLI.Arguments.auto_params; - const KV = struct { []const u8, void }; - var entries: [auto_params.len]KV = undefined; - var i = 0; - for (auto_params) |param| { - if (param.takes_value != .none) { - if (param.names.long) |name| { - entries[i] = .{ "--" ++ name, {} }; - i += 1; - } - if (param.names.short) |name| { - entries[i] = .{ &[_]u8{ '-', name }, {} }; - i += 1; - } - } - } - - var result: [i]KV = undefined; - @memcpy(&result, entries[0..i]); - break :brk result; - }); - - if (prev) |p| if (map.has(p)) { - args.append(bun.String.createUTF8(arg)) catch @panic("OOM"); - continue; - }; - - // we hit the script name - break; - } - - return bun.String.toJSArray(globalObject, args.items); - } - - pub fn getArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - const vm = globalObject.bunVM(); - - // Allocate up to 32 strings in stack - var stack_fallback_allocator = std.heap.stackFallback( - 32 * @sizeOf(JSC.ZigString) + (bun.MAX_PATH_BYTES + 1) + 32, - heap_allocator, - ); - const allocator = stack_fallback_allocator.get(); - - var args_count: usize = vm.argv.len; - if (vm.worker) |worker| { - args_count = if (worker.argv) |argv| argv.len else 0; - } - - const args = allocator.alloc( - bun.String, - // argv omits "bun" because it could be "bun run" or "bun" and it's kind of ambiguous - // argv also omits the script name - args_count + 2, - ) catch bun.outOfMemory(); - var args_list = std.ArrayListUnmanaged(bun.String){ .items = args, .capacity = args.len }; - args_list.items.len = 0; - - if (vm.standalone_module_graph != null) { - // Don't break user's code because they did process.argv.slice(2) - // Even if they didn't type "bun", we still want to add it as argv[0] - args_list.appendAssumeCapacity( - bun.String.static("bun"), - ); - } else { - const exe_path = bun.selfExePath() catch null; - args_list.appendAssumeCapacity( - if (exe_path) |str| bun.String.fromUTF8(str) else bun.String.static("bun"), - ); - } - - if (vm.main.len > 0) - args_list.appendAssumeCapacity(bun.String.fromUTF8(vm.main)); - - defer allocator.free(args); - - if (vm.worker) |worker| { - if (worker.argv) |argv| { - for (argv) |arg| { - args_list.appendAssumeCapacity(bun.String.init(arg)); - } - } - } else { - for (vm.argv) |arg| { - const str = bun.String.fromUTF8(arg); - // https://github.com/yargs/yargs/blob/adb0d11e02c613af3d9427b3028cc192703a3869/lib/utils/process-argv.ts#L1 - args_list.appendAssumeCapacity(str); - } - } - - return bun.String.toJSArray(globalObject, args_list.items); - } - - pub fn getCwd(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - var buf: bun.PathBuffer = undefined; - return switch (Path.getCwd(&buf)) { - .result => |r| toJSString(globalObject, r), - .err => |e| e.toJSC(globalObject), - }; - } - - pub fn setCwd(globalObject: *JSC.JSGlobalObject, to: *JSC.ZigString) callconv(.C) JSC.JSValue { - if (to.len == 0) { - return JSC.toInvalidArguments("path is required", .{}, globalObject.ref()); + pub fn setCwd(globalObject: *JSC.JSGlobalObject, to: *JSC.ZigString) callconv(.C) JSC.JSValue { + if (to.len == 0) { + globalObject.throwInvalidArguments("Expected path to be a non-empty string", .{}); + return .zero; } var buf: bun.PathBuffer = undefined; const slice = to.sliceZBuf(&buf) catch { - return JSC.toInvalidArguments("Invalid path", .{}, globalObject.ref()); + globalObject.throw("Invalid path", .{}); + return .zero; }; switch (Syscall.chdir(slice)) { @@ -5048,22 +2019,29 @@ pub const Process = struct { // However, this might be called many times in a row, so we use a pre-allocated buffer // that way we don't have to worry about garbage collector const fs = JSC.VirtualMachine.get().bundler.fs; - fs.top_level_dir = switch (Path.getCwd(&fs.top_level_dir_buf)) { + const into_cwd_buf = switch (bun.sys.getcwd(&buf)) { .result => |r| r, - .err => { + .err => |err| { _ = Syscall.chdir(@as([:0]const u8, @ptrCast(fs.top_level_dir))); - return JSC.toInvalidArguments("Invalid path", .{}, globalObject.ref()); + globalObject.throwValue(err.toJSC(globalObject)); + return .zero; }, }; + @memcpy(fs.top_level_dir_buf[0..into_cwd_buf.len], into_cwd_buf); + fs.top_level_dir = fs.top_level_dir_buf[0..into_cwd_buf.len]; const len = fs.top_level_dir.len; fs.top_level_dir_buf[len] = std.fs.path.sep; fs.top_level_dir_buf[len + 1] = 0; fs.top_level_dir = fs.top_level_dir_buf[0 .. len + 1]; - return JSC.JSValue.jsUndefined(); + var str = bun.String.createUTF8(strings.withoutTrailingSlash(fs.top_level_dir)); + return str.transferToJS(globalObject); + }, + .err => |e| { + globalObject.throwValue(e.toJSC(globalObject)); + return .zero; }, - .err => |e| return e.toJSC(globalObject), } } @@ -5075,8 +2053,9 @@ pub const Process = struct { return; } + vm.exit_handler.exit_code = code; vm.onExit(); - bun.Global.exit(code); + vm.globalExit(); } pub export const Bun__version: [*:0]const u8 = "v" ++ bun.Global.package_json_version; @@ -5091,6 +2070,7 @@ pub const Process = struct { pub export const Bun__versions_tinycc: [*:0]const u8 = bun.Global.versions.tinycc; pub export const Bun__versions_lolhtml: [*:0]const u8 = bun.Global.versions.lolhtml; pub export const Bun__versions_c_ares: [*:0]const u8 = bun.Global.versions.c_ares; + pub export const Bun__versions_libdeflate: [*:0]const u8 = bun.Global.versions.libdeflate; pub export const Bun__versions_usockets: [*:0]const u8 = bun.Environment.git_sha; pub export const Bun__version_sha: [*:0]const u8 = bun.Environment.git_sha; pub export const Bun__versions_lshpack: [*:0]const u8 = bun.Global.versions.lshpack; @@ -5099,5 +2079,4 @@ pub const Process = struct { comptime { std.testing.refAllDecls(Process); - std.testing.refAllDecls(Path); } diff --git a/src/bun.js/node/util/parse_args.zig b/src/bun.js/node/util/parse_args.zig index ac21dbbc57b49..c047fddd258dc 100644 --- a/src/bun.js/node/util/parse_args.zig +++ b/src/bun.js/node/util/parse_args.zig @@ -193,7 +193,7 @@ fn checkOptionLikeValue(globalThis: *JSGlobalObject, token: OptionToken) ParseAr var err: JSValue = undefined; if (token.raw.asBunString(globalThis).hasPrefixComptime("--")) { err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{}' argument is ambiguous.\nDid you forget to specify the option argument for '{}'?\nTo specify an option argument starting with a dash use '{}=-XYZ'.", .{ raw_name, raw_name, raw_name }, globalThis, @@ -201,7 +201,7 @@ fn checkOptionLikeValue(globalThis: *JSGlobalObject, token: OptionToken) ParseAr } else { const token_name = token.name.asBunString(globalThis); err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{}' argument is ambiguous.\nDid you forget to specify the option argument for '{}'?\nTo specify an option argument starting with a dash use '--{}=-XYZ' or '{}-XYZ'.", .{ raw_name, raw_name, token_name, raw_name }, globalThis, @@ -219,7 +219,7 @@ fn checkOptionUsage(globalThis: *JSGlobalObject, options: []const OptionDefiniti switch (option.type) { .string => if (token.value == .jsvalue and !token.value.jsvalue.isString()) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{s}{s}{s}--{s} ' argument missing", .{ if (!option.short_name.isEmpty()) "-" else "", @@ -234,7 +234,7 @@ fn checkOptionUsage(globalThis: *JSGlobalObject, options: []const OptionDefiniti }, .boolean => if (token.value != .jsvalue or !token.value.jsvalue.isUndefined()) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{s}{s}{s}--{s}' does not take an argument", .{ if (!option.short_name.isEmpty()) "-" else "", @@ -252,12 +252,12 @@ fn checkOptionUsage(globalThis: *JSGlobalObject, options: []const OptionDefiniti const raw_name = OptionToken.RawNameFormatter{ .token = token, .globalThis = globalThis }; const err = if (allow_positionals) (JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_UNKNOWN_OPTION, + .ERR_PARSE_ARGS_UNKNOWN_OPTION, "Unknown option '{}'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- \"{}\"", .{ raw_name, raw_name }, globalThis, )) else (JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_UNKNOWN_OPTION, + .ERR_PARSE_ARGS_UNKNOWN_OPTION, "Unknown option '{}'", .{raw_name}, globalThis, @@ -328,7 +328,7 @@ fn parseOptionDefinitions(globalThis: *JSGlobalObject, options_obj: JSValue, opt try validateString(globalThis, short_option, "options.{s}.short", .{option.long_name}); var short_option_str = short_option.toBunString(globalThis); if (short_option_str.length() != 1) { - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "options.{s}.short must be a single character", .{option.long_name}, globalThis); + const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "options.{s}.short must be a single character", .{option.long_name}, globalThis); globalThis.vm().throwError(globalThis, err); return error.ParseError; } @@ -592,7 +592,7 @@ const ParseArgsState = struct { .positional => |token| { if (!this.allow_positionals) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL, + .ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL, "Unexpected argument '{s}'. This command does not take positional arguments", .{token.value.asBunString(globalThis)}, globalThis, @@ -618,7 +618,7 @@ const ParseArgsState = struct { // reuse JSValue for the kind names: "positional", "option", "option-terminator" const kind_idx = @intFromEnum(token_generic); const kind_jsvalue = this.kinds_jsvalues[kind_idx] orelse kindval: { - const val = String.static(@as(string, @tagName(token_generic))).toJS(globalThis); + const val = String.static(@tagName(token_generic)).toJS(globalThis); this.kinds_jsvalues[kind_idx] = val; break :kindval val; }; @@ -652,7 +652,7 @@ const ParseArgsState = struct { pub fn parseArgs( globalThis: *JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSValue { +) JSValue { JSC.markBinding(@src()); const arguments = callframe.arguments(1).slice(); const config = if (arguments.len > 0) arguments[0] else JSValue.undefined; @@ -665,6 +665,11 @@ pub fn parseArgs( }; } +comptime { + const parseArgsFn = JSC.toJSHostFunction(parseArgs); + @export(parseArgsFn, .{ .name = "Bun__NodeUtil__jsParseArgs" }); +} + pub fn parseArgsImpl(globalThis: *JSGlobalObject, config_obj: JSValue) !JSValue { // // Phase 0: parse the config object diff --git a/src/bun.js/node/util/validators.zig b/src/bun.js/node/util/validators.zig index 6ea243b67c0cd..f13de796c2118 100644 --- a/src/bun.js/node/util/validators.zig +++ b/src/bun.js/node/util/validators.zig @@ -16,23 +16,22 @@ pub fn getTypeName(globalObject: *JSGlobalObject, value: JSValue) ZigString { pub fn throwErrInvalidArgValue( globalThis: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) !void { @setCold(true); - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, fmt, args, globalThis); + const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fmt, args, globalThis); globalThis.vm().throwError(globalThis, err); return error.InvalidArgument; } pub fn throwErrInvalidArgTypeWithMessage( globalThis: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) !void { @setCold(true); - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, fmt, args, globalThis); - globalThis.vm().throwError(globalThis, err); + globalThis.ERR_INVALID_ARG_TYPE(fmt, args).throw(); return error.InvalidArgument; } @@ -50,7 +49,7 @@ pub fn throwErrInvalidArgType( pub fn throwRangeError( globalThis: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) !void { @setCold(true); diff --git a/src/bun.js/rare_data.zig b/src/bun.js/rare_data.zig index 9cbc965718ec2..45ebac3282559 100644 --- a/src/bun.js/rare_data.zig +++ b/src/bun.js/rare_data.zig @@ -23,14 +23,15 @@ stderr_store: ?*Blob.Store = null, stdin_store: ?*Blob.Store = null, stdout_store: ?*Blob.Store = null, +postgresql_context: JSC.Postgres.PostgresSQLContext = .{}, + entropy_cache: ?*EntropyCache = null, hot_map: ?HotMap = null, // TODO: make this per JSGlobalObject instead of global // This does not handle ShadowRealm correctly! -tail_cleanup_hook: ?*CleanupHook = null, -cleanup_hook: ?*CleanupHook = null, +cleanup_hooks: std.ArrayListUnmanaged(CleanupHook) = .{}, file_polls_: ?*Async.FilePoll.Store = null, @@ -43,7 +44,7 @@ mime_types: ?bun.http.MimeType.Map = null, node_fs_stat_watcher_scheduler: ?*StatWatcherScheduler = null, listening_sockets_for_watch_mode: std.ArrayListUnmanaged(bun.FileDescriptor) = .{}, -listening_sockets_for_watch_mode_lock: bun.Lock = bun.Lock.init(), +listening_sockets_for_watch_mode_lock: bun.Lock = .{}, temp_pipe_read_buffer: ?*PipeReadBuffer = null, @@ -154,7 +155,7 @@ pub const HotMap = struct { pub fn filePolls(this: *RareData, vm: *JSC.VirtualMachine) *Async.FilePoll.Store { return this.file_polls_ orelse { this.file_polls_ = vm.allocator.create(Async.FilePoll.Store) catch unreachable; - this.file_polls_.?.* = Async.FilePoll.Store.init(vm.allocator); + this.file_polls_.?.* = Async.FilePoll.Store.init(); return this.file_polls_.?; }; } @@ -218,7 +219,6 @@ pub const EntropyCache = struct { }; pub const CleanupHook = struct { - next: ?*CleanupHook = null, ctx: ?*anyopaque, func: Function, globalThis: *JSC.JSGlobalObject, @@ -231,13 +231,12 @@ pub const CleanupHook = struct { self.func(self.ctx); } - pub fn from( + pub fn init( globalThis: *JSC.JSGlobalObject, ctx: ?*anyopaque, func: CleanupHook.Function, ) CleanupHook { return .{ - .next = null, .ctx = ctx, .func = func, .globalThis = globalThis, @@ -253,14 +252,7 @@ pub fn pushCleanupHook( ctx: ?*anyopaque, func: CleanupHook.Function, ) void { - const hook = JSC.VirtualMachine.get().allocator.create(CleanupHook) catch unreachable; - hook.* = CleanupHook.from(globalThis, ctx, func); - if (this.cleanup_hook == null) { - this.cleanup_hook = hook; - this.tail_cleanup_hook = hook; - } else { - this.cleanup_hook.?.next = hook; - } + this.cleanup_hooks.append(bun.default_allocator, CleanupHook.init(globalThis, ctx, func)) catch bun.outOfMemory(); } pub fn boringEngine(rare: *RareData) *BoringSSL.ENGINE { @@ -390,3 +382,16 @@ pub fn nodeFSStatWatcherScheduler(rare: *RareData, vm: *JSC.VirtualMachine) *Sta return rare.node_fs_stat_watcher_scheduler.?; }; } + +pub fn deinit(this: *RareData) void { + if (this.temp_pipe_read_buffer) |pipe| { + this.temp_pipe_read_buffer = null; + bun.default_allocator.destroy(pipe); + } + + if (this.boring_ssl_engine) |engine| { + _ = bun.BoringSSL.ENGINE_free(engine); + } + + this.cleanup_hooks.clearAndFree(bun.default_allocator); +} diff --git a/src/bun.js/test/diff_format.zig b/src/bun.js/test/diff_format.zig index 5d28d2532cd9e..c907d16fd420f 100644 --- a/src/bun.js/test/diff_format.zig +++ b/src/bun.js/test/diff_format.zig @@ -148,15 +148,15 @@ pub const DiffFormatter = struct { var formatter = ConsoleObject.Formatter{ .globalThis = this.globalThis, .quote_strings = true }; if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(fmt, true), .{ - expected.toFmt(this.globalThis, &formatter), - received.toFmt(this.globalThis, &formatter), + expected.toFmt(&formatter), + received.toFmt(&formatter), }); return; } try writer.print(Output.prettyFmt(fmt, true), .{ - expected.toFmt(this.globalThis, &formatter), - received.toFmt(this.globalThis, &formatter), + expected.toFmt(&formatter), + received.toFmt(&formatter), }); return; }, diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index 0c2dba92d6848..b32b9342f198f 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -42,7 +42,6 @@ const JSTypeOfMap = bun.ComptimeStringMap([]const u8, .{ pub var active_test_expectation_counter: Counter = .{}; pub var is_expecting_assertions: bool = false; pub var is_expecting_assertions_count: bool = false; -pub var expected_assertions_number: u32 = 0; const log = bun.Output.scoped(.expect, false); @@ -93,7 +92,27 @@ pub const Expect = struct { not: bool = false, - _: u5 = undefined, // padding + // This was originally padding. + // We don't use all the bits in the u5, so if you need to reuse this elsewhere, you could. + asymmetric_matcher_constructor_type: AsymmetricMatcherConstructorType = .none, + + pub const AsymmetricMatcherConstructorType = enum(u5) { + none = 0, + Symbol = 1, + String = 2, + Object = 3, + Array = 4, + BigInt = 5, + Boolean = 6, + Number = 7, + Promise = 8, + InstanceOf = 9, + + extern fn AsymmetricMatcherConstructorType__fromJS(globalObject: *JSGlobalObject, value: JSValue) u8; + pub fn fromJS(globalObject: *JSGlobalObject, value: JSValue) AsymmetricMatcherConstructorType { + return @enumFromInt(AsymmetricMatcherConstructorType__fromJS(globalObject, value)); + } + }; pub const FlagsCppType = u8; comptime { @@ -109,7 +128,7 @@ pub const Expect = struct { } }; - pub fn getSignature(comptime matcher_name: string, comptime args: string, comptime not: bool) string { + pub fn getSignature(comptime matcher_name: string, comptime args: string, comptime not: bool) [:0]const u8 { const received = "expect(received)."; comptime if (not) { return received ++ "not." ++ matcher_name ++ "(" ++ args ++ ")"; @@ -146,12 +165,12 @@ pub const Expect = struct { } } - pub fn getNot(this: *Expect, thisValue: JSValue, _: *JSGlobalObject) callconv(.C) JSValue { + pub fn getNot(this: *Expect, thisValue: JSValue, _: *JSGlobalObject) JSValue { this.flags.not = !this.flags.not; return thisValue; } - pub fn getResolves(this: *Expect, thisValue: JSValue, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn getResolves(this: *Expect, thisValue: JSValue, globalThis: *JSGlobalObject) JSValue { this.flags.promise = switch (this.flags.promise) { .resolves, .none => .resolves, .rejects => { @@ -163,7 +182,7 @@ pub const Expect = struct { return thisValue; } - pub fn getRejects(this: *Expect, thisValue: JSValue, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn getRejects(this: *Expect, thisValue: JSValue, globalThis: *JSGlobalObject) JSValue { this.flags.promise = switch (this.flags.promise) { .none, .rejects => .rejects, .resolves => { @@ -202,31 +221,31 @@ pub const Expect = struct { const newValue = promise.result(vm); switch (promise.status(vm)) { - .Fulfilled => switch (resolution) { + .fulfilled => switch (resolution) { .resolves => {}, .rejects => { if (!silent) { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; const message = "Expected promise that rejects\nReceived promise that resolved: {any}\n"; - throwPrettyMatcherError(globalThis, custom_label, matcher_name, matcher_params, flags, message, .{value.toFmt(globalThis, &formatter)}); + throwPrettyMatcherError(globalThis, custom_label, matcher_name, matcher_params, flags, message, .{value.toFmt(&formatter)}); } return null; }, .none => unreachable, }, - .Rejected => switch (resolution) { + .rejected => switch (resolution) { .rejects => {}, .resolves => { if (!silent) { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; const message = "Expected promise that resolves\nReceived promise that rejected: {any}\n"; - throwPrettyMatcherError(globalThis, custom_label, matcher_name, matcher_params, flags, message, .{value.toFmt(globalThis, &formatter)}); + throwPrettyMatcherError(globalThis, custom_label, matcher_name, matcher_params, flags, message, .{value.toFmt(&formatter)}); } return null; }, .none => unreachable, }, - .Pending => unreachable, + .pending => unreachable, } newValue.ensureStillAlive(); @@ -235,7 +254,7 @@ pub const Expect = struct { if (!silent) { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; const message = "Expected promise\nReceived: {any}\n"; - throwPrettyMatcherError(globalThis, custom_label, matcher_name, matcher_params, flags, message, .{value.toFmt(globalThis, &formatter)}); + throwPrettyMatcherError(globalThis, custom_label, matcher_name, matcher_params, flags, message, .{value.toFmt(&formatter)}); } return null; } @@ -246,12 +265,35 @@ pub const Expect = struct { return value; } + pub fn isAsymmetricMatcher(value: JSValue) bool { + if (ExpectCustomAsymmetricMatcher.fromJS(value) != null) { + return true; + } else if (ExpectAny.fromJS(value) != null) { + return true; + } else if (ExpectAnything.fromJS(value) != null) { + return true; + } else if (ExpectStringMatching.fromJS(value) != null) { + return true; + } else if (ExpectCloseTo.fromJS(value) != null) { + return true; + } else if (ExpectObjectContaining.fromJS(value) != null) { + return true; + } else if (ExpectStringContaining.fromJS(value) != null) { + return true; + } else if (ExpectArrayContaining.fromJS(value) != null) { + return true; + } + + return false; + } + /// Called by C++ when matching with asymmetric matchers - fn readFlagsAndProcessPromise(instanceValue: JSValue, globalThis: *JSGlobalObject, outFlags: *Expect.Flags.FlagsCppType, value: *JSValue) callconv(.C) bool { + fn readFlagsAndProcessPromise(instanceValue: JSValue, globalThis: *JSGlobalObject, outFlags: *Expect.Flags.FlagsCppType, value: *JSValue, any_constructor_type: *u8) callconv(.C) bool { const flags: Expect.Flags = flags: { if (ExpectCustomAsymmetricMatcher.fromJS(instanceValue)) |instance| { break :flags instance.flags; } else if (ExpectAny.fromJS(instanceValue)) |instance| { + any_constructor_type.* = @intFromEnum(instance.flags.asymmetric_matcher_constructor_type); break :flags instance.flags; } else if (ExpectAnything.fromJS(instanceValue)) |instance| { break :flags instance.flags; @@ -332,9 +374,9 @@ pub const Expect = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn call(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(2).slice(); - const value = if (arguments.len < 1) JSC.JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; var custom_label = bun.String.empty; if (arguments.len > 1) { @@ -373,18 +415,18 @@ pub const Expect = struct { return expect_js_value; } - pub fn throw(this: *Expect, globalThis: *JSC.JSGlobalObject, comptime signature: string, comptime fmt: string, args: anytype) void { + pub fn throw(this: *Expect, globalThis: *JSGlobalObject, comptime signature: [:0]const u8, comptime fmt: [:0]const u8, args: anytype) void { if (this.custom_label.isEmpty()) { - globalThis.throwPretty(comptime signature ++ fmt, args); + globalThis.throwPretty(signature ++ fmt, args); } else { - globalThis.throwPretty(comptime "{}" ++ fmt, .{this.custom_label} ++ args); + globalThis.throwPretty("{}" ++ fmt, .{this.custom_label} ++ args); } } pub fn constructor( - globalThis: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) callconv(.C) ?*Expect { + globalThis: *JSGlobalObject, + _: *CallFrame, + ) ?*Expect { globalThis.throw("expect() cannot be called with new", .{}); return null; } @@ -392,9 +434,9 @@ pub const Expect = struct { // pass here has a leading underscore to avoid name collision with the pass variable in other functions pub fn _pass( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const arguments_ = callFrame.arguments(1); @@ -439,9 +481,9 @@ pub const Expect = struct { pub fn fail( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const arguments_ = callFrame.arguments(1); @@ -482,9 +524,9 @@ pub const Expect = struct { /// Object.is() pub fn toBe( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callframe: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callframe.this(); const arguments_ = callframe.arguments(2); @@ -513,7 +555,7 @@ pub const Expect = struct { inline else => |has_custom_label| { if (not) { const signature = comptime getSignature("toBe", "expected", true); - this.throw(globalThis, signature, "\n\nExpected: not {any}\n", .{right.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, "\n\nExpected: not {any}\n", .{right.toFmt(&formatter)}); return .zero; } @@ -523,7 +565,7 @@ pub const Expect = struct { (if (!has_custom_label) "\n\nIf this test should pass, replace \"toBe\" with \"toEqual\" or \"toStrictEqual\"" else "") ++ "\n\nExpected: {any}\n" ++ "Received: serializes to the same string\n"; - this.throw(globalThis, signature, fmt, .{right.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, fmt, .{right.toFmt(&formatter)}); return .zero; } @@ -539,8 +581,8 @@ pub const Expect = struct { } this.throw(globalThis, signature, "\n\nExpected: {any}\nReceived: {any}\n", .{ - right.toFmt(globalThis, &formatter), - left.toFmt(globalThis, &formatter), + right.toFmt(&formatter), + left.toFmt(&formatter), }); return .zero; }, @@ -549,9 +591,9 @@ pub const Expect = struct { pub fn toHaveLength( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callframe: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callframe.this(); const arguments_ = callframe.arguments(1); @@ -569,20 +611,20 @@ pub const Expect = struct { if (!value.isObject() and !value.isString()) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Received value does not have a length property: {any}", .{value.toFmt(globalThis, &fmt)}); + globalThis.throw("Received value does not have a length property: {any}", .{value.toFmt(&fmt)}); return .zero; } if (!expected.isNumber()) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(globalThis, &fmt)}); + globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(&fmt)}); return .zero; } const expected_length: f64 = expected.asNumber(); if (@round(expected_length) != expected_length or std.math.isInf(expected_length) or std.math.isNan(expected_length) or expected_length < 0) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(globalThis, &fmt)}); + globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(&fmt)}); return .zero; } @@ -593,7 +635,7 @@ pub const Expect = struct { if (actual_length == std.math.inf(f64)) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Received value does not have a length property: {any}", .{value.toFmt(globalThis, &fmt)}); + globalThis.throw("Received value does not have a length property: {any}", .{value.toFmt(&fmt)}); return .zero; } else if (std.math.isNan(actual_length)) { globalThis.throw("Received value has non-number length property: {}", .{actual_length}); @@ -624,9 +666,9 @@ pub const Expect = struct { pub fn toBeOneOf( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -646,7 +688,7 @@ pub const Expect = struct { var pass = false; const ExpectedEntry = struct { - globalThis: *JSC.JSGlobalObject, + globalThis: *JSGlobalObject, expected: JSValue, pass: *bool, }; @@ -691,10 +733,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = list_value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = list_value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = list_value.toFmt(globalThis, &formatter); + const received_fmt = list_value.toFmt(&formatter); const expected_line = "Expected to not be one of: {any}\nReceived: {any}\n"; const signature = comptime getSignature("toBeOneOf", "expected", true); this.throw(globalThis, signature, "\n\n" ++ expected_line, .{ received_fmt, expected_fmt }); @@ -710,9 +752,9 @@ pub const Expect = struct { pub fn toContain( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -733,7 +775,7 @@ pub const Expect = struct { var pass = false; const ExpectedEntry = struct { - globalThis: *JSC.JSGlobalObject, + globalThis: *JSGlobalObject, expected: JSValue, pass: *bool, }; @@ -789,10 +831,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalThis, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain: {any}\nReceived: {any}\n"; const signature = comptime getSignature("toContain", "expected", true); this.throw(globalThis, signature, "\n\n" ++ expected_line, .{ expected_fmt, received_fmt }); @@ -808,9 +850,9 @@ pub const Expect = struct { pub fn toContainKey( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -830,11 +872,7 @@ pub const Expect = struct { const not = this.flags.not; if (!value.isObject()) { - const err = globalThis.createTypeErrorInstance("Expected value must be an object\nReceived: {}", .{value.toFmt( - globalThis, - &formatter, - )}); - globalThis.throwValue(err); + globalThis.throwInvalidArguments("Expected value must be an object\nReceived: {}", .{value.toFmt(&formatter)}); return .zero; } @@ -849,10 +887,10 @@ pub const Expect = struct { // handle failure - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalThis, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain: {any}\nReceived: {any}\n"; const signature = comptime getSignature("toContainKey", "expected", true); this.throw(globalThis, signature, "\n\n" ++ expected_line, .{ expected_fmt, received_fmt }); @@ -868,9 +906,9 @@ pub const Expect = struct { pub fn toContainKeys( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -922,10 +960,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalThis, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain: {any}\nReceived: {any}\n"; const signature = comptime getSignature("toContainKeys", "expected", true); this.throw(globalThis, signature, "\n\n" ++ expected_line, .{ expected_fmt, received_fmt }); @@ -941,9 +979,9 @@ pub const Expect = struct { pub fn toContainAllKeys( this: *Expect, - globalObject: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalObject: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalObject); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -990,10 +1028,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true }; - const value_fmt = keys.toFmt(globalObject, &formatter); - const expected_fmt = expected.toFmt(globalObject, &formatter); + const value_fmt = keys.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = keys.toFmt(globalObject, &formatter); + const received_fmt = keys.toFmt(&formatter); const expected_line = "Expected to not contain all keys: {any}\nReceived: {any}\n"; const fmt = "\n\n" ++ expected_line; this.throw(globalObject, comptime getSignature("toContainAllKeys", "expected", true), fmt, .{ expected_fmt, received_fmt }); @@ -1009,9 +1047,9 @@ pub const Expect = struct { pub fn toContainAnyKeys( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -1058,10 +1096,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalThis, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain: {any}\nReceived: {any}\n"; const signature = comptime getSignature("toContainAnyKeys", "expected", true); this.throw(globalThis, signature, "\n\n" ++ expected_line, .{ expected_fmt, received_fmt }); @@ -1077,9 +1115,9 @@ pub const Expect = struct { pub fn toContainValue( this: *Expect, - globalObject: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalObject: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalObject); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -1115,10 +1153,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true }; - const value_fmt = value.toFmt(globalObject, &formatter); - const expected_fmt = expected.toFmt(globalObject, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalObject, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain: {any}\nReceived: {any}\n"; const fmt = "\n\n" ++ expected_line; this.throw(globalObject, comptime getSignature("toContainValue", "expected", true), fmt, .{ expected_fmt, received_fmt }); @@ -1134,9 +1172,9 @@ pub const Expect = struct { pub fn toContainValues( this: *Expect, - globalObject: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalObject: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalObject); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -1182,10 +1220,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true }; - const value_fmt = value.toFmt(globalObject, &formatter); - const expected_fmt = expected.toFmt(globalObject, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalObject, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain: {any}\nReceived: {any}\n"; const fmt = "\n\n" ++ expected_line; this.throw(globalObject, comptime getSignature("toContainValues", "expected", true), fmt, .{ expected_fmt, received_fmt }); @@ -1201,9 +1239,9 @@ pub const Expect = struct { pub fn toContainAllValues( this: *Expect, - globalObject: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalObject: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalObject); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -1255,10 +1293,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true }; - const value_fmt = value.toFmt(globalObject, &formatter); - const expected_fmt = expected.toFmt(globalObject, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalObject, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain all values: {any}\nReceived: {any}\n"; const fmt = "\n\n" ++ expected_line; this.throw(globalObject, comptime getSignature("toContainAllValues", "expected", true), fmt, .{ expected_fmt, received_fmt }); @@ -1274,9 +1312,9 @@ pub const Expect = struct { pub fn toContainAnyValues( this: *Expect, - globalObject: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalObject: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalObject); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -1322,10 +1360,10 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true }; - const value_fmt = value.toFmt(globalObject, &formatter); - const expected_fmt = expected.toFmt(globalObject, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { - const received_fmt = value.toFmt(globalObject, &formatter); + const received_fmt = value.toFmt(&formatter); const expected_line = "Expected to not contain any of the following values: {any}\nReceived: {any}\n"; const fmt = "\n\n" ++ expected_line; this.throw(globalObject, comptime getSignature("toContainAnyValues", "expected", true), fmt, .{ expected_fmt, received_fmt }); @@ -1341,9 +1379,9 @@ pub const Expect = struct { pub fn toContainEqual( this: *Expect, - globalThis: *JSC.JSGlobalObject, - callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callFrame: *CallFrame, + ) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); @@ -1364,7 +1402,7 @@ pub const Expect = struct { var pass = false; const ExpectedEntry = struct { - globalThis: *JSC.JSGlobalObject, + globalThis: *JSGlobalObject, expected: JSValue, pass: *bool, }; @@ -1429,8 +1467,8 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { const expected_line = "Expected to not contain: {any}\n"; const signature = comptime getSignature("toContainEqual", "expected", true); @@ -1445,7 +1483,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeTruthy(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeTruthy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const value: JSValue = this.getValue(globalThis, thisValue, "toBeTruthy", "") orelse return .zero; @@ -1463,7 +1501,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeTruthy", "", true); @@ -1477,7 +1515,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeUndefined(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeUndefined(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const value: JSValue = this.getValue(globalThis, thisValue, "toBeUndefined", "") orelse return .zero; @@ -1493,7 +1531,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeUndefined", "", true); @@ -1507,7 +1545,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeNaN(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeNaN(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1527,7 +1565,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeNaN", "", true); @@ -1541,7 +1579,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeNull(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeNull(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1556,7 +1594,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeNull", "", true); @@ -1570,7 +1608,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeDefined(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeDefined(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1585,7 +1623,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeDefined", "", true); @@ -1599,7 +1637,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeFalsy(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeFalsy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1619,7 +1657,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeFalsy", "", true); @@ -1633,7 +1671,7 @@ pub const Expect = struct { return .zero; } - pub fn toEqual(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1675,7 +1713,7 @@ pub const Expect = struct { return .zero; } - pub fn toStrictEqual(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toStrictEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1712,7 +1750,7 @@ pub const Expect = struct { return .zero; } - pub fn toHaveProperty(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveProperty(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1764,8 +1802,8 @@ pub const Expect = struct { const signature = comptime getSignature("toHaveProperty", "path, value", true); if (!received_property.isEmpty()) { this.throw(globalThis, signature, "\n\nExpected path: {any}\n\nExpected value: not {any}\n", .{ - expected_property_path.toFmt(globalThis, &formatter), - expected_property.?.toFmt(globalThis, &formatter), + expected_property_path.toFmt(&formatter), + expected_property.?.toFmt(&formatter), }); return .zero; } @@ -1773,8 +1811,8 @@ pub const Expect = struct { const signature = comptime getSignature("toHaveProperty", "path", true); this.throw(globalThis, signature, "\n\nExpected path: not {any}\n\nReceived value: {any}\n", .{ - expected_property_path.toFmt(globalThis, &formatter), - received_property.toFmt(globalThis, &formatter), + expected_property_path.toFmt(&formatter), + received_property.toFmt(&formatter), }); return .zero; } @@ -1796,18 +1834,18 @@ pub const Expect = struct { const fmt = "\n\nExpected path: {any}\n\nExpected value: {any}\n\n" ++ "Unable to find property\n"; this.throw(globalThis, signature, fmt, .{ - expected_property_path.toFmt(globalThis, &formatter), - expected_property.?.toFmt(globalThis, &formatter), + expected_property_path.toFmt(&formatter), + expected_property.?.toFmt(&formatter), }); return .zero; } const signature = comptime getSignature("toHaveProperty", "path", false); - this.throw(globalThis, signature, "\n\nExpected path: {any}\n\nUnable to find property\n", .{expected_property_path.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, "\n\nExpected path: {any}\n\nUnable to find property\n", .{expected_property_path.toFmt(&formatter)}); return .zero; } - pub fn toBeEven(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeEven(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1847,7 +1885,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeEven", "", true); @@ -1861,7 +1899,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeGreaterThan(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toBeGreaterThan(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1907,8 +1945,8 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = other_value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = other_value.toFmt(&formatter); if (not) { const expected_line = "Expected: not \\> {any}\n"; const received_line = "Received: {any}\n"; @@ -1924,7 +1962,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeGreaterThanOrEqual(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toBeGreaterThanOrEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -1970,8 +2008,8 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = other_value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = other_value.toFmt(&formatter); if (not) { const expected_line = "Expected: not \\>= {any}\n"; const received_line = "Received: {any}\n"; @@ -1987,7 +2025,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeLessThan(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toBeLessThan(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2033,8 +2071,8 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = other_value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = other_value.toFmt(&formatter); if (not) { const expected_line = "Expected: not \\< {any}\n"; const received_line = "Received: {any}\n"; @@ -2050,7 +2088,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeLessThanOrEqual(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toBeLessThanOrEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2096,8 +2134,8 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = other_value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = other_value.toFmt(&formatter); if (not) { const expected_line = "Expected: not \\<= {any}\n"; const received_line = "Received: {any}\n"; @@ -2113,7 +2151,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeCloseTo(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toBeCloseTo(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2174,8 +2212,8 @@ pub const Expect = struct { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const expected_fmt = expected_.toFmt(globalThis, &formatter); - const received_fmt = received_.toFmt(globalThis, &formatter); + const expected_fmt = expected_.toFmt(&formatter); + const received_fmt = received_.toFmt(&formatter); const expected_line = "Expected: {any}\n"; const received_line = "Received: {any}\n"; @@ -2196,7 +2234,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeOdd(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeOdd(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2234,7 +2272,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const received_line = "Received: {any}\n"; const signature = comptime getSignature("toBeOdd", "", true); @@ -2248,7 +2286,7 @@ pub const Expect = struct { return .zero; } - pub fn toThrow(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2261,8 +2299,14 @@ pub const Expect = struct { const value = arguments[0]; if (value.isEmptyOrUndefinedOrNull() or !value.isObject() and !value.isString()) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Expected value must be string or Error: {any}", .{value.toFmt(globalThis, &fmt)}); + globalThis.throw("Expected value must be string or Error: {any}", .{value.toFmt(&fmt)}); return .zero; + } else if (value.isObject()) { + if (ExpectAny.fromJSDirect(value)) |_| { + if (ExpectAny.constructorValueGetCached(value)) |innerConstructorValue| { + break :brk innerConstructorValue; + } + } } break :brk value; } else .zero; @@ -2292,43 +2336,40 @@ pub const Expect = struct { const prev_unhandled_pending_rejection_to_capture = vm.unhandled_pending_rejection_to_capture; vm.unhandled_pending_rejection_to_capture = &return_value; vm.onUnhandledRejection = &VirtualMachine.onQuietUnhandledRejectionHandlerCaptureValue; - const return_value_from_fucntion: JSValue = value.call(globalThis, &.{}); + const return_value_from_function: JSValue = value.call(globalThis, .undefined, &.{}) catch |err| + globalThis.takeException(err); vm.unhandled_pending_rejection_to_capture = prev_unhandled_pending_rejection_to_capture; vm.global.handleRejectedPromises(); if (return_value == .zero) { - return_value = return_value_from_fucntion; + return_value = return_value_from_function; } if (return_value.asAnyPromise()) |promise| { vm.waitForPromise(promise); scope.apply(vm); - const promise_result = promise.result(globalThis.vm()); - - switch (promise.status(globalThis.vm())) { - .Fulfilled => { + switch (promise.unwrap(globalThis.vm(), .mark_handled)) { + .fulfilled => { break :brk null; }, - .Rejected => { - promise.setHandled(globalThis.vm()); - + .rejected => |rejected| { // since we know for sure it rejected, we should always return the error - break :brk promise_result.toError() orelse promise_result; + break :brk rejected.toError() orelse rejected; }, - .Pending => unreachable, + .pending => unreachable, } } - if (return_value != return_value_from_fucntion) { - if (return_value_from_fucntion.asAnyPromise()) |existing| { + if (return_value != return_value_from_function) { + if (return_value_from_function.asAnyPromise()) |existing| { existing.setHandled(globalThis.vm()); } } scope.apply(vm); - break :brk return_value.toError() orelse return_value_from_fucntion.toError(); + break :brk return_value.toError() orelse return_value_from_function.toError(); }; const did_throw = result_ != null; @@ -2344,24 +2385,30 @@ pub const Expect = struct { if (expected_value.isEmpty() or expected_value.isUndefined()) { const signature_no_args = comptime getSignature("toThrow", "", true); if (result.toError()) |err| { - const name = err.get(globalThis, "name") orelse JSValue.undefined; - const message = err.get(globalThis, "message") orelse JSValue.undefined; + const name = err.getTruthyComptime(globalThis, "name") orelse JSValue.undefined; + const message = err.getTruthyComptime(globalThis, "message") orelse JSValue.undefined; const fmt = signature_no_args ++ "\n\nError name: {any}\nError message: {any}\n"; globalThis.throwPretty(fmt, .{ - name.toFmt(globalThis, &formatter), - message.toFmt(globalThis, &formatter), + name.toFmt(&formatter), + message.toFmt(&formatter), }); return .zero; } // non error thrown const fmt = signature_no_args ++ "\n\nThrown value: {any}\n"; - globalThis.throwPretty(fmt, .{result.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{result.toFmt(&formatter)}); return .zero; } if (expected_value.isString()) { - const received_message = result.getIfPropertyExistsImpl(globalThis, "message", 7); + const received_message: JSValue = (if (result.isObject()) + result.fastGet(globalThis, .message) + else if (result.toStringOrNull(globalThis)) |js_str| + JSValue.fromCell(js_str) + else + .undefined) orelse .undefined; + if (globalThis.hasException()) return .zero; // TODO: remove this allocation // partial match @@ -2374,34 +2421,47 @@ pub const Expect = struct { } this.throw(globalThis, signature, "\n\nExpected substring: not {any}\nReceived message: {any}\n", .{ - expected_value.toFmt(globalThis, &formatter), - received_message.toFmt(globalThis, &formatter), + expected_value.toFmt(&formatter), + received_message.toFmt(&formatter), }); return .zero; } if (expected_value.isRegExp()) { - const received_message = result.getIfPropertyExistsImpl(globalThis, "message", 7); + const received_message: JSValue = (if (result.isObject()) + result.fastGet(globalThis, .message) + else if (result.toStringOrNull(globalThis)) |js_str| + JSValue.fromCell(js_str) + else + .undefined) orelse .undefined; + if (globalThis.hasException()) return .zero; // TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly. if (expected_value.get(globalThis, "test")) |test_fn| { - const matches = test_fn.callWithThis(globalThis, expected_value, &.{received_message}); + const matches = test_fn.call(globalThis, expected_value, &.{received_message}) catch |err| globalThis.takeException(err); if (!matches.toBooleanSlow(globalThis)) return .undefined; } this.throw(globalThis, signature, "\n\nExpected pattern: not {any}\nReceived message: {any}\n", .{ - expected_value.toFmt(globalThis, &formatter), - received_message.toFmt(globalThis, &formatter), + expected_value.toFmt(&formatter), + received_message.toFmt(&formatter), }); return .zero; } - if (expected_value.get(globalThis, "message")) |expected_message| { - const received_message = result.getIfPropertyExistsImpl(globalThis, "message", 7); + if (expected_value.fastGet(globalThis, .message)) |expected_message| { + const received_message: JSValue = (if (result.isObject()) + result.fastGet(globalThis, .message) + else if (result.toStringOrNull(globalThis)) |js_str| + JSValue.fromCell(js_str) + else + .undefined) orelse .undefined; + if (globalThis.hasException()) return .zero; + // no partial match for this case if (!expected_message.isSameValue(received_message, globalThis)) return .undefined; - this.throw(globalThis, signature, "\n\nExpected message: not {any}\n", .{expected_message.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, "\n\nExpected message: not {any}\n", .{expected_message.toFmt(&formatter)}); return .zero; } @@ -2409,8 +2469,8 @@ pub const Expect = struct { var expected_class = ZigString.Empty; expected_value.getClassName(globalThis, &expected_class); - const received_message = result.getIfPropertyExistsImpl(globalThis, "message", 7); - this.throw(globalThis, signature, "\n\nExpected constructor: not {s}\n\nReceived message: {any}\n", .{ expected_class, received_message.toFmt(globalThis, &formatter) }); + const received_message = result.fastGet(globalThis, .message) orelse .undefined; + this.throw(globalThis, signature, "\n\nExpected constructor: not {s}\n\nReceived message: {any}\n", .{ expected_class, received_message.toFmt(&formatter) }); return .zero; } @@ -2423,9 +2483,9 @@ pub const Expect = struct { result_.?; const _received_message: ?JSValue = if (result.isObject()) - result.get(globalThis, "message") + result.fastGet(globalThis, .message) else if (result.toStringOrNull(globalThis)) |js_str| - JSC.JSValue.fromCell(js_str) + JSValue.fromCell(js_str) else null; @@ -2446,14 +2506,14 @@ pub const Expect = struct { const signature = comptime getSignature("toThrow", "expected", false); if (_received_message) |received_message| { - const expected_value_fmt = expected_value.toFmt(globalThis, &formatter); - const received_message_fmt = received_message.toFmt(globalThis, &formatter); + const expected_value_fmt = expected_value.toFmt(&formatter); + const received_message_fmt = received_message.toFmt(&formatter); this.throw(globalThis, signature, "\n\n" ++ "Expected substring: {any}\nReceived message: {any}\n", .{ expected_value_fmt, received_message_fmt }); return .zero; } - const expected_fmt = expected_value.toFmt(globalThis, &formatter); - const received_fmt = result.toFmt(globalThis, &formatter); + const expected_fmt = expected_value.toFmt(&formatter); + const received_fmt = result.toFmt(&formatter); this.throw(globalThis, signature, "\n\n" ++ "Expected substring: {any}\nReceived value: {any}", .{ expected_fmt, received_fmt }); return .zero; @@ -2463,7 +2523,7 @@ pub const Expect = struct { if (_received_message) |received_message| { // TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly. if (expected_value.get(globalThis, "test")) |test_fn| { - const matches = test_fn.callWithThis(globalThis, expected_value, &.{received_message}); + const matches = test_fn.call(globalThis, expected_value, &.{received_message}) catch |err| globalThis.takeException(err); if (matches.toBooleanSlow(globalThis)) return .undefined; } } @@ -2472,8 +2532,8 @@ pub const Expect = struct { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; if (_received_message) |received_message| { - const expected_value_fmt = expected_value.toFmt(globalThis, &formatter); - const received_message_fmt = received_message.toFmt(globalThis, &formatter); + const expected_value_fmt = expected_value.toFmt(&formatter); + const received_message_fmt = received_message.toFmt(&formatter); const signature = comptime getSignature("toThrow", "expected", false); this.throw(globalThis, signature, "\n\n" ++ "Expected pattern: {any}\nReceived message: {any}\n", .{ expected_value_fmt, received_message_fmt }); @@ -2481,17 +2541,36 @@ pub const Expect = struct { return .zero; } - const expected_fmt = expected_value.toFmt(globalThis, &formatter); - const received_fmt = result.toFmt(globalThis, &formatter); + const expected_fmt = expected_value.toFmt(&formatter); + const received_fmt = result.toFmt(&formatter); const signature = comptime getSignature("toThrow", "expected", false); this.throw(globalThis, signature, "\n\n" ++ "Expected pattern: {any}\nReceived value: {any}", .{ expected_fmt, received_fmt }); return .zero; } + if (Expect.isAsymmetricMatcher(expected_value)) { + const signature = comptime getSignature("toThrow", "expected", false); + const is_equal = result.jestStrictDeepEquals(expected_value, globalThis); + + if (globalThis.hasException()) { + return .zero; + } + + if (is_equal) { + return .undefined; + } + + var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; + const received_fmt = result.toFmt(&formatter); + const expected_fmt = expected_value.toFmt(&formatter); + this.throw(globalThis, signature, "\n\nExpected value: {any}\nReceived value: {any}\n", .{ expected_fmt, received_fmt }); + return .zero; + } + // If it's not an object, we are going to crash here. assert(expected_value.isObject()); - if (expected_value.get(globalThis, "message")) |expected_message| { + if (expected_value.fastGet(globalThis, .message)) |expected_message| { const signature = comptime getSignature("toThrow", "expected", false); if (_received_message) |received_message| { @@ -2502,14 +2581,14 @@ pub const Expect = struct { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; if (_received_message) |received_message| { - const expected_fmt = expected_message.toFmt(globalThis, &formatter); - const received_fmt = received_message.toFmt(globalThis, &formatter); + const expected_fmt = expected_message.toFmt(&formatter); + const received_fmt = received_message.toFmt(&formatter); this.throw(globalThis, signature, "\n\nExpected message: {any}\nReceived message: {any}\n", .{ expected_fmt, received_fmt }); return .zero; } - const expected_fmt = expected_message.toFmt(globalThis, &formatter); - const received_fmt = result.toFmt(globalThis, &formatter); + const expected_fmt = expected_message.toFmt(&formatter); + const received_fmt = result.toFmt(&formatter); this.throw(globalThis, signature, "\n\nExpected message: {any}\nReceived value: {any}\n", .{ expected_fmt, received_fmt }); return .zero; } @@ -2527,7 +2606,7 @@ pub const Expect = struct { if (_received_message) |received_message| { const message_fmt = fmt ++ "Received message: {any}\n"; - const received_message_fmt = received_message.toFmt(globalThis, &formatter); + const received_message_fmt = received_message.toFmt(&formatter); globalThis.throwPretty(message_fmt, .{ expected_class, @@ -2537,7 +2616,7 @@ pub const Expect = struct { return .zero; } - const received_fmt = result.toFmt(globalThis, &formatter); + const received_fmt = result.toFmt(&formatter); const value_fmt = fmt ++ "Received value: {any}\n"; globalThis.throwPretty(value_fmt, .{ @@ -2562,19 +2641,19 @@ pub const Expect = struct { if (expected_value.isString()) { const expected_fmt = "\n\nExpected substring: {any}\n\n" ++ received_line; - this.throw(globalThis, signature, expected_fmt, .{expected_value.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, expected_fmt, .{expected_value.toFmt(&formatter)}); return .zero; } if (expected_value.isRegExp()) { const expected_fmt = "\n\nExpected pattern: {any}\n\n" ++ received_line; - this.throw(globalThis, signature, expected_fmt, .{expected_value.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, expected_fmt, .{expected_value.toFmt(&formatter)}); return .zero; } - if (expected_value.get(globalThis, "message")) |expected_message| { + if (expected_value.fastGet(globalThis, .message)) |expected_message| { const expected_fmt = "\n\nExpected message: {any}\n\n" ++ received_line; - this.throw(globalThis, signature, expected_fmt, .{expected_message.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, expected_fmt, .{expected_message.toFmt(&formatter)}); return .zero; } @@ -2584,8 +2663,7 @@ pub const Expect = struct { this.throw(globalThis, signature, expected_fmt, .{expected_class}); return .zero; } - - pub fn toMatchSnapshot(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toMatchSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); const _arguments = callFrame.arguments(2); @@ -2652,7 +2730,7 @@ pub const Expect = struct { "\n\nReceived: {any}\n"; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis }; - globalThis.throwPretty(fmt, .{value.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{value.toFmt(&formatter)}); return .zero; } } @@ -2665,7 +2743,7 @@ pub const Expect = struct { error.FailedToMakeSnapshotDirectory => globalThis.throw("Failed to make snapshot directory for test file: {s}", .{test_file_path}), error.FailedToWriteSnapshotFile => globalThis.throw("Failed write to snapshot file: {s}", .{test_file_path}), error.ParseError => globalThis.throw("Failed to parse snapshot file for: {s}", .{test_file_path}), - else => globalThis.throw("Failed to snapshot value: {any}", .{value.toFmt(globalThis, &formatter)}), + else => globalThis.throw("Failed to snapshot value: {any}", .{value.toFmt(&formatter)}), } return .zero; }; @@ -2674,7 +2752,7 @@ pub const Expect = struct { var pretty_value: MutableString = MutableString.init(default_allocator, 0) catch unreachable; value.jestSnapshotPrettyFormat(&pretty_value, globalThis) catch { var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis }; - globalThis.throw("Failed to pretty format value: {s}", .{value.toFmt(globalThis, &formatter)}); + globalThis.throw("Failed to pretty format value: {s}", .{value.toFmt(&formatter)}); return .zero; }; defer pretty_value.deinit(); @@ -2700,7 +2778,7 @@ pub const Expect = struct { return .undefined; } - pub fn toBeEmpty(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toBeEmpty(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2742,7 +2820,7 @@ pub const Expect = struct { const signature = comptime getSignature("toBeEmpty", "", false); const fmt = signature ++ "\n\nExpected value to be a string, object, or iterable" ++ "\n\nReceived: {any}\n"; - globalThis.throwPretty(fmt, .{value.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{value.toFmt(&formatter)}); return .zero; } } else if (std.math.isNan(actual_length)) { @@ -2756,7 +2834,7 @@ pub const Expect = struct { const signature = comptime getSignature("toBeEmpty", "", true); const fmt = signature ++ "\n\nExpected value not to be a string, object, or iterable" ++ "\n\nReceived: {any}\n"; - globalThis.throwPretty(fmt, .{value.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{value.toFmt(&formatter)}); return .zero; } @@ -2767,18 +2845,18 @@ pub const Expect = struct { const signature = comptime getSignature("toBeEmpty", "", true); const fmt = signature ++ "\n\nExpected value not to be empty" ++ "\n\nReceived: {any}\n"; - globalThis.throwPretty(fmt, .{value.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{value.toFmt(&formatter)}); return .zero; } const signature = comptime getSignature("toBeEmpty", "", false); const fmt = signature ++ "\n\nExpected value to be empty" ++ "\n\nReceived: {any}\n"; - globalThis.throwPretty(fmt, .{value.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{value.toFmt(&formatter)}); return .zero; } - pub fn toBeEmptyObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeEmptyObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2793,7 +2871,7 @@ pub const Expect = struct { if (pass) return thisValue; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeEmptyObject", "", true); @@ -2806,7 +2884,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeNil(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeNil(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2820,7 +2898,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeNil", "", true); @@ -2833,7 +2911,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeArray(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeArray(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2847,7 +2925,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeArray", "", true); @@ -2860,7 +2938,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeArrayOfSize(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeArrayOfSize(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2891,7 +2969,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeArrayOfSize", "", true); @@ -2904,7 +2982,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeBoolean(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeBoolean(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2918,7 +2996,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeBoolean", "", true); @@ -2931,7 +3009,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeTypeOf(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeTypeOf(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -2994,8 +3072,8 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); - const expected_str = expected.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); + const expected_str = expected.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeTypeOf", "", true); @@ -3008,7 +3086,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeTrue(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeTrue(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3022,7 +3100,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeTrue", "", true); @@ -3035,7 +3113,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeFalse(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeFalse(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3049,7 +3127,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeFalse", "", true); @@ -3062,7 +3140,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeNumber(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeNumber(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3076,7 +3154,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeNumber", "", true); @@ -3089,7 +3167,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeInteger(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeInteger(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3103,7 +3181,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeInteger", "", true); @@ -3116,7 +3194,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3130,7 +3208,7 @@ pub const Expect = struct { if (pass) return thisValue; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeObject", "", true); @@ -3143,7 +3221,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeFinite(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeFinite(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3163,7 +3241,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeFinite", "", true); @@ -3176,7 +3254,7 @@ pub const Expect = struct { return .zero; } - pub fn toBePositive(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBePositive(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3196,7 +3274,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBePositive", "", true); @@ -3209,7 +3287,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeNegative(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeNegative(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3229,7 +3307,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeNegative", "", true); @@ -3242,7 +3320,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeWithin(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeWithin(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3286,9 +3364,9 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const start_fmt = startValue.toFmt(globalThis, &formatter); - const end_fmt = endValue.toFmt(globalThis, &formatter); - const received_fmt = value.toFmt(globalThis, &formatter); + const start_fmt = startValue.toFmt(&formatter); + const end_fmt = endValue.toFmt(&formatter); + const received_fmt = value.toFmt(&formatter); if (not) { const expected_line = "Expected: not between {any} (inclusive) and {any} (exclusive)\n"; @@ -3305,7 +3383,7 @@ pub const Expect = struct { return .zero; } - pub fn toEqualIgnoringWhitespace(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toEqualIgnoringWhitespace(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3373,8 +3451,8 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const expected_fmt = expected.toFmt(globalThis, &formatter); - const value_fmt = value.toFmt(globalThis, &formatter); + const expected_fmt = expected.toFmt(&formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toEqualIgnoringWhitespace", "expected", true); @@ -3387,7 +3465,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeSymbol(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeSymbol(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3401,7 +3479,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeSymbol", "", true); @@ -3414,7 +3492,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeFunction(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeFunction(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3428,7 +3506,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeFunction", "", true); @@ -3441,7 +3519,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeDate(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeDate(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3455,7 +3533,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeDate", "", true); @@ -3468,7 +3546,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeValidDate(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeValidDate(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3483,7 +3561,7 @@ pub const Expect = struct { if (pass) return thisValue; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeValidDate", "", true); @@ -3496,7 +3574,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeString(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toBeString(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3510,7 +3588,7 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received = value.toFmt(globalThis, &formatter); + const received = value.toFmt(&formatter); if (not) { const signature = comptime getSignature("toBeString", "", true); @@ -3523,7 +3601,7 @@ pub const Expect = struct { return .zero; } - pub fn toInclude(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toInclude(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3562,8 +3640,8 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { const expected_line = "Expected to not include: {any}\n"; @@ -3580,7 +3658,7 @@ pub const Expect = struct { return .zero; } - pub fn toIncludeRepeated(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toIncludeRepeated(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3650,9 +3728,9 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const expect_string_fmt = expect_string.toFmt(globalThis, &formatter); - const substring_fmt = substring.toFmt(globalThis, &formatter); - const times_fmt = count.toFmt(globalThis, &formatter); + const expect_string_fmt = expect_string.toFmt(&formatter); + const substring_fmt = substring.toFmt(&formatter); + const times_fmt = count.toFmt(&formatter); const received_line = "Received: {any}\n"; @@ -3691,7 +3769,7 @@ pub const Expect = struct { return .zero; } - pub fn toSatisfy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toSatisfy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3719,18 +3797,12 @@ pub const Expect = struct { }; value.ensureStillAlive(); - const result = predicate.call(globalThis, &.{value}); - - if (result.toError()) |err| { - var errors: [1]*anyopaque = undefined; - var _err = errors[0..errors.len]; - - _err[0] = err.asVoid(); - + const result = predicate.call(globalThis, .undefined, &.{value}) catch |e| { + const err = globalThis.takeException(e); const fmt = ZigString.init("toSatisfy() predicate threw an exception"); - globalThis.vm().throwError(globalThis, globalThis.createAggregateError(_err.ptr, _err.len, &fmt)); + globalThis.vm().throwError(globalThis, globalThis.createAggregateError(&.{err}, &fmt)); return .zero; - } + }; const not = this.flags.not; const pass = (result.isBoolean() and result.toBoolean()) != not; @@ -3741,21 +3813,21 @@ pub const Expect = struct { if (not) { const signature = comptime getSignature("toSatisfy", "expected", true); - this.throw(globalThis, signature, "\n\nExpected: not {any}\n", .{predicate.toFmt(globalThis, &formatter)}); + this.throw(globalThis, signature, "\n\nExpected: not {any}\n", .{predicate.toFmt(&formatter)}); return .zero; } const signature = comptime getSignature("toSatisfy", "expected", false); this.throw(globalThis, signature, "\n\nExpected: {any}\nReceived: {any}\n", .{ - predicate.toFmt(globalThis, &formatter), - value.toFmt(globalThis, &formatter), + predicate.toFmt(&formatter), + value.toFmt(&formatter), }); return .zero; } - pub fn toStartWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toStartWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3794,8 +3866,8 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { const expected_line = "Expected to not start with: {any}\n"; @@ -3812,7 +3884,7 @@ pub const Expect = struct { return .zero; } - pub fn toEndWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + pub fn toEndWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3851,8 +3923,8 @@ pub const Expect = struct { if (pass) return .undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const value_fmt = value.toFmt(globalThis, &formatter); - const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(&formatter); + const expected_fmt = expected.toFmt(&formatter); if (not) { const expected_line = "Expected to not end with: {any}\n"; @@ -3869,7 +3941,7 @@ pub const Expect = struct { return .zero; } - pub fn toBeInstanceOf(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toBeInstanceOf(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer this.postMatch(globalThis); const thisValue = callFrame.this(); @@ -3886,7 +3958,7 @@ pub const Expect = struct { const expected_value = arguments[0]; if (!expected_value.isConstructor()) { - globalThis.throw("Expected value must be a function: {any}", .{expected_value.toFmt(globalThis, &formatter)}); + globalThis.throw("Expected value must be a function: {any}", .{expected_value.toFmt(&formatter)}); return .zero; } expected_value.ensureStillAlive(); @@ -3899,8 +3971,8 @@ pub const Expect = struct { if (pass) return .undefined; // handle failure - const expected_fmt = expected_value.toFmt(globalThis, &formatter); - const value_fmt = value.toFmt(globalThis, &formatter); + const expected_fmt = expected_value.toFmt(&formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const expected_line = "Expected constructor: not {any}\n"; const received_line = "Received value: {any}\n"; @@ -3916,7 +3988,7 @@ pub const Expect = struct { return .zero; } - pub fn toMatch(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toMatch(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { JSC.markBinding(@src()); defer this.postMatch(globalThis); @@ -3936,7 +4008,7 @@ pub const Expect = struct { const expected_value = arguments[0]; if (!expected_value.isString() and !expected_value.isRegExp()) { - globalThis.throw("Expected value must be a string or regular expression: {any}", .{expected_value.toFmt(globalThis, &formatter)}); + globalThis.throw("Expected value must be a string or regular expression: {any}", .{expected_value.toFmt(&formatter)}); return .zero; } expected_value.ensureStillAlive(); @@ -3944,7 +4016,7 @@ pub const Expect = struct { const value: JSValue = this.getValue(globalThis, thisValue, "toMatch", "expected") orelse return .zero; if (!value.isString()) { - globalThis.throw("Received value must be a string: {any}", .{value.toFmt(globalThis, &formatter)}); + globalThis.throw("Received value must be a string: {any}", .{value.toFmt(&formatter)}); return .zero; } @@ -3962,8 +4034,8 @@ pub const Expect = struct { if (pass) return .undefined; // handle failure - const expected_fmt = expected_value.toFmt(globalThis, &formatter); - const value_fmt = value.toFmt(globalThis, &formatter); + const expected_fmt = expected_value.toFmt(&formatter); + const value_fmt = value.toFmt(&formatter); if (not) { const expected_line = "Expected substring or pattern: not {any}\n"; @@ -3980,7 +4052,7 @@ pub const Expect = struct { return .zero; } - pub fn toHaveBeenCalled(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveBeenCalled(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { JSC.markBinding(@src()); const thisValue = callframe.this(); defer this.postMatch(globalThis); @@ -4013,7 +4085,7 @@ pub const Expect = struct { return .zero; } - pub fn toHaveBeenCalledTimes(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveBeenCalledTimes(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { JSC.markBinding(@src()); const thisValue = callframe.this(); @@ -4056,7 +4128,7 @@ pub const Expect = struct { return .zero; } - pub fn toMatchObject(this: *Expect, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn toMatchObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { JSC.markBinding(@src()); defer this.postMatch(globalThis); @@ -4120,7 +4192,7 @@ pub const Expect = struct { return .zero; } - pub fn toHaveBeenCalledWith(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveBeenCalledWith(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { JSC.markBinding(@src()); const thisValue = callframe.this(); @@ -4183,7 +4255,7 @@ pub const Expect = struct { return .zero; } - pub fn toHaveBeenLastCalledWith(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveBeenLastCalledWith(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { JSC.markBinding(@src()); const thisValue = callframe.this(); @@ -4232,7 +4304,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received_fmt = lastCallValue.toFmt(globalThis, &formatter); + const received_fmt = lastCallValue.toFmt(&formatter); if (not) { const signature = comptime getSignature("toHaveBeenLastCalledWith", "expected", true); @@ -4245,7 +4317,7 @@ pub const Expect = struct { return .zero; } - pub fn toHaveBeenNthCalledWith(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveBeenNthCalledWith(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { JSC.markBinding(@src()); const thisValue = callframe.this(); @@ -4300,7 +4372,7 @@ pub const Expect = struct { // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - const received_fmt = nthCallValue.toFmt(globalThis, &formatter); + const received_fmt = nthCallValue.toFmt(&formatter); if (not) { const signature = comptime getSignature("toHaveBeenNthCalledWith", "expected", true); @@ -4321,7 +4393,7 @@ pub const Expect = struct { pub const Map = bun.ComptimeEnumMap(ReturnStatus); }; - inline fn toHaveReturnedTimesFn(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, comptime known_index: ?i32) JSC.JSValue { + inline fn toHaveReturnedTimesFn(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame, comptime known_index: ?i32) JSValue { JSC.markBinding(@src()); const thisValue = callframe.this(); @@ -4395,7 +4467,7 @@ pub const Expect = struct { .globalThis = globalThis, .quote_strings = true, }; - globalThis.throwPretty(fmt, .{times_value.get(globalThis, "value").?.toFmt(globalThis, &formatter)}); + globalThis.throwPretty(fmt, .{times_value.get(globalThis, "value").?.toFmt(&formatter)}); return .zero; } @@ -4408,11 +4480,11 @@ pub const Expect = struct { } } - pub fn toHaveReturned(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveReturned(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return toHaveReturnedTimesFn(this, globalThis, callframe, 1); } - pub fn toHaveReturnedTimes(this: *Expect, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toHaveReturnedTimes(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return toHaveReturnedTimesFn(this, globalThis, callframe, null); } @@ -4423,48 +4495,48 @@ pub const Expect = struct { pub const toThrowErrorMatchingSnapshot = notImplementedJSCFn; pub const toThrowErrorMatchingInlineSnapshot = notImplementedJSCFn; - pub fn getStaticNot(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) callconv(.C) JSValue { + pub fn getStaticNot(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) JSValue { return ExpectStatic.create(globalThis, .{ .not = true }); } - pub fn getStaticResolvesTo(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) callconv(.C) JSValue { + pub fn getStaticResolvesTo(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) JSValue { return ExpectStatic.create(globalThis, .{ .promise = .resolves }); } - pub fn getStaticRejectsTo(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) callconv(.C) JSValue { + pub fn getStaticRejectsTo(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) JSValue { return ExpectStatic.create(globalThis, .{ .promise = .rejects }); } - pub fn any(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn any(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectAny.call(globalThis, callFrame); } - pub fn anything(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn anything(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectAnything.call(globalThis, callFrame); } - pub fn closeTo(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn closeTo(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectCloseTo.call(globalThis, callFrame); } - pub fn objectContaining(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn objectContaining(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectObjectContaining.call(globalThis, callFrame); } - pub fn stringContaining(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn stringContaining(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectStringContaining.call(globalThis, callFrame); } - pub fn stringMatching(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn stringMatching(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectStringMatching.call(globalThis, callFrame); } - pub fn arrayContaining(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn arrayContaining(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return ExpectArrayContaining.call(globalThis, callFrame); } /// Implements `expect.extend({ ... })` - pub fn extend(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn extend(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or !args[0].isObject()) { @@ -4497,7 +4569,7 @@ pub const Expect = struct { // Even though they point to the same native functions for all matchers, // multiple instances are created because each instance will hold the matcher_fn as a property - const wrapper_fn = Bun__JSWrappingFunction__create(globalThis, matcher_name, &Expect.applyCustomMatcher, matcher_fn, true); + const wrapper_fn = Bun__JSWrappingFunction__create(globalThis, matcher_name, Expect.applyCustomMatcher, matcher_fn, true); expect_proto.put(globalThis, matcher_name, wrapper_fn); expect_constructor.put(globalThis, matcher_name, wrapper_fn); @@ -4512,7 +4584,7 @@ pub const Expect = struct { const CustomMatcherParamsFormatter = struct { colors: bool, - globalThis: *JSC.JSGlobalObject, + globalThis: *JSGlobalObject, matcher_fn: JSValue, pub fn format(this: CustomMatcherParamsFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { @@ -4557,7 +4629,7 @@ pub const Expect = struct { } }; - fn throwInvalidMatcherError(globalThis: *JSC.JSGlobalObject, matcher_name: bun.String, result: JSValue) void { + fn throwInvalidMatcherError(globalThis: *JSGlobalObject, matcher_name: bun.String, result: JSValue) void { @setCold(true); var formatter = JSC.ConsoleObject.Formatter{ @@ -4571,16 +4643,16 @@ pub const Expect = struct { " {{message?: string | function, pass: boolean}}\n" ++ "'{any}' was returned"; const err = switch (Output.enable_ansi_colors) { - inline else => |colors| globalThis.createErrorInstance(Output.prettyFmt(fmt, colors), .{ matcher_name, result.toFmt(globalThis, &formatter) }), + inline else => |colors| globalThis.createErrorInstance(Output.prettyFmt(fmt, colors), .{ matcher_name, result.toFmt(&formatter) }), }; - err.put(globalThis, ZigString.static("name"), ZigString.init("InvalidMatcherError").toValueGC(globalThis)); + err.put(globalThis, ZigString.static("name"), ZigString.init("InvalidMatcherError").toJS(globalThis)); globalThis.throwValue(err); } /// Execute the custom matcher for the given args (the left value + the args passed to the matcher call). /// This function is called both for symmetric and asymmetric matching. /// If silent=false, throws an exception in JS if the matcher result didn't result in a pass (or if the matcher result is invalid). - pub fn executeCustomMatcher(globalThis: *JSC.JSGlobalObject, matcher_name: bun.String, matcher_fn: JSValue, args: []const JSValue, flags: Expect.Flags, silent: bool) bool { + pub fn executeCustomMatcher(globalThis: *JSGlobalObject, matcher_name: bun.String, matcher_fn: JSValue, args: []const JSValue, flags: Expect.Flags, silent: bool) bool { // prepare the this object const matcher_context = globalThis.bunVM().allocator.create(ExpectMatcherContext) catch { globalThis.throwOutOfMemory(); @@ -4591,7 +4663,7 @@ pub const Expect = struct { matcher_context_jsvalue.ensureStillAlive(); // call the custom matcher implementation - var result = matcher_fn.callWithThis(globalThis, matcher_context_jsvalue, args); + var result = matcher_fn.call(globalThis, matcher_context_jsvalue, args) catch |err| globalThis.takeException(err); assert(!result.isEmpty()); if (result.toError()) |err| { globalThis.throwValue(err); @@ -4608,9 +4680,9 @@ pub const Expect = struct { result.ensureStillAlive(); assert(!result.isEmpty()); switch (promise.status(vm)) { - .Pending => unreachable, - .Fulfilled => {}, - .Rejected => { + .pending => unreachable, + .fulfilled => {}, + .rejected => { // TODO: rewrite this code to use .then() instead of blocking the event loop JSC.VirtualMachine.get().runErrorHandler(result, null); globalThis.throw("Matcher `{s}` returned a promise that rejected", .{matcher_name}); @@ -4629,7 +4701,7 @@ pub const Expect = struct { pass = pass_value.toBooleanSlow(globalThis); if (globalThis.hasException()) return false; - if (result.get(globalThis, "message")) |message_value| { + if (result.fastGet(globalThis, .message)) |message_value| { if (!message_value.isString() and !message_value.isCallable(globalThis.vm())) { break :valid false; } @@ -4662,7 +4734,8 @@ pub const Expect = struct { if (comptime Environment.allow_assert) assert(message.isCallable(globalThis.vm())); // checked above - var message_result = message.callWithGlobalThis(globalThis, &[_]JSValue{}); + const message_result = message.callWithGlobalThis(globalThis, &.{}) catch |err| + globalThis.takeException(err); assert(!message_result.isEmpty()); if (message_result.toError()) |err| { globalThis.throwValue(err); @@ -4678,7 +4751,7 @@ pub const Expect = struct { }; globalThis.throw( "Expected custom matcher message to return a string, but got: {}", - .{message_result.toFmt(globalThis, &formatter)}, + .{message_result.toFmt(&formatter)}, ); return false; } @@ -4695,7 +4768,7 @@ pub const Expect = struct { /// Function that is run for either `expect.myMatcher()` call or `expect().myMatcher` call, /// and we can known which case it is based on if the `callFrame.this()` value is an instance of Expect - pub fn applyCustomMatcher(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn applyCustomMatcher(globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(JSC.conv) JSValue { defer globalThis.bunVM().autoGarbageCollect(); // retrieve the user-provided matcher function (matcher_fn) @@ -4757,7 +4830,7 @@ pub const Expect = struct { pub const addSnapshotSerializer = notImplementedStaticFn; - pub fn hasAssertions(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn hasAssertions(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { _ = callFrame; defer globalThis.bunVM().autoGarbageCollect(); @@ -4766,7 +4839,7 @@ pub const Expect = struct { return .undefined; } - pub fn assertions(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn assertions(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { defer globalThis.bunVM().autoGarbageCollect(); const arguments_ = callFrame.arguments(1); @@ -4781,51 +4854,51 @@ pub const Expect = struct { if (!expected.isNumber()) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(globalThis, &fmt)}); + globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(&fmt)}); return .zero; } - const expected_assertions: f64 = expected.asNumber(); - if (@round(expected_assertions) != expected_assertions or std.math.isInf(expected_assertions) or std.math.isNan(expected_assertions) or expected_assertions < 0) { + const expected_assertions: f64 = expected.coerceToDouble(globalThis); + if (@round(expected_assertions) != expected_assertions or std.math.isInf(expected_assertions) or std.math.isNan(expected_assertions) or expected_assertions < 0 or expected_assertions > std.math.maxInt(u32)) { var fmt = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; - globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(globalThis, &fmt)}); + globalThis.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(&fmt)}); return .zero; } const unsigned_expected_assertions: u32 = @intFromFloat(expected_assertions); is_expecting_assertions_count = true; - expected_assertions_number = unsigned_expected_assertions; + active_test_expectation_counter.expected = unsigned_expected_assertions; return .undefined; } - pub fn notImplementedJSCFn(_: *Expect, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn notImplementedJSCFn(_: *Expect, globalThis: *JSGlobalObject, _: *CallFrame) JSValue { globalThis.throw("Not implemented", .{}); return .zero; } - pub fn notImplementedStaticFn(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn notImplementedStaticFn(globalThis: *JSGlobalObject, _: *CallFrame) JSValue { globalThis.throw("Not implemented", .{}); return .zero; } - pub fn notImplementedJSCProp(_: *Expect, _: JSC.JSValue, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn notImplementedJSCProp(_: *Expect, _: JSValue, globalThis: *JSGlobalObject) JSValue { globalThis.throw("Not implemented", .{}); return .zero; } - pub fn notImplementedStaticProp(globalThis: *JSC.JSGlobalObject, _: JSC.JSValue, _: JSC.JSValue) callconv(.C) JSC.JSValue { + pub fn notImplementedStaticProp(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) JSValue { globalThis.throw("Not implemented", .{}); return .zero; } - pub fn postMatch(_: *Expect, globalThis: *JSC.JSGlobalObject) void { + pub fn postMatch(_: *Expect, globalThis: *JSGlobalObject) void { var vm = globalThis.bunVM(); vm.autoGarbageCollect(); } - pub fn doUnreachable(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn doUnreachable(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arg = callframe.arguments(1).ptr[0]; if (arg.isEmptyOrUndefinedOrNull()) { @@ -4860,7 +4933,7 @@ pub const ExpectStatic = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn create(globalThis: *JSC.JSGlobalObject, flags: Expect.Flags) JSValue { + pub fn create(globalThis: *JSGlobalObject, flags: Expect.Flags) JSValue { var expect = globalThis.bunVM().allocator.create(ExpectStatic) catch { globalThis.throwOutOfMemory(); return .zero; @@ -4872,20 +4945,20 @@ pub const ExpectStatic = struct { return value; } - pub fn getNot(this: *ExpectStatic, _: JSValue, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn getNot(this: *ExpectStatic, _: JSValue, globalThis: *JSGlobalObject) JSValue { var flags = this.flags; flags.not = !this.flags.not; return create(globalThis, flags); } - pub fn getResolvesTo(this: *ExpectStatic, _: JSValue, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn getResolvesTo(this: *ExpectStatic, _: JSValue, globalThis: *JSGlobalObject) JSValue { var flags = this.flags; if (flags.promise != .none) return asyncChainingError(globalThis, flags, "resolvesTo"); flags.promise = .resolves; return create(globalThis, flags); } - pub fn getRejectsTo(this: *ExpectStatic, _: JSValue, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn getRejectsTo(this: *ExpectStatic, _: JSValue, globalThis: *JSGlobalObject) JSValue { var flags = this.flags; if (flags.promise != .none) return asyncChainingError(globalThis, flags, "rejectsTo"); flags.promise = .rejects; @@ -4903,7 +4976,7 @@ pub const ExpectStatic = struct { return .zero; } - fn createAsymmetricMatcherWithFlags(T: anytype, this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSValue { + fn createAsymmetricMatcherWithFlags(T: anytype, this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { //const this: *ExpectStatic = ExpectStatic.fromJS(callFrame.this()); const instance_jsvalue = T.call(globalThis, callFrame); if (!instance_jsvalue.isEmpty() and !instance_jsvalue.isAnyError()) { @@ -4916,31 +4989,31 @@ pub const ExpectStatic = struct { return instance_jsvalue; } - pub fn anything(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn anything(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectAnything, this, globalThis, callFrame); } - pub fn any(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn any(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectAny, this, globalThis, callFrame); } - pub fn arrayContaining(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn arrayContaining(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectArrayContaining, this, globalThis, callFrame); } - pub fn closeTo(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn closeTo(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectCloseTo, this, globalThis, callFrame); } - pub fn objectContaining(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn objectContaining(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectObjectContaining, this, globalThis, callFrame); } - pub fn stringContaining(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn stringContaining(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectStringContaining, this, globalThis, callFrame); } - pub fn stringMatching(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn stringMatching(this: *ExpectStatic, globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { return createAsymmetricMatcherWithFlags(ExpectStringMatching, this, globalThis, callFrame); } }; @@ -4956,7 +5029,7 @@ pub const ExpectAnything = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, _: *CallFrame) JSValue { const anything = globalThis.bunVM().allocator.create(ExpectAnything) catch { globalThis.throwOutOfMemory(); return .zero; @@ -4984,7 +5057,7 @@ pub const ExpectStringMatching = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or (!args[0].isString() and !args[0].isRegExp())) { @@ -5021,7 +5094,7 @@ pub const ExpectCloseTo = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const args = callFrame.arguments(2).slice(); if (args.len == 0 or !args[0].isNumber()) { @@ -5068,7 +5141,7 @@ pub const ExpectObjectContaining = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or !args[0].isObject()) { @@ -5105,7 +5178,7 @@ pub const ExpectStringContaining = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or !args[0].isString()) { @@ -5142,7 +5215,7 @@ pub const ExpectAny = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn call(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const _arguments = callFrame.arguments(1); const arguments: []const JSValue = _arguments.ptr[0.._arguments.len]; @@ -5159,11 +5232,22 @@ pub const ExpectAny = struct { return .zero; } + const asymmetric_matcher_constructor_type = Expect.Flags.AsymmetricMatcherConstructorType.fromJS(globalThis, constructor); + + // I don't think this case is possible, but just in case! + if (globalThis.hasException()) { + return .zero; + } + var any = globalThis.bunVM().allocator.create(ExpectAny) catch { globalThis.throwOutOfMemory(); return .zero; }; - any.* = .{}; + any.* = .{ + .flags = .{ + .asymmetric_matcher_constructor_type = asymmetric_matcher_constructor_type, + }, + }; const any_js_value = any.toJS(globalThis); any_js_value.ensureStillAlive(); @@ -5188,7 +5272,7 @@ pub const ExpectArrayContaining = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn call(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callFrame: *CallFrame) JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or !args[0].jsType().isArray()) { @@ -5232,7 +5316,7 @@ pub const ExpectCustomAsymmetricMatcher = struct { /// Implements the static call of the custom matcher (`expect.myCustomMatcher()`), /// which creates an asymmetric matcher instance (`ExpectCustomAsymmetricMatcher`). /// This will not run the matcher, but just capture the args etc. - pub fn create(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame, matcher_fn: JSValue) callconv(.C) JSValue { + pub fn create(globalThis: *JSGlobalObject, callFrame: *CallFrame, matcher_fn: JSValue) JSValue { var flags: Expect.Flags = undefined; // try to retrieve the ExpectStatic instance (to get the flags) @@ -5274,7 +5358,7 @@ pub const ExpectCustomAsymmetricMatcher = struct { } /// Function called by c++ function "matchAsymmetricMatcher" to execute the custom matcher against the provided leftValue - pub fn execute(this: *ExpectCustomAsymmetricMatcher, thisValue: JSValue, globalThis: *JSC.JSGlobalObject, received: JSValue) callconv(.C) bool { + pub fn execute(this: *ExpectCustomAsymmetricMatcher, thisValue: JSValue, globalThis: *JSGlobalObject, received: JSValue) callconv(.C) bool { // retrieve the user-provided matcher implementation function (the function passed to expect.extend({ ... })) const matcher_fn: JSValue = ExpectCustomAsymmetricMatcher.matcherFnGetCached(thisValue) orelse { globalThis.throw("Internal consistency error: the ExpectCustomAsymmetricMatcher(matcherFn) was garbage collected but it should not have been!", .{}); @@ -5312,15 +5396,15 @@ pub const ExpectCustomAsymmetricMatcher = struct { return Expect.executeCustomMatcher(globalThis, matcher_name, matcher_fn, matcher_args.items, this.flags, true); } - pub fn asymmetricMatch(this: *ExpectCustomAsymmetricMatcher, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn asymmetricMatch(this: *ExpectCustomAsymmetricMatcher, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(1).slice(); - const received_value = if (arguments.len < 1) JSC.JSValue.jsUndefined() else arguments[0]; + const received_value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; const matched = execute(this, callframe.this(), globalThis, received_value); return JSValue.jsBoolean(matched); } /// Calls a custom implementation (if provided) to stringify this asymmetric matcher, and returns true if it was provided and it succeed - pub fn customPrint(_: *ExpectCustomAsymmetricMatcher, thisValue: JSValue, globalThis: *JSC.JSGlobalObject, writer: anytype, comptime dontThrow: bool) !bool { + pub fn customPrint(_: *ExpectCustomAsymmetricMatcher, thisValue: JSValue, globalThis: *JSGlobalObject, writer: anytype, comptime dontThrow: bool) !bool { const matcher_fn: JSValue = ExpectCustomAsymmetricMatcher.matcherFnGetCached(thisValue) orelse return false; if (matcher_fn.get(globalThis, "toAsymmetricMatcher")) |fn_value| { if (fn_value.jsType().isFunction()) { @@ -5333,22 +5417,20 @@ pub const ExpectCustomAsymmetricMatcher = struct { args.appendAssumeCapacity(arg); } - var result = matcher_fn.callWithThis(globalThis, thisValue, args.items); - if (result.toError()) |err| { + const result = matcher_fn.call(globalThis, thisValue, args.items) catch |err| { if (dontThrow) { + globalThis.clearException(); return false; - } else { - globalThis.throwValue(globalThis, err); - return error.JSError; } - } + return err; + }; try writer.print("{}", .{result.toBunString(globalThis)}); } } return false; } - pub fn toAsymmetricMatcher(this: *ExpectCustomAsymmetricMatcher, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn toAsymmetricMatcher(this: *ExpectCustomAsymmetricMatcher, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { var stack_fallback = std.heap.stackFallback(512, globalThis.allocator()); var mutable_string = bun.MutableString.init2048(stack_fallback.get()) catch { globalThis.throwOutOfMemory(); @@ -5379,15 +5461,15 @@ pub const ExpectMatcherContext = struct { VirtualMachine.get().allocator.destroy(this); } - pub fn getUtils(_: *ExpectMatcherContext, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getUtils(_: *ExpectMatcherContext, globalThis: *JSGlobalObject) JSValue { return ExpectMatcherUtils__getSingleton(globalThis); } - pub fn getIsNot(this: *ExpectMatcherContext, _: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getIsNot(this: *ExpectMatcherContext, _: *JSGlobalObject) JSValue { return JSValue.jsBoolean(this.flags.not); } - pub fn getPromise(this: *ExpectMatcherContext, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getPromise(this: *ExpectMatcherContext, globalThis: *JSGlobalObject) JSValue { return switch (this.flags.promise) { .rejects => bun.String.static("rejects").toJS(globalThis), .resolves => bun.String.static("resolves").toJS(globalThis), @@ -5395,13 +5477,13 @@ pub const ExpectMatcherContext = struct { }; } - pub fn getExpand(_: *ExpectMatcherContext, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getExpand(_: *ExpectMatcherContext, globalThis: *JSGlobalObject) JSValue { _ = globalThis; // TODO: this should return whether running tests in verbose mode or not (jest flag --expand), but bun currently doesn't have this switch return JSValue.false; } - pub fn equals(_: *ExpectMatcherContext, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn equals(_: *ExpectMatcherContext, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { var arguments = callframe.arguments(3); if (arguments.len < 2) { globalThis.throw("expect.extends matcher: this.util.equals expects at least 2 arguments", .{}); @@ -5416,7 +5498,7 @@ pub const ExpectMatcherContext = struct { pub const ExpectMatcherUtils = struct { pub usingnamespace JSC.Codegen.JSExpectMatcherUtils; - fn createSingleton(globalThis: *JSC.JSGlobalObject) callconv(.C) JSValue { + fn createSingleton(globalThis: *JSGlobalObject) callconv(.C) JSValue { var instance = globalThis.bunVM().allocator.create(ExpectMatcherUtils) catch { globalThis.throwOutOfMemory(); return .zero; @@ -5430,7 +5512,7 @@ pub const ExpectMatcherUtils = struct { VirtualMachine.get().allocator.destroy(this); } - fn printValue(globalThis: *JSC.JSGlobalObject, value: JSValue, comptime color_or_null: ?[]const u8) !JSValue { + fn printValue(globalThis: *JSGlobalObject, value: JSValue, comptime color_or_null: ?[]const u8) !JSValue { var stack_fallback = std.heap.stackFallback(512, globalThis.allocator()); var mutable_string = try bun.MutableString.init2048(stack_fallback.get()); defer mutable_string.deinit(); @@ -5448,7 +5530,7 @@ pub const ExpectMatcherUtils = struct { .globalThis = globalThis, .quote_strings = true, }; - try writer.print("{}", .{value.toFmt(globalThis, &formatter)}); + try writer.print("{}", .{value.toFmt(&formatter)}); if (comptime color_or_null) |_| { if (Output.enable_ansi_colors) { @@ -5463,32 +5545,32 @@ pub const ExpectMatcherUtils = struct { return str.toJS(globalThis); } - inline fn printValueCatched(globalThis: *JSC.JSGlobalObject, value: JSValue, comptime color_or_null: ?[]const u8) JSValue { + inline fn printValueCatched(globalThis: *JSGlobalObject, value: JSValue, comptime color_or_null: ?[]const u8) JSValue { return printValue(globalThis, value, color_or_null) catch { globalThis.throwOutOfMemory(); return .zero; }; } - pub fn stringify(_: *ExpectMatcherUtils, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn stringify(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(1).slice(); - const value = if (arguments.len < 1) JSC.JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; return printValueCatched(globalThis, value, null); } - pub fn printExpected(_: *ExpectMatcherUtils, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn printExpected(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(1).slice(); - const value = if (arguments.len < 1) JSC.JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; return printValueCatched(globalThis, value, ""); } - pub fn printReceived(_: *ExpectMatcherUtils, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn printReceived(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(1).slice(); - const value = if (arguments.len < 1) JSC.JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; return printValueCatched(globalThis, value, ""); } - pub fn matcherHint(_: *ExpectMatcherUtils, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn matcherHint(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(4).slice(); if (arguments.len == 0 or !arguments[0].isString()) { @@ -5569,13 +5651,13 @@ extern fn JSMockFunction__getCalls(JSValue) JSValue; /// If there were no calls, it returns an empty JSArray* extern fn JSMockFunction__getReturns(JSValue) JSValue; -extern fn Bun__JSWrappingFunction__create(globalThis: *JSC.JSGlobalObject, symbolName: *const bun.String, functionPointer: JSC.JSHostFunctionPtr, wrappedFn: JSValue, strong: bool) JSValue; -extern fn Bun__JSWrappingFunction__getWrappedFunction(this: JSC.JSValue, globalThis: *JSC.JSGlobalObject) JSValue; +extern fn Bun__JSWrappingFunction__create(globalThis: *JSGlobalObject, symbolName: *const bun.String, functionPointer: JSC.JSHostFunctionPtr, wrappedFn: JSValue, strong: bool) JSValue; +extern fn Bun__JSWrappingFunction__getWrappedFunction(this: JSValue, globalThis: *JSGlobalObject) JSValue; -extern fn ExpectMatcherUtils__getSingleton(globalThis: *JSC.JSGlobalObject) JSC.JSValue; +extern fn ExpectMatcherUtils__getSingleton(globalThis: *JSGlobalObject) JSValue; -extern fn Expect__getPrototype(globalThis: *JSC.JSGlobalObject) JSC.JSValue; -extern fn ExpectStatic__getPrototype(globalThis: *JSC.JSGlobalObject) JSC.JSValue; +extern fn Expect__getPrototype(globalThis: *JSGlobalObject) JSValue; +extern fn ExpectStatic__getPrototype(globalThis: *JSGlobalObject) JSValue; comptime { @export(ExpectMatcherUtils.createSingleton, .{ .name = "ExpectMatcherUtils_createSigleton" }); diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 50c7d2cbc4152..c057b69b579e5 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -92,10 +92,10 @@ pub const TestRunner = struct { test_options: *const bun.CLI.Command.TestOptions = undefined, global_callbacks: struct { - beforeAll: std.ArrayListUnmanaged(JSC.JSValue) = .{}, - beforeEach: std.ArrayListUnmanaged(JSC.JSValue) = .{}, - afterEach: std.ArrayListUnmanaged(JSC.JSValue) = .{}, - afterAll: std.ArrayListUnmanaged(JSC.JSValue) = .{}, + beforeAll: std.ArrayListUnmanaged(JSValue) = .{}, + beforeEach: std.ArrayListUnmanaged(JSValue) = .{}, + afterEach: std.ArrayListUnmanaged(JSValue) = .{}, + afterAll: std.ArrayListUnmanaged(JSValue) = .{}, } = .{}, // Used for --test-name-pattern to reduce allocations @@ -264,6 +264,8 @@ pub const TestRunner = struct { skip, todo, fail_because_todo_passed, + fail_because_expected_has_assertions, + fail_because_expected_assertion_count, }; }; }; @@ -274,9 +276,9 @@ pub const Jest = struct { fn globalHook(comptime name: string) JSC.JSHostFunctionType { return struct { pub fn appendGlobalFunctionCallback( - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + globalThis: *JSGlobalObject, + callframe: *CallFrame, + ) callconv(JSC.conv) JSValue { const the_runner = runner orelse { globalThis.throw("Cannot use " ++ name ++ "() outside of the test runner. Run \"bun test\" to run tests.", .{}); return .zero; @@ -304,26 +306,26 @@ pub const Jest = struct { bun.default_allocator, function, ) catch unreachable; - return JSC.JSValue.jsUndefined(); + return JSValue.jsUndefined(); } }.appendGlobalFunctionCallback; } - pub fn Bun__Jest__createTestModuleObject(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn Bun__Jest__createTestModuleObject(globalObject: *JSGlobalObject) callconv(.C) JSValue { return createTestModule(globalObject, false); } - pub fn Bun__Jest__createTestPreloadObject(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn Bun__Jest__createTestPreloadObject(globalObject: *JSGlobalObject) callconv(.C) JSValue { return createTestModule(globalObject, true); } - pub fn createTestModule(globalObject: *JSC.JSGlobalObject, comptime outside_of_test: bool) JSC.JSValue { + pub fn createTestModule(globalObject: *JSGlobalObject, comptime outside_of_test: bool) JSValue { const ThisTestScope, const ThisDescribeScope = if (outside_of_test) .{ WrappedTestScope, WrappedDescribeScope } else .{ TestScope, DescribeScope }; - const module = JSC.JSValue.createEmptyObject(globalObject, 14); + const module = JSValue.createEmptyObject(globalObject, 14); const test_fn = JSC.NewFunction(globalObject, ZigString.static("test"), 2, ThisTestScope.call, false); module.put( @@ -419,13 +421,12 @@ pub const Jest = struct { const function = if (outside_of_test) JSC.NewFunction(globalObject, null, 1, globalHook(name), false) else - JSC.NewRuntimeFunction( + JSC.NewFunction( globalObject, ZigString.static(name), 1, @field(DescribeScope, name), false, - false, ); module.put(globalObject, ZigString.static(name), function); function.ensureStillAlive(); @@ -448,7 +449,7 @@ pub const Jest = struct { return module; } - fn createMockObjects(globalObject: *JSGlobalObject, module: JSC.JSValue) void { + fn createMockObjects(globalObject: *JSGlobalObject, module: JSValue) void { const setSystemTime = JSC.NewFunction(globalObject, ZigString.static("setSystemTime"), 0, JSMock__jsSetSystemTime, false); module.put( globalObject, @@ -507,22 +508,22 @@ pub const Jest = struct { module.put(globalObject, ZigString.static("vi"), vi); } - extern fn Bun__Jest__testPreloadObject(*JSC.JSGlobalObject) JSC.JSValue; - extern fn Bun__Jest__testModuleObject(*JSC.JSGlobalObject) JSC.JSValue; - extern fn JSMock__jsMockFn(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsModuleMock(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsNow(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsSetSystemTime(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsRestoreAllMocks(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsClearAllMocks(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsSpyOn(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsUseFakeTimers(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; - extern fn JSMock__jsUseRealTimers(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; + extern fn Bun__Jest__testPreloadObject(*JSGlobalObject) JSValue; + extern fn Bun__Jest__testModuleObject(*JSGlobalObject) JSValue; + extern fn JSMock__jsMockFn(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsModuleMock(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsNow(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsSetSystemTime(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsRestoreAllMocks(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsClearAllMocks(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsSpyOn(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsUseFakeTimers(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; + extern fn JSMock__jsUseRealTimers(*JSGlobalObject, *CallFrame) callconv(JSC.conv) JSValue; pub fn call( - globalObject: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalObject: *JSGlobalObject, + callframe: *CallFrame, + ) JSValue { const vm = globalObject.bunVM(); if (vm.is_in_preload or runner == null) { return Bun__Jest__testPreloadObject(globalObject); @@ -550,7 +551,7 @@ pub const Jest = struct { return Bun__Jest__testModuleObject(globalObject); } - fn jsSetDefaultTimeout(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + fn jsSetDefaultTimeout(globalObject: *JSGlobalObject, callframe: *CallFrame) JSValue { const arguments = callframe.arguments(1).slice(); if (arguments.len < 1 or !arguments[0].isNumber()) { globalObject.throw("setTimeout() expects a number (milliseconds)", .{}); @@ -578,8 +579,8 @@ pub const TestScope = struct { label: string = "", parent: *DescribeScope, - func: JSC.JSValue, - func_arg: []JSC.JSValue, + func: JSValue, + func_arg: []JSValue, func_has_callback: bool = false, id: TestRunner.Test.ID = 0, @@ -600,39 +601,39 @@ pub const TestScope = struct { actual: u32 = 0, }; - pub fn call(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "test()", true, .pass); } - pub fn only(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn only(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "test.only()", true, .only); } - pub fn skip(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn skip(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "test.skip()", true, .skip); } - pub fn todo(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn todo(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "test.todo()", true, .todo); } - pub fn each(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn each(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createEach(globalThis, callframe, "test.each()", "each", true); } - pub fn callIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn callIf(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createIfScope(globalThis, callframe, "test.if()", "if", TestScope, .pass); } - pub fn skipIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn skipIf(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createIfScope(globalThis, callframe, "test.skipIf()", "skipIf", TestScope, .skip); } - pub fn todoIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn todoIf(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createIfScope(globalThis, callframe, "test.todoIf()", "todoIf", TestScope, .todo); } - pub fn onReject(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onReject(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { debug("onReject", .{}); const arguments = callframe.arguments(2); const err = arguments.ptr[0]; @@ -642,8 +643,9 @@ pub const TestScope = struct { globalThis.bunVM().autoGarbageCollect(); return JSValue.jsUndefined(); } + const jsOnReject = JSC.toJSHostFunction(onReject); - pub fn onResolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onResolve(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { debug("onResolve", .{}); const arguments = callframe.arguments(2); var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask); @@ -651,11 +653,12 @@ pub const TestScope = struct { globalThis.bunVM().autoGarbageCollect(); return JSValue.jsUndefined(); } + const jsOnResolve = JSC.toJSHostFunction(onResolve); pub fn onDone( - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + globalThis: *JSGlobalObject, + callframe: *CallFrame, + ) JSValue { const function = callframe.callee(); const args = callframe.arguments(1); defer globalThis.bunVM().autoGarbageCollect(); @@ -753,11 +756,11 @@ pub const TestScope = struct { // TODO: not easy to coerce JSInternalPromise as JSValue, // so simply wait for completion for now. switch (promise) { - .Internal => vm.waitForPromise(promise), + .internal => vm.waitForPromise(promise), else => {}, } switch (promise.status(vm.global.vm())) { - .Rejected => { + .rejected => { if (!promise.isHandled(vm.global.vm())) { _ = vm.unhandledRejection(vm.global, promise.result(vm.global.vm()), promise.asValue(vm.global)); } @@ -768,11 +771,11 @@ pub const TestScope = struct { return .{ .fail = expect.active_test_expectation_counter.actual }; }, - .Pending => { + .pending => { task.promise_state = .pending; switch (promise) { - .Normal => |p| { - _ = p.asValue(vm.global).then(vm.global, task, onResolve, onReject); + .normal => |p| { + _ = p.asValue(vm.global).then(vm.global, task, jsOnResolve, jsOnReject); return .{ .pending = {} }; }, else => unreachable, @@ -801,29 +804,23 @@ pub const TestScope = struct { pub const name = "TestScope"; pub const shim = JSC.Shimmer("Bun", name, @This()); - pub const Export = shim.exportFunctions(.{ - .onResolve = onResolve, - .onReject = onReject, - }); comptime { - if (!JSC.is_bindgen) { - @export(onResolve, .{ - .name = Export[0].symbol_name, - }); - @export(onReject, .{ - .name = Export[1].symbol_name, - }); - } + @export(jsOnResolve, .{ + .name = shim.symbolName("onResolve"), + }); + @export(jsOnReject, .{ + .name = shim.symbolName("onReject"), + }); } }; pub const DescribeScope = struct { label: string = "", parent: ?*DescribeScope = null, - beforeAll: std.ArrayListUnmanaged(JSC.JSValue) = .{}, - beforeEach: std.ArrayListUnmanaged(JSC.JSValue) = .{}, - afterEach: std.ArrayListUnmanaged(JSC.JSValue) = .{}, - afterAll: std.ArrayListUnmanaged(JSC.JSValue) = .{}, + beforeAll: std.ArrayListUnmanaged(JSValue) = .{}, + beforeEach: std.ArrayListUnmanaged(JSValue) = .{}, + afterEach: std.ArrayListUnmanaged(JSValue) = .{}, + afterAll: std.ArrayListUnmanaged(JSValue) = .{}, test_id_start: TestRunner.Test.ID = 0, test_id_len: TestRunner.Test.ID = 0, tests: std.ArrayListUnmanaged(TestScope) = .{}, @@ -890,17 +887,14 @@ pub const DescribeScope = struct { pub threadlocal var active: ?*DescribeScope = null; - const CallbackFn = fn ( - *JSC.JSGlobalObject, - *JSC.CallFrame, - ) callconv(.C) JSC.JSValue; + const CallbackFn = JSC.JSHostFunctionType; fn createCallback(comptime hook: LifecycleHook) CallbackFn { return struct { pub fn run( - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + globalThis: *JSGlobalObject, + callframe: *CallFrame, + ) callconv(JSC.conv) JSValue { const arguments = callframe.arguments(2); if (arguments.len < 1) { globalThis.throwNotEnoughArguments("callback", 1, arguments.len); @@ -915,15 +909,15 @@ pub const DescribeScope = struct { cb.protect(); @field(DescribeScope.active.?, @tagName(hook)).append(getAllocator(globalThis), cb) catch unreachable; - return JSC.JSValue.jsBoolean(true); + return JSValue.jsBoolean(true); } }.run; } pub fn onDone( ctx: js.JSContextRef, - callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + callframe: *CallFrame, + ) JSValue { const function = callframe.callee(); const args = callframe.arguments(1); defer ctx.bunVM().autoGarbageCollect(); @@ -948,7 +942,7 @@ pub const DescribeScope = struct { pub const beforeAll = createCallback(.beforeAll); pub const beforeEach = createCallback(.beforeEach); - pub fn execCallback(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { + pub fn execCallback(this: *DescribeScope, globalObject: *JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { var hooks = &@field(this, @tagName(hook)); defer { if (comptime hook == .beforeAll or hook == .afterAll) { @@ -968,7 +962,7 @@ pub const DescribeScope = struct { } const vm = VirtualMachine.get(); - var result: JSC.JSValue = switch (cb.getLength(globalObject)) { + var result: JSValue = switch (cb.getLength(globalObject)) { 0 => callJSFunctionForTestRunner(vm, globalObject, cb, &.{}), else => brk: { this.done = false; @@ -989,7 +983,7 @@ pub const DescribeScope = struct { }, }; if (result.asAnyPromise()) |promise| { - if (promise.status(globalObject.vm()) == .Pending) { + if (promise.status(globalObject.vm()) == .pending) { result.protect(); vm.waitForPromise(promise); result.unprotect(); @@ -1004,7 +998,7 @@ pub const DescribeScope = struct { return null; } - pub fn runGlobalCallbacks(globalThis: *JSC.JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { + pub fn runGlobalCallbacks(globalThis: *JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { // global callbacks var hooks = &@field(Jest.runner.?.global_callbacks, @tagName(hook)); defer { @@ -1026,10 +1020,10 @@ pub const DescribeScope = struct { const vm = VirtualMachine.get(); // note: we do not support "done" callback in global hooks in the first release. - var result: JSC.JSValue = callJSFunctionForTestRunner(vm, globalThis, cb, &.{}); + var result: JSValue = callJSFunctionForTestRunner(vm, globalThis, cb, &.{}); if (result.asAnyPromise()) |promise| { - if (promise.status(globalThis.vm()) == .Pending) { + if (promise.status(globalThis.vm()) == .pending) { result.protect(); vm.waitForPromise(promise); result.unprotect(); @@ -1044,7 +1038,7 @@ pub const DescribeScope = struct { return null; } - fn runBeforeCallbacks(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { + fn runBeforeCallbacks(this: *DescribeScope, globalObject: *JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { if (this.parent) |scope| { if (scope.runBeforeCallbacks(globalObject, hook)) |err| { return err; @@ -1053,7 +1047,7 @@ pub const DescribeScope = struct { return this.execCallback(globalObject, hook); } - pub fn runCallback(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { + pub fn runCallback(this: *DescribeScope, globalObject: *JSGlobalObject, comptime hook: LifecycleHook) ?JSValue { if (comptime hook == .afterAll or hook == .afterEach) { var parent: ?*DescribeScope = this; while (parent) |scope| { @@ -1077,39 +1071,39 @@ pub const DescribeScope = struct { return null; } - pub fn call(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn call(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "describe()", false, .pass); } - pub fn only(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn only(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "describe.only()", false, .only); } - pub fn skip(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn skip(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "describe.skip()", false, .skip); } - pub fn todo(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn todo(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createScope(globalThis, callframe, "describe.todo()", false, .todo); } - pub fn each(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn each(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createEach(globalThis, callframe, "describe.each()", "each", false); } - pub fn callIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn callIf(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createIfScope(globalThis, callframe, "describe.if()", "if", DescribeScope, .pass); } - pub fn skipIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn skipIf(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createIfScope(globalThis, callframe, "describe.skipIf()", "skipIf", DescribeScope, .skip); } - pub fn todoIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn todoIf(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue { return createIfScope(globalThis, callframe, "describe.todoIf()", "todoIf", DescribeScope, .todo); } - pub fn run(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, callback: JSC.JSValue, args: []const JSC.JSValue) JSC.JSValue { + pub fn run(this: *DescribeScope, globalObject: *JSGlobalObject, callback: JSValue, args: []const JSValue) JSValue { if (comptime is_bindgen) return undefined; callback.protect(); defer callback.unprotect(); @@ -1129,7 +1123,7 @@ pub const DescribeScope = struct { if (result.asAnyPromise()) |prom| { globalObject.bunVM().waitForPromise(prom); switch (prom.status(globalObject.ptr().vm())) { - JSPromise.Status.Fulfilled => {}, + .fulfilled => {}, else => { _ = globalObject.bunVM().unhandledRejection(globalObject, prom.result(globalObject.ptr().vm()), prom.asValue(globalObject)); return .undefined; @@ -1145,7 +1139,7 @@ pub const DescribeScope = struct { return .undefined; } - pub fn runTests(this: *DescribeScope, globalObject: *JSC.JSGlobalObject) void { + pub fn runTests(this: *DescribeScope, globalObject: *JSGlobalObject) void { // Step 1. Initialize the test block globalObject.clearTerminationException(); @@ -1202,7 +1196,7 @@ pub const DescribeScope = struct { } } - pub fn onTestComplete(this: *DescribeScope, globalThis: *JSC.JSGlobalObject, test_id: TestRunner.Test.ID, skipped: bool) void { + pub fn onTestComplete(this: *DescribeScope, globalThis: *JSGlobalObject, test_id: TestRunner.Test.ID, skipped: bool) void { // invalidate it this.current_test_id = std.math.maxInt(TestRunner.Test.ID); if (test_id != std.math.maxInt(TestRunner.Test.ID)) this.pending_tests.unset(test_id); @@ -1249,9 +1243,9 @@ pub const DescribeScope = struct { }; -pub fn wrapTestFunction(comptime name: []const u8, comptime func: DescribeScope.CallbackFn) DescribeScope.CallbackFn { +pub fn wrapTestFunction(comptime name: []const u8, comptime func: anytype) DescribeScope.CallbackFn { return struct { - pub fn wrapped(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue { + pub fn wrapped(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(JSC.conv) JSValue { if (Jest.runner == null) { globalThis.throw("Cannot use " ++ name ++ "() outside of the test runner. Run \"bun test\" to run tests.", .{}); return .zero; @@ -1292,7 +1286,7 @@ pub const WrappedDescribeScope = struct { pub const TestRunnerTask = struct { test_id: TestRunner.Test.ID, describe: *DescribeScope, - globalThis: *JSC.JSGlobalObject, + globalThis: *JSGlobalObject, source_file_path: string = "", needs_before_each: bool = true, ref: JSC.Ref = JSC.Ref.init(), @@ -1309,7 +1303,7 @@ pub const TestRunnerTask = struct { fulfilled, }; - pub fn onUnhandledRejection(jsc_vm: *VirtualMachine, globalObject: *JSC.JSGlobalObject, rejection: JSC.JSValue) void { + pub fn onUnhandledRejection(jsc_vm: *VirtualMachine, globalObject: *JSGlobalObject, rejection: JSValue) void { var deduped = false; const is_unhandled = jsc_vm.onUnhandledRejectionCtx == null; @@ -1348,6 +1342,20 @@ pub const TestRunnerTask = struct { } } + pub fn checkAssertionsCounter(result: *Result) void { + if (expect.is_expecting_assertions and expect.active_test_expectation_counter.actual == 0) { + expect.is_expecting_assertions = false; + expect.is_expecting_assertions_count = false; + result.* = .{ .fail_because_expected_has_assertions = {} }; + } + + if (expect.is_expecting_assertions_count and expect.active_test_expectation_counter.actual != expect.active_test_expectation_counter.expected) { + expect.is_expecting_assertions = false; + expect.is_expecting_assertions_count = false; + result.* = .{ .fail_because_expected_assertion_count = expect.active_test_expectation_counter }; + } + } + pub fn run(this: *TestRunnerTask) bool { var describe = this.describe; var globalThis = this.globalThis; @@ -1372,7 +1380,7 @@ pub const TestRunnerTask = struct { var test_: TestScope = this.describe.tests.items[test_id]; describe.current_test_id = test_id; - if (test_.func == .zero or !describe.shouldEvaluateScope()) { + if (test_.func == .zero or !describe.shouldEvaluateScope() or (test_.tag != .only and Jest.runner.?.only)) { const tag = if (!describe.shouldEvaluateScope()) describe.tag else test_.tag; switch (tag) { .todo => { @@ -1410,40 +1418,24 @@ pub const TestRunnerTask = struct { } // rejected promises should fail the test - if (result != .fail) + if (!result.isFailure()) globalThis.handleRejectedPromises(); if (result == .pending and this.sync_state == .pending and (this.done_callback_state == .pending or this.promise_state == .pending)) { this.sync_state = .fulfilled; - return true; - } - - if (expect.is_expecting_assertions and expect.active_test_expectation_counter.actual == 0) { - const fmt = comptime "expect.hasAssertions()\n\nExpected at least one assertion to be called but received none.\n"; - const error_value = if (Output.enable_ansi_colors) - globalThis.createErrorInstance(Output.prettyFmt(fmt, true), .{}) - else - globalThis.createErrorInstance(Output.prettyFmt(fmt, false), .{}); - - globalThis.*.bunVM().runErrorHandler(error_value, null); - result = .{ .fail = 0 }; - } - if (expect.is_expecting_assertions_count and expect.active_test_expectation_counter.actual != expect.expected_assertions_number) { - const fmt = comptime "expect.assertions({})\n\nExpected {} assertion to be called but found {} assertions instead.\n"; - const fmt_args = .{ expect.expected_assertions_number, expect.expected_assertions_number, expect.active_test_expectation_counter.actual }; - const error_value = if (Output.enable_ansi_colors) - globalThis.createErrorInstance(Output.prettyFmt(fmt, true), fmt_args) - else - globalThis.createErrorInstance(Output.prettyFmt(fmt, false), fmt_args); + if (this.reported and this.promise_state != .pending) { + // An unhandled error was reported. + // Let's allow any pending work to run, and then move on to the next test. + this.continueRunningTestsAfterMicrotasksRun(); + } - globalThis.*.bunVM().runErrorHandler(error_value, null); - result = .{ .fail = expect.active_test_expectation_counter.actual }; + return true; } - this.handleResult(result, .sync); + this.handleResultPtr(&result, .sync); - if (result == .fail) { + if (result.isFailure()) { globalThis.handleRejectedPromises(); } @@ -1467,12 +1459,25 @@ pub const TestRunnerTask = struct { }; pub fn handleResult(this: *TestRunnerTask, result: Result, from: ResultType) void { + var result_copy = result; + this.handleResultPtr(&result_copy, from); + } + + fn continueRunningTestsAfterMicrotasksRun(this: *TestRunnerTask) void { + if (this.ref.has) + // Drain microtasks one more time. + // But don't hang forever. + // We report the test failure before that task is run. + this.globalThis.bunVM().enqueueTask(JSC.ManagedTask.New(@This(), deinit).init(this)); + } + + pub fn handleResultPtr(this: *TestRunnerTask, result: *Result, from: ResultType) void { switch (from) { .promise => { if (comptime Environment.allow_assert) assert(this.promise_state == .pending); this.promise_state = .fulfilled; - if (this.done_callback_state == .pending and result == .pass) { + if (this.done_callback_state == .pending and result.* == .pass) { return; } }, @@ -1480,7 +1485,7 @@ pub const TestRunnerTask = struct { if (comptime Environment.allow_assert) assert(this.done_callback_state == .pending); this.done_callback_state = .fulfilled; - if (this.promise_state == .pending and result == .pass) { + if (this.promise_state == .pending and result.* == .pass) { return; } }, @@ -1496,8 +1501,43 @@ pub const TestRunnerTask = struct { this.deinit(); } - if (this.reported) + if (this.reported) { + // This covers the following scenario: + // + // test("foo", async done => { + // await Bun.sleep(42); + // throw new Error("foo"); + // }); + // + // The test will hang forever if we don't drain microtasks here. + // + // It is okay for this to be called multiple times, as it unrefs() the event loop once, and doesn't free memory. + if (result.* != .pass and this.promise_state != .pending and this.done_callback_state == .pending and this.sync_state == .fulfilled) { + this.continueRunningTestsAfterMicrotasksRun(); + } return; + } + + // This covers the following scenario: + // + // + // test("foo", done => { + // setTimeout(() => { + // if (Math.random() > 0.5) { + // done(); + // } else { + // throw new Error("boom"); + // } + // }, 100); + // }) + // + // It is okay for this to be called multiple times, as it unrefs() the event loop once, and doesn't free memory. + if (this.promise_state != .pending and this.sync_state != .pending and this.done_callback_state == .pending) { + // Drain microtasks one more time. + // But don't hang forever. + // We report the test failure before that task is run. + this.continueRunningTestsAfterMicrotasksRun(); + } this.reported = true; @@ -1515,10 +1555,11 @@ pub const TestRunnerTask = struct { _ = this.globalThis.bunVM().uncaughtException(this.globalThis, err, true); } - processTestResult(this, this.globalThis, result, test_, test_id, describe); + checkAssertionsCounter(result); + processTestResult(this, this.globalThis, result.*, test_, test_id, describe); } - fn processTestResult(this: *TestRunnerTask, globalThis: *JSC.JSGlobalObject, result: Result, test_: TestScope, test_id: u32, describe: *DescribeScope) void { + fn processTestResult(this: *TestRunnerTask, globalThis: *JSGlobalObject, result: Result, test_: TestScope, test_id: u32, describe: *DescribeScope) void { switch (result.forceTODO(test_.tag == .todo)) { .pass => |count| Jest.runner.?.reportPass( test_id, @@ -1536,6 +1577,33 @@ pub const TestRunnerTask = struct { this.started_at.sinceNow(), describe, ), + .fail_because_expected_has_assertions => { + Output.err(error.AssertionError, "received 0 assertions, but expected at least one assertion to be called\n", .{}); + Output.flush(); + Jest.runner.?.reportFailure( + test_id, + this.source_file_path, + test_.label, + 0, + this.started_at.sinceNow(), + describe, + ); + }, + .fail_because_expected_assertion_count => |counter| { + Output.err(error.AssertionError, "expected {d} assertions, but test ended with {d} assertions\n", .{ + counter.expected, + counter.actual, + }); + Output.flush(); + Jest.runner.?.reportFailure( + test_id, + this.source_file_path, + test_.label, + counter.actual, + this.started_at.sinceNow(), + describe, + ); + }, .skip => Jest.runner.?.reportSkip(test_id, this.source_file_path, test_.label, describe), .todo => Jest.runner.?.reportTodo(test_id, this.source_file_path, test_.label, describe), .fail_because_todo_passed => |count| { @@ -1551,7 +1619,7 @@ pub const TestRunnerTask = struct { }, .pending => @panic("Unexpected pending test"), } - describe.onTestComplete(globalThis, test_id, result == .skip); + describe.onTestComplete(globalThis, test_id, result == .skip or (!Jest.runner.?.test_options.run_todo and result == .todo)); Jest.runner.?.runNextTest(); } @@ -1584,6 +1652,12 @@ pub const Result = union(TestRunner.Test.Status) { skip: void, todo: void, fail_because_todo_passed: u32, + fail_because_expected_has_assertions: void, + fail_because_expected_assertion_count: Counter, + + pub fn isFailure(this: *const Result) bool { + return this.* == .fail or this.* == .fail_because_expected_has_assertions or this.* == .fail_because_expected_assertion_count; + } pub fn forceTODO(this: Result, is_todo: bool) Result { if (is_todo and this == .pass) @@ -1633,7 +1707,7 @@ inline fn createScope( } if (function.isEmptyOrUndefinedOrNull() or !function.isCell() or !function.isCallable(globalThis.vm())) { - if (tag != .todo) { + if (tag != .todo and tag != .skip) { globalThis.throwPretty("{s} expects a function", .{signature}); return .zero; } @@ -1682,7 +1756,7 @@ inline fn createScope( Jest.runner.?.setOnly(); tag_to_use = .only; } else if (is_test and Jest.runner.?.only and parent.tag != .only) { - return .zero; + return .undefined; } var is_skip = tag == .skip or @@ -1718,7 +1792,7 @@ inline fn createScope( has_callback = true; arg_size = 1; } - const function_args = allocator.alloc(JSC.JSValue, arg_size) catch unreachable; + const function_args = allocator.alloc(JSValue, arg_size) catch unreachable; parent.tests.append(allocator, TestScope{ .label = label, @@ -1747,7 +1821,7 @@ inline fn createScope( inline fn createIfScope( globalThis: *JSGlobalObject, callframe: *CallFrame, - comptime property: string, + comptime property: [:0]const u8, comptime signature: string, comptime Scope: type, comptime tag: Tag, @@ -1763,45 +1837,46 @@ inline fn createIfScope( const name = ZigString.static(property); const value = args[0].toBooleanSlow(globalThis); - const truthy_falsey: [2]JSC.JSHostFunctionPtr = switch (tag) { - .pass => .{ Scope.call, Scope.skip }, + const truthy_falsey = comptime switch (tag) { + .pass => .{ Scope.skip, Scope.call }, .fail => @compileError("unreachable"), .only => @compileError("unreachable"), - .skip => .{ Scope.skip, Scope.call }, - .todo => .{ Scope.todo, Scope.call }, + .skip => .{ Scope.call, Scope.skip }, + .todo => .{ Scope.call, Scope.todo }, }; - const call = truthy_falsey[if (value) 0 else 1]; - return JSC.NewFunction(globalThis, name, 2, call, false); + switch (@intFromBool(value)) { + inline else => |index| return JSC.NewFunction(globalThis, name, 2, truthy_falsey[index], false), + } } fn consumeArg( - globalThis: *JSC.JSGlobalObject, + globalThis: *JSGlobalObject, should_write: bool, str_idx: *usize, args_idx: *usize, array_list: *std.ArrayListUnmanaged(u8), - arg: *const JSC.JSValue, + arg: *const JSValue, fallback: []const u8, ) !void { const allocator = getAllocator(globalThis); if (should_write) { const owned_slice = arg.toSliceOrNull(globalThis) orelse return error.Failed; defer owned_slice.deinit(); - try array_list.appendSlice(allocator, owned_slice.slice()); + array_list.appendSlice(allocator, owned_slice.slice()) catch bun.outOfMemory(); } else { - try array_list.appendSlice(allocator, fallback); + array_list.appendSlice(allocator, fallback) catch bun.outOfMemory(); } str_idx.* += 1; args_idx.* += 1; } // Generate test label by positionally injecting parameters with printf formatting -fn formatLabel(globalThis: *JSC.JSGlobalObject, label: string, function_args: []JSC.JSValue, test_idx: usize) !string { +fn formatLabel(globalThis: *JSGlobalObject, label: string, function_args: []JSValue, test_idx: usize) !string { const allocator = getAllocator(globalThis); var idx: usize = 0; var args_idx: usize = 0; - var list = try std.ArrayListUnmanaged(u8).initCapacity(allocator, label.len); + var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, label.len) catch bun.outOfMemory(); while (idx < label.len) { const char = label[idx]; @@ -1825,9 +1900,9 @@ fn formatLabel(globalThis: *JSC.JSGlobalObject, label: string, function_args: [] var str = bun.String.empty; defer str.deref(); current_arg.jsonStringify(globalThis, 0, &str); - const owned_slice = try str.toOwnedSlice(allocator); + const owned_slice = str.toOwnedSlice(allocator) catch bun.outOfMemory(); defer allocator.free(owned_slice); - try list.appendSlice(allocator, owned_slice); + list.appendSlice(allocator, owned_slice) catch bun.outOfMemory(); idx += 1; args_idx += 1; }, @@ -1836,28 +1911,28 @@ fn formatLabel(globalThis: *JSC.JSGlobalObject, label: string, function_args: [] .globalThis = globalThis, .quote_strings = true, }; - const value_fmt = current_arg.toFmt(globalThis, &formatter); - const test_index_str = try std.fmt.allocPrint(allocator, "{any}", .{value_fmt}); + const value_fmt = current_arg.toFmt(&formatter); + const test_index_str = std.fmt.allocPrint(allocator, "{any}", .{value_fmt}) catch bun.outOfMemory(); defer allocator.free(test_index_str); - try list.appendSlice(allocator, test_index_str); + list.appendSlice(allocator, test_index_str) catch bun.outOfMemory(); idx += 1; args_idx += 1; }, '#' => { - const test_index_str = try std.fmt.allocPrint(allocator, "{d}", .{test_idx}); + const test_index_str = std.fmt.allocPrint(allocator, "{d}", .{test_idx}) catch bun.outOfMemory(); defer allocator.free(test_index_str); - try list.appendSlice(allocator, test_index_str); + list.appendSlice(allocator, test_index_str) catch bun.outOfMemory(); idx += 1; }, '%' => { - try list.append(allocator, '%'); + list.append(allocator, '%') catch bun.outOfMemory(); idx += 1; }, else => { // ignore unrecognized fmt }, } - } else try list.append(allocator, char); + } else list.append(allocator, char) catch bun.outOfMemory(); idx += 1; } @@ -1869,7 +1944,7 @@ pub const EachData = struct { strong: JSC.Strong, is_test: bool }; fn eachBind( globalThis: *JSGlobalObject, callframe: *CallFrame, -) callconv(.C) JSValue { +) JSValue { const signature = "eachBind"; const callee = callframe.callee(); const arguments = callframe.arguments(3); @@ -1925,14 +2000,14 @@ fn eachBind( const allocator = getAllocator(globalThis); const each_data = bun.cast(*EachData, data); JSC.setFunctionData(callee, null); - const array = each_data.*.strong.get() orelse return .zero; + const array = each_data.*.strong.get() orelse return .undefined; defer { each_data.*.strong.deinit(); allocator.destroy(each_data); } if (array.isUndefinedOrNull() or !array.jsType().isArray()) { - return .zero; + return .undefined; } var iter = array.arrayIterator(globalThis); @@ -1953,7 +2028,7 @@ fn eachBind( arg_size += 1; } - var function_args = allocator.alloc(JSC.JSValue, arg_size) catch @panic("can't create function_args"); + var function_args = allocator.alloc(JSValue, arg_size) catch @panic("can't create function_args"); var idx: u32 = 0; if (item_is_array) { @@ -2003,7 +2078,7 @@ fn eachBind( function.unprotect(); } else if (each_data.is_test) { if (Jest.runner.?.only and tag != .only) { - return .zero; + return .undefined; } else { function.protect(); parent.tests.append(allocator, TestScope{ @@ -2033,13 +2108,13 @@ fn eachBind( } } - return .zero; + return .undefined; } inline fn createEach( globalThis: *JSGlobalObject, callframe: *CallFrame, - comptime property: string, + comptime property: [:0]const u8, comptime signature: string, comptime is_test: bool, ) JSValue { @@ -2069,17 +2144,12 @@ inline fn createEach( return JSC.NewFunctionWithData(globalThis, name, 3, eachBind, true, each_data); } -fn callJSFunctionForTestRunner(vm: *JSC.VirtualMachine, globalObject: *JSC.JSGlobalObject, function: JSC.JSValue, args: []const JSC.JSValue) JSC.JSValue { +fn callJSFunctionForTestRunner(vm: *JSC.VirtualMachine, globalObject: *JSGlobalObject, function: JSValue, args: []const JSValue) JSValue { vm.eventLoop().enter(); - defer { - vm.eventLoop().exit(); - } + defer vm.eventLoop().exit(); globalObject.clearTerminationException(); - const result = function.call(globalObject, args); - result.ensureStillAlive(); - - return result; + return function.call(globalObject, .undefined, args) catch |err| globalObject.takeException(err); } const assert = bun.assert; diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index 7f027b5b075c4..9f25a93d7c0d2 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -359,7 +359,7 @@ pub const JestPrettyFormat = struct { const Result = struct { tag: Tag, - cell: JSValue.JSType = JSValue.JSType.Cell, + cell: JSValue.JSType = .Cell, }; pub fn get(value: JSValue, globalThis: *JSGlobalObject) Result { @@ -455,36 +455,37 @@ pub const JestPrettyFormat = struct { return .{ .tag = switch (js_type) { - JSValue.JSType.ErrorInstance => .Error, - JSValue.JSType.NumberObject => .Double, - JSValue.JSType.DerivedArray, JSValue.JSType.Array => .Array, - JSValue.JSType.DerivedStringObject, JSValue.JSType.String, JSValue.JSType.StringObject => .String, - JSValue.JSType.RegExpObject => .String, - JSValue.JSType.Symbol => .Symbol, - JSValue.JSType.BooleanObject => .Boolean, - JSValue.JSType.JSFunction => .Function, - JSValue.JSType.JSWeakMap, JSValue.JSType.JSMap => .Map, - JSValue.JSType.JSWeakSet, JSValue.JSType.JSSet => .Set, - JSValue.JSType.JSDate => .JSON, - JSValue.JSType.JSPromise => .Promise, - JSValue.JSType.Object, - JSValue.JSType.FinalObject, + .ErrorInstance => .Error, + .NumberObject => .Double, + .DerivedArray, .Array => .Array, + .DerivedStringObject, .String, .StringObject => .String, + .RegExpObject => .String, + .Symbol => .Symbol, + .BooleanObject => .Boolean, + .JSFunction => .Function, + .JSWeakMap, .JSMap => .Map, + .JSWeakSet, .JSSet => .Set, + .JSDate => .JSON, + .JSPromise => .Promise, + .Object, + .FinalObject, .ModuleNamespaceObject, .GlobalObject, => .Object, .ArrayBuffer, - JSValue.JSType.Int8Array, - JSValue.JSType.Uint8Array, - JSValue.JSType.Uint8ClampedArray, - JSValue.JSType.Int16Array, - JSValue.JSType.Uint16Array, - JSValue.JSType.Int32Array, - JSValue.JSType.Uint32Array, - JSValue.JSType.Float32Array, - JSValue.JSType.Float64Array, - JSValue.JSType.BigInt64Array, - JSValue.JSType.BigUint64Array, + .Int8Array, + .Uint8Array, + .Uint8ClampedArray, + .Int16Array, + .Uint16Array, + .Int32Array, + .Uint32Array, + .Float16Array, + .Float32Array, + .Float64Array, + .BigInt64Array, + .BigUint64Array, .DataView, => .TypedArray, @@ -677,7 +678,7 @@ pub const JestPrettyFormat = struct { return struct { formatter: *JestPrettyFormat.Formatter, writer: Writer, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); const key = JSC.JSObject.getIndex(nextValue, globalObject, 0); const value = JSC.JSObject.getIndex(nextValue, globalObject, 1); @@ -712,7 +713,7 @@ pub const JestPrettyFormat = struct { return struct { formatter: *JestPrettyFormat.Formatter, writer: Writer, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); this.formatter.writeIndent(Writer, this.writer) catch {}; const key_tag = Tag.get(nextValue, globalObject); @@ -1101,11 +1102,14 @@ pub const JestPrettyFormat = struct { .Error => { var classname = ZigString.Empty; value.getClassName(this.globalThis, &classname); - var message_string = ZigString.Empty; - if (value.get(this.globalThis, "message")) |message_prop| { - message_prop.toZigString(&message_string, this.globalThis); + var message_string = bun.String.empty; + defer message_string.deref(); + + if (value.fastGet(this.globalThis, .message)) |message_prop| { + message_string = message_prop.toBunString(this.globalThis); } - if (message_string.len == 0) { + + if (message_string.isEmpty()) { writer.print("[{s}]", .{classname}); return; } @@ -1240,7 +1244,8 @@ pub const JestPrettyFormat = struct { .Object, Writer, writer_, - toJSONFunction.callWithThis(this.globalThis, value, &.{}), + toJSONFunction.call(this.globalThis, value, &.{}) catch |err| + this.globalThis.takeException(err), .Object, enable_ansi_colors, ); @@ -1368,7 +1373,7 @@ pub const JestPrettyFormat = struct { value.jsonStringify(this.globalThis, this.indent, &str); this.addForNewLine(str.length()); - if (jsType == JSValue.JSType.JSDate) { + if (jsType == .JSDate) { // in the code for printing dates, it never exceeds this amount var iso_string_buf: [36]u8 = undefined; var out_buf: []const u8 = std.fmt.bufPrint(&iso_string_buf, "{}", .{str}) catch ""; @@ -1384,10 +1389,21 @@ pub const JestPrettyFormat = struct { writer.print("{}", .{str}); }, .Event => { - const event_type = EventType.map.getWithEql(value.get(this.globalThis, "type").?.getZigString(this.globalThis), ZigString.eqlComptime) orelse EventType.unknown; - if (event_type != .MessageEvent and event_type != .ErrorEvent) { - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); - } + const event_type_value = brk: { + const value_ = value.get(this.globalThis, "type") orelse break :brk JSValue.undefined; + if (value_.isString()) { + break :brk value_; + } + + break :brk JSValue.undefined; + }; + + const event_type = switch (EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { + .MessageEvent, .ErrorEvent => |evt| evt, + else => { + return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + }, + }; writer.print( comptime Output.prettyFmt("{s} {{\n", enable_ansi_colors), @@ -1409,35 +1425,53 @@ pub const JestPrettyFormat = struct { event_type.label(), }, ); - this.writeIndent(Writer, writer_) catch unreachable; + + if (value.fastGet(this.globalThis, .message)) |message_value| { + if (message_value.isString()) { + this.writeIndent(Writer, writer_) catch unreachable; + writer.print( + comptime Output.prettyFmt("message: ", enable_ansi_colors), + .{}, + ); + + const tag = Tag.get(message_value, this.globalThis); + this.format(tag, Writer, writer_, message_value, this.globalThis, enable_ansi_colors); + writer.writeAll(", \n"); + } + } switch (event_type) { .MessageEvent => { + this.writeIndent(Writer, writer_) catch unreachable; writer.print( comptime Output.prettyFmt("data: ", enable_ansi_colors), .{}, ); - const data = value.fastGet(this.globalThis, .data).?; + const data = value.fastGet(this.globalThis, .data) orelse JSValue.undefined; const tag = Tag.get(data, this.globalThis); + if (tag.cell.isStringLike()) { this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); } else { this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); } + writer.writeAll(", \n"); }, .ErrorEvent => { - writer.print( - comptime Output.prettyFmt("error:\n", enable_ansi_colors), - .{}, - ); + if (value.fastGet(this.globalThis, .@"error")) |data| { + this.writeIndent(Writer, writer_) catch unreachable; + writer.print( + comptime Output.prettyFmt("error: ", enable_ansi_colors), + .{}, + ); - const data = value.get(this.globalThis, "error").?; - const tag = Tag.get(data, this.globalThis); - this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); + const tag = Tag.get(data, this.globalThis); + this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); + writer.writeAll("\n"); + } }, else => unreachable, } - writer.writeAll("\n"); } this.writeIndent(Writer, writer_) catch unreachable; @@ -1836,6 +1870,16 @@ pub const JestPrettyFormat = struct { writer.print("{d},", .{el}); } }, + .Float16Array => { + const slice_with_type: []align(std.meta.alignment([]f16)) f16 = @alignCast(std.mem.bytesAsSlice(f16, slice)); + this.indent += 1; + defer this.indent -|= 1; + for (slice_with_type) |el| { + writer.writeAll("\n"); + this.writeIndent(Writer, writer_) catch {}; + writer.print("{d},", .{el}); + } + }, .Float32Array => { const slice_with_type: []align(std.meta.alignment([]f32)) f32 = @alignCast(std.mem.bytesAsSlice(f32, slice)); this.indent += 1; diff --git a/src/bun.js/test/snapshot.zig b/src/bun.js/test/snapshot.zig index 41fcea954d4e5..e8d46bb378d76 100644 --- a/src/bun.js/test/snapshot.zig +++ b/src/bun.js/test/snapshot.zig @@ -140,48 +140,32 @@ pub const Snapshots = struct { // TODO: when common js transform changes, keep this updated or add flag to support this version - const export_default = brk: { - for (ast.parts.slice()) |part| { - for (part.stmts) |stmt| { - if (stmt.data == .s_export_default and stmt.data.s_export_default.value == .expr) { - break :brk stmt.data.s_export_default.value.expr; - } - } - } - - return; - }; - - if (export_default.data == .e_call) { - const function_call = export_default.data.e_call; - if (function_call.args.len == 2 and function_call.args.ptr[0].data == .e_function) { - const arg_function_stmts = function_call.args.ptr[0].data.e_function.func.body.stmts; - for (arg_function_stmts) |stmt| { - switch (stmt.data) { - .s_expr => |expr| { - if (expr.value.data == .e_binary and expr.value.data.e_binary.op == .bin_assign) { - const left = expr.value.data.e_binary.left; - if (left.data == .e_index and left.data.e_index.index.data == .e_string and left.data.e_index.target.data == .e_identifier) { - const target: js_ast.E.Identifier = left.data.e_index.target.data.e_identifier; - var index: *js_ast.E.String = left.data.e_index.index.data.e_string; - if (target.ref.eql(exports_ref) and expr.value.data.e_binary.right.data == .e_string) { - const key = index.slice(this.allocator); - var value_string = expr.value.data.e_binary.right.data.e_string; - const value = value_string.slice(this.allocator); - defer { - if (!index.isUTF8()) this.allocator.free(key); - if (!value_string.isUTF8()) this.allocator.free(value); - } - const value_clone = try this.allocator.alloc(u8, value.len); - bun.copy(u8, value_clone, value); - const name_hash = bun.hash(key); - try this.values.put(name_hash, value_clone); + for (ast.parts.slice()) |part| { + for (part.stmts) |stmt| { + switch (stmt.data) { + .s_expr => |expr| { + if (expr.value.data == .e_binary and expr.value.data.e_binary.op == .bin_assign) { + const left = expr.value.data.e_binary.left; + if (left.data == .e_index and left.data.e_index.index.data == .e_string and left.data.e_index.target.data == .e_identifier) { + const target: js_ast.E.Identifier = left.data.e_index.target.data.e_identifier; + var index: *js_ast.E.String = left.data.e_index.index.data.e_string; + if (target.ref.eql(exports_ref) and expr.value.data.e_binary.right.data == .e_string) { + const key = index.slice(this.allocator); + var value_string = expr.value.data.e_binary.right.data.e_string; + const value = value_string.slice(this.allocator); + defer { + if (!index.isUTF8()) this.allocator.free(key); + if (!value_string.isUTF8()) this.allocator.free(value); } + const value_clone = try this.allocator.alloc(u8, value.len); + bun.copy(u8, value_clone, value); + const name_hash = bun.hash(key); + try this.values.put(name_hash, value_clone); } } - }, - else => {}, - } + } + }, + else => {}, } } } diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index 28a5dbdf6d757..127c0751bc073 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -23,7 +23,7 @@ pub const WebWorker = struct { /// Already resolved. specifier: []const u8 = "", store_fd: bool = false, - arena: bun.MimallocArena = undefined, + arena: ?bun.MimallocArena = null, name: [:0]const u8 = "Worker", cpp_worker: *anyopaque, mini: bool = false, @@ -101,14 +101,66 @@ pub const WebWorker = struct { defer parent.bundler.setLog(prev_log); defer temp_log.deinit(); - var resolved_entry_point: bun.resolver.Result = undefined; - const path = brk: { const str = spec_slice.slice(); if (parent.standalone_module_graph) |graph| { if (graph.find(str) != null) { break :brk str; } + + // Since `bun build --compile` renames files to `.js` by + // default, we need to do the reverse of our file extension + // mapping. + // + // new Worker("./foo") -> new Worker("./foo.js") + // new Worker("./foo.ts") -> new Worker("./foo.js") + // new Worker("./foo.jsx") -> new Worker("./foo.js") + // new Worker("./foo.mjs") -> new Worker("./foo.js") + // new Worker("./foo.mts") -> new Worker("./foo.js") + // new Worker("./foo.cjs") -> new Worker("./foo.js") + // new Worker("./foo.cts") -> new Worker("./foo.js") + // new Worker("./foo.tsx") -> new Worker("./foo.js") + // + if (bun.strings.hasPrefixComptime(str, "./") or bun.strings.hasPrefixComptime(str, "../")) try_from_extension: { + var pathbuf: bun.PathBuffer = undefined; + var base = str; + + base = bun.path.joinAbsStringBuf(bun.StandaloneModuleGraph.base_public_path_with_default_suffix, &pathbuf, &.{str}, .loose); + const extname = std.fs.path.extension(base); + + // ./foo -> ./foo.js + if (extname.len == 0) { + pathbuf[base.len..][0..3].* = ".js".*; + if (graph.find(pathbuf[0 .. base.len + 3])) |js_file| { + break :brk js_file.name; + } + + break :try_from_extension; + } + + // ./foo.ts -> ./foo.js + if (bun.strings.eqlComptime(extname, ".ts")) { + pathbuf[base.len - 3 .. base.len][0..3].* = ".js".*; + if (graph.find(pathbuf[0..base.len])) |js_file| { + break :brk js_file.name; + } + + break :try_from_extension; + } + + if (extname.len == 4) { + inline for (.{ ".tsx", ".jsx", ".mjs", ".mts", ".cts", ".cjs" }) |ext| { + if (bun.strings.eqlComptime(extname, ext)) { + pathbuf[base.len - ext.len ..][0..".js".len].* = ".js".*; + const as_js = pathbuf[0 .. base.len - ext.len + ".js".len]; + if (graph.find(as_js)) |js_file| { + break :brk js_file.name; + } + break :try_from_extension; + } + } + } + } } if (JSC.WebCore.ObjectURLRegistry.isBlobURL(str)) { @@ -120,13 +172,13 @@ pub const WebWorker = struct { } } - resolved_entry_point = parent.bundler.resolveEntryPoint(str) catch { + var resolved_entry_point: bun.resolver.Result = parent.bundler.resolveEntryPoint(str) catch { const out = temp_log.toJS(parent.global, bun.default_allocator, "Error resolving Worker entry point").toBunString(parent.global); error_message.* = out; return null; }; - const entry_path = resolved_entry_point.path() orelse { + const entry_path: *bun.fs.Path = resolved_entry_point.path() orelse { error_message.* = bun.String.static("Worker entry point is missing"); return null; }; @@ -162,6 +214,7 @@ pub const WebWorker = struct { pub fn startWithErrorHandling( this: *WebWorker, ) void { + bun.Analytics.Features.workers_spawned += 1; start(this) catch |err| { Output.panic("An unhandled error occurred while starting a worker: {s}\n", .{@errorName(err)}); }; @@ -177,7 +230,7 @@ pub const WebWorker = struct { } if (this.hasRequestedTerminate()) { - this.deinit(); + this.exitAndDeinit(); return; } @@ -186,13 +239,13 @@ pub const WebWorker = struct { this.arena = try bun.MimallocArena.init(); var vm = try JSC.VirtualMachine.initWorker(this, .{ - .allocator = this.arena.allocator(), + .allocator = this.arena.?.allocator(), .args = this.parent.bundler.options.transform_options, .store_fd = this.store_fd, .graph = this.parent.standalone_module_graph, }); - vm.allocator = this.arena.allocator(); - vm.arena = &this.arena; + vm.allocator = this.arena.?.allocator(); + vm.arena = &this.arena.?; var b = &vm.bundler; @@ -217,7 +270,7 @@ pub const WebWorker = struct { vm.bundler.env = loader; - vm.loadExtraEnv(); + vm.loadExtraEnvAndSourceCodePrinter(); vm.is_main_thread = false; JSC.VirtualMachine.is_main_thread_vm = false; vm.onUnhandledRejection = onUnhandledRejection; @@ -314,7 +367,7 @@ pub const WebWorker = struct { return; }; - if (promise.status(vm.global.vm()) == .Rejected) { + if (promise.status(vm.global.vm()) == .rejected) { const handled = vm.uncaughtException(vm.global, promise.result(vm.global.vm()), true); if (!handled) { @@ -404,6 +457,7 @@ pub const WebWorker = struct { pub fn exitAndDeinit(this: *WebWorker) noreturn { JSC.markBinding(@src()); this.setStatus(.terminated); + bun.Analytics.Features.workers_terminated += 1; log("[{d}] exitAndDeinit", .{this.execution_context_id}); const cpp_worker = this.cpp_worker; @@ -421,13 +475,15 @@ pub const WebWorker = struct { var arena = this.arena; WebWorker__dispatchExit(globalObject, cpp_worker, exit_code); + bun.uws.onThreadExit(); this.deinit(); if (vm_to_deinit) |vm| { vm.deinit(); // NOTE: deinit here isn't implemented, so freeing workers will leak the vm. } - - arena.deinit(); + if (arena) |*arena_| { + arena_.deinit(); + } bun.exitThread(); } diff --git a/src/bun.js/webcore.zig b/src/bun.js/webcore.zig index bdbba988a52f3..26aa7f7c18af8 100644 --- a/src/bun.js/webcore.zig +++ b/src/bun.js/webcore.zig @@ -22,7 +22,7 @@ pub const Lifetime = enum { }; /// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-alert -fn alert(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +fn alert(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(1).slice(); var output = bun.Output.writer(); const has_message = arguments.len != 0; @@ -72,7 +72,7 @@ fn alert(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv( return .undefined; } -fn confirm(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +fn confirm(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(1).slice(); var output = bun.Output.writer(); const has_message = arguments.len != 0; @@ -210,7 +210,7 @@ pub const Prompt = struct { pub fn call( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(3).slice(); var state = std.heap.stackFallback(2048, bun.default_allocator); const allocator = state.get(); @@ -353,7 +353,7 @@ pub const Prompt = struct { // * Too complex for server context. // 9. Return result. - return result.toValueGC(globalObject); + return result.toJS(globalObject); } }; @@ -512,7 +512,7 @@ pub const Crypto = struct { // i don't think its a real scenario, but just in case buf = globalThis.allocator().alloc(u8, keylen) catch { globalThis.throw("Failed to allocate memory", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; needs_deinit = true; } else { @@ -538,25 +538,13 @@ pub const Crypto = struct { } fn throwInvalidParameter(globalThis: *JSC.JSGlobalObject) JSC.JSValue { - const err = globalThis.createErrorInstanceWithCode( - .ERR_CRYPTO_SCRYPT_INVALID_PARAMETER, - "Invalid scrypt parameters", - .{}, - ); - globalThis.throwValue(err); + globalThis.ERR_CRYPTO_SCRYPT_INVALID_PARAMETER("Invalid scrypt parameters", .{}).throw(); return .zero; } - fn throwInvalidParams(globalThis: *JSC.JSGlobalObject, comptime error_type: @Type(.EnumLiteral), comptime message: string, fmt: anytype) JSC.JSValue { - const err = switch (error_type) { - .RangeError => globalThis.createRangeErrorInstanceWithCode( - .ERR_CRYPTO_INVALID_SCRYPT_PARAMS, - message, - fmt, - ), - else => @compileError("Error type not added!"), - }; - globalThis.throwValue(err); + fn throwInvalidParams(globalThis: *JSC.JSGlobalObject, comptime error_type: @Type(.EnumLiteral), comptime message: [:0]const u8, fmt: anytype) JSC.JSValue { + if (error_type != .RangeError) @compileError("Error type not added!"); + globalThis.ERR_CRYPTO_INVALID_SCRYPT_PARAMS(message, fmt).throw(); BoringSSL.ERR_clear_error(); return .zero; } @@ -565,30 +553,30 @@ pub const Crypto = struct { _: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments = callframe.arguments(2).slice(); if (arguments.len < 2) { globalThis.throwInvalidArguments("Expected 2 typed arrays but got nothing", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; } const array_buffer_a = arguments[0].asArrayBuffer(globalThis) orelse { globalThis.throwInvalidArguments("Expected typed array but got {s}", .{@tagName(arguments[0].jsType())}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; const a = array_buffer_a.byteSlice(); const array_buffer_b = arguments[1].asArrayBuffer(globalThis) orelse { globalThis.throwInvalidArguments("Expected typed array but got {s}", .{@tagName(arguments[1].jsType())}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; const b = array_buffer_b.byteSlice(); const len = a.len; if (b.len != len) { globalThis.throw("Input buffers must have the same byte length", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; } return JSC.jsBoolean(len == 0 or bun.BoringSSL.CRYPTO_memcmp(a.ptr, b.ptr, len) == 0); } @@ -598,7 +586,7 @@ pub const Crypto = struct { globalThis: *JSC.JSGlobalObject, array_a: *JSC.JSUint8Array, array_b: *JSC.JSUint8Array, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const a = array_a.slice(); const b = array_b.slice(); @@ -615,16 +603,16 @@ pub const Crypto = struct { _: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments = callframe.arguments(1).slice(); if (arguments.len == 0) { globalThis.throwInvalidArguments("Expected typed array but got nothing", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; } var array_buffer = arguments[0].asArrayBuffer(globalThis) orelse { globalThis.throwInvalidArguments("Expected typed array but got {s}", .{@tagName(arguments[0].jsType())}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; const slice = array_buffer.byteSlice(); @@ -637,7 +625,7 @@ pub const Crypto = struct { _: *@This(), globalThis: *JSC.JSGlobalObject, array: *JSC.JSUint8Array, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const slice = array.slice(); randomData(globalThis, slice.ptr, slice.len); return @as(JSC.JSValue, @enumFromInt(@as(i64, @bitCast(@intFromPtr(array))))); @@ -666,7 +654,7 @@ pub const Crypto = struct { _: *@This(), globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const str, var bytes = bun.String.createUninitialized(.latin1, 36); defer str.deref(); @@ -679,7 +667,7 @@ pub const Crypto = struct { pub fn randomUUIDWithoutTypeChecks( _: *Crypto, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const str, var bytes = bun.String.createUninitialized(.latin1, 36); defer str.deref(); @@ -691,7 +679,7 @@ pub const Crypto = struct { return str.toJS(globalThis); } - pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*Crypto { + pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*Crypto { globalThis.throw("Crypto is not constructable", .{}); return null; } diff --git a/src/bun.js/webcore/ObjectURLRegistry.zig b/src/bun.js/webcore/ObjectURLRegistry.zig index 9eef7476dbbcf..10a30fdab8d2b 100644 --- a/src/bun.js/webcore/ObjectURLRegistry.zig +++ b/src/bun.js/webcore/ObjectURLRegistry.zig @@ -5,7 +5,7 @@ const UUID = bun.UUID; const assert = bun.assert; const ObjectURLRegistry = @This(); -lock: bun.Lock = bun.Lock.init(), +lock: bun.Lock = .{}, map: std.AutoHashMap(UUID, *RegistryEntry) = std.AutoHashMap(UUID, *RegistryEntry).init(bun.default_allocator), pub const RegistryEntry = struct { @@ -68,6 +68,12 @@ pub fn resolveAndDupe(this: *ObjectURLRegistry, pathname: []const u8) ?JSC.WebCo return entry.blob.dupeWithContentType(true); } +pub fn resolveAndDupeToJS(this: *ObjectURLRegistry, pathname: []const u8, globalObject: *JSC.JSGlobalObject) ?JSC.JSValue { + var blob = JSC.WebCore.Blob.new(this.resolveAndDupe(pathname) orelse return null); + blob.allocator = bun.default_allocator; + return blob.toJS(globalObject); +} + pub fn revoke(this: *ObjectURLRegistry, pathname: []const u8) void { const uuid = uuidFromPathname(pathname) orelse return; this.lock.lock(); @@ -83,7 +89,7 @@ pub fn has(this: *ObjectURLRegistry, pathname: []const u8) bool { return this.map.contains(uuid); } -export fn Bun__createObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +export fn Bun__createObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(1); if (arguments.len < 1) { globalObject.throwNotEnoughArguments("createObjectURL", 1, arguments.len); @@ -99,7 +105,7 @@ export fn Bun__createObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JS return str.transferToJS(globalObject); } -export fn Bun__revokeObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +export fn Bun__revokeObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const arguments = callframe.arguments(1); if (arguments.len < 1) { globalObject.throwNotEnoughArguments("revokeObjectURL", 1, arguments.len); @@ -126,9 +132,39 @@ export fn Bun__revokeObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JS return JSC.JSValue.undefined; } +export fn jsFunctionResolveObjectURL(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(1); + + // Errors are ignored. + // Not thrown. + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/blob.js#L441 + if (arguments.len < 1) { + return JSC.JSValue.undefined; + } + const str = arguments.ptr[0].toBunString(globalObject); + defer str.deref(); + + if (globalObject.hasException()) { + return .zero; + } + + if (!str.hasPrefixComptime("blob:") or str.length() < specifier_len) { + return JSC.JSValue.undefined; + } + + const slice = str.toUTF8WithoutRef(bun.default_allocator); + defer slice.deinit(); + const sliced = slice.slice(); + + const registry = ObjectURLRegistry.singleton(); + const blob = registry.resolveAndDupeToJS(sliced["blob:".len..], globalObject); + return blob orelse JSC.JSValue.undefined; +} + comptime { _ = &Bun__createObjectURL; _ = &Bun__revokeObjectURL; + _ = &jsFunctionResolveObjectURL; } pub const specifier_len = "blob:".len + UUID.stringLength; diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 5dd332750a7d7..a66967e74cdd4 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -260,8 +260,9 @@ pub const Blob = struct { joiner.pushStatic("\r\n\r\n"); if (blob.store) |store| { - blob.resolveSize(); - + if (blob.size == Blob.max_size) { + blob.resolveSize(); + } switch (store.data) { .file => |file| { @@ -310,7 +311,7 @@ pub const Blob = struct { const StructuredCloneWriter = struct { ctx: *anyopaque, - impl: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void, + impl: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(JSC.conv) void, pub const WriteError = error{}; pub fn write(this: StructuredCloneWriter, bytes: []const u8) WriteError!usize { @@ -352,8 +353,8 @@ pub const Blob = struct { this: *Blob, globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, - writeBytes: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void, - ) callconv(.C) void { + writeBytes: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(JSC.conv) void, + ) void { _ = globalThis; const Writer = std.io.Writer(StructuredCloneWriter, StructuredCloneWriter.WriteError, StructuredCloneWriter.write); @@ -372,7 +373,7 @@ pub const Blob = struct { globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, write: *const fn (*anyopaque, ptr: [*]const u8, len: usize) callconv(.C) void, - ) callconv(.C) void { + ) void { _ = write; _ = ctx; _ = this; @@ -515,7 +516,7 @@ pub const Blob = struct { globalThis: *JSC.JSGlobalObject, ptr: [*]u8, end: [*]u8, - ) callconv(.C) JSValue { + ) JSValue { const total_length: usize = @intFromPtr(end) - @intFromPtr(ptr); var buffer_stream = std.io.fixedBufferStream(ptr[0..total_length]); const reader = buffer_stream.reader(); @@ -650,21 +651,29 @@ pub const Blob = struct { _ = Blob__getFileNameString; } - pub fn writeFormatForSize(size: usize, writer: anytype, comptime enable_ansi_colors: bool) !void { - try writer.writeAll(comptime Output.prettyFmt("Blob", enable_ansi_colors)); + pub fn writeFormatForSize(is_jdom_file: bool, size: usize, writer: anytype, comptime enable_ansi_colors: bool) !void { + if (is_jdom_file) { + try writer.writeAll(comptime Output.prettyFmt("File", enable_ansi_colors)); + } else { + try writer.writeAll(comptime Output.prettyFmt("Blob", enable_ansi_colors)); + } try writer.print( comptime Output.prettyFmt(" ({any})", enable_ansi_colors), .{ - bun.fmt.size(size), + bun.fmt.size(size, .{}), }, ); } - pub fn writeFormat(this: *const Blob, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { + pub fn writeFormat(this: *Blob, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { const Writer = @TypeOf(writer); if (this.isDetached()) { - try writer.writeAll(comptime Output.prettyFmt("[Blob detached]", enable_ansi_colors)); + if (this.is_jsdom_file) { + try writer.writeAll(comptime Output.prettyFmt("[File detached]", enable_ansi_colors)); + } else { + try writer.writeAll(comptime Output.prettyFmt("[Blob detached]", enable_ansi_colors)); + } return; } @@ -687,7 +696,7 @@ pub const Blob = struct { if (comptime Environment.isWindows) { if (fd_impl.kind == .uv) { try writer.print( - comptime Output.prettyFmt(" (fd: {d})", enable_ansi_colors), + comptime Output.prettyFmt(" (fd: {d})", enable_ansi_colors), .{fd_impl.uv()}, ); } else { @@ -696,13 +705,13 @@ pub const Blob = struct { @panic("this shouldn't be reachable."); } try writer.print( - comptime Output.prettyFmt(" (fd: {any})", enable_ansi_colors), + comptime Output.prettyFmt(" (fd: {any})", enable_ansi_colors), .{fd_impl.system()}, ); } } else { try writer.print( - comptime Output.prettyFmt(" (fd: {d})", enable_ansi_colors), + comptime Output.prettyFmt(" (fd: {d})", enable_ansi_colors), .{fd_impl.system()}, ); } @@ -710,28 +719,46 @@ pub const Blob = struct { } }, .bytes => { - try writeFormatForSize(this.size, writer, enable_ansi_colors); + try writeFormatForSize(this.is_jsdom_file, this.size, writer, enable_ansi_colors); }, } } - if (this.content_type.len > 0 or this.offset > 0) { + const show_name = (this.is_jsdom_file and this.getNameString() != null) or (!this.name.isEmpty() and this.store != null and this.store.?.data == .bytes); + if (this.content_type.len > 0 or this.offset > 0 or show_name or this.last_modified != 0.0) { try writer.writeAll(" {\n"); { formatter.indent += 1; defer formatter.indent -= 1; + if (show_name) { + try formatter.writeIndent(Writer, writer); + + try writer.print( + comptime Output.prettyFmt("name: \"{}\"", enable_ansi_colors), + .{ + this.getNameString() orelse bun.String.empty, + }, + ); + + if (this.content_type.len > 0 or this.offset > 0 or this.last_modified != 0) { + try formatter.printComma(Writer, writer, enable_ansi_colors); + } + + try writer.writeAll("\n"); + } + if (this.content_type.len > 0) { try formatter.writeIndent(Writer, writer); try writer.print( - comptime Output.prettyFmt("type: \"{s}\"", enable_ansi_colors), + comptime Output.prettyFmt("type: \"{s}\"", enable_ansi_colors), .{ this.content_type, }, ); - if (this.offset > 0) { - formatter.printComma(Writer, writer, enable_ansi_colors) catch unreachable; + if (this.offset > 0 or this.last_modified != 0) { + try formatter.printComma(Writer, writer, enable_ansi_colors); } try writer.writeAll("\n"); @@ -741,11 +768,28 @@ pub const Blob = struct { try formatter.writeIndent(Writer, writer); try writer.print( - comptime Output.prettyFmt("offset: {d}\n", enable_ansi_colors), + comptime Output.prettyFmt("offset: {d}\n", enable_ansi_colors), .{ this.offset, }, ); + + if (this.last_modified != 0) { + try formatter.printComma(Writer, writer, enable_ansi_colors); + } + + try writer.writeAll("\n"); + } + + if (this.last_modified != 0) { + try formatter.writeIndent(Writer, writer); + + try writer.print( + comptime Output.prettyFmt("lastModified: {d}\n", enable_ansi_colors), + .{ + this.last_modified, + }, + ); } } @@ -870,15 +914,13 @@ pub const Blob = struct { // If this is file <> file, we can just copy the file else if (destination_type == .file and source_type == .file) { if (comptime Environment.isWindows) { - var copier = Store.CopyFileWindows.init( + return Store.CopyFileWindows.init( destination_blob.store.?, source_blob.store.?, ctx.bunVM().eventLoop(), mkdirp_if_not_exists, destination_blob.size, ); - - return copier.promise.value(); } var file_copier = Store.CopyFile.create( bun.default_allocator, @@ -920,7 +962,7 @@ pub const Blob = struct { pub fn writeFile( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments = callframe.arguments(3).slice(); var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); defer args.deinit(); @@ -1111,12 +1153,10 @@ pub const Blob = struct { => { break :brk response.body.use(); }, - .Error => { + .Error => |*err_ref| { destination_blob.detach(); - const err = response.body.value.Error; - err.unprotect(); _ = response.body.value.use(); - return JSC.JSPromise.rejectedPromiseValue(globalThis, err); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err_ref.toJS(globalThis)); }, .Locked => { var task = bun.new(WriteFileWaitFromLockedValueTask, .{ @@ -1144,12 +1184,10 @@ pub const Blob = struct { => { break :brk request.body.value.use(); }, - .Error => { + .Error => |*err_ref| { destination_blob.detach(); - const err = request.body.value.Error; - err.unprotect(); _ = request.body.value.use(); - return JSC.JSPromise.rejectedPromiseValue(globalThis, err); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err_ref.toJS(globalThis)); }, .Locked => { var task = bun.new(WriteFileWaitFromLockedValueTask, .{ @@ -1377,7 +1415,7 @@ pub const Blob = struct { return JSC.JSPromise.resolvedPromiseValue(globalThis, JSC.JSValue.jsNumber(written)); } - pub export fn JSDOMFile__hasInstance(_: JSC.JSValue, _: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(.C) bool { + pub export fn JSDOMFile__hasInstance(_: JSC.JSValue, _: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(JSC.conv) bool { JSC.markBinding(@src()); const blob = value.as(Blob) orelse return false; return blob.is_jsdom_file; @@ -1386,7 +1424,7 @@ pub const Blob = struct { pub export fn JSDOMFile__construct( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) ?*Blob { + ) callconv(JSC.conv) ?*Blob { JSC.markBinding(@src()); const allocator = bun.default_allocator; var blob: Blob = undefined; @@ -1399,24 +1437,35 @@ pub const Blob = struct { } { const name_value_str = bun.String.tryFromJS(args[1], globalThis) orelse { - globalThis.throwInvalidArguments("new File(bits, name) expects string as the second argument", .{}); + if (!globalThis.hasException()) { + globalThis.throwInvalidArguments("new File(bits, name) expects string as the second argument", .{}); + } return null; }; defer name_value_str.deref(); blob = get(globalThis, args[0], false, true) catch |err| { - if (err == error.InvalidArguments) { - globalThis.throwInvalidArguments("new File(bits, name) expects iterable as the first argument", .{}); - return null; + if (!globalThis.hasException()) { + if (err == error.InvalidArguments) { + globalThis.throwInvalidArguments("new File(bits, name) expects iterable as the first argument", .{}); + return null; + } + globalThis.throwOutOfMemory(); } - globalThis.throwOutOfMemory(); return null; }; if (blob.store) |store_| { - store_.data.bytes.stored_name = bun.PathString.init( - (name_value_str.toUTF8WithoutRef(bun.default_allocator).clone(bun.default_allocator) catch unreachable).slice(), - ); + switch (store_.data) { + .bytes => |*bytes| { + bytes.stored_name = bun.PathString.init( + (name_value_str.toUTF8WithoutRef(bun.default_allocator).clone(bun.default_allocator) catch unreachable).slice(), + ); + }, + .file => { + blob.name = name_value_str.dupeRef(); + }, + } } } @@ -1496,7 +1545,7 @@ pub const Blob = struct { this.reported_estimated_size = size + (this.content_type.len * @intFromBool(this.content_type_allocated)); } - pub fn estimatedSize(this: *Blob) callconv(.C) usize { + pub fn estimatedSize(this: *Blob) usize { return this.reported_estimated_size; } @@ -1510,7 +1559,7 @@ pub const Blob = struct { pub fn constructBunFile( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var vm = globalObject.bunVM(); const arguments = callframe.arguments(2).slice(); var args = JSC.Node.ArgumentsSlice.init(vm, arguments); @@ -1657,6 +1706,21 @@ pub const Blob = struct { assert(old > 0); } + pub fn hasOneRef(this: *const Store) bool { + return this.ref_count.load(.monotonic) == 1; + } + + /// Caller is responsible for derefing the Store. + pub fn toAnyBlob(this: *Store) ?AnyBlob { + if (this.hasOneRef()) { + if (this.data == .bytes) { + return .{ .InternalBlob = this.data.bytes.toInternalBlob() }; + } + } + + return null; + } + pub fn external(ptr: ?*anyopaque, _: ?*anyopaque, _: usize) callconv(.C) void { if (ptr == null) return; var this = bun.cast(*Store, ptr); @@ -1979,6 +2043,204 @@ pub const Blob = struct { /// For mkdirp err: ?bun.sys.Error = null, + /// When we are unable to get the original file path, we do a read-write loop that uses libuv. + read_write_loop: ReadWriteLoop = .{}, + + pub const ReadWriteLoop = struct { + source_fd: bun.FileDescriptor = invalid_fd, + must_close_source_fd: bool = false, + destination_fd: bun.FileDescriptor = invalid_fd, + must_close_destination_fd: bool = false, + written: usize = 0, + read_buf: std.ArrayList(u8) = std.ArrayList(u8).init(default_allocator), + uv_buf: libuv.uv_buf_t = .{ .base = undefined, .len = 0 }, + + pub fn start(read_write_loop: *ReadWriteLoop, this: *CopyFileWindows) JSC.Maybe(void) { + read_write_loop.read_buf.ensureTotalCapacityPrecise(64 * 1024) catch bun.outOfMemory(); + + return read(read_write_loop, this); + } + + pub fn read(read_write_loop: *ReadWriteLoop, this: *CopyFileWindows) JSC.Maybe(void) { + read_write_loop.read_buf.items.len = 0; + read_write_loop.uv_buf = libuv.uv_buf_t.init(read_write_loop.read_buf.allocatedSlice()); + const loop = this.event_loop.virtual_machine.event_loop_handle.?; + + // This io_request is used for both reading and writing. + // For now, we don't start reading the next chunk until + // we've finished writing all the previous chunks. + this.io_request.data = @ptrCast(this); + + const rc = libuv.uv_fs_read( + loop, + &this.io_request, + bun.uvfdcast(read_write_loop.source_fd), + @ptrCast(&read_write_loop.uv_buf), + 1, + -1, + &onRead, + ); + + if (rc.toError(.read)) |err| { + return .{ .err = err }; + } + + return .{ .result = {} }; + } + + fn onRead(req: *libuv.fs_t) callconv(.C) void { + var this: *CopyFileWindows = @fieldParentPtr("io_request", req); + bun.assert(req.data == @as(?*anyopaque, @ptrCast(this))); + + const source_fd = this.read_write_loop.source_fd; + const destination_fd = this.read_write_loop.destination_fd; + const read_buf = &this.read_write_loop.read_buf.items; + + const event_loop = this.event_loop; + + const rc = req.result; + + bun.sys.syslog("uv_fs_read({}, {d}) = {d}", .{ source_fd, read_buf.len, rc.int() }); + if (rc.toError(.read)) |err| { + this.err = err; + this.onReadWriteLoopComplete(); + return; + } + + read_buf.len = @intCast(rc.int()); + this.read_write_loop.uv_buf = libuv.uv_buf_t.init(read_buf.*); + + if (rc.int() == 0) { + // Handle EOF. We can't read any more. + this.onReadWriteLoopComplete(); + return; + } + + // Re-use the fs request. + req.deinit(); + const rc2 = libuv.uv_fs_write( + event_loop.virtual_machine.event_loop_handle.?, + &this.io_request, + bun.uvfdcast(destination_fd), + @ptrCast(&this.read_write_loop.uv_buf), + 1, + -1, + &onWrite, + ); + req.data = @ptrCast(this); + + if (rc2.toError(.write)) |err| { + this.err = err; + this.onReadWriteLoopComplete(); + return; + } + } + + fn onWrite(req: *libuv.fs_t) callconv(.C) void { + var this: *CopyFileWindows = @fieldParentPtr("io_request", req); + bun.assert(req.data == @as(?*anyopaque, @ptrCast(this))); + const buf = &this.read_write_loop.read_buf.items; + + const destination_fd = this.read_write_loop.destination_fd; + + const rc = req.result; + + bun.sys.syslog("uv_fs_write({}, {d}) = {d}", .{ destination_fd, buf.len, rc.int() }); + + if (rc.toError(.write)) |err| { + this.err = err; + this.onReadWriteLoopComplete(); + return; + } + + const wrote: u32 = @intCast(rc.int()); + + this.read_write_loop.written += wrote; + + if (wrote < buf.len) { + if (wrote == 0) { + // Handle EOF. We can't write any more. + this.onReadWriteLoopComplete(); + return; + } + + // Re-use the fs request. + req.deinit(); + req.data = @ptrCast(this); + + this.read_write_loop.uv_buf = libuv.uv_buf_t.init(this.read_write_loop.uv_buf.slice()[wrote..]); + const rc2 = libuv.uv_fs_write( + this.event_loop.virtual_machine.event_loop_handle.?, + &this.io_request, + bun.uvfdcast(destination_fd), + @ptrCast(&this.read_write_loop.uv_buf), + 1, + -1, + &onWrite, + ); + + if (rc2.toError(.write)) |err| { + this.err = err; + this.onReadWriteLoopComplete(); + return; + } + + return; + } + + req.deinit(); + switch (this.read_write_loop.read(this)) { + .err => |err| { + this.err = err; + this.onReadWriteLoopComplete(); + }, + .result => {}, + } + } + + pub fn close(this: *ReadWriteLoop) void { + if (this.must_close_source_fd) { + if (bun.toLibUVOwnedFD(this.source_fd)) |fd| { + bun.Async.Closer.close( + bun.uvfdcast(fd), + bun.Async.Loop.get(), + ); + } else |_| { + _ = bun.sys.close(this.source_fd); + } + this.must_close_source_fd = false; + this.source_fd = invalid_fd; + } + + if (this.must_close_destination_fd) { + if (bun.toLibUVOwnedFD(this.destination_fd)) |fd| { + bun.Async.Closer.close( + bun.uvfdcast(fd), + bun.Async.Loop.get(), + ); + } else |_| { + _ = bun.sys.close(this.destination_fd); + } + this.must_close_destination_fd = false; + this.destination_fd = invalid_fd; + } + + this.read_buf.clearAndFree(); + } + }; + + pub fn onReadWriteLoopComplete(this: *CopyFileWindows) void { + this.event_loop.unrefConcurrently(); + + if (this.err) |err| { + this.err = null; + this.throw(err); + return; + } + + this.onComplete(this.read_write_loop.written); + } + pub usingnamespace bun.New(@This()); pub fn init( @@ -1987,7 +2249,7 @@ pub const Blob = struct { event_loop: *JSC.EventLoop, mkdirp_if_not_exists: bool, size_: Blob.SizeType, - ) *CopyFileWindows { + ) JSC.JSValue { destination_file_store.ref(); source_file_store.ref(); const result = CopyFileWindows.new(.{ @@ -1999,13 +2261,93 @@ pub const Blob = struct { .mkdirp_if_not_exists = mkdirp_if_not_exists, .size = size_, }); + const promise = result.promise.value(); + // On error, this function might free the CopyFileWindows struct. + // So we can no longer reference it beyond this point. result.copyfile(); - return result; + return promise; + } + + fn preparePathlike(pathlike: *JSC.Node.PathOrFileDescriptor, must_close: *bool, is_reading: bool) JSC.Maybe(bun.FileDescriptor) { + if (pathlike.* == .path) { + const fd = switch (bun.sys.openatWindowsT( + u8, + bun.invalid_fd, + pathlike.path.slice(), + if (is_reading) + bun.O.RDONLY + else + bun.O.WRONLY | bun.O.CREAT, + )) { + .result => |result| bun.toLibUVOwnedFD(result) catch { + _ = bun.sys.close(result); + return .{ + .err = .{ + .errno = @as(c_int, @intCast(@intFromEnum(bun.C.SystemErrno.EMFILE))), + .syscall = .open, + .path = pathlike.path.slice(), + }, + }; + }, + .err => |err| { + return .{ + .err = err, + }; + }, + }; + must_close.* = true; + return .{ .result = fd }; + } else { + // We assume that this is already a uv-casted file descriptor. + return .{ .result = pathlike.fd }; + } + } + + fn prepareReadWriteLoop(this: *CopyFileWindows) void { + // Open the destination first, so that if we need to call + // mkdirp(), we don't spend extra time opening the file handle for + // the source. + this.read_write_loop.destination_fd = switch (preparePathlike(&this.destination_file_store.data.file.pathlike, &this.read_write_loop.must_close_destination_fd, false)) { + .result => |fd| fd, + .err => |err| { + if (this.mkdirp_if_not_exists and err.getErrno() == .NOENT) { + this.mkdirp(); + return; + } + + this.throw(err); + return; + }, + }; + + this.read_write_loop.source_fd = switch (preparePathlike(&this.source_file_store.data.file.pathlike, &this.read_write_loop.must_close_source_fd, true)) { + .result => |fd| fd, + .err => |err| { + this.throw(err); + return; + }, + }; + + switch (this.read_write_loop.start(this)) { + .err => |err| { + this.throw(err); + return; + }, + .result => { + this.event_loop.refConcurrently(); + }, + } } fn copyfile(this: *CopyFileWindows) void { + // This is for making it easier for us to test this code path + if (bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE")) { + this.prepareReadWriteLoop(); + return; + } + var pathbuf1: bun.PathBuffer = undefined; var pathbuf2: bun.PathBuffer = undefined; var destination_file_store = &this.destination_file_store.data.file; @@ -2017,16 +2359,35 @@ pub const Blob = struct { break :brk destination_file_store.pathlike.path.sliceZ(&pathbuf1); }, .fd => |fd| { - const out = bun.getFdPath(fd, &pathbuf1) catch { - this.throw(.{ - .errno = @as(c_int, @intCast(@intFromEnum(bun.C.SystemErrno.EINVAL))), - .fd = fd, - .syscall = .open, - }); - return; - }; - pathbuf1[out.len] = 0; - break :brk pathbuf1[0..out.len :0]; + switch (bun.sys.File.from(fd).kind()) { + .err => |err| { + this.throw(err); + return; + }, + .result => |kind| { + switch (kind) { + .directory => { + this.throw(bun.sys.Error.fromCode(.ISDIR, .open)); + return; + }, + .character_device => { + this.prepareReadWriteLoop(); + return; + }, + else => { + const out = bun.getFdPath(fd, &pathbuf1) catch { + // This case can happen when either: + // - NUL device + // - Pipe. `cat foo.txt | bun bar.ts` + this.prepareReadWriteLoop(); + return; + }; + pathbuf1[out.len] = 0; + break :brk pathbuf1[0..out.len :0]; + }, + } + }, + } }, } }; @@ -2036,17 +2397,35 @@ pub const Blob = struct { break :brk source_file_store.pathlike.path.sliceZ(&pathbuf2); }, .fd => |fd| { - const out = bun.getFdPath(fd, &pathbuf2) catch { - this.throw(.{ - .errno = @as(c_int, @intCast(@intFromEnum(bun.C.SystemErrno.EINVAL))), - .fd = fd, - .syscall = .open, - }); - return; - }; - - pathbuf2[out.len] = 0; - break :brk pathbuf2[0..out.len :0]; + switch (bun.sys.File.from(fd).kind()) { + .err => |err| { + this.throw(err); + return; + }, + .result => |kind| { + switch (kind) { + .directory => { + this.throw(bun.sys.Error.fromCode(.ISDIR, .open)); + return; + }, + .character_device => { + this.prepareReadWriteLoop(); + return; + }, + else => { + const out = bun.getFdPath(fd, &pathbuf2) catch { + // This case can happen when either: + // - NUL device + // - Pipe. `cat foo.txt | bun bar.ts` + this.prepareReadWriteLoop(); + return; + }; + pathbuf2[out.len] = 0; + break :brk pathbuf2[0..out.len :0]; + }, + } + }, + } }, } }; @@ -2123,15 +2502,20 @@ pub const Blob = struct { return; } - var written = req.statbuf.size; + this.onComplete(req.statbuf.size); + } + pub fn onComplete(this: *CopyFileWindows, written_actual: usize) void { + var written = written_actual; if (written != @as(@TypeOf(written), @intCast(this.size)) and this.size != Blob.max_size) { this.truncate(); written = @intCast(this.size); } const globalThis = this.promise.strong.globalThis.?; const promise = this.promise.swap(); - defer event_loop.drainMicrotasks(); + var event_loop = this.event_loop; + event_loop.enter(); + defer event_loop.exit(); this.deinit(); promise.resolve(globalThis, JSC.JSValue.jsNumberFromUint64(written)); @@ -2152,11 +2536,12 @@ pub const Blob = struct { } pub fn deinit(this: *CopyFileWindows) void { + this.read_write_loop.close(); this.destination_file_store.deref(); this.source_file_store.deref(); - this.promise.strong.deinit(); + this.promise.deinit(); this.io_request.deinit(); - bun.destroy(this); + this.destroy(); } fn mkdirp( @@ -2173,6 +2558,7 @@ pub const Blob = struct { return; } + this.event_loop.refConcurrently(); JSC.Node.Async.AsyncMkdirp.new(.{ .completion = @ptrCast(&onMkdirpCompleteConcurrent), .completion_ctx = this, @@ -2183,6 +2569,8 @@ pub const Blob = struct { } fn onMkdirpComplete(this: *CopyFileWindows) void { + this.event_loop.unrefConcurrently(); + if (this.err) |err| { this.throw(err); bun.default_allocator.free(err.path); @@ -2462,6 +2850,7 @@ pub const Blob = struct { } while (true) { + // TODO: this should use non-blocking I/O. const written = switch (comptime use) { .copy_file_range => linux.copy_file_range(src_fd.cast(), null, dest_fd.cast(), null, remain, 0), .sendfile => linux.sendfile(dest_fd.cast(), src_fd.cast(), null, remain), @@ -2472,6 +2861,7 @@ pub const Blob = struct { .SUCCESS => {}, .NOSYS, .XDEV => { + // TODO: this should use non-blocking I/O. switch (JSC.Node.NodeFS.copyFileUsingReadWriteLoop("", "", src_fd, dest_fd, if (unknown_size) 0 else remain, &total_written)) { .err => |err| { this.system_error = err.toSystemError(); @@ -2504,6 +2894,7 @@ pub const Blob = struct { // incompatible with the chosen syscall, fall back // to a read/write loop if (total_written == 0) { + // TODO: this should use non-blocking I/O. switch (JSC.Node.NodeFS.copyFileUsingReadWriteLoop("", "", src_fd, dest_fd, if (unknown_size) 0 else remain, &total_written)) { .err => |err| { this.system_error = err.toSystemError(); @@ -2538,12 +2929,33 @@ pub const Blob = struct { } } - pub fn doFCopyFile(this: *CopyFile) anyerror!void { + pub fn doFCopyFileWithReadWriteLoopFallback(this: *CopyFile) anyerror!void { switch (bun.sys.fcopyfile(this.source_fd, this.destination_fd, posix.system.COPYFILE_DATA)) { .err => |errno| { - this.system_error = errno.toSystemError(); + switch (errno.getErrno()) { + // If the file type doesn't support seeking, it may return EBADF + // Example case: + // + // bun test bun-write.test | xargs echo + // + .BADF => { + var total_written: u64 = 0; + + // TODO: this should use non-blocking I/O. + switch (JSC.Node.NodeFS.copyFileUsingReadWriteLoop("", "", this.source_fd, this.destination_fd, 0, &total_written)) { + .err => |err| { + this.system_error = err.toSystemError(); + return bun.errnoToZigErr(err.errno); + }, + .result => {}, + } + }, + else => { + this.system_error = errno.toSystemError(); - return bun.errnoToZigErr(errno.errno); + return bun.errnoToZigErr(errno.errno); + }, + } }, .result => {}, } @@ -2589,15 +3001,6 @@ pub const Blob = struct { this.source_fd = this.source_file_store.pathlike.fd; } - if (comptime Environment.isWindows) { - this.system_error = SystemError{ - .code = bun.String.static("TODO"), - .syscall = bun.String.static("CopyFileEx"), - .message = bun.String.static("Not implemented on Windows yet"), - }; - return; - } - // Do we need to open both files? if (this.destination_fd == invalid_fd and this.source_fd == invalid_fd) { @@ -2748,7 +3151,7 @@ pub const Blob = struct { } if (comptime Environment.isMac) { - this.doFCopyFile() catch { + this.doFCopyFileWithReadWriteLoopFallback() catch { this.doClose(); return; @@ -2814,6 +3217,20 @@ pub const Blob = struct { return ByteStore.init(list.items, allocator); } + pub fn toInternalBlob(this: *ByteStore) InternalBlob { + const result = InternalBlob{ + .bytes = std.ArrayList(u8){ + .items = this.ptr[0..this.len], + .capacity = this.cap, + .allocator = this.allocator, + }, + }; + + this.allocator = bun.default_allocator; + this.len = 0; + this.cap = 0; + return result; + } pub fn slice(this: ByteStore) []u8 { return this.ptr[0..this.len]; } @@ -2843,7 +3260,7 @@ pub const Blob = struct { this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const thisValue = callframe.this(); if (Blob.streamGetCached(thisValue)) |cached| { return cached; @@ -2886,7 +3303,7 @@ pub const Blob = struct { pub fn toStreamWithOffset( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const this = callframe.this().as(Blob) orelse @panic("this is not a Blob"); const args = callframe.arguments(1).slice(); @@ -2897,22 +3314,31 @@ pub const Blob = struct { ); } - fn promisified( - value: JSC.JSValue, - global: *JSGlobalObject, - ) JSC.JSValue { - return JSC.JSPromise.wrap(global, value); + // Zig doesn't let you pass a function with a comptime argument to a runtime-knwon function. + fn lifetimeWrap(comptime Fn: anytype, comptime lifetime: JSC.WebCore.Lifetime) fn (*Blob, *JSC.JSGlobalObject) JSC.JSValue { + return struct { + fn wrap(this: *Blob, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + return Fn(this, globalObject, lifetime); + } + }.wrap; } pub fn getText( this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { + return this.getTextClone(globalThis); + } + + pub fn getTextClone( + this: *Blob, + globalObject: *JSC.JSGlobalObject, + ) JSC.JSValue { const store = this.store; if (store) |st| st.ref(); defer if (store) |st| st.deref(); - return promisified(this.toString(globalThis, .clone), globalThis); + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(toString, .clone), .{ this, globalObject }); } pub fn getTextTransfer( @@ -2922,21 +3348,26 @@ pub const Blob = struct { const store = this.store; if (store) |st| st.ref(); defer if (store) |st| st.deref(); - return promisified(this.toString(globalObject, .transfer), globalObject); + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(toString, .transfer), .{ this, globalObject }); } pub fn getJSON( this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { + return this.getJSONShare(globalThis); + } + + pub fn getJSONShare( + this: *Blob, + globalObject: *JSC.JSGlobalObject, + ) JSC.JSValue { const store = this.store; if (store) |st| st.ref(); defer if (store) |st| st.deref(); - - return promisified(this.toJSON(globalThis, .share), globalThis); + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(toJSON, .share), .{ this, globalObject }); } - pub fn getArrayBufferTransfer( this: *Blob, globalThis: *JSC.JSGlobalObject, @@ -2945,41 +3376,65 @@ pub const Blob = struct { if (store) |st| st.ref(); defer if (store) |st| st.deref(); - return promisified(this.toArrayBuffer(globalThis, .transfer), globalThis); + return JSC.JSPromise.wrap(globalThis, lifetimeWrap(toArrayBuffer, .transfer), .{ this, globalThis }); + } + + pub fn getArrayBufferClone( + this: *Blob, + globalThis: *JSC.JSGlobalObject, + ) JSC.JSValue { + const store = this.store; + if (store) |st| st.ref(); + defer if (store) |st| st.deref(); + return JSC.JSPromise.wrap(globalThis, lifetimeWrap(toArrayBuffer, .clone), .{ this, globalThis }); } pub fn getArrayBuffer( this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { + return this.getArrayBufferClone(globalThis); + } + + pub fn getBytesClone( + this: *Blob, + globalThis: *JSC.JSGlobalObject, + ) JSValue { const store = this.store; if (store) |st| st.ref(); defer if (store) |st| st.deref(); - return promisified(this.toArrayBuffer(globalThis, .clone), globalThis); + return JSC.JSPromise.wrap(globalThis, lifetimeWrap(toUint8Array, .clone), .{ this, globalThis }); } pub fn getBytes( this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { + return this.getBytesClone(globalThis); + } + + pub fn getBytesTransfer( + this: *Blob, + globalThis: *JSC.JSGlobalObject, + ) JSValue { const store = this.store; if (store) |st| st.ref(); defer if (store) |st| st.deref(); - return promisified(this.toUint8Array(globalThis, .clone), globalThis); + return JSC.JSPromise.wrap(globalThis, lifetimeWrap(toUint8Array, .transfer), .{ this, globalThis }); } pub fn getFormData( this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const store = this.store; if (store) |st| st.ref(); defer if (store) |st| st.deref(); - return promisified(this.toFormData(globalThis, .temporary), globalThis); + return JSC.JSPromise.wrap(globalThis, lifetimeWrap(toFormData, .temporary), .{ this, globalThis }); } fn getExistsSync(this: *Blob) JSC.JSValue { @@ -3006,7 +3461,7 @@ pub const Blob = struct { this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { return JSC.JSPromise.resolvedPromiseValue(globalThis, this.getExistsSync()); } @@ -3014,7 +3469,7 @@ pub const Blob = struct { this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var arguments_ = callframe.arguments(1); var arguments = arguments_.ptr[0..arguments_.len]; @@ -3038,8 +3493,9 @@ pub const Blob = struct { const vm = globalThis.bunVM(); const fd: bun.FileDescriptor = if (pathlike == .fd) pathlike.fd else brk: { var file_path: bun.PathBuffer = undefined; + const path = pathlike.path.sliceZ(&file_path); switch (bun.sys.open( - pathlike.path.sliceZ(&file_path), + path, bun.O.WRONLY | bun.O.CREAT | bun.O.NONBLOCK, write_permissions, )) { @@ -3047,7 +3503,7 @@ pub const Blob = struct { break :brk result; }, .err => |err| { - globalThis.throwInvalidArguments("Failed to create FileSink: {}", .{err.getErrno()}); + globalThis.throwValue(err.withPath(path).toJSC(globalThis)); return JSValue.jsUndefined(); }, } @@ -3075,11 +3531,12 @@ pub const Blob = struct { }; }; var sink = JSC.WebCore.FileSink.init(fd, this.globalThis.bunVM().eventLoop()); + sink.writer.owns_fd = pathlike != .fd; if (is_stdout_or_stderr) { switch (sink.writer.startSync(fd, false)) { .err => |err| { - globalThis.vm().throwError(globalThis, err.toJSC(globalThis)); + globalThis.throwValue(err.toJSC(globalThis)); sink.deref(); return JSC.JSValue.zero; @@ -3089,7 +3546,7 @@ pub const Blob = struct { } else { switch (sink.writer.start(fd, true)) { .err => |err| { - globalThis.vm().throwError(globalThis, err.toJSC(globalThis)); + globalThis.throwValue(err.toJSC(globalThis)); sink.deref(); return JSC.JSValue.zero; @@ -3151,7 +3608,7 @@ pub const Blob = struct { this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const allocator = bun.default_allocator; var arguments_ = callframe.arguments(3); var args = arguments_.ptr[0..arguments_.len]; @@ -3278,19 +3735,30 @@ pub const Blob = struct { pub fn getType( this: *Blob, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.content_type.len > 0) { if (this.content_type_allocated) { - return ZigString.init(this.content_type).toValueGC(globalThis); + return ZigString.init(this.content_type).toJS(globalThis); } - return ZigString.init(this.content_type).toValueGC(globalThis); + return ZigString.init(this.content_type).toJS(globalThis); } if (this.store) |store| { - return ZigString.init(store.mime_type.value).toValueGC(globalThis); + return ZigString.init(store.mime_type.value).toJS(globalThis); + } + + return ZigString.Empty.toJS(globalThis); + } + + pub fn getNameString(this: *Blob) ?bun.String { + if (this.name.tag != .Dead) return this.name; + + if (this.getFileName()) |path| { + this.name = bun.String.createUTF8(path); + return this.name; } - return ZigString.Empty.toValue(globalThis); + return null; } // TODO: Move this to a separate `File` object or BunFile @@ -3298,16 +3766,8 @@ pub const Blob = struct { this: *Blob, _: JSC.JSValue, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { - if (this.name.tag != .Dead) return this.name.toJS(globalThis); - - if (this.getFileName()) |path| { - var str = bun.String.createUTF8(path); - this.name = str; - return str.toJS(globalThis); - } - - return JSValue.undefined; + ) JSValue { + return if (this.getNameString()) |name| name.toJS(globalThis) else .undefined; } pub fn setName( @@ -3315,7 +3775,7 @@ pub const Blob = struct { jsThis: JSC.JSValue, globalThis: *JSC.JSGlobalObject, value: JSValue, - ) callconv(.C) bool { + ) bool { // by default we don't have a name so lets allow it to be set undefined if (value.isEmptyOrUndefinedOrNull()) { this.name.deref(); @@ -3325,8 +3785,13 @@ pub const Blob = struct { } if (value.isString()) { this.name.deref(); - this.name = bun.String.tryFromJS(value, globalThis) orelse return false; - this.name.ref(); + + this.name = bun.String.tryFromJS(value, globalThis) orelse { + // Handle allocation failure. + this.name = bun.String.empty; + return false; + }; + // We don't need to increment the reference count since tryFromJS already did it. Blob.nameSetCached(jsThis, globalThis, value); return true; } @@ -3356,7 +3821,7 @@ pub const Blob = struct { pub fn getLastModified( this: *Blob, _: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { if (this.store) |store| { if (store.data == .file) { // last_modified can be already set during read. @@ -3403,7 +3868,7 @@ pub const Blob = struct { } } - pub fn getSize(this: *Blob, _: *JSC.JSGlobalObject) callconv(.C) JSValue { + pub fn getSize(this: *Blob, _: *JSC.JSGlobalObject) JSValue { if (this.size == Blob.max_size) { this.resolveSize(); if (this.size == Blob.max_size and this.store != null) { @@ -3490,7 +3955,7 @@ pub const Blob = struct { pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) ?*Blob { + ) ?*Blob { const allocator = bun.default_allocator; var blob: Blob = undefined; var arguments = callframe.arguments(2); @@ -3507,7 +3972,8 @@ pub const Blob = struct { globalThis.throwInvalidArguments("new Blob() expects an Array", .{}); return null; } - globalThis.throw("out of memory", .{}); + if (!globalThis.hasException()) + globalThis.throwOutOfMemory(); return null; }; @@ -3556,7 +4022,7 @@ pub const Blob = struct { return blob_; } - pub fn finalize(this: *Blob) callconv(.C) void { + pub fn finalize(this: *Blob) void { this.deinit(); } @@ -3772,10 +4238,13 @@ pub const Blob = struct { const bom, const buf = strings.BOM.detectAndSplit(raw_bytes); if (buf.len == 0) { - return ZigString.Empty.toValue(global); + // If all it contained was the bom, we need to free the bytes + if (lifetime == .temporary) bun.default_allocator.free(raw_bytes); + return ZigString.Empty.toJS(global); } if (bom == .utf16_le) { + defer if (lifetime == .temporary) bun.default_allocator.free(raw_bytes); var out = bun.String.createUTF16(bun.reinterpretSlice(u16, buf)); defer out.deref(); return out.toJS(global); @@ -3849,6 +4318,10 @@ pub const Blob = struct { } } + pub fn toStringTransfer(this: *Blob, global: *JSGlobalObject) JSValue { + return this.toString(global, .transfer); + } + pub fn toString(this: *Blob, global: *JSGlobalObject, comptime lifetime: Lifetime) JSValue { if (this.needsToReadFile()) { return this.doReadFile(toStringWithBytes, global); @@ -3858,7 +4331,7 @@ pub const Blob = struct { @constCast(this.sharedView()); if (view_.len == 0) - return ZigString.Empty.toValue(global); + return ZigString.Empty.toJS(global); return toStringWithBytes(this, global, view_, lifetime); } @@ -3879,6 +4352,8 @@ pub const Blob = struct { if (bom == .utf16_le) { var out = bun.String.createUTF16(bun.reinterpretSlice(u16, buf)); + defer if (lifetime == .temporary) bun.default_allocator.free(raw_bytes); + defer if (lifetime == .transfer) this.detach(); defer out.deref(); return out.toJSByParseJSON(global); } @@ -3893,7 +4368,7 @@ pub const Blob = struct { // if toUTF16Alloc returns null, it means there are no non-ASCII characters if (strings.toUTF16Alloc(allocator, buf, false, false) catch null) |external| { if (comptime lifetime != .temporary) this.setIsASCIIFlag(false); - const result = ZigString.init16(external).toJSONObject(global); + const result = ZigString.initUTF16(external).toJSONObject(global); allocator.free(external); return result; } @@ -3925,6 +4400,15 @@ pub const Blob = struct { pub fn toArrayBufferViewWithBytes(this: *Blob, global: *JSGlobalObject, buf: []u8, comptime lifetime: Lifetime, comptime TypedArrayView: JSC.JSValue.JSType) JSValue { switch (comptime lifetime) { .clone => { + if (TypedArrayView != .ArrayBuffer) { + // ArrayBuffer doesn't have this limit. + if (buf.len > JSC.synthetic_allocation_limit) { + global.throwOutOfMemory(); + this.detach(); + return JSValue.zero; + } + } + if (comptime Environment.isLinux) { // If we can use a copy-on-write clone of the buffer, do so. if (this.store) |store| { @@ -3959,6 +4443,11 @@ pub const Blob = struct { return JSC.ArrayBuffer.create(global, buf, TypedArrayView); }, .share => { + if (buf.len > JSC.synthetic_allocation_limit and TypedArrayView != .ArrayBuffer) { + global.throwOutOfMemory(); + return JSValue.zero; + } + this.store.?.ref(); return JSC.ArrayBuffer.fromBytes(buf, TypedArrayView).toJSWithContext( global, @@ -3968,6 +4457,12 @@ pub const Blob = struct { ); }, .transfer => { + if (buf.len > JSC.synthetic_allocation_limit and TypedArrayView != .ArrayBuffer) { + global.throwOutOfMemory(); + this.detach(); + return JSValue.zero; + } + const store = this.store.?; this.transfer(); return JSC.ArrayBuffer.fromBytes(buf, TypedArrayView).toJSWithContext( @@ -3978,6 +4473,12 @@ pub const Blob = struct { ); }, .temporary => { + if (buf.len > JSC.synthetic_allocation_limit and TypedArrayView != .ArrayBuffer) { + global.throwOutOfMemory(); + bun.default_allocator.free(buf); + return JSValue.zero; + } + return JSC.ArrayBuffer.fromBytes(buf, TypedArrayView).toJS( global, null, @@ -4077,6 +4578,7 @@ pub const Blob = struct { var might_only_be_one_thing = false; arg.ensureStillAlive(); defer arg.ensureStillAlive(); + var fail_if_top_value_is_not_typed_array_like = false; switch (current.jsTypeLoose()) { .Array, .DerivedArray => { var top_iter = JSC.JSArrayIterator.init(current, global); @@ -4091,7 +4593,7 @@ pub const Blob = struct { else => { might_only_be_one_thing = true; if (require_array) { - return error.InvalidArguments; + fail_if_top_value_is_not_typed_array_like = true; } }, } @@ -4106,10 +4608,12 @@ pub const Blob = struct { JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject, => { - var str = top_value.toBunString(global); - defer str.deref(); - const bytes, const ascii = try str.toOwnedSliceReturningAllASCII(bun.default_allocator); - return Blob.initWithAllASCII(bytes, bun.default_allocator, global, ascii); + if (!fail_if_top_value_is_not_typed_array_like) { + var str = top_value.toBunString(global); + defer str.deref(); + const bytes, const ascii = try str.toOwnedSliceReturningAllASCII(bun.default_allocator); + return Blob.initWithAllASCII(bytes, bun.default_allocator, global, ascii); + } }, JSC.JSValue.JSType.ArrayBuffer, @@ -4120,6 +4624,7 @@ pub const Blob = struct { JSC.JSValue.JSType.Uint16Array, JSC.JSValue.JSType.Int32Array, JSC.JSValue.JSType.Uint32Array, + JSC.JSValue.JSType.Float16Array, JSC.JSValue.JSType.Float32Array, JSC.JSValue.JSType.Float64Array, JSC.JSValue.JSType.BigInt64Array, @@ -4130,33 +4635,41 @@ pub const Blob = struct { }, .DOMWrapper => { - if (top_value.as(Blob)) |blob| { - if (comptime move) { - var _blob = blob.*; - _blob.allocator = null; - blob.transfer(); - return _blob; - } else { - return blob.dupe(); - } - } else if (top_value.as(JSC.API.BuildArtifact)) |build| { - if (comptime move) { - // I don't think this case should happen? - var blob = build.blob; - blob.transfer(); - return blob; - } else { - return build.blob.dupe(); - } - } else if (current.toSliceClone(global)) |sliced| { - if (sliced.allocator.get()) |allocator| { - return Blob.initWithAllASCII(@constCast(sliced.slice()), allocator, global, false); + if (!fail_if_top_value_is_not_typed_array_like) { + if (top_value.as(Blob)) |blob| { + if (comptime move) { + var _blob = blob.*; + _blob.allocator = null; + blob.transfer(); + return _blob; + } else { + return blob.dupe(); + } + } else if (top_value.as(JSC.API.BuildArtifact)) |build| { + if (comptime move) { + // I don't think this case should happen? + var blob = build.blob; + blob.transfer(); + return blob; + } else { + return build.blob.dupe(); + } + } else if (current.toSliceClone(global)) |sliced| { + if (sliced.allocator.get()) |allocator| { + return Blob.initWithAllASCII(@constCast(sliced.slice()), allocator, global, false); + } } } }, else => {}, } + + // new Blob("ok") + // new File("ok", "file.txt") + if (fail_if_top_value_is_not_typed_array_like) { + return error.InvalidArguments; + } } var stack_allocator = std.heap.stackFallback(1024, bun.default_allocator); @@ -4196,9 +4709,9 @@ pub const Blob = struct { switch (item.jsTypeLoose()) { .NumberObject, .Cell, - JSC.JSValue.JSType.String, - JSC.JSValue.JSType.StringObject, - JSC.JSValue.JSType.DerivedStringObject, + .String, + .StringObject, + .DerivedStringObject, => { var sliced = item.toSlice(global, bun.default_allocator); const allocator = sliced.allocator.get(); @@ -4206,19 +4719,20 @@ pub const Blob = struct { joiner.push(sliced.slice(), allocator); continue; }, - JSC.JSValue.JSType.ArrayBuffer, - JSC.JSValue.JSType.Int8Array, - JSC.JSValue.JSType.Uint8Array, - JSC.JSValue.JSType.Uint8ClampedArray, - JSC.JSValue.JSType.Int16Array, - JSC.JSValue.JSType.Uint16Array, - JSC.JSValue.JSType.Int32Array, - JSC.JSValue.JSType.Uint32Array, - JSC.JSValue.JSType.Float32Array, - JSC.JSValue.JSType.Float64Array, - JSC.JSValue.JSType.BigInt64Array, - JSC.JSValue.JSType.BigUint64Array, - JSC.JSValue.JSType.DataView, + .ArrayBuffer, + .Int8Array, + .Uint8Array, + .Uint8ClampedArray, + .Int16Array, + .Uint16Array, + .Int32Array, + .Uint32Array, + .Float16Array, + .Float32Array, + .Float64Array, + .BigInt64Array, + .BigUint64Array, + .DataView, => { could_have_non_ascii = true; var buf = item.asArrayBuffer(global).?; @@ -4261,19 +4775,20 @@ pub const Blob = struct { } }, - JSC.JSValue.JSType.ArrayBuffer, - JSC.JSValue.JSType.Int8Array, - JSC.JSValue.JSType.Uint8Array, - JSC.JSValue.JSType.Uint8ClampedArray, - JSC.JSValue.JSType.Int16Array, - JSC.JSValue.JSType.Uint16Array, - JSC.JSValue.JSType.Int32Array, - JSC.JSValue.JSType.Uint32Array, - JSC.JSValue.JSType.Float32Array, - JSC.JSValue.JSType.Float64Array, - JSC.JSValue.JSType.BigInt64Array, - JSC.JSValue.JSType.BigUint64Array, - JSC.JSValue.JSType.DataView, + .ArrayBuffer, + .Int8Array, + .Uint8Array, + .Uint8ClampedArray, + .Int16Array, + .Uint16Array, + .Int32Array, + .Uint32Array, + .Float16Array, + .Float32Array, + .Float64Array, + .BigInt64Array, + .BigUint64Array, + .DataView, => { var buf = current.asArrayBuffer(global).?; joiner.pushStatic(buf.slice()); @@ -4309,6 +4824,14 @@ pub const AnyBlob = union(enum) { InternalBlob: InternalBlob, WTFStringImpl: bun.WTF.StringImpl, + pub fn hasOneRef(this: *const AnyBlob) bool { + if (this.store()) |s| { + return s.hasOneRef(); + } + + return false; + } + pub fn getFileName(this: *const AnyBlob) ?[]const u8 { return switch (this.*) { .Blob => this.Blob.getFileName(), @@ -4333,6 +4856,65 @@ pub const AnyBlob = union(enum) { }; } + fn toInternalBlobIfPossible(this: *AnyBlob) void { + if (this.* == .Blob) { + if (this.Blob.store) |s| { + if (s.data == .bytes and s.hasOneRef()) { + this.* = .{ .InternalBlob = s.data.bytes.toInternalBlob() }; + s.deref(); + return; + } + } + } + } + + pub fn toActionValue(this: *AnyBlob, globalThis: *JSGlobalObject, action: JSC.WebCore.BufferedReadableStreamAction) JSC.JSValue { + if (action != .blob) { + this.toInternalBlobIfPossible(); + } + + switch (action) { + .text => { + if (this.* == .Blob) { + return this.toString(globalThis, .clone); + } + + return this.toStringTransfer(globalThis); + }, + .bytes => { + if (this.* == .Blob) { + return this.toArrayBufferView(globalThis, .clone, .Uint8Array); + } + + return this.toUint8ArrayTransfer(globalThis); + }, + .blob => { + const result = Blob.new(this.toBlob(globalThis)); + result.allocator = bun.default_allocator; + result.globalThis = globalThis; + return result.toJS(globalThis); + }, + .arrayBuffer => { + if (this.* == .Blob) { + return this.toArrayBufferView(globalThis, .clone, .ArrayBuffer); + } + + return this.toArrayBufferTransfer(globalThis); + }, + .json => { + return this.toJSON(globalThis, .share); + }, + } + } + + pub fn toPromise(this: *AnyBlob, globalThis: *JSGlobalObject, action: JSC.WebCore.BufferedReadableStreamAction) JSC.JSValue { + return JSC.JSPromise.wrap(globalThis, toActionValue, .{ this, globalThis, action }); + } + + pub fn wrap(this: *AnyBlob, promise: JSC.AnyPromise, globalThis: *JSGlobalObject, action: JSC.WebCore.BufferedReadableStreamAction) void { + promise.wrap(globalThis, toActionValue, .{ this, globalThis, action }); + } + pub fn toJSON(this: *AnyBlob, global: *JSGlobalObject, comptime lifetime: JSC.WebCore.Lifetime) JSValue { switch (this.*) { .Blob => return this.Blob.toJSON(global, lifetime), @@ -4373,6 +4955,42 @@ pub const AnyBlob = union(enum) { } } + pub fn toJSONShare(this: *AnyBlob, global: *JSGlobalObject) JSValue { + return this.toJSON(global, .share); + } + + pub fn toStringTransfer(this: *AnyBlob, global: *JSGlobalObject) JSValue { + return this.toString(global, .transfer); + } + + pub fn toUint8ArrayTransfer(this: *AnyBlob, global: *JSGlobalObject) JSValue { + return this.toUint8Array(global, .transfer); + } + + pub fn toArrayBufferTransfer(this: *AnyBlob, global: *JSGlobalObject) JSValue { + return this.toArrayBuffer(global, .transfer); + } + + pub fn toBlob(this: *AnyBlob, global: *JSGlobalObject) Blob { + if (this.size() == 0) { + return Blob.initEmpty(global); + } + + if (this.* == .Blob) { + return this.Blob.dupe(); + } + + if (this.* == .WTFStringImpl) { + const blob = Blob.create(this.slice(), bun.default_allocator, global, true); + this.* = .{ .Blob = .{} }; + return blob; + } + + const blob = Blob.init(this.InternalBlob.slice(), this.InternalBlob.bytes.allocator, global); + this.* = .{ .Blob = .{} }; + return blob; + } + pub fn toString(this: *AnyBlob, global: *JSGlobalObject, comptime lifetime: JSC.WebCore.Lifetime) JSValue { switch (this.*) { .Blob => return this.Blob.toString(global, lifetime), @@ -4386,7 +5004,7 @@ pub const AnyBlob = union(enum) { // }, .InternalBlob => { if (this.InternalBlob.bytes.items.len == 0) { - return ZigString.Empty.toValue(global); + return ZigString.Empty.toJS(global); } const owned = this.InternalBlob.toStringOwned(global); @@ -4434,11 +5052,12 @@ pub const AnyBlob = union(enum) { const bytes = this.InternalBlob.toOwnedSlice(); this.* = .{ .Blob = .{} }; - const value = JSC.ArrayBuffer.fromBytes( + + return JSC.ArrayBuffer.fromDefaultAllocator( + global, bytes, TypedArrayView, ); - return value.toJS(global, null); }, .WTFStringImpl => { const str = bun.String.init(this.WTFStringImpl); @@ -4447,11 +5066,11 @@ pub const AnyBlob = union(enum) { const out_bytes = str.toUTF8WithoutRef(bun.default_allocator); if (out_bytes.isAllocated()) { - const value = JSC.ArrayBuffer.fromBytes( + return JSC.ArrayBuffer.fromDefaultAllocator( + global, @constCast(out_bytes.slice()), TypedArrayView, ); - return value.toJS(global, null); } return JSC.ArrayBuffer.create(global, out_bytes.slice(), TypedArrayView); @@ -4668,7 +5287,7 @@ pub const InlineBlob = extern struct { pub fn toStringOwned(this: *@This(), globalThis: *JSC.JSGlobalObject) JSValue { if (this.len == 0) - return ZigString.Empty.toValue(globalThis); + return ZigString.Empty.toJS(globalThis); var str = ZigString.init(this.sliceConst()); @@ -4676,7 +5295,7 @@ pub const InlineBlob = extern struct { str.markUTF8(); } - const out = str.toValueGC(globalThis); + const out = str.toJS(globalThis); out.ensureStillAlive(); this.len = 0; return out; diff --git a/src/bun.js/webcore/blob/ReadFile.zig b/src/bun.js/webcore/blob/ReadFile.zig index e4c3e03d8f249..02ab3921b1414 100644 --- a/src/bun.js/webcore/blob/ReadFile.zig +++ b/src/bun.js/webcore/blob/ReadFile.zig @@ -34,15 +34,14 @@ pub fn NewReadFileHandler(comptime Function: anytype) type { .result => |result| { const bytes = result.buf; if (blob.size > 0) - blob.size = @min(@as(u32, @truncate(bytes.len)), blob.size); - const value = Function(&blob, globalThis, bytes, .temporary); + blob.size = @min(@as(Blob.SizeType, @truncate(bytes.len)), blob.size); + const WrappedFn = struct { + pub fn wrapped(b: *Blob, g: *JSGlobalObject, by: []u8) JSC.JSValue { + return Function(b, g, by, .temporary); + } + }; - // invalid JSON needs to be rejected - if (value.isAnyError()) { - promise.reject(globalThis, value); - } else { - promise.resolve(globalThis, value); - } + JSC.AnyPromise.wrap(.{ .normal = promise }, globalThis, WrappedFn.wrapped, .{ &blob, globalThis, bytes }); }, .err => |err| { promise.reject(globalThis, err.toErrorInstance(globalThis)); @@ -476,6 +475,15 @@ pub const ReadFile = struct { // record the amount of data read this.buffer.items.len += read.len; } + // - If they DID set a max length, we should stop + // reading after that. + // + // - If they DID NOT set a max_length, then it will + // be Blob.max_size which is an impossibly large + // amount to read. + if (!this.read_eof and this.buffer.items.len >= this.max_length) { + break; + } if (!continue_reading) { // Stop reading, we errored @@ -496,14 +504,7 @@ pub const ReadFile = struct { if ((retry or (this.could_block and // If we received EOF, we can skip the poll() system // call. We already know it's done. - !this.read_eof)) and - // - If they DID set a max length, we should stop - // reading after that. - // - // - If they DID NOT set a max_length, then it will - // be Blob.max_size which is an impossibly large - // amount to read. - @as(usize, this.max_length) > this.buffer.items.len) + !this.read_eof))) { if ((this.could_block and // If we received EOF, we can skip the poll() system @@ -729,7 +730,9 @@ pub const ReadFileUV = struct { } pub fn queueRead(this: *ReadFileUV) void { - if (this.remainingBuffer().len > 0 and this.errno == null and !this.read_eof) { + // if not a regular file, buffer capacity is arbitrary, and running out doesn't mean we're + // at the end of the file + if ((this.remainingBuffer().len > 0 or !this.is_regular_file) and this.errno == null and !this.read_eof) { log("ReadFileUV.queueRead - this.remainingBuffer().len = {d}", .{this.remainingBuffer().len}); if (!this.is_regular_file) { diff --git a/src/bun.js/webcore/blob/WriteFile.zig b/src/bun.js/webcore/blob/WriteFile.zig index 06ba8e92e195a..13804680aeb18 100644 --- a/src/bun.js/webcore/blob/WriteFile.zig +++ b/src/bun.js/webcore/blob/WriteFile.zig @@ -507,9 +507,11 @@ pub const WriteFileWindows = struct { fn onMkdirpComplete(this: *WriteFileWindows) void { this.event_loop.unrefConcurrently(); - if (this.err) |err| { - this.throw(err); - bun.default_allocator.free(err.path); + const err = this.err; + this.err = null; + if (err) |err_| { + this.throw(err_); + bun.default_allocator.free(err_.path); return; } @@ -685,17 +687,17 @@ pub const WriteFileWaitFromLockedValueTask = struct { var globalThis = this.globalThis; var file_blob = this.file_blob; switch (value.*) { - .Error => |err| { + .Error => |*err_ref| { file_blob.detach(); _ = value.use(); - this.promise.strong.deinit(); + this.promise.deinit(); bun.destroy(this); - promise.reject(globalThis, err); + promise.reject(globalThis, err_ref.toJS(globalThis)); }, .Used => { file_blob.detach(); _ = value.use(); - this.promise.strong.deinit(); + this.promise.deinit(); bun.destroy(this); promise.reject(globalThis, ZigString.init("Body was used after it was consumed").toErrorInstance(globalThis)); }, @@ -708,26 +710,18 @@ pub const WriteFileWaitFromLockedValueTask = struct { var blob = value.use(); // TODO: this should be one promise not two! const new_promise = Blob.writeFileWithSourceDestination(globalThis, &blob, &file_blob, this.mkdirp_if_not_exists); - if (new_promise.asAnyPromise()) |_promise| { - switch (_promise.status(globalThis.vm())) { - .Pending => { - // Fulfill the new promise using the old promise - promise.resolve( - globalThis, - new_promise, - ); - }, - .Rejected => { - promise.reject(globalThis, _promise.result(globalThis.vm())); - }, - else => { - promise.resolve(globalThis, _promise.result(globalThis.vm())); - }, + if (new_promise.asAnyPromise()) |p| { + switch (p.unwrap(globalThis.vm(), .mark_handled)) { + // Fulfill the new promise using the pending promise + .pending => promise.resolve(globalThis, new_promise), + + .rejected => |err| promise.reject(globalThis, err), + .fulfilled => |result| promise.resolve(globalThis, result), } } file_blob.detach(); - this.promise.strong.deinit(); + this.promise.deinit(); bun.destroy(this); }, .Locked => { diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 632bce1609551..1bae953617f70 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -69,7 +69,7 @@ pub const Body = struct { }; } - pub fn writeFormat(this: *const Body, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { + pub fn writeFormat(this: *Body, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { const Writer = @TypeOf(writer); try formatter.writeIndent(Writer, writer); @@ -85,7 +85,7 @@ pub const Body = struct { try formatter.printComma(Writer, writer, enable_ansi_colors); try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); - try Blob.writeFormatForSize(this.value.size(), writer, enable_ansi_colors); + try Blob.writeFormatForSize(false, this.value.size(), writer, enable_ansi_colors); } else if (this.value == .Locked) { if (this.value.Locked.readable.get()) |stream| { try formatter.printComma(Writer, writer, enable_ansi_colors); @@ -115,7 +115,7 @@ pub const Body = struct { /// used in HTTP server to ignore request bodies unless asked for it onStartBuffering: ?*const fn (ctx: *anyopaque) void = null, onStartStreaming: ?*const fn (ctx: *anyopaque) JSC.WebCore.DrainResult = null, - onReadableStreamAvailable: ?*const fn (ctx: *anyopaque, readable: JSC.WebCore.ReadableStream) void = null, + onReadableStreamAvailable: ?*const fn (ctx: *anyopaque, globalThis: *JSC.JSGlobalObject, readable: JSC.WebCore.ReadableStream) void = null, size_hint: Blob.SizeType = 0, deinit: bool = false, @@ -160,12 +160,15 @@ pub const Body = struct { return false; } + pub fn isStreamingOrBuffering(this: *PendingValue) bool { + return this.readable.held.has() or (this.promise != null and !this.promise.?.isEmptyOrUndefinedOrNull()); + } pub fn hasPendingPromise(this: *PendingValue) bool { const promise = this.promise orelse return false; if (promise.asAnyPromise()) |internal| { - if (internal.status(this.global.vm()) != .Pending) { + if (internal.status(this.global.vm()) != .pending) { promise.unprotect(); this.promise = null; return false; @@ -191,28 +194,16 @@ pub const Body = struct { pub fn setPromise(value: *PendingValue, globalThis: *JSC.JSGlobalObject, action: Action) JSValue { value.action = action; - if (value.readable.get()) |readable| handle_stream: { + if (value.readable.get()) |readable| { switch (action) { .getFormData, .getText, .getJSON, .getBlob, .getArrayBuffer, .getBytes => { - value.promise = switch (action) { + const promise = switch (action) { .getJSON => globalThis.readableStreamToJSON(readable.value), .getArrayBuffer => globalThis.readableStreamToArrayBuffer(readable.value), .getBytes => globalThis.readableStreamToBytes(readable.value), .getText => globalThis.readableStreamToText(readable.value), .getBlob => globalThis.readableStreamToBlob(readable.value), .getFormData => |form_data| brk: { - if (value.onStartBuffering != null) { - if (readable.isDisturbed(globalThis)) { - form_data.?.deinit(); - value.readable.deinit(); - value.action = .{ .none = {} }; - return JSC.JSPromise.rejectedPromiseValue(globalThis, globalThis.createErrorInstance("ReadableStream is already used", .{})); - } else { - value.readable.deinit(); - } - - break :handle_stream; - } defer { form_data.?.deinit(); value.action.getFormData = null; @@ -220,18 +211,16 @@ pub const Body = struct { break :brk globalThis.readableStreamToFormData(readable.value, switch (form_data.?.encoding) { .Multipart => |multipart| bun.String.init(multipart).toJS(globalThis), - .URLEncoded => JSC.JSValue.jsUndefined(), + .URLEncoded => .undefined, }); }, else => unreachable, }; - value.promise.?.ensureStillAlive(); - - readable.detachIfPossible(globalThis); value.readable.deinit(); - value.promise.?.protect(); - - return value.promise.?; + // The ReadableStream within is expected to keep this Promise alive. + // If you try to protect() this, it will leak memory because the other end of the ReadableStream won't call it. + // See https://github.com/oven-sh/bun/issues/13678 + return promise; }, .none => {}, @@ -307,9 +296,66 @@ pub const Body = struct { Locked: PendingValue, Used: void, Empty: void, - Error: JSValue, + Error: ValueError, Null: void, + pub const heap_breakdown_label = "BodyValue"; + pub const ValueError = union(enum) { + AbortReason: JSC.CommonAbortReason, + SystemError: JSC.SystemError, + Message: bun.String, + JSValue: JSC.Strong, + + pub fn toStreamError(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.WebCore.StreamResult.StreamError { + return switch (this.*) { + .AbortReason => .{ + .AbortReason = this.AbortReason, + }, + else => .{ + .JSValue = this.toJS(globalObject), + }, + }; + } + + pub fn toJS(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + const js_value = switch (this.*) { + .AbortReason => |reason| reason.toJS(globalObject), + .SystemError => |system_error| system_error.toErrorInstance(globalObject), + .Message => |message| message.toErrorInstance(globalObject), + // do a early return in this case we don't need to create a new Strong + .JSValue => |js_value| return js_value.get() orelse JSC.JSValue.jsUndefined(), + }; + this.* = .{ .JSValue = JSC.Strong.create(js_value, globalObject) }; + return js_value; + } + + pub fn dupe(this: *const @This(), globalObject: *JSC.JSGlobalObject) @This() { + var value = this.*; + switch (this.*) { + .SystemError => value.SystemError.ref(), + .Message => value.Message.ref(), + .JSValue => |js_ref| { + if (js_ref.get()) |js_value| { + return .{ .JSValue = JSC.Strong.create(js_value, globalObject) }; + } + return .{ .JSValue = .{} }; + }, + .AbortReason => {}, + } + return value; + } + + pub fn deinit(this: *@This()) void { + switch (this.*) { + .SystemError => |system_error| system_error.deref(), + .Message => |message| message.deref(), + .JSValue => this.JSValue.deinit(), + .AbortReason => {}, + } + // safe empty value after deinit + this.* = .{ .JSValue = .{} }; + } + }; pub fn toBlobIfPossible(this: *Value) void { if (this.* == .WTFStringImpl) { if (this.WTFStringImpl.toUTF8IfNeeded(bun.default_allocator)) |bytes| { @@ -436,7 +482,7 @@ pub const Body = struct { if (locked.readable.get()) |readable| { return readable.value; } - if (locked.promise != null) { + if (locked.promise != null or locked.action != .none) { return JSC.WebCore.ReadableStream.used(globalThis); } var drain_result: JSC.WebCore.DrainResult = .{ @@ -474,7 +520,7 @@ pub const Body = struct { }, globalThis); if (locked.onReadableStreamAvailable) |onReadableStreamAvailable| { - onReadableStreamAvailable(locked.task.?, locked.readable.get().?); + onReadableStreamAvailable(locked.task.?, globalThis, locked.readable.get().?); } return locked.readable.get().?.value; @@ -549,13 +595,13 @@ pub const Body = struct { if (value.as(JSC.DOMFormData)) |form_data| { return Body.Value{ - .Blob = Blob.fromDOMFormData(globalThis, globalThis.allocator(), form_data), + .Blob = Blob.fromDOMFormData(globalThis, bun.default_allocator, form_data), }; } if (value.as(JSC.URLSearchParams)) |search_params| { return Body.Value{ - .Blob = Blob.fromURLSearchParams(globalThis, globalThis.allocator(), search_params), + .Blob = Blob.fromURLSearchParams(globalThis, bun.default_allocator, search_params), }; } @@ -597,12 +643,15 @@ pub const Body = struct { return Body.Value{ .Blob = Blob.get(globalThis, value, true, false) catch |err| { - if (err == error.InvalidArguments) { - globalThis.throwInvalidArguments("Expected an Array", .{}); - return null; + if (!globalThis.hasException()) { + if (err == error.InvalidArguments) { + globalThis.throwInvalidArguments("Expected an Array", .{}); + return null; + } + + globalThis.throwInvalidArguments("Invalid Body object", .{}); } - globalThis.throwInvalidArguments("Invalid Body object", .{}); return null; }, }; @@ -643,6 +692,8 @@ pub const Body = struct { locked.promise = null; switch (locked.action) { + // These ones must use promise.wrap() to handle exceptions thrown while calling .toJS() on the value. + // These exceptions can happen if the String is too long, ArrayBuffer is too large, JSON parse error, etc. .getText => { switch (new.*) { .WTFStringImpl, @@ -650,44 +701,39 @@ pub const Body = struct { // .InlineBlob, => { var blob = new.useAsAnyBlobAllowNonUTF8String(); - promise.resolve(global, blob.toString(global, .transfer)); + promise.wrap(global, AnyBlob.toStringTransfer, .{ &blob, global }); }, else => { var blob = new.use(); - promise.resolve(global, blob.toString(global, .transfer)); + promise.wrap(global, Blob.toStringTransfer, .{ &blob, global }); }, } }, .getJSON => { var blob = new.useAsAnyBlobAllowNonUTF8String(); - const json_value = blob.toJSON(global, .share); + promise.wrap(global, AnyBlob.toJSONShare, .{ &blob, global }); blob.detach(); - - if (json_value.isAnyError()) { - promise.reject(global, json_value); - } else { - promise.resolve(global, json_value); - } }, .getArrayBuffer => { var blob = new.useAsAnyBlobAllowNonUTF8String(); - promise.resolve(global, blob.toArrayBuffer(global, .transfer)); + promise.wrap(global, AnyBlob.toArrayBufferTransfer, .{ &blob, global }); }, .getBytes => { var blob = new.useAsAnyBlobAllowNonUTF8String(); - promise.resolve(global, blob.toUint8Array(global, .transfer)); + promise.wrap(global, AnyBlob.toUint8ArrayTransfer, .{ &blob, global }); }, + .getFormData => inner: { var blob = new.useAsAnyBlob(); defer blob.detach(); - var async_form_data = locked.action.getFormData orelse { + var async_form_data: *bun.FormData.AsyncFormData = locked.action.getFormData orelse { promise.reject(global, ZigString.init("Internal error: task for FormData must not be null").toErrorInstance(global)); break :inner; }; defer async_form_data.deinit(); async_form_data.toJS(global, blob.slice(), promise); }, - else => { + .none, .getBlob => { var blob = Blob.new(new.use()); blob.allocator = bun.default_allocator; if (headers) |fetch_headers| { @@ -857,48 +903,52 @@ pub const Body = struct { return any_blob; } - pub fn toErrorInstance(this: *Value, error_instance: JSC.JSValue, global: *JSGlobalObject) void { - error_instance.ensureStillAlive(); + pub fn toErrorInstance(this: *Value, err: ValueError, global: *JSGlobalObject) void { if (this.* == .Locked) { var locked = this.Locked; - locked.deinit = true; + this.* = .{ .Error = err }; + + var strong_readable = locked.readable; + locked.readable = .{}; + defer strong_readable.deinit(); + if (locked.hasPendingPromise()) { const promise = locked.promise.?; + defer promise.unprotect(); locked.promise = null; if (promise.asAnyPromise()) |internal| { - internal.reject(global, error_instance); + internal.reject(global, this.Error.toJS(global)); } - promise.unprotect(); } - if (locked.readable.get()) |readable| { - readable.abort(global); - locked.readable.deinit(); + // The Promise version goes before the ReadableStream version incase the Promise version is used too. + // Avoid creating unnecessary duplicate JSValue. + if (strong_readable.get()) |readable| { + if (readable.ptr == .Bytes) { + readable.ptr.Bytes.onData( + .{ .err = this.Error.toStreamError(global) }, + bun.default_allocator, + ); + } else { + readable.abort(global); + } } - // will be unprotected by body value deinit - error_instance.protect(); - this.* = .{ .Error = error_instance }; + if (locked.onReceiveValue) |onReceiveValue| { locked.onReceiveValue = null; onReceiveValue(locked.task.?, this); } return; } - // will be unprotected by body value deinit - error_instance.protect(); - this.* = .{ .Error = error_instance }; + this.* = .{ .Error = err }; } pub fn toError(this: *Value, err: anyerror, global: *JSGlobalObject) void { - var error_str = ZigString.init(std.fmt.allocPrint( - bun.default_allocator, + return this.toErrorInstance(.{ .Message = bun.String.createFormat( "Error reading file {s}", .{@errorName(err)}, - ) catch unreachable); - error_str.mark(); - const error_instance = error_str.toErrorInstance(global); - return this.toErrorInstance(error_instance, global); + ) catch bun.outOfMemory() }, global); } pub fn deinit(this: *Value) void { @@ -929,10 +979,84 @@ pub const Body = struct { } if (tag == .Error) { - JSC.C.JSValueUnprotect(VirtualMachine.get().global, this.Error.asObjectRef()); + this.Error.deinit(); + } + } + + pub fn tee(this: *Value, globalThis: *JSC.JSGlobalObject) Value { + var locked = &this.Locked; + + if (locked.readable.isDisturbed(globalThis)) { + return Value{ .Used = {} }; + } + + if (locked.readable.tee(globalThis)) |readable| { + return Value{ + .Locked = .{ + .readable = JSC.WebCore.ReadableStream.Strong.init(readable, globalThis), + .global = globalThis, + }, + }; + } + if (locked.promise != null or locked.action != .none or locked.readable.has()) { + return Value{ .Used = {} }; + } + + var drain_result: JSC.WebCore.DrainResult = .{ + .estimated_size = 0, + }; + + if (locked.onStartStreaming) |drain| { + locked.onStartStreaming = null; + drain_result = drain(locked.task.?); + } + + if (drain_result == .empty or drain_result == .aborted) { + this.* = .{ .Null = {} }; + return Value{ .Null = {} }; + } + + var reader = JSC.WebCore.ByteStream.Source.new(.{ + .context = undefined, + .globalThis = globalThis, + }); + + reader.context.setup(); + + if (drain_result == .estimated_size) { + reader.context.highWaterMark = @as(Blob.SizeType, @truncate(drain_result.estimated_size)); + reader.context.size_hint = @as(Blob.SizeType, @truncate(drain_result.estimated_size)); + } else if (drain_result == .owned) { + reader.context.buffer = drain_result.owned.list; + reader.context.size_hint = @as(Blob.SizeType, @truncate(drain_result.owned.size_hint)); } + + locked.readable = JSC.WebCore.ReadableStream.Strong.init(.{ + .ptr = .{ .Bytes = &reader.context }, + .value = reader.toReadableStream(globalThis), + }, globalThis); + + if (locked.onReadableStreamAvailable) |onReadableStreamAvailable| { + onReadableStreamAvailable(locked.task.?, globalThis, locked.readable.get().?); + } + + const teed = locked.readable.tee(globalThis) orelse return Value{ .Used = {} }; + + return Value{ + .Locked = .{ + .readable = JSC.WebCore.ReadableStream.Strong.init(teed, globalThis), + .global = globalThis, + }, + }; } + pub fn clone(this: *Value, globalThis: *JSC.JSGlobalObject) Value { + this.toBlobIfPossible(); + + if (this.* == .Locked) { + return this.tee(globalThis); + } + if (this.* == .InternalBlob) { var internal_blob = this.InternalBlob; this.* = .{ @@ -986,14 +1110,14 @@ pub fn BodyMixin(comptime Type: type) type { this: *Type, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var value: *Body.Value = this.getBodyValue(); if (value.* == .Used) { return handleBodyAlreadyUsed(globalObject); } if (value.* == .Locked) { - if (value.Locked.isDisturbed(Type, globalObject, callframe.this())) { + if (value.Locked.action != .none or value.Locked.isDisturbed(Type, globalObject, callframe.this())) { return handleBodyAlreadyUsed(globalObject); } @@ -1001,13 +1125,13 @@ pub fn BodyMixin(comptime Type: type) type { } var blob = value.useAsAnyBlobAllowNonUTF8String(); - return JSC.JSPromise.wrap(globalObject, blob.toString(globalObject, .transfer)); + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(AnyBlob.toString, .transfer), .{ &blob, globalObject }); } pub fn getBody( this: *Type, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { var body: *Body.Value = this.getBodyValue(); if (body.* == .Used) { @@ -1020,11 +1144,15 @@ pub fn BodyMixin(comptime Type: type) type { pub fn getBodyUsed( this: *Type, globalObject: *JSC.JSGlobalObject, - ) callconv(.C) JSValue { + ) JSValue { return JSValue.jsBoolean( switch (this.getBodyValue().*) { .Used => true, .Locked => |*pending| brk: { + if (pending.action != .none) { + break :brk true; + } + if (pending.readable.get()) |*stream| { break :brk stream.isDisturbed(globalObject); } @@ -1036,41 +1164,49 @@ pub fn BodyMixin(comptime Type: type) type { ); } + fn lifetimeWrap(comptime Fn: anytype, comptime lifetime: JSC.WebCore.Lifetime) fn (*AnyBlob, *JSC.JSGlobalObject) JSC.JSValue { + return struct { + fn wrap(this: *AnyBlob, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + return Fn(this, globalObject, lifetime); + } + }.wrap; + } + pub fn getJSON( this: *Type, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var value: *Body.Value = this.getBodyValue(); if (value.* == .Used) { return handleBodyAlreadyUsed(globalObject); } if (value.* == .Locked) { - if (value.Locked.isDisturbed(Type, globalObject, callframe.this())) { + if (value.Locked.action != .none or value.Locked.isDisturbed(Type, globalObject, callframe.this())) { return handleBodyAlreadyUsed(globalObject); } - return value.Locked.setPromise(globalObject, .{ .getJSON = {} }); + + value.toBlobIfPossible(); + if (value.* == .Locked) { + return value.Locked.setPromise(globalObject, .{ .getJSON = {} }); + } } var blob = value.useAsAnyBlobAllowNonUTF8String(); - const result = blob.toJSON(globalObject, .share); - return JSC.JSPromise.wrap(globalObject, result); + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(AnyBlob.toJSON, .share), .{ &blob, globalObject }); } fn handleBodyAlreadyUsed(globalObject: *JSC.JSGlobalObject) JSValue { - return JSC.JSPromise.rejectedPromiseValue( - globalObject, - ZigString.static("Body already used").toErrorInstance(globalObject), - ); + return globalObject.ERR_BODY_ALREADY_USED("Body already used", .{}).reject(); } pub fn getArrayBuffer( this: *Type, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var value: *Body.Value = this.getBodyValue(); if (value.* == .Used) { @@ -1078,22 +1214,27 @@ pub fn BodyMixin(comptime Type: type) type { } if (value.* == .Locked) { - if (value.Locked.isDisturbed(Type, globalObject, callframe.this())) { + if (value.Locked.action != .none or value.Locked.isDisturbed(Type, globalObject, callframe.this())) { return handleBodyAlreadyUsed(globalObject); } - return value.Locked.setPromise(globalObject, .{ .getArrayBuffer = {} }); + value.toBlobIfPossible(); + + if (value.* == .Locked) { + return value.Locked.setPromise(globalObject, .{ .getArrayBuffer = {} }); + } } // toArrayBuffer in AnyBlob checks for non-UTF8 strings var blob: AnyBlob = value.useAsAnyBlobAllowNonUTF8String(); - return JSC.JSPromise.wrap(globalObject, blob.toArrayBuffer(globalObject, .transfer)); + + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(AnyBlob.toArrayBuffer, .transfer), .{ &blob, globalObject }); } pub fn getBytes( this: *Type, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var value: *Body.Value = this.getBodyValue(); if (value.* == .Used) { @@ -1101,22 +1242,25 @@ pub fn BodyMixin(comptime Type: type) type { } if (value.* == .Locked) { - if (value.Locked.isDisturbed(Type, globalObject, callframe.this())) { + if (value.Locked.action != .none or value.Locked.isDisturbed(Type, globalObject, callframe.this())) { return handleBodyAlreadyUsed(globalObject); } - return value.Locked.setPromise(globalObject, .{ .getBytes = {} }); + value.toBlobIfPossible(); + if (value.* == .Locked) { + return value.Locked.setPromise(globalObject, .{ .getBytes = {} }); + } } // toArrayBuffer in AnyBlob checks for non-UTF8 strings var blob: AnyBlob = value.useAsAnyBlobAllowNonUTF8String(); - return JSC.JSPromise.wrap(globalObject, blob.toUint8Array(globalObject, .transfer)); + return JSC.JSPromise.wrap(globalObject, lifetimeWrap(AnyBlob.toUint8Array, .transfer), .{ &blob, globalObject }); } pub fn getFormData( this: *Type, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var value: *Body.Value = this.getBodyValue(); if (value.* == .Used) { @@ -1124,18 +1268,15 @@ pub fn BodyMixin(comptime Type: type) type { } if (value.* == .Locked) { - if (value.Locked.isDisturbed(Type, globalObject, callframe.this())) { + if (value.Locked.action != .none or value.Locked.isDisturbed(Type, globalObject, callframe.this())) { return handleBodyAlreadyUsed(globalObject); } + value.toBlobIfPossible(); } var encoder = this.getFormDataEncoding() orelse { // TODO: catch specific errors from getFormDataEncoding - const err = globalObject.createTypeErrorInstance("Can't decode form data from body because of incorrect MIME type/boundary", .{}); - return JSC.JSPromise.rejectedPromiseValue( - globalObject, - err, - ); + return globalObject.ERR_FORMDATA_PARSE_ERROR("Can't decode form data from body because of incorrect MIME type/boundary", .{}).reject(); }; if (value.* == .Locked) { @@ -1151,18 +1292,15 @@ pub fn BodyMixin(comptime Type: type) type { blob.slice(), encoder.encoding, ) catch |err| { - return JSC.JSPromise.rejectedPromiseValue( - globalObject, - globalObject.createTypeErrorInstance( - "FormData parse error {s}", - .{ - @errorName(err), - }, - ), - ); + return globalObject.ERR_FORMDATA_PARSE_ERROR( + "FormData parse error {s}", + .{ + @errorName(err), + }, + ).reject(); }; - return JSC.JSPromise.wrap( + return JSC.JSPromise.wrapValue( globalObject, js_value, ); @@ -1171,14 +1309,15 @@ pub fn BodyMixin(comptime Type: type) type { pub fn getBlob( this: *Type, globalObject: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { - return this.getBlobWithoutCallFrame(globalObject); + callframe: *JSC.CallFrame, + ) JSC.JSValue { + return getBlobWithThisValue(this, globalObject, callframe.this()); } - pub fn getBlobWithoutCallFrame( + pub fn getBlobWithThisValue( this: *Type, globalObject: *JSC.JSGlobalObject, + this_value: JSValue, ) JSC.JSValue { var value: *Body.Value = this.getBodyValue(); @@ -1187,10 +1326,18 @@ pub fn BodyMixin(comptime Type: type) type { } if (value.* == .Locked) { - if (value.Locked.promise == null or value.Locked.promise.?.isEmptyOrUndefinedOrNull()) { + if (value.Locked.action != .none or + ((this_value != .zero and value.Locked.isDisturbed(Type, globalObject, this_value)) or + (this_value == .zero and value.Locked.readable.isDisturbed(globalObject)))) + { + return handleBodyAlreadyUsed(globalObject); + } + + value.toBlobIfPossible(); + + if (value.* == .Locked) { return value.Locked.setPromise(globalObject, .{ .getBlob = {} }); } - return handleBodyAlreadyUsed(globalObject); } var blob = Blob.new(value.use()); @@ -1219,6 +1366,13 @@ pub fn BodyMixin(comptime Type: type) type { } return JSC.JSPromise.resolvedPromiseValue(globalObject, blob.toJS(globalObject)); } + + pub fn getBlobWithoutCallFrame( + this: *Type, + globalObject: *JSC.JSGlobalObject, + ) JSC.JSValue { + return getBlobWithThisValue(this, globalObject, .zero); + } }; } @@ -1226,7 +1380,7 @@ pub const BodyValueBufferer = struct { const log = bun.Output.scoped(.BodyValueBufferer, false); const ArrayBufferSink = JSC.WebCore.ArrayBufferSink; - const Callback = *const fn (ctx: *anyopaque, bytes: []const u8, err: ?JSC.JSValue, is_async: bool) void; + const Callback = *const fn (ctx: *anyopaque, bytes: []const u8, err: ?Body.Value.ValueError, is_async: bool) void; ctx: *anyopaque, onFinishedBuffering: Callback, @@ -1290,7 +1444,8 @@ pub const BodyValueBufferer = struct { .Error => |err| { log("Error", .{}); - return sink.onFinishedBuffering(sink.ctx, "", err, false); + sink.onFinishedBuffering(sink.ctx, "", err, false); + return; }, // .InlineBlob, .WTFStringImpl, @@ -1321,7 +1476,7 @@ pub const BodyValueBufferer = struct { switch (bytes) { .err => |err| { log("onFinishedLoadingFile Error", .{}); - sink.onFinishedBuffering(sink.ctx, "", err.toErrorInstance(sink.global), true); + sink.onFinishedBuffering(sink.ctx, "", .{ .SystemError = err }, true); return; }, .result => |data| { @@ -1357,14 +1512,14 @@ pub const BodyValueBufferer = struct { } } - pub fn onResolveStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onResolveStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { var args = callframe.arguments(2); var sink: *@This() = args.ptr[args.len - 1].asPromisePtr(@This()); sink.handleResolveStream(true); return JSValue.jsUndefined(); } - pub fn onRejectStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onRejectStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { const args = callframe.arguments(2); var sink = args.ptr[args.len - 1].asPromisePtr(@This()); const err = args.ptr[0]; @@ -1378,7 +1533,9 @@ pub const BodyValueBufferer = struct { sink.js_sink = null; wrapper.sink.destroy(); } - sink.onFinishedBuffering(sink.ctx, "", err, is_async); + var ref = JSC.Strong.create(err, sink.global); + defer ref.deinit(); + sink.onFinishedBuffering(sink.ctx, "", .{ .JSValue = ref }, is_async); } fn handleResolveStream(sink: *@This(), is_async: bool) void { @@ -1524,9 +1681,9 @@ pub const BodyValueBufferer = struct { fn onReceiveValue(ctx: *anyopaque, value: *JSC.WebCore.Body.Value) void { const sink = bun.cast(*@This(), ctx); switch (value.*) { - .Error => { + .Error => |err| { log("onReceiveValue Error", .{}); - sink.onFinishedBuffering(sink.ctx, "", value.Error, true); + sink.onFinishedBuffering(sink.ctx, "", err, true); return; }, else => { @@ -1534,7 +1691,7 @@ pub const BodyValueBufferer = struct { var input = value.useAsAnyBlobAllowNonUTF8String(); const bytes = input.slice(); log("onReceiveValue {}", .{bytes.len}); - sink.onFinishedBuffering(sink.ctx, bytes, value.Error, true); + sink.onFinishedBuffering(sink.ctx, bytes, null, true); }, } } diff --git a/src/bun.js/webcore/encoding.classes.ts b/src/bun.js/webcore/encoding.classes.ts index 7fd70406f4dd8..b26078ccc1fa3 100644 --- a/src/bun.js/webcore/encoding.classes.ts +++ b/src/bun.js/webcore/encoding.classes.ts @@ -31,4 +31,32 @@ export default [ }, }, }), + define({ + name: "TextEncoderStreamEncoder", + construct: true, + finalize: true, + JSType: "0b11101110", + configurable: false, + klass: {}, + proto: { + encode: { + fn: "encode", + length: 1, + + DOMJIT: { + returns: "JSUint8Array", + args: ["JSString"], + }, + }, + flush: { + fn: "flush", + length: 0, + + DOMJIT: { + returns: "JSUint8Array", + args: [], + }, + }, + }, + }), ]; diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index 8156ca0f7ebcc..6ac268370913c 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -15,9 +15,9 @@ const Output = bun.Output; const MutableString = bun.MutableString; const strings = bun.strings; const string = bun.string; -const default_allocator = bun.default_allocator; const FeatureFlags = bun.FeatureFlags; const ArrayBuffer = @import("../base.zig").ArrayBuffer; +const JSUint8Array = JSC.JSUint8Array; const Properties = @import("../base.zig").Properties; const castObj = @import("../base.zig").castObj; @@ -38,10 +38,6 @@ const Task = @import("../javascript.zig").Task; const picohttp = bun.picohttp; pub const TextEncoder = struct { - filler: u32 = 0, - - const utf8_string: string = "utf-8"; - pub export fn TextEncoder__encode8( globalThis: *JSGlobalObject, ptr: [*]const u8, @@ -112,7 +108,7 @@ pub const TextEncoder = struct { return uint8array; } else { const bytes = strings.toUTF8AllocWithType( - default_allocator, + bun.default_allocator, @TypeOf(slice), slice, ) catch { @@ -182,7 +178,7 @@ pub const TextEncoder = struct { var stack_buf: [2048]u8 = undefined; var buf_to_use: []u8 = &stack_buf; const length = rope_str.length(); - var array: JSValue = JSValue.zero; + var array: JSValue = .zero; if (length > stack_buf.len / 2) { array = JSC.JSValue.createUninitializedUint8Array(globalThis, length); array.ensureStillAlive(); @@ -198,7 +194,7 @@ pub const TextEncoder = struct { array.ensureStillAlive(); if (encoder.any_non_ascii) { - return JSC.JSValue.jsUndefined(); + return .undefined; } if (array.isEmpty()) { @@ -226,7 +222,7 @@ pub const TextEncoder = struct { result.written = 3; } const sized: [2]u32 = .{ result.read, result.written }; - return @as(u64, @bitCast(sized)); + return @bitCast(sized); } pub export fn TextEncoder__encodeInto8( @@ -240,7 +236,7 @@ pub const TextEncoder = struct { const result: strings.EncodeIntoResult = strings.copyLatin1IntoUTF8(output, []const u8, input); const sized: [2]u32 = .{ result.read, result.written }; - return @as(u64, @bitCast(sized)); + return @bitCast(sized); } }; @@ -372,6 +368,9 @@ pub const EncodingLabel = enum { Eight.case("utf-16le"), => EncodingLabel.@"UTF-16LE", + Eight.case("utf-16be"), + => EncodingLabel.@"UTF-16BE", + Eight.case("utf8"), Eight.case("utf-8") => EncodingLabel.@"UTF-8", else => null, }, @@ -407,14 +406,232 @@ pub const EncodingLabel = enum { } }; +pub const TextEncoderStreamEncoder = struct { + pending_lead_surrogate: ?u16 = null, + + const log = Output.scoped(.TextEncoderStreamEncoder, false); + + pub usingnamespace JSC.Codegen.JSTextEncoderStreamEncoder; + pub usingnamespace bun.New(TextEncoderStreamEncoder); + + pub fn finalize(this: *TextEncoderStreamEncoder) void { + this.destroy(); + } + + pub fn constructor(_: *JSGlobalObject, _: *JSC.CallFrame) ?*TextEncoderStreamEncoder { + return TextEncoderStreamEncoder.new(.{}); + } + + pub fn encode(this: *TextEncoderStreamEncoder, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSValue { + const arguments = callFrame.arguments(1).slice(); + if (arguments.len == 0) { + globalObject.throwNotEnoughArguments("TextEncoderStreamEncoder.encode", 1, arguments.len); + return .zero; + } + + const str: ZigString = (arguments[0].toStringOrNull(globalObject) orelse return .zero).getZigString(globalObject); + + if (str.is16Bit()) { + return this.encodeUTF16(globalObject, str.utf16SliceAligned()); + } + + return this.encodeLatin1(globalObject, str.slice()); + } + + pub fn encodeWithoutTypeChecks(this: *TextEncoderStreamEncoder, globalObject: *JSC.JSGlobalObject, input: *JSC.JSString) JSValue { + const str = input.getZigString(globalObject); + + if (str.is16Bit()) { + return this.encodeUTF16(globalObject, str.utf16SliceAligned()); + } + + return this.encodeLatin1(globalObject, str.slice()); + } + + fn encodeLatin1(this: *TextEncoderStreamEncoder, globalObject: *JSGlobalObject, input: []const u8) JSValue { + log("encodeLatin1: \"{s}\"", .{input}); + + if (input.len == 0) return JSUint8Array.createEmpty(globalObject); + + const prepend_replacement_len: usize = prepend_replacement: { + if (this.pending_lead_surrogate != null) { + this.pending_lead_surrogate = null; + // no latin1 surrogate pairs + break :prepend_replacement 3; + } + + break :prepend_replacement 0; + }; + // In a previous benchmark, counting the length took about as much time as allocating the buffer. + // + // Benchmark Time % CPU (ns) Iterations Ratio + // 288.00 ms 13.5% 288.00 ms simdutf::arm64::implementation::convert_latin1_to_utf8(char const*, unsigned long, char*) const + // 278.00 ms 13.0% 278.00 ms simdutf::arm64::implementation::utf8_length_from_latin1(char const*, unsigned long) const + // + // + var buffer = std.ArrayList(u8).initCapacity(bun.default_allocator, input.len + prepend_replacement_len) catch { + globalObject.throwOutOfMemory(); + return .zero; + }; + if (prepend_replacement_len > 0) { + buffer.appendSliceAssumeCapacity(&[3]u8{ 0xef, 0xbf, 0xbd }); + } + + var remain = input; + while (remain.len > 0) { + const result = strings.copyLatin1IntoUTF8(buffer.unusedCapacitySlice(), []const u8, remain); + + buffer.items.len += result.written; + remain = remain[result.read..]; + + if (result.written == 0 and result.read == 0) { + buffer.ensureUnusedCapacity(2) catch { + buffer.deinit(); + globalObject.throwOutOfMemory(); + return .zero; + }; + } else if (buffer.items.len == buffer.capacity and remain.len > 0) { + buffer.ensureTotalCapacity(buffer.items.len + remain.len + 1) catch { + buffer.deinit(); + globalObject.throwOutOfMemory(); + return .zero; + }; + } + } + + if (comptime Environment.isDebug) { + // wrap in comptime if so simdutf isn't called in a release build here. + bun.debugAssert(buffer.items.len == (bun.simdutf.length.utf8.from.latin1(input) + prepend_replacement_len)); + } + + return JSC.JSUint8Array.fromBytes(globalObject, buffer.items); + } + + fn encodeUTF16(this: *TextEncoderStreamEncoder, globalObject: *JSGlobalObject, input: []const u16) JSValue { + log("encodeUTF16: \"{}\"", .{bun.fmt.utf16(input)}); + + if (input.len == 0) return JSUint8Array.createEmpty(globalObject); + + const Prepend = struct { + bytes: [4]u8, + len: u3, + + pub const replacement: @This() = .{ .bytes = .{ 0xef, 0xbf, 0xbd, 0 }, .len = 3 }; + + pub fn fromSequence(seq: [4]u8, length: u3) @This() { + return .{ .bytes = seq, .len = length }; + } + }; + + var remain = input; + + const prepend: ?Prepend = prepend: { + if (this.pending_lead_surrogate) |lead| { + this.pending_lead_surrogate = null; + const maybe_trail = remain[0]; + if (strings.u16IsTrail(maybe_trail)) { + const converted = strings.utf16CodepointWithFFFD([]const u16, &.{ lead, maybe_trail }); + // shouldn't fail because `u16IsTrail` is true and `pending_lead_surrogate` is always + // a valid lead. + bun.debugAssert(!converted.fail); + + const sequence = strings.wtf8Sequence(converted.code_point); + + remain = remain[1..]; + if (remain.len == 0) { + return JSUint8Array.fromBytesCopy( + globalObject, + sequence[0..converted.utf8Width()], + ); + } + + break :prepend Prepend.fromSequence(sequence, converted.utf8Width()); + } + + break :prepend Prepend.replacement; + } + break :prepend null; + }; + + const length = bun.simdutf.length.utf8.from.utf16.le(remain); + + var buf = std.ArrayList(u8).initCapacity( + bun.default_allocator, + length + @as(usize, if (prepend) |pre| pre.len else 0), + ) catch { + globalObject.throwOutOfMemory(); + return .zero; + }; + + if (prepend) |*pre| { + buf.appendSliceAssumeCapacity(pre.bytes[0..pre.len]); + } + + const result = bun.simdutf.convert.utf16.to.utf8.with_errors.le(remain, buf.unusedCapacitySlice()); + + switch (result.status) { + else => { + // Slow path: there was invalid UTF-16, so we need to convert it without simdutf. + const lead_surrogate = strings.toUTF8ListWithTypeBun(&buf, []const u16, remain, true) catch { + buf.deinit(); + globalObject.throwOutOfMemory(); + return .zero; + }; + + if (lead_surrogate) |pending_lead| { + this.pending_lead_surrogate = pending_lead; + if (buf.items.len == 0) return JSUint8Array.createEmpty(globalObject); + } + + return JSC.JSUint8Array.fromBytes(globalObject, buf.items); + }, + .success => { + buf.items.len += result.count; + return JSC.JSUint8Array.fromBytes(globalObject, buf.items); + }, + } + } + + pub fn flush(this: *TextEncoderStreamEncoder, globalObject: *JSGlobalObject, _: *JSC.CallFrame) JSValue { + return flushBody(this, globalObject); + } + + pub fn flushWithoutTypeChecks(this: *TextEncoderStreamEncoder, globalObject: *JSGlobalObject) JSValue { + return flushBody(this, globalObject); + } + + fn flushBody(this: *TextEncoderStreamEncoder, globalObject: *JSGlobalObject) JSValue { + return if (this.pending_lead_surrogate == null) + JSUint8Array.createEmpty(globalObject) + else + JSUint8Array.fromBytesCopy(globalObject, &.{ 0xef, 0xbf, 0xbd }); + } +}; + pub const TextDecoder = struct { - scratch_memory: []u8 = &[_]u8{}, + + // used for utf8 decoding + buffered: struct { + buf: [3]u8 = .{0} ** 3, + len: u2 = 0, + + pub fn slice(this: *@This()) []const u8 { + return this.buf[0..this.len]; + } + } = .{}, + + // used for utf16 decoding + lead_byte: ?u8 = null, + lead_surrogate: ?u16 = null, + ignore_bom: bool = false, fatal: bool = false, encoding: EncodingLabel = EncodingLabel.@"UTF-8", - pub fn finalize(this: *TextDecoder) callconv(.C) void { - bun.default_allocator.destroy(this); + pub usingnamespace bun.New(TextDecoder); + + pub fn finalize(this: *TextDecoder) void { + this.destroy(); } pub usingnamespace JSC.Codegen.JSTextDecoder; @@ -422,13 +639,13 @@ pub const TextDecoder = struct { pub fn getIgnoreBOM( this: *TextDecoder, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.ignore_bom); } // pub fn setIgnoreBOM( // this: *TextDecoder, // _: *JSC.JSGlobalObject, - // ) callconv(.C) JSC.JSValue { + // ) JSC.JSValue { // this.ignore_bom = JSValue.fromRef(this.ignore_bom).toBoolean(); // return true; // } @@ -447,181 +664,166 @@ pub const TextDecoder = struct { pub fn getFatal( this: *TextDecoder, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return JSC.JSValue.jsBoolean(this.fatal); } - const utf8_string: string = "utf-8"; pub fn getEncoding( this: *TextDecoder, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(EncodingLabel.label.get(this.encoding).?).toValue(globalThis); + ) JSC.JSValue { + return ZigString.init(EncodingLabel.label.get(this.encoding).?).toJS(globalThis); } const Vector16 = std.meta.Vector(16, u16); const max_16_ascii: Vector16 = @splat(@as(u16, 127)); - fn decodeUTF16WithAlignment( - _: *TextDecoder, - comptime Slice: type, - slice: Slice, - ctx: js.JSContextRef, - ) JSC.JSValue { - var i: usize = 0; - - while (i < slice.len) { - while (i + strings.ascii_u16_vector_size <= slice.len) { - const vec: strings.AsciiU16Vector = slice[i..][0..strings.ascii_u16_vector_size].*; - if ((@reduce( - .Or, - @as( - strings.AsciiVectorU16U1, - @bitCast(vec > strings.max_u16_ascii), - ) | @as( - strings.AsciiVectorU16U1, - @bitCast(vec < strings.min_u16_ascii), - ), - ) == 0)) { - break; - } - i += strings.ascii_u16_vector_size; - } - while (i < slice.len and slice[i] <= 127) { - i += 1; + fn processCodeUnitUTF16( + this: *TextDecoder, + output: *std.ArrayListUnmanaged(u16), + saw_error: *bool, + code_unit: u16, + ) error{OutOfMemory}!void { + if (this.lead_surrogate) |lead_surrogate| { + this.lead_surrogate = null; + + if (strings.u16IsTrail(code_unit)) { + // TODO: why is this here? + // const code_point = strings.u16GetSupplementary(lead_surrogate, code_unit); + try output.appendSlice( + bun.default_allocator, + &.{ lead_surrogate, code_unit }, + ); + return; } - break; + try output.append(bun.default_allocator, strings.unicode_replacement); + saw_error.* = true; } - // is this actually a UTF-16 string that is just ascii? - // we can still allocate as UTF-16 and just copy the bytes - if (i == slice.len) { - if (comptime Slice == []u16) { - return ZigString.init16(slice).toValueGC(ctx); - } else { - var str = ZigString.init(""); - str._unsafe_ptr_do_not_use = @as([*]const u8, @ptrCast(slice.ptr)); - str.len = slice.len; - str.markUTF16(); - return str.toValueGC(ctx.ptr()); - } + if (strings.u16IsLead(code_unit)) { + this.lead_surrogate = code_unit; + return; } - var buffer = std.ArrayListAlignedUnmanaged(u16, @alignOf(@TypeOf(slice.ptr))){}; - // copy the allocator to reduce the number of threadlocal accesses - const allocator = VirtualMachine.get().allocator; - buffer.ensureTotalCapacity(allocator, slice.len) catch unreachable; - buffer.items.len = i; - - var len = std.mem.sliceAsBytes(slice[0..i]).len; - @memcpy( - std.mem.sliceAsBytes(buffer.items)[0..len], - std.mem.sliceAsBytes(slice)[0..len], - ); - - const first_high_surrogate = 0xD800; - const last_high_surrogate = 0xDBFF; - const first_low_surrogate = 0xDC00; - const last_low_surrogate = 0xDFFF; - - var remainder = slice[i..]; - while (remainder.len > 0) { - switch (remainder[0]) { - 0...127 => { - const count: usize = if (strings.firstNonASCII16(Slice, remainder)) |index| index + 1 else remainder.len; - - buffer.ensureUnusedCapacity(allocator, count) catch unreachable; - - const prev = buffer.items.len; - buffer.items.len += count; - // Since this string is freshly allocated, we know it's not going to overlap - len = std.mem.sliceAsBytes(remainder[0..count]).len; - @memcpy( - std.mem.sliceAsBytes(buffer.items[prev..])[0..len], - std.mem.sliceAsBytes(remainder)[0..len], - ); - remainder = remainder[count..]; - }, - first_high_surrogate...last_high_surrogate => |first| { - if (remainder.len > 1) { - if (remainder[1] >= first_low_surrogate and remainder[1] <= last_low_surrogate) { - buffer.ensureUnusedCapacity(allocator, 2) catch unreachable; - buffer.items.ptr[buffer.items.len] = first; - buffer.items.ptr[buffer.items.len + 1] = remainder[1]; - buffer.items.len += 2; - remainder = remainder[2..]; - continue; - } - } - buffer.ensureUnusedCapacity(allocator, 1) catch unreachable; - buffer.items.ptr[buffer.items.len] = strings.unicode_replacement; - buffer.items.len += 1; - remainder = remainder[1..]; - continue; - }, - // BOM handling - 0xFEFF => { - buffer.ensureTotalCapacity(allocator, 1) catch unreachable; - buffer.items.ptr[buffer.items.len] = remainder[0]; - buffer.items.len += 1; - remainder = remainder[1..]; - }, - - // Is this an unpaired low surrogate or four-digit hex escape? - else => { - buffer.ensureUnusedCapacity(allocator, 1) catch unreachable; - buffer.items.ptr[buffer.items.len] = strings.unicode_replacement; - buffer.items.len += 1; - remainder = remainder[1..]; - }, - } + if (strings.u16IsTrail(code_unit)) { + try output.append(bun.default_allocator, strings.unicode_replacement); + saw_error.* = true; + return; } - const full = buffer.toOwnedSlice(allocator) catch @panic("TODO"); + try output.append(bun.default_allocator, code_unit); + return; + } - var out = ZigString.init(""); - out._unsafe_ptr_do_not_use = @as([*]u8, @ptrCast(full.ptr)); - out.len = full.len; - out.markUTF16(); - return out.toValueGC(ctx.ptr()); + pub fn codeUnitFromBytesUTF16( + first: u16, + second: u16, + comptime big_endian: bool, + ) u16 { + return if (comptime big_endian) + (first << 8) | second + else + first | (second << 8); } - pub fn decode(this: *TextDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { - const arguments_ = callframe.arguments(2); - const arguments = arguments_.ptr[0..arguments_.len]; + pub fn decodeUTF16( + this: *TextDecoder, + bytes: []const u8, + comptime big_endian: bool, + comptime flush: bool, + ) error{OutOfMemory}!struct { std.ArrayListUnmanaged(u16), bool } { + var output: std.ArrayListUnmanaged(u16) = .{}; + try output.ensureTotalCapacity(bun.default_allocator, @divFloor(bytes.len, 2)); + + var remain = bytes; + var saw_error = false; + + if (this.lead_byte) |lead_byte| { + if (remain.len > 0) { + this.lead_byte = null; + + try this.processCodeUnitUTF16( + &output, + &saw_error, + codeUnitFromBytesUTF16(@intCast(lead_byte), @intCast(remain[0]), big_endian), + ); + remain = remain[1..]; + } + } + + var i: usize = 0; - if (arguments.len < 1 or arguments[0].isUndefined()) { - return ZigString.Empty.toValue(globalThis); + while (i < remain.len -| 1) { + try this.processCodeUnitUTF16( + &output, + &saw_error, + codeUnitFromBytesUTF16(@intCast(remain[i]), @intCast(remain[i + 1]), big_endian), + ); + i += 2; } - const array_buffer = arguments[0].asArrayBuffer(globalThis) orelse { + if (remain.len != 0 and i == remain.len - 1) { + this.lead_byte = remain[i]; + } else { + bun.assertWithLocation(i == remain.len, @src()); + } + + if (comptime flush) { + if (this.lead_byte != null or this.lead_surrogate != null) { + this.lead_byte = null; + this.lead_surrogate = null; + try output.append(bun.default_allocator, strings.unicode_replacement); + saw_error = true; + return .{ output, saw_error }; + } + } + + return .{ output, saw_error }; + } + + pub fn decode(this: *TextDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + const arguments = callframe.arguments(2).slice(); + + const input_slice = input_slice: { + if (arguments.len == 0 or arguments[0].isUndefined()) { + break :input_slice ""; + } + + if (arguments[0].asArrayBuffer(globalThis)) |array_buffer| { + break :input_slice array_buffer.slice(); + } + globalThis.throwInvalidArguments("TextDecoder.decode expects an ArrayBuffer or TypedArray", .{}); - return JSValue.zero; + return .zero; }; - if (arguments.len > 1 and arguments[1].isObject()) { - if (arguments[1].fastGet(globalThis, .stream)) |stream| { - if (stream.coerce(bool, globalThis)) { - return this.decodeSlice(globalThis, array_buffer.slice(), true); - } - - if (globalThis.hasException()) { - return JSValue.zero; + const stream = stream: { + if (arguments.len > 1 and arguments[1].isObject()) { + if (arguments[1].fastGet(globalThis, .stream)) |stream_value| { + const stream_bool = stream_value.coerce(bool, globalThis); + if (globalThis.hasException()) { + return .zero; + } + break :stream stream_bool; } } - } - return this.decodeSlice(globalThis, array_buffer.slice(), false); + break :stream false; + }; + + return switch (!stream) { + inline else => |flush| this.decodeSlice(globalThis, input_slice, flush), + }; } - pub fn decodeWithoutTypeChecks(this: *TextDecoder, globalThis: *JSC.JSGlobalObject, uint8array: *JSC.JSUint8Array) callconv(.C) JSValue { + pub fn decodeWithoutTypeChecks(this: *TextDecoder, globalThis: *JSC.JSGlobalObject, uint8array: *JSC.JSUint8Array) JSValue { return this.decodeSlice(globalThis, uint8array.slice(), false); } - fn decodeSlice(this: *TextDecoder, globalThis: *JSC.JSGlobalObject, buffer_slice: []const u8, comptime stream: bool) JSValue { + fn decodeSlice(this: *TextDecoder, globalThis: *JSC.JSGlobalObject, buffer_slice: []const u8, comptime flush: bool) JSValue { switch (this.encoding) { EncodingLabel.latin1 => { if (strings.isAllASCII(buffer_slice)) { - return ZigString.init(buffer_slice).toValueGC(globalThis); + return ZigString.init(buffer_slice).toJS(globalThis); } // It's unintuitive that we encode Latin1 as UTF16 even though the engine natively supports Latin1 strings... @@ -638,64 +840,85 @@ pub const TextDecoder = struct { return ZigString.toExternalU16(bytes.ptr, out.written, globalThis); }, EncodingLabel.@"UTF-8" => { - const toUTF16 = if (stream) strings.toUTF16Alloc else strings.toUTF16AllocNoTrim; - const moved_buffer_slice_8 = if (!this.ignore_bom and buffer_slice.len > 3 and std.mem.eql(u8, &[_]u8{ '\xEF', '\xBB', '\xBF' }, buffer_slice[0..3])) - buffer_slice[3..] - else - buffer_slice; + const input, const deinit = input: { + const maybe_without_bom = if (!this.ignore_bom and strings.hasPrefixComptime(buffer_slice, "\xef\xbb\xbf")) + buffer_slice[3..] + else + buffer_slice; + + if (this.buffered.len > 0) { + defer this.buffered.len = 0; + const joined = bun.default_allocator.alloc(u8, maybe_without_bom.len + this.buffered.len) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + @memcpy(joined[0..this.buffered.len], this.buffered.slice()); + @memcpy(joined[this.buffered.len..][0..maybe_without_bom.len], maybe_without_bom); + break :input .{ joined, true }; + } - if (this.fatal) { - if (toUTF16(default_allocator, moved_buffer_slice_8, true, false)) |result_| { - if (result_) |result| { - return ZigString.toExternalU16(result.ptr, result.len, globalThis); - } - } else |err| { - switch (err) { - error.InvalidByteSequence => { - const type_error = globalThis.createErrorInstanceWithCode(.ERR_ENCODING_INVALID_ENCODED_DATA, "Invalid byte sequence", .{}); - globalThis.throwValue(type_error); + break :input .{ maybe_without_bom, false }; + }; + + const maybe_decode_result = switch (this.fatal) { + inline else => |fail_if_invalid| strings.toUTF16AllocMaybeBuffered(bun.default_allocator, input, fail_if_invalid, flush) catch |err| { + if (deinit) bun.default_allocator.free(input); + if (comptime fail_if_invalid) { + if (err == error.InvalidByteSequence) { + globalThis.ERR_ENCODING_INVALID_ENCODED_DATA("Invalid byte sequence", .{}).throw(); return .zero; - }, - error.OutOfMemory => { - globalThis.throwOutOfMemory(); - return JSValue.zero; - }, + } } - } - } else { - if (toUTF16(default_allocator, moved_buffer_slice_8, false, false)) |result_| { - if (result_) |result| { - return ZigString.toExternalU16(result.ptr, result.len, globalThis); - } - } else |err| { - switch (err) { - error.OutOfMemory => { - globalThis.throwOutOfMemory(); - return JSValue.zero; - }, + + bun.assert(err == error.OutOfMemory); + globalThis.throwOutOfMemory(); + return .zero; + }, + }; + + if (maybe_decode_result) |decode_result| { + if (deinit) bun.default_allocator.free(input); + const decoded, const leftover, const leftover_len = decode_result; + bun.assert(this.buffered.len == 0); + if (comptime !flush) { + if (leftover_len != 0) { + this.buffered.buf = leftover; + this.buffered.len = leftover_len; } } + return ZigString.toExternalU16(decoded.ptr, decoded.len, globalThis); } + bun.debugAssert(input.len == 0 or !deinit); + // Experiment: using mimalloc directly is slightly slower - return ZigString.init(moved_buffer_slice_8).toValueGC(globalThis); + return ZigString.init(input).toJS(globalThis); }, - EncodingLabel.@"UTF-16LE" => { - const moved_buffer_slice_16 = if (!this.ignore_bom and buffer_slice.len > 2 and std.mem.eql(u8, &[_]u8{ '\xFF', '\xFE' }, buffer_slice[0..2])) + inline .@"UTF-16LE", .@"UTF-16BE" => |utf16_encoding| { + const bom = if (comptime utf16_encoding == .@"UTF-16LE") "\xff\xfe" else "\xfe\xff"; + const input = if (!this.ignore_bom and strings.hasPrefixComptime(buffer_slice, bom)) buffer_slice[2..] else buffer_slice; - if (std.mem.isAligned(@intFromPtr(moved_buffer_slice_16.ptr), @alignOf([*]const u16))) { - return this.decodeUTF16WithAlignment([]align(2) const u16, @as([]align(2) const u16, @alignCast(std.mem.bytesAsSlice(u16, moved_buffer_slice_16))), globalThis); + var decoded, const saw_error = this.decodeUTF16(input, utf16_encoding == .@"UTF-16BE", flush) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + + if (saw_error and this.fatal) { + decoded.deinit(bun.default_allocator); + globalThis.ERR_ENCODING_INVALID_ENCODED_DATA("The encoded data was not valid {s} data", .{@tagName(utf16_encoding)}).throw(); + return .zero; } - return this.decodeUTF16WithAlignment([]align(1) const u16, std.mem.bytesAsSlice(u16, moved_buffer_slice_16), globalThis); + var output = bun.String.fromUTF16(decoded.items); + return output.toJS(globalThis); }, else => { globalThis.throwInvalidArguments("TextDecoder.decode set to unsupported encoding", .{}); - return JSValue.zero; + return .zero; }, } } @@ -703,7 +926,7 @@ pub const TextDecoder = struct { pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) ?*TextDecoder { + ) ?*TextDecoder { var args_ = callframe.arguments(2); var arguments: []const JSC.JSValue = args_.ptr[0..args_.len]; @@ -712,7 +935,7 @@ pub const TextDecoder = struct { if (arguments.len > 0) { // encoding if (arguments[0].isString()) { - var str = arguments[0].toSlice(globalThis, default_allocator); + var str = arguments[0].toSlice(globalThis, bun.default_allocator); defer if (str.isAllocated()) str.deinit(); if (EncodingLabel.which(str.slice())) |label| { @@ -757,9 +980,7 @@ pub const TextDecoder = struct { } } - const result = getAllocator(globalThis).create(TextDecoder) catch unreachable; - result.* = decoder; - return result; + return TextDecoder.new(decoder); } }; @@ -893,8 +1114,11 @@ pub const Encoder = struct { return bun.String.createExternalGloballyAllocated(.latin1, input); } - defer bun.default_allocator.free(input); const str, const chars = bun.String.createUninitialized(.latin1, input.len); + defer bun.default_allocator.free(input); + if (str.tag == .Dead) { + return str; + } strings.copyLatin1IntoASCII(chars, input); return str; }, @@ -902,7 +1126,11 @@ pub const Encoder = struct { return bun.String.createExternalGloballyAllocated(.latin1, input); }, .buffer, .utf8 => { - const converted = strings.toUTF16Alloc(bun.default_allocator, input, false, false) catch return bun.String.dead; + const converted = strings.toUTF16Alloc(bun.default_allocator, input, false, false) catch { + bun.default_allocator.free(input); + return bun.String.dead; + }; + if (converted) |utf16| { defer bun.default_allocator.free(input); return bun.String.createExternalGloballyAllocated(.utf16, utf16); @@ -919,7 +1147,6 @@ pub const Encoder = struct { } const as_u16 = std.mem.bytesAsSlice(u16, input); - return bun.String.createExternalGloballyAllocated(.utf16, @alignCast(as_u16)); }, @@ -927,6 +1154,10 @@ pub const Encoder = struct { defer bun.default_allocator.free(input); const str, const chars = bun.String.createUninitialized(.latin1, input.len * 2); + if (str.tag == .Dead) { + return str; + } + const wrote = strings.encodeBytesToHex(chars, input); // Return an empty string in this case, just like node. @@ -944,14 +1175,16 @@ pub const Encoder = struct { .base64url => { defer bun.default_allocator.free(input); const out, const chars = bun.String.createUninitialized(.latin1, bun.base64.urlSafeEncodeLen(input)); - _ = bun.base64.encodeURLSafe(chars, input); + if (out.tag != .Dead) { + _ = bun.base64.encodeURLSafe(chars, input); + } return out; }, .base64 => { defer bun.default_allocator.free(input); const to_len = bun.base64.encodeLen(input); - var to = bun.default_allocator.alloc(u8, to_len) catch return bun.String.dead; + const to = bun.default_allocator.alloc(u8, to_len) catch return bun.String.dead; const wrote = bun.base64.encode(to, input); return bun.String.createExternalGloballyAllocated(.latin1, to[0..wrote]); }, @@ -960,7 +1193,7 @@ pub const Encoder = struct { pub fn toString(input_ptr: [*]const u8, len: usize, global: *JSGlobalObject, comptime encoding: JSC.Node.Encoding) JSValue { if (len == 0) - return ZigString.Empty.toValue(global); + return ZigString.Empty.toJS(global); const input = input_ptr[0..len]; const allocator = VirtualMachine.get().allocator; @@ -988,11 +1221,11 @@ pub const Encoder = struct { // If we get here, it means we can safely assume the string is 100% ASCII characters // For this, we rely on the GC to manage the memory to minimize potential for memory leaks - return ZigString.init(input).toValueGC(global); + return ZigString.init(input).toJS(global); }, .ucs2, .utf16le => { // Avoid incomplete characters - if (len / 2 == 0) return ZigString.Empty.toValue(global); + if (len / 2 == 0) return ZigString.Empty.toJS(global); var output, const chars = bun.String.createUninitialized(.utf16, len / 2); defer output.deref(); diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index 2a5b06a17cfc1..61c9c735e2f44 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -49,32 +49,24 @@ const Body = JSC.WebCore.Body; const Blob = JSC.WebCore.Blob; const Response = JSC.WebCore.Response; -const body_value_pool_size: u16 = 256; -pub const BodyValueRef = bun.HiveRef(Body.Value, body_value_pool_size); -const BodyValueHiveAllocator = bun.HiveArray(BodyValueRef, body_value_pool_size).Fallback; - -var body_value_hive_allocator = BodyValueHiveAllocator.init(bun.default_allocator); - -pub fn InitRequestBodyValue(value: Body.Value) !*BodyValueRef { - return try BodyValueRef.init(value, &body_value_hive_allocator); -} // https://developer.mozilla.org/en-US/docs/Web/API/Request pub const Request = struct { url: bun.String = bun.String.empty, // NOTE(@cirospaciari): renamed to _headers to avoid direct manipulation, use getFetchHeaders, setFetchHeaders, ensureFetchHeaders and hasFetchHeaders instead _headers: ?*FetchHeaders = null, signal: ?*AbortSignal = null, - body: *BodyValueRef, + body: *JSC.BodyValueRef, method: Method = Method.GET, request_context: JSC.API.AnyRequestContext = JSC.API.AnyRequestContext.Null, https: bool = false, - upgrader: ?*anyopaque = null, - + weak_ptr_data: bun.WeakPtrData = .{}, // We must report a consistent value for this reported_estimated_size: usize = 0, + internal_event_callback: InternalJSEventCallback = .{}, const RequestMixin = BodyMixin(@This()); pub usingnamespace JSC.Codegen.JSRequest; + pub usingnamespace bun.New(@This()); pub const getText = RequestMixin.getText; pub const getBytes = RequestMixin.getBytes; @@ -85,6 +77,7 @@ pub const Request = struct { pub const getBlob = RequestMixin.getBlob; pub const getFormData = RequestMixin.getFormData; pub const getBlobWithoutCallFrame = RequestMixin.getBlobWithoutCallFrame; + pub const WeakRef = bun.WeakPtr(Request, .weak_ptr_data); pub export fn Request__getUWSRequest( this: *Request, @@ -92,16 +85,73 @@ pub const Request = struct { return this.request_context.getRequest(); } + pub export fn Request__setInternalEventCallback( + this: *Request, + callback: JSC.JSValue, + globalThis: *JSC.JSGlobalObject, + ) void { + this.internal_event_callback = InternalJSEventCallback.init(callback, globalThis); + // we always have the abort event but we need to enable the timeout event as well in case of `node:http`.Server.setTimeout is set + this.request_context.enableTimeoutEvents(); + } + + pub export fn Request__setTimeout( + this: *Request, + seconds: JSC.JSValue, + globalThis: *JSC.JSGlobalObject, + ) void { + if (!seconds.isNumber()) { + globalThis.throw("Failed to set timeout: The provided value is not of type 'number'.", .{}); + return; + } + + this.setTimeout(seconds.to(c_uint)); + } + comptime { if (!JSC.is_bindgen) { _ = Request__getUWSRequest; + _ = Request__setInternalEventCallback; + _ = Request__setTimeout; } } + pub const InternalJSEventCallback = struct { + function: JSC.Strong = .{}, + + pub const EventType = enum(u8) { + timeout = 0, + abort = 1, + }; + pub fn init(function: JSC.JSValue, globalThis: *JSC.JSGlobalObject) InternalJSEventCallback { + return InternalJSEventCallback{ + .function = JSC.Strong.create(function, globalThis), + }; + } + + pub fn hasCallback(this: *InternalJSEventCallback) bool { + return this.function.has(); + } + + pub fn trigger(this: *InternalJSEventCallback, eventType: EventType, globalThis: *JSC.JSGlobalObject) bool { + if (this.function.get()) |callback| { + _ = callback.call(globalThis, JSC.JSValue.jsUndefined(), &.{JSC.JSValue.jsNumber( + @intFromEnum(eventType), + )}) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); + return true; + } + return false; + } + + pub fn deinit(this: *InternalJSEventCallback) void { + this.function.deinit(); + } + }; + pub fn init( url: bun.String, headers: ?*FetchHeaders, - body: *BodyValueRef, + body: *JSC.BodyValueRef, method: Method, ) Request { return Request{ @@ -157,7 +207,7 @@ pub const Request = struct { pub fn writeFormat(this: *Request, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { const Writer = @TypeOf(writer); - try writer.print("Request ({}) {{\n", .{bun.fmt.size(this.body.value.size())}); + try writer.print("Request ({}) {{\n", .{bun.fmt.size(this.body.value.size(), .{})}); { formatter.indent += 1; defer formatter.indent -|= 1; @@ -190,9 +240,10 @@ pub const Request = struct { try formatter.writeIndent(Writer, writer); const size = this.body.value.size(); if (size == 0) { - try Blob.initEmpty(undefined).writeFormat(Formatter, formatter, writer, enable_ansi_colors); + var empty = Blob.initEmpty(undefined); + try empty.writeFormat(Formatter, formatter, writer, enable_ansi_colors); } else { - try Blob.writeFormatForSize(size, writer, enable_ansi_colors); + try Blob.writeFormatForSize(false, size, writer, enable_ansi_colors); } } else if (this.body.value == .Locked) { if (this.body.value.Locked.readable.get()) |stream| { @@ -232,30 +283,30 @@ pub const Request = struct { pub fn getCache( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(Properties.UTF8.default).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(Properties.UTF8.default).toJS(globalThis); } pub fn getCredentials( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(Properties.UTF8.include).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(Properties.UTF8.include).toJS(globalThis); } pub fn getDestination( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init("").toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init("").toJS(globalThis); } pub fn getIntegrity( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.Empty.toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.Empty.toJS(globalThis); } - pub fn getSignal(this: *Request, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getSignal(this: *Request, globalThis: *JSC.JSGlobalObject) JSC.JSValue { // Already have an C++ instance if (this.signal) |signal| { return signal.toJS(globalThis); @@ -273,15 +324,15 @@ pub const Request = struct { pub fn getMethod( this: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return bun.String.static(@tagName(this.method)).toJS(globalThis); } pub fn getMode( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(Properties.UTF8.navigate).toValue(globalThis); + ) JSC.JSValue { + return ZigString.init(Properties.UTF8.navigate).toJS(globalThis); } pub fn finalizeWithoutDeinit(this: *Request) void { @@ -294,45 +345,48 @@ pub const Request = struct { this.url = bun.String.empty; if (this.signal) |signal| { - _ = signal.unref(); + signal.unref(); this.signal = null; } + this.internal_event_callback.deinit(); } - pub fn finalize(this: *Request) callconv(.C) void { + pub fn finalize(this: *Request) void { this.finalizeWithoutDeinit(); _ = this.body.unref(); - bun.default_allocator.destroy(this); + if (this.weak_ptr_data.onFinalize()) { + this.destroy(); + } } pub fn getRedirect( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init(Properties.UTF8.follow).toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init(Properties.UTF8.follow).toJS(globalThis); } pub fn getReferrer( this: *Request, globalObject: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { if (this._headers) |headers_ref| { if (headers_ref.get("referrer", globalObject)) |referrer| { - return ZigString.init(referrer).toValueGC(globalObject); + return ZigString.init(referrer).toJS(globalObject); } } - return ZigString.init("").toValueGC(globalObject); + return ZigString.init("").toJS(globalObject); } pub fn getReferrerPolicy( _: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { - return ZigString.init("").toValueGC(globalThis); + ) JSC.JSValue { + return ZigString.init("").toJS(globalThis); } pub fn getUrl( this: *Request, globalObject: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { this.ensureURL() catch { globalObject.throw("Failed to join URL", .{}); return .zero; @@ -476,19 +530,29 @@ pub const Request = struct { globalThis: *JSC.JSGlobalObject, arguments: []const JSC.JSValue, ) ?Request { + var success = false; + const vm = globalThis.bunVM(); + const body = vm.initRequestBodyValue(.{ .Null = {} }) catch { + return null; + }; var req = Request{ - .body = InitRequestBodyValue(.{ .Null = {} }) catch { - return null; - }, + .body = body, }; + defer { + if (!success) { + req.finalizeWithoutDeinit(); + _ = req.body.unref(); + } + if (req.body != body) { + _ = body.unref(); + } + } if (arguments.len == 0) { globalThis.throw("Failed to construct 'Request': 1 argument required, but only 0 present.", .{}); - _ = req.body.unref(); return null; } else if (arguments[0].isEmptyOrUndefinedOrNull() or !arguments[0].isCell()) { globalThis.throw("Failed to construct 'Request': expected non-empty string or object, got undefined", .{}); - _ = req.body.unref(); return null; } @@ -504,8 +568,6 @@ pub const Request = struct { if (is_first_argument_a_url) { const str = bun.String.tryFromJS(arguments[0], globalThis) orelse { - req.finalizeWithoutDeinit(); - _ = req.body.unref(); return null; }; req.url = str; @@ -514,7 +576,6 @@ pub const Request = struct { fields.insert(.url); } else if (!url_or_object_type.isObject()) { globalThis.throw("Failed to construct 'Request': expected non-empty string or object", .{}); - _ = req.body.unref(); return null; } @@ -536,6 +597,7 @@ pub const Request = struct { if (value.asDirect(Request)) |request| { if (values_to_try.len == 1) { request.cloneInto(&req, globalThis.allocator(), globalThis, fields.contains(.url)); + success = true; return req; } @@ -549,6 +611,8 @@ pub const Request = struct { req._headers = headers; fields.insert(.headers); } + + if (globalThis.hasException()) return null; } if (!fields.contains(.body)) { @@ -556,6 +620,7 @@ pub const Request = struct { .Null, .Empty, .Used => {}, else => { req.body.value = request.body.value.clone(globalThis); + if (globalThis.hasException()) return null; fields.insert(.body); }, } @@ -591,20 +656,20 @@ pub const Request = struct { }, } } + + if (globalThis.hasException()) return null; } } if (!fields.contains(.body)) { if (value.fastGet(globalThis, .body)) |body_| { fields.insert(.body); - if (Body.Value.fromJS(globalThis, body_)) |body| { - req.body.value = body; - } else { - req.finalizeWithoutDeinit(); - _ = req.body.unref(); + req.body.value = Body.Value.fromJS(globalThis, body_) orelse { return null; - } + }; } + + if (globalThis.hasException()) return null; } if (!fields.contains(.url)) { @@ -617,15 +682,13 @@ pub const Request = struct { } else if (@intFromEnum(value) == @intFromEnum(values_to_try[values_to_try.len - 1]) and !is_first_argument_a_url and value.implementsToString(globalThis)) { - const str = bun.String.tryFromJS(value, globalThis) orelse { - req.finalizeWithoutDeinit(); - _ = req.body.unref(); - return null; - }; + const str = bun.String.tryFromJS(value, globalThis) orelse return null; req.url = str; if (!req.url.isEmpty()) fields.insert(.url); } + + if (globalThis.hasException()) return null; } if (!fields.contains(.signal)) { @@ -636,22 +699,19 @@ pub const Request = struct { signal_.ensureStillAlive(); req.signal = signal.ref(); } else { - globalThis.throw("Failed to construct 'Request': signal is not of type AbortSignal.", .{}); - req.finalizeWithoutDeinit(); - _ = req.body.unref(); + if (!globalThis.hasException()) { + globalThis.throw("Failed to construct 'Request': signal is not of type AbortSignal.", .{}); + } return null; } } + + if (globalThis.hasException()) return null; } if (!fields.contains(.method) or !fields.contains(.headers)) { - if (Response.Init.init(globalThis.allocator(), globalThis, value) catch null) |response_init| { - if (!explicit_check or (explicit_check and value.fastGet(globalThis, .method) != null)) { - if (!fields.contains(.method)) { - req.method = response_init.method; - fields.insert(.method); - } - } + if (globalThis.hasException()) return null; + if (Response.Init.init(globalThis, value) catch null) |response_init| { if (!explicit_check or (explicit_check and value.fastGet(globalThis, .headers) != null)) { if (response_init.headers) |headers| { if (!fields.contains(.headers)) { @@ -662,25 +722,40 @@ pub const Request = struct { } } } + + if (globalThis.hasException()) return null; + + if (!explicit_check or (explicit_check and value.fastGet(globalThis, .method) != null)) { + if (!fields.contains(.method)) { + req.method = response_init.method; + fields.insert(.method); + } + } + if (globalThis.hasException()) return null; } + + if (globalThis.hasException()) return null; } } + + if (globalThis.hasException()) { + return null; + } + if (req.url.isEmpty()) { globalThis.throw("Failed to construct 'Request': url is required.", .{}); - req.finalizeWithoutDeinit(); - _ = req.body.unref(); return null; } const href = JSC.URL.hrefFromString(req.url); if (href.isEmpty()) { - // globalThis.throw can cause GC, which could cause the above string to be freed. - // so we must increment the reference count before calling it. - globalThis.throw("Failed to construct 'Request': Invalid URL \"{}\"", .{ - req.url, - }); - req.finalizeWithoutDeinit(); - _ = req.body.unref(); + if (!globalThis.hasException()) { + // globalThis.throw can cause GC, which could cause the above string to be freed. + // so we must increment the reference count before calling it. + globalThis.ERR_INVALID_URL("Failed to construct 'Request': Invalid URL \"{}\"", .{ + req.url, + }).throw(); + } return null; } @@ -698,28 +773,25 @@ pub const Request = struct { req.body.value.Blob.content_type.len > 0 and !req._headers.?.fastHas(.ContentType)) { - req._headers.?.put("content-type", req.body.value.Blob.content_type, globalThis); + req._headers.?.put(.ContentType, req.body.value.Blob.content_type, globalThis); } req.calculateEstimatedByteSize(); + success = true; return req; } pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) ?*Request { + ) ?*Request { const arguments_ = callframe.arguments(2); const arguments = arguments_.ptr[0..arguments_.len]; const request = constructInto(globalThis, arguments) orelse { return null; }; - const request_ = getAllocator(globalThis).create(Request) catch { - return null; - }; - request_.* = request; - return request_; + return Request.new(request); } pub fn getBodyValue( @@ -731,10 +803,34 @@ pub const Request = struct { pub fn doClone( this: *Request, globalThis: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + callframe: *JSC.CallFrame, + ) JSC.JSValue { + const this_value = callframe.this(); var cloned = this.clone(getAllocator(globalThis), globalThis); - return cloned.toJS(globalThis); + + if (globalThis.hasException()) { + cloned.finalize(); + return .zero; + } + + const js_wrapper = cloned.toJS(globalThis); + if (js_wrapper != .zero) { + if (cloned.body.value == .Locked) { + if (cloned.body.value.Locked.readable.get()) |readable| { + // If we are teed, then we need to update the cached .body + // value to point to the new readable stream + // We must do this on both the original and cloned request + // but especially the original request since it will have a stale .body value now. + Request.bodySetCached(js_wrapper, globalThis, readable.value); + + if (this.body.value.Locked.readable.get()) |other_readable| { + Request.bodySetCached(this_value, globalThis, other_readable.value); + } + } + } + } + + return js_wrapper; } // Returns if the request has headers already cached/set. @@ -769,7 +865,7 @@ pub const Request = struct { if (this.request_context.getRequest()) |req| { // we have a request context, so we can get the headers from it - this._headers = FetchHeaders.createFromUWS(globalThis, req); + this._headers = FetchHeaders.createFromUWS(req); } else { // we don't have a request context, so we need to create an empty headers object this._headers = FetchHeaders.createEmpty(); @@ -777,7 +873,7 @@ pub const Request = struct { if (this.body.value == .Blob) { const content_type = this.body.value.Blob.content_type; if (content_type.len > 0) { - this._headers.?.put("content-type", content_type, globalThis); + this._headers.?.put(.ContentType, content_type, globalThis); } } } @@ -785,6 +881,23 @@ pub const Request = struct { return this._headers.?; } + pub fn getFetchHeadersUnlessEmpty( + this: *Request, + ) ?*FetchHeaders { + if (this._headers == null) { + if (this.request_context.getRequest()) |req| { + // we have a request context, so we can get the headers from it + this._headers = FetchHeaders.createFromUWS(req); + } + } + + const headers = this._headers orelse return null; + if (headers.isEmpty()) { + return null; + } + return headers; + } + /// Returns the headers of the request. This will not look at the request contex to get the headers. pub fn getFetchHeaders( this: *Request, @@ -796,14 +909,14 @@ pub const Request = struct { pub fn getHeaders( this: *Request, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return this.ensureFetchHeaders(globalThis).toJS(globalThis); } pub fn cloneHeaders(this: *Request, globalThis: *JSGlobalObject) ?*FetchHeaders { if (this._headers == null) { if (this.request_context.getRequest()) |uws_req| { - this._headers = FetchHeaders.createFromUWS(globalThis, uws_req); + this._headers = FetchHeaders.createFromUWS(uws_req); } } @@ -827,9 +940,11 @@ pub const Request = struct { ) void { _ = allocator; this.ensureURL() catch {}; - - const body = InitRequestBodyValue(this.body.value.clone(globalThis)) catch { - globalThis.throw("Failed to clone request", .{}); + const vm = globalThis.bunVM(); + const body = vm.initRequestBodyValue(this.body.value.clone(globalThis)) catch { + if (!globalThis.hasException()) { + globalThis.throw("Failed to clone request", .{}); + } return; }; const original_url = req.url; @@ -847,8 +962,15 @@ pub const Request = struct { } pub fn clone(this: *Request, allocator: std.mem.Allocator, globalThis: *JSGlobalObject) *Request { - const req = allocator.create(Request) catch unreachable; + const req = Request.new(undefined); this.cloneInto(req, allocator, globalThis, false); return req; } + + pub fn setTimeout( + this: *Request, + seconds: c_uint, + ) void { + _ = this.request_context.setTimeout(seconds); + } }; diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index 3a522f2d75669..bcbf7b00548e4 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -137,7 +137,7 @@ pub const Response = struct { pub fn writeFormat(this: *Response, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { const Writer = @TypeOf(writer); - try writer.print("Response ({}) {{\n", .{bun.fmt.size(this.body.len())}); + try writer.print("Response ({}) {{\n", .{bun.fmt.size(this.body.len(), .{})}); { formatter.indent += 1; @@ -196,7 +196,7 @@ pub const Response = struct { pub fn getURL( this: *Response, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { // https://developer.mozilla.org/en-US/docs/Web/API/Response/url return this.url.toJS(globalThis); } @@ -204,18 +204,18 @@ pub const Response = struct { pub fn getResponseType( this: *Response, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { if (this.init.status_code < 200) { - return ZigString.init("error").toValue(globalThis); + return ZigString.init("error").toJS(globalThis); } - return ZigString.init("default").toValue(globalThis); + return ZigString.init("default").toJS(globalThis); } pub fn getStatusText( this: *Response, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { // https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText return this.init.status_text.toJS(globalThis); } @@ -223,7 +223,7 @@ pub const Response = struct { pub fn getRedirected( this: *Response, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { // https://developer.mozilla.org/en-US/docs/Web/API/Response/redirected return JSValue.jsBoolean(this.redirected); } @@ -231,7 +231,7 @@ pub const Response = struct { pub fn getOK( this: *Response, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { // https://developer.mozilla.org/en-US/docs/Web/API/Response/ok return JSValue.jsBoolean(this.isOK()); } @@ -243,7 +243,7 @@ pub const Response = struct { if (this.body.value == .Blob) { const content_type = this.body.value.Blob.content_type; if (content_type.len > 0) { - this.init.headers.?.put("content-type", content_type, globalThis); + this.init.headers.?.put(.ContentType, content_type, globalThis); } } } @@ -254,17 +254,40 @@ pub const Response = struct { pub fn getHeaders( this: *Response, globalThis: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { return this.getOrCreateHeaders(globalThis).toJS(globalThis); } pub fn doClone( this: *Response, globalThis: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) callconv(.C) JSValue { + callframe: *JSC.CallFrame, + ) JSValue { + const this_value = callframe.this(); const cloned = this.clone(globalThis); - return Response.makeMaybePooled(globalThis, cloned); + if (globalThis.hasException()) { + cloned.finalize(); + return .zero; + } + + const js_wrapper = Response.makeMaybePooled(globalThis, cloned); + + if (js_wrapper != .zero) { + if (cloned.body.value == .Locked) { + if (cloned.body.value.Locked.readable.get()) |readable| { + // If we are teed, then we need to update the cached .body + // value to point to the new readable stream + // We must do this on both the original and cloned response + // but especially the original response since it will have a stale .body value now. + Response.bodySetCached(js_wrapper, globalThis, readable.value); + if (this.body.value.Locked.readable.get()) |other_readable| { + Response.bodySetCached(this_value, globalThis, other_readable.value); + } + } + } + } + + return js_wrapper; } pub fn makeMaybePooled(globalObject: *JSC.JSGlobalObject, ptr: *Response) JSValue { @@ -290,7 +313,7 @@ pub const Response = struct { pub fn getStatus( this: *Response, _: *JSC.JSGlobalObject, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { // https://developer.mozilla.org/en-US/docs/Web/API/Response/status return JSValue.jsNumber(this.init.status_code); } @@ -342,7 +365,7 @@ pub const Response = struct { pub fn constructJSON( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const args_list = callframe.arguments(2); // https://github.com/remix-run/remix/blob/db2c31f64affb2095e4286b91306b96435967969/packages/remix-server-runtime/responses.ts#L4 var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), args_list.ptr[0..args_list.len]); @@ -356,7 +379,13 @@ pub const Response = struct { }, .url = bun.String.empty, }; - + var did_succeed = false; + defer { + if (!did_succeed) { + response.body.deinit(bun.default_allocator); + response.init.deinit(bun.default_allocator); + } + } const json_value = args.nextEat() orelse JSC.JSValue.zero; if (@intFromEnum(json_value) != 0) { @@ -365,6 +394,10 @@ pub const Response = struct { // so this is correct json_value.jsonStringify(globalThis, 0, &str); + if (globalThis.hasException()) { + return .zero; + } + if (!str.isEmpty()) { if (str.value.WTFStringImpl.toUTF8IfNeeded(bun.default_allocator)) |bytes| { defer str.deref(); @@ -386,57 +419,75 @@ pub const Response = struct { if (init.isUndefinedOrNull()) {} else if (init.isNumber()) { response.init.status_code = @as(u16, @intCast(@min(@max(0, init.toInt32()), std.math.maxInt(u16)))); } else { - if (Response.Init.init(getAllocator(globalThis), globalThis, init) catch null) |_init| { + if (Response.Init.init(globalThis, init) catch |err| if (err == error.JSError) return .zero else null) |_init| { response.init = _init; } } } var headers_ref = response.getOrCreateHeaders(globalThis); - headers_ref.putDefault("content-type", MimeType.json.value, globalThis); + headers_ref.putDefault(.ContentType, MimeType.json.value, globalThis); + did_succeed = true; return bun.new(Response, response).toJS(globalThis); } pub fn constructRedirect( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { var args_list = callframe.arguments(4); // https://github.com/remix-run/remix/blob/db2c31f64affb2095e4286b91306b96435967969/packages/remix-server-runtime/responses.ts#L4 var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), args_list.ptr[0..args_list.len]); - var response = Response{ - .init = Response.Init{ - .status_code = 302, - }, - .body = Body{ - .value = .{ .Empty = {} }, - }, - .url = bun.String.empty, - }; + var url_string_slice = ZigString.Slice.empty; + defer url_string_slice.deinit(); + var response: Response = brk: { + var response = Response{ + .init = Response.Init{ + .status_code = 302, + }, + .body = Body{ + .value = .{ .Empty = {} }, + }, + .url = bun.String.empty, + }; - const url_string_value = args.nextEat() orelse JSC.JSValue.zero; - var url_string = ZigString.init(""); + const url_string_value = args.nextEat() orelse JSC.JSValue.zero; + var url_string = ZigString.init(""); - if (@intFromEnum(url_string_value) != 0) { - url_string = url_string_value.getZigString(globalThis.ptr()); - } - var url_string_slice = url_string.toSlice(getAllocator(globalThis)); - defer url_string_slice.deinit(); + if (@intFromEnum(url_string_value) != 0) { + url_string = url_string_value.getZigString(globalThis.ptr()); + } + url_string_slice = url_string.toSlice(getAllocator(globalThis)); + var did_succeed = false; + defer { + if (!did_succeed) { + response.body.deinit(bun.default_allocator); + response.init.deinit(bun.default_allocator); + } + } - if (args.nextEat()) |init| { - if (init.isUndefinedOrNull()) {} else if (init.isNumber()) { - response.init.status_code = @as(u16, @intCast(@min(@max(0, init.toInt32()), std.math.maxInt(u16)))); - } else { - if (Response.Init.init(getAllocator(globalThis), globalThis, init) catch null) |_init| { - response.init = _init; - response.init.status_code = 302; + if (args.nextEat()) |init| { + if (init.isUndefinedOrNull()) {} else if (init.isNumber()) { + response.init.status_code = @as(u16, @intCast(@min(@max(0, init.toInt32()), std.math.maxInt(u16)))); + } else { + if (Response.Init.init(globalThis, init) catch |err| + if (err == error.JSError) return .zero else null) |_init| + { + response.init = _init; + response.init.status_code = 302; + } } } - } + if (globalThis.hasException()) { + return .zero; + } + did_succeed = true; + break :brk response; + }; response.init.headers = response.getOrCreateHeaders(globalThis); var headers_ref = response.init.headers.?; - headers_ref.put("location", url_string_slice.slice(), globalThis); + headers_ref.put(.Location, url_string_slice.slice(), globalThis); const ptr = bun.new(Response, response); return ptr.toJS(globalThis); @@ -444,7 +495,7 @@ pub const Response = struct { pub fn constructError( globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const response = bun.new( Response, Response{ @@ -463,7 +514,7 @@ pub const Response = struct { pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) ?*Response { + ) ?*Response { const args_list = brk: { var args = callframe.arguments(2); if (args.len > 1 and args.ptr[1].isEmptyOrUndefinedOrNull()) { @@ -474,7 +525,7 @@ pub const Response = struct { const arguments = args_list.ptr[0..args_list.len]; - const init: Init = @as(?Init, brk: { + var init: Init = @as(?Init, brk: { switch (arguments.len) { 0 => { break :brk Init{ @@ -490,20 +541,25 @@ pub const Response = struct { }, else => { if (arguments[1].isObject()) { - break :brk Init.init(bun.default_allocator, globalThis, arguments[1]) catch null; + break :brk Init.init(globalThis, arguments[1]) catch null; } - bun.assert(!arguments[1].isEmptyOrUndefinedOrNull()); + if (!globalThis.hasException()) { + globalThis.throwInvalidArguments("new Response() requires a Response-like object in the 2nd argument", .{}); + } - const err = globalThis.createTypeErrorInstance("Expected options to be one of: null, undefined, or object", .{}); - globalThis.throwValue(err); break :brk null; }, } unreachable; }) orelse return null; - const body: Body = brk: { + if (globalThis.hasException()) { + init.deinit(bun.default_allocator); + return null; + } + + var body: Body = brk: { switch (arguments.len) { 0 => { break :brk Body{ @@ -515,7 +571,17 @@ pub const Response = struct { }, } unreachable; - } orelse return null; + } orelse { + init.deinit(bun.default_allocator); + + return null; + }; + + if (globalThis.hasException()) { + body.deinit(bun.default_allocator); + init.deinit(bun.default_allocator); + return null; + } var response = bun.new(Response, Response{ .body = body, @@ -527,7 +593,7 @@ pub const Response = struct { response.body.value.Blob.content_type.len > 0 and !response.init.headers.?.fastHas(.ContentType)) { - response.init.headers.?.put("content-type", response.body.value.Blob.content_type, globalThis); + response.init.headers.?.put(.ContentType, response.body.value.Blob.content_type, globalThis); } response.calculateEstimatedByteSize(); @@ -552,8 +618,11 @@ pub const Response = struct { return that; } - pub fn init(_: std.mem.Allocator, ctx: *JSGlobalObject, response_init: JSC.JSValue) !?Init { + pub fn init(globalThis: *JSGlobalObject, response_init: JSC.JSValue) !?Init { var result = Init{ .status_code = 200 }; + errdefer { + result.deinit(bun.default_allocator); + } if (!response_init.isCell()) return null; @@ -562,10 +631,8 @@ pub const Response = struct { // fast path: it's a Request object or a Response object // we can skip calling JS getters if (response_init.asDirect(Request)) |req| { - if (req.getFetchHeaders()) |headers| { - if (!headers.isEmpty()) { - result.headers = headers.cloneThis(ctx); - } + if (req.getFetchHeadersUnlessEmpty()) |headers| { + result.headers = headers.cloneThis(globalThis); } result.method = req.method; @@ -573,41 +640,63 @@ pub const Response = struct { } if (response_init.asDirect(Response)) |resp| { - return resp.init.clone(ctx); + return resp.init.clone(globalThis); } } - if (response_init.fastGet(ctx, .headers)) |headers| { + if (globalThis.hasException()) { + return error.JSError; + } + + if (response_init.fastGet(globalThis, .headers)) |headers| { if (headers.as(FetchHeaders)) |orig| { if (!orig.isEmpty()) { - result.headers = orig.cloneThis(ctx); + result.headers = orig.cloneThis(globalThis); } } else { - result.headers = FetchHeaders.createFromJS(ctx.ptr(), headers); + result.headers = FetchHeaders.createFromJS(globalThis.ptr(), headers); } } - if (response_init.fastGet(ctx, .status)) |status_value| { - const number = status_value.coerceToInt64(ctx); + if (globalThis.hasException()) { + return error.JSError; + } + + if (response_init.fastGet(globalThis, .status)) |status_value| { + const number = status_value.coerceToInt64(globalThis); if ((200 <= number and number < 600) or number == 101) { result.status_code = @as(u16, @truncate(@as(u32, @intCast(number)))); } else { - const err = ctx.createRangeErrorInstance("The status provided ({d}) must be 101 or in the range of [200, 599]", .{number}); - ctx.throwValue(err); - return null; + if (!globalThis.hasException()) { + const err = globalThis.createRangeErrorInstance("The status provided ({d}) must be 101 or in the range of [200, 599]", .{number}); + globalThis.throwValue(err); + } + return error.JSError; } } - if (response_init.fastGet(ctx, .statusText)) |status_text| { - result.status_text = bun.String.fromJS(status_text, ctx); + if (globalThis.hasException()) { + return error.JSError; + } + + if (response_init.fastGet(globalThis, .statusText)) |status_text| { + result.status_text = bun.String.fromJS(status_text, globalThis); } - if (response_init.fastGet(ctx, .method)) |method_value| { - if (Method.fromJS(ctx, method_value)) |method| { + if (globalThis.hasException()) { + return error.JSError; + } + + if (response_init.fastGet(globalThis, .method)) |method_value| { + if (Method.fromJS(globalThis, method_value)) |method| { result.method = method; } } + if (globalThis.hasException()) { + return error.JSError; + } + return result; } @@ -619,6 +708,7 @@ pub const Response = struct { } this.status_text.deref(); + this.status_text = bun.String.empty; } }; @@ -713,6 +803,7 @@ pub const Fetch = struct { comptime { if (!JSC.is_bindgen) { _ = Bun__fetch; + _ = Bun__fetchPreconnect; } } @@ -757,7 +848,7 @@ pub const Fetch = struct { has_schedule_callback: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), // must be stored because AbortSignal stores reason weakly - abort_reason: JSValue = JSValue.zero, + abort_reason: JSC.Strong = .{}, // custom checkServerIdentity check_server_identity: JSC.Strong = .{}, @@ -843,8 +934,8 @@ pub const Fetch = struct { this.request_headers.buf.deinit(allocator); this.request_headers = Headers{ .allocator = undefined }; - if (this.http != null) { - this.http.?.clearData(); + if (this.http) |http_| { + http_.clearData(); } if (this.metadata != null) { @@ -865,17 +956,9 @@ pub const Fetch = struct { this.scheduled_response_buffer.deinit(); this.request_body.detach(); - if (this.abort_reason != .zero) { - this.abort_reason.unprotect(); - this.abort_reason = .zero; - } - + this.abort_reason.deinit(); this.check_server_identity.deinit(); - - if (this.signal) |signal| { - this.signal = null; - signal.detach(this); - } + this.clearAbortSignal(); } fn deinit(this: *FetchTasklet) void { @@ -888,7 +971,10 @@ pub const Fetch = struct { var reporter = this.memory_reporter; const allocator = reporter.allocator(); - if (this.http) |http_| allocator.destroy(http_); + if (this.http) |http_| { + this.http = null; + allocator.destroy(http_); + } allocator.destroy(this); // reporter.assert(); bun.default_allocator.destroy(reporter); @@ -913,31 +999,24 @@ pub const Fetch = struct { pub fn onBodyReceived(this: *FetchTasklet) void { const success = this.result.isSuccess(); const globalThis = this.global_this; - const is_done = !success or !this.result.has_more; // reset the buffer if we are streaming or if we are not waiting for bufferig anymore var buffer_reset = true; defer { if (buffer_reset) { this.scheduled_response_buffer.reset(); } - - this.mutex.unlock(); - if (is_done) { - const vm = globalThis.bunVM(); - this.poll_ref.unref(vm); - this.deref(); - } } if (!success) { - const err = this.onReject(); - err.ensureStillAlive(); + var err = this.onReject(); + var need_deinit = true; + defer if (need_deinit) err.deinit(); // if we are streaming update with error if (this.readable_stream_ref.get()) |readable| { if (readable.ptr == .Bytes) { readable.ptr.Bytes.onData( .{ - .err = .{ .JSValue = err }, + .err = .{ .JSValue = err.toJS(globalThis) }, }, bun.default_allocator, ); @@ -945,14 +1024,15 @@ pub const Fetch = struct { } // if we are buffering resolve the promise if (this.getCurrentResponse()) |response| { + response.body.value.toErrorInstance(err, globalThis); + need_deinit = false; // body value now owns the error const body = response.body; if (body.value == .Locked) { if (body.value.Locked.promise) |promise_| { const promise = promise_.asAnyPromise().?; - promise.reject(globalThis, err); + promise.reject(globalThis, response.body.value.Error.toJS(globalThis)); } } - response.body.value.toErrorInstance(err, globalThis); } return; } @@ -976,9 +1056,18 @@ pub const Fetch = struct { var prev = this.readable_stream_ref; this.readable_stream_ref = .{}; defer prev.deinit(); + buffer_reset = false; + this.memory_reporter.discard(scheduled_response_buffer.allocatedSlice()); + this.scheduled_response_buffer = .{ + .allocator = bun.default_allocator, + .list = .{ + .items = &.{}, + .capacity = 0, + }, + }; readable.ptr.Bytes.onData( .{ - .temporary_and_done = bun.ByteList.initConst(chunk), + .owned_and_done = bun.ByteList.initConst(chunk), }, bun.default_allocator, ); @@ -1058,10 +1147,32 @@ pub const Fetch = struct { pub fn onProgressUpdate(this: *FetchTasklet) void { JSC.markBinding(@src()); log("onProgressUpdate", .{}); - defer this.deref(); this.mutex.lock(); this.has_schedule_callback.store(false, .monotonic); + const is_done = !this.result.has_more; + + const vm = this.javascript_vm; + // vm is shutting down we cannot touch JS + if (vm.isShuttingDown()) { + this.mutex.unlock(); + if (is_done) { + this.deref(); + } + return; + } + const globalThis = this.global_this; + defer { + this.mutex.unlock(); + // if we are not done we wait until the next call + if (is_done) { + var poll_ref = this.poll_ref; + this.poll_ref = .{}; + poll_ref.unref(vm); + this.deref(); + } + } + // if we already respond the metadata and still need to process the body if (this.is_waiting_body) { this.onBodyReceived(); return; @@ -1069,33 +1180,14 @@ pub const Fetch = struct { // if we abort because of cert error // we wait the Http Client because we already have the response // we just need to deinit - const globalThis = this.global_this; - if (this.is_waiting_abort) { - // has_more will be false when the request is aborted/finished - if (this.result.has_more) { - this.mutex.unlock(); - return; - } - this.mutex.unlock(); - var poll_ref = this.poll_ref; - const vm = globalThis.bunVM(); - - poll_ref.unref(vm); - this.deref(); return; } const promise_value = this.promise.valueOrEmpty(); - var poll_ref = this.poll_ref; - const vm = globalThis.bunVM(); - if (promise_value.isEmptyOrUndefinedOrNull()) { log("onProgressUpdate: promise_value is null", .{}); this.promise.deinit(); - this.mutex.unlock(); - poll_ref.unref(vm); - this.deref(); return; } @@ -1109,29 +1201,19 @@ pub const Fetch = struct { // we need to abort the request const promise = promise_value.asAnyPromise().?; const tracker = this.tracker; - const result = this.onReject(); + var result = this.onReject(); + defer result.deinit(); - result.ensureStillAlive(); promise_value.ensureStillAlive(); - - promise.reject(globalThis, result); + promise.reject(globalThis, result.toJS(globalThis)); tracker.didDispatch(globalThis); this.promise.deinit(); - this.mutex.unlock(); - if (this.is_waiting_abort) { - return; - } - // we are already done we can deinit - poll_ref.unref(vm); - this.deref(); return; } // everything ok if (this.metadata == null) { log("onProgressUpdate: metadata is null", .{}); - // cannot continue without metadata - this.mutex.unlock(); return; } } @@ -1142,19 +1224,18 @@ pub const Fetch = struct { log("onProgressUpdate: promise_value is not null", .{}); tracker.didDispatch(globalThis); this.promise.deinit(); - this.mutex.unlock(); - if (!this.is_waiting_body) { - poll_ref.unref(vm); - this.deref(); - } } const success = this.result.isSuccess(); const result = switch (success) { - true => this.onResolve(), - false => this.onReject(), + true => JSC.Strong.create(this.onResolve(), globalThis), + false => brk: { + // in this case we wanna a JSC.Strong so we just convert it + var value = this.onReject(); + _ = value.toJS(globalThis); + break :brk value.JSValue; + }, }; - result.ensureStillAlive(); promise_value.ensureStillAlive(); const Holder = struct { @@ -1164,32 +1245,33 @@ pub const Fetch = struct { task: JSC.AnyTask, pub fn resolve(self: *@This()) void { + // cleanup + defer bun.default_allocator.destroy(self); + defer self.held.deinit(); + defer self.promise.deinit(); + // resolve the promise var prom = self.promise.swap().asAnyPromise().?; - const globalObject = self.globalObject; const res = self.held.swap(); - self.held.deinit(); - self.promise.deinit(); res.ensureStillAlive(); - - bun.default_allocator.destroy(self); - prom.resolve(globalObject, res); + prom.resolve(self.globalObject, res); } pub fn reject(self: *@This()) void { + // cleanup + defer bun.default_allocator.destroy(self); + defer self.held.deinit(); + defer self.promise.deinit(); + + // reject the promise var prom = self.promise.swap().asAnyPromise().?; - const globalObject = self.globalObject; const res = self.held.swap(); - self.held.deinit(); - self.promise.deinit(); res.ensureStillAlive(); - - bun.default_allocator.destroy(self); - prom.reject(globalObject, res); + prom.reject(self.globalObject, res); } }; var holder = bun.default_allocator.create(Holder) catch bun.outOfMemory(); holder.* = .{ - .held = JSC.Strong.create(result, globalThis), + .held = result, // we need the promise to be alive until the task is done .promise = this.promise.strong, .globalObject = globalThis, @@ -1201,7 +1283,7 @@ pub const Fetch = struct { false => JSC.AnyTask.New(Holder, Holder.reject).init(holder), }; - globalThis.bunVM().enqueueTask(JSC.Task.init(&holder.task)); + vm.enqueueTask(JSC.Task.init(&holder.task)); } pub fn checkServerIdentity(this: *FetchTasklet, certificate_info: http.CertificateInfo) bool { @@ -1219,25 +1301,30 @@ pub const Fetch = struct { const js_hostname = hostname.toJS(globalObject); js_hostname.ensureStillAlive(); js_cert.ensureStillAlive(); - const check_result = check_server_identity.callWithThis(globalObject, JSC.JSValue.jsUndefined(), &[_]JSC.JSValue{ js_hostname, js_cert }); - // if check failed abort the request + const check_result = check_server_identity.call( + globalObject, + .undefined, + &.{ js_hostname, js_cert }, + ) catch |err| globalObject.takeException(err); + + // > Returns object [...] on failure if (check_result.isAnyError()) { // mark to wait until deinit this.is_waiting_abort = this.result.has_more; - - check_result.ensureStillAlive(); - check_result.protect(); - this.abort_reason = check_result; + this.abort_reason.set(globalObject, check_result); this.signal_store.aborted.store(true, .monotonic); this.tracker.didCancel(this.global_this); // we need to abort the request - if (this.http != null) { - http.http_thread.scheduleShutdown(this.http.?); + if (this.http) |http_| { + http.http_thread.scheduleShutdown(http_); } this.result.fail = error.ERR_TLS_CERT_ALTNAME_INVALID; return false; } + + // > On success, returns + // We treat any non-error value as a success. return true; } } @@ -1246,25 +1333,37 @@ pub const Fetch = struct { return false; } - pub fn getAbortError(this: *FetchTasklet) ?JSValue { - // If this thread already received a signal we should abort - if (this.abort_reason != .zero) { - return this.abort_reason; + fn getAbortError(this: *FetchTasklet) ?Body.Value.ValueError { + if (this.abort_reason.has()) { + defer this.clearAbortSignal(); + const out = this.abort_reason; + + this.abort_reason = .{}; + return Body.Value.ValueError{ .JSValue = out }; } + if (this.signal) |signal| { - if (signal.aborted()) { - this.abort_reason = signal.abortReason(); - if (this.abort_reason.isEmptyOrUndefinedOrNull()) { - return JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.global_this); - } - this.abort_reason.protect(); - return this.abort_reason; + if (signal.reasonIfAborted(this.global_this)) |reason| { + defer this.clearAbortSignal(); + return reason.toBodyValueError(this.global_this); } } + return null; } - pub fn onReject(this: *FetchTasklet) JSValue { + fn clearAbortSignal(this: *FetchTasklet) void { + const signal = this.signal orelse return; + this.signal = null; + defer { + signal.pendingActivityUnref(); + signal.unref(); + } + + signal.cleanNativeBindings(this); + } + + pub fn onReject(this: *FetchTasklet) Body.Value.ValueError { bun.assert(this.result.fail != null); log("onReject", .{}); @@ -1272,14 +1371,8 @@ pub const Fetch = struct { return err; } - if (this.result.isTimeout()) { - // Timeout without reason - return JSC.WebCore.AbortSignal.createTimeoutError(JSC.ZigString.static("The operation timed out"), &JSC.ZigString.Empty, this.global_this); - } - - if (this.result.isAbort()) { - // Abort without reason - return JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.global_this); + if (this.result.abortReason()) |reason| { + return .{ .AbortReason = reason }; } // some times we don't have metadata so we also check http.url @@ -1374,12 +1467,12 @@ pub const Fetch = struct { .path = path, }; - return fetch_error.toErrorInstance(this.global_this); + return .{ .SystemError = fetch_error }; } - pub fn onReadableStreamAvailable(ctx: *anyopaque, readable: JSC.WebCore.ReadableStream) void { + pub fn onReadableStreamAvailable(ctx: *anyopaque, globalThis: *JSC.JSGlobalObject, readable: JSC.WebCore.ReadableStream) void { const this = bun.cast(*FetchTasklet, ctx); - this.readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(readable, this.global_this); + this.readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(readable, globalThis); } pub fn onStartStreamingRequestBodyCallback(ctx: *anyopaque) JSC.WebCore.DrainResult { @@ -1426,7 +1519,7 @@ pub const Fetch = struct { return switch (this.body_size) { .content_length => @truncate(this.body_size.content_length), .total_received => @truncate(this.body_size.total_received), - else => 0, + .unknown => 0, }; } @@ -1494,7 +1587,7 @@ pub const Fetch = struct { http_.enableBodyStreaming(); } // we should not keep the process alive if we are ignoring the body - const vm = this.global_this.bunVM(); + const vm = this.javascript_vm; this.poll_ref.unref(vm); // clean any remaining refereces this.readable_stream_ref.deinit(); @@ -1561,7 +1654,7 @@ pub const Fetch = struct { var fetch_tasklet = try allocator.create(FetchTasklet); fetch_tasklet.* = .{ - .mutex = Mutex.init(), + .mutex = .{}, .scheduled_response_buffer = .{ .allocator = fetch_options.memory_reporter.allocator(), .list = .{ @@ -1622,7 +1715,6 @@ pub const Fetch = struct { fetch_options.headers.buf.items, &fetch_tasklet.response_buffer, fetch_tasklet.request_body.slice(), - fetch_options.timeout, http.HTTPClientResult.Callback.New( *FetchTasklet, FetchTasklet.callback, @@ -1658,6 +1750,7 @@ pub const Fetch = struct { } if (fetch_tasklet.signal) |signal| { + signal.pendingActivityRef(); fetch_tasklet.signal = signal.listen(FetchTasklet, fetch_tasklet, FetchTasklet.abortListener); } return fetch_tasklet; @@ -1666,13 +1759,12 @@ pub const Fetch = struct { pub fn abortListener(this: *FetchTasklet, reason: JSValue) void { log("abortListener", .{}); reason.ensureStillAlive(); - this.abort_reason = reason; - reason.protect(); + this.abort_reason.set(this.global_this, reason); this.signal_store.aborted.store(true, .monotonic); this.tracker.didCancel(this.global_this); - if (this.http != null) { - http.http_thread.scheduleShutdown(this.http.?); + if (this.http) |http_| { + http.http_thread.scheduleShutdown(http_); } } @@ -1680,7 +1772,6 @@ pub const Fetch = struct { method: Method, headers: Headers, body: HTTPRequestBody, - timeout: usize, disable_timeout: bool, disable_keepalive: bool, disable_decompression: bool, @@ -1706,7 +1797,7 @@ pub const Fetch = struct { fetch_options: FetchOptions, promise: JSC.JSPromise.Strong, ) !*FetchTasklet { - try http.HTTPThread.init(); + http.HTTPThread.init(); var node = try get( allocator, global, @@ -1718,21 +1809,24 @@ pub const Fetch = struct { node.http.?.schedule(allocator, &batch); node.poll_ref.ref(global.bunVM()); + // increment ref so we can keep it alive until the http client is done + node.ref(); http.http_thread.schedule(batch); return node; } pub fn callback(task: *FetchTasklet, async_http: *http.AsyncHTTP, result: http.HTTPClientResult) void { - task.ref(); - task.mutex.lock(); defer task.mutex.unlock(); + const is_done = !result.has_more; + // we are done with the http client so we can deref our side + defer if (is_done) task.deref(); task.http.?.* = async_http.*; task.http.?.response_buffer = async_http.response_buffer; - log("callback success {} has_more {} bytes {}", .{ result.isSuccess(), result.has_more, result.body.?.list.items.len }); + log("callback success={} has_more={} bytes={}", .{ result.isSuccess(), result.has_more, result.body.?.list.items.len }); const prev_metadata = task.result.metadata; const prev_cert_info = task.result.certificate_info; @@ -1775,7 +1869,6 @@ pub const Fetch = struct { } if (success and result.has_more) { // we are ignoring the body so we should not receive more data, so will only signal when result.has_more = true - task.deref(); return; } } else { @@ -1788,7 +1881,6 @@ pub const Fetch = struct { if (task.has_schedule_callback.cmpxchgStrong(false, true, .acquire, .monotonic)) |has_schedule_callback| { if (has_schedule_callback) { - task.deref(); return; } } @@ -1836,10 +1928,75 @@ pub const Fetch = struct { return JSPromise.resolvedPromiseValue(globalThis, response.toJS(globalThis)); } + pub export fn Bun__fetchPreconnect( + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(JSC.conv) JSC.JSValue { + const arguments = callframe.arguments(1).slice(); + + if (arguments.len < 1) { + globalObject.throwNotEnoughArguments("fetch.preconnect", 1, arguments.len); + return .zero; + } + + var url_str = JSC.URL.hrefFromJS(arguments[0], globalObject); + defer url_str.deref(); + + if (globalObject.hasException()) { + return .zero; + } + + if (url_str.tag == .Dead) { + globalObject.ERR_INVALID_ARG_TYPE("Invalid URL", .{}).throw(); + return .zero; + } + + if (url_str.isEmpty()) { + globalObject.ERR_INVALID_ARG_TYPE(fetch_error_blank_url, .{}).throw(); + return .zero; + } + + const url = ZigURL.parse(url_str.toOwnedSlice(bun.default_allocator) catch bun.outOfMemory()); + if (!url.isHTTP() and !url.isHTTPS()) { + globalObject.throwInvalidArguments("URL must be HTTP or HTTPS", .{}); + bun.default_allocator.free(url.href); + return .zero; + } + + if (url.hostname.len == 0) { + globalObject.ERR_INVALID_ARG_TYPE(fetch_error_blank_url, .{}).throw(); + bun.default_allocator.free(url.href); + return .zero; + } + + if (!url.hasValidPort()) { + globalObject.throwInvalidArguments("Invalid port", .{}); + bun.default_allocator.free(url.href); + return .zero; + } + + bun.http.AsyncHTTP.preconnect(url, true); + return .undefined; + } + + const StringOrURL = struct { + pub fn fromJS(value: JSC.JSValue, globalThis: *JSC.JSGlobalObject) ?bun.String { + if (value.isString()) { + return bun.String.tryFromJS(value, globalThis); + } + + const out = JSC.URL.hrefFromJS(value, globalThis); + if (out.tag == .Dead) { + return null; + } + return out; + } + }; + pub export fn Bun__fetch( ctx: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { JSC.markBinding(@src()); const globalThis = ctx.ptr(); const arguments = callframe.arguments(2); @@ -1886,6 +2043,7 @@ pub const Fetch = struct { var body: AnyBlob = AnyBlob{ .Blob = .{}, }; + var disable_timeout = false; var disable_keepalive = false; var disable_decompression = false; @@ -1914,568 +2072,670 @@ pub const Fetch = struct { var check_server_identity: JSValue = .zero; defer { - if (is_error) { - unix_socket_path.deinit(); + if (signal) |sig| { + signal = null; + sig.unref(); } - } - // TODO: move this into a DRYer implementation - // The status quo is very repetitive and very bug prone - if (first_arg.as(Request)) |request| { - const can_use_fast_getters = first_arg.asDirect(Request) == request; - const slow_getters: ?JSC.JSValue = if (can_use_fast_getters) null else first_arg; - request.ensureURL() catch bun.outOfMemory(); + if (!is_error and globalThis.hasException()) { + is_error = true; + } - var url_str = request.url; - var need_to_deinit_url_str = false; - defer if (need_to_deinit_url_str) url_str.deref(); + unix_socket_path.deinit(); - if (!can_use_fast_getters) { - if (first_arg.fastGet(globalThis, .url)) |url_value| { - url_str = url_value.toBunString(globalThis); - need_to_deinit_url_str = true; - if (globalThis.hasException()) { - return .zero; - } + allocator.free(url_proxy_buffer); + url_proxy_buffer = ""; + + if (headers) |*headers_| { + headers_.buf.deinit(allocator); + headers_.entries.deinit(allocator); + headers = null; + } + + body.detach(); + + // clean hostname if any + if (hostname) |hn| { + bun.default_allocator.free(hn); + hostname = null; + } + + if (ssl_config) |conf| { + ssl_config = null; + conf.deinit(); + bun.default_allocator.destroy(conf); + } + } + + const options_object: ?JSValue = brk: { + if (args.nextEat()) |options| { + if (options.isObject() or options.jsType() == .DOMWrapper) { + break :brk options; } } - if (url_str.isEmpty()) { - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_blank_url, .{}, ctx); - // clean hostname if any - if (hostname) |host| { - allocator.free(host); - hostname = null; + break :brk null; + }; + const request: ?*Request = brk: { + if (first_arg.isCell()) { + if (first_arg.asDirect(Request)) |request_| { + break :brk request_; } - is_error = true; - return JSPromise.rejectedPromiseValue(globalThis, err); } - if (url_str.hasPrefixComptime("data:")) { - var url_slice = url_str.toUTF8WithoutRef(allocator); - defer url_slice.deinit(); + break :brk null; + }; + // If it's NOT a Request or a subclass of Request, treat the first argument as a URL. + const url_str_optional = if (first_arg.as(Request) == null) StringOrURL.fromJS(first_arg, globalThis) else null; + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + const request_init_object: ?JSValue = brk: { + if (request != null) break :brk null; + if (url_str_optional != null) break :brk null; + if (first_arg.isObject()) break :brk first_arg; + break :brk null; + }; - var data_url = DataURL.parseWithoutCheck(url_slice.slice()) catch { - const err = JSC.createError(globalThis, "failed to fetch the data URL", .{}); - return JSPromise.rejectedPromiseValue(globalThis, err); - }; + var url_str = extract_url: { + if (url_str_optional) |str| break :extract_url str; - data_url.url = url_str; - return dataURLResponse(data_url, globalThis, allocator); + if (request) |req| { + req.ensureURL() catch bun.outOfMemory(); + break :extract_url req.url.dupeRef(); } - url = ZigURL.fromString(allocator, url_str) catch { - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() URL is invalid", .{}, ctx); - // clean hostname if any - if (hostname) |host| { - allocator.free(host); - hostname = null; - } - is_error = true; - return JSPromise.rejectedPromiseValue( - globalThis, - err, - ); - }; - if (url.isFile()) { - url_type = URLType.file; - } else if (url.isBlob()) { - url_type = URLType.blob; - } - url_proxy_buffer = url.href; - if (url_type == URLType.remote) { - if (args.nextEat()) |options| { - if (options.isObject() or options.jsType() == .DOMWrapper) { - if (options.fastGetOrElse(ctx.ptr(), .method, slow_getters)) |method_| { - method = Method.fromJS(ctx, method_) orelse .GET; - } else if (can_use_fast_getters) { - method = request.method; + if (request_init_object) |request_init| { + if (request_init.fastGet(globalThis, .url)) |url_| { + if (!url_.isUndefined()) { + if (bun.String.tryFromJS(url_, globalThis)) |str| { + break :extract_url str; } + } + } + } - if (options.fastGetOrElse( - ctx.ptr(), - .body, - slow_getters, - )) |body__| { - if (Body.Value.fromJS(ctx.ptr(), body__)) |body_const| { - var body_value = body_const; - // TODO: buffer ReadableStream? - // we have to explicitly check for InternalBlob - body = body_value.useAsAnyBlob(); - } else { - // clean hostname if any - if (hostname) |host| { - allocator.free(host); - hostname = null; - } - // an error was thrown - return JSC.JSValue.jsUndefined(); - } - } else { - body = request.body.value.useAsAnyBlob(); - } + break :extract_url bun.String.empty; + }; + defer url_str.deref(); - if (options.fastGetOrElse(ctx.ptr(), .headers, slow_getters)) |headers_| { - if (headers_.as(FetchHeaders)) |headers__| { - if (headers__.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { - if (hostname) |host| { - allocator.free(host); - } - hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); - } - headers = Headers.from(headers__, allocator, .{ .body = &body }) catch bun.outOfMemory(); - // TODO: make this one pass - } else if (FetchHeaders.createFromJS(ctx.ptr(), headers_)) |headers__| { - if (headers__.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { - if (hostname) |host| { - allocator.free(host); - } - hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); - } - headers = Headers.from(headers__, allocator, .{ .body = &body }) catch bun.outOfMemory(); - headers__.deref(); - } else if (request.getFetchHeaders()) |head| { - if (head.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { - if (hostname) |host| { - allocator.free(host); - } - hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); - } - headers = Headers.from(head, allocator, .{ .body = &body }) catch bun.outOfMemory(); - } - } else if (request.getFetchHeaders()) |head| { - headers = Headers.from(head, allocator, .{ .body = &body }) catch bun.outOfMemory(); - } + if (globalThis.hasException()) { + is_error = true; + return .zero; + } - if (options.get(ctx, "timeout")) |timeout_value| { - if (timeout_value.isBoolean()) { - disable_timeout = !timeout_value.asBoolean(); - } else if (timeout_value.isNumber()) { - disable_timeout = timeout_value.to(i32) == 0; - } - } + if (url_str.isEmpty()) { + is_error = true; + const err = JSC.toTypeError(.ERR_INVALID_URL, fetch_error_blank_url, .{}, ctx); + return JSPromise.rejectedPromiseValue(globalThis, err); + } - if (options.getOptionalEnum(ctx, "redirect", FetchRedirect) catch { - return .zero; - }) |redirect_value| { - redirect_type = redirect_value; - } + if (url_str.hasPrefixComptime("data:")) { + var url_slice = url_str.toUTF8WithoutRef(allocator); + defer url_slice.deinit(); - if (options.get(ctx, "keepalive")) |keepalive_value| { - if (keepalive_value.isBoolean()) { - disable_keepalive = !keepalive_value.asBoolean(); - } else if (keepalive_value.isNumber()) { - disable_keepalive = keepalive_value.to(i32) == 0; - } - } + var data_url = DataURL.parseWithoutCheck(url_slice.slice()) catch { + const err = JSC.createError(globalThis, "failed to fetch the data URL", .{}); + is_error = true; + return JSPromise.rejectedPromiseValue(globalThis, err); + }; - if (options.get(globalThis, "verbose")) |verb| verbose: { - if (verb.isString()) { - if (verb.getZigString(globalThis).eqlComptime("curl")) { - verbose = .curl; - break :verbose; - } - } + data_url.url = url_str; + return dataURLResponse(data_url, globalThis, allocator); + } - if (verb.isBoolean()) { - verbose = if (verb.toBoolean()) .headers else .none; - } - } + url = ZigURL.fromString(allocator, url_str) catch { + const err = JSC.toTypeError(.ERR_INVALID_URL, "fetch() URL is invalid", .{}, ctx); + is_error = true; + return JSPromise.rejectedPromiseValue( + globalThis, + err, + ); + }; + if (url.isFile()) { + url_type = URLType.file; + } else if (url.isBlob()) { + url_type = URLType.blob; + } + url_proxy_buffer = url.href; - if (options.get(globalThis, "signal")) |signal_arg| { - if (signal_arg.as(JSC.WebCore.AbortSignal)) |signal_| { - _ = signal_.ref(); - signal = signal_; - } - } + if (url_str.hasPrefixComptime("data:")) { + var url_slice = url_str.toUTF8WithoutRef(allocator); + defer url_slice.deinit(); - if (options.get(ctx, "decompress")) |decompress| { - if (decompress.isBoolean()) { - disable_decompression = !decompress.asBoolean(); - } else if (decompress.isNumber()) { - disable_decompression = decompress.to(i32) == 0; - } - } + var data_url = DataURL.parseWithoutCheck(url_slice.slice()) catch { + const err = JSC.createError(globalThis, "failed to fetch the data URL", .{}); + return JSPromise.rejectedPromiseValue(globalThis, err); + }; + data_url.url = url_str; - if (options.get(ctx, "tls")) |tls| { - if (!tls.isEmptyOrUndefinedOrNull() and tls.isObject()) { - if (SSLConfig.inJS(vm, globalThis, tls, exception)) |config| { - if (ssl_config) |existing_conf| { - existing_conf.deinit(); - bun.default_allocator.destroy(existing_conf); - ssl_config = null; - } - ssl_config = bun.default_allocator.create(SSLConfig) catch bun.outOfMemory(); - ssl_config.?.* = config; - } - if (tls.get(ctx, "rejectUnauthorized")) |reject| { - if (reject.isBoolean()) { - reject_unauthorized = reject.asBoolean(); - } else if (reject.isNumber()) { - reject_unauthorized = reject.to(i32) != 0; - } - } - if (tls.get(ctx, "checkServerIdentity")) |checkServerIdentity| { - if (checkServerIdentity.isCell() and checkServerIdentity.isCallable(globalThis.vm())) { - check_server_identity = checkServerIdentity; - } - } - } - } + return dataURLResponse(data_url, globalThis, allocator); + } - if (options.get(globalThis, "proxy")) |proxy_arg| { - if (proxy_arg.isString() and proxy_arg.getLength(ctx) > 0) { - var href = JSC.URL.hrefFromJS(proxy_arg, globalThis); - if (href.tag == .Dead) { - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() proxy URL is invalid", .{}, ctx); - // clean hostname and tls props if any - if (ssl_config) |conf| { - conf.deinit(); - bun.default_allocator.destroy(conf); - } - if (hostname) |hn| { - bun.default_allocator.free(hn); - hostname = null; - } - allocator.free(url_proxy_buffer); - - return JSPromise.rejectedPromiseValue(globalThis, err); - } - defer href.deref(); - var buffer = std.fmt.allocPrint(allocator, "{s}{}", .{ url_proxy_buffer, href }) catch { - globalThis.throwOutOfMemory(); - return .zero; - }; - url = ZigURL.parse(buffer[0..url.href.len]); - if (url.isFile()) { - url_type = URLType.file; - } else if (url.isBlob()) { - url_type = URLType.blob; - } + // **Start with the harmless ones.** - proxy = ZigURL.parse(buffer[url.href.len..]); - allocator.free(url_proxy_buffer); - url_proxy_buffer = buffer; - } - } + // "method" + method = extract_method: { + if (options_object) |options| { + if (options.getTruthyComptime(globalThis, "method")) |method_| { + break :extract_method Method.fromJS(globalThis, method_); + } - if (options.get(globalThis, "unix")) |socket_path| { - if (socket_path.isString() and socket_path.getLength(ctx) > 0) { - if (socket_path.toSliceCloneWithAllocator(globalThis, allocator)) |slice| { - unix_socket_path = slice; - } - } + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } + + if (request) |req| { + break :extract_method req.method; + } + + if (request_init_object) |req| { + if (req.getTruthyComptime(globalThis, "method")) |method_| { + break :extract_method Method.fromJS(globalThis, method_); + } + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } + + break :extract_method null; + } orelse .GET; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // "decompression: boolean" + disable_decompression = extract_disable_decompression: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; + + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "decompress")) |decompression_value| { + if (decompression_value.isBoolean()) { + break :extract_disable_decompression !decompression_value.asBoolean(); + } else if (decompression_value.isNumber()) { + break :extract_disable_decompression decompression_value.to(i32) == 0; } } - } else { - if (can_use_fast_getters) { - method = request.method; - } else if (first_arg.fastGet(globalThis, .method)) |method_value| { - method = Method.fromJS(globalThis, method_value) orelse .GET; + + if (globalThis.hasException()) { + is_error = true; + return .zero; } + } + } + + break :extract_disable_decompression disable_decompression; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } - if (request.body.value == .Locked) { - if (request.body.value.Locked.readable.get()) |stream| { - if (stream.isDisturbed(globalThis)) { - globalThis.throw("ReadableStream has already been consumed", .{}); - if (hostname) |host| { - allocator.free(host); - hostname = null; + // "tls: TLSConfig" + ssl_config = extract_ssl_config: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; + + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "tls")) |tls| { + if (tls.isObject()) { + if (tls.get(ctx, "rejectUnauthorized")) |reject| { + if (reject.isBoolean()) { + reject_unauthorized = reject.asBoolean(); + } else if (reject.isNumber()) { + reject_unauthorized = reject.to(i32) != 0; } + } + + if (globalThis.hasException()) { + is_error = true; return .zero; } - } - } - // Support headers getter on subclass - // - // class MyRequest extends Request { - // get headers() { - // return {a: "1"}; - // } - // } - // - // fetch(request) - var fetch_headers_to_deref: ?*JSC.FetchHeaders = null; - defer { - if (fetch_headers_to_deref) |fetch_headers| { - fetch_headers.deref(); - } - } + if (tls.get(ctx, "checkServerIdentity")) |checkServerIdentity| { + if (checkServerIdentity.isCell() and checkServerIdentity.isCallable(globalThis.vm())) { + check_server_identity = checkServerIdentity; + } + } - if (get_fetch_headers: { - if (can_use_fast_getters) - break :get_fetch_headers request.getFetchHeaders(); + if (globalThis.hasException()) { + is_error = true; + return .zero; + } - if (first_arg.fastGet(globalThis, .headers)) |headers_value| { - // Faster path: existing FetchHeaders object: - if (FetchHeaders.cast(headers_value)) |fetch_headers| { - break :get_fetch_headers fetch_headers; + if (SSLConfig.inJS(vm, globalThis, tls, exception)) |config| { + if (exception.* != null) { + is_error = true; + return .zero; + } + + const ssl_config_object = bun.default_allocator.create(SSLConfig) catch bun.outOfMemory(); + ssl_config_object.* = config; + break :extract_ssl_config ssl_config_object; } - // Slow path: create a new FetchHeaders: - if (FetchHeaders.createFromJS(globalThis, headers_value)) |fetch_headers| { - fetch_headers_to_deref = fetch_headers; - break :get_fetch_headers fetch_headers; + if (exception.* != null) { + is_error = true; + return .zero; } } + } + } + } + + break :extract_ssl_config ssl_config; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // unix: string | undefined + unix_socket_path = extract_unix_socket_path: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; - break :get_fetch_headers null; - }) |head| { - if (head.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { - if (hostname) |host| { - allocator.free(host); + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "unix")) |socket_path| { + if (socket_path.isString() and socket_path.getLength(ctx) > 0) { + if (socket_path.toSliceCloneWithAllocator(globalThis, allocator)) |slice| { + break :extract_unix_socket_path slice; } - hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); } - headers = Headers.from(head, allocator, .{ .body = &body }) catch bun.outOfMemory(); } - // Creating headers can throw. if (globalThis.hasException()) { - if (hostname) |host| { - allocator.free(host); - hostname = null; - } + is_error = true; return .zero; } + } + } + break :extract_unix_socket_path unix_socket_path; + }; - // TODO: remove second isDisturbed check in useAsAnyBlob - body = request.body.value.useAsAnyBlob(); + if (globalThis.hasException()) { + is_error = true; + return .zero; + } - // Assume that useAsAnyBlob() has already thrown an error if it was going to. - if (globalThis.hasException()) { - if (hostname) |host| { - allocator.free(host); - hostname = null; + // timeout: false | number | undefined + disable_timeout = extract_disable_timeout: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; + + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "timeout")) |timeout_value| { + if (timeout_value.isBoolean()) { + break :extract_disable_timeout !timeout_value.asBoolean(); + } else if (timeout_value.isNumber()) { + break :extract_disable_timeout timeout_value.to(i32) == 0; } - return .zero; } - if (request.signal) |signal_| { - _ = signal_.ref(); - signal = signal_; + if (globalThis.hasException()) { + is_error = true; + return .zero; } } } - } else if (bun.String.tryFromJS(first_arg, globalThis)) |str| { - defer str.deref(); - if (str.isEmpty()) { - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_blank_url, .{}, ctx); - // clean hostname if any - if (hostname) |hn| { - bun.default_allocator.free(hn); - hostname = null; + + break :extract_disable_timeout disable_timeout; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // redirect: "follow" | "error" | "manual" | undefined; + redirect_type = extract_redirect_type: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; + + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].getOptionalEnum(globalThis, "redirect", FetchRedirect) catch { + is_error = true; + return .zero; + }) |redirect_value| { + break :extract_redirect_type redirect_value; + } } - return JSPromise.rejectedPromiseValue(globalThis, err); } - if (str.hasPrefixComptime("data:")) { - var url_slice = str.toUTF8WithoutRef(allocator); - defer url_slice.deinit(); + break :extract_redirect_type redirect_type; + }; - var data_url = DataURL.parseWithoutCheck(url_slice.slice()) catch { - const err = JSC.createError(globalThis, "failed to fetch the data URL", .{}); - return JSPromise.rejectedPromiseValue(globalThis, err); - }; - data_url.url = str; + if (globalThis.hasException()) { + is_error = true; + return .zero; + } - return dataURLResponse(data_url, globalThis, allocator); - } + // keepalive: boolean | undefined; + disable_keepalive = extract_disable_keepalive: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; - url = ZigURL.fromString(allocator, str) catch { - // clean hostname if any - if (hostname) |hn| { - bun.default_allocator.free(hn); - hostname = null; + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "keepalive")) |keepalive_value| { + if (keepalive_value.isBoolean()) { + break :extract_disable_keepalive !keepalive_value.asBoolean(); + } else if (keepalive_value.isNumber()) { + break :extract_disable_keepalive keepalive_value.to(i32) == 0; + } + } + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } } - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() URL is invalid", .{}, ctx); - return JSPromise.rejectedPromiseValue(globalThis, err); + } + + break :extract_disable_keepalive disable_keepalive; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // verbose: boolean | "curl" | undefined; + verbose = extract_verbose: { + const objects_to_try = [_]JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, }; - url_proxy_buffer = url.href; - if (url.isFile()) { - url_type = URLType.file; - } else if (url.isBlob()) { - url_type = URLType.blob; - } - - if (url_type == URLType.remote) { - if (args.nextEat()) |options| { - if (options.isObject() or options.jsType() == .DOMWrapper) { - if (options.fastGet(ctx.ptr(), .method)) |method_| { - method = Method.fromJS(ctx, method_) orelse .GET; - if (globalThis.hasException()) { - return .zero; - } - } - if (options.fastGet(ctx.ptr(), .body)) |body__| { - if (Body.Value.fromJS(ctx.ptr(), body__)) |body_const| { - var body_value = body_const; - // TODO: buffer ReadableStream? - // we have to explicitly check for InternalBlob - body = body_value.useAsAnyBlob(); - } else { - // clean hostname if any - if (hostname) |host| { - allocator.free(host); - hostname = null; - } - // an error was thrown - return JSC.JSValue.jsUndefined(); + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "verbose")) |verb| { + if (verb.isString()) { + if (verb.getZigString(globalThis).eqlComptime("curl")) { + break :extract_verbose .curl; } + } else if (verb.isBoolean()) { + break :extract_verbose if (verb.toBoolean()) .headers else .none; } + } - if (options.fastGet(ctx.ptr(), .headers)) |headers_| { - if (headers_.as(FetchHeaders)) |headers__| { - if (headers__.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { - if (hostname) |host| { - allocator.free(host); - } - hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); - } - headers = Headers.from(headers__, allocator, .{ .body = &body }) catch bun.outOfMemory(); - // TODO: make this one pass - } else if (FetchHeaders.createFromJS(ctx.ptr(), headers_)) |headers__| { - defer headers__.deref(); - if (headers__.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { - if (hostname) |host| { - allocator.free(host); - } - hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); - } - headers = Headers.from(headers__, allocator, .{ .body = &body }) catch bun.outOfMemory(); - } else { - // Converting the headers failed; return null and - // let the set exception get thrown + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } + } + break :extract_verbose verbose; + }; + + // proxy: string | undefined; + url_proxy_buffer = extract_proxy: { + const objects_to_try = [_]JSC.JSValue{ + options_object orelse .zero, + request_init_object orelse .zero, + }; + inline for (0..2) |i| { + if (objects_to_try[i] != .zero) { + if (objects_to_try[i].get(globalThis, "proxy")) |proxy_arg| { + if (proxy_arg.isString() and proxy_arg.getLength(ctx) > 0) { + var href = JSC.URL.hrefFromJS(proxy_arg, globalThis); + if (href.tag == .Dead) { + const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() proxy URL is invalid", .{}, ctx); + is_error = true; + return JSPromise.rejectedPromiseValue(globalThis, err); + } + defer href.deref(); + const buffer = std.fmt.allocPrint(allocator, "{s}{}", .{ url_proxy_buffer, href }) catch { + globalThis.throwOutOfMemory(); return .zero; + }; + url = ZigURL.parse(buffer[0..url.href.len]); + if (url.isFile()) { + url_type = URLType.file; + } else if (url.isBlob()) { + url_type = URLType.blob; } + + proxy = ZigURL.parse(buffer[url.href.len..]); + allocator.free(url_proxy_buffer); + break :extract_proxy buffer; } + } - if (options.get(ctx, "timeout")) |timeout_value| { - if (timeout_value.isBoolean()) { - disable_timeout = !timeout_value.asBoolean(); - } else if (timeout_value.isNumber()) { - disable_timeout = timeout_value.to(i32) == 0; - } + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } + } + + break :extract_proxy url_proxy_buffer; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // signal: AbortSignal | undefined; + signal = extract_signal: { + if (options_object) |options| { + if (options.get(globalThis, "signal")) |signal_| { + if (!signal_.isUndefined()) { + if (signal_.as(JSC.WebCore.AbortSignal)) |signal__| { + break :extract_signal signal__.ref(); } + } + } - if (options.getOptionalEnum(ctx, "redirect", FetchRedirect) catch { - return .zero; - }) |redirect_value| { - redirect_type = redirect_value; + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } + + if (request) |req| { + if (req.signal) |signal_| { + break :extract_signal signal_.ref(); + } + break :extract_signal null; + } + + if (request_init_object) |options| { + if (options.get(globalThis, "signal")) |signal_| { + if (signal_.isUndefined()) { + break :extract_signal null; + } + + if (signal_.as(JSC.WebCore.AbortSignal)) |signal__| { + break :extract_signal signal__.ref(); + } + } + } + + break :extract_signal null; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // We do this 2nd to last instead of last so that if it's a FormData + // object, we can still insert the boundary. + // + // body: BodyInit | null | undefined; + // + body = extract_body: { + if (options_object) |options| { + if (options.fastGet(globalThis, .body)) |body__| { + if (!body__.isUndefined()) { + if (Body.Value.fromJS(ctx.ptr(), body__)) |body_const| { + var body_value = body_const; + break :extract_body body_value.useAsAnyBlob(); } + } + } - if (options.get(ctx, "keepalive")) |keepalive_value| { - if (keepalive_value.isBoolean()) { - disable_keepalive = !keepalive_value.asBoolean(); - } else if (keepalive_value.isNumber()) { - disable_keepalive = keepalive_value.to(i32) == 0; - } + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } + + if (request) |req| { + if (req.body.value == .Used or (req.body.value == .Locked and (req.body.value.Locked.action != .none or req.body.value.Locked.isDisturbed(Request, globalThis, first_arg)))) { + globalThis.ERR_BODY_ALREADY_USED("Request body already used", .{}).throw(); + is_error = true; + return .zero; + } + + break :extract_body req.body.value.useAsAnyBlob(); + } + + if (request_init_object) |req| { + if (req.fastGet(globalThis, .body)) |body__| { + if (!body__.isUndefined()) { + if (Body.Value.fromJS(ctx.ptr(), body__)) |body_const| { + var body_value = body_const; + break :extract_body body_value.useAsAnyBlob(); } + } + } + } + + break :extract_body null; + } orelse AnyBlob{ .Blob = .{} }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + // headers: Headers | undefined; + headers = extract_headers: { + var fetch_headers_to_deref: ?*JSC.FetchHeaders = null; + defer { + if (fetch_headers_to_deref) |fetch_headers| { + fetch_headers.deref(); + } + } - if (options.get(globalThis, "verbose")) |verb| verbose: { - if (verb.isString()) { - if (verb.getZigString(globalThis).eqlComptime("curl")) { - verbose = .curl; - break :verbose; + const fetch_headers: ?*JSC.FetchHeaders = brk: { + if (options_object) |options| { + if (options.fastGet(globalThis, .headers)) |headers_value| { + if (!headers_value.isUndefined()) { + if (headers_value.as(FetchHeaders)) |headers__| { + if (headers__.isEmpty()) { + break :brk null; } - } - if (verb.isBoolean()) { - verbose = if (verb.toBoolean()) .headers else .none; + break :brk headers__; } - } - if (options.get(globalThis, "signal")) |signal_arg| { - if (signal_arg.as(JSC.WebCore.AbortSignal)) |signal_| { - _ = signal_.ref(); - signal = signal_; + if (FetchHeaders.createFromJS(ctx.ptr(), headers_value)) |headers__| { + fetch_headers_to_deref = headers__; + break :brk headers__; } - } - if (options.get(ctx, "decompress")) |decompress| { - if (decompress.isBoolean()) { - disable_decompression = !decompress.asBoolean(); - } else if (decompress.isNumber()) { - disable_decompression = decompress.to(i32) == 0; - } + break :brk null; } + } - if (options.get(ctx, "tls")) |tls| { - if (!tls.isEmptyOrUndefinedOrNull() and tls.isObject()) { - if (SSLConfig.inJS(vm, globalThis, tls, exception)) |config| { - ssl_config = bun.default_allocator.create(SSLConfig) catch bun.outOfMemory(); - ssl_config.?.* = config; - } - if (tls.get(ctx, "rejectUnauthorized")) |reject| { - if (reject.isBoolean()) { - reject_unauthorized = reject.asBoolean(); - } else if (reject.isNumber()) { - reject_unauthorized = reject.to(i32) != 0; - } - } + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + } - if (tls.get(ctx, "checkServerIdentity")) |checkServerIdentity| { - if (checkServerIdentity.isCell() and checkServerIdentity.isCallable(globalThis.vm())) { - check_server_identity = checkServerIdentity; - } - } - } - } + if (request) |req| { + if (req.getFetchHeadersUnlessEmpty()) |head| { + break :brk head; + } - if (options.getTruthy(globalThis, "proxy")) |proxy_arg| { - if (proxy_arg.isString() and proxy_arg.getLength(globalThis) > 0) { - var href = JSC.URL.hrefFromJS(proxy_arg, globalThis); - if (href.tag == .Dead) { - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() proxy URL is invalid", .{}, ctx); - // clean hostname if any - if (hostname) |host| { - allocator.free(host); - hostname = null; - } - - if (ssl_config) |conf| { - conf.deinit(); - bun.default_allocator.destroy(conf); - } - allocator.free(url_proxy_buffer); - is_error = true; - return JSPromise.rejectedPromiseValue(globalThis, err); + break :brk null; + } + + if (request_init_object) |options| { + if (options.fastGet(globalThis, .headers)) |headers_value| { + if (!headers_value.isUndefined()) { + if (headers_value.as(FetchHeaders)) |headers__| { + if (headers__.isEmpty()) { + break :brk null; } - defer href.deref(); - var buffer = std.fmt.allocPrint(allocator, "{s}{}", .{ url_proxy_buffer, href }) catch { - globalThis.throwOutOfMemory(); - return .zero; - }; - url = ZigURL.parse(buffer[0..url.href.len]); - proxy = ZigURL.parse(buffer[url.href.len..]); - allocator.free(url_proxy_buffer); - url_proxy_buffer = buffer; + + break :brk headers__; } - } - if (options.get(globalThis, "unix")) |socket_path| { - if (socket_path.isString() and socket_path.getLength(ctx) > 0) { - if (socket_path.toSliceCloneWithAllocator(globalThis, allocator)) |slice| { - unix_socket_path = slice; - } + if (FetchHeaders.createFromJS(ctx.ptr(), headers_value)) |headers__| { + fetch_headers_to_deref = headers__; + break :brk headers__; } + + break :brk null; } } } + + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + + break :extract_headers headers; + }; + + if (globalThis.hasException()) { + is_error = true; + return .zero; } - } else { - const fetch_error = fetch_type_error_strings.get(js.JSValueGetType(ctx, first_arg.asRef())); - const err = JSC.toTypeError(.ERR_INVALID_ARG_TYPE, "{s}", .{fetch_error}, ctx); - exception.* = err.asObjectRef(); - return .zero; - } - if (url.isEmpty()) { + if (fetch_headers) |headers_| { + if (headers_.fastGet(JSC.FetchHeaders.HTTPHeaderName.Host)) |_hostname| { + if (hostname) |host| { + hostname = null; + allocator.free(host); + } + hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); + } + + break :extract_headers Headers.from(headers_, allocator, .{ .body = &body }) catch bun.outOfMemory(); + } + + break :extract_headers headers; + }; + + if (globalThis.hasException()) { is_error = true; - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_blank_url, .{}, ctx); - return JSPromise.rejectedPromiseValue(globalThis, err); + return .zero; } if (proxy != null and unix_socket_path.length() > 0) { @@ -2484,11 +2744,15 @@ pub const Fetch = struct { return JSPromise.rejectedPromiseValue(globalThis, err); } + if (globalThis.hasException()) { + is_error = true; + return .zero; + } + // This is not 100% correct. // We don't pass along headers, we ignore method, we ignore status code... // But it's better than status quo. if (url_type != .remote) { - defer allocator.free(url_proxy_buffer); defer unix_socket_path.deinit(); var path_buf: bun.PathBuffer = undefined; const PercentEncoding = @import("../../url.zig").PercentEncoding; @@ -2598,7 +2862,6 @@ pub const Fetch = struct { if (url.protocol.len > 0) { if (!(url.isHTTP() or url.isHTTPS())) { - defer allocator.free(url_proxy_buffer); const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "protocol must be http: or https:", .{}, ctx); is_error = true; return JSPromise.rejectedPromiseValue(globalThis, err); @@ -2606,7 +2869,6 @@ pub const Fetch = struct { } if (!method.hasRequestBody() and body.size() > 0) { - defer allocator.free(url_proxy_buffer); const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_unexpected_body, .{}, ctx); is_error = true; return JSPromise.rejectedPromiseValue(globalThis, err); @@ -2633,14 +2895,7 @@ pub const Fetch = struct { const opened_fd = switch (opened_fd_res) { .err => |err| { - allocator.free(url_proxy_buffer); - const rejected_value = JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)); - body.detach(); - if (headers) |*headers_| { - headers_.buf.deinit(allocator); - headers_.entries.deinit(allocator); - } is_error = true; return rejected_value; }, @@ -2711,14 +2966,9 @@ pub const Fetch = struct { switch (res) { .err => |err| { - allocator.free(url_proxy_buffer); is_error = true; const rejected_value = JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)); body.detach(); - if (headers) |*headers_| { - headers_.buf.deinit(allocator); - headers_.entries.deinit(allocator); - } return rejected_value; }, @@ -2737,6 +2987,16 @@ pub const Fetch = struct { const promise_val = promise.value(); + const initial_body_reference_count: if (Environment.isDebug) usize else u0 = brk: { + if (Environment.isDebug) { + if (body.store()) |store| { + break :brk store.ref_count.load(.monotonic); + } + } + + break :brk 0; + }; + _ = FetchTasklet.queue( allocator, globalThis, @@ -2747,7 +3007,6 @@ pub const Fetch = struct { .allocator = allocator, }, .body = http_body, - .timeout = std.time.ns_per_hour, .disable_keepalive = disable_keepalive, .disable_timeout = disable_timeout, .disable_decompression = disable_decompression, @@ -2769,6 +3028,34 @@ pub const Fetch = struct { // see https://github.com/oven-sh/bun/issues/2985 promise, ) catch bun.outOfMemory(); + + if (Environment.isDebug) { + if (body.store()) |store| { + if (store.ref_count.load(.monotonic) == initial_body_reference_count) { + Output.panic("Expected body ref count to have incremented in FetchTasklet", .{}); + } + } + } + + // These are now owned by FetchTasklet. + url = .{}; + headers = null; + // Reference count for the blob is incremented above. + if (body.store() != null) { + body.detach(); + } else { + // These are single-use, and have effectively been moved to the FetchTasklet. + body = .{ + .Blob = .{}, + }; + } + proxy = null; + url_proxy_buffer = ""; + signal = null; + ssl_config = null; + hostname = null; + unix_socket_path = ZigString.Slice.empty; + return promise_val; } }; @@ -2780,6 +3067,11 @@ pub const Headers = struct { buf: std.ArrayListUnmanaged(u8) = .{}, allocator: std.mem.Allocator, + pub fn deinit(this: *Headers) void { + this.entries.deinit(this.allocator); + this.buf.clearAndFree(this.allocator); + } + pub fn asStr(this: *const Headers, ptr: Api.StringPointer) []const u8 { return if (ptr.offset + ptr.length <= this.buf.items.len) this.buf.items[ptr.offset..][0..ptr.length] diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 6d0486a5c3325..9b978eea4a685 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -57,6 +57,18 @@ pub const ReadableStream = struct { return this.held.globalThis; } + pub fn has(this: *Strong) bool { + return this.held.has(); + } + + pub fn isDisturbed(this: *const Strong, global: *JSC.JSGlobalObject) bool { + if (this.get()) |stream| { + return stream.isDisturbed(global); + } + + return false; + } + pub fn init(this: ReadableStream, global: *JSGlobalObject) Strong { return .{ .held = JSC.Strong.create(this.value, global), @@ -76,8 +88,29 @@ pub const ReadableStream = struct { // } this.held.deinit(); } + + pub fn tee(this: *Strong, global: *JSGlobalObject) ?ReadableStream { + if (this.get()) |stream| { + const first, const second = stream.tee(global) orelse return null; + this.held.set(global, first.value); + return second; + } + return null; + } }; + extern fn ReadableStream__tee(stream: JSValue, globalThis: *JSGlobalObject, out1: *JSC.JSValue, out2: *JSC.JSValue) bool; + pub fn tee(this: *const ReadableStream, globalThis: *JSGlobalObject) ?struct { ReadableStream, ReadableStream } { + var out1: JSC.JSValue = .zero; + var out2: JSC.JSValue = .zero; + if (!ReadableStream__tee(this.value, globalThis, &out1, &out2)) { + return null; + } + const out_stream2 = ReadableStream.fromJS(out2, globalThis) orelse return null; + const out_stream1 = ReadableStream.fromJS(out1, globalThis) orelse return null; + return .{ out_stream1, out_stream2 }; + } + pub fn toJS(this: *const ReadableStream) JSValue { return this.value; } @@ -102,13 +135,10 @@ pub const ReadableStream = struct { switch (stream.ptr) { .Blob => |blobby| { - var blob = JSC.WebCore.Blob.initWithStore(blobby.store orelse return null, globalThis); - blob.offset = blobby.offset; - blob.size = blobby.remain; - blob.store.?.ref(); - stream.done(globalThis); - - return AnyBlob{ .Blob = blob }; + if (blobby.toAnyBlob(globalThis)) |blob| { + stream.done(globalThis); + return blob; + } }, .File => |blobby| { if (blobby.lazy == .blob) { @@ -124,11 +154,7 @@ pub const ReadableStream = struct { // If we've received the complete body by the time this function is called // we can avoid streaming it and convert it to a Blob - if (bytes.has_received_last_chunk) { - var blob: JSC.WebCore.AnyBlob = undefined; - blob.from(bytes.buffer); - bytes.buffer.items = &.{}; - bytes.buffer.capacity = 0; + if (bytes.toAnyBlob()) |blob| { stream.done(globalThis); return blob; } @@ -481,14 +507,14 @@ pub const StreamStart = union(Tag) { pub fn toJS(this: StreamStart, globalThis: *JSGlobalObject) JSC.JSValue { switch (this) { .empty, .ready => { - return JSC.JSValue.jsUndefined(); + return .undefined; }, .chunk_size => |chunk| { return JSC.JSValue.jsNumber(@as(Blob.SizeType, @intCast(chunk))); }, .err => |err| { globalThis.vm().throwError(globalThis, err.toJSC(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; }, .owned_and_done => |list| { return JSC.ArrayBuffer.fromBytes(list.slice(), .Uint8Array).toJS(globalThis, null); @@ -497,7 +523,7 @@ pub const StreamStart = union(Tag) { return JSC.ArrayBuffer.create(globalThis, list.slice(), .Uint8Array); }, else => { - return JSC.JSValue.jsUndefined(); + return .undefined; }, } } @@ -660,7 +686,7 @@ pub const DrainResult = union(enum) { pub const StreamResult = union(Tag) { pending: *Pending, - err: union(Err) { Error: Syscall.Error, JSValue: JSC.JSValue }, + err: StreamError, done: void, owned: bun.ByteList, owned_and_done: bun.ByteList, @@ -673,13 +699,41 @@ pub const StreamResult = union(Tag) { switch (this.*) { .owned => |*owned| owned.deinitWithAllocator(bun.default_allocator), .owned_and_done => |*owned_and_done| owned_and_done.deinitWithAllocator(bun.default_allocator), + .err => |err| { + if (err == .JSValue) { + err.JSValue.unprotect(); + } + }, else => {}, } } - pub const Err = enum { - Error, - JSValue, + pub const StreamError = union(enum) { + Error: Syscall.Error, + AbortReason: JSC.CommonAbortReason, + + // TODO: use an explicit JSC.Strong here. + JSValue: JSC.JSValue, + WeakJSValue: JSC.JSValue, + + const WasStrong = enum { + Strong, + Weak, + }; + + pub fn toJSWeak(this: *const @This(), globalObject: *JSC.JSGlobalObject) struct { JSC.JSValue, WasStrong } { + return switch (this.*) { + .Error => |err| { + return .{ err.toJSC(globalObject), WasStrong.Weak }; + }, + .JSValue => .{ this.JSValue, WasStrong.Strong }, + .WeakJSValue => .{ this.WeakJSValue, WasStrong.Weak }, + .AbortReason => |reason| { + const value = reason.toJS(globalObject); + return .{ value, WasStrong.Weak }; + }, + }; + } }; pub const Tag = enum { @@ -739,7 +793,7 @@ pub const StreamResult = union(Tag) { pub fn deinit(this: *@This()) void { if (this.* == .promise) { - this.promise.strong.deinit(); + this.promise.deinit(); this.* = .{ .none = {} }; } } @@ -938,13 +992,12 @@ pub const StreamResult = union(Tag) { defer loop.exit(); switch (result.*) { - .err => |err| { + .err => |*err| { const value = brk: { - if (err == .Error) break :brk err.Error.toJSC(globalThis); - - const js_err = err.JSValue; + const js_err, const was_strong = err.toJSWeak(globalThis); js_err.ensureStillAlive(); - js_err.unprotect(); + if (was_strong == .Strong) + js_err.unprotect(); break :brk js_err; }; @@ -956,6 +1009,8 @@ pub const StreamResult = union(Tag) { }, else => { const value = result.toJS(globalThis); + value.ensureStillAlive(); + result.* = .{ .temporary = .{} }; promise.resolve(globalThis, value); }, @@ -1003,12 +1058,11 @@ pub const StreamResult = union(Tag) { }, .err => |err| { - if (err == .Error) { - return JSC.JSPromise.rejectedPromise(globalThis, JSValue.c(err.Error.toJS(globalThis))).asValue(globalThis); + const js_err, const was_strong = err.toJSWeak(globalThis); + if (was_strong == .Strong) { + js_err.unprotect(); } - const js_err = err.JSValue; js_err.ensureStillAlive(); - js_err.unprotect(); return JSC.JSPromise.rejectedPromise(globalThis, js_err).asValue(globalThis); }, @@ -1621,7 +1675,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return shim.cppFn("setDestroyCallback", .{ value, callback }); } - pub fn construct(globalThis: *JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn construct(globalThis: *JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); if (comptime !@hasDecl(SinkType, "construct")) { @@ -1630,10 +1684,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { }; const err = JSC.SystemError{ .message = bun.String.static(Static.message), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_ILLEGAL_CONSTRUCTOR))), + .code = bun.String.static(@tagName(.ERR_ILLEGAL_CONSTRUCTOR)), }; globalThis.throwValue(err.toErrorInstance(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; } var allocator = globalThis.bunVM().allocator; @@ -1641,7 +1695,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { globalThis.vm().throwError(globalThis, Syscall.Error.oom.toJSC( globalThis, )); - return JSC.JSValue.jsUndefined(); + return .undefined; }; this.sink.construct(allocator); return createObject(globalThis, this, 0); @@ -1670,7 +1724,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { shim.cppFn("detachPtr", .{ptr}); } - fn getThis(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) ?*ThisSink { + inline fn getThis(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) ?*ThisSink { return @as( *ThisSink, @ptrCast(@alignCast( @@ -1683,9 +1737,9 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { } fn invalidThis(globalThis: *JSGlobalObject) JSValue { - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + const err = JSC.toTypeError(.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } pub fn unprotect(this: *@This()) void { @@ -1693,14 +1747,14 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { } - pub fn write(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn write(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis); if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } @@ -1709,12 +1763,12 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (args.len == 0) { globalThis.vm().throwError(globalThis, JSC.toTypeError( - JSC.Node.ErrorCode.ERR_MISSING_ARGS, + .ERR_MISSING_ARGS, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis, )); - return JSC.JSValue.jsUndefined(); + return .undefined; } const arg = args[0]; @@ -1723,12 +1777,12 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (arg.isEmptyOrUndefinedOrNull()) { globalThis.vm().throwError(globalThis, JSC.toTypeError( - JSC.Node.ErrorCode.ERR_STREAM_NULL_VALUES, + .ERR_STREAM_NULL_VALUES, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis, )); - return JSC.JSValue.jsUndefined(); + return .undefined; } if (arg.asArrayBuffer(globalThis)) |buffer| { @@ -1742,12 +1796,12 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (!arg.isString()) { globalThis.vm().throwError(globalThis, JSC.toTypeError( - JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + .ERR_INVALID_ARG_TYPE, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis, )); - return JSC.JSValue.jsUndefined(); + return .undefined; } const str = arg.getZigString(globalThis); @@ -1762,7 +1816,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return this.sink.writeLatin1(.{ .temporary = bun.ByteList.initConst(str.slice()) }).toJS(globalThis); } - pub fn writeUTF8(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn writeUTF8(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis); @@ -1770,7 +1824,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } @@ -1778,13 +1832,13 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { const args = args_list.ptr[0..args_list.len]; if (args.len == 0 or !args[0].isString()) { const err = JSC.toTypeError( - if (args.len == 0) JSC.Node.ErrorCode.ERR_MISSING_ARGS else JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + if (args.len == 0) .ERR_MISSING_ARGS else .ERR_INVALID_ARG_TYPE, "writeUTF8() expects a string", .{}, globalThis, ); globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } const arg = args[0]; @@ -1808,14 +1862,14 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } return this.sink.end(null).toJS(globalThis); } - pub fn flush(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn flush(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis); @@ -1823,7 +1877,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } @@ -1842,7 +1896,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { .result => |value| value, .err => |err| blk: { globalThis.vm().throwError(globalThis, err.toJSC(globalThis)); - break :blk JSC.JSValue.jsUndefined(); + break :blk .undefined; }, }; } @@ -1850,7 +1904,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return this.sink.flush().toJS(globalThis); } - pub fn start(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn start(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis); @@ -1858,7 +1912,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } @@ -1883,7 +1937,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { ).toJS(globalThis); } - pub fn end(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn end(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis); @@ -1891,7 +1945,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } @@ -1906,7 +1960,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return this.sink.endFromJS(globalThis).toJS(globalThis); } - pub fn endWithSink(ptr: *anyopaque, globalThis: *JSGlobalObject) callconv(.C) JSValue { + pub fn endWithSink(ptr: *anyopaque, globalThis: *JSGlobalObject) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); var this = @as(*ThisSink, @ptrCast(@alignCast(ptr))); @@ -1914,7 +1968,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { globalThis.vm().throwError(globalThis, err); - return JSC.JSValue.jsUndefined(); + return .undefined; } } @@ -1925,18 +1979,6 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return shim.cppFn("assignToStream", .{ globalThis, stream, ptr, jsvalue_ptr }); } - pub const Export = shim.exportFunctions(.{ - .finalize = finalize, - .write = write, - .close = close, - .flush = flush, - .start = start, - .end = end, - .construct = construct, - .endWithSink = endWithSink, - .updateRef = updateRef, - }); - pub fn updateRef(ptr: *anyopaque, value: bool) callconv(.C) void { JSC.markBinding(@src()); var this = bun.cast(*ThisSink, ptr); @@ -1945,17 +1987,23 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { } comptime { - if (!JSC.is_bindgen) { - @export(finalize, .{ .name = Export[0].symbol_name }); - @export(write, .{ .name = Export[1].symbol_name }); - @export(close, .{ .name = Export[2].symbol_name }); - @export(flush, .{ .name = Export[3].symbol_name }); - @export(start, .{ .name = Export[4].symbol_name }); - @export(end, .{ .name = Export[5].symbol_name }); - @export(construct, .{ .name = Export[6].symbol_name }); - @export(endWithSink, .{ .name = Export[7].symbol_name }); - @export(updateRef, .{ .name = Export[8].symbol_name }); - } + @export(finalize, .{ .name = shim.symbolName("finalize") }); + @export(write, .{ .name = shim.symbolName("write") }); + @export(close, .{ .name = shim.symbolName("close") }); + @export(flush, .{ .name = shim.symbolName("flush") }); + @export(start, .{ .name = shim.symbolName("start") }); + @export(end, .{ .name = shim.symbolName("end") }); + @export(construct, .{ .name = shim.symbolName("construct") }); + @export(endWithSink, .{ .name = shim.symbolName("endWithSink") }); + @export(updateRef, .{ .name = shim.symbolName("updateRef") }); + + shim.assertJSFunction(.{ + write, + close, + flush, + start, + end, + }); } pub const Extern = [_][]const u8{ "createObject", "fromJS", "assignToStream", "onReady", "onClose", "detachPtr" }; @@ -1969,13 +2017,13 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { // socket: Socket, -// pub fn connect(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { +// pub fn connect(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { // JSC.markBinding(@src()); // var this = @ptrCast(*ThisSocket, @alignCast( fromJS(globalThis, callframe.this()) orelse { -// const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Socket", .{}, globalThis); +// const err = JSC.toTypeError(.ERR_INVALID_THIS, "Expected Socket", .{}, globalThis); // globalThis.vm().throwError(globalThis, err); -// return JSC.JSValue.jsUndefined(); +// return .undefined; // })); // } // }; @@ -2098,7 +2146,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type { return this.buffer.ptr[this.offset..this.buffer.len]; } - pub fn onWritable(this: *@This(), write_offset: u64, _: *UWSResponse) callconv(.C) bool { + pub fn onWritable(this: *@This(), write_offset: u64, _: *UWSResponse) bool { // write_offset is the amount of data that was written not how much we need to write log("onWritable ({d})", .{write_offset}); // onWritable reset backpressure state to allow flushing @@ -2500,6 +2548,8 @@ pub fn HTTPServerWritable(comptime ssl: bool) type { } fn registerAutoFlusher(this: *@This()) void { + // if we enqueue data we should reset the timeout + this.res.resetTimeout(); if (!this.auto_flusher.registered) AutoFlusher.registerDeferredMicrotaskWithTypeUnchecked(@This(), this, this.globalThis.bunVM()); } @@ -2589,6 +2639,14 @@ pub fn HTTPServerWritable(comptime ssl: bool) type { pub const HTTPSResponseSink = HTTPServerWritable(true); pub const HTTPResponseSink = HTTPServerWritable(false); +pub const BufferedReadableStreamAction = enum { + text, + arrayBuffer, + blob, + bytes, + json, +}; + pub fn ReadableStreamSource( comptime Context: type, comptime name_: []const u8, @@ -2598,6 +2656,7 @@ pub fn ReadableStreamSource( comptime deinit_fn: fn (this: *Context) void, comptime setRefUnrefFn: ?fn (this: *Context, enable: bool) void, comptime drainInternalBuffer: ?fn (this: *Context) bun.ByteList, + comptime toBufferedValue: ?fn (this: *Context, globalThis: *JSC.JSGlobalObject, action: BufferedReadableStreamAction) JSC.JSValue, ) type { return struct { context: Context, @@ -2610,7 +2669,6 @@ pub fn ReadableStreamSource( globalThis: *JSGlobalObject = undefined, this_jsvalue: JSC.JSValue = .zero, is_closed: bool = false, - const This = @This(); const ReadableStreamSourceType = @This(); @@ -2727,7 +2785,7 @@ pub fn ReadableStreamSource( return ReadableStream.fromNative(globalThis, out_value); } - pub fn setRawModeFromJS(this: *ReadableStreamSourceType, global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn setRawModeFromJS(this: *ReadableStreamSourceType, global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) JSValue { if (@hasDecl(Context, "setRawMode")) { const flag = call_frame.argument(0); if (Environment.allow_assert) { @@ -2757,21 +2815,26 @@ pub fn ReadableStreamSource( pub const finalize = JSReadableStreamSource.finalize; pub const construct = JSReadableStreamSource.construct; pub const getIsClosedFromJS = JSReadableStreamSource.isClosed; + pub const textFromJS = JSReadableStreamSource.text; + pub const jsonFromJS = JSReadableStreamSource.json; + pub const arrayBufferFromJS = JSReadableStreamSource.arrayBuffer; + pub const blobFromJS = JSReadableStreamSource.blob; + pub const bytesFromJS = JSReadableStreamSource.bytes; pub const JSReadableStreamSource = struct { - pub fn construct(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) ?*ReadableStreamSourceType { + pub fn construct(globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) ?*ReadableStreamSourceType { _ = callFrame; // autofix globalThis.throw("Cannot construct ReadableStreamSource", .{}); return null; } - pub fn pull(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn pull(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); const this_jsvalue = callFrame.this(); const arguments = callFrame.arguments(2); const view = arguments.ptr[0]; view.ensureStillAlive(); this.this_jsvalue = this_jsvalue; - var buffer = view.asArrayBuffer(globalThis) orelse return JSC.JSValue.jsUndefined(); + var buffer = view.asArrayBuffer(globalThis) orelse return .undefined; return processResult( this_jsvalue, globalThis, @@ -2780,7 +2843,7 @@ pub fn ReadableStreamSource( ); } - pub fn start(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn start(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); this.globalThis = globalThis; this.this_jsvalue = callFrame.this(); @@ -2790,7 +2853,7 @@ pub fn ReadableStreamSource( .chunk_size => |size| return JSValue.jsNumber(size), .err => |err| { globalThis.vm().throwError(globalThis, err.toJSC(globalThis)); - return JSC.JSValue.jsUndefined(); + return .undefined; }, else => |rc| { return rc.toJS(globalThis); @@ -2798,7 +2861,7 @@ pub fn ReadableStreamSource( } } - pub fn isClosed(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn isClosed(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) JSC.JSValue { _ = globalObject; // autofix return JSC.JSValue.jsBoolean(this.is_closed); } @@ -2829,15 +2892,15 @@ pub fn ReadableStreamSource( } } - pub fn cancel(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn cancel(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { _ = globalObject; // autofix JSC.markBinding(@src()); this.this_jsvalue = callFrame.this(); this.cancel(); - return JSC.JSValue.jsUndefined(); + return .undefined; } - pub fn setOnCloseFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(.C) bool { + pub fn setOnCloseFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) bool { JSC.markBinding(@src()); this.close_handler = JSReadableStreamSource.onClose; this.globalThis = globalObject; @@ -2856,7 +2919,7 @@ pub fn ReadableStreamSource( return true; } - pub fn setOnDrainFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(.C) bool { + pub fn setOnDrainFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) bool { JSC.markBinding(@src()); this.globalThis = globalObject; @@ -2874,7 +2937,7 @@ pub fn ReadableStreamSource( return true; } - pub fn getOnCloseFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getOnCloseFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) JSC.JSValue { _ = globalObject; // autofix JSC.markBinding(@src()); @@ -2882,7 +2945,7 @@ pub fn ReadableStreamSource( return this.close_jsvalue.get() orelse .undefined; } - pub fn getOnDrainFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { + pub fn getOnDrainFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) JSC.JSValue { _ = globalObject; // autofix JSC.markBinding(@src()); @@ -2894,13 +2957,13 @@ pub fn ReadableStreamSource( return .undefined; } - pub fn updateRef(this: *ReadableStreamSourceType, globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn updateRef(this: *ReadableStreamSourceType, globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); this.this_jsvalue = callFrame.this(); const ref_or_unref = callFrame.argument(0).toBooleanSlow(globalObject); this.setRef(ref_or_unref); - return JSC.JSValue.jsUndefined(); + return .undefined; } fn onClose(ptr: ?*anyopaque) void { @@ -2913,13 +2976,13 @@ pub fn ReadableStreamSource( this.close_jsvalue.clear(); } - pub fn finalize(this: *ReadableStreamSourceType) callconv(.C) void { + pub fn finalize(this: *ReadableStreamSourceType) void { this.this_jsvalue = .zero; _ = this.decrementCount(); } - pub fn drain(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn drain(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { JSC.markBinding(@src()); this.this_jsvalue = callFrame.this(); var list = this.drain(); @@ -2928,6 +2991,66 @@ pub fn ReadableStreamSource( } return JSValue.jsUndefined(); } + + pub fn text(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { + JSC.markBinding(@src()); + this.this_jsvalue = callFrame.this(); + + if (toBufferedValue) |to_buffered_value| { + return to_buffered_value(&this.context, globalThis, .text); + } + + globalThis.throwTODO("This is not implemented yet"); + return .zero; + } + + pub fn arrayBuffer(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { + JSC.markBinding(@src()); + this.this_jsvalue = callFrame.this(); + + if (toBufferedValue) |to_buffered_value| { + return to_buffered_value(&this.context, globalThis, .arrayBuffer); + } + + globalThis.throwTODO("This is not implemented yet"); + return .zero; + } + + pub fn blob(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { + JSC.markBinding(@src()); + this.this_jsvalue = callFrame.this(); + + if (toBufferedValue) |to_buffered_value| { + return to_buffered_value(&this.context, globalThis, .blob); + } + + globalThis.throwTODO("This is not implemented yet"); + return .zero; + } + + pub fn bytes(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { + JSC.markBinding(@src()); + this.this_jsvalue = callFrame.this(); + + if (toBufferedValue) |to_buffered_value| { + return to_buffered_value(&this.context, globalThis, .bytes); + } + + globalThis.throwTODO("This is not implemented yet"); + return .zero; + } + + pub fn json(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue { + JSC.markBinding(@src()); + this.this_jsvalue = callFrame.this(); + + if (toBufferedValue) |to_buffered_value| { + return to_buffered_value(&this.context, globalThis, .json); + } + + globalThis.throwTODO("This is not implemented yet"); + return .zero; + } }; }; } @@ -3014,13 +3137,13 @@ pub const FileSink = struct { // if we are not done yet and has pending data we just wait so we do not runPending twice if (status == .pending and has_pending_data) { if (this.pending.state == .pending) { - this.pending.consumed += @truncate(amount); + this.pending.consumed = @truncate(amount); } return; } if (this.pending.state == .pending) { - this.pending.consumed += @truncate(amount); + this.pending.consumed = @truncate(amount); // when "done" is true, we will never receive more data. if (this.done or status == .end_of_file) { @@ -3065,6 +3188,7 @@ pub const FileSink = struct { this.signal.ready(null, null); } + pub fn onClose(this: *FileSink) void { log("onClose()", .{}); this.signal.close(null); @@ -3254,7 +3378,7 @@ pub const FileSink = struct { } if (this.done) { - return .{ .result = JSC.JSValue.jsUndefined() }; + return .{ .result = .undefined }; } const rc = this.writer.flush(); @@ -4079,6 +4203,7 @@ pub const FileReader = struct { deinit, setRefOrUnref, drain, + null, ); }; @@ -4152,6 +4277,25 @@ pub const ByteBlobLoader = struct { return .{ .into_array = .{ .value = array, .len = copied } }; } + pub fn toAnyBlob(this: *ByteBlobLoader, globalThis: *JSC.JSGlobalObject) ?AnyBlob { + if (this.store) |store| { + _ = this.detachStore(); + if (this.offset == 0 and this.remain == store.size()) { + if (store.toAnyBlob()) |blob| { + defer store.deref(); + return blob; + } + } + + var blob = JSC.WebCore.Blob.initWithStore(store, globalThis); + blob.offset = this.offset; + blob.size = this.remain; + this.parent().is_closed = true; + return .{ .Blob = blob }; + } + return null; + } + pub fn detachStore(this: *ByteBlobLoader) ?*Blob.Store { if (this.store) |store| { this.store = null; @@ -4191,6 +4335,15 @@ pub const ByteBlobLoader = struct { return bun.ByteList.fromList(cloned); } + pub fn toBufferedValue(this: *ByteBlobLoader, globalThis: *JSC.JSGlobalObject, action: BufferedReadableStreamAction) JSC.JSValue { + if (this.toAnyBlob(globalThis)) |blob_| { + var blob = blob_; + return blob.toPromise(globalThis, action); + } + + return .zero; + } + pub const Source = ReadableStreamSource( @This(), "Blob", @@ -4200,6 +4353,7 @@ pub const ByteBlobLoader = struct { deinit, null, drain, + toBufferedValue, ); }; @@ -4251,6 +4405,57 @@ pub const ByteStream = struct { highWaterMark: Blob.SizeType = 0, pipe: Pipe = .{}, size_hint: Blob.SizeType = 0, + buffer_action: ?BufferAction = null, + + const BufferAction = union(BufferedReadableStreamAction) { + text: JSC.JSPromise.Strong, + arrayBuffer: JSC.JSPromise.Strong, + blob: JSC.JSPromise.Strong, + bytes: JSC.JSPromise.Strong, + json: JSC.JSPromise.Strong, + + pub fn fulfill(this: *BufferAction, blob: *AnyBlob) void { + blob.wrap(.{ .normal = this.swap() }, this.globalThis().?, this.*); + } + + pub fn reject(this: *BufferAction, err: StreamResult.StreamError) void { + this.swap().reject(this.globalThis().?, err.toJSWeak(this.globalThis().?)[0]); + } + + pub fn resolve(this: *BufferAction, value_: JSC.JSValue) void { + this.swap().resolve(this.globalThis().?, value_); + } + + pub fn globalThis(this: *BufferAction) ?*JSC.JSGlobalObject { + return switch (this.*) { + inline else => |promise| promise.strong.globalThis, + }; + } + + pub fn value(this: *BufferAction) JSC.JSValue { + return switch (this.*) { + inline else => |promise| promise.value(), + }; + } + + pub fn get(this: *BufferAction) *JSC.JSPromise { + return switch (this.*) { + inline else => |promise| promise.get(), + }; + } + + pub fn swap(this: *BufferAction) *JSC.JSPromise { + return switch (this.*) { + inline else => |*promise| promise.swap(), + }; + } + + pub fn deinit(this: *BufferAction) void { + switch (this.*) { + inline else => |*promise| promise.deinit(), + } + } + }; pub const tag = ReadableStream.Tag.Bytes; @@ -4264,14 +4469,18 @@ pub const ByteStream = struct { } if (this.has_received_last_chunk) { - return .{ .chunk_size = @min(1024 * 1024 * 2, this.buffer.items.len) }; + return .{ .owned_and_done = bun.ByteList.fromList(this.buffer.moveToUnmanaged()) }; } if (this.highWaterMark == 0) { return .{ .ready = {} }; } - return .{ .chunk_size = @max(this.highWaterMark, std.mem.page_size) }; + // For HTTP, the maximum streaming response body size will be 512 KB. + // #define LIBUS_RECV_BUFFER_LENGTH 524288 + // For HTTPS, the size is probably quite a bit lower like 64 KB due to TLS transmission. + // We add 1 extra page size so that if there's a little bit of excess buffered data, we avoid extra allocations. + return .{ .chunk_size = @min(512 * 1024 + std.mem.page_size, @max(this.highWaterMark, std.mem.page_size)) }; } pub fn value(this: *@This()) JSValue { @@ -4306,7 +4515,7 @@ pub const ByteStream = struct { return; } - bun.assert(!this.has_received_last_chunk); + bun.assert(!this.has_received_last_chunk or stream == .err); this.has_received_last_chunk = stream.isDone(); if (this.pipe.ctx) |ctx| { @@ -4316,6 +4525,51 @@ pub const ByteStream = struct { const chunk = stream.slice(); + if (this.buffer_action) |*action| { + if (stream == .err) { + defer { + this.buffer.clearAndFree(); + this.pending.result.deinit(); + this.pending.result = .{ .done = {} }; + this.buffer_action = null; + } + + action.reject(stream.err); + return; + } + + if (this.has_received_last_chunk) { + defer { + this.buffer_action = null; + } + + if (this.buffer.capacity == 0 and stream == .owned_and_done) { + this.buffer = std.ArrayList(u8).fromOwnedSlice(bun.default_allocator, @constCast(chunk)); + var blob = this.toAnyBlob().?; + action.fulfill(&blob); + return; + } + defer { + if (stream == .owned_and_done or stream == .owned) { + allocator.free(stream.slice()); + } + } + + this.buffer.appendSlice(chunk) catch bun.outOfMemory(); + var blob = this.toAnyBlob().?; + action.fulfill(&blob); + return; + } else { + this.buffer.appendSlice(chunk) catch bun.outOfMemory(); + + if (stream == .owned_and_done or stream == .owned) { + allocator.free(stream.slice()); + } + } + + return; + } + if (this.pending.state == .pending) { bun.assert(this.buffer.items.len == 0); const to_copy = this.pending_buffer[0..@min(chunk.len, this.pending_buffer.len)]; @@ -4331,13 +4585,9 @@ pub const ByteStream = struct { if (to_copy.len == 0) { if (stream == .err) { - if (stream.err == .Error) { - this.pending.result = .{ .err = .{ .Error = stream.err.Error } }; - } - const js_err = stream.err.JSValue; - js_err.ensureStillAlive(); - js_err.protect(); - this.pending.result = .{ .err = .{ .JSValue = js_err } }; + this.pending.result = .{ + .err = stream.err, + }; } else { this.pending.result = .{ .done = {}, @@ -4362,19 +4612,20 @@ pub const ByteStream = struct { const remaining = chunk[to_copy.len..]; if (remaining.len > 0) - this.append(stream, to_copy.len, allocator) catch @panic("Out of memory while copying request body"); + this.append(stream, to_copy.len, chunk, allocator) catch @panic("Out of memory while copying request body"); this.pending.run(); return; } - this.append(stream, 0, allocator) catch @panic("Out of memory while copying request body"); + this.append(stream, 0, chunk, allocator) catch @panic("Out of memory while copying request body"); } pub fn append( this: *@This(), stream: StreamResult, offset: usize, + base_address: []const u8, allocator: std.mem.Allocator, ) !void { const chunk = stream.slice()[offset..]; @@ -4405,12 +4656,22 @@ pub const ByteStream = struct { .temporary_and_done, .temporary => { try this.buffer.appendSlice(chunk); }, + .owned_and_done, .owned => { + try this.buffer.appendSlice(chunk); + allocator.free(@constCast(base_address)); + }, .err => { + if (this.buffer_action != null) { + @panic("Expected buffer action to be null"); + } + this.pending.result = .{ .err = stream.err }; }, // We don't support the rest of these yet else => unreachable, } + + return; } pub fn setValue(this: *@This(), view: JSC.JSValue) void { @@ -4425,6 +4686,7 @@ pub const ByteStream = struct { pub fn onPull(this: *@This(), buffer: []u8, view: JSC.JSValue) StreamResult { JSC.markBinding(@src()); bun.assert(buffer.len > 0); + bun.debugAssert(this.buffer_action == null); if (this.buffer.items.len > 0) { bun.assert(this.value() == .zero); @@ -4486,9 +4748,15 @@ pub const ByteStream = struct { if (view != .zero) { this.pending_buffer = &.{}; + this.pending.result.deinit(); this.pending.result = .{ .done = {} }; this.pending.run(); } + + if (this.buffer_action) |*action| { + action.reject(.{ .AbortReason = .UserAbort }); + this.buffer_action = null; + } } pub fn deinit(this: *@This()) void { @@ -4500,13 +4768,84 @@ pub const ByteStream = struct { this.done = true; this.pending_buffer = &.{}; + this.pending.result.deinit(); this.pending.result = .{ .done = {} }; this.pending.run(); } - + if (this.buffer_action) |*action| { + action.deinit(); + } this.parent().destroy(); } + pub fn drain(this: *@This()) bun.ByteList { + if (this.buffer.items.len > 0) { + const out = bun.ByteList.fromList(this.buffer); + this.buffer = .{ + .allocator = bun.default_allocator, + .items = &.{}, + .capacity = 0, + }; + + return out; + } + + return .{}; + } + + pub fn toAnyBlob(this: *@This()) ?AnyBlob { + if (this.has_received_last_chunk) { + const buffer = this.buffer; + this.buffer = .{ + .allocator = bun.default_allocator, + .items = &.{}, + .capacity = 0, + }; + this.done = true; + this.pending.result.deinit(); + this.pending.result = .{ .done = {} }; + this.parent().is_closed = true; + return AnyBlob{ + .InternalBlob = JSC.WebCore.InternalBlob{ + .bytes = buffer, + .was_string = false, + }, + }; + } + + return null; + } + + pub fn toBufferedValue(this: *@This(), globalThis: *JSC.JSGlobalObject, action: BufferedReadableStreamAction) JSC.JSValue { + if (this.buffer_action != null) { + globalThis.throw("Cannot buffer value twice", .{}); + return .zero; + } + + if (this.pending.result == .err) { + const err, _ = this.pending.result.err.toJSWeak(globalThis); + this.pending.result.deinit(); + this.done = true; + this.buffer.clearAndFree(); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err); + } + + if (this.toAnyBlob()) |blob_| { + var blob = blob_; + return blob.toPromise(globalThis, action); + } + + this.buffer_action = switch (action) { + .blob => .{ .blob = JSC.JSPromise.Strong.init(globalThis) }, + .bytes => .{ .bytes = JSC.JSPromise.Strong.init(globalThis) }, + .arrayBuffer => .{ .arrayBuffer = JSC.JSPromise.Strong.init(globalThis) }, + .json => .{ .json = JSC.JSPromise.Strong.init(globalThis) }, + .text => .{ .text = JSC.JSPromise.Strong.init(globalThis) }, + }; + + return this.buffer_action.?.value(); + } + pub const Source = ReadableStreamSource( @This(), "Bytes", @@ -4515,7 +4854,8 @@ pub const ByteStream = struct { onCancel, deinit, null, - null, + drain, + toBufferedValue, ); }; diff --git a/src/bun.zig b/src/bun.zig index 46086746aaeb0..0da72ab1e172a 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -7,6 +7,8 @@ // Otherwise, you risk a circular dependency or Zig including multiple copies of this file which leads to strange bugs. const builtin = @import("builtin"); const std = @import("std"); +const bun = @This(); + pub const Environment = @import("env.zig"); pub const use_mimalloc = !Environment.isTest; @@ -16,6 +18,12 @@ pub const default_allocator: std.mem.Allocator = if (!use_mimalloc) else @import("./memory_allocator.zig").c_allocator; +/// Zeroing memory allocator +pub const z_allocator: std.mem.Allocator = if (!use_mimalloc) + std.heap.c_allocator +else + @import("./memory_allocator.zig").z_allocator; + pub const huge_allocator: std.mem.Allocator = if (!use_mimalloc) std.heap.c_allocator else @@ -35,6 +43,31 @@ pub const callconv_inline: std.builtin.CallingConvention = if (builtin.mode == . /// FileSystem is a singleton. pub const fs_allocator = default_allocator; +pub fn typedAllocator(comptime T: type) std.mem.Allocator { + if (heap_breakdown.enabled) + return heap_breakdown.allocator(comptime T); + + return default_allocator; +} + +pub inline fn namedAllocator(comptime name: [:0]const u8) std.mem.Allocator { + if (heap_breakdown.enabled) + return heap_breakdown.namedAllocator(name); + + return default_allocator; +} + +pub const OOM = error{OutOfMemory}; + +pub const JSError = error{ + /// There is an active exception on the global object. Options: + /// + /// - Bubble it up to the caller + /// - Call `global.takeException(err)` to get the JSValue of the exception, + /// - Call `global.reportActiveExceptionAsUnhandled(err)` to make it unhandled. + JSError, +}; + pub const C = @import("root").C; pub const sha = @import("./sha.zig"); pub const FeatureFlags = @import("feature_flags.zig"); @@ -59,6 +92,7 @@ pub const ComptimeStringMapWithKeyType = comptime_string_map.ComptimeStringMapWi pub const glob = @import("./glob.zig"); pub const patch = @import("./patch.zig"); +pub const ini = @import("./ini.zig"); pub const shell = struct { pub usingnamespace @import("./shell/shell.zig"); @@ -96,17 +130,17 @@ pub const FileDescriptor = enum(FileDescriptorInt) { /// On Windows, it is always a mistake, as the integer is bitcast of a tagged packed struct. /// /// TODO(@paperdave): remove this API. - pub inline fn int(self: FileDescriptor) std.posix.fd_t { + pub fn int(self: FileDescriptor) std.posix.fd_t { if (Environment.isWindows) @compileError("FileDescriptor.int() is not allowed on Windows."); return @intFromEnum(self); } - pub inline fn writeTo(fd: FileDescriptor, writer: anytype, endian: std.builtin.Endian) !void { + pub fn writeTo(fd: FileDescriptor, writer: anytype, endian: std.builtin.Endian) !void { try writer.writeInt(FileDescriptorInt, @intFromEnum(fd), endian); } - pub inline fn readFrom(reader: anytype, endian: std.builtin.Endian) !FileDescriptor { + pub fn readFrom(reader: anytype, endian: std.builtin.Endian) !FileDescriptor { return @enumFromInt(try reader.readInt(FileDescriptorInt, endian)); } @@ -116,35 +150,35 @@ pub const FileDescriptor = enum(FileDescriptorInt) { /// to Windows' *HANDLE, and casts the types for proper usage. /// /// This may be needed in places where a FileDescriptor is given to `std` or `kernel32` apis - pub inline fn cast(fd: FileDescriptor) std.posix.fd_t { + pub fn cast(fd: FileDescriptor) std.posix.fd_t { if (!Environment.isWindows) return fd.int(); // if not having this check, the cast may crash zig compiler? if (@inComptime() and fd == invalid_fd) return FDImpl.invalid.system(); - return FDImpl.decode(fd).system(); + return fd.impl().system(); } - pub inline fn asDir(fd: FileDescriptor) std.fs.Dir { + pub fn asDir(fd: FileDescriptor) std.fs.Dir { return std.fs.Dir{ .fd = fd.cast() }; } - pub inline fn asFile(fd: FileDescriptor) std.fs.File { + pub fn asFile(fd: FileDescriptor) std.fs.File { return std.fs.File{ .handle = fd.cast() }; } pub fn format(fd: FileDescriptor, comptime fmt_: string, options_: std.fmt.FormatOptions, writer: anytype) !void { - try FDImpl.format(FDImpl.decode(fd), fmt_, options_, writer); + try FDImpl.format(fd.impl(), fmt_, options_, writer); } pub fn assertValid(fd: FileDescriptor) void { - FDImpl.decode(fd).assertValid(); + fd.impl().assertValid(); } pub fn isValid(fd: FileDescriptor) bool { - return FDImpl.decode(fd).isValid(); + return fd.impl().isValid(); } pub fn assertKind(fd: FileDescriptor, kind: FDImpl.Kind) void { - assert(FDImpl.decode(fd).kind == kind); + assert(fd.impl().kind == kind); } pub fn cwd() FileDescriptor { @@ -170,7 +204,7 @@ pub const FileDescriptor = enum(FileDescriptorInt) { pub fn isStdio(fd: FileDescriptor) bool { // fd.assertValid(); - const decoded = FDImpl.decode(fd); + const decoded = fd.impl(); return switch (Environment.os) { else => decoded.value.as_system < 3, .windows => switch (decoded.kind) { @@ -185,6 +219,10 @@ pub const FileDescriptor = enum(FileDescriptorInt) { pub fn toJS(value: FileDescriptor, global: *JSC.JSGlobalObject) JSC.JSValue { return FDImpl.decode(value).toJS(global); } + + pub fn impl(fd: FileDescriptor) FDImpl { + return FDImpl.decode(fd); + } }; pub const FDImpl = @import("./fd.zig").FDImpl; @@ -366,6 +404,51 @@ pub const StringHashMapUnowned = struct { }; pub const BabyList = @import("./baby_list.zig").BabyList; pub const ByteList = BabyList(u8); +pub const OffsetByteList = struct { + head: u32 = 0, + byte_list: ByteList = .{}, + + pub fn init(head: u32, byte_list: ByteList) OffsetByteList { + return OffsetByteList{ + .head = head, + .byte_list = byte_list, + }; + } + + pub fn write(self: *OffsetByteList, allocator: std.mem.Allocator, bytes: []const u8) !void { + _ = try self.byte_list.write(allocator, bytes); + } + + pub fn slice(this: *OffsetByteList) []u8 { + return this.byte_list.slice()[0..this.head]; + } + + pub fn remaining(this: *OffsetByteList) []u8 { + return this.byte_list.slice()[this.head..]; + } + + pub fn consume(self: *OffsetByteList, bytes: u32) void { + self.head +|= bytes; + if (self.head >= self.byte_list.len) { + self.head = 0; + self.byte_list.len = 0; + } + } + + pub fn len(self: *const OffsetByteList) u32 { + return self.byte_list.len - self.head; + } + + pub fn clear(self: *OffsetByteList) void { + self.head = 0; + self.byte_list.len = 0; + } + + pub fn deinit(self: *OffsetByteList, allocator: std.mem.Allocator) void { + self.byte_list.deinitWithAllocator(allocator); + self.* = .{}; + } +}; pub fn DebugOnly(comptime Type: type) type { if (comptime Environment.allow_assert) { @@ -394,29 +477,10 @@ pub inline fn range(comptime min: anytype, comptime max: anytype) [max - min]usi } pub fn copy(comptime Type: type, dest: []Type, src: []const Type) void { - if (comptime Environment.allow_assert) assert(dest.len >= src.len); - if (@intFromPtr(src.ptr) == @intFromPtr(dest.ptr) or src.len == 0) return; - const input: []const u8 = std.mem.sliceAsBytes(src); const output: []u8 = std.mem.sliceAsBytes(dest); - assert(input.len > 0); - assert(output.len > 0); - - const does_input_or_output_overlap = (@intFromPtr(input.ptr) < @intFromPtr(output.ptr) and - @intFromPtr(input.ptr) + input.len > @intFromPtr(output.ptr)) or - (@intFromPtr(output.ptr) < @intFromPtr(input.ptr) and - @intFromPtr(output.ptr) + output.len > @intFromPtr(input.ptr)); - - if (!does_input_or_output_overlap) { - @memcpy(output[0..input.len], input); - } else if (comptime Environment.isNative) { - C.memmove(output.ptr, input.ptr, input.len); - } else { - for (input, output) |input_byte, *out| { - out.* = input_byte; - } - } + return memmove(output, input); } pub fn clone(item: anytype, allocator: std.mem.Allocator) !@TypeOf(item) { @@ -464,7 +528,7 @@ pub fn fastRandom() u64 { // and we only need to do it once per process var value = seed_value.load(.monotonic); while (value == 0) : (value = seed_value.load(.monotonic)) { - if (comptime Environment.isDebug) outer: { + if (comptime Environment.isDebug or Environment.is_canary) outer: { if (getenvZ("BUN_DEBUG_HASH_RANDOM_SEED")) |env| { value = std.fmt.parseInt(u64, env, 10) catch break :outer; seed_value.store(value, .monotonic); @@ -733,9 +797,9 @@ pub fn openDir(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.Dir { } } -pub fn openDirNoRenamingOrDeletingWindows(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.Dir { +pub fn openDirNoRenamingOrDeletingWindows(dir: FileDescriptor, path_: [:0]const u8) !std.fs.Dir { if (comptime !Environment.isWindows) @compileError("use openDir!"); - const res = try sys.openDirAtWindowsA(toFD(dir.fd), path_, .{ .iterable = true, .can_rename_or_delete = false, .read_only = true }).unwrap(); + const res = try sys.openDirAtWindowsA(dir, path_, .{ .iterable = true, .can_rename_or_delete = false, .read_only = true }).unwrap(); return res.asDir(); } @@ -760,14 +824,23 @@ pub fn openDirForIteration(dir: std.fs.Dir, path_: []const u8) !std.fs.Dir { } pub fn openDirAbsolute(path_: []const u8) !std.fs.Dir { - if (comptime Environment.isWindows) { - const res = try sys.openDirAtWindowsA(invalid_fd, path_, .{ .iterable = true, .can_rename_or_delete = true, .read_only = true }).unwrap(); - return res.asDir(); - } else { - const fd = try sys.openA(path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); - return fd.asDir(); - } + const fd = if (comptime Environment.isWindows) + try sys.openDirAtWindowsA(invalid_fd, path_, .{ .iterable = true, .can_rename_or_delete = true, .read_only = true }).unwrap() + else + try sys.openA(path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); + + return fd.asDir(); } + +pub fn openDirAbsoluteNotForDeletingOrRenaming(path_: []const u8) !std.fs.Dir { + const fd = if (comptime Environment.isWindows) + try sys.openDirAtWindowsA(invalid_fd, path_, .{ .iterable = true, .can_rename_or_delete = false, .read_only = true }).unwrap() + else + try sys.openA(path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); + + return fd.asDir(); +} + pub const MimallocArena = @import("./mimalloc_arena.zig").Arena; pub fn getRuntimeFeatureFlag(comptime flag: [:0]const u8) bool { return struct { @@ -800,7 +873,7 @@ pub fn getenvZ(key: [:0]const u8) ?[]const u8 { for (std.os.environ) |lineZ| { const line = sliceTo(lineZ, 0); const key_end = strings.indexOfCharUsize(line, '=') orelse line.len; - if (strings.eqlInsensitive(line[0..key_end], key)) { + if (strings.eqlCaseInsensitiveASCII(line[0..key_end], key, true)) { return line[@min(key_end + 1, line.len)..]; } } @@ -812,6 +885,11 @@ pub fn getenvZ(key: [:0]const u8) ?[]const u8 { return sliceTo(ptr, 0); } +pub fn getenvTruthy(key: [:0]const u8) bool { + if (getenvZ(key)) |value| return std.mem.eql(u8, value, "true") or std.mem.eql(u8, value, "1"); + return false; +} + pub const FDHashMapContext = struct { pub fn hash(_: @This(), fd: FileDescriptor) u64 { // a file descriptor is i32 on linux, u64 on windows @@ -1008,6 +1086,10 @@ pub fn CaseInsensitiveASCIIStringArrayHashMap(comptime Type: type) type { return std.ArrayHashMap([]const u8, Type, CaseInsensitiveASCIIStringContext, true); } +pub fn CaseInsensitiveASCIIStringArrayHashMapUnmanaged(comptime Type: type) type { + return std.ArrayHashMapUnmanaged([]const u8, Type, CaseInsensitiveASCIIStringContext, true); +} + pub fn StringArrayHashMapUnmanaged(comptime Type: type) type { return std.ArrayHashMapUnmanaged([]const u8, Type, StringArrayHashMapContext, true); } @@ -1029,6 +1111,7 @@ pub fn U32HashMap(comptime Type: type) type { } const CopyFile = @import("./copy_file.zig"); +pub const copyFileErrnoConvert = CopyFile.copyFileErrorConvert; pub const copyFileRange = CopyFile.copyFileRange; pub const canUseCopyFileRangeSyscall = CopyFile.canUseCopyFileRangeSyscall; pub const disableCopyFileRangeSyscall = CopyFile.disableCopyFileRangeSyscall; @@ -1206,11 +1289,11 @@ pub const JSON = @import("./json_parser.zig"); pub const JSAst = @import("./js_ast.zig"); pub const bit_set = @import("./bit_set.zig"); -pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) { +pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) [:0]const u8) { const Map = struct { const vargs = args; const labels = brk: { - var vabels_ = std.enums.EnumArray(T, []const u8).initFill(""); + var vabels_ = std.enums.EnumArray(T, [:0]const u8).initFill(""); @setEvalBranchQuota(99999); for (vargs) |field| { vabels_.set(field.@"0", field.@"1"); @@ -1218,7 +1301,7 @@ pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) { break :brk vabels_; }; - pub fn get(input: T) []const u8 { + pub fn get(input: T) [:0]const u8 { return labels.get(input); } }; @@ -1227,15 +1310,11 @@ pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) { } pub fn ComptimeEnumMap(comptime T: type) type { - comptime { - var entries: [std.enums.values(T).len]struct { string, T } = undefined; - var i: usize = 0; - for (std.enums.values(T)) |value| { - entries[i] = .{ .@"0" = @tagName(value), .@"1" = value }; - i += 1; - } - return ComptimeStringMap(T, entries); + var entries: [std.enums.values(T).len]struct { [:0]const u8, T } = undefined; + for (std.enums.values(T), &entries) |value, *entry| { + entry.* = .{ .@"0" = @tagName(value), .@"1" = value }; } + return ComptimeStringMap(T, entries); } /// Write 0's for every byte in Type @@ -1276,7 +1355,7 @@ pub fn getcwdAlloc(allocator: std.mem.Allocator) ![]u8 { /// Get the absolute path to a file descriptor. /// On Linux, when `/proc/self/fd` is not available, this function will attempt to use `fchdir` and `getcwd` to get the path instead. -pub fn getFdPath(fd_: anytype, buf: *[@This().MAX_PATH_BYTES]u8) ![]u8 { +pub fn getFdPath(fd_: anytype, buf: *[MAX_PATH_BYTES]u8) ![]u8 { const fd = toFD(fd_).cast(); if (comptime Environment.isWindows) { @@ -1479,10 +1558,8 @@ pub const StringJoiner = @import("./StringJoiner.zig"); pub const NullableAllocator = @import("./NullableAllocator.zig"); pub const renamer = @import("./renamer.zig"); -pub const sourcemap = struct { - pub usingnamespace @import("./sourcemap/sourcemap.zig"); - pub usingnamespace @import("./sourcemap/CodeCoverage.zig"); -}; +// TODO: Rename to SourceMap as this is a struct. +pub const sourcemap = @import("./sourcemap/sourcemap.zig"); pub fn asByteSlice(buffer: anytype) []const u8 { return switch (@TypeOf(buffer)) { @@ -1616,7 +1693,6 @@ pub fn reloadProcess( } Output.Source.Stdio.restore(); - const bun = @This(); if (comptime Environment.isWindows) { // on windows we assume that we have a parent process that is monitoring us and will restart us if we exit with a magic exit code @@ -1823,8 +1899,9 @@ pub const StringMap = struct { }; pub const DotEnv = @import("./env_loader.zig"); -pub const BundleV2 = @import("./bundler/bundle_v2.zig").BundleV2; -pub const ParseTask = @import("./bundler/bundle_v2.zig").ParseTask; +pub const bundle_v2 = @import("./bundler/bundle_v2.zig"); +pub const BundleV2 = bundle_v2.BundleV2; +pub const ParseTask = bundle_v2.ParseTask; pub const Lock = @import("./lock.zig").Lock; pub const UnboundedQueue = @import("./bun.js/unbounded_queue.zig").UnboundedQueue; @@ -1873,15 +1950,17 @@ pub fn Ref(comptime T: type) type { pub fn HiveRef(comptime T: type, comptime capacity: u16) type { return struct { const HiveAllocator = HiveArray(@This(), capacity).Fallback; - ref_count: u32, allocator: *HiveAllocator, value: T, + pub fn init(value: T, allocator: *HiveAllocator) !*@This() { - var this = try allocator.tryGet(); - this.allocator = allocator; - this.ref_count = 1; - this.value = value; + const this = try allocator.tryGet(); + this.* = .{ + .ref_count = 1, + .allocator = allocator, + .value = value, + }; return this; } @@ -1891,8 +1970,9 @@ pub fn HiveRef(comptime T: type, comptime capacity: u16) type { } pub fn unref(this: *@This()) ?*@This() { - this.ref_count -= 1; - if (this.ref_count == 0) { + const ref_count = this.ref_count; + this.ref_count = ref_count - 1; + if (ref_count == 1) { if (@hasDecl(T, "deinit")) { this.value.deinit(); } @@ -1954,10 +2034,10 @@ pub const ArenaAllocator = @import("./ArenaAllocator.zig").ArenaAllocator; pub const Wyhash11 = @import("./wyhash.zig").Wyhash11; pub const RegularExpression = @import("./bun.js/bindings/RegularExpression.zig").RegularExpression; + pub inline fn assertComptime() void { - if (comptime !@inComptime()) { - @compileError("This function can only be called in comptime."); - } + var x = 0; // if you hit an error on this line, you are not in a comptime context + _ = &x; } const TODO_LOG = Output.scoped(.TODO, false); @@ -1988,7 +2068,7 @@ pub inline fn toFD(fd: anytype) FileDescriptor { }).encode(); } else { // TODO: remove intCast. we should not be casting u32 -> i32 - // even though file descriptors are always positive, linux/mac repesents them as signed integers + // even though file descriptors are always positive, linux/mac represents them as signed integers return switch (T) { FileDescriptor => fd, // TODO: remove the toFD call from these places and make this a @compileError sys.File => fd.handle, @@ -2293,7 +2373,7 @@ pub const win32 = struct { if (exit_code == watcher_reload_exit) { continue; } else { - Global.exitWide(exit_code); + Global.exit(exit_code); } } } @@ -2577,7 +2657,7 @@ pub inline fn pathLiteral(comptime literal: anytype) *const [literal.len:0]u8 { var buf: [literal.len:0]u8 = undefined; for (literal, 0..) |c, i| { buf[i] = if (c == '/') '\\' else c; - std.debug.assert(buf[i] != 0 and buf[i] < 128); + assert(buf[i] != 0 and buf[i] < 128); } buf[buf.len] = 0; const final = buf[0..buf.len :0].*; @@ -2592,7 +2672,7 @@ pub inline fn OSPathLiteral(comptime literal: anytype) *const [literal.len:0]OSP var buf: [literal.len:0]OSPathChar = undefined; for (literal, 0..) |c, i| { buf[i] = if (c == '/') '\\' else c; - std.debug.assert(buf[i] != 0 and buf[i] < 128); + assert(buf[i] != 0 and buf[i] < 128); } buf[buf.len] = 0; const final = buf[0..buf.len :0].*; @@ -2879,88 +2959,76 @@ pub noinline fn outOfMemory() noreturn { crash_handler.crashHandler(.out_of_memory, null, @returnAddress()); } +/// Wrapper around allocator.create(T) that safely initializes the pointer. Prefer this over +/// `std.mem.Allocator.create`, but prefer using `bun.new` over `create(default_allocator, T, t)` pub fn create(allocator: std.mem.Allocator, comptime T: type, t: T) *T { const ptr = allocator.create(T) catch outOfMemory(); ptr.* = t; return ptr; } -pub const is_heap_breakdown_enabled = Environment.allow_assert and Environment.isMac; - -pub const HeapBreakdown = if (is_heap_breakdown_enabled) @import("./heap_breakdown.zig") else struct {}; +pub const heap_breakdown = @import("./heap_breakdown.zig"); /// Globally-allocate a value on the heap. /// +/// **Prefer `bun.New`, `bun.NewRefCounted`, or `bun.NewThreadSafeRefCounted` instead.** +/// Use this when the struct is a third-party struct you cannot modify, like a +/// Zig stdlib struct. Choosing the wrong allocator is an easy way to introduce +/// bugs. +/// /// When used, you must call `bun.destroy` to free the memory. /// default_allocator.destroy should not be used. /// /// On macOS, you can use `Bun.unsafe.mimallocDump()` /// to dump the heap. -pub inline fn new(comptime T: type, t: T) *T { - if (comptime @hasDecl(T, "is_bun.New()")) { - // You will get weird memory bugs in debug builds if you use the wrong allocator. - @compileError("Use " ++ @typeName(T) ++ ".new() instead of bun.new()"); - } - if (comptime is_heap_breakdown_enabled) { - const ptr = HeapBreakdown.allocator(T).create(T) catch outOfMemory(); - ptr.* = t; - return ptr; +pub inline fn new(comptime T: type, init: T) *T { + const ptr = if (heap_breakdown.enabled) + heap_breakdown.getZoneT(T).create(T, init) + else ptr: { + const ptr = default_allocator.create(T) catch outOfMemory(); + ptr.* = init; + break :ptr ptr; + }; + + if (comptime Environment.allow_assert) { + const logAlloc = Output.scoped(.alloc, @hasDecl(T, "logAllocations")); + logAlloc("new({s}) = {*}", .{ meta.typeName(T), ptr }); } - const ptr = default_allocator.create(T) catch outOfMemory(); - ptr.* = t; return ptr; } -pub const newWithAlloc = @compileError("If you're going to use a global allocator, don't conditionally use it. Use bun.New() instead."); -pub const destroyWithAlloc = @compileError("If you're going to use a global allocator, don't conditionally use it. Use bun.New() instead."); +/// Free a globally-allocated a value from `bun.new()`. Using this with +/// pointers allocated from other means may cause crashes. +pub inline fn destroy(ptr: anytype) void { + const T = std.meta.Child(@TypeOf(ptr)); -pub inline fn dupe(comptime T: type, t: *T) *T { - if (comptime is_heap_breakdown_enabled) { - const ptr = HeapBreakdown.allocator(T).create(T) catch outOfMemory(); - ptr.* = t.*; - return ptr; + if (Environment.allow_assert) { + const logAlloc = Output.scoped(.alloc, @hasDecl(T, "logAllocations")); + logAlloc("destroy({s}) = {*}", .{ meta.typeName(T), ptr }); } - const ptr = default_allocator.create(T) catch outOfMemory(); - ptr.* = t.*; - return ptr; + if (comptime heap_breakdown.enabled) { + heap_breakdown.getZoneT(T).destroy(T, ptr); + } else { + default_allocator.destroy(ptr); + } +} + +pub inline fn dupe(comptime T: type, t: *T) *T { + return new(T, t.*); } pub fn New(comptime T: type) type { return struct { - const allocation_logger = Output.scoped(.alloc, @hasDecl(T, "logAllocations")); - pub const @"is_bun.New()" = true; + pub const ban_standard_library_allocator = true; pub inline fn destroy(self: *T) void { - if (comptime Environment.allow_assert) { - allocation_logger("destroy({*})", .{self}); - } - - if (comptime is_heap_breakdown_enabled) { - HeapBreakdown.allocator(T).destroy(self); - } else { - default_allocator.destroy(self); - } + bun.destroy(self); } pub inline fn new(t: T) *T { - if (comptime is_heap_breakdown_enabled) { - const ptr = HeapBreakdown.allocator(T).create(T) catch outOfMemory(); - ptr.* = t; - if (comptime Environment.allow_assert) { - allocation_logger("new() = {*}", .{ptr}); - } - return ptr; - } - - const ptr = default_allocator.create(T) catch outOfMemory(); - ptr.* = t; - - if (comptime Environment.allow_assert) { - allocation_logger("new() = {*}", .{ptr}); - } - return ptr; + return bun.new(T, t); } }; } @@ -2986,28 +3054,23 @@ pub fn NewRefCounted(comptime T: type, comptime deinit_fn: ?fn (self: *T) void) const log = Output.scoped(output_name, true); return struct { - const allocation_logger = Output.scoped(.alloc, @hasDecl(T, "logAllocations")); - pub fn destroy(self: *T) void { - if (comptime Environment.allow_assert) { + if (Environment.allow_assert) { assert(self.ref_count == 0); - allocation_logger("destroy() = {*}", .{self}); } - if (comptime is_heap_breakdown_enabled) { - HeapBreakdown.allocator(T).destroy(self); - } else { - default_allocator.destroy(self); - } + bun.destroy(self); } pub fn ref(self: *T) void { - if (comptime Environment.isDebug) log("0x{x} ref {d} + 1 = {d}", .{ @intFromPtr(self), self.ref_count, self.ref_count + 1 }); + if (Environment.isDebug) log("0x{x} ref {d} + 1 = {d}", .{ @intFromPtr(self), self.ref_count, self.ref_count + 1 }); + self.ref_count += 1; } pub fn deref(self: *T) void { - if (comptime Environment.isDebug) log("0x{x} deref {d} - 1 = {d}", .{ @intFromPtr(self), self.ref_count, self.ref_count - 1 }); + if (Environment.isDebug) log("0x{x} deref {d} - 1 = {d}", .{ @intFromPtr(self), self.ref_count, self.ref_count - 1 }); + self.ref_count -= 1; if (self.ref_count == 0) { @@ -3020,26 +3083,71 @@ pub fn NewRefCounted(comptime T: type, comptime deinit_fn: ?fn (self: *T) void) } pub inline fn new(t: T) *T { - if (comptime is_heap_breakdown_enabled) { - const ptr = HeapBreakdown.allocator(T).create(T) catch outOfMemory(); - ptr.* = t; + const ptr = bun.new(T, t); - if (comptime Environment.allow_assert) { - if (ptr.ref_count != 1) { - std.debug.panic("Expected ref_count to be 1, got {d}", .{ptr.ref_count}); - } - allocation_logger("new() = {*}", .{ptr}); + if (Environment.enable_logs) { + if (ptr.ref_count != 1) { + Output.panic("Expected ref_count to be 1, got {d}", .{ptr.ref_count}); } + } + + return ptr; + } + }; +} + +pub fn NewThreadSafeRefCounted(comptime T: type, comptime deinit_fn: ?fn (self: *T) void) type { + if (!@hasField(T, "ref_count")) { + @compileError("Expected a field named \"ref_count\" with a default value of 1 on " ++ @typeName(T)); + } + + for (std.meta.fields(T)) |field| { + if (strings.eqlComptime(field.name, "ref_count")) { + if (field.default_value == null) { + @compileError("Expected a field named \"ref_count\" with a default value of 1 on " ++ @typeName(T)); + } + } + } + + const output_name: []const u8 = if (@hasDecl(T, "DEBUG_REFCOUNT_NAME")) T.DEBUG_REFCOUNT_NAME else meta.typeBaseName(@typeName(T)); + + const log = Output.scoped(output_name, true); + + return struct { + pub fn destroy(self: *T) void { + if (Environment.allow_assert) { + assert(self.ref_count.load(.seq_cst) == 0); + } + + bun.destroy(self); + } - return ptr; + pub fn ref(self: *T) void { + const ref_count = self.ref_count.fetchAdd(1, .seq_cst); + if (Environment.isDebug) log("0x{x} ref {d} + 1 = {d}", .{ @intFromPtr(self), ref_count, ref_count - 1 }); + bun.debugAssert(ref_count > 0); + } + + pub fn deref(self: *T) void { + const ref_count = self.ref_count.fetchSub(1, .seq_cst); + if (Environment.isDebug) log("0x{x} deref {d} - 1 = {d}", .{ @intFromPtr(self), ref_count, ref_count -| 1 }); + + if (ref_count == 1) { + if (comptime deinit_fn) |deinit| { + deinit(self); + } else { + self.destroy(); + } } + } - const ptr = default_allocator.create(T) catch outOfMemory(); - ptr.* = t; + pub inline fn new(t: T) *T { + const ptr = bun.new(T, t); - if (comptime Environment.allow_assert) { - assert(ptr.ref_count == 1); - allocation_logger("new() = {*}", .{ptr}); + if (Environment.enable_logs) { + if (ptr.ref_count.load(.seq_cst) != 1) { + Output.panic("Expected ref_count to be 1, got {d}", .{ptr.ref_count.load(.seq_cst)}); + } } return ptr; @@ -3047,20 +3155,6 @@ pub fn NewRefCounted(comptime T: type, comptime deinit_fn: ?fn (self: *T) void) }; } -/// Free a globally-allocated a value. -/// -/// Must have used `new` to allocate the value. -/// -/// On macOS, you can use `Bun.unsafe.mimallocDump()` -/// to dump the heap. -pub inline fn destroy(t: anytype) void { - if (comptime is_heap_breakdown_enabled) { - HeapBreakdown.allocator(std.meta.Child(@TypeOf(t))).destroy(t); - } else { - default_allocator.destroy(t); - } -} - pub fn exitThread() noreturn { const exiter = struct { pub extern "C" fn pthread_exit(?*anyopaque) noreturn; @@ -3156,6 +3250,49 @@ pub fn getUserName(output_buffer: []u8) ?[]const u8 { return output_buffer[0..size]; } +pub fn runtimeEmbedFile( + comptime root: enum { codegen, src }, + comptime sub_path: []const u8, +) []const u8 { + comptime assert(Environment.isDebug); + comptime assert(!Environment.embed_code); + + const abs_path = comptime path: { + var buf: bun.PathBuffer = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buf); + const resolved = (std.fs.path.resolve(fba.allocator(), &.{ + switch (root) { + .codegen => Environment.codegen_path, + .src => Environment.base_path ++ "/src", + }, + sub_path, + }) catch + @compileError(unreachable))[0..].*; + break :path &resolved; + }; + + const static = struct { + var storage: []const u8 = undefined; + var once = std.once(load); + + fn load() void { + storage = std.fs.cwd().readFileAlloc(default_allocator, abs_path, std.math.maxInt(usize)) catch |e| { + Output.panic( + \\Failed to load '{s}': {} + \\ + \\To improve iteration speed, some files are not embedded but + \\loaded at runtime, at the cost of making the binary non-portable. + \\To fix this, pass -DFORCE_EMBED_CODE=1 to CMake + , .{ abs_path, e }); + }; + } + }; + + static.once.call(); + + return static.storage; +} + pub inline fn markWindowsOnly() if (Environment.isWindows) void else noreturn { if (Environment.isWindows) { return; @@ -3195,7 +3332,7 @@ pub fn selfExePath() ![:0]u8 { 4096 + 1 // + 1 for the null terminator ]u8 = undefined; var len: usize = 0; - var lock = Lock.init(); + var lock: Lock = .{}; pub fn load() ![:0]u8 { const init = try std.fs.selfExePath(&value); @@ -3264,7 +3401,7 @@ noinline fn assertionFailureWithLocation(src: std.builtin.SourceLocation) noretu }); } -pub inline fn debugAssert(cheap_value_only_plz: bool) void { +pub fn debugAssert(cheap_value_only_plz: bool) callconv(callconv_inline) void { if (comptime !Environment.isDebug) { return; } @@ -3297,16 +3434,23 @@ pub fn assertWithLocation(value: bool, src: std.builtin.SourceLocation) callconv } /// This has no effect on the real code but capturing 'a' and 'b' into parameters makes assertion failures much easier inspect in a debugger. -pub inline fn assert_eql(a: anytype, b: anytype) void { +pub fn assert_eql(a: anytype, b: anytype) callconv(callconv_inline) void { + if (@inComptime()) { + if (a != b) { + @compileLog(a); + @compileLog(b); + @compileError("A != B"); + } + } return assert(a == b); } /// This has no effect on the real code but capturing 'a' and 'b' into parameters makes assertion failures much easier inspect in a debugger. -pub inline fn assert_neql(a: anytype, b: anytype) void { +pub fn assert_neql(a: anytype, b: anytype) callconv(callconv_inline) void { return assert(a != b); } -pub inline fn unsafeAssert(condition: bool) void { +pub fn unsafeAssert(condition: bool) callconv(callconv_inline) void { if (!condition) { unreachable; } @@ -3545,3 +3689,99 @@ pub fn OrdinalT(comptime Int: type) type { /// ABI-equivalent of WTF::OrdinalNumber pub const Ordinal = OrdinalT(c_int); + +pub fn memmove(output: []u8, input: []const u8) void { + if (@intFromPtr(output.ptr) == @intFromPtr(input.ptr) or output.len == 0) return; + if (comptime Environment.allow_assert) { + assert(output.len >= input.len and output.len > 0); + } + + const does_input_or_output_overlap = (@intFromPtr(input.ptr) < @intFromPtr(output.ptr) and + @intFromPtr(input.ptr) + input.len > @intFromPtr(output.ptr)) or + (@intFromPtr(output.ptr) < @intFromPtr(input.ptr) and + @intFromPtr(output.ptr) + output.len > @intFromPtr(input.ptr)); + + if (!does_input_or_output_overlap) { + @memcpy(output[0..input.len], input); + } else if (comptime Environment.isNative) { + C.memmove(output.ptr, input.ptr, input.len); + } else { + for (input, output) |input_byte, *out| { + out.* = input_byte; + } + } +} + +pub const hmac = @import("./hmac.zig"); +pub const libdeflate = @import("./deps/libdeflate.zig"); + +pub const kit = @import("kit/kit.zig"); + +/// like std.enums.tagName, except it doesn't lose the sentinel value. +pub fn tagName(comptime Enum: type, value: Enum) ?[:0]const u8 { + return inline for (@typeInfo(Enum).Enum.fields) |f| { + if (@intFromEnum(value) == f.value) break f.name; + } else null; +} +extern "C" fn Bun__ramSize() usize; +pub fn getTotalMemorySize() usize { + return Bun__ramSize(); +} + +pub const WeakPtrData = packed struct(u32) { + reference_count: u31 = 0, + finalized: bool = false, + + pub fn onFinalize(this: *WeakPtrData) bool { + bun.debugAssert(!this.finalized); + this.finalized = true; + return this.reference_count == 0; + } +}; + +pub fn WeakPtr(comptime T: type, comptime weakable_field: std.meta.FieldEnum(T)) type { + return struct { + const WeakRef = @This(); + + value: ?*T = null, + pub fn create(req: *T) WeakRef { + bun.debugAssert(!@field(req, @tagName(weakable_field)).finalized); + @field(req, @tagName(weakable_field)).reference_count += 1; + return .{ .value = req }; + } + + comptime { + if (@TypeOf(@field(@as(T, undefined), @tagName(weakable_field))) != WeakPtrData) { + @compileError("Expected " ++ @typeName(T) ++ " to have a " ++ @typeName(WeakPtrData) ++ " field named " ++ @tagName(weakable_field)); + } + } + + fn deinitInternal(this: *WeakRef, value: *T) void { + const weak_data: *WeakPtrData = &@field(value, @tagName(weakable_field)); + + this.value = null; + const count = weak_data.reference_count - 1; + weak_data.reference_count = count; + if (weak_data.finalized and count == 0) { + value.destroy(); + } + } + + pub fn deinit(this: *WeakRef) void { + if (this.value) |value| { + this.deinitInternal(value); + } + } + + pub fn get(this: *WeakRef) ?*T { + if (this.value) |value| { + if (!@field(value, @tagName(weakable_field)).finalized) { + return value; + } + + this.deinitInternal(value); + } + return null; + } + }; +} diff --git a/src/bun_js.zig b/src/bun_js.zig index 1147d08d72248..7144c3ae49b7d 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -44,13 +44,14 @@ pub const Run = struct { pub fn bootStandalone(ctx: Command.Context, entry_path: string, graph: bun.StandaloneModuleGraph) !void { JSC.markBinding(@src()); - bun.JSC.initialize(); + bun.JSC.initialize(false); + bun.Analytics.Features.standalone_executable += 1; const graph_ptr = try bun.default_allocator.create(bun.StandaloneModuleGraph); graph_ptr.* = graph; - js_ast.Expr.Data.Store.create(default_allocator); - js_ast.Stmt.Data.Store.create(default_allocator); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); var arena = try Arena.init(); if (!ctx.debug.loaded_bunfig) { @@ -88,6 +89,7 @@ pub const Run = struct { b.options.minify_identifiers = ctx.bundler_options.minify_identifiers; b.options.minify_whitespace = ctx.bundler_options.minify_whitespace; + b.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations; b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers; b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace; @@ -114,14 +116,42 @@ pub const Run = struct { AsyncHTTP.loadEnv(vm.allocator, vm.log, b.env); - vm.loadExtraEnv(); + vm.loadExtraEnvAndSourceCodePrinter(); vm.is_main_thread = true; JSC.VirtualMachine.is_main_thread_vm = true; + doPreconnect(ctx.runtime_options.preconnect); + const callback = OpaqueWrap(Run, Run.start); vm.global.vm().holdAPILock(&run, callback); } + fn doPreconnect(preconnect: []const string) void { + if (preconnect.len == 0) return; + bun.HTTPThread.init(); + + for (preconnect) |url_str| { + const url = bun.URL.parse(url_str); + + if (!url.isHTTP() and !url.isHTTPS()) { + Output.errGeneric("preconnect URL must be HTTP or HTTPS: {}", .{bun.fmt.quote(url_str)}); + Global.exit(1); + } + + if (url.hostname.len == 0) { + Output.errGeneric("preconnect URL must have a hostname: {}", .{bun.fmt.quote(url_str)}); + Global.exit(1); + } + + if (!url.hasValidPort()) { + Output.errGeneric("preconnect URL must have a valid port: {}", .{bun.fmt.quote(url_str)}); + Global.exit(1); + } + + AsyncHTTP.preconnect(url, false); + } + } + fn bootBunShell(ctx: Command.Context, entry_path: []const u8) !bun.shell.ExitCode { @setCold(true); @@ -145,17 +175,18 @@ pub const Run = struct { try bun.CLI.Arguments.loadConfigPath(ctx.allocator, true, "bunfig.toml", ctx, .RunCommand); } + // The shell does not need to initialize JSC. + // JSC initialization costs 1-3ms. We skip this if we know it's a shell script. if (strings.endsWithComptime(entry_path, ".sh")) { const exit_code = try bootBunShell(ctx, entry_path); - Global.exitWide(exit_code); + Global.exit(exit_code); return; } - // The shell does not need to initialize JSC. - // JSC initialization costs 1-3ms - bun.JSC.initialize(); - js_ast.Expr.Data.Store.create(default_allocator); - js_ast.Stmt.Data.Store.create(default_allocator); + bun.JSC.initialize(ctx.runtime_options.eval.eval_and_print); + + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); var arena = try Arena.init(); run = .{ @@ -204,6 +235,7 @@ pub const Run = struct { b.options.minify_identifiers = ctx.bundler_options.minify_identifiers; b.options.minify_whitespace = ctx.bundler_options.minify_whitespace; + b.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations; b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers; b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace; @@ -229,7 +261,7 @@ pub const Run = struct { AsyncHTTP.loadEnv(vm.allocator, vm.log, b.env); - vm.loadExtraEnv(); + vm.loadExtraEnvAndSourceCodePrinter(); vm.is_main_thread = true; JSC.VirtualMachine.is_main_thread_vm = true; @@ -242,12 +274,14 @@ pub const Run = struct { vm.bundler.env.loadTracy(); + doPreconnect(ctx.runtime_options.preconnect); + const callback = OpaqueWrap(Run, Run.start); vm.global.vm().holdAPILock(&run, callback); } fn onUnhandledRejectionBeforeClose(this: *JSC.VirtualMachine, _: *JSC.JSGlobalObject, value: JSC.JSValue) void { - this.runErrorHandler(value, null); + this.runErrorHandler(value, this.onUnhandledRejectionExceptionList); run.any_unhandled = true; } @@ -273,7 +307,7 @@ pub const Run = struct { } if (vm.loadEntryPoint(this.entry_path)) |promise| { - if (promise.status(vm.global.vm()) == .Rejected) { + if (promise.status(vm.global.vm()) == .rejected) { const handled = vm.uncaughtException(vm.global, promise.result(vm.global.vm()), true); if (vm.hot_reload != .none or handled) { @@ -291,7 +325,7 @@ pub const Run = struct { .{Global.unhandled_error_bun_version_string}, ); } - Global.exit(1); + vm.globalExit(); } } @@ -324,7 +358,7 @@ pub const Run = struct { .{Global.unhandled_error_bun_version_string}, ); } - Global.exit(1); + vm.globalExit(); } } @@ -341,7 +375,7 @@ pub const Run = struct { { if (this.vm.isWatcherEnabled()) { var prev_promise = this.vm.pending_internal_promise; - if (prev_promise.status(vm.global.vm()) == .Rejected) { + if (prev_promise.status(vm.global.vm()) == .rejected) { _ = vm.unhandledRejection(this.vm.global, this.vm.pending_internal_promise.result(vm.global.vm()), this.vm.pending_internal_promise.asValue()); } @@ -350,7 +384,7 @@ pub const Run = struct { vm.tick(); // Report exceptions in hot-reloaded modules - if (this.vm.pending_internal_promise.status(vm.global.vm()) == .Rejected and prev_promise != this.vm.pending_internal_promise) { + if (this.vm.pending_internal_promise.status(vm.global.vm()) == .rejected and prev_promise != this.vm.pending_internal_promise) { prev_promise = this.vm.pending_internal_promise; _ = vm.unhandledRejection(this.vm.global, this.vm.pending_internal_promise.result(vm.global.vm()), this.vm.pending_internal_promise.asValue()); continue; @@ -361,7 +395,7 @@ pub const Run = struct { vm.onBeforeExit(); - if (this.vm.pending_internal_promise.status(vm.global.vm()) == .Rejected and prev_promise != this.vm.pending_internal_promise) { + if (this.vm.pending_internal_promise.status(vm.global.vm()) == .rejected and prev_promise != this.vm.pending_internal_promise) { prev_promise = this.vm.pending_internal_promise; _ = vm.unhandledRejection(this.vm.global, this.vm.pending_internal_promise.result(vm.global.vm()), this.vm.pending_internal_promise.asValue()); } @@ -369,7 +403,7 @@ pub const Run = struct { vm.eventLoop().tickPossiblyForever(); } - if (this.vm.pending_internal_promise.status(vm.global.vm()) == .Rejected and prev_promise != this.vm.pending_internal_promise) { + if (this.vm.pending_internal_promise.status(vm.global.vm()) == .rejected and prev_promise != this.vm.pending_internal_promise) { prev_promise = this.vm.pending_internal_promise; _ = vm.unhandledRejection(this.vm.global, this.vm.pending_internal_promise.result(vm.global.vm()), this.vm.pending_internal_promise.asValue()); } @@ -384,7 +418,7 @@ pub const Run = struct { const result = vm.entry_point_result.value.get() orelse .undefined; if (result.asAnyPromise()) |promise| { switch (promise.status(vm.jsc)) { - .Pending => { + .pending => { result._then(vm.global, .undefined, Bun__onResolveEntryPointResult, Bun__onRejectEntryPointResult); vm.tick(); @@ -418,6 +452,8 @@ pub const Run = struct { vm.onUnhandledRejection = &onUnhandledRejectionBeforeClose; vm.global.handleRejectedPromises(); + vm.onExit(); + if (this.any_unhandled and this.vm.exit_handler.exit_code == 0) { this.vm.exit_handler.exit_code = 1; @@ -428,16 +464,13 @@ pub const Run = struct { .{Global.unhandled_error_bun_version_string}, ); } - const exit_code = this.vm.exit_handler.exit_code; - - vm.onExit(); if (!JSC.is_bindgen) JSC.napi.fixDeadCodeElimination(); - Global.exit(exit_code); + vm.globalExit(); } }; -pub export fn Bun__onResolveEntryPointResult(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) noreturn { +pub export fn Bun__onResolveEntryPointResult(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) noreturn { const arguments = callframe.arguments(1).slice(); const result = arguments[0]; result.print(global, .Log, .Log); @@ -445,7 +478,7 @@ pub export fn Bun__onResolveEntryPointResult(global: *JSC.JSGlobalObject, callfr return .undefined; } -pub export fn Bun__onRejectEntryPointResult(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) noreturn { +pub export fn Bun__onRejectEntryPointResult(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) noreturn { const arguments = callframe.arguments(1).slice(); const result = arguments[0]; result.print(global, .Log, .Log); diff --git a/src/bundler.zig b/src/bundler.zig index c7177379ebc03..4c09202e2bdb7 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -439,11 +439,10 @@ pub const Bundler = struct { opts: Api.TransformOptions, env_loader_: ?*DotEnv.Loader, ) !Bundler { - js_ast.Expr.Data.Store.create(allocator); - js_ast.Stmt.Data.Store.create(allocator); - const fs = try Fs.FileSystem.init( - opts.absolute_working_dir, - ); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); + + const fs = try Fs.FileSystem.init(opts.absolute_working_dir); const bundle_options = try options.BundleOptions.fromApi( allocator, fs, @@ -576,8 +575,8 @@ pub const Bundler = struct { this.options.jsx.setProduction(this.env.isProduction()); - js_ast.Expr.Data.Store.create(this.allocator); - js_ast.Stmt.Data.Store.create(this.allocator); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); defer js_ast.Expr.Data.Store.reset(); defer js_ast.Stmt.Data.Store.reset(); @@ -595,18 +594,6 @@ pub const Bundler = struct { if (this.options.define.dots.get("NODE_ENV")) |NODE_ENV| { if (NODE_ENV.len > 0 and NODE_ENV[0].data.value == .e_string and NODE_ENV[0].data.value.e_string.eqlComptime("production")) { this.options.production = true; - - if (this.options.target.isBun()) { - if (strings.eqlComptime(this.options.jsx.package_name, "react")) { - if (this.options.jsx_optimization_inline == null) { - this.options.jsx_optimization_inline = true; - } - - if (this.options.jsx_optimization_hoist == null and (this.options.jsx_optimization_inline orelse false)) { - this.options.jsx_optimization_hoist = true; - } - } - } } } } @@ -640,7 +627,7 @@ pub const Bundler = struct { framework.resolved = true; this.options.framework = framework.*; } else if (!framework.resolved) { - Global.panic("directly passing framework path is not implemented yet!", .{}); + Output.panic("directly passing framework path is not implemented yet!", .{}); } } } @@ -1145,6 +1132,7 @@ pub const Bundler = struct { .minify_identifiers = bundler.options.minify_identifiers, .transform_only = bundler.options.transform_only, .runtime_transpiler_cache = runtime_transpiler_cache, + .print_dce_annotations = bundler.options.emit_dce_annotations, }, enable_source_map, ), @@ -1168,6 +1156,7 @@ pub const Bundler = struct { .transform_only = bundler.options.transform_only, .import_meta_ref = ast.import_meta_ref, .runtime_transpiler_cache = runtime_transpiler_cache, + .print_dce_annotations = bundler.options.emit_dce_annotations, }, enable_source_map, ), @@ -1200,6 +1189,8 @@ pub const Bundler = struct { .inline_require_and_import_errors = false, .import_meta_ref = ast.import_meta_ref, .runtime_transpiler_cache = runtime_transpiler_cache, + .target = bundler.options.target, + .print_dce_annotations = bundler.options.emit_dce_annotations, }, enable_source_map, ), @@ -1235,6 +1226,18 @@ pub const Bundler = struct { comptime format: js_printer.Format, handler: js_printer.SourceMapHandler, ) !usize { + if (bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS")) { + return bundler.printWithSourceMapMaybe( + result.ast, + &result.source, + Writer, + writer, + format, + false, + handler, + result.runtime_transpiler_cache, + ); + } return bundler.printWithSourceMapMaybe( result.ast, &result.source, @@ -1384,17 +1387,17 @@ pub const Bundler = struct { var opts = js_parser.Parser.Options.init(jsx, loader); - opts.legacy_transform_require_to_import = bundler.options.allow_runtime and !bundler.options.target.isBun(); opts.features.emit_decorator_metadata = this_parse.emit_decorator_metadata; opts.features.allow_runtime = bundler.options.allow_runtime; opts.features.set_breakpoint_on_first_line = this_parse.set_breakpoint_on_first_line; opts.features.trim_unused_imports = bundler.options.trim_unused_imports orelse loader.isTypeScript(); - opts.features.should_fold_typescript_constant_expressions = loader.isTypeScript() or target.isBun() or bundler.options.minify_syntax; opts.features.use_import_meta_require = target.isBun(); opts.features.no_macros = bundler.options.no_macros; opts.features.runtime_transpiler_cache = this_parse.runtime_transpiler_cache; opts.transform_only = bundler.options.transform_only; + opts.ignore_dce_annotations = bundler.options.ignore_dce_annotations; + // @bun annotation opts.features.dont_bundle_twice = this_parse.dont_bundle_twice; @@ -1409,13 +1412,7 @@ pub const Bundler = struct { opts.filepath_hash_for_hmr = file_hash orelse 0; opts.features.auto_import_jsx = bundler.options.auto_import_jsx; opts.warn_about_unbundled_modules = target.isNotBun(); - opts.features.jsx_optimization_inline = opts.features.allow_runtime and - (bundler.options.jsx_optimization_inline orelse (target.isBun() and jsx.parse and - !jsx.development)) and - (jsx.runtime == .automatic or jsx.runtime == .classic) and - strings.eqlComptime(jsx.import_source.production, "react/jsx-runtime"); - opts.features.jsx_optimization_hoist = bundler.options.jsx_optimization_hoist orelse opts.features.jsx_optimization_inline; opts.features.inject_jest_globals = this_parse.inject_jest_globals; opts.features.minify_syntax = bundler.options.minify_syntax; opts.features.minify_identifiers = bundler.options.minify_identifiers; @@ -1651,7 +1648,7 @@ pub const Bundler = struct { } }, .css => {}, - else => Global.panic("Unsupported loader {s} for path: {s}", .{ @tagName(loader), source.path.text }), + else => Output.panic("Unsupported loader {s} for path: {s}", .{ @tagName(loader), source.path.text }), } return null; diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 02dde1721d248..a3e635caae7a4 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -126,6 +126,8 @@ const debugTreeShake = Output.scoped(.TreeShake, true); const BitSet = bun.bit_set.DynamicBitSetUnmanaged; const Async = bun.Async; +const logPartDependencyTree = Output.scoped(.part_dep_tree, false); + fn tracer(comptime src: std.builtin.SourceLocation, comptime name: [:0]const u8) bun.tracy.Ctx { return bun.tracy.traceNamed(src, "Bundler." ++ name); } @@ -133,7 +135,7 @@ fn tracer(comptime src: std.builtin.SourceLocation, comptime name: [:0]const u8) pub const ThreadPool = struct { pool: *ThreadPoolLib = undefined, workers_assignments: std.AutoArrayHashMap(std.Thread.Id, *Worker) = std.AutoArrayHashMap(std.Thread.Id, *Worker).init(bun.default_allocator), - workers_assignments_lock: bun.Lock = bun.Lock.init(), + workers_assignments_lock: bun.Lock = .{}, v2: *BundleV2 = undefined, @@ -328,8 +330,9 @@ pub const BundleV2 = struct { graph: Graph = Graph{}, linker: LinkerContext = LinkerContext{ .loop = undefined }, bun_watcher: ?*Watcher.Watcher = null, + // kit_watcher: ?*bun.kit.DevServer.HotReloader.Watcher = null, plugins: ?*JSC.API.JSBundler.Plugin = null, - completion: ?*JSBundleCompletionTask = null, + completion: ?CompletionPtr = null, source_code_length: usize = 0, // There is a race condition where an onResolve plugin may schedule a task on the bundle thread before it's parsing task completes @@ -341,6 +344,17 @@ pub const BundleV2 = struct { unique_key: u64 = 0, dynamic_import_entry_points: std.AutoArrayHashMap(Index.Int, void) = undefined, + pub const CompletionPtr = union(enum) { + js: *JSBundleCompletionTask, + kit: *bun.kit.DevServer.BundleTask, + + pub fn log(ptr: CompletionPtr) *bun.logger.Log { + return switch (ptr) { + inline else => |inner| &inner.log, + }; + } + }; + const debug = Output.scoped(.Bundle, false); pub inline fn loop(this: *BundleV2) *EventLoop { @@ -481,7 +495,7 @@ pub const BundleV2 = struct { ) catch |err| { var handles_import_errors = false; var source: ?*const Logger.Source = null; - const log = &this.completion.?.log; + const log = this.completion.?.log(); if (import_record.importer_source_index) |importer| { var record: *ImportRecord = &this.graph.ast.items(.import_records)[importer].slice()[import_record.import_record_index]; @@ -568,7 +582,7 @@ pub const BundleV2 = struct { if (path.pretty.ptr == path.text.ptr) { // TODO: outbase const rel = bun.path.relativePlatform(this.bundler.fs.top_level_dir, path.text, .loose, false); - path.pretty = this.graph.allocator.dupe(u8, rel) catch @panic("Ran out of memory"); + path.pretty = this.graph.allocator.dupe(u8, rel) catch bun.outOfMemory(); } path.assertPrettyIsValid(); @@ -578,13 +592,13 @@ pub const BundleV2 = struct { secondary != path and !strings.eqlLong(secondary.text, path.text, true)) { - secondary_path_to_copy = secondary.dupeAlloc(this.graph.allocator) catch @panic("Ran out of memory"); + secondary_path_to_copy = secondary.dupeAlloc(this.graph.allocator) catch bun.outOfMemory(); } } - const entry = this.graph.path_to_source_index_map.getOrPut(this.graph.allocator, path.hashKey()) catch @panic("Ran out of memory"); + const entry = this.graph.path_to_source_index_map.getOrPut(this.graph.allocator, path.hashKey()) catch bun.outOfMemory(); if (!entry.found_existing) { - path.* = path.dupeAllocFixPretty(this.graph.allocator) catch @panic("Ran out of memory"); + path.* = path.dupeAllocFixPretty(this.graph.allocator) catch bun.outOfMemory(); // We need to parse this const source_index = Index.init(@as(u32, @intCast(this.graph.ast.len))); @@ -614,8 +628,8 @@ pub const BundleV2 = struct { .text, .json, .toml, .file => _resolver.SideEffects.no_side_effects__pure_data, else => _resolver.SideEffects.has_side_effects, }, - }) catch @panic("Ran out of memory"); - var task = this.graph.allocator.create(ParseTask) catch @panic("Ran out of memory"); + }) catch bun.outOfMemory(); + var task = this.graph.allocator.create(ParseTask) catch bun.outOfMemory(); task.* = ParseTask.init(&resolve_result, source_index, this); task.loader = loader; task.jsx = this.bundler.options.jsx; @@ -653,23 +667,23 @@ pub const BundleV2 = struct { hash: ?u64, batch: *ThreadPoolLib.Batch, resolve: _resolver.Result, + is_entry_point: bool, ) !?Index.Int { var result = resolve; var path = result.path() orelse return null; - const loader = this.bundler.options.loaders.get(path.name.ext) orelse .file; - const entry = try this.graph.path_to_source_index_map.getOrPut(this.graph.allocator, hash orelse path.hashKey()); if (entry.found_existing) { return null; } _ = @atomicRmw(usize, &this.graph.parse_pending, .Add, 1, .monotonic); const source_index = Index.source(this.graph.input_files.len); + const loader = this.bundler.options.loaders.get(path.name.ext) orelse .file; if (path.pretty.ptr == path.text.ptr) { // TODO: outbase const rel = bun.path.relativePlatform(this.bundler.fs.top_level_dir, path.text, .loose, false); - path.pretty = this.graph.allocator.dupe(u8, rel) catch @panic("Ran out of memory"); + path.pretty = this.graph.allocator.dupe(u8, rel) catch bun.outOfMemory(); } path.* = try path.dupeAllocFixPretty(this.graph.allocator); path.assertPrettyIsValid(); @@ -691,6 +705,7 @@ pub const BundleV2 = struct { task.loader = loader; task.task.node.next = null; task.tree_shaking = this.linker.options.tree_shaking; + task.is_entry_point = is_entry_point; // Handle onLoad plugins as entry points if (!this.enqueueOnLoadPluginIfNeeded(task)) { @@ -717,12 +732,11 @@ pub const BundleV2 = struct { ) !*BundleV2 { bundler.env.loadTracy(); - var generator = try allocator.create(BundleV2); + const this = try allocator.create(BundleV2); bundler.options.mark_builtins_as_external = bundler.options.target.isBun() or bundler.options.target == .node; bundler.resolver.opts.mark_builtins_as_external = bundler.options.target.isBun() or bundler.options.target == .node; - const this = generator; - generator.* = BundleV2{ + this.* = .{ .bundler = bundler, .client_bundler = bundler, .server_bundler = bundler, @@ -735,47 +749,68 @@ pub const BundleV2 = struct { .loop = event_loop, .graph = .{ .allocator = undefined, + .bundler_graph = undefined, }, }, }; - generator.linker.graph.allocator = generator.graph.heap.allocator(); - generator.graph.allocator = generator.linker.graph.allocator; - generator.bundler.allocator = generator.graph.allocator; - generator.bundler.resolver.allocator = generator.graph.allocator; - generator.bundler.linker.allocator = generator.graph.allocator; - generator.bundler.log.msgs.allocator = generator.graph.allocator; - generator.bundler.log.clone_line_text = true; - - // We don't expose a way to disable this right now. - generator.bundler.options.tree_shaking = true; - generator.bundler.resolver.opts.tree_shaking = true; - - generator.linker.resolver = &generator.bundler.resolver; - generator.linker.graph.code_splitting = bundler.options.code_splitting; - generator.graph.code_splitting = bundler.options.code_splitting; - - generator.linker.options.minify_syntax = bundler.options.minify_syntax; - generator.linker.options.minify_identifiers = bundler.options.minify_identifiers; - generator.linker.options.minify_whitespace = bundler.options.minify_whitespace; - generator.linker.options.source_maps = bundler.options.source_map; - generator.linker.options.tree_shaking = bundler.options.tree_shaking; - generator.linker.options.public_path = bundler.options.public_path; - - var pool = try generator.graph.allocator.create(ThreadPool); + this.linker.graph.allocator = this.graph.heap.allocator(); + this.graph.allocator = this.linker.graph.allocator; + this.bundler.allocator = this.graph.allocator; + this.bundler.resolver.allocator = this.graph.allocator; + this.bundler.linker.allocator = this.graph.allocator; + this.bundler.log.msgs.allocator = this.graph.allocator; + this.bundler.log.clone_line_text = true; + + // We don't expose an option to disable this. Kit requires tree-shaking + // disabled since every export is always referenced in case a future + // module depends on a previously unused export. + if (this.bundler.options.output_format == .internal_kit_dev) { + this.bundler.options.tree_shaking = false; + this.bundler.resolver.opts.tree_shaking = false; + } else { + this.bundler.options.tree_shaking = true; + this.bundler.resolver.opts.tree_shaking = true; + } + + this.linker.graph.bundler_graph = &this.graph; + this.linker.resolver = &this.bundler.resolver; + this.linker.graph.code_splitting = bundler.options.code_splitting; + this.graph.code_splitting = bundler.options.code_splitting; + + this.linker.options.minify_syntax = bundler.options.minify_syntax; + this.linker.options.minify_identifiers = bundler.options.minify_identifiers; + this.linker.options.minify_whitespace = bundler.options.minify_whitespace; + this.linker.options.emit_dce_annotations = bundler.options.emit_dce_annotations; + this.linker.options.ignore_dce_annotations = bundler.options.ignore_dce_annotations; + + this.linker.options.source_maps = bundler.options.source_map; + this.linker.options.tree_shaking = bundler.options.tree_shaking; + this.linker.options.public_path = bundler.options.public_path; + this.linker.options.target = bundler.options.target; + this.linker.options.output_format = bundler.options.output_format; + + var pool = try this.graph.allocator.create(ThreadPool); if (enable_reloading) { - Watcher.enableHotModuleReloading(generator); + Watcher.enableHotModuleReloading(this); } // errdefer pool.destroy(); - errdefer generator.graph.heap.deinit(); + errdefer this.graph.heap.deinit(); pool.* = ThreadPool{}; - generator.graph.pool = pool; + this.graph.pool = pool; try pool.start( this, thread_pool, ); - return generator; + // sanity checks for kit + if (this.bundler.options.output_format == .internal_kit_dev) { + if (this.bundler.options.compile) @panic("TODO: internal_kit_dev does not support compile"); + if (this.bundler.options.code_splitting) @panic("TODO: internal_kit_dev does not support code splitting"); + if (this.bundler.options.transform_only) @panic("TODO: internal_kit_dev does not support transform_only"); + } + + return this; } pub fn enqueueEntryPoints(this: *BundleV2, user_entry_points: []const string) !ThreadPoolLib.Batch { @@ -816,7 +851,7 @@ pub const BundleV2 = struct { for (entry_points) |entry_point| { const resolved = this.bundler.resolveEntryPoint(entry_point) catch continue; - if (try this.enqueueItem(null, &batch, resolved)) |source_index| { + if (try this.enqueueItem(null, &batch, resolved, true)) |source_index| { this.graph.entry_points.append(this.graph.allocator, Index.source(source_index)) catch unreachable; } else {} } @@ -830,7 +865,7 @@ pub const BundleV2 = struct { for (user_entry_points) |entry_point| { const resolved = this.bundler.resolveEntryPoint(entry_point) catch continue; - if (try this.enqueueItem(null, &batch, resolved)) |source_index| { + if (try this.enqueueItem(null, &batch, resolved, true)) |source_index| { this.graph.entry_points.append(this.graph.allocator, Index.source(source_index)) catch unreachable; } else {} } @@ -1133,6 +1168,8 @@ pub const BundleV2 = struct { } } + pub const JSBundleThread = BundleThread(JSBundleCompletionTask); + pub fn generateFromJavaScript( config: bun.JSC.API.JSBundler.Config, plugins: ?*bun.JSC.API.JSBundler.Plugin, @@ -1160,19 +1197,7 @@ pub const BundleV2 = struct { // conditions from creating two _ = JSC.WorkPool.get(); - if (BundleThread.instance) |existing| { - existing.queue.push(completion); - existing.waker.?.wake(); - } else { - var instance = bun.default_allocator.create(BundleThread) catch unreachable; - instance.queue = .{}; - instance.waker = null; - instance.queue.push(completion); - BundleThread.instance = instance; - - var thread = try std.Thread.spawn(.{}, generateInNewThreadWrap, .{instance}); - thread.detach(); - } + JSBundleThread.singleton.enqueue(completion); completion.poll_ref.ref(globalThis.bunVM()); @@ -1183,6 +1208,12 @@ pub const BundleV2 = struct { output_files: std.ArrayList(options.OutputFile), }; + pub const Result = union(enum) { + pending: void, + err: anyerror, + value: BuildResult, + }; + pub const JSBundleCompletionTask = struct { config: bun.JSC.API.JSBundler.Config, jsc_event_loop: *bun.JSC.EventLoop, @@ -1200,11 +1231,67 @@ pub const BundleV2 = struct { plugins: ?*bun.JSC.API.JSBundler.Plugin = null, ref_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(1), - pub const Result = union(enum) { - pending: void, - err: anyerror, - value: BuildResult, - }; + pub fn configureBundler( + completion: *JSBundleCompletionTask, + bundler: *Bundler, + allocator: std.mem.Allocator, + ) !void { + const config = &completion.config; + + bundler.* = try bun.Bundler.init( + allocator, + &completion.log, + Api.TransformOptions{ + .define = if (config.define.count() > 0) config.define.toAPI() else null, + .entry_points = config.entry_points.keys(), + .target = config.target.toAPI(), + .absolute_working_dir = if (config.dir.list.items.len > 0) + config.dir.toOwnedSliceLeaky() + else + null, + .inject = &.{}, + .external = config.external.keys(), + .main_fields = &.{}, + .extension_order = &.{}, + .env_files = &.{}, + .conditions = config.conditions.map.keys(), + .ignore_dce_annotations = bundler.options.ignore_dce_annotations, + }, + completion.env, + ); + + bundler.options.entry_points = config.entry_points.keys(); + bundler.options.jsx = config.jsx; + bundler.options.no_macros = config.no_macros; + bundler.options.react_server_components = config.server_components.client.items.len > 0 or config.server_components.server.items.len > 0; + bundler.options.loaders = try options.loadersFromTransformOptions(allocator, config.loaders, config.target); + bundler.options.entry_naming = config.names.entry_point.data; + bundler.options.chunk_naming = config.names.chunk.data; + bundler.options.asset_naming = config.names.asset.data; + + bundler.options.public_path = config.public_path.list.items; + + bundler.options.output_dir = config.outdir.toOwnedSliceLeaky(); + bundler.options.root_dir = config.rootdir.toOwnedSliceLeaky(); + bundler.options.minify_syntax = config.minify.syntax; + bundler.options.minify_whitespace = config.minify.whitespace; + bundler.options.minify_identifiers = config.minify.identifiers; + bundler.options.inlining = config.minify.syntax; + bundler.options.source_map = config.source_map; + bundler.options.packages = config.packages; + bundler.options.code_splitting = config.code_splitting; + bundler.options.emit_dce_annotations = config.emit_dce_annotations orelse !config.minify.whitespace; + bundler.options.ignore_dce_annotations = config.ignore_dce_annotations; + + bundler.configureLinker(); + try bundler.configureDefines(); + + bundler.resolver.opts = bundler.options; + } + + pub fn completeOnBundleThread(completion: *JSBundleCompletionTask) void { + completion.jsc_event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.create(completion.task.task())); + } pub const TaskCompletion = bun.JSC.AnyTask.New(JSBundleCompletionTask, onComplete); @@ -1370,7 +1457,7 @@ pub const BundleV2 = struct { bun.fmt.quote(source.path.namespace), }) catch {}; - // An error ocurred, prevent spinning the event loop forever + // An error occurred, prevent spinning the event loop forever _ = @atomicRmw(usize, &this.graph.parse_pending, .Sub, 1, .monotonic); }, .success => |code| { @@ -1389,7 +1476,7 @@ pub const BundleV2 = struct { log.errors += @as(usize, @intFromBool(err.kind == .err)); log.warnings += @as(usize, @intFromBool(err.kind == .warn)); - // An error ocurred, prevent spinning the event loop forever + // An error occurred, prevent spinning the event loop forever _ = @atomicRmw(usize, &this.graph.parse_pending, .Sub, 1, .monotonic); }, .pending, .consumed => unreachable, @@ -1549,151 +1636,6 @@ pub const BundleV2 = struct { } } - pub fn timerCallback(_: *bun.windows.libuv.Timer) callconv(.C) void {} - - pub fn generateInNewThreadWrap(instance: *BundleThread) void { - Output.Source.configureNamedThread("Bundler"); - - instance.waker = bun.Async.Waker.init() catch @panic("Failed to create waker"); - - var timer: bun.windows.libuv.Timer = undefined; - if (bun.Environment.isWindows) { - timer.init(instance.waker.?.loop.uv_loop); - timer.start(std.math.maxInt(u64), std.math.maxInt(u64), &timerCallback); - } - - var has_bundled = false; - while (true) { - while (instance.queue.pop()) |completion| { - generateInNewThread(completion, instance.generation) catch |err| { - completion.result = .{ .err = err }; - const concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch bun.outOfMemory(); - concurrent_task.* = JSC.ConcurrentTask{ - .auto_delete = true, - .task = completion.task.task(), - .next = null, - }; - completion.jsc_event_loop.enqueueTaskConcurrent(concurrent_task); - }; - has_bundled = true; - } - instance.generation +|= 1; - - if (has_bundled) { - bun.Mimalloc.mi_collect(false); - has_bundled = false; - } - - _ = instance.waker.?.wait(); - } - } - - pub const BundleThread = struct { - waker: ?bun.Async.Waker, - queue: bun.UnboundedQueue(JSBundleCompletionTask, .next) = .{}, - generation: bun.Generation = 0, - - pub var instance: ?*BundleThread = undefined; - }; - - fn generateInNewThread( - completion: *JSBundleCompletionTask, - generation: bun.Generation, - ) !void { - var heap = try ThreadlocalArena.init(); - defer heap.deinit(); - - const allocator = heap.allocator(); - var ast_memory_allocator = try allocator.create(js_ast.ASTMemoryAllocator); - ast_memory_allocator.* = .{ - .allocator = allocator, - }; - ast_memory_allocator.reset(); - ast_memory_allocator.push(); - - const config = &completion.config; - var bundler = try allocator.create(bun.Bundler); - - bundler.* = try bun.Bundler.init( - allocator, - &completion.log, - Api.TransformOptions{ - .define = if (config.define.count() > 0) config.define.toAPI() else null, - .entry_points = config.entry_points.keys(), - .target = config.target.toAPI(), - .absolute_working_dir = if (config.dir.list.items.len > 0) config.dir.toOwnedSliceLeaky() else null, - .inject = &.{}, - .external = config.external.keys(), - .main_fields = &.{}, - .extension_order = &.{}, - .env_files = &.{}, - .conditions = config.conditions.map.keys(), - }, - completion.env, - ); - bundler.options.jsx = config.jsx; - bundler.options.no_macros = config.no_macros; - bundler.options.react_server_components = config.server_components.client.items.len > 0 or config.server_components.server.items.len > 0; - bundler.options.loaders = try options.loadersFromTransformOptions(allocator, config.loaders, config.target); - bundler.options.entry_naming = config.names.entry_point.data; - bundler.options.chunk_naming = config.names.chunk.data; - bundler.options.asset_naming = config.names.asset.data; - - bundler.options.public_path = config.public_path.list.items; - - bundler.options.output_dir = config.outdir.toOwnedSliceLeaky(); - bundler.options.root_dir = config.rootdir.toOwnedSliceLeaky(); - bundler.options.minify_syntax = config.minify.syntax; - bundler.options.minify_whitespace = config.minify.whitespace; - bundler.options.minify_identifiers = config.minify.identifiers; - bundler.options.inlining = config.minify.syntax; - bundler.options.source_map = config.source_map; - bundler.resolver.generation = generation; - bundler.options.code_splitting = config.code_splitting; - - bundler.configureLinker(); - try bundler.configureDefines(); - - bundler.resolver.opts = bundler.options; - - var this = try BundleV2.init(bundler, allocator, JSC.AnyEventLoop.init(allocator), false, JSC.WorkPool.get(), heap); - this.plugins = completion.plugins; - this.completion = completion; - completion.bundler = this; - - errdefer { - var out_log = Logger.Log.init(bun.default_allocator); - this.bundler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); - completion.log = out_log; - } - - defer { - if (this.graph.pool.pool.threadpool_context == @as(?*anyopaque, @ptrCast(this.graph.pool))) { - this.graph.pool.pool.threadpool_context = null; - } - - ast_memory_allocator.pop(); - this.deinit(); - } - - completion.result = .{ - .value = .{ - .output_files = try this.runFromJSInNewThread(config), - }, - }; - - const concurrent_task = try bun.default_allocator.create(JSC.ConcurrentTask); - concurrent_task.* = JSC.ConcurrentTask{ - .auto_delete = true, - .task = completion.task.task(), - .next = null, - }; - var out_log = Logger.Log.init(bun.default_allocator); - this.bundler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); - completion.log = out_log; - completion.jsc_event_loop.enqueueTaskConcurrent(concurrent_task); - } - pub fn deinit(this: *BundleV2) void { defer this.graph.ast.deinit(bun.default_allocator); defer this.graph.input_files.deinit(bun.default_allocator); @@ -1717,7 +1659,7 @@ pub const BundleV2 = struct { this.free_list.clearAndFree(); } - pub fn runFromJSInNewThread(this: *BundleV2, config: *const bun.JSC.API.JSBundler.Config) !std.ArrayList(options.OutputFile) { + pub fn runFromJSInNewThread(this: *BundleV2, entry_points: []const []const u8) !std.ArrayList(options.OutputFile) { this.unique_key = std.crypto.random.int(u64); if (this.bundler.log.errors > 0) { @@ -1729,7 +1671,7 @@ pub const BundleV2 = struct { bun.Mimalloc.mi_collect(true); } - this.graph.pool.pool.schedule(try this.enqueueEntryPoints(config.entry_points.keys())); + this.graph.pool.pool.schedule(try this.enqueueEntryPoints(entry_points)); // We must wait for all the parse tasks to complete, even if there are errors. this.waitForParse(); @@ -1797,7 +1739,7 @@ pub const BundleV2 = struct { .original_target = original_target orelse this.bundler.options.target, }, }, - this.completion.?, + this.completion.?.js, ); resolve.dispatch(); return true; @@ -1817,7 +1759,7 @@ pub const BundleV2 = struct { }); var load = bun.default_allocator.create(JSC.API.JSBundler.Load) catch unreachable; load.* = JSC.API.JSBundler.Load.create( - this.completion.?, + this.completion.?.js, parse.source_index, parse.path.loader(&this.bundler.options.loaders) orelse options.Loader.js, parse.path, @@ -2009,7 +1951,7 @@ pub const BundleV2 = struct { continue; } - const resolve_entry = resolve_queue.getOrPut(hash_key) catch @panic("Ran out of memory"); + const resolve_entry = resolve_queue.getOrPut(hash_key) catch bun.outOfMemory(); if (resolve_entry.found_existing) { import_record.path = resolve_entry.value_ptr.*.path; @@ -2021,7 +1963,7 @@ pub const BundleV2 = struct { const rel = bun.path.relativePlatform(this.bundler.fs.top_level_dir, path.text, .loose, false); path.pretty = this.graph.allocator.dupe(u8, rel) catch bun.outOfMemory(); } - path.* = path.dupeAllocFixPretty(this.graph.allocator) catch @panic("Ran out of memory"); + path.* = path.dupeAllocFixPretty(this.graph.allocator) catch bun.outOfMemory(); var secondary_path_to_copy: ?Fs.Path = null; if (resolve_result.path_pair.secondary) |*secondary| { @@ -2029,14 +1971,14 @@ pub const BundleV2 = struct { secondary != path and !strings.eqlLong(secondary.text, path.text, true)) { - secondary_path_to_copy = secondary.dupeAlloc(this.graph.allocator) catch @panic("Ran out of memory"); + secondary_path_to_copy = secondary.dupeAlloc(this.graph.allocator) catch bun.outOfMemory(); } } import_record.path = path.*; debug("created ParseTask: {s}", .{path.text}); - var resolve_task = bun.default_allocator.create(ParseTask) catch @panic("Ran out of memory"); + var resolve_task = bun.default_allocator.create(ParseTask) catch bun.outOfMemory(); resolve_task.* = ParseTask.init(&resolve_result, null, this); resolve_task.secondary_path_for_commonjs_interop = secondary_path_to_copy; @@ -2116,9 +2058,9 @@ pub const BundleV2 = struct { }); } - if (this.bun_watcher != null) { + if (this.bun_watcher) |watcher| { if (empty_result.watcher_data.fd != .zero and empty_result.watcher_data.fd != bun.invalid_fd) { - _ = this.bun_watcher.?.addFile( + _ = watcher.addFile( empty_result.watcher_data.fd, input_files.items(.source)[empty_result.source_index.get()].path.text, bun.hash32(input_files.items(.source)[empty_result.source_index.get()].path.text), @@ -2129,15 +2071,28 @@ pub const BundleV2 = struct { ); } } + // else if (this.kit_watcher) |watcher| { + // if (empty_result.watcher_data.fd != .zero and empty_result.watcher_data.fd != bun.invalid_fd) { + // _ = watcher.addFile( + // empty_result.watcher_data.fd, + // input_files.items(.source)[empty_result.source_index.get()].path.text, + // bun.hash32(input_files.items(.source)[empty_result.source_index.get()].path.text), + // graph.input_files.items(.loader)[empty_result.source_index.get()], + // empty_result.watcher_data.dir_fd, + // null, + // false, + // ); + // } + // } }, .success => |*result| { result.log.cloneToWithRecycled(this.bundler.log, true) catch unreachable; { // to minimize contention, we add watcher here - if (this.bun_watcher != null) { + if (this.bun_watcher) |watcher| { if (result.watcher_data.fd != .zero and result.watcher_data.fd != bun.invalid_fd) { - _ = this.bun_watcher.?.addFile( + _ = watcher.addFile( result.watcher_data.fd, result.source.path.text, bun.hash32(result.source.path.text), @@ -2148,6 +2103,19 @@ pub const BundleV2 = struct { ); } } + // else if (this.kit_watcher) |watcher| { + // if (result.watcher_data.fd != .zero and result.watcher_data.fd != bun.invalid_fd) { + // _ = watcher.addFile( + // result.watcher_data.fd, + // result.source.path.text, + // bun.hash32(result.source.path.text), + // result.source.path.loader(&this.bundler.options.loaders) orelse options.Loader.file, + // result.watcher_data.dir_fd, + // result.watcher_data.package_json, + // false, + // ); + // } + // } } // Warning: this array may resize in this function call @@ -2295,6 +2263,182 @@ pub const BundleV2 = struct { } }; +/// Used to keep the bundle thread from spinning on Windows +pub fn timerCallback(_: *bun.windows.libuv.Timer) callconv(.C) void {} + +/// Used for Bun.build and Kit, as they asynchronously schedule multiple +/// bundles. To account for their respective differences, the scheduling code +/// is generalized over the Task structure. +/// +/// - `configureBundler` is used to configure `Bundler`. +/// - `completeOnBundleThread` is used to tell the task that it is done. +/// +pub fn BundleThread(CompletionStruct: type) type { + return struct { + const Self = @This(); + + waker: bun.Async.Waker, + ready_event: std.Thread.ResetEvent, + queue: bun.UnboundedQueue(CompletionStruct, .next), + generation: bun.Generation = 0, + + /// To initialize, put this somewhere in memory, and then call `spawn()` + pub const uninitialized: Self = .{ + .waker = undefined, + .queue = .{}, + .generation = 0, + .ready_event = .{}, + }; + + pub fn spawn(instance: *Self) !std.Thread { + const thread = try std.Thread.spawn(.{}, threadMain, .{instance}); + instance.ready_event.wait(); + return thread; + } + + /// Lazily-initialized singleton. This is used for `Bun.build` since the + /// bundle thread may not be needed. Kit always uses the bundler, so it + /// just initializes `BundleThread` + pub const singleton = struct { + var once = std.once(loadOnceImpl); + var instance: ?*Self = null; + + // Blocks the calling thread until the bun build thread is created. + // std.once also blocks other callers of this function until the first caller is done. + fn loadOnceImpl() void { + const bundle_thread = bun.default_allocator.create(Self) catch bun.outOfMemory(); + bundle_thread.* = uninitialized; + instance = bundle_thread; + + // 2. Spawn the bun build thread. + const os_thread = bundle_thread.spawn() catch + Output.panic("Failed to spawn bun build thread", .{}); + os_thread.detach(); + } + + pub fn get() *Self { + once.call(); + return instance.?; + } + + pub fn enqueue(completion: *CompletionStruct) void { + get().enqueue(completion); + } + }; + + pub fn enqueue(instance: *Self, completion: *CompletionStruct) void { + instance.queue.push(completion); + instance.waker.wake(); + } + + fn threadMain(instance: *Self) void { + Output.Source.configureNamedThread("Bundler"); + + instance.waker = bun.Async.Waker.init() catch @panic("Failed to create waker"); + + // Unblock the calling thread so it can continue. + instance.ready_event.set(); + + var timer: bun.windows.libuv.Timer = undefined; + if (bun.Environment.isWindows) { + timer.init(instance.waker.loop.uv_loop); + timer.start(std.math.maxInt(u64), std.math.maxInt(u64), &timerCallback); + } + + var has_bundled = false; + while (true) { + while (instance.queue.pop()) |completion| { + generateInNewThread(completion, instance.generation) catch |err| { + completion.result = .{ .err = err }; + completion.completeOnBundleThread(); + }; + has_bundled = true; + } + instance.generation +|= 1; + + if (has_bundled) { + bun.Mimalloc.mi_collect(false); + has_bundled = false; + } + + _ = instance.waker.wait(); + } + } + + /// This is called from `Bun.build` in JavaScript. + fn generateInNewThread(completion: *CompletionStruct, generation: bun.Generation) !void { + var heap = try ThreadlocalArena.init(); + defer heap.deinit(); + + const allocator = heap.allocator(); + var ast_memory_allocator = try allocator.create(js_ast.ASTMemoryAllocator); + ast_memory_allocator.* = .{ .allocator = allocator }; + ast_memory_allocator.reset(); + ast_memory_allocator.push(); + + const bundler = try allocator.create(bun.Bundler); + + try completion.configureBundler(bundler, allocator); + + bundler.resolver.generation = generation; + + const this = try BundleV2.init( + bundler, + allocator, + JSC.AnyEventLoop.init(allocator), + false, + JSC.WorkPool.get(), + heap, + ); + + // switch (CompletionStruct) { + // bun.kit.DevServer.BundleTask => { + // this.kit_watcher = completion.route.dev.bun_watcher; + // }, + // else => {}, + // } + + this.plugins = completion.plugins; + this.completion = switch (CompletionStruct) { + BundleV2.JSBundleCompletionTask => .{ .js = completion }, + bun.kit.DevServer.BundleTask => .{ .kit = completion }, + else => @compileError("Unknown completion struct: " ++ CompletionStruct), + }; + completion.bundler = this; + + defer { + if (this.graph.pool.pool.threadpool_context == @as(?*anyopaque, @ptrCast(this.graph.pool))) { + this.graph.pool.pool.threadpool_context = null; + } + + ast_memory_allocator.pop(); + this.deinit(); + } + + errdefer { + // Wait for wait groups to finish. There still may be + this.linker.source_maps.line_offset_wait_group.wait(); + this.linker.source_maps.quoted_contents_wait_group.wait(); + + var out_log = Logger.Log.init(bun.default_allocator); + this.bundler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); + completion.log = out_log; + } + + completion.result = .{ + .value = .{ + .output_files = try this.runFromJSInNewThread(bundler.options.entry_points), + }, + }; + + var out_log = Logger.Log.init(bun.default_allocator); + this.bundler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); + completion.log = out_log; + completion.completeOnBundleThread(); + } + }; +} + const UseDirective = js_ast.UseDirective; pub const ParseTask = struct { @@ -2318,6 +2462,7 @@ pub const ParseTask = struct { emit_decorator_metadata: bool = false, ctx: *BundleV2, package_version: string = "", + is_entry_point: bool = false, /// Used by generated client components presolved_source_indices: []const Index.Int = &.{}, @@ -2491,7 +2636,8 @@ pub const ParseTask = struct { } pub const Result = struct { - task: EventLoop.Task = undefined, + task: EventLoop.Task, + ctx: *BundleV2, value: union(Tag) { success: Success, @@ -2797,11 +2943,11 @@ pub const ParseTask = struct { else null, ) catch |err| { - const source_ = &Logger.Source.initEmptyFile(log.msgs.allocator.dupe(u8, file_path.text) catch unreachable); + const source = &Logger.Source.initEmptyFile(log.msgs.allocator.dupe(u8, file_path.text) catch unreachable); switch (err) { error.ENOENT, error.FileNotFound => { log.addErrorFmt( - source_, + source, Logger.Loc.Empty, allocator, "File not found {}", @@ -2810,7 +2956,7 @@ pub const ParseTask = struct { }, else => { log.addErrorFmt( - source_, + source, Logger.Loc.Empty, allocator, "{s} reading file: {}", @@ -2829,7 +2975,11 @@ pub const ParseTask = struct { errdefer if (task.contents_or_fd == .fd) entry.deinit(allocator); - const will_close_file_descriptor = task.contents_or_fd == .fd and !entry.fd.isStdio() and this.ctx.bun_watcher == null; + const will_close_file_descriptor = task.contents_or_fd == .fd and + !entry.fd.isStdio() and + (this.ctx.bun_watcher == null + // and this.ctx.kit_watcher == null + ); if (will_close_file_descriptor) { _ = entry.closeFD(); } @@ -2860,27 +3010,38 @@ pub const ParseTask = struct { const target = targetFromHashbang(entry.contents) orelse use_directive.target(task.known_target orelse bundler.options.target); var opts = js_parser.Parser.Options.init(task.jsx, loader); - opts.legacy_transform_require_to_import = false; - opts.features.allow_runtime = !source.index.isRuntime(); - opts.features.use_import_meta_require = target.isBun(); + opts.bundle = true; opts.warn_about_unbundled_modules = false; opts.macro_context = &this.data.macro_context; - opts.bundle = true; opts.package_version = task.package_version; + opts.features.allow_runtime = !source.index.isRuntime(); + opts.features.use_import_meta_require = target.isBun(); opts.features.top_level_await = true; - opts.features.jsx_optimization_inline = target.isBun() and (bundler.options.jsx_optimization_inline orelse !task.jsx.development); opts.features.auto_import_jsx = task.jsx.parse and bundler.options.auto_import_jsx; opts.features.trim_unused_imports = loader.isTypeScript() or (bundler.options.trim_unused_imports orelse false); opts.features.inlining = bundler.options.minify_syntax; opts.features.minify_syntax = bundler.options.minify_syntax; opts.features.minify_identifiers = bundler.options.minify_identifiers; - opts.features.should_fold_typescript_constant_expressions = opts.features.inlining or loader.isTypeScript(); opts.features.emit_decorator_metadata = bundler.options.emit_decorator_metadata; + opts.features.unwrap_commonjs_packages = bundler.options.unwrap_commonjs_packages; + opts.features.hot_module_reloading = bundler.options.output_format == .internal_kit_dev and !source.index.isRuntime(); + opts.features.react_fast_refresh = (bundler.options.hot_module_reloading or bundler.options.react_fast_refresh) and + loader.isJSX() and !source.path.isNodeModule(); + + opts.ignore_dce_annotations = bundler.options.ignore_dce_annotations and !source.index.isRuntime(); + + // For files that are not user-specified entrypoints, set `import.meta.main` to `false`. + // Entrypoints will have `import.meta.main` set as "unknown", unless we use `--compile`, + // in which we inline `true`. + if (bundler.options.inline_entrypoint_import_meta_main or !task.is_entry_point) { + opts.import_meta_main_value = task.is_entry_point; + } else if (bundler.options.target == .node) { + opts.lower_import_meta_main_for_node_js = true; + } opts.tree_shaking = if (source.index.isRuntime()) true else bundler.options.tree_shaking; opts.module_type = task.module_type; - opts.features.unwrap_commonjs_packages = bundler.options.unwrap_commonjs_packages; task.jsx.parse = loader.isJSX(); @@ -2901,7 +3062,7 @@ pub const ParseTask = struct { ast.target = target; if (ast.parts.len <= 1) { - task.side_effects = _resolver.SideEffects.no_side_effects__empty_ast; + task.side_effects = .no_side_effects__empty_ast; } if (task.presolved_source_indices.len > 0) { @@ -2952,6 +3113,8 @@ pub const ParseTask = struct { const result = bun.default_allocator.create(Result) catch unreachable; result.* = .{ + .ctx = this.ctx, + .task = undefined, .value = brk: { if (run_( this, @@ -2997,13 +3160,24 @@ pub const ParseTask = struct { }, }; - worker.ctx.loop().enqueueTaskConcurrent( - Result, - BundleV2, - result, - BundleV2.onParseTaskComplete, - .task, - ); + switch (worker.ctx.loop().*) { + .js => |jsc_event_loop| { + jsc_event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(result, onComplete)); + }, + .mini => |*mini| { + mini.enqueueTaskConcurrentWithExtraCtx( + Result, + BundleV2, + result, + BundleV2.onParseTaskComplete, + .task, + ); + }, + } + } + + pub fn onComplete(result: *Result) void { + BundleV2.onParseTaskComplete(result, result.ctx); } }; @@ -3202,8 +3376,6 @@ pub const Graph = struct { use_directive_entry_points: UseDirective.List = .{}, - const_values: std.HashMapUnmanaged(Ref, Expr, Ref.HashCtx, 80) = .{}, - estimated_file_loader_count: usize = 0, additional_output_files: std.ArrayListUnmanaged(options.OutputFile) = .{}, @@ -3301,6 +3473,10 @@ const AstSourceIDMapping = struct { const LinkerGraph = struct { const debug = Output.scoped(.LinkerGraph, false); + /// TODO(@paperdave): remove this. i added it before realizing this is available + /// via LinkerContext.parse_graph. it may also be worth removing the other cloned data. + bundler_graph: *const Graph, + files: File.List = .{}, files_live: BitSet = undefined, entry_points: EntryPoint.List = .{}, @@ -3315,6 +3491,13 @@ const LinkerGraph = struct { ast: MultiArrayList(JSAst) = .{}, meta: MultiArrayList(JSMeta) = .{}, + /// We should avoid traversing all files in the bundle, because the linker + /// should be able to run a linking operation on a large bundle where only + /// a few files are needed (e.g. an incremental compilation scenario). This + /// holds all files that could possibly be reached through the entry points. + /// If you need to iterate over all files in the linking operation, iterate + /// over this array. This array is also sorted in a deterministic ordering + /// to help ensure deterministic builds (source indices are random). reachable_files: []Index = &[_]Index{}, stable_source_indices: []const u32 = &[_]u32{}, @@ -3324,7 +3507,10 @@ const LinkerGraph = struct { has_client_components: bool = false, has_server_components: bool = false, - const_values: std.HashMapUnmanaged(Ref, Expr, Ref.HashCtx, 80) = .{}, + /// This is for cross-module inlining of detected inlinable constants + // const_values: js_ast.Ast.ConstValuesMap = .{}, + /// This is for cross-module inlining of TypeScript enum constants + ts_enums: js_ast.Ast.TsEnumsMap = .{}, pub fn init(allocator: std.mem.Allocator, file_count: usize) !LinkerGraph { return LinkerGraph{ @@ -3354,7 +3540,7 @@ const LinkerGraph = struct { } pub fn generateNewSymbol(this: *LinkerGraph, source_index: u32, kind: Symbol.Kind, original_name: string) Ref { - var source_symbols = &this.symbols.symbols_for_source.slice()[source_index]; + const source_symbols = &this.symbols.symbols_for_source.slice()[source_index]; var ref = Ref.init( @as(Ref.Int, @truncate(source_symbols.len)), @@ -3498,8 +3684,8 @@ const LinkerGraph = struct { // Track that this specific symbol was imported if (source_index_to_import_from.get() != source_index) { - var to_bind = &g.meta.items(.imports_to_bind)[source_index]; - try to_bind.put(g.allocator, ref, .{ + const imports_to_bind = &g.meta.items(.imports_to_bind)[source_index]; + try imports_to_bind.put(g.allocator, ref, .{ .data = .{ .source_index = source_index_to_import_from, .import_ref = ref, @@ -3708,29 +3894,45 @@ const LinkerGraph = struct { this.symbols = js_ast.Symbol.Map.initList(symbols); } + // TODO: const_values + // { + // var const_values = this.const_values; + // var count: usize = 0; + + // for (this.ast.items(.const_values)) |const_value| { + // count += const_value.count(); + // } + + // if (count > 0) { + // try const_values.ensureTotalCapacity(this.allocator, count); + // for (this.ast.items(.const_values)) |const_value| { + // for (const_value.keys(), const_value.values()) |key, value| { + // const_values.putAssumeCapacityNoClobber(key, value); + // } + // } + // } + + // this.const_values = const_values; + // } + { - var const_values = this.const_values; var count: usize = 0; - - for (this.ast.items(.const_values)) |const_value| { - count += const_value.count(); + for (this.ast.items(.ts_enums)) |ts_enums| { + count += ts_enums.count(); } - if (count > 0) { - try const_values.ensureTotalCapacity(this.allocator, @as(u32, @truncate(count))); - for (this.ast.items(.const_values)) |const_value| { - for (const_value.keys(), const_value.values()) |key, value| { - const_values.putAssumeCapacityNoClobber(key, value); + try this.ts_enums.ensureTotalCapacity(this.allocator, count); + for (this.ast.items(.ts_enums)) |ts_enums| { + for (ts_enums.keys(), ts_enums.values()) |key, value| { + this.ts_enums.putAssumeCapacityNoClobber(key, value); } } } - - this.const_values = const_values; } - const in_resolved_exports: []ResolvedExports = this.meta.items(.resolved_exports); - const src_resolved_exports: []js_ast.Ast.NamedExports = this.ast.items(.named_exports); - for (src_resolved_exports, in_resolved_exports, 0..) |src, *dest, source_index| { + const src_named_exports: []js_ast.Ast.NamedExports = this.ast.items(.named_exports); + const dest_resolved_exports: []ResolvedExports = this.meta.items(.resolved_exports); + for (src_named_exports, dest_resolved_exports, 0..) |src, *dest, source_index| { var resolved = ResolvedExports{}; resolved.ensureTotalCapacity(this.allocator, src.count()) catch unreachable; for (src.keys(), src.values()) |key, value| { @@ -3783,7 +3985,7 @@ const LinkerGraph = struct { }; }; -const LinkerContext = struct { +pub const LinkerContext = struct { const debug = Output.scoped(.LinkerCtx, false); parse_graph: *Graph = undefined, @@ -3793,7 +3995,6 @@ const LinkerContext = struct { resolver: *Resolver = undefined, cycle_detector: std.ArrayList(ImportTracker) = undefined, - swap_cycle_detector: std.ArrayList(ImportTracker) = undefined, /// We may need to refer to the "__esm" and/or "__commonJS" runtime symbols cjs_runtime_ref: Ref = Ref.None, @@ -3804,7 +4005,7 @@ const LinkerContext = struct { options: LinkerOptions = .{}, - wait_group: ThreadPoolLib.WaitGroup = undefined, + wait_group: ThreadPoolLib.WaitGroup = .{}, ambiguous_result_pool: std.ArrayList(MatchImport) = undefined, @@ -3823,13 +4024,15 @@ const LinkerContext = struct { pending_task_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), pub const LinkerOptions = struct { - output_format: options.OutputFormat = .esm, + output_format: options.Format = .esm, ignore_dce_annotations: bool = false, + emit_dce_annotations: bool = true, tree_shaking: bool = true, minify_whitespace: bool = false, minify_syntax: bool = false, minify_identifiers: bool = false, source_maps: options.SourceMapOption = .none, + target: options.Target = .browser, mode: Mode = Mode.bundle, @@ -3842,10 +4045,10 @@ const LinkerContext = struct { }; pub const SourceMapData = struct { - line_offset_wait_group: sync.WaitGroup = undefined, + line_offset_wait_group: sync.WaitGroup = .{}, line_offset_tasks: []Task = &.{}, - quoted_contents_wait_group: sync.WaitGroup = undefined, + quoted_contents_wait_group: sync.WaitGroup = .{}, quoted_contents_tasks: []Task = &.{}, pub const Task = struct { @@ -3920,7 +4123,7 @@ const LinkerContext = struct { record.source_index.get() != source_index; } - inline fn shouldCallRuntimeRequire(format: options.OutputFormat) bool { + inline fn shouldCallRuntimeRequire(format: options.Format) bool { return format != .cjs; } @@ -3959,7 +4162,6 @@ const LinkerContext = struct { this.resolver = &bundle.bundler.resolver; this.cycle_detector = std.ArrayList(ImportTracker).init(this.allocator); - this.swap_cycle_detector = std.ArrayList(ImportTracker).init(this.allocator); this.graph.reachable_files = reachable; @@ -4130,6 +4332,7 @@ const LinkerContext = struct { chunks: []Chunk, allocator: std.mem.Allocator, source_id: u32, + pub fn next(c: *@This(), chunk_id: usize) void { _ = c.chunks[chunk_id].files_with_parts_in_chunk.getOrPut(c.allocator, @as(u32, @truncate(c.source_id))) catch unreachable; } @@ -4671,7 +4874,7 @@ const LinkerContext = struct { const other_kind = exports_kind[other_file]; switch (record.kind) { - ImportKind.stmt => { + .stmt => { // Importing using ES6 syntax from a file without any ES6 syntax // causes that module to be considered CommonJS-style, even if it // doesn't have any CommonJS exports. @@ -4707,7 +4910,7 @@ const LinkerContext = struct { flags[other_file].wrap = .cjs; } }, - ImportKind.require => + .require => // Files that are imported with require() must be CommonJS modules { if (other_kind == .esm) { @@ -4718,7 +4921,7 @@ const LinkerContext = struct { exports_kind[other_file] = .cjs; } }, - ImportKind.dynamic => { + .dynamic => { if (!this.graph.code_splitting) { // If we're not splitting, then import() is just a require() that // returns a promise, so the imported file must be a CommonJS module @@ -4743,11 +4946,10 @@ const LinkerContext = struct { // for entry point files in CommonJS format (or when in pass-through mode). if (kind == .cjs and (!entry_point_kinds[id].isEntryPoint() or output_format == .iife or output_format == .esm)) { flags[id].wrap = .cjs; - bun.assert(kind == .cjs); } } - if (comptime Environment.allow_assert) { + if (comptime Environment.enable_logs) { var cjs_count: usize = 0; var esm_count: usize = 0; var wrap_cjs_count: usize = 0; @@ -4841,11 +5043,12 @@ const LinkerContext = struct { const source_index = source_index_.get(); const id = source_index; - // -- + // Expression-style loaders defer code generation until linking. Code + // generation is done here because at this point we know that the + // "ExportsKind" field has its final value and will not be changed. if (ast_flags_list[id].has_lazy_export) { try this.generateCodeForLazyExport(id); } - // -- // Propagate exports for export star statements const export_star_ids = export_star_import_records[id]; @@ -4863,8 +5066,6 @@ const LinkerContext = struct { .exports_kind = exports_kind, .named_exports = this.graph.ast.items(.named_exports), }; - } else { - export_star_ctx.?.source_index_stack.clearRetainingCapacity(); } export_star_ctx.?.addExports(&resolved_exports[id], source_index); } @@ -4891,22 +5092,21 @@ const LinkerContext = struct { this.cycle_detector.clearRetainingCapacity(); const trace = tracer(@src(), "MatchImportsWithExports"); defer trace.end(); - var wrapper_part_indices = this.graph.meta.items(.wrapper_part_index); - var imports_to_bind = this.graph.meta.items(.imports_to_bind); + const wrapper_part_indices = this.graph.meta.items(.wrapper_part_index); + const imports_to_bind = this.graph.meta.items(.imports_to_bind); for (reachable) |source_index_| { const source_index = source_index_.get(); - const id = source_index; // not a JS ast or empty - if (id >= named_imports.len) { + if (source_index >= named_imports.len) { continue; } - var named_imports_ = &named_imports[id]; + const named_imports_ = &named_imports[source_index]; if (named_imports_.count() > 0) { this.matchImportsWithExportsForFile( named_imports_, - &imports_to_bind[id], + &imports_to_bind[source_index], source_index, ); @@ -4914,34 +5114,33 @@ const LinkerContext = struct { return error.ImportResolutionFailed; } } - const export_kind = exports_kind[id]; - var flag = flags[id]; + const export_kind = exports_kind[source_index]; + var flag = flags[source_index]; // If we're exporting as CommonJS and this file was originally CommonJS, // then we'll be using the actual CommonJS "exports" and/or "module" // symbols. In that case make sure to mark them as such so they don't // get minified. - if ((output_format == .cjs or output_format == .preserve) and + if ((output_format == .cjs) and entry_point_kinds[source_index].isEntryPoint() and export_kind == .cjs and flag.wrap == .none) { - const exports_ref = symbols.follow(exports_refs[id]); - const module_ref = symbols.follow(module_refs[id]); + const exports_ref = symbols.follow(exports_refs[source_index]); + const module_ref = symbols.follow(module_refs[source_index]); symbols.get(exports_ref).?.kind = .unbound; symbols.get(module_ref).?.kind = .unbound; } else if (flag.force_include_exports_for_entry_point or export_kind != .cjs) { flag.needs_exports_variable = true; - flags[id] = flag; + flags[source_index] = flag; } - const wrapped_ref = this.graph.ast.items(.wrapper_ref)[id]; - if (wrapped_ref.isNull() or wrapped_ref.isEmpty()) continue; + const wrapped_ref = this.graph.ast.items(.wrapper_ref)[source_index]; // Create the wrapper part for wrapped files. This is needed by a later step. this.createWrapperForFile( flag.wrap, // if this one is null, the AST does not need to be wrapped. wrapped_ref, - &wrapper_part_indices[id], + &wrapper_part_indices[source_index], source_index, ); } @@ -4994,7 +5193,6 @@ const LinkerContext = struct { const module_ref = module_refs[id]; - // TODO: see if counting and batching into a single large allocation instead of per-file improves perf const string_buffer_len: usize = brk: { var count: usize = 0; if (is_entry_point and this.options.output_format == .esm) { @@ -5008,11 +5206,11 @@ const LinkerContext = struct { else std.fmt.count("{}", .{source.fmtIdentifier()}); - if (wrap == .esm) { + if (wrap == .esm and wrapper_refs[id].isValid()) { count += "init_".len + ident_fmt_len; } - if (wrap != .cjs and export_kind != .cjs) { + if (wrap != .cjs and export_kind != .cjs and this.options.output_format != .internal_kit_dev) { count += "exports_".len + ident_fmt_len; count += "module_".len + ident_fmt_len; } @@ -5044,14 +5242,15 @@ const LinkerContext = struct { // Use "init_*" for ESM wrappers instead of "require_*" if (wrap == .esm) { - const original_name = builder.fmt( - "init_{}", - .{ - source.fmtIdentifier(), - }, - ); + const ref = wrapper_refs[id]; + if (ref.isValid()) { + const original_name = builder.fmt( + "init_{}", + .{source.fmtIdentifier()}, + ); - this.graph.symbols.get(wrapper_refs[id]).?.original_name = original_name; + this.graph.symbols.get(ref).?.original_name = original_name; + } } // If this isn't CommonJS, then rename the unused "exports" and "module" @@ -5059,7 +5258,7 @@ const LinkerContext = struct { // actual CommonJS files from being renamed. This is purely about // aesthetics and is not about correctness. This is done here because by // this point, we know the CommonJS status will not change further. - if (wrap != .cjs and export_kind != .cjs) { + if (wrap != .cjs and export_kind != .cjs and this.options.output_format != .internal_kit_dev) { const exports_name = builder.fmt("exports_{}", .{source.fmtIdentifier()}); const module_name = builder.fmt("module_{}", .{source.fmtIdentifier()}); @@ -5101,19 +5300,19 @@ const LinkerContext = struct { var imports_to_bind_list: []RefImportData = this.graph.meta.items(.imports_to_bind); var parts_list: []js_ast.Part.List = ast_fields.items(.parts); - var imports_to_bind = &imports_to_bind_list[id]; var parts: []js_ast.Part = parts_list[id].slice(); - for (0..imports_to_bind.count()) |i| { - const ref = imports_to_bind.keys()[i]; - const import = imports_to_bind.values()[i]; + const imports_to_bind = &imports_to_bind_list[id]; + for (imports_to_bind.keys(), imports_to_bind.values()) |ref_untyped, import_untyped| { + const ref: Ref = ref_untyped; // ZLS + const import: ImportData = import_untyped; // ZLS const import_source_index = import.data.source_index.get(); if (named_imports[id].get(ref)) |named_import| { for (named_import.local_parts_with_uses.slice()) |part_index| { var part: *js_ast.Part = &parts[part_index]; - const parts_declaring_symbol: []const u32 = this.graph.topLevelSymbolToParts(import_source_index, ref); + const parts_declaring_symbol: []const u32 = this.graph.topLevelSymbolToParts(import_source_index, import.data.import_ref); const total_len = parts_declaring_symbol.len + @as(usize, import.re_exports.len) + @as(usize, part.dependencies.len); if (part.dependencies.cap < total_len) { @@ -5125,12 +5324,10 @@ const LinkerContext = struct { // Depend on the file containing the imported symbol for (parts_declaring_symbol) |resolved_part_index| { - part.dependencies.appendAssumeCapacity( - .{ - .source_index = Index.source(import_source_index), - .part_index = resolved_part_index, - }, - ); + part.dependencies.appendAssumeCapacity(.{ + .source_index = Index.source(import_source_index), + .part_index = resolved_part_index, + }); } // Also depend on any files that re-exported this symbol in between the @@ -5146,40 +5343,38 @@ const LinkerContext = struct { if (is_entry_point) { const force_include_exports = flag.force_include_exports_for_entry_point; const add_wrapper = wrap != .none; - var dependencies = std.ArrayList(js_ast.Dependency).initCapacity( - this.allocator, - @as(usize, @intFromBool(force_include_exports)) + @as(usize, @intFromBool(add_wrapper)), - ) catch unreachable; + + const extra_count = @as(usize, @intFromBool(force_include_exports)) + + @as(usize, @intFromBool(add_wrapper)); + + var dependencies = std.ArrayList(js_ast.Dependency).initCapacity(this.allocator, extra_count) catch bun.outOfMemory(); + var resolved_exports_list: *ResolvedExports = &this.graph.meta.items(.resolved_exports)[id]; for (aliases) |alias| { - var export_ = resolved_exports_list.get(alias).?; - var target_source_index = export_.data.source_index.get(); - var target_id = target_source_index; - var target_ref = export_.data.import_ref; + const exp = resolved_exports_list.get(alias).?; + var target_source_index = exp.data.source_index; + var target_ref = exp.data.import_ref; // If this is an import, then target what the import points to - - if (imports_to_bind.get(target_ref)) |import_data| { - target_source_index = import_data.data.source_index.get(); - target_id = target_source_index; + if (imports_to_bind_list[target_source_index.get()].get(target_ref)) |import_data| { + target_source_index = import_data.data.source_index; target_ref = import_data.data.import_ref; - dependencies.appendSlice(import_data.re_exports.slice()) catch unreachable; + + dependencies.appendSlice(import_data.re_exports.slice()) catch bun.outOfMemory(); } - const top_to_parts = this.topLevelSymbolsToParts(target_id, target_ref); - dependencies.ensureUnusedCapacity(top_to_parts.len) catch unreachable; // Pull in all declarations of this symbol + const top_to_parts = this.topLevelSymbolsToParts(target_source_index.get(), target_ref); + dependencies.ensureUnusedCapacity(top_to_parts.len) catch bun.outOfMemory(); for (top_to_parts) |part_index| { - dependencies.appendAssumeCapacity( - .{ - .source_index = Index.source(target_source_index), - .part_index = part_index, - }, - ); + dependencies.appendAssumeCapacity(.{ + .source_index = target_source_index, + .part_index = part_index, + }); } } - dependencies.ensureUnusedCapacity(@as(usize, @intFromBool(force_include_exports)) + @as(usize, @intFromBool(add_wrapper))) catch unreachable; + dependencies.ensureUnusedCapacity(extra_count) catch bun.outOfMemory(); // Ensure "exports" is included if the current output format needs it if (force_include_exports) { @@ -5188,6 +5383,7 @@ const LinkerContext = struct { ); } + // Include the wrapper if present if (add_wrapper) { dependencies.appendAssumeCapacity( .{ @@ -5204,12 +5400,13 @@ const LinkerContext = struct { .dependencies = js_ast.Dependency.List.fromList(dependencies), .can_be_removed_if_unused = false, }, - ) catch unreachable; + ) catch bun.outOfMemory(); + parts = parts_list[id].slice(); this.graph.meta.items(.entry_point_part_index)[id] = Index.part(entry_point_part_index); // Pull in the "__toCommonJS" symbol if we need it due to being an entry point - if (force_include_exports) { + if (force_include_exports and this.options.output_format != .internal_kit_dev) { this.graph.generateRuntimeSymbolImportAndUse( source_index, Index.part(entry_point_part_index), @@ -5220,7 +5417,7 @@ const LinkerContext = struct { } // Encode import-specific constraints in the dependency graph - var import_records: []ImportRecord = import_records_list[id].slice(); + const import_records: []ImportRecord = import_records_list[id].slice(); debug("Binding {d} imports for file {s} (#{d})", .{ import_records.len, source.path.text, id }); for (parts, 0..) |*part, part_index| { @@ -5236,6 +5433,8 @@ const LinkerContext = struct { // Don't follow external imports (this includes import() expressions) if (!record.source_index.isValid() or this.isExternalDynamicImport(record, source_index)) { + if (this.options.output_format == .internal_kit_dev) continue; + // This is an external import. Check if it will be a "require()" call. if (kind == .require or !output_format.keepES6ImportExportSyntax() or kind == .dynamic) { if (record.source_index.isValid() and kind == .dynamic and ast_flags[other_id].force_cjs_to_esm) { @@ -5295,17 +5494,19 @@ const LinkerContext = struct { if (other_flags.wrap != .none) { // Depend on the automatically-generated require wrapper symbol const wrapper_ref = wrapper_refs[other_id]; - this.graph.generateSymbolImportAndUse( - source_index, - @as(u32, @intCast(part_index)), - wrapper_ref, - 1, - Index.source(other_source_index), - ) catch unreachable; + if (wrapper_ref.isValid()) { + this.graph.generateSymbolImportAndUse( + source_index, + @as(u32, @intCast(part_index)), + wrapper_ref, + 1, + Index.source(other_source_index), + ) catch unreachable; + } // This is an ES6 import of a CommonJS module, so it needs the // "__toESM" wrapper as long as it's not a bare "require()" - if (kind != .require and other_export_kind == .cjs) { + if (kind != .require and other_export_kind == .cjs and this.options.output_format != .internal_kit_dev) { record.wrap_with_to_esm = true; to_esm_uses += 1; } @@ -5354,33 +5555,6 @@ const LinkerContext = struct { } } - // If there's an ES6 import of a CommonJS module, then we're going to need the - // "__toESM" symbol from the runtime to wrap the result of "require()" - this.graph.generateRuntimeSymbolImportAndUse( - source_index, - Index.part(part_index), - "__toESM", - to_esm_uses, - ) catch unreachable; - - // If there's a CommonJS require of an ES6 module, then we're going to need the - // "__toCommonJS" symbol from the runtime to wrap the exports object - this.graph.generateRuntimeSymbolImportAndUse( - source_index, - Index.part(part_index), - "__toCommonJS", - to_common_js_uses, - ) catch unreachable; - - // If there are unbundled calls to "require()" and we're not generating - // code for node, then substitute a "__require" wrapper for "require". - this.graph.generateRuntimeSymbolImportAndUse( - source_index, - Index.part(part_index), - "__require", - runtime_require_uses, - ) catch unreachable; - // If there's an ES6 export star statement of a non-ES6 module, then we're // going to need the "__reExport" symbol from the runtime var re_export_uses: u32 = 0; @@ -5428,13 +5602,41 @@ const LinkerContext = struct { } } - this.graph.generateRuntimeSymbolImportAndUse( - source_index, - Index.part(part_index), + if (this.options.output_format != .internal_kit_dev) { + // If there's an ES6 import of a CommonJS module, then we're going to need the + // "__toESM" symbol from the runtime to wrap the result of "require()" + this.graph.generateRuntimeSymbolImportAndUse( + source_index, + Index.part(part_index), + "__toESM", + to_esm_uses, + ) catch unreachable; - "__reExport", - re_export_uses, - ) catch unreachable; + // If there's a CommonJS require of an ES6 module, then we're going to need the + // "__toCommonJS" symbol from the runtime to wrap the exports object + this.graph.generateRuntimeSymbolImportAndUse( + source_index, + Index.part(part_index), + "__toCommonJS", + to_common_js_uses, + ) catch unreachable; + + // If there are unbundled calls to "require()" and we're not generating + // code for node, then substitute a "__require" wrapper for "require". + this.graph.generateRuntimeSymbolImportAndUse( + source_index, + Index.part(part_index), + "__require", + runtime_require_uses, + ) catch unreachable; + + this.graph.generateRuntimeSymbolImportAndUse( + source_index, + Index.part(part_index), + "__reExport", + re_export_uses, + ) catch unreachable; + } } } } @@ -5442,7 +5644,7 @@ const LinkerContext = struct { pub fn createExportsForFile( c: *LinkerContext, - allocator_: std.mem.Allocator, + allocator: std.mem.Allocator, id: u32, resolved_exports: *ResolvedExports, imports_to_bind: []RefImportData, @@ -5461,50 +5663,49 @@ const LinkerContext = struct { // 1 property per export var properties = std.ArrayList(js_ast.G.Property) - .initCapacity(allocator_, export_aliases.len) catch unreachable; + .initCapacity(allocator, export_aliases.len) catch bun.outOfMemory(); var ns_export_symbol_uses = js_ast.Part.SymbolUseMap{}; - ns_export_symbol_uses.ensureTotalCapacity(allocator_, export_aliases.len) catch unreachable; + ns_export_symbol_uses.ensureTotalCapacity(allocator, export_aliases.len) catch bun.outOfMemory(); const needs_exports_variable = c.graph.meta.items(.flags)[id].needs_exports_variable; const stmts_count = - // 2 statements for every export - export_aliases.len * 2 + + // 1 statement for every export + export_aliases.len + // + 1 if there are non-zero exports @as(usize, @intFromBool(export_aliases.len > 0)) + // + 1 if we need to inject the exports variable @as(usize, @intFromBool(needs_exports_variable)); - var stmts = js_ast.Stmt.Batcher.init(allocator_, stmts_count) catch unreachable; + var stmts = js_ast.Stmt.Batcher.init(allocator, stmts_count) catch bun.outOfMemory(); defer stmts.done(); const loc = Logger.Loc.Empty; // todo: investigate if preallocating this array is faster - var ns_export_dependencies = std.ArrayList(js_ast.Dependency).initCapacity(allocator_, re_exports_count) catch unreachable; + var ns_export_dependencies = std.ArrayList(js_ast.Dependency).initCapacity(allocator, re_exports_count) catch bun.outOfMemory(); for (export_aliases) |alias| { - var export_ = resolved_exports.getPtr(alias).?; - - const other_id = export_.data.source_index.get(); + var exp = resolved_exports.getPtr(alias).?.*; // If this is an export of an import, reference the symbol that the import // was eventually resolved to. We need to do this because imports have // already been resolved by this point, so we can't generate a new import // and have that be resolved later. - if (imports_to_bind[other_id].get(export_.data.import_ref)) |import_data| { - export_.data = import_data.data; - ns_export_dependencies.appendSlice(import_data.re_exports.slice()) catch unreachable; + if (imports_to_bind[exp.data.source_index.get()].get(exp.data.import_ref)) |import_data| { + exp.data.import_ref = import_data.data.import_ref; + exp.data.source_index = import_data.data.source_index; + ns_export_dependencies.appendSlice(import_data.re_exports.slice()) catch bun.outOfMemory(); } // Exports of imports need EImportIdentifier in case they need to be re- // written to a property access later on // note: this is stack allocated const value: js_ast.Expr = brk: { - if (c.graph.symbols.getConst(export_.data.import_ref)) |symbol| { + if (c.graph.symbols.getConst(exp.data.import_ref)) |symbol| { if (symbol.namespace_alias != null) { break :brk js_ast.Expr.init( js_ast.E.ImportIdentifier, js_ast.E.ImportIdentifier{ - .ref = export_.data.import_ref, + .ref = exp.data.import_ref, }, loc, ); @@ -5514,64 +5715,55 @@ const LinkerContext = struct { break :brk js_ast.Expr.init( js_ast.E.Identifier, js_ast.E.Identifier{ - .ref = export_.data.import_ref, + .ref = exp.data.import_ref, }, loc, ); }; - const block = stmts.eat1( - js_ast.Stmt.allocate(allocator_, js_ast.S.Block, .{ - .stmts = stmts.eat1( - js_ast.Stmt.allocate( - allocator_, - js_ast.S.Return, - .{ .value = value }, - loc, - ), - ), - }, loc), - ); const fn_body = js_ast.G.FnBody{ - .stmts = block, - .loc = loc, - }; - properties.appendAssumeCapacity( - .{ - .key = js_ast.Expr.allocate( - allocator_, - js_ast.E.String, - .{ - // TODO: test emoji work as expected - // relevant for WASM exports - .data = alias, - }, - loc, - ), - .value = js_ast.Expr.allocate( - allocator_, - js_ast.E.Arrow, - .{ .prefer_expr = true, .body = fn_body }, + .stmts = stmts.eat1( + js_ast.Stmt.allocate( + allocator, + js_ast.S.Return, + .{ .value = value }, loc, ), - }, - ); - ns_export_symbol_uses.putAssumeCapacity(export_.data.import_ref, .{ .count_estimate = 1 }); + ), + .loc = loc, + }; + properties.appendAssumeCapacity(.{ + .key = js_ast.Expr.allocate( + allocator, + js_ast.E.String, + .{ + // TODO: test emoji work as expected + // relevant for WASM exports + .data = alias, + }, + loc, + ), + .value = js_ast.Expr.allocate( + allocator, + js_ast.E.Arrow, + .{ .prefer_expr = true, .body = fn_body }, + loc, + ), + }); + ns_export_symbol_uses.putAssumeCapacity(exp.data.import_ref, .{ .count_estimate = 1 }); // Make sure the part that declares the export is included - const parts = c.topLevelSymbolsToParts(other_id, export_.data.import_ref); + const parts = c.topLevelSymbolsToParts(exp.data.source_index.get(), exp.data.import_ref); ns_export_dependencies.ensureUnusedCapacity(parts.len) catch unreachable; - var ptr = ns_export_dependencies.items.ptr + ns_export_dependencies.items.len; - ns_export_dependencies.items.len += parts.len; - - for (parts, ptr[0..parts.len]) |part_id, *dependency| { + for (parts, ns_export_dependencies.unusedCapacitySlice()[0..parts.len]) |part_id, *dest| { // Use a non-local dependency since this is likely from a different // file if it came in through an export star - dependency.* = .{ - .source_index = export_.data.source_index, + dest.* = .{ + .source_index = exp.data.source_index, .part_index = part_id, }; } + ns_export_dependencies.items.len += parts.len; } var declared_symbols = js_ast.DeclaredSymbol.List{}; @@ -5583,19 +5775,19 @@ const LinkerContext = struct { // Prefix this part with "var exports = {}" if this isn't a CommonJS entry point if (needs_exports_variable) { - var decls = allocator_.alloc(js_ast.G.Decl, 1) catch unreachable; + var decls = allocator.alloc(js_ast.G.Decl, 1) catch unreachable; decls[0] = .{ .binding = js_ast.Binding.alloc( - allocator_, + allocator, js_ast.B.Identifier{ .ref = exports_ref, }, loc, ), - .value = js_ast.Expr.allocate(allocator_, js_ast.E.Object, .{}, loc), + .value = js_ast.Expr.allocate(allocator, js_ast.E.Object, .{}, loc), }; remaining_stmts[0] = js_ast.Stmt.allocate( - allocator_, + allocator, js_ast.S.Local, .{ .decls = G.Decl.List.init(decls), @@ -5603,24 +5795,24 @@ const LinkerContext = struct { loc, ); remaining_stmts = remaining_stmts[1..]; - declared_symbols.append(allocator_, .{ .ref = exports_ref, .is_top_level = true }) catch unreachable; + declared_symbols.append(allocator, .{ .ref = exports_ref, .is_top_level = true }) catch unreachable; } // "__export(exports, { foo: () => foo })" var export_ref = Ref.None; if (properties.items.len > 0) { export_ref = c.graph.ast.items(.module_scope)[Index.runtime.get()].members.get("__export").?.ref; - var args = allocator_.alloc(js_ast.Expr, 2) catch unreachable; + var args = allocator.alloc(js_ast.Expr, 2) catch unreachable; args[0..2].* = [_]js_ast.Expr{ js_ast.Expr.initIdentifier(exports_ref, loc), - js_ast.Expr.allocate(allocator_, js_ast.E.Object, .{ .properties = js_ast.G.Property.List.fromList(properties) }, loc), + js_ast.Expr.allocate(allocator, js_ast.E.Object, .{ .properties = js_ast.G.Property.List.fromList(properties) }, loc), }; remaining_stmts[0] = js_ast.Stmt.allocate( - allocator_, + allocator, js_ast.S.SExpr, .{ .value = js_ast.Expr.allocate( - allocator_, + allocator, js_ast.E.Call, .{ .target = js_ast.Expr.initIdentifier(export_ref, loc), @@ -5653,7 +5845,7 @@ const LinkerContext = struct { // Initialize the part that was allocated for us earlier. The information // here will be used after this during tree shaking. c.graph.ast.items(.parts)[id].slice()[js_ast.namespace_export_part_index] = .{ - .stmts = all_export_stmts, + .stmts = if (c.options.output_format != .internal_kit_dev) all_export_stmts else &.{}, .symbol_uses = ns_export_symbol_uses, .dependencies = js_ast.Dependency.List.fromList(ns_export_dependencies), .declared_symbols = declared_symbols, @@ -5683,20 +5875,20 @@ const LinkerContext = struct { const id = source_index; if (id > c.graph.meta.len) return; - var worker: *ThreadPool.Worker = ThreadPool.Worker.get(@fieldParentPtr("linker", c)); + const worker: *ThreadPool.Worker = ThreadPool.Worker.get(@fieldParentPtr("linker", c)); defer worker.unget(); // we must use this allocator here - const allocator_ = worker.allocator; + const allocator = worker.allocator; - var resolved_exports: *ResolvedExports = &c.graph.meta.items(.resolved_exports)[id]; + const resolved_exports: *ResolvedExports = &c.graph.meta.items(.resolved_exports)[id]; // Now that all exports have been resolved, sort and filter them to create // something we can iterate over later. - var aliases = std.ArrayList(string).initCapacity(allocator_, resolved_exports.count()) catch unreachable; + var aliases = std.ArrayList(string).initCapacity(allocator, resolved_exports.count()) catch unreachable; var alias_iter = resolved_exports.iterator(); - var imports_to_bind = c.graph.meta.items(.imports_to_bind); - var probably_typescript_type = c.graph.meta.items(.probably_typescript_type); + const imports_to_bind = c.graph.meta.items(.imports_to_bind); + const probably_typescript_type = c.graph.meta.items(.probably_typescript_type); // counting in here saves us an extra pass through the array var re_exports_count: usize = 0; @@ -5743,7 +5935,7 @@ const LinkerContext = struct { // Export creation uses "sortedAndFilteredExportAliases" so this must // come second after we fill in that array c.createExportsForFile( - allocator_, + allocator, id, resolved_exports, imports_to_bind, @@ -5752,60 +5944,105 @@ const LinkerContext = struct { ); // Each part tracks the other parts it depends on within this file - var local_dependencies = std.AutoHashMap(u32, u32).init(allocator_); + var local_dependencies = std.AutoHashMap(u32, u32).init(allocator); defer local_dependencies.deinit(); - var parts = &c.graph.ast.items(.parts)[id]; - const parts_slice: []js_ast.Part = parts.slice(); - var named_imports: *js_ast.Ast.NamedImports = &c.graph.ast.items(.named_imports)[id]; - outer: for (parts_slice, 0..) |*part, part_index| { - // TODO: inline const TypeScript enum here + const parts_slice: []js_ast.Part = c.graph.ast.items(.parts)[id].slice(); + const named_imports: *js_ast.Ast.NamedImports = &c.graph.ast.items(.named_imports)[id]; - // TODO: inline function calls here - - // Inline cross-module constants - if (c.graph.const_values.count() > 0) { - // First, find any symbol usage that points to a constant value. - // This will be pretty rare. - const first_constant_i: ?usize = brk: { - for (part.symbol_uses.keys(), 0..) |ref, j| { - if (c.graph.const_values.contains(ref)) { - break :brk j; - } - } + const our_imports_to_bind = imports_to_bind[id]; + outer: for (parts_slice, 0..) |*part, part_index| { + // Now that all files have been parsed, determine which property + // accesses off of imported symbols are inlined enum values and + // which ones aren't + for ( + part.import_symbol_property_uses.keys(), + part.import_symbol_property_uses.values(), + ) |ref, properties| { + const use = part.symbol_uses.getPtr(ref).?; + + // Rare path: this import is a TypeScript enum + if (our_imports_to_bind.get(ref)) |import_data| { + const import_ref = import_data.data.import_ref; + if (c.graph.symbols.get(import_ref)) |symbol| { + if (symbol.kind == .ts_enum) { + if (c.graph.ts_enums.get(import_ref)) |enum_data| { + var found_non_inlined_enum = false; + + var it = properties.iterator(); + while (it.next()) |next| { + const name = next.key_ptr.*; + const prop_use = next.value_ptr; + + if (enum_data.get(name) == null) { + found_non_inlined_enum = true; + use.count_estimate += prop_use.count_estimate; + } + } - break :brk null; - }; - if (first_constant_i) |j| { - var end_i: usize = 0; - // symbol_uses is an array - var keys = part.symbol_uses.keys()[j..]; - var values = part.symbol_uses.values()[j..]; - for (keys, values) |ref, val| { - if (c.graph.const_values.contains(ref)) { - continue; + if (!found_non_inlined_enum) { + if (use.count_estimate == 0) { + _ = part.symbol_uses.swapRemove(ref); + } + continue; + } + } } - - keys[end_i] = ref; - values[end_i] = val; - end_i += 1; - } - part.symbol_uses.entries.len = end_i + j; - - if (part.symbol_uses.entries.len == 0 and part.can_be_removed_if_unused) { - part.tag = .dead_due_to_inlining; - part.dependencies.len = 0; - continue :outer; } + } - part.symbol_uses.reIndex(allocator_) catch unreachable; + // Common path: this import isn't a TypeScript enum + var it = properties.valueIterator(); + while (it.next()) |prop_use| { + use.count_estimate += prop_use.count_estimate; } } - const symbol_uses = part.symbol_uses.keys(); + // TODO: inline function calls here + + // TODO: Inline cross-module constants + // if (c.graph.const_values.count() > 0) { + // // First, find any symbol usage that points to a constant value. + // // This will be pretty rare. + // const first_constant_i: ?usize = brk: { + // for (part.symbol_uses.keys(), 0..) |ref, j| { + // if (c.graph.const_values.contains(ref)) { + // break :brk j; + // } + // } + + // break :brk null; + // }; + // if (first_constant_i) |j| { + // var end_i: usize = 0; + // // symbol_uses is an array + // var keys = part.symbol_uses.keys()[j..]; + // var values = part.symbol_uses.values()[j..]; + // for (keys, values) |ref, val| { + // if (c.graph.const_values.contains(ref)) { + // continue; + // } + + // keys[end_i] = ref; + // values[end_i] = val; + // end_i += 1; + // } + // part.symbol_uses.entries.len = end_i + j; + + // if (part.symbol_uses.entries.len == 0 and part.can_be_removed_if_unused) { + // part.tag = .dead_due_to_inlining; + // part.dependencies.len = 0; + // continue :outer; + // } + + // part.symbol_uses.reIndex(allocator) catch unreachable; + // } + // } + if (false) break :outer; // this `if` is here to preserve the unused + // block label from the above commented code. // Now that we know this, we can determine cross-part dependencies - for (symbol_uses, 0..) |ref, j| { + for (part.symbol_uses.keys(), 0..) |ref, j| { if (comptime Environment.allow_assert) { bun.assert(part.symbol_uses.values()[j].count_estimate > 0); } @@ -5813,12 +6050,12 @@ const LinkerContext = struct { const other_parts = c.topLevelSymbolsToParts(id, ref); for (other_parts) |other_part_index| { - const local = local_dependencies.getOrPut(@as(u32, @intCast(other_part_index))) catch unreachable; + const local = local_dependencies.getOrPut(other_part_index) catch unreachable; if (!local.found_existing or local.value_ptr.* != part_index) { local.value_ptr.* = @as(u32, @intCast(part_index)); // note: if we crash on append, it is due to threadlocal heaps in mimalloc part.dependencies.push( - allocator_, + allocator, .{ .source_index = Index.source(source_index), .part_index = other_part_index, @@ -5829,7 +6066,7 @@ const LinkerContext = struct { // Also map from imports to parts that use them if (named_imports.getPtr(ref)) |existing| { - existing.local_parts_with_uses.push(allocator_, @as(u32, @intCast(part_index))) catch unreachable; + existing.local_parts_with_uses.push(allocator, @intCast(part_index)) catch unreachable; } } } @@ -6000,26 +6237,28 @@ const LinkerContext = struct { const used_refs = part.symbol_uses.keys(); - for (used_refs) |ref_| { + // Record each symbol used in this part. This will later be matched up + // with our map of which chunk a given symbol is declared in to + // determine if the symbol needs to be imported from another chunk. + for (used_refs) |ref| { const ref_to_use = brk: { - var ref = ref_; - var symbol = deps.symbols.getConst(ref).?; + var ref_to_use = ref; + var symbol = deps.symbols.getConst(ref_to_use).?; // Ignore unbound symbols if (symbol.kind == .unbound) continue; // Ignore symbols that are going to be replaced by undefined - if (symbol.import_item_status == .missing) { + if (symbol.import_item_status == .missing) continue; - } // If this is imported from another file, follow the import // reference and reference the symbol in that file instead - if (imports_to_bind.get(ref)) |import_data| { - ref = import_data.data.import_ref; - symbol = deps.symbols.getConst(ref).?; - } else if (wrap == .cjs and ref.eql(wrapper_ref)) { + if (imports_to_bind.get(ref_to_use)) |import_data| { + ref_to_use = import_data.data.import_ref; + symbol = deps.symbols.getConst(ref_to_use).?; + } else if (wrap == .cjs and ref_to_use.eql(wrapper_ref)) { // The only internal symbol that wrapped CommonJS files export // is the wrapper itself. continue; @@ -6030,9 +6269,9 @@ const LinkerContext = struct { // identifier. In that case we want to pull in the namespace symbol // instead. The namespace symbol stores the result of "require()". if (symbol.namespace_alias) |*namespace_alias| { - ref = namespace_alias.namespace_ref; + ref_to_use = namespace_alias.namespace_ref; } - break :brk ref; + break :brk ref_to_use; }; if (comptime Environment.allow_assert) @@ -6655,6 +6894,17 @@ const LinkerContext = struct { defer ctx.wg.finish(); var worker = ThreadPool.Worker.get(@fieldParentPtr("linker", ctx.c)); defer worker.unget(); + + const prev_action = if (Environment.isDebug) bun.crash_handler.current_action; + defer if (Environment.isDebug) { + bun.crash_handler.current_action = prev_action; + }; + if (Environment.isDebug) bun.crash_handler.current_action = .{ .bundle_generate_chunk = .{ + .chunk = ctx.chunk, + .context = ctx.c, + .part_range = &part_range.part_range, + } }; + ctx.chunk.compile_results_for_chunk[part_range.i] = generateCompileResultForJSChunk_(worker, ctx.c, ctx.chunk, part_range.part_range); } @@ -6703,8 +6953,8 @@ const LinkerContext = struct { const c = ctx.c; bun.assert(chunk.content == .javascript); - js_ast.Expr.Data.Store.create(bun.default_allocator); - js_ast.Stmt.Data.Store.create(bun.default_allocator); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); defer chunk.renamer.deinit(bun.default_allocator); @@ -6722,12 +6972,8 @@ const LinkerContext = struct { const runtimeRequireRef = if (c.resolver.opts.target.isBun()) null else c.graph.symbols.follow(runtime_members.get("__require").?.ref); { - const indent: usize = 0; - // TODO: IIFE indent - const print_options = js_printer.Options{ - // TODO: IIFE - .indent = indent, + .indent = .{}, .has_run_symbol_renamer = true, .allocator = worker.allocator, @@ -6735,7 +6981,9 @@ const LinkerContext = struct { .minify_whitespace = c.options.minify_whitespace, .minify_identifiers = c.options.minify_identifiers, .minify_syntax = c.options.minify_syntax, - .const_values = c.graph.const_values, + .target = c.options.target, + .print_dce_annotations = c.options.emit_dce_annotations, + // .const_values = c.graph.const_values, }; var cross_chunk_import_records = ImportRecord.List.initCapacity(worker.allocator, chunk.cross_chunk_imports.len) catch unreachable; @@ -6835,7 +7083,31 @@ const LinkerContext = struct { // TODO: directive - // TODO: IIFE wrap + // For Kit, hoist runtime.js outside of the IIFE + const compile_results = chunk.compile_results_for_chunk; + if (c.options.output_format == .internal_kit_dev) { + for (compile_results) |compile_result| { + const source_index = compile_result.sourceIndex(); + if (source_index != Index.runtime.value) break; + line_offset.advance(compile_result.code()); + j.push(compile_result.code(), bun.default_allocator); + } + } + + switch (c.options.output_format) { + .internal_kit_dev => { + const start = bun.kit.getHmrRuntime(if (c.options.target.isBun()) .server else .client); + j.pushStatic(start); + line_offset.advance(start); + }, + .iife => { + // Bun does not do arrow function lowering. So the wrapper can be an arrow. + const start = if (c.options.minify_whitespace) "(()=>{" else "(() => {\n"; + j.pushStatic(start); + line_offset.advance(start); + }, + else => {}, // no wrapper + } if (cross_chunk_prefix.len > 0) { newline_before_comment = true; @@ -6844,31 +7116,31 @@ const LinkerContext = struct { } // Concatenate the generated JavaScript chunks together - var prev_filename_comment: Index.Int = 0; - const compile_results = chunk.compile_results_for_chunk; - var compile_results_for_source_map = std.MultiArrayList(CompileResultForSourceMap){}; - compile_results_for_source_map.ensureUnusedCapacity(worker.allocator, compile_results.len) catch unreachable; + var compile_results_for_source_map: std.MultiArrayList(CompileResultForSourceMap) = .{}; + compile_results_for_source_map.setCapacity(worker.allocator, compile_results.len) catch bun.outOfMemory(); + + const show_comments = c.options.mode == .bundle and + !c.options.minify_whitespace; const sources: []const Logger.Source = c.parse_graph.input_files.items(.source); - for (@as([]CompileResult, compile_results)) |compile_result| { + for (compile_results) |compile_result| { const source_index = compile_result.sourceIndex(); const is_runtime = source_index == Index.runtime.value; // TODO: extracated legal comments // Add a comment with the file path before the file contents - if (c.options.mode == .bundle and !c.options.minify_whitespace and source_index != prev_filename_comment and compile_result.code().len > 0) { + if (show_comments and source_index != prev_filename_comment and compile_result.code().len > 0) { prev_filename_comment = source_index; + if (newline_before_comment) { j.pushStatic("\n"); line_offset.advance("\n"); } - // Make sure newlines in the path can't cause a syntax error. This does - // not minimize allocations because it's expected that this case never - // comes up in practice. + // Make sure newlines in the path can't cause a syntax error. const CommentType = enum { multiline, single, @@ -6882,6 +7154,13 @@ const LinkerContext = struct { else CommentType.single; + if (!c.options.minify_whitespace and + (c.options.output_format == .iife or c.options.output_format == .internal_kit_dev)) + { + j.pushStatic(" "); + line_offset.advance(" "); + } + switch (comment_type) { .multiline => { j.pushStatic("/* "); @@ -6906,12 +7185,13 @@ const LinkerContext = struct { line_offset.advance("\n"); }, } - prev_filename_comment = source_index; } if (is_runtime) { - line_offset.advance(compile_result.code()); - j.push(compile_result.code(), bun.default_allocator); + if (c.options.output_format != .internal_kit_dev) { + line_offset.advance(compile_result.code()); + j.push(compile_result.code(), bun.default_allocator); + } } else { j.push(compile_result.code(), bun.default_allocator); @@ -6951,15 +7231,44 @@ const LinkerContext = struct { j.push(cross_chunk_suffix, bun.default_allocator); } - if (c.options.output_format == .iife) { - const without_newline = "})();"; + switch (c.options.output_format) { + .iife => { + const without_newline = "})();"; - const with_newline = if (newline_before_comment) - without_newline ++ "\n" - else - without_newline; + const with_newline = if (newline_before_comment) + without_newline ++ "\n" + else + without_newline; - j.pushStatic(with_newline); + j.pushStatic(with_newline); + }, + .internal_kit_dev => { + { + const str = "}, {\n main: "; + j.pushStatic(str); + line_offset.advance(str); + } + { + const input = c.parse_graph.input_files.items(.source)[chunk.entry_point.source_index].path; + // var buf = MutableString.initEmpty(c.allocator); + // js_printer.quoteForJSONBuffer(input.pretty, &buf, true) catch bun.outOfMemory(); + // const str = buf.toOwnedSliceLeaky(); // c.allocator is an arena + const str = try std.fmt.allocPrint(c.allocator, "{d}", .{input.hashForKit()}); + j.pushStatic(str); + line_offset.advance(str); + } + // { + // const str = "\n react_refresh: "; + // j.pushStatic(str); + // line_offset.advance(str); + // } + { + const str = "\n});"; + j.pushStatic(str); + line_offset.advance(str); + } + }, + else => {}, } j.ensureNewlineAtEnd(); @@ -7068,12 +7377,12 @@ const LinkerContext = struct { \\ "sourcesContent": [ ); - const source_indicies_for_contents = source_id_map.keys(); - if (source_indicies_for_contents.len > 0) { + const source_indices_for_contents = source_id_map.keys(); + if (source_indices_for_contents.len > 0) { j.pushStatic("\n "); - j.pushStatic(quoted_source_map_contents[source_indicies_for_contents[0]]); + j.pushStatic(quoted_source_map_contents[source_indices_for_contents[0]]); - for (source_indicies_for_contents[1..]) |index| { + for (source_indices_for_contents[1..]) |index| { j.pushStatic(",\n "); j.pushStatic(quoted_source_map_contents[index]); } @@ -7172,7 +7481,7 @@ const LinkerContext = struct { } }; - // Include the path namespace in the hash so that files with the same + // Include the path namespace in the hash hasher.write(source.key_path.namespace); // Then include the file path @@ -7213,6 +7522,26 @@ const LinkerContext = struct { } } + // Also include the source map data in the hash. The source map is named the + // same name as the chunk name for ease of discovery. So we want the hash to + // change if the source map data changes even if the chunk data doesn't change. + // Otherwise the output path for the source map wouldn't change and the source + // map wouldn't end up being updated. + // + // Note that this means the contents of all input files are included in the + // hash because of "sourcesContent", so changing a comment in an input file + // can now change the hash of the output file. This only happens when you + // have source maps enabled (and "sourcesContent", which is on by default). + // + // The generated positions in the mappings here are in the output content + // *before* the final paths have been substituted. This may seem weird. + // However, I think this shouldn't cause issues because a) the unique key + // values are all always the same length so the offsets are deterministic + // and b) the final paths will be folded into the final hash later. + hasher.write(chunk.output_source_map.prefix.items); + hasher.write(chunk.output_source_map.mappings.items); + hasher.write(chunk.output_source_map.suffix.items); + return hasher.digest(); } @@ -7231,9 +7560,6 @@ const LinkerContext = struct { const ast: JSAst = c.graph.ast.get(source_index); switch (c.options.output_format) { - // TODO: - .preserve => {}, - .esm => { switch (flags.wrap) { .cjs => { @@ -7264,7 +7590,7 @@ const LinkerContext = struct { ) catch unreachable; }, else => { - if (flags.wrap == .esm) { + if (flags.wrap == .esm and ast.wrapper_ref.isValid()) { if (flags.is_async_or_has_async_dependency) { // "await init_foo();" stmts.append( @@ -7548,6 +7874,11 @@ const LinkerContext = struct { // TODO: iife .iife => {}, + .internal_kit_dev => { + // nothing needs to be done here, as the exports are already + // forwarded in the module closure. + }, + .cjs => { switch (flags.wrap) { .cjs => { @@ -7615,8 +7946,8 @@ const LinkerContext = struct { } const print_options = js_printer.Options{ - // TODO: IIFE - .indent = 0, + // TODO: IIFE indent + .indent = .{}, .has_run_symbol_renamer = true, .allocator = allocator, @@ -7625,8 +7956,9 @@ const LinkerContext = struct { .require_or_import_meta_for_source_callback = js_printer.RequireOrImportMeta.Callback.init(LinkerContext, requireOrImportMetaForSource, c), .minify_whitespace = c.options.minify_whitespace, + .print_dce_annotations = c.options.emit_dce_annotations, .minify_syntax = c.options.minify_syntax, - .const_values = c.graph.const_values, + // .const_values = c.graph.const_values, }; return .{ @@ -7800,33 +8132,21 @@ const LinkerContext = struct { .cjs => { // Replace the statement with a call to "require()" if this module is not wrapped try stmts.inside_wrapper_prefix.append( - Stmt.alloc( - S.Local, - S.Local{ - .decls = try G.Decl.List.fromSlice( - allocator, - &.{ - .{ - .binding = Binding.alloc( - allocator, - B.Identifier{ - .ref = namespace_ref, - }, - loc, - ), - .value = Expr.init( - E.RequireString, - E.RequireString{ - .import_record_index = import_record_index, - }, - loc, - ), - }, + Stmt.alloc(S.Local, .{ + .decls = try G.Decl.List.fromSlice( + allocator, + &.{ + .{ + .binding = Binding.alloc(allocator, B.Identifier{ + .ref = namespace_ref, + }, loc), + .value = Expr.init(E.RequireString, .{ + .import_record_index = import_record_index, + }, loc), }, - ), - }, - loc, - ), + }, + ), + }, loc), ); }, .esm => { @@ -7837,43 +8157,36 @@ const LinkerContext = struct { return true; } + const wrapper_ref = c.graph.ast.items(.wrapper_ref)[record.source_index.get()]; + if (wrapper_ref.isEmpty()) { + return true; + } + // Replace the statement with a call to "init()" const value: Expr = brk: { - const default = Expr.init( - E.Call, - E.Call{ - .target = Expr.initIdentifier( - c.graph.ast.items(.wrapper_ref)[record.source_index.get()], - loc, - ), - }, - loc, - ); + const default = Expr.init(E.Call, .{ + .target = Expr.initIdentifier( + wrapper_ref, + loc, + ), + }, loc); if (other_flags.is_async_or_has_async_dependency) { // This currently evaluates sibling dependencies in serial instead of in // parallel, which is incorrect. This should be changed to store a promise // and await all stored promises after all imports but before any code. - break :brk Expr.init( - E.Await, - E.Await{ - .value = default, - }, - loc, - ); + break :brk Expr.init(E.Await, .{ + .value = default, + }, loc); } break :brk default; }; try stmts.inside_wrapper_prefix.append( - Stmt.alloc( - S.SExpr, - S.SExpr{ - .value = value, - }, - loc, - ), + Stmt.alloc(S.SExpr, .{ + .value = value, + }, loc), ); }, } @@ -7890,12 +8203,12 @@ const LinkerContext = struct { /// /// prefix - outer /// ... - /// init_esm = () => { + /// var init_foo = __esm(() => { /// prefix - inner /// ... /// suffix - inenr - /// }; - /// ... + /// }); + /// ... /// suffix - outer /// /// Keep in mind that we may need to wrap ES modules in some cases too @@ -7906,7 +8219,6 @@ const LinkerContext = struct { /// In that case, when bundling, we still need to preserve that module /// namespace object (foo) because we cannot know what they are going to /// attempt to access statically - /// fn convertStmtsForChunk( c: *LinkerContext, source_index: u32, @@ -7917,6 +8229,12 @@ const LinkerContext = struct { wrap: WrapKind, ast: *const JSAst, ) !void { + // for Bun Kit, export wrapping is already done. Import wrapping is special cased. + if (c.options.output_format == .internal_kit_dev and source_index != Index.runtime.value) { + try c.convertStmtsForChunkKit(source_index, stmts, part_stmts, allocator, ast); + return; + } + const shouldExtractESMStmtsForWrap = wrap != .none; const shouldStripExports = c.options.mode != .passthrough or c.graph.files.items(.entry_point_kind)[source_index] != .none; @@ -7950,7 +8268,7 @@ const LinkerContext = struct { for (part_stmts) |stmt_| { var stmt = stmt_; - proccess_stmt: { + process_stmt: { switch (stmt.data) { .s_import => |s| { // "import * as ns from 'path'" @@ -8005,12 +8323,12 @@ const LinkerContext = struct { continue; } - break :proccess_stmt; + break :process_stmt; } // "export * from 'path'" if (!shouldStripExports) { - break :proccess_stmt; + break :process_stmt; } const record = ast.import_records.at(s.import_record_index); @@ -8085,27 +8403,20 @@ const LinkerContext = struct { } else { if (record.source_index.isValid()) { const flag = flags[record.source_index.get()]; - if (flag.wrap == .esm) { + const wrapper_ref = c.graph.ast.items(.wrapper_ref)[record.source_index.get()]; + if (flag.wrap == .esm and wrapper_ref.isValid()) { try stmts.inside_wrapper_prefix.append( - Stmt.alloc( - S.SExpr, - .{ - .value = Expr.init( - E.Call, - E.Call{ - .target = Expr.init( - E.Identifier, - E.Identifier{ - .ref = c.graph.ast.items(.wrapper_ref)[record.source_index.get()], - }, - stmt.loc, - ), + Stmt.alloc(S.SExpr, .{ + .value = Expr.init(E.Call, .{ + .target = Expr.init( + E.Identifier, + E.Identifier{ + .ref = wrapper_ref, }, stmt.loc, ), - }, - stmt.loc, - ), + }, stmt.loc), + }, stmt.loc), ); } } @@ -8176,7 +8487,6 @@ const LinkerContext = struct { .s_export_from => |s| { // "export {foo} from 'path'" - if (try c.shouldRemoveImportExportStmt( stmts, stmt.loc, @@ -8224,7 +8534,6 @@ const LinkerContext = struct { if (shouldStripExports) { // Remove export statements entirely - continue; } @@ -8236,7 +8545,6 @@ const LinkerContext = struct { }, .s_function => |s| { - // Strip the "export" keyword while bundling if (shouldStripExports and s.func.flags.contains(.is_export)) { // Be c areful to not modify the original statement @@ -8252,10 +8560,9 @@ const LinkerContext = struct { }, .s_class => |s| { - // Strip the "export" keyword while bundling if (shouldStripExports and s.is_export) { - // Be c areful to not modify the original statement + // Be careful to not modify the original statement stmt = Stmt.alloc( S.Class, S.Class{ @@ -8270,7 +8577,7 @@ const LinkerContext = struct { .s_local => |s| { // Strip the "export" keyword while bundling if (shouldStripExports and s.is_export) { - // Be c areful to not modify the original statement + // Be careful to not modify the original statement stmt = Stmt.alloc( S.Local, s.*, @@ -8414,6 +8721,112 @@ const LinkerContext = struct { } } + /// The conversion logic is completely different for format .kit_internal_hmr + fn convertStmtsForChunkKit( + c: *LinkerContext, + source_index: u32, + stmts: *StmtList, + part_stmts: []const js_ast.Stmt, + allocator: std.mem.Allocator, + ast: *const JSAst, + ) !void { + _ = source_index; // autofix + + const receiver_args = try allocator.dupe(G.Arg, &.{ + .{ .binding = Binding.alloc(allocator, B.Identifier{ .ref = ast.module_ref }, Logger.Loc.Empty) }, + }); + const module_id = Expr.initIdentifier(ast.module_ref, Logger.Loc.Empty); + for (part_stmts) |stmt| { + switch (stmt.data) { + else => { + try stmts.inside_wrapper_suffix.append(stmt); + }, + .s_local => |st| { + // TODO: check if this local is immediately assigned + // `require()` if so, we will instrument it with hot module + // reloading. other cases of `require` won't receive receive + // updates. + _ = st; // autofix + + try stmts.inside_wrapper_suffix.append(stmt); + }, + .s_import => |st| { + // hmr-runtime.ts defines `module.importSync` to be + // a synchronous import. this is different from + // require in that esm <-> cjs is handled + // automatically, instead of with bundler-added + // annotations like '__commonJS'. + // + // this is not done in the parse step because the final + // pretty path is not yet known. the other statement types + // are not handled here because some of those generate + // new local variables (it is too late to do that here). + const record = ast.import_records.at(st.import_record_index); + const path = c.parse_graph.input_files.items(.source)[record.source_index.get()].path; + + const is_bare_import = st.star_name_loc == null and st.items.len == 0 and st.default_name == null; + + const key_expr = Expr.init(E.InlinedEnum, .{ + .comment = path.pretty, + .value = Expr.init(E.Number, .{ + .value = @floatFromInt(path.hashForKit()), + }, stmt.loc), + }, stmt.loc); + + // module.importSync('path', (module) => ns = module) + const call = Expr.init(E.Call, .{ + .target = Expr.init(E.Dot, .{ + .target = module_id, + .name = "importSync", + .name_loc = stmt.loc, + }, stmt.loc), + .args = js_ast.ExprNodeList.init( + try allocator.dupe(Expr, if (is_bare_import) + &.{key_expr} + else + &.{ + key_expr, + Expr.init(E.Arrow, .{ + .args = receiver_args, + .body = .{ + .stmts = try allocator.dupe(Stmt, &.{Stmt.alloc(S.Return, .{ + .value = Expr.assign( + Expr.initIdentifier(st.namespace_ref, st.star_name_loc orelse stmt.loc), + module_id, + ), + }, stmt.loc)}), + .loc = stmt.loc, + }, + .prefer_expr = true, + }, stmt.loc), + }), + ), + }, stmt.loc); + + if (is_bare_import) { + // the import value is never read + try stmts.inside_wrapper_prefix.append(Stmt.alloc(S.SExpr, .{ .value = call }, stmt.loc)); + } else { + // 'let namespace = module.importSync(...)' + try stmts.inside_wrapper_prefix.append(Stmt.alloc(S.Local, .{ + .kind = .k_let, + .decls = try G.Decl.List.fromSlice(allocator, &.{.{ + .binding = Binding.alloc( + allocator, + B.Identifier{ .ref = st.namespace_ref }, + st.star_name_loc orelse stmt.loc, + ), + .value = call, + }}), + }, stmt.loc)); + } + + continue; + }, + } + } + } + fn runtimeFunction(c: *LinkerContext, name: []const u8) Ref { return c.graph.runtimeFunction(name); } @@ -8431,10 +8844,7 @@ const LinkerContext = struct { allocator: std.mem.Allocator, temp_allocator: std.mem.Allocator, ) js_printer.PrintResult { - - // var file = &c.graph.files.items(.input_file)[part.source_index.get()]; const parts: []js_ast.Part = c.graph.ast.items(.parts)[part_range.source_index.get()].slice()[part_range.part_index_begin..part_range.part_index_end]; - // const resolved_exports: []ResolvedExports = c.graph.meta.items(.resolved_exports); const all_flags: []const JSMeta.Flags = c.graph.meta.items(.flags); const flags = all_flags[part_range.source_index.get()]; const wrapper_part_index = if (flags.wrap != .none) @@ -8473,8 +8883,9 @@ const LinkerContext = struct { temp_allocator, flags.wrap, &ast, - ) catch |err| return .{ - .err = err, + ) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + return .{ .err = err }; }; switch (flags.wrap) { @@ -8629,24 +9040,55 @@ const LinkerContext = struct { var out_stmts: []js_ast.Stmt = stmts.all_stmts.items; + // Turn each module into a function if this is Kit + var stmt_storage: Stmt = undefined; + if (c.options.output_format == .internal_kit_dev and !part_range.source_index.isRuntime()) { + var clousure_args = std.BoundedArray(G.Arg, 2).fromSlice(&.{ + .{ .binding = Binding.alloc(temp_allocator, B.Identifier{ + .ref = ast.module_ref, + }, Logger.Loc.Empty) }, + }) catch unreachable; // is within bounds + + if (flags.wrap == .cjs and ast.flags.uses_exports_ref) { + clousure_args.appendAssumeCapacity( + .{ + .binding = Binding.alloc(temp_allocator, B.Identifier{ + .ref = ast.exports_ref, + }, Logger.Loc.Empty), + .default = Expr.allocate(temp_allocator, E.Dot, .{ + .target = Expr.initIdentifier(ast.module_ref, Logger.Loc.Empty), + .name = "exports", + .name_loc = Logger.Loc.Empty, + }, Logger.Loc.Empty), + }, + ); + } + + stmt_storage = Stmt.allocateExpr(temp_allocator, Expr.init(E.Function, .{ .func = .{ + .args = temp_allocator.dupe(G.Arg, clousure_args.slice()) catch bun.outOfMemory(), + .body = .{ + .stmts = stmts.all_stmts.items, + .loc = Logger.Loc.Empty, + }, + } }, Logger.Loc.Empty)); + out_stmts = (&stmt_storage)[0..1]; + } // Optionally wrap all statements in a closure - if (needs_wrapper) { + else if (needs_wrapper) { switch (flags.wrap) { .cjs => { - const uses_exports_ref = ast.uses_exports_ref(); - // Only include the arguments that are actually used - var args = std.ArrayList(js_ast.G.Arg).initCapacity( + var args = std.ArrayList(G.Arg).initCapacity( temp_allocator, - if (ast.uses_module_ref() or uses_exports_ref) 2 else 0, + if (ast.flags.uses_module_ref or ast.flags.uses_exports_ref) 2 else 0, ) catch unreachable; - if (ast.uses_module_ref() or uses_exports_ref) { + if (ast.flags.uses_module_ref or ast.flags.uses_exports_ref) { args.appendAssumeCapacity( - js_ast.G.Arg{ - .binding = js_ast.Binding.alloc( + G.Arg{ + .binding = Binding.alloc( temp_allocator, - js_ast.B.Identifier{ + B.Identifier{ .ref = ast.exports_ref, }, Logger.Loc.Empty, @@ -8654,12 +9096,12 @@ const LinkerContext = struct { }, ); - if (ast.uses_module_ref()) { + if (ast.flags.uses_module_ref) { args.appendAssumeCapacity( - js_ast.G.Arg{ - .binding = js_ast.Binding.alloc( + G.Arg{ + .binding = Binding.alloc( temp_allocator, - js_ast.B.Identifier{ + B.Identifier{ .ref = ast.module_ref, }, Logger.Loc.Empty, @@ -8729,13 +9171,16 @@ const LinkerContext = struct { // isn't async because then calling "require()" on that module would // swallow any exceptions thrown during module initialization. const is_async = flags.is_async_or_has_async_dependency; - const Hoisty = struct { - decls: std.ArrayList(G.Decl), + + const ExportHoist = struct { + decls: std.ArrayListUnmanaged(G.Decl), allocator: std.mem.Allocator, + next_value: ?Expr = null, pub fn wrapIdentifier(w: *@This(), loc: Logger.Loc, ref: Ref) Expr { w.decls.append( - G.Decl{ + w.allocator, + .{ .binding = Binding.alloc( w.allocator, B.Identifier{ @@ -8743,113 +9188,119 @@ const LinkerContext = struct { }, loc, ), + .value = w.next_value, }, - ) catch unreachable; - return Expr.init( - E.Identifier, - E.Identifier{ - .ref = ref, - }, - loc, - ); + ) catch bun.outOfMemory(); + + return Expr.initIdentifier(ref, loc); } }; - var hoisty = Hoisty{ - .decls = std.ArrayList(G.Decl).init(temp_allocator), + + var hoist = ExportHoist{ + .decls = .{}, .allocator = temp_allocator, }; + var inner_stmts = stmts.all_stmts.items; + // Hoist all top-level "var" and "function" declarations out of the closure { var end: usize = 0; - for (stmts.all_stmts.items) |stmt_| { - var stmt: Stmt = stmt_; - switch (stmt.data) { - .s_local => |local| { - if (local.was_commonjs_export or ast.commonjs_named_exports.count() == 0) { - var value: Expr = Expr.init(E.Missing, E.Missing{}, Logger.Loc.Empty); - for (local.decls.slice()) |*decl| { - const binding = decl.binding.toExpr(&hoisty); - if (decl.value) |other| { + for (stmts.all_stmts.items) |stmt| { + const transformed = switch (stmt.data) { + .s_local => |local| stmt: { + // Convert the declarations to assignments + var value = Expr.empty; + for (local.decls.slice()) |*decl| { + if (decl.value) |initializer| { + const can_be_moved = initializer.canBeMoved(); + hoist.next_value = if (can_be_moved) initializer else null; + const binding = decl.binding.toExpr(&hoist); + if (!can_be_moved) { value = value.joinWithComma( - binding.assign( - other, - ), + binding.assign(initializer), temp_allocator, ); } + } else { + _ = decl.binding.toExpr(&hoist); } + } - if (value.isEmpty()) { - continue; - } - stmt = Stmt.alloc( - S.SExpr, - S.SExpr{ - .value = value, - }, - stmt.loc, - ); + if (value.isEmpty()) { + continue; } + + break :stmt Stmt.allocateExpr(temp_allocator, value); }, - .s_class, .s_function => { - stmts.outside_wrapper_prefix.append(stmt) catch unreachable; + .s_function => { + stmts.outside_wrapper_prefix.append(stmt) catch bun.outOfMemory(); continue; }, - else => {}, - } - inner_stmts[end] = stmt; + .s_class => |class| stmt: { + if (class.class.canBeMoved()) { + stmts.outside_wrapper_prefix.append(stmt) catch bun.outOfMemory(); + continue; + } + + hoist.next_value = null; + + break :stmt Stmt.allocateExpr( + temp_allocator, + Expr.assign(hoist.wrapIdentifier( + class.class.class_name.?.loc, + class.class.class_name.?.ref.?, + ), .{ + .data = .{ .e_class = &class.class }, + .loc = stmt.loc, + }), + ); + }, + else => stmt, + }; + + inner_stmts[end] = transformed; end += 1; } inner_stmts.len = end; } - if (hoisty.decls.items.len > 0) { + if (hoist.decls.items.len > 0) { stmts.outside_wrapper_prefix.append( Stmt.alloc( S.Local, S.Local{ - .decls = G.Decl.List.fromList(hoisty.decls), + .decls = G.Decl.List.fromList(hoist.decls), }, Logger.Loc.Empty, ), ) catch unreachable; - hoisty.decls.items.len = 0; + hoist.decls.items.len = 0; } - // "__esm(() => { ... })" - var esm_args = temp_allocator.alloc(Expr, 1) catch unreachable; - esm_args[0] = Expr.init( - E.Arrow, - E.Arrow{ + if (inner_stmts.len > 0) { + // See the comment in needsWrapperRef for why the symbol + // is sometimes not generated. + bun.assert(!ast.wrapper_ref.isEmpty()); // js_parser's needsWrapperRef thought wrapper was not needed + + // "__esm(() => { ... })" + var esm_args = temp_allocator.alloc(Expr, 1) catch bun.outOfMemory(); + esm_args[0] = Expr.init(E.Arrow, .{ .args = &.{}, .is_async = is_async, .body = .{ .stmts = inner_stmts, .loc = Logger.Loc.Empty, }, - }, - Logger.Loc.Empty, - ); + }, Logger.Loc.Empty); - // "var init_foo = __esm(...);" - { - const value = Expr.init( - E.Call, - E.Call{ - .target = Expr.init( - E.Identifier, - E.Identifier{ - .ref = c.esm_runtime_ref, - }, - Logger.Loc.Empty, - ), - .args = bun.BabyList(Expr).init(esm_args), - }, - Logger.Loc.Empty, - ); + // "var init_foo = __esm(...);" + const value = Expr.init(E.Call, .{ + .target = Expr.initIdentifier(c.esm_runtime_ref, Logger.Loc.Empty), + .args = bun.BabyList(Expr).init(esm_args), + }, Logger.Loc.Empty); - var decls = temp_allocator.alloc(G.Decl, 1) catch unreachable; + var decls = temp_allocator.alloc(G.Decl, 1) catch bun.outOfMemory(); decls[0] = G.Decl{ .binding = Binding.alloc( temp_allocator, @@ -8862,14 +9313,53 @@ const LinkerContext = struct { }; stmts.outside_wrapper_prefix.append( - Stmt.alloc( - S.Local, - S.Local{ - .decls = G.Decl.List.init(decls), + Stmt.alloc(S.Local, .{ + .decls = G.Decl.List.init(decls), + }, Logger.Loc.Empty), + ) catch bun.outOfMemory(); + } else { + // // If this fails, then there will be places we reference + // // `init_foo` without it actually existing. + // bun.assert(ast.wrapper_ref.isEmpty()); + + // TODO: the edge case where we are wrong is when there + // are references to other ESM modules, but those get + // fully hoisted. The look like side effects, but they + // are removed. + // + // It is too late to retroactively delete the + // wrapper_ref, since printing has already begun. The + // most we can do to salvage the situation is to print + // an empty arrow function. + // + // This is marked as a TODO, because this can be solved + // via a count of external modules, decremented during + // linking. + if (!ast.wrapper_ref.isEmpty()) { + const value = Expr.init(E.Arrow, .{ + .args = &.{}, + .is_async = is_async, + .body = .{ + .stmts = inner_stmts, + .loc = Logger.Loc.Empty, }, - Logger.Loc.Empty, - ), - ) catch unreachable; + }, Logger.Loc.Empty); + + stmts.outside_wrapper_prefix.append( + Stmt.alloc(S.Local, .{ + .decls = G.Decl.List.fromSlice(temp_allocator, &.{.{ + .binding = Binding.alloc( + temp_allocator, + B.Identifier{ + .ref = ast.wrapper_ref, + }, + Logger.Loc.Empty, + ), + .value = value, + }}) catch bun.outOfMemory(), + }, Logger.Loc.Empty), + ) catch bun.outOfMemory(); + } } }, else => {}, @@ -8896,27 +9386,43 @@ const LinkerContext = struct { const print_options = js_printer.Options{ // TODO: IIFE - .indent = 0, - + .indent = .{}, .commonjs_named_exports = ast.commonjs_named_exports, .commonjs_named_exports_ref = ast.exports_ref, + .commonjs_module_ref = if (ast.flags.uses_module_ref or c.options.output_format == .internal_kit_dev) + ast.module_ref + else + Ref.None, .commonjs_named_exports_deoptimized = flags.wrap == .cjs, - .const_values = c.graph.const_values, + .commonjs_module_exports_assigned_deoptimized = ast.flags.commonjs_module_exports_assigned_deoptimized, + // .const_values = c.graph.const_values, + .ts_enums = c.graph.ts_enums, + .minify_whitespace = c.options.minify_whitespace, .minify_syntax = c.options.minify_syntax, - .module_type = c.options.output_format, + .module_type = switch (c.options.output_format) { + else => |format| format, + .internal_kit_dev => if (part_range.source_index.isRuntime()) .esm else .internal_kit_dev, + }, + .print_dce_annotations = c.options.emit_dce_annotations, .has_run_symbol_renamer = true, .allocator = allocator, .to_esm_ref = toESMRef, .to_commonjs_ref = toCommonJSRef, - .require_ref = runtimeRequireRef, + .require_ref = if (c.options.output_format == .internal_kit_dev) ast.require_ref else runtimeRequireRef, .require_or_import_meta_for_source_callback = js_printer.RequireOrImportMeta.Callback.init( LinkerContext, requireOrImportMetaForSource, c, ), .line_offset_tables = c.graph.files.items(.line_offset_table)[part_range.source_index.get()], + .target = c.options.target, + + .input_files_for_kit = if (c.options.output_format == .internal_kit_dev and !part_range.source_index.isRuntime()) + c.parse_graph.input_files.items(.source) + else + null, }; writer.buffer.reset(); @@ -9007,12 +9513,13 @@ const LinkerContext = struct { wait_group.deinit(); c.allocator.destroy(wait_group); } + errdefer wait_group.wait(); { var total_count: usize = 0; for (chunks, chunk_contexts) |*chunk, *chunk_ctx| { chunk_ctx.* = .{ .wg = wait_group, .c = c, .chunks = chunks, .chunk = chunk }; total_count += chunk.content.javascript.parts_in_chunk_in_order.len; - chunk.compile_results_for_chunk = c.allocator.alloc(CompileResult, chunk.content.javascript.parts_in_chunk_in_order.len) catch unreachable; + chunk.compile_results_for_chunk = c.allocator.alloc(CompileResult, chunk.content.javascript.parts_in_chunk_in_order.len) catch bun.outOfMemory(); } debug(" START {d} compiling part ranges", .{total_count}); @@ -9067,14 +9574,19 @@ const LinkerContext = struct { const DuplicateEntry = struct { sources: std.ArrayListUnmanaged(*Chunk) = .{}, }; - var duplicates_map: std.StringArrayHashMapUnmanaged(DuplicateEntry) = .{}; + var duplicates_map: bun.StringArrayHashMapUnmanaged(DuplicateEntry) = .{}; - // Compute the final hashes of each chunk. This can technically be done in - // parallel but it probably doesn't matter so much because we're not hashing - // that much data. - for (chunks) |*chunk| { - // TODO: non-isolated-hash - chunk.template.placeholder.hash = chunk.isolated_hash; + var chunk_visit_map = try AutoBitSet.initEmpty(c.allocator, chunks.len); + defer chunk_visit_map.deinit(c.allocator); + + // Compute the final hashes of each chunk, then use those to create the final + // paths of each chunk. This can technically be done in parallel but it + // probably doesn't matter so much because we're not hashing that much data. + for (chunks, 0..) |*chunk, index| { + var hash: ContentHasher = .{}; + c.appendIsolatedHashesForImportedChunks(&hash, chunks, @intCast(index), &chunk_visit_map); + chunk_visit_map.setAll(false); + chunk.template.placeholder.hash = hash.digest(); const rel_path = std.fmt.allocPrint(c.allocator, "{any}", .{chunk.template}) catch bun.outOfMemory(); bun.path.platformToPosixInPlace(u8, rel_path); @@ -9095,6 +9607,7 @@ const LinkerContext = struct { chunk.final_rel_path = rel_path_fixed; continue; } + chunk.final_rel_path = rel_path; } @@ -9409,7 +9922,7 @@ const LinkerContext = struct { .allocator = Chunk.IntermediateOutput.allocatorForSize(code_result.buffer.len), }, }, - .hash = chunk.isolated_hash, + .hash = chunk.template.placeholder.hash, .loader = .js, .input_path = input_path, .display_size = @as(u32, @truncate(display_size)), @@ -9457,6 +9970,59 @@ const LinkerContext = struct { return output_files; } + fn appendIsolatedHashesForImportedChunks( + c: *LinkerContext, + hash: *ContentHasher, + chunks: []Chunk, + index: u32, + chunk_visit_map: *AutoBitSet, + ) void { + // Only visit each chunk at most once. This is important because there may be + // cycles in the chunk import graph. If there's a cycle, we want to include + // the hash of every chunk involved in the cycle (along with all of their + // dependencies). This depth-first traversal will naturally do that. + if (chunk_visit_map.isSet(index)) { + return; + } + chunk_visit_map.set(index); + + // Visit the other chunks that this chunk imports before visiting this chunk + const chunk = &chunks[index]; + for (chunk.cross_chunk_imports.slice()) |import| { + c.appendIsolatedHashesForImportedChunks( + hash, + chunks, + import.chunk_index, + chunk_visit_map, + ); + } + + // Mix in hashes for referenced asset paths (i.e. the "file" loader) + switch (chunk.intermediate_output) { + .pieces => |pieces| for (pieces.slice()) |piece| { + if (piece.index.kind == .asset) { + var from_chunk_dir = std.fs.path.dirnamePosix(chunk.final_rel_path) orelse ""; + if (strings.eqlComptime(from_chunk_dir, ".")) + from_chunk_dir = ""; + + const additional_files: []AdditionalFile = c.graph.bundler_graph.input_files.items(.additional_files)[piece.index.index].slice(); + bun.assert(additional_files.len > 0); + switch (additional_files[0]) { + .output_file => |output_file_id| { + const path = c.graph.bundler_graph.additional_output_files.items[output_file_id].dest_path; + hash.write(bun.path.relativePlatform(from_chunk_dir, path, .posix, false)); + }, + .source_index => {}, + } + } + }, + else => {}, + } + + // Mix in the hash for this chunk + hash.write(std.mem.asBytes(&chunk.isolated_hash)); + } + fn writeOutputFilesToDisk( c: *LinkerContext, root_path: string, @@ -9677,7 +10243,7 @@ const LinkerContext = struct { c.parse_graph.input_files.items(.loader)[chunk.entry_point.source_index] else .js, - .hash = chunk.isolated_hash, + .hash = chunk.template.placeholder.hash, .output_kind = if (chunk.entry_point.is_entry_point) c.graph.files.items(.entry_point_kind)[chunk.entry_point.source_index].OutputKind() else @@ -9937,40 +10503,43 @@ const LinkerContext = struct { import_records: []bun.BabyList(bun.ImportRecord), entry_point_kinds: []EntryPoint.Kind, ) void { - if (comptime bun.Environment.allow_assert) - debugTreeShake( - "markFileLiveForTreeShaking({d}, {s}) = {s}", - .{ - source_index, - c.parse_graph.input_files.get(source_index).source.path.text, - if (c.graph.files_live.isSet(source_index)) "seen" else "not seen", - }, - ); + if (comptime bun.Environment.allow_assert) { + debugTreeShake("markFileLiveForTreeShaking({d}, {s}) = {s}", .{ + source_index, + c.parse_graph.input_files.get(source_index).source.path.text, + if (c.graph.files_live.isSet(source_index)) "seen" else "not seen", + }); + } - if (c.graph.files_live.isSet(source_index)) - return; + defer if (Environment.allow_assert) { + debugTreeShake("end()", .{}); + }; + if (c.graph.files_live.isSet(source_index)) { + if (Environment.allow_assert) { + debugTreeShake("already set", .{}); + } + return; + } c.graph.files_live.set(source_index); - // TODO: CSS source index - - const id = source_index; - if (@as(usize, id) >= c.graph.ast.len) + if (source_index >= c.graph.ast.len) { + bun.assert(false); return; - const _parts = parts[id].slice(); - for (_parts, 0..) |part, part_index| { + } + + for (parts[source_index].slice(), 0..) |part, part_index| { var can_be_removed_if_unused = part.can_be_removed_if_unused; if (can_be_removed_if_unused and part.tag == .commonjs_named_export) { - if (c.graph.meta.items(.flags)[id].wrap == .cjs) { + if (c.graph.meta.items(.flags)[source_index].wrap == .cjs) { can_be_removed_if_unused = false; } } // Also include any statement-level imports - for (part.import_record_indices.slice()) |import_record_Index| { - var record: *ImportRecord = &import_records[source_index].slice()[import_record_Index]; - + for (part.import_record_indices.slice()) |import_index| { + const record = import_records[source_index].at(import_index); if (record.kind != .stmt) continue; @@ -9979,7 +10548,11 @@ const LinkerContext = struct { // Don't include this module for its side effects if it can be // considered to have no side effects - if (side_effects[other_source_index] != .has_side_effects and !c.options.ignore_dce_annotations) { + const se = side_effects[other_source_index]; + + if (se != .has_side_effects and + !c.options.ignore_dce_annotations) + { continue; } @@ -10007,11 +10580,11 @@ const LinkerContext = struct { if (!can_be_removed_if_unused or (!part.force_tree_shaking and !c.options.tree_shaking and - entry_point_kinds[id].isEntryPoint())) + entry_point_kinds[source_index].isEntryPoint())) { - _ = c.markPartLiveForTreeShaking( - @as(u32, @intCast(part_index)), - id, + c.markPartLiveForTreeShaking( + @intCast(part_index), + source_index, side_effects, parts, import_records, @@ -10024,39 +10597,57 @@ const LinkerContext = struct { pub fn markPartLiveForTreeShaking( c: *LinkerContext, part_index: Index.Int, - id: Index.Int, + source_index: Index.Int, side_effects: []_resolver.SideEffects, parts: []bun.BabyList(js_ast.Part), import_records: []bun.BabyList(bun.ImportRecord), entry_point_kinds: []EntryPoint.Kind, - ) bool { - var part: *js_ast.Part = &parts[id].slice()[part_index]; + ) void { + const part: *js_ast.Part = &parts[source_index].slice()[part_index]; + // only once if (part.is_live) { - return false; + return; } - part.is_live = true; - if (comptime bun.Environment.allow_assert) + + if (comptime bun.Environment.isDebug) { debugTreeShake("markPartLiveForTreeShaking({d}): {s}:{d} = {d}, {s}", .{ - id, - c.parse_graph.input_files.get(id).source.path.text, + source_index, + c.parse_graph.input_files.get(source_index).source.path.text, part_index, if (part.stmts.len > 0) part.stmts[0].loc.start else Logger.Loc.Empty.start, if (part.stmts.len > 0) @tagName(part.stmts[0].data) else @tagName(Stmt.empty().data), }); + } + + defer if (Environment.allow_assert) { + debugTreeShake("end()", .{}); + }; // Include the file containing this part c.markFileLiveForTreeShaking( - id, + source_index, side_effects, parts, import_records, entry_point_kinds, ); + if (Environment.enable_logs and part.dependencies.slice().len == 0) { + logPartDependencyTree("markPartLiveForTreeShaking {d}:{d} | EMPTY", .{ + source_index, part_index, + }); + } + for (part.dependencies.slice()) |dependency| { - _ = c.markPartLiveForTreeShaking( + if (Environment.enable_logs and source_index != 0 and dependency.source_index.get() != 0) { + logPartDependencyTree("markPartLiveForTreeShaking: {d}:{d} --> {d}:{d}\n", .{ + source_index, part_index, dependency.source_index.get(), dependency.part_index, + }); + } + + c.markPartLiveForTreeShaking( dependency.part_index, dependency.source_index.get(), side_effects, @@ -10065,18 +10656,20 @@ const LinkerContext = struct { entry_point_kinds, ); } - - return true; } pub fn matchImportWithExport( c: *LinkerContext, - init_tracker: *ImportTracker, + init_tracker: ImportTracker, re_exports: *std.ArrayList(js_ast.Dependency), ) MatchImport { + const cycle_detector_top = c.cycle_detector.items.len; + defer c.cycle_detector.shrinkRetainingCapacity(cycle_detector_top); + var tracker = init_tracker; var ambiguous_results = std.ArrayList(MatchImport).init(c.allocator); defer ambiguous_results.clearAndFree(); + var result: MatchImport = MatchImport{}; const named_imports = c.graph.ast.items(.named_imports); @@ -10090,30 +10683,26 @@ const LinkerContext = struct { // // This uses a O(n^2) array scan instead of a O(n) map because the vast // majority of cases have one or two elements - for (c.cycle_detector.items) |prev_tracker| { - if (std.meta.eql(tracker.*, prev_tracker)) { + for (c.cycle_detector.items[cycle_detector_top..]) |prev_tracker| { + if (std.meta.eql(tracker, prev_tracker)) { result = .{ .kind = .cycle }; break :loop; } } - const prev_import_ref = tracker.import_ref; - if (tracker.source_index.isInvalid()) { // External break; } const prev_source_index = tracker.source_index.get(); - c.cycle_detector.append(tracker.*) catch unreachable; + c.cycle_detector.append(tracker) catch bun.outOfMemory(); // Resolve the import by one step - var advanced = c.advanceImportTracker(tracker); - advanced.tracker.* = advanced.value; - const next_tracker = advanced.tracker.*; + const advanced = c.advanceImportTracker(&tracker); + const next_tracker = advanced.value; const status = advanced.status; const potentially_ambiguous_export_star_refs = advanced.import_data; - const other_id = advanced.value.source_index.get(); switch (status) { .cjs, .cjs_without_exports, .disabled, .external => { @@ -10127,7 +10716,7 @@ const LinkerContext = struct { // property access. Don't do this if the namespace reference is invalid // though. This is the case for star imports, where the import is the // namespace. - const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(prev_import_ref).?; + const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(tracker.import_ref).?; if (named_import.namespace_ref != null and named_import.namespace_ref.?.isValid()) { if (result.kind == .normal) { @@ -10163,13 +10752,13 @@ const LinkerContext = struct { // if the file was rewritten from CommonJS into ESM // and the developer imported an export that doesn't exist // We don't do a runtime error since that CJS would have returned undefined. - const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(prev_import_ref).?; + const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(tracker.import_ref).?; if (named_import.namespace_ref != null and named_import.namespace_ref.?.isValid()) { - const symbol = c.graph.symbols.get(prev_import_ref).?; + const symbol = c.graph.symbols.get(tracker.import_ref).?; symbol.import_item_status = .missing; result.kind = .normal_and_namespace; - result.namespace_ref = prev_import_ref; + result.namespace_ref = tracker.import_ref; result.alias = named_import.alias.?; result.name_loc = named_import.alias_loc orelse Logger.Loc.Empty; } @@ -10177,7 +10766,7 @@ const LinkerContext = struct { .dynamic_fallback => { // If it's a file with dynamic export fallback, rewrite the import to a property access - const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(prev_import_ref).?; + const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(tracker.import_ref).?; if (named_import.namespace_ref != null and named_import.namespace_ref.?.isValid()) { if (result.kind == .normal) { result.kind = .normal_and_namespace; @@ -10194,8 +10783,8 @@ const LinkerContext = struct { }, .no_match => { // Report mismatched imports and exports - const symbol = c.graph.symbols.get(prev_import_ref).?; - const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(prev_import_ref).?; + const symbol = c.graph.symbols.get(tracker.import_ref).?; + const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(tracker.import_ref).?; const source = c.source_(prev_source_index); const next_source = c.source_(next_tracker.source_index.get()); @@ -10276,15 +10865,7 @@ const LinkerContext = struct { for (potentially_ambiguous_export_star_refs) |*ambiguous_tracker| { // If this is a re-export of another import, follow the import if (named_imports[ambiguous_tracker.data.source_index.get()].contains(ambiguous_tracker.data.import_ref)) { - c.cycle_detector.clearRetainingCapacity(); - c.swap_cycle_detector.clearRetainingCapacity(); - - const old_cycle_detector = c.cycle_detector; - c.cycle_detector = c.swap_cycle_detector; - const ambig = c.matchImportWithExport(&ambiguous_tracker.data, re_exports); - c.cycle_detector.clearRetainingCapacity(); - c.swap_cycle_detector = c.cycle_detector; - c.cycle_detector = old_cycle_detector; + const ambig = c.matchImportWithExport(ambiguous_tracker.data, re_exports); ambiguous_results.append(ambig) catch unreachable; } else { ambiguous_results.append(.{ @@ -10311,7 +10892,7 @@ const LinkerContext = struct { // Depend on the statement(s) that declared this import symbol in the // original file { - const deps = c.topLevelSymbolsToParts(other_id, tracker.import_ref); + const deps = c.topLevelSymbolsToParts(prev_source_index, tracker.import_ref); re_exports.ensureUnusedCapacity(deps.len) catch unreachable; for (deps) |dep| { re_exports.appendAssumeCapacity( @@ -10327,7 +10908,7 @@ const LinkerContext = struct { // iteration of the loop to resolve that import as well const next_id = next_tracker.source_index.get(); if (named_imports[next_id].contains(next_tracker.import_ref)) { - tracker.* = next_tracker; + tracker = next_tracker; continue :loop; } }, @@ -10386,33 +10967,33 @@ const LinkerContext = struct { // However, that generation is special-cased for various reasons and is // done later on. Still, we're going to need to ensure that this file // both depends on the "__commonJS" symbol and declares the "require_foo" - // symbol. Instead of special-casing this during the reachablity analysis + // symbol. Instead of special-casing this during the reachability analysis // below, we just append a dummy part to the end of the file with these - // dependencies and let the general-purpose reachablity analysis take care + // dependencies and let the general-purpose reachability analysis take care // of it. .cjs => { const common_js_parts = c.topLevelSymbolsToPartsForRuntime(c.cjs_runtime_ref); - var total_dependencies_count = common_js_parts.len; - var runtime_parts = c.graph.ast.items(.parts)[Index.runtime.get()].slice(); - for (common_js_parts) |part_id| { - var part: *js_ast.Part = &runtime_parts[part_id]; + const runtime_parts = c.graph.ast.items(.parts)[Index.runtime.get()].slice(); + const part: *js_ast.Part = &runtime_parts[part_id]; const symbol_refs = part.symbol_uses.keys(); for (symbol_refs) |ref| { if (ref.eql(c.cjs_runtime_ref)) continue; - total_dependencies_count += c.topLevelSymbolsToPartsForRuntime(ref).len; } } - // generate a dummy part that depends on the "__commonJS" symbol - const dependencies = c.allocator.alloc(js_ast.Dependency, common_js_parts.len) catch unreachable; - for (common_js_parts, dependencies) |part, *cjs| { - cjs.* = .{ - .part_index = part, - .source_index = Index.runtime, - }; - } + // Generate a dummy part that depends on the "__commonJS" symbol. + const dependencies: []js_ast.Dependency = if (c.options.output_format != .internal_kit_dev) brk: { + const dependencies = c.allocator.alloc(js_ast.Dependency, common_js_parts.len) catch bun.outOfMemory(); + for (common_js_parts, dependencies) |part, *cjs| { + cjs.* = .{ + .part_index = part, + .source_index = Index.runtime, + }; + } + break :brk dependencies; + } else &.{}; const part_index = c.graph.addPartToFile( source_index, .{ @@ -10437,13 +11018,17 @@ const LinkerContext = struct { ) catch unreachable; bun.assert(part_index != js_ast.namespace_export_part_index); wrapper_part_index.* = Index.part(part_index); - c.graph.generateSymbolImportAndUse( - source_index, - part_index, - c.cjs_runtime_ref, - 1, - Index.runtime, - ) catch unreachable; + + // Kit uses a wrapping approach that does not use __commonJS + if (c.options.output_format != .internal_kit_dev) { + c.graph.generateSymbolImportAndUse( + source_index, + part_index, + c.cjs_runtime_ref, + 1, + Index.runtime, + ) catch unreachable; + } }, .esm => { @@ -10457,7 +11042,10 @@ const LinkerContext = struct { // // This depends on the "__esm" symbol and declares the "init_foo" symbol // for similar reasons to the CommonJS closure above. - const esm_parts = c.topLevelSymbolsToPartsForRuntime(c.esm_runtime_ref); + const esm_parts = if (wrapper_ref.isValid() and c.options.output_format != .internal_kit_dev) + c.topLevelSymbolsToPartsForRuntime(c.esm_runtime_ref) + else + &.{}; // generate a dummy part that depends on the "__esm" symbol const dependencies = c.allocator.alloc(js_ast.Dependency, esm_parts.len) catch unreachable; @@ -10486,19 +11074,21 @@ const LinkerContext = struct { ) catch unreachable; bun.assert(part_index != js_ast.namespace_export_part_index); wrapper_part_index.* = Index.part(part_index); - c.graph.generateSymbolImportAndUse( - source_index, - part_index, - c.esm_runtime_ref, - 1, - Index.runtime, - ) catch unreachable; + if (wrapper_ref.isValid() and c.options.output_format != .internal_kit_dev) { + c.graph.generateSymbolImportAndUse( + source_index, + part_index, + c.esm_runtime_ref, + 1, + Index.runtime, + ) catch bun.outOfMemory(); + } }, else => {}, } } - pub fn advanceImportTracker(c: *LinkerContext, tracker: *ImportTracker) ImportTracker.Iterator { + pub fn advanceImportTracker(c: *LinkerContext, tracker: *const ImportTracker) ImportTracker.Iterator { const id = tracker.source_index.get(); var named_imports: *JSAst.NamedImports = &c.graph.ast.items(.named_imports)[id]; var import_records = c.graph.ast.items(.import_records)[id]; @@ -10511,7 +11101,6 @@ const LinkerContext = struct { return .{ .value = .{}, .status = .external, - .tracker = tracker, }; // Is this an external file? @@ -10520,7 +11109,6 @@ const LinkerContext = struct { return .{ .value = .{}, .status = .external, - .tracker = tracker, }; } @@ -10534,7 +11122,6 @@ const LinkerContext = struct { .source_index = record.source_index, }, .status = .disabled, - .tracker = tracker, }; } @@ -10556,7 +11143,6 @@ const LinkerContext = struct { .import_ref = Ref.None, }, .status = .cjs_without_exports, - .tracker = tracker, }; } const other_kind = exports_kind[other_id]; @@ -10568,7 +11154,6 @@ const LinkerContext = struct { .import_ref = Ref.None, }, .status = .cjs, - .tracker = tracker, }; } @@ -10581,7 +11166,6 @@ const LinkerContext = struct { .value = matching_export.data, .status = .found, .import_data = matching_export.potentially_ambiguous_export_star_refs.slice(), - .tracker = tracker, }; } } @@ -10597,7 +11181,6 @@ const LinkerContext = struct { }, .status = .found, .import_data = matching_export.potentially_ambiguous_export_star_refs.slice(), - .tracker = tracker, }; } @@ -10613,7 +11196,6 @@ const LinkerContext = struct { .dynamic_fallback_interop_default else .dynamic_fallback, - .tracker = tracker, }; } @@ -10623,7 +11205,6 @@ const LinkerContext = struct { return .{ .value = .{}, .status = .probably_typescript_type, - .tracker = tracker, }; } @@ -10632,7 +11213,6 @@ const LinkerContext = struct { .source_index = Index.source(other_source_index), }, .status = .no_match, - .tracker = tracker, }; } @@ -10666,15 +11246,12 @@ const LinkerContext = struct { const import_ref = ref; - var import_tracker = ImportData{ - .data = .{ + var re_exports = std.ArrayList(js_ast.Dependency).init(c.allocator); + const result = c.matchImportWithExport( + .{ .source_index = Index.source(source_index), .import_ref = import_ref, }, - }; - var re_exports = std.ArrayList(js_ast.Dependency).init(c.allocator); - const result = c.matchImportWithExport( - &import_tracker.data, &re_exports, ); @@ -10792,14 +11369,13 @@ const LinkerContext = struct { if (i == source_index) return; } - - this.source_index_stack.append(source_index) catch unreachable; + this.source_index_stack.append(source_index) catch bun.outOfMemory(); const stack_end_pos = this.source_index_stack.items.len; - const id = source_index; + defer this.source_index_stack.shrinkRetainingCapacity(stack_end_pos - 1); - const import_records = this.import_records_list[id].slice(); + const import_records = this.import_records_list[source_index].slice(); - for (this.export_star_records[id]) |import_id| { + for (this.export_star_records[source_index]) |import_id| { const other_source_index = import_records[import_id].source_index.get(); const other_id = other_source_index; @@ -10816,9 +11392,11 @@ const LinkerContext = struct { // re-exports as property accesses off of a generated require() call. if (this.exports_kind[other_id] == .cjs) continue; + var iter = this.named_exports[other_id].iterator(); next_export: while (iter.next()) |entry| { const alias = entry.key_ptr.*; + const name = entry.value_ptr.*; // ES6 export star statements ignore exports named "default" if (strings.eqlComptime(alias, "default")) @@ -10830,34 +11408,35 @@ const LinkerContext = struct { continue :next_export; } } - const ref = entry.value_ptr.ref; - var resolved = resolved_exports.getOrPut(this.allocator, entry.key_ptr.*) catch unreachable; - if (!resolved.found_existing) { - resolved.value_ptr.* = .{ + + const gop = resolved_exports.getOrPut(this.allocator, alias) catch bun.outOfMemory(); + if (!gop.found_existing) { + // Initialize the re-export + gop.value_ptr.* = .{ .data = .{ - .import_ref = ref, + .import_ref = name.ref, .source_index = Index.source(other_source_index), - .name_loc = entry.value_ptr.alias_loc, + .name_loc = name.alias_loc, }, }; // Make sure the symbol is marked as imported so that code splitting // imports it correctly if it ends up being shared with another chunk - this.imports_to_bind[id].put(this.allocator, entry.value_ptr.ref, .{ + this.imports_to_bind[source_index].put(this.allocator, name.ref, .{ .data = .{ - .import_ref = ref, + .import_ref = name.ref, .source_index = Index.source(other_source_index), }, - }) catch unreachable; - } else if (resolved.value_ptr.data.source_index.get() != other_source_index) { + }) catch bun.outOfMemory(); + } else if (gop.value_ptr.data.source_index.get() != other_source_index) { // Two different re-exports colliding makes it potentially ambiguous - resolved.value_ptr.potentially_ambiguous_export_star_refs.push(this.allocator, .{ + gop.value_ptr.potentially_ambiguous_export_star_refs.push(this.allocator, .{ .data = .{ .source_index = Index.source(other_source_index), - .import_ref = ref, - .name_loc = entry.value_ptr.alias_loc, + .import_ref = name.ref, + .name_loc = name.alias_loc, }, - }) catch unreachable; + }) catch bun.outOfMemory(); } } @@ -10979,7 +11558,7 @@ const LinkerContext = struct { export_star_map: std.AutoHashMap(Index.Int, void), entry_point_kinds: []EntryPoint.Kind, export_star_records: [][]u32, - output_format: options.OutputFormat, + output_format: options.Format, pub fn hasDynamicExportsDueToExportStar(this: *DependencyWrapper, source_index: Index.Int) bool { // Terminate the traversal now if this file already has dynamic exports @@ -11104,7 +11683,6 @@ pub const ImportTracker = struct { status: Status = Status.no_match, value: ImportTracker = .{}, import_data: []ImportData = &.{}, - tracker: *ImportTracker, }; }; @@ -11427,7 +12005,7 @@ pub const Chunk = struct { }; pub const OutputPiece = struct { - // layed out like this so it takes up the same amount of space as a []const u8 + // laid out like this so it takes up the same amount of space as a []const u8 data_ptr: [*]const u8 = undefined, data_len: u32 = 0, diff --git a/src/bunfig.zig b/src/bunfig.zig index 6b4167fcfbfd3..ede8389cde3c3 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -21,6 +21,7 @@ const Npm = @import("./install/npm.zig"); const PackageManager = @import("./install/install.zig").PackageManager; const PackageJSON = @import("./resolver/package_json.zig").PackageJSON; const resolver = @import("./resolver/resolver.zig"); +const TestCommand = @import("./cli/test_command.zig").TestCommand; pub const MacroImportReplacementMap = bun.StringArrayHashMap(string); pub const MacroMap = bun.StringArrayHashMapUnmanaged(MacroImportReplacementMap); pub const BundlePackageOverride = bun.StringArrayHashMapUnmanaged(options.BundleOverride); @@ -42,7 +43,7 @@ pub const Bunfig = struct { &.{ "online", OfflineMode.online }, }); - const Parser = struct { + pub const Parser = struct { json: js_ast.Expr, source: *const logger.Source, log: *logger.Log, @@ -55,6 +56,11 @@ pub const Bunfig = struct { return error.@"Invalid Bunfig"; } + fn addErrorFormat(this: *Parser, loc: logger.Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + this.log.addErrorFmt(this.source, loc, allocator, text, args) catch unreachable; + return error.@"Invalid Bunfig"; + } + fn parseRegistryURLString(this: *Parser, str: *js_ast.E.String) !Api.NpmRegistry { const url = URL.parse(str.data); var registry = std.mem.zeroes(Api.NpmRegistry); @@ -252,6 +258,41 @@ pub const Bunfig = struct { this.ctx.test_options.coverage.enabled = expr.data.e_boolean.value; } + if (test_.get("coverageReporter")) |expr| brk: { + this.ctx.test_options.coverage.reporters = .{ .text = false, .lcov = false }; + if (expr.data == .e_string) { + const item_str = expr.asString(bun.default_allocator) orelse ""; + if (bun.strings.eqlComptime(item_str, "text")) { + this.ctx.test_options.coverage.reporters.text = true; + } else if (bun.strings.eqlComptime(item_str, "lcov")) { + this.ctx.test_options.coverage.reporters.lcov = true; + } else { + try this.addErrorFormat(expr.loc, allocator, "Invalid coverage reporter \"{s}\"", .{item_str}); + } + + break :brk; + } + + try this.expect(expr, .e_array); + const items = expr.data.e_array.items.slice(); + for (items) |item| { + try this.expectString(item); + const item_str = item.asString(bun.default_allocator) orelse ""; + if (bun.strings.eqlComptime(item_str, "text")) { + this.ctx.test_options.coverage.reporters.text = true; + } else if (bun.strings.eqlComptime(item_str, "lcov")) { + this.ctx.test_options.coverage.reporters.lcov = true; + } else { + try this.addErrorFormat(item.loc, allocator, "Invalid coverage reporter \"{s}\"", .{item_str}); + } + } + } + + if (test_.get("coverageDir")) |expr| { + try this.expectString(expr); + this.ctx.test_options.coverage.reports_directory = try expr.data.e_string.string(allocator); + } + if (test_.get("coverageThreshold")) |expr| outer: { if (expr.data == .e_number) { this.ctx.test_options.coverage.fractions.functions = expr.data.e_number.value; @@ -341,39 +382,20 @@ pub const Bunfig = struct { } if (_bun.get("scopes")) |scopes| { - var registry_map = install.scoped orelse std.mem.zeroes(Api.NpmRegistryMap); + var registry_map = install.scoped orelse Api.NpmRegistryMap{}; try this.expect(scopes, .e_object); - const count = scopes.data.e_object.properties.len + registry_map.registries.len; - var registries = try std.ArrayListUnmanaged(Api.NpmRegistry).initCapacity(this.allocator, count); - registries.appendSliceAssumeCapacity(registry_map.registries); - - var names = try std.ArrayListUnmanaged(string).initCapacity(this.allocator, count); - names.appendSliceAssumeCapacity(registry_map.scopes); + try registry_map.scopes.ensureUnusedCapacity(this.allocator, scopes.data.e_object.properties.len); for (scopes.data.e_object.properties.slice()) |prop| { const name_ = prop.key.?.asString(this.allocator) orelse continue; const value = prop.value orelse continue; if (name_.len == 0) continue; const name = if (name_[0] == '@') name_[1..] else name_; - var index = names.items.len; - for (names.items, 0..) |comparator, i| { - if (strings.eql(name, comparator)) { - index = i; - break; - } - } - - if (index == names.items.len) { - names.items.len += 1; - registries.items.len += 1; - } - names.items[index] = name; - registries.items[index] = try this.parseRegistry(value); + const registry = try this.parseRegistry(value); + try registry_map.scopes.put(this.allocator, name, registry); } - registry_map.registries = registries.items; - registry_map.scopes = names.items; install.scoped = registry_map; } diff --git a/src/c.zig b/src/c.zig index 4c08263c1db08..ff9226a66087c 100644 --- a/src/c.zig +++ b/src/c.zig @@ -104,9 +104,8 @@ pub fn lstat_absolute(path: [:0]const u8) !Stat { // renameatZ fails when renaming across mount points // we assume that this is relatively uncommon -// TODO: change types to use `bun.FileDescriptor` pub fn moveFileZ(from_dir: bun.FileDescriptor, filename: [:0]const u8, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void { - switch (bun.sys.renameat(from_dir, filename, to_dir, destination)) { + switch (bun.sys.renameatConcurrentlyWithoutFallback(from_dir, filename, to_dir, destination)) { .err => |err| { // allow over-writing an empty directory if (err.getErrno() == .ISDIR) { @@ -137,7 +136,7 @@ pub fn moveFileZWithHandle(from_handle: bun.FileDescriptor, from_dir: bun.FileDe } if (err.getErrno() == .XDEV) { - try copyFileZSlowWithHandle(from_handle, to_dir, destination); + try copyFileZSlowWithHandle(from_handle, to_dir, destination).unwrap(); _ = bun.sys.unlinkat(from_dir, filename); } @@ -147,55 +146,77 @@ pub fn moveFileZWithHandle(from_handle: bun.FileDescriptor, from_dir: bun.FileDe } } +const Maybe = bun.sys.Maybe; + // On Linux, this will be fast because sendfile() supports copying between two file descriptors on disk // macOS & BSDs will be slow because pub fn moveFileZSlow(from_dir: bun.FileDescriptor, filename: [:0]const u8, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void { - const in_handle = try bun.sys.openat(from_dir, filename, bun.O.RDONLY | bun.O.CLOEXEC, if (Environment.isWindows) 0 else 0o644).unwrap(); + return try moveFileZSlowMaybe(from_dir, filename, to_dir, destination).unwrap(); +} + +pub fn moveFileZSlowMaybe(from_dir: bun.FileDescriptor, filename: [:0]const u8, to_dir: bun.FileDescriptor, destination: [:0]const u8) Maybe(void) { + const in_handle = switch (bun.sys.openat(from_dir, filename, bun.O.RDONLY | bun.O.CLOEXEC, if (Environment.isWindows) 0 else 0o644)) { + .result => |f| f, + .err => |e| return .{ .err = e }, + }; defer _ = bun.sys.close(in_handle); _ = bun.sys.unlinkat(from_dir, filename); - try copyFileZSlowWithHandle(in_handle, to_dir, destination); + return copyFileZSlowWithHandle(in_handle, to_dir, destination); } -pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void { +pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDescriptor, destination: [:0]const u8) Maybe(void) { if (comptime Environment.isWindows) { var buf0: bun.WPathBuffer = undefined; var buf1: bun.WPathBuffer = undefined; - const dest = try bun.sys.normalizePathWindows(u8, to_dir, destination, &buf0).unwrap(); + const dest = switch (bun.sys.normalizePathWindows(u8, to_dir, destination, &buf0)) { + .result => |x| x, + .err => |e| return .{ .err = e }, + }; const src_len = bun.windows.GetFinalPathNameByHandleW(in_handle.cast(), &buf1, buf1.len, 0); if (src_len == 0) { - return error.EBUSY; + return Maybe(void).errno(bun.C.E.BUSY, .GetFinalPathNameByHandle); } else if (src_len >= buf1.len) { - return error.ENAMETOOLONG; + return Maybe(void).errno(bun.C.E.NAMETOOLONG, .GetFinalPathNameByHandle); } const src = buf1[0..src_len :0]; - try bun.copyFile(src, dest); - return; - } - - const stat_ = if (comptime Environment.isPosix) try std.posix.fstat(in_handle.cast()) else void{}; - - // Attempt to delete incase it already existed. - // This fixes ETXTBUSY on Linux - _ = bun.sys.unlinkat(to_dir, destination); - - const out_handle = try bun.sys.openat( - to_dir, - destination, - bun.O.WRONLY | bun.O.CREAT | bun.O.CLOEXEC | bun.O.TRUNC, - if (comptime Environment.isPosix) 0o644 else 0, - ).unwrap(); - defer _ = bun.sys.close(out_handle); + return bun.copyFile(src, dest); + } else { + const stat_ = switch (bun.sys.fstat(in_handle)) { + .result => |s| s, + .err => |e| return .{ .err = e }, + }; + + // Attempt to delete incase it already existed. + // This fixes ETXTBUSY on Linux + _ = bun.sys.unlinkat(to_dir, destination); + + const out_handle = switch (bun.sys.openat( + to_dir, + destination, + bun.O.WRONLY | bun.O.CREAT | bun.O.CLOEXEC | bun.O.TRUNC, + if (comptime Environment.isPosix) 0o644 else 0, + )) { + .result => |fd| fd, + .err => |e| return .{ .err = e }, + }; + defer _ = bun.sys.close(out_handle); + + if (comptime Environment.isLinux) { + _ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size)); + } - if (comptime Environment.isLinux) { - _ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size)); - } + switch (bun.copyFile(in_handle.cast(), out_handle.cast())) { + .err => |e| return .{ .err = e }, + .result => {}, + } - try bun.copyFile(in_handle.cast(), out_handle.cast()); + if (comptime Environment.isPosix) { + _ = fchmod(out_handle.cast(), stat_.mode); + _ = fchown(out_handle.cast(), stat_.uid, stat_.gid); + } - if (comptime Environment.isPosix) { - _ = fchmod(out_handle.cast(), stat_.mode); - _ = fchown(out_handle.cast(), stat_.uid, stat_.gid); + return Maybe(void).success; } } diff --git a/src/cli.zig b/src/cli.zig index 94b10894225a7..ad59352479eb1 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -70,6 +70,19 @@ pub const Cli = struct { pub threadlocal var is_main_thread: bool = false; }; +pub const debug_flags = if (Environment.isDebug) struct { + var resolve_breakpoints: []const []const u8 = &.{}; + + pub fn hasResolveBreakpoint(str: []const u8) bool { + for (resolve_breakpoints) |bp| { + if (strings.contains(str, bp)) { + return true; + } + } + return false; + } +} else @compileError("Do not access this namespace in a release build"); + const LoaderMatcher = strings.ExactSizeMatcher(4); const ColonListType = @import("./cli/colon_list_type.zig").ColonListType; pub const LoaderColonList = ColonListType(Api.Loader, Arguments.loader_resolver); @@ -102,6 +115,7 @@ pub const BunxCommand = @import("./cli/bunx_command.zig").BunxCommand; pub const ExecCommand = @import("./cli/exec_command.zig").ExecCommand; pub const PatchCommand = @import("./cli/patch_command.zig").PatchCommand; pub const PatchCommitCommand = @import("./cli/patch_commit_command.zig").PatchCommitCommand; +pub const OutdatedCommand = @import("./cli/outdated_command.zig").OutdatedCommand; pub const Arguments = struct { pub fn loader_resolver(in: string) !Api.Loader { @@ -146,7 +160,7 @@ pub const Arguments = struct { pub const ParamType = clap.Param(clap.Help); - const base_params_ = [_]ParamType{ + const base_params_ = (if (Environment.isDebug) debug_params else [_]ParamType{}) ++ [_]ParamType{ clap.parseParam("--env-file ... Load environment variables from the specified file(s)") catch unreachable, clap.parseParam("--cwd Absolute path to resolve files & entry points from. This just changes the process' cwd.") catch unreachable, clap.parseParam("-c, --config ? Specify path to Bun config file. Default $cwd/bunfig.toml") catch unreachable, @@ -157,6 +171,10 @@ pub const Arguments = struct { clap.parseParam("--verbose-error-trace") catch unreachable, } else [_]ParamType{}; + const debug_params = [_]ParamType{ + clap.parseParam("--breakpoint-resolve ... DEBUG MODE: breakpoint when resolving something that includes this string") catch unreachable, + }; + const transpiler_params_ = [_]ParamType{ clap.parseParam("--main-fields ... Main fields to lookup in package.json. Defaults to --target dependent") catch unreachable, clap.parseParam("--extension-order ... Defaults to: .tsx,.ts,.jsx,.js,.json ") catch unreachable, @@ -168,6 +186,7 @@ pub const Arguments = struct { clap.parseParam("--jsx-fragment Changes the function called when compiling JSX fragments") catch unreachable, clap.parseParam("--jsx-import-source Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: \"react\"") catch unreachable, clap.parseParam("--jsx-runtime \"automatic\" (default) or \"classic\"") catch unreachable, + clap.parseParam("--ignore-dce-annotations Ignore tree-shaking annotations such as @__PURE__") catch unreachable, }; const runtime_params_ = [_]ParamType{ clap.parseParam("--watch Automatically restart the process on file change") catch unreachable, @@ -189,6 +208,8 @@ pub const Arguments = struct { clap.parseParam("-p, --port Set the default port for Bun.serve") catch unreachable, clap.parseParam("-u, --origin ") catch unreachable, clap.parseParam("--conditions ... Pass custom conditions to resolve") catch unreachable, + clap.parseParam("--fetch-preconnect ... Preconnect to a URL while code is loading") catch unreachable, + clap.parseParam("--max-http-header-size Set the maximum size of HTTP headers in bytes. Default is 16KiB") catch unreachable, }; const auto_or_run_params = [_]ParamType{ @@ -220,22 +241,25 @@ pub const Arguments = struct { clap.parseParam("--no-clear-screen Disable clearing the terminal screen on reload when --watch is enabled") catch unreachable, clap.parseParam("--target The intended execution environment for the bundle. \"browser\", \"bun\" or \"node\"") catch unreachable, clap.parseParam("--outdir Default to \"dist\" if multiple files") catch unreachable, - clap.parseParam("--outfile Write to a file") catch unreachable, - clap.parseParam("--sourcemap ? Build with sourcemaps - 'inline', 'external', or 'none'") catch unreachable, + clap.parseParam("--outfile Write to a file") catch unreachable, + clap.parseParam("--sourcemap ? Build with sourcemaps - 'linked', 'inline', 'external', or 'none'") catch unreachable, clap.parseParam("--format Specifies the module format to build to. Only \"esm\" is supported.") catch unreachable, clap.parseParam("--root Root directory used for multiple entry points") catch unreachable, clap.parseParam("--splitting Enable code splitting") catch unreachable, clap.parseParam("--public-path A prefix to be appended to any import paths in bundled code") catch unreachable, clap.parseParam("-e, --external ... Exclude module from transpilation (can use * wildcards). ex: -e react") catch unreachable, + clap.parseParam("--packages Add dependencies to bundle or keep them external. \"external\", \"bundle\" is supported. Defaults to \"bundle\".") catch unreachable, clap.parseParam("--entry-naming Customize entry point filenames. Defaults to \"[dir]/[name].[ext]\"") catch unreachable, clap.parseParam("--chunk-naming Customize chunk filenames. Defaults to \"[name]-[hash].[ext]\"") catch unreachable, clap.parseParam("--asset-naming Customize asset filenames. Defaults to \"[name]-[hash].[ext]\"") catch unreachable, + clap.parseParam("--react-fast-refresh Enable React Fast Refresh transform (does not emit hot-module code, use this for testing)") catch unreachable, clap.parseParam("--server-components Enable React Server Components (experimental)") catch unreachable, clap.parseParam("--no-bundle Transpile file only, do not bundle") catch unreachable, + clap.parseParam("--emit-dce-annotations Re-emit DCE annotations in bundles. Enabled by default unless --minify-whitespace is passed.") catch unreachable, clap.parseParam("--minify Enable all minification flags") catch unreachable, clap.parseParam("--minify-syntax Minify syntax and inline data") catch unreachable, clap.parseParam("--minify-whitespace Minify whitespace") catch unreachable, - clap.parseParam("--minify-identifiers Minify identifiers") catch unreachable, + clap.parseParam("--minify-identifiers Minify identifiers") catch unreachable, clap.parseParam("--dump-environment-variables") catch unreachable, clap.parseParam("--conditions ... Pass custom conditions to resolve") catch unreachable, }; @@ -244,11 +268,13 @@ pub const Arguments = struct { // TODO: update test completions const test_only_params = [_]ParamType{ clap.parseParam("--timeout Set the per-test timeout in milliseconds, default is 5000.") catch unreachable, - clap.parseParam("--update-snapshots Update snapshot files") catch unreachable, + clap.parseParam("-u, --update-snapshots Update snapshot files") catch unreachable, clap.parseParam("--rerun-each Re-run each test file times, helps catch certain bugs") catch unreachable, clap.parseParam("--only Only run tests that are marked with \"test.only()\"") catch unreachable, clap.parseParam("--todo Include tests that are marked with \"test.todo()\"") catch unreachable, clap.parseParam("--coverage Generate a coverage profile") catch unreachable, + clap.parseParam("--coverage-reporter ... Report coverage in 'text' and/or 'lcov'. Defaults to 'text'.") catch unreachable, + clap.parseParam("--coverage-dir Directory for coverage files. Defaults to 'coverage'.") catch unreachable, clap.parseParam("--bail ? Exit the test suite after failures. If you do not specify a number, it defaults to 1.") catch unreachable, clap.parseParam("-t, --test-name-pattern Run only tests with a name that matches the given regex.") catch unreachable, }; @@ -277,8 +303,8 @@ pub const Arguments = struct { Global.exit(1); }; - js_ast.Stmt.Data.Store.create(allocator); - js_ast.Expr.Data.Store.create(allocator); + js_ast.Stmt.Data.Store.create(); + js_ast.Expr.Data.Store.create(); defer { js_ast.Stmt.Data.Store.reset(); js_ast.Expr.Data.Store.reset(); @@ -441,6 +467,24 @@ pub const Arguments = struct { ctx.test_options.coverage.enabled = args.flag("--coverage"); } + if (args.options("--coverage-reporter").len > 0) { + ctx.test_options.coverage.reporters = .{ .text = false, .lcov = false }; + for (args.options("--coverage-reporter")) |reporter| { + if (bun.strings.eqlComptime(reporter, "text")) { + ctx.test_options.coverage.reporters.text = true; + } else if (bun.strings.eqlComptime(reporter, "lcov")) { + ctx.test_options.coverage.reporters.lcov = true; + } else { + Output.prettyErrorln("error: --coverage-reporter received invalid reporter: \"{s}\"", .{reporter}); + Global.exit(1); + } + } + } + + if (args.option("--coverage-dir")) |dir| { + ctx.test_options.coverage.reports_directory = dir; + } + if (args.option("--bail")) |bail| { if (bail.len > 0) { ctx.test_options.bail = std.fmt.parseInt(u32, bail, 10) catch |e| { @@ -524,7 +568,7 @@ pub const Arguments = struct { ctx.passthrough = args.remaining(); - if (cmd == .AutoCommand or cmd == .RunCommand or cmd == .BuildCommand) { + if (cmd == .AutoCommand or cmd == .RunCommand or cmd == .BuildCommand or cmd == .TestCommand) { if (args.options("--conditions").len > 0) { opts.conditions = args.options("--conditions"); } @@ -563,13 +607,31 @@ pub const Arguments = struct { ctx.runtime_options.eval.eval_and_print = true; } else { opts.port = std.fmt.parseInt(u16, port_str, 10) catch { - Output.errGeneric("Invalid value for --port: \"{s}\". Must be a number\n", .{port_str}); + Output.errFmt( + bun.fmt.outOfRange(port_str, .{ + .field_name = "--port", + .min = 0, + .max = std.math.maxInt(u16), + }), + ); Output.note("To evaluate TypeScript here, use 'bun --print'", .{}); Global.exit(1); }; } } + if (args.option("--max-http-header-size")) |size_str| { + const size = std.fmt.parseInt(usize, size_str, 10) catch { + Output.errGeneric("Invalid value for --max-http-header-size: \"{s}\". Must be a positive integer\n", .{size_str}); + Global.exit(1); + }; + if (size == 0) { + bun.http.max_http_header_size = 1024 * 1024 * 1024; + } else { + bun.http.max_http_header_size = size; + } + } + ctx.debug.offline_mode_setting = if (args.flag("--prefer-offline")) Bunfig.OfflineMode.offline else if (args.flag("--prefer-latest")) @@ -611,6 +673,8 @@ pub const Arguments = struct { } ctx.runtime_options.if_present = args.flag("--if-present"); ctx.runtime_options.smol = args.flag("--smol"); + ctx.runtime_options.preconnect = args.options("--fetch-preconnect"); + if (args.option("--inspect")) |inspect_flag| { ctx.runtime_options.debugger = if (inspect_flag.len == 0) Command.Debugger{ .enable = .{} } @@ -656,6 +720,8 @@ pub const Arguments = struct { const output_dir: ?string = null; const output_file: ?string = null; + ctx.bundler_options.ignore_dce_annotations = args.flag("--ignore-dce-annotations"); + if (cmd == .BuildCommand) { ctx.bundler_options.transform_only = args.flag("--no-bundle"); @@ -668,14 +734,28 @@ pub const Arguments = struct { ctx.bundler_options.minify_whitespace = minify_flag or args.flag("--minify-whitespace"); ctx.bundler_options.minify_identifiers = minify_flag or args.flag("--minify-identifiers"); + ctx.bundler_options.emit_dce_annotations = args.flag("--emit-dce-annotations") or + !ctx.bundler_options.minify_whitespace; + if (args.options("--external").len > 0) { - var externals = try allocator.alloc([]u8, args.options("--external").len); + var externals = try allocator.alloc([]const u8, args.options("--external").len); for (args.options("--external"), 0..) |external, i| { - externals[i] = @constCast(external); + externals[i] = external; } opts.external = externals; } + if (args.option("--packages")) |packages| { + if (strings.eqlComptime(packages, "bundle")) { + opts.packages = .bundle; + } else if (strings.eqlComptime(packages, "external")) { + opts.packages = .external; + } else { + Output.prettyErrorln("error: Invalid packages setting: \"{s}\"", .{packages}); + Global.crash(); + } + } + const TargetMatcher = strings.ExactSizeMatcher(8); if (args.option("--target")) |_target| brk: { if (comptime cmd == .BuildCommand) { @@ -715,6 +795,7 @@ pub const Arguments = struct { if (args.flag("--compile")) { ctx.bundler_options.compile = true; + ctx.bundler_options.inline_entrypoint_import_meta_main = true; } if (args.option("--outdir")) |outdir| { @@ -735,16 +816,27 @@ pub const Arguments = struct { if (args.option("--format")) |format_str| { const format = options.Format.fromString(format_str) orelse { - Output.prettyErrorln("error: Invalid format - must be esm, cjs, or iife", .{}); + Output.errGeneric("Invalid format - must be esm, cjs, or iife", .{}); Global.crash(); }; + switch (format) { - .esm => {}, - else => { - Output.prettyErrorln("error: Formats besides 'esm' are not implemented", .{}); - Global.crash(); + .internal_kit_dev => { + bun.Output.warn("--format={s} is for debugging only, and may experience breaking changes at any moment", .{format_str}); + bun.Output.flush(); + }, + .cjs => { + // Make this a soft error in debug to allow experimenting with these flags. + const function = if (Environment.isDebug) Output.debugWarn else Output.errGeneric; + function("Format '{s}' are not implemented", .{@tagName(format)}); + if (!Environment.isDebug) { + Global.crash(); + } }, + else => {}, } + + ctx.bundler_options.output_format = format; } if (args.flag("--splitting")) { @@ -763,10 +855,12 @@ pub const Arguments = struct { ctx.bundler_options.asset_naming = try strings.concat(allocator, &.{ "./", bun.strings.removeLeadingDotSlash(asset_naming) }); } - if (comptime FeatureFlags.react_server_components) { - if (args.flag("--server-components")) { - ctx.bundler_options.react_server_components = true; - } + if (args.flag("--server-components")) { + ctx.bundler_options.react_server_components = true; + } + + if (args.flag("--react-fast-refresh")) { + ctx.bundler_options.react_fast_refresh = true; } if (args.option("--sourcemap")) |setting| { @@ -788,6 +882,13 @@ pub const Arguments = struct { Output.prettyErrorln("error: Invalid sourcemap setting: \"{s}\"", .{setting}); Global.crash(); } + + // when using --compile, only `external` works, as we do not + // look at the source map comment. so after we validate the + // user's choice was in the list, we secretly override it + if (ctx.bundler_options.compile) { + opts.source_map = .external; + } } } @@ -927,6 +1028,10 @@ pub const Arguments = struct { } } + if (Environment.isDebug) { + debug_flags.resolve_breakpoints = args.options("--breakpoint-resolve"); + } + return opts; } }; @@ -1011,9 +1116,11 @@ pub const HelpCommand = struct { \\ add {s:<16} Add a dependency to package.json (bun a) \\ remove {s:<16} Remove a dependency from package.json (bun rm) \\ update {s:<16} Update outdated dependencies + \\ outdated Display latest versions of outdated dependencies + \\ pack Archive the current workspace package \\ link [\] Register or link a local npm package \\ unlink Unregister a local npm package - \\ patch \ Prepare a package for patching + \\ patch \ Prepare a package for patching \\ pm \ Additional package management utilities \\ \\ build ./a.ts ./b.jsx Bundle TypeScript & JavaScript into a single file @@ -1175,16 +1282,11 @@ pub const Command = struct { script: []const u8 = "", eval_and_print: bool = false, } = .{}, + preconnect: []const []const u8 = &[_][]const u8{}, }; var global_cli_ctx: Context = undefined; - - var context_data: ContextData = ContextData{ - .args = std.mem.zeroes(Api.TransformOptions), - .log = undefined, - .start_time = 0, - .allocator = undefined, - }; + var context_data: ContextData = undefined; pub const init = ContextData.create; @@ -1219,19 +1321,27 @@ pub const Command = struct { chunk_naming: []const u8 = "./[name]-[hash].[ext]", asset_naming: []const u8 = "./[name]-[hash].[ext]", react_server_components: bool = false, + react_fast_refresh: bool = false, code_splitting: bool = false, transform_only: bool = false, + inline_entrypoint_import_meta_main: bool = false, minify_syntax: bool = false, minify_whitespace: bool = false, minify_identifiers: bool = false, + ignore_dce_annotations: bool = false, + emit_dce_annotations: bool = true, + output_format: options.Format = .esm, }; pub fn create(allocator: std.mem.Allocator, log: *logger.Log, comptime command: Command.Tag) anyerror!Context { Cli.cmd = command; + context_data = .{ + .args = std.mem.zeroes(Api.TransformOptions), + .log = log, + .start_time = start_time, + .allocator = allocator, + }; global_cli_ctx = &context_data; - global_cli_ctx.log = log; - global_cli_ctx.start_time = start_time; - global_cli_ctx.allocator = allocator; if (comptime Command.Tag.uses_global_options.get(command)) { global_cli_ctx.args = try Arguments.parse(allocator, global_cli_ctx, command); @@ -1366,6 +1476,8 @@ pub const Command = struct { RootCommandMatcher.case("exec") => .ExecCommand, + RootCommandMatcher.case("outdated") => .OutdatedCommand, + // These are reserved for future use by Bun, so that someone // doing `bun deploy` to run a script doesn't accidentally break // when we add our actual command @@ -1380,7 +1492,6 @@ pub const Command = struct { RootCommandMatcher.case("whoami") => .ReservedCommand, RootCommandMatcher.case("publish") => .ReservedCommand, RootCommandMatcher.case("prune") => .ReservedCommand, - RootCommandMatcher.case("outdated") => .ReservedCommand, RootCommandMatcher.case("list") => .ReservedCommand, RootCommandMatcher.case("why") => .ReservedCommand, @@ -1502,6 +1613,13 @@ pub const Command = struct { try PatchCommitCommand.exec(ctx); return; }, + .OutdatedCommand => { + if (comptime bun.fast_debug_build_mode and bun.fast_debug_build_cmd != .OutdatedCommand) unreachable; + const ctx = try Command.init(allocator, log, .OutdatedCommand); + + try OutdatedCommand.exec(ctx); + return; + }, .BunxCommand => { if (comptime bun.fast_debug_build_mode and bun.fast_debug_build_cmd != .BunxCommand) unreachable; const ctx = try Command.init(allocator, log, .BunxCommand); @@ -2069,6 +2187,7 @@ pub const Command = struct { ExecCommand, PatchCommand, PatchCommitCommand, + OutdatedCommand, /// Used by crash reports. /// @@ -2100,6 +2219,7 @@ pub const Command = struct { .ExecCommand => 'e', .PatchCommand => 'x', .PatchCommitCommand => 'z', + .OutdatedCommand => 'o', }; } @@ -2317,12 +2437,17 @@ pub const Command = struct { \\Note: If executing this from a shell, make sure to escape the string! \\ \\Examples: - \\ bunx exec "echo hi" - \\ bunx exec "echo \"hey friends\"!" + \\ bun exec "echo hi" + \\ bun exec "echo \"hey friends\"!" \\ , .{}); Output.flush(); }, + .OutdatedCommand => { + Install.PackageManager.CommandLineArguments.printHelp(switch (cmd) { + .OutdatedCommand => .outdated, + }); + }, else => { HelpCommand.printWithReason(.explicit); }, @@ -2331,7 +2456,16 @@ pub const Command = struct { pub fn readGlobalConfig(this: Tag) bool { return switch (this) { - .BunxCommand, .PackageManagerCommand, .InstallCommand, .AddCommand, .RemoveCommand, .UpdateCommand, .PatchCommand, .PatchCommitCommand => true, + .BunxCommand, + .PackageManagerCommand, + .InstallCommand, + .AddCommand, + .RemoveCommand, + .UpdateCommand, + .PatchCommand, + .PatchCommitCommand, + .OutdatedCommand, + => true, else => false, }; } @@ -2348,6 +2482,7 @@ pub const Command = struct { .UpdateCommand, .PatchCommand, .PatchCommitCommand, + .OutdatedCommand, => true, else => false, }; @@ -2367,6 +2502,7 @@ pub const Command = struct { .AutoCommand = true, .RunCommand = true, .RunAsNodeCommand = true, + .OutdatedCommand = true, }); pub const always_loads_config: std.EnumArray(Tag, bool) = std.EnumArray(Tag, bool).initDefault(false, .{ @@ -2380,6 +2516,7 @@ pub const Command = struct { .PatchCommitCommand = true, .PackageManagerCommand = true, .BunxCommand = true, + .OutdatedCommand = true, }); pub const uses_global_options: std.EnumArray(Tag, bool) = std.EnumArray(Tag, bool).initDefault(true, .{ @@ -2394,6 +2531,7 @@ pub const Command = struct { .LinkCommand = false, .UnlinkCommand = false, .BunxCommand = false, + .OutdatedCommand = false, }); }; }; diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 631278298854f..f5bb2afd6c141 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -77,10 +77,8 @@ pub const BuildCommand = struct { var this_bundler = try bundler.Bundler.init(allocator, log, ctx.args, null); this_bundler.options.source_map = options.SourceMapOption.fromApi(ctx.args.source_map); - this_bundler.resolver.opts.source_map = options.SourceMapOption.fromApi(ctx.args.source_map); this_bundler.options.compile = ctx.bundler_options.compile; - this_bundler.resolver.opts.compile = ctx.bundler_options.compile; if (this_bundler.options.source_map == .external and ctx.bundler_options.outdir.len == 0 and !ctx.bundler_options.compile) { Output.prettyErrorln("error: cannot use an external source map without --outdir", .{}); @@ -90,29 +88,18 @@ pub const BuildCommand = struct { var outfile = ctx.bundler_options.outfile; this_bundler.options.public_path = ctx.bundler_options.public_path; - this_bundler.resolver.opts.public_path = ctx.bundler_options.public_path; - this_bundler.options.entry_naming = ctx.bundler_options.entry_naming; this_bundler.options.chunk_naming = ctx.bundler_options.chunk_naming; this_bundler.options.asset_naming = ctx.bundler_options.asset_naming; - this_bundler.resolver.opts.entry_naming = ctx.bundler_options.entry_naming; - this_bundler.resolver.opts.chunk_naming = ctx.bundler_options.chunk_naming; - this_bundler.resolver.opts.asset_naming = ctx.bundler_options.asset_naming; - this_bundler.options.react_server_components = ctx.bundler_options.react_server_components; - this_bundler.resolver.opts.react_server_components = ctx.bundler_options.react_server_components; - + this_bundler.options.react_fast_refresh = ctx.bundler_options.react_fast_refresh; + this_bundler.options.inline_entrypoint_import_meta_main = ctx.bundler_options.inline_entrypoint_import_meta_main; this_bundler.options.code_splitting = ctx.bundler_options.code_splitting; - this_bundler.resolver.opts.code_splitting = ctx.bundler_options.code_splitting; - this_bundler.options.minify_syntax = ctx.bundler_options.minify_syntax; - this_bundler.resolver.opts.minify_syntax = ctx.bundler_options.minify_syntax; - this_bundler.options.minify_whitespace = ctx.bundler_options.minify_whitespace; - this_bundler.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace; - this_bundler.options.minify_identifiers = ctx.bundler_options.minify_identifiers; - this_bundler.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers; + this_bundler.options.emit_dce_annotations = ctx.bundler_options.emit_dce_annotations; + this_bundler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations; if (ctx.bundler_options.compile) { if (ctx.bundler_options.code_splitting) { @@ -121,12 +108,6 @@ pub const BuildCommand = struct { return; } - if (this_bundler.options.entry_points.len > 1) { - Output.prettyErrorln("error: multiple entry points are not supported with --compile", .{}); - Global.exit(1); - return; - } - if (ctx.bundler_options.outdir.len > 0) { Output.prettyErrorln("error: cannot use --compile with --outdir", .{}); Global.exit(1); @@ -136,7 +117,6 @@ pub const BuildCommand = struct { const base_public_path = bun.StandaloneModuleGraph.targetBasePublicPath(compile_target.os, "root/"); this_bundler.options.public_path = base_public_path; - this_bundler.resolver.opts.public_path = base_public_path; if (outfile.len == 0) { outfile = std.fs.path.basename(this_bundler.options.entry_points[0]); @@ -168,7 +148,7 @@ pub const BuildCommand = struct { } } - if (ctx.bundler_options.outdir.len == 0) { + if (ctx.bundler_options.outdir.len == 0 and !ctx.bundler_options.compile) { if (this_bundler.options.entry_points.len > 1) { Output.prettyErrorln("error: Must use --outdir when specifying more than one entry point.", .{}); Global.exit(1); @@ -182,7 +162,7 @@ pub const BuildCommand = struct { } this_bundler.options.output_dir = ctx.bundler_options.outdir; - this_bundler.resolver.opts.output_dir = ctx.bundler_options.outdir; + this_bundler.options.output_format = ctx.bundler_options.output_format; var src_root_dir_buf: bun.PathBuffer = undefined; const src_root_dir: string = brk1: { @@ -211,17 +191,15 @@ pub const BuildCommand = struct { }; this_bundler.options.root_dir = src_root_dir; - this_bundler.resolver.opts.root_dir = src_root_dir; - - this_bundler.options.react_server_components = ctx.bundler_options.react_server_components; - this_bundler.resolver.opts.react_server_components = ctx.bundler_options.react_server_components; this_bundler.options.code_splitting = ctx.bundler_options.code_splitting; - this_bundler.resolver.opts.code_splitting = ctx.bundler_options.code_splitting; this_bundler.options.transform_only = ctx.bundler_options.transform_only; + if (this_bundler.options.transform_only) { this_bundler.options.resolve_mode = .disable; } + this_bundler.resolver.opts = this_bundler.options; + this_bundler.configureLinker(); // This step is optional @@ -349,7 +327,7 @@ pub const BuildCommand = struct { std.fs.cwd() else std.fs.cwd().makeOpenPath(root_path, .{}) catch |err| { - Output.prettyErrorln("{s} while attemping to open output directory {}", .{ @errorName(err), bun.fmt.quote(root_path) }); + Output.prettyErrorln("{s} while attempting to open output directory {}", .{ @errorName(err), bun.fmt.quote(root_path) }); exitOrWatch(1, ctx.debug.hot_reload == .watch); unreachable; }; @@ -586,14 +564,14 @@ fn printSummary(bundled_end: i128, minify_duration: u64, minified: bool, input_c Output.prettyln( " minify -{} (estimate)", .{ - bun.fmt.size(@as(usize, @intCast(delta))), + bun.fmt.size(@as(usize, @intCast(delta)), .{}), }, ); } else if (-delta > 1024) { Output.prettyln( " minify +{} (estimate)", .{ - bun.fmt.size(@as(usize, @intCast(-delta))), + bun.fmt.size(@as(usize, @intCast(-delta)), .{}), }, ); } else { diff --git a/src/cli/bunx_command.zig b/src/cli/bunx_command.zig index bb0fc13a0f490..c0bb2fa7f77be 100644 --- a/src/cli/bunx_command.zig +++ b/src/cli/bunx_command.zig @@ -79,8 +79,8 @@ pub const BunxCommand = struct { const package_json_contents = package_json_read.bytes.items; const source = bun.logger.Source.initPathString(bun.span(subpath_z), package_json_contents); - bun.JSAst.Expr.Data.Store.create(default_allocator); - bun.JSAst.Stmt.Data.Store.create(default_allocator); + bun.JSAst.Expr.Data.Store.create(); + bun.JSAst.Stmt.Data.Store.create(); const expr = try bun.JSON.ParsePackageJSONUTF8(&source, bundler.log, bundler.allocator); diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 92f6d823aa601..7b7bf50c7c66b 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -50,8 +50,8 @@ pub var initialized_store = false; pub fn initializeStore() void { if (initialized_store) return; initialized_store = true; - js_ast.Expr.Data.Store.create(default_allocator); - js_ast.Stmt.Data.Store.create(default_allocator); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); } const skip_dirs = &[_]bun.OSPathSlice{ @@ -241,7 +241,7 @@ pub const CreateCommand = struct { @setCold(true); Global.configureAllocator(.{ .long_running = false }); - try HTTP.HTTPThread.init(); + HTTP.HTTPThread.init(); var create_options = try CreateOptions.parse(ctx); const positionals = create_options.positionals; @@ -583,7 +583,7 @@ pub const CreateCommand = struct { }, } - CopyFile.copyFile(infile.handle, outfile.handle) catch |err| { + CopyFile.copyFile(infile.handle, outfile.handle).unwrap() catch |err| { node_.end(); progress_.refresh(); Output.err(err, "failed to copy file {}", .{bun.fmt.fmtOSPath(entry.path, .{})}); @@ -1436,7 +1436,7 @@ pub const CreateCommand = struct { const package_json_writer = JSPrinter.NewFileWriter(package_json_file.?); - const written = JSPrinter.printJSON(@TypeOf(package_json_writer), package_json_writer, package_json_expr, &source) catch |err| { + const written = JSPrinter.printJSON(@TypeOf(package_json_writer), package_json_writer, package_json_expr, &source, .{}) catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); package_json_file = null; break :process_package_json; @@ -1811,7 +1811,6 @@ pub const Example = struct { const examples_url: string = "https://registry.npmjs.org/bun-examples-all/latest"; var url: URL = undefined; - pub const timeout: u32 = 6000; var app_name_buf: [512]u8 = undefined; pub fn print(examples: []const Example, default_app_name: ?string) void { @@ -1977,13 +1976,12 @@ pub const Example = struct { headers_buf, mutable, "", - 60 * std.time.ns_per_min, http_proxy, null, HTTP.FetchRedirect.follow, ); async_http.client.progress_node = progress; - async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); const response = try async_http.sendSync(true); @@ -1999,7 +1997,7 @@ pub const Example = struct { var is_expected_content_type = false; var content_type: string = ""; for (response.headers) |header| { - if (strings.eqlInsensitive(header.name, "content-type")) { + if (strings.eqlCaseInsensitiveASCII(header.name, "content-type", true)) { content_type = header.value; if (strings.eqlComptime(header.value, "application/x-gzip")) { @@ -2055,13 +2053,12 @@ pub const Example = struct { "", mutable, "", - 60 * std.time.ns_per_min, http_proxy, null, HTTP.FetchRedirect.follow, ); async_http.client.progress_node = progress; - async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); var response = try async_http.sendSync(true); @@ -2145,13 +2142,12 @@ pub const Example = struct { "", mutable, "", - 60 * std.time.ns_per_min, http_proxy, null, HTTP.FetchRedirect.follow, ); async_http.client.progress_node = progress; - async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); refresher.maybeRefresh(); @@ -2188,12 +2184,11 @@ pub const Example = struct { "", mutable, "", - 60 * std.time.ns_per_min, http_proxy, null, HTTP.FetchRedirect.follow, ); - async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); if (Output.enable_ansi_colors) { async_http.client.progress_node = progress_node; diff --git a/src/cli/exec_command.zig b/src/cli/exec_command.zig index de51c9c20de3f..5ea2fd36cc5bf 100644 --- a/src/cli/exec_command.zig +++ b/src/cli/exec_command.zig @@ -50,7 +50,7 @@ pub const ExecCommand = struct { // Output.flush(); // } - Global.exitWide(code); + Global.exit(code); // } } }; diff --git a/src/cli/filter_arg.zig b/src/cli/filter_arg.zig index 4ac81ae07678e..297233df3afee 100644 --- a/src/cli/filter_arg.zig +++ b/src/cli/filter_arg.zig @@ -37,9 +37,8 @@ fn globIgnoreFn(val: []const u8) bool { const GlobWalker = Glob.GlobWalker_(globIgnoreFn, Glob.DirEntryAccessor, false); pub fn getCandidatePackagePatterns(allocator: std.mem.Allocator, log: *bun.logger.Log, out_patterns: *std.ArrayList([]u8), workdir_: []const u8, root_buf: *bun.PathBuffer) ![]const u8 { - bun.JSAst.Expr.Data.Store.create(bun.default_allocator); - bun.JSAst.Stmt.Data.Store.create(bun.default_allocator); - + bun.JSAst.Expr.Data.Store.create(); + bun.JSAst.Stmt.Data.Store.create(); defer { bun.JSAst.Expr.Data.Store.reset(); bun.JSAst.Stmt.Data.Store.reset(); @@ -188,7 +187,7 @@ pub const FilterSet = struct { pub fn matchesPath(self: *const FilterSet, path: []const u8) bool { for (self.filters) |filter| { - if (Glob.matchImpl(filter.codepoints, path)) { + if (Glob.matchImpl(filter.codepoints, path).matches()) { return true; } } @@ -201,7 +200,7 @@ pub const FilterSet = struct { .name => name, .path => path, }; - if (Glob.matchImpl(filter.codepoints, target)) { + if (Glob.matchImpl(filter.codepoints, target).matches()) { return true; } } diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig index 5683b83d7d10d..35d22af2fe2ae 100644 --- a/src/cli/init_command.zig +++ b/src/cli/init_command.zig @@ -362,6 +362,7 @@ pub const InitCommand = struct { package_json_writer, js_ast.Expr{ .data = .{ .e_object = fields.object }, .loc = logger.Loc.Empty }, &logger.Source.initEmptyFile("package.json"), + .{}, ) catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); package_json_file = null; diff --git a/src/cli/install.ps1 b/src/cli/install.ps1 index 44a06dcd9ebde..d541cd1103278 100644 --- a/src/cli/install.ps1 +++ b/src/cli/install.ps1 @@ -216,8 +216,12 @@ function Install-Bun { # http://community.sqlbackupandftp.com/t/error-1073741515-solved/1305 if (($LASTEXITCODE -eq 3221225781) -or ($LASTEXITCODE -eq -1073741515)) # STATUS_DLL_NOT_FOUND { + # TODO: as of July 2024, Bun has no external dependencies. + # I want to keep this error message in for a few months to ensure that + # if someone somehow runs into this, it can be reported. Write-Output "Install Failed - You are missing a DLL required to run bun.exe" Write-Output "This can be solved by installing the Visual C++ Redistributable from Microsoft:`nSee https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist`nDirect Download -> https://aka.ms/vs/17/release/vc_redist.x64.exe`n`n" + Write-Output "The error above should be unreachable as Bun does not depend on this library. Please comment in https://github.com/oven-sh/bun/issues/8598 or open a new issue.`n`n" Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n" return 1 } diff --git a/src/cli/install.sh b/src/cli/install.sh index a2530c324ba8f..08a0817f6d606 100644 --- a/src/cli/install.sh +++ b/src/cli/install.sh @@ -247,7 +247,7 @@ bash) commands=( "export $install_env=$quoted_install_dir" - "export PATH=$bin_env:\$PATH" + "export PATH=\"$bin_env:\$PATH\"" ) bash_configs=( diff --git a/src/cli/outdated_command.zig b/src/cli/outdated_command.zig new file mode 100644 index 0000000000000..a786e3afbb6de --- /dev/null +++ b/src/cli/outdated_command.zig @@ -0,0 +1,679 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Global = bun.Global; +const Output = bun.Output; +const Command = bun.CLI.Command; +const Install = bun.install; +const PackageManager = Install.PackageManager; +const Lockfile = Install.Lockfile; +const PackageID = Install.PackageID; +const DependencyID = Install.DependencyID; +const Behavior = Install.Dependency.Behavior; +const invalid_package_id = Install.invalid_package_id; +const Resolution = Install.Resolution; +const string = bun.string; +const strings = bun.strings; +const PathBuffer = bun.PathBuffer; +const FileSystem = bun.fs.FileSystem; +const path = bun.path; +const glob = bun.glob; +const Table = bun.fmt.Table; + +pub const OutdatedCommand = struct { + pub fn exec(ctx: Command.Context) !void { + Output.prettyErrorln("bun outdated v" ++ Global.package_json_version_with_sha ++ "", .{}); + Output.flush(); + + const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .outdated); + + const manager, const original_cwd = PackageManager.init(ctx, cli, .outdated) catch |err| { + if (!cli.silent) { + if (err == error.MissingPackageJSON) { + Output.errGeneric("missing package.json, nothing outdated", .{}); + } + Output.errGeneric("failed to initialize bun install: {s}", .{@errorName(err)}); + } + + Global.crash(); + }; + defer ctx.allocator.free(original_cwd); + + return switch (manager.options.log_level) { + inline else => |log_level| outdated(ctx, original_cwd, manager, log_level), + }; + } + + fn outdated(ctx: Command.Context, original_cwd: string, manager: *PackageManager, comptime log_level: PackageManager.Options.LogLevel) !void { + const load_lockfile_result = manager.lockfile.loadFromDisk( + manager, + manager.allocator, + manager.log, + manager.options.lockfile_path, + true, + ); + + manager.lockfile = switch (load_lockfile_result) { + .not_found => { + if (log_level != .silent) { + Output.errGeneric("missing lockfile, nothing outdated", .{}); + } + Global.crash(); + }, + .err => |cause| { + if (log_level != .silent) { + switch (cause.step) { + .open_file => Output.errGeneric("failed to open lockfile: {s}", .{ + @errorName(cause.value), + }), + .parse_file => Output.errGeneric("failed to parse lockfile: {s}", .{ + @errorName(cause.value), + }), + .read_file => Output.errGeneric("failed to read lockfile: {s}", .{ + @errorName(cause.value), + }), + .migrating => Output.errGeneric("failed to migrate lockfile: {s}", .{ + @errorName(cause.value), + }), + } + + if (ctx.log.hasErrors()) { + switch (Output.enable_ansi_colors) { + inline else => |enable_ansi_colors| try manager.log.printForLogLevelWithEnableAnsiColors( + Output.errorWriter(), + enable_ansi_colors, + ), + } + } + } + + Global.crash(); + }, + .ok => |ok| ok.lockfile, + }; + + switch (Output.enable_ansi_colors) { + inline else => |enable_ansi_colors| { + if (manager.options.filter_patterns.len > 0) { + const filters = manager.options.filter_patterns; + const workspace_pkg_ids = findMatchingWorkspaces( + bun.default_allocator, + original_cwd, + manager, + filters, + ) catch bun.outOfMemory(); + defer bun.default_allocator.free(workspace_pkg_ids); + + try updateManifestsIfNecessary(manager, log_level, workspace_pkg_ids); + try printOutdatedInfoTable(manager, workspace_pkg_ids, true, enable_ansi_colors); + } else { + // just the current workspace + const root_pkg_id = manager.root_package_id.get(manager.lockfile, manager.workspace_name_hash); + if (root_pkg_id == invalid_package_id) return; + + try updateManifestsIfNecessary(manager, log_level, &.{root_pkg_id}); + try printOutdatedInfoTable(manager, &.{root_pkg_id}, false, enable_ansi_colors); + } + }, + } + } + + // TODO: use in `bun pack, publish, run, ...` + const FilterType = union(enum) { + all, + name: []const u32, + path: []const u32, + + pub fn init(pattern: []const u32, is_path: bool) @This() { + return if (is_path) .{ + .path = pattern, + } else .{ + .name = pattern, + }; + } + + pub fn deinit(this: @This(), allocator: std.mem.Allocator) void { + switch (this) { + .path, .name => |pattern| allocator.free(pattern), + else => {}, + } + } + }; + + fn findMatchingWorkspaces( + allocator: std.mem.Allocator, + original_cwd: string, + manager: *PackageManager, + filters: []const string, + ) error{OutOfMemory}![]const PackageID { + const lockfile = manager.lockfile; + const packages = lockfile.packages.slice(); + const pkg_names = packages.items(.name); + const pkg_resolutions = packages.items(.resolution); + const string_buf = lockfile.buffers.string_bytes.items; + + var workspace_pkg_ids: std.ArrayListUnmanaged(PackageID) = .{}; + for (pkg_resolutions, 0..) |resolution, pkg_id| { + if (resolution.tag != .workspace and resolution.tag != .root) continue; + try workspace_pkg_ids.append(allocator, @intCast(pkg_id)); + } + + const converted_filters = converted_filters: { + const buf = try allocator.alloc(FilterType, filters.len); + for (filters, buf) |filter, *converted| { + if ((filter.len == 1 and filter[0] == '*') or strings.eqlComptime(filter, "**")) { + converted.* = .all; + continue; + } + + const is_path = filter.len > 0 and filter[0] == '.'; + + const joined_filter = if (is_path) + strings.withoutTrailingSlash(path.joinAbsString(original_cwd, &[_]string{filter}, .posix)) + else + filter; + + if (joined_filter.len == 0) { + converted.* = FilterType.init(&.{}, is_path); + continue; + } + + const length = bun.simdutf.length.utf32.from.utf8.le(joined_filter); + const convert_buf = try allocator.alloc(u32, length); + + const convert_result = bun.simdutf.convert.utf8.to.utf32.with_errors.le(joined_filter, convert_buf); + if (!convert_result.isSuccessful()) { + // nothing would match + converted.* = FilterType.init(&.{}, false); + continue; + } + + converted.* = FilterType.init(convert_buf[0..convert_result.count], is_path); + } + break :converted_filters buf; + }; + defer { + for (converted_filters) |filter| { + filter.deinit(allocator); + } + allocator.free(converted_filters); + } + + // move all matched workspaces to front of array + var i: usize = 0; + while (i < workspace_pkg_ids.items.len) { + const workspace_pkg_id = workspace_pkg_ids.items[i]; + + const matched = matched: { + for (converted_filters) |filter| { + switch (filter) { + .path => |pattern| { + if (pattern.len == 0) continue; + const res = pkg_resolutions[workspace_pkg_id]; + + const res_path = switch (res.tag) { + .workspace => res.value.workspace.slice(string_buf), + .root => FileSystem.instance.top_level_dir, + else => unreachable, + }; + + const abs_res_path = path.joinAbsString(FileSystem.instance.top_level_dir, &[_]string{res_path}, .posix); + + if (!glob.matchImpl(pattern, strings.withoutTrailingSlash(abs_res_path)).matches()) { + break :matched false; + } + }, + .name => |pattern| { + const name = pkg_names[workspace_pkg_id].slice(string_buf); + + if (!glob.matchImpl(pattern, name).matches()) { + break :matched false; + } + }, + .all => {}, + } + } + + break :matched true; + }; + + if (matched) { + i += 1; + } else { + _ = workspace_pkg_ids.swapRemove(i); + } + } + + return workspace_pkg_ids.items; + } + + fn printOutdatedInfoTable( + manager: *PackageManager, + workspace_pkg_ids: []const PackageID, + was_filtered: bool, + comptime enable_ansi_colors: bool, + ) !void { + const package_patterns = package_patterns: { + const args = manager.options.positionals[1..]; + if (args.len == 0) break :package_patterns null; + + var at_least_one_greater_than_zero = false; + + const patterns_buf = bun.default_allocator.alloc(FilterType, args.len) catch bun.outOfMemory(); + for (args, patterns_buf) |arg, *converted| { + if (arg.len == 0) { + converted.* = FilterType.init(&.{}, false); + continue; + } + + if ((arg.len == 1 and arg[0] == '*') or strings.eqlComptime(arg, "**")) { + converted.* = .all; + at_least_one_greater_than_zero = true; + continue; + } + + const length = bun.simdutf.length.utf32.from.utf8.le(arg); + const convert_buf = bun.default_allocator.alloc(u32, length) catch bun.outOfMemory(); + + const convert_result = bun.simdutf.convert.utf8.to.utf32.with_errors.le(arg, convert_buf); + if (!convert_result.isSuccessful()) { + converted.* = FilterType.init(&.{}, false); + continue; + } + + converted.* = FilterType.init(convert_buf[0..convert_result.count], false); + at_least_one_greater_than_zero = at_least_one_greater_than_zero or convert_result.count > 0; + } + + // nothing will match + if (!at_least_one_greater_than_zero) return; + + break :package_patterns patterns_buf; + }; + defer { + if (package_patterns) |patterns| { + for (patterns) |pattern| { + pattern.deinit(bun.default_allocator); + } + bun.default_allocator.free(patterns); + } + } + + var max_name: usize = 0; + var max_current: usize = 0; + var max_update: usize = 0; + var max_latest: usize = 0; + var max_workspace: usize = 0; + + const lockfile = manager.lockfile; + const string_buf = lockfile.buffers.string_bytes.items; + const dependencies = lockfile.buffers.dependencies.items; + const packages = lockfile.packages.slice(); + const pkg_names = packages.items(.name); + const pkg_resolutions = packages.items(.resolution); + const pkg_dependencies = packages.items(.dependencies); + + var version_buf = std.ArrayList(u8).init(bun.default_allocator); + defer version_buf.deinit(); + const version_writer = version_buf.writer(); + + var outdated_ids: std.ArrayListUnmanaged(struct { package_id: PackageID, dep_id: DependencyID, workspace_pkg_id: PackageID }) = .{}; + defer outdated_ids.deinit(manager.allocator); + + for (workspace_pkg_ids) |workspace_pkg_id| { + const pkg_deps = pkg_dependencies[workspace_pkg_id]; + for (pkg_deps.begin()..pkg_deps.end()) |dep_id| { + const package_id = lockfile.buffers.resolutions.items[dep_id]; + if (package_id == invalid_package_id) continue; + const dep = lockfile.buffers.dependencies.items[dep_id]; + if (dep.version.tag != .npm and dep.version.tag != .dist_tag) continue; + const resolution = pkg_resolutions[package_id]; + if (resolution.tag != .npm) continue; + + // package patterns match against dependency name (name in package.json) + if (package_patterns) |patterns| { + const match = match: { + for (patterns) |pattern| { + switch (pattern) { + .path => unreachable, + .name => |name_pattern| { + if (name_pattern.len == 0) continue; + if (!glob.matchImpl(name_pattern, dep.name.slice(string_buf)).matches()) { + break :match false; + } + }, + .all => {}, + } + } + + break :match true; + }; + if (!match) { + continue; + } + } + + const package_name = pkg_names[package_id].slice(string_buf); + var expired = false; + const manifest = manager.manifests.byNameAllowExpired( + manager.scopeForPackageName(package_name), + package_name, + &expired, + ) orelse continue; + + const latest = manifest.findByDistTag("latest") orelse continue; + + const update_version = if (dep.version.tag == .npm) + manifest.findBestVersion(dep.version.value.npm.version, string_buf) orelse continue + else + manifest.findByDistTag(dep.version.value.dist_tag.tag.slice(string_buf)) orelse continue; + + if (resolution.value.npm.version.order(latest.version, string_buf, manifest.string_buf) != .lt) continue; + + const package_name_len = package_name.len + + if (dep.behavior.dev) + " (dev)".len + else if (dep.behavior.peer) + " (peer)".len + else if (dep.behavior.optional) + " (optional)".len + else + 0; + + if (package_name_len > max_name) max_name = package_name_len; + + version_writer.print("{}", .{resolution.value.npm.version.fmt(string_buf)}) catch bun.outOfMemory(); + if (version_buf.items.len > max_current) max_current = version_buf.items.len; + version_buf.clearRetainingCapacity(); + + version_writer.print("{}", .{update_version.version.fmt(manifest.string_buf)}) catch bun.outOfMemory(); + if (version_buf.items.len > max_update) max_update = version_buf.items.len; + version_buf.clearRetainingCapacity(); + + version_writer.print("{}", .{latest.version.fmt(manifest.string_buf)}) catch bun.outOfMemory(); + if (version_buf.items.len > max_latest) max_latest = version_buf.items.len; + version_buf.clearRetainingCapacity(); + + const workspace_name = pkg_names[workspace_pkg_id].slice(string_buf); + if (workspace_name.len > max_workspace) max_workspace = workspace_name.len; + + outdated_ids.append( + bun.default_allocator, + .{ + .package_id = package_id, + .dep_id = @intCast(dep_id), + .workspace_pkg_id = workspace_pkg_id, + }, + ) catch bun.outOfMemory(); + } + } + + if (outdated_ids.items.len == 0) return; + + const package_column_inside_length = @max("Packages".len, max_name); + const current_column_inside_length = @max("Current".len, max_current); + const update_column_inside_length = @max("Update".len, max_update); + const latest_column_inside_length = @max("Latest".len, max_latest); + const workspace_column_inside_length = @max("Workspace".len, max_workspace); + + const column_left_pad = 1; + const column_right_pad = 1; + + const table = Table("blue", column_left_pad, column_right_pad, enable_ansi_colors).init( + &if (was_filtered) + [_][]const u8{ + "Package", + "Current", + "Update", + "Latest", + "Workspace", + } + else + [_][]const u8{ + "Package", + "Current", + "Update", + "Latest", + }, + &if (was_filtered) + [_]usize{ + package_column_inside_length, + current_column_inside_length, + update_column_inside_length, + latest_column_inside_length, + workspace_column_inside_length, + } + else + [_]usize{ + package_column_inside_length, + current_column_inside_length, + update_column_inside_length, + latest_column_inside_length, + }, + ); + + table.printTopLineSeparator(); + table.printColumnNames(); + + for (workspace_pkg_ids) |workspace_pkg_id| { + inline for ( + .{ + Behavior{ .normal = true }, + Behavior{ .dev = true }, + Behavior{ .peer = true }, + Behavior{ .optional = true }, + }, + ) |group_behavior| { + for (outdated_ids.items) |ids| { + if (workspace_pkg_id != ids.workspace_pkg_id) continue; + const package_id = ids.package_id; + const dep_id = ids.dep_id; + + const dep = dependencies[dep_id]; + if (@as(u8, @bitCast(group_behavior)) & @as(u8, @bitCast(dep.behavior)) == 0) continue; + + const package_name = pkg_names[package_id].slice(string_buf); + const resolution = pkg_resolutions[package_id]; + + var expired = false; + const manifest = manager.manifests.byNameAllowExpired( + manager.scopeForPackageName(package_name), + package_name, + &expired, + ) orelse continue; + + const latest = manifest.findByDistTag("latest") orelse continue; + const update = if (dep.version.tag == .npm) + manifest.findBestVersion(dep.version.value.npm.version, string_buf) orelse continue + else + manifest.findByDistTag(dep.version.value.dist_tag.tag.slice(string_buf)) orelse continue; + + table.printLineSeparator(); + + { + // package name + const behavior_str = if (dep.behavior.dev) + " (dev)" + else if (dep.behavior.peer) + " (peer)" + else if (dep.behavior.optional) + " (optional)" + else + ""; + + Output.pretty("{s}", .{table.verticalEdge()}); + for (0..column_left_pad) |_| Output.pretty(" ", .{}); + + Output.pretty("{s}{s}", .{ package_name, behavior_str }); + for (package_name.len + behavior_str.len..package_column_inside_length + column_right_pad) |_| Output.pretty(" ", .{}); + } + + { + // current version + Output.pretty("{s}", .{table.verticalEdge()}); + for (0..column_left_pad) |_| Output.pretty(" ", .{}); + + version_writer.print("{}", .{resolution.value.npm.version.fmt(string_buf)}) catch bun.outOfMemory(); + Output.pretty("{s}", .{version_buf.items}); + for (version_buf.items.len..current_column_inside_length + column_right_pad) |_| Output.pretty(" ", .{}); + version_buf.clearRetainingCapacity(); + } + + { + // update version + Output.pretty("{s}", .{table.verticalEdge()}); + for (0..column_left_pad) |_| Output.pretty(" ", .{}); + + version_writer.print("{}", .{update.version.fmt(manifest.string_buf)}) catch bun.outOfMemory(); + Output.pretty("{s}", .{update.version.diffFmt(resolution.value.npm.version, manifest.string_buf, string_buf)}); + for (version_buf.items.len..update_column_inside_length + column_right_pad) |_| Output.pretty(" ", .{}); + version_buf.clearRetainingCapacity(); + } + + { + // latest version + Output.pretty("{s}", .{table.verticalEdge()}); + for (0..column_left_pad) |_| Output.pretty(" ", .{}); + + version_writer.print("{}", .{latest.version.fmt(manifest.string_buf)}) catch bun.outOfMemory(); + Output.pretty("{s}", .{latest.version.diffFmt(resolution.value.npm.version, manifest.string_buf, string_buf)}); + for (version_buf.items.len..latest_column_inside_length + column_right_pad) |_| Output.pretty(" ", .{}); + version_buf.clearRetainingCapacity(); + } + + if (was_filtered) { + Output.pretty("{s}", .{table.verticalEdge()}); + for (0..column_left_pad) |_| Output.pretty(" ", .{}); + + const workspace_name = pkg_names[workspace_pkg_id].slice(string_buf); + Output.pretty("{s}", .{workspace_name}); + + for (workspace_name.len..workspace_column_inside_length + column_right_pad) |_| Output.pretty(" ", .{}); + } + + Output.pretty("{s}\n", .{table.verticalEdge()}); + } + } + } + + table.printBottomLineSeparator(); + } + + fn updateManifestsIfNecessary( + manager: *PackageManager, + comptime log_level: PackageManager.Options.LogLevel, + workspace_pkg_ids: []const PackageID, + ) !void { + const lockfile = manager.lockfile; + const resolutions = lockfile.buffers.resolutions.items; + const dependencies = lockfile.buffers.dependencies.items; + const string_buf = lockfile.buffers.string_bytes.items; + const packages = lockfile.packages.slice(); + const pkg_resolutions = packages.items(.resolution); + const pkg_names = packages.items(.name); + const pkg_dependencies = packages.items(.dependencies); + + for (workspace_pkg_ids) |workspace_pkg_id| { + const pkg_deps = pkg_dependencies[workspace_pkg_id]; + for (pkg_deps.begin()..pkg_deps.end()) |dep_id| { + if (dep_id >= dependencies.len) continue; + const package_id = resolutions[dep_id]; + if (package_id == invalid_package_id) continue; + const dep = dependencies[dep_id]; + if (dep.version.tag != .npm and dep.version.tag != .dist_tag) continue; + const resolution: Install.Resolution = pkg_resolutions[package_id]; + if (resolution.tag != .npm) continue; + + const package_name = pkg_names[package_id].slice(string_buf); + _ = manager.manifests.byName( + manager.scopeForPackageName(package_name), + package_name, + ) orelse { + const task_id = Install.Task.Id.forManifest(package_name); + if (manager.hasCreatedNetworkTask(task_id, dep.behavior.optional)) continue; + + manager.startProgressBarIfNone(); + + var task = manager.getNetworkTask(); + task.* = .{ + .package_manager = &PackageManager.instance, + .callback = undefined, + .task_id = task_id, + .allocator = manager.allocator, + }; + try task.forManifest( + package_name, + manager.allocator, + manager.scopeForPackageName(package_name), + null, + dep.behavior.optional, + ); + + manager.enqueueNetworkTask(task); + }; + } + + manager.flushNetworkQueue(); + _ = manager.scheduleTasks(); + + if (manager.pendingTaskCount() > 1) { + try manager.runTasks( + *PackageManager, + manager, + .{ + .onExtract = {}, + .onResolve = {}, + .onPackageManifestError = {}, + .onPackageDownloadError = {}, + .progress_bar = true, + .manifests_only = true, + }, + true, + log_level, + ); + } + } + + manager.flushNetworkQueue(); + _ = manager.scheduleTasks(); + + const RunClosure = struct { + manager: *PackageManager, + err: ?anyerror = null, + pub fn isDone(closure: *@This()) bool { + if (closure.manager.pendingTaskCount() > 0) { + closure.manager.runTasks( + *PackageManager, + closure.manager, + .{ + .onExtract = {}, + .onResolve = {}, + .onPackageManifestError = {}, + .onPackageDownloadError = {}, + .progress_bar = true, + .manifests_only = true, + }, + true, + log_level, + ) catch |err| { + closure.err = err; + return true; + }; + } + + return closure.manager.pendingTaskCount() == 0; + } + }; + + var run_closure: RunClosure = .{ .manager = manager }; + manager.sleepUntil(&run_closure, &RunClosure.isDone); + + if (comptime log_level.showProgress()) { + manager.endProgressBar(); + Output.flush(); + } + + if (run_closure.err) |err| { + return err; + } + } +}; diff --git a/src/cli/pack_command.zig b/src/cli/pack_command.zig new file mode 100644 index 0000000000000..ee41c8ddaac5a --- /dev/null +++ b/src/cli/pack_command.zig @@ -0,0 +1,2276 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Global = bun.Global; +const Output = bun.Output; +const Command = bun.CLI.Command; +const Install = bun.install; +const Bin = Install.Bin; +const PackageManager = Install.PackageManager; +const Lockfile = Install.Lockfile; +const PackageID = Install.PackageID; +const DependencyID = Install.DependencyID; +const Behavior = Install.Dependency.Behavior; +const string = bun.string; +const stringZ = bun.stringZ; +const libarchive = @import("../libarchive/libarchive.zig").lib; +const Archive = libarchive.Archive; +const Expr = bun.js_parser.Expr; +const Semver = @import("../install/semver.zig"); +const File = bun.sys.File; +const FD = bun.FD; +const strings = bun.strings; +const glob = bun.glob; +const PathBuffer = bun.PathBuffer; +const DirIterator = bun.DirIterator; +const Environment = bun.Environment; +const RunCommand = bun.RunCommand; +const FileSystem = bun.fs.FileSystem; +const OOM = bun.OOM; +const js_printer = bun.js_printer; +const E = bun.js_parser.E; +const Progress = bun.Progress; +const JSON = bun.JSON; +const BoringSSL = bun.BoringSSL; +const sha = bun.sha; +const LogLevel = PackageManager.Options.LogLevel; +const FileDescriptor = bun.FileDescriptor; + +pub const PackCommand = struct { + pub const Context = struct { + manager: *PackageManager, + allocator: std.mem.Allocator, + command_ctx: Command.Context, + + // `bun pack` does not require a lockfile, but + // it's possible we will need it for finding + // workspace versions. This is the only valid lockfile + // pointer in this file. `manager.lockfile` is incorrect + lockfile: ?*Lockfile, + + bundled_deps: std.ArrayListUnmanaged(BundledDep) = .{}, + + stats: struct { + unpacked_size: usize = 0, + total_files: usize = 0, + ignored_files: usize = 0, + ignored_directories: usize = 0, + packed_size: usize = 0, + bundled_deps: usize = 0, + } = .{}, + + pub const BundledDep = struct { + name: string, + was_packed: bool = false, + from_root_package_json: bool, + }; + + const IntegrityFormatter = struct { + bytes: [sha.SHA512.digest]u8, + + pub fn format(this: IntegrityFormatter, comptime _: string, _: std.fmt.FormatOptions, writer: anytype) !void { + var buf: [std.base64.standard.Encoder.calcSize(sha.SHA512.digest)]u8 = undefined; + const count = bun.simdutf.base64.encode(this.bytes[0..sha.SHA512.digest], &buf, false); + + const encoded = buf[0..count]; + + try writer.print("sha512-{s}[...]{s}", .{ encoded[0..13], encoded[encoded.len - 15 ..] }); + } + }; + + fn fmtIntegrity(bytes: [sha.SHA512.digest]u8) IntegrityFormatter { + return .{ + .bytes = bytes, + }; + } + + pub fn printSummary(this: *const Context, sha1_digest: ?[sha.SHA1.digest]u8, sha512_digest: ?[sha.SHA512.digest]u8, comptime log_level: LogLevel) void { + if (comptime log_level != .silent) { + const stats = this.stats; + Output.prettyln("\nTotal files: {d}", .{stats.total_files}); + if (sha1_digest) |sha1| { + Output.prettyln("Shasum: {s}", .{bun.fmt.bytesToHex(sha1, .lower)}); + } + if (sha512_digest) |sha512| { + Output.prettyln("Integrity: {}", .{fmtIntegrity(sha512)}); + } + Output.prettyln("Unpacked size: {}", .{ + bun.fmt.size(stats.unpacked_size, .{ .space_between_number_and_unit = false }), + }); + if (stats.packed_size > 0) { + Output.pretty("Packed size: {}\n", .{ + bun.fmt.size(stats.packed_size, .{ .space_between_number_and_unit = false }), + }); + } + if (stats.bundled_deps > 0) { + Output.pretty("Bundled deps: {d}\n", .{stats.bundled_deps}); + } + } + } + }; + + pub fn execWithManager(ctx: Command.Context, manager: *PackageManager) !void { + Output.prettyErrorln("bun pack v" ++ Global.package_json_version_with_sha ++ "", .{}); + Output.flush(); + + var lockfile: Lockfile = undefined; + const load_from_disk_result = lockfile.loadFromDisk( + manager, + manager.allocator, + manager.log, + manager.options.lockfile_path, + false, + ); + + var pack_ctx: Context = .{ + .manager = manager, + .allocator = ctx.allocator, + .command_ctx = ctx, + .lockfile = switch (load_from_disk_result) { + .ok => |ok| ok.lockfile, + .err => |cause| err: { + switch (cause.step) { + .open_file => { + if (cause.value == error.ENOENT) break :err null; + Output.errGeneric("failed to open lockfile: {s}", .{ + @errorName(cause.value), + }); + }, + .parse_file => Output.errGeneric("failed to parse lockfile: {s}", .{ + @errorName(cause.value), + }), + .read_file => Output.errGeneric("failed to read lockfile: {s}", .{ + @errorName(cause.value), + }), + .migrating => Output.errGeneric("failed to migrate lockfile: {s}", .{ + @errorName(cause.value), + }), + } + + if (ctx.log.hasErrors()) { + switch (Output.enable_ansi_colors) { + inline else => |enable_ansi_colors| try manager.log.printForLogLevelWithEnableAnsiColors( + Output.errorWriter(), + enable_ansi_colors, + ), + } + } + + Global.crash(); + }, + else => null, + }, + }; + + switch (manager.options.log_level) { + inline else => |log_level| { + // var arena = std.heap.ArenaAllocator.init(ctx.allocator); + // defer arena.deinit(); + + // if (manager.options.filter_patterns.len > 0) { + // // TODO: --filter + // // loop, convert, find matching workspaces, then pack each + // return; + // } + + // just pack the current workspace + pack(&pack_ctx, manager.original_package_json_path, log_level) catch |err| { + switch (err) { + error.OutOfMemory => bun.outOfMemory(), + error.MissingPackageName, error.MissingPackageVersion => { + Output.errGeneric("package.json must have `name` and `version` fields", .{}); + Global.crash(); + }, + error.InvalidPackageName, error.InvalidPackageVersion => { + Output.errGeneric("package.json `name` and `version` fields must be non-empty strings", .{}); + Global.crash(); + }, + error.MissingPackageJSON => { + Output.errGeneric("failed to find a package.json in: \"{s}\"", .{manager.original_package_json_path}); + Global.crash(); + }, + } + }; + }, + } + } + + pub fn exec(ctx: Command.Context) !void { + const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .pack); + + const manager, const original_cwd = PackageManager.init(ctx, cli, .pack) catch |err| { + if (!cli.silent) { + switch (err) { + error.MissingPackageJSON => { + var cwd_buf: bun.PathBuffer = undefined; + const cwd = bun.getcwd(&cwd_buf) catch { + Output.errGeneric("failed to find project package.json", .{}); + Global.crash(); + }; + Output.errGeneric("failed to find project package.json from: \"{s}\"", .{cwd}); + }, + else => Output.errGeneric("failed to initialize bun install: {s}", .{@errorName(err)}), + } + } + + Global.crash(); + }; + defer ctx.allocator.free(original_cwd); + + return execWithManager(ctx, manager); + } + + const PackError = OOM || error{ + MissingPackageName, + InvalidPackageName, + MissingPackageVersion, + InvalidPackageVersion, + MissingPackageJSON, + }; + + const package_prefix = "package/"; + + const root_default_ignore_patterns = [_][]const u32{ + &.{ 112, 97, 99, 107, 97, 103, 101, 45, 108, 111, 99, 107, 46, 106, 115, 111, 110 }, // package-lock.json + &.{ 121, 97, 114, 110, 46, 108, 111, 99, 107 }, // yarn.lock + &.{ 112, 110, 112, 109, 45, 108, 111, 99, 107, 46, 121, 97, 109, 108 }, // pnpm-lock.yaml + &.{ 'b', 'u', 'n', '.', 'l', 'o', 'c', 'k', 'b' }, // bun.lockb + &.{ 'b', 'u', 'n', '.', 'l', 'o', 'c', 'k' }, + }; + + // pattern, can override + const default_ignore_patterns = [_]struct { []const u32, bool }{ + .{ &.{ '.', '*', '.', 's', 'w', 'p' }, true }, + .{ &.{ 46, 95, 42 }, true }, // "._*", + .{ &.{ 46, 68, 83, 95, 83, 116, 111, 114, 101 }, true }, // ".DS_Store", + .{ &.{ 46, 103, 105, 116 }, false }, // ".git", + .{ &.{ 46, 103, 105, 116, 105, 103, 110, 111, 114, 101 }, true }, // ".gitignore", + .{ &.{ 46, 104, 103 }, false }, // ".hg", + .{ &.{ 46, 110, 112, 109, 105, 103, 110, 111, 114, 101 }, true }, // ".npmignore", + .{ &.{ 46, 110, 112, 109, 114, 99 }, false }, // ".npmrc", + .{ &.{ 46, 108, 111, 99, 107, 45, 119, 115, 99, 114, 105, 112, 116 }, true }, // ".lock-wscript", + .{ &.{ 46, 115, 118, 110 }, true }, // ".svn", + .{ &.{ 46, 119, 97, 102, 112, 105, 99, 107, 108, 101, 45, 42 }, true }, // ".wafpickle-*", + .{ &.{ 67, 86, 83 }, true }, // "CVS", + .{ &.{ 110, 112, 109, 45, 100, 101, 98, 117, 103, 46, 108, 111, 103 }, true }, // "npm-debug.log", + // mentioned in the docs but does not appear to be ignored by default + // .{ &.{ 99, 111, 110, 102, 105, 103, 46, 103, 121, 112, 105 }, false }, // "config.gypi", + + .{ &.{ '.', 'e', 'n', 'v', '.', 'p', 'r', 'o', 'd', 'u', 'c', 't', 'i', 'o', 'n' }, true }, + .{ &.{ 'b', 'u', 'n', 'f', 'i', 'g', '.', 't', 'o', 'm', 'l' }, true }, + }; + + const PackListEntry = struct { + subpath: stringZ, + size: usize = 0, + }; + const PackList = std.ArrayListUnmanaged(PackListEntry); + + const PackQueueContext = struct { + pub fn lessThan(_: void, a: string, b: string) std.math.Order { + return strings.order(a, b); + } + }; + + const PackQueue = std.PriorityQueue(stringZ, void, PackQueueContext.lessThan); + + const DirInfo = struct { + std.fs.Dir, // the dir + string, // the dir subpath + usize, // dir depth. used to shrink ignore stack + }; + + fn iterateIncludedProjectTree( + ctx: *Context, + includes: []const Pattern, + root_dir: std.fs.Dir, + comptime log_level: LogLevel, + ) OOM!PackQueue { + var pack_queue = PackQueue.init(ctx.allocator, {}); + + var ignores: std.ArrayListUnmanaged(IgnorePatterns) = .{}; + defer ignores.deinit(ctx.allocator); + + var dirs: std.ArrayListUnmanaged(DirInfo) = .{}; + defer dirs.deinit(ctx.allocator); + + try dirs.append(ctx.allocator, .{ root_dir, "", 1 }); + + var included_dirs: std.ArrayListUnmanaged(DirInfo) = .{}; + defer included_dirs.deinit(ctx.allocator); + + var subpath_dedupe = bun.StringHashMap(void).init(ctx.allocator); + defer subpath_dedupe.deinit(); + + // first find included dirs and files + while (dirs.popOrNull()) |dir_info| { + var dir, const dir_subpath, const dir_depth = dir_info; + defer { + if (dir_depth != 1) { + dir.close(); + } + } + + var dir_iter = DirIterator.iterate(dir, .u8); + while (dir_iter.next().unwrap() catch null) |entry| { + if (entry.kind != .file and entry.kind != .directory) continue; + + const entry_name = entry.name.slice(); + const entry_subpath = try entrySubpath(ctx, dir_subpath, entry_name); + + var included = false; + + if (dir_depth == 1) { + if (strings.eqlComptime(entry_name, "package.json")) continue; + if (strings.eqlComptime(entry_name, "node_modules")) continue; + + // TODO: should this be case insensitive on all platforms? + const eql = if (comptime Environment.isLinux) + strings.eqlComptime + else + strings.eqlCaseInsensitiveASCIIICheckLength; + + if (entry.kind == .file and + (eql(entry_name, "package.json") or + eql(entry_name, "LICENSE") or + eql(entry_name, "LICENCE") or + eql(entry_name, "README") or + entry_name.len > "README.".len and eql(entry_name[0.."README.".len], "README."))) + included = true; + } + + if (!included) { + for (includes) |include| { + if (include.dirs_only and entry.kind != .directory) continue; + + // include patters are not recursive unless they start with `**/` + // normally the behavior of `index.js` and `**/index.js` are the same, + // but includes require `**/` + const match_path = if (include.@"leading **/") entry_name else entry_subpath; + switch (glob.matchImpl(include.glob, match_path)) { + .match => included = true, + .negate_no_match => included = false, + + else => {}, + } + } + } + + if (!included) { + if (entry.kind == .directory) { + const subdir = openSubdir(dir, entry_name, entry_subpath); + try dirs.append(ctx.allocator, .{ subdir, entry_subpath, dir_depth + 1 }); + } + + continue; + } + + switch (entry.kind) { + .directory => { + const subdir = openSubdir(dir, entry_name, entry_subpath); + try included_dirs.append(ctx.allocator, .{ subdir, entry_subpath, dir_depth + 1 }); + }, + .file => { + const dedupe_entry = try subpath_dedupe.getOrPut(entry_subpath); + bun.assertWithLocation(!dedupe_entry.found_existing, @src()); + if (dedupe_entry.found_existing) continue; + + try pack_queue.add(entry_subpath); + }, + else => unreachable, + } + } + } + + // for each included dir, traverse it's entries, exclude any with `negate_no_match`. + for (included_dirs.items) |included_dir_info| { + try addEntireTree(ctx, included_dir_info, &pack_queue, &subpath_dedupe, log_level); + } + + return pack_queue; + } + + /// Adds all files in a directory tree to `pack_list` (default ignores still apply) + fn addEntireTree( + ctx: *Context, + root_dir_info: DirInfo, + pack_queue: *PackQueue, + maybe_dedupe: ?*bun.StringHashMap(void), + comptime log_level: LogLevel, + ) OOM!void { + var dirs: std.ArrayListUnmanaged(DirInfo) = .{}; + defer dirs.deinit(ctx.allocator); + + try dirs.append(ctx.allocator, root_dir_info); + + var ignores: std.ArrayListUnmanaged(IgnorePatterns) = .{}; + defer ignores.deinit(ctx.allocator); + + while (dirs.popOrNull()) |dir_info| { + var dir, const dir_subpath, const dir_depth = dir_info; + defer dir.close(); + + while (ignores.getLastOrNull()) |last| { + if (last.depth < dir_depth) break; + + last.deinit(ctx.allocator); + ignores.items.len -= 1; + } + + if (try IgnorePatterns.readFromDisk(ctx, dir, dir_depth)) |patterns| { + try ignores.append(ctx.allocator, patterns); + } + + if (comptime Environment.isDebug) { + // make sure depths are in order + if (ignores.items.len > 0) { + for (1..ignores.items.len) |i| { + bun.assertWithLocation(ignores.items[i - 1].depth < ignores.items[i].depth, @src()); + } + } + } + + var iter = DirIterator.iterate(dir, .u8); + while (iter.next().unwrap() catch null) |entry| { + if (entry.kind != .file and entry.kind != .directory) continue; + + const entry_name = entry.name.slice(); + const entry_subpath = try entrySubpath(ctx, dir_subpath, entry_name); + + if (dir_depth == root_dir_info[2]) { + if (entry.kind == .directory and strings.eqlComptime(entry_name, "node_modules")) continue; + } + + if (isExcluded(entry, entry_subpath, dir_depth, ignores.items)) |used_pattern_info| { + if (comptime log_level.isVerbose()) { + const pattern, const kind = used_pattern_info; + Output.prettyln("ignore [{s}:{}] {s}{s}", .{ + @tagName(kind), + bun.fmt.debugUtf32PathFormatter(pattern), + entry_subpath, + if (entry.kind == .directory) "/" else "", + }); + Output.flush(); + } + continue; + } + + switch (entry.kind) { + .file => { + if (maybe_dedupe) |dedupe| { + const dedupe_entry = try dedupe.getOrPut(entry_subpath); + if (dedupe_entry.found_existing) continue; + } + try pack_queue.add(entry_subpath); + }, + .directory => { + const subdir = openSubdir(dir, entry_name, entry_subpath); + + try dirs.append(ctx.allocator, .{ + subdir, + entry_subpath, + dir_depth + 1, + }); + }, + else => unreachable, + } + } + } + } + + fn openSubdir( + dir: std.fs.Dir, + entry_name: string, + entry_subpath: stringZ, + ) std.fs.Dir { + return dir.openDirZ( + entryNameZ(entry_name, entry_subpath), + .{ .iterate = true }, + ) catch |err| { + Output.err(err, "failed to open directory \"{s}\" for packing", .{entry_subpath}); + Global.crash(); + }; + } + + fn entrySubpath( + ctx: *Context, + dir_subpath: string, + entry_name: string, + ) OOM!stringZ { + return std.fmt.allocPrintZ(ctx.allocator, "{s}{s}{s}", .{ + dir_subpath, + if (dir_subpath.len == 0) "" else "/", + entry_name, + }); + } + + fn entryNameZ( + entry_name: string, + entry_subpath: stringZ, + ) stringZ { + // doing this because `entry_subpath` has a sentinel and I don't trust `entry.name.sliceAssumeZ()` + return entry_subpath[entry_subpath.len - entry_name.len ..][0..entry_name.len :0]; + } + + fn iterateBundledDeps( + ctx: *Context, + root_dir: std.fs.Dir, + comptime log_level: LogLevel, + ) OOM!PackQueue { + var bundled_pack_queue = PackQueue.init(ctx.allocator, {}); + if (ctx.bundled_deps.items.len == 0) return bundled_pack_queue; + + const dir = root_dir.openDirZ("node_modules", .{ .iterate = true }) catch |err| { + switch (err) { + // ignore node_modules if it isn't a directory + error.NotDir => return bundled_pack_queue, + + else => { + Output.err(err, "failed to open \"node_modules\" to pack bundled dependencies", .{}); + Global.crash(); + }, + } + }; + + // A set of bundled dependency locations + // - node_modules/is-even + // - node_modules/is-even/node_modules/is-odd + // - node_modules/is-odd + // - ... + var dedupe = bun.StringHashMap(void).init(ctx.allocator); + defer dedupe.deinit(); + + var additional_bundled_deps: std.ArrayListUnmanaged(DirInfo) = .{}; + defer additional_bundled_deps.deinit(ctx.allocator); + + var iter = DirIterator.iterate(dir, .u8); + while (iter.next().unwrap() catch null) |entry| { + if (entry.kind != .directory) continue; + + const entry_name = entry.name.slice(); + + for (ctx.bundled_deps.items) |*dep| { + bun.assertWithLocation(dep.from_root_package_json, @src()); + if (!strings.eqlLong(entry_name, dep.name, true)) continue; + + const entry_subpath = try entrySubpath(ctx, "node_modules", entry_name); + + const dedupe_entry = try dedupe.getOrPut(entry_subpath); + if (dedupe_entry.found_existing) { + // already got to it in `addBundledDep` below + dep.was_packed = true; + break; + } + + const subdir = openSubdir(dir, entry_name, entry_subpath); + dep.was_packed = true; + try addBundledDep( + ctx, + root_dir, + .{ subdir, entry_subpath, 2 }, + &bundled_pack_queue, + &dedupe, + &additional_bundled_deps, + log_level, + ); + + break; + } + } + + while (additional_bundled_deps.popOrNull()) |bundled_dir_info| { + const dir_subpath = bundled_dir_info[1]; + const maybe_slash = strings.lastIndexOfChar(dir_subpath, '/'); + bun.assertWithLocation(maybe_slash != null, @src()); + const dep_name: string = if (maybe_slash) |slash| dir_subpath[slash + 1 ..] else dir_subpath; + + try ctx.bundled_deps.append(ctx.allocator, .{ + .name = dep_name, + .from_root_package_json = false, + .was_packed = true, + }); + + try addBundledDep( + ctx, + root_dir, + bundled_dir_info, + &bundled_pack_queue, + &dedupe, + &additional_bundled_deps, + log_level, + ); + } + + return bundled_pack_queue; + } + + fn addBundledDep( + ctx: *Context, + root_dir: std.fs.Dir, + bundled_dir_info: DirInfo, + bundled_pack_queue: *PackQueue, + dedupe: *bun.StringHashMap(void), + additional_bundled_deps: *std.ArrayListUnmanaged(DirInfo), + comptime log_level: LogLevel, + ) OOM!void { + ctx.stats.bundled_deps += 1; + + var dirs: std.ArrayListUnmanaged(DirInfo) = .{}; + defer dirs.deinit(ctx.allocator); + + try dirs.append(ctx.allocator, bundled_dir_info); + + while (dirs.popOrNull()) |dir_info| { + var dir, const dir_subpath, const dir_depth = dir_info; + defer dir.close(); + + var iter = DirIterator.iterate(dir, .u8); + while (iter.next().unwrap() catch null) |entry| { + if (entry.kind != .file and entry.kind != .directory) continue; + + const entry_name = entry.name.slice(); + const entry_subpath = try entrySubpath(ctx, dir_subpath, entry_name); + + if (dir_depth == bundled_dir_info[2]) root_depth: { + if (strings.eqlComptime(entry_name, "package.json")) { + if (entry.kind != .file) break :root_depth; + // find more dependencies to bundle + const source = File.toSourceAt(dir, entryNameZ(entry_name, entry_subpath), ctx.allocator).unwrap() catch |err| { + Output.err(err, "failed to read package.json: \"{s}\"", .{entry_subpath}); + Global.crash(); + }; + + const json = JSON.ParsePackageJSONUTF8(&source, ctx.manager.log, ctx.allocator) catch + break :root_depth; + + // for each dependency in `dependencies` find the closest node_modules folder + // with the dependency name as a dir entry, starting from the node_modules of the + // current bundled dependency + + for ([_]string{ "dependencies", "optionalDependencies" }) |dependency_group| { + const dependencies_expr = json.get(dependency_group) orelse continue; + if (dependencies_expr.data != .e_object) continue; + + const dependencies = dependencies_expr.data.e_object; + next_dep: for (dependencies.properties.slice()) |dep| { + if (dep.key == null) continue; + if (dep.value == null) continue; + + const dep_name = dep.key.?.asString(ctx.allocator) orelse continue; + + const dep_subpath = try std.fmt.allocPrintZ(ctx.allocator, "{s}/node_modules/{s}", .{ + dir_subpath, + dep_name, + }); + + // starting at `node_modules/is-even/node_modules/is-odd` + var dep_dir_depth: usize = bundled_dir_info[2] + 2; + + if (root_dir.openDirZ(dep_subpath, .{ .iterate = true })) |dep_dir| { + const dedupe_entry = try dedupe.getOrPut(dep_subpath); + if (dedupe_entry.found_existing) continue; + + try additional_bundled_deps.append(ctx.allocator, .{ dep_dir, dep_subpath, dep_dir_depth }); + } else |_| { + // keep searching + + // slice off the `node_modules` from above + var remain: []u8 = dep_subpath[0..dir_subpath.len]; + + while (strings.lastIndexOf(remain, "node_modules")) |node_modules_start| { + dep_dir_depth -= 2; + const node_modules_end = node_modules_start + "node_modules".len; + dep_subpath[node_modules_end] = '/'; + @memcpy(dep_subpath[node_modules_end + 1 ..][0..dep_name.len], dep_name); + dep_subpath[node_modules_end + 1 + dep_name.len] = 0; + const parent_dep_subpath = dep_subpath[0 .. node_modules_end + 1 + dep_name.len :0]; + remain = remain[0..node_modules_start]; + + const parent_dep_dir = root_dir.openDirZ(parent_dep_subpath, .{ .iterate = true }) catch continue; + + const dedupe_entry = try dedupe.getOrPut(parent_dep_subpath); + if (dedupe_entry.found_existing) continue :next_dep; + + try additional_bundled_deps.append(ctx.allocator, .{ parent_dep_dir, parent_dep_subpath, dep_dir_depth }); + continue :next_dep; + } + } + } + } + + break :root_depth; + } + + if (strings.eqlComptime(entry_name, "node_modules")) continue; + } + + if (isExcluded(entry, entry_subpath, dir_depth, &.{})) |used_pattern_info| { + if (comptime log_level.isVerbose()) { + const pattern, const kind = used_pattern_info; + Output.prettyln("ignore [{s}:{}] {s}{s}", .{ + @tagName(kind), + bun.fmt.debugUtf32PathFormatter(pattern), + entry_subpath, + if (entry.kind == .directory) "/" else "", + }); + Output.flush(); + } + continue; + } + + switch (entry.kind) { + .file => { + try bundled_pack_queue.add(entry_subpath); + }, + .directory => { + const subdir = openSubdir(dir, entry_name, entry_subpath); + + try dirs.append(ctx.allocator, .{ + subdir, + entry_subpath, + dir_depth + 1, + }); + }, + else => unreachable, + } + } + } + } + + /// Returns a list of files to pack and another list of files from bundled dependencies + fn iterateProjectTree( + ctx: *Context, + root_dir: std.fs.Dir, + comptime log_level: LogLevel, + ) OOM!PackQueue { + var pack_queue = PackQueue.init(ctx.allocator, {}); + + var ignores: std.ArrayListUnmanaged(IgnorePatterns) = .{}; + defer ignores.deinit(ctx.allocator); + + // Stacks and depth-first traversal. Doing so means we can push and pop from + // ignore patterns without needing to clone the entire list for future use. + var dirs: std.ArrayListUnmanaged(DirInfo) = .{}; + defer dirs.deinit(ctx.allocator); + + try dirs.append(ctx.allocator, .{ root_dir, "", 1 }); + + while (dirs.popOrNull()) |dir_info| { + var dir, const dir_subpath, const dir_depth = dir_info; + defer { + if (dir_depth != 1) { + dir.close(); + } + } + + while (ignores.getLastOrNull()) |last| { + if (last.depth < dir_depth) break; + + // pop patterns from files greater than or equal to the current depth. + last.deinit(ctx.allocator); + ignores.items.len -= 1; + } + + if (try IgnorePatterns.readFromDisk(ctx, dir, dir_depth)) |patterns| { + try ignores.append(ctx.allocator, patterns); + } + + if (comptime Environment.isDebug) { + // make sure depths are in order + if (ignores.items.len > 0) { + for (1..ignores.items.len) |i| { + bun.assertWithLocation(ignores.items[i - 1].depth < ignores.items[i].depth, @src()); + } + } + } + + var dir_iter = DirIterator.iterate(dir, .u8); + while (dir_iter.next().unwrap() catch null) |entry| { + if (entry.kind != .file and entry.kind != .directory) continue; + + const entry_name = entry.name.slice(); + const entry_subpath = try entrySubpath(ctx, dir_subpath, entry_name); + + if (dir_depth == 1) { + // Special case root package.json. It is always included + // and is possibly edited, so it's easier to handle it + // separately + if (strings.eqlComptime(entry_name, "package.json")) continue; + + // bundled dependencies are included only if they exist on disk. + // handled later for simplicity + if (strings.eqlComptime(entry_name, "node_modules")) continue; + } + + if (isExcluded(entry, entry_subpath, dir_depth, ignores.items)) |used_pattern_info| { + if (comptime log_level.isVerbose()) { + const pattern, const kind = used_pattern_info; + Output.prettyln("ignore [{s}:{}] {s}{s}", .{ + @tagName(kind), + bun.fmt.debugUtf32PathFormatter(pattern), + entry_subpath, + if (entry.kind == .directory) "/" else "", + }); + Output.flush(); + } + continue; + } + + switch (entry.kind) { + .file => { + bun.assertWithLocation(entry_subpath.len > 0, @src()); + try pack_queue.add(entry_subpath); + }, + .directory => { + const subdir = openSubdir(dir, entry_name, entry_subpath); + + try dirs.append(ctx.allocator, .{ + subdir, + entry_subpath, + dir_depth + 1, + }); + }, + else => unreachable, + } + } + } + + return pack_queue; + } + + fn getBundledDeps( + ctx: *Context, + json: Expr, + comptime field: string, + ) OOM!?std.ArrayListUnmanaged(Context.BundledDep) { + var deps: std.ArrayListUnmanaged(Context.BundledDep) = .{}; + const bundled_deps = json.get(field) orelse return null; + + invalid_field: { + var iter = bundled_deps.asArray() orelse switch (bundled_deps.data) { + .e_array => return .{}, + else => break :invalid_field, + }; + while (iter.next()) |bundled_dep_item| { + const bundled_dep = bundled_dep_item.asStringCloned(ctx.allocator) orelse break :invalid_field; + try deps.append(ctx.allocator, .{ + .name = bundled_dep, + .from_root_package_json = true, + }); + } + + return deps; + } + + Output.errGeneric("expected `{s}` to be an array of strings", .{field}); + Global.crash(); + } + + const BinInfo = struct { + path: string, + type: Type, + + const Type = enum { + file, + dir, + }; + }; + + fn getPackageBins( + ctx: *Context, + json: Expr, + ) OOM![]const BinInfo { + var bins: std.ArrayListUnmanaged(BinInfo) = .{}; + + var path_buf: PathBuffer = undefined; + + if (json.asProperty("bin")) |bin| { + if (bin.expr.asString(ctx.allocator)) |bin_str| { + const normalized = bun.path.normalizeBuf(bin_str, &path_buf, .posix); + try bins.append(ctx.allocator, .{ + .path = try ctx.allocator.dupe(u8, normalized), + .type = .file, + }); + return bins.items; + } + + switch (bin.expr.data) { + .e_object => |bin_obj| { + if (bin_obj.properties.len == 0) return &.{}; + + for (bin_obj.properties.slice()) |bin_prop| { + if (bin_prop.value) |bin_prop_value| { + if (bin_prop_value.asString(ctx.allocator)) |bin_str| { + const normalized = bun.path.normalizeBuf(bin_str, &path_buf, .posix); + try bins.append(ctx.allocator, .{ + .path = try ctx.allocator.dupe(u8, normalized), + .type = .file, + }); + } + } + } + }, + else => {}, + } + + return bins.items; + } + + if (json.asProperty("directories")) |directories| { + switch (directories.expr.data) { + .e_object => |directories_obj| { + if (directories_obj.asProperty("bin")) |bin| { + if (bin.expr.asString(ctx.allocator)) |bin_str| { + const normalized = bun.path.normalizeBuf(bin_str, &path_buf, .posix); + try bins.append(ctx.allocator, .{ + .path = try ctx.allocator.dupe(u8, normalized), + .type = .dir, + }); + } + } + }, + else => {}, + } + } + + return bins.items; + } + + fn isPackageBin(bins: []const BinInfo, maybe_bin_path: string) bool { + for (bins) |bin| { + switch (bin.type) { + .file => { + if (strings.eqlLong(bin.path, maybe_bin_path, true)) { + return true; + } + }, + .dir => { + const bin_without_trailing = strings.withoutTrailingSlash(bin.path); + if (strings.hasPrefix(maybe_bin_path, bin_without_trailing)) { + const remain = maybe_bin_path[bin_without_trailing.len..]; + if (remain.len > 1 and remain[0] == '/' and !strings.containsChar(remain[1..], '/')) { + return true; + } + } + }, + } + } + + return false; + } + + fn isExcluded( + entry: DirIterator.IteratorResult, + entry_subpath: stringZ, + dir_depth: usize, + ignores: []const IgnorePatterns, + ) ?struct { []const u32, IgnorePatterns.Kind } { + const entry_name = entry.name.slice(); + + if (dir_depth == 1) { + + // TODO: should this be case insensitive on all platforms? + const eql = if (comptime Environment.isLinux) + strings.eqlComptime + else + strings.eqlCaseInsensitiveASCIIICheckLength; + + // first, check files that can never be ignored. project root directory only + if (entry.kind == .file and + (eql(entry_name, "package.json") or + eql(entry_name, "LICENSE") or + eql(entry_name, "LICENCE") or + eql(entry_name, "README") or + entry_name.len > "README.".len and eql(entry_name[0.."README.".len], "README.") or + eql(entry_name, "CHANGELOG") or + entry_name.len > "CHANGELOG.".len and eql(entry_name[0.."CHANGELOG.".len], "CHANGELOG."))) + return null; + + // check default ignores that only apply to the root project directory + for (root_default_ignore_patterns) |pattern| { + switch (glob.matchImpl(pattern, entry_name)) { + .match => { + // cannot be reversed + return .{ + pattern, + .default, + }; + }, + + .no_match => {}, + + // default patterns don't use `!` + .negate_no_match => unreachable, + .negate_match => unreachable, + } + } + } + + var ignore_pattern: []const u32 = &.{}; + var ignore_kind: IgnorePatterns.Kind = .@".npmignore"; + + // then check default ignore list. None of the defaults contain slashes + // so just match agaist entry name + var ignored = false; + + for (default_ignore_patterns) |pattern_info| { + const pattern, const can_override = pattern_info; + switch (glob.matchImpl(pattern, entry_name)) { + .match => { + if (can_override) { + ignored = true; + ignore_pattern = pattern; + ignore_kind = .default; + + // break. doesnt matter if more default patterns + // match this path + break; + } + + return .{ + pattern, + .default, + }; + }, + .no_match => {}, + + // default patterns don't use `!` + .negate_no_match => unreachable, + .negate_match => unreachable, + } + } + + // lastly, check each .npmignore/.gitignore from root directory to + // the current directory. + for (ignores) |ignore| { + var rel = entry_subpath; + if (ignore.has_rel_path) { + // trim parent directories up to the directory + // containing this ignore file + for (1..ignore.depth) |_| { + if (strings.indexOfChar(rel, '/')) |sep| { + rel = rel[sep + 1 ..]; + } + } + } + for (ignore.list) |pattern| { + if (pattern.dirs_only and entry.kind != .directory) continue; + + const match_path = if (pattern.rel_path) rel else entry_name; + switch (glob.matchImpl(pattern.glob, match_path)) { + .match => { + ignored = true; + ignore_pattern = pattern.glob; + ignore_kind = ignore.kind; + }, + .negate_no_match => ignored = false, + else => {}, + } + } + } + + return if (!ignored) + null + else + .{ + ignore_pattern, + ignore_kind, + }; + } + + const BufferedFileReader = std.io.BufferedReader(1024 * 512, File.Reader); + + fn pack( + ctx: *Context, + abs_package_json_path: stringZ, + comptime log_level: LogLevel, + ) PackError!void { + const manager = ctx.manager; + const json = switch (manager.workspace_package_json_cache.getWithPath(manager.allocator, manager.log, abs_package_json_path, .{ + .guess_indentation = true, + })) { + .read_err => |err| { + Output.err(err, "failed to read package.json: {s}", .{abs_package_json_path}); + Global.crash(); + }, + .parse_err => |err| { + Output.err(err, "failed to parse package.json: {s}", .{abs_package_json_path}); + switch (Output.enable_ansi_colors) { + inline else => |enable_ansi_colors| { + manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; + }, + } + Global.crash(); + }, + .entry => |entry| entry, + }; + + const package_name_expr: Expr = json.root.get("name") orelse return error.MissingPackageName; + const package_name = package_name_expr.asStringCloned(ctx.allocator) orelse return error.InvalidPackageName; + defer ctx.allocator.free(package_name); + if (package_name.len == 0) return error.InvalidPackageName; + + const package_version_expr: Expr = json.root.get("version") orelse return error.MissingPackageVersion; + const package_version = package_version_expr.asStringCloned(ctx.allocator) orelse return error.InvalidPackageVersion; + defer ctx.allocator.free(package_version); + if (package_version.len == 0) return error.InvalidPackageVersion; + + var this_bundler: bun.bundler.Bundler = undefined; + + _ = RunCommand.configureEnvForRun( + ctx.command_ctx, + &this_bundler, + manager.env, + manager.options.log_level != .silent, + false, + ) catch |err| { + switch (err) { + error.OutOfMemory => |oom| return oom, + else => { + Output.errGeneric("failed to run pack scripts due to error: {s}\n", .{@errorName(err)}); + Global.crash(); + }, + } + }; + + const abs_workspace_path: string = strings.withoutTrailingSlash(strings.withoutSuffixComptime(abs_package_json_path, "package.json")); + + const postpack_script: ?string = postpack_script: { + // --ignore-scripts + if (!manager.options.do.run_scripts) break :postpack_script null; + + const scripts = json.root.asProperty("scripts") orelse break :postpack_script null; + if (scripts.expr.data != .e_object) break :postpack_script null; + + if (scripts.expr.get("prepack")) |prepack_script| { + if (prepack_script.asString(ctx.allocator)) |prepack_script_str| { + _ = RunCommand.runPackageScriptForeground( + ctx.command_ctx, + ctx.allocator, + prepack_script_str, + "prepack", + abs_workspace_path, + this_bundler.env, + &.{}, + manager.options.log_level == .silent, + ctx.command_ctx.debug.use_system_shell, + ) catch |err| { + switch (err) { + error.MissingShell => { + Output.errGeneric("failed to find shell executable to run prepack script", .{}); + Global.crash(); + }, + error.OutOfMemory => |oom| return oom, + } + }; + } + } + + if (scripts.expr.get("prepare")) |prepare_script| { + if (prepare_script.asString(ctx.allocator)) |prepare_script_str| { + _ = RunCommand.runPackageScriptForeground( + ctx.command_ctx, + ctx.allocator, + prepare_script_str, + "prepare", + abs_workspace_path, + this_bundler.env, + &.{}, + manager.options.log_level == .silent, + ctx.command_ctx.debug.use_system_shell, + ) catch |err| { + switch (err) { + error.MissingShell => { + Output.errGeneric("failed to find shell executable to run prepare script", .{}); + Global.crash(); + }, + error.OutOfMemory => |oom| return oom, + } + }; + } + } + + if (scripts.expr.get("postpack")) |postpack| { + if (postpack.asString(ctx.allocator)) |postpack_str| { + break :postpack_script postpack_str; + } + } + + break :postpack_script null; + }; + + var root_dir = root_dir: { + var path_buf: PathBuffer = undefined; + @memcpy(path_buf[0..abs_workspace_path.len], abs_workspace_path); + path_buf[abs_workspace_path.len] = 0; + break :root_dir std.fs.openDirAbsoluteZ(path_buf[0..abs_workspace_path.len :0], .{ + .iterate = true, + }) catch |err| { + Output.err(err, "failed to open root directory: {s}\n", .{abs_workspace_path}); + Global.crash(); + }; + }; + defer root_dir.close(); + + ctx.bundled_deps = try getBundledDeps(ctx, json.root, "bundledDependencies") orelse + try getBundledDeps(ctx, json.root, "bundleDependencies") orelse + .{}; + + var pack_queue = pack_queue: { + if (json.root.get("files")) |files| { + files_error: { + if (files.asArray()) |_files_array| { + var includes: std.ArrayListUnmanaged(Pattern) = .{}; + defer includes.deinit(ctx.allocator); + + var files_array = _files_array; + while (files_array.next()) |files_entry| { + if (files_entry.asString(ctx.allocator)) |file_entry_str| { + const parsed = try Pattern.fromUTF8(ctx, file_entry_str) orelse continue; + try includes.append(ctx.allocator, parsed); + continue; + } + + break :files_error; + } + + break :pack_queue try iterateIncludedProjectTree( + ctx, + includes.items, + root_dir, + log_level, + ); + } + } + + Output.errGeneric("expected `files` to be an array of string values", .{}); + Global.crash(); + } + + // pack from project root + break :pack_queue try iterateProjectTree( + ctx, + root_dir, + log_level, + ); + }; + defer pack_queue.deinit(); + + var bundled_pack_queue = try iterateBundledDeps(ctx, root_dir, log_level); + defer bundled_pack_queue.deinit(); + + // +1 for package.json + ctx.stats.total_files = pack_queue.count() + bundled_pack_queue.count() + 1; + + if (manager.options.dry_run) { + // don't create the tarball, but run scripts if they exists + + printArchivedFilesAndPackages(ctx, root_dir, true, &pack_queue, 0); + + if (manager.options.pack_destination.len == 0) { + Output.pretty("\n{}\n", .{fmtTarballFilename(package_name, package_version)}); + } else { + var dest_buf: PathBuffer = undefined; + const abs_tarball_dest, _ = absTarballDestination(ctx, abs_workspace_path, package_name, package_version, &dest_buf); + Output.pretty("\n{s}\n", .{abs_tarball_dest}); + } + + ctx.printSummary(null, null, log_level); + + if (postpack_script) |postpack_script_str| { + _ = RunCommand.runPackageScriptForeground( + ctx.command_ctx, + ctx.allocator, + postpack_script_str, + "postpack", + abs_workspace_path, + manager.env, + &.{}, + manager.options.log_level == .silent, + ctx.command_ctx.debug.use_system_shell, + ) catch |err| { + switch (err) { + error.MissingShell => { + Output.errGeneric("failed to find shell executable to run postpack script", .{}); + Global.crash(); + }, + error.OutOfMemory => |oom| return oom, + } + }; + } + return; + } + + const bins = try getPackageBins(ctx, json.root); + defer for (bins) |bin| ctx.allocator.free(bin.path); + + var print_buf = std.ArrayList(u8).init(ctx.allocator); + defer print_buf.deinit(); + const print_buf_writer = print_buf.writer(); + + var archive = Archive.writeNew(); + + switch (archive.writeSetFormatPaxRestricted()) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to set archive format: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + switch (archive.writeSetCompressionGzip()) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to set archive compression to gzip: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + + // default is 9 + // https://github.com/npm/cli/blob/ec105f400281a5bfd17885de1ea3d54d0c231b27/node_modules/pacote/lib/util/tar-create-options.js#L12 + const compression_level = manager.options.pack_gzip_level orelse "9"; + try print_buf_writer.print("{s}\x00", .{compression_level}); + switch (archive.writeSetFilterOption(null, "compression-level", print_buf.items[0..compression_level.len :0])) { + .failed, .fatal, .warn => { + Output.errGeneric("compression level must be between 0 and 9, received {s}", .{compression_level}); + Global.crash(); + }, + else => {}, + } + print_buf.clearRetainingCapacity(); + + switch (archive.writeSetOptions("gzip:!timestamp")) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to unset gzip timestamp option: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + + var dest_buf: PathBuffer = undefined; + const abs_tarball_dest, const abs_tarball_dest_dir_end = absTarballDestination( + ctx, + abs_workspace_path, + package_name, + package_version, + &dest_buf, + ); + + { + // create the directory if it doesn't exist + const most_likely_a_slash = dest_buf[abs_tarball_dest_dir_end]; + dest_buf[abs_tarball_dest_dir_end] = 0; + const abs_tarball_dest_dir = dest_buf[0..abs_tarball_dest_dir_end :0]; + bun.makePath(std.fs.cwd(), abs_tarball_dest_dir) catch {}; + dest_buf[abs_tarball_dest_dir_end] = most_likely_a_slash; + } + + // TODO: experiment with `archive.writeOpenMemory()` + switch (archive.writeOpenFilename(abs_tarball_dest)) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to open tarball file destination: \"{s}\"", .{abs_tarball_dest}); + Global.crash(); + }, + else => {}, + } + + // append removed items from `pack_queue` with their file size + var pack_list: PackList = .{}; + defer pack_list.deinit(ctx.allocator); + + var read_buf: [8192]u8 = undefined; + const file_reader = try ctx.allocator.create(BufferedFileReader); + defer ctx.allocator.destroy(file_reader); + file_reader.* = .{ + .unbuffered_reader = undefined, + }; + + var entry = Archive.Entry.new2(archive); + + const package_json_size = archive_with_progress: { + var progress: if (log_level == .silent) void else Progress = if (comptime log_level == .silent) {} else .{}; + var node = if (comptime log_level == .silent) {} else node: { + progress.supports_ansi_escape_codes = Output.enable_ansi_colors; + var node: *Progress.Node = progress.start("", pack_queue.count() + bundled_pack_queue.count() + 1); + node.unit = " files"; + break :node node; + }; + defer if (comptime log_level != .silent) node.end(); + + entry, const edited_package_json_size = try editAndArchivePackageJSON(ctx, archive, entry, root_dir, json); + if (comptime log_level != .silent) node.completeOne(); + + while (pack_queue.removeOrNull()) |pathname| { + defer if (comptime log_level != .silent) node.completeOne(); + + const file = bun.sys.openat(bun.toFD(root_dir.fd), pathname, bun.O.RDONLY, 0).unwrap() catch |err| { + Output.err(err, "failed to open file: \"{s}\"", .{pathname}); + Global.crash(); + }; + + const fd = bun.sys.toLibUVOwnedFD(file, .open, .close_on_fail).unwrap() catch |err| { + Output.err(err, "failed to open file: \"{s}\"", .{pathname}); + Global.crash(); + }; + + defer _ = bun.sys.close(fd); + + const stat = bun.sys.sys_uv.fstat(fd).unwrap() catch |err| { + Output.err(err, "failed to stat file: \"{s}\"", .{pathname}); + Global.crash(); + }; + + try pack_list.append(ctx.allocator, .{ .subpath = pathname, .size = @intCast(stat.size) }); + + entry = try addArchiveEntry( + ctx, + fd, + stat, + pathname, + &read_buf, + file_reader, + archive, + entry, + &print_buf, + bins, + ); + } + + while (bundled_pack_queue.removeOrNull()) |pathname| { + defer if (comptime log_level != .silent) node.completeOne(); + + const file = File.openat(root_dir, pathname, bun.O.RDONLY, 0).unwrap() catch |err| { + Output.err(err, "failed to open file: \"{s}\"", .{pathname}); + Global.crash(); + }; + defer file.close(); + const stat = file.stat().unwrap() catch |err| { + Output.err(err, "failed to stat file: \"{}\"", .{file.handle}); + Global.crash(); + }; + + entry = try addArchiveEntry( + ctx, + file.handle, + stat, + pathname, + &read_buf, + file_reader, + archive, + entry, + &print_buf, + bins, + ); + } + + break :archive_with_progress edited_package_json_size; + }; + + entry.free(); + + switch (archive.writeClose()) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to close archive: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + + switch (archive.writeFree()) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to free archive: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + + var sha1_digest: sha.SHA1.Digest = undefined; + var sha512_digest: sha.SHA512.Digest = undefined; + + { + const tarball_file = File.open(abs_tarball_dest, bun.O.RDONLY, 0).unwrap() catch |err| { + Output.err(err, "failed to open tarball at: \"{s}\"", .{abs_tarball_dest}); + Global.crash(); + }; + defer tarball_file.close(); + + var sha1 = sha.SHA1.init(); + defer sha1.deinit(); + + var sha512 = sha.SHA512.init(); + defer sha512.deinit(); + + file_reader.* = .{ + .unbuffered_reader = tarball_file.reader(), + }; + + var size: usize = 0; + var read = file_reader.read(&read_buf) catch |err| { + Output.err(err, "failed to read tarball: \"{s}\"", .{abs_tarball_dest}); + Global.crash(); + }; + while (read > 0) { + sha1.update(read_buf[0..read]); + sha512.update(read_buf[0..read]); + size += read; + read = file_reader.read(&read_buf) catch |err| { + Output.err(err, "failed to read tarball: \"{s}\"", .{abs_tarball_dest}); + Global.crash(); + }; + } + + sha1.final(&sha1_digest); + sha512.final(&sha512_digest); + + ctx.stats.packed_size = size; + } + + printArchivedFilesAndPackages( + ctx, + root_dir, + false, + pack_list, + package_json_size, + ); + + if (manager.options.pack_destination.len == 0) { + Output.pretty("\n{}\n", .{fmtTarballFilename(package_name, package_version)}); + } else { + Output.pretty("\n{s}\n", .{abs_tarball_dest}); + } + + ctx.printSummary(sha1_digest, sha512_digest, log_level); + + if (postpack_script) |postpack_script_str| { + _ = RunCommand.runPackageScriptForeground( + ctx.command_ctx, + ctx.allocator, + postpack_script_str, + "postpack", + abs_workspace_path, + manager.env, + &.{}, + manager.options.log_level == .silent, + ctx.command_ctx.debug.use_system_shell, + ) catch |err| { + switch (err) { + error.MissingShell => { + Output.errGeneric("failed to find shell executable to run postpack script", .{}); + Global.crash(); + }, + error.OutOfMemory => |oom| return oom, + } + }; + } + } + + fn absTarballDestination( + ctx: *Context, + abs_workspace_path: string, + package_name: string, + package_version: string, + dest_buf: []u8, + ) struct { stringZ, usize } { + const tarball_destination_dir = bun.path.joinAbsStringBuf( + abs_workspace_path, + dest_buf, + &.{ctx.manager.options.pack_destination}, + .auto, + ); + + const tarball_name = std.fmt.bufPrint(dest_buf[strings.withoutTrailingSlash(tarball_destination_dir).len..], "/{}\x00", .{ + fmtTarballFilename(package_name, package_version), + }) catch { + Output.errGeneric("archive destination name too long: \"{s}/{}\"", .{ + strings.withoutTrailingSlash(tarball_destination_dir), + fmtTarballFilename(package_name, package_version), + }); + Global.crash(); + }; + + return .{ + dest_buf[0 .. strings.withoutTrailingSlash(tarball_destination_dir).len + tarball_name.len - 1 :0], + tarball_destination_dir.len, + }; + } + + fn fmtTarballFilename(package_name: string, package_version: string) TarballNameFormatter { + return .{ + .package_name = package_name, + .package_version = package_version, + }; + } + + const TarballNameFormatter = struct { + package_name: string, + package_version: string, + + pub fn format(this: TarballNameFormatter, comptime _: string, _: std.fmt.FormatOptions, writer: anytype) !void { + if (this.package_name[0] == '@') { + if (this.package_name.len > 1) { + if (strings.indexOfChar(this.package_name, '/')) |slash| { + return writer.print("{s}-{s}-{s}.tgz", .{ + this.package_name[1..][0 .. slash - 1], + this.package_name[slash + 1 ..], + this.package_version, + }); + } + } + + return writer.print("{s}-{s}.tgz", .{ + this.package_name[1..], + this.package_version, + }); + } + + return writer.print("{s}-{s}.tgz", .{ + this.package_name, + this.package_version, + }); + } + }; + + fn editAndArchivePackageJSON( + ctx: *Context, + archive: *Archive, + entry: *Archive.Entry, + root_dir: std.fs.Dir, + json: *PackageManager.WorkspacePackageJSONCache.MapEntry, + ) OOM!struct { *Archive.Entry, usize } { + const edited_package_json = try editRootPackageJSON(ctx, json); + + const stat = bun.sys.fstatat(bun.toFD(root_dir), "package.json").unwrap() catch |err| { + Output.err(err, "failed to stat package.json", .{}); + Global.crash(); + }; + + entry.setPathname(package_prefix ++ "package.json"); + entry.setSize(@intCast(edited_package_json.len)); + // https://github.com/libarchive/libarchive/blob/898dc8319355b7e985f68a9819f182aaed61b53a/libarchive/archive_entry.h#L185 + entry.setFiletype(0o100000); + entry.setPerm(@intCast(stat.mode)); + // '1985-10-26T08:15:00.000Z' + // https://github.com/npm/cli/blob/ec105f400281a5bfd17885de1ea3d54d0c231b27/node_modules/pacote/lib/util/tar-create-options.js#L28 + entry.setMtime(499162500, 0); + + switch (archive.writeHeader(entry)) { + .failed, .fatal, .warn => { + Output.errGeneric("failed to write tarball header: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + + ctx.stats.unpacked_size += @intCast(archive.writeData(edited_package_json)); + + return .{ entry.clear(), edited_package_json.len }; + } + + fn addArchiveEntry( + ctx: *Context, + file: FileDescriptor, + stat: bun.Stat, + filename: stringZ, + read_buf: []u8, + file_reader: *BufferedFileReader, + archive: *Archive, + entry: *Archive.Entry, + print_buf: *std.ArrayList(u8), + bins: []const BinInfo, + ) OOM!*Archive.Entry { + const print_buf_writer = print_buf.writer(); + + try print_buf_writer.print("{s}{s}\x00", .{ package_prefix, filename }); + const pathname = print_buf.items[0 .. package_prefix.len + filename.len :0]; + if (comptime Environment.isWindows) + entry.setPathnameUtf8(pathname) + else + entry.setPathname(pathname); + print_buf_writer.context.clearRetainingCapacity(); + + entry.setSize(@intCast(stat.size)); + + // https://github.com/libarchive/libarchive/blob/898dc8319355b7e985f68a9819f182aaed61b53a/libarchive/archive_entry.h#L185 + entry.setFiletype(0o100000); + + var perm: bun.Mode = @intCast(stat.mode); + // https://github.com/npm/cli/blob/ec105f400281a5bfd17885de1ea3d54d0c231b27/node_modules/pacote/lib/util/tar-create-options.js#L20 + if (comptime !Environment.isWindows) { + // on windows we create a shim executable. the bin file permissions + // do not need to change + if (isPackageBin(bins, filename)) perm |= 0o111; + } + entry.setPerm(@intCast(perm)); + + // '1985-10-26T08:15:00.000Z' + // https://github.com/npm/cli/blob/ec105f400281a5bfd17885de1ea3d54d0c231b27/node_modules/pacote/lib/util/tar-create-options.js#L28 + entry.setMtime(499162500, 0); + + switch (archive.writeHeader(entry)) { + .failed, .fatal => { + Output.errGeneric("failed to write tarball header: {s}", .{archive.errorString()}); + Global.crash(); + }, + else => {}, + } + + file_reader.* = .{ + .unbuffered_reader = File.from(file).reader(), + }; + + var read = file_reader.read(read_buf) catch |err| { + Output.err(err, "failed to read file: \"{s}\"", .{filename}); + Global.crash(); + }; + while (read > 0) { + ctx.stats.unpacked_size += @intCast(archive.writeData(read_buf[0..read])); + read = file_reader.read(read_buf) catch |err| { + Output.err(err, "failed to read file: \"{s}\"", .{filename}); + Global.crash(); + }; + } + + return entry.clear(); + } + + /// Strip workspace protocols from dependency versions then + /// returns the printed json + fn editRootPackageJSON( + ctx: *Context, + json: *PackageManager.WorkspacePackageJSONCache.MapEntry, + ) OOM!string { + for ([_]string{ + "dependencies", + "devDependencies", + "peerDependencies", + "optionalDependencies", + }) |dependency_group| { + if (json.root.get(dependency_group)) |dependencies_expr| { + switch (dependencies_expr.data) { + .e_object => |dependencies| { + for (dependencies.properties.slice()) |*dependency| { + if (dependency.key == null) continue; + if (dependency.value == null) continue; + + const package_spec = dependency.value.?.asString(ctx.allocator) orelse continue; + if (strings.withoutPrefixIfPossibleComptime(package_spec, "workspace:")) |without_workspace_protocol| { + + // TODO: make semver parsing more strict. `^`, `~` are not valid + // const parsed = Semver.Version.parseUTF8(without_workspace_protocol); + // if (parsed.valid) { + // dependency.value = Expr.allocate( + // ctx.manager.allocator, + // E.String, + // .{ + // .data = without_workspace_protocol, + // }, + // .{}, + // ); + // continue; + // } + + if (without_workspace_protocol.len == 1) { + // TODO: this might be too strict + const c = without_workspace_protocol[0]; + if (c == '^' or c == '~' or c == '*') { + const dependency_name = dependency.key.?.asString(ctx.allocator) orelse { + Output.errGeneric("expected string value for dependency name in \"{s}\"", .{ + dependency_group, + }); + Global.crash(); + }; + + failed_to_resolve: { + // find the current workspace version and append to package spec without `workspace:` + const lockfile = ctx.lockfile orelse break :failed_to_resolve; + + const workspace_version = lockfile.workspace_versions.get(Semver.String.Builder.stringHash(dependency_name)) orelse break :failed_to_resolve; + + dependency.value = Expr.allocate( + ctx.manager.allocator, + E.String, + .{ + .data = try std.fmt.allocPrint(ctx.allocator, "{s}{}", .{ + switch (c) { + '^' => "^", + '~' => "~", + '*' => "", + else => unreachable, + }, + workspace_version.fmt(lockfile.buffers.string_bytes.items), + }), + }, + .{}, + ); + + continue; + } + + // only produce this error only when we need to get the workspace version + Output.errGeneric("Failed to resolve workspace version for \"{s}\" in `{s}`. Run `bun install` and try again.", .{ + dependency_name, + dependency_group, + }); + Global.crash(); + } + } + + dependency.value = Expr.allocate( + ctx.manager.allocator, + E.String, + .{ + .data = try ctx.allocator.dupe(u8, without_workspace_protocol), + }, + .{}, + ); + } + } + }, + else => {}, + } + } + } + + const has_trailing_newline = json.source.contents.len > 0 and json.source.contents[json.source.contents.len - 1] == '\n'; + var buffer_writer = try js_printer.BufferWriter.init(ctx.allocator); + try buffer_writer.buffer.list.ensureTotalCapacity(ctx.allocator, json.source.contents.len + 1); + buffer_writer.append_newline = has_trailing_newline; + var package_json_writer = js_printer.BufferPrinter.init(buffer_writer); + + const written = js_printer.printJSON( + @TypeOf(&package_json_writer), + &package_json_writer, + json.root, + + // shouldn't be used + &json.source, + .{ + .indent = json.indentation, + }, + ) catch |err| { + return switch (err) { + error.OutOfMemory => |oom| oom, + else => { + Output.errGeneric("failed to print edited package.json: {s}", .{@errorName(err)}); + Global.crash(); + }, + }; + }; + _ = written; + + return package_json_writer.ctx.writtenWithoutTrailingZero(); + } + + /// A pattern used to ignore or include + /// files in the project tree. Might come + /// from .npmignore, .gitignore, or `files` + /// in package.json + const Pattern = struct { + glob: []const u32, + /// beginning or middle slash (leading slash was trimmed) + rel_path: bool, + // can only match directories (had an ending slash, also trimmed) + dirs_only: bool, + + @"leading **/": bool, + + pub fn fromUTF8(ctx: *Context, pattern: string) OOM!?Pattern { + var remain = pattern; + var @"has leading **/, (could start with '!')" = false; + const has_leading_or_middle_slash, const has_trailing_slash, const add_negate = check_slashes: { + const before_length = remain.len; + + // strip `!` and add one if any existed + while (remain.len > 0 and remain[0] == '!') remain = remain[1..]; + + const skipped_negate = before_length != remain.len; + + if (remain.len == 0) return null; + + // `**/foo` matches the same as `foo` + if (strings.hasPrefixComptime(remain, "**/")) { + remain = remain["**/".len..]; + if (remain.len == 0) return null; + @"has leading **/, (could start with '!')" = true; + } + + const trailing_slash = remain[remain.len - 1] == '/'; + if (trailing_slash) { + // trim trailing slash + remain = remain[0 .. remain.len - 1]; + if (remain.len == 0) return null; + } + + var leading_or_middle_slash = remain[0] == '/'; + if (!leading_or_middle_slash) { + // check for middle slash + if (strings.indexOfChar(remain, '/')) |slash_index| { + leading_or_middle_slash = slash_index != remain.len - 1; + } + } else { + // trim leading slash + remain = remain[1..]; + if (remain.len == 0) return null; + } + + break :check_slashes .{ leading_or_middle_slash, trailing_slash, skipped_negate }; + }; + + const length = bun.simdutf.length.utf32.from.utf8.le(remain) + @intFromBool(add_negate); + const buf = try ctx.allocator.alloc(u32, length); + const result = bun.simdutf.convert.utf8.to.utf32.with_errors.le(remain, buf[@intFromBool(add_negate)..]); + if (!result.isSuccessful()) { + ctx.allocator.free(buf); + return null; + } + + if (add_negate) { + buf[0] = '!'; + } + + return .{ + .glob = buf[0 .. result.count + @intFromBool(add_negate)], + .rel_path = has_leading_or_middle_slash, + .@"leading **/" = @"has leading **/, (could start with '!')", + .dirs_only = has_trailing_slash, + }; + } + + pub fn deinit(this: Pattern, allocator: std.mem.Allocator) void { + allocator.free(this.glob); + } + }; + + pub const IgnorePatterns = struct { + list: []const Pattern, + kind: Kind, + depth: usize, + + // At least one of the patterns has a leading + // or middle slash. A relative path will need to + // be created + has_rel_path: bool, + + pub const Kind = enum { + default, + @".npmignore", + @".gitignore", + }; + + pub const List = std.ArrayListUnmanaged(IgnorePatterns); + + fn ignoreFileFail(dir: std.fs.Dir, ignore_kind: Kind, reason: enum { read, open }, err: anyerror) noreturn { + var buf: PathBuffer = undefined; + const dir_path = bun.getFdPath(dir, &buf) catch ""; + Output.err(err, "failed to {s} {s} at: \"{s}{s}{s}\"", .{ + @tagName(reason), + @tagName(ignore_kind), + strings.withoutTrailingSlash(dir_path), + std.fs.path.sep_str, + @tagName(ignore_kind), + }); + Global.crash(); + } + + fn trimTrailingSpaces(line: string) string { + // TODO: copy this function + // https://github.com/git/git/blob/17d4b10aea6bda2027047a0e3548a6f8ad667dde/dir.c#L986 + return line; + } + + fn maybeTrimLeadingSpaces(line: string) string { + // npm will trim, git will not + return line; + } + + // ignore files are always ignored, don't need to worry about opening or reading twice + pub fn readFromDisk(ctx: *Context, dir: std.fs.Dir, dir_depth: usize) OOM!?IgnorePatterns { + var patterns: std.ArrayListUnmanaged(Pattern) = .{}; + errdefer patterns.deinit(ctx.allocator); + + var ignore_kind: Kind = .@".npmignore"; + + const ignore_file = dir.openFileZ(".npmignore", .{}) catch |err| ignore_file: { + if (err != error.FileNotFound) { + // Crash if the file exists and fails to open. Don't want to create a tarball + // with files you want to ignore. + ignoreFileFail(dir, ignore_kind, .open, err); + } + ignore_kind = .@".gitignore"; + break :ignore_file dir.openFileZ(".gitignore", .{}) catch |err2| { + if (err2 != error.FileNotFound) { + ignoreFileFail(dir, ignore_kind, .open, err2); + } + + return null; + }; + }; + defer ignore_file.close(); + + const contents = File.from(ignore_file).readToEnd(ctx.allocator).unwrap() catch |err| { + ignoreFileFail(dir, ignore_kind, .read, err); + }; + defer ctx.allocator.free(contents); + + var has_rel_path = false; + + var iter = std.mem.tokenizeScalar(u8, contents, '\n'); + while (iter.next()) |line| { + if (line.len == 0) continue; + + // comment + if (line[0] == '#') continue; + + const trimmed = trimmed: { + var remain = line; + if (remain[remain.len - 1] == '\r') { + remain = remain[0 .. remain.len - 1]; + } + + break :trimmed trimTrailingSpaces(remain); + }; + + if (trimmed.len == 0) continue; + + const parsed = try Pattern.fromUTF8(ctx, trimmed) orelse continue; + try patterns.append(ctx.allocator, parsed); + + has_rel_path = has_rel_path or parsed.rel_path; + } + + if (patterns.items.len == 0) return null; + + return .{ + .list = patterns.items, + .kind = ignore_kind, + .depth = dir_depth, + .has_rel_path = has_rel_path, + }; + } + + pub fn deinit(this: *const IgnorePatterns, allocator: std.mem.Allocator) void { + for (this.list) |pattern_info| { + allocator.free(pattern_info.glob); + } + allocator.free(this.list); + } + }; + + fn printArchivedFilesAndPackages( + ctx: *Context, + root_dir: std.fs.Dir, + comptime is_dry_run: bool, + pack_list: if (is_dry_run) *PackQueue else PackList, + package_json_len: usize, + ) void { + if (ctx.manager.options.log_level == .silent) return; + const packed_fmt = "packed {} {s}"; + + if (comptime is_dry_run) { + const package_json_stat = bun.sys.fstatat(bun.toFD(root_dir), "package.json").unwrap() catch |err| { + Output.err(err, "failed to stat package.json", .{}); + Global.crash(); + }; + + ctx.stats.unpacked_size += @intCast(package_json_stat.size); + + Output.prettyln("\n" ++ packed_fmt, .{ + bun.fmt.size(package_json_stat.size, .{ .space_between_number_and_unit = false }), + "package.json", + }); + + while (pack_list.removeOrNull()) |filename| { + const stat = bun.sys.fstatat(bun.toFD(root_dir), filename).unwrap() catch |err| { + Output.err(err, "failed to stat file: \"{s}\"", .{filename}); + Global.crash(); + }; + + ctx.stats.unpacked_size += @intCast(stat.size); + + Output.prettyln(packed_fmt, .{ + bun.fmt.size(stat.size, .{ .space_between_number_and_unit = false }), + filename, + }); + } + + for (ctx.bundled_deps.items) |dep| { + if (!dep.was_packed) continue; + Output.prettyln("bundled {s}", .{dep.name}); + } + + Output.flush(); + return; + } + + Output.prettyln("\n" ++ packed_fmt, .{ + bun.fmt.size(package_json_len, .{ .space_between_number_and_unit = false }), + "package.json", + }); + + for (pack_list.items) |entry| { + Output.prettyln(packed_fmt, .{ + bun.fmt.size(entry.size, .{ .space_between_number_and_unit = false }), + entry.subpath, + }); + } + + for (ctx.bundled_deps.items) |dep| { + if (!dep.was_packed) continue; + Output.prettyln("bundled {s}", .{dep.name}); + } + + Output.flush(); + } +}; + +pub const bindings = struct { + const JSC = bun.JSC; + const JSValue = JSC.JSValue; + const JSGlobalObject = JSC.JSGlobalObject; + const CallFrame = JSC.CallFrame; + const ZigString = JSC.ZigString; + const String = bun.String; + const JSArray = JSC.JSArray; + const JSObject = JSC.JSObject; + + // pub fn generate(global: *JSGlobalObject) JSValue { + // const obj = JSValue.createEmptyObject(global, 1); + + // const readTarEntries = ZigString.static("readTarEntries"); + // obj.put(global, readTarEntries, JSC.createCallback(global, readTarEntries, 1, jsReadTarEntries)); + // return obj; + // } + + pub fn jsReadTarball(global: *JSGlobalObject, callFrame: *CallFrame) JSValue { + const args = callFrame.arguments(1).slice(); + if (args.len < 1 or !args[0].isString()) { + global.throw("expected tarball path string argument", .{}); + return .zero; + } + + const tarball_path_str = args[0].toBunString(global); + defer tarball_path_str.deref(); + + const tarball_path = tarball_path_str.toUTF8(bun.default_allocator); + defer tarball_path.deinit(); + + const tarball_file = File.from(std.fs.openFileAbsolute(tarball_path.slice(), .{}) catch |err| { + global.throw("failed to open tarball file \"{s}\": {s}", .{ tarball_path.slice(), @errorName(err) }); + return .zero; + }); + defer tarball_file.close(); + + const tarball = tarball_file.readToEnd(bun.default_allocator).unwrap() catch |err| { + global.throw("failed to read tarball contents \"{s}\": {s}", .{ tarball_path.slice(), @errorName(err) }); + return .zero; + }; + defer bun.default_allocator.free(tarball); + + var sha1_digest: sha.SHA1.Digest = undefined; + var sha1 = sha.SHA1.init(); + defer sha1.deinit(); + sha1.update(tarball); + sha1.final(&sha1_digest); + const shasum_str = String.createFormat("{s}", .{bun.fmt.bytesToHex(sha1_digest, .lower)}) catch bun.outOfMemory(); + + var sha512_digest: sha.SHA512.Digest = undefined; + var sha512 = sha.SHA512.init(); + defer sha512.deinit(); + sha512.update(tarball); + sha512.final(&sha512_digest); + var base64_buf: [std.base64.standard.Encoder.calcSize(sha.SHA512.digest)]u8 = undefined; + const encode_count = bun.simdutf.base64.encode(&sha512_digest, &base64_buf, false); + const integrity_str = String.createUTF8(base64_buf[0..encode_count]); + + const EntryInfo = struct { + pathname: String, + kind: String, + perm: bun.Mode, + size: ?usize = null, + contents: ?String = null, + }; + var entries_info = std.ArrayList(EntryInfo).init(bun.default_allocator); + defer entries_info.deinit(); + + const archive = libarchive.archive_read_new(); + defer { + _ = libarchive.archive_read_close(archive); + _ = libarchive.archive_read_free(archive); + } + + _ = libarchive.archive_read_support_format_tar(archive); + _ = libarchive.archive_read_support_format_gnutar(archive); + _ = libarchive.archive_read_support_compression_gzip(archive); + + _ = libarchive.archive_read_set_options(archive, "read_concatenated_archives"); + + _ = libarchive.archive_read_open_memory(archive, tarball.ptr, tarball.len); + + var archive_entry: *libarchive.archive_entry = undefined; + + var header_status: Archive.Result = @enumFromInt(libarchive.archive_read_next_header(archive, &archive_entry)); + + var read_buf = std.ArrayList(u8).init(bun.default_allocator); + defer read_buf.deinit(); + + while (header_status != .eof) : (header_status = @enumFromInt(libarchive.archive_read_next_header(archive, &archive_entry))) { + switch (header_status) { + .eof => unreachable, + .retry => continue, + .failed, .fatal => { + global.throw("failed to read next archive header: {s}", .{Archive.errorString(@ptrCast(archive))}); + return .zero; + }, + else => { + const pathname = std.mem.sliceTo(libarchive.archive_entry_pathname(archive_entry), 0); + const kind = bun.C.kindFromMode(libarchive.archive_entry_filetype(archive_entry)); + const perm = libarchive.archive_entry_perm(archive_entry); + + var entry_info: EntryInfo = .{ + .pathname = String.createUTF8(pathname), + .kind = String.static(@tagName(kind)), + .perm = perm, + }; + + if (kind == .file) { + const size: usize = @intCast(libarchive.archive_entry_size(archive_entry)); + read_buf.ensureTotalCapacity(size) catch bun.outOfMemory(); + defer read_buf.clearRetainingCapacity(); + + const read = libarchive.archive_read_data(archive, read_buf.items.ptr, size); + if (read < 0) { + global.throw("failed to read archive entry \"{}\": {s}", .{ + bun.fmt.fmtPath(u8, pathname, .{}), + Archive.errorString(@ptrCast(archive)), + }); + return .zero; + } + read_buf.items.len = @intCast(read); + entry_info.contents = String.createUTF8(read_buf.items); + } + + entries_info.append(entry_info) catch bun.outOfMemory(); + }, + } + } + + const entries = JSArray.createEmpty(global, entries_info.items.len); + + for (entries_info.items, 0..) |entry, i| { + const obj = JSValue.createEmptyObject(global, 4); + obj.put(global, "pathname", entry.pathname.toJS(global)); + obj.put(global, "kind", entry.kind.toJS(global)); + obj.put(global, "perm", JSValue.jsNumber(entry.perm)); + if (entry.contents) |contents| { + obj.put(global, "contents", contents.toJS(global)); + } + entries.putIndex(global, @intCast(i), obj); + } + + const result = JSValue.createEmptyObject(global, 2); + result.put(global, "entries", entries); + result.put(global, "size", JSValue.jsNumber(tarball.len)); + result.put(global, "shasum", shasum_str.toJS(global)); + result.put(global, "integrity", integrity_str.toJS(global)); + + return result; + } +}; diff --git a/src/cli/package_manager_command.zig b/src/cli/package_manager_command.zig index 55631722ef94b..90c2cb11d3e78 100644 --- a/src/cli/package_manager_command.zig +++ b/src/cli/package_manager_command.zig @@ -24,6 +24,7 @@ const UntrustedCommand = @import("./pm_trusted_command.zig").UntrustedCommand; const TrustCommand = @import("./pm_trusted_command.zig").TrustCommand; const DefaultTrustedCommand = @import("./pm_trusted_command.zig").DefaultTrustedCommand; const Environment = bun.Environment; +const PackCommand = @import("./pack_command.zig").PackCommand; const ByName = struct { dependencies: []const Dependency, @@ -61,7 +62,9 @@ pub const PackageManagerCommand = struct { @memcpy(lockfile_buffer[0..lockfile_.len], lockfile_); lockfile_buffer[lockfile_.len] = 0; const lockfile = lockfile_buffer[0..lockfile_.len :0]; - var pm = try PackageManager.init(ctx, PackageManager.Subcommand.pm); + const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .pm); + var pm, const cwd = try PackageManager.init(ctx, cli, PackageManager.Subcommand.pm); + defer ctx.allocator.free(cwd); const load_lockfile = pm.lockfile.loadFromDisk(pm, ctx.allocator, ctx.log, lockfile, true); handleLoadLockfileErrors(load_lockfile, pm); @@ -97,6 +100,11 @@ pub const PackageManagerCommand = struct { Output.prettyln( \\bun pm: Package manager utilities \\ + \\ bun pm pack create a tarball of the current workspace + \\ --dry-run do everything except for writing the tarball to disk + \\ --destination the directory the tarball will be saved in + \\ --ignore-scripts don't run pre/postpack and prepare scripts + \\ --gzip-level specify a custom compression level for gzip (0-9, default is 9) \\ bun pm bin print the path to bin folder \\ -g print the global path to bin folder \\ bun pm ls list the dependency tree according to the current lockfile @@ -120,8 +128,8 @@ pub const PackageManagerCommand = struct { pub fn exec(ctx: Command.Context) !void { var args = try std.process.argsAlloc(ctx.allocator); args = args[1..]; - - var pm = PackageManager.init(ctx, PackageManager.Subcommand.pm) catch |err| { + const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .pm); + var pm, const cwd = PackageManager.init(ctx, cli, PackageManager.Subcommand.pm) catch |err| { if (err == error.MissingPackageJSON) { var cwd_buf: bun.PathBuffer = undefined; if (bun.getcwd(&cwd_buf)) |cwd| { @@ -134,13 +142,17 @@ pub const PackageManagerCommand = struct { } return err; }; + defer ctx.allocator.free(cwd); const subcommand = getSubcommand(&pm.options.positionals); if (pm.options.global) { try pm.setupGlobalDir(ctx); } - if (strings.eqlComptime(subcommand, "bin")) { + if (strings.eqlComptime(subcommand, "pack")) { + try PackCommand.execWithManager(ctx, pm); + Global.exit(0); + } else if (strings.eqlComptime(subcommand, "bin")) { const output_path = Path.joinAbs(Fs.FileSystem.instance.top_level_dir, .auto, bun.asByteSlice(pm.options.bin_path)); Output.prettyln("{s}", .{output_path}); if (Output.stdout_descriptor_type == .terminal) { diff --git a/src/cli/pm_trusted_command.zig b/src/cli/pm_trusted_command.zig index 8faad13b93fde..159aad49b8877 100644 --- a/src/cli/pm_trusted_command.zig +++ b/src/cli/pm_trusted_command.zig @@ -172,7 +172,7 @@ pub const TrustCommand = struct { fn printErrorZeroUntrustedDependenciesFound(trust_all: bool, packages_to_trust: []const string) void { Output.print("\n", .{}); if (trust_all) { - Output.errGeneric("0 scripts ran. This means all dependencies are already trusted or non have scripts.", .{}); + Output.errGeneric("0 scripts ran. This means all dependencies are already trusted or none have scripts.", .{}); } else { Output.errGeneric("0 scripts ran. The following packages are already trusted, don't have scripts to run, or don't exist:\n\n", .{}); for (packages_to_trust) |arg| { @@ -239,7 +239,7 @@ pub const TrustCommand = struct { try abs_node_modules_path.appendSlice(ctx.allocator, top_level_without_trailing_slash); try abs_node_modules_path.append(ctx.allocator, std.fs.path.sep); - var package_names_to_add: std.StringArrayHashMapUnmanaged(void) = .{}; + var package_names_to_add: bun.StringArrayHashMapUnmanaged(void) = .{}; var scripts_at_depth: std.AutoArrayHashMapUnmanaged(usize, std.ArrayListUnmanaged(struct { package_id: PackageID, scripts_list: Lockfile.Package.Scripts.List, @@ -264,7 +264,7 @@ pub const TrustCommand = struct { const alias = dep.name.slice(buf); const package_id = pm.lockfile.buffers.resolutions.items[dep_id]; if (comptime Environment.allow_assert) { - bun.assert(package_id != Install.invalid_package_id); + bun.assertWithLocation(package_id != Install.invalid_package_id, @src()); } const resolution = &resolutions[package_id]; var package_scripts = scripts[package_id]; @@ -329,14 +329,9 @@ pub const TrustCommand = struct { pm.scripts_node = &scripts_node; } - var depth = scripts_at_depth.count(); - while (depth > 0) { - depth -= 1; - const _entry = scripts_at_depth.get(depth); - if (comptime bun.Environment.allow_assert) { - bun.assert(_entry != null); - } - if (_entry) |entry| { + { + var iter = std.mem.reverseIterator(scripts_at_depth.values()); + while (iter.next()) |entry| { for (entry.items) |info| { if (info.skip) continue; @@ -387,7 +382,7 @@ pub const TrustCommand = struct { // now add the package names to lockfile.trustedDependencies and package.json `trustedDependencies` const names = package_names_to_add.keys(); if (comptime Environment.allow_assert) { - bun.assert(names.len > 0); + bun.assertWithLocation(names.len > 0, @src()); } // could be null if these are the first packages to be trusted @@ -399,10 +394,9 @@ pub const TrustCommand = struct { Output.print("\n", .{}); - depth = scripts_at_depth.count(); - while (depth > 0) { - depth -= 1; - if (scripts_at_depth.get(depth)) |entry| { + { + var iter = std.mem.reverseIterator(scripts_at_depth.values()); + while (iter.next()) |entry| { for (entry.items) |info| { const resolution = pm.lockfile.packages.items(.resolution)[info.package_id]; if (info.skip) { @@ -431,7 +425,7 @@ pub const TrustCommand = struct { buffer_writer.append_newline = package_json_contents.len > 0 and package_json_contents[package_json_contents.len - 1] == '\n'; var package_json_writer = bun.js_printer.BufferPrinter.init(buffer_writer); - _ = bun.js_printer.printJSON(@TypeOf(&package_json_writer), &package_json_writer, package_json, &package_json_source) catch |err| { + _ = bun.js_printer.printJSON(@TypeOf(&package_json_writer), &package_json_writer, package_json, &package_json_source, .{}) catch |err| { Output.errGeneric("failed to print package.json: {s}", .{@errorName(err)}); Global.crash(); }; @@ -443,7 +437,7 @@ pub const TrustCommand = struct { pm.root_package_json_file.close(); if (comptime Environment.allow_assert) { - bun.assert(total_scripts_ran > 0); + bun.assertWithLocation(total_scripts_ran > 0, @src()); } Output.pretty(" {d} script{s} ran across {d} package{s} ", .{ diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index f873d334ea59b..84b62dfe6d07f 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -13,6 +13,7 @@ const std = @import("std"); const uws = bun.uws; const JSC = bun.JSC; const WaiterThread = JSC.Subprocess.WaiterThread; +const OOM = bun.OOM; const lex = bun.js_lexer; const logger = bun.logger; @@ -139,7 +140,7 @@ pub const RunCommand = struct { pub inline fn replacePackageManagerRun( copy_script: *std.ArrayList(u8), script: string, - ) !void { + ) OOM!void { var entry_i: usize = 0; var delimiter: u8 = ' '; @@ -265,7 +266,7 @@ pub const RunCommand = struct { const log = Output.scoped(.RUN, false); - fn runPackageScriptForeground( + pub fn runPackageScriptForeground( ctx: Command.Context, allocator: std.mem.Allocator, original_script: string, @@ -275,7 +276,7 @@ pub const RunCommand = struct { passthrough: []const string, silent: bool, use_system_shell: bool, - ) !bool { + ) !void { const shell_bin = findShell(env.get("PATH") orelse "", cwd) orelse return error.MissingShell; const script = original_script; @@ -307,12 +308,12 @@ pub const RunCommand = struct { combined_script = combined_script_buf; } - if (!use_system_shell) { - if (!silent) { - Output.prettyErrorln("$ {s}", .{combined_script}); - Output.flush(); - } + if (!silent) { + Output.prettyErrorln("$ {s}", .{combined_script}); + Output.flush(); + } + if (!use_system_shell) { const mini = bun.JSC.MiniEventLoop.initGlobal(env); const code = bun.shell.Interpreter.initAndRunFromSource(ctx, mini, name, combined_script) catch |err| { if (!silent) { @@ -328,10 +329,10 @@ pub const RunCommand = struct { Output.flush(); } - Global.exitWide(code); + Global.exit(code); } - return true; + return; } const argv = [_]string{ @@ -340,10 +341,11 @@ pub const RunCommand = struct { combined_script, }; - if (!silent) { - Output.prettyErrorln("$ {s}", .{combined_script}); - Output.flush(); - } + const ipc_fd = if (!Environment.isWindows) blk: { + const node_ipc_fd = bun.getenvZ("NODE_CHANNEL_FD") orelse break :blk null; + const fd = std.fmt.parseInt(u32, node_ipc_fd, 10) catch break :blk null; + break :blk bun.toFD(@as(i32, @intCast(fd))); + } else null; // TODO: implement on Windows const spawn_result = switch ((bun.spawnSync(&.{ .argv = &argv, @@ -357,6 +359,7 @@ pub const RunCommand = struct { .stderr = .inherit, .stdout = .inherit, .stdin = .inherit, + .ipc = ipc_fd, .windows = if (Environment.isWindows) .{ .loop = JSC.EventLoopHandle.init(JSC.MiniEventLoop.initGlobal(env)), @@ -367,7 +370,7 @@ pub const RunCommand = struct { } Output.flush(); - return true; + return; })) { .err => |err| { if (!silent) { @@ -375,7 +378,7 @@ pub const RunCommand = struct { } Output.flush(); - return true; + return; }, .result => |result| result, }; @@ -414,13 +417,13 @@ pub const RunCommand = struct { } Output.flush(); - return true; + return; }, else => {}, } - return true; + return; } /// When printing error messages from 'bun run', attribute bun overridden node.js to bun @@ -578,8 +581,7 @@ pub const RunCommand = struct { }); } - Output.flush(); - Global.raiseIgnoringPanicHandler(@intFromEnum(signal)); + Global.raiseIgnoringPanicHandler(signal); }, .exited => |exit_code| { @@ -592,8 +594,7 @@ pub const RunCommand = struct { }); } - Output.flush(); - Global.raiseIgnoringPanicHandler(@intFromEnum(exit_code.signal)); + Global.raiseIgnoringPanicHandler(exit_code.signal); } const code = exit_code.code; @@ -883,7 +884,7 @@ pub const RunCommand = struct { // the use of npm/? is copying yarn // e.g. // > "yarn/1.22.4 npm/? node/v12.16.3 darwin x64", - "bun/" ++ Global.package_json_version ++ " npm/? node/v22.2.0 " ++ Global.os_name ++ " " ++ Global.arch_name, + "bun/" ++ Global.package_json_version ++ " npm/? node/v" ++ Environment.reported_nodejs_version ++ " " ++ Global.os_name ++ " " ++ Global.arch_name, ) catch unreachable; if (this_bundler.env.get("npm_execpath") == null) { @@ -1443,7 +1444,7 @@ pub const RunCommand = struct { defer ctx.allocator.free(temp_script_buffer); if (scripts.get(temp_script_buffer[1..])) |prescript| { - if (!try runPackageScriptForeground( + try runPackageScriptForeground( ctx, ctx.allocator, prescript, @@ -1453,12 +1454,10 @@ pub const RunCommand = struct { &.{}, ctx.debug.silent, ctx.debug.use_system_shell, - )) { - return false; - } + ); } - if (!try runPackageScriptForeground( + try runPackageScriptForeground( ctx, ctx.allocator, script_content, @@ -1468,12 +1467,12 @@ pub const RunCommand = struct { passthrough, ctx.debug.silent, ctx.debug.use_system_shell, - )) return false; + ); temp_script_buffer[0.."post".len].* = "post".*; if (scripts.get(temp_script_buffer)) |postscript| { - if (!try runPackageScriptForeground( + try runPackageScriptForeground( ctx, ctx.allocator, postscript, @@ -1483,9 +1482,7 @@ pub const RunCommand = struct { &.{}, ctx.debug.silent, ctx.debug.use_system_shell, - )) { - return false; - } + ); } return true; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index e02df273f0f1d..a6ccf6f4ad81f 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -42,7 +42,7 @@ const jest = JSC.Jest; const TestRunner = JSC.Jest.TestRunner; const Snapshots = JSC.Snapshot.Snapshots; const Test = TestRunner.Test; - +const CodeCoverageReport = bun.sourcemap.CodeCoverageReport; const uws = bun.uws; fn fmtStatusTextLine(comptime status: @Type(.EnumLiteral), comptime emoji_or_color: bool) []const u8 { @@ -271,23 +271,17 @@ pub const CommandLineReporter = struct { Output.printStartEnd(bun.start_time, std.time.nanoTimestamp()); } - pub fn printCodeCoverage(this: *CommandLineReporter, vm: *JSC.VirtualMachine, opts: *TestCommand.CodeCoverageOptions, comptime enable_ansi_colors: bool) !void { - const trace = bun.tracy.traceNamed(@src(), "TestCommand.printCodeCoverage"); - defer trace.end(); + pub fn generateCodeCoverage(this: *CommandLineReporter, vm: *JSC.VirtualMachine, opts: *TestCommand.CodeCoverageOptions, comptime reporters: TestCommand.Reporters, comptime enable_ansi_colors: bool) !void { + if (comptime !reporters.text and !reporters.lcov) { + return; + } - _ = this; var map = bun.sourcemap.ByteRangeMapping.map orelse return; var iter = map.valueIterator(); - var max_filepath_length: usize = "All files".len; - const relative_dir = vm.bundler.fs.top_level_dir; - var byte_ranges = try std.ArrayList(bun.sourcemap.ByteRangeMapping).initCapacity(bun.default_allocator, map.count()); while (iter.next()) |entry| { - const value: bun.sourcemap.ByteRangeMapping = entry.*; - const utf8 = value.source_url.slice(); - byte_ranges.appendAssumeCapacity(value); - max_filepath_length = @max(bun.path.relative(relative_dir, utf8).len, max_filepath_length); + byte_ranges.appendAssumeCapacity(entry.*); } if (byte_ranges.items.len == 0) { @@ -296,25 +290,65 @@ pub const CommandLineReporter = struct { std.sort.pdq(bun.sourcemap.ByteRangeMapping, byte_ranges.items, void{}, bun.sourcemap.ByteRangeMapping.isLessThan); - iter = map.valueIterator(); - var writer = Output.errorWriter(); + try this.printCodeCoverage(vm, opts, byte_ranges.items, reporters, enable_ansi_colors); + } + + pub fn printCodeCoverage(this: *CommandLineReporter, vm: *JSC.VirtualMachine, opts: *TestCommand.CodeCoverageOptions, byte_ranges: []bun.sourcemap.ByteRangeMapping, comptime reporters: TestCommand.Reporters, comptime enable_ansi_colors: bool) !void { + _ = this; // autofix + const trace = bun.tracy.traceNamed(@src(), comptime brk: { + if (reporters.text and reporters.lcov) { + break :brk "TestCommand.printCodeCoverageLCovAndText"; + } + + if (reporters.text) { + break :brk "TestCommand.printCodeCoverageText"; + } + + if (reporters.lcov) { + break :brk "TestCommand.printCodeCoverageLCov"; + } + + @compileError("No reporters enabled"); + }); + defer trace.end(); + + if (comptime !reporters.text and !reporters.lcov) { + @compileError("No reporters enabled"); + } + + const relative_dir = vm.bundler.fs.top_level_dir; + + // --- Text --- + const max_filepath_length: usize = if (reporters.text) brk: { + var len = "All files".len; + for (byte_ranges) |*entry| { + const utf8 = entry.source_url.slice(); + len = @max(bun.path.relative(relative_dir, utf8).len, len); + } + + break :brk len; + } else 0; + + var console = Output.errorWriter(); const base_fraction = opts.fractions; var failing = false; - writer.writeAll(Output.prettyFmt("", enable_ansi_colors)) catch return; - writer.writeByteNTimes('-', max_filepath_length + 2) catch return; - writer.writeAll(Output.prettyFmt("|---------|---------|-------------------\n", enable_ansi_colors)) catch return; - writer.writeAll("File") catch return; - writer.writeByteNTimes(' ', max_filepath_length - "File".len + 1) catch return; - // writer.writeAll(Output.prettyFmt(" | % Funcs | % Blocks | % Lines | Uncovered Line #s\n", enable_ansi_colors)) catch return; - writer.writeAll(Output.prettyFmt(" | % Funcs | % Lines | Uncovered Line #s\n", enable_ansi_colors)) catch return; - writer.writeAll(Output.prettyFmt("", enable_ansi_colors)) catch return; - writer.writeByteNTimes('-', max_filepath_length + 2) catch return; - writer.writeAll(Output.prettyFmt("|---------|---------|-------------------\n", enable_ansi_colors)) catch return; - - var coverage_buffer = bun.MutableString.initEmpty(bun.default_allocator); - var coverage_buffer_buffer = coverage_buffer.bufferedWriter(); - var coverage_writer = coverage_buffer_buffer.writer(); + if (comptime reporters.text) { + console.writeAll(Output.prettyFmt("", enable_ansi_colors)) catch return; + console.writeByteNTimes('-', max_filepath_length + 2) catch return; + console.writeAll(Output.prettyFmt("|---------|---------|-------------------\n", enable_ansi_colors)) catch return; + console.writeAll("File") catch return; + console.writeByteNTimes(' ', max_filepath_length - "File".len + 1) catch return; + // writer.writeAll(Output.prettyFmt(" | % Funcs | % Blocks | % Lines | Uncovered Line #s\n", enable_ansi_colors)) catch return; + console.writeAll(Output.prettyFmt(" | % Funcs | % Lines | Uncovered Line #s\n", enable_ansi_colors)) catch return; + console.writeAll(Output.prettyFmt("", enable_ansi_colors)) catch return; + console.writeByteNTimes('-', max_filepath_length + 2) catch return; + console.writeAll(Output.prettyFmt("|---------|---------|-------------------\n", enable_ansi_colors)) catch return; + } + + var console_buffer = bun.MutableString.initEmpty(bun.default_allocator); + var console_buffer_buffer = console_buffer.bufferedWriter(); + var console_writer = console_buffer_buffer.writer(); var avg = bun.sourcemap.CoverageFraction{ .functions = 0.0, @@ -322,50 +356,149 @@ pub const CommandLineReporter = struct { .stmts = 0.0, }; var avg_count: f64 = 0; + // --- Text --- + + // --- LCOV --- + var lcov_name_buf: bun.PathBuffer = undefined; + const lcov_file, const lcov_name, const lcov_buffered_writer, const lcov_writer = brk: { + if (comptime !reporters.lcov) break :brk .{ {}, {}, {}, {} }; + + // Ensure the directory exists + var fs = bun.JSC.Node.NodeFS{}; + _ = fs.mkdirRecursive( + .{ + .path = bun.JSC.Node.PathLike{ + .encoded_slice = JSC.ZigString.Slice.fromUTF8NeverFree(opts.reports_directory), + }, + .always_return_none = true, + }, + .sync, + ); - for (byte_ranges.items) |*entry| { - var report = bun.sourcemap.CodeCoverageReport.generate(vm.global, bun.default_allocator, entry, opts.ignore_sourcemap) orelse continue; + // Write the lcov.info file to a temporary file we atomically rename to the final name after it succeeds + var base64_bytes: [8]u8 = undefined; + var shortname_buf: [512]u8 = undefined; + bun.rand(&base64_bytes); + const tmpname = std.fmt.bufPrintZ(&shortname_buf, ".lcov.info.{s}.tmp", .{bun.fmt.fmtSliceHexLower(&base64_bytes)}) catch unreachable; + const path = bun.path.joinAbsStringBufZ(relative_dir, &lcov_name_buf, &.{ opts.reports_directory, tmpname }, .auto); + const file = bun.sys.File.openat( + std.fs.cwd(), + path, + bun.O.CREAT | bun.O.WRONLY | bun.O.TRUNC | bun.O.CLOEXEC, + 0o644, + ); + + switch (file) { + .err => |err| { + Output.err(.lcovCoverageError, "Failed to create lcov file", .{}); + Output.printError("\n{s}", .{err}); + Global.exit(1); + }, + .result => |f| { + const buffered = buffered_writer: { + const writer = f.writer(); + // Heap-allocate the buffered writer because we want a stable memory address + 64 KB is kind of a lot. + const ptr = try bun.default_allocator.create(std.io.BufferedWriter(64 * 1024, bun.sys.File.Writer)); + ptr.* = .{ + .end = 0, + .unbuffered_writer = writer, + }; + break :buffered_writer ptr; + }; + + break :brk .{ + f, + path, + buffered, + buffered.writer(), + }; + }, + } + }; + errdefer { + if (comptime reporters.lcov) { + lcov_file.close(); + _ = bun.sys.unlink( + lcov_name, + ); + } + } + // --- LCOV --- + + for (byte_ranges) |*entry| { + var report = CodeCoverageReport.generate(vm.global, bun.default_allocator, entry, opts.ignore_sourcemap) orelse continue; defer report.deinit(bun.default_allocator); - var fraction = base_fraction; - report.writeFormat(max_filepath_length, &fraction, relative_dir, coverage_writer, enable_ansi_colors) catch continue; - avg.functions += fraction.functions; - avg.lines += fraction.lines; - avg.stmts += fraction.stmts; - avg_count += 1.0; - if (fraction.failing) { - failing = true; + + if (comptime reporters.text) { + var fraction = base_fraction; + CodeCoverageReport.Text.writeFormat(&report, max_filepath_length, &fraction, relative_dir, console_writer, enable_ansi_colors) catch continue; + avg.functions += fraction.functions; + avg.lines += fraction.lines; + avg.stmts += fraction.stmts; + avg_count += 1.0; + if (fraction.failing) { + failing = true; + } + + console_writer.writeAll("\n") catch continue; } - coverage_writer.writeAll("\n") catch continue; + if (comptime reporters.lcov) { + CodeCoverageReport.Lcov.writeFormat( + &report, + relative_dir, + lcov_writer, + ) catch continue; + } } - { - avg.functions /= avg_count; - avg.lines /= avg_count; - avg.stmts /= avg_count; - - try bun.sourcemap.CodeCoverageReport.writeFormatWithValues( - "All files", - max_filepath_length, - avg, - base_fraction, - failing, - writer, - false, - enable_ansi_colors, - ); + if (comptime reporters.text) { + { + avg.functions /= avg_count; + avg.lines /= avg_count; + avg.stmts /= avg_count; + + try CodeCoverageReport.Text.writeFormatWithValues( + "All files", + max_filepath_length, + avg, + base_fraction, + failing, + console, + false, + enable_ansi_colors, + ); + + try console.writeAll(Output.prettyFmt(" |\n", enable_ansi_colors)); + } - try writer.writeAll(Output.prettyFmt(" |\n", enable_ansi_colors)); - } + console_buffer_buffer.flush() catch return; + try console.writeAll(console_buffer.list.items); + try console.writeAll(Output.prettyFmt("", enable_ansi_colors)); + console.writeByteNTimes('-', max_filepath_length + 2) catch return; + console.writeAll(Output.prettyFmt("|---------|---------|-------------------\n", enable_ansi_colors)) catch return; - coverage_buffer_buffer.flush() catch return; - try writer.writeAll(coverage_buffer.list.items); - try writer.writeAll(Output.prettyFmt("", enable_ansi_colors)); - writer.writeByteNTimes('-', max_filepath_length + 2) catch return; - writer.writeAll(Output.prettyFmt("|---------|---------|-------------------\n", enable_ansi_colors)) catch return; + opts.fractions.failing = failing; + Output.flush(); + } - opts.fractions.failing = failing; - Output.flush(); + if (comptime reporters.lcov) { + try lcov_buffered_writer.flush(); + lcov_file.close(); + bun.C.moveFileZ( + bun.toFD(std.fs.cwd()), + lcov_name, + bun.toFD(std.fs.cwd()), + bun.path.joinAbsStringZ( + relative_dir, + &.{ opts.reports_directory, "lcov.info" }, + .auto, + ), + ) catch |err| { + Output.err(err, "Failed to save lcov.info file", .{}); + Global.exit(1); + }; + } } }; @@ -436,10 +569,12 @@ const Scanner = struct { bun.assert(bun.toFD(dir.fd) != bun.invalid_fd); const parts2 = &[_]string{ entry.dir_path, entry.name.slice() }; - var path2 = this.fs.absBuf(parts2, &this.open_dir_buf); - const child_dir = bun.openDirAbsolute(path2) catch continue; - path2 = this.fs.dirname_store.append(string, path2) catch bun.outOfMemory(); - _ = this.readDirWithName(path2, child_dir) catch bun.outOfMemory(); + const path2 = this.fs.absBufZ(parts2, &this.open_dir_buf); + const child_dir = bun.openDirNoRenamingOrDeletingWindows(bun.invalid_fd, path2) catch continue; + _ = this.readDirWithName( + this.fs.dirname_store.append(string, path2) catch bun.outOfMemory(), + child_dir, + ) catch bun.outOfMemory(); } } } @@ -463,7 +598,7 @@ const Scanner = struct { }; // always ignore node_modules. - if (strings.contains(slice, "/" ++ "node_modules" ++ "/")) { + if (strings.contains(slice, "/node_modules/") or strings.contains(slice, "\\node_modules\\")) { return false; } @@ -572,11 +707,21 @@ pub const TestCommand = struct { pub const name = "test"; pub const CodeCoverageOptions = struct { skip_test_files: bool = !Environment.allow_assert, + reporters: Reporters = .{ .text = true, .lcov = false }, + reports_directory: string = "coverage", fractions: bun.sourcemap.CoverageFraction = .{}, ignore_sourcemap: bool = false, enabled: bool = false, fail_on_low_coverage: bool = false, }; + pub const Reporter = enum { + text, + lcov, + }; + const Reporters = struct { + text: bool, + lcov: bool, + }; pub fn exec(ctx: Command.Context) !void { if (comptime is_bindgen) unreachable; @@ -595,8 +740,8 @@ pub const TestCommand = struct { loader.* = DotEnv.Loader.init(map, ctx.allocator); break :brk loader; }; - bun.JSC.initialize(); - HTTPThread.init() catch {}; + bun.JSC.initialize(false); + HTTPThread.init(); var snapshot_file_buf = std.ArrayList(u8).init(ctx.allocator); var snapshot_values = Snapshots.ValuesHashMap.init(ctx.allocator); @@ -637,8 +782,8 @@ pub const TestCommand = struct { reporter.jest.callback = &reporter.callback; jest.Jest.runner = &reporter.jest; reporter.jest.test_options = &ctx.test_options; - js_ast.Expr.Data.Store.create(default_allocator); - js_ast.Stmt.Data.Store.create(default_allocator); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); var vm = try JSC.VirtualMachine.init( .{ .allocator = ctx.allocator, @@ -671,7 +816,7 @@ pub const TestCommand = struct { try vm.bundler.configureDefines(); - vm.loadExtraEnv(); + vm.loadExtraEnvAndSourceCodePrinter(); vm.is_main_thread = true; JSC.VirtualMachine.is_main_thread_vm = true; @@ -859,7 +1004,13 @@ pub const TestCommand = struct { if (coverage.enabled) { switch (Output.enable_ansi_colors_stderr) { - inline else => |colors| reporter.printCodeCoverage(vm, &coverage, colors) catch {}, + inline else => |colors| switch (coverage.reporters.text) { + inline else => |console| switch (coverage.reporters.lcov) { + inline else => |lcov| { + try reporter.generateCodeCoverage(vm, &coverage, .{ .text = console, .lcov = lcov }, colors); + }, + }, + }, } } @@ -954,7 +1105,7 @@ pub const TestCommand = struct { if (reporter.summary.fail > 0 or (coverage.enabled and coverage.fractions.failing and coverage.fail_on_low_coverage)) { Global.exit(1); } else if (reporter.jest.unhandled_errors_between_tests > 0) { - Global.exitWide(@intCast(reporter.jest.unhandled_errors_between_tests)); + Global.exit(reporter.jest.unhandled_errors_between_tests); } } @@ -1022,6 +1173,10 @@ pub const TestCommand = struct { Output.flush(); } + // Restore test.only state after each module. + const prev_only = reporter.jest.only; + defer reporter.jest.only = prev_only; + const file_start = reporter.jest.files.len; const resolution = try vm.bundler.resolveEntryPoint(file_name); vm.clearEntryPoint(); @@ -1051,7 +1206,7 @@ pub const TestCommand = struct { reporter.summary.files += 1; switch (promise.status(vm.global.vm())) { - .Rejected => { + .rejected => { _ = vm.unhandledRejection(vm.global, promise.result(vm.global.vm()), promise.asValue()); reporter.summary.fail += 1; @@ -1099,7 +1254,7 @@ pub const TestCommand = struct { if (!jest.Jest.runner.?.has_pending_tests) break; vm.eventLoop().tick(); } else { - vm.eventLoop().tickImmediateTasks(); + vm.eventLoop().tickImmediateTasks(vm); } while (prev_unhandled_count < vm.unhandled_error_counter) { diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index de498e7c14113..c5fca9ef5417c 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -45,8 +45,8 @@ pub var initialized_store = false; pub fn initializeStore() void { if (initialized_store) return; initialized_store = true; - js_ast.Expr.Data.Store.create(default_allocator); - js_ast.Stmt.Data.Store.create(default_allocator); + js_ast.Expr.Data.Store.create(); + js_ast.Stmt.Data.Store.create(); } pub const Version = struct { @@ -133,7 +133,7 @@ pub const UpgradeCheckerThread = struct { std.time.sleep(std.time.ns_per_ms * delay); Output.Source.configureThread(); - try HTTP.HTTPThread.init(); + HTTP.HTTPThread.init(); defer { js_ast.Expr.Data.Store.deinit(); @@ -163,7 +163,6 @@ pub const UpgradeCheckerThread = struct { }; pub const UpgradeCommand = struct { - pub const timeout: u32 = 30000; const default_github_headers: string = "Acceptapplication/vnd.github.v3+json"; var github_repository_url_buf: bun.PathBuffer = undefined; var current_executable_buf: bun.PathBuffer = undefined; @@ -245,12 +244,11 @@ pub const UpgradeCommand = struct { headers_buf, &metadata_body, "", - 60 * std.time.ns_per_min, http_proxy, null, HTTP.FetchRedirect.follow, ); - async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); if (!silent) async_http.client.progress_node = progress.?; const response = try async_http.sendSync(true); @@ -442,7 +440,7 @@ pub const UpgradeCommand = struct { } fn _exec(ctx: Command.Context) !void { - try HTTP.HTTPThread.init(); + HTTP.HTTPThread.init(); var filesystem = try fs.FileSystem.init(null); var env_loader: DotEnv.Loader = brk: { @@ -528,14 +526,12 @@ pub const UpgradeCommand = struct { "", zip_file_buffer, "", - timeout, http_proxy, null, HTTP.FetchRedirect.follow, ); - async_http.client.timeout = timeout; async_http.client.progress_node = progress; - async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env_loader.getTLSRejectUnauthorized(); const response = try async_http.sendSync(true); @@ -656,10 +652,10 @@ pub const UpgradeCommand = struct { // Run a powershell script to unzip the file const unzip_script = try std.fmt.allocPrint( ctx.allocator, - "$global:ProgressPreference='SilentlyContinue';Expand-Archive -Path {s} {s} -Force", + "$global:ProgressPreference='SilentlyContinue';Expand-Archive -Path \"{}\" \"{}\" -Force", .{ - tmpname, - tmpdir_path, + bun.fmt.escapePowershell(tmpname), + bun.fmt.escapePowershell(tmpdir_path), }, ); @@ -1000,7 +996,7 @@ pub const upgrade_js_bindings = struct { /// For testing upgrades when the temp directory has an open handle without FILE_SHARE_DELETE. /// Windows only - pub fn jsOpenTempDirWithoutSharingDelete(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) bun.JSC.JSValue { + pub fn jsOpenTempDirWithoutSharingDelete(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSC.JSValue { if (comptime !Environment.isWindows) return .undefined; const w = std.os.windows; @@ -1054,7 +1050,7 @@ pub const upgrade_js_bindings = struct { return .undefined; } - pub fn jsCloseTempDirHandle(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + pub fn jsCloseTempDirHandle(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { if (comptime !Environment.isWindows) return .undefined; if (tempdir_fd) |fd| { diff --git a/src/codegen/buildTypeFlag.ts b/src/codegen/buildTypeFlag.ts new file mode 100644 index 0000000000000..78800cbe3d325 --- /dev/null +++ b/src/codegen/buildTypeFlag.ts @@ -0,0 +1,18 @@ +const buildTypeFlag = process.argv.find(argv => { + if (argv.startsWith("--build-type=")) { + return argv; + } +}); + +enum BuildType { + debug, + release, +} + +if (buildTypeFlag) { + process.argv.splice(process.argv.indexOf(buildTypeFlag), 1); +} + +let buildType = buildTypeFlag ? BuildType[buildTypeFlag.split("=")[1].toLowerCase()] : BuildType.release; + +export { BuildType, buildType }; diff --git a/src/codegen/bundle-functions.ts b/src/codegen/bundle-functions.ts index 1fcf46189a5bb..0ada057c1f7ba 100644 --- a/src/codegen/bundle-functions.ts +++ b/src/codegen/bundle-functions.ts @@ -8,13 +8,19 @@ // library, instead of RegExp hacks. // // For explanation on this, please nag @paperdave to write documentation on how everything works. +// +// The output is intended to be similar to what WebCore does internally with a couple differences: +// +// - We concatenate all the sources into one big string, which then createsa +// single JSC::SourceProvider and pass start/end positions to each function's +// JSC::SourceCode. JSC does this, but WebCore does not seem to. import { readdirSync, rmSync } from "fs"; import path from "path"; import { sliceSourceCode } from "./builtin-parser"; -import { applyGlobalReplacements, define } from "./replacements"; -import { cap, fmtCPPCharArray, low, writeIfNotChanged } from "./helpers"; import { createAssertClientJS, createLogClientJS } from "./client-js"; -import { getJS2NativeDTS } from "./js2native-generator"; +import { getJS2NativeDTS } from "./generate-js2native"; +import { addCPPCharArray, cap, low, writeIfNotChanged } from "./helpers"; +import { applyGlobalReplacements, define } from "./replacements"; const PARALLEL = false; const KEEP_TMP = true; @@ -38,6 +44,7 @@ interface ParsedBuiltin { directives: Record; source: string; async: boolean; + enums: string[]; } interface BundledBuiltin { @@ -52,6 +59,7 @@ interface BundledBuiltin { source: string; params: string[]; visibility: string; + sourceOffset: number; } /** @@ -67,13 +75,15 @@ async function processFileSplit(filename: string): Promise<{ functions: BundledB // and then compile those separately const consumeWhitespace = /^\s*/; - const consumeTopLevelContent = /^(\/\*|\/\/|type|import|interface|\$|export (?:async )?function|(?:async )?function)/; - const consumeEndOfType = /;|.(?=export|type|interface|\$|\/\/|\/\*|function)/; + const consumeTopLevelContent = + /^(\/\*|\/\/|type|import|interface|\$|const enum|export (?:async )?function|(?:async )?function)/; + const consumeEndOfType = /;|.(?=export|type|interface|\$|\/\/|\/\*|function|const enum)/; const functions: ParsedBuiltin[] = []; let directives: Record = {}; const bundledFunctions: BundledBuiltin[] = []; let internal = false; + const topLevelEnums: { name: string; code: string }[] = []; while (contents.length) { contents = contents.replace(consumeWhitespace, ""); @@ -100,6 +110,16 @@ async function processFileSplit(filename: string): Promise<{ functions: BundledB contents = contents.slice(i + 1); } else if (match[1] === "interface") { contents = sliceSourceCode(contents, false).rest; + } else if (match[1] === "const enum") { + const { result, rest } = sliceSourceCode(contents, false); + const i = result.indexOf("{\n"); + // Support const enums in module scope. + topLevelEnums.push({ + name: result.slice("const enum ".length, i).trim(), + code: "\n" + result, + }); + + contents = rest; } else if (match[1] === "$") { const directive = contents.match(/^\$([a-zA-Z0-9]+)(?:\s*=\s*([^\r\n]+?))?\s*;?\r?\n/); if (!directive) { @@ -141,12 +161,27 @@ async function processFileSplit(filename: string): Promise<{ functions: BundledB globalThis.requireTransformer(x, SRC_DIR + "/" + basename), ); + const source = result.trim().slice(2, -1); + const constEnumsUsedInFunction: string[] = []; + if (topLevelEnums.length) { + // If the function references a top-level const enum let's add the code + // to the top-level scope of the function so that the transpiler will + // inline all the values and strip out the enum object. + for (const { name, code } of topLevelEnums) { + // Only include const enums which are referenced in the function source. + if (source.includes(name)) { + constEnumsUsedInFunction.push(code); + } + } + } + functions.push({ name, params, directives, - source: result.trim().slice(2, -1), + source, async, + enums: constEnumsUsedInFunction, }); contents = rest; directives = {}; @@ -171,7 +206,7 @@ async function processFileSplit(filename: string): Promise<{ functions: BundledB `// @ts-nocheck // GENERATED TEMP FILE - DO NOT EDIT // Sourced from ${path.relative(TMP_DIR, filename)} - +${fn.enums.join("\n")} // do not allow the bundler to rename a symbol to $ ($); @@ -228,6 +263,10 @@ $$capture_start$$(${fn.async ? "async " : ""}${ constructKind: fn.directives.ConstructKind ?? "None", isLinkTimeConstant: !!fn.directives.linkTimeConstant, intrinsic: fn.directives.intrinsic ?? "NoIntrinsic", + + // Not known yet. + sourceOffset: 0, + overriddenName: fn.directives.getter ? `"get ${fn.name}"_s` : fn.directives.overriddenName @@ -275,6 +314,34 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } } + let combinedSourceCodeChars = ""; + let combinedSourceCodeLength = 0; + // Compute source offsets + { + for (const { basename, functions } of files) { + for (const fn of functions) { + fn.sourceOffset = combinedSourceCodeLength; + combinedSourceCodeLength += fn.source.length; + if (combinedSourceCodeChars && !combinedSourceCodeChars.endsWith(",")) { + combinedSourceCodeChars += ","; + } + combinedSourceCodeChars += addCPPCharArray(fn.source, false); + + // If you want to see the individual function sources: + // if (true) { + // Bun.write(CODEGEN_DIR + "/functions/" + low(basename) + cap(fn.name) + ".js", fn.source + "\n"); + // } + } + } + } + + let additionalPrivateNames = new Set(); + + function privateName(name) { + additionalPrivateNames.add(name); + return "builtinNames." + name + "PrivateName()"; + } + // C++ codegen let bundledCPP = `// Generated by ${import.meta.path} namespace Zig { class GlobalObject; } @@ -283,48 +350,78 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt #include "JSDOMGlobalObject.h" #include "WebCoreJSClientData.h" #include - + #include "BunBuiltinNames.h" + namespace WebCore { - + static const LChar combinedSourceCodeBuffer[${combinedSourceCodeLength + 1}] = { ${combinedSourceCodeChars}, 0 }; + static const std::span internalCombinedSource = { combinedSourceCodeBuffer, ${combinedSourceCodeLength} }; `; for (const { basename, functions } of files) { - bundledCPP += `/* ${basename}.ts */\n`; + bundledCPP += ` +#pragma mark ${basename} +`; + const lowerBasename = low(basename); for (const fn of functions) { - const [code, count] = fmtCPPCharArray(fn.source, true); - const name = `${lowerBasename}${cap(fn.name)}Code`; - bundledCPP += `// ${fn.name} - const JSC::ConstructAbility s_${name}ConstructAbility = JSC::ConstructAbility::${fn.constructAbility}; - const JSC::InlineAttribute s_${name}InlineAttribute = JSC::InlineAttribute::${ - fn.directives.alwaysInline ? "Always" : "None" - }; - const JSC::ConstructorKind s_${name}ConstructorKind = JSC::ConstructorKind::${fn.constructKind}; - const JSC::ImplementationVisibility s_${name}ImplementationVisibility = JSC::ImplementationVisibility::${ - fn.visibility - }; - const int s_${name}Length = ${fn.source.length}; - const JSC::Intrinsic s_${name}Intrinsic = JSC::NoIntrinsic; - const char s_${name}Bytes[${count}] = ${code}; - const char* s_${name} = s_${name}Bytes; - `; + const name = `${basename}${cap(fn.name)}`; + bundledCPP += ` +JSC::FunctionExecutable* ${lowerBasename}${cap(fn.name)}CodeGenerator(JSC::VM& vm) +{ + auto &builtins = static_cast(vm.clientData)->builtinFunctions().${lowerBasename}Builtins(); + auto *executable = builtins.${lowerBasename}${cap(fn.name)}CodeExecutable(); + return executable->link(vm, nullptr, builtins.${lowerBasename}${cap(fn.name)}CodeSource(), std::nullopt, JSC::NoIntrinsic); +} +`; } - bundledCPP += `#define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \\ - JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\ - {\\ - JSVMClientData* clientData = static_cast(vm.clientData); \\ - return clientData->builtinFunctions().${lowerBasename}Builtins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().${lowerBasename}Builtins().codeName##Source(), std::nullopt, s_##codeName##Intrinsic); \\ + } + + const initializeSourceCodeFn = (fn: BundledBuiltin, basename: string) => { + const name = `${low(basename)}${cap(fn.name)}CodeSource`; + return `m_${name}(SourceCode(sourceProvider.copyRef(), ${fn.sourceOffset}, ${fn.source.length + fn.sourceOffset}, 1, 1))`; + }; + for (const { basename, internal, functions } of files) { + bundledCPP += ` +#pragma mark ${basename} + +${basename}BuiltinsWrapper::${basename}BuiltinsWrapper(JSC::VM& vm, RefPtr sourceProvider, BunBuiltinNames &builtinNames) + : m_vm(vm)`; + + if (internal) { + bundledCPP += `, ${functions.map(fn => `m_${fn.name}PrivateName(${privateName(fn.name)})`).join(",\n ")}`; + } + bundledCPP += `, ${functions.map(fn => initializeSourceCodeFn(fn, basename)).join(",\n ")} {} +`; + } + + bundledCPP += ` +RefPtr createBuiltinsSourceProvider() { + return JSC::StringSourceProvider::create(StringImpl::createWithoutCopying(internalCombinedSource), SourceOrigin(), String(), SourceTaintedOrigin()); +} +`; + + bundledCPP += ` +JSBuiltinFunctions::JSBuiltinFunctions(JSC::VM& vm, RefPtr provider, BunBuiltinNames& builtinNames) : m_vm(vm), + ${files.map(({ basename }) => `m_${low(basename)}Builtins(vm, provider, builtinNames)`).join(", ")} +{} + +void JSBuiltinFunctions::exportNames() { +`; + + for (const { basename, internal } of files) { + if (internal) { + bundledCPP += ` m_${low(basename)}Builtins.exportNames();\n`; } - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR) - #undef DEFINE_BUILTIN_GENERATOR - - `; } + bundledCPP += ` +} + +`; + bundledCPP += ` - JSBuiltinInternalFunctions::JSBuiltinInternalFunctions(JSC::VM& vm) - : m_vm(vm) +JSBuiltinInternalFunctions::JSBuiltinInternalFunctions(JSC::VM& vm) : m_vm(vm) `; for (const { basename, internal } of files) { @@ -333,10 +430,9 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } } - bundledCPP += ` - { - UNUSED_PARAM(vm); - } + bundledCPP += `{ + UNUSED_PARAM(vm); + } template void JSBuiltinInternalFunctions::visit(Visitor& visitor) @@ -417,12 +513,10 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt const name = `${lowerBasename}${cap(fn.name)}Code`; bundledHeader += `// ${fn.name} #define WEBCORE_BUILTIN_${basename.toUpperCase()}_${fn.name.toUpperCase()} 1 - extern const char* s_${name}; - extern const int s_${name}Length; - extern const JSC::ConstructAbility s_${name}ConstructAbility; - extern const JSC::InlineAttribute s_${name}InlineAttribute; - extern const JSC::ConstructorKind s_${name}ConstructorKind; - extern const JSC::ImplementationVisibility s_${name}ImplementationVisibility; + static constexpr JSC::ConstructAbility s_${name}ConstructAbility = JSC::ConstructAbility::${fn.constructAbility}; + static constexpr JSC::InlineAttribute s_${name}InlineAttribute = JSC::InlineAttribute::${fn.directives.alwaysInline ? "Always" : "None"}; + static constexpr JSC::ConstructorKind s_${name}ConstructorKind = JSC::ConstructorKind::${fn.constructKind}; + static constexpr JSC::ImplementationVisibility s_${name}ImplementationVisibility = JSC::ImplementationVisibility::${fn.visibility}; `; } @@ -450,14 +544,7 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt class ${basename}BuiltinsWrapper : private JSC::WeakHandleOwner { public: - explicit ${basename}BuiltinsWrapper(JSC::VM& vm) - : m_vm(vm) - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(INITIALIZE_BUILTIN_NAMES) - #define INITIALIZE_BUILTIN_SOURCE_MEMBERS(name, functionName, overriddenName, length) , m_##name##Source(JSC::makeSource(StringImpl::createWithoutCopying({reinterpret_cast(s_##name), static_cast(length)}), { }, JSC::SourceTaintedOrigin::Untainted)) - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(INITIALIZE_BUILTIN_SOURCE_MEMBERS) - #undef INITIALIZE_BUILTIN_SOURCE_MEMBERS - { - } + explicit ${basename}BuiltinsWrapper(JSC::VM& vm, RefPtr sourceProvider, BunBuiltinNames &builtinNames); #define EXPOSE_BUILTIN_EXECUTABLES(name, functionName, overriddenName, length) \\ JSC::UnlinkedFunctionExecutable* name##Executable(); \\ @@ -524,7 +611,7 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt inline void ${basename}BuiltinFunctions::init(JSC::JSGlobalObject& globalObject) { #define EXPORT_FUNCTION(codeName, functionName, overriddenName, length) \\ - m_##functionName##Function.set(m_vm, &globalObject, JSC::JSFunction::create(m_vm, codeName##Generator(m_vm), &globalObject)); + m_##functionName##Function.set(m_vm, &globalObject, JSC::JSFunction::create(m_vm, &globalObject, codeName##Generator(m_vm), &globalObject)); WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(EXPORT_FUNCTION) #undef EXPORT_FUNCTION } @@ -544,25 +631,9 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } bundledHeader += `class JSBuiltinFunctions { public: - explicit JSBuiltinFunctions(JSC::VM& vm) - : m_vm(vm) - `; - - for (const { basename } of files) { - bundledHeader += ` , m_${low(basename)}Builtins(m_vm)\n`; - } - - bundledHeader += ` - { - `; - - for (const { basename, internal } of files) { - if (internal) { - bundledHeader += ` m_${low(basename)}Builtins.exportNames();\n`; - } - } - - bundledHeader += ` } + explicit JSBuiltinFunctions(JSC::VM& vm, RefPtr provider, BunBuiltinNames &builtinNames); + void exportNames(); + `; for (const { basename } of files) { @@ -613,7 +684,53 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } // namespace WebCore `; + // Handle builtin names + { + const BunBuiltinNamesHeader = require("fs").readFileSync( + path.join(import.meta.dir, "../js/builtins/BunBuiltinNames.h"), + "utf8", + ); + let definedBuiltinNamesStartI = BunBuiltinNamesHeader.indexOf( + "#define BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME", + ); + let definedBuiltinNamesMacroEndI = BunBuiltinNamesHeader.indexOf( + "--- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME ---", + ); + const definedBuiltinNames = BunBuiltinNamesHeader.slice(definedBuiltinNamesStartI, definedBuiltinNamesMacroEndI) + .split("\n") + .map(x => x.trim()) + .filter(x => x.startsWith("macro(")) + .map(x => x.slice(x.indexOf("(") + 1, x.indexOf(")"))) + .map(x => x.trim()) + .sort(); + + const uniqueDefinedBuiltinNames = new Set(); + for (let name of definedBuiltinNames) { + const prevSize = uniqueDefinedBuiltinNames.size; + uniqueDefinedBuiltinNames.add(name); + if (uniqueDefinedBuiltinNames.size === prevSize) { + throw new Error(`Duplicate private name "${name}" in BunBuiltinNames.h`); + } + } + for (let additionalPrivateName of additionalPrivateNames) { + if (uniqueDefinedBuiltinNames.has(additionalPrivateName)) { + additionalPrivateNames.delete(additionalPrivateName); + } + } + + let additionalPrivateNamesHeader = `// Generated by ${import.meta.path} +#pragma once +#ifndef BUN_ADDITIONAL_BUILTIN_NAMES +#define BUN_ADDITIONAL_BUILTIN_NAMES(macro) \\ + ${Array.from(additionalPrivateNames) + .map(x => `macro(${x})`) + .join(" \\\n ")} +#endif +`; + + writeIfNotChanged(path.join(CODEGEN_DIR, "BunBuiltinNames+extras.h"), additionalPrivateNamesHeader); + } writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.h"), bundledHeader); writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.cpp"), bundledCPP); diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index 0be71f3b9228f..aaa86834d467f 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -9,15 +9,16 @@ // // For explanation on this, please nag @paperdave to write documentation on how everything works. import fs from "fs"; -import { writeFile, mkdir } from "fs/promises"; +import { mkdir, writeFile } from "fs/promises"; +import { builtinModules } from "node:module"; import path from "path"; +import ErrorCode from "../bun.js/bindings/ErrorCode"; import { sliceSourceCode } from "./builtin-parser"; -import { cap, declareASCIILiteral, writeIfNotChanged } from "./helpers"; import { createAssertClientJS, createLogClientJS } from "./client-js"; -import { builtinModules } from "node:module"; -import { define } from "./replacements"; +import { getJS2NativeCPP, getJS2NativeZig } from "./generate-js2native"; +import { cap, declareASCIILiteral, writeIfNotChanged } from "./helpers"; import { createInternalModuleRegistry } from "./internal-module-registry-scanner"; -import { getJS2NativeCPP, getJS2NativeZig } from "./js2native-generator"; +import { define } from "./replacements"; const BASE = path.join(import.meta.dir, "../js"); const debug = process.argv[2] === "--debug=ON"; @@ -432,6 +433,31 @@ writeIfNotChanged(path.join(CODEGEN_DIR, "GeneratedJS2Native.h"), getJS2NativeCP const js2nativeZigPath = path.join(import.meta.dir, "../bun.js/bindings/GeneratedJS2Native.zig"); writeIfNotChanged(js2nativeZigPath, getJS2NativeZig(js2nativeZigPath)); +const generatedDTSPath = path.join(CODEGEN_DIR, "generated.d.ts"); +writeIfNotChanged( + generatedDTSPath, + (() => { + let dts = ` +// GENERATED TEMP FILE - DO NOT EDIT +`; + + for (let i = 0; i < ErrorCode.length; i++) { + const [code, _, name] = ErrorCode[i]; + dts += ` +/** + * Generate a ${name} error with the \`code\` property set to ${code}. + * + * @param msg The error message + * @param args Additional arguments + */ +declare function $${code}(msg: string, ...args: any[]): ${name}; +`; + } + + return dts; + })(), +); + mark("Generate Code"); if (!silent) { diff --git a/src/codegen/class-definitions.ts b/src/codegen/class-definitions.ts index 594325b1d0114..1e4ef004153fb 100644 --- a/src/codegen/class-definitions.ts +++ b/src/codegen/class-definitions.ts @@ -29,6 +29,7 @@ export type Field = | ({ fn: string; length?: number; + passThis?: boolean; DOMJIT?: { returns: string; args?: [string, string] | [string, string, string] | [string] | []; @@ -101,7 +102,21 @@ export function define( estimatedSize, structuredClone, values, - klass: Object.fromEntries(Object.entries(klass).sort(([a], [b]) => a.localeCompare(b))), - proto: Object.fromEntries(Object.entries(proto).sort(([a], [b]) => a.localeCompare(b))), + klass: Object.fromEntries( + Object.entries(klass) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([k, v]) => { + v.DOMJIT = undefined; + return [k, v]; + }), + ), + proto: Object.fromEntries( + Object.entries(proto) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([k, v]) => { + v.DOMJIT = undefined; + return [k, v]; + }), + ), }; } diff --git a/src/codegen/create-hash-table.ts b/src/codegen/create-hash-table.ts index a94dc3d7c2934..e47c1d036ff13 100644 --- a/src/codegen/create-hash-table.ts +++ b/src/codegen/create-hash-table.ts @@ -43,6 +43,7 @@ str = str.replaceAll(/^\/\/.*$/gm, ""); str = str.replaceAll(/^#include.*$/gm, ""); str = str.replaceAll(`namespace JSC {`, ""); str = str.replaceAll(`} // namespace JSC`, ""); -str = "// File generated via `static-hash-table.ts`\n" + str.trim() + "\n"; +str = str.replaceAll(/NativeFunctionType,\s([a-zA-Z0-99_]+)/gm, "NativeFunctionType, &$1"); +str = "#pragma once" + "\n" + "// File generated via `create-hash-table.ts`\n" + str.trim() + "\n"; writeIfNotChanged(output, str); diff --git a/src/codegen/generate-classes.ts b/src/codegen/generate-classes.ts index ef7c8cf865082..7295986bfde56 100644 --- a/src/codegen/generate-classes.ts +++ b/src/codegen/generate-classes.ts @@ -1,8 +1,7 @@ // @ts-nocheck import path from "path"; -import type { Field, ClassDefinition } from "./class-definitions"; -import { writeIfNotChanged } from "./helpers"; -import { camelCase, pascalCase } from "change-case"; +import type { ClassDefinition, Field } from "./class-definitions"; +import { camelCase, pascalCase, writeIfNotChanged } from "./helpers"; if (process.env.BUN_SILENT === "1") { console.log = () => {}; @@ -10,7 +9,7 @@ if (process.env.BUN_SILENT === "1") { const files = process.argv.slice(2); const outBase = files.pop(); - +let externs = ""; const CommonIdentifiers = { "name": true, }; @@ -88,10 +87,14 @@ function ZigDOMJITArgType(type) { }[type]; } +function ZigDOMJITArgTypeDefinition(type, index) { + return `arg${index}: ${ZigDOMJITArgType(type)}`; +} + function ZigDOMJITFunctionType(thisName, { args, returns }) { return `fn (*${thisName}, *JSC.JSGlobalObject, ${args .map(ZigDOMJITArgType) - .join(", ")}) callconv(.C) ${ZigDOMJITArgType("JSValue")}`; + .join(", ")}) callconv(JSC.conv) ${ZigDOMJITArgType("JSValue")}`; } function DOMJITReturnType(type) { @@ -108,13 +111,16 @@ function DOMJITFunctionDeclaration(jsClassName, fnName, symName, { args, returns const argNames = args.map((arg, i) => `${argTypeName(arg)} arg${i}`); const formattedArgs = argNames.length > 0 ? `, ${argNames.join(", ")}` : ""; const domJITArgs = args.length > 0 ? `, ${args.map(DOMJITType).join(", ")}` : ""; - return ` - extern "C" JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(${DOMJITName( - fnName, - )}Wrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue${formattedArgs})); - extern "C" EncodedJSValue ${DOMJITName(symName)}(void* ptr, JSC::JSGlobalObject * lexicalGlobalObject${formattedArgs}); + externs += ` +extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${DOMJITName(symName)}(void* ptr, JSC::JSGlobalObject * lexicalGlobalObject${formattedArgs}); + `; - static const JSC::DOMJIT::Signature DOMJITSignatureFor${fnName}(${DOMJITName(fnName)}Wrapper, + return ( + ` +extern JSC_CALLCONV JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(${DOMJITName( + fnName, + )}Wrapper, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue${formattedArgs})); +static const JSC::DOMJIT::Signature DOMJITSignatureFor${fnName}(${DOMJITName(fnName)}Wrapper, ${jsClassName}::info(), ${ pure @@ -122,10 +128,11 @@ function DOMJITFunctionDeclaration(jsClassName, fnName, symName, { args, returns : "JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top())" }, ${returns === "JSString" ? "JSC::SpecString" : DOMJITType("JSValue")}${domJITArgs}); -`.trim(); +`.trim() + "\n" + ); } -function DOMJITFunctionDefinition(jsClassName, fnName, symName, { args }) { +function DOMJITFunctionDefinition(jsClassName, fnName, symName, { args }, fn) { const argNames = args.map((arg, i) => `${argTypeName(arg)} arg${i}`); const formattedArgs = argNames.length > 0 ? `, ${argNames.join(", ")}` : ""; const retArgs = argNames.length > 0 ? `, ${args.map((b, i) => "arg" + i).join(", ")}` : ""; @@ -133,20 +140,44 @@ function DOMJITFunctionDefinition(jsClassName, fnName, symName, { args }) { return ` JSC_DEFINE_JIT_OPERATION(${DOMJITName( fnName, - )}Wrapper, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue${formattedArgs})) + )}Wrapper, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, void* thisValue${formattedArgs})) { VM& vm = JSC::getVM(lexicalGlobalObject); IGNORE_WARNINGS_BEGIN("frame-address") CallFrame* callFrame = DECLARE_CALL_FRAME(vm); IGNORE_WARNINGS_END JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame); - return ${DOMJITName(symName)}(reinterpret_cast<${jsClassName}*>(thisValue)->wrapped(), lexicalGlobalObject${retArgs}); +#if BUN_DEBUG + ${jsClassName}* wrapper = reinterpret_cast<${jsClassName}*>(thisValue); + JSC::EncodedJSValue result = ${DOMJITName(symName)}(wrapper->wrapped(), lexicalGlobalObject${retArgs}); + JSValue decoded = JSValue::decode(result); + if (wrapper->m_${fn}_expectedResultType) { + if (decoded.isCell() && !decoded.isEmpty()) { + ASSERT_WITH_MESSAGE(wrapper->m_${fn}_expectedResultType.value().has_value(), "DOMJIT function return type changed!"); + ASSERT_WITH_MESSAGE(wrapper->m_${fn}_expectedResultType.value().value() == decoded.asCell()->type(), "DOMJIT function return type changed!"); + } else { + ASSERT_WITH_MESSAGE(!wrapper->m_${fn}_expectedResultType.value().has_value(), "DOMJIT function return type changed!"); + } + } else if (!decoded.isEmpty()) { + wrapper->m_${fn}_expectedResultType = decoded.isCell() + ? std::optional(decoded.asCell()->type()) + : std::optional(std::nullopt); + } + return { result }; +#endif + return {${DOMJITName(symName)}(reinterpret_cast<${jsClassName}*>(thisValue)->wrapped(), lexicalGlobalObject${retArgs})}; } -`; +`.trim(); } -function appendSymbols(to: Map, symbolName: (name: string) => string, prop) { +function zigExportName(to: Map, symbolName: (name: string) => string, prop) { var { defaultValue, getter, setter, accessor, fn, DOMJIT, cache } = prop; + const exportNames = { + getter: "", + setter: "", + fn: "", + DOMJIT: "", + }; if (accessor) { getter = accessor.getter; @@ -154,19 +185,21 @@ function appendSymbols(to: Map, symbolName: (name: string) => st } if (getter && !to.get(getter)) { - to.set(getter, symbolName(getter)); + to.set(getter, (exportNames.getter = symbolName(getter))); } if (setter && !to.get(setter)) { - to.set(setter, symbolName(setter)); + to.set(setter, (exportNames.setter = symbolName(setter))); } if (fn && !to.get(fn)) { if (DOMJIT) { - to.set(DOMJITName(fn), symbolName(DOMJITName(fn))); + to.set(DOMJITName(fn), (exportNames.DOMJIT = symbolName(DOMJITName(fn)))); } - to.set(fn, symbolName(fn)); + to.set(fn, (exportNames.fn = symbolName(fn))); } + + return exportNames; } function propRow( symbolName: (a: string, b: string) => string, @@ -321,6 +354,36 @@ function generatePrototype(typeName, obj) { var staticPrototypeValues = ""; + if (obj.construct) { + externs += ` +extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ${classSymbolName(typeName, "construct")}(JSC::JSGlobalObject*, JSC::CallFrame*); +JSC_DECLARE_CUSTOM_GETTER(js${typeName}Constructor); +`; + } + + if (obj.structuredClone) { + externs += + `extern JSC_CALLCONV void JSC_HOST_CALL_ATTRIBUTES ${symbolName( + typeName, + "onStructuredCloneSerialize", + )}(void*, JSC::JSGlobalObject*, WebCore::CloneSerializer*, SYSV_ABI void (*) (WebCore::CloneSerializer*, const uint8_t*, uint32_t));` + + "\n"; + + externs += + `extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${symbolName( + typeName, + "onStructuredCloneDeserialize", + )}(JSC::JSGlobalObject*, const uint8_t*, const uint8_t*);` + "\n"; + } + if (obj.finalize) { + externs += + `extern JSC_CALLCONV void JSC_HOST_CALL_ATTRIBUTES ${classSymbolName(typeName, "finalize")}(void*);` + "\n"; + } + + if (obj.call) { + externs += `extern JSC_CALLCONV JSC_DECLARE_HOST_FUNCTION(${classSymbolName(typeName, "call")}) SYSV_ABI;` + "\n"; + } + for (const name in protoFields) { if ("value" in protoFields[name]) { const { value } = protoFields[name]; @@ -359,34 +422,6 @@ function generatePrototype(typeName, obj) { } return ` -${ - obj.construct - ? `extern "C" void* ${classSymbolName(typeName, "construct")}(JSC::JSGlobalObject*, JSC::CallFrame*); -JSC_DECLARE_CUSTOM_GETTER(js${typeName}Constructor);` - : "" -} - -${ - obj.structuredClone - ? `extern "C" void ${symbolName( - typeName, - "onStructuredCloneSerialize", - )}(void*, JSC::JSGlobalObject*, void*, void (*) (CloneSerializer*, const uint8_t*, uint32_t));` - : "" -} - -${ - obj.structuredClone - ? `extern "C" JSC::EncodedJSValue ${symbolName( - typeName, - "onStructuredCloneDeserialize", - )}(JSC::JSGlobalObject*, const uint8_t*, const uint8_t*);` - : "" -} - -${"finalize" in obj ? `extern "C" void ${classSymbolName(typeName, "finalize")}(void*);` : ""} -${obj.call ? `extern "C" JSC_DECLARE_HOST_FUNCTION(${classSymbolName(typeName, "call")});` : ""} - ${renderDecls(protoSymbolName, typeName, protoFields, obj.supportsObjectCreate || false)} STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(${proto}, ${proto}::Base); @@ -425,37 +460,38 @@ function generatePrototypeHeader(typename) { const proto = prototypeName(typename); return ` - class ${proto} final : public JSC::JSNonFinalObject { - public: - using Base = JSC::JSNonFinalObject; +class ${proto} final : public JSC::JSNonFinalObject { + public: + using Base = JSC::JSNonFinalObject; - static ${proto}* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) - { - ${proto}* ptr = new (NotNull, JSC::allocateCell<${proto}>(vm)) ${proto}(vm, globalObject, structure); - ptr->finishCreation(vm, globalObject); - return ptr; - } + static ${proto}* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + ${proto}* ptr = new (NotNull, JSC::allocateCell<${proto}>(vm)) ${proto}(vm, globalObject, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } - DECLARE_INFO; - template - static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) - { - STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(${proto}, Base); - return &vm.plainObjectSpace(); - } - static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) - { - return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); - } + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(${proto}, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } - private: - ${proto}(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) - : Base(vm, structure) - { - } + private: + ${proto}(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } - void finishCreation(JSC::VM&, JSC::JSGlobalObject*); - };`; + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); +}; +`; } function generateConstructorHeader(typeName) { @@ -463,49 +499,51 @@ function generateConstructorHeader(typeName) { // we use a single shared isosubspace for constructors since they will rarely // ever be created multiple times per VM and have no fields themselves - return ` - class ${name} final : public JSC::InternalFunction { - public: - using Base = JSC::InternalFunction; - static ${name}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, ${prototypeName( - typeName, - )}* prototype); + return ( + ` +class ${name} final : public JSC::InternalFunction { + public: + using Base = JSC::InternalFunction; + static ${name}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, ${prototypeName( + typeName, + )}* prototype); - static constexpr unsigned StructureFlags = Base::StructureFlags; - static constexpr bool needsDestruction = false; + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; - static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) - { - return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); - } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } - template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) - { - if constexpr (mode == JSC::SubspaceAccess::Concurrently) - return nullptr; - - return WebCore::subspaceForImpl<${name}, WebCore::UseCustomHeapCellType::No>( - vm, - [](auto& spaces) { return spaces.${clientSubspaceFor("BunClass")}Constructor.get(); }, - [](auto& spaces, auto&& space) { spaces.${clientSubspaceFor("BunClass")}Constructor = std::forward(space); }, - [](auto& spaces) { return spaces.${subspaceFor("BunClass")}Constructor.get(); }, - [](auto& spaces, auto&& space) { spaces.${subspaceFor("BunClass")}Constructor = std::forward(space); }); - } + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + + return WebCore::subspaceForImpl<${name}, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.${clientSubspaceFor("BunClass")}Constructor.get(); }, + [](auto& spaces, auto&& space) { spaces.${clientSubspaceFor("BunClass")}Constructor = std::forward(space); }, + [](auto& spaces) { return spaces.${subspaceFor("BunClass")}Constructor.get(); }, + [](auto& spaces, auto&& space) { spaces.${subspaceFor("BunClass")}Constructor = std::forward(space); }); + } - void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, ${prototypeName(typeName)}* prototype); + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, ${prototypeName(typeName)}* prototype); - // Must be defined for each specialization class. - static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); - static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*); + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*); - DECLARE_EXPORT_INFO; - private: - ${name}(JSC::VM& vm, JSC::Structure* structure); - void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, ${prototypeName(typeName)}* prototype); - }; + DECLARE_EXPORT_INFO; + private: + ${name}(JSC::VM& vm, JSC::Structure* structure); + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, ${prototypeName(typeName)}* prototype); +}; - `; + `.trim() + "\n" + ); } function generateConstructorImpl(typeName, obj: ClassDefinition) { @@ -515,8 +553,11 @@ function generateConstructorImpl(typeName, obj: ClassDefinition) { Object.keys(fields).length > 0 ? generateHashTable(name, classSymbolName, typeName, obj, fields, false) : ""; const hashTableIdentifier = hashTable.length ? `${name}TableValues` : ""; + if (obj.estimatedSize) { + externs += `extern JSC_CALLCONV size_t ${symbolName(typeName, "estimatedSize")}(void* ptr);` + "\n"; + } + return ` -${obj.estimatedSize ? `extern "C" size_t ${symbolName(typeName, "estimatedSize")}(void* ptr);` : ""} ${renderStaticDecls(classSymbolName, typeName, fields, obj.supportsObjectCreate || false)} ${hashTable} @@ -577,7 +618,7 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${name}::construct(JSC::JSGlobalObj auto* constructor = globalObject->${className(typeName)}Constructor(); Structure* structure = globalObject->${className(typeName)}Structure(); if (UNLIKELY(constructor != newTarget)) { - auto* functionGlobalObject = reinterpret_cast( + auto* functionGlobalObject = defaultGlobalObject( // ShadowRealm functions belong to a different global object. getFunctionRealm(globalObject, newTarget) ); @@ -617,7 +658,7 @@ const ClassInfo ${name}::s_info = { "Function"_s, &Base::s_info, nullptr, nullpt ${ !obj.noConstructor ? ` - extern "C" EncodedJSValue ${typeName}__getConstructor(Zig::GlobalObject* globalObject) { + extern JSC_CALLCONV JSC::EncodedJSValue ${typeName}__getConstructor(Zig::GlobalObject* globalObject) { return JSValue::encode(globalObject->${className(typeName)}Constructor()); }` : "" @@ -663,12 +704,12 @@ function renderCallbacksCppImpl(typeName, callbacks: Record) { for (const name in callbacks) { rows.push( ` - extern "C" EncodedJSValue ${symbolName(typeName, "_callback_get_" + name)}(JSC::EncodedJSValue encodedThisValue) { + extern JSC_CALLCONV JSC::EncodedJSValue ${symbolName(typeName, "_callback_get_" + name)}(JSC::EncodedJSValue encodedThisValue) { auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); return JSValue::encode(thisObject->m_callback_${name}.get()); } - extern "C" void ${symbolName(typeName, "_callback_set_" + name)}(JSC::EncodedJSValue encodedThisValue, JSC::EncodedJSValue encodedCallback) { + extern JSC_CALLCONV void ${symbolName(typeName, "_callback_set_" + name)}(JSC::EncodedJSValue encodedThisValue, JSC::EncodedJSValue encodedCallback) { auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); JSValue callback = JSValue::decode(encodedCallback); #if ASSERT_ENABLED @@ -688,7 +729,7 @@ function renderCallbacksCppImpl(typeName, callbacks: Record) { } rows.push(` - extern "C" void ${symbolName(typeName, "_setAllCallbacks")}(JSC::EncodedJSValue encodedThisValue, ${Object.keys( + extern JSC_CALLCONV void ${symbolName(typeName, "_setAllCallbacks")}(JSC::EncodedJSValue encodedThisValue, ${Object.keys( callbacks, ) .map((_, i) => `JSC::EncodedJSValue encodedCallback${i}`) @@ -708,7 +749,7 @@ function renderCallbacksCppImpl(typeName, callbacks: Record) { `); - return rows.join("\n"); + return rows.map(a => a.trim()).join("\n"); } function renderCallbacksZig(typeName, callbacks: Record) { @@ -724,8 +765,8 @@ function renderCallbacksZig(typeName, callbacks: Record) { const get = symbolName(typeName, "_callback_get_" + name); const set = symbolName(typeName, "_callback_set_" + name); out += ` - extern fn ${get}(JSC.JSValue) JSC.JSValue; - extern fn ${set}(JSC.JSValue, JSC.JSValue) void; + extern fn ${get}(JSC.JSValue) callconv(JSC.conv) JSC.JSValue; + extern fn ${set}(JSC.JSValue, JSC.JSValue) callconv(JSC.conv) void; pub const ${pascalCase(name)}Callback = JSC.Codegen.CallbackWrapper(${get}, ${set}); pub fn ${camelCase(name)}(cb: @This(), thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, args: []const JSC.JSValue) ?JSC.JSValue { return ${pascalCase(name)}Callback.call(.{.instance = cb.instance}, thisValue, globalObject, args); @@ -738,7 +779,7 @@ function renderCallbacksZig(typeName, callbacks: Record) { out += ` extern fn ${symbolName(typeName, "_setAllCallbacks")}(JSC.JSValue, ${Object.keys(callbacks) .map((a, i) => `callback${i}: JSC.JSValue`) - .join(", ")}) void; + .join(", ")}) callconv(JSC.conv) void; pub inline fn set(this: @This(), values: struct { ${Object.keys(callbacks) @@ -769,13 +810,15 @@ function renderDecls(symbolName, typeName, proto, supportsObjectCreate = false) for (const name in proto) { if ("getter" in proto[name] || ("accessor" in proto[name] && proto[name].getter)) { - rows.push( - `extern "C" JSC::EncodedJSValue ${symbolName( + externs += + `extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${symbolName( typeName, proto[name].getter || proto[name].accessor.getter, )}(void* ptr,${ !!proto[name].this ? " JSC::EncodedJSValue thisValue, " : "" - } JSC::JSGlobalObject* lexicalGlobalObject);`, + } JSC::JSGlobalObject* lexicalGlobalObject);` + "\n"; + + rows.push( ` JSC_DECLARE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap); ${proto[name].writable ? `JSC_DECLARE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap);` : ""} @@ -789,10 +832,11 @@ function renderDecls(symbolName, typeName, proto, supportsObjectCreate = false) } if ("setter" in proto[name] || ("accessor" in proto[name] && proto[name].setter)) { - rows.push( - `extern "C" bool ${symbolName(typeName, proto[name].setter || proto[name].accessor.setter)}(void* ptr,${ + externs += + `extern JSC_CALLCONV bool JSC_HOST_CALL_ATTRIBUTES ${symbolName(typeName, proto[name].setter || proto[name].accessor.setter)}(void* ptr,${ !!proto[name].this ? " JSC::EncodedJSValue thisValue, " : "" - } JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value);`, + } JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value);` + "\n"; + rows.push( ` static JSC_DECLARE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap); `.trim(), @@ -801,12 +845,13 @@ function renderDecls(symbolName, typeName, proto, supportsObjectCreate = false) } if ("fn" in proto[name]) { - rows.push( - `extern "C" EncodedJSValue ${symbolName( + externs += + `extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${symbolName( typeName, proto[name].fn, - )}(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);`, - + )}(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame${proto[name].passThis ? ", JSC::EncodedJSValue thisValue" : ""});` + + "\n"; + rows.push( ` JSC_DECLARE_HOST_FUNCTION(${symbolName(typeName, name)}Callback); `.trim(), @@ -826,13 +871,14 @@ function renderDecls(symbolName, typeName, proto, supportsObjectCreate = false) symbolName(typeName, name), symbolName(typeName, proto[name].fn), proto[name].DOMJIT, + proto[name].fn, ), ); } } } - return rows.join("\n"); + return rows.map(a => a.trim()).join("\n"); } function renderStaticDecls(symbolName, typeName, fields, supportsObjectCreate = false) { @@ -840,25 +886,24 @@ function renderStaticDecls(symbolName, typeName, fields, supportsObjectCreate = for (const name in fields) { if ("getter" in fields[name] || ("accessor" in fields[name] && fields[name].getter)) { - rows.push( - `extern "C" JSC_DECLARE_CUSTOM_GETTER(${symbolName( + externs += + `extern JSC_CALLCONV JSC_DECLARE_CUSTOM_GETTER(${symbolName( typeName, fields[name].getter || fields[name].accessor.getter, - )});`, - ); + )});` + "\n"; } if ("setter" in fields[name] || ("accessor" in fields[name] && fields[name].setter)) { - rows.push( - `extern "C" JSC_DECLARE_CUSTOM_SETTER(${symbolName( + externs += + `extern JSC_CALLCONV JSC_DECLARE_CUSTOM_SETTER(${symbolName( typeName, fields[name].setter || fields[name].accessor.setter, - )});`, - ); + )});` + "\n"; } if ("fn" in fields[name]) { - rows.push(`extern "C" JSC_DECLARE_HOST_FUNCTION(${symbolName(typeName, fields[name].fn)});`); + externs += + `extern JSC_CALLCONV JSC_DECLARE_HOST_FUNCTION(${symbolName(typeName, fields[name].fn)}) SYSV_ABI;` + "\n"; } } @@ -867,20 +912,20 @@ function renderStaticDecls(symbolName, typeName, fields, supportsObjectCreate = function writeBarrier(symbolName, typeName, name, cacheName) { return ` - extern "C" void ${symbolName(typeName, name)}SetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) - { - auto& vm = globalObject->vm(); - auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(thisValue)); - thisObject->${cacheName}.set(vm, thisObject, JSValue::decode(value)); - } - - extern "C" EncodedJSValue ${symbolName(typeName, name)}GetCachedValue(JSC::EncodedJSValue thisValue) - { +extern JSC_CALLCONV void ${symbolName(typeName, name)}SetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(thisValue)); - return JSValue::encode(thisObject->${cacheName}.get()); - } + thisObject->${cacheName}.set(vm, thisObject, JSValue::decode(value)); +} - `; +extern JSC_CALLCONV JSC::EncodedJSValue ${symbolName(typeName, name)}GetCachedValue(JSC::EncodedJSValue thisValue) +{ + auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(thisValue)); + return JSValue::encode(thisObject->${cacheName}.get()); +} + + `.trim(); } function renderFieldsImpl( symbolName: (typeName: string, name: string) => string, @@ -894,7 +939,8 @@ function renderFieldsImpl( const supportsObjectCreate = obj.supportsObjectCreate || false; if (obj.construct) { - rows.push(` + rows.push( + ` JSC_DEFINE_CUSTOM_GETTER(js${typeName}Constructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) { @@ -908,7 +954,8 @@ JSC_DEFINE_CUSTOM_GETTER(js${typeName}Constructor, (JSGlobalObject * lexicalGlob return JSValue::encode(globalObject->${className(typeName)}Constructor()); } -`); +`.trim(), + ); } for (const name in proto) { @@ -916,7 +963,8 @@ JSC_DEFINE_CUSTOM_GETTER(js${typeName}Constructor, (JSGlobalObject * lexicalGlob const cacheName = typeof proto[name].cache === "string" ? `m_${proto[name].cache}` : `m_${name}`; if ("cache" in proto[name]) { if (!supportsObjectCreate) { - rows.push(` + rows.push( + ` JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) { auto& vm = lexicalGlobalObject->vm(); @@ -936,44 +984,50 @@ JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObjec RETURN_IF_EXCEPTION(throwScope, {}); thisObject->${cacheName}.set(vm, thisObject, result); RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); -}`); +}`.trim(), + ); if (proto[name].writable) { - rows.push(` - JSC_DEFINE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName)) - { - auto& vm = lexicalGlobalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); - JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); - thisObject->${cacheName}.set(vm, thisObject, JSValue::decode(encodedValue)); - RELEASE_AND_RETURN(throwScope, true); - }`); + rows.push( + ` +JSC_DEFINE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + thisObject->${cacheName}.set(vm, thisObject, JSValue::decode(encodedValue)); + RELEASE_AND_RETURN(throwScope, true); +}`.trim(), + ); } } else { - rows.push(` - JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObject * globalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) - { - auto& vm = globalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - ${className(typeName)}* thisObject = jsDynamicCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); - if (UNLIKELY(!thisObject)) { - return JSValue::encode(jsUndefined()); - } + rows.push( + ` +JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObject * globalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) +{ + auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + ${className(typeName)}* thisObject = jsDynamicCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); + if (UNLIKELY(!thisObject)) { + return JSValue::encode(jsUndefined()); + } - JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); - - if (JSValue cachedValue = thisObject->${cacheName}.get()) - return JSValue::encode(cachedValue); - - JSC::JSValue result = JSC::JSValue::decode( - ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ - proto[name].this!! ? " thisValue, " : "" - } globalObject) - ); - RETURN_IF_EXCEPTION(throwScope, {}); - thisObject->${cacheName}.set(vm, thisObject, result); - RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); - }`); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->${cacheName}.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ + proto[name].this!! ? " thisValue, " : "" + } globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->${cacheName}.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +`.trim(), + ); } } rows.push(writeBarrier(symbolName, typeName, name, cacheName)); @@ -996,22 +1050,22 @@ JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObjec `); } else { rows.push(` - JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) - { - auto& vm = lexicalGlobalObject->vm(); - Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); - auto throwScope = DECLARE_THROW_SCOPE(vm); - ${className(typeName)}* thisObject = jsDynamicCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); - if (UNLIKELY(!thisObject)) { - return JSValue::encode(jsUndefined()); - } - JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); - JSC::EncodedJSValue result = ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ - !!proto[name].this ? " encodedThisValue, " : "" - } globalObject); - RETURN_IF_EXCEPTION(throwScope, {}); - RELEASE_AND_RETURN(throwScope, result); - } +JSC_DEFINE_CUSTOM_GETTER(${symbolName(typeName, name)}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + ${className(typeName)}* thisObject = jsDynamicCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); + if (UNLIKELY(!thisObject)) { + return JSValue::encode(jsUndefined()); + } + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ + !!proto[name].this ? " encodedThisValue, " : "" + } globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} `); } } @@ -1036,58 +1090,79 @@ JSC_DEFINE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap, (JSGlobalObjec } else if (supportsObjectCreate) { rows.push( ` - JSC_DEFINE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName)) - { - auto& vm = lexicalGlobalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - JSValue thisValue = JSValue::decode(encodedThisValue); - if (!thisValue.isObject()) { - return false; - } +JSC_DEFINE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSValue thisValue = JSValue::decode(encodedThisValue); + if (!thisValue.isObject()) { + return false; + } - JSObject *thisObject = asObject(thisValue); - thisObject->putDirect(vm, attributeName, JSValue::decode(encodedValue), 0); - return true; - } + JSObject *thisObject = asObject(thisValue); + thisObject->putDirect(vm, attributeName, JSValue::decode(encodedValue), 0); + return true; +} `, ); } if ("fn" in proto[name]) { + const fn = proto[name].fn; rows.push(` - JSC_DEFINE_HOST_FUNCTION(${symbolName(typeName, name)}Callback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) - { - auto& vm = lexicalGlobalObject->vm(); - - ${className(typeName)}* thisObject = jsDynamicCast<${className(typeName)}*>(callFrame->thisValue()); +JSC_DEFINE_HOST_FUNCTION(${symbolName(typeName, name)}Callback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - auto throwScope = DECLARE_THROW_SCOPE(vm); - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof ${typeName}"_s); - return JSValue::encode({}); - } + ${className(typeName)}* thisObject = jsDynamicCast<${className(typeName)}*>(callFrame->thisValue()); - JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + if (UNLIKELY(!thisObject)) { + scope.throwException(lexicalGlobalObject, Bun::createInvalidThisError(lexicalGlobalObject, callFrame->thisValue(), "${typeName}"_s)); + return {}; + } - #ifdef BUN_DEBUG - /** View the file name of the JS file that called this function - * from a debugger */ - SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); - const char* fileName = sourceOrigin.string().utf8().data(); - static const char* lastFileName = nullptr; - if (lastFileName != fileName) { - lastFileName = fileName; - } + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); - JSC::EncodedJSValue result = ${symbolName(typeName, proto[name].fn)}(thisObject->wrapped(), lexicalGlobalObject, callFrame); +#if BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } - ASSERT_WITH_MESSAGE(!JSValue::decode(result).isEmpty() or DECLARE_CATCH_SCOPE(vm).exception() != 0, \"${typeName}.${proto[name].fn} returned an empty value without an exception\"); + JSC::EncodedJSValue result = ${symbolName(typeName, fn)}(thisObject->wrapped(), lexicalGlobalObject, callFrame${proto[name].passThis ? ", JSValue::encode(thisObject)" : ""}); - return result; - #endif + ASSERT_WITH_MESSAGE(!JSValue::decode(result).isEmpty() or DECLARE_CATCH_SCOPE(vm).exception() != 0, \"${typeName}.${proto[name].fn} returned an empty value without an exception\"); - return ${symbolName(typeName, proto[name].fn)}(thisObject->wrapped(), lexicalGlobalObject, callFrame); + ${ + !proto[name].DOMJIT + ? "" + : ` + JSValue decoded = JSValue::decode(result); + if (thisObject->m_${fn}_expectedResultType) { + if (decoded.isCell() && !decoded.isEmpty()) { + ASSERT_WITH_MESSAGE(thisObject->m_${fn}_expectedResultType.value().has_value(), "DOMJIT function return type changed!"); + ASSERT_WITH_MESSAGE(thisObject->m_${fn}_expectedResultType.value().value() == decoded.asCell()->type(), "DOMJIT function return type changed!"); + } else { + ASSERT_WITH_MESSAGE(!thisObject->m_${fn}_expectedResultType.value().has_value(), "DOMJIT function return type changed!"); + } + } else if (!decoded.isEmpty()) { + thisObject->m_${fn}_expectedResultType = decoded.isCell() + ? std::optional(decoded.asCell()->type()) + : std::optional(std::nullopt); + }` } + + return result; +#endif + + return ${symbolName(typeName, proto[name].fn)}(thisObject->wrapped(), lexicalGlobalObject, callFrame${proto[name].passThis ? ", JSValue::encode(thisObject)" : ""}); +} + `); } } @@ -1098,7 +1173,7 @@ JSC_DEFINE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap, (JSGlobalObjec } } - return rows.join("\n"); + return rows.map(a => a.trim()).join("\n"); } var extraIncludes = []; @@ -1229,6 +1304,8 @@ function generateClassHeader(typeName, obj: ClassDefinition) { }) .join("\n")} + ${domJITTypeCheckFields(proto, klass)} + ${weakOwner} ${DECLARE_VISIT_CHILDREN} @@ -1237,7 +1314,24 @@ function generateClassHeader(typeName, obj: ClassDefinition) { ${callbacks ? renderCallbacksHeader(typeName, obj.callbacks) : ""} }; ${suffix} - `; + `.trim(); +} + +function domJITTypeCheckFields(proto, klass) { + var output = "#if BUN_DEBUG\n"; + for (const name in proto) { + const { DOMJIT, fn } = proto[name]; + if (!DOMJIT) continue; + output += `std::optional> m_${fn}_expectedResultType = std::nullopt;\n`; + } + + for (const name in klass) { + const { DOMJIT, fn } = klass[name]; + if (!DOMJIT) continue; + output += `std::optional> m_${fn}_expectedResultType = std::nullopt;\n`; + } + output += "#endif\n"; + return output; } function generateClassImpl(typeName, obj: ClassDefinition) { @@ -1327,8 +1421,10 @@ ${renderCallbacksCppImpl(typeName, callbacks)} } if (hasPendingActivity) { + externs += + `extern JSC_CALLCONV bool JSC_HOST_CALL_ATTRIBUTES ${symbolName(typeName, "hasPendingActivity")}(void* ptr);` + + "\n"; output += ` - extern "C" bool ${symbolName(typeName, "hasPendingActivity")}(void* ptr); bool ${name}::hasPendingActivity(void* ctx) { return ${symbolName(typeName, "hasPendingActivity")}(ctx); } @@ -1336,9 +1432,8 @@ ${renderCallbacksCppImpl(typeName, callbacks)} } if (getInternalProperties) { + externs += `extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${symbolName(typeName, "getInternalProperties")}(void* ptr, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue thisValue);`; output += ` - extern "C" EncodedJSValue ${symbolName(typeName, "getInternalProperties")}(void* ptr, JSC::JSGlobalObject *globalObject, EncodedJSValue thisValue); - JSC::JSValue getInternalProperties(JSC::VM &, JSC::JSGlobalObject *globalObject, ${name}* castedThis) { return JSValue::decode(${symbolName(typeName, "getInternalProperties")}(castedThis->impl(), globalObject, JSValue::encode(castedThis))); @@ -1388,7 +1483,7 @@ ${name}* ${name}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::St return ptr; } -extern "C" void* ${typeName}__fromJS(JSC::EncodedJSValue value) { +extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ${typeName}__fromJS(JSC::EncodedJSValue value) { JSC::JSValue decodedValue = JSC::JSValue::decode(value); if (decodedValue.isEmpty() || !decodedValue.isCell()) return nullptr; @@ -1402,7 +1497,7 @@ extern "C" void* ${typeName}__fromJS(JSC::EncodedJSValue value) { return object->wrapped(); } -extern "C" void* ${typeName}__fromJSDirect(JSC::EncodedJSValue value) { +extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ${typeName}__fromJSDirect(JSC::EncodedJSValue value) { JSC::JSValue decodedValue = JSC::JSValue::decode(value); ASSERT(decodedValue.isCell()); @@ -1421,7 +1516,7 @@ extern "C" void* ${typeName}__fromJSDirect(JSC::EncodedJSValue value) { return object->wrapped(); } -extern "C" bool ${typeName}__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr) { +extern JSC_CALLCONV bool JSC_HOST_CALL_ATTRIBUTES ${typeName}__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr) { ${className(typeName)}* object = JSC::jsDynamicCast<${className(typeName)}*>(JSValue::decode(value)); if (!object) return false; @@ -1438,7 +1533,7 @@ void ${name}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast<${name}*>(cell); if (void* wrapped = thisObject->wrapped()) { // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + // analyzer.setLabelForCell(cell, makeString("url ", thisObject->scriptExecutionContext()->url().string())); } Base::analyzeHeap(cell, analyzer); } @@ -1461,7 +1556,7 @@ JSObject* ${name}::createPrototype(VM& vm, JSDOMGlobalObject* globalObject) return ${prototypeName(typeName)}::create(vm, globalObject, structure); } -extern "C" EncodedJSValue ${typeName}__create(Zig::GlobalObject* globalObject, void* ptr) { +extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${typeName}__create(Zig::GlobalObject* globalObject, void* ptr) { auto &vm = globalObject->vm(); JSC::Structure* structure = globalObject->${className(typeName)}Structure(); ${className(typeName)}* instance = ${className(typeName)}::create(vm, globalObject, structure, ptr); @@ -1523,27 +1618,9 @@ function generateZig( ) { const exports = new Map(); - if (construct && !noConstructor) { - exports.set(`constructor`, classSymbolName(typeName, "construct")); - } - - if (call) { - exports.set(`call`, classSymbolName(typeName, "call")); - } - - if (finalize) { - exports.set(`finalize`, classSymbolName(typeName, "finalize")); - } - - if (estimatedSize) { - exports.set(`estimatedSize`, symbolName(typeName, "estimatedSize")); - } - if (hasPendingActivity) { exports.set("hasPendingActivity", symbolName(typeName, "hasPendingActivity")); } - Object.values(klass).map(a => appendSymbols(exports, name => classSymbolName(typeName, name), a)); - Object.values(proto).map(a => appendSymbols(exports, name => protoSymbolName(typeName, name), a)); if (getInternalProperties) { exports.set("getInternalProperties", symbolName(typeName, "getInternalProperties")); @@ -1566,9 +1643,9 @@ function generateZig( .filter(([name, { cache, internal }]) => (cache && typeof cache !== "string") || internal) .map( ([name]) => - `extern fn ${protoSymbolName(typeName, name)}SetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + `extern fn ${protoSymbolName(typeName, name)}SetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(JSC.conv) void; - extern fn ${protoSymbolName(typeName, name)}GetCachedValue(JSC.JSValue) JSC.JSValue; + extern fn ${protoSymbolName(typeName, name)}GetCachedValue(JSC.JSValue) callconv(JSC.conv) JSC.JSValue; /// \`${typeName}.${name}\` setter /// This value will be visited by the garbage collector. @@ -1596,173 +1673,221 @@ function generateZig( renderedCallbacks = renderCallbacksZig(typeName, callbacks); } - function typeCheck() { - var output = ""; + function renderMethods() { + const exports = new Map(); + var output = ` +const JavaScriptCoreBindings = struct { + +`; if (estimatedSize) { + exports.set("estimatedSize", symbolName(typeName, "estimatedSize")); output += ` - if (@TypeOf(${typeName}.estimatedSize) != (fn(*${typeName}) callconv(.C) usize)) { - @compileLog("${typeName}.estimatedSize is not a size function"); + pub fn ${symbolName(typeName, "estimatedSize")}(thisValue: *${typeName}) callconv(JSC.conv) usize { + return @call(.always_inline, ${typeName}.estimatedSize, .{thisValue}); } `; } - if (structuredClone) { + if (hasPendingActivity) { + exports.set("hasPendingActivity", symbolName(typeName, "hasPendingActivity")); output += ` - if (@TypeOf(${typeName}.onStructuredCloneSerialize) != (fn(*${typeName}, globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, writeBytes: *const fn(*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void) callconv(.C) void)) { - @compileLog("${typeName}.onStructuredCloneSerialize is not a structured clone serialize function"); - } - `; - - if (getInternalProperties) { - output += ` - if (@TypeOf(${typeName}.getInternalProperties) != (fn(*${typeName}, globalThis: *JSC.JSGlobalObject, JSC.JSValue thisValue) callconv(.C) JSC.JSValue { - @compileLog("${typeName}.getInternalProperties is not a getInternalProperties function"); + pub fn ${symbolName(typeName, "hasPendingActivity")}(thisValue: *${typeName}) callconv(JSC.conv) bool { + return @call(.always_inline, ${typeName}.hasPendingActivity, .{thisValue}); } - `; - } + `; + } - if (structuredClone === "transferable") { - exports.set("structuredClone", symbolName(typeName, "onTransferableStructuredClone")); - output += ` - if (@TypeOf(${typeName}.onStructuredCloneTransfer) != (fn(*${typeName}, globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, write: *const fn(*anyopaque, ptr: [*]const u8, len: usize) callconv(.C) void) callconv(.C) void)) { - @compileLog("${typeName}.onStructuredCloneTransfer is not a structured clone transfer function"); + if (finalize) { + exports.set("finalize", classSymbolName(typeName, "finalize")); + output += ` + pub fn ${classSymbolName(typeName, "finalize")}(thisValue: *${typeName}) callconv(JSC.conv) void { + if (comptime Environment.enable_logs) zig("~${typeName} 0x{x:8}", .{@intFromPtr(thisValue)}); + @call(.always_inline, ${typeName}.finalize, .{thisValue}); } - `; - } + `; + } + if (construct && !noConstructor) { + exports.set("construct", classSymbolName(typeName, "construct")); output += ` - if (@TypeOf(${typeName}.onStructuredCloneDeserialize) != (fn(globalThis: *JSC.JSGlobalObject, ptr: [*]u8, end: [*]u8) callconv(.C) JSC.JSValue)) { - @compileLog("${typeName}.onStructuredCloneDeserialize is not a structured clone deserialize function"); - } + pub fn ${classSymbolName(typeName, "construct")}(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(JSC.conv) ?*${typeName} { + if (comptime Environment.enable_logs) zig("new ${typeName}({})", .{callFrame}); + return @call(.always_inline, ${typeName}.constructor, .{globalObject, callFrame}); + } `; } - if (construct && !noConstructor) { + if (call) { + exports.set("call", classSymbolName(typeName, "call")); output += ` - if (@TypeOf(${typeName}.constructor) != (fn(*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*${typeName})) { - @compileLog("${typeName}.constructor is not a constructor"); + pub fn ${classSymbolName(typeName, "call")}(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) zig("${typeName}({})", .{callFrame}); + return @call(.always_inline, ${typeName}.call, .{globalObject, callFrame}); } `; } - if (finalize) { + if (getInternalProperties) { + exports.set("getInternalProperties", classSymbolName(typeName, "getInternalProperties")); output += ` - if (@TypeOf(${typeName}.finalize) != (fn(*${typeName}) callconv(.C) void)) { - @compileLog("${typeName}.finalize is not a finalizer"); + pub fn ${classSymbolName(typeName, "getInternalProperties")}(thisValue: *${typeName}, globalObject: *JSC.JSGlobalObject, thisValue: JSC.JSValue) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + return @call(.always_inline, ${typeName}.getInternalProperties, .{thisValue, globalObject, thisValue}); } `; } - [...Object.values(proto)].forEach(({ getter, setter, accessor, fn, this: thisValue = false, cache, DOMJIT }) => { - if (accessor) { - getter = accessor.getter; - setter = accessor.setter; - } + { + const exportNames = name => zigExportName(exports, name => protoSymbolName(typeName, name), proto[name]); + for (const name in proto) { + const { getter, setter, accessor, fn, this: thisValue = false, cache, DOMJIT } = proto[name]; + const names = exportNames(name); + if (names.getter) { + output += ` + pub fn ${names.getter}(this: *${typeName}, ${thisValue ? "thisValue: JSC.JSValue," : ""} globalObject: *JSC.JSGlobalObject) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) zig("get ${typeName}.${name}", .{}); + return @call(.always_inline, ${typeName}.${getter}, .{this, ${thisValue ? "thisValue," : ""} globalObject}); + } + `; + } - if (getter) { - if (thisValue) { + if (names.setter) { output += ` - if (@TypeOf(${typeName}.${getter}) != GetterTypeWithThisValue) - @compileLog("Expected ${typeName}.${getter} to be a getter with thisValue");`; - } else { + pub fn ${names.setter}(this: *${typeName}, ${thisValue ? "thisValue: JSC.JSValue," : ""} globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(JSC.conv) bool { + if (comptime Environment.enable_logs) zig("set ${typeName}.${name} = {}", .{value}); + return @call(.always_inline, ${typeName}.${setter}, .{this, ${thisValue ? "thisValue," : ""} globalObject, value}); + } + `; + } + + if (names.fn) { + if (names.DOMJIT) { + const { args, returns } = DOMJIT; + output += ` + pub fn ${names.DOMJIT}(thisValue: *${typeName}, globalObject: *JSC.JSGlobalObject, ${args + .map(ZigDOMJITArgTypeDefinition) + .join(", ")}) callconv(JSC.conv) JSC.JSValue { + return @call(.always_inline, ${typeName}.${DOMJITName(fn)}, .{thisValue, globalObject, ${args.map((_, i) => `arg${i}`).join(", ")}}); + } + `; + } + output += ` - if (@TypeOf(${typeName}.${getter}) != GetterType) - @compileLog( - "Expected ${typeName}.${getter} to be a getter" - ); -`; + pub fn ${names.fn}(thisValue: *${typeName}, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame${proto[name].passThis ? ", js_this_value: JSC.JSValue" : ""}) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) zig("${typeName}.${name}({})", .{callFrame}); + return @call(.always_inline, ${typeName}.${fn}, .{thisValue, globalObject, callFrame${proto[name].passThis ? ", js_this_value" : ""}}); + } + `; } } + } - if (setter) { - if (thisValue) { - output += ` - if (@TypeOf(${typeName}.${setter}) != SetterTypeWithThisValue) - @compileLog("Expected ${typeName}.${setter} to be a setter with thisValue");`; - } else { + { + const exportNames = name => zigExportName(exports, name => classSymbolName(typeName, name), klass[name]); + for (const name in klass) { + const { getter, setter, accessor, fn, this: thisValue = true, cache, DOMJIT } = klass[name]; + const names = exportNames(name); + if (names.getter) { output += ` - if (@TypeOf(${typeName}.${setter}) != SetterType) - @compileLog( - "Expected ${typeName}.${setter} to be a setter" - );`; + pub fn ${names.getter}(globalObject: *JSC.JSGlobalObject, ${thisValue ? "thisValue: JSC.JSValue," : ""} propertyName: JSC.JSValue) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + return @call(.always_inline, ${typeName}.${getter}, .{globalObject, ${thisValue ? "thisValue," : ""} propertyName}); + } + `; } - } - if (fn) { - if (DOMJIT) { + if (names.setter) { output += ` - if (@TypeOf(${typeName}.${DOMJITName(fn)}) != ${ZigDOMJITFunctionType(typeName, DOMJIT)}) - @compileLog( - "Expected ${typeName}.${DOMJITName(fn)} to be a DOMJIT function" - );`; + pub fn ${names.setter}(globalObject: *JSC.JSGlobalObject, thisValue: JSC.JSValue, target: JSC.JSValue) callconv(JSC.conv) bool { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + return @call(.always_inline, ${typeName}.${setter || accessor.setter}, .{thisValue, globalObject, target}); + } + `; } - output += ` - if (@TypeOf(${typeName}.${fn}) != CallbackType) - @compileLog( - "Expected ${typeName}.${fn} to be a callback but received " ++ @typeName(@TypeOf(${typeName}.${fn})) - );`; - } - }); + if (names.fn) { + if (DOMJIT) { + const { args, returns } = DOMJIT; - [...Object.values(klass)].forEach(({ getter, setter, accessor, fn }) => { - if (accessor) { - getter = accessor.getter; - setter = accessor.setter; - } + output += ` + pub fn ${names.DOMJIT}(globalObject: *JSC.JSGlobalObject, thisValue: JSC.JSValue, ${args + .map(ZigDOMJITArgTypeDefinition) + .join(", ")}) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + return @call(.always_inline, ${typeName}.${DOMJITName(fn)}, .{thisValue, globalObject, ${args.map((_, i) => `arg${i}`).join(", ")}}); + } + `; + } - if (getter) { - output += ` - if (@TypeOf(${typeName}.${getter}) != StaticGetterType) - @compileLog( - "Expected ${typeName}.${getter} to be a static getter" - ); -`; + output += ` + pub fn ${names.fn}(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + return @call(.always_inline, ${typeName}.${fn}, .{globalObject, callFrame}); + } + `; + } } + } - if (setter) { - output += ` - if (@TypeOf(${typeName}.${setter}) != StaticSetterType) - @compileLog( - "Expected ${typeName}.${setter} to be a static setter" - );`; + if (structuredClone) { + exports.set("structuredClone", symbolName(typeName, "onStructuredCloneSerialize")); + output += ` + pub fn ${symbolName(typeName, "onStructuredCloneSerialize")}(thisValue: *${typeName}, globalObject: *JSC.JSGlobalObject, ctx: *anyopaque, writeBytes: WriteBytesFn) callconv(JSC.conv) void { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + @call(.always_inline, ${typeName}.onStructuredCloneSerialize, .{thisValue, globalObject, ctx, writeBytes}); } + `; - if (fn) { + if (structuredClone === "transferable") { + exports.set("structuredClone_transferable", symbolName(typeName, "onStructuredCloneTransfer")); output += ` - if (@TypeOf(${typeName}.${fn}) != StaticCallbackType) - @compileLog( - "Expected ${typeName}.${fn} to be a static callback" - );`; + pub fn ${exports.get("structuredClone_transferable")}(thisValue: *${typeName}, globalObject: *JSC.JSGlobalObject, ctx: *anyopaque, write: WriteBytesFn) callconv(JSC.conv) void { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + @call(.always_inline, ${typeName}.onStructuredCloneTransfer, .{thisValue, globalObject, ctx, write}); + } + `; } - }); - if (!!call) { + exports.set("structuredCloneDeserialize", symbolName(typeName, "onStructuredCloneDeserialize")); + output += ` - if (@TypeOf(${typeName}.call) != StaticCallbackType) - @compileLog( - "Expected ${typeName}.call to be a static callback" - );`; + pub fn ${symbolName(typeName, "onStructuredCloneDeserialize")}(globalObject: *JSC.JSGlobalObject, ptr: [*]u8, end: [*]u8) callconv(JSC.conv) JSC.JSValue { + if (comptime Environment.enable_logs) JSC.markBinding(@src()); + return @call(.always_inline, ${typeName}.onStructuredCloneDeserialize, .{globalObject, ptr, end}); + } + `; + } else { + output += ` + pub fn ${symbolName(typeName, "onStructuredCloneSerialize")}(thisValue: *${typeName}, globalObject: *JSC.JSGlobalObject, ctx: *anyopaque, writeBytes: WriteBytesFn) callconv(JSC.conv) void { + _ = thisValue; + _ = globalObject; + _ = ctx; + _ = writeBytes; + @compileLog("onStructuredCloneSerialize not implemented for ${typeName}"); + } + `; } - return output; + return ( + output.trim() + + ` + }; + comptime { +${[...exports.values()].map(name => ` @export(JavaScriptCoreBindings.${name}, .{ .name = "${name}" });`).join("\n")} + }` + ); } return ` pub const ${className(typeName)} = struct { const ${typeName} = Classes.${typeName}; - const GetterType = fn(*${typeName}, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; - const GetterTypeWithThisValue = fn(*${typeName}, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; - const SetterType = fn(*${typeName}, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; - const SetterTypeWithThisValue = fn(*${typeName}, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; - const CallbackType = fn(*${typeName}, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; /// Return the pointer to the wrapped object. /// If the object does not match the type, return null. pub fn fromJS(value: JSC.JSValue) ?*${typeName} { - JSC.markBinding(@src()); + if (comptime Environment.enable_logs) zig("${typeName}.fromJS", .{}); return ${symbolName(typeName, "fromJS")}(value); } @@ -1771,7 +1896,7 @@ pub const ${className(typeName)} = struct { /// If the object is a subclass of the type or has mutated the structure, return null. /// Note: this may return null for direct instances of the type if the user adds properties to the object. pub fn fromJSDirect(value: JSC.JSValue) ?*${typeName} { - JSC.markBinding(@src()); + if (comptime Environment.enable_logs) zig("${typeName}.fromJSDirect", .{}); return ${symbolName(typeName, "fromJSDirect")}(value); } @@ -1783,7 +1908,7 @@ pub const ${className(typeName)} = struct { /// Get the ${typeName} constructor value. /// This loads lazily from the global object. pub fn getConstructor(globalObject: *JSC.JSGlobalObject) JSC.JSValue { - JSC.markBinding(@src()); + if (comptime Environment.enable_logs) zig("${typeName}.getConstructor", .{}); return ${symbolName(typeName, "getConstructor")}(globalObject); } ` @@ -1795,7 +1920,7 @@ pub const ${className(typeName)} = struct { ? ` /// Create a new instance of ${typeName} pub fn toJS(this: *${typeName}, globalObject: *JSC.JSGlobalObject) JSC.JSValue { - JSC.markBinding(@src()); + if (comptime Environment.enable_logs) zig("${typeName}.toJS", .{}); if (comptime Environment.allow_assert) { const value__ = ${symbolName(typeName, "create")}(globalObject, this); @import("root").bun.assert(value__.as(${typeName}).? == this); // If this fails, likely a C ABI issue. @@ -1819,31 +1944,20 @@ pub const ${className(typeName)} = struct { bun.assert(${symbolName(typeName, "dangerouslySetPtr")}(value, null)); } - extern fn ${symbolName(typeName, "fromJS")}(JSC.JSValue) ?*${typeName}; - extern fn ${symbolName(typeName, "fromJSDirect")}(JSC.JSValue) ?*${typeName}; - extern fn ${symbolName(typeName, "getConstructor")}(*JSC.JSGlobalObject) JSC.JSValue; - - extern fn ${symbolName(typeName, "create")}(globalObject: *JSC.JSGlobalObject, ptr: ?*${typeName}) JSC.JSValue; + extern fn ${symbolName(typeName, "fromJS")}(JSC.JSValue) callconv(JSC.conv) ?*${typeName}; + extern fn ${symbolName(typeName, "fromJSDirect")}(JSC.JSValue) callconv(JSC.conv) ?*${typeName}; + extern fn ${symbolName(typeName, "getConstructor")}(*JSC.JSGlobalObject) callconv(JSC.conv) JSC.JSValue; + extern fn ${symbolName(typeName, "create")}(globalObject: *JSC.JSGlobalObject, ptr: ?*${typeName}) callconv(JSC.conv) JSC.JSValue; /// Create a new instance of ${typeName} without validating it works. pub const toJSUnchecked = ${symbolName(typeName, "create")}; - extern fn ${typeName}__dangerouslySetPtr(JSC.JSValue, ?*${typeName}) bool; + extern fn ${typeName}__dangerouslySetPtr(JSC.JSValue, ?*${typeName}) callconv(JSC.conv) bool; - ${renderedCallbacks} +${renderMethods()} - comptime { - ${typeCheck()} - if (!JSC.is_bindgen) { -${[...exports] - .sort(([a], [b]) => a.localeCompare(b)) - .map(([internalName, externalName]) => `@export(${typeName}.${internalName}, .{.name = "${externalName}"});`) - .join("\n ")} - } - } }; - `; } @@ -1904,7 +2018,7 @@ using namespace JSC; `, ]; -const GENERATED_CLASSES_IMPL_HEADER = ` +const GENERATED_CLASSES_IMPL_HEADER_PRE = ` // GENERATED CODE - DO NOT MODIFY BY HAND // Generated by make codegen #include "root.h" @@ -1928,10 +2042,19 @@ const GENERATED_CLASSES_IMPL_HEADER = ` #include "JSDOMConvertBufferSource.h" #include "ZigGeneratedClasses.h" +#include "ErrorCode+List.h" +#include "ErrorCode.h" +#if !OS(WINDOWS) +#define JSC_CALLCONV "C" +#else +#define JSC_CALLCONV "C" SYSV_ABI +#endif +`; +const GENERATED_CLASSES_IMPL_HEADER_POST = ` namespace WebCore { using namespace JSC; @@ -1988,10 +2111,12 @@ const JSC = bun.JSC; const Classes = JSC.GeneratedClassesList; const Environment = bun.Environment; const std = @import("std"); +const zig = bun.Output.scoped(.zig, true); -pub const StaticGetterType = fn(*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue) callconv(.C) JSC.JSValue; -pub const StaticSetterType = fn(*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue, JSC.JSValue) callconv(.C) bool; -pub const StaticCallbackType = fn(*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; +pub const StaticGetterType = fn(*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue) callconv(JSC.conv) JSC.JSValue; +pub const StaticSetterType = fn(*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue, JSC.JSValue) callconv(JSC.conv) bool; +pub const StaticCallbackType = JSC.JSHostFunctionType; +pub const WriteBytesFn = *const fn(*anyopaque, ptr: [*]const u8, len: u32) callconv(JSC.conv) void; `; @@ -2024,12 +2149,13 @@ for (const obj of classes) { const GENERATED_CLASSES_FOOTER = ` +typedef SYSV_ABI void (*CppStructuredCloneableSerializeFunction)(CloneSerializer*, const uint8_t*, uint32_t); +typedef SYSV_ABI void (*ZigStructuredCloneableSerializeFunction)(void*, JSC::JSGlobalObject*, CloneSerializer*, CppStructuredCloneableSerializeFunction); + class StructuredCloneableSerialize { public: - - void (*cppWriteBytes)(CloneSerializer*, const uint8_t*, uint32_t); - - std::function zigFunction; + CppStructuredCloneableSerializeFunction cppWriteBytes; + ZigStructuredCloneableSerializeFunction zigFunction; uint8_t tag; @@ -2118,9 +2244,13 @@ if (!process.env.ONLY_ZIG) { ...allHeaders, GENERATED_CLASSES_FOOTER, ]); + + const allImpls = classes.map(a => generateImpl(a.name, a)); await writeIfNotChanged(`${outBase}/ZigGeneratedClasses.cpp`, [ - GENERATED_CLASSES_IMPL_HEADER, - ...classes.map(a => generateImpl(a.name, a)), + GENERATED_CLASSES_IMPL_HEADER_PRE, + externs.trim(), + GENERATED_CLASSES_IMPL_HEADER_POST, + allImpls.join("\n"), writeCppSerializers(classes), GENERATED_CLASSES_IMPL_FOOTER, ]); diff --git a/src/codegen/generate-js2native.ts b/src/codegen/generate-js2native.ts new file mode 100644 index 0000000000000..1d9ffee55ce4a --- /dev/null +++ b/src/codegen/generate-js2native.ts @@ -0,0 +1,234 @@ +// This file implements the global state for $zig and $cpp preprocessor macros +// as well as all the code it generates. +// +// For the actual parsing, see replacements.ts + +import path, { basename, sep } from "path"; +import { readdirRecursiveWithExclusionsAndExtensionsSync } from "./helpers"; + +// +interface NativeCall { + id: number; + type: NativeCallType; + filename: string; + symbol: string; + is_wrapped: boolean; +} + +interface WrapperCall { + type: NativeCallType; + wrap_kind: "new-function"; + symbol_taget: string; + symbol_generated: string; + display_name: string; + call_length: number; + filename: string; +} + +type NativeCallType = "zig" | "cpp"; + +const nativeCalls: NativeCall[] = []; +const wrapperCalls: WrapperCall[] = []; + +const sourceFiles = readdirRecursiveWithExclusionsAndExtensionsSync( + path.join(import.meta.dir, "../"), + ["deps", "node_modules", "WebKit"], + [".cpp", ".zig"], +); + +function callBaseName(x: string) { + return x.split(/[^A-Za-z0-9]/g).pop()!; +} + +function resolveNativeFileId(call_type: NativeCallType, filename: string) { + if (!filename.endsWith("." + call_type)) { + throw new Error( + `Expected filename for $${call_type} to have .${call_type} extension, got ${JSON.stringify(filename)}`, + ); + } + + const resolved = sourceFiles.find(file => file.endsWith(sep + filename)); + if (!resolved) { + throw new Error(`Could not find file ${filename} in $${call_type} call`); + } + + if (call_type === "zig") { + return resolved; + } + + return filename; +} + +export function registerNativeCall( + call_type: NativeCallType, + filename: string, + symbol: string, + create_fn_len: null | number, +) { + const resolved_filename = resolveNativeFileId(call_type, filename); + + const maybe_wrapped_symbol = create_fn_len != null ? "js2native_wrap_" + symbol.replace(/[^A-Za-z]/g, "_") : symbol; + + const existing = nativeCalls.find( + call => + call.is_wrapped == (create_fn_len != null) && + call.filename === resolved_filename && + call.symbol === maybe_wrapped_symbol, + ); + if (existing) { + return existing.id; + } + + const id = nativeCalls.length; + nativeCalls.push({ + id, + type: create_fn_len != null ? "cpp" : call_type, + filename: resolved_filename, + symbol: maybe_wrapped_symbol, + is_wrapped: create_fn_len != null, + }); + if (create_fn_len != null) { + wrapperCalls.push({ + type: call_type, + wrap_kind: "new-function", + symbol_taget: symbol, + symbol_generated: "js2native_wrap_" + symbol.replace(/[^A-Za-z]/g, "_"), + display_name: callBaseName(symbol), + call_length: create_fn_len, + filename: resolved_filename, + }); + } + return id; +} + +function symbol(call: Pick) { + return call.type === "zig" + ? `JS2Zig__${call.filename ? normalizeSymbolPathPrefix(call.filename) + "_" : ""}${call.symbol.replace(/[^A-Za-z]/g, "_")}` + : call.symbol; +} + +function normalizeSymbolPathPrefix(input: string) { + input = path.resolve(input); + + const bunDir = path.resolve(path.join(import.meta.dir, "..", "..")); + if (input.startsWith(bunDir)) { + input = input.slice(bunDir.length); + } + + return input.replaceAll(".zig", "_zig_").replace(/[^A-Za-z]/g, "_"); +} + +function cppPointer(call: NativeCall) { + return `&${symbol(call)}`; +} + +export function getJS2NativeCPP() { + const files = [ + ...new Set(nativeCalls.filter(x => x.filename.endsWith(".cpp")).map(x => x.filename.replace(/.cpp$/, ".h"))), + ]; + + const externs: string[] = []; + + const nativeCallStrings = nativeCalls + .filter(x => x.type === "zig") + .flatMap( + call => ( + externs.push(`extern "C" SYSV_ABI JSC::EncodedJSValue ${symbol(call)}_workaround(Zig::GlobalObject*);` + "\n"), + [ + `JSC::JSValue ${symbol(call)}(Zig::GlobalObject* global) {`, + ` return JSValue::decode(${symbol(call)}_workaround(global));`, + `}` + "\n\n", + ] + ), + ); + + const wrapperCallStrings = wrapperCalls.map(x => { + if (x.wrap_kind === "new-function") { + return [ + (x.type === "zig" && + externs.push( + `BUN_DECLARE_HOST_FUNCTION(${symbol({ + type: "zig", + symbol: x.symbol_taget, + })});`, + ), + "") || "", + `JSC::JSValue ${x.symbol_generated}(Zig::GlobalObject* globalObject) {`, + ` return JSC::JSFunction::create(globalObject->vm(), globalObject, ${x.call_length}, ${JSON.stringify( + x.display_name, + )}_s, ${symbol({ type: x.type, symbol: x.symbol_taget })}, JSC::ImplementationVisibility::Public);`, + `}`, + ].join("\n"); + } + throw new Error(`Unknown wrap kind ${x.wrap_kind}`); + }); + + return [ + `#pragma once`, + `#include "root.h"`, + ...files.map(filename => `#include ${JSON.stringify(filename)}`), + ...externs, + "\n" + "namespace JS2NativeGenerated {", + "using namespace Bun;", + "using namespace JSC;", + "using namespace WebCore;" + "\n", + ...nativeCallStrings, + ...wrapperCallStrings, + `typedef JSC::JSValue (*JS2NativeFunction)(Zig::GlobalObject*);`, + `static JS2NativeFunction js2nativePointers[] = {`, + ...nativeCalls.map(x => ` ${cppPointer(x)},`), + `};`, + `};`, + `#define JS2NATIVE_COUNT ${nativeCalls.length}`, + ].join("\n"); +} + +export function getJS2NativeZig(gs2NativeZigPath: string) { + return [ + "//! This file is generated by src/codegen/generate-js2native.ts based on seen calls to the $zig() JS macro", + `const JSC = @import("root").bun.JSC;`, + ...nativeCalls + .filter(x => x.type === "zig") + .flatMap(call => [ + `export fn ${symbol(call)}_workaround(global: *JSC.JSGlobalObject) callconv(JSC.conv) JSC.JSValue {`, + ` return @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), call.filename))}).${ + call.symbol + }(global);`, + "}", + ]), + ...wrapperCalls + .filter(x => x.type === "zig") + .flatMap(x => [ + `export fn ${symbol({ + type: "zig", + symbol: x.symbol_taget, + })}(global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue {`, + ` + const function = @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), x.filename))}); + return @call(.always_inline, function.${x.symbol_taget}, .{global, call_frame});`, + "}", + ]), + "comptime {", + ...nativeCalls.filter(x => x.type === "zig").flatMap(call => ` _ = &${symbol(call)}_workaround;`), + ...wrapperCalls + .filter(x => x.type === "zig") + .flatMap(x => ` _ = &${symbol({ type: "zig", symbol: x.symbol_taget })};`), + "}", + ].join("\n"); +} + +export function getJS2NativeDTS() { + return [ + "declare type NativeFilenameCPP = " + + sourceFiles + .filter(x => x.endsWith("cpp")) + .map(x => JSON.stringify(basename(x))) + .join("|"), + "declare type NativeFilenameZig = " + + sourceFiles + .filter(x => x.endsWith("zig")) + .map(x => JSON.stringify(basename(x))) + .join("|"), + "", + ].join("\n"); +} diff --git a/src/codegen/generate-jssink.ts b/src/codegen/generate-jssink.ts index 9288da8fb9610..b8e760e3fb77f 100644 --- a/src/codegen/generate-jssink.ts +++ b/src/codegen/generate-jssink.ts @@ -1,4 +1,4 @@ -import { resolve, join } from "path"; +import { join, resolve } from "path"; const classes = ["ArrayBufferSink", "FileSink", "HTTPResponseSink", "HTTPSResponseSink"]; @@ -713,7 +713,7 @@ void ${className}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) if (void* wrapped = thisObject->wrapped()) { analyzer.setWrappedObjectForCell(cell, wrapped); // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + // analyzer.setLabelForCell(cell, makeString("url ", thisObject->scriptExecutionContext()->url().string())); } Base::analyzeHeap(cell, analyzer); } @@ -724,7 +724,7 @@ void ${controller}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) if (void* wrapped = thisObject->wrapped()) { analyzer.setWrappedObjectForCell(cell, wrapped); // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + // analyzer.setLabelForCell(cell, makeString("url ", thisObject->scriptExecutionContext()->url().string())); } Base::analyzeHeap(cell, analyzer); } @@ -896,13 +896,14 @@ extern "C" void ${name}__onReady(JSC__JSValue controllerValue, JSC__JSValue amt, if (!function) return; JSC::JSGlobalObject *globalObject = controller->globalObject(); - + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::MarkedArgumentBuffer arguments; arguments.append(controller); arguments.append(JSC::JSValue::decode(amt)); arguments.append(JSC::JSValue::decode(offset)); AsyncContextFrame::call(globalObject, function, JSC::jsUndefined(), arguments); + RELEASE_AND_RETURN(scope, void()); } extern "C" void ${name}__onStart(JSC__JSValue controllerValue) @@ -920,13 +921,14 @@ extern "C" void ${name}__onClose(JSC__JSValue controllerValue, JSC__JSValue reas // only call close once controller->m_onClose.clear(); JSC::JSGlobalObject* globalObject = controller->globalObject(); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::MarkedArgumentBuffer arguments; auto readableStream = controller->m_weakReadableStream.get(); arguments.append(readableStream ? readableStream : JSC::jsUndefined()); - arguments.append(JSC::JSValue::decode(reason)); AsyncContextFrame::call(globalObject, function, JSC::jsUndefined(), arguments); + RELEASE_AND_RETURN(scope, void()); } `; diff --git a/src/codegen/generate-node-errors.ts b/src/codegen/generate-node-errors.ts new file mode 100644 index 0000000000000..e8683237f1c1d --- /dev/null +++ b/src/codegen/generate-node-errors.ts @@ -0,0 +1,128 @@ +import path from "node:path"; +import NodeErrors from "../bun.js/bindings/ErrorCode.ts"; +const outputDir = process.argv[2]; + +if (!outputDir) { + throw new Error("Missing output directory"); +} + +let enumHeader = ``; +let listHeader = ``; +let zig = ``; + +enumHeader = ` +// clang-format off +// Generated by: src/codegen/generate-node-errors.ts +#pragma once + +namespace Bun { + static constexpr size_t NODE_ERROR_COUNT = ${NodeErrors.length}; + enum class ErrorCode : uint8_t { +`; + +listHeader = ` +// clang-format off +// Generated by: src/codegen/generate-node-errors.ts +#pragma once + +struct ErrorCodeData { + JSC::ErrorType type; + WTF::ASCIILiteral name; + WTF::ASCIILiteral code; +}; +static constexpr ErrorCodeData errors[${NodeErrors.length}] = { +`; + +zig = ` +// Generated by: src/codegen/generate-node-errors.ts +const std = @import("std"); +const bun = @import("root").bun; +const JSC = bun.JSC; + +fn ErrorBuilder(comptime code_: Error, comptime fmt_: [:0]const u8, Args: type) type { + return struct { + const code = code_; + const fmt = fmt_; + globalThis: *JSC.JSGlobalObject, + args: Args, + + // Throw this error as a JS exception + pub inline fn throw(this: @This()) void { + code.throw(this.globalThis, fmt, this.args); + } + + /// Turn this into a JSValue + pub inline fn toJS(this: @This()) JSC.JSValue { + return code.fmt(this.globalThis, fmt, this.args); + } + + /// Turn this into a JSPromise that is already rejected. + pub inline fn reject(this: @This()) JSC.JSValue { + return JSC.JSPromise.rejectedPromiseValue(this.globalThis, code.fmt(this.globalThis, fmt, this.args)); + } + + }; +} + +pub const Error = enum(u8) { + +`; + +let i = 0; +let listForUsingNamespace = ""; +for (const [code, constructor, name] of NodeErrors) { + enumHeader += ` ${code} = ${i},\n`; + listHeader += ` { JSC::ErrorType::${constructor.name}, "${name}"_s, "${code}"_s },\n`; + zig += ` ${code} = ${i},\n`; + listForUsingNamespace += ` /// ${name}: ${code} (instanceof ${constructor.name})\n`; + listForUsingNamespace += ` pub inline fn ${code}(globalThis: *JSC.JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) ErrorBuilder(Error.${code}, fmt, @TypeOf(args)) {\n`; + listForUsingNamespace += ` return .{ .globalThis = globalThis, .args = args };\n`; + listForUsingNamespace += ` }\n`; + i++; +} + +enumHeader += ` +}; +} // namespace Bun +`; + +listHeader += ` +}; +`; + +zig += ` + + + extern fn Bun__createErrorWithCode(globalThis: *JSC.JSGlobalObject, code: Error, message: *bun.String) JSC.JSValue; + + /// Creates an Error object with the given error code. + /// Derefs the message string. + pub fn toJS(this: Error, globalThis: *JSC.JSGlobalObject, message: *bun.String) JSC.JSValue { + defer message.deref(); + return Bun__createErrorWithCode(globalThis, this, message); + } + + pub fn fmt(this: Error, globalThis: *JSC.JSGlobalObject, comptime fmt_str: [:0]const u8, args: anytype) JSC.JSValue { + if (comptime std.meta.fieldNames(@TypeOf(args)).len == 0) { + var message = bun.String.static(fmt_str); + return toJS(this, globalThis, &message); + } + + var message = bun.String.createFormat(fmt_str, args) catch bun.outOfMemory(); + return toJS(this, globalThis, &message); + } + + pub fn throw(this: Error, globalThis: *JSC.JSGlobalObject, comptime fmt_str: [:0]const u8, args: anytype) void { + globalThis.throwValue(fmt(this, globalThis, fmt_str, args)); + } + +}; + +pub const JSGlobalObjectExtensions = struct { +${listForUsingNamespace} +}; +`; + +await Bun.write(path.join(outputDir, "ErrorCode+List.h"), enumHeader); +await Bun.write(path.join(outputDir, "ErrorCode+Data.h"), listHeader); +await Bun.write(path.join(outputDir, "ErrorCode.zig"), zig); diff --git a/src/codegen/helpers.ts b/src/codegen/helpers.ts index 463372a02d983..bfd6cdafcc1b0 100644 --- a/src/codegen/helpers.ts +++ b/src/codegen/helpers.ts @@ -1,6 +1,6 @@ +import { isAscii } from "buffer"; import fs from "fs"; import path from "path"; -import { isAscii } from "buffer"; // MSVC has a max of 16k characters per string literal // Combining string literals didn't support constexpr apparently @@ -18,7 +18,17 @@ export function fmtCPPCharArray(str: string, nullTerminated: boolean = true) { .join(",") + (nullTerminated ? ",0" : "") + "}"; - return [chars, normalized.length + (nullTerminated ? 1 : 0)]; + return [chars, normalized.length + (nullTerminated ? 1 : 0)] as const; +} + +export function addCPPCharArray(str: string, nullTerminated: boolean = true) { + const normalized = str.trim() + "\n"; + return ( + normalized + .split("") + .map(a => a.charCodeAt(0)) + .join(",") + (nullTerminated ? ",0" : "") + ); } export function declareASCIILiteral(name: string, value: string) { @@ -65,13 +75,14 @@ export function checkAscii(str: string) { export function writeIfNotChanged(file: string, contents: string) { if (Array.isArray(contents)) contents = contents.join(""); + contents = contents.replaceAll("\r\n", "\n").trim() + "\n"; - if (fs.existsSync(file)) { + try { const oldContents = fs.readFileSync(file, "utf8"); if (oldContents === contents) { return; } - } + } catch (e) {} try { fs.writeFileSync(file, contents); @@ -79,6 +90,10 @@ export function writeIfNotChanged(file: string, contents: string) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, contents); } + + if (fs.readFileSync(file, "utf8") !== contents) { + throw new Error(`Failed to write file ${file}`); + } } export function readdirRecursiveWithExclusionsAndExtensionsSync( @@ -105,3 +120,13 @@ export function pathToUpperSnakeCase(filepath: string) { .join("_") .toUpperCase(); } + +export function camelCase(string: string) { + return string + .split(/[\s_]/) + .map((e, i) => (i ? e.charAt(0).toUpperCase() + e.slice(1).toLowerCase() : e.toLowerCase())); +} + +export function pascalCase(string: string) { + return string.split(/[\s_]/).map((e, i) => (i ? e.charAt(0).toUpperCase() + e.slice(1) : e.toLowerCase())); +} diff --git a/src/codegen/js2native-generator.ts b/src/codegen/js2native-generator.ts deleted file mode 100644 index 09c71f8217b31..0000000000000 --- a/src/codegen/js2native-generator.ts +++ /dev/null @@ -1,207 +0,0 @@ -// This file implements the global state for $zig and $cpp preprocessor macros -// as well as all the code it generates. -// -// For the actual parsing, see replacements.ts - -import path, { basename, sep } from "path"; -import { readdirRecursiveWithExclusionsAndExtensionsSync } from "./helpers"; - -// -interface NativeCall { - id: number; - type: NativeCallType; - filename: string; - symbol: string; - is_wrapped: boolean; -} - -interface WrapperCall { - type: NativeCallType; - wrap_kind: "new-function"; - symbol_taget: string; - symbol_generated: string; - display_name: string; - call_length: number; - filename: string; -} - -type NativeCallType = "zig" | "cpp"; - -const nativeCalls: NativeCall[] = []; -const wrapperCalls: WrapperCall[] = []; - -const sourceFiles = readdirRecursiveWithExclusionsAndExtensionsSync( - path.join(import.meta.dir, "../"), - ["deps", "node_modules", "WebKit"], - [".cpp", ".zig"], -); - -function callBaseName(x: string) { - return x.split(/[^A-Za-z0-9]/g).pop()!; -} - -function resolveNativeFileId(call_type: NativeCallType, filename: string) { - if (!filename.endsWith("." + call_type)) { - throw new Error( - `Expected filename for $${call_type} to have .${call_type} extension, got ${JSON.stringify(filename)}`, - ); - } - - const resolved = sourceFiles.find(file => file.endsWith(sep + filename)); - if (!resolved) { - throw new Error(`Could not find file ${filename} in $${call_type} call`); - } - - if (call_type === "zig") { - return resolved; - } - - return filename; -} - -export function registerNativeCall( - call_type: NativeCallType, - filename: string, - symbol: string, - create_fn_len: null | number, -) { - const resolved_filename = resolveNativeFileId(call_type, filename); - - const maybe_wrapped_symbol = create_fn_len != null ? "js2native_wrap_" + symbol.replace(/[^A-Za-z]/g, "_") : symbol; - - const existing = nativeCalls.find( - call => - call.is_wrapped == (create_fn_len != null) && - call.filename === resolved_filename && - call.symbol === maybe_wrapped_symbol, - ); - if (existing) { - return existing.id; - } - - const id = nativeCalls.length; - nativeCalls.push({ - id, - type: create_fn_len != null ? "cpp" : call_type, - filename: resolved_filename, - symbol: maybe_wrapped_symbol, - is_wrapped: create_fn_len != null, - }); - if (create_fn_len != null) { - wrapperCalls.push({ - type: call_type, - wrap_kind: "new-function", - symbol_taget: symbol, - symbol_generated: "js2native_wrap_" + symbol.replace(/[^A-Za-z]/g, "_"), - display_name: callBaseName(symbol), - call_length: create_fn_len, - filename: resolved_filename, - }); - } - return id; -} - -function symbol(call: Pick) { - return call.type === "zig" ? `JS2Zig__${call.symbol.replace(/[^A-Za-z]/g, "_")}` : call.symbol; -} - -function cppPointer(call: NativeCall) { - return `&${symbol(call)}`; -} - -export function getJS2NativeCPP() { - const files = [ - ...new Set(nativeCalls.filter(x => x.filename.endsWith(".cpp")).map(x => x.filename.replace(/.cpp$/, ".h"))), - ]; - - return [ - `#pragma once`, - ...files.map(filename => `#include ${JSON.stringify(filename)}`), - "namespace JS2NativeGenerated {", - "using namespace Bun;", - "using namespace JSC;", - "using namespace WebCore;", - ...nativeCalls - .filter(x => x.type === "zig") - .flatMap(call => [ - `extern "C" JSC::EncodedJSValue ${symbol(call)}_workaround(Zig::GlobalObject*);`, - `JSC::JSValue ${symbol(call)}(Zig::GlobalObject* global) {`, - ` return JSValue::decode(${symbol(call)}_workaround(global));`, - `}`, - ]), - ...wrapperCalls.map(x => { - if (x.wrap_kind === "new-function") { - return [ - x.type === "zig" - ? `extern "C" JSC::EncodedJSValue ${symbol({ - type: "zig", - symbol: x.symbol_taget, - })}(JSC::JSGlobalObject*, JSC::CallFrame*);` - : "", - `JSC::JSValue ${x.symbol_generated}(Zig::GlobalObject* globalObject) {`, - ` return JSC::JSFunction::create(globalObject->vm(), globalObject, ${x.call_length}, ${JSON.stringify( - x.display_name, - )}_s, ${symbol({ type: x.type, symbol: x.symbol_taget })}, JSC::ImplementationVisibility::Public);`, - `}`, - ].join("\n"); - } - throw new Error(`Unknown wrap kind ${x.wrap_kind}`); - }), - `typedef JSC::JSValue (*JS2NativeFunction)(Zig::GlobalObject*);`, - `static JS2NativeFunction js2nativePointers[] = {`, - ...nativeCalls.map(x => ` ${cppPointer(x)},`), - `};`, - `};`, - `#define JS2NATIVE_COUNT ${nativeCalls.length}`, - ].join("\n"); -} - -export function getJS2NativeZig(gs2NativeZigPath: string) { - return [ - "//! This file is generated by src/codegen/js2native-generator.ts based on seen calls to the $zig() JS macro", - `const JSC = @import("root").bun.JSC;`, - ...nativeCalls - .filter(x => x.type === "zig") - .flatMap(call => [ - `export fn ${symbol(call)}_workaround(global: u64) callconv(.C) JSC.JSValue {`, - ` return @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), call.filename))}).${ - call.symbol - }(@ptrFromInt(global));`, - "}", - ]), - ...wrapperCalls - .filter(x => x.type === "zig") - .flatMap(x => [ - `export fn ${symbol({ - type: "zig", - symbol: x.symbol_taget, - })}(global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) callconv(.C) JSC.JSValue {`, - ` return @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), x.filename))}).${ - x.symbol_taget - }(global, call_frame);`, - "}", - ]), - "comptime {", - ...nativeCalls.filter(x => x.type === "zig").flatMap(call => ` _ = &${symbol(call)}_workaround;`), - ...wrapperCalls - .filter(x => x.type === "zig") - .flatMap(x => ` _ = &${symbol({ type: "zig", symbol: x.symbol_taget })};`), - "}", - ].join("\n"); -} - -export function getJS2NativeDTS() { - return [ - "declare type NativeFilenameCPP = " + - sourceFiles - .filter(x => x.endsWith("cpp")) - .map(x => JSON.stringify(basename(x))) - .join("|"), - "declare type NativeFilenameZig = " + - sourceFiles - .filter(x => x.endsWith("zig")) - .map(x => JSON.stringify(basename(x))) - .join("|"), - "", - ].join("\n"); -} diff --git a/src/codegen/kit-codegen.ts b/src/codegen/kit-codegen.ts new file mode 100644 index 0000000000000..e6ec16d40a4e3 --- /dev/null +++ b/src/codegen/kit-codegen.ts @@ -0,0 +1,152 @@ +import { join, basename } from 'node:path'; +import { writeFileSync, existsSync } from 'node:fs'; +import assert from 'node:assert'; + +// arg parsing +const options = {}; +for (const arg of process.argv.slice(2)) { + if(!arg.startsWith('--')) { + console.error('Unknown argument ' + arg); + process.exit(1); + } + const split = arg.split('='); + const value = split[1] || 'true'; + options[split[0].slice(2)] = value; +} + +let { codegen_root, debug } = options as any; +if (!codegen_root) {console.error('Missing --codegen_root=...'); process.exit(1);} +if (debug === 'false' || debug === '0' || debug == 'OFF') debug = false; + +const kit_dir = join(import.meta.dirname, '../kit'); +process.chdir(kit_dir); // to make bun build predictable in development + +const results = await Promise.allSettled(['client', 'server'].map(async mode => { + let result = await Bun.build({ + entrypoints: [join(kit_dir, 'hmr-runtime.ts')], + define: { + mode: JSON.stringify(mode), + IS_BUN_DEVELOPMENT: String(!!debug), + }, + minify: { + syntax: true, + } + }); + if(!result.success) throw new AggregateError(result.logs); + assert(result.outputs.length === 1, 'must bundle to a single file'); + // @ts-ignore + let code = await result.outputs[0].text(); + + // A second pass is used to convert global variables into parameters, while + // allowing for renaming to properly function when minification is enabled. + const in_names = [ + 'input_graph', + 'config', + mode === 'server' && 'server_fetch_function' + ].filter(Boolean); + const combined_source = ` + __marker__; + let ${in_names.join(',')}; + __marker__(${in_names.join(',')}); + ${code}; + `; + const generated_entrypoint = join(kit_dir, `.runtime-${mode}.generated.ts`); + + writeFileSync(generated_entrypoint, combined_source); + using _ = {[Symbol.dispose] : () => { + try { + rmSync(generated_entrypoint); + } catch {} + }}; + + result = await Bun.build({ + entrypoints: [generated_entrypoint], + minify: { + syntax: true, + whitespace: !debug, + identifiers: !debug, + } + }); + if(!result.success) throw new AggregateError(result.logs); + assert(result.outputs.length === 1, 'must bundle to a single file'); + // @ts-ignore + code = await result.outputs[0].text(); + + let names: string = ''; + code = code + .replace(/(\n?)\s*__marker__.*__marker__\((.+?)\);\s*/s, (_, n, captured) => { + names = captured; + return n; + }) + .replace(`// ${basename(generated_entrypoint)}`, '') + .trim(); + assert(names, 'missing name'); + + if (debug) { + code = '\n ' + code.replace(/\n/g, '\n ') + '\n'; + } + + if (code[code.length - 1] === ';') code = code.slice(0, -1); + + if (mode === 'server') { + const server_fetch_function = names.split(',')[2].trim(); + code = debug + ? `${code} return ${server_fetch_function};\n` + : `${code};return ${server_fetch_function};` + } + + code = debug + ? `((${names}) => {${code}})({\n` + : `((${names})=>{${code}})({`; + + if (mode === 'server') { + code = `export default await ${code}`; + } + + writeFileSync(join(codegen_root, `kit.${mode}.js`), code); +})); + +// print failures in a de-duplicated fashion. +interface Err { kind: 'client' | 'server' | 'both', err: any } +const failed = [ + { kind: 'client', result: results[0] }, + { kind: 'server', result: results[1] }, +] + .filter(x => x.result.status === 'rejected') + .map(x => ({ kind: x.kind, err: x.result.reason })) as Err[]; +if(failed.length > 0) { + const flattened_errors: Err[] = []; + for (const { kind, err } of failed) { + if (err instanceof AggregateError) { + flattened_errors.push(...err.errors.map(err => ({ kind, err }))); + } + flattened_errors.push({ kind, err }); + } + for(let i = 0; i < flattened_errors.length; i++) { + const x = flattened_errors[i]; + if (!x.err?.message) continue; + for (const other of flattened_errors.slice(0, i)) { + if(other.err?.message === x.err.message || other.err.stack === x.err.stack) { + other.kind = 'both'; + flattened_errors.splice(i, 1); + i -= 1; + continue; + } + } + } + let current = ''; + for(const { kind, err } of flattened_errors) { + if(kind !== current) { + const map = { both: 'runtime', client: 'client runtime', server: 'server runtime' } + console.error(`Errors while bundling Kit ${map[kind]}:`); + } + console.error(err); + } + process.exit(1); +} else { + console.log('-> kit.client.js, kit.server.js'); + + const empty_file = join(codegen_root, 'kit_empty_file'); + if (!existsSync(empty_file)) + writeFileSync(empty_file, 'this is used to fulfill a cmake dependency'); +} diff --git a/src/codegen/replacements.ts b/src/codegen/replacements.ts index 79500c57db7a3..3d20dc4fa12e2 100644 --- a/src/codegen/replacements.ts +++ b/src/codegen/replacements.ts @@ -1,6 +1,7 @@ import { LoaderKeys } from "../api/schema"; +import NodeErrors from "../bun.js/bindings/ErrorCode.ts"; import { sliceSourceCode } from "./builtin-parser"; -import { registerNativeCall } from "./js2native-generator"; +import { registerNativeCall } from "./generate-js2native"; // This is a list of extra syntax replacements to do. Kind of like macros // These are only run on code itself, not string contents or comments. @@ -12,6 +13,14 @@ export const replacements: ReplacementRule[] = [ { from: /\bexport\s*default/g, to: "$exports =" }, ]; +for (let i = 0; i < NodeErrors.length; i++) { + const [code] = NodeErrors[i]; + replacements.push({ + from: new RegExp(`\\b\\__intrinsic__${code}\\(`, "g"), + to: `$makeErrorWithCode(${i}, `, + }); +} + // These rules are run on the entire file, including within strings. export const globalReplacements: ReplacementRule[] = [ { diff --git a/src/compile_target.zig b/src/compile_target.zig index 240c824bcc74d..67d0c0aab104c 100644 --- a/src/compile_target.zig +++ b/src/compile_target.zig @@ -12,7 +12,7 @@ const Output = bun.Output; const CompileTarget = @This(); os: Environment.OperatingSystem = Environment.os, -arch: Environment.Archictecture = Environment.arch, +arch: Environment.Architecture = Environment.arch, baseline: bool = !Environment.enableSIMD, version: bun.Semver.Version = .{ .major = @truncate(Environment.version.major), @@ -137,7 +137,7 @@ const HTTP = bun.http; const MutableString = bun.MutableString; const Global = bun.Global; pub fn downloadToPath(this: *const CompileTarget, env: *bun.DotEnv.Loader, allocator: std.mem.Allocator, dest_z: [:0]const u8) !void { - try HTTP.HTTPThread.init(); + HTTP.HTTPThread.init(); var refresher = bun.Progress{}; { @@ -153,7 +153,6 @@ pub fn downloadToPath(this: *const CompileTarget, env: *bun.DotEnv.Loader, alloc { var progress = refresher.start("Downloading", 0); defer progress.end(); - const timeout = 30000; const http_proxy: ?bun.URL = env.getHttpProxy(url); async_http.* = HTTP.AsyncHTTP.initSync( @@ -164,14 +163,12 @@ pub fn downloadToPath(this: *const CompileTarget, env: *bun.DotEnv.Loader, alloc "", compressed_archive_bytes, "", - timeout, http_proxy, null, HTTP.FetchRedirect.follow, ); - async_http.client.timeout = timeout; async_http.client.progress_node = progress; - async_http.client.reject_unauthorized = env.getTLSRejectUnauthorized(); + async_http.client.flags.reject_unauthorized = env.getTLSRejectUnauthorized(); const response = try async_http.sendSync(true); @@ -294,8 +291,10 @@ pub fn downloadToPath(this: *const CompileTarget, env: *bun.DotEnv.Loader, alloc const dirname = bun.path.dirname(dest_z, .loose); if (dirname.len > 0) { std.fs.cwd().makePath(dirname) catch {}; + continue; } - continue; + + // fallthrough, failed for another reason } node.end(); Output.err(err, "Failed to move cross-compiled bun binary into cache directory {}", .{bun.fmt.fmtPath(u8, dest_z, .{})}); @@ -340,7 +339,7 @@ pub fn from(input_: []const u8) CompileTarget { const token = splitter.next() orelse break; if (token.len == 0) continue; - if (Environment.Archictecture.names.get(token)) |arch| { + if (Environment.Architecture.names.get(token)) |arch| { this.arch = arch; found_arch = true; continue; diff --git a/src/comptime_string_map.zig b/src/comptime_string_map.zig index 28eedeca421f4..0dad921d9fa76 100644 --- a/src/comptime_string_map.zig +++ b/src/comptime_string_map.zig @@ -462,44 +462,3 @@ const TestEnum2 = enum { .{ "00", .FL }, }); }; - -pub fn compareString(input: []const u8) !void { - const str = try std.heap.page_allocator.dupe(u8, input); - if (TestEnum2.map.has(str) != TestEnum2.official.has(str)) { - std.debug.panic("{s} - TestEnum2.map.has(str) ({d}) != TestEnum2.official.has(str) ({d})", .{ - str, - @intFromBool(TestEnum2.map.has(str)), - @intFromBool(TestEnum2.official.has(str)), - }); - } - - std.debug.print("For string: \"{s}\" (has a match? {d})\n", .{ str, @intFromBool(TestEnum2.map.has(str)) }); - - var is_eql = false; - var timer = try std.time.Timer.start(); - - for (0..99999999) |_| { - is_eql = @call(.never_inline, TestEnum2.map.has, .{str}); - } - const new = timer.lap(); - - std.debug.print("- new {}\n", .{std.fmt.fmtDuration(new)}); - - for (0..99999999) |_| { - is_eql = @call(.never_inline, TestEnum2.official.has, .{str}); - } - - const _std = timer.lap(); - - std.debug.print("- std {}\n\n", .{std.fmt.fmtDuration(_std)}); -} - -pub fn main() anyerror!void { - try compareString("naaaaaa"); - try compareString("nothinz"); - try compareString("these"); - try compareString("incommon"); - try compareString("noMatch"); - try compareString("0"); - try compareString("00"); -} diff --git a/src/copy_file.zig b/src/copy_file.zig index c3c7f2afaa4b1..1d66e8ed047ed 100644 --- a/src/copy_file.zig +++ b/src/copy_file.zig @@ -22,8 +22,6 @@ pub const CopyFileRangeError = error{ FileBusy, } || posix.PReadError || posix.PWriteError || posix.UnexpectedError; -const CopyFileError = error{SystemResources} || CopyFileRangeError || posix.SendFileError; - const InputType = if (Environment.isWindows) bun.OSPathSliceZ else posix.fd_t; /// In a `bun install` with prisma, this reduces the system call count from ~18,000 to ~12,000 @@ -58,16 +56,18 @@ const LinuxCopyFileState = packed struct { }; const EmptyCopyFileState = struct {}; pub const CopyFileState = if (Environment.isLinux) LinuxCopyFileState else EmptyCopyFileState; -pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFileState) CopyFileError!void { +const CopyFileReturnType = bun.sys.Maybe(void); + +pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFileState) CopyFileReturnType { if (comptime Environment.isMac) { const rc = posix.system.fcopyfile(in, out, null, posix.system.COPYFILE_DATA); + switch (posix.errno(rc)) { - .SUCCESS => return, - .NOMEM => return error.SystemResources, + .SUCCESS => return CopyFileReturnType.success, // The source file is not a directory, symbolic link, or regular file. // Try with the fallback path before giving up. .OPNOTSUPP => {}, - else => |err| return posix.unexpectedErrno(err), + else => return CopyFileReturnType.errnoSys(rc, .copyfile).?, } } @@ -79,7 +79,7 @@ pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFi // the ordering is flipped but it is consistent with other system calls. bun.sys.syslog("ioctl_ficlone({d}, {d}) = {d}", .{ in, out, rc }); switch (bun.C.getErrno(rc)) { - .SUCCESS => return, + .SUCCESS => return CopyFileReturnType.success, .XDEV => { copy_file_state.has_seen_exdev = true; }, @@ -106,45 +106,37 @@ pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFi // The kernel checks the u64 value `offset+count` for overflow, use // a 32 bit value so that the syscall won't return EINVAL except for // impossibly large files (> 2^64-1 - 2^32-1). - const amt = try copyFileRange(in, out, math.maxInt(i32) - 1, 0, copy_file_state); + const amt = switch (copyFileRange(in, out, math.maxInt(i32) - 1, 0, copy_file_state)) { + .result => |a| a, + .err => |err| return .{ .err = err }, + }; // Terminate when no data was copied if (amt == 0) break :cfr_loop; offset += amt; } - return; + return CopyFileReturnType.success; } if (comptime Environment.isWindows) { - if (bun.windows.CopyFileW(in.ptr, out.ptr, 0) == bun.windows.FALSE) { - switch (@as(bun.C.E, @enumFromInt(@intFromEnum(bun.windows.GetLastError())))) { - .SUCCESS => return, - .FBIG => return error.FileTooBig, - .IO => return error.InputOutput, - .ISDIR => return error.IsDir, - .NOMEM => return error.OutOfMemory, - .NOSPC => return error.NoSpaceLeft, - .OVERFLOW => return error.Unseekable, - .PERM => return error.PermissionDenied, - .TXTBSY => return error.FileBusy, - else => return error.Unexpected, - } + if (CopyFileReturnType.errnoSys(bun.windows.CopyFileW(in.ptr, out.ptr, 0), .copyfile)) |err| { + return err; } - return; + return CopyFileReturnType.success; } - // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the - // fallback code will copy the contents chunk by chunk. - const empty_iovec = [0]posix.iovec_const{}; - var offset: u64 = 0; - sendfile_loop: while (true) { - const amt = try posix.sendfile(out, in, offset, 0, &empty_iovec, &empty_iovec, 0); - // Terminate when no data was copied - if (amt == 0) break :sendfile_loop; - offset += amt; + while (true) { + switch (copyFileReadWriteLoop(in, out, math.maxInt(i32) - 1)) { + .err => |err| return .{ .err = err }, + .result => |amt| { + if (amt == 0) break; + }, + } } + + return CopyFileReturnType.success; } -pub fn copyFile(in: InputType, out: InputType) CopyFileError!void { +pub fn copyFile(in: InputType, out: InputType) CopyFileReturnType { var state: CopyFileState = .{}; return copyFileWithState(in, out, &state); } @@ -215,14 +207,15 @@ pub fn can_use_ioctl_ficlone() bool { } const fd_t = std.posix.fd_t; +const Maybe = bun.sys.Maybe; -pub fn copyFileRange(in: fd_t, out: fd_t, len: usize, flags: u32, copy_file_state: *CopyFileState) CopyFileRangeError!usize { +pub fn copyFileRange(in: fd_t, out: fd_t, len: usize, flags: u32, copy_file_state: *CopyFileState) Maybe(usize) { if (canUseCopyFileRangeSyscall() and !copy_file_state.has_seen_exdev and !copy_file_state.has_copy_file_range_failed) { while (true) { const rc = std.os.linux.copy_file_range(in, null, out, null, len, flags); bun.sys.syslog("copy_file_range({d}, {d}, {d}) = {d}", .{ in, out, len, rc }); switch (bun.C.getErrno(rc)) { - .SUCCESS => return @as(usize, @intCast(rc)), + .SUCCESS => return .{ .result = @intCast(rc) }, // these may not be regular files, try fallback .INVAL => { copy_file_state.has_copy_file_range_failed = true; @@ -253,7 +246,7 @@ pub fn copyFileRange(in: fd_t, out: fd_t, len: usize, flags: u32, copy_file_stat const rc = std.os.linux.sendfile(@intCast(out), @intCast(in), null, len); bun.sys.syslog("sendfile({d}, {d}, {d}) = {d}", .{ in, out, len, rc }); switch (bun.C.getErrno(rc)) { - .SUCCESS => return @as(usize, @intCast(rc)), + .SUCCESS => return .{ .result = @intCast(rc) }, .INTR => continue, // these may not be regular files, try fallback .INVAL => { @@ -276,9 +269,36 @@ pub fn copyFileRange(in: fd_t, out: fd_t, len: usize, flags: u32, copy_file_stat break; } + return copyFileReadWriteLoop(in, out, len); +} + +pub fn copyFileReadWriteLoop( + in: fd_t, + out: fd_t, + len: usize, +) Maybe(usize) { var buf: [8 * 4096]u8 = undefined; const adjusted_count = @min(buf.len, len); - const amt_read = try posix.read(in, buf[0..adjusted_count]); - if (amt_read == 0) return 0; - return posix.write(out, buf[0..amt_read]); + switch (bun.sys.read(bun.toFD(in), buf[0..adjusted_count])) { + .result => |amt_read| { + var amt_written: usize = 0; + if (amt_read == 0) return .{ .result = 0 }; + + while (amt_written < amt_read) { + switch (bun.sys.write(bun.toFD(out), buf[amt_written..amt_read])) { + .result => |wrote| { + if (wrote == 0) { + return .{ .result = amt_written }; + } + + amt_written += wrote; + }, + .err => |err| return .{ .err = err }, + } + } + if (amt_read == 0) return .{ .result = 0 }; + return .{ .result = amt_read }; + }, + .err => |err| return .{ .err = err }, + } } diff --git a/src/crash_handler.zig b/src/crash_handler.zig index 9755bb398032b..23dd84ef49dfe 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -32,7 +32,7 @@ const debug = std.debug; /// This is useful for testing as a crash in here will not 'panicked during a panic'. pub const enable = true; -/// Override with BUN_CRASH_REPORT_URL enviroment variable. +/// Overridable with BUN_CRASH_REPORT_URL environment variable. const default_report_base_url = "https://bun.report"; /// Only print the `Bun has crashed` message once. Once this is true, control @@ -50,6 +50,17 @@ var panic_mutex = std.Thread.Mutex{}; /// This is used to catch and handle panics triggered by the panic handler. threadlocal var panic_stage: usize = 0; +/// This can be set by various parts of the codebase to indicate a broader +/// action being taken. It is printed when a crash happens, which can help +/// narrow down what the bug is. Example: "Crashed while parsing /path/to/file.js" +/// +/// Some of these are enabled in release builds, which may encourage users to +/// attach the affected files to crash report. Others, which may have low crash +/// rate or only crash due to assertion failures, are debug-only. See `Action`. +pub threadlocal var current_action: ?Action = null; + +const CPUFeatures = @import("./bun.js/bindings/CPUFeatures.zig").CPUFeatures; + /// This structure and formatter must be kept in sync with `bun.report`'s decoder implementation. pub const CrashReason = union(enum) { /// From @panic() @@ -75,9 +86,9 @@ pub const CrashReason = union(enum) { out_of_memory, - pub fn format(self: CrashReason, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - switch (self) { - .panic => try writer.print("{s}", .{self.panic}), + pub fn format(reason: CrashReason, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + switch (reason) { + .panic => |message| try writer.print("{s}", .{message}), .@"unreachable" => try writer.writeAll("reached unreachable code"), .segmentation_fault => |addr| try writer.print("Segmentation fault at address 0x{X}", .{addr}), .illegal_instruction => |addr| try writer.print("Illegal instruction at address 0x{X}", .{addr}), @@ -91,7 +102,64 @@ pub const CrashReason = union(enum) { } }; -/// This function is invoked when a crash happpens. A crash is classified in `CrashReason`. +pub const Action = union(enum) { + parse: []const u8, + visit: []const u8, + print: []const u8, + + /// bun.bundle_v2.LinkerContext.generateCompileResultForJSChunk + bundle_generate_chunk: if (bun.Environment.isDebug) struct { + context: *const anyopaque, // unfortunate dependency loop workaround + chunk: *const bun.bundle_v2.Chunk, + part_range: *const bun.bundle_v2.PartRange, + + pub fn linkerContext(data: *const @This()) *const bun.bundle_v2.LinkerContext { + return @ptrCast(@alignCast(data.context)); + } + } else void, + + resolver: if (bun.Environment.isDebug) struct { + source_dir: []const u8, + import_path: []const u8, + kind: bun.ImportKind, + } else void, + + pub fn format(act: Action, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + switch (act) { + .parse => |path| try writer.print("parsing {s}", .{path}), + .visit => |path| try writer.print("visiting {s}", .{path}), + .print => |path| try writer.print("printing {s}", .{path}), + .bundle_generate_chunk => |data| if (bun.Environment.isDebug) { + try writer.print( + \\generating bundler chunk + \\ chunk entry point: {s} + \\ source: {s} + \\ part range: {d}..{d} + , + .{ + data.linkerContext().graph.bundler_graph.input_files + .items(.source)[data.chunk.entry_point.source_index] + .path.text, + data.linkerContext().graph.bundler_graph.input_files + .items(.source)[data.part_range.source_index.get()] + .path.text, + data.part_range.part_index_begin, + data.part_range.part_index_end, + }, + ); + }, + .resolver => |res| if (bun.Environment.isDebug) { + try writer.print("resolving {s} from {s} ({s})", .{ + res.import_path, + res.source_dir, + res.kind.label(), + }); + }, + } + } +}; + +/// This function is invoked when a crash happens. A crash is classified in `CrashReason`. pub fn crashHandler( reason: CrashReason, // TODO: if both of these are specified, what is supposed to happen? @@ -100,10 +168,6 @@ pub fn crashHandler( ) noreturn { @setCold(true); - // If a segfault happens while panicking, we want it to actually segfault, not trigger - // the handler. - resetSegfaultHandler(); - if (bun.Environment.isDebug) bun.Output.disableScopedDebugWriter(); @@ -125,11 +189,11 @@ pub fn crashHandler( // is not configured correctly, so that would also mask the message. // // Output.errorWriter() is not used here because it may not be configured - // if the program crashes immediatly at startup. + // if the program crashes immediately at startup. const writer = std.io.getStdErr().writer(); // The format of the panic trace is slightly different in debug - // builds Mainly, we demangle the backtrace immediately instead + // builds. Mainly, we demangle the backtrace immediately instead // of using a trace string. // // To make the release-mode behavior easier to demo, debug mode @@ -140,6 +204,9 @@ pub fn crashHandler( break :check_flag false; } } + // Act like release build when explicitly enabling reporting + if (isReportingEnabled()) break :check_flag false; + break :check_flag true; }; @@ -155,11 +222,10 @@ pub fn crashHandler( } writer.writeAll("oh no") catch std.posix.abort(); if (Output.enable_ansi_colors) { - writer.writeAll(Output.prettyFmt(": ", true)) catch std.posix.abort(); + writer.writeAll(Output.prettyFmt(": multiple threads are crashing\n", true)) catch std.posix.abort(); } else { - writer.writeAll(Output.prettyFmt(": ", true)) catch std.posix.abort(); + writer.writeAll(Output.prettyFmt(": multiple threads are crashing\n", true)) catch std.posix.abort(); } - writer.writeAll("multiple threads are crashing") catch std.posix.abort(); } if (reason != .out_of_memory or debug_trace) { @@ -196,6 +262,10 @@ pub fn crashHandler( writer.print("{}\n", .{reason}) catch std.posix.abort(); } + if (current_action) |action| { + writer.print("Crashed while {}\n", .{action}) catch std.posix.abort(); + } + var addr_buf: [10]usize = undefined; var trace_buf: std.builtin.StackTrace = undefined; @@ -210,6 +280,8 @@ pub fn crashHandler( }; if (debug_trace) { + has_printed_message = true; + dumpStackTrace(trace.*); trace_str_buf.writer().print("{}", .{TraceString{ @@ -270,14 +342,24 @@ pub fn crashHandler( writer.writeAll("\n") catch std.posix.abort(); } } + // Be aware that this function only lets one thread return from it. // This is important so that we do not try to run the following reload logic twice. waitForOtherThreadToFinishPanicking(); report(trace_str_buf.slice()); + // At this point, the crash handler has performed it's job. Reset the segfault handler + // so that a crash will actually crash. We need this because we want the process to + // exit with a signal, and allow tools to be able to gather core dumps. + // + // This is done so late (in comparison to the Zig Standard Library's panic handler) + // because if multiple threads segfault (more often the case on Windows), we don't + // want another thread to interrupt the crashing of the first one. + resetSegfaultHandler(); + if (bun.auto_reload_on_crash and - // Do not reload if the panic arised FROM the reload function. + // Do not reload if the panic arose FROM the reload function. !bun.isProcessReloadInProgressOnAnotherThread()) { // attempt to prevent a double panic @@ -288,13 +370,15 @@ pub fn crashHandler( }); Output.flush(); - comptime std.debug.assert(void == @TypeOf(bun.reloadProcess(bun.default_allocator, false, true))); + comptime bun.assert(void == @TypeOf(bun.reloadProcess(bun.default_allocator, false, true))); bun.reloadProcess(bun.default_allocator, false, true); } }, inline 1, 2 => |t| { if (t == 1) { panic_stage = 2; + + resetSegfaultHandler(); Output.flush(); } panic_stage = 3; @@ -308,6 +392,7 @@ pub fn crashHandler( }, 3 => { // Panicked while printing "Panicked during a panic." + panic_stage = 4; }, else => { // Panicked or otherwise looped into the panic handler while trying to exit. @@ -621,7 +706,7 @@ else const metadata_version_line = std.fmt.comptimePrint( "Bun {s}v{s} {s} {s}{s}\n", .{ - if (bun.Environment.is_canary) "Canary " else "", + if (bun.Environment.isDebug) "Debug " else if (bun.Environment.is_canary) "Canary " else "", Global.package_json_version_with_sha, bun.Environment.os.displayString(), arch_display_string, @@ -633,7 +718,7 @@ fn handleSegfaultPosix(sig: i32, info: *const std.posix.siginfo_t, _: ?*const an const addr = switch (bun.Environment.os) { .linux => @intFromPtr(info.fields.sigfault.addr), .mac => @intFromPtr(info.addr), - else => unreachable, + else => @compileError(unreachable), }; crashHandler( @@ -678,6 +763,15 @@ pub fn updatePosixSegfaultHandler(act: ?*std.posix.Sigaction) !void { var windows_segfault_handle: ?windows.HANDLE = null; +pub fn resetOnPosix() void { + var act = std.posix.Sigaction{ + .handler = .{ .sigaction = handleSegfaultPosix }, + .mask = std.posix.empty_sigset, + .flags = (std.posix.SA.SIGINFO | std.posix.SA.RESTART | std.posix.SA.RESETHAND), + }; + updatePosixSegfaultHandler(&act) catch {}; +} + pub fn init() void { if (!enable) return; switch (bun.Environment.os) { @@ -685,18 +779,15 @@ pub fn init() void { windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); }, .mac, .linux => { - var act = std.posix.Sigaction{ - .handler = .{ .sigaction = handleSegfaultPosix }, - .mask = std.posix.empty_sigset, - .flags = (std.posix.SA.SIGINFO | std.posix.SA.RESTART | std.posix.SA.RESETHAND), - }; - updatePosixSegfaultHandler(&act) catch {}; + resetOnPosix(); }, else => @compileError("TODO"), } } pub fn resetSegfaultHandler() void { + if (!enable) return; + if (bun.Environment.os == .windows) { if (windows_segfault_handle) |handle| { const rc = windows.kernel32.RemoveVectoredExceptionHandler(handle); @@ -736,13 +827,44 @@ pub fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows ); } +extern "C" fn gnu_get_libc_version() ?[*:0]const u8; + pub fn printMetadata(writer: anytype) !void { if (Output.enable_ansi_colors) { try writer.writeAll(Output.prettyFmt("", true)); } + var is_ancient_cpu = false; + try writer.writeAll(metadata_version_line); { + const platform = bun.Analytics.GenerateHeader.GeneratePlatform.forOS(); + const cpu_features = CPUFeatures.get(); + if (bun.Environment.isLinux) { + // TODO: musl + const version = gnu_get_libc_version() orelse ""; + const kernel_version = bun.Analytics.GenerateHeader.GeneratePlatform.kernelVersion(); + if (platform.os == .wsl) { + try writer.print("WSL Kernel v{d}.{d}.{d} | glibc v{s}\n", .{ kernel_version.major, kernel_version.minor, kernel_version.patch, bun.sliceTo(version, 0) }); + } else { + try writer.print("Linux Kernel v{d}.{d}.{d} | glibc v{s}\n", .{ kernel_version.major, kernel_version.minor, kernel_version.patch, bun.sliceTo(version, 0) }); + } + } else if (bun.Environment.isMac) { + try writer.print("macOS v{s}\n", .{platform.version}); + } else if (bun.Environment.isWindows) { + try writer.print("Windows v{s}\n", .{std.zig.system.windows.detectRuntimeVersion()}); + } + + if (comptime bun.Environment.isX64) { + if (!cpu_features.avx and !cpu_features.avx2 and !cpu_features.avx512) { + is_ancient_cpu = true; + } + } + + if (!cpu_features.isEmpty()) { + try writer.print("CPU: {}\n", .{cpu_features}); + } + try writer.print("Args: ", .{}); var arg_chars_left: usize = if (bun.Environment.isDebug) 4096 else 196; for (bun.argv, 0..) |arg, i| { @@ -776,10 +898,12 @@ pub fn printMetadata(writer: anytype) !void { &peak_commit, &page_faults, ); - try writer.print("Elapsed: {d}ms | User: {d}ms | Sys: {d}ms\nRSS: {:<3.2} | Peak: {:<3.2} | Commit: {:<3.2} | Faults: {d}\n", .{ + try writer.print("Elapsed: {d}ms | User: {d}ms | Sys: {d}ms\n", .{ elapsed_msecs, user_msecs, system_msecs, + }); + try writer.print("RSS: {:<3.2} | Peak: {:<3.2} | Commit: {:<3.2} | Faults: {d}\n", .{ std.fmt.fmtIntSizeDec(current_rss), std.fmt.fmtIntSizeDec(peak_rss), std.fmt.fmtIntSizeDec(current_commit), @@ -791,6 +915,12 @@ pub fn printMetadata(writer: anytype) !void { try writer.writeAll(Output.prettyFmt("", true)); } try writer.writeAll("\n"); + + if (comptime bun.Environment.isX64) { + if (is_ancient_cpu) { + try writer.writeAll("CPU lacks AVX support. Please consider upgrading to a newer CPU.\n"); + } + } } fn waitForOtherThreadToFinishPanicking() void { @@ -806,6 +936,22 @@ fn waitForOtherThreadToFinishPanicking() void { } } +/// This is to be called by any thread that is attempting to exit the process. +/// If another thread is panicking, this will sleep this thread forever, under +/// the assumption that the crash handler will terminate the program. +/// +/// There have been situations in the past where a bundler thread starts +/// panicking, but the main thread ends up marking a test as passing and then +/// exiting with code zero before the crash handler can finish the crash. +pub fn sleepForeverIfAnotherThreadIsCrashing() void { + if (panicking.load(.acquire) > 0) { + // Sleep forever without hammering the CPU + var futex = std.atomic.Value(u32).init(0); + while (true) std.Thread.Futex.wait(&futex, 0); + comptime unreachable; + } +} + /// Each platform is encoded as a single character. It is placed right after the /// slash after the version, so someone just reading the trace string can tell /// what platform it came from. L, M, and W are for Linux, macOS, and Windows, @@ -869,7 +1015,11 @@ const StackLine = struct { .address = @intCast(addr - base_address), .object = if (!std.mem.eql(u16, name, image_path)) name: { - const basename = name[std.mem.lastIndexOfAny(u16, name, &[_]u16{ '\\', '/' }) orelse 0 ..]; + const basename = name[if (std.mem.lastIndexOfAny(u16, name, &[_]u16{ '\\', '/' })) |i| + // skip the last slash + i + 1 + else + 0..]; break :name bun.strings.convertUTF16toUTF8InBuffer(name_bytes, basename) catch null; } else null, }; @@ -1014,7 +1164,7 @@ const StackLine = struct { const TraceString = struct { trace: *const std.builtin.StackTrace, reason: CrashReason, - action: Action, + action: TraceString.Action, const Action = enum { /// Open a pre-filled GitHub issue with the expanded trace @@ -1391,7 +1541,7 @@ pub fn dumpStackTrace(trace: std.builtin.StackTrace) void { }, .linux => { // Linux doesnt seem to be able to decode it's own debug info. - // TODO(@paperdave): see if zig 0.12 fixes this + // TODO(@paperdave): see if zig 0.14 fixes this }, else => { stdDumpStackTrace(trace); @@ -1466,6 +1616,7 @@ pub const js_bindings = struct { .{ "panic", jsPanic }, .{ "rootError", jsRootError }, .{ "outOfMemory", jsOutOfMemory }, + .{ "raiseIgnoringPanicHandler", jsRaiseIgnoringPanicHandler }, }) |tuple| { const name = JSC.ZigString.static(tuple[0]); obj.put(global, name, JSC.createCallback(global, name, 1, tuple[1])); @@ -1473,17 +1624,17 @@ pub const js_bindings = struct { return obj; } - pub fn jsGetMachOImageZeroOffset(_: *bun.JSC.JSGlobalObject, _: *bun.JSC.CallFrame) callconv(.C) bun.JSC.JSValue { + pub fn jsGetMachOImageZeroOffset(_: *bun.JSC.JSGlobalObject, _: *bun.JSC.CallFrame) JSValue { if (!bun.Environment.isMac) return .undefined; const header = std.c._dyld_get_image_header(0) orelse return .undefined; const base_address = @intFromPtr(header); const vmaddr_slide = std.c._dyld_get_image_vmaddr_slide(0); - return bun.JSC.JSValue.jsNumber(base_address - vmaddr_slide); + return JSValue.jsNumber(base_address - vmaddr_slide); } - pub fn jsSegfault(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsSegfault(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { @setRuntimeSafety(false); const ptr: [*]align(1) u64 = @ptrFromInt(0xDEADBEEF); ptr[0] = 0xDEADBEEF; @@ -1491,29 +1642,33 @@ pub const js_bindings = struct { return .undefined; } - pub fn jsPanic(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsPanic(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { bun.crash_handler.panicImpl("invoked crashByPanic() handler", null, null); } - pub fn jsRootError(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsRootError(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { bun.crash_handler.handleRootError(error.Test, null); } - pub fn jsOutOfMemory(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsOutOfMemory(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { bun.outOfMemory(); } - pub fn jsGetFeaturesAsVLQ(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsRaiseIgnoringPanicHandler(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { + bun.Global.raiseIgnoringPanicHandler(.SIGSEGV); + } + + pub fn jsGetFeaturesAsVLQ(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { const bits = bun.Analytics.packedFeatures(); var buf = std.BoundedArray(u8, 16){}; writeU64AsTwoVLQs(buf.writer(), @bitCast(bits)) catch { - // there is definetly enough space in the bounded array + // there is definitely enough space in the bounded array unreachable; }; return bun.String.createLatin1(buf.slice()).toJS(global); } - pub fn jsGetFeatureData(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn jsGetFeatureData(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { const obj = JSValue.createEmptyObject(global, 5); const list = bun.Analytics.packed_features_list; const array = JSValue.createEmptyArray(global, list.len); @@ -1524,7 +1679,7 @@ pub const js_bindings = struct { obj.put(global, JSC.ZigString.static("version"), bun.String.init(Global.package_json_version).toJS(global)); obj.put(global, JSC.ZigString.static("is_canary"), JSC.JSValue.jsBoolean(bun.Environment.is_canary)); obj.put(global, JSC.ZigString.static("revision"), bun.String.init(bun.Environment.git_sha).toJS(global)); - obj.put(global, JSC.ZigString.static("generated_at"), bun.JSC.JSValue.jsNumberFromInt64(@max(std.time.milliTimestamp(), 0))); + obj.put(global, JSC.ZigString.static("generated_at"), JSValue.jsNumberFromInt64(@max(std.time.milliTimestamp(), 0))); return obj; } }; diff --git a/src/darwin_c.zig b/src/darwin_c.zig index 4e96ecd2205aa..cfea6b1f38671 100644 --- a/src/darwin_c.zig +++ b/src/darwin_c.zig @@ -298,11 +298,11 @@ pub const SystemErrno = enum(u8) { return @enumFromInt(code); } - pub fn label(this: SystemErrno) ?[]const u8 { + pub fn label(this: SystemErrno) ?[:0]const u8 { return labels.get(this) orelse null; } - const LabelMap = std.EnumMap(SystemErrno, []const u8); + const LabelMap = std.EnumMap(SystemErrno, [:0]const u8); pub const labels: LabelMap = brk: { var map: LabelMap = LabelMap.initFull(""); map.put(.E2BIG, "Argument list too long"); @@ -363,7 +363,7 @@ pub const SystemErrno = enum(u8) { map.put(.ENOEXEC, "Exec format error"); map.put(.ENOLCK, "No locks available"); map.put(.ENOLINK, "Reserved"); - map.put(.ENOMEM, "Cannot allocate memory"); + map.put(.ENOMEM, "Out of memory"); map.put(.ENOMSG, "No message of desired type"); map.put(.ENOPOLICY, "No such policy registered"); map.put(.ENOPROTOOPT, "Protocol not available"); @@ -415,6 +415,75 @@ pub const SystemErrno = enum(u8) { }; }; +pub const UV_E2BIG: i32 = @intFromEnum(SystemErrno.E2BIG); +pub const UV_EACCES: i32 = @intFromEnum(SystemErrno.EACCES); +pub const UV_EADDRINUSE: i32 = @intFromEnum(SystemErrno.EADDRINUSE); +pub const UV_EADDRNOTAVAIL: i32 = @intFromEnum(SystemErrno.EADDRNOTAVAIL); +pub const UV_EAFNOSUPPORT: i32 = @intFromEnum(SystemErrno.EAFNOSUPPORT); +pub const UV_EAGAIN: i32 = @intFromEnum(SystemErrno.EAGAIN); +pub const UV_EALREADY: i32 = @intFromEnum(SystemErrno.EALREADY); +pub const UV_EBADF: i32 = @intFromEnum(SystemErrno.EBADF); +pub const UV_EBUSY: i32 = @intFromEnum(SystemErrno.EBUSY); +pub const UV_ECANCELED: i32 = @intFromEnum(SystemErrno.ECANCELED); +pub const UV_ECHARSET: i32 = -bun.windows.libuv.UV__ECHARSET; +pub const UV_ECONNABORTED: i32 = @intFromEnum(SystemErrno.ECONNABORTED); +pub const UV_ECONNREFUSED: i32 = @intFromEnum(SystemErrno.ECONNREFUSED); +pub const UV_ECONNRESET: i32 = @intFromEnum(SystemErrno.ECONNRESET); +pub const UV_EDESTADDRREQ: i32 = @intFromEnum(SystemErrno.EDESTADDRREQ); +pub const UV_EEXIST: i32 = @intFromEnum(SystemErrno.EEXIST); +pub const UV_EFAULT: i32 = @intFromEnum(SystemErrno.EFAULT); +pub const UV_EHOSTUNREACH: i32 = @intFromEnum(SystemErrno.EHOSTUNREACH); +pub const UV_EINTR: i32 = @intFromEnum(SystemErrno.EINTR); +pub const UV_EINVAL: i32 = @intFromEnum(SystemErrno.EINVAL); +pub const UV_EIO: i32 = @intFromEnum(SystemErrno.EIO); +pub const UV_EISCONN: i32 = @intFromEnum(SystemErrno.EISCONN); +pub const UV_EISDIR: i32 = @intFromEnum(SystemErrno.EISDIR); +pub const UV_ELOOP: i32 = @intFromEnum(SystemErrno.ELOOP); +pub const UV_EMFILE: i32 = @intFromEnum(SystemErrno.EMFILE); +pub const UV_EMSGSIZE: i32 = @intFromEnum(SystemErrno.EMSGSIZE); +pub const UV_ENAMETOOLONG: i32 = @intFromEnum(SystemErrno.ENAMETOOLONG); +pub const UV_ENETDOWN: i32 = @intFromEnum(SystemErrno.ENETDOWN); +pub const UV_ENETUNREACH: i32 = @intFromEnum(SystemErrno.ENETUNREACH); +pub const UV_ENFILE: i32 = @intFromEnum(SystemErrno.ENFILE); +pub const UV_ENOBUFS: i32 = @intFromEnum(SystemErrno.ENOBUFS); +pub const UV_ENODEV: i32 = @intFromEnum(SystemErrno.ENODEV); +pub const UV_ENOENT: i32 = @intFromEnum(SystemErrno.ENOENT); +pub const UV_ENOMEM: i32 = @intFromEnum(SystemErrno.ENOMEM); +pub const UV_ENONET: i32 = -bun.windows.libuv.UV_ENONET; +pub const UV_ENOSPC: i32 = @intFromEnum(SystemErrno.ENOSPC); +pub const UV_ENOSYS: i32 = @intFromEnum(SystemErrno.ENOSYS); +pub const UV_ENOTCONN: i32 = @intFromEnum(SystemErrno.ENOTCONN); +pub const UV_ENOTDIR: i32 = @intFromEnum(SystemErrno.ENOTDIR); +pub const UV_ENOTEMPTY: i32 = @intFromEnum(SystemErrno.ENOTEMPTY); +pub const UV_ENOTSOCK: i32 = @intFromEnum(SystemErrno.ENOTSOCK); +pub const UV_ENOTSUP: i32 = @intFromEnum(SystemErrno.ENOTSUP); +pub const UV_EPERM: i32 = @intFromEnum(SystemErrno.EPERM); +pub const UV_EPIPE: i32 = @intFromEnum(SystemErrno.EPIPE); +pub const UV_EPROTO: i32 = @intFromEnum(SystemErrno.EPROTO); +pub const UV_EPROTONOSUPPORT: i32 = @intFromEnum(SystemErrno.EPROTONOSUPPORT); +pub const UV_EPROTOTYPE: i32 = @intFromEnum(SystemErrno.EPROTOTYPE); +pub const UV_EROFS: i32 = @intFromEnum(SystemErrno.EROFS); +pub const UV_ESHUTDOWN: i32 = @intFromEnum(SystemErrno.ESHUTDOWN); +pub const UV_ESPIPE: i32 = @intFromEnum(SystemErrno.ESPIPE); +pub const UV_ESRCH: i32 = @intFromEnum(SystemErrno.ESRCH); +pub const UV_ETIMEDOUT: i32 = @intFromEnum(SystemErrno.ETIMEDOUT); +pub const UV_ETXTBSY: i32 = @intFromEnum(SystemErrno.ETXTBSY); +pub const UV_EXDEV: i32 = @intFromEnum(SystemErrno.EXDEV); +pub const UV_EFBIG: i32 = @intFromEnum(SystemErrno.EFBIG); +pub const UV_ENOPROTOOPT: i32 = @intFromEnum(SystemErrno.ENOPROTOOPT); +pub const UV_ERANGE: i32 = @intFromEnum(SystemErrno.ERANGE); +pub const UV_ENXIO: i32 = @intFromEnum(SystemErrno.ENXIO); +pub const UV_EMLINK: i32 = @intFromEnum(SystemErrno.EMLINK); +pub const UV_EHOSTDOWN: i32 = @intFromEnum(SystemErrno.EHOSTDOWN); +pub const UV_EREMOTEIO: i32 = -bun.windows.libuv.UV_EREMOTEIO; +pub const UV_ENOTTY: i32 = @intFromEnum(SystemErrno.ENOTTY); +pub const UV_EFTYPE: i32 = @intFromEnum(SystemErrno.EFTYPE); +pub const UV_EILSEQ: i32 = @intFromEnum(SystemErrno.EILSEQ); +pub const UV_EOVERFLOW: i32 = @intFromEnum(SystemErrno.EOVERFLOW); +pub const UV_ESOCKTNOSUPPORT: i32 = @intFromEnum(SystemErrno.ESOCKTNOSUPPORT); +pub const UV_ENODATA: i32 = @intFromEnum(SystemErrno.ENODATA); +pub const UV_EUNATCH: i32 = -bun.windows.libuv.UV_EUNATCH; + // Courtesy of https://github.com/nodejs/node/blob/master/deps/uv/src/unix/darwin-stub.h pub const struct_CFArrayCallBacks = opaque {}; pub const CFIndex = c_long; @@ -484,8 +553,7 @@ pub const kFSEventStreamEventFlagUnmount: c_int = 128; pub const kFSEventStreamEventFlagUserDropped: c_int = 2; pub fn getFreeMemory() u64 { - // NOT IMPLEMENTED YET - return 1024 * 1024; + return @extern(*const fn () callconv(.C) u64, .{ .name = "Bun__Os__getFreeMemory" })(); } pub fn getTotalMemory() u64 { @@ -816,3 +884,7 @@ pub const CLOCK_UPTIME_RAW = 8; pub const CLOCK_UPTIME_RAW_APPROX = 9; pub const CLOCK_PROCESS_CPUTIME_ID = 12; pub const CLOCK_THREAD_CPUTIME_ID = 1; + +pub const netdb = @cImport({ + @cInclude("netdb.h"); +}); diff --git a/src/defines-table.zig b/src/defines-table.zig index 645d7ef91382f..8fdfeda58e52f 100644 --- a/src/defines-table.zig +++ b/src/defines-table.zig @@ -234,6 +234,7 @@ pub const pure_global_identifiers = .{ .{ "EvalError", pure_global_identifier_define }, .{ "Event", pure_global_identifier_define }, .{ "EventTarget", pure_global_identifier_define }, + .{ "Float16Array", pure_global_identifier_define }, .{ "Float32Array", pure_global_identifier_define }, .{ "Float64Array", pure_global_identifier_define }, .{ "Int16Array", pure_global_identifier_define }, diff --git a/src/defines.zig b/src/defines.zig index 2974547b7c27b..43cdd144b6cce 100644 --- a/src/defines.zig +++ b/src/defines.zig @@ -57,6 +57,13 @@ pub const DefineData = struct { return self.valueless; } + pub fn initBoolean(value: bool) DefineData { + return .{ + .value = .{ .e_boolean = .{ .value = value } }, + .can_be_removed_if_unused = true, + }; + } + pub fn merge(a: DefineData, b: DefineData) DefineData { return DefineData{ .value = b.value, @@ -66,8 +73,8 @@ pub const DefineData = struct { }; } - pub fn from_mergable_input(defines: RawDefines, user_defines: *UserDefines, log: *logger.Log, allocator: std.mem.Allocator) !void { - try user_defines.ensureUnusedCapacity(@as(u32, @truncate(defines.count()))); + pub fn fromMergeableInput(defines: RawDefines, user_defines: *UserDefines, log: *logger.Log, allocator: std.mem.Allocator) !void { + try user_defines.ensureUnusedCapacity(@truncate(defines.count())); var iter = defines.iterator(); while (iter.next()) |entry| { var keySplitter = std.mem.split(u8, entry.key_ptr.*, "."); @@ -85,42 +92,33 @@ pub const DefineData = struct { // check for nested identifiers var valueSplitter = std.mem.split(u8, entry.value_ptr.*, "."); var isIdent = true; + while (valueSplitter.next()) |part| { if (!js_lexer.isIdentifier(part) or js_lexer.Keywords.has(part)) { isIdent = false; break; } } - if (isIdent) { + if (isIdent) { // Special-case undefined. it's not an identifier here // https://github.com/evanw/esbuild/issues/1407 - if (strings.eqlComptime(entry.value_ptr.*, "undefined")) { - user_defines.putAssumeCapacity( - entry.key_ptr.*, - DefineData{ - .value = js_ast.Expr.Data{ .e_undefined = js_ast.E.Undefined{} }, - .original_name = entry.value_ptr.*, - .can_be_removed_if_unused = true, - }, - ); - } else { - const ident = js_ast.E.Identifier{ .ref = Ref.None, .can_be_removed_if_unused = true }; - - user_defines.putAssumeCapacity( - entry.key_ptr.*, - DefineData{ - .value = js_ast.Expr.Data{ .e_identifier = ident }, - .original_name = entry.value_ptr.*, - .can_be_removed_if_unused = true, - }, - ); - } - - // user_defines.putAssumeCapacity( - // entry.key_ptr, - // DefineData{ .value = js_ast.Expr.Data{.e_identifier = } }, - // ); + const value = if (strings.eqlComptime(entry.value_ptr.*, "undefined")) + js_ast.Expr.Data{ .e_undefined = js_ast.E.Undefined{} } + else + js_ast.Expr.Data{ .e_identifier = .{ + .ref = Ref.None, + .can_be_removed_if_unused = true, + } }; + + user_defines.putAssumeCapacity( + entry.key_ptr.*, + DefineData{ + .value = value, + .original_name = entry.value_ptr.*, + .can_be_removed_if_unused = true, + }, + ); continue; } const _log = log; @@ -129,47 +127,18 @@ pub const DefineData = struct { .path = defines_path, .key_path = fs.Path.initWithNamespace("defines", "internal"), }; - var expr = try json_parser.ParseEnvJSON(&source, _log, allocator); - var data: js_ast.Expr.Data = undefined; - switch (expr.data) { - .e_missing => { - data = .{ .e_missing = js_ast.E.Missing{} }; - }, - // We must copy so we don't recycle - .e_string => { - data = .{ .e_string = try allocator.create(js_ast.E.String) }; - data.e_string.* = try expr.data.e_string.clone(allocator); - }, - .e_null, .e_boolean, .e_number => { - data = expr.data; - }, - // We must copy so we don't recycle - .e_object => |obj| { - expr.data.e_object = try allocator.create(js_ast.E.Object); - expr.data.e_object.* = obj.*; - data = expr.data; - }, - // We must copy so we don't recycle - .e_array => |obj| { - expr.data.e_array = try allocator.create(js_ast.E.Array); - expr.data.e_array.* = obj.*; - data = expr.data; - }, - else => { - continue; - }, - } - + const expr = try json_parser.ParseEnvJSON(&source, _log, allocator); + const cloned = try expr.data.deepClone(allocator); user_defines.putAssumeCapacity(entry.key_ptr.*, DefineData{ - .value = data, - .can_be_removed_if_unused = @as(js_ast.Expr.Tag, data).isPrimitiveLiteral(), + .value = cloned, + .can_be_removed_if_unused = expr.isPrimitiveLiteral(), }); } } - pub fn from_input(defines: RawDefines, log: *logger.Log, allocator: std.mem.Allocator) !UserDefines { + pub fn fromInput(defines: RawDefines, log: *logger.Log, allocator: std.mem.Allocator) !UserDefines { var user_defines = UserDefines.init(allocator); - try from_mergable_input(defines, &user_defines, log, allocator); + try fromMergeableInput(defines, &user_defines, log, allocator); return user_defines; } @@ -203,6 +172,8 @@ pub const Define = struct { dots: bun.StringHashMap([]DotDefine), allocator: std.mem.Allocator, + pub const Data = DefineData; + pub fn forIdentifier(this: *const Define, name: []const u8) ?IdentifierDefine { if (this.identifiers.get(name)) |data| { return data; @@ -212,58 +183,60 @@ pub const Define = struct { } pub fn insertFromIterator(define: *Define, allocator: std.mem.Allocator, comptime Iterator: type, iter: Iterator) !void { - outer: while (iter.next()) |user_define| { - const user_define_key = user_define.key_ptr.*; - // If it has a dot, then it's a DotDefine. - // e.g. process.env.NODE_ENV - if (strings.lastIndexOfChar(user_define_key, '.')) |last_dot| { - const tail = user_define_key[last_dot + 1 .. user_define_key.len]; - const remainder = user_define_key[0..last_dot]; - const count = std.mem.count(u8, remainder, ".") + 1; - var parts = try allocator.alloc(string, count + 1); - var splitter = std.mem.split(u8, remainder, "."); - var i: usize = 0; - while (splitter.next()) |split| : (i += 1) { - parts[i] = split; - } - parts[i] = tail; - var initial_values: []DotDefine = &([_]DotDefine{}); - - // "NODE_ENV" - const gpe_entry = try define.dots.getOrPut(tail); - - if (gpe_entry.found_existing) { - for (gpe_entry.value_ptr.*) |*part| { - // ["process", "env"] === ["process", "env"] (if that actually worked) - if (arePartsEqual(part.parts, parts)) { - part.data = part.data.merge(user_define.value_ptr.*); - continue :outer; - } - } - - initial_values = gpe_entry.value_ptr.*; - } + while (iter.next()) |user_define| { + try define.insert(allocator, user_define.key_ptr.*, user_define.value_ptr.*); + } + } - var list = try std.ArrayList(DotDefine).initCapacity(allocator, initial_values.len + 1); - if (initial_values.len > 0) { - list.appendSliceAssumeCapacity(initial_values); + pub fn insert(define: *Define, allocator: std.mem.Allocator, key: []const u8, value: DefineData) !void { + // If it has a dot, then it's a DotDefine. + // e.g. process.env.NODE_ENV + if (strings.lastIndexOfChar(key, '.')) |last_dot| { + const tail = key[last_dot + 1 .. key.len]; + const remainder = key[0..last_dot]; + const count = std.mem.count(u8, remainder, ".") + 1; + var parts = try allocator.alloc(string, count + 1); + var splitter = std.mem.split(u8, remainder, "."); + var i: usize = 0; + while (splitter.next()) |split| : (i += 1) { + parts[i] = split; + } + parts[i] = tail; + var initial_values: []DotDefine = &([_]DotDefine{}); + + // "NODE_ENV" + const gpe_entry = try define.dots.getOrPut(tail); + + if (gpe_entry.found_existing) { + for (gpe_entry.value_ptr.*) |*part| { + // ["process", "env"] === ["process", "env"] (if that actually worked) + if (arePartsEqual(part.parts, parts)) { + part.data = part.data.merge(value); + return; + } } - list.appendAssumeCapacity(DotDefine{ - .data = user_define.value_ptr.*, - // TODO: do we need to allocate this? - .parts = parts, - }); - gpe_entry.value_ptr.* = try list.toOwnedSlice(); - } else { + initial_values = gpe_entry.value_ptr.*; + } - // e.g. IS_BROWSER - try define.identifiers.put(user_define_key, user_define.value_ptr.*); + var list = try std.ArrayList(DotDefine).initCapacity(allocator, initial_values.len + 1); + if (initial_values.len > 0) { + list.appendSliceAssumeCapacity(initial_values); } + + list.appendAssumeCapacity(DotDefine{ + .data = value, + // TODO: do we need to allocate this? + .parts = parts, + }); + gpe_entry.value_ptr.* = try list.toOwnedSlice(); + } else { + // e.g. IS_BROWSER + try define.identifiers.put(key, value); } } - pub fn init(allocator: std.mem.Allocator, _user_defines: ?UserDefines, string_defines: ?UserDefinesArray) !*@This() { + pub fn init(allocator: std.mem.Allocator, _user_defines: ?UserDefines, string_defines: ?UserDefinesArray) std.mem.Allocator.Error!*@This() { var define = try allocator.create(Define); define.allocator = allocator; define.identifiers = bun.StringHashMap(IdentifierDefine).init(allocator); diff --git a/src/deps/_libusockets.h b/src/deps/_libusockets.h index c3ac82afc6de8..2f746879b6fa8 100644 --- a/src/deps/_libusockets.h +++ b/src/deps/_libusockets.h @@ -138,204 +138,7 @@ typedef void (*uws_get_headers_server_handler)(const char *header_name, size_t header_value_size, void *user_data); -// Basic HTTP -uws_app_t *uws_create_app(int ssl, - struct us_bun_socket_context_options_t options); - -void uws_app_destroy(int ssl, uws_app_t *app); -void uws_app_get(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_post(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_options(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_delete(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_patch(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_put(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_head(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_connect(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_trace(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); -void uws_app_any(int ssl, uws_app_t *app, const char *pattern, - uws_method_handler handler, void *user_data); - -void uws_app_run(int ssl, uws_app_t *); - -void uws_app_listen(int ssl, uws_app_t *app, int port, - uws_listen_handler handler, void *user_data); -void uws_app_listen_with_config(int ssl, uws_app_t *app, const char *host, - uint16_t port, int32_t options, - uws_listen_handler handler, void *user_data); -void uws_app_listen_domain(int ssl, uws_app_t *app, const char *domain, - size_t pathlen, uws_listen_domain_handler handler, - void *user_data); - -void uws_app_listen_domain_with_options(int ssl, uws_app_t *app, - const char *domain, size_t pathlen, - int options, - uws_listen_domain_handler handler, - void *user_data); -void uws_app_domain(int ssl, uws_app_t *app, const char *server_name); - -bool uws_constructor_failed(int ssl, uws_app_t *app); - -unsigned int uws_num_subscribers(int ssl, uws_app_t *app, const char *topic, - size_t topic_length); -bool uws_publish(int ssl, uws_app_t *app, const char *topic, - size_t topic_length, const char *message, - size_t message_length, uws_opcode_t opcode, bool compress); -void *uws_get_native_handle(int ssl, uws_app_t *app); -void uws_remove_server_name(int ssl, uws_app_t *app, - const char *hostname_pattern); -void uws_add_server_name(int ssl, uws_app_t *app, const char *hostname_pattern); -void uws_add_server_name_with_options( - int ssl, uws_app_t *app, const char *hostname_pattern, - struct us_bun_socket_context_options_t options); -void uws_missing_server_name(int ssl, uws_app_t *app, - uws_missing_server_handler handler, - void *user_data); -void uws_filter(int ssl, uws_app_t *app, uws_filter_handler handler, - void *user_data); - -// WebSocket -void uws_ws(int ssl, uws_app_t *app, void *upgradeCtx, const char *pattern, - size_t pattern_length, size_t id, - const uws_socket_behavior_t *behavior); -void *uws_ws_get_user_data(int ssl, uws_websocket_t *ws); -void uws_ws_close(int ssl, uws_websocket_t *ws); -uws_sendstatus_t uws_ws_send(int ssl, uws_websocket_t *ws, const char *message, - size_t length, uws_opcode_t opcode); -uws_sendstatus_t uws_ws_send_with_options(int ssl, uws_websocket_t *ws, - const char *message, size_t length, - uws_opcode_t opcode, bool compress, - bool fin); -uws_sendstatus_t uws_ws_send_fragment(int ssl, uws_websocket_t *ws, - const char *message, size_t length, - bool compress); -uws_sendstatus_t uws_ws_send_first_fragment(int ssl, uws_websocket_t *ws, - const char *message, size_t length, - bool compress); -uws_sendstatus_t -uws_ws_send_first_fragment_with_opcode(int ssl, uws_websocket_t *ws, - const char *message, size_t length, - uws_opcode_t opcode, bool compress); -uws_sendstatus_t uws_ws_send_last_fragment(int ssl, uws_websocket_t *ws, - const char *message, size_t length, - bool compress); -void uws_ws_end(int ssl, uws_websocket_t *ws, int code, const char *message, - size_t length); -void uws_ws_cork(int ssl, uws_websocket_t *ws, void (*handler)(void *user_data), - void *user_data); -bool uws_ws_subscribe(int ssl, uws_websocket_t *ws, const char *topic, - size_t length); -bool uws_ws_unsubscribe(int ssl, uws_websocket_t *ws, const char *topic, - size_t length); -bool uws_ws_is_subscribed(int ssl, uws_websocket_t *ws, const char *topic, - size_t length); -void uws_ws_iterate_topics(int ssl, uws_websocket_t *ws, - void (*callback)(const char *topic, size_t length, - void *user_data), - void *user_data); -bool uws_ws_publish(int ssl, uws_websocket_t *ws, const char *topic, - size_t topic_length, const char *message, - size_t message_length); -bool uws_ws_publish_with_options(int ssl, uws_websocket_t *ws, - const char *topic, size_t topic_length, - const char *message, size_t message_length, - uws_opcode_t opcode, bool compress); -unsigned int uws_ws_get_buffered_amount(int ssl, uws_websocket_t *ws); -size_t uws_ws_get_remote_address(int ssl, uws_websocket_t *ws, - const char **dest); -size_t uws_ws_get_remote_address_as_text(int ssl, uws_websocket_t *ws, - const char **dest); - -// Response -void uws_res_end(int ssl, uws_res_t *res, const char *data, size_t length, - bool close_connection); -void uws_res_pause(int ssl, uws_res_t *res); -void uws_res_resume(int ssl, uws_res_t *res); -void uws_res_write_continwue(int ssl, uws_res_t *res); -void uws_res_write_status(int ssl, uws_res_t *res, const char *status, - size_t length); -void uws_res_write_header(int ssl, uws_res_t *res, const char *key, - size_t key_length, const char *value, - size_t value_length); - -void uws_res_write_header_int(int ssl, uws_res_t *res, const char *key, - size_t key_length, uint64_t value); -void uws_res_end_without_body(int ssl, uws_res_t *res, bool close_connection); -void uws_res_end_stream(int ssl, uws_res_t *res, bool close_connection); -bool uws_res_write(int ssl, uws_res_t *res, const char *data, size_t length); -uint64_t uws_res_get_write_offset(int ssl, uws_res_t *res); -bool uws_res_has_responded(int ssl, uws_res_t *res); -void uws_res_on_writable(int ssl, uws_res_t *res, - bool (*handler)(uws_res_t *res, uint64_t, - void *opcional_data), - void *user_data); -void uws_res_on_aborted(int ssl, uws_res_t *res, - void (*handler)(uws_res_t *res, void *opcional_data), - void *opcional_data); -void uws_res_on_data(int ssl, uws_res_t *res, - void (*handler)(uws_res_t *res, const char *chunk, - size_t chunk_length, bool is_end, - void *opcional_data), - void *opcional_data); -void uws_res_upgrade(int ssl, uws_res_t *res, void *data, - const char *sec_web_socket_key, - size_t sec_web_socket_key_length, - const char *sec_web_socket_protocol, - size_t sec_web_socket_protocol_length, - const char *sec_web_socket_extensions, - size_t sec_web_socket_extensions_length, - uws_socket_context_t *ws); - -// Request -bool uws_req_is_ancient(uws_req_t *res); -bool uws_req_get_yield(uws_req_t *res); -void uws_req_set_yield(uws_req_t *res, bool yield); -size_t uws_req_get_url(uws_req_t *res, const char **dest); -size_t uws_req_get_method(uws_req_t *res, const char **dest); -size_t uws_req_get_header(uws_req_t *res, const char *lower_case_header, - size_t lower_case_header_length, const char **dest); -size_t uws_req_get_query(uws_req_t *res, const char *key, size_t key_length, - const char **dest); -size_t uws_req_get_parameter(uws_req_t *res, unsigned short index, - const char **dest); -void uws_req_for_each_header(uws_req_t *res, - uws_get_headers_server_handler handler, - void *user_data); - struct us_loop_t *uws_get_loop(); -struct us_loop_t *uws_get_loop_with_native(void *existing_native_loop); - -void uws_loop_addPostHandler(us_loop_t *loop, void *ctx_, - void (*cb)(void *ctx, us_loop_t *loop)); -void uws_loop_removePostHandler(us_loop_t *loop, void *key); -void uws_loop_addPreHandler(us_loop_t *loop, void *key, - void (*cb)(void *ctx, us_loop_t *loop)); -void uws_loop_removePreHandler(us_loop_t *loop, void *ctx_); -void uws_loop_defer(us_loop_t *loop, void *ctx, void (*cb)(void *ctx)); - -void uws_res_write_headers(int ssl, uws_res_t *res, const StringPointer *names, - const StringPointer *values, size_t count, - const char *buf); - -void *uws_res_get_native_handle(int ssl, uws_res_t *res); -void uws_res_uncork(int ssl, uws_res_t *res); -void us_socket_mark_needs_more_not_ssl(uws_res_t *res); -int uws_res_state(int ssl, uws_res_t *res); -bool uws_res_try_end(int ssl, uws_res_t *res, const char *bytes, size_t len, - size_t total_len, bool close); - -void uws_res_prepare_for_sendfile(int ssl, uws_res_t *res); -void uws_res_override_write_offset(int ssl, uws_res_t *res, uint64_t offset); - -void uws_app_close(int ssl, uws_app_t *app); #ifdef __cplusplus } diff --git a/src/deps/boringssl b/src/deps/boringssl deleted file mode 160000 index 29a2cd359458c..0000000000000 --- a/src/deps/boringssl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 29a2cd359458c9384694b75456026e4b57e3e567 diff --git a/src/deps/boringssl.translated.zig b/src/deps/boringssl.translated.zig index 3a96c8a9d63ac..ee25a0948daf9 100644 --- a/src/deps/boringssl.translated.zig +++ b/src/deps/boringssl.translated.zig @@ -176,7 +176,7 @@ pub const struct_bn_mont_ctx_st = extern struct { }; pub const BN_MONT_CTX = struct_bn_mont_ctx_st; pub const struct_bn_blinding_st = opaque {}; -pub const BN_BLINDING = struct_bn_blinding_st; // src/deps/boringssl/include/openssl/rsa.h:788:12: warning: struct demoted to opaque type - has bitfield +pub const BN_BLINDING = struct_bn_blinding_st; // boringssl/include/openssl/rsa.h:788:12: warning: struct demoted to opaque type - has bitfield pub const struct_rsa_st = opaque {}; pub const RSA = struct_rsa_st; pub const struct_dsa_st = extern struct { @@ -292,8 +292,8 @@ pub const struct_buf_mem_st = extern struct { max: usize, }; pub const BUF_MEM = struct_buf_mem_st; -pub const CBB = struct_cbb_st; // src/deps/boringssl/include/openssl/bytestring.h:403:12: warning: struct demoted to opaque type - has bitfield -pub const struct_cbb_buffer_st = opaque {}; // src/deps/boringssl/include/openssl/bytestring.h:418:12: warning: struct demoted to opaque type - has bitfield +pub const CBB = struct_cbb_st; // boringssl/include/openssl/bytestring.h:403:12: warning: struct demoted to opaque type - has bitfield +pub const struct_cbb_buffer_st = opaque {}; // boringssl/include/openssl/bytestring.h:418:12: warning: struct demoted to opaque type - has bitfield pub const struct_cbb_child_st = opaque {}; const union_unnamed_3 = extern union { base: struct_cbb_buffer_st, @@ -5035,7 +5035,7 @@ pub extern fn d2i_PKCS8PrivateKey_bio(bp: [*c]BIO, x: [*c][*c]EVP_PKEY, cb: ?*co // pub extern fn d2i_PKCS8PrivateKey_fp(fp: [*c]FILE, x: [*c][*c]EVP_PKEY, cb: ?*const pem_password_cb, u: ?*anyopaque) [*c]EVP_PKEY; // pub extern fn PEM_write_PKCS8PrivateKey(fp: [*c]FILE, x: [*c]EVP_PKEY, enc: ?*const EVP_CIPHER, kstr: [*c]u8, klen: c_int, cd: ?*const pem_password_cb, u: ?*anyopaque) c_int; -pub extern fn HMAC(evp_md: ?*const EVP_MD, key: ?*const anyopaque, key_len: usize, data: [*c]const u8, data_len: usize, out: [*c]u8, out_len: [*c]c_uint) [*c]u8; +pub extern fn HMAC(evp_md: *const EVP_MD, key: *const anyopaque, key_len: usize, data: [*]const u8, data_len: usize, out: [*]u8, out_len: *c_uint) ?[*]u8; pub extern fn HMAC_CTX_init(ctx: [*c]HMAC_CTX) void; pub extern fn HMAC_CTX_new() [*c]HMAC_CTX; pub extern fn HMAC_CTX_cleanup(ctx: [*c]HMAC_CTX) void; @@ -18737,7 +18737,7 @@ pub extern fn RAND_bytes(buf: [*]u8, len: usize) c_int; /// Hence, this function should never be called by libraries. pub extern fn RAND_enable_fork_unsafe_buffering(c_int) void; -pub extern fn SSL_new(ctx: ?*SSL_CTX) *SSL; +pub extern fn SSL_new(ctx: ?*SSL_CTX) ?*SSL; pub extern fn EVP_md4() *const EVP_MD; pub extern fn EVP_md5() *const EVP_MD; @@ -19162,7 +19162,6 @@ pub const SSL_CTX = opaque { if (auto_crypto_buffer_pool == null) auto_crypto_buffer_pool = CRYPTO_BUFFER_POOL_new(); SSL_CTX_set0_buffer_pool(ctx, auto_crypto_buffer_pool); _ = SSL_CTX_set_cipher_list(ctx, SSL_DEFAULT_CIPHER_LIST); - SSL_CTX_set_quiet_shutdown(ctx, 1); } pub inline fn setCustomVerify(this: *SSL_CTX, cb: ?VerifyCallback) void { diff --git a/src/deps/brotli/README.md b/src/deps/brotli/README.md deleted file mode 100644 index f49a75273703e..0000000000000 --- a/src/deps/brotli/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Brotli 1.1.0 - -This is a vendored copy of [Brotli](https://github.com/google/brotli/commits/v1.1.0) v1.1.0, with just the C source files. - -Brotli is MIT licensed. diff --git a/src/deps/brotli/common/constants.c b/src/deps/brotli/common/constants.c deleted file mode 100644 index 89866b150503c..0000000000000 --- a/src/deps/brotli/common/constants.c +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "constants.h" - -const BrotliPrefixCodeRange - _kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS] = { - {1, 2}, {5, 2}, {9, 2}, {13, 2}, {17, 3}, {25, 3}, - {33, 3}, {41, 3}, {49, 4}, {65, 4}, {81, 4}, {97, 4}, - {113, 5}, {145, 5}, {177, 5}, {209, 5}, {241, 6}, {305, 6}, - {369, 7}, {497, 8}, {753, 9}, {1265, 10}, {2289, 11}, {4337, 12}, - {8433, 13}, {16625, 24}}; diff --git a/src/deps/brotli/common/constants.h b/src/deps/brotli/common/constants.h deleted file mode 100644 index 31e5bd376e90f..0000000000000 --- a/src/deps/brotli/common/constants.h +++ /dev/null @@ -1,201 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/** - * @file - * Common constants used in decoder and encoder API. - */ - -#ifndef BROTLI_COMMON_CONSTANTS_H_ -#define BROTLI_COMMON_CONSTANTS_H_ - -#include -#include - -#include "platform.h" - -/* Specification: 7.3. Encoding of the context map */ -#define BROTLI_CONTEXT_MAP_MAX_RLE 16 - -/* Specification: 2. Compressed representation overview */ -#define BROTLI_MAX_NUMBER_OF_BLOCK_TYPES 256 - -/* Specification: 3.3. Alphabet sizes: insert-and-copy length */ -#define BROTLI_NUM_LITERAL_SYMBOLS 256 -#define BROTLI_NUM_COMMAND_SYMBOLS 704 -#define BROTLI_NUM_BLOCK_LEN_SYMBOLS 26 -#define BROTLI_MAX_CONTEXT_MAP_SYMBOLS (BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + \ - BROTLI_CONTEXT_MAP_MAX_RLE) -#define BROTLI_MAX_BLOCK_TYPE_SYMBOLS (BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + 2) - -/* Specification: 3.5. Complex prefix codes */ -#define BROTLI_REPEAT_PREVIOUS_CODE_LENGTH 16 -#define BROTLI_REPEAT_ZERO_CODE_LENGTH 17 -#define BROTLI_CODE_LENGTH_CODES (BROTLI_REPEAT_ZERO_CODE_LENGTH + 1) -/* "code length of 8 is repeated" */ -#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8 - -/* "Large Window Brotli" */ - -/** - * The theoretical maximum number of distance bits specified for large window - * brotli, for 64-bit encoders and decoders. Even when in practice 32-bit - * encoders and decoders only support up to 30 max distance bits, the value is - * set to 62 because it affects the large window brotli file format. - * Specifically, it affects the encoding of simple huffman tree for distances, - * see Specification RFC 7932 chapter 3.4. - */ -#define BROTLI_LARGE_MAX_DISTANCE_BITS 62U -#define BROTLI_LARGE_MIN_WBITS 10 -/** - * The maximum supported large brotli window bits by the encoder and decoder. - * Large window brotli allows up to 62 bits, however the current encoder and - * decoder, designed for 32-bit integers, only support up to 30 bits maximum. - */ -#define BROTLI_LARGE_MAX_WBITS 30 - -/* Specification: 4. Encoding of distances */ -#define BROTLI_NUM_DISTANCE_SHORT_CODES 16 -/** - * Maximal number of "postfix" bits. - * - * Number of "postfix" bits is stored as 2 bits in meta-block header. - */ -#define BROTLI_MAX_NPOSTFIX 3 -#define BROTLI_MAX_NDIRECT 120 -#define BROTLI_MAX_DISTANCE_BITS 24U -#define BROTLI_DISTANCE_ALPHABET_SIZE(NPOSTFIX, NDIRECT, MAXNBITS) ( \ - BROTLI_NUM_DISTANCE_SHORT_CODES + (NDIRECT) + \ - ((MAXNBITS) << ((NPOSTFIX) + 1))) -/* BROTLI_NUM_DISTANCE_SYMBOLS == 1128 */ -#define BROTLI_NUM_DISTANCE_SYMBOLS \ - BROTLI_DISTANCE_ALPHABET_SIZE( \ - BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX, BROTLI_LARGE_MAX_DISTANCE_BITS) - -/* ((1 << 26) - 4) is the maximal distance that can be expressed in RFC 7932 - brotli stream using NPOSTFIX = 0 and NDIRECT = 0. With other NPOSTFIX and - NDIRECT values distances up to ((1 << 29) + 88) could be expressed. */ -#define BROTLI_MAX_DISTANCE 0x3FFFFFC - -/* ((1 << 31) - 4) is the safe distance limit. Using this number as a limit - allows safe distance calculation without overflows, given the distance - alphabet size is limited to corresponding size - (see kLargeWindowDistanceCodeLimits). */ -#define BROTLI_MAX_ALLOWED_DISTANCE 0x7FFFFFFC - - -/* Specification: 4. Encoding of Literal Insertion Lengths and Copy Lengths */ -#define BROTLI_NUM_INS_COPY_CODES 24 - -/* 7.1. Context modes and context ID lookup for literals */ -/* "context IDs for literals are in the range of 0..63" */ -#define BROTLI_LITERAL_CONTEXT_BITS 6 - -/* 7.2. Context ID for distances */ -#define BROTLI_DISTANCE_CONTEXT_BITS 2 - -/* 9.1. Format of the Stream Header */ -/* Number of slack bytes for window size. Don't confuse - with BROTLI_NUM_DISTANCE_SHORT_CODES. */ -#define BROTLI_WINDOW_GAP 16 -#define BROTLI_MAX_BACKWARD_LIMIT(W) (((size_t)1 << (W)) - BROTLI_WINDOW_GAP) - -typedef struct BrotliDistanceCodeLimit { - uint32_t max_alphabet_size; - uint32_t max_distance; -} BrotliDistanceCodeLimit; - -/* This function calculates maximal size of distance alphabet, such that the - distances greater than the given values can not be represented. - - This limits are designed to support fast and safe 32-bit decoders. - "32-bit" means that signed integer values up to ((1 << 31) - 1) could be - safely expressed. - - Brotli distance alphabet symbols do not represent consecutive distance - ranges. Each distance alphabet symbol (excluding direct distances and short - codes), represent interleaved (for NPOSTFIX > 0) range of distances. - A "group" of consecutive (1 << NPOSTFIX) symbols represent non-interleaved - range. Two consecutive groups require the same amount of "extra bits". - - It is important that distance alphabet represents complete "groups". - To avoid complex logic on encoder side about interleaved ranges - it was decided to restrict both sides to complete distance code "groups". - */ -BROTLI_UNUSED_FUNCTION BrotliDistanceCodeLimit BrotliCalculateDistanceCodeLimit( - uint32_t max_distance, uint32_t npostfix, uint32_t ndirect) { - BrotliDistanceCodeLimit result; - /* Marking this function as unused, because not all files - including "constants.h" use it -> compiler warns about that. */ - BROTLI_UNUSED(&BrotliCalculateDistanceCodeLimit); - if (max_distance <= ndirect) { - /* This case never happens / exists only for the sake of completeness. */ - result.max_alphabet_size = max_distance + BROTLI_NUM_DISTANCE_SHORT_CODES; - result.max_distance = max_distance; - return result; - } else { - /* The first prohibited value. */ - uint32_t forbidden_distance = max_distance + 1; - /* Subtract "directly" encoded region. */ - uint32_t offset = forbidden_distance - ndirect - 1; - uint32_t ndistbits = 0; - uint32_t tmp; - uint32_t half; - uint32_t group; - /* Postfix for the last dcode in the group. */ - uint32_t postfix = (1u << npostfix) - 1; - uint32_t extra; - uint32_t start; - /* Remove postfix and "head-start". */ - offset = (offset >> npostfix) + 4; - /* Calculate the number of distance bits. */ - tmp = offset / 2; - /* Poor-man's log2floor, to avoid extra dependencies. */ - while (tmp != 0) {ndistbits++; tmp = tmp >> 1;} - /* One bit is covered with subrange addressing ("half"). */ - ndistbits--; - /* Find subrange. */ - half = (offset >> ndistbits) & 1; - /* Calculate the "group" part of dcode. */ - group = ((ndistbits - 1) << 1) | half; - /* Calculated "group" covers the prohibited distance value. */ - if (group == 0) { - /* This case is added for correctness; does not occur for limit > 128. */ - result.max_alphabet_size = ndirect + BROTLI_NUM_DISTANCE_SHORT_CODES; - result.max_distance = ndirect; - return result; - } - /* Decrement "group", so it is the last permitted "group". */ - group--; - /* After group was decremented, ndistbits and half must be recalculated. */ - ndistbits = (group >> 1) + 1; - /* The last available distance in the subrange has all extra bits set. */ - extra = (1u << ndistbits) - 1; - /* Calculate region start. NB: ndistbits >= 1. */ - start = (1u << (ndistbits + 1)) - 4; - /* Move to subregion. */ - start += (group & 1) << ndistbits; - /* Calculate the alphabet size. */ - result.max_alphabet_size = ((group << npostfix) | postfix) + ndirect + - BROTLI_NUM_DISTANCE_SHORT_CODES + 1; - /* Calculate the maximal distance representable by alphabet. */ - result.max_distance = ((start + extra) << npostfix) + postfix + ndirect + 1; - return result; - } -} - -/* Represents the range of values belonging to a prefix code: - [offset, offset + 2^nbits) */ -typedef struct { - uint16_t offset; - uint8_t nbits; -} BrotliPrefixCodeRange; - -/* "Soft-private", it is exported, but not "advertised" as API. */ -BROTLI_COMMON_API extern const BrotliPrefixCodeRange - _kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; - -#endif /* BROTLI_COMMON_CONSTANTS_H_ */ diff --git a/src/deps/brotli/common/context.c b/src/deps/brotli/common/context.c deleted file mode 100644 index 7f9c95869917b..0000000000000 --- a/src/deps/brotli/common/context.c +++ /dev/null @@ -1,156 +0,0 @@ -#include "context.h" - -#include - -/* Common context lookup table for all context modes. */ -const uint8_t _kBrotliContextLookupTable[2048] = { - /* CONTEXT_LSB6, last byte. */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - - /* CONTEXT_LSB6, second last byte, */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - /* CONTEXT_MSB6, last byte. */ - 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, - 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, - 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, - 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, - 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, - 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, - 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, - 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, - 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, - 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, - 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, - 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, - 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, - 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, - 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, - 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, - - /* CONTEXT_MSB6, second last byte, */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - /* CONTEXT_UTF8, last byte. */ - /* ASCII range. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12, - 12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48, - 52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12, - 12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56, - 60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0, - /* UTF8 continuation byte range. */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, - /* UTF8 lead byte range. */ - 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - - /* CONTEXT_UTF8 second last byte. */ - /* ASCII range. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, - 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0, - /* UTF8 continuation byte range. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* UTF8 lead byte range. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - - /* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */ - 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56, - - /* CONTEXT_SIGNED, second last byte. */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, -}; diff --git a/src/deps/brotli/common/context.h b/src/deps/brotli/common/context.h deleted file mode 100644 index 685a279dc0699..0000000000000 --- a/src/deps/brotli/common/context.h +++ /dev/null @@ -1,113 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Lookup table to map the previous two bytes to a context id. - - There are four different context modeling modes defined here: - CONTEXT_LSB6: context id is the least significant 6 bits of the last byte, - CONTEXT_MSB6: context id is the most significant 6 bits of the last byte, - CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text, - CONTEXT_SIGNED: second-order context model tuned for signed integers. - - If |p1| and |p2| are the previous two bytes, and |mode| is current context - mode, we calculate the context as: - - context = ContextLut(mode)[p1] | ContextLut(mode)[p2 + 256]. - - For CONTEXT_UTF8 mode, if the previous two bytes are ASCII characters - (i.e. < 128), this will be equivalent to - - context = 4 * context1(p1) + context2(p2), - - where context1 is based on the previous byte in the following way: - - 0 : non-ASCII control - 1 : \t, \n, \r - 2 : space - 3 : other punctuation - 4 : " ' - 5 : % - 6 : ( < [ { - 7 : ) > ] } - 8 : , ; : - 9 : . - 10 : = - 11 : number - 12 : upper-case vowel - 13 : upper-case consonant - 14 : lower-case vowel - 15 : lower-case consonant - - and context2 is based on the second last byte: - - 0 : control, space - 1 : punctuation - 2 : upper-case letter, number - 3 : lower-case letter - - If the last byte is ASCII, and the second last byte is not (in a valid UTF8 - stream it will be a continuation byte, value between 128 and 191), the - context is the same as if the second last byte was an ASCII control or space. - - If the last byte is a UTF8 lead byte (value >= 192), then the next byte will - be a continuation byte and the context id is 2 or 3 depending on the LSB of - the last byte and to a lesser extent on the second last byte if it is ASCII. - - If the last byte is a UTF8 continuation byte, the second last byte can be: - - continuation byte: the next byte is probably ASCII or lead byte (assuming - 4-byte UTF8 characters are rare) and the context id is 0 or 1. - - lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1 - - lead byte (208 - 255): next byte is continuation byte, context is 2 or 3 - - The possible value combinations of the previous two bytes, the range of - context ids and the type of the next byte is summarized in the table below: - - |--------\-----------------------------------------------------------------| - | \ Last byte | - | Second \---------------------------------------------------------------| - | last byte \ ASCII | cont. byte | lead byte | - | \ (0-127) | (128-191) | (192-) | - |=============|===================|=====================|==================| - | ASCII | next: ASCII/lead | not valid | next: cont. | - | (0-127) | context: 4 - 63 | | context: 2 - 3 | - |-------------|-------------------|---------------------|------------------| - | cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. | - | (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 | - |-------------|-------------------|---------------------|------------------| - | lead byte | not valid | next: ASCII/lead | not valid | - | (192-207) | | context: 0 - 1 | | - |-------------|-------------------|---------------------|------------------| - | lead byte | not valid | next: cont. | not valid | - | (208-) | | context: 2 - 3 | | - |-------------|-------------------|---------------------|------------------| -*/ - -#ifndef BROTLI_COMMON_CONTEXT_H_ -#define BROTLI_COMMON_CONTEXT_H_ - -#include -#include - -typedef enum ContextType { - CONTEXT_LSB6 = 0, - CONTEXT_MSB6 = 1, - CONTEXT_UTF8 = 2, - CONTEXT_SIGNED = 3 -} ContextType; - -/* "Soft-private", it is exported, but not "advertised" as API. */ -/* Common context lookup table for all context modes. */ -BROTLI_COMMON_API extern const uint8_t _kBrotliContextLookupTable[2048]; - -typedef const uint8_t* ContextLut; - -/* typeof(MODE) == ContextType; returns ContextLut */ -#define BROTLI_CONTEXT_LUT(MODE) (&_kBrotliContextLookupTable[(MODE) << 9]) - -/* typeof(LUT) == ContextLut */ -#define BROTLI_CONTEXT(P1, P2, LUT) ((LUT)[P1] | ((LUT) + 256)[P2]) - -#endif /* BROTLI_COMMON_CONTEXT_H_ */ diff --git a/src/deps/brotli/common/dictionary.c b/src/deps/brotli/common/dictionary.c deleted file mode 100644 index 7c015ab0ba73b..0000000000000 --- a/src/deps/brotli/common/dictionary.c +++ /dev/null @@ -1,5916 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "dictionary.h" -#include "platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#if !defined(BROTLI_EXTERNAL_DICTIONARY_DATA) -static const uint8_t kBrotliDictionaryData[] = -/* GENERATED CODE START */ -{ -116,105,109,101,100,111,119,110,108,105,102,101,108,101,102,116,98,97,99,107,99, -111,100,101,100,97,116,97,115,104,111,119,111,110,108,121,115,105,116,101,99,105 -,116,121,111,112,101,110,106,117,115,116,108,105,107,101,102,114,101,101,119,111 -,114,107,116,101,120,116,121,101,97,114,111,118,101,114,98,111,100,121,108,111, -118,101,102,111,114,109,98,111,111,107,112,108,97,121,108,105,118,101,108,105, -110,101,104,101,108,112,104,111,109,101,115,105,100,101,109,111,114,101,119,111, -114,100,108,111,110,103,116,104,101,109,118,105,101,119,102,105,110,100,112,97, -103,101,100,97,121,115,102,117,108,108,104,101,97,100,116,101,114,109,101,97,99, -104,97,114,101,97,102,114,111,109,116,114,117,101,109,97,114,107,97,98,108,101, -117,112,111,110,104,105,103,104,100,97,116,101,108,97,110,100,110,101,119,115, -101,118,101,110,110,101,120,116,99,97,115,101,98,111,116,104,112,111,115,116,117 -,115,101,100,109,97,100,101,104,97,110,100,104,101,114,101,119,104,97,116,110,97 -,109,101,76,105,110,107,98,108,111,103,115,105,122,101,98,97,115,101,104,101,108 -,100,109,97,107,101,109,97,105,110,117,115,101,114,39,41,32,43,104,111,108,100, -101,110,100,115,119,105,116,104,78,101,119,115,114,101,97,100,119,101,114,101, -115,105,103,110,116,97,107,101,104,97,118,101,103,97,109,101,115,101,101,110,99, -97,108,108,112,97,116,104,119,101,108,108,112,108,117,115,109,101,110,117,102, -105,108,109,112,97,114,116,106,111,105,110,116,104,105,115,108,105,115,116,103, -111,111,100,110,101,101,100,119,97,121,115,119,101,115,116,106,111,98,115,109, -105,110,100,97,108,115,111,108,111,103,111,114,105,99,104,117,115,101,115,108,97 -,115,116,116,101,97,109,97,114,109,121,102,111,111,100,107,105,110,103,119,105, -108,108,101,97,115,116,119,97,114,100,98,101,115,116,102,105,114,101,80,97,103, -101,107,110,111,119,97,119,97,121,46,112,110,103,109,111,118,101,116,104,97,110, -108,111,97,100,103,105,118,101,115,101,108,102,110,111,116,101,109,117,99,104, -102,101,101,100,109,97,110,121,114,111,99,107,105,99,111,110,111,110,99,101,108, -111,111,107,104,105,100,101,100,105,101,100,72,111,109,101,114,117,108,101,104, -111,115,116,97,106,97,120,105,110,102,111,99,108,117,98,108,97,119,115,108,101, -115,115,104,97,108,102,115,111,109,101,115,117,99,104,122,111,110,101,49,48,48, -37,111,110,101,115,99,97,114,101,84,105,109,101,114,97,99,101,98,108,117,101,102 -,111,117,114,119,101,101,107,102,97,99,101,104,111,112,101,103,97,118,101,104,97 -,114,100,108,111,115,116,119,104,101,110,112,97,114,107,107,101,112,116,112,97, -115,115,115,104,105,112,114,111,111,109,72,84,77,76,112,108,97,110,84,121,112, -101,100,111,110,101,115,97,118,101,107,101,101,112,102,108,97,103,108,105,110, -107,115,111,108,100,102,105,118,101,116,111,111,107,114,97,116,101,116,111,119, -110,106,117,109,112,116,104,117,115,100,97,114,107,99,97,114,100,102,105,108,101 -,102,101,97,114,115,116,97,121,107,105,108,108,116,104,97,116,102,97,108,108,97, -117,116,111,101,118,101,114,46,99,111,109,116,97,108,107,115,104,111,112,118,111 -,116,101,100,101,101,112,109,111,100,101,114,101,115,116,116,117,114,110,98,111, -114,110,98,97,110,100,102,101,108,108,114,111,115,101,117,114,108,40,115,107,105 -,110,114,111,108,101,99,111,109,101,97,99,116,115,97,103,101,115,109,101,101,116 -,103,111,108,100,46,106,112,103,105,116,101,109,118,97,114,121,102,101,108,116, -116,104,101,110,115,101,110,100,100,114,111,112,86,105,101,119,99,111,112,121,49 -,46,48,34,60,47,97,62,115,116,111,112,101,108,115,101,108,105,101,115,116,111, -117,114,112,97,99,107,46,103,105,102,112,97,115,116,99,115,115,63,103,114,97,121 -,109,101,97,110,38,103,116,59,114,105,100,101,115,104,111,116,108,97,116,101,115 -,97,105,100,114,111,97,100,118,97,114,32,102,101,101,108,106,111,104,110,114,105 -,99,107,112,111,114,116,102,97,115,116,39,85,65,45,100,101,97,100,60,47,98,62, -112,111,111,114,98,105,108,108,116,121,112,101,85,46,83,46,119,111,111,100,109, -117,115,116,50,112,120,59,73,110,102,111,114,97,110,107,119,105,100,101,119,97, -110,116,119,97,108,108,108,101,97,100,91,48,93,59,112,97,117,108,119,97,118,101, -115,117,114,101,36,40,39,35,119,97,105,116,109,97,115,115,97,114,109,115,103,111 -,101,115,103,97,105,110,108,97,110,103,112,97,105,100,33,45,45,32,108,111,99,107 -,117,110,105,116,114,111,111,116,119,97,108,107,102,105,114,109,119,105,102,101, -120,109,108,34,115,111,110,103,116,101,115,116,50,48,112,120,107,105,110,100,114 -,111,119,115,116,111,111,108,102,111,110,116,109,97,105,108,115,97,102,101,115, -116,97,114,109,97,112,115,99,111,114,101,114,97,105,110,102,108,111,119,98,97,98 -,121,115,112,97,110,115,97,121,115,52,112,120,59,54,112,120,59,97,114,116,115, -102,111,111,116,114,101,97,108,119,105,107,105,104,101,97,116,115,116,101,112, -116,114,105,112,111,114,103,47,108,97,107,101,119,101,97,107,116,111,108,100,70, -111,114,109,99,97,115,116,102,97,110,115,98,97,110,107,118,101,114,121,114,117, -110,115,106,117,108,121,116,97,115,107,49,112,120,59,103,111,97,108,103,114,101, -119,115,108,111,119,101,100,103,101,105,100,61,34,115,101,116,115,53,112,120,59, -46,106,115,63,52,48,112,120,105,102,32,40,115,111,111,110,115,101,97,116,110,111 -,110,101,116,117,98,101,122,101,114,111,115,101,110,116,114,101,101,100,102,97, -99,116,105,110,116,111,103,105,102,116,104,97,114,109,49,56,112,120,99,97,109, -101,104,105,108,108,98,111,108,100,122,111,111,109,118,111,105,100,101,97,115, -121,114,105,110,103,102,105,108,108,112,101,97,107,105,110,105,116,99,111,115, -116,51,112,120,59,106,97,99,107,116,97,103,115,98,105,116,115,114,111,108,108, -101,100,105,116,107,110,101,119,110,101,97,114,60,33,45,45,103,114,111,119,74,83 -,79,78,100,117,116,121,78,97,109,101,115,97,108,101,121,111,117,32,108,111,116, -115,112,97,105,110,106,97,122,122,99,111,108,100,101,121,101,115,102,105,115,104 -,119,119,119,46,114,105,115,107,116,97,98,115,112,114,101,118,49,48,112,120,114, -105,115,101,50,53,112,120,66,108,117,101,100,105,110,103,51,48,48,44,98,97,108, -108,102,111,114,100,101,97,114,110,119,105,108,100,98,111,120,46,102,97,105,114, -108,97,99,107,118,101,114,115,112,97,105,114,106,117,110,101,116,101,99,104,105, -102,40,33,112,105,99,107,101,118,105,108,36,40,34,35,119,97,114,109,108,111,114, -100,100,111,101,115,112,117,108,108,44,48,48,48,105,100,101,97,100,114,97,119, -104,117,103,101,115,112,111,116,102,117,110,100,98,117,114,110,104,114,101,102, -99,101,108,108,107,101,121,115,116,105,99,107,104,111,117,114,108,111,115,115, -102,117,101,108,49,50,112,120,115,117,105,116,100,101,97,108,82,83,83,34,97,103, -101,100,103,114,101,121,71,69,84,34,101,97,115,101,97,105,109,115,103,105,114, -108,97,105,100,115,56,112,120,59,110,97,118,121,103,114,105,100,116,105,112,115, -35,57,57,57,119,97,114,115,108,97,100,121,99,97,114,115,41,59,32,125,112,104,112 -,63,104,101,108,108,116,97,108,108,119,104,111,109,122,104,58,229,42,47,13,10,32 -,49,48,48,104,97,108,108,46,10,10,65,55,112,120,59,112,117,115,104,99,104,97,116 -,48,112,120,59,99,114,101,119,42,47,60,47,104,97,115,104,55,53,112,120,102,108, -97,116,114,97,114,101,32,38,38,32,116,101,108,108,99,97,109,112,111,110,116,111, -108,97,105,100,109,105,115,115,115,107,105,112,116,101,110,116,102,105,110,101, -109,97,108,101,103,101,116,115,112,108,111,116,52,48,48,44,13,10,13,10,99,111, -111,108,102,101,101,116,46,112,104,112,60,98,114,62,101,114,105,99,109,111,115, -116,103,117,105,100,98,101,108,108,100,101,115,99,104,97,105,114,109,97,116,104, -97,116,111,109,47,105,109,103,38,35,56,50,108,117,99,107,99,101,110,116,48,48,48 -,59,116,105,110,121,103,111,110,101,104,116,109,108,115,101,108,108,100,114,117, -103,70,82,69,69,110,111,100,101,110,105,99,107,63,105,100,61,108,111,115,101,110 -,117,108,108,118,97,115,116,119,105,110,100,82,83,83,32,119,101,97,114,114,101, -108,121,98,101,101,110,115,97,109,101,100,117,107,101,110,97,115,97,99,97,112, -101,119,105,115,104,103,117,108,102,84,50,51,58,104,105,116,115,115,108,111,116, -103,97,116,101,107,105,99,107,98,108,117,114,116,104,101,121,49,53,112,120,39,39 -,41,59,41,59,34,62,109,115,105,101,119,105,110,115,98,105,114,100,115,111,114, -116,98,101,116,97,115,101,101,107,84,49,56,58,111,114,100,115,116,114,101,101, -109,97,108,108,54,48,112,120,102,97,114,109,226,128,153,115,98,111,121,115,91,48 -,93,46,39,41,59,34,80,79,83,84,98,101,97,114,107,105,100,115,41,59,125,125,109, -97,114,121,116,101,110,100,40,85,75,41,113,117,97,100,122,104,58,230,45,115,105, -122,45,45,45,45,112,114,111,112,39,41,59,13,108,105,102,116,84,49,57,58,118,105, -99,101,97,110,100,121,100,101,98,116,62,82,83,83,112,111,111,108,110,101,99,107, -98,108,111,119,84,49,54,58,100,111,111,114,101,118,97,108,84,49,55,58,108,101, -116,115,102,97,105,108,111,114,97,108,112,111,108,108,110,111,118,97,99,111,108, -115,103,101,110,101,32,226,128,148,115,111,102,116,114,111,109,101,116,105,108, -108,114,111,115,115,60,104,51,62,112,111,117,114,102,97,100,101,112,105,110,107, -60,116,114,62,109,105,110,105,41,124,33,40,109,105,110,101,122,104,58,232,98,97, -114,115,104,101,97,114,48,48,41,59,109,105,108,107,32,45,45,62,105,114,111,110, -102,114,101,100,100,105,115,107,119,101,110,116,115,111,105,108,112,117,116,115, -47,106,115,47,104,111,108,121,84,50,50,58,73,83,66,78,84,50,48,58,97,100,97,109, -115,101,101,115,60,104,50,62,106,115,111,110,39,44,32,39,99,111,110,116,84,50,49 -,58,32,82,83,83,108,111,111,112,97,115,105,97,109,111,111,110,60,47,112,62,115, -111,117,108,76,73,78,69,102,111,114,116,99,97,114,116,84,49,52,58,60,104,49,62, -56,48,112,120,33,45,45,60,57,112,120,59,84,48,52,58,109,105,107,101,58,52,54,90, -110,105,99,101,105,110,99,104,89,111,114,107,114,105,99,101,122,104,58,228,39,41 -,41,59,112,117,114,101,109,97,103,101,112,97,114,97,116,111,110,101,98,111,110, -100,58,51,55,90,95,111,102,95,39,93,41,59,48,48,48,44,122,104,58,231,116,97,110, -107,121,97,114,100,98,111,119,108,98,117,115,104,58,53,54,90,74,97,118,97,51,48, -112,120,10,124,125,10,37,67,51,37,58,51,52,90,106,101,102,102,69,88,80,73,99,97, -115,104,118,105,115,97,103,111,108,102,115,110,111,119,122,104,58,233,113,117, -101,114,46,99,115,115,115,105,99,107,109,101,97,116,109,105,110,46,98,105,110, -100,100,101,108,108,104,105,114,101,112,105,99,115,114,101,110,116,58,51,54,90, -72,84,84,80,45,50,48,49,102,111,116,111,119,111,108,102,69,78,68,32,120,98,111, -120,58,53,52,90,66,79,68,89,100,105,99,107,59,10,125,10,101,120,105,116,58,51,53 -,90,118,97,114,115,98,101,97,116,39,125,41,59,100,105,101,116,57,57,57,59,97,110 -,110,101,125,125,60,47,91,105,93,46,76,97,110,103,107,109,194,178,119,105,114, -101,116,111,121,115,97,100,100,115,115,101,97,108,97,108,101,120,59,10,9,125,101 -,99,104,111,110,105,110,101,46,111,114,103,48,48,53,41,116,111,110,121,106,101, -119,115,115,97,110,100,108,101,103,115,114,111,111,102,48,48,48,41,32,50,48,48, -119,105,110,101,103,101,97,114,100,111,103,115,98,111,111,116,103,97,114,121,99, -117,116,115,116,121,108,101,116,101,109,112,116,105,111,110,46,120,109,108,99, -111,99,107,103,97,110,103,36,40,39,46,53,48,112,120,80,104,46,68,109,105,115,99, -97,108,97,110,108,111,97,110,100,101,115,107,109,105,108,101,114,121,97,110,117, -110,105,120,100,105,115,99,41,59,125,10,100,117,115,116,99,108,105,112,41,46,10, -10,55,48,112,120,45,50,48,48,68,86,68,115,55,93,62,60,116,97,112,101,100,101,109 -,111,105,43,43,41,119,97,103,101,101,117,114,111,112,104,105,108,111,112,116,115 -,104,111,108,101,70,65,81,115,97,115,105,110,45,50,54,84,108,97,98,115,112,101, -116,115,85,82,76,32,98,117,108,107,99,111,111,107,59,125,13,10,72,69,65,68,91,48 -,93,41,97,98,98,114,106,117,97,110,40,49,57,56,108,101,115,104,116,119,105,110, -60,47,105,62,115,111,110,121,103,117,121,115,102,117,99,107,112,105,112,101,124, -45,10,33,48,48,50,41,110,100,111,119,91,49,93,59,91,93,59,10,76,111,103,32,115, -97,108,116,13,10,9,9,98,97,110,103,116,114,105,109,98,97,116,104,41,123,13,10,48 -,48,112,120,10,125,41,59,107,111,58,236,102,101,101,115,97,100,62,13,115,58,47, -47,32,91,93,59,116,111,108,108,112,108,117,103,40,41,123,10,123,13,10,32,46,106, -115,39,50,48,48,112,100,117,97,108,98,111,97,116,46,74,80,71,41,59,10,125,113, -117,111,116,41,59,10,10,39,41,59,10,13,10,125,13,50,48,49,52,50,48,49,53,50,48, -49,54,50,48,49,55,50,48,49,56,50,48,49,57,50,48,50,48,50,48,50,49,50,48,50,50,50 -,48,50,51,50,48,50,52,50,48,50,53,50,48,50,54,50,48,50,55,50,48,50,56,50,48,50, -57,50,48,51,48,50,48,51,49,50,48,51,50,50,48,51,51,50,48,51,52,50,48,51,53,50,48 -,51,54,50,48,51,55,50,48,49,51,50,48,49,50,50,48,49,49,50,48,49,48,50,48,48,57, -50,48,48,56,50,48,48,55,50,48,48,54,50,48,48,53,50,48,48,52,50,48,48,51,50,48,48 -,50,50,48,48,49,50,48,48,48,49,57,57,57,49,57,57,56,49,57,57,55,49,57,57,54,49, -57,57,53,49,57,57,52,49,57,57,51,49,57,57,50,49,57,57,49,49,57,57,48,49,57,56,57 -,49,57,56,56,49,57,56,55,49,57,56,54,49,57,56,53,49,57,56,52,49,57,56,51,49,57, -56,50,49,57,56,49,49,57,56,48,49,57,55,57,49,57,55,56,49,57,55,55,49,57,55,54,49 -,57,55,53,49,57,55,52,49,57,55,51,49,57,55,50,49,57,55,49,49,57,55,48,49,57,54, -57,49,57,54,56,49,57,54,55,49,57,54,54,49,57,54,53,49,57,54,52,49,57,54,51,49,57 -,54,50,49,57,54,49,49,57,54,48,49,57,53,57,49,57,53,56,49,57,53,55,49,57,53,54, -49,57,53,53,49,57,53,52,49,57,53,51,49,57,53,50,49,57,53,49,49,57,53,48,49,48,48 -,48,49,48,50,52,49,51,57,52,48,48,48,48,57,57,57,57,99,111,109,111,109,195,161, -115,101,115,116,101,101,115,116,97,112,101,114,111,116,111,100,111,104,97,99,101 -,99,97,100,97,97,195,177,111,98,105,101,110,100,195,173,97,97,115,195,173,118, -105,100,97,99,97,115,111,111,116,114,111,102,111,114,111,115,111,108,111,111,116 -,114,97,99,117,97,108,100,105,106,111,115,105,100,111,103,114,97,110,116,105,112 -,111,116,101,109,97,100,101,98,101,97,108,103,111,113,117,195,169,101,115,116, -111,110,97,100,97,116,114,101,115,112,111,99,111,99,97,115,97,98,97,106,111,116, -111,100,97,115,105,110,111,97,103,117,97,112,117,101,115,117,110,111,115,97,110, -116,101,100,105,99,101,108,117,105,115,101,108,108,97,109,97,121,111,122,111,110 -,97,97,109,111,114,112,105,115,111,111,98,114,97,99,108,105,99,101,108,108,111, -100,105,111,115,104,111,114,97,99,97,115,105,208,183,208,176,208,189,208,176,208 -,190,208,188,209,128,208,176,209,128,209,131,209,130,208,176,208,189,208,181,208 -,191,208,190,208,190,209,130,208,184,208,183,208,189,208,190,208,180,208,190,209 -,130,208,190,208,182,208,181,208,190,208,189,208,184,209,133,208,157,208,176,208 -,181,208,181,208,177,209,139,208,188,209,139,208,146,209,139,209,129,208,190,208 -,178,209,139,208,178,208,190,208,157,208,190,208,190,208,177,208,159,208,190,208 -,187,208,184,208,189,208,184,208,160,208,164,208,157,208,181,208,156,209,139,209 -,130,209,139,208,158,208,189,208,184,208,188,208,180,208,176,208,151,208,176,208 -,148,208,176,208,157,209,131,208,158,208,177,209,130,208,181,208,152,208,183,208 -,181,208,185,208,189,209,131,208,188,208,188,208,162,209,139,209,131,208,182,217 -,129,217,138,216,163,217,134,217,133,216,167,217,133,216,185,217,131,217,132,216 -,163,217,136,216,177,216,175,217,138,216,167,217,129,217,137,217,135,217,136,217 -,132,217,133,217,132,217,131,216,167,217,136,217,132,217,135,216,168,216,179,216 -,167,217,132,216,165,217,134,217,135,217,138,216,163,217,138,217,130,216,175,217 -,135,217,132,216,171,217,133,216,168,217,135,217,132,217,136,217,132,217,138,216 -,168,217,132,216,167,217,138,216,168,217,131,216,180,217,138,216,167,217,133,216 -,163,217,133,217,134,216,170,216,168,217,138,217,132,217,134,216,173,216,168,217 -,135,217,133,217,133,216,180,217,136,216,180,102,105,114,115,116,118,105,100,101 -,111,108,105,103,104,116,119,111,114,108,100,109,101,100,105,97,119,104,105,116, -101,99,108,111,115,101,98,108,97,99,107,114,105,103,104,116,115,109,97,108,108, -98,111,111,107,115,112,108,97,99,101,109,117,115,105,99,102,105,101,108,100,111, -114,100,101,114,112,111,105,110,116,118,97,108,117,101,108,101,118,101,108,116, -97,98,108,101,98,111,97,114,100,104,111,117,115,101,103,114,111,117,112,119,111, -114,107,115,121,101,97,114,115,115,116,97,116,101,116,111,100,97,121,119,97,116, -101,114,115,116,97,114,116,115,116,121,108,101,100,101,97,116,104,112,111,119, -101,114,112,104,111,110,101,110,105,103,104,116,101,114,114,111,114,105,110,112, -117,116,97,98,111,117,116,116,101,114,109,115,116,105,116,108,101,116,111,111, -108,115,101,118,101,110,116,108,111,99,97,108,116,105,109,101,115,108,97,114,103 -,101,119,111,114,100,115,103,97,109,101,115,115,104,111,114,116,115,112,97,99, -101,102,111,99,117,115,99,108,101,97,114,109,111,100,101,108,98,108,111,99,107, -103,117,105,100,101,114,97,100,105,111,115,104,97,114,101,119,111,109,101,110,97 -,103,97,105,110,109,111,110,101,121,105,109,97,103,101,110,97,109,101,115,121, -111,117,110,103,108,105,110,101,115,108,97,116,101,114,99,111,108,111,114,103, -114,101,101,110,102,114,111,110,116,38,97,109,112,59,119,97,116,99,104,102,111, -114,99,101,112,114,105,99,101,114,117,108,101,115,98,101,103,105,110,97,102,116, -101,114,118,105,115,105,116,105,115,115,117,101,97,114,101,97,115,98,101,108,111 -,119,105,110,100,101,120,116,111,116,97,108,104,111,117,114,115,108,97,98,101, -108,112,114,105,110,116,112,114,101,115,115,98,117,105,108,116,108,105,110,107, -115,115,112,101,101,100,115,116,117,100,121,116,114,97,100,101,102,111,117,110, -100,115,101,110,115,101,117,110,100,101,114,115,104,111,119,110,102,111,114,109, -115,114,97,110,103,101,97,100,100,101,100,115,116,105,108,108,109,111,118,101, -100,116,97,107,101,110,97,98,111,118,101,102,108,97,115,104,102,105,120,101,100, -111,102,116,101,110,111,116,104,101,114,118,105,101,119,115,99,104,101,99,107, -108,101,103,97,108,114,105,118,101,114,105,116,101,109,115,113,117,105,99,107, -115,104,97,112,101,104,117,109,97,110,101,120,105,115,116,103,111,105,110,103, -109,111,118,105,101,116,104,105,114,100,98,97,115,105,99,112,101,97,99,101,115, -116,97,103,101,119,105,100,116,104,108,111,103,105,110,105,100,101,97,115,119, -114,111,116,101,112,97,103,101,115,117,115,101,114,115,100,114,105,118,101,115, -116,111,114,101,98,114,101,97,107,115,111,117,116,104,118,111,105,99,101,115,105 -,116,101,115,109,111,110,116,104,119,104,101,114,101,98,117,105,108,100,119,104, -105,99,104,101,97,114,116,104,102,111,114,117,109,116,104,114,101,101,115,112, -111,114,116,112,97,114,116,121,67,108,105,99,107,108,111,119,101,114,108,105,118 -,101,115,99,108,97,115,115,108,97,121,101,114,101,110,116,114,121,115,116,111, -114,121,117,115,97,103,101,115,111,117,110,100,99,111,117,114,116,121,111,117, -114,32,98,105,114,116,104,112,111,112,117,112,116,121,112,101,115,97,112,112,108 -,121,73,109,97,103,101,98,101,105,110,103,117,112,112,101,114,110,111,116,101, -115,101,118,101,114,121,115,104,111,119,115,109,101,97,110,115,101,120,116,114, -97,109,97,116,99,104,116,114,97,99,107,107,110,111,119,110,101,97,114,108,121,98 -,101,103,97,110,115,117,112,101,114,112,97,112,101,114,110,111,114,116,104,108, -101,97,114,110,103,105,118,101,110,110,97,109,101,100,101,110,100,101,100,84,101 -,114,109,115,112,97,114,116,115,71,114,111,117,112,98,114,97,110,100,117,115,105 -,110,103,119,111,109,97,110,102,97,108,115,101,114,101,97,100,121,97,117,100,105 -,111,116,97,107,101,115,119,104,105,108,101,46,99,111,109,47,108,105,118,101,100 -,99,97,115,101,115,100,97,105,108,121,99,104,105,108,100,103,114,101,97,116,106, -117,100,103,101,116,104,111,115,101,117,110,105,116,115,110,101,118,101,114,98, -114,111,97,100,99,111,97,115,116,99,111,118,101,114,97,112,112,108,101,102,105, -108,101,115,99,121,99,108,101,115,99,101,110,101,112,108,97,110,115,99,108,105, -99,107,119,114,105,116,101,113,117,101,101,110,112,105,101,99,101,101,109,97,105 -,108,102,114,97,109,101,111,108,100,101,114,112,104,111,116,111,108,105,109,105, -116,99,97,99,104,101,99,105,118,105,108,115,99,97,108,101,101,110,116,101,114, -116,104,101,109,101,116,104,101,114,101,116,111,117,99,104,98,111,117,110,100, -114,111,121,97,108,97,115,107,101,100,119,104,111,108,101,115,105,110,99,101,115 -,116,111,99,107,32,110,97,109,101,102,97,105,116,104,104,101,97,114,116,101,109, -112,116,121,111,102,102,101,114,115,99,111,112,101,111,119,110,101,100,109,105, -103,104,116,97,108,98,117,109,116,104,105,110,107,98,108,111,111,100,97,114,114, -97,121,109,97,106,111,114,116,114,117,115,116,99,97,110,111,110,117,110,105,111, -110,99,111,117,110,116,118,97,108,105,100,115,116,111,110,101,83,116,121,108,101 -,76,111,103,105,110,104,97,112,112,121,111,99,99,117,114,108,101,102,116,58,102, -114,101,115,104,113,117,105,116,101,102,105,108,109,115,103,114,97,100,101,110, -101,101,100,115,117,114,98,97,110,102,105,103,104,116,98,97,115,105,115,104,111, -118,101,114,97,117,116,111,59,114,111,117,116,101,46,104,116,109,108,109,105,120 -,101,100,102,105,110,97,108,89,111,117,114,32,115,108,105,100,101,116,111,112, -105,99,98,114,111,119,110,97,108,111,110,101,100,114,97,119,110,115,112,108,105, -116,114,101,97,99,104,82,105,103,104,116,100,97,116,101,115,109,97,114,99,104, -113,117,111,116,101,103,111,111,100,115,76,105,110,107,115,100,111,117,98,116,97 -,115,121,110,99,116,104,117,109,98,97,108,108,111,119,99,104,105,101,102,121,111 -,117,116,104,110,111,118,101,108,49,48,112,120,59,115,101,114,118,101,117,110, -116,105,108,104,97,110,100,115,67,104,101,99,107,83,112,97,99,101,113,117,101, -114,121,106,97,109,101,115,101,113,117,97,108,116,119,105,99,101,48,44,48,48,48, -83,116,97,114,116,112,97,110,101,108,115,111,110,103,115,114,111,117,110,100,101 -,105,103,104,116,115,104,105,102,116,119,111,114,116,104,112,111,115,116,115,108 -,101,97,100,115,119,101,101,107,115,97,118,111,105,100,116,104,101,115,101,109, -105,108,101,115,112,108,97,110,101,115,109,97,114,116,97,108,112,104,97,112,108, -97,110,116,109,97,114,107,115,114,97,116,101,115,112,108,97,121,115,99,108,97, -105,109,115,97,108,101,115,116,101,120,116,115,115,116,97,114,115,119,114,111, -110,103,60,47,104,51,62,116,104,105,110,103,46,111,114,103,47,109,117,108,116, -105,104,101,97,114,100,80,111,119,101,114,115,116,97,110,100,116,111,107,101,110 -,115,111,108,105,100,40,116,104,105,115,98,114,105,110,103,115,104,105,112,115, -115,116,97,102,102,116,114,105,101,100,99,97,108,108,115,102,117,108,108,121,102 -,97,99,116,115,97,103,101,110,116,84,104,105,115,32,47,47,45,45,62,97,100,109, -105,110,101,103,121,112,116,69,118,101,110,116,49,53,112,120,59,69,109,97,105, -108,116,114,117,101,34,99,114,111,115,115,115,112,101,110,116,98,108,111,103,115 -,98,111,120,34,62,110,111,116,101,100,108,101,97,118,101,99,104,105,110,97,115, -105,122,101,115,103,117,101,115,116,60,47,104,52,62,114,111,98,111,116,104,101, -97,118,121,116,114,117,101,44,115,101,118,101,110,103,114,97,110,100,99,114,105, -109,101,115,105,103,110,115,97,119,97,114,101,100,97,110,99,101,112,104,97,115, -101,62,60,33,45,45,101,110,95,85,83,38,35,51,57,59,50,48,48,112,120,95,110,97, -109,101,108,97,116,105,110,101,110,106,111,121,97,106,97,120,46,97,116,105,111, -110,115,109,105,116,104,85,46,83,46,32,104,111,108,100,115,112,101,116,101,114, -105,110,100,105,97,110,97,118,34,62,99,104,97,105,110,115,99,111,114,101,99,111, -109,101,115,100,111,105,110,103,112,114,105,111,114,83,104,97,114,101,49,57,57, -48,115,114,111,109,97,110,108,105,115,116,115,106,97,112,97,110,102,97,108,108, -115,116,114,105,97,108,111,119,110,101,114,97,103,114,101,101,60,47,104,50,62,97 -,98,117,115,101,97,108,101,114,116,111,112,101,114,97,34,45,47,47,87,99,97,114, -100,115,104,105,108,108,115,116,101,97,109,115,80,104,111,116,111,116,114,117, -116,104,99,108,101,97,110,46,112,104,112,63,115,97,105,110,116,109,101,116,97, -108,108,111,117,105,115,109,101,97,110,116,112,114,111,111,102,98,114,105,101, -102,114,111,119,34,62,103,101,110,114,101,116,114,117,99,107,108,111,111,107,115 -,86,97,108,117,101,70,114,97,109,101,46,110,101,116,47,45,45,62,10,60,116,114, -121,32,123,10,118,97,114,32,109,97,107,101,115,99,111,115,116,115,112,108,97,105 -,110,97,100,117,108,116,113,117,101,115,116,116,114,97,105,110,108,97,98,111,114 -,104,101,108,112,115,99,97,117,115,101,109,97,103,105,99,109,111,116,111,114,116 -,104,101,105,114,50,53,48,112,120,108,101,97,115,116,115,116,101,112,115,67,111, -117,110,116,99,111,117,108,100,103,108,97,115,115,115,105,100,101,115,102,117, -110,100,115,104,111,116,101,108,97,119,97,114,100,109,111,117,116,104,109,111, -118,101,115,112,97,114,105,115,103,105,118,101,115,100,117,116,99,104,116,101, -120,97,115,102,114,117,105,116,110,117,108,108,44,124,124,91,93,59,116,111,112, -34,62,10,60,33,45,45,80,79,83,84,34,111,99,101,97,110,60,98,114,47,62,102,108, -111,111,114,115,112,101,97,107,100,101,112,116,104,32,115,105,122,101,98,97,110, -107,115,99,97,116,99,104,99,104,97,114,116,50,48,112,120,59,97,108,105,103,110, -100,101,97,108,115,119,111,117,108,100,53,48,112,120,59,117,114,108,61,34,112,97 -,114,107,115,109,111,117,115,101,77,111,115,116,32,46,46,46,60,47,97,109,111,110 -,103,98,114,97,105,110,98,111,100,121,32,110,111,110,101,59,98,97,115,101,100,99 -,97,114,114,121,100,114,97,102,116,114,101,102,101,114,112,97,103,101,95,104,111 -,109,101,46,109,101,116,101,114,100,101,108,97,121,100,114,101,97,109,112,114, -111,118,101,106,111,105,110,116,60,47,116,114,62,100,114,117,103,115,60,33,45,45 -,32,97,112,114,105,108,105,100,101,97,108,97,108,108,101,110,101,120,97,99,116, -102,111,114,116,104,99,111,100,101,115,108,111,103,105,99,86,105,101,119,32,115, -101,101,109,115,98,108,97,110,107,112,111,114,116,115,32,40,50,48,48,115,97,118, -101,100,95,108,105,110,107,103,111,97,108,115,103,114,97,110,116,103,114,101,101 -,107,104,111,109,101,115,114,105,110,103,115,114,97,116,101,100,51,48,112,120,59 -,119,104,111,115,101,112,97,114,115,101,40,41,59,34,32,66,108,111,99,107,108,105 -,110,117,120,106,111,110,101,115,112,105,120,101,108,39,41,59,34,62,41,59,105, -102,40,45,108,101,102,116,100,97,118,105,100,104,111,114,115,101,70,111,99,117, -115,114,97,105,115,101,98,111,120,101,115,84,114,97,99,107,101,109,101,110,116, -60,47,101,109,62,98,97,114,34,62,46,115,114,99,61,116,111,119,101,114,97,108,116 -,61,34,99,97,98,108,101,104,101,110,114,121,50,52,112,120,59,115,101,116,117,112 -,105,116,97,108,121,115,104,97,114,112,109,105,110,111,114,116,97,115,116,101, -119,97,110,116,115,116,104,105,115,46,114,101,115,101,116,119,104,101,101,108, -103,105,114,108,115,47,99,115,115,47,49,48,48,37,59,99,108,117,98,115,115,116, -117,102,102,98,105,98,108,101,118,111,116,101,115,32,49,48,48,48,107,111,114,101 -,97,125,41,59,13,10,98,97,110,100,115,113,117,101,117,101,61,32,123,125,59,56,48 -,112,120,59,99,107,105,110,103,123,13,10,9,9,97,104,101,97,100,99,108,111,99,107 -,105,114,105,115,104,108,105,107,101,32,114,97,116,105,111,115,116,97,116,115,70 -,111,114,109,34,121,97,104,111,111,41,91,48,93,59,65,98,111,117,116,102,105,110, -100,115,60,47,104,49,62,100,101,98,117,103,116,97,115,107,115,85,82,76,32,61,99, -101,108,108,115,125,41,40,41,59,49,50,112,120,59,112,114,105,109,101,116,101,108 -,108,115,116,117,114,110,115,48,120,54,48,48,46,106,112,103,34,115,112,97,105, -110,98,101,97,99,104,116,97,120,101,115,109,105,99,114,111,97,110,103,101,108,45 -,45,62,60,47,103,105,102,116,115,115,116,101,118,101,45,108,105,110,107,98,111, -100,121,46,125,41,59,10,9,109,111,117,110,116,32,40,49,57,57,70,65,81,60,47,114, -111,103,101,114,102,114,97,110,107,67,108,97,115,115,50,56,112,120,59,102,101, -101,100,115,60,104,49,62,60,115,99,111,116,116,116,101,115,116,115,50,50,112,120 -,59,100,114,105,110,107,41,32,124,124,32,108,101,119,105,115,115,104,97,108,108, -35,48,51,57,59,32,102,111,114,32,108,111,118,101,100,119,97,115,116,101,48,48, -112,120,59,106,97,58,227,130,115,105,109,111,110,60,102,111,110,116,114,101,112, -108,121,109,101,101,116,115,117,110,116,101,114,99,104,101,97,112,116,105,103, -104,116,66,114,97,110,100,41,32,33,61,32,100,114,101,115,115,99,108,105,112,115, -114,111,111,109,115,111,110,107,101,121,109,111,98,105,108,109,97,105,110,46,78, -97,109,101,32,112,108,97,116,101,102,117,110,110,121,116,114,101,101,115,99,111, -109,47,34,49,46,106,112,103,119,109,111,100,101,112,97,114,97,109,83,84,65,82,84 -,108,101,102,116,32,105,100,100,101,110,44,32,50,48,49,41,59,10,125,10,102,111, -114,109,46,118,105,114,117,115,99,104,97,105,114,116,114,97,110,115,119,111,114, -115,116,80,97,103,101,115,105,116,105,111,110,112,97,116,99,104,60,33,45,45,10, -111,45,99,97,99,102,105,114,109,115,116,111,117,114,115,44,48,48,48,32,97,115, -105,97,110,105,43,43,41,123,97,100,111,98,101,39,41,91,48,93,105,100,61,49,48,98 -,111,116,104,59,109,101,110,117,32,46,50,46,109,105,46,112,110,103,34,107,101, -118,105,110,99,111,97,99,104,67,104,105,108,100,98,114,117,99,101,50,46,106,112, -103,85,82,76,41,43,46,106,112,103,124,115,117,105,116,101,115,108,105,99,101,104 -,97,114,114,121,49,50,48,34,32,115,119,101,101,116,116,114,62,13,10,110,97,109, -101,61,100,105,101,103,111,112,97,103,101,32,115,119,105,115,115,45,45,62,10,10, -35,102,102,102,59,34,62,76,111,103,46,99,111,109,34,116,114,101,97,116,115,104, -101,101,116,41,32,38,38,32,49,52,112,120,59,115,108,101,101,112,110,116,101,110, -116,102,105,108,101,100,106,97,58,227,131,105,100,61,34,99,78,97,109,101,34,119, -111,114,115,101,115,104,111,116,115,45,98,111,120,45,100,101,108,116,97,10,38, -108,116,59,98,101,97,114,115,58,52,56,90,60,100,97,116,97,45,114,117,114,97,108, -60,47,97,62,32,115,112,101,110,100,98,97,107,101,114,115,104,111,112,115,61,32, -34,34,59,112,104,112,34,62,99,116,105,111,110,49,51,112,120,59,98,114,105,97,110 -,104,101,108,108,111,115,105,122,101,61,111,61,37,50,70,32,106,111,105,110,109, -97,121,98,101,60,105,109,103,32,105,109,103,34,62,44,32,102,106,115,105,109,103, -34,32,34,41,91,48,93,77,84,111,112,66,84,121,112,101,34,110,101,119,108,121,68, -97,110,115,107,99,122,101,99,104,116,114,97,105,108,107,110,111,119,115,60,47, -104,53,62,102,97,113,34,62,122,104,45,99,110,49,48,41,59,10,45,49,34,41,59,116, -121,112,101,61,98,108,117,101,115,116,114,117,108,121,100,97,118,105,115,46,106, -115,39,59,62,13,10,60,33,115,116,101,101,108,32,121,111,117,32,104,50,62,13,10, -102,111,114,109,32,106,101,115,117,115,49,48,48,37,32,109,101,110,117,46,13,10,9 -,13,10,119,97,108,101,115,114,105,115,107,115,117,109,101,110,116,100,100,105, -110,103,98,45,108,105,107,116,101,97,99,104,103,105,102,34,32,118,101,103,97,115 -,100,97,110,115,107,101,101,115,116,105,115,104,113,105,112,115,117,111,109,105, -115,111,98,114,101,100,101,115,100,101,101,110,116,114,101,116,111,100,111,115, -112,117,101,100,101,97,195,177,111,115,101,115,116,195,161,116,105,101,110,101, -104,97,115,116,97,111,116,114,111,115,112,97,114,116,101,100,111,110,100,101,110 -,117,101,118,111,104,97,99,101,114,102,111,114,109,97,109,105,115,109,111,109, -101,106,111,114,109,117,110,100,111,97,113,117,195,173,100,195,173,97,115,115, -195,179,108,111,97,121,117,100,97,102,101,99,104,97,116,111,100,97,115,116,97, -110,116,111,109,101,110,111,115,100,97,116,111,115,111,116,114,97,115,115,105, -116,105,111,109,117,99,104,111,97,104,111,114,97,108,117,103,97,114,109,97,121, -111,114,101,115,116,111,115,104,111,114,97,115,116,101,110,101,114,97,110,116, -101,115,102,111,116,111,115,101,115,116,97,115,112,97,195,173,115,110,117,101, -118,97,115,97,108,117,100,102,111,114,111,115,109,101,100,105,111,113,117,105, -101,110,109,101,115,101,115,112,111,100,101,114,99,104,105,108,101,115,101,114, -195,161,118,101,99,101,115,100,101,99,105,114,106,111,115,195,169,101,115,116,97 -,114,118,101,110,116,97,103,114,117,112,111,104,101,99,104,111,101,108,108,111, -115,116,101,110,103,111,97,109,105,103,111,99,111,115,97,115,110,105,118,101,108 -,103,101,110,116,101,109,105,115,109,97,97,105,114,101,115,106,117,108,105,111, -116,101,109,97,115,104,97,99,105,97,102,97,118,111,114,106,117,110,105,111,108, -105,98,114,101,112,117,110,116,111,98,117,101,110,111,97,117,116,111,114,97,98, -114,105,108,98,117,101,110,97,116,101,120,116,111,109,97,114,122,111,115,97,98, -101,114,108,105,115,116,97,108,117,101,103,111,99,195,179,109,111,101,110,101, -114,111,106,117,101,103,111,112,101,114,195,186,104,97,98,101,114,101,115,116, -111,121,110,117,110,99,97,109,117,106,101,114,118,97,108,111,114,102,117,101,114 -,97,108,105,98,114,111,103,117,115,116,97,105,103,117,97,108,118,111,116,111,115 -,99,97,115,111,115,103,117,195,173,97,112,117,101,100,111,115,111,109,111,115,97 -,118,105,115,111,117,115,116,101,100,100,101,98,101,110,110,111,99,104,101,98, -117,115,99,97,102,97,108,116,97,101,117,114,111,115,115,101,114,105,101,100,105, -99,104,111,99,117,114,115,111,99,108,97,118,101,99,97,115,97,115,108,101,195,179 -,110,112,108,97,122,111,108,97,114,103,111,111,98,114,97,115,118,105,115,116,97, -97,112,111,121,111,106,117,110,116,111,116,114,97,116,97,118,105,115,116,111,99, -114,101,97,114,99,97,109,112,111,104,101,109,111,115,99,105,110,99,111,99,97,114 -,103,111,112,105,115,111,115,111,114,100,101,110,104,97,99,101,110,195,161,114, -101,97,100,105,115,99,111,112,101,100,114,111,99,101,114,99,97,112,117,101,100, -97,112,97,112,101,108,109,101,110,111,114,195,186,116,105,108,99,108,97,114,111, -106,111,114,103,101,99,97,108,108,101,112,111,110,101,114,116,97,114,100,101,110 -,97,100,105,101,109,97,114,99,97,115,105,103,117,101,101,108,108,97,115,115,105, -103,108,111,99,111,99,104,101,109,111,116,111,115,109,97,100,114,101,99,108,97, -115,101,114,101,115,116,111,110,105,195,177,111,113,117,101,100,97,112,97,115,97 -,114,98,97,110,99,111,104,105,106,111,115,118,105,97,106,101,112,97,98,108,111, -195,169,115,116,101,118,105,101,110,101,114,101,105,110,111,100,101,106,97,114, -102,111,110,100,111,99,97,110,97,108,110,111,114,116,101,108,101,116,114,97,99, -97,117,115,97,116,111,109,97,114,109,97,110,111,115,108,117,110,101,115,97,117, -116,111,115,118,105,108,108,97,118,101,110,100,111,112,101,115,97,114,116,105, -112,111,115,116,101,110,103,97,109,97,114,99,111,108,108,101,118,97,112,97,100, -114,101,117,110,105,100,111,118,97,109,111,115,122,111,110,97,115,97,109,98,111, -115,98,97,110,100,97,109,97,114,105,97,97,98,117,115,111,109,117,99,104,97,115, -117,98,105,114,114,105,111,106,97,118,105,118,105,114,103,114,97,100,111,99,104, -105,99,97,97,108,108,195,173,106,111,118,101,110,100,105,99,104,97,101,115,116, -97,110,116,97,108,101,115,115,97,108,105,114,115,117,101,108,111,112,101,115,111 -,115,102,105,110,101,115,108,108,97,109,97,98,117,115,99,111,195,169,115,116,97, -108,108,101,103,97,110,101,103,114,111,112,108,97,122,97,104,117,109,111,114,112 -,97,103,97,114,106,117,110,116,97,100,111,98,108,101,105,115,108,97,115,98,111, -108,115,97,98,97,195,177,111,104,97,98,108,97,108,117,99,104,97,195,129,114,101, -97,100,105,99,101,110,106,117,103,97,114,110,111,116,97,115,118,97,108,108,101, -97,108,108,195,161,99,97,114,103,97,100,111,108,111,114,97,98,97,106,111,101,115 -,116,195,169,103,117,115,116,111,109,101,110,116,101,109,97,114,105,111,102,105, -114,109,97,99,111,115,116,111,102,105,99,104,97,112,108,97,116,97,104,111,103,97 -,114,97,114,116,101,115,108,101,121,101,115,97,113,117,101,108,109,117,115,101, -111,98,97,115,101,115,112,111,99,111,115,109,105,116,97,100,99,105,101,108,111, -99,104,105,99,111,109,105,101,100,111,103,97,110,97,114,115,97,110,116,111,101, -116,97,112,97,100,101,98,101,115,112,108,97,121,97,114,101,100,101,115,115,105, -101,116,101,99,111,114,116,101,99,111,114,101,97,100,117,100,97,115,100,101,115, -101,111,118,105,101,106,111,100,101,115,101,97,97,103,117,97,115,38,113,117,111, -116,59,100,111,109,97,105,110,99,111,109,109,111,110,115,116,97,116,117,115,101, -118,101,110,116,115,109,97,115,116,101,114,115,121,115,116,101,109,97,99,116,105 -,111,110,98,97,110,110,101,114,114,101,109,111,118,101,115,99,114,111,108,108, -117,112,100,97,116,101,103,108,111,98,97,108,109,101,100,105,117,109,102,105,108 -,116,101,114,110,117,109,98,101,114,99,104,97,110,103,101,114,101,115,117,108, -116,112,117,98,108,105,99,115,99,114,101,101,110,99,104,111,111,115,101,110,111, -114,109,97,108,116,114,97,118,101,108,105,115,115,117,101,115,115,111,117,114,99 -,101,116,97,114,103,101,116,115,112,114,105,110,103,109,111,100,117,108,101,109, -111,98,105,108,101,115,119,105,116,99,104,112,104,111,116,111,115,98,111,114,100 -,101,114,114,101,103,105,111,110,105,116,115,101,108,102,115,111,99,105,97,108, -97,99,116,105,118,101,99,111,108,117,109,110,114,101,99,111,114,100,102,111,108, -108,111,119,116,105,116,108,101,62,101,105,116,104,101,114,108,101,110,103,116, -104,102,97,109,105,108,121,102,114,105,101,110,100,108,97,121,111,117,116,97,117 -,116,104,111,114,99,114,101,97,116,101,114,101,118,105,101,119,115,117,109,109, -101,114,115,101,114,118,101,114,112,108,97,121,101,100,112,108,97,121,101,114, -101,120,112,97,110,100,112,111,108,105,99,121,102,111,114,109,97,116,100,111,117 -,98,108,101,112,111,105,110,116,115,115,101,114,105,101,115,112,101,114,115,111, -110,108,105,118,105,110,103,100,101,115,105,103,110,109,111,110,116,104,115,102, -111,114,99,101,115,117,110,105,113,117,101,119,101,105,103,104,116,112,101,111, -112,108,101,101,110,101,114,103,121,110,97,116,117,114,101,115,101,97,114,99,104 -,102,105,103,117,114,101,104,97,118,105,110,103,99,117,115,116,111,109,111,102, -102,115,101,116,108,101,116,116,101,114,119,105,110,100,111,119,115,117,98,109, -105,116,114,101,110,100,101,114,103,114,111,117,112,115,117,112,108,111,97,100, -104,101,97,108,116,104,109,101,116,104,111,100,118,105,100,101,111,115,115,99, -104,111,111,108,102,117,116,117,114,101,115,104,97,100,111,119,100,101,98,97,116 -,101,118,97,108,117,101,115,79,98,106,101,99,116,111,116,104,101,114,115,114,105 -,103,104,116,115,108,101,97,103,117,101,99,104,114,111,109,101,115,105,109,112, -108,101,110,111,116,105,99,101,115,104,97,114,101,100,101,110,100,105,110,103, -115,101,97,115,111,110,114,101,112,111,114,116,111,110,108,105,110,101,115,113, -117,97,114,101,98,117,116,116,111,110,105,109,97,103,101,115,101,110,97,98,108, -101,109,111,118,105,110,103,108,97,116,101,115,116,119,105,110,116,101,114,70, -114,97,110,99,101,112,101,114,105,111,100,115,116,114,111,110,103,114,101,112, -101,97,116,76,111,110,100,111,110,100,101,116,97,105,108,102,111,114,109,101,100 -,100,101,109,97,110,100,115,101,99,117,114,101,112,97,115,115,101,100,116,111, -103,103,108,101,112,108,97,99,101,115,100,101,118,105,99,101,115,116,97,116,105, -99,99,105,116,105,101,115,115,116,114,101,97,109,121,101,108,108,111,119,97,116, -116,97,99,107,115,116,114,101,101,116,102,108,105,103,104,116,104,105,100,100, -101,110,105,110,102,111,34,62,111,112,101,110,101,100,117,115,101,102,117,108, -118,97,108,108,101,121,99,97,117,115,101,115,108,101,97,100,101,114,115,101,99, -114,101,116,115,101,99,111,110,100,100,97,109,97,103,101,115,112,111,114,116,115 -,101,120,99,101,112,116,114,97,116,105,110,103,115,105,103,110,101,100,116,104, -105,110,103,115,101,102,102,101,99,116,102,105,101,108,100,115,115,116,97,116, -101,115,111,102,102,105,99,101,118,105,115,117,97,108,101,100,105,116,111,114, -118,111,108,117,109,101,82,101,112,111,114,116,109,117,115,101,117,109,109,111, -118,105,101,115,112,97,114,101,110,116,97,99,99,101,115,115,109,111,115,116,108, -121,109,111,116,104,101,114,34,32,105,100,61,34,109,97,114,107,101,116,103,114, -111,117,110,100,99,104,97,110,99,101,115,117,114,118,101,121,98,101,102,111,114, -101,115,121,109,98,111,108,109,111,109,101,110,116,115,112,101,101,99,104,109, -111,116,105,111,110,105,110,115,105,100,101,109,97,116,116,101,114,67,101,110, -116,101,114,111,98,106,101,99,116,101,120,105,115,116,115,109,105,100,100,108, -101,69,117,114,111,112,101,103,114,111,119,116,104,108,101,103,97,99,121,109,97, -110,110,101,114,101,110,111,117,103,104,99,97,114,101,101,114,97,110,115,119,101 -,114,111,114,105,103,105,110,112,111,114,116,97,108,99,108,105,101,110,116,115, -101,108,101,99,116,114,97,110,100,111,109,99,108,111,115,101,100,116,111,112,105 -,99,115,99,111,109,105,110,103,102,97,116,104,101,114,111,112,116,105,111,110, -115,105,109,112,108,121,114,97,105,115,101,100,101,115,99,97,112,101,99,104,111, -115,101,110,99,104,117,114,99,104,100,101,102,105,110,101,114,101,97,115,111,110 -,99,111,114,110,101,114,111,117,116,112,117,116,109,101,109,111,114,121,105,102, -114,97,109,101,112,111,108,105,99,101,109,111,100,101,108,115,78,117,109,98,101, -114,100,117,114,105,110,103,111,102,102,101,114,115,115,116,121,108,101,115,107, -105,108,108,101,100,108,105,115,116,101,100,99,97,108,108,101,100,115,105,108, -118,101,114,109,97,114,103,105,110,100,101,108,101,116,101,98,101,116,116,101, -114,98,114,111,119,115,101,108,105,109,105,116,115,71,108,111,98,97,108,115,105, -110,103,108,101,119,105,100,103,101,116,99,101,110,116,101,114,98,117,100,103, -101,116,110,111,119,114,97,112,99,114,101,100,105,116,99,108,97,105,109,115,101, -110,103,105,110,101,115,97,102,101,116,121,99,104,111,105,99,101,115,112,105,114 -,105,116,45,115,116,121,108,101,115,112,114,101,97,100,109,97,107,105,110,103, -110,101,101,100,101,100,114,117,115,115,105,97,112,108,101,97,115,101,101,120, -116,101,110,116,83,99,114,105,112,116,98,114,111,107,101,110,97,108,108,111,119, -115,99,104,97,114,103,101,100,105,118,105,100,101,102,97,99,116,111,114,109,101, -109,98,101,114,45,98,97,115,101,100,116,104,101,111,114,121,99,111,110,102,105, -103,97,114,111,117,110,100,119,111,114,107,101,100,104,101,108,112,101,100,67, -104,117,114,99,104,105,109,112,97,99,116,115,104,111,117,108,100,97,108,119,97, -121,115,108,111,103,111,34,32,98,111,116,116,111,109,108,105,115,116,34,62,41, -123,118,97,114,32,112,114,101,102,105,120,111,114,97,110,103,101,72,101,97,100, -101,114,46,112,117,115,104,40,99,111,117,112,108,101,103,97,114,100,101,110,98, -114,105,100,103,101,108,97,117,110,99,104,82,101,118,105,101,119,116,97,107,105, -110,103,118,105,115,105,111,110,108,105,116,116,108,101,100,97,116,105,110,103, -66,117,116,116,111,110,98,101,97,117,116,121,116,104,101,109,101,115,102,111,114 -,103,111,116,83,101,97,114,99,104,97,110,99,104,111,114,97,108,109,111,115,116, -108,111,97,100,101,100,67,104,97,110,103,101,114,101,116,117,114,110,115,116,114 -,105,110,103,114,101,108,111,97,100,77,111,98,105,108,101,105,110,99,111,109,101 -,115,117,112,112,108,121,83,111,117,114,99,101,111,114,100,101,114,115,118,105, -101,119,101,100,38,110,98,115,112,59,99,111,117,114,115,101,65,98,111,117,116,32 -,105,115,108,97,110,100,60,104,116,109,108,32,99,111,111,107,105,101,110,97,109, -101,61,34,97,109,97,122,111,110,109,111,100,101,114,110,97,100,118,105,99,101, -105,110,60,47,97,62,58,32,84,104,101,32,100,105,97,108,111,103,104,111,117,115, -101,115,66,69,71,73,78,32,77,101,120,105,99,111,115,116,97,114,116,115,99,101, -110,116,114,101,104,101,105,103,104,116,97,100,100,105,110,103,73,115,108,97,110 -,100,97,115,115,101,116,115,69,109,112,105,114,101,83,99,104,111,111,108,101,102 -,102,111,114,116,100,105,114,101,99,116,110,101,97,114,108,121,109,97,110,117,97 -,108,83,101,108,101,99,116,46,10,10,79,110,101,106,111,105,110,101,100,109,101, -110,117,34,62,80,104,105,108,105,112,97,119,97,114,100,115,104,97,110,100,108, -101,105,109,112,111,114,116,79,102,102,105,99,101,114,101,103,97,114,100,115,107 -,105,108,108,115,110,97,116,105,111,110,83,112,111,114,116,115,100,101,103,114, -101,101,119,101,101,107,108,121,32,40,101,46,103,46,98,101,104,105,110,100,100, -111,99,116,111,114,108,111,103,103,101,100,117,110,105,116,101,100,60,47,98,62, -60,47,98,101,103,105,110,115,112,108,97,110,116,115,97,115,115,105,115,116,97, -114,116,105,115,116,105,115,115,117,101,100,51,48,48,112,120,124,99,97,110,97, -100,97,97,103,101,110,99,121,115,99,104,101,109,101,114,101,109,97,105,110,66, -114,97,122,105,108,115,97,109,112,108,101,108,111,103,111,34,62,98,101,121,111, -110,100,45,115,99,97,108,101,97,99,99,101,112,116,115,101,114,118,101,100,109,97 -,114,105,110,101,70,111,111,116,101,114,99,97,109,101,114,97,60,47,104,49,62,10, -95,102,111,114,109,34,108,101,97,118,101,115,115,116,114,101,115,115,34,32,47,62 -,13,10,46,103,105,102,34,32,111,110,108,111,97,100,108,111,97,100,101,114,79,120 -,102,111,114,100,115,105,115,116,101,114,115,117,114,118,105,118,108,105,115,116 -,101,110,102,101,109,97,108,101,68,101,115,105,103,110,115,105,122,101,61,34,97, -112,112,101,97,108,116,101,120,116,34,62,108,101,118,101,108,115,116,104,97,110, -107,115,104,105,103,104,101,114,102,111,114,99,101,100,97,110,105,109,97,108,97, -110,121,111,110,101,65,102,114,105,99,97,97,103,114,101,101,100,114,101,99,101, -110,116,80,101,111,112,108,101,60,98,114,32,47,62,119,111,110,100,101,114,112, -114,105,99,101,115,116,117,114,110,101,100,124,124,32,123,125,59,109,97,105,110, -34,62,105,110,108,105,110,101,115,117,110,100,97,121,119,114,97,112,34,62,102,97 -,105,108,101,100,99,101,110,115,117,115,109,105,110,117,116,101,98,101,97,99,111 -,110,113,117,111,116,101,115,49,53,48,112,120,124,101,115,116,97,116,101,114,101 -,109,111,116,101,101,109,97,105,108,34,108,105,110,107,101,100,114,105,103,104, -116,59,115,105,103,110,97,108,102,111,114,109,97,108,49,46,104,116,109,108,115, -105,103,110,117,112,112,114,105,110,99,101,102,108,111,97,116,58,46,112,110,103, -34,32,102,111,114,117,109,46,65,99,99,101,115,115,112,97,112,101,114,115,115,111 -,117,110,100,115,101,120,116,101,110,100,72,101,105,103,104,116,115,108,105,100, -101,114,85,84,70,45,56,34,38,97,109,112,59,32,66,101,102,111,114,101,46,32,87, -105,116,104,115,116,117,100,105,111,111,119,110,101,114,115,109,97,110,97,103, -101,112,114,111,102,105,116,106,81,117,101,114,121,97,110,110,117,97,108,112,97, -114,97,109,115,98,111,117,103,104,116,102,97,109,111,117,115,103,111,111,103,108 -,101,108,111,110,103,101,114,105,43,43,41,32,123,105,115,114,97,101,108,115,97, -121,105,110,103,100,101,99,105,100,101,104,111,109,101,34,62,104,101,97,100,101, -114,101,110,115,117,114,101,98,114,97,110,99,104,112,105,101,99,101,115,98,108, -111,99,107,59,115,116,97,116,101,100,116,111,112,34,62,60,114,97,99,105,110,103, -114,101,115,105,122,101,45,45,38,103,116,59,112,97,99,105,116,121,115,101,120, -117,97,108,98,117,114,101,97,117,46,106,112,103,34,32,49,48,44,48,48,48,111,98, -116,97,105,110,116,105,116,108,101,115,97,109,111,117,110,116,44,32,73,110,99,46 -,99,111,109,101,100,121,109,101,110,117,34,32,108,121,114,105,99,115,116,111,100 -,97,121,46,105,110,100,101,101,100,99,111,117,110,116,121,95,108,111,103,111,46, -70,97,109,105,108,121,108,111,111,107,101,100,77,97,114,107,101,116,108,115,101, -32,105,102,80,108,97,121,101,114,116,117,114,107,101,121,41,59,118,97,114,32,102 -,111,114,101,115,116,103,105,118,105,110,103,101,114,114,111,114,115,68,111,109, -97,105,110,125,101,108,115,101,123,105,110,115,101,114,116,66,108,111,103,60,47, -102,111,111,116,101,114,108,111,103,105,110,46,102,97,115,116,101,114,97,103,101 -,110,116,115,60,98,111,100,121,32,49,48,112,120,32,48,112,114,97,103,109,97,102, -114,105,100,97,121,106,117,110,105,111,114,100,111,108,108,97,114,112,108,97,99, -101,100,99,111,118,101,114,115,112,108,117,103,105,110,53,44,48,48,48,32,112,97, -103,101,34,62,98,111,115,116,111,110,46,116,101,115,116,40,97,118,97,116,97,114, -116,101,115,116,101,100,95,99,111,117,110,116,102,111,114,117,109,115,115,99,104 -,101,109,97,105,110,100,101,120,44,102,105,108,108,101,100,115,104,97,114,101, -115,114,101,97,100,101,114,97,108,101,114,116,40,97,112,112,101,97,114,83,117,98 -,109,105,116,108,105,110,101,34,62,98,111,100,121,34,62,10,42,32,84,104,101,84, -104,111,117,103,104,115,101,101,105,110,103,106,101,114,115,101,121,78,101,119, -115,60,47,118,101,114,105,102,121,101,120,112,101,114,116,105,110,106,117,114, -121,119,105,100,116,104,61,67,111,111,107,105,101,83,84,65,82,84,32,97,99,114, -111,115,115,95,105,109,97,103,101,116,104,114,101,97,100,110,97,116,105,118,101, -112,111,99,107,101,116,98,111,120,34,62,10,83,121,115,116,101,109,32,68,97,118, -105,100,99,97,110,99,101,114,116,97,98,108,101,115,112,114,111,118,101,100,65, -112,114,105,108,32,114,101,97,108,108,121,100,114,105,118,101,114,105,116,101, -109,34,62,109,111,114,101,34,62,98,111,97,114,100,115,99,111,108,111,114,115,99, -97,109,112,117,115,102,105,114,115,116,32,124,124,32,91,93,59,109,101,100,105,97 -,46,103,117,105,116,97,114,102,105,110,105,115,104,119,105,100,116,104,58,115, -104,111,119,101,100,79,116,104,101,114,32,46,112,104,112,34,32,97,115,115,117, -109,101,108,97,121,101,114,115,119,105,108,115,111,110,115,116,111,114,101,115, -114,101,108,105,101,102,115,119,101,100,101,110,67,117,115,116,111,109,101,97, -115,105,108,121,32,121,111,117,114,32,83,116,114,105,110,103,10,10,87,104,105, -108,116,97,121,108,111,114,99,108,101,97,114,58,114,101,115,111,114,116,102,114, -101,110,99,104,116,104,111,117,103,104,34,41,32,43,32,34,60,98,111,100,121,62,98 -,117,121,105,110,103,98,114,97,110,100,115,77,101,109,98,101,114,110,97,109,101, -34,62,111,112,112,105,110,103,115,101,99,116,111,114,53,112,120,59,34,62,118,115 -,112,97,99,101,112,111,115,116,101,114,109,97,106,111,114,32,99,111,102,102,101, -101,109,97,114,116,105,110,109,97,116,117,114,101,104,97,112,112,101,110,60,47, -110,97,118,62,107,97,110,115,97,115,108,105,110,107,34,62,73,109,97,103,101,115, -61,102,97,108,115,101,119,104,105,108,101,32,104,115,112,97,99,101,48,38,97,109, -112,59,32,10,10,73,110,32,32,112,111,119,101,114,80,111,108,115,107,105,45,99, -111,108,111,114,106,111,114,100,97,110,66,111,116,116,111,109,83,116,97,114,116, -32,45,99,111,117,110,116,50,46,104,116,109,108,110,101,119,115,34,62,48,49,46, -106,112,103,79,110,108,105,110,101,45,114,105,103,104,116,109,105,108,108,101, -114,115,101,110,105,111,114,73,83,66,78,32,48,48,44,48,48,48,32,103,117,105,100, -101,115,118,97,108,117,101,41,101,99,116,105,111,110,114,101,112,97,105,114,46, -120,109,108,34,32,32,114,105,103,104,116,115,46,104,116,109,108,45,98,108,111,99 -,107,114,101,103,69,120,112,58,104,111,118,101,114,119,105,116,104,105,110,118, -105,114,103,105,110,112,104,111,110,101,115,60,47,116,114,62,13,117,115,105,110, -103,32,10,9,118,97,114,32,62,39,41,59,10,9,60,47,116,100,62,10,60,47,116,114,62, -10,98,97,104,97,115,97,98,114,97,115,105,108,103,97,108,101,103,111,109,97,103, -121,97,114,112,111,108,115,107,105,115,114,112,115,107,105,216,177,216,175,217, -136,228,184,173,230,150,135,231,174,128,228,189,147,231,185,129,233,171,148,228, -191,161,230,129,175,228,184,173,229,155,189,230,136,145,228,187,172,228,184,128, -228,184,170,229,133,172,229,143,184,231,174,161,231,144,134,232,174,186,229,157, -155,229,143,175,228,187,165,230,156,141,229,138,161,230,151,182,233,151,180,228, -184,170,228,186,186,228,186,167,229,147,129,232,135,170,229,183,177,228,188,129, -228,184,154,230,159,165,231,156,139,229,183,165,228,189,156,232,129,148,231,179, -187,230,178,161,230,156,137,231,189,145,231,171,153,230,137,128,230,156,137,232, -175,132,232,174,186,228,184,173,229,191,131,230,150,135,231,171,160,231,148,168, -230,136,183,233,166,150,233,161,181,228,189,156,232,128,133,230,138,128,230,156, -175,233,151,174,233,162,152,231,155,184,229,133,179,228,184,139,232,189,189,230, -144,156,231,180,162,228,189,191,231,148,168,232,189,175,228,187,182,229,156,168, -231,186,191,228,184,187,233,162,152,232,181,132,230,150,153,232,167,134,233,162, -145,229,155,158,229,164,141,230,179,168,229,134,140,231,189,145,231,187,156,230, -148,182,232,151,143,229,134,133,229,174,185,230,142,168,232,141,144,229,184,130, -229,156,186,230,182,136,230,129,175,231,169,186,233,151,180,229,143,145,229,184, -131,228,187,128,228,185,136,229,165,189,229,143,139,231,148,159,230,180,187,229, -155,190,231,137,135,229,143,145,229,177,149,229,166,130,230,158,156,230,137,139, -230,156,186,230,150,176,233,151,187,230,156,128,230,150,176,230,150,185,229,188, -143,229,140,151,228,186,172,230,143,144,228,190,155,229,133,179,228,186,142,230, -155,180,229,164,154,232,191,153,228,184,170,231,179,187,231,187,159,231,159,165, -233,129,147,230,184,184,230,136,143,229,185,191,229,145,138,229,133,182,228,187, -150,229,143,145,232,161,168,229,174,137,229,133,168,231,172,172,228,184,128,228, -188,154,229,145,152,232,191,155,232,161,140,231,130,185,229,135,187,231,137,136, -230,157,131,231,148,181,229,173,144,228,184,150,231,149,140,232,174,190,232,174, -161,229,133,141,232,180,185,230,149,153,232,130,178,229,138,160,229,133,165,230, -180,187,229,138,168,228,187,150,228,187,172,229,149,134,229,147,129,229,141,154, -229,174,162,231,142,176,229,156,168,228,184,138,230,181,183,229,166,130,228,189, -149,229,183,178,231,187,143,231,149,153,232,168,128,232,175,166,231,187,134,231, -164,190,229,140,186,231,153,187,229,189,149,230,156,172,231,171,153,233,156,128, -232,166,129,228,187,183,230,160,188,230,148,175,230,140,129,229,155,189,233,153, -133,233,147,190,230,142,165,229,155,189,229,174,182,229,187,186,232,174,190,230, -156,139,229,143,139,233,152,133,232,175,187,230,179,149,229,190,139,228,189,141, -231,189,174,231,187,143,230,181,142,233,128,137,230,139,169,232,191,153,230,160, -183,229,189,147,229,137,141,229,136,134,231,177,187,230,142,146,232,161,140,229, -155,160,228,184,186,228,186,164,230,152,147,230,156,128,229,144,142,233,159,179, -228,185,144,228,184,141,232,131,189,233,128,154,232,191,135,232,161,140,228,184, -154,231,167,145,230,138,128,229,143,175,232,131,189,232,174,190,229,164,135,229, -144,136,228,189,156,229,164,167,229,174,182,231,164,190,228,188,154,231,160,148, -231,169,182,228,184,147,228,184,154,229,133,168,233,131,168,233,161,185,231,155, -174,232,191,153,233,135,140,232,191,152,230,152,175,229,188,128,229,167,139,230, -131,133,229,134,181,231,148,181,232,132,145,230,150,135,228,187,182,229,147,129, -231,137,140,229,184,174,229,138,169,230,150,135,229,140,150,232,181,132,230,186, -144,229,164,167,229,173,166,229,173,166,228,185,160,229,156,176,229,157,128,230, -181,143,232,167,136,230,138,149,232,181,132,229,183,165,231,168,139,232,166,129, -230,177,130,230,128,142,228,185,136,230,151,182,229,128,153,229,138,159,232,131, -189,228,184,187,232,166,129,231,155,174,229,137,141,232,181,132,232,174,175,229, -159,142,229,184,130,230,150,185,230,179,149,231,148,181,229,189,177,230,139,155, -232,129,152,229,163,176,230,152,142,228,187,187,228,189,149,229,129,165,229,186, -183,230,149,176,230,141,174,231,190,142,229,155,189,230,177,189,232,189,166,228, -187,139,231,187,141,228,189,134,230,152,175,228,186,164,230,181,129,231,148,159, -228,186,167,230,137,128,228,187,165,231,148,181,232,175,157,230,152,190,231,164, -186,228,184,128,228,186,155,229,141,149,228,189,141,228,186,186,229,145,152,229, -136,134,230,158,144,229,156,176,229,155,190,230,151,133,230,184,184,229,183,165, -229,133,183,229,173,166,231,148,159,231,179,187,229,136,151,231,189,145,229,143, -139,229,184,150,229,173,144,229,175,134,231,160,129,233,162,145,233,129,147,230, -142,167,229,136,182,229,156,176,229,140,186,229,159,186,230,156,172,229,133,168, -229,155,189,231,189,145,228,184,138,233,135,141,232,166,129,231,172,172,228,186, -140,229,150,156,230,172,162,232,191,155,229,133,165,229,143,139,230,131,133,232, -191,153,228,186,155,232,128,131,232,175,149,229,143,145,231,142,176,229,159,185, -232,174,173,228,187,165,228,184,138,230,148,191,229,186,156,230,136,144,228,184, -186,231,142,175,229,162,131,233,166,153,230,184,175,229,144,140,230,151,182,229, -168,177,228,185,144,229,143,145,233,128,129,228,184,128,229,174,154,229,188,128, -229,143,145,228,189,156,229,147,129,230,160,135,229,135,134,230,172,162,232,191, -142,232,167,163,229,134,179,229,156,176,230,150,185,228,184,128,228,184,139,228, -187,165,229,143,138,232,180,163,228,187,187,230,136,150,232,128,133,229,174,162, -230,136,183,228,187,163,232,161,168,231,167,175,229,136,134,229,165,179,228,186, -186,230,149,176,231,160,129,233,148,128,229,148,174,229,135,186,231,142,176,231, -166,187,231,186,191,229,186,148,231,148,168,229,136,151,232,161,168,228,184,141, -229,144,140,231,188,150,232,190,145,231,187,159,232,174,161,230,159,165,232,175, -162,228,184,141,232,166,129,230,156,137,229,133,179,230,156,186,230,158,132,229, -190,136,229,164,154,230,146,173,230,148,190,231,187,132,231,187,135,230,148,191, -231,173,150,231,155,180,230,142,165,232,131,189,229,138,155,230,157,165,230,186, -144,230,153,130,233,150,147,231,156,139,229,136,176,231,131,173,233,151,168,229, -133,179,233,148,174,228,184,147,229,140,186,233,157,158,229,184,184,232,139,177, -232,175,173,231,153,190,229,186,166,229,184,140,230,156,155,231,190,142,229,165, -179,230,175,148,232,190,131,231,159,165,232,175,134,232,167,132,229,174,154,229, -187,186,232,174,174,233,131,168,233,151,168,230,132,143,232,167,129,231,178,190, -229,189,169,230,151,165,230,156,172,230,143,144,233,171,152,229,143,145,232,168, -128,230,150,185,233,157,162,229,159,186,233,135,145,229,164,132,231,144,134,230, -157,131,233,153,144,229,189,177,231,137,135,233,147,182,232,161,140,232,191,152, -230,156,137,229,136,134,228,186,171,231,137,169,229,147,129,231,187,143,232,144, -165,230,183,187,229,138,160,228,184,147,229,174,182,232,191,153,231,167,141,232, -175,157,233,162,152,232,181,183,230,157,165,228,184,154,229,138,161,229,133,172, -229,145,138,232,174,176,229,189,149,231,174,128,228,187,139,232,180,168,233,135, -143,231,148,183,228,186,186,229,189,177,229,147,141,229,188,149,231,148,168,230, -138,165,229,145,138,233,131,168,229,136,134,229,191,171,233,128,159,229,146,168, -232,175,162,230,151,182,229,176,154,230,179,168,230,132,143,231,148,179,232,175, -183,229,173,166,230,160,161,229,186,148,232,175,165,229,142,134,229,143,178,229, -143,170,230,152,175,232,191,148,229,155,158,232,180,173,228,185,176,229,144,141, -231,167,176,228,184,186,228,186,134,230,136,144,229,138,159,232,175,180,230,152, -142,228,190,155,229,186,148,229,173,169,229,173,144,228,184,147,233,162,152,231, -168,139,229,186,143,228,184,128,232,136,172,230,156,131,229,147,161,229,143,170, -230,156,137,229,133,182,229,174,131,228,191,157,230,138,164,232,128,140,228,184, -148,228,187,138,229,164,169,231,170,151,229,143,163,229,138,168,230,128,129,231, -138,182,230,128,129,231,137,185,229,136,171,232,174,164,228,184,186,229,191,133, -233,161,187,230,155,180,230,150,176,229,176,143,232,175,180,230,136,145,229,128, -145,228,189,156,228,184,186,229,170,146,228,189,147,229,140,133,230,139,172,233, -130,163,228,185,136,228,184,128,230,160,183,229,155,189,229,134,133,230,152,175, -229,144,166,230,160,185,230,141,174,231,148,181,232,167,134,229,173,166,233,153, -162,229,133,183,230,156,137,232,191,135,231,168,139,231,148,177,228,186,142,228, -186,186,230,137,141,229,135,186,230,157,165,228,184,141,232,191,135,230,173,163, -229,156,168,230,152,142,230,152,159,230,149,133,228,186,139,229,133,179,231,179, -187,230,160,135,233,162,152,229,149,134,229,138,161,232,190,147,229,133,165,228, -184,128,231,155,180,229,159,186,231,161,128,230,149,153,229,173,166,228,186,134, -232,167,163,229,187,186,231,173,145,231,187,147,230,158,156,229,133,168,231,144, -131,233,128,154,231,159,165,232,174,161,229,136,146,229,175,185,228,186,142,232, -137,186,230,156,175,231,155,184,229,134,140,229,143,145,231,148,159,231,156,159, -231,154,132,229,187,186,231,171,139,231,173,137,231,186,167,231,177,187,229,158, -139,231,187,143,233,170,140,229,174,158,231,142,176,229,136,182,228,189,156,230, -157,165,232,135,170,230,160,135,231,173,190,228,187,165,228,184,139,229,142,159, -229,136,155,230,151,160,230,179,149,229,133,182,228,184,173,229,128,139,228,186, -186,228,184,128,229,136,135,230,140,135,229,141,151,229,133,179,233,151,173,233, -155,134,229,155,162,231,172,172,228,184,137,229,133,179,230,179,168,229,155,160, -230,173,164,231,133,167,231,137,135,230,183,177,229,156,179,229,149,134,228,184, -154,229,185,191,229,183,158,230,151,165,230,156,159,233,171,152,231,186,167,230, -156,128,232,191,145,231,187,188,229,144,136,232,161,168,231,164,186,228,184,147, -232,190,145,232,161,140,228,184,186,228,186,164,233,128,154,232,175,132,228,187, -183,232,167,137,229,190,151,231,178,190,229,141,142,229,174,182,229,186,173,229, -174,140,230,136,144,230,132,159,232,167,137,229,174,137,232,163,133,229,190,151, -229,136,176,233,130,174,228,187,182,229,136,182,229,186,166,233,163,159,229,147, -129,232,153,189,231,132,182,232,189,172,232,189,189,230,138,165,228,187,183,232, -174,176,232,128,133,230,150,185,230,161,136,232,161,140,230,148,191,228,186,186, -230,176,145,231,148,168,229,147,129,228,184,156,232,165,191,230,143,144,229,135, -186,233,133,146,229,186,151,231,132,182,229,144,142,228,187,152,230,172,190,231, -131,173,231,130,185,228,187,165,229,137,141,229,174,140,229,133,168,229,143,145, -229,184,150,232,174,190,231,189,174,233,162,134,229,175,188,229,183,165,228,184, -154,229,140,187,233,153,162,231,156,139,231,156,139,231,187,143,229,133,184,229, -142,159,229,155,160,229,185,179,229,143,176,229,144,132,231,167,141,229,162,158, -229,138,160,230,157,144,230,150,153,230,150,176,229,162,158,228,185,139,229,144, -142,232,129,140,228,184,154,230,149,136,230,158,156,228,187,138,229,185,180,232, -174,186,230,150,135,230,136,145,229,155,189,229,145,138,232,175,137,231,137,136, -228,184,187,228,191,174,230,148,185,229,143,130,228,184,142,230,137,147,229,141, -176,229,191,171,228,185,144,230,156,186,230,162,176,232,167,130,231,130,185,229, -173,152,229,156,168,231,178,190,231,165,158,232,142,183,229,190,151,229,136,169, -231,148,168,231,187,167,231,187,173,228,189,160,228,187,172,232,191,153,228,185, -136,230,168,161,229,188,143,232,175,173,232,168,128,232,131,189,229,164,159,233, -155,133,232,153,142,230,147,141,228,189,156,233,163,142,230,160,188,228,184,128, -232,181,183,231,167,145,229,173,166,228,189,147,232,130,178,231,159,173,228,191, -161,230,157,161,228,187,182,230,178,187,231,150,151,232,191,144,229,138,168,228, -186,167,228,184,154,228,188,154,232,174,174,229,175,188,232,136,170,229,133,136, -231,148,159,232,129,148,231,155,159,229,143,175,230,152,175,229,149,143,233,161, -140,231,187,147,230,158,132,228,189,156,231,148,168,232,176,131,230,159,165,232, -179,135,230,150,153,232,135,170,229,138,168,232,180,159,232,180,163,229,134,156, -228,184,154,232,174,191,233,151,174,229,174,158,230,150,189,230,142,165,229,143, -151,232,174,168,232,174,186,233,130,163,228,184,170,229,143,141,233,166,136,229, -138,160,229,188,186,229,165,179,230,128,167,232,140,131,229,155,180,230,156,141, -229,139,153,228,188,145,233,151,178,228,187,138,230,151,165,229,174,162,230,156, -141,232,167,128,231,156,139,229,143,130,229,138,160,231,154,132,232,175,157,228, -184,128,231,130,185,228,191,157,232,175,129,229,155,190,228,185,166,230,156,137, -230,149,136,230,181,139,232,175,149,231,167,187,229,138,168,230,137,141,232,131, -189,229,134,179,229,174,154,232,130,161,231,165,168,228,184,141,230,150,173,233, -156,128,230,177,130,228,184,141,229,190,151,229,138,158,230,179,149,228,185,139, -233,151,180,233,135,135,231,148,168,232,144,165,233,148,128,230,138,149,232,175, -137,231,155,174,230,160,135,231,136,177,230,131,133,230,145,132,229,189,177,230, -156,137,228,186,155,232,164,135,232,163,189,230,150,135,229,173,166,230,156,186, -228,188,154,230,149,176,229,173,151,232,163,133,228,191,174,232,180,173,231,137, -169,229,134,156,230,157,145,229,133,168,233,157,162,231,178,190,229,147,129,229, -133,182,229,174,158,228,186,139,230,131,133,230,176,180,229,185,179,230,143,144, -231,164,186,228,184,138,229,184,130,232,176,162,232,176,162,230,153,174,233,128, -154,230,149,153,229,184,136,228,184,138,228,188,160,231,177,187,229,136,171,230, -173,140,230,155,178,230,139,165,230,156,137,229,136,155,230,150,176,233,133,141, -228,187,182,229,143,170,232,166,129,230,151,182,228,187,163,232,179,135,232,168, -138,232,190,190,229,136,176,228,186,186,231,148,159,232,174,162,233,152,133,232, -128,129,229,184,136,229,177,149,231,164,186,229,191,131,231,144,134,232,180,180, -229,173,144,231,182,178,231,171,153,228,184,187,233,161,140,232,135,170,231,132, -182,231,186,167,229,136,171,231,174,128,229,141,149,230,148,185,233,157,169,233, -130,163,228,186,155,230,157,165,232,175,180,230,137,147,229,188,128,228,187,163, -231,160,129,229,136,160,233,153,164,232,175,129,229,136,184,232,138,130,231,155, -174,233,135,141,231,130,185,230,172,161,230,149,184,229,164,154,229,176,145,232, -167,132,229,136,146,232,181,132,233,135,145,230,137,190,229,136,176,228,187,165, -229,144,142,229,164,167,229,133,168,228,184,187,233,161,181,230,156,128,228,189, -179,229,155,158,231,173,148,229,164,169,228,184,139,228,191,157,233,154,156,231, -142,176,228,187,163,230,163,128,230,159,165,230,138,149,231,165,168,229,176,143, -230,151,182,230,178,146,230,156,137,230,173,163,229,184,184,231,148,154,232,135, -179,228,187,163,231,144,134,231,155,174,229,189,149,229,133,172,229,188,128,229, -164,141,229,136,182,233,135,145,232,158,141,229,185,184,231,166,143,231,137,136, -230,156,172,229,189,162,230,136,144,229,135,134,229,164,135,232,161,140,230,131, -133,229,155,158,229,136,176,230,128,157,230,131,179,230,128,142,230,160,183,229, -141,143,232,174,174,232,174,164,232,175,129,230,156,128,229,165,189,228,186,167, -231,148,159,230,140,137,231,133,167,230,156,141,232,163,133,229,185,191,228,184, -156,229,138,168,230,188,171,233,135,135,232,180,173,230,150,176,230,137,139,231, -187,132,229,155,190,233,157,162,230,157,191,229,143,130,232,128,131,230,148,191, -230,178,187,229,174,185,230,152,147,229,164,169,229,156,176,229,138,170,229,138, -155,228,186,186,228,187,172,229,141,135,231,186,167,233,128,159,229,186,166,228, -186,186,231,137,169,232,176,131,230,149,180,230,181,129,232,161,140,233,128,160, -230,136,144,230,150,135,229,173,151,233,159,169,229,155,189,232,180,184,230,152, -147,229,188,128,229,177,149,231,155,184,233,151,156,232,161,168,231,142,176,229, -189,177,232,167,134,229,166,130,230,173,164,231,190,142,229,174,185,229,164,167, -229,176,143,230,138,165,233,129,147,230,157,161,230,172,190,229,191,131,230,131, -133,232,174,184,229,164,154,230,179,149,232,167,132,229,174,182,229,177,133,228, -185,166,229,186,151,232,191,158,230,142,165,231,171,139,229,141,179,228,184,190, -230,138,165,230,138,128,229,183,167,229,165,165,232,191,144,231,153,187,229,133, -165,228,187,165,230,157,165,231,144,134,232,174,186,228,186,139,228,187,182,232, -135,170,231,148,177,228,184,173,229,141,142,229,138,158,229,133,172,229,166,136, -229,166,136,231,156,159,230,173,163,228,184,141,233,148,153,229,133,168,230,150, -135,229,144,136,229,144,140,228,187,183,229,128,188,229,136,171,228,186,186,231, -155,145,231,157,163,229,133,183,228,189,147,228,184,150,231,186,170,229,155,162, -233,152,159,229,136,155,228,184,154,230,137,191,230,139,133,229,162,158,233,149, -191,230,156,137,228,186,186,228,191,157,230,140,129,229,149,134,229,174,182,231, -187,180,228,191,174,229,143,176,230,185,190,229,183,166,229,143,179,232,130,161, -228,187,189,231,173,148,230,161,136,229,174,158,233,153,133,231,148,181,228,191, -161,231,187,143,231,144,134,231,148,159,229,145,189,229,174,163,228,188,160,228, -187,187,229,138,161,230,173,163,229,188,143,231,137,185,232,137,178,228,184,139, -230,157,165,229,141,143,228,188,154,229,143,170,232,131,189,229,189,147,231,132, -182,233,135,141,230,150,176,229,133,167,229,174,185,230,140,135,229,175,188,232, -191,144,232,161,140,230,151,165,229,191,151,232,179,163,229,174,182,232,182,133, -232,191,135,229,156,159,229,156,176,230,181,153,230,177,159,230,148,175,228,187, -152,230,142,168,229,135,186,231,171,153,233,149,191,230,157,173,229,183,158,230, -137,167,232,161,140,229,136,182,233,128,160,228,185,139,228,184,128,230,142,168, -229,185,191,231,142,176,229,156,186,230,143,143,232,191,176,229,143,152,229,140, -150,228,188,160,231,187,159,230,173,140,230,137,139,228,191,157,233,153,169,232, -175,190,231,168,139,229,140,187,231,150,151,231,187,143,232,191,135,232,191,135, -229,142,187,228,185,139,229,137,141,230,148,182,229,133,165,229,185,180,229,186, -166,230,157,130,229,191,151,231,190,142,228,184,189,230,156,128,233,171,152,231, -153,187,233,153,134,230,156,170,230,157,165,229,138,160,229,183,165,229,133,141, -232,180,163,230,149,153,231,168,139,231,137,136,229,157,151,232,186,171,228,189, -147,233,135,141,229,186,134,229,135,186,229,148,174,230,136,144,230,156,172,229, -189,162,229,188,143,229,156,159,232,177,134,229,135,186,229,131,185,228,184,156, -230,150,185,233,130,174,231,174,177,229,141,151,228,186,172,230,177,130,232,129, -140,229,143,150,229,190,151,232,129,140,228,189,141,231,155,184,228,191,161,233, -161,181,233,157,162,229,136,134,233,146,159,231,189,145,233,161,181,231,161,174, -229,174,154,229,155,190,228,190,139,231,189,145,229,157,128,231,167,175,230,158, -129,233,148,153,232,175,175,231,155,174,231,154,132,229,174,157,232,180,157,230, -156,186,229,133,179,233,163,142,233,153,169,230,142,136,230,157,131,231,151,133, -230,175,146,229,174,160,231,137,169,233,153,164,228,186,134,232,169,149,232,171, -150,231,150,190,231,151,133,229,143,138,230,151,182,230,177,130,232,180,173,231, -171,153,231,130,185,229,132,191,231,171,165,230,175,143,229,164,169,228,184,173, -229,164,174,232,174,164,232,175,134,230,175,143,228,184,170,229,164,169,230,180, -165,229,173,151,228,189,147,229,143,176,231,129,163,231,187,180,230,138,164,230, -156,172,233,161,181,228,184,170,230,128,167,229,174,152,230,150,185,229,184,184, -232,167,129,231,155,184,230,156,186,230,136,152,231,149,165,229,186,148,229,189, -147,229,190,139,229,184,136,230,150,185,228,190,191,230,160,161,229,155,173,232, -130,161,229,184,130,230,136,191,229,177,139,230,160,143,231,155,174,229,145,152, -229,183,165,229,175,188,232,135,180,231,170,129,231,132,182,233,129,147,229,133, -183,230,156,172,231,189,145,231,187,147,229,144,136,230,161,163,230,161,136,229, -138,179,229,138,168,229,143,166,229,164,150,231,190,142,229,133,131,229,188,149, -232,181,183,230,148,185,229,143,152,231,172,172,229,155,155,228,188,154,232,174, -161,232,170,170,230,152,142,233,154,144,231,167,129,229,174,157,229,174,157,232, -167,132,232,140,131,230,182,136,232,180,185,229,133,177,229,144,140,229,191,152, -232,174,176,228,189,147,231,179,187,229,184,166,230,157,165,229,144,141,229,173, -151,231,153,188,232,161,168,229,188,128,230,148,190,229,138,160,231,155,159,229, -143,151,229,136,176,228,186,140,230,137,139,229,164,167,233,135,143,230,136,144, -228,186,186,230,149,176,233,135,143,229,133,177,228,186,171,229,140,186,229,159, -159,229,165,179,229,173,169,229,142,159,229,136,153,230,137,128,229,156,168,231, -187,147,230,157,159,233,128,154,228,191,161,232,182,133,231,186,167,233,133,141, -231,189,174,229,189,147,230,151,182,228,188,152,231,167,128,230,128,167,230,132, -159,230,136,191,228,186,167,233,129,138,230,136,178,229,135,186,229,143,163,230, -143,144,228,186,164,229,176,177,228,184,154,228,191,157,229,129,165,231,168,139, -229,186,166,229,143,130,230,149,176,228,186,139,228,184,154,230,149,180,228,184, -170,229,177,177,228,184,156,230,131,133,230,132,159,231,137,185,230,174,138,229, -136,134,233,161,158,230,144,156,229,176,139,229,177,158,228,186,142,233,151,168, -230,136,183,232,180,162,229,138,161,229,163,176,233,159,179,229,143,138,229,133, -182,232,180,162,231,187,143,229,157,154,230,140,129,229,185,178,233,131,168,230, -136,144,231,171,139,229,136,169,231,155,138,232,128,131,232,153,145,230,136,144, -233,131,189,229,140,133,232,163,133,231,148,168,230,136,182,230,175,148,232,181, -155,230,150,135,230,152,142,230,139,155,229,149,134,229,174,140,230,149,180,231, -156,159,230,152,175,231,156,188,231,157,155,228,188,153,228,188,180,229,168,129, -230,156,155,233,162,134,229,159,159,229,141,171,231,148,159,228,188,152,230,131, -160,232,171,150,229,163,135,229,133,172,229,133,177,232,137,175,229,165,189,229, -133,133,229,136,134,231,172,166,229,144,136,233,153,132,228,187,182,231,137,185, -231,130,185,228,184,141,229,143,175,232,139,177,230,150,135,232,181,132,228,186, -167,230,160,185,230,156,172,230,152,142,230,152,190,229,175,134,231,162,188,229, -133,172,228,188,151,230,176,145,230,151,143,230,155,180,229,138,160,228,186,171, -229,143,151,229,144,140,229,173,166,229,144,175,229,138,168,233,128,130,229,144, -136,229,142,159,230,157,165,233,151,174,231,173,148,230,156,172,230,150,135,231, -190,142,233,163,159,231,187,191,232,137,178,231,168,179,229,174,154,231,187,136, -228,186,142,231,148,159,231,137,169,228,190,155,230,177,130,230,144,156,231,139, -144,229,138,155,233,135,143,228,184,165,233,135,141,230,176,184,232,191,156,229, -134,153,231,156,159,230,156,137,233,153,144,231,171,158,228,186,137,229,175,185, -232,177,161,232,180,185,231,148,168,228,184,141,229,165,189,231,187,157,229,175, -185,229,141,129,229,136,134,228,191,131,232,191,155,231,130,185,232,175,132,229, -189,177,233,159,179,228,188,152,229,138,191,228,184,141,229,176,145,230,172,163, -232,181,143,229,185,182,228,184,148,230,156,137,231,130,185,230,150,185,229,144, -145,229,133,168,230,150,176,228,191,161,231,148,168,232,174,190,230,150,189,229, -189,162,232,177,161,232,181,132,230,160,188,231,170,129,231,160,180,233,154,143, -231,157,128,233,135,141,229,164,167,228,186,142,230,152,175,230,175,149,228,184, -154,230,153,186,232,131,189,229,140,150,229,183,165,229,174,140,231,190,142,229, -149,134,229,159,142,231,187,159,228,184,128,229,135,186,231,137,136,230,137,147, -233,128,160,231,148,162,229,147,129,230,166,130,229,134,181,231,148,168,228,186, -142,228,191,157,231,149,153,229,155,160,231,180,160,228,184,173,229,156,139,229, -173,152,229,130,168,232,180,180,229,155,190,230,156,128,230,132,155,233,149,191, -230,156,159,229,143,163,228,187,183,231,144,134,232,180,162,229,159,186,229,156, -176,229,174,137,230,142,146,230,173,166,230,177,137,233,135,140,233,157,162,229, -136,155,229,187,186,229,164,169,231,169,186,233,166,150,229,133,136,229,174,140, -229,150,132,233,169,177,229,138,168,228,184,139,233,157,162,228,184,141,229,134, -141,232,175,154,228,191,161,230,132,143,228,185,137,233,152,179,229,133,137,232, -139,177,229,155,189,230,188,130,228,186,174,229,134,155,228,186,139,231,142,169, -229,174,182,231,190,164,228,188,151,229,134,156,230,176,145,229,141,179,229,143, -175,229,144,141,231,168,177,229,174,182,229,133,183,229,138,168,231,148,187,230, -131,179,229,136,176,230,179,168,230,152,142,229,176,143,229,173,166,230,128,167, -232,131,189,232,128,131,231,160,148,231,161,172,228,187,182,232,167,130,231,156, -139,230,184,133,230,165,154,230,144,158,231,172,145,233,166,150,233,160,129,233, -187,132,233,135,145,233,128,130,231,148,168,230,177,159,232,139,143,231,156,159, -229,174,158,228,184,187,231,174,161,233,152,182,230,174,181,232,168,187,229,134, -138,231,191,187,232,175,145,230,157,131,229,136,169,229,129,154,229,165,189,228, -188,188,228,185,142,233,128,154,232,174,175,230,150,189,229,183,165,231,139,128, -230,133,139,228,185,159,232,174,184,231,142,175,228,191,157,229,159,185,229,133, -187,230,166,130,229,191,181,229,164,167,229,158,139,230,156,186,231,165,168,231, -144,134,232,167,163,229,140,191,229,144,141,99,117,97,110,100,111,101,110,118, -105,97,114,109,97,100,114,105,100,98,117,115,99,97,114,105,110,105,99,105,111, -116,105,101,109,112,111,112,111,114,113,117,101,99,117,101,110,116,97,101,115, -116,97,100,111,112,117,101,100,101,110,106,117,101,103,111,115,99,111,110,116, -114,97,101,115,116,195,161,110,110,111,109,98,114,101,116,105,101,110,101,110, -112,101,114,102,105,108,109,97,110,101,114,97,97,109,105,103,111,115,99,105,117, -100,97,100,99,101,110,116,114,111,97,117,110,113,117,101,112,117,101,100,101,115 -,100,101,110,116,114,111,112,114,105,109,101,114,112,114,101,99,105,111,115,101, -103,195,186,110,98,117,101,110,111,115,118,111,108,118,101,114,112,117,110,116, -111,115,115,101,109,97,110,97,104,97,98,195,173,97,97,103,111,115,116,111,110, -117,101,118,111,115,117,110,105,100,111,115,99,97,114,108,111,115,101,113,117, -105,112,111,110,105,195,177,111,115,109,117,99,104,111,115,97,108,103,117,110,97 -,99,111,114,114,101,111,105,109,97,103,101,110,112,97,114,116,105,114,97,114,114 -,105,98,97,109,97,114,195,173,97,104,111,109,98,114,101,101,109,112,108,101,111, -118,101,114,100,97,100,99,97,109,98,105,111,109,117,99,104,97,115,102,117,101, -114,111,110,112,97,115,97,100,111,108,195,173,110,101,97,112,97,114,101,99,101, -110,117,101,118,97,115,99,117,114,115,111,115,101,115,116,97,98,97,113,117,105, -101,114,111,108,105,98,114,111,115,99,117,97,110,116,111,97,99,99,101,115,111, -109,105,103,117,101,108,118,97,114,105,111,115,99,117,97,116,114,111,116,105,101 -,110,101,115,103,114,117,112,111,115,115,101,114,195,161,110,101,117,114,111,112 -,97,109,101,100,105,111,115,102,114,101,110,116,101,97,99,101,114,99,97,100,101, -109,195,161,115,111,102,101,114,116,97,99,111,99,104,101,115,109,111,100,101,108 -,111,105,116,97,108,105,97,108,101,116,114,97,115,97,108,103,195,186,110,99,111, -109,112,114,97,99,117,97,108,101,115,101,120,105,115,116,101,99,117,101,114,112, -111,115,105,101,110,100,111,112,114,101,110,115,97,108,108,101,103,97,114,118, -105,97,106,101,115,100,105,110,101,114,111,109,117,114,99,105,97,112,111,100,114 -,195,161,112,117,101,115,116,111,100,105,97,114,105,111,112,117,101,98,108,111, -113,117,105,101,114,101,109,97,110,117,101,108,112,114,111,112,105,111,99,114, -105,115,105,115,99,105,101,114,116,111,115,101,103,117,114,111,109,117,101,114, -116,101,102,117,101,110,116,101,99,101,114,114,97,114,103,114,97,110,100,101,101 -,102,101,99,116,111,112,97,114,116,101,115,109,101,100,105,100,97,112,114,111, -112,105,97,111,102,114,101,99,101,116,105,101,114,114,97,101,45,109,97,105,108, -118,97,114,105,97,115,102,111,114,109,97,115,102,117,116,117,114,111,111,98,106, -101,116,111,115,101,103,117,105,114,114,105,101,115,103,111,110,111,114,109,97, -115,109,105,115,109,111,115,195,186,110,105,99,111,99,97,109,105,110,111,115,105 -,116,105,111,115,114,97,122,195,179,110,100,101,98,105,100,111,112,114,117,101, -98,97,116,111,108,101,100,111,116,101,110,195,173,97,106,101,115,195,186,115,101 -,115,112,101,114,111,99,111,99,105,110,97,111,114,105,103,101,110,116,105,101, -110,100,97,99,105,101,110,116,111,99,195,161,100,105,122,104,97,98,108,97,114, -115,101,114,195,173,97,108,97,116,105,110,97,102,117,101,114,122,97,101,115,116, -105,108,111,103,117,101,114,114,97,101,110,116,114,97,114,195,169,120,105,116, -111,108,195,179,112,101,122,97,103,101,110,100,97,118,195,173,100,101,111,101, -118,105,116,97,114,112,97,103,105,110,97,109,101,116,114,111,115,106,97,118,105, -101,114,112,97,100,114,101,115,102,195,161,99,105,108,99,97,98,101,122,97,195, -161,114,101,97,115,115,97,108,105,100,97,101,110,118,195,173,111,106,97,112,195, -179,110,97,98,117,115,111,115,98,105,101,110,101,115,116,101,120,116,111,115,108 -,108,101,118,97,114,112,117,101,100,97,110,102,117,101,114,116,101,99,111,109, -195,186,110,99,108,97,115,101,115,104,117,109,97,110,111,116,101,110,105,100,111 -,98,105,108,98,97,111,117,110,105,100,97,100,101,115,116,195,161,115,101,100,105 -,116,97,114,99,114,101,97,100,111,208,180,208,187,209,143,209,135,209,130,208, -190,208,186,208,176,208,186,208,184,208,187,208,184,209,141,209,130,208,190,208, -178,209,129,208,181,208,181,208,179,208,190,208,191,209,128,208,184,209,130,208, -176,208,186,208,181,209,137,208,181,209,131,208,182,208,181,208,154,208,176,208, -186,208,177,208,181,208,183,208,177,209,139,208,187,208,190,208,189,208,184,208, -146,209,129,208,181,208,191,208,190,208,180,208,173,209,130,208,190,209,130,208, -190,208,188,209,135,208,181,208,188,208,189,208,181,209,130,208,187,208,181,209, -130,209,128,208,176,208,183,208,190,208,189,208,176,208,179,208,180,208,181,208, -188,208,189,208,181,208,148,208,187,209,143,208,159,209,128,208,184,208,189,208, -176,209,129,208,189,208,184,209,133,209,130,208,181,208,188,208,186,209,130,208, -190,208,179,208,190,208,180,208,178,208,190,209,130,209,130,208,176,208,188,208, -161,208,168,208,144,208,188,208,176,209,143,208,167,209,130,208,190,208,178,208, -176,209,129,208,178,208,176,208,188,208,181,208,188,209,131,208,162,208,176,208, -186,208,180,208,178,208,176,208,189,208,176,208,188,209,141,209,130,208,184,209, -141,209,130,209,131,208,146,208,176,208,188,209,130,208,181,209,133,208,191,209, -128,208,190,209,130,209,131,209,130,208,189,208,176,208,180,208,180,208,189,209, -143,208,146,208,190,209,130,209,130,209,128,208,184,208,189,208,181,208,185,208, -146,208,176,209,129,208,189,208,184,208,188,209,129,208,176,208,188,209,130,208, -190,209,130,209,128,209,131,208,177,208,158,208,189,208,184,208,188,208,184,209, -128,208,189,208,181,208,181,208,158,208,158,208,158,208,187,208,184,209,134,209, -141,209,130,208,176,208,158,208,189,208,176,208,189,208,181,208,188,208,180,208, -190,208,188,208,188,208,190,208,185,208,180,208,178,208,181,208,190,208,189,208, -190,209,129,209,131,208,180,224,164,149,224,165,135,224,164,185,224,165,136,224, -164,149,224,165,128,224,164,184,224,165,135,224,164,149,224,164,190,224,164,149, -224,165,139,224,164,148,224,164,176,224,164,170,224,164,176,224,164,168,224,165, -135,224,164,143,224,164,149,224,164,149,224,164,191,224,164,173,224,165,128,224, -164,135,224,164,184,224,164,149,224,164,176,224,164,164,224,165,139,224,164,185, -224,165,139,224,164,134,224,164,170,224,164,185,224,165,128,224,164,175,224,164, -185,224,164,175,224,164,190,224,164,164,224,164,149,224,164,165,224,164,190,106, -97,103,114,97,110,224,164,134,224,164,156,224,164,156,224,165,139,224,164,133, -224,164,172,224,164,166,224,165,139,224,164,151,224,164,136,224,164,156,224,164, -190,224,164,151,224,164,143,224,164,185,224,164,174,224,164,135,224,164,168,224, -164,181,224,164,185,224,164,175,224,165,135,224,164,165,224,165,135,224,164,165, -224,165,128,224,164,152,224,164,176,224,164,156,224,164,172,224,164,166,224,165, -128,224,164,149,224,164,136,224,164,156,224,165,128,224,164,181,224,165,135,224, -164,168,224,164,136,224,164,168,224,164,143,224,164,185,224,164,176,224,164,137, -224,164,184,224,164,174,224,165,135,224,164,149,224,164,174,224,164,181,224,165, -139,224,164,178,224,165,135,224,164,184,224,164,172,224,164,174,224,164,136,224, -164,166,224,165,135,224,164,147,224,164,176,224,164,134,224,164,174,224,164,172, -224,164,184,224,164,173,224,164,176,224,164,172,224,164,168,224,164,154,224,164, -178,224,164,174,224,164,168,224,164,134,224,164,151,224,164,184,224,165,128,224, -164,178,224,165,128,216,185,217,132,217,137,216,165,217,132,217,137,217,135,216, -176,216,167,216,162,216,174,216,177,216,185,216,175,216,175,216,167,217,132,217, -137,217,135,216,176,217,135,216,181,217,136,216,177,216,186,217,138,216,177,217, -131,216,167,217,134,217,136,217,132,216,167,216,168,217,138,217,134,216,185,216, -177,216,182,216,176,217,132,217,131,217,135,217,134,216,167,217,138,217,136,217, -133,217,130,216,167,217,132,216,185,217,132,217,138,216,167,217,134,216,167,217, -132,217,131,217,134,216,173,216,170,217,137,217,130,216,168,217,132,217,136,216, -173,216,169,216,167,216,174,216,177,217,129,217,130,216,183,216,185,216,168,216, -175,216,177,217,131,217,134,216,165,216,176,216,167,217,131,217,133,216,167,216, -167,216,173,216,175,216,165,217,132,216,167,217,129,217,138,217,135,216,168,216, -185,216,182,217,131,217,138,217,129,216,168,216,173,216,171,217,136,217,133,217, -134,217,136,217,135,217,136,216,163,217,134,216,167,216,172,216,175,216,167,217, -132,217,135,216,167,216,179,217,132,217,133,216,185,217,134,216,175,217,132,217, -138,216,179,216,185,216,168,216,177,216,181,217,132,217,137,217,133,217,134,216, -176,216,168,217,135,216,167,216,163,217,134,217,135,217,133,216,171,217,132,217, -131,217,134,216,170,216,167,217,132,216,167,216,173,217,138,216,171,217,133,216, -181,216,177,216,180,216,177,216,173,216,173,217,136,217,132,217,136,217,129,217, -138,216,167,216,176,216,167,217,132,217,131,217,132,217,133,216,177,216,169,216, -167,217,134,216,170,216,167,217,132,217,129,216,163,216,168,217,136,216,174,216, -167,216,181,216,163,217,134,216,170,216,167,217,134,217,135,216,167,217,132,217, -138,216,185,216,182,217,136,217,136,217,130,216,175,216,167,216,168,217,134,216, -174,217,138,216,177,216,168,217,134,216,170,217,132,217,131,217,133,216,180,216, -167,216,161,217,136,217,135,217,138,216,167,216,168,217,136,217,130,216,181,216, -181,217,136,217,133,216,167,216,177,217,130,217,133,216,163,216,173,216,175,217, -134,216,173,217,134,216,185,216,175,217,133,216,177,216,163,217,138,216,167,216, -173,216,169,217,131,216,170,216,168,216,175,217,136,217,134,217,138,216,172,216, -168,217,133,217,134,217,135,216,170,216,173,216,170,216,172,217,135,216,169,216, -179,217,134,216,169,217,138,216,170,217,133,217,131,216,177,216,169,216,186,216, -178,216,169,217,134,217,129,216,179,216,168,217,138,216,170,217,132,217,132,217, -135,217,132,217,134,216,167,216,170,217,132,217,131,217,130,217,132,216,168,217, -132,217,133,216,167,216,185,217,134,217,135,216,163,217,136,217,132,216,180,217, -138,216,161,217,134,217,136,216,177,216,163,217,133,216,167,217,129,217,138,217, -131,216,168,217,131,217,132,216,176,216,167,216,170,216,177,216,170,216,168,216, -168,216,163,217,134,217,135,217,133,216,179,216,167,217,134,217,131,216,168,217, -138,216,185,217,129,217,130,216,175,216,173,216,179,217,134,217,132,217,135,217, -133,216,180,216,185,216,177,216,163,217,135,217,132,216,180,217,135,216,177,217, -130,216,183,216,177,216,183,217,132,216,168,112,114,111,102,105,108,101,115,101, -114,118,105,99,101,100,101,102,97,117,108,116,104,105,109,115,101,108,102,100, -101,116,97,105,108,115,99,111,110,116,101,110,116,115,117,112,112,111,114,116, -115,116,97,114,116,101,100,109,101,115,115,97,103,101,115,117,99,99,101,115,115, -102,97,115,104,105,111,110,60,116,105,116,108,101,62,99,111,117,110,116,114,121, -97,99,99,111,117,110,116,99,114,101,97,116,101,100,115,116,111,114,105,101,115, -114,101,115,117,108,116,115,114,117,110,110,105,110,103,112,114,111,99,101,115, -115,119,114,105,116,105,110,103,111,98,106,101,99,116,115,118,105,115,105,98,108 -,101,119,101,108,99,111,109,101,97,114,116,105,99,108,101,117,110,107,110,111, -119,110,110,101,116,119,111,114,107,99,111,109,112,97,110,121,100,121,110,97,109 -,105,99,98,114,111,119,115,101,114,112,114,105,118,97,99,121,112,114,111,98,108, -101,109,83,101,114,118,105,99,101,114,101,115,112,101,99,116,100,105,115,112,108 -,97,121,114,101,113,117,101,115,116,114,101,115,101,114,118,101,119,101,98,115, -105,116,101,104,105,115,116,111,114,121,102,114,105,101,110,100,115,111,112,116, -105,111,110,115,119,111,114,107,105,110,103,118,101,114,115,105,111,110,109,105, -108,108,105,111,110,99,104,97,110,110,101,108,119,105,110,100,111,119,46,97,100, -100,114,101,115,115,118,105,115,105,116,101,100,119,101,97,116,104,101,114,99, -111,114,114,101,99,116,112,114,111,100,117,99,116,101,100,105,114,101,99,116,102 -,111,114,119,97,114,100,121,111,117,32,99,97,110,114,101,109,111,118,101,100,115 -,117,98,106,101,99,116,99,111,110,116,114,111,108,97,114,99,104,105,118,101,99, -117,114,114,101,110,116,114,101,97,100,105,110,103,108,105,98,114,97,114,121,108 -,105,109,105,116,101,100,109,97,110,97,103,101,114,102,117,114,116,104,101,114, -115,117,109,109,97,114,121,109,97,99,104,105,110,101,109,105,110,117,116,101,115 -,112,114,105,118,97,116,101,99,111,110,116,101,120,116,112,114,111,103,114,97, -109,115,111,99,105,101,116,121,110,117,109,98,101,114,115,119,114,105,116,116, -101,110,101,110,97,98,108,101,100,116,114,105,103,103,101,114,115,111,117,114,99 -,101,115,108,111,97,100,105,110,103,101,108,101,109,101,110,116,112,97,114,116, -110,101,114,102,105,110,97,108,108,121,112,101,114,102,101,99,116,109,101,97,110 -,105,110,103,115,121,115,116,101,109,115,107,101,101,112,105,110,103,99,117,108, -116,117,114,101,38,113,117,111,116,59,44,106,111,117,114,110,97,108,112,114,111, -106,101,99,116,115,117,114,102,97,99,101,115,38,113,117,111,116,59,101,120,112, -105,114,101,115,114,101,118,105,101,119,115,98,97,108,97,110,99,101,69,110,103, -108,105,115,104,67,111,110,116,101,110,116,116,104,114,111,117,103,104,80,108, -101,97,115,101,32,111,112,105,110,105,111,110,99,111,110,116,97,99,116,97,118, -101,114,97,103,101,112,114,105,109,97,114,121,118,105,108,108,97,103,101,83,112, -97,110,105,115,104,103,97,108,108,101,114,121,100,101,99,108,105,110,101,109,101 -,101,116,105,110,103,109,105,115,115,105,111,110,112,111,112,117,108,97,114,113, -117,97,108,105,116,121,109,101,97,115,117,114,101,103,101,110,101,114,97,108,115 -,112,101,99,105,101,115,115,101,115,115,105,111,110,115,101,99,116,105,111,110, -119,114,105,116,101,114,115,99,111,117,110,116,101,114,105,110,105,116,105,97, -108,114,101,112,111,114,116,115,102,105,103,117,114,101,115,109,101,109,98,101, -114,115,104,111,108,100,105,110,103,100,105,115,112,117,116,101,101,97,114,108, -105,101,114,101,120,112,114,101,115,115,100,105,103,105,116,97,108,112,105,99, -116,117,114,101,65,110,111,116,104,101,114,109,97,114,114,105,101,100,116,114,97 -,102,102,105,99,108,101,97,100,105,110,103,99,104,97,110,103,101,100,99,101,110, -116,114,97,108,118,105,99,116,111,114,121,105,109,97,103,101,115,47,114,101,97, -115,111,110,115,115,116,117,100,105,101,115,102,101,97,116,117,114,101,108,105, -115,116,105,110,103,109,117,115,116,32,98,101,115,99,104,111,111,108,115,86,101, -114,115,105,111,110,117,115,117,97,108,108,121,101,112,105,115,111,100,101,112, -108,97,121,105,110,103,103,114,111,119,105,110,103,111,98,118,105,111,117,115, -111,118,101,114,108,97,121,112,114,101,115,101,110,116,97,99,116,105,111,110,115 -,60,47,117,108,62,13,10,119,114,97,112,112,101,114,97,108,114,101,97,100,121,99, -101,114,116,97,105,110,114,101,97,108,105,116,121,115,116,111,114,97,103,101,97, -110,111,116,104,101,114,100,101,115,107,116,111,112,111,102,102,101,114,101,100, -112,97,116,116,101,114,110,117,110,117,115,117,97,108,68,105,103,105,116,97,108, -99,97,112,105,116,97,108,87,101,98,115,105,116,101,102,97,105,108,117,114,101,99 -,111,110,110,101,99,116,114,101,100,117,99,101,100,65,110,100,114,111,105,100, -100,101,99,97,100,101,115,114,101,103,117,108,97,114,32,38,97,109,112,59,32,97, -110,105,109,97,108,115,114,101,108,101,97,115,101,65,117,116,111,109,97,116,103, -101,116,116,105,110,103,109,101,116,104,111,100,115,110,111,116,104,105,110,103, -80,111,112,117,108,97,114,99,97,112,116,105,111,110,108,101,116,116,101,114,115, -99,97,112,116,117,114,101,115,99,105,101,110,99,101,108,105,99,101,110,115,101, -99,104,97,110,103,101,115,69,110,103,108,97,110,100,61,49,38,97,109,112,59,72, -105,115,116,111,114,121,32,61,32,110,101,119,32,67,101,110,116,114,97,108,117, -112,100,97,116,101,100,83,112,101,99,105,97,108,78,101,116,119,111,114,107,114, -101,113,117,105,114,101,99,111,109,109,101,110,116,119,97,114,110,105,110,103,67 -,111,108,108,101,103,101,116,111,111,108,98,97,114,114,101,109,97,105,110,115,98 -,101,99,97,117,115,101,101,108,101,99,116,101,100,68,101,117,116,115,99,104,102, -105,110,97,110,99,101,119,111,114,107,101,114,115,113,117,105,99,107,108,121,98, -101,116,119,101,101,110,101,120,97,99,116,108,121,115,101,116,116,105,110,103, -100,105,115,101,97,115,101,83,111,99,105,101,116,121,119,101,97,112,111,110,115, -101,120,104,105,98,105,116,38,108,116,59,33,45,45,67,111,110,116,114,111,108,99, -108,97,115,115,101,115,99,111,118,101,114,101,100,111,117,116,108,105,110,101,97 -,116,116,97,99,107,115,100,101,118,105,99,101,115,40,119,105,110,100,111,119,112 -,117,114,112,111,115,101,116,105,116,108,101,61,34,77,111,98,105,108,101,32,107, -105,108,108,105,110,103,115,104,111,119,105,110,103,73,116,97,108,105,97,110,100 -,114,111,112,112,101,100,104,101,97,118,105,108,121,101,102,102,101,99,116,115, -45,49,39,93,41,59,10,99,111,110,102,105,114,109,67,117,114,114,101,110,116,97, -100,118,97,110,99,101,115,104,97,114,105,110,103,111,112,101,110,105,110,103,100 -,114,97,119,105,110,103,98,105,108,108,105,111,110,111,114,100,101,114,101,100, -71,101,114,109,97,110,121,114,101,108,97,116,101,100,60,47,102,111,114,109,62, -105,110,99,108,117,100,101,119,104,101,116,104,101,114,100,101,102,105,110,101, -100,83,99,105,101,110,99,101,99,97,116,97,108,111,103,65,114,116,105,99,108,101, -98,117,116,116,111,110,115,108,97,114,103,101,115,116,117,110,105,102,111,114, -109,106,111,117,114,110,101,121,115,105,100,101,98,97,114,67,104,105,99,97,103, -111,104,111,108,105,100,97,121,71,101,110,101,114,97,108,112,97,115,115,97,103, -101,44,38,113,117,111,116,59,97,110,105,109,97,116,101,102,101,101,108,105,110, -103,97,114,114,105,118,101,100,112,97,115,115,105,110,103,110,97,116,117,114,97, -108,114,111,117,103,104,108,121,46,10,10,84,104,101,32,98,117,116,32,110,111,116 -,100,101,110,115,105,116,121,66,114,105,116,97,105,110,67,104,105,110,101,115, -101,108,97,99,107,32,111,102,116,114,105,98,117,116,101,73,114,101,108,97,110, -100,34,32,100,97,116,97,45,102,97,99,116,111,114,115,114,101,99,101,105,118,101, -116,104,97,116,32,105,115,76,105,98,114,97,114,121,104,117,115,98,97,110,100,105 -,110,32,102,97,99,116,97,102,102,97,105,114,115,67,104,97,114,108,101,115,114,97 -,100,105,99,97,108,98,114,111,117,103,104,116,102,105,110,100,105,110,103,108,97 -,110,100,105,110,103,58,108,97,110,103,61,34,114,101,116,117,114,110,32,108,101, -97,100,101,114,115,112,108,97,110,110,101,100,112,114,101,109,105,117,109,112,97 -,99,107,97,103,101,65,109,101,114,105,99,97,69,100,105,116,105,111,110,93,38,113 -,117,111,116,59,77,101,115,115,97,103,101,110,101,101,100,32,116,111,118,97,108, -117,101,61,34,99,111,109,112,108,101,120,108,111,111,107,105,110,103,115,116,97, -116,105,111,110,98,101,108,105,101,118,101,115,109,97,108,108,101,114,45,109,111 -,98,105,108,101,114,101,99,111,114,100,115,119,97,110,116,32,116,111,107,105,110 -,100,32,111,102,70,105,114,101,102,111,120,121,111,117,32,97,114,101,115,105,109 -,105,108,97,114,115,116,117,100,105,101,100,109,97,120,105,109,117,109,104,101, -97,100,105,110,103,114,97,112,105,100,108,121,99,108,105,109,97,116,101,107,105, -110,103,100,111,109,101,109,101,114,103,101,100,97,109,111,117,110,116,115,102, -111,117,110,100,101,100,112,105,111,110,101,101,114,102,111,114,109,117,108,97, -100,121,110,97,115,116,121,104,111,119,32,116,111,32,83,117,112,112,111,114,116, -114,101,118,101,110,117,101,101,99,111,110,111,109,121,82,101,115,117,108,116, -115,98,114,111,116,104,101,114,115,111,108,100,105,101,114,108,97,114,103,101, -108,121,99,97,108,108,105,110,103,46,38,113,117,111,116,59,65,99,99,111,117,110, -116,69,100,119,97,114,100,32,115,101,103,109,101,110,116,82,111,98,101,114,116, -32,101,102,102,111,114,116,115,80,97,99,105,102,105,99,108,101,97,114,110,101, -100,117,112,32,119,105,116,104,104,101,105,103,104,116,58,119,101,32,104,97,118, -101,65,110,103,101,108,101,115,110,97,116,105,111,110,115,95,115,101,97,114,99, -104,97,112,112,108,105,101,100,97,99,113,117,105,114,101,109,97,115,115,105,118, -101,103,114,97,110,116,101,100,58,32,102,97,108,115,101,116,114,101,97,116,101, -100,98,105,103,103,101,115,116,98,101,110,101,102,105,116,100,114,105,118,105, -110,103,83,116,117,100,105,101,115,109,105,110,105,109,117,109,112,101,114,104, -97,112,115,109,111,114,110,105,110,103,115,101,108,108,105,110,103,105,115,32, -117,115,101,100,114,101,118,101,114,115,101,118,97,114,105,97,110,116,32,114,111 -,108,101,61,34,109,105,115,115,105,110,103,97,99,104,105,101,118,101,112,114,111 -,109,111,116,101,115,116,117,100,101,110,116,115,111,109,101,111,110,101,101,120 -,116,114,101,109,101,114,101,115,116,111,114,101,98,111,116,116,111,109,58,101, -118,111,108,118,101,100,97,108,108,32,116,104,101,115,105,116,101,109,97,112,101 -,110,103,108,105,115,104,119,97,121,32,116,111,32,32,65,117,103,117,115,116,115, -121,109,98,111,108,115,67,111,109,112,97,110,121,109,97,116,116,101,114,115,109, -117,115,105,99,97,108,97,103,97,105,110,115,116,115,101,114,118,105,110,103,125, -41,40,41,59,13,10,112,97,121,109,101,110,116,116,114,111,117,98,108,101,99,111, -110,99,101,112,116,99,111,109,112,97,114,101,112,97,114,101,110,116,115,112,108, -97,121,101,114,115,114,101,103,105,111,110,115,109,111,110,105,116,111,114,32,39 -,39,84,104,101,32,119,105,110,110,105,110,103,101,120,112,108,111,114,101,97,100 -,97,112,116,101,100,71,97,108,108,101,114,121,112,114,111,100,117,99,101,97,98, -105,108,105,116,121,101,110,104,97,110,99,101,99,97,114,101,101,114,115,41,46,32 -,84,104,101,32,99,111,108,108,101,99,116,83,101,97,114,99,104,32,97,110,99,105, -101,110,116,101,120,105,115,116,101,100,102,111,111,116,101,114,32,104,97,110, -100,108,101,114,112,114,105,110,116,101,100,99,111,110,115,111,108,101,69,97,115 -,116,101,114,110,101,120,112,111,114,116,115,119,105,110,100,111,119,115,67,104, -97,110,110,101,108,105,108,108,101,103,97,108,110,101,117,116,114,97,108,115,117 -,103,103,101,115,116,95,104,101,97,100,101,114,115,105,103,110,105,110,103,46, -104,116,109,108,34,62,115,101,116,116,108,101,100,119,101,115,116,101,114,110,99 -,97,117,115,105,110,103,45,119,101,98,107,105,116,99,108,97,105,109,101,100,74, -117,115,116,105,99,101,99,104,97,112,116,101,114,118,105,99,116,105,109,115,84, -104,111,109,97,115,32,109,111,122,105,108,108,97,112,114,111,109,105,115,101,112 -,97,114,116,105,101,115,101,100,105,116,105,111,110,111,117,116,115,105,100,101, -58,102,97,108,115,101,44,104,117,110,100,114,101,100,79,108,121,109,112,105,99, -95,98,117,116,116,111,110,97,117,116,104,111,114,115,114,101,97,99,104,101,100, -99,104,114,111,110,105,99,100,101,109,97,110,100,115,115,101,99,111,110,100,115, -112,114,111,116,101,99,116,97,100,111,112,116,101,100,112,114,101,112,97,114,101 -,110,101,105,116,104,101,114,103,114,101,97,116,108,121,103,114,101,97,116,101, -114,111,118,101,114,97,108,108,105,109,112,114,111,118,101,99,111,109,109,97,110 -,100,115,112,101,99,105,97,108,115,101,97,114,99,104,46,119,111,114,115,104,105, -112,102,117,110,100,105,110,103,116,104,111,117,103,104,116,104,105,103,104,101, -115,116,105,110,115,116,101,97,100,117,116,105,108,105,116,121,113,117,97,114, -116,101,114,67,117,108,116,117,114,101,116,101,115,116,105,110,103,99,108,101,97 -,114,108,121,101,120,112,111,115,101,100,66,114,111,119,115,101,114,108,105,98, -101,114,97,108,125,32,99,97,116,99,104,80,114,111,106,101,99,116,101,120,97,109, -112,108,101,104,105,100,101,40,41,59,70,108,111,114,105,100,97,97,110,115,119, -101,114,115,97,108,108,111,119,101,100,69,109,112,101,114,111,114,100,101,102, -101,110,115,101,115,101,114,105,111,117,115,102,114,101,101,100,111,109,83,101, -118,101,114,97,108,45,98,117,116,116,111,110,70,117,114,116,104,101,114,111,117, -116,32,111,102,32,33,61,32,110,117,108,108,116,114,97,105,110,101,100,68,101,110 -,109,97,114,107,118,111,105,100,40,48,41,47,97,108,108,46,106,115,112,114,101, -118,101,110,116,82,101,113,117,101,115,116,83,116,101,112,104,101,110,10,10,87, -104,101,110,32,111,98,115,101,114,118,101,60,47,104,50,62,13,10,77,111,100,101, -114,110,32,112,114,111,118,105,100,101,34,32,97,108,116,61,34,98,111,114,100,101 -,114,115,46,10,10,70,111,114,32,10,10,77,97,110,121,32,97,114,116,105,115,116, -115,112,111,119,101,114,101,100,112,101,114,102,111,114,109,102,105,99,116,105, -111,110,116,121,112,101,32,111,102,109,101,100,105,99,97,108,116,105,99,107,101, -116,115,111,112,112,111,115,101,100,67,111,117,110,99,105,108,119,105,116,110, -101,115,115,106,117,115,116,105,99,101,71,101,111,114,103,101,32,66,101,108,103, -105,117,109,46,46,46,60,47,97,62,116,119,105,116,116,101,114,110,111,116,97,98, -108,121,119,97,105,116,105,110,103,119,97,114,102,97,114,101,32,79,116,104,101, -114,32,114,97,110,107,105,110,103,112,104,114,97,115,101,115,109,101,110,116,105 -,111,110,115,117,114,118,105,118,101,115,99,104,111,108,97,114,60,47,112,62,13, -10,32,67,111,117,110,116,114,121,105,103,110,111,114,101,100,108,111,115,115,32, -111,102,106,117,115,116,32,97,115,71,101,111,114,103,105,97,115,116,114,97,110, -103,101,60,104,101,97,100,62,60,115,116,111,112,112,101,100,49,39,93,41,59,13,10 -,105,115,108,97,110,100,115,110,111,116,97,98,108,101,98,111,114,100,101,114,58, -108,105,115,116,32,111,102,99,97,114,114,105,101,100,49,48,48,44,48,48,48,60,47, -104,51,62,10,32,115,101,118,101,114,97,108,98,101,99,111,109,101,115,115,101,108 -,101,99,116,32,119,101,100,100,105,110,103,48,48,46,104,116,109,108,109,111,110, -97,114,99,104,111,102,102,32,116,104,101,116,101,97,99,104,101,114,104,105,103, -104,108,121,32,98,105,111,108,111,103,121,108,105,102,101,32,111,102,111,114,32, -101,118,101,110,114,105,115,101,32,111,102,38,114,97,113,117,111,59,112,108,117, -115,111,110,101,104,117,110,116,105,110,103,40,116,104,111,117,103,104,68,111, -117,103,108,97,115,106,111,105,110,105,110,103,99,105,114,99,108,101,115,70,111, -114,32,116,104,101,65,110,99,105,101,110,116,86,105,101,116,110,97,109,118,101, -104,105,99,108,101,115,117,99,104,32,97,115,99,114,121,115,116,97,108,118,97,108 -,117,101,32,61,87,105,110,100,111,119,115,101,110,106,111,121,101,100,97,32,115, -109,97,108,108,97,115,115,117,109,101,100,60,97,32,105,100,61,34,102,111,114,101 -,105,103,110,32,65,108,108,32,114,105,104,111,119,32,116,104,101,68,105,115,112, -108,97,121,114,101,116,105,114,101,100,104,111,119,101,118,101,114,104,105,100, -100,101,110,59,98,97,116,116,108,101,115,115,101,101,107,105,110,103,99,97,98, -105,110,101,116,119,97,115,32,110,111,116,108,111,111,107,32,97,116,99,111,110, -100,117,99,116,103,101,116,32,116,104,101,74,97,110,117,97,114,121,104,97,112, -112,101,110,115,116,117,114,110,105,110,103,97,58,104,111,118,101,114,79,110,108 -,105,110,101,32,70,114,101,110,99,104,32,108,97,99,107,105,110,103,116,121,112, -105,99,97,108,101,120,116,114,97,99,116,101,110,101,109,105,101,115,101,118,101, -110,32,105,102,103,101,110,101,114,97,116,100,101,99,105,100,101,100,97,114,101, -32,110,111,116,47,115,101,97,114,99,104,98,101,108,105,101,102,115,45,105,109,97 -,103,101,58,108,111,99,97,116,101,100,115,116,97,116,105,99,46,108,111,103,105, -110,34,62,99,111,110,118,101,114,116,118,105,111,108,101,110,116,101,110,116,101 -,114,101,100,102,105,114,115,116,34,62,99,105,114,99,117,105,116,70,105,110,108, -97,110,100,99,104,101,109,105,115,116,115,104,101,32,119,97,115,49,48,112,120,59 -,34,62,97,115,32,115,117,99,104,100,105,118,105,100,101,100,60,47,115,112,97,110 -,62,119,105,108,108,32,98,101,108,105,110,101,32,111,102,97,32,103,114,101,97, -116,109,121,115,116,101,114,121,47,105,110,100,101,120,46,102,97,108,108,105,110 -,103,100,117,101,32,116,111,32,114,97,105,108,119,97,121,99,111,108,108,101,103, -101,109,111,110,115,116,101,114,100,101,115,99,101,110,116,105,116,32,119,105, -116,104,110,117,99,108,101,97,114,74,101,119,105,115,104,32,112,114,111,116,101, -115,116,66,114,105,116,105,115,104,102,108,111,119,101,114,115,112,114,101,100, -105,99,116,114,101,102,111,114,109,115,98,117,116,116,111,110,32,119,104,111,32, -119,97,115,108,101,99,116,117,114,101,105,110,115,116,97,110,116,115,117,105,99, -105,100,101,103,101,110,101,114,105,99,112,101,114,105,111,100,115,109,97,114, -107,101,116,115,83,111,99,105,97,108,32,102,105,115,104,105,110,103,99,111,109, -98,105,110,101,103,114,97,112,104,105,99,119,105,110,110,101,114,115,60,98,114, -32,47,62,60,98,121,32,116,104,101,32,78,97,116,117,114,97,108,80,114,105,118,97, -99,121,99,111,111,107,105,101,115,111,117,116,99,111,109,101,114,101,115,111,108 -,118,101,83,119,101,100,105,115,104,98,114,105,101,102,108,121,80,101,114,115, -105,97,110,115,111,32,109,117,99,104,67,101,110,116,117,114,121,100,101,112,105, -99,116,115,99,111,108,117,109,110,115,104,111,117,115,105,110,103,115,99,114,105 -,112,116,115,110,101,120,116,32,116,111,98,101,97,114,105,110,103,109,97,112,112 -,105,110,103,114,101,118,105,115,101,100,106,81,117,101,114,121,40,45,119,105, -100,116,104,58,116,105,116,108,101,34,62,116,111,111,108,116,105,112,83,101,99, -116,105,111,110,100,101,115,105,103,110,115,84,117,114,107,105,115,104,121,111, -117,110,103,101,114,46,109,97,116,99,104,40,125,41,40,41,59,10,10,98,117,114,110 -,105,110,103,111,112,101,114,97,116,101,100,101,103,114,101,101,115,115,111,117, -114,99,101,61,82,105,99,104,97,114,100,99,108,111,115,101,108,121,112,108,97,115 -,116,105,99,101,110,116,114,105,101,115,60,47,116,114,62,13,10,99,111,108,111, -114,58,35,117,108,32,105,100,61,34,112,111,115,115,101,115,115,114,111,108,108, -105,110,103,112,104,121,115,105,99,115,102,97,105,108,105,110,103,101,120,101,99 -,117,116,101,99,111,110,116,101,115,116,108,105,110,107,32,116,111,68,101,102,97 -,117,108,116,60,98,114,32,47,62,10,58,32,116,114,117,101,44,99,104,97,114,116, -101,114,116,111,117,114,105,115,109,99,108,97,115,115,105,99,112,114,111,99,101, -101,100,101,120,112,108,97,105,110,60,47,104,49,62,13,10,111,110,108,105,110,101 -,46,63,120,109,108,32,118,101,104,101,108,112,105,110,103,100,105,97,109,111,110 -,100,117,115,101,32,116,104,101,97,105,114,108,105,110,101,101,110,100,32,45,45, -62,41,46,97,116,116,114,40,114,101,97,100,101,114,115,104,111,115,116,105,110, -103,35,102,102,102,102,102,102,114,101,97,108,105,122,101,86,105,110,99,101,110, -116,115,105,103,110,97,108,115,32,115,114,99,61,34,47,80,114,111,100,117,99,116, -100,101,115,112,105,116,101,100,105,118,101,114,115,101,116,101,108,108,105,110, -103,80,117,98,108,105,99,32,104,101,108,100,32,105,110,74,111,115,101,112,104,32 -,116,104,101,97,116,114,101,97,102,102,101,99,116,115,60,115,116,121,108,101,62, -97,32,108,97,114,103,101,100,111,101,115,110,39,116,108,97,116,101,114,44,32,69, -108,101,109,101,110,116,102,97,118,105,99,111,110,99,114,101,97,116,111,114,72, -117,110,103,97,114,121,65,105,114,112,111,114,116,115,101,101,32,116,104,101,115 -,111,32,116,104,97,116,77,105,99,104,97,101,108,83,121,115,116,101,109,115,80, -114,111,103,114,97,109,115,44,32,97,110,100,32,32,119,105,100,116,104,61,101,38, -113,117,111,116,59,116,114,97,100,105,110,103,108,101,102,116,34,62,10,112,101, -114,115,111,110,115,71,111,108,100,101,110,32,65,102,102,97,105,114,115,103,114, -97,109,109,97,114,102,111,114,109,105,110,103,100,101,115,116,114,111,121,105, -100,101,97,32,111,102,99,97,115,101,32,111,102,111,108,100,101,115,116,32,116, -104,105,115,32,105,115,46,115,114,99,32,61,32,99,97,114,116,111,111,110,114,101, -103,105,115,116,114,67,111,109,109,111,110,115,77,117,115,108,105,109,115,87,104 -,97,116,32,105,115,105,110,32,109,97,110,121,109,97,114,107,105,110,103,114,101, -118,101,97,108,115,73,110,100,101,101,100,44,101,113,117,97,108,108,121,47,115, -104,111,119,95,97,111,117,116,100,111,111,114,101,115,99,97,112,101,40,65,117, -115,116,114,105,97,103,101,110,101,116,105,99,115,121,115,116,101,109,44,73,110, -32,116,104,101,32,115,105,116,116,105,110,103,72,101,32,97,108,115,111,73,115, -108,97,110,100,115,65,99,97,100,101,109,121,10,9,9,60,33,45,45,68,97,110,105,101 -,108,32,98,105,110,100,105,110,103,98,108,111,99,107,34,62,105,109,112,111,115, -101,100,117,116,105,108,105,122,101,65,98,114,97,104,97,109,40,101,120,99,101, -112,116,123,119,105,100,116,104,58,112,117,116,116,105,110,103,41,46,104,116,109 -,108,40,124,124,32,91,93,59,10,68,65,84,65,91,32,42,107,105,116,99,104,101,110, -109,111,117,110,116,101,100,97,99,116,117,97,108,32,100,105,97,108,101,99,116, -109,97,105,110,108,121,32,95,98,108,97,110,107,39,105,110,115,116,97,108,108,101 -,120,112,101,114,116,115,105,102,40,116,121,112,101,73,116,32,97,108,115,111,38, -99,111,112,121,59,32,34,62,84,101,114,109,115,98,111,114,110,32,105,110,79,112, -116,105,111,110,115,101,97,115,116,101,114,110,116,97,108,107,105,110,103,99,111 -,110,99,101,114,110,103,97,105,110,101,100,32,111,110,103,111,105,110,103,106, -117,115,116,105,102,121,99,114,105,116,105,99,115,102,97,99,116,111,114,121,105, -116,115,32,111,119,110,97,115,115,97,117,108,116,105,110,118,105,116,101,100,108 -,97,115,116,105,110,103,104,105,115,32,111,119,110,104,114,101,102,61,34,47,34, -32,114,101,108,61,34,100,101,118,101,108,111,112,99,111,110,99,101,114,116,100, -105,97,103,114,97,109,100,111,108,108,97,114,115,99,108,117,115,116,101,114,112, -104,112,63,105,100,61,97,108,99,111,104,111,108,41,59,125,41,40,41,59,117,115, -105,110,103,32,97,62,60,115,112,97,110,62,118,101,115,115,101,108,115,114,101, -118,105,118,97,108,65,100,100,114,101,115,115,97,109,97,116,101,117,114,97,110, -100,114,111,105,100,97,108,108,101,103,101,100,105,108,108,110,101,115,115,119, -97,108,107,105,110,103,99,101,110,116,101,114,115,113,117,97,108,105,102,121,109 -,97,116,99,104,101,115,117,110,105,102,105,101,100,101,120,116,105,110,99,116,68 -,101,102,101,110,115,101,100,105,101,100,32,105,110,10,9,60,33,45,45,32,99,117, -115,116,111,109,115,108,105,110,107,105,110,103,76,105,116,116,108,101,32,66,111 -,111,107,32,111,102,101,118,101,110,105,110,103,109,105,110,46,106,115,63,97,114 -,101,32,116,104,101,107,111,110,116,97,107,116,116,111,100,97,121,39,115,46,104, -116,109,108,34,32,116,97,114,103,101,116,61,119,101,97,114,105,110,103,65,108, -108,32,82,105,103,59,10,125,41,40,41,59,114,97,105,115,105,110,103,32,65,108,115 -,111,44,32,99,114,117,99,105,97,108,97,98,111,117,116,34,62,100,101,99,108,97, -114,101,45,45,62,10,60,115,99,102,105,114,101,102,111,120,97,115,32,109,117,99, -104,97,112,112,108,105,101,115,105,110,100,101,120,44,32,115,44,32,98,117,116,32 -,116,121,112,101,32,61,32,10,13,10,60,33,45,45,116,111,119,97,114,100,115,82,101 -,99,111,114,100,115,80,114,105,118,97,116,101,70,111,114,101,105,103,110,80,114, -101,109,105,101,114,99,104,111,105,99,101,115,86,105,114,116,117,97,108,114,101, -116,117,114,110,115,67,111,109,109,101,110,116,80,111,119,101,114,101,100,105, -110,108,105,110,101,59,112,111,118,101,114,116,121,99,104,97,109,98,101,114,76, -105,118,105,110,103,32,118,111,108,117,109,101,115,65,110,116,104,111,110,121, -108,111,103,105,110,34,32,82,101,108,97,116,101,100,69,99,111,110,111,109,121, -114,101,97,99,104,101,115,99,117,116,116,105,110,103,103,114,97,118,105,116,121, -108,105,102,101,32,105,110,67,104,97,112,116,101,114,45,115,104,97,100,111,119, -78,111,116,97,98,108,101,60,47,116,100,62,13,10,32,114,101,116,117,114,110,115, -116,97,100,105,117,109,119,105,100,103,101,116,115,118,97,114,121,105,110,103, -116,114,97,118,101,108,115,104,101,108,100,32,98,121,119,104,111,32,97,114,101, -119,111,114,107,32,105,110,102,97,99,117,108,116,121,97,110,103,117,108,97,114, -119,104,111,32,104,97,100,97,105,114,112,111,114,116,116,111,119,110,32,111,102, -10,10,83,111,109,101,32,39,99,108,105,99,107,39,99,104,97,114,103,101,115,107, -101,121,119,111,114,100,105,116,32,119,105,108,108,99,105,116,121,32,111,102,40, -116,104,105,115,41,59,65,110,100,114,101,119,32,117,110,105,113,117,101,32,99, -104,101,99,107,101,100,111,114,32,109,111,114,101,51,48,48,112,120,59,32,114,101 -,116,117,114,110,59,114,115,105,111,110,61,34,112,108,117,103,105,110,115,119, -105,116,104,105,110,32,104,101,114,115,101,108,102,83,116,97,116,105,111,110,70, -101,100,101,114,97,108,118,101,110,116,117,114,101,112,117,98,108,105,115,104, -115,101,110,116,32,116,111,116,101,110,115,105,111,110,97,99,116,114,101,115,115 -,99,111,109,101,32,116,111,102,105,110,103,101,114,115,68,117,107,101,32,111,102 -,112,101,111,112,108,101,44,101,120,112,108,111,105,116,119,104,97,116,32,105, -115,104,97,114,109,111,110,121,97,32,109,97,106,111,114,34,58,34,104,116,116,112 -,105,110,32,104,105,115,32,109,101,110,117,34,62,10,109,111,110,116,104,108,121, -111,102,102,105,99,101,114,99,111,117,110,99,105,108,103,97,105,110,105,110,103, -101,118,101,110,32,105,110,83,117,109,109,97,114,121,100,97,116,101,32,111,102, -108,111,121,97,108,116,121,102,105,116,110,101,115,115,97,110,100,32,119,97,115, -101,109,112,101,114,111,114,115,117,112,114,101,109,101,83,101,99,111,110,100,32 -,104,101,97,114,105,110,103,82,117,115,115,105,97,110,108,111,110,103,101,115, -116,65,108,98,101,114,116,97,108,97,116,101,114,97,108,115,101,116,32,111,102,32 -,115,109,97,108,108,34,62,46,97,112,112,101,110,100,100,111,32,119,105,116,104, -102,101,100,101,114,97,108,98,97,110,107,32,111,102,98,101,110,101,97,116,104,68 -,101,115,112,105,116,101,67,97,112,105,116,97,108,103,114,111,117,110,100,115,41 -,44,32,97,110,100,32,112,101,114,99,101,110,116,105,116,32,102,114,111,109,99, -108,111,115,105,110,103,99,111,110,116,97,105,110,73,110,115,116,101,97,100,102, -105,102,116,101,101,110,97,115,32,119,101,108,108,46,121,97,104,111,111,46,114, -101,115,112,111,110,100,102,105,103,104,116,101,114,111,98,115,99,117,114,101, -114,101,102,108,101,99,116,111,114,103,97,110,105,99,61,32,77,97,116,104,46,101, -100,105,116,105,110,103,111,110,108,105,110,101,32,112,97,100,100,105,110,103,97 -,32,119,104,111,108,101,111,110,101,114,114,111,114,121,101,97,114,32,111,102, -101,110,100,32,111,102,32,98,97,114,114,105,101,114,119,104,101,110,32,105,116, -104,101,97,100,101,114,32,104,111,109,101,32,111,102,114,101,115,117,109,101,100 -,114,101,110,97,109,101,100,115,116,114,111,110,103,62,104,101,97,116,105,110, -103,114,101,116,97,105,110,115,99,108,111,117,100,102,114,119,97,121,32,111,102, -32,77,97,114,99,104,32,49,107,110,111,119,105,110,103,105,110,32,112,97,114,116, -66,101,116,119,101,101,110,108,101,115,115,111,110,115,99,108,111,115,101,115, -116,118,105,114,116,117,97,108,108,105,110,107,115,34,62,99,114,111,115,115,101, -100,69,78,68,32,45,45,62,102,97,109,111,117,115,32,97,119,97,114,100,101,100,76, -105,99,101,110,115,101,72,101,97,108,116,104,32,102,97,105,114,108,121,32,119, -101,97,108,116,104,121,109,105,110,105,109,97,108,65,102,114,105,99,97,110,99, -111,109,112,101,116,101,108,97,98,101,108,34,62,115,105,110,103,105,110,103,102, -97,114,109,101,114,115,66,114,97,115,105,108,41,100,105,115,99,117,115,115,114, -101,112,108,97,99,101,71,114,101,103,111,114,121,102,111,110,116,32,99,111,112, -117,114,115,117,101,100,97,112,112,101,97,114,115,109,97,107,101,32,117,112,114, -111,117,110,100,101,100,98,111,116,104,32,111,102,98,108,111,99,107,101,100,115, -97,119,32,116,104,101,111,102,102,105,99,101,115,99,111,108,111,117,114,115,105, -102,40,100,111,99,117,119,104,101,110,32,104,101,101,110,102,111,114,99,101,112, -117,115,104,40,102,117,65,117,103,117,115,116,32,85,84,70,45,56,34,62,70,97,110, -116,97,115,121,105,110,32,109,111,115,116,105,110,106,117,114,101,100,85,115,117 -,97,108,108,121,102,97,114,109,105,110,103,99,108,111,115,117,114,101,111,98,106 -,101,99,116,32,100,101,102,101,110,99,101,117,115,101,32,111,102,32,77,101,100, -105,99,97,108,60,98,111,100,121,62,10,101,118,105,100,101,110,116,98,101,32,117, -115,101,100,107,101,121,67,111,100,101,115,105,120,116,101,101,110,73,115,108,97 -,109,105,99,35,48,48,48,48,48,48,101,110,116,105,114,101,32,119,105,100,101,108, -121,32,97,99,116,105,118,101,32,40,116,121,112,101,111,102,111,110,101,32,99,97, -110,99,111,108,111,114,32,61,115,112,101,97,107,101,114,101,120,116,101,110,100, -115,80,104,121,115,105,99,115,116,101,114,114,97,105,110,60,116,98,111,100,121, -62,102,117,110,101,114,97,108,118,105,101,119,105,110,103,109,105,100,100,108, -101,32,99,114,105,99,107,101,116,112,114,111,112,104,101,116,115,104,105,102,116 -,101,100,100,111,99,116,111,114,115,82,117,115,115,101,108,108,32,116,97,114,103 -,101,116,99,111,109,112,97,99,116,97,108,103,101,98,114,97,115,111,99,105,97,108 -,45,98,117,108,107,32,111,102,109,97,110,32,97,110,100,60,47,116,100,62,10,32, -104,101,32,108,101,102,116,41,46,118,97,108,40,41,102,97,108,115,101,41,59,108, -111,103,105,99,97,108,98,97,110,107,105,110,103,104,111,109,101,32,116,111,110, -97,109,105,110,103,32,65,114,105,122,111,110,97,99,114,101,100,105,116,115,41,59 -,10,125,41,59,10,102,111,117,110,100,101,114,105,110,32,116,117,114,110,67,111, -108,108,105,110,115,98,101,102,111,114,101,32,66,117,116,32,116,104,101,99,104, -97,114,103,101,100,84,105,116,108,101,34,62,67,97,112,116,97,105,110,115,112,101 -,108,108,101,100,103,111,100,100,101,115,115,84,97,103,32,45,45,62,65,100,100, -105,110,103,58,98,117,116,32,119,97,115,82,101,99,101,110,116,32,112,97,116,105, -101,110,116,98,97,99,107,32,105,110,61,102,97,108,115,101,38,76,105,110,99,111, -108,110,119,101,32,107,110,111,119,67,111,117,110,116,101,114,74,117,100,97,105, -115,109,115,99,114,105,112,116,32,97,108,116,101,114,101,100,39,93,41,59,10,32, -32,104,97,115,32,116,104,101,117,110,99,108,101,97,114,69,118,101,110,116,39,44, -98,111,116,104,32,105,110,110,111,116,32,97,108,108,10,10,60,33,45,45,32,112,108 -,97,99,105,110,103,104,97,114,100,32,116,111,32,99,101,110,116,101,114,115,111, -114,116,32,111,102,99,108,105,101,110,116,115,115,116,114,101,101,116,115,66,101 -,114,110,97,114,100,97,115,115,101,114,116,115,116,101,110,100,32,116,111,102,97 -,110,116,97,115,121,100,111,119,110,32,105,110,104,97,114,98,111,117,114,70,114, -101,101,100,111,109,106,101,119,101,108,114,121,47,97,98,111,117,116,46,46,115, -101,97,114,99,104,108,101,103,101,110,100,115,105,115,32,109,97,100,101,109,111, -100,101,114,110,32,111,110,108,121,32,111,110,111,110,108,121,32,116,111,105,109 -,97,103,101,34,32,108,105,110,101,97,114,32,112,97,105,110,116,101,114,97,110, -100,32,110,111,116,114,97,114,101,108,121,32,97,99,114,111,110,121,109,100,101, -108,105,118,101,114,115,104,111,114,116,101,114,48,48,38,97,109,112,59,97,115,32 -,109,97,110,121,119,105,100,116,104,61,34,47,42,32,60,33,91,67,116,105,116,108, -101,32,61,111,102,32,116,104,101,32,108,111,119,101,115,116,32,112,105,99,107, -101,100,32,101,115,99,97,112,101,100,117,115,101,115,32,111,102,112,101,111,112, -108,101,115,32,80,117,98,108,105,99,77,97,116,116,104,101,119,116,97,99,116,105, -99,115,100,97,109,97,103,101,100,119,97,121,32,102,111,114,108,97,119,115,32,111 -,102,101,97,115,121,32,116,111,32,119,105,110,100,111,119,115,116,114,111,110, -103,32,32,115,105,109,112,108,101,125,99,97,116,99,104,40,115,101,118,101,110, -116,104,105,110,102,111,98,111,120,119,101,110,116,32,116,111,112,97,105,110,116 -,101,100,99,105,116,105,122,101,110,73,32,100,111,110,39,116,114,101,116,114,101 -,97,116,46,32,83,111,109,101,32,119,119,46,34,41,59,10,98,111,109,98,105,110,103 -,109,97,105,108,116,111,58,109,97,100,101,32,105,110,46,32,77,97,110,121,32,99, -97,114,114,105,101,115,124,124,123,125,59,119,105,119,111,114,107,32,111,102,115 -,121,110,111,110,121,109,100,101,102,101,97,116,115,102,97,118,111,114,101,100, -111,112,116,105,99,97,108,112,97,103,101,84,114,97,117,110,108,101,115,115,32, -115,101,110,100,105,110,103,108,101,102,116,34,62,60,99,111,109,83,99,111,114,65 -,108,108,32,116,104,101,106,81,117,101,114,121,46,116,111,117,114,105,115,116,67 -,108,97,115,115,105,99,102,97,108,115,101,34,32,87,105,108,104,101,108,109,115, -117,98,117,114,98,115,103,101,110,117,105,110,101,98,105,115,104,111,112,115,46, -115,112,108,105,116,40,103,108,111,98,97,108,32,102,111,108,108,111,119,115,98, -111,100,121,32,111,102,110,111,109,105,110,97,108,67,111,110,116,97,99,116,115, -101,99,117,108,97,114,108,101,102,116,32,116,111,99,104,105,101,102,108,121,45, -104,105,100,100,101,110,45,98,97,110,110,101,114,60,47,108,105,62,10,10,46,32,87 -,104,101,110,32,105,110,32,98,111,116,104,100,105,115,109,105,115,115,69,120,112 -,108,111,114,101,97,108,119,97,121,115,32,118,105,97,32,116,104,101,115,112,97, -195,177,111,108,119,101,108,102,97,114,101,114,117,108,105,110,103,32,97,114,114 -,97,110,103,101,99,97,112,116,97,105,110,104,105,115,32,115,111,110,114,117,108, -101,32,111,102,104,101,32,116,111,111,107,105,116,115,101,108,102,44,61,48,38,97 -,109,112,59,40,99,97,108,108,101,100,115,97,109,112,108,101,115,116,111,32,109, -97,107,101,99,111,109,47,112,97,103,77,97,114,116,105,110,32,75,101,110,110,101, -100,121,97,99,99,101,112,116,115,102,117,108,108,32,111,102,104,97,110,100,108, -101,100,66,101,115,105,100,101,115,47,47,45,45,62,60,47,97,98,108,101,32,116,111 -,116,97,114,103,101,116,115,101,115,115,101,110,99,101,104,105,109,32,116,111,32 -,105,116,115,32,98,121,32,99,111,109,109,111,110,46,109,105,110,101,114,97,108, -116,111,32,116,97,107,101,119,97,121,115,32,116,111,115,46,111,114,103,47,108,97 -,100,118,105,115,101,100,112,101,110,97,108,116,121,115,105,109,112,108,101,58, -105,102,32,116,104,101,121,76,101,116,116,101,114,115,97,32,115,104,111,114,116, -72,101,114,98,101,114,116,115,116,114,105,107,101,115,32,103,114,111,117,112,115 -,46,108,101,110,103,116,104,102,108,105,103,104,116,115,111,118,101,114,108,97, -112,115,108,111,119,108,121,32,108,101,115,115,101,114,32,115,111,99,105,97,108, -32,60,47,112,62,10,9,9,105,116,32,105,110,116,111,114,97,110,107,101,100,32,114, -97,116,101,32,111,102,117,108,62,13,10,32,32,97,116,116,101,109,112,116,112,97, -105,114,32,111,102,109,97,107,101,32,105,116,75,111,110,116,97,107,116,65,110, -116,111,110,105,111,104,97,118,105,110,103,32,114,97,116,105,110,103,115,32,97, -99,116,105,118,101,115,116,114,101,97,109,115,116,114,97,112,112,101,100,34,41, -46,99,115,115,40,104,111,115,116,105,108,101,108,101,97,100,32,116,111,108,105, -116,116,108,101,32,103,114,111,117,112,115,44,80,105,99,116,117,114,101,45,45,62 -,13,10,13,10,32,114,111,119,115,61,34,32,111,98,106,101,99,116,105,110,118,101, -114,115,101,60,102,111,111,116,101,114,67,117,115,116,111,109,86,62,60,92,47,115 -,99,114,115,111,108,118,105,110,103,67,104,97,109,98,101,114,115,108,97,118,101, -114,121,119,111,117,110,100,101,100,119,104,101,114,101,97,115,33,61,32,39,117, -110,100,102,111,114,32,97,108,108,112,97,114,116,108,121,32,45,114,105,103,104, -116,58,65,114,97,98,105,97,110,98,97,99,107,101,100,32,99,101,110,116,117,114, -121,117,110,105,116,32,111,102,109,111,98,105,108,101,45,69,117,114,111,112,101, -44,105,115,32,104,111,109,101,114,105,115,107,32,111,102,100,101,115,105,114,101 -,100,67,108,105,110,116,111,110,99,111,115,116,32,111,102,97,103,101,32,111,102, -32,98,101,99,111,109,101,32,110,111,110,101,32,111,102,112,38,113,117,111,116,59 -,77,105,100,100,108,101,32,101,97,100,39,41,91,48,67,114,105,116,105,99,115,115, -116,117,100,105,111,115,62,38,99,111,112,121,59,103,114,111,117,112,34,62,97,115 -,115,101,109,98,108,109,97,107,105,110,103,32,112,114,101,115,115,101,100,119, -105,100,103,101,116,46,112,115,58,34,32,63,32,114,101,98,117,105,108,116,98,121, -32,115,111,109,101,70,111,114,109,101,114,32,101,100,105,116,111,114,115,100,101 -,108,97,121,101,100,67,97,110,111,110,105,99,104,97,100,32,116,104,101,112,117, -115,104,105,110,103,99,108,97,115,115,61,34,98,117,116,32,97,114,101,112,97,114, -116,105,97,108,66,97,98,121,108,111,110,98,111,116,116,111,109,32,99,97,114,114, -105,101,114,67,111,109,109,97,110,100,105,116,115,32,117,115,101,65,115,32,119, -105,116,104,99,111,117,114,115,101,115,97,32,116,104,105,114,100,100,101,110,111 -,116,101,115,97,108,115,111,32,105,110,72,111,117,115,116,111,110,50,48,112,120, -59,34,62,97,99,99,117,115,101,100,100,111,117,98,108,101,32,103,111,97,108,32, -111,102,70,97,109,111,117,115,32,41,46,98,105,110,100,40,112,114,105,101,115,116 -,115,32,79,110,108,105,110,101,105,110,32,74,117,108,121,115,116,32,43,32,34,103 -,99,111,110,115,117,108,116,100,101,99,105,109,97,108,104,101,108,112,102,117, -108,114,101,118,105,118,101,100,105,115,32,118,101,114,121,114,39,43,39,105,112, -116,108,111,115,105,110,103,32,102,101,109,97,108,101,115,105,115,32,97,108,115, -111,115,116,114,105,110,103,115,100,97,121,115,32,111,102,97,114,114,105,118,97, -108,102,117,116,117,114,101,32,60,111,98,106,101,99,116,102,111,114,99,105,110, -103,83,116,114,105,110,103,40,34,32,47,62,10,9,9,104,101,114,101,32,105,115,101, -110,99,111,100,101,100,46,32,32,84,104,101,32,98,97,108,108,111,111,110,100,111, -110,101,32,98,121,47,99,111,109,109,111,110,98,103,99,111,108,111,114,108,97,119 -,32,111,102,32,73,110,100,105,97,110,97,97,118,111,105,100,101,100,98,117,116,32 -,116,104,101,50,112,120,32,51,112,120,106,113,117,101,114,121,46,97,102,116,101, -114,32,97,112,111,108,105,99,121,46,109,101,110,32,97,110,100,102,111,111,116, -101,114,45,61,32,116,114,117,101,59,102,111,114,32,117,115,101,115,99,114,101, -101,110,46,73,110,100,105,97,110,32,105,109,97,103,101,32,61,102,97,109,105,108, -121,44,104,116,116,112,58,47,47,32,38,110,98,115,112,59,100,114,105,118,101,114, -115,101,116,101,114,110,97,108,115,97,109,101,32,97,115,110,111,116,105,99,101, -100,118,105,101,119,101,114,115,125,41,40,41,59,10,32,105,115,32,109,111,114,101 -,115,101,97,115,111,110,115,102,111,114,109,101,114,32,116,104,101,32,110,101, -119,105,115,32,106,117,115,116,99,111,110,115,101,110,116,32,83,101,97,114,99, -104,119,97,115,32,116,104,101,119,104,121,32,116,104,101,115,104,105,112,112,101 -,100,98,114,62,60,98,114,62,119,105,100,116,104,58,32,104,101,105,103,104,116,61 -,109,97,100,101,32,111,102,99,117,105,115,105,110,101,105,115,32,116,104,97,116, -97,32,118,101,114,121,32,65,100,109,105,114,97,108,32,102,105,120,101,100,59,110 -,111,114,109,97,108,32,77,105,115,115,105,111,110,80,114,101,115,115,44,32,111, -110,116,97,114,105,111,99,104,97,114,115,101,116,116,114,121,32,116,111,32,105, -110,118,97,100,101,100,61,34,116,114,117,101,34,115,112,97,99,105,110,103,105, -115,32,109,111,115,116,97,32,109,111,114,101,32,116,111,116,97,108,108,121,102, -97,108,108,32,111,102,125,41,59,13,10,32,32,105,109,109,101,110,115,101,116,105, -109,101,32,105,110,115,101,116,32,111,117,116,115,97,116,105,115,102,121,116,111 -,32,102,105,110,100,100,111,119,110,32,116,111,108,111,116,32,111,102,32,80,108, -97,121,101,114,115,105,110,32,74,117,110,101,113,117,97,110,116,117,109,110,111, -116,32,116,104,101,116,105,109,101,32,116,111,100,105,115,116,97,110,116,70,105, -110,110,105,115,104,115,114,99,32,61,32,40,115,105,110,103,108,101,32,104,101, -108,112,32,111,102,71,101,114,109,97,110,32,108,97,119,32,97,110,100,108,97,98, -101,108,101,100,102,111,114,101,115,116,115,99,111,111,107,105,110,103,115,112, -97,99,101,34,62,104,101,97,100,101,114,45,119,101,108,108,32,97,115,83,116,97, -110,108,101,121,98,114,105,100,103,101,115,47,103,108,111,98,97,108,67,114,111, -97,116,105,97,32,65,98,111,117,116,32,91,48,93,59,10,32,32,105,116,44,32,97,110, -100,103,114,111,117,112,101,100,98,101,105,110,103,32,97,41,123,116,104,114,111, -119,104,101,32,109,97,100,101,108,105,103,104,116,101,114,101,116,104,105,99,97, -108,70,70,70,70,70,70,34,98,111,116,116,111,109,34,108,105,107,101,32,97,32,101, -109,112,108,111,121,115,108,105,118,101,32,105,110,97,115,32,115,101,101,110,112 -,114,105,110,116,101,114,109,111,115,116,32,111,102,117,98,45,108,105,110,107, -114,101,106,101,99,116,115,97,110,100,32,117,115,101,105,109,97,103,101,34,62, -115,117,99,99,101,101,100,102,101,101,100,105,110,103,78,117,99,108,101,97,114, -105,110,102,111,114,109,97,116,111,32,104,101,108,112,87,111,109,101,110,39,115, -78,101,105,116,104,101,114,77,101,120,105,99,97,110,112,114,111,116,101,105,110, -60,116,97,98,108,101,32,98,121,32,109,97,110,121,104,101,97,108,116,104,121,108, -97,119,115,117,105,116,100,101,118,105,115,101,100,46,112,117,115,104,40,123,115 -,101,108,108,101,114,115,115,105,109,112,108,121,32,84,104,114,111,117,103,104, -46,99,111,111,107,105,101,32,73,109,97,103,101,40,111,108,100,101,114,34,62,117, -115,46,106,115,34,62,32,83,105,110,99,101,32,117,110,105,118,101,114,115,108,97, -114,103,101,114,32,111,112,101,110,32,116,111,33,45,45,32,101,110,100,108,105, -101,115,32,105,110,39,93,41,59,13,10,32,32,109,97,114,107,101,116,119,104,111,32 -,105,115,32,40,34,68,79,77,67,111,109,97,110,97,103,101,100,111,110,101,32,102, -111,114,116,121,112,101,111,102,32,75,105,110,103,100,111,109,112,114,111,102, -105,116,115,112,114,111,112,111,115,101,116,111,32,115,104,111,119,99,101,110, -116,101,114,59,109,97,100,101,32,105,116,100,114,101,115,115,101,100,119,101,114 -,101,32,105,110,109,105,120,116,117,114,101,112,114,101,99,105,115,101,97,114, -105,115,105,110,103,115,114,99,32,61,32,39,109,97,107,101,32,97,32,115,101,99, -117,114,101,100,66,97,112,116,105,115,116,118,111,116,105,110,103,32,10,9,9,118, -97,114,32,77,97,114,99,104,32,50,103,114,101,119,32,117,112,67,108,105,109,97, -116,101,46,114,101,109,111,118,101,115,107,105,108,108,101,100,119,97,121,32,116 -,104,101,60,47,104,101,97,100,62,102,97,99,101,32,111,102,97,99,116,105,110,103, -32,114,105,103,104,116,34,62,116,111,32,119,111,114,107,114,101,100,117,99,101, -115,104,97,115,32,104,97,100,101,114,101,99,116,101,100,115,104,111,119,40,41,59 -,97,99,116,105,111,110,61,98,111,111,107,32,111,102,97,110,32,97,114,101,97,61, -61,32,34,104,116,116,60,104,101,97,100,101,114,10,60,104,116,109,108,62,99,111, -110,102,111,114,109,102,97,99,105,110,103,32,99,111,111,107,105,101,46,114,101, -108,121,32,111,110,104,111,115,116,101,100,32,46,99,117,115,116,111,109,104,101, -32,119,101,110,116,98,117,116,32,102,111,114,115,112,114,101,97,100,32,70,97,109 -,105,108,121,32,97,32,109,101,97,110,115,111,117,116,32,116,104,101,102,111,114, -117,109,115,46,102,111,111,116,97,103,101,34,62,77,111,98,105,108,67,108,101,109 -,101,110,116,115,34,32,105,100,61,34,97,115,32,104,105,103,104,105,110,116,101, -110,115,101,45,45,62,60,33,45,45,102,101,109,97,108,101,32,105,115,32,115,101, -101,110,105,109,112,108,105,101,100,115,101,116,32,116,104,101,97,32,115,116,97, -116,101,97,110,100,32,104,105,115,102,97,115,116,101,115,116,98,101,115,105,100, -101,115,98,117,116,116,111,110,95,98,111,117,110,100,101,100,34,62,60,105,109, -103,32,73,110,102,111,98,111,120,101,118,101,110,116,115,44,97,32,121,111,117, -110,103,97,110,100,32,97,114,101,78,97,116,105,118,101,32,99,104,101,97,112,101, -114,84,105,109,101,111,117,116,97,110,100,32,104,97,115,101,110,103,105,110,101, -115,119,111,110,32,116,104,101,40,109,111,115,116,108,121,114,105,103,104,116,58 -,32,102,105,110,100,32,97,32,45,98,111,116,116,111,109,80,114,105,110,99,101,32, -97,114,101,97,32,111,102,109,111,114,101,32,111,102,115,101,97,114,99,104,95,110 -,97,116,117,114,101,44,108,101,103,97,108,108,121,112,101,114,105,111,100,44,108 -,97,110,100,32,111,102,111,114,32,119,105,116,104,105,110,100,117,99,101,100,112 -,114,111,118,105,110,103,109,105,115,115,105,108,101,108,111,99,97,108,108,121, -65,103,97,105,110,115,116,116,104,101,32,119,97,121,107,38,113,117,111,116,59, -112,120,59,34,62,13,10,112,117,115,104,101,100,32,97,98,97,110,100,111,110,110, -117,109,101,114,97,108,67,101,114,116,97,105,110,73,110,32,116,104,105,115,109, -111,114,101,32,105,110,111,114,32,115,111,109,101,110,97,109,101,32,105,115,97, -110,100,44,32,105,110,99,114,111,119,110,101,100,73,83,66,78,32,48,45,99,114,101 -,97,116,101,115,79,99,116,111,98,101,114,109,97,121,32,110,111,116,99,101,110, -116,101,114,32,108,97,116,101,32,105,110,68,101,102,101,110,99,101,101,110,97,99 -,116,101,100,119,105,115,104,32,116,111,98,114,111,97,100,108,121,99,111,111,108 -,105,110,103,111,110,108,111,97,100,61,105,116,46,32,84,104,101,114,101,99,111, -118,101,114,77,101,109,98,101,114,115,104,101,105,103,104,116,32,97,115,115,117, -109,101,115,60,104,116,109,108,62,10,112,101,111,112,108,101,46,105,110,32,111, -110,101,32,61,119,105,110,100,111,119,102,111,111,116,101,114,95,97,32,103,111, -111,100,32,114,101,107,108,97,109,97,111,116,104,101,114,115,44,116,111,32,116, -104,105,115,95,99,111,111,107,105,101,112,97,110,101,108,34,62,76,111,110,100, -111,110,44,100,101,102,105,110,101,115,99,114,117,115,104,101,100,98,97,112,116, -105,115,109,99,111,97,115,116,97,108,115,116,97,116,117,115,32,116,105,116,108, -101,34,32,109,111,118,101,32,116,111,108,111,115,116,32,105,110,98,101,116,116, -101,114,32,105,109,112,108,105,101,115,114,105,118,97,108,114,121,115,101,114, -118,101,114,115,32,83,121,115,116,101,109,80,101,114,104,97,112,115,101,115,32, -97,110,100,32,99,111,110,116,101,110,100,102,108,111,119,105,110,103,108,97,115, -116,101,100,32,114,105,115,101,32,105,110,71,101,110,101,115,105,115,118,105,101 -,119,32,111,102,114,105,115,105,110,103,32,115,101,101,109,32,116,111,98,117,116 -,32,105,110,32,98,97,99,107,105,110,103,104,101,32,119,105,108,108,103,105,118, -101,110,32,97,103,105,118,105,110,103,32,99,105,116,105,101,115,46,102,108,111, -119,32,111,102,32,76,97,116,101,114,32,97,108,108,32,98,117,116,72,105,103,104, -119,97,121,111,110,108,121,32,98,121,115,105,103,110,32,111,102,104,101,32,100, -111,101,115,100,105,102,102,101,114,115,98,97,116,116,101,114,121,38,97,109,112, -59,108,97,115,105,110,103,108,101,115,116,104,114,101,97,116,115,105,110,116,101 -,103,101,114,116,97,107,101,32,111,110,114,101,102,117,115,101,100,99,97,108,108 -,101,100,32,61,85,83,38,97,109,112,83,101,101,32,116,104,101,110,97,116,105,118, -101,115,98,121,32,116,104,105,115,115,121,115,116,101,109,46,104,101,97,100,32, -111,102,58,104,111,118,101,114,44,108,101,115,98,105,97,110,115,117,114,110,97, -109,101,97,110,100,32,97,108,108,99,111,109,109,111,110,47,104,101,97,100,101, -114,95,95,112,97,114,97,109,115,72,97,114,118,97,114,100,47,112,105,120,101,108, -46,114,101,109,111,118,97,108,115,111,32,108,111,110,103,114,111,108,101,32,111, -102,106,111,105,110,116,108,121,115,107,121,115,99,114,97,85,110,105,99,111,100, -101,98,114,32,47,62,13,10,65,116,108,97,110,116,97,110,117,99,108,101,117,115,67 -,111,117,110,116,121,44,112,117,114,101,108,121,32,99,111,117,110,116,34,62,101, -97,115,105,108,121,32,98,117,105,108,100,32,97,111,110,99,108,105,99,107,97,32, -103,105,118,101,110,112,111,105,110,116,101,114,104,38,113,117,111,116,59,101, -118,101,110,116,115,32,101,108,115,101,32,123,10,100,105,116,105,111,110,115,110 -,111,119,32,116,104,101,44,32,119,105,116,104,32,109,97,110,32,119,104,111,111, -114,103,47,87,101,98,111,110,101,32,97,110,100,99,97,118,97,108,114,121,72,101, -32,100,105,101,100,115,101,97,116,116,108,101,48,48,44,48,48,48,32,123,119,105, -110,100,111,119,104,97,118,101,32,116,111,105,102,40,119,105,110,100,97,110,100, -32,105,116,115,115,111,108,101,108,121,32,109,38,113,117,111,116,59,114,101,110, -101,119,101,100,68,101,116,114,111,105,116,97,109,111,110,103,115,116,101,105, -116,104,101,114,32,116,104,101,109,32,105,110,83,101,110,97,116,111,114,85,115, -60,47,97,62,60,75,105,110,103,32,111,102,70,114,97,110,99,105,115,45,112,114,111 -,100,117,99,104,101,32,117,115,101,100,97,114,116,32,97,110,100,104,105,109,32, -97,110,100,117,115,101,100,32,98,121,115,99,111,114,105,110,103,97,116,32,104, -111,109,101,116,111,32,104,97,118,101,114,101,108,97,116,101,115,105,98,105,108, -105,116,121,102,97,99,116,105,111,110,66,117,102,102,97,108,111,108,105,110,107, -34,62,60,119,104,97,116,32,104,101,102,114,101,101,32,116,111,67,105,116,121,32, -111,102,99,111,109,101,32,105,110,115,101,99,116,111,114,115,99,111,117,110,116, -101,100,111,110,101,32,100,97,121,110,101,114,118,111,117,115,115,113,117,97,114 -,101,32,125,59,105,102,40,103,111,105,110,32,119,104,97,116,105,109,103,34,32,97 -,108,105,115,32,111,110,108,121,115,101,97,114,99,104,47,116,117,101,115,100,97, -121,108,111,111,115,101,108,121,83,111,108,111,109,111,110,115,101,120,117,97, -108,32,45,32,60,97,32,104,114,109,101,100,105,117,109,34,68,79,32,78,79,84,32,70 -,114,97,110,99,101,44,119,105,116,104,32,97,32,119,97,114,32,97,110,100,115,101, -99,111,110,100,32,116,97,107,101,32,97,32,62,13,10,13,10,13,10,109,97,114,107, -101,116,46,104,105,103,104,119,97,121,100,111,110,101,32,105,110,99,116,105,118, -105,116,121,34,108,97,115,116,34,62,111,98,108,105,103,101,100,114,105,115,101, -32,116,111,34,117,110,100,101,102,105,109,97,100,101,32,116,111,32,69,97,114,108 -,121,32,112,114,97,105,115,101,100,105,110,32,105,116,115,32,102,111,114,32,104, -105,115,97,116,104,108,101,116,101,74,117,112,105,116,101,114,89,97,104,111,111, -33,32,116,101,114,109,101,100,32,115,111,32,109,97,110,121,114,101,97,108,108, -121,32,115,46,32,84,104,101,32,97,32,119,111,109,97,110,63,118,97,108,117,101,61 -,100,105,114,101,99,116,32,114,105,103,104,116,34,32,98,105,99,121,99,108,101,97 -,99,105,110,103,61,34,100,97,121,32,97,110,100,115,116,97,116,105,110,103,82,97, -116,104,101,114,44,104,105,103,104,101,114,32,79,102,102,105,99,101,32,97,114, -101,32,110,111,119,116,105,109,101,115,44,32,119,104,101,110,32,97,32,112,97,121 -,32,102,111,114,111,110,32,116,104,105,115,45,108,105,110,107,34,62,59,98,111, -114,100,101,114,97,114,111,117,110,100,32,97,110,110,117,97,108,32,116,104,101, -32,78,101,119,112,117,116,32,116,104,101,46,99,111,109,34,32,116,97,107,105,110, -32,116,111,97,32,98,114,105,101,102,40,105,110,32,116,104,101,103,114,111,117, -112,115,46,59,32,119,105,100,116,104,101,110,122,121,109,101,115,115,105,109,112 -,108,101,32,105,110,32,108,97,116,101,123,114,101,116,117,114,110,116,104,101, -114,97,112,121,97,32,112,111,105,110,116,98,97,110,110,105,110,103,105,110,107, -115,34,62,10,40,41,59,34,32,114,101,97,32,112,108,97,99,101,92,117,48,48,51,67, -97,97,98,111,117,116,32,97,116,114,62,13,10,9,9,99,99,111,117,110,116,32,103,105 -,118,101,115,32,97,60,83,67,82,73,80,84,82,97,105,108,119,97,121,116,104,101,109 -,101,115,47,116,111,111,108,98,111,120,66,121,73,100,40,34,120,104,117,109,97, -110,115,44,119,97,116,99,104,101,115,105,110,32,115,111,109,101,32,105,102,32,40 -,119,105,99,111,109,105,110,103,32,102,111,114,109,97,116,115,32,85,110,100,101, -114,32,98,117,116,32,104,97,115,104,97,110,100,101,100,32,109,97,100,101,32,98, -121,116,104,97,110,32,105,110,102,101,97,114,32,111,102,100,101,110,111,116,101, -100,47,105,102,114,97,109,101,108,101,102,116,32,105,110,118,111,108,116,97,103, -101,105,110,32,101,97,99,104,97,38,113,117,111,116,59,98,97,115,101,32,111,102, -73,110,32,109,97,110,121,117,110,100,101,114,103,111,114,101,103,105,109,101,115 -,97,99,116,105,111,110,32,60,47,112,62,13,10,60,117,115,116,111,109,86,97,59,38, -103,116,59,60,47,105,109,112,111,114,116,115,111,114,32,116,104,97,116,109,111, -115,116,108,121,32,38,97,109,112,59,114,101,32,115,105,122,101,61,34,60,47,97,62 -,60,47,104,97,32,99,108,97,115,115,112,97,115,115,105,118,101,72,111,115,116,32, -61,32,87,104,101,116,104,101,114,102,101,114,116,105,108,101,86,97,114,105,111, -117,115,61,91,93,59,40,102,117,99,97,109,101,114,97,115,47,62,60,47,116,100,62, -97,99,116,115,32,97,115,73,110,32,115,111,109,101,62,13,10,13,10,60,33,111,114, -103,97,110,105,115,32,60,98,114,32,47,62,66,101,105,106,105,110,103,99,97,116,97 -,108,195,160,100,101,117,116,115,99,104,101,117,114,111,112,101,117,101,117,115, -107,97,114,97,103,97,101,105,108,103,101,115,118,101,110,115,107,97,101,115,112, -97,195,177,97,109,101,110,115,97,106,101,117,115,117,97,114,105,111,116,114,97, -98,97,106,111,109,195,169,120,105,99,111,112,195,161,103,105,110,97,115,105,101, -109,112,114,101,115,105,115,116,101,109,97,111,99,116,117,98,114,101,100,117,114 -,97,110,116,101,97,195,177,97,100,105,114,101,109,112,114,101,115,97,109,111,109 -,101,110,116,111,110,117,101,115,116,114,111,112,114,105,109,101,114,97,116,114, -97,118,195,169,115,103,114,97,99,105,97,115,110,117,101,115,116,114,97,112,114, -111,99,101,115,111,101,115,116,97,100,111,115,99,97,108,105,100,97,100,112,101, -114,115,111,110,97,110,195,186,109,101,114,111,97,99,117,101,114,100,111,109,195 -,186,115,105,99,97,109,105,101,109,98,114,111,111,102,101,114,116,97,115,97,108, -103,117,110,111,115,112,97,195,173,115,101,115,101,106,101,109,112,108,111,100, -101,114,101,99,104,111,97,100,101,109,195,161,115,112,114,105,118,97,100,111,97, -103,114,101,103,97,114,101,110,108,97,99,101,115,112,111,115,105,98,108,101,104, -111,116,101,108,101,115,115,101,118,105,108,108,97,112,114,105,109,101,114,111, -195,186,108,116,105,109,111,101,118,101,110,116,111,115,97,114,99,104,105,118, -111,99,117,108,116,117,114,97,109,117,106,101,114,101,115,101,110,116,114,97,100 -,97,97,110,117,110,99,105,111,101,109,98,97,114,103,111,109,101,114,99,97,100, -111,103,114,97,110,100,101,115,101,115,116,117,100,105,111,109,101,106,111,114, -101,115,102,101,98,114,101,114,111,100,105,115,101,195,177,111,116,117,114,105, -115,109,111,99,195,179,100,105,103,111,112,111,114,116,97,100,97,101,115,112,97, -99,105,111,102,97,109,105,108,105,97,97,110,116,111,110,105,111,112,101,114,109, -105,116,101,103,117,97,114,100,97,114,97,108,103,117,110,97,115,112,114,101,99, -105,111,115,97,108,103,117,105,101,110,115,101,110,116,105,100,111,118,105,115, -105,116,97,115,116,195,173,116,117,108,111,99,111,110,111,99,101,114,115,101,103 -,117,110,100,111,99,111,110,115,101,106,111,102,114,97,110,99,105,97,109,105,110 -,117,116,111,115,115,101,103,117,110,100,97,116,101,110,101,109,111,115,101,102, -101,99,116,111,115,109,195,161,108,97,103,97,115,101,115,105,195,179,110,114,101 -,118,105,115,116,97,103,114,97,110,97,100,97,99,111,109,112,114,97,114,105,110, -103,114,101,115,111,103,97,114,99,195,173,97,97,99,99,105,195,179,110,101,99,117 -,97,100,111,114,113,117,105,101,110,101,115,105,110,99,108,117,115,111,100,101, -98,101,114,195,161,109,97,116,101,114,105,97,104,111,109,98,114,101,115,109,117, -101,115,116,114,97,112,111,100,114,195,173,97,109,97,195,177,97,110,97,195,186, -108,116,105,109,97,101,115,116,97,109,111,115,111,102,105,99,105,97,108,116,97, -109,98,105,101,110,110,105,110,103,195,186,110,115,97,108,117,100,111,115,112, -111,100,101,109,111,115,109,101,106,111,114,97,114,112,111,115,105,116,105,111, -110,98,117,115,105,110,101,115,115,104,111,109,101,112,97,103,101,115,101,99,117 -,114,105,116,121,108,97,110,103,117,97,103,101,115,116,97,110,100,97,114,100,99, -97,109,112,97,105,103,110,102,101,97,116,117,114,101,115,99,97,116,101,103,111, -114,121,101,120,116,101,114,110,97,108,99,104,105,108,100,114,101,110,114,101, -115,101,114,118,101,100,114,101,115,101,97,114,99,104,101,120,99,104,97,110,103, -101,102,97,118,111,114,105,116,101,116,101,109,112,108,97,116,101,109,105,108, -105,116,97,114,121,105,110,100,117,115,116,114,121,115,101,114,118,105,99,101, -115,109,97,116,101,114,105,97,108,112,114,111,100,117,99,116,115,122,45,105,110, -100,101,120,58,99,111,109,109,101,110,116,115,115,111,102,116,119,97,114,101,99, -111,109,112,108,101,116,101,99,97,108,101,110,100,97,114,112,108,97,116,102,111, -114,109,97,114,116,105,99,108,101,115,114,101,113,117,105,114,101,100,109,111, -118,101,109,101,110,116,113,117,101,115,116,105,111,110,98,117,105,108,100,105, -110,103,112,111,108,105,116,105,99,115,112,111,115,115,105,98,108,101,114,101, -108,105,103,105,111,110,112,104,121,115,105,99,97,108,102,101,101,100,98,97,99, -107,114,101,103,105,115,116,101,114,112,105,99,116,117,114,101,115,100,105,115, -97,98,108,101,100,112,114,111,116,111,99,111,108,97,117,100,105,101,110,99,101, -115,101,116,116,105,110,103,115,97,99,116,105,118,105,116,121,101,108,101,109, -101,110,116,115,108,101,97,114,110,105,110,103,97,110,121,116,104,105,110,103,97 -,98,115,116,114,97,99,116,112,114,111,103,114,101,115,115,111,118,101,114,118, -105,101,119,109,97,103,97,122,105,110,101,101,99,111,110,111,109,105,99,116,114, -97,105,110,105,110,103,112,114,101,115,115,117,114,101,118,97,114,105,111,117, -115,32,60,115,116,114,111,110,103,62,112,114,111,112,101,114,116,121,115,104,111 -,112,112,105,110,103,116,111,103,101,116,104,101,114,97,100,118,97,110,99,101, -100,98,101,104,97,118,105,111,114,100,111,119,110,108,111,97,100,102,101,97,116, -117,114,101,100,102,111,111,116,98,97,108,108,115,101,108,101,99,116,101,100,76, -97,110,103,117,97,103,101,100,105,115,116,97,110,99,101,114,101,109,101,109,98, -101,114,116,114,97,99,107,105,110,103,112,97,115,115,119,111,114,100,109,111,100 -,105,102,105,101,100,115,116,117,100,101,110,116,115,100,105,114,101,99,116,108, -121,102,105,103,104,116,105,110,103,110,111,114,116,104,101,114,110,100,97,116, -97,98,97,115,101,102,101,115,116,105,118,97,108,98,114,101,97,107,105,110,103, -108,111,99,97,116,105,111,110,105,110,116,101,114,110,101,116,100,114,111,112, -100,111,119,110,112,114,97,99,116,105,99,101,101,118,105,100,101,110,99,101,102, -117,110,99,116,105,111,110,109,97,114,114,105,97,103,101,114,101,115,112,111,110 -,115,101,112,114,111,98,108,101,109,115,110,101,103,97,116,105,118,101,112,114, -111,103,114,97,109,115,97,110,97,108,121,115,105,115,114,101,108,101,97,115,101, -100,98,97,110,110,101,114,34,62,112,117,114,99,104,97,115,101,112,111,108,105,99 -,105,101,115,114,101,103,105,111,110,97,108,99,114,101,97,116,105,118,101,97,114 -,103,117,109,101,110,116,98,111,111,107,109,97,114,107,114,101,102,101,114,114, -101,114,99,104,101,109,105,99,97,108,100,105,118,105,115,105,111,110,99,97,108, -108,98,97,99,107,115,101,112,97,114,97,116,101,112,114,111,106,101,99,116,115,99 -,111,110,102,108,105,99,116,104,97,114,100,119,97,114,101,105,110,116,101,114, -101,115,116,100,101,108,105,118,101,114,121,109,111,117,110,116,97,105,110,111, -98,116,97,105,110,101,100,61,32,102,97,108,115,101,59,102,111,114,40,118,97,114, -32,97,99,99,101,112,116,101,100,99,97,112,97,99,105,116,121,99,111,109,112,117, -116,101,114,105,100,101,110,116,105,116,121,97,105,114,99,114,97,102,116,101,109 -,112,108,111,121,101,100,112,114,111,112,111,115,101,100,100,111,109,101,115,116 -,105,99,105,110,99,108,117,100,101,115,112,114,111,118,105,100,101,100,104,111, -115,112,105,116,97,108,118,101,114,116,105,99,97,108,99,111,108,108,97,112,115, -101,97,112,112,114,111,97,99,104,112,97,114,116,110,101,114,115,108,111,103,111, -34,62,60,97,100,97,117,103,104,116,101,114,97,117,116,104,111,114,34,32,99,117, -108,116,117,114,97,108,102,97,109,105,108,105,101,115,47,105,109,97,103,101,115, -47,97,115,115,101,109,98,108,121,112,111,119,101,114,102,117,108,116,101,97,99, -104,105,110,103,102,105,110,105,115,104,101,100,100,105,115,116,114,105,99,116, -99,114,105,116,105,99,97,108,99,103,105,45,98,105,110,47,112,117,114,112,111,115 -,101,115,114,101,113,117,105,114,101,115,101,108,101,99,116,105,111,110,98,101, -99,111,109,105,110,103,112,114,111,118,105,100,101,115,97,99,97,100,101,109,105, -99,101,120,101,114,99,105,115,101,97,99,116,117,97,108,108,121,109,101,100,105, -99,105,110,101,99,111,110,115,116,97,110,116,97,99,99,105,100,101,110,116,77,97, -103,97,122,105,110,101,100,111,99,117,109,101,110,116,115,116,97,114,116,105,110 -,103,98,111,116,116,111,109,34,62,111,98,115,101,114,118,101,100,58,32,38,113, -117,111,116,59,101,120,116,101,110,100,101,100,112,114,101,118,105,111,117,115, -83,111,102,116,119,97,114,101,99,117,115,116,111,109,101,114,100,101,99,105,115, -105,111,110,115,116,114,101,110,103,116,104,100,101,116,97,105,108,101,100,115, -108,105,103,104,116,108,121,112,108,97,110,110,105,110,103,116,101,120,116,97, -114,101,97,99,117,114,114,101,110,99,121,101,118,101,114,121,111,110,101,115,116 -,114,97,105,103,104,116,116,114,97,110,115,102,101,114,112,111,115,105,116,105, -118,101,112,114,111,100,117,99,101,100,104,101,114,105,116,97,103,101,115,104, -105,112,112,105,110,103,97,98,115,111,108,117,116,101,114,101,99,101,105,118,101 -,100,114,101,108,101,118,97,110,116,98,117,116,116,111,110,34,32,118,105,111,108 -,101,110,99,101,97,110,121,119,104,101,114,101,98,101,110,101,102,105,116,115, -108,97,117,110,99,104,101,100,114,101,99,101,110,116,108,121,97,108,108,105,97, -110,99,101,102,111,108,108,111,119,101,100,109,117,108,116,105,112,108,101,98, -117,108,108,101,116,105,110,105,110,99,108,117,100,101,100,111,99,99,117,114,114 -,101,100,105,110,116,101,114,110,97,108,36,40,116,104,105,115,41,46,114,101,112, -117,98,108,105,99,62,60,116,114,62,60,116,100,99,111,110,103,114,101,115,115,114 -,101,99,111,114,100,101,100,117,108,116,105,109,97,116,101,115,111,108,117,116, -105,111,110,60,117,108,32,105,100,61,34,100,105,115,99,111,118,101,114,72,111, -109,101,60,47,97,62,119,101,98,115,105,116,101,115,110,101,116,119,111,114,107, -115,97,108,116,104,111,117,103,104,101,110,116,105,114,101,108,121,109,101,109, -111,114,105,97,108,109,101,115,115,97,103,101,115,99,111,110,116,105,110,117,101 -,97,99,116,105,118,101,34,62,115,111,109,101,119,104,97,116,118,105,99,116,111, -114,105,97,87,101,115,116,101,114,110,32,32,116,105,116,108,101,61,34,76,111,99, -97,116,105,111,110,99,111,110,116,114,97,99,116,118,105,115,105,116,111,114,115, -68,111,119,110,108,111,97,100,119,105,116,104,111,117,116,32,114,105,103,104,116 -,34,62,10,109,101,97,115,117,114,101,115,119,105,100,116,104,32,61,32,118,97,114 -,105,97,98,108,101,105,110,118,111,108,118,101,100,118,105,114,103,105,110,105, -97,110,111,114,109,97,108,108,121,104,97,112,112,101,110,101,100,97,99,99,111, -117,110,116,115,115,116,97,110,100,105,110,103,110,97,116,105,111,110,97,108,82, -101,103,105,115,116,101,114,112,114,101,112,97,114,101,100,99,111,110,116,114, -111,108,115,97,99,99,117,114,97,116,101,98,105,114,116,104,100,97,121,115,116, -114,97,116,101,103,121,111,102,102,105,99,105,97,108,103,114,97,112,104,105,99, -115,99,114,105,109,105,110,97,108,112,111,115,115,105,98,108,121,99,111,110,115, -117,109,101,114,80,101,114,115,111,110,97,108,115,112,101,97,107,105,110,103,118 -,97,108,105,100,97,116,101,97,99,104,105,101,118,101,100,46,106,112,103,34,32,47 -,62,109,97,99,104,105,110,101,115,60,47,104,50,62,10,32,32,107,101,121,119,111, -114,100,115,102,114,105,101,110,100,108,121,98,114,111,116,104,101,114,115,99, -111,109,98,105,110,101,100,111,114,105,103,105,110,97,108,99,111,109,112,111,115 -,101,100,101,120,112,101,99,116,101,100,97,100,101,113,117,97,116,101,112,97,107 -,105,115,116,97,110,102,111,108,108,111,119,34,32,118,97,108,117,97,98,108,101, -60,47,108,97,98,101,108,62,114,101,108,97,116,105,118,101,98,114,105,110,103,105 -,110,103,105,110,99,114,101,97,115,101,103,111,118,101,114,110,111,114,112,108, -117,103,105,110,115,47,76,105,115,116,32,111,102,32,72,101,97,100,101,114,34,62, -34,32,110,97,109,101,61,34,32,40,38,113,117,111,116,59,103,114,97,100,117,97,116 -,101,60,47,104,101,97,100,62,10,99,111,109,109,101,114,99,101,109,97,108,97,121, -115,105,97,100,105,114,101,99,116,111,114,109,97,105,110,116,97,105,110,59,104, -101,105,103,104,116,58,115,99,104,101,100,117,108,101,99,104,97,110,103,105,110, -103,98,97,99,107,32,116,111,32,99,97,116,104,111,108,105,99,112,97,116,116,101, -114,110,115,99,111,108,111,114,58,32,35,103,114,101,97,116,101,115,116,115,117, -112,112,108,105,101,115,114,101,108,105,97,98,108,101,60,47,117,108,62,10,9,9,60 -,115,101,108,101,99,116,32,99,105,116,105,122,101,110,115,99,108,111,116,104,105 -,110,103,119,97,116,99,104,105,110,103,60,108,105,32,105,100,61,34,115,112,101, -99,105,102,105,99,99,97,114,114,121,105,110,103,115,101,110,116,101,110,99,101, -60,99,101,110,116,101,114,62,99,111,110,116,114,97,115,116,116,104,105,110,107, -105,110,103,99,97,116,99,104,40,101,41,115,111,117,116,104,101,114,110,77,105,99 -,104,97,101,108,32,109,101,114,99,104,97,110,116,99,97,114,111,117,115,101,108, -112,97,100,100,105,110,103,58,105,110,116,101,114,105,111,114,46,115,112,108,105 -,116,40,34,108,105,122,97,116,105,111,110,79,99,116,111,98,101,114,32,41,123,114 -,101,116,117,114,110,105,109,112,114,111,118,101,100,45,45,38,103,116,59,10,10, -99,111,118,101,114,97,103,101,99,104,97,105,114,109,97,110,46,112,110,103,34,32, -47,62,115,117,98,106,101,99,116,115,82,105,99,104,97,114,100,32,119,104,97,116, -101,118,101,114,112,114,111,98,97,98,108,121,114,101,99,111,118,101,114,121,98, -97,115,101,98,97,108,108,106,117,100,103,109,101,110,116,99,111,110,110,101,99, -116,46,46,99,115,115,34,32,47,62,32,119,101,98,115,105,116,101,114,101,112,111, -114,116,101,100,100,101,102,97,117,108,116,34,47,62,60,47,97,62,13,10,101,108, -101,99,116,114,105,99,115,99,111,116,108,97,110,100,99,114,101,97,116,105,111, -110,113,117,97,110,116,105,116,121,46,32,73,83,66,78,32,48,100,105,100,32,110, -111,116,32,105,110,115,116,97,110,99,101,45,115,101,97,114,99,104,45,34,32,108, -97,110,103,61,34,115,112,101,97,107,101,114,115,67,111,109,112,117,116,101,114, -99,111,110,116,97,105,110,115,97,114,99,104,105,118,101,115,109,105,110,105,115, -116,101,114,114,101,97,99,116,105,111,110,100,105,115,99,111,117,110,116,73,116, -97,108,105,97,110,111,99,114,105,116,101,114,105,97,115,116,114,111,110,103,108, -121,58,32,39,104,116,116,112,58,39,115,99,114,105,112,116,39,99,111,118,101,114, -105,110,103,111,102,102,101,114,105,110,103,97,112,112,101,97,114,101,100,66,114 -,105,116,105,115,104,32,105,100,101,110,116,105,102,121,70,97,99,101,98,111,111, -107,110,117,109,101,114,111,117,115,118,101,104,105,99,108,101,115,99,111,110,99 -,101,114,110,115,65,109,101,114,105,99,97,110,104,97,110,100,108,105,110,103,100 -,105,118,32,105,100,61,34,87,105,108,108,105,97,109,32,112,114,111,118,105,100, -101,114,95,99,111,110,116,101,110,116,97,99,99,117,114,97,99,121,115,101,99,116, -105,111,110,32,97,110,100,101,114,115,111,110,102,108,101,120,105,98,108,101,67, -97,116,101,103,111,114,121,108,97,119,114,101,110,99,101,60,115,99,114,105,112, -116,62,108,97,121,111,117,116,61,34,97,112,112,114,111,118,101,100,32,109,97,120 -,105,109,117,109,104,101,97,100,101,114,34,62,60,47,116,97,98,108,101,62,83,101, -114,118,105,99,101,115,104,97,109,105,108,116,111,110,99,117,114,114,101,110,116 -,32,99,97,110,97,100,105,97,110,99,104,97,110,110,101,108,115,47,116,104,101,109 -,101,115,47,47,97,114,116,105,99,108,101,111,112,116,105,111,110,97,108,112,111, -114,116,117,103,97,108,118,97,108,117,101,61,34,34,105,110,116,101,114,118,97, -108,119,105,114,101,108,101,115,115,101,110,116,105,116,108,101,100,97,103,101, -110,99,105,101,115,83,101,97,114,99,104,34,32,109,101,97,115,117,114,101,100,116 -,104,111,117,115,97,110,100,115,112,101,110,100,105,110,103,38,104,101,108,108, -105,112,59,110,101,119,32,68,97,116,101,34,32,115,105,122,101,61,34,112,97,103, -101,78,97,109,101,109,105,100,100,108,101,34,32,34,32,47,62,60,47,97,62,104,105, -100,100,101,110,34,62,115,101,113,117,101,110,99,101,112,101,114,115,111,110,97, -108,111,118,101,114,102,108,111,119,111,112,105,110,105,111,110,115,105,108,108, -105,110,111,105,115,108,105,110,107,115,34,62,10,9,60,116,105,116,108,101,62,118 -,101,114,115,105,111,110,115,115,97,116,117,114,100,97,121,116,101,114,109,105, -110,97,108,105,116,101,109,112,114,111,112,101,110,103,105,110,101,101,114,115, -101,99,116,105,111,110,115,100,101,115,105,103,110,101,114,112,114,111,112,111, -115,97,108,61,34,102,97,108,115,101,34,69,115,112,97,195,177,111,108,114,101,108 -,101,97,115,101,115,115,117,98,109,105,116,34,32,101,114,38,113,117,111,116,59, -97,100,100,105,116,105,111,110,115,121,109,112,116,111,109,115,111,114,105,101, -110,116,101,100,114,101,115,111,117,114,99,101,114,105,103,104,116,34,62,60,112, -108,101,97,115,117,114,101,115,116,97,116,105,111,110,115,104,105,115,116,111, -114,121,46,108,101,97,118,105,110,103,32,32,98,111,114,100,101,114,61,99,111,110 -,116,101,110,116,115,99,101,110,116,101,114,34,62,46,10,10,83,111,109,101,32,100 -,105,114,101,99,116,101,100,115,117,105,116,97,98,108,101,98,117,108,103,97,114, -105,97,46,115,104,111,119,40,41,59,100,101,115,105,103,110,101,100,71,101,110, -101,114,97,108,32,99,111,110,99,101,112,116,115,69,120,97,109,112,108,101,115, -119,105,108,108,105,97,109,115,79,114,105,103,105,110,97,108,34,62,60,115,112,97 -,110,62,115,101,97,114,99,104,34,62,111,112,101,114,97,116,111,114,114,101,113, -117,101,115,116,115,97,32,38,113,117,111,116,59,97,108,108,111,119,105,110,103, -68,111,99,117,109,101,110,116,114,101,118,105,115,105,111,110,46,32,10,10,84,104 -,101,32,121,111,117,114,115,101,108,102,67,111,110,116,97,99,116,32,109,105,99, -104,105,103,97,110,69,110,103,108,105,115,104,32,99,111,108,117,109,98,105,97, -112,114,105,111,114,105,116,121,112,114,105,110,116,105,110,103,100,114,105,110, -107,105,110,103,102,97,99,105,108,105,116,121,114,101,116,117,114,110,101,100,67 -,111,110,116,101,110,116,32,111,102,102,105,99,101,114,115,82,117,115,115,105,97 -,110,32,103,101,110,101,114,97,116,101,45,56,56,53,57,45,49,34,105,110,100,105, -99,97,116,101,102,97,109,105,108,105,97,114,32,113,117,97,108,105,116,121,109,97 -,114,103,105,110,58,48,32,99,111,110,116,101,110,116,118,105,101,119,112,111,114 -,116,99,111,110,116,97,99,116,115,45,116,105,116,108,101,34,62,112,111,114,116, -97,98,108,101,46,108,101,110,103,116,104,32,101,108,105,103,105,98,108,101,105, -110,118,111,108,118,101,115,97,116,108,97,110,116,105,99,111,110,108,111,97,100, -61,34,100,101,102,97,117,108,116,46,115,117,112,112,108,105,101,100,112,97,121, -109,101,110,116,115,103,108,111,115,115,97,114,121,10,10,65,102,116,101,114,32, -103,117,105,100,97,110,99,101,60,47,116,100,62,60,116,100,101,110,99,111,100,105 -,110,103,109,105,100,100,108,101,34,62,99,97,109,101,32,116,111,32,100,105,115, -112,108,97,121,115,115,99,111,116,116,105,115,104,106,111,110,97,116,104,97,110, -109,97,106,111,114,105,116,121,119,105,100,103,101,116,115,46,99,108,105,110,105 -,99,97,108,116,104,97,105,108,97,110,100,116,101,97,99,104,101,114,115,60,104, -101,97,100,62,10,9,97,102,102,101,99,116,101,100,115,117,112,112,111,114,116,115 -,112,111,105,110,116,101,114,59,116,111,83,116,114,105,110,103,60,47,115,109,97, -108,108,62,111,107,108,97,104,111,109,97,119,105,108,108,32,98,101,32,105,110, -118,101,115,116,111,114,48,34,32,97,108,116,61,34,104,111,108,105,100,97,121,115 -,82,101,115,111,117,114,99,101,108,105,99,101,110,115,101,100,32,40,119,104,105, -99,104,32,46,32,65,102,116,101,114,32,99,111,110,115,105,100,101,114,118,105,115 -,105,116,105,110,103,101,120,112,108,111,114,101,114,112,114,105,109,97,114,121, -32,115,101,97,114,99,104,34,32,97,110,100,114,111,105,100,34,113,117,105,99,107, -108,121,32,109,101,101,116,105,110,103,115,101,115,116,105,109,97,116,101,59,114 -,101,116,117,114,110,32,59,99,111,108,111,114,58,35,32,104,101,105,103,104,116, -61,97,112,112,114,111,118,97,108,44,32,38,113,117,111,116,59,32,99,104,101,99, -107,101,100,46,109,105,110,46,106,115,34,109,97,103,110,101,116,105,99,62,60,47, -97,62,60,47,104,102,111,114,101,99,97,115,116,46,32,87,104,105,108,101,32,116, -104,117,114,115,100,97,121,100,118,101,114,116,105,115,101,38,101,97,99,117,116, -101,59,104,97,115,67,108,97,115,115,101,118,97,108,117,97,116,101,111,114,100, -101,114,105,110,103,101,120,105,115,116,105,110,103,112,97,116,105,101,110,116, -115,32,79,110,108,105,110,101,32,99,111,108,111,114,97,100,111,79,112,116,105, -111,110,115,34,99,97,109,112,98,101,108,108,60,33,45,45,32,101,110,100,60,47,115 -,112,97,110,62,60,60,98,114,32,47,62,13,10,95,112,111,112,117,112,115,124,115,99 -,105,101,110,99,101,115,44,38,113,117,111,116,59,32,113,117,97,108,105,116,121, -32,87,105,110,100,111,119,115,32,97,115,115,105,103,110,101,100,104,101,105,103, -104,116,58,32,60,98,32,99,108,97,115,115,108,101,38,113,117,111,116,59,32,118,97 -,108,117,101,61,34,32,67,111,109,112,97,110,121,101,120,97,109,112,108,101,115, -60,105,102,114,97,109,101,32,98,101,108,105,101,118,101,115,112,114,101,115,101, -110,116,115,109,97,114,115,104,97,108,108,112,97,114,116,32,111,102,32,112,114, -111,112,101,114,108,121,41,46,10,10,84,104,101,32,116,97,120,111,110,111,109,121 -,109,117,99,104,32,111,102,32,60,47,115,112,97,110,62,10,34,32,100,97,116,97,45, -115,114,116,117,103,117,195,170,115,115,99,114,111,108,108,84,111,32,112,114,111 -,106,101,99,116,60,104,101,97,100,62,13,10,97,116,116,111,114,110,101,121,101, -109,112,104,97,115,105,115,115,112,111,110,115,111,114,115,102,97,110,99,121,98, -111,120,119,111,114,108,100,39,115,32,119,105,108,100,108,105,102,101,99,104,101 -,99,107,101,100,61,115,101,115,115,105,111,110,115,112,114,111,103,114,97,109, -109,112,120,59,102,111,110,116,45,32,80,114,111,106,101,99,116,106,111,117,114, -110,97,108,115,98,101,108,105,101,118,101,100,118,97,99,97,116,105,111,110,116, -104,111,109,112,115,111,110,108,105,103,104,116,105,110,103,97,110,100,32,116, -104,101,32,115,112,101,99,105,97,108,32,98,111,114,100,101,114,61,48,99,104,101, -99,107,105,110,103,60,47,116,98,111,100,121,62,60,98,117,116,116,111,110,32,67, -111,109,112,108,101,116,101,99,108,101,97,114,102,105,120,10,60,104,101,97,100, -62,10,97,114,116,105,99,108,101,32,60,115,101,99,116,105,111,110,102,105,110,100 -,105,110,103,115,114,111,108,101,32,105,110,32,112,111,112,117,108,97,114,32,32, -79,99,116,111,98,101,114,119,101,98,115,105,116,101,32,101,120,112,111,115,117, -114,101,117,115,101,100,32,116,111,32,32,99,104,97,110,103,101,115,111,112,101, -114,97,116,101,100,99,108,105,99,107,105,110,103,101,110,116,101,114,105,110,103 -,99,111,109,109,97,110,100,115,105,110,102,111,114,109,101,100,32,110,117,109,98 -,101,114,115,32,32,60,47,100,105,118,62,99,114,101,97,116,105,110,103,111,110,83 -,117,98,109,105,116,109,97,114,121,108,97,110,100,99,111,108,108,101,103,101,115 -,97,110,97,108,121,116,105,99,108,105,115,116,105,110,103,115,99,111,110,116,97, -99,116,46,108,111,103,103,101,100,73,110,97,100,118,105,115,111,114,121,115,105, -98,108,105,110,103,115,99,111,110,116,101,110,116,34,115,38,113,117,111,116,59, -41,115,46,32,84,104,105,115,32,112,97,99,107,97,103,101,115,99,104,101,99,107,98 -,111,120,115,117,103,103,101,115,116,115,112,114,101,103,110,97,110,116,116,111, -109,111,114,114,111,119,115,112,97,99,105,110,103,61,105,99,111,110,46,112,110, -103,106,97,112,97,110,101,115,101,99,111,100,101,98,97,115,101,98,117,116,116, -111,110,34,62,103,97,109,98,108,105,110,103,115,117,99,104,32,97,115,32,44,32, -119,104,105,108,101,32,60,47,115,112,97,110,62,32,109,105,115,115,111,117,114, -105,115,112,111,114,116,105,110,103,116,111,112,58,49,112,120,32,46,60,47,115, -112,97,110,62,116,101,110,115,105,111,110,115,119,105,100,116,104,61,34,50,108, -97,122,121,108,111,97,100,110,111,118,101,109,98,101,114,117,115,101,100,32,105, -110,32,104,101,105,103,104,116,61,34,99,114,105,112,116,34,62,10,38,110,98,115, -112,59,60,47,60,116,114,62,60,116,100,32,104,101,105,103,104,116,58,50,47,112, -114,111,100,117,99,116,99,111,117,110,116,114,121,32,105,110,99,108,117,100,101, -32,102,111,111,116,101,114,34,32,38,108,116,59,33,45,45,32,116,105,116,108,101, -34,62,60,47,106,113,117,101,114,121,46,60,47,102,111,114,109,62,10,40,231,174, -128,228,189,147,41,40,231,185,129,233,171,148,41,104,114,118,97,116,115,107,105, -105,116,97,108,105,97,110,111,114,111,109,195,162,110,196,131,116,195,188,114, -107,195,167,101,216,167,216,177,216,175,217,136,116,97,109,98,105,195,169,110, -110,111,116,105,99,105,97,115,109,101,110,115,97,106,101,115,112,101,114,115,111 -,110,97,115,100,101,114,101,99,104,111,115,110,97,99,105,111,110,97,108,115,101, -114,118,105,99,105,111,99,111,110,116,97,99,116,111,117,115,117,97,114,105,111, -115,112,114,111,103,114,97,109,97,103,111,98,105,101,114,110,111,101,109,112,114 -,101,115,97,115,97,110,117,110,99,105,111,115,118,97,108,101,110,99,105,97,99, -111,108,111,109,98,105,97,100,101,115,112,117,195,169,115,100,101,112,111,114, -116,101,115,112,114,111,121,101,99,116,111,112,114,111,100,117,99,116,111,112, -195,186,98,108,105,99,111,110,111,115,111,116,114,111,115,104,105,115,116,111, -114,105,97,112,114,101,115,101,110,116,101,109,105,108,108,111,110,101,115,109, -101,100,105,97,110,116,101,112,114,101,103,117,110,116,97,97,110,116,101,114,105 -,111,114,114,101,99,117,114,115,111,115,112,114,111,98,108,101,109,97,115,97,110 -,116,105,97,103,111,110,117,101,115,116,114,111,115,111,112,105,110,105,195,179, -110,105,109,112,114,105,109,105,114,109,105,101,110,116,114,97,115,97,109,195, -169,114,105,99,97,118,101,110,100,101,100,111,114,115,111,99,105,101,100,97,100, -114,101,115,112,101,99,116,111,114,101,97,108,105,122,97,114,114,101,103,105,115 -,116,114,111,112,97,108,97,98,114,97,115,105,110,116,101,114,195,169,115,101,110 -,116,111,110,99,101,115,101,115,112,101,99,105,97,108,109,105,101,109,98,114,111 -,115,114,101,97,108,105,100,97,100,99,195,179,114,100,111,98,97,122,97,114,97, -103,111,122,97,112,195,161,103,105,110,97,115,115,111,99,105,97,108,101,115,98, -108,111,113,117,101,97,114,103,101,115,116,105,195,179,110,97,108,113,117,105, -108,101,114,115,105,115,116,101,109,97,115,99,105,101,110,99,105,97,115,99,111, -109,112,108,101,116,111,118,101,114,115,105,195,179,110,99,111,109,112,108,101, -116,97,101,115,116,117,100,105,111,115,112,195,186,98,108,105,99,97,111,98,106, -101,116,105,118,111,97,108,105,99,97,110,116,101,98,117,115,99,97,100,111,114,99 -,97,110,116,105,100,97,100,101,110,116,114,97,100,97,115,97,99,99,105,111,110, -101,115,97,114,99,104,105,118,111,115,115,117,112,101,114,105,111,114,109,97,121 -,111,114,195,173,97,97,108,101,109,97,110,105,97,102,117,110,99,105,195,179,110, -195,186,108,116,105,109,111,115,104,97,99,105,101,110,100,111,97,113,117,101,108 -,108,111,115,101,100,105,99,105,195,179,110,102,101,114,110,97,110,100,111,97, -109,98,105,101,110,116,101,102,97,99,101,98,111,111,107,110,117,101,115,116,114, -97,115,99,108,105,101,110,116,101,115,112,114,111,99,101,115,111,115,98,97,115, -116,97,110,116,101,112,114,101,115,101,110,116,97,114,101,112,111,114,116,97,114 -,99,111,110,103,114,101,115,111,112,117,98,108,105,99,97,114,99,111,109,101,114, -99,105,111,99,111,110,116,114,97,116,111,106,195,179,118,101,110,101,115,100,105 -,115,116,114,105,116,111,116,195,169,99,110,105,99,97,99,111,110,106,117,110,116 -,111,101,110,101,114,103,195,173,97,116,114,97,98,97,106,97,114,97,115,116,117, -114,105,97,115,114,101,99,105,101,110,116,101,117,116,105,108,105,122,97,114,98, -111,108,101,116,195,173,110,115,97,108,118,97,100,111,114,99,111,114,114,101,99, -116,97,116,114,97,98,97,106,111,115,112,114,105,109,101,114,111,115,110,101,103, -111,99,105,111,115,108,105,98,101,114,116,97,100,100,101,116,97,108,108,101,115, -112,97,110,116,97,108,108,97,112,114,195,179,120,105,109,111,97,108,109,101,114, -195,173,97,97,110,105,109,97,108,101,115,113,117,105,195,169,110,101,115,99,111, -114,97,122,195,179,110,115,101,99,99,105,195,179,110,98,117,115,99,97,110,100, -111,111,112,99,105,111,110,101,115,101,120,116,101,114,105,111,114,99,111,110,99 -,101,112,116,111,116,111,100,97,118,195,173,97,103,97,108,101,114,195,173,97,101 -,115,99,114,105,98,105,114,109,101,100,105,99,105,110,97,108,105,99,101,110,99, -105,97,99,111,110,115,117,108,116,97,97,115,112,101,99,116,111,115,99,114,195, -173,116,105,99,97,100,195,179,108,97,114,101,115,106,117,115,116,105,99,105,97, -100,101,98,101,114,195,161,110,112,101,114,195,173,111,100,111,110,101,99,101, -115,105,116,97,109,97,110,116,101,110,101,114,112,101,113,117,101,195,177,111, -114,101,99,105,98,105,100,97,116,114,105,98,117,110,97,108,116,101,110,101,114, -105,102,101,99,97,110,99,105,195,179,110,99,97,110,97,114,105,97,115,100,101,115 -,99,97,114,103,97,100,105,118,101,114,115,111,115,109,97,108,108,111,114,99,97, -114,101,113,117,105,101,114,101,116,195,169,99,110,105,99,111,100,101,98,101,114 -,195,173,97,118,105,118,105,101,110,100,97,102,105,110,97,110,122,97,115,97,100, -101,108,97,110,116,101,102,117,110,99,105,111,110,97,99,111,110,115,101,106,111, -115,100,105,102,195,173,99,105,108,99,105,117,100,97,100,101,115,97,110,116,105, -103,117,97,115,97,118,97,110,122,97,100,97,116,195,169,114,109,105,110,111,117, -110,105,100,97,100,101,115,115,195,161,110,99,104,101,122,99,97,109,112,97,195, -177,97,115,111,102,116,111,110,105,99,114,101,118,105,115,116,97,115,99,111,110, -116,105,101,110,101,115,101,99,116,111,114,101,115,109,111,109,101,110,116,111, -115,102,97,99,117,108,116,97,100,99,114,195,169,100,105,116,111,100,105,118,101, -114,115,97,115,115,117,112,117,101,115,116,111,102,97,99,116,111,114,101,115,115 -,101,103,117,110,100,111,115,112,101,113,117,101,195,177,97,208,179,208,190,208, -180,208,176,208,181,209,129,208,187,208,184,208,181,209,129,209,130,209,140,208, -177,209,139,208,187,208,190,208,177,209,139,209,130,209,140,209,141,209,130,208, -190,208,188,208,149,209,129,208,187,208,184,209,130,208,190,208,179,208,190,208, -188,208,181,208,189,209,143,208,178,209,129,208,181,209,133,209,141,209,130,208, -190,208,185,208,180,208,176,208,182,208,181,208,177,209,139,208,187,208,184,208, -179,208,190,208,180,209,131,208,180,208,181,208,189,209,140,209,141,209,130,208, -190,209,130,208,177,209,139,208,187,208,176,209,129,208,181,208,177,209,143,208, -190,208,180,208,184,208,189,209,129,208,181,208,177,208,181,208,189,208,176,208, -180,208,190,209,129,208,176,208,185,209,130,209,132,208,190,209,130,208,190,208, -189,208,181,208,179,208,190,209,129,208,178,208,190,208,184,209,129,208,178,208, -190,208,185,208,184,208,179,209,128,209,139,209,130,208,190,208,182,208,181,208, -178,209,129,208,181,208,188,209,129,208,178,208,190,209,142,208,187,208,184,209, -136,209,140,209,141,209,130,208,184,209,133,208,191,208,190,208,186,208,176,208, -180,208,189,208,181,208,185,208,180,208,190,208,188,208,176,208,188,208,184,209, -128,208,176,208,187,208,184,208,177,208,190,209,130,208,181,208,188,209,131,209, -133,208,190,209,130,209,143,208,180,208,178,209,131,209,133,209,129,208,181,209, -130,208,184,208,187,209,142,208,180,208,184,208,180,208,181,208,187,208,190,208, -188,208,184,209,128,208,181,209,130,208,181,208,177,209,143,209,129,208,178,208, -190,208,181,208,178,208,184,208,180,208,181,209,135,208,181,208,179,208,190,209, -141,209,130,208,184,208,188,209,129,209,135,208,181,209,130,209,130,208,181,208, -188,209,139,209,134,208,181,208,189,209,139,209,129,209,130,208,176,208,187,208, -178,208,181,208,180,209,140,209,130,208,181,208,188,208,181,208,178,208,190,208, -180,209,139,209,130,208,181,208,177,208,181,208,178,209,139,209,136,208,181,208, -189,208,176,208,188,208,184,209,130,208,184,208,191,208,176,209,130,208,190,208, -188,209,131,208,191,209,128,208,176,208,178,208,187,208,184,209,134,208,176,208, -190,208,180,208,189,208,176,208,179,208,190,208,180,209,139,208,183,208,189,208, -176,209,142,208,188,208,190,208,179,209,131,208,180,209,128,209,131,208,179,208, -178,209,129,208,181,208,185,208,184,208,180,208,181,209,130,208,186,208,184,208, -189,208,190,208,190,208,180,208,189,208,190,208,180,208,181,208,187,208,176,208, -180,208,181,208,187,208,181,209,129,209,128,208,190,208,186,208,184,209,142,208, -189,209,143,208,178,208,181,209,129,209,140,208,149,209,129,209,130,209,140,209, -128,208,176,208,183,208,176,208,189,208,176,209,136,208,184,216,167,217,132,217, -132,217,135,216,167,217,132,216,170,217,138,216,172,217,133,217,138,216,185,216, -174,216,167,216,181,216,169,216,167,217,132,216,176,217,138,216,185,217,132,217, -138,217,135,216,172,216,175,217,138,216,175,216,167,217,132,216,162,217,134,216, -167,217,132,216,177,216,175,216,170,216,173,217,131,217,133,216,181,217,129,216, -173,216,169,217,131,216,167,217,134,216,170,216,167,217,132,217,132,217,138,217, -138,217,131,217,136,217,134,216,180,216,168,217,131,216,169,217,129,217,138,217, -135,216,167,216,168,217,134,216,167,216,170,216,173,217,136,216,167,216,161,216, -163,217,131,216,171,216,177,216,174,217,132,216,167,217,132,216,167,217,132,216, -173,216,168,216,175,217,132,217,138,217,132,216,175,216,177,217,136,216,179,216, -167,216,182,216,186,216,183,216,170,217,131,217,136,217,134,217,135,217,134,216, -167,217,131,216,179,216,167,216,173,216,169,217,134,216,167,216,175,217,138,216, -167,217,132,216,183,216,168,216,185,217,132,217,138,217,131,216,180,217,131,216, -177,216,167,217,138,217,133,217,131,217,134,217,133,217,134,217,135,216,167,216, -180,216,177,217,131,216,169,216,177,216,166,217,138,216,179,217,134,216,180,217, -138,216,183,217,133,216,167,216,176,216,167,216,167,217,132,217,129,217,134,216, -180,216,168,216,167,216,168,216,170,216,185,216,168,216,177,216,177,216,173,217, -133,216,169,217,131,216,167,217,129,216,169,217,138,217,130,217,136,217,132,217, -133,216,177,217,131,216,178,217,131,217,132,217,133,216,169,216,163,216,173,217, -133,216,175,217,130,217,132,216,168,217,138,217,138,216,185,217,134,217,138,216, -181,217,136,216,177,216,169,216,183,216,177,217,138,217,130,216,180,216,167,216, -177,217,131,216,172,217,136,216,167,217,132,216,163,216,174,216,177,217,137,217, -133,216,185,217,134,216,167,216,167,216,168,216,173,216,171,216,185,216,177,217, -136,216,182,216,168,216,180,217,131,217,132,217,133,216,179,216,172,217,132,216, -168,217,134,216,167,217,134,216,174,216,167,217,132,216,175,217,131,216,170,216, -167,216,168,217,131,217,132,217,138,216,169,216,168,216,175,217,136,217,134,216, -163,217,138,216,182,216,167,217,138,217,136,216,172,216,175,217,129,216,177,217, -138,217,130,217,131,216,170,216,168,216,170,216,163,217,129,216,182,217,132,217, -133,216,183,216,168,216,174,216,167,217,131,216,171,216,177,216,168,216,167,216, -177,217,131,216,167,217,129,216,182,217,132,216,167,216,173,217,132,217,137,217, -134,217,129,216,179,217,135,216,163,217,138,216,167,217,133,216,177,216,175,217, -136,216,175,216,163,217,134,217,135,216,167,216,175,217,138,217,134,216,167,216, -167,217,132,216,167,217,134,217,133,216,185,216,177,216,182,216,170,216,185,217, -132,217,133,216,175,216,167,216,174,217,132,217,133,217,133,217,131,217,134,0,0, -0,0,0,0,0,0,1,0,1,0,1,0,1,0,2,0,2,0,2,0,2,0,4,0,4,0,4,0,4,0,0,1,2,3,4,5,6,7,7,6, -5,4,3,2,1,0,8,9,10,11,12,13,14,15,15,14,13,12,11,10,9,8,16,17,18,19,20,21,22,23, -23,22,21,20,19,18,17,16,24,25,26,27,28,29,30,31,31,30,29,28,27,26,25,24,255,255, -255,255,0,0,0,0,0,0,0,0,255,255,255,255,1,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0, -3,0,0,0,255,255,0,1,0,0,0,1,0,0,255,255,0,1,0,0,0,8,0,8,0,8,0,8,0,0,0,1,0,2,0,3, -0,4,0,5,0,6,0,7,114,101,115,111,117,114,99,101,115,99,111,117,110,116,114,105, -101,115,113,117,101,115,116,105,111,110,115,101,113,117,105,112,109,101,110,116, -99,111,109,109,117,110,105,116,121,97,118,97,105,108,97,98,108,101,104,105,103, -104,108,105,103,104,116,68,84,68,47,120,104,116,109,108,109,97,114,107,101,116, -105,110,103,107,110,111,119,108,101,100,103,101,115,111,109,101,116,104,105,110, -103,99,111,110,116,97,105,110,101,114,100,105,114,101,99,116,105,111,110,115,117 -,98,115,99,114,105,98,101,97,100,118,101,114,116,105,115,101,99,104,97,114,97,99 -,116,101,114,34,32,118,97,108,117,101,61,34,60,47,115,101,108,101,99,116,62,65, -117,115,116,114,97,108,105,97,34,32,99,108,97,115,115,61,34,115,105,116,117,97, -116,105,111,110,97,117,116,104,111,114,105,116,121,102,111,108,108,111,119,105, -110,103,112,114,105,109,97,114,105,108,121,111,112,101,114,97,116,105,111,110,99 -,104,97,108,108,101,110,103,101,100,101,118,101,108,111,112,101,100,97,110,111, -110,121,109,111,117,115,102,117,110,99,116,105,111,110,32,102,117,110,99,116,105 -,111,110,115,99,111,109,112,97,110,105,101,115,115,116,114,117,99,116,117,114, -101,97,103,114,101,101,109,101,110,116,34,32,116,105,116,108,101,61,34,112,111, -116,101,110,116,105,97,108,101,100,117,99,97,116,105,111,110,97,114,103,117,109, -101,110,116,115,115,101,99,111,110,100,97,114,121,99,111,112,121,114,105,103,104 -,116,108,97,110,103,117,97,103,101,115,101,120,99,108,117,115,105,118,101,99,111 -,110,100,105,116,105,111,110,60,47,102,111,114,109,62,13,10,115,116,97,116,101, -109,101,110,116,97,116,116,101,110,116,105,111,110,66,105,111,103,114,97,112,104 -,121,125,32,101,108,115,101,32,123,10,115,111,108,117,116,105,111,110,115,119, -104,101,110,32,116,104,101,32,65,110,97,108,121,116,105,99,115,116,101,109,112, -108,97,116,101,115,100,97,110,103,101,114,111,117,115,115,97,116,101,108,108,105 -,116,101,100,111,99,117,109,101,110,116,115,112,117,98,108,105,115,104,101,114, -105,109,112,111,114,116,97,110,116,112,114,111,116,111,116,121,112,101,105,110, -102,108,117,101,110,99,101,38,114,97,113,117,111,59,60,47,101,102,102,101,99,116 -,105,118,101,103,101,110,101,114,97,108,108,121,116,114,97,110,115,102,111,114, -109,98,101,97,117,116,105,102,117,108,116,114,97,110,115,112,111,114,116,111,114 -,103,97,110,105,122,101,100,112,117,98,108,105,115,104,101,100,112,114,111,109, -105,110,101,110,116,117,110,116,105,108,32,116,104,101,116,104,117,109,98,110,97 -,105,108,78,97,116,105,111,110,97,108,32,46,102,111,99,117,115,40,41,59,111,118, -101,114,32,116,104,101,32,109,105,103,114,97,116,105,111,110,97,110,110,111,117, -110,99,101,100,102,111,111,116,101,114,34,62,10,101,120,99,101,112,116,105,111, -110,108,101,115,115,32,116,104,97,110,101,120,112,101,110,115,105,118,101,102, -111,114,109,97,116,105,111,110,102,114,97,109,101,119,111,114,107,116,101,114, -114,105,116,111,114,121,110,100,105,99,97,116,105,111,110,99,117,114,114,101,110 -,116,108,121,99,108,97,115,115,78,97,109,101,99,114,105,116,105,99,105,115,109, -116,114,97,100,105,116,105,111,110,101,108,115,101,119,104,101,114,101,65,108, -101,120,97,110,100,101,114,97,112,112,111,105,110,116,101,100,109,97,116,101,114 -,105,97,108,115,98,114,111,97,100,99,97,115,116,109,101,110,116,105,111,110,101, -100,97,102,102,105,108,105,97,116,101,60,47,111,112,116,105,111,110,62,116,114, -101,97,116,109,101,110,116,100,105,102,102,101,114,101,110,116,47,100,101,102,97 -,117,108,116,46,80,114,101,115,105,100,101,110,116,111,110,99,108,105,99,107,61, -34,98,105,111,103,114,97,112,104,121,111,116,104,101,114,119,105,115,101,112,101 -,114,109,97,110,101,110,116,70,114,97,110,195,167,97,105,115,72,111,108,108,121, -119,111,111,100,101,120,112,97,110,115,105,111,110,115,116,97,110,100,97,114,100 -,115,60,47,115,116,121,108,101,62,10,114,101,100,117,99,116,105,111,110,68,101, -99,101,109,98,101,114,32,112,114,101,102,101,114,114,101,100,67,97,109,98,114, -105,100,103,101,111,112,112,111,110,101,110,116,115,66,117,115,105,110,101,115, -115,32,99,111,110,102,117,115,105,111,110,62,10,60,116,105,116,108,101,62,112, -114,101,115,101,110,116,101,100,101,120,112,108,97,105,110,101,100,100,111,101, -115,32,110,111,116,32,119,111,114,108,100,119,105,100,101,105,110,116,101,114, -102,97,99,101,112,111,115,105,116,105,111,110,115,110,101,119,115,112,97,112,101 -,114,60,47,116,97,98,108,101,62,10,109,111,117,110,116,97,105,110,115,108,105, -107,101,32,116,104,101,32,101,115,115,101,110,116,105,97,108,102,105,110,97,110, -99,105,97,108,115,101,108,101,99,116,105,111,110,97,99,116,105,111,110,61,34,47, -97,98,97,110,100,111,110,101,100,69,100,117,99,97,116,105,111,110,112,97,114,115 -,101,73,110,116,40,115,116,97,98,105,108,105,116,121,117,110,97,98,108,101,32, -116,111,60,47,116,105,116,108,101,62,10,114,101,108,97,116,105,111,110,115,78, -111,116,101,32,116,104,97,116,101,102,102,105,99,105,101,110,116,112,101,114,102 -,111,114,109,101,100,116,119,111,32,121,101,97,114,115,83,105,110,99,101,32,116, -104,101,116,104,101,114,101,102,111,114,101,119,114,97,112,112,101,114,34,62,97, -108,116,101,114,110,97,116,101,105,110,99,114,101,97,115,101,100,66,97,116,116, -108,101,32,111,102,112,101,114,99,101,105,118,101,100,116,114,121,105,110,103,32 -,116,111,110,101,99,101,115,115,97,114,121,112,111,114,116,114,97,121,101,100, -101,108,101,99,116,105,111,110,115,69,108,105,122,97,98,101,116,104,60,47,105, -102,114,97,109,101,62,100,105,115,99,111,118,101,114,121,105,110,115,117,114,97, -110,99,101,115,46,108,101,110,103,116,104,59,108,101,103,101,110,100,97,114,121, -71,101,111,103,114,97,112,104,121,99,97,110,100,105,100,97,116,101,99,111,114, -112,111,114,97,116,101,115,111,109,101,116,105,109,101,115,115,101,114,118,105, -99,101,115,46,105,110,104,101,114,105,116,101,100,60,47,115,116,114,111,110,103, -62,67,111,109,109,117,110,105,116,121,114,101,108,105,103,105,111,117,115,108, -111,99,97,116,105,111,110,115,67,111,109,109,105,116,116,101,101,98,117,105,108, -100,105,110,103,115,116,104,101,32,119,111,114,108,100,110,111,32,108,111,110, -103,101,114,98,101,103,105,110,110,105,110,103,114,101,102,101,114,101,110,99, -101,99,97,110,110,111,116,32,98,101,102,114,101,113,117,101,110,99,121,116,121, -112,105,99,97,108,108,121,105,110,116,111,32,116,104,101,32,114,101,108,97,116, -105,118,101,59,114,101,99,111,114,100,105,110,103,112,114,101,115,105,100,101, -110,116,105,110,105,116,105,97,108,108,121,116,101,99,104,110,105,113,117,101, -116,104,101,32,111,116,104,101,114,105,116,32,99,97,110,32,98,101,101,120,105, -115,116,101,110,99,101,117,110,100,101,114,108,105,110,101,116,104,105,115,32, -116,105,109,101,116,101,108,101,112,104,111,110,101,105,116,101,109,115,99,111, -112,101,112,114,97,99,116,105,99,101,115,97,100,118,97,110,116,97,103,101,41,59, -114,101,116,117,114,110,32,70,111,114,32,111,116,104,101,114,112,114,111,118,105 -,100,105,110,103,100,101,109,111,99,114,97,99,121,98,111,116,104,32,116,104,101, -32,101,120,116,101,110,115,105,118,101,115,117,102,102,101,114,105,110,103,115, -117,112,112,111,114,116,101,100,99,111,109,112,117,116,101,114,115,32,102,117, -110,99,116,105,111,110,112,114,97,99,116,105,99,97,108,115,97,105,100,32,116,104 -,97,116,105,116,32,109,97,121,32,98,101,69,110,103,108,105,115,104,60,47,102,114 -,111,109,32,116,104,101,32,115,99,104,101,100,117,108,101,100,100,111,119,110, -108,111,97,100,115,60,47,108,97,98,101,108,62,10,115,117,115,112,101,99,116,101, -100,109,97,114,103,105,110,58,32,48,115,112,105,114,105,116,117,97,108,60,47,104 -,101,97,100,62,10,10,109,105,99,114,111,115,111,102,116,103,114,97,100,117,97, -108,108,121,100,105,115,99,117,115,115,101,100,104,101,32,98,101,99,97,109,101, -101,120,101,99,117,116,105,118,101,106,113,117,101,114,121,46,106,115,104,111, -117,115,101,104,111,108,100,99,111,110,102,105,114,109,101,100,112,117,114,99, -104,97,115,101,100,108,105,116,101,114,97,108,108,121,100,101,115,116,114,111, -121,101,100,117,112,32,116,111,32,116,104,101,118,97,114,105,97,116,105,111,110, -114,101,109,97,105,110,105,110,103,105,116,32,105,115,32,110,111,116,99,101,110, -116,117,114,105,101,115,74,97,112,97,110,101,115,101,32,97,109,111,110,103,32, -116,104,101,99,111,109,112,108,101,116,101,100,97,108,103,111,114,105,116,104, -109,105,110,116,101,114,101,115,116,115,114,101,98,101,108,108,105,111,110,117, -110,100,101,102,105,110,101,100,101,110,99,111,117,114,97,103,101,114,101,115, -105,122,97,98,108,101,105,110,118,111,108,118,105,110,103,115,101,110,115,105, -116,105,118,101,117,110,105,118,101,114,115,97,108,112,114,111,118,105,115,105, -111,110,40,97,108,116,104,111,117,103,104,102,101,97,116,117,114,105,110,103,99, -111,110,100,117,99,116,101,100,41,44,32,119,104,105,99,104,32,99,111,110,116,105 -,110,117,101,100,45,104,101,97,100,101,114,34,62,70,101,98,114,117,97,114,121,32 -,110,117,109,101,114,111,117,115,32,111,118,101,114,102,108,111,119,58,99,111, -109,112,111,110,101,110,116,102,114,97,103,109,101,110,116,115,101,120,99,101, -108,108,101,110,116,99,111,108,115,112,97,110,61,34,116,101,99,104,110,105,99,97 -,108,110,101,97,114,32,116,104,101,32,65,100,118,97,110,99,101,100,32,115,111, -117,114,99,101,32,111,102,101,120,112,114,101,115,115,101,100,72,111,110,103,32, -75,111,110,103,32,70,97,99,101,98,111,111,107,109,117,108,116,105,112,108,101,32 -,109,101,99,104,97,110,105,115,109,101,108,101,118,97,116,105,111,110,111,102, -102,101,110,115,105,118,101,60,47,102,111,114,109,62,10,9,115,112,111,110,115, -111,114,101,100,100,111,99,117,109,101,110,116,46,111,114,32,38,113,117,111,116, -59,116,104,101,114,101,32,97,114,101,116,104,111,115,101,32,119,104,111,109,111, -118,101,109,101,110,116,115,112,114,111,99,101,115,115,101,115,100,105,102,102, -105,99,117,108,116,115,117,98,109,105,116,116,101,100,114,101,99,111,109,109,101 -,110,100,99,111,110,118,105,110,99,101,100,112,114,111,109,111,116,105,110,103, -34,32,119,105,100,116,104,61,34,46,114,101,112,108,97,99,101,40,99,108,97,115, -115,105,99,97,108,99,111,97,108,105,116,105,111,110,104,105,115,32,102,105,114, -115,116,100,101,99,105,115,105,111,110,115,97,115,115,105,115,116,97,110,116,105 -,110,100,105,99,97,116,101,100,101,118,111,108,117,116,105,111,110,45,119,114,97 -,112,112,101,114,34,101,110,111,117,103,104,32,116,111,97,108,111,110,103,32,116 -,104,101,100,101,108,105,118,101,114,101,100,45,45,62,13,10,60,33,45,45,65,109, -101,114,105,99,97,110,32,112,114,111,116,101,99,116,101,100,78,111,118,101,109, -98,101,114,32,60,47,115,116,121,108,101,62,60,102,117,114,110,105,116,117,114, -101,73,110,116,101,114,110,101,116,32,32,111,110,98,108,117,114,61,34,115,117, -115,112,101,110,100,101,100,114,101,99,105,112,105,101,110,116,98,97,115,101,100 -,32,111,110,32,77,111,114,101,111,118,101,114,44,97,98,111,108,105,115,104,101, -100,99,111,108,108,101,99,116,101,100,119,101,114,101,32,109,97,100,101,101,109, -111,116,105,111,110,97,108,101,109,101,114,103,101,110,99,121,110,97,114,114,97, -116,105,118,101,97,100,118,111,99,97,116,101,115,112,120,59,98,111,114,100,101, -114,99,111,109,109,105,116,116,101,100,100,105,114,61,34,108,116,114,34,101,109, -112,108,111,121,101,101,115,114,101,115,101,97,114,99,104,46,32,115,101,108,101, -99,116,101,100,115,117,99,99,101,115,115,111,114,99,117,115,116,111,109,101,114, -115,100,105,115,112,108,97,121,101,100,83,101,112,116,101,109,98,101,114,97,100, -100,67,108,97,115,115,40,70,97,99,101,98,111,111,107,32,115,117,103,103,101,115, -116,101,100,97,110,100,32,108,97,116,101,114,111,112,101,114,97,116,105,110,103, -101,108,97,98,111,114,97,116,101,83,111,109,101,116,105,109,101,115,73,110,115, -116,105,116,117,116,101,99,101,114,116,97,105,110,108,121,105,110,115,116,97,108 -,108,101,100,102,111,108,108,111,119,101,114,115,74,101,114,117,115,97,108,101, -109,116,104,101,121,32,104,97,118,101,99,111,109,112,117,116,105,110,103,103,101 -,110,101,114,97,116,101,100,112,114,111,118,105,110,99,101,115,103,117,97,114,97 -,110,116,101,101,97,114,98,105,116,114,97,114,121,114,101,99,111,103,110,105,122 -,101,119,97,110,116,101,100,32,116,111,112,120,59,119,105,100,116,104,58,116,104 -,101,111,114,121,32,111,102,98,101,104,97,118,105,111,117,114,87,104,105,108,101 -,32,116,104,101,101,115,116,105,109,97,116,101,100,98,101,103,97,110,32,116,111, -32,105,116,32,98,101,99,97,109,101,109,97,103,110,105,116,117,100,101,109,117, -115,116,32,104,97,118,101,109,111,114,101,32,116,104,97,110,68,105,114,101,99, -116,111,114,121,101,120,116,101,110,115,105,111,110,115,101,99,114,101,116,97, -114,121,110,97,116,117,114,97,108,108,121,111,99,99,117,114,114,105,110,103,118, -97,114,105,97,98,108,101,115,103,105,118,101,110,32,116,104,101,112,108,97,116, -102,111,114,109,46,60,47,108,97,98,101,108,62,60,102,97,105,108,101,100,32,116, -111,99,111,109,112,111,117,110,100,115,107,105,110,100,115,32,111,102,32,115,111 -,99,105,101,116,105,101,115,97,108,111,110,103,115,105,100,101,32,45,45,38,103, -116,59,10,10,115,111,117,116,104,119,101,115,116,116,104,101,32,114,105,103,104, -116,114,97,100,105,97,116,105,111,110,109,97,121,32,104,97,118,101,32,117,110, -101,115,99,97,112,101,40,115,112,111,107,101,110,32,105,110,34,32,104,114,101, -102,61,34,47,112,114,111,103,114,97,109,109,101,111,110,108,121,32,116,104,101, -32,99,111,109,101,32,102,114,111,109,100,105,114,101,99,116,111,114,121,98,117, -114,105,101,100,32,105,110,97,32,115,105,109,105,108,97,114,116,104,101,121,32, -119,101,114,101,60,47,102,111,110,116,62,60,47,78,111,114,119,101,103,105,97,110 -,115,112,101,99,105,102,105,101,100,112,114,111,100,117,99,105,110,103,112,97, -115,115,101,110,103,101,114,40,110,101,119,32,68,97,116,101,116,101,109,112,111, -114,97,114,121,102,105,99,116,105,111,110,97,108,65,102,116,101,114,32,116,104, -101,101,113,117,97,116,105,111,110,115,100,111,119,110,108,111,97,100,46,114,101 -,103,117,108,97,114,108,121,100,101,118,101,108,111,112,101,114,97,98,111,118, -101,32,116,104,101,108,105,110,107,101,100,32,116,111,112,104,101,110,111,109, -101,110,97,112,101,114,105,111,100,32,111,102,116,111,111,108,116,105,112,34,62, -115,117,98,115,116,97,110,99,101,97,117,116,111,109,97,116,105,99,97,115,112,101 -,99,116,32,111,102,65,109,111,110,103,32,116,104,101,99,111,110,110,101,99,116, -101,100,101,115,116,105,109,97,116,101,115,65,105,114,32,70,111,114,99,101,115, -121,115,116,101,109,32,111,102,111,98,106,101,99,116,105,118,101,105,109,109,101 -,100,105,97,116,101,109,97,107,105,110,103,32,105,116,112,97,105,110,116,105,110 -,103,115,99,111,110,113,117,101,114,101,100,97,114,101,32,115,116,105,108,108, -112,114,111,99,101,100,117,114,101,103,114,111,119,116,104,32,111,102,104,101,97 -,100,101,100,32,98,121,69,117,114,111,112,101,97,110,32,100,105,118,105,115,105, -111,110,115,109,111,108,101,99,117,108,101,115,102,114,97,110,99,104,105,115,101 -,105,110,116,101,110,116,105,111,110,97,116,116,114,97,99,116,101,100,99,104,105 -,108,100,104,111,111,100,97,108,115,111,32,117,115,101,100,100,101,100,105,99,97 -,116,101,100,115,105,110,103,97,112,111,114,101,100,101,103,114,101,101,32,111, -102,102,97,116,104,101,114,32,111,102,99,111,110,102,108,105,99,116,115,60,47,97 -,62,60,47,112,62,10,99,97,109,101,32,102,114,111,109,119,101,114,101,32,117,115, -101,100,110,111,116,101,32,116,104,97,116,114,101,99,101,105,118,105,110,103,69, -120,101,99,117,116,105,118,101,101,118,101,110,32,109,111,114,101,97,99,99,101, -115,115,32,116,111,99,111,109,109,97,110,100,101,114,80,111,108,105,116,105,99, -97,108,109,117,115,105,99,105,97,110,115,100,101,108,105,99,105,111,117,115,112, -114,105,115,111,110,101,114,115,97,100,118,101,110,116,32,111,102,85,84,70,45,56 -,34,32,47,62,60,33,91,67,68,65,84,65,91,34,62,67,111,110,116,97,99,116,83,111, -117,116,104,101,114,110,32,98,103,99,111,108,111,114,61,34,115,101,114,105,101, -115,32,111,102,46,32,73,116,32,119,97,115,32,105,110,32,69,117,114,111,112,101, -112,101,114,109,105,116,116,101,100,118,97,108,105,100,97,116,101,46,97,112,112, -101,97,114,105,110,103,111,102,102,105,99,105,97,108,115,115,101,114,105,111,117 -,115,108,121,45,108,97,110,103,117,97,103,101,105,110,105,116,105,97,116,101,100 -,101,120,116,101,110,100,105,110,103,108,111,110,103,45,116,101,114,109,105,110, -102,108,97,116,105,111,110,115,117,99,104,32,116,104,97,116,103,101,116,67,111, -111,107,105,101,109,97,114,107,101,100,32,98,121,60,47,98,117,116,116,111,110,62 -,105,109,112,108,101,109,101,110,116,98,117,116,32,105,116,32,105,115,105,110,99 -,114,101,97,115,101,115,100,111,119,110,32,116,104,101,32,114,101,113,117,105, -114,105,110,103,100,101,112,101,110,100,101,110,116,45,45,62,10,60,33,45,45,32, -105,110,116,101,114,118,105,101,119,87,105,116,104,32,116,104,101,32,99,111,112, -105,101,115,32,111,102,99,111,110,115,101,110,115,117,115,119,97,115,32,98,117, -105,108,116,86,101,110,101,122,117,101,108,97,40,102,111,114,109,101,114,108,121 -,116,104,101,32,115,116,97,116,101,112,101,114,115,111,110,110,101,108,115,116, -114,97,116,101,103,105,99,102,97,118,111,117,114,32,111,102,105,110,118,101,110, -116,105,111,110,87,105,107,105,112,101,100,105,97,99,111,110,116,105,110,101,110 -,116,118,105,114,116,117,97,108,108,121,119,104,105,99,104,32,119,97,115,112,114 -,105,110,99,105,112,108,101,67,111,109,112,108,101,116,101,32,105,100,101,110, -116,105,99,97,108,115,104,111,119,32,116,104,97,116,112,114,105,109,105,116,105, -118,101,97,119,97,121,32,102,114,111,109,109,111,108,101,99,117,108,97,114,112, -114,101,99,105,115,101,108,121,100,105,115,115,111,108,118,101,100,85,110,100, -101,114,32,116,104,101,118,101,114,115,105,111,110,61,34,62,38,110,98,115,112,59 -,60,47,73,116,32,105,115,32,116,104,101,32,84,104,105,115,32,105,115,32,119,105, -108,108,32,104,97,118,101,111,114,103,97,110,105,115,109,115,115,111,109,101,32, -116,105,109,101,70,114,105,101,100,114,105,99,104,119,97,115,32,102,105,114,115, -116,116,104,101,32,111,110,108,121,32,102,97,99,116,32,116,104,97,116,102,111, -114,109,32,105,100,61,34,112,114,101,99,101,100,105,110,103,84,101,99,104,110, -105,99,97,108,112,104,121,115,105,99,105,115,116,111,99,99,117,114,115,32,105, -110,110,97,118,105,103,97,116,111,114,115,101,99,116,105,111,110,34,62,115,112, -97,110,32,105,100,61,34,115,111,117,103,104,116,32,116,111,98,101,108,111,119,32 -,116,104,101,115,117,114,118,105,118,105,110,103,125,60,47,115,116,121,108,101, -62,104,105,115,32,100,101,97,116,104,97,115,32,105,110,32,116,104,101,99,97,117, -115,101,100,32,98,121,112,97,114,116,105,97,108,108,121,101,120,105,115,116,105, -110,103,32,117,115,105,110,103,32,116,104,101,119,97,115,32,103,105,118,101,110, -97,32,108,105,115,116,32,111,102,108,101,118,101,108,115,32,111,102,110,111,116, -105,111,110,32,111,102,79,102,102,105,99,105,97,108,32,100,105,115,109,105,115, -115,101,100,115,99,105,101,110,116,105,115,116,114,101,115,101,109,98,108,101, -115,100,117,112,108,105,99,97,116,101,101,120,112,108,111,115,105,118,101,114, -101,99,111,118,101,114,101,100,97,108,108,32,111,116,104,101,114,103,97,108,108, -101,114,105,101,115,123,112,97,100,100,105,110,103,58,112,101,111,112,108,101,32 -,111,102,114,101,103,105,111,110,32,111,102,97,100,100,114,101,115,115,101,115, -97,115,115,111,99,105,97,116,101,105,109,103,32,97,108,116,61,34,105,110,32,109, -111,100,101,114,110,115,104,111,117,108,100,32,98,101,109,101,116,104,111,100,32 -,111,102,114,101,112,111,114,116,105,110,103,116,105,109,101,115,116,97,109,112, -110,101,101,100,101,100,32,116,111,116,104,101,32,71,114,101,97,116,114,101,103, -97,114,100,105,110,103,115,101,101,109,101,100,32,116,111,118,105,101,119,101, -100,32,97,115,105,109,112,97,99,116,32,111,110,105,100,101,97,32,116,104,97,116, -116,104,101,32,87,111,114,108,100,104,101,105,103,104,116,32,111,102,101,120,112 -,97,110,100,105,110,103,84,104,101,115,101,32,97,114,101,99,117,114,114,101,110, -116,34,62,99,97,114,101,102,117,108,108,121,109,97,105,110,116,97,105,110,115,99 -,104,97,114,103,101,32,111,102,67,108,97,115,115,105,99,97,108,97,100,100,114, -101,115,115,101,100,112,114,101,100,105,99,116,101,100,111,119,110,101,114,115, -104,105,112,60,100,105,118,32,105,100,61,34,114,105,103,104,116,34,62,13,10,114, -101,115,105,100,101,110,99,101,108,101,97,118,101,32,116,104,101,99,111,110,116, -101,110,116,34,62,97,114,101,32,111,102,116,101,110,32,32,125,41,40,41,59,13,10, -112,114,111,98,97,98,108,121,32,80,114,111,102,101,115,115,111,114,45,98,117,116 -,116,111,110,34,32,114,101,115,112,111,110,100,101,100,115,97,121,115,32,116,104 -,97,116,104,97,100,32,116,111,32,98,101,112,108,97,99,101,100,32,105,110,72,117, -110,103,97,114,105,97,110,115,116,97,116,117,115,32,111,102,115,101,114,118,101, -115,32,97,115,85,110,105,118,101,114,115,97,108,101,120,101,99,117,116,105,111, -110,97,103,103,114,101,103,97,116,101,102,111,114,32,119,104,105,99,104,105,110, -102,101,99,116,105,111,110,97,103,114,101,101,100,32,116,111,104,111,119,101,118 -,101,114,44,32,112,111,112,117,108,97,114,34,62,112,108,97,99,101,100,32,111,110 -,99,111,110,115,116,114,117,99,116,101,108,101,99,116,111,114,97,108,115,121,109 -,98,111,108,32,111,102,105,110,99,108,117,100,105,110,103,114,101,116,117,114, -110,32,116,111,97,114,99,104,105,116,101,99,116,67,104,114,105,115,116,105,97, -110,112,114,101,118,105,111,117,115,32,108,105,118,105,110,103,32,105,110,101,97 -,115,105,101,114,32,116,111,112,114,111,102,101,115,115,111,114,10,38,108,116,59 -,33,45,45,32,101,102,102,101,99,116,32,111,102,97,110,97,108,121,116,105,99,115, -119,97,115,32,116,97,107,101,110,119,104,101,114,101,32,116,104,101,116,111,111, -107,32,111,118,101,114,98,101,108,105,101,102,32,105,110,65,102,114,105,107,97, -97,110,115,97,115,32,102,97,114,32,97,115,112,114,101,118,101,110,116,101,100, -119,111,114,107,32,119,105,116,104,97,32,115,112,101,99,105,97,108,60,102,105, -101,108,100,115,101,116,67,104,114,105,115,116,109,97,115,82,101,116,114,105,101 -,118,101,100,10,10,73,110,32,116,104,101,32,98,97,99,107,32,105,110,116,111,110, -111,114,116,104,101,97,115,116,109,97,103,97,122,105,110,101,115,62,60,115,116, -114,111,110,103,62,99,111,109,109,105,116,116,101,101,103,111,118,101,114,110, -105,110,103,103,114,111,117,112,115,32,111,102,115,116,111,114,101,100,32,105, -110,101,115,116,97,98,108,105,115,104,97,32,103,101,110,101,114,97,108,105,116, -115,32,102,105,114,115,116,116,104,101,105,114,32,111,119,110,112,111,112,117, -108,97,116,101,100,97,110,32,111,98,106,101,99,116,67,97,114,105,98,98,101,97, -110,97,108,108,111,119,32,116,104,101,100,105,115,116,114,105,99,116,115,119,105 -,115,99,111,110,115,105,110,108,111,99,97,116,105,111,110,46,59,32,119,105,100, -116,104,58,32,105,110,104,97,98,105,116,101,100,83,111,99,105,97,108,105,115,116 -,74,97,110,117,97,114,121,32,49,60,47,102,111,111,116,101,114,62,115,105,109,105 -,108,97,114,108,121,99,104,111,105,99,101,32,111,102,116,104,101,32,115,97,109, -101,32,115,112,101,99,105,102,105,99,32,98,117,115,105,110,101,115,115,32,84,104 -,101,32,102,105,114,115,116,46,108,101,110,103,116,104,59,32,100,101,115,105,114 -,101,32,116,111,100,101,97,108,32,119,105,116,104,115,105,110,99,101,32,116,104, -101,117,115,101,114,65,103,101,110,116,99,111,110,99,101,105,118,101,100,105,110 -,100,101,120,46,112,104,112,97,115,32,38,113,117,111,116,59,101,110,103,97,103, -101,32,105,110,114,101,99,101,110,116,108,121,44,102,101,119,32,121,101,97,114, -115,119,101,114,101,32,97,108,115,111,10,60,104,101,97,100,62,10,60,101,100,105, -116,101,100,32,98,121,97,114,101,32,107,110,111,119,110,99,105,116,105,101,115, -32,105,110,97,99,99,101,115,115,107,101,121,99,111,110,100,101,109,110,101,100, -97,108,115,111,32,104,97,118,101,115,101,114,118,105,99,101,115,44,102,97,109, -105,108,121,32,111,102,83,99,104,111,111,108,32,111,102,99,111,110,118,101,114, -116,101,100,110,97,116,117,114,101,32,111,102,32,108,97,110,103,117,97,103,101, -109,105,110,105,115,116,101,114,115,60,47,111,98,106,101,99,116,62,116,104,101, -114,101,32,105,115,32,97,32,112,111,112,117,108,97,114,115,101,113,117,101,110, -99,101,115,97,100,118,111,99,97,116,101,100,84,104,101,121,32,119,101,114,101,97 -,110,121,32,111,116,104,101,114,108,111,99,97,116,105,111,110,61,101,110,116,101 -,114,32,116,104,101,109,117,99,104,32,109,111,114,101,114,101,102,108,101,99,116 -,101,100,119,97,115,32,110,97,109,101,100,111,114,105,103,105,110,97,108,32,97, -32,116,121,112,105,99,97,108,119,104,101,110,32,116,104,101,121,101,110,103,105, -110,101,101,114,115,99,111,117,108,100,32,110,111,116,114,101,115,105,100,101, -110,116,115,119,101,100,110,101,115,100,97,121,116,104,101,32,116,104,105,114, -100,32,112,114,111,100,117,99,116,115,74,97,110,117,97,114,121,32,50,119,104,97, -116,32,116,104,101,121,97,32,99,101,114,116,97,105,110,114,101,97,99,116,105,111 -,110,115,112,114,111,99,101,115,115,111,114,97,102,116,101,114,32,104,105,115, -116,104,101,32,108,97,115,116,32,99,111,110,116,97,105,110,101,100,34,62,60,47, -100,105,118,62,10,60,47,97,62,60,47,116,100,62,100,101,112,101,110,100,32,111, -110,115,101,97,114,99,104,34,62,10,112,105,101,99,101,115,32,111,102,99,111,109, -112,101,116,105,110,103,82,101,102,101,114,101,110,99,101,116,101,110,110,101, -115,115,101,101,119,104,105,99,104,32,104,97,115,32,118,101,114,115,105,111,110, -61,60,47,115,112,97,110,62,32,60,60,47,104,101,97,100,101,114,62,103,105,118,101 -,115,32,116,104,101,104,105,115,116,111,114,105,97,110,118,97,108,117,101,61,34, -34,62,112,97,100,100,105,110,103,58,48,118,105,101,119,32,116,104,97,116,116,111 -,103,101,116,104,101,114,44,116,104,101,32,109,111,115,116,32,119,97,115,32,102, -111,117,110,100,115,117,98,115,101,116,32,111,102,97,116,116,97,99,107,32,111, -110,99,104,105,108,100,114,101,110,44,112,111,105,110,116,115,32,111,102,112,101 -,114,115,111,110,97,108,32,112,111,115,105,116,105,111,110,58,97,108,108,101,103 -,101,100,108,121,67,108,101,118,101,108,97,110,100,119,97,115,32,108,97,116,101, -114,97,110,100,32,97,102,116,101,114,97,114,101,32,103,105,118,101,110,119,97, -115,32,115,116,105,108,108,115,99,114,111,108,108,105,110,103,100,101,115,105, -103,110,32,111,102,109,97,107,101,115,32,116,104,101,109,117,99,104,32,108,101, -115,115,65,109,101,114,105,99,97,110,115,46,10,10,65,102,116,101,114,32,44,32,98 -,117,116,32,116,104,101,77,117,115,101,117,109,32,111,102,108,111,117,105,115, -105,97,110,97,40,102,114,111,109,32,116,104,101,109,105,110,110,101,115,111,116, -97,112,97,114,116,105,99,108,101,115,97,32,112,114,111,99,101,115,115,68,111,109 -,105,110,105,99,97,110,118,111,108,117,109,101,32,111,102,114,101,116,117,114, -110,105,110,103,100,101,102,101,110,115,105,118,101,48,48,112,120,124,114,105, -103,104,109,97,100,101,32,102,114,111,109,109,111,117,115,101,111,118,101,114,34 -,32,115,116,121,108,101,61,34,115,116,97,116,101,115,32,111,102,40,119,104,105, -99,104,32,105,115,99,111,110,116,105,110,117,101,115,70,114,97,110,99,105,115,99 -,111,98,117,105,108,100,105,110,103,32,119,105,116,104,111,117,116,32,97,119,105 -,116,104,32,115,111,109,101,119,104,111,32,119,111,117,108,100,97,32,102,111,114 -,109,32,111,102,97,32,112,97,114,116,32,111,102,98,101,102,111,114,101,32,105, -116,107,110,111,119,110,32,97,115,32,32,83,101,114,118,105,99,101,115,108,111,99 -,97,116,105,111,110,32,97,110,100,32,111,102,116,101,110,109,101,97,115,117,114, -105,110,103,97,110,100,32,105,116,32,105,115,112,97,112,101,114,98,97,99,107,118 -,97,108,117,101,115,32,111,102,13,10,60,116,105,116,108,101,62,61,32,119,105,110 -,100,111,119,46,100,101,116,101,114,109,105,110,101,101,114,38,113,117,111,116, -59,32,112,108,97,121,101,100,32,98,121,97,110,100,32,101,97,114,108,121,60,47,99 -,101,110,116,101,114,62,102,114,111,109,32,116,104,105,115,116,104,101,32,116, -104,114,101,101,112,111,119,101,114,32,97,110,100,111,102,32,38,113,117,111,116, -59,105,110,110,101,114,72,84,77,76,60,97,32,104,114,101,102,61,34,121,58,105,110 -,108,105,110,101,59,67,104,117,114,99,104,32,111,102,116,104,101,32,101,118,101, -110,116,118,101,114,121,32,104,105,103,104,111,102,102,105,99,105,97,108,32,45, -104,101,105,103,104,116,58,32,99,111,110,116,101,110,116,61,34,47,99,103,105,45, -98,105,110,47,116,111,32,99,114,101,97,116,101,97,102,114,105,107,97,97,110,115, -101,115,112,101,114,97,110,116,111,102,114,97,110,195,167,97,105,115,108,97,116, -118,105,101,197,161,117,108,105,101,116,117,118,105,197,179,196,140,101,197,161, -116,105,110,97,196,141,101,197,161,116,105,110,97,224,185,132,224,184,151,224, -184,162,230,151,165,230,156,172,232,170,158,231,174,128,228,189,147,229,173,151, -231,185,129,233,171,148,229,173,151,237,149,156,234,181,173,236,150,180,228,184, -186,228,187,128,228,185,136,232,174,161,231,174,151,230,156,186,231,172,148,232, -174,176,230,156,172,232,168,142,232,171,150,229,141,128,230,156,141,229,138,161, -229,153,168,228,186,146,232,129,148,231,189,145,230,136,191,229,156,176,228,186, -167,228,191,177,228,185,144,233,131,168,229,135,186,231,137,136,231,164,190,230, -142,146,232,161,140,230,166,156,233,131,168,232,144,189,230,160,188,232,191,155, -228,184,128,230,173,165,230,148,175,228,187,152,229,174,157,233,170,140,232,175, -129,231,160,129,229,167,148,229,145,152,228,188,154,230,149,176,230,141,174,229, -186,147,230,182,136,232,180,185,232,128,133,229,138,158,229,133,172,229,174,164, -232,174,168,232,174,186,229,140,186,230,183,177,229,156,179,229,184,130,230,146, -173,230,148,190,229,153,168,229,140,151,228,186,172,229,184,130,229,164,167,229, -173,166,231,148,159,232,182,138,230,157,165,232,182,138,231,174,161,231,144,134, -229,145,152,228,191,161,230,129,175,231,189,145,115,101,114,118,105,99,105,111, -115,97,114,116,195,173,99,117,108,111,97,114,103,101,110,116,105,110,97,98,97, -114,99,101,108,111,110,97,99,117,97,108,113,117,105,101,114,112,117,98,108,105, -99,97,100,111,112,114,111,100,117,99,116,111,115,112,111,108,195,173,116,105,99, -97,114,101,115,112,117,101,115,116,97,119,105,107,105,112,101,100,105,97,115,105 -,103,117,105,101,110,116,101,98,195,186,115,113,117,101,100,97,99,111,109,117, -110,105,100,97,100,115,101,103,117,114,105,100,97,100,112,114,105,110,99,105,112 -,97,108,112,114,101,103,117,110,116,97,115,99,111,110,116,101,110,105,100,111, -114,101,115,112,111,110,100,101,114,118,101,110,101,122,117,101,108,97,112,114, -111,98,108,101,109,97,115,100,105,99,105,101,109,98,114,101,114,101,108,97,99, -105,195,179,110,110,111,118,105,101,109,98,114,101,115,105,109,105,108,97,114, -101,115,112,114,111,121,101,99,116,111,115,112,114,111,103,114,97,109,97,115,105 -,110,115,116,105,116,117,116,111,97,99,116,105,118,105,100,97,100,101,110,99,117 -,101,110,116,114,97,101,99,111,110,111,109,195,173,97,105,109,195,161,103,101, -110,101,115,99,111,110,116,97,99,116,97,114,100,101,115,99,97,114,103,97,114,110 -,101,99,101,115,97,114,105,111,97,116,101,110,99,105,195,179,110,116,101,108,195 -,169,102,111,110,111,99,111,109,105,115,105,195,179,110,99,97,110,99,105,111,110 -,101,115,99,97,112,97,99,105,100,97,100,101,110,99,111,110,116,114,97,114,97,110 -,195,161,108,105,115,105,115,102,97,118,111,114,105,116,111,115,116,195,169,114, -109,105,110,111,115,112,114,111,118,105,110,99,105,97,101,116,105,113,117,101, -116,97,115,101,108,101,109,101,110,116,111,115,102,117,110,99,105,111,110,101, -115,114,101,115,117,108,116,97,100,111,99,97,114,195,161,99,116,101,114,112,114, -111,112,105,101,100,97,100,112,114,105,110,99,105,112,105,111,110,101,99,101,115 -,105,100,97,100,109,117,110,105,99,105,112,97,108,99,114,101,97,99,105,195,179, -110,100,101,115,99,97,114,103,97,115,112,114,101,115,101,110,99,105,97,99,111, -109,101,114,99,105,97,108,111,112,105,110,105,111,110,101,115,101,106,101,114,99 -,105,99,105,111,101,100,105,116,111,114,105,97,108,115,97,108,97,109,97,110,99, -97,103,111,110,122,195,161,108,101,122,100,111,99,117,109,101,110,116,111,112, -101,108,195,173,99,117,108,97,114,101,99,105,101,110,116,101,115,103,101,110,101 -,114,97,108,101,115,116,97,114,114,97,103,111,110,97,112,114,195,161,99,116,105, -99,97,110,111,118,101,100,97,100,101,115,112,114,111,112,117,101,115,116,97,112, -97,99,105,101,110,116,101,115,116,195,169,99,110,105,99,97,115,111,98,106,101, -116,105,118,111,115,99,111,110,116,97,99,116,111,115,224,164,174,224,165,135,224 -,164,130,224,164,178,224,164,191,224,164,143,224,164,185,224,165,136,224,164,130 -,224,164,151,224,164,175,224,164,190,224,164,184,224,164,190,224,164,165,224,164 -,143,224,164,181,224,164,130,224,164,176,224,164,185,224,165,135,224,164,149,224 -,165,139,224,164,136,224,164,149,224,165,129,224,164,155,224,164,176,224,164,185 -,224,164,190,224,164,172,224,164,190,224,164,166,224,164,149,224,164,185,224,164 -,190,224,164,184,224,164,173,224,165,128,224,164,185,224,165,129,224,164,143,224 -,164,176,224,164,185,224,165,128,224,164,174,224,165,136,224,164,130,224,164,166 -,224,164,191,224,164,168,224,164,172,224,164,190,224,164,164,100,105,112,108,111 -,100,111,99,115,224,164,184,224,164,174,224,164,175,224,164,176,224,165,130,224, -164,170,224,164,168,224,164,190,224,164,174,224,164,170,224,164,164,224,164,190, -224,164,171,224,164,191,224,164,176,224,164,148,224,164,184,224,164,164,224,164, -164,224,164,176,224,164,185,224,164,178,224,165,139,224,164,151,224,164,185,224, -165,129,224,164,134,224,164,172,224,164,190,224,164,176,224,164,166,224,165,135, -224,164,182,224,164,185,224,165,129,224,164,136,224,164,150,224,165,135,224,164, -178,224,164,175,224,164,166,224,164,191,224,164,149,224,164,190,224,164,174,224, -164,181,224,165,135,224,164,172,224,164,164,224,165,128,224,164,168,224,164,172, -224,165,128,224,164,154,224,164,174,224,165,140,224,164,164,224,164,184,224,164, -190,224,164,178,224,164,178,224,165,135,224,164,150,224,164,156,224,165,137,224, -164,172,224,164,174,224,164,166,224,164,166,224,164,164,224,164,165,224,164,190, -224,164,168,224,164,185,224,165,128,224,164,182,224,164,185,224,164,176,224,164, -133,224,164,178,224,164,151,224,164,149,224,164,173,224,165,128,224,164,168,224, -164,151,224,164,176,224,164,170,224,164,190,224,164,184,224,164,176,224,164,190, -224,164,164,224,164,149,224,164,191,224,164,143,224,164,137,224,164,184,224,165, -135,224,164,151,224,164,175,224,165,128,224,164,185,224,165,130,224,164,129,224, -164,134,224,164,151,224,165,135,224,164,159,224,165,128,224,164,174,224,164,150, -224,165,139,224,164,156,224,164,149,224,164,190,224,164,176,224,164,133,224,164, -173,224,165,128,224,164,151,224,164,175,224,165,135,224,164,164,224,165,129,224, -164,174,224,164,181,224,165,139,224,164,159,224,164,166,224,165,135,224,164,130, -224,164,133,224,164,151,224,164,176,224,164,144,224,164,184,224,165,135,224,164, -174,224,165,135,224,164,178,224,164,178,224,164,151,224,164,190,224,164,185,224, -164,190,224,164,178,224,164,138,224,164,170,224,164,176,224,164,154,224,164,190, -224,164,176,224,164,144,224,164,184,224,164,190,224,164,166,224,165,135,224,164, -176,224,164,156,224,164,191,224,164,184,224,164,166,224,164,191,224,164,178,224, -164,172,224,164,130,224,164,166,224,164,172,224,164,168,224,164,190,224,164,185, -224,165,130,224,164,130,224,164,178,224,164,190,224,164,150,224,164,156,224,165, -128,224,164,164,224,164,172,224,164,159,224,164,168,224,164,174,224,164,191,224, -164,178,224,164,135,224,164,184,224,165,135,224,164,134,224,164,168,224,165,135, -224,164,168,224,164,175,224,164,190,224,164,149,224,165,129,224,164,178,224,164, -178,224,165,137,224,164,151,224,164,173,224,164,190,224,164,151,224,164,176,224, -165,135,224,164,178,224,164,156,224,164,151,224,164,185,224,164,176,224,164,190, -224,164,174,224,164,178,224,164,151,224,165,135,224,164,170,224,165,135,224,164, -156,224,164,185,224,164,190,224,164,165,224,164,135,224,164,184,224,165,128,224, -164,184,224,164,185,224,165,128,224,164,149,224,164,178,224,164,190,224,164,160, -224,165,128,224,164,149,224,164,185,224,164,190,224,164,129,224,164,166,224,165, -130,224,164,176,224,164,164,224,164,185,224,164,164,224,164,184,224,164,190,224, -164,164,224,164,175,224,164,190,224,164,166,224,164,134,224,164,175,224,164,190, -224,164,170,224,164,190,224,164,149,224,164,149,224,165,140,224,164,168,224,164, -182,224,164,190,224,164,174,224,164,166,224,165,135,224,164,150,224,164,175,224, -164,185,224,165,128,224,164,176,224,164,190,224,164,175,224,164,150,224,165,129, -224,164,166,224,164,178,224,164,151,224,165,128,99,97,116,101,103,111,114,105, -101,115,101,120,112,101,114,105,101,110,99,101,60,47,116,105,116,108,101,62,13, -10,67,111,112,121,114,105,103,104,116,32,106,97,118,97,115,99,114,105,112,116,99 -,111,110,100,105,116,105,111,110,115,101,118,101,114,121,116,104,105,110,103,60, -112,32,99,108,97,115,115,61,34,116,101,99,104,110,111,108,111,103,121,98,97,99, -107,103,114,111,117,110,100,60,97,32,99,108,97,115,115,61,34,109,97,110,97,103, -101,109,101,110,116,38,99,111,112,121,59,32,50,48,49,106,97,118,97,83,99,114,105 -,112,116,99,104,97,114,97,99,116,101,114,115,98,114,101,97,100,99,114,117,109,98 -,116,104,101,109,115,101,108,118,101,115,104,111,114,105,122,111,110,116,97,108, -103,111,118,101,114,110,109,101,110,116,67,97,108,105,102,111,114,110,105,97,97, -99,116,105,118,105,116,105,101,115,100,105,115,99,111,118,101,114,101,100,78,97, -118,105,103,97,116,105,111,110,116,114,97,110,115,105,116,105,111,110,99,111,110 -,110,101,99,116,105,111,110,110,97,118,105,103,97,116,105,111,110,97,112,112,101 -,97,114,97,110,99,101,60,47,116,105,116,108,101,62,60,109,99,104,101,99,107,98, -111,120,34,32,116,101,99,104,110,105,113,117,101,115,112,114,111,116,101,99,116, -105,111,110,97,112,112,97,114,101,110,116,108,121,97,115,32,119,101,108,108,32, -97,115,117,110,116,39,44,32,39,85,65,45,114,101,115,111,108,117,116,105,111,110, -111,112,101,114,97,116,105,111,110,115,116,101,108,101,118,105,115,105,111,110, -116,114,97,110,115,108,97,116,101,100,87,97,115,104,105,110,103,116,111,110,110, -97,118,105,103,97,116,111,114,46,32,61,32,119,105,110,100,111,119,46,105,109,112 -,114,101,115,115,105,111,110,38,108,116,59,98,114,38,103,116,59,108,105,116,101, -114,97,116,117,114,101,112,111,112,117,108,97,116,105,111,110,98,103,99,111,108, -111,114,61,34,35,101,115,112,101,99,105,97,108,108,121,32,99,111,110,116,101,110 -,116,61,34,112,114,111,100,117,99,116,105,111,110,110,101,119,115,108,101,116, -116,101,114,112,114,111,112,101,114,116,105,101,115,100,101,102,105,110,105,116, -105,111,110,108,101,97,100,101,114,115,104,105,112,84,101,99,104,110,111,108,111 -,103,121,80,97,114,108,105,97,109,101,110,116,99,111,109,112,97,114,105,115,111, -110,117,108,32,99,108,97,115,115,61,34,46,105,110,100,101,120,79,102,40,34,99, -111,110,99,108,117,115,105,111,110,100,105,115,99,117,115,115,105,111,110,99,111 -,109,112,111,110,101,110,116,115,98,105,111,108,111,103,105,99,97,108,82,101,118 -,111,108,117,116,105,111,110,95,99,111,110,116,97,105,110,101,114,117,110,100, -101,114,115,116,111,111,100,110,111,115,99,114,105,112,116,62,60,112,101,114,109 -,105,115,115,105,111,110,101,97,99,104,32,111,116,104,101,114,97,116,109,111,115 -,112,104,101,114,101,32,111,110,102,111,99,117,115,61,34,60,102,111,114,109,32, -105,100,61,34,112,114,111,99,101,115,115,105,110,103,116,104,105,115,46,118,97, -108,117,101,103,101,110,101,114,97,116,105,111,110,67,111,110,102,101,114,101, -110,99,101,115,117,98,115,101,113,117,101,110,116,119,101,108,108,45,107,110,111 -,119,110,118,97,114,105,97,116,105,111,110,115,114,101,112,117,116,97,116,105, -111,110,112,104,101,110,111,109,101,110,111,110,100,105,115,99,105,112,108,105, -110,101,108,111,103,111,46,112,110,103,34,32,40,100,111,99,117,109,101,110,116, -44,98,111,117,110,100,97,114,105,101,115,101,120,112,114,101,115,115,105,111,110 -,115,101,116,116,108,101,109,101,110,116,66,97,99,107,103,114,111,117,110,100, -111,117,116,32,111,102,32,116,104,101,101,110,116,101,114,112,114,105,115,101,40 -,34,104,116,116,112,115,58,34,32,117,110,101,115,99,97,112,101,40,34,112,97,115, -115,119,111,114,100,34,32,100,101,109,111,99,114,97,116,105,99,60,97,32,104,114, -101,102,61,34,47,119,114,97,112,112,101,114,34,62,10,109,101,109,98,101,114,115, -104,105,112,108,105,110,103,117,105,115,116,105,99,112,120,59,112,97,100,100,105 -,110,103,112,104,105,108,111,115,111,112,104,121,97,115,115,105,115,116,97,110, -99,101,117,110,105,118,101,114,115,105,116,121,102,97,99,105,108,105,116,105,101 -,115,114,101,99,111,103,110,105,122,101,100,112,114,101,102,101,114,101,110,99, -101,105,102,32,40,116,121,112,101,111,102,109,97,105,110,116,97,105,110,101,100, -118,111,99,97,98,117,108,97,114,121,104,121,112,111,116,104,101,115,105,115,46, -115,117,98,109,105,116,40,41,59,38,97,109,112,59,110,98,115,112,59,97,110,110, -111,116,97,116,105,111,110,98,101,104,105,110,100,32,116,104,101,70,111,117,110, -100,97,116,105,111,110,112,117,98,108,105,115,104,101,114,34,97,115,115,117,109, -112,116,105,111,110,105,110,116,114,111,100,117,99,101,100,99,111,114,114,117, -112,116,105,111,110,115,99,105,101,110,116,105,115,116,115,101,120,112,108,105, -99,105,116,108,121,105,110,115,116,101,97,100,32,111,102,100,105,109,101,110,115 -,105,111,110,115,32,111,110,67,108,105,99,107,61,34,99,111,110,115,105,100,101, -114,101,100,100,101,112,97,114,116,109,101,110,116,111,99,99,117,112,97,116,105, -111,110,115,111,111,110,32,97,102,116,101,114,105,110,118,101,115,116,109,101, -110,116,112,114,111,110,111,117,110,99,101,100,105,100,101,110,116,105,102,105, -101,100,101,120,112,101,114,105,109,101,110,116,77,97,110,97,103,101,109,101,110 -,116,103,101,111,103,114,97,112,104,105,99,34,32,104,101,105,103,104,116,61,34, -108,105,110,107,32,114,101,108,61,34,46,114,101,112,108,97,99,101,40,47,100,101, -112,114,101,115,115,105,111,110,99,111,110,102,101,114,101,110,99,101,112,117, -110,105,115,104,109,101,110,116,101,108,105,109,105,110,97,116,101,100,114,101, -115,105,115,116,97,110,99,101,97,100,97,112,116,97,116,105,111,110,111,112,112, -111,115,105,116,105,111,110,119,101,108,108,32,107,110,111,119,110,115,117,112, -112,108,101,109,101,110,116,100,101,116,101,114,109,105,110,101,100,104,49,32,99 -,108,97,115,115,61,34,48,112,120,59,109,97,114,103,105,110,109,101,99,104,97,110 -,105,99,97,108,115,116,97,116,105,115,116,105,99,115,99,101,108,101,98,114,97, -116,101,100,71,111,118,101,114,110,109,101,110,116,10,10,68,117,114,105,110,103, -32,116,100,101,118,101,108,111,112,101,114,115,97,114,116,105,102,105,99,105,97, -108,101,113,117,105,118,97,108,101,110,116,111,114,105,103,105,110,97,116,101, -100,67,111,109,109,105,115,115,105,111,110,97,116,116,97,99,104,109,101,110,116, -60,115,112,97,110,32,105,100,61,34,116,104,101,114,101,32,119,101,114,101,78,101 -,100,101,114,108,97,110,100,115,98,101,121,111,110,100,32,116,104,101,114,101, -103,105,115,116,101,114,101,100,106,111,117,114,110,97,108,105,115,116,102,114, -101,113,117,101,110,116,108,121,97,108,108,32,111,102,32,116,104,101,108,97,110, -103,61,34,101,110,34,32,60,47,115,116,121,108,101,62,13,10,97,98,115,111,108,117 -,116,101,59,32,115,117,112,112,111,114,116,105,110,103,101,120,116,114,101,109, -101,108,121,32,109,97,105,110,115,116,114,101,97,109,60,47,115,116,114,111,110, -103,62,32,112,111,112,117,108,97,114,105,116,121,101,109,112,108,111,121,109,101 -,110,116,60,47,116,97,98,108,101,62,13,10,32,99,111,108,115,112,97,110,61,34,60, -47,102,111,114,109,62,10,32,32,99,111,110,118,101,114,115,105,111,110,97,98,111, -117,116,32,116,104,101,32,60,47,112,62,60,47,100,105,118,62,105,110,116,101,103, -114,97,116,101,100,34,32,108,97,110,103,61,34,101,110,80,111,114,116,117,103,117 -,101,115,101,115,117,98,115,116,105,116,117,116,101,105,110,100,105,118,105,100, -117,97,108,105,109,112,111,115,115,105,98,108,101,109,117,108,116,105,109,101, -100,105,97,97,108,109,111,115,116,32,97,108,108,112,120,32,115,111,108,105,100, -32,35,97,112,97,114,116,32,102,114,111,109,115,117,98,106,101,99,116,32,116,111, -105,110,32,69,110,103,108,105,115,104,99,114,105,116,105,99,105,122,101,100,101, -120,99,101,112,116,32,102,111,114,103,117,105,100,101,108,105,110,101,115,111, -114,105,103,105,110,97,108,108,121,114,101,109,97,114,107,97,98,108,101,116,104, -101,32,115,101,99,111,110,100,104,50,32,99,108,97,115,115,61,34,60,97,32,116,105 -,116,108,101,61,34,40,105,110,99,108,117,100,105,110,103,112,97,114,97,109,101, -116,101,114,115,112,114,111,104,105,98,105,116,101,100,61,32,34,104,116,116,112, -58,47,47,100,105,99,116,105,111,110,97,114,121,112,101,114,99,101,112,116,105, -111,110,114,101,118,111,108,117,116,105,111,110,102,111,117,110,100,97,116,105, -111,110,112,120,59,104,101,105,103,104,116,58,115,117,99,99,101,115,115,102,117, -108,115,117,112,112,111,114,116,101,114,115,109,105,108,108,101,110,110,105,117, -109,104,105,115,32,102,97,116,104,101,114,116,104,101,32,38,113,117,111,116,59, -110,111,45,114,101,112,101,97,116,59,99,111,109,109,101,114,99,105,97,108,105, -110,100,117,115,116,114,105,97,108,101,110,99,111,117,114,97,103,101,100,97,109, -111,117,110,116,32,111,102,32,117,110,111,102,102,105,99,105,97,108,101,102,102, -105,99,105,101,110,99,121,82,101,102,101,114,101,110,99,101,115,99,111,111,114, -100,105,110,97,116,101,100,105,115,99,108,97,105,109,101,114,101,120,112,101,100 -,105,116,105,111,110,100,101,118,101,108,111,112,105,110,103,99,97,108,99,117, -108,97,116,101,100,115,105,109,112,108,105,102,105,101,100,108,101,103,105,116, -105,109,97,116,101,115,117,98,115,116,114,105,110,103,40,48,34,32,99,108,97,115, -115,61,34,99,111,109,112,108,101,116,101,108,121,105,108,108,117,115,116,114,97, -116,101,102,105,118,101,32,121,101,97,114,115,105,110,115,116,114,117,109,101, -110,116,80,117,98,108,105,115,104,105,110,103,49,34,32,99,108,97,115,115,61,34, -112,115,121,99,104,111,108,111,103,121,99,111,110,102,105,100,101,110,99,101,110 -,117,109,98,101,114,32,111,102,32,97,98,115,101,110,99,101,32,111,102,102,111,99 -,117,115,101,100,32,111,110,106,111,105,110,101,100,32,116,104,101,115,116,114, -117,99,116,117,114,101,115,112,114,101,118,105,111,117,115,108,121,62,60,47,105, -102,114,97,109,101,62,111,110,99,101,32,97,103,97,105,110,98,117,116,32,114,97, -116,104,101,114,105,109,109,105,103,114,97,110,116,115,111,102,32,99,111,117,114 -,115,101,44,97,32,103,114,111,117,112,32,111,102,76,105,116,101,114,97,116,117, -114,101,85,110,108,105,107,101,32,116,104,101,60,47,97,62,38,110,98,115,112,59, -10,102,117,110,99,116,105,111,110,32,105,116,32,119,97,115,32,116,104,101,67,111 -,110,118,101,110,116,105,111,110,97,117,116,111,109,111,98,105,108,101,80,114, -111,116,101,115,116,97,110,116,97,103,103,114,101,115,115,105,118,101,97,102,116 -,101,114,32,116,104,101,32,83,105,109,105,108,97,114,108,121,44,34,32,47,62,60, -47,100,105,118,62,99,111,108,108,101,99,116,105,111,110,13,10,102,117,110,99,116 -,105,111,110,118,105,115,105,98,105,108,105,116,121,116,104,101,32,117,115,101, -32,111,102,118,111,108,117,110,116,101,101,114,115,97,116,116,114,97,99,116,105, -111,110,117,110,100,101,114,32,116,104,101,32,116,104,114,101,97,116,101,110,101 -,100,42,60,33,91,67,68,65,84,65,91,105,109,112,111,114,116,97,110,99,101,105,110 -,32,103,101,110,101,114,97,108,116,104,101,32,108,97,116,116,101,114,60,47,102, -111,114,109,62,10,60,47,46,105,110,100,101,120,79,102,40,39,105,32,61,32,48,59, -32,105,32,60,100,105,102,102,101,114,101,110,99,101,100,101,118,111,116,101,100, -32,116,111,116,114,97,100,105,116,105,111,110,115,115,101,97,114,99,104,32,102, -111,114,117,108,116,105,109,97,116,101,108,121,116,111,117,114,110,97,109,101, -110,116,97,116,116,114,105,98,117,116,101,115,115,111,45,99,97,108,108,101,100, -32,125,10,60,47,115,116,121,108,101,62,101,118,97,108,117,97,116,105,111,110,101 -,109,112,104,97,115,105,122,101,100,97,99,99,101,115,115,105,98,108,101,60,47, -115,101,99,116,105,111,110,62,115,117,99,99,101,115,115,105,111,110,97,108,111, -110,103,32,119,105,116,104,77,101,97,110,119,104,105,108,101,44,105,110,100,117, -115,116,114,105,101,115,60,47,97,62,60,98,114,32,47,62,104,97,115,32,98,101,99, -111,109,101,97,115,112,101,99,116,115,32,111,102,84,101,108,101,118,105,115,105, -111,110,115,117,102,102,105,99,105,101,110,116,98,97,115,107,101,116,98,97,108, -108,98,111,116,104,32,115,105,100,101,115,99,111,110,116,105,110,117,105,110,103 -,97,110,32,97,114,116,105,99,108,101,60,105,109,103,32,97,108,116,61,34,97,100, -118,101,110,116,117,114,101,115,104,105,115,32,109,111,116,104,101,114,109,97, -110,99,104,101,115,116,101,114,112,114,105,110,99,105,112,108,101,115,112,97,114 -,116,105,99,117,108,97,114,99,111,109,109,101,110,116,97,114,121,101,102,102,101 -,99,116,115,32,111,102,100,101,99,105,100,101,100,32,116,111,34,62,60,115,116, -114,111,110,103,62,112,117,98,108,105,115,104,101,114,115,74,111,117,114,110,97, -108,32,111,102,100,105,102,102,105,99,117,108,116,121,102,97,99,105,108,105,116, -97,116,101,97,99,99,101,112,116,97,98,108,101,115,116,121,108,101,46,99,115,115, -34,9,102,117,110,99,116,105,111,110,32,105,110,110,111,118,97,116,105,111,110,62 -,67,111,112,121,114,105,103,104,116,115,105,116,117,97,116,105,111,110,115,119, -111,117,108,100,32,104,97,118,101,98,117,115,105,110,101,115,115,101,115,68,105, -99,116,105,111,110,97,114,121,115,116,97,116,101,109,101,110,116,115,111,102,116 -,101,110,32,117,115,101,100,112,101,114,115,105,115,116,101,110,116,105,110,32, -74,97,110,117,97,114,121,99,111,109,112,114,105,115,105,110,103,60,47,116,105, -116,108,101,62,10,9,100,105,112,108,111,109,97,116,105,99,99,111,110,116,97,105, -110,105,110,103,112,101,114,102,111,114,109,105,110,103,101,120,116,101,110,115, -105,111,110,115,109,97,121,32,110,111,116,32,98,101,99,111,110,99,101,112,116,32 -,111,102,32,111,110,99,108,105,99,107,61,34,73,116,32,105,115,32,97,108,115,111, -102,105,110,97,110,99,105,97,108,32,109,97,107,105,110,103,32,116,104,101,76,117 -,120,101,109,98,111,117,114,103,97,100,100,105,116,105,111,110,97,108,97,114,101 -,32,99,97,108,108,101,100,101,110,103,97,103,101,100,32,105,110,34,115,99,114, -105,112,116,34,41,59,98,117,116,32,105,116,32,119,97,115,101,108,101,99,116,114, -111,110,105,99,111,110,115,117,98,109,105,116,61,34,10,60,33,45,45,32,69,110,100 -,32,101,108,101,99,116,114,105,99,97,108,111,102,102,105,99,105,97,108,108,121, -115,117,103,103,101,115,116,105,111,110,116,111,112,32,111,102,32,116,104,101, -117,110,108,105,107,101,32,116,104,101,65,117,115,116,114,97,108,105,97,110,79, -114,105,103,105,110,97,108,108,121,114,101,102,101,114,101,110,99,101,115,10,60, -47,104,101,97,100,62,13,10,114,101,99,111,103,110,105,115,101,100,105,110,105, -116,105,97,108,105,122,101,108,105,109,105,116,101,100,32,116,111,65,108,101,120 -,97,110,100,114,105,97,114,101,116,105,114,101,109,101,110,116,65,100,118,101, -110,116,117,114,101,115,102,111,117,114,32,121,101,97,114,115,10,10,38,108,116, -59,33,45,45,32,105,110,99,114,101,97,115,105,110,103,100,101,99,111,114,97,116, -105,111,110,104,51,32,99,108,97,115,115,61,34,111,114,105,103,105,110,115,32,111 -,102,111,98,108,105,103,97,116,105,111,110,114,101,103,117,108,97,116,105,111, -110,99,108,97,115,115,105,102,105,101,100,40,102,117,110,99,116,105,111,110,40, -97,100,118,97,110,116,97,103,101,115,98,101,105,110,103,32,116,104,101,32,104, -105,115,116,111,114,105,97,110,115,60,98,97,115,101,32,104,114,101,102,114,101, -112,101,97,116,101,100,108,121,119,105,108,108,105,110,103,32,116,111,99,111,109 -,112,97,114,97,98,108,101,100,101,115,105,103,110,97,116,101,100,110,111,109,105 -,110,97,116,105,111,110,102,117,110,99,116,105,111,110,97,108,105,110,115,105, -100,101,32,116,104,101,114,101,118,101,108,97,116,105,111,110,101,110,100,32,111 -,102,32,116,104,101,115,32,102,111,114,32,116,104,101,32,97,117,116,104,111,114, -105,122,101,100,114,101,102,117,115,101,100,32,116,111,116,97,107,101,32,112,108 -,97,99,101,97,117,116,111,110,111,109,111,117,115,99,111,109,112,114,111,109,105 -,115,101,112,111,108,105,116,105,99,97,108,32,114,101,115,116,97,117,114,97,110, -116,116,119,111,32,111,102,32,116,104,101,70,101,98,114,117,97,114,121,32,50,113 -,117,97,108,105,116,121,32,111,102,115,119,102,111,98,106,101,99,116,46,117,110, -100,101,114,115,116,97,110,100,110,101,97,114,108,121,32,97,108,108,119,114,105, -116,116,101,110,32,98,121,105,110,116,101,114,118,105,101,119,115,34,32,119,105, -100,116,104,61,34,49,119,105,116,104,100,114,97,119,97,108,102,108,111,97,116,58 -,108,101,102,116,105,115,32,117,115,117,97,108,108,121,99,97,110,100,105,100,97, -116,101,115,110,101,119,115,112,97,112,101,114,115,109,121,115,116,101,114,105, -111,117,115,68,101,112,97,114,116,109,101,110,116,98,101,115,116,32,107,110,111, -119,110,112,97,114,108,105,97,109,101,110,116,115,117,112,112,114,101,115,115, -101,100,99,111,110,118,101,110,105,101,110,116,114,101,109,101,109,98,101,114, -101,100,100,105,102,102,101,114,101,110,116,32,115,121,115,116,101,109,97,116, -105,99,104,97,115,32,108,101,100,32,116,111,112,114,111,112,97,103,97,110,100,97 -,99,111,110,116,114,111,108,108,101,100,105,110,102,108,117,101,110,99,101,115, -99,101,114,101,109,111,110,105,97,108,112,114,111,99,108,97,105,109,101,100,80, -114,111,116,101,99,116,105,111,110,108,105,32,99,108,97,115,115,61,34,83,99,105, -101,110,116,105,102,105,99,99,108,97,115,115,61,34,110,111,45,116,114,97,100,101 -,109,97,114,107,115,109,111,114,101,32,116,104,97,110,32,119,105,100,101,115,112 -,114,101,97,100,76,105,98,101,114,97,116,105,111,110,116,111,111,107,32,112,108, -97,99,101,100,97,121,32,111,102,32,116,104,101,97,115,32,108,111,110,103,32,97, -115,105,109,112,114,105,115,111,110,101,100,65,100,100,105,116,105,111,110,97, -108,10,60,104,101,97,100,62,10,60,109,76,97,98,111,114,97,116,111,114,121,78,111 -,118,101,109,98,101,114,32,50,101,120,99,101,112,116,105,111,110,115,73,110,100, -117,115,116,114,105,97,108,118,97,114,105,101,116,121,32,111,102,102,108,111,97, -116,58,32,108,101,102,68,117,114,105,110,103,32,116,104,101,97,115,115,101,115, -115,109,101,110,116,104,97,118,101,32,98,101,101,110,32,100,101,97,108,115,32, -119,105,116,104,83,116,97,116,105,115,116,105,99,115,111,99,99,117,114,114,101, -110,99,101,47,117,108,62,60,47,100,105,118,62,99,108,101,97,114,102,105,120,34, -62,116,104,101,32,112,117,98,108,105,99,109,97,110,121,32,121,101,97,114,115,119 -,104,105,99,104,32,119,101,114,101,111,118,101,114,32,116,105,109,101,44,115,121 -,110,111,110,121,109,111,117,115,99,111,110,116,101,110,116,34,62,10,112,114,101 -,115,117,109,97,98,108,121,104,105,115,32,102,97,109,105,108,121,117,115,101,114 -,65,103,101,110,116,46,117,110,101,120,112,101,99,116,101,100,105,110,99,108,117 -,100,105,110,103,32,99,104,97,108,108,101,110,103,101,100,97,32,109,105,110,111, -114,105,116,121,117,110,100,101,102,105,110,101,100,34,98,101,108,111,110,103, -115,32,116,111,116,97,107,101,110,32,102,114,111,109,105,110,32,79,99,116,111,98 -,101,114,112,111,115,105,116,105,111,110,58,32,115,97,105,100,32,116,111,32,98, -101,114,101,108,105,103,105,111,117,115,32,70,101,100,101,114,97,116,105,111,110 -,32,114,111,119,115,112,97,110,61,34,111,110,108,121,32,97,32,102,101,119,109, -101,97,110,116,32,116,104,97,116,108,101,100,32,116,111,32,116,104,101,45,45,62, -13,10,60,100,105,118,32,60,102,105,101,108,100,115,101,116,62,65,114,99,104,98, -105,115,104,111,112,32,99,108,97,115,115,61,34,110,111,98,101,105,110,103,32,117 -,115,101,100,97,112,112,114,111,97,99,104,101,115,112,114,105,118,105,108,101, -103,101,115,110,111,115,99,114,105,112,116,62,10,114,101,115,117,108,116,115,32, -105,110,109,97,121,32,98,101,32,116,104,101,69,97,115,116,101,114,32,101,103,103 -,109,101,99,104,97,110,105,115,109,115,114,101,97,115,111,110,97,98,108,101,80, -111,112,117,108,97,116,105,111,110,67,111,108,108,101,99,116,105,111,110,115,101 -,108,101,99,116,101,100,34,62,110,111,115,99,114,105,112,116,62,13,47,105,110, -100,101,120,46,112,104,112,97,114,114,105,118,97,108,32,111,102,45,106,115,115, -100,107,39,41,41,59,109,97,110,97,103,101,100,32,116,111,105,110,99,111,109,112, -108,101,116,101,99,97,115,117,97,108,116,105,101,115,99,111,109,112,108,101,116, -105,111,110,67,104,114,105,115,116,105,97,110,115,83,101,112,116,101,109,98,101, -114,32,97,114,105,116,104,109,101,116,105,99,112,114,111,99,101,100,117,114,101, -115,109,105,103,104,116,32,104,97,118,101,80,114,111,100,117,99,116,105,111,110, -105,116,32,97,112,112,101,97,114,115,80,104,105,108,111,115,111,112,104,121,102, -114,105,101,110,100,115,104,105,112,108,101,97,100,105,110,103,32,116,111,103, -105,118,105,110,103,32,116,104,101,116,111,119,97,114,100,32,116,104,101,103,117 -,97,114,97,110,116,101,101,100,100,111,99,117,109,101,110,116,101,100,99,111,108 -,111,114,58,35,48,48,48,118,105,100,101,111,32,103,97,109,101,99,111,109,109,105 -,115,115,105,111,110,114,101,102,108,101,99,116,105,110,103,99,104,97,110,103, -101,32,116,104,101,97,115,115,111,99,105,97,116,101,100,115,97,110,115,45,115, -101,114,105,102,111,110,107,101,121,112,114,101,115,115,59,32,112,97,100,100,105 -,110,103,58,72,101,32,119,97,115,32,116,104,101,117,110,100,101,114,108,121,105, -110,103,116,121,112,105,99,97,108,108,121,32,44,32,97,110,100,32,116,104,101,32, -115,114,99,69,108,101,109,101,110,116,115,117,99,99,101,115,115,105,118,101,115, -105,110,99,101,32,116,104,101,32,115,104,111,117,108,100,32,98,101,32,110,101, -116,119,111,114,107,105,110,103,97,99,99,111,117,110,116,105,110,103,117,115,101 -,32,111,102,32,116,104,101,108,111,119,101,114,32,116,104,97,110,115,104,111,119 -,115,32,116,104,97,116,60,47,115,112,97,110,62,10,9,9,99,111,109,112,108,97,105, -110,116,115,99,111,110,116,105,110,117,111,117,115,113,117,97,110,116,105,116, -105,101,115,97,115,116,114,111,110,111,109,101,114,104,101,32,100,105,100,32,110 -,111,116,100,117,101,32,116,111,32,105,116,115,97,112,112,108,105,101,100,32,116 -,111,97,110,32,97,118,101,114,97,103,101,101,102,102,111,114,116,115,32,116,111, -116,104,101,32,102,117,116,117,114,101,97,116,116,101,109,112,116,32,116,111,84, -104,101,114,101,102,111,114,101,44,99,97,112,97,98,105,108,105,116,121,82,101, -112,117,98,108,105,99,97,110,119,97,115,32,102,111,114,109,101,100,69,108,101,99 -,116,114,111,110,105,99,107,105,108,111,109,101,116,101,114,115,99,104,97,108, -108,101,110,103,101,115,112,117,98,108,105,115,104,105,110,103,116,104,101,32, -102,111,114,109,101,114,105,110,100,105,103,101,110,111,117,115,100,105,114,101, -99,116,105,111,110,115,115,117,98,115,105,100,105,97,114,121,99,111,110,115,112, -105,114,97,99,121,100,101,116,97,105,108,115,32,111,102,97,110,100,32,105,110,32 -,116,104,101,97,102,102,111,114,100,97,98,108,101,115,117,98,115,116,97,110,99, -101,115,114,101,97,115,111,110,32,102,111,114,99,111,110,118,101,110,116,105,111 -,110,105,116,101,109,116,121,112,101,61,34,97,98,115,111,108,117,116,101,108,121 -,115,117,112,112,111,115,101,100,108,121,114,101,109,97,105,110,101,100,32,97,97 -,116,116,114,97,99,116,105,118,101,116,114,97,118,101,108,108,105,110,103,115, -101,112,97,114,97,116,101,108,121,102,111,99,117,115,101,115,32,111,110,101,108, -101,109,101,110,116,97,114,121,97,112,112,108,105,99,97,98,108,101,102,111,117, -110,100,32,116,104,97,116,115,116,121,108,101,115,104,101,101,116,109,97,110,117 -,115,99,114,105,112,116,115,116,97,110,100,115,32,102,111,114,32,110,111,45,114, -101,112,101,97,116,40,115,111,109,101,116,105,109,101,115,67,111,109,109,101,114 -,99,105,97,108,105,110,32,65,109,101,114,105,99,97,117,110,100,101,114,116,97, -107,101,110,113,117,97,114,116,101,114,32,111,102,97,110,32,101,120,97,109,112, -108,101,112,101,114,115,111,110,97,108,108,121,105,110,100,101,120,46,112,104, -112,63,60,47,98,117,116,116,111,110,62,10,112,101,114,99,101,110,116,97,103,101, -98,101,115,116,45,107,110,111,119,110,99,114,101,97,116,105,110,103,32,97,34,32, -100,105,114,61,34,108,116,114,76,105,101,117,116,101,110,97,110,116,10,60,100, -105,118,32,105,100,61,34,116,104,101,121,32,119,111,117,108,100,97,98,105,108, -105,116,121,32,111,102,109,97,100,101,32,117,112,32,111,102,110,111,116,101,100, -32,116,104,97,116,99,108,101,97,114,32,116,104,97,116,97,114,103,117,101,32,116, -104,97,116,116,111,32,97,110,111,116,104,101,114,99,104,105,108,100,114,101,110, -39,115,112,117,114,112,111,115,101,32,111,102,102,111,114,109,117,108,97,116,101 -,100,98,97,115,101,100,32,117,112,111,110,116,104,101,32,114,101,103,105,111,110 -,115,117,98,106,101,99,116,32,111,102,112,97,115,115,101,110,103,101,114,115,112 -,111,115,115,101,115,115,105,111,110,46,10,10,73,110,32,116,104,101,32,66,101, -102,111,114,101,32,116,104,101,97,102,116,101,114,119,97,114,100,115,99,117,114, -114,101,110,116,108,121,32,97,99,114,111,115,115,32,116,104,101,115,99,105,101, -110,116,105,102,105,99,99,111,109,109,117,110,105,116,121,46,99,97,112,105,116, -97,108,105,115,109,105,110,32,71,101,114,109,97,110,121,114,105,103,104,116,45, -119,105,110,103,116,104,101,32,115,121,115,116,101,109,83,111,99,105,101,116,121 -,32,111,102,112,111,108,105,116,105,99,105,97,110,100,105,114,101,99,116,105,111 -,110,58,119,101,110,116,32,111,110,32,116,111,114,101,109,111,118,97,108,32,111, -102,32,78,101,119,32,89,111,114,107,32,97,112,97,114,116,109,101,110,116,115,105 -,110,100,105,99,97,116,105,111,110,100,117,114,105,110,103,32,116,104,101,117, -110,108,101,115,115,32,116,104,101,104,105,115,116,111,114,105,99,97,108,104,97, -100,32,98,101,101,110,32,97,100,101,102,105,110,105,116,105,118,101,105,110,103, -114,101,100,105,101,110,116,97,116,116,101,110,100,97,110,99,101,67,101,110,116, -101,114,32,102,111,114,112,114,111,109,105,110,101,110,99,101,114,101,97,100,121 -,83,116,97,116,101,115,116,114,97,116,101,103,105,101,115,98,117,116,32,105,110, -32,116,104,101,97,115,32,112,97,114,116,32,111,102,99,111,110,115,116,105,116, -117,116,101,99,108,97,105,109,32,116,104,97,116,108,97,98,111,114,97,116,111,114 -,121,99,111,109,112,97,116,105,98,108,101,102,97,105,108,117,114,101,32,111,102, -44,32,115,117,99,104,32,97,115,32,98,101,103,97,110,32,119,105,116,104,117,115, -105,110,103,32,116,104,101,32,116,111,32,112,114,111,118,105,100,101,102,101,97, -116,117,114,101,32,111,102,102,114,111,109,32,119,104,105,99,104,47,34,32,99,108 -,97,115,115,61,34,103,101,111,108,111,103,105,99,97,108,115,101,118,101,114,97, -108,32,111,102,100,101,108,105,98,101,114,97,116,101,105,109,112,111,114,116,97, -110,116,32,104,111,108,100,115,32,116,104,97,116,105,110,103,38,113,117,111,116, -59,32,118,97,108,105,103,110,61,116,111,112,116,104,101,32,71,101,114,109,97,110 -,111,117,116,115,105,100,101,32,111,102,110,101,103,111,116,105,97,116,101,100, -104,105,115,32,99,97,114,101,101,114,115,101,112,97,114,97,116,105,111,110,105, -100,61,34,115,101,97,114,99,104,119,97,115,32,99,97,108,108,101,100,116,104,101, -32,102,111,117,114,116,104,114,101,99,114,101,97,116,105,111,110,111,116,104,101 -,114,32,116,104,97,110,112,114,101,118,101,110,116,105,111,110,119,104,105,108, -101,32,116,104,101,32,101,100,117,99,97,116,105,111,110,44,99,111,110,110,101,99 -,116,105,110,103,97,99,99,117,114,97,116,101,108,121,119,101,114,101,32,98,117, -105,108,116,119,97,115,32,107,105,108,108,101,100,97,103,114,101,101,109,101,110 -,116,115,109,117,99,104,32,109,111,114,101,32,68,117,101,32,116,111,32,116,104, -101,119,105,100,116,104,58,32,49,48,48,115,111,109,101,32,111,116,104,101,114,75 -,105,110,103,100,111,109,32,111,102,116,104,101,32,101,110,116,105,114,101,102, -97,109,111,117,115,32,102,111,114,116,111,32,99,111,110,110,101,99,116,111,98, -106,101,99,116,105,118,101,115,116,104,101,32,70,114,101,110,99,104,112,101,111, -112,108,101,32,97,110,100,102,101,97,116,117,114,101,100,34,62,105,115,32,115,97 -,105,100,32,116,111,115,116,114,117,99,116,117,114,97,108,114,101,102,101,114, -101,110,100,117,109,109,111,115,116,32,111,102,116,101,110,97,32,115,101,112,97, -114,97,116,101,45,62,10,60,100,105,118,32,105,100,32,79,102,102,105,99,105,97, -108,32,119,111,114,108,100,119,105,100,101,46,97,114,105,97,45,108,97,98,101,108 -,116,104,101,32,112,108,97,110,101,116,97,110,100,32,105,116,32,119,97,115,100, -34,32,118,97,108,117,101,61,34,108,111,111,107,105,110,103,32,97,116,98,101,110, -101,102,105,99,105,97,108,97,114,101,32,105,110,32,116,104,101,109,111,110,105, -116,111,114,105,110,103,114,101,112,111,114,116,101,100,108,121,116,104,101,32, -109,111,100,101,114,110,119,111,114,107,105,110,103,32,111,110,97,108,108,111, -119,101,100,32,116,111,119,104,101,114,101,32,116,104,101,32,105,110,110,111,118 -,97,116,105,118,101,60,47,97,62,60,47,100,105,118,62,115,111,117,110,100,116,114 -,97,99,107,115,101,97,114,99,104,70,111,114,109,116,101,110,100,32,116,111,32,98 -,101,105,110,112,117,116,32,105,100,61,34,111,112,101,110,105,110,103,32,111,102 -,114,101,115,116,114,105,99,116,101,100,97,100,111,112,116,101,100,32,98,121,97, -100,100,114,101,115,115,105,110,103,116,104,101,111,108,111,103,105,97,110,109, -101,116,104,111,100,115,32,111,102,118,97,114,105,97,110,116,32,111,102,67,104, -114,105,115,116,105,97,110,32,118,101,114,121,32,108,97,114,103,101,97,117,116, -111,109,111,116,105,118,101,98,121,32,102,97,114,32,116,104,101,114,97,110,103, -101,32,102,114,111,109,112,117,114,115,117,105,116,32,111,102,102,111,108,108, -111,119,32,116,104,101,98,114,111,117,103,104,116,32,116,111,105,110,32,69,110, -103,108,97,110,100,97,103,114,101,101,32,116,104,97,116,97,99,99,117,115,101,100 -,32,111,102,99,111,109,101,115,32,102,114,111,109,112,114,101,118,101,110,116, -105,110,103,100,105,118,32,115,116,121,108,101,61,104,105,115,32,111,114,32,104, -101,114,116,114,101,109,101,110,100,111,117,115,102,114,101,101,100,111,109,32, -111,102,99,111,110,99,101,114,110,105,110,103,48,32,49,101,109,32,49,101,109,59, -66,97,115,107,101,116,98,97,108,108,47,115,116,121,108,101,46,99,115,115,97,110, -32,101,97,114,108,105,101,114,101,118,101,110,32,97,102,116,101,114,47,34,32,116 -,105,116,108,101,61,34,46,99,111,109,47,105,110,100,101,120,116,97,107,105,110, -103,32,116,104,101,112,105,116,116,115,98,117,114,103,104,99,111,110,116,101,110 -,116,34,62,13,60,115,99,114,105,112,116,62,40,102,116,117,114,110,101,100,32,111 -,117,116,104,97,118,105,110,103,32,116,104,101,60,47,115,112,97,110,62,13,10,32, -111,99,99,97,115,105,111,110,97,108,98,101,99,97,117,115,101,32,105,116,115,116, -97,114,116,101,100,32,116,111,112,104,121,115,105,99,97,108,108,121,62,60,47,100 -,105,118,62,10,32,32,99,114,101,97,116,101,100,32,98,121,67,117,114,114,101,110, -116,108,121,44,32,98,103,99,111,108,111,114,61,34,116,97,98,105,110,100,101,120, -61,34,100,105,115,97,115,116,114,111,117,115,65,110,97,108,121,116,105,99,115,32 -,97,108,115,111,32,104,97,115,32,97,62,60,100,105,118,32,105,100,61,34,60,47,115 -,116,121,108,101,62,10,60,99,97,108,108,101,100,32,102,111,114,115,105,110,103, -101,114,32,97,110,100,46,115,114,99,32,61,32,34,47,47,118,105,111,108,97,116,105 -,111,110,115,116,104,105,115,32,112,111,105,110,116,99,111,110,115,116,97,110, -116,108,121,105,115,32,108,111,99,97,116,101,100,114,101,99,111,114,100,105,110, -103,115,100,32,102,114,111,109,32,116,104,101,110,101,100,101,114,108,97,110,100 -,115,112,111,114,116,117,103,117,195,170,115,215,162,215,145,215,168,215,153,215 -,170,217,129,216,167,216,177,216,179,219,140,100,101,115,97,114,114,111,108,108, -111,99,111,109,101,110,116,97,114,105,111,101,100,117,99,97,99,105,195,179,110, -115,101,112,116,105,101,109,98,114,101,114,101,103,105,115,116,114,97,100,111, -100,105,114,101,99,99,105,195,179,110,117,98,105,99,97,99,105,195,179,110,112, -117,98,108,105,99,105,100,97,100,114,101,115,112,117,101,115,116,97,115,114,101, -115,117,108,116,97,100,111,115,105,109,112,111,114,116,97,110,116,101,114,101, -115,101,114,118,97,100,111,115,97,114,116,195,173,99,117,108,111,115,100,105,102 -,101,114,101,110,116,101,115,115,105,103,117,105,101,110,116,101,115,114,101,112 -,195,186,98,108,105,99,97,115,105,116,117,97,99,105,195,179,110,109,105,110,105, -115,116,101,114,105,111,112,114,105,118,97,99,105,100,97,100,100,105,114,101,99, -116,111,114,105,111,102,111,114,109,97,99,105,195,179,110,112,111,98,108,97,99, -105,195,179,110,112,114,101,115,105,100,101,110,116,101,99,111,110,116,101,110, -105,100,111,115,97,99,99,101,115,111,114,105,111,115,116,101,99,104,110,111,114, -97,116,105,112,101,114,115,111,110,97,108,101,115,99,97,116,101,103,111,114,195, -173,97,101,115,112,101,99,105,97,108,101,115,100,105,115,112,111,110,105,98,108, -101,97,99,116,117,97,108,105,100,97,100,114,101,102,101,114,101,110,99,105,97, -118,97,108,108,97,100,111,108,105,100,98,105,98,108,105,111,116,101,99,97,114, -101,108,97,99,105,111,110,101,115,99,97,108,101,110,100,97,114,105,111,112,111, -108,195,173,116,105,99,97,115,97,110,116,101,114,105,111,114,101,115,100,111,99, -117,109,101,110,116,111,115,110,97,116,117,114,97,108,101,122,97,109,97,116,101, -114,105,97,108,101,115,100,105,102,101,114,101,110,99,105,97,101,99,111,110,195, -179,109,105,99,97,116,114,97,110,115,112,111,114,116,101,114,111,100,114,195,173 -,103,117,101,122,112,97,114,116,105,99,105,112,97,114,101,110,99,117,101,110,116 -,114,97,110,100,105,115,99,117,115,105,195,179,110,101,115,116,114,117,99,116, -117,114,97,102,117,110,100,97,99,105,195,179,110,102,114,101,99,117,101,110,116, -101,115,112,101,114,109,97,110,101,110,116,101,116,111,116,97,108,109,101,110, -116,101,208,188,208,190,208,182,208,189,208,190,208,177,209,131,208,180,208,181, -209,130,208,188,208,190,208,182,208,181,209,130,208,178,209,128,208,181,208,188, -209,143,209,130,208,176,208,186,208,182,208,181,209,135,209,130,208,190,208,177, -209,139,208,177,208,190,208,187,208,181,208,181,208,190,209,135,208,181,208,189, -209,140,209,141,209,130,208,190,208,179,208,190,208,186,208,190,208,179,208,180, -208,176,208,191,208,190,209,129,208,187,208,181,208,178,209,129,208,181,208,179, -208,190,209,129,208,176,208,185,209,130,208,181,209,135,208,181,209,128,208,181, -208,183,208,188,208,190,208,179,209,131,209,130,209,129,208,176,208,185,209,130, -208,176,208,182,208,184,208,183,208,189,208,184,208,188,208,181,208,182,208,180, -209,131,208,177,209,131,208,180,209,131,209,130,208,159,208,190,208,184,209,129, -208,186,208,183,208,180,208,181,209,129,209,140,208,178,208,184,208,180,208,181, -208,190,209,129,208,178,209,143,208,183,208,184,208,189,209,131,208,182,208,189, -208,190,209,129,208,178,208,190,208,181,208,185,208,187,209,142,208,180,208,181, -208,185,208,191,208,190,209,128,208,189,208,190,208,188,208,189,208,190,208,179, -208,190,208,180,208,181,209,130,208,181,208,185,209,129,208,178,208,190,208,184, -209,133,208,191,209,128,208,176,208,178,208,176,209,130,208,176,208,186,208,190, -208,185,208,188,208,181,209,129,209,130,208,190,208,184,208,188,208,181,208,181, -209,130,208,182,208,184,208,183,208,189,209,140,208,190,208,180,208,189,208,190, -208,185,208,187,209,131,209,135,209,136,208,181,208,191,208,181,209,128,208,181, -208,180,209,135,208,176,209,129,209,130,208,184,209,135,208,176,209,129,209,130, -209,140,209,128,208,176,208,177,208,190,209,130,208,189,208,190,208,178,209,139, -209,133,208,191,209,128,208,176,208,178,208,190,209,129,208,190,208,177,208,190, -208,185,208,191,208,190,209,130,208,190,208,188,208,188,208,181,208,189,208,181, -208,181,209,135,208,184,209,129,208,187,208,181,208,189,208,190,208,178,209,139, -208,181,209,131,209,129,208,187,209,131,208,179,208,190,208,186,208,190,208,187, -208,190,208,189,208,176,208,183,208,176,208,180,209,130,208,176,208,186,208,190, -208,181,209,130,208,190,208,179,208,180,208,176,208,191,208,190,209,135,209,130, -208,184,208,159,208,190,209,129,208,187,208,181,209,130,208,176,208,186,208,184, -208,181,208,189,208,190,208,178,209,139,208,185,209,129,209,130,208,190,208,184, -209,130,209,130,208,176,208,186,208,184,209,133,209,129,209,128,208,176,208,183, -209,131,208,161,208,176,208,189,208,186,209,130,209,132,208,190,209,128,209,131, -208,188,208,154,208,190,208,179,208,180,208,176,208,186,208,189,208,184,208,179, -208,184,209,129,208,187,208,190,208,178,208,176,208,189,208,176,209,136,208,181, -208,185,208,189,208,176,208,185,209,130,208,184,209,129,208,178,208,190,208,184, -208,188,209,129,208,178,209,143,208,183,209,140,208,187,209,142,208,177,208,190, -208,185,209,135,208,176,209,129,209,130,208,190,209,129,209,128,208,181,208,180, -208,184,208,154,209,128,208,190,208,188,208,181,208,164,208,190,209,128,209,131, -208,188,209,128,209,139,208,189,208,186,208,181,209,129,209,130,208,176,208,187, -208,184,208,191,208,190,208,184,209,129,208,186,209,130,209,139,209,129,209,143, -209,135,208,188,208,181,209,129,209,143,209,134,209,134,208,181,208,189,209,130, -209,128,209,130,209,128,209,131,208,180,208,176,209,129,208,176,208,188,209,139, -209,133,209,128,209,139,208,189,208,186,208,176,208,157,208,190,208,178,209,139, -208,185,209,135,208,176,209,129,208,190,208,178,208,188,208,181,209,129,209,130, -208,176,209,132,208,184,208,187,209,140,208,188,208,188,208,176,209,128,209,130, -208,176,209,129,209,130,209,128,208,176,208,189,208,188,208,181,209,129,209,130, -208,181,209,130,208,181,208,186,209,129,209,130,208,189,208,176,209,136,208,184, -209,133,208,188,208,184,208,189,209,131,209,130,208,184,208,188,208,181,208,189, -208,184,208,184,208,188,208,181,209,142,209,130,208,189,208,190,208,188,208,181, -209,128,208,179,208,190,209,128,208,190,208,180,209,129,208,176,208,188,208,190, -208,188,209,141,209,130,208,190,208,188,209,131,208,186,208,190,208,189,209,134, -208,181,209,129,208,178,208,190,208,181,208,188,208,186,208,176,208,186,208,190, -208,185,208,144,209,128,209,133,208,184,208,178,217,133,217,134,216,170,216,175, -217,137,216,165,216,177,216,179,216,167,217,132,216,177,216,179,216,167,217,132, -216,169,216,167,217,132,216,185,216,167,217,133,217,131,216,170,216,168,217,135, -216,167,216,168,216,177,216,167,217,133,216,172,216,167,217,132,217,138,217,136, -217,133,216,167,217,132,216,181,217,136,216,177,216,172,216,175,217,138,216,175, -216,169,216,167,217,132,216,185,216,182,217,136,216,165,216,182,216,167,217,129, -216,169,216,167,217,132,217,130,216,179,217,133,216,167,217,132,216,185,216,167, -216,168,216,170,216,173,217,133,217,138,217,132,217,133,217,132,217,129,216,167, -216,170,217,133,217,132,216,170,217,130,217,137,216,170,216,185,216,175,217,138, -217,132,216,167,217,132,216,180,216,185,216,177,216,163,216,174,216,168,216,167, -216,177,216,170,216,183,217,136,217,138,216,177,216,185,217,132,217,138,217,131, -217,133,216,165,216,177,217,129,216,167,217,130,216,183,217,132,216,168,216,167, -216,170,216,167,217,132,217,132,216,186,216,169,216,170,216,177,216,170,217,138, -216,168,216,167,217,132,217,134,216,167,216,179,216,167,217,132,216,180,217,138, -216,174,217,133,217,134,216,170,216,175,217,138,216,167,217,132,216,185,216,177, -216,168,216,167,217,132,217,130,216,181,216,181,216,167,217,129,217,132,216,167, -217,133,216,185,217,132,217,138,217,135,216,167,216,170,216,173,216,175,217,138, -216,171,216,167,217,132,217,132,217,135,217,133,216,167,217,132,216,185,217,133, -217,132,217,133,217,131,216,170,216,168,216,169,217,138,217,133,217,131,217,134, -217,131,216,167,217,132,216,183,217,129,217,132,217,129,217,138,216,175,217,138, -217,136,216,165,216,175,216,167,216,177,216,169,216,170,216,167,216,177,217,138, -216,174,216,167,217,132,216,181,216,173,216,169,216,170,216,179,216,172,217,138, -217,132,216,167,217,132,217,136,217,130,216,170,216,185,217,134,216,175,217,133, -216,167,217,133,216,175,217,138,217,134,216,169,216,170,216,181,217,133,217,138, -217,133,216,163,216,177,216,180,217,138,217,129,216,167,217,132,216,176,217,138, -217,134,216,185,216,177,216,168,217,138,216,169,216,168,217,136,216,167,216,168, -216,169,216,163,217,132,216,185,216,167,216,168,216,167,217,132,216,179,217,129, -216,177,217,133,216,180,216,167,217,131,217,132,216,170,216,185,216,167,217,132, -217,137,216,167,217,132,216,163,217,136,217,132,216,167,217,132,216,179,217,134, -216,169,216,172,216,167,217,133,216,185,216,169,216,167,217,132,216,181,216,173, -217,129,216,167,217,132,216,175,217,138,217,134,217,131,217,132,217,133,216,167, -216,170,216,167,217,132,216,174,216,167,216,181,216,167,217,132,217,133,217,132, -217,129,216,163,216,185,216,182,216,167,216,161,217,131,216,170,216,167,216,168, -216,169,216,167,217,132,216,174,217,138,216,177,216,177,216,179,216,167,216,166, -217,132,216,167,217,132,217,130,217,132,216,168,216,167,217,132,216,163,216,175, -216,168,217,133,217,130,216,167,216,183,216,185,217,133,216,177,216,167,216,179, -217,132,217,133,217,134,216,183,217,130,216,169,216,167,217,132,217,131,216,170, -216,168,216,167,217,132,216,177,216,172,217,132,216,167,216,180,216,170,216,177, -217,131,216,167,217,132,217,130,216,175,217,133,217,138,216,185,216,183,217,138, -217,131,115,66,121,84,97,103,78,97,109,101,40,46,106,112,103,34,32,97,108,116,61 -,34,49,112,120,32,115,111,108,105,100,32,35,46,103,105,102,34,32,97,108,116,61, -34,116,114,97,110,115,112,97,114,101,110,116,105,110,102,111,114,109,97,116,105, -111,110,97,112,112,108,105,99,97,116,105,111,110,34,32,111,110,99,108,105,99,107 -,61,34,101,115,116,97,98,108,105,115,104,101,100,97,100,118,101,114,116,105,115, -105,110,103,46,112,110,103,34,32,97,108,116,61,34,101,110,118,105,114,111,110, -109,101,110,116,112,101,114,102,111,114,109,97,110,99,101,97,112,112,114,111,112 -,114,105,97,116,101,38,97,109,112,59,109,100,97,115,104,59,105,109,109,101,100, -105,97,116,101,108,121,60,47,115,116,114,111,110,103,62,60,47,114,97,116,104,101 -,114,32,116,104,97,110,116,101,109,112,101,114,97,116,117,114,101,100,101,118, -101,108,111,112,109,101,110,116,99,111,109,112,101,116,105,116,105,111,110,112, -108,97,99,101,104,111,108,100,101,114,118,105,115,105,98,105,108,105,116,121,58, -99,111,112,121,114,105,103,104,116,34,62,48,34,32,104,101,105,103,104,116,61,34, -101,118,101,110,32,116,104,111,117,103,104,114,101,112,108,97,99,101,109,101,110 -,116,100,101,115,116,105,110,97,116,105,111,110,67,111,114,112,111,114,97,116, -105,111,110,60,117,108,32,99,108,97,115,115,61,34,65,115,115,111,99,105,97,116, -105,111,110,105,110,100,105,118,105,100,117,97,108,115,112,101,114,115,112,101, -99,116,105,118,101,115,101,116,84,105,109,101,111,117,116,40,117,114,108,40,104, -116,116,112,58,47,47,109,97,116,104,101,109,97,116,105,99,115,109,97,114,103,105 -,110,45,116,111,112,58,101,118,101,110,116,117,97,108,108,121,32,100,101,115,99, -114,105,112,116,105,111,110,41,32,110,111,45,114,101,112,101,97,116,99,111,108, -108,101,99,116,105,111,110,115,46,74,80,71,124,116,104,117,109,98,124,112,97,114 -,116,105,99,105,112,97,116,101,47,104,101,97,100,62,60,98,111,100,121,102,108, -111,97,116,58,108,101,102,116,59,60,108,105,32,99,108,97,115,115,61,34,104,117, -110,100,114,101,100,115,32,111,102,10,10,72,111,119,101,118,101,114,44,32,99,111 -,109,112,111,115,105,116,105,111,110,99,108,101,97,114,58,98,111,116,104,59,99, -111,111,112,101,114,97,116,105,111,110,119,105,116,104,105,110,32,116,104,101,32 -,108,97,98,101,108,32,102,111,114,61,34,98,111,114,100,101,114,45,116,111,112,58 -,78,101,119,32,90,101,97,108,97,110,100,114,101,99,111,109,109,101,110,100,101, -100,112,104,111,116,111,103,114,97,112,104,121,105,110,116,101,114,101,115,116, -105,110,103,38,108,116,59,115,117,112,38,103,116,59,99,111,110,116,114,111,118, -101,114,115,121,78,101,116,104,101,114,108,97,110,100,115,97,108,116,101,114,110 -,97,116,105,118,101,109,97,120,108,101,110,103,116,104,61,34,115,119,105,116,122 -,101,114,108,97,110,100,68,101,118,101,108,111,112,109,101,110,116,101,115,115, -101,110,116,105,97,108,108,121,10,10,65,108,116,104,111,117,103,104,32,60,47,116 -,101,120,116,97,114,101,97,62,116,104,117,110,100,101,114,98,105,114,100,114,101 -,112,114,101,115,101,110,116,101,100,38,97,109,112,59,110,100,97,115,104,59,115, -112,101,99,117,108,97,116,105,111,110,99,111,109,109,117,110,105,116,105,101,115 -,108,101,103,105,115,108,97,116,105,111,110,101,108,101,99,116,114,111,110,105, -99,115,10,9,60,100,105,118,32,105,100,61,34,105,108,108,117,115,116,114,97,116, -101,100,101,110,103,105,110,101,101,114,105,110,103,116,101,114,114,105,116,111, -114,105,101,115,97,117,116,104,111,114,105,116,105,101,115,100,105,115,116,114, -105,98,117,116,101,100,54,34,32,104,101,105,103,104,116,61,34,115,97,110,115,45, -115,101,114,105,102,59,99,97,112,97,98,108,101,32,111,102,32,100,105,115,97,112, -112,101,97,114,101,100,105,110,116,101,114,97,99,116,105,118,101,108,111,111,107 -,105,110,103,32,102,111,114,105,116,32,119,111,117,108,100,32,98,101,65,102,103, -104,97,110,105,115,116,97,110,119,97,115,32,99,114,101,97,116,101,100,77,97,116, -104,46,102,108,111,111,114,40,115,117,114,114,111,117,110,100,105,110,103,99,97, -110,32,97,108,115,111,32,98,101,111,98,115,101,114,118,97,116,105,111,110,109,97 -,105,110,116,101,110,97,110,99,101,101,110,99,111,117,110,116,101,114,101,100,60 -,104,50,32,99,108,97,115,115,61,34,109,111,114,101,32,114,101,99,101,110,116,105 -,116,32,104,97,115,32,98,101,101,110,105,110,118,97,115,105,111,110,32,111,102, -41,46,103,101,116,84,105,109,101,40,41,102,117,110,100,97,109,101,110,116,97,108 -,68,101,115,112,105,116,101,32,116,104,101,34,62,60,100,105,118,32,105,100,61,34 -,105,110,115,112,105,114,97,116,105,111,110,101,120,97,109,105,110,97,116,105, -111,110,112,114,101,112,97,114,97,116,105,111,110,101,120,112,108,97,110,97,116, -105,111,110,60,105,110,112,117,116,32,105,100,61,34,60,47,97,62,60,47,115,112,97 -,110,62,118,101,114,115,105,111,110,115,32,111,102,105,110,115,116,114,117,109, -101,110,116,115,98,101,102,111,114,101,32,116,104,101,32,32,61,32,39,104,116,116 -,112,58,47,47,68,101,115,99,114,105,112,116,105,111,110,114,101,108,97,116,105, -118,101,108,121,32,46,115,117,98,115,116,114,105,110,103,40,101,97,99,104,32,111 -,102,32,116,104,101,101,120,112,101,114,105,109,101,110,116,115,105,110,102,108, -117,101,110,116,105,97,108,105,110,116,101,103,114,97,116,105,111,110,109,97,110 -,121,32,112,101,111,112,108,101,100,117,101,32,116,111,32,116,104,101,32,99,111, -109,98,105,110,97,116,105,111,110,100,111,32,110,111,116,32,104,97,118,101,77, -105,100,100,108,101,32,69,97,115,116,60,110,111,115,99,114,105,112,116,62,60,99, -111,112,121,114,105,103,104,116,34,32,112,101,114,104,97,112,115,32,116,104,101, -105,110,115,116,105,116,117,116,105,111,110,105,110,32,68,101,99,101,109,98,101, -114,97,114,114,97,110,103,101,109,101,110,116,109,111,115,116,32,102,97,109,111, -117,115,112,101,114,115,111,110,97,108,105,116,121,99,114,101,97,116,105,111,110 -,32,111,102,108,105,109,105,116,97,116,105,111,110,115,101,120,99,108,117,115, -105,118,101,108,121,115,111,118,101,114,101,105,103,110,116,121,45,99,111,110, -116,101,110,116,34,62,10,60,116,100,32,99,108,97,115,115,61,34,117,110,100,101, -114,103,114,111,117,110,100,112,97,114,97,108,108,101,108,32,116,111,100,111,99, -116,114,105,110,101,32,111,102,111,99,99,117,112,105,101,100,32,98,121,116,101, -114,109,105,110,111,108,111,103,121,82,101,110,97,105,115,115,97,110,99,101,97, -32,110,117,109,98,101,114,32,111,102,115,117,112,112,111,114,116,32,102,111,114, -101,120,112,108,111,114,97,116,105,111,110,114,101,99,111,103,110,105,116,105, -111,110,112,114,101,100,101,99,101,115,115,111,114,60,105,109,103,32,115,114,99, -61,34,47,60,104,49,32,99,108,97,115,115,61,34,112,117,98,108,105,99,97,116,105, -111,110,109,97,121,32,97,108,115,111,32,98,101,115,112,101,99,105,97,108,105,122 -,101,100,60,47,102,105,101,108,100,115,101,116,62,112,114,111,103,114,101,115, -115,105,118,101,109,105,108,108,105,111,110,115,32,111,102,115,116,97,116,101, -115,32,116,104,97,116,101,110,102,111,114,99,101,109,101,110,116,97,114,111,117, -110,100,32,116,104,101,32,111,110,101,32,97,110,111,116,104,101,114,46,112,97, -114,101,110,116,78,111,100,101,97,103,114,105,99,117,108,116,117,114,101,65,108, -116,101,114,110,97,116,105,118,101,114,101,115,101,97,114,99,104,101,114,115,116 -,111,119,97,114,100,115,32,116,104,101,77,111,115,116,32,111,102,32,116,104,101, -109,97,110,121,32,111,116,104,101,114,32,40,101,115,112,101,99,105,97,108,108, -121,60,116,100,32,119,105,100,116,104,61,34,59,119,105,100,116,104,58,49,48,48, -37,105,110,100,101,112,101,110,100,101,110,116,60,104,51,32,99,108,97,115,115,61 -,34,32,111,110,99,104,97,110,103,101,61,34,41,46,97,100,100,67,108,97,115,115,40 -,105,110,116,101,114,97,99,116,105,111,110,79,110,101,32,111,102,32,116,104,101, -32,100,97,117,103,104,116,101,114,32,111,102,97,99,99,101,115,115,111,114,105, -101,115,98,114,97,110,99,104,101,115,32,111,102,13,10,60,100,105,118,32,105,100, -61,34,116,104,101,32,108,97,114,103,101,115,116,100,101,99,108,97,114,97,116,105 -,111,110,114,101,103,117,108,97,116,105,111,110,115,73,110,102,111,114,109,97, -116,105,111,110,116,114,97,110,115,108,97,116,105,111,110,100,111,99,117,109,101 -,110,116,97,114,121,105,110,32,111,114,100,101,114,32,116,111,34,62,10,60,104, -101,97,100,62,10,60,34,32,104,101,105,103,104,116,61,34,49,97,99,114,111,115,115 -,32,116,104,101,32,111,114,105,101,110,116,97,116,105,111,110,41,59,60,47,115,99 -,114,105,112,116,62,105,109,112,108,101,109,101,110,116,101,100,99,97,110,32,98, -101,32,115,101,101,110,116,104,101,114,101,32,119,97,115,32,97,100,101,109,111, -110,115,116,114,97,116,101,99,111,110,116,97,105,110,101,114,34,62,99,111,110, -110,101,99,116,105,111,110,115,116,104,101,32,66,114,105,116,105,115,104,119,97, -115,32,119,114,105,116,116,101,110,33,105,109,112,111,114,116,97,110,116,59,112, -120,59,32,109,97,114,103,105,110,45,102,111,108,108,111,119,101,100,32,98,121,97 -,98,105,108,105,116,121,32,116,111,32,99,111,109,112,108,105,99,97,116,101,100, -100,117,114,105,110,103,32,116,104,101,32,105,109,109,105,103,114,97,116,105,111 -,110,97,108,115,111,32,99,97,108,108,101,100,60,104,52,32,99,108,97,115,115,61, -34,100,105,115,116,105,110,99,116,105,111,110,114,101,112,108,97,99,101,100,32, -98,121,103,111,118,101,114,110,109,101,110,116,115,108,111,99,97,116,105,111,110 -,32,111,102,105,110,32,78,111,118,101,109,98,101,114,119,104,101,116,104,101,114 -,32,116,104,101,60,47,112,62,10,60,47,100,105,118,62,97,99,113,117,105,115,105, -116,105,111,110,99,97,108,108,101,100,32,116,104,101,32,112,101,114,115,101,99, -117,116,105,111,110,100,101,115,105,103,110,97,116,105,111,110,123,102,111,110, -116,45,115,105,122,101,58,97,112,112,101,97,114,101,100,32,105,110,105,110,118, -101,115,116,105,103,97,116,101,101,120,112,101,114,105,101,110,99,101,100,109, -111,115,116,32,108,105,107,101,108,121,119,105,100,101,108,121,32,117,115,101, -100,100,105,115,99,117,115,115,105,111,110,115,112,114,101,115,101,110,99,101,32 -,111,102,32,40,100,111,99,117,109,101,110,116,46,101,120,116,101,110,115,105,118 -,101,108,121,73,116,32,104,97,115,32,98,101,101,110,105,116,32,100,111,101,115, -32,110,111,116,99,111,110,116,114,97,114,121,32,116,111,105,110,104,97,98,105, -116,97,110,116,115,105,109,112,114,111,118,101,109,101,110,116,115,99,104,111, -108,97,114,115,104,105,112,99,111,110,115,117,109,112,116,105,111,110,105,110, -115,116,114,117,99,116,105,111,110,102,111,114,32,101,120,97,109,112,108,101,111 -,110,101,32,111,114,32,109,111,114,101,112,120,59,32,112,97,100,100,105,110,103, -116,104,101,32,99,117,114,114,101,110,116,97,32,115,101,114,105,101,115,32,111, -102,97,114,101,32,117,115,117,97,108,108,121,114,111,108,101,32,105,110,32,116, -104,101,112,114,101,118,105,111,117,115,108,121,32,100,101,114,105,118,97,116, -105,118,101,115,101,118,105,100,101,110,99,101,32,111,102,101,120,112,101,114, -105,101,110,99,101,115,99,111,108,111,114,115,99,104,101,109,101,115,116,97,116, -101,100,32,116,104,97,116,99,101,114,116,105,102,105,99,97,116,101,60,47,97,62, -60,47,100,105,118,62,10,32,115,101,108,101,99,116,101,100,61,34,104,105,103,104, -32,115,99,104,111,111,108,114,101,115,112,111,110,115,101,32,116,111,99,111,109, -102,111,114,116,97,98,108,101,97,100,111,112,116,105,111,110,32,111,102,116,104, -114,101,101,32,121,101,97,114,115,116,104,101,32,99,111,117,110,116,114,121,105, -110,32,70,101,98,114,117,97,114,121,115,111,32,116,104,97,116,32,116,104,101,112 -,101,111,112,108,101,32,119,104,111,32,112,114,111,118,105,100,101,100,32,98,121 -,60,112,97,114,97,109,32,110,97,109,101,97,102,102,101,99,116,101,100,32,98,121, -105,110,32,116,101,114,109,115,32,111,102,97,112,112,111,105,110,116,109,101,110 -,116,73,83,79,45,56,56,53,57,45,49,34,119,97,115,32,98,111,114,110,32,105,110, -104,105,115,116,111,114,105,99,97,108,32,114,101,103,97,114,100,101,100,32,97, -115,109,101,97,115,117,114,101,109,101,110,116,105,115,32,98,97,115,101,100,32, -111,110,32,97,110,100,32,111,116,104,101,114,32,58,32,102,117,110,99,116,105,111 -,110,40,115,105,103,110,105,102,105,99,97,110,116,99,101,108,101,98,114,97,116, -105,111,110,116,114,97,110,115,109,105,116,116,101,100,47,106,115,47,106,113,117 -,101,114,121,46,105,115,32,107,110,111,119,110,32,97,115,116,104,101,111,114,101 -,116,105,99,97,108,32,116,97,98,105,110,100,101,120,61,34,105,116,32,99,111,117, -108,100,32,98,101,60,110,111,115,99,114,105,112,116,62,10,104,97,118,105,110,103 -,32,98,101,101,110,13,10,60,104,101,97,100,62,13,10,60,32,38,113,117,111,116,59, -84,104,101,32,99,111,109,112,105,108,97,116,105,111,110,104,101,32,104,97,100,32 -,98,101,101,110,112,114,111,100,117,99,101,100,32,98,121,112,104,105,108,111,115 -,111,112,104,101,114,99,111,110,115,116,114,117,99,116,101,100,105,110,116,101, -110,100,101,100,32,116,111,97,109,111,110,103,32,111,116,104,101,114,99,111,109, -112,97,114,101,100,32,116,111,116,111,32,115,97,121,32,116,104,97,116,69,110,103 -,105,110,101,101,114,105,110,103,97,32,100,105,102,102,101,114,101,110,116,114, -101,102,101,114,114,101,100,32,116,111,100,105,102,102,101,114,101,110,99,101, -115,98,101,108,105,101,102,32,116,104,97,116,112,104,111,116,111,103,114,97,112, -104,115,105,100,101,110,116,105,102,121,105,110,103,72,105,115,116,111,114,121, -32,111,102,32,82,101,112,117,98,108,105,99,32,111,102,110,101,99,101,115,115,97, -114,105,108,121,112,114,111,98,97,98,105,108,105,116,121,116,101,99,104,110,105, -99,97,108,108,121,108,101,97,118,105,110,103,32,116,104,101,115,112,101,99,116, -97,99,117,108,97,114,102,114,97,99,116,105,111,110,32,111,102,101,108,101,99,116 -,114,105,99,105,116,121,104,101,97,100,32,111,102,32,116,104,101,114,101,115,116 -,97,117,114,97,110,116,115,112,97,114,116,110,101,114,115,104,105,112,101,109, -112,104,97,115,105,115,32,111,110,109,111,115,116,32,114,101,99,101,110,116,115, -104,97,114,101,32,119,105,116,104,32,115,97,121,105,110,103,32,116,104,97,116, -102,105,108,108,101,100,32,119,105,116,104,100,101,115,105,103,110,101,100,32, -116,111,105,116,32,105,115,32,111,102,116,101,110,34,62,60,47,105,102,114,97,109 -,101,62,97,115,32,102,111,108,108,111,119,115,58,109,101,114,103,101,100,32,119, -105,116,104,116,104,114,111,117,103,104,32,116,104,101,99,111,109,109,101,114,99 -,105,97,108,32,112,111,105,110,116,101,100,32,111,117,116,111,112,112,111,114, -116,117,110,105,116,121,118,105,101,119,32,111,102,32,116,104,101,114,101,113, -117,105,114,101,109,101,110,116,100,105,118,105,115,105,111,110,32,111,102,112, -114,111,103,114,97,109,109,105,110,103,104,101,32,114,101,99,101,105,118,101,100 -,115,101,116,73,110,116,101,114,118,97,108,34,62,60,47,115,112,97,110,62,60,47, -105,110,32,78,101,119,32,89,111,114,107,97,100,100,105,116,105,111,110,97,108,32 -,99,111,109,112,114,101,115,115,105,111,110,10,10,60,100,105,118,32,105,100,61, -34,105,110,99,111,114,112,111,114,97,116,101,59,60,47,115,99,114,105,112,116,62, -60,97,116,116,97,99,104,69,118,101,110,116,98,101,99,97,109,101,32,116,104,101, -32,34,32,116,97,114,103,101,116,61,34,95,99,97,114,114,105,101,100,32,111,117, -116,83,111,109,101,32,111,102,32,116,104,101,115,99,105,101,110,99,101,32,97,110 -,100,116,104,101,32,116,105,109,101,32,111,102,67,111,110,116,97,105,110,101,114 -,34,62,109,97,105,110,116,97,105,110,105,110,103,67,104,114,105,115,116,111,112, -104,101,114,77,117,99,104,32,111,102,32,116,104,101,119,114,105,116,105,110,103, -115,32,111,102,34,32,104,101,105,103,104,116,61,34,50,115,105,122,101,32,111,102 -,32,116,104,101,118,101,114,115,105,111,110,32,111,102,32,109,105,120,116,117, -114,101,32,111,102,32,98,101,116,119,101,101,110,32,116,104,101,69,120,97,109, -112,108,101,115,32,111,102,101,100,117,99,97,116,105,111,110,97,108,99,111,109, -112,101,116,105,116,105,118,101,32,111,110,115,117,98,109,105,116,61,34,100,105, -114,101,99,116,111,114,32,111,102,100,105,115,116,105,110,99,116,105,118,101,47, -68,84,68,32,88,72,84,77,76,32,114,101,108,97,116,105,110,103,32,116,111,116,101, -110,100,101,110,99,121,32,116,111,112,114,111,118,105,110,99,101,32,111,102,119, -104,105,99,104,32,119,111,117,108,100,100,101,115,112,105,116,101,32,116,104,101 -,115,99,105,101,110,116,105,102,105,99,32,108,101,103,105,115,108,97,116,117,114 -,101,46,105,110,110,101,114,72,84,77,76,32,97,108,108,101,103,97,116,105,111,110 -,115,65,103,114,105,99,117,108,116,117,114,101,119,97,115,32,117,115,101,100,32, -105,110,97,112,112,114,111,97,99,104,32,116,111,105,110,116,101,108,108,105,103, -101,110,116,121,101,97,114,115,32,108,97,116,101,114,44,115,97,110,115,45,115, -101,114,105,102,100,101,116,101,114,109,105,110,105,110,103,80,101,114,102,111, -114,109,97,110,99,101,97,112,112,101,97,114,97,110,99,101,115,44,32,119,104,105, -99,104,32,105,115,32,102,111,117,110,100,97,116,105,111,110,115,97,98,98,114,101 -,118,105,97,116,101,100,104,105,103,104,101,114,32,116,104,97,110,115,32,102,114 -,111,109,32,116,104,101,32,105,110,100,105,118,105,100,117,97,108,32,99,111,109, -112,111,115,101,100,32,111,102,115,117,112,112,111,115,101,100,32,116,111,99,108 -,97,105,109,115,32,116,104,97,116,97,116,116,114,105,98,117,116,105,111,110,102, -111,110,116,45,115,105,122,101,58,49,101,108,101,109,101,110,116,115,32,111,102, -72,105,115,116,111,114,105,99,97,108,32,104,105,115,32,98,114,111,116,104,101, -114,97,116,32,116,104,101,32,116,105,109,101,97,110,110,105,118,101,114,115,97, -114,121,103,111,118,101,114,110,101,100,32,98,121,114,101,108,97,116,101,100,32, -116,111,32,117,108,116,105,109,97,116,101,108,121,32,105,110,110,111,118,97,116, -105,111,110,115,105,116,32,105,115,32,115,116,105,108,108,99,97,110,32,111,110, -108,121,32,98,101,100,101,102,105,110,105,116,105,111,110,115,116,111,71,77,84, -83,116,114,105,110,103,65,32,110,117,109,98,101,114,32,111,102,105,109,103,32,99 -,108,97,115,115,61,34,69,118,101,110,116,117,97,108,108,121,44,119,97,115,32,99, -104,97,110,103,101,100,111,99,99,117,114,114,101,100,32,105,110,110,101,105,103, -104,98,111,114,105,110,103,100,105,115,116,105,110,103,117,105,115,104,119,104, -101,110,32,104,101,32,119,97,115,105,110,116,114,111,100,117,99,105,110,103,116, -101,114,114,101,115,116,114,105,97,108,77,97,110,121,32,111,102,32,116,104,101, -97,114,103,117,101,115,32,116,104,97,116,97,110,32,65,109,101,114,105,99,97,110, -99,111,110,113,117,101,115,116,32,111,102,119,105,100,101,115,112,114,101,97,100 -,32,119,101,114,101,32,107,105,108,108,101,100,115,99,114,101,101,110,32,97,110, -100,32,73,110,32,111,114,100,101,114,32,116,111,101,120,112,101,99,116,101,100, -32,116,111,100,101,115,99,101,110,100,97,110,116,115,97,114,101,32,108,111,99,97 -,116,101,100,108,101,103,105,115,108,97,116,105,118,101,103,101,110,101,114,97, -116,105,111,110,115,32,98,97,99,107,103,114,111,117,110,100,109,111,115,116,32, -112,101,111,112,108,101,121,101,97,114,115,32,97,102,116,101,114,116,104,101,114 -,101,32,105,115,32,110,111,116,104,101,32,104,105,103,104,101,115,116,102,114, -101,113,117,101,110,116,108,121,32,116,104,101,121,32,100,111,32,110,111,116,97, -114,103,117,101,100,32,116,104,97,116,115,104,111,119,101,100,32,116,104,97,116, -112,114,101,100,111,109,105,110,97,110,116,116,104,101,111,108,111,103,105,99,97 -,108,98,121,32,116,104,101,32,116,105,109,101,99,111,110,115,105,100,101,114,105 -,110,103,115,104,111,114,116,45,108,105,118,101,100,60,47,115,112,97,110,62,60, -47,97,62,99,97,110,32,98,101,32,117,115,101,100,118,101,114,121,32,108,105,116, -116,108,101,111,110,101,32,111,102,32,116,104,101,32,104,97,100,32,97,108,114, -101,97,100,121,105,110,116,101,114,112,114,101,116,101,100,99,111,109,109,117, -110,105,99,97,116,101,102,101,97,116,117,114,101,115,32,111,102,103,111,118,101, -114,110,109,101,110,116,44,60,47,110,111,115,99,114,105,112,116,62,101,110,116, -101,114,101,100,32,116,104,101,34,32,104,101,105,103,104,116,61,34,51,73,110,100 -,101,112,101,110,100,101,110,116,112,111,112,117,108,97,116,105,111,110,115,108, -97,114,103,101,45,115,99,97,108,101,46,32,65,108,116,104,111,117,103,104,32,117, -115,101,100,32,105,110,32,116,104,101,100,101,115,116,114,117,99,116,105,111,110 -,112,111,115,115,105,98,105,108,105,116,121,115,116,97,114,116,105,110,103,32, -105,110,116,119,111,32,111,114,32,109,111,114,101,101,120,112,114,101,115,115, -105,111,110,115,115,117,98,111,114,100,105,110,97,116,101,108,97,114,103,101,114 -,32,116,104,97,110,104,105,115,116,111,114,121,32,97,110,100,60,47,111,112,116, -105,111,110,62,13,10,67,111,110,116,105,110,101,110,116,97,108,101,108,105,109, -105,110,97,116,105,110,103,119,105,108,108,32,110,111,116,32,98,101,112,114,97, -99,116,105,99,101,32,111,102,105,110,32,102,114,111,110,116,32,111,102,115,105, -116,101,32,111,102,32,116,104,101,101,110,115,117,114,101,32,116,104,97,116,116, -111,32,99,114,101,97,116,101,32,97,109,105,115,115,105,115,115,105,112,112,105, -112,111,116,101,110,116,105,97,108,108,121,111,117,116,115,116,97,110,100,105, -110,103,98,101,116,116,101,114,32,116,104,97,110,119,104,97,116,32,105,115,32, -110,111,119,115,105,116,117,97,116,101,100,32,105,110,109,101,116,97,32,110,97, -109,101,61,34,84,114,97,100,105,116,105,111,110,97,108,115,117,103,103,101,115, -116,105,111,110,115,84,114,97,110,115,108,97,116,105,111,110,116,104,101,32,102, -111,114,109,32,111,102,97,116,109,111,115,112,104,101,114,105,99,105,100,101,111 -,108,111,103,105,99,97,108,101,110,116,101,114,112,114,105,115,101,115,99,97,108 -,99,117,108,97,116,105,110,103,101,97,115,116,32,111,102,32,116,104,101,114,101, -109,110,97,110,116,115,32,111,102,112,108,117,103,105,110,115,112,97,103,101,47, -105,110,100,101,120,46,112,104,112,63,114,101,109,97,105,110,101,100,32,105,110, -116,114,97,110,115,102,111,114,109,101,100,72,101,32,119,97,115,32,97,108,115, -111,119,97,115,32,97,108,114,101,97,100,121,115,116,97,116,105,115,116,105,99,97 -,108,105,110,32,102,97,118,111,114,32,111,102,77,105,110,105,115,116,114,121,32, -111,102,109,111,118,101,109,101,110,116,32,111,102,102,111,114,109,117,108,97, -116,105,111,110,105,115,32,114,101,113,117,105,114,101,100,60,108,105,110,107,32 -,114,101,108,61,34,84,104,105,115,32,105,115,32,116,104,101,32,60,97,32,104,114, -101,102,61,34,47,112,111,112,117,108,97,114,105,122,101,100,105,110,118,111,108, -118,101,100,32,105,110,97,114,101,32,117,115,101,100,32,116,111,97,110,100,32, -115,101,118,101,114,97,108,109,97,100,101,32,98,121,32,116,104,101,115,101,101, -109,115,32,116,111,32,98,101,108,105,107,101,108,121,32,116,104,97,116,80,97,108 -,101,115,116,105,110,105,97,110,110,97,109,101,100,32,97,102,116,101,114,105,116 -,32,104,97,100,32,98,101,101,110,109,111,115,116,32,99,111,109,109,111,110,116, -111,32,114,101,102,101,114,32,116,111,98,117,116,32,116,104,105,115,32,105,115, -99,111,110,115,101,99,117,116,105,118,101,116,101,109,112,111,114,97,114,105,108 -,121,73,110,32,103,101,110,101,114,97,108,44,99,111,110,118,101,110,116,105,111, -110,115,116,97,107,101,115,32,112,108,97,99,101,115,117,98,100,105,118,105,115, -105,111,110,116,101,114,114,105,116,111,114,105,97,108,111,112,101,114,97,116, -105,111,110,97,108,112,101,114,109,97,110,101,110,116,108,121,119,97,115,32,108, -97,114,103,101,108,121,111,117,116,98,114,101,97,107,32,111,102,105,110,32,116, -104,101,32,112,97,115,116,102,111,108,108,111,119,105,110,103,32,97,32,120,109, -108,110,115,58,111,103,61,34,62,60,97,32,99,108,97,115,115,61,34,99,108,97,115, -115,61,34,116,101,120,116,67,111,110,118,101,114,115,105,111,110,32,109,97,121, -32,98,101,32,117,115,101,100,109,97,110,117,102,97,99,116,117,114,101,97,102,116 -,101,114,32,98,101,105,110,103,99,108,101,97,114,102,105,120,34,62,10,113,117, -101,115,116,105,111,110,32,111,102,119,97,115,32,101,108,101,99,116,101,100,116, -111,32,98,101,99,111,109,101,32,97,98,101,99,97,117,115,101,32,111,102,32,115, -111,109,101,32,112,101,111,112,108,101,105,110,115,112,105,114,101,100,32,98,121 -,115,117,99,99,101,115,115,102,117,108,32,97,32,116,105,109,101,32,119,104,101, -110,109,111,114,101,32,99,111,109,109,111,110,97,109,111,110,103,115,116,32,116, -104,101,97,110,32,111,102,102,105,99,105,97,108,119,105,100,116,104,58,49,48,48, -37,59,116,101,99,104,110,111,108,111,103,121,44,119,97,115,32,97,100,111,112,116 -,101,100,116,111,32,107,101,101,112,32,116,104,101,115,101,116,116,108,101,109, -101,110,116,115,108,105,118,101,32,98,105,114,116,104,115,105,110,100,101,120,46 -,104,116,109,108,34,67,111,110,110,101,99,116,105,99,117,116,97,115,115,105,103, -110,101,100,32,116,111,38,97,109,112,59,116,105,109,101,115,59,97,99,99,111,117, -110,116,32,102,111,114,97,108,105,103,110,61,114,105,103,104,116,116,104,101,32, -99,111,109,112,97,110,121,97,108,119,97,121,115,32,98,101,101,110,114,101,116, -117,114,110,101,100,32,116,111,105,110,118,111,108,118,101,109,101,110,116,66, -101,99,97,117,115,101,32,116,104,101,116,104,105,115,32,112,101,114,105,111,100, -34,32,110,97,109,101,61,34,113,34,32,99,111,110,102,105,110,101,100,32,116,111, -97,32,114,101,115,117,108,116,32,111,102,118,97,108,117,101,61,34,34,32,47,62, -105,115,32,97,99,116,117,97,108,108,121,69,110,118,105,114,111,110,109,101,110, -116,13,10,60,47,104,101,97,100,62,13,10,67,111,110,118,101,114,115,101,108,121, -44,62,10,60,100,105,118,32,105,100,61,34,48,34,32,119,105,100,116,104,61,34,49, -105,115,32,112,114,111,98,97,98,108,121,104,97,118,101,32,98,101,99,111,109,101, -99,111,110,116,114,111,108,108,105,110,103,116,104,101,32,112,114,111,98,108,101 -,109,99,105,116,105,122,101,110,115,32,111,102,112,111,108,105,116,105,99,105,97 -,110,115,114,101,97,99,104,101,100,32,116,104,101,97,115,32,101,97,114,108,121, -32,97,115,58,110,111,110,101,59,32,111,118,101,114,60,116,97,98,108,101,32,99, -101,108,108,118,97,108,105,100,105,116,121,32,111,102,100,105,114,101,99,116,108 -,121,32,116,111,111,110,109,111,117,115,101,100,111,119,110,119,104,101,114,101, -32,105,116,32,105,115,119,104,101,110,32,105,116,32,119,97,115,109,101,109,98, -101,114,115,32,111,102,32,114,101,108,97,116,105,111,110,32,116,111,97,99,99,111 -,109,109,111,100,97,116,101,97,108,111,110,103,32,119,105,116,104,32,73,110,32, -116,104,101,32,108,97,116,101,116,104,101,32,69,110,103,108,105,115,104,100,101, -108,105,99,105,111,117,115,34,62,116,104,105,115,32,105,115,32,110,111,116,116, -104,101,32,112,114,101,115,101,110,116,105,102,32,116,104,101,121,32,97,114,101, -97,110,100,32,102,105,110,97,108,108,121,97,32,109,97,116,116,101,114,32,111,102 -,13,10,9,60,47,100,105,118,62,13,10,13,10,60,47,115,99,114,105,112,116,62,102,97 -,115,116,101,114,32,116,104,97,110,109,97,106,111,114,105,116,121,32,111,102,97, -102,116,101,114,32,119,104,105,99,104,99,111,109,112,97,114,97,116,105,118,101, -116,111,32,109,97,105,110,116,97,105,110,105,109,112,114,111,118,101,32,116,104, -101,97,119,97,114,100,101,100,32,116,104,101,101,114,34,32,99,108,97,115,115,61, -34,102,114,97,109,101,98,111,114,100,101,114,114,101,115,116,111,114,97,116,105, -111,110,105,110,32,116,104,101,32,115,97,109,101,97,110,97,108,121,115,105,115, -32,111,102,116,104,101,105,114,32,102,105,114,115,116,68,117,114,105,110,103,32, -116,104,101,32,99,111,110,116,105,110,101,110,116,97,108,115,101,113,117,101,110 -,99,101,32,111,102,102,117,110,99,116,105,111,110,40,41,123,102,111,110,116,45, -115,105,122,101,58,32,119,111,114,107,32,111,110,32,116,104,101,60,47,115,99,114 -,105,112,116,62,10,60,98,101,103,105,110,115,32,119,105,116,104,106,97,118,97, -115,99,114,105,112,116,58,99,111,110,115,116,105,116,117,101,110,116,119,97,115, -32,102,111,117,110,100,101,100,101,113,117,105,108,105,98,114,105,117,109,97,115 -,115,117,109,101,32,116,104,97,116,105,115,32,103,105,118,101,110,32,98,121,110, -101,101,100,115,32,116,111,32,98,101,99,111,111,114,100,105,110,97,116,101,115, -116,104,101,32,118,97,114,105,111,117,115,97,114,101,32,112,97,114,116,32,111, -102,111,110,108,121,32,105,110,32,116,104,101,115,101,99,116,105,111,110,115,32, -111,102,105,115,32,97,32,99,111,109,109,111,110,116,104,101,111,114,105,101,115, -32,111,102,100,105,115,99,111,118,101,114,105,101,115,97,115,115,111,99,105,97, -116,105,111,110,101,100,103,101,32,111,102,32,116,104,101,115,116,114,101,110, -103,116,104,32,111,102,112,111,115,105,116,105,111,110,32,105,110,112,114,101, -115,101,110,116,45,100,97,121,117,110,105,118,101,114,115,97,108,108,121,116,111 -,32,102,111,114,109,32,116,104,101,98,117,116,32,105,110,115,116,101,97,100,99, -111,114,112,111,114,97,116,105,111,110,97,116,116,97,99,104,101,100,32,116,111, -105,115,32,99,111,109,109,111,110,108,121,114,101,97,115,111,110,115,32,102,111, -114,32,38,113,117,111,116,59,116,104,101,32,99,97,110,32,98,101,32,109,97,100, -101,119,97,115,32,97,98,108,101,32,116,111,119,104,105,99,104,32,109,101,97,110, -115,98,117,116,32,100,105,100,32,110,111,116,111,110,77,111,117,115,101,79,118, -101,114,97,115,32,112,111,115,115,105,98,108,101,111,112,101,114,97,116,101,100, -32,98,121,99,111,109,105,110,103,32,102,114,111,109,116,104,101,32,112,114,105, -109,97,114,121,97,100,100,105,116,105,111,110,32,111,102,102,111,114,32,115,101, -118,101,114,97,108,116,114,97,110,115,102,101,114,114,101,100,97,32,112,101,114, -105,111,100,32,111,102,97,114,101,32,97,98,108,101,32,116,111,104,111,119,101, -118,101,114,44,32,105,116,115,104,111,117,108,100,32,104,97,118,101,109,117,99, -104,32,108,97,114,103,101,114,10,9,60,47,115,99,114,105,112,116,62,97,100,111, -112,116,101,100,32,116,104,101,112,114,111,112,101,114,116,121,32,111,102,100, -105,114,101,99,116,101,100,32,98,121,101,102,102,101,99,116,105,118,101,108,121, -119,97,115,32,98,114,111,117,103,104,116,99,104,105,108,100,114,101,110,32,111, -102,80,114,111,103,114,97,109,109,105,110,103,108,111,110,103,101,114,32,116,104 -,97,110,109,97,110,117,115,99,114,105,112,116,115,119,97,114,32,97,103,97,105, -110,115,116,98,121,32,109,101,97,110,115,32,111,102,97,110,100,32,109,111,115, -116,32,111,102,115,105,109,105,108,97,114,32,116,111,32,112,114,111,112,114,105, -101,116,97,114,121,111,114,105,103,105,110,97,116,105,110,103,112,114,101,115, -116,105,103,105,111,117,115,103,114,97,109,109,97,116,105,99,97,108,101,120,112, -101,114,105,101,110,99,101,46,116,111,32,109,97,107,101,32,116,104,101,73,116,32 -,119,97,115,32,97,108,115,111,105,115,32,102,111,117,110,100,32,105,110,99,111, -109,112,101,116,105,116,111,114,115,105,110,32,116,104,101,32,85,46,83,46,114, -101,112,108,97,99,101,32,116,104,101,98,114,111,117,103,104,116,32,116,104,101, -99,97,108,99,117,108,97,116,105,111,110,102,97,108,108,32,111,102,32,116,104,101 -,116,104,101,32,103,101,110,101,114,97,108,112,114,97,99,116,105,99,97,108,108, -121,105,110,32,104,111,110,111,114,32,111,102,114,101,108,101,97,115,101,100,32, -105,110,114,101,115,105,100,101,110,116,105,97,108,97,110,100,32,115,111,109,101 -,32,111,102,107,105,110,103,32,111,102,32,116,104,101,114,101,97,99,116,105,111, -110,32,116,111,49,115,116,32,69,97,114,108,32,111,102,99,117,108,116,117,114,101 -,32,97,110,100,112,114,105,110,99,105,112,97,108,108,121,60,47,116,105,116,108, -101,62,10,32,32,116,104,101,121,32,99,97,110,32,98,101,98,97,99,107,32,116,111, -32,116,104,101,115,111,109,101,32,111,102,32,104,105,115,101,120,112,111,115,117 -,114,101,32,116,111,97,114,101,32,115,105,109,105,108,97,114,102,111,114,109,32, -111,102,32,116,104,101,97,100,100,70,97,118,111,114,105,116,101,99,105,116,105, -122,101,110,115,104,105,112,112,97,114,116,32,105,110,32,116,104,101,112,101,111 -,112,108,101,32,119,105,116,104,105,110,32,112,114,97,99,116,105,99,101,116,111, -32,99,111,110,116,105,110,117,101,38,97,109,112,59,109,105,110,117,115,59,97,112 -,112,114,111,118,101,100,32,98,121,32,116,104,101,32,102,105,114,115,116,32,97, -108,108,111,119,101,100,32,116,104,101,97,110,100,32,102,111,114,32,116,104,101, -102,117,110,99,116,105,111,110,105,110,103,112,108,97,121,105,110,103,32,116,104 -,101,115,111,108,117,116,105,111,110,32,116,111,104,101,105,103,104,116,61,34,48 -,34,32,105,110,32,104,105,115,32,98,111,111,107,109,111,114,101,32,116,104,97, -110,32,97,102,111,108,108,111,119,115,32,116,104,101,99,114,101,97,116,101,100, -32,116,104,101,112,114,101,115,101,110,99,101,32,105,110,38,110,98,115,112,59,60 -,47,116,100,62,110,97,116,105,111,110,97,108,105,115,116,116,104,101,32,105,100, -101,97,32,111,102,97,32,99,104,97,114,97,99,116,101,114,119,101,114,101,32,102, -111,114,99,101,100,32,99,108,97,115,115,61,34,98,116,110,100,97,121,115,32,111, -102,32,116,104,101,102,101,97,116,117,114,101,100,32,105,110,115,104,111,119,105 -,110,103,32,116,104,101,105,110,116,101,114,101,115,116,32,105,110,105,110,32, -112,108,97,99,101,32,111,102,116,117,114,110,32,111,102,32,116,104,101,116,104, -101,32,104,101,97,100,32,111,102,76,111,114,100,32,111,102,32,116,104,101,112, -111,108,105,116,105,99,97,108,108,121,104,97,115,32,105,116,115,32,111,119,110, -69,100,117,99,97,116,105,111,110,97,108,97,112,112,114,111,118,97,108,32,111,102 -,115,111,109,101,32,111,102,32,116,104,101,101,97,99,104,32,111,116,104,101,114, -44,98,101,104,97,118,105,111,114,32,111,102,97,110,100,32,98,101,99,97,117,115, -101,97,110,100,32,97,110,111,116,104,101,114,97,112,112,101,97,114,101,100,32, -111,110,114,101,99,111,114,100,101,100,32,105,110,98,108,97,99,107,38,113,117, -111,116,59,109,97,121,32,105,110,99,108,117,100,101,116,104,101,32,119,111,114, -108,100,39,115,99,97,110,32,108,101,97,100,32,116,111,114,101,102,101,114,115,32 -,116,111,32,97,98,111,114,100,101,114,61,34,48,34,32,103,111,118,101,114,110,109 -,101,110,116,32,119,105,110,110,105,110,103,32,116,104,101,114,101,115,117,108, -116,101,100,32,105,110,32,119,104,105,108,101,32,116,104,101,32,87,97,115,104, -105,110,103,116,111,110,44,116,104,101,32,115,117,98,106,101,99,116,99,105,116, -121,32,105,110,32,116,104,101,62,60,47,100,105,118,62,13,10,9,9,114,101,102,108, -101,99,116,32,116,104,101,116,111,32,99,111,109,112,108,101,116,101,98,101,99,97 -,109,101,32,109,111,114,101,114,97,100,105,111,97,99,116,105,118,101,114,101,106 -,101,99,116,101,100,32,98,121,119,105,116,104,111,117,116,32,97,110,121,104,105, -115,32,102,97,116,104,101,114,44,119,104,105,99,104,32,99,111,117,108,100,99,111 -,112,121,32,111,102,32,116,104,101,116,111,32,105,110,100,105,99,97,116,101,97, -32,112,111,108,105,116,105,99,97,108,97,99,99,111,117,110,116,115,32,111,102,99, -111,110,115,116,105,116,117,116,101,115,119,111,114,107,101,100,32,119,105,116, -104,101,114,60,47,97,62,60,47,108,105,62,111,102,32,104,105,115,32,108,105,102, -101,97,99,99,111,109,112,97,110,105,101,100,99,108,105,101,110,116,87,105,100, -116,104,112,114,101,118,101,110,116,32,116,104,101,76,101,103,105,115,108,97,116 -,105,118,101,100,105,102,102,101,114,101,110,116,108,121,116,111,103,101,116,104 -,101,114,32,105,110,104,97,115,32,115,101,118,101,114,97,108,102,111,114,32,97, -110,111,116,104,101,114,116,101,120,116,32,111,102,32,116,104,101,102,111,117, -110,100,101,100,32,116,104,101,101,32,119,105,116,104,32,116,104,101,32,105,115, -32,117,115,101,100,32,102,111,114,99,104,97,110,103,101,100,32,116,104,101,117, -115,117,97,108,108,121,32,116,104,101,112,108,97,99,101,32,119,104,101,114,101, -119,104,101,114,101,97,115,32,116,104,101,62,32,60,97,32,104,114,101,102,61,34, -34,62,60,97,32,104,114,101,102,61,34,116,104,101,109,115,101,108,118,101,115,44, -97,108,116,104,111,117,103,104,32,104,101,116,104,97,116,32,99,97,110,32,98,101, -116,114,97,100,105,116,105,111,110,97,108,114,111,108,101,32,111,102,32,116,104, -101,97,115,32,97,32,114,101,115,117,108,116,114,101,109,111,118,101,67,104,105, -108,100,100,101,115,105,103,110,101,100,32,98,121,119,101,115,116,32,111,102,32, -116,104,101,83,111,109,101,32,112,101,111,112,108,101,112,114,111,100,117,99,116 -,105,111,110,44,115,105,100,101,32,111,102,32,116,104,101,110,101,119,115,108, -101,116,116,101,114,115,117,115,101,100,32,98,121,32,116,104,101,100,111,119,110 -,32,116,111,32,116,104,101,97,99,99,101,112,116,101,100,32,98,121,108,105,118, -101,32,105,110,32,116,104,101,97,116,116,101,109,112,116,115,32,116,111,111,117, -116,115,105,100,101,32,116,104,101,102,114,101,113,117,101,110,99,105,101,115,72 -,111,119,101,118,101,114,44,32,105,110,112,114,111,103,114,97,109,109,101,114, -115,97,116,32,108,101,97,115,116,32,105,110,97,112,112,114,111,120,105,109,97, -116,101,97,108,116,104,111,117,103,104,32,105,116,119,97,115,32,112,97,114,116, -32,111,102,97,110,100,32,118,97,114,105,111,117,115,71,111,118,101,114,110,111, -114,32,111,102,116,104,101,32,97,114,116,105,99,108,101,116,117,114,110,101,100, -32,105,110,116,111,62,60,97,32,104,114,101,102,61,34,47,116,104,101,32,101,99, -111,110,111,109,121,105,115,32,116,104,101,32,109,111,115,116,109,111,115,116,32 -,119,105,100,101,108,121,119,111,117,108,100,32,108,97,116,101,114,97,110,100,32 -,112,101,114,104,97,112,115,114,105,115,101,32,116,111,32,116,104,101,111,99,99, -117,114,115,32,119,104,101,110,117,110,100,101,114,32,119,104,105,99,104,99,111, -110,100,105,116,105,111,110,115,46,116,104,101,32,119,101,115,116,101,114,110, -116,104,101,111,114,121,32,116,104,97,116,105,115,32,112,114,111,100,117,99,101, -100,116,104,101,32,99,105,116,121,32,111,102,105,110,32,119,104,105,99,104,32, -104,101,115,101,101,110,32,105,110,32,116,104,101,116,104,101,32,99,101,110,116, -114,97,108,98,117,105,108,100,105,110,103,32,111,102,109,97,110,121,32,111,102, -32,104,105,115,97,114,101,97,32,111,102,32,116,104,101,105,115,32,116,104,101,32 -,111,110,108,121,109,111,115,116,32,111,102,32,116,104,101,109,97,110,121,32,111 -,102,32,116,104,101,116,104,101,32,87,101,115,116,101,114,110,84,104,101,114,101 -,32,105,115,32,110,111,101,120,116,101,110,100,101,100,32,116,111,83,116,97,116, -105,115,116,105,99,97,108,99,111,108,115,112,97,110,61,50,32,124,115,104,111,114 -,116,32,115,116,111,114,121,112,111,115,115,105,98,108,101,32,116,111,116,111, -112,111,108,111,103,105,99,97,108,99,114,105,116,105,99,97,108,32,111,102,114, -101,112,111,114,116,101,100,32,116,111,97,32,67,104,114,105,115,116,105,97,110, -100,101,99,105,115,105,111,110,32,116,111,105,115,32,101,113,117,97,108,32,116, -111,112,114,111,98,108,101,109,115,32,111,102,84,104,105,115,32,99,97,110,32,98, -101,109,101,114,99,104,97,110,100,105,115,101,102,111,114,32,109,111,115,116,32, -111,102,110,111,32,101,118,105,100,101,110,99,101,101,100,105,116,105,111,110, -115,32,111,102,101,108,101,109,101,110,116,115,32,105,110,38,113,117,111,116,59, -46,32,84,104,101,99,111,109,47,105,109,97,103,101,115,47,119,104,105,99,104,32, -109,97,107,101,115,116,104,101,32,112,114,111,99,101,115,115,114,101,109,97,105, -110,115,32,116,104,101,108,105,116,101,114,97,116,117,114,101,44,105,115,32,97, -32,109,101,109,98,101,114,116,104,101,32,112,111,112,117,108,97,114,116,104,101, -32,97,110,99,105,101,110,116,112,114,111,98,108,101,109,115,32,105,110,116,105, -109,101,32,111,102,32,116,104,101,100,101,102,101,97,116,101,100,32,98,121,98, -111,100,121,32,111,102,32,116,104,101,97,32,102,101,119,32,121,101,97,114,115, -109,117,99,104,32,111,102,32,116,104,101,116,104,101,32,119,111,114,107,32,111, -102,67,97,108,105,102,111,114,110,105,97,44,115,101,114,118,101,100,32,97,115,32 -,97,103,111,118,101,114,110,109,101,110,116,46,99,111,110,99,101,112,116,115,32, -111,102,109,111,118,101,109,101,110,116,32,105,110,9,9,60,100,105,118,32,105,100 -,61,34,105,116,34,32,118,97,108,117,101,61,34,108,97,110,103,117,97,103,101,32, -111,102,97,115,32,116,104,101,121,32,97,114,101,112,114,111,100,117,99,101,100, -32,105,110,105,115,32,116,104,97,116,32,116,104,101,101,120,112,108,97,105,110, -32,116,104,101,100,105,118,62,60,47,100,105,118,62,10,72,111,119,101,118,101,114 -,32,116,104,101,108,101,97,100,32,116,111,32,116,104,101,9,60,97,32,104,114,101, -102,61,34,47,119,97,115,32,103,114,97,110,116,101,100,112,101,111,112,108,101,32 -,104,97,118,101,99,111,110,116,105,110,117,97,108,108,121,119,97,115,32,115,101, -101,110,32,97,115,97,110,100,32,114,101,108,97,116,101,100,116,104,101,32,114, -111,108,101,32,111,102,112,114,111,112,111,115,101,100,32,98,121,111,102,32,116, -104,101,32,98,101,115,116,101,97,99,104,32,111,116,104,101,114,46,67,111,110,115 -,116,97,110,116,105,110,101,112,101,111,112,108,101,32,102,114,111,109,100,105, -97,108,101,99,116,115,32,111,102,116,111,32,114,101,118,105,115,105,111,110,119, -97,115,32,114,101,110,97,109,101,100,97,32,115,111,117,114,99,101,32,111,102,116 -,104,101,32,105,110,105,116,105,97,108,108,97,117,110,99,104,101,100,32,105,110, -112,114,111,118,105,100,101,32,116,104,101,116,111,32,116,104,101,32,119,101,115 -,116,119,104,101,114,101,32,116,104,101,114,101,97,110,100,32,115,105,109,105, -108,97,114,98,101,116,119,101,101,110,32,116,119,111,105,115,32,97,108,115,111, -32,116,104,101,69,110,103,108,105,115,104,32,97,110,100,99,111,110,100,105,116, -105,111,110,115,44,116,104,97,116,32,105,116,32,119,97,115,101,110,116,105,116, -108,101,100,32,116,111,116,104,101,109,115,101,108,118,101,115,46,113,117,97,110 -,116,105,116,121,32,111,102,114,97,110,115,112,97,114,101,110,99,121,116,104,101 -,32,115,97,109,101,32,97,115,116,111,32,106,111,105,110,32,116,104,101,99,111, -117,110,116,114,121,32,97,110,100,116,104,105,115,32,105,115,32,116,104,101,84, -104,105,115,32,108,101,100,32,116,111,97,32,115,116,97,116,101,109,101,110,116, -99,111,110,116,114,97,115,116,32,116,111,108,97,115,116,73,110,100,101,120,79, -102,116,104,114,111,117,103,104,32,104,105,115,105,115,32,100,101,115,105,103, -110,101,100,116,104,101,32,116,101,114,109,32,105,115,105,115,32,112,114,111,118 -,105,100,101,100,112,114,111,116,101,99,116,32,116,104,101,110,103,60,47,97,62, -60,47,108,105,62,84,104,101,32,99,117,114,114,101,110,116,116,104,101,32,115,105 -,116,101,32,111,102,115,117,98,115,116,97,110,116,105,97,108,101,120,112,101,114 -,105,101,110,99,101,44,105,110,32,116,104,101,32,87,101,115,116,116,104,101,121, -32,115,104,111,117,108,100,115,108,111,118,101,110,196,141,105,110,97,99,111,109 -,101,110,116,97,114,105,111,115,117,110,105,118,101,114,115,105,100,97,100,99, -111,110,100,105,99,105,111,110,101,115,97,99,116,105,118,105,100,97,100,101,115, -101,120,112,101,114,105,101,110,99,105,97,116,101,99,110,111,108,111,103,195,173 -,97,112,114,111,100,117,99,99,105,195,179,110,112,117,110,116,117,97,99,105,195, -179,110,97,112,108,105,99,97,99,105,195,179,110,99,111,110,116,114,97,115,101, -195,177,97,99,97,116,101,103,111,114,195,173,97,115,114,101,103,105,115,116,114, -97,114,115,101,112,114,111,102,101,115,105,111,110,97,108,116,114,97,116,97,109, -105,101,110,116,111,114,101,103,195,173,115,116,114,97,116,101,115,101,99,114, -101,116,97,114,195,173,97,112,114,105,110,99,105,112,97,108,101,115,112,114,111, -116,101,99,99,105,195,179,110,105,109,112,111,114,116,97,110,116,101,115,105,109 -,112,111,114,116,97,110,99,105,97,112,111,115,105,98,105,108,105,100,97,100,105, -110,116,101,114,101,115,97,110,116,101,99,114,101,99,105,109,105,101,110,116,111 -,110,101,99,101,115,105,100,97,100,101,115,115,117,115,99,114,105,98,105,114,115 -,101,97,115,111,99,105,97,99,105,195,179,110,100,105,115,112,111,110,105,98,108, -101,115,101,118,97,108,117,97,99,105,195,179,110,101,115,116,117,100,105,97,110, -116,101,115,114,101,115,112,111,110,115,97,98,108,101,114,101,115,111,108,117,99 -,105,195,179,110,103,117,97,100,97,108,97,106,97,114,97,114,101,103,105,115,116, -114,97,100,111,115,111,112,111,114,116,117,110,105,100,97,100,99,111,109,101,114 -,99,105,97,108,101,115,102,111,116,111,103,114,97,102,195,173,97,97,117,116,111, -114,105,100,97,100,101,115,105,110,103,101,110,105,101,114,195,173,97,116,101, -108,101,118,105,115,105,195,179,110,99,111,109,112,101,116,101,110,99,105,97,111 -,112,101,114,97,99,105,111,110,101,115,101,115,116,97,98,108,101,99,105,100,111, -115,105,109,112,108,101,109,101,110,116,101,97,99,116,117,97,108,109,101,110,116 -,101,110,97,118,101,103,97,99,105,195,179,110,99,111,110,102,111,114,109,105,100 -,97,100,108,105,110,101,45,104,101,105,103,104,116,58,102,111,110,116,45,102,97, -109,105,108,121,58,34,32,58,32,34,104,116,116,112,58,47,47,97,112,112,108,105,99 -,97,116,105,111,110,115,108,105,110,107,34,32,104,114,101,102,61,34,115,112,101, -99,105,102,105,99,97,108,108,121,47,47,60,33,91,67,68,65,84,65,91,10,79,114,103, -97,110,105,122,97,116,105,111,110,100,105,115,116,114,105,98,117,116,105,111,110 -,48,112,120,59,32,104,101,105,103,104,116,58,114,101,108,97,116,105,111,110,115, -104,105,112,100,101,118,105,99,101,45,119,105,100,116,104,60,100,105,118,32,99, -108,97,115,115,61,34,60,108,97,98,101,108,32,102,111,114,61,34,114,101,103,105, -115,116,114,97,116,105,111,110,60,47,110,111,115,99,114,105,112,116,62,10,47,105 -,110,100,101,120,46,104,116,109,108,34,119,105,110,100,111,119,46,111,112,101, -110,40,32,33,105,109,112,111,114,116,97,110,116,59,97,112,112,108,105,99,97,116, -105,111,110,47,105,110,100,101,112,101,110,100,101,110,99,101,47,47,119,119,119, -46,103,111,111,103,108,101,111,114,103,97,110,105,122,97,116,105,111,110,97,117, -116,111,99,111,109,112,108,101,116,101,114,101,113,117,105,114,101,109,101,110, -116,115,99,111,110,115,101,114,118,97,116,105,118,101,60,102,111,114,109,32,110, -97,109,101,61,34,105,110,116,101,108,108,101,99,116,117,97,108,109,97,114,103, -105,110,45,108,101,102,116,58,49,56,116,104,32,99,101,110,116,117,114,121,97,110 -,32,105,109,112,111,114,116,97,110,116,105,110,115,116,105,116,117,116,105,111, -110,115,97,98,98,114,101,118,105,97,116,105,111,110,60,105,109,103,32,99,108,97, -115,115,61,34,111,114,103,97,110,105,115,97,116,105,111,110,99,105,118,105,108, -105,122,97,116,105,111,110,49,57,116,104,32,99,101,110,116,117,114,121,97,114,99 -,104,105,116,101,99,116,117,114,101,105,110,99,111,114,112,111,114,97,116,101, -100,50,48,116,104,32,99,101,110,116,117,114,121,45,99,111,110,116,97,105,110,101 -,114,34,62,109,111,115,116,32,110,111,116,97,98,108,121,47,62,60,47,97,62,60,47, -100,105,118,62,110,111,116,105,102,105,99,97,116,105,111,110,39,117,110,100,101, -102,105,110,101,100,39,41,70,117,114,116,104,101,114,109,111,114,101,44,98,101, -108,105,101,118,101,32,116,104,97,116,105,110,110,101,114,72,84,77,76,32,61,32, -112,114,105,111,114,32,116,111,32,116,104,101,100,114,97,109,97,116,105,99,97, -108,108,121,114,101,102,101,114,114,105,110,103,32,116,111,110,101,103,111,116, -105,97,116,105,111,110,115,104,101,97,100,113,117,97,114,116,101,114,115,83,111, -117,116,104,32,65,102,114,105,99,97,117,110,115,117,99,99,101,115,115,102,117, -108,80,101,110,110,115,121,108,118,97,110,105,97,65,115,32,97,32,114,101,115,117 -,108,116,44,60,104,116,109,108,32,108,97,110,103,61,34,38,108,116,59,47,115,117, -112,38,103,116,59,100,101,97,108,105,110,103,32,119,105,116,104,112,104,105,108, -97,100,101,108,112,104,105,97,104,105,115,116,111,114,105,99,97,108,108,121,41, -59,60,47,115,99,114,105,112,116,62,10,112,97,100,100,105,110,103,45,116,111,112, -58,101,120,112,101,114,105,109,101,110,116,97,108,103,101,116,65,116,116,114,105 -,98,117,116,101,105,110,115,116,114,117,99,116,105,111,110,115,116,101,99,104, -110,111,108,111,103,105,101,115,112,97,114,116,32,111,102,32,116,104,101,32,61, -102,117,110,99,116,105,111,110,40,41,123,115,117,98,115,99,114,105,112,116,105, -111,110,108,46,100,116,100,34,62,13,10,60,104,116,103,101,111,103,114,97,112,104 -,105,99,97,108,67,111,110,115,116,105,116,117,116,105,111,110,39,44,32,102,117, -110,99,116,105,111,110,40,115,117,112,112,111,114,116,101,100,32,98,121,97,103, -114,105,99,117,108,116,117,114,97,108,99,111,110,115,116,114,117,99,116,105,111, -110,112,117,98,108,105,99,97,116,105,111,110,115,102,111,110,116,45,115,105,122, -101,58,32,49,97,32,118,97,114,105,101,116,121,32,111,102,60,100,105,118,32,115, -116,121,108,101,61,34,69,110,99,121,99,108,111,112,101,100,105,97,105,102,114,97 -,109,101,32,115,114,99,61,34,100,101,109,111,110,115,116,114,97,116,101,100,97, -99,99,111,109,112,108,105,115,104,101,100,117,110,105,118,101,114,115,105,116, -105,101,115,68,101,109,111,103,114,97,112,104,105,99,115,41,59,60,47,115,99,114, -105,112,116,62,60,100,101,100,105,99,97,116,101,100,32,116,111,107,110,111,119, -108,101,100,103,101,32,111,102,115,97,116,105,115,102,97,99,116,105,111,110,112, -97,114,116,105,99,117,108,97,114,108,121,60,47,100,105,118,62,60,47,100,105,118, -62,69,110,103,108,105,115,104,32,40,85,83,41,97,112,112,101,110,100,67,104,105, -108,100,40,116,114,97,110,115,109,105,115,115,105,111,110,115,46,32,72,111,119, -101,118,101,114,44,32,105,110,116,101,108,108,105,103,101,110,99,101,34,32,116, -97,98,105,110,100,101,120,61,34,102,108,111,97,116,58,114,105,103,104,116,59,67, -111,109,109,111,110,119,101,97,108,116,104,114,97,110,103,105,110,103,32,102,114 -,111,109,105,110,32,119,104,105,99,104,32,116,104,101,97,116,32,108,101,97,115, -116,32,111,110,101,114,101,112,114,111,100,117,99,116,105,111,110,101,110,99,121 -,99,108,111,112,101,100,105,97,59,102,111,110,116,45,115,105,122,101,58,49,106, -117,114,105,115,100,105,99,116,105,111,110,97,116,32,116,104,97,116,32,116,105, -109,101,34,62,60,97,32,99,108,97,115,115,61,34,73,110,32,97,100,100,105,116,105, -111,110,44,100,101,115,99,114,105,112,116,105,111,110,43,99,111,110,118,101,114, -115,97,116,105,111,110,99,111,110,116,97,99,116,32,119,105,116,104,105,115,32, -103,101,110,101,114,97,108,108,121,114,34,32,99,111,110,116,101,110,116,61,34, -114,101,112,114,101,115,101,110,116,105,110,103,38,108,116,59,109,97,116,104,38, -103,116,59,112,114,101,115,101,110,116,97,116,105,111,110,111,99,99,97,115,105, -111,110,97,108,108,121,60,105,109,103,32,119,105,100,116,104,61,34,110,97,118, -105,103,97,116,105,111,110,34,62,99,111,109,112,101,110,115,97,116,105,111,110, -99,104,97,109,112,105,111,110,115,104,105,112,109,101,100,105,97,61,34,97,108, -108,34,32,118,105,111,108,97,116,105,111,110,32,111,102,114,101,102,101,114,101, -110,99,101,32,116,111,114,101,116,117,114,110,32,116,114,117,101,59,83,116,114, -105,99,116,47,47,69,78,34,32,116,114,97,110,115,97,99,116,105,111,110,115,105, -110,116,101,114,118,101,110,116,105,111,110,118,101,114,105,102,105,99,97,116, -105,111,110,73,110,102,111,114,109,97,116,105,111,110,32,100,105,102,102,105,99, -117,108,116,105,101,115,67,104,97,109,112,105,111,110,115,104,105,112,99,97,112, -97,98,105,108,105,116,105,101,115,60,33,91,101,110,100,105,102,93,45,45,62,125, -10,60,47,115,99,114,105,112,116,62,10,67,104,114,105,115,116,105,97,110,105,116, -121,102,111,114,32,101,120,97,109,112,108,101,44,80,114,111,102,101,115,115,105, -111,110,97,108,114,101,115,116,114,105,99,116,105,111,110,115,115,117,103,103, -101,115,116,32,116,104,97,116,119,97,115,32,114,101,108,101,97,115,101,100,40, -115,117,99,104,32,97,115,32,116,104,101,114,101,109,111,118,101,67,108,97,115, -115,40,117,110,101,109,112,108,111,121,109,101,110,116,116,104,101,32,65,109,101 -,114,105,99,97,110,115,116,114,117,99,116,117,114,101,32,111,102,47,105,110,100, -101,120,46,104,116,109,108,32,112,117,98,108,105,115,104,101,100,32,105,110,115, -112,97,110,32,99,108,97,115,115,61,34,34,62,60,97,32,104,114,101,102,61,34,47, -105,110,116,114,111,100,117,99,116,105,111,110,98,101,108,111,110,103,105,110, -103,32,116,111,99,108,97,105,109,101,100,32,116,104,97,116,99,111,110,115,101, -113,117,101,110,99,101,115,60,109,101,116,97,32,110,97,109,101,61,34,71,117,105, -100,101,32,116,111,32,116,104,101,111,118,101,114,119,104,101,108,109,105,110, -103,97,103,97,105,110,115,116,32,116,104,101,32,99,111,110,99,101,110,116,114,97 -,116,101,100,44,10,46,110,111,110,116,111,117,99,104,32,111,98,115,101,114,118, -97,116,105,111,110,115,60,47,97,62,10,60,47,100,105,118,62,10,102,32,40,100,111, -99,117,109,101,110,116,46,98,111,114,100,101,114,58,32,49,112,120,32,123,102,111 -,110,116,45,115,105,122,101,58,49,116,114,101,97,116,109,101,110,116,32,111,102, -48,34,32,104,101,105,103,104,116,61,34,49,109,111,100,105,102,105,99,97,116,105, -111,110,73,110,100,101,112,101,110,100,101,110,99,101,100,105,118,105,100,101, -100,32,105,110,116,111,103,114,101,97,116,101,114,32,116,104,97,110,97,99,104, -105,101,118,101,109,101,110,116,115,101,115,116,97,98,108,105,115,104,105,110, -103,74,97,118,97,83,99,114,105,112,116,34,32,110,101,118,101,114,116,104,101,108 -,101,115,115,115,105,103,110,105,102,105,99,97,110,99,101,66,114,111,97,100,99, -97,115,116,105,110,103,62,38,110,98,115,112,59,60,47,116,100,62,99,111,110,116, -97,105,110,101,114,34,62,10,115,117,99,104,32,97,115,32,116,104,101,32,105,110, -102,108,117,101,110,99,101,32,111,102,97,32,112,97,114,116,105,99,117,108,97,114 -,115,114,99,61,39,104,116,116,112,58,47,47,110,97,118,105,103,97,116,105,111,110 -,34,32,104,97,108,102,32,111,102,32,116,104,101,32,115,117,98,115,116,97,110,116 -,105,97,108,32,38,110,98,115,112,59,60,47,100,105,118,62,97,100,118,97,110,116, -97,103,101,32,111,102,100,105,115,99,111,118,101,114,121,32,111,102,102,117,110, -100,97,109,101,110,116,97,108,32,109,101,116,114,111,112,111,108,105,116,97,110, -116,104,101,32,111,112,112,111,115,105,116,101,34,32,120,109,108,58,108,97,110, -103,61,34,100,101,108,105,98,101,114,97,116,101,108,121,97,108,105,103,110,61,99 -,101,110,116,101,114,101,118,111,108,117,116,105,111,110,32,111,102,112,114,101, -115,101,114,118,97,116,105,111,110,105,109,112,114,111,118,101,109,101,110,116, -115,98,101,103,105,110,110,105,110,103,32,105,110,74,101,115,117,115,32,67,104, -114,105,115,116,80,117,98,108,105,99,97,116,105,111,110,115,100,105,115,97,103, -114,101,101,109,101,110,116,116,101,120,116,45,97,108,105,103,110,58,114,44,32, -102,117,110,99,116,105,111,110,40,41,115,105,109,105,108,97,114,105,116,105,101, -115,98,111,100,121,62,60,47,104,116,109,108,62,105,115,32,99,117,114,114,101,110 -,116,108,121,97,108,112,104,97,98,101,116,105,99,97,108,105,115,32,115,111,109, -101,116,105,109,101,115,116,121,112,101,61,34,105,109,97,103,101,47,109,97,110, -121,32,111,102,32,116,104,101,32,102,108,111,119,58,104,105,100,100,101,110,59, -97,118,97,105,108,97,98,108,101,32,105,110,100,101,115,99,114,105,98,101,32,116, -104,101,101,120,105,115,116,101,110,99,101,32,111,102,97,108,108,32,111,118,101, -114,32,116,104,101,116,104,101,32,73,110,116,101,114,110,101,116,9,60,117,108,32 -,99,108,97,115,115,61,34,105,110,115,116,97,108,108,97,116,105,111,110,110,101, -105,103,104,98,111,114,104,111,111,100,97,114,109,101,100,32,102,111,114,99,101, -115,114,101,100,117,99,105,110,103,32,116,104,101,99,111,110,116,105,110,117,101 -,115,32,116,111,78,111,110,101,116,104,101,108,101,115,115,44,116,101,109,112, -101,114,97,116,117,114,101,115,10,9,9,60,97,32,104,114,101,102,61,34,99,108,111, -115,101,32,116,111,32,116,104,101,101,120,97,109,112,108,101,115,32,111,102,32, -105,115,32,97,98,111,117,116,32,116,104,101,40,115,101,101,32,98,101,108,111,119 -,41,46,34,32,105,100,61,34,115,101,97,114,99,104,112,114,111,102,101,115,115,105 -,111,110,97,108,105,115,32,97,118,97,105,108,97,98,108,101,116,104,101,32,111, -102,102,105,99,105,97,108,9,9,60,47,115,99,114,105,112,116,62,10,10,9,9,60,100, -105,118,32,105,100,61,34,97,99,99,101,108,101,114,97,116,105,111,110,116,104,114 -,111,117,103,104,32,116,104,101,32,72,97,108,108,32,111,102,32,70,97,109,101,100 -,101,115,99,114,105,112,116,105,111,110,115,116,114,97,110,115,108,97,116,105, -111,110,115,105,110,116,101,114,102,101,114,101,110,99,101,32,116,121,112,101,61 -,39,116,101,120,116,47,114,101,99,101,110,116,32,121,101,97,114,115,105,110,32, -116,104,101,32,119,111,114,108,100,118,101,114,121,32,112,111,112,117,108,97,114 -,123,98,97,99,107,103,114,111,117,110,100,58,116,114,97,100,105,116,105,111,110, -97,108,32,115,111,109,101,32,111,102,32,116,104,101,32,99,111,110,110,101,99,116 -,101,100,32,116,111,101,120,112,108,111,105,116,97,116,105,111,110,101,109,101, -114,103,101,110,99,101,32,111,102,99,111,110,115,116,105,116,117,116,105,111,110 -,65,32,72,105,115,116,111,114,121,32,111,102,115,105,103,110,105,102,105,99,97, -110,116,32,109,97,110,117,102,97,99,116,117,114,101,100,101,120,112,101,99,116, -97,116,105,111,110,115,62,60,110,111,115,99,114,105,112,116,62,60,99,97,110,32, -98,101,32,102,111,117,110,100,98,101,99,97,117,115,101,32,116,104,101,32,104,97, -115,32,110,111,116,32,98,101,101,110,110,101,105,103,104,98,111,117,114,105,110, -103,119,105,116,104,111,117,116,32,116,104,101,32,97,100,100,101,100,32,116,111, -32,116,104,101,9,60,108,105,32,99,108,97,115,115,61,34,105,110,115,116,114,117, -109,101,110,116,97,108,83,111,118,105,101,116,32,85,110,105,111,110,97,99,107, -110,111,119,108,101,100,103,101,100,119,104,105,99,104,32,99,97,110,32,98,101, -110,97,109,101,32,102,111,114,32,116,104,101,97,116,116,101,110,116,105,111,110, -32,116,111,97,116,116,101,109,112,116,115,32,116,111,32,100,101,118,101,108,111, -112,109,101,110,116,115,73,110,32,102,97,99,116,44,32,116,104,101,60,108,105,32, -99,108,97,115,115,61,34,97,105,109,112,108,105,99,97,116,105,111,110,115,115,117 -,105,116,97,98,108,101,32,102,111,114,109,117,99,104,32,111,102,32,116,104,101, -32,99,111,108,111,110,105,122,97,116,105,111,110,112,114,101,115,105,100,101,110 -,116,105,97,108,99,97,110,99,101,108,66,117,98,98,108,101,32,73,110,102,111,114, -109,97,116,105,111,110,109,111,115,116,32,111,102,32,116,104,101,32,105,115,32, -100,101,115,99,114,105,98,101,100,114,101,115,116,32,111,102,32,116,104,101,32, -109,111,114,101,32,111,114,32,108,101,115,115,105,110,32,83,101,112,116,101,109, -98,101,114,73,110,116,101,108,108,105,103,101,110,99,101,115,114,99,61,34,104, -116,116,112,58,47,47,112,120,59,32,104,101,105,103,104,116,58,32,97,118,97,105, -108,97,98,108,101,32,116,111,109,97,110,117,102,97,99,116,117,114,101,114,104, -117,109,97,110,32,114,105,103,104,116,115,108,105,110,107,32,104,114,101,102,61, -34,47,97,118,97,105,108,97,98,105,108,105,116,121,112,114,111,112,111,114,116, -105,111,110,97,108,111,117,116,115,105,100,101,32,116,104,101,32,97,115,116,114, -111,110,111,109,105,99,97,108,104,117,109,97,110,32,98,101,105,110,103,115,110, -97,109,101,32,111,102,32,116,104,101,32,97,114,101,32,102,111,117,110,100,32,105 -,110,97,114,101,32,98,97,115,101,100,32,111,110,115,109,97,108,108,101,114,32, -116,104,97,110,97,32,112,101,114,115,111,110,32,119,104,111,101,120,112,97,110, -115,105,111,110,32,111,102,97,114,103,117,105,110,103,32,116,104,97,116,110,111, -119,32,107,110,111,119,110,32,97,115,73,110,32,116,104,101,32,101,97,114,108,121 -,105,110,116,101,114,109,101,100,105,97,116,101,100,101,114,105,118,101,100,32, -102,114,111,109,83,99,97,110,100,105,110,97,118,105,97,110,60,47,97,62,60,47,100 -,105,118,62,13,10,99,111,110,115,105,100,101,114,32,116,104,101,97,110,32,101, -115,116,105,109,97,116,101,100,116,104,101,32,78,97,116,105,111,110,97,108,60, -100,105,118,32,105,100,61,34,112,97,103,114,101,115,117,108,116,105,110,103,32, -105,110,99,111,109,109,105,115,115,105,111,110,101,100,97,110,97,108,111,103,111 -,117,115,32,116,111,97,114,101,32,114,101,113,117,105,114,101,100,47,117,108,62, -10,60,47,100,105,118,62,10,119,97,115,32,98,97,115,101,100,32,111,110,97,110,100 -,32,98,101,99,97,109,101,32,97,38,110,98,115,112,59,38,110,98,115,112,59,116,34, -32,118,97,108,117,101,61,34,34,32,119,97,115,32,99,97,112,116,117,114,101,100, -110,111,32,109,111,114,101,32,116,104,97,110,114,101,115,112,101,99,116,105,118, -101,108,121,99,111,110,116,105,110,117,101,32,116,111,32,62,13,10,60,104,101,97, -100,62,13,10,60,119,101,114,101,32,99,114,101,97,116,101,100,109,111,114,101,32, -103,101,110,101,114,97,108,105,110,102,111,114,109,97,116,105,111,110,32,117,115 -,101,100,32,102,111,114,32,116,104,101,105,110,100,101,112,101,110,100,101,110, -116,32,116,104,101,32,73,109,112,101,114,105,97,108,99,111,109,112,111,110,101, -110,116,32,111,102,116,111,32,116,104,101,32,110,111,114,116,104,105,110,99,108, -117,100,101,32,116,104,101,32,67,111,110,115,116,114,117,99,116,105,111,110,115, -105,100,101,32,111,102,32,116,104,101,32,119,111,117,108,100,32,110,111,116,32, -98,101,102,111,114,32,105,110,115,116,97,110,99,101,105,110,118,101,110,116,105, -111,110,32,111,102,109,111,114,101,32,99,111,109,112,108,101,120,99,111,108,108, -101,99,116,105,118,101,108,121,98,97,99,107,103,114,111,117,110,100,58,32,116, -101,120,116,45,97,108,105,103,110,58,32,105,116,115,32,111,114,105,103,105,110, -97,108,105,110,116,111,32,97,99,99,111,117,110,116,116,104,105,115,32,112,114, -111,99,101,115,115,97,110,32,101,120,116,101,110,115,105,118,101,104,111,119,101 -,118,101,114,44,32,116,104,101,116,104,101,121,32,97,114,101,32,110,111,116,114, -101,106,101,99,116,101,100,32,116,104,101,99,114,105,116,105,99,105,115,109,32, -111,102,100,117,114,105,110,103,32,119,104,105,99,104,112,114,111,98,97,98,108, -121,32,116,104,101,116,104,105,115,32,97,114,116,105,99,108,101,40,102,117,110, -99,116,105,111,110,40,41,123,73,116,32,115,104,111,117,108,100,32,98,101,97,110, -32,97,103,114,101,101,109,101,110,116,97,99,99,105,100,101,110,116,97,108,108, -121,100,105,102,102,101,114,115,32,102,114,111,109,65,114,99,104,105,116,101,99, -116,117,114,101,98,101,116,116,101,114,32,107,110,111,119,110,97,114,114,97,110, -103,101,109,101,110,116,115,105,110,102,108,117,101,110,99,101,32,111,110,97,116 -,116,101,110,100,101,100,32,116,104,101,105,100,101,110,116,105,99,97,108,32,116 -,111,115,111,117,116,104,32,111,102,32,116,104,101,112,97,115,115,32,116,104,114 -,111,117,103,104,120,109,108,34,32,116,105,116,108,101,61,34,119,101,105,103,104 -,116,58,98,111,108,100,59,99,114,101,97,116,105,110,103,32,116,104,101,100,105, -115,112,108,97,121,58,110,111,110,101,114,101,112,108,97,99,101,100,32,116,104, -101,60,105,109,103,32,115,114,99,61,34,47,105,104,116,116,112,115,58,47,47,119, -119,119,46,87,111,114,108,100,32,87,97,114,32,73,73,116,101,115,116,105,109,111, -110,105,97,108,115,102,111,117,110,100,32,105,110,32,116,104,101,114,101,113,117 -,105,114,101,100,32,116,111,32,97,110,100,32,116,104,97,116,32,116,104,101,98, -101,116,119,101,101,110,32,116,104,101,32,119,97,115,32,100,101,115,105,103,110, -101,100,99,111,110,115,105,115,116,115,32,111,102,32,99,111,110,115,105,100,101, -114,97,98,108,121,112,117,98,108,105,115,104,101,100,32,98,121,116,104,101,32, -108,97,110,103,117,97,103,101,67,111,110,115,101,114,118,97,116,105,111,110,99, -111,110,115,105,115,116,101,100,32,111,102,114,101,102,101,114,32,116,111,32,116 -,104,101,98,97,99,107,32,116,111,32,116,104,101,32,99,115,115,34,32,109,101,100, -105,97,61,34,80,101,111,112,108,101,32,102,114,111,109,32,97,118,97,105,108,97, -98,108,101,32,111,110,112,114,111,118,101,100,32,116,111,32,98,101,115,117,103, -103,101,115,116,105,111,110,115,34,119,97,115,32,107,110,111,119,110,32,97,115, -118,97,114,105,101,116,105,101,115,32,111,102,108,105,107,101,108,121,32,116,111 -,32,98,101,99,111,109,112,114,105,115,101,100,32,111,102,115,117,112,112,111,114 -,116,32,116,104,101,32,104,97,110,100,115,32,111,102,32,116,104,101,99,111,117, -112,108,101,100,32,119,105,116,104,99,111,110,110,101,99,116,32,97,110,100,32,98 -,111,114,100,101,114,58,110,111,110,101,59,112,101,114,102,111,114,109,97,110,99 -,101,115,98,101,102,111,114,101,32,98,101,105,110,103,108,97,116,101,114,32,98, -101,99,97,109,101,99,97,108,99,117,108,97,116,105,111,110,115,111,102,116,101, -110,32,99,97,108,108,101,100,114,101,115,105,100,101,110,116,115,32,111,102,109, -101,97,110,105,110,103,32,116,104,97,116,62,60,108,105,32,99,108,97,115,115,61, -34,101,118,105,100,101,110,99,101,32,102,111,114,101,120,112,108,97,110,97,116, -105,111,110,115,101,110,118,105,114,111,110,109,101,110,116,115,34,62,60,47,97, -62,60,47,100,105,118,62,119,104,105,99,104,32,97,108,108,111,119,115,73,110,116, -114,111,100,117,99,116,105,111,110,100,101,118,101,108,111,112,101,100,32,98,121 -,97,32,119,105,100,101,32,114,97,110,103,101,111,110,32,98,101,104,97,108,102,32 -,111,102,118,97,108,105,103,110,61,34,116,111,112,34,112,114,105,110,99,105,112, -108,101,32,111,102,97,116,32,116,104,101,32,116,105,109,101,44,60,47,110,111,115 -,99,114,105,112,116,62,13,115,97,105,100,32,116,111,32,104,97,118,101,105,110,32 -,116,104,101,32,102,105,114,115,116,119,104,105,108,101,32,111,116,104,101,114, -115,104,121,112,111,116,104,101,116,105,99,97,108,112,104,105,108,111,115,111, -112,104,101,114,115,112,111,119,101,114,32,111,102,32,116,104,101,99,111,110,116 -,97,105,110,101,100,32,105,110,112,101,114,102,111,114,109,101,100,32,98,121,105 -,110,97,98,105,108,105,116,121,32,116,111,119,101,114,101,32,119,114,105,116,116 -,101,110,115,112,97,110,32,115,116,121,108,101,61,34,105,110,112,117,116,32,110, -97,109,101,61,34,116,104,101,32,113,117,101,115,116,105,111,110,105,110,116,101, -110,100,101,100,32,102,111,114,114,101,106,101,99,116,105,111,110,32,111,102,105 -,109,112,108,105,101,115,32,116,104,97,116,105,110,118,101,110,116,101,100,32, -116,104,101,116,104,101,32,115,116,97,110,100,97,114,100,119,97,115,32,112,114, -111,98,97,98,108,121,108,105,110,107,32,98,101,116,119,101,101,110,112,114,111, -102,101,115,115,111,114,32,111,102,105,110,116,101,114,97,99,116,105,111,110,115 -,99,104,97,110,103,105,110,103,32,116,104,101,73,110,100,105,97,110,32,79,99,101 -,97,110,32,99,108,97,115,115,61,34,108,97,115,116,119,111,114,107,105,110,103,32 -,119,105,116,104,39,104,116,116,112,58,47,47,119,119,119,46,121,101,97,114,115, -32,98,101,102,111,114,101,84,104,105,115,32,119,97,115,32,116,104,101,114,101,99 -,114,101,97,116,105,111,110,97,108,101,110,116,101,114,105,110,103,32,116,104, -101,109,101,97,115,117,114,101,109,101,110,116,115,97,110,32,101,120,116,114,101 -,109,101,108,121,118,97,108,117,101,32,111,102,32,116,104,101,115,116,97,114,116 -,32,111,102,32,116,104,101,10,60,47,115,99,114,105,112,116,62,10,10,97,110,32, -101,102,102,111,114,116,32,116,111,105,110,99,114,101,97,115,101,32,116,104,101, -116,111,32,116,104,101,32,115,111,117,116,104,115,112,97,99,105,110,103,61,34,48 -,34,62,115,117,102,102,105,99,105,101,110,116,108,121,116,104,101,32,69,117,114, -111,112,101,97,110,99,111,110,118,101,114,116,101,100,32,116,111,99,108,101,97, -114,84,105,109,101,111,117,116,100,105,100,32,110,111,116,32,104,97,118,101,99, -111,110,115,101,113,117,101,110,116,108,121,102,111,114,32,116,104,101,32,110, -101,120,116,101,120,116,101,110,115,105,111,110,32,111,102,101,99,111,110,111, -109,105,99,32,97,110,100,97,108,116,104,111,117,103,104,32,116,104,101,97,114, -101,32,112,114,111,100,117,99,101,100,97,110,100,32,119,105,116,104,32,116,104, -101,105,110,115,117,102,102,105,99,105,101,110,116,103,105,118,101,110,32,98,121 -,32,116,104,101,115,116,97,116,105,110,103,32,116,104,97,116,101,120,112,101,110 -,100,105,116,117,114,101,115,60,47,115,112,97,110,62,60,47,97,62,10,116,104,111, -117,103,104,116,32,116,104,97,116,111,110,32,116,104,101,32,98,97,115,105,115,99 -,101,108,108,112,97,100,100,105,110,103,61,105,109,97,103,101,32,111,102,32,116, -104,101,114,101,116,117,114,110,105,110,103,32,116,111,105,110,102,111,114,109, -97,116,105,111,110,44,115,101,112,97,114,97,116,101,100,32,98,121,97,115,115,97, -115,115,105,110,97,116,101,100,115,34,32,99,111,110,116,101,110,116,61,34,97,117 -,116,104,111,114,105,116,121,32,111,102,110,111,114,116,104,119,101,115,116,101, -114,110,60,47,100,105,118,62,10,60,100,105,118,32,34,62,60,47,100,105,118,62,13, -10,32,32,99,111,110,115,117,108,116,97,116,105,111,110,99,111,109,109,117,110, -105,116,121,32,111,102,116,104,101,32,110,97,116,105,111,110,97,108,105,116,32, -115,104,111,117,108,100,32,98,101,112,97,114,116,105,99,105,112,97,110,116,115, -32,97,108,105,103,110,61,34,108,101,102,116,116,104,101,32,103,114,101,97,116, -101,115,116,115,101,108,101,99,116,105,111,110,32,111,102,115,117,112,101,114, -110,97,116,117,114,97,108,100,101,112,101,110,100,101,110,116,32,111,110,105,115 -,32,109,101,110,116,105,111,110,101,100,97,108,108,111,119,105,110,103,32,116, -104,101,119,97,115,32,105,110,118,101,110,116,101,100,97,99,99,111,109,112,97, -110,121,105,110,103,104,105,115,32,112,101,114,115,111,110,97,108,97,118,97,105, -108,97,98,108,101,32,97,116,115,116,117,100,121,32,111,102,32,116,104,101,111, -110,32,116,104,101,32,111,116,104,101,114,101,120,101,99,117,116,105,111,110,32, -111,102,72,117,109,97,110,32,82,105,103,104,116,115,116,101,114,109,115,32,111, -102,32,116,104,101,97,115,115,111,99,105,97,116,105,111,110,115,114,101,115,101, -97,114,99,104,32,97,110,100,115,117,99,99,101,101,100,101,100,32,98,121,100,101, -102,101,97,116,101,100,32,116,104,101,97,110,100,32,102,114,111,109,32,116,104, -101,98,117,116,32,116,104,101,121,32,97,114,101,99,111,109,109,97,110,100,101, -114,32,111,102,115,116,97,116,101,32,111,102,32,116,104,101,121,101,97,114,115, -32,111,102,32,97,103,101,116,104,101,32,115,116,117,100,121,32,111,102,60,117, -108,32,99,108,97,115,115,61,34,115,112,108,97,99,101,32,105,110,32,116,104,101, -119,104,101,114,101,32,104,101,32,119,97,115,60,108,105,32,99,108,97,115,115,61, -34,102,116,104,101,114,101,32,97,114,101,32,110,111,119,104,105,99,104,32,98,101 -,99,97,109,101,104,101,32,112,117,98,108,105,115,104,101,100,101,120,112,114,101 -,115,115,101,100,32,105,110,116,111,32,119,104,105,99,104,32,116,104,101,99,111, -109,109,105,115,115,105,111,110,101,114,102,111,110,116,45,119,101,105,103,104, -116,58,116,101,114,114,105,116,111,114,121,32,111,102,101,120,116,101,110,115, -105,111,110,115,34,62,82,111,109,97,110,32,69,109,112,105,114,101,101,113,117,97 -,108,32,116,111,32,116,104,101,73,110,32,99,111,110,116,114,97,115,116,44,104, -111,119,101,118,101,114,44,32,97,110,100,105,115,32,116,121,112,105,99,97,108, -108,121,97,110,100,32,104,105,115,32,119,105,102,101,40,97,108,115,111,32,99,97, -108,108,101,100,62,60,117,108,32,99,108,97,115,115,61,34,101,102,102,101,99,116, -105,118,101,108,121,32,101,118,111,108,118,101,100,32,105,110,116,111,115,101, -101,109,32,116,111,32,104,97,118,101,119,104,105,99,104,32,105,115,32,116,104, -101,116,104,101,114,101,32,119,97,115,32,110,111,97,110,32,101,120,99,101,108, -108,101,110,116,97,108,108,32,111,102,32,116,104,101,115,101,100,101,115,99,114, -105,98,101,100,32,98,121,73,110,32,112,114,97,99,116,105,99,101,44,98,114,111,97 -,100,99,97,115,116,105,110,103,99,104,97,114,103,101,100,32,119,105,116,104,114, -101,102,108,101,99,116,101,100,32,105,110,115,117,98,106,101,99,116,101,100,32, -116,111,109,105,108,105,116,97,114,121,32,97,110,100,116,111,32,116,104,101,32, -112,111,105,110,116,101,99,111,110,111,109,105,99,97,108,108,121,115,101,116,84, -97,114,103,101,116,105,110,103,97,114,101,32,97,99,116,117,97,108,108,121,118, -105,99,116,111,114,121,32,111,118,101,114,40,41,59,60,47,115,99,114,105,112,116, -62,99,111,110,116,105,110,117,111,117,115,108,121,114,101,113,117,105,114,101, -100,32,102,111,114,101,118,111,108,117,116,105,111,110,97,114,121,97,110,32,101, -102,102,101,99,116,105,118,101,110,111,114,116,104,32,111,102,32,116,104,101,44, -32,119,104,105,99,104,32,119,97,115,32,102,114,111,110,116,32,111,102,32,116,104 -,101,111,114,32,111,116,104,101,114,119,105,115,101,115,111,109,101,32,102,111, -114,109,32,111,102,104,97,100,32,110,111,116,32,98,101,101,110,103,101,110,101, -114,97,116,101,100,32,98,121,105,110,102,111,114,109,97,116,105,111,110,46,112, -101,114,109,105,116,116,101,100,32,116,111,105,110,99,108,117,100,101,115,32,116 -,104,101,100,101,118,101,108,111,112,109,101,110,116,44,101,110,116,101,114,101, -100,32,105,110,116,111,116,104,101,32,112,114,101,118,105,111,117,115,99,111,110 -,115,105,115,116,101,110,116,108,121,97,114,101,32,107,110,111,119,110,32,97,115 -,116,104,101,32,102,105,101,108,100,32,111,102,116,104,105,115,32,116,121,112, -101,32,111,102,103,105,118,101,110,32,116,111,32,116,104,101,116,104,101,32,116, -105,116,108,101,32,111,102,99,111,110,116,97,105,110,115,32,116,104,101,105,110, -115,116,97,110,99,101,115,32,111,102,105,110,32,116,104,101,32,110,111,114,116, -104,100,117,101,32,116,111,32,116,104,101,105,114,97,114,101,32,100,101,115,105, -103,110,101,100,99,111,114,112,111,114,97,116,105,111,110,115,119,97,115,32,116, -104,97,116,32,116,104,101,111,110,101,32,111,102,32,116,104,101,115,101,109,111, -114,101,32,112,111,112,117,108,97,114,115,117,99,99,101,101,100,101,100,32,105, -110,115,117,112,112,111,114,116,32,102,114,111,109,105,110,32,100,105,102,102, -101,114,101,110,116,100,111,109,105,110,97,116,101,100,32,98,121,100,101,115,105 -,103,110,101,100,32,102,111,114,111,119,110,101,114,115,104,105,112,32,111,102, -97,110,100,32,112,111,115,115,105,98,108,121,115,116,97,110,100,97,114,100,105, -122,101,100,114,101,115,112,111,110,115,101,84,101,120,116,119,97,115,32,105,110 -,116,101,110,100,101,100,114,101,99,101,105,118,101,100,32,116,104,101,97,115, -115,117,109,101,100,32,116,104,97,116,97,114,101,97,115,32,111,102,32,116,104, -101,112,114,105,109,97,114,105,108,121,32,105,110,116,104,101,32,98,97,115,105, -115,32,111,102,105,110,32,116,104,101,32,115,101,110,115,101,97,99,99,111,117, -110,116,115,32,102,111,114,100,101,115,116,114,111,121,101,100,32,98,121,97,116, -32,108,101,97,115,116,32,116,119,111,119,97,115,32,100,101,99,108,97,114,101,100 -,99,111,117,108,100,32,110,111,116,32,98,101,83,101,99,114,101,116,97,114,121,32 -,111,102,97,112,112,101,97,114,32,116,111,32,98,101,109,97,114,103,105,110,45, -116,111,112,58,49,47,94,92,115,43,124,92,115,43,36,47,103,101,41,123,116,104,114 -,111,119,32,101,125,59,116,104,101,32,115,116,97,114,116,32,111,102,116,119,111, -32,115,101,112,97,114,97,116,101,108,97,110,103,117,97,103,101,32,97,110,100,119 -,104,111,32,104,97,100,32,98,101,101,110,111,112,101,114,97,116,105,111,110,32, -111,102,100,101,97,116,104,32,111,102,32,116,104,101,114,101,97,108,32,110,117, -109,98,101,114,115,9,60,108,105,110,107,32,114,101,108,61,34,112,114,111,118,105 -,100,101,100,32,116,104,101,116,104,101,32,115,116,111,114,121,32,111,102,99,111 -,109,112,101,116,105,116,105,111,110,115,101,110,103,108,105,115,104,32,40,85,75 -,41,101,110,103,108,105,115,104,32,40,85,83,41,208,156,208,190,208,189,208,179, -208,190,208,187,208,161,209,128,208,191,209,129,208,186,208,184,209,129,209,128, -208,191,209,129,208,186,208,184,209,129,209,128,208,191,209,129,208,186,208,190, -217,132,216,185,216,177,216,168,217,138,216,169,230,173,163,233,171,148,228,184, -173,230,150,135,231,174,128,228,189,147,228,184,173,230,150,135,231,185,129,228, -189,147,228,184,173,230,150,135,230,156,137,233,153,144,229,133,172,229,143,184, -228,186,186,230,176,145,230,148,191,229,186,156,233,152,191,233,135,140,229,183, -180,229,183,180,231,164,190,228,188,154,228,184,187,228,185,137,230,147,141,228, -189,156,231,179,187,231,187,159,230,148,191,231,173,150,230,179,149,232,167,132, -105,110,102,111,114,109,97,99,105,195,179,110,104,101,114,114,97,109,105,101,110 -,116,97,115,101,108,101,99,116,114,195,179,110,105,99,111,100,101,115,99,114,105 -,112,99,105,195,179,110,99,108,97,115,105,102,105,99,97,100,111,115,99,111,110, -111,99,105,109,105,101,110,116,111,112,117,98,108,105,99,97,99,105,195,179,110, -114,101,108,97,99,105,111,110,97,100,97,115,105,110,102,111,114,109,195,161,116, -105,99,97,114,101,108,97,99,105,111,110,97,100,111,115,100,101,112,97,114,116,97 -,109,101,110,116,111,116,114,97,98,97,106,97,100,111,114,101,115,100,105,114,101 -,99,116,97,109,101,110,116,101,97,121,117,110,116,97,109,105,101,110,116,111,109 -,101,114,99,97,100,111,76,105,98,114,101,99,111,110,116,195,161,99,116,101,110, -111,115,104,97,98,105,116,97,99,105,111,110,101,115,99,117,109,112,108,105,109, -105,101,110,116,111,114,101,115,116,97,117,114,97,110,116,101,115,100,105,115, -112,111,115,105,99,105,195,179,110,99,111,110,115,101,99,117,101,110,99,105,97, -101,108,101,99,116,114,195,179,110,105,99,97,97,112,108,105,99,97,99,105,111,110 -,101,115,100,101,115,99,111,110,101,99,116,97,100,111,105,110,115,116,97,108,97, -99,105,195,179,110,114,101,97,108,105,122,97,99,105,195,179,110,117,116,105,108, -105,122,97,99,105,195,179,110,101,110,99,105,99,108,111,112,101,100,105,97,101, -110,102,101,114,109,101,100,97,100,101,115,105,110,115,116,114,117,109,101,110, -116,111,115,101,120,112,101,114,105,101,110,99,105,97,115,105,110,115,116,105, -116,117,99,105,195,179,110,112,97,114,116,105,99,117,108,97,114,101,115,115,117, -98,99,97,116,101,103,111,114,105,97,209,130,208,190,208,187,209,140,208,186,208, -190,208,160,208,190,209,129,209,129,208,184,208,184,209,128,208,176,208,177,208, -190,209,130,209,139,208,177,208,190,208,187,209,140,209,136,208,181,208,191,209, -128,208,190,209,129,209,130,208,190,208,188,208,190,208,182,208,181,209,130,208, -181,208,180,209,128,209,131,208,179,208,184,209,133,209,129,208,187,209,131,209, -135,208,176,208,181,209,129,208,181,208,185,209,135,208,176,209,129,208,178,209, -129,208,181,208,179,208,180,208,176,208,160,208,190,209,129,209,129,208,184,209, -143,208,156,208,190,209,129,208,186,208,178,208,181,208,180,209,128,209,131,208, -179,208,184,208,181,208,179,208,190,209,128,208,190,208,180,208,176,208,178,208, -190,208,191,209,128,208,190,209,129,208,180,208,176,208,189,208,189,209,139,209, -133,208,180,208,190,208,187,208,182,208,189,209,139,208,184,208,188,208,181,208, -189,208,189,208,190,208,156,208,190,209,129,208,186,208,178,209,139,209,128,209, -131,208,177,208,187,208,181,208,185,208,156,208,190,209,129,208,186,208,178,208, -176,209,129,209,130,209,128,208,176,208,189,209,139,208,189,208,184,209,135,208, -181,208,179,208,190,209,128,208,176,208,177,208,190,209,130,208,181,208,180,208, -190,208,187,208,182,208,181,208,189,209,131,209,129,208,187,209,131,208,179,208, -184,209,130,208,181,208,191,208,181,209,128,209,140,208,158,208,180,208,189,208, -176,208,186,208,190,208,191,208,190,209,130,208,190,208,188,209,131,209,128,208, -176,208,177,208,190,209,130,209,131,208,176,208,191,209,128,208,181,208,187,209, -143,208,178,208,190,208,190,208,177,209,137,208,181,208,190,208,180,208,189,208, -190,208,179,208,190,209,129,208,178,208,190,208,181,208,179,208,190,209,129,209, -130,208,176,209,130,209,140,208,184,208,180,209,128,209,131,208,179,208,190,208, -185,209,132,208,190,209,128,209,131,208,188,208,181,209,133,208,190,209,128,208, -190,209,136,208,190,208,191,209,128,208,190,209,130,208,184,208,178,209,129,209, -129,209,139,208,187,208,186,208,176,208,186,208,176,208,182,208,180,209,139,208, -185,208,178,208,187,208,176,209,129,209,130,208,184,208,179,209,128,209,131,208, -191,208,191,209,139,208,178,208,188,208,181,209,129,209,130,208,181,209,128,208, -176,208,177,208,190,209,130,208,176,209,129,208,186,208,176,208,183,208,176,208, -187,208,191,208,181,209,128,208,178,209,139,208,185,208,180,208,181,208,187,208, -176,209,130,209,140,208,180,208,181,208,189,209,140,208,179,208,184,208,191,208, -181,209,128,208,184,208,190,208,180,208,177,208,184,208,183,208,189,208,181,209, -129,208,190,209,129,208,189,208,190,208,178,208,181,208,188,208,190,208,188,208, -181,208,189,209,130,208,186,209,131,208,191,208,184,209,130,209,140,208,180,208, -190,208,187,208,182,208,189,208,176,209,128,208,176,208,188,208,186,208,176,209, -133,208,189,208,176,209,135,208,176,208,187,208,190,208,160,208,176,208,177,208, -190,209,130,208,176,208,162,208,190,208,187,209,140,208,186,208,190,209,129,208, -190,208,178,209,129,208,181,208,188,208,178,209,130,208,190,209,128,208,190,208, -185,208,189,208,176,209,135,208,176,208,187,208,176,209,129,208,191,208,184,209, -129,208,190,208,186,209,129,208,187,209,131,208,182,208,177,209,139,209,129,208, -184,209,129,209,130,208,181,208,188,208,191,208,181,209,135,208,176,209,130,208, -184,208,189,208,190,208,178,208,190,208,179,208,190,208,191,208,190,208,188,208, -190,209,137,208,184,209,129,208,176,208,185,209,130,208,190,208,178,208,191,208, -190,209,135,208,181,208,188,209,131,208,191,208,190,208,188,208,190,209,137,209, -140,208,180,208,190,208,187,208,182,208,189,208,190,209,129,209,129,209,139,208, -187,208,186,208,184,208,177,209,139,209,129,209,130,209,128,208,190,208,180,208, -176,208,189,208,189,209,139,208,181,208,188,208,189,208,190,208,179,208,184,208, -181,208,191,209,128,208,190,208,181,208,186,209,130,208,161,208,181,208,185,209, -135,208,176,209,129,208,188,208,190,208,180,208,181,208,187,208,184,209,130,208, -176,208,186,208,190,208,179,208,190,208,190,208,189,208,187,208,176,208,185,208, -189,208,179,208,190,209,128,208,190,208,180,208,181,208,178,208,181,209,128,209, -129,208,184,209,143,209,129,209,130,209,128,208,176,208,189,208,181,209,132,208, -184,208,187,209,140,208,188,209,139,209,131,209,128,208,190,208,178,208,189,209, -143,209,128,208,176,208,183,208,189,209,139,209,133,208,184,209,129,208,186,208, -176,209,130,209,140,208,189,208,181,208,180,208,181,208,187,209,142,209,143,208, -189,208,178,208,176,209,128,209,143,208,188,208,181,208,189,209,140,209,136,208, -181,208,188,208,189,208,190,208,179,208,184,209,133,208,180,208,176,208,189,208, -189,208,190,208,185,208,183,208,189,208,176,209,135,208,184,209,130,208,189,208, -181,208,187,209,140,208,183,209,143,209,132,208,190,209,128,209,131,208,188,208, -176,208,162,208,181,208,191,208,181,209,128,209,140,208,188,208,181,209,129,209, -143,209,134,208,176,208,183,208,176,209,137,208,184,209,130,209,139,208,155,209, -131,209,135,209,136,208,184,208,181,224,164,168,224,164,185,224,165,128,224,164, -130,224,164,149,224,164,176,224,164,168,224,165,135,224,164,133,224,164,170,224, -164,168,224,165,135,224,164,149,224,164,191,224,164,175,224,164,190,224,164,149, -224,164,176,224,165,135,224,164,130,224,164,133,224,164,168,224,165,141,224,164, -175,224,164,149,224,165,141,224,164,175,224,164,190,224,164,151,224,164,190,224, -164,135,224,164,161,224,164,172,224,164,190,224,164,176,224,165,135,224,164,149, -224,164,191,224,164,184,224,165,128,224,164,166,224,164,191,224,164,175,224,164, -190,224,164,170,224,164,185,224,164,178,224,165,135,224,164,184,224,164,191,224, -164,130,224,164,185,224,164,173,224,164,190,224,164,176,224,164,164,224,164,133, -224,164,170,224,164,168,224,165,128,224,164,181,224,164,190,224,164,178,224,165, -135,224,164,184,224,165,135,224,164,181,224,164,190,224,164,149,224,164,176,224, -164,164,224,165,135,224,164,174,224,165,135,224,164,176,224,165,135,224,164,185, -224,165,139,224,164,168,224,165,135,224,164,184,224,164,149,224,164,164,224,165, -135,224,164,172,224,164,185,224,165,129,224,164,164,224,164,184,224,164,190,224, -164,135,224,164,159,224,164,185,224,165,139,224,164,151,224,164,190,224,164,156, -224,164,190,224,164,168,224,165,135,224,164,174,224,164,191,224,164,168,224,164, -159,224,164,149,224,164,176,224,164,164,224,164,190,224,164,149,224,164,176,224, -164,168,224,164,190,224,164,137,224,164,168,224,164,149,224,165,135,224,164,175, -224,164,185,224,164,190,224,164,129,224,164,184,224,164,172,224,164,184,224,165, -135,224,164,173,224,164,190,224,164,183,224,164,190,224,164,134,224,164,170,224, -164,149,224,165,135,224,164,178,224,164,191,224,164,175,224,165,135,224,164,182, -224,165,129,224,164,176,224,165,130,224,164,135,224,164,184,224,164,149,224,165, -135,224,164,152,224,164,130,224,164,159,224,165,135,224,164,174,224,165,135,224, -164,176,224,165,128,224,164,184,224,164,149,224,164,164,224,164,190,224,164,174, -224,165,135,224,164,176,224,164,190,224,164,178,224,165,135,224,164,149,224,164, -176,224,164,133,224,164,167,224,164,191,224,164,149,224,164,133,224,164,170,224, -164,168,224,164,190,224,164,184,224,164,174,224,164,190,224,164,156,224,164,174, -224,165,129,224,164,157,224,165,135,224,164,149,224,164,190,224,164,176,224,164, -163,224,164,185,224,165,139,224,164,164,224,164,190,224,164,149,224,164,161,224, -164,188,224,165,128,224,164,175,224,164,185,224,164,190,224,164,130,224,164,185, -224,165,139,224,164,159,224,164,178,224,164,182,224,164,172,224,165,141,224,164, -166,224,164,178,224,164,191,224,164,175,224,164,190,224,164,156,224,165,128,224, -164,181,224,164,168,224,164,156,224,164,190,224,164,164,224,164,190,224,164,149, -224,165,136,224,164,184,224,165,135,224,164,134,224,164,170,224,164,149,224,164, -190,224,164,181,224,164,190,224,164,178,224,165,128,224,164,166,224,165,135,224, -164,168,224,165,135,224,164,170,224,165,130,224,164,176,224,165,128,224,164,170, -224,164,190,224,164,168,224,165,128,224,164,137,224,164,184,224,164,149,224,165, -135,224,164,185,224,165,139,224,164,151,224,165,128,224,164,172,224,165,136,224, -164,160,224,164,149,224,164,134,224,164,170,224,164,149,224,165,128,224,164,181, -224,164,176,224,165,141,224,164,183,224,164,151,224,164,190,224,164,130,224,164, -181,224,164,134,224,164,170,224,164,149,224,165,139,224,164,156,224,164,191,224, -164,178,224,164,190,224,164,156,224,164,190,224,164,168,224,164,190,224,164,184, -224,164,185,224,164,174,224,164,164,224,164,185,224,164,174,224,165,135,224,164, -130,224,164,137,224,164,168,224,164,149,224,165,128,224,164,175,224,164,190,224, -164,185,224,165,130,224,164,166,224,164,176,224,165,141,224,164,156,224,164,184, -224,165,130,224,164,154,224,165,128,224,164,170,224,164,184,224,164,130,224,164, -166,224,164,184,224,164,181,224,164,190,224,164,178,224,164,185,224,165,139,224, -164,168,224,164,190,224,164,185,224,165,139,224,164,164,224,165,128,224,164,156, -224,165,136,224,164,184,224,165,135,224,164,181,224,164,190,224,164,170,224,164, -184,224,164,156,224,164,168,224,164,164,224,164,190,224,164,168,224,165,135,224, -164,164,224,164,190,224,164,156,224,164,190,224,164,176,224,165,128,224,164,152, -224,164,190,224,164,175,224,164,178,224,164,156,224,164,191,224,164,178,224,165, -135,224,164,168,224,165,128,224,164,154,224,165,135,224,164,156,224,164,190,224, -164,130,224,164,154,224,164,170,224,164,164,224,165,141,224,164,176,224,164,151, -224,165,130,224,164,151,224,164,178,224,164,156,224,164,190,224,164,164,224,165, -135,224,164,172,224,164,190,224,164,185,224,164,176,224,164,134,224,164,170,224, -164,168,224,165,135,224,164,181,224,164,190,224,164,185,224,164,168,224,164,135, -224,164,184,224,164,149,224,164,190,224,164,184,224,165,129,224,164,172,224,164, -185,224,164,176,224,164,185,224,164,168,224,165,135,224,164,135,224,164,184,224, -164,184,224,165,135,224,164,184,224,164,185,224,164,191,224,164,164,224,164,172, -224,164,161,224,164,188,224,165,135,224,164,152,224,164,159,224,164,168,224,164, -190,224,164,164,224,164,178,224,164,190,224,164,182,224,164,170,224,164,190,224, -164,130,224,164,154,224,164,182,224,165,141,224,164,176,224,165,128,224,164,172, -224,164,161,224,164,188,224,165,128,224,164,185,224,165,139,224,164,164,224,165, -135,224,164,184,224,164,190,224,164,136,224,164,159,224,164,182,224,164,190,224, -164,175,224,164,166,224,164,184,224,164,149,224,164,164,224,165,128,224,164,156, -224,164,190,224,164,164,224,165,128,224,164,181,224,164,190,224,164,178,224,164, -190,224,164,185,224,164,156,224,164,190,224,164,176,224,164,170,224,164,159,224, -164,168,224,164,190,224,164,176,224,164,150,224,164,168,224,165,135,224,164,184, -224,164,161,224,164,188,224,164,149,224,164,174,224,164,191,224,164,178,224,164, -190,224,164,137,224,164,184,224,164,149,224,165,128,224,164,149,224,165,135,224, -164,181,224,164,178,224,164,178,224,164,151,224,164,164,224,164,190,224,164,150, -224,164,190,224,164,168,224,164,190,224,164,133,224,164,176,224,165,141,224,164, -165,224,164,156,224,164,185,224,164,190,224,164,130,224,164,166,224,165,135,224, -164,150,224,164,190,224,164,170,224,164,185,224,164,178,224,165,128,224,164,168, -224,164,191,224,164,175,224,164,174,224,164,172,224,164,191,224,164,168,224,164, -190,224,164,172,224,165,136,224,164,130,224,164,149,224,164,149,224,164,185,224, -165,128,224,164,130,224,164,149,224,164,185,224,164,168,224,164,190,224,164,166, -224,165,135,224,164,164,224,164,190,224,164,185,224,164,174,224,164,178,224,165, -135,224,164,149,224,164,190,224,164,171,224,165,128,224,164,156,224,164,172,224, -164,149,224,164,191,224,164,164,224,165,129,224,164,176,224,164,164,224,164,174, -224,164,190,224,164,130,224,164,151,224,164,181,224,164,185,224,165,128,224,164, -130,224,164,176,224,165,139,224,164,156,224,164,188,224,164,174,224,164,191,224, -164,178,224,165,128,224,164,134,224,164,176,224,165,139,224,164,170,224,164,184, -224,165,135,224,164,168,224,164,190,224,164,175,224,164,190,224,164,166,224,164, -181,224,164,178,224,165,135,224,164,168,224,165,135,224,164,150,224,164,190,224, -164,164,224,164,190,224,164,149,224,164,176,224,165,128,224,164,172,224,164,137, -224,164,168,224,164,149,224,164,190,224,164,156,224,164,181,224,164,190,224,164, -172,224,164,170,224,165,130,224,164,176,224,164,190,224,164,172,224,164,161,224, -164,188,224,164,190,224,164,184,224,165,140,224,164,166,224,164,190,224,164,182, -224,165,135,224,164,175,224,164,176,224,164,149,224,164,191,224,164,175,224,165, -135,224,164,149,224,164,185,224,164,190,224,164,130,224,164,133,224,164,149,224, -164,184,224,164,176,224,164,172,224,164,168,224,164,190,224,164,143,224,164,181, -224,164,185,224,164,190,224,164,130,224,164,184,224,165,141,224,164,165,224,164, -178,224,164,174,224,164,191,224,164,178,224,165,135,224,164,178,224,165,135,224, -164,150,224,164,149,224,164,181,224,164,191,224,164,183,224,164,175,224,164,149, -224,165,141,224,164,176,224,164,130,224,164,184,224,164,174,224,165,130,224,164, -185,224,164,165,224,164,190,224,164,168,224,164,190,216,170,216,179,216,170,216, -183,217,138,216,185,217,133,216,180,216,167,216,177,217,131,216,169,216,168,217, -136,216,167,216,179,216,183,216,169,216,167,217,132,216,181,217,129,216,173,216, -169,217,133,217,136,216,167,216,182,217,138,216,185,216,167,217,132,216,174,216, -167,216,181,216,169,216,167,217,132,217,133,216,178,217,138,216,175,216,167,217, -132,216,185,216,167,217,133,216,169,216,167,217,132,217,131,216,167,216,170,216, -168,216,167,217,132,216,177,216,175,217,136,216,175,216,168,216,177,217,134,216, -167,217,133,216,172,216,167,217,132,216,175,217,136,217,132,216,169,216,167,217, -132,216,185,216,167,217,132,217,133,216,167,217,132,217,133,217,136,217,130,216, -185,216,167,217,132,216,185,216,177,216,168,217,138,216,167,217,132,216,179,216, -177,217,138,216,185,216,167,217,132,216,172,217,136,216,167,217,132,216,167,217, -132,216,176,217,135,216,167,216,168,216,167,217,132,216,173,217,138,216,167,216, -169,216,167,217,132,216,173,217,130,217,136,217,130,216,167,217,132,217,131,216, -177,217,138,217,133,216,167,217,132,216,185,216,177,216,167,217,130,217,133,216, -173,217,129,217,136,216,184,216,169,216,167,217,132,216,171,216,167,217,134,217, -138,217,133,216,180,216,167,217,135,216,175,216,169,216,167,217,132,217,133,216, -177,216,163,216,169,216,167,217,132,217,130,216,177,216,162,217,134,216,167,217, -132,216,180,216,168,216,167,216,168,216,167,217,132,216,173,217,136,216,167,216, -177,216,167,217,132,216,172,216,175,217,138,216,175,216,167,217,132,216,163,216, -179,216,177,216,169,216,167,217,132,216,185,217,132,217,136,217,133,217,133,216, -172,217,133,217,136,216,185,216,169,216,167,217,132,216,177,216,173,217,133,217, -134,216,167,217,132,217,134,217,130,216,167,216,183,217,129,217,132,216,179,216, -183,217,138,217,134,216,167,217,132,217,131,217,136,217,138,216,170,216,167,217, -132,216,175,217,134,217,138,216,167,216,168,216,177,217,131,216,167,216,170,217, -135,216,167,217,132,216,177,217,138,216,167,216,182,216,170,216,173,217,138,216, -167,216,170,217,138,216,168,216,170,217,136,217,130,217,138,216,170,216,167,217, -132,216,163,217,136,217,132,217,137,216,167,217,132,216,168,216,177,217,138,216, -175,216,167,217,132,217,131,217,132,216,167,217,133,216,167,217,132,216,177,216, -167,216,168,216,183,216,167,217,132,216,180,216,174,216,181,217,138,216,179,217, -138,216,167,216,177,216,167,216,170,216,167,217,132,216,171,216,167,217,132,216, -171,216,167,217,132,216,181,217,132,216,167,216,169,216,167,217,132,216,173,216, -175,217,138,216,171,216,167,217,132,216,178,217,136,216,167,216,177,216,167,217, -132,216,174,217,132,217,138,216,172,216,167,217,132,216,172,217,133,217,138,216, -185,216,167,217,132,216,185,216,167,217,133,217,135,216,167,217,132,216,172,217, -133,216,167,217,132,216,167,217,132,216,179,216,167,216,185,216,169,217,133,216, -180,216,167,217,135,216,175,217,135,216,167,217,132,216,177,216,166,217,138,216, -179,216,167,217,132,216,175,216,174,217,136,217,132,216,167,217,132,217,129,217, -134,217,138,216,169,216,167,217,132,217,131,216,170,216,167,216,168,216,167,217, -132,216,175,217,136,216,177,217,138,216,167,217,132,216,175,216,177,217,136,216, -179,216,167,216,179,216,170,216,186,216,177,217,130,216,170,216,181,216,167,217, -133,217,138,217,133,216,167,217,132,216,168,217,134,216,167,216,170,216,167,217, -132,216,185,216,184,217,138,217,133,101,110,116,101,114,116,97,105,110,109,101, -110,116,117,110,100,101,114,115,116,97,110,100,105,110,103,32,61,32,102,117,110, -99,116,105,111,110,40,41,46,106,112,103,34,32,119,105,100,116,104,61,34,99,111, -110,102,105,103,117,114,97,116,105,111,110,46,112,110,103,34,32,119,105,100,116, -104,61,34,60,98,111,100,121,32,99,108,97,115,115,61,34,77,97,116,104,46,114,97, -110,100,111,109,40,41,99,111,110,116,101,109,112,111,114,97,114,121,32,85,110, -105,116,101,100,32,83,116,97,116,101,115,99,105,114,99,117,109,115,116,97,110,99 -,101,115,46,97,112,112,101,110,100,67,104,105,108,100,40,111,114,103,97,110,105, -122,97,116,105,111,110,115,60,115,112,97,110,32,99,108,97,115,115,61,34,34,62,60 -,105,109,103,32,115,114,99,61,34,47,100,105,115,116,105,110,103,117,105,115,104, -101,100,116,104,111,117,115,97,110,100,115,32,111,102,32,99,111,109,109,117,110, -105,99,97,116,105,111,110,99,108,101,97,114,34,62,60,47,100,105,118,62,105,110, -118,101,115,116,105,103,97,116,105,111,110,102,97,118,105,99,111,110,46,105,99, -111,34,32,109,97,114,103,105,110,45,114,105,103,104,116,58,98,97,115,101,100,32, -111,110,32,116,104,101,32,77,97,115,115,97,99,104,117,115,101,116,116,115,116,97 -,98,108,101,32,98,111,114,100,101,114,61,105,110,116,101,114,110,97,116,105,111, -110,97,108,97,108,115,111,32,107,110,111,119,110,32,97,115,112,114,111,110,117, -110,99,105,97,116,105,111,110,98,97,99,107,103,114,111,117,110,100,58,35,102,112 -,97,100,100,105,110,103,45,108,101,102,116,58,70,111,114,32,101,120,97,109,112, -108,101,44,32,109,105,115,99,101,108,108,97,110,101,111,117,115,38,108,116,59,47 -,109,97,116,104,38,103,116,59,112,115,121,99,104,111,108,111,103,105,99,97,108, -105,110,32,112,97,114,116,105,99,117,108,97,114,101,97,114,99,104,34,32,116,121, -112,101,61,34,102,111,114,109,32,109,101,116,104,111,100,61,34,97,115,32,111,112 -,112,111,115,101,100,32,116,111,83,117,112,114,101,109,101,32,67,111,117,114,116 -,111,99,99,97,115,105,111,110,97,108,108,121,32,65,100,100,105,116,105,111,110, -97,108,108,121,44,78,111,114,116,104,32,65,109,101,114,105,99,97,112,120,59,98, -97,99,107,103,114,111,117,110,100,111,112,112,111,114,116,117,110,105,116,105, -101,115,69,110,116,101,114,116,97,105,110,109,101,110,116,46,116,111,76,111,119, -101,114,67,97,115,101,40,109,97,110,117,102,97,99,116,117,114,105,110,103,112, -114,111,102,101,115,115,105,111,110,97,108,32,99,111,109,98,105,110,101,100,32, -119,105,116,104,70,111,114,32,105,110,115,116,97,110,99,101,44,99,111,110,115, -105,115,116,105,110,103,32,111,102,34,32,109,97,120,108,101,110,103,116,104,61, -34,114,101,116,117,114,110,32,102,97,108,115,101,59,99,111,110,115,99,105,111, -117,115,110,101,115,115,77,101,100,105,116,101,114,114,97,110,101,97,110,101,120 -,116,114,97,111,114,100,105,110,97,114,121,97,115,115,97,115,115,105,110,97,116, -105,111,110,115,117,98,115,101,113,117,101,110,116,108,121,32,98,117,116,116,111 -,110,32,116,121,112,101,61,34,116,104,101,32,110,117,109,98,101,114,32,111,102, -116,104,101,32,111,114,105,103,105,110,97,108,32,99,111,109,112,114,101,104,101, -110,115,105,118,101,114,101,102,101,114,115,32,116,111,32,116,104,101,60,47,117, -108,62,10,60,47,100,105,118,62,10,112,104,105,108,111,115,111,112,104,105,99,97, -108,108,111,99,97,116,105,111,110,46,104,114,101,102,119,97,115,32,112,117,98, -108,105,115,104,101,100,83,97,110,32,70,114,97,110,99,105,115,99,111,40,102,117, -110,99,116,105,111,110,40,41,123,10,60,100,105,118,32,105,100,61,34,109,97,105, -110,115,111,112,104,105,115,116,105,99,97,116,101,100,109,97,116,104,101,109,97, -116,105,99,97,108,32,47,104,101,97,100,62,13,10,60,98,111,100,121,115,117,103, -103,101,115,116,115,32,116,104,97,116,100,111,99,117,109,101,110,116,97,116,105, -111,110,99,111,110,99,101,110,116,114,97,116,105,111,110,114,101,108,97,116,105, -111,110,115,104,105,112,115,109,97,121,32,104,97,118,101,32,98,101,101,110,40, -102,111,114,32,101,120,97,109,112,108,101,44,84,104,105,115,32,97,114,116,105,99 -,108,101,32,105,110,32,115,111,109,101,32,99,97,115,101,115,112,97,114,116,115, -32,111,102,32,116,104,101,32,100,101,102,105,110,105,116,105,111,110,32,111,102, -71,114,101,97,116,32,66,114,105,116,97,105,110,32,99,101,108,108,112,97,100,100, -105,110,103,61,101,113,117,105,118,97,108,101,110,116,32,116,111,112,108,97,99, -101,104,111,108,100,101,114,61,34,59,32,102,111,110,116,45,115,105,122,101,58,32 -,106,117,115,116,105,102,105,99,97,116,105,111,110,98,101,108,105,101,118,101, -100,32,116,104,97,116,115,117,102,102,101,114,101,100,32,102,114,111,109,97,116, -116,101,109,112,116,101,100,32,116,111,32,108,101,97,100,101,114,32,111,102,32, -116,104,101,99,114,105,112,116,34,32,115,114,99,61,34,47,40,102,117,110,99,116, -105,111,110,40,41,32,123,97,114,101,32,97,118,97,105,108,97,98,108,101,10,9,60, -108,105,110,107,32,114,101,108,61,34,32,115,114,99,61,39,104,116,116,112,58,47, -47,105,110,116,101,114,101,115,116,101,100,32,105,110,99,111,110,118,101,110,116 -,105,111,110,97,108,32,34,32,97,108,116,61,34,34,32,47,62,60,47,97,114,101,32, -103,101,110,101,114,97,108,108,121,104,97,115,32,97,108,115,111,32,98,101,101, -110,109,111,115,116,32,112,111,112,117,108,97,114,32,99,111,114,114,101,115,112, -111,110,100,105,110,103,99,114,101,100,105,116,101,100,32,119,105,116,104,116, -121,108,101,61,34,98,111,114,100,101,114,58,60,47,97,62,60,47,115,112,97,110,62, -60,47,46,103,105,102,34,32,119,105,100,116,104,61,34,60,105,102,114,97,109,101, -32,115,114,99,61,34,116,97,98,108,101,32,99,108,97,115,115,61,34,105,110,108,105 -,110,101,45,98,108,111,99,107,59,97,99,99,111,114,100,105,110,103,32,116,111,32, -116,111,103,101,116,104,101,114,32,119,105,116,104,97,112,112,114,111,120,105, -109,97,116,101,108,121,112,97,114,108,105,97,109,101,110,116,97,114,121,109,111, -114,101,32,97,110,100,32,109,111,114,101,100,105,115,112,108,97,121,58,110,111, -110,101,59,116,114,97,100,105,116,105,111,110,97,108,108,121,112,114,101,100,111 -,109,105,110,97,110,116,108,121,38,110,98,115,112,59,124,38,110,98,115,112,59,38 -,110,98,115,112,59,60,47,115,112,97,110,62,32,99,101,108,108,115,112,97,99,105, -110,103,61,60,105,110,112,117,116,32,110,97,109,101,61,34,111,114,34,32,99,111, -110,116,101,110,116,61,34,99,111,110,116,114,111,118,101,114,115,105,97,108,112, -114,111,112,101,114,116,121,61,34,111,103,58,47,120,45,115,104,111,99,107,119,97 -,118,101,45,100,101,109,111,110,115,116,114,97,116,105,111,110,115,117,114,114, -111,117,110,100,101,100,32,98,121,78,101,118,101,114,116,104,101,108,101,115,115 -,44,119,97,115,32,116,104,101,32,102,105,114,115,116,99,111,110,115,105,100,101, -114,97,98,108,101,32,65,108,116,104,111,117,103,104,32,116,104,101,32,99,111,108 -,108,97,98,111,114,97,116,105,111,110,115,104,111,117,108,100,32,110,111,116,32, -98,101,112,114,111,112,111,114,116,105,111,110,32,111,102,60,115,112,97,110,32, -115,116,121,108,101,61,34,107,110,111,119,110,32,97,115,32,116,104,101,32,115, -104,111,114,116,108,121,32,97,102,116,101,114,102,111,114,32,105,110,115,116,97, -110,99,101,44,100,101,115,99,114,105,98,101,100,32,97,115,32,47,104,101,97,100, -62,10,60,98,111,100,121,32,115,116,97,114,116,105,110,103,32,119,105,116,104,105 -,110,99,114,101,97,115,105,110,103,108,121,32,116,104,101,32,102,97,99,116,32, -116,104,97,116,100,105,115,99,117,115,115,105,111,110,32,111,102,109,105,100,100 -,108,101,32,111,102,32,116,104,101,97,110,32,105,110,100,105,118,105,100,117,97, -108,100,105,102,102,105,99,117,108,116,32,116,111,32,112,111,105,110,116,32,111, -102,32,118,105,101,119,104,111,109,111,115,101,120,117,97,108,105,116,121,97,99, -99,101,112,116,97,110,99,101,32,111,102,60,47,115,112,97,110,62,60,47,100,105, -118,62,109,97,110,117,102,97,99,116,117,114,101,114,115,111,114,105,103,105,110, -32,111,102,32,116,104,101,99,111,109,109,111,110,108,121,32,117,115,101,100,105, -109,112,111,114,116,97,110,99,101,32,111,102,100,101,110,111,109,105,110,97,116, -105,111,110,115,98,97,99,107,103,114,111,117,110,100,58,32,35,108,101,110,103, -116,104,32,111,102,32,116,104,101,100,101,116,101,114,109,105,110,97,116,105,111 -,110,97,32,115,105,103,110,105,102,105,99,97,110,116,34,32,98,111,114,100,101, -114,61,34,48,34,62,114,101,118,111,108,117,116,105,111,110,97,114,121,112,114, -105,110,99,105,112,108,101,115,32,111,102,105,115,32,99,111,110,115,105,100,101, -114,101,100,119,97,115,32,100,101,118,101,108,111,112,101,100,73,110,100,111,45, -69,117,114,111,112,101,97,110,118,117,108,110,101,114,97,98,108,101,32,116,111, -112,114,111,112,111,110,101,110,116,115,32,111,102,97,114,101,32,115,111,109,101 -,116,105,109,101,115,99,108,111,115,101,114,32,116,111,32,116,104,101,78,101,119 -,32,89,111,114,107,32,67,105,116,121,32,110,97,109,101,61,34,115,101,97,114,99, -104,97,116,116,114,105,98,117,116,101,100,32,116,111,99,111,117,114,115,101,32, -111,102,32,116,104,101,109,97,116,104,101,109,97,116,105,99,105,97,110,98,121,32 -,116,104,101,32,101,110,100,32,111,102,97,116,32,116,104,101,32,101,110,100,32, -111,102,34,32,98,111,114,100,101,114,61,34,48,34,32,116,101,99,104,110,111,108, -111,103,105,99,97,108,46,114,101,109,111,118,101,67,108,97,115,115,40,98,114,97, -110,99,104,32,111,102,32,116,104,101,101,118,105,100,101,110,99,101,32,116,104, -97,116,33,91,101,110,100,105,102,93,45,45,62,13,10,73,110,115,116,105,116,117, -116,101,32,111,102,32,105,110,116,111,32,97,32,115,105,110,103,108,101,114,101, -115,112,101,99,116,105,118,101,108,121,46,97,110,100,32,116,104,101,114,101,102, -111,114,101,112,114,111,112,101,114,116,105,101,115,32,111,102,105,115,32,108, -111,99,97,116,101,100,32,105,110,115,111,109,101,32,111,102,32,119,104,105,99, -104,84,104,101,114,101,32,105,115,32,97,108,115,111,99,111,110,116,105,110,117, -101,100,32,116,111,32,97,112,112,101,97,114,97,110,99,101,32,111,102,32,38,97, -109,112,59,110,100,97,115,104,59,32,100,101,115,99,114,105,98,101,115,32,116,104 -,101,99,111,110,115,105,100,101,114,97,116,105,111,110,97,117,116,104,111,114,32 -,111,102,32,116,104,101,105,110,100,101,112,101,110,100,101,110,116,108,121,101, -113,117,105,112,112,101,100,32,119,105,116,104,100,111,101,115,32,110,111,116,32 -,104,97,118,101,60,47,97,62,60,97,32,104,114,101,102,61,34,99,111,110,102,117, -115,101,100,32,119,105,116,104,60,108,105,110,107,32,104,114,101,102,61,34,47,97 -,116,32,116,104,101,32,97,103,101,32,111,102,97,112,112,101,97,114,32,105,110,32 -,116,104,101,84,104,101,115,101,32,105,110,99,108,117,100,101,114,101,103,97,114 -,100,108,101,115,115,32,111,102,99,111,117,108,100,32,98,101,32,117,115,101,100, -32,115,116,121,108,101,61,38,113,117,111,116,59,115,101,118,101,114,97,108,32, -116,105,109,101,115,114,101,112,114,101,115,101,110,116,32,116,104,101,98,111, -100,121,62,10,60,47,104,116,109,108,62,116,104,111,117,103,104,116,32,116,111,32 -,98,101,112,111,112,117,108,97,116,105,111,110,32,111,102,112,111,115,115,105,98 -,105,108,105,116,105,101,115,112,101,114,99,101,110,116,97,103,101,32,111,102,97 -,99,99,101,115,115,32,116,111,32,116,104,101,97,110,32,97,116,116,101,109,112, -116,32,116,111,112,114,111,100,117,99,116,105,111,110,32,111,102,106,113,117,101 -,114,121,47,106,113,117,101,114,121,116,119,111,32,100,105,102,102,101,114,101, -110,116,98,101,108,111,110,103,32,116,111,32,116,104,101,101,115,116,97,98,108, -105,115,104,109,101,110,116,114,101,112,108,97,99,105,110,103,32,116,104,101,100 -,101,115,99,114,105,112,116,105,111,110,34,32,100,101,116,101,114,109,105,110, -101,32,116,104,101,97,118,97,105,108,97,98,108,101,32,102,111,114,65,99,99,111, -114,100,105,110,103,32,116,111,32,119,105,100,101,32,114,97,110,103,101,32,111, -102,9,60,100,105,118,32,99,108,97,115,115,61,34,109,111,114,101,32,99,111,109, -109,111,110,108,121,111,114,103,97,110,105,115,97,116,105,111,110,115,102,117, -110,99,116,105,111,110,97,108,105,116,121,119,97,115,32,99,111,109,112,108,101, -116,101,100,32,38,97,109,112,59,109,100,97,115,104,59,32,112,97,114,116,105,99, -105,112,97,116,105,111,110,116,104,101,32,99,104,97,114,97,99,116,101,114,97,110 -,32,97,100,100,105,116,105,111,110,97,108,97,112,112,101,97,114,115,32,116,111, -32,98,101,102,97,99,116,32,116,104,97,116,32,116,104,101,97,110,32,101,120,97, -109,112,108,101,32,111,102,115,105,103,110,105,102,105,99,97,110,116,108,121,111 -,110,109,111,117,115,101,111,118,101,114,61,34,98,101,99,97,117,115,101,32,116, -104,101,121,32,97,115,121,110,99,32,61,32,116,114,117,101,59,112,114,111,98,108, -101,109,115,32,119,105,116,104,115,101,101,109,115,32,116,111,32,104,97,118,101, -116,104,101,32,114,101,115,117,108,116,32,111,102,32,115,114,99,61,34,104,116, -116,112,58,47,47,102,97,109,105,108,105,97,114,32,119,105,116,104,112,111,115, -115,101,115,115,105,111,110,32,111,102,102,117,110,99,116,105,111,110,32,40,41, -32,123,116,111,111,107,32,112,108,97,99,101,32,105,110,97,110,100,32,115,111,109 -,101,116,105,109,101,115,115,117,98,115,116,97,110,116,105,97,108,108,121,60,115 -,112,97,110,62,60,47,115,112,97,110,62,105,115,32,111,102,116,101,110,32,117,115 -,101,100,105,110,32,97,110,32,97,116,116,101,109,112,116,103,114,101,97,116,32, -100,101,97,108,32,111,102,69,110,118,105,114,111,110,109,101,110,116,97,108,115, -117,99,99,101,115,115,102,117,108,108,121,32,118,105,114,116,117,97,108,108,121, -32,97,108,108,50,48,116,104,32,99,101,110,116,117,114,121,44,112,114,111,102,101 -,115,115,105,111,110,97,108,115,110,101,99,101,115,115,97,114,121,32,116,111,32, -100,101,116,101,114,109,105,110,101,100,32,98,121,99,111,109,112,97,116,105,98, -105,108,105,116,121,98,101,99,97,117,115,101,32,105,116,32,105,115,68,105,99,116 -,105,111,110,97,114,121,32,111,102,109,111,100,105,102,105,99,97,116,105,111,110 -,115,84,104,101,32,102,111,108,108,111,119,105,110,103,109,97,121,32,114,101,102 -,101,114,32,116,111,58,67,111,110,115,101,113,117,101,110,116,108,121,44,73,110, -116,101,114,110,97,116,105,111,110,97,108,97,108,116,104,111,117,103,104,32,115, -111,109,101,116,104,97,116,32,119,111,117,108,100,32,98,101,119,111,114,108,100, -39,115,32,102,105,114,115,116,99,108,97,115,115,105,102,105,101,100,32,97,115,98 -,111,116,116,111,109,32,111,102,32,116,104,101,40,112,97,114,116,105,99,117,108, -97,114,108,121,97,108,105,103,110,61,34,108,101,102,116,34,32,109,111,115,116,32 -,99,111,109,109,111,110,108,121,98,97,115,105,115,32,102,111,114,32,116,104,101, -102,111,117,110,100,97,116,105,111,110,32,111,102,99,111,110,116,114,105,98,117, -116,105,111,110,115,112,111,112,117,108,97,114,105,116,121,32,111,102,99,101,110 -,116,101,114,32,111,102,32,116,104,101,116,111,32,114,101,100,117,99,101,32,116, -104,101,106,117,114,105,115,100,105,99,116,105,111,110,115,97,112,112,114,111, -120,105,109,97,116,105,111,110,32,111,110,109,111,117,115,101,111,117,116,61,34, -78,101,119,32,84,101,115,116,97,109,101,110,116,99,111,108,108,101,99,116,105, -111,110,32,111,102,60,47,115,112,97,110,62,60,47,97,62,60,47,105,110,32,116,104, -101,32,85,110,105,116,101,100,102,105,108,109,32,100,105,114,101,99,116,111,114, -45,115,116,114,105,99,116,46,100,116,100,34,62,104,97,115,32,98,101,101,110,32, -117,115,101,100,114,101,116,117,114,110,32,116,111,32,116,104,101,97,108,116,104 -,111,117,103,104,32,116,104,105,115,99,104,97,110,103,101,32,105,110,32,116,104, -101,115,101,118,101,114,97,108,32,111,116,104,101,114,98,117,116,32,116,104,101, -114,101,32,97,114,101,117,110,112,114,101,99,101,100,101,110,116,101,100,105,115 -,32,115,105,109,105,108,97,114,32,116,111,101,115,112,101,99,105,97,108,108,121, -32,105,110,119,101,105,103,104,116,58,32,98,111,108,100,59,105,115,32,99,97,108, -108,101,100,32,116,104,101,99,111,109,112,117,116,97,116,105,111,110,97,108,105, -110,100,105,99,97,116,101,32,116,104,97,116,114,101,115,116,114,105,99,116,101, -100,32,116,111,9,60,109,101,116,97,32,110,97,109,101,61,34,97,114,101,32,116,121 -,112,105,99,97,108,108,121,99,111,110,102,108,105,99,116,32,119,105,116,104,72, -111,119,101,118,101,114,44,32,116,104,101,32,65,110,32,101,120,97,109,112,108, -101,32,111,102,99,111,109,112,97,114,101,100,32,119,105,116,104,113,117,97,110, -116,105,116,105,101,115,32,111,102,114,97,116,104,101,114,32,116,104,97,110,32, -97,99,111,110,115,116,101,108,108,97,116,105,111,110,110,101,99,101,115,115,97, -114,121,32,102,111,114,114,101,112,111,114,116,101,100,32,116,104,97,116,115,112 -,101,99,105,102,105,99,97,116,105,111,110,112,111,108,105,116,105,99,97,108,32, -97,110,100,38,110,98,115,112,59,38,110,98,115,112,59,60,114,101,102,101,114,101, -110,99,101,115,32,116,111,116,104,101,32,115,97,109,101,32,121,101,97,114,71,111 -,118,101,114,110,109,101,110,116,32,111,102,103,101,110,101,114,97,116,105,111, -110,32,111,102,104,97,118,101,32,110,111,116,32,98,101,101,110,115,101,118,101, -114,97,108,32,121,101,97,114,115,99,111,109,109,105,116,109,101,110,116,32,116, -111,9,9,60,117,108,32,99,108,97,115,115,61,34,118,105,115,117,97,108,105,122,97, -116,105,111,110,49,57,116,104,32,99,101,110,116,117,114,121,44,112,114,97,99,116 -,105,116,105,111,110,101,114,115,116,104,97,116,32,104,101,32,119,111,117,108, -100,97,110,100,32,99,111,110,116,105,110,117,101,100,111,99,99,117,112,97,116, -105,111,110,32,111,102,105,115,32,100,101,102,105,110,101,100,32,97,115,99,101, -110,116,114,101,32,111,102,32,116,104,101,116,104,101,32,97,109,111,117,110,116, -32,111,102,62,60,100,105,118,32,115,116,121,108,101,61,34,101,113,117,105,118,97 -,108,101,110,116,32,111,102,100,105,102,102,101,114,101,110,116,105,97,116,101, -98,114,111,117,103,104,116,32,97,98,111,117,116,109,97,114,103,105,110,45,108, -101,102,116,58,32,97,117,116,111,109,97,116,105,99,97,108,108,121,116,104,111, -117,103,104,116,32,111,102,32,97,115,83,111,109,101,32,111,102,32,116,104,101, -115,101,10,60,100,105,118,32,99,108,97,115,115,61,34,105,110,112,117,116,32,99, -108,97,115,115,61,34,114,101,112,108,97,99,101,100,32,119,105,116,104,105,115,32 -,111,110,101,32,111,102,32,116,104,101,101,100,117,99,97,116,105,111,110,32,97, -110,100,105,110,102,108,117,101,110,99,101,100,32,98,121,114,101,112,117,116,97, -116,105,111,110,32,97,115,10,60,109,101,116,97,32,110,97,109,101,61,34,97,99,99, -111,109,109,111,100,97,116,105,111,110,60,47,100,105,118,62,10,60,47,100,105,118 -,62,108,97,114,103,101,32,112,97,114,116,32,111,102,73,110,115,116,105,116,117, -116,101,32,102,111,114,116,104,101,32,115,111,45,99,97,108,108,101,100,32,97,103 -,97,105,110,115,116,32,116,104,101,32,73,110,32,116,104,105,115,32,99,97,115,101 -,44,119,97,115,32,97,112,112,111,105,110,116,101,100,99,108,97,105,109,101,100, -32,116,111,32,98,101,72,111,119,101,118,101,114,44,32,116,104,105,115,68,101,112 -,97,114,116,109,101,110,116,32,111,102,116,104,101,32,114,101,109,97,105,110,105 -,110,103,101,102,102,101,99,116,32,111,110,32,116,104,101,112,97,114,116,105,99, -117,108,97,114,108,121,32,100,101,97,108,32,119,105,116,104,32,116,104,101,10,60 -,100,105,118,32,115,116,121,108,101,61,34,97,108,109,111,115,116,32,97,108,119, -97,121,115,97,114,101,32,99,117,114,114,101,110,116,108,121,101,120,112,114,101, -115,115,105,111,110,32,111,102,112,104,105,108,111,115,111,112,104,121,32,111, -102,102,111,114,32,109,111,114,101,32,116,104,97,110,99,105,118,105,108,105,122, -97,116,105,111,110,115,111,110,32,116,104,101,32,105,115,108,97,110,100,115,101, -108,101,99,116,101,100,73,110,100,101,120,99,97,110,32,114,101,115,117,108,116, -32,105,110,34,32,118,97,108,117,101,61,34,34,32,47,62,116,104,101,32,115,116,114 -,117,99,116,117,114,101,32,47,62,60,47,97,62,60,47,100,105,118,62,77,97,110,121, -32,111,102,32,116,104,101,115,101,99,97,117,115,101,100,32,98,121,32,116,104,101 -,111,102,32,116,104,101,32,85,110,105,116,101,100,115,112,97,110,32,99,108,97, -115,115,61,34,109,99,97,110,32,98,101,32,116,114,97,99,101,100,105,115,32,114, -101,108,97,116,101,100,32,116,111,98,101,99,97,109,101,32,111,110,101,32,111,102 -,105,115,32,102,114,101,113,117,101,110,116,108,121,108,105,118,105,110,103,32, -105,110,32,116,104,101,116,104,101,111,114,101,116,105,99,97,108,108,121,70,111, -108,108,111,119,105,110,103,32,116,104,101,82,101,118,111,108,117,116,105,111, -110,97,114,121,103,111,118,101,114,110,109,101,110,116,32,105,110,105,115,32,100 -,101,116,101,114,109,105,110,101,100,116,104,101,32,112,111,108,105,116,105,99, -97,108,105,110,116,114,111,100,117,99,101,100,32,105,110,115,117,102,102,105,99, -105,101,110,116,32,116,111,100,101,115,99,114,105,112,116,105,111,110,34,62,115, -104,111,114,116,32,115,116,111,114,105,101,115,115,101,112,97,114,97,116,105,111 -,110,32,111,102,97,115,32,116,111,32,119,104,101,116,104,101,114,107,110,111,119 -,110,32,102,111,114,32,105,116,115,119,97,115,32,105,110,105,116,105,97,108,108, -121,100,105,115,112,108,97,121,58,98,108,111,99,107,105,115,32,97,110,32,101,120 -,97,109,112,108,101,116,104,101,32,112,114,105,110,99,105,112,97,108,99,111,110, -115,105,115,116,115,32,111,102,32,97,114,101,99,111,103,110,105,122,101,100,32, -97,115,47,98,111,100,121,62,60,47,104,116,109,108,62,97,32,115,117,98,115,116,97 -,110,116,105,97,108,114,101,99,111,110,115,116,114,117,99,116,101,100,104,101,97 -,100,32,111,102,32,115,116,97,116,101,114,101,115,105,115,116,97,110,99,101,32, -116,111,117,110,100,101,114,103,114,97,100,117,97,116,101,84,104,101,114,101,32, -97,114,101,32,116,119,111,103,114,97,118,105,116,97,116,105,111,110,97,108,97, -114,101,32,100,101,115,99,114,105,98,101,100,105,110,116,101,110,116,105,111,110 -,97,108,108,121,115,101,114,118,101,100,32,97,115,32,116,104,101,99,108,97,115, -115,61,34,104,101,97,100,101,114,111,112,112,111,115,105,116,105,111,110,32,116, -111,102,117,110,100,97,109,101,110,116,97,108,108,121,100,111,109,105,110,97,116 -,101,100,32,116,104,101,97,110,100,32,116,104,101,32,111,116,104,101,114,97,108, -108,105,97,110,99,101,32,119,105,116,104,119,97,115,32,102,111,114,99,101,100,32 -,116,111,114,101,115,112,101,99,116,105,118,101,108,121,44,97,110,100,32,112,111 -,108,105,116,105,99,97,108,105,110,32,115,117,112,112,111,114,116,32,111,102,112 -,101,111,112,108,101,32,105,110,32,116,104,101,50,48,116,104,32,99,101,110,116, -117,114,121,46,97,110,100,32,112,117,98,108,105,115,104,101,100,108,111,97,100, -67,104,97,114,116,98,101,97,116,116,111,32,117,110,100,101,114,115,116,97,110, -100,109,101,109,98,101,114,32,115,116,97,116,101,115,101,110,118,105,114,111,110 -,109,101,110,116,97,108,102,105,114,115,116,32,104,97,108,102,32,111,102,99,111, -117,110,116,114,105,101,115,32,97,110,100,97,114,99,104,105,116,101,99,116,117, -114,97,108,98,101,32,99,111,110,115,105,100,101,114,101,100,99,104,97,114,97,99, -116,101,114,105,122,101,100,99,108,101,97,114,73,110,116,101,114,118,97,108,97, -117,116,104,111,114,105,116,97,116,105,118,101,70,101,100,101,114,97,116,105,111 -,110,32,111,102,119,97,115,32,115,117,99,99,101,101,100,101,100,97,110,100,32, -116,104,101,114,101,32,97,114,101,97,32,99,111,110,115,101,113,117,101,110,99, -101,116,104,101,32,80,114,101,115,105,100,101,110,116,97,108,115,111,32,105,110, -99,108,117,100,101,100,102,114,101,101,32,115,111,102,116,119,97,114,101,115,117 -,99,99,101,115,115,105,111,110,32,111,102,100,101,118,101,108,111,112,101,100,32 -,116,104,101,119,97,115,32,100,101,115,116,114,111,121,101,100,97,119,97,121,32, -102,114,111,109,32,116,104,101,59,10,60,47,115,99,114,105,112,116,62,10,60,97, -108,116,104,111,117,103,104,32,116,104,101,121,102,111,108,108,111,119,101,100, -32,98,121,32,97,109,111,114,101,32,112,111,119,101,114,102,117,108,114,101,115, -117,108,116,101,100,32,105,110,32,97,85,110,105,118,101,114,115,105,116,121,32, -111,102,72,111,119,101,118,101,114,44,32,109,97,110,121,116,104,101,32,112,114, -101,115,105,100,101,110,116,72,111,119,101,118,101,114,44,32,115,111,109,101,105 -,115,32,116,104,111,117,103,104,116,32,116,111,117,110,116,105,108,32,116,104, -101,32,101,110,100,119,97,115,32,97,110,110,111,117,110,99,101,100,97,114,101,32 -,105,109,112,111,114,116,97,110,116,97,108,115,111,32,105,110,99,108,117,100,101 -,115,62,60,105,110,112,117,116,32,116,121,112,101,61,116,104,101,32,99,101,110, -116,101,114,32,111,102,32,68,79,32,78,79,84,32,65,76,84,69,82,117,115,101,100,32 -,116,111,32,114,101,102,101,114,116,104,101,109,101,115,47,63,115,111,114,116,61 -,116,104,97,116,32,104,97,100,32,98,101,101,110,116,104,101,32,98,97,115,105,115 -,32,102,111,114,104,97,115,32,100,101,118,101,108,111,112,101,100,105,110,32,116 -,104,101,32,115,117,109,109,101,114,99,111,109,112,97,114,97,116,105,118,101,108 -,121,100,101,115,99,114,105,98,101,100,32,116,104,101,115,117,99,104,32,97,115, -32,116,104,111,115,101,116,104,101,32,114,101,115,117,108,116,105,110,103,105, -115,32,105,109,112,111,115,115,105,98,108,101,118,97,114,105,111,117,115,32,111, -116,104,101,114,83,111,117,116,104,32,65,102,114,105,99,97,110,104,97,118,101,32 -,116,104,101,32,115,97,109,101,101,102,102,101,99,116,105,118,101,110,101,115, -115,105,110,32,119,104,105,99,104,32,99,97,115,101,59,32,116,101,120,116,45,97, -108,105,103,110,58,115,116,114,117,99,116,117,114,101,32,97,110,100,59,32,98,97, -99,107,103,114,111,117,110,100,58,114,101,103,97,114,100,105,110,103,32,116,104, -101,115,117,112,112,111,114,116,101,100,32,116,104,101,105,115,32,97,108,115,111 -,32,107,110,111,119,110,115,116,121,108,101,61,34,109,97,114,103,105,110,105,110 -,99,108,117,100,105,110,103,32,116,104,101,98,97,104,97,115,97,32,77,101,108,97, -121,117,110,111,114,115,107,32,98,111,107,109,195,165,108,110,111,114,115,107,32 -,110,121,110,111,114,115,107,115,108,111,118,101,110,197,161,196,141,105,110,97, -105,110,116,101,114,110,97,99,105,111,110,97,108,99,97,108,105,102,105,99,97,99, -105,195,179,110,99,111,109,117,110,105,99,97,99,105,195,179,110,99,111,110,115, -116,114,117,99,99,105,195,179,110,34,62,60,100,105,118,32,99,108,97,115,115,61, -34,100,105,115,97,109,98,105,103,117,97,116,105,111,110,68,111,109,97,105,110,78 -,97,109,101,39,44,32,39,97,100,109,105,110,105,115,116,114,97,116,105,111,110, -115,105,109,117,108,116,97,110,101,111,117,115,108,121,116,114,97,110,115,112, -111,114,116,97,116,105,111,110,73,110,116,101,114,110,97,116,105,111,110,97,108, -32,109,97,114,103,105,110,45,98,111,116,116,111,109,58,114,101,115,112,111,110, -115,105,98,105,108,105,116,121,60,33,91,101,110,100,105,102,93,45,45,62,10,60,47 -,62,60,109,101,116,97,32,110,97,109,101,61,34,105,109,112,108,101,109,101,110, -116,97,116,105,111,110,105,110,102,114,97,115,116,114,117,99,116,117,114,101,114 -,101,112,114,101,115,101,110,116,97,116,105,111,110,98,111,114,100,101,114,45,98 -,111,116,116,111,109,58,60,47,104,101,97,100,62,10,60,98,111,100,121,62,61,104, -116,116,112,37,51,65,37,50,70,37,50,70,60,102,111,114,109,32,109,101,116,104,111 -,100,61,34,109,101,116,104,111,100,61,34,112,111,115,116,34,32,47,102,97,118,105 -,99,111,110,46,105,99,111,34,32,125,41,59,10,60,47,115,99,114,105,112,116,62,10, -46,115,101,116,65,116,116,114,105,98,117,116,101,40,65,100,109,105,110,105,115, -116,114,97,116,105,111,110,61,32,110,101,119,32,65,114,114,97,121,40,41,59,60,33 -,91,101,110,100,105,102,93,45,45,62,13,10,100,105,115,112,108,97,121,58,98,108, -111,99,107,59,85,110,102,111,114,116,117,110,97,116,101,108,121,44,34,62,38,110, -98,115,112,59,60,47,100,105,118,62,47,102,97,118,105,99,111,110,46,105,99,111,34 -,62,61,39,115,116,121,108,101,115,104,101,101,116,39,32,105,100,101,110,116,105, -102,105,99,97,116,105,111,110,44,32,102,111,114,32,101,120,97,109,112,108,101,44 -,60,108,105,62,60,97,32,104,114,101,102,61,34,47,97,110,32,97,108,116,101,114, -110,97,116,105,118,101,97,115,32,97,32,114,101,115,117,108,116,32,111,102,112, -116,34,62,60,47,115,99,114,105,112,116,62,10,116,121,112,101,61,34,115,117,98, -109,105,116,34,32,10,40,102,117,110,99,116,105,111,110,40,41,32,123,114,101,99, -111,109,109,101,110,100,97,116,105,111,110,102,111,114,109,32,97,99,116,105,111, -110,61,34,47,116,114,97,110,115,102,111,114,109,97,116,105,111,110,114,101,99, -111,110,115,116,114,117,99,116,105,111,110,46,115,116,121,108,101,46,100,105,115 -,112,108,97,121,32,65,99,99,111,114,100,105,110,103,32,116,111,32,104,105,100, -100,101,110,34,32,110,97,109,101,61,34,97,108,111,110,103,32,119,105,116,104,32, -116,104,101,100,111,99,117,109,101,110,116,46,98,111,100,121,46,97,112,112,114, -111,120,105,109,97,116,101,108,121,32,67,111,109,109,117,110,105,99,97,116,105, -111,110,115,112,111,115,116,34,32,97,99,116,105,111,110,61,34,109,101,97,110,105 -,110,103,32,38,113,117,111,116,59,45,45,60,33,91,101,110,100,105,102,93,45,45,62 -,80,114,105,109,101,32,77,105,110,105,115,116,101,114,99,104,97,114,97,99,116, -101,114,105,115,116,105,99,60,47,97,62,32,60,97,32,99,108,97,115,115,61,116,104, -101,32,104,105,115,116,111,114,121,32,111,102,32,111,110,109,111,117,115,101,111 -,118,101,114,61,34,116,104,101,32,103,111,118,101,114,110,109,101,110,116,104, -114,101,102,61,34,104,116,116,112,115,58,47,47,119,97,115,32,111,114,105,103,105 -,110,97,108,108,121,119,97,115,32,105,110,116,114,111,100,117,99,101,100,99,108, -97,115,115,105,102,105,99,97,116,105,111,110,114,101,112,114,101,115,101,110,116 -,97,116,105,118,101,97,114,101,32,99,111,110,115,105,100,101,114,101,100,60,33, -91,101,110,100,105,102,93,45,45,62,10,10,100,101,112,101,110,100,115,32,111,110, -32,116,104,101,85,110,105,118,101,114,115,105,116,121,32,111,102,32,105,110,32, -99,111,110,116,114,97,115,116,32,116,111,32,112,108,97,99,101,104,111,108,100, -101,114,61,34,105,110,32,116,104,101,32,99,97,115,101,32,111,102,105,110,116,101 -,114,110,97,116,105,111,110,97,108,32,99,111,110,115,116,105,116,117,116,105,111 -,110,97,108,115,116,121,108,101,61,34,98,111,114,100,101,114,45,58,32,102,117, -110,99,116,105,111,110,40,41,32,123,66,101,99,97,117,115,101,32,111,102,32,116, -104,101,45,115,116,114,105,99,116,46,100,116,100,34,62,10,60,116,97,98,108,101, -32,99,108,97,115,115,61,34,97,99,99,111,109,112,97,110,105,101,100,32,98,121,97, -99,99,111,117,110,116,32,111,102,32,116,104,101,60,115,99,114,105,112,116,32,115 -,114,99,61,34,47,110,97,116,117,114,101,32,111,102,32,116,104,101,32,116,104,101 -,32,112,101,111,112,108,101,32,105,110,32,105,110,32,97,100,100,105,116,105,111, -110,32,116,111,115,41,59,32,106,115,46,105,100,32,61,32,105,100,34,32,119,105, -100,116,104,61,34,49,48,48,37,34,114,101,103,97,114,100,105,110,103,32,116,104, -101,32,82,111,109,97,110,32,67,97,116,104,111,108,105,99,97,110,32,105,110,100, -101,112,101,110,100,101,110,116,102,111,108,108,111,119,105,110,103,32,116,104, -101,32,46,103,105,102,34,32,119,105,100,116,104,61,34,49,116,104,101,32,102,111, -108,108,111,119,105,110,103,32,100,105,115,99,114,105,109,105,110,97,116,105,111 -,110,97,114,99,104,97,101,111,108,111,103,105,99,97,108,112,114,105,109,101,32, -109,105,110,105,115,116,101,114,46,106,115,34,62,60,47,115,99,114,105,112,116,62 -,99,111,109,98,105,110,97,116,105,111,110,32,111,102,32,109,97,114,103,105,110, -119,105,100,116,104,61,34,99,114,101,97,116,101,69,108,101,109,101,110,116,40, -119,46,97,116,116,97,99,104,69,118,101,110,116,40,60,47,97,62,60,47,116,100,62, -60,47,116,114,62,115,114,99,61,34,104,116,116,112,115,58,47,47,97,73,110,32,112, -97,114,116,105,99,117,108,97,114,44,32,97,108,105,103,110,61,34,108,101,102,116, -34,32,67,122,101,99,104,32,82,101,112,117,98,108,105,99,85,110,105,116,101,100, -32,75,105,110,103,100,111,109,99,111,114,114,101,115,112,111,110,100,101,110,99, -101,99,111,110,99,108,117,100,101,100,32,116,104,97,116,46,104,116,109,108,34,32 -,116,105,116,108,101,61,34,40,102,117,110,99,116,105,111,110,32,40,41,32,123,99, -111,109,101,115,32,102,114,111,109,32,116,104,101,97,112,112,108,105,99,97,116, -105,111,110,32,111,102,60,115,112,97,110,32,99,108,97,115,115,61,34,115,98,101, -108,105,101,118,101,100,32,116,111,32,98,101,101,109,101,110,116,40,39,115,99, -114,105,112,116,39,60,47,97,62,10,60,47,108,105,62,10,60,108,105,118,101,114,121 -,32,100,105,102,102,101,114,101,110,116,62,60,115,112,97,110,32,99,108,97,115, -115,61,34,111,112,116,105,111,110,32,118,97,108,117,101,61,34,40,97,108,115,111, -32,107,110,111,119,110,32,97,115,9,60,108,105,62,60,97,32,104,114,101,102,61,34, -62,60,105,110,112,117,116,32,110,97,109,101,61,34,115,101,112,97,114,97,116,101, -100,32,102,114,111,109,114,101,102,101,114,114,101,100,32,116,111,32,97,115,32, -118,97,108,105,103,110,61,34,116,111,112,34,62,102,111,117,110,100,101,114,32, -111,102,32,116,104,101,97,116,116,101,109,112,116,105,110,103,32,116,111,32,99, -97,114,98,111,110,32,100,105,111,120,105,100,101,10,10,60,100,105,118,32,99,108, -97,115,115,61,34,99,108,97,115,115,61,34,115,101,97,114,99,104,45,47,98,111,100, -121,62,10,60,47,104,116,109,108,62,111,112,112,111,114,116,117,110,105,116,121, -32,116,111,99,111,109,109,117,110,105,99,97,116,105,111,110,115,60,47,104,101,97 -,100,62,13,10,60,98,111,100,121,32,115,116,121,108,101,61,34,119,105,100,116,104 -,58,84,105,225,186,191,110,103,32,86,105,225,187,135,116,99,104,97,110,103,101, -115,32,105,110,32,116,104,101,98,111,114,100,101,114,45,99,111,108,111,114,58,35 -,48,34,32,98,111,114,100,101,114,61,34,48,34,32,60,47,115,112,97,110,62,60,47, -100,105,118,62,60,119,97,115,32,100,105,115,99,111,118,101,114,101,100,34,32,116 -,121,112,101,61,34,116,101,120,116,34,32,41,59,10,60,47,115,99,114,105,112,116, -62,10,10,68,101,112,97,114,116,109,101,110,116,32,111,102,32,101,99,99,108,101, -115,105,97,115,116,105,99,97,108,116,104,101,114,101,32,104,97,115,32,98,101,101 -,110,114,101,115,117,108,116,105,110,103,32,102,114,111,109,60,47,98,111,100,121 -,62,60,47,104,116,109,108,62,104,97,115,32,110,101,118,101,114,32,98,101,101,110 -,116,104,101,32,102,105,114,115,116,32,116,105,109,101,105,110,32,114,101,115, -112,111,110,115,101,32,116,111,97,117,116,111,109,97,116,105,99,97,108,108,121, -32,60,47,100,105,118,62,10,10,60,100,105,118,32,105,119,97,115,32,99,111,110,115 -,105,100,101,114,101,100,112,101,114,99,101,110,116,32,111,102,32,116,104,101,34 -,32,47,62,60,47,97,62,60,47,100,105,118,62,99,111,108,108,101,99,116,105,111,110 -,32,111,102,32,100,101,115,99,101,110,100,101,100,32,102,114,111,109,115,101,99, -116,105,111,110,32,111,102,32,116,104,101,97,99,99,101,112,116,45,99,104,97,114, -115,101,116,116,111,32,98,101,32,99,111,110,102,117,115,101,100,109,101,109,98, -101,114,32,111,102,32,116,104,101,32,112,97,100,100,105,110,103,45,114,105,103, -104,116,58,116,114,97,110,115,108,97,116,105,111,110,32,111,102,105,110,116,101, -114,112,114,101,116,97,116,105,111,110,32,104,114,101,102,61,39,104,116,116,112, -58,47,47,119,104,101,116,104,101,114,32,111,114,32,110,111,116,84,104,101,114, -101,32,97,114,101,32,97,108,115,111,116,104,101,114,101,32,97,114,101,32,109,97, -110,121,97,32,115,109,97,108,108,32,110,117,109,98,101,114,111,116,104,101,114, -32,112,97,114,116,115,32,111,102,105,109,112,111,115,115,105,98,108,101,32,116, -111,32,32,99,108,97,115,115,61,34,98,117,116,116,111,110,108,111,99,97,116,101, -100,32,105,110,32,116,104,101,46,32,72,111,119,101,118,101,114,44,32,116,104,101 -,97,110,100,32,101,118,101,110,116,117,97,108,108,121,65,116,32,116,104,101,32, -101,110,100,32,111,102,32,98,101,99,97,117,115,101,32,111,102,32,105,116,115,114 -,101,112,114,101,115,101,110,116,115,32,116,104,101,60,102,111,114,109,32,97,99, -116,105,111,110,61,34,32,109,101,116,104,111,100,61,34,112,111,115,116,34,105, -116,32,105,115,32,112,111,115,115,105,98,108,101,109,111,114,101,32,108,105,107, -101,108,121,32,116,111,97,110,32,105,110,99,114,101,97,115,101,32,105,110,104,97 -,118,101,32,97,108,115,111,32,98,101,101,110,99,111,114,114,101,115,112,111,110, -100,115,32,116,111,97,110,110,111,117,110,99,101,100,32,116,104,97,116,97,108, -105,103,110,61,34,114,105,103,104,116,34,62,109,97,110,121,32,99,111,117,110,116 -,114,105,101,115,102,111,114,32,109,97,110,121,32,121,101,97,114,115,101,97,114, -108,105,101,115,116,32,107,110,111,119,110,98,101,99,97,117,115,101,32,105,116, -32,119,97,115,112,116,34,62,60,47,115,99,114,105,112,116,62,13,32,118,97,108,105 -,103,110,61,34,116,111,112,34,32,105,110,104,97,98,105,116,97,110,116,115,32,111 -,102,102,111,108,108,111,119,105,110,103,32,121,101,97,114,13,10,60,100,105,118, -32,99,108,97,115,115,61,34,109,105,108,108,105,111,110,32,112,101,111,112,108, -101,99,111,110,116,114,111,118,101,114,115,105,97,108,32,99,111,110,99,101,114, -110,105,110,103,32,116,104,101,97,114,103,117,101,32,116,104,97,116,32,116,104, -101,103,111,118,101,114,110,109,101,110,116,32,97,110,100,97,32,114,101,102,101, -114,101,110,99,101,32,116,111,116,114,97,110,115,102,101,114,114,101,100,32,116, -111,100,101,115,99,114,105,98,105,110,103,32,116,104,101,32,115,116,121,108,101, -61,34,99,111,108,111,114,58,97,108,116,104,111,117,103,104,32,116,104,101,114, -101,98,101,115,116,32,107,110,111,119,110,32,102,111,114,115,117,98,109,105,116, -34,32,110,97,109,101,61,34,109,117,108,116,105,112,108,105,99,97,116,105,111,110 -,109,111,114,101,32,116,104,97,110,32,111,110,101,32,114,101,99,111,103,110,105, -116,105,111,110,32,111,102,67,111,117,110,99,105,108,32,111,102,32,116,104,101, -101,100,105,116,105,111,110,32,111,102,32,116,104,101,32,32,60,109,101,116,97,32 -,110,97,109,101,61,34,69,110,116,101,114,116,97,105,110,109,101,110,116,32,97, -119,97,121,32,102,114,111,109,32,116,104,101,32,59,109,97,114,103,105,110,45,114 -,105,103,104,116,58,97,116,32,116,104,101,32,116,105,109,101,32,111,102,105,110, -118,101,115,116,105,103,97,116,105,111,110,115,99,111,110,110,101,99,116,101,100 -,32,119,105,116,104,97,110,100,32,109,97,110,121,32,111,116,104,101,114,97,108, -116,104,111,117,103,104,32,105,116,32,105,115,98,101,103,105,110,110,105,110,103 -,32,119,105,116,104,32,60,115,112,97,110,32,99,108,97,115,115,61,34,100,101,115, -99,101,110,100,97,110,116,115,32,111,102,60,115,112,97,110,32,99,108,97,115,115, -61,34,105,32,97,108,105,103,110,61,34,114,105,103,104,116,34,60,47,104,101,97, -100,62,10,60,98,111,100,121,32,97,115,112,101,99,116,115,32,111,102,32,116,104, -101,104,97,115,32,115,105,110,99,101,32,98,101,101,110,69,117,114,111,112,101,97 -,110,32,85,110,105,111,110,114,101,109,105,110,105,115,99,101,110,116,32,111,102 -,109,111,114,101,32,100,105,102,102,105,99,117,108,116,86,105,99,101,32,80,114, -101,115,105,100,101,110,116,99,111,109,112,111,115,105,116,105,111,110,32,111, -102,112,97,115,115,101,100,32,116,104,114,111,117,103,104,109,111,114,101,32,105 -,109,112,111,114,116,97,110,116,102,111,110,116,45,115,105,122,101,58,49,49,112, -120,101,120,112,108,97,110,97,116,105,111,110,32,111,102,116,104,101,32,99,111, -110,99,101,112,116,32,111,102,119,114,105,116,116,101,110,32,105,110,32,116,104, -101,9,60,115,112,97,110,32,99,108,97,115,115,61,34,105,115,32,111,110,101,32,111 -,102,32,116,104,101,32,114,101,115,101,109,98,108,97,110,99,101,32,116,111,111, -110,32,116,104,101,32,103,114,111,117,110,100,115,119,104,105,99,104,32,99,111, -110,116,97,105,110,115,105,110,99,108,117,100,105,110,103,32,116,104,101,32,100, -101,102,105,110,101,100,32,98,121,32,116,104,101,112,117,98,108,105,99,97,116, -105,111,110,32,111,102,109,101,97,110,115,32,116,104,97,116,32,116,104,101,111, -117,116,115,105,100,101,32,111,102,32,116,104,101,115,117,112,112,111,114,116,32 -,111,102,32,116,104,101,60,105,110,112,117,116,32,99,108,97,115,115,61,34,60,115 -,112,97,110,32,99,108,97,115,115,61,34,116,40,77,97,116,104,46,114,97,110,100, -111,109,40,41,109,111,115,116,32,112,114,111,109,105,110,101,110,116,100,101,115 -,99,114,105,112,116,105,111,110,32,111,102,67,111,110,115,116,97,110,116,105,110 -,111,112,108,101,119,101,114,101,32,112,117,98,108,105,115,104,101,100,60,100, -105,118,32,99,108,97,115,115,61,34,115,101,97,112,112,101,97,114,115,32,105,110, -32,116,104,101,49,34,32,104,101,105,103,104,116,61,34,49,34,32,109,111,115,116, -32,105,109,112,111,114,116,97,110,116,119,104,105,99,104,32,105,110,99,108,117, -100,101,115,119,104,105,99,104,32,104,97,100,32,98,101,101,110,100,101,115,116, -114,117,99,116,105,111,110,32,111,102,116,104,101,32,112,111,112,117,108,97,116, -105,111,110,10,9,60,100,105,118,32,99,108,97,115,115,61,34,112,111,115,115,105, -98,105,108,105,116,121,32,111,102,115,111,109,101,116,105,109,101,115,32,117,115 -,101,100,97,112,112,101,97,114,32,116,111,32,104,97,118,101,115,117,99,99,101, -115,115,32,111,102,32,116,104,101,105,110,116,101,110,100,101,100,32,116,111,32, -98,101,112,114,101,115,101,110,116,32,105,110,32,116,104,101,115,116,121,108,101 -,61,34,99,108,101,97,114,58,98,13,10,60,47,115,99,114,105,112,116,62,13,10,60, -119,97,115,32,102,111,117,110,100,101,100,32,105,110,105,110,116,101,114,118,105 -,101,119,32,119,105,116,104,95,105,100,34,32,99,111,110,116,101,110,116,61,34,99 -,97,112,105,116,97,108,32,111,102,32,116,104,101,13,10,60,108,105,110,107,32,114 -,101,108,61,34,115,114,101,108,101,97,115,101,32,111,102,32,116,104,101,112,111, -105,110,116,32,111,117,116,32,116,104,97,116,120,77,76,72,116,116,112,82,101,113 -,117,101,115,116,97,110,100,32,115,117,98,115,101,113,117,101,110,116,115,101,99 -,111,110,100,32,108,97,114,103,101,115,116,118,101,114,121,32,105,109,112,111, -114,116,97,110,116,115,112,101,99,105,102,105,99,97,116,105,111,110,115,115,117, -114,102,97,99,101,32,111,102,32,116,104,101,97,112,112,108,105,101,100,32,116, -111,32,116,104,101,102,111,114,101,105,103,110,32,112,111,108,105,99,121,95,115, -101,116,68,111,109,97,105,110,78,97,109,101,101,115,116,97,98,108,105,115,104, -101,100,32,105,110,105,115,32,98,101,108,105,101,118,101,100,32,116,111,73,110, -32,97,100,100,105,116,105,111,110,32,116,111,109,101,97,110,105,110,103,32,111, -102,32,116,104,101,105,115,32,110,97,109,101,100,32,97,102,116,101,114,116,111, -32,112,114,111,116,101,99,116,32,116,104,101,105,115,32,114,101,112,114,101,115, -101,110,116,101,100,68,101,99,108,97,114,97,116,105,111,110,32,111,102,109,111, -114,101,32,101,102,102,105,99,105,101,110,116,67,108,97,115,115,105,102,105,99, -97,116,105,111,110,111,116,104,101,114,32,102,111,114,109,115,32,111,102,104,101 -,32,114,101,116,117,114,110,101,100,32,116,111,60,115,112,97,110,32,99,108,97, -115,115,61,34,99,112,101,114,102,111,114,109,97,110,99,101,32,111,102,40,102,117 -,110,99,116,105,111,110,40,41,32,123,13,105,102,32,97,110,100,32,111,110,108,121 -,32,105,102,114,101,103,105,111,110,115,32,111,102,32,116,104,101,108,101,97,100 -,105,110,103,32,116,111,32,116,104,101,114,101,108,97,116,105,111,110,115,32,119 -,105,116,104,85,110,105,116,101,100,32,78,97,116,105,111,110,115,115,116,121,108 -,101,61,34,104,101,105,103,104,116,58,111,116,104,101,114,32,116,104,97,110,32, -116,104,101,121,112,101,34,32,99,111,110,116,101,110,116,61,34,65,115,115,111,99 -,105,97,116,105,111,110,32,111,102,10,60,47,104,101,97,100,62,10,60,98,111,100, -121,108,111,99,97,116,101,100,32,111,110,32,116,104,101,105,115,32,114,101,102, -101,114,114,101,100,32,116,111,40,105,110,99,108,117,100,105,110,103,32,116,104, -101,99,111,110,99,101,110,116,114,97,116,105,111,110,115,116,104,101,32,105,110, -100,105,118,105,100,117,97,108,97,109,111,110,103,32,116,104,101,32,109,111,115, -116,116,104,97,110,32,97,110,121,32,111,116,104,101,114,47,62,10,60,108,105,110, -107,32,114,101,108,61,34,32,114,101,116,117,114,110,32,102,97,108,115,101,59,116 -,104,101,32,112,117,114,112,111,115,101,32,111,102,116,104,101,32,97,98,105,108, -105,116,121,32,116,111,59,99,111,108,111,114,58,35,102,102,102,125,10,46,10,60, -115,112,97,110,32,99,108,97,115,115,61,34,116,104,101,32,115,117,98,106,101,99, -116,32,111,102,100,101,102,105,110,105,116,105,111,110,115,32,111,102,62,13,10, -60,108,105,110,107,32,114,101,108,61,34,99,108,97,105,109,32,116,104,97,116,32, -116,104,101,104,97,118,101,32,100,101,118,101,108,111,112,101,100,60,116,97,98, -108,101,32,119,105,100,116,104,61,34,99,101,108,101,98,114,97,116,105,111,110,32 -,111,102,70,111,108,108,111,119,105,110,103,32,116,104,101,32,116,111,32,100,105 -,115,116,105,110,103,117,105,115,104,60,115,112,97,110,32,99,108,97,115,115,61, -34,98,116,97,107,101,115,32,112,108,97,99,101,32,105,110,117,110,100,101,114,32, -116,104,101,32,110,97,109,101,110,111,116,101,100,32,116,104,97,116,32,116,104, -101,62,60,33,91,101,110,100,105,102,93,45,45,62,10,115,116,121,108,101,61,34,109 -,97,114,103,105,110,45,105,110,115,116,101,97,100,32,111,102,32,116,104,101,105, -110,116,114,111,100,117,99,101,100,32,116,104,101,116,104,101,32,112,114,111,99, -101,115,115,32,111,102,105,110,99,114,101,97,115,105,110,103,32,116,104,101,100, -105,102,102,101,114,101,110,99,101,115,32,105,110,101,115,116,105,109,97,116,101 -,100,32,116,104,97,116,101,115,112,101,99,105,97,108,108,121,32,116,104,101,47, -100,105,118,62,60,100,105,118,32,105,100,61,34,119,97,115,32,101,118,101,110,116 -,117,97,108,108,121,116,104,114,111,117,103,104,111,117,116,32,104,105,115,116, -104,101,32,100,105,102,102,101,114,101,110,99,101,115,111,109,101,116,104,105, -110,103,32,116,104,97,116,115,112,97,110,62,60,47,115,112,97,110,62,60,47,115, -105,103,110,105,102,105,99,97,110,116,108,121,32,62,60,47,115,99,114,105,112,116 -,62,13,10,13,10,101,110,118,105,114,111,110,109,101,110,116,97,108,32,116,111,32 -,112,114,101,118,101,110,116,32,116,104,101,104,97,118,101,32,98,101,101,110,32, -117,115,101,100,101,115,112,101,99,105,97,108,108,121,32,102,111,114,117,110,100 -,101,114,115,116,97,110,100,32,116,104,101,105,115,32,101,115,115,101,110,116, -105,97,108,108,121,119,101,114,101,32,116,104,101,32,102,105,114,115,116,105,115 -,32,116,104,101,32,108,97,114,103,101,115,116,104,97,118,101,32,98,101,101,110, -32,109,97,100,101,34,32,115,114,99,61,34,104,116,116,112,58,47,47,105,110,116, -101,114,112,114,101,116,101,100,32,97,115,115,101,99,111,110,100,32,104,97,108, -102,32,111,102,99,114,111,108,108,105,110,103,61,34,110,111,34,32,105,115,32,99, -111,109,112,111,115,101,100,32,111,102,73,73,44,32,72,111,108,121,32,82,111,109, -97,110,105,115,32,101,120,112,101,99,116,101,100,32,116,111,104,97,118,101,32, -116,104,101,105,114,32,111,119,110,100,101,102,105,110,101,100,32,97,115,32,116, -104,101,116,114,97,100,105,116,105,111,110,97,108,108,121,32,104,97,118,101,32, -100,105,102,102,101,114,101,110,116,97,114,101,32,111,102,116,101,110,32,117,115 -,101,100,116,111,32,101,110,115,117,114,101,32,116,104,97,116,97,103,114,101,101 -,109,101,110,116,32,119,105,116,104,99,111,110,116,97,105,110,105,110,103,32,116 -,104,101,97,114,101,32,102,114,101,113,117,101,110,116,108,121,105,110,102,111, -114,109,97,116,105,111,110,32,111,110,101,120,97,109,112,108,101,32,105,115,32, -116,104,101,114,101,115,117,108,116,105,110,103,32,105,110,32,97,60,47,97,62,60, -47,108,105,62,60,47,117,108,62,32,99,108,97,115,115,61,34,102,111,111,116,101, -114,97,110,100,32,101,115,112,101,99,105,97,108,108,121,116,121,112,101,61,34,98 -,117,116,116,111,110,34,32,60,47,115,112,97,110,62,60,47,115,112,97,110,62,119, -104,105,99,104,32,105,110,99,108,117,100,101,100,62,10,60,109,101,116,97,32,110, -97,109,101,61,34,99,111,110,115,105,100,101,114,101,100,32,116,104,101,99,97,114 -,114,105,101,100,32,111,117,116,32,98,121,72,111,119,101,118,101,114,44,32,105, -116,32,105,115,98,101,99,97,109,101,32,112,97,114,116,32,111,102,105,110,32,114, -101,108,97,116,105,111,110,32,116,111,112,111,112,117,108,97,114,32,105,110,32, -116,104,101,116,104,101,32,99,97,112,105,116,97,108,32,111,102,119,97,115,32,111 -,102,102,105,99,105,97,108,108,121,119,104,105,99,104,32,104,97,115,32,98,101, -101,110,116,104,101,32,72,105,115,116,111,114,121,32,111,102,97,108,116,101,114, -110,97,116,105,118,101,32,116,111,100,105,102,102,101,114,101,110,116,32,102,114 -,111,109,116,111,32,115,117,112,112,111,114,116,32,116,104,101,115,117,103,103, -101,115,116,101,100,32,116,104,97,116,105,110,32,116,104,101,32,112,114,111,99, -101,115,115,32,32,60,100,105,118,32,99,108,97,115,115,61,34,116,104,101,32,102, -111,117,110,100,97,116,105,111,110,98,101,99,97,117,115,101,32,111,102,32,104, -105,115,99,111,110,99,101,114,110,101,100,32,119,105,116,104,116,104,101,32,117, -110,105,118,101,114,115,105,116,121,111,112,112,111,115,101,100,32,116,111,32, -116,104,101,116,104,101,32,99,111,110,116,101,120,116,32,111,102,60,115,112,97, -110,32,99,108,97,115,115,61,34,112,116,101,120,116,34,32,110,97,109,101,61,34, -113,34,9,9,60,100,105,118,32,99,108,97,115,115,61,34,116,104,101,32,115,99,105, -101,110,116,105,102,105,99,114,101,112,114,101,115,101,110,116,101,100,32,98,121 -,109,97,116,104,101,109,97,116,105,99,105,97,110,115,101,108,101,99,116,101,100, -32,98,121,32,116,104,101,116,104,97,116,32,104,97,118,101,32,98,101,101,110,62, -60,100,105,118,32,99,108,97,115,115,61,34,99,100,105,118,32,105,100,61,34,104, -101,97,100,101,114,105,110,32,112,97,114,116,105,99,117,108,97,114,44,99,111,110 -,118,101,114,116,101,100,32,105,110,116,111,41,59,10,60,47,115,99,114,105,112, -116,62,10,60,112,104,105,108,111,115,111,112,104,105,99,97,108,32,115,114,112, -115,107,111,104,114,118,97,116,115,107,105,116,105,225,186,191,110,103,32,86,105 -,225,187,135,116,208,160,209,131,209,129,209,129,208,186,208,184,208,185,209,128 -,209,131,209,129,209,129,208,186,208,184,208,185,105,110,118,101,115,116,105,103 -,97,99,105,195,179,110,112,97,114,116,105,99,105,112,97,99,105,195,179,110,208, -186,208,190,209,130,208,190,209,128,209,139,208,181,208,190,208,177,208,187,208, -176,209,129,209,130,208,184,208,186,208,190,209,130,208,190,209,128,209,139,208, -185,209,135,208,181,208,187,208,190,208,178,208,181,208,186,209,129,208,184,209, -129,209,130,208,181,208,188,209,139,208,157,208,190,208,178,208,190,209,129,209, -130,208,184,208,186,208,190,209,130,208,190,209,128,209,139,209,133,208,190,208, -177,208,187,208,176,209,129,209,130,209,140,208,178,209,128,208,181,208,188,208, -181,208,189,208,184,208,186,208,190,209,130,208,190,209,128,208,176,209,143,209, -129,208,181,208,179,208,190,208,180,208,189,209,143,209,129,208,186,208,176,209, -135,208,176,209,130,209,140,208,189,208,190,208,178,208,190,209,129,209,130,208, -184,208,163,208,186,209,128,208,176,208,184,208,189,209,139,208,178,208,190,208, -191,209,128,208,190,209,129,209,139,208,186,208,190,209,130,208,190,209,128,208, -190,208,185,209,129,208,180,208,181,208,187,208,176,209,130,209,140,208,191,208, -190,208,188,208,190,209,137,209,140,209,142,209,129,209,128,208,181,208,180,209, -129,209,130,208,178,208,190,208,177,209,128,208,176,208,183,208,190,208,188,209, -129,209,130,208,190,209,128,208,190,208,189,209,139,209,131,209,135,208,176,209, -129,209,130,208,184,208,181,209,130,208,181,209,135,208,181,208,189,208,184,208, -181,208,147,208,187,208,176,208,178,208,189,208,176,209,143,208,184,209,129,209, -130,208,190,209,128,208,184,208,184,209,129,208,184,209,129,209,130,208,181,208, -188,208,176,209,128,208,181,209,136,208,181,208,189,208,184,209,143,208,161,208, -186,208,176,209,135,208,176,209,130,209,140,208,191,208,190,209,141,209,130,208, -190,208,188,209,131,209,129,208,187,208,181,208,180,209,131,208,181,209,130,209, -129,208,186,208,176,208,183,208,176,209,130,209,140,209,130,208,190,208,178,208, -176,209,128,208,190,208,178,208,186,208,190,208,189,208,181,209,135,208,189,208, -190,209,128,208,181,209,136,208,181,208,189,208,184,208,181,208,186,208,190,209, -130,208,190,209,128,208,190,208,181,208,190,209,128,208,179,208,176,208,189,208, -190,208,178,208,186,208,190,209,130,208,190,209,128,208,190,208,188,208,160,208, -181,208,186,208,187,208,176,208,188,208,176,216,167,217,132,217,133,217,134,216, -170,216,175,217,137,217,133,217,134,216,170,216,175,217,138,216,167,216,170,216, -167,217,132,217,133,217,136,216,182,217,136,216,185,216,167,217,132,216,168,216, -177,216,167,217,133,216,172,216,167,217,132,217,133,217,136,216,167,217,130,216, -185,216,167,217,132,216,177,216,179,216,167,216,166,217,132,217,133,216,180,216, -167,216,177,217,131,216,167,216,170,216,167,217,132,216,163,216,185,216,182,216, -167,216,161,216,167,217,132,216,177,217,138,216,167,216,182,216,169,216,167,217, -132,216,170,216,181,217,133,217,138,217,133,216,167,217,132,216,167,216,185,216, -182,216,167,216,161,216,167,217,132,217,134,216,170,216,167,216,166,216,172,216, -167,217,132,216,163,217,132,216,185,216,167,216,168,216,167,217,132,216,170,216, -179,216,172,217,138,217,132,216,167,217,132,216,163,217,130,216,179,216,167,217, -133,216,167,217,132,216,182,216,186,216,183,216,167,216,170,216,167,217,132,217, -129,217,138,216,175,217,138,217,136,216,167,217,132,216,170,216,177,216,173,217, -138,216,168,216,167,217,132,216,172,216,175,217,138,216,175,216,169,216,167,217, -132,216,170,216,185,217,132,217,138,217,133,216,167,217,132,216,163,216,174,216, -168,216,167,216,177,216,167,217,132,216,167,217,129,217,132,216,167,217,133,216, -167,217,132,216,163,217,129,217,132,216,167,217,133,216,167,217,132,216,170,216, -167,216,177,217,138,216,174,216,167,217,132,216,170,217,130,217,134,217,138,216, -169,216,167,217,132,216,167,217,132,216,185,216,167,216,168,216,167,217,132,216, -174,217,136,216,167,216,183,216,177,216,167,217,132,217,133,216,172,216,170,217, -133,216,185,216,167,217,132,216,175,217,138,217,131,217,136,216,177,216,167,217, -132,216,179,217,138,216,167,216,173,216,169,216,185,216,168,216,175,216,167,217, -132,217,132,217,135,216,167,217,132,216,170,216,177,216,168,217,138,216,169,216, -167,217,132,216,177,217,136,216,167,216,168,216,183,216,167,217,132,216,163,216, -175,216,168,217,138,216,169,216,167,217,132,216,167,216,174,216,168,216,167,216, -177,216,167,217,132,217,133,216,170,216,173,216,175,216,169,216,167,217,132,216, -167,216,186,216,167,217,134,217,138,99,117,114,115,111,114,58,112,111,105,110, -116,101,114,59,60,47,116,105,116,108,101,62,10,60,109,101,116,97,32,34,32,104, -114,101,102,61,34,104,116,116,112,58,47,47,34,62,60,115,112,97,110,32,99,108,97, -115,115,61,34,109,101,109,98,101,114,115,32,111,102,32,116,104,101,32,119,105, -110,100,111,119,46,108,111,99,97,116,105,111,110,118,101,114,116,105,99,97,108, -45,97,108,105,103,110,58,47,97,62,32,124,32,60,97,32,104,114,101,102,61,34,60,33 -,100,111,99,116,121,112,101,32,104,116,109,108,62,109,101,100,105,97,61,34,115, -99,114,101,101,110,34,32,60,111,112,116,105,111,110,32,118,97,108,117,101,61,34, -102,97,118,105,99,111,110,46,105,99,111,34,32,47,62,10,9,9,60,100,105,118,32,99, -108,97,115,115,61,34,99,104,97,114,97,99,116,101,114,105,115,116,105,99,115,34, -32,109,101,116,104,111,100,61,34,103,101,116,34,32,47,98,111,100,121,62,10,60,47 -,104,116,109,108,62,10,115,104,111,114,116,99,117,116,32,105,99,111,110,34,32, -100,111,99,117,109,101,110,116,46,119,114,105,116,101,40,112,97,100,100,105,110, -103,45,98,111,116,116,111,109,58,114,101,112,114,101,115,101,110,116,97,116,105, -118,101,115,115,117,98,109,105,116,34,32,118,97,108,117,101,61,34,97,108,105,103 -,110,61,34,99,101,110,116,101,114,34,32,116,104,114,111,117,103,104,111,117,116, -32,116,104,101,32,115,99,105,101,110,99,101,32,102,105,99,116,105,111,110,10,32, -32,60,100,105,118,32,99,108,97,115,115,61,34,115,117,98,109,105,116,34,32,99,108 -,97,115,115,61,34,111,110,101,32,111,102,32,116,104,101,32,109,111,115,116,32, -118,97,108,105,103,110,61,34,116,111,112,34,62,60,119,97,115,32,101,115,116,97, -98,108,105,115,104,101,100,41,59,13,10,60,47,115,99,114,105,112,116,62,13,10,114 -,101,116,117,114,110,32,102,97,108,115,101,59,34,62,41,46,115,116,121,108,101,46 -,100,105,115,112,108,97,121,98,101,99,97,117,115,101,32,111,102,32,116,104,101, -32,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,60,102,111,114,109, -32,97,99,116,105,111,110,61,34,47,125,98,111,100,121,123,109,97,114,103,105,110, -58,48,59,69,110,99,121,99,108,111,112,101,100,105,97,32,111,102,118,101,114,115, -105,111,110,32,111,102,32,116,104,101,32,46,99,114,101,97,116,101,69,108,101,109 -,101,110,116,40,110,97,109,101,34,32,99,111,110,116,101,110,116,61,34,60,47,100, -105,118,62,10,60,47,100,105,118,62,10,10,97,100,109,105,110,105,115,116,114,97, -116,105,118,101,32,60,47,98,111,100,121,62,10,60,47,104,116,109,108,62,104,105, -115,116,111,114,121,32,111,102,32,116,104,101,32,34,62,60,105,110,112,117,116,32 -,116,121,112,101,61,34,112,111,114,116,105,111,110,32,111,102,32,116,104,101,32, -97,115,32,112,97,114,116,32,111,102,32,116,104,101,32,38,110,98,115,112,59,60,97 -,32,104,114,101,102,61,34,111,116,104,101,114,32,99,111,117,110,116,114,105,101, -115,34,62,10,60,100,105,118,32,99,108,97,115,115,61,34,60,47,115,112,97,110,62, -60,47,115,112,97,110,62,60,73,110,32,111,116,104,101,114,32,119,111,114,100,115, -44,100,105,115,112,108,97,121,58,32,98,108,111,99,107,59,99,111,110,116,114,111, -108,32,111,102,32,116,104,101,32,105,110,116,114,111,100,117,99,116,105,111,110, -32,111,102,47,62,10,60,109,101,116,97,32,110,97,109,101,61,34,97,115,32,119,101, -108,108,32,97,115,32,116,104,101,32,105,110,32,114,101,99,101,110,116,32,121,101 -,97,114,115,13,10,9,60,100,105,118,32,99,108,97,115,115,61,34,60,47,100,105,118, -62,10,9,60,47,100,105,118,62,10,105,110,115,112,105,114,101,100,32,98,121,32,116 -,104,101,116,104,101,32,101,110,100,32,111,102,32,116,104,101,32,99,111,109,112, -97,116,105,98,108,101,32,119,105,116,104,98,101,99,97,109,101,32,107,110,111,119 -,110,32,97,115,32,115,116,121,108,101,61,34,109,97,114,103,105,110,58,46,106,115 -,34,62,60,47,115,99,114,105,112,116,62,60,32,73,110,116,101,114,110,97,116,105, -111,110,97,108,32,116,104,101,114,101,32,104,97,118,101,32,98,101,101,110,71,101 -,114,109,97,110,32,108,97,110,103,117,97,103,101,32,115,116,121,108,101,61,34,99 -,111,108,111,114,58,35,67,111,109,109,117,110,105,115,116,32,80,97,114,116,121, -99,111,110,115,105,115,116,101,110,116,32,119,105,116,104,98,111,114,100,101,114 -,61,34,48,34,32,99,101,108,108,32,109,97,114,103,105,110,104,101,105,103,104,116 -,61,34,116,104,101,32,109,97,106,111,114,105,116,121,32,111,102,34,32,97,108,105 -,103,110,61,34,99,101,110,116,101,114,114,101,108,97,116,101,100,32,116,111,32, -116,104,101,32,109,97,110,121,32,100,105,102,102,101,114,101,110,116,32,79,114, -116,104,111,100,111,120,32,67,104,117,114,99,104,115,105,109,105,108,97,114,32, -116,111,32,116,104,101,32,47,62,10,60,108,105,110,107,32,114,101,108,61,34,115, -119,97,115,32,111,110,101,32,111,102,32,116,104,101,32,117,110,116,105,108,32, -104,105,115,32,100,101,97,116,104,125,41,40,41,59,10,60,47,115,99,114,105,112, -116,62,111,116,104,101,114,32,108,97,110,103,117,97,103,101,115,99,111,109,112, -97,114,101,100,32,116,111,32,116,104,101,112,111,114,116,105,111,110,115,32,111, -102,32,116,104,101,116,104,101,32,78,101,116,104,101,114,108,97,110,100,115,116, -104,101,32,109,111,115,116,32,99,111,109,109,111,110,98,97,99,107,103,114,111, -117,110,100,58,117,114,108,40,97,114,103,117,101,100,32,116,104,97,116,32,116, -104,101,115,99,114,111,108,108,105,110,103,61,34,110,111,34,32,105,110,99,108, -117,100,101,100,32,105,110,32,116,104,101,78,111,114,116,104,32,65,109,101,114, -105,99,97,110,32,116,104,101,32,110,97,109,101,32,111,102,32,116,104,101,105,110 -,116,101,114,112,114,101,116,97,116,105,111,110,115,116,104,101,32,116,114,97, -100,105,116,105,111,110,97,108,100,101,118,101,108,111,112,109,101,110,116,32, -111,102,32,102,114,101,113,117,101,110,116,108,121,32,117,115,101,100,97,32,99, -111,108,108,101,99,116,105,111,110,32,111,102,118,101,114,121,32,115,105,109,105 -,108,97,114,32,116,111,115,117,114,114,111,117,110,100,105,110,103,32,116,104, -101,101,120,97,109,112,108,101,32,111,102,32,116,104,105,115,97,108,105,103,110, -61,34,99,101,110,116,101,114,34,62,119,111,117,108,100,32,104,97,118,101,32,98, -101,101,110,105,109,97,103,101,95,99,97,112,116,105,111,110,32,61,97,116,116,97, -99,104,101,100,32,116,111,32,116,104,101,115,117,103,103,101,115,116,105,110,103 -,32,116,104,97,116,105,110,32,116,104,101,32,102,111,114,109,32,111,102,32,105, -110,118,111,108,118,101,100,32,105,110,32,116,104,101,105,115,32,100,101,114,105 -,118,101,100,32,102,114,111,109,110,97,109,101,100,32,97,102,116,101,114,32,116, -104,101,73,110,116,114,111,100,117,99,116,105,111,110,32,116,111,114,101,115,116 -,114,105,99,116,105,111,110,115,32,111,110,32,115,116,121,108,101,61,34,119,105, -100,116,104,58,32,99,97,110,32,98,101,32,117,115,101,100,32,116,111,32,116,104, -101,32,99,114,101,97,116,105,111,110,32,111,102,109,111,115,116,32,105,109,112, -111,114,116,97,110,116,32,105,110,102,111,114,109,97,116,105,111,110,32,97,110, -100,114,101,115,117,108,116,101,100,32,105,110,32,116,104,101,99,111,108,108,97, -112,115,101,32,111,102,32,116,104,101,84,104,105,115,32,109,101,97,110,115,32, -116,104,97,116,101,108,101,109,101,110,116,115,32,111,102,32,116,104,101,119,97, -115,32,114,101,112,108,97,99,101,100,32,98,121,97,110,97,108,121,115,105,115,32, -111,102,32,116,104,101,105,110,115,112,105,114,97,116,105,111,110,32,102,111,114 -,114,101,103,97,114,100,101,100,32,97,115,32,116,104,101,109,111,115,116,32,115, -117,99,99,101,115,115,102,117,108,107,110,111,119,110,32,97,115,32,38,113,117, -111,116,59,97,32,99,111,109,112,114,101,104,101,110,115,105,118,101,72,105,115, -116,111,114,121,32,111,102,32,116,104,101,32,119,101,114,101,32,99,111,110,115, -105,100,101,114,101,100,114,101,116,117,114,110,101,100,32,116,111,32,116,104, -101,97,114,101,32,114,101,102,101,114,114,101,100,32,116,111,85,110,115,111,117, -114,99,101,100,32,105,109,97,103,101,62,10,9,60,100,105,118,32,99,108,97,115,115 -,61,34,99,111,110,115,105,115,116,115,32,111,102,32,116,104,101,115,116,111,112, -80,114,111,112,97,103,97,116,105,111,110,105,110,116,101,114,101,115,116,32,105, -110,32,116,104,101,97,118,97,105,108,97,98,105,108,105,116,121,32,111,102,97,112 -,112,101,97,114,115,32,116,111,32,104,97,118,101,101,108,101,99,116,114,111,109, -97,103,110,101,116,105,99,101,110,97,98,108,101,83,101,114,118,105,99,101,115,40 -,102,117,110,99,116,105,111,110,32,111,102,32,116,104,101,73,116,32,105,115,32, -105,109,112,111,114,116,97,110,116,60,47,115,99,114,105,112,116,62,60,47,100,105 -,118,62,102,117,110,99,116,105,111,110,40,41,123,118,97,114,32,114,101,108,97, -116,105,118,101,32,116,111,32,116,104,101,97,115,32,97,32,114,101,115,117,108, -116,32,111,102,32,116,104,101,32,112,111,115,105,116,105,111,110,32,111,102,70, -111,114,32,101,120,97,109,112,108,101,44,32,105,110,32,109,101,116,104,111,100, -61,34,112,111,115,116,34,32,119,97,115,32,102,111,108,108,111,119,101,100,32,98, -121,38,97,109,112,59,109,100,97,115,104,59,32,116,104,101,116,104,101,32,97,112, -112,108,105,99,97,116,105,111,110,106,115,34,62,60,47,115,99,114,105,112,116,62, -13,10,117,108,62,60,47,100,105,118,62,60,47,100,105,118,62,97,102,116,101,114,32 -,116,104,101,32,100,101,97,116,104,119,105,116,104,32,114,101,115,112,101,99,116 -,32,116,111,115,116,121,108,101,61,34,112,97,100,100,105,110,103,58,105,115,32, -112,97,114,116,105,99,117,108,97,114,108,121,100,105,115,112,108,97,121,58,105, -110,108,105,110,101,59,32,116,121,112,101,61,34,115,117,98,109,105,116,34,32,105 -,115,32,100,105,118,105,100,101,100,32,105,110,116,111,228,184,173,230,150,135, -32,40,231,174,128,228,189,147,41,114,101,115,112,111,110,115,97,98,105,108,105, -100,97,100,97,100,109,105,110,105,115,116,114,97,99,105,195,179,110,105,110,116, -101,114,110,97,99,105,111,110,97,108,101,115,99,111,114,114,101,115,112,111,110, -100,105,101,110,116,101,224,164,137,224,164,170,224,164,175,224,165,139,224,164, -151,224,164,170,224,165,130,224,164,176,224,165,141,224,164,181,224,164,185,224, -164,174,224,164,190,224,164,176,224,165,135,224,164,178,224,165,139,224,164,151, -224,165,139,224,164,130,224,164,154,224,165,129,224,164,168,224,164,190,224,164, -181,224,164,178,224,165,135,224,164,149,224,164,191,224,164,168,224,164,184,224, -164,176,224,164,149,224,164,190,224,164,176,224,164,170,224,165,129,224,164,178, -224,164,191,224,164,184,224,164,150,224,165,139,224,164,156,224,165,135,224,164, -130,224,164,154,224,164,190,224,164,185,224,164,191,224,164,143,224,164,173,224, -165,135,224,164,156,224,165,135,224,164,130,224,164,182,224,164,190,224,164,174, -224,164,191,224,164,178,224,164,185,224,164,174,224,164,190,224,164,176,224,165, -128,224,164,156,224,164,190,224,164,151,224,164,176,224,164,163,224,164,172,224, -164,168,224,164,190,224,164,168,224,165,135,224,164,149,224,165,129,224,164,174, -224,164,190,224,164,176,224,164,172,224,165,141,224,164,178,224,165,137,224,164, -151,224,164,174,224,164,190,224,164,178,224,164,191,224,164,149,224,164,174,224, -164,185,224,164,191,224,164,178,224,164,190,224,164,170,224,165,131,224,164,183, -224,165,141,224,164,160,224,164,172,224,164,162,224,164,188,224,164,164,224,165, -135,224,164,173,224,164,190,224,164,156,224,164,170,224,164,190,224,164,149,224, -165,141,224,164,178,224,164,191,224,164,149,224,164,159,224,165,141,224,164,176, -224,165,135,224,164,168,224,164,150,224,164,191,224,164,178,224,164,190,224,164, -171,224,164,166,224,165,140,224,164,176,224,164,190,224,164,168,224,164,174,224, -164,190,224,164,174,224,164,178,224,165,135,224,164,174,224,164,164,224,164,166, -224,164,190,224,164,168,224,164,172,224,164,190,224,164,156,224,164,190,224,164, -176,224,164,181,224,164,191,224,164,149,224,164,190,224,164,184,224,164,149,224, -165,141,224,164,175,224,165,139,224,164,130,224,164,154,224,164,190,224,164,185, -224,164,164,224,165,135,224,164,170,224,164,185,224,165,129,224,164,129,224,164, -154,224,164,172,224,164,164,224,164,190,224,164,175,224,164,190,224,164,184,224, -164,130,224,164,181,224,164,190,224,164,166,224,164,166,224,165,135,224,164,150, -224,164,168,224,165,135,224,164,170,224,164,191,224,164,155,224,164,178,224,165, -135,224,164,181,224,164,191,224,164,182,224,165,135,224,164,183,224,164,176,224, -164,190,224,164,156,224,165,141,224,164,175,224,164,137,224,164,164,224,165,141, -224,164,164,224,164,176,224,164,174,224,165,129,224,164,130,224,164,172,224,164, -136,224,164,166,224,165,139,224,164,168,224,165,139,224,164,130,224,164,137,224, -164,170,224,164,149,224,164,176,224,164,163,224,164,170,224,164,162,224,164,188, -224,165,135,224,164,130,224,164,184,224,165,141,224,164,165,224,164,191,224,164, -164,224,164,171,224,164,191,224,164,178,224,165,141,224,164,174,224,164,174,224, -165,129,224,164,150,224,165,141,224,164,175,224,164,133,224,164,154,224,165,141, -224,164,155,224,164,190,224,164,155,224,165,130,224,164,159,224,164,164,224,165, -128,224,164,184,224,164,130,224,164,151,224,165,128,224,164,164,224,164,156,224, -164,190,224,164,143,224,164,151,224,164,190,224,164,181,224,164,191,224,164,173, -224,164,190,224,164,151,224,164,152,224,164,163,224,165,141,224,164,159,224,165, -135,224,164,166,224,165,130,224,164,184,224,164,176,224,165,135,224,164,166,224, -164,191,224,164,168,224,165,139,224,164,130,224,164,185,224,164,164,224,165,141, -224,164,175,224,164,190,224,164,184,224,165,135,224,164,149,224,165,141,224,164, -184,224,164,151,224,164,190,224,164,130,224,164,167,224,165,128,224,164,181,224, -164,191,224,164,182,224,165,141,224,164,181,224,164,176,224,164,190,224,164,164, -224,165,135,224,164,130,224,164,166,224,165,136,224,164,159,224,165,141,224,164, -184,224,164,168,224,164,149,224,165,141,224,164,182,224,164,190,224,164,184,224, -164,190,224,164,174,224,164,168,224,165,135,224,164,133,224,164,166,224,164,190, -224,164,178,224,164,164,224,164,172,224,164,191,224,164,156,224,164,178,224,165, -128,224,164,170,224,165,129,224,164,176,224,165,130,224,164,183,224,164,185,224, -164,191,224,164,130,224,164,166,224,165,128,224,164,174,224,164,191,224,164,164, -224,165,141,224,164,176,224,164,149,224,164,181,224,164,191,224,164,164,224,164, -190,224,164,176,224,165,129,224,164,170,224,164,175,224,165,135,224,164,184,224, -165,141,224,164,165,224,164,190,224,164,168,224,164,149,224,164,176,224,165,139, -224,164,161,224,164,188,224,164,174,224,165,129,224,164,149,224,165,141,224,164, -164,224,164,175,224,165,139,224,164,156,224,164,168,224,164,190,224,164,149,224, -165,131,224,164,170,224,164,175,224,164,190,224,164,170,224,165,139,224,164,184, -224,165,141,224,164,159,224,164,152,224,164,176,224,165,135,224,164,178,224,165, -130,224,164,149,224,164,190,224,164,176,224,165,141,224,164,175,224,164,181,224, -164,191,224,164,154,224,164,190,224,164,176,224,164,184,224,165,130,224,164,154, -224,164,168,224,164,190,224,164,174,224,165,130,224,164,178,224,165,141,224,164, -175,224,164,166,224,165,135,224,164,150,224,165,135,224,164,130,224,164,185,224, -164,174,224,165,135,224,164,182,224,164,190,224,164,184,224,165,141,224,164,149, -224,165,130,224,164,178,224,164,174,224,165,136,224,164,130,224,164,168,224,165, -135,224,164,164,224,165,136,224,164,175,224,164,190,224,164,176,224,164,156,224, -164,191,224,164,184,224,164,149,224,165,135,114,115,115,43,120,109,108,34,32,116 -,105,116,108,101,61,34,45,116,121,112,101,34,32,99,111,110,116,101,110,116,61,34 -,116,105,116,108,101,34,32,99,111,110,116,101,110,116,61,34,97,116,32,116,104, -101,32,115,97,109,101,32,116,105,109,101,46,106,115,34,62,60,47,115,99,114,105, -112,116,62,10,60,34,32,109,101,116,104,111,100,61,34,112,111,115,116,34,32,60,47 -,115,112,97,110,62,60,47,97,62,60,47,108,105,62,118,101,114,116,105,99,97,108,45 -,97,108,105,103,110,58,116,47,106,113,117,101,114,121,46,109,105,110,46,106,115, -34,62,46,99,108,105,99,107,40,102,117,110,99,116,105,111,110,40,32,115,116,121, -108,101,61,34,112,97,100,100,105,110,103,45,125,41,40,41,59,10,60,47,115,99,114, -105,112,116,62,10,60,47,115,112,97,110,62,60,97,32,104,114,101,102,61,34,60,97, -32,104,114,101,102,61,34,104,116,116,112,58,47,47,41,59,32,114,101,116,117,114, -110,32,102,97,108,115,101,59,116,101,120,116,45,100,101,99,111,114,97,116,105, -111,110,58,32,115,99,114,111,108,108,105,110,103,61,34,110,111,34,32,98,111,114, -100,101,114,45,99,111,108,108,97,112,115,101,58,97,115,115,111,99,105,97,116,101 -,100,32,119,105,116,104,32,66,97,104,97,115,97,32,73,110,100,111,110,101,115,105 -,97,69,110,103,108,105,115,104,32,108,97,110,103,117,97,103,101,60,116,101,120, -116,32,120,109,108,58,115,112,97,99,101,61,46,103,105,102,34,32,98,111,114,100, -101,114,61,34,48,34,60,47,98,111,100,121,62,10,60,47,104,116,109,108,62,10,111, -118,101,114,102,108,111,119,58,104,105,100,100,101,110,59,105,109,103,32,115,114 -,99,61,34,104,116,116,112,58,47,47,97,100,100,69,118,101,110,116,76,105,115,116, -101,110,101,114,114,101,115,112,111,110,115,105,98,108,101,32,102,111,114,32,115 -,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,47,102,97,118,105,99,111, -110,46,105,99,111,34,32,47,62,111,112,101,114,97,116,105,110,103,32,115,121,115, -116,101,109,34,32,115,116,121,108,101,61,34,119,105,100,116,104,58,49,116,97,114 -,103,101,116,61,34,95,98,108,97,110,107,34,62,83,116,97,116,101,32,85,110,105, -118,101,114,115,105,116,121,116,101,120,116,45,97,108,105,103,110,58,108,101,102 -,116,59,10,100,111,99,117,109,101,110,116,46,119,114,105,116,101,40,44,32,105, -110,99,108,117,100,105,110,103,32,116,104,101,32,97,114,111,117,110,100,32,116, -104,101,32,119,111,114,108,100,41,59,13,10,60,47,115,99,114,105,112,116,62,13,10 -,60,34,32,115,116,121,108,101,61,34,104,101,105,103,104,116,58,59,111,118,101, -114,102,108,111,119,58,104,105,100,100,101,110,109,111,114,101,32,105,110,102, -111,114,109,97,116,105,111,110,97,110,32,105,110,116,101,114,110,97,116,105,111, -110,97,108,97,32,109,101,109,98,101,114,32,111,102,32,116,104,101,32,111,110,101 -,32,111,102,32,116,104,101,32,102,105,114,115,116,99,97,110,32,98,101,32,102,111 -,117,110,100,32,105,110,32,60,47,100,105,118,62,10,9,9,60,47,100,105,118,62,10, -100,105,115,112,108,97,121,58,32,110,111,110,101,59,34,62,34,32,47,62,10,60,108, -105,110,107,32,114,101,108,61,34,10,32,32,40,102,117,110,99,116,105,111,110,40, -41,32,123,116,104,101,32,49,53,116,104,32,99,101,110,116,117,114,121,46,112,114, -101,118,101,110,116,68,101,102,97,117,108,116,40,108,97,114,103,101,32,110,117, -109,98,101,114,32,111,102,32,66,121,122,97,110,116,105,110,101,32,69,109,112,105 -,114,101,46,106,112,103,124,116,104,117,109,98,124,108,101,102,116,124,118,97, -115,116,32,109,97,106,111,114,105,116,121,32,111,102,109,97,106,111,114,105,116, -121,32,111,102,32,116,104,101,32,32,97,108,105,103,110,61,34,99,101,110,116,101, -114,34,62,85,110,105,118,101,114,115,105,116,121,32,80,114,101,115,115,100,111, -109,105,110,97,116,101,100,32,98,121,32,116,104,101,83,101,99,111,110,100,32,87, -111,114,108,100,32,87,97,114,100,105,115,116,114,105,98,117,116,105,111,110,32, -111,102,32,115,116,121,108,101,61,34,112,111,115,105,116,105,111,110,58,116,104, -101,32,114,101,115,116,32,111,102,32,116,104,101,32,99,104,97,114,97,99,116,101, -114,105,122,101,100,32,98,121,32,114,101,108,61,34,110,111,102,111,108,108,111, -119,34,62,100,101,114,105,118,101,115,32,102,114,111,109,32,116,104,101,114,97, -116,104,101,114,32,116,104,97,110,32,116,104,101,32,97,32,99,111,109,98,105,110, -97,116,105,111,110,32,111,102,115,116,121,108,101,61,34,119,105,100,116,104,58, -49,48,48,69,110,103,108,105,115,104,45,115,112,101,97,107,105,110,103,99,111,109 -,112,117,116,101,114,32,115,99,105,101,110,99,101,98,111,114,100,101,114,61,34, -48,34,32,97,108,116,61,34,116,104,101,32,101,120,105,115,116,101,110,99,101,32, -111,102,68,101,109,111,99,114,97,116,105,99,32,80,97,114,116,121,34,32,115,116, -121,108,101,61,34,109,97,114,103,105,110,45,70,111,114,32,116,104,105,115,32,114 -,101,97,115,111,110,44,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,9,115 -,66,121,84,97,103,78,97,109,101,40,115,41,91,48,93,106,115,34,62,60,47,115,99, -114,105,112,116,62,13,10,60,46,106,115,34,62,60,47,115,99,114,105,112,116,62,13, -10,108,105,110,107,32,114,101,108,61,34,105,99,111,110,34,32,39,32,97,108,116,61 -,39,39,32,99,108,97,115,115,61,39,102,111,114,109,97,116,105,111,110,32,111,102, -32,116,104,101,118,101,114,115,105,111,110,115,32,111,102,32,116,104,101,32,60, -47,97,62,60,47,100,105,118,62,60,47,100,105,118,62,47,112,97,103,101,62,10,32,32 -,60,112,97,103,101,62,10,60,100,105,118,32,99,108,97,115,115,61,34,99,111,110, -116,98,101,99,97,109,101,32,116,104,101,32,102,105,114,115,116,98,97,104,97,115, -97,32,73,110,100,111,110,101,115,105,97,101,110,103,108,105,115,104,32,40,115, -105,109,112,108,101,41,206,149,206,187,206,187,206,183,206,189,206,185,206,186, -206,172,209,133,209,128,208,178,208,176,209,130,209,129,208,186,208,184,208,186, -208,190,208,188,208,191,208,176,208,189,208,184,208,184,209,143,208,178,208,187, -209,143,208,181,209,130,209,129,209,143,208,148,208,190,208,177,208,176,208,178, -208,184,209,130,209,140,209,135,208,181,208,187,208,190,208,178,208,181,208,186, -208,176,209,128,208,176,208,183,208,178,208,184,209,130,208,184,209,143,208,152, -208,189,209,130,208,181,209,128,208,189,208,181,209,130,208,158,209,130,208,178, -208,181,209,130,208,184,209,130,209,140,208,189,208,176,208,191,209,128,208,184, -208,188,208,181,209,128,208,184,208,189,209,130,208,181,209,128,208,189,208,181, -209,130,208,186,208,190,209,130,208,190,209,128,208,190,208,179,208,190,209,129, -209,130,209,128,208,176,208,189,208,184,209,134,209,139,208,186,208,176,209,135, -208,181,209,129,209,130,208,178,208,181,209,131,209,129,208,187,208,190,208,178, -208,184,209,143,209,133,208,191,209,128,208,190,208,177,208,187,208,181,208,188, -209,139,208,191,208,190,208,187,209,131,209,135,208,184,209,130,209,140,209,143, -208,178,208,187,209,143,209,142,209,130,209,129,209,143,208,189,208,176,208,184, -208,177,208,190,208,187,208,181,208,181,208,186,208,190,208,188,208,191,208,176, -208,189,208,184,209,143,208,178,208,189,208,184,208,188,208,176,208,189,208,184, -208,181,209,129,209,128,208,181,208,180,209,129,209,130,208,178,208,176,216,167, -217,132,217,133,217,136,216,167,216,182,217,138,216,185,216,167,217,132,216,177, -216,166,217,138,216,179,217,138,216,169,216,167,217,132,216,167,217,134,216,170, -217,130,216,167,217,132,217,133,216,180,216,167,216,177,217,131,216,167,216,170, -217,131,216,167,217,132,216,179,217,138,216,167,216,177,216,167,216,170,216,167, -217,132,217,133,217,131,216,170,217,136,216,168,216,169,216,167,217,132,216,179, -216,185,217,136,216,175,217,138,216,169,216,167,216,173,216,181,216,167,216,166, -217,138,216,167,216,170,216,167,217,132,216,185,216,167,217,132,217,133,217,138, -216,169,216,167,217,132,216,181,217,136,216,170,217,138,216,167,216,170,216,167, -217,132,216,167,217,134,216,170,216,177,217,134,216,170,216,167,217,132,216,170, -216,181,216,167,217,133,217,138,217,133,216,167,217,132,216,165,216,179,217,132, -216,167,217,133,217,138,216,167,217,132,217,133,216,180,216,167,216,177,217,131, -216,169,216,167,217,132,217,133,216,177,216,166,217,138,216,167,216,170,114,111, -98,111,116,115,34,32,99,111,110,116,101,110,116,61,34,60,100,105,118,32,105,100, -61,34,102,111,111,116,101,114,34,62,116,104,101,32,85,110,105,116,101,100,32,83, -116,97,116,101,115,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47, -46,106,112,103,124,114,105,103,104,116,124,116,104,117,109,98,124,46,106,115,34, -62,60,47,115,99,114,105,112,116,62,13,10,60,108,111,99,97,116,105,111,110,46,112 -,114,111,116,111,99,111,108,102,114,97,109,101,98,111,114,100,101,114,61,34,48, -34,32,115,34,32,47,62,10,60,109,101,116,97,32,110,97,109,101,61,34,60,47,97,62, -60,47,100,105,118,62,60,47,100,105,118,62,60,102,111,110,116,45,119,101,105,103, -104,116,58,98,111,108,100,59,38,113,117,111,116,59,32,97,110,100,32,38,113,117, -111,116,59,100,101,112,101,110,100,105,110,103,32,111,110,32,116,104,101,32,109, -97,114,103,105,110,58,48,59,112,97,100,100,105,110,103,58,34,32,114,101,108,61, -34,110,111,102,111,108,108,111,119,34,32,80,114,101,115,105,100,101,110,116,32, -111,102,32,116,104,101,32,116,119,101,110,116,105,101,116,104,32,99,101,110,116, -117,114,121,101,118,105,115,105,111,110,62,10,32,32,60,47,112,97,103,101,73,110, -116,101,114,110,101,116,32,69,120,112,108,111,114,101,114,97,46,97,115,121,110, -99,32,61,32,116,114,117,101,59,13,10,105,110,102,111,114,109,97,116,105,111,110, -32,97,98,111,117,116,60,100,105,118,32,105,100,61,34,104,101,97,100,101,114,34, -62,34,32,97,99,116,105,111,110,61,34,104,116,116,112,58,47,47,60,97,32,104,114, -101,102,61,34,104,116,116,112,115,58,47,47,60,100,105,118,32,105,100,61,34,99, -111,110,116,101,110,116,34,60,47,100,105,118,62,13,10,60,47,100,105,118,62,13,10 -,60,100,101,114,105,118,101,100,32,102,114,111,109,32,116,104,101,32,60,105,109, -103,32,115,114,99,61,39,104,116,116,112,58,47,47,97,99,99,111,114,100,105,110, -103,32,116,111,32,116,104,101,32,10,60,47,98,111,100,121,62,10,60,47,104,116,109 -,108,62,10,115,116,121,108,101,61,34,102,111,110,116,45,115,105,122,101,58,115, -99,114,105,112,116,32,108,97,110,103,117,97,103,101,61,34,65,114,105,97,108,44, -32,72,101,108,118,101,116,105,99,97,44,60,47,97,62,60,115,112,97,110,32,99,108, -97,115,115,61,34,60,47,115,99,114,105,112,116,62,60,115,99,114,105,112,116,32, -112,111,108,105,116,105,99,97,108,32,112,97,114,116,105,101,115,116,100,62,60,47 -,116,114,62,60,47,116,97,98,108,101,62,60,104,114,101,102,61,34,104,116,116,112, -58,47,47,119,119,119,46,105,110,116,101,114,112,114,101,116,97,116,105,111,110, -32,111,102,114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,32,100, -111,99,117,109,101,110,116,46,119,114,105,116,101,40,39,60,99,104,97,114,115,101 -,116,61,34,117,116,102,45,56,34,62,10,98,101,103,105,110,110,105,110,103,32,111, -102,32,116,104,101,32,114,101,118,101,97,108,101,100,32,116,104,97,116,32,116, -104,101,116,101,108,101,118,105,115,105,111,110,32,115,101,114,105,101,115,34,32 -,114,101,108,61,34,110,111,102,111,108,108,111,119,34,62,32,116,97,114,103,101, -116,61,34,95,98,108,97,110,107,34,62,99,108,97,105,109,105,110,103,32,116,104,97 -,116,32,116,104,101,104,116,116,112,37,51,65,37,50,70,37,50,70,119,119,119,46, -109,97,110,105,102,101,115,116,97,116,105,111,110,115,32,111,102,80,114,105,109, -101,32,77,105,110,105,115,116,101,114,32,111,102,105,110,102,108,117,101,110,99, -101,100,32,98,121,32,116,104,101,99,108,97,115,115,61,34,99,108,101,97,114,102, -105,120,34,62,47,100,105,118,62,13,10,60,47,100,105,118,62,13,10,13,10,116,104, -114,101,101,45,100,105,109,101,110,115,105,111,110,97,108,67,104,117,114,99,104, -32,111,102,32,69,110,103,108,97,110,100,111,102,32,78,111,114,116,104,32,67,97, -114,111,108,105,110,97,115,113,117,97,114,101,32,107,105,108,111,109,101,116,114 -,101,115,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110,101,114,100,105 -,115,116,105,110,99,116,32,102,114,111,109,32,116,104,101,99,111,109,109,111,110 -,108,121,32,107,110,111,119,110,32,97,115,80,104,111,110,101,116,105,99,32,65, -108,112,104,97,98,101,116,100,101,99,108,97,114,101,100,32,116,104,97,116,32,116 -,104,101,99,111,110,116,114,111,108,108,101,100,32,98,121,32,116,104,101,66,101, -110,106,97,109,105,110,32,70,114,97,110,107,108,105,110,114,111,108,101,45,112, -108,97,121,105,110,103,32,103,97,109,101,116,104,101,32,85,110,105,118,101,114, -115,105,116,121,32,111,102,105,110,32,87,101,115,116,101,114,110,32,69,117,114, -111,112,101,112,101,114,115,111,110,97,108,32,99,111,109,112,117,116,101,114,80, -114,111,106,101,99,116,32,71,117,116,101,110,98,101,114,103,114,101,103,97,114, -100,108,101,115,115,32,111,102,32,116,104,101,104,97,115,32,98,101,101,110,32, -112,114,111,112,111,115,101,100,116,111,103,101,116,104,101,114,32,119,105,116, -104,32,116,104,101,62,60,47,108,105,62,60,108,105,32,99,108,97,115,115,61,34,105 -,110,32,115,111,109,101,32,99,111,117,110,116,114,105,101,115,109,105,110,46,106 -,115,34,62,60,47,115,99,114,105,112,116,62,111,102,32,116,104,101,32,112,111,112 -,117,108,97,116,105,111,110,111,102,102,105,99,105,97,108,32,108,97,110,103,117, -97,103,101,60,105,109,103,32,115,114,99,61,34,105,109,97,103,101,115,47,105,100, -101,110,116,105,102,105,101,100,32,98,121,32,116,104,101,110,97,116,117,114,97, -108,32,114,101,115,111,117,114,99,101,115,99,108,97,115,115,105,102,105,99,97, -116,105,111,110,32,111,102,99,97,110,32,98,101,32,99,111,110,115,105,100,101,114 -,101,100,113,117,97,110,116,117,109,32,109,101,99,104,97,110,105,99,115,78,101, -118,101,114,116,104,101,108,101,115,115,44,32,116,104,101,109,105,108,108,105, -111,110,32,121,101,97,114,115,32,97,103,111,60,47,98,111,100,121,62,13,10,60,47, -104,116,109,108,62,13,206,149,206,187,206,187,206,183,206,189,206,185,206,186, -206,172,10,116,97,107,101,32,97,100,118,97,110,116,97,103,101,32,111,102,97,110, -100,44,32,97,99,99,111,114,100,105,110,103,32,116,111,97,116,116,114,105,98,117, -116,101,100,32,116,111,32,116,104,101,77,105,99,114,111,115,111,102,116,32,87, -105,110,100,111,119,115,116,104,101,32,102,105,114,115,116,32,99,101,110,116,117 -,114,121,117,110,100,101,114,32,116,104,101,32,99,111,110,116,114,111,108,100, -105,118,32,99,108,97,115,115,61,34,104,101,97,100,101,114,115,104,111,114,116, -108,121,32,97,102,116,101,114,32,116,104,101,110,111,116,97,98,108,101,32,101, -120,99,101,112,116,105,111,110,116,101,110,115,32,111,102,32,116,104,111,117,115 -,97,110,100,115,115,101,118,101,114,97,108,32,100,105,102,102,101,114,101,110, -116,97,114,111,117,110,100,32,116,104,101,32,119,111,114,108,100,46,114,101,97, -99,104,105,110,103,32,109,105,108,105,116,97,114,121,105,115,111,108,97,116,101, -100,32,102,114,111,109,32,116,104,101,111,112,112,111,115,105,116,105,111,110,32 -,116,111,32,116,104,101,116,104,101,32,79,108,100,32,84,101,115,116,97,109,101, -110,116,65,102,114,105,99,97,110,32,65,109,101,114,105,99,97,110,115,105,110,115 -,101,114,116,101,100,32,105,110,116,111,32,116,104,101,115,101,112,97,114,97,116 -,101,32,102,114,111,109,32,116,104,101,109,101,116,114,111,112,111,108,105,116, -97,110,32,97,114,101,97,109,97,107,101,115,32,105,116,32,112,111,115,115,105,98, -108,101,97,99,107,110,111,119,108,101,100,103,101,100,32,116,104,97,116,97,114, -103,117,97,98,108,121,32,116,104,101,32,109,111,115,116,116,121,112,101,61,34, -116,101,120,116,47,99,115,115,34,62,10,116,104,101,32,73,110,116,101,114,110,97, -116,105,111,110,97,108,65,99,99,111,114,100,105,110,103,32,116,111,32,116,104, -101,32,112,101,61,34,116,101,120,116,47,99,115,115,34,32,47,62,10,99,111,105,110 -,99,105,100,101,32,119,105,116,104,32,116,104,101,116,119,111,45,116,104,105,114 -,100,115,32,111,102,32,116,104,101,68,117,114,105,110,103,32,116,104,105,115,32, -116,105,109,101,44,100,117,114,105,110,103,32,116,104,101,32,112,101,114,105,111 -,100,97,110,110,111,117,110,99,101,100,32,116,104,97,116,32,104,101,116,104,101, -32,105,110,116,101,114,110,97,116,105,111,110,97,108,97,110,100,32,109,111,114, -101,32,114,101,99,101,110,116,108,121,98,101,108,105,101,118,101,100,32,116,104, -97,116,32,116,104,101,99,111,110,115,99,105,111,117,115,110,101,115,115,32,97, -110,100,102,111,114,109,101,114,108,121,32,107,110,111,119,110,32,97,115,115,117 -,114,114,111,117,110,100,101,100,32,98,121,32,116,104,101,102,105,114,115,116,32 -,97,112,112,101,97,114,101,100,32,105,110,111,99,99,97,115,105,111,110,97,108, -108,121,32,117,115,101,100,112,111,115,105,116,105,111,110,58,97,98,115,111,108, -117,116,101,59,34,32,116,97,114,103,101,116,61,34,95,98,108,97,110,107,34,32,112 -,111,115,105,116,105,111,110,58,114,101,108,97,116,105,118,101,59,116,101,120, -116,45,97,108,105,103,110,58,99,101,110,116,101,114,59,106,97,120,47,108,105,98, -115,47,106,113,117,101,114,121,47,49,46,98,97,99,107,103,114,111,117,110,100,45, -99,111,108,111,114,58,35,116,121,112,101,61,34,97,112,112,108,105,99,97,116,105, -111,110,47,97,110,103,117,97,103,101,34,32,99,111,110,116,101,110,116,61,34,60, -109,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,80,114,105,118,97 -,99,121,32,80,111,108,105,99,121,60,47,97,62,101,40,34,37,51,67,115,99,114,105, -112,116,32,115,114,99,61,39,34,32,116,97,114,103,101,116,61,34,95,98,108,97,110, -107,34,62,79,110,32,116,104,101,32,111,116,104,101,114,32,104,97,110,100,44,46, -106,112,103,124,116,104,117,109,98,124,114,105,103,104,116,124,50,60,47,100,105, -118,62,60,100,105,118,32,99,108,97,115,115,61,34,60,100,105,118,32,115,116,121, -108,101,61,34,102,108,111,97,116,58,110,105,110,101,116,101,101,110,116,104,32, -99,101,110,116,117,114,121,60,47,98,111,100,121,62,13,10,60,47,104,116,109,108, -62,13,10,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,115,59,116, -101,120,116,45,97,108,105,103,110,58,99,101,110,116,101,114,102,111,110,116,45, -119,101,105,103,104,116,58,32,98,111,108,100,59,32,65,99,99,111,114,100,105,110, -103,32,116,111,32,116,104,101,32,100,105,102,102,101,114,101,110,99,101,32,98, -101,116,119,101,101,110,34,32,102,114,97,109,101,98,111,114,100,101,114,61,34,48 -,34,32,34,32,115,116,121,108,101,61,34,112,111,115,105,116,105,111,110,58,108, -105,110,107,32,104,114,101,102,61,34,104,116,116,112,58,47,47,104,116,109,108,52 -,47,108,111,111,115,101,46,100,116,100,34,62,10,100,117,114,105,110,103,32,116, -104,105,115,32,112,101,114,105,111,100,60,47,116,100,62,60,47,116,114,62,60,47, -116,97,98,108,101,62,99,108,111,115,101,108,121,32,114,101,108,97,116,101,100,32 -,116,111,102,111,114,32,116,104,101,32,102,105,114,115,116,32,116,105,109,101,59 -,102,111,110,116,45,119,101,105,103,104,116,58,98,111,108,100,59,105,110,112,117 -,116,32,116,121,112,101,61,34,116,101,120,116,34,32,60,115,112,97,110,32,115,116 -,121,108,101,61,34,102,111,110,116,45,111,110,114,101,97,100,121,115,116,97,116, -101,99,104,97,110,103,101,9,60,100,105,118,32,99,108,97,115,115,61,34,99,108,101 -,97,114,100,111,99,117,109,101,110,116,46,108,111,99,97,116,105,111,110,46,32,70 -,111,114,32,101,120,97,109,112,108,101,44,32,116,104,101,32,97,32,119,105,100, -101,32,118,97,114,105,101,116,121,32,111,102,32,60,33,68,79,67,84,89,80,69,32, -104,116,109,108,62,13,10,60,38,110,98,115,112,59,38,110,98,115,112,59,38,110,98, -115,112,59,34,62,60,97,32,104,114,101,102,61,34,104,116,116,112,58,47,47,115,116 -,121,108,101,61,34,102,108,111,97,116,58,108,101,102,116,59,99,111,110,99,101, -114,110,101,100,32,119,105,116,104,32,116,104,101,61,104,116,116,112,37,51,65,37 -,50,70,37,50,70,119,119,119,46,105,110,32,112,111,112,117,108,97,114,32,99,117, -108,116,117,114,101,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,32,47 -,62,105,116,32,105,115,32,112,111,115,115,105,98,108,101,32,116,111,32,72,97,114 -,118,97,114,100,32,85,110,105,118,101,114,115,105,116,121,116,121,108,101,115, -104,101,101,116,34,32,104,114,101,102,61,34,47,116,104,101,32,109,97,105,110,32, -99,104,97,114,97,99,116,101,114,79,120,102,111,114,100,32,85,110,105,118,101,114 -,115,105,116,121,32,32,110,97,109,101,61,34,107,101,121,119,111,114,100,115,34, -32,99,115,116,121,108,101,61,34,116,101,120,116,45,97,108,105,103,110,58,116,104 -,101,32,85,110,105,116,101,100,32,75,105,110,103,100,111,109,102,101,100,101,114 -,97,108,32,103,111,118,101,114,110,109,101,110,116,60,100,105,118,32,115,116,121 -,108,101,61,34,109,97,114,103,105,110,32,100,101,112,101,110,100,105,110,103,32, -111,110,32,116,104,101,32,100,101,115,99,114,105,112,116,105,111,110,32,111,102, -32,116,104,101,60,100,105,118,32,99,108,97,115,115,61,34,104,101,97,100,101,114, -46,109,105,110,46,106,115,34,62,60,47,115,99,114,105,112,116,62,100,101,115,116, -114,117,99,116,105,111,110,32,111,102,32,116,104,101,115,108,105,103,104,116,108 -,121,32,100,105,102,102,101,114,101,110,116,105,110,32,97,99,99,111,114,100,97, -110,99,101,32,119,105,116,104,116,101,108,101,99,111,109,109,117,110,105,99,97, -116,105,111,110,115,105,110,100,105,99,97,116,101,115,32,116,104,97,116,32,116, -104,101,115,104,111,114,116,108,121,32,116,104,101,114,101,97,102,116,101,114, -101,115,112,101,99,105,97,108,108,121,32,105,110,32,116,104,101,32,69,117,114, -111,112,101,97,110,32,99,111,117,110,116,114,105,101,115,72,111,119,101,118,101, -114,44,32,116,104,101,114,101,32,97,114,101,115,114,99,61,34,104,116,116,112,58, -47,47,115,116,97,116,105,99,115,117,103,103,101,115,116,101,100,32,116,104,97, -116,32,116,104,101,34,32,115,114,99,61,34,104,116,116,112,58,47,47,119,119,119, -46,97,32,108,97,114,103,101,32,110,117,109,98,101,114,32,111,102,32,84,101,108, -101,99,111,109,109,117,110,105,99,97,116,105,111,110,115,34,32,114,101,108,61,34 -,110,111,102,111,108,108,111,119,34,32,116,72,111,108,121,32,82,111,109,97,110, -32,69,109,112,101,114,111,114,97,108,109,111,115,116,32,101,120,99,108,117,115, -105,118,101,108,121,34,32,98,111,114,100,101,114,61,34,48,34,32,97,108,116,61,34 -,83,101,99,114,101,116,97,114,121,32,111,102,32,83,116,97,116,101,99,117,108,109 -,105,110,97,116,105,110,103,32,105,110,32,116,104,101,67,73,65,32,87,111,114,108 -,100,32,70,97,99,116,98,111,111,107,116,104,101,32,109,111,115,116,32,105,109, -112,111,114,116,97,110,116,97,110,110,105,118,101,114,115,97,114,121,32,111,102, -32,116,104,101,115,116,121,108,101,61,34,98,97,99,107,103,114,111,117,110,100,45 -,60,108,105,62,60,101,109,62,60,97,32,104,114,101,102,61,34,47,116,104,101,32,65 -,116,108,97,110,116,105,99,32,79,99,101,97,110,115,116,114,105,99,116,108,121,32 -,115,112,101,97,107,105,110,103,44,115,104,111,114,116,108,121,32,98,101,102,111 -,114,101,32,116,104,101,100,105,102,102,101,114,101,110,116,32,116,121,112,101, -115,32,111,102,116,104,101,32,79,116,116,111,109,97,110,32,69,109,112,105,114, -101,62,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,65,110,32,73, -110,116,114,111,100,117,99,116,105,111,110,32,116,111,99,111,110,115,101,113,117 -,101,110,99,101,32,111,102,32,116,104,101,100,101,112,97,114,116,117,114,101,32, -102,114,111,109,32,116,104,101,67,111,110,102,101,100,101,114,97,116,101,32,83, -116,97,116,101,115,105,110,100,105,103,101,110,111,117,115,32,112,101,111,112, -108,101,115,80,114,111,99,101,101,100,105,110,103,115,32,111,102,32,116,104,101, -105,110,102,111,114,109,97,116,105,111,110,32,111,110,32,116,104,101,116,104,101 -,111,114,105,101,115,32,104,97,118,101,32,98,101,101,110,105,110,118,111,108,118 -,101,109,101,110,116,32,105,110,32,116,104,101,100,105,118,105,100,101,100,32, -105,110,116,111,32,116,104,114,101,101,97,100,106,97,99,101,110,116,32,99,111, -117,110,116,114,105,101,115,105,115,32,114,101,115,112,111,110,115,105,98,108, -101,32,102,111,114,100,105,115,115,111,108,117,116,105,111,110,32,111,102,32,116 -,104,101,99,111,108,108,97,98,111,114,97,116,105,111,110,32,119,105,116,104,119, -105,100,101,108,121,32,114,101,103,97,114,100,101,100,32,97,115,104,105,115,32, -99,111,110,116,101,109,112,111,114,97,114,105,101,115,102,111,117,110,100,105, -110,103,32,109,101,109,98,101,114,32,111,102,68,111,109,105,110,105,99,97,110,32 -,82,101,112,117,98,108,105,99,103,101,110,101,114,97,108,108,121,32,97,99,99,101 -,112,116,101,100,116,104,101,32,112,111,115,115,105,98,105,108,105,116,121,32, -111,102,97,114,101,32,97,108,115,111,32,97,118,97,105,108,97,98,108,101,117,110, -100,101,114,32,99,111,110,115,116,114,117,99,116,105,111,110,114,101,115,116,111 -,114,97,116,105,111,110,32,111,102,32,116,104,101,116,104,101,32,103,101,110,101 -,114,97,108,32,112,117,98,108,105,99,105,115,32,97,108,109,111,115,116,32,101, -110,116,105,114,101,108,121,112,97,115,115,101,115,32,116,104,114,111,117,103, -104,32,116,104,101,104,97,115,32,98,101,101,110,32,115,117,103,103,101,115,116, -101,100,99,111,109,112,117,116,101,114,32,97,110,100,32,118,105,100,101,111,71, -101,114,109,97,110,105,99,32,108,97,110,103,117,97,103,101,115,32,97,99,99,111, -114,100,105,110,103,32,116,111,32,116,104,101,32,100,105,102,102,101,114,101,110 -,116,32,102,114,111,109,32,116,104,101,115,104,111,114,116,108,121,32,97,102,116 -,101,114,119,97,114,100,115,104,114,101,102,61,34,104,116,116,112,115,58,47,47, -119,119,119,46,114,101,99,101,110,116,32,100,101,118,101,108,111,112,109,101,110 -,116,66,111,97,114,100,32,111,102,32,68,105,114,101,99,116,111,114,115,60,100, -105,118,32,99,108,97,115,115,61,34,115,101,97,114,99,104,124,32,60,97,32,104,114 -,101,102,61,34,104,116,116,112,58,47,47,73,110,32,112,97,114,116,105,99,117,108, -97,114,44,32,116,104,101,77,117,108,116,105,112,108,101,32,102,111,111,116,110, -111,116,101,115,111,114,32,111,116,104,101,114,32,115,117,98,115,116,97,110,99, -101,116,104,111,117,115,97,110,100,115,32,111,102,32,121,101,97,114,115,116,114, -97,110,115,108,97,116,105,111,110,32,111,102,32,116,104,101,60,47,100,105,118,62 -,13,10,60,47,100,105,118,62,13,10,13,10,60,97,32,104,114,101,102,61,34,105,110, -100,101,120,46,112,104,112,119,97,115,32,101,115,116,97,98,108,105,115,104,101, -100,32,105,110,109,105,110,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10, -112,97,114,116,105,99,105,112,97,116,101,32,105,110,32,116,104,101,97,32,115,116 -,114,111,110,103,32,105,110,102,108,117,101,110,99,101,115,116,121,108,101,61,34 -,109,97,114,103,105,110,45,116,111,112,58,114,101,112,114,101,115,101,110,116, -101,100,32,98,121,32,116,104,101,103,114,97,100,117,97,116,101,100,32,102,114, -111,109,32,116,104,101,84,114,97,100,105,116,105,111,110,97,108,108,121,44,32, -116,104,101,69,108,101,109,101,110,116,40,34,115,99,114,105,112,116,34,41,59,72, -111,119,101,118,101,114,44,32,115,105,110,99,101,32,116,104,101,47,100,105,118, -62,10,60,47,100,105,118,62,10,60,100,105,118,32,108,101,102,116,59,32,109,97,114 -,103,105,110,45,108,101,102,116,58,112,114,111,116,101,99,116,105,111,110,32,97, -103,97,105,110,115,116,48,59,32,118,101,114,116,105,99,97,108,45,97,108,105,103, -110,58,85,110,102,111,114,116,117,110,97,116,101,108,121,44,32,116,104,101,116, -121,112,101,61,34,105,109,97,103,101,47,120,45,105,99,111,110,47,100,105,118,62, -10,60,100,105,118,32,99,108,97,115,115,61,34,32,99,108,97,115,115,61,34,99,108, -101,97,114,102,105,120,34,62,60,100,105,118,32,99,108,97,115,115,61,34,102,111, -111,116,101,114,9,9,60,47,100,105,118,62,10,9,9,60,47,100,105,118,62,10,116,104, -101,32,109,111,116,105,111,110,32,112,105,99,116,117,114,101,208,145,209,138,208 -,187,208,179,208,176,209,128,209,129,208,186,208,184,208,177,209,138,208,187,208 -,179,208,176,209,128,209,129,208,186,208,184,208,164,208,181,208,180,208,181,209 -,128,208,176,209,134,208,184,208,184,208,189,208,181,209,129,208,186,208,190,208 -,187,209,140,208,186,208,190,209,129,208,190,208,190,208,177,209,137,208,181,208 -,189,208,184,208,181,209,129,208,190,208,190,208,177,209,137,208,181,208,189,208 -,184,209,143,208,191,209,128,208,190,208,179,209,128,208,176,208,188,208,188,209 -,139,208,158,209,130,208,191,209,128,208,176,208,178,208,184,209,130,209,140,208 -,177,208,181,209,129,208,191,208,187,208,176,209,130,208,189,208,190,208,188,208 -,176,209,130,208,181,209,128,208,184,208,176,208,187,209,139,208,191,208,190,208 -,183,208,178,208,190,208,187,209,143,208,181,209,130,208,191,208,190,209,129,208 -,187,208,181,208,180,208,189,208,184,208,181,209,128,208,176,208,183,208,187,208 -,184,209,135,208,189,209,139,209,133,208,191,209,128,208,190,208,180,209,131,208 -,186,209,134,208,184,208,184,208,191,209,128,208,190,208,179,209,128,208,176,208 -,188,208,188,208,176,208,191,208,190,208,187,208,189,208,190,209,129,209,130,209 -,140,209,142,208,189,208,176,209,133,208,190,208,180,208,184,209,130,209,129,209 -,143,208,184,208,183,208,177,209,128,208,176,208,189,208,189,208,190,208,181,208 -,189,208,176,209,129,208,181,208,187,208,181,208,189,208,184,209,143,208,184,208 -,183,208,188,208,181,208,189,208,181,208,189,208,184,209,143,208,186,208,176,209 -,130,208,181,208,179,208,190,209,128,208,184,208,184,208,144,208,187,208,181,208 -,186,209,129,208,176,208,189,208,180,209,128,224,164,166,224,165,141,224,164,181 -,224,164,190,224,164,176,224,164,190,224,164,174,224,165,136,224,164,168,224,165 -,129,224,164,133,224,164,178,224,164,170,224,165,141,224,164,176,224,164,166,224 -,164,190,224,164,168,224,164,173,224,164,190,224,164,176,224,164,164,224,165,128 -,224,164,175,224,164,133,224,164,168,224,165,129,224,164,166,224,165,135,224,164 -,182,224,164,185,224,164,191,224,164,168,224,165,141,224,164,166,224,165,128,224 -,164,135,224,164,130,224,164,161,224,164,191,224,164,175,224,164,190,224,164,166 -,224,164,191,224,164,178,224,165,141,224,164,178,224,165,128,224,164,133,224,164 -,167,224,164,191,224,164,149,224,164,190,224,164,176,224,164,181,224,165,128,224 -,164,161,224,164,191,224,164,175,224,165,139,224,164,154,224,164,191,224,164,159 -,224,165,141,224,164,160,224,165,135,224,164,184,224,164,174,224,164,190,224,164 -,154,224,164,190,224,164,176,224,164,156,224,164,130,224,164,149,224,165,141,224 -,164,182,224,164,168,224,164,166,224,165,129,224,164,168,224,164,191,224,164,175 -,224,164,190,224,164,170,224,165,141,224,164,176,224,164,175,224,165,139,224,164 -,151,224,164,133,224,164,168,224,165,129,224,164,184,224,164,190,224,164,176,224 -,164,145,224,164,168,224,164,178,224,164,190,224,164,135,224,164,168,224,164,170 -,224,164,190,224,164,176,224,165,141,224,164,159,224,165,128,224,164,182,224,164 -,176,224,165,141,224,164,164,224,165,139,224,164,130,224,164,178,224,165,139,224 -,164,149,224,164,184,224,164,173,224,164,190,224,164,171,224,164,188,224,165,141 -,224,164,178,224,165,136,224,164,182,224,164,182,224,164,176,224,165,141,224,164 -,164,224,165,135,224,164,130,224,164,170,224,165,141,224,164,176,224,164,166,224 -,165,135,224,164,182,224,164,170,224,165,141,224,164,178,224,165,135,224,164,175 -,224,164,176,224,164,149,224,165,135,224,164,130,224,164,166,224,165,141,224,164 -,176,224,164,184,224,165,141,224,164,165,224,164,191,224,164,164,224,164,191,224 -,164,137,224,164,164,224,165,141,224,164,170,224,164,190,224,164,166,224,164,137 -,224,164,168,224,165,141,224,164,185,224,165,135,224,164,130,224,164,154,224,164 -,191,224,164,159,224,165,141,224,164,160,224,164,190,224,164,175,224,164,190,224 -,164,164,224,165,141,224,164,176,224,164,190,224,164,156,224,165,141,224,164,175 -,224,164,190,224,164,166,224,164,190,224,164,170,224,165,129,224,164,176,224,164 -,190,224,164,168,224,165,135,224,164,156,224,165,139,224,164,161,224,164,188,224 -,165,135,224,164,130,224,164,133,224,164,168,224,165,129,224,164,181,224,164,190 -,224,164,166,224,164,182,224,165,141,224,164,176,224,165,135,224,164,163,224,165 -,128,224,164,182,224,164,191,224,164,149,224,165,141,224,164,183,224,164,190,224 -,164,184,224,164,176,224,164,149,224,164,190,224,164,176,224,165,128,224,164,184 -,224,164,130,224,164,151,224,165,141,224,164,176,224,164,185,224,164,170,224,164 -,176,224,164,191,224,164,163,224,164,190,224,164,174,224,164,172,224,165,141,224 -,164,176,224,164,190,224,164,130,224,164,161,224,164,172,224,164,154,224,165,141 -,224,164,154,224,165,139,224,164,130,224,164,137,224,164,170,224,164,178,224,164 -,172,224,165,141,224,164,167,224,164,174,224,164,130,224,164,164,224,165,141,224 -,164,176,224,165,128,224,164,184,224,164,130,224,164,170,224,164,176,224,165,141 -,224,164,149,224,164,137,224,164,174,224,165,141,224,164,174,224,165,128,224,164 -,166,224,164,174,224,164,190,224,164,167,224,165,141,224,164,175,224,164,174,224 -,164,184,224,164,185,224,164,190,224,164,175,224,164,164,224,164,190,224,164,182 -,224,164,172,224,165,141,224,164,166,224,165,139,224,164,130,224,164,174,224,165 -,128,224,164,161,224,164,191,224,164,175,224,164,190,224,164,134,224,164,136,224 -,164,170,224,165,128,224,164,143,224,164,178,224,164,174,224,165,139,224,164,172 -,224,164,190,224,164,135,224,164,178,224,164,184,224,164,130,224,164,150,224,165 -,141,224,164,175,224,164,190,224,164,134,224,164,170,224,164,176,224,165,135,224 -,164,182,224,164,168,224,164,133,224,164,168,224,165,129,224,164,172,224,164,130 -,224,164,167,224,164,172,224,164,190,224,164,156,224,164,188,224,164,190,224,164 -,176,224,164,168,224,164,181,224,165,128,224,164,168,224,164,164,224,164,174,224 -,164,170,224,165,141,224,164,176,224,164,174,224,165,129,224,164,150,224,164,170 -,224,165,141,224,164,176,224,164,182,224,165,141,224,164,168,224,164,170,224,164 -,176,224,164,191,224,164,181,224,164,190,224,164,176,224,164,168,224,165,129,224 -,164,149,224,164,184,224,164,190,224,164,168,224,164,184,224,164,174,224,164,176 -,224,165,141,224,164,165,224,164,168,224,164,134,224,164,175,224,165,139,224,164 -,156,224,164,191,224,164,164,224,164,184,224,165,139,224,164,174,224,164,181,224 -,164,190,224,164,176,216,167,217,132,217,133,216,180,216,167,216,177,217,131,216 -,167,216,170,216,167,217,132,217,133,217,134,216,170,216,175,217,138,216,167,216 -,170,216,167,217,132,217,131,217,133,216,168,217,138,217,136,216,170,216,177,216 -,167,217,132,217,133,216,180,216,167,217,135,216,175,216,167,216,170,216,185,216 -,175,216,175,216,167,217,132,216,178,217,136,216,167,216,177,216,185,216,175,216 -,175,216,167,217,132,216,177,216,175,217,136,216,175,216,167,217,132,216,165,216 -,179,217,132,216,167,217,133,217,138,216,169,216,167,217,132,217,129,217,136,216 -,170,217,136,216,180,217,136,216,168,216,167,217,132,217,133,216,179,216,167,216 -,168,217,130,216,167,216,170,216,167,217,132,217,133,216,185,217,132,217,136,217 -,133,216,167,216,170,216,167,217,132,217,133,216,179,217,132,216,179,217,132,216 -,167,216,170,216,167,217,132,216,172,216,177,216,167,217,129,217,138,217,131,216 -,179,216,167,217,132,216,167,216,179,217,132,216,167,217,133,217,138,216,169,216 -,167,217,132,216,167,216,170,216,181,216,167,217,132,216,167,216,170,107,101,121 -,119,111,114,100,115,34,32,99,111,110,116,101,110,116,61,34,119,51,46,111,114, -103,47,49,57,57,57,47,120,104,116,109,108,34,62,60,97,32,116,97,114,103,101,116, -61,34,95,98,108,97,110,107,34,32,116,101,120,116,47,104,116,109,108,59,32,99,104 -,97,114,115,101,116,61,34,32,116,97,114,103,101,116,61,34,95,98,108,97,110,107, -34,62,60,116,97,98,108,101,32,99,101,108,108,112,97,100,100,105,110,103,61,34,97 -,117,116,111,99,111,109,112,108,101,116,101,61,34,111,102,102,34,32,116,101,120, -116,45,97,108,105,103,110,58,32,99,101,110,116,101,114,59,116,111,32,108,97,115, -116,32,118,101,114,115,105,111,110,32,98,121,32,98,97,99,107,103,114,111,117,110 -,100,45,99,111,108,111,114,58,32,35,34,32,104,114,101,102,61,34,104,116,116,112, -58,47,47,119,119,119,46,47,100,105,118,62,60,47,100,105,118,62,60,100,105,118,32 -,105,100,61,60,97,32,104,114,101,102,61,34,35,34,32,99,108,97,115,115,61,34,34, -62,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,99,114,105,112, -116,34,32,115,114,99,61,34,104,116,116,112,58,47,47,10,60,115,99,114,105,112,116 -,32,108,97,110,103,117,97,103,101,61,34,47,47,69,78,34,32,34,104,116,116,112,58, -47,47,119,119,119,46,119,101,110,99,111,100,101,85,82,73,67,111,109,112,111,110, -101,110,116,40,34,32,104,114,101,102,61,34,106,97,118,97,115,99,114,105,112,116, -58,60,100,105,118,32,99,108,97,115,115,61,34,99,111,110,116,101,110,116,100,111, -99,117,109,101,110,116,46,119,114,105,116,101,40,39,60,115,99,112,111,115,105, -116,105,111,110,58,32,97,98,115,111,108,117,116,101,59,115,99,114,105,112,116,32 -,115,114,99,61,34,104,116,116,112,58,47,47,32,115,116,121,108,101,61,34,109,97, -114,103,105,110,45,116,111,112,58,46,109,105,110,46,106,115,34,62,60,47,115,99, -114,105,112,116,62,10,60,47,100,105,118,62,10,60,100,105,118,32,99,108,97,115, -115,61,34,119,51,46,111,114,103,47,49,57,57,57,47,120,104,116,109,108,34,32,10, -13,10,60,47,98,111,100,121,62,13,10,60,47,104,116,109,108,62,100,105,115,116,105 -,110,99,116,105,111,110,32,98,101,116,119,101,101,110,47,34,32,116,97,114,103, -101,116,61,34,95,98,108,97,110,107,34,62,60,108,105,110,107,32,104,114,101,102, -61,34,104,116,116,112,58,47,47,101,110,99,111,100,105,110,103,61,34,117,116,102, -45,56,34,63,62,10,119,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110, -101,114,63,97,99,116,105,111,110,61,34,104,116,116,112,58,47,47,119,119,119,46, -105,99,111,110,34,32,104,114,101,102,61,34,104,116,116,112,58,47,47,32,115,116, -121,108,101,61,34,98,97,99,107,103,114,111,117,110,100,58,116,121,112,101,61,34, -116,101,120,116,47,99,115,115,34,32,47,62,10,109,101,116,97,32,112,114,111,112, -101,114,116,121,61,34,111,103,58,116,60,105,110,112,117,116,32,116,121,112,101, -61,34,116,101,120,116,34,32,32,115,116,121,108,101,61,34,116,101,120,116,45,97, -108,105,103,110,58,116,104,101,32,100,101,118,101,108,111,112,109,101,110,116,32 -,111,102,32,116,121,108,101,115,104,101,101,116,34,32,116,121,112,101,61,34,116, -101,104,116,109,108,59,32,99,104,97,114,115,101,116,61,117,116,102,45,56,105,115 -,32,99,111,110,115,105,100,101,114,101,100,32,116,111,32,98,101,116,97,98,108, -101,32,119,105,100,116,104,61,34,49,48,48,37,34,32,73,110,32,97,100,100,105,116, -105,111,110,32,116,111,32,116,104,101,32,99,111,110,116,114,105,98,117,116,101, -100,32,116,111,32,116,104,101,32,100,105,102,102,101,114,101,110,99,101,115,32, -98,101,116,119,101,101,110,100,101,118,101,108,111,112,109,101,110,116,32,111, -102,32,116,104,101,32,73,116,32,105,115,32,105,109,112,111,114,116,97,110,116,32 -,116,111,32,60,47,115,99,114,105,112,116,62,10,10,60,115,99,114,105,112,116,32, -32,115,116,121,108,101,61,34,102,111,110,116,45,115,105,122,101,58,49,62,60,47, -115,112,97,110,62,60,115,112,97,110,32,105,100,61,103,98,76,105,98,114,97,114, -121,32,111,102,32,67,111,110,103,114,101,115,115,60,105,109,103,32,115,114,99,61 -,34,104,116,116,112,58,47,47,105,109,69,110,103,108,105,115,104,32,116,114,97, -110,115,108,97,116,105,111,110,65,99,97,100,101,109,121,32,111,102,32,83,99,105, -101,110,99,101,115,100,105,118,32,115,116,121,108,101,61,34,100,105,115,112,108, -97,121,58,99,111,110,115,116,114,117,99,116,105,111,110,32,111,102,32,116,104, -101,46,103,101,116,69,108,101,109,101,110,116,66,121,73,100,40,105,100,41,105, -110,32,99,111,110,106,117,110,99,116,105,111,110,32,119,105,116,104,69,108,101, -109,101,110,116,40,39,115,99,114,105,112,116,39,41,59,32,60,109,101,116,97,32, -112,114,111,112,101,114,116,121,61,34,111,103,58,208,145,209,138,208,187,208,179 -,208,176,209,128,209,129,208,186,208,184,10,32,116,121,112,101,61,34,116,101,120 -,116,34,32,110,97,109,101,61,34,62,80,114,105,118,97,99,121,32,80,111,108,105,99 -,121,60,47,97,62,97,100,109,105,110,105,115,116,101,114,101,100,32,98,121,32,116 -,104,101,101,110,97,98,108,101,83,105,110,103,108,101,82,101,113,117,101,115,116 -,115,116,121,108,101,61,38,113,117,111,116,59,109,97,114,103,105,110,58,60,47, -100,105,118,62,60,47,100,105,118,62,60,47,100,105,118,62,60,62,60,105,109,103,32 -,115,114,99,61,34,104,116,116,112,58,47,47,105,32,115,116,121,108,101,61,38,113, -117,111,116,59,102,108,111,97,116,58,114,101,102,101,114,114,101,100,32,116,111, -32,97,115,32,116,104,101,32,116,111,116,97,108,32,112,111,112,117,108,97,116,105 -,111,110,32,111,102,105,110,32,87,97,115,104,105,110,103,116,111,110,44,32,68,46 -,67,46,32,115,116,121,108,101,61,34,98,97,99,107,103,114,111,117,110,100,45,97, -109,111,110,103,32,111,116,104,101,114,32,116,104,105,110,103,115,44,111,114,103 -,97,110,105,122,97,116,105,111,110,32,111,102,32,116,104,101,112,97,114,116,105, -99,105,112,97,116,101,100,32,105,110,32,116,104,101,116,104,101,32,105,110,116, -114,111,100,117,99,116,105,111,110,32,111,102,105,100,101,110,116,105,102,105, -101,100,32,119,105,116,104,32,116,104,101,102,105,99,116,105,111,110,97,108,32, -99,104,97,114,97,99,116,101,114,32,79,120,102,111,114,100,32,85,110,105,118,101, -114,115,105,116,121,32,109,105,115,117,110,100,101,114,115,116,97,110,100,105, -110,103,32,111,102,84,104,101,114,101,32,97,114,101,44,32,104,111,119,101,118, -101,114,44,115,116,121,108,101,115,104,101,101,116,34,32,104,114,101,102,61,34, -47,67,111,108,117,109,98,105,97,32,85,110,105,118,101,114,115,105,116,121,101, -120,112,97,110,100,101,100,32,116,111,32,105,110,99,108,117,100,101,117,115,117, -97,108,108,121,32,114,101,102,101,114,114,101,100,32,116,111,105,110,100,105,99, -97,116,105,110,103,32,116,104,97,116,32,116,104,101,104,97,118,101,32,115,117, -103,103,101,115,116,101,100,32,116,104,97,116,97,102,102,105,108,105,97,116,101, -100,32,119,105,116,104,32,116,104,101,99,111,114,114,101,108,97,116,105,111,110, -32,98,101,116,119,101,101,110,110,117,109,98,101,114,32,111,102,32,100,105,102, -102,101,114,101,110,116,62,60,47,116,100,62,60,47,116,114,62,60,47,116,97,98,108 -,101,62,82,101,112,117,98,108,105,99,32,111,102,32,73,114,101,108,97,110,100,10, -60,47,115,99,114,105,112,116,62,10,60,115,99,114,105,112,116,32,117,110,100,101, -114,32,116,104,101,32,105,110,102,108,117,101,110,99,101,99,111,110,116,114,105, -98,117,116,105,111,110,32,116,111,32,116,104,101,79,102,102,105,99,105,97,108,32 -,119,101,98,115,105,116,101,32,111,102,104,101,97,100,113,117,97,114,116,101,114 -,115,32,111,102,32,116,104,101,99,101,110,116,101,114,101,100,32,97,114,111,117, -110,100,32,116,104,101,105,109,112,108,105,99,97,116,105,111,110,115,32,111,102, -32,116,104,101,104,97,118,101,32,98,101,101,110,32,100,101,118,101,108,111,112, -101,100,70,101,100,101,114,97,108,32,82,101,112,117,98,108,105,99,32,111,102,98, -101,99,97,109,101,32,105,110,99,114,101,97,115,105,110,103,108,121,99,111,110, -116,105,110,117,97,116,105,111,110,32,111,102,32,116,104,101,78,111,116,101,44, -32,104,111,119,101,118,101,114,44,32,116,104,97,116,115,105,109,105,108,97,114, -32,116,111,32,116,104,97,116,32,111,102,32,99,97,112,97,98,105,108,105,116,105, -101,115,32,111,102,32,116,104,101,97,99,99,111,114,100,97,110,99,101,32,119,105, -116,104,32,116,104,101,112,97,114,116,105,99,105,112,97,110,116,115,32,105,110, -32,116,104,101,102,117,114,116,104,101,114,32,100,101,118,101,108,111,112,109, -101,110,116,117,110,100,101,114,32,116,104,101,32,100,105,114,101,99,116,105,111 -,110,105,115,32,111,102,116,101,110,32,99,111,110,115,105,100,101,114,101,100, -104,105,115,32,121,111,117,110,103,101,114,32,98,114,111,116,104,101,114,60,47, -116,100,62,60,47,116,114,62,60,47,116,97,98,108,101,62,60,97,32,104,116,116,112, -45,101,113,117,105,118,61,34,88,45,85,65,45,112,104,121,115,105,99,97,108,32,112 -,114,111,112,101,114,116,105,101,115,111,102,32,66,114,105,116,105,115,104,32,67 -,111,108,117,109,98,105,97,104,97,115,32,98,101,101,110,32,99,114,105,116,105,99 -,105,122,101,100,40,119,105,116,104,32,116,104,101,32,101,120,99,101,112,116,105 -,111,110,113,117,101,115,116,105,111,110,115,32,97,98,111,117,116,32,116,104,101 -,112,97,115,115,105,110,103,32,116,104,114,111,117,103,104,32,116,104,101,48,34, -32,99,101,108,108,112,97,100,100,105,110,103,61,34,48,34,32,116,104,111,117,115, -97,110,100,115,32,111,102,32,112,101,111,112,108,101,114,101,100,105,114,101,99, -116,115,32,104,101,114,101,46,32,70,111,114,104,97,118,101,32,99,104,105,108,100 -,114,101,110,32,117,110,100,101,114,37,51,69,37,51,67,47,115,99,114,105,112,116, -37,51,69,34,41,41,59,60,97,32,104,114,101,102,61,34,104,116,116,112,58,47,47,119 -,119,119,46,60,108,105,62,60,97,32,104,114,101,102,61,34,104,116,116,112,58,47, -47,115,105,116,101,95,110,97,109,101,34,32,99,111,110,116,101,110,116,61,34,116, -101,120,116,45,100,101,99,111,114,97,116,105,111,110,58,110,111,110,101,115,116, -121,108,101,61,34,100,105,115,112,108,97,121,58,32,110,111,110,101,60,109,101, -116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,88,45,110,101,119,32,68, -97,116,101,40,41,46,103,101,116,84,105,109,101,40,41,32,116,121,112,101,61,34, -105,109,97,103,101,47,120,45,105,99,111,110,34,60,47,115,112,97,110,62,60,115, -112,97,110,32,99,108,97,115,115,61,34,108,97,110,103,117,97,103,101,61,34,106,97 -,118,97,115,99,114,105,112,116,119,105,110,100,111,119,46,108,111,99,97,116,105, -111,110,46,104,114,101,102,60,97,32,104,114,101,102,61,34,106,97,118,97,115,99, -114,105,112,116,58,45,45,62,13,10,60,115,99,114,105,112,116,32,116,121,112,101, -61,34,116,60,97,32,104,114,101,102,61,39,104,116,116,112,58,47,47,119,119,119,46 -,104,111,114,116,99,117,116,32,105,99,111,110,34,32,104,114,101,102,61,34,60,47, -100,105,118,62,13,10,60,100,105,118,32,99,108,97,115,115,61,34,60,115,99,114,105 -,112,116,32,115,114,99,61,34,104,116,116,112,58,47,47,34,32,114,101,108,61,34, -115,116,121,108,101,115,104,101,101,116,34,32,116,60,47,100,105,118,62,10,60,115 -,99,114,105,112,116,32,116,121,112,101,61,47,97,62,32,60,97,32,104,114,101,102, -61,34,104,116,116,112,58,47,47,32,97,108,108,111,119,84,114,97,110,115,112,97, -114,101,110,99,121,61,34,88,45,85,65,45,67,111,109,112,97,116,105,98,108,101,34, -32,99,111,110,114,101,108,97,116,105,111,110,115,104,105,112,32,98,101,116,119, -101,101,110,10,60,47,115,99,114,105,112,116,62,13,10,60,115,99,114,105,112,116, -32,60,47,97,62,60,47,108,105,62,60,47,117,108,62,60,47,100,105,118,62,97,115,115 -,111,99,105,97,116,101,100,32,119,105,116,104,32,116,104,101,32,112,114,111,103, -114,97,109,109,105,110,103,32,108,97,110,103,117,97,103,101,60,47,97,62,60,97,32 -,104,114,101,102,61,34,104,116,116,112,58,47,47,60,47,97,62,60,47,108,105,62,60, -108,105,32,99,108,97,115,115,61,34,102,111,114,109,32,97,99,116,105,111,110,61, -34,104,116,116,112,58,47,47,60,100,105,118,32,115,116,121,108,101,61,34,100,105, -115,112,108,97,121,58,116,121,112,101,61,34,116,101,120,116,34,32,110,97,109,101 -,61,34,113,34,60,116,97,98,108,101,32,119,105,100,116,104,61,34,49,48,48,37,34, -32,98,97,99,107,103,114,111,117,110,100,45,112,111,115,105,116,105,111,110,58,34 -,32,98,111,114,100,101,114,61,34,48,34,32,119,105,100,116,104,61,34,114,101,108, -61,34,115,104,111,114,116,99,117,116,32,105,99,111,110,34,32,104,54,62,60,117, -108,62,60,108,105,62,60,97,32,104,114,101,102,61,34,32,32,60,109,101,116,97,32, -104,116,116,112,45,101,113,117,105,118,61,34,99,115,115,34,32,109,101,100,105,97 -,61,34,115,99,114,101,101,110,34,32,114,101,115,112,111,110,115,105,98,108,101, -32,102,111,114,32,116,104,101,32,34,32,116,121,112,101,61,34,97,112,112,108,105, -99,97,116,105,111,110,47,34,32,115,116,121,108,101,61,34,98,97,99,107,103,114, -111,117,110,100,45,104,116,109,108,59,32,99,104,97,114,115,101,116,61,117,116, -102,45,56,34,32,97,108,108,111,119,116,114,97,110,115,112,97,114,101,110,99,121, -61,34,115,116,121,108,101,115,104,101,101,116,34,32,116,121,112,101,61,34,116, -101,13,10,60,109,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,62, -60,47,115,112,97,110,62,60,115,112,97,110,32,99,108,97,115,115,61,34,48,34,32,99 -,101,108,108,115,112,97,99,105,110,103,61,34,48,34,62,59,10,60,47,115,99,114,105 -,112,116,62,10,60,115,99,114,105,112,116,32,115,111,109,101,116,105,109,101,115, -32,99,97,108,108,101,100,32,116,104,101,100,111,101,115,32,110,111,116,32,110, -101,99,101,115,115,97,114,105,108,121,70,111,114,32,109,111,114,101,32,105,110, -102,111,114,109,97,116,105,111,110,97,116,32,116,104,101,32,98,101,103,105,110, -110,105,110,103,32,111,102,32,60,33,68,79,67,84,89,80,69,32,104,116,109,108,62, -60,104,116,109,108,112,97,114,116,105,99,117,108,97,114,108,121,32,105,110,32, -116,104,101,32,116,121,112,101,61,34,104,105,100,100,101,110,34,32,110,97,109, -101,61,34,106,97,118,97,115,99,114,105,112,116,58,118,111,105,100,40,48,41,59,34 -,101,102,102,101,99,116,105,118,101,110,101,115,115,32,111,102,32,116,104,101,32 -,97,117,116,111,99,111,109,112,108,101,116,101,61,34,111,102,102,34,32,103,101, -110,101,114,97,108,108,121,32,99,111,110,115,105,100,101,114,101,100,62,60,105, -110,112,117,116,32,116,121,112,101,61,34,116,101,120,116,34,32,34,62,60,47,115, -99,114,105,112,116,62,13,10,60,115,99,114,105,112,116,116,104,114,111,117,103, -104,111,117,116,32,116,104,101,32,119,111,114,108,100,99,111,109,109,111,110,32, -109,105,115,99,111,110,99,101,112,116,105,111,110,97,115,115,111,99,105,97,116, -105,111,110,32,119,105,116,104,32,116,104,101,60,47,100,105,118,62,10,60,47,100, -105,118,62,10,60,100,105,118,32,99,100,117,114,105,110,103,32,104,105,115,32,108 -,105,102,101,116,105,109,101,44,99,111,114,114,101,115,112,111,110,100,105,110, -103,32,116,111,32,116,104,101,116,121,112,101,61,34,105,109,97,103,101,47,120,45 -,105,99,111,110,34,32,97,110,32,105,110,99,114,101,97,115,105,110,103,32,110,117 -,109,98,101,114,100,105,112,108,111,109,97,116,105,99,32,114,101,108,97,116,105, -111,110,115,97,114,101,32,111,102,116,101,110,32,99,111,110,115,105,100,101,114, -101,100,109,101,116,97,32,99,104,97,114,115,101,116,61,34,117,116,102,45,56,34, -32,60,105,110,112,117,116,32,116,121,112,101,61,34,116,101,120,116,34,32,101,120 -,97,109,112,108,101,115,32,105,110,99,108,117,100,101,32,116,104,101,34,62,60, -105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,105,112,97,114,116,105, -99,105,112,97,116,105,111,110,32,105,110,32,116,104,101,116,104,101,32,101,115, -116,97,98,108,105,115,104,109,101,110,116,32,111,102,10,60,47,100,105,118,62,10, -60,100,105,118,32,99,108,97,115,115,61,34,38,97,109,112,59,110,98,115,112,59,38, -97,109,112,59,110,98,115,112,59,116,111,32,100,101,116,101,114,109,105,110,101, -32,119,104,101,116,104,101,114,113,117,105,116,101,32,100,105,102,102,101,114, -101,110,116,32,102,114,111,109,109,97,114,107,101,100,32,116,104,101,32,98,101, -103,105,110,110,105,110,103,100,105,115,116,97,110,99,101,32,98,101,116,119,101, -101,110,32,116,104,101,99,111,110,116,114,105,98,117,116,105,111,110,115,32,116, -111,32,116,104,101,99,111,110,102,108,105,99,116,32,98,101,116,119,101,101,110, -32,116,104,101,119,105,100,101,108,121,32,99,111,110,115,105,100,101,114,101,100 -,32,116,111,119,97,115,32,111,110,101,32,111,102,32,116,104,101,32,102,105,114, -115,116,119,105,116,104,32,118,97,114,121,105,110,103,32,100,101,103,114,101,101 -,115,104,97,118,101,32,115,112,101,99,117,108,97,116,101,100,32,116,104,97,116, -40,100,111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,112, -97,114,116,105,99,105,112,97,116,105,110,103,32,105,110,32,116,104,101,111,114, -105,103,105,110,97,108,108,121,32,100,101,118,101,108,111,112,101,100,101,116,97 -,32,99,104,97,114,115,101,116,61,34,117,116,102,45,56,34,62,32,116,121,112,101, -61,34,116,101,120,116,47,99,115,115,34,32,47,62,10,105,110,116,101,114,99,104,97 -,110,103,101,97,98,108,121,32,119,105,116,104,109,111,114,101,32,99,108,111,115, -101,108,121,32,114,101,108,97,116,101,100,115,111,99,105,97,108,32,97,110,100,32 -,112,111,108,105,116,105,99,97,108,116,104,97,116,32,119,111,117,108,100,32,111, -116,104,101,114,119,105,115,101,112,101,114,112,101,110,100,105,99,117,108,97, -114,32,116,111,32,116,104,101,115,116,121,108,101,32,116,121,112,101,61,34,116, -101,120,116,47,99,115,115,116,121,112,101,61,34,115,117,98,109,105,116,34,32,110 -,97,109,101,61,34,102,97,109,105,108,105,101,115,32,114,101,115,105,100,105,110, -103,32,105,110,100,101,118,101,108,111,112,105,110,103,32,99,111,117,110,116,114 -,105,101,115,99,111,109,112,117,116,101,114,32,112,114,111,103,114,97,109,109, -105,110,103,101,99,111,110,111,109,105,99,32,100,101,118,101,108,111,112,109,101 -,110,116,100,101,116,101,114,109,105,110,97,116,105,111,110,32,111,102,32,116, -104,101,102,111,114,32,109,111,114,101,32,105,110,102,111,114,109,97,116,105,111 -,110,111,110,32,115,101,118,101,114,97,108,32,111,99,99,97,115,105,111,110,115, -112,111,114,116,117,103,117,195,170,115,32,40,69,117,114,111,112,101,117,41,208, -163,208,186,209,128,208,176,209,151,208,189,209,129,209,140,208,186,208,176,209, -131,208,186,209,128,208,176,209,151,208,189,209,129,209,140,208,186,208,176,208, -160,208,190,209,129,209,129,208,184,208,185,209,129,208,186,208,190,208,185,208, -188,208,176,209,130,208,181,209,128,208,184,208,176,208,187,208,190,208,178,208, -184,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,208,184,209, -131,208,191,209,128,208,176,208,178,208,187,208,181,208,189,208,184,209,143,208, -189,208,181,208,190,208,177,209,133,208,190,208,180,208,184,208,188,208,190,208, -184,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,209,143,208, -152,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,209,143,208, -160,208,181,209,129,208,191,209,131,208,177,208,187,208,184,208,186,208,184,208, -186,208,190,208,187,208,184,209,135,208,181,209,129,209,130,208,178,208,190,208, -184,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,209,142,209, -130,208,181,209,128,209,128,208,184,209,130,208,190,209,128,208,184,208,184,208, -180,208,190,209,129,209,130,208,176,209,130,208,190,209,135,208,189,208,190,216, -167,217,132,217,133,216,170,217,136,216,167,216,172,216,175,217,136,217,134,216, -167,217,132,216,167,216,180,216,170,216,177,216,167,217,131,216,167,216,170,216, -167,217,132,216,167,217,130,216,170,216,177,216,167,216,173,216,167,216,170,104, -116,109,108,59,32,99,104,97,114,115,101,116,61,85,84,70,45,56,34,32,115,101,116, -84,105,109,101,111,117,116,40,102,117,110,99,116,105,111,110,40,41,100,105,115, -112,108,97,121,58,105,110,108,105,110,101,45,98,108,111,99,107,59,60,105,110,112 -,117,116,32,116,121,112,101,61,34,115,117,98,109,105,116,34,32,116,121,112,101, -32,61,32,39,116,101,120,116,47,106,97,118,97,115,99,114,105,60,105,109,103,32, -115,114,99,61,34,104,116,116,112,58,47,47,119,119,119,46,34,32,34,104,116,116, -112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,115,104,111,114,116,99,117, -116,32,105,99,111,110,34,32,104,114,101,102,61,34,34,32,97,117,116,111,99,111, -109,112,108,101,116,101,61,34,111,102,102,34,32,60,47,97,62,60,47,100,105,118,62 -,60,100,105,118,32,99,108,97,115,115,61,60,47,97,62,60,47,108,105,62,10,60,108, -105,32,99,108,97,115,115,61,34,99,115,115,34,32,116,121,112,101,61,34,116,101, -120,116,47,99,115,115,34,32,60,102,111,114,109,32,97,99,116,105,111,110,61,34, -104,116,116,112,58,47,47,120,116,47,99,115,115,34,32,104,114,101,102,61,34,104, -116,116,112,58,47,47,108,105,110,107,32,114,101,108,61,34,97,108,116,101,114,110 -,97,116,101,34,32,13,10,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116, -101,120,116,47,32,111,110,99,108,105,99,107,61,34,106,97,118,97,115,99,114,105, -112,116,58,40,110,101,119,32,68,97,116,101,41,46,103,101,116,84,105,109,101,40, -41,125,104,101,105,103,104,116,61,34,49,34,32,119,105,100,116,104,61,34,49,34,32 -,80,101,111,112,108,101,39,115,32,82,101,112,117,98,108,105,99,32,111,102,32,32, -60,97,32,104,114,101,102,61,34,104,116,116,112,58,47,47,119,119,119,46,116,101, -120,116,45,100,101,99,111,114,97,116,105,111,110,58,117,110,100,101,114,116,104, -101,32,98,101,103,105,110,110,105,110,103,32,111,102,32,116,104,101,32,60,47,100 -,105,118,62,10,60,47,100,105,118,62,10,60,47,100,105,118,62,10,101,115,116,97,98 -,108,105,115,104,109,101,110,116,32,111,102,32,116,104,101,32,60,47,100,105,118, -62,60,47,100,105,118,62,60,47,100,105,118,62,60,47,100,35,118,105,101,119,112, -111,114,116,123,109,105,110,45,104,101,105,103,104,116,58,10,60,115,99,114,105, -112,116,32,115,114,99,61,34,104,116,116,112,58,47,47,111,112,116,105,111,110,62, -60,111,112,116,105,111,110,32,118,97,108,117,101,61,111,102,116,101,110,32,114, -101,102,101,114,114,101,100,32,116,111,32,97,115,32,47,111,112,116,105,111,110, -62,10,60,111,112,116,105,111,110,32,118,97,108,117,60,33,68,79,67,84,89,80,69,32 -,104,116,109,108,62,10,60,33,45,45,91,73,110,116,101,114,110,97,116,105,111,110, -97,108,32,65,105,114,112,111,114,116,62,10,60,97,32,104,114,101,102,61,34,104, -116,116,112,58,47,47,119,119,119,60,47,97,62,60,97,32,104,114,101,102,61,34,104, -116,116,112,58,47,47,119,224,184,160,224,184,178,224,184,169,224,184,178,224,185 -,132,224,184,151,224,184,162,225,131,165,225,131,144,225,131,160,225,131,151,225 -,131,163,225,131,154,225,131,152,230,173,163,233,171,148,228,184,173,230,150,135 -,32,40,231,185,129,233,171,148,41,224,164,168,224,164,191,224,164,176,224,165, -141,224,164,166,224,165,135,224,164,182,224,164,161,224,164,190,224,164,137,224, -164,168,224,164,178,224,165,139,224,164,161,224,164,149,224,165,141,224,164,183, -224,165,135,224,164,164,224,165,141,224,164,176,224,164,156,224,164,190,224,164, -168,224,164,149,224,164,190,224,164,176,224,165,128,224,164,184,224,164,130,224, -164,172,224,164,130,224,164,167,224,164,191,224,164,164,224,164,184,224,165,141, -224,164,165,224,164,190,224,164,170,224,164,168,224,164,190,224,164,184,224,165, -141,224,164,181,224,165,128,224,164,149,224,164,190,224,164,176,224,164,184,224, -164,130,224,164,184,224,165,141,224,164,149,224,164,176,224,164,163,224,164,184, -224,164,190,224,164,174,224,164,151,224,165,141,224,164,176,224,165,128,224,164, -154,224,164,191,224,164,159,224,165,141,224,164,160,224,165,139,224,164,130,224, -164,181,224,164,191,224,164,156,224,165,141,224,164,158,224,164,190,224,164,168, -224,164,133,224,164,174,224,165,135,224,164,176,224,164,191,224,164,149,224,164, -190,224,164,181,224,164,191,224,164,173,224,164,191,224,164,168,224,165,141,224, -164,168,224,164,151,224,164,190,224,164,161,224,164,191,224,164,175,224,164,190, -224,164,129,224,164,149,224,165,141,224,164,175,224,165,139,224,164,130,224,164, -149,224,164,191,224,164,184,224,165,129,224,164,176,224,164,149,224,165,141,224, -164,183,224,164,190,224,164,170,224,164,185,224,165,129,224,164,129,224,164,154, -224,164,164,224,165,128,224,164,170,224,165,141,224,164,176,224,164,172,224,164, -130,224,164,167,224,164,168,224,164,159,224,164,191,224,164,170,224,165,141,224, -164,170,224,164,163,224,165,128,224,164,149,224,165,141,224,164,176,224,164,191, -224,164,149,224,165,135,224,164,159,224,164,170,224,165,141,224,164,176,224,164, -190,224,164,176,224,164,130,224,164,173,224,164,170,224,165,141,224,164,176,224, -164,190,224,164,170,224,165,141,224,164,164,224,164,174,224,164,190,224,164,178, -224,164,191,224,164,149,224,165,139,224,164,130,224,164,176,224,164,171,224,164, -188,224,165,141,224,164,164,224,164,190,224,164,176,224,164,168,224,164,191,224, -164,176,224,165,141,224,164,174,224,164,190,224,164,163,224,164,178,224,164,191, -224,164,174,224,164,191,224,164,159,224,165,135,224,164,161,100,101,115,99,114, -105,112,116,105,111,110,34,32,99,111,110,116,101,110,116,61,34,100,111,99,117, -109,101,110,116,46,108,111,99,97,116,105,111,110,46,112,114,111,116,46,103,101, -116,69,108,101,109,101,110,116,115,66,121,84,97,103,78,97,109,101,40,60,33,68,79 -,67,84,89,80,69,32,104,116,109,108,62,10,60,104,116,109,108,32,60,109,101,116,97 -,32,99,104,97,114,115,101,116,61,34,117,116,102,45,56,34,62,58,117,114,108,34,32 -,99,111,110,116,101,110,116,61,34,104,116,116,112,58,47,47,46,99,115,115,34,32, -114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,115,116,121,108,101 -,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,62,116,121,112,101,61 -,34,116,101,120,116,47,99,115,115,34,32,104,114,101,102,61,34,119,51,46,111,114, -103,47,49,57,57,57,47,120,104,116,109,108,34,32,120,109,108,116,121,112,101,61, -34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,32,109,101,116,104 -,111,100,61,34,103,101,116,34,32,97,99,116,105,111,110,61,34,108,105,110,107,32, -114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,32,32,61,32,100,111 -,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,116,121,112, -101,61,34,105,109,97,103,101,47,120,45,105,99,111,110,34,32,47,62,99,101,108,108 -,112,97,100,100,105,110,103,61,34,48,34,32,99,101,108,108,115,112,46,99,115,115, -34,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,32,60,47,97,62,60, -47,108,105,62,60,108,105,62,60,97,32,104,114,101,102,61,34,34,32,119,105,100,116 -,104,61,34,49,34,32,104,101,105,103,104,116,61,34,49,34,34,62,60,97,32,104,114, -101,102,61,34,104,116,116,112,58,47,47,119,119,119,46,115,116,121,108,101,61,34, -100,105,115,112,108,97,121,58,110,111,110,101,59,34,62,97,108,116,101,114,110,97 -,116,101,34,32,116,121,112,101,61,34,97,112,112,108,105,45,47,47,87,51,67,47,47, -68,84,68,32,88,72,84,77,76,32,49,46,48,32,101,108,108,115,112,97,99,105,110,103, -61,34,48,34,32,99,101,108,108,112,97,100,32,116,121,112,101,61,34,104,105,100, -100,101,110,34,32,118,97,108,117,101,61,34,47,97,62,38,110,98,115,112,59,60,115, -112,97,110,32,114,111,108,101,61,34,115,10,60,105,110,112,117,116,32,116,121,112 -,101,61,34,104,105,100,100,101,110,34,32,108,97,110,103,117,97,103,101,61,34,74, -97,118,97,83,99,114,105,112,116,34,32,32,100,111,99,117,109,101,110,116,46,103, -101,116,69,108,101,109,101,110,116,115,66,103,61,34,48,34,32,99,101,108,108,115, -112,97,99,105,110,103,61,34,48,34,32,121,112,101,61,34,116,101,120,116,47,99,115 -,115,34,32,109,101,100,105,97,61,34,116,121,112,101,61,39,116,101,120,116,47,106 -,97,118,97,115,99,114,105,112,116,39,119,105,116,104,32,116,104,101,32,101,120, -99,101,112,116,105,111,110,32,111,102,32,121,112,101,61,34,116,101,120,116,47,99 -,115,115,34,32,114,101,108,61,34,115,116,32,104,101,105,103,104,116,61,34,49,34, -32,119,105,100,116,104,61,34,49,34,32,61,39,43,101,110,99,111,100,101,85,82,73, -67,111,109,112,111,110,101,110,116,40,60,108,105,110,107,32,114,101,108,61,34,97 -,108,116,101,114,110,97,116,101,34,32,10,98,111,100,121,44,32,116,114,44,32,105, -110,112,117,116,44,32,116,101,120,116,109,101,116,97,32,110,97,109,101,61,34,114 -,111,98,111,116,115,34,32,99,111,110,109,101,116,104,111,100,61,34,112,111,115, -116,34,32,97,99,116,105,111,110,61,34,62,10,60,97,32,104,114,101,102,61,34,104, -116,116,112,58,47,47,119,119,119,46,99,115,115,34,32,114,101,108,61,34,115,116, -121,108,101,115,104,101,101,116,34,32,60,47,100,105,118,62,60,47,100,105,118,62, -60,100,105,118,32,99,108,97,115,115,108,97,110,103,117,97,103,101,61,34,106,97, -118,97,115,99,114,105,112,116,34,62,97,114,105,97,45,104,105,100,100,101,110,61, -34,116,114,117,101,34,62,194,183,60,114,105,112,116,34,32,116,121,112,101,61,34, -116,101,120,116,47,106,97,118,97,115,108,61,48,59,125,41,40,41,59,10,40,102,117, -110,99,116,105,111,110,40,41,123,98,97,99,107,103,114,111,117,110,100,45,105,109 -,97,103,101,58,32,117,114,108,40,47,97,62,60,47,108,105,62,60,108,105,62,60,97, -32,104,114,101,102,61,34,104,9,9,60,108,105,62,60,97,32,104,114,101,102,61,34, -104,116,116,112,58,47,47,97,116,111,114,34,32,97,114,105,97,45,104,105,100,100, -101,110,61,34,116,114,117,62,32,60,97,32,104,114,101,102,61,34,104,116,116,112, -58,47,47,119,119,119,46,108,97,110,103,117,97,103,101,61,34,106,97,118,97,115,99 -,114,105,112,116,34,32,47,111,112,116,105,111,110,62,10,60,111,112,116,105,111, -110,32,118,97,108,117,101,47,100,105,118,62,60,47,100,105,118,62,60,100,105,118, -32,99,108,97,115,115,61,114,97,116,111,114,34,32,97,114,105,97,45,104,105,100, -100,101,110,61,34,116,114,101,61,40,110,101,119,32,68,97,116,101,41,46,103,101, -116,84,105,109,101,40,41,112,111,114,116,117,103,117,195,170,115,32,40,100,111, -32,66,114,97,115,105,108,41,208,190,209,128,208,179,208,176,208,189,208,184,208, -183,208,176,209,134,208,184,208,184,208,178,208,190,208,183,208,188,208,190,208, -182,208,189,208,190,209,129,209,130,209,140,208,190,208,177,209,128,208,176,208, -183,208,190,208,178,208,176,208,189,208,184,209,143,209,128,208,181,208,179,208, -184,209,129,209,130,209,128,208,176,209,134,208,184,208,184,208,178,208,190,208, -183,208,188,208,190,208,182,208,189,208,190,209,129,209,130,208,184,208,190,208, -177,209,143,208,183,208,176,209,130,208,181,208,187,209,140,208,189,208,176,60, -33,68,79,67,84,89,80,69,32,104,116,109,108,32,80,85,66,76,73,67,32,34,110,116,45 -,84,121,112,101,34,32,99,111,110,116,101,110,116,61,34,116,101,120,116,47,60,109 -,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,67,111,110,116,101, -114,97,110,115,105,116,105,111,110,97,108,47,47,69,78,34,32,34,104,116,116,112, -58,60,104,116,109,108,32,120,109,108,110,115,61,34,104,116,116,112,58,47,47,119, -119,119,45,47,47,87,51,67,47,47,68,84,68,32,88,72,84,77,76,32,49,46,48,32,84,68, -84,68,47,120,104,116,109,108,49,45,116,114,97,110,115,105,116,105,111,110,97,108 -,47,47,119,119,119,46,119,51,46,111,114,103,47,84,82,47,120,104,116,109,108,49, -47,112,101,32,61,32,39,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116, -39,59,60,109,101,116,97,32,110,97,109,101,61,34,100,101,115,99,114,105,112,116, -105,111,110,112,97,114,101,110,116,78,111,100,101,46,105,110,115,101,114,116,66, -101,102,111,114,101,60,105,110,112,117,116,32,116,121,112,101,61,34,104,105,100, -100,101,110,34,32,110,97,106,115,34,32,116,121,112,101,61,34,116,101,120,116,47, -106,97,118,97,115,99,114,105,40,100,111,99,117,109,101,110,116,41,46,114,101,97, -100,121,40,102,117,110,99,116,105,115,99,114,105,112,116,32,116,121,112,101,61, -34,116,101,120,116,47,106,97,118,97,115,105,109,97,103,101,34,32,99,111,110,116, -101,110,116,61,34,104,116,116,112,58,47,47,85,65,45,67,111,109,112,97,116,105,98 -,108,101,34,32,99,111,110,116,101,110,116,61,116,109,108,59,32,99,104,97,114,115 -,101,116,61,117,116,102,45,56,34,32,47,62,10,108,105,110,107,32,114,101,108,61, -34,115,104,111,114,116,99,117,116,32,105,99,111,110,60,108,105,110,107,32,114, -101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,32,60,47,115,99,114,105 -,112,116,62,10,60,115,99,114,105,112,116,32,116,121,112,101,61,61,32,100,111,99, -117,109,101,110,116,46,99,114,101,97,116,101,69,108,101,109,101,110,60,97,32,116 -,97,114,103,101,116,61,34,95,98,108,97,110,107,34,32,104,114,101,102,61,32,100, -111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,115,66,105, -110,112,117,116,32,116,121,112,101,61,34,116,101,120,116,34,32,110,97,109,101,61 -,97,46,116,121,112,101,32,61,32,39,116,101,120,116,47,106,97,118,97,115,99,114, -105,110,112,117,116,32,116,121,112,101,61,34,104,105,100,100,101,110,34,32,110, -97,109,101,104,116,109,108,59,32,99,104,97,114,115,101,116,61,117,116,102,45,56, -34,32,47,62,100,116,100,34,62,10,60,104,116,109,108,32,120,109,108,110,115,61,34 -,104,116,116,112,45,47,47,87,51,67,47,47,68,84,68,32,72,84,77,76,32,52,46,48,49, -32,84,101,110,116,115,66,121,84,97,103,78,97,109,101,40,39,115,99,114,105,112, -116,39,41,105,110,112,117,116,32,116,121,112,101,61,34,104,105,100,100,101,110, -34,32,110,97,109,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120, -116,47,106,97,118,97,115,34,32,115,116,121,108,101,61,34,100,105,115,112,108,97, -121,58,110,111,110,101,59,34,62,100,111,99,117,109,101,110,116,46,103,101,116,69 -,108,101,109,101,110,116,66,121,73,100,40,61,100,111,99,117,109,101,110,116,46, -99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,32,116,121,112,101,61,39, -116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,39,105,110,112,117,116, -32,116,121,112,101,61,34,116,101,120,116,34,32,110,97,109,101,61,34,100,46,103, -101,116,69,108,101,109,101,110,116,115,66,121,84,97,103,78,97,109,101,40,115,110 -,105,99,97,108,34,32,104,114,101,102,61,34,104,116,116,112,58,47,47,119,119,119, -46,67,47,47,68,84,68,32,72,84,77,76,32,52,46,48,49,32,84,114,97,110,115,105,116, -60,115,116,121,108,101,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34 -,62,10,10,60,115,116,121,108,101,32,116,121,112,101,61,34,116,101,120,116,47,99, -115,115,34,62,105,111,110,97,108,46,100,116,100,34,62,10,60,104,116,109,108,32, -120,109,108,110,115,61,104,116,116,112,45,101,113,117,105,118,61,34,67,111,110, -116,101,110,116,45,84,121,112,101,100,105,110,103,61,34,48,34,32,99,101,108,108, -115,112,97,99,105,110,103,61,34,48,34,104,116,109,108,59,32,99,104,97,114,115, -101,116,61,117,116,102,45,56,34,32,47,62,10,32,115,116,121,108,101,61,34,100,105 -,115,112,108,97,121,58,110,111,110,101,59,34,62,60,60,108,105,62,60,97,32,104, -114,101,102,61,34,104,116,116,112,58,47,47,119,119,119,46,32,116,121,112,101,61, -39,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,39,62,208,180,208,181 -,209,143,209,130,208,181,208,187,209,140,208,189,208,190,209,129,209,130,208,184 -,209,129,208,190,208,190,209,130,208,178,208,181,209,130,209,129,209,130,208,178 -,208,184,208,184,208,191,209,128,208,190,208,184,208,183,208,178,208,190,208,180 -,209,129,209,130,208,178,208,176,208,177,208,181,208,183,208,190,208,191,208,176 -,209,129,208,189,208,190,209,129,209,130,208,184,224,164,170,224,165,129,224,164 -,184,224,165,141,224,164,164,224,164,191,224,164,149,224,164,190,224,164,149,224 -,164,190,224,164,130,224,164,151,224,165,141,224,164,176,224,165,135,224,164,184 -,224,164,137,224,164,168,224,165,141,224,164,185,224,165,139,224,164,130,224,164 -,168,224,165,135,224,164,181,224,164,191,224,164,167,224,164,190,224,164,168,224 -,164,184,224,164,173,224,164,190,224,164,171,224,164,191,224,164,149,224,165,141 -,224,164,184,224,164,191,224,164,130,224,164,151,224,164,184,224,165,129,224,164 -,176,224,164,149,224,165,141,224,164,183,224,164,191,224,164,164,224,164,149,224 -,165,137,224,164,170,224,165,128,224,164,176,224,164,190,224,164,135,224,164,159 -,224,164,181,224,164,191,224,164,156,224,165,141,224,164,158,224,164,190,224,164 -,170,224,164,168,224,164,149,224,164,190,224,164,176,224,165,141,224,164,176,224 -,164,181,224,164,190,224,164,136,224,164,184,224,164,149,224,165,141,224,164,176 -,224,164,191,224,164,175,224,164,164,224,164,190 -} -/* GENERATED CODE END */ -; -#endif /* !BROTLI_EXTERNAL_DICTIONARY_DATA */ - -#if !defined(BROTLI_EXTERNAL_DICTIONARY_DATA) -static const BrotliDictionary kBrotliDictionary = { -#else -static BrotliDictionary kBrotliDictionary = { -#endif - /* size_bits_by_length */ - { - 0, 0, 0, 0, 10, 10, 11, 11, - 10, 10, 10, 10, 10, 9, 9, 8, - 7, 7, 8, 7, 7, 6, 6, 5, - 5, 0, 0, 0, 0, 0, 0, 0 - }, - - /* offsets_by_length */ - { - 0, 0, 0, 0, 0, 4096, 9216, 21504, - 35840, 44032, 53248, 63488, 74752, 87040, 93696, 100864, - 104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280, - 122016, 122784, 122784, 122784, 122784, 122784, 122784, 122784 - }, - - /* data_size == sizeof(kBrotliDictionaryData) */ - 122784, - - /* data */ -#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA) - NULL -#else - kBrotliDictionaryData -#endif -}; - -const BrotliDictionary* BrotliGetDictionary(void) { - return &kBrotliDictionary; -} - -void BrotliSetDictionaryData(const uint8_t* data) { -#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA) - if (!!data && !kBrotliDictionary.data) { - kBrotliDictionary.data = data; - } -#else - BROTLI_UNUSED(data); // Appease -Werror=unused-parameter -#endif -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/common/dictionary.h b/src/deps/brotli/common/dictionary.h deleted file mode 100644 index b1c6f7f580e9c..0000000000000 --- a/src/deps/brotli/common/dictionary.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Collection of static dictionary words. */ - -#ifndef BROTLI_COMMON_DICTIONARY_H_ -#define BROTLI_COMMON_DICTIONARY_H_ - -#include -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct BrotliDictionary { - /** - * Number of bits to encode index of dictionary word in a bucket. - * - * Specification: Appendix A. Static Dictionary Data - * - * Words in a dictionary are bucketed by length. - * @c 0 means that there are no words of a given length. - * Dictionary consists of words with length of [4..24] bytes. - * Values at [0..3] and [25..31] indices should not be addressed. - */ - uint8_t size_bits_by_length[32]; - - /* assert(offset[i + 1] == offset[i] + (bits[i] ? (i << bits[i]) : 0)) */ - uint32_t offsets_by_length[32]; - - /* assert(data_size == offsets_by_length[31]) */ - size_t data_size; - - /* Data array is not bound, and should obey to size_bits_by_length values. - Specified size matches default (RFC 7932) dictionary. Its size is - defined by data_size */ - const uint8_t* data; -} BrotliDictionary; - -BROTLI_COMMON_API const BrotliDictionary* BrotliGetDictionary(void); - -/** - * Sets dictionary data. - * - * When dictionary data is already set / present, this method is no-op. - * - * Dictionary data MUST be provided before BrotliGetDictionary is invoked. - * This method is used ONLY in multi-client environment (e.g. C + Java), - * to reduce storage by sharing single dictionary between implementations. - */ -BROTLI_COMMON_API void BrotliSetDictionaryData(const uint8_t* data); - -#define BROTLI_MIN_DICTIONARY_WORD_LENGTH 4 -#define BROTLI_MAX_DICTIONARY_WORD_LENGTH 24 - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_COMMON_DICTIONARY_H_ */ diff --git a/src/deps/brotli/common/platform.c b/src/deps/brotli/common/platform.c deleted file mode 100644 index 25d84a9467215..0000000000000 --- a/src/deps/brotli/common/platform.c +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include - -#include - -#include "platform.h" - -/* Default brotli_alloc_func */ -void* BrotliDefaultAllocFunc(void* opaque, size_t size) { - BROTLI_UNUSED(opaque); - return malloc(size); -} - -/* Default brotli_free_func */ -void BrotliDefaultFreeFunc(void* opaque, void* address) { - BROTLI_UNUSED(opaque); - free(address); -} diff --git a/src/deps/brotli/common/platform.h b/src/deps/brotli/common/platform.h deleted file mode 100644 index 7406f3fe6968e..0000000000000 --- a/src/deps/brotli/common/platform.h +++ /dev/null @@ -1,541 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Macros for compiler / platform specific features and build options. - - Build options are: - * BROTLI_BUILD_32_BIT disables 64-bit optimizations - * BROTLI_BUILD_64_BIT forces to use 64-bit optimizations - * BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations - * BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations - * BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations - * BROTLI_BUILD_NO_RBIT disables "rbit" optimization for ARM CPUs - * BROTLI_BUILD_NO_UNALIGNED_READ_FAST forces off the fast-unaligned-read - optimizations (mainly for testing purposes) - * BROTLI_DEBUG dumps file name and line number when decoder detects stream - or memory error - * BROTLI_ENABLE_LOG enables asserts and dumps various state information - * BROTLI_ENABLE_DUMP overrides default "dump" behaviour -*/ - -#ifndef BROTLI_COMMON_PLATFORM_H_ -#define BROTLI_COMMON_PLATFORM_H_ - -#include /* memcpy */ - -#include -#include - -#if defined(OS_LINUX) || defined(OS_CYGWIN) || defined(__EMSCRIPTEN__) -#include -#elif defined(OS_FREEBSD) -#include -#elif defined(OS_MACOSX) -#include -/* Let's try and follow the Linux convention */ -#define BROTLI_X_BYTE_ORDER BYTE_ORDER -#define BROTLI_X_LITTLE_ENDIAN LITTLE_ENDIAN -#define BROTLI_X_BIG_ENDIAN BIG_ENDIAN -#endif - -#if BROTLI_MSVC_VERSION_CHECK(18, 0, 0) -#include -#endif - -#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG) -#include -#include -#endif - -/* The following macros were borrowed from https://github.com/nemequ/hedley - * with permission of original author - Evan Nemerson */ - -/* >>> >>> >>> hedley macros */ - -/* Define "BROTLI_PREDICT_TRUE" and "BROTLI_PREDICT_FALSE" macros for capable - compilers. - -To apply compiler hint, enclose the branching condition into macros, like this: - - if (BROTLI_PREDICT_TRUE(zero == 0)) { - // main execution path - } else { - // compiler should place this code outside of main execution path - } - -OR: - - if (BROTLI_PREDICT_FALSE(something_rare_or_unexpected_happens)) { - // compiler should place this code outside of main execution path - } - -*/ -#if BROTLI_GNUC_HAS_BUILTIN(__builtin_expect, 3, 0, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ - BROTLI_SUNPRO_VERSION_CHECK(5, 15, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ - BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ - BROTLI_TI_VERSION_CHECK(7, 3, 0) || \ - BROTLI_TINYC_VERSION_CHECK(0, 9, 27) -#define BROTLI_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) -#define BROTLI_PREDICT_FALSE(x) (__builtin_expect(x, 0)) -#else -#define BROTLI_PREDICT_FALSE(x) (x) -#define BROTLI_PREDICT_TRUE(x) (x) -#endif - -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__cplusplus) -#define BROTLI_RESTRICT restrict -#elif BROTLI_GNUC_VERSION_CHECK(3, 1, 0) || \ - BROTLI_MSVC_VERSION_CHECK(14, 0, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ - BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ - BROTLI_PGI_VERSION_CHECK(17, 10, 0) || \ - BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ - BROTLI_IAR_VERSION_CHECK(8, 0, 0) || \ - (BROTLI_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus)) -#define BROTLI_RESTRICT __restrict -#elif BROTLI_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus) -#define BROTLI_RESTRICT _Restrict -#else -#define BROTLI_RESTRICT -#endif - -#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - (defined(__cplusplus) && (__cplusplus >= 199711L)) -#define BROTLI_MAYBE_INLINE inline -#elif defined(__GNUC_STDC_INLINE__) || defined(__GNUC_GNU_INLINE__) || \ - BROTLI_ARM_VERSION_CHECK(6, 2, 0) -#define BROTLI_MAYBE_INLINE __inline__ -#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || BROTLI_TI_VERSION_CHECK(8, 0, 0) -#define BROTLI_MAYBE_INLINE __inline -#else -#define BROTLI_MAYBE_INLINE -#endif - -#if BROTLI_GNUC_HAS_ATTRIBUTE(always_inline, 4, 0, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ - BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ - BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ - BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ - (BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define BROTLI_INLINE BROTLI_MAYBE_INLINE __attribute__((__always_inline__)) -#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0) -#define BROTLI_INLINE BROTLI_MAYBE_INLINE __forceinline -#elif BROTLI_TI_VERSION_CHECK(7, 0, 0) && defined(__cplusplus) -#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("FUNC_ALWAYS_INLINE;") -#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0) -#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("inline=forced") -#else -#define BROTLI_INLINE BROTLI_MAYBE_INLINE -#endif - -#if BROTLI_GNUC_HAS_ATTRIBUTE(noinline, 4, 0, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ - BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ - BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ - BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ - (BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define BROTLI_NOINLINE __attribute__((__noinline__)) -#elif BROTLI_MSVC_VERSION_CHECK(13, 10, 0) -#define BROTLI_NOINLINE __declspec(noinline) -#elif BROTLI_PGI_VERSION_CHECK(10, 2, 0) -#define BROTLI_NOINLINE _Pragma("noinline") -#elif BROTLI_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) -#define BROTLI_NOINLINE _Pragma("FUNC_CANNOT_INLINE;") -#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0) -#define BROTLI_NOINLINE _Pragma("inline=never") -#else -#define BROTLI_NOINLINE -#endif - -/* <<< <<< <<< end of hedley macros. */ - -#if BROTLI_GNUC_HAS_ATTRIBUTE(unused, 2, 7, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) -#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE __attribute__ ((unused)) -#else -#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE -#endif - -#if BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) -#define BROTLI_ALIGNED(N) __attribute__((aligned(N))) -#else -#define BROTLI_ALIGNED(N) -#endif - -#if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \ - (defined(M_ARM) && (M_ARM == 7)) -#define BROTLI_TARGET_ARMV7 -#endif /* ARMv7 */ - -#if (defined(__ARM_ARCH) && (__ARM_ARCH == 8)) || \ - defined(__aarch64__) || defined(__ARM64_ARCH_8__) -#define BROTLI_TARGET_ARMV8_ANY - -#if defined(__ARM_32BIT_STATE) -#define BROTLI_TARGET_ARMV8_32 -#elif defined(__ARM_64BIT_STATE) -#define BROTLI_TARGET_ARMV8_64 -#endif - -#endif /* ARMv8 */ - -#if defined(__ARM_NEON__) || defined(__ARM_NEON) -#define BROTLI_TARGET_NEON -#endif - -#if defined(__i386) || defined(_M_IX86) -#define BROTLI_TARGET_X86 -#endif - -#if defined(__x86_64__) || defined(_M_X64) -#define BROTLI_TARGET_X64 -#endif - -#if defined(__PPC64__) -#define BROTLI_TARGET_POWERPC64 -#endif - -#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64 -#define BROTLI_TARGET_RISCV64 -#endif - -#if defined(__loongarch_lp64) -#define BROTLI_TARGET_LOONGARCH64 -#endif - -#if defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8_64) || \ - defined(BROTLI_TARGET_POWERPC64) || defined(BROTLI_TARGET_RISCV64) || \ - defined(BROTLI_TARGET_LOONGARCH64) -#define BROTLI_TARGET_64_BITS 1 -#else -#define BROTLI_TARGET_64_BITS 0 -#endif - -#if defined(BROTLI_BUILD_64_BIT) -#define BROTLI_64_BITS 1 -#elif defined(BROTLI_BUILD_32_BIT) -#define BROTLI_64_BITS 0 -#else -#define BROTLI_64_BITS BROTLI_TARGET_64_BITS -#endif - -#if (BROTLI_64_BITS) -#define brotli_reg_t uint64_t -#else -#define brotli_reg_t uint32_t -#endif - -#if defined(BROTLI_BUILD_BIG_ENDIAN) -#define BROTLI_BIG_ENDIAN 1 -#elif defined(BROTLI_BUILD_LITTLE_ENDIAN) -#define BROTLI_LITTLE_ENDIAN 1 -#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL) -/* Just break elif chain. */ -#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#define BROTLI_LITTLE_ENDIAN 1 -#elif defined(_WIN32) || defined(BROTLI_TARGET_X64) -/* Win32 & x64 can currently always be assumed to be little endian */ -#define BROTLI_LITTLE_ENDIAN 1 -#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#define BROTLI_BIG_ENDIAN 1 -#elif defined(BROTLI_X_BYTE_ORDER) -#if BROTLI_X_BYTE_ORDER == BROTLI_X_LITTLE_ENDIAN -#define BROTLI_LITTLE_ENDIAN 1 -#elif BROTLI_X_BYTE_ORDER == BROTLI_X_BIG_ENDIAN -#define BROTLI_BIG_ENDIAN 1 -#endif -#endif /* BROTLI_X_BYTE_ORDER */ - -#if !defined(BROTLI_LITTLE_ENDIAN) -#define BROTLI_LITTLE_ENDIAN 0 -#endif - -#if !defined(BROTLI_BIG_ENDIAN) -#define BROTLI_BIG_ENDIAN 0 -#endif - -#if defined(BROTLI_X_BYTE_ORDER) -#undef BROTLI_X_BYTE_ORDER -#undef BROTLI_X_LITTLE_ENDIAN -#undef BROTLI_X_BIG_ENDIAN -#endif - -#if defined(BROTLI_BUILD_NO_UNALIGNED_READ_FAST) -#define BROTLI_UNALIGNED_READ_FAST (!!0) -#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \ - defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) || \ - defined(BROTLI_TARGET_RISCV64) || defined(BROTLI_TARGET_LOONGARCH64) -/* These targets are known to generate efficient code for unaligned reads - * (e.g. a single instruction, not multiple 1-byte loads, shifted and or'd - * together). */ -#define BROTLI_UNALIGNED_READ_FAST (!!1) -#else -#define BROTLI_UNALIGNED_READ_FAST (!!0) -#endif - -/* Portable unaligned memory access: read / write values via memcpy. */ -static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) { - uint16_t t; - memcpy(&t, p, sizeof t); - return t; -} -static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) { - uint32_t t; - memcpy(&t, p, sizeof t); - return t; -} -static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) { - uint64_t t; - memcpy(&t, p, sizeof t); - return t; -} -static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) { - memcpy(p, &v, sizeof v); -} - -#if BROTLI_LITTLE_ENDIAN -/* Straight endianness. Just read / write values. */ -#define BROTLI_UNALIGNED_LOAD16LE BrotliUnalignedRead16 -#define BROTLI_UNALIGNED_LOAD32LE BrotliUnalignedRead32 -#define BROTLI_UNALIGNED_LOAD64LE BrotliUnalignedRead64 -#define BROTLI_UNALIGNED_STORE64LE BrotliUnalignedWrite64 -#elif BROTLI_BIG_ENDIAN /* BROTLI_LITTLE_ENDIAN */ -/* Explain compiler to byte-swap values. */ -#define BROTLI_BSWAP16_(V) ((uint16_t)( \ - (((V) & 0xFFU) << 8) | \ - (((V) >> 8) & 0xFFU))) -static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) { - uint16_t value = BrotliUnalignedRead16(p); - return BROTLI_BSWAP16_(value); -} -#define BROTLI_BSWAP32_(V) ( \ - (((V) & 0xFFU) << 24) | (((V) & 0xFF00U) << 8) | \ - (((V) >> 8) & 0xFF00U) | (((V) >> 24) & 0xFFU)) -static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) { - uint32_t value = BrotliUnalignedRead32(p); - return BROTLI_BSWAP32_(value); -} -#define BROTLI_BSWAP64_(V) ( \ - (((V) & 0xFFU) << 56) | (((V) & 0xFF00U) << 40) | \ - (((V) & 0xFF0000U) << 24) | (((V) & 0xFF000000U) << 8) | \ - (((V) >> 8) & 0xFF000000U) | (((V) >> 24) & 0xFF0000U) | \ - (((V) >> 40) & 0xFF00U) | (((V) >> 56) & 0xFFU)) -static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) { - uint64_t value = BrotliUnalignedRead64(p); - return BROTLI_BSWAP64_(value); -} -static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) { - uint64_t value = BROTLI_BSWAP64_(v); - BrotliUnalignedWrite64(p, value); -} -#else /* BROTLI_LITTLE_ENDIAN */ -/* Read / store values byte-wise; hopefully compiler will understand. */ -static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) { - const uint8_t* in = (const uint8_t*)p; - return (uint16_t)(in[0] | (in[1] << 8)); -} -static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) { - const uint8_t* in = (const uint8_t*)p; - uint32_t value = (uint32_t)(in[0]); - value |= (uint32_t)(in[1]) << 8; - value |= (uint32_t)(in[2]) << 16; - value |= (uint32_t)(in[3]) << 24; - return value; -} -static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) { - const uint8_t* in = (const uint8_t*)p; - uint64_t value = (uint64_t)(in[0]); - value |= (uint64_t)(in[1]) << 8; - value |= (uint64_t)(in[2]) << 16; - value |= (uint64_t)(in[3]) << 24; - value |= (uint64_t)(in[4]) << 32; - value |= (uint64_t)(in[5]) << 40; - value |= (uint64_t)(in[6]) << 48; - value |= (uint64_t)(in[7]) << 56; - return value; -} -static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) { - uint8_t* out = (uint8_t*)p; - out[0] = (uint8_t)v; - out[1] = (uint8_t)(v >> 8); - out[2] = (uint8_t)(v >> 16); - out[3] = (uint8_t)(v >> 24); - out[4] = (uint8_t)(v >> 32); - out[5] = (uint8_t)(v >> 40); - out[6] = (uint8_t)(v >> 48); - out[7] = (uint8_t)(v >> 56); -} -#endif /* BROTLI_LITTLE_ENDIAN */ - -static BROTLI_INLINE void* BROTLI_UNALIGNED_LOAD_PTR(const void* p) { - void* v; - memcpy(&v, p, sizeof(void*)); - return v; -} - -static BROTLI_INLINE void BROTLI_UNALIGNED_STORE_PTR(void* p, const void* v) { - memcpy(p, &v, sizeof(void*)); -} - -/* BROTLI_IS_CONSTANT macros returns true for compile-time constants. */ -#if BROTLI_GNUC_HAS_BUILTIN(__builtin_constant_p, 3, 0, 1) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) -#define BROTLI_IS_CONSTANT(x) (!!__builtin_constant_p(x)) -#else -#define BROTLI_IS_CONSTANT(x) (!!0) -#endif - -#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) -#define BROTLI_HAS_UBFX (!!1) -#else -#define BROTLI_HAS_UBFX (!!0) -#endif - -#if defined(BROTLI_ENABLE_LOG) -#define BROTLI_LOG(x) printf x -#else -#define BROTLI_LOG(x) -#endif - -#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG) -#define BROTLI_ENABLE_DUMP_DEFAULT 1 -#define BROTLI_DCHECK(x) assert(x) -#else -#define BROTLI_ENABLE_DUMP_DEFAULT 0 -#define BROTLI_DCHECK(x) -#endif - -#if !defined(BROTLI_ENABLE_DUMP) -#define BROTLI_ENABLE_DUMP BROTLI_ENABLE_DUMP_DEFAULT -#endif - -#if BROTLI_ENABLE_DUMP -static BROTLI_INLINE void BrotliDump(const char* f, int l, const char* fn) { - fprintf(stderr, "%s:%d (%s)\n", f, l, fn); - fflush(stderr); -} -#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__) -#else -#define BROTLI_DUMP() (void)(0) -#endif - -/* BrotliRBit assumes brotli_reg_t fits native CPU register type. */ -#if (BROTLI_64_BITS == BROTLI_TARGET_64_BITS) -/* TODO(eustas): add appropriate icc/sunpro/arm/ibm/ti checks. */ -#if (BROTLI_GNUC_VERSION_CHECK(3, 0, 0) || defined(__llvm__)) && \ - !defined(BROTLI_BUILD_NO_RBIT) -#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) -/* TODO(eustas): detect ARMv6T2 and enable this code for it. */ -static BROTLI_INLINE brotli_reg_t BrotliRBit(brotli_reg_t input) { - brotli_reg_t output; - __asm__("rbit %0, %1\n" : "=r"(output) : "r"(input)); - return output; -} -#define BROTLI_RBIT(x) BrotliRBit(x) -#endif /* armv7 / armv8 */ -#endif /* gcc || clang */ -#endif /* brotli_reg_t is native */ -#if !defined(BROTLI_RBIT) -static BROTLI_INLINE void BrotliRBit(void) { /* Should break build if used. */ } -#endif /* BROTLI_RBIT */ - -#define BROTLI_REPEAT_4(X) {X; X; X; X;} -#define BROTLI_REPEAT_5(X) {X; X; X; X; X;} -#define BROTLI_REPEAT_6(X) {X; X; X; X; X; X;} - -#define BROTLI_UNUSED(X) (void)(X) - -#define BROTLI_MIN_MAX(T) \ - static BROTLI_INLINE T brotli_min_ ## T (T a, T b) { return a < b ? a : b; } \ - static BROTLI_INLINE T brotli_max_ ## T (T a, T b) { return a > b ? a : b; } -BROTLI_MIN_MAX(double) BROTLI_MIN_MAX(float) BROTLI_MIN_MAX(int) -BROTLI_MIN_MAX(size_t) BROTLI_MIN_MAX(uint32_t) BROTLI_MIN_MAX(uint8_t) -#undef BROTLI_MIN_MAX -#define BROTLI_MIN(T, A, B) (brotli_min_ ## T((A), (B))) -#define BROTLI_MAX(T, A, B) (brotli_max_ ## T((A), (B))) - -#define BROTLI_SWAP(T, A, I, J) { \ - T __brotli_swap_tmp = (A)[(I)]; \ - (A)[(I)] = (A)[(J)]; \ - (A)[(J)] = __brotli_swap_tmp; \ -} - -#if BROTLI_64_BITS -#if BROTLI_GNUC_HAS_BUILTIN(__builtin_ctzll, 3, 4, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) -#define BROTLI_TZCNT64 __builtin_ctzll -#elif BROTLI_MSVC_VERSION_CHECK(18, 0, 0) -#if defined(BROTLI_TARGET_X64) -#define BROTLI_TZCNT64 _tzcnt_u64 -#else /* BROTLI_TARGET_X64 */ -static BROTLI_INLINE uint32_t BrotliBsf64Msvc(uint64_t x) { - uint32_t lsb; - _BitScanForward64(&lsb, x); - return lsb; -} -#define BROTLI_TZCNT64 BrotliBsf64Msvc -#endif /* BROTLI_TARGET_X64 */ -#endif /* __builtin_ctzll */ -#endif /* BROTLI_64_BITS */ - -#if BROTLI_GNUC_HAS_BUILTIN(__builtin_clz, 3, 4, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) -#define BROTLI_BSR32(x) (31u ^ (uint32_t)__builtin_clz(x)) -#elif BROTLI_MSVC_VERSION_CHECK(18, 0, 0) -static BROTLI_INLINE uint32_t BrotliBsr32Msvc(uint32_t x) { - unsigned long msb; - _BitScanReverse(&msb, x); - return (uint32_t)msb; -} -#define BROTLI_BSR32 BrotliBsr32Msvc -#endif /* __builtin_clz */ - -/* Default brotli_alloc_func */ -BROTLI_COMMON_API void* BrotliDefaultAllocFunc(void* opaque, size_t size); - -/* Default brotli_free_func */ -BROTLI_COMMON_API void BrotliDefaultFreeFunc(void* opaque, void* address); - -BROTLI_UNUSED_FUNCTION void BrotliSuppressUnusedFunctions(void) { - BROTLI_UNUSED(&BrotliSuppressUnusedFunctions); - BROTLI_UNUSED(&BrotliUnalignedRead16); - BROTLI_UNUSED(&BrotliUnalignedRead32); - BROTLI_UNUSED(&BrotliUnalignedRead64); - BROTLI_UNUSED(&BrotliUnalignedWrite64); - BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD16LE); - BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD32LE); - BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD64LE); - BROTLI_UNUSED(&BROTLI_UNALIGNED_STORE64LE); - BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD_PTR); - BROTLI_UNUSED(&BROTLI_UNALIGNED_STORE_PTR); - BROTLI_UNUSED(&BrotliRBit); - BROTLI_UNUSED(&brotli_min_double); - BROTLI_UNUSED(&brotli_max_double); - BROTLI_UNUSED(&brotli_min_float); - BROTLI_UNUSED(&brotli_max_float); - BROTLI_UNUSED(&brotli_min_int); - BROTLI_UNUSED(&brotli_max_int); - BROTLI_UNUSED(&brotli_min_size_t); - BROTLI_UNUSED(&brotli_max_size_t); - BROTLI_UNUSED(&brotli_min_uint32_t); - BROTLI_UNUSED(&brotli_max_uint32_t); - BROTLI_UNUSED(&brotli_min_uint8_t); - BROTLI_UNUSED(&brotli_max_uint8_t); - BROTLI_UNUSED(&BrotliDefaultAllocFunc); - BROTLI_UNUSED(&BrotliDefaultFreeFunc); -#if BROTLI_ENABLE_DUMP - BROTLI_UNUSED(&BrotliDump); -#endif -} - -#endif /* BROTLI_COMMON_PLATFORM_H_ */ diff --git a/src/deps/brotli/common/shared_dictionary.c b/src/deps/brotli/common/shared_dictionary.c deleted file mode 100644 index 49f1c9b075fd6..0000000000000 --- a/src/deps/brotli/common/shared_dictionary.c +++ /dev/null @@ -1,521 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Shared Dictionary definition and utilities. */ - -#include - -#include -#include /* malloc, free */ -#include - -#include "dictionary.h" -#include "platform.h" -#include "shared_dictionary_internal.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#if defined(BROTLI_EXPERIMENTAL) - -#define BROTLI_NUM_ENCODED_LENGTHS (SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH \ - - SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH + 1) - -/* Max allowed by spec */ -#define BROTLI_MAX_SIZE_BITS 15u - -/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ -static BROTLI_BOOL ReadBool(const uint8_t* encoded, size_t size, size_t* pos, - BROTLI_BOOL* result) { - uint8_t value; - size_t position = *pos; - if (position >= size) return BROTLI_FALSE; /* past file end */ - value = encoded[position++]; - if (value > 1) return BROTLI_FALSE; /* invalid bool */ - *result = TO_BROTLI_BOOL(value); - *pos = position; - return BROTLI_TRUE; /* success */ -} - -/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ -static BROTLI_BOOL ReadUint8(const uint8_t* encoded, size_t size, size_t* pos, - uint8_t* result) { - size_t position = *pos; - if (position + sizeof(uint8_t) > size) return BROTLI_FALSE; - *result = encoded[position++]; - *pos = position; - return BROTLI_TRUE; -} - -/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ -static BROTLI_BOOL ReadUint16(const uint8_t* encoded, size_t size, size_t* pos, - uint16_t* result) { - size_t position = *pos; - if (position + sizeof(uint16_t) > size) return BROTLI_FALSE; - *result = BROTLI_UNALIGNED_LOAD16LE(&encoded[position]); - position += 2; - *pos = position; - return BROTLI_TRUE; -} - -/* Reads a varint into a uint32_t, and returns error if it's too large */ -/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ -static BROTLI_BOOL ReadVarint32(const uint8_t* encoded, size_t size, - size_t* pos, uint32_t* result) { - int num = 0; - uint8_t byte; - *result = 0; - for (;;) { - if (*pos >= size) return BROTLI_FALSE; - byte = encoded[(*pos)++]; - if (num == 4 && byte > 15) return BROTLI_FALSE; - *result |= (uint32_t)(byte & 127) << (num * 7); - if (byte < 128) return BROTLI_TRUE; - num++; - } -} - -/* Returns the total length of word list. */ -static size_t BrotliSizeBitsToOffsets(const uint8_t* size_bits_by_length, - uint32_t* offsets_by_length) { - uint32_t pos = 0; - uint32_t i; - for (i = 0; i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; i++) { - offsets_by_length[i] = pos; - if (size_bits_by_length[i] != 0) { - pos += i << size_bits_by_length[i]; - } - } - return pos; -} - -static BROTLI_BOOL ParseWordList(size_t size, const uint8_t* encoded, - size_t* pos, BrotliDictionary* out) { - size_t offset; - size_t i; - size_t position = *pos; - if (position + BROTLI_NUM_ENCODED_LENGTHS > size) { - return BROTLI_FALSE; - } - - memset(out->size_bits_by_length, 0, SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH); - memcpy(out->size_bits_by_length + SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH, - &encoded[position], BROTLI_NUM_ENCODED_LENGTHS); - for (i = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; - i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; i++) { - if (out->size_bits_by_length[i] > BROTLI_MAX_SIZE_BITS) { - return BROTLI_FALSE; - } - } - position += BROTLI_NUM_ENCODED_LENGTHS; - offset = BrotliSizeBitsToOffsets( - out->size_bits_by_length, out->offsets_by_length); - - out->data = &encoded[position]; - out->data_size = offset; - position += offset; - if (position > size) return BROTLI_FALSE; - *pos = position; - return BROTLI_TRUE; -} - -/* Computes the cutOffTransforms of a BrotliTransforms which already has the - transforms data correctly filled in. */ -static void ComputeCutoffTransforms(BrotliTransforms* transforms) { - uint32_t i; - for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) { - transforms->cutOffTransforms[i] = -1; - } - for (i = 0; i < transforms->num_transforms; i++) { - const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, i); - uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, i); - const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, i); - if (type <= BROTLI_TRANSFORM_OMIT_LAST_9 && *prefix == 0 && *suffix == 0 && - transforms->cutOffTransforms[type] == -1) { - transforms->cutOffTransforms[type] = (int16_t)i; - } - } -} - -static BROTLI_BOOL ParsePrefixSuffixTable(size_t size, const uint8_t* encoded, - size_t* pos, BrotliTransforms* out, uint16_t* out_table, - size_t* out_table_size) { - size_t position = *pos; - size_t offset = 0; - size_t stringlet_count = 0; /* NUM_PREFIX_SUFFIX */ - size_t data_length = 0; - - /* PREFIX_SUFFIX_LENGTH */ - if (!ReadUint16(encoded, size, &position, &out->prefix_suffix_size)) { - return BROTLI_FALSE; - } - data_length = out->prefix_suffix_size; - - /* Must at least have space for null terminator. */ - if (data_length < 1) return BROTLI_FALSE; - out->prefix_suffix = &encoded[position]; - if (position + data_length >= size) return BROTLI_FALSE; - while (BROTLI_TRUE) { - /* STRING_LENGTH */ - size_t stringlet_len = encoded[position + offset]; - out_table[stringlet_count] = (uint16_t)offset; - stringlet_count++; - offset++; - if (stringlet_len == 0) { - if (offset == data_length) { - break; - } else { - return BROTLI_FALSE; - } - } - if (stringlet_count > 255) return BROTLI_FALSE; - offset += stringlet_len; - if (offset >= data_length) return BROTLI_FALSE; - } - - position += data_length; - *pos = position; - *out_table_size = (uint16_t)stringlet_count; - return BROTLI_TRUE; -} - -static BROTLI_BOOL ParseTransformsList(size_t size, const uint8_t* encoded, - size_t* pos, BrotliTransforms* out, uint16_t* prefix_suffix_table, - size_t* prefix_suffix_count) { - uint32_t i; - BROTLI_BOOL has_params = BROTLI_FALSE; - BROTLI_BOOL prefix_suffix_ok = BROTLI_FALSE; - size_t position = *pos; - size_t stringlet_cnt = 0; - if (position >= size) return BROTLI_FALSE; - - prefix_suffix_ok = ParsePrefixSuffixTable( - size, encoded, &position, out, prefix_suffix_table, &stringlet_cnt); - if (!prefix_suffix_ok) return BROTLI_FALSE; - out->prefix_suffix_map = prefix_suffix_table; - *prefix_suffix_count = stringlet_cnt; - - out->num_transforms = encoded[position++]; - out->transforms = &encoded[position]; - position += (size_t)out->num_transforms * 3; - if (position > size) return BROTLI_FALSE; - /* Check for errors and read extra parameters. */ - for (i = 0; i < out->num_transforms; i++) { - uint8_t prefix_id = BROTLI_TRANSFORM_PREFIX_ID(out, i); - uint8_t type = BROTLI_TRANSFORM_TYPE(out, i); - uint8_t suffix_id = BROTLI_TRANSFORM_SUFFIX_ID(out, i); - if (prefix_id >= stringlet_cnt) return BROTLI_FALSE; - if (type >= BROTLI_NUM_TRANSFORM_TYPES) return BROTLI_FALSE; - if (suffix_id >= stringlet_cnt) return BROTLI_FALSE; - if (type == BROTLI_TRANSFORM_SHIFT_FIRST || - type == BROTLI_TRANSFORM_SHIFT_ALL) { - has_params = BROTLI_TRUE; - } - } - if (has_params) { - out->params = &encoded[position]; - position += (size_t)out->num_transforms * 2; - if (position > size) return BROTLI_FALSE; - for (i = 0; i < out->num_transforms; i++) { - uint8_t type = BROTLI_TRANSFORM_TYPE(out, i); - if (type != BROTLI_TRANSFORM_SHIFT_FIRST && - type != BROTLI_TRANSFORM_SHIFT_ALL) { - if (out->params[i * 2] != 0 || out->params[i * 2 + 1] != 0) { - return BROTLI_FALSE; - } - } - } - } else { - out->params = NULL; - } - ComputeCutoffTransforms(out); - *pos = position; - return BROTLI_TRUE; -} - -static BROTLI_BOOL DryParseDictionary(const uint8_t* encoded, - size_t size, uint32_t* num_prefix, BROTLI_BOOL* is_custom_static_dict) { - size_t pos = 0; - uint32_t chunk_size = 0; - uint8_t num_word_lists; - uint8_t num_transform_lists; - *is_custom_static_dict = BROTLI_FALSE; - *num_prefix = 0; - - /* Skip magic header bytes. */ - pos += 2; - - /* LZ77_DICTIONARY_LENGTH */ - if (!ReadVarint32(encoded, size, &pos, &chunk_size)) return BROTLI_FALSE; - if (chunk_size != 0) { - /* This limitation is not specified but the 32-bit Brotli decoder for now */ - if (chunk_size > 1073741823) return BROTLI_FALSE; - *num_prefix = 1; - if (pos + chunk_size > size) return BROTLI_FALSE; - pos += chunk_size; - } - - if (!ReadUint8(encoded, size, &pos, &num_word_lists)) { - return BROTLI_FALSE; - } - if (!ReadUint8(encoded, size, &pos, &num_transform_lists)) { - return BROTLI_FALSE; - } - - if (num_word_lists > 0 || num_transform_lists > 0) { - *is_custom_static_dict = BROTLI_TRUE; - } - - return BROTLI_TRUE; -} - -static BROTLI_BOOL ParseDictionary(const uint8_t* encoded, size_t size, - BrotliSharedDictionary* dict) { - uint32_t i; - size_t pos = 0; - uint32_t chunk_size = 0; - size_t total_prefix_suffix_count = 0; - size_t trasform_list_start[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; - uint16_t temporary_prefix_suffix_table[256]; - - /* Skip magic header bytes. */ - pos += 2; - - /* LZ77_DICTIONARY_LENGTH */ - if (!ReadVarint32(encoded, size, &pos, &chunk_size)) return BROTLI_FALSE; - if (chunk_size != 0) { - if (pos + chunk_size > size) return BROTLI_FALSE; - dict->prefix_size[dict->num_prefix] = chunk_size; - dict->prefix[dict->num_prefix] = &encoded[pos]; - dict->num_prefix++; - /* LZ77_DICTIONARY_LENGTH bytes. */ - pos += chunk_size; - } - - /* NUM_WORD_LISTS */ - if (!ReadUint8(encoded, size, &pos, &dict->num_word_lists)) { - return BROTLI_FALSE; - } - if (dict->num_word_lists > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) { - return BROTLI_FALSE; - } - - if (dict->num_word_lists != 0) { - dict->words_instances = (BrotliDictionary*)dict->alloc_func( - dict->memory_manager_opaque, - dict->num_word_lists * sizeof(*dict->words_instances)); - if (!dict->words_instances) return BROTLI_FALSE; /* OOM */ - } - for (i = 0; i < dict->num_word_lists; i++) { - if (!ParseWordList(size, encoded, &pos, &dict->words_instances[i])) { - return BROTLI_FALSE; - } - } - - /* NUM_TRANSFORM_LISTS */ - if (!ReadUint8(encoded, size, &pos, &dict->num_transform_lists)) { - return BROTLI_FALSE; - } - if (dict->num_transform_lists > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) { - return BROTLI_FALSE; - } - - if (dict->num_transform_lists != 0) { - dict->transforms_instances = (BrotliTransforms*)dict->alloc_func( - dict->memory_manager_opaque, - dict->num_transform_lists * sizeof(*dict->transforms_instances)); - if (!dict->transforms_instances) return BROTLI_FALSE; /* OOM */ - } - for (i = 0; i < dict->num_transform_lists; i++) { - BROTLI_BOOL ok = BROTLI_FALSE; - size_t prefix_suffix_count = 0; - trasform_list_start[i] = pos; - dict->transforms_instances[i].prefix_suffix_map = - temporary_prefix_suffix_table; - ok = ParseTransformsList( - size, encoded, &pos, &dict->transforms_instances[i], - temporary_prefix_suffix_table, &prefix_suffix_count); - if (!ok) return BROTLI_FALSE; - total_prefix_suffix_count += prefix_suffix_count; - } - if (total_prefix_suffix_count != 0) { - dict->prefix_suffix_maps = (uint16_t*)dict->alloc_func( - dict->memory_manager_opaque, - total_prefix_suffix_count * sizeof(*dict->prefix_suffix_maps)); - if (!dict->prefix_suffix_maps) return BROTLI_FALSE; /* OOM */ - } - total_prefix_suffix_count = 0; - for (i = 0; i < dict->num_transform_lists; i++) { - size_t prefix_suffix_count = 0; - size_t position = trasform_list_start[i]; - uint16_t* prefix_suffix_map = - &dict->prefix_suffix_maps[total_prefix_suffix_count]; - BROTLI_BOOL ok = ParsePrefixSuffixTable( - size, encoded, &position, &dict->transforms_instances[i], - prefix_suffix_map, &prefix_suffix_count); - if (!ok) return BROTLI_FALSE; - dict->transforms_instances[i].prefix_suffix_map = prefix_suffix_map; - total_prefix_suffix_count += prefix_suffix_count; - } - - if (dict->num_word_lists != 0 || dict->num_transform_lists != 0) { - if (!ReadUint8(encoded, size, &pos, &dict->num_dictionaries)) { - return BROTLI_FALSE; - } - if (dict->num_dictionaries == 0 || - dict->num_dictionaries > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) { - return BROTLI_FALSE; - } - for (i = 0; i < dict->num_dictionaries; i++) { - uint8_t words_index; - uint8_t transforms_index; - if (!ReadUint8(encoded, size, &pos, &words_index)) { - return BROTLI_FALSE; - } - if (words_index > dict->num_word_lists) return BROTLI_FALSE; - if (!ReadUint8(encoded, size, &pos, &transforms_index)) { - return BROTLI_FALSE; - } - if (transforms_index > dict->num_transform_lists) return BROTLI_FALSE; - dict->words[i] = words_index == dict->num_word_lists ? - BrotliGetDictionary() : &dict->words_instances[words_index]; - dict->transforms[i] = transforms_index == dict->num_transform_lists ? - BrotliGetTransforms(): &dict->transforms_instances[transforms_index]; - } - /* CONTEXT_ENABLED */ - if (!ReadBool(encoded, size, &pos, &dict->context_based)) { - return BROTLI_FALSE; - } - - /* CONTEXT_MAP */ - if (dict->context_based) { - for (i = 0; i < SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS; i++) { - if (!ReadUint8(encoded, size, &pos, &dict->context_map[i])) { - return BROTLI_FALSE; - } - if (dict->context_map[i] >= dict->num_dictionaries) { - return BROTLI_FALSE; - } - } - } - } else { - dict->context_based = BROTLI_FALSE; - dict->num_dictionaries = 1; - dict->words[0] = BrotliGetDictionary(); - dict->transforms[0] = BrotliGetTransforms(); - } - - return BROTLI_TRUE; -} - -/* Decodes shared dictionary and verifies correctness. - Returns BROTLI_TRUE if dictionary is valid, BROTLI_FALSE otherwise. - The BrotliSharedDictionary must already have been initialized. If the - BrotliSharedDictionary already contains data, compound dictionaries - will be appended, but an error will be returned if it already has - custom words or transforms. - TODO(lode): link to RFC for shared brotli once published. */ -static BROTLI_BOOL DecodeSharedDictionary( - const uint8_t* encoded, size_t size, BrotliSharedDictionary* dict) { - uint32_t num_prefix = 0; - BROTLI_BOOL is_custom_static_dict = BROTLI_FALSE; - BROTLI_BOOL has_custom_static_dict = - dict->num_word_lists > 0 || dict->num_transform_lists > 0; - - /* Check magic header bytes. */ - if (size < 2) return BROTLI_FALSE; - if (encoded[0] != 0x91 || encoded[1] != 0) return BROTLI_FALSE; - - if (!DryParseDictionary(encoded, size, &num_prefix, &is_custom_static_dict)) { - return BROTLI_FALSE; - } - - if (num_prefix + dict->num_prefix > SHARED_BROTLI_MAX_COMPOUND_DICTS) { - return BROTLI_FALSE; - } - - /* Cannot combine different static dictionaries, only prefix dictionaries */ - if (has_custom_static_dict && is_custom_static_dict) return BROTLI_FALSE; - - return ParseDictionary(encoded, size, dict); -} - -#endif /* BROTLI_EXPERIMENTAL */ - -void BrotliSharedDictionaryDestroyInstance( - BrotliSharedDictionary* dict) { - if (!dict) { - return; - } else { - brotli_free_func free_func = dict->free_func; - void* opaque = dict->memory_manager_opaque; - /* Cleanup. */ - free_func(opaque, dict->words_instances); - free_func(opaque, dict->transforms_instances); - free_func(opaque, dict->prefix_suffix_maps); - /* Self-destruction. */ - free_func(opaque, dict); - } -} - -BROTLI_BOOL BrotliSharedDictionaryAttach( - BrotliSharedDictionary* dict, BrotliSharedDictionaryType type, - size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]) { - if (!dict) { - return BROTLI_FALSE; - } -#if defined(BROTLI_EXPERIMENTAL) - if (type == BROTLI_SHARED_DICTIONARY_SERIALIZED) { - return DecodeSharedDictionary(data, data_size, dict); - } -#endif /* BROTLI_EXPERIMENTAL */ - if (type == BROTLI_SHARED_DICTIONARY_RAW) { - if (dict->num_prefix >= SHARED_BROTLI_MAX_COMPOUND_DICTS) { - return BROTLI_FALSE; - } - dict->prefix_size[dict->num_prefix] = data_size; - dict->prefix[dict->num_prefix] = data; - dict->num_prefix++; - return BROTLI_TRUE; - } - return BROTLI_FALSE; -} - -BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - BrotliSharedDictionary* dict = 0; - if (!alloc_func && !free_func) { - dict = (BrotliSharedDictionary*)malloc(sizeof(BrotliSharedDictionary)); - } else if (alloc_func && free_func) { - dict = (BrotliSharedDictionary*)alloc_func( - opaque, sizeof(BrotliSharedDictionary)); - } - if (dict == 0) { - return 0; - } - - /* TODO(eustas): explicitly initialize all the fields? */ - memset(dict, 0, sizeof(BrotliSharedDictionary)); - - dict->context_based = BROTLI_FALSE; - dict->num_dictionaries = 1; - dict->num_word_lists = 0; - dict->num_transform_lists = 0; - - dict->words[0] = BrotliGetDictionary(); - dict->transforms[0] = BrotliGetTransforms(); - - dict->alloc_func = alloc_func ? alloc_func : BrotliDefaultAllocFunc; - dict->free_func = free_func ? free_func : BrotliDefaultFreeFunc; - dict->memory_manager_opaque = opaque; - - return dict; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/common/shared_dictionary_internal.h b/src/deps/brotli/common/shared_dictionary_internal.h deleted file mode 100644 index 963762e43250d..0000000000000 --- a/src/deps/brotli/common/shared_dictionary_internal.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* (Transparent) Shared Dictionary definition. */ - -#ifndef BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ -#define BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ - -#include -#include - -#include "dictionary.h" -#include "transform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -struct BrotliSharedDictionaryStruct { - /* LZ77 prefixes (compound dictionary). */ - uint32_t num_prefix; /* max SHARED_BROTLI_MAX_COMPOUND_DICTS */ - size_t prefix_size[SHARED_BROTLI_MAX_COMPOUND_DICTS]; - const uint8_t* prefix[SHARED_BROTLI_MAX_COMPOUND_DICTS]; - - /* If set, the context map is used to select word and transform list from 64 - contexts, if not set, the context map is not used and only words[0] and - transforms[0] are to be used. */ - BROTLI_BOOL context_based; - - uint8_t context_map[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; - - /* Amount of word_list+transform_list combinations. */ - uint8_t num_dictionaries; - - /* Must use num_dictionaries values. */ - const BrotliDictionary* words[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; - - /* Must use num_dictionaries values. */ - const BrotliTransforms* transforms[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; - - /* Amount of custom word lists. May be 0 if only Brotli's built-in is used */ - uint8_t num_word_lists; - - /* Contents of the custom words lists. Must be NULL if num_word_lists is 0. */ - BrotliDictionary* words_instances; - - /* Amount of custom transform lists. May be 0 if only Brotli's built-in is - used */ - uint8_t num_transform_lists; - - /* Contents of the custom transform lists. Must be NULL if num_transform_lists - is 0. */ - BrotliTransforms* transforms_instances; - - /* Concatenated prefix_suffix_maps of the custom transform lists. Must be NULL - if num_transform_lists is 0. */ - uint16_t* prefix_suffix_maps; - - /* Memory management */ - brotli_alloc_func alloc_func; - brotli_free_func free_func; - void* memory_manager_opaque; -}; - -typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionaryInternal; -#define BrotliSharedDictionary BrotliSharedDictionaryInternal - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ */ diff --git a/src/deps/brotli/common/transform.c b/src/deps/brotli/common/transform.c deleted file mode 100644 index 49455fc496f02..0000000000000 --- a/src/deps/brotli/common/transform.c +++ /dev/null @@ -1,291 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "transform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* RFC 7932 transforms string data */ -static const char kPrefixSuffix[217] = - "\1 \2, \10 of the \4 of \2s \1.\5 and \4 " -/* 0x _0 _2 __5 _E _3 _6 _8 _E */ - "in \1\"\4 to \2\">\1\n\2. \1]\5 for \3 a \6 " -/* 2x _3_ _5 _A_ _D_ _F _2 _4 _A _E */ - "that \1\'\6 with \6 from \4 by \1(\6. T" -/* 4x _5_ _7 _E _5 _A _C */ - "he \4 on \4 as \4 is \4ing \2\n\t\1:\3ed " -/* 6x _3 _8 _D _2 _7_ _ _A _C */ - "\2=\"\4 at \3ly \1,\2=\'\5.com/\7. This \5" -/* 8x _0 _ _3 _8 _C _E _ _1 _7 _F */ - " not \3er \3al \4ful \4ive \5less \4es" -/* Ax _5 _9 _D _2 _7 _D */ - "t \4ize \2\xc2\xa0\4ous \5 the \2e "; /* \0 - implicit trailing zero. */ -/* Cx _2 _7___ ___ _A _F _5 _8 */ - -static const uint16_t kPrefixSuffixMap[50] = { - 0x00, 0x02, 0x05, 0x0E, 0x13, 0x16, 0x18, 0x1E, 0x23, 0x25, - 0x2A, 0x2D, 0x2F, 0x32, 0x34, 0x3A, 0x3E, 0x45, 0x47, 0x4E, - 0x55, 0x5A, 0x5C, 0x63, 0x68, 0x6D, 0x72, 0x77, 0x7A, 0x7C, - 0x80, 0x83, 0x88, 0x8C, 0x8E, 0x91, 0x97, 0x9F, 0xA5, 0xA9, - 0xAD, 0xB2, 0xB7, 0xBD, 0xC2, 0xC7, 0xCA, 0xCF, 0xD5, 0xD8 -}; - -/* RFC 7932 transforms */ -static const uint8_t kTransformsData[] = { - 49, BROTLI_TRANSFORM_IDENTITY, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 0, - 0, BROTLI_TRANSFORM_IDENTITY, 0, - 49, BROTLI_TRANSFORM_OMIT_FIRST_1, 49, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0, - 49, BROTLI_TRANSFORM_IDENTITY, 47, - 0, BROTLI_TRANSFORM_IDENTITY, 49, - 4, BROTLI_TRANSFORM_IDENTITY, 0, - 49, BROTLI_TRANSFORM_IDENTITY, 3, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 6, - 49, BROTLI_TRANSFORM_OMIT_FIRST_2, 49, - 49, BROTLI_TRANSFORM_OMIT_LAST_1, 49, - 1, BROTLI_TRANSFORM_IDENTITY, 0, - 49, BROTLI_TRANSFORM_IDENTITY, 1, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0, - 49, BROTLI_TRANSFORM_IDENTITY, 7, - 49, BROTLI_TRANSFORM_IDENTITY, 9, - 48, BROTLI_TRANSFORM_IDENTITY, 0, - 49, BROTLI_TRANSFORM_IDENTITY, 8, - 49, BROTLI_TRANSFORM_IDENTITY, 5, - 49, BROTLI_TRANSFORM_IDENTITY, 10, - 49, BROTLI_TRANSFORM_IDENTITY, 11, - 49, BROTLI_TRANSFORM_OMIT_LAST_3, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 13, - 49, BROTLI_TRANSFORM_IDENTITY, 14, - 49, BROTLI_TRANSFORM_OMIT_FIRST_3, 49, - 49, BROTLI_TRANSFORM_OMIT_LAST_2, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 15, - 49, BROTLI_TRANSFORM_IDENTITY, 16, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 12, - 5, BROTLI_TRANSFORM_IDENTITY, 49, - 0, BROTLI_TRANSFORM_IDENTITY, 1, - 49, BROTLI_TRANSFORM_OMIT_FIRST_4, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 18, - 49, BROTLI_TRANSFORM_IDENTITY, 17, - 49, BROTLI_TRANSFORM_IDENTITY, 19, - 49, BROTLI_TRANSFORM_IDENTITY, 20, - 49, BROTLI_TRANSFORM_OMIT_FIRST_5, 49, - 49, BROTLI_TRANSFORM_OMIT_FIRST_6, 49, - 47, BROTLI_TRANSFORM_IDENTITY, 49, - 49, BROTLI_TRANSFORM_OMIT_LAST_4, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 22, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 23, - 49, BROTLI_TRANSFORM_IDENTITY, 24, - 49, BROTLI_TRANSFORM_IDENTITY, 25, - 49, BROTLI_TRANSFORM_OMIT_LAST_7, 49, - 49, BROTLI_TRANSFORM_OMIT_LAST_1, 26, - 49, BROTLI_TRANSFORM_IDENTITY, 27, - 49, BROTLI_TRANSFORM_IDENTITY, 28, - 0, BROTLI_TRANSFORM_IDENTITY, 12, - 49, BROTLI_TRANSFORM_IDENTITY, 29, - 49, BROTLI_TRANSFORM_OMIT_FIRST_9, 49, - 49, BROTLI_TRANSFORM_OMIT_FIRST_7, 49, - 49, BROTLI_TRANSFORM_OMIT_LAST_6, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 21, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1, - 49, BROTLI_TRANSFORM_OMIT_LAST_8, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 31, - 49, BROTLI_TRANSFORM_IDENTITY, 32, - 47, BROTLI_TRANSFORM_IDENTITY, 3, - 49, BROTLI_TRANSFORM_OMIT_LAST_5, 49, - 49, BROTLI_TRANSFORM_OMIT_LAST_9, 49, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 8, - 5, BROTLI_TRANSFORM_IDENTITY, 21, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 0, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 10, - 49, BROTLI_TRANSFORM_IDENTITY, 30, - 0, BROTLI_TRANSFORM_IDENTITY, 5, - 35, BROTLI_TRANSFORM_IDENTITY, 49, - 47, BROTLI_TRANSFORM_IDENTITY, 2, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 17, - 49, BROTLI_TRANSFORM_IDENTITY, 36, - 49, BROTLI_TRANSFORM_IDENTITY, 33, - 5, BROTLI_TRANSFORM_IDENTITY, 0, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 21, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5, - 49, BROTLI_TRANSFORM_IDENTITY, 37, - 0, BROTLI_TRANSFORM_IDENTITY, 30, - 49, BROTLI_TRANSFORM_IDENTITY, 38, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 0, - 49, BROTLI_TRANSFORM_IDENTITY, 39, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 49, - 49, BROTLI_TRANSFORM_IDENTITY, 34, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 8, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12, - 0, BROTLI_TRANSFORM_IDENTITY, 21, - 49, BROTLI_TRANSFORM_IDENTITY, 40, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12, - 49, BROTLI_TRANSFORM_IDENTITY, 41, - 49, BROTLI_TRANSFORM_IDENTITY, 42, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 17, - 49, BROTLI_TRANSFORM_IDENTITY, 43, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 10, - 0, BROTLI_TRANSFORM_IDENTITY, 34, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33, - 49, BROTLI_TRANSFORM_IDENTITY, 44, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 5, - 45, BROTLI_TRANSFORM_IDENTITY, 49, - 0, BROTLI_TRANSFORM_IDENTITY, 33, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 30, - 49, BROTLI_TRANSFORM_IDENTITY, 46, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 1, - 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 30, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 1, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 33, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 21, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 12, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 5, - 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 34, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 12, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30, - 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 34, - 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34, -}; - -static const BrotliTransforms kBrotliTransforms = { - sizeof(kPrefixSuffix), - (const uint8_t*)kPrefixSuffix, - kPrefixSuffixMap, - sizeof(kTransformsData) / (3 * sizeof(kTransformsData[0])), - kTransformsData, - NULL, /* no extra parameters */ - {0, 12, 27, 23, 42, 63, 56, 48, 59, 64} -}; - -const BrotliTransforms* BrotliGetTransforms(void) { - return &kBrotliTransforms; -} - -static int ToUpperCase(uint8_t* p) { - if (p[0] < 0xC0) { - if (p[0] >= 'a' && p[0] <= 'z') { - p[0] ^= 32; - } - return 1; - } - /* An overly simplified uppercasing model for UTF-8. */ - if (p[0] < 0xE0) { - p[1] ^= 32; - return 2; - } - /* An arbitrary transform for three byte characters. */ - p[2] ^= 5; - return 3; -} - -static int Shift(uint8_t* word, int word_len, uint16_t parameter) { - /* Limited sign extension: scalar < (1 << 24). */ - uint32_t scalar = - (parameter & 0x7FFFu) + (0x1000000u - (parameter & 0x8000u)); - if (word[0] < 0x80) { - /* 1-byte rune / 0sssssss / 7 bit scalar (ASCII). */ - scalar += (uint32_t)word[0]; - word[0] = (uint8_t)(scalar & 0x7Fu); - return 1; - } else if (word[0] < 0xC0) { - /* Continuation / 10AAAAAA. */ - return 1; - } else if (word[0] < 0xE0) { - /* 2-byte rune / 110sssss AAssssss / 11 bit scalar. */ - if (word_len < 2) return 1; - scalar += (uint32_t)((word[1] & 0x3Fu) | ((word[0] & 0x1Fu) << 6u)); - word[0] = (uint8_t)(0xC0 | ((scalar >> 6u) & 0x1F)); - word[1] = (uint8_t)((word[1] & 0xC0) | (scalar & 0x3F)); - return 2; - } else if (word[0] < 0xF0) { - /* 3-byte rune / 1110ssss AAssssss BBssssss / 16 bit scalar. */ - if (word_len < 3) return word_len; - scalar += (uint32_t)((word[2] & 0x3Fu) | ((word[1] & 0x3Fu) << 6u) | - ((word[0] & 0x0Fu) << 12u)); - word[0] = (uint8_t)(0xE0 | ((scalar >> 12u) & 0x0F)); - word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 6u) & 0x3F)); - word[2] = (uint8_t)((word[2] & 0xC0) | (scalar & 0x3F)); - return 3; - } else if (word[0] < 0xF8) { - /* 4-byte rune / 11110sss AAssssss BBssssss CCssssss / 21 bit scalar. */ - if (word_len < 4) return word_len; - scalar += (uint32_t)((word[3] & 0x3Fu) | ((word[2] & 0x3Fu) << 6u) | - ((word[1] & 0x3Fu) << 12u) | ((word[0] & 0x07u) << 18u)); - word[0] = (uint8_t)(0xF0 | ((scalar >> 18u) & 0x07)); - word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 12u) & 0x3F)); - word[2] = (uint8_t)((word[2] & 0xC0) | ((scalar >> 6u) & 0x3F)); - word[3] = (uint8_t)((word[3] & 0xC0) | (scalar & 0x3F)); - return 4; - } - return 1; -} - -int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len, - const BrotliTransforms* transforms, int transform_idx) { - int idx = 0; - const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, transform_idx); - uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, transform_idx); - const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, transform_idx); - { - int prefix_len = *prefix++; - while (prefix_len--) { dst[idx++] = *prefix++; } - } - { - const int t = type; - int i = 0; - if (t <= BROTLI_TRANSFORM_OMIT_LAST_9) { - len -= t; - } else if (t >= BROTLI_TRANSFORM_OMIT_FIRST_1 - && t <= BROTLI_TRANSFORM_OMIT_FIRST_9) { - int skip = t - (BROTLI_TRANSFORM_OMIT_FIRST_1 - 1); - word += skip; - len -= skip; - } - while (i < len) { dst[idx++] = word[i++]; } - if (t == BROTLI_TRANSFORM_UPPERCASE_FIRST) { - ToUpperCase(&dst[idx - len]); - } else if (t == BROTLI_TRANSFORM_UPPERCASE_ALL) { - uint8_t* uppercase = &dst[idx - len]; - while (len > 0) { - int step = ToUpperCase(uppercase); - uppercase += step; - len -= step; - } - } else if (t == BROTLI_TRANSFORM_SHIFT_FIRST) { - uint16_t param = (uint16_t)(transforms->params[transform_idx * 2] - + (transforms->params[transform_idx * 2 + 1] << 8u)); - Shift(&dst[idx - len], len, param); - } else if (t == BROTLI_TRANSFORM_SHIFT_ALL) { - uint16_t param = (uint16_t)(transforms->params[transform_idx * 2] - + (transforms->params[transform_idx * 2 + 1] << 8u)); - uint8_t* shift = &dst[idx - len]; - while (len > 0) { - int step = Shift(shift, len, param); - shift += step; - len -= step; - } - } - } - { - int suffix_len = *suffix++; - while (suffix_len--) { dst[idx++] = *suffix++; } - return idx; - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/common/transform.h b/src/deps/brotli/common/transform.h deleted file mode 100644 index b6f86cc7d5ad2..0000000000000 --- a/src/deps/brotli/common/transform.h +++ /dev/null @@ -1,85 +0,0 @@ -/* transforms is a part of ABI, but not API. - - It means that there are some functions that are supposed to be in "common" - library, but header itself is not placed into include/brotli. This way, - aforementioned functions will be available only to brotli internals. - */ - -#ifndef BROTLI_COMMON_TRANSFORM_H_ -#define BROTLI_COMMON_TRANSFORM_H_ - -#include -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -enum BrotliWordTransformType { - BROTLI_TRANSFORM_IDENTITY = 0, - BROTLI_TRANSFORM_OMIT_LAST_1 = 1, - BROTLI_TRANSFORM_OMIT_LAST_2 = 2, - BROTLI_TRANSFORM_OMIT_LAST_3 = 3, - BROTLI_TRANSFORM_OMIT_LAST_4 = 4, - BROTLI_TRANSFORM_OMIT_LAST_5 = 5, - BROTLI_TRANSFORM_OMIT_LAST_6 = 6, - BROTLI_TRANSFORM_OMIT_LAST_7 = 7, - BROTLI_TRANSFORM_OMIT_LAST_8 = 8, - BROTLI_TRANSFORM_OMIT_LAST_9 = 9, - BROTLI_TRANSFORM_UPPERCASE_FIRST = 10, - BROTLI_TRANSFORM_UPPERCASE_ALL = 11, - BROTLI_TRANSFORM_OMIT_FIRST_1 = 12, - BROTLI_TRANSFORM_OMIT_FIRST_2 = 13, - BROTLI_TRANSFORM_OMIT_FIRST_3 = 14, - BROTLI_TRANSFORM_OMIT_FIRST_4 = 15, - BROTLI_TRANSFORM_OMIT_FIRST_5 = 16, - BROTLI_TRANSFORM_OMIT_FIRST_6 = 17, - BROTLI_TRANSFORM_OMIT_FIRST_7 = 18, - BROTLI_TRANSFORM_OMIT_FIRST_8 = 19, - BROTLI_TRANSFORM_OMIT_FIRST_9 = 20, - BROTLI_TRANSFORM_SHIFT_FIRST = 21, - BROTLI_TRANSFORM_SHIFT_ALL = 22, - BROTLI_NUM_TRANSFORM_TYPES /* Counts transforms, not a transform itself. */ -}; - -#define BROTLI_TRANSFORMS_MAX_CUT_OFF BROTLI_TRANSFORM_OMIT_LAST_9 - -typedef struct BrotliTransforms { - uint16_t prefix_suffix_size; - /* Last character must be null, so prefix_suffix_size must be at least 1. */ - const uint8_t* prefix_suffix; - const uint16_t* prefix_suffix_map; - uint32_t num_transforms; - /* Each entry is a [prefix_id, transform, suffix_id] triplet. */ - const uint8_t* transforms; - /* Shift for BROTLI_TRANSFORM_SHIFT_FIRST and BROTLI_TRANSFORM_SHIFT_ALL, - must be NULL if and only if no such transforms are present. */ - const uint8_t* params; - /* Indices of transforms like ["", BROTLI_TRANSFORM_OMIT_LAST_#, ""]. - 0-th element corresponds to ["", BROTLI_TRANSFORM_IDENTITY, ""]. - -1, if cut-off transform does not exist. */ - int16_t cutOffTransforms[BROTLI_TRANSFORMS_MAX_CUT_OFF + 1]; -} BrotliTransforms; - -/* T is BrotliTransforms*; result is uint8_t. */ -#define BROTLI_TRANSFORM_PREFIX_ID(T, I) ((T)->transforms[((I) * 3) + 0]) -#define BROTLI_TRANSFORM_TYPE(T, I) ((T)->transforms[((I) * 3) + 1]) -#define BROTLI_TRANSFORM_SUFFIX_ID(T, I) ((T)->transforms[((I) * 3) + 2]) - -/* T is BrotliTransforms*; result is const uint8_t*. */ -#define BROTLI_TRANSFORM_PREFIX(T, I) (&(T)->prefix_suffix[ \ - (T)->prefix_suffix_map[BROTLI_TRANSFORM_PREFIX_ID(T, I)]]) -#define BROTLI_TRANSFORM_SUFFIX(T, I) (&(T)->prefix_suffix[ \ - (T)->prefix_suffix_map[BROTLI_TRANSFORM_SUFFIX_ID(T, I)]]) - -BROTLI_COMMON_API const BrotliTransforms* BrotliGetTransforms(void); - -BROTLI_COMMON_API int BrotliTransformDictionaryWord( - uint8_t* dst, const uint8_t* word, int len, - const BrotliTransforms* transforms, int transform_idx); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_COMMON_TRANSFORM_H_ */ diff --git a/src/deps/brotli/common/version.h b/src/deps/brotli/common/version.h deleted file mode 100644 index 8098040f646bc..0000000000000 --- a/src/deps/brotli/common/version.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Version definition. */ - -#ifndef BROTLI_COMMON_VERSION_H_ -#define BROTLI_COMMON_VERSION_H_ - -/* Compose 3 components into a single number. In a hexadecimal representation - B and C components occupy exactly 3 digits. */ -#define BROTLI_MAKE_HEX_VERSION(A, B, C) ((A << 24) | (B << 12) | C) - -/* Those macros should only be used when library is compiled together with - the client. If library is dynamically linked, use BrotliDecoderVersion and - BrotliEncoderVersion methods. */ - -#define BROTLI_VERSION_MAJOR 1 -#define BROTLI_VERSION_MINOR 1 -#define BROTLI_VERSION_PATCH 0 - -#define BROTLI_VERSION BROTLI_MAKE_HEX_VERSION( \ - BROTLI_VERSION_MAJOR, BROTLI_VERSION_MINOR, BROTLI_VERSION_PATCH) - -/* This macro is used by build system to produce Libtool-friendly soname. See - https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html - Version evolution rules: - - interfaces added (or change is compatible) -> current+1:0:age+1 - - interfaces removed (or changed is incompatible) -> current+1:0:0 - - interfaces not changed -> current:revision+1:age - */ - -#define BROTLI_ABI_CURRENT 2 -#define BROTLI_ABI_REVISION 0 -#define BROTLI_ABI_AGE 1 - -#if BROTLI_VERSION_MAJOR != (BROTLI_ABI_CURRENT - BROTLI_ABI_AGE) -#error ABI/API version inconsistency -#endif - -#if BROTLI_VERSION_MINOR != BROTLI_ABI_AGE -#error ABI/API version inconsistency -#endif - -#if BROTLI_VERSION_PATCH != BROTLI_ABI_REVISION -#error ABI/API version inconsistency -#endif - -#endif /* BROTLI_COMMON_VERSION_H_ */ diff --git a/src/deps/brotli/dec/bit_reader.c b/src/deps/brotli/dec/bit_reader.c deleted file mode 100644 index 35101ddc1a342..0000000000000 --- a/src/deps/brotli/dec/bit_reader.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Bit reading helpers */ - -#include "bit_reader.h" - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -const brotli_reg_t kBrotliBitMask[33] = { 0x00000000, - 0x00000001, 0x00000003, 0x00000007, 0x0000000F, - 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, - 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, - 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, - 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, - 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, - 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, - 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF -}; - -void BrotliInitBitReader(BrotliBitReader* const br) { - br->val_ = 0; - br->bit_pos_ = 0; -} - -BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br) { - size_t aligned_read_mask = (sizeof(br->val_) >> 1) - 1; - /* Fixing alignment after unaligned BrotliFillWindow would result accumulator - overflow. If unalignment is caused by BrotliSafeReadBits, then there is - enough space in accumulator to fix alignment. */ - if (BROTLI_UNALIGNED_READ_FAST) { - aligned_read_mask = 0; - } - if (BrotliGetAvailableBits(br) == 0) { - br->val_ = 0; - if (!BrotliPullByte(br)) { - return BROTLI_FALSE; - } - } - - while ((((size_t)br->next_in) & aligned_read_mask) != 0) { - if (!BrotliPullByte(br)) { - /* If we consumed all the input, we don't care about the alignment. */ - return BROTLI_TRUE; - } - } - return BROTLI_TRUE; -} - -BROTLI_BOOL BrotliSafeReadBits32Slow(BrotliBitReader* const br, - brotli_reg_t n_bits, brotli_reg_t* val) { - brotli_reg_t low_val; - brotli_reg_t high_val; - BrotliBitReaderState memento; - BROTLI_DCHECK(n_bits <= 32); - BROTLI_DCHECK(n_bits > 24); - BrotliBitReaderSaveState(br, &memento); - if (!BrotliSafeReadBits(br, 16, &low_val) || - !BrotliSafeReadBits(br, n_bits - 16, &high_val)) { - BrotliBitReaderRestoreState(br, &memento); - return BROTLI_FALSE; - } - *val = low_val | (high_val << 16); - return BROTLI_TRUE; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/dec/bit_reader.h b/src/deps/brotli/dec/bit_reader.h deleted file mode 100644 index 930dc60f1d6da..0000000000000 --- a/src/deps/brotli/dec/bit_reader.h +++ /dev/null @@ -1,423 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Bit reading helpers */ - -#ifndef BROTLI_DEC_BIT_READER_H_ -#define BROTLI_DEC_BIT_READER_H_ - -#include /* memcpy */ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define BROTLI_SHORT_FILL_BIT_WINDOW_READ (sizeof(brotli_reg_t) >> 1) - -/* 162 bits + 7 bytes */ -#define BROTLI_FAST_INPUT_SLACK 28 - -BROTLI_INTERNAL extern const brotli_reg_t kBrotliBitMask[33]; - -static BROTLI_INLINE brotli_reg_t BitMask(brotli_reg_t n) { - if (BROTLI_IS_CONSTANT(n) || BROTLI_HAS_UBFX) { - /* Masking with this expression turns to a single - "Unsigned Bit Field Extract" UBFX instruction on ARM. */ - return ~(~((brotli_reg_t)0) << n); - } else { - return kBrotliBitMask[n]; - } -} - -typedef struct { - brotli_reg_t val_; /* pre-fetched bits */ - brotli_reg_t bit_pos_; /* current bit-reading position in val_ */ - const uint8_t* next_in; /* the byte we're reading from */ - const uint8_t* guard_in; /* position from which "fast-path" is prohibited */ - const uint8_t* last_in; /* == next_in + avail_in */ -} BrotliBitReader; - -typedef struct { - brotli_reg_t val_; - brotli_reg_t bit_pos_; - const uint8_t* next_in; - size_t avail_in; -} BrotliBitReaderState; - -/* Initializes the BrotliBitReader fields. */ -BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* br); - -/* Ensures that accumulator is not empty. - May consume up to sizeof(brotli_reg_t) - 1 bytes of input. - Returns BROTLI_FALSE if data is required but there is no input available. - For !BROTLI_UNALIGNED_READ_FAST this function also prepares bit reader for - aligned reading. */ -BROTLI_INTERNAL BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* br); - -/* Fallback for BrotliSafeReadBits32. Extracted as noninlined method to unburden - the main code-path. Never called for RFC brotli streams, required only for - "large-window" mode and other extensions. */ -BROTLI_INTERNAL BROTLI_NOINLINE BROTLI_BOOL BrotliSafeReadBits32Slow( - BrotliBitReader* br, brotli_reg_t n_bits, brotli_reg_t* val); - -static BROTLI_INLINE size_t -BrotliBitReaderGetAvailIn(BrotliBitReader* const br) { - return (size_t)(br->last_in - br->next_in); -} - -static BROTLI_INLINE void BrotliBitReaderSaveState( - BrotliBitReader* const from, BrotliBitReaderState* to) { - to->val_ = from->val_; - to->bit_pos_ = from->bit_pos_; - to->next_in = from->next_in; - to->avail_in = BrotliBitReaderGetAvailIn(from); -} - -static BROTLI_INLINE void BrotliBitReaderSetInput( - BrotliBitReader* const br, const uint8_t* next_in, size_t avail_in) { - br->next_in = next_in; - br->last_in = (avail_in == 0) ? next_in : (next_in + avail_in); - if (avail_in + 1 > BROTLI_FAST_INPUT_SLACK) { - br->guard_in = next_in + (avail_in + 1 - BROTLI_FAST_INPUT_SLACK); - } else { - br->guard_in = next_in; - } -} - -static BROTLI_INLINE void BrotliBitReaderRestoreState( - BrotliBitReader* const to, BrotliBitReaderState* from) { - to->val_ = from->val_; - to->bit_pos_ = from->bit_pos_; - to->next_in = from->next_in; - BrotliBitReaderSetInput(to, from->next_in, from->avail_in); -} - -static BROTLI_INLINE brotli_reg_t BrotliGetAvailableBits( - const BrotliBitReader* br) { - return br->bit_pos_; -} - -/* Returns amount of unread bytes the bit reader still has buffered from the - BrotliInput, including whole bytes in br->val_. Result is capped with - maximal ring-buffer size (larger number won't be utilized anyway). */ -static BROTLI_INLINE size_t BrotliGetRemainingBytes(BrotliBitReader* br) { - static const size_t kCap = (size_t)1 << BROTLI_LARGE_MAX_WBITS; - size_t avail_in = BrotliBitReaderGetAvailIn(br); - if (avail_in > kCap) return kCap; - return avail_in + (BrotliGetAvailableBits(br) >> 3); -} - -/* Checks if there is at least |num| bytes left in the input ring-buffer - (excluding the bits remaining in br->val_). */ -static BROTLI_INLINE BROTLI_BOOL BrotliCheckInputAmount( - BrotliBitReader* const br) { - return TO_BROTLI_BOOL(br->next_in < br->guard_in); -} - -/* Load more bits into accumulator. */ -static BROTLI_INLINE brotli_reg_t BrotliBitReaderLoadBits(brotli_reg_t val, - brotli_reg_t new_bits, - brotli_reg_t count, - brotli_reg_t offset) { - BROTLI_DCHECK( - !((val >> offset) & ~new_bits & ~(~((brotli_reg_t)0) << count))); - (void)count; - return val | (new_bits << offset); -} - -/* Guarantees that there are at least |n_bits| + 1 bits in accumulator. - Precondition: accumulator contains at least 1 bit. - |n_bits| should be in the range [1..24] for regular build. For portable - non-64-bit little-endian build only 16 bits are safe to request. */ -static BROTLI_INLINE void BrotliFillBitWindow( - BrotliBitReader* const br, brotli_reg_t n_bits) { -#if (BROTLI_64_BITS) - if (BROTLI_UNALIGNED_READ_FAST && BROTLI_IS_CONSTANT(n_bits) && - (n_bits <= 8)) { - brotli_reg_t bit_pos = br->bit_pos_; - if (bit_pos <= 8) { - br->val_ = BrotliBitReaderLoadBits(br->val_, - BROTLI_UNALIGNED_LOAD64LE(br->next_in), 56, bit_pos); - br->bit_pos_ = bit_pos + 56; - br->next_in += 7; - } - } else if (BROTLI_UNALIGNED_READ_FAST && BROTLI_IS_CONSTANT(n_bits) && - (n_bits <= 16)) { - brotli_reg_t bit_pos = br->bit_pos_; - if (bit_pos <= 16) { - br->val_ = BrotliBitReaderLoadBits(br->val_, - BROTLI_UNALIGNED_LOAD64LE(br->next_in), 48, bit_pos); - br->bit_pos_ = bit_pos + 48; - br->next_in += 6; - } - } else { - brotli_reg_t bit_pos = br->bit_pos_; - if (bit_pos <= 32) { - br->val_ = BrotliBitReaderLoadBits(br->val_, - (uint64_t)BROTLI_UNALIGNED_LOAD32LE(br->next_in), 32, bit_pos); - br->bit_pos_ = bit_pos + 32; - br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ; - } - } -#else - if (BROTLI_UNALIGNED_READ_FAST && BROTLI_IS_CONSTANT(n_bits) && - (n_bits <= 8)) { - brotli_reg_t bit_pos = br->bit_pos_; - if (bit_pos <= 8) { - br->val_ = BrotliBitReaderLoadBits(br->val_, - BROTLI_UNALIGNED_LOAD32LE(br->next_in), 24, bit_pos); - br->bit_pos_ = bit_pos + 24; - br->next_in += 3; - } - } else { - brotli_reg_t bit_pos = br->bit_pos_; - if (bit_pos <= 16) { - br->val_ = BrotliBitReaderLoadBits(br->val_, - (uint32_t)BROTLI_UNALIGNED_LOAD16LE(br->next_in), 16, bit_pos); - br->bit_pos_ = bit_pos + 16; - br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ; - } - } -#endif -} - -/* Mostly like BrotliFillBitWindow, but guarantees only 16 bits and reads no - more than BROTLI_SHORT_FILL_BIT_WINDOW_READ bytes of input. */ -static BROTLI_INLINE void BrotliFillBitWindow16(BrotliBitReader* const br) { - BrotliFillBitWindow(br, 17); -} - -/* Tries to pull one byte of input to accumulator. - Returns BROTLI_FALSE if there is no input available. */ -static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) { - if (br->next_in == br->last_in) { - return BROTLI_FALSE; - } - br->val_ = BrotliBitReaderLoadBits(br->val_, - (brotli_reg_t)*br->next_in, 8, br->bit_pos_); - br->bit_pos_ += 8; - ++br->next_in; - return BROTLI_TRUE; -} - -/* Returns currently available bits. - The number of valid bits could be calculated by BrotliGetAvailableBits. */ -static BROTLI_INLINE brotli_reg_t BrotliGetBitsUnmasked( - BrotliBitReader* const br) { - return br->val_; -} - -/* Like BrotliGetBits, but does not mask the result. - The result contains at least 16 valid bits. */ -static BROTLI_INLINE brotli_reg_t BrotliGet16BitsUnmasked( - BrotliBitReader* const br) { - BrotliFillBitWindow(br, 16); - return (brotli_reg_t)BrotliGetBitsUnmasked(br); -} - -/* Returns the specified number of bits from |br| without advancing bit - position. */ -static BROTLI_INLINE brotli_reg_t BrotliGetBits( - BrotliBitReader* const br, brotli_reg_t n_bits) { - BrotliFillBitWindow(br, n_bits); - return BrotliGetBitsUnmasked(br) & BitMask(n_bits); -} - -/* Tries to peek the specified amount of bits. Returns BROTLI_FALSE, if there - is not enough input. */ -static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits( - BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { - while (BrotliGetAvailableBits(br) < n_bits) { - if (!BrotliPullByte(br)) { - return BROTLI_FALSE; - } - } - *val = BrotliGetBitsUnmasked(br) & BitMask(n_bits); - return BROTLI_TRUE; -} - -/* Advances the bit pos by |n_bits|. */ -static BROTLI_INLINE void BrotliDropBits( - BrotliBitReader* const br, brotli_reg_t n_bits) { - br->bit_pos_ -= n_bits; - br->val_ >>= n_bits; -} - -/* Make sure that there are no spectre bits in accumulator. - This is important for the cases when some bytes are skipped - (i.e. never placed into accumulator). */ -static BROTLI_INLINE void BrotliBitReaderNormalize(BrotliBitReader* br) { - /* Actually, it is enough to normalize when br->bit_pos_ == 0 */ - if (br->bit_pos_ < (sizeof(brotli_reg_t) << 3u)) { - br->val_ &= (((brotli_reg_t)1) << br->bit_pos_) - 1; - } -} - -static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) { - brotli_reg_t unused_bytes = BrotliGetAvailableBits(br) >> 3; - brotli_reg_t unused_bits = unused_bytes << 3; - br->next_in = - (unused_bytes == 0) ? br->next_in : (br->next_in - unused_bytes); - br->bit_pos_ -= unused_bits; - BrotliBitReaderNormalize(br); -} - -/* Reads the specified number of bits from |br| and advances the bit pos. - Precondition: accumulator MUST contain at least |n_bits|. */ -static BROTLI_INLINE void BrotliTakeBits(BrotliBitReader* const br, - brotli_reg_t n_bits, - brotli_reg_t* val) { - *val = BrotliGetBitsUnmasked(br) & BitMask(n_bits); - BROTLI_LOG(("[BrotliTakeBits] %d %d %d val: %6x\n", - (int)BrotliBitReaderGetAvailIn(br), (int)br->bit_pos_, - (int)n_bits, (int)*val)); - BrotliDropBits(br, n_bits); -} - -/* Reads the specified number of bits from |br| and advances the bit pos. - Assumes that there is enough input to perform BrotliFillBitWindow. - Up to 24 bits are allowed to be requested from this method. */ -static BROTLI_INLINE brotli_reg_t BrotliReadBits24( - BrotliBitReader* const br, brotli_reg_t n_bits) { - BROTLI_DCHECK(n_bits <= 24); - if (BROTLI_64_BITS || (n_bits <= 16)) { - brotli_reg_t val; - BrotliFillBitWindow(br, n_bits); - BrotliTakeBits(br, n_bits, &val); - return val; - } else { - brotli_reg_t low_val; - brotli_reg_t high_val; - BrotliFillBitWindow(br, 16); - BrotliTakeBits(br, 16, &low_val); - BrotliFillBitWindow(br, 8); - BrotliTakeBits(br, n_bits - 16, &high_val); - return low_val | (high_val << 16); - } -} - -/* Same as BrotliReadBits24, but allows reading up to 32 bits. */ -static BROTLI_INLINE brotli_reg_t BrotliReadBits32( - BrotliBitReader* const br, brotli_reg_t n_bits) { - BROTLI_DCHECK(n_bits <= 32); - if (BROTLI_64_BITS || (n_bits <= 16)) { - brotli_reg_t val; - BrotliFillBitWindow(br, n_bits); - BrotliTakeBits(br, n_bits, &val); - return val; - } else { - brotli_reg_t low_val; - brotli_reg_t high_val; - BrotliFillBitWindow(br, 16); - BrotliTakeBits(br, 16, &low_val); - BrotliFillBitWindow(br, 16); - BrotliTakeBits(br, n_bits - 16, &high_val); - return low_val | (high_val << 16); - } -} - -/* Tries to read the specified amount of bits. Returns BROTLI_FALSE, if there - is not enough input. |n_bits| MUST be positive. - Up to 24 bits are allowed to be requested from this method. */ -static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits( - BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { - BROTLI_DCHECK(n_bits <= 24); - while (BrotliGetAvailableBits(br) < n_bits) { - if (!BrotliPullByte(br)) { - return BROTLI_FALSE; - } - } - BrotliTakeBits(br, n_bits, val); - return BROTLI_TRUE; -} - -/* Same as BrotliSafeReadBits, but allows reading up to 32 bits. */ -static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits32( - BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { - BROTLI_DCHECK(n_bits <= 32); - if (BROTLI_64_BITS || (n_bits <= 24)) { - while (BrotliGetAvailableBits(br) < n_bits) { - if (!BrotliPullByte(br)) { - return BROTLI_FALSE; - } - } - BrotliTakeBits(br, n_bits, val); - return BROTLI_TRUE; - } else { - return BrotliSafeReadBits32Slow(br, n_bits, val); - } -} - -/* Advances the bit reader position to the next byte boundary and verifies - that any skipped bits are set to zero. */ -static BROTLI_INLINE BROTLI_BOOL BrotliJumpToByteBoundary(BrotliBitReader* br) { - brotli_reg_t pad_bits_count = BrotliGetAvailableBits(br) & 0x7; - brotli_reg_t pad_bits = 0; - if (pad_bits_count != 0) { - BrotliTakeBits(br, pad_bits_count, &pad_bits); - } - BrotliBitReaderNormalize(br); - return TO_BROTLI_BOOL(pad_bits == 0); -} - -static BROTLI_INLINE void BrotliDropBytes(BrotliBitReader* br, size_t num) { - /* Check detour is legal: accumulator must to be empty. */ - BROTLI_DCHECK(br->bit_pos_ == 0); - BROTLI_DCHECK(br->val_ == 0); - br->next_in += num; -} - -/* Copies remaining input bytes stored in the bit reader to the output. Value - |num| may not be larger than BrotliGetRemainingBytes. The bit reader must be - warmed up again after this. */ -static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest, - BrotliBitReader* br, size_t num) { - while (BrotliGetAvailableBits(br) >= 8 && num > 0) { - *dest = (uint8_t)BrotliGetBitsUnmasked(br); - BrotliDropBits(br, 8); - ++dest; - --num; - } - BrotliBitReaderNormalize(br); - if (num > 0) { - memcpy(dest, br->next_in, num); - BrotliDropBytes(br, num); - } -} - -BROTLI_UNUSED_FUNCTION void BrotliBitReaderSuppressUnusedFunctions(void) { - BROTLI_UNUSED(&BrotliBitReaderSuppressUnusedFunctions); - - BROTLI_UNUSED(&BrotliBitReaderGetAvailIn); - BROTLI_UNUSED(&BrotliBitReaderLoadBits); - BROTLI_UNUSED(&BrotliBitReaderRestoreState); - BROTLI_UNUSED(&BrotliBitReaderSaveState); - BROTLI_UNUSED(&BrotliBitReaderSetInput); - BROTLI_UNUSED(&BrotliBitReaderUnload); - BROTLI_UNUSED(&BrotliCheckInputAmount); - BROTLI_UNUSED(&BrotliCopyBytes); - BROTLI_UNUSED(&BrotliFillBitWindow16); - BROTLI_UNUSED(&BrotliGet16BitsUnmasked); - BROTLI_UNUSED(&BrotliGetBits); - BROTLI_UNUSED(&BrotliGetRemainingBytes); - BROTLI_UNUSED(&BrotliJumpToByteBoundary); - BROTLI_UNUSED(&BrotliReadBits24); - BROTLI_UNUSED(&BrotliReadBits32); - BROTLI_UNUSED(&BrotliSafeGetBits); - BROTLI_UNUSED(&BrotliSafeReadBits); - BROTLI_UNUSED(&BrotliSafeReadBits32); -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_DEC_BIT_READER_H_ */ diff --git a/src/deps/brotli/dec/decode.c b/src/deps/brotli/dec/decode.c deleted file mode 100644 index 220c7e85c6331..0000000000000 --- a/src/deps/brotli/dec/decode.c +++ /dev/null @@ -1,2875 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include - -#include /* free, malloc */ -#include /* memcpy, memset */ - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "../common/shared_dictionary_internal.h" -#include "../common/transform.h" -#include "../common/version.h" -#include "bit_reader.h" -#include "huffman.h" -#include "prefix.h" -#include "state.h" - -#if defined(BROTLI_TARGET_NEON) -#include -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define BROTLI_FAILURE(CODE) (BROTLI_DUMP(), CODE) - -#define BROTLI_LOG_UINT(name) \ - BROTLI_LOG(("[%s] %s = %lu\n", __func__, #name, (unsigned long)(name))) -#define BROTLI_LOG_ARRAY_INDEX(array_name, idx) \ - BROTLI_LOG(("[%s] %s[%lu] = %lu\n", __func__, #array_name, \ - (unsigned long)(idx), (unsigned long)array_name[idx])) - -#define HUFFMAN_TABLE_BITS 8U -#define HUFFMAN_TABLE_MASK 0xFF - -/* We need the slack region for the following reasons: - - doing up to two 16-byte copies for fast backward copying - - inserting transformed dictionary word: - 255 prefix + 32 base + 255 suffix */ -static const brotli_reg_t kRingBufferWriteAheadSlack = 542; - -static const uint8_t kCodeLengthCodeOrder[BROTLI_CODE_LENGTH_CODES] = { - 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15, -}; - -/* Static prefix code for the complex code length code lengths. */ -static const uint8_t kCodeLengthPrefixLength[16] = { - 2, 2, 2, 3, 2, 2, 2, 4, 2, 2, 2, 3, 2, 2, 2, 4, -}; - -static const uint8_t kCodeLengthPrefixValue[16] = { - 0, 4, 3, 2, 0, 4, 3, 1, 0, 4, 3, 2, 0, 4, 3, 5, -}; - -BROTLI_BOOL BrotliDecoderSetParameter( - BrotliDecoderState* state, BrotliDecoderParameter p, uint32_t value) { - if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE; - switch (p) { - case BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: - state->canny_ringbuffer_allocation = !!value ? 0 : 1; - return BROTLI_TRUE; - - case BROTLI_DECODER_PARAM_LARGE_WINDOW: - state->large_window = TO_BROTLI_BOOL(!!value); - return BROTLI_TRUE; - - default: return BROTLI_FALSE; - } -} - -BrotliDecoderState* BrotliDecoderCreateInstance( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - BrotliDecoderState* state = 0; - if (!alloc_func && !free_func) { - state = (BrotliDecoderState*)malloc(sizeof(BrotliDecoderState)); - } else if (alloc_func && free_func) { - state = (BrotliDecoderState*)alloc_func(opaque, sizeof(BrotliDecoderState)); - } - if (state == 0) { - BROTLI_DUMP(); - return 0; - } - if (!BrotliDecoderStateInit(state, alloc_func, free_func, opaque)) { - BROTLI_DUMP(); - if (!alloc_func && !free_func) { - free(state); - } else if (alloc_func && free_func) { - free_func(opaque, state); - } - return 0; - } - return state; -} - -/* Deinitializes and frees BrotliDecoderState instance. */ -void BrotliDecoderDestroyInstance(BrotliDecoderState* state) { - if (!state) { - return; - } else { - brotli_free_func free_func = state->free_func; - void* opaque = state->memory_manager_opaque; - BrotliDecoderStateCleanup(state); - free_func(opaque, state); - } -} - -/* Saves error code and converts it to BrotliDecoderResult. */ -static BROTLI_NOINLINE BrotliDecoderResult SaveErrorCode( - BrotliDecoderState* s, BrotliDecoderErrorCode e, size_t consumed_input) { - s->error_code = (int)e; - s->used_input += consumed_input; - if ((s->buffer_length != 0) && (s->br.next_in == s->br.last_in)) { - /* If internal buffer is depleted at last, reset it. */ - s->buffer_length = 0; - } - switch (e) { - case BROTLI_DECODER_SUCCESS: - return BROTLI_DECODER_RESULT_SUCCESS; - - case BROTLI_DECODER_NEEDS_MORE_INPUT: - return BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; - - case BROTLI_DECODER_NEEDS_MORE_OUTPUT: - return BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; - - default: - return BROTLI_DECODER_RESULT_ERROR; - } -} - -/* Decodes WBITS by reading 1 - 7 bits, or 0x11 for "Large Window Brotli". - Precondition: bit-reader accumulator has at least 8 bits. */ -static BrotliDecoderErrorCode DecodeWindowBits(BrotliDecoderState* s, - BrotliBitReader* br) { - brotli_reg_t n; - BROTLI_BOOL large_window = s->large_window; - s->large_window = BROTLI_FALSE; - BrotliTakeBits(br, 1, &n); - if (n == 0) { - s->window_bits = 16; - return BROTLI_DECODER_SUCCESS; - } - BrotliTakeBits(br, 3, &n); - if (n != 0) { - s->window_bits = (17u + n) & 63u; - return BROTLI_DECODER_SUCCESS; - } - BrotliTakeBits(br, 3, &n); - if (n == 1) { - if (large_window) { - BrotliTakeBits(br, 1, &n); - if (n == 1) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS); - } - s->large_window = BROTLI_TRUE; - return BROTLI_DECODER_SUCCESS; - } else { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS); - } - } - if (n != 0) { - s->window_bits = (8u + n) & 63u; - return BROTLI_DECODER_SUCCESS; - } - s->window_bits = 17; - return BROTLI_DECODER_SUCCESS; -} - -static BROTLI_INLINE void memmove16(uint8_t* dst, uint8_t* src) { -#if defined(BROTLI_TARGET_NEON) - vst1q_u8(dst, vld1q_u8(src)); -#else - uint32_t buffer[4]; - memcpy(buffer, src, 16); - memcpy(dst, buffer, 16); -#endif -} - -/* Decodes a number in the range [0..255], by reading 1 - 11 bits. */ -static BROTLI_NOINLINE BrotliDecoderErrorCode DecodeVarLenUint8( - BrotliDecoderState* s, BrotliBitReader* br, brotli_reg_t* value) { - brotli_reg_t bits; - switch (s->substate_decode_uint8) { - case BROTLI_STATE_DECODE_UINT8_NONE: - if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, 1, &bits))) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (bits == 0) { - *value = 0; - return BROTLI_DECODER_SUCCESS; - } - /* Fall through. */ - - case BROTLI_STATE_DECODE_UINT8_SHORT: - if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, 3, &bits))) { - s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_SHORT; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (bits == 0) { - *value = 1; - s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE; - return BROTLI_DECODER_SUCCESS; - } - /* Use output value as a temporary storage. It MUST be persisted. */ - *value = bits; - /* Fall through. */ - - case BROTLI_STATE_DECODE_UINT8_LONG: - if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, *value, &bits))) { - s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_LONG; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - *value = (1U << *value) + bits; - s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE; - return BROTLI_DECODER_SUCCESS; - - default: - return - BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ - } -} - -/* Decodes a metablock length and flags by reading 2 - 31 bits. */ -static BrotliDecoderErrorCode BROTLI_NOINLINE DecodeMetaBlockLength( - BrotliDecoderState* s, BrotliBitReader* br) { - brotli_reg_t bits; - int i; - for (;;) { - switch (s->substate_metablock_header) { - case BROTLI_STATE_METABLOCK_HEADER_NONE: - if (!BrotliSafeReadBits(br, 1, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - s->is_last_metablock = bits ? 1 : 0; - s->meta_block_remaining_len = 0; - s->is_uncompressed = 0; - s->is_metadata = 0; - if (!s->is_last_metablock) { - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NIBBLES; - break; - } - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_EMPTY; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER_EMPTY: - if (!BrotliSafeReadBits(br, 1, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (bits) { - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; - return BROTLI_DECODER_SUCCESS; - } - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NIBBLES; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER_NIBBLES: - if (!BrotliSafeReadBits(br, 2, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - s->size_nibbles = (uint8_t)(bits + 4); - s->loop_counter = 0; - if (bits == 3) { - s->is_metadata = 1; - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_RESERVED; - break; - } - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_SIZE; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER_SIZE: - i = s->loop_counter; - for (; i < (int)s->size_nibbles; ++i) { - if (!BrotliSafeReadBits(br, 4, &bits)) { - s->loop_counter = i; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (i + 1 == (int)s->size_nibbles && s->size_nibbles > 4 && - bits == 0) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE); - } - s->meta_block_remaining_len |= (int)(bits << (i * 4)); - } - s->substate_metablock_header = - BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED: - if (!s->is_last_metablock) { - if (!BrotliSafeReadBits(br, 1, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - s->is_uncompressed = bits ? 1 : 0; - } - ++s->meta_block_remaining_len; - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; - return BROTLI_DECODER_SUCCESS; - - case BROTLI_STATE_METABLOCK_HEADER_RESERVED: - if (!BrotliSafeReadBits(br, 1, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (bits != 0) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_RESERVED); - } - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_BYTES; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER_BYTES: - if (!BrotliSafeReadBits(br, 2, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (bits == 0) { - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; - return BROTLI_DECODER_SUCCESS; - } - s->size_nibbles = (uint8_t)bits; - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_METADATA; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER_METADATA: - i = s->loop_counter; - for (; i < (int)s->size_nibbles; ++i) { - if (!BrotliSafeReadBits(br, 8, &bits)) { - s->loop_counter = i; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (i + 1 == (int)s->size_nibbles && s->size_nibbles > 1 && - bits == 0) { - return BROTLI_FAILURE( - BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE); - } - s->meta_block_remaining_len |= (int)(bits << (i * 8)); - } - ++s->meta_block_remaining_len; - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; - return BROTLI_DECODER_SUCCESS; - - default: - return - BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ - } - } -} - -/* Decodes the Huffman code. - This method doesn't read data from the bit reader, BUT drops the amount of - bits that correspond to the decoded symbol. - bits MUST contain at least 15 (BROTLI_HUFFMAN_MAX_CODE_LENGTH) valid bits. */ -static BROTLI_INLINE brotli_reg_t DecodeSymbol(brotli_reg_t bits, - const HuffmanCode* table, - BrotliBitReader* br) { - BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); - BROTLI_HC_ADJUST_TABLE_INDEX(table, bits & HUFFMAN_TABLE_MASK); - if (BROTLI_HC_FAST_LOAD_BITS(table) > HUFFMAN_TABLE_BITS) { - brotli_reg_t nbits = BROTLI_HC_FAST_LOAD_BITS(table) - HUFFMAN_TABLE_BITS; - BrotliDropBits(br, HUFFMAN_TABLE_BITS); - BROTLI_HC_ADJUST_TABLE_INDEX(table, - BROTLI_HC_FAST_LOAD_VALUE(table) + - ((bits >> HUFFMAN_TABLE_BITS) & BitMask(nbits))); - } - BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(table)); - return BROTLI_HC_FAST_LOAD_VALUE(table); -} - -/* Reads and decodes the next Huffman code from bit-stream. - This method peeks 16 bits of input and drops 0 - 15 of them. */ -static BROTLI_INLINE brotli_reg_t ReadSymbol(const HuffmanCode* table, - BrotliBitReader* br) { - return DecodeSymbol(BrotliGet16BitsUnmasked(br), table, br); -} - -/* Same as DecodeSymbol, but it is known that there is less than 15 bits of - input are currently available. */ -static BROTLI_NOINLINE BROTLI_BOOL SafeDecodeSymbol( - const HuffmanCode* table, BrotliBitReader* br, brotli_reg_t* result) { - brotli_reg_t val; - brotli_reg_t available_bits = BrotliGetAvailableBits(br); - BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); - if (available_bits == 0) { - if (BROTLI_HC_FAST_LOAD_BITS(table) == 0) { - *result = BROTLI_HC_FAST_LOAD_VALUE(table); - return BROTLI_TRUE; - } - return BROTLI_FALSE; /* No valid bits at all. */ - } - val = BrotliGetBitsUnmasked(br); - BROTLI_HC_ADJUST_TABLE_INDEX(table, val & HUFFMAN_TABLE_MASK); - if (BROTLI_HC_FAST_LOAD_BITS(table) <= HUFFMAN_TABLE_BITS) { - if (BROTLI_HC_FAST_LOAD_BITS(table) <= available_bits) { - BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(table)); - *result = BROTLI_HC_FAST_LOAD_VALUE(table); - return BROTLI_TRUE; - } else { - return BROTLI_FALSE; /* Not enough bits for the first level. */ - } - } - if (available_bits <= HUFFMAN_TABLE_BITS) { - return BROTLI_FALSE; /* Not enough bits to move to the second level. */ - } - - /* Speculatively drop HUFFMAN_TABLE_BITS. */ - val = (val & BitMask(BROTLI_HC_FAST_LOAD_BITS(table))) >> HUFFMAN_TABLE_BITS; - available_bits -= HUFFMAN_TABLE_BITS; - BROTLI_HC_ADJUST_TABLE_INDEX(table, BROTLI_HC_FAST_LOAD_VALUE(table) + val); - if (available_bits < BROTLI_HC_FAST_LOAD_BITS(table)) { - return BROTLI_FALSE; /* Not enough bits for the second level. */ - } - - BrotliDropBits(br, HUFFMAN_TABLE_BITS + BROTLI_HC_FAST_LOAD_BITS(table)); - *result = BROTLI_HC_FAST_LOAD_VALUE(table); - return BROTLI_TRUE; -} - -static BROTLI_INLINE BROTLI_BOOL SafeReadSymbol( - const HuffmanCode* table, BrotliBitReader* br, brotli_reg_t* result) { - brotli_reg_t val; - if (BROTLI_PREDICT_TRUE(BrotliSafeGetBits(br, 15, &val))) { - *result = DecodeSymbol(val, table, br); - return BROTLI_TRUE; - } - return SafeDecodeSymbol(table, br, result); -} - -/* Makes a look-up in first level Huffman table. Peeks 8 bits. */ -static BROTLI_INLINE void PreloadSymbol(int safe, - const HuffmanCode* table, - BrotliBitReader* br, - brotli_reg_t* bits, - brotli_reg_t* value) { - if (safe) { - return; - } - BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); - BROTLI_HC_ADJUST_TABLE_INDEX(table, BrotliGetBits(br, HUFFMAN_TABLE_BITS)); - *bits = BROTLI_HC_FAST_LOAD_BITS(table); - *value = BROTLI_HC_FAST_LOAD_VALUE(table); -} - -/* Decodes the next Huffman code using data prepared by PreloadSymbol. - Reads 0 - 15 bits. Also peeks 8 following bits. */ -static BROTLI_INLINE brotli_reg_t ReadPreloadedSymbol(const HuffmanCode* table, - BrotliBitReader* br, - brotli_reg_t* bits, - brotli_reg_t* value) { - brotli_reg_t result = *value; - if (BROTLI_PREDICT_FALSE(*bits > HUFFMAN_TABLE_BITS)) { - brotli_reg_t val = BrotliGet16BitsUnmasked(br); - const HuffmanCode* ext = table + (val & HUFFMAN_TABLE_MASK) + *value; - brotli_reg_t mask = BitMask((*bits - HUFFMAN_TABLE_BITS)); - BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(ext); - BrotliDropBits(br, HUFFMAN_TABLE_BITS); - BROTLI_HC_ADJUST_TABLE_INDEX(ext, (val >> HUFFMAN_TABLE_BITS) & mask); - BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(ext)); - result = BROTLI_HC_FAST_LOAD_VALUE(ext); - } else { - BrotliDropBits(br, *bits); - } - PreloadSymbol(0, table, br, bits, value); - return result; -} - -static BROTLI_INLINE brotli_reg_t Log2Floor(brotli_reg_t x) { - brotli_reg_t result = 0; - while (x) { - x >>= 1; - ++result; - } - return result; -} - -/* Reads (s->symbol + 1) symbols. - Totally 1..4 symbols are read, 1..11 bits each. - The list of symbols MUST NOT contain duplicates. */ -static BrotliDecoderErrorCode ReadSimpleHuffmanSymbols( - brotli_reg_t alphabet_size_max, brotli_reg_t alphabet_size_limit, - BrotliDecoderState* s) { - /* max_bits == 1..11; symbol == 0..3; 1..44 bits will be read. */ - BrotliBitReader* br = &s->br; - BrotliMetablockHeaderArena* h = &s->arena.header; - brotli_reg_t max_bits = Log2Floor(alphabet_size_max - 1); - brotli_reg_t i = h->sub_loop_counter; - brotli_reg_t num_symbols = h->symbol; - while (i <= num_symbols) { - brotli_reg_t v; - if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, max_bits, &v))) { - h->sub_loop_counter = i; - h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_READ; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (v >= alphabet_size_limit) { - return - BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET); - } - h->symbols_lists_array[i] = (uint16_t)v; - BROTLI_LOG_UINT(h->symbols_lists_array[i]); - ++i; - } - - for (i = 0; i < num_symbols; ++i) { - brotli_reg_t k = i + 1; - for (; k <= num_symbols; ++k) { - if (h->symbols_lists_array[i] == h->symbols_lists_array[k]) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME); - } - } - } - - return BROTLI_DECODER_SUCCESS; -} - -/* Process single decoded symbol code length: - A) reset the repeat variable - B) remember code length (if it is not 0) - C) extend corresponding index-chain - D) reduce the Huffman space - E) update the histogram */ -static BROTLI_INLINE void ProcessSingleCodeLength(brotli_reg_t code_len, - brotli_reg_t* symbol, brotli_reg_t* repeat, brotli_reg_t* space, - brotli_reg_t* prev_code_len, uint16_t* symbol_lists, - uint16_t* code_length_histo, int* next_symbol) { - *repeat = 0; - if (code_len != 0) { /* code_len == 1..15 */ - symbol_lists[next_symbol[code_len]] = (uint16_t)(*symbol); - next_symbol[code_len] = (int)(*symbol); - *prev_code_len = code_len; - *space -= 32768U >> code_len; - code_length_histo[code_len]++; - BROTLI_LOG(("[ReadHuffmanCode] code_length[%d] = %d\n", - (int)*symbol, (int)code_len)); - } - (*symbol)++; -} - -/* Process repeated symbol code length. - A) Check if it is the extension of previous repeat sequence; if the decoded - value is not BROTLI_REPEAT_PREVIOUS_CODE_LENGTH, then it is a new - symbol-skip - B) Update repeat variable - C) Check if operation is feasible (fits alphabet) - D) For each symbol do the same operations as in ProcessSingleCodeLength - - PRECONDITION: code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH or - code_len == BROTLI_REPEAT_ZERO_CODE_LENGTH */ -static BROTLI_INLINE void ProcessRepeatedCodeLength(brotli_reg_t code_len, - brotli_reg_t repeat_delta, brotli_reg_t alphabet_size, brotli_reg_t* symbol, - brotli_reg_t* repeat, brotli_reg_t* space, brotli_reg_t* prev_code_len, - brotli_reg_t* repeat_code_len, uint16_t* symbol_lists, - uint16_t* code_length_histo, int* next_symbol) { - brotli_reg_t old_repeat; - brotli_reg_t extra_bits = 3; /* for BROTLI_REPEAT_ZERO_CODE_LENGTH */ - brotli_reg_t new_len = 0; /* for BROTLI_REPEAT_ZERO_CODE_LENGTH */ - if (code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) { - new_len = *prev_code_len; - extra_bits = 2; - } - if (*repeat_code_len != new_len) { - *repeat = 0; - *repeat_code_len = new_len; - } - old_repeat = *repeat; - if (*repeat > 0) { - *repeat -= 2; - *repeat <<= extra_bits; - } - *repeat += repeat_delta + 3U; - repeat_delta = *repeat - old_repeat; - if (*symbol + repeat_delta > alphabet_size) { - BROTLI_DUMP(); - *symbol = alphabet_size; - *space = 0xFFFFF; - return; - } - BROTLI_LOG(("[ReadHuffmanCode] code_length[%d..%d] = %d\n", - (int)*symbol, (int)(*symbol + repeat_delta - 1), (int)*repeat_code_len)); - if (*repeat_code_len != 0) { - brotli_reg_t last = *symbol + repeat_delta; - int next = next_symbol[*repeat_code_len]; - do { - symbol_lists[next] = (uint16_t)*symbol; - next = (int)*symbol; - } while (++(*symbol) != last); - next_symbol[*repeat_code_len] = next; - *space -= repeat_delta << (15 - *repeat_code_len); - code_length_histo[*repeat_code_len] = - (uint16_t)(code_length_histo[*repeat_code_len] + repeat_delta); - } else { - *symbol += repeat_delta; - } -} - -/* Reads and decodes symbol codelengths. */ -static BrotliDecoderErrorCode ReadSymbolCodeLengths( - brotli_reg_t alphabet_size, BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - BrotliMetablockHeaderArena* h = &s->arena.header; - brotli_reg_t symbol = h->symbol; - brotli_reg_t repeat = h->repeat; - brotli_reg_t space = h->space; - brotli_reg_t prev_code_len = h->prev_code_len; - brotli_reg_t repeat_code_len = h->repeat_code_len; - uint16_t* symbol_lists = h->symbol_lists; - uint16_t* code_length_histo = h->code_length_histo; - int* next_symbol = h->next_symbol; - if (!BrotliWarmupBitReader(br)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - while (symbol < alphabet_size && space > 0) { - const HuffmanCode* p = h->table; - brotli_reg_t code_len; - BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(p); - if (!BrotliCheckInputAmount(br)) { - h->symbol = symbol; - h->repeat = repeat; - h->prev_code_len = prev_code_len; - h->repeat_code_len = repeat_code_len; - h->space = space; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - BrotliFillBitWindow16(br); - BROTLI_HC_ADJUST_TABLE_INDEX(p, BrotliGetBitsUnmasked(br) & - BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH)); - BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p)); /* Use 1..5 bits. */ - code_len = BROTLI_HC_FAST_LOAD_VALUE(p); /* code_len == 0..17 */ - if (code_len < BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) { - ProcessSingleCodeLength(code_len, &symbol, &repeat, &space, - &prev_code_len, symbol_lists, code_length_histo, next_symbol); - } else { /* code_len == 16..17, extra_bits == 2..3 */ - brotli_reg_t extra_bits = - (code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) ? 2 : 3; - brotli_reg_t repeat_delta = - BrotliGetBitsUnmasked(br) & BitMask(extra_bits); - BrotliDropBits(br, extra_bits); - ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size, - &symbol, &repeat, &space, &prev_code_len, &repeat_code_len, - symbol_lists, code_length_histo, next_symbol); - } - } - h->space = space; - return BROTLI_DECODER_SUCCESS; -} - -static BrotliDecoderErrorCode SafeReadSymbolCodeLengths( - brotli_reg_t alphabet_size, BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - BrotliMetablockHeaderArena* h = &s->arena.header; - BROTLI_BOOL get_byte = BROTLI_FALSE; - while (h->symbol < alphabet_size && h->space > 0) { - const HuffmanCode* p = h->table; - brotli_reg_t code_len; - brotli_reg_t available_bits; - brotli_reg_t bits = 0; - BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(p); - if (get_byte && !BrotliPullByte(br)) return BROTLI_DECODER_NEEDS_MORE_INPUT; - get_byte = BROTLI_FALSE; - available_bits = BrotliGetAvailableBits(br); - if (available_bits != 0) { - bits = (uint32_t)BrotliGetBitsUnmasked(br); - } - BROTLI_HC_ADJUST_TABLE_INDEX(p, - bits & BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH)); - if (BROTLI_HC_FAST_LOAD_BITS(p) > available_bits) { - get_byte = BROTLI_TRUE; - continue; - } - code_len = BROTLI_HC_FAST_LOAD_VALUE(p); /* code_len == 0..17 */ - if (code_len < BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) { - BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p)); - ProcessSingleCodeLength(code_len, &h->symbol, &h->repeat, &h->space, - &h->prev_code_len, h->symbol_lists, h->code_length_histo, - h->next_symbol); - } else { /* code_len == 16..17, extra_bits == 2..3 */ - brotli_reg_t extra_bits = code_len - 14U; - brotli_reg_t repeat_delta = (bits >> BROTLI_HC_FAST_LOAD_BITS(p)) & - BitMask(extra_bits); - if (available_bits < BROTLI_HC_FAST_LOAD_BITS(p) + extra_bits) { - get_byte = BROTLI_TRUE; - continue; - } - BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p) + extra_bits); - ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size, - &h->symbol, &h->repeat, &h->space, &h->prev_code_len, - &h->repeat_code_len, h->symbol_lists, h->code_length_histo, - h->next_symbol); - } - } - return BROTLI_DECODER_SUCCESS; -} - -/* Reads and decodes 15..18 codes using static prefix code. - Each code is 2..4 bits long. In total 30..72 bits are used. */ -static BrotliDecoderErrorCode ReadCodeLengthCodeLengths(BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - BrotliMetablockHeaderArena* h = &s->arena.header; - brotli_reg_t num_codes = h->repeat; - brotli_reg_t space = h->space; - brotli_reg_t i = h->sub_loop_counter; - for (; i < BROTLI_CODE_LENGTH_CODES; ++i) { - const uint8_t code_len_idx = kCodeLengthCodeOrder[i]; - brotli_reg_t ix; - brotli_reg_t v; - if (BROTLI_PREDICT_FALSE(!BrotliSafeGetBits(br, 4, &ix))) { - brotli_reg_t available_bits = BrotliGetAvailableBits(br); - if (available_bits != 0) { - ix = BrotliGetBitsUnmasked(br) & 0xF; - } else { - ix = 0; - } - if (kCodeLengthPrefixLength[ix] > available_bits) { - h->sub_loop_counter = i; - h->repeat = num_codes; - h->space = space; - h->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - } - v = kCodeLengthPrefixValue[ix]; - BrotliDropBits(br, kCodeLengthPrefixLength[ix]); - h->code_length_code_lengths[code_len_idx] = (uint8_t)v; - BROTLI_LOG_ARRAY_INDEX(h->code_length_code_lengths, code_len_idx); - if (v != 0) { - space = space - (32U >> v); - ++num_codes; - ++h->code_length_histo[v]; - if (space - 1U >= 32U) { - /* space is 0 or wrapped around. */ - break; - } - } - } - if (!(num_codes == 1 || space == 0)) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_CL_SPACE); - } - return BROTLI_DECODER_SUCCESS; -} - -/* Decodes the Huffman tables. - There are 2 scenarios: - A) Huffman code contains only few symbols (1..4). Those symbols are read - directly; their code lengths are defined by the number of symbols. - For this scenario 4 - 49 bits will be read. - - B) 2-phase decoding: - B.1) Small Huffman table is decoded; it is specified with code lengths - encoded with predefined entropy code. 32 - 74 bits are used. - B.2) Decoded table is used to decode code lengths of symbols in resulting - Huffman table. In worst case 3520 bits are read. */ -static BrotliDecoderErrorCode ReadHuffmanCode(brotli_reg_t alphabet_size_max, - brotli_reg_t alphabet_size_limit, - HuffmanCode* table, - brotli_reg_t* opt_table_size, - BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - BrotliMetablockHeaderArena* h = &s->arena.header; - /* State machine. */ - for (;;) { - switch (h->substate_huffman) { - case BROTLI_STATE_HUFFMAN_NONE: - if (!BrotliSafeReadBits(br, 2, &h->sub_loop_counter)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - BROTLI_LOG_UINT(h->sub_loop_counter); - /* The value is used as follows: - 1 for simple code; - 0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */ - if (h->sub_loop_counter != 1) { - h->space = 32; - h->repeat = 0; /* num_codes */ - memset(&h->code_length_histo[0], 0, sizeof(h->code_length_histo[0]) * - (BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1)); - memset(&h->code_length_code_lengths[0], 0, - sizeof(h->code_length_code_lengths)); - h->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX; - continue; - } - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_SIMPLE_SIZE: - /* Read symbols, codes & code lengths directly. */ - if (!BrotliSafeReadBits(br, 2, &h->symbol)) { /* num_symbols */ - h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_SIZE; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - h->sub_loop_counter = 0; - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_SIMPLE_READ: { - BrotliDecoderErrorCode result = - ReadSimpleHuffmanSymbols(alphabet_size_max, alphabet_size_limit, s); - if (result != BROTLI_DECODER_SUCCESS) { - return result; - } - } - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_SIMPLE_BUILD: { - brotli_reg_t table_size; - if (h->symbol == 3) { - brotli_reg_t bits; - if (!BrotliSafeReadBits(br, 1, &bits)) { - h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - h->symbol += bits; - } - BROTLI_LOG_UINT(h->symbol); - table_size = BrotliBuildSimpleHuffmanTable(table, HUFFMAN_TABLE_BITS, - h->symbols_lists_array, - (uint32_t)h->symbol); - if (opt_table_size) { - *opt_table_size = table_size; - } - h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE; - return BROTLI_DECODER_SUCCESS; - } - - /* Decode Huffman-coded code lengths. */ - case BROTLI_STATE_HUFFMAN_COMPLEX: { - brotli_reg_t i; - BrotliDecoderErrorCode result = ReadCodeLengthCodeLengths(s); - if (result != BROTLI_DECODER_SUCCESS) { - return result; - } - BrotliBuildCodeLengthsHuffmanTable(h->table, - h->code_length_code_lengths, - h->code_length_histo); - memset(&h->code_length_histo[0], 0, sizeof(h->code_length_histo)); - for (i = 0; i <= BROTLI_HUFFMAN_MAX_CODE_LENGTH; ++i) { - h->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); - h->symbol_lists[h->next_symbol[i]] = 0xFFFF; - } - - h->symbol = 0; - h->prev_code_len = BROTLI_INITIAL_REPEATED_CODE_LENGTH; - h->repeat = 0; - h->repeat_code_len = 0; - h->space = 32768; - h->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS; - } - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS: { - brotli_reg_t table_size; - BrotliDecoderErrorCode result = ReadSymbolCodeLengths( - alphabet_size_limit, s); - if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) { - result = SafeReadSymbolCodeLengths(alphabet_size_limit, s); - } - if (result != BROTLI_DECODER_SUCCESS) { - return result; - } - - if (h->space != 0) { - BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", (int)h->space)); - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE); - } - table_size = BrotliBuildHuffmanTable( - table, HUFFMAN_TABLE_BITS, h->symbol_lists, h->code_length_histo); - if (opt_table_size) { - *opt_table_size = table_size; - } - h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE; - return BROTLI_DECODER_SUCCESS; - } - - default: - return - BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ - } - } -} - -/* Decodes a block length by reading 3..39 bits. */ -static BROTLI_INLINE brotli_reg_t ReadBlockLength(const HuffmanCode* table, - BrotliBitReader* br) { - brotli_reg_t code; - brotli_reg_t nbits; - code = ReadSymbol(table, br); - nbits = _kBrotliPrefixCodeRanges[code].nbits; /* nbits == 2..24 */ - return _kBrotliPrefixCodeRanges[code].offset + BrotliReadBits24(br, nbits); -} - -/* WARNING: if state is not BROTLI_STATE_READ_BLOCK_LENGTH_NONE, then - reading can't be continued with ReadBlockLength. */ -static BROTLI_INLINE BROTLI_BOOL SafeReadBlockLength( - BrotliDecoderState* s, brotli_reg_t* result, const HuffmanCode* table, - BrotliBitReader* br) { - brotli_reg_t index; - if (s->substate_read_block_length == BROTLI_STATE_READ_BLOCK_LENGTH_NONE) { - if (!SafeReadSymbol(table, br, &index)) { - return BROTLI_FALSE; - } - } else { - index = s->block_length_index; - } - { - brotli_reg_t bits; - brotli_reg_t nbits = _kBrotliPrefixCodeRanges[index].nbits; - brotli_reg_t offset = _kBrotliPrefixCodeRanges[index].offset; - if (!BrotliSafeReadBits(br, nbits, &bits)) { - s->block_length_index = index; - s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX; - return BROTLI_FALSE; - } - *result = offset + bits; - s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; - return BROTLI_TRUE; - } -} - -/* Transform: - 1) initialize list L with values 0, 1,... 255 - 2) For each input element X: - 2.1) let Y = L[X] - 2.2) remove X-th element from L - 2.3) prepend Y to L - 2.4) append Y to output - - In most cases max(Y) <= 7, so most of L remains intact. - To reduce the cost of initialization, we reuse L, remember the upper bound - of Y values, and reinitialize only first elements in L. - - Most of input values are 0 and 1. To reduce number of branches, we replace - inner for loop with do-while. */ -static BROTLI_NOINLINE void InverseMoveToFrontTransform( - uint8_t* v, brotli_reg_t v_len, BrotliDecoderState* state) { - /* Reinitialize elements that could have been changed. */ - brotli_reg_t i = 1; - brotli_reg_t upper_bound = state->mtf_upper_bound; - uint32_t* mtf = &state->mtf[1]; /* Make mtf[-1] addressable. */ - uint8_t* mtf_u8 = (uint8_t*)mtf; - /* Load endian-aware constant. */ - const uint8_t b0123[4] = {0, 1, 2, 3}; - uint32_t pattern; - memcpy(&pattern, &b0123, 4); - - /* Initialize list using 4 consequent values pattern. */ - mtf[0] = pattern; - do { - pattern += 0x04040404; /* Advance all 4 values by 4. */ - mtf[i] = pattern; - i++; - } while (i <= upper_bound); - - /* Transform the input. */ - upper_bound = 0; - for (i = 0; i < v_len; ++i) { - int index = v[i]; - uint8_t value = mtf_u8[index]; - upper_bound |= v[i]; - v[i] = value; - mtf_u8[-1] = value; - do { - index--; - mtf_u8[index + 1] = mtf_u8[index]; - } while (index >= 0); - } - /* Remember amount of elements to be reinitialized. */ - state->mtf_upper_bound = upper_bound >> 2; -} - -/* Decodes a series of Huffman table using ReadHuffmanCode function. */ -static BrotliDecoderErrorCode HuffmanTreeGroupDecode( - HuffmanTreeGroup* group, BrotliDecoderState* s) { - BrotliMetablockHeaderArena* h = &s->arena.header; - if (h->substate_tree_group != BROTLI_STATE_TREE_GROUP_LOOP) { - h->next = group->codes; - h->htree_index = 0; - h->substate_tree_group = BROTLI_STATE_TREE_GROUP_LOOP; - } - while (h->htree_index < group->num_htrees) { - brotli_reg_t table_size; - BrotliDecoderErrorCode result = ReadHuffmanCode(group->alphabet_size_max, - group->alphabet_size_limit, h->next, &table_size, s); - if (result != BROTLI_DECODER_SUCCESS) return result; - group->htrees[h->htree_index] = h->next; - h->next += table_size; - ++h->htree_index; - } - h->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE; - return BROTLI_DECODER_SUCCESS; -} - -/* Decodes a context map. - Decoding is done in 4 phases: - 1) Read auxiliary information (6..16 bits) and allocate memory. - In case of trivial context map, decoding is finished at this phase. - 2) Decode Huffman table using ReadHuffmanCode function. - This table will be used for reading context map items. - 3) Read context map items; "0" values could be run-length encoded. - 4) Optionally, apply InverseMoveToFront transform to the resulting map. */ -static BrotliDecoderErrorCode DecodeContextMap(brotli_reg_t context_map_size, - brotli_reg_t* num_htrees, - uint8_t** context_map_arg, - BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS; - BrotliMetablockHeaderArena* h = &s->arena.header; - - switch ((int)h->substate_context_map) { - case BROTLI_STATE_CONTEXT_MAP_NONE: - result = DecodeVarLenUint8(s, br, num_htrees); - if (result != BROTLI_DECODER_SUCCESS) { - return result; - } - (*num_htrees)++; - h->context_index = 0; - BROTLI_LOG_UINT(context_map_size); - BROTLI_LOG_UINT(*num_htrees); - *context_map_arg = - (uint8_t*)BROTLI_DECODER_ALLOC(s, (size_t)context_map_size); - if (*context_map_arg == 0) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP); - } - if (*num_htrees <= 1) { - memset(*context_map_arg, 0, (size_t)context_map_size); - return BROTLI_DECODER_SUCCESS; - } - h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_READ_PREFIX; - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MAP_READ_PREFIX: { - brotli_reg_t bits; - /* In next stage ReadHuffmanCode uses at least 4 bits, so it is safe - to peek 4 bits ahead. */ - if (!BrotliSafeGetBits(br, 5, &bits)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if ((bits & 1) != 0) { /* Use RLE for zeros. */ - h->max_run_length_prefix = (bits >> 1) + 1; - BrotliDropBits(br, 5); - } else { - h->max_run_length_prefix = 0; - BrotliDropBits(br, 1); - } - BROTLI_LOG_UINT(h->max_run_length_prefix); - h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_HUFFMAN; - } - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MAP_HUFFMAN: { - brotli_reg_t alphabet_size = *num_htrees + h->max_run_length_prefix; - result = ReadHuffmanCode(alphabet_size, alphabet_size, - h->context_map_table, NULL, s); - if (result != BROTLI_DECODER_SUCCESS) return result; - h->code = 0xFFFF; - h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_DECODE; - } - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MAP_DECODE: { - brotli_reg_t context_index = h->context_index; - brotli_reg_t max_run_length_prefix = h->max_run_length_prefix; - uint8_t* context_map = *context_map_arg; - brotli_reg_t code = h->code; - BROTLI_BOOL skip_preamble = (code != 0xFFFF); - while (context_index < context_map_size || skip_preamble) { - if (!skip_preamble) { - if (!SafeReadSymbol(h->context_map_table, br, &code)) { - h->code = 0xFFFF; - h->context_index = context_index; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - BROTLI_LOG_UINT(code); - - if (code == 0) { - context_map[context_index++] = 0; - continue; - } - if (code > max_run_length_prefix) { - context_map[context_index++] = - (uint8_t)(code - max_run_length_prefix); - continue; - } - } else { - skip_preamble = BROTLI_FALSE; - } - /* RLE sub-stage. */ - { - brotli_reg_t reps; - if (!BrotliSafeReadBits(br, code, &reps)) { - h->code = code; - h->context_index = context_index; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - reps += 1U << code; - BROTLI_LOG_UINT(reps); - if (context_index + reps > context_map_size) { - return - BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT); - } - do { - context_map[context_index++] = 0; - } while (--reps); - } - } - } - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MAP_TRANSFORM: { - brotli_reg_t bits; - if (!BrotliSafeReadBits(br, 1, &bits)) { - h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_TRANSFORM; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - if (bits != 0) { - InverseMoveToFrontTransform(*context_map_arg, context_map_size, s); - } - h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE; - return BROTLI_DECODER_SUCCESS; - } - - default: - return - BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ - } -} - -/* Decodes a command or literal and updates block type ring-buffer. - Reads 3..54 bits. */ -static BROTLI_INLINE BROTLI_BOOL DecodeBlockTypeAndLength( - int safe, BrotliDecoderState* s, int tree_type) { - brotli_reg_t max_block_type = s->num_block_types[tree_type]; - const HuffmanCode* type_tree = &s->block_type_trees[ - tree_type * BROTLI_HUFFMAN_MAX_SIZE_258]; - const HuffmanCode* len_tree = &s->block_len_trees[ - tree_type * BROTLI_HUFFMAN_MAX_SIZE_26]; - BrotliBitReader* br = &s->br; - brotli_reg_t* ringbuffer = &s->block_type_rb[tree_type * 2]; - brotli_reg_t block_type; - if (max_block_type <= 1) { - return BROTLI_FALSE; - } - - /* Read 0..15 + 3..39 bits. */ - if (!safe) { - block_type = ReadSymbol(type_tree, br); - s->block_length[tree_type] = ReadBlockLength(len_tree, br); - } else { - BrotliBitReaderState memento; - BrotliBitReaderSaveState(br, &memento); - if (!SafeReadSymbol(type_tree, br, &block_type)) return BROTLI_FALSE; - if (!SafeReadBlockLength(s, &s->block_length[tree_type], len_tree, br)) { - s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; - BrotliBitReaderRestoreState(br, &memento); - return BROTLI_FALSE; - } - } - - if (block_type == 1) { - block_type = ringbuffer[1] + 1; - } else if (block_type == 0) { - block_type = ringbuffer[0]; - } else { - block_type -= 2; - } - if (block_type >= max_block_type) { - block_type -= max_block_type; - } - ringbuffer[0] = ringbuffer[1]; - ringbuffer[1] = block_type; - return BROTLI_TRUE; -} - -static BROTLI_INLINE void DetectTrivialLiteralBlockTypes( - BrotliDecoderState* s) { - size_t i; - for (i = 0; i < 8; ++i) s->trivial_literal_contexts[i] = 0; - for (i = 0; i < s->num_block_types[0]; i++) { - size_t offset = i << BROTLI_LITERAL_CONTEXT_BITS; - size_t error = 0; - size_t sample = s->context_map[offset]; - size_t j; - for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS);) { - /* NOLINTNEXTLINE(bugprone-macro-repeated-side-effects) */ - BROTLI_REPEAT_4({ error |= s->context_map[offset + j++] ^ sample; }) - } - if (error == 0) { - s->trivial_literal_contexts[i >> 5] |= 1u << (i & 31); - } - } -} - -static BROTLI_INLINE void PrepareLiteralDecoding(BrotliDecoderState* s) { - uint8_t context_mode; - size_t trivial; - brotli_reg_t block_type = s->block_type_rb[1]; - brotli_reg_t context_offset = block_type << BROTLI_LITERAL_CONTEXT_BITS; - s->context_map_slice = s->context_map + context_offset; - trivial = s->trivial_literal_contexts[block_type >> 5]; - s->trivial_literal_context = (trivial >> (block_type & 31)) & 1; - s->literal_htree = s->literal_hgroup.htrees[s->context_map_slice[0]]; - context_mode = s->context_modes[block_type] & 3; - s->context_lookup = BROTLI_CONTEXT_LUT(context_mode); -} - -/* Decodes the block type and updates the state for literal context. - Reads 3..54 bits. */ -static BROTLI_INLINE BROTLI_BOOL DecodeLiteralBlockSwitchInternal( - int safe, BrotliDecoderState* s) { - if (!DecodeBlockTypeAndLength(safe, s, 0)) { - return BROTLI_FALSE; - } - PrepareLiteralDecoding(s); - return BROTLI_TRUE; -} - -static void BROTLI_NOINLINE DecodeLiteralBlockSwitch(BrotliDecoderState* s) { - DecodeLiteralBlockSwitchInternal(0, s); -} - -static BROTLI_BOOL BROTLI_NOINLINE SafeDecodeLiteralBlockSwitch( - BrotliDecoderState* s) { - return DecodeLiteralBlockSwitchInternal(1, s); -} - -/* Block switch for insert/copy length. - Reads 3..54 bits. */ -static BROTLI_INLINE BROTLI_BOOL DecodeCommandBlockSwitchInternal( - int safe, BrotliDecoderState* s) { - if (!DecodeBlockTypeAndLength(safe, s, 1)) { - return BROTLI_FALSE; - } - s->htree_command = s->insert_copy_hgroup.htrees[s->block_type_rb[3]]; - return BROTLI_TRUE; -} - -static void BROTLI_NOINLINE DecodeCommandBlockSwitch(BrotliDecoderState* s) { - DecodeCommandBlockSwitchInternal(0, s); -} - -static BROTLI_BOOL BROTLI_NOINLINE SafeDecodeCommandBlockSwitch( - BrotliDecoderState* s) { - return DecodeCommandBlockSwitchInternal(1, s); -} - -/* Block switch for distance codes. - Reads 3..54 bits. */ -static BROTLI_INLINE BROTLI_BOOL DecodeDistanceBlockSwitchInternal( - int safe, BrotliDecoderState* s) { - if (!DecodeBlockTypeAndLength(safe, s, 2)) { - return BROTLI_FALSE; - } - s->dist_context_map_slice = s->dist_context_map + - (s->block_type_rb[5] << BROTLI_DISTANCE_CONTEXT_BITS); - s->dist_htree_index = s->dist_context_map_slice[s->distance_context]; - return BROTLI_TRUE; -} - -static void BROTLI_NOINLINE DecodeDistanceBlockSwitch(BrotliDecoderState* s) { - DecodeDistanceBlockSwitchInternal(0, s); -} - -static BROTLI_BOOL BROTLI_NOINLINE SafeDecodeDistanceBlockSwitch( - BrotliDecoderState* s) { - return DecodeDistanceBlockSwitchInternal(1, s); -} - -static size_t UnwrittenBytes(const BrotliDecoderState* s, BROTLI_BOOL wrap) { - size_t pos = wrap && s->pos > s->ringbuffer_size ? - (size_t)s->ringbuffer_size : (size_t)(s->pos); - size_t partial_pos_rb = (s->rb_roundtrips * (size_t)s->ringbuffer_size) + pos; - return partial_pos_rb - s->partial_pos_out; -} - -/* Dumps output. - Returns BROTLI_DECODER_NEEDS_MORE_OUTPUT only if there is more output to push - and either ring-buffer is as big as window size, or |force| is true. */ -static BrotliDecoderErrorCode BROTLI_NOINLINE WriteRingBuffer( - BrotliDecoderState* s, size_t* available_out, uint8_t** next_out, - size_t* total_out, BROTLI_BOOL force) { - uint8_t* start = - s->ringbuffer + (s->partial_pos_out & (size_t)s->ringbuffer_mask); - size_t to_write = UnwrittenBytes(s, BROTLI_TRUE); - size_t num_written = *available_out; - if (num_written > to_write) { - num_written = to_write; - } - if (s->meta_block_remaining_len < 0) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1); - } - if (next_out && !*next_out) { - *next_out = start; - } else { - if (next_out) { - memcpy(*next_out, start, num_written); - *next_out += num_written; - } - } - *available_out -= num_written; - BROTLI_LOG_UINT(to_write); - BROTLI_LOG_UINT(num_written); - s->partial_pos_out += num_written; - if (total_out) { - *total_out = s->partial_pos_out; - } - if (num_written < to_write) { - if (s->ringbuffer_size == (1 << s->window_bits) || force) { - return BROTLI_DECODER_NEEDS_MORE_OUTPUT; - } else { - return BROTLI_DECODER_SUCCESS; - } - } - /* Wrap ring buffer only if it has reached its maximal size. */ - if (s->ringbuffer_size == (1 << s->window_bits) && - s->pos >= s->ringbuffer_size) { - s->pos -= s->ringbuffer_size; - s->rb_roundtrips++; - s->should_wrap_ringbuffer = (size_t)s->pos != 0 ? 1 : 0; - } - return BROTLI_DECODER_SUCCESS; -} - -static void BROTLI_NOINLINE WrapRingBuffer(BrotliDecoderState* s) { - if (s->should_wrap_ringbuffer) { - memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)s->pos); - s->should_wrap_ringbuffer = 0; - } -} - -/* Allocates ring-buffer. - - s->ringbuffer_size MUST be updated by BrotliCalculateRingBufferSize before - this function is called. - - Last two bytes of ring-buffer are initialized to 0, so context calculation - could be done uniformly for the first two and all other positions. */ -static BROTLI_BOOL BROTLI_NOINLINE BrotliEnsureRingBuffer( - BrotliDecoderState* s) { - uint8_t* old_ringbuffer = s->ringbuffer; - if (s->ringbuffer_size == s->new_ringbuffer_size) { - return BROTLI_TRUE; - } - - s->ringbuffer = (uint8_t*)BROTLI_DECODER_ALLOC(s, - (size_t)(s->new_ringbuffer_size) + kRingBufferWriteAheadSlack); - if (s->ringbuffer == 0) { - /* Restore previous value. */ - s->ringbuffer = old_ringbuffer; - return BROTLI_FALSE; - } - s->ringbuffer[s->new_ringbuffer_size - 2] = 0; - s->ringbuffer[s->new_ringbuffer_size - 1] = 0; - - if (!!old_ringbuffer) { - memcpy(s->ringbuffer, old_ringbuffer, (size_t)s->pos); - BROTLI_DECODER_FREE(s, old_ringbuffer); - } - - s->ringbuffer_size = s->new_ringbuffer_size; - s->ringbuffer_mask = s->new_ringbuffer_size - 1; - s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size; - - return BROTLI_TRUE; -} - -static BrotliDecoderErrorCode BROTLI_NOINLINE -SkipMetadataBlock(BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - - if (s->meta_block_remaining_len == 0) { - return BROTLI_DECODER_SUCCESS; - } - - BROTLI_DCHECK((BrotliGetAvailableBits(br) & 7) == 0); - - /* Drain accumulator. */ - if (BrotliGetAvailableBits(br) >= 8) { - uint8_t buffer[8]; - int nbytes = (int)(BrotliGetAvailableBits(br)) >> 3; - BROTLI_DCHECK(nbytes <= 8); - if (nbytes > s->meta_block_remaining_len) { - nbytes = s->meta_block_remaining_len; - } - BrotliCopyBytes(buffer, br, (size_t)nbytes); - if (s->metadata_chunk_func) { - s->metadata_chunk_func(s->metadata_callback_opaque, buffer, - (size_t)nbytes); - } - s->meta_block_remaining_len -= nbytes; - if (s->meta_block_remaining_len == 0) { - return BROTLI_DECODER_SUCCESS; - } - } - - /* Direct access to metadata is possible. */ - int nbytes = (int)BrotliGetRemainingBytes(br); - if (nbytes > s->meta_block_remaining_len) { - nbytes = s->meta_block_remaining_len; - } - if (nbytes > 0) { - if (s->metadata_chunk_func) { - s->metadata_chunk_func(s->metadata_callback_opaque, br->next_in, - (size_t)nbytes); - } - BrotliDropBytes(br, (size_t)nbytes); - s->meta_block_remaining_len -= nbytes; - if (s->meta_block_remaining_len == 0) { - return BROTLI_DECODER_SUCCESS; - } - } - - BROTLI_DCHECK(BrotliGetRemainingBytes(br) == 0); - - return BROTLI_DECODER_NEEDS_MORE_INPUT; -} - -static BrotliDecoderErrorCode BROTLI_NOINLINE CopyUncompressedBlockToOutput( - size_t* available_out, uint8_t** next_out, size_t* total_out, - BrotliDecoderState* s) { - /* TODO(eustas): avoid allocation for single uncompressed block. */ - if (!BrotliEnsureRingBuffer(s)) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1); - } - - /* State machine */ - for (;;) { - switch (s->substate_uncompressed) { - case BROTLI_STATE_UNCOMPRESSED_NONE: { - int nbytes = (int)BrotliGetRemainingBytes(&s->br); - if (nbytes > s->meta_block_remaining_len) { - nbytes = s->meta_block_remaining_len; - } - if (s->pos + nbytes > s->ringbuffer_size) { - nbytes = s->ringbuffer_size - s->pos; - } - /* Copy remaining bytes from s->br.buf_ to ring-buffer. */ - BrotliCopyBytes(&s->ringbuffer[s->pos], &s->br, (size_t)nbytes); - s->pos += nbytes; - s->meta_block_remaining_len -= nbytes; - if (s->pos < 1 << s->window_bits) { - if (s->meta_block_remaining_len == 0) { - return BROTLI_DECODER_SUCCESS; - } - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE; - } - /* Fall through. */ - - case BROTLI_STATE_UNCOMPRESSED_WRITE: { - BrotliDecoderErrorCode result; - result = WriteRingBuffer( - s, available_out, next_out, total_out, BROTLI_FALSE); - if (result != BROTLI_DECODER_SUCCESS) { - return result; - } - if (s->ringbuffer_size == 1 << s->window_bits) { - s->max_distance = s->max_backward_distance; - } - s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE; - break; - } - } - } - BROTLI_DCHECK(0); /* Unreachable */ -} - -static BROTLI_BOOL AttachCompoundDictionary( - BrotliDecoderState* state, const uint8_t* data, size_t size) { - BrotliDecoderCompoundDictionary* addon = state->compound_dictionary; - if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE; - if (!addon) { - addon = (BrotliDecoderCompoundDictionary*)BROTLI_DECODER_ALLOC( - state, sizeof(BrotliDecoderCompoundDictionary)); - if (!addon) return BROTLI_FALSE; - addon->num_chunks = 0; - addon->total_size = 0; - addon->br_length = 0; - addon->br_copied = 0; - addon->block_bits = -1; - addon->chunk_offsets[0] = 0; - state->compound_dictionary = addon; - } - if (addon->num_chunks == 15) return BROTLI_FALSE; - addon->chunks[addon->num_chunks] = data; - addon->num_chunks++; - addon->total_size += (int)size; - addon->chunk_offsets[addon->num_chunks] = addon->total_size; - return BROTLI_TRUE; -} - -static void EnsureCoumpoundDictionaryInitialized(BrotliDecoderState* state) { - BrotliDecoderCompoundDictionary* addon = state->compound_dictionary; - /* 256 = (1 << 8) slots in block map. */ - int block_bits = 8; - int cursor = 0; - int index = 0; - if (addon->block_bits != -1) return; - while (((addon->total_size - 1) >> block_bits) != 0) block_bits++; - block_bits -= 8; - addon->block_bits = block_bits; - while (cursor < addon->total_size) { - while (addon->chunk_offsets[index + 1] < cursor) index++; - addon->block_map[cursor >> block_bits] = (uint8_t)index; - cursor += 1 << block_bits; - } -} - -static BROTLI_BOOL InitializeCompoundDictionaryCopy(BrotliDecoderState* s, - int address, int length) { - BrotliDecoderCompoundDictionary* addon = s->compound_dictionary; - int index; - EnsureCoumpoundDictionaryInitialized(s); - index = addon->block_map[address >> addon->block_bits]; - while (address >= addon->chunk_offsets[index + 1]) index++; - if (addon->total_size < address + length) return BROTLI_FALSE; - /* Update the recent distances cache. */ - s->dist_rb[s->dist_rb_idx & 3] = s->distance_code; - ++s->dist_rb_idx; - s->meta_block_remaining_len -= length; - addon->br_index = index; - addon->br_offset = address - addon->chunk_offsets[index]; - addon->br_length = length; - addon->br_copied = 0; - return BROTLI_TRUE; -} - -static int GetCompoundDictionarySize(BrotliDecoderState* s) { - return s->compound_dictionary ? s->compound_dictionary->total_size : 0; -} - -static int CopyFromCompoundDictionary(BrotliDecoderState* s, int pos) { - BrotliDecoderCompoundDictionary* addon = s->compound_dictionary; - int orig_pos = pos; - while (addon->br_length != addon->br_copied) { - uint8_t* copy_dst = &s->ringbuffer[pos]; - const uint8_t* copy_src = - addon->chunks[addon->br_index] + addon->br_offset; - int space = s->ringbuffer_size - pos; - int rem_chunk_length = (addon->chunk_offsets[addon->br_index + 1] - - addon->chunk_offsets[addon->br_index]) - addon->br_offset; - int length = addon->br_length - addon->br_copied; - if (length > rem_chunk_length) length = rem_chunk_length; - if (length > space) length = space; - memcpy(copy_dst, copy_src, (size_t)length); - pos += length; - addon->br_offset += length; - addon->br_copied += length; - if (length == rem_chunk_length) { - addon->br_index++; - addon->br_offset = 0; - } - if (pos == s->ringbuffer_size) break; - } - return pos - orig_pos; -} - -BROTLI_BOOL BrotliDecoderAttachDictionary( - BrotliDecoderState* state, BrotliSharedDictionaryType type, - size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]) { - brotli_reg_t i; - brotli_reg_t num_prefix_before = state->dictionary->num_prefix; - if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE; - if (!BrotliSharedDictionaryAttach(state->dictionary, type, data_size, data)) { - return BROTLI_FALSE; - } - for (i = num_prefix_before; i < state->dictionary->num_prefix; i++) { - if (!AttachCompoundDictionary( - state, state->dictionary->prefix[i], - state->dictionary->prefix_size[i])) { - return BROTLI_FALSE; - } - } - return BROTLI_TRUE; -} - -/* Calculates the smallest feasible ring buffer. - - If we know the data size is small, do not allocate more ring buffer - size than needed to reduce memory usage. - - When this method is called, metablock size and flags MUST be decoded. */ -static void BROTLI_NOINLINE BrotliCalculateRingBufferSize( - BrotliDecoderState* s) { - int window_size = 1 << s->window_bits; - int new_ringbuffer_size = window_size; - /* We need at least 2 bytes of ring buffer size to get the last two - bytes for context from there */ - int min_size = s->ringbuffer_size ? s->ringbuffer_size : 1024; - int output_size; - - /* If maximum is already reached, no further extension is retired. */ - if (s->ringbuffer_size == window_size) { - return; - } - - /* Metadata blocks does not touch ring buffer. */ - if (s->is_metadata) { - return; - } - - if (!s->ringbuffer) { - output_size = 0; - } else { - output_size = s->pos; - } - output_size += s->meta_block_remaining_len; - min_size = min_size < output_size ? output_size : min_size; - - if (!!s->canny_ringbuffer_allocation) { - /* Reduce ring buffer size to save memory when server is unscrupulous. - In worst case memory usage might be 1.5x bigger for a short period of - ring buffer reallocation. */ - while ((new_ringbuffer_size >> 1) >= min_size) { - new_ringbuffer_size >>= 1; - } - } - - s->new_ringbuffer_size = new_ringbuffer_size; -} - -/* Reads 1..256 2-bit context modes. */ -static BrotliDecoderErrorCode ReadContextModes(BrotliDecoderState* s) { - BrotliBitReader* br = &s->br; - int i = s->loop_counter; - - while (i < (int)s->num_block_types[0]) { - brotli_reg_t bits; - if (!BrotliSafeReadBits(br, 2, &bits)) { - s->loop_counter = i; - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } - s->context_modes[i] = (uint8_t)bits; - BROTLI_LOG_ARRAY_INDEX(s->context_modes, i); - i++; - } - return BROTLI_DECODER_SUCCESS; -} - -static BROTLI_INLINE void TakeDistanceFromRingBuffer(BrotliDecoderState* s) { - int offset = s->distance_code - 3; - if (s->distance_code <= 3) { - /* Compensate double distance-ring-buffer roll for dictionary items. */ - s->distance_context = 1 >> s->distance_code; - s->distance_code = s->dist_rb[(s->dist_rb_idx - offset) & 3]; - s->dist_rb_idx -= s->distance_context; - } else { - int index_delta = 3; - int delta; - int base = s->distance_code - 10; - if (s->distance_code < 10) { - base = s->distance_code - 4; - } else { - index_delta = 2; - } - /* Unpack one of six 4-bit values. */ - delta = ((0x605142 >> (4 * base)) & 0xF) - 3; - s->distance_code = s->dist_rb[(s->dist_rb_idx + index_delta) & 0x3] + delta; - if (s->distance_code <= 0) { - /* A huge distance will cause a BROTLI_FAILURE() soon. - This is a little faster than failing here. */ - s->distance_code = 0x7FFFFFFF; - } - } -} - -static BROTLI_INLINE BROTLI_BOOL SafeReadBits( - BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { - if (n_bits != 0) { - return BrotliSafeReadBits(br, n_bits, val); - } else { - *val = 0; - return BROTLI_TRUE; - } -} - -static BROTLI_INLINE BROTLI_BOOL SafeReadBits32( - BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { - if (n_bits != 0) { - return BrotliSafeReadBits32(br, n_bits, val); - } else { - *val = 0; - return BROTLI_TRUE; - } -} - -/* - RFC 7932 Section 4 with "..." shortenings and "[]" emendations. - - Each distance ... is represented with a pair ... - The distance code is encoded using a prefix code... The number of extra bits - can be 0..24... Two additional parameters: NPOSTFIX (0..3), and ... - NDIRECT (0..120) ... are encoded in the meta-block header... - - The first 16 distance symbols ... reference past distances... ring buffer ... - Next NDIRECT distance symbols ... represent distances from 1 to NDIRECT... - [For] distance symbols 16 + NDIRECT and greater ... the number of extra bits - ... is given by the following formula: - - [ xcode = dcode - NDIRECT - 16 ] - ndistbits = 1 + [ xcode ] >> (NPOSTFIX + 1) - - ... -*/ - -/* - RFC 7932 Section 9.2 with "..." shortenings and "[]" emendations. - - ... to get the actual value of the parameter NDIRECT, left-shift this - four-bit number by NPOSTFIX bits ... -*/ - -/* Remaining formulas from RFC 7932 Section 4 could be rewritten as following: - - alphabet_size = 16 + NDIRECT + (max_distbits << (NPOSTFIX + 1)) - - half = ((xcode >> NPOSTFIX) & 1) << ndistbits - postfix = xcode & ((1 << NPOSTFIX) - 1) - range_start = 2 * (1 << ndistbits - 1 - 1) - - distance = (range_start + half + extra) << NPOSTFIX + postfix + NDIRECT + 1 - - NB: ndistbits >= 1 -> range_start >= 0 - NB: range_start has factor 2, as the range is covered by 2 "halves" - NB: extra -1 offset in range_start formula covers the absence of - ndistbits = 0 case - NB: when NPOSTFIX = 0, NDIRECT is not greater than 15 - - In other words, xcode has the following binary structure - XXXHPPP: - - XXX represent the number of extra distance bits - - H selects upper / lower range of distances - - PPP represent "postfix" - - "Regular" distance encoding has NPOSTFIX = 0; omitting the postfix part - simplifies distance calculation. - - Using NPOSTFIX > 0 allows cheaper encoding of regular structures, e.g. where - most of distances have the same reminder of division by 2/4/8. For example, - the table of int32_t values that come from different sources; if it is likely - that 3 highest bytes of values from the same source are the same, then - copy distance often looks like 4x + y. - - Distance calculation could be rewritten to: - - ndistbits = NDISTBITS(NDIRECT, NPOSTFIX)[dcode] - distance = OFFSET(NDIRECT, NPOSTFIX)[dcode] + extra << NPOSTFIX - - NDISTBITS and OFFSET could be pre-calculated, as NDIRECT and NPOSTFIX could - change only once per meta-block. -*/ - -/* Calculates distance lookup table. - NB: it is possible to have all 64 tables precalculated. */ -static void CalculateDistanceLut(BrotliDecoderState* s) { - BrotliMetablockBodyArena* b = &s->arena.body; - brotli_reg_t npostfix = s->distance_postfix_bits; - brotli_reg_t ndirect = s->num_direct_distance_codes; - brotli_reg_t alphabet_size_limit = s->distance_hgroup.alphabet_size_limit; - brotli_reg_t postfix = 1u << npostfix; - brotli_reg_t j; - brotli_reg_t bits = 1; - brotli_reg_t half = 0; - - /* Skip short codes. */ - brotli_reg_t i = BROTLI_NUM_DISTANCE_SHORT_CODES; - - /* Fill direct codes. */ - for (j = 0; j < ndirect; ++j) { - b->dist_extra_bits[i] = 0; - b->dist_offset[i] = j + 1; - ++i; - } - - /* Fill regular distance codes. */ - while (i < alphabet_size_limit) { - brotli_reg_t base = ndirect + ((((2 + half) << bits) - 4) << npostfix) + 1; - /* Always fill the complete group. */ - for (j = 0; j < postfix; ++j) { - b->dist_extra_bits[i] = (uint8_t)bits; - b->dist_offset[i] = base + j; - ++i; - } - bits = bits + half; - half = half ^ 1; - } -} - -/* Precondition: s->distance_code < 0. */ -static BROTLI_INLINE BROTLI_BOOL ReadDistanceInternal( - int safe, BrotliDecoderState* s, BrotliBitReader* br) { - BrotliMetablockBodyArena* b = &s->arena.body; - brotli_reg_t code; - brotli_reg_t bits; - BrotliBitReaderState memento; - HuffmanCode* distance_tree = s->distance_hgroup.htrees[s->dist_htree_index]; - if (!safe) { - code = ReadSymbol(distance_tree, br); - } else { - BrotliBitReaderSaveState(br, &memento); - if (!SafeReadSymbol(distance_tree, br, &code)) { - return BROTLI_FALSE; - } - } - --s->block_length[2]; - /* Convert the distance code to the actual distance by possibly - looking up past distances from the s->dist_rb. */ - s->distance_context = 0; - if ((code & ~0xFu) == 0) { - s->distance_code = (int)code; - TakeDistanceFromRingBuffer(s); - return BROTLI_TRUE; - } - if (!safe) { - bits = BrotliReadBits32(br, b->dist_extra_bits[code]); - } else { - if (!SafeReadBits32(br, b->dist_extra_bits[code], &bits)) { - ++s->block_length[2]; - BrotliBitReaderRestoreState(br, &memento); - return BROTLI_FALSE; - } - } - s->distance_code = - (int)(b->dist_offset[code] + (bits << s->distance_postfix_bits)); - return BROTLI_TRUE; -} - -static BROTLI_INLINE void ReadDistance( - BrotliDecoderState* s, BrotliBitReader* br) { - ReadDistanceInternal(0, s, br); -} - -static BROTLI_INLINE BROTLI_BOOL SafeReadDistance( - BrotliDecoderState* s, BrotliBitReader* br) { - return ReadDistanceInternal(1, s, br); -} - -static BROTLI_INLINE BROTLI_BOOL ReadCommandInternal( - int safe, BrotliDecoderState* s, BrotliBitReader* br, int* insert_length) { - brotli_reg_t cmd_code; - brotli_reg_t insert_len_extra = 0; - brotli_reg_t copy_length; - CmdLutElement v; - BrotliBitReaderState memento; - if (!safe) { - cmd_code = ReadSymbol(s->htree_command, br); - } else { - BrotliBitReaderSaveState(br, &memento); - if (!SafeReadSymbol(s->htree_command, br, &cmd_code)) { - return BROTLI_FALSE; - } - } - v = kCmdLut[cmd_code]; - s->distance_code = v.distance_code; - s->distance_context = v.context; - s->dist_htree_index = s->dist_context_map_slice[s->distance_context]; - *insert_length = v.insert_len_offset; - if (!safe) { - if (BROTLI_PREDICT_FALSE(v.insert_len_extra_bits != 0)) { - insert_len_extra = BrotliReadBits24(br, v.insert_len_extra_bits); - } - copy_length = BrotliReadBits24(br, v.copy_len_extra_bits); - } else { - if (!SafeReadBits(br, v.insert_len_extra_bits, &insert_len_extra) || - !SafeReadBits(br, v.copy_len_extra_bits, ©_length)) { - BrotliBitReaderRestoreState(br, &memento); - return BROTLI_FALSE; - } - } - s->copy_length = (int)copy_length + v.copy_len_offset; - --s->block_length[1]; - *insert_length += (int)insert_len_extra; - return BROTLI_TRUE; -} - -static BROTLI_INLINE void ReadCommand( - BrotliDecoderState* s, BrotliBitReader* br, int* insert_length) { - ReadCommandInternal(0, s, br, insert_length); -} - -static BROTLI_INLINE BROTLI_BOOL SafeReadCommand( - BrotliDecoderState* s, BrotliBitReader* br, int* insert_length) { - return ReadCommandInternal(1, s, br, insert_length); -} - -static BROTLI_INLINE BROTLI_BOOL CheckInputAmount( - int safe, BrotliBitReader* const br) { - if (safe) { - return BROTLI_TRUE; - } - return BrotliCheckInputAmount(br); -} - -#define BROTLI_SAFE(METHOD) \ - { \ - if (safe) { \ - if (!Safe##METHOD) { \ - result = BROTLI_DECODER_NEEDS_MORE_INPUT; \ - goto saveStateAndReturn; \ - } \ - } else { \ - METHOD; \ - } \ - } - -static BROTLI_INLINE BrotliDecoderErrorCode ProcessCommandsInternal( - int safe, BrotliDecoderState* s) { - int pos = s->pos; - int i = s->loop_counter; - BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS; - BrotliBitReader* br = &s->br; - int compound_dictionary_size = GetCompoundDictionarySize(s); - - if (!CheckInputAmount(safe, br)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - goto saveStateAndReturn; - } - if (!safe) { - BROTLI_UNUSED(BrotliWarmupBitReader(br)); - } - - /* Jump into state machine. */ - if (s->state == BROTLI_STATE_COMMAND_BEGIN) { - goto CommandBegin; - } else if (s->state == BROTLI_STATE_COMMAND_INNER) { - goto CommandInner; - } else if (s->state == BROTLI_STATE_COMMAND_POST_DECODE_LITERALS) { - goto CommandPostDecodeLiterals; - } else if (s->state == BROTLI_STATE_COMMAND_POST_WRAP_COPY) { - goto CommandPostWrapCopy; - } else { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ - } - -CommandBegin: - if (safe) { - s->state = BROTLI_STATE_COMMAND_BEGIN; - } - if (!CheckInputAmount(safe, br)) { - s->state = BROTLI_STATE_COMMAND_BEGIN; - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - goto saveStateAndReturn; - } - if (BROTLI_PREDICT_FALSE(s->block_length[1] == 0)) { - BROTLI_SAFE(DecodeCommandBlockSwitch(s)); - goto CommandBegin; - } - /* Read the insert/copy length in the command. */ - BROTLI_SAFE(ReadCommand(s, br, &i)); - BROTLI_LOG(("[ProcessCommandsInternal] pos = %d insert = %d copy = %d\n", - pos, i, s->copy_length)); - if (i == 0) { - goto CommandPostDecodeLiterals; - } - s->meta_block_remaining_len -= i; - -CommandInner: - if (safe) { - s->state = BROTLI_STATE_COMMAND_INNER; - } - /* Read the literals in the command. */ - if (s->trivial_literal_context) { - brotli_reg_t bits; - brotli_reg_t value; - PreloadSymbol(safe, s->literal_htree, br, &bits, &value); - do { - if (!CheckInputAmount(safe, br)) { - s->state = BROTLI_STATE_COMMAND_INNER; - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - goto saveStateAndReturn; - } - if (BROTLI_PREDICT_FALSE(s->block_length[0] == 0)) { - goto NextLiteralBlock; - } - if (!safe) { - s->ringbuffer[pos] = - (uint8_t)ReadPreloadedSymbol(s->literal_htree, br, &bits, &value); - } else { - brotli_reg_t literal; - if (!SafeReadSymbol(s->literal_htree, br, &literal)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - goto saveStateAndReturn; - } - s->ringbuffer[pos] = (uint8_t)literal; - } - --s->block_length[0]; - BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos); - ++pos; - if (BROTLI_PREDICT_FALSE(pos == s->ringbuffer_size)) { - s->state = BROTLI_STATE_COMMAND_INNER_WRITE; - --i; - goto saveStateAndReturn; - } - } while (--i != 0); - } else { - uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask]; - uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask]; - do { - const HuffmanCode* hc; - uint8_t context; - if (!CheckInputAmount(safe, br)) { - s->state = BROTLI_STATE_COMMAND_INNER; - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - goto saveStateAndReturn; - } - if (BROTLI_PREDICT_FALSE(s->block_length[0] == 0)) { - goto NextLiteralBlock; - } - context = BROTLI_CONTEXT(p1, p2, s->context_lookup); - BROTLI_LOG_UINT(context); - hc = s->literal_hgroup.htrees[s->context_map_slice[context]]; - p2 = p1; - if (!safe) { - p1 = (uint8_t)ReadSymbol(hc, br); - } else { - brotli_reg_t literal; - if (!SafeReadSymbol(hc, br, &literal)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - goto saveStateAndReturn; - } - p1 = (uint8_t)literal; - } - s->ringbuffer[pos] = p1; - --s->block_length[0]; - BROTLI_LOG_UINT(s->context_map_slice[context]); - BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos & s->ringbuffer_mask); - ++pos; - if (BROTLI_PREDICT_FALSE(pos == s->ringbuffer_size)) { - s->state = BROTLI_STATE_COMMAND_INNER_WRITE; - --i; - goto saveStateAndReturn; - } - } while (--i != 0); - } - BROTLI_LOG_UINT(s->meta_block_remaining_len); - if (BROTLI_PREDICT_FALSE(s->meta_block_remaining_len <= 0)) { - s->state = BROTLI_STATE_METABLOCK_DONE; - goto saveStateAndReturn; - } - -CommandPostDecodeLiterals: - if (safe) { - s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS; - } - if (s->distance_code >= 0) { - /* Implicit distance case. */ - s->distance_context = s->distance_code ? 0 : 1; - --s->dist_rb_idx; - s->distance_code = s->dist_rb[s->dist_rb_idx & 3]; - } else { - /* Read distance code in the command, unless it was implicitly zero. */ - if (BROTLI_PREDICT_FALSE(s->block_length[2] == 0)) { - BROTLI_SAFE(DecodeDistanceBlockSwitch(s)); - } - BROTLI_SAFE(ReadDistance(s, br)); - } - BROTLI_LOG(("[ProcessCommandsInternal] pos = %d distance = %d\n", - pos, s->distance_code)); - if (s->max_distance != s->max_backward_distance) { - s->max_distance = - (pos < s->max_backward_distance) ? pos : s->max_backward_distance; - } - i = s->copy_length; - /* Apply copy of LZ77 back-reference, or static dictionary reference if - the distance is larger than the max LZ77 distance */ - if (s->distance_code > s->max_distance) { - /* The maximum allowed distance is BROTLI_MAX_ALLOWED_DISTANCE = 0x7FFFFFFC. - With this choice, no signed overflow can occur after decoding - a special distance code (e.g., after adding 3 to the last distance). */ - if (s->distance_code > BROTLI_MAX_ALLOWED_DISTANCE) { - BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", - pos, s->distance_code, i, s->meta_block_remaining_len)); - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DISTANCE); - } - if (s->distance_code - s->max_distance - 1 < compound_dictionary_size) { - int address = compound_dictionary_size - - (s->distance_code - s->max_distance); - if (!InitializeCompoundDictionaryCopy(s, address, i)) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY); - } - pos += CopyFromCompoundDictionary(s, pos); - if (pos >= s->ringbuffer_size) { - s->state = BROTLI_STATE_COMMAND_POST_WRITE_1; - goto saveStateAndReturn; - } - } else if (i >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH && - i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH) { - uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask]; - uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask]; - uint8_t dict_id = s->dictionary->context_based ? - s->dictionary->context_map[BROTLI_CONTEXT(p1, p2, s->context_lookup)] - : 0; - const BrotliDictionary* words = s->dictionary->words[dict_id]; - const BrotliTransforms* transforms = s->dictionary->transforms[dict_id]; - int offset = (int)words->offsets_by_length[i]; - brotli_reg_t shift = words->size_bits_by_length[i]; - int address = - s->distance_code - s->max_distance - 1 - compound_dictionary_size; - int mask = (int)BitMask(shift); - int word_idx = address & mask; - int transform_idx = address >> shift; - /* Compensate double distance-ring-buffer roll. */ - s->dist_rb_idx += s->distance_context; - offset += word_idx * i; - /* If the distance is out of bound, select a next static dictionary if - there exist multiple. */ - if ((transform_idx >= (int)transforms->num_transforms || - words->size_bits_by_length[i] == 0) && - s->dictionary->num_dictionaries > 1) { - uint8_t dict_id2; - int dist_remaining = address - - (int)(((1u << shift) & ~1u)) * (int)transforms->num_transforms; - for (dict_id2 = 0; dict_id2 < s->dictionary->num_dictionaries; - dict_id2++) { - const BrotliDictionary* words2 = s->dictionary->words[dict_id2]; - if (dict_id2 != dict_id && words2->size_bits_by_length[i] != 0) { - const BrotliTransforms* transforms2 = - s->dictionary->transforms[dict_id2]; - brotli_reg_t shift2 = words2->size_bits_by_length[i]; - int num = (int)((1u << shift2) & ~1u) * - (int)transforms2->num_transforms; - if (dist_remaining < num) { - dict_id = dict_id2; - words = words2; - transforms = transforms2; - address = dist_remaining; - shift = shift2; - mask = (int)BitMask(shift); - word_idx = address & mask; - transform_idx = address >> shift; - offset = (int)words->offsets_by_length[i] + word_idx * i; - break; - } - dist_remaining -= num; - } - } - } - if (BROTLI_PREDICT_FALSE(words->size_bits_by_length[i] == 0)) { - BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", - pos, s->distance_code, i, s->meta_block_remaining_len)); - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DICTIONARY); - } - if (BROTLI_PREDICT_FALSE(!words->data)) { - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET); - } - if (transform_idx < (int)transforms->num_transforms) { - const uint8_t* word = &words->data[offset]; - int len = i; - if (transform_idx == transforms->cutOffTransforms[0]) { - memcpy(&s->ringbuffer[pos], word, (size_t)len); - BROTLI_LOG(("[ProcessCommandsInternal] dictionary word: [%.*s]\n", - len, word)); - } else { - len = BrotliTransformDictionaryWord(&s->ringbuffer[pos], word, len, - transforms, transform_idx); - BROTLI_LOG(("[ProcessCommandsInternal] dictionary word: [%.*s]," - " transform_idx = %d, transformed: [%.*s]\n", - i, word, transform_idx, len, &s->ringbuffer[pos])); - if (len == 0 && s->distance_code <= 120) { - BROTLI_LOG(("Invalid length-0 dictionary word after transform\n")); - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_TRANSFORM); - } - } - pos += len; - s->meta_block_remaining_len -= len; - if (pos >= s->ringbuffer_size) { - s->state = BROTLI_STATE_COMMAND_POST_WRITE_1; - goto saveStateAndReturn; - } - } else { - BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", - pos, s->distance_code, i, s->meta_block_remaining_len)); - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_TRANSFORM); - } - } else { - BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", - pos, s->distance_code, i, s->meta_block_remaining_len)); - return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DICTIONARY); - } - } else { - int src_start = (pos - s->distance_code) & s->ringbuffer_mask; - uint8_t* copy_dst = &s->ringbuffer[pos]; - uint8_t* copy_src = &s->ringbuffer[src_start]; - int dst_end = pos + i; - int src_end = src_start + i; - /* Update the recent distances cache. */ - s->dist_rb[s->dist_rb_idx & 3] = s->distance_code; - ++s->dist_rb_idx; - s->meta_block_remaining_len -= i; - /* There are 32+ bytes of slack in the ring-buffer allocation. - Also, we have 16 short codes, that make these 16 bytes irrelevant - in the ring-buffer. Let's copy over them as a first guess. */ - memmove16(copy_dst, copy_src); - if (src_end > pos && dst_end > src_start) { - /* Regions intersect. */ - goto CommandPostWrapCopy; - } - if (dst_end >= s->ringbuffer_size || src_end >= s->ringbuffer_size) { - /* At least one region wraps. */ - goto CommandPostWrapCopy; - } - pos += i; - if (i > 16) { - if (i > 32) { - memcpy(copy_dst + 16, copy_src + 16, (size_t)(i - 16)); - } else { - /* This branch covers about 45% cases. - Fixed size short copy allows more compiler optimizations. */ - memmove16(copy_dst + 16, copy_src + 16); - } - } - } - BROTLI_LOG_UINT(s->meta_block_remaining_len); - if (s->meta_block_remaining_len <= 0) { - /* Next metablock, if any. */ - s->state = BROTLI_STATE_METABLOCK_DONE; - goto saveStateAndReturn; - } else { - goto CommandBegin; - } -CommandPostWrapCopy: - { - int wrap_guard = s->ringbuffer_size - pos; - while (--i >= 0) { - s->ringbuffer[pos] = - s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask]; - ++pos; - if (BROTLI_PREDICT_FALSE(--wrap_guard == 0)) { - s->state = BROTLI_STATE_COMMAND_POST_WRITE_2; - goto saveStateAndReturn; - } - } - } - if (s->meta_block_remaining_len <= 0) { - /* Next metablock, if any. */ - s->state = BROTLI_STATE_METABLOCK_DONE; - goto saveStateAndReturn; - } else { - goto CommandBegin; - } - -NextLiteralBlock: - BROTLI_SAFE(DecodeLiteralBlockSwitch(s)); - goto CommandInner; - -saveStateAndReturn: - s->pos = pos; - s->loop_counter = i; - return result; -} - -#undef BROTLI_SAFE - -static BROTLI_NOINLINE BrotliDecoderErrorCode ProcessCommands( - BrotliDecoderState* s) { - return ProcessCommandsInternal(0, s); -} - -static BROTLI_NOINLINE BrotliDecoderErrorCode SafeProcessCommands( - BrotliDecoderState* s) { - return ProcessCommandsInternal(1, s); -} - -BrotliDecoderResult BrotliDecoderDecompress( - size_t encoded_size, - const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], - size_t* decoded_size, - uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]) { - BrotliDecoderState s; - BrotliDecoderResult result; - size_t total_out = 0; - size_t available_in = encoded_size; - const uint8_t* next_in = encoded_buffer; - size_t available_out = *decoded_size; - uint8_t* next_out = decoded_buffer; - if (!BrotliDecoderStateInit(&s, 0, 0, 0)) { - return BROTLI_DECODER_RESULT_ERROR; - } - result = BrotliDecoderDecompressStream( - &s, &available_in, &next_in, &available_out, &next_out, &total_out); - *decoded_size = total_out; - BrotliDecoderStateCleanup(&s); - if (result != BROTLI_DECODER_RESULT_SUCCESS) { - result = BROTLI_DECODER_RESULT_ERROR; - } - return result; -} - -/* Invariant: input stream is never overconsumed: - - invalid input implies that the whole stream is invalid -> any amount of - input could be read and discarded - - when result is "needs more input", then at least one more byte is REQUIRED - to complete decoding; all input data MUST be consumed by decoder, so - client could swap the input buffer - - when result is "needs more output" decoder MUST ensure that it doesn't - hold more than 7 bits in bit reader; this saves client from swapping input - buffer ahead of time - - when result is "success" decoder MUST return all unused data back to input - buffer; this is possible because the invariant is held on enter */ -BrotliDecoderResult BrotliDecoderDecompressStream( - BrotliDecoderState* s, size_t* available_in, const uint8_t** next_in, - size_t* available_out, uint8_t** next_out, size_t* total_out) { - BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS; - BrotliBitReader* br = &s->br; - size_t input_size = *available_in; -#define BROTLI_SAVE_ERROR_CODE(code) \ - SaveErrorCode(s, (code), input_size - *available_in) - /* Ensure that |total_out| is set, even if no data will ever be pushed out. */ - if (total_out) { - *total_out = s->partial_pos_out; - } - /* Do not try to process further in a case of unrecoverable error. */ - if ((int)s->error_code < 0) { - return BROTLI_DECODER_RESULT_ERROR; - } - if (*available_out && (!next_out || !*next_out)) { - return BROTLI_SAVE_ERROR_CODE( - BROTLI_FAILURE(BROTLI_DECODER_ERROR_INVALID_ARGUMENTS)); - } - if (!*available_out) next_out = 0; - if (s->buffer_length == 0) { /* Just connect bit reader to input stream. */ - BrotliBitReaderSetInput(br, *next_in, *available_in); - } else { - /* At least one byte of input is required. More than one byte of input may - be required to complete the transaction -> reading more data must be - done in a loop -> do it in a main loop. */ - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - BrotliBitReaderSetInput(br, &s->buffer.u8[0], s->buffer_length); - } - /* State machine */ - for (;;) { - if (result != BROTLI_DECODER_SUCCESS) { - /* Error, needs more input/output. */ - if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) { - if (s->ringbuffer != 0) { /* Pro-actively push output. */ - BrotliDecoderErrorCode intermediate_result = WriteRingBuffer(s, - available_out, next_out, total_out, BROTLI_TRUE); - /* WriteRingBuffer checks s->meta_block_remaining_len validity. */ - if ((int)intermediate_result < 0) { - result = intermediate_result; - break; - } - } - if (s->buffer_length != 0) { /* Used with internal buffer. */ - if (br->next_in == br->last_in) { - /* Successfully finished read transaction. - Accumulator contains less than 8 bits, because internal buffer - is expanded byte-by-byte until it is enough to complete read. */ - s->buffer_length = 0; - /* Switch to input stream and restart. */ - result = BROTLI_DECODER_SUCCESS; - BrotliBitReaderSetInput(br, *next_in, *available_in); - continue; - } else if (*available_in != 0) { - /* Not enough data in buffer, but can take one more byte from - input stream. */ - result = BROTLI_DECODER_SUCCESS; - BROTLI_DCHECK(s->buffer_length < 8); - s->buffer.u8[s->buffer_length] = **next_in; - s->buffer_length++; - BrotliBitReaderSetInput(br, &s->buffer.u8[0], s->buffer_length); - (*next_in)++; - (*available_in)--; - /* Retry with more data in buffer. */ - continue; - } - /* Can't finish reading and no more input. */ - break; - } else { /* Input stream doesn't contain enough input. */ - /* Copy tail to internal buffer and return. */ - *next_in = br->next_in; - *available_in = BrotliBitReaderGetAvailIn(br); - while (*available_in) { - s->buffer.u8[s->buffer_length] = **next_in; - s->buffer_length++; - (*next_in)++; - (*available_in)--; - } - break; - } - /* Unreachable. */ - } - - /* Fail or needs more output. */ - - if (s->buffer_length != 0) { - /* Just consumed the buffered input and produced some output. Otherwise - it would result in "needs more input". Reset internal buffer. */ - s->buffer_length = 0; - } else { - /* Using input stream in last iteration. When decoder switches to input - stream it has less than 8 bits in accumulator, so it is safe to - return unused accumulator bits there. */ - BrotliBitReaderUnload(br); - *available_in = BrotliBitReaderGetAvailIn(br); - *next_in = br->next_in; - } - break; - } - switch (s->state) { - case BROTLI_STATE_UNINITED: - /* Prepare to the first read. */ - if (!BrotliWarmupBitReader(br)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - break; - } - /* Decode window size. */ - result = DecodeWindowBits(s, br); /* Reads 1..8 bits. */ - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - if (s->large_window) { - s->state = BROTLI_STATE_LARGE_WINDOW_BITS; - break; - } - s->state = BROTLI_STATE_INITIALIZE; - break; - - case BROTLI_STATE_LARGE_WINDOW_BITS: { - brotli_reg_t bits; - if (!BrotliSafeReadBits(br, 6, &bits)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - break; - } - s->window_bits = bits & 63u; - if (s->window_bits < BROTLI_LARGE_MIN_WBITS || - s->window_bits > BROTLI_LARGE_MAX_WBITS) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS); - break; - } - s->state = BROTLI_STATE_INITIALIZE; - } - /* Fall through. */ - - case BROTLI_STATE_INITIALIZE: - BROTLI_LOG_UINT(s->window_bits); - /* Maximum distance, see section 9.1. of the spec. */ - s->max_backward_distance = (1 << s->window_bits) - BROTLI_WINDOW_GAP; - - /* Allocate memory for both block_type_trees and block_len_trees. */ - s->block_type_trees = (HuffmanCode*)BROTLI_DECODER_ALLOC(s, - sizeof(HuffmanCode) * 3 * - (BROTLI_HUFFMAN_MAX_SIZE_258 + BROTLI_HUFFMAN_MAX_SIZE_26)); - if (s->block_type_trees == 0) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES); - break; - } - s->block_len_trees = - s->block_type_trees + 3 * BROTLI_HUFFMAN_MAX_SIZE_258; - - s->state = BROTLI_STATE_METABLOCK_BEGIN; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_BEGIN: - BrotliDecoderStateMetablockBegin(s); - BROTLI_LOG_UINT(s->pos); - s->state = BROTLI_STATE_METABLOCK_HEADER; - /* Fall through. */ - - case BROTLI_STATE_METABLOCK_HEADER: - result = DecodeMetaBlockLength(s, br); /* Reads 2 - 31 bits. */ - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - BROTLI_LOG_UINT(s->is_last_metablock); - BROTLI_LOG_UINT(s->meta_block_remaining_len); - BROTLI_LOG_UINT(s->is_metadata); - BROTLI_LOG_UINT(s->is_uncompressed); - if (s->is_metadata || s->is_uncompressed) { - if (!BrotliJumpToByteBoundary(br)) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_PADDING_1); - break; - } - } - if (s->is_metadata) { - s->state = BROTLI_STATE_METADATA; - if (s->metadata_start_func) { - s->metadata_start_func(s->metadata_callback_opaque, - (size_t)s->meta_block_remaining_len); - } - break; - } - if (s->meta_block_remaining_len == 0) { - s->state = BROTLI_STATE_METABLOCK_DONE; - break; - } - BrotliCalculateRingBufferSize(s); - if (s->is_uncompressed) { - s->state = BROTLI_STATE_UNCOMPRESSED; - break; - } - s->state = BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER; - /* Fall through. */ - - case BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER: { - BrotliMetablockHeaderArena* h = &s->arena.header; - s->loop_counter = 0; - /* Initialize compressed metablock header arena. */ - h->sub_loop_counter = 0; - /* Make small negative indexes addressable. */ - h->symbol_lists = - &h->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1]; - h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE; - h->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE; - h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE; - s->state = BROTLI_STATE_HUFFMAN_CODE_0; - } - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_CODE_0: - if (s->loop_counter >= 3) { - s->state = BROTLI_STATE_METABLOCK_HEADER_2; - break; - } - /* Reads 1..11 bits. */ - result = DecodeVarLenUint8(s, br, &s->num_block_types[s->loop_counter]); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - s->num_block_types[s->loop_counter]++; - BROTLI_LOG_UINT(s->num_block_types[s->loop_counter]); - if (s->num_block_types[s->loop_counter] < 2) { - s->loop_counter++; - break; - } - s->state = BROTLI_STATE_HUFFMAN_CODE_1; - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_CODE_1: { - brotli_reg_t alphabet_size = s->num_block_types[s->loop_counter] + 2; - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_258; - result = ReadHuffmanCode(alphabet_size, alphabet_size, - &s->block_type_trees[tree_offset], NULL, s); - if (result != BROTLI_DECODER_SUCCESS) break; - s->state = BROTLI_STATE_HUFFMAN_CODE_2; - } - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_CODE_2: { - brotli_reg_t alphabet_size = BROTLI_NUM_BLOCK_LEN_SYMBOLS; - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; - result = ReadHuffmanCode(alphabet_size, alphabet_size, - &s->block_len_trees[tree_offset], NULL, s); - if (result != BROTLI_DECODER_SUCCESS) break; - s->state = BROTLI_STATE_HUFFMAN_CODE_3; - } - /* Fall through. */ - - case BROTLI_STATE_HUFFMAN_CODE_3: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; - if (!SafeReadBlockLength(s, &s->block_length[s->loop_counter], - &s->block_len_trees[tree_offset], br)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - break; - } - BROTLI_LOG_UINT(s->block_length[s->loop_counter]); - s->loop_counter++; - s->state = BROTLI_STATE_HUFFMAN_CODE_0; - break; - } - - case BROTLI_STATE_UNCOMPRESSED: { - result = CopyUncompressedBlockToOutput( - available_out, next_out, total_out, s); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - s->state = BROTLI_STATE_METABLOCK_DONE; - break; - } - - case BROTLI_STATE_METADATA: - result = SkipMetadataBlock(s); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - s->state = BROTLI_STATE_METABLOCK_DONE; - break; - - case BROTLI_STATE_METABLOCK_HEADER_2: { - brotli_reg_t bits; - if (!BrotliSafeReadBits(br, 6, &bits)) { - result = BROTLI_DECODER_NEEDS_MORE_INPUT; - break; - } - s->distance_postfix_bits = bits & BitMask(2); - bits >>= 2; - s->num_direct_distance_codes = bits << s->distance_postfix_bits; - BROTLI_LOG_UINT(s->num_direct_distance_codes); - BROTLI_LOG_UINT(s->distance_postfix_bits); - s->context_modes = - (uint8_t*)BROTLI_DECODER_ALLOC(s, (size_t)s->num_block_types[0]); - if (s->context_modes == 0) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES); - break; - } - s->loop_counter = 0; - s->state = BROTLI_STATE_CONTEXT_MODES; - } - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MODES: - result = ReadContextModes(s); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - s->state = BROTLI_STATE_CONTEXT_MAP_1; - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MAP_1: - result = DecodeContextMap( - s->num_block_types[0] << BROTLI_LITERAL_CONTEXT_BITS, - &s->num_literal_htrees, &s->context_map, s); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - DetectTrivialLiteralBlockTypes(s); - s->state = BROTLI_STATE_CONTEXT_MAP_2; - /* Fall through. */ - - case BROTLI_STATE_CONTEXT_MAP_2: { - brotli_reg_t npostfix = s->distance_postfix_bits; - brotli_reg_t ndirect = s->num_direct_distance_codes; - brotli_reg_t distance_alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( - npostfix, ndirect, BROTLI_MAX_DISTANCE_BITS); - brotli_reg_t distance_alphabet_size_limit = distance_alphabet_size_max; - BROTLI_BOOL allocation_success = BROTLI_TRUE; - if (s->large_window) { - BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit( - BROTLI_MAX_ALLOWED_DISTANCE, (uint32_t)npostfix, - (uint32_t)ndirect); - distance_alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( - npostfix, ndirect, BROTLI_LARGE_MAX_DISTANCE_BITS); - distance_alphabet_size_limit = limit.max_alphabet_size; - } - result = DecodeContextMap( - s->num_block_types[2] << BROTLI_DISTANCE_CONTEXT_BITS, - &s->num_dist_htrees, &s->dist_context_map, s); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - allocation_success &= BrotliDecoderHuffmanTreeGroupInit( - s, &s->literal_hgroup, BROTLI_NUM_LITERAL_SYMBOLS, - BROTLI_NUM_LITERAL_SYMBOLS, s->num_literal_htrees); - allocation_success &= BrotliDecoderHuffmanTreeGroupInit( - s, &s->insert_copy_hgroup, BROTLI_NUM_COMMAND_SYMBOLS, - BROTLI_NUM_COMMAND_SYMBOLS, s->num_block_types[1]); - allocation_success &= BrotliDecoderHuffmanTreeGroupInit( - s, &s->distance_hgroup, distance_alphabet_size_max, - distance_alphabet_size_limit, s->num_dist_htrees); - if (!allocation_success) { - return BROTLI_SAVE_ERROR_CODE( - BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS)); - } - s->loop_counter = 0; - s->state = BROTLI_STATE_TREE_GROUP; - } - /* Fall through. */ - - case BROTLI_STATE_TREE_GROUP: { - HuffmanTreeGroup* hgroup = NULL; - switch (s->loop_counter) { - case 0: hgroup = &s->literal_hgroup; break; - case 1: hgroup = &s->insert_copy_hgroup; break; - case 2: hgroup = &s->distance_hgroup; break; - default: return BROTLI_SAVE_ERROR_CODE(BROTLI_FAILURE( - BROTLI_DECODER_ERROR_UNREACHABLE)); /* COV_NF_LINE */ - } - result = HuffmanTreeGroupDecode(hgroup, s); - if (result != BROTLI_DECODER_SUCCESS) break; - s->loop_counter++; - if (s->loop_counter < 3) { - break; - } - s->state = BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY; - } - /* Fall through. */ - - case BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY: - PrepareLiteralDecoding(s); - s->dist_context_map_slice = s->dist_context_map; - s->htree_command = s->insert_copy_hgroup.htrees[0]; - if (!BrotliEnsureRingBuffer(s)) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2); - break; - } - CalculateDistanceLut(s); - s->state = BROTLI_STATE_COMMAND_BEGIN; - /* Fall through. */ - - case BROTLI_STATE_COMMAND_BEGIN: - /* Fall through. */ - case BROTLI_STATE_COMMAND_INNER: - /* Fall through. */ - case BROTLI_STATE_COMMAND_POST_DECODE_LITERALS: - /* Fall through. */ - case BROTLI_STATE_COMMAND_POST_WRAP_COPY: - result = ProcessCommands(s); - if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) { - result = SafeProcessCommands(s); - } - break; - - case BROTLI_STATE_COMMAND_INNER_WRITE: - /* Fall through. */ - case BROTLI_STATE_COMMAND_POST_WRITE_1: - /* Fall through. */ - case BROTLI_STATE_COMMAND_POST_WRITE_2: - result = WriteRingBuffer( - s, available_out, next_out, total_out, BROTLI_FALSE); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - WrapRingBuffer(s); - if (s->ringbuffer_size == 1 << s->window_bits) { - s->max_distance = s->max_backward_distance; - } - if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) { - BrotliDecoderCompoundDictionary* addon = s->compound_dictionary; - if (addon && (addon->br_length != addon->br_copied)) { - s->pos += CopyFromCompoundDictionary(s, s->pos); - if (s->pos >= s->ringbuffer_size) continue; - } - if (s->meta_block_remaining_len == 0) { - /* Next metablock, if any. */ - s->state = BROTLI_STATE_METABLOCK_DONE; - } else { - s->state = BROTLI_STATE_COMMAND_BEGIN; - } - break; - } else if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_2) { - s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY; - } else { /* BROTLI_STATE_COMMAND_INNER_WRITE */ - if (s->loop_counter == 0) { - if (s->meta_block_remaining_len == 0) { - s->state = BROTLI_STATE_METABLOCK_DONE; - } else { - s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS; - } - break; - } - s->state = BROTLI_STATE_COMMAND_INNER; - } - break; - - case BROTLI_STATE_METABLOCK_DONE: - if (s->meta_block_remaining_len < 0) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2); - break; - } - BrotliDecoderStateCleanupAfterMetablock(s); - if (!s->is_last_metablock) { - s->state = BROTLI_STATE_METABLOCK_BEGIN; - break; - } - if (!BrotliJumpToByteBoundary(br)) { - result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_PADDING_2); - break; - } - if (s->buffer_length == 0) { - BrotliBitReaderUnload(br); - *available_in = BrotliBitReaderGetAvailIn(br); - *next_in = br->next_in; - } - s->state = BROTLI_STATE_DONE; - /* Fall through. */ - - case BROTLI_STATE_DONE: - if (s->ringbuffer != 0) { - result = WriteRingBuffer( - s, available_out, next_out, total_out, BROTLI_TRUE); - if (result != BROTLI_DECODER_SUCCESS) { - break; - } - } - return BROTLI_SAVE_ERROR_CODE(result); - } - } - return BROTLI_SAVE_ERROR_CODE(result); -#undef BROTLI_SAVE_ERROR_CODE -} - -BROTLI_BOOL BrotliDecoderHasMoreOutput(const BrotliDecoderState* s) { - /* After unrecoverable error remaining output is considered nonsensical. */ - if ((int)s->error_code < 0) { - return BROTLI_FALSE; - } - return TO_BROTLI_BOOL( - s->ringbuffer != 0 && UnwrittenBytes(s, BROTLI_FALSE) != 0); -} - -const uint8_t* BrotliDecoderTakeOutput(BrotliDecoderState* s, size_t* size) { - uint8_t* result = 0; - size_t available_out = *size ? *size : 1u << 24; - size_t requested_out = available_out; - BrotliDecoderErrorCode status; - if ((s->ringbuffer == 0) || ((int)s->error_code < 0)) { - *size = 0; - return 0; - } - WrapRingBuffer(s); - status = WriteRingBuffer(s, &available_out, &result, 0, BROTLI_TRUE); - /* Either WriteRingBuffer returns those "success" codes... */ - if (status == BROTLI_DECODER_SUCCESS || - status == BROTLI_DECODER_NEEDS_MORE_OUTPUT) { - *size = requested_out - available_out; - } else { - /* ... or stream is broken. Normally this should be caught by - BrotliDecoderDecompressStream, this is just a safeguard. */ - if ((int)status < 0) SaveErrorCode(s, status, 0); - *size = 0; - result = 0; - } - return result; -} - -BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* s) { - return TO_BROTLI_BOOL(s->state != BROTLI_STATE_UNINITED || - BrotliGetAvailableBits(&s->br) != 0); -} - -BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* s) { - return TO_BROTLI_BOOL(s->state == BROTLI_STATE_DONE) && - !BrotliDecoderHasMoreOutput(s); -} - -BrotliDecoderErrorCode BrotliDecoderGetErrorCode(const BrotliDecoderState* s) { - return (BrotliDecoderErrorCode)s->error_code; -} - -const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c) { - switch (c) { -#define BROTLI_ERROR_CODE_CASE_(PREFIX, NAME, CODE) \ - case BROTLI_DECODER ## PREFIX ## NAME: return #PREFIX #NAME; -#define BROTLI_NOTHING_ - BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_CASE_, BROTLI_NOTHING_) -#undef BROTLI_ERROR_CODE_CASE_ -#undef BROTLI_NOTHING_ - default: return "INVALID"; - } -} - -uint32_t BrotliDecoderVersion(void) { - return BROTLI_VERSION; -} - -void BrotliDecoderSetMetadataCallbacks( - BrotliDecoderState* state, - brotli_decoder_metadata_start_func start_func, - brotli_decoder_metadata_chunk_func chunk_func, void* opaque) { - state->metadata_start_func = start_func; - state->metadata_chunk_func = chunk_func; - state->metadata_callback_opaque = opaque; -} - -/* Escalate internal functions visibility; for testing purposes only. */ -#if defined(BROTLI_TEST) -BROTLI_BOOL SafeReadSymbolForTest( - const HuffmanCode*, BrotliBitReader*, brotli_reg_t*); -BROTLI_BOOL SafeReadSymbolForTest( - const HuffmanCode* table, BrotliBitReader* br, brotli_reg_t* result) { - return SafeReadSymbol(table, br, result); -} - -void InverseMoveToFrontTransformForTest( - uint8_t*, brotli_reg_t, BrotliDecoderState*); -void InverseMoveToFrontTransformForTest( - uint8_t* v, brotli_reg_t l, BrotliDecoderState* s) { - InverseMoveToFrontTransform(v, l, s); -} -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/dec/huffman.c b/src/deps/brotli/dec/huffman.c deleted file mode 100644 index 3806454864047..0000000000000 --- a/src/deps/brotli/dec/huffman.c +++ /dev/null @@ -1,342 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Utilities for building Huffman decoding tables. */ - -#include "huffman.h" - -#include /* memcpy, memset */ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define BROTLI_REVERSE_BITS_MAX 8 - -#if defined(BROTLI_RBIT) -#define BROTLI_REVERSE_BITS_BASE \ - ((sizeof(brotli_reg_t) << 3) - BROTLI_REVERSE_BITS_MAX) -#else -#define BROTLI_REVERSE_BITS_BASE 0 -static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF -}; -#endif /* BROTLI_RBIT */ - -#define BROTLI_REVERSE_BITS_LOWEST \ - ((brotli_reg_t)1 << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE)) - -/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX), - where reverse(value, len) is the bit-wise reversal of the len least - significant bits of value. */ -static BROTLI_INLINE brotli_reg_t BrotliReverseBits(brotli_reg_t num) { -#if defined(BROTLI_RBIT) - return BROTLI_RBIT(num); -#else - return kReverseBits[num]; -#endif -} - -/* Stores code in table[0], table[step], table[2*step], ..., table[end] */ -/* Assumes that end is an integer multiple of step */ -static BROTLI_INLINE void ReplicateValue(HuffmanCode* table, - int step, int end, - HuffmanCode code) { - do { - end -= step; - table[end] = code; - } while (end > 0); -} - -/* Returns the table width of the next 2nd level table. |count| is the histogram - of bit lengths for the remaining symbols, |len| is the code length of the - next processed symbol. */ -static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count, - int len, int root_bits) { - int left = 1 << (len - root_bits); - while (len < BROTLI_HUFFMAN_MAX_CODE_LENGTH) { - left -= count[len]; - if (left <= 0) break; - ++len; - left <<= 1; - } - return len - root_bits; -} - -void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table, - const uint8_t* const code_lengths, - uint16_t* count) { - HuffmanCode code; /* current table entry */ - int symbol; /* symbol index in original or sorted table */ - brotli_reg_t key; /* prefix code */ - brotli_reg_t key_step; /* prefix code addend */ - int step; /* step size to replicate values in current table */ - int table_size; /* size of current table */ - int sorted[BROTLI_CODE_LENGTH_CODES]; /* symbols sorted by code length */ - /* offsets in sorted table for each length */ - int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1]; - int bits; - int bits_count; - BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <= - BROTLI_REVERSE_BITS_MAX); - BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH == 5); - - /* Generate offsets into sorted symbol table by code length. */ - symbol = -1; - bits = 1; - /* BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH == 5 */ - BROTLI_REPEAT_5({ - symbol += count[bits]; - offset[bits] = symbol; - bits++; - }); - /* Symbols with code length 0 are placed after all other symbols. */ - offset[0] = BROTLI_CODE_LENGTH_CODES - 1; - - /* Sort symbols by length, by symbol order within each length. */ - symbol = BROTLI_CODE_LENGTH_CODES; - do { - BROTLI_REPEAT_6({ - symbol--; - sorted[offset[code_lengths[symbol]]--] = symbol; - }); - } while (symbol != 0); - - table_size = 1 << BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH; - - /* Special case: all symbols but one have 0 code length. */ - if (offset[0] == 0) { - code = ConstructHuffmanCode(0, (uint16_t)sorted[0]); - for (key = 0; key < (brotli_reg_t)table_size; ++key) { - table[key] = code; - } - return; - } - - /* Fill in table. */ - key = 0; - key_step = BROTLI_REVERSE_BITS_LOWEST; - symbol = 0; - bits = 1; - step = 2; - do { - for (bits_count = count[bits]; bits_count != 0; --bits_count) { - code = ConstructHuffmanCode((uint8_t)bits, (uint16_t)sorted[symbol++]); - ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code); - key += key_step; - } - step <<= 1; - key_step >>= 1; - } while (++bits <= BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH); -} - -uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, - int root_bits, - const uint16_t* const symbol_lists, - uint16_t* count) { - HuffmanCode code; /* current table entry */ - HuffmanCode* table; /* next available space in table */ - int len; /* current code length */ - int symbol; /* symbol index in original or sorted table */ - brotli_reg_t key; /* prefix code */ - brotli_reg_t key_step; /* prefix code addend */ - brotli_reg_t sub_key; /* 2nd level table prefix code */ - brotli_reg_t sub_key_step; /* 2nd level table prefix code addend */ - int step; /* step size to replicate values in current table */ - int table_bits; /* key length of current table */ - int table_size; /* size of current table */ - int total_size; /* sum of root table size and 2nd level table sizes */ - int max_length = -1; - int bits; - int bits_count; - - BROTLI_DCHECK(root_bits <= BROTLI_REVERSE_BITS_MAX); - BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH - root_bits <= - BROTLI_REVERSE_BITS_MAX); - - while (symbol_lists[max_length] == 0xFFFF) max_length--; - max_length += BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1; - - table = root_table; - table_bits = root_bits; - table_size = 1 << table_bits; - total_size = table_size; - - /* Fill in the root table. Reduce the table size to if possible, - and create the repetitions by memcpy. */ - if (table_bits > max_length) { - table_bits = max_length; - table_size = 1 << table_bits; - } - key = 0; - key_step = BROTLI_REVERSE_BITS_LOWEST; - bits = 1; - step = 2; - do { - symbol = bits - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); - for (bits_count = count[bits]; bits_count != 0; --bits_count) { - symbol = symbol_lists[symbol]; - code = ConstructHuffmanCode((uint8_t)bits, (uint16_t)symbol); - ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code); - key += key_step; - } - step <<= 1; - key_step >>= 1; - } while (++bits <= table_bits); - - /* If root_bits != table_bits then replicate to fill the remaining slots. */ - while (total_size != table_size) { - memcpy(&table[table_size], &table[0], - (size_t)table_size * sizeof(table[0])); - table_size <<= 1; - } - - /* Fill in 2nd level tables and add pointers to root table. */ - key_step = BROTLI_REVERSE_BITS_LOWEST >> (root_bits - 1); - sub_key = (BROTLI_REVERSE_BITS_LOWEST << 1); - sub_key_step = BROTLI_REVERSE_BITS_LOWEST; - for (len = root_bits + 1, step = 2; len <= max_length; ++len) { - symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); - for (; count[len] != 0; --count[len]) { - if (sub_key == (BROTLI_REVERSE_BITS_LOWEST << 1U)) { - table += table_size; - table_bits = NextTableBitSize(count, len, root_bits); - table_size = 1 << table_bits; - total_size += table_size; - sub_key = BrotliReverseBits(key); - key += key_step; - root_table[sub_key] = ConstructHuffmanCode( - (uint8_t)(table_bits + root_bits), - (uint16_t)(((size_t)(table - root_table)) - sub_key)); - sub_key = 0; - } - symbol = symbol_lists[symbol]; - code = ConstructHuffmanCode((uint8_t)(len - root_bits), (uint16_t)symbol); - ReplicateValue( - &table[BrotliReverseBits(sub_key)], step, table_size, code); - sub_key += sub_key_step; - } - step <<= 1; - sub_key_step >>= 1; - } - return (uint32_t)total_size; -} - -uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table, - int root_bits, - uint16_t* val, - uint32_t num_symbols) { - uint32_t table_size = 1; - const uint32_t goal_size = 1U << root_bits; - switch (num_symbols) { - case 0: - table[0] = ConstructHuffmanCode(0, val[0]); - break; - case 1: - if (val[1] > val[0]) { - table[0] = ConstructHuffmanCode(1, val[0]); - table[1] = ConstructHuffmanCode(1, val[1]); - } else { - table[0] = ConstructHuffmanCode(1, val[1]); - table[1] = ConstructHuffmanCode(1, val[0]); - } - table_size = 2; - break; - case 2: - table[0] = ConstructHuffmanCode(1, val[0]); - table[2] = ConstructHuffmanCode(1, val[0]); - if (val[2] > val[1]) { - table[1] = ConstructHuffmanCode(2, val[1]); - table[3] = ConstructHuffmanCode(2, val[2]); - } else { - table[1] = ConstructHuffmanCode(2, val[2]); - table[3] = ConstructHuffmanCode(2, val[1]); - } - table_size = 4; - break; - case 3: { - int i, k; - for (i = 0; i < 3; ++i) { - for (k = i + 1; k < 4; ++k) { - if (val[k] < val[i]) { - uint16_t t = val[k]; - val[k] = val[i]; - val[i] = t; - } - } - } - table[0] = ConstructHuffmanCode(2, val[0]); - table[2] = ConstructHuffmanCode(2, val[1]); - table[1] = ConstructHuffmanCode(2, val[2]); - table[3] = ConstructHuffmanCode(2, val[3]); - table_size = 4; - break; - } - case 4: { - if (val[3] < val[2]) { - uint16_t t = val[3]; - val[3] = val[2]; - val[2] = t; - } - table[0] = ConstructHuffmanCode(1, val[0]); - table[1] = ConstructHuffmanCode(2, val[1]); - table[2] = ConstructHuffmanCode(1, val[0]); - table[3] = ConstructHuffmanCode(3, val[2]); - table[4] = ConstructHuffmanCode(1, val[0]); - table[5] = ConstructHuffmanCode(2, val[1]); - table[6] = ConstructHuffmanCode(1, val[0]); - table[7] = ConstructHuffmanCode(3, val[3]); - table_size = 8; - break; - } - } - while (table_size != goal_size) { - memcpy(&table[table_size], &table[0], - (size_t)table_size * sizeof(table[0])); - table_size <<= 1; - } - return goal_size; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/dec/huffman.h b/src/deps/brotli/dec/huffman.h deleted file mode 100644 index 50360962c7347..0000000000000 --- a/src/deps/brotli/dec/huffman.h +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Utilities for building Huffman decoding tables. */ - -#ifndef BROTLI_DEC_HUFFMAN_H_ -#define BROTLI_DEC_HUFFMAN_H_ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define BROTLI_HUFFMAN_MAX_CODE_LENGTH 15 - -/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */ -#define BROTLI_HUFFMAN_MAX_SIZE_26 396 -/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */ -#define BROTLI_HUFFMAN_MAX_SIZE_258 632 -/* BROTLI_MAX_CONTEXT_MAP_SYMBOLS == 272 */ -#define BROTLI_HUFFMAN_MAX_SIZE_272 646 - -#define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5 - -#if ((defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_32)) && \ - BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0)) -#define BROTLI_HUFFMAN_CODE_FAST_LOAD -#endif - -#if !defined(BROTLI_HUFFMAN_CODE_FAST_LOAD) -/* Do not create this struct directly - use the ConstructHuffmanCode - * constructor below! */ -typedef struct { - uint8_t bits; /* number of bits used for this symbol */ - uint16_t value; /* symbol value or table offset */ -} HuffmanCode; - -static BROTLI_INLINE HuffmanCode ConstructHuffmanCode(const uint8_t bits, - const uint16_t value) { - HuffmanCode h; - h.bits = bits; - h.value = value; - return h; -} - -/* Please use the following macros to optimize HuffmanCode accesses in hot - * paths. - * - * For example, assuming |table| contains a HuffmanCode pointer: - * - * BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); - * BROTLI_HC_ADJUST_TABLE_INDEX(table, index_into_table); - * *bits = BROTLI_HC_GET_BITS(table); - * *value = BROTLI_HC_GET_VALUE(table); - * BROTLI_HC_ADJUST_TABLE_INDEX(table, offset); - * *bits2 = BROTLI_HC_GET_BITS(table); - * *value2 = BROTLI_HC_GET_VALUE(table); - * - */ - -#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H) -#define BROTLI_HC_ADJUST_TABLE_INDEX(H, V) H += (V) - -/* These must be given a HuffmanCode pointer! */ -#define BROTLI_HC_FAST_LOAD_BITS(H) (H->bits) -#define BROTLI_HC_FAST_LOAD_VALUE(H) (H->value) - -#else /* BROTLI_HUFFMAN_CODE_FAST_LOAD */ - -typedef BROTLI_ALIGNED(4) uint32_t HuffmanCode; - -static BROTLI_INLINE HuffmanCode ConstructHuffmanCode(const uint8_t bits, - const uint16_t value) { - return (HuffmanCode) ((value & 0xFFFF) << 16) | (bits & 0xFF); -} - -#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H) uint32_t __fastload_##H = (*H) -#define BROTLI_HC_ADJUST_TABLE_INDEX(H, V) H += (V); __fastload_##H = (*H) - -/* These must be given a HuffmanCode pointer! */ -#define BROTLI_HC_FAST_LOAD_BITS(H) ((__fastload_##H) & 0xFF) -#define BROTLI_HC_FAST_LOAD_VALUE(H) ((__fastload_##H) >> 16) -#endif /* BROTLI_HUFFMAN_CODE_FAST_LOAD */ - -/* Builds Huffman lookup table assuming code lengths are in symbol order. */ -BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table, - const uint8_t* const code_lengths, uint16_t* count); - -/* Builds Huffman lookup table assuming code lengths are in symbol order. - Returns size of resulting table. */ -BROTLI_INTERNAL uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, - int root_bits, const uint16_t* const symbol_lists, uint16_t* count); - -/* Builds a simple Huffman table. The |num_symbols| parameter is to be - interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, - 2 means 3 symbols, 3 means 4 symbols with lengths [2, 2, 2, 2], - 4 means 4 symbols with lengths [1, 2, 3, 3]. */ -BROTLI_INTERNAL uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table, - int root_bits, uint16_t* symbols, uint32_t num_symbols); - -/* Contains a collection of Huffman trees with the same alphabet size. */ -/* alphabet_size_limit is needed due to simple codes, since - log2(alphabet_size_max) could be greater than log2(alphabet_size_limit). */ -typedef struct { - HuffmanCode** htrees; - HuffmanCode* codes; - uint16_t alphabet_size_max; - uint16_t alphabet_size_limit; - uint16_t num_htrees; -} HuffmanTreeGroup; - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_DEC_HUFFMAN_H_ */ diff --git a/src/deps/brotli/dec/prefix.h b/src/deps/brotli/dec/prefix.h deleted file mode 100644 index e8acf0774006e..0000000000000 --- a/src/deps/brotli/dec/prefix.h +++ /dev/null @@ -1,733 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Lookup tables to map prefix codes to value ranges. This is used during - decoding of the block lengths, literal insertion lengths and copy lengths. */ - -#ifndef BROTLI_DEC_PREFIX_H_ -#define BROTLI_DEC_PREFIX_H_ - -#include - -#include "../common/constants.h" - -typedef struct CmdLutElement { - uint8_t insert_len_extra_bits; - uint8_t copy_len_extra_bits; - int8_t distance_code; - uint8_t context; - uint16_t insert_len_offset; - uint16_t copy_len_offset; -} CmdLutElement; - -static const CmdLutElement kCmdLut[BROTLI_NUM_COMMAND_SYMBOLS] = { - { 0x00, 0x00, 0, 0x00, 0x0000, 0x0002 }, - { 0x00, 0x00, 0, 0x01, 0x0000, 0x0003 }, - { 0x00, 0x00, 0, 0x02, 0x0000, 0x0004 }, - { 0x00, 0x00, 0, 0x03, 0x0000, 0x0005 }, - { 0x00, 0x00, 0, 0x03, 0x0000, 0x0006 }, - { 0x00, 0x00, 0, 0x03, 0x0000, 0x0007 }, - { 0x00, 0x00, 0, 0x03, 0x0000, 0x0008 }, - { 0x00, 0x00, 0, 0x03, 0x0000, 0x0009 }, - { 0x00, 0x00, 0, 0x00, 0x0001, 0x0002 }, - { 0x00, 0x00, 0, 0x01, 0x0001, 0x0003 }, - { 0x00, 0x00, 0, 0x02, 0x0001, 0x0004 }, - { 0x00, 0x00, 0, 0x03, 0x0001, 0x0005 }, - { 0x00, 0x00, 0, 0x03, 0x0001, 0x0006 }, - { 0x00, 0x00, 0, 0x03, 0x0001, 0x0007 }, - { 0x00, 0x00, 0, 0x03, 0x0001, 0x0008 }, - { 0x00, 0x00, 0, 0x03, 0x0001, 0x0009 }, - { 0x00, 0x00, 0, 0x00, 0x0002, 0x0002 }, - { 0x00, 0x00, 0, 0x01, 0x0002, 0x0003 }, - { 0x00, 0x00, 0, 0x02, 0x0002, 0x0004 }, - { 0x00, 0x00, 0, 0x03, 0x0002, 0x0005 }, - { 0x00, 0x00, 0, 0x03, 0x0002, 0x0006 }, - { 0x00, 0x00, 0, 0x03, 0x0002, 0x0007 }, - { 0x00, 0x00, 0, 0x03, 0x0002, 0x0008 }, - { 0x00, 0x00, 0, 0x03, 0x0002, 0x0009 }, - { 0x00, 0x00, 0, 0x00, 0x0003, 0x0002 }, - { 0x00, 0x00, 0, 0x01, 0x0003, 0x0003 }, - { 0x00, 0x00, 0, 0x02, 0x0003, 0x0004 }, - { 0x00, 0x00, 0, 0x03, 0x0003, 0x0005 }, - { 0x00, 0x00, 0, 0x03, 0x0003, 0x0006 }, - { 0x00, 0x00, 0, 0x03, 0x0003, 0x0007 }, - { 0x00, 0x00, 0, 0x03, 0x0003, 0x0008 }, - { 0x00, 0x00, 0, 0x03, 0x0003, 0x0009 }, - { 0x00, 0x00, 0, 0x00, 0x0004, 0x0002 }, - { 0x00, 0x00, 0, 0x01, 0x0004, 0x0003 }, - { 0x00, 0x00, 0, 0x02, 0x0004, 0x0004 }, - { 0x00, 0x00, 0, 0x03, 0x0004, 0x0005 }, - { 0x00, 0x00, 0, 0x03, 0x0004, 0x0006 }, - { 0x00, 0x00, 0, 0x03, 0x0004, 0x0007 }, - { 0x00, 0x00, 0, 0x03, 0x0004, 0x0008 }, - { 0x00, 0x00, 0, 0x03, 0x0004, 0x0009 }, - { 0x00, 0x00, 0, 0x00, 0x0005, 0x0002 }, - { 0x00, 0x00, 0, 0x01, 0x0005, 0x0003 }, - { 0x00, 0x00, 0, 0x02, 0x0005, 0x0004 }, - { 0x00, 0x00, 0, 0x03, 0x0005, 0x0005 }, - { 0x00, 0x00, 0, 0x03, 0x0005, 0x0006 }, - { 0x00, 0x00, 0, 0x03, 0x0005, 0x0007 }, - { 0x00, 0x00, 0, 0x03, 0x0005, 0x0008 }, - { 0x00, 0x00, 0, 0x03, 0x0005, 0x0009 }, - { 0x01, 0x00, 0, 0x00, 0x0006, 0x0002 }, - { 0x01, 0x00, 0, 0x01, 0x0006, 0x0003 }, - { 0x01, 0x00, 0, 0x02, 0x0006, 0x0004 }, - { 0x01, 0x00, 0, 0x03, 0x0006, 0x0005 }, - { 0x01, 0x00, 0, 0x03, 0x0006, 0x0006 }, - { 0x01, 0x00, 0, 0x03, 0x0006, 0x0007 }, - { 0x01, 0x00, 0, 0x03, 0x0006, 0x0008 }, - { 0x01, 0x00, 0, 0x03, 0x0006, 0x0009 }, - { 0x01, 0x00, 0, 0x00, 0x0008, 0x0002 }, - { 0x01, 0x00, 0, 0x01, 0x0008, 0x0003 }, - { 0x01, 0x00, 0, 0x02, 0x0008, 0x0004 }, - { 0x01, 0x00, 0, 0x03, 0x0008, 0x0005 }, - { 0x01, 0x00, 0, 0x03, 0x0008, 0x0006 }, - { 0x01, 0x00, 0, 0x03, 0x0008, 0x0007 }, - { 0x01, 0x00, 0, 0x03, 0x0008, 0x0008 }, - { 0x01, 0x00, 0, 0x03, 0x0008, 0x0009 }, - { 0x00, 0x01, 0, 0x03, 0x0000, 0x000a }, - { 0x00, 0x01, 0, 0x03, 0x0000, 0x000c }, - { 0x00, 0x02, 0, 0x03, 0x0000, 0x000e }, - { 0x00, 0x02, 0, 0x03, 0x0000, 0x0012 }, - { 0x00, 0x03, 0, 0x03, 0x0000, 0x0016 }, - { 0x00, 0x03, 0, 0x03, 0x0000, 0x001e }, - { 0x00, 0x04, 0, 0x03, 0x0000, 0x0026 }, - { 0x00, 0x04, 0, 0x03, 0x0000, 0x0036 }, - { 0x00, 0x01, 0, 0x03, 0x0001, 0x000a }, - { 0x00, 0x01, 0, 0x03, 0x0001, 0x000c }, - { 0x00, 0x02, 0, 0x03, 0x0001, 0x000e }, - { 0x00, 0x02, 0, 0x03, 0x0001, 0x0012 }, - { 0x00, 0x03, 0, 0x03, 0x0001, 0x0016 }, - { 0x00, 0x03, 0, 0x03, 0x0001, 0x001e }, - { 0x00, 0x04, 0, 0x03, 0x0001, 0x0026 }, - { 0x00, 0x04, 0, 0x03, 0x0001, 0x0036 }, - { 0x00, 0x01, 0, 0x03, 0x0002, 0x000a }, - { 0x00, 0x01, 0, 0x03, 0x0002, 0x000c }, - { 0x00, 0x02, 0, 0x03, 0x0002, 0x000e }, - { 0x00, 0x02, 0, 0x03, 0x0002, 0x0012 }, - { 0x00, 0x03, 0, 0x03, 0x0002, 0x0016 }, - { 0x00, 0x03, 0, 0x03, 0x0002, 0x001e }, - { 0x00, 0x04, 0, 0x03, 0x0002, 0x0026 }, - { 0x00, 0x04, 0, 0x03, 0x0002, 0x0036 }, - { 0x00, 0x01, 0, 0x03, 0x0003, 0x000a }, - { 0x00, 0x01, 0, 0x03, 0x0003, 0x000c }, - { 0x00, 0x02, 0, 0x03, 0x0003, 0x000e }, - { 0x00, 0x02, 0, 0x03, 0x0003, 0x0012 }, - { 0x00, 0x03, 0, 0x03, 0x0003, 0x0016 }, - { 0x00, 0x03, 0, 0x03, 0x0003, 0x001e }, - { 0x00, 0x04, 0, 0x03, 0x0003, 0x0026 }, - { 0x00, 0x04, 0, 0x03, 0x0003, 0x0036 }, - { 0x00, 0x01, 0, 0x03, 0x0004, 0x000a }, - { 0x00, 0x01, 0, 0x03, 0x0004, 0x000c }, - { 0x00, 0x02, 0, 0x03, 0x0004, 0x000e }, - { 0x00, 0x02, 0, 0x03, 0x0004, 0x0012 }, - { 0x00, 0x03, 0, 0x03, 0x0004, 0x0016 }, - { 0x00, 0x03, 0, 0x03, 0x0004, 0x001e }, - { 0x00, 0x04, 0, 0x03, 0x0004, 0x0026 }, - { 0x00, 0x04, 0, 0x03, 0x0004, 0x0036 }, - { 0x00, 0x01, 0, 0x03, 0x0005, 0x000a }, - { 0x00, 0x01, 0, 0x03, 0x0005, 0x000c }, - { 0x00, 0x02, 0, 0x03, 0x0005, 0x000e }, - { 0x00, 0x02, 0, 0x03, 0x0005, 0x0012 }, - { 0x00, 0x03, 0, 0x03, 0x0005, 0x0016 }, - { 0x00, 0x03, 0, 0x03, 0x0005, 0x001e }, - { 0x00, 0x04, 0, 0x03, 0x0005, 0x0026 }, - { 0x00, 0x04, 0, 0x03, 0x0005, 0x0036 }, - { 0x01, 0x01, 0, 0x03, 0x0006, 0x000a }, - { 0x01, 0x01, 0, 0x03, 0x0006, 0x000c }, - { 0x01, 0x02, 0, 0x03, 0x0006, 0x000e }, - { 0x01, 0x02, 0, 0x03, 0x0006, 0x0012 }, - { 0x01, 0x03, 0, 0x03, 0x0006, 0x0016 }, - { 0x01, 0x03, 0, 0x03, 0x0006, 0x001e }, - { 0x01, 0x04, 0, 0x03, 0x0006, 0x0026 }, - { 0x01, 0x04, 0, 0x03, 0x0006, 0x0036 }, - { 0x01, 0x01, 0, 0x03, 0x0008, 0x000a }, - { 0x01, 0x01, 0, 0x03, 0x0008, 0x000c }, - { 0x01, 0x02, 0, 0x03, 0x0008, 0x000e }, - { 0x01, 0x02, 0, 0x03, 0x0008, 0x0012 }, - { 0x01, 0x03, 0, 0x03, 0x0008, 0x0016 }, - { 0x01, 0x03, 0, 0x03, 0x0008, 0x001e }, - { 0x01, 0x04, 0, 0x03, 0x0008, 0x0026 }, - { 0x01, 0x04, 0, 0x03, 0x0008, 0x0036 }, - { 0x00, 0x00, -1, 0x00, 0x0000, 0x0002 }, - { 0x00, 0x00, -1, 0x01, 0x0000, 0x0003 }, - { 0x00, 0x00, -1, 0x02, 0x0000, 0x0004 }, - { 0x00, 0x00, -1, 0x03, 0x0000, 0x0005 }, - { 0x00, 0x00, -1, 0x03, 0x0000, 0x0006 }, - { 0x00, 0x00, -1, 0x03, 0x0000, 0x0007 }, - { 0x00, 0x00, -1, 0x03, 0x0000, 0x0008 }, - { 0x00, 0x00, -1, 0x03, 0x0000, 0x0009 }, - { 0x00, 0x00, -1, 0x00, 0x0001, 0x0002 }, - { 0x00, 0x00, -1, 0x01, 0x0001, 0x0003 }, - { 0x00, 0x00, -1, 0x02, 0x0001, 0x0004 }, - { 0x00, 0x00, -1, 0x03, 0x0001, 0x0005 }, - { 0x00, 0x00, -1, 0x03, 0x0001, 0x0006 }, - { 0x00, 0x00, -1, 0x03, 0x0001, 0x0007 }, - { 0x00, 0x00, -1, 0x03, 0x0001, 0x0008 }, - { 0x00, 0x00, -1, 0x03, 0x0001, 0x0009 }, - { 0x00, 0x00, -1, 0x00, 0x0002, 0x0002 }, - { 0x00, 0x00, -1, 0x01, 0x0002, 0x0003 }, - { 0x00, 0x00, -1, 0x02, 0x0002, 0x0004 }, - { 0x00, 0x00, -1, 0x03, 0x0002, 0x0005 }, - { 0x00, 0x00, -1, 0x03, 0x0002, 0x0006 }, - { 0x00, 0x00, -1, 0x03, 0x0002, 0x0007 }, - { 0x00, 0x00, -1, 0x03, 0x0002, 0x0008 }, - { 0x00, 0x00, -1, 0x03, 0x0002, 0x0009 }, - { 0x00, 0x00, -1, 0x00, 0x0003, 0x0002 }, - { 0x00, 0x00, -1, 0x01, 0x0003, 0x0003 }, - { 0x00, 0x00, -1, 0x02, 0x0003, 0x0004 }, - { 0x00, 0x00, -1, 0x03, 0x0003, 0x0005 }, - { 0x00, 0x00, -1, 0x03, 0x0003, 0x0006 }, - { 0x00, 0x00, -1, 0x03, 0x0003, 0x0007 }, - { 0x00, 0x00, -1, 0x03, 0x0003, 0x0008 }, - { 0x00, 0x00, -1, 0x03, 0x0003, 0x0009 }, - { 0x00, 0x00, -1, 0x00, 0x0004, 0x0002 }, - { 0x00, 0x00, -1, 0x01, 0x0004, 0x0003 }, - { 0x00, 0x00, -1, 0x02, 0x0004, 0x0004 }, - { 0x00, 0x00, -1, 0x03, 0x0004, 0x0005 }, - { 0x00, 0x00, -1, 0x03, 0x0004, 0x0006 }, - { 0x00, 0x00, -1, 0x03, 0x0004, 0x0007 }, - { 0x00, 0x00, -1, 0x03, 0x0004, 0x0008 }, - { 0x00, 0x00, -1, 0x03, 0x0004, 0x0009 }, - { 0x00, 0x00, -1, 0x00, 0x0005, 0x0002 }, - { 0x00, 0x00, -1, 0x01, 0x0005, 0x0003 }, - { 0x00, 0x00, -1, 0x02, 0x0005, 0x0004 }, - { 0x00, 0x00, -1, 0x03, 0x0005, 0x0005 }, - { 0x00, 0x00, -1, 0x03, 0x0005, 0x0006 }, - { 0x00, 0x00, -1, 0x03, 0x0005, 0x0007 }, - { 0x00, 0x00, -1, 0x03, 0x0005, 0x0008 }, - { 0x00, 0x00, -1, 0x03, 0x0005, 0x0009 }, - { 0x01, 0x00, -1, 0x00, 0x0006, 0x0002 }, - { 0x01, 0x00, -1, 0x01, 0x0006, 0x0003 }, - { 0x01, 0x00, -1, 0x02, 0x0006, 0x0004 }, - { 0x01, 0x00, -1, 0x03, 0x0006, 0x0005 }, - { 0x01, 0x00, -1, 0x03, 0x0006, 0x0006 }, - { 0x01, 0x00, -1, 0x03, 0x0006, 0x0007 }, - { 0x01, 0x00, -1, 0x03, 0x0006, 0x0008 }, - { 0x01, 0x00, -1, 0x03, 0x0006, 0x0009 }, - { 0x01, 0x00, -1, 0x00, 0x0008, 0x0002 }, - { 0x01, 0x00, -1, 0x01, 0x0008, 0x0003 }, - { 0x01, 0x00, -1, 0x02, 0x0008, 0x0004 }, - { 0x01, 0x00, -1, 0x03, 0x0008, 0x0005 }, - { 0x01, 0x00, -1, 0x03, 0x0008, 0x0006 }, - { 0x01, 0x00, -1, 0x03, 0x0008, 0x0007 }, - { 0x01, 0x00, -1, 0x03, 0x0008, 0x0008 }, - { 0x01, 0x00, -1, 0x03, 0x0008, 0x0009 }, - { 0x00, 0x01, -1, 0x03, 0x0000, 0x000a }, - { 0x00, 0x01, -1, 0x03, 0x0000, 0x000c }, - { 0x00, 0x02, -1, 0x03, 0x0000, 0x000e }, - { 0x00, 0x02, -1, 0x03, 0x0000, 0x0012 }, - { 0x00, 0x03, -1, 0x03, 0x0000, 0x0016 }, - { 0x00, 0x03, -1, 0x03, 0x0000, 0x001e }, - { 0x00, 0x04, -1, 0x03, 0x0000, 0x0026 }, - { 0x00, 0x04, -1, 0x03, 0x0000, 0x0036 }, - { 0x00, 0x01, -1, 0x03, 0x0001, 0x000a }, - { 0x00, 0x01, -1, 0x03, 0x0001, 0x000c }, - { 0x00, 0x02, -1, 0x03, 0x0001, 0x000e }, - { 0x00, 0x02, -1, 0x03, 0x0001, 0x0012 }, - { 0x00, 0x03, -1, 0x03, 0x0001, 0x0016 }, - { 0x00, 0x03, -1, 0x03, 0x0001, 0x001e }, - { 0x00, 0x04, -1, 0x03, 0x0001, 0x0026 }, - { 0x00, 0x04, -1, 0x03, 0x0001, 0x0036 }, - { 0x00, 0x01, -1, 0x03, 0x0002, 0x000a }, - { 0x00, 0x01, -1, 0x03, 0x0002, 0x000c }, - { 0x00, 0x02, -1, 0x03, 0x0002, 0x000e }, - { 0x00, 0x02, -1, 0x03, 0x0002, 0x0012 }, - { 0x00, 0x03, -1, 0x03, 0x0002, 0x0016 }, - { 0x00, 0x03, -1, 0x03, 0x0002, 0x001e }, - { 0x00, 0x04, -1, 0x03, 0x0002, 0x0026 }, - { 0x00, 0x04, -1, 0x03, 0x0002, 0x0036 }, - { 0x00, 0x01, -1, 0x03, 0x0003, 0x000a }, - { 0x00, 0x01, -1, 0x03, 0x0003, 0x000c }, - { 0x00, 0x02, -1, 0x03, 0x0003, 0x000e }, - { 0x00, 0x02, -1, 0x03, 0x0003, 0x0012 }, - { 0x00, 0x03, -1, 0x03, 0x0003, 0x0016 }, - { 0x00, 0x03, -1, 0x03, 0x0003, 0x001e }, - { 0x00, 0x04, -1, 0x03, 0x0003, 0x0026 }, - { 0x00, 0x04, -1, 0x03, 0x0003, 0x0036 }, - { 0x00, 0x01, -1, 0x03, 0x0004, 0x000a }, - { 0x00, 0x01, -1, 0x03, 0x0004, 0x000c }, - { 0x00, 0x02, -1, 0x03, 0x0004, 0x000e }, - { 0x00, 0x02, -1, 0x03, 0x0004, 0x0012 }, - { 0x00, 0x03, -1, 0x03, 0x0004, 0x0016 }, - { 0x00, 0x03, -1, 0x03, 0x0004, 0x001e }, - { 0x00, 0x04, -1, 0x03, 0x0004, 0x0026 }, - { 0x00, 0x04, -1, 0x03, 0x0004, 0x0036 }, - { 0x00, 0x01, -1, 0x03, 0x0005, 0x000a }, - { 0x00, 0x01, -1, 0x03, 0x0005, 0x000c }, - { 0x00, 0x02, -1, 0x03, 0x0005, 0x000e }, - { 0x00, 0x02, -1, 0x03, 0x0005, 0x0012 }, - { 0x00, 0x03, -1, 0x03, 0x0005, 0x0016 }, - { 0x00, 0x03, -1, 0x03, 0x0005, 0x001e }, - { 0x00, 0x04, -1, 0x03, 0x0005, 0x0026 }, - { 0x00, 0x04, -1, 0x03, 0x0005, 0x0036 }, - { 0x01, 0x01, -1, 0x03, 0x0006, 0x000a }, - { 0x01, 0x01, -1, 0x03, 0x0006, 0x000c }, - { 0x01, 0x02, -1, 0x03, 0x0006, 0x000e }, - { 0x01, 0x02, -1, 0x03, 0x0006, 0x0012 }, - { 0x01, 0x03, -1, 0x03, 0x0006, 0x0016 }, - { 0x01, 0x03, -1, 0x03, 0x0006, 0x001e }, - { 0x01, 0x04, -1, 0x03, 0x0006, 0x0026 }, - { 0x01, 0x04, -1, 0x03, 0x0006, 0x0036 }, - { 0x01, 0x01, -1, 0x03, 0x0008, 0x000a }, - { 0x01, 0x01, -1, 0x03, 0x0008, 0x000c }, - { 0x01, 0x02, -1, 0x03, 0x0008, 0x000e }, - { 0x01, 0x02, -1, 0x03, 0x0008, 0x0012 }, - { 0x01, 0x03, -1, 0x03, 0x0008, 0x0016 }, - { 0x01, 0x03, -1, 0x03, 0x0008, 0x001e }, - { 0x01, 0x04, -1, 0x03, 0x0008, 0x0026 }, - { 0x01, 0x04, -1, 0x03, 0x0008, 0x0036 }, - { 0x02, 0x00, -1, 0x00, 0x000a, 0x0002 }, - { 0x02, 0x00, -1, 0x01, 0x000a, 0x0003 }, - { 0x02, 0x00, -1, 0x02, 0x000a, 0x0004 }, - { 0x02, 0x00, -1, 0x03, 0x000a, 0x0005 }, - { 0x02, 0x00, -1, 0x03, 0x000a, 0x0006 }, - { 0x02, 0x00, -1, 0x03, 0x000a, 0x0007 }, - { 0x02, 0x00, -1, 0x03, 0x000a, 0x0008 }, - { 0x02, 0x00, -1, 0x03, 0x000a, 0x0009 }, - { 0x02, 0x00, -1, 0x00, 0x000e, 0x0002 }, - { 0x02, 0x00, -1, 0x01, 0x000e, 0x0003 }, - { 0x02, 0x00, -1, 0x02, 0x000e, 0x0004 }, - { 0x02, 0x00, -1, 0x03, 0x000e, 0x0005 }, - { 0x02, 0x00, -1, 0x03, 0x000e, 0x0006 }, - { 0x02, 0x00, -1, 0x03, 0x000e, 0x0007 }, - { 0x02, 0x00, -1, 0x03, 0x000e, 0x0008 }, - { 0x02, 0x00, -1, 0x03, 0x000e, 0x0009 }, - { 0x03, 0x00, -1, 0x00, 0x0012, 0x0002 }, - { 0x03, 0x00, -1, 0x01, 0x0012, 0x0003 }, - { 0x03, 0x00, -1, 0x02, 0x0012, 0x0004 }, - { 0x03, 0x00, -1, 0x03, 0x0012, 0x0005 }, - { 0x03, 0x00, -1, 0x03, 0x0012, 0x0006 }, - { 0x03, 0x00, -1, 0x03, 0x0012, 0x0007 }, - { 0x03, 0x00, -1, 0x03, 0x0012, 0x0008 }, - { 0x03, 0x00, -1, 0x03, 0x0012, 0x0009 }, - { 0x03, 0x00, -1, 0x00, 0x001a, 0x0002 }, - { 0x03, 0x00, -1, 0x01, 0x001a, 0x0003 }, - { 0x03, 0x00, -1, 0x02, 0x001a, 0x0004 }, - { 0x03, 0x00, -1, 0x03, 0x001a, 0x0005 }, - { 0x03, 0x00, -1, 0x03, 0x001a, 0x0006 }, - { 0x03, 0x00, -1, 0x03, 0x001a, 0x0007 }, - { 0x03, 0x00, -1, 0x03, 0x001a, 0x0008 }, - { 0x03, 0x00, -1, 0x03, 0x001a, 0x0009 }, - { 0x04, 0x00, -1, 0x00, 0x0022, 0x0002 }, - { 0x04, 0x00, -1, 0x01, 0x0022, 0x0003 }, - { 0x04, 0x00, -1, 0x02, 0x0022, 0x0004 }, - { 0x04, 0x00, -1, 0x03, 0x0022, 0x0005 }, - { 0x04, 0x00, -1, 0x03, 0x0022, 0x0006 }, - { 0x04, 0x00, -1, 0x03, 0x0022, 0x0007 }, - { 0x04, 0x00, -1, 0x03, 0x0022, 0x0008 }, - { 0x04, 0x00, -1, 0x03, 0x0022, 0x0009 }, - { 0x04, 0x00, -1, 0x00, 0x0032, 0x0002 }, - { 0x04, 0x00, -1, 0x01, 0x0032, 0x0003 }, - { 0x04, 0x00, -1, 0x02, 0x0032, 0x0004 }, - { 0x04, 0x00, -1, 0x03, 0x0032, 0x0005 }, - { 0x04, 0x00, -1, 0x03, 0x0032, 0x0006 }, - { 0x04, 0x00, -1, 0x03, 0x0032, 0x0007 }, - { 0x04, 0x00, -1, 0x03, 0x0032, 0x0008 }, - { 0x04, 0x00, -1, 0x03, 0x0032, 0x0009 }, - { 0x05, 0x00, -1, 0x00, 0x0042, 0x0002 }, - { 0x05, 0x00, -1, 0x01, 0x0042, 0x0003 }, - { 0x05, 0x00, -1, 0x02, 0x0042, 0x0004 }, - { 0x05, 0x00, -1, 0x03, 0x0042, 0x0005 }, - { 0x05, 0x00, -1, 0x03, 0x0042, 0x0006 }, - { 0x05, 0x00, -1, 0x03, 0x0042, 0x0007 }, - { 0x05, 0x00, -1, 0x03, 0x0042, 0x0008 }, - { 0x05, 0x00, -1, 0x03, 0x0042, 0x0009 }, - { 0x05, 0x00, -1, 0x00, 0x0062, 0x0002 }, - { 0x05, 0x00, -1, 0x01, 0x0062, 0x0003 }, - { 0x05, 0x00, -1, 0x02, 0x0062, 0x0004 }, - { 0x05, 0x00, -1, 0x03, 0x0062, 0x0005 }, - { 0x05, 0x00, -1, 0x03, 0x0062, 0x0006 }, - { 0x05, 0x00, -1, 0x03, 0x0062, 0x0007 }, - { 0x05, 0x00, -1, 0x03, 0x0062, 0x0008 }, - { 0x05, 0x00, -1, 0x03, 0x0062, 0x0009 }, - { 0x02, 0x01, -1, 0x03, 0x000a, 0x000a }, - { 0x02, 0x01, -1, 0x03, 0x000a, 0x000c }, - { 0x02, 0x02, -1, 0x03, 0x000a, 0x000e }, - { 0x02, 0x02, -1, 0x03, 0x000a, 0x0012 }, - { 0x02, 0x03, -1, 0x03, 0x000a, 0x0016 }, - { 0x02, 0x03, -1, 0x03, 0x000a, 0x001e }, - { 0x02, 0x04, -1, 0x03, 0x000a, 0x0026 }, - { 0x02, 0x04, -1, 0x03, 0x000a, 0x0036 }, - { 0x02, 0x01, -1, 0x03, 0x000e, 0x000a }, - { 0x02, 0x01, -1, 0x03, 0x000e, 0x000c }, - { 0x02, 0x02, -1, 0x03, 0x000e, 0x000e }, - { 0x02, 0x02, -1, 0x03, 0x000e, 0x0012 }, - { 0x02, 0x03, -1, 0x03, 0x000e, 0x0016 }, - { 0x02, 0x03, -1, 0x03, 0x000e, 0x001e }, - { 0x02, 0x04, -1, 0x03, 0x000e, 0x0026 }, - { 0x02, 0x04, -1, 0x03, 0x000e, 0x0036 }, - { 0x03, 0x01, -1, 0x03, 0x0012, 0x000a }, - { 0x03, 0x01, -1, 0x03, 0x0012, 0x000c }, - { 0x03, 0x02, -1, 0x03, 0x0012, 0x000e }, - { 0x03, 0x02, -1, 0x03, 0x0012, 0x0012 }, - { 0x03, 0x03, -1, 0x03, 0x0012, 0x0016 }, - { 0x03, 0x03, -1, 0x03, 0x0012, 0x001e }, - { 0x03, 0x04, -1, 0x03, 0x0012, 0x0026 }, - { 0x03, 0x04, -1, 0x03, 0x0012, 0x0036 }, - { 0x03, 0x01, -1, 0x03, 0x001a, 0x000a }, - { 0x03, 0x01, -1, 0x03, 0x001a, 0x000c }, - { 0x03, 0x02, -1, 0x03, 0x001a, 0x000e }, - { 0x03, 0x02, -1, 0x03, 0x001a, 0x0012 }, - { 0x03, 0x03, -1, 0x03, 0x001a, 0x0016 }, - { 0x03, 0x03, -1, 0x03, 0x001a, 0x001e }, - { 0x03, 0x04, -1, 0x03, 0x001a, 0x0026 }, - { 0x03, 0x04, -1, 0x03, 0x001a, 0x0036 }, - { 0x04, 0x01, -1, 0x03, 0x0022, 0x000a }, - { 0x04, 0x01, -1, 0x03, 0x0022, 0x000c }, - { 0x04, 0x02, -1, 0x03, 0x0022, 0x000e }, - { 0x04, 0x02, -1, 0x03, 0x0022, 0x0012 }, - { 0x04, 0x03, -1, 0x03, 0x0022, 0x0016 }, - { 0x04, 0x03, -1, 0x03, 0x0022, 0x001e }, - { 0x04, 0x04, -1, 0x03, 0x0022, 0x0026 }, - { 0x04, 0x04, -1, 0x03, 0x0022, 0x0036 }, - { 0x04, 0x01, -1, 0x03, 0x0032, 0x000a }, - { 0x04, 0x01, -1, 0x03, 0x0032, 0x000c }, - { 0x04, 0x02, -1, 0x03, 0x0032, 0x000e }, - { 0x04, 0x02, -1, 0x03, 0x0032, 0x0012 }, - { 0x04, 0x03, -1, 0x03, 0x0032, 0x0016 }, - { 0x04, 0x03, -1, 0x03, 0x0032, 0x001e }, - { 0x04, 0x04, -1, 0x03, 0x0032, 0x0026 }, - { 0x04, 0x04, -1, 0x03, 0x0032, 0x0036 }, - { 0x05, 0x01, -1, 0x03, 0x0042, 0x000a }, - { 0x05, 0x01, -1, 0x03, 0x0042, 0x000c }, - { 0x05, 0x02, -1, 0x03, 0x0042, 0x000e }, - { 0x05, 0x02, -1, 0x03, 0x0042, 0x0012 }, - { 0x05, 0x03, -1, 0x03, 0x0042, 0x0016 }, - { 0x05, 0x03, -1, 0x03, 0x0042, 0x001e }, - { 0x05, 0x04, -1, 0x03, 0x0042, 0x0026 }, - { 0x05, 0x04, -1, 0x03, 0x0042, 0x0036 }, - { 0x05, 0x01, -1, 0x03, 0x0062, 0x000a }, - { 0x05, 0x01, -1, 0x03, 0x0062, 0x000c }, - { 0x05, 0x02, -1, 0x03, 0x0062, 0x000e }, - { 0x05, 0x02, -1, 0x03, 0x0062, 0x0012 }, - { 0x05, 0x03, -1, 0x03, 0x0062, 0x0016 }, - { 0x05, 0x03, -1, 0x03, 0x0062, 0x001e }, - { 0x05, 0x04, -1, 0x03, 0x0062, 0x0026 }, - { 0x05, 0x04, -1, 0x03, 0x0062, 0x0036 }, - { 0x00, 0x05, -1, 0x03, 0x0000, 0x0046 }, - { 0x00, 0x05, -1, 0x03, 0x0000, 0x0066 }, - { 0x00, 0x06, -1, 0x03, 0x0000, 0x0086 }, - { 0x00, 0x07, -1, 0x03, 0x0000, 0x00c6 }, - { 0x00, 0x08, -1, 0x03, 0x0000, 0x0146 }, - { 0x00, 0x09, -1, 0x03, 0x0000, 0x0246 }, - { 0x00, 0x0a, -1, 0x03, 0x0000, 0x0446 }, - { 0x00, 0x18, -1, 0x03, 0x0000, 0x0846 }, - { 0x00, 0x05, -1, 0x03, 0x0001, 0x0046 }, - { 0x00, 0x05, -1, 0x03, 0x0001, 0x0066 }, - { 0x00, 0x06, -1, 0x03, 0x0001, 0x0086 }, - { 0x00, 0x07, -1, 0x03, 0x0001, 0x00c6 }, - { 0x00, 0x08, -1, 0x03, 0x0001, 0x0146 }, - { 0x00, 0x09, -1, 0x03, 0x0001, 0x0246 }, - { 0x00, 0x0a, -1, 0x03, 0x0001, 0x0446 }, - { 0x00, 0x18, -1, 0x03, 0x0001, 0x0846 }, - { 0x00, 0x05, -1, 0x03, 0x0002, 0x0046 }, - { 0x00, 0x05, -1, 0x03, 0x0002, 0x0066 }, - { 0x00, 0x06, -1, 0x03, 0x0002, 0x0086 }, - { 0x00, 0x07, -1, 0x03, 0x0002, 0x00c6 }, - { 0x00, 0x08, -1, 0x03, 0x0002, 0x0146 }, - { 0x00, 0x09, -1, 0x03, 0x0002, 0x0246 }, - { 0x00, 0x0a, -1, 0x03, 0x0002, 0x0446 }, - { 0x00, 0x18, -1, 0x03, 0x0002, 0x0846 }, - { 0x00, 0x05, -1, 0x03, 0x0003, 0x0046 }, - { 0x00, 0x05, -1, 0x03, 0x0003, 0x0066 }, - { 0x00, 0x06, -1, 0x03, 0x0003, 0x0086 }, - { 0x00, 0x07, -1, 0x03, 0x0003, 0x00c6 }, - { 0x00, 0x08, -1, 0x03, 0x0003, 0x0146 }, - { 0x00, 0x09, -1, 0x03, 0x0003, 0x0246 }, - { 0x00, 0x0a, -1, 0x03, 0x0003, 0x0446 }, - { 0x00, 0x18, -1, 0x03, 0x0003, 0x0846 }, - { 0x00, 0x05, -1, 0x03, 0x0004, 0x0046 }, - { 0x00, 0x05, -1, 0x03, 0x0004, 0x0066 }, - { 0x00, 0x06, -1, 0x03, 0x0004, 0x0086 }, - { 0x00, 0x07, -1, 0x03, 0x0004, 0x00c6 }, - { 0x00, 0x08, -1, 0x03, 0x0004, 0x0146 }, - { 0x00, 0x09, -1, 0x03, 0x0004, 0x0246 }, - { 0x00, 0x0a, -1, 0x03, 0x0004, 0x0446 }, - { 0x00, 0x18, -1, 0x03, 0x0004, 0x0846 }, - { 0x00, 0x05, -1, 0x03, 0x0005, 0x0046 }, - { 0x00, 0x05, -1, 0x03, 0x0005, 0x0066 }, - { 0x00, 0x06, -1, 0x03, 0x0005, 0x0086 }, - { 0x00, 0x07, -1, 0x03, 0x0005, 0x00c6 }, - { 0x00, 0x08, -1, 0x03, 0x0005, 0x0146 }, - { 0x00, 0x09, -1, 0x03, 0x0005, 0x0246 }, - { 0x00, 0x0a, -1, 0x03, 0x0005, 0x0446 }, - { 0x00, 0x18, -1, 0x03, 0x0005, 0x0846 }, - { 0x01, 0x05, -1, 0x03, 0x0006, 0x0046 }, - { 0x01, 0x05, -1, 0x03, 0x0006, 0x0066 }, - { 0x01, 0x06, -1, 0x03, 0x0006, 0x0086 }, - { 0x01, 0x07, -1, 0x03, 0x0006, 0x00c6 }, - { 0x01, 0x08, -1, 0x03, 0x0006, 0x0146 }, - { 0x01, 0x09, -1, 0x03, 0x0006, 0x0246 }, - { 0x01, 0x0a, -1, 0x03, 0x0006, 0x0446 }, - { 0x01, 0x18, -1, 0x03, 0x0006, 0x0846 }, - { 0x01, 0x05, -1, 0x03, 0x0008, 0x0046 }, - { 0x01, 0x05, -1, 0x03, 0x0008, 0x0066 }, - { 0x01, 0x06, -1, 0x03, 0x0008, 0x0086 }, - { 0x01, 0x07, -1, 0x03, 0x0008, 0x00c6 }, - { 0x01, 0x08, -1, 0x03, 0x0008, 0x0146 }, - { 0x01, 0x09, -1, 0x03, 0x0008, 0x0246 }, - { 0x01, 0x0a, -1, 0x03, 0x0008, 0x0446 }, - { 0x01, 0x18, -1, 0x03, 0x0008, 0x0846 }, - { 0x06, 0x00, -1, 0x00, 0x0082, 0x0002 }, - { 0x06, 0x00, -1, 0x01, 0x0082, 0x0003 }, - { 0x06, 0x00, -1, 0x02, 0x0082, 0x0004 }, - { 0x06, 0x00, -1, 0x03, 0x0082, 0x0005 }, - { 0x06, 0x00, -1, 0x03, 0x0082, 0x0006 }, - { 0x06, 0x00, -1, 0x03, 0x0082, 0x0007 }, - { 0x06, 0x00, -1, 0x03, 0x0082, 0x0008 }, - { 0x06, 0x00, -1, 0x03, 0x0082, 0x0009 }, - { 0x07, 0x00, -1, 0x00, 0x00c2, 0x0002 }, - { 0x07, 0x00, -1, 0x01, 0x00c2, 0x0003 }, - { 0x07, 0x00, -1, 0x02, 0x00c2, 0x0004 }, - { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0005 }, - { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0006 }, - { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0007 }, - { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0008 }, - { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0009 }, - { 0x08, 0x00, -1, 0x00, 0x0142, 0x0002 }, - { 0x08, 0x00, -1, 0x01, 0x0142, 0x0003 }, - { 0x08, 0x00, -1, 0x02, 0x0142, 0x0004 }, - { 0x08, 0x00, -1, 0x03, 0x0142, 0x0005 }, - { 0x08, 0x00, -1, 0x03, 0x0142, 0x0006 }, - { 0x08, 0x00, -1, 0x03, 0x0142, 0x0007 }, - { 0x08, 0x00, -1, 0x03, 0x0142, 0x0008 }, - { 0x08, 0x00, -1, 0x03, 0x0142, 0x0009 }, - { 0x09, 0x00, -1, 0x00, 0x0242, 0x0002 }, - { 0x09, 0x00, -1, 0x01, 0x0242, 0x0003 }, - { 0x09, 0x00, -1, 0x02, 0x0242, 0x0004 }, - { 0x09, 0x00, -1, 0x03, 0x0242, 0x0005 }, - { 0x09, 0x00, -1, 0x03, 0x0242, 0x0006 }, - { 0x09, 0x00, -1, 0x03, 0x0242, 0x0007 }, - { 0x09, 0x00, -1, 0x03, 0x0242, 0x0008 }, - { 0x09, 0x00, -1, 0x03, 0x0242, 0x0009 }, - { 0x0a, 0x00, -1, 0x00, 0x0442, 0x0002 }, - { 0x0a, 0x00, -1, 0x01, 0x0442, 0x0003 }, - { 0x0a, 0x00, -1, 0x02, 0x0442, 0x0004 }, - { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0005 }, - { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0006 }, - { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0007 }, - { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0008 }, - { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0009 }, - { 0x0c, 0x00, -1, 0x00, 0x0842, 0x0002 }, - { 0x0c, 0x00, -1, 0x01, 0x0842, 0x0003 }, - { 0x0c, 0x00, -1, 0x02, 0x0842, 0x0004 }, - { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0005 }, - { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0006 }, - { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0007 }, - { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0008 }, - { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0009 }, - { 0x0e, 0x00, -1, 0x00, 0x1842, 0x0002 }, - { 0x0e, 0x00, -1, 0x01, 0x1842, 0x0003 }, - { 0x0e, 0x00, -1, 0x02, 0x1842, 0x0004 }, - { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0005 }, - { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0006 }, - { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0007 }, - { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0008 }, - { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0009 }, - { 0x18, 0x00, -1, 0x00, 0x5842, 0x0002 }, - { 0x18, 0x00, -1, 0x01, 0x5842, 0x0003 }, - { 0x18, 0x00, -1, 0x02, 0x5842, 0x0004 }, - { 0x18, 0x00, -1, 0x03, 0x5842, 0x0005 }, - { 0x18, 0x00, -1, 0x03, 0x5842, 0x0006 }, - { 0x18, 0x00, -1, 0x03, 0x5842, 0x0007 }, - { 0x18, 0x00, -1, 0x03, 0x5842, 0x0008 }, - { 0x18, 0x00, -1, 0x03, 0x5842, 0x0009 }, - { 0x02, 0x05, -1, 0x03, 0x000a, 0x0046 }, - { 0x02, 0x05, -1, 0x03, 0x000a, 0x0066 }, - { 0x02, 0x06, -1, 0x03, 0x000a, 0x0086 }, - { 0x02, 0x07, -1, 0x03, 0x000a, 0x00c6 }, - { 0x02, 0x08, -1, 0x03, 0x000a, 0x0146 }, - { 0x02, 0x09, -1, 0x03, 0x000a, 0x0246 }, - { 0x02, 0x0a, -1, 0x03, 0x000a, 0x0446 }, - { 0x02, 0x18, -1, 0x03, 0x000a, 0x0846 }, - { 0x02, 0x05, -1, 0x03, 0x000e, 0x0046 }, - { 0x02, 0x05, -1, 0x03, 0x000e, 0x0066 }, - { 0x02, 0x06, -1, 0x03, 0x000e, 0x0086 }, - { 0x02, 0x07, -1, 0x03, 0x000e, 0x00c6 }, - { 0x02, 0x08, -1, 0x03, 0x000e, 0x0146 }, - { 0x02, 0x09, -1, 0x03, 0x000e, 0x0246 }, - { 0x02, 0x0a, -1, 0x03, 0x000e, 0x0446 }, - { 0x02, 0x18, -1, 0x03, 0x000e, 0x0846 }, - { 0x03, 0x05, -1, 0x03, 0x0012, 0x0046 }, - { 0x03, 0x05, -1, 0x03, 0x0012, 0x0066 }, - { 0x03, 0x06, -1, 0x03, 0x0012, 0x0086 }, - { 0x03, 0x07, -1, 0x03, 0x0012, 0x00c6 }, - { 0x03, 0x08, -1, 0x03, 0x0012, 0x0146 }, - { 0x03, 0x09, -1, 0x03, 0x0012, 0x0246 }, - { 0x03, 0x0a, -1, 0x03, 0x0012, 0x0446 }, - { 0x03, 0x18, -1, 0x03, 0x0012, 0x0846 }, - { 0x03, 0x05, -1, 0x03, 0x001a, 0x0046 }, - { 0x03, 0x05, -1, 0x03, 0x001a, 0x0066 }, - { 0x03, 0x06, -1, 0x03, 0x001a, 0x0086 }, - { 0x03, 0x07, -1, 0x03, 0x001a, 0x00c6 }, - { 0x03, 0x08, -1, 0x03, 0x001a, 0x0146 }, - { 0x03, 0x09, -1, 0x03, 0x001a, 0x0246 }, - { 0x03, 0x0a, -1, 0x03, 0x001a, 0x0446 }, - { 0x03, 0x18, -1, 0x03, 0x001a, 0x0846 }, - { 0x04, 0x05, -1, 0x03, 0x0022, 0x0046 }, - { 0x04, 0x05, -1, 0x03, 0x0022, 0x0066 }, - { 0x04, 0x06, -1, 0x03, 0x0022, 0x0086 }, - { 0x04, 0x07, -1, 0x03, 0x0022, 0x00c6 }, - { 0x04, 0x08, -1, 0x03, 0x0022, 0x0146 }, - { 0x04, 0x09, -1, 0x03, 0x0022, 0x0246 }, - { 0x04, 0x0a, -1, 0x03, 0x0022, 0x0446 }, - { 0x04, 0x18, -1, 0x03, 0x0022, 0x0846 }, - { 0x04, 0x05, -1, 0x03, 0x0032, 0x0046 }, - { 0x04, 0x05, -1, 0x03, 0x0032, 0x0066 }, - { 0x04, 0x06, -1, 0x03, 0x0032, 0x0086 }, - { 0x04, 0x07, -1, 0x03, 0x0032, 0x00c6 }, - { 0x04, 0x08, -1, 0x03, 0x0032, 0x0146 }, - { 0x04, 0x09, -1, 0x03, 0x0032, 0x0246 }, - { 0x04, 0x0a, -1, 0x03, 0x0032, 0x0446 }, - { 0x04, 0x18, -1, 0x03, 0x0032, 0x0846 }, - { 0x05, 0x05, -1, 0x03, 0x0042, 0x0046 }, - { 0x05, 0x05, -1, 0x03, 0x0042, 0x0066 }, - { 0x05, 0x06, -1, 0x03, 0x0042, 0x0086 }, - { 0x05, 0x07, -1, 0x03, 0x0042, 0x00c6 }, - { 0x05, 0x08, -1, 0x03, 0x0042, 0x0146 }, - { 0x05, 0x09, -1, 0x03, 0x0042, 0x0246 }, - { 0x05, 0x0a, -1, 0x03, 0x0042, 0x0446 }, - { 0x05, 0x18, -1, 0x03, 0x0042, 0x0846 }, - { 0x05, 0x05, -1, 0x03, 0x0062, 0x0046 }, - { 0x05, 0x05, -1, 0x03, 0x0062, 0x0066 }, - { 0x05, 0x06, -1, 0x03, 0x0062, 0x0086 }, - { 0x05, 0x07, -1, 0x03, 0x0062, 0x00c6 }, - { 0x05, 0x08, -1, 0x03, 0x0062, 0x0146 }, - { 0x05, 0x09, -1, 0x03, 0x0062, 0x0246 }, - { 0x05, 0x0a, -1, 0x03, 0x0062, 0x0446 }, - { 0x05, 0x18, -1, 0x03, 0x0062, 0x0846 }, - { 0x06, 0x01, -1, 0x03, 0x0082, 0x000a }, - { 0x06, 0x01, -1, 0x03, 0x0082, 0x000c }, - { 0x06, 0x02, -1, 0x03, 0x0082, 0x000e }, - { 0x06, 0x02, -1, 0x03, 0x0082, 0x0012 }, - { 0x06, 0x03, -1, 0x03, 0x0082, 0x0016 }, - { 0x06, 0x03, -1, 0x03, 0x0082, 0x001e }, - { 0x06, 0x04, -1, 0x03, 0x0082, 0x0026 }, - { 0x06, 0x04, -1, 0x03, 0x0082, 0x0036 }, - { 0x07, 0x01, -1, 0x03, 0x00c2, 0x000a }, - { 0x07, 0x01, -1, 0x03, 0x00c2, 0x000c }, - { 0x07, 0x02, -1, 0x03, 0x00c2, 0x000e }, - { 0x07, 0x02, -1, 0x03, 0x00c2, 0x0012 }, - { 0x07, 0x03, -1, 0x03, 0x00c2, 0x0016 }, - { 0x07, 0x03, -1, 0x03, 0x00c2, 0x001e }, - { 0x07, 0x04, -1, 0x03, 0x00c2, 0x0026 }, - { 0x07, 0x04, -1, 0x03, 0x00c2, 0x0036 }, - { 0x08, 0x01, -1, 0x03, 0x0142, 0x000a }, - { 0x08, 0x01, -1, 0x03, 0x0142, 0x000c }, - { 0x08, 0x02, -1, 0x03, 0x0142, 0x000e }, - { 0x08, 0x02, -1, 0x03, 0x0142, 0x0012 }, - { 0x08, 0x03, -1, 0x03, 0x0142, 0x0016 }, - { 0x08, 0x03, -1, 0x03, 0x0142, 0x001e }, - { 0x08, 0x04, -1, 0x03, 0x0142, 0x0026 }, - { 0x08, 0x04, -1, 0x03, 0x0142, 0x0036 }, - { 0x09, 0x01, -1, 0x03, 0x0242, 0x000a }, - { 0x09, 0x01, -1, 0x03, 0x0242, 0x000c }, - { 0x09, 0x02, -1, 0x03, 0x0242, 0x000e }, - { 0x09, 0x02, -1, 0x03, 0x0242, 0x0012 }, - { 0x09, 0x03, -1, 0x03, 0x0242, 0x0016 }, - { 0x09, 0x03, -1, 0x03, 0x0242, 0x001e }, - { 0x09, 0x04, -1, 0x03, 0x0242, 0x0026 }, - { 0x09, 0x04, -1, 0x03, 0x0242, 0x0036 }, - { 0x0a, 0x01, -1, 0x03, 0x0442, 0x000a }, - { 0x0a, 0x01, -1, 0x03, 0x0442, 0x000c }, - { 0x0a, 0x02, -1, 0x03, 0x0442, 0x000e }, - { 0x0a, 0x02, -1, 0x03, 0x0442, 0x0012 }, - { 0x0a, 0x03, -1, 0x03, 0x0442, 0x0016 }, - { 0x0a, 0x03, -1, 0x03, 0x0442, 0x001e }, - { 0x0a, 0x04, -1, 0x03, 0x0442, 0x0026 }, - { 0x0a, 0x04, -1, 0x03, 0x0442, 0x0036 }, - { 0x0c, 0x01, -1, 0x03, 0x0842, 0x000a }, - { 0x0c, 0x01, -1, 0x03, 0x0842, 0x000c }, - { 0x0c, 0x02, -1, 0x03, 0x0842, 0x000e }, - { 0x0c, 0x02, -1, 0x03, 0x0842, 0x0012 }, - { 0x0c, 0x03, -1, 0x03, 0x0842, 0x0016 }, - { 0x0c, 0x03, -1, 0x03, 0x0842, 0x001e }, - { 0x0c, 0x04, -1, 0x03, 0x0842, 0x0026 }, - { 0x0c, 0x04, -1, 0x03, 0x0842, 0x0036 }, - { 0x0e, 0x01, -1, 0x03, 0x1842, 0x000a }, - { 0x0e, 0x01, -1, 0x03, 0x1842, 0x000c }, - { 0x0e, 0x02, -1, 0x03, 0x1842, 0x000e }, - { 0x0e, 0x02, -1, 0x03, 0x1842, 0x0012 }, - { 0x0e, 0x03, -1, 0x03, 0x1842, 0x0016 }, - { 0x0e, 0x03, -1, 0x03, 0x1842, 0x001e }, - { 0x0e, 0x04, -1, 0x03, 0x1842, 0x0026 }, - { 0x0e, 0x04, -1, 0x03, 0x1842, 0x0036 }, - { 0x18, 0x01, -1, 0x03, 0x5842, 0x000a }, - { 0x18, 0x01, -1, 0x03, 0x5842, 0x000c }, - { 0x18, 0x02, -1, 0x03, 0x5842, 0x000e }, - { 0x18, 0x02, -1, 0x03, 0x5842, 0x0012 }, - { 0x18, 0x03, -1, 0x03, 0x5842, 0x0016 }, - { 0x18, 0x03, -1, 0x03, 0x5842, 0x001e }, - { 0x18, 0x04, -1, 0x03, 0x5842, 0x0026 }, - { 0x18, 0x04, -1, 0x03, 0x5842, 0x0036 }, - { 0x06, 0x05, -1, 0x03, 0x0082, 0x0046 }, - { 0x06, 0x05, -1, 0x03, 0x0082, 0x0066 }, - { 0x06, 0x06, -1, 0x03, 0x0082, 0x0086 }, - { 0x06, 0x07, -1, 0x03, 0x0082, 0x00c6 }, - { 0x06, 0x08, -1, 0x03, 0x0082, 0x0146 }, - { 0x06, 0x09, -1, 0x03, 0x0082, 0x0246 }, - { 0x06, 0x0a, -1, 0x03, 0x0082, 0x0446 }, - { 0x06, 0x18, -1, 0x03, 0x0082, 0x0846 }, - { 0x07, 0x05, -1, 0x03, 0x00c2, 0x0046 }, - { 0x07, 0x05, -1, 0x03, 0x00c2, 0x0066 }, - { 0x07, 0x06, -1, 0x03, 0x00c2, 0x0086 }, - { 0x07, 0x07, -1, 0x03, 0x00c2, 0x00c6 }, - { 0x07, 0x08, -1, 0x03, 0x00c2, 0x0146 }, - { 0x07, 0x09, -1, 0x03, 0x00c2, 0x0246 }, - { 0x07, 0x0a, -1, 0x03, 0x00c2, 0x0446 }, - { 0x07, 0x18, -1, 0x03, 0x00c2, 0x0846 }, - { 0x08, 0x05, -1, 0x03, 0x0142, 0x0046 }, - { 0x08, 0x05, -1, 0x03, 0x0142, 0x0066 }, - { 0x08, 0x06, -1, 0x03, 0x0142, 0x0086 }, - { 0x08, 0x07, -1, 0x03, 0x0142, 0x00c6 }, - { 0x08, 0x08, -1, 0x03, 0x0142, 0x0146 }, - { 0x08, 0x09, -1, 0x03, 0x0142, 0x0246 }, - { 0x08, 0x0a, -1, 0x03, 0x0142, 0x0446 }, - { 0x08, 0x18, -1, 0x03, 0x0142, 0x0846 }, - { 0x09, 0x05, -1, 0x03, 0x0242, 0x0046 }, - { 0x09, 0x05, -1, 0x03, 0x0242, 0x0066 }, - { 0x09, 0x06, -1, 0x03, 0x0242, 0x0086 }, - { 0x09, 0x07, -1, 0x03, 0x0242, 0x00c6 }, - { 0x09, 0x08, -1, 0x03, 0x0242, 0x0146 }, - { 0x09, 0x09, -1, 0x03, 0x0242, 0x0246 }, - { 0x09, 0x0a, -1, 0x03, 0x0242, 0x0446 }, - { 0x09, 0x18, -1, 0x03, 0x0242, 0x0846 }, - { 0x0a, 0x05, -1, 0x03, 0x0442, 0x0046 }, - { 0x0a, 0x05, -1, 0x03, 0x0442, 0x0066 }, - { 0x0a, 0x06, -1, 0x03, 0x0442, 0x0086 }, - { 0x0a, 0x07, -1, 0x03, 0x0442, 0x00c6 }, - { 0x0a, 0x08, -1, 0x03, 0x0442, 0x0146 }, - { 0x0a, 0x09, -1, 0x03, 0x0442, 0x0246 }, - { 0x0a, 0x0a, -1, 0x03, 0x0442, 0x0446 }, - { 0x0a, 0x18, -1, 0x03, 0x0442, 0x0846 }, - { 0x0c, 0x05, -1, 0x03, 0x0842, 0x0046 }, - { 0x0c, 0x05, -1, 0x03, 0x0842, 0x0066 }, - { 0x0c, 0x06, -1, 0x03, 0x0842, 0x0086 }, - { 0x0c, 0x07, -1, 0x03, 0x0842, 0x00c6 }, - { 0x0c, 0x08, -1, 0x03, 0x0842, 0x0146 }, - { 0x0c, 0x09, -1, 0x03, 0x0842, 0x0246 }, - { 0x0c, 0x0a, -1, 0x03, 0x0842, 0x0446 }, - { 0x0c, 0x18, -1, 0x03, 0x0842, 0x0846 }, - { 0x0e, 0x05, -1, 0x03, 0x1842, 0x0046 }, - { 0x0e, 0x05, -1, 0x03, 0x1842, 0x0066 }, - { 0x0e, 0x06, -1, 0x03, 0x1842, 0x0086 }, - { 0x0e, 0x07, -1, 0x03, 0x1842, 0x00c6 }, - { 0x0e, 0x08, -1, 0x03, 0x1842, 0x0146 }, - { 0x0e, 0x09, -1, 0x03, 0x1842, 0x0246 }, - { 0x0e, 0x0a, -1, 0x03, 0x1842, 0x0446 }, - { 0x0e, 0x18, -1, 0x03, 0x1842, 0x0846 }, - { 0x18, 0x05, -1, 0x03, 0x5842, 0x0046 }, - { 0x18, 0x05, -1, 0x03, 0x5842, 0x0066 }, - { 0x18, 0x06, -1, 0x03, 0x5842, 0x0086 }, - { 0x18, 0x07, -1, 0x03, 0x5842, 0x00c6 }, - { 0x18, 0x08, -1, 0x03, 0x5842, 0x0146 }, - { 0x18, 0x09, -1, 0x03, 0x5842, 0x0246 }, - { 0x18, 0x0a, -1, 0x03, 0x5842, 0x0446 }, - { 0x18, 0x18, -1, 0x03, 0x5842, 0x0846 }, -}; - -#endif /* BROTLI_DEC_PREFIX_H_ */ diff --git a/src/deps/brotli/dec/state.c b/src/deps/brotli/dec/state.c deleted file mode 100644 index be6a26680bb04..0000000000000 --- a/src/deps/brotli/dec/state.c +++ /dev/null @@ -1,183 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "state.h" - -#include /* free, malloc */ - -#include - -#include "../common/dictionary.h" -#include "huffman.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s, - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - if (!alloc_func) { - s->alloc_func = BrotliDefaultAllocFunc; - s->free_func = BrotliDefaultFreeFunc; - s->memory_manager_opaque = 0; - } else { - s->alloc_func = alloc_func; - s->free_func = free_func; - s->memory_manager_opaque = opaque; - } - - s->error_code = 0; /* BROTLI_DECODER_NO_ERROR */ - - BrotliInitBitReader(&s->br); - s->state = BROTLI_STATE_UNINITED; - s->large_window = 0; - s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; - s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE; - s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE; - s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; - - s->buffer_length = 0; - s->loop_counter = 0; - s->pos = 0; - s->rb_roundtrips = 0; - s->partial_pos_out = 0; - s->used_input = 0; - - s->block_type_trees = NULL; - s->block_len_trees = NULL; - s->ringbuffer = NULL; - s->ringbuffer_size = 0; - s->new_ringbuffer_size = 0; - s->ringbuffer_mask = 0; - - s->context_map = NULL; - s->context_modes = NULL; - s->dist_context_map = NULL; - s->context_map_slice = NULL; - s->dist_context_map_slice = NULL; - - s->literal_hgroup.codes = NULL; - s->literal_hgroup.htrees = NULL; - s->insert_copy_hgroup.codes = NULL; - s->insert_copy_hgroup.htrees = NULL; - s->distance_hgroup.codes = NULL; - s->distance_hgroup.htrees = NULL; - - s->is_last_metablock = 0; - s->is_uncompressed = 0; - s->is_metadata = 0; - s->should_wrap_ringbuffer = 0; - s->canny_ringbuffer_allocation = 1; - - s->window_bits = 0; - s->max_distance = 0; - s->dist_rb[0] = 16; - s->dist_rb[1] = 15; - s->dist_rb[2] = 11; - s->dist_rb[3] = 4; - s->dist_rb_idx = 0; - s->block_type_trees = NULL; - s->block_len_trees = NULL; - - s->mtf_upper_bound = 63; - - s->compound_dictionary = NULL; - s->dictionary = - BrotliSharedDictionaryCreateInstance(alloc_func, free_func, opaque); - if (!s->dictionary) return BROTLI_FALSE; - - s->metadata_start_func = NULL; - s->metadata_chunk_func = NULL; - s->metadata_callback_opaque = 0; - - return BROTLI_TRUE; -} - -void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) { - s->meta_block_remaining_len = 0; - s->block_length[0] = BROTLI_BLOCK_SIZE_CAP; - s->block_length[1] = BROTLI_BLOCK_SIZE_CAP; - s->block_length[2] = BROTLI_BLOCK_SIZE_CAP; - s->num_block_types[0] = 1; - s->num_block_types[1] = 1; - s->num_block_types[2] = 1; - s->block_type_rb[0] = 1; - s->block_type_rb[1] = 0; - s->block_type_rb[2] = 1; - s->block_type_rb[3] = 0; - s->block_type_rb[4] = 1; - s->block_type_rb[5] = 0; - s->context_map = NULL; - s->context_modes = NULL; - s->dist_context_map = NULL; - s->context_map_slice = NULL; - s->literal_htree = NULL; - s->dist_context_map_slice = NULL; - s->dist_htree_index = 0; - s->context_lookup = NULL; - s->literal_hgroup.codes = NULL; - s->literal_hgroup.htrees = NULL; - s->insert_copy_hgroup.codes = NULL; - s->insert_copy_hgroup.htrees = NULL; - s->distance_hgroup.codes = NULL; - s->distance_hgroup.htrees = NULL; -} - -void BrotliDecoderStateCleanupAfterMetablock(BrotliDecoderState* s) { - BROTLI_DECODER_FREE(s, s->context_modes); - BROTLI_DECODER_FREE(s, s->context_map); - BROTLI_DECODER_FREE(s, s->dist_context_map); - BROTLI_DECODER_FREE(s, s->literal_hgroup.htrees); - BROTLI_DECODER_FREE(s, s->insert_copy_hgroup.htrees); - BROTLI_DECODER_FREE(s, s->distance_hgroup.htrees); -} - -#ifdef BROTLI_REPORTING -/* When BROTLI_REPORTING is defined extra reporting module have to be linked. */ -void BrotliDecoderOnFinish(const BrotliDecoderState* s); -#define BROTLI_DECODER_ON_FINISH(s) BrotliDecoderOnFinish(s); -#else -#if !defined(BROTLI_DECODER_ON_FINISH) -#define BROTLI_DECODER_ON_FINISH(s) (void)(s); -#endif -#endif - -void BrotliDecoderStateCleanup(BrotliDecoderState* s) { - BrotliDecoderStateCleanupAfterMetablock(s); - - BROTLI_DECODER_ON_FINISH(s); - - BROTLI_DECODER_FREE(s, s->compound_dictionary); - BrotliSharedDictionaryDestroyInstance(s->dictionary); - s->dictionary = NULL; - BROTLI_DECODER_FREE(s, s->ringbuffer); - BROTLI_DECODER_FREE(s, s->block_type_trees); -} - -BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(BrotliDecoderState* s, - HuffmanTreeGroup* group, brotli_reg_t alphabet_size_max, - brotli_reg_t alphabet_size_limit, brotli_reg_t ntrees) { - /* 376 = 256 (1-st level table) + 4 + 7 + 15 + 31 + 63 (2-nd level mix-tables) - This number is discovered "unlimited" "enough" calculator; it is actually - a wee bigger than required in several cases (especially for alphabets with - less than 16 symbols). */ - const size_t max_table_size = alphabet_size_limit + 376; - const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size; - const size_t htree_size = sizeof(HuffmanCode*) * ntrees; - /* Pointer alignment is, hopefully, wider than sizeof(HuffmanCode). */ - HuffmanCode** p = (HuffmanCode**)BROTLI_DECODER_ALLOC(s, - code_size + htree_size); - group->alphabet_size_max = (uint16_t)alphabet_size_max; - group->alphabet_size_limit = (uint16_t)alphabet_size_limit; - group->num_htrees = (uint16_t)ntrees; - group->htrees = p; - group->codes = (HuffmanCode*)(&p[ntrees]); - return !!p; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/dec/state.h b/src/deps/brotli/dec/state.h deleted file mode 100644 index fd250b6842a46..0000000000000 --- a/src/deps/brotli/dec/state.h +++ /dev/null @@ -1,400 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Brotli state for partial streaming decoding. */ - -#ifndef BROTLI_DEC_STATE_H_ -#define BROTLI_DEC_STATE_H_ - -#include -#include -#include - -#include "../common/constants.h" -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "../common/transform.h" -#include "bit_reader.h" -#include "huffman.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* Graphviz diagram that describes state transitions: - -digraph States { - graph [compound=true] - concentrate=true - node [shape="box"] - - UNINITED -> {LARGE_WINDOW_BITS -> INITIALIZE} - subgraph cluster_metablock_workflow { - style="rounded" - label=< METABLOCK CYCLE > - METABLOCK_BEGIN -> METABLOCK_HEADER - METABLOCK_HEADER:sw -> METADATA - METABLOCK_HEADER:s -> UNCOMPRESSED - METABLOCK_HEADER:se -> METABLOCK_DONE:ne - METADATA:s -> METABLOCK_DONE:w - UNCOMPRESSED:s -> METABLOCK_DONE:n - METABLOCK_DONE:e -> METABLOCK_BEGIN:e [constraint="false"] - } - INITIALIZE -> METABLOCK_BEGIN - METABLOCK_DONE -> DONE - - subgraph cluster_compressed_metablock { - style="rounded" - label=< COMPRESSED METABLOCK > - - subgraph cluster_command { - style="rounded" - label=< HOT LOOP > - - _METABLOCK_DONE_PORT_ [shape=point style=invis] - - { - // Set different shape for nodes returning from "compressed metablock". - node [shape=invhouse]; CMD_INNER CMD_POST_DECODE_LITERALS; - CMD_POST_WRAP_COPY; CMD_INNER_WRITE; CMD_POST_WRITE_1; - } - - CMD_BEGIN -> CMD_INNER -> CMD_POST_DECODE_LITERALS -> CMD_POST_WRAP_COPY - - // IO ("write") nodes are not in the hot loop! - CMD_INNER_WRITE [style=dashed] - CMD_INNER -> CMD_INNER_WRITE - CMD_POST_WRITE_1 [style=dashed] - CMD_POST_DECODE_LITERALS -> CMD_POST_WRITE_1 - CMD_POST_WRITE_2 [style=dashed] - CMD_POST_WRAP_COPY -> CMD_POST_WRITE_2 - - CMD_POST_WRITE_1 -> CMD_BEGIN:s [constraint="false"] - CMD_INNER_WRITE -> {CMD_INNER CMD_POST_DECODE_LITERALS} - [constraint="false"] - CMD_BEGIN:ne -> CMD_POST_DECODE_LITERALS [constraint="false"] - CMD_POST_WRAP_COPY -> CMD_BEGIN [constraint="false"] - CMD_POST_DECODE_LITERALS -> CMD_BEGIN:ne [constraint="false"] - CMD_POST_WRITE_2 -> CMD_POST_WRAP_COPY [constraint="false"] - {rank=same; CMD_BEGIN; CMD_INNER; CMD_POST_DECODE_LITERALS; - CMD_POST_WRAP_COPY} - {rank=same; CMD_INNER_WRITE; CMD_POST_WRITE_1; CMD_POST_WRITE_2} - - {CMD_INNER CMD_POST_DECODE_LITERALS CMD_POST_WRAP_COPY} -> - _METABLOCK_DONE_PORT_ [style=invis] - {CMD_INNER_WRITE CMD_POST_WRITE_1} -> _METABLOCK_DONE_PORT_ - [constraint="false" style=invis] - } - - BEFORE_COMPRESSED_METABLOCK_HEADER:s -> HUFFMAN_CODE_0:n - HUFFMAN_CODE_0 -> HUFFMAN_CODE_1 -> HUFFMAN_CODE_2 -> HUFFMAN_CODE_3 - HUFFMAN_CODE_0 -> METABLOCK_HEADER_2 -> CONTEXT_MODES -> CONTEXT_MAP_1 - CONTEXT_MAP_1 -> CONTEXT_MAP_2 -> TREE_GROUP - TREE_GROUP -> BEFORE_COMPRESSED_METABLOCK_BODY:e - BEFORE_COMPRESSED_METABLOCK_BODY:s -> CMD_BEGIN:n - - HUFFMAN_CODE_3:e -> HUFFMAN_CODE_0:ne [constraint="false"] - {rank=same; HUFFMAN_CODE_0; HUFFMAN_CODE_1; HUFFMAN_CODE_2; HUFFMAN_CODE_3} - {rank=same; METABLOCK_HEADER_2; CONTEXT_MODES; CONTEXT_MAP_1; CONTEXT_MAP_2; - TREE_GROUP} - } - METABLOCK_HEADER:e -> BEFORE_COMPRESSED_METABLOCK_HEADER:n - - _METABLOCK_DONE_PORT_ -> METABLOCK_DONE:se - [constraint="false" ltail=cluster_command] - - UNINITED [shape=Mdiamond]; - DONE [shape=Msquare]; -} - - - */ - -typedef enum { - BROTLI_STATE_UNINITED, - BROTLI_STATE_LARGE_WINDOW_BITS, - BROTLI_STATE_INITIALIZE, - BROTLI_STATE_METABLOCK_BEGIN, - BROTLI_STATE_METABLOCK_HEADER, - BROTLI_STATE_METABLOCK_HEADER_2, - BROTLI_STATE_CONTEXT_MODES, - BROTLI_STATE_COMMAND_BEGIN, - BROTLI_STATE_COMMAND_INNER, - BROTLI_STATE_COMMAND_POST_DECODE_LITERALS, - BROTLI_STATE_COMMAND_POST_WRAP_COPY, - BROTLI_STATE_UNCOMPRESSED, - BROTLI_STATE_METADATA, - BROTLI_STATE_COMMAND_INNER_WRITE, - BROTLI_STATE_METABLOCK_DONE, - BROTLI_STATE_COMMAND_POST_WRITE_1, - BROTLI_STATE_COMMAND_POST_WRITE_2, - BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER, - BROTLI_STATE_HUFFMAN_CODE_0, - BROTLI_STATE_HUFFMAN_CODE_1, - BROTLI_STATE_HUFFMAN_CODE_2, - BROTLI_STATE_HUFFMAN_CODE_3, - BROTLI_STATE_CONTEXT_MAP_1, - BROTLI_STATE_CONTEXT_MAP_2, - BROTLI_STATE_TREE_GROUP, - BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY, - BROTLI_STATE_DONE -} BrotliRunningState; - -typedef enum { - BROTLI_STATE_METABLOCK_HEADER_NONE, - BROTLI_STATE_METABLOCK_HEADER_EMPTY, - BROTLI_STATE_METABLOCK_HEADER_NIBBLES, - BROTLI_STATE_METABLOCK_HEADER_SIZE, - BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED, - BROTLI_STATE_METABLOCK_HEADER_RESERVED, - BROTLI_STATE_METABLOCK_HEADER_BYTES, - BROTLI_STATE_METABLOCK_HEADER_METADATA -} BrotliRunningMetablockHeaderState; - -typedef enum { - BROTLI_STATE_UNCOMPRESSED_NONE, - BROTLI_STATE_UNCOMPRESSED_WRITE -} BrotliRunningUncompressedState; - -typedef enum { - BROTLI_STATE_TREE_GROUP_NONE, - BROTLI_STATE_TREE_GROUP_LOOP -} BrotliRunningTreeGroupState; - -typedef enum { - BROTLI_STATE_CONTEXT_MAP_NONE, - BROTLI_STATE_CONTEXT_MAP_READ_PREFIX, - BROTLI_STATE_CONTEXT_MAP_HUFFMAN, - BROTLI_STATE_CONTEXT_MAP_DECODE, - BROTLI_STATE_CONTEXT_MAP_TRANSFORM -} BrotliRunningContextMapState; - -typedef enum { - BROTLI_STATE_HUFFMAN_NONE, - BROTLI_STATE_HUFFMAN_SIMPLE_SIZE, - BROTLI_STATE_HUFFMAN_SIMPLE_READ, - BROTLI_STATE_HUFFMAN_SIMPLE_BUILD, - BROTLI_STATE_HUFFMAN_COMPLEX, - BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS -} BrotliRunningHuffmanState; - -typedef enum { - BROTLI_STATE_DECODE_UINT8_NONE, - BROTLI_STATE_DECODE_UINT8_SHORT, - BROTLI_STATE_DECODE_UINT8_LONG -} BrotliRunningDecodeUint8State; - -typedef enum { - BROTLI_STATE_READ_BLOCK_LENGTH_NONE, - BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX -} BrotliRunningReadBlockLengthState; - -/* BrotliDecoderState addon, used for Compound Dictionary functionality. */ -typedef struct BrotliDecoderCompoundDictionary { - int num_chunks; - int total_size; - int br_index; - int br_offset; - int br_length; - int br_copied; - const uint8_t* chunks[16]; - int chunk_offsets[16]; - int block_bits; - uint8_t block_map[256]; -} BrotliDecoderCompoundDictionary; - -typedef struct BrotliMetablockHeaderArena { - BrotliRunningTreeGroupState substate_tree_group; - BrotliRunningContextMapState substate_context_map; - BrotliRunningHuffmanState substate_huffman; - - brotli_reg_t sub_loop_counter; - - brotli_reg_t repeat_code_len; - brotli_reg_t prev_code_len; - - /* For ReadHuffmanCode. */ - brotli_reg_t symbol; - brotli_reg_t repeat; - brotli_reg_t space; - - /* Huffman table for "histograms". */ - HuffmanCode table[32]; - /* List of heads of symbol chains. */ - uint16_t* symbol_lists; - /* Storage from symbol_lists. */ - uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 + - BROTLI_NUM_COMMAND_SYMBOLS]; - /* Tails of symbol chains. */ - int next_symbol[32]; - uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES]; - /* Population counts for the code lengths. */ - uint16_t code_length_histo[16]; - /* TODO(eustas): +2 bytes padding */ - - /* For HuffmanTreeGroupDecode. */ - int htree_index; - HuffmanCode* next; - - /* For DecodeContextMap. */ - brotli_reg_t context_index; - brotli_reg_t max_run_length_prefix; - brotli_reg_t code; - HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272]; -} BrotliMetablockHeaderArena; - -typedef struct BrotliMetablockBodyArena { - uint8_t dist_extra_bits[544]; - brotli_reg_t dist_offset[544]; -} BrotliMetablockBodyArena; - -struct BrotliDecoderStateStruct { - BrotliRunningState state; - - /* This counter is reused for several disjoint loops. */ - int loop_counter; - - BrotliBitReader br; - - brotli_alloc_func alloc_func; - brotli_free_func free_func; - void* memory_manager_opaque; - - /* Temporary storage for remaining input. Brotli stream format is designed in - a way, that 64 bits are enough to make progress in decoding. */ - union { - uint64_t u64; - uint8_t u8[8]; - } buffer; - brotli_reg_t buffer_length; - - int pos; - int max_backward_distance; - int max_distance; - int ringbuffer_size; - int ringbuffer_mask; - int dist_rb_idx; - int dist_rb[4]; - int error_code; - int meta_block_remaining_len; - - uint8_t* ringbuffer; - uint8_t* ringbuffer_end; - HuffmanCode* htree_command; - const uint8_t* context_lookup; - uint8_t* context_map_slice; - uint8_t* dist_context_map_slice; - - /* This ring buffer holds a few past copy distances that will be used by - some special distance codes. */ - HuffmanTreeGroup literal_hgroup; - HuffmanTreeGroup insert_copy_hgroup; - HuffmanTreeGroup distance_hgroup; - HuffmanCode* block_type_trees; - HuffmanCode* block_len_trees; - /* This is true if the literal context map histogram type always matches the - block type. It is then not needed to keep the context (faster decoding). */ - int trivial_literal_context; - /* Distance context is actual after command is decoded and before distance is - computed. After distance computation it is used as a temporary variable. */ - int distance_context; - brotli_reg_t block_length[3]; - brotli_reg_t block_length_index; - brotli_reg_t num_block_types[3]; - brotli_reg_t block_type_rb[6]; - brotli_reg_t distance_postfix_bits; - brotli_reg_t num_direct_distance_codes; - brotli_reg_t num_dist_htrees; - uint8_t* dist_context_map; - HuffmanCode* literal_htree; - - /* For partial write operations. */ - size_t rb_roundtrips; /* how many times we went around the ring-buffer */ - size_t partial_pos_out; /* how much output to the user in total */ - - /* For InverseMoveToFrontTransform. */ - brotli_reg_t mtf_upper_bound; - uint32_t mtf[64 + 1]; - - int copy_length; - int distance_code; - - uint8_t dist_htree_index; - /* TODO(eustas): +3 bytes padding */ - - /* Less used attributes are at the end of this struct. */ - - brotli_decoder_metadata_start_func metadata_start_func; - brotli_decoder_metadata_chunk_func metadata_chunk_func; - void* metadata_callback_opaque; - - /* For reporting. */ - uint64_t used_input; /* how many bytes of input are consumed */ - - /* States inside function calls. */ - BrotliRunningMetablockHeaderState substate_metablock_header; - BrotliRunningUncompressedState substate_uncompressed; - BrotliRunningDecodeUint8State substate_decode_uint8; - BrotliRunningReadBlockLengthState substate_read_block_length; - - int new_ringbuffer_size; - /* TODO(eustas): +4 bytes padding */ - - unsigned int is_last_metablock : 1; - unsigned int is_uncompressed : 1; - unsigned int is_metadata : 1; - unsigned int should_wrap_ringbuffer : 1; - unsigned int canny_ringbuffer_allocation : 1; - unsigned int large_window : 1; - unsigned int window_bits : 6; - unsigned int size_nibbles : 8; - /* TODO(eustas): +12 bits padding */ - - brotli_reg_t num_literal_htrees; - uint8_t* context_map; - uint8_t* context_modes; - - BrotliSharedDictionary* dictionary; - BrotliDecoderCompoundDictionary* compound_dictionary; - - uint32_t trivial_literal_contexts[8]; /* 256 bits */ - - union { - BrotliMetablockHeaderArena header; - BrotliMetablockBodyArena body; - } arena; -}; - -typedef struct BrotliDecoderStateStruct BrotliDecoderStateInternal; -#define BrotliDecoderState BrotliDecoderStateInternal - -BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s, - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); -BROTLI_INTERNAL void BrotliDecoderStateCleanup(BrotliDecoderState* s); -BROTLI_INTERNAL void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s); -BROTLI_INTERNAL void BrotliDecoderStateCleanupAfterMetablock( - BrotliDecoderState* s); -BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit( - BrotliDecoderState* s, HuffmanTreeGroup* group, - brotli_reg_t alphabet_size_max, brotli_reg_t alphabet_size_limit, - brotli_reg_t ntrees); - -#define BROTLI_DECODER_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L) - -#define BROTLI_DECODER_FREE(S, X) { \ - S->free_func(S->memory_manager_opaque, X); \ - X = NULL; \ -} - -/* Literal/Command/Distance block size maximum; same as maximum metablock size; - used as block size when there is no block switching. */ -#define BROTLI_BLOCK_SIZE_CAP (1U << 24) - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_DEC_STATE_H_ */ diff --git a/src/deps/brotli/enc/backward_references.c b/src/deps/brotli/enc/backward_references.c deleted file mode 100644 index f600e6452ff17..0000000000000 --- a/src/deps/brotli/enc/backward_references.c +++ /dev/null @@ -1,207 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function to find backward reference copies. */ - -#include "backward_references.h" - -#include - -#include "../common/constants.h" -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "command.h" -#include "compound_dictionary.h" -#include "dictionary_hash.h" -#include "encoder_dict.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance, - size_t max_distance, - const int* dist_cache) { - if (distance <= max_distance) { - size_t distance_plus_3 = distance + 3; - size_t offset0 = distance_plus_3 - (size_t)dist_cache[0]; - size_t offset1 = distance_plus_3 - (size_t)dist_cache[1]; - if (distance == (size_t)dist_cache[0]) { - return 0; - } else if (distance == (size_t)dist_cache[1]) { - return 1; - } else if (offset0 < 7) { - return (0x9750468 >> (4 * offset0)) & 0xF; - } else if (offset1 < 7) { - return (0xFDB1ACE >> (4 * offset1)) & 0xF; - } else if (distance == (size_t)dist_cache[2]) { - return 2; - } else if (distance == (size_t)dist_cache[3]) { - return 3; - } - } - return distance + BROTLI_NUM_DISTANCE_SHORT_CODES - 1; -} - -#define EXPAND_CAT(a, b) CAT(a, b) -#define CAT(a, b) a ## b -#define FN(X) EXPAND_CAT(X, HASHER()) -#define EXPORT_FN(X) EXPAND_CAT(X, EXPAND_CAT(PREFIX(), HASHER())) - -#define PREFIX() N -#define ENABLE_COMPOUND_DICTIONARY 0 - -#define HASHER() H2 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H3 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H4 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H5 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H6 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H40 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H41 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H42 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H54 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H35 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H55 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#define HASHER() H65 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#undef ENABLE_COMPOUND_DICTIONARY -#undef PREFIX -#define PREFIX() D -#define ENABLE_COMPOUND_DICTIONARY 1 - -#define HASHER() H5 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER -#define HASHER() H6 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER -#define HASHER() H40 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER -#define HASHER() H41 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER -#define HASHER() H42 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER -#define HASHER() H55 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER -#define HASHER() H65 -/* NOLINTNEXTLINE(build/include) */ -#include "backward_references_inc.h" -#undef HASHER - -#undef ENABLE_COMPOUND_DICTIONARY -#undef PREFIX - -#undef EXPORT_FN -#undef FN -#undef CAT -#undef EXPAND_CAT - -void BrotliCreateBackwardReferences(size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals) { - if (params->dictionary.compound.num_chunks != 0) { - switch (params->hasher.type) { -#define CASE_(N) \ - case N: \ - CreateBackwardReferencesDH ## N(num_bytes, \ - position, ringbuffer, ringbuffer_mask, \ - literal_context_lut, params, hasher, dist_cache, \ - last_insert_len, commands, num_commands, num_literals); \ - return; - CASE_(5) - CASE_(6) - CASE_(40) - CASE_(41) - CASE_(42) - CASE_(55) - CASE_(65) -#undef CASE_ - default: - BROTLI_DCHECK(false); - break; - } - } - - switch (params->hasher.type) { -#define CASE_(N) \ - case N: \ - CreateBackwardReferencesNH ## N(num_bytes, \ - position, ringbuffer, ringbuffer_mask, \ - literal_context_lut, params, hasher, dist_cache, \ - last_insert_len, commands, num_commands, num_literals); \ - return; - FOR_GENERIC_HASHERS(CASE_) -#undef CASE_ - default: - BROTLI_DCHECK(false); - break; - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/backward_references.h b/src/deps/brotli/enc/backward_references.h deleted file mode 100644 index 20fb98a4d88eb..0000000000000 --- a/src/deps/brotli/enc/backward_references.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function to find backward reference copies. */ - -#ifndef BROTLI_ENC_BACKWARD_REFERENCES_H_ -#define BROTLI_ENC_BACKWARD_REFERENCES_H_ - -#include - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "command.h" -#include "hash.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* "commands" points to the next output command to write to, "*num_commands" is - initially the total amount of commands output by previous - CreateBackwardReferences calls, and must be incremented by the amount written - by this call. */ -BROTLI_INTERNAL void BrotliCreateBackwardReferences(size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_BACKWARD_REFERENCES_H_ */ diff --git a/src/deps/brotli/enc/backward_references_hq.c b/src/deps/brotli/enc/backward_references_hq.c deleted file mode 100644 index 6325032e1cd66..0000000000000 --- a/src/deps/brotli/enc/backward_references_hq.c +++ /dev/null @@ -1,939 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function to find backward reference copies. */ - -#include "backward_references_hq.h" - -#include /* memcpy, memset */ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "command.h" -#include "compound_dictionary.h" -#include "encoder_dict.h" -#include "fast_log.h" -#include "find_match_length.h" -#include "literal_cost.h" -#include "memory.h" -#include "params.h" -#include "prefix.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* BrotliCalculateDistanceCodeLimit(BROTLI_MAX_ALLOWED_DISTANCE, 3, 120). */ -#define BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE 544 - -static const float kInfinity = 1.7e38f; /* ~= 2 ^ 127 */ - -static const uint32_t kDistanceCacheIndex[] = { - 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, -}; -static const int kDistanceCacheOffset[] = { - 0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3 -}; - -void BrotliInitZopfliNodes(ZopfliNode* array, size_t length) { - ZopfliNode stub; - size_t i; - stub.length = 1; - stub.distance = 0; - stub.dcode_insert_length = 0; - stub.u.cost = kInfinity; - for (i = 0; i < length; ++i) array[i] = stub; -} - -static BROTLI_INLINE uint32_t ZopfliNodeCopyLength(const ZopfliNode* self) { - return self->length & 0x1FFFFFF; -} - -static BROTLI_INLINE uint32_t ZopfliNodeLengthCode(const ZopfliNode* self) { - const uint32_t modifier = self->length >> 25; - return ZopfliNodeCopyLength(self) + 9u - modifier; -} - -static BROTLI_INLINE uint32_t ZopfliNodeCopyDistance(const ZopfliNode* self) { - return self->distance; -} - -static BROTLI_INLINE uint32_t ZopfliNodeDistanceCode(const ZopfliNode* self) { - const uint32_t short_code = self->dcode_insert_length >> 27; - return short_code == 0 ? - ZopfliNodeCopyDistance(self) + BROTLI_NUM_DISTANCE_SHORT_CODES - 1 : - short_code - 1; -} - -static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) { - return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF); -} - -/* Temporary data for ZopfliCostModelSetFromCommands. */ -typedef struct ZopfliCostModelArena { - uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS]; - uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS]; - uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE]; - float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS]; -} ZopfliCostModelArena; - -/* Histogram based cost model for zopflification. */ -typedef struct ZopfliCostModel { - /* The insert and copy length symbols. */ - float cost_cmd_[BROTLI_NUM_COMMAND_SYMBOLS]; - float* cost_dist_; - uint32_t distance_histogram_size; - /* Cumulative costs of literals per position in the stream. */ - float* literal_costs_; - float min_cost_cmd_; - size_t num_bytes_; - - /* Temporary data. */ - union { - size_t literal_histograms[3 * 256]; - ZopfliCostModelArena arena; - }; -} ZopfliCostModel; - -static void InitZopfliCostModel( - MemoryManager* m, ZopfliCostModel* self, const BrotliDistanceParams* dist, - size_t num_bytes) { - self->num_bytes_ = num_bytes; - self->literal_costs_ = BROTLI_ALLOC(m, float, num_bytes + 2); - self->cost_dist_ = BROTLI_ALLOC(m, float, dist->alphabet_size_limit); - self->distance_histogram_size = dist->alphabet_size_limit; - if (BROTLI_IS_OOM(m)) return; -} - -static void CleanupZopfliCostModel(MemoryManager* m, ZopfliCostModel* self) { - BROTLI_FREE(m, self->literal_costs_); - BROTLI_FREE(m, self->cost_dist_); -} - -static void SetCost(const uint32_t* histogram, size_t histogram_size, - BROTLI_BOOL literal_histogram, float* cost) { - size_t sum = 0; - size_t missing_symbol_sum; - float log2sum; - float missing_symbol_cost; - size_t i; - for (i = 0; i < histogram_size; i++) { - sum += histogram[i]; - } - log2sum = (float)FastLog2(sum); - missing_symbol_sum = sum; - if (!literal_histogram) { - for (i = 0; i < histogram_size; i++) { - if (histogram[i] == 0) missing_symbol_sum++; - } - } - missing_symbol_cost = (float)FastLog2(missing_symbol_sum) + 2; - for (i = 0; i < histogram_size; i++) { - if (histogram[i] == 0) { - cost[i] = missing_symbol_cost; - continue; - } - - /* Shannon bits for this symbol. */ - cost[i] = log2sum - (float)FastLog2(histogram[i]); - - /* Cannot be coded with less than 1 bit */ - if (cost[i] < 1) cost[i] = 1; - } -} - -static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self, - size_t position, - const uint8_t* ringbuffer, - size_t ringbuffer_mask, - const Command* commands, - size_t num_commands, - size_t last_insert_len) { - ZopfliCostModelArena* arena = &self->arena; - size_t pos = position - last_insert_len; - float min_cost_cmd = kInfinity; - size_t i; - float* cost_cmd = self->cost_cmd_; - - memset(arena->histogram_literal, 0, sizeof(arena->histogram_literal)); - memset(arena->histogram_cmd, 0, sizeof(arena->histogram_cmd)); - memset(arena->histogram_dist, 0, sizeof(arena->histogram_dist)); - - for (i = 0; i < num_commands; i++) { - size_t inslength = commands[i].insert_len_; - size_t copylength = CommandCopyLen(&commands[i]); - size_t distcode = commands[i].dist_prefix_ & 0x3FF; - size_t cmdcode = commands[i].cmd_prefix_; - size_t j; - - arena->histogram_cmd[cmdcode]++; - if (cmdcode >= 128) arena->histogram_dist[distcode]++; - - for (j = 0; j < inslength; j++) { - arena->histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++; - } - - pos += inslength + copylength; - } - - SetCost(arena->histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE, - arena->cost_literal); - SetCost(arena->histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE, - cost_cmd); - SetCost(arena->histogram_dist, self->distance_histogram_size, BROTLI_FALSE, - self->cost_dist_); - - for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) { - min_cost_cmd = BROTLI_MIN(float, min_cost_cmd, cost_cmd[i]); - } - self->min_cost_cmd_ = min_cost_cmd; - - { - float* literal_costs = self->literal_costs_; - float literal_carry = 0.0; - size_t num_bytes = self->num_bytes_; - literal_costs[0] = 0.0; - for (i = 0; i < num_bytes; ++i) { - literal_carry += - arena->cost_literal[ringbuffer[(position + i) & ringbuffer_mask]]; - literal_costs[i + 1] = literal_costs[i] + literal_carry; - literal_carry -= literal_costs[i + 1] - literal_costs[i]; - } - } -} - -static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self, - size_t position, - const uint8_t* ringbuffer, - size_t ringbuffer_mask) { - float* literal_costs = self->literal_costs_; - float literal_carry = 0.0; - float* cost_dist = self->cost_dist_; - float* cost_cmd = self->cost_cmd_; - size_t num_bytes = self->num_bytes_; - size_t i; - BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, - ringbuffer, self->literal_histograms, - &literal_costs[1]); - literal_costs[0] = 0.0; - for (i = 0; i < num_bytes; ++i) { - literal_carry += literal_costs[i + 1]; - literal_costs[i + 1] = literal_costs[i] + literal_carry; - literal_carry -= literal_costs[i + 1] - literal_costs[i]; - } - for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) { - cost_cmd[i] = (float)FastLog2(11 + (uint32_t)i); - } - for (i = 0; i < self->distance_histogram_size; ++i) { - cost_dist[i] = (float)FastLog2(20 + (uint32_t)i); - } - self->min_cost_cmd_ = (float)FastLog2(11); -} - -static BROTLI_INLINE float ZopfliCostModelGetCommandCost( - const ZopfliCostModel* self, uint16_t cmdcode) { - return self->cost_cmd_[cmdcode]; -} - -static BROTLI_INLINE float ZopfliCostModelGetDistanceCost( - const ZopfliCostModel* self, size_t distcode) { - return self->cost_dist_[distcode]; -} - -static BROTLI_INLINE float ZopfliCostModelGetLiteralCosts( - const ZopfliCostModel* self, size_t from, size_t to) { - return self->literal_costs_[to] - self->literal_costs_[from]; -} - -static BROTLI_INLINE float ZopfliCostModelGetMinCostCmd( - const ZopfliCostModel* self) { - return self->min_cost_cmd_; -} - -/* REQUIRES: len >= 2, start_pos <= pos */ -/* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */ -/* Maintains the "ZopfliNode array invariant". */ -static BROTLI_INLINE void UpdateZopfliNode(ZopfliNode* nodes, size_t pos, - size_t start_pos, size_t len, size_t len_code, size_t dist, - size_t short_code, float cost) { - ZopfliNode* next = &nodes[pos + len]; - next->length = (uint32_t)(len | ((len + 9u - len_code) << 25)); - next->distance = (uint32_t)dist; - next->dcode_insert_length = (uint32_t)( - (short_code << 27) | (pos - start_pos)); - next->u.cost = cost; -} - -typedef struct PosData { - size_t pos; - int distance_cache[4]; - float costdiff; - float cost; -} PosData; - -/* Maintains the smallest 8 cost difference together with their positions */ -typedef struct StartPosQueue { - PosData q_[8]; - size_t idx_; -} StartPosQueue; - -static BROTLI_INLINE void InitStartPosQueue(StartPosQueue* self) { - self->idx_ = 0; -} - -static size_t StartPosQueueSize(const StartPosQueue* self) { - return BROTLI_MIN(size_t, self->idx_, 8); -} - -static void StartPosQueuePush(StartPosQueue* self, const PosData* posdata) { - size_t offset = ~(self->idx_++) & 7; - size_t len = StartPosQueueSize(self); - size_t i; - PosData* q = self->q_; - q[offset] = *posdata; - /* Restore the sorted order. In the list of |len| items at most |len - 1| - adjacent element comparisons / swaps are required. */ - for (i = 1; i < len; ++i) { - if (q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff) { - BROTLI_SWAP(PosData, q, offset & 7, (offset + 1) & 7); - } - ++offset; - } -} - -static const PosData* StartPosQueueAt(const StartPosQueue* self, size_t k) { - return &self->q_[(k - self->idx_) & 7]; -} - -/* Returns the minimum possible copy length that can improve the cost of any */ -/* future position. */ -static size_t ComputeMinimumCopyLength(const float start_cost, - const ZopfliNode* nodes, - const size_t num_bytes, - const size_t pos) { - /* Compute the minimum possible cost of reaching any future position. */ - float min_cost = start_cost; - size_t len = 2; - size_t next_len_bucket = 4; - size_t next_len_offset = 10; - while (pos + len <= num_bytes && nodes[pos + len].u.cost <= min_cost) { - /* We already reached (pos + len) with no more cost than the minimum - possible cost of reaching anything from this pos, so there is no point in - looking for lengths <= len. */ - ++len; - if (len == next_len_offset) { - /* We reached the next copy length code bucket, so we add one more - extra bit to the minimum cost. */ - min_cost += 1.0f; - next_len_offset += next_len_bucket; - next_len_bucket *= 2; - } - } - return len; -} - -/* REQUIRES: nodes[pos].cost < kInfinity - REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ -static uint32_t ComputeDistanceShortcut(const size_t block_start, - const size_t pos, - const size_t max_backward_limit, - const size_t gap, - const ZopfliNode* nodes) { - const size_t clen = ZopfliNodeCopyLength(&nodes[pos]); - const size_t ilen = nodes[pos].dcode_insert_length & 0x7FFFFFF; - const size_t dist = ZopfliNodeCopyDistance(&nodes[pos]); - /* Since |block_start + pos| is the end position of the command, the copy part - starts from |block_start + pos - clen|. Distances that are greater than - this or greater than |max_backward_limit| + |gap| are static dictionary - references, and do not update the last distances. - Also distance code 0 (last distance) does not update the last distances. */ - if (pos == 0) { - return 0; - } else if (dist + clen <= block_start + pos + gap && - dist <= max_backward_limit + gap && - ZopfliNodeDistanceCode(&nodes[pos]) > 0) { - return (uint32_t)pos; - } else { - return nodes[pos - clen - ilen].u.shortcut; - } -} - -/* Fills in dist_cache[0..3] with the last four distances (as defined by - Section 4. of the Spec) that would be used at (block_start + pos) if we - used the shortest path of commands from block_start, computed from - nodes[0..pos]. The last four distances at block_start are in - starting_dist_cache[0..3]. - REQUIRES: nodes[pos].cost < kInfinity - REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ -static void ComputeDistanceCache(const size_t pos, - const int* starting_dist_cache, - const ZopfliNode* nodes, - int* dist_cache) { - int idx = 0; - size_t p = nodes[pos].u.shortcut; - while (idx < 4 && p > 0) { - const size_t ilen = nodes[p].dcode_insert_length & 0x7FFFFFF; - const size_t clen = ZopfliNodeCopyLength(&nodes[p]); - const size_t dist = ZopfliNodeCopyDistance(&nodes[p]); - dist_cache[idx++] = (int)dist; - /* Because of prerequisite, p >= clen + ilen >= 2. */ - p = nodes[p - clen - ilen].u.shortcut; - } - for (; idx < 4; ++idx) { - dist_cache[idx] = *starting_dist_cache++; - } -} - -/* Maintains "ZopfliNode array invariant" and pushes node to the queue, if it - is eligible. */ -static void EvaluateNode( - const size_t block_start, const size_t pos, const size_t max_backward_limit, - const size_t gap, const int* starting_dist_cache, - const ZopfliCostModel* model, StartPosQueue* queue, ZopfliNode* nodes) { - /* Save cost, because ComputeDistanceCache invalidates it. */ - float node_cost = nodes[pos].u.cost; - nodes[pos].u.shortcut = ComputeDistanceShortcut( - block_start, pos, max_backward_limit, gap, nodes); - if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) { - PosData posdata; - posdata.pos = pos; - posdata.cost = node_cost; - posdata.costdiff = node_cost - - ZopfliCostModelGetLiteralCosts(model, 0, pos); - ComputeDistanceCache( - pos, starting_dist_cache, nodes, posdata.distance_cache); - StartPosQueuePush(queue, &posdata); - } -} - -/* Returns longest copy length. */ -static size_t UpdateNodes( - const size_t num_bytes, const size_t block_start, const size_t pos, - const uint8_t* ringbuffer, const size_t ringbuffer_mask, - const BrotliEncoderParams* params, const size_t max_backward_limit, - const int* starting_dist_cache, const size_t num_matches, - const BackwardMatch* matches, const ZopfliCostModel* model, - StartPosQueue* queue, ZopfliNode* nodes) { - const size_t stream_offset = params->stream_offset; - const size_t cur_ix = block_start + pos; - const size_t cur_ix_masked = cur_ix & ringbuffer_mask; - const size_t max_distance = BROTLI_MIN(size_t, cur_ix, max_backward_limit); - const size_t dictionary_start = BROTLI_MIN(size_t, - cur_ix + stream_offset, max_backward_limit); - const size_t max_len = num_bytes - pos; - const size_t max_zopfli_len = MaxZopfliLen(params); - const size_t max_iters = MaxZopfliCandidates(params); - size_t min_len; - size_t result = 0; - size_t k; - const CompoundDictionary* addon = ¶ms->dictionary.compound; - size_t gap = addon->total_size; - - EvaluateNode(block_start + stream_offset, pos, max_backward_limit, gap, - starting_dist_cache, model, queue, nodes); - - { - const PosData* posdata = StartPosQueueAt(queue, 0); - float min_cost = (posdata->cost + ZopfliCostModelGetMinCostCmd(model) + - ZopfliCostModelGetLiteralCosts(model, posdata->pos, pos)); - min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos); - } - - /* Go over the command starting positions in order of increasing cost - difference. */ - for (k = 0; k < max_iters && k < StartPosQueueSize(queue); ++k) { - const PosData* posdata = StartPosQueueAt(queue, k); - const size_t start = posdata->pos; - const uint16_t inscode = GetInsertLengthCode(pos - start); - const float start_costdiff = posdata->costdiff; - const float base_cost = start_costdiff + (float)GetInsertExtra(inscode) + - ZopfliCostModelGetLiteralCosts(model, 0, pos); - - /* Look for last distance matches using the distance cache from this - starting position. */ - size_t best_len = min_len - 1; - size_t j = 0; - for (; j < BROTLI_NUM_DISTANCE_SHORT_CODES && best_len < max_len; ++j) { - const size_t idx = kDistanceCacheIndex[j]; - const size_t backward = - (size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]); - size_t prev_ix = cur_ix - backward; - size_t len = 0; - uint8_t continuation = ringbuffer[cur_ix_masked + best_len]; - if (cur_ix_masked + best_len > ringbuffer_mask) { - break; - } - if (BROTLI_PREDICT_FALSE(backward > dictionary_start + gap)) { - /* Word dictionary -> ignore. */ - continue; - } - if (backward <= max_distance) { - /* Regular backward reference. */ - if (prev_ix >= cur_ix) { - continue; - } - - prev_ix &= ringbuffer_mask; - if (prev_ix + best_len > ringbuffer_mask || - continuation != ringbuffer[prev_ix + best_len]) { - continue; - } - len = FindMatchLengthWithLimit(&ringbuffer[prev_ix], - &ringbuffer[cur_ix_masked], - max_len); - } else if (backward > dictionary_start) { - size_t d = 0; - size_t offset; - size_t limit; - const uint8_t* source; - offset = dictionary_start + 1 + addon->total_size - 1; - while (offset >= backward + addon->chunk_offsets[d + 1]) d++; - source = addon->chunk_source[d]; - offset = offset - addon->chunk_offsets[d] - backward; - limit = addon->chunk_offsets[d + 1] - addon->chunk_offsets[d] - offset; - limit = limit > max_len ? max_len : limit; - if (best_len >= limit || - continuation != source[offset + best_len]) { - continue; - } - len = FindMatchLengthWithLimit(&source[offset], - &ringbuffer[cur_ix_masked], - limit); - } else { - /* "Gray" area. It is addressable by decoder, but this encoder - instance does not have that data -> should not touch it. */ - continue; - } - { - const float dist_cost = base_cost + - ZopfliCostModelGetDistanceCost(model, j); - size_t l; - for (l = best_len + 1; l <= len; ++l) { - const uint16_t copycode = GetCopyLengthCode(l); - const uint16_t cmdcode = - CombineLengthCodes(inscode, copycode, j == 0); - const float cost = (cmdcode < 128 ? base_cost : dist_cost) + - (float)GetCopyExtra(copycode) + - ZopfliCostModelGetCommandCost(model, cmdcode); - if (cost < nodes[pos + l].u.cost) { - UpdateZopfliNode(nodes, pos, start, l, l, backward, j + 1, cost); - result = BROTLI_MAX(size_t, result, l); - } - best_len = l; - } - } - } - - /* At higher iterations look only for new last distance matches, since - looking only for new command start positions with the same distances - does not help much. */ - if (k >= 2) continue; - - { - /* Loop through all possible copy lengths at this position. */ - size_t len = min_len; - for (j = 0; j < num_matches; ++j) { - BackwardMatch match = matches[j]; - size_t dist = match.distance; - BROTLI_BOOL is_dictionary_match = - TO_BROTLI_BOOL(dist > dictionary_start + gap); - /* We already tried all possible last distance matches, so we can use - normal distance code here. */ - size_t dist_code = dist + BROTLI_NUM_DISTANCE_SHORT_CODES - 1; - uint16_t dist_symbol; - uint32_t distextra; - uint32_t distnumextra; - float dist_cost; - size_t max_match_len; - PrefixEncodeCopyDistance( - dist_code, params->dist.num_direct_distance_codes, - params->dist.distance_postfix_bits, &dist_symbol, &distextra); - distnumextra = dist_symbol >> 10; - dist_cost = base_cost + (float)distnumextra + - ZopfliCostModelGetDistanceCost(model, dist_symbol & 0x3FF); - - /* Try all copy lengths up until the maximum copy length corresponding - to this distance. If the distance refers to the static dictionary, or - the maximum length is long enough, try only one maximum length. */ - max_match_len = BackwardMatchLength(&match); - if (len < max_match_len && - (is_dictionary_match || max_match_len > max_zopfli_len)) { - len = max_match_len; - } - for (; len <= max_match_len; ++len) { - const size_t len_code = - is_dictionary_match ? BackwardMatchLengthCode(&match) : len; - const uint16_t copycode = GetCopyLengthCode(len_code); - const uint16_t cmdcode = CombineLengthCodes(inscode, copycode, 0); - const float cost = dist_cost + (float)GetCopyExtra(copycode) + - ZopfliCostModelGetCommandCost(model, cmdcode); - if (cost < nodes[pos + len].u.cost) { - UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0, cost); - result = BROTLI_MAX(size_t, result, len); - } - } - } - } - } - return result; -} - -static size_t ComputeShortestPathFromNodes(size_t num_bytes, - ZopfliNode* nodes) { - size_t index = num_bytes; - size_t num_commands = 0; - while ((nodes[index].dcode_insert_length & 0x7FFFFFF) == 0 && - nodes[index].length == 1) --index; - nodes[index].u.next = BROTLI_UINT32_MAX; - while (index != 0) { - size_t len = ZopfliNodeCommandLength(&nodes[index]); - index -= len; - nodes[index].u.next = (uint32_t)len; - num_commands++; - } - return num_commands; -} - -/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ -void BrotliZopfliCreateCommands(const size_t num_bytes, - const size_t block_start, const ZopfliNode* nodes, int* dist_cache, - size_t* last_insert_len, const BrotliEncoderParams* params, - Command* commands, size_t* num_literals) { - const size_t stream_offset = params->stream_offset; - const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); - size_t pos = 0; - uint32_t offset = nodes[0].u.next; - size_t i; - size_t gap = params->dictionary.compound.total_size; - for (i = 0; offset != BROTLI_UINT32_MAX; i++) { - const ZopfliNode* next = &nodes[pos + offset]; - size_t copy_length = ZopfliNodeCopyLength(next); - size_t insert_length = next->dcode_insert_length & 0x7FFFFFF; - pos += insert_length; - offset = next->u.next; - if (i == 0) { - insert_length += *last_insert_len; - *last_insert_len = 0; - } - { - size_t distance = ZopfliNodeCopyDistance(next); - size_t len_code = ZopfliNodeLengthCode(next); - size_t dictionary_start = BROTLI_MIN(size_t, - block_start + pos + stream_offset, max_backward_limit); - BROTLI_BOOL is_dictionary = - TO_BROTLI_BOOL(distance > dictionary_start + gap); - size_t dist_code = ZopfliNodeDistanceCode(next); - InitCommand(&commands[i], ¶ms->dist, insert_length, - copy_length, (int)len_code - (int)copy_length, dist_code); - - if (!is_dictionary && dist_code > 0) { - dist_cache[3] = dist_cache[2]; - dist_cache[2] = dist_cache[1]; - dist_cache[1] = dist_cache[0]; - dist_cache[0] = (int)distance; - } - } - - *num_literals += insert_length; - pos += copy_length; - } - *last_insert_len += num_bytes - pos; -} - -static size_t ZopfliIterate(size_t num_bytes, size_t position, - const uint8_t* ringbuffer, size_t ringbuffer_mask, - const BrotliEncoderParams* params, const size_t gap, const int* dist_cache, - const ZopfliCostModel* model, const uint32_t* num_matches, - const BackwardMatch* matches, ZopfliNode* nodes) { - const size_t stream_offset = params->stream_offset; - const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); - const size_t max_zopfli_len = MaxZopfliLen(params); - StartPosQueue queue; - size_t cur_match_pos = 0; - size_t i; - nodes[0].length = 0; - nodes[0].u.cost = 0; - InitStartPosQueue(&queue); - for (i = 0; i + 3 < num_bytes; i++) { - size_t skip = UpdateNodes(num_bytes, position, i, ringbuffer, - ringbuffer_mask, params, max_backward_limit, dist_cache, - num_matches[i], &matches[cur_match_pos], model, &queue, nodes); - if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0; - cur_match_pos += num_matches[i]; - if (num_matches[i] == 1 && - BackwardMatchLength(&matches[cur_match_pos - 1]) > max_zopfli_len) { - skip = BROTLI_MAX(size_t, - BackwardMatchLength(&matches[cur_match_pos - 1]), skip); - } - if (skip > 1) { - skip--; - while (skip) { - i++; - if (i + 3 >= num_bytes) break; - EvaluateNode(position + stream_offset, i, max_backward_limit, gap, - dist_cache, model, &queue, nodes); - cur_match_pos += num_matches[i]; - skip--; - } - } - } - return ComputeShortestPathFromNodes(num_bytes, nodes); -} - -static void MergeMatches(BackwardMatch* dst, - BackwardMatch* src1, size_t len1, BackwardMatch* src2, size_t len2) { - while (len1 > 0 && len2 > 0) { - size_t l1 = BackwardMatchLength(src1); - size_t l2 = BackwardMatchLength(src2); - if (l1 < l2 || ((l1 == l2) && (src1->distance < src2->distance))) { - *dst++ = *src1++; - len1--; - } else { - *dst++ = *src2++; - len2--; - } - } - while (len1-- > 0) *dst++ = *src1++; - while (len2-- > 0) *dst++ = *src2++; -} - -/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ -size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - const int* dist_cache, Hasher* hasher, ZopfliNode* nodes) { - const size_t stream_offset = params->stream_offset; - const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); - const size_t max_zopfli_len = MaxZopfliLen(params); - StartPosQueue queue; - BackwardMatch* BROTLI_RESTRICT matches = - BROTLI_ALLOC(m, BackwardMatch, 2 * (MAX_NUM_MATCHES_H10 + 64)); - const size_t store_end = num_bytes >= StoreLookaheadH10() ? - position + num_bytes - StoreLookaheadH10() + 1 : position; - size_t i; - const CompoundDictionary* addon = ¶ms->dictionary.compound; - size_t gap = addon->total_size; - size_t lz_matches_offset = - (addon->num_chunks != 0) ? (MAX_NUM_MATCHES_H10 + 128) : 0; - ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) || BROTLI_IS_NULL(matches)) { - return 0; - } - nodes[0].length = 0; - nodes[0].u.cost = 0; - InitZopfliCostModel(m, model, ¶ms->dist, num_bytes); - if (BROTLI_IS_OOM(m)) return 0; - ZopfliCostModelSetFromLiteralCosts( - model, position, ringbuffer, ringbuffer_mask); - InitStartPosQueue(&queue); - for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) { - const size_t pos = position + i; - const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit); - const size_t dictionary_start = BROTLI_MIN(size_t, - pos + stream_offset, max_backward_limit); - size_t skip; - size_t num_matches; - int dict_id = 0; - if (params->dictionary.contextual.context_based) { - uint8_t p1 = pos >= 1 ? - ringbuffer[(size_t)(pos - 1) & ringbuffer_mask] : 0; - uint8_t p2 = pos >= 2 ? - ringbuffer[(size_t)(pos - 2) & ringbuffer_mask] : 0; - dict_id = params->dictionary.contextual.context_map[ - BROTLI_CONTEXT(p1, p2, literal_context_lut)]; - } - num_matches = FindAllMatchesH10(&hasher->privat._H10, - params->dictionary.contextual.dict[dict_id], - ringbuffer, ringbuffer_mask, pos, num_bytes - i, max_distance, - dictionary_start + gap, params, &matches[lz_matches_offset]); - if (addon->num_chunks != 0) { - size_t cd_matches = LookupAllCompoundDictionaryMatches(addon, - ringbuffer, ringbuffer_mask, pos, 3, num_bytes - i, - dictionary_start, params->dist.max_distance, - &matches[lz_matches_offset - 64], 64); - MergeMatches(matches, &matches[lz_matches_offset - 64], cd_matches, - &matches[lz_matches_offset], num_matches); - num_matches += cd_matches; - } - if (num_matches > 0 && - BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) { - matches[0] = matches[num_matches - 1]; - num_matches = 1; - } - skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, - params, max_backward_limit, dist_cache, num_matches, matches, model, - &queue, nodes); - if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0; - if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) { - skip = BROTLI_MAX(size_t, BackwardMatchLength(&matches[0]), skip); - } - if (skip > 1) { - /* Add the tail of the copy to the hasher. */ - StoreRangeH10(&hasher->privat._H10, - ringbuffer, ringbuffer_mask, pos + 1, BROTLI_MIN( - size_t, pos + skip, store_end)); - skip--; - while (skip) { - i++; - if (i + HashTypeLengthH10() - 1 >= num_bytes) break; - EvaluateNode(position + stream_offset, i, max_backward_limit, gap, - dist_cache, model, &queue, nodes); - skip--; - } - } - } - CleanupZopfliCostModel(m, model); - BROTLI_FREE(m, model); - BROTLI_FREE(m, matches); - return ComputeShortestPathFromNodes(num_bytes, nodes); -} - -void BrotliCreateZopfliBackwardReferences(MemoryManager* m, size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals) { - ZopfliNode* nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return; - BrotliInitZopfliNodes(nodes, num_bytes + 1); - *num_commands += BrotliZopfliComputeShortestPath(m, num_bytes, - position, ringbuffer, ringbuffer_mask, literal_context_lut, params, - dist_cache, hasher, nodes); - if (BROTLI_IS_OOM(m)) return; - BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache, - last_insert_len, params, commands, num_literals); - BROTLI_FREE(m, nodes); -} - -void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals) { - const size_t stream_offset = params->stream_offset; - const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); - uint32_t* num_matches = BROTLI_ALLOC(m, uint32_t, num_bytes); - size_t matches_size = 4 * num_bytes; - const size_t store_end = num_bytes >= StoreLookaheadH10() ? - position + num_bytes - StoreLookaheadH10() + 1 : position; - size_t cur_match_pos = 0; - size_t i; - size_t orig_num_literals; - size_t orig_last_insert_len; - int orig_dist_cache[4]; - size_t orig_num_commands; - ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1); - ZopfliNode* nodes; - BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size); - const CompoundDictionary* addon = ¶ms->dictionary.compound; - size_t gap = addon->total_size; - size_t shadow_matches = - (addon->num_chunks != 0) ? (MAX_NUM_MATCHES_H10 + 128) : 0; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) || - BROTLI_IS_NULL(num_matches) || BROTLI_IS_NULL(matches)) { - return; - } - for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) { - const size_t pos = position + i; - size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit); - size_t dictionary_start = BROTLI_MIN(size_t, - pos + stream_offset, max_backward_limit); - size_t max_length = num_bytes - i; - size_t num_found_matches; - size_t cur_match_end; - size_t j; - int dict_id = 0; - if (params->dictionary.contextual.context_based) { - uint8_t p1 = pos >= 1 ? - ringbuffer[(size_t)(pos - 1) & ringbuffer_mask] : 0; - uint8_t p2 = pos >= 2 ? - ringbuffer[(size_t)(pos - 2) & ringbuffer_mask] : 0; - dict_id = params->dictionary.contextual.context_map[ - BROTLI_CONTEXT(p1, p2, literal_context_lut)]; - } - /* Ensure that we have enough free slots. */ - BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size, - cur_match_pos + MAX_NUM_MATCHES_H10 + shadow_matches); - if (BROTLI_IS_OOM(m)) return; - num_found_matches = FindAllMatchesH10(&hasher->privat._H10, - params->dictionary.contextual.dict[dict_id], - ringbuffer, ringbuffer_mask, pos, max_length, - max_distance, dictionary_start + gap, params, - &matches[cur_match_pos + shadow_matches]); - if (addon->num_chunks != 0) { - size_t cd_matches = LookupAllCompoundDictionaryMatches(addon, - ringbuffer, ringbuffer_mask, pos, 3, max_length, - dictionary_start, params->dist.max_distance, - &matches[cur_match_pos + shadow_matches - 64], 64); - MergeMatches(&matches[cur_match_pos], - &matches[cur_match_pos + shadow_matches - 64], cd_matches, - &matches[cur_match_pos + shadow_matches], num_found_matches); - num_found_matches += cd_matches; - } - cur_match_end = cur_match_pos + num_found_matches; - for (j = cur_match_pos; j + 1 < cur_match_end; ++j) { - BROTLI_DCHECK(BackwardMatchLength(&matches[j]) <= - BackwardMatchLength(&matches[j + 1])); - } - num_matches[i] = (uint32_t)num_found_matches; - if (num_found_matches > 0) { - const size_t match_len = BackwardMatchLength(&matches[cur_match_end - 1]); - if (match_len > MAX_ZOPFLI_LEN_QUALITY_11) { - const size_t skip = match_len - 1; - matches[cur_match_pos++] = matches[cur_match_end - 1]; - num_matches[i] = 1; - /* Add the tail of the copy to the hasher. */ - StoreRangeH10(&hasher->privat._H10, - ringbuffer, ringbuffer_mask, pos + 1, - BROTLI_MIN(size_t, pos + match_len, store_end)); - memset(&num_matches[i + 1], 0, skip * sizeof(num_matches[0])); - i += skip; - } else { - cur_match_pos = cur_match_end; - } - } - } - orig_num_literals = *num_literals; - orig_last_insert_len = *last_insert_len; - memcpy(orig_dist_cache, dist_cache, 4 * sizeof(dist_cache[0])); - orig_num_commands = *num_commands; - nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return; - InitZopfliCostModel(m, model, ¶ms->dist, num_bytes); - if (BROTLI_IS_OOM(m)) return; - for (i = 0; i < 2; i++) { - BrotliInitZopfliNodes(nodes, num_bytes + 1); - if (i == 0) { - ZopfliCostModelSetFromLiteralCosts( - model, position, ringbuffer, ringbuffer_mask); - } else { - ZopfliCostModelSetFromCommands(model, position, ringbuffer, - ringbuffer_mask, commands, *num_commands - orig_num_commands, - orig_last_insert_len); - } - *num_commands = orig_num_commands; - *num_literals = orig_num_literals; - *last_insert_len = orig_last_insert_len; - memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0])); - *num_commands += ZopfliIterate(num_bytes, position, ringbuffer, - ringbuffer_mask, params, gap, dist_cache, model, num_matches, matches, - nodes); - BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache, - last_insert_len, params, commands, num_literals); - } - CleanupZopfliCostModel(m, model); - BROTLI_FREE(m, model); - BROTLI_FREE(m, nodes); - BROTLI_FREE(m, matches); - BROTLI_FREE(m, num_matches); -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/backward_references_hq.h b/src/deps/brotli/enc/backward_references_hq.h deleted file mode 100644 index 8acf975ab98fe..0000000000000 --- a/src/deps/brotli/enc/backward_references_hq.h +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function to find backward reference copies. */ - -#ifndef BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ -#define BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ - -#include - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "command.h" -#include "hash.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -BROTLI_INTERNAL void BrotliCreateZopfliBackwardReferences(MemoryManager* m, - size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals); - -BROTLI_INTERNAL void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, - size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals); - -typedef struct ZopfliNode { - /* Best length to get up to this byte (not including this byte itself) - highest 7 bit is used to reconstruct the length code. */ - uint32_t length; - /* Distance associated with the length. */ - uint32_t distance; - /* Number of literal inserts before this copy; highest 5 bits contain - distance short code + 1 (or zero if no short code). */ - uint32_t dcode_insert_length; - - /* This union holds information used by dynamic-programming. During forward - pass |cost| it used to store the goal function. When node is processed its - |cost| is invalidated in favor of |shortcut|. On path back-tracing pass - |next| is assigned the offset to next node on the path. */ - union { - /* Smallest cost to get to this byte from the beginning, as found so far. */ - float cost; - /* Offset to the next node on the path. Equals to command_length() of the - next node on the path. For last node equals to BROTLI_UINT32_MAX */ - uint32_t next; - /* Node position that provides next distance for distance cache. */ - uint32_t shortcut; - } u; -} ZopfliNode; - -BROTLI_INTERNAL void BrotliInitZopfliNodes(ZopfliNode* array, size_t length); - -/* Computes the shortest path of commands from position to at most - position + num_bytes. - - On return, path->size() is the number of commands found and path[i] is the - length of the i-th command (copy length plus insert length). - Note that the sum of the lengths of all commands can be less than num_bytes. - - On return, the nodes[0..num_bytes] array will have the following - "ZopfliNode array invariant": - For each i in [1..num_bytes], if nodes[i].cost < kInfinity, then - (1) nodes[i].copy_length() >= 2 - (2) nodes[i].command_length() <= i and - (3) nodes[i - nodes[i].command_length()].cost < kInfinity */ -BROTLI_INTERNAL size_t BrotliZopfliComputeShortestPath( - MemoryManager* m, size_t num_bytes, - size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - const int* dist_cache, Hasher* hasher, ZopfliNode* nodes); - -BROTLI_INTERNAL void BrotliZopfliCreateCommands( - const size_t num_bytes, const size_t block_start, const ZopfliNode* nodes, - int* dist_cache, size_t* last_insert_len, const BrotliEncoderParams* params, - Command* commands, size_t* num_literals); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ */ diff --git a/src/deps/brotli/enc/backward_references_inc.h b/src/deps/brotli/enc/backward_references_inc.h deleted file mode 100644 index 752c12e9fd2ff..0000000000000 --- a/src/deps/brotli/enc/backward_references_inc.h +++ /dev/null @@ -1,189 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: EXPORT_FN, FN */ - -static BROTLI_NOINLINE void EXPORT_FN(CreateBackwardReferences)( - size_t num_bytes, size_t position, - const uint8_t* ringbuffer, size_t ringbuffer_mask, - ContextLut literal_context_lut, const BrotliEncoderParams* params, - Hasher* hasher, int* dist_cache, size_t* last_insert_len, - Command* commands, size_t* num_commands, size_t* num_literals) { - HASHER()* privat = &hasher->privat.FN(_); - /* Set maximum distance, see section 9.1. of the spec. */ - const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); - const size_t position_offset = params->stream_offset; - - const Command* const orig_commands = commands; - size_t insert_length = *last_insert_len; - const size_t pos_end = position + num_bytes; - const size_t store_end = num_bytes >= FN(StoreLookahead)() ? - position + num_bytes - FN(StoreLookahead)() + 1 : position; - - /* For speed up heuristics for random data. */ - const size_t random_heuristics_window_size = - LiteralSpreeLengthForSparseSearch(params); - size_t apply_random_heuristics = position + random_heuristics_window_size; - const size_t gap = params->dictionary.compound.total_size; - - /* Minimum score to accept a backward reference. */ - const score_t kMinScore = BROTLI_SCORE_BASE + 100; - - FN(PrepareDistanceCache)(privat, dist_cache); - - while (position + FN(HashTypeLength)() < pos_end) { - size_t max_length = pos_end - position; - size_t max_distance = BROTLI_MIN(size_t, position, max_backward_limit); - size_t dictionary_start = BROTLI_MIN(size_t, - position + position_offset, max_backward_limit); - HasherSearchResult sr; - int dict_id = 0; - uint8_t p1 = 0; - uint8_t p2 = 0; - if (params->dictionary.contextual.context_based) { - p1 = position >= 1 ? - ringbuffer[(size_t)(position - 1) & ringbuffer_mask] : 0; - p2 = position >= 2 ? - ringbuffer[(size_t)(position - 2) & ringbuffer_mask] : 0; - dict_id = params->dictionary.contextual.context_map[ - BROTLI_CONTEXT(p1, p2, literal_context_lut)]; - } - sr.len = 0; - sr.len_code_delta = 0; - sr.distance = 0; - sr.score = kMinScore; - FN(FindLongestMatch)(privat, params->dictionary.contextual.dict[dict_id], - ringbuffer, ringbuffer_mask, dist_cache, position, max_length, - max_distance, dictionary_start + gap, params->dist.max_distance, &sr); - if (ENABLE_COMPOUND_DICTIONARY) { - LookupCompoundDictionaryMatch(¶ms->dictionary.compound, ringbuffer, - ringbuffer_mask, dist_cache, position, max_length, - dictionary_start, params->dist.max_distance, &sr); - } - if (sr.score > kMinScore) { - /* Found a match. Let's look for something even better ahead. */ - int delayed_backward_references_in_row = 0; - --max_length; - for (;; --max_length) { - const score_t cost_diff_lazy = 175; - HasherSearchResult sr2; - sr2.len = params->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH ? - BROTLI_MIN(size_t, sr.len - 1, max_length) : 0; - sr2.len_code_delta = 0; - sr2.distance = 0; - sr2.score = kMinScore; - max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit); - dictionary_start = BROTLI_MIN(size_t, - position + 1 + position_offset, max_backward_limit); - if (params->dictionary.contextual.context_based) { - p2 = p1; - p1 = ringbuffer[position & ringbuffer_mask]; - dict_id = params->dictionary.contextual.context_map[ - BROTLI_CONTEXT(p1, p2, literal_context_lut)]; - } - FN(FindLongestMatch)(privat, - params->dictionary.contextual.dict[dict_id], - ringbuffer, ringbuffer_mask, dist_cache, position + 1, max_length, - max_distance, dictionary_start + gap, params->dist.max_distance, - &sr2); - if (ENABLE_COMPOUND_DICTIONARY) { - LookupCompoundDictionaryMatch( - ¶ms->dictionary.compound, ringbuffer, - ringbuffer_mask, dist_cache, position + 1, max_length, - dictionary_start, params->dist.max_distance, &sr2); - } - if (sr2.score >= sr.score + cost_diff_lazy) { - /* Ok, let's just write one byte for now and start a match from the - next byte. */ - ++position; - ++insert_length; - sr = sr2; - if (++delayed_backward_references_in_row < 4 && - position + FN(HashTypeLength)() < pos_end) { - continue; - } - } - break; - } - apply_random_heuristics = - position + 2 * sr.len + random_heuristics_window_size; - dictionary_start = BROTLI_MIN(size_t, - position + position_offset, max_backward_limit); - { - /* The first 16 codes are special short-codes, - and the minimum offset is 1. */ - size_t distance_code = ComputeDistanceCode( - sr.distance, dictionary_start + gap, dist_cache); - if ((sr.distance <= (dictionary_start + gap)) && distance_code > 0) { - dist_cache[3] = dist_cache[2]; - dist_cache[2] = dist_cache[1]; - dist_cache[1] = dist_cache[0]; - dist_cache[0] = (int)sr.distance; - FN(PrepareDistanceCache)(privat, dist_cache); - } - InitCommand(commands++, ¶ms->dist, insert_length, - sr.len, sr.len_code_delta, distance_code); - } - *num_literals += insert_length; - insert_length = 0; - /* Put the hash keys into the table, if there are enough bytes left. - Depending on the hasher implementation, it can push all positions - in the given range or only a subset of them. - Avoid hash poisoning with RLE data. */ - { - size_t range_start = position + 2; - size_t range_end = BROTLI_MIN(size_t, position + sr.len, store_end); - if (sr.distance < (sr.len >> 2)) { - range_start = BROTLI_MIN(size_t, range_end, BROTLI_MAX(size_t, - range_start, position + sr.len - (sr.distance << 2))); - } - FN(StoreRange)(privat, ringbuffer, ringbuffer_mask, range_start, - range_end); - } - position += sr.len; - } else { - ++insert_length; - ++position; - /* If we have not seen matches for a long time, we can skip some - match lookups. Unsuccessful match lookups are very very expensive - and this kind of a heuristic speeds up compression quite - a lot. */ - if (position > apply_random_heuristics) { - /* Going through uncompressible data, jump. */ - if (position > - apply_random_heuristics + 4 * random_heuristics_window_size) { - /* It is quite a long time since we saw a copy, so we assume - that this data is not compressible, and store hashes less - often. Hashes of non compressible data are less likely to - turn out to be useful in the future, too, so we store less of - them to not to flood out the hash table of good compressible - data. */ - const size_t kMargin = - BROTLI_MAX(size_t, FN(StoreLookahead)() - 1, 4); - size_t pos_jump = - BROTLI_MIN(size_t, position + 16, pos_end - kMargin); - for (; position < pos_jump; position += 4) { - FN(Store)(privat, ringbuffer, ringbuffer_mask, position); - insert_length += 4; - } - } else { - const size_t kMargin = - BROTLI_MAX(size_t, FN(StoreLookahead)() - 1, 2); - size_t pos_jump = - BROTLI_MIN(size_t, position + 8, pos_end - kMargin); - for (; position < pos_jump; position += 2) { - FN(Store)(privat, ringbuffer, ringbuffer_mask, position); - insert_length += 2; - } - } - } - } - } - insert_length += pos_end - position; - *last_insert_len = insert_length; - *num_commands += (size_t)(commands - orig_commands); -} diff --git a/src/deps/brotli/enc/bit_cost.c b/src/deps/brotli/enc/bit_cost.c deleted file mode 100644 index 6b7c904cede9e..0000000000000 --- a/src/deps/brotli/enc/bit_cost.c +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions to estimate the bit cost of Huffman trees. */ - -#include "bit_cost.h" - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "fast_log.h" -#include "histogram.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define FN(X) X ## Literal -#include "bit_cost_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Command -#include "bit_cost_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Distance -#include "bit_cost_inc.h" /* NOLINT(build/include) */ -#undef FN - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/bit_cost.h b/src/deps/brotli/enc/bit_cost.h deleted file mode 100644 index f6f2773994a45..0000000000000 --- a/src/deps/brotli/enc/bit_cost.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions to estimate the bit cost of Huffman trees. */ - -#ifndef BROTLI_ENC_BIT_COST_H_ -#define BROTLI_ENC_BIT_COST_H_ - -#include - -#include "../common/platform.h" -#include "fast_log.h" -#include "histogram.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static BROTLI_INLINE double ShannonEntropy( - const uint32_t* population, size_t size, size_t* total) { - size_t sum = 0; - double retval = 0; - const uint32_t* population_end = population + size; - size_t p; - if (size & 1) { - goto odd_number_of_elements_left; - } - while (population < population_end) { - p = *population++; - sum += p; - retval -= (double)p * FastLog2(p); - odd_number_of_elements_left: - p = *population++; - sum += p; - retval -= (double)p * FastLog2(p); - } - if (sum) retval += (double)sum * FastLog2(sum); - *total = sum; - return retval; -} - -static BROTLI_INLINE double BitsEntropy( - const uint32_t* population, size_t size) { - size_t sum; - double retval = ShannonEntropy(population, size, &sum); - if (retval < (double)sum) { - /* At least one bit per literal is needed. */ - retval = (double)sum; - } - return retval; -} - -BROTLI_INTERNAL double BrotliPopulationCostLiteral(const HistogramLiteral*); -BROTLI_INTERNAL double BrotliPopulationCostCommand(const HistogramCommand*); -BROTLI_INTERNAL double BrotliPopulationCostDistance(const HistogramDistance*); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_BIT_COST_H_ */ diff --git a/src/deps/brotli/enc/bit_cost_inc.h b/src/deps/brotli/enc/bit_cost_inc.h deleted file mode 100644 index 453c226042093..0000000000000 --- a/src/deps/brotli/enc/bit_cost_inc.h +++ /dev/null @@ -1,127 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN */ - -#define HistogramType FN(Histogram) - -double FN(BrotliPopulationCost)(const HistogramType* histogram) { - static const double kOneSymbolHistogramCost = 12; - static const double kTwoSymbolHistogramCost = 20; - static const double kThreeSymbolHistogramCost = 28; - static const double kFourSymbolHistogramCost = 37; - const size_t data_size = FN(HistogramDataSize)(); - int count = 0; - size_t s[5]; - double bits = 0.0; - size_t i; - if (histogram->total_count_ == 0) { - return kOneSymbolHistogramCost; - } - for (i = 0; i < data_size; ++i) { - if (histogram->data_[i] > 0) { - s[count] = i; - ++count; - if (count > 4) break; - } - } - if (count == 1) { - return kOneSymbolHistogramCost; - } - if (count == 2) { - return (kTwoSymbolHistogramCost + (double)histogram->total_count_); - } - if (count == 3) { - const uint32_t histo0 = histogram->data_[s[0]]; - const uint32_t histo1 = histogram->data_[s[1]]; - const uint32_t histo2 = histogram->data_[s[2]]; - const uint32_t histomax = - BROTLI_MAX(uint32_t, histo0, BROTLI_MAX(uint32_t, histo1, histo2)); - return (kThreeSymbolHistogramCost + - 2 * (histo0 + histo1 + histo2) - histomax); - } - if (count == 4) { - uint32_t histo[4]; - uint32_t h23; - uint32_t histomax; - for (i = 0; i < 4; ++i) { - histo[i] = histogram->data_[s[i]]; - } - /* Sort */ - for (i = 0; i < 4; ++i) { - size_t j; - for (j = i + 1; j < 4; ++j) { - if (histo[j] > histo[i]) { - BROTLI_SWAP(uint32_t, histo, j, i); - } - } - } - h23 = histo[2] + histo[3]; - histomax = BROTLI_MAX(uint32_t, h23, histo[0]); - return (kFourSymbolHistogramCost + - 3 * h23 + 2 * (histo[0] + histo[1]) - histomax); - } - - { - /* In this loop we compute the entropy of the histogram and simultaneously - build a simplified histogram of the code length codes where we use the - zero repeat code 17, but we don't use the non-zero repeat code 16. */ - size_t max_depth = 1; - uint32_t depth_histo[BROTLI_CODE_LENGTH_CODES] = { 0 }; - const double log2total = FastLog2(histogram->total_count_); - for (i = 0; i < data_size;) { - if (histogram->data_[i] > 0) { - /* Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) = - = log2(total_count) - log2(count(symbol)) */ - double log2p = log2total - FastLog2(histogram->data_[i]); - /* Approximate the bit depth by round(-log2(P(symbol))) */ - size_t depth = (size_t)(log2p + 0.5); - bits += histogram->data_[i] * log2p; - if (depth > 15) { - depth = 15; - } - if (depth > max_depth) { - max_depth = depth; - } - ++depth_histo[depth]; - ++i; - } else { - /* Compute the run length of zeros and add the appropriate number of 0 - and 17 code length codes to the code length code histogram. */ - uint32_t reps = 1; - size_t k; - for (k = i + 1; k < data_size && histogram->data_[k] == 0; ++k) { - ++reps; - } - i += reps; - if (i == data_size) { - /* Don't add any cost for the last zero run, since these are encoded - only implicitly. */ - break; - } - if (reps < 3) { - depth_histo[0] += reps; - } else { - reps -= 2; - while (reps > 0) { - ++depth_histo[BROTLI_REPEAT_ZERO_CODE_LENGTH]; - /* Add the 3 extra bits for the 17 code length code. */ - bits += 3; - reps >>= 3; - } - } - } - } - /* Add the estimated encoding cost of the code length code histogram. */ - bits += (double)(18 + 2 * max_depth); - /* Add the entropy of the code length code histogram. */ - bits += BitsEntropy(depth_histo, BROTLI_CODE_LENGTH_CODES); - } - return bits; -} - -#undef HistogramType diff --git a/src/deps/brotli/enc/block_encoder_inc.h b/src/deps/brotli/enc/block_encoder_inc.h deleted file mode 100644 index 8cbd5eac67b2a..0000000000000 --- a/src/deps/brotli/enc/block_encoder_inc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2014 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN */ - -#define HistogramType FN(Histogram) - -/* Creates entropy codes for all block types and stores them to the bit - stream. */ -static void FN(BuildAndStoreEntropyCodes)(MemoryManager* m, BlockEncoder* self, - const HistogramType* histograms, const size_t histograms_size, - const size_t alphabet_size, HuffmanTree* tree, - size_t* storage_ix, uint8_t* storage) { - const size_t table_size = histograms_size * self->histogram_length_; - self->depths_ = BROTLI_ALLOC(m, uint8_t, table_size); - self->bits_ = BROTLI_ALLOC(m, uint16_t, table_size); - if (BROTLI_IS_OOM(m)) return; - - { - size_t i; - for (i = 0; i < histograms_size; ++i) { - size_t ix = i * self->histogram_length_; - BuildAndStoreHuffmanTree(&histograms[i].data_[0], self->histogram_length_, - alphabet_size, tree, &self->depths_[ix], &self->bits_[ix], - storage_ix, storage); - } - } -} - -#undef HistogramType diff --git a/src/deps/brotli/enc/block_splitter.c b/src/deps/brotli/enc/block_splitter.c deleted file mode 100644 index eba1b691e5100..0000000000000 --- a/src/deps/brotli/enc/block_splitter.c +++ /dev/null @@ -1,217 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Block split point selection utilities. */ - -#include "block_splitter.h" - -#include /* memcpy, memset */ - -#include "../common/platform.h" -#include "bit_cost.h" -#include "cluster.h" -#include "command.h" -#include "fast_log.h" -#include "histogram.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static const size_t kMaxLiteralHistograms = 100; -static const size_t kMaxCommandHistograms = 50; -static const double kLiteralBlockSwitchCost = 28.1; -static const double kCommandBlockSwitchCost = 13.5; -static const double kDistanceBlockSwitchCost = 14.6; -static const size_t kLiteralStrideLength = 70; -static const size_t kCommandStrideLength = 40; -static const size_t kDistanceStrideLength = 40; -static const size_t kSymbolsPerLiteralHistogram = 544; -static const size_t kSymbolsPerCommandHistogram = 530; -static const size_t kSymbolsPerDistanceHistogram = 544; -static const size_t kMinLengthForBlockSplitting = 128; -static const size_t kIterMulForRefining = 2; -static const size_t kMinItersForRefining = 100; - -static size_t CountLiterals(const Command* cmds, const size_t num_commands) { - /* Count how many we have. */ - size_t total_length = 0; - size_t i; - for (i = 0; i < num_commands; ++i) { - total_length += cmds[i].insert_len_; - } - return total_length; -} - -static void CopyLiteralsToByteArray(const Command* cmds, - const size_t num_commands, - const uint8_t* data, - const size_t offset, - const size_t mask, - uint8_t* literals) { - size_t pos = 0; - size_t from_pos = offset & mask; - size_t i; - for (i = 0; i < num_commands; ++i) { - size_t insert_len = cmds[i].insert_len_; - if (from_pos + insert_len > mask) { - size_t head_size = mask + 1 - from_pos; - memcpy(literals + pos, data + from_pos, head_size); - from_pos = 0; - pos += head_size; - insert_len -= head_size; - } - if (insert_len > 0) { - memcpy(literals + pos, data + from_pos, insert_len); - pos += insert_len; - } - from_pos = (from_pos + insert_len + CommandCopyLen(&cmds[i])) & mask; - } -} - -static BROTLI_INLINE uint32_t MyRand(uint32_t* seed) { - /* Initial seed should be 7. In this case, loop length is (1 << 29). */ - *seed *= 16807U; - return *seed; -} - -static BROTLI_INLINE double BitCost(size_t count) { - return count == 0 ? -2.0 : FastLog2(count); -} - -#define HISTOGRAMS_PER_BATCH 64 -#define CLUSTERS_PER_BATCH 16 - -#define FN(X) X ## Literal -#define DataType uint8_t -/* NOLINTNEXTLINE(build/include) */ -#include "block_splitter_inc.h" -#undef DataType -#undef FN - -#define FN(X) X ## Command -#define DataType uint16_t -/* NOLINTNEXTLINE(build/include) */ -#include "block_splitter_inc.h" -#undef FN - -#define FN(X) X ## Distance -/* NOLINTNEXTLINE(build/include) */ -#include "block_splitter_inc.h" -#undef DataType -#undef FN - -void BrotliInitBlockSplit(BlockSplit* self) { - self->num_types = 0; - self->num_blocks = 0; - self->types = 0; - self->lengths = 0; - self->types_alloc_size = 0; - self->lengths_alloc_size = 0; -} - -void BrotliDestroyBlockSplit(MemoryManager* m, BlockSplit* self) { - BROTLI_FREE(m, self->types); - BROTLI_FREE(m, self->lengths); -} - -/* Extracts literals, command distance and prefix codes, then applies - * SplitByteVector to create partitioning. */ -void BrotliSplitBlock(MemoryManager* m, - const Command* cmds, - const size_t num_commands, - const uint8_t* data, - const size_t pos, - const size_t mask, - const BrotliEncoderParams* params, - BlockSplit* literal_split, - BlockSplit* insert_and_copy_split, - BlockSplit* dist_split) { - { - size_t literals_count = CountLiterals(cmds, num_commands); - uint8_t* literals = BROTLI_ALLOC(m, uint8_t, literals_count); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(literals)) return; - /* Create a continuous array of literals. */ - CopyLiteralsToByteArray(cmds, num_commands, data, pos, mask, literals); - /* Create the block split on the array of literals. - * Literal histograms can have alphabet size up to 256. - * Though, to accomodate context modeling, less than half of maximum size - * is allowed. */ - SplitByteVectorLiteral( - m, literals, literals_count, - kSymbolsPerLiteralHistogram, kMaxLiteralHistograms, - kLiteralStrideLength, kLiteralBlockSwitchCost, params, - literal_split); - if (BROTLI_IS_OOM(m)) return; - BROTLI_FREE(m, literals); - /* NB: this might be a good place for injecting extra splitting without - * increasing encoder complexity; however, output parition would be less - * optimal than one produced with forced splitting inside - * SplitByteVector (FindBlocks / ClusterBlocks). */ - } - - { - /* Compute prefix codes for commands. */ - uint16_t* insert_and_copy_codes = BROTLI_ALLOC(m, uint16_t, num_commands); - size_t i; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(insert_and_copy_codes)) return; - for (i = 0; i < num_commands; ++i) { - insert_and_copy_codes[i] = cmds[i].cmd_prefix_; - } - /* Create the block split on the array of command prefixes. */ - SplitByteVectorCommand( - m, insert_and_copy_codes, num_commands, - kSymbolsPerCommandHistogram, kMaxCommandHistograms, - kCommandStrideLength, kCommandBlockSwitchCost, params, - insert_and_copy_split); - if (BROTLI_IS_OOM(m)) return; - /* TODO(eustas): reuse for distances? */ - BROTLI_FREE(m, insert_and_copy_codes); - } - - { - /* Create a continuous array of distance prefixes. */ - uint16_t* distance_prefixes = BROTLI_ALLOC(m, uint16_t, num_commands); - size_t j = 0; - size_t i; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(distance_prefixes)) return; - for (i = 0; i < num_commands; ++i) { - const Command* cmd = &cmds[i]; - if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) { - distance_prefixes[j++] = cmd->dist_prefix_ & 0x3FF; - } - } - /* Create the block split on the array of distance prefixes. */ - SplitByteVectorDistance( - m, distance_prefixes, j, - kSymbolsPerDistanceHistogram, kMaxCommandHistograms, - kDistanceStrideLength, kDistanceBlockSwitchCost, params, - dist_split); - if (BROTLI_IS_OOM(m)) return; - BROTLI_FREE(m, distance_prefixes); - } -} - -#if defined(BROTLI_TEST) -size_t CountLiteralsForTest(const Command*, const size_t); -size_t CountLiteralsForTest(const Command* cmds, const size_t num_commands) { - return CountLiterals(cmds, num_commands); -} - -void CopyLiteralsToByteArrayForTest(const Command*, - const size_t, const uint8_t*, const size_t, const size_t, uint8_t*); -void CopyLiteralsToByteArrayForTest(const Command* cmds, - const size_t num_commands, const uint8_t* data, const size_t offset, - const size_t mask, uint8_t* literals) { - CopyLiteralsToByteArray(cmds, num_commands, data, offset, mask, literals); -} -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/block_splitter.h b/src/deps/brotli/enc/block_splitter.h deleted file mode 100644 index 6046b90a5deb7..0000000000000 --- a/src/deps/brotli/enc/block_splitter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Block split point selection utilities. */ - -#ifndef BROTLI_ENC_BLOCK_SPLITTER_H_ -#define BROTLI_ENC_BLOCK_SPLITTER_H_ - -#include - -#include "../common/platform.h" -#include "command.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct BlockSplit { - size_t num_types; /* Amount of distinct types */ - size_t num_blocks; /* Amount of values in types and length */ - uint8_t* types; - uint32_t* lengths; - - size_t types_alloc_size; - size_t lengths_alloc_size; -} BlockSplit; - -BROTLI_INTERNAL void BrotliInitBlockSplit(BlockSplit* self); -BROTLI_INTERNAL void BrotliDestroyBlockSplit(MemoryManager* m, - BlockSplit* self); - -BROTLI_INTERNAL void BrotliSplitBlock(MemoryManager* m, - const Command* cmds, - const size_t num_commands, - const uint8_t* data, - const size_t offset, - const size_t mask, - const BrotliEncoderParams* params, - BlockSplit* literal_split, - BlockSplit* insert_and_copy_split, - BlockSplit* dist_split); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_BLOCK_SPLITTER_H_ */ diff --git a/src/deps/brotli/enc/block_splitter_inc.h b/src/deps/brotli/enc/block_splitter_inc.h deleted file mode 100644 index aa40bfd329d54..0000000000000 --- a/src/deps/brotli/enc/block_splitter_inc.h +++ /dev/null @@ -1,481 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, DataType */ - -#define HistogramType FN(Histogram) - -static void FN(InitialEntropyCodes)(const DataType* data, size_t length, - size_t stride, - size_t num_histograms, - HistogramType* histograms) { - uint32_t seed = 7; - size_t block_length = length / num_histograms; - size_t i; - FN(ClearHistograms)(histograms, num_histograms); - for (i = 0; i < num_histograms; ++i) { - size_t pos = length * i / num_histograms; - if (i != 0) { - pos += MyRand(&seed) % block_length; - } - if (pos + stride >= length) { - pos = length - stride - 1; - } - FN(HistogramAddVector)(&histograms[i], data + pos, stride); - } -} - -static void FN(RandomSample)(uint32_t* seed, - const DataType* data, - size_t length, - size_t stride, - HistogramType* sample) { - size_t pos = 0; - if (stride >= length) { - stride = length; - } else { - pos = MyRand(seed) % (length - stride + 1); - } - FN(HistogramAddVector)(sample, data + pos, stride); -} - -static void FN(RefineEntropyCodes)(const DataType* data, size_t length, - size_t stride, - size_t num_histograms, - HistogramType* histograms, - HistogramType* tmp) { - size_t iters = - kIterMulForRefining * length / stride + kMinItersForRefining; - uint32_t seed = 7; - size_t iter; - iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms; - for (iter = 0; iter < iters; ++iter) { - FN(HistogramClear)(tmp); - FN(RandomSample)(&seed, data, length, stride, tmp); - FN(HistogramAddHistogram)(&histograms[iter % num_histograms], tmp); - } -} - -/* Assigns a block id from the range [0, num_histograms) to each data element - in data[0..length) and fills in block_id[0..length) with the assigned values. - Returns the number of blocks, i.e. one plus the number of block switches. */ -static size_t FN(FindBlocks)(const DataType* data, const size_t length, - const double block_switch_bitcost, - const size_t num_histograms, - const HistogramType* histograms, - double* insert_cost, - double* cost, - uint8_t* switch_signal, - uint8_t* block_id) { - const size_t alphabet_size = FN(HistogramDataSize)(); - const size_t bitmap_len = (num_histograms + 7) >> 3; - size_t num_blocks = 1; - size_t byte_ix; - size_t i; - size_t j; - BROTLI_DCHECK(num_histograms <= 256); - - /* Trivial case: single historgram -> single block type. */ - if (num_histograms <= 1) { - for (i = 0; i < length; ++i) { - block_id[i] = 0; - } - return 1; - } - - /* Fill bitcost for each symbol of all histograms. - * Non-existing symbol cost: 2 + log2(total_count). - * Regular symbol cost: -log2(symbol_count / total_count). */ - memset(insert_cost, 0, - sizeof(insert_cost[0]) * alphabet_size * num_histograms); - for (i = 0; i < num_histograms; ++i) { - insert_cost[i] = FastLog2((uint32_t)histograms[i].total_count_); - } - for (i = alphabet_size; i != 0;) { - /* Reverse order to use the 0-th row as a temporary storage. */ - --i; - for (j = 0; j < num_histograms; ++j) { - insert_cost[i * num_histograms + j] = - insert_cost[j] - BitCost(histograms[j].data_[i]); - } - } - - /* After each iteration of this loop, cost[k] will contain the difference - between the minimum cost of arriving at the current byte position using - entropy code k, and the minimum cost of arriving at the current byte - position. This difference is capped at the block switch cost, and if it - reaches block switch cost, it means that when we trace back from the last - position, we need to switch here. */ - memset(cost, 0, sizeof(cost[0]) * num_histograms); - memset(switch_signal, 0, sizeof(switch_signal[0]) * length * bitmap_len); - for (byte_ix = 0; byte_ix < length; ++byte_ix) { - size_t ix = byte_ix * bitmap_len; - size_t symbol = data[byte_ix]; - size_t insert_cost_ix = symbol * num_histograms; - double min_cost = 1e99; - double block_switch_cost = block_switch_bitcost; - size_t k; - for (k = 0; k < num_histograms; ++k) { - /* We are coding the symbol with entropy code k. */ - cost[k] += insert_cost[insert_cost_ix + k]; - if (cost[k] < min_cost) { - min_cost = cost[k]; - block_id[byte_ix] = (uint8_t)k; - } - } - /* More blocks for the beginning. */ - if (byte_ix < 2000) { - block_switch_cost *= 0.77 + 0.07 * (double)byte_ix / 2000; - } - for (k = 0; k < num_histograms; ++k) { - cost[k] -= min_cost; - if (cost[k] >= block_switch_cost) { - const uint8_t mask = (uint8_t)(1u << (k & 7)); - cost[k] = block_switch_cost; - BROTLI_DCHECK((k >> 3) < bitmap_len); - switch_signal[ix + (k >> 3)] |= mask; - } - } - } - - byte_ix = length - 1; - { /* Trace back from the last position and switch at the marked places. */ - size_t ix = byte_ix * bitmap_len; - uint8_t cur_id = block_id[byte_ix]; - while (byte_ix > 0) { - const uint8_t mask = (uint8_t)(1u << (cur_id & 7)); - BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmap_len); - --byte_ix; - ix -= bitmap_len; - if (switch_signal[ix + (cur_id >> 3)] & mask) { - if (cur_id != block_id[byte_ix]) { - cur_id = block_id[byte_ix]; - ++num_blocks; - } - } - block_id[byte_ix] = cur_id; - } - } - return num_blocks; -} - -static size_t FN(RemapBlockIds)(uint8_t* block_ids, const size_t length, - uint16_t* new_id, const size_t num_histograms) { - static const uint16_t kInvalidId = 256; - uint16_t next_id = 0; - size_t i; - for (i = 0; i < num_histograms; ++i) { - new_id[i] = kInvalidId; - } - for (i = 0; i < length; ++i) { - BROTLI_DCHECK(block_ids[i] < num_histograms); - if (new_id[block_ids[i]] == kInvalidId) { - new_id[block_ids[i]] = next_id++; - } - } - for (i = 0; i < length; ++i) { - block_ids[i] = (uint8_t)new_id[block_ids[i]]; - BROTLI_DCHECK(block_ids[i] < num_histograms); - } - BROTLI_DCHECK(next_id <= num_histograms); - return next_id; -} - -static void FN(BuildBlockHistograms)(const DataType* data, const size_t length, - const uint8_t* block_ids, - const size_t num_histograms, - HistogramType* histograms) { - size_t i; - FN(ClearHistograms)(histograms, num_histograms); - for (i = 0; i < length; ++i) { - FN(HistogramAdd)(&histograms[block_ids[i]], data[i]); - } -} - -/* Given the initial partitioning build partitioning with limited number - * of histograms (and block types). */ -static void FN(ClusterBlocks)(MemoryManager* m, - const DataType* data, const size_t length, - const size_t num_blocks, - uint8_t* block_ids, - BlockSplit* split) { - uint32_t* histogram_symbols = BROTLI_ALLOC(m, uint32_t, num_blocks); - uint32_t* u32 = - BROTLI_ALLOC(m, uint32_t, num_blocks + 4 * HISTOGRAMS_PER_BATCH); - const size_t expected_num_clusters = CLUSTERS_PER_BATCH * - (num_blocks + HISTOGRAMS_PER_BATCH - 1) / HISTOGRAMS_PER_BATCH; - size_t all_histograms_size = 0; - size_t all_histograms_capacity = expected_num_clusters; - HistogramType* all_histograms = - BROTLI_ALLOC(m, HistogramType, all_histograms_capacity); - size_t cluster_size_size = 0; - size_t cluster_size_capacity = expected_num_clusters; - uint32_t* cluster_size = BROTLI_ALLOC(m, uint32_t, cluster_size_capacity); - size_t num_clusters = 0; - HistogramType* histograms = BROTLI_ALLOC(m, HistogramType, - BROTLI_MIN(size_t, num_blocks, HISTOGRAMS_PER_BATCH)); - size_t max_num_pairs = - HISTOGRAMS_PER_BATCH * HISTOGRAMS_PER_BATCH / 2; - size_t pairs_capacity = max_num_pairs + 1; - HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity); - size_t pos = 0; - uint32_t* clusters; - size_t num_final_clusters; - static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX; - uint32_t* new_index; - size_t i; - uint32_t* BROTLI_RESTRICT const sizes = u32 + 0 * HISTOGRAMS_PER_BATCH; - uint32_t* BROTLI_RESTRICT const new_clusters = u32 + 1 * HISTOGRAMS_PER_BATCH; - uint32_t* BROTLI_RESTRICT const symbols = u32 + 2 * HISTOGRAMS_PER_BATCH; - uint32_t* BROTLI_RESTRICT const remap = u32 + 3 * HISTOGRAMS_PER_BATCH; - uint32_t* BROTLI_RESTRICT const block_lengths = - u32 + 4 * HISTOGRAMS_PER_BATCH; - /* TODO(eustas): move to arena? */ - HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 2); - - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histogram_symbols) || - BROTLI_IS_NULL(u32) || BROTLI_IS_NULL(all_histograms) || - BROTLI_IS_NULL(cluster_size) || BROTLI_IS_NULL(histograms) || - BROTLI_IS_NULL(pairs) || BROTLI_IS_NULL(tmp)) { - return; - } - - memset(u32, 0, (num_blocks + 4 * HISTOGRAMS_PER_BATCH) * sizeof(uint32_t)); - - /* Calculate block lengths (convert repeating values -> series length). */ - { - size_t block_idx = 0; - for (i = 0; i < length; ++i) { - BROTLI_DCHECK(block_idx < num_blocks); - ++block_lengths[block_idx]; - if (i + 1 == length || block_ids[i] != block_ids[i + 1]) { - ++block_idx; - } - } - BROTLI_DCHECK(block_idx == num_blocks); - } - - /* Pre-cluster blocks (cluster batches). */ - for (i = 0; i < num_blocks; i += HISTOGRAMS_PER_BATCH) { - const size_t num_to_combine = - BROTLI_MIN(size_t, num_blocks - i, HISTOGRAMS_PER_BATCH); - size_t num_new_clusters; - size_t j; - for (j = 0; j < num_to_combine; ++j) { - size_t k; - size_t block_length = block_lengths[i + j]; - FN(HistogramClear)(&histograms[j]); - for (k = 0; k < block_length; ++k) { - FN(HistogramAdd)(&histograms[j], data[pos++]); - } - histograms[j].bit_cost_ = FN(BrotliPopulationCost)(&histograms[j]); - new_clusters[j] = (uint32_t)j; - symbols[j] = (uint32_t)j; - sizes[j] = 1; - } - num_new_clusters = FN(BrotliHistogramCombine)( - histograms, tmp, sizes, symbols, new_clusters, pairs, num_to_combine, - num_to_combine, HISTOGRAMS_PER_BATCH, max_num_pairs); - BROTLI_ENSURE_CAPACITY(m, HistogramType, all_histograms, - all_histograms_capacity, all_histograms_size + num_new_clusters); - BROTLI_ENSURE_CAPACITY(m, uint32_t, cluster_size, - cluster_size_capacity, cluster_size_size + num_new_clusters); - if (BROTLI_IS_OOM(m)) return; - for (j = 0; j < num_new_clusters; ++j) { - all_histograms[all_histograms_size++] = histograms[new_clusters[j]]; - cluster_size[cluster_size_size++] = sizes[new_clusters[j]]; - remap[new_clusters[j]] = (uint32_t)j; - } - for (j = 0; j < num_to_combine; ++j) { - histogram_symbols[i + j] = (uint32_t)num_clusters + remap[symbols[j]]; - } - num_clusters += num_new_clusters; - BROTLI_DCHECK(num_clusters == cluster_size_size); - BROTLI_DCHECK(num_clusters == all_histograms_size); - } - BROTLI_FREE(m, histograms); - - /* Final clustering. */ - max_num_pairs = - BROTLI_MIN(size_t, 64 * num_clusters, (num_clusters / 2) * num_clusters); - if (pairs_capacity < max_num_pairs + 1) { - BROTLI_FREE(m, pairs); - pairs = BROTLI_ALLOC(m, HistogramPair, max_num_pairs + 1); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(pairs)) return; - } - clusters = BROTLI_ALLOC(m, uint32_t, num_clusters); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(clusters)) return; - for (i = 0; i < num_clusters; ++i) { - clusters[i] = (uint32_t)i; - } - num_final_clusters = FN(BrotliHistogramCombine)( - all_histograms, tmp, cluster_size, histogram_symbols, clusters, pairs, - num_clusters, num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES, - max_num_pairs); - BROTLI_FREE(m, pairs); - BROTLI_FREE(m, cluster_size); - - /* Assign blocks to final histograms. */ - new_index = BROTLI_ALLOC(m, uint32_t, num_clusters); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return; - for (i = 0; i < num_clusters; ++i) new_index[i] = kInvalidIndex; - pos = 0; - { - uint32_t next_index = 0; - for (i = 0; i < num_blocks; ++i) { - size_t j; - uint32_t best_out; - double best_bits; - FN(HistogramClear)(tmp); - for (j = 0; j < block_lengths[i]; ++j) { - FN(HistogramAdd)(tmp, data[pos++]); - } - /* Among equally good histograms prefer last used. */ - /* TODO(eustas): should we give a block-switch discount here? */ - best_out = (i == 0) ? histogram_symbols[0] : histogram_symbols[i - 1]; - best_bits = FN(BrotliHistogramBitCostDistance)( - tmp, &all_histograms[best_out], tmp + 1); - for (j = 0; j < num_final_clusters; ++j) { - const double cur_bits = FN(BrotliHistogramBitCostDistance)( - tmp, &all_histograms[clusters[j]], tmp + 1); - if (cur_bits < best_bits) { - best_bits = cur_bits; - best_out = clusters[j]; - } - } - histogram_symbols[i] = best_out; - if (new_index[best_out] == kInvalidIndex) { - new_index[best_out] = next_index++; - } - } - } - BROTLI_FREE(m, tmp); - BROTLI_FREE(m, clusters); - BROTLI_FREE(m, all_histograms); - BROTLI_ENSURE_CAPACITY( - m, uint8_t, split->types, split->types_alloc_size, num_blocks); - BROTLI_ENSURE_CAPACITY( - m, uint32_t, split->lengths, split->lengths_alloc_size, num_blocks); - if (BROTLI_IS_OOM(m)) return; - - /* Rewrite final assignment to block-split. There might be less blocks - * than |num_blocks| due to clustering. */ - { - uint32_t cur_length = 0; - size_t block_idx = 0; - uint8_t max_type = 0; - for (i = 0; i < num_blocks; ++i) { - cur_length += block_lengths[i]; - if (i + 1 == num_blocks || - histogram_symbols[i] != histogram_symbols[i + 1]) { - const uint8_t id = (uint8_t)new_index[histogram_symbols[i]]; - split->types[block_idx] = id; - split->lengths[block_idx] = cur_length; - max_type = BROTLI_MAX(uint8_t, max_type, id); - cur_length = 0; - ++block_idx; - } - } - split->num_blocks = block_idx; - split->num_types = (size_t)max_type + 1; - } - BROTLI_FREE(m, new_index); - BROTLI_FREE(m, u32); - BROTLI_FREE(m, histogram_symbols); -} - -/* Create BlockSplit (partitioning) given the limits, estimates and "effort" - * parameters. - * - * NB: max_histograms is often less than number of histograms allowed by format; - * this is done intentionally, to save some "space" for context-aware - * clustering (here entropy is estimated for context-free symbols). */ -static void FN(SplitByteVector)(MemoryManager* m, - const DataType* data, const size_t length, - const size_t symbols_per_histogram, - const size_t max_histograms, - const size_t sampling_stride_length, - const double block_switch_cost, - const BrotliEncoderParams* params, - BlockSplit* split) { - const size_t data_size = FN(HistogramDataSize)(); - HistogramType* histograms; - HistogramType* tmp; - /* Calculate number of histograms; initial estimate is one histogram per - * specified amount of symbols; however, this value is capped. */ - size_t num_histograms = length / symbols_per_histogram + 1; - if (num_histograms > max_histograms) { - num_histograms = max_histograms; - } - - /* Corner case: no input. */ - if (length == 0) { - split->num_types = 1; - return; - } - - if (length < kMinLengthForBlockSplitting) { - BROTLI_ENSURE_CAPACITY(m, uint8_t, - split->types, split->types_alloc_size, split->num_blocks + 1); - BROTLI_ENSURE_CAPACITY(m, uint32_t, - split->lengths, split->lengths_alloc_size, split->num_blocks + 1); - if (BROTLI_IS_OOM(m)) return; - split->num_types = 1; - split->types[split->num_blocks] = 0; - split->lengths[split->num_blocks] = (uint32_t)length; - split->num_blocks++; - return; - } - histograms = BROTLI_ALLOC(m, HistogramType, num_histograms + 1); - tmp = histograms + num_histograms; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histograms)) return; - /* Find good entropy codes. */ - FN(InitialEntropyCodes)(data, length, - sampling_stride_length, - num_histograms, histograms); - FN(RefineEntropyCodes)(data, length, - sampling_stride_length, - num_histograms, histograms, tmp); - { - /* Find a good path through literals with the good entropy codes. */ - uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length); - size_t num_blocks = 0; - const size_t bitmaplen = (num_histograms + 7) >> 3; - double* insert_cost = BROTLI_ALLOC(m, double, data_size * num_histograms); - double* cost = BROTLI_ALLOC(m, double, num_histograms); - uint8_t* switch_signal = BROTLI_ALLOC(m, uint8_t, length * bitmaplen); - uint16_t* new_id = BROTLI_ALLOC(m, uint16_t, num_histograms); - const size_t iters = params->quality < HQ_ZOPFLIFICATION_QUALITY ? 3 : 10; - size_t i; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(block_ids) || - BROTLI_IS_NULL(insert_cost) || BROTLI_IS_NULL(cost) || - BROTLI_IS_NULL(switch_signal) || BROTLI_IS_NULL(new_id)) { - return; - } - for (i = 0; i < iters; ++i) { - num_blocks = FN(FindBlocks)(data, length, - block_switch_cost, - num_histograms, histograms, - insert_cost, cost, switch_signal, - block_ids); - num_histograms = FN(RemapBlockIds)(block_ids, length, - new_id, num_histograms); - FN(BuildBlockHistograms)(data, length, block_ids, - num_histograms, histograms); - } - BROTLI_FREE(m, insert_cost); - BROTLI_FREE(m, cost); - BROTLI_FREE(m, switch_signal); - BROTLI_FREE(m, new_id); - BROTLI_FREE(m, histograms); - FN(ClusterBlocks)(m, data, length, num_blocks, block_ids, split); - if (BROTLI_IS_OOM(m)) return; - BROTLI_FREE(m, block_ids); - } -} - -#undef HistogramType diff --git a/src/deps/brotli/enc/brotli_bit_stream.c b/src/deps/brotli/enc/brotli_bit_stream.c deleted file mode 100644 index 5fa0c69aa843d..0000000000000 --- a/src/deps/brotli/enc/brotli_bit_stream.c +++ /dev/null @@ -1,1336 +0,0 @@ -/* Copyright 2014 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Brotli bit stream functions to support the low level format. There are no - compression algorithms here, just the right ordering of bits to match the - specs. */ - -#include "brotli_bit_stream.h" - -#include /* memcpy, memset */ - -#include - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/platform.h" -#include "entropy_encode.h" -#include "entropy_encode_static.h" -#include "fast_log.h" -#include "histogram.h" -#include "memory.h" -#include "write_bits.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define MAX_HUFFMAN_TREE_SIZE (2 * BROTLI_NUM_COMMAND_SYMBOLS + 1) -/* The maximum size of Huffman dictionary for distances assuming that - NPOSTFIX = 0 and NDIRECT = 0. */ -#define MAX_SIMPLE_DISTANCE_ALPHABET_SIZE \ - BROTLI_DISTANCE_ALPHABET_SIZE(0, 0, BROTLI_LARGE_MAX_DISTANCE_BITS) -/* MAX_SIMPLE_DISTANCE_ALPHABET_SIZE == 140 */ - -static BROTLI_INLINE uint32_t BlockLengthPrefixCode(uint32_t len) { - uint32_t code = (len >= 177) ? (len >= 753 ? 20 : 14) : (len >= 41 ? 7 : 0); - while (code < (BROTLI_NUM_BLOCK_LEN_SYMBOLS - 1) && - len >= _kBrotliPrefixCodeRanges[code + 1].offset) ++code; - return code; -} - -static BROTLI_INLINE void GetBlockLengthPrefixCode(uint32_t len, size_t* code, - uint32_t* n_extra, uint32_t* extra) { - *code = BlockLengthPrefixCode(len); - *n_extra = _kBrotliPrefixCodeRanges[*code].nbits; - *extra = len - _kBrotliPrefixCodeRanges[*code].offset; -} - -typedef struct BlockTypeCodeCalculator { - size_t last_type; - size_t second_last_type; -} BlockTypeCodeCalculator; - -static void InitBlockTypeCodeCalculator(BlockTypeCodeCalculator* self) { - self->last_type = 1; - self->second_last_type = 0; -} - -static BROTLI_INLINE size_t NextBlockTypeCode( - BlockTypeCodeCalculator* calculator, uint8_t type) { - size_t type_code = (type == calculator->last_type + 1) ? 1u : - (type == calculator->second_last_type) ? 0u : type + 2u; - calculator->second_last_type = calculator->last_type; - calculator->last_type = type; - return type_code; -} - -/* |nibblesbits| represents the 2 bits to encode MNIBBLES (0-3) - REQUIRES: length > 0 - REQUIRES: length <= (1 << 24) */ -static void BrotliEncodeMlen(size_t length, uint64_t* bits, - size_t* numbits, uint64_t* nibblesbits) { - size_t lg = (length == 1) ? 1 : Log2FloorNonZero((uint32_t)(length - 1)) + 1; - size_t mnibbles = (lg < 16 ? 16 : (lg + 3)) / 4; - BROTLI_DCHECK(length > 0); - BROTLI_DCHECK(length <= (1 << 24)); - BROTLI_DCHECK(lg <= 24); - *nibblesbits = mnibbles - 4; - *numbits = mnibbles * 4; - *bits = length - 1; -} - -static BROTLI_INLINE void StoreCommandExtra( - const Command* cmd, size_t* storage_ix, uint8_t* storage) { - uint32_t copylen_code = CommandCopyLenCode(cmd); - uint16_t inscode = GetInsertLengthCode(cmd->insert_len_); - uint16_t copycode = GetCopyLengthCode(copylen_code); - uint32_t insnumextra = GetInsertExtra(inscode); - uint64_t insextraval = cmd->insert_len_ - GetInsertBase(inscode); - uint64_t copyextraval = copylen_code - GetCopyBase(copycode); - uint64_t bits = (copyextraval << insnumextra) | insextraval; - BrotliWriteBits( - insnumextra + GetCopyExtra(copycode), bits, storage_ix, storage); -} - -/* Data structure that stores almost everything that is needed to encode each - block switch command. */ -typedef struct BlockSplitCode { - BlockTypeCodeCalculator type_code_calculator; - uint8_t type_depths[BROTLI_MAX_BLOCK_TYPE_SYMBOLS]; - uint16_t type_bits[BROTLI_MAX_BLOCK_TYPE_SYMBOLS]; - uint8_t length_depths[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; - uint16_t length_bits[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; -} BlockSplitCode; - -/* Stores a number between 0 and 255. */ -static void StoreVarLenUint8(size_t n, size_t* storage_ix, uint8_t* storage) { - if (n == 0) { - BrotliWriteBits(1, 0, storage_ix, storage); - } else { - size_t nbits = Log2FloorNonZero(n); - BrotliWriteBits(1, 1, storage_ix, storage); - BrotliWriteBits(3, nbits, storage_ix, storage); - BrotliWriteBits(nbits, n - ((size_t)1 << nbits), storage_ix, storage); - } -} - -/* Stores the compressed meta-block header. - REQUIRES: length > 0 - REQUIRES: length <= (1 << 24) */ -static void StoreCompressedMetaBlockHeader(BROTLI_BOOL is_final_block, - size_t length, - size_t* storage_ix, - uint8_t* storage) { - uint64_t lenbits; - size_t nlenbits; - uint64_t nibblesbits; - - /* Write ISLAST bit. */ - BrotliWriteBits(1, (uint64_t)is_final_block, storage_ix, storage); - /* Write ISEMPTY bit. */ - if (is_final_block) { - BrotliWriteBits(1, 0, storage_ix, storage); - } - - BrotliEncodeMlen(length, &lenbits, &nlenbits, &nibblesbits); - BrotliWriteBits(2, nibblesbits, storage_ix, storage); - BrotliWriteBits(nlenbits, lenbits, storage_ix, storage); - - if (!is_final_block) { - /* Write ISUNCOMPRESSED bit. */ - BrotliWriteBits(1, 0, storage_ix, storage); - } -} - -/* Stores the uncompressed meta-block header. - REQUIRES: length > 0 - REQUIRES: length <= (1 << 24) */ -static void BrotliStoreUncompressedMetaBlockHeader(size_t length, - size_t* storage_ix, - uint8_t* storage) { - uint64_t lenbits; - size_t nlenbits; - uint64_t nibblesbits; - - /* Write ISLAST bit. - Uncompressed block cannot be the last one, so set to 0. */ - BrotliWriteBits(1, 0, storage_ix, storage); - BrotliEncodeMlen(length, &lenbits, &nlenbits, &nibblesbits); - BrotliWriteBits(2, nibblesbits, storage_ix, storage); - BrotliWriteBits(nlenbits, lenbits, storage_ix, storage); - /* Write ISUNCOMPRESSED bit. */ - BrotliWriteBits(1, 1, storage_ix, storage); -} - -static void BrotliStoreHuffmanTreeOfHuffmanTreeToBitMask( - const int num_codes, const uint8_t* code_length_bitdepth, - size_t* storage_ix, uint8_t* storage) { - static const uint8_t kStorageOrder[BROTLI_CODE_LENGTH_CODES] = { - 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15 - }; - /* The bit lengths of the Huffman code over the code length alphabet - are compressed with the following static Huffman code: - Symbol Code - ------ ---- - 0 00 - 1 1110 - 2 110 - 3 01 - 4 10 - 5 1111 */ - static const uint8_t kHuffmanBitLengthHuffmanCodeSymbols[6] = { - 0, 7, 3, 2, 1, 15 - }; - static const uint8_t kHuffmanBitLengthHuffmanCodeBitLengths[6] = { - 2, 4, 3, 2, 2, 4 - }; - - size_t skip_some = 0; /* skips none. */ - - /* Throw away trailing zeros: */ - size_t codes_to_store = BROTLI_CODE_LENGTH_CODES; - if (num_codes > 1) { - for (; codes_to_store > 0; --codes_to_store) { - if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { - break; - } - } - } - if (code_length_bitdepth[kStorageOrder[0]] == 0 && - code_length_bitdepth[kStorageOrder[1]] == 0) { - skip_some = 2; /* skips two. */ - if (code_length_bitdepth[kStorageOrder[2]] == 0) { - skip_some = 3; /* skips three. */ - } - } - BrotliWriteBits(2, skip_some, storage_ix, storage); - { - size_t i; - for (i = skip_some; i < codes_to_store; ++i) { - size_t l = code_length_bitdepth[kStorageOrder[i]]; - BrotliWriteBits(kHuffmanBitLengthHuffmanCodeBitLengths[l], - kHuffmanBitLengthHuffmanCodeSymbols[l], storage_ix, storage); - } - } -} - -static void BrotliStoreHuffmanTreeToBitMask( - const size_t huffman_tree_size, const uint8_t* huffman_tree, - const uint8_t* huffman_tree_extra_bits, const uint8_t* code_length_bitdepth, - const uint16_t* code_length_bitdepth_symbols, - size_t* BROTLI_RESTRICT storage_ix, uint8_t* BROTLI_RESTRICT storage) { - size_t i; - for (i = 0; i < huffman_tree_size; ++i) { - size_t ix = huffman_tree[i]; - BrotliWriteBits(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix], - storage_ix, storage); - /* Extra bits */ - switch (ix) { - case BROTLI_REPEAT_PREVIOUS_CODE_LENGTH: - BrotliWriteBits(2, huffman_tree_extra_bits[i], storage_ix, storage); - break; - case BROTLI_REPEAT_ZERO_CODE_LENGTH: - BrotliWriteBits(3, huffman_tree_extra_bits[i], storage_ix, storage); - break; - } - } -} - -static void StoreSimpleHuffmanTree(const uint8_t* depths, - size_t symbols[4], - size_t num_symbols, - size_t max_bits, - size_t* storage_ix, uint8_t* storage) { - /* value of 1 indicates a simple Huffman code */ - BrotliWriteBits(2, 1, storage_ix, storage); - BrotliWriteBits(2, num_symbols - 1, storage_ix, storage); /* NSYM - 1 */ - - { - /* Sort */ - size_t i; - for (i = 0; i < num_symbols; i++) { - size_t j; - for (j = i + 1; j < num_symbols; j++) { - if (depths[symbols[j]] < depths[symbols[i]]) { - BROTLI_SWAP(size_t, symbols, j, i); - } - } - } - } - - if (num_symbols == 2) { - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); - } else if (num_symbols == 3) { - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); - } else { - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[3], storage_ix, storage); - /* tree-select */ - BrotliWriteBits(1, depths[symbols[0]] == 1 ? 1 : 0, storage_ix, storage); - } -} - -/* num = alphabet size - depths = symbol depths */ -void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num, - HuffmanTree* tree, - size_t* storage_ix, uint8_t* storage) { - /* Write the Huffman tree into the brotli-representation. - The command alphabet is the largest, so this allocation will fit all - alphabets. */ - /* TODO(eustas): fix me */ - uint8_t huffman_tree[BROTLI_NUM_COMMAND_SYMBOLS]; - uint8_t huffman_tree_extra_bits[BROTLI_NUM_COMMAND_SYMBOLS]; - size_t huffman_tree_size = 0; - uint8_t code_length_bitdepth[BROTLI_CODE_LENGTH_CODES] = { 0 }; - uint16_t code_length_bitdepth_symbols[BROTLI_CODE_LENGTH_CODES]; - uint32_t huffman_tree_histogram[BROTLI_CODE_LENGTH_CODES] = { 0 }; - size_t i; - int num_codes = 0; - size_t code = 0; - - BROTLI_DCHECK(num <= BROTLI_NUM_COMMAND_SYMBOLS); - - BrotliWriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree, - huffman_tree_extra_bits); - - /* Calculate the statistics of the Huffman tree in brotli-representation. */ - for (i = 0; i < huffman_tree_size; ++i) { - ++huffman_tree_histogram[huffman_tree[i]]; - } - - for (i = 0; i < BROTLI_CODE_LENGTH_CODES; ++i) { - if (huffman_tree_histogram[i]) { - if (num_codes == 0) { - code = i; - num_codes = 1; - } else if (num_codes == 1) { - num_codes = 2; - break; - } - } - } - - /* Calculate another Huffman tree to use for compressing both the - earlier Huffman tree with. */ - BrotliCreateHuffmanTree(huffman_tree_histogram, BROTLI_CODE_LENGTH_CODES, - 5, tree, code_length_bitdepth); - BrotliConvertBitDepthsToSymbols(code_length_bitdepth, - BROTLI_CODE_LENGTH_CODES, - code_length_bitdepth_symbols); - - /* Now, we have all the data, let's start storing it */ - BrotliStoreHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth, - storage_ix, storage); - - if (num_codes == 1) { - code_length_bitdepth[code] = 0; - } - - /* Store the real Huffman tree now. */ - BrotliStoreHuffmanTreeToBitMask(huffman_tree_size, - huffman_tree, - huffman_tree_extra_bits, - code_length_bitdepth, - code_length_bitdepth_symbols, - storage_ix, storage); -} - -/* Builds a Huffman tree from histogram[0:length] into depth[0:length] and - bits[0:length] and stores the encoded tree to the bit stream. */ -static void BuildAndStoreHuffmanTree(const uint32_t* histogram, - const size_t histogram_length, - const size_t alphabet_size, - HuffmanTree* tree, - uint8_t* depth, - uint16_t* bits, - size_t* storage_ix, - uint8_t* storage) { - size_t count = 0; - size_t s4[4] = { 0 }; - size_t i; - size_t max_bits = 0; - for (i = 0; i < histogram_length; i++) { - if (histogram[i]) { - if (count < 4) { - s4[count] = i; - } else if (count > 4) { - break; - } - count++; - } - } - - { - size_t max_bits_counter = alphabet_size - 1; - while (max_bits_counter) { - max_bits_counter >>= 1; - ++max_bits; - } - } - - if (count <= 1) { - BrotliWriteBits(4, 1, storage_ix, storage); - BrotliWriteBits(max_bits, s4[0], storage_ix, storage); - depth[s4[0]] = 0; - bits[s4[0]] = 0; - return; - } - - memset(depth, 0, histogram_length * sizeof(depth[0])); - BrotliCreateHuffmanTree(histogram, histogram_length, 15, tree, depth); - BrotliConvertBitDepthsToSymbols(depth, histogram_length, bits); - - if (count <= 4) { - StoreSimpleHuffmanTree(depth, s4, count, max_bits, storage_ix, storage); - } else { - BrotliStoreHuffmanTree(depth, histogram_length, tree, storage_ix, storage); - } -} - -static BROTLI_INLINE BROTLI_BOOL SortHuffmanTree( - const HuffmanTree* v0, const HuffmanTree* v1) { - return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_); -} - -void BrotliBuildAndStoreHuffmanTreeFast(HuffmanTree* tree, - const uint32_t* histogram, - const size_t histogram_total, - const size_t max_bits, - uint8_t* depth, uint16_t* bits, - size_t* storage_ix, - uint8_t* storage) { - size_t count = 0; - size_t symbols[4] = { 0 }; - size_t length = 0; - size_t total = histogram_total; - while (total != 0) { - if (histogram[length]) { - if (count < 4) { - symbols[count] = length; - } - ++count; - total -= histogram[length]; - } - ++length; - } - - if (count <= 1) { - BrotliWriteBits(4, 1, storage_ix, storage); - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - depth[symbols[0]] = 0; - bits[symbols[0]] = 0; - return; - } - - memset(depth, 0, length * sizeof(depth[0])); - { - uint32_t count_limit; - for (count_limit = 1; ; count_limit *= 2) { - HuffmanTree* node = tree; - size_t l; - for (l = length; l != 0;) { - --l; - if (histogram[l]) { - if (BROTLI_PREDICT_TRUE(histogram[l] >= count_limit)) { - InitHuffmanTree(node, histogram[l], -1, (int16_t)l); - } else { - InitHuffmanTree(node, count_limit, -1, (int16_t)l); - } - ++node; - } - } - { - const int n = (int)(node - tree); - HuffmanTree sentinel; - int i = 0; /* Points to the next leaf node. */ - int j = n + 1; /* Points to the next non-leaf node. */ - int k; - - SortHuffmanTreeItems(tree, (size_t)n, SortHuffmanTree); - /* The nodes are: - [0, n): the sorted leaf nodes that we start with. - [n]: we add a sentinel here. - [n + 1, 2n): new parent nodes are added here, starting from - (n+1). These are naturally in ascending order. - [2n]: we add a sentinel at the end as well. - There will be (2n+1) elements at the end. */ - InitHuffmanTree(&sentinel, BROTLI_UINT32_MAX, -1, -1); - *node++ = sentinel; - *node++ = sentinel; - - for (k = n - 1; k > 0; --k) { - int left, right; - if (tree[i].total_count_ <= tree[j].total_count_) { - left = i; - ++i; - } else { - left = j; - ++j; - } - if (tree[i].total_count_ <= tree[j].total_count_) { - right = i; - ++i; - } else { - right = j; - ++j; - } - /* The sentinel node becomes the parent node. */ - node[-1].total_count_ = - tree[left].total_count_ + tree[right].total_count_; - node[-1].index_left_ = (int16_t)left; - node[-1].index_right_or_value_ = (int16_t)right; - /* Add back the last sentinel node. */ - *node++ = sentinel; - } - if (BrotliSetDepth(2 * n - 1, tree, depth, 14)) { - /* We need to pack the Huffman tree in 14 bits. If this was not - successful, add fake entities to the lowest values and retry. */ - break; - } - } - } - } - BrotliConvertBitDepthsToSymbols(depth, length, bits); - if (count <= 4) { - size_t i; - /* value of 1 indicates a simple Huffman code */ - BrotliWriteBits(2, 1, storage_ix, storage); - BrotliWriteBits(2, count - 1, storage_ix, storage); /* NSYM - 1 */ - - /* Sort */ - for (i = 0; i < count; i++) { - size_t j; - for (j = i + 1; j < count; j++) { - if (depth[symbols[j]] < depth[symbols[i]]) { - BROTLI_SWAP(size_t, symbols, j, i); - } - } - } - - if (count == 2) { - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); - } else if (count == 3) { - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); - } else { - BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); - BrotliWriteBits(max_bits, symbols[3], storage_ix, storage); - /* tree-select */ - BrotliWriteBits(1, depth[symbols[0]] == 1 ? 1 : 0, storage_ix, storage); - } - } else { - uint8_t previous_value = 8; - size_t i; - /* Complex Huffman Tree */ - StoreStaticCodeLengthCode(storage_ix, storage); - - /* Actual RLE coding. */ - for (i = 0; i < length;) { - const uint8_t value = depth[i]; - size_t reps = 1; - size_t k; - for (k = i + 1; k < length && depth[k] == value; ++k) { - ++reps; - } - i += reps; - if (value == 0) { - BrotliWriteBits(kZeroRepsDepth[reps], kZeroRepsBits[reps], - storage_ix, storage); - } else { - if (previous_value != value) { - BrotliWriteBits(kCodeLengthDepth[value], kCodeLengthBits[value], - storage_ix, storage); - --reps; - } - if (reps < 3) { - while (reps != 0) { - reps--; - BrotliWriteBits(kCodeLengthDepth[value], kCodeLengthBits[value], - storage_ix, storage); - } - } else { - reps -= 3; - BrotliWriteBits(kNonZeroRepsDepth[reps], kNonZeroRepsBits[reps], - storage_ix, storage); - } - previous_value = value; - } - } - } -} - -static size_t IndexOf(const uint8_t* v, size_t v_size, uint8_t value) { - size_t i = 0; - for (; i < v_size; ++i) { - if (v[i] == value) return i; - } - return i; -} - -static void MoveToFront(uint8_t* v, size_t index) { - uint8_t value = v[index]; - size_t i; - for (i = index; i != 0; --i) { - v[i] = v[i - 1]; - } - v[0] = value; -} - -static void MoveToFrontTransform(const uint32_t* BROTLI_RESTRICT v_in, - const size_t v_size, - uint32_t* v_out) { - size_t i; - uint8_t mtf[256]; - uint32_t max_value; - if (v_size == 0) { - return; - } - max_value = v_in[0]; - for (i = 1; i < v_size; ++i) { - if (v_in[i] > max_value) max_value = v_in[i]; - } - BROTLI_DCHECK(max_value < 256u); - for (i = 0; i <= max_value; ++i) { - mtf[i] = (uint8_t)i; - } - { - size_t mtf_size = max_value + 1; - for (i = 0; i < v_size; ++i) { - size_t index = IndexOf(mtf, mtf_size, (uint8_t)v_in[i]); - BROTLI_DCHECK(index < mtf_size); - v_out[i] = (uint32_t)index; - MoveToFront(mtf, index); - } - } -} - -/* Finds runs of zeros in v[0..in_size) and replaces them with a prefix code of - the run length plus extra bits (lower 9 bits is the prefix code and the rest - are the extra bits). Non-zero values in v[] are shifted by - *max_length_prefix. Will not create prefix codes bigger than the initial - value of *max_run_length_prefix. The prefix code of run length L is simply - Log2Floor(L) and the number of extra bits is the same as the prefix code. */ -static void RunLengthCodeZeros(const size_t in_size, - uint32_t* BROTLI_RESTRICT v, size_t* BROTLI_RESTRICT out_size, - uint32_t* BROTLI_RESTRICT max_run_length_prefix) { - uint32_t max_reps = 0; - size_t i; - uint32_t max_prefix; - for (i = 0; i < in_size;) { - uint32_t reps = 0; - for (; i < in_size && v[i] != 0; ++i) ; - for (; i < in_size && v[i] == 0; ++i) { - ++reps; - } - max_reps = BROTLI_MAX(uint32_t, reps, max_reps); - } - max_prefix = max_reps > 0 ? Log2FloorNonZero(max_reps) : 0; - max_prefix = BROTLI_MIN(uint32_t, max_prefix, *max_run_length_prefix); - *max_run_length_prefix = max_prefix; - *out_size = 0; - for (i = 0; i < in_size;) { - BROTLI_DCHECK(*out_size <= i); - if (v[i] != 0) { - v[*out_size] = v[i] + *max_run_length_prefix; - ++i; - ++(*out_size); - } else { - uint32_t reps = 1; - size_t k; - for (k = i + 1; k < in_size && v[k] == 0; ++k) { - ++reps; - } - i += reps; - while (reps != 0) { - if (reps < (2u << max_prefix)) { - uint32_t run_length_prefix = Log2FloorNonZero(reps); - const uint32_t extra_bits = reps - (1u << run_length_prefix); - v[*out_size] = run_length_prefix + (extra_bits << 9); - ++(*out_size); - break; - } else { - const uint32_t extra_bits = (1u << max_prefix) - 1u; - v[*out_size] = max_prefix + (extra_bits << 9); - reps -= (2u << max_prefix) - 1u; - ++(*out_size); - } - } - } - } -} - -#define SYMBOL_BITS 9 - -typedef struct EncodeContextMapArena { - uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; - uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; - uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; -} EncodeContextMapArena; - -static void EncodeContextMap(MemoryManager* m, - EncodeContextMapArena* arena, - const uint32_t* context_map, - size_t context_map_size, - size_t num_clusters, - HuffmanTree* tree, - size_t* storage_ix, uint8_t* storage) { - size_t i; - uint32_t* rle_symbols; - uint32_t max_run_length_prefix = 6; - size_t num_rle_symbols = 0; - uint32_t* BROTLI_RESTRICT const histogram = arena->histogram; - static const uint32_t kSymbolMask = (1u << SYMBOL_BITS) - 1u; - uint8_t* BROTLI_RESTRICT const depths = arena->depths; - uint16_t* BROTLI_RESTRICT const bits = arena->bits; - - StoreVarLenUint8(num_clusters - 1, storage_ix, storage); - - if (num_clusters == 1) { - return; - } - - rle_symbols = BROTLI_ALLOC(m, uint32_t, context_map_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(rle_symbols)) return; - MoveToFrontTransform(context_map, context_map_size, rle_symbols); - RunLengthCodeZeros(context_map_size, rle_symbols, - &num_rle_symbols, &max_run_length_prefix); - memset(histogram, 0, sizeof(arena->histogram)); - for (i = 0; i < num_rle_symbols; ++i) { - ++histogram[rle_symbols[i] & kSymbolMask]; - } - { - BROTLI_BOOL use_rle = TO_BROTLI_BOOL(max_run_length_prefix > 0); - BrotliWriteBits(1, (uint64_t)use_rle, storage_ix, storage); - if (use_rle) { - BrotliWriteBits(4, max_run_length_prefix - 1, storage_ix, storage); - } - } - BuildAndStoreHuffmanTree(histogram, num_clusters + max_run_length_prefix, - num_clusters + max_run_length_prefix, - tree, depths, bits, storage_ix, storage); - for (i = 0; i < num_rle_symbols; ++i) { - const uint32_t rle_symbol = rle_symbols[i] & kSymbolMask; - const uint32_t extra_bits_val = rle_symbols[i] >> SYMBOL_BITS; - BrotliWriteBits(depths[rle_symbol], bits[rle_symbol], storage_ix, storage); - if (rle_symbol > 0 && rle_symbol <= max_run_length_prefix) { - BrotliWriteBits(rle_symbol, extra_bits_val, storage_ix, storage); - } - } - BrotliWriteBits(1, 1, storage_ix, storage); /* use move-to-front */ - BROTLI_FREE(m, rle_symbols); -} - -/* Stores the block switch command with index block_ix to the bit stream. */ -static BROTLI_INLINE void StoreBlockSwitch(BlockSplitCode* code, - const uint32_t block_len, - const uint8_t block_type, - BROTLI_BOOL is_first_block, - size_t* storage_ix, - uint8_t* storage) { - size_t typecode = NextBlockTypeCode(&code->type_code_calculator, block_type); - size_t lencode; - uint32_t len_nextra; - uint32_t len_extra; - if (!is_first_block) { - BrotliWriteBits(code->type_depths[typecode], code->type_bits[typecode], - storage_ix, storage); - } - GetBlockLengthPrefixCode(block_len, &lencode, &len_nextra, &len_extra); - - BrotliWriteBits(code->length_depths[lencode], code->length_bits[lencode], - storage_ix, storage); - BrotliWriteBits(len_nextra, len_extra, storage_ix, storage); -} - -/* Builds a BlockSplitCode data structure from the block split given by the - vector of block types and block lengths and stores it to the bit stream. */ -static void BuildAndStoreBlockSplitCode(const uint8_t* types, - const uint32_t* lengths, - const size_t num_blocks, - const size_t num_types, - HuffmanTree* tree, - BlockSplitCode* code, - size_t* storage_ix, - uint8_t* storage) { - uint32_t type_histo[BROTLI_MAX_BLOCK_TYPE_SYMBOLS]; - uint32_t length_histo[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; - size_t i; - BlockTypeCodeCalculator type_code_calculator; - memset(type_histo, 0, (num_types + 2) * sizeof(type_histo[0])); - memset(length_histo, 0, sizeof(length_histo)); - InitBlockTypeCodeCalculator(&type_code_calculator); - for (i = 0; i < num_blocks; ++i) { - size_t type_code = NextBlockTypeCode(&type_code_calculator, types[i]); - if (i != 0) ++type_histo[type_code]; - ++length_histo[BlockLengthPrefixCode(lengths[i])]; - } - StoreVarLenUint8(num_types - 1, storage_ix, storage); - if (num_types > 1) { /* TODO(eustas): else? could StoreBlockSwitch occur? */ - BuildAndStoreHuffmanTree(&type_histo[0], num_types + 2, num_types + 2, tree, - &code->type_depths[0], &code->type_bits[0], - storage_ix, storage); - BuildAndStoreHuffmanTree(&length_histo[0], BROTLI_NUM_BLOCK_LEN_SYMBOLS, - BROTLI_NUM_BLOCK_LEN_SYMBOLS, - tree, &code->length_depths[0], - &code->length_bits[0], storage_ix, storage); - StoreBlockSwitch(code, lengths[0], types[0], 1, storage_ix, storage); - } -} - -/* Stores a context map where the histogram type is always the block type. */ -static void StoreTrivialContextMap(EncodeContextMapArena* arena, - size_t num_types, - size_t context_bits, - HuffmanTree* tree, - size_t* storage_ix, - uint8_t* storage) { - StoreVarLenUint8(num_types - 1, storage_ix, storage); - if (num_types > 1) { - size_t repeat_code = context_bits - 1u; - size_t repeat_bits = (1u << repeat_code) - 1u; - size_t alphabet_size = num_types + repeat_code; - uint32_t* BROTLI_RESTRICT const histogram = arena->histogram; - uint8_t* BROTLI_RESTRICT const depths = arena->depths; - uint16_t* BROTLI_RESTRICT const bits = arena->bits; - size_t i; - memset(histogram, 0, alphabet_size * sizeof(histogram[0])); - /* Write RLEMAX. */ - BrotliWriteBits(1, 1, storage_ix, storage); - BrotliWriteBits(4, repeat_code - 1, storage_ix, storage); - histogram[repeat_code] = (uint32_t)num_types; - histogram[0] = 1; - for (i = context_bits; i < alphabet_size; ++i) { - histogram[i] = 1; - } - BuildAndStoreHuffmanTree(histogram, alphabet_size, alphabet_size, - tree, depths, bits, storage_ix, storage); - for (i = 0; i < num_types; ++i) { - size_t code = (i == 0 ? 0 : i + context_bits - 1); - BrotliWriteBits(depths[code], bits[code], storage_ix, storage); - BrotliWriteBits( - depths[repeat_code], bits[repeat_code], storage_ix, storage); - BrotliWriteBits(repeat_code, repeat_bits, storage_ix, storage); - } - /* Write IMTF (inverse-move-to-front) bit. */ - BrotliWriteBits(1, 1, storage_ix, storage); - } -} - -/* Manages the encoding of one block category (literal, command or distance). */ -typedef struct BlockEncoder { - size_t histogram_length_; - size_t num_block_types_; - const uint8_t* block_types_; /* Not owned. */ - const uint32_t* block_lengths_; /* Not owned. */ - size_t num_blocks_; - BlockSplitCode block_split_code_; - size_t block_ix_; - size_t block_len_; - size_t entropy_ix_; - uint8_t* depths_; - uint16_t* bits_; -} BlockEncoder; - -static void InitBlockEncoder(BlockEncoder* self, size_t histogram_length, - size_t num_block_types, const uint8_t* block_types, - const uint32_t* block_lengths, const size_t num_blocks) { - self->histogram_length_ = histogram_length; - self->num_block_types_ = num_block_types; - self->block_types_ = block_types; - self->block_lengths_ = block_lengths; - self->num_blocks_ = num_blocks; - InitBlockTypeCodeCalculator(&self->block_split_code_.type_code_calculator); - self->block_ix_ = 0; - self->block_len_ = num_blocks == 0 ? 0 : block_lengths[0]; - self->entropy_ix_ = 0; - self->depths_ = 0; - self->bits_ = 0; -} - -static void CleanupBlockEncoder(MemoryManager* m, BlockEncoder* self) { - BROTLI_FREE(m, self->depths_); - BROTLI_FREE(m, self->bits_); -} - -/* Creates entropy codes of block lengths and block types and stores them - to the bit stream. */ -static void BuildAndStoreBlockSwitchEntropyCodes(BlockEncoder* self, - HuffmanTree* tree, size_t* storage_ix, uint8_t* storage) { - BuildAndStoreBlockSplitCode(self->block_types_, self->block_lengths_, - self->num_blocks_, self->num_block_types_, tree, &self->block_split_code_, - storage_ix, storage); -} - -/* Stores the next symbol with the entropy code of the current block type. - Updates the block type and block length at block boundaries. */ -static void StoreSymbol(BlockEncoder* self, size_t symbol, size_t* storage_ix, - uint8_t* storage) { - if (self->block_len_ == 0) { - size_t block_ix = ++self->block_ix_; - uint32_t block_len = self->block_lengths_[block_ix]; - uint8_t block_type = self->block_types_[block_ix]; - self->block_len_ = block_len; - self->entropy_ix_ = block_type * self->histogram_length_; - StoreBlockSwitch(&self->block_split_code_, block_len, block_type, 0, - storage_ix, storage); - } - --self->block_len_; - { - size_t ix = self->entropy_ix_ + symbol; - BrotliWriteBits(self->depths_[ix], self->bits_[ix], storage_ix, storage); - } -} - -/* Stores the next symbol with the entropy code of the current block type and - context value. - Updates the block type and block length at block boundaries. */ -static void StoreSymbolWithContext(BlockEncoder* self, size_t symbol, - size_t context, const uint32_t* context_map, size_t* storage_ix, - uint8_t* storage, const size_t context_bits) { - if (self->block_len_ == 0) { - size_t block_ix = ++self->block_ix_; - uint32_t block_len = self->block_lengths_[block_ix]; - uint8_t block_type = self->block_types_[block_ix]; - self->block_len_ = block_len; - self->entropy_ix_ = (size_t)block_type << context_bits; - StoreBlockSwitch(&self->block_split_code_, block_len, block_type, 0, - storage_ix, storage); - } - --self->block_len_; - { - size_t histo_ix = context_map[self->entropy_ix_ + context]; - size_t ix = histo_ix * self->histogram_length_ + symbol; - BrotliWriteBits(self->depths_[ix], self->bits_[ix], storage_ix, storage); - } -} - -#define FN(X) X ## Literal -/* NOLINTNEXTLINE(build/include) */ -#include "block_encoder_inc.h" -#undef FN - -#define FN(X) X ## Command -/* NOLINTNEXTLINE(build/include) */ -#include "block_encoder_inc.h" -#undef FN - -#define FN(X) X ## Distance -/* NOLINTNEXTLINE(build/include) */ -#include "block_encoder_inc.h" -#undef FN - -static void JumpToByteBoundary(size_t* storage_ix, uint8_t* storage) { - *storage_ix = (*storage_ix + 7u) & ~7u; - storage[*storage_ix >> 3] = 0; -} - -typedef struct StoreMetablockArena { - BlockEncoder literal_enc; - BlockEncoder command_enc; - BlockEncoder distance_enc; - EncodeContextMapArena context_map_arena; -} StoreMetablockArena; - -void BrotliStoreMetaBlock(MemoryManager* m, - const uint8_t* input, size_t start_pos, size_t length, size_t mask, - uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last, - const BrotliEncoderParams* params, ContextType literal_context_mode, - const Command* commands, size_t n_commands, const MetaBlockSplit* mb, - size_t* storage_ix, uint8_t* storage) { - - size_t pos = start_pos; - size_t i; - uint32_t num_distance_symbols = params->dist.alphabet_size_max; - uint32_t num_effective_distance_symbols = params->dist.alphabet_size_limit; - HuffmanTree* tree; - ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); - StoreMetablockArena* arena = NULL; - BlockEncoder* literal_enc = NULL; - BlockEncoder* command_enc = NULL; - BlockEncoder* distance_enc = NULL; - const BrotliDistanceParams* dist = ¶ms->dist; - BROTLI_DCHECK( - num_effective_distance_symbols <= BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS); - - StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); - - tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE); - arena = BROTLI_ALLOC(m, StoreMetablockArena, 1); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree) || BROTLI_IS_NULL(arena)) return; - literal_enc = &arena->literal_enc; - command_enc = &arena->command_enc; - distance_enc = &arena->distance_enc; - InitBlockEncoder(literal_enc, BROTLI_NUM_LITERAL_SYMBOLS, - mb->literal_split.num_types, mb->literal_split.types, - mb->literal_split.lengths, mb->literal_split.num_blocks); - InitBlockEncoder(command_enc, BROTLI_NUM_COMMAND_SYMBOLS, - mb->command_split.num_types, mb->command_split.types, - mb->command_split.lengths, mb->command_split.num_blocks); - InitBlockEncoder(distance_enc, num_effective_distance_symbols, - mb->distance_split.num_types, mb->distance_split.types, - mb->distance_split.lengths, mb->distance_split.num_blocks); - - BuildAndStoreBlockSwitchEntropyCodes(literal_enc, tree, storage_ix, storage); - BuildAndStoreBlockSwitchEntropyCodes(command_enc, tree, storage_ix, storage); - BuildAndStoreBlockSwitchEntropyCodes(distance_enc, tree, storage_ix, storage); - - BrotliWriteBits(2, dist->distance_postfix_bits, storage_ix, storage); - BrotliWriteBits( - 4, dist->num_direct_distance_codes >> dist->distance_postfix_bits, - storage_ix, storage); - for (i = 0; i < mb->literal_split.num_types; ++i) { - BrotliWriteBits(2, literal_context_mode, storage_ix, storage); - } - - if (mb->literal_context_map_size == 0) { - StoreTrivialContextMap( - &arena->context_map_arena, mb->literal_histograms_size, - BROTLI_LITERAL_CONTEXT_BITS, tree, storage_ix, storage); - } else { - EncodeContextMap(m, &arena->context_map_arena, - mb->literal_context_map, mb->literal_context_map_size, - mb->literal_histograms_size, tree, storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - } - - if (mb->distance_context_map_size == 0) { - StoreTrivialContextMap( - &arena->context_map_arena, mb->distance_histograms_size, - BROTLI_DISTANCE_CONTEXT_BITS, tree, storage_ix, storage); - } else { - EncodeContextMap(m, &arena->context_map_arena, - mb->distance_context_map, mb->distance_context_map_size, - mb->distance_histograms_size, tree, storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - } - - BuildAndStoreEntropyCodesLiteral(m, literal_enc, mb->literal_histograms, - mb->literal_histograms_size, BROTLI_NUM_LITERAL_SYMBOLS, tree, - storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - BuildAndStoreEntropyCodesCommand(m, command_enc, mb->command_histograms, - mb->command_histograms_size, BROTLI_NUM_COMMAND_SYMBOLS, tree, - storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - BuildAndStoreEntropyCodesDistance(m, distance_enc, mb->distance_histograms, - mb->distance_histograms_size, num_distance_symbols, tree, - storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - BROTLI_FREE(m, tree); - - for (i = 0; i < n_commands; ++i) { - const Command cmd = commands[i]; - size_t cmd_code = cmd.cmd_prefix_; - StoreSymbol(command_enc, cmd_code, storage_ix, storage); - StoreCommandExtra(&cmd, storage_ix, storage); - if (mb->literal_context_map_size == 0) { - size_t j; - for (j = cmd.insert_len_; j != 0; --j) { - StoreSymbol(literal_enc, input[pos & mask], storage_ix, storage); - ++pos; - } - } else { - size_t j; - for (j = cmd.insert_len_; j != 0; --j) { - size_t context = - BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut); - uint8_t literal = input[pos & mask]; - StoreSymbolWithContext(literal_enc, literal, context, - mb->literal_context_map, storage_ix, storage, - BROTLI_LITERAL_CONTEXT_BITS); - prev_byte2 = prev_byte; - prev_byte = literal; - ++pos; - } - } - pos += CommandCopyLen(&cmd); - if (CommandCopyLen(&cmd)) { - prev_byte2 = input[(pos - 2) & mask]; - prev_byte = input[(pos - 1) & mask]; - if (cmd.cmd_prefix_ >= 128) { - size_t dist_code = cmd.dist_prefix_ & 0x3FF; - uint32_t distnumextra = cmd.dist_prefix_ >> 10; - uint64_t distextra = cmd.dist_extra_; - if (mb->distance_context_map_size == 0) { - StoreSymbol(distance_enc, dist_code, storage_ix, storage); - } else { - size_t context = CommandDistanceContext(&cmd); - StoreSymbolWithContext(distance_enc, dist_code, context, - mb->distance_context_map, storage_ix, storage, - BROTLI_DISTANCE_CONTEXT_BITS); - } - BrotliWriteBits(distnumextra, distextra, storage_ix, storage); - } - } - } - CleanupBlockEncoder(m, distance_enc); - CleanupBlockEncoder(m, command_enc); - CleanupBlockEncoder(m, literal_enc); - BROTLI_FREE(m, arena); - if (is_last) { - JumpToByteBoundary(storage_ix, storage); - } -} - -static void BuildHistograms(const uint8_t* input, - size_t start_pos, - size_t mask, - const Command* commands, - size_t n_commands, - HistogramLiteral* lit_histo, - HistogramCommand* cmd_histo, - HistogramDistance* dist_histo) { - size_t pos = start_pos; - size_t i; - for (i = 0; i < n_commands; ++i) { - const Command cmd = commands[i]; - size_t j; - HistogramAddCommand(cmd_histo, cmd.cmd_prefix_); - for (j = cmd.insert_len_; j != 0; --j) { - HistogramAddLiteral(lit_histo, input[pos & mask]); - ++pos; - } - pos += CommandCopyLen(&cmd); - if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) { - HistogramAddDistance(dist_histo, cmd.dist_prefix_ & 0x3FF); - } - } -} - -static void StoreDataWithHuffmanCodes(const uint8_t* input, - size_t start_pos, - size_t mask, - const Command* commands, - size_t n_commands, - const uint8_t* lit_depth, - const uint16_t* lit_bits, - const uint8_t* cmd_depth, - const uint16_t* cmd_bits, - const uint8_t* dist_depth, - const uint16_t* dist_bits, - size_t* storage_ix, - uint8_t* storage) { - size_t pos = start_pos; - size_t i; - for (i = 0; i < n_commands; ++i) { - const Command cmd = commands[i]; - const size_t cmd_code = cmd.cmd_prefix_; - size_t j; - BrotliWriteBits( - cmd_depth[cmd_code], cmd_bits[cmd_code], storage_ix, storage); - StoreCommandExtra(&cmd, storage_ix, storage); - for (j = cmd.insert_len_; j != 0; --j) { - const uint8_t literal = input[pos & mask]; - BrotliWriteBits( - lit_depth[literal], lit_bits[literal], storage_ix, storage); - ++pos; - } - pos += CommandCopyLen(&cmd); - if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) { - const size_t dist_code = cmd.dist_prefix_ & 0x3FF; - const uint32_t distnumextra = cmd.dist_prefix_ >> 10; - const uint32_t distextra = cmd.dist_extra_; - BrotliWriteBits(dist_depth[dist_code], dist_bits[dist_code], - storage_ix, storage); - BrotliWriteBits(distnumextra, distextra, storage_ix, storage); - } - } -} - -/* TODO(eustas): pull alloc/dealloc to caller? */ -typedef struct MetablockArena { - HistogramLiteral lit_histo; - HistogramCommand cmd_histo; - HistogramDistance dist_histo; - /* TODO(eustas): merge bits and depth? */ - uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS]; - uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS]; - uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS]; - uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS]; - uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE]; - uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE]; - HuffmanTree tree[MAX_HUFFMAN_TREE_SIZE]; -} MetablockArena; - -void BrotliStoreMetaBlockTrivial(MemoryManager* m, - const uint8_t* input, size_t start_pos, size_t length, size_t mask, - BROTLI_BOOL is_last, const BrotliEncoderParams* params, - const Command* commands, size_t n_commands, - size_t* storage_ix, uint8_t* storage) { - MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1); - uint32_t num_distance_symbols = params->dist.alphabet_size_max; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; - - StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); - - HistogramClearLiteral(&arena->lit_histo); - HistogramClearCommand(&arena->cmd_histo); - HistogramClearDistance(&arena->dist_histo); - - BuildHistograms(input, start_pos, mask, commands, n_commands, - &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo); - - BrotliWriteBits(13, 0, storage_ix, storage); - - BuildAndStoreHuffmanTree(arena->lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS, - BROTLI_NUM_LITERAL_SYMBOLS, arena->tree, - arena->lit_depth, arena->lit_bits, - storage_ix, storage); - BuildAndStoreHuffmanTree(arena->cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS, - BROTLI_NUM_COMMAND_SYMBOLS, arena->tree, - arena->cmd_depth, arena->cmd_bits, - storage_ix, storage); - BuildAndStoreHuffmanTree(arena->dist_histo.data_, - MAX_SIMPLE_DISTANCE_ALPHABET_SIZE, - num_distance_symbols, arena->tree, - arena->dist_depth, arena->dist_bits, - storage_ix, storage); - StoreDataWithHuffmanCodes(input, start_pos, mask, commands, - n_commands, arena->lit_depth, arena->lit_bits, - arena->cmd_depth, arena->cmd_bits, - arena->dist_depth, arena->dist_bits, - storage_ix, storage); - BROTLI_FREE(m, arena); - if (is_last) { - JumpToByteBoundary(storage_ix, storage); - } -} - -void BrotliStoreMetaBlockFast(MemoryManager* m, - const uint8_t* input, size_t start_pos, size_t length, size_t mask, - BROTLI_BOOL is_last, const BrotliEncoderParams* params, - const Command* commands, size_t n_commands, - size_t* storage_ix, uint8_t* storage) { - MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1); - uint32_t num_distance_symbols = params->dist.alphabet_size_max; - uint32_t distance_alphabet_bits = - Log2FloorNonZero(num_distance_symbols - 1) + 1; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; - - StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); - - BrotliWriteBits(13, 0, storage_ix, storage); - - if (n_commands <= 128) { - uint32_t histogram[BROTLI_NUM_LITERAL_SYMBOLS] = { 0 }; - size_t pos = start_pos; - size_t num_literals = 0; - size_t i; - for (i = 0; i < n_commands; ++i) { - const Command cmd = commands[i]; - size_t j; - for (j = cmd.insert_len_; j != 0; --j) { - ++histogram[input[pos & mask]]; - ++pos; - } - num_literals += cmd.insert_len_; - pos += CommandCopyLen(&cmd); - } - BrotliBuildAndStoreHuffmanTreeFast(arena->tree, histogram, num_literals, - /* max_bits = */ 8, - arena->lit_depth, arena->lit_bits, - storage_ix, storage); - StoreStaticCommandHuffmanTree(storage_ix, storage); - StoreStaticDistanceHuffmanTree(storage_ix, storage); - StoreDataWithHuffmanCodes(input, start_pos, mask, commands, - n_commands, arena->lit_depth, arena->lit_bits, - kStaticCommandCodeDepth, - kStaticCommandCodeBits, - kStaticDistanceCodeDepth, - kStaticDistanceCodeBits, - storage_ix, storage); - } else { - HistogramClearLiteral(&arena->lit_histo); - HistogramClearCommand(&arena->cmd_histo); - HistogramClearDistance(&arena->dist_histo); - BuildHistograms(input, start_pos, mask, commands, n_commands, - &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo); - BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->lit_histo.data_, - arena->lit_histo.total_count_, - /* max_bits = */ 8, - arena->lit_depth, arena->lit_bits, - storage_ix, storage); - BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->cmd_histo.data_, - arena->cmd_histo.total_count_, - /* max_bits = */ 10, - arena->cmd_depth, arena->cmd_bits, - storage_ix, storage); - BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->dist_histo.data_, - arena->dist_histo.total_count_, - /* max_bits = */ - distance_alphabet_bits, - arena->dist_depth, arena->dist_bits, - storage_ix, storage); - StoreDataWithHuffmanCodes(input, start_pos, mask, commands, - n_commands, arena->lit_depth, arena->lit_bits, - arena->cmd_depth, arena->cmd_bits, - arena->dist_depth, arena->dist_bits, - storage_ix, storage); - } - - BROTLI_FREE(m, arena); - - if (is_last) { - JumpToByteBoundary(storage_ix, storage); - } -} - -/* This is for storing uncompressed blocks (simple raw storage of - bytes-as-bytes). */ -void BrotliStoreUncompressedMetaBlock(BROTLI_BOOL is_final_block, - const uint8_t* BROTLI_RESTRICT input, - size_t position, size_t mask, - size_t len, - size_t* BROTLI_RESTRICT storage_ix, - uint8_t* BROTLI_RESTRICT storage) { - size_t masked_pos = position & mask; - BrotliStoreUncompressedMetaBlockHeader(len, storage_ix, storage); - JumpToByteBoundary(storage_ix, storage); - - if (masked_pos + len > mask + 1) { - size_t len1 = mask + 1 - masked_pos; - memcpy(&storage[*storage_ix >> 3], &input[masked_pos], len1); - *storage_ix += len1 << 3; - len -= len1; - masked_pos = 0; - } - memcpy(&storage[*storage_ix >> 3], &input[masked_pos], len); - *storage_ix += len << 3; - - /* We need to clear the next 4 bytes to continue to be - compatible with BrotliWriteBits. */ - BrotliWriteBitsPrepareStorage(*storage_ix, storage); - - /* Since the uncompressed block itself may not be the final block, add an - empty one after this. */ - if (is_final_block) { - BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ - BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ - JumpToByteBoundary(storage_ix, storage); - } -} - -#if defined(BROTLI_TEST) -void GetBlockLengthPrefixCodeForTest(uint32_t len, size_t* code, - uint32_t* n_extra, uint32_t* extra) { - GetBlockLengthPrefixCode(len, code, n_extra, extra); -} -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/brotli_bit_stream.h b/src/deps/brotli/enc/brotli_bit_stream.h deleted file mode 100644 index a289509af35a8..0000000000000 --- a/src/deps/brotli/enc/brotli_bit_stream.h +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2014 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions to convert brotli-related data structures into the - brotli bit stream. The functions here operate under - assumption that there is enough space in the storage, i.e., there are - no out-of-range checks anywhere. - - These functions do bit addressing into a byte array. The byte array - is called "storage" and the index to the bit is called storage_ix - in function arguments. */ - -#ifndef BROTLI_ENC_BROTLI_BIT_STREAM_H_ -#define BROTLI_ENC_BROTLI_BIT_STREAM_H_ - -#include - -#include "../common/context.h" -#include "../common/platform.h" -#include "command.h" -#include "entropy_encode.h" -#include "memory.h" -#include "metablock.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* All Store functions here will use a storage_ix, which is always the bit - position for the current storage. */ - -BROTLI_INTERNAL void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num, - HuffmanTree* tree, size_t* storage_ix, uint8_t* storage); - -BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast( - HuffmanTree* tree, const uint32_t* histogram, const size_t histogram_total, - const size_t max_bits, uint8_t* depth, uint16_t* bits, size_t* storage_ix, - uint8_t* storage); - -/* REQUIRES: length > 0 */ -/* REQUIRES: length <= (1 << 24) */ -BROTLI_INTERNAL void BrotliStoreMetaBlock(MemoryManager* m, - const uint8_t* input, size_t start_pos, size_t length, size_t mask, - uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last, - const BrotliEncoderParams* params, ContextType literal_context_mode, - const Command* commands, size_t n_commands, const MetaBlockSplit* mb, - size_t* storage_ix, uint8_t* storage); - -/* Stores the meta-block without doing any block splitting, just collects - one histogram per block category and uses that for entropy coding. - REQUIRES: length > 0 - REQUIRES: length <= (1 << 24) */ -BROTLI_INTERNAL void BrotliStoreMetaBlockTrivial(MemoryManager* m, - const uint8_t* input, size_t start_pos, size_t length, size_t mask, - BROTLI_BOOL is_last, const BrotliEncoderParams* params, - const Command* commands, size_t n_commands, - size_t* storage_ix, uint8_t* storage); - -/* Same as above, but uses static prefix codes for histograms with a only a few - symbols, and uses static code length prefix codes for all other histograms. - REQUIRES: length > 0 - REQUIRES: length <= (1 << 24) */ -BROTLI_INTERNAL void BrotliStoreMetaBlockFast(MemoryManager* m, - const uint8_t* input, size_t start_pos, size_t length, size_t mask, - BROTLI_BOOL is_last, const BrotliEncoderParams* params, - const Command* commands, size_t n_commands, - size_t* storage_ix, uint8_t* storage); - -/* This is for storing uncompressed blocks (simple raw storage of - bytes-as-bytes). - REQUIRES: length > 0 - REQUIRES: length <= (1 << 24) */ -BROTLI_INTERNAL void BrotliStoreUncompressedMetaBlock( - BROTLI_BOOL is_final_block, const uint8_t* BROTLI_RESTRICT input, - size_t position, size_t mask, size_t len, - size_t* BROTLI_RESTRICT storage_ix, uint8_t* BROTLI_RESTRICT storage); - -#if defined(BROTLI_TEST) -void GetBlockLengthPrefixCodeForTest(uint32_t, size_t*, uint32_t*, uint32_t*); -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_BROTLI_BIT_STREAM_H_ */ diff --git a/src/deps/brotli/enc/cluster.c b/src/deps/brotli/enc/cluster.c deleted file mode 100644 index b0faf8114c52f..0000000000000 --- a/src/deps/brotli/enc/cluster.c +++ /dev/null @@ -1,57 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions for clustering similar histograms together. */ - -#include "cluster.h" - -#include - -#include "../common/platform.h" -#include "bit_cost.h" /* BrotliPopulationCost */ -#include "fast_log.h" -#include "histogram.h" -#include "memory.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static BROTLI_INLINE BROTLI_BOOL HistogramPairIsLess( - const HistogramPair* p1, const HistogramPair* p2) { - if (p1->cost_diff != p2->cost_diff) { - return TO_BROTLI_BOOL(p1->cost_diff > p2->cost_diff); - } - return TO_BROTLI_BOOL((p1->idx2 - p1->idx1) > (p2->idx2 - p2->idx1)); -} - -/* Returns entropy reduction of the context map when we combine two clusters. */ -static BROTLI_INLINE double ClusterCostDiff(size_t size_a, size_t size_b) { - size_t size_c = size_a + size_b; - return (double)size_a * FastLog2(size_a) + - (double)size_b * FastLog2(size_b) - - (double)size_c * FastLog2(size_c); -} - -#define CODE(X) X - -#define FN(X) X ## Literal -#include "cluster_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Command -#include "cluster_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Distance -#include "cluster_inc.h" /* NOLINT(build/include) */ -#undef FN - -#undef CODE - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/cluster.h b/src/deps/brotli/enc/cluster.h deleted file mode 100644 index 013629c6d95ed..0000000000000 --- a/src/deps/brotli/enc/cluster.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions for clustering similar histograms together. */ - -#ifndef BROTLI_ENC_CLUSTER_H_ -#define BROTLI_ENC_CLUSTER_H_ - -#include - -#include "../common/platform.h" -#include "histogram.h" -#include "memory.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct HistogramPair { - uint32_t idx1; - uint32_t idx2; - double cost_combo; - double cost_diff; -} HistogramPair; - -#define CODE(X) /* Declaration */; - -#define FN(X) X ## Literal -#include "cluster_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Command -#include "cluster_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Distance -#include "cluster_inc.h" /* NOLINT(build/include) */ -#undef FN - -#undef CODE - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_CLUSTER_H_ */ diff --git a/src/deps/brotli/enc/cluster_inc.h b/src/deps/brotli/enc/cluster_inc.h deleted file mode 100644 index d6215ef06e936..0000000000000 --- a/src/deps/brotli/enc/cluster_inc.h +++ /dev/null @@ -1,325 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, CODE */ - -#define HistogramType FN(Histogram) - -/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if - it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */ -BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)( - const HistogramType* out, HistogramType* tmp, const uint32_t* cluster_size, - uint32_t idx1, uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs, - size_t* num_pairs) CODE({ - BROTLI_BOOL is_good_pair = BROTLI_FALSE; - HistogramPair p; - p.idx1 = p.idx2 = 0; - p.cost_diff = p.cost_combo = 0; - if (idx1 == idx2) { - return; - } - if (idx2 < idx1) { - uint32_t t = idx2; - idx2 = idx1; - idx1 = t; - } - p.idx1 = idx1; - p.idx2 = idx2; - p.cost_diff = 0.5 * ClusterCostDiff(cluster_size[idx1], cluster_size[idx2]); - p.cost_diff -= out[idx1].bit_cost_; - p.cost_diff -= out[idx2].bit_cost_; - - if (out[idx1].total_count_ == 0) { - p.cost_combo = out[idx2].bit_cost_; - is_good_pair = BROTLI_TRUE; - } else if (out[idx2].total_count_ == 0) { - p.cost_combo = out[idx1].bit_cost_; - is_good_pair = BROTLI_TRUE; - } else { - double threshold = *num_pairs == 0 ? 1e99 : - BROTLI_MAX(double, 0.0, pairs[0].cost_diff); - double cost_combo; - *tmp = out[idx1]; - FN(HistogramAddHistogram)(tmp, &out[idx2]); - cost_combo = FN(BrotliPopulationCost)(tmp); - if (cost_combo < threshold - p.cost_diff) { - p.cost_combo = cost_combo; - is_good_pair = BROTLI_TRUE; - } - } - if (is_good_pair) { - p.cost_diff += p.cost_combo; - if (*num_pairs > 0 && HistogramPairIsLess(&pairs[0], &p)) { - /* Replace the top of the queue if needed. */ - if (*num_pairs < max_num_pairs) { - pairs[*num_pairs] = pairs[0]; - ++(*num_pairs); - } - pairs[0] = p; - } else if (*num_pairs < max_num_pairs) { - pairs[*num_pairs] = p; - ++(*num_pairs); - } - } -}) - -BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out, - HistogramType* tmp, - uint32_t* cluster_size, - uint32_t* symbols, - uint32_t* clusters, - HistogramPair* pairs, - size_t num_clusters, - size_t symbols_size, - size_t max_clusters, - size_t max_num_pairs) CODE({ - double cost_diff_threshold = 0.0; - size_t min_cluster_size = 1; - size_t num_pairs = 0; - - { - /* We maintain a vector of histogram pairs, with the property that the pair - with the maximum bit cost reduction is the first. */ - size_t idx1; - for (idx1 = 0; idx1 < num_clusters; ++idx1) { - size_t idx2; - for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) { - FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, clusters[idx1], - clusters[idx2], max_num_pairs, &pairs[0], &num_pairs); - } - } - } - - while (num_clusters > min_cluster_size) { - uint32_t best_idx1; - uint32_t best_idx2; - size_t i; - if (pairs[0].cost_diff >= cost_diff_threshold) { - cost_diff_threshold = 1e99; - min_cluster_size = max_clusters; - continue; - } - /* Take the best pair from the top of heap. */ - best_idx1 = pairs[0].idx1; - best_idx2 = pairs[0].idx2; - FN(HistogramAddHistogram)(&out[best_idx1], &out[best_idx2]); - out[best_idx1].bit_cost_ = pairs[0].cost_combo; - cluster_size[best_idx1] += cluster_size[best_idx2]; - for (i = 0; i < symbols_size; ++i) { - if (symbols[i] == best_idx2) { - symbols[i] = best_idx1; - } - } - for (i = 0; i < num_clusters; ++i) { - if (clusters[i] == best_idx2) { - memmove(&clusters[i], &clusters[i + 1], - (num_clusters - i - 1) * sizeof(clusters[0])); - break; - } - } - --num_clusters; - { - /* Remove pairs intersecting the just combined best pair. */ - size_t copy_to_idx = 0; - for (i = 0; i < num_pairs; ++i) { - HistogramPair* p = &pairs[i]; - if (p->idx1 == best_idx1 || p->idx2 == best_idx1 || - p->idx1 == best_idx2 || p->idx2 == best_idx2) { - /* Remove invalid pair from the queue. */ - continue; - } - if (HistogramPairIsLess(&pairs[0], p)) { - /* Replace the top of the queue if needed. */ - HistogramPair front = pairs[0]; - pairs[0] = *p; - pairs[copy_to_idx] = front; - } else { - pairs[copy_to_idx] = *p; - } - ++copy_to_idx; - } - num_pairs = copy_to_idx; - } - - /* Push new pairs formed with the combined histogram to the heap. */ - for (i = 0; i < num_clusters; ++i) { - FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, best_idx1, - clusters[i], max_num_pairs, &pairs[0], &num_pairs); - } - } - return num_clusters; -}) - -/* What is the bit cost of moving histogram from cur_symbol to candidate. */ -BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)( - const HistogramType* histogram, const HistogramType* candidate, - HistogramType* tmp) CODE({ - if (histogram->total_count_ == 0) { - return 0.0; - } else { - *tmp = *histogram; - FN(HistogramAddHistogram)(tmp, candidate); - return FN(BrotliPopulationCost)(tmp) - candidate->bit_cost_; - } -}) - -/* Find the best 'out' histogram for each of the 'in' histograms. - When called, clusters[0..num_clusters) contains the unique values from - symbols[0..in_size), but this property is not preserved in this function. - Note: we assume that out[]->bit_cost_ is already up-to-date. */ -BROTLI_INTERNAL void FN(BrotliHistogramRemap)(const HistogramType* in, - size_t in_size, const uint32_t* clusters, size_t num_clusters, - HistogramType* out, HistogramType* tmp, uint32_t* symbols) CODE({ - size_t i; - for (i = 0; i < in_size; ++i) { - uint32_t best_out = i == 0 ? symbols[0] : symbols[i - 1]; - double best_bits = - FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out], tmp); - size_t j; - for (j = 0; j < num_clusters; ++j) { - const double cur_bits = - FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]], tmp); - if (cur_bits < best_bits) { - best_bits = cur_bits; - best_out = clusters[j]; - } - } - symbols[i] = best_out; - } - - /* Recompute each out based on raw and symbols. */ - for (i = 0; i < num_clusters; ++i) { - FN(HistogramClear)(&out[clusters[i]]); - } - for (i = 0; i < in_size; ++i) { - FN(HistogramAddHistogram)(&out[symbols[i]], &in[i]); - } -}) - -/* Reorders elements of the out[0..length) array and changes values in - symbols[0..length) array in the following way: - * when called, symbols[] contains indexes into out[], and has N unique - values (possibly N < length) - * on return, symbols'[i] = f(symbols[i]) and - out'[symbols'[i]] = out[symbols[i]], for each 0 <= i < length, - where f is a bijection between the range of symbols[] and [0..N), and - the first occurrences of values in symbols'[i] come in consecutive - increasing order. - Returns N, the number of unique values in symbols[]. */ -BROTLI_INTERNAL size_t FN(BrotliHistogramReindex)(MemoryManager* m, - HistogramType* out, uint32_t* symbols, size_t length) CODE({ - static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX; - uint32_t* new_index = BROTLI_ALLOC(m, uint32_t, length); - uint32_t next_index; - HistogramType* tmp; - size_t i; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return 0; - for (i = 0; i < length; ++i) { - new_index[i] = kInvalidIndex; - } - next_index = 0; - for (i = 0; i < length; ++i) { - if (new_index[symbols[i]] == kInvalidIndex) { - new_index[symbols[i]] = next_index; - ++next_index; - } - } - /* TODO(eustas): by using idea of "cycle-sort" we can avoid allocation of - tmp and reduce the number of copying by the factor of 2. */ - tmp = BROTLI_ALLOC(m, HistogramType, next_index); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return 0; - next_index = 0; - for (i = 0; i < length; ++i) { - if (new_index[symbols[i]] == next_index) { - tmp[next_index] = out[symbols[i]]; - ++next_index; - } - symbols[i] = new_index[symbols[i]]; - } - BROTLI_FREE(m, new_index); - for (i = 0; i < next_index; ++i) { - out[i] = tmp[i]; - } - BROTLI_FREE(m, tmp); - return next_index; -}) - -BROTLI_INTERNAL void FN(BrotliClusterHistograms)( - MemoryManager* m, const HistogramType* in, const size_t in_size, - size_t max_histograms, HistogramType* out, size_t* out_size, - uint32_t* histogram_symbols) CODE({ - uint32_t* cluster_size = BROTLI_ALLOC(m, uint32_t, in_size); - uint32_t* clusters = BROTLI_ALLOC(m, uint32_t, in_size); - size_t num_clusters = 0; - const size_t max_input_histograms = 64; - size_t pairs_capacity = max_input_histograms * max_input_histograms / 2; - /* For the first pass of clustering, we allow all pairs. */ - HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity + 1); - /* TODO(eustas): move to "persistent" arena? */ - HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 1); - size_t i; - - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(cluster_size) || - BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)|| BROTLI_IS_NULL(tmp)) { - return; - } - - for (i = 0; i < in_size; ++i) { - cluster_size[i] = 1; - } - - for (i = 0; i < in_size; ++i) { - out[i] = in[i]; - out[i].bit_cost_ = FN(BrotliPopulationCost)(&in[i]); - histogram_symbols[i] = (uint32_t)i; - } - - for (i = 0; i < in_size; i += max_input_histograms) { - size_t num_to_combine = - BROTLI_MIN(size_t, in_size - i, max_input_histograms); - size_t num_new_clusters; - size_t j; - for (j = 0; j < num_to_combine; ++j) { - clusters[num_clusters + j] = (uint32_t)(i + j); - } - num_new_clusters = - FN(BrotliHistogramCombine)(out, tmp, cluster_size, - &histogram_symbols[i], - &clusters[num_clusters], pairs, - num_to_combine, num_to_combine, - max_histograms, pairs_capacity); - num_clusters += num_new_clusters; - } - - { - /* For the second pass, we limit the total number of histogram pairs. - After this limit is reached, we only keep searching for the best pair. */ - size_t max_num_pairs = BROTLI_MIN(size_t, - 64 * num_clusters, (num_clusters / 2) * num_clusters); - BROTLI_ENSURE_CAPACITY( - m, HistogramPair, pairs, pairs_capacity, max_num_pairs + 1); - if (BROTLI_IS_OOM(m)) return; - - /* Collapse similar histograms. */ - num_clusters = FN(BrotliHistogramCombine)(out, tmp, cluster_size, - histogram_symbols, clusters, - pairs, num_clusters, in_size, - max_histograms, max_num_pairs); - } - BROTLI_FREE(m, pairs); - BROTLI_FREE(m, cluster_size); - /* Find the optimal map from original histograms to the final ones. */ - FN(BrotliHistogramRemap)(in, in_size, clusters, num_clusters, - out, tmp, histogram_symbols); - BROTLI_FREE(m, tmp); - BROTLI_FREE(m, clusters); - /* Convert the context map to a canonical form. */ - *out_size = FN(BrotliHistogramReindex)(m, out, histogram_symbols, in_size); - if (BROTLI_IS_OOM(m)) return; -}) - -#undef HistogramType diff --git a/src/deps/brotli/enc/command.c b/src/deps/brotli/enc/command.c deleted file mode 100644 index bf80561bcab80..0000000000000 --- a/src/deps/brotli/enc/command.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "command.h" - -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -const uint32_t kBrotliInsBase[BROTLI_NUM_INS_COPY_CODES] = { - 0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, - 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210, 22594}; -const uint32_t kBrotliInsExtra[BROTLI_NUM_INS_COPY_CODES] = { - 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24}; -const uint32_t kBrotliCopyBase[BROTLI_NUM_INS_COPY_CODES] = { - 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 18, - 22, 30, 38, 54, 70, 102, 134, 198, 326, 582, 1094, 2118}; -const uint32_t kBrotliCopyExtra[BROTLI_NUM_INS_COPY_CODES] = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24}; - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/command.h b/src/deps/brotli/enc/command.h deleted file mode 100644 index ba4de7eab3bab..0000000000000 --- a/src/deps/brotli/enc/command.h +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* This class models a sequence of literals and a backward reference copy. */ - -#ifndef BROTLI_ENC_COMMAND_H_ -#define BROTLI_ENC_COMMAND_H_ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "fast_log.h" -#include "params.h" -#include "prefix.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -BROTLI_INTERNAL extern const uint32_t - kBrotliInsBase[BROTLI_NUM_INS_COPY_CODES]; -BROTLI_INTERNAL extern const uint32_t - kBrotliInsExtra[BROTLI_NUM_INS_COPY_CODES]; -BROTLI_INTERNAL extern const uint32_t - kBrotliCopyBase[BROTLI_NUM_INS_COPY_CODES]; -BROTLI_INTERNAL extern const uint32_t - kBrotliCopyExtra[BROTLI_NUM_INS_COPY_CODES]; - -static BROTLI_INLINE uint16_t GetInsertLengthCode(size_t insertlen) { - if (insertlen < 6) { - return (uint16_t)insertlen; - } else if (insertlen < 130) { - uint32_t nbits = Log2FloorNonZero(insertlen - 2) - 1u; - return (uint16_t)((nbits << 1) + ((insertlen - 2) >> nbits) + 2); - } else if (insertlen < 2114) { - return (uint16_t)(Log2FloorNonZero(insertlen - 66) + 10); - } else if (insertlen < 6210) { - return 21u; - } else if (insertlen < 22594) { - return 22u; - } else { - return 23u; - } -} - -static BROTLI_INLINE uint16_t GetCopyLengthCode(size_t copylen) { - if (copylen < 10) { - return (uint16_t)(copylen - 2); - } else if (copylen < 134) { - uint32_t nbits = Log2FloorNonZero(copylen - 6) - 1u; - return (uint16_t)((nbits << 1) + ((copylen - 6) >> nbits) + 4); - } else if (copylen < 2118) { - return (uint16_t)(Log2FloorNonZero(copylen - 70) + 12); - } else { - return 23u; - } -} - -static BROTLI_INLINE uint16_t CombineLengthCodes( - uint16_t inscode, uint16_t copycode, BROTLI_BOOL use_last_distance) { - uint16_t bits64 = - (uint16_t)((copycode & 0x7u) | ((inscode & 0x7u) << 3u)); - if (use_last_distance && inscode < 8u && copycode < 16u) { - return (copycode < 8u) ? bits64 : (bits64 | 64u); - } else { - /* Specification: 5 Encoding of ... (last table) */ - /* offset = 2 * index, where index is in range [0..8] */ - uint32_t offset = 2u * ((copycode >> 3u) + 3u * (inscode >> 3u)); - /* All values in specification are K * 64, - where K = [2, 3, 6, 4, 5, 8, 7, 9, 10], - i + 1 = [1, 2, 3, 4, 5, 6, 7, 8, 9], - K - i - 1 = [1, 1, 3, 0, 0, 2, 0, 1, 2] = D. - All values in D require only 2 bits to encode. - Magic constant is shifted 6 bits left, to avoid final multiplication. */ - offset = (offset << 5u) + 0x40u + ((0x520D40u >> offset) & 0xC0u); - return (uint16_t)(offset | bits64); - } -} - -static BROTLI_INLINE void GetLengthCode(size_t insertlen, size_t copylen, - BROTLI_BOOL use_last_distance, - uint16_t* code) { - uint16_t inscode = GetInsertLengthCode(insertlen); - uint16_t copycode = GetCopyLengthCode(copylen); - *code = CombineLengthCodes(inscode, copycode, use_last_distance); -} - -static BROTLI_INLINE uint32_t GetInsertBase(uint16_t inscode) { - return kBrotliInsBase[inscode]; -} - -static BROTLI_INLINE uint32_t GetInsertExtra(uint16_t inscode) { - return kBrotliInsExtra[inscode]; -} - -static BROTLI_INLINE uint32_t GetCopyBase(uint16_t copycode) { - return kBrotliCopyBase[copycode]; -} - -static BROTLI_INLINE uint32_t GetCopyExtra(uint16_t copycode) { - return kBrotliCopyExtra[copycode]; -} - -typedef struct Command { - uint32_t insert_len_; - /* Stores copy_len in low 25 bits and copy_code - copy_len in high 7 bit. */ - uint32_t copy_len_; - /* Stores distance extra bits. */ - uint32_t dist_extra_; - uint16_t cmd_prefix_; - /* Stores distance code in low 10 bits - and number of extra bits in high 6 bits. */ - uint16_t dist_prefix_; -} Command; - -/* distance_code is e.g. 0 for same-as-last short code, or 16 for offset 1. */ -static BROTLI_INLINE void InitCommand(Command* self, - const BrotliDistanceParams* dist, size_t insertlen, - size_t copylen, int copylen_code_delta, size_t distance_code) { - /* Don't rely on signed int representation, use honest casts. */ - uint32_t delta = (uint8_t)((int8_t)copylen_code_delta); - self->insert_len_ = (uint32_t)insertlen; - self->copy_len_ = (uint32_t)(copylen | (delta << 25)); - /* The distance prefix and extra bits are stored in this Command as if - npostfix and ndirect were 0, they are only recomputed later after the - clustering if needed. */ - PrefixEncodeCopyDistance( - distance_code, dist->num_direct_distance_codes, - dist->distance_postfix_bits, &self->dist_prefix_, &self->dist_extra_); - GetLengthCode( - insertlen, (size_t)((int)copylen + copylen_code_delta), - TO_BROTLI_BOOL((self->dist_prefix_ & 0x3FF) == 0), &self->cmd_prefix_); -} - -static BROTLI_INLINE void InitInsertCommand(Command* self, size_t insertlen) { - self->insert_len_ = (uint32_t)insertlen; - self->copy_len_ = 4 << 25; - self->dist_extra_ = 0; - self->dist_prefix_ = BROTLI_NUM_DISTANCE_SHORT_CODES; - GetLengthCode(insertlen, 4, BROTLI_FALSE, &self->cmd_prefix_); -} - -static BROTLI_INLINE uint32_t CommandRestoreDistanceCode( - const Command* self, const BrotliDistanceParams* dist) { - if ((self->dist_prefix_ & 0x3FFu) < - BROTLI_NUM_DISTANCE_SHORT_CODES + dist->num_direct_distance_codes) { - return self->dist_prefix_ & 0x3FFu; - } else { - uint32_t dcode = self->dist_prefix_ & 0x3FFu; - uint32_t nbits = self->dist_prefix_ >> 10; - uint32_t extra = self->dist_extra_; - uint32_t postfix_mask = (1U << dist->distance_postfix_bits) - 1U; - uint32_t hcode = (dcode - dist->num_direct_distance_codes - - BROTLI_NUM_DISTANCE_SHORT_CODES) >> - dist->distance_postfix_bits; - uint32_t lcode = (dcode - dist->num_direct_distance_codes - - BROTLI_NUM_DISTANCE_SHORT_CODES) & postfix_mask; - uint32_t offset = ((2U + (hcode & 1U)) << nbits) - 4U; - return ((offset + extra) << dist->distance_postfix_bits) + lcode + - dist->num_direct_distance_codes + BROTLI_NUM_DISTANCE_SHORT_CODES; - } -} - -static BROTLI_INLINE uint32_t CommandDistanceContext(const Command* self) { - uint32_t r = self->cmd_prefix_ >> 6; - uint32_t c = self->cmd_prefix_ & 7; - if ((r == 0 || r == 2 || r == 4 || r == 7) && (c <= 2)) { - return c; - } - return 3; -} - -static BROTLI_INLINE uint32_t CommandCopyLen(const Command* self) { - return self->copy_len_ & 0x1FFFFFF; -} - -static BROTLI_INLINE uint32_t CommandCopyLenCode(const Command* self) { - uint32_t modifier = self->copy_len_ >> 25; - int32_t delta = (int8_t)((uint8_t)(modifier | ((modifier & 0x40) << 1))); - return (uint32_t)((int32_t)(self->copy_len_ & 0x1FFFFFF) + delta); -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_COMMAND_H_ */ diff --git a/src/deps/brotli/enc/compound_dictionary.c b/src/deps/brotli/enc/compound_dictionary.c deleted file mode 100644 index a3b5e6933dd1c..0000000000000 --- a/src/deps/brotli/enc/compound_dictionary.c +++ /dev/null @@ -1,207 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "compound_dictionary.h" - -#include - -#include "../common/platform.h" -#include "memory.h" -#include "quality.h" - -static PreparedDictionary* CreatePreparedDictionaryWithParams(MemoryManager* m, - const uint8_t* source, size_t source_size, uint32_t bucket_bits, - uint32_t slot_bits, uint32_t hash_bits, uint16_t bucket_limit) { - /* Step 1: create "bloated" hasher. */ - uint32_t num_slots = 1u << slot_bits; - uint32_t num_buckets = 1u << bucket_bits; - uint32_t hash_shift = 64u - bucket_bits; - uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits); - uint32_t slot_mask = num_slots - 1; - size_t alloc_size = (sizeof(uint32_t) << slot_bits) + - (sizeof(uint32_t) << slot_bits) + - (sizeof(uint16_t) << bucket_bits) + - (sizeof(uint32_t) << bucket_bits) + - (sizeof(uint32_t) * source_size); - uint8_t* flat = NULL; - PreparedDictionary* result = NULL; - uint16_t* num = NULL; - uint32_t* bucket_heads = NULL; - uint32_t* next_bucket = NULL; - uint32_t* slot_offsets = NULL; - uint16_t* heads = NULL; - uint32_t* items = NULL; - uint8_t** source_ref = NULL; - uint32_t i; - uint32_t* slot_size = NULL; - uint32_t* slot_limit = NULL; - uint32_t total_items = 0; - if (slot_bits > 16) return NULL; - if (slot_bits > bucket_bits) return NULL; - if (bucket_bits - slot_bits >= 16) return NULL; - - flat = BROTLI_ALLOC(m, uint8_t, alloc_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(flat)) return NULL; - - slot_size = (uint32_t*)flat; - slot_limit = (uint32_t*)(&slot_size[num_slots]); - num = (uint16_t*)(&slot_limit[num_slots]); - bucket_heads = (uint32_t*)(&num[num_buckets]); - next_bucket = (uint32_t*)(&bucket_heads[num_buckets]); - memset(num, 0, num_buckets * sizeof(num[0])); - - /* TODO(eustas): apply custom "store" order. */ - for (i = 0; i + 7 < source_size; ++i) { - const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(&source[i]) & hash_mask) * - kPreparedDictionaryHashMul64Long; - const uint32_t key = (uint32_t)(h >> hash_shift); - uint16_t count = num[key]; - next_bucket[i] = (count == 0) ? ((uint32_t)(-1)) : bucket_heads[key]; - bucket_heads[key] = i; - count++; - if (count > bucket_limit) count = bucket_limit; - num[key] = count; - } - - /* Step 2: find slot limits. */ - for (i = 0; i < num_slots; ++i) { - BROTLI_BOOL overflow = BROTLI_FALSE; - slot_limit[i] = bucket_limit; - while (BROTLI_TRUE) { - uint32_t limit = slot_limit[i]; - size_t j; - uint32_t count = 0; - overflow = BROTLI_FALSE; - for (j = i; j < num_buckets; j += num_slots) { - uint32_t size = num[j]; - /* Last chain may span behind 64K limit; overflow happens only if - we are about to use 0xFFFF+ as item offset. */ - if (count >= 0xFFFF) { - overflow = BROTLI_TRUE; - break; - } - if (size > limit) size = limit; - count += size; - } - if (!overflow) { - slot_size[i] = count; - total_items += count; - break; - } - slot_limit[i]--; - } - } - - /* Step 3: transfer data to "slim" hasher. */ - alloc_size = sizeof(PreparedDictionary) + (sizeof(uint32_t) << slot_bits) + - (sizeof(uint16_t) << bucket_bits) + (sizeof(uint32_t) * total_items) + - sizeof(uint8_t*); - - result = (PreparedDictionary*)BROTLI_ALLOC(m, uint8_t, alloc_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(result)) { - BROTLI_FREE(m, flat); - return NULL; - } - slot_offsets = (uint32_t*)(&result[1]); - heads = (uint16_t*)(&slot_offsets[num_slots]); - items = (uint32_t*)(&heads[num_buckets]); - source_ref = (uint8_t**)(&items[total_items]); - - result->magic = kLeanPreparedDictionaryMagic; - result->num_items = total_items; - result->source_size = (uint32_t)source_size; - result->hash_bits = hash_bits; - result->bucket_bits = bucket_bits; - result->slot_bits = slot_bits; - BROTLI_UNALIGNED_STORE_PTR(source_ref, source); - - total_items = 0; - for (i = 0; i < num_slots; ++i) { - slot_offsets[i] = total_items; - total_items += slot_size[i]; - slot_size[i] = 0; - } - for (i = 0; i < num_buckets; ++i) { - uint32_t slot = i & slot_mask; - uint32_t count = num[i]; - uint32_t pos; - size_t j; - size_t cursor = slot_size[slot]; - if (count > slot_limit[slot]) count = slot_limit[slot]; - if (count == 0) { - heads[i] = 0xFFFF; - continue; - } - heads[i] = (uint16_t)cursor; - cursor += slot_offsets[slot]; - slot_size[slot] += count; - pos = bucket_heads[i]; - for (j = 0; j < count; j++) { - items[cursor++] = pos; - pos = next_bucket[pos]; - } - items[cursor - 1] |= 0x80000000; - } - - BROTLI_FREE(m, flat); - return result; -} - -PreparedDictionary* CreatePreparedDictionary(MemoryManager* m, - const uint8_t* source, size_t source_size) { - uint32_t bucket_bits = 17; - uint32_t slot_bits = 7; - uint32_t hash_bits = 40; - uint16_t bucket_limit = 32; - size_t volume = 16u << bucket_bits; - /* Tune parameters to fit dictionary size. */ - while (volume < source_size && bucket_bits < 22) { - bucket_bits++; - slot_bits++; - volume <<= 1; - } - return CreatePreparedDictionaryWithParams(m, - source, source_size, bucket_bits, slot_bits, hash_bits, bucket_limit); -} - -void DestroyPreparedDictionary(MemoryManager* m, - PreparedDictionary* dictionary) { - if (!dictionary) return; - BROTLI_FREE(m, dictionary); -} - -BROTLI_BOOL AttachPreparedDictionary( - CompoundDictionary* compound, const PreparedDictionary* dictionary) { - size_t length = 0; - size_t index = 0; - - if (compound->num_chunks == SHARED_BROTLI_MAX_COMPOUND_DICTS) { - return BROTLI_FALSE; - } - - if (!dictionary) return BROTLI_FALSE; - - length = dictionary->source_size; - index = compound->num_chunks; - compound->total_size += length; - compound->chunks[index] = dictionary; - compound->chunk_offsets[index + 1] = compound->total_size; - { - uint32_t* slot_offsets = (uint32_t*)(&dictionary[1]); - uint16_t* heads = (uint16_t*)(&slot_offsets[1u << dictionary->slot_bits]); - uint32_t* items = (uint32_t*)(&heads[1u << dictionary->bucket_bits]); - const void* tail = (void*)&items[dictionary->num_items]; - if (dictionary->magic == kPreparedDictionaryMagic) { - compound->chunk_source[index] = (const uint8_t*)tail; - } else { - /* dictionary->magic == kLeanPreparedDictionaryMagic */ - compound->chunk_source[index] = - (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail); - } - } - compound->num_chunks++; - return BROTLI_TRUE; -} diff --git a/src/deps/brotli/enc/compound_dictionary.h b/src/deps/brotli/enc/compound_dictionary.h deleted file mode 100644 index 9c531d5b194b9..0000000000000 --- a/src/deps/brotli/enc/compound_dictionary.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#ifndef BROTLI_ENC_PREPARED_DICTIONARY_H_ -#define BROTLI_ENC_PREPARED_DICTIONARY_H_ - -#include -#include - -#include "../common/platform.h" -#include "../common/constants.h" -#include "memory.h" - -/* "Fat" prepared dictionary, could be cooked outside of C implementation, - * e.g. on Java side. LZ77 data is copied inside PreparedDictionary struct. */ -static const uint32_t kPreparedDictionaryMagic = 0xDEBCEDE0; - -static const uint32_t kSharedDictionaryMagic = 0xDEBCEDE1; - -static const uint32_t kManagedDictionaryMagic = 0xDEBCEDE2; - -/* "Lean" prepared dictionary. LZ77 data is referenced. It is the responsibility - * of caller of "prepare dictionary" to keep the LZ77 data while prepared - * dictionary is in use. */ -static const uint32_t kLeanPreparedDictionaryMagic = 0xDEBCEDE3; - -static const uint64_t kPreparedDictionaryHashMul64Long = - BROTLI_MAKE_UINT64_T(0x1FE35A7Bu, 0xD3579BD3u); - -typedef struct PreparedDictionary { - uint32_t magic; - uint32_t num_items; - uint32_t source_size; - uint32_t hash_bits; - uint32_t bucket_bits; - uint32_t slot_bits; - - /* --- Dynamic size members --- */ - - /* uint32_t slot_offsets[1 << slot_bits]; */ - /* uint16_t heads[1 << bucket_bits]; */ - /* uint32_t items[variable]; */ - - /* [maybe] uint8_t* source_ref, depending on magic. */ - /* [maybe] uint8_t source[source_size], depending on magic. */ -} PreparedDictionary; - -BROTLI_INTERNAL PreparedDictionary* CreatePreparedDictionary(MemoryManager* m, - const uint8_t* source, size_t source_size); - -BROTLI_INTERNAL void DestroyPreparedDictionary(MemoryManager* m, - PreparedDictionary* dictionary); - -typedef struct CompoundDictionary { - /* LZ77 prefix, compound dictionary */ - size_t num_chunks; - size_t total_size; - /* Client instances. */ - const PreparedDictionary* chunks[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; - const uint8_t* chunk_source[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; - size_t chunk_offsets[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; - - size_t num_prepared_instances_; - /* Owned instances. */ - PreparedDictionary* prepared_instances_[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; -} CompoundDictionary; - -BROTLI_INTERNAL BROTLI_BOOL AttachPreparedDictionary( - CompoundDictionary* compound, const PreparedDictionary* dictionary); - -#endif /* BROTLI_ENC_PREPARED_DICTIONARY */ diff --git a/src/deps/brotli/enc/compress_fragment.c b/src/deps/brotli/enc/compress_fragment.c deleted file mode 100644 index 13890eabf6292..0000000000000 --- a/src/deps/brotli/enc/compress_fragment.c +++ /dev/null @@ -1,800 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function for fast encoding of an input fragment, independently from the input - history. This function uses one-pass processing: when we find a backward - match, we immediately emit the corresponding command and literal codes to - the bit stream. - - Adapted from the CompressFragment() function in - https://github.com/google/snappy/blob/master/snappy.cc */ - -#include "compress_fragment.h" - -#include /* memcmp, memcpy, memset */ - -#include - -#include "../common/platform.h" -#include "brotli_bit_stream.h" -#include "entropy_encode.h" -#include "fast_log.h" -#include "find_match_length.h" -#include "write_bits.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define MAX_DISTANCE (long)BROTLI_MAX_BACKWARD_LIMIT(18) - -/* kHashMul32 multiplier has these properties: - * The multiplier must be odd. Otherwise we may lose the highest bit. - * No long streaks of ones or zeros. - * There is no effort to ensure that it is a prime, the oddity is enough - for this use. - * The number has been tuned heuristically against compression benchmarks. */ -static const uint32_t kHashMul32 = 0x1E35A7BD; - -static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) { - const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(p) << 24) * kHashMul32; - return (uint32_t)(h >> shift); -} - -static BROTLI_INLINE uint32_t HashBytesAtOffset( - uint64_t v, int offset, size_t shift) { - BROTLI_DCHECK(offset >= 0); - BROTLI_DCHECK(offset <= 3); - { - const uint64_t h = ((v >> (8 * offset)) << 24) * kHashMul32; - return (uint32_t)(h >> shift); - } -} - -static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2) { - return TO_BROTLI_BOOL( - BrotliUnalignedRead32(p1) == BrotliUnalignedRead32(p2) && - p1[4] == p2[4]); -} - -/* Builds a literal prefix code into "depths" and "bits" based on the statistics - of the "input" string and stores it into the bit stream. - Note that the prefix code here is built from the pre-LZ77 input, therefore - we can only approximate the statistics of the actual literal stream. - Moreover, for long inputs we build a histogram from a sample of the input - and thus have to assign a non-zero depth for each literal. - Returns estimated compression ratio millibytes/char for encoding given input - with generated code. */ -static size_t BuildAndStoreLiteralPrefixCode(BrotliOnePassArena* s, - const uint8_t* input, - const size_t input_size, - uint8_t depths[256], - uint16_t bits[256], - size_t* storage_ix, - uint8_t* storage) { - uint32_t* BROTLI_RESTRICT const histogram = s->histogram; - size_t histogram_total; - size_t i; - memset(histogram, 0, sizeof(s->histogram)); - - if (input_size < (1 << 15)) { - for (i = 0; i < input_size; ++i) { - ++histogram[input[i]]; - } - histogram_total = input_size; - for (i = 0; i < 256; ++i) { - /* We weigh the first 11 samples with weight 3 to account for the - balancing effect of the LZ77 phase on the histogram. */ - const uint32_t adjust = 2 * BROTLI_MIN(uint32_t, histogram[i], 11u); - histogram[i] += adjust; - histogram_total += adjust; - } - } else { - static const size_t kSampleRate = 29; - for (i = 0; i < input_size; i += kSampleRate) { - ++histogram[input[i]]; - } - histogram_total = (input_size + kSampleRate - 1) / kSampleRate; - for (i = 0; i < 256; ++i) { - /* We add 1 to each population count to avoid 0 bit depths (since this is - only a sample and we don't know if the symbol appears or not), and we - weigh the first 11 samples with weight 3 to account for the balancing - effect of the LZ77 phase on the histogram (more frequent symbols are - more likely to be in backward references instead as literals). */ - const uint32_t adjust = 1 + 2 * BROTLI_MIN(uint32_t, histogram[i], 11u); - histogram[i] += adjust; - histogram_total += adjust; - } - } - BrotliBuildAndStoreHuffmanTreeFast(s->tree, histogram, histogram_total, - /* max_bits = */ 8, - depths, bits, storage_ix, storage); - { - size_t literal_ratio = 0; - for (i = 0; i < 256; ++i) { - if (histogram[i]) literal_ratio += histogram[i] * depths[i]; - } - /* Estimated encoding ratio, millibytes per symbol. */ - return (literal_ratio * 125) / histogram_total; - } -} - -/* Builds a command and distance prefix code (each 64 symbols) into "depth" and - "bits" based on "histogram" and stores it into the bit stream. */ -static void BuildAndStoreCommandPrefixCode(BrotliOnePassArena* s, - size_t* storage_ix, uint8_t* storage) { - const uint32_t* const histogram = s->cmd_histo; - uint8_t* const depth = s->cmd_depth; - uint16_t* const bits = s->cmd_bits; - uint8_t* BROTLI_RESTRICT const tmp_depth = s->tmp_depth; - uint16_t* BROTLI_RESTRICT const tmp_bits = s->tmp_bits; - /* TODO(eustas): do only once on initialization. */ - memset(tmp_depth, 0, BROTLI_NUM_COMMAND_SYMBOLS); - - BrotliCreateHuffmanTree(histogram, 64, 15, s->tree, depth); - BrotliCreateHuffmanTree(&histogram[64], 64, 14, s->tree, &depth[64]); - /* We have to jump through a few hoops here in order to compute - the command bits because the symbols are in a different order than in - the full alphabet. This looks complicated, but having the symbols - in this order in the command bits saves a few branches in the Emit* - functions. */ - memcpy(tmp_depth, depth, 24); - memcpy(tmp_depth + 24, depth + 40, 8); - memcpy(tmp_depth + 32, depth + 24, 8); - memcpy(tmp_depth + 40, depth + 48, 8); - memcpy(tmp_depth + 48, depth + 32, 8); - memcpy(tmp_depth + 56, depth + 56, 8); - BrotliConvertBitDepthsToSymbols(tmp_depth, 64, tmp_bits); - memcpy(bits, tmp_bits, 48); - memcpy(bits + 24, tmp_bits + 32, 16); - memcpy(bits + 32, tmp_bits + 48, 16); - memcpy(bits + 40, tmp_bits + 24, 16); - memcpy(bits + 48, tmp_bits + 40, 16); - memcpy(bits + 56, tmp_bits + 56, 16); - BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]); - { - /* Create the bit length array for the full command alphabet. */ - size_t i; - memset(tmp_depth, 0, 64); /* only 64 first values were used */ - memcpy(tmp_depth, depth, 8); - memcpy(tmp_depth + 64, depth + 8, 8); - memcpy(tmp_depth + 128, depth + 16, 8); - memcpy(tmp_depth + 192, depth + 24, 8); - memcpy(tmp_depth + 384, depth + 32, 8); - for (i = 0; i < 8; ++i) { - tmp_depth[128 + 8 * i] = depth[40 + i]; - tmp_depth[256 + 8 * i] = depth[48 + i]; - tmp_depth[448 + 8 * i] = depth[56 + i]; - } - /* TODO(eustas): could/should full-length machinery be avoided? */ - BrotliStoreHuffmanTree( - tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS, s->tree, storage_ix, storage); - } - BrotliStoreHuffmanTree(&depth[64], 64, s->tree, storage_ix, storage); -} - -/* REQUIRES: insertlen < 6210 */ -static BROTLI_INLINE void EmitInsertLen(size_t insertlen, - const uint8_t depth[128], - const uint16_t bits[128], - uint32_t histo[128], - size_t* storage_ix, - uint8_t* storage) { - if (insertlen < 6) { - const size_t code = insertlen + 40; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - ++histo[code]; - } else if (insertlen < 130) { - const size_t tail = insertlen - 2; - const uint32_t nbits = Log2FloorNonZero(tail) - 1u; - const size_t prefix = tail >> nbits; - const size_t inscode = (nbits << 1) + prefix + 42; - BrotliWriteBits(depth[inscode], bits[inscode], storage_ix, storage); - BrotliWriteBits(nbits, tail - (prefix << nbits), storage_ix, storage); - ++histo[inscode]; - } else if (insertlen < 2114) { - const size_t tail = insertlen - 66; - const uint32_t nbits = Log2FloorNonZero(tail); - const size_t code = nbits + 50; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - BrotliWriteBits(nbits, tail - ((size_t)1 << nbits), storage_ix, storage); - ++histo[code]; - } else { - BrotliWriteBits(depth[61], bits[61], storage_ix, storage); - BrotliWriteBits(12, insertlen - 2114, storage_ix, storage); - ++histo[61]; - } -} - -static BROTLI_INLINE void EmitLongInsertLen(size_t insertlen, - const uint8_t depth[128], - const uint16_t bits[128], - uint32_t histo[128], - size_t* storage_ix, - uint8_t* storage) { - if (insertlen < 22594) { - BrotliWriteBits(depth[62], bits[62], storage_ix, storage); - BrotliWriteBits(14, insertlen - 6210, storage_ix, storage); - ++histo[62]; - } else { - BrotliWriteBits(depth[63], bits[63], storage_ix, storage); - BrotliWriteBits(24, insertlen - 22594, storage_ix, storage); - ++histo[63]; - } -} - -static BROTLI_INLINE void EmitCopyLen(size_t copylen, - const uint8_t depth[128], - const uint16_t bits[128], - uint32_t histo[128], - size_t* storage_ix, - uint8_t* storage) { - if (copylen < 10) { - BrotliWriteBits( - depth[copylen + 14], bits[copylen + 14], storage_ix, storage); - ++histo[copylen + 14]; - } else if (copylen < 134) { - const size_t tail = copylen - 6; - const uint32_t nbits = Log2FloorNonZero(tail) - 1u; - const size_t prefix = tail >> nbits; - const size_t code = (nbits << 1) + prefix + 20; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - BrotliWriteBits(nbits, tail - (prefix << nbits), storage_ix, storage); - ++histo[code]; - } else if (copylen < 2118) { - const size_t tail = copylen - 70; - const uint32_t nbits = Log2FloorNonZero(tail); - const size_t code = nbits + 28; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - BrotliWriteBits(nbits, tail - ((size_t)1 << nbits), storage_ix, storage); - ++histo[code]; - } else { - BrotliWriteBits(depth[39], bits[39], storage_ix, storage); - BrotliWriteBits(24, copylen - 2118, storage_ix, storage); - ++histo[39]; - } -} - -static BROTLI_INLINE void EmitCopyLenLastDistance(size_t copylen, - const uint8_t depth[128], - const uint16_t bits[128], - uint32_t histo[128], - size_t* storage_ix, - uint8_t* storage) { - if (copylen < 12) { - BrotliWriteBits(depth[copylen - 4], bits[copylen - 4], storage_ix, storage); - ++histo[copylen - 4]; - } else if (copylen < 72) { - const size_t tail = copylen - 8; - const uint32_t nbits = Log2FloorNonZero(tail) - 1; - const size_t prefix = tail >> nbits; - const size_t code = (nbits << 1) + prefix + 4; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - BrotliWriteBits(nbits, tail - (prefix << nbits), storage_ix, storage); - ++histo[code]; - } else if (copylen < 136) { - const size_t tail = copylen - 8; - const size_t code = (tail >> 5) + 30; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - BrotliWriteBits(5, tail & 31, storage_ix, storage); - BrotliWriteBits(depth[64], bits[64], storage_ix, storage); - ++histo[code]; - ++histo[64]; - } else if (copylen < 2120) { - const size_t tail = copylen - 72; - const uint32_t nbits = Log2FloorNonZero(tail); - const size_t code = nbits + 28; - BrotliWriteBits(depth[code], bits[code], storage_ix, storage); - BrotliWriteBits(nbits, tail - ((size_t)1 << nbits), storage_ix, storage); - BrotliWriteBits(depth[64], bits[64], storage_ix, storage); - ++histo[code]; - ++histo[64]; - } else { - BrotliWriteBits(depth[39], bits[39], storage_ix, storage); - BrotliWriteBits(24, copylen - 2120, storage_ix, storage); - BrotliWriteBits(depth[64], bits[64], storage_ix, storage); - ++histo[39]; - ++histo[64]; - } -} - -static BROTLI_INLINE void EmitDistance(size_t distance, - const uint8_t depth[128], - const uint16_t bits[128], - uint32_t histo[128], - size_t* storage_ix, uint8_t* storage) { - const size_t d = distance + 3; - const uint32_t nbits = Log2FloorNonZero(d) - 1u; - const size_t prefix = (d >> nbits) & 1; - const size_t offset = (2 + prefix) << nbits; - const size_t distcode = 2 * (nbits - 1) + prefix + 80; - BrotliWriteBits(depth[distcode], bits[distcode], storage_ix, storage); - BrotliWriteBits(nbits, d - offset, storage_ix, storage); - ++histo[distcode]; -} - -static BROTLI_INLINE void EmitLiterals(const uint8_t* input, const size_t len, - const uint8_t depth[256], - const uint16_t bits[256], - size_t* storage_ix, uint8_t* storage) { - size_t j; - for (j = 0; j < len; j++) { - const uint8_t lit = input[j]; - BrotliWriteBits(depth[lit], bits[lit], storage_ix, storage); - } -} - -/* REQUIRES: len <= 1 << 24. */ -static void BrotliStoreMetaBlockHeader( - size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix, - uint8_t* storage) { - size_t nibbles = 6; - /* ISLAST */ - BrotliWriteBits(1, 0, storage_ix, storage); - if (len <= (1U << 16)) { - nibbles = 4; - } else if (len <= (1U << 20)) { - nibbles = 5; - } - BrotliWriteBits(2, nibbles - 4, storage_ix, storage); - BrotliWriteBits(nibbles * 4, len - 1, storage_ix, storage); - /* ISUNCOMPRESSED */ - BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage); -} - -static void UpdateBits(size_t n_bits, uint32_t bits, size_t pos, - uint8_t* array) { - while (n_bits > 0) { - size_t byte_pos = pos >> 3; - size_t n_unchanged_bits = pos & 7; - size_t n_changed_bits = BROTLI_MIN(size_t, n_bits, 8 - n_unchanged_bits); - size_t total_bits = n_unchanged_bits + n_changed_bits; - uint32_t mask = - (~((1u << total_bits) - 1u)) | ((1u << n_unchanged_bits) - 1u); - uint32_t unchanged_bits = array[byte_pos] & mask; - uint32_t changed_bits = bits & ((1u << n_changed_bits) - 1u); - array[byte_pos] = - (uint8_t)((changed_bits << n_unchanged_bits) | unchanged_bits); - n_bits -= n_changed_bits; - bits >>= n_changed_bits; - pos += n_changed_bits; - } -} - -static void RewindBitPosition(const size_t new_storage_ix, - size_t* storage_ix, uint8_t* storage) { - const size_t bitpos = new_storage_ix & 7; - const size_t mask = (1u << bitpos) - 1; - storage[new_storage_ix >> 3] &= (uint8_t)mask; - *storage_ix = new_storage_ix; -} - -static BROTLI_BOOL ShouldMergeBlock(BrotliOnePassArena* s, - const uint8_t* data, size_t len, const uint8_t* depths) { - uint32_t* BROTLI_RESTRICT const histo = s->histogram; - static const size_t kSampleRate = 43; - size_t i; - memset(histo, 0, sizeof(s->histogram)); - for (i = 0; i < len; i += kSampleRate) { - ++histo[data[i]]; - } - { - const size_t total = (len + kSampleRate - 1) / kSampleRate; - double r = (FastLog2(total) + 0.5) * (double)total + 200; - for (i = 0; i < 256; ++i) { - r -= (double)histo[i] * (depths[i] + FastLog2(histo[i])); - } - return TO_BROTLI_BOOL(r >= 0.0); - } -} - -/* Acceptable loss for uncompressible speedup is 2% */ -#define MIN_RATIO 980 - -static BROTLI_INLINE BROTLI_BOOL ShouldUseUncompressedMode( - const uint8_t* metablock_start, const uint8_t* next_emit, - const size_t insertlen, const size_t literal_ratio) { - const size_t compressed = (size_t)(next_emit - metablock_start); - if (compressed * 50 > insertlen) { - return BROTLI_FALSE; - } else { - return TO_BROTLI_BOOL(literal_ratio > MIN_RATIO); - } -} - -static void EmitUncompressedMetaBlock(const uint8_t* begin, const uint8_t* end, - const size_t storage_ix_start, - size_t* storage_ix, uint8_t* storage) { - const size_t len = (size_t)(end - begin); - RewindBitPosition(storage_ix_start, storage_ix, storage); - BrotliStoreMetaBlockHeader(len, 1, storage_ix, storage); - *storage_ix = (*storage_ix + 7u) & ~7u; - memcpy(&storage[*storage_ix >> 3], begin, len); - *storage_ix += len << 3; - storage[*storage_ix >> 3] = 0; -} - -static uint32_t kCmdHistoSeed[128] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0, 0, 0, -}; - -static BROTLI_INLINE void BrotliCompressFragmentFastImpl( - BrotliOnePassArena* s, const uint8_t* input, size_t input_size, - BROTLI_BOOL is_last, int* table, size_t table_bits, - size_t* storage_ix, uint8_t* storage) { - uint8_t* BROTLI_RESTRICT const cmd_depth = s->cmd_depth; - uint16_t* BROTLI_RESTRICT const cmd_bits = s->cmd_bits; - uint32_t* BROTLI_RESTRICT const cmd_histo = s->cmd_histo; - uint8_t* BROTLI_RESTRICT const lit_depth = s->lit_depth; - uint16_t* BROTLI_RESTRICT const lit_bits = s->lit_bits; - const uint8_t* ip_end; - - /* "next_emit" is a pointer to the first byte that is not covered by a - previous copy. Bytes between "next_emit" and the start of the next copy or - the end of the input will be emitted as literal bytes. */ - const uint8_t* next_emit = input; - /* Save the start of the first block for position and distance computations. - */ - const uint8_t* base_ip = input; - - static const size_t kFirstBlockSize = 3 << 15; - static const size_t kMergeBlockSize = 1 << 16; - - const size_t kInputMarginBytes = BROTLI_WINDOW_GAP; - const size_t kMinMatchLen = 5; - - const uint8_t* metablock_start = input; - size_t block_size = BROTLI_MIN(size_t, input_size, kFirstBlockSize); - size_t total_block_size = block_size; - /* Save the bit position of the MLEN field of the meta-block header, so that - we can update it later if we decide to extend this meta-block. */ - size_t mlen_storage_ix = *storage_ix + 3; - - size_t literal_ratio; - - const uint8_t* ip; - int last_distance; - - const size_t shift = 64u - table_bits; - - BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); - /* No block splits, no contexts. */ - BrotliWriteBits(13, 0, storage_ix, storage); - - literal_ratio = BuildAndStoreLiteralPrefixCode( - s, input, block_size, s->lit_depth, s->lit_bits, storage_ix, storage); - - { - /* Store the pre-compressed command and distance prefix codes. */ - size_t i; - for (i = 0; i + 7 < s->cmd_code_numbits; i += 8) { - BrotliWriteBits(8, s->cmd_code[i >> 3], storage_ix, storage); - } - } - BrotliWriteBits(s->cmd_code_numbits & 7, - s->cmd_code[s->cmd_code_numbits >> 3], storage_ix, storage); - - emit_commands: - /* Initialize the command and distance histograms. We will gather - statistics of command and distance codes during the processing - of this block and use it to update the command and distance - prefix codes for the next block. */ - memcpy(s->cmd_histo, kCmdHistoSeed, sizeof(kCmdHistoSeed)); - - /* "ip" is the input pointer. */ - ip = input; - last_distance = -1; - ip_end = input + block_size; - - if (BROTLI_PREDICT_TRUE(block_size >= kInputMarginBytes)) { - /* For the last block, we need to keep a 16 bytes margin so that we can be - sure that all distances are at most window size - 16. - For all other blocks, we only need to keep a margin of 5 bytes so that - we don't go over the block size with a copy. */ - const size_t len_limit = BROTLI_MIN(size_t, block_size - kMinMatchLen, - input_size - kInputMarginBytes); - const uint8_t* ip_limit = input + len_limit; - - uint32_t next_hash; - for (next_hash = Hash(++ip, shift); ; ) { - /* Step 1: Scan forward in the input looking for a 5-byte-long match. - If we get close to exhausting the input then goto emit_remainder. - - Heuristic match skipping: If 32 bytes are scanned with no matches - found, start looking only at every other byte. If 32 more bytes are - scanned, look at every third byte, etc.. When a match is found, - immediately go back to looking at every byte. This is a small loss - (~5% performance, ~0.1% density) for compressible data due to more - bookkeeping, but for non-compressible data (such as JPEG) it's a huge - win since the compressor quickly "realizes" the data is incompressible - and doesn't bother looking for matches everywhere. - - The "skip" variable keeps track of how many bytes there are since the - last match; dividing it by 32 (i.e. right-shifting by five) gives the - number of bytes to move ahead for each iteration. */ - uint32_t skip = 32; - - const uint8_t* next_ip = ip; - const uint8_t* candidate; - BROTLI_DCHECK(next_emit < ip); -trawl: - do { - uint32_t hash = next_hash; - uint32_t bytes_between_hash_lookups = skip++ >> 5; - BROTLI_DCHECK(hash == Hash(next_ip, shift)); - ip = next_ip; - next_ip = ip + bytes_between_hash_lookups; - if (BROTLI_PREDICT_FALSE(next_ip > ip_limit)) { - goto emit_remainder; - } - next_hash = Hash(next_ip, shift); - candidate = ip - last_distance; - if (IsMatch(ip, candidate)) { - if (BROTLI_PREDICT_TRUE(candidate < ip)) { - table[hash] = (int)(ip - base_ip); - break; - } - } - candidate = base_ip + table[hash]; - BROTLI_DCHECK(candidate >= base_ip); - BROTLI_DCHECK(candidate < ip); - - table[hash] = (int)(ip - base_ip); - } while (BROTLI_PREDICT_TRUE(!IsMatch(ip, candidate))); - - /* Check copy distance. If candidate is not feasible, continue search. - Checking is done outside of hot loop to reduce overhead. */ - if (ip - candidate > MAX_DISTANCE) goto trawl; - - /* Step 2: Emit the found match together with the literal bytes from - "next_emit" to the bit stream, and then see if we can find a next match - immediately afterwards. Repeat until we find no match for the input - without emitting some literal bytes. */ - - { - /* We have a 5-byte match at ip, and we need to emit bytes in - [next_emit, ip). */ - const uint8_t* base = ip; - size_t matched = 5 + FindMatchLengthWithLimit( - candidate + 5, ip + 5, (size_t)(ip_end - ip) - 5); - int distance = (int)(base - candidate); /* > 0 */ - size_t insert = (size_t)(base - next_emit); - ip += matched; - BROTLI_LOG(("[CompressFragment] pos = %d insert = %lu copy = %d\n", - (int)(next_emit - base_ip), (unsigned long)insert, 2)); - BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); - if (BROTLI_PREDICT_TRUE(insert < 6210)) { - EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, - storage_ix, storage); - } else if (ShouldUseUncompressedMode(metablock_start, next_emit, insert, - literal_ratio)) { - EmitUncompressedMetaBlock(metablock_start, base, mlen_storage_ix - 3, - storage_ix, storage); - input_size -= (size_t)(base - input); - input = base; - next_emit = input; - goto next_block; - } else { - EmitLongInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, - storage_ix, storage); - } - EmitLiterals(next_emit, insert, lit_depth, lit_bits, - storage_ix, storage); - if (distance == last_distance) { - BrotliWriteBits(cmd_depth[64], cmd_bits[64], storage_ix, storage); - ++cmd_histo[64]; - } else { - EmitDistance((size_t)distance, cmd_depth, cmd_bits, - cmd_histo, storage_ix, storage); - last_distance = distance; - } - EmitCopyLenLastDistance(matched, cmd_depth, cmd_bits, cmd_histo, - storage_ix, storage); - BROTLI_LOG(("[CompressFragment] pos = %d distance = %d\n" - "[CompressFragment] pos = %d insert = %d copy = %d\n" - "[CompressFragment] pos = %d distance = %d\n", - (int)(base - base_ip), (int)distance, - (int)(base - base_ip) + 2, 0, (int)matched - 2, - (int)(base - base_ip) + 2, (int)distance)); - - next_emit = ip; - if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { - goto emit_remainder; - } - /* We could immediately start working at ip now, but to improve - compression we first update "table" with the hashes of some positions - within the last copy. */ - { - uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); - uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift); - uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift); - table[prev_hash] = (int)(ip - base_ip - 3); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift); - table[prev_hash] = (int)(ip - base_ip - 2); - prev_hash = HashBytesAtOffset(input_bytes, 2, shift); - table[prev_hash] = (int)(ip - base_ip - 1); - - candidate = base_ip + table[cur_hash]; - table[cur_hash] = (int)(ip - base_ip); - } - } - - while (IsMatch(ip, candidate)) { - /* We have a 5-byte match at ip, and no need to emit any literal bytes - prior to ip. */ - const uint8_t* base = ip; - size_t matched = 5 + FindMatchLengthWithLimit( - candidate + 5, ip + 5, (size_t)(ip_end - ip) - 5); - if (ip - candidate > MAX_DISTANCE) break; - ip += matched; - last_distance = (int)(base - candidate); /* > 0 */ - BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); - EmitCopyLen(matched, cmd_depth, cmd_bits, cmd_histo, - storage_ix, storage); - EmitDistance((size_t)last_distance, cmd_depth, cmd_bits, - cmd_histo, storage_ix, storage); - BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n" - "[CompressFragment] pos = %d distance = %d\n", - (int)(base - base_ip), 0, (int)matched, - (int)(base - base_ip), (int)last_distance)); - - next_emit = ip; - if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { - goto emit_remainder; - } - /* We could immediately start working at ip now, but to improve - compression we first update "table" with the hashes of some positions - within the last copy. */ - { - uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); - uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift); - uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift); - table[prev_hash] = (int)(ip - base_ip - 3); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift); - table[prev_hash] = (int)(ip - base_ip - 2); - prev_hash = HashBytesAtOffset(input_bytes, 2, shift); - table[prev_hash] = (int)(ip - base_ip - 1); - - candidate = base_ip + table[cur_hash]; - table[cur_hash] = (int)(ip - base_ip); - } - } - - next_hash = Hash(++ip, shift); - } - } - - emit_remainder: - BROTLI_DCHECK(next_emit <= ip_end); - input += block_size; - input_size -= block_size; - block_size = BROTLI_MIN(size_t, input_size, kMergeBlockSize); - - /* Decide if we want to continue this meta-block instead of emitting the - last insert-only command. */ - if (input_size > 0 && - total_block_size + block_size <= (1 << 20) && - ShouldMergeBlock(s, input, block_size, lit_depth)) { - BROTLI_DCHECK(total_block_size > (1 << 16)); - /* Update the size of the current meta-block and continue emitting commands. - We can do this because the current size and the new size both have 5 - nibbles. */ - total_block_size += block_size; - UpdateBits(20, (uint32_t)(total_block_size - 1), mlen_storage_ix, storage); - goto emit_commands; - } - - /* Emit the remaining bytes as literals. */ - if (next_emit < ip_end) { - const size_t insert = (size_t)(ip_end - next_emit); - BROTLI_LOG(("[CompressFragment] pos = %d insert = %lu copy = %d\n", - (int)(next_emit - base_ip), (unsigned long)insert, 2)); - if (BROTLI_PREDICT_TRUE(insert < 6210)) { - EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, - storage_ix, storage); - EmitLiterals(next_emit, insert, lit_depth, lit_bits, storage_ix, storage); - } else if (ShouldUseUncompressedMode(metablock_start, next_emit, insert, - literal_ratio)) { - EmitUncompressedMetaBlock(metablock_start, ip_end, mlen_storage_ix - 3, - storage_ix, storage); - } else { - EmitLongInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, - storage_ix, storage); - EmitLiterals(next_emit, insert, lit_depth, lit_bits, - storage_ix, storage); - } - } - next_emit = ip_end; - -next_block: - /* If we have more data, write a new meta-block header and prefix codes and - then continue emitting commands. */ - if (input_size > 0) { - metablock_start = input; - block_size = BROTLI_MIN(size_t, input_size, kFirstBlockSize); - total_block_size = block_size; - /* Save the bit position of the MLEN field of the meta-block header, so that - we can update it later if we decide to extend this meta-block. */ - mlen_storage_ix = *storage_ix + 3; - BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); - /* No block splits, no contexts. */ - BrotliWriteBits(13, 0, storage_ix, storage); - literal_ratio = BuildAndStoreLiteralPrefixCode( - s, input, block_size, lit_depth, lit_bits, storage_ix, storage); - BuildAndStoreCommandPrefixCode(s, storage_ix, storage); - goto emit_commands; - } - - if (!is_last) { - /* If this is not the last block, update the command and distance prefix - codes for the next block and store the compressed forms. */ - s->cmd_code[0] = 0; - s->cmd_code_numbits = 0; - BuildAndStoreCommandPrefixCode(s, &s->cmd_code_numbits, s->cmd_code); - } -} - -#define FOR_TABLE_BITS_(X) X(9) X(11) X(13) X(15) - -#define BAKE_METHOD_PARAM_(B) \ -static BROTLI_NOINLINE void BrotliCompressFragmentFastImpl ## B( \ - BrotliOnePassArena* s, const uint8_t* input, size_t input_size, \ - BROTLI_BOOL is_last, int* table, size_t* storage_ix, uint8_t* storage) { \ - BrotliCompressFragmentFastImpl(s, input, input_size, is_last, table, B, \ - storage_ix, storage); \ -} -FOR_TABLE_BITS_(BAKE_METHOD_PARAM_) -#undef BAKE_METHOD_PARAM_ - -void BrotliCompressFragmentFast( - BrotliOnePassArena* s, const uint8_t* input, size_t input_size, - BROTLI_BOOL is_last, int* table, size_t table_size, - size_t* storage_ix, uint8_t* storage) { - const size_t initial_storage_ix = *storage_ix; - const size_t table_bits = Log2FloorNonZero(table_size); - - if (input_size == 0) { - BROTLI_DCHECK(is_last); - BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ - BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ - *storage_ix = (*storage_ix + 7u) & ~7u; - return; - } - - switch (table_bits) { -#define CASE_(B) \ - case B: \ - BrotliCompressFragmentFastImpl ## B( \ - s, input, input_size, is_last, table, storage_ix, storage);\ - break; - FOR_TABLE_BITS_(CASE_) -#undef CASE_ - default: BROTLI_DCHECK(0); break; - } - - /* If output is larger than single uncompressed block, rewrite it. */ - if (*storage_ix - initial_storage_ix > 31 + (input_size << 3)) { - EmitUncompressedMetaBlock(input, input + input_size, initial_storage_ix, - storage_ix, storage); - } - - if (is_last) { - BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ - BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ - *storage_ix = (*storage_ix + 7u) & ~7u; - } -} - -#undef FOR_TABLE_BITS_ - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/compress_fragment.h b/src/deps/brotli/enc/compress_fragment.h deleted file mode 100644 index 9c0780f8c99f7..0000000000000 --- a/src/deps/brotli/enc/compress_fragment.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function for fast encoding of an input fragment, independently from the input - history. This function uses one-pass processing: when we find a backward - match, we immediately emit the corresponding command and literal codes to - the bit stream. */ - -#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_ -#define BROTLI_ENC_COMPRESS_FRAGMENT_H_ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "entropy_encode.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct BrotliOnePassArena { - uint8_t lit_depth[256]; - uint16_t lit_bits[256]; - - /* Command and distance prefix codes (each 64 symbols, stored back-to-back) - used for the next block. The command prefix code is over a smaller alphabet - with the following 64 symbols: - 0 - 15: insert length code 0, copy length code 0 - 15, same distance - 16 - 39: insert length code 0, copy length code 0 - 23 - 40 - 63: insert length code 0 - 23, copy length code 0 - Note that symbols 16 and 40 represent the same code in the full alphabet, - but we do not use either of them. */ - uint8_t cmd_depth[128]; - uint16_t cmd_bits[128]; - uint32_t cmd_histo[128]; - - /* The compressed form of the command and distance prefix codes for the next - block. */ - uint8_t cmd_code[512]; - size_t cmd_code_numbits; - - HuffmanTree tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1]; - uint32_t histogram[256]; - uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS]; - uint16_t tmp_bits[64]; -} BrotliOnePassArena; - -/* Compresses "input" string to the "*storage" buffer as one or more complete - meta-blocks, and updates the "*storage_ix" bit position. - - If "is_last" is 1, emits an additional empty last meta-block. - - "cmd_depth" and "cmd_bits" contain the command and distance prefix codes - (see comment in encode.h) used for the encoding of this input fragment. - If "is_last" is 0, they are updated to reflect the statistics - of this input fragment, to be used for the encoding of the next fragment. - - "*cmd_code_numbits" is the number of bits of the compressed representation - of the command and distance prefix codes, and "cmd_code" is an array of - at least "(*cmd_code_numbits + 7) >> 3" size that contains the compressed - command and distance prefix codes. If "is_last" is 0, these are also - updated to represent the updated "cmd_depth" and "cmd_bits". - - REQUIRES: "input_size" is greater than zero, or "is_last" is 1. - REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). - REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. - REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two - OUTPUT: maximal copy distance <= |input_size| - OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ -BROTLI_INTERNAL void BrotliCompressFragmentFast(BrotliOnePassArena* s, - const uint8_t* input, - size_t input_size, - BROTLI_BOOL is_last, - int* table, size_t table_size, - size_t* storage_ix, - uint8_t* storage); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_COMPRESS_FRAGMENT_H_ */ diff --git a/src/deps/brotli/enc/compress_fragment_two_pass.c b/src/deps/brotli/enc/compress_fragment_two_pass.c deleted file mode 100644 index a762679c1f8b8..0000000000000 --- a/src/deps/brotli/enc/compress_fragment_two_pass.c +++ /dev/null @@ -1,657 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function for fast encoding of an input fragment, independently from the input - history. This function uses two-pass processing: in the first pass we save - the found backward matches and literal bytes into a buffer, and in the - second pass we emit them into the bit stream using prefix codes built based - on the actual command and literal byte histograms. */ - -#include "compress_fragment_two_pass.h" - -#include /* memcmp, memcpy, memset */ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "bit_cost.h" -#include "brotli_bit_stream.h" -#include "entropy_encode.h" -#include "fast_log.h" -#include "find_match_length.h" -#include "write_bits.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define MAX_DISTANCE (long)BROTLI_MAX_BACKWARD_LIMIT(18) - -/* kHashMul32 multiplier has these properties: - * The multiplier must be odd. Otherwise we may lose the highest bit. - * No long streaks of ones or zeros. - * There is no effort to ensure that it is a prime, the oddity is enough - for this use. - * The number has been tuned heuristically against compression benchmarks. */ -static const uint32_t kHashMul32 = 0x1E35A7BD; - -static BROTLI_INLINE uint32_t Hash(const uint8_t* p, - size_t shift, size_t length) { - const uint64_t h = - (BROTLI_UNALIGNED_LOAD64LE(p) << ((8 - length) * 8)) * kHashMul32; - return (uint32_t)(h >> shift); -} - -static BROTLI_INLINE uint32_t HashBytesAtOffset(uint64_t v, size_t offset, - size_t shift, size_t length) { - BROTLI_DCHECK(offset <= 8 - length); - { - const uint64_t h = ((v >> (8 * offset)) << ((8 - length) * 8)) * kHashMul32; - return (uint32_t)(h >> shift); - } -} - -static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2, - size_t length) { - if (BrotliUnalignedRead32(p1) == BrotliUnalignedRead32(p2)) { - if (length == 4) return BROTLI_TRUE; - return TO_BROTLI_BOOL(p1[4] == p2[4] && p1[5] == p2[5]); - } - return BROTLI_FALSE; -} - -/* Builds a command and distance prefix code (each 64 symbols) into "depth" and - "bits" based on "histogram" and stores it into the bit stream. */ -static void BuildAndStoreCommandPrefixCode(BrotliTwoPassArena* s, - size_t* storage_ix, - uint8_t* storage) { - /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ - /* TODO(eustas): initialize once. */ - memset(s->tmp_depth, 0, sizeof(s->tmp_depth)); - BrotliCreateHuffmanTree(s->cmd_histo, 64, 15, s->tmp_tree, s->cmd_depth); - BrotliCreateHuffmanTree(&s->cmd_histo[64], 64, 14, s->tmp_tree, - &s->cmd_depth[64]); - /* We have to jump through a few hoops here in order to compute - the command bits because the symbols are in a different order than in - the full alphabet. This looks complicated, but having the symbols - in this order in the command bits saves a few branches in the Emit* - functions. */ - memcpy(s->tmp_depth, s->cmd_depth + 24, 24); - memcpy(s->tmp_depth + 24, s->cmd_depth, 8); - memcpy(s->tmp_depth + 32, s->cmd_depth + 48, 8); - memcpy(s->tmp_depth + 40, s->cmd_depth + 8, 8); - memcpy(s->tmp_depth + 48, s->cmd_depth + 56, 8); - memcpy(s->tmp_depth + 56, s->cmd_depth + 16, 8); - BrotliConvertBitDepthsToSymbols(s->tmp_depth, 64, s->tmp_bits); - memcpy(s->cmd_bits, s->tmp_bits + 24, 16); - memcpy(s->cmd_bits + 8, s->tmp_bits + 40, 16); - memcpy(s->cmd_bits + 16, s->tmp_bits + 56, 16); - memcpy(s->cmd_bits + 24, s->tmp_bits, 48); - memcpy(s->cmd_bits + 48, s->tmp_bits + 32, 16); - memcpy(s->cmd_bits + 56, s->tmp_bits + 48, 16); - BrotliConvertBitDepthsToSymbols(&s->cmd_depth[64], 64, &s->cmd_bits[64]); - { - /* Create the bit length array for the full command alphabet. */ - size_t i; - memset(s->tmp_depth, 0, 64); /* only 64 first values were used */ - memcpy(s->tmp_depth, s->cmd_depth + 24, 8); - memcpy(s->tmp_depth + 64, s->cmd_depth + 32, 8); - memcpy(s->tmp_depth + 128, s->cmd_depth + 40, 8); - memcpy(s->tmp_depth + 192, s->cmd_depth + 48, 8); - memcpy(s->tmp_depth + 384, s->cmd_depth + 56, 8); - for (i = 0; i < 8; ++i) { - s->tmp_depth[128 + 8 * i] = s->cmd_depth[i]; - s->tmp_depth[256 + 8 * i] = s->cmd_depth[8 + i]; - s->tmp_depth[448 + 8 * i] = s->cmd_depth[16 + i]; - } - BrotliStoreHuffmanTree(s->tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS, - s->tmp_tree, storage_ix, storage); - } - BrotliStoreHuffmanTree(&s->cmd_depth[64], 64, s->tmp_tree, storage_ix, - storage); -} - -static BROTLI_INLINE void EmitInsertLen( - uint32_t insertlen, uint32_t** commands) { - if (insertlen < 6) { - **commands = insertlen; - } else if (insertlen < 130) { - const uint32_t tail = insertlen - 2; - const uint32_t nbits = Log2FloorNonZero(tail) - 1u; - const uint32_t prefix = tail >> nbits; - const uint32_t inscode = (nbits << 1) + prefix + 2; - const uint32_t extra = tail - (prefix << nbits); - **commands = inscode | (extra << 8); - } else if (insertlen < 2114) { - const uint32_t tail = insertlen - 66; - const uint32_t nbits = Log2FloorNonZero(tail); - const uint32_t code = nbits + 10; - const uint32_t extra = tail - (1u << nbits); - **commands = code | (extra << 8); - } else if (insertlen < 6210) { - const uint32_t extra = insertlen - 2114; - **commands = 21 | (extra << 8); - } else if (insertlen < 22594) { - const uint32_t extra = insertlen - 6210; - **commands = 22 | (extra << 8); - } else { - const uint32_t extra = insertlen - 22594; - **commands = 23 | (extra << 8); - } - ++(*commands); -} - -static BROTLI_INLINE void EmitCopyLen(size_t copylen, uint32_t** commands) { - if (copylen < 10) { - **commands = (uint32_t)(copylen + 38); - } else if (copylen < 134) { - const size_t tail = copylen - 6; - const size_t nbits = Log2FloorNonZero(tail) - 1; - const size_t prefix = tail >> nbits; - const size_t code = (nbits << 1) + prefix + 44; - const size_t extra = tail - (prefix << nbits); - **commands = (uint32_t)(code | (extra << 8)); - } else if (copylen < 2118) { - const size_t tail = copylen - 70; - const size_t nbits = Log2FloorNonZero(tail); - const size_t code = nbits + 52; - const size_t extra = tail - ((size_t)1 << nbits); - **commands = (uint32_t)(code | (extra << 8)); - } else { - const size_t extra = copylen - 2118; - **commands = (uint32_t)(63 | (extra << 8)); - } - ++(*commands); -} - -static BROTLI_INLINE void EmitCopyLenLastDistance( - size_t copylen, uint32_t** commands) { - if (copylen < 12) { - **commands = (uint32_t)(copylen + 20); - ++(*commands); - } else if (copylen < 72) { - const size_t tail = copylen - 8; - const size_t nbits = Log2FloorNonZero(tail) - 1; - const size_t prefix = tail >> nbits; - const size_t code = (nbits << 1) + prefix + 28; - const size_t extra = tail - (prefix << nbits); - **commands = (uint32_t)(code | (extra << 8)); - ++(*commands); - } else if (copylen < 136) { - const size_t tail = copylen - 8; - const size_t code = (tail >> 5) + 54; - const size_t extra = tail & 31; - **commands = (uint32_t)(code | (extra << 8)); - ++(*commands); - **commands = 64; - ++(*commands); - } else if (copylen < 2120) { - const size_t tail = copylen - 72; - const size_t nbits = Log2FloorNonZero(tail); - const size_t code = nbits + 52; - const size_t extra = tail - ((size_t)1 << nbits); - **commands = (uint32_t)(code | (extra << 8)); - ++(*commands); - **commands = 64; - ++(*commands); - } else { - const size_t extra = copylen - 2120; - **commands = (uint32_t)(63 | (extra << 8)); - ++(*commands); - **commands = 64; - ++(*commands); - } -} - -static BROTLI_INLINE void EmitDistance(uint32_t distance, uint32_t** commands) { - uint32_t d = distance + 3; - uint32_t nbits = Log2FloorNonZero(d) - 1; - const uint32_t prefix = (d >> nbits) & 1; - const uint32_t offset = (2 + prefix) << nbits; - const uint32_t distcode = 2 * (nbits - 1) + prefix + 80; - uint32_t extra = d - offset; - **commands = distcode | (extra << 8); - ++(*commands); -} - -/* REQUIRES: len <= 1 << 24. */ -static void BrotliStoreMetaBlockHeader( - size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix, - uint8_t* storage) { - size_t nibbles = 6; - /* ISLAST */ - BrotliWriteBits(1, 0, storage_ix, storage); - if (len <= (1U << 16)) { - nibbles = 4; - } else if (len <= (1U << 20)) { - nibbles = 5; - } - BrotliWriteBits(2, nibbles - 4, storage_ix, storage); - BrotliWriteBits(nibbles * 4, len - 1, storage_ix, storage); - /* ISUNCOMPRESSED */ - BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage); -} - -static BROTLI_INLINE void CreateCommands(const uint8_t* input, - size_t block_size, size_t input_size, const uint8_t* base_ip, int* table, - size_t table_bits, size_t min_match, - uint8_t** literals, uint32_t** commands) { - /* "ip" is the input pointer. */ - const uint8_t* ip = input; - const size_t shift = 64u - table_bits; - const uint8_t* ip_end = input + block_size; - /* "next_emit" is a pointer to the first byte that is not covered by a - previous copy. Bytes between "next_emit" and the start of the next copy or - the end of the input will be emitted as literal bytes. */ - const uint8_t* next_emit = input; - - int last_distance = -1; - const size_t kInputMarginBytes = BROTLI_WINDOW_GAP; - - if (BROTLI_PREDICT_TRUE(block_size >= kInputMarginBytes)) { - /* For the last block, we need to keep a 16 bytes margin so that we can be - sure that all distances are at most window size - 16. - For all other blocks, we only need to keep a margin of 5 bytes so that - we don't go over the block size with a copy. */ - const size_t len_limit = BROTLI_MIN(size_t, block_size - min_match, - input_size - kInputMarginBytes); - const uint8_t* ip_limit = input + len_limit; - - uint32_t next_hash; - for (next_hash = Hash(++ip, shift, min_match); ; ) { - /* Step 1: Scan forward in the input looking for a 6-byte-long match. - If we get close to exhausting the input then goto emit_remainder. - - Heuristic match skipping: If 32 bytes are scanned with no matches - found, start looking only at every other byte. If 32 more bytes are - scanned, look at every third byte, etc.. When a match is found, - immediately go back to looking at every byte. This is a small loss - (~5% performance, ~0.1% density) for compressible data due to more - bookkeeping, but for non-compressible data (such as JPEG) it's a huge - win since the compressor quickly "realizes" the data is incompressible - and doesn't bother looking for matches everywhere. - - The "skip" variable keeps track of how many bytes there are since the - last match; dividing it by 32 (ie. right-shifting by five) gives the - number of bytes to move ahead for each iteration. */ - uint32_t skip = 32; - - const uint8_t* next_ip = ip; - const uint8_t* candidate; - - BROTLI_DCHECK(next_emit < ip); -trawl: - do { - uint32_t hash = next_hash; - uint32_t bytes_between_hash_lookups = skip++ >> 5; - ip = next_ip; - BROTLI_DCHECK(hash == Hash(ip, shift, min_match)); - next_ip = ip + bytes_between_hash_lookups; - if (BROTLI_PREDICT_FALSE(next_ip > ip_limit)) { - goto emit_remainder; - } - next_hash = Hash(next_ip, shift, min_match); - candidate = ip - last_distance; - if (IsMatch(ip, candidate, min_match)) { - if (BROTLI_PREDICT_TRUE(candidate < ip)) { - table[hash] = (int)(ip - base_ip); - break; - } - } - candidate = base_ip + table[hash]; - BROTLI_DCHECK(candidate >= base_ip); - BROTLI_DCHECK(candidate < ip); - - table[hash] = (int)(ip - base_ip); - } while (BROTLI_PREDICT_TRUE(!IsMatch(ip, candidate, min_match))); - - /* Check copy distance. If candidate is not feasible, continue search. - Checking is done outside of hot loop to reduce overhead. */ - if (ip - candidate > MAX_DISTANCE) goto trawl; - - /* Step 2: Emit the found match together with the literal bytes from - "next_emit", and then see if we can find a next match immediately - afterwards. Repeat until we find no match for the input - without emitting some literal bytes. */ - - { - /* We have a 6-byte match at ip, and we need to emit bytes in - [next_emit, ip). */ - const uint8_t* base = ip; - size_t matched = min_match + FindMatchLengthWithLimit( - candidate + min_match, ip + min_match, - (size_t)(ip_end - ip) - min_match); - int distance = (int)(base - candidate); /* > 0 */ - int insert = (int)(base - next_emit); - ip += matched; - BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); - EmitInsertLen((uint32_t)insert, commands); - BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n", - (int)(next_emit - base_ip), insert, 2)); - memcpy(*literals, next_emit, (size_t)insert); - *literals += insert; - if (distance == last_distance) { - **commands = 64; - ++(*commands); - } else { - EmitDistance((uint32_t)distance, commands); - last_distance = distance; - } - EmitCopyLenLastDistance(matched, commands); - BROTLI_LOG(("[CompressFragment] pos = %d distance = %d\n" - "[CompressFragment] pos = %d insert = %d copy = %d\n" - "[CompressFragment] pos = %d distance = %d\n", - (int)(base - base_ip), (int)distance, - (int)(base - base_ip) + 2, 0, (int)matched - 2, - (int)(base - base_ip) + 2, (int)distance)); - - next_emit = ip; - if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { - goto emit_remainder; - } - { - /* We could immediately start working at ip now, but to improve - compression we first update "table" with the hashes of some - positions within the last copy. */ - uint64_t input_bytes; - uint32_t cur_hash; - uint32_t prev_hash; - if (min_match == 4) { - input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); - cur_hash = HashBytesAtOffset(input_bytes, 3, shift, min_match); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 3); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 2); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 1); - } else { - input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 5); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 4); - prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 3); - input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2); - cur_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 2); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 1); - } - - candidate = base_ip + table[cur_hash]; - table[cur_hash] = (int)(ip - base_ip); - } - } - - while (ip - candidate <= MAX_DISTANCE && - IsMatch(ip, candidate, min_match)) { - /* We have a 6-byte match at ip, and no need to emit any - literal bytes prior to ip. */ - const uint8_t* base = ip; - size_t matched = min_match + FindMatchLengthWithLimit( - candidate + min_match, ip + min_match, - (size_t)(ip_end - ip) - min_match); - ip += matched; - last_distance = (int)(base - candidate); /* > 0 */ - BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); - EmitCopyLen(matched, commands); - EmitDistance((uint32_t)last_distance, commands); - BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n" - "[CompressFragment] pos = %d distance = %d\n", - (int)(base - base_ip), 0, (int)matched, - (int)(base - base_ip), (int)last_distance)); - - next_emit = ip; - if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { - goto emit_remainder; - } - { - /* We could immediately start working at ip now, but to improve - compression we first update "table" with the hashes of some - positions within the last copy. */ - uint64_t input_bytes; - uint32_t cur_hash; - uint32_t prev_hash; - if (min_match == 4) { - input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); - cur_hash = HashBytesAtOffset(input_bytes, 3, shift, min_match); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 3); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 2); - prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 1); - } else { - input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 5); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 4); - prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 3); - input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2); - cur_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); - prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 2); - prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); - table[prev_hash] = (int)(ip - base_ip - 1); - } - - candidate = base_ip + table[cur_hash]; - table[cur_hash] = (int)(ip - base_ip); - } - } - - next_hash = Hash(++ip, shift, min_match); - } - } - -emit_remainder: - BROTLI_DCHECK(next_emit <= ip_end); - /* Emit the remaining bytes as literals. */ - if (next_emit < ip_end) { - const uint32_t insert = (uint32_t)(ip_end - next_emit); - EmitInsertLen(insert, commands); - BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n", - (int)(next_emit - base_ip), insert, 2)); - memcpy(*literals, next_emit, insert); - *literals += insert; - } -} - -static void StoreCommands(BrotliTwoPassArena* s, - const uint8_t* literals, const size_t num_literals, - const uint32_t* commands, const size_t num_commands, - size_t* storage_ix, uint8_t* storage) { - static const uint32_t kNumExtraBits[128] = { - 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, - 6, 7, 8, 9, 10, 12, 14, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, - 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, - 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, - }; - static const uint32_t kInsertOffset[24] = { - 0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, - 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210, 22594, - }; - - size_t i; - memset(s->lit_histo, 0, sizeof(s->lit_histo)); - /* TODO(eustas): is that necessary? */ - memset(s->cmd_depth, 0, sizeof(s->cmd_depth)); - /* TODO(eustas): is that necessary? */ - memset(s->cmd_bits, 0, sizeof(s->cmd_bits)); - memset(s->cmd_histo, 0, sizeof(s->cmd_histo)); - for (i = 0; i < num_literals; ++i) { - ++s->lit_histo[literals[i]]; - } - BrotliBuildAndStoreHuffmanTreeFast(s->tmp_tree, s->lit_histo, num_literals, - /* max_bits = */ 8, s->lit_depth, - s->lit_bits, storage_ix, storage); - - for (i = 0; i < num_commands; ++i) { - const uint32_t code = commands[i] & 0xFF; - BROTLI_DCHECK(code < 128); - ++s->cmd_histo[code]; - } - s->cmd_histo[1] += 1; - s->cmd_histo[2] += 1; - s->cmd_histo[64] += 1; - s->cmd_histo[84] += 1; - BuildAndStoreCommandPrefixCode(s, storage_ix, storage); - - for (i = 0; i < num_commands; ++i) { - const uint32_t cmd = commands[i]; - const uint32_t code = cmd & 0xFF; - const uint32_t extra = cmd >> 8; - BROTLI_DCHECK(code < 128); - BrotliWriteBits(s->cmd_depth[code], s->cmd_bits[code], storage_ix, storage); - BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage); - if (code < 24) { - const uint32_t insert = kInsertOffset[code] + extra; - uint32_t j; - for (j = 0; j < insert; ++j) { - const uint8_t lit = *literals; - BrotliWriteBits(s->lit_depth[lit], s->lit_bits[lit], storage_ix, - storage); - ++literals; - } - } - } -} - -/* Acceptable loss for uncompressible speedup is 2% */ -#define MIN_RATIO 0.98 -#define SAMPLE_RATE 43 - -static BROTLI_BOOL ShouldCompress(BrotliTwoPassArena* s, - const uint8_t* input, size_t input_size, size_t num_literals) { - double corpus_size = (double)input_size; - if ((double)num_literals < MIN_RATIO * corpus_size) { - return BROTLI_TRUE; - } else { - const double max_total_bit_cost = corpus_size * 8 * MIN_RATIO / SAMPLE_RATE; - size_t i; - memset(s->lit_histo, 0, sizeof(s->lit_histo)); - for (i = 0; i < input_size; i += SAMPLE_RATE) { - ++s->lit_histo[input[i]]; - } - return TO_BROTLI_BOOL(BitsEntropy(s->lit_histo, 256) < max_total_bit_cost); - } -} - -static void RewindBitPosition(const size_t new_storage_ix, - size_t* storage_ix, uint8_t* storage) { - const size_t bitpos = new_storage_ix & 7; - const size_t mask = (1u << bitpos) - 1; - storage[new_storage_ix >> 3] &= (uint8_t)mask; - *storage_ix = new_storage_ix; -} - -static void EmitUncompressedMetaBlock(const uint8_t* input, size_t input_size, - size_t* storage_ix, uint8_t* storage) { - BrotliStoreMetaBlockHeader(input_size, 1, storage_ix, storage); - *storage_ix = (*storage_ix + 7u) & ~7u; - memcpy(&storage[*storage_ix >> 3], input, input_size); - *storage_ix += input_size << 3; - storage[*storage_ix >> 3] = 0; -} - -static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl( - BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, - BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, - int* table, size_t table_bits, size_t min_match, - size_t* storage_ix, uint8_t* storage) { - /* Save the start of the first block for position and distance computations. - */ - const uint8_t* base_ip = input; - BROTLI_UNUSED(is_last); - - while (input_size > 0) { - size_t block_size = - BROTLI_MIN(size_t, input_size, kCompressFragmentTwoPassBlockSize); - uint32_t* commands = command_buf; - uint8_t* literals = literal_buf; - size_t num_literals; - CreateCommands(input, block_size, input_size, base_ip, table, - table_bits, min_match, &literals, &commands); - num_literals = (size_t)(literals - literal_buf); - if (ShouldCompress(s, input, block_size, num_literals)) { - const size_t num_commands = (size_t)(commands - command_buf); - BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); - /* No block splits, no contexts. */ - BrotliWriteBits(13, 0, storage_ix, storage); - StoreCommands(s, literal_buf, num_literals, command_buf, num_commands, - storage_ix, storage); - } else { - /* Since we did not find many backward references and the entropy of - the data is close to 8 bits, we can simply emit an uncompressed block. - This makes compression speed of uncompressible data about 3x faster. */ - EmitUncompressedMetaBlock(input, block_size, storage_ix, storage); - } - input += block_size; - input_size -= block_size; - } -} - -#define FOR_TABLE_BITS_(X) \ - X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) - -#define BAKE_METHOD_PARAM_(B) \ -static BROTLI_NOINLINE void BrotliCompressFragmentTwoPassImpl ## B( \ - BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, \ - BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, \ - int* table, size_t* storage_ix, uint8_t* storage) { \ - size_t min_match = (B <= 15) ? 4 : 6; \ - BrotliCompressFragmentTwoPassImpl(s, input, input_size, is_last, command_buf,\ - literal_buf, table, B, min_match, storage_ix, storage); \ -} -FOR_TABLE_BITS_(BAKE_METHOD_PARAM_) -#undef BAKE_METHOD_PARAM_ - -void BrotliCompressFragmentTwoPass( - BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, - BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, - int* table, size_t table_size, size_t* storage_ix, uint8_t* storage) { - const size_t initial_storage_ix = *storage_ix; - const size_t table_bits = Log2FloorNonZero(table_size); - switch (table_bits) { -#define CASE_(B) \ - case B: \ - BrotliCompressFragmentTwoPassImpl ## B( \ - s, input, input_size, is_last, command_buf, \ - literal_buf, table, storage_ix, storage); \ - break; - FOR_TABLE_BITS_(CASE_) -#undef CASE_ - default: BROTLI_DCHECK(0); break; - } - - /* If output is larger than single uncompressed block, rewrite it. */ - if (*storage_ix - initial_storage_ix > 31 + (input_size << 3)) { - RewindBitPosition(initial_storage_ix, storage_ix, storage); - EmitUncompressedMetaBlock(input, input_size, storage_ix, storage); - } - - if (is_last) { - BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ - BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ - *storage_ix = (*storage_ix + 7u) & ~7u; - } -} - -#undef FOR_TABLE_BITS_ - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/compress_fragment_two_pass.h b/src/deps/brotli/enc/compress_fragment_two_pass.h deleted file mode 100644 index 6d28d9bb78e45..0000000000000 --- a/src/deps/brotli/enc/compress_fragment_two_pass.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function for fast encoding of an input fragment, independently from the input - history. This function uses two-pass processing: in the first pass we save - the found backward matches and literal bytes into a buffer, and in the - second pass we emit them into the bit stream using prefix codes built based - on the actual command and literal byte histograms. */ - -#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ -#define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "entropy_encode.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* TODO(eustas): turn to macro. */ -static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17; - -typedef struct BrotliTwoPassArena { - uint32_t lit_histo[256]; - uint8_t lit_depth[256]; - uint16_t lit_bits[256]; - - uint32_t cmd_histo[128]; - uint8_t cmd_depth[128]; - uint16_t cmd_bits[128]; - - /* BuildAndStoreCommandPrefixCode */ - HuffmanTree tmp_tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1]; - uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS]; - uint16_t tmp_bits[64]; -} BrotliTwoPassArena; - -/* Compresses "input" string to the "*storage" buffer as one or more complete - meta-blocks, and updates the "*storage_ix" bit position. - - If "is_last" is 1, emits an additional empty last meta-block. - - REQUIRES: "input_size" is greater than zero, or "is_last" is 1. - REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). - REQUIRES: "command_buf" and "literal_buf" point to at least - kCompressFragmentTwoPassBlockSize long arrays. - REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. - REQUIRES: "table_size" is a power of two - OUTPUT: maximal copy distance <= |input_size| - OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ -BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(BrotliTwoPassArena* s, - const uint8_t* input, - size_t input_size, - BROTLI_BOOL is_last, - uint32_t* command_buf, - uint8_t* literal_buf, - int* table, - size_t table_size, - size_t* storage_ix, - uint8_t* storage); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ */ diff --git a/src/deps/brotli/enc/dictionary_hash.c b/src/deps/brotli/enc/dictionary_hash.c deleted file mode 100644 index 1a60eb3cdaa95..0000000000000 --- a/src/deps/brotli/enc/dictionary_hash.c +++ /dev/null @@ -1,1848 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Hash table on the 4-byte prefixes of static dictionary words. */ - -#include "../common/platform.h" -#include "dictionary_hash.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* GENERATED CODE START */ -BROTLI_INTERNAL const uint16_t kStaticDictionaryHashWords[32768] = { -1002,0,0,0,0,0,0,0,0,683,0,0,0,0,0,0,0,1265,0,0,0,0,0,1431,0,0,0,0,0,0,40,0,0,0, -0,155,8,741,0,624,0,0,0,0,0,0,0,0,0,0,0,0,66,503,0,0,0,451,0,0,0,0,0,0,0,835,70, -0,0,539,0,0,0,0,0,0,0,0,0,113,0,0,0,0,718,0,0,0,0,0,0,520,0,1070,0,0,0,0,0,1515, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,610,0,0,750,0,0,0,307,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,964,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,999,0,0,0,0,0,0,0,0, -645,75,0,649,52,282,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1621,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,211,225,0,0,687,718,0,0,110,0,58,0,0,0,0,0,0,345,0,0,301,0,0, -0,203,0,0,1154,674,1949,0,0,0,0,0,0,0,0,0,259,0,0,0,0,0,0,0,1275,0,0,0,1231,254, -0,0,0,0,0,0,0,277,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,0,0,800,0,0,0,29, -116,100,490,0,0,0,0,0,1641,0,543,0,0,0,0,41,181,0,657,0,0,202,25,0,0,0,0,0,0,0, -0,0,0,423,0,0,0,113,0,0,0,927,963,0,976,0,206,0,0,0,0,0,0,0,0,0,2002,0,0,0,0,0, -0,0,0,0,0,0,696,0,1170,0,0,0,0,226,13,0,769,678,551,0,0,0,0,0,0,57,0,0,0,10,188, -0,0,0,624,0,0,0,0,0,0,0,0,0,1941,130,0,0,0,0,378,269,0,0,528,0,1146,0,0,0,1105, -0,1616,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,656,0,1940,0,0,0,0,0,173,0,0,0,0,0,0,0,0,0, -0,0,457,342,810,0,0,0,0,620,0,0,0,0,0,0,0,967,95,447,406,0,0,0,477,0,1268,944, -1941,0,0,0,629,0,0,0,0,0,375,0,0,0,1636,0,0,0,0,774,0,1,1034,0,0,0,0,0,824,0,0, -0,0,0,118,0,0,560,296,0,0,0,0,0,0,0,0,1009,894,0,0,0,0,0,0,0,0,0,0,0,0,0,1474, -366,0,0,0,0,0,0,0,0,0,79,1723,0,0,200,0,0,0,0,0,0,0,0,1759,372,0,16,0,943,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,258,0,0,900,1839,707,30,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,2004,0,0,10,115,0,50,0,0,0,0,0,0,0,0,0,0,520,1,0,738,98,482,0,0,0,0, -0,0,0,0,0,0,701,2,0,0,0,0,0,0,0,0,557,0,0,0,0,0,0,0,0,0,347,0,0,0,0,572,0,0,0,0, -0,0,0,0,0,832,0,0,797,809,0,0,0,0,0,0,0,0,0,0,0,528,0,0,0,861,0,0,294,0,0,0,109, -0,0,0,0,0,0,0,0,1187,290,266,0,0,0,0,49,50,748,0,0,466,399,0,0,0,0,0,0,0,378,0, -519,0,0,0,0,0,0,0,0,0,0,0,0,667,351,902,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,180, -0,0,869,0,0,0,0,0,0,0,260,0,0,0,0,0,0,0,0,0,0,523,36,0,0,587,510,809,29,260,0,0, -0,0,0,0,0,0,570,0,565,0,1464,0,0,0,0,0,0,10,0,0,787,399,380,200,0,0,0,0,516,0, -844,887,0,0,0,0,0,0,0,44,0,0,0,305,1655,0,0,0,0,0,0,0,0,0,0,0,0,0,0,786,10,0,0, -0,0,0,0,0,0,0,2031,0,0,0,0,0,684,0,0,0,0,0,1480,0,0,0,27,0,0,0,395,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,813,511,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,0,0,0,206, -496,0,0,0,0,0,909,0,891,0,0,0,0,0,0,0,0,0,687,0,0,0,1342,0,0,0,0,0,0,0,0,0,0, -160,41,0,0,0,0,0,0,0,0,0,0,0,1718,778,0,0,0,0,0,0,0,0,0,0,1610,0,0,0,0,0,115,0, -0,0,0,314,294,0,0,0,983,178,193,0,0,0,0,0,0,0,0,0,174,0,0,0,0,0,0,0,0,0,0,848, -1796,0,0,0,0,0,0,221,0,687,1660,0,0,0,0,262,0,0,179,0,0,0,0,0,66,0,773,0,352,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,0,152,0,0,1197,0,0,0,0,0,0,0,0,0,0,0,0,560,0,0, -564,0,0,0,797,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,556,0,819,0,0,0,0,0,0,0,0,719,544, -637,5,0,0,0,0,0,0,0,0,0,0,0,101,0,1441,0,0,0,893,0,0,0,0,0,0,0,0,0,238,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,1296,0,0,969,1729,314,60,0,0,0,0,0,1144,0,1147,0,0,0,0,0, -0,0,0,0,0,437,1853,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,828,0,176,0,0,0,0,0,0,434,39,0, -0,0,0,0,159,0,0,0,902,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,270,0,0,0,0,801,556,0,0, -0,0,0,0,0,416,19,197,369,0,0,0,0,0,0,0,0,0,28,34,0,757,0,0,898,1553,0,721,0,0,0, -0,1012,0,0,0,0,1102,0,898,183,0,0,0,0,0,0,0,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,277,0,0,0,435,0,0,0,0,0,1311,0,0,0,0, -0,0,211,437,0,0,0,28,0,0,750,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2012,0,702, -0,808,0,0,0,0,739,166,0,0,0,0,0,0,719,170,500,0,0,0,0,0,0,0,0,1500,327,0,0,450, -0,0,0,1318,0,0,0,1602,0,0,331,754,0,0,0,0,0,1368,0,0,557,0,0,0,799,850,0,0,0,0, -0,0,0,0,908,0,0,0,0,0,19,62,459,0,0,0,0,0,0,0,0,0,0,0,0,1802,0,0,0,0,0,0,0,0,0, -1397,0,0,0,0,120,238,0,0,0,0,0,0,0,0,0,0,0,1324,0,0,0,0,0,0,0,0,602,201,0,0,164, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,615,0,0,0,0,0,0,0,0,0,0,0,0,0,1243,0,0,0,0,968,0,0, -0,0,0,0,882,0,0,0,907,329,100,0,0,0,0,0,0,0,0,0,0,0,176,26,9,0,0,265,256,0,0,0, -0,0,0,0,0,0,643,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,610,0,0,0,0,973,2001,0, -0,0,0,0,0,522,0,0,0,0,0,0,0,0,0,0,0,553,0,0,0,0,0,0,1582,0,1578,0,0,0,0,0,0,0,0, -0,0,0,795,0,0,0,432,0,0,0,0,0,0,84,126,0,0,0,0,790,0,377,64,0,1529,0,0,0,0,530, -1857,539,1104,0,0,0,0,0,0,0,0,0,0,0,0,977,0,0,0,34,0,0,0,0,0,0,0,0,0,0,0,24,26, -0,0,918,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,183,379,0,0,0,0,0,0,0,792, -0,0,0,0,0,0,0,0,0,1920,0,0,0,0,0,0,0,0,0,771,0,0,0,1979,0,901,254,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,0,0,0,0,0,440,37,0, -508,0,0,0,513,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,533,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,752,920,0,1048,0,153,0, -0,391,0,0,1952,0,0,0,0,0,0,0,0,0,0,126,0,0,0,0,640,0,483,69,1616,0,0,0,0,0,734, -0,0,0,0,0,0,480,0,495,0,472,0,0,0,0,0,0,0,0,874,229,0,0,0,0,948,0,0,0,0,0,0,0,0, -1009,748,0,555,0,0,0,0,0,0,193,0,653,0,0,0,0,0,0,0,0,0,0,984,0,0,0,172,0,0,0,0, -0,0,0,0,83,1568,0,0,384,0,0,0,0,0,0,0,164,880,0,0,0,0,0,0,0,0,0,0,0,367,121,0,0, -828,0,0,0,0,0,0,0,1541,0,0,0,0,0,0,0,343,0,0,0,0,0,0,0,0,561,57,0,0,0,0,0,0,0, -926,0,0,0,0,827,0,194,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0, -0,0,0,896,1249,0,0,0,0,0,1614,0,0,0,860,0,0,0,0,0,0,0,0,964,102,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,899,0,569,0,0,0,0,795,2045,0,0,0, -0,0,0,104,52,0,0,0,0,0,604,0,0,0,0,779,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0, -494,0,677,0,0,0,0,0,0,0,508,0,0,0,0,0,0,0,0,0,1014,0,957,0,0,630,310,0,0,0,570, -0,0,449,0,64,537,0,0,0,0,0,0,0,244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,702,1650,49,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,338,0,0,0,0,1279,0,0,0,0,0,0,0,896,0,0, -178,0,0,0,0,0,0,0,0,0,0,0,0,0,808,695,0,0,0,0,539,1117,0,0,0,0,0,0,0,0,257,0, -1003,0,0,0,1,448,0,516,0,0,960,0,125,4,0,1268,30,748,0,0,852,0,0,0,6,0,0,848, -236,1385,862,1811,0,0,0,0,698,803,0,0,0,0,0,0,0,610,992,0,0,878,0,1847,0,0,0,0, -0,0,0,383,0,1404,0,0,0,0,986,0,347,0,0,0,0,0,0,0,0,0,0,0,592,572,0,1411,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,606,0,0,0,0,0,0, -0,0,0,0,0,0,0,1829,0,0,0,0,0,0,0,0,0,0,0,0,700,748,0,0,0,0,0,0,365,0,0,127,0,0, -83,198,0,0,0,0,0,0,864,55,0,0,0,0,726,1752,0,0,0,0,0,0,0,0,0,0,0,0,0,1066,0,764, -0,0,0,0,683,0,550,309,0,0,874,1212,0,0,0,1364,0,986,381,723,0,0,0,1573,0,0,0,0, -0,1025,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1559,0,0,0,0,493,133,0,0,0,0,148, -119,0,0,0,0,0,0,537,14,541,0,635,126,0,0,0,495,0,0,0,0,861,998,1009,0,0,0,0,0,0, -0,359,368,0,0,0,0,304,1577,0,0,0,0,0,1107,0,0,0,0,0,929,0,0,0,1142,0,0,0,0,289, -175,0,432,0,219,0,0,0,0,0,785,0,0,595,0,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0, -931,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1323,0,0,0,0,290,0,559,1751,127,0,0,0, -934,1167,0,963,0,260,0,0,0,573,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -580,1689,0,0,0,0,0,0,0,0,0,1164,0,0,982,1922,0,63,0,0,0,0,0,793,0,0,0,0,0,0,0,0, -0,0,0,0,0,67,790,0,0,0,0,0,0,0,0,0,0,391,443,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,271,0,0,0,0,0,0,0,0,0,0,0,1140,0,0,0,0,340,300,0,897,0,0,0,0,0,0, -0,0,0,0,890,0,0,0,0,818,321,53,0,0,0,0,0,0,0,0,0,468,0,243,0,870,0,0,0,1765,121, -0,0,0,180,518,0,822,419,634,0,0,0,0,0,0,0,0,0,898,0,0,0,0,454,36,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,806,0,0,0,0,0,0,0,0,0,0,0,0,1326,0,104,0,0,0,0,0,0,0, -0,0,260,0,0,0,0,0,0,0,0,0,0,0,0,542,45,0,0,263,1516,42,0,0,0,0,0,468,0,1005,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,288,87,0,0,0,0,0,0,0,0,502,988,133,0,0,0,0,0,0, -141,0,0,872,1842,0,0,0,0,0,0,0,0,261,619,0,0,0,0,189,246,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,678,0,0,0,0,0,0,0,0,0,0,0,0,285,35,0,517,0,0,0,0,0,0,0,0,0,0, -540,214,667,0,74,0,0,125,0,0,0,0,0,761,131,0,0,0,0,0,0,0,0,0,0,0,0,0,333,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1338,94,0,0,0,0,0,0,0,0,0,0,0,0,449,0,646,103, -86,641,2028,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,869,87,277,117,39,0,0,0,0,0,0,0,0,938, -297,0,0,0,0,558,464,0,0,0,0,0,0,0,0,0,0,731,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1608,0, -0,0,0,0,0,0,1429,0,0,733,1010,0,0,338,1656,0,0,0,1038,979,2010,0,0,0,0,0,0,0, -1005,0,0,121,0,0,0,219,20,0,0,0,0,0,0,872,1440,0,0,0,683,0,1070,0,0,522,0,0,0,0, -439,669,0,0,0,0,0,0,0,0,1245,0,0,0,0,0,1218,0,0,547,233,0,0,0,0,0,0,0,0,0,482,0, -0,0,0,0,0,0,886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,795,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,371,0,0,0,0,0,0,0,0,0,0,0,0,0,622,0,625,0,0,0,339,29,0,0,338,0,0,0, -0,130,0,0,0,0,0,0,0,0,0,307,0,0,0,0,0,0,0,0,0,0,2044,0,0,0,0,0,0,0,0,308,770,0, -0,0,0,0,1266,0,0,0,0,0,0,0,0,0,400,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,690,739,0,0, -0,0,0,0,0,990,0,0,0,1831,0,0,0,0,0,0,0,0,0,0,0,0,0,613,0,0,0,0,0,0,0,0,0,0,0,0, -0,763,0,878,0,0,0,977,0,100,0,0,0,0,0,0,0,0,0,463,0,0,0,0,623,318,0,0,296,463, -137,0,0,454,0,0,0,1527,58,0,0,0,0,0,0,0,18,48,0,0,0,0,0,729,0,0,0,442,0,0,0,0, -40,449,0,853,0,0,0,0,0,0,227,0,0,0,0,0,0,1491,0,0,0,0,0,0,0,0,0,0,161,55,0,450, -0,1174,62,0,207,0,0,0,0,0,0,0,0,869,0,0,0,0,80,213,0,0,0,0,0,0,0,0,0,0,354,820, -0,0,747,0,0,0,954,0,0,1073,0,556,0,0,0,692,0,191,0,804,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,831,162,0,0,35,0,0,0,0,0,0,0,0,1235,0,0,0,0,0,1234,0,0, -0,0,0,0,0,0,0,0,96,0,0,0,0,0,0,0,149,0,0,0,902,204,0,0,833,0,287,366,0,0,0,0,0, -0,992,2020,0,0,0,0,0,0,0,0,0,0,0,356,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,784,0,0,567, -630,0,0,0,539,0,0,27,0,0,0,0,0,0,0,0,0,0,755,0,0,0,0,0,0,0,0,0,0,0,0,814,0,0,0, -0,0,0,0,0,0,0,0,0,0,987,0,0,255,761,194,0,1086,0,0,0,0,0,0,1016,0,0,1396,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,562,271,913,0,0,0,0,0,0,0,0,320,153,45,475,0,0, -0,0,0,0,0,713,0,327,0,0,0,0,0,0,604,552,3,359,0,0,0,0,853,80,0,0,0,0,0,0,0,2016, -6,887,0,0,0,0,975,0,961,0,0,0,0,0,916,1891,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,100,101,390,708,0,0,0,587,983,512,0,0,0,0,0,0,0,0,0,0,0,645,0,0,0,851,0,0,0, -0,0,498,140,217,0,0,0,1448,0,0,0,0,0,0,0,0,0,905,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -643,105,0,792,0,0,0,0,0,0,0,0,0,0,0,0,56,0,0,0,0,0,0,0,0,0,0,535,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1748,0,0,0,0,0,754,0,0,0,0,0,0,0,0,0,0,0,0,91,0,0,1565,0,91,792, -939,3,370,0,0,0,0,95,0,0,0,0,551,7,619,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1150,0, -0,0,0,0,0,0,0,0,0,0,0,0,671,0,0,0,0,0,888,368,149,0,0,105,1134,0,983,0,0,458,31, -0,643,0,0,0,312,0,740,0,0,0,1642,0,0,0,0,0,0,0,236,0,0,0,0,0,0,0,59,68,0,0,0,0, -0,867,795,0,0,0,0,970,1977,0,0,0,0,0,0,0,1148,0,775,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,970,0,0,0,0,0,0,0,0,0,665,71,0,0,0,0,827,0,0,0,0,0,0,0,0,0, -0,479,0,0,0,0,0,0,0,0,99,607,0,0,0,0,0,0,0,1960,0,0,0,793,0,0,871,41,0,0,241,94, -0,0,0,0,209,0,0,1497,0,0,0,0,0,0,0,0,0,98,0,0,0,463,0,0,0,0,291,0,0,0,0,0,0,0,0, -0,0,984,0,0,0,0,0,205,0,0,0,0,0,0,205,42,0,801,0,0,0,0,0,635,0,0,533,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,371,0,1282,0,0,0,825,0,0,0,0,0,0,0,0,0,357,879,467,0,317,0,0, -0,0,0,0,0,924,0,0,0,0,849,1795,0,0,0,0,895,1799,43,0,0,0,0,0,0,0,0,0,0,1820,0,0, -0,0,0,0,0,525,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,0,493,0,174,417,0,0, -0,0,0,583,733,0,0,0,0,0,0,481,215,0,0,0,0,477,0,0,0,0,0,0,0,0,308,0,0,0,0,0,0,0, -0,297,126,0,0,361,1551,0,0,0,0,0,0,871,1807,0,0,0,0,0,1307,0,685,0,0,0,0,0,0,0, -797,0,858,0,565,0,0,0,0,0,0,0,0,0,0,0,0,434,252,826,0,0,0,0,0,0,791,0,0,0,0,509, -231,178,601,0,0,0,0,0,0,0,0,43,1591,0,0,0,0,0,1683,0,0,0,0,45,0,0,0,0,0,0,0,0,0, -0,1120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,556,494,0,398,0,0,0,1030,0,0,0,0,0,0, -168,0,0,0,0,0,0,0,0,0,0,973,0,642,0,0,0,0,0,0,0,0,0,1615,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,378,594,0,1093,0,679,112,0,0,0,0,1492,540,1374,714, -1486,0,0,0,0,825,1511,0,0,0,0,0,0,0,0,0,0,0,0,0,952,0,0,736,143,0,700,0,1540,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1557,0,0,0,860,990,0,0,0,807,0,0,0,0,0,131, -515,0,646,0,0,0,0,117,728,508,121,0,0,0,0,0,0,357,0,0,0,0,0,0,237,0,0,0,0,0,0,0, -0,0,1784,0,0,0,0,0,0,0,0,0,0,0,713,348,1536,0,738,0,0,0,0,0,0,0,434,0,0,0,0,0,0, -366,1877,39,0,0,0,0,0,0,580,0,0,0,0,0,0,0,0,0,0,0,0,0,0,873,0,0,0,0,171,0,625, -550,107,343,943,0,0,0,0,0,0,0,768,0,0,0,0,0,0,0,799,0,0,0,894,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1673,0,0,0,0,0,0,0,0,0,0,0,1052,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -272,0,441,0,0,3,9,0,0,0,1182,0,1346,0,0,0,0,0,0,0,0,682,0,0,1004,24,0,0,968,0,0, -0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,0,0,0,578, -474,0,0,0,0,0,0,0,0,0,0,0,0,0,0,113,530,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,556,0,0,0,0,0,0,16,1317,0,0,97,0,0,0,703,0,0,0,0,0,0,0,0,892,0,0,0,1571,0,0, -426,186,0,1101,0,0,0,0,0,0,0,0,937,585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,644,291, -0,0,0,0,749,0,162,0,0,381,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,762,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,628,21,0,0,0,0,0,0,0,0,919,0,0,0,0,0,0,0,0,0, -633,0,0,0,0,332,0,0,0,0,0,0,0,0,0,1489,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,832,398,0,645,0,0,0,13,0,0,0,0,0,0,0,0,0,0,20,0,800,0,0,0,0,0,0,0,0,0, -0,0,0,0,1993,0,0,0,0,769,0,0,0,665,0,0,0,0,0,0,0,0,0,0,1426,0,0,0,0,60,0,0,0, -641,1874,0,644,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1757,0,0,0,0,0,937,0,1652,0,654,0, -0,0,0,0,0,0,527,0,0,0,0,0,0,0,0,0,0,0,0,0,226,0,0,0,0,0,1486,0,0,0,0,0,0,0,0,0, -0,0,325,0,0,0,0,0,0,0,1345,0,0,91,0,404,0,0,0,0,0,0,0,0,0,0,0,0,973,0,0,0,0,0,0, -0,1176,0,549,0,0,0,0,0,0,0,0,0,0,976,0,0,0,0,0,21,0,0,0,0,0,51,0,0,0,0,314,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,198,6,0,1093,0,0,0,0,0,0,0,0,0, -0,0,0,0,1776,0,0,0,0,0,1528,0,419,0,0,0,0,0,0,0,0,76,138,0,0,0,0,638,29,0,0,0,0, -0,0,0,1418,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1710,0,0,0,0,0, -0,0,0,0,0,0,0,532,23,0,0,0,0,0,0,0,862,0,0,946,592,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,70,0,0,0,0,0,0,0,0,0,812,0,0,0,76,0,0,988,0,442,0,0,0,896,0,0,0,0,0,0, -483,0,0,0,0,1709,0,0,0,0,0,0,119,0,0,0,117,0,309,0,0,0,0,0,596,976,0,0,0,0,0,0, -0,0,0,0,0,768,0,0,0,0,0,0,0,0,0,518,0,0,0,0,0,0,0,0,0,0,0,0,0,0,863,0,0,0,24, -145,1020,0,0,1984,0,0,0,0,0,0,0,658,0,0,0,0,0,0,0,0,0,0,106,1827,0,1010,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,582,87,0,0,0,0,0,0,0,267,0,0,0,703,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,496,0,0,0,0,1121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,561,0,0,0,0,0, -0,0,760,0,0,154,0,0,0,255,0,419,323,0,0,0,0,0,368,0,0,0,0,0,0,0,0,0,0,522,0,0,0, -0,0,0,0,551,562,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,0,0,0,0, -0,0,0,284,525,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,958,0,0,594,0,0,0,0,0,0,6,479,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,61,0,0,0,0,0,0,0,820,1641,0,1556,0,0,0,0,0,0,0,302,0,0, -0,0,0,148,0,0,676,0,0,0,0,0,0,1674,0,0,0,0,0,0,178,0,0,0,0,0,0,0,94,389,0,0,0,0, -91,8,0,0,0,0,0,0,0,0,0,0,112,0,0,0,0,0,0,0,0,0,0,747,0,0,0,0,0,0,0,1746,0,0,0,0, -0,24,0,1352,158,1530,0,0,718,130,280,1401,0,0,0,0,0,1946,8,0,0,0,0,1607,0,0,0,0, -0,0,882,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,417,0,0,0,1597,633,433,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,234,0,0,0,0,0,0,0,0,680,1950,0,0,0,0,249,5,0,0,0, -0,0,0,0,0,0,1216,0,1773,0,0,0,0,0,0,0,0,0,0,0,0,0,0,509,180,0,0,0,0,0,0,0,1002, -0,0,0,0,0,0,0,0,0,0,0,0,0,931,0,0,0,0,0,0,0,0,747,943,0,1837,0,0,0,0,0,0,0,641, -0,0,0,0,280,0,0,0,5,0,0,0,0,0,72,545,0,0,0,0,0,0,0,0,0,742,0,0,254,151,872,0,0, -0,0,0,0,0,0,0,0,0,0,921,0,0,517,833,0,1680,0,0,436,251,584,0,0,0,0,0,0,0,0,0,0, -0,24,500,0,0,0,0,0,0,0,0,195,1775,514,389,0,0,0,0,0,0,0,743,0,0,0,0,0,0,292,0,0, -0,227,1283,774,1805,0,0,0,0,0,0,0,0,0,0,119,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,913, -1910,0,0,0,1826,490,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1162,700,30, -0,0,0,721,839,0,0,0,617,0,0,0,0,0,0,0,0,0,169,428,0,0,0,0,0,1648,637,1205,0,0,0, -1596,0,0,4,266,0,0,0,0,0,0,0,0,0,0,0,862,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0, -0,279,157,391,604,0,0,713,945,877,973,0,0,0,0,0,0,0,0,0,0,0,0,0,0,859,567,628, -1846,0,0,0,0,0,0,0,0,0,762,0,0,191,0,0,0,0,298,0,0,767,909,0,0,0,0,0,0,0,795,0, -0,301,0,0,1970,0,0,0,0,0,0,0,0,0,1236,0,0,0,0,0,0,644,369,15,0,160,71,0,0,0,0,0, -1447,0,0,0,0,0,0,0,0,735,1255,76,0,0,0,0,0,0,0,0,0,0,474,0,0,0,0,0,0,0,0,0,0, -841,0,0,0,0,0,0,0,0,0,0,836,0,0,0,0,0,1622,0,0,735,0,0,0,0,1601,804,1390,394,0, -0,0,0,0,0,96,0,289,0,0,35,688,0,0,0,667,0,513,0,0,0,0,0,0,0,2034,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,704,0,1524,0,1078,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,306, -0,0,0,0,0,0,0,431,0,1196,0,0,54,0,15,1448,0,1418,0,0,0,0,0,0,0,0,0,907,0,0,0,0, -0,0,194,1767,0,0,0,0,0,840,0,900,0,0,0,0,0,0,0,0,0,0,0,1436,0,0,0,0,642,1560,0, -0,0,0,0,0,94,386,0,0,0,0,0,0,0,0,0,0,830,416,0,0,20,731,0,0,0,0,0,0,0,0,697,0,0, -662,0,0,0,0,0,0,0,0,0,861,0,0,0,0,0,0,0,871,671,864,0,928,7,0,332,0,0,0,0,1055, -0,0,0,0,0,0,986,0,0,0,0,0,44,76,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,175,518, -831,1108,0,0,0,836,0,1852,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,843,1804,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,246,0,0,0,610,202,0,0,36,0,0,0,240,654,13,0,0,0,0,0,0,0, -0,391,0,403,0,0,0,0,0,0,0,0,0,0,75,0,366,815,0,0,631,0,0,0,0,0,0,0,0,345,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,952,0,0,0,0,0,0,0,0,0,0,0,673,35,662,0,287,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,5,34,0,0,0,0,0,0,0,0,151,0,427,0,0,382,0,0,0,329,0,0,279,0,0,0, -0,0,0,0,0,0,0,906,0,0,366,843,0,1443,0,1372,992,0,36,123,0,649,0,0,0,0,0,767,0, -1018,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,995,0,0,0,0,0,0,0,72,368,0,0,1345,0,0,0, -589,0,0,0,0,0,0,0,0,0,1988,0,0,220,541,0,0,0,686,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,32,196,0,0,0,0,0,0,0,0,0,0,0,0,0,381,0,0,0,0,0,0,0,0,0,1452,0, -0,0,616,0,0,0,0,0,0,0,0,0,1229,0,0,0,0,0,0,0,0,0,0,667,120,0,0,0,0,0,0,0,1146,0, -0,0,0,0,0,0,0,0,0,0,352,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0,0,935,0,1050,0, -147,88,0,0,923,0,0,0,0,0,934,0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,341,222,0,0,0,0,0,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0, -637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1159,0,0,0,847,0,0,0,0,0,0,683,0,867,944,0,0, -0,0,0,1809,0,0,0,0,0,0,0,0,0,0,395,170,0,0,0,0,0,0,0,0,0,0,618,535,0,1625,0,0,0, -0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,778,0,0,0,0,0,46,0,2032,0,0,37, -1458,0,938,363,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,314,0,0,0,0,0,0,889,0,0,0,0,0,0,0, -0,0,0,0,462,0,0,0,0,525,0,0,23,0,0,0,0,0,0,0,0,0,0,0,676,0,0,0,0,0,0,0,0,0,0,0, -0,498,725,0,0,0,0,7,0,0,0,0,773,0,0,0,164,0,0,0,0,0,0,0,0,936,583,659,1462,0, -220,0,0,0,0,803,0,0,544,119,0,0,0,0,0,0,0,0,0,0,0,181,176,0,1192,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,1878,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,0,0,0, -944,0,0,0,0,0,0,0,273,0,0,0,0,0,855,0,0,0,0,5,127,0,0,0,0,0,0,0,0,752,230,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,162,0,654,48,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197, -0,0,0,0,0,0,0,963,0,0,0,0,0,0,0,0,0,0,858,0,0,0,0,0,0,0,0,0,0,676,1978,0,0,102, -972,0,0,0,0,0,0,0,361,0,461,0,0,0,472,0,0,0,0,0,0,0,0,0,0,0,0,0,0,747,905,0,0,0, -155,0,0,0,0,0,0,0,0,0,0,319,163,0,0,0,0,0,0,0,0,0,848,0,0,36,631,0,0,0,0,0,1769, -0,0,0,0,0,144,0,0,0,0,0,0,0,0,0,0,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,555,247,0,0, -996,0,0,189,0,0,0,0,0,0,0,0,0,0,280,0,0,0,0,0,0,0,0,0,0,0,526,746,0,0,345,0,0,0, -1017,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,651,428,0,0,0,1162,230,327,546,792,0,0,0, -1203,0,0,0,0,0,0,0,0,0,672,189,0,0,0,0,0,0,99,0,0,0,298,0,0,0,0,0,0,555,397,0,0, -0,0,0,1157,0,0,0,0,0,0,0,0,0,0,398,1523,0,366,0,0,787,0,0,0,282,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,157,0,941,0,0,0,0,0,1336,0,0,116,0,0,0,0,0,0,787,0,0,0,0,0,0,0,0,0, -0,170,160,0,1815,0,0,0,0,0,866,0,0,0,0,0,0,0,0,0,689,0,0,0,0,820,0,498,108,0,0, -0,1119,0,0,0,244,609,1005,0,581,0,0,0,0,0,895,0,0,0,1898,0,0,0,0,0,926,0,0,0,0, -0,0,0,0,0,0,0,0,0,538,496,294,301,0,0,0,18,0,0,757,0,0,0,0,0,1263,0,820,0,722,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2028,0,0,0,0,124,1875,0,0,0,881,0,0,0,1348, -0,0,0,0,0,0,0,911,0,954,0,0,0,0,414,0,0,0,0,517,0,0,0,0,0,816,0,0,0,0,0,0,0,0, -713,0,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,593,150,0,0,0,0, -0,553,0,0,0,0,0,0,0,0,0,0,108,0,0,0,0,420,0,0,0,0,0,0,0,0,0,0,0,1777,0,0,55,493, -0,0,81,0,321,980,0,0,0,0,0,0,0,0,0,0,0,0,0,0,362,112,0,74,0,0,0,0,0,0,0,625,0,0, -0,0,0,0,377,16,0,0,61,281,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,1031,0,0,0,0,0,0,51,0, -0,0,0,0,0,0,211,309,15,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,789,173,0,439,9,648, -0,0,294,0,0,0,0,0,0,0,374,8,0,1099,0,0,0,0,0,0,0,575,0,0,0,518,0,0,0,702,0,0,0, -0,0,0,87,0,0,0,438,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,464,122,0,0,0,1802,0,0,0,0, -0,0,499,0,0,0,87,476,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,840,283,0,0,0,0,1620,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,609,1160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600, -323,372,0,0,0,0,471,722,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0, -477,1304,0,1774,0,0,88,0,438,12,0,0,0,0,0,0,0,0,671,997,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,639,22,0,0,782,681,0,0,0,0,0,0,0,0,0,0,1013,664,0,942,0,1349,0,0,0,0,0,0,0, -0,0,0,0,0,356,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,215,289,0,1975, -109,450,0,0,0,0,0,0,0,0,0,0,705,0,0,664,0,0,0,0,0,0,0,1238,0,0,318,0,0,0,0,0,0, -0,0,0,0,0,0,0,960,1872,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,0,0,0,0,0,0,0,0,0,239, -777,0,26,0,0,0,0,0,0,0,0,0,0,0,0,375,414,0,17,0,0,0,1350,0,955,0,0,0,0,0,0,0,0, -887,960,0,0,0,0,0,0,0,0,0,0,708,710,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,919,0,0,0, -0,502,280,7,45,0,0,0,0,777,0,0,0,0,410,0,1110,0,0,0,0,0,0,414,341,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,787,0,0,0,436,0,0,0,0,0,0,0,1707,613,377,96,0,0,0,0,451, -0,0,0,0,0,0,0,0,0,0,0,0,0,680,0,483,916,0,0,0,0,0,0,937,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,739,0,0,0,0,0,0,0,0,82,0,0,663,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,128,0,0,0,0,0,0,0,0,1087,0,0,0,0,0,0,0,503,0,0,0,0,0,0,9,113,104,324,0,460,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,935,702,434,485,1014,949,423,0,900, -0,0,0,0,0,0,0,2018,574,0,0,0,0,0,0,0,0,0,0,0,0,1206,0,0,0,0,0,0,0,0,38,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1022,0,0,0,0,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2029,0,0,0,0,0,0,0,0,0,0,0,0,523,0,0,0,0,0,0,625,0,0,425,37,0,0,0,1943,0,0,0, -0,0,765,0,0,0,0,0,0,0,0,0,0,551,0,0,0,0,0,0,0,0,0,0,0,0,168,0,0,1010,0,0,1994,0, -0,0,91,0,0,0,0,532,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1884,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,240,15,0,0,0,1227,0,1534,0,0,0,0,0,0,0,0,0,0,0,0,0,0,392,0, -0,0,0,0,0,0,0,0,0,0,0,655,562,395,0,0,0,501,1019,0,0,0,0,509,267,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1099,0,0,0,0,0,0,948,0,0,0,0,0,0,0, -462,114,0,0,258,404,0,1717,0,0,0,0,82,1061,0,724,0,0,0,0,0,1133,0,0,0,0,0,0, -1021,841,0,1021,0,0,0,0,0,0,0,0,0,0,488,373,37,0,0,0,0,564,0,0,0,0,0,513,0,0,0, -825,0,0,899,0,0,778,0,0,12,1417,0,1116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,545,0,5, -0,0,0,0,0,0,0,192,0,0,763,0,0,0,0,0,0,0,755,759,0,0,0,0,0,0,0,0,0,370,0,1237,0, -0,0,0,0,0,298,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0, -0,0,0,0,814,991,0,757,57,0,0,0,0,0,0,0,0,0,540,0,0,0,0,608,0,0,0,0,0,0,0,0,1014, -0,0,0,902,0,0,0,0,553,1668,0,0,0,0,0,0,0,0,0,559,60,0,0,0,0,0,511,0,0,675,0,0, -156,0,0,0,0,0,0,709,0,698,0,0,0,1745,0,0,0,0,0,0,0,0,0,714,0,0,0,0,0,0,0,0,206, -8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,776,0,0,0,0,0,0,0,0,0,1272,0,0, -0,0,0,1059,0,0,0,0,0,0,406,0,0,0,0,0,0,0,0,0,0,947,0,0,0,0,0,0,168,0,0,0,0,0,0, -870,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,554,0,0,0,0,784,908,0,0,0,0,0,0, -0,396,358,0,0,0,0,0,0,0,0,2,228,0,0,0,0,0,0,0,0,0,0,0,845,14,0,716,1820,594,0, -81,1428,0,161,0,782,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,998,0, -0,0,0,0,0,0,0,0,0,0,0,1043,0,1496,0,0,0,0,0,0,0,0,781,0,0,0,0,0,0,0,817,1114,0, -1814,958,0,0,0,0,812,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,236,643,0,0,0,0,0,0,0,0,0,1172,0,0,0,0,0,0,0,0,0,1338,0,0,0, -0,0,0,0,0,0,0,0,54,0,0,0,256,0,0,351,0,955,1885,0,469,0,0,0,1270,0,744,0,313,0, -0,0,0,0,0,0,0,402,969,0,0,0,0,0,0,50,0,0,0,0,572,0,0,0,0,847,0,0,0,0,0,0,0,248, -43,0,369,0,0,0,0,0,0,0,0,0,0,0,0,0,766,0,363,0,0,0,0,0,0,0,0,0,0,0,678,0,0,409, -258,82,249,0,0,0,0,0,0,0,0,0,0,0,0,32,393,0,788,0,0,0,1281,509,1968,0,0,0,0,39, -291,0,0,0,589,0,0,54,1059,0,0,0,0,0,0,824,0,0,0,0,0,0,0,0,0,0,1005,0,1598,0,0,0, -0,0,919,0,0,0,0,0,0,0,0,52,132,0,0,0,0,0,328,0,0,0,0,173,0,0,0,0,0,65,1411,0,0, -0,0,0,0,0,0,0,0,442,0,842,0,0,0,0,0,0,0,0,0,534,0,0,0,0,0,0,0,0,0,0,0,0,0,845, -210,0,0,0,0,0,0,0,0,892,0,0,223,0,0,0,0,529,0,0,0,807,0,137,218,0,1444,0,0,0,0, -0,332,661,0,0,0,0,0,0,0,76,1517,0,0,0,0,0,0,0,0,0,0,0,418,0,0,0,0,0,0,0,0,481, -379,0,0,0,0,0,149,18,0,0,0,0,0,0,0,0,742,304,142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,799,925,195,51,0,0,0,0,688,0,0,0,0,697,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1169,751,0,0,0,452,929,0,221,0,1437,0,0,0,0,955,1251,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,132,0,0,0,0,0,865,0,0,0,0,0,0,0,767, -672,42,0,0,0,1050,0,0,0,0,0,0,0,0,368,44,0,0,0,0,0,0,0,570,29,0,0,0,0,0,0,227,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,522,0,0,0,0,0,0,0,1529,0,0,0,0,0,0,739,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1667,0,0,0,0,0,0,132,511,0,138,208,1020,0,0,23,565,0,344,0,0,0, -0,0,922,0,0,0,0,0,0,0,240,0,0,415,171,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,402,0,0,754,31,716,0,982,731,0,0,0,0,0,0,0,888,0,0,0,803,847,0,0,823, -0,0,0,0,0,0,785,0,0,2,0,0,0,0,0,0,0,532,0,0,681,0,0,314,0,384,684,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,649,447,0,1818,1007,0,321,0,66,360,0,0,0,385,0,0,0,0,0,0, -0,900,73,254,0,0,0,0,683,1959,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,86,0,0,725,0,0,0,0,0,196,0,0,0,0,0,831,0,0,0,0,723,0,0,0,0,0,994,627,0,0, -0,0,0,0,0,0,0,0,764,66,0,0,0,0,205,36,0,0,0,0,0,0,0,950,0,0,0,887,111,0,0,831, -388,165,0,0,0,0,0,155,0,0,0,0,0,0,0,0,0,0,0,0,0,0,780,755,0,0,0,0,898,146,0,0,0, -0,0,0,0,45,7,0,0,0,0,0,0,0,0,607,0,0,0,0,0,0,65,0,0,0,0,0,0,0,0,0,88,0,0,0,0,0, -621,600,0,367,0,0,0,0,0,0,0,561,0,559,0,585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -287,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,672,157,0,0,0,0,714,0,0,0, -0,0,456,0,925,0,0,0,0,0,0,0,0,19,0,0,0,0,1473,0,0,0,0,0,0,0,0,0,0,113,0,0,0,0,0, -0,0,0,0,0,0,0,0,69,463,0,0,82,193,2,471,0,0,0,0,633,0,0,0,0,0,0,1148,129,1392, -542,803,0,0,0,0,0,0,0,0,0,0,0,0,438,0,0,0,0,0,0,875,0,0,0,0,0,237,0,0,0,0,0,0,0, -65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,563,0,0,0,9,444,0,0,43,1260,0,0,0,0,0,0, -971,0,0,699,0,0,0,0,0,1116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,829,242,0, -0,593,0,0,0,0,0,0,0,0,201,36,224,0,0,0,0,0,0,1430,0,1806,0,523,0,0,212,1889,0,0, -0,827,0,0,0,0,0,2043,136,242,0,0,0,0,0,0,284,148,10,0,0,0,0,0,0,1249,0,0,0,807, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,0,0,0,494,0,0,0,0,0,0,0,0,1510,0,0,0,0,0, -0,0,0,0,0,505,1306,0,0,764,268,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,384,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1703,0,0,0,0,159,964,583,0,0,0, -0,0,0,515,0,0,854,0,0,0,0,0,0,0,0,0,0,0,0,1123,0,0,0,0,0,0,0,136,0,0,0,0,0,1782, -0,0,44,1287,0,0,0,0,0,732,0,0,0,0,313,679,0,0,316,0,0,0,0,595,0,0,0,0,0,0,753, -147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,137,0,0,0,0,414,0,1762,0,0,0,0,0,0,0,0, -0,0,0,599,0,0,0,0,0,0,0,0,0,1749,0,0,0,1627,0,488,0,0,0,0,0,83,0,0,0,0,676,0,0, -1639,0,0,0,0,0,0,0,0,0,278,0,0,0,0,0,0,97,0,14,1085,0,0,0,0,0,0,781,388,0,849, -59,229,0,0,0,0,0,1115,0,0,0,0,108,0,0,0,0,700,0,0,0,0,0,0,0,0,0,1414,0,0,0,0,0, -0,0,0,0,0,0,0,0,660,737,1035,0,0,0,0,0,0,521,690,0,0,0,0,0,0,0,0,0,0,0,0,272,0, -0,0,0,0,0,0,0,0,0,1744,0,0,0,0,0,0,128,733,0,0,277,0,0,0,0,0,0,0,0,0,4,0,0,0,0, -0,0,0,0,0,0,0,0,0,936,1981,40,0,0,0,0,0,0,0,0,775,0,0,0,0,0,0,0,0,0,306,0,0,0,0, -0,0,0,979,0,0,0,0,0,611,0,0,0,0,0,178,0,0,0,1969,0,0,0,0,0,0,0,664,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,390,0,0,0,1510,0,0,0,0,0,0,0,0,0,0,0,493,0,0,37,0,0,0,0,724,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,1537,0,0,168,473,0,0,0,105,0,0,0,0, -627,438,0,0,0,0,0,0,0,0,0,0,11,1256,0,0,0,1626,0,779,0,0,0,0,25,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,308,0,0,0,0,0,741,0,671,0,0,0,0,649,150,0,0,99,521,0,0,3,339,0,0,0, -543,0,0,0,0,0,0,0,0,0,1358,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,234,155, -0,0,0,0,0,0,0,1628,0,766,0,0,0,0,0,0,0,0,0,0,0,0,0,829,0,0,0,1445,0,0,0,486,0,0, -0,0,2,1635,0,0,0,0,558,0,0,0,0,0,0,0,0,0,0,1461,0,0,0,0,0,599,0,0,0,0,0,0,0,0,0, -1376,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,0,0,0,0,0,0,447,0,0,66,1432,0,0,0,0, -0,0,307,0,413,609,0,0,0,930,0,0,0,0,21,939,0,0,0,0,0,962,4,651,0,0,0,0,15,579,0, -0,0,0,0,597,0,0,0,0,0,981,0,0,0,545,0,0,0,0,0,0,0,1558,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,800,17,0,0,17,0,907,0,0,0,110,0,0,0,53,458,0,1983,0,0,0,0,0,0,0,0,0,0,443,0, -0,0,0,0,0,0,0,0,0,0,924,1844,0,1232,0,0,0,0,70,519,0,993,0,0,0,0,0,0,14,530,0, -907,0,0,0,0,0,733,0,0,0,0,0,0,0,0,55,0,188,531,56,0,0,1693,0,0,0,0,0,0,0,0,441, -0,192,928,0,0,0,0,0,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1525,0,259,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,512,185,0,464,1603,0,0,0,0,0,0,0,0,0,0,0,1113, -284,720,0,0,722,0,0,0,0,0,13,0,0,0,0,0,0,0,4,289,43,0,0,0,0,0,0,1694,0,0,0,0, -193,0,0,0,0,409,0,0,0,0,0,0,0,0,0,0,0,0,308,0,0,1863,0,0,0,0,0,0,0,0,0,790,0,0, -745,1002,0,0,0,0,0,0,0,0,0,289,68,477,13,0,0,0,0,0,0,0,0,0,0,609,0,0,0,0,0,0,0, -0,0,0,0,367,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,528,0,0,0,0,0,0,0,0,0,694,58, -548,0,0,0,0,0,0,687,0,0,0,0,1749,0,0,0,0,0,0,0,0,1004,661,0,0,0,0,0,0,445,0,0,0, -74,0,0,0,0,213,0,0,0,0,0,0,0,0,0,0,0,0,0,834,0,0,189,1672,0,0,0,0,0,0,0,1548, -192,0,0,0,0,0,0,0,0,0,0,0,0,0,32,751,0,78,0,0,0,0,0,0,544,1602,105,473,0,0,0,0, -0,0,156,1949,0,1779,0,0,0,0,0,0,0,0,0,0,0,763,0,0,0,0,0,0,0,0,29,0,0,0,0,0,0,0, -0,0,0,883,0,0,0,0,0,0,0,488,0,617,0,0,50,0,694,1518,785,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,546,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,1016,0,0,0,577,0,0,0,0,0,0, -184,935,114,720,0,0,100,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,95,14,0,969,0,0,0,0,0,0,0, -727,0,1021,0,0,0,0,0,1190,0,0,0,0,0,0,0,0,0,0,0,0,0,153,0,0,0,0,0,0,0,0,0,798,0, -587,0,0,695,42,0,1929,141,957,0,465,7,908,0,0,450,148,0,0,0,1166,0,0,0,0,0,0,0, -0,0,0,0,0,253,0,1003,0,0,0,0,0,0,0,0,0,0,0,46,0,0,879,0,806,0,1868,0,0,0,0,0, -1846,0,0,0,730,0,0,0,0,0,0,0,965,0,0,0,0,506,0,0,0,10,0,0,0,22,0,0,0,0,0,0,0,0, -0,0,0,0,0,960,296,0,0,0,0,0,0,0,0,0,0,0,587,0,0,0,0,20,0,0,0,32,982,0,0,0,0,0,0, -0,0,0,0,941,0,0,0,0,435,0,0,0,0,0,0,71,419,0,0,0,0,0,0,688,740,94,345,0,0,679, -582,0,0,0,0,0,0,0,945,0,0,0,0,0,0,0,0,0,0,0,0,539,0,684,1993,0,0,0,659,0,583,0, -803,0,704,0,0,0,0,0,198,181,347,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,481,405,203,0,0,99,826,0,0,0,0,0,0,0,492,0,408,0,0,0,0,0,0,0,0,0,0,4,0,0, -0,0,665,349,137,0,0,0,0,612,1270,0,0,0,0,0,371,0,0,0,826,0,0,0,0,21,1535,858, -374,0,0,0,0,0,0,311,0,0,0,991,1968,0,0,0,0,494,1647,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,769,0,0,0,0,0,642,0,0,157,123,0,0,0,1435,0,0,0,0,0,0,0,0,0,0,79,0,0,0, -0,0,0,1425,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,393,486,1690,0,0,0,0, -0,0,0,0,0,0,0,0,756,184,0,0,0,1382,0,0,0,175,0,1493,0,1007,0,0,0,0,0,0,0,0,0,0, -0,219,0,0,0,0,515,99,0,851,0,0,0,0,0,1278,0,0,0,0,0,0,0,1000,982,0,762,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,910,1819,0,0,0,0,0,0,906,0,0,0,0,0,0,0,0,0,0,1730,0,0, -0,0,0,0,0,0,0,0,0,1185,0,0,0,0,0,0,0,0,40,0,0,0,147,0,0,0,0,0,0,0,0,0,0,0,0,0, -650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,30,0,553,0,0,20,597,0,1614,0,0,0,0,0,327, -49,0,0,0,0,0,0,0,78,0,0,786,134,0,0,0,12,496,0,0,0,0,0,0,0,0,0,0,42,204,0,614,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,247,0,0,0,0,942,0,0,2023,0,0,0,0, -0,0,67,285,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1309,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,532,0,0,0,0,0,0,0, -1692,0,0,0,0,55,1704,0,0,0,0,988,0,0,0,223,0,0,0,0,0,0,0,57,1123,0,0,0,0,0,1764, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2015,0,0,0,1599,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,534,0,0,0,0,0,0,0,0,0,0,0, -0,0,504,621,1248,321,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1397,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,441,75,0,0,0,0,0,0,0,0,0,0,841,0,0,0,0,0,693,0,650,314,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,913,0,0,0,0,0,0,0,0,0,0,0,0,0,0,880,0,475,0, -0,1016,179,602,111,329,0,0,0,1864,0,0,0,0,846,1888,0,0,780,0,0,0,82,0,0,0,0,821, -0,0,0,0,0,0,0,0,0,0,0,956,112,0,0,0,261,455,0,0,0,0,0,0,337,385,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,184,1865,0,0,721,16,0,486,0,0,0,265,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,621,0,0,0,0,0,0,0,0,234,0,0,815,0,0,743, -1987,205,197,0,0,0,0,0,0,0,0,0,314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,219,452,589,0, -176,333,0,0,0,0,0,0,0,1110,47,0,0,0,0,0,0,0,0,0,0,0,864,0,0,300,0,1237,0,0,0,0, -0,0,0,0,0,0,0,1685,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,135,395,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,631,0,0,0,0,0,0,835,0,0,0,606,459,0,979,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,612,0,0,0,0,0,0,0,0,158,372,0,854,0,0,0,0,0, -0,0,1492,0,0,0,833,0,0,0,0,0,0,0,1739,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -195,0,0,0,0,0,0,0,0,730,1997,0,0,0,0,0,0,0,0,61,0,0,0,0,0,0,0,266,751,0,0,0,0,0, -0,0,821,0,0,0,715,0,0,0,868,0,959,0,0,0,0,0,0,0,0,0,0,0,1053,0,0,0,950,0,1081,0, -1595,0,0,0,0,59,0,0,0,0,0,0,0,0,0,0,47,684,0,0,0,0,0,0,1606,0,777,0,1020,0,0,0, -1094,0,0,0,0,0,0,0,350,0,0,0,0,0,0,242,1812,0,0,0,967,0,0,0,473,286,0,0,0,0,0,0, -798,629,222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,513,337,306,0,0,0,0,0,0,0,0,0, -146,0,0,1646,0,0,0,0,0,465,0,0,0,525,0,0,0,0,0,0,299,165,0,0,0,0,0,0,0,1064,0,0, -0,0,0,596,0,0,0,0,0,0,0,0,0,0,0,0,0,0,238,1741,0,1233,451,1824,0,0,0,0,733,495, -0,0,0,0,0,1204,0,0,0,559,341,0,224,21,0,0,0,0,0,0,0,0,97,1446,0,0,0,0,0,0,0,729, -0,0,565,727,0,1948,0,0,0,519,0,0,0,0,0,0,0,0,0,1193,0,0,0,0,0,0,790,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,323,2,201,0,0,59,0,0,34,0,896,961,0,1285,0,0,46,0,479,0,0, -0,0,549,0,663,0,0,0,0,0,783,65,682,0,0,0,0,0,11,0,0,0,0,0,522,0,0,0,52,0,0,0,0, -0,383,0,0,0,0,0,0,0,0,127,0,0,0,0,0,397,194,0,0,635,0,0,0,0,0,0,0,0,0,0,975,0,0, -0,0,0,0,0,0,0,0,116,0,51,0,0,858,0,1075,535,448,0,0,0,0,0,610,0,0,0,0,0,0,0,0,0, -0,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,267,673,319,94,92,0,551,0,0,218, -1406,69,256,0,0,952,1980,0,833,0,0,0,0,0,0,0,0,0,0,0,0,39,0,0,0,0,0,0,0,81,0,0, -0,352,634,0,0,0,0,0,618,0,0,0,0,0,0,73,339,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,759, -0,0,0,0,0,0,0,0,0,0,0,0,0,1075,0,0,0,0,0,0,482,649,0,0,0,0,0,0,0,0,386,336,0,0, -0,1035,0,0,0,0,0,0,0,0,0,0,0,924,0,73,0,0,0,0,0,1971,0,0,0,0,0,0,0,0,0,1344,0, -501,0,0,0,0,0,0,0,0,46,799,0,0,0,0,0,0,0,276,0,0,0,0,0,0,0,770,0,0,0,0,0,0,0,0, -0,0,0,0,0,158,0,0,0,0,0,1432,0,0,0,0,0,0,0,0,0,0,25,0,0,2001,0,0,0,0,0,0,0,0,0, -0,0,0,0,478,0,0,0,0,0,0,91,1461,211,602,0,0,0,0,0,0,0,0,0,1068,0,0,124,567,0,0, -0,1006,0,0,0,0,0,0,0,0,0,735,812,0,0,323,0,0,0,304,0,0,0,0,0,0,0,0,0,148,0,0,0, -0,0,0,0,0,0,523,0,0,144,730,0,0,981,0,0,111,0,0,132,0,0,0,0,0,0,890,0,0,0,0,0, -444,0,1787,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,2041,932,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,937,0,995,0,0,255,0,0,138,863,965,0,0,631,0,0,0,0,1394,16,652,0,0,0,0,0,0, -0,0,0,0,0,0,0,897,0,321,0,0,0,0,0,922,0,619,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,844,0,0,0,0,0,0,1659,0,1100,0,0,0,1173,0,1930,268,251,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,390,711,0,0,0,0,0,0,0,0,0,0,0,0,0,744,0,0,0,0,0,0,0,0,0,624,0,0,0, -1998,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1125,0,0,0,594,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,268,0,0,0,0,0,0,0,563,0,0,0,0,0,0,0,0,2,39,0,0,0,1332,0,0,0,0,0, -0,0,508,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,796,0,0,0,0,527,0,0,0,0,98,0,0,576,0, -0,0,0,0,122,0,276,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,645,0,0,0,0, -0,0,0,0,0,0,0,290,0,0,762,1292,0,0,0,1315,0,1955,0,0,0,0,0,0,0,0,0,0,210,131,0, -0,0,0,797,0,38,0,11,488,0,936,0,441,0,0,0,0,0,595,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -991,0,0,0,0,0,0,0,0,0,0,0,653,0,523,0,0,0,903,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0, -0,0,0,0,0,0,432,0,0,314,0,0,0,0,232,1368,534,0,0,0,0,0,27,0,0,0,12,0,0,0,0,0,0, -0,0,0,264,736,0,1657,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1117,0,127,0,0,0,1208,0,1294, -0,0,0,0,364,0,0,0,0,0,125,1334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,792,0,0,0,0,0,0,0, -849,699,0,0,0,0,0,968,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1446, -124,397,0,0,0,0,0,0,0,0,0,0,0,641,0,0,0,0,0,0,0,0,0,0,0,0,127,346,0,0,517,75,0, -0,0,0,0,0,0,0,83,0,0,0,0,0,0,1031,0,0,0,0,0,0,0,1470,0,954,0,0,345,304,410,0,0, -0,0,734,0,0,0,0,0,1822,0,0,0,1798,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,161, -1865,69,0,0,0,0,0,0,922,0,0,0,0,0,0,0,0,0,0,0,541,0,627,0,0,0,0,0,0,0,0,0,166,0, -0,0,0,0,0,0,0,0,849,0,0,0,0,0,0,0,717,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600,0,0,0,0,0, -0,654,0,0,188,273,0,0,0,543,0,410,87,0,0,941,0,0,186,250,0,1785,0,0,0,0,0,1339, -462,961,0,780,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,529,0,0,0,0,0,0,474,1276,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,24,948,0,0,0,0,657,753,0,0,0,0,941,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,706,985,837,0,1861,0,0,0,0,0,0,0,0,0,0,0,0,0,0,292,933,0,0,0,0,0, -0,0,0,0,767,0,0,0,0,0,0,0,641,0,0,0,1233,114,0,883,0,274,2008,0,1794,285,0,0, -571,0,0,0,0,0,0,0,0,0,0,823,960,16,617,0,431,0,0,0,0,0,0,0,0,0,0,567,0,401,0,2, -781,424,33,0,2006,0,0,274,0,0,1882,0,794,0,0,0,1848,0,0,0,0,0,0,448,47,0,0,0, -1199,0,0,0,0,0,0,0,0,417,0,0,0,0,0,0,0,0,0,0,295,0,0,0,0,0,0,0,1019,0,0,0,0,0,0, -0,0,0,0,0,0,0,620,0,0,0,0,464,0,0,0,0,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,442,0,930,0,0,0,0,0,516,68,0,0,0,0,0,1128,104,0,0,0,0,0,0,0,0,787,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,491,0,0,0,0,0,0,711,0,0,9,0,101,441,0,0,0,0,0,0,0,0, -0,0,160,396,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,679,326,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,1128,0,0,0,0,0,737,0,1796,0,0,0,0,0,0,0,0,0,0,0,0,338,574,0,0, -0,0,0,1096,491,405,0,0,0,0,0,1081,0,0,0,0,0,0,0,0,0,0,0,0,0,1676,0,1207,0,0,0,0, -0,0,969,354,0,0,0,0,598,0,297,0,0,0,0,0,0,0,0,1772,751,0,37,0,0,1828,0,0,0,0,0, -0,0,0,0,257,191,582,0,0,0,0,0,0,790,0,0,0,0,0,47,0,0,0,0,0,0,0,449,306,1011,0,0, -0,0,0,299,0,0,0,0,0,0,837,0,0,0,0,0,0,10,329,0,0,0,0,0,1320,0,0,0,0,0,0,158,657, -0,1191,0,0,0,0,0,0,7,0,974,1939,0,1665,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,288, -66,0,0,0,0,494,175,0,1643,0,0,0,0,0,0,0,0,570,750,719,0,0,0,0,0,0,0,0,0,0,0,0,0, -13,0,0,1247,0,0,221,356,0,0,0,0,0,0,0,0,0,0,694,1809,0,0,0,0,0,0,0,411,0,44,31, -0,0,0,0,669,0,673,0,0,0,0,0,0,0,0,0,1303,704,299,0,0,0,275,0,0,216,1761,0,0,0,0, -0,0,0,0,0,0,0,1319,0,0,428,0,0,0,0,0,0,0,0,0,0,514,0,0,0,0,0,0,49,55,102,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,364,0,0,0,0,379,0,921,971,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1258,0,0,0,1058,0,0,0,0,0,656,0,0,0,0,0,144,0,0,0,0,0,0,0,0,0,0, -0,1373,10,605,0,0,0,0,0,0,0,838,0,1012,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,365,0,0, -0,0,0,0,0,0,0,340,0,0,0,0,0,810,0,0,0,0,0,0,495,0,0,0,0,0,0,0,0,0,261,0,535,248, -0,358,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,567,445,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,697,0,0,0,1336,0,0,0,0,0,0,0,0,917,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,972,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,351,0,0,0,0,0,0,0,0,0,0, -0,0,0,286,0,0,56,438,0,0,0,0,0,1950,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,738,0,0,0,0,0, -0,0,0,0,0,969,2047,0,0,0,0,0,0,0,818,0,0,0,0,0,0,0,866,0,0,0,0,0,0,0,1467,0,0,0, -0,0,0,0,0,0,0,0,0,0,972,0,355,0,0,0,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,267,189,104,0,0,0,0,1613,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,886,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,863,0,0,0,0,0, -0,0,1953,450,1773,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,381,0,0,0,0,0,0,0, -0,0,0,0,0,1142,0,1189,0,0,0,663,0,0,0,0,0,0,0,846,0,0,528,0,393,378,0,0,0,0,0,0, -325,899,680,1880,0,1770,0,0,0,0,0,648,0,0,0,0,0,0,185,167,0,2046,0,0,0,0,0,0, -249,1645,0,152,0,0,0,1733,0,0,0,0,0,1006,0,0,0,0,0,420,0,0,0,832,0,0,0,0,0,351, -0,0,0,0,6,40,0,0,60,0,0,0,0,1354,745,724,0,0,0,0,0,0,0,0,772,1951,275,108,639,0, -0,0,0,0,0,0,0,0,500,1758,0,0,0,0,0,0,0,0,0,0,0,1886,711,205,0,0,965,865,0,0,0, -534,0,0,0,0,691,0,0,0,237,443,0,878,0,0,0,0,0,1410,0,0,0,0,0,0,0,0,0,0,0,0,0, -995,0,0,0,0,0,0,0,0,0,0,0,0,0,578,0,0,0,0,881,0,0,0,0,0,0,0,0,822,0,923,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,924,0,0,0,665,0,0,0,0,0,1901,0,0,0,0,0,950,498,93, -0,0,0,1451,0,0,0,0,0,747,828,788,400,184,0,198,0,0,0,0,0,0,0,0,0,0,0,994,0,0,0, -0,0,0,0,0,615,320,0,0,0,978,843,905,0,0,0,0,0,0,0,0,850,974,0,0,0,0,6,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,509,0,0,0,0,0,273,0,0,0,0,0,0,0,0,0,0,0,0,0, -201,0,0,0,1041,0,0,0,1040,0,0,0,0,0,0,0,0,0,693,234,774,0,336,0,1399,22,0,805, -802,777,167,789,0,0,1705,0,0,0,0,0,0,0,0,0,0,0,10,13,11,0,0,204,264,0,0,56,0,0, -1917,0,470,0,0,0,0,0,0,0,0,0,0,0,1198,0,0,0,0,0,0,0,0,0,0,1015,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,715,0,0,1002,0,0,0,298,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,867,0,0,724,0,0,0,0,0,0,0,0,0,0,0,0,768,0,0,0,0,0,1066,0,0,0,0,67,0,174,948, -0,0,0,0,0,0,0,0,0,0,0,0,0,764,0,0,0,0,75,137,0,756,0,0,0,0,0,0,1008,842,643,0,0, -0,67,0,0,0,0,0,0,0,0,0,0,0,135,821,0,0,0,0,0,0,0,0,736,0,389,355,0,0,786,0,0,0, -0,0,0,2044,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1030,0,0,0,1083,0,0,0,0,0, -1226,0,0,0,0,356,319,8,389,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,474,0,0,0,427, -0,413,0,730,0,0,0,0,0,373,0,0,0,0,0,0,0,0,0,799,0,0,0,1793,0,0,0,322,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,290,2,0,0,0,0,0,0,0,0,0,0,672, -699,1860,0,0,0,737,0,0,0,1612,0,0,0,0,0,0,0,0,0,0,0,145,124,884,0,0,0,0,0,387,0, -0,0,0,0,0,0,0,0,0,0,679,0,550,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1305,0,0,0,0,0,0,0, -576,0,0,0,0,0,0,0,686,0,607,0,0,37,0,0,0,0,0,0,0,0,0,101,1726,0,0,0,0,0,958,0,0, -0,903,0,0,0,0,147,0,0,0,0,0,0,0,0,0,0,0,367,0,0,0,0,690,0,705,273,0,0,887,0,0,0, -0,0,0,0,0,0,0,0,90,0,0,0,0,0,0,0,908,0,0,0,0,0,0,0,1261,0,0,497,1235,0,429,0,0, -0,0,904,0,12,125,0,0,0,841,0,0,0,0,0,860,946,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,768,0,770,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,271,0,0,0,0,0,0,0,719,0,699,581,0,0,0,0,0,0,0,0,0,0,862,304,0,631,0,0,0,0,880, -1513,0,0,0,0,0,981,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,434,0,0,0,0,0,550,0,0,476,930, -824,553,0,0,452,0,151,0,0,0,0,0,0,772,0,292,135,0,0,0,0,0,0,0,504,0,0,1089,0,0, -0,0,0,0,0,0,0,0,0,783,0,0,0,0,0,0,206,393,0,0,0,0,0,0,0,0,232,912,0,0,0,0,0,977, -0,0,716,98,0,0,0,0,0,733,0,0,0,0,0,0,0,0,19,0,0,0,0,668,0,360,0,0,0,0,0,0,656,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,726,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,1269,0,0,463,0, -0,0,0,0,0,1454,0,1287,245,0,989,0,0,0,0,0,0,0,0,0,107,164,0,0,0,0,0,0,0,1061,0, -0,0,0,2,484,0,0,0,0,0,0,0,1127,0,0,0,0,0,0,0,460,0,0,0,0,0,932,0,0,0,0,0,0,0, -588,625,0,0,0,0,76,92,0,0,0,0,0,0,0,0,0,0,0,0,0,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -763,0,622,0,0,0,253,0,546,0,0,110,0,256,916,0,0,35,212,0,0,746,0,0,0,150,0,0, -1466,0,0,0,1299,0,0,0,0,0,0,0,0,0,1518,0,0,0,0,0,0,0,0,0,0,0,0,0,1229,0,0,0,816, -0,0,0,0,0,0,159,0,0,0,0,0,734,869,126,1716,0,0,0,0,0,0,202,232,0,0,0,0,212,0,0, -0,0,0,111,1003,0,0,0,0,0,0,0,0,0,0,0,1712,0,0,216,0,0,0,0,516,0,0,0,0,0,650,0,0, -0,0,57,99,0,0,0,0,300,574,0,0,0,0,1023,0,0,302,0,1871,0,728,252,0,0,461,0,0,0, -323,0,0,0,0,0,0,775,461,0,0,0,0,0,0,172,0,0,464,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,73,727,0,1023,0,0,0,0,0,0,0,0,0,0,577,0,0,0,0,0,0,0,0,1037,0,0,0,0,0,0, -0,0,280,677,0,0,0,0,0,0,0,0,0,0,0,799,0,0,0,0,159,0,446,1730,0,0,0,0,0,0,0,0,0, -395,0,0,0,0,145,0,0,0,0,0,0,0,20,0,0,426,608,0,0,0,0,0,977,0,250,0,0,0,0,0,100, -0,0,0,0,1982,0,0,0,0,0,476,0,0,0,0,0,0,594,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,447,0,0,0,0,526,0,0,14,1124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,188,0,0,0,0,0,0,0,0,362,301,0,0,0,1743,0,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,872,0,831,0,0,208,202,0,0,0,0,0,0,0,1954,0, -0,0,0,516,872,0,0,313,224,0,0,24,0,11,546,0,0,0,1937,242,241,46,0,0,0,830,1273, -0,0,0,0,0,0,0,825,327,1006,0,0,0,0,0,1580,516,366,0,0,0,0,0,1736,0,0,0,0,0,0,0, -0,0,0,0,1935,0,826,0,0,0,0,139,331,0,0,0,0,0,0,0,0,0,0,0,288,0,916,0,0,0,0,0, -1888,0,0,0,0,0,0,0,1471,0,1570,0,394,0,0,0,0,0,0,0,1931,0,1719,0,658,228,0,0,0, -0,0,374,0,0,0,0,735,0,0,0,0,0,0,323,498,0,1063,0,0,0,0,155,0,0,0,0,0,0,0,0,906, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,616, -902,0,0,0,0,0,692,0,0,0,0,0,0,823,0,0,0,305,0,0,0,0,0,0,0,681,0,0,0,0,0,214, -1004,0,0,0,0,0,0,0,23,0,0,1703,0,0,0,0,0,0,0,0,0,1443,0,0,19,714,0,0,0,0,64,737, -0,0,345,1758,0,0,579,47,0,0,539,139,0,0,0,0,388,0,0,0,0,253,0,0,0,0,0,0,252,0, -745,0,0,0,0,0,0,0,0,0,0,0,504,107,0,871,0,0,0,229,0,0,0,0,0,903,0,0,71,0,0,549, -6,47,0,0,0,0,0,0,0,0,0,980,865,705,0,0,0,161,0,0,0,0,143,1331,0,0,0,1388,33,724, -0,0,0,19,0,0,0,395,0,0,0,0,0,846,210,0,0,0,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,695,937,497,0,0,0,0,0,718,0,0,0,0,0,0,0,1581,0, -0,0,0,0,0,161,49,0,0,0,0,0,0,0,0,0,597,0,0,0,1094,0,0,0,811,908,0,0,0,0,0,0,0,0, -0,0,1471,0,0,0,0,0,0,0,0,0,0,42,1935,0,0,0,2014,66,2007,0,0,586,0,0,0,0,0,0,0,0, -0,28,1077,0,0,0,1221,0,0,62,0,0,0,0,0,0,0,0,0,0,1766,0,0,0,0,0,0,0,0,0,0,0,0,25, -0,499,1388,0,0,97,10,0,0,0,0,0,481,0,0,0,0,0,0,0,0,0,0,37,134,155,486,0,1442,0, -0,0,0,0,591,0,0,0,0,0,0,310,1173,0,0,0,0,409,1156,0,0,0,482,0,0,263,926,0,0,0,0, -0,0,0,0,0,0,0,0,0,804,0,0,0,0,0,0,0,0,0,0,0,0,0,1265,0,415,0,348,0,0,0,1012,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,165,1803,0,0,0,0,0,0,0,408, -0,0,0,0,0,0,257,1321,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1138,0,0,0,249,0, -0,0,576,0,0,0,0,231,0,0,0,288,0,0,0,0,0,0,0,0,0,433,1487,569,1678,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,0,0,0,0,0,779,538,0,0,0,413,0,0,0, -0,0,0,0,0,0,0,495,0,0,0,0,0,191,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,530,567, -0,0,0,0,0,1484,0,0,0,0,0,0,815,609,0,0,0,0,0,484,0,0,0,0,0,0,0,0,0,0,900,0,0,0, -0,1335,0,1724,0,0,0,0,0,0,0,0,0,0,0,640,0,0,0,0,0,0,0,0,0,0,0,1831,0,0,0,0,0,0, -0,0,0,0,0,0,0,474,0,0,0,0,0,0,0,0,0,1103,0,1504,655,1034,0,0,0,0,0,305,0,0,0,0, -0,0,0,0,0,1236,0,0,429,217,0,0,0,0,739,278,0,0,0,0,0,0,0,708,0,0,0,0,0,1840,233, -0,0,0,0,0,0,0,0,2017,0,0,0,0,0,1488,0,0,0,1590,0,0,0,0,0,1800,28,0,0,0,0,0,0,0, -0,0,45,0,36,0,22,1442,378,0,0,0,0,0,0,1507,0,0,0,0,0,0,0,0,0,0,39,0,0,1054,725, -1955,0,2036,0,0,0,0,0,0,0,0,0,0,896,1871,0,0,0,0,0,0,0,0,0,0,805,0,0,0,0,2046,0, -0,0,0,17,712,0,617,55,320,271,0,0,0,0,0,0,0,0,0,445,0,184,103,0,0,0,0,0,0,0,0, -659,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,676,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -337,0,0,0,506,0,0,0,0,0,843,77,0,458,0,0,0,0,0,1420,382,109,142,330,0,0,0,0,0,0, -0,0,0,0,0,0,87,0,0,0,492,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1239,0,0,0,0,0,0, -211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1049,0,321,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,1985,0,0,122,0,0,234,0,0,0,1098,0,0,0,0,0,0,549,253,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,522,131,0,0,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,507,0,0,0,0,811,630,0,0,0,343, -0,0,0,0,0,448,591,455,0,1381,0,0,0,0,0,0,0,575,0,0,0,0,0,1175,0,0,0,0,0,0,0,0,0, -653,0,0,0,1761,0,1198,0,0,0,0,297,1127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,678,0,0, -164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,0,45,0,0,0,0,0,121,0,0,0,0,0,0, -0,0,125,0,0,0,1622,0,0,0,0,0,721,145,0,0,0,970,792,0,0,0,715,0,0,0,0,0,1999,0,0, -74,531,0,0,65,0,0,0,105,220,0,0,0,0,0,0,0,960,0,0,0,0,0,0,428,19,0,0,401,96,0,0, -0,0,0,1595,116,0,1021,0,0,0,0,0,750,1961,0,0,148,0,0,0,0,0,0,0,0,0,0,0,0,0,75,0, -0,1383,0,0,0,0,0,0,0,0,0,0,0,0,0,0,779,0,0,0,0,0,0,0,0,598,0,424,0,0,0,0,0,0,0, -1222,0,0,0,876,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,0,0,0,0,187,0,8,0,0,0,0,0, -0,0,429,0,685,0,0,0,0,0,0,0,0,0,0,0,132,472,0,0,0,0,0,0,0,0,0,938,0,0,874,0,0,0, -0,0,774,0,0,0,0,0,92,0,0,0,0,0,0,830,701,0,0,0,0,0,426,350,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,603,59,0,0,0,0,0,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0,0,0,441,163,4,0, -0,0,0,0,0,0,0,0,806,0,0,0,0,0,0,233,0,0,0,0,1994,0,1739,0,0,393,0,47,1038,0,0,0, -309,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,363,0,0,0,175,0,0,0,0,0,0,0,666, -0,0,1675,0,1600,0,0,0,808,0,0,0,0,0,0,0,0,0,0,0,280,54,0,0,0,0,0,0,0,0,421,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,0,0,103,254,0,262,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,805,0,0,0,0,0,0,0,0,0,1630,0,0,0,0,0,0,0,0,0,0,0,0,0,671,972,989,0,0, -0,0,0,0,0,889,0,0,0,1382,0,0,0,0,0,0,0,775,0,0,0,0,0,0,0,0,0,0,388,202,0,0,0,0, -16,560,0,0,0,841,0,0,566,0,0,0,938,0,0,0,0,0,0,0,0,0,0,912,0,0,0,1361,0,0,0,0,0, -0,618,236,0,1854,0,0,318,190,0,1376,0,0,0,0,0,0,0,349,0,0,0,0,951,1972,0,0,0,0, -0,0,344,0,0,0,0,0,0,0,0,850,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,910,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,163,85,0,487,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,145,0,83,0,0,1013,0,0,0,1922,0,0,169,557,66,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1193,82,0,352,454,57,0,0,1333,396,107,0,370,0,0,0,0,0,0,0,0,0,204,0,0,0, -0,0,1706,0,0,0,0,0,0,0,0,0,0,0,0,394,1204,0,0,0,0,0,1007,0,0,0,1696,0,1519,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,981,0,0,0,0,1072,0,0,0,712,0,1629,0,0,0,0,0,0,0,728,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1271,0,0,0,1608,16,0,0,0,0,485,0,0,0,0,0,0, -153,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1991,0,0,0,0,0,0,0,0,52,0,21,0, -0,0,0,0,0,0,0,0,819,0,0,0,0,0,917,0,0,0,0,784,0,0,0,0,135,0,0,0,0,0,454,0,0,0,0, -0,0,0,0,0,852,1719,0,0,0,0,0,852,0,0,0,0,0,952,0,0,0,0,568,0,0,0,0,0,448,0,0,0, -67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1826,657,0,729,666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -669,0,0,0,0,0,0,0,402,0,0,152,0,0,0,0,912,0,0,0,0,0,0,51,320,0,445,0,0,0,0,308, -0,0,0,0,0,386,0,0,239,0,0,130,83,0,143,0,348,0,0,0,0,0,0,0,958,0,0,0,0,0,210,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,430,0,0,0,0,0,0,0,0,0,0,0,0,7,213,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,801,0,0,0,0,0,0,0,0,0,936,0,108,0,0, -0,0,0,0,0,0,0,885,587,219,398,364,0,1165,0,0,342,241,303,0,0,0,0,0,0,0,0,0,0, -1454,0,0,0,0,0,0,0,0,0,0,254,562,0,786,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1294,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,493,216,0,0,0,0,219,341,0,0,0,0,0, -0,0,0,0,0,130,1734,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,701,604,0,0,879,0,195, -666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1669,0,0,0,1791,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1228,0,0,0,0,0,623,0,0,0,0,0,0,0,798,0,0,0,0,0,0,0,0,0,0,0,0,84, -122,0,0,0,837,0,0,0,0,0,0,1013,0,0,577,0,0,0,460,932,0,0,0,0,0,0,0,0,0,0,0,31, -131,0,0,0,605,0,0,0,1246,0,0,0,0,68,278,165,307,781,0,0,0,0,0,0,33,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,1113,0,0,720,1953,203,0,0,0,0,0,0,0,425,326,0,0,0,0,0, -0,0,0,0,0,241,1316,0,0,0,0,0,416,0,0,0,1300,0,847,0,0,662,358,0,0,0,0,839,1823, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,654,1522,0,0,0,0,0,0,163,0,0,0,0,0,314,978,0,0,0, -601,0,0,0,0,0,946,434,0,0,0,402,411,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,1467, -410,0,0,0,0,0,0,0,0,0,0,0,0,0,0,483,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,0,70,0,0,0,0,1405,0,0,0,0,0,0,108,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,777,0,0,0,0,0,747,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,505,0,326,0,0,164,628,654,0,0,0, -37,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,668,152,0,0,0,0,0,0,0,0,0,0,0,581, -0,0,0,0,44,126,89,0,0,0,0,0,0,0,0,1531,0,0,0,0,0,0,0,0,203,1167,0,0,0,0,0,0,0,0, -531,1232,0,0,0,0,0,943,0,670,231,880,0,1617,0,0,0,1957,0,0,0,0,0,0,0,975,0,0,0, -0,0,0,0,0,0,0,0,242,0,0,0,0,0,0,0,0,0,421,0,0,14,834,0,0,0,0,0,0,0,0,0,0,0,0, -465,0,0,0,0,0,834,688,413,855,0,0,0,590,0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,0,0,0, -0,45,169,0,0,0,0,0,0,0,0,0,0,0,198,0,0,565,585,0,0,0,0,0,0,0,0,0,0,0,0,0,691,0, -0,0,593,0,0,0,0,0,0,0,0,0,913,116,0,0,0,0,1360,0,0,0,802,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,673,308,0,709,1006,1895,0,228,0,0,0,1840,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,608,0,0,0,0,0,0,0,0,0,1573,0,2039,136,540,0,0,0,0,0,0,0, -897,0,0,938,1878,0,0,0,0,0,0,0,0,0,1469,0,999,0,299,0,0,0,0,0,0,0,578,0,0,0,0,0, -456,0,0,0,1679,163,693,0,0,0,0,0,0,48,755,0,0,0,0,0,0,0,0,0,0,0,0,338,0,0,0,0, -1091,0,0,0,0,695,0,0,1464,0,0,0,0,0,975,0,0,335,0,0,1979,0,0,0,0,269,1566,630, -396,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1815,634,0,0,0,966,0,0,0,0,0,0,0,9, -412,0,958,0,0,579,382,0,212,0,0,0,0,965,681,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,655, -0,0,0,0,67,0,0,0,0,0,0,751,0,0,0,0,423,231,0,0,1016,300,0,0,0,0,100,237,0,0,0, -1370,0,0,0,1208,0,0,0,0,0,1219,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,199,0,0,427,0,0, -0,0,949,665,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,712,0,0,0,0,0,1186,0,0,0,0,0,0,0,0,0,0,295,312,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -151,0,0,0,0,588,4,0,0,0,0,0,414,104,0,0,757,263,0,561,0,0,0,320,0,0,0,0,0,0,0,0, -0,0,0,225,0,0,0,0,37,817,0,974,0,0,0,0,0,0,0,0,0,0,0,0,0,2026,131,235,16,0,590, -1157,0,0,0,0,0,0,0,0,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,390,0,0,0,0, -0,0,0,1144,0,0,0,464,0,0,0,0,0,0,0,0,0,0,0,0,204,407,303,1218,0,0,0,0,5,325,0,0, -0,0,12,800,0,1783,0,0,0,0,0,0,0,0,0,0,504,621,0,0,0,0,0,0,0,0,0,920,0,376,0,0,0, -0,0,218,580,0,768,454,0,0,0,0,0,0,0,0,0,0,0,0,676,0,0,0,0,0,0,164,0,0,0,0,0,0,0, -0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,120,285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,343, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,0,0,1812,0,0,8,0,0,0,21,1125,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,1327,0,0,0,0,575,1598,0,0,0,0,0,0,0,0,0,895,0,0,0,959,0,0, -0,0,0,1759,173,0,0,0,0,266,261,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1427,0,0,300,1033,0,0,0,0,0,0,0,0,0,0,0,584,0,0,0,0,52,734, -0,0,217,239,0,1129,0,0,0,0,0,0,0,0,732,20,0,0,0,0,0,0,0,0,0,0,0,418,0,0,0,613,0, -0,0,0,0,0,0,0,0,632,0,0,85,984,0,0,0,0,909,694,7,1109,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,167,0,0,0,0,280,62,0,0,33,0,0,359,186,980,0,0,0,0,0,0,0,0,0,0,0,585,0,0,0, -211,0,0,336,145,0,1130,0,873,0,0,840,263,0,0,0,0,0,0,0,0,0,916,0,0,0,0,0,0,0,0, -0,0,155,0,0,0,461,97,0,0,0,0,0,1356,0,0,0,0,0,0,0,593,0,0,0,0,0,1392,0,0,0,0, -126,0,0,0,0,1179,0,0,0,0,0,162,0,0,0,0,0,765,0,187,0,1286,0,0,0,0,0,0,0,0,0,635, -0,0,23,215,0,0,0,1306,0,0,97,716,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,657,0, -0,0,0,0,0,0,0,299,0,0,0,0,0,0,134,0,0,0,0,0,0,0,0,0,0,0,658,1082,0,0,0,0,0,2002, -0,0,0,0,0,0,833,248,0,0,0,0,0,1654,0,0,531,0,0,0,0,0,0,634,0,0,0,0,0,0,0,0,0, -853,573,249,0,0,0,0,0,0,0,0,527,0,0,0,0,1419,0,0,0,0,0,0,20,49,0,0,0,992,0,0,0, -728,0,0,0,0,0,0,0,0,0,0,0,0,497,1579,0,0,0,0,62,268,0,0,0,0,0,0,0,1201,0,0,0,0, -0,0,0,0,0,0,0,0,495,193,0,0,0,0,106,0,0,859,0,0,23,0,0,0,0,0,0,0,813,925,0,0, -223,613,953,0,0,0,0,0,0,0,0,666,0,0,0,0,0,0,0,0,0,670,0,0,40,216,0,0,0,0,0,0, -259,0,0,0,440,1114,0,0,0,0,0,0,0,0,74,475,0,0,188,139,0,797,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,1572,0,0,0,0,39,0,0,0,0,0,0,0,0,0,0,0,0,1594,0,0,0,0,0,0,0,290,0,232, -0,0,887,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,521,14,0,0,0,0,0,741,0,0,0,992,0, -0,0,0,0,0,0,0,111,0,0,425,0,0,0,0,0,789,0,0,0,1593,0,1768,0,0,233,0,0,0,0,943,0, -0,0,0,0,0,0,955,225,245,0,0,0,0,0,0,241,0,0,0,0,1943,0,0,0,1284,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,709,0,0,0,0,0,0,554,0,0,0,0,0,0,0,0,1564,0,0,0, -443,0,0,0,0,0,0,280,0,0,0,0,0,0,0,0,729,0,0,0,348,0,0,0,0,0,0,0,758,848,298,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,829,1422,189,121,0,0,632,812,0,0,556,0,0,0,0,0,436,172, -530,844,232,984,0,0,0,0,0,0,0,0,0,0,147,0,0,0,0,0,0,0,0,537,0,0,0,0,0,859,0,0, -842,0,0,0,0,0,0,0,0,0,0,1291,0,0,0,0,0,0,0,0,0,0,0,1482,612,392,0,0,0,262,31,0, -0,0,0,0,0,0,0,0,0,753,549,0,0,0,0,0,0,696,0,0,0,0,0,0,0,834,0,0,0,0,0,771,0,0,0, -0,0,0,0,0,0,0,0,0,0,921,0,0,0,674,0,0,0,0,0,0,0,0,0,0,308,444,0,0,0,0,0,0,805, -180,0,0,278,271,0,0,214,505,0,1215,0,0,0,0,0,0,387,271,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,1645,42,92,0,459,0,0,330,1557,0,0,0,0,0,0,0,0,113,18,0,0,0, -1742,0,0,0,965,0,0,0,0,0,0,0,0,0,0,0,0,0,182,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,973, -0,0,0,0,0,328,0,0,588,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1786, -0,0,962,1985,0,0,0,308,508,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,588,0,0,0,0,0,0,614,793,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,290,0,0,0,0,0,0,0,0,0,0,1136,0,0,0,0,0,0,0,0,0,0,796,719,0,0, -326,210,0,0,0,701,758,472,0,0,0,1947,278,1079,0,0,0,0,0,0,497,41,0,0,634,46,961, -0,810,524,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,532,0,997,0,0,0,0,0,0,0,0,0,0,0,1301,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1298,0,671,0,0,0,306,0,0,0,0,0,0,0,0,0,0, -693,1823,0,0,0,759,0,0,0,0,0,1932,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,182,0,0,0,1964, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,521,0,0,0,0,0,0,424,857,0,0,0,0,671,328,0, -529,0,0,0,0,0,716,0,1509,80,67,0,0,0,0,59,141,0,0,0,0,0,0,783,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1498,0,0,0,0,343,430,803,1183,677, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1357,53,0,0,0,0,590,0,0,0,0,0,0,0,0,0,0, -0,0,0,329,0,0,0,0,0,0,0,469,0,0,0,0,0,0,0,0,0,0,460,0,0,1743,0,0,963,340,0,0,0, -0,0,1603,0,0,250,0,0,0,0,0,646,218,0,1794,0,0,0,571,0,455,0,0,0,1012,0,0,0,0,0, -0,0,0,0,0,0,0,597,161,0,349,0,524,0,0,0,0,0,0,0,0,0,0,0,0,322,432,0,0,0,0,0,0, -325,223,0,0,0,0,0,566,0,0,0,1394,481,436,0,48,457,610,756,618,0,0,0,755,0,1217, -0,0,0,0,0,197,0,0,0,0,0,0,0,0,0,0,0,0,0,0,544,492,107,414,0,0,0,0,0,0,0,0,0,0,0, -1007,0,0,0,0,5,0,0,1580,0,0,0,0,0,0,0,0,0,0,0,0,0,673,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1843,0,0,0,0,0,0,0,0,0,165,0,0,0,0,0,0,809,885,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,498,0,0,0,306,9,0,0,0,0,0,0,0,437,721,146,0,0,0,0,0,0,0,0,0,0,0,177,0,0,0,0, -0,0,0,1377,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,959,0,0,0,1928,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1435,0,481,0,0,0,0,0,0,142,84,0,0,0,0,0, -1015,0,0,0,315,0,0,0,0,0,0,759,0,0,0,0,0,0,0,0,712,0,0,0,1722,0,0,0,0,0,0,0,0,0, -0,0,0,222,0,985,1414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1273, -538,706,0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,0,0,0,0,1781,0,0,0,0,0,431,97,665,42, -237,0,0,0,264,0,0,213,0,0,0,0,0,0,0,455,0,0,0,906,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -624,0,574,0,0,0,0,0,0,0,0,0,0,0,0,354,0,0,0,1558,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68, -235,723,1813,0,0,0,957,0,830,0,0,0,0,0,0,0,0,0,0,0,0,23,0,0,496,0,0,0,0,0,0,0, -547,239,88,0,0,0,0,0,0,0,0,0,1310,0,0,0,0,0,0,0,0,80,1076,0,0,118,0,0,0,479,274, -0,0,0,0,0,0,0,0,0,0,0,497,0,0,669,261,0,0,0,0,13,0,0,0,0,0,0,791,250,642,0,0,0, -1429,939,949,0,0,0,0,0,0,0,0,0,0,0,0,0,818,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,982,330,0,0,0,0,545,0,0,0,0,0,0,947,0,1188,0,0,0,0,0,904,0,0,0,0,0,1372,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,693,377,0,0,0,0,0,0,0,0,0,0,0,0,0,0,695,0,0, -713,386,0,0,0,0,128,1575,0,0,0,0,0,0,424,893,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,904,0,0,0,0,0,552,322,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,1808,49,0,0,0,0, -1832,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,421,0,0,442,415,0,0,289, -0,0,0,0,0,206,110,0,0,0,0,0,205,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -19,1539,0,0,0,0,0,1340,0,1194,0,0,0,0,0,0,0,0,549,0,0,0,0,0,0,0,0,1720,0,0,0,0, -0,0,0,0,0,319,0,0,0,0,112,1180,0,0,0,0,0,0,0,0,0,0,0,967,0,0,0,0,0,0,0,0,0,1940, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,735,0,0,0,0,0,0,0,0,0,897,132,0,0,0,0,0,0,0, -0,0,0,38,838,0,0,0,379,218,8,660,1017,0,0,0,0,0,0,111,387,647,877,0,0,53,790,0, -0,0,0,0,0,0,0,458,0,0,0,0,0,0,954,0,0,0,394,0,1367,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,882,0,0,0,0,0,0,0,1409,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,124,342,199,0,0,0,0, -0,0,0,0,0,0,724,628,0,0,0,0,804,266,0,0,0,0,0,208,0,79,0,0,0,0,0,0,0,0,741,0,0, -0,0,0,0,0,0,0,0,606,0,1494,821,1553,0,0,135,405,0,0,178,100,0,0,0,0,0,0,0,0,0,0, -0,0,0,481,0,0,0,1378,0,0,0,0,0,0,0,0,0,0,0,0,0,791,33,1227,857,0,467,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,447,0,0,0,0,0,0,86,128,0,0,0,0,0,0,587,0,0,0,692,1018,0, -195,0,0,0,0,0,0,0,1546,0,0,0,0,0,0,0,0,0,0,0,684,0,0,345,0,0,0,0,0,0,365,0,1683, -0,0,472,0,433,0,0,0,0,0,0,0,28,0,0,0,997,0,705,3,0,0,0,0,0,0,0,0,0,229,0,0,0,0, -102,0,0,0,0,866,1022,0,0,0,0,0,0,0,0,0,55,0,115,0,0,0,0,933,0,0,0,0,0,0,0,702,0, -0,0,0,0,0,0,1728,26,484,0,0,0,185,618,417,0,803,0,0,0,0,0,0,0,0,0,0,0,1262,0,0, -0,0,0,0,0,0,0,0,0,0,0,633,0,0,0,0,0,0,0,0,0,0,0,0,0,479,262,0,0,0,0,0,0,830,0,0, -0,0,26,70,0,0,0,0,0,0,0,0,217,0,640,51,0,0,360,1586,0,0,0,0,0,652,0,0,0,0,0,766, -0,0,0,0,298,737,0,0,0,0,0,0,0,0,0,0,655,222,906,0,0,1013,991,2009,0,0,0,0,503,0, -0,0,216,154,0,0,0,716,0,844,0,0,0,0,621,252,0,0,0,0,748,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,103,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,576,0,0,0,648,0,0,0,331,0,0,0, -0,0,0,0,0,0,0,0,0,632,0,0,0,518,107,0,0,0,0,0,0,0,0,851,0,0,0,0,504,0,0,0,0,0,0, -0,0,0,0,0,0,7,883,0,0,0,0,0,0,0,922,0,0,0,0,0,0,0,0,91,993,0,0,0,0,0,0,200,131, -10,0,0,0,0,0,0,0,0,0,0,0,0,0,365,1433,0,0,0,0,28,103,0,0,798,1013,0,0,0,0,0,0,0, -0,39,1925,0,853,0,0,271,519,0,0,0,0,338,0,0,300,470,419,0,0,0,0,0,0,836,0,0,0,0, -0,0,1937,0,0,0,0,0,393,0,0,357,0,0,0,0,0,703,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,387,0,0,0,0,0,0,75,708,453,1351,0,303,0,0,772,0,0,0,0,0,0,0,0,749,0,0, -0,0,0,0,0,0,0,0,0,0,0,1065,0,0,717,226,0,0,0,0,0,890,431,626,0,0,0,0,706,0,0,0, -51,698,0,0,0,0,0,0,0,0,0,0,0,828,0,0,17,0,0,0,0,1929,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,871,498,0,101,1793,0,0,0,0,0,0,435,0, -0,0,0,0,966,0,129,1644,0,0,0,0,0,0,0,0,0,0,0,0,0,997,502,0,0,0,0,0,0,0,0,0,0,0, -0,823,0,1927,0,0,0,0,98,1756,0,0,0,0,0,0,0,0,0,0,0,0,8,0,160,1046,0,492,0,0,0,0, -0,0,129,45,0,0,0,0,0,0,353,558,0,0,0,0,0,785,0,0,0,1145,189,0,0,0,26,353,0,0,0, -0,0,2024,0,0,0,606,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,855,0,0,0,0,0,0,0,0,0,0,0, -0,0,2011,0,0,5,4,0,0,461,764,0,0,0,1449,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1445,0,0, -0,1168,0,0,0,233,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,216,0,0,0,286,0,0,0, -3,0,0,0,723,536,0,0,0,0,0,285,0,0,0,560,0,0,0,0,0,690,0,0,0,0,0,1246,0,0,63,0, -33,0,0,0,0,0,520,1862,0,0,0,0,0,0,0,0,0,0,0,0,630,0,0,0,0,554,0,0,0,0,0,1001,0, -0,0,0,0,446,0,0,0,0,0,0,0,1313,0,0,837,636,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278, -0,0,0,0,0,0,0,0,868,0,0,0,0,1010,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1231,0,304,0,506,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,93,1408,794, -843,704,0,285,114,485,898,145,0,19,2035,0,0,0,1933,0,0,0,0,0,0,0,1728,0,0,0,0,0, -0,0,0,746,0,0,0,0,0,0,0,995,1964,0,0,0,0,0,0,0,0,0,0,0,1550,0,874,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,1018,0,0,0,814,126,0,0,1264,0,0,814,955,0,0,0,0,0,0, -0,981,0,0,0,0,0,0,0,0,915,56,0,0,100,0,0,0,0,0,0,0,0,0,638,0,0,0,0,738,0,0,0,0, -0,0,0,0,0,758,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1112,0,0,214,0,0,0,133,0,196, -168,0,0,0,0,0,1152,0,1245,0,0,538,169,871,1816,0,0,413,133,0,0,0,978,0,0,43,93, -371,0,0,0,0,0,0,526,25,0,754,335,0,0,0,0,182,0,0,0,0,0,0,0,0,0,0,0,39,601,0,0,0, -0,0,0,0,181,370,0,0,1652,358,0,0,0,0,0,0,0,0,0,176,286,0,788,0,0,0,0,0,1223,780, -254,1003,896,0,0,0,1447,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,744,0,0,0,0,0,126,0, -41,788,0,0,0,629,0,0,0,0,0,0,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,420,37,1900,0,0,0,0,542,1570,957,0,0,0,0,0,0, -0,373,31,0,0,0,0,125,325,0,0,0,0,0,0,323,0,0,1547,0,0,0,0,0,0,0,0,0,0,0,0,0, -1216,0,0,0,0,0,0,198,1905,629,15,0,0,0,0,0,0,20,75,543,1353,0,0,0,533,0,0,6,0,0, -0,0,0,0,538,0,0,0,0,0,0,0,0,0,0,0,338,0,0,0,0,11,0,0,0,284,659,0,989,0,0,0,0,0, -0,0,0,0,848,0,0,507,0,0,0,0,0,0,0,0,188,991,884,0,0,0,0,60,959,0,0,0,0,0,1653,0, -0,922,337,0,638,0,0,500,0,0,0,0,0,0,0,0,0,0,0,166,0,0,0,0,0,0,0,0,0,0,0,0,418,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,760,0,0,0,0,0,0,1277,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,770,0,0,0,0,0,0,0,243,89,0,0,0,0,0,0,0,0,0,1396,0, -560,0,0,3,1658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,586,0,0,1271,0,0,0,505,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1947, -41,445,0,0,0,0,0,0,0,0,57,189,0,0,371,0,0,0,0,552,0,883,0,923,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,875,0,0,0,1788,49,0,0,0,0,0, -0,0,0,0,0,0,661,0,0,1945,0,0,0,0,0,794,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,1135,0,0,0,745,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,410,0,976,0,0,0,0,0,703,0,0, -0,0,0,0,187,322,0,0,0,227,0,0,0,0,560,0,31,1395,0,0,0,0,0,466,0,0,0,0,643,167,0, -0,0,1428,0,412,0,0,0,0,0,0,0,0,0,1118,562,0,0,0,0,0,256,0,0,0,0,0,0,1771,0,0,0, -0,0,1190,132,0,66,0,0,0,0,0,0,0,0,0,0,317,0,0,0,63,0,0,0,0,0,0,0,1475,0,0,0,0,0, -0,0,288,0,0,0,0,608,0,0,0,0,0,0,0,0,1225,0,1189,0,0,0,0,0,0,0,1468,0,0,0,0,0, -689,120,0,0,0,0,0,0,0,1,0,329,0,0,0,0,226,0,0,0,0,0,1855,0,0,461,0,0,0,0,1346,0, -0,0,0,0,85,0,0,299,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1171,0,0, -0,980,0,0,0,0,0,0,0,0,637,279,0,0,0,0,0,293,0,0,0,0,528,17,0,0,0,0,5,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,601,0,0,0,0,0,0,779,0, -196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1322,737,752,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,412,192,80,0,0,8,1470,0,0,0,0,0,0,0,0,0,873,0,0,0,0,0,835,0,0,0,0,256, -38,986,0,0,0,0,0,0,0,0,0,91,257,278,911,0,0,0,0,0,0,0,0,749,151,0,0,0,0,0,0,0,0, -0,0,0,0,989,0,0,990,0,0,90,194,0,0,0,0,0,425,0,0,0,0,0,774,0,0,0,0,0,0,0,0,0,0, -646,827,752,0,0,0,662,0,22,21,0,0,0,0,0,0,95,239,0,0,0,431,0,0,0,0,0,874,0,0, -265,65,0,0,0,1350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1887,0,0,0,0,0,0,0,809, -0,696,0,1074,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,630,0,0,802,0,0,0,56,776,0, -970,0,0,797,0,0,0,0,0,400,0,0,1951,0,0,41,0,11,118,0,0,0,0,0,0,0,0,251,615,0,0, -0,1044,0,0,0,0,0,0,0,0,0,0,0,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,370,0,0,0,0, -104,48,209,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,930,0,0,0,0, -0,0,0,0,0,0,0,1286,0,759,0,120,385,0,0,0,429,0,0,0,0,0,0,0,0,820,0,0,0,0,0,0, -199,0,10,151,0,0,0,761,365,0,0,0,0,0,0,0,0,0,46,1086,0,0,0,0,11,1624,58,344,0,0, -1008,1868,0,0,0,888,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,711,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,440,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,914,1913,0,958,0,885,0,0,0,0,0,0,0,0,0,0,0, -0,0,847,276,0,302,65,0,0,0,510,0,1514,0,0,0,0,0,0,152,291,0,0,0,0,0,0,0,0,0,0,0, -0,282,589,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,0,463,42,0,0,0,0,0,372,0,0,0,0,0,0,0, -0,0,680,0,0,0,0,0,0,0,0,977,1997,0,0,0,810,0,0,0,0,0,0,0,0,0,1390,0,0,0,644,0,0, -867,982,0,0,0,0,0,0,0,540,0,123,0,0,0,1978,0,0,0,0,789,623,0,1723,0,1220,0,0,0, -0,0,0,0,480,0,0,0,0,0,0,0,0,0,0,0,888,0,0,0,0,0,0,0,0,0,0,0,0,299,1995,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,788,179,0,0,0,0,0,0,431,156,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1373,39,80,196,0,0,507,0,0,0,646,0,0,0,0, -0,1214,0,0,0,0,926,0,0,0,1,114,0,0,0,0,0,446,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,490,0,0,0,491,0,1584,0,0,507,250,0,0,0,158, -10,362,1,0,0,0,0,0,0,0,0,0,408,228,860,480,0,779,0,0,0,557,0,0,142,197,0,0,0,0, -0,0,0,0,0,0,0,1490,11,378,316,1057,0,0,18,579,299,1546,0,177,0,0,0,0,0,0,0,0,0, -411,0,0,0,0,727,439,0,0,0,0,0,1528,0,0,0,0,0,0,58,0,482,0,0,0,505,1952,0,0,0,0, -0,0,0,0,0,0,0,242,0,0,0,0,0,0,0,953,0,0,0,0,802,0,0,0,0,0,0,0,0,0,0,290,0,0,791, -52,0,0,0,0,0,0,0,0,0,0,0,112,0,0,0,0,0,1028,0,0,138,0,0,0,0,1811,0,0,0,0,0,0, -934,1821,0,0,0,0,371,38,0,0,0,1296,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,723,0,0,0,0,0, -0,0,0,0,0,0,0,0,1330,0,0,0,0,0,0,0,1255,296,109,0,0,0,0,0,660,0,0,0,0,270,591,0, -0,0,0,0,0,0,1090,81,0,0,0,0,391,0,0,0,0,249,322,0,0,0,0,0,0,0,1412,0,0,0,0,0,0, -0,0,0,0,526,632,0,0,0,0,0,0,235,144,0,0,0,0,0,940,0,0,0,52,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,309,196,0,0,0,0,0,1912,0,1290,0,686,0,0,625,0,0,0,0,0,0,0,0,0,0,0,412,0, -0,0,0,43,0,0,0,0,11,967,758,0,0,0,0,0,0,0,0,0,0,0,0,0,0,220,0,0,0,0,0,0,0,0,0,0, -873,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,890,0,0,2,0,0,0,0,0,0,0,0,1774, -393,263,0,0,0,0,0,0,818,456,0,0,251,178,393,97,0,0,0,0,0,674,168,0,0,0,0,0,0,0, -159,1639,0,0,0,0,0,0,0,0,59,934,0,191,0,0,0,0,346,165,0,877,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,128,0,0,0,0,0,0,1297,0,0,0,0,0,0,164,0,0,0,15,132,241,1073,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,324,53,0,0,910,0,0,0,0,0,0,0,0,734,705, -217,73,0,0,0,0,0,0,0,0,636,389,0,1409,0,0,0,0,0,893,0,0,0,0,21,0,0,0,0,0,0,0,0, -0,0,0,0,0,721,0,0,0,959,0,0,0,0,1433,0,0,0,0,0,0,0,0,0,0,0,0,174,189,0,0,0,0,0, -0,0,0,0,0,22,2,0,0,815,354,0,0,0,0,425,0,411,60,13,1611,0,0,0,0,0,0,0,0,0,0,0,0, -0,1478,596,0,0,398,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,1159,0,0,0,0,0, -592,223,0,0,0,0,0,0,0,245,64,0,0,0,0,278,0,604,0,0,1502,265,0,0,0,0,0,0,0,310, -1763,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,1356,0,0,0,0,0,0,0, -0,505,0,0,0,0,0,0,0,1000,0,0,966,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,839,0,0,0,0,0,0, -0,0,0,0,0,0,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,590,0,0,0,0,280,0,0,0,1386,0,0,0, -281,0,1064,0,0,0,0,0,917,0,0,15,555,0,0,1014,1883,0,0,0,965,0,0,117,33,0,0,0, -801,0,0,0,0,0,877,0,824,0,0,0,0,0,0,0,0,0,0,0,365,0,0,0,0,0,0,774,7,0,430,0,0, -231,360,0,0,0,0,0,0,0,0,822,740,0,0,929,1485,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,852,0,0,0,0,17,0,0,0,0,0,0,1001,0,0,0,0,35,831,0,0,384,457,0,0,0,1351,0,27, -0,0,984,0,264,552,0,401,0,0,0,710,0,1211,0,0,11,205,0,0,0,0,0,0,0,0,0,0,0,0,5, -579,0,717,0,0,1011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,805,0,0,0,0,0,0,0,0,0,0,0,489,0, -0,0,1024,0,0,0,0,0,0,0,0,0,892,0,0,0,0,0,0,0,0,0,0,0,0,473,0,0,0,659,864,0,0,0, -0,0,0,152,819,0,51,0,0,0,0,0,0,0,0,0,0,130,0,0,0,0,0,229,0,0,0,0,674,0,0,0,0,0, -0,0,0,0,770,52,79,0,0,0,1666,0,409,0,0,0,0,0,0,0,195,0,688,0,0,0,0,0,0,0,0,0,0, -0,889,174,160,0,0,0,0,0,0,0,0,0,0,0,0,0,872,0,918,569,268,0,0,0,1224,0,1361,0,0, -0,0,0,0,0,0,0,374,0,0,0,0,0,731,0,0,0,0,190,0,0,0,0,0,0,0,202,506,444,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,835,0,17,1526,0,0,0,0,0,477,0,0, -994,1374,76,0,0,0,0,0,0,0,355,287,0,1389,0,0,0,0,0,0,455,384,0,0,0,264,0,0,0,0, -0,0,0,0,0,0,0,0,1001,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,851,175,359,0,0,0,0,0,0,0, -0,287,740,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,857,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -819,1402,0,0,0,0,0,0,174,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1649, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,655,573,0,0,0,0,0,0,0,0,128,351,0,0,0,0,0,0, -0,0,0,0,0,918,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,687,0,0,0,0,0,0,0,0,0,1525, -0,0,0,1009,0,0,0,0,0,0,0,340,0,0,0,0,0,0,0,0,0,0,861,0,176,0,0,0,0,0,0,0,0,0,96, -985,0,615,0,0,0,0,0,0,0,1919,0,0,0,0,0,1131,0,0,0,0,0,0,0,247,0,0,0,0,27,23,0,0, -0,0,0,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1015,0,0,0,0,0,1088,0,0, -0,0,0,1585,0,0,0,0,227,0,0,0,478,360,0,0,0,95,0,0,0,0,0,0,699,0,0,0,26,0,0,0,0, -1119,0,0,0,739,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,741,67,0,0,0,0,0,0,464,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,0,96,0,0,0,26,342,0,0,0,0,0,0,203,0,0,449,0, -0,0,0,0,0,0,0,0,0,256,311,0,0,0,0,0,0,758,0,0,0,0,0,0,0,0,827,0,0,0,0,581,64,0, -1047,0,0,0,0,0,288,0,0,0,0,0,1375,0,0,0,0,0,0,0,0,0,0,0,1309,0,0,0,0,0,0,0,0, -376,12,0,0,0,0,0,154,0,1520,0,1753,95,502,0,0,0,0,0,0,0,269,291,1197,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,1341,0,1017,0,0,0,0,0,0,0, -0,857,1810,533,0,0,1453,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,836,211,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,19,0,156,0,0,0,0,1009,0,0,0,0,0,0,0,0,0,0,0,0,0,820,0,0, -0,0,0,0,0,0,0,228,0,0,0,1131,0,1276,0,0,0,0,0,0,0,0,0,0,0,0,849,1792,0,0,389, -291,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,525,0,0, -0,453,0,0,0,0,666,0,0,0,422,0,355,0,0,0,0,165,0,260,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,865,0,0,0,0,0,0,0,1625,0,0,0,234,0,1383,0,0,0,0,0,0,0,0,306,0,0,0,802,1921, -0,0,0,0,0,0,180,0,0,0,0,1312,814,0,0,0,0,0,0,0,0,0,0,707,0,0,0,1493,11,61,733,0, -0,0,341,0,0,0,98,0,0,0,0,0,0,0,0,0,0,0,1014,0,0,0,0,0,0,0,142,102,0,0,30,0,0, -823,0,1045,0,0,0,1930,0,1512,0,0,0,0,0,0,0,87,0,1243,245,0,0,0,0,0,0,0,48,68,0, -0,0,0,0,0,0,0,126,77,625,938,0,0,351,0,0,0,174,1668,0,707,0,0,0,0,0,0,0,0,0,0,0, -403,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,282,0,0,0,0,0,0,8,44,0,0,363,115,0,0,0,0,0,0, -0,0,0,0,0,0,545,761,0,0,835,1254,0,0,0,0,930,1936,0,0,0,0,0,0,0,0,653,0,0,0,0,0, -344,0,0,1483,673,185,0,0,460,93,753,478,0,0,0,0,0,1020,0,0,0,0,0,0,0,103,0,0,0, -499,0,0,0,0,0,0,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,968,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,3,0,0,0,0,399,0,0,0,0,224,563,0,0,0,0,0,704,0,0,0,0,0,0,0,0,0,0,0, -1559,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,861,0,0,0,0,946,333,746,0,0,0,0,0, -0,0,910,0,0,0,0,0,0,0,0,0,0,0,0,0,652,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1393,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1514,0,0,0,0,201,0,510,717,0,0,528,0,0,0,0, -20,0,0,0,1251,0,0,0,1163,0,0,0,307,0,0,0,0,0,1091,0,0,0,0,0,0,0,0,0,0,0,429,0,0, -0,881,0,0,0,0,0,621,0,0,0,0,0,0,0,736,0,348,0,868,0,0,0,0,433,0,0,0,771,1495,0, -0,0,0,215,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,0,0,55,0,0,0,0,0,0,0,112,62,0,856,270, -0,572,0,0,0,0,939,0,0,0,0,0,0,0,352,0,0,0,0,0,0,0,0,0,647,0,0,0,0,10,0,0,0,0,0, -0,0,220,0,0,0,0,0,0,0,0,0,0,0,0,0,464,0,0,109,0,0,0,1746,0,0,0,515,0,0,0,566,0, -0,0,0,0,0,67,40,0,0,722,992,0,0,923,0,0,0,0,0,0,1145,0,0,0,0,0,0,0,0,0,0,0,568, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,0,0,0,0,645,0,0,328,0,0,0,0,0,0,0,0,0,0,0,0, -1363,0,0,0,0,0,1280,0,0,0,0,0,0,0,0,0,0,7,28,360,162,0,0,0,0,0,0,0,0,0,0,0,764, -0,0,833,862,0,856,0,0,0,0,0,0,736,92,0,0,948,1944,0,1479,63,590,0,0,0,1521,0,0, -0,709,0,0,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,483,0,0,0,0,1213, -0,0,0,0,29,1022,0,1712,0,466,0,0,0,0,0,0,0,0,0,0,0,0,0,731,0,0,0,0,0,0,171,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,241,0,0,0,0,0,0,0,0,0,0,0,964,2005,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1100,0,0,0,954,0,0,0,0,0,0,0,0,0,1958,0,0,34,549,994,0,0,449, -137,850,0,0,670,146,0,0,0,0,518,159,0,0,0,0,0,0,0,0,151,0,0,1027,0,0,0,0,0,0,0, -0,0,0,983,0,0,0,0,993,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,501,0,0,0, -0,0,0,0,0,0,452,0,0,0,0,0,0,0,0,0,0,233,149,0,0,0,0,0,0,0,0,582,0,0,0,801,0,0,0, -0,0,0,70,0,0,369,0,36,0,0,0,0,0,0,0,204,721,430,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1817,16,1078,1021,0,0, -406,0,0,0,0,0,69,0,0,0,0,0,1830,0,0,0,824,0,0,0,0,0,0,0,0,0,826,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,816,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,717,1845,0,423,0,0, -0,0,0,0,0,0,510,0,0,1048,0,0,0,618,0,0,0,520,0,0,0,0,990,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,321,0,0,0,0,0,0,0,1135,0,0,921,0,0,0,24,397,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,856,0,0,0,139,282,981,0,288,0,0,0,1890,651,56,0,0,0,0,0,0,0, -0,261,0,0,0,0,0,0,0,0,0,0,0,617,1403,0,1205,0,0,563,0,0,0,0,0,0,0,0,333,0,0,0,0, -0,369,0,0,0,0,0,0,0,0,0,622,0,0,0,1407,0,0,0,0,0,0,0,0,0,0,0,0,624,160,0,363,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,619,0,174,292,0,0,656,616,0,0,0,685,0,0,0,0,0,0,0,0,0,0,0,0,0,647,0,0,0,631,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1267,0,0,0,1797,0,0,0,1684,0,0,469,0,531, -1230,73,0,0,0,0,0,0,0,0,0,268,0,0,0,0,0,102,558,109,65,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,595,0,0,0,0,0,374,1832,0,0,0,0,0,0,16,0,405,6,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,881,0,1495,0,0,0,0,0,0,0,0,0,142,0,0,0,0,0,0,0,0,0,0,21,466,23, -257,0,0,0,0,0,0,77,404,0,0,0,0,0,0,712,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,860, -1848,0,0,652,629,0,0,0,0,13,377,0,1842,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1501,0, -0,0,1906,0,0,0,0,0,0,0,0,0,0,0,0,0,491,234,171,0,0,0,0,631,1186,0,0,0,0,0,0,0,0, -0,0,0,0,931,0,170,0,0,0,0,0,0,0,0,0,0,1587,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -765,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,424,0,0,714,0,0,0,0,685,0,0,0,0,0, -0,285,0,0,0,0,0,0,429,0,0,0,0,0,0,0,0,0,0,71,18,0,0,0,0,0,0,0,0,0,0,116,828,0,0, -0,0,0,0,289,0,0,0,0,0,0,0,0,675,0,0,0,1424,0,0,0,0,0,647,0,0,0,1334,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,36,209,0,0,0,0,0,0,0,342,0,0,0,928,0,0,0,0,0,1838,118,856,654, -318,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,915,895,454,0,0,513,1425,0,0, -0,0,0,0,791,0,153,0,0,0,0,0,0,796,909,445,345,0,0,0,0,0,0,0,0,578,0,0,0,1387,0, -0,0,555,0,0,0,0,0,0,766,0,0,0,0,0,0,0,0,0,0,541,0,0,0,0,0,0,0,0,0,0,0,0,0,880,0, -0,0,0,0,1506,0,0,983,0,768,0,0,0,0,584,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,737, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,30,426,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,462,0,0,0,385,0,398,0,0,0,0,0,0, -0,0,0,347,0,0,0,0,125,1259,644,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,469,0,0,0,0,0, -1367,0,0,0,0,0,0,0,0,0,0,0,719,0,0,0,0,0,0,0,0,0,0,0,0,0,1423,0,0,0,0,0,0,0,0,0, -749,0,0,0,0,546,645,0,0,0,0,0,0,277,0,0,1275,0,0,0,0,0,0,0,453,536,555,0,0,987, -1107,0,0,90,0,0,0,0,0,0,0,0,860,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -257,0,1768,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1071,0,0,0,0,0,0,0,0,0,0,0,0,0,83, -0,835,0,0,0,0,0,0,0,2006,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,696,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,95,1718,0,0,0,0,0,0,0,26,0,550,0,0,0,0,0,901,0,0,0,0,0, -0,822,0,0,122,0,0,0,807,0,0,0,0,0,262,0,620,601,34,0,0,170,0,0,0,0,537,0,0,0,0, -0,0,0,0,0,332,0,0,208,1909,182,261,0,0,0,1721,0,0,0,0,0,933,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,1609,0,895,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,812,0,0,942,1916,0,0,0,0, -0,0,0,778,0,0,0,137,0,1314,0,0,0,0,0,0,0,1661,0,0,0,0,0,0,0,1591,0,0,0,0,0,0, -820,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,89,0,1160,230,6,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,63,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1740,0,0,177, -170,0,1961,0,0,0,0,0,0,0,0,0,0,0,0,91,0,17,44,0,0,0,0,0,0,0,0,0,270,0,296,0,0,0, -0,0,0,0,1523,0,0,0,0,0,0,0,0,0,0,757,7,0,0,0,0,0,0,0,0,0,0,530,588,0,0,0,0,0,0, -0,0,0,786,0,0,0,0,0,580,627,88,447,57,0,0,0,0,0,0,0,0,845,735,0,0,0,0,0,31,15,0, -460,521,12,424,0,0,0,1302,0,0,0,0,0,0,0,595,0,0,0,13,548,97,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,1472,452,1767,0,0,0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,1543,0,1111,0,0,0,0, -1,0,359,488,0,267,0,0,0,1983,0,0,0,0,0,0,0,1155,0,1575,0,1438,31,0,0,377,101,0, -0,0,0,0,0,0,0,0,0,0,0,0,476,0,0,0,0,0,0,0,0,2023,0,0,0,0,0,1836,0,0,0,0,35,843, -0,0,0,0,0,0,0,554,0,0,0,536,625,207,0,1371,0,0,0,424,785,336,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,896,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,750,0,0,0,0,238,0,0, -0,0,0,383,0,0,0,0,0,0,0,0,603,725,11,0,0,0,0,0,0,0,0,0,476,0,0,0,0,0,1552,0,0,0, -0,0,0,0,680,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,435,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1431,0,0,13,112,0,0,356,0,0,0,0,0,0,0,0,0,0,1963,0,0,0,1244,18,0,0,0,0,0,0,867, -0,0,0,0,0,0,50,708,73,592,0,502,0,0,0,0,0,0,161,347,0,0,0,0,470,33,0,246,571,10, -0,465,614,0,237,0,0,0,0,0,24,18,0,506,0,0,0,0,0,0,33,309,0,0,0,0,0,0,0,0,0,0, -140,0,0,0,0,1056,0,0,0,1704,0,0,0,0,0,0,0,1036,0,0,0,0,0,0,0,0,0,1315,432,86, -264,524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,0,0,0,0,0,123,927,0,0,957,1149,0,0, -0,0,0,778,0,502,196,0,0,0,0,1312,0,0,0,0,0,0,0,855,0,0,0,0,0,0,0,0,0,0,45,1400, -0,0,0,1003,0,0,0,0,0,1097,0,0,0,0,0,0,0,0,545,612,0,0,0,0,0,0,0,0,0,0,0,0,54,0, -0,0,0,172,0,0,0,1029,0,0,0,0,0,0,0,0,0,568,0,0,0,732,617,0,0,974,94,989,733,0,0, -0,0,0,0,1789,0,0,665,2015,0,0,0,0,0,0,806,287,0,0,0,0,0,1539,0,0,0,0,0,0,0,0,0, -0,182,1563,0,0,0,0,0,0,0,0,0,484,0,0,0,0,0,1623,0,0,0,0,0,0,0,0,878,1833,0,1569, -0,0,0,0,0,0,0,0,93,0,715,994,0,0,0,0,0,63,0,591,0,0,0,0,0,0,0,749,0,0,0,0,547, -366,0,0,0,1747,0,0,0,0,0,0,0,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1463,0,772, -893,0,0,0,48,0,0,941,0,0,690,1785,106,440,0,0,0,0,0,0,0,0,0,0,32,0,332,216,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,852,0, -0,416,564,0,918,0,1764,0,0,3,0,0,274,0,0,0,0,501,0,0,0,0,0,0,0,851,743,0,49,0, -879,0,0,47,0,0,0,0,0,0,865,0,1202,0,0,0,0,0,0,47,272,0,0,0,0,0,0,0,0,0,0,0,1455, -0,0,0,0,891,1911,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,761,0,0,0,0,0,0,0,0,0,407,0, -183,0,0,490,0,0,0,0,0,0,0,35,731,0,0,0,0,0,0,0,819,0,0,0,0,0,0,0,0,0,0,0,0,0, -575,0,0,0,0,45,818,0,0,77,222,0,0,0,0,849,1880,0,0,0,633,0,1308,0,0,0,0,0,0,0,0, -0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,817,0,0,0,0,0,0,0,0,0,882,0,0,0,914,0,0,0,0, -0,0,0,0,0,0,865,0,0,426,399,58,0,0,0,0,0,0,538,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,876,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,566,0,63,12,0,0,0, -0,0,0,0,0,0,0,0,0,0,3,114,0,0,0,0,0,0,0,0,576,0,0,0,0,0,0,0,0,933,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,692,0,0,0,0,0,0,0,0,0,0,0,0,752,0,0,0,0, -0,0,0,0,375,0,1011,0,0,96,0,0,0,0,0,0,0,0,0,148,0,0,0,0,0,0,0,0,0,0,0,337,56, -666,0,246,394,0,0,0,0,0,0,0,0,437,0,0,0,506,0,0,0,0,1003,0,1163,0,328,0,0,0,0,0, -0,0,0,1000,0,0,0,0,0,744,101,0,0,0,0,0,726,0,0,176,0,146,9,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,839,0,0,0,0,0,0,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,246,1931,29,0,0,1771,0,0,0,0,0,846,6,157,0,0,0,0,0,0,0,0,0,875,0,0,477, -773,177,639,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1747,0,0,0,0,158,873,0,659,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,391,0,0,0,0,0,0,0,0,0,0,0,0,668,883,0,78,628,0,0,0, -0,0,0,0,0,0,0,0,0,1460,0,962,0,0,0,0,0,460,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,199,0, -0,0,388,474,0,271,0,333,608,0,0,0,0,0,0,49,0,988,0,707,617,0,0,0,0,0,0,0,756,0, -0,0,0,0,1583,0,0,0,0,0,0,0,0,0,0,285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,344,0,0,0,0,0, -0,0,0,515,1709,0,0,0,0,0,0,0,0,404,0,0,0,0,500,0,0,0,0,0,0,0,0,0,68,216,0,0,0,0, -0,0,0,488,353,0,0,177,236,0,0,458,490,0,0,0,0,0,0,756,1504,0,757,0,1735,0,0,108, -598,0,0,0,0}; -BROTLI_INTERNAL const uint8_t kStaticDictionaryHashLengths[32768] = { -8,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,12,0,0,0,0,4,22,5,0, -4,0,0,0,0,0,0,0,0,0,0,0,0,14,6,0,0,0,5,0,0,0,0,0,0,0,7,13,0,0,4,0,0,0,0,0,0,0,0, -0,6,0,0,0,0,8,0,0,0,0,0,0,7,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,4,0,0,0,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,10,4,0,5,13,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,8,7,0,0,9,0,8,0,0,0,0,0,0,6,0, -0,9,0,0,0,11,0,0,6,8,7,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,6,8,0,0,0,0,0, -0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,9,0,0,0,8,4,13,7,0,0,0,0,0, -7,0,5,0,0,0,0,8,5,0,5,0,0,8,7,0,0,0,0,0,0,0,0,0,0,9,0,0,0,8,0,0,0,10,4,0,5,0,4, -0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,8,7,0,4,9,4,0,0,0,0,0,0, -9,0,0,0,8,5,0,0,0,6,0,0,0,0,0,0,0,0,0,7,18,0,0,0,0,4,9,0,0,4,0,6,0,0,0,6,0,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,5,8,7,0,0,0, -0,9,0,0,0,0,0,0,0,8,6,10,6,0,0,0,4,0,6,8,6,0,0,0,4,0,0,0,0,0,5,0,0,0,6,0,0,0,0, -10,0,12,7,0,0,0,0,0,4,0,0,0,0,0,5,0,0,8,7,0,0,0,0,0,0,0,0,9,5,0,0,0,0,0,0,0,0,0, -0,0,0,0,6,11,0,0,0,0,0,0,0,0,0,8,7,0,0,10,0,0,0,0,0,0,0,0,6,10,0,17,0,8,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,8,6,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -7,0,0,11,4,0,5,0,0,0,0,0,0,0,0,0,0,10,5,0,6,8,5,0,0,0,0,0,0,0,0,0,0,11,5,0,0,0, -0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,9,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,8,7,0,0,0,0,0, -0,0,0,0,0,0,5,0,0,0,6,0,0,10,0,0,0,20,0,0,0,0,0,0,0,0,6,9,5,0,0,0,0,10,4,8,0,0, -4,13,0,0,0,0,0,0,0,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,4,8,6,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,12,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,12,5,0,0,10,4,10,7,13, -0,0,0,0,0,0,0,0,6,0,6,0,6,0,0,0,0,0,0,19,0,0,4,12,6,9,0,0,0,0,4,0,4,11,0,0,0,0, -0,0,0,12,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,4,0,0,0,0,0,0,0,0,0,6,0,0,0,0, -0,5,0,0,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,9,6,0,0,0,0,0,4,0,4,0,0,0,0,0,0,0,0,0,4,0,0,0, -6,0,0,0,0,0,0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,6,0,0, -0,0,0,5,0,0,0,0,14,4,0,0,0,4,12,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,6,0, -0,0,0,0,0,12,0,9,6,0,0,0,0,13,0,0,5,0,0,0,0,0,4,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,13,0,9,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,5,0,0,0,6,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,8,7,8,4,0,0,0,0,0,0,0,0,0,0,0,7,0,7,0,0,0,4,0, -0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,6,8,4,0,0,0,0,0,6,0,7,0, -0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,7,0,0,0,0,0,0,9,5,0,0, -0,0,0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,9,4,0,0,0,0,0,0,0,4, -12,5,11,0,0,0,0,0,0,0,0,0,8,7,0,5,0,0,8,7,0,5,0,0,0,0,8,0,0,0,0,7,0,4,10,0,0,0, -0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -13,5,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,0,14,5,0,0,0,7,0,0,10,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,6,0,4,0,5,0,0,0,0,8,5,0,0,0,0,0,0,9,5,9,0,0,0,0,0,0,0,0,6,9,0, -0,4,0,0,0,7,0,0,0,6,0,0,10,4,0,0,0,0,0,6,0,0,10,0,0,0,8,5,0,0,0,0,0,0,0,0,10,0, -0,0,0,0,18,4,12,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,8,7,0,0,0, -0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,4,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, -0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,8,0,0,0,0,0,0,6,0,0,0,4,10,5,0,0,0,0,0,0,0,0,0,0, -0,4,8,7,0,0,8,6,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0, -0,0,0,8,6,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0, -0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,8,7,0,0,0,0,8,0,12,6,0,6,0,0,0,0,9,7,11,7,0,0,0, -0,0,0,0,0,0,0,0,0,11,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,10,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0, -0,0,0,6,0,0,0,7,0,4,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,14,0,0,0,0,0,8,4,0,4,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,20,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,12,5,0,7,0,5,0,0,10,0,0,7,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,6,0,4,9,7,0,0,0, -0,0,7,0,0,0,0,0,0,10,0,9,0,9,0,0,0,0,0,0,0,0,4,9,0,0,0,0,6,0,0,0,0,0,0,0,0,11,4, -0,6,0,0,0,0,0,0,8,0,8,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,0,0,0,0,0,0,0,0,13,6,0,0,11, -0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,6,18,0,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0, -0,5,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,6,0,0,0,0,9,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,11, -4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,4,0,0,0,0,8, -6,0,0,0,0,0,0,9,6,0,0,0,0,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0, -0,6,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,6,0,6,0,0,10,6,0,0,0,7,0,0,8,0,8,7,0, -0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,0,0,0,0,5,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0, -0,0,0,8,7,0,0,0,0,0,0,0,0,12,0,12,0,0,0,11,6,0,5,0,0,12,0,12,5,0,7,11,6,0,0,11, -0,0,0,12,0,0,4,12,7,8,6,0,0,0,0,8,5,0,0,0,0,0,0,0,4,11,0,0,6,0,7,0,0,0,0,0,0,0, -5,0,6,0,0,0,0,8,0,10,0,0,0,0,0,0,0,0,0,0,0,9,7,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0, -0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,0,0,10,0,0,5,0,0,12,6,0,0,0,0,0,0,10,6,0,0,0,0,8, -6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,5,0,0,0,0,11,0,10,6,0,0,8,6,0,0,0,6,0,7,10,6,0, -0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,10,7,0,0,0,0, -10,6,0,0,0,0,0,0,8,5,11,0,8,4,0,0,0,4,0,0,0,0,9,4,8,0,0,0,0,0,0,0,11,6,0,0,0,0, -10,7,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,7,0,0,0,0,9,6,0,5,0,7,0,0,0,0,0,7,0,0,11,0,0, -0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, -0,0,0,0,13,0,8,6,13,0,0,0,11,7,0,7,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,6,0,0,9,6,0,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0, -0,0,0,0,0,5,9,0,0,0,0,0,0,0,0,0,0,4,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,9,7,0,7,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0, -5,11,5,0,0,0,0,0,0,0,0,0,4,0,7,0,6,0,0,0,6,20,0,0,0,10,7,0,5,14,4,0,0,0,0,0,0,0, -0,0,6,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0, -0,0,6,0,4,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0,0,11,6,15,0,0,0,0,0, -10,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,7,0,0,0,0,0,0,0,0,9,7,13,0,0,0,0,0, -0,7,0,0,8,6,0,0,0,0,0,0,0,0,9,4,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,5,0,0,0,0,0,0,0,0,0,0,0,0,8,5,0,4,0,0,0,0,0,0,0,0,0,0,12,6,8,0,12,0,0,7,0,0,0, -0,0,5,10,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7, -14,0,0,0,0,0,0,0,0,0,0,0,0,5,0,5,8,7,10,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,18,6, -14,7,0,0,0,0,0,0,0,0,11,6,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,11,7,0,0,10,7,0,0,0,6,8,6,0,0,0,0,0,0,0,6,0,0, -19,0,0,0,9,5,0,0,0,0,0,0,11,7,0,0,0,7,0,6,0,0,11,0,0,0,0,4,8,0,0,0,0,0,0,0,0,6, -0,0,0,0,0,6,0,0,8,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,7, -0,7,0,0,0,7,15,0,0,5,0,0,0,0,10,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,7,0,0, -0,0,0,0,0,0,9,6,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -11,7,0,0,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0, -0,0,5,0,4,0,0,0,4,0,4,0,0,0,0,0,0,0,0,0,6,0,0,0,0,11,6,0,0,8,5,14,0,0,4,0,0,0,7, -17,0,0,0,0,0,0,0,13,5,0,0,0,0,0,5,0,0,0,5,0,0,0,0,16,6,0,4,0,0,0,0,0,0,12,0,0,0, -0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,5,0,5,0,6,10,0,12,0,0,0,0,0,0,0,0,7,0,0,0,0,8,4, -0,0,0,0,0,0,0,0,0,0,8,7,0,0,8,0,0,0,8,0,0,6,0,7,0,0,0,5,0,6,0,4,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,22,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0, -0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,18,0,0,0,9,4,0,0,8,0,9,7,0,0,0,0,0,0,8,6,0,0,0,0, -0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,9,7,0,0,0,6,0,0,14,0,0,0,0, -0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,7,10,4, -0,6,0,0,0,0,0,0,8,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,6,0,0,0,0,0,0, -0,0,11,6,12,7,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,0,9,6,11,6,0,0,0,0,9,5,0,0,0,0,0,0, -0,6,8,5,0,0,0,0,8,0,10,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9, -5,10,7,0,0,0,5,8,7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,4,8,7,0,0,0,6,0,0, -0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,22, -0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,5,0,0,0,0,0,0,0, -0,0,0,0,0,17,0,0,6,0,6,12,4,19,6,0,0,0,0,16,0,0,0,0,7,15,7,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,4,10,4,0,0,8,7,0,7,0,0,9, -4,0,6,0,0,0,4,0,5,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,7,10,0,0,0,0,0,11,7,0,0, -0,0,12,6,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, -0,0,0,0,0,0,0,0,0,10,4,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,8,7,0,0, -0,0,0,0,0,6,0,0,0,4,0,0,11,4,0,0,12,7,0,0,0,0,9,0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0, -4,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,9,4,0,6,0,0,0,0,0,4, -0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0,0,0,5,0,0,0,0,0,0,0,0,0,7,9,6,0,7,0, -0,0,0,0,0,0,6,0,0,0,0,8,6,0,0,0,0,10,6,11,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,5, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,5,0,4,8,0,0,0,0,0,9,7,0,0,0,0,0,0, -13,5,0,0,0,0,8,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,5,0,0,11,7,0,0,0,0,0,0,8,6,0, -0,0,0,0,7,0,4,0,0,0,0,0,0,0,5,0,6,0,5,0,0,0,0,0,0,0,0,0,0,0,0,10,4,9,0,0,0,0,0, -0,4,0,0,0,0,10,5,10,7,0,0,0,0,0,0,0,0,16,7,0,0,0,0,0,7,0,0,0,0,11,0,0,0,0,0,0,0, -0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,5,0,4,0,0,0,7,0,0,0,0,0,0,13,0,0, -0,0,0,0,0,0,0,0,7,0,4,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,13,7,0,7,0,4,16,0,0,0,0,6,8,7,9,7,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0, -0,6,0,0,8,5,0,4,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,11,7,0,0,11, -0,0,0,0,0,9,5,0,4,0,0,0,0,9,7,8,6,0,0,0,0,0,0,10,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0, -0,7,0,0,0,0,0,0,0,0,0,0,0,4,10,6,0,7,0,0,0,0,0,0,0,5,0,0,0,0,0,0,10,7,10,0,0,0, -0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,7,0,6,8,7,12,4,0,0,0,0,0,0,0,5,14, -0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0, -6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0,0,20,4,0,0,0,7,0,6,0,0,0,0,0,0,0,0,8,0, -0,6,15,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,12,0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,5,0,0,0,0,0,0,8,6,0,0,18,0,0,0,10,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,9,6,0, -6,0,0,0,0,0,0,0,0,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,9,0,9,0,0,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,9,5,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,10,0,0,0,0,7,0,0,0,0,0,0,0,0,0,7,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,0,8,0,0,0,16,0,0,0,0,0,0,0, -0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,8,0,0,0,11,0,0,0,0,0,0,0,0,0,0, -6,0,0,0,0,11,0,0,0,9,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,7,0,7,0,6, -0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6, -0,0,0,0,0,0,0,6,0,0,18,0,8,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,7,0,4,0,0,0, -0,0,0,0,0,0,0,8,0,0,0,0,0,16,0,0,0,0,0,16,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,18,0,0,0,0,0,0,0,0,0,9,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,4,0, -0,0,0,0,0,0,0,9,4,0,0,0,0,12,5,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,12,5,0,0,0,0,0,0,0,5,0,0,10,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,9,0,0,0,11,0,0,6,0,6,0,0, -0,7,0,0,0,0,0,0,8,0,0,0,0,6,0,0,0,0,0,0,19,0,0,0,12,0,9,0,0,0,0,0,10,7,0,0,0,0, -0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,16,7,12, -0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,6,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,10,5,0,0,0,0,0,0,0,4,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,7,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,0,4,0,0,9,0,0,0,8,0,12,4,0,0,0,0, -0,4,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,5,0, -0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,8,6,0,6,0,0,0,0,0,0, -0,4,0,0,0,0,0,6,0,0,9,0,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,10,6,0,0,0,0,8, -6,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,7,0,6, -10,7,0,0,10,5,11,6,0,0,0,0,0,7,16,0,0,0,0,6,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0, -0,0,0,0,0,8,7,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -8,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,12,7,0,7,0,0,0, -0,0,0,0,6,0,0,0,0,9,0,0,0,23,0,0,0,0,0,10,5,0,0,0,0,0,0,0,0,0,4,0,0,11,7,10,0,0, -0,0,0,0,0,0,0,0,0,0,6,0,0,8,7,0,7,0,0,8,7,8,0,0,0,0,0,0,0,0,0,0,0,14,5,0,0,0,0, -0,0,0,0,18,6,8,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,11,0,0,0,9,7,12,6,0,0,0,0,0,0,0,0, -0,0,12,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,7,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,8,7,0,0,0,6,10,0,0,0,9,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,6, -10,7,0,0,0,7,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0, -0,0,0,8,7,8,6,0,0,11,7,10,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,4,8,7,0,0,0,0,0,0,0,0, -0,5,0,0,13,0,0,0,0,5,0,0,9,7,0,0,0,0,0,0,0,4,0,0,11,0,0,7,0,0,0,0,0,0,0,0,0,6,0, -0,0,0,0,0,12,7,19,0,8,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,10,6,8,0,0,0,0,0,0,0,0,0,0, -6,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,7,0,0,12,0,0,0,0,6,9,6, -14,0,0,0,0,0,0,6,0,5,0,0,8,7,0,0,0,6,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,4,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0, -7,0,0,10,0,9,7,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,12,6,0,0,0,0,0,5,0,6,0,0,0,0, -0,0,0,0,0,0,0,6,0,0,0,0,9,7,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,11,7,0,0,13,7, -0,0,0,0,0,0,0,0,12,0,0,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,6,11,5,0,5,13,0,8,0, -0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,11,5, -9,6,0,0,0,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,10,0,0,0,8,5,0,0,9,0,0,0,8,7,9,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,0, -0,11,0,13,6,0,0,9,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0, -0,0,0,0,0,5,21,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,5,0,0,0,0,0,0,0,0,10,0,8,0, -0,6,0,0,0,4,0,0,9,0,0,0,0,0,0,0,0,0,0,4,0,0,8,6,0,6,0,7,10,0,8,4,0,4,0,0,0,0,0, -5,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,6,12,0,0,7,0,0,0,5,0,0, -0,0,0,0,0,0,0,6,0,0,8,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,24,7,0,0,0,0,0,0,0,0,0, -7,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,4,12,0,0,7,0,0,0,0,0,5,0,0,0,0,0,0,0,0,15,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,8,0,0,0, -0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,9,0,9,6, -0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,8,4,0,7,0,0,0,0,0,0,0,0, -22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,7,0,0,21,7,0,7,9,6,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,8,0,0,6, -0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,23,0,0,0,0,7,0,0,0, -4,0,0,0,0,0,0,0,0,9,4,11,7,0,5,0,0,0,0,11,0,0,4,20,0,0,0,0,0,0,0,0,0,0,0,11,5,0, -7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -21,0,0,0,0,0,0,7,0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,11,6,0,0,0,0,0,0,0,0,9,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,5,0,4,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0, -0,0,0,10,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,7,0,0,11,7,0,0,0,0,0,0,0,4, -0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,8,7,0, -0,0,0,0,0,0,0,0,6,0,0,21,6,0,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,14,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,8,0,0,7,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0, -0,0,0,8,7,0,0,11,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0,7,13,7,10,4,0, -0,0,6,0,0,0,0,0,0,0,0,0,5,10,0,0,0,0,0,0,5,0,0,0,7,0,0,0,0,0,0,8,4,0,0,0,0,0,6, -0,0,0,0,0,0,0,0,0,0,12,7,0,6,0,0,10,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0, -0,0,0,0,7,0,0,8,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,10,5,0,6,0,0,0,0,0,4,0,0,0,0, -0,0,0,0,0,4,0,0,0,0,9,0,11,4,0,0,0,6,0,0,0,5,12,7,0,5,0,0,0,0,0,4,0,0,0,7,0,0,0, -0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,13,6,10,0,0,0,17,0,0,4,0,0,0,0,0,6,0,4,0,5,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,11,7,0,0,0,7,0,0,0,6,0,0,0,0,0,0, -0,6,0,4,0,0,0,0,8,0,0,0,0,5,0,0,0,0,0,4,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,9,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,0,0, -0,0,7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,16,4,0,0,11,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -8,7,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,8,6,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10, -7,0,0,0,0,0,0,9,0,0,0,0,0,0,0,12,5,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0, -5,18,7,0,0,14,0,0,0,0,0,0,0,9,4,0,7,0,0,0,0,0,0,0,5,0,0,0,6,0,0,0,6,0,0,0,0,0,0, -8,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0,7,0,0,0,0,0,0,11,0,0,0, -10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,14,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,14,6,0,0,0,0,11,4,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,10,7,0,6,0,0,9,0,9,5,0,0,0,0,0, -0,0,0,10,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,8,5,0,0,0,0,0,0,0,0,0,0,11,4,0,6, -0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,12,4,0,6,8,6,0,0,0,0,0,0,0,0,0,0,8,0,0,5,0,0,0,0,0,0,0,7,0,0,13,0,0,0,0,0,0,0, -0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,12,7,0,6,0,0,0, -0,0,0,0,0,0,0,0,0,13,4,0,7,0,0,0,7,0,7,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,0,0,0,0, -9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,10,6,21,5,0,0,0,0,8,0,0,0,0,4,0, -7,0,0,0,0,0,0,11,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,4,0,0,0,0,0,0, -0,7,9,6,11,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,7,10,0,0,0,0,0,0,6,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,19,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,9,4,10,4,0,7,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,9,7,9,7,10,4,0,7,0,0,0,0,0,0,0,6,12,0, -0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0, -0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,8,0, -0,0,0,0,0,5,0,0,8,7,0,0,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0, -0,0,0,0,4,0,0,8,0,0,6,0,0,0,7,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,7,9,7,0,0,0,4,8,0,0,0,0,6,11,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,10,0,0,0,0,0,0,0,13,4,0,0, -12,6,0,6,0,0,0,0,8,7,0,7,0,0,0,0,0,6,0,0,0,0,0,0,12,6,0,4,0,0,0,0,0,0,0,0,0,0,9, -7,22,0,0,0,0,4,0,0,0,0,0,6,0,0,0,4,0,0,9,0,0,6,0,0,24,7,0,7,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,10,6,0,5,0,0,0,0,0,0,0,7,0,0,8,0,0,0,0,0,0,0,10,5,0,0,0,0,0,0,0,0,0,7,0, -7,0,0,0,0,0,0,13,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0, -0,0,0,0,0,7,12,0,9,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,4,0,0,0,7,0, -0,0,0,8,7,0,0,0,0,0,0,0,0,0,4,18,0,0,0,0,0,10,0,0,5,0,0,11,0,0,0,0,0,0,5,0,6,0, -0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0, -4,0,0,0,0,0,0,10,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0, -0,0,0,5,8,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,20,7,0,0,0,0,0,0,0,0,0,0,0,4,9,0,12, -6,8,0,14,7,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,10,0,0, -0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,9,6,0,7,12,0,0,0,0,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7, -0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,4,0,0,9,0, -12,6,0,5,0,0,0,6,0,4,0,6,0,0,0,0,0,0,0,0,10,7,0,0,0,0,0,0,8,0,0,0,0,4,0,0,0,0, -10,0,0,0,0,0,0,0,8,6,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,5,0,0,0,0,0,0,0,0,0,0,0, -6,0,0,12,6,20,5,0,0,0,0,0,0,0,0,0,0,0,0,9,5,0,5,0,0,0,6,13,7,0,0,0,0,15,6,0,0,0, -6,0,0,13,7,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,5,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0, -10,6,0,0,0,0,0,6,0,0,0,0,9,0,0,0,0,0,19,6,0,0,0,0,0,0,0,0,0,0,13,0,11,0,0,0,0,0, -0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,0,10,0,0,6,0,0,0,0,8,0,0, -0,9,0,15,4,0,6,0,0,0,0,0,6,12,0,0,0,0,0,0,0,14,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, -0,0,0,0,0,8,7,0,0,0,0,0,6,10,0,0,0,0,0,0,0,0,7,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,7,10,5,0,0,0,0,8,0,0,0,0,4,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,6,12,0,0,0,10,7,0,5,0,6,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,6,0,4,0,0,0,0,0,7,0,0,0,0,0,0,0,4,9,6,0,0,0,7,0,0,0,0,0,0,0,0,8,6,0,0, -0,0,0,0,0,4,12,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,7,0, -0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,12,6,0,6,9,4,0,0,8,4,0,6, -0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,8,0,0,6,13,4,0,5,8,0,0,0,0,0,0,0,8,0,0,0,10,5,0,0,9,0,0,0,0,0,0,6,0,0, -24,0,0,0,0,0,0,0,8,0,0,7,0,0,12,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0, -6,8,0,10,0,9,7,0,0,0,5,0,0,0,0,0,0,0,4,8,5,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,4,0,0,0,0,0,6,0,0,0,0,0,5,0,0,0,0,8,0,0, -0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,10,4,0,0,0,0,0,0,0,6,0,0,0,4,20,0,0,7, -10,6,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,9,6,0,0,0,0,0,0,0,4, -12,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,9,4,0,5,0,0, -0,0,0,0,0,6,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,6,9,0,0,0,0,7,0,0,0,0,0,6,0,5,0,0,0,0,0,0,0,0,9,0,0,0, -0,6,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,17,7,0,0,13,6,14,6,0,0,0,0, -8,0,0,0,0,0,0,7,12,7,8,7,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,4,0,0,0,0,0,4,0, -0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,12,4,0,0,10,7,0,0,0, -0,0,0,10,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,0,0,6, -0,0,0,0,0,0,0,0,8,7,12,0,0,0,0,0,0,6,0,6,0,4,0,0,18,6,0,0,0,6,0,0,0,0,0,6,10,6, -0,0,0,0,0,0,8,7,14,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19, -0,0,0,8,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,7,0,0,10,5,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, -0,0,9,4,8,0,0,0,0,0,0,4,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,0, -0,6,0,0,9,7,0,0,0,0,0,5,0,0,0,0,8,7,0,0,14,0,0,0,0,6,0,0,0,0,0,0,9,6,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,5,0,7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0, -0,0,0,6,0,0,0,6,0,4,0,0,0,0,0,4,0,0,0,0,12,0,0,7,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0, -0,12,0,16,6,0,0,0,0,0,0,11,7,0,4,8,7,0,0,0,0,0,6,0,0,0,0,16,0,0,0,0,6,0,0,0,0,0, -0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,10,7,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0, -0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,13,4,0,0,10,0,0,0,0,0,0,0,0,0,19,0,0,0, -0,0,0,0,0,0,0,0,0,0,8,6,22,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0, -5,0,0,0,0,0,5,0,0,0,0,0,5,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -4,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0,0,18,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,14,7,0,0,11,5,0,0,0,5,0,0,0,0,12,5,0,0,0,0,0,0,0,0,0,0,24,6,0,0, -0,7,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,7,0,4,0,0,0,0,8,7,0,0, -9,6,0,0,14,5,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,12,6,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,7,0,0,0,5,0,0, -0,0,12,7,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,6,0,0,13,7,0,0,0,0,0,0,14,0,11,4,0, -0,0,4,0,0,0,0,14,5,0,0,0,0,0,5,11,5,0,0,0,0,22,5,0,0,0,0,0,7,0,0,0,0,0,4,0,0,0, -4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,17,0,10,0,0,0,8,0,0,0,19, -5,18,7,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,10,6,0,6,0,0,0,0,10,4,0,4,0, -0,0,0,0,0,14,7,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,0,9,6,12,0,0,6,0,0,0,0,0,0,0,0, -12,0,10,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,4,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,13,0,9,7,0,0,0,0,0,0,0,0,0,0,0,7,9,7,0,0,8,0,0,0,0,0, -22,0,0,0,0,0,0,0,23,6,14,0,0,0,0,0,0,7,0,0,0,0,11,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0, -0,0,10,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,8,5,0,0,0,0,0,0,0,0,0,7,11,6,21,0,0,0,0,0, -0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0, -0,0,0,0,0,0,0,4,9,7,0,0,0,0,0,0,12,0,0,0,0,7,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,9, -0,0,0,20,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,11,7,0,0,0,0,0,0,0,6,15,0,0, -0,0,0,0,0,0,0,0,0,0,0,12,4,0,5,0,0,0,0,0,0,11,7,17,6,0,0,0,0,0,0,15,6,0,7,0,0,0, -0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,6,0,5, -0,0,11,0,11,7,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0, -17,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0,8,7,9,6,0,0,14,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0, -8,7,0,4,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0, -0,0,0,5,0,4,0,0,8,7,0,6,12,5,0,7,18,7,0,0,8,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0, -10,0,11,0,0,0,0,0,0,0,0,0,0,0,9,0,0,4,0,6,0,7,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0, -7,0,0,0,0,8,0,0,0,15,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,0,0, -0,0,6,0,0,0,0,23,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,5,0,0,0,0,0,0,8,6,0,0, -0,0,0,0,12,7,9,7,0,0,10,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,9,0,8,7,0,0,0, -6,0,6,0,4,0,5,0,0,0,0,0,5,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,7,10,5,0,0,11,6,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,4,9,7,0, -0,0,0,11,7,0,0,0,0,0,5,0,0,0,7,0,0,0,0,23,6,11,4,0,0,0,0,0,0,9,0,0,0,10,6,0,0,0, -0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,10,6,0,0,0,7,0,0, -0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13, -6,11,7,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0,6,0,0,0,5,0,6,0,6,0,0,0,0,0,0,0,0,0,0, -0,6,0,0,0,0,8,7,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,4,10,0,8,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,10,6,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0, -0,0,0,0,0,0,10,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,11,6,0,4,0,0,14,5,0,7,0,0,0,0,0,6,16,0,0,0,0,0,0,0,10,0,0,7,15,0,0,0,11,7,0,0, -0,0,0,0,0,0,0,0,8,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,5,0,0,0, -0,8,0,0,6,0,0,0,0,0,0,9,5,0,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,6,0, -0,0,0,0,0,0,7,0,0,0,0,15,7,0,0,0,0,8,0,0,0,14,0,0,0,0,0,0,0,16,7,0,0,0,0,0,7,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,12,6,11,7, -9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13, -7,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,12,0,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,8,0,0,5,8,7,10,6,0,0,0,7,0,0,0,0,12,6, -0,0,9,0,0,0,12,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,10,0,0,0,10,5,0,0,0,0,0,0,9,6, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,6,0,0,9,5,0,4,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,9,0,0,5,0,0,8,7,8, -6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,10,0,9,4,0,0,0,0,0,0,0,6, -11,0,0,0,0,0,0,0,0,0,0,0,8,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,8,7,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, -0,0,0,10,0,0,0,8,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0, -0,0,8,4,0,5,0,0,0,0,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,8,5,0,0,0, -0,0,0,0,7,0,0,0,6,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,0,6,0,7,0,0,0,0, -20,0,0,0,0,0,0,0,0,0,0,7,9,0,0,0,0,0,0,6,0,6,0,7,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0, -0,0,0,14,7,0,0,0,5,0,0,22,4,10,0,0,0,0,0,0,4,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,11,5,13,0,0,0,0,0,0,0,0,0,8,0,0,7,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,10,7,0, -0,0,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,7,0,7,14,6,0,0,0,0,9,5, -0,0,0,0,0,6,0,0,0,5,10,0,8,6,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,6,0,0,8,4,0,6,0, -0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7, -14,0,0,5,0,0,18,0,8,4,0,6,0,0,20,0,13,0,0,0,0,7,0,4,0,0,0,0,0,4,8,4,0,0,0,0,0,6, -0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,0,14,0,0,0,0,0,9,7,0,0,9,0,0,0,0, -0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,20,0,14,0,0,4,0,6,8,5,0,0,0,0,0,7,0,0,0,0,0,0, -0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,10,4,12,7,0,6,0,0,9,7,10,5, -0,0,8,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,18,0,0,0,14,7,0,0,0,0,0,4, -0,0,0,0,0,0,17,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,4,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0, -0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,8,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,5,0,7,0,0,0,0,0, -7,0,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,8,5,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,5,0, -0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,23,0,0,7,0,0,0,0,0,0, -0,0,0,0,0,0,0,4,0,0,0,0,0,0,12,7,8,4,0,0,0,0,0,0,0,0,0,6,0,0,9,5,0,0,0,7,0,0,0, -0,0,0,0,0,0,4,10,0,0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,0,0,18,7, -0,0,8,0,0,5,0,0,10,0,0,0,0,0,0,6,0,0,0,0,0,5,0,7,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0, -6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,6,0,0,10,0,0,5,10,4,0,0,12,0,0,0,0, -6,22,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,7,0,5,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,6,0,7,0,0,0,6,0,6,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,7,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, -0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,16,6,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,12,7,0,0,0,0,9,0,0,0,0,6,0,0,11,0,0,0,0,0,13,0,9,6,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,0,0,10,7,0,0,0,7,0,6,0, -0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,11,0,15,0,22,7,0,4,0,6,0,0,0,0,0,7,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0, -18,0,0,0,0,0,0,0,0,0,14,0,0,4,0,0,0,0,8,7,9,0,0,0,0,0,9,0,0,0,14,0,0,0,0,0,0,0, -0,0,11,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,7,0,0,0,6,0,6,0,0,0,0,8,0,0,0,0, -0,11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,9,4,0,0,0,0,0,4,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,8,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0, -0,0,0,0,0,0,8,6,0,0,9,5,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,5,0, -0,10,6,9,0,0,0,0,6,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0, -11,7,12,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0, -0,0,0,0,6,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,5,0,0,10,6, -0,0,0,4,0,7,13,0,0,4,0,0,11,4,0,6,0,0,0,0,0,6,8,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,5,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,5,0,0,0,0,12,6,0,0,0,0, -11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,11,5,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, -7,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,6,17,0,9,0,10,6,0,6,12,0,0,4,0,0,0, -0,0,0,0,0,0,0,8,5,12,7,0,4,0,0,0,0,0,0,0,0,0,0,11,0,9,0,10,6,11,5,0,7,0,0,8,0,0, -7,0,4,0,0,0,7,0,0,0,0,0,0,8,6,0,0,0,6,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,6,0, -0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,11,0,0,0,0,6,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,10,0,0,0,0,0,8,6,0,0,0,0,0,6,12,0,0,0,0,0, -0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,6,0,0,16,0,11,5,0,0,0,0,0, -0,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,10, -7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,0,9,5,0,0,0,0,8,0,9,0,0, -0,0,0,0,0,0,7,10,0,13,0,0,6,0,0,0,0,0,0,0,0,0,6,9,4,0,0,0,0,0,0,10,0,0,0,0,0,10, -0,0,0,0,0,0,0,10,6,11,0,0,0,0,0,9,0,0,0,0,0,0,4,0,0,0,0,0,0,10,5,0,0,0,0,0,6,0, -0,0,0,0,0,18,4,0,7,0,0,0,0,0,0,24,0,8,6,0,7,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0, -0,8,5,0,0,0,0,10,7,0,6,0,0,0,0,0,0,0,0,8,5,10,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0, -6,0,0,8,7,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,4,0,5,15,0,0,0,0,7,0,7,0,0,0,0, -0,0,0,0,0,6,10,5,0,0,0,6,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,12,0,0,0,0,0,0,0,0, -0,0,5,0,0,0,0,0,0,14,4,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,11,0,10,4,9,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,7,0,0,0, -0,0,0,0,0,0,0,0,7,13,7,0,0,0,0,0,0,0,5,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0, -0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,8,0,10,6,0,4,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0, -0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,9,7,0,0,0,0,0,6, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,6,0,0,0, -0,0,0,0,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0,0,0,5,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,11,0,0,0,0,6,0,0,0,0,0,0,0,6, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0, -6,0,0,0,0,0,0,0,6,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0, -0,0,0,0,0,0,0,6,0,6,0,0,0,5,0,0,0,0,0,0,0,5,0,0,10,0,11,5,0,0,0,0,0,0,14,7,9,7, -0,6,0,0,0,0,0,4,0,0,0,0,0,0,11,7,0,6,0,0,0,0,0,0,9,7,0,4,0,0,0,7,0,0,0,0,0,5,0, -0,0,0,0,5,0,0,0,7,0,0,0,0,0,5,0,0,0,0,17,5,0,0,8,0,0,0,0,6,9,4,0,0,0,0,0,0,0,0, -8,7,11,7,9,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,6,9,5,0,0,8,6,0,0,0,5,0, -0,0,0,9,0,0,0,9,6,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0, -0,0,0,0,4,0,0,0,0,10,0,0,0,0,0,0,0,0,4,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,4,0,0,0,5,0,0,0,0,0,7,0,0,0,0,0,7,13,5,0,0,0,7,0,0,0,0,0,7,9,6,11,7,0,7,0,0,0, -0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,8,5,0,0,0,5,9,4,0,0,0,0,0,0,0,0,8,4,0,0,0,0, -24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0, -0,0,0,0,6,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0,5,11,6,0,4,0,7,20,0,8,5,9,5,9,0,0,6, -0,0,0,0,0,0,0,0,0,0,0,7,23,5,0,0,8,4,0,0,10,0,0,6,0,5,0,0,0,0,0,0,0,0,0,0,0,7,0, -0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,9,0,0,0, -10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0, -6,0,0,0,0,14,0,18,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,9,6,0,4,0,0,0,0,0,0,8,4, -11,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,12,0,10,7,0,0,10,0,0,0,0, -0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,6,0,0,0,0,8, -6,10,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,4,0,6,0,4,0,0,0,0,0,5,0,0, -0,0,0,0,0,0,0,7,0,0,0,7,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,17,7,11,0,0,0,0,0,0,0,0,0,0,4,12,6,0,0,0,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0, -0,5,12,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,6,0,0,20,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,4, -0,0,0,5,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,6,0,4,13,0,0,7,0,0,0,0,0,0, -0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,12,6,0,7,0,0,0,0,10,0,23,6,0,0, -0,4,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -10,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,11,0,9,7,0,0, -0,0,0,0,0,0,0,0,9,7,0,4,0,0,0,0,8,7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, -0,0,0,0,0,6,0,0,10,7,10,5,0,0,8,0,8,0,0,0,0,0,0,4,0,5,10,0,0,0,0,0,0,0,9,0,0,6, -0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,11,7,0,0,0,0,0,0,0,0,9,4,0,0,0,0,0,6,0,0,8, -7,0,0,0,0,0,5,0,0,0,0,0,0,0,0,10,0,0,0,0,5,0,4,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,24,7,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,6,0,0,9,0,0,0,0,0,0,7,0,6,13,0,8, -0,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,6,0,0,0,0,8,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0, -4,0,0,0,0,0,4,0,0,0,0,0,0,0,6,8,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,4,0,4,0,0,0,5,0,7,0,0,10,0,10,7,0,0,12,5,0,0,9,0,0,0,10,0, -0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0, -12,0,0,0,0,0,8,5,13,6,0,0,0,0,0,0,9,4,0,0,0,0,8,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0, -0,0,6,0,0,14,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,17,6,0,0,0,0,12,6,0,0,0,0,8,0,0,7,0, -7,0,4,9,0,0,6,0,0,0,6,0,0,0,0,0,0,8,7,0,0,0,0,0,0,11,0,0,4,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,18,7,0,4,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,6,0,0,0,0,0, -0,0,0,12,5,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,8,0,11,7,0,0,0,0,0,0,0,0,0,4,0,0,0,0, -11,0,0,0,0,0,0,0,21,0,0,6,10,0,0,0,0,0,9,0,10,0,0,0,0,0,11,0,0,0,0,6,0,0,0,0,0, -5,0,0,0,0,0,0,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,4,0,0,23,7,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,9,7,0,0,0,7, -0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0,0, -11,6,0,0,0,0,0,0,0,6,0,0,0,0,10,7,0,0,9,4,0,0,11,0,8,5,0,0,0,7,8,5,22,0,0,0,9,6, -0,0,0,0,0,0,0,6,10,4,0,0,0,0,0,7,9,4,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0, -0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,7,0,0,0,0,0,0,0,6,0,6,0,4,0,0, -0,0,0,0,0,7,0,7,0,4,13,0,0,0,0,0,8,0,0,0,0,7,0,0,0,0,0,0,11,6,0,7,0,0,0,0,9,0,0, -0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,8,0,0,0,0,0,8,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,6,0,0,0,0,13,5,8,0,0, -0,0,0,0,0,14,0,0,6,0,0,0,0,0,0,0,0,0,7,0,0,17,6,0,0,0,0,13,4,0,0,9,6,0,0,10,5,0, -0,10,5,0,0,0,0,13,0,0,0,0,6,0,0,0,0,0,0,10,0,12,0,0,0,0,0,0,0,0,0,0,0,8,4,0,4,0, -0,0,4,0,0,0,0,0,4,0,0,12,0,0,5,9,4,0,0,0,0,0,0,0,0,0,5,8,5,0,0,0,7,0,0,0,0,8,7, -0,0,0,6,12,5,0,0,0,5,0,0,0,5,0,0,0,0,0,4,12,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,7,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0, -0,9,6,0,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,4,11,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0, -0,0,0,0,12,7,0,0,0,7,10,7,0,0,11,0,0,0,0,0,0,0,0,0,11,7,0,0,0,6,0,0,11,0,0,0,0, -0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,22,0,10,7,0,0,8,5,0,0,0,0,0,5,0,0,0,0,0,0, -0,0,0,0,9,6,8,7,0,6,0,0,0,0,0,5,0,0,0,0,0,0,8,7,0,0,0,0,9,7,0,0,0,6,0,0,8,7,0,0, -0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,4,0,5,0,0,0,4,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,6,0,0,0,0,0,0,0,4,0,0,0,0,0,0,9, -6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,5,0,0,0,0,14,0,0,0, -9,0,0,0,0,0,0,0,0,0,9,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,12,0,0,0,0,0,12,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,10,7,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,6,0,0,0,0,0,0,9,6,0,0,0,0,0,6,0,0,0,0,0, -0,0,0,0,0,9,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0, -0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,6,0,7,12,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0, -0,7,0,0,8,6,0,0,0,0,10,7,0,0,0,0,0,0,0,6,0,0,0,0,0,6,12,0,0,0,0,0,0,0,0,6,0,0,0, -0,0,6,0,0,0,6,0,0,0,0,0,6,16,0,0,0,0,0,0,0,0,0,9,0,17,0,14,7,8,0,0,0,0,0,0,6,0, -0,0,0,0,0,0,0,0,0,11,0,0,6,8,7,0,6,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,0,0, -9,0,0,0,0,7,0,0,0,0,11,5,0,4,9,6,8,0,0,0,0,0,0,0,0,0,10,0,11,7,0,0,0,0,0,0,0,0, -9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11, -0,0,0,12,0,0,0,0,0,10,5,0,4,0,0,0,0,0,7,10,6,11,6,0,0,0,0,0,0,0,0,0,0,0,0,17,0, -0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,8,0,0,4,0,0,0,6,0,0,0, -0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,4,0,0,0,0,9,6,0,0,0,4,0,0,0,0,0,4,10,7,0,7,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0, -0,0,0,0,0,0,6,0,0,0,6,0,6,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,18,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,13,0,0,0,0,0,10,0,0,0,0,0,0,0,0,4, -0,0,0,6,0,0,0,0,0,4,8,0,0,0,11,7,0,0,0,4,0,0,0,0,0,7,0,0,8,5,0,0,16,0,0,0,13,6, -0,0,0,0,0,0,0,6,0,0,0,0,20,0,11,6,0,0,8,7,0,0,0,0,0,6,17,0,8,0,0,0,0,0,8,7,0,0, -9,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0, -0,0,4,0,7,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,8, -0,8,0,0,0,0,0,0,0,11,0,8,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,6,0,0,9,0, -0,0,0,0,8,0,0,0,0,0,18,0,0,0,0,0,0,4,9,0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,9,6,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,0,0,0,0,0,0,0,0, -0,4,0,0,0,0,0,0,14,0,0,0,0,7,0,6,0,0,8,0,20,7,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,8,0,0,0,14,0,0,0,0,0,0,0,8,0,0,7,0,6,0,0,0,7,0,0,0,0,0,0,0,0, -0,0,0,4,12,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,10,6,0, -5,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0, -0,0,0,5,8,4,0,0,0,0,0,0,0,4,0,0,0,7,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,12,7,0, -0,0,0,13,6,0,0,0,7,0,0,8,0,0,0,8,0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,0,0,0,0,0,0,11,5, -0,6,0,0,8,5,0,7,0,0,0,0,0,0,0,7,0,0,0,0,8,6,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,4,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -14,0,10,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,19,0,0,4,0,0,0,7, -0,0,11,5,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,16,0,10,5,18,0,0,7,9,6,0,5,0,0,0,0,0, -0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,5,0,0,0,7,0,6,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,4,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,23,0,0,0,0,5,0,0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,14,0,20,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0, -11,0,0,0,0,7,0,0,0,0,15,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,7,0,0,0,0, -0,4,0,0,0,0,10,0,0,0,0,0,9,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,10,0,11,6,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,5,0,0,11,0,0,0,0,7,0,0,0,0,0,0,8,7,0, -4,0,0,0,0,11,0,0,0,0,0,11,0,0,5,0,0,8,7,0,4,0,7,0,0,0,0,0,0,0,6,0,0,0,0,0,4,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,0,0, -0,0,4,11,5,10,7,0,7,0,0,9,6,9,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,9,4,0,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,8,6,0,0,0,0,11,7,0,0,0,0,0,0,0,0,0,0,11,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,8,5,0,0,8,0,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0, -10,7,0,0,0,6,0,0,0,0,0,0,8,0,0,6,0,0,0,6,10,0,0,0,0,0,0,0,0,0,0,0,8,5,0,0,0,6,0, -0,0,6,0,0,0,0,9,5,8,5,8,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0, -0,8,7,10,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,0,5,0,0,0,6,0,7,0,0, -10,5,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,11,0,0,0,0,0,13,4, -0,0,0,4,0,0,0,0,0,5,8,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,14,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,7,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,5,0,0,15,6,10,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,14,6,10,0,0,0,0,0,0,0,0,6,0, -0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,6,0,5,11,4,0,6,0,0,0,7,0,0,0,0, -0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,5,0,0,8,5,0,0,0,0,0,0,0,0,0,0, -0,0,10,0,0,0,0,0,9,6,9,4,0,0,0,4,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,8,5,0, -0,0,0,0,0,0,0,0,0,0,4,0,0,11,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0, -0,0,0,7,12,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0, -4,9,6,0,4,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,6,0, -7,8,6,0,0,0,0,0,0,0,4,0,0,9,6,0,0,0,0,0,0,0,0,0,6,0,5,0,4,0,0,0,0,0,0,0,5,0,0,0, -0,0,5,0,0,0,7,12,7,0,0,0,0,0,0,18,4,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,6,0,0,0, -0,12,0,0,7,0,0,0,0,0,7,0,0,13,0,0,6,0,0,0,0,8,7,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,7,10,5,0,0,8,0,0,0,0,0,0,0,8,6,0,7,0,0,8,4,0,4,0,0,0,0,10,4,0,0,14,0, -0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,17,0,0,0,0,0,0,6,0,0,0,0,8,6,0,0,10,5,0,0,0,0,8, -6,0,0,0,6,0,0,0,7,0,0,0,0,0,6,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,12,0,0,0,0,6, -8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, -0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,4,24,0,0, -0,0,0,12,6,0,0,10,6,0,5,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,17,7,0,5,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,11,5,9,0,8,7,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,10,7,0,0,0,0,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,5,8,7,0,0,0, -0,8,5,0,0,0,0,10,7,0,7,0,0,0,0,0,0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,6,0,4,0,0,0,0, -0,6,12,0,8,7,0,0,0,0,0,0,0,0,0,0,16,0,10,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,22,0,0,0, -0,0,0,0,0,0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,22,0,0,6,0,0,21,0,0,0,22,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -6,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,7,8,0,0,0,0,6,14,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,6,0,0,0,0,0,0, -0,0,0,0,0,6,0,0,0,0,8,5,0,0,11,7,0,6,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0, -6,0,0,0,5,0,0,0,0,0,0,0,0,0,4,0,0,8,7,0,0,0,0,8,5,11,7,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,0,0,0,0,8,5,0,0,10,0,0,4,13,7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,0,0,13,6, -0,6,0,7,0,0,8,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,15,0,0,0,10,7,0,0,0,0,0, -7,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,19,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,6,0,5, -0,7,0,0,0,0,0,0,0,0,0,6,0,0,11,4,0,0,0,6,0,0,13,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,8,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0, -0,7,0,0,0,0,0,0,11,7,0,0,0,0,0,6,0,0,10,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,5,11,6, -0,0,0,0,0,0,0,0,10,0,0,0,0,6,0,0,0,0,0,0,8,7,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,0,0, -0,0,0,8,7,0,0,0,0,9,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,10,0, -0,6,0,0,13,0,0,0,0,0,0,0,9,6,0,0,8,6,8,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,0, -0,9,7,0,0,0,0,0,0,11,0,0,0,10,7,0,0,0,0,0,0,0,0,9,6,0,0,12,4,0,4,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,6,0,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,5,0,0, -9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0, -16,0,0,4,0,0,0,0,0,7,0,0,0,6,0,6,0,0,11,0,0,0,0,5,0,0,0,0,0,0,0,4,8,5,0,0,0,0,0, -0,14,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0, -0,0,8,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0, -0,0,0,0,6,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,14,7,0,0,9,7,0,0,11,0,0,0,0,0,10, -4,11,5,13,6,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,5,0,0,0,0,0,4,0,0,9,0,0,0,0, -0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,6,12,5,0,0,0,6,14,0,0,0,0,0,0,0,0,0,0,4,9,4, -0,0,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,5,0,0, -0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,11,6,0,0,13,7,0,0,13,6,0,7,0,0,0,0,0,0,8,6,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,10,6,0,4,0,0,12,6,0,0,0,0,0,0,0,0,10,6, -0,0,0,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,6,0, -0,0,0,0,7,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,6,0, -0,0,7,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0, -0,0,0,5,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0, -0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,8,7,0,0,8,5,0,0,0,4,9,5,0,0,0,7,10,6,0,0, -0,0,0,0,9,7,0,0,8,5,8,0,8,4,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,5,0,5,0,0,0,0,0,0,0, -0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0, -0,11,7,0,0,0,7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,5,0,0,0,7,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,9,7,0,0,0,0,8,5,0,4,0,0,0,0,0,6,0,6,14, -6,0,0,0,0,9,6,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,6,0,0,0,0,14,7,9,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,16, -0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,14,0, -0,6,0,0,8,6,0,0,0,0,0,6,0,0,12,0,0,0,0,0,8,5,0,7,11,0,0,5,0,4,0,0,0,6,0,0,0,0,0, -0,0,0,0,0,0,0,9,6,0,4,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,10,5,0,0,0,0, -0,4,0,0,0,7,11,6,0,4,8,5,9,5,0,0,0,5,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,8,5,14,7,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,16,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,9,6,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,9,0,0,0,12,5,0,0,0,0,0,0,0,4,10,5,0,0,0,0,0,0,0,0,0,0,0,6,0, -0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,4,0,0,0,6,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,10,4,0,0,0,0,0,5,0,0,0,4, -0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,8,0,10,7,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,10,7,0,0,0,0,0,0,0,0,15,0,0,0, -0,0,0,0,0,0,0,7,0,0,0,0,0,7,10,7,9,7,0,0,0,7,0,0,8,0,0,0,0,0,0,0,9,0,0,0,8,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,8,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,7,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,15,7,12,6,0,0,0,7,0,5,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,5,0,0,0,0, -0,0,0,6,9,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,9,7,0,0,14,0,0,0,11,7,0,0,0,0,0, -0,0,0,0,0,0,4,0,0,11,7,0,0,0,0,8,0,0,0,0,0,0,6,8,7,0,0,0,7,10,4,0,0,0,0,0,0,0,0, -0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,10,0,0,0,0,0,0, -6,0,6,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,11,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,7,0,0,10,7,0,0,0,0,9,7,0,0,0,0,0,0,13,7,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,12,0, -0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,9,6,0,0,11,0,0, -0,0,0,14,4,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,7,0,0, -0,0,0,6,0,7,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,0,20, -7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,8,0,0,0,0,0,0,0,0,0,11,5,0,0,0,0,0,0,0,0,0,0,10,4,0,0,0,5,8,5,10,4,0,0,0,0,0, -0,13,6,9,7,0,0,10,7,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,6,0,0,0,7,0,6,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,10,7,0,0, -0,0,0,0,0,0,0,0,12,4,0,0,0,0,8,7,0,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0, -0,0,0,0,6,0,6,9,6,0,0,12,5,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,0, -0,0,0,0,0,0,0,0,0,5,8,7,9,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,11, -4,0,0,0,0,0,0,8,0,0,0,10,7,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,0, -0,0,0,0,0,5,0,6,0,0,10,0,14,0,0,0,0,0,0,0,23,0,0,0,12,0,10,5,0,0,0,0,0,0,0,0,0, -5,0,0,0,0,8,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,22,0,8,0,0,0,0,6,0,0,0,0,0,0,0,5,0,0, -0,0,0,0,0,6,18,4,0,0,0,7,10,6,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0, -0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,7,10,0,0,0,0,0,0,6,0,0,0,0,11,5,0,0,0,0,0,0,0,0, -15,0,8,6,0,0,13,7,0,0,0,0,0,7,0,0,0,0,0,7,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,9,5,9, -0,0,6,8,6,0,0,0,0,10,0,0,0,18,5,0,0,0,5,0,7,0,0,0,0,8,6,0,0,0,0,9,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,14,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,6,0,0,0,5,0, -0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,8,5,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,0,0,0,0,0,0, -0,0,0,0,20,5,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,9,5,0,0,0,0,0,0,8,4,24,0,0,0,0,0,0, -0,0,0,0,0,0,0,9,7,0,0,0,0,10,5,0,0,8,5,0,0,0,0,0,0,0,0,12,7,0,6,0,0,10,6,0,0,0, -0,14,0,0,4,9,5,0,0,0,0,0,0,9,0,0,0,0,0,0,6,0,0,0,0,0,4,0,0,8,0,0,0,0,0,11,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,8,5,11,7,0,4,0,0,10,0,0,0,0, -0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,11,6,0,0,0,0,0,5,14,6,0,0,0,0,10,0,0, -0,13,4,0,0,0,0,0,0,0,0,0,0,0,6,0,0,10,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,7,12,0,10,6,0,0,0,0,0,0,10,0,0,0,0,0,10,0,9, -7,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,0,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,0,9,7,0,0,0, -0,0,0,0,0,0,0,0,0,24,0,11,7,0,7,0,0,0,0,0,0,8,6,0,0,0,0,0,0,8,7,0,0,0,0,0,5,0,0, -0,6,9,0,0,0,23,5,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,7,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,0,0,18,4,0,0,11,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, -0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,9,0,0,0,11,0,0,0,23,0,0, -0,10,4,0,0,0,0,0,7,0,0,0,7,0,0,0,0,0,4,0,0,0,0,0,7,0,0,19,0,11,0,0,0,0,0,12,7,0, -0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,5,0,0,0,0,0,5,0,0,0,0,0,5,0,0,0,0,0,0,0,6,0,0, -9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,4,0,0,0,0,10,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,22,0,8,7,10,4,11,0,13,5,8,7,9,0,8,7,0,0,0,7,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0, -0,8,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,4,0,0,0,4,11,0,0,6,0,0,8,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,5,0,0, -20,0,0,0,0,0,0,0,0,0,11,0,0,0,0,5,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,7,0,0,14,0,0,0,9,0,13,7,0,0,0,0,0,6,0,7,0,0,8,6,10,6,0,0,8,6,0,0,0,6,0, -0,12,6,9,0,0,0,0,0,0,5,9,0,12,4,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,8,5,0,0,0,0,0, -0,0,4,8,0,0,6,8,0,0,0,0,0,0,0,0,0,13,6,0,7,0,0,0,0,0,6,8,7,8,6,0,0,0,7,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,18,0,11,4,0,0,0,5,0,0,0,0,0,0,0,0,0,0, -0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,14, -6,0,0,0,0,12,7,8,0,0,0,0,0,0,0,8,7,0,0,0,0,10,4,0,0,0,0,0,0,10,0,0,6,0,0,0,0,0, -0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,15,6,9,7,0,0,0,0,0,0,15,6,11,7,0,0,0,7,0,0,21,0,0, -0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,17,6,0,0,10,5,0,5,0,0,0,0,0,0,0,0,0,7, -0,0,10,0,0,0,0,0,0,0,0,4,11,5,0,0,0,0,16,7,0,0,0,0,0,6,0,0,8,7,0,4,0,0,10,0,0,0, -0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,8,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0, -0,0,0,10,4,0,0,0,0,0,0,0,0,0,6,0,5,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0, -0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,6,10,7,0,0,0,0,0,0,0,0,8,4,0,0,10,0,0,0,0,4,0,6,0,6,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,7,17,0,0,0,0,0, -0,0,0,0,0,0,10,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -6,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0,4,0,0,0,0,0,6,0,0,0,0,0,0,10,5,0,0, -0,5,0,0,0,0,9,0,19,7,0,0,0,0,0,7,0,0,0,0,10,6,0,0,0,6,0,5,0,0,0,0,0,0,0,0,0,6,8, -0,0,0,0,0,11,0,0,0,0,0,0,6,0,0,0,0,0,7,9,0,15,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,0,0, -0,0,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,0,9,0,0,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,6,0,0, -0,0,0,6,10,0,0,0,0,0,0,0,23,0,14,0,0,0,0,7,0,0,0,0,0,7,0,0,9,0,0,0,0,7,0,0,0,0, -0,6,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0, -0,0,0,0,0,9,5,0,0,0,0,0,4,0,0,0,0,9,5,0,0,0,0,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,10,0,0,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,11,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,14,7,0,0,12,7,0,0,0, -0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,8,6,10,0,0,0,0,0,0,0,0,0,10,7,8,5,0,0,0,0,0,0, -0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,5,0,0,9,5,0,0,0,0,0,5,0,0,0,0,0,4,0,0,0, -0,0,0,0,0,0,0,12,4,11,0,0,0,9,0,11,7,0,0,0,0,0,0,10,6,0,0,0,6,0,0,0,0,15,5,0,0, -11,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,4,0,4,0,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,8,0,0,0,19,7,0,4,0,0,9,0,0,0,0,0,10,0, -0,6,0,0,13,0,12,6,0,0,0,0,0,0,0,0,10,7,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,13,7,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,4,9,0,0,0,10,0,0,0,0,0,0,0, -0,5,0,0,0,0,0,0,10,0,23,6,0,0,0,6,8,0,0,0,0,0,0,0,0,0,17,7,0,0,0,0,11,6,22,5,0, -0,9,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,5,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,4,11,0,9,4,0,0, -0,7,0,7,0,0,0,0,0,0,12,4,0,0,0,0,0,0,0,0,0,0,0,0,11,4,0,0,0,0,0,0,0,0,0,0,0,0,0, -4,0,0,11,5,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,6,0,0,0,4,0,0,0,0, -0,0,0,0,0,7,0,0,0,4,0,0,10,4,0,0,0,0,0,0,0,7,0,7,0,0,0,6,0,0,0,0,8,6,0,6,0,6,0, -0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,6,22,6,12,0,0,6,0,0,0,6,0,0,0,0,0,7,0,0,0,0,11,0,0,0, -9,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,6,0,0,0,6,0,6,0,0,8,7,0,0,0,4,9,7,19,0,0,0,0,0,0,0,0,0,9,6,10,6,0,6,0,0,0, -4,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,6,16,7,10,6,0,0,23,6,11,7,0,4,0,0,0,0,0,0,0,0,0, -5,0,0,0,0,10,7,0,0,0,0,0,7,0,0,0,0,0,0,15,0,10,0,0,0,14,6,0,0,0,0,0,0,0,0,0,0,0, -5,0,0,0,0,0,0,0,5,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,5,0,0,11,5,0,0,0,0,0,0,0,0,0,0, -0,4,0,0,0,0,0,6,0,0,10,0,0,0,0,7,0,0,0,0,0,0,10,6,0,0,0,0,8,4,0,0,0,7,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,12,5,0,0,0,0, -0,6,0,0,0,0,9,6,0,0,0,0,0,0,0,6,9,0,0,0,0,6,0,0,0,0,8,7,0,0,0,0,0,0,0,6,0,0,0,0, -0,0,0,0,0,0,10,5,0,0,0,0,0,0,8,6,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,8,5,0,0,0,0,0,7,0,7,0,4,0,0,10,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,5,0,0,0,0,13, -7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,7,0,0,13,0,0,0,0,0,0,0,0,7,10,5,0,0,0,0,0,0,9,7,0,0,8,6,9, -5,0,0,0,0,0,6,12,0,0,0,0,0,0,0,18,6,0,0,0,0,0,0,0,0,19,7,0,4,0,0,0,0,9,5,0,5,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,7,0,0,0,0,0,0,14,0,0,0,23,7,8,7,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,22,0,0,7,0,0,0,0,0,0,0,0,9,7,8,4,0, -0,0,0,0,0,0,0,8,5,0,6,0,0,0,0,0,6,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0, -8,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,12,5,0,0,0,0,0,0,0,0,0,0,8,6,0,0,11,7,0,0,0, -0,12,0,8,6,19,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,11,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,11,7,0,0,0,0,0,4,10,0,0,0,0,0,0,0,8,7,0,0,0,0,14,0,8,0,0,6,10,0,0, -0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,0,0, -0,0,0,0,13,0,0,0,0,0,0,0,11,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0, -0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,5,0,0,0,6,0,0,0,5,0,7,0,0,0, -0,0,6,0,0,21,7,0,0,9,6,0,0,0,6,0,0,13,7,0,0,0,5,0,0,0,0,0,4,0,6,0,0,0,0,0,0,0,0, -0,0,0,4,0,0,0,0,0,0,11,5,0,6,0,0,10,5,0,0,0,0,0,0,0,0,9,6,0,0,8,7,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,9,0,0,0,0,0,0,6,0,0,0,0,15,4,0,0,12,7,0,0,0,6, -0,7,0,0,8,0,9,5,0,4,0,0,0,6,0,6,0,0,23,4,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,4,0,0,8, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,0,0,0,0,0,0, -7,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,12,6,0,0,0,0,0,0,10,7,0,7,0,0,0,0,0,0,0,0,0,0, -9,0,0,0,0,0,8,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,11,5,0,0,0,6,0,6,0,0,0,0,0,0,0,6,0, -4,0,0,0,0,0,0,0,0,0,0,0,5,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,8,7,0,0,0,6,0,6,0, -0,0,0,0,0,0,0,0,5,0,0,0,0,0,5,0,0,0,0,11,0,0,0,0,0,0,0,10,5,9,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,23,7,0,0,0,0,0,7,0,0,10,6,18,0,0,0, -0,0,0,0,8,7,0,6,0,0,0,0,0,0,8,5,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0, -0,0,0,0,0,6,0,0,0,4,12,7,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,13,5,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0, -11,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0, -0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,10,0,11,0,0,0,0,0,0,0,0,0, -17,5,0,4,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,0,8,7,0,0,0,0,0,0,0, -0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0, -10,0,0,0,8,6,0,0,0,7,0,0,0,0,0,0,8,0,0,0,14,0,0,0,0,7,0,0,0,4,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,9,4,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0, -10,0,0,0,16,5,0,0,0,0,0,0,8,0,0,4,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,10,0,0,0, -0,0,0,0,0,5,0,0,0,0,12,5,0,7,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0, -0,0,0,0,0,0,0,12,6,0,0,0,0,0,7,0,6,0,6,12,6,0,0,0,0,0,0,0,4,8,7,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,10,6,8,0,0, -6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -16,0,8,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,23,5,0,0,0,7,0,6,0, -0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,14,0,0,0,0,7,0,0,0,4,17,5,0,0,0,0,11,0,9,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,6,0,0,0,5,0,7,0,0,0,0,0,0,0,0,8,0,0,0, -12,6,0,0,0,0,0,0,13,0,0,0,0,7,9,0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,10,7,12,0,0,0,9,0, -0,0,14,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,15,6,0,0,23,0,0,7,0,6,0,0,0,7,0,6, -0,0,0,0,0,0,0,6,0,6,9,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,8,7,9,4,0,0,10,0,0,0,10, -6,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,23,0,0,6,0,0,0,0,0,0,9,4, -0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0,0,9,6,0,0,0,0,8,6,0,0,0,0,0,0,0,0,12,0,0, -0,0,0,8,0,0,6,11,6,0,0,8,7,8,5,0,0,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0, -10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0, -7,0,0,0,0,9,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,8,0,0,0,0,6,12,5,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,10,0,10, -7,0,0,8,0,0,0,0,4,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0, -0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,6,0,5,0,0,0,0,8,0,0,0,10,7,0,0,0,0,10,0,0,0, -0,0,13,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,19,7,0,4,12,0,8,0,0,0,0,6,0,0,0,0, -0,0,0,6,0,0,0,0,0,0,0,0,0,4,0,0,0,0,18,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0, -0,14,0,0,4,0,0,0,6,0,0,0,6,0,0,0,7,0,0,0,0,0,0,10,4,0,0,9,7,0,0,11,0,0,0,0,0,0, -7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,12,0,0,0, -0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,22,5,9,7,0,0,0,0,0,0,0,0,0, -0,0,6,0,0,9,6,0,5,0,0,0,0,0,0,10,5,0,0,8,6,0,6,10,5,0,0,0,6,0,0,0,6,0,0,20,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,0,17,4,0,7,0,6, -0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10, -0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0, -0,0,7,0,0,8,6,12,0,0,7,18,7,0,0,8,4,0,0,0,0,9,6,0,0,0,0,0,0,0,0,13,0,0,6,0,0,0, -0,0,0,0,0,0,0,10,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,0, -0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,12,0,0,0,8,0,0,0,0,0,0, -4,0,0,10,0,16,0,0,0,0,0,0,0,12,7,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,16,6,10,0,0,5,0,0,0,0,0,6,0,0,0,0, -0,7,0,0,0,7,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,5,8,7,0,7,0,0,0,0,0,0,0,0,8,0,0,6,0,0,0,6,0,0,0,4,0,0,0,0, -8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,7,0,0,8,0,0,0, -9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,13,5,0,5,0,0,0,7,8,4,0,0,0,0,0,0,0, -0,12,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,0,11,0,0,0,0,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0, -0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,11,6,0,0,10,6,0,0, -0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, -0,0,0,6,0,0,0,7,0,0,9,0,8,7,11,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,9,6,10,5,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,10,7,0,0,0,0,0,0,11,0,9,6,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,15,5,12,5, -0,0,0,0,0,0,12,7,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,12,6,0, -0,0,0,24,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0, -0,0,0,4,10,4,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,9,0,11,0,0,0,0,0,0,0,0,0,0,6, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, -0,0,8,0,0,0,0,7,0,0,0,0,0,0,10,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,0, -0,0,0,0,0,14,7,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,0,0,6,0,0,0,6,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,7,20,7,11,4,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,7,9,6,0,0,12,7,0,0,0,0,0,0,10,0,12,0, -0,0,0,0,0,4,9,6,13,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,5,0,0,0,0,0,0,8,0,0,0,0,0,0, -0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,11,0,9,0,0,0,0,4,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,5,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, -0,4,0,5,0,0,0,0,0,0,0,0,0,4,0,0,0,0,9,7,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, -0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6, -0,0,0,0,8,7,0,0,0,0,0,0,12,0,0,6,0,0,0,0,0,0,0,6,8,4,0,0,10,7,0,0,10,0,0,0,0,0, -0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,5, -0,4,0,0,0,0,0,6,0,0,0,0,0,0,8,0,0,6,0,0,0,6,0,0,0,0,0,7,0,5,8,4,0,0,9,0,0,0,0,4, -0,0,0,0,0,0,0,0,0,5,0,0,15,6,8,6,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,9,6,0,0,0,0,0,0,0,7,0,0,0,4,0, -6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,9,5,0,6,12,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,0,12,7,0,0,0,0, -0,0,0,0,0,5,0,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,11,4,0,0,0,0,0,0,0,0,0,0,10, -7,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,7,8,7,9,6,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,5,12,0, -10,5,12,6,0,0,0,7,0,0,0,0,0,0,0,5,0,0,0,5,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7, -11,7,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,6,0,7,0,0,0,0,8,0,8,5,0,6,0,0,0,6,0,0,0, -0,0,0,0,6,0,6,0,6,9,0,0,5,17,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,7,0,0, -0,0,0,7,0,0,0,0,16,5,0,0,0,0,0,0,0,4,0,0,0,5,11,5,0,7,0,0,0,4,8,7,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,12,0,0,0, -0,0,12,0,0,0,0,0,0,0,0,4,10,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,0,0,4,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,20,5,0,0, -10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,12,0,0,0,0,0,0,6,0,0,0,0,0,0,9,4,10,7,0,4,0,0, -0,0,0,0,10,6,0,0,0,0,8,4,0,7,8,6,0,6,8,0,10,0,0,0,0,0,13,5,0,6,0,0,0,0,0,0,22,4, -0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,10, -5,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,10,4,0,0,10,7,0,0,0,0,0,5,0, -5,8,0,0,0,0,6,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,4,0,0,0,0,0,6,0,0, -0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0, -4,0,0,0,4,10,0,0,6,13,7,8,0,0,0,0,0,0,7,0,0,12,7,0,0,0,0,0,0,10,5,0,0,0,0,0,6,0, -0,0,0,0,0,0,0,0,0,13,7,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,6,0,6, -0,0,0,0,0,0,0,0,12,0,8,4,0,0,0,0,0,4,0,4,0,0,0,0,0,0,0,5,0,0,0,0,12,5,0,0,0,7,0, -0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,10,0,0,0,20,0,0,5,0,0,10, -7,11,7,0,0,0,0,0,0,0,0,0,0,17,0,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,10,7,0,4,0,6,0,0,24,0,0,5,0,0,0,0,8,0,0, -0,0,0,0,0,10,5,0,4,0,6,0,0,8,0,0,0,0,0,0,4,0,6,0,0,0,0,0,0,9,5,0,0,0,0,0,0,0,0, -0,0,0,6,0,0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,0,7, -0,0,13,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0, -17,7,0,0,11,6,0,0,0,0,12,6,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0,10,0,0,4,8,6,0,0,0, -0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,9,5,0,7,18,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,8,0,0,0, -0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0, -0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,4,0,6,0,0,9,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0, -0,0,0,8,7,10,0,8,5,0,0,0,0,0,0,0,0,9,0,0,0,10,0,0,0,0,6,0,7,0,4,0,0,0,0,0,0,0,0, -8,0,0,0,0,0,8,4,0,0,0,0,0,5,0,0,10,0,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -4,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,6,11,0,0, -7,0,0,0,0,0,6,10,5,0,0,0,0,0,0,0,0,0,5,0,0,9,5,12,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,6,0,0,0,0,13,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0, -0,0,0,8,4,0,6,12,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,11,4,0,0,0,6,14,0,11,0,9,6,0,0,0,0,0,0,22,0,12,0,8,6,0,0,0,0,0,0,0,6,0, -0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0, -10,7,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,11,0,0,0,0,0,0,0,8,6,0,0,9, -7,0,0,12,4,0,0,0,0,0,0,12,6,0,6,0,7,0,0,8,5,0,0,0,0}; -/* GENERATED CODE END */ - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/dictionary_hash.h b/src/deps/brotli/enc/dictionary_hash.h deleted file mode 100644 index e553ea5d4efb2..0000000000000 --- a/src/deps/brotli/enc/dictionary_hash.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Hash table on the 4-byte prefixes of static dictionary words. */ - -#ifndef BROTLI_ENC_DICTIONARY_HASH_H_ -#define BROTLI_ENC_DICTIONARY_HASH_H_ - -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -extern const uint16_t kStaticDictionaryHashWords[32768]; -extern const uint8_t kStaticDictionaryHashLengths[32768]; - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_DICTIONARY_HASH_H_ */ diff --git a/src/deps/brotli/enc/encode.c b/src/deps/brotli/enc/encode.c deleted file mode 100644 index 95ff2eaf500bc..0000000000000 --- a/src/deps/brotli/enc/encode.c +++ /dev/null @@ -1,1996 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Implementation of Brotli compressor. */ - -#include -#include -#include - -#include /* free, malloc */ -#include /* memcpy, memset */ - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/platform.h" -#include "../common/version.h" -#include "backward_references.h" -#include "backward_references_hq.h" -#include "bit_cost.h" -#include "brotli_bit_stream.h" -#include "compress_fragment.h" -#include "compress_fragment_two_pass.h" -#include "dictionary_hash.h" -#include "encoder_dict.h" -#include "entropy_encode.h" -#include "fast_log.h" -#include "hash.h" -#include "histogram.h" -#include "memory.h" -#include "metablock.h" -#include "prefix.h" -#include "state.h" -#include "quality.h" -#include "ringbuffer.h" -#include "utf8_util.h" -#include "write_bits.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define COPY_ARRAY(dst, src) memcpy(dst, src, sizeof(src)); - -static size_t InputBlockSize(BrotliEncoderState* s) { - return (size_t)1 << s->params.lgblock; -} - -static uint64_t UnprocessedInputSize(BrotliEncoderState* s) { - return s->input_pos_ - s->last_processed_pos_; -} - -static size_t RemainingInputBlockSize(BrotliEncoderState* s) { - const uint64_t delta = UnprocessedInputSize(s); - size_t block_size = InputBlockSize(s); - if (delta >= block_size) return 0; - return block_size - (size_t)delta; -} - -BROTLI_BOOL BrotliEncoderSetParameter( - BrotliEncoderState* state, BrotliEncoderParameter p, uint32_t value) { - /* Changing parameters on the fly is not implemented yet. */ - if (state->is_initialized_) return BROTLI_FALSE; - /* TODO(eustas): Validate/clamp parameters here. */ - switch (p) { - case BROTLI_PARAM_MODE: - state->params.mode = (BrotliEncoderMode)value; - return BROTLI_TRUE; - - case BROTLI_PARAM_QUALITY: - state->params.quality = (int)value; - return BROTLI_TRUE; - - case BROTLI_PARAM_LGWIN: - state->params.lgwin = (int)value; - return BROTLI_TRUE; - - case BROTLI_PARAM_LGBLOCK: - state->params.lgblock = (int)value; - return BROTLI_TRUE; - - case BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: - if ((value != 0) && (value != 1)) return BROTLI_FALSE; - state->params.disable_literal_context_modeling = TO_BROTLI_BOOL(!!value); - return BROTLI_TRUE; - - case BROTLI_PARAM_SIZE_HINT: - state->params.size_hint = value; - return BROTLI_TRUE; - - case BROTLI_PARAM_LARGE_WINDOW: - state->params.large_window = TO_BROTLI_BOOL(!!value); - return BROTLI_TRUE; - - case BROTLI_PARAM_NPOSTFIX: - state->params.dist.distance_postfix_bits = value; - return BROTLI_TRUE; - - case BROTLI_PARAM_NDIRECT: - state->params.dist.num_direct_distance_codes = value; - return BROTLI_TRUE; - - case BROTLI_PARAM_STREAM_OFFSET: - if (value > (1u << 30)) return BROTLI_FALSE; - state->params.stream_offset = value; - return BROTLI_TRUE; - - default: return BROTLI_FALSE; - } -} - -/* Wraps 64-bit input position to 32-bit ring-buffer position preserving - "not-a-first-lap" feature. */ -static uint32_t WrapPosition(uint64_t position) { - uint32_t result = (uint32_t)position; - uint64_t gb = position >> 30; - if (gb > 2) { - /* Wrap every 2GiB; The first 3GB are continuous. */ - result = (result & ((1u << 30) - 1)) | ((uint32_t)((gb - 1) & 1) + 1) << 30; - } - return result; -} - -static uint8_t* GetBrotliStorage(BrotliEncoderState* s, size_t size) { - MemoryManager* m = &s->memory_manager_; - if (s->storage_size_ < size) { - BROTLI_FREE(m, s->storage_); - s->storage_ = BROTLI_ALLOC(m, uint8_t, size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->storage_)) return NULL; - s->storage_size_ = size; - } - return s->storage_; -} - -static size_t HashTableSize(size_t max_table_size, size_t input_size) { - size_t htsize = 256; - while (htsize < max_table_size && htsize < input_size) { - htsize <<= 1; - } - return htsize; -} - -static int* GetHashTable(BrotliEncoderState* s, int quality, - size_t input_size, size_t* table_size) { - /* Use smaller hash table when input.size() is smaller, since we - fill the table, incurring O(hash table size) overhead for - compression, and if the input is short, we won't need that - many hash table entries anyway. */ - MemoryManager* m = &s->memory_manager_; - const size_t max_table_size = MaxHashTableSize(quality); - size_t htsize = HashTableSize(max_table_size, input_size); - int* table; - BROTLI_DCHECK(max_table_size >= 256); - if (quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { - /* Only odd shifts are supported by fast-one-pass. */ - if ((htsize & 0xAAAAA) == 0) { - htsize <<= 1; - } - } - - if (htsize <= sizeof(s->small_table_) / sizeof(s->small_table_[0])) { - table = s->small_table_; - } else { - if (htsize > s->large_table_size_) { - s->large_table_size_ = htsize; - BROTLI_FREE(m, s->large_table_); - s->large_table_ = BROTLI_ALLOC(m, int, htsize); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->large_table_)) return 0; - } - table = s->large_table_; - } - - *table_size = htsize; - memset(table, 0, htsize * sizeof(*table)); - return table; -} - -static void EncodeWindowBits(int lgwin, BROTLI_BOOL large_window, - uint16_t* last_bytes, uint8_t* last_bytes_bits) { - if (large_window) { - *last_bytes = (uint16_t)(((lgwin & 0x3F) << 8) | 0x11); - *last_bytes_bits = 14; - } else { - if (lgwin == 16) { - *last_bytes = 0; - *last_bytes_bits = 1; - } else if (lgwin == 17) { - *last_bytes = 1; - *last_bytes_bits = 7; - } else if (lgwin > 17) { - *last_bytes = (uint16_t)(((lgwin - 17) << 1) | 0x01); - *last_bytes_bits = 4; - } else { - *last_bytes = (uint16_t)(((lgwin - 8) << 4) | 0x01); - *last_bytes_bits = 7; - } - } -} - -/* TODO(eustas): move to compress_fragment.c? */ -/* Initializes the command and distance prefix codes for the first block. */ -static void InitCommandPrefixCodes(BrotliOnePassArena* s) { - static const uint8_t kDefaultCommandDepths[128] = { - 0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, - 0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, - 7, 7, 10, 10, 10, 10, 10, 10, 0, 4, 4, 5, 5, 5, 6, 6, - 7, 8, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, - 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 10, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - }; - static const uint16_t kDefaultCommandBits[128] = { - 0, 0, 8, 9, 3, 35, 7, 71, - 39, 103, 23, 47, 175, 111, 239, 31, - 0, 0, 0, 4, 12, 2, 10, 6, - 13, 29, 11, 43, 27, 59, 87, 55, - 15, 79, 319, 831, 191, 703, 447, 959, - 0, 14, 1, 25, 5, 21, 19, 51, - 119, 159, 95, 223, 479, 991, 63, 575, - 127, 639, 383, 895, 255, 767, 511, 1023, - 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 27, 59, 7, 39, 23, 55, 30, 1, 17, 9, 25, 5, 0, 8, 4, 12, - 2, 10, 6, 21, 13, 29, 3, 19, 11, 15, 47, 31, 95, 63, 127, 255, - 767, 2815, 1791, 3839, 511, 2559, 1535, 3583, 1023, 3071, 2047, 4095, - }; - static const uint8_t kDefaultCommandCode[] = { - 0xff, 0x77, 0xd5, 0xbf, 0xe7, 0xde, 0xea, 0x9e, 0x51, 0x5d, 0xde, 0xc6, - 0x70, 0x57, 0xbc, 0x58, 0x58, 0x58, 0xd8, 0xd8, 0x58, 0xd5, 0xcb, 0x8c, - 0xea, 0xe0, 0xc3, 0x87, 0x1f, 0x83, 0xc1, 0x60, 0x1c, 0x67, 0xb2, 0xaa, - 0x06, 0x83, 0xc1, 0x60, 0x30, 0x18, 0xcc, 0xa1, 0xce, 0x88, 0x54, 0x94, - 0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x04, 0x00, - }; - static const size_t kDefaultCommandCodeNumBits = 448; - COPY_ARRAY(s->cmd_depth, kDefaultCommandDepths); - COPY_ARRAY(s->cmd_bits, kDefaultCommandBits); - - /* Initialize the pre-compressed form of the command and distance prefix - codes. */ - COPY_ARRAY(s->cmd_code, kDefaultCommandCode); - s->cmd_code_numbits = kDefaultCommandCodeNumBits; -} - -/* Decide about the context map based on the ability of the prediction - ability of the previous byte UTF8-prefix on the next byte. The - prediction ability is calculated as Shannon entropy. Here we need - Shannon entropy instead of 'BitsEntropy' since the prefix will be - encoded with the remaining 6 bits of the following byte, and - BitsEntropy will assume that symbol to be stored alone using Huffman - coding. */ -static void ChooseContextMap(int quality, - uint32_t* bigram_histo, - size_t* num_literal_contexts, - const uint32_t** literal_context_map) { - static const uint32_t kStaticContextMapContinuation[64] = { - 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; - static const uint32_t kStaticContextMapSimpleUTF8[64] = { - 0, 0, 1, 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, - }; - - uint32_t monogram_histo[3] = { 0 }; - uint32_t two_prefix_histo[6] = { 0 }; - size_t total; - size_t i; - size_t dummy; - double entropy[4]; - for (i = 0; i < 9; ++i) { - monogram_histo[i % 3] += bigram_histo[i]; - two_prefix_histo[i % 6] += bigram_histo[i]; - } - entropy[1] = ShannonEntropy(monogram_histo, 3, &dummy); - entropy[2] = (ShannonEntropy(two_prefix_histo, 3, &dummy) + - ShannonEntropy(two_prefix_histo + 3, 3, &dummy)); - entropy[3] = 0; - for (i = 0; i < 3; ++i) { - entropy[3] += ShannonEntropy(bigram_histo + 3 * i, 3, &dummy); - } - - total = monogram_histo[0] + monogram_histo[1] + monogram_histo[2]; - BROTLI_DCHECK(total != 0); - entropy[0] = 1.0 / (double)total; - entropy[1] *= entropy[0]; - entropy[2] *= entropy[0]; - entropy[3] *= entropy[0]; - - if (quality < MIN_QUALITY_FOR_HQ_CONTEXT_MODELING) { - /* 3 context models is a bit slower, don't use it at lower qualities. */ - entropy[3] = entropy[1] * 10; - } - /* If expected savings by symbol are less than 0.2 bits, skip the - context modeling -- in exchange for faster decoding speed. */ - if (entropy[1] - entropy[2] < 0.2 && - entropy[1] - entropy[3] < 0.2) { - *num_literal_contexts = 1; - } else if (entropy[2] - entropy[3] < 0.02) { - *num_literal_contexts = 2; - *literal_context_map = kStaticContextMapSimpleUTF8; - } else { - *num_literal_contexts = 3; - *literal_context_map = kStaticContextMapContinuation; - } -} - -/* Decide if we want to use a more complex static context map containing 13 - context values, based on the entropy reduction of histograms over the - first 5 bits of literals. */ -static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input, - size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint, - size_t* num_literal_contexts, const uint32_t** literal_context_map, - uint32_t* arena) { - static const uint32_t kStaticContextMapComplexUTF8[64] = { - 11, 11, 12, 12, /* 0 special */ - 0, 0, 0, 0, /* 4 lf */ - 1, 1, 9, 9, /* 8 space */ - 2, 2, 2, 2, /* !, first after space/lf and after something else. */ - 1, 1, 1, 1, /* " */ - 8, 3, 3, 3, /* % */ - 1, 1, 1, 1, /* ({[ */ - 2, 2, 2, 2, /* }]) */ - 8, 4, 4, 4, /* :; */ - 8, 7, 4, 4, /* . */ - 8, 0, 0, 0, /* > */ - 3, 3, 3, 3, /* [0..9] */ - 5, 5, 10, 5, /* [A-Z] */ - 5, 5, 10, 5, - 6, 6, 6, 6, /* [a-z] */ - 6, 6, 6, 6, - }; - BROTLI_UNUSED(quality); - /* Try the more complex static context map only for long data. */ - if (size_hint < (1 << 20)) { - return BROTLI_FALSE; - } else { - const size_t end_pos = start_pos + length; - /* To make entropy calculations faster, we collect histograms - over the 5 most significant bits of literals. One histogram - without context and 13 additional histograms for each context value. */ - uint32_t* BROTLI_RESTRICT const combined_histo = arena; - uint32_t* BROTLI_RESTRICT const context_histo = arena + 32; - uint32_t total = 0; - double entropy[3]; - size_t dummy; - size_t i; - ContextLut utf8_lut = BROTLI_CONTEXT_LUT(CONTEXT_UTF8); - memset(arena, 0, sizeof(arena[0]) * 32 * 14); - for (; start_pos + 64 <= end_pos; start_pos += 4096) { - const size_t stride_end_pos = start_pos + 64; - uint8_t prev2 = input[start_pos & mask]; - uint8_t prev1 = input[(start_pos + 1) & mask]; - size_t pos; - /* To make the analysis of the data faster we only examine 64 byte long - strides at every 4kB intervals. */ - for (pos = start_pos + 2; pos < stride_end_pos; ++pos) { - const uint8_t literal = input[pos & mask]; - const uint8_t context = (uint8_t)kStaticContextMapComplexUTF8[ - BROTLI_CONTEXT(prev1, prev2, utf8_lut)]; - ++total; - ++combined_histo[literal >> 3]; - ++context_histo[(context << 5) + (literal >> 3)]; - prev2 = prev1; - prev1 = literal; - } - } - entropy[1] = ShannonEntropy(combined_histo, 32, &dummy); - entropy[2] = 0; - for (i = 0; i < 13; ++i) { - entropy[2] += ShannonEntropy(context_histo + (i << 5), 32, &dummy); - } - entropy[0] = 1.0 / (double)total; - entropy[1] *= entropy[0]; - entropy[2] *= entropy[0]; - /* The triggering heuristics below were tuned by compressing the individual - files of the silesia corpus. If we skip this kind of context modeling - for not very well compressible input (i.e. entropy using context modeling - is 60% of maximal entropy) or if expected savings by symbol are less - than 0.2 bits, then in every case when it triggers, the final compression - ratio is improved. Note however that this heuristics might be too strict - for some cases and could be tuned further. */ - if (entropy[2] > 3.0 || entropy[1] - entropy[2] < 0.2) { - return BROTLI_FALSE; - } else { - *num_literal_contexts = 13; - *literal_context_map = kStaticContextMapComplexUTF8; - return BROTLI_TRUE; - } - } -} - -static void DecideOverLiteralContextModeling(const uint8_t* input, - size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint, - size_t* num_literal_contexts, const uint32_t** literal_context_map, - uint32_t* arena) { - if (quality < MIN_QUALITY_FOR_CONTEXT_MODELING || length < 64) { - return; - } else if (ShouldUseComplexStaticContextMap( - input, start_pos, length, mask, quality, size_hint, - num_literal_contexts, literal_context_map, arena)) { - /* Context map was already set, nothing else to do. */ - } else { - /* Gather bi-gram data of the UTF8 byte prefixes. To make the analysis of - UTF8 data faster we only examine 64 byte long strides at every 4kB - intervals. */ - const size_t end_pos = start_pos + length; - uint32_t* BROTLI_RESTRICT const bigram_prefix_histo = arena; - memset(bigram_prefix_histo, 0, sizeof(arena[0]) * 9); - for (; start_pos + 64 <= end_pos; start_pos += 4096) { - static const int lut[4] = { 0, 0, 1, 2 }; - const size_t stride_end_pos = start_pos + 64; - int prev = lut[input[start_pos & mask] >> 6] * 3; - size_t pos; - for (pos = start_pos + 1; pos < stride_end_pos; ++pos) { - const uint8_t literal = input[pos & mask]; - ++bigram_prefix_histo[prev + lut[literal >> 6]]; - prev = lut[literal >> 6] * 3; - } - } - ChooseContextMap(quality, &bigram_prefix_histo[0], num_literal_contexts, - literal_context_map); - } -} - -static BROTLI_BOOL ShouldCompress( - const uint8_t* data, const size_t mask, const uint64_t last_flush_pos, - const size_t bytes, const size_t num_literals, const size_t num_commands) { - /* TODO(eustas): find more precise minimal block overhead. */ - if (bytes <= 2) return BROTLI_FALSE; - if (num_commands < (bytes >> 8) + 2) { - if ((double)num_literals > 0.99 * (double)bytes) { - uint32_t literal_histo[256] = { 0 }; - static const uint32_t kSampleRate = 13; - static const double kMinEntropy = 7.92; - const double bit_cost_threshold = - (double)bytes * kMinEntropy / kSampleRate; - size_t t = (bytes + kSampleRate - 1) / kSampleRate; - uint32_t pos = (uint32_t)last_flush_pos; - size_t i; - for (i = 0; i < t; i++) { - ++literal_histo[data[pos & mask]]; - pos += kSampleRate; - } - if (BitsEntropy(literal_histo, 256) > bit_cost_threshold) { - return BROTLI_FALSE; - } - } - } - return BROTLI_TRUE; -} - -/* Chooses the literal context mode for a metablock */ -static ContextType ChooseContextMode(const BrotliEncoderParams* params, - const uint8_t* data, const size_t pos, const size_t mask, - const size_t length) { - /* We only do the computation for the option of something else than - CONTEXT_UTF8 for the highest qualities */ - if (params->quality >= MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING && - !BrotliIsMostlyUTF8(data, pos, mask, length, kMinUTF8Ratio)) { - return CONTEXT_SIGNED; - } - return CONTEXT_UTF8; -} - -static void WriteMetaBlockInternal(MemoryManager* m, - const uint8_t* data, - const size_t mask, - const uint64_t last_flush_pos, - const size_t bytes, - const BROTLI_BOOL is_last, - ContextType literal_context_mode, - const BrotliEncoderParams* params, - const uint8_t prev_byte, - const uint8_t prev_byte2, - const size_t num_literals, - const size_t num_commands, - Command* commands, - const int* saved_dist_cache, - int* dist_cache, - size_t* storage_ix, - uint8_t* storage) { - const uint32_t wrapped_last_flush_pos = WrapPosition(last_flush_pos); - uint16_t last_bytes; - uint8_t last_bytes_bits; - ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); - BrotliEncoderParams block_params = *params; - - if (bytes == 0) { - /* Write the ISLAST and ISEMPTY bits. */ - BrotliWriteBits(2, 3, storage_ix, storage); - *storage_ix = (*storage_ix + 7u) & ~7u; - return; - } - - if (!ShouldCompress(data, mask, last_flush_pos, bytes, - num_literals, num_commands)) { - /* Restore the distance cache, as its last update by - CreateBackwardReferences is now unused. */ - memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0])); - BrotliStoreUncompressedMetaBlock(is_last, data, - wrapped_last_flush_pos, mask, bytes, - storage_ix, storage); - return; - } - - BROTLI_DCHECK(*storage_ix <= 14); - last_bytes = (uint16_t)((storage[1] << 8) | storage[0]); - last_bytes_bits = (uint8_t)(*storage_ix); - if (params->quality <= MAX_QUALITY_FOR_STATIC_ENTROPY_CODES) { - BrotliStoreMetaBlockFast(m, data, wrapped_last_flush_pos, - bytes, mask, is_last, params, - commands, num_commands, - storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - } else if (params->quality < MIN_QUALITY_FOR_BLOCK_SPLIT) { - BrotliStoreMetaBlockTrivial(m, data, wrapped_last_flush_pos, - bytes, mask, is_last, params, - commands, num_commands, - storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - } else { - MetaBlockSplit mb; - InitMetaBlockSplit(&mb); - if (params->quality < MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING) { - size_t num_literal_contexts = 1; - const uint32_t* literal_context_map = NULL; - if (!params->disable_literal_context_modeling) { - /* TODO(eustas): pull to higher level and reuse. */ - uint32_t* arena = BROTLI_ALLOC(m, uint32_t, 14 * 32); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; - DecideOverLiteralContextModeling( - data, wrapped_last_flush_pos, bytes, mask, params->quality, - params->size_hint, &num_literal_contexts, - &literal_context_map, arena); - BROTLI_FREE(m, arena); - } - BrotliBuildMetaBlockGreedy(m, data, wrapped_last_flush_pos, mask, - prev_byte, prev_byte2, literal_context_lut, num_literal_contexts, - literal_context_map, commands, num_commands, &mb); - if (BROTLI_IS_OOM(m)) return; - } else { - BrotliBuildMetaBlock(m, data, wrapped_last_flush_pos, mask, &block_params, - prev_byte, prev_byte2, - commands, num_commands, - literal_context_mode, - &mb); - if (BROTLI_IS_OOM(m)) return; - } - if (params->quality >= MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS) { - /* The number of distance symbols effectively used for distance - histograms. It might be less than distance alphabet size - for "Large Window Brotli" (32-bit). */ - BrotliOptimizeHistograms(block_params.dist.alphabet_size_limit, &mb); - } - BrotliStoreMetaBlock(m, data, wrapped_last_flush_pos, bytes, mask, - prev_byte, prev_byte2, - is_last, - &block_params, - literal_context_mode, - commands, num_commands, - &mb, - storage_ix, storage); - if (BROTLI_IS_OOM(m)) return; - DestroyMetaBlockSplit(m, &mb); - } - if (bytes + 4 < (*storage_ix >> 3)) { - /* Restore the distance cache and last byte. */ - memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0])); - storage[0] = (uint8_t)last_bytes; - storage[1] = (uint8_t)(last_bytes >> 8); - *storage_ix = last_bytes_bits; - BrotliStoreUncompressedMetaBlock(is_last, data, - wrapped_last_flush_pos, mask, - bytes, storage_ix, storage); - } -} - -static void ChooseDistanceParams(BrotliEncoderParams* params) { - uint32_t distance_postfix_bits = 0; - uint32_t num_direct_distance_codes = 0; - - if (params->quality >= MIN_QUALITY_FOR_NONZERO_DISTANCE_PARAMS) { - uint32_t ndirect_msb; - if (params->mode == BROTLI_MODE_FONT) { - distance_postfix_bits = 1; - num_direct_distance_codes = 12; - } else { - distance_postfix_bits = params->dist.distance_postfix_bits; - num_direct_distance_codes = params->dist.num_direct_distance_codes; - } - ndirect_msb = (num_direct_distance_codes >> distance_postfix_bits) & 0x0F; - if (distance_postfix_bits > BROTLI_MAX_NPOSTFIX || - num_direct_distance_codes > BROTLI_MAX_NDIRECT || - (ndirect_msb << distance_postfix_bits) != num_direct_distance_codes) { - distance_postfix_bits = 0; - num_direct_distance_codes = 0; - } - } - - BrotliInitDistanceParams(¶ms->dist, distance_postfix_bits, - num_direct_distance_codes, params->large_window); -} - -static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s) { - MemoryManager* m = &s->memory_manager_; - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - if (s->is_initialized_) return BROTLI_TRUE; - - s->last_bytes_bits_ = 0; - s->last_bytes_ = 0; - s->flint_ = BROTLI_FLINT_DONE; - s->remaining_metadata_bytes_ = BROTLI_UINT32_MAX; - - SanitizeParams(&s->params); - s->params.lgblock = ComputeLgBlock(&s->params); - ChooseDistanceParams(&s->params); - - if (s->params.stream_offset != 0) { - s->flint_ = BROTLI_FLINT_NEEDS_2_BYTES; - /* Poison the distance cache. -16 +- 3 is still less than zero (invalid). */ - s->dist_cache_[0] = -16; - s->dist_cache_[1] = -16; - s->dist_cache_[2] = -16; - s->dist_cache_[3] = -16; - memcpy(s->saved_dist_cache_, s->dist_cache_, sizeof(s->saved_dist_cache_)); - } - - RingBufferSetup(&s->params, &s->ringbuffer_); - - /* Initialize last byte with stream header. */ - { - int lgwin = s->params.lgwin; - if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || - s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { - lgwin = BROTLI_MAX(int, lgwin, 18); - } - if (s->params.stream_offset == 0) { - EncodeWindowBits(lgwin, s->params.large_window, - &s->last_bytes_, &s->last_bytes_bits_); - } else { - /* Bigger values have the same effect, but could cause overflows. */ - s->params.stream_offset = BROTLI_MIN(size_t, - s->params.stream_offset, BROTLI_MAX_BACKWARD_LIMIT(lgwin)); - } - } - - if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { - s->one_pass_arena_ = BROTLI_ALLOC(m, BrotliOnePassArena, 1); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - InitCommandPrefixCodes(s->one_pass_arena_); - } else if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { - s->two_pass_arena_ = BROTLI_ALLOC(m, BrotliTwoPassArena, 1); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } - - s->is_initialized_ = BROTLI_TRUE; - return BROTLI_TRUE; -} - -static void BrotliEncoderInitParams(BrotliEncoderParams* params) { - params->mode = BROTLI_DEFAULT_MODE; - params->large_window = BROTLI_FALSE; - params->quality = BROTLI_DEFAULT_QUALITY; - params->lgwin = BROTLI_DEFAULT_WINDOW; - params->lgblock = 0; - params->stream_offset = 0; - params->size_hint = 0; - params->disable_literal_context_modeling = BROTLI_FALSE; - BrotliInitSharedEncoderDictionary(¶ms->dictionary); - params->dist.distance_postfix_bits = 0; - params->dist.num_direct_distance_codes = 0; - params->dist.alphabet_size_max = - BROTLI_DISTANCE_ALPHABET_SIZE(0, 0, BROTLI_MAX_DISTANCE_BITS); - params->dist.alphabet_size_limit = params->dist.alphabet_size_max; - params->dist.max_distance = BROTLI_MAX_DISTANCE; -} - -static void BrotliEncoderCleanupParams(MemoryManager* m, - BrotliEncoderParams* params) { - BrotliCleanupSharedEncoderDictionary(m, ¶ms->dictionary); -} - -static void BrotliEncoderInitState(BrotliEncoderState* s) { - BrotliEncoderInitParams(&s->params); - s->input_pos_ = 0; - s->num_commands_ = 0; - s->num_literals_ = 0; - s->last_insert_len_ = 0; - s->last_flush_pos_ = 0; - s->last_processed_pos_ = 0; - s->prev_byte_ = 0; - s->prev_byte2_ = 0; - s->storage_size_ = 0; - s->storage_ = 0; - HasherInit(&s->hasher_); - s->large_table_ = NULL; - s->large_table_size_ = 0; - s->one_pass_arena_ = NULL; - s->two_pass_arena_ = NULL; - s->command_buf_ = NULL; - s->literal_buf_ = NULL; - s->total_in_ = 0; - s->next_out_ = NULL; - s->available_out_ = 0; - s->total_out_ = 0; - s->stream_state_ = BROTLI_STREAM_PROCESSING; - s->is_last_block_emitted_ = BROTLI_FALSE; - s->is_initialized_ = BROTLI_FALSE; - - RingBufferInit(&s->ringbuffer_); - - s->commands_ = 0; - s->cmd_alloc_size_ = 0; - - /* Initialize distance cache. */ - s->dist_cache_[0] = 4; - s->dist_cache_[1] = 11; - s->dist_cache_[2] = 15; - s->dist_cache_[3] = 16; - /* Save the state of the distance cache in case we need to restore it for - emitting an uncompressed block. */ - memcpy(s->saved_dist_cache_, s->dist_cache_, sizeof(s->saved_dist_cache_)); -} - -BrotliEncoderState* BrotliEncoderCreateInstance( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - BrotliEncoderState* state = (BrotliEncoderState*)BrotliBootstrapAlloc( - sizeof(BrotliEncoderState), alloc_func, free_func, opaque); - if (state == NULL) { - /* BROTLI_DUMP(); */ - return 0; - } - BrotliInitMemoryManager( - &state->memory_manager_, alloc_func, free_func, opaque); - BrotliEncoderInitState(state); - return state; -} - -#ifdef BROTLI_REPORTING -/* When BROTLI_REPORTING is defined extra reporting module have to be linked. */ -void BrotliEncoderOnFinish(const BrotliEncoderState* s); -#define BROTLI_ENCODER_ON_FINISH(s) BrotliEncoderOnFinish(s); -#else -#if !defined(BROTLI_ENCODER_ON_FINISH) -#define BROTLI_ENCODER_ON_FINISH(s) (void)(s); -#endif -#endif - -static void BrotliEncoderCleanupState(BrotliEncoderState* s) { - MemoryManager* m = &s->memory_manager_; - - BROTLI_ENCODER_ON_FINISH(s); - - if (BROTLI_IS_OOM(m)) { - BrotliWipeOutMemoryManager(m); - return; - } - - BROTLI_FREE(m, s->storage_); - BROTLI_FREE(m, s->commands_); - RingBufferFree(m, &s->ringbuffer_); - DestroyHasher(m, &s->hasher_); - BROTLI_FREE(m, s->large_table_); - BROTLI_FREE(m, s->one_pass_arena_); - BROTLI_FREE(m, s->two_pass_arena_); - BROTLI_FREE(m, s->command_buf_); - BROTLI_FREE(m, s->literal_buf_); - BrotliEncoderCleanupParams(m, &s->params); -} - -/* Deinitializes and frees BrotliEncoderState instance. */ -void BrotliEncoderDestroyInstance(BrotliEncoderState* state) { - if (!state) { - return; - } else { - BrotliEncoderCleanupState(state); - BrotliBootstrapFree(state, &state->memory_manager_); - } -} - -/* - Copies the given input data to the internal ring buffer of the compressor. - No processing of the data occurs at this time and this function can be - called multiple times before calling WriteBrotliData() to process the - accumulated input. At most input_block_size() bytes of input data can be - copied to the ring buffer, otherwise the next WriteBrotliData() will fail. - */ -static void CopyInputToRingBuffer(BrotliEncoderState* s, - const size_t input_size, - const uint8_t* input_buffer) { - RingBuffer* ringbuffer_ = &s->ringbuffer_; - MemoryManager* m = &s->memory_manager_; - RingBufferWrite(m, input_buffer, input_size, ringbuffer_); - if (BROTLI_IS_OOM(m)) return; - s->input_pos_ += input_size; - - /* TL;DR: If needed, initialize 7 more bytes in the ring buffer to make the - hashing not depend on uninitialized data. This makes compression - deterministic and it prevents uninitialized memory warnings in Valgrind. - Even without erasing, the output would be valid (but nondeterministic). - - Background information: The compressor stores short (at most 8 bytes) - substrings of the input already read in a hash table, and detects - repetitions by looking up such substrings in the hash table. If it - can find a substring, it checks whether the substring is really there - in the ring buffer (or it's just a hash collision). Should the hash - table become corrupt, this check makes sure that the output is - still valid, albeit the compression ratio would be bad. - - The compressor populates the hash table from the ring buffer as it's - reading new bytes from the input. However, at the last few indexes of - the ring buffer, there are not enough bytes to build full-length - substrings from. Since the hash table always contains full-length - substrings, we erase with dummy zeros here to make sure that those - substrings will contain zeros at the end instead of uninitialized - data. - - Please note that erasing is not necessary (because the - memory region is already initialized since he ring buffer - has a `tail' that holds a copy of the beginning,) so we - skip erasing if we have already gone around at least once in - the ring buffer. - - Only clear during the first round of ring-buffer writes. On - subsequent rounds data in the ring-buffer would be affected. */ - if (ringbuffer_->pos_ <= ringbuffer_->mask_) { - /* This is the first time when the ring buffer is being written. - We clear 7 bytes just after the bytes that have been copied from - the input buffer. - - The ring-buffer has a "tail" that holds a copy of the beginning, - but only once the ring buffer has been fully written once, i.e., - pos <= mask. For the first time, we need to write values - in this tail (where index may be larger than mask), so that - we have exactly defined behavior and don't read uninitialized - memory. Due to performance reasons, hashing reads data using a - LOAD64, which can go 7 bytes beyond the bytes written in the - ring-buffer. */ - memset(ringbuffer_->buffer_ + ringbuffer_->pos_, 0, 7); - } -} - -/* Marks all input as processed. - Returns true if position wrapping occurs. */ -static BROTLI_BOOL UpdateLastProcessedPos(BrotliEncoderState* s) { - uint32_t wrapped_last_processed_pos = WrapPosition(s->last_processed_pos_); - uint32_t wrapped_input_pos = WrapPosition(s->input_pos_); - s->last_processed_pos_ = s->input_pos_; - return TO_BROTLI_BOOL(wrapped_input_pos < wrapped_last_processed_pos); -} - -static void ExtendLastCommand(BrotliEncoderState* s, uint32_t* bytes, - uint32_t* wrapped_last_processed_pos) { - Command* last_command = &s->commands_[s->num_commands_ - 1]; - const uint8_t* data = s->ringbuffer_.buffer_; - const uint32_t mask = s->ringbuffer_.mask_; - uint64_t max_backward_distance = - (((uint64_t)1) << s->params.lgwin) - BROTLI_WINDOW_GAP; - uint64_t last_copy_len = last_command->copy_len_ & 0x1FFFFFF; - uint64_t last_processed_pos = s->last_processed_pos_ - last_copy_len; - uint64_t max_distance = last_processed_pos < max_backward_distance ? - last_processed_pos : max_backward_distance; - uint64_t cmd_dist = (uint64_t)s->dist_cache_[0]; - uint32_t distance_code = CommandRestoreDistanceCode(last_command, - &s->params.dist); - const CompoundDictionary* dict = &s->params.dictionary.compound; - size_t compound_dictionary_size = dict->total_size; - if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES || - distance_code - (BROTLI_NUM_DISTANCE_SHORT_CODES - 1) == cmd_dist) { - if (cmd_dist <= max_distance) { - while (*bytes != 0 && data[*wrapped_last_processed_pos & mask] == - data[(*wrapped_last_processed_pos - cmd_dist) & mask]) { - last_command->copy_len_++; - (*bytes)--; - (*wrapped_last_processed_pos)++; - } - } else { - if ((cmd_dist - max_distance - 1) < compound_dictionary_size && - last_copy_len < cmd_dist - max_distance) { - size_t address = - compound_dictionary_size - (size_t)(cmd_dist - max_distance) + - (size_t)last_copy_len; - size_t br_index = 0; - size_t br_offset; - const uint8_t* chunk; - size_t chunk_length; - while (address >= dict->chunk_offsets[br_index + 1]) br_index++; - br_offset = address - dict->chunk_offsets[br_index]; - chunk = dict->chunk_source[br_index]; - chunk_length = - dict->chunk_offsets[br_index + 1] - dict->chunk_offsets[br_index]; - while (*bytes != 0 && data[*wrapped_last_processed_pos & mask] == - chunk[br_offset]) { - last_command->copy_len_++; - (*bytes)--; - (*wrapped_last_processed_pos)++; - if (++br_offset == chunk_length) { - br_index++; - br_offset = 0; - if (br_index != dict->num_chunks) { - chunk = dict->chunk_source[br_index]; - chunk_length = dict->chunk_offsets[br_index + 1] - - dict->chunk_offsets[br_index]; - } else { - break; - } - } - } - } - } - /* The copy length is at most the metablock size, and thus expressible. */ - GetLengthCode(last_command->insert_len_, - (size_t)((int)(last_command->copy_len_ & 0x1FFFFFF) + - (int)(last_command->copy_len_ >> 25)), - TO_BROTLI_BOOL((last_command->dist_prefix_ & 0x3FF) == 0), - &last_command->cmd_prefix_); - } -} - -/* - Processes the accumulated input data and sets |*out_size| to the length of - the new output meta-block, or to zero if no new output meta-block has been - created (in this case the processed input data is buffered internally). - If |*out_size| is positive, |*output| points to the start of the output - data. If |is_last| or |force_flush| is BROTLI_TRUE, an output meta-block is - always created. However, until |is_last| is BROTLI_TRUE encoder may retain up - to 7 bits of the last byte of output. Byte-alignment could be enforced by - emitting an empty meta-data block. - Returns BROTLI_FALSE if the size of the input data is larger than - input_block_size(). - */ -static BROTLI_BOOL EncodeData( - BrotliEncoderState* s, const BROTLI_BOOL is_last, - const BROTLI_BOOL force_flush, size_t* out_size, uint8_t** output) { - const uint64_t delta = UnprocessedInputSize(s); - uint32_t bytes = (uint32_t)delta; - uint32_t wrapped_last_processed_pos = WrapPosition(s->last_processed_pos_); - uint8_t* data; - uint32_t mask; - MemoryManager* m = &s->memory_manager_; - ContextType literal_context_mode; - ContextLut literal_context_lut; - BROTLI_BOOL fast_compress = - s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || - s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY; - - data = s->ringbuffer_.buffer_; - mask = s->ringbuffer_.mask_; - - if (delta == 0) { /* No new input; still might want to flush or finish. */ - if (!data) { /* No input has been processed so far. */ - if (is_last) { /* Emit complete finalized stream. */ - BROTLI_DCHECK(s->last_bytes_bits_ <= 14); - s->last_bytes_ |= (uint16_t)(3u << s->last_bytes_bits_); - s->last_bytes_bits_ = (uint8_t)(s->last_bytes_bits_ + 2u); - s->tiny_buf_.u8[0] = (uint8_t)s->last_bytes_; - s->tiny_buf_.u8[1] = (uint8_t)(s->last_bytes_ >> 8); - *output = s->tiny_buf_.u8; - *out_size = (s->last_bytes_bits_ + 7u) >> 3u; - return BROTLI_TRUE; - } else { /* No data, not last -> no-op. */ - *out_size = 0; - return BROTLI_TRUE; - } - } else { - /* Fast compress performs flush every block -> flush is no-op. */ - if (!is_last && (!force_flush || fast_compress)) { /* Another no-op. */ - *out_size = 0; - return BROTLI_TRUE; - } - } - } - BROTLI_DCHECK(data); - - if (s->params.quality > s->params.dictionary.max_quality) return BROTLI_FALSE; - /* Adding more blocks after "last" block is forbidden. */ - if (s->is_last_block_emitted_) return BROTLI_FALSE; - if (is_last) s->is_last_block_emitted_ = BROTLI_TRUE; - - if (delta > InputBlockSize(s)) { - return BROTLI_FALSE; - } - if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY && - !s->command_buf_) { - s->command_buf_ = - BROTLI_ALLOC(m, uint32_t, kCompressFragmentTwoPassBlockSize); - s->literal_buf_ = - BROTLI_ALLOC(m, uint8_t, kCompressFragmentTwoPassBlockSize); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->command_buf_) || - BROTLI_IS_NULL(s->literal_buf_)) { - return BROTLI_FALSE; - } - } - - if (fast_compress) { - uint8_t* storage; - size_t storage_ix = s->last_bytes_bits_; - size_t table_size; - int* table; - - storage = GetBrotliStorage(s, 2 * bytes + 503); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - storage[0] = (uint8_t)s->last_bytes_; - storage[1] = (uint8_t)(s->last_bytes_ >> 8); - table = GetHashTable(s, s->params.quality, bytes, &table_size); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { - BrotliCompressFragmentFast( - s->one_pass_arena_, &data[wrapped_last_processed_pos & mask], - bytes, is_last, - table, table_size, - &storage_ix, storage); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } else { - BrotliCompressFragmentTwoPass( - s->two_pass_arena_, &data[wrapped_last_processed_pos & mask], - bytes, is_last, - s->command_buf_, s->literal_buf_, - table, table_size, - &storage_ix, storage); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } - s->last_bytes_ = (uint16_t)(storage[storage_ix >> 3]); - s->last_bytes_bits_ = storage_ix & 7u; - UpdateLastProcessedPos(s); - *output = &storage[0]; - *out_size = storage_ix >> 3; - return BROTLI_TRUE; - } - - { - /* Theoretical max number of commands is 1 per 2 bytes. */ - size_t newsize = s->num_commands_ + bytes / 2 + 1; - if (newsize > s->cmd_alloc_size_) { - Command* new_commands; - /* Reserve a bit more memory to allow merging with a next block - without reallocation: that would impact speed. */ - newsize += (bytes / 4) + 16; - s->cmd_alloc_size_ = newsize; - new_commands = BROTLI_ALLOC(m, Command, newsize); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_commands)) return BROTLI_FALSE; - if (s->commands_) { - memcpy(new_commands, s->commands_, sizeof(Command) * s->num_commands_); - BROTLI_FREE(m, s->commands_); - } - s->commands_ = new_commands; - } - } - - InitOrStitchToPreviousBlock(m, &s->hasher_, data, mask, &s->params, - wrapped_last_processed_pos, bytes, is_last); - - literal_context_mode = ChooseContextMode( - &s->params, data, WrapPosition(s->last_flush_pos_), - mask, (size_t)(s->input_pos_ - s->last_flush_pos_)); - literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); - - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - - if (s->num_commands_ && s->last_insert_len_ == 0) { - ExtendLastCommand(s, &bytes, &wrapped_last_processed_pos); - } - - if (s->params.quality == ZOPFLIFICATION_QUALITY) { - BROTLI_DCHECK(s->params.hasher.type == 10); - BrotliCreateZopfliBackwardReferences(m, bytes, wrapped_last_processed_pos, - data, mask, literal_context_lut, &s->params, - &s->hasher_, s->dist_cache_, - &s->last_insert_len_, &s->commands_[s->num_commands_], - &s->num_commands_, &s->num_literals_); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } else if (s->params.quality == HQ_ZOPFLIFICATION_QUALITY) { - BROTLI_DCHECK(s->params.hasher.type == 10); - BrotliCreateHqZopfliBackwardReferences(m, bytes, wrapped_last_processed_pos, - data, mask, literal_context_lut, &s->params, - &s->hasher_, s->dist_cache_, - &s->last_insert_len_, &s->commands_[s->num_commands_], - &s->num_commands_, &s->num_literals_); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } else { - BrotliCreateBackwardReferences(bytes, wrapped_last_processed_pos, - data, mask, literal_context_lut, &s->params, - &s->hasher_, s->dist_cache_, - &s->last_insert_len_, &s->commands_[s->num_commands_], - &s->num_commands_, &s->num_literals_); - } - - { - const size_t max_length = MaxMetablockSize(&s->params); - const size_t max_literals = max_length / 8; - const size_t max_commands = max_length / 8; - const size_t processed_bytes = (size_t)(s->input_pos_ - s->last_flush_pos_); - /* If maximal possible additional block doesn't fit metablock, flush now. */ - /* TODO(eustas): Postpone decision until next block arrives? */ - const BROTLI_BOOL next_input_fits_metablock = TO_BROTLI_BOOL( - processed_bytes + InputBlockSize(s) <= max_length); - /* If block splitting is not used, then flush as soon as there is some - amount of commands / literals produced. */ - const BROTLI_BOOL should_flush = TO_BROTLI_BOOL( - s->params.quality < MIN_QUALITY_FOR_BLOCK_SPLIT && - s->num_literals_ + s->num_commands_ >= MAX_NUM_DELAYED_SYMBOLS); - if (!is_last && !force_flush && !should_flush && - next_input_fits_metablock && - s->num_literals_ < max_literals && - s->num_commands_ < max_commands) { - /* Merge with next input block. Everything will happen later. */ - if (UpdateLastProcessedPos(s)) { - HasherReset(&s->hasher_); - } - *out_size = 0; - return BROTLI_TRUE; - } - } - - /* Create the last insert-only command. */ - if (s->last_insert_len_ > 0) { - InitInsertCommand(&s->commands_[s->num_commands_++], s->last_insert_len_); - s->num_literals_ += s->last_insert_len_; - s->last_insert_len_ = 0; - } - - if (!is_last && s->input_pos_ == s->last_flush_pos_) { - /* We have no new input data and we don't have to finish the stream, so - nothing to do. */ - *out_size = 0; - return BROTLI_TRUE; - } - BROTLI_DCHECK(s->input_pos_ >= s->last_flush_pos_); - BROTLI_DCHECK(s->input_pos_ > s->last_flush_pos_ || is_last); - BROTLI_DCHECK(s->input_pos_ - s->last_flush_pos_ <= 1u << 24); - { - const uint32_t metablock_size = - (uint32_t)(s->input_pos_ - s->last_flush_pos_); - uint8_t* storage = GetBrotliStorage(s, 2 * metablock_size + 503); - size_t storage_ix = s->last_bytes_bits_; - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - storage[0] = (uint8_t)s->last_bytes_; - storage[1] = (uint8_t)(s->last_bytes_ >> 8); - WriteMetaBlockInternal( - m, data, mask, s->last_flush_pos_, metablock_size, is_last, - literal_context_mode, &s->params, s->prev_byte_, s->prev_byte2_, - s->num_literals_, s->num_commands_, s->commands_, s->saved_dist_cache_, - s->dist_cache_, &storage_ix, storage); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - s->last_bytes_ = (uint16_t)(storage[storage_ix >> 3]); - s->last_bytes_bits_ = storage_ix & 7u; - s->last_flush_pos_ = s->input_pos_; - if (UpdateLastProcessedPos(s)) { - HasherReset(&s->hasher_); - } - if (s->last_flush_pos_ > 0) { - s->prev_byte_ = data[((uint32_t)s->last_flush_pos_ - 1) & mask]; - } - if (s->last_flush_pos_ > 1) { - s->prev_byte2_ = data[(uint32_t)(s->last_flush_pos_ - 2) & mask]; - } - s->num_commands_ = 0; - s->num_literals_ = 0; - /* Save the state of the distance cache in case we need to restore it for - emitting an uncompressed block. */ - memcpy(s->saved_dist_cache_, s->dist_cache_, sizeof(s->saved_dist_cache_)); - *output = &storage[0]; - *out_size = storage_ix >> 3; - return BROTLI_TRUE; - } -} - -/* Dumps remaining output bits and metadata header to |header|. - Returns number of produced bytes. - REQUIRED: |header| should be 8-byte aligned and at least 16 bytes long. - REQUIRED: |block_size| <= (1 << 24). */ -static size_t WriteMetadataHeader( - BrotliEncoderState* s, const size_t block_size, uint8_t* header) { - size_t storage_ix; - storage_ix = s->last_bytes_bits_; - header[0] = (uint8_t)s->last_bytes_; - header[1] = (uint8_t)(s->last_bytes_ >> 8); - s->last_bytes_ = 0; - s->last_bytes_bits_ = 0; - - BrotliWriteBits(1, 0, &storage_ix, header); - BrotliWriteBits(2, 3, &storage_ix, header); - BrotliWriteBits(1, 0, &storage_ix, header); - if (block_size == 0) { - BrotliWriteBits(2, 0, &storage_ix, header); - } else { - uint32_t nbits = (block_size == 1) ? 1 : - (Log2FloorNonZero((uint32_t)block_size - 1) + 1); - uint32_t nbytes = (nbits + 7) / 8; - BrotliWriteBits(2, nbytes, &storage_ix, header); - BrotliWriteBits(8 * nbytes, block_size - 1, &storage_ix, header); - } - return (storage_ix + 7u) >> 3; -} - -size_t BrotliEncoderMaxCompressedSize(size_t input_size) { - /* [window bits / empty metadata] + N * [uncompressed] + [last empty] */ - size_t num_large_blocks = input_size >> 14; - size_t overhead = 2 + (4 * num_large_blocks) + 3 + 1; - size_t result = input_size + overhead; - if (input_size == 0) return 2; - return (result < input_size) ? 0 : result; -} - -/* Wraps data to uncompressed brotli stream with minimal window size. - |output| should point at region with at least BrotliEncoderMaxCompressedSize - addressable bytes. - Returns the length of stream. */ -static size_t MakeUncompressedStream( - const uint8_t* input, size_t input_size, uint8_t* output) { - size_t size = input_size; - size_t result = 0; - size_t offset = 0; - if (input_size == 0) { - output[0] = 6; - return 1; - } - output[result++] = 0x21; /* window bits = 10, is_last = false */ - output[result++] = 0x03; /* empty metadata, padding */ - while (size > 0) { - uint32_t nibbles = 0; - uint32_t chunk_size; - uint32_t bits; - chunk_size = (size > (1u << 24)) ? (1u << 24) : (uint32_t)size; - if (chunk_size > (1u << 16)) nibbles = (chunk_size > (1u << 20)) ? 2 : 1; - bits = - (nibbles << 1) | ((chunk_size - 1) << 3) | (1u << (19 + 4 * nibbles)); - output[result++] = (uint8_t)bits; - output[result++] = (uint8_t)(bits >> 8); - output[result++] = (uint8_t)(bits >> 16); - if (nibbles == 2) output[result++] = (uint8_t)(bits >> 24); - memcpy(&output[result], &input[offset], chunk_size); - result += chunk_size; - offset += chunk_size; - size -= chunk_size; - } - output[result++] = 3; - return result; -} - -BROTLI_BOOL BrotliEncoderCompress( - int quality, int lgwin, BrotliEncoderMode mode, size_t input_size, - const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)], - size_t* encoded_size, - uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]) { - BrotliEncoderState* s; - size_t out_size = *encoded_size; - const uint8_t* input_start = input_buffer; - uint8_t* output_start = encoded_buffer; - size_t max_out_size = BrotliEncoderMaxCompressedSize(input_size); - if (out_size == 0) { - /* Output buffer needs at least one byte. */ - return BROTLI_FALSE; - } - if (input_size == 0) { - /* Handle the special case of empty input. */ - *encoded_size = 1; - *encoded_buffer = 6; - return BROTLI_TRUE; - } - - s = BrotliEncoderCreateInstance(0, 0, 0); - if (!s) { - return BROTLI_FALSE; - } else { - size_t available_in = input_size; - const uint8_t* next_in = input_buffer; - size_t available_out = *encoded_size; - uint8_t* next_out = encoded_buffer; - size_t total_out = 0; - BROTLI_BOOL result = BROTLI_FALSE; - /* TODO(eustas): check that parameters are sane. */ - BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, (uint32_t)quality); - BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, (uint32_t)lgwin); - BrotliEncoderSetParameter(s, BROTLI_PARAM_MODE, (uint32_t)mode); - BrotliEncoderSetParameter(s, BROTLI_PARAM_SIZE_HINT, (uint32_t)input_size); - if (lgwin > BROTLI_MAX_WINDOW_BITS) { - BrotliEncoderSetParameter(s, BROTLI_PARAM_LARGE_WINDOW, BROTLI_TRUE); - } - result = BrotliEncoderCompressStream(s, BROTLI_OPERATION_FINISH, - &available_in, &next_in, &available_out, &next_out, &total_out); - if (!BrotliEncoderIsFinished(s)) result = 0; - *encoded_size = total_out; - BrotliEncoderDestroyInstance(s); - if (!result || (max_out_size && *encoded_size > max_out_size)) { - goto fallback; - } - return BROTLI_TRUE; - } -fallback: - *encoded_size = 0; - if (!max_out_size) return BROTLI_FALSE; - if (out_size >= max_out_size) { - *encoded_size = - MakeUncompressedStream(input_start, input_size, output_start); - return BROTLI_TRUE; - } - return BROTLI_FALSE; -} - -static void InjectBytePaddingBlock(BrotliEncoderState* s) { - uint32_t seal = s->last_bytes_; - size_t seal_bits = s->last_bytes_bits_; - uint8_t* destination; - s->last_bytes_ = 0; - s->last_bytes_bits_ = 0; - /* is_last = 0, data_nibbles = 11, reserved = 0, meta_nibbles = 00 */ - seal |= 0x6u << seal_bits; - seal_bits += 6; - /* If we have already created storage, then append to it. - Storage is valid until next block is being compressed. */ - if (s->next_out_) { - destination = s->next_out_ + s->available_out_; - } else { - destination = s->tiny_buf_.u8; - s->next_out_ = destination; - } - destination[0] = (uint8_t)seal; - if (seal_bits > 8) destination[1] = (uint8_t)(seal >> 8); - if (seal_bits > 16) destination[2] = (uint8_t)(seal >> 16); - s->available_out_ += (seal_bits + 7) >> 3; -} - -/* Fills the |total_out|, if it is not NULL. */ -static void SetTotalOut(BrotliEncoderState* s, size_t* total_out) { - if (total_out) { - /* Saturating conversion uint64_t -> size_t */ - size_t result = (size_t)-1; - if (s->total_out_ < result) { - result = (size_t)s->total_out_; - } - *total_out = result; - } -} - -/* Injects padding bits or pushes compressed data to output. - Returns false if nothing is done. */ -static BROTLI_BOOL InjectFlushOrPushOutput(BrotliEncoderState* s, - size_t* available_out, uint8_t** next_out, size_t* total_out) { - if (s->stream_state_ == BROTLI_STREAM_FLUSH_REQUESTED && - s->last_bytes_bits_ != 0) { - InjectBytePaddingBlock(s); - return BROTLI_TRUE; - } - - if (s->available_out_ != 0 && *available_out != 0) { - size_t copy_output_size = - BROTLI_MIN(size_t, s->available_out_, *available_out); - memcpy(*next_out, s->next_out_, copy_output_size); - *next_out += copy_output_size; - *available_out -= copy_output_size; - s->next_out_ += copy_output_size; - s->available_out_ -= copy_output_size; - s->total_out_ += copy_output_size; - SetTotalOut(s, total_out); - return BROTLI_TRUE; - } - - return BROTLI_FALSE; -} - -static void CheckFlushComplete(BrotliEncoderState* s) { - if (s->stream_state_ == BROTLI_STREAM_FLUSH_REQUESTED && - s->available_out_ == 0) { - s->stream_state_ = BROTLI_STREAM_PROCESSING; - s->next_out_ = 0; - } -} - -static BROTLI_BOOL BrotliEncoderCompressStreamFast( - BrotliEncoderState* s, BrotliEncoderOperation op, size_t* available_in, - const uint8_t** next_in, size_t* available_out, uint8_t** next_out, - size_t* total_out) { - const size_t block_size_limit = (size_t)1 << s->params.lgwin; - const size_t buf_size = BROTLI_MIN(size_t, kCompressFragmentTwoPassBlockSize, - BROTLI_MIN(size_t, *available_in, block_size_limit)); - uint32_t* tmp_command_buf = NULL; - uint32_t* command_buf = NULL; - uint8_t* tmp_literal_buf = NULL; - uint8_t* literal_buf = NULL; - MemoryManager* m = &s->memory_manager_; - if (s->params.quality != FAST_ONE_PASS_COMPRESSION_QUALITY && - s->params.quality != FAST_TWO_PASS_COMPRESSION_QUALITY) { - return BROTLI_FALSE; - } - if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { - if (!s->command_buf_ && buf_size == kCompressFragmentTwoPassBlockSize) { - s->command_buf_ = - BROTLI_ALLOC(m, uint32_t, kCompressFragmentTwoPassBlockSize); - s->literal_buf_ = - BROTLI_ALLOC(m, uint8_t, kCompressFragmentTwoPassBlockSize); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->command_buf_) || - BROTLI_IS_NULL(s->literal_buf_)) { - return BROTLI_FALSE; - } - } - if (s->command_buf_) { - command_buf = s->command_buf_; - literal_buf = s->literal_buf_; - } else { - tmp_command_buf = BROTLI_ALLOC(m, uint32_t, buf_size); - tmp_literal_buf = BROTLI_ALLOC(m, uint8_t, buf_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp_command_buf) || - BROTLI_IS_NULL(tmp_literal_buf)) { - return BROTLI_FALSE; - } - command_buf = tmp_command_buf; - literal_buf = tmp_literal_buf; - } - } - - while (BROTLI_TRUE) { - if (InjectFlushOrPushOutput(s, available_out, next_out, total_out)) { - continue; - } - - /* Compress block only when internal output buffer is empty, stream is not - finished, there is no pending flush request, and there is either - additional input or pending operation. */ - if (s->available_out_ == 0 && - s->stream_state_ == BROTLI_STREAM_PROCESSING && - (*available_in != 0 || op != BROTLI_OPERATION_PROCESS)) { - size_t block_size = BROTLI_MIN(size_t, block_size_limit, *available_in); - BROTLI_BOOL is_last = - (*available_in == block_size) && (op == BROTLI_OPERATION_FINISH); - BROTLI_BOOL force_flush = - (*available_in == block_size) && (op == BROTLI_OPERATION_FLUSH); - size_t max_out_size = 2 * block_size + 503; - BROTLI_BOOL inplace = BROTLI_TRUE; - uint8_t* storage = NULL; - size_t storage_ix = s->last_bytes_bits_; - size_t table_size; - int* table; - - if (force_flush && block_size == 0) { - s->stream_state_ = BROTLI_STREAM_FLUSH_REQUESTED; - continue; - } - if (max_out_size <= *available_out) { - storage = *next_out; - } else { - inplace = BROTLI_FALSE; - storage = GetBrotliStorage(s, max_out_size); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } - storage[0] = (uint8_t)s->last_bytes_; - storage[1] = (uint8_t)(s->last_bytes_ >> 8); - table = GetHashTable(s, s->params.quality, block_size, &table_size); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - - if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { - BrotliCompressFragmentFast(s->one_pass_arena_, *next_in, block_size, - is_last, table, table_size, &storage_ix, storage); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } else { - BrotliCompressFragmentTwoPass(s->two_pass_arena_, *next_in, block_size, - is_last, command_buf, literal_buf, table, table_size, - &storage_ix, storage); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - } - if (block_size != 0) { - *next_in += block_size; - *available_in -= block_size; - s->total_in_ += block_size; - } - if (inplace) { - size_t out_bytes = storage_ix >> 3; - BROTLI_DCHECK(out_bytes <= *available_out); - BROTLI_DCHECK((storage_ix & 7) == 0 || out_bytes < *available_out); - *next_out += out_bytes; - *available_out -= out_bytes; - s->total_out_ += out_bytes; - SetTotalOut(s, total_out); - } else { - size_t out_bytes = storage_ix >> 3; - s->next_out_ = storage; - s->available_out_ = out_bytes; - } - s->last_bytes_ = (uint16_t)(storage[storage_ix >> 3]); - s->last_bytes_bits_ = storage_ix & 7u; - - if (force_flush) s->stream_state_ = BROTLI_STREAM_FLUSH_REQUESTED; - if (is_last) s->stream_state_ = BROTLI_STREAM_FINISHED; - continue; - } - break; - } - BROTLI_FREE(m, tmp_command_buf); - BROTLI_FREE(m, tmp_literal_buf); - CheckFlushComplete(s); - return BROTLI_TRUE; -} - -static BROTLI_BOOL ProcessMetadata( - BrotliEncoderState* s, size_t* available_in, const uint8_t** next_in, - size_t* available_out, uint8_t** next_out, size_t* total_out) { - if (*available_in > (1u << 24)) return BROTLI_FALSE; - /* Switch to metadata block workflow, if required. */ - if (s->stream_state_ == BROTLI_STREAM_PROCESSING) { - s->remaining_metadata_bytes_ = (uint32_t)*available_in; - s->stream_state_ = BROTLI_STREAM_METADATA_HEAD; - } - if (s->stream_state_ != BROTLI_STREAM_METADATA_HEAD && - s->stream_state_ != BROTLI_STREAM_METADATA_BODY) { - return BROTLI_FALSE; - } - - while (BROTLI_TRUE) { - if (InjectFlushOrPushOutput(s, available_out, next_out, total_out)) { - continue; - } - if (s->available_out_ != 0) break; - - if (s->input_pos_ != s->last_flush_pos_) { - BROTLI_BOOL result = EncodeData(s, BROTLI_FALSE, BROTLI_TRUE, - &s->available_out_, &s->next_out_); - if (!result) return BROTLI_FALSE; - continue; - } - - if (s->stream_state_ == BROTLI_STREAM_METADATA_HEAD) { - s->next_out_ = s->tiny_buf_.u8; - s->available_out_ = - WriteMetadataHeader(s, s->remaining_metadata_bytes_, s->next_out_); - s->stream_state_ = BROTLI_STREAM_METADATA_BODY; - continue; - } else { - /* Exit workflow only when there is no more input and no more output. - Otherwise client may continue producing empty metadata blocks. */ - if (s->remaining_metadata_bytes_ == 0) { - s->remaining_metadata_bytes_ = BROTLI_UINT32_MAX; - s->stream_state_ = BROTLI_STREAM_PROCESSING; - break; - } - if (*available_out) { - /* Directly copy input to output. */ - uint32_t copy = (uint32_t)BROTLI_MIN( - size_t, s->remaining_metadata_bytes_, *available_out); - memcpy(*next_out, *next_in, copy); - *next_in += copy; - *available_in -= copy; - s->total_in_ += copy; /* not actually data input, though */ - s->remaining_metadata_bytes_ -= copy; - *next_out += copy; - *available_out -= copy; - } else { - /* This guarantees progress in "TakeOutput" workflow. */ - uint32_t copy = BROTLI_MIN(uint32_t, s->remaining_metadata_bytes_, 16); - s->next_out_ = s->tiny_buf_.u8; - memcpy(s->next_out_, *next_in, copy); - *next_in += copy; - *available_in -= copy; - s->total_in_ += copy; /* not actually data input, though */ - s->remaining_metadata_bytes_ -= copy; - s->available_out_ = copy; - } - continue; - } - } - - return BROTLI_TRUE; -} - -static void UpdateSizeHint(BrotliEncoderState* s, size_t available_in) { - if (s->params.size_hint == 0) { - uint64_t delta = UnprocessedInputSize(s); - uint64_t tail = available_in; - uint32_t limit = 1u << 30; - uint32_t total; - if ((delta >= limit) || (tail >= limit) || ((delta + tail) >= limit)) { - total = limit; - } else { - total = (uint32_t)(delta + tail); - } - s->params.size_hint = total; - } -} - -BROTLI_BOOL BrotliEncoderCompressStream( - BrotliEncoderState* s, BrotliEncoderOperation op, size_t* available_in, - const uint8_t** next_in, size_t* available_out, uint8_t** next_out, - size_t* total_out) { - if (!EnsureInitialized(s)) return BROTLI_FALSE; - - /* Unfinished metadata block; check requirements. */ - if (s->remaining_metadata_bytes_ != BROTLI_UINT32_MAX) { - if (*available_in != s->remaining_metadata_bytes_) return BROTLI_FALSE; - if (op != BROTLI_OPERATION_EMIT_METADATA) return BROTLI_FALSE; - } - - if (op == BROTLI_OPERATION_EMIT_METADATA) { - UpdateSizeHint(s, 0); /* First data metablock might be emitted here. */ - return ProcessMetadata( - s, available_in, next_in, available_out, next_out, total_out); - } - - if (s->stream_state_ == BROTLI_STREAM_METADATA_HEAD || - s->stream_state_ == BROTLI_STREAM_METADATA_BODY) { - return BROTLI_FALSE; - } - - if (s->stream_state_ != BROTLI_STREAM_PROCESSING && *available_in != 0) { - return BROTLI_FALSE; - } - if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || - s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { - return BrotliEncoderCompressStreamFast(s, op, available_in, next_in, - available_out, next_out, total_out); - } - while (BROTLI_TRUE) { - size_t remaining_block_size = RemainingInputBlockSize(s); - /* Shorten input to flint size. */ - if (s->flint_ >= 0 && remaining_block_size > (size_t)s->flint_) { - remaining_block_size = (size_t)s->flint_; - } - - if (remaining_block_size != 0 && *available_in != 0) { - size_t copy_input_size = - BROTLI_MIN(size_t, remaining_block_size, *available_in); - CopyInputToRingBuffer(s, copy_input_size, *next_in); - *next_in += copy_input_size; - *available_in -= copy_input_size; - s->total_in_ += copy_input_size; - if (s->flint_ > 0) s->flint_ = (int8_t)(s->flint_ - (int)copy_input_size); - continue; - } - - if (InjectFlushOrPushOutput(s, available_out, next_out, total_out)) { - /* Exit the "emit flint" workflow. */ - if (s->flint_ == BROTLI_FLINT_WAITING_FOR_FLUSHING) { - CheckFlushComplete(s); - if (s->stream_state_ == BROTLI_STREAM_PROCESSING) { - s->flint_ = BROTLI_FLINT_DONE; - } - } - continue; - } - - /* Compress data only when internal output buffer is empty, stream is not - finished and there is no pending flush request. */ - if (s->available_out_ == 0 && - s->stream_state_ == BROTLI_STREAM_PROCESSING) { - if (remaining_block_size == 0 || op != BROTLI_OPERATION_PROCESS) { - BROTLI_BOOL is_last = TO_BROTLI_BOOL( - (*available_in == 0) && op == BROTLI_OPERATION_FINISH); - BROTLI_BOOL force_flush = TO_BROTLI_BOOL( - (*available_in == 0) && op == BROTLI_OPERATION_FLUSH); - BROTLI_BOOL result; - /* Force emitting (uncompressed) piece containing flint. */ - if (!is_last && s->flint_ == 0) { - s->flint_ = BROTLI_FLINT_WAITING_FOR_FLUSHING; - force_flush = BROTLI_TRUE; - } - UpdateSizeHint(s, *available_in); - result = EncodeData(s, is_last, force_flush, - &s->available_out_, &s->next_out_); - if (!result) return BROTLI_FALSE; - if (force_flush) s->stream_state_ = BROTLI_STREAM_FLUSH_REQUESTED; - if (is_last) s->stream_state_ = BROTLI_STREAM_FINISHED; - continue; - } - } - break; - } - CheckFlushComplete(s); - return BROTLI_TRUE; -} - -BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* s) { - return TO_BROTLI_BOOL(s->stream_state_ == BROTLI_STREAM_FINISHED && - !BrotliEncoderHasMoreOutput(s)); -} - -BROTLI_BOOL BrotliEncoderHasMoreOutput(BrotliEncoderState* s) { - return TO_BROTLI_BOOL(s->available_out_ != 0); -} - -const uint8_t* BrotliEncoderTakeOutput(BrotliEncoderState* s, size_t* size) { - size_t consumed_size = s->available_out_; - uint8_t* result = s->next_out_; - if (*size) { - consumed_size = BROTLI_MIN(size_t, *size, s->available_out_); - } - if (consumed_size) { - s->next_out_ += consumed_size; - s->available_out_ -= consumed_size; - s->total_out_ += consumed_size; - CheckFlushComplete(s); - *size = consumed_size; - } else { - *size = 0; - result = 0; - } - return result; -} - -uint32_t BrotliEncoderVersion(void) { - return BROTLI_VERSION; -} - -BrotliEncoderPreparedDictionary* BrotliEncoderPrepareDictionary( - BrotliSharedDictionaryType type, size_t size, - const uint8_t data[BROTLI_ARRAY_PARAM(size)], int quality, - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - ManagedDictionary* managed_dictionary = NULL; - BROTLI_BOOL type_is_known = BROTLI_FALSE; - type_is_known |= (type == BROTLI_SHARED_DICTIONARY_RAW); -#if defined(BROTLI_EXPERIMENTAL) - type_is_known |= (type == BROTLI_SHARED_DICTIONARY_SERIALIZED); -#endif /* BROTLI_EXPERIMENTAL */ - if (!type_is_known) { - return NULL; - } - managed_dictionary = - BrotliCreateManagedDictionary(alloc_func, free_func, opaque); - if (managed_dictionary == NULL) { - return NULL; - } - if (type == BROTLI_SHARED_DICTIONARY_RAW) { - managed_dictionary->dictionary = (uint32_t*)CreatePreparedDictionary( - &managed_dictionary->memory_manager_, data, size); - } -#if defined(BROTLI_EXPERIMENTAL) - if (type == BROTLI_SHARED_DICTIONARY_SERIALIZED) { - SharedEncoderDictionary* dict = (SharedEncoderDictionary*)BrotliAllocate( - &managed_dictionary->memory_manager_, sizeof(SharedEncoderDictionary)); - managed_dictionary->dictionary = (uint32_t*)dict; - if (dict != NULL) { - BROTLI_BOOL ok = BrotliInitCustomSharedEncoderDictionary( - &managed_dictionary->memory_manager_, data, size, quality, dict); - if (!ok) { - BrotliFree(&managed_dictionary->memory_manager_, dict); - managed_dictionary->dictionary = NULL; - } - } - } -#else /* BROTLI_EXPERIMENTAL */ - (void)quality; -#endif /* BROTLI_EXPERIMENTAL */ - if (managed_dictionary->dictionary == NULL) { - BrotliDestroyManagedDictionary(managed_dictionary); - return NULL; - } - return (BrotliEncoderPreparedDictionary*)managed_dictionary; -} - -void BrotliEncoderDestroyPreparedDictionary( - BrotliEncoderPreparedDictionary* dictionary) { - ManagedDictionary* dict = (ManagedDictionary*)dictionary; - if (!dictionary) return; - /* First field of dictionary structs. */ - /* Only managed dictionaries are eligible for destruction by this method. */ - if (dict->magic != kManagedDictionaryMagic) { - return; - } - if (dict->dictionary == NULL) { - /* This should never ever happen. */ - } else if (*dict->dictionary == kLeanPreparedDictionaryMagic) { - DestroyPreparedDictionary( - &dict->memory_manager_, (PreparedDictionary*)dict->dictionary); - } else if (*dict->dictionary == kSharedDictionaryMagic) { - BrotliCleanupSharedEncoderDictionary(&dict->memory_manager_, - (SharedEncoderDictionary*)dict->dictionary); - BrotliFree(&dict->memory_manager_, dict->dictionary); - } else { - /* There is also kPreparedDictionaryMagic, but such instances should be - * constructed and destroyed by different means. */ - } - dict->dictionary = NULL; - BrotliDestroyManagedDictionary(dict); -} - -BROTLI_BOOL BrotliEncoderAttachPreparedDictionary(BrotliEncoderState* state, - const BrotliEncoderPreparedDictionary* dictionary) { - /* First field of dictionary structs */ - const BrotliEncoderPreparedDictionary* dict = dictionary; - uint32_t magic = *((const uint32_t*)dict); - SharedEncoderDictionary* current = NULL; - if (magic == kManagedDictionaryMagic) { - /* Unwrap managed dictionary. */ - ManagedDictionary* managed_dictionary = (ManagedDictionary*)dict; - magic = *managed_dictionary->dictionary; - dict = (BrotliEncoderPreparedDictionary*)managed_dictionary->dictionary; - } - current = &state->params.dictionary; - if (magic == kPreparedDictionaryMagic || - magic == kLeanPreparedDictionaryMagic) { - const PreparedDictionary* prepared = (const PreparedDictionary*)dict; - if (!AttachPreparedDictionary(¤t->compound, prepared)) { - return BROTLI_FALSE; - } - } else if (magic == kSharedDictionaryMagic) { - const SharedEncoderDictionary* attached = - (const SharedEncoderDictionary*)dict; - BROTLI_BOOL was_default = !current->contextual.context_based && - current->contextual.num_dictionaries == 1 && - current->contextual.dict[0]->hash_table_words == - kStaticDictionaryHashWords && - current->contextual.dict[0]->hash_table_lengths == - kStaticDictionaryHashLengths; - BROTLI_BOOL new_default = !attached->contextual.context_based && - attached->contextual.num_dictionaries == 1 && - attached->contextual.dict[0]->hash_table_words == - kStaticDictionaryHashWords && - attached->contextual.dict[0]->hash_table_lengths == - kStaticDictionaryHashLengths; - size_t i; - if (state->is_initialized_) return BROTLI_FALSE; - current->max_quality = - BROTLI_MIN(int, current->max_quality, attached->max_quality); - for (i = 0; i < attached->compound.num_chunks; i++) { - if (!AttachPreparedDictionary(¤t->compound, - attached->compound.chunks[i])) { - return BROTLI_FALSE; - } - } - if (!new_default) { - if (!was_default) return BROTLI_FALSE; - /* Copy by value, but then set num_instances_ to 0 because their memory - is managed by attached, not by current */ - current->contextual = attached->contextual; - current->contextual.num_instances_ = 0; - } - } else { - return BROTLI_FALSE; - } - return BROTLI_TRUE; -} - -size_t BrotliEncoderEstimatePeakMemoryUsage(int quality, int lgwin, - size_t input_size) { - BrotliEncoderParams params; - size_t memory_manager_slots = BROTLI_ENCODER_MEMORY_MANAGER_SLOTS; - size_t memory_manager_size = memory_manager_slots * sizeof(void*); - BrotliEncoderInitParams(¶ms); - params.quality = quality; - params.lgwin = lgwin; - params.size_hint = input_size; - params.large_window = lgwin > BROTLI_MAX_WINDOW_BITS; - SanitizeParams(¶ms); - params.lgblock = ComputeLgBlock(¶ms); - ChooseHasher(¶ms, ¶ms.hasher); - if (params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || - params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { - size_t state_size = sizeof(BrotliEncoderState); - size_t block_size = BROTLI_MIN(size_t, input_size, (1ul << params.lgwin)); - size_t hash_table_size = - HashTableSize(MaxHashTableSize(params.quality), block_size); - size_t hash_size = - (hash_table_size < (1u << 10)) ? 0 : sizeof(int) * hash_table_size; - size_t cmdbuf_size = params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY ? - 5 * BROTLI_MIN(size_t, block_size, 1ul << 17) : 0; - if (params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { - state_size += sizeof(BrotliOnePassArena); - } else { - state_size += sizeof(BrotliTwoPassArena); - } - return hash_size + cmdbuf_size + state_size; - } else { - size_t short_ringbuffer_size = (size_t)1 << params.lgblock; - int ringbuffer_bits = ComputeRbBits(¶ms); - size_t ringbuffer_size = input_size < short_ringbuffer_size ? - input_size : (1u << ringbuffer_bits) + short_ringbuffer_size; - size_t hash_size[4] = {0}; - size_t metablock_size = - BROTLI_MIN(size_t, input_size, MaxMetablockSize(¶ms)); - size_t inputblock_size = - BROTLI_MIN(size_t, input_size, (size_t)1 << params.lgblock); - size_t cmdbuf_size = metablock_size * 2 + inputblock_size * 6; - size_t outbuf_size = metablock_size * 2 + 503; - size_t histogram_size = 0; - HasherSize(¶ms, BROTLI_TRUE, input_size, hash_size); - if (params.quality < MIN_QUALITY_FOR_BLOCK_SPLIT) { - cmdbuf_size = BROTLI_MIN(size_t, cmdbuf_size, - MAX_NUM_DELAYED_SYMBOLS * sizeof(Command) + inputblock_size * 12); - } - if (params.quality >= MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING) { - /* Only a very rough estimation, based on enwik8. */ - histogram_size = 200 << 20; - } else if (params.quality >= MIN_QUALITY_FOR_BLOCK_SPLIT) { - size_t literal_histograms = - BROTLI_MIN(size_t, metablock_size / 6144, 256); - size_t command_histograms = - BROTLI_MIN(size_t, metablock_size / 6144, 256); - size_t distance_histograms = - BROTLI_MIN(size_t, metablock_size / 6144, 256); - histogram_size = literal_histograms * sizeof(HistogramLiteral) + - command_histograms * sizeof(HistogramCommand) + - distance_histograms * sizeof(HistogramDistance); - } - return (memory_manager_size + ringbuffer_size + - hash_size[0] + hash_size[1] + hash_size[2] + hash_size[3] + - cmdbuf_size + - outbuf_size + - histogram_size); - } -} -size_t BrotliEncoderGetPreparedDictionarySize( - const BrotliEncoderPreparedDictionary* prepared_dictionary) { - /* First field of dictionary structs */ - const BrotliEncoderPreparedDictionary* prepared = prepared_dictionary; - uint32_t magic = *((const uint32_t*)prepared); - size_t overhead = 0; - if (magic == kManagedDictionaryMagic) { - const ManagedDictionary* managed = (const ManagedDictionary*)prepared; - overhead = sizeof(ManagedDictionary); - magic = *managed->dictionary; - prepared = (const BrotliEncoderPreparedDictionary*)managed->dictionary; - } - - if (magic == kPreparedDictionaryMagic) { - const PreparedDictionary* dictionary = - (const PreparedDictionary*)prepared; - /* Keep in sync with step 3 of CreatePreparedDictionary */ - return sizeof(PreparedDictionary) + dictionary->source_size + - (sizeof(uint32_t) << dictionary->slot_bits) + - (sizeof(uint16_t) << dictionary->bucket_bits) + - (sizeof(uint32_t) * dictionary->num_items) + overhead; - } else if (magic == kLeanPreparedDictionaryMagic) { - const PreparedDictionary* dictionary = - (const PreparedDictionary*)prepared; - /* Keep in sync with step 3 of CreatePreparedDictionary */ - return sizeof(PreparedDictionary) + sizeof(uint8_t*) + - (sizeof(uint32_t) << dictionary->slot_bits) + - (sizeof(uint16_t) << dictionary->bucket_bits) + - (sizeof(uint32_t) * dictionary->num_items) + overhead; - } else if (magic == kSharedDictionaryMagic) { - const SharedEncoderDictionary* dictionary = - (const SharedEncoderDictionary*)prepared; - const CompoundDictionary* compound = &dictionary->compound; - const ContextualEncoderDictionary* contextual = &dictionary->contextual; - size_t result = sizeof(*dictionary); - size_t i; - size_t num_instances; - const BrotliEncoderDictionary* instances; - for (i = 0; i < compound->num_prepared_instances_; i++) { - size_t size = BrotliEncoderGetPreparedDictionarySize( - (const BrotliEncoderPreparedDictionary*) - compound->prepared_instances_[i]); - if (!size) return 0; /* error */ - result += size; - } - if (contextual->context_based) { - num_instances = contextual->num_instances_; - instances = contextual->instances_; - result += sizeof(*instances) * num_instances; - } else { - num_instances = 1; - instances = &contextual->instance_; - } - for (i = 0; i < num_instances; i++) { - const BrotliEncoderDictionary* dict = &instances[i]; - result += dict->trie.pool_capacity * sizeof(BrotliTrieNode); - if (dict->hash_table_data_words_) { - result += sizeof(kStaticDictionaryHashWords); - } - if (dict->hash_table_data_lengths_) { - result += sizeof(kStaticDictionaryHashLengths); - } - if (dict->buckets_data_) { - result += sizeof(*dict->buckets_data_) * dict->buckets_alloc_size_; - } - if (dict->dict_words_data_) { - result += sizeof(*dict->dict_words) * dict->dict_words_alloc_size_; - } - if (dict->words_instance_) { - result += sizeof(*dict->words_instance_); - /* data_size not added here: it is never allocated by the - SharedEncoderDictionary, instead it always points to the file - already loaded in memory. So if the caller wants to include - this memory as well, add the size of the loaded dictionary - file to this. */ - } - } - return result + overhead; - } - return 0; /* error */ -} - -#if defined(BROTLI_TEST) -size_t MakeUncompressedStreamForTest(const uint8_t*, size_t, uint8_t*); -size_t MakeUncompressedStreamForTest( - const uint8_t* input, size_t input_size, uint8_t* output) { - return MakeUncompressedStream(input, input_size, output); -} -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/encoder_dict.c b/src/deps/brotli/enc/encoder_dict.c deleted file mode 100644 index 6602c55a24e45..0000000000000 --- a/src/deps/brotli/enc/encoder_dict.c +++ /dev/null @@ -1,640 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "encoder_dict.h" - -#include /* malloc, free */ - -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "../common/shared_dictionary_internal.h" -#include "../common/transform.h" -#include "compound_dictionary.h" -#include "dictionary_hash.h" -#include "memory.h" -#include "quality.h" -#include "hash.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define NUM_HASH_BITS 15u -#define NUM_HASH_BUCKETS (1u << NUM_HASH_BITS) - -static void BrotliTrieInit(BrotliTrie* trie) { - trie->pool_capacity = 0; - trie->pool_size = 0; - trie->pool = 0; - - /* Set up the root node */ - trie->root.single = 0; - trie->root.len_ = 0; - trie->root.idx_ = 0; - trie->root.sub = 0; -} - -static void BrotliTrieFree(MemoryManager* m, BrotliTrie* trie) { - BrotliFree(m, trie->pool); -} - -/* Initializes to RFC 7932 static dictionary / transforms. */ -static void InitEncoderDictionary(BrotliEncoderDictionary* dict) { - dict->words = BrotliGetDictionary(); - dict->num_transforms = (uint32_t)BrotliGetTransforms()->num_transforms; - - dict->hash_table_words = kStaticDictionaryHashWords; - dict->hash_table_lengths = kStaticDictionaryHashLengths; - dict->buckets = kStaticDictionaryBuckets; - dict->dict_words = kStaticDictionaryWords; - - dict->cutoffTransformsCount = kCutoffTransformsCount; - dict->cutoffTransforms = kCutoffTransforms; - - dict->parent = 0; - - dict->hash_table_data_words_ = 0; - dict->hash_table_data_lengths_ = 0; - dict->buckets_alloc_size_ = 0; - dict->buckets_data_ = 0; - dict->dict_words_alloc_size_ = 0; - dict->dict_words_data_ = 0; - dict->words_instance_ = 0; - dict->has_words_heavy = BROTLI_FALSE; - BrotliTrieInit(&dict->trie); -} - -static void BrotliDestroyEncoderDictionary(MemoryManager* m, - BrotliEncoderDictionary* dict) { - BrotliFree(m, dict->hash_table_data_words_); - BrotliFree(m, dict->hash_table_data_lengths_); - BrotliFree(m, dict->buckets_data_); - BrotliFree(m, dict->dict_words_data_); - BrotliFree(m, dict->words_instance_); - BrotliTrieFree(m, &dict->trie); -} - -#if defined(BROTLI_EXPERIMENTAL) -/* Word length must be at least 4 bytes */ -static uint32_t Hash(const uint8_t* data, int bits) { - uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return h >> (32 - bits); -} - -/* Theoretical max possible word size after transform */ -#define kTransformedBufferSize \ - (256 + 256 + SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH) - -/* To be safe buffer must have at least kTransformedBufferSize */ -static void TransformedDictionaryWord(uint32_t word_idx, int len, int transform, - const BrotliTransforms* transforms, - const BrotliEncoderDictionary* dict, - uint8_t* buffer, size_t* size) { - const uint8_t* dict_word = &dict->words->data[ - dict->words->offsets_by_length[len] + (uint32_t)len * word_idx]; - *size = (size_t)BrotliTransformDictionaryWord(buffer, dict_word, len, - transforms, transform); -} - -static DictWord MakeDictWord(uint8_t len, uint8_t transform, uint16_t idx) { - DictWord result; - result.len = len; - result.transform = transform; - result.idx = idx; - return result; -} - -static uint32_t BrotliTrieAlloc(MemoryManager* m, size_t num, BrotliTrie* trie, - BrotliTrieNode** keep) { - uint32_t result; - uint32_t keep_index = 0; - if (keep && *keep != &trie->root) { - /* Optional node to keep, since address may change after re-allocating */ - keep_index = (uint32_t)(*keep - trie->pool); - } - if (trie->pool_size == 0) { - /* Have a dummy node in the front. We do not want the result to be 0, it - must be at least 1, 0 represents "null pointer" */ - trie->pool_size = 1; - } - BROTLI_ENSURE_CAPACITY(m, BrotliTrieNode, trie->pool, trie->pool_capacity, - trie->pool_size + num); - if (BROTLI_IS_OOM(m)) return 0; - /* Init the new nodes to empty */ - memset(trie->pool + trie->pool_size, 0, sizeof(*trie->pool) * num); - result = (uint32_t)trie->pool_size; - trie->pool_size += num; - if (keep && *keep != &trie->root) { - *keep = trie->pool + keep_index; - } - return result; -} - -/** - * len and idx: payload for last node - * word, size: the string - * index: position in the string - */ -static BROTLI_BOOL BrotliTrieNodeAdd(MemoryManager* m, uint8_t len, - uint32_t idx, const uint8_t* word, size_t size, int index, - BrotliTrieNode* node, BrotliTrie* trie) { - BrotliTrieNode* child = 0; - uint8_t c; - if ((size_t)index == size) { - if (!node->len_ || idx < node->idx_) { - node->len_ = len; - node->idx_ = idx; - } - return BROTLI_TRUE; - } - c = word[index]; - if (node->single && c != node->c) { - BrotliTrieNode old = trie->pool[node->sub]; - uint32_t new_nodes = BrotliTrieAlloc(m, 32, trie, &node); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - node->single = 0; - node->sub = new_nodes; - trie->pool[node->sub + (node->c >> 4)].sub = new_nodes + 16; - trie->pool[trie->pool[node->sub + (node->c >> 4)].sub + (node->c & 15)] = - old; - } - if (!node->sub) { - uint32_t new_node = BrotliTrieAlloc(m, 1, trie, &node); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - node->single = 1; - node->c = c; - node->sub = new_node; - } - if (node->single) { - child = &trie->pool[node->sub]; - } else { - if (!trie->pool[node->sub + (c >> 4)].sub) { - uint32_t new_nodes = BrotliTrieAlloc(m, 16, trie, &node); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - trie->pool[node->sub + (c >> 4)].sub = new_nodes; - } - child = &trie->pool[trie->pool[node->sub + (c >> 4)].sub + (c & 15)]; - } - return BrotliTrieNodeAdd(m, len, idx, word, size, index + 1, child, trie); -} - -static BROTLI_BOOL BrotliTrieAdd(MemoryManager* m, uint8_t len, uint32_t idx, - const uint8_t* word, size_t size, BrotliTrie* trie) { - return BrotliTrieNodeAdd(m, len, idx, word, size, 0, &trie->root, trie); -} - -const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie, - const BrotliTrieNode* node, uint8_t c) { - BrotliTrieNode* temp_node; - if (node->single) { - if (node->c == c) return &trie->pool[node->sub]; - return 0; - } - if (!node->sub) return 0; - temp_node = &trie->pool[node->sub + (c >> 4)]; - if (!temp_node->sub) return 0; - return &trie->pool[temp_node->sub + (c & 15)]; -} - -static const BrotliTrieNode* BrotliTrieFind(const BrotliTrie* trie, - const uint8_t* word, size_t size) { - const BrotliTrieNode* node = &trie->root; - size_t i; - for (i = 0; i < size; i++) { - node = BrotliTrieSub(trie, node, word[i]); - if (!node) return 0; - } - return node; -} - -static BROTLI_BOOL BuildDictionaryLut(MemoryManager* m, - const BrotliTransforms* transforms, - BrotliEncoderDictionary* dict) { - uint32_t i; - DictWord* dict_words; - uint16_t* buckets; - DictWord** words_by_hash; - size_t* words_by_hash_size; - size_t* words_by_hash_capacity; - BrotliTrie dedup; - uint8_t word[kTransformedBufferSize]; - size_t word_size; - size_t total = 0; - uint8_t l; - uint16_t idx; - - BrotliTrieInit(&dedup); - - words_by_hash = (DictWord**)BrotliAllocate(m, - sizeof(*words_by_hash) * NUM_HASH_BUCKETS); - words_by_hash_size = (size_t*)BrotliAllocate(m, - sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS); - words_by_hash_capacity = (size_t*)BrotliAllocate(m, - sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - memset(words_by_hash, 0, sizeof(*words_by_hash) * NUM_HASH_BUCKETS); - memset(words_by_hash_size, 0, sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS); - memset(words_by_hash_capacity, 0, - sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS); - - if (transforms->num_transforms > 0) { - for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; - l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) { - uint16_t n = dict->words->size_bits_by_length[l] ? - (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u; - for (idx = 0; idx < n; ++idx) { - uint32_t key; - /* First transform (usually identity) */ - TransformedDictionaryWord(idx, l, 0, transforms, dict, word, - &word_size); - /* Cannot hash words smaller than 4 bytes */ - if (word_size < 4) { - /* Break instead of continue, all next words of this length will have - same length after transform */ - break; - } - if (!BrotliTrieAdd(m, 0, idx, word, word_size, &dedup)) { - return BROTLI_FALSE; - } - key = Hash(word, NUM_HASH_BITS); - BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key], - words_by_hash_capacity[key], words_by_hash_size[key], - MakeDictWord(l, 0, idx)); - ++total; - } - } - } - - /* These LUT transforms only supported if no custom transforms. This is - ok, we will use the heavy trie instead. */ - if (transforms == BrotliGetTransforms()) { - for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; - l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) { - uint16_t n = dict->words->size_bits_by_length[l] ? - (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u; - for (idx = 0; idx < n; ++idx) { - int k; - BROTLI_BOOL is_ascii = BROTLI_TRUE; - size_t offset = dict->words->offsets_by_length[l] + (size_t)l * idx; - const uint8_t* data = &dict->words->data[offset]; - for (k = 0; k < l; ++k) { - if (data[k] >= 128) is_ascii = BROTLI_FALSE; - } - if (data[0] < 128) { - int transform = 9; /* {empty, uppercase first, empty} */ - uint32_t ix = idx + (uint32_t)transform * n; - const BrotliTrieNode* it; - TransformedDictionaryWord(idx, l, transform, transforms, - dict, word, &word_size); - it = BrotliTrieFind(&dedup, word, word_size); - if (!it || it->idx_ > ix) { - uint32_t key = Hash(word, NUM_HASH_BITS); - if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) { - return BROTLI_FALSE; - } - BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key], - words_by_hash_capacity[key], words_by_hash_size[key], - MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_FIRST, idx)); - ++total; - } - } - if (is_ascii) { - int transform = 44; /* {empty, uppercase all, empty} */ - uint32_t ix = idx + (uint32_t)transform * n; - const BrotliTrieNode* it; - TransformedDictionaryWord(idx, l, transform, transforms, - dict, word, &word_size); - it = BrotliTrieFind(&dedup, word, word_size); - if (!it || it->idx_ > ix) { - uint32_t key = Hash(word, NUM_HASH_BITS); - if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) { - return BROTLI_FALSE; - } - BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key], - words_by_hash_capacity[key], words_by_hash_size[key], - MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_ALL, idx)); - ++total; - } - } - } - } - } - - dict_words = (DictWord*)BrotliAllocate(m, - sizeof(*dict->dict_words) * (total + 1)); - buckets = (uint16_t*)BrotliAllocate(m, - sizeof(*dict->buckets) * NUM_HASH_BUCKETS); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - dict->dict_words_alloc_size_ = total + 1; - dict->dict_words = dict->dict_words_data_ = dict_words; - dict->buckets_alloc_size_ = NUM_HASH_BUCKETS; - dict->buckets = dict->buckets_data_ = buckets; - - /* Unused; makes offsets start from 1. */ - dict_words[0] = MakeDictWord(0, 0, 0); - total = 1; - for (i = 0; i < NUM_HASH_BUCKETS; ++i) { - size_t num_words = words_by_hash_size[i]; - if (num_words > 0) { - buckets[i] = (uint16_t)(total); - memcpy(&dict_words[total], &words_by_hash[i][0], - sizeof(dict_words[0]) * num_words); - total += num_words; - dict_words[total - 1].len |= 0x80; - } else { - buckets[i] = 0; - } - } - - for (i = 0; i < NUM_HASH_BUCKETS; ++i) { - BrotliFree(m, words_by_hash[i]); - } - BrotliFree(m, words_by_hash); - BrotliFree(m, words_by_hash_size); - BrotliFree(m, words_by_hash_capacity); - BrotliTrieFree(m, &dedup); - - return BROTLI_TRUE; -} - -static void BuildDictionaryHashTable(uint16_t* hash_table_words, - uint8_t* hash_table_lengths, const BrotliDictionary* dict) { - int j, len; - /* The order of the loops is such that in case of collision, words with - shorter length are preferred, and in case of same length, words with - smaller index. There is only a single word per bucket. */ - /* TODO(lode): consider adding optional user-supplied frequency_map to use - for preferred words instead, this can make the encoder better for - quality 9 and below without affecting the decoder */ - memset(hash_table_words, 0, sizeof(kStaticDictionaryHashWords)); - memset(hash_table_lengths, 0, sizeof(kStaticDictionaryHashLengths)); - for (len = SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; - len >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; --len) { - const size_t num_words = dict->size_bits_by_length[len] ? - (1u << dict->size_bits_by_length[len]) : 0; - for (j = (int)num_words - 1; j >= 0; --j) { - size_t offset = dict->offsets_by_length[len] + - (size_t)len * (size_t)j; - const uint8_t* word = &dict->data[offset]; - const uint32_t key = Hash(word, 14); - int idx = (int)(key << 1) + (len < 8 ? 1 : 0); - BROTLI_DCHECK(idx < (int)NUM_HASH_BUCKETS); - hash_table_words[idx] = (uint16_t)j; - hash_table_lengths[idx] = (uint8_t)len; - } - } -} - -static BROTLI_BOOL GenerateWordsHeavy(MemoryManager* m, - const BrotliTransforms* transforms, - BrotliEncoderDictionary* dict) { - int i, j, l; - for (j = (int)transforms->num_transforms - 1; j >= 0 ; --j) { - for (l = 0; l < 32; l++) { - int num = (int)((1u << dict->words->size_bits_by_length[l]) & ~1u); - for (i = 0; i < num; i++) { - uint8_t transformed[kTransformedBufferSize]; - size_t size; - TransformedDictionaryWord( - (uint32_t)i, l, j, transforms, dict, transformed, &size); - if (size < 4) continue; - if (!BrotliTrieAdd(m, (uint8_t)l, (uint32_t)(i + num * j), - transformed, size, &dict->trie)) { - return BROTLI_FALSE; - } - } - } - } - return BROTLI_TRUE; -} - -/* Computes cutoffTransformsCount (in count) and cutoffTransforms (in data) for - the custom transforms, where possible within the limits of the - cutoffTransforms encoding. The fast encoder uses this to do fast lookup for - transforms that remove the N last characters (OmitLast). */ -static void ComputeCutoffTransforms( - const BrotliTransforms* transforms, - uint32_t* count, uint64_t* data) { - int i; - /* The encoding in a 64-bit integer of transform N in the data is: (N << 2) + - ((cutoffTransforms >> (N * 6)) & 0x3F), so for example the identity - transform code must be 0-63, for N=1 the transform code must be 4-67, ..., - for N=9 it must be 36-99. - TODO(lode): consider a simple flexible uint8_t[10] instead of the uint64_t - for the cutoff transforms, so that shared dictionaries can have the - OmitLast transforms anywhere without loss. */ - *count = 0; - *data = 0; - for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) { - int idx = transforms->cutOffTransforms[i]; - if (idx == -1) break; /* Not found */ - if (idx < (i << 2)) break; /* Too small for the encoding */ - if (idx >= (i << 2) + 64) break; /* Too large for the encoding */ - (*count)++; - *data |= (uint64_t)(((uint64_t)idx - - ((uint64_t)i << 2u)) << ((uint64_t)i * 6u)); - } -} - -static BROTLI_BOOL ComputeDictionary(MemoryManager* m, int quality, - const BrotliTransforms* transforms, - BrotliEncoderDictionary* current) { - int default_words = current->words == BrotliGetDictionary(); - int default_transforms = transforms == BrotliGetTransforms(); - - if (default_words && default_transforms) { - /* hashes are already set to Brotli defaults */ - return BROTLI_TRUE; - } - - current->hash_table_data_words_ = (uint16_t*)BrotliAllocate( - m, sizeof(kStaticDictionaryHashWords)); - current->hash_table_data_lengths_ = (uint8_t*)BrotliAllocate( - m, sizeof(kStaticDictionaryHashLengths)); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - current->hash_table_words = current->hash_table_data_words_; - current->hash_table_lengths = current->hash_table_data_lengths_; - - BuildDictionaryHashTable(current->hash_table_data_words_, - current->hash_table_data_lengths_, current->words); - - ComputeCutoffTransforms(transforms, - ¤t->cutoffTransformsCount, ¤t->cutoffTransforms); - - /* Only compute the data for slow encoder if the requested quality is high - enough to need it */ - if (quality >= ZOPFLIFICATION_QUALITY) { - if (!BuildDictionaryLut(m, transforms, current)) return BROTLI_FALSE; - - /* For the built-in Brotli transforms, there is a hard-coded function to - handle all transforms, but for custom transforms, we use the following - large hammer instead */ - current->has_words_heavy = !default_transforms; - if (current->has_words_heavy) { - if (!GenerateWordsHeavy(m, transforms, current)) return BROTLI_FALSE; - } - } - - return BROTLI_TRUE; -} -#endif /* BROTLI_EXPERIMENTAL */ - -void BrotliInitSharedEncoderDictionary(SharedEncoderDictionary* dict) { - dict->magic = kSharedDictionaryMagic; - - dict->compound.num_chunks = 0; - dict->compound.total_size = 0; - dict->compound.chunk_offsets[0] = 0; - dict->compound.num_prepared_instances_ = 0; - - dict->contextual.context_based = 0; - dict->contextual.num_dictionaries = 1; - dict->contextual.instances_ = 0; - dict->contextual.num_instances_ = 1; /* The instance_ field */ - dict->contextual.dict[0] = &dict->contextual.instance_; - InitEncoderDictionary(&dict->contextual.instance_); - dict->contextual.instance_.parent = &dict->contextual; - - dict->max_quality = BROTLI_MAX_QUALITY; -} - -#if defined(BROTLI_EXPERIMENTAL) -/* TODO(eustas): make sure that tooling will warn user if not all the cutoff - transforms are available (for low-quality encoder). */ -static BROTLI_BOOL InitCustomSharedEncoderDictionary( - MemoryManager* m, const BrotliSharedDictionary* decoded_dict, - int quality, SharedEncoderDictionary* dict) { - ContextualEncoderDictionary* contextual; - CompoundDictionary* compound; - BrotliEncoderDictionary* instances; - int i; - BrotliInitSharedEncoderDictionary(dict); - - contextual = &dict->contextual; - compound = &dict->compound; - - for (i = 0; i < (int)decoded_dict->num_prefix; i++) { - PreparedDictionary* prepared = CreatePreparedDictionary(m, - decoded_dict->prefix[i], decoded_dict->prefix_size[i]); - AttachPreparedDictionary(compound, prepared); - /* remember for cleanup */ - compound->prepared_instances_[ - compound->num_prepared_instances_++] = prepared; - } - - dict->max_quality = quality; - contextual->context_based = decoded_dict->context_based; - if (decoded_dict->context_based) { - memcpy(contextual->context_map, decoded_dict->context_map, - SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS); - } - - contextual->num_dictionaries = decoded_dict->num_dictionaries; - contextual->num_instances_ = decoded_dict->num_dictionaries; - if (contextual->num_instances_ == 1) { - instances = &contextual->instance_; - } else { - contextual->instances_ = (BrotliEncoderDictionary*) - BrotliAllocate(m, sizeof(*contextual->instances_) * - contextual->num_instances_); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - instances = contextual->instances_; - } - for (i = 0; i < (int)contextual->num_instances_; i++) { - BrotliEncoderDictionary* current = &instances[i]; - InitEncoderDictionary(current); - current->parent = &dict->contextual; - if (decoded_dict->words[i] == BrotliGetDictionary()) { - current->words = BrotliGetDictionary(); - } else { - current->words_instance_ = (BrotliDictionary*)BrotliAllocate( - m, sizeof(BrotliDictionary)); - if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; - *current->words_instance_ = *decoded_dict->words[i]; - current->words = current->words_instance_; - } - current->num_transforms = - (uint32_t)decoded_dict->transforms[i]->num_transforms; - if (!ComputeDictionary( - m, quality, decoded_dict->transforms[i], current)) { - return BROTLI_FALSE; - } - - contextual->dict[i] = current; - } - - return BROTLI_TRUE; /* success */ -} - -BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary( - MemoryManager* m, const uint8_t* encoded_dict, size_t size, - int quality, SharedEncoderDictionary* dict) { - BROTLI_BOOL success = BROTLI_FALSE; - BrotliSharedDictionary* decoded_dict = BrotliSharedDictionaryCreateInstance( - m->alloc_func, m->free_func, m->opaque); - if (!decoded_dict) { /* OOM */ - return BROTLI_FALSE; - } - success = BrotliSharedDictionaryAttach( - decoded_dict, BROTLI_SHARED_DICTIONARY_SERIALIZED, size, encoded_dict); - if (success) { - success = InitCustomSharedEncoderDictionary(m, - decoded_dict, quality, dict); - } - BrotliSharedDictionaryDestroyInstance(decoded_dict); - return success; -} -#endif /* BROTLI_EXPERIMENTAL */ - -void BrotliCleanupSharedEncoderDictionary(MemoryManager* m, - SharedEncoderDictionary* dict) { - size_t i; - for (i = 0; i < dict->compound.num_prepared_instances_; i++) { - DestroyPreparedDictionary(m, - (PreparedDictionary*)dict->compound.prepared_instances_[i]); - } - if (dict->contextual.num_instances_ == 1) { - BrotliDestroyEncoderDictionary(m, &dict->contextual.instance_); - } else if (dict->contextual.num_instances_ > 1) { - for (i = 0; i < dict->contextual.num_instances_; i++) { - BrotliDestroyEncoderDictionary(m, &dict->contextual.instances_[i]); - } - BrotliFree(m, dict->contextual.instances_); - } -} - -ManagedDictionary* BrotliCreateManagedDictionary( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - ManagedDictionary* result = (ManagedDictionary*)BrotliBootstrapAlloc( - sizeof(ManagedDictionary), alloc_func, free_func, opaque); - if (result == NULL) return NULL; - - result->magic = kManagedDictionaryMagic; - BrotliInitMemoryManager( - &result->memory_manager_, alloc_func, free_func, opaque); - result->dictionary = NULL; - - return result; -} - -void BrotliDestroyManagedDictionary(ManagedDictionary* dictionary) { - if (!dictionary) return; - BrotliBootstrapFree(dictionary, &dictionary->memory_manager_); -} - -/* Escalate internal functions visibility; for testing purposes only. */ -#if defined(BROTLI_TEST) -void InitEncoderDictionaryForTest(BrotliEncoderDictionary*); -void InitEncoderDictionaryForTest(BrotliEncoderDictionary* d) { - InitEncoderDictionary(d); -} -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/encoder_dict.h b/src/deps/brotli/enc/encoder_dict.h deleted file mode 100644 index 27dcbcd2f3336..0000000000000 --- a/src/deps/brotli/enc/encoder_dict.h +++ /dev/null @@ -1,157 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#ifndef BROTLI_ENC_ENCODER_DICT_H_ -#define BROTLI_ENC_ENCODER_DICT_H_ - -#include -#include - -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "compound_dictionary.h" -#include "memory.h" -#include "static_dict_lut.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* -Dictionary hierarchy for Encoder: --SharedEncoderDictionary ---CompoundDictionary ----PreparedDictionary [up to 15x] - = prefix dictionary with precomputed hashes ---ContextualEncoderDictionary ----BrotliEncoderDictionary [up to 64x] - = for each context, precomputed static dictionary with words + transforms - -Dictionary hiearchy from common: similar, but without precomputed hashes --BrotliSharedDictionary ---BrotliDictionary [up to 64x] ---BrotliTransforms [up to 64x] ---const uint8_t* prefix [up to 15x]: compound dictionaries -*/ - -typedef struct BrotliTrieNode { - uint8_t single; /* if 1, sub is a single node for c instead of 256 */ - uint8_t c; - uint8_t len_; /* untransformed length */ - uint32_t idx_; /* word index + num words * transform index */ - uint32_t sub; /* index of sub node(s) in the pool */ -} BrotliTrieNode; - -typedef struct BrotliTrie { - BrotliTrieNode* pool; - size_t pool_capacity; - size_t pool_size; - BrotliTrieNode root; -} BrotliTrie; - -#if defined(BROTLI_EXPERIMENTAL) -BROTLI_INTERNAL const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie, - const BrotliTrieNode* node, uint8_t c); -#endif /* BROTLI_EXPERIMENTAL */ - -/* Dictionary data (words and transforms) for 1 possible context */ -typedef struct BrotliEncoderDictionary { - const BrotliDictionary* words; - uint32_t num_transforms; - - /* cut off for fast encoder */ - uint32_t cutoffTransformsCount; - uint64_t cutoffTransforms; - - /* from dictionary_hash.h, for fast encoder */ - const uint16_t* hash_table_words; - const uint8_t* hash_table_lengths; - - /* from static_dict_lut.h, for slow encoder */ - const uint16_t* buckets; - const DictWord* dict_words; - /* Heavy version, for use by slow encoder when there are custom transforms. - Contains every possible transformed dictionary word in a trie. It encodes - about as fast as the non-heavy encoder but consumes a lot of memory and - takes time to build. */ - BrotliTrie trie; - BROTLI_BOOL has_words_heavy; - - /* Reference to other dictionaries. */ - const struct ContextualEncoderDictionary* parent; - - /* Allocated memory, used only when not using the Brotli defaults */ - uint16_t* hash_table_data_words_; - uint8_t* hash_table_data_lengths_; - size_t buckets_alloc_size_; - uint16_t* buckets_data_; - size_t dict_words_alloc_size_; - DictWord* dict_words_data_; - BrotliDictionary* words_instance_; -} BrotliEncoderDictionary; - -/* Dictionary data for all 64 contexts */ -typedef struct ContextualEncoderDictionary { - BROTLI_BOOL context_based; - uint8_t num_dictionaries; - uint8_t context_map[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; - const BrotliEncoderDictionary* dict[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; - - /* If num_instances_ is 1, instance_ is used, else dynamic allocation with - instances_ is used. */ - size_t num_instances_; - BrotliEncoderDictionary instance_; - BrotliEncoderDictionary* instances_; -} ContextualEncoderDictionary; - -typedef struct SharedEncoderDictionary { - /* Magic value to distinguish this struct from PreparedDictionary for - certain external usages. */ - uint32_t magic; - - /* LZ77 prefix, compound dictionary */ - CompoundDictionary compound; - - /* Custom static dictionary (optionally context-based) */ - ContextualEncoderDictionary contextual; - - /* The maximum quality the dictionary was computed for */ - int max_quality; -} SharedEncoderDictionary; - -typedef struct ManagedDictionary { - uint32_t magic; - MemoryManager memory_manager_; - uint32_t* dictionary; -} ManagedDictionary; - -/* Initializes to the brotli built-in dictionary */ -BROTLI_INTERNAL void BrotliInitSharedEncoderDictionary( - SharedEncoderDictionary* dict); - -#if defined(BROTLI_EXPERIMENTAL) -/* Initializes to shared dictionary that will be parsed from - encoded_dict. Requires that you keep the encoded_dict buffer - around, parts of data will point to it. */ -BROTLI_INTERNAL BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary( - MemoryManager* m, const uint8_t* encoded_dict, size_t size, - int quality, SharedEncoderDictionary* dict); -#endif /* BROTLI_EXPERIMENTAL */ - -BROTLI_INTERNAL void BrotliCleanupSharedEncoderDictionary( - MemoryManager* m, SharedEncoderDictionary* dict); - -BROTLI_INTERNAL ManagedDictionary* BrotliCreateManagedDictionary( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); - -BROTLI_INTERNAL void BrotliDestroyManagedDictionary( - ManagedDictionary* dictionary); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_ENCODER_DICT_H_ */ diff --git a/src/deps/brotli/enc/entropy_encode.c b/src/deps/brotli/enc/entropy_encode.c deleted file mode 100644 index 9aed43b6eb31f..0000000000000 --- a/src/deps/brotli/enc/entropy_encode.c +++ /dev/null @@ -1,504 +0,0 @@ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Entropy encoding (Huffman) utilities. */ - -#include "entropy_encode.h" - -#include /* memset */ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -const size_t kBrotliShellGaps[] = {132, 57, 23, 10, 4, 1}; - -BROTLI_BOOL BrotliSetDepth( - int p0, HuffmanTree* pool, uint8_t* depth, int max_depth) { - int stack[16]; - int level = 0; - int p = p0; - BROTLI_DCHECK(max_depth <= 15); - stack[0] = -1; - while (BROTLI_TRUE) { - if (pool[p].index_left_ >= 0) { - level++; - if (level > max_depth) return BROTLI_FALSE; - stack[level] = pool[p].index_right_or_value_; - p = pool[p].index_left_; - continue; - } else { - depth[pool[p].index_right_or_value_] = (uint8_t)level; - } - while (level >= 0 && stack[level] == -1) level--; - if (level < 0) return BROTLI_TRUE; - p = stack[level]; - stack[level] = -1; - } -} - -/* Sort the root nodes, least popular first. */ -static BROTLI_INLINE BROTLI_BOOL SortHuffmanTree( - const HuffmanTree* v0, const HuffmanTree* v1) { - if (v0->total_count_ != v1->total_count_) { - return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_); - } - return TO_BROTLI_BOOL(v0->index_right_or_value_ > v1->index_right_or_value_); -} - -/* This function will create a Huffman tree. - - The catch here is that the tree cannot be arbitrarily deep. - Brotli specifies a maximum depth of 15 bits for "code trees" - and 7 bits for "code length code trees." - - count_limit is the value that is to be faked as the minimum value - and this minimum value is raised until the tree matches the - maximum length requirement. - - This algorithm is not of excellent performance for very long data blocks, - especially when population counts are longer than 2**tree_limit, but - we are not planning to use this with extremely long blocks. - - See http://en.wikipedia.org/wiki/Huffman_coding */ -void BrotliCreateHuffmanTree(const uint32_t* data, - const size_t length, - const int tree_limit, - HuffmanTree* tree, - uint8_t* depth) { - uint32_t count_limit; - HuffmanTree sentinel; - InitHuffmanTree(&sentinel, BROTLI_UINT32_MAX, -1, -1); - /* For block sizes below 64 kB, we never need to do a second iteration - of this loop. Probably all of our block sizes will be smaller than - that, so this loop is mostly of academic interest. If we actually - would need this, we would be better off with the Katajainen algorithm. */ - for (count_limit = 1; ; count_limit *= 2) { - size_t n = 0; - size_t i; - size_t j; - size_t k; - for (i = length; i != 0;) { - --i; - if (data[i]) { - const uint32_t count = BROTLI_MAX(uint32_t, data[i], count_limit); - InitHuffmanTree(&tree[n++], count, -1, (int16_t)i); - } - } - - if (n == 1) { - depth[tree[0].index_right_or_value_] = 1; /* Only one element. */ - break; - } - - SortHuffmanTreeItems(tree, n, SortHuffmanTree); - - /* The nodes are: - [0, n): the sorted leaf nodes that we start with. - [n]: we add a sentinel here. - [n + 1, 2n): new parent nodes are added here, starting from - (n+1). These are naturally in ascending order. - [2n]: we add a sentinel at the end as well. - There will be (2n+1) elements at the end. */ - tree[n] = sentinel; - tree[n + 1] = sentinel; - - i = 0; /* Points to the next leaf node. */ - j = n + 1; /* Points to the next non-leaf node. */ - for (k = n - 1; k != 0; --k) { - size_t left, right; - if (tree[i].total_count_ <= tree[j].total_count_) { - left = i; - ++i; - } else { - left = j; - ++j; - } - if (tree[i].total_count_ <= tree[j].total_count_) { - right = i; - ++i; - } else { - right = j; - ++j; - } - - { - /* The sentinel node becomes the parent node. */ - size_t j_end = 2 * n - k; - tree[j_end].total_count_ = - tree[left].total_count_ + tree[right].total_count_; - tree[j_end].index_left_ = (int16_t)left; - tree[j_end].index_right_or_value_ = (int16_t)right; - - /* Add back the last sentinel node. */ - tree[j_end + 1] = sentinel; - } - } - if (BrotliSetDepth((int)(2 * n - 1), &tree[0], depth, tree_limit)) { - /* We need to pack the Huffman tree in tree_limit bits. If this was not - successful, add fake entities to the lowest values and retry. */ - break; - } - } -} - -static void Reverse(uint8_t* v, size_t start, size_t end) { - --end; - while (start < end) { - uint8_t tmp = v[start]; - v[start] = v[end]; - v[end] = tmp; - ++start; - --end; - } -} - -static void BrotliWriteHuffmanTreeRepetitions( - const uint8_t previous_value, - const uint8_t value, - size_t repetitions, - size_t* tree_size, - uint8_t* tree, - uint8_t* extra_bits_data) { - BROTLI_DCHECK(repetitions > 0); - if (previous_value != value) { - tree[*tree_size] = value; - extra_bits_data[*tree_size] = 0; - ++(*tree_size); - --repetitions; - } - if (repetitions == 7) { - tree[*tree_size] = value; - extra_bits_data[*tree_size] = 0; - ++(*tree_size); - --repetitions; - } - if (repetitions < 3) { - size_t i; - for (i = 0; i < repetitions; ++i) { - tree[*tree_size] = value; - extra_bits_data[*tree_size] = 0; - ++(*tree_size); - } - } else { - size_t start = *tree_size; - repetitions -= 3; - while (BROTLI_TRUE) { - tree[*tree_size] = BROTLI_REPEAT_PREVIOUS_CODE_LENGTH; - extra_bits_data[*tree_size] = repetitions & 0x3; - ++(*tree_size); - repetitions >>= 2; - if (repetitions == 0) { - break; - } - --repetitions; - } - Reverse(tree, start, *tree_size); - Reverse(extra_bits_data, start, *tree_size); - } -} - -static void BrotliWriteHuffmanTreeRepetitionsZeros( - size_t repetitions, - size_t* tree_size, - uint8_t* tree, - uint8_t* extra_bits_data) { - if (repetitions == 11) { - tree[*tree_size] = 0; - extra_bits_data[*tree_size] = 0; - ++(*tree_size); - --repetitions; - } - if (repetitions < 3) { - size_t i; - for (i = 0; i < repetitions; ++i) { - tree[*tree_size] = 0; - extra_bits_data[*tree_size] = 0; - ++(*tree_size); - } - } else { - size_t start = *tree_size; - repetitions -= 3; - while (BROTLI_TRUE) { - tree[*tree_size] = BROTLI_REPEAT_ZERO_CODE_LENGTH; - extra_bits_data[*tree_size] = repetitions & 0x7; - ++(*tree_size); - repetitions >>= 3; - if (repetitions == 0) { - break; - } - --repetitions; - } - Reverse(tree, start, *tree_size); - Reverse(extra_bits_data, start, *tree_size); - } -} - -void BrotliOptimizeHuffmanCountsForRle(size_t length, uint32_t* counts, - uint8_t* good_for_rle) { - size_t nonzero_count = 0; - size_t stride; - size_t limit; - size_t sum; - const size_t streak_limit = 1240; - /* Let's make the Huffman code more compatible with RLE encoding. */ - size_t i; - for (i = 0; i < length; i++) { - if (counts[i]) { - ++nonzero_count; - } - } - if (nonzero_count < 16) { - return; - } - while (length != 0 && counts[length - 1] == 0) { - --length; - } - if (length == 0) { - return; /* All zeros. */ - } - /* Now counts[0..length - 1] does not have trailing zeros. */ - { - size_t nonzeros = 0; - uint32_t smallest_nonzero = 1 << 30; - for (i = 0; i < length; ++i) { - if (counts[i] != 0) { - ++nonzeros; - if (smallest_nonzero > counts[i]) { - smallest_nonzero = counts[i]; - } - } - } - if (nonzeros < 5) { - /* Small histogram will model it well. */ - return; - } - if (smallest_nonzero < 4) { - size_t zeros = length - nonzeros; - if (zeros < 6) { - for (i = 1; i < length - 1; ++i) { - if (counts[i - 1] != 0 && counts[i] == 0 && counts[i + 1] != 0) { - counts[i] = 1; - } - } - } - } - if (nonzeros < 28) { - return; - } - } - /* 2) Let's mark all population counts that already can be encoded - with an RLE code. */ - memset(good_for_rle, 0, length); - { - /* Let's not spoil any of the existing good RLE codes. - Mark any seq of 0's that is longer as 5 as a good_for_rle. - Mark any seq of non-0's that is longer as 7 as a good_for_rle. */ - uint32_t symbol = counts[0]; - size_t step = 0; - for (i = 0; i <= length; ++i) { - if (i == length || counts[i] != symbol) { - if ((symbol == 0 && step >= 5) || - (symbol != 0 && step >= 7)) { - size_t k; - for (k = 0; k < step; ++k) { - good_for_rle[i - k - 1] = 1; - } - } - step = 1; - if (i != length) { - symbol = counts[i]; - } - } else { - ++step; - } - } - } - /* 3) Let's replace those population counts that lead to more RLE codes. - Math here is in 24.8 fixed point representation. */ - stride = 0; - limit = 256 * (counts[0] + counts[1] + counts[2]) / 3 + 420; - sum = 0; - for (i = 0; i <= length; ++i) { - if (i == length || good_for_rle[i] || - (i != 0 && good_for_rle[i - 1]) || - (256 * counts[i] - limit + streak_limit) >= 2 * streak_limit) { - if (stride >= 4 || (stride >= 3 && sum == 0)) { - size_t k; - /* The stride must end, collapse what we have, if we have enough (4). */ - size_t count = (sum + stride / 2) / stride; - if (count == 0) { - count = 1; - } - if (sum == 0) { - /* Don't make an all zeros stride to be upgraded to ones. */ - count = 0; - } - for (k = 0; k < stride; ++k) { - /* We don't want to change value at counts[i], - that is already belonging to the next stride. Thus - 1. */ - counts[i - k - 1] = (uint32_t)count; - } - } - stride = 0; - sum = 0; - if (i < length - 2) { - /* All interesting strides have a count of at least 4, */ - /* at least when non-zeros. */ - limit = 256 * (counts[i] + counts[i + 1] + counts[i + 2]) / 3 + 420; - } else if (i < length) { - limit = 256 * counts[i]; - } else { - limit = 0; - } - } - ++stride; - if (i != length) { - sum += counts[i]; - if (stride >= 4) { - limit = (256 * sum + stride / 2) / stride; - } - if (stride == 4) { - limit += 120; - } - } - } -} - -static void DecideOverRleUse(const uint8_t* depth, const size_t length, - BROTLI_BOOL* use_rle_for_non_zero, - BROTLI_BOOL* use_rle_for_zero) { - size_t total_reps_zero = 0; - size_t total_reps_non_zero = 0; - size_t count_reps_zero = 1; - size_t count_reps_non_zero = 1; - size_t i; - for (i = 0; i < length;) { - const uint8_t value = depth[i]; - size_t reps = 1; - size_t k; - for (k = i + 1; k < length && depth[k] == value; ++k) { - ++reps; - } - if (reps >= 3 && value == 0) { - total_reps_zero += reps; - ++count_reps_zero; - } - if (reps >= 4 && value != 0) { - total_reps_non_zero += reps; - ++count_reps_non_zero; - } - i += reps; - } - *use_rle_for_non_zero = - TO_BROTLI_BOOL(total_reps_non_zero > count_reps_non_zero * 2); - *use_rle_for_zero = TO_BROTLI_BOOL(total_reps_zero > count_reps_zero * 2); -} - -void BrotliWriteHuffmanTree(const uint8_t* depth, - size_t length, - size_t* tree_size, - uint8_t* tree, - uint8_t* extra_bits_data) { - uint8_t previous_value = BROTLI_INITIAL_REPEATED_CODE_LENGTH; - size_t i; - BROTLI_BOOL use_rle_for_non_zero = BROTLI_FALSE; - BROTLI_BOOL use_rle_for_zero = BROTLI_FALSE; - - /* Throw away trailing zeros. */ - size_t new_length = length; - for (i = 0; i < length; ++i) { - if (depth[length - i - 1] == 0) { - --new_length; - } else { - break; - } - } - - /* First gather statistics on if it is a good idea to do RLE. */ - if (length > 50) { - /* Find RLE coding for longer codes. - Shorter codes seem not to benefit from RLE. */ - DecideOverRleUse(depth, new_length, - &use_rle_for_non_zero, &use_rle_for_zero); - } - - /* Actual RLE coding. */ - for (i = 0; i < new_length;) { - const uint8_t value = depth[i]; - size_t reps = 1; - if ((value != 0 && use_rle_for_non_zero) || - (value == 0 && use_rle_for_zero)) { - size_t k; - for (k = i + 1; k < new_length && depth[k] == value; ++k) { - ++reps; - } - } - if (value == 0) { - BrotliWriteHuffmanTreeRepetitionsZeros( - reps, tree_size, tree, extra_bits_data); - } else { - BrotliWriteHuffmanTreeRepetitions(previous_value, - value, reps, tree_size, - tree, extra_bits_data); - previous_value = value; - } - i += reps; - } -} - -static uint16_t BrotliReverseBits(size_t num_bits, uint16_t bits) { - static const size_t kLut[16] = { /* Pre-reversed 4-bit values. */ - 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E, - 0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F - }; - size_t retval = kLut[bits & 0x0F]; - size_t i; - for (i = 4; i < num_bits; i += 4) { - retval <<= 4; - bits = (uint16_t)(bits >> 4); - retval |= kLut[bits & 0x0F]; - } - retval >>= ((0 - num_bits) & 0x03); - return (uint16_t)retval; -} - -/* 0..15 are values for bits */ -#define MAX_HUFFMAN_BITS 16 - -void BrotliConvertBitDepthsToSymbols(const uint8_t* depth, - size_t len, - uint16_t* bits) { - /* In Brotli, all bit depths are [1..15] - 0 bit depth means that the symbol does not exist. */ - uint16_t bl_count[MAX_HUFFMAN_BITS] = { 0 }; - uint16_t next_code[MAX_HUFFMAN_BITS]; - size_t i; - int code = 0; - for (i = 0; i < len; ++i) { - ++bl_count[depth[i]]; - } - bl_count[0] = 0; - next_code[0] = 0; - for (i = 1; i < MAX_HUFFMAN_BITS; ++i) { - code = (code + bl_count[i - 1]) << 1; - next_code[i] = (uint16_t)code; - } - for (i = 0; i < len; ++i) { - if (depth[i]) { - bits[i] = BrotliReverseBits(depth[i], next_code[depth[i]]++); - } - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/entropy_encode.h b/src/deps/brotli/enc/entropy_encode.h deleted file mode 100644 index e1c779cc6f4dc..0000000000000 --- a/src/deps/brotli/enc/entropy_encode.h +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Entropy encoding (Huffman) utilities. */ - -#ifndef BROTLI_ENC_ENTROPY_ENCODE_H_ -#define BROTLI_ENC_ENTROPY_ENCODE_H_ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* A node of a Huffman tree. */ -typedef struct HuffmanTree { - uint32_t total_count_; - int16_t index_left_; - int16_t index_right_or_value_; -} HuffmanTree; - -static BROTLI_INLINE void InitHuffmanTree(HuffmanTree* self, uint32_t count, - int16_t left, int16_t right) { - self->total_count_ = count; - self->index_left_ = left; - self->index_right_or_value_ = right; -} - -/* Returns 1 is assignment of depths succeeded, otherwise 0. */ -BROTLI_INTERNAL BROTLI_BOOL BrotliSetDepth( - int p, HuffmanTree* pool, uint8_t* depth, int max_depth); - -/* This function will create a Huffman tree. - - The (data,length) contains the population counts. - The tree_limit is the maximum bit depth of the Huffman codes. - - The depth contains the tree, i.e., how many bits are used for - the symbol. - - The actual Huffman tree is constructed in the tree[] array, which has to - be at least 2 * length + 1 long. - - See http://en.wikipedia.org/wiki/Huffman_coding */ -BROTLI_INTERNAL void BrotliCreateHuffmanTree(const uint32_t* data, - const size_t length, - const int tree_limit, - HuffmanTree* tree, - uint8_t* depth); - -/* Change the population counts in a way that the consequent - Huffman tree compression, especially its RLE-part will be more - likely to compress this data more efficiently. - - length contains the size of the histogram. - counts contains the population counts. - good_for_rle is a buffer of at least length size */ -BROTLI_INTERNAL void BrotliOptimizeHuffmanCountsForRle( - size_t length, uint32_t* counts, uint8_t* good_for_rle); - -/* Write a Huffman tree from bit depths into the bit-stream representation - of a Huffman tree. The generated Huffman tree is to be compressed once - more using a Huffman tree */ -BROTLI_INTERNAL void BrotliWriteHuffmanTree(const uint8_t* depth, - size_t num, - size_t* tree_size, - uint8_t* tree, - uint8_t* extra_bits_data); - -/* Get the actual bit values for a tree of bit depths. */ -BROTLI_INTERNAL void BrotliConvertBitDepthsToSymbols(const uint8_t* depth, - size_t len, - uint16_t* bits); - -BROTLI_INTERNAL extern const size_t kBrotliShellGaps[6]; -/* Input size optimized Shell sort. */ -typedef BROTLI_BOOL (*HuffmanTreeComparator)( - const HuffmanTree*, const HuffmanTree*); -static BROTLI_INLINE void SortHuffmanTreeItems(HuffmanTree* items, - const size_t n, HuffmanTreeComparator comparator) { - if (n < 13) { - /* Insertion sort. */ - size_t i; - for (i = 1; i < n; ++i) { - HuffmanTree tmp = items[i]; - size_t k = i; - size_t j = i - 1; - while (comparator(&tmp, &items[j])) { - items[k] = items[j]; - k = j; - if (!j--) break; - } - items[k] = tmp; - } - return; - } else { - /* Shell sort. */ - int g = n < 57 ? 2 : 0; - for (; g < 6; ++g) { - size_t gap = kBrotliShellGaps[g]; - size_t i; - for (i = gap; i < n; ++i) { - size_t j = i; - HuffmanTree tmp = items[i]; - for (; j >= gap && comparator(&tmp, &items[j - gap]); j -= gap) { - items[j] = items[j - gap]; - } - items[j] = tmp; - } - } - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_ENTROPY_ENCODE_H_ */ diff --git a/src/deps/brotli/enc/entropy_encode_static.h b/src/deps/brotli/enc/entropy_encode_static.h deleted file mode 100644 index ecff1fe9eeba1..0000000000000 --- a/src/deps/brotli/enc/entropy_encode_static.h +++ /dev/null @@ -1,542 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Static entropy codes used for faster meta-block encoding. */ - -#ifndef BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_ -#define BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "write_bits.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static const uint8_t kCodeLengthDepth[18] = { - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 0, 4, 4, -}; - -static const uint8_t kStaticCommandCodeDepth[BROTLI_NUM_COMMAND_SYMBOLS] = { - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, -}; - -static const uint8_t kStaticDistanceCodeDepth[64] = { - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, -}; - -/* GENERATED CODE START */ -static const uint32_t kCodeLengthBits[18] = { - 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 15, 31, 0, 11, 7, -}; - -static BROTLI_INLINE void StoreStaticCodeLengthCode( - size_t* storage_ix, uint8_t* storage) { - BrotliWriteBits( - 40, BROTLI_MAKE_UINT64_T(0x0000FFu, 0x55555554u), storage_ix, storage); -} - -static const uint64_t kZeroRepsBits[BROTLI_NUM_COMMAND_SYMBOLS] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000007, 0x00000017, 0x00000027, - 0x00000037, 0x00000047, 0x00000057, 0x00000067, 0x00000077, 0x00000770, - 0x00000b87, 0x00001387, 0x00001b87, 0x00002387, 0x00002b87, 0x00003387, - 0x00003b87, 0x00000397, 0x00000b97, 0x00001397, 0x00001b97, 0x00002397, - 0x00002b97, 0x00003397, 0x00003b97, 0x000003a7, 0x00000ba7, 0x000013a7, - 0x00001ba7, 0x000023a7, 0x00002ba7, 0x000033a7, 0x00003ba7, 0x000003b7, - 0x00000bb7, 0x000013b7, 0x00001bb7, 0x000023b7, 0x00002bb7, 0x000033b7, - 0x00003bb7, 0x000003c7, 0x00000bc7, 0x000013c7, 0x00001bc7, 0x000023c7, - 0x00002bc7, 0x000033c7, 0x00003bc7, 0x000003d7, 0x00000bd7, 0x000013d7, - 0x00001bd7, 0x000023d7, 0x00002bd7, 0x000033d7, 0x00003bd7, 0x000003e7, - 0x00000be7, 0x000013e7, 0x00001be7, 0x000023e7, 0x00002be7, 0x000033e7, - 0x00003be7, 0x000003f7, 0x00000bf7, 0x000013f7, 0x00001bf7, 0x000023f7, - 0x00002bf7, 0x000033f7, 0x00003bf7, 0x0001c387, 0x0005c387, 0x0009c387, - 0x000dc387, 0x0011c387, 0x0015c387, 0x0019c387, 0x001dc387, 0x0001cb87, - 0x0005cb87, 0x0009cb87, 0x000dcb87, 0x0011cb87, 0x0015cb87, 0x0019cb87, - 0x001dcb87, 0x0001d387, 0x0005d387, 0x0009d387, 0x000dd387, 0x0011d387, - 0x0015d387, 0x0019d387, 0x001dd387, 0x0001db87, 0x0005db87, 0x0009db87, - 0x000ddb87, 0x0011db87, 0x0015db87, 0x0019db87, 0x001ddb87, 0x0001e387, - 0x0005e387, 0x0009e387, 0x000de387, 0x0011e387, 0x0015e387, 0x0019e387, - 0x001de387, 0x0001eb87, 0x0005eb87, 0x0009eb87, 0x000deb87, 0x0011eb87, - 0x0015eb87, 0x0019eb87, 0x001deb87, 0x0001f387, 0x0005f387, 0x0009f387, - 0x000df387, 0x0011f387, 0x0015f387, 0x0019f387, 0x001df387, 0x0001fb87, - 0x0005fb87, 0x0009fb87, 0x000dfb87, 0x0011fb87, 0x0015fb87, 0x0019fb87, - 0x001dfb87, 0x0001c397, 0x0005c397, 0x0009c397, 0x000dc397, 0x0011c397, - 0x0015c397, 0x0019c397, 0x001dc397, 0x0001cb97, 0x0005cb97, 0x0009cb97, - 0x000dcb97, 0x0011cb97, 0x0015cb97, 0x0019cb97, 0x001dcb97, 0x0001d397, - 0x0005d397, 0x0009d397, 0x000dd397, 0x0011d397, 0x0015d397, 0x0019d397, - 0x001dd397, 0x0001db97, 0x0005db97, 0x0009db97, 0x000ddb97, 0x0011db97, - 0x0015db97, 0x0019db97, 0x001ddb97, 0x0001e397, 0x0005e397, 0x0009e397, - 0x000de397, 0x0011e397, 0x0015e397, 0x0019e397, 0x001de397, 0x0001eb97, - 0x0005eb97, 0x0009eb97, 0x000deb97, 0x0011eb97, 0x0015eb97, 0x0019eb97, - 0x001deb97, 0x0001f397, 0x0005f397, 0x0009f397, 0x000df397, 0x0011f397, - 0x0015f397, 0x0019f397, 0x001df397, 0x0001fb97, 0x0005fb97, 0x0009fb97, - 0x000dfb97, 0x0011fb97, 0x0015fb97, 0x0019fb97, 0x001dfb97, 0x0001c3a7, - 0x0005c3a7, 0x0009c3a7, 0x000dc3a7, 0x0011c3a7, 0x0015c3a7, 0x0019c3a7, - 0x001dc3a7, 0x0001cba7, 0x0005cba7, 0x0009cba7, 0x000dcba7, 0x0011cba7, - 0x0015cba7, 0x0019cba7, 0x001dcba7, 0x0001d3a7, 0x0005d3a7, 0x0009d3a7, - 0x000dd3a7, 0x0011d3a7, 0x0015d3a7, 0x0019d3a7, 0x001dd3a7, 0x0001dba7, - 0x0005dba7, 0x0009dba7, 0x000ddba7, 0x0011dba7, 0x0015dba7, 0x0019dba7, - 0x001ddba7, 0x0001e3a7, 0x0005e3a7, 0x0009e3a7, 0x000de3a7, 0x0011e3a7, - 0x0015e3a7, 0x0019e3a7, 0x001de3a7, 0x0001eba7, 0x0005eba7, 0x0009eba7, - 0x000deba7, 0x0011eba7, 0x0015eba7, 0x0019eba7, 0x001deba7, 0x0001f3a7, - 0x0005f3a7, 0x0009f3a7, 0x000df3a7, 0x0011f3a7, 0x0015f3a7, 0x0019f3a7, - 0x001df3a7, 0x0001fba7, 0x0005fba7, 0x0009fba7, 0x000dfba7, 0x0011fba7, - 0x0015fba7, 0x0019fba7, 0x001dfba7, 0x0001c3b7, 0x0005c3b7, 0x0009c3b7, - 0x000dc3b7, 0x0011c3b7, 0x0015c3b7, 0x0019c3b7, 0x001dc3b7, 0x0001cbb7, - 0x0005cbb7, 0x0009cbb7, 0x000dcbb7, 0x0011cbb7, 0x0015cbb7, 0x0019cbb7, - 0x001dcbb7, 0x0001d3b7, 0x0005d3b7, 0x0009d3b7, 0x000dd3b7, 0x0011d3b7, - 0x0015d3b7, 0x0019d3b7, 0x001dd3b7, 0x0001dbb7, 0x0005dbb7, 0x0009dbb7, - 0x000ddbb7, 0x0011dbb7, 0x0015dbb7, 0x0019dbb7, 0x001ddbb7, 0x0001e3b7, - 0x0005e3b7, 0x0009e3b7, 0x000de3b7, 0x0011e3b7, 0x0015e3b7, 0x0019e3b7, - 0x001de3b7, 0x0001ebb7, 0x0005ebb7, 0x0009ebb7, 0x000debb7, 0x0011ebb7, - 0x0015ebb7, 0x0019ebb7, 0x001debb7, 0x0001f3b7, 0x0005f3b7, 0x0009f3b7, - 0x000df3b7, 0x0011f3b7, 0x0015f3b7, 0x0019f3b7, 0x001df3b7, 0x0001fbb7, - 0x0005fbb7, 0x0009fbb7, 0x000dfbb7, 0x0011fbb7, 0x0015fbb7, 0x0019fbb7, - 0x001dfbb7, 0x0001c3c7, 0x0005c3c7, 0x0009c3c7, 0x000dc3c7, 0x0011c3c7, - 0x0015c3c7, 0x0019c3c7, 0x001dc3c7, 0x0001cbc7, 0x0005cbc7, 0x0009cbc7, - 0x000dcbc7, 0x0011cbc7, 0x0015cbc7, 0x0019cbc7, 0x001dcbc7, 0x0001d3c7, - 0x0005d3c7, 0x0009d3c7, 0x000dd3c7, 0x0011d3c7, 0x0015d3c7, 0x0019d3c7, - 0x001dd3c7, 0x0001dbc7, 0x0005dbc7, 0x0009dbc7, 0x000ddbc7, 0x0011dbc7, - 0x0015dbc7, 0x0019dbc7, 0x001ddbc7, 0x0001e3c7, 0x0005e3c7, 0x0009e3c7, - 0x000de3c7, 0x0011e3c7, 0x0015e3c7, 0x0019e3c7, 0x001de3c7, 0x0001ebc7, - 0x0005ebc7, 0x0009ebc7, 0x000debc7, 0x0011ebc7, 0x0015ebc7, 0x0019ebc7, - 0x001debc7, 0x0001f3c7, 0x0005f3c7, 0x0009f3c7, 0x000df3c7, 0x0011f3c7, - 0x0015f3c7, 0x0019f3c7, 0x001df3c7, 0x0001fbc7, 0x0005fbc7, 0x0009fbc7, - 0x000dfbc7, 0x0011fbc7, 0x0015fbc7, 0x0019fbc7, 0x001dfbc7, 0x0001c3d7, - 0x0005c3d7, 0x0009c3d7, 0x000dc3d7, 0x0011c3d7, 0x0015c3d7, 0x0019c3d7, - 0x001dc3d7, 0x0001cbd7, 0x0005cbd7, 0x0009cbd7, 0x000dcbd7, 0x0011cbd7, - 0x0015cbd7, 0x0019cbd7, 0x001dcbd7, 0x0001d3d7, 0x0005d3d7, 0x0009d3d7, - 0x000dd3d7, 0x0011d3d7, 0x0015d3d7, 0x0019d3d7, 0x001dd3d7, 0x0001dbd7, - 0x0005dbd7, 0x0009dbd7, 0x000ddbd7, 0x0011dbd7, 0x0015dbd7, 0x0019dbd7, - 0x001ddbd7, 0x0001e3d7, 0x0005e3d7, 0x0009e3d7, 0x000de3d7, 0x0011e3d7, - 0x0015e3d7, 0x0019e3d7, 0x001de3d7, 0x0001ebd7, 0x0005ebd7, 0x0009ebd7, - 0x000debd7, 0x0011ebd7, 0x0015ebd7, 0x0019ebd7, 0x001debd7, 0x0001f3d7, - 0x0005f3d7, 0x0009f3d7, 0x000df3d7, 0x0011f3d7, 0x0015f3d7, 0x0019f3d7, - 0x001df3d7, 0x0001fbd7, 0x0005fbd7, 0x0009fbd7, 0x000dfbd7, 0x0011fbd7, - 0x0015fbd7, 0x0019fbd7, 0x001dfbd7, 0x0001c3e7, 0x0005c3e7, 0x0009c3e7, - 0x000dc3e7, 0x0011c3e7, 0x0015c3e7, 0x0019c3e7, 0x001dc3e7, 0x0001cbe7, - 0x0005cbe7, 0x0009cbe7, 0x000dcbe7, 0x0011cbe7, 0x0015cbe7, 0x0019cbe7, - 0x001dcbe7, 0x0001d3e7, 0x0005d3e7, 0x0009d3e7, 0x000dd3e7, 0x0011d3e7, - 0x0015d3e7, 0x0019d3e7, 0x001dd3e7, 0x0001dbe7, 0x0005dbe7, 0x0009dbe7, - 0x000ddbe7, 0x0011dbe7, 0x0015dbe7, 0x0019dbe7, 0x001ddbe7, 0x0001e3e7, - 0x0005e3e7, 0x0009e3e7, 0x000de3e7, 0x0011e3e7, 0x0015e3e7, 0x0019e3e7, - 0x001de3e7, 0x0001ebe7, 0x0005ebe7, 0x0009ebe7, 0x000debe7, 0x0011ebe7, - 0x0015ebe7, 0x0019ebe7, 0x001debe7, 0x0001f3e7, 0x0005f3e7, 0x0009f3e7, - 0x000df3e7, 0x0011f3e7, 0x0015f3e7, 0x0019f3e7, 0x001df3e7, 0x0001fbe7, - 0x0005fbe7, 0x0009fbe7, 0x000dfbe7, 0x0011fbe7, 0x0015fbe7, 0x0019fbe7, - 0x001dfbe7, 0x0001c3f7, 0x0005c3f7, 0x0009c3f7, 0x000dc3f7, 0x0011c3f7, - 0x0015c3f7, 0x0019c3f7, 0x001dc3f7, 0x0001cbf7, 0x0005cbf7, 0x0009cbf7, - 0x000dcbf7, 0x0011cbf7, 0x0015cbf7, 0x0019cbf7, 0x001dcbf7, 0x0001d3f7, - 0x0005d3f7, 0x0009d3f7, 0x000dd3f7, 0x0011d3f7, 0x0015d3f7, 0x0019d3f7, - 0x001dd3f7, 0x0001dbf7, 0x0005dbf7, 0x0009dbf7, 0x000ddbf7, 0x0011dbf7, - 0x0015dbf7, 0x0019dbf7, 0x001ddbf7, 0x0001e3f7, 0x0005e3f7, 0x0009e3f7, - 0x000de3f7, 0x0011e3f7, 0x0015e3f7, 0x0019e3f7, 0x001de3f7, 0x0001ebf7, - 0x0005ebf7, 0x0009ebf7, 0x000debf7, 0x0011ebf7, 0x0015ebf7, 0x0019ebf7, - 0x001debf7, 0x0001f3f7, 0x0005f3f7, 0x0009f3f7, 0x000df3f7, 0x0011f3f7, - 0x0015f3f7, 0x0019f3f7, 0x001df3f7, 0x0001fbf7, 0x0005fbf7, 0x0009fbf7, - 0x000dfbf7, 0x0011fbf7, 0x0015fbf7, 0x0019fbf7, 0x001dfbf7, 0x00e1c387, - 0x02e1c387, 0x04e1c387, 0x06e1c387, 0x08e1c387, 0x0ae1c387, 0x0ce1c387, - 0x0ee1c387, 0x00e5c387, 0x02e5c387, 0x04e5c387, 0x06e5c387, 0x08e5c387, - 0x0ae5c387, 0x0ce5c387, 0x0ee5c387, 0x00e9c387, 0x02e9c387, 0x04e9c387, - 0x06e9c387, 0x08e9c387, 0x0ae9c387, 0x0ce9c387, 0x0ee9c387, 0x00edc387, - 0x02edc387, 0x04edc387, 0x06edc387, 0x08edc387, 0x0aedc387, 0x0cedc387, - 0x0eedc387, 0x00f1c387, 0x02f1c387, 0x04f1c387, 0x06f1c387, 0x08f1c387, - 0x0af1c387, 0x0cf1c387, 0x0ef1c387, 0x00f5c387, 0x02f5c387, 0x04f5c387, - 0x06f5c387, 0x08f5c387, 0x0af5c387, 0x0cf5c387, 0x0ef5c387, 0x00f9c387, - 0x02f9c387, 0x04f9c387, 0x06f9c387, 0x08f9c387, 0x0af9c387, 0x0cf9c387, - 0x0ef9c387, 0x00fdc387, 0x02fdc387, 0x04fdc387, 0x06fdc387, 0x08fdc387, - 0x0afdc387, 0x0cfdc387, 0x0efdc387, 0x00e1cb87, 0x02e1cb87, 0x04e1cb87, - 0x06e1cb87, 0x08e1cb87, 0x0ae1cb87, 0x0ce1cb87, 0x0ee1cb87, 0x00e5cb87, - 0x02e5cb87, 0x04e5cb87, 0x06e5cb87, 0x08e5cb87, 0x0ae5cb87, 0x0ce5cb87, - 0x0ee5cb87, 0x00e9cb87, 0x02e9cb87, 0x04e9cb87, 0x06e9cb87, 0x08e9cb87, - 0x0ae9cb87, 0x0ce9cb87, 0x0ee9cb87, 0x00edcb87, 0x02edcb87, 0x04edcb87, - 0x06edcb87, 0x08edcb87, 0x0aedcb87, 0x0cedcb87, 0x0eedcb87, 0x00f1cb87, - 0x02f1cb87, 0x04f1cb87, 0x06f1cb87, 0x08f1cb87, 0x0af1cb87, 0x0cf1cb87, - 0x0ef1cb87, 0x00f5cb87, 0x02f5cb87, 0x04f5cb87, 0x06f5cb87, 0x08f5cb87, - 0x0af5cb87, 0x0cf5cb87, 0x0ef5cb87, 0x00f9cb87, 0x02f9cb87, 0x04f9cb87, - 0x06f9cb87, 0x08f9cb87, -}; - -static const uint32_t kZeroRepsDepth[BROTLI_NUM_COMMAND_SYMBOLS] = { - 0, 4, 8, 7, 7, 7, 7, 7, 7, 7, 7, 11, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -}; - -static const uint64_t kNonZeroRepsBits[BROTLI_NUM_COMMAND_SYMBOLS] = { - 0x0000000b, 0x0000001b, 0x0000002b, 0x0000003b, 0x000002cb, 0x000006cb, - 0x00000acb, 0x00000ecb, 0x000002db, 0x000006db, 0x00000adb, 0x00000edb, - 0x000002eb, 0x000006eb, 0x00000aeb, 0x00000eeb, 0x000002fb, 0x000006fb, - 0x00000afb, 0x00000efb, 0x0000b2cb, 0x0001b2cb, 0x0002b2cb, 0x0003b2cb, - 0x0000b6cb, 0x0001b6cb, 0x0002b6cb, 0x0003b6cb, 0x0000bacb, 0x0001bacb, - 0x0002bacb, 0x0003bacb, 0x0000becb, 0x0001becb, 0x0002becb, 0x0003becb, - 0x0000b2db, 0x0001b2db, 0x0002b2db, 0x0003b2db, 0x0000b6db, 0x0001b6db, - 0x0002b6db, 0x0003b6db, 0x0000badb, 0x0001badb, 0x0002badb, 0x0003badb, - 0x0000bedb, 0x0001bedb, 0x0002bedb, 0x0003bedb, 0x0000b2eb, 0x0001b2eb, - 0x0002b2eb, 0x0003b2eb, 0x0000b6eb, 0x0001b6eb, 0x0002b6eb, 0x0003b6eb, - 0x0000baeb, 0x0001baeb, 0x0002baeb, 0x0003baeb, 0x0000beeb, 0x0001beeb, - 0x0002beeb, 0x0003beeb, 0x0000b2fb, 0x0001b2fb, 0x0002b2fb, 0x0003b2fb, - 0x0000b6fb, 0x0001b6fb, 0x0002b6fb, 0x0003b6fb, 0x0000bafb, 0x0001bafb, - 0x0002bafb, 0x0003bafb, 0x0000befb, 0x0001befb, 0x0002befb, 0x0003befb, - 0x002cb2cb, 0x006cb2cb, 0x00acb2cb, 0x00ecb2cb, 0x002db2cb, 0x006db2cb, - 0x00adb2cb, 0x00edb2cb, 0x002eb2cb, 0x006eb2cb, 0x00aeb2cb, 0x00eeb2cb, - 0x002fb2cb, 0x006fb2cb, 0x00afb2cb, 0x00efb2cb, 0x002cb6cb, 0x006cb6cb, - 0x00acb6cb, 0x00ecb6cb, 0x002db6cb, 0x006db6cb, 0x00adb6cb, 0x00edb6cb, - 0x002eb6cb, 0x006eb6cb, 0x00aeb6cb, 0x00eeb6cb, 0x002fb6cb, 0x006fb6cb, - 0x00afb6cb, 0x00efb6cb, 0x002cbacb, 0x006cbacb, 0x00acbacb, 0x00ecbacb, - 0x002dbacb, 0x006dbacb, 0x00adbacb, 0x00edbacb, 0x002ebacb, 0x006ebacb, - 0x00aebacb, 0x00eebacb, 0x002fbacb, 0x006fbacb, 0x00afbacb, 0x00efbacb, - 0x002cbecb, 0x006cbecb, 0x00acbecb, 0x00ecbecb, 0x002dbecb, 0x006dbecb, - 0x00adbecb, 0x00edbecb, 0x002ebecb, 0x006ebecb, 0x00aebecb, 0x00eebecb, - 0x002fbecb, 0x006fbecb, 0x00afbecb, 0x00efbecb, 0x002cb2db, 0x006cb2db, - 0x00acb2db, 0x00ecb2db, 0x002db2db, 0x006db2db, 0x00adb2db, 0x00edb2db, - 0x002eb2db, 0x006eb2db, 0x00aeb2db, 0x00eeb2db, 0x002fb2db, 0x006fb2db, - 0x00afb2db, 0x00efb2db, 0x002cb6db, 0x006cb6db, 0x00acb6db, 0x00ecb6db, - 0x002db6db, 0x006db6db, 0x00adb6db, 0x00edb6db, 0x002eb6db, 0x006eb6db, - 0x00aeb6db, 0x00eeb6db, 0x002fb6db, 0x006fb6db, 0x00afb6db, 0x00efb6db, - 0x002cbadb, 0x006cbadb, 0x00acbadb, 0x00ecbadb, 0x002dbadb, 0x006dbadb, - 0x00adbadb, 0x00edbadb, 0x002ebadb, 0x006ebadb, 0x00aebadb, 0x00eebadb, - 0x002fbadb, 0x006fbadb, 0x00afbadb, 0x00efbadb, 0x002cbedb, 0x006cbedb, - 0x00acbedb, 0x00ecbedb, 0x002dbedb, 0x006dbedb, 0x00adbedb, 0x00edbedb, - 0x002ebedb, 0x006ebedb, 0x00aebedb, 0x00eebedb, 0x002fbedb, 0x006fbedb, - 0x00afbedb, 0x00efbedb, 0x002cb2eb, 0x006cb2eb, 0x00acb2eb, 0x00ecb2eb, - 0x002db2eb, 0x006db2eb, 0x00adb2eb, 0x00edb2eb, 0x002eb2eb, 0x006eb2eb, - 0x00aeb2eb, 0x00eeb2eb, 0x002fb2eb, 0x006fb2eb, 0x00afb2eb, 0x00efb2eb, - 0x002cb6eb, 0x006cb6eb, 0x00acb6eb, 0x00ecb6eb, 0x002db6eb, 0x006db6eb, - 0x00adb6eb, 0x00edb6eb, 0x002eb6eb, 0x006eb6eb, 0x00aeb6eb, 0x00eeb6eb, - 0x002fb6eb, 0x006fb6eb, 0x00afb6eb, 0x00efb6eb, 0x002cbaeb, 0x006cbaeb, - 0x00acbaeb, 0x00ecbaeb, 0x002dbaeb, 0x006dbaeb, 0x00adbaeb, 0x00edbaeb, - 0x002ebaeb, 0x006ebaeb, 0x00aebaeb, 0x00eebaeb, 0x002fbaeb, 0x006fbaeb, - 0x00afbaeb, 0x00efbaeb, 0x002cbeeb, 0x006cbeeb, 0x00acbeeb, 0x00ecbeeb, - 0x002dbeeb, 0x006dbeeb, 0x00adbeeb, 0x00edbeeb, 0x002ebeeb, 0x006ebeeb, - 0x00aebeeb, 0x00eebeeb, 0x002fbeeb, 0x006fbeeb, 0x00afbeeb, 0x00efbeeb, - 0x002cb2fb, 0x006cb2fb, 0x00acb2fb, 0x00ecb2fb, 0x002db2fb, 0x006db2fb, - 0x00adb2fb, 0x00edb2fb, 0x002eb2fb, 0x006eb2fb, 0x00aeb2fb, 0x00eeb2fb, - 0x002fb2fb, 0x006fb2fb, 0x00afb2fb, 0x00efb2fb, 0x002cb6fb, 0x006cb6fb, - 0x00acb6fb, 0x00ecb6fb, 0x002db6fb, 0x006db6fb, 0x00adb6fb, 0x00edb6fb, - 0x002eb6fb, 0x006eb6fb, 0x00aeb6fb, 0x00eeb6fb, 0x002fb6fb, 0x006fb6fb, - 0x00afb6fb, 0x00efb6fb, 0x002cbafb, 0x006cbafb, 0x00acbafb, 0x00ecbafb, - 0x002dbafb, 0x006dbafb, 0x00adbafb, 0x00edbafb, 0x002ebafb, 0x006ebafb, - 0x00aebafb, 0x00eebafb, 0x002fbafb, 0x006fbafb, 0x00afbafb, 0x00efbafb, - 0x002cbefb, 0x006cbefb, 0x00acbefb, 0x00ecbefb, 0x002dbefb, 0x006dbefb, - 0x00adbefb, 0x00edbefb, 0x002ebefb, 0x006ebefb, 0x00aebefb, 0x00eebefb, - 0x002fbefb, 0x006fbefb, 0x00afbefb, 0x00efbefb, 0x0b2cb2cb, 0x1b2cb2cb, - 0x2b2cb2cb, 0x3b2cb2cb, 0x0b6cb2cb, 0x1b6cb2cb, 0x2b6cb2cb, 0x3b6cb2cb, - 0x0bacb2cb, 0x1bacb2cb, 0x2bacb2cb, 0x3bacb2cb, 0x0becb2cb, 0x1becb2cb, - 0x2becb2cb, 0x3becb2cb, 0x0b2db2cb, 0x1b2db2cb, 0x2b2db2cb, 0x3b2db2cb, - 0x0b6db2cb, 0x1b6db2cb, 0x2b6db2cb, 0x3b6db2cb, 0x0badb2cb, 0x1badb2cb, - 0x2badb2cb, 0x3badb2cb, 0x0bedb2cb, 0x1bedb2cb, 0x2bedb2cb, 0x3bedb2cb, - 0x0b2eb2cb, 0x1b2eb2cb, 0x2b2eb2cb, 0x3b2eb2cb, 0x0b6eb2cb, 0x1b6eb2cb, - 0x2b6eb2cb, 0x3b6eb2cb, 0x0baeb2cb, 0x1baeb2cb, 0x2baeb2cb, 0x3baeb2cb, - 0x0beeb2cb, 0x1beeb2cb, 0x2beeb2cb, 0x3beeb2cb, 0x0b2fb2cb, 0x1b2fb2cb, - 0x2b2fb2cb, 0x3b2fb2cb, 0x0b6fb2cb, 0x1b6fb2cb, 0x2b6fb2cb, 0x3b6fb2cb, - 0x0bafb2cb, 0x1bafb2cb, 0x2bafb2cb, 0x3bafb2cb, 0x0befb2cb, 0x1befb2cb, - 0x2befb2cb, 0x3befb2cb, 0x0b2cb6cb, 0x1b2cb6cb, 0x2b2cb6cb, 0x3b2cb6cb, - 0x0b6cb6cb, 0x1b6cb6cb, 0x2b6cb6cb, 0x3b6cb6cb, 0x0bacb6cb, 0x1bacb6cb, - 0x2bacb6cb, 0x3bacb6cb, 0x0becb6cb, 0x1becb6cb, 0x2becb6cb, 0x3becb6cb, - 0x0b2db6cb, 0x1b2db6cb, 0x2b2db6cb, 0x3b2db6cb, 0x0b6db6cb, 0x1b6db6cb, - 0x2b6db6cb, 0x3b6db6cb, 0x0badb6cb, 0x1badb6cb, 0x2badb6cb, 0x3badb6cb, - 0x0bedb6cb, 0x1bedb6cb, 0x2bedb6cb, 0x3bedb6cb, 0x0b2eb6cb, 0x1b2eb6cb, - 0x2b2eb6cb, 0x3b2eb6cb, 0x0b6eb6cb, 0x1b6eb6cb, 0x2b6eb6cb, 0x3b6eb6cb, - 0x0baeb6cb, 0x1baeb6cb, 0x2baeb6cb, 0x3baeb6cb, 0x0beeb6cb, 0x1beeb6cb, - 0x2beeb6cb, 0x3beeb6cb, 0x0b2fb6cb, 0x1b2fb6cb, 0x2b2fb6cb, 0x3b2fb6cb, - 0x0b6fb6cb, 0x1b6fb6cb, 0x2b6fb6cb, 0x3b6fb6cb, 0x0bafb6cb, 0x1bafb6cb, - 0x2bafb6cb, 0x3bafb6cb, 0x0befb6cb, 0x1befb6cb, 0x2befb6cb, 0x3befb6cb, - 0x0b2cbacb, 0x1b2cbacb, 0x2b2cbacb, 0x3b2cbacb, 0x0b6cbacb, 0x1b6cbacb, - 0x2b6cbacb, 0x3b6cbacb, 0x0bacbacb, 0x1bacbacb, 0x2bacbacb, 0x3bacbacb, - 0x0becbacb, 0x1becbacb, 0x2becbacb, 0x3becbacb, 0x0b2dbacb, 0x1b2dbacb, - 0x2b2dbacb, 0x3b2dbacb, 0x0b6dbacb, 0x1b6dbacb, 0x2b6dbacb, 0x3b6dbacb, - 0x0badbacb, 0x1badbacb, 0x2badbacb, 0x3badbacb, 0x0bedbacb, 0x1bedbacb, - 0x2bedbacb, 0x3bedbacb, 0x0b2ebacb, 0x1b2ebacb, 0x2b2ebacb, 0x3b2ebacb, - 0x0b6ebacb, 0x1b6ebacb, 0x2b6ebacb, 0x3b6ebacb, 0x0baebacb, 0x1baebacb, - 0x2baebacb, 0x3baebacb, 0x0beebacb, 0x1beebacb, 0x2beebacb, 0x3beebacb, - 0x0b2fbacb, 0x1b2fbacb, 0x2b2fbacb, 0x3b2fbacb, 0x0b6fbacb, 0x1b6fbacb, - 0x2b6fbacb, 0x3b6fbacb, 0x0bafbacb, 0x1bafbacb, 0x2bafbacb, 0x3bafbacb, - 0x0befbacb, 0x1befbacb, 0x2befbacb, 0x3befbacb, 0x0b2cbecb, 0x1b2cbecb, - 0x2b2cbecb, 0x3b2cbecb, 0x0b6cbecb, 0x1b6cbecb, 0x2b6cbecb, 0x3b6cbecb, - 0x0bacbecb, 0x1bacbecb, 0x2bacbecb, 0x3bacbecb, 0x0becbecb, 0x1becbecb, - 0x2becbecb, 0x3becbecb, 0x0b2dbecb, 0x1b2dbecb, 0x2b2dbecb, 0x3b2dbecb, - 0x0b6dbecb, 0x1b6dbecb, 0x2b6dbecb, 0x3b6dbecb, 0x0badbecb, 0x1badbecb, - 0x2badbecb, 0x3badbecb, 0x0bedbecb, 0x1bedbecb, 0x2bedbecb, 0x3bedbecb, - 0x0b2ebecb, 0x1b2ebecb, 0x2b2ebecb, 0x3b2ebecb, 0x0b6ebecb, 0x1b6ebecb, - 0x2b6ebecb, 0x3b6ebecb, 0x0baebecb, 0x1baebecb, 0x2baebecb, 0x3baebecb, - 0x0beebecb, 0x1beebecb, 0x2beebecb, 0x3beebecb, 0x0b2fbecb, 0x1b2fbecb, - 0x2b2fbecb, 0x3b2fbecb, 0x0b6fbecb, 0x1b6fbecb, 0x2b6fbecb, 0x3b6fbecb, - 0x0bafbecb, 0x1bafbecb, 0x2bafbecb, 0x3bafbecb, 0x0befbecb, 0x1befbecb, - 0x2befbecb, 0x3befbecb, 0x0b2cb2db, 0x1b2cb2db, 0x2b2cb2db, 0x3b2cb2db, - 0x0b6cb2db, 0x1b6cb2db, 0x2b6cb2db, 0x3b6cb2db, 0x0bacb2db, 0x1bacb2db, - 0x2bacb2db, 0x3bacb2db, 0x0becb2db, 0x1becb2db, 0x2becb2db, 0x3becb2db, - 0x0b2db2db, 0x1b2db2db, 0x2b2db2db, 0x3b2db2db, 0x0b6db2db, 0x1b6db2db, - 0x2b6db2db, 0x3b6db2db, 0x0badb2db, 0x1badb2db, 0x2badb2db, 0x3badb2db, - 0x0bedb2db, 0x1bedb2db, 0x2bedb2db, 0x3bedb2db, 0x0b2eb2db, 0x1b2eb2db, - 0x2b2eb2db, 0x3b2eb2db, 0x0b6eb2db, 0x1b6eb2db, 0x2b6eb2db, 0x3b6eb2db, - 0x0baeb2db, 0x1baeb2db, 0x2baeb2db, 0x3baeb2db, 0x0beeb2db, 0x1beeb2db, - 0x2beeb2db, 0x3beeb2db, 0x0b2fb2db, 0x1b2fb2db, 0x2b2fb2db, 0x3b2fb2db, - 0x0b6fb2db, 0x1b6fb2db, 0x2b6fb2db, 0x3b6fb2db, 0x0bafb2db, 0x1bafb2db, - 0x2bafb2db, 0x3bafb2db, 0x0befb2db, 0x1befb2db, 0x2befb2db, 0x3befb2db, - 0x0b2cb6db, 0x1b2cb6db, 0x2b2cb6db, 0x3b2cb6db, 0x0b6cb6db, 0x1b6cb6db, - 0x2b6cb6db, 0x3b6cb6db, 0x0bacb6db, 0x1bacb6db, 0x2bacb6db, 0x3bacb6db, - 0x0becb6db, 0x1becb6db, 0x2becb6db, 0x3becb6db, 0x0b2db6db, 0x1b2db6db, - 0x2b2db6db, 0x3b2db6db, 0x0b6db6db, 0x1b6db6db, 0x2b6db6db, 0x3b6db6db, - 0x0badb6db, 0x1badb6db, 0x2badb6db, 0x3badb6db, 0x0bedb6db, 0x1bedb6db, - 0x2bedb6db, 0x3bedb6db, 0x0b2eb6db, 0x1b2eb6db, 0x2b2eb6db, 0x3b2eb6db, - 0x0b6eb6db, 0x1b6eb6db, 0x2b6eb6db, 0x3b6eb6db, 0x0baeb6db, 0x1baeb6db, - 0x2baeb6db, 0x3baeb6db, -}; - -static const uint32_t kNonZeroRepsDepth[BROTLI_NUM_COMMAND_SYMBOLS] = { - 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, -}; - -static const uint16_t kStaticCommandCodeBits[BROTLI_NUM_COMMAND_SYMBOLS] = { - 0, 256, 128, 384, 64, 320, 192, 448, - 32, 288, 160, 416, 96, 352, 224, 480, - 16, 272, 144, 400, 80, 336, 208, 464, - 48, 304, 176, 432, 112, 368, 240, 496, - 8, 264, 136, 392, 72, 328, 200, 456, - 40, 296, 168, 424, 104, 360, 232, 488, - 24, 280, 152, 408, 88, 344, 216, 472, - 56, 312, 184, 440, 120, 376, 248, 504, - 4, 260, 132, 388, 68, 324, 196, 452, - 36, 292, 164, 420, 100, 356, 228, 484, - 20, 276, 148, 404, 84, 340, 212, 468, - 52, 308, 180, 436, 116, 372, 244, 500, - 12, 268, 140, 396, 76, 332, 204, 460, - 44, 300, 172, 428, 108, 364, 236, 492, - 28, 284, 156, 412, 92, 348, 220, 476, - 60, 316, 188, 444, 124, 380, 252, 508, - 2, 258, 130, 386, 66, 322, 194, 450, - 34, 290, 162, 418, 98, 354, 226, 482, - 18, 274, 146, 402, 82, 338, 210, 466, - 50, 306, 178, 434, 114, 370, 242, 498, - 10, 266, 138, 394, 74, 330, 202, 458, - 42, 298, 170, 426, 106, 362, 234, 490, - 26, 282, 154, 410, 90, 346, 218, 474, - 58, 314, 186, 442, 122, 378, 250, 506, - 6, 262, 134, 390, 70, 326, 198, 454, - 38, 294, 166, 422, 102, 358, 230, 486, - 22, 278, 150, 406, 86, 342, 214, 470, - 54, 310, 182, 438, 118, 374, 246, 502, - 14, 270, 142, 398, 78, 334, 206, 462, - 46, 302, 174, 430, 110, 366, 238, 494, - 30, 286, 158, 414, 94, 350, 222, 478, - 62, 318, 190, 446, 126, 382, 254, 510, - 1, 257, 129, 385, 65, 321, 193, 449, - 33, 289, 161, 417, 97, 353, 225, 481, - 17, 273, 145, 401, 81, 337, 209, 465, - 49, 305, 177, 433, 113, 369, 241, 497, - 9, 265, 137, 393, 73, 329, 201, 457, - 41, 297, 169, 425, 105, 361, 233, 489, - 25, 281, 153, 409, 89, 345, 217, 473, - 57, 313, 185, 441, 121, 377, 249, 505, - 5, 261, 133, 389, 69, 325, 197, 453, - 37, 293, 165, 421, 101, 357, 229, 485, - 21, 277, 149, 405, 85, 341, 213, 469, - 53, 309, 181, 437, 117, 373, 245, 501, - 13, 269, 141, 397, 77, 333, 205, 461, - 45, 301, 173, 429, 109, 365, 237, 493, - 29, 285, 157, 413, 93, 349, 221, 477, - 61, 317, 189, 445, 125, 381, 253, 509, - 3, 259, 131, 387, 67, 323, 195, 451, - 35, 291, 163, 419, 99, 355, 227, 483, - 19, 275, 147, 403, 83, 339, 211, 467, - 51, 307, 179, 435, 115, 371, 243, 499, - 11, 267, 139, 395, 75, 331, 203, 459, - 43, 299, 171, 427, 107, 363, 235, 491, - 27, 283, 155, 411, 91, 347, 219, 475, - 59, 315, 187, 443, 123, 379, 251, 507, - 7, 1031, 519, 1543, 263, 1287, 775, 1799, - 135, 1159, 647, 1671, 391, 1415, 903, 1927, - 71, 1095, 583, 1607, 327, 1351, 839, 1863, - 199, 1223, 711, 1735, 455, 1479, 967, 1991, - 39, 1063, 551, 1575, 295, 1319, 807, 1831, - 167, 1191, 679, 1703, 423, 1447, 935, 1959, - 103, 1127, 615, 1639, 359, 1383, 871, 1895, - 231, 1255, 743, 1767, 487, 1511, 999, 2023, - 23, 1047, 535, 1559, 279, 1303, 791, 1815, - 151, 1175, 663, 1687, 407, 1431, 919, 1943, - 87, 1111, 599, 1623, 343, 1367, 855, 1879, - 215, 1239, 727, 1751, 471, 1495, 983, 2007, - 55, 1079, 567, 1591, 311, 1335, 823, 1847, - 183, 1207, 695, 1719, 439, 1463, 951, 1975, - 119, 1143, 631, 1655, 375, 1399, 887, 1911, - 247, 1271, 759, 1783, 503, 1527, 1015, 2039, - 15, 1039, 527, 1551, 271, 1295, 783, 1807, - 143, 1167, 655, 1679, 399, 1423, 911, 1935, - 79, 1103, 591, 1615, 335, 1359, 847, 1871, - 207, 1231, 719, 1743, 463, 1487, 975, 1999, - 47, 1071, 559, 1583, 303, 1327, 815, 1839, - 175, 1199, 687, 1711, 431, 1455, 943, 1967, - 111, 1135, 623, 1647, 367, 1391, 879, 1903, - 239, 1263, 751, 1775, 495, 1519, 1007, 2031, - 31, 1055, 543, 1567, 287, 1311, 799, 1823, - 159, 1183, 671, 1695, 415, 1439, 927, 1951, - 95, 1119, 607, 1631, 351, 1375, 863, 1887, - 223, 1247, 735, 1759, 479, 1503, 991, 2015, - 63, 1087, 575, 1599, 319, 1343, 831, 1855, - 191, 1215, 703, 1727, 447, 1471, 959, 1983, - 127, 1151, 639, 1663, 383, 1407, 895, 1919, - 255, 1279, 767, 1791, 511, 1535, 1023, 2047, -}; - -static BROTLI_INLINE void StoreStaticCommandHuffmanTree( - size_t* storage_ix, uint8_t* storage) { - BrotliWriteBits( - 56, BROTLI_MAKE_UINT64_T(0x926244U, 0x16307003U), storage_ix, storage); - BrotliWriteBits(3, 0x00000000U, storage_ix, storage); -} - -static const uint16_t kStaticDistanceCodeBits[64] = { - 0, 32, 16, 48, 8, 40, 24, 56, 4, 36, 20, 52, 12, 44, 28, 60, - 2, 34, 18, 50, 10, 42, 26, 58, 6, 38, 22, 54, 14, 46, 30, 62, - 1, 33, 17, 49, 9, 41, 25, 57, 5, 37, 21, 53, 13, 45, 29, 61, - 3, 35, 19, 51, 11, 43, 27, 59, 7, 39, 23, 55, 15, 47, 31, 63, -}; - -static BROTLI_INLINE void StoreStaticDistanceHuffmanTree( - size_t* storage_ix, uint8_t* storage) { - BrotliWriteBits(28, 0x0369DC03u, storage_ix, storage); -} -/* GENERATED CODE END */ - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_ */ diff --git a/src/deps/brotli/enc/fast_log.c b/src/deps/brotli/enc/fast_log.c deleted file mode 100644 index 2fa0efcf862ae..0000000000000 --- a/src/deps/brotli/enc/fast_log.c +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "fast_log.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* ", ".join(["%.16ff" % x for x in [0.0]+[log2(x) for x in range(1, 256)]]) */ -const double kBrotliLog2Table[BROTLI_LOG2_TABLE_SIZE] = { - 0.0000000000000000f, 0.0000000000000000f, 1.0000000000000000f, - 1.5849625007211563f, 2.0000000000000000f, 2.3219280948873622f, - 2.5849625007211561f, 2.8073549220576042f, 3.0000000000000000f, - 3.1699250014423126f, 3.3219280948873626f, 3.4594316186372978f, - 3.5849625007211565f, 3.7004397181410922f, 3.8073549220576037f, - 3.9068905956085187f, 4.0000000000000000f, 4.0874628412503400f, - 4.1699250014423122f, 4.2479275134435852f, 4.3219280948873626f, - 4.3923174227787607f, 4.4594316186372973f, 4.5235619560570131f, - 4.5849625007211570f, 4.6438561897747244f, 4.7004397181410926f, - 4.7548875021634691f, 4.8073549220576037f, 4.8579809951275728f, - 4.9068905956085187f, 4.9541963103868758f, 5.0000000000000000f, - 5.0443941193584534f, 5.0874628412503400f, 5.1292830169449664f, - 5.1699250014423122f, 5.2094533656289501f, 5.2479275134435852f, - 5.2854022188622487f, 5.3219280948873626f, 5.3575520046180838f, - 5.3923174227787607f, 5.4262647547020979f, 5.4594316186372973f, - 5.4918530963296748f, 5.5235619560570131f, 5.5545888516776376f, - 5.5849625007211570f, 5.6147098441152083f, 5.6438561897747244f, - 5.6724253419714961f, 5.7004397181410926f, 5.7279204545631996f, - 5.7548875021634691f, 5.7813597135246599f, 5.8073549220576046f, - 5.8328900141647422f, 5.8579809951275719f, 5.8826430493618416f, - 5.9068905956085187f, 5.9307373375628867f, 5.9541963103868758f, - 5.9772799234999168f, 6.0000000000000000f, 6.0223678130284544f, - 6.0443941193584534f, 6.0660891904577721f, 6.0874628412503400f, - 6.1085244567781700f, 6.1292830169449672f, 6.1497471195046822f, - 6.1699250014423122f, 6.1898245588800176f, 6.2094533656289510f, - 6.2288186904958804f, 6.2479275134435861f, 6.2667865406949019f, - 6.2854022188622487f, 6.3037807481771031f, 6.3219280948873617f, - 6.3398500028846252f, 6.3575520046180847f, 6.3750394313469254f, - 6.3923174227787598f, 6.4093909361377026f, 6.4262647547020979f, - 6.4429434958487288f, 6.4594316186372982f, 6.4757334309663976f, - 6.4918530963296748f, 6.5077946401986964f, 6.5235619560570131f, - 6.5391588111080319f, 6.5545888516776376f, 6.5698556083309478f, - 6.5849625007211561f, 6.5999128421871278f, 6.6147098441152092f, - 6.6293566200796095f, 6.6438561897747253f, 6.6582114827517955f, - 6.6724253419714952f, 6.6865005271832185f, 6.7004397181410917f, - 6.7142455176661224f, 6.7279204545631988f, 6.7414669864011465f, - 6.7548875021634691f, 6.7681843247769260f, 6.7813597135246599f, - 6.7944158663501062f, 6.8073549220576037f, 6.8201789624151887f, - 6.8328900141647422f, 6.8454900509443757f, 6.8579809951275719f, - 6.8703647195834048f, 6.8826430493618416f, 6.8948177633079437f, - 6.9068905956085187f, 6.9188632372745955f, 6.9307373375628867f, - 6.9425145053392399f, 6.9541963103868758f, 6.9657842846620879f, - 6.9772799234999168f, 6.9886846867721664f, 7.0000000000000000f, - 7.0112272554232540f, 7.0223678130284544f, 7.0334230015374501f, - 7.0443941193584534f, 7.0552824355011898f, 7.0660891904577721f, - 7.0768155970508317f, 7.0874628412503400f, 7.0980320829605272f, - 7.1085244567781700f, 7.1189410727235076f, 7.1292830169449664f, - 7.1395513523987937f, 7.1497471195046822f, 7.1598713367783891f, - 7.1699250014423130f, 7.1799090900149345f, 7.1898245588800176f, - 7.1996723448363644f, 7.2094533656289492f, 7.2191685204621621f, - 7.2288186904958804f, 7.2384047393250794f, 7.2479275134435861f, - 7.2573878426926521f, 7.2667865406949019f, 7.2761244052742384f, - 7.2854022188622487f, 7.2946207488916270f, 7.3037807481771031f, - 7.3128829552843557f, 7.3219280948873617f, 7.3309168781146177f, - 7.3398500028846243f, 7.3487281542310781f, 7.3575520046180847f, - 7.3663222142458151f, 7.3750394313469254f, 7.3837042924740528f, - 7.3923174227787607f, 7.4008794362821844f, 7.4093909361377026f, - 7.4178525148858991f, 7.4262647547020979f, 7.4346282276367255f, - 7.4429434958487288f, 7.4512111118323299f, 7.4594316186372973f, - 7.4676055500829976f, 7.4757334309663976f, 7.4838157772642564f, - 7.4918530963296748f, 7.4998458870832057f, 7.5077946401986964f, - 7.5156998382840436f, 7.5235619560570131f, 7.5313814605163119f, - 7.5391588111080319f, 7.5468944598876373f, 7.5545888516776376f, - 7.5622424242210728f, 7.5698556083309478f, 7.5774288280357487f, - 7.5849625007211561f, 7.5924570372680806f, 7.5999128421871278f, - 7.6073303137496113f, 7.6147098441152075f, 7.6220518194563764f, - 7.6293566200796095f, 7.6366246205436488f, 7.6438561897747244f, - 7.6510516911789290f, 7.6582114827517955f, 7.6653359171851765f, - 7.6724253419714952f, 7.6794800995054464f, 7.6865005271832185f, - 7.6934869574993252f, 7.7004397181410926f, 7.7073591320808825f, - 7.7142455176661224f, 7.7210991887071856f, 7.7279204545631996f, - 7.7347096202258392f, 7.7414669864011465f, 7.7481928495894596f, - 7.7548875021634691f, 7.7615512324444795f, 7.7681843247769260f, - 7.7747870596011737f, 7.7813597135246608f, 7.7879025593914317f, - 7.7944158663501062f, 7.8008998999203047f, 7.8073549220576037f, - 7.8137811912170374f, 7.8201789624151887f, 7.8265484872909159f, - 7.8328900141647422f, 7.8392037880969445f, 7.8454900509443757f, - 7.8517490414160571f, 7.8579809951275719f, 7.8641861446542798f, - 7.8703647195834048f, 7.8765169465650002f, 7.8826430493618425f, - 7.8887432488982601f, 7.8948177633079446f, 7.9008668079807496f, - 7.9068905956085187f, 7.9128893362299619f, 7.9188632372745955f, - 7.9248125036057813f, 7.9307373375628867f, 7.9366379390025719f, - 7.9425145053392399f, 7.9483672315846778f, 7.9541963103868758f, - 7.9600019320680806f, 7.9657842846620870f, 7.9715435539507720f, - 7.9772799234999168f, 7.9829935746943104f, 7.9886846867721664f, - 7.9943534368588578f -}; - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/fast_log.h b/src/deps/brotli/enc/fast_log.h deleted file mode 100644 index f82f4cffc840f..0000000000000 --- a/src/deps/brotli/enc/fast_log.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Utilities for fast computation of logarithms. */ - -#ifndef BROTLI_ENC_FAST_LOG_H_ -#define BROTLI_ENC_FAST_LOG_H_ - -#include - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static BROTLI_INLINE uint32_t Log2FloorNonZero(size_t n) { -#if defined(BROTLI_BSR32) - return BROTLI_BSR32((uint32_t)n); -#else - uint32_t result = 0; - while (n >>= 1) result++; - return result; -#endif -} - -#define BROTLI_LOG2_TABLE_SIZE 256 - -/* A lookup table for small values of log2(int) to be used in entropy - computation. */ -BROTLI_INTERNAL extern const double kBrotliLog2Table[BROTLI_LOG2_TABLE_SIZE]; - -/* Visual Studio 2012 and Android API levels < 18 do not have the log2() - * function defined, so we use log() and a multiplication instead. */ -#if !defined(BROTLI_HAVE_LOG2) -#if ((defined(_MSC_VER) && _MSC_VER <= 1700) || \ - (defined(__ANDROID_API__) && __ANDROID_API__ < 18)) -#define BROTLI_HAVE_LOG2 0 -#else -#define BROTLI_HAVE_LOG2 1 -#endif -#endif - -#define LOG_2_INV 1.4426950408889634 - -/* Faster logarithm for small integers, with the property of log2(0) == 0. */ -static BROTLI_INLINE double FastLog2(size_t v) { - if (v < BROTLI_LOG2_TABLE_SIZE) { - return kBrotliLog2Table[v]; - } -#if !(BROTLI_HAVE_LOG2) - return log((double)v) * LOG_2_INV; -#else - return log2((double)v); -#endif -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_FAST_LOG_H_ */ diff --git a/src/deps/brotli/enc/find_match_length.h b/src/deps/brotli/enc/find_match_length.h deleted file mode 100644 index f3de0bdb6b068..0000000000000 --- a/src/deps/brotli/enc/find_match_length.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Function to find maximal matching prefixes of strings. */ - -#ifndef BROTLI_ENC_FIND_MATCH_LENGTH_H_ -#define BROTLI_ENC_FIND_MATCH_LENGTH_H_ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* Separate implementation for little-endian 64-bit targets, for speed. */ -#if defined(BROTLI_TZCNT64) && BROTLI_64_BITS && BROTLI_LITTLE_ENDIAN -static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1, - const uint8_t* s2, - size_t limit) { - const uint8_t *s1_orig = s1; - for (; limit >= 8; limit -= 8) { - uint64_t x = BROTLI_UNALIGNED_LOAD64LE(s2) ^ - BROTLI_UNALIGNED_LOAD64LE(s1); - s2 += 8; - if (x != 0) { - size_t matching_bits = (size_t)BROTLI_TZCNT64(x); - return (size_t)(s1 - s1_orig) + (matching_bits >> 3); - } - s1 += 8; - } - while (limit && *s1 == *s2) { - limit--; - ++s2; - ++s1; - } - return (size_t)(s1 - s1_orig); -} -#else -static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1, - const uint8_t* s2, - size_t limit) { - size_t matched = 0; - const uint8_t* s2_limit = s2 + limit; - const uint8_t* s2_ptr = s2; - /* Find out how long the match is. We loop over the data 32 bits at a - time until we find a 32-bit block that doesn't match; then we find - the first non-matching bit and use that to calculate the total - length of the match. */ - while (s2_ptr <= s2_limit - 4 && - BrotliUnalignedRead32(s2_ptr) == - BrotliUnalignedRead32(s1 + matched)) { - s2_ptr += 4; - matched += 4; - } - while ((s2_ptr < s2_limit) && (s1[matched] == *s2_ptr)) { - ++s2_ptr; - ++matched; - } - return matched; -} -#endif - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_FIND_MATCH_LENGTH_H_ */ diff --git a/src/deps/brotli/enc/hash.h b/src/deps/brotli/enc/hash.h deleted file mode 100644 index 5677d82920b0a..0000000000000 --- a/src/deps/brotli/enc/hash.h +++ /dev/null @@ -1,728 +0,0 @@ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* A (forgetful) hash table to the data seen by the compressor, to - help create backward references to previous data. */ - -#ifndef BROTLI_ENC_HASH_H_ -#define BROTLI_ENC_HASH_H_ - -#include /* exit */ -#include /* memcmp, memset */ - -#include - -#include "../common/constants.h" -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "compound_dictionary.h" -#include "encoder_dict.h" -#include "fast_log.h" -#include "find_match_length.h" -#include "memory.h" -#include "quality.h" -#include "static_dict.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct { - /** - * Dynamically allocated areas; regular hasher uses one or two allocations; - * "composite" hasher uses up to 4 allocations. - */ - void* extra[4]; - - /** - * False before the fisrt invocation of HasherSetup (where "extra" memory) - * is allocated. - */ - BROTLI_BOOL is_setup_; - - size_t dict_num_lookups; - size_t dict_num_matches; - - BrotliHasherParams params; - - /** - * False if hasher needs to be "prepared" before use (before the first - * invocation of HasherSetup or after HasherReset). "preparation" is hasher - * data initialization (using input ringbuffer). - */ - BROTLI_BOOL is_prepared_; -} HasherCommon; - -#define score_t size_t - -static const uint32_t kCutoffTransformsCount = 10; -/* 0, 12, 27, 23, 42, 63, 56, 48, 59, 64 */ -/* 0+0, 4+8, 8+19, 12+11, 16+26, 20+43, 24+32, 28+20, 32+27, 36+28 */ -static const uint64_t kCutoffTransforms = - BROTLI_MAKE_UINT64_T(0x071B520A, 0xDA2D3200); - -typedef struct HasherSearchResult { - size_t len; - size_t distance; - score_t score; - int len_code_delta; /* == len_code - len */ -} HasherSearchResult; - -/* kHashMul32 multiplier has these properties: - * The multiplier must be odd. Otherwise we may lose the highest bit. - * No long streaks of ones or zeros. - * There is no effort to ensure that it is a prime, the oddity is enough - for this use. - * The number has been tuned heuristically against compression benchmarks. */ -static const uint32_t kHashMul32 = 0x1E35A7BD; -static const uint64_t kHashMul64 = - BROTLI_MAKE_UINT64_T(0x1FE35A7Bu, 0xD3579BD3u); - -static BROTLI_INLINE uint32_t Hash14(const uint8_t* data) { - uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return h >> (32 - 14); -} - -static BROTLI_INLINE void PrepareDistanceCache( - int* BROTLI_RESTRICT distance_cache, const int num_distances) { - if (num_distances > 4) { - int last_distance = distance_cache[0]; - distance_cache[4] = last_distance - 1; - distance_cache[5] = last_distance + 1; - distance_cache[6] = last_distance - 2; - distance_cache[7] = last_distance + 2; - distance_cache[8] = last_distance - 3; - distance_cache[9] = last_distance + 3; - if (num_distances > 10) { - int next_last_distance = distance_cache[1]; - distance_cache[10] = next_last_distance - 1; - distance_cache[11] = next_last_distance + 1; - distance_cache[12] = next_last_distance - 2; - distance_cache[13] = next_last_distance + 2; - distance_cache[14] = next_last_distance - 3; - distance_cache[15] = next_last_distance + 3; - } - } -} - -#define BROTLI_LITERAL_BYTE_SCORE 135 -#define BROTLI_DISTANCE_BIT_PENALTY 30 -/* Score must be positive after applying maximal penalty. */ -#define BROTLI_SCORE_BASE (BROTLI_DISTANCE_BIT_PENALTY * 8 * sizeof(size_t)) - -/* Usually, we always choose the longest backward reference. This function - allows for the exception of that rule. - - If we choose a backward reference that is further away, it will - usually be coded with more bits. We approximate this by assuming - log2(distance). If the distance can be expressed in terms of the - last four distances, we use some heuristic constants to estimate - the bits cost. For the first up to four literals we use the bit - cost of the literals from the literal cost model, after that we - use the average bit cost of the cost model. - - This function is used to sometimes discard a longer backward reference - when it is not much longer and the bit cost for encoding it is more - than the saved literals. - - backward_reference_offset MUST be positive. */ -static BROTLI_INLINE score_t BackwardReferenceScore( - size_t copy_length, size_t backward_reference_offset) { - return BROTLI_SCORE_BASE + BROTLI_LITERAL_BYTE_SCORE * (score_t)copy_length - - BROTLI_DISTANCE_BIT_PENALTY * Log2FloorNonZero(backward_reference_offset); -} - -static BROTLI_INLINE score_t BackwardReferenceScoreUsingLastDistance( - size_t copy_length) { - return BROTLI_LITERAL_BYTE_SCORE * (score_t)copy_length + - BROTLI_SCORE_BASE + 15; -} - -static BROTLI_INLINE score_t BackwardReferencePenaltyUsingLastDistance( - size_t distance_short_code) { - return (score_t)39 + ((0x1CA10 >> (distance_short_code & 0xE)) & 0xE); -} - -static BROTLI_INLINE BROTLI_BOOL TestStaticDictionaryItem( - const BrotliEncoderDictionary* dictionary, size_t len, size_t word_idx, - const uint8_t* data, size_t max_length, size_t max_backward, - size_t max_distance, HasherSearchResult* out) { - size_t offset; - size_t matchlen; - size_t backward; - score_t score; - offset = dictionary->words->offsets_by_length[len] + len * word_idx; - if (len > max_length) { - return BROTLI_FALSE; - } - - matchlen = - FindMatchLengthWithLimit(data, &dictionary->words->data[offset], len); - if (matchlen + dictionary->cutoffTransformsCount <= len || matchlen == 0) { - return BROTLI_FALSE; - } - { - size_t cut = len - matchlen; - size_t transform_id = (cut << 2) + - (size_t)((dictionary->cutoffTransforms >> (cut * 6)) & 0x3F); - backward = max_backward + 1 + word_idx + - (transform_id << dictionary->words->size_bits_by_length[len]); - } - if (backward > max_distance) { - return BROTLI_FALSE; - } - score = BackwardReferenceScore(matchlen, backward); - if (score < out->score) { - return BROTLI_FALSE; - } - out->len = matchlen; - out->len_code_delta = (int)len - (int)matchlen; - out->distance = backward; - out->score = score; - return BROTLI_TRUE; -} - -static BROTLI_INLINE void SearchInStaticDictionary( - const BrotliEncoderDictionary* dictionary, - HasherCommon* common, const uint8_t* data, size_t max_length, - size_t max_backward, size_t max_distance, - HasherSearchResult* out, BROTLI_BOOL shallow) { - size_t key; - size_t i; - if (common->dict_num_matches < (common->dict_num_lookups >> 7)) { - return; - } - key = Hash14(data) << 1; - for (i = 0; i < (shallow ? 1u : 2u); ++i, ++key) { - common->dict_num_lookups++; - if (dictionary->hash_table_lengths[key] != 0) { - BROTLI_BOOL item_matches = TestStaticDictionaryItem( - dictionary, dictionary->hash_table_lengths[key], - dictionary->hash_table_words[key], data, - max_length, max_backward, max_distance, out); - if (item_matches) { - common->dict_num_matches++; - } - } - } -} - -typedef struct BackwardMatch { - uint32_t distance; - uint32_t length_and_code; -} BackwardMatch; - -static BROTLI_INLINE void InitBackwardMatch(BackwardMatch* self, - size_t dist, size_t len) { - self->distance = (uint32_t)dist; - self->length_and_code = (uint32_t)(len << 5); -} - -static BROTLI_INLINE void InitDictionaryBackwardMatch(BackwardMatch* self, - size_t dist, size_t len, size_t len_code) { - self->distance = (uint32_t)dist; - self->length_and_code = - (uint32_t)((len << 5) | (len == len_code ? 0 : len_code)); -} - -static BROTLI_INLINE size_t BackwardMatchLength(const BackwardMatch* self) { - return self->length_and_code >> 5; -} - -static BROTLI_INLINE size_t BackwardMatchLengthCode(const BackwardMatch* self) { - size_t code = self->length_and_code & 31; - return code ? code : BackwardMatchLength(self); -} - -#define EXPAND_CAT(a, b) CAT(a, b) -#define CAT(a, b) a ## b -#define FN(X) EXPAND_CAT(X, HASHER()) - -#define HASHER() H10 -#define BUCKET_BITS 17 -#define MAX_TREE_SEARCH_DEPTH 64 -#define MAX_TREE_COMP_LENGTH 128 -#include "hash_to_binary_tree_inc.h" /* NOLINT(build/include) */ -#undef MAX_TREE_SEARCH_DEPTH -#undef MAX_TREE_COMP_LENGTH -#undef BUCKET_BITS -#undef HASHER -/* MAX_NUM_MATCHES == 64 + MAX_TREE_SEARCH_DEPTH */ -#define MAX_NUM_MATCHES_H10 128 - -/* For BUCKET_SWEEP_BITS == 0, enabling the dictionary lookup makes compression - a little faster (0.5% - 1%) and it compresses 0.15% better on small text - and HTML inputs. */ - -#define HASHER() H2 -#define BUCKET_BITS 16 -#define BUCKET_SWEEP_BITS 0 -#define HASH_LEN 5 -#define USE_DICTIONARY 1 -#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ -#undef BUCKET_SWEEP_BITS -#undef USE_DICTIONARY -#undef HASHER - -#define HASHER() H3 -#define BUCKET_SWEEP_BITS 1 -#define USE_DICTIONARY 0 -#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ -#undef USE_DICTIONARY -#undef BUCKET_SWEEP_BITS -#undef BUCKET_BITS -#undef HASHER - -#define HASHER() H4 -#define BUCKET_BITS 17 -#define BUCKET_SWEEP_BITS 2 -#define USE_DICTIONARY 1 -#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ -#undef USE_DICTIONARY -#undef HASH_LEN -#undef BUCKET_SWEEP_BITS -#undef BUCKET_BITS -#undef HASHER - -#define HASHER() H5 -#include "hash_longest_match_inc.h" /* NOLINT(build/include) */ -#undef HASHER - -#define HASHER() H6 -#include "hash_longest_match64_inc.h" /* NOLINT(build/include) */ -#undef HASHER - -#define BUCKET_BITS 15 - -#define NUM_LAST_DISTANCES_TO_CHECK 4 -#define NUM_BANKS 1 -#define BANK_BITS 16 -#define HASHER() H40 -#include "hash_forgetful_chain_inc.h" /* NOLINT(build/include) */ -#undef HASHER -#undef NUM_LAST_DISTANCES_TO_CHECK - -#define NUM_LAST_DISTANCES_TO_CHECK 10 -#define HASHER() H41 -#include "hash_forgetful_chain_inc.h" /* NOLINT(build/include) */ -#undef HASHER -#undef NUM_LAST_DISTANCES_TO_CHECK -#undef NUM_BANKS -#undef BANK_BITS - -#define NUM_LAST_DISTANCES_TO_CHECK 16 -#define NUM_BANKS 512 -#define BANK_BITS 9 -#define HASHER() H42 -#include "hash_forgetful_chain_inc.h" /* NOLINT(build/include) */ -#undef HASHER -#undef NUM_LAST_DISTANCES_TO_CHECK -#undef NUM_BANKS -#undef BANK_BITS - -#undef BUCKET_BITS - -#define HASHER() H54 -#define BUCKET_BITS 20 -#define BUCKET_SWEEP_BITS 2 -#define HASH_LEN 7 -#define USE_DICTIONARY 0 -#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ -#undef USE_DICTIONARY -#undef HASH_LEN -#undef BUCKET_SWEEP_BITS -#undef BUCKET_BITS -#undef HASHER - -/* fast large window hashers */ - -#define HASHER() HROLLING_FAST -#define CHUNKLEN 32 -#define JUMP 4 -#define NUMBUCKETS 16777216 -#define MASK ((NUMBUCKETS * 64) - 1) -#include "hash_rolling_inc.h" /* NOLINT(build/include) */ -#undef JUMP -#undef HASHER - - -#define HASHER() HROLLING -#define JUMP 1 -#include "hash_rolling_inc.h" /* NOLINT(build/include) */ -#undef MASK -#undef NUMBUCKETS -#undef JUMP -#undef CHUNKLEN -#undef HASHER - -#define HASHER() H35 -#define HASHER_A H3 -#define HASHER_B HROLLING_FAST -#include "hash_composite_inc.h" /* NOLINT(build/include) */ -#undef HASHER_A -#undef HASHER_B -#undef HASHER - -#define HASHER() H55 -#define HASHER_A H54 -#define HASHER_B HROLLING_FAST -#include "hash_composite_inc.h" /* NOLINT(build/include) */ -#undef HASHER_A -#undef HASHER_B -#undef HASHER - -#define HASHER() H65 -#define HASHER_A H6 -#define HASHER_B HROLLING -#include "hash_composite_inc.h" /* NOLINT(build/include) */ -#undef HASHER_A -#undef HASHER_B -#undef HASHER - -#undef FN -#undef CAT -#undef EXPAND_CAT - -#define FOR_SIMPLE_HASHERS(H) H(2) H(3) H(4) H(5) H(6) H(40) H(41) H(42) H(54) -#define FOR_COMPOSITE_HASHERS(H) H(35) H(55) H(65) -#define FOR_GENERIC_HASHERS(H) FOR_SIMPLE_HASHERS(H) FOR_COMPOSITE_HASHERS(H) -#define FOR_ALL_HASHERS(H) FOR_GENERIC_HASHERS(H) H(10) - -typedef struct { - HasherCommon common; - - union { -#define MEMBER_(N) \ - H ## N _H ## N; - FOR_ALL_HASHERS(MEMBER_) -#undef MEMBER_ - } privat; -} Hasher; - -/* MUST be invoked before any other method. */ -static BROTLI_INLINE void HasherInit(Hasher* hasher) { - hasher->common.is_setup_ = BROTLI_FALSE; - hasher->common.extra[0] = NULL; - hasher->common.extra[1] = NULL; - hasher->common.extra[2] = NULL; - hasher->common.extra[3] = NULL; -} - -static BROTLI_INLINE void DestroyHasher(MemoryManager* m, Hasher* hasher) { - if (hasher->common.extra[0] != NULL) BROTLI_FREE(m, hasher->common.extra[0]); - if (hasher->common.extra[1] != NULL) BROTLI_FREE(m, hasher->common.extra[1]); - if (hasher->common.extra[2] != NULL) BROTLI_FREE(m, hasher->common.extra[2]); - if (hasher->common.extra[3] != NULL) BROTLI_FREE(m, hasher->common.extra[3]); -} - -static BROTLI_INLINE void HasherReset(Hasher* hasher) { - hasher->common.is_prepared_ = BROTLI_FALSE; -} - -static BROTLI_INLINE void HasherSize(const BrotliEncoderParams* params, - BROTLI_BOOL one_shot, const size_t input_size, size_t* alloc_size) { - switch (params->hasher.type) { -#define SIZE_(N) \ - case N: \ - HashMemAllocInBytesH ## N(params, one_shot, input_size, alloc_size); \ - break; - FOR_ALL_HASHERS(SIZE_) -#undef SIZE_ - default: - break; - } -} - -static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher, - BrotliEncoderParams* params, const uint8_t* data, size_t position, - size_t input_size, BROTLI_BOOL is_last) { - BROTLI_BOOL one_shot = (position == 0 && is_last); - if (!hasher->common.is_setup_) { - size_t alloc_size[4] = {0}; - size_t i; - ChooseHasher(params, ¶ms->hasher); - hasher->common.params = params->hasher; - hasher->common.dict_num_lookups = 0; - hasher->common.dict_num_matches = 0; - HasherSize(params, one_shot, input_size, alloc_size); - for (i = 0; i < 4; ++i) { - if (alloc_size[i] == 0) continue; - hasher->common.extra[i] = BROTLI_ALLOC(m, uint8_t, alloc_size[i]); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra[i])) return; - } - switch (hasher->common.params.type) { -#define INITIALIZE_(N) \ - case N: \ - InitializeH ## N(&hasher->common, \ - &hasher->privat._H ## N, params); \ - break; - FOR_ALL_HASHERS(INITIALIZE_); -#undef INITIALIZE_ - default: - break; - } - HasherReset(hasher); - hasher->common.is_setup_ = BROTLI_TRUE; - } - - if (!hasher->common.is_prepared_) { - switch (hasher->common.params.type) { -#define PREPARE_(N) \ - case N: \ - PrepareH ## N( \ - &hasher->privat._H ## N, \ - one_shot, input_size, data); \ - break; - FOR_ALL_HASHERS(PREPARE_) -#undef PREPARE_ - default: break; - } - hasher->common.is_prepared_ = BROTLI_TRUE; - } -} - -static BROTLI_INLINE void InitOrStitchToPreviousBlock( - MemoryManager* m, Hasher* hasher, const uint8_t* data, size_t mask, - BrotliEncoderParams* params, size_t position, size_t input_size, - BROTLI_BOOL is_last) { - HasherSetup(m, hasher, params, data, position, input_size, is_last); - if (BROTLI_IS_OOM(m)) return; - switch (hasher->common.params.type) { -#define INIT_(N) \ - case N: \ - StitchToPreviousBlockH ## N( \ - &hasher->privat._H ## N, \ - input_size, position, data, mask); \ - break; - FOR_ALL_HASHERS(INIT_) -#undef INIT_ - default: break; - } -} - -/* NB: when seamless dictionary-ring-buffer copies are implemented, don't forget - to add proper guards for non-zero-BROTLI_PARAM_STREAM_OFFSET. */ -static BROTLI_INLINE void FindCompoundDictionaryMatch( - const PreparedDictionary* self, const uint8_t* BROTLI_RESTRICT data, - const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache, - const size_t cur_ix, const size_t max_length, const size_t distance_offset, - const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) { - const uint32_t source_size = self->source_size; - const size_t boundary = distance_offset - source_size; - const uint32_t hash_bits = self->hash_bits; - const uint32_t bucket_bits = self->bucket_bits; - const uint32_t slot_bits = self->slot_bits; - - const uint32_t hash_shift = 64u - bucket_bits; - const uint32_t slot_mask = (~((uint32_t)0U)) >> (32 - slot_bits); - const uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits); - - const uint32_t* slot_offsets = (uint32_t*)(&self[1]); - const uint16_t* heads = (uint16_t*)(&slot_offsets[1u << slot_bits]); - const uint32_t* items = (uint32_t*)(&heads[1u << bucket_bits]); - const uint8_t* source = NULL; - - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - score_t best_score = out->score; - size_t best_len = out->len; - size_t i; - const uint64_t h = - (BROTLI_UNALIGNED_LOAD64LE(&data[cur_ix_masked]) & hash_mask) * - kPreparedDictionaryHashMul64Long; - const uint32_t key = (uint32_t)(h >> hash_shift); - const uint32_t slot = key & slot_mask; - const uint32_t head = heads[key]; - const uint32_t* BROTLI_RESTRICT chain = &items[slot_offsets[slot] + head]; - uint32_t item = (head == 0xFFFF) ? 1 : 0; - - const void* tail = (void*)&items[self->num_items]; - if (self->magic == kPreparedDictionaryMagic) { - source = (const uint8_t*)tail; - } else { - /* kLeanPreparedDictionaryMagic */ - source = (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail); - } - - for (i = 0; i < 4; ++i) { - const size_t distance = (size_t)distance_cache[i]; - size_t offset; - size_t limit; - size_t len; - if (distance <= boundary || distance > distance_offset) continue; - offset = distance_offset - distance; - limit = source_size - offset; - limit = limit > max_length ? max_length : limit; - len = FindMatchLengthWithLimit(&source[offset], &data[cur_ix_masked], - limit); - if (len >= 2) { - score_t score = BackwardReferenceScoreUsingLastDistance(len); - if (best_score < score) { - if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); - if (best_score < score) { - best_score = score; - if (len > best_len) best_len = len; - out->len = len; - out->len_code_delta = 0; - out->distance = distance; - out->score = best_score; - } - } - } - } - while (item == 0) { - size_t offset; - size_t distance; - size_t limit; - item = *chain; - chain++; - offset = item & 0x7FFFFFFF; - item &= 0x80000000; - distance = distance_offset - offset; - limit = source_size - offset; - limit = (limit > max_length) ? max_length : limit; - if (distance > max_distance) continue; - if (cur_ix_masked + best_len > ring_buffer_mask || - best_len >= limit || - data[cur_ix_masked + best_len] != source[offset + best_len]) { - continue; - } - { - const size_t len = FindMatchLengthWithLimit(&source[offset], - &data[cur_ix_masked], - limit); - if (len >= 4) { - score_t score = BackwardReferenceScore(len, distance); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->len_code_delta = 0; - out->distance = distance; - out->score = best_score; - } - } - } - } -} - -/* NB: when seamless dictionary-ring-buffer copies are implemented, don't forget - to add proper guards for non-zero-BROTLI_PARAM_STREAM_OFFSET. */ -static BROTLI_INLINE size_t FindAllCompoundDictionaryMatches( - const PreparedDictionary* self, const uint8_t* BROTLI_RESTRICT data, - const size_t ring_buffer_mask, const size_t cur_ix, const size_t min_length, - const size_t max_length, const size_t distance_offset, - const size_t max_distance, BackwardMatch* matches, size_t match_limit) { - const uint32_t source_size = self->source_size; - const uint32_t hash_bits = self->hash_bits; - const uint32_t bucket_bits = self->bucket_bits; - const uint32_t slot_bits = self->slot_bits; - - const uint32_t hash_shift = 64u - bucket_bits; - const uint32_t slot_mask = (~((uint32_t)0U)) >> (32 - slot_bits); - const uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits); - - const uint32_t* slot_offsets = (uint32_t*)(&self[1]); - const uint16_t* heads = (uint16_t*)(&slot_offsets[1u << slot_bits]); - const uint32_t* items = (uint32_t*)(&heads[1u << bucket_bits]); - const uint8_t* source = NULL; - - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - size_t best_len = min_length; - const uint64_t h = - (BROTLI_UNALIGNED_LOAD64LE(&data[cur_ix_masked]) & hash_mask) * - kPreparedDictionaryHashMul64Long; - const uint32_t key = (uint32_t)(h >> hash_shift); - const uint32_t slot = key & slot_mask; - const uint32_t head = heads[key]; - const uint32_t* BROTLI_RESTRICT chain = &items[slot_offsets[slot] + head]; - uint32_t item = (head == 0xFFFF) ? 1 : 0; - size_t found = 0; - - const void* tail = (void*)&items[self->num_items]; - if (self->magic == kPreparedDictionaryMagic) { - source = (const uint8_t*)tail; - } else { - /* kLeanPreparedDictionaryMagic */ - source = (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail); - } - - while (item == 0) { - size_t offset; - size_t distance; - size_t limit; - size_t len; - item = *chain; - chain++; - offset = item & 0x7FFFFFFF; - item &= 0x80000000; - distance = distance_offset - offset; - limit = source_size - offset; - limit = (limit > max_length) ? max_length : limit; - if (distance > max_distance) continue; - if (cur_ix_masked + best_len > ring_buffer_mask || - best_len >= limit || - data[cur_ix_masked + best_len] != source[offset + best_len]) { - continue; - } - len = FindMatchLengthWithLimit( - &source[offset], &data[cur_ix_masked], limit); - if (len > best_len) { - best_len = len; - InitBackwardMatch(matches++, distance, len); - found++; - if (found == match_limit) break; - } - } - return found; -} - -static BROTLI_INLINE void LookupCompoundDictionaryMatch( - const CompoundDictionary* addon, const uint8_t* BROTLI_RESTRICT data, - const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache, - const size_t cur_ix, const size_t max_length, - const size_t max_ring_buffer_distance, const size_t max_distance, - HasherSearchResult* sr) { - size_t base_offset = max_ring_buffer_distance + 1 + addon->total_size - 1; - size_t d; - for (d = 0; d < addon->num_chunks; ++d) { - /* Only one prepared dictionary type is currently supported. */ - FindCompoundDictionaryMatch( - (const PreparedDictionary*)addon->chunks[d], data, ring_buffer_mask, - distance_cache, cur_ix, max_length, - base_offset - addon->chunk_offsets[d], max_distance, sr); - } -} - -static BROTLI_INLINE size_t LookupAllCompoundDictionaryMatches( - const CompoundDictionary* addon, const uint8_t* BROTLI_RESTRICT data, - const size_t ring_buffer_mask, const size_t cur_ix, size_t min_length, - const size_t max_length, const size_t max_ring_buffer_distance, - const size_t max_distance, BackwardMatch* matches, - size_t match_limit) { - size_t base_offset = max_ring_buffer_distance + 1 + addon->total_size - 1; - size_t d; - size_t total_found = 0; - for (d = 0; d < addon->num_chunks; ++d) { - /* Only one prepared dictionary type is currently supported. */ - total_found += FindAllCompoundDictionaryMatches( - (const PreparedDictionary*)addon->chunks[d], data, ring_buffer_mask, - cur_ix, min_length, max_length, base_offset - addon->chunk_offsets[d], - max_distance, matches + total_found, match_limit - total_found); - if (total_found == match_limit) break; - if (total_found > 0) { - min_length = BackwardMatchLength(&matches[total_found - 1]); - } - } - return total_found; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_HASH_H_ */ diff --git a/src/deps/brotli/enc/hash_composite_inc.h b/src/deps/brotli/enc/hash_composite_inc.h deleted file mode 100644 index 3923bc72c82ab..0000000000000 --- a/src/deps/brotli/enc/hash_composite_inc.h +++ /dev/null @@ -1,140 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2018 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, HASHER_A, HASHER_B */ - -/* Composite hasher: This hasher allows to combine two other hashers, HASHER_A - and HASHER_B. */ - -#define HashComposite HASHER() - -#define FN_A(X) EXPAND_CAT(X, HASHER_A) -#define FN_B(X) EXPAND_CAT(X, HASHER_B) - -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { - size_t a = FN_A(HashTypeLength)(); - size_t b = FN_B(HashTypeLength)(); - return a > b ? a : b; -} - -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { - size_t a = FN_A(StoreLookahead)(); - size_t b = FN_B(StoreLookahead)(); - return a > b ? a : b; -} - -typedef struct HashComposite { - HASHER_A ha; - HASHER_B hb; - HasherCommon ha_common; - HasherCommon hb_common; - - /* Shortcuts. */ - HasherCommon* common; - - BROTLI_BOOL fresh; - const BrotliEncoderParams* params; -} HashComposite; - -static void FN(Initialize)(HasherCommon* common, - HashComposite* BROTLI_RESTRICT self, const BrotliEncoderParams* params) { - self->common = common; - - self->ha_common = *self->common; - self->hb_common = *self->common; - self->fresh = BROTLI_TRUE; - self->params = params; - /* TODO(lode): Initialize of the hashers is deferred to Prepare (and params - remembered here) because we don't get the one_shot and input_size params - here that are needed to know the memory size of them. Instead provide - those params to all hashers FN(Initialize) */ -} - -static void FN(Prepare)( - HashComposite* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - if (self->fresh) { - self->fresh = BROTLI_FALSE; - self->ha_common.extra[0] = self->common->extra[0]; - self->ha_common.extra[1] = self->common->extra[1]; - self->ha_common.extra[2] = NULL; - self->ha_common.extra[3] = NULL; - self->hb_common.extra[0] = self->common->extra[2]; - self->hb_common.extra[1] = self->common->extra[3]; - self->hb_common.extra[2] = NULL; - self->hb_common.extra[3] = NULL; - - FN_A(Initialize)(&self->ha_common, &self->ha, self->params); - FN_B(Initialize)(&self->hb_common, &self->hb, self->params); - } - FN_A(Prepare)(&self->ha, one_shot, input_size, data); - FN_B(Prepare)(&self->hb, one_shot, input_size, data); -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - size_t alloc_size_a[4] = {0}; - size_t alloc_size_b[4] = {0}; - FN_A(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_a); - FN_B(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_b); - /* Should never happen. */ - if (alloc_size_a[2] != 0 || alloc_size_a[3] != 0) exit(EXIT_FAILURE); - if (alloc_size_b[2] != 0 || alloc_size_b[3] != 0) exit(EXIT_FAILURE); - alloc_size[0] = alloc_size_a[0]; - alloc_size[1] = alloc_size_a[1]; - alloc_size[2] = alloc_size_b[0]; - alloc_size[3] = alloc_size_b[1]; -} - -static BROTLI_INLINE void FN(Store)(HashComposite* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { - FN_A(Store)(&self->ha, data, mask, ix); - FN_B(Store)(&self->hb, data, mask, ix); -} - -static BROTLI_INLINE void FN(StoreRange)( - HashComposite* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, - const size_t mask, const size_t ix_start, - const size_t ix_end) { - FN_A(StoreRange)(&self->ha, data, mask, ix_start, ix_end); - FN_B(StoreRange)(&self->hb, data, mask, ix_start, ix_end); -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashComposite* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, const uint8_t* ringbuffer, - size_t ring_buffer_mask) { - FN_A(StitchToPreviousBlock)(&self->ha, num_bytes, position, - ringbuffer, ring_buffer_mask); - FN_B(StitchToPreviousBlock)(&self->hb, num_bytes, position, - ringbuffer, ring_buffer_mask); -} - -static BROTLI_INLINE void FN(PrepareDistanceCache)( - HashComposite* BROTLI_RESTRICT self, int* BROTLI_RESTRICT distance_cache) { - FN_A(PrepareDistanceCache)(&self->ha, distance_cache); - FN_B(PrepareDistanceCache)(&self->hb, distance_cache); -} - -static BROTLI_INLINE void FN(FindLongestMatch)( - HashComposite* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, - const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, - const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const size_t max_distance, - HasherSearchResult* BROTLI_RESTRICT out) { - FN_A(FindLongestMatch)(&self->ha, dictionary, data, ring_buffer_mask, - distance_cache, cur_ix, max_length, max_backward, dictionary_distance, - max_distance, out); - FN_B(FindLongestMatch)(&self->hb, dictionary, data, ring_buffer_mask, - distance_cache, cur_ix, max_length, max_backward, dictionary_distance, - max_distance, out); -} - -#undef HashComposite diff --git a/src/deps/brotli/enc/hash_forgetful_chain_inc.h b/src/deps/brotli/enc/hash_forgetful_chain_inc.h deleted file mode 100644 index 48e1cdcdf2eb4..0000000000000 --- a/src/deps/brotli/enc/hash_forgetful_chain_inc.h +++ /dev/null @@ -1,295 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, BUCKET_BITS, NUM_BANKS, BANK_BITS, - NUM_LAST_DISTANCES_TO_CHECK */ - -/* A (forgetful) hash table to the data seen by the compressor, to - help create backward references to previous data. - - Hashes are stored in chains which are bucketed to groups. Group of chains - share a storage "bank". When more than "bank size" chain nodes are added, - oldest nodes are replaced; this way several chains may share a tail. */ - -#define HashForgetfulChain HASHER() - -#define BANK_SIZE (1 << BANK_BITS) - -/* Number of hash buckets. */ -#define BUCKET_SIZE (1 << BUCKET_BITS) - -#define CAPPED_CHAINS 0 - -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } - -/* HashBytes is the function that chooses the bucket to place the address in.*/ -static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data) { - const uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return h >> (32 - BUCKET_BITS); -} - -typedef struct FN(Slot) { - uint16_t delta; - uint16_t next; -} FN(Slot); - -typedef struct FN(Bank) { - FN(Slot) slots[BANK_SIZE]; -} FN(Bank); - -typedef struct HashForgetfulChain { - uint16_t free_slot_idx[NUM_BANKS]; /* Up to 1KiB. Move to dynamic? */ - size_t max_hops; - - /* Shortcuts. */ - void* extra[2]; - HasherCommon* common; - - /* --- Dynamic size members --- */ - - /* uint32_t addr[BUCKET_SIZE]; */ - - /* uint16_t head[BUCKET_SIZE]; */ - - /* Truncated hash used for quick rejection of "distance cache" candidates. */ - /* uint8_t tiny_hash[65536];*/ - - /* FN(Bank) banks[NUM_BANKS]; */ -} HashForgetfulChain; - -static uint32_t* FN(Addr)(void* extra) { - return (uint32_t*)extra; -} - -static uint16_t* FN(Head)(void* extra) { - return (uint16_t*)(&FN(Addr)(extra)[BUCKET_SIZE]); -} - -static uint8_t* FN(TinyHash)(void* extra) { - return (uint8_t*)(&FN(Head)(extra)[BUCKET_SIZE]); -} - -static FN(Bank)* FN(Banks)(void* extra) { - return (FN(Bank)*)(extra); -} - -static void FN(Initialize)( - HasherCommon* common, HashForgetfulChain* BROTLI_RESTRICT self, - const BrotliEncoderParams* params) { - self->common = common; - self->extra[0] = common->extra[0]; - self->extra[1] = common->extra[1]; - - self->max_hops = (params->quality > 6 ? 7u : 8u) << (params->quality - 4); -} - -static void FN(Prepare)( - HashForgetfulChain* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]); - uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]); - uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra[0]); - /* Partial preparation is 100 times slower (per socket). */ - size_t partial_prepare_threshold = BUCKET_SIZE >> 6; - if (one_shot && input_size <= partial_prepare_threshold) { - size_t i; - for (i = 0; i < input_size; ++i) { - size_t bucket = FN(HashBytes)(&data[i]); - /* See InitEmpty comment. */ - addr[bucket] = 0xCCCCCCCC; - head[bucket] = 0xCCCC; - } - } else { - /* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position - processed by hasher never reaches 3GB + 64M; this makes all new chains - to be terminated after the first node. */ - memset(addr, 0xCC, sizeof(uint32_t) * BUCKET_SIZE); - memset(head, 0, sizeof(uint16_t) * BUCKET_SIZE); - } - memset(tiny_hash, 0, sizeof(uint8_t) * 65536); - memset(self->free_slot_idx, 0, sizeof(self->free_slot_idx)); -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - BROTLI_UNUSED(params); - BROTLI_UNUSED(one_shot); - BROTLI_UNUSED(input_size); - alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE + - sizeof(uint16_t) * BUCKET_SIZE + sizeof(uint8_t) * 65536; - alloc_size[1] = sizeof(FN(Bank)) * NUM_BANKS; -} - -/* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend - node to corresponding chain; also update tiny_hash for current position. */ -static BROTLI_INLINE void FN(Store)(HashForgetfulChain* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { - uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]); - uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]); - uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra[0]); - FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra[1]); - const size_t key = FN(HashBytes)(&data[ix & mask]); - const size_t bank = key & (NUM_BANKS - 1); - const size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1); - size_t delta = ix - addr[key]; - tiny_hash[(uint16_t)ix] = (uint8_t)key; - if (delta > 0xFFFF) delta = CAPPED_CHAINS ? 0 : 0xFFFF; - banks[bank].slots[idx].delta = (uint16_t)delta; - banks[bank].slots[idx].next = head[key]; - addr[key] = (uint32_t)ix; - head[key] = (uint16_t)idx; -} - -static BROTLI_INLINE void FN(StoreRange)( - HashForgetfulChain* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, - const size_t ix_start, const size_t ix_end) { - size_t i; - for (i = ix_start; i < ix_end; ++i) { - FN(Store)(self, data, mask, i); - } -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashForgetfulChain* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, const uint8_t* ringbuffer, - size_t ring_buffer_mask) { - if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { - /* Prepare the hashes for three last bytes of the last write. - These could not be calculated before, since they require knowledge - of both the previous and the current block. */ - FN(Store)(self, ringbuffer, ring_buffer_mask, position - 3); - FN(Store)(self, ringbuffer, ring_buffer_mask, position - 2); - FN(Store)(self, ringbuffer, ring_buffer_mask, position - 1); - } -} - -static BROTLI_INLINE void FN(PrepareDistanceCache)( - HashForgetfulChain* BROTLI_RESTRICT self, - int* BROTLI_RESTRICT distance_cache) { - BROTLI_UNUSED(self); - PrepareDistanceCache(distance_cache, NUM_LAST_DISTANCES_TO_CHECK); -} - -/* Find a longest backward match of &data[cur_ix] up to the length of - max_length and stores the position cur_ix in the hash table. - - REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache - values; if this method is invoked repeatedly with the same distance - cache values, it is enough to invoke FN(PrepareDistanceCache) once. - - Does not look for matches longer than max_length. - Does not look for matches further away than max_backward. - Writes the best match into |out|. - |out|->score is updated only if a better match is found. */ -static BROTLI_INLINE void FN(FindLongestMatch)( - HashForgetfulChain* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, - const int* BROTLI_RESTRICT distance_cache, - const size_t cur_ix, const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const size_t max_distance, - HasherSearchResult* BROTLI_RESTRICT out) { - uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]); - uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]); - uint8_t* BROTLI_RESTRICT tiny_hashes = FN(TinyHash)(self->extra[0]); - FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra[1]); - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - /* Don't accept a short copy from far away. */ - score_t min_score = out->score; - score_t best_score = out->score; - size_t best_len = out->len; - size_t i; - const size_t key = FN(HashBytes)(&data[cur_ix_masked]); - const uint8_t tiny_hash = (uint8_t)(key); - out->len = 0; - out->len_code_delta = 0; - /* Try last distance first. */ - for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) { - const size_t backward = (size_t)distance_cache[i]; - size_t prev_ix = (cur_ix - backward); - /* For distance code 0 we want to consider 2-byte matches. */ - if (i > 0 && tiny_hashes[(uint16_t)prev_ix] != tiny_hash) continue; - if (prev_ix >= cur_ix || backward > max_backward) { - continue; - } - prev_ix &= ring_buffer_mask; - { - const size_t len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 2) { - score_t score = BackwardReferenceScoreUsingLastDistance(len); - if (best_score < score) { - if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->distance = backward; - out->score = best_score; - } - } - } - } - } - { - const size_t bank = key & (NUM_BANKS - 1); - size_t backward = 0; - size_t hops = self->max_hops; - size_t delta = cur_ix - addr[key]; - size_t slot = head[key]; - while (hops--) { - size_t prev_ix; - size_t last = slot; - backward += delta; - if (backward > max_backward || (CAPPED_CHAINS && !delta)) break; - prev_ix = (cur_ix - backward) & ring_buffer_mask; - slot = banks[bank].slots[last].next; - delta = banks[bank].slots[last].delta; - if (cur_ix_masked + best_len > ring_buffer_mask || - prev_ix + best_len > ring_buffer_mask || - data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { - continue; - } - { - const size_t len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 4) { - /* Comparing for >= 3 does not change the semantics, but just saves - for a few unnecessary binary logarithms in backward reference - score, since we are not interested in such short matches. */ - score_t score = BackwardReferenceScore(len, backward); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->distance = backward; - out->score = best_score; - } - } - } - } - FN(Store)(self, data, ring_buffer_mask, cur_ix); - } - if (out->score == min_score) { - SearchInStaticDictionary(dictionary, - self->common, &data[cur_ix_masked], max_length, dictionary_distance, - max_distance, out, BROTLI_FALSE); - } -} - -#undef BANK_SIZE -#undef BUCKET_SIZE -#undef CAPPED_CHAINS - -#undef HashForgetfulChain diff --git a/src/deps/brotli/enc/hash_longest_match64_inc.h b/src/deps/brotli/enc/hash_longest_match64_inc.h deleted file mode 100644 index e48fc61554423..0000000000000 --- a/src/deps/brotli/enc/hash_longest_match64_inc.h +++ /dev/null @@ -1,262 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN */ - -/* A (forgetful) hash table to the data seen by the compressor, to - help create backward references to previous data. - - This is a hash map of fixed size (bucket_size_) to a ring buffer of - fixed size (block_size_). The ring buffer contains the last block_size_ - index positions of the given hash key in the compressed data. */ - -#define HashLongestMatch HASHER() - -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 8; } -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; } - -/* HashBytes is the function that chooses the bucket to place the address in. */ -static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data, - uint64_t hash_mul) { - const uint64_t h = BROTLI_UNALIGNED_LOAD64LE(data) * hash_mul; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return (size_t)(h >> (64 - 15)); -} - -typedef struct HashLongestMatch { - /* Number of hash buckets. */ - size_t bucket_size_; - /* Only block_size_ newest backward references are kept, - and the older are forgotten. */ - size_t block_size_; - /* Hash multiplier tuned to match length. */ - uint64_t hash_mul_; - /* Mask for accessing entries in a block (in a ring-buffer manner). */ - uint32_t block_mask_; - - int block_bits_; - int num_last_distances_to_check_; - - /* Shortcuts. */ - HasherCommon* common_; - - /* --- Dynamic size members --- */ - - /* Number of entries in a particular bucket. */ - uint16_t* num_; /* uint16_t[bucket_size]; */ - - /* Buckets containing block_size_ of backward references. */ - uint32_t* buckets_; /* uint32_t[bucket_size * block_size]; */ -} HashLongestMatch; - -static void FN(Initialize)( - HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self, - const BrotliEncoderParams* params) { - self->common_ = common; - - BROTLI_UNUSED(params); - self->hash_mul_ = kHashMul64 << (64 - 5 * 8); - BROTLI_DCHECK(common->params.bucket_bits == 15); - self->bucket_size_ = (size_t)1 << common->params.bucket_bits; - self->block_bits_ = common->params.block_bits; - self->block_size_ = (size_t)1 << common->params.block_bits; - self->block_mask_ = (uint32_t)(self->block_size_ - 1); - self->num_last_distances_to_check_ = - common->params.num_last_distances_to_check; - self->num_ = (uint16_t*)common->extra[0]; - self->buckets_ = (uint32_t*)common->extra[1]; -} - -static void FN(Prepare)( - HashLongestMatch* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - uint16_t* BROTLI_RESTRICT num = self->num_; - /* Partial preparation is 100 times slower (per socket). */ - size_t partial_prepare_threshold = self->bucket_size_ >> 6; - if (one_shot && input_size <= partial_prepare_threshold) { - size_t i; - for (i = 0; i < input_size; ++i) { - const size_t key = FN(HashBytes)(&data[i], self->hash_mul_); - num[key] = 0; - } - } else { - memset(num, 0, self->bucket_size_ * sizeof(num[0])); - } -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - size_t bucket_size = (size_t)1 << params->hasher.bucket_bits; - size_t block_size = (size_t)1 << params->hasher.block_bits; - BROTLI_UNUSED(one_shot); - BROTLI_UNUSED(input_size); - alloc_size[0] = sizeof(uint16_t) * bucket_size; - alloc_size[1] = sizeof(uint32_t) * bucket_size * block_size; -} - -/* Look at 4 bytes at &data[ix & mask]. - Compute a hash from these, and store the value of ix at that position. */ -static BROTLI_INLINE void FN(Store)( - HashLongestMatch* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, - const size_t mask, const size_t ix) { - uint16_t* BROTLI_RESTRICT num = self->num_; - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - const size_t key = FN(HashBytes)(&data[ix & mask], self->hash_mul_); - const size_t minor_ix = num[key] & self->block_mask_; - const size_t offset = minor_ix + (key << self->block_bits_); - ++num[key]; - buckets[offset] = (uint32_t)ix; -} - -static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, - const size_t ix_start, const size_t ix_end) { - size_t i; - for (i = ix_start; i < ix_end; ++i) { - FN(Store)(self, data, mask, i); - } -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashLongestMatch* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, const uint8_t* ringbuffer, - size_t ringbuffer_mask) { - if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { - /* Prepare the hashes for three last bytes of the last write. - These could not be calculated before, since they require knowledge - of both the previous and the current block. */ - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); - } -} - -static BROTLI_INLINE void FN(PrepareDistanceCache)( - HashLongestMatch* BROTLI_RESTRICT self, - int* BROTLI_RESTRICT distance_cache) { - PrepareDistanceCache(distance_cache, self->num_last_distances_to_check_); -} - -/* Find a longest backward match of &data[cur_ix] up to the length of - max_length and stores the position cur_ix in the hash table. - - REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache - values; if this method is invoked repeatedly with the same distance - cache values, it is enough to invoke FN(PrepareDistanceCache) once. - - Does not look for matches longer than max_length. - Does not look for matches further away than max_backward. - Writes the best match into |out|. - |out|->score is updated only if a better match is found. */ -static BROTLI_INLINE void FN(FindLongestMatch)( - HashLongestMatch* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, - const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, - const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const size_t max_distance, - HasherSearchResult* BROTLI_RESTRICT out) { - uint16_t* BROTLI_RESTRICT num = self->num_; - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - /* Don't accept a short copy from far away. */ - score_t min_score = out->score; - score_t best_score = out->score; - size_t best_len = out->len; - size_t i; - out->len = 0; - out->len_code_delta = 0; - /* Try last distance first. */ - for (i = 0; i < (size_t)self->num_last_distances_to_check_; ++i) { - const size_t backward = (size_t)distance_cache[i]; - size_t prev_ix = (size_t)(cur_ix - backward); - if (prev_ix >= cur_ix) { - continue; - } - if (BROTLI_PREDICT_FALSE(backward > max_backward)) { - continue; - } - prev_ix &= ring_buffer_mask; - - if (cur_ix_masked + best_len > ring_buffer_mask || - prev_ix + best_len > ring_buffer_mask || - data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { - continue; - } - { - const size_t len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 3 || (len == 2 && i < 2)) { - /* Comparing for >= 2 does not change the semantics, but just saves for - a few unnecessary binary logarithms in backward reference score, - since we are not interested in such short matches. */ - score_t score = BackwardReferenceScoreUsingLastDistance(len); - if (best_score < score) { - if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->distance = backward; - out->score = best_score; - } - } - } - } - } - { - const size_t key = FN(HashBytes)(&data[cur_ix_masked], self->hash_mul_); - uint32_t* BROTLI_RESTRICT bucket = &buckets[key << self->block_bits_]; - const size_t down = - (num[key] > self->block_size_) ? - (num[key] - self->block_size_) : 0u; - const uint32_t first4 = BrotliUnalignedRead32(data + cur_ix_masked); - const size_t max_length_m4 = max_length - 4; - i = num[key]; - for (; i > down;) { - size_t prev_ix = bucket[--i & self->block_mask_]; - uint32_t current4; - const size_t backward = cur_ix - prev_ix; - if (BROTLI_PREDICT_FALSE(backward > max_backward)) { - break; - } - prev_ix &= ring_buffer_mask; - if (cur_ix_masked + best_len > ring_buffer_mask || - prev_ix + best_len > ring_buffer_mask || - data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { - continue; - } - current4 = BrotliUnalignedRead32(data + prev_ix); - if (first4 != current4) continue; - { - const size_t len = FindMatchLengthWithLimit(&data[prev_ix + 4], - &data[cur_ix_masked + 4], - max_length_m4) + 4; - const score_t score = BackwardReferenceScore(len, backward); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->distance = backward; - out->score = best_score; - } - } - } - bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix; - ++num[key]; - } - if (min_score == out->score) { - SearchInStaticDictionary(dictionary, - self->common_, &data[cur_ix_masked], max_length, dictionary_distance, - max_distance, out, BROTLI_FALSE); - } -} - -#undef HashLongestMatch diff --git a/src/deps/brotli/enc/hash_longest_match_inc.h b/src/deps/brotli/enc/hash_longest_match_inc.h deleted file mode 100644 index 788e9ef993756..0000000000000 --- a/src/deps/brotli/enc/hash_longest_match_inc.h +++ /dev/null @@ -1,258 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN */ - -/* A (forgetful) hash table to the data seen by the compressor, to - help create backward references to previous data. - - This is a hash map of fixed size (bucket_size_) to a ring buffer of - fixed size (block_size_). The ring buffer contains the last block_size_ - index positions of the given hash key in the compressed data. */ - -#define HashLongestMatch HASHER() - -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } - -/* HashBytes is the function that chooses the bucket to place the address in. */ -static uint32_t FN(HashBytes)( - const uint8_t* BROTLI_RESTRICT data, const int shift) { - uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return (uint32_t)(h >> shift); -} - -typedef struct HashLongestMatch { - /* Number of hash buckets. */ - size_t bucket_size_; - /* Only block_size_ newest backward references are kept, - and the older are forgotten. */ - size_t block_size_; - /* Left-shift for computing hash bucket index from hash value. */ - int hash_shift_; - /* Mask for accessing entries in a block (in a ring-buffer manner). */ - uint32_t block_mask_; - - int block_bits_; - int num_last_distances_to_check_; - - /* Shortcuts. */ - HasherCommon* common_; - - /* --- Dynamic size members --- */ - - /* Number of entries in a particular bucket. */ - uint16_t* num_; /* uint16_t[bucket_size]; */ - - /* Buckets containing block_size_ of backward references. */ - uint32_t* buckets_; /* uint32_t[bucket_size * block_size]; */ -} HashLongestMatch; - -static void FN(Initialize)( - HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self, - const BrotliEncoderParams* params) { - self->common_ = common; - - BROTLI_UNUSED(params); - self->hash_shift_ = 32 - common->params.bucket_bits; - self->bucket_size_ = (size_t)1 << common->params.bucket_bits; - self->block_size_ = (size_t)1 << common->params.block_bits; - self->block_mask_ = (uint32_t)(self->block_size_ - 1); - self->num_ = (uint16_t*)common->extra[0]; - self->buckets_ = (uint32_t*)common->extra[1]; - self->block_bits_ = common->params.block_bits; - self->num_last_distances_to_check_ = - common->params.num_last_distances_to_check; -} - -static void FN(Prepare)( - HashLongestMatch* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - uint16_t* BROTLI_RESTRICT num = self->num_; - /* Partial preparation is 100 times slower (per socket). */ - size_t partial_prepare_threshold = self->bucket_size_ >> 6; - if (one_shot && input_size <= partial_prepare_threshold) { - size_t i; - for (i = 0; i < input_size; ++i) { - const uint32_t key = FN(HashBytes)(&data[i], self->hash_shift_); - num[key] = 0; - } - } else { - memset(num, 0, self->bucket_size_ * sizeof(num[0])); - } -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - size_t bucket_size = (size_t)1 << params->hasher.bucket_bits; - size_t block_size = (size_t)1 << params->hasher.block_bits; - BROTLI_UNUSED(one_shot); - BROTLI_UNUSED(input_size); - alloc_size[0] = sizeof(uint16_t) * bucket_size; - alloc_size[1] = sizeof(uint32_t) * bucket_size * block_size; -} - -/* Look at 4 bytes at &data[ix & mask]. - Compute a hash from these, and store the value of ix at that position. */ -static BROTLI_INLINE void FN(Store)( - HashLongestMatch* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, - const size_t mask, const size_t ix) { - const uint32_t key = FN(HashBytes)(&data[ix & mask], self->hash_shift_); - const size_t minor_ix = self->num_[key] & self->block_mask_; - const size_t offset = minor_ix + (key << self->block_bits_); - self->buckets_[offset] = (uint32_t)ix; - ++self->num_[key]; -} - -static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, - const size_t ix_start, const size_t ix_end) { - size_t i; - for (i = ix_start; i < ix_end; ++i) { - FN(Store)(self, data, mask, i); - } -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashLongestMatch* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, const uint8_t* ringbuffer, - size_t ringbuffer_mask) { - if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { - /* Prepare the hashes for three last bytes of the last write. - These could not be calculated before, since they require knowledge - of both the previous and the current block. */ - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); - } -} - -static BROTLI_INLINE void FN(PrepareDistanceCache)( - HashLongestMatch* BROTLI_RESTRICT self, - int* BROTLI_RESTRICT distance_cache) { - PrepareDistanceCache(distance_cache, self->num_last_distances_to_check_); -} - -/* Find a longest backward match of &data[cur_ix] up to the length of - max_length and stores the position cur_ix in the hash table. - - REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache - values; if this method is invoked repeatedly with the same distance - cache values, it is enough to invoke FN(PrepareDistanceCache) once. - - Does not look for matches longer than max_length. - Does not look for matches further away than max_backward. - Writes the best match into |out|. - |out|->score is updated only if a better match is found. */ -static BROTLI_INLINE void FN(FindLongestMatch)( - HashLongestMatch* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, - const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, - const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const size_t max_distance, - HasherSearchResult* BROTLI_RESTRICT out) { - uint16_t* BROTLI_RESTRICT num = self->num_; - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - /* Don't accept a short copy from far away. */ - score_t min_score = out->score; - score_t best_score = out->score; - size_t best_len = out->len; - size_t i; - out->len = 0; - out->len_code_delta = 0; - /* Try last distance first. */ - for (i = 0; i < (size_t)self->num_last_distances_to_check_; ++i) { - const size_t backward = (size_t)distance_cache[i]; - size_t prev_ix = (size_t)(cur_ix - backward); - if (prev_ix >= cur_ix) { - continue; - } - if (BROTLI_PREDICT_FALSE(backward > max_backward)) { - continue; - } - prev_ix &= ring_buffer_mask; - - if (cur_ix_masked + best_len > ring_buffer_mask || - prev_ix + best_len > ring_buffer_mask || - data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { - continue; - } - { - const size_t len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 3 || (len == 2 && i < 2)) { - /* Comparing for >= 2 does not change the semantics, but just saves for - a few unnecessary binary logarithms in backward reference score, - since we are not interested in such short matches. */ - score_t score = BackwardReferenceScoreUsingLastDistance(len); - if (best_score < score) { - if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->distance = backward; - out->score = best_score; - } - } - } - } - } - { - const uint32_t key = - FN(HashBytes)(&data[cur_ix_masked], self->hash_shift_); - uint32_t* BROTLI_RESTRICT bucket = &buckets[key << self->block_bits_]; - const size_t down = - (num[key] > self->block_size_) ? (num[key] - self->block_size_) : 0u; - for (i = num[key]; i > down;) { - size_t prev_ix = bucket[--i & self->block_mask_]; - const size_t backward = cur_ix - prev_ix; - if (BROTLI_PREDICT_FALSE(backward > max_backward)) { - break; - } - prev_ix &= ring_buffer_mask; - if (cur_ix_masked + best_len > ring_buffer_mask || - prev_ix + best_len > ring_buffer_mask || - data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { - continue; - } - { - const size_t len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 4) { - /* Comparing for >= 3 does not change the semantics, but just saves - for a few unnecessary binary logarithms in backward reference - score, since we are not interested in such short matches. */ - score_t score = BackwardReferenceScore(len, backward); - if (best_score < score) { - best_score = score; - best_len = len; - out->len = best_len; - out->distance = backward; - out->score = best_score; - } - } - } - } - bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix; - ++num[key]; - } - if (min_score == out->score) { - SearchInStaticDictionary(dictionary, - self->common_, &data[cur_ix_masked], max_length, dictionary_distance, - max_distance, out, BROTLI_FALSE); - } -} - -#undef HashLongestMatch diff --git a/src/deps/brotli/enc/hash_longest_match_quickly_inc.h b/src/deps/brotli/enc/hash_longest_match_quickly_inc.h deleted file mode 100644 index 54397ef891707..0000000000000 --- a/src/deps/brotli/enc/hash_longest_match_quickly_inc.h +++ /dev/null @@ -1,266 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, BUCKET_BITS, BUCKET_SWEEP_BITS, HASH_LEN, - USE_DICTIONARY - */ - -#define HashLongestMatchQuickly HASHER() - -#define BUCKET_SIZE (1 << BUCKET_BITS) -#define BUCKET_MASK (BUCKET_SIZE - 1) -#define BUCKET_SWEEP (1 << BUCKET_SWEEP_BITS) -#define BUCKET_SWEEP_MASK ((BUCKET_SWEEP - 1) << 3) - -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 8; } -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; } - -/* HashBytes is the function that chooses the bucket to place - the address in. The HashLongestMatch and HashLongestMatchQuickly - classes have separate, different implementations of hashing. */ -static uint32_t FN(HashBytes)(const uint8_t* data) { - const uint64_t h = ((BROTLI_UNALIGNED_LOAD64LE(data) << (64 - 8 * HASH_LEN)) * - kHashMul64); - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return (uint32_t)(h >> (64 - BUCKET_BITS)); -} - -/* A (forgetful) hash table to the data seen by the compressor, to - help create backward references to previous data. - - This is a hash map of fixed size (BUCKET_SIZE). */ -typedef struct HashLongestMatchQuickly { - /* Shortcuts. */ - HasherCommon* common; - - /* --- Dynamic size members --- */ - - uint32_t* buckets_; /* uint32_t[BUCKET_SIZE]; */ -} HashLongestMatchQuickly; - -static void FN(Initialize)( - HasherCommon* common, HashLongestMatchQuickly* BROTLI_RESTRICT self, - const BrotliEncoderParams* params) { - self->common = common; - - BROTLI_UNUSED(params); - self->buckets_ = (uint32_t*)common->extra[0]; -} - -static void FN(Prepare)( - HashLongestMatchQuickly* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - /* Partial preparation is 100 times slower (per socket). */ - size_t partial_prepare_threshold = BUCKET_SIZE >> 5; - if (one_shot && input_size <= partial_prepare_threshold) { - size_t i; - for (i = 0; i < input_size; ++i) { - const uint32_t key = FN(HashBytes)(&data[i]); - if (BUCKET_SWEEP == 1) { - buckets[key] = 0; - } else { - uint32_t j; - for (j = 0; j < BUCKET_SWEEP; ++j) { - buckets[(key + (j << 3)) & BUCKET_MASK] = 0; - } - } - } - } else { - /* It is not strictly necessary to fill this buffer here, but - not filling will make the results of the compression stochastic - (but correct). This is because random data would cause the - system to find accidentally good backward references here and there. */ - memset(buckets, 0, sizeof(uint32_t) * BUCKET_SIZE); - } -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - BROTLI_UNUSED(params); - BROTLI_UNUSED(one_shot); - BROTLI_UNUSED(input_size); - alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE; -} - -/* Look at 5 bytes at &data[ix & mask]. - Compute a hash from these, and store the value somewhere within - [ix .. ix+3]. */ -static BROTLI_INLINE void FN(Store)( - HashLongestMatchQuickly* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { - const uint32_t key = FN(HashBytes)(&data[ix & mask]); - if (BUCKET_SWEEP == 1) { - self->buckets_[key] = (uint32_t)ix; - } else { - /* Wiggle the value with the bucket sweep range. */ - const uint32_t off = ix & BUCKET_SWEEP_MASK; - self->buckets_[(key + off) & BUCKET_MASK] = (uint32_t)ix; - } -} - -static BROTLI_INLINE void FN(StoreRange)( - HashLongestMatchQuickly* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, - const size_t ix_start, const size_t ix_end) { - size_t i; - for (i = ix_start; i < ix_end; ++i) { - FN(Store)(self, data, mask, i); - } -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashLongestMatchQuickly* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, - const uint8_t* ringbuffer, size_t ringbuffer_mask) { - if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { - /* Prepare the hashes for three last bytes of the last write. - These could not be calculated before, since they require knowledge - of both the previous and the current block. */ - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); - FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); - } -} - -static BROTLI_INLINE void FN(PrepareDistanceCache)( - HashLongestMatchQuickly* BROTLI_RESTRICT self, - int* BROTLI_RESTRICT distance_cache) { - BROTLI_UNUSED(self); - BROTLI_UNUSED(distance_cache); -} - -/* Find a longest backward match of &data[cur_ix & ring_buffer_mask] - up to the length of max_length and stores the position cur_ix in the - hash table. - - Does not look for matches longer than max_length. - Does not look for matches further away than max_backward. - Writes the best match into |out|. - |out|->score is updated only if a better match is found. */ -static BROTLI_INLINE void FN(FindLongestMatch)( - HashLongestMatchQuickly* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, - const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache, - const size_t cur_ix, const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const size_t max_distance, - HasherSearchResult* BROTLI_RESTRICT out) { - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - const size_t best_len_in = out->len; - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - int compare_char = data[cur_ix_masked + best_len_in]; - size_t key = FN(HashBytes)(&data[cur_ix_masked]); - size_t key_out; - score_t min_score = out->score; - score_t best_score = out->score; - size_t best_len = best_len_in; - size_t cached_backward = (size_t)distance_cache[0]; - size_t prev_ix = cur_ix - cached_backward; - out->len_code_delta = 0; - if (prev_ix < cur_ix) { - prev_ix &= (uint32_t)ring_buffer_mask; - if (compare_char == data[prev_ix + best_len]) { - const size_t len = FindMatchLengthWithLimit( - &data[prev_ix], &data[cur_ix_masked], max_length); - if (len >= 4) { - const score_t score = BackwardReferenceScoreUsingLastDistance(len); - if (best_score < score) { - out->len = len; - out->distance = cached_backward; - out->score = score; - if (BUCKET_SWEEP == 1) { - buckets[key] = (uint32_t)cur_ix; - return; - } else { - best_len = len; - best_score = score; - compare_char = data[cur_ix_masked + len]; - } - } - } - } - } - if (BUCKET_SWEEP == 1) { - size_t backward; - size_t len; - /* Only one to look for, don't bother to prepare for a loop. */ - prev_ix = buckets[key]; - buckets[key] = (uint32_t)cur_ix; - backward = cur_ix - prev_ix; - prev_ix &= (uint32_t)ring_buffer_mask; - if (compare_char != data[prev_ix + best_len_in]) { - return; - } - if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) { - return; - } - len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 4) { - const score_t score = BackwardReferenceScore(len, backward); - if (best_score < score) { - out->len = len; - out->distance = backward; - out->score = score; - return; - } - } - } else { - size_t keys[BUCKET_SWEEP]; - size_t i; - for (i = 0; i < BUCKET_SWEEP; ++i) { - keys[i] = (key + (i << 3)) & BUCKET_MASK; - } - key_out = keys[(cur_ix & BUCKET_SWEEP_MASK) >> 3]; - for (i = 0; i < BUCKET_SWEEP; ++i) { - size_t len; - size_t backward; - prev_ix = buckets[keys[i]]; - backward = cur_ix - prev_ix; - prev_ix &= (uint32_t)ring_buffer_mask; - if (compare_char != data[prev_ix + best_len]) { - continue; - } - if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) { - continue; - } - len = FindMatchLengthWithLimit(&data[prev_ix], - &data[cur_ix_masked], - max_length); - if (len >= 4) { - const score_t score = BackwardReferenceScore(len, backward); - if (best_score < score) { - best_len = len; - out->len = len; - compare_char = data[cur_ix_masked + len]; - best_score = score; - out->score = score; - out->distance = backward; - } - } - } - } - if (USE_DICTIONARY && min_score == out->score) { - SearchInStaticDictionary(dictionary, - self->common, &data[cur_ix_masked], max_length, dictionary_distance, - max_distance, out, BROTLI_TRUE); - } - if (BUCKET_SWEEP != 1) { - buckets[key_out] = (uint32_t)cur_ix; - } -} - -#undef BUCKET_SWEEP_MASK -#undef BUCKET_SWEEP -#undef BUCKET_MASK -#undef BUCKET_SIZE - -#undef HashLongestMatchQuickly diff --git a/src/deps/brotli/enc/hash_rolling_inc.h b/src/deps/brotli/enc/hash_rolling_inc.h deleted file mode 100644 index 4c7a6b199adbb..0000000000000 --- a/src/deps/brotli/enc/hash_rolling_inc.h +++ /dev/null @@ -1,212 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2018 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, JUMP, NUMBUCKETS, MASK, CHUNKLEN */ -/* NUMBUCKETS / (MASK + 1) = probability of storing and using hash code. */ -/* JUMP = skip bytes for speedup */ - -/* Rolling hash for long distance long string matches. Stores one position - per bucket, bucket key is computed over a long region. */ - -#define HashRolling HASHER() - -static const uint32_t FN(kRollingHashMul32) = 69069; -static const uint32_t FN(kInvalidPos) = 0xffffffff; - -/* This hasher uses a longer forward length, but returning a higher value here - will hurt compression by the main hasher when combined with a composite - hasher. The hasher tests for forward itself instead. */ -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } - -/* Computes a code from a single byte. A lookup table of 256 values could be - used, but simply adding 1 works about as good. */ -static uint32_t FN(HashByte)(uint8_t byte) { - return (uint32_t)byte + 1u; -} - -static uint32_t FN(HashRollingFunctionInitial)(uint32_t state, uint8_t add, - uint32_t factor) { - return (uint32_t)(factor * state + FN(HashByte)(add)); -} - -static uint32_t FN(HashRollingFunction)(uint32_t state, uint8_t add, - uint8_t rem, uint32_t factor, - uint32_t factor_remove) { - return (uint32_t)(factor * state + - FN(HashByte)(add) - factor_remove * FN(HashByte)(rem)); -} - -typedef struct HashRolling { - uint32_t state; - uint32_t* table; - size_t next_ix; - - uint32_t chunk_len; - uint32_t factor; - uint32_t factor_remove; -} HashRolling; - -static void FN(Initialize)( - HasherCommon* common, HashRolling* BROTLI_RESTRICT self, - const BrotliEncoderParams* params) { - size_t i; - self->state = 0; - self->next_ix = 0; - - self->factor = FN(kRollingHashMul32); - - /* Compute the factor of the oldest byte to remove: factor**steps modulo - 0xffffffff (the multiplications rely on 32-bit overflow) */ - self->factor_remove = 1; - for (i = 0; i < CHUNKLEN; i += JUMP) { - self->factor_remove *= self->factor; - } - - self->table = (uint32_t*)common->extra[0]; - for (i = 0; i < NUMBUCKETS; i++) { - self->table[i] = FN(kInvalidPos); - } - - BROTLI_UNUSED(params); -} - -static void FN(Prepare)(HashRolling* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - size_t i; - /* Too small size, cannot use this hasher. */ - if (input_size < CHUNKLEN) return; - self->state = 0; - for (i = 0; i < CHUNKLEN; i += JUMP) { - self->state = FN(HashRollingFunctionInitial)( - self->state, data[i], self->factor); - } - BROTLI_UNUSED(one_shot); -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - BROTLI_UNUSED(params); - BROTLI_UNUSED(one_shot); - BROTLI_UNUSED(input_size); - alloc_size[0] = NUMBUCKETS * sizeof(uint32_t); -} - -static BROTLI_INLINE void FN(Store)(HashRolling* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { - BROTLI_UNUSED(self); - BROTLI_UNUSED(data); - BROTLI_UNUSED(mask); - BROTLI_UNUSED(ix); -} - -static BROTLI_INLINE void FN(StoreRange)(HashRolling* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, - const size_t ix_start, const size_t ix_end) { - BROTLI_UNUSED(self); - BROTLI_UNUSED(data); - BROTLI_UNUSED(mask); - BROTLI_UNUSED(ix_start); - BROTLI_UNUSED(ix_end); -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashRolling* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, const uint8_t* ringbuffer, - size_t ring_buffer_mask) { - /* In this case we must re-initialize the hasher from scratch from the - current position. */ - size_t position_masked; - size_t available = num_bytes; - if ((position & (JUMP - 1)) != 0) { - size_t diff = JUMP - (position & (JUMP - 1)); - available = (diff > available) ? 0 : (available - diff); - position += diff; - } - position_masked = position & ring_buffer_mask; - /* wrapping around ringbuffer not handled. */ - if (available > ring_buffer_mask - position_masked) { - available = ring_buffer_mask - position_masked; - } - - FN(Prepare)(self, BROTLI_FALSE, available, - ringbuffer + (position & ring_buffer_mask)); - self->next_ix = position; - BROTLI_UNUSED(num_bytes); -} - -static BROTLI_INLINE void FN(PrepareDistanceCache)( - HashRolling* BROTLI_RESTRICT self, - int* BROTLI_RESTRICT distance_cache) { - BROTLI_UNUSED(self); - BROTLI_UNUSED(distance_cache); -} - -static BROTLI_INLINE void FN(FindLongestMatch)( - HashRolling* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, - const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, - const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const size_t max_distance, - HasherSearchResult* BROTLI_RESTRICT out) { - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - size_t pos; - - if ((cur_ix & (JUMP - 1)) != 0) return; - - /* Not enough lookahead */ - if (max_length < CHUNKLEN) return; - - for (pos = self->next_ix; pos <= cur_ix; pos += JUMP) { - uint32_t code = self->state & MASK; - - uint8_t rem = data[pos & ring_buffer_mask]; - uint8_t add = data[(pos + CHUNKLEN) & ring_buffer_mask]; - size_t found_ix = FN(kInvalidPos); - - self->state = FN(HashRollingFunction)( - self->state, add, rem, self->factor, self->factor_remove); - - if (code < NUMBUCKETS) { - found_ix = self->table[code]; - self->table[code] = (uint32_t)pos; - if (pos == cur_ix && found_ix != FN(kInvalidPos)) { - /* The cast to 32-bit makes backward distances up to 4GB work even - if cur_ix is above 4GB, despite using 32-bit values in the table. */ - size_t backward = (uint32_t)(cur_ix - found_ix); - if (backward <= max_backward) { - const size_t found_ix_masked = found_ix & ring_buffer_mask; - const size_t len = FindMatchLengthWithLimit(&data[found_ix_masked], - &data[cur_ix_masked], - max_length); - if (len >= 4 && len > out->len) { - score_t score = BackwardReferenceScore(len, backward); - if (score > out->score) { - out->len = len; - out->distance = backward; - out->score = score; - out->len_code_delta = 0; - } - } - } - } - } - } - - self->next_ix = cur_ix + JUMP; - - /* NOTE: this hasher does not search in the dictionary. It is used as - backup-hasher, the main hasher already searches in it. */ - BROTLI_UNUSED(dictionary); - BROTLI_UNUSED(distance_cache); - BROTLI_UNUSED(dictionary_distance); - BROTLI_UNUSED(max_distance); -} - -#undef HashRolling diff --git a/src/deps/brotli/enc/hash_to_binary_tree_inc.h b/src/deps/brotli/enc/hash_to_binary_tree_inc.h deleted file mode 100644 index a639d2d4b0d3e..0000000000000 --- a/src/deps/brotli/enc/hash_to_binary_tree_inc.h +++ /dev/null @@ -1,330 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN, BUCKET_BITS, MAX_TREE_COMP_LENGTH, - MAX_TREE_SEARCH_DEPTH */ - -/* A (forgetful) hash table where each hash bucket contains a binary tree of - sequences whose first 4 bytes share the same hash code. - Each sequence is MAX_TREE_COMP_LENGTH long and is identified by its starting - position in the input data. The binary tree is sorted by the lexicographic - order of the sequences, and it is also a max-heap with respect to the - starting positions. */ - -#define HashToBinaryTree HASHER() - -#define BUCKET_SIZE (1 << BUCKET_BITS) - -static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } -static BROTLI_INLINE size_t FN(StoreLookahead)(void) { - return MAX_TREE_COMP_LENGTH; -} - -static uint32_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data) { - uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return h >> (32 - BUCKET_BITS); -} - -typedef struct HashToBinaryTree { - /* The window size minus 1 */ - size_t window_mask_; - - /* Hash table that maps the 4-byte hashes of the sequence to the last - position where this hash was found, which is the root of the binary - tree of sequences that share this hash bucket. */ - uint32_t* buckets_; /* uint32_t[BUCKET_SIZE]; */ - - /* A position used to mark a non-existent sequence, i.e. a tree is empty if - its root is at invalid_pos_ and a node is a leaf if both its children - are at invalid_pos_. */ - uint32_t invalid_pos_; - - /* --- Dynamic size members --- */ - - /* The union of the binary trees of each hash bucket. The root of the tree - corresponding to a hash is a sequence starting at buckets_[hash] and - the left and right children of a sequence starting at pos are - forest_[2 * pos] and forest_[2 * pos + 1]. */ - uint32_t* forest_; /* uint32_t[2 * num_nodes] */ -} HashToBinaryTree; - -static void FN(Initialize)( - HasherCommon* common, HashToBinaryTree* BROTLI_RESTRICT self, - const BrotliEncoderParams* params) { - self->buckets_ = (uint32_t*)common->extra[0]; - self->forest_ = (uint32_t*)common->extra[1]; - - self->window_mask_ = (1u << params->lgwin) - 1u; - self->invalid_pos_ = (uint32_t)(0 - self->window_mask_); -} - -static void FN(Prepare) - (HashToBinaryTree* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, - size_t input_size, const uint8_t* BROTLI_RESTRICT data) { - uint32_t invalid_pos = self->invalid_pos_; - uint32_t i; - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - BROTLI_UNUSED(data); - BROTLI_UNUSED(one_shot); - BROTLI_UNUSED(input_size); - for (i = 0; i < BUCKET_SIZE; i++) { - buckets[i] = invalid_pos; - } -} - -static BROTLI_INLINE void FN(HashMemAllocInBytes)( - const BrotliEncoderParams* params, BROTLI_BOOL one_shot, - size_t input_size, size_t* alloc_size) { - size_t num_nodes = (size_t)1 << params->lgwin; - if (one_shot && input_size < num_nodes) { - num_nodes = input_size; - } - alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE; - alloc_size[1] = 2 * sizeof(uint32_t) * num_nodes; -} - -static BROTLI_INLINE size_t FN(LeftChildIndex)( - HashToBinaryTree* BROTLI_RESTRICT self, - const size_t pos) { - return 2 * (pos & self->window_mask_); -} - -static BROTLI_INLINE size_t FN(RightChildIndex)( - HashToBinaryTree* BROTLI_RESTRICT self, - const size_t pos) { - return 2 * (pos & self->window_mask_) + 1; -} - -/* Stores the hash of the next 4 bytes and in a single tree-traversal, the - hash bucket's binary tree is searched for matches and is re-rooted at the - current position. - - If less than MAX_TREE_COMP_LENGTH data is available, the hash bucket of the - current position is searched for matches, but the state of the hash table - is not changed, since we can not know the final sorting order of the - current (incomplete) sequence. - - This function must be called with increasing cur_ix positions. */ -static BROTLI_INLINE BackwardMatch* FN(StoreAndFindMatches)( - HashToBinaryTree* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, - const size_t cur_ix, const size_t ring_buffer_mask, const size_t max_length, - const size_t max_backward, size_t* const BROTLI_RESTRICT best_len, - BackwardMatch* BROTLI_RESTRICT matches) { - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - const size_t max_comp_len = - BROTLI_MIN(size_t, max_length, MAX_TREE_COMP_LENGTH); - const BROTLI_BOOL should_reroot_tree = - TO_BROTLI_BOOL(max_length >= MAX_TREE_COMP_LENGTH); - const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]); - uint32_t* BROTLI_RESTRICT buckets = self->buckets_; - uint32_t* BROTLI_RESTRICT forest = self->forest_; - size_t prev_ix = buckets[key]; - /* The forest index of the rightmost node of the left subtree of the new - root, updated as we traverse and re-root the tree of the hash bucket. */ - size_t node_left = FN(LeftChildIndex)(self, cur_ix); - /* The forest index of the leftmost node of the right subtree of the new - root, updated as we traverse and re-root the tree of the hash bucket. */ - size_t node_right = FN(RightChildIndex)(self, cur_ix); - /* The match length of the rightmost node of the left subtree of the new - root, updated as we traverse and re-root the tree of the hash bucket. */ - size_t best_len_left = 0; - /* The match length of the leftmost node of the right subtree of the new - root, updated as we traverse and re-root the tree of the hash bucket. */ - size_t best_len_right = 0; - size_t depth_remaining; - if (should_reroot_tree) { - buckets[key] = (uint32_t)cur_ix; - } - for (depth_remaining = MAX_TREE_SEARCH_DEPTH; ; --depth_remaining) { - const size_t backward = cur_ix - prev_ix; - const size_t prev_ix_masked = prev_ix & ring_buffer_mask; - if (backward == 0 || backward > max_backward || depth_remaining == 0) { - if (should_reroot_tree) { - forest[node_left] = self->invalid_pos_; - forest[node_right] = self->invalid_pos_; - } - break; - } - { - const size_t cur_len = BROTLI_MIN(size_t, best_len_left, best_len_right); - size_t len; - BROTLI_DCHECK(cur_len <= MAX_TREE_COMP_LENGTH); - len = cur_len + - FindMatchLengthWithLimit(&data[cur_ix_masked + cur_len], - &data[prev_ix_masked + cur_len], - max_length - cur_len); - BROTLI_DCHECK( - 0 == memcmp(&data[cur_ix_masked], &data[prev_ix_masked], len)); - if (matches && len > *best_len) { - *best_len = len; - InitBackwardMatch(matches++, backward, len); - } - if (len >= max_comp_len) { - if (should_reroot_tree) { - forest[node_left] = forest[FN(LeftChildIndex)(self, prev_ix)]; - forest[node_right] = forest[FN(RightChildIndex)(self, prev_ix)]; - } - break; - } - if (data[cur_ix_masked + len] > data[prev_ix_masked + len]) { - best_len_left = len; - if (should_reroot_tree) { - forest[node_left] = (uint32_t)prev_ix; - } - node_left = FN(RightChildIndex)(self, prev_ix); - prev_ix = forest[node_left]; - } else { - best_len_right = len; - if (should_reroot_tree) { - forest[node_right] = (uint32_t)prev_ix; - } - node_right = FN(LeftChildIndex)(self, prev_ix); - prev_ix = forest[node_right]; - } - } - } - return matches; -} - -/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the - length of max_length and stores the position cur_ix in the hash table. - - Sets *num_matches to the number of matches found, and stores the found - matches in matches[0] to matches[*num_matches - 1]. The matches will be - sorted by strictly increasing length and (non-strictly) increasing - distance. */ -static BROTLI_INLINE size_t FN(FindAllMatches)( - HashToBinaryTree* BROTLI_RESTRICT self, - const BrotliEncoderDictionary* dictionary, - const uint8_t* BROTLI_RESTRICT data, - const size_t ring_buffer_mask, const size_t cur_ix, - const size_t max_length, const size_t max_backward, - const size_t dictionary_distance, const BrotliEncoderParams* params, - BackwardMatch* matches) { - BackwardMatch* const orig_matches = matches; - const size_t cur_ix_masked = cur_ix & ring_buffer_mask; - size_t best_len = 1; - const size_t short_match_max_backward = - params->quality != HQ_ZOPFLIFICATION_QUALITY ? 16 : 64; - size_t stop = cur_ix - short_match_max_backward; - uint32_t dict_matches[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1]; - size_t i; - if (cur_ix < short_match_max_backward) { stop = 0; } - for (i = cur_ix - 1; i > stop && best_len <= 2; --i) { - size_t prev_ix = i; - const size_t backward = cur_ix - prev_ix; - if (BROTLI_PREDICT_FALSE(backward > max_backward)) { - break; - } - prev_ix &= ring_buffer_mask; - if (data[cur_ix_masked] != data[prev_ix] || - data[cur_ix_masked + 1] != data[prev_ix + 1]) { - continue; - } - { - const size_t len = - FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked], - max_length); - if (len > best_len) { - best_len = len; - InitBackwardMatch(matches++, backward, len); - } - } - } - if (best_len < max_length) { - matches = FN(StoreAndFindMatches)(self, data, cur_ix, - ring_buffer_mask, max_length, max_backward, &best_len, matches); - } - for (i = 0; i <= BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN; ++i) { - dict_matches[i] = kInvalidMatch; - } - { - size_t minlen = BROTLI_MAX(size_t, 4, best_len + 1); - if (BrotliFindAllStaticDictionaryMatches(dictionary, - &data[cur_ix_masked], minlen, max_length, &dict_matches[0])) { - size_t maxlen = BROTLI_MIN( - size_t, BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN, max_length); - size_t l; - for (l = minlen; l <= maxlen; ++l) { - uint32_t dict_id = dict_matches[l]; - if (dict_id < kInvalidMatch) { - size_t distance = dictionary_distance + (dict_id >> 5) + 1; - if (distance <= params->dist.max_distance) { - InitDictionaryBackwardMatch(matches++, distance, l, dict_id & 31); - } - } - } - } - } - return (size_t)(matches - orig_matches); -} - -/* Stores the hash of the next 4 bytes and re-roots the binary tree at the - current sequence, without returning any matches. - REQUIRES: ix + MAX_TREE_COMP_LENGTH <= end-of-current-block */ -static BROTLI_INLINE void FN(Store)(HashToBinaryTree* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, - const size_t mask, const size_t ix) { - /* Maximum distance is window size - 16, see section 9.1. of the spec. */ - const size_t max_backward = self->window_mask_ - BROTLI_WINDOW_GAP + 1; - FN(StoreAndFindMatches)(self, data, ix, mask, MAX_TREE_COMP_LENGTH, - max_backward, NULL, NULL); -} - -static BROTLI_INLINE void FN(StoreRange)(HashToBinaryTree* BROTLI_RESTRICT self, - const uint8_t* BROTLI_RESTRICT data, const size_t mask, - const size_t ix_start, const size_t ix_end) { - size_t i = ix_start; - size_t j = ix_start; - if (ix_start + 63 <= ix_end) { - i = ix_end - 63; - } - if (ix_start + 512 <= i) { - for (; j < i; j += 8) { - FN(Store)(self, data, mask, j); - } - } - for (; i < ix_end; ++i) { - FN(Store)(self, data, mask, i); - } -} - -static BROTLI_INLINE void FN(StitchToPreviousBlock)( - HashToBinaryTree* BROTLI_RESTRICT self, - size_t num_bytes, size_t position, const uint8_t* ringbuffer, - size_t ringbuffer_mask) { - if (num_bytes >= FN(HashTypeLength)() - 1 && - position >= MAX_TREE_COMP_LENGTH) { - /* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher. - These could not be calculated before, since they require knowledge - of both the previous and the current block. */ - const size_t i_start = position - MAX_TREE_COMP_LENGTH + 1; - const size_t i_end = BROTLI_MIN(size_t, position, i_start + num_bytes); - size_t i; - for (i = i_start; i < i_end; ++i) { - /* Maximum distance is window size - 16, see section 9.1. of the spec. - Furthermore, we have to make sure that we don't look further back - from the start of the next block than the window size, otherwise we - could access already overwritten areas of the ring-buffer. */ - const size_t max_backward = - self->window_mask_ - BROTLI_MAX(size_t, - BROTLI_WINDOW_GAP - 1, - position - i); - /* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the - end of the current block and that we have at least - MAX_TREE_COMP_LENGTH tail in the ring-buffer. */ - FN(StoreAndFindMatches)(self, ringbuffer, i, ringbuffer_mask, - MAX_TREE_COMP_LENGTH, max_backward, NULL, NULL); - } - } -} - -#undef BUCKET_SIZE - -#undef HashToBinaryTree diff --git a/src/deps/brotli/enc/histogram.c b/src/deps/brotli/enc/histogram.c deleted file mode 100644 index 4dbb87f9070ec..0000000000000 --- a/src/deps/brotli/enc/histogram.c +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Build per-context histograms of literals, commands and distance codes. */ - -#include "histogram.h" - -#include "../common/context.h" -#include "block_splitter.h" -#include "command.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct BlockSplitIterator { - const BlockSplit* split_; /* Not owned. */ - size_t idx_; - size_t type_; - size_t length_; -} BlockSplitIterator; - -static void InitBlockSplitIterator(BlockSplitIterator* self, - const BlockSplit* split) { - self->split_ = split; - self->idx_ = 0; - self->type_ = 0; - self->length_ = split->lengths ? split->lengths[0] : 0; -} - -static void BlockSplitIteratorNext(BlockSplitIterator* self) { - if (self->length_ == 0) { - ++self->idx_; - self->type_ = self->split_->types[self->idx_]; - self->length_ = self->split_->lengths[self->idx_]; - } - --self->length_; -} - -void BrotliBuildHistogramsWithContext( - const Command* cmds, const size_t num_commands, - const BlockSplit* literal_split, const BlockSplit* insert_and_copy_split, - const BlockSplit* dist_split, const uint8_t* ringbuffer, size_t start_pos, - size_t mask, uint8_t prev_byte, uint8_t prev_byte2, - const ContextType* context_modes, HistogramLiteral* literal_histograms, - HistogramCommand* insert_and_copy_histograms, - HistogramDistance* copy_dist_histograms) { - size_t pos = start_pos; - BlockSplitIterator literal_it; - BlockSplitIterator insert_and_copy_it; - BlockSplitIterator dist_it; - size_t i; - - InitBlockSplitIterator(&literal_it, literal_split); - InitBlockSplitIterator(&insert_and_copy_it, insert_and_copy_split); - InitBlockSplitIterator(&dist_it, dist_split); - for (i = 0; i < num_commands; ++i) { - const Command* cmd = &cmds[i]; - size_t j; - BlockSplitIteratorNext(&insert_and_copy_it); - HistogramAddCommand(&insert_and_copy_histograms[insert_and_copy_it.type_], - cmd->cmd_prefix_); - /* TODO(eustas): unwrap iterator blocks. */ - for (j = cmd->insert_len_; j != 0; --j) { - size_t context; - BlockSplitIteratorNext(&literal_it); - context = literal_it.type_; - if (context_modes) { - ContextLut lut = BROTLI_CONTEXT_LUT(context_modes[context]); - context = (context << BROTLI_LITERAL_CONTEXT_BITS) + - BROTLI_CONTEXT(prev_byte, prev_byte2, lut); - } - HistogramAddLiteral(&literal_histograms[context], - ringbuffer[pos & mask]); - prev_byte2 = prev_byte; - prev_byte = ringbuffer[pos & mask]; - ++pos; - } - pos += CommandCopyLen(cmd); - if (CommandCopyLen(cmd)) { - prev_byte2 = ringbuffer[(pos - 2) & mask]; - prev_byte = ringbuffer[(pos - 1) & mask]; - if (cmd->cmd_prefix_ >= 128) { - size_t context; - BlockSplitIteratorNext(&dist_it); - context = (dist_it.type_ << BROTLI_DISTANCE_CONTEXT_BITS) + - CommandDistanceContext(cmd); - HistogramAddDistance(©_dist_histograms[context], - cmd->dist_prefix_ & 0x3FF); - } - } - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/histogram.h b/src/deps/brotli/enc/histogram.h deleted file mode 100644 index d1abd973c18e4..0000000000000 --- a/src/deps/brotli/enc/histogram.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Models the histograms of literals, commands and distance codes. */ - -#ifndef BROTLI_ENC_HISTOGRAM_H_ -#define BROTLI_ENC_HISTOGRAM_H_ - -#include /* memset */ - -#include - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/platform.h" -#include "block_splitter.h" -#include "command.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* The distance symbols effectively used by "Large Window Brotli" (32-bit). */ -#define BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS 544 - -#define FN(X) X ## Literal -#define DATA_SIZE BROTLI_NUM_LITERAL_SYMBOLS -#define DataType uint8_t -#include "histogram_inc.h" /* NOLINT(build/include) */ -#undef DataType -#undef DATA_SIZE -#undef FN - -#define FN(X) X ## Command -#define DataType uint16_t -#define DATA_SIZE BROTLI_NUM_COMMAND_SYMBOLS -#include "histogram_inc.h" /* NOLINT(build/include) */ -#undef DATA_SIZE -#undef FN - -#define FN(X) X ## Distance -#define DATA_SIZE BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS -#include "histogram_inc.h" /* NOLINT(build/include) */ -#undef DataType -#undef DATA_SIZE -#undef FN - -BROTLI_INTERNAL void BrotliBuildHistogramsWithContext( - const Command* cmds, const size_t num_commands, - const BlockSplit* literal_split, const BlockSplit* insert_and_copy_split, - const BlockSplit* dist_split, const uint8_t* ringbuffer, size_t pos, - size_t mask, uint8_t prev_byte, uint8_t prev_byte2, - const ContextType* context_modes, HistogramLiteral* literal_histograms, - HistogramCommand* insert_and_copy_histograms, - HistogramDistance* copy_dist_histograms); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_HISTOGRAM_H_ */ diff --git a/src/deps/brotli/enc/histogram_inc.h b/src/deps/brotli/enc/histogram_inc.h deleted file mode 100644 index 50eaf7468dab4..0000000000000 --- a/src/deps/brotli/enc/histogram_inc.h +++ /dev/null @@ -1,51 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: Histogram, DATA_SIZE, DataType */ - -/* A simple container for histograms of data in blocks. */ - -typedef struct FN(Histogram) { - uint32_t data_[DATA_SIZE]; - size_t total_count_; - double bit_cost_; -} FN(Histogram); - -static BROTLI_INLINE void FN(HistogramClear)(FN(Histogram)* self) { - memset(self->data_, 0, sizeof(self->data_)); - self->total_count_ = 0; - self->bit_cost_ = HUGE_VAL; -} - -static BROTLI_INLINE void FN(ClearHistograms)( - FN(Histogram)* array, size_t length) { - size_t i; - for (i = 0; i < length; ++i) FN(HistogramClear)(array + i); -} - -static BROTLI_INLINE void FN(HistogramAdd)(FN(Histogram)* self, size_t val) { - ++self->data_[val]; - ++self->total_count_; -} - -static BROTLI_INLINE void FN(HistogramAddVector)(FN(Histogram)* self, - const DataType* p, size_t n) { - self->total_count_ += n; - n += 1; - while (--n) ++self->data_[*p++]; -} - -static BROTLI_INLINE void FN(HistogramAddHistogram)(FN(Histogram)* self, - const FN(Histogram)* v) { - size_t i; - self->total_count_ += v->total_count_; - for (i = 0; i < DATA_SIZE; ++i) { - self->data_[i] += v->data_[i]; - } -} - -static BROTLI_INLINE size_t FN(HistogramDataSize)(void) { return DATA_SIZE; } diff --git a/src/deps/brotli/enc/literal_cost.c b/src/deps/brotli/enc/literal_cost.c deleted file mode 100644 index 2ac847f3c98f6..0000000000000 --- a/src/deps/brotli/enc/literal_cost.c +++ /dev/null @@ -1,180 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Literal cost model to allow backward reference replacement to be efficient. -*/ - -#include "literal_cost.h" - -#include /* memset */ - -#include - -#include "../common/platform.h" -#include "fast_log.h" -#include "utf8_util.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static size_t UTF8Position(size_t last, size_t c, size_t clamp) { - if (c < 128) { - return 0; /* Next one is the 'Byte 1' again. */ - } else if (c >= 192) { /* Next one is the 'Byte 2' of utf-8 encoding. */ - return BROTLI_MIN(size_t, 1, clamp); - } else { - /* Let's decide over the last byte if this ends the sequence. */ - if (last < 0xE0) { - return 0; /* Completed two or three byte coding. */ - } else { /* Next one is the 'Byte 3' of utf-8 encoding. */ - return BROTLI_MIN(size_t, 2, clamp); - } - } -} - -static size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask, - const uint8_t* data) { - size_t counts[3] = { 0 }; - size_t max_utf8 = 1; /* should be 2, but 1 compresses better. */ - size_t last_c = 0; - size_t i; - for (i = 0; i < len; ++i) { - size_t c = data[(pos + i) & mask]; - ++counts[UTF8Position(last_c, c, 2)]; - last_c = c; - } - if (counts[2] < 500) { - max_utf8 = 1; - } - if (counts[1] + counts[2] < 25) { - max_utf8 = 0; - } - return max_utf8; -} - -static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask, - const uint8_t* data, - size_t* histogram, float* cost) { - /* max_utf8 is 0 (normal ASCII single byte modeling), - 1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */ - const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data); - size_t window_half = 495; - size_t in_window = BROTLI_MIN(size_t, window_half, len); - size_t in_window_utf8[3] = { 0 }; - size_t i; - memset(histogram, 0, 3 * 256 * sizeof(histogram[0])); - - { /* Bootstrap histograms. */ - size_t last_c = 0; - size_t utf8_pos = 0; - for (i = 0; i < in_window; ++i) { - size_t c = data[(pos + i) & mask]; - ++histogram[256 * utf8_pos + c]; - ++in_window_utf8[utf8_pos]; - utf8_pos = UTF8Position(last_c, c, max_utf8); - last_c = c; - } - } - - /* Compute bit costs with sliding window. */ - for (i = 0; i < len; ++i) { - if (i >= window_half) { - /* Remove a byte in the past. */ - size_t c = - i < window_half + 1 ? 0 : data[(pos + i - window_half - 1) & mask]; - size_t last_c = - i < window_half + 2 ? 0 : data[(pos + i - window_half - 2) & mask]; - size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8); - --histogram[256 * utf8_pos2 + data[(pos + i - window_half) & mask]]; - --in_window_utf8[utf8_pos2]; - } - if (i + window_half < len) { - /* Add a byte in the future. */ - size_t c = data[(pos + i + window_half - 1) & mask]; - size_t last_c = data[(pos + i + window_half - 2) & mask]; - size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8); - ++histogram[256 * utf8_pos2 + data[(pos + i + window_half) & mask]]; - ++in_window_utf8[utf8_pos2]; - } - { - size_t c = i < 1 ? 0 : data[(pos + i - 1) & mask]; - size_t last_c = i < 2 ? 0 : data[(pos + i - 2) & mask]; - size_t utf8_pos = UTF8Position(last_c, c, max_utf8); - size_t masked_pos = (pos + i) & mask; - size_t histo = histogram[256 * utf8_pos + data[masked_pos]]; - double lit_cost; - if (histo == 0) { - histo = 1; - } - lit_cost = FastLog2(in_window_utf8[utf8_pos]) - FastLog2(histo); - lit_cost += 0.02905; - if (lit_cost < 1.0) { - lit_cost *= 0.5; - lit_cost += 0.5; - } - /* Make the first bytes more expensive -- seems to help, not sure why. - Perhaps because the entropy source is changing its properties - rapidly in the beginning of the file, perhaps because the beginning - of the data is a statistical "anomaly". */ - if (i < 2000) { - lit_cost += 0.7 - ((double)(2000 - i) / 2000.0 * 0.35); - } - cost[i] = (float)lit_cost; - } - } -} - -void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask, - const uint8_t* data, - size_t* histogram, float* cost) { - if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) { - EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, histogram, cost); - return; - } else { - size_t window_half = 2000; - size_t in_window = BROTLI_MIN(size_t, window_half, len); - size_t i; - memset(histogram, 0, 256 * sizeof(histogram[0])); - - /* Bootstrap histogram. */ - for (i = 0; i < in_window; ++i) { - ++histogram[data[(pos + i) & mask]]; - } - - /* Compute bit costs with sliding window. */ - for (i = 0; i < len; ++i) { - size_t histo; - if (i >= window_half) { - /* Remove a byte in the past. */ - --histogram[data[(pos + i - window_half) & mask]]; - --in_window; - } - if (i + window_half < len) { - /* Add a byte in the future. */ - ++histogram[data[(pos + i + window_half) & mask]]; - ++in_window; - } - histo = histogram[data[(pos + i) & mask]]; - if (histo == 0) { - histo = 1; - } - { - double lit_cost = FastLog2(in_window) - FastLog2(histo); - lit_cost += 0.029; - if (lit_cost < 1.0) { - lit_cost *= 0.5; - lit_cost += 0.5; - } - cost[i] = (float)lit_cost; - } - } - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/literal_cost.h b/src/deps/brotli/enc/literal_cost.h deleted file mode 100644 index 284a8e5af7315..0000000000000 --- a/src/deps/brotli/enc/literal_cost.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Literal cost model to allow backward reference replacement to be efficient. -*/ - -#ifndef BROTLI_ENC_LITERAL_COST_H_ -#define BROTLI_ENC_LITERAL_COST_H_ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* Estimates how many bits the literals in the interval [pos, pos + len) in the - ring-buffer (data, mask) will take entropy coded and writes these estimates - to the cost[0..len) array. */ -BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals( - size_t pos, size_t len, size_t mask, const uint8_t* data, size_t* histogram, - float* cost); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_LITERAL_COST_H_ */ diff --git a/src/deps/brotli/enc/memory.c b/src/deps/brotli/enc/memory.c deleted file mode 100644 index bb5e364198e3f..0000000000000 --- a/src/deps/brotli/enc/memory.c +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Algorithms for distributing the literals and commands of a metablock between - block types and contexts. */ - -#include "memory.h" - -#include /* exit, free, malloc */ -#include /* memcpy */ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define MAX_NEW_ALLOCATED (BROTLI_ENCODER_MEMORY_MANAGER_SLOTS >> 2) -#define MAX_NEW_FREED (BROTLI_ENCODER_MEMORY_MANAGER_SLOTS >> 2) -#define MAX_PERM_ALLOCATED (BROTLI_ENCODER_MEMORY_MANAGER_SLOTS >> 1) - -#define PERM_ALLOCATED_OFFSET 0 -#define NEW_ALLOCATED_OFFSET MAX_PERM_ALLOCATED -#define NEW_FREED_OFFSET (MAX_PERM_ALLOCATED + MAX_NEW_ALLOCATED) - -void BrotliInitMemoryManager( - MemoryManager* m, brotli_alloc_func alloc_func, brotli_free_func free_func, - void* opaque) { - if (!alloc_func) { - m->alloc_func = BrotliDefaultAllocFunc; - m->free_func = BrotliDefaultFreeFunc; - m->opaque = 0; - } else { - m->alloc_func = alloc_func; - m->free_func = free_func; - m->opaque = opaque; - } -#if !defined(BROTLI_ENCODER_EXIT_ON_OOM) - m->is_oom = BROTLI_FALSE; - m->perm_allocated = 0; - m->new_allocated = 0; - m->new_freed = 0; -#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ -} - -#if defined(BROTLI_ENCODER_EXIT_ON_OOM) - -void* BrotliAllocate(MemoryManager* m, size_t n) { - void* result = m->alloc_func(m->opaque, n); - if (!result) exit(EXIT_FAILURE); - return result; -} - -void BrotliFree(MemoryManager* m, void* p) { - m->free_func(m->opaque, p); -} - -void BrotliWipeOutMemoryManager(MemoryManager* m) { - BROTLI_UNUSED(m); -} - -#else /* BROTLI_ENCODER_EXIT_ON_OOM */ - -static void SortPointers(void** items, const size_t n) { - /* Shell sort. */ - /* TODO(eustas): fine-tune for "many slots" case */ - static const size_t gaps[] = {23, 10, 4, 1}; - int g = 0; - for (; g < 4; ++g) { - size_t gap = gaps[g]; - size_t i; - for (i = gap; i < n; ++i) { - size_t j = i; - void* tmp = items[i]; - for (; j >= gap && tmp < items[j - gap]; j -= gap) { - items[j] = items[j - gap]; - } - items[j] = tmp; - } - } -} - -static size_t Annihilate(void** a, size_t a_len, void** b, size_t b_len) { - size_t a_read_index = 0; - size_t b_read_index = 0; - size_t a_write_index = 0; - size_t b_write_index = 0; - size_t annihilated = 0; - while (a_read_index < a_len && b_read_index < b_len) { - if (a[a_read_index] == b[b_read_index]) { - a_read_index++; - b_read_index++; - annihilated++; - } else if (a[a_read_index] < b[b_read_index]) { - a[a_write_index++] = a[a_read_index++]; - } else { - b[b_write_index++] = b[b_read_index++]; - } - } - while (a_read_index < a_len) a[a_write_index++] = a[a_read_index++]; - while (b_read_index < b_len) b[b_write_index++] = b[b_read_index++]; - return annihilated; -} - -static void CollectGarbagePointers(MemoryManager* m) { - size_t annihilated; - SortPointers(m->pointers + NEW_ALLOCATED_OFFSET, m->new_allocated); - SortPointers(m->pointers + NEW_FREED_OFFSET, m->new_freed); - annihilated = Annihilate( - m->pointers + NEW_ALLOCATED_OFFSET, m->new_allocated, - m->pointers + NEW_FREED_OFFSET, m->new_freed); - m->new_allocated -= annihilated; - m->new_freed -= annihilated; - - if (m->new_freed != 0) { - annihilated = Annihilate( - m->pointers + PERM_ALLOCATED_OFFSET, m->perm_allocated, - m->pointers + NEW_FREED_OFFSET, m->new_freed); - m->perm_allocated -= annihilated; - m->new_freed -= annihilated; - BROTLI_DCHECK(m->new_freed == 0); - } - - if (m->new_allocated != 0) { - BROTLI_DCHECK(m->perm_allocated + m->new_allocated <= MAX_PERM_ALLOCATED); - memcpy(m->pointers + PERM_ALLOCATED_OFFSET + m->perm_allocated, - m->pointers + NEW_ALLOCATED_OFFSET, - sizeof(void*) * m->new_allocated); - m->perm_allocated += m->new_allocated; - m->new_allocated = 0; - SortPointers(m->pointers + PERM_ALLOCATED_OFFSET, m->perm_allocated); - } -} - -void* BrotliAllocate(MemoryManager* m, size_t n) { - void* result = m->alloc_func(m->opaque, n); - if (!result) { - m->is_oom = BROTLI_TRUE; - return NULL; - } - if (m->new_allocated == MAX_NEW_ALLOCATED) CollectGarbagePointers(m); - m->pointers[NEW_ALLOCATED_OFFSET + (m->new_allocated++)] = result; - return result; -} - -void BrotliFree(MemoryManager* m, void* p) { - if (!p) return; - m->free_func(m->opaque, p); - if (m->new_freed == MAX_NEW_FREED) CollectGarbagePointers(m); - m->pointers[NEW_FREED_OFFSET + (m->new_freed++)] = p; -} - -void BrotliWipeOutMemoryManager(MemoryManager* m) { - size_t i; - CollectGarbagePointers(m); - /* Now all unfreed pointers are in perm-allocated list. */ - for (i = 0; i < m->perm_allocated; ++i) { - m->free_func(m->opaque, m->pointers[PERM_ALLOCATED_OFFSET + i]); - } - m->perm_allocated = 0; -} - -#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ - -void* BrotliBootstrapAlloc(size_t size, - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { - if (!alloc_func && !free_func) { - return malloc(size); - } else if (alloc_func && free_func) { - return alloc_func(opaque, size); - } - return NULL; -} - -void BrotliBootstrapFree(void* address, MemoryManager* m) { - if (!address) { - /* Should not happen! */ - return; - } else { - /* Copy values, as those would be freed. */ - brotli_free_func free_func = m->free_func; - void* opaque = m->opaque; - free_func(opaque, address); - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/memory.h b/src/deps/brotli/enc/memory.h deleted file mode 100644 index a4417df4cb2c0..0000000000000 --- a/src/deps/brotli/enc/memory.h +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Macros for memory management. */ - -#ifndef BROTLI_ENC_MEMORY_H_ -#define BROTLI_ENC_MEMORY_H_ - -#include /* memcpy */ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#if !defined(BROTLI_ENCODER_CLEANUP_ON_OOM) && \ - !defined(BROTLI_ENCODER_EXIT_ON_OOM) -#define BROTLI_ENCODER_EXIT_ON_OOM -#endif - -#if !defined(BROTLI_ENCODER_EXIT_ON_OOM) -#if defined(BROTLI_EXPERIMENTAL) -#define BROTLI_ENCODER_MEMORY_MANAGER_SLOTS (48*1024) -#else /* BROTLI_EXPERIMENTAL */ -#define BROTLI_ENCODER_MEMORY_MANAGER_SLOTS 256 -#endif /* BROTLI_EXPERIMENTAL */ -#else /* BROTLI_ENCODER_EXIT_ON_OOM */ -#define BROTLI_ENCODER_MEMORY_MANAGER_SLOTS 0 -#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ - -typedef struct MemoryManager { - brotli_alloc_func alloc_func; - brotli_free_func free_func; - void* opaque; -#if !defined(BROTLI_ENCODER_EXIT_ON_OOM) - BROTLI_BOOL is_oom; - size_t perm_allocated; - size_t new_allocated; - size_t new_freed; - void* pointers[BROTLI_ENCODER_MEMORY_MANAGER_SLOTS]; -#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ -} MemoryManager; - -BROTLI_INTERNAL void BrotliInitMemoryManager( - MemoryManager* m, brotli_alloc_func alloc_func, brotli_free_func free_func, - void* opaque); - -BROTLI_INTERNAL void* BrotliAllocate(MemoryManager* m, size_t n); -#define BROTLI_ALLOC(M, T, N) \ - ((N) > 0 ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL) - -BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p); -#define BROTLI_FREE(M, P) { \ - BrotliFree((M), (P)); \ - P = NULL; \ -} - -#if defined(BROTLI_ENCODER_EXIT_ON_OOM) -#define BROTLI_IS_OOM(M) (!!0) -#else /* BROTLI_ENCODER_EXIT_ON_OOM */ -#define BROTLI_IS_OOM(M) (!!(M)->is_oom) -#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ - -/* -BROTLI_IS_NULL is a fake check, BROTLI_IS_OOM does the heavy lifting. -The only purpose of it is to explain static analyzers the state of things. -NB: use ONLY together with BROTLI_IS_OOM - AND ONLY for allocations in the current scope. - */ -#if defined(__clang_analyzer__) && !defined(BROTLI_ENCODER_EXIT_ON_OOM) -#define BROTLI_IS_NULL(A) ((A) == nullptr) -#else /* defined(__clang_analyzer__) */ -#define BROTLI_IS_NULL(A) (!!0) -#endif /* defined(__clang_analyzer__) */ - -BROTLI_INTERNAL void BrotliWipeOutMemoryManager(MemoryManager* m); - -/* -Dynamically grows array capacity to at least the requested size -M: MemoryManager -T: data type -A: array -C: capacity -R: requested size -*/ -#define BROTLI_ENSURE_CAPACITY(M, T, A, C, R) { \ - if (C < (R)) { \ - size_t _new_size = (C == 0) ? (R) : C; \ - T* new_array; \ - while (_new_size < (R)) _new_size *= 2; \ - new_array = BROTLI_ALLOC((M), T, _new_size); \ - if (!BROTLI_IS_OOM(M) && !BROTLI_IS_NULL(new_array) && C != 0) \ - memcpy(new_array, A, C * sizeof(T)); \ - BROTLI_FREE((M), A); \ - A = new_array; \ - C = _new_size; \ - } \ -} - -/* -Appends value and dynamically grows array capacity when needed -M: MemoryManager -T: data type -A: array -C: array capacity -S: array size -V: value to append -*/ -#define BROTLI_ENSURE_CAPACITY_APPEND(M, T, A, C, S, V) { \ - (S)++; \ - BROTLI_ENSURE_CAPACITY(M, T, A, C, S); \ - A[(S) - 1] = (V); \ -} - -/* "Bootstrap" allocations are not tracked by memory manager; should be used - only to allocate MemoryManager itself (or structure containing it). */ -BROTLI_INTERNAL void* BrotliBootstrapAlloc(size_t size, - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); -BROTLI_INTERNAL void BrotliBootstrapFree(void* address, MemoryManager* m); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_MEMORY_H_ */ diff --git a/src/deps/brotli/enc/metablock.c b/src/deps/brotli/enc/metablock.c deleted file mode 100644 index 0c5c078d05ea0..0000000000000 --- a/src/deps/brotli/enc/metablock.c +++ /dev/null @@ -1,677 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Algorithms for distributing the literals and commands of a metablock between - block types and contexts. */ - -#include "metablock.h" - -#include - -#include "../common/constants.h" -#include "../common/context.h" -#include "../common/platform.h" -#include "bit_cost.h" -#include "block_splitter.h" -#include "cluster.h" -#include "entropy_encode.h" -#include "histogram.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -void BrotliInitDistanceParams(BrotliDistanceParams* dist_params, - uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window) { - uint32_t alphabet_size_max; - uint32_t alphabet_size_limit; - uint32_t max_distance; - - dist_params->distance_postfix_bits = npostfix; - dist_params->num_direct_distance_codes = ndirect; - - alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( - npostfix, ndirect, BROTLI_MAX_DISTANCE_BITS); - alphabet_size_limit = alphabet_size_max; - max_distance = ndirect + (1U << (BROTLI_MAX_DISTANCE_BITS + npostfix + 2)) - - (1U << (npostfix + 2)); - - if (large_window) { - BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit( - BROTLI_MAX_ALLOWED_DISTANCE, npostfix, ndirect); - alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( - npostfix, ndirect, BROTLI_LARGE_MAX_DISTANCE_BITS); - alphabet_size_limit = limit.max_alphabet_size; - max_distance = limit.max_distance; - } - - dist_params->alphabet_size_max = alphabet_size_max; - dist_params->alphabet_size_limit = alphabet_size_limit; - dist_params->max_distance = max_distance; -} - -static void RecomputeDistancePrefixes(Command* cmds, - size_t num_commands, - const BrotliDistanceParams* orig_params, - const BrotliDistanceParams* new_params) { - size_t i; - - if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits && - orig_params->num_direct_distance_codes == - new_params->num_direct_distance_codes) { - return; - } - - for (i = 0; i < num_commands; ++i) { - Command* cmd = &cmds[i]; - if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) { - PrefixEncodeCopyDistance(CommandRestoreDistanceCode(cmd, orig_params), - new_params->num_direct_distance_codes, - new_params->distance_postfix_bits, - &cmd->dist_prefix_, - &cmd->dist_extra_); - } - } -} - -static BROTLI_BOOL ComputeDistanceCost(const Command* cmds, - size_t num_commands, - const BrotliDistanceParams* orig_params, - const BrotliDistanceParams* new_params, - double* cost, - HistogramDistance* tmp) { - size_t i; - BROTLI_BOOL equal_params = BROTLI_FALSE; - uint16_t dist_prefix; - uint32_t dist_extra; - double extra_bits = 0.0; - HistogramClearDistance(tmp); - - if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits && - orig_params->num_direct_distance_codes == - new_params->num_direct_distance_codes) { - equal_params = BROTLI_TRUE; - } - - for (i = 0; i < num_commands; i++) { - const Command* cmd = &cmds[i]; - if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) { - if (equal_params) { - dist_prefix = cmd->dist_prefix_; - } else { - uint32_t distance = CommandRestoreDistanceCode(cmd, orig_params); - if (distance > new_params->max_distance) { - return BROTLI_FALSE; - } - PrefixEncodeCopyDistance(distance, - new_params->num_direct_distance_codes, - new_params->distance_postfix_bits, - &dist_prefix, - &dist_extra); - } - HistogramAddDistance(tmp, dist_prefix & 0x3FF); - extra_bits += dist_prefix >> 10; - } - } - - *cost = BrotliPopulationCostDistance(tmp) + extra_bits; - return BROTLI_TRUE; -} - -void BrotliBuildMetaBlock(MemoryManager* m, - const uint8_t* ringbuffer, - const size_t pos, - const size_t mask, - BrotliEncoderParams* params, - uint8_t prev_byte, - uint8_t prev_byte2, - Command* cmds, - size_t num_commands, - ContextType literal_context_mode, - MetaBlockSplit* mb) { - /* Histogram ids need to fit in one byte. */ - static const size_t kMaxNumberOfHistograms = 256; - HistogramDistance* distance_histograms; - HistogramLiteral* literal_histograms; - ContextType* literal_context_modes = NULL; - size_t literal_histograms_size; - size_t distance_histograms_size; - size_t i; - size_t literal_context_multiplier = 1; - uint32_t npostfix; - uint32_t ndirect_msb = 0; - BROTLI_BOOL check_orig = BROTLI_TRUE; - double best_dist_cost = 1e99; - BrotliDistanceParams orig_params = params->dist; - BrotliDistanceParams new_params = params->dist; - HistogramDistance* tmp = BROTLI_ALLOC(m, HistogramDistance, 1); - - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return; - - for (npostfix = 0; npostfix <= BROTLI_MAX_NPOSTFIX; npostfix++) { - for (; ndirect_msb < 16; ndirect_msb++) { - uint32_t ndirect = ndirect_msb << npostfix; - BROTLI_BOOL skip; - double dist_cost; - BrotliInitDistanceParams(&new_params, npostfix, ndirect, - params->large_window); - if (npostfix == orig_params.distance_postfix_bits && - ndirect == orig_params.num_direct_distance_codes) { - check_orig = BROTLI_FALSE; - } - skip = !ComputeDistanceCost( - cmds, num_commands, &orig_params, &new_params, &dist_cost, tmp); - if (skip || (dist_cost > best_dist_cost)) { - break; - } - best_dist_cost = dist_cost; - params->dist = new_params; - } - if (ndirect_msb > 0) ndirect_msb--; - ndirect_msb /= 2; - } - if (check_orig) { - double dist_cost; - ComputeDistanceCost(cmds, num_commands, &orig_params, &orig_params, - &dist_cost, tmp); - if (dist_cost < best_dist_cost) { - /* NB: currently unused; uncomment when more param tuning is added. */ - /* best_dist_cost = dist_cost; */ - params->dist = orig_params; - } - } - BROTLI_FREE(m, tmp); - RecomputeDistancePrefixes(cmds, num_commands, &orig_params, ¶ms->dist); - - BrotliSplitBlock(m, cmds, num_commands, - ringbuffer, pos, mask, params, - &mb->literal_split, - &mb->command_split, - &mb->distance_split); - if (BROTLI_IS_OOM(m)) return; - - if (!params->disable_literal_context_modeling) { - literal_context_multiplier = 1 << BROTLI_LITERAL_CONTEXT_BITS; - literal_context_modes = - BROTLI_ALLOC(m, ContextType, mb->literal_split.num_types); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(literal_context_modes)) return; - for (i = 0; i < mb->literal_split.num_types; ++i) { - literal_context_modes[i] = literal_context_mode; - } - } - - literal_histograms_size = - mb->literal_split.num_types * literal_context_multiplier; - literal_histograms = - BROTLI_ALLOC(m, HistogramLiteral, literal_histograms_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(literal_histograms)) return; - ClearHistogramsLiteral(literal_histograms, literal_histograms_size); - - distance_histograms_size = - mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS; - distance_histograms = - BROTLI_ALLOC(m, HistogramDistance, distance_histograms_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(distance_histograms)) return; - ClearHistogramsDistance(distance_histograms, distance_histograms_size); - - BROTLI_DCHECK(mb->command_histograms == 0); - mb->command_histograms_size = mb->command_split.num_types; - mb->command_histograms = - BROTLI_ALLOC(m, HistogramCommand, mb->command_histograms_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->command_histograms)) return; - ClearHistogramsCommand(mb->command_histograms, mb->command_histograms_size); - - BrotliBuildHistogramsWithContext(cmds, num_commands, - &mb->literal_split, &mb->command_split, &mb->distance_split, - ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes, - literal_histograms, mb->command_histograms, distance_histograms); - BROTLI_FREE(m, literal_context_modes); - - BROTLI_DCHECK(mb->literal_context_map == 0); - mb->literal_context_map_size = - mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS; - mb->literal_context_map = - BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->literal_context_map)) return; - - BROTLI_DCHECK(mb->literal_histograms == 0); - mb->literal_histograms_size = mb->literal_context_map_size; - mb->literal_histograms = - BROTLI_ALLOC(m, HistogramLiteral, mb->literal_histograms_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->literal_histograms)) return; - - BrotliClusterHistogramsLiteral(m, literal_histograms, literal_histograms_size, - kMaxNumberOfHistograms, mb->literal_histograms, - &mb->literal_histograms_size, mb->literal_context_map); - if (BROTLI_IS_OOM(m)) return; - BROTLI_FREE(m, literal_histograms); - - if (params->disable_literal_context_modeling) { - /* Distribute assignment to all contexts. */ - for (i = mb->literal_split.num_types; i != 0;) { - size_t j = 0; - i--; - for (; j < (1 << BROTLI_LITERAL_CONTEXT_BITS); j++) { - mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] = - mb->literal_context_map[i]; - } - } - } - - BROTLI_DCHECK(mb->distance_context_map == 0); - mb->distance_context_map_size = - mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS; - mb->distance_context_map = - BROTLI_ALLOC(m, uint32_t, mb->distance_context_map_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->distance_context_map)) return; - - BROTLI_DCHECK(mb->distance_histograms == 0); - mb->distance_histograms_size = mb->distance_context_map_size; - mb->distance_histograms = - BROTLI_ALLOC(m, HistogramDistance, mb->distance_histograms_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->distance_histograms)) return; - - BrotliClusterHistogramsDistance(m, distance_histograms, - mb->distance_context_map_size, - kMaxNumberOfHistograms, - mb->distance_histograms, - &mb->distance_histograms_size, - mb->distance_context_map); - if (BROTLI_IS_OOM(m)) return; - BROTLI_FREE(m, distance_histograms); -} - -#define FN(X) X ## Literal -#include "metablock_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Command -#include "metablock_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define FN(X) X ## Distance -#include "metablock_inc.h" /* NOLINT(build/include) */ -#undef FN - -#define BROTLI_MAX_STATIC_CONTEXTS 13 - -/* Greedy block splitter for one block category (literal, command or distance). - Gathers histograms for all context buckets. */ -typedef struct ContextBlockSplitter { - /* Alphabet size of particular block category. */ - size_t alphabet_size_; - size_t num_contexts_; - size_t max_block_types_; - /* We collect at least this many symbols for each block. */ - size_t min_block_size_; - /* We merge histograms A and B if - entropy(A+B) < entropy(A) + entropy(B) + split_threshold_, - where A is the current histogram and B is the histogram of the last or the - second last block type. */ - double split_threshold_; - - size_t num_blocks_; - BlockSplit* split_; /* not owned */ - HistogramLiteral* histograms_; /* not owned */ - size_t* histograms_size_; /* not owned */ - - /* The number of symbols that we want to collect before deciding on whether - or not to merge the block with a previous one or emit a new block. */ - size_t target_block_size_; - /* The number of symbols in the current histogram. */ - size_t block_size_; - /* Offset of the current histogram. */ - size_t curr_histogram_ix_; - /* Offset of the histograms of the previous two block types. */ - size_t last_histogram_ix_[2]; - /* Entropy of the previous two block types. */ - double last_entropy_[2 * BROTLI_MAX_STATIC_CONTEXTS]; - /* The number of times we merged the current block with the last one. */ - size_t merge_last_count_; -} ContextBlockSplitter; - -static void InitContextBlockSplitter( - MemoryManager* m, ContextBlockSplitter* self, size_t alphabet_size, - size_t num_contexts, size_t min_block_size, double split_threshold, - size_t num_symbols, BlockSplit* split, HistogramLiteral** histograms, - size_t* histograms_size) { - size_t max_num_blocks = num_symbols / min_block_size + 1; - size_t max_num_types; - BROTLI_DCHECK(num_contexts <= BROTLI_MAX_STATIC_CONTEXTS); - - self->alphabet_size_ = alphabet_size; - self->num_contexts_ = num_contexts; - self->max_block_types_ = BROTLI_MAX_NUMBER_OF_BLOCK_TYPES / num_contexts; - self->min_block_size_ = min_block_size; - self->split_threshold_ = split_threshold; - self->num_blocks_ = 0; - self->split_ = split; - self->histograms_size_ = histograms_size; - self->target_block_size_ = min_block_size; - self->block_size_ = 0; - self->curr_histogram_ix_ = 0; - self->merge_last_count_ = 0; - - /* We have to allocate one more histogram than the maximum number of block - types for the current histogram when the meta-block is too big. */ - max_num_types = - BROTLI_MIN(size_t, max_num_blocks, self->max_block_types_ + 1); - BROTLI_ENSURE_CAPACITY(m, uint8_t, - split->types, split->types_alloc_size, max_num_blocks); - BROTLI_ENSURE_CAPACITY(m, uint32_t, - split->lengths, split->lengths_alloc_size, max_num_blocks); - if (BROTLI_IS_OOM(m)) return; - split->num_blocks = max_num_blocks; - if (BROTLI_IS_OOM(m)) return; - BROTLI_DCHECK(*histograms == 0); - *histograms_size = max_num_types * num_contexts; - *histograms = BROTLI_ALLOC(m, HistogramLiteral, *histograms_size); - self->histograms_ = *histograms; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(*histograms)) return; - /* Clear only current histogram. */ - ClearHistogramsLiteral(&self->histograms_[0], num_contexts); - self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0; -} - -/* Does either of three things: - (1) emits the current block with a new block type; - (2) emits the current block with the type of the second last block; - (3) merges the current block with the last block. */ -static void ContextBlockSplitterFinishBlock( - ContextBlockSplitter* self, MemoryManager* m, BROTLI_BOOL is_final) { - BlockSplit* split = self->split_; - const size_t num_contexts = self->num_contexts_; - double* last_entropy = self->last_entropy_; - HistogramLiteral* histograms = self->histograms_; - - if (self->block_size_ < self->min_block_size_) { - self->block_size_ = self->min_block_size_; - } - if (self->num_blocks_ == 0) { - size_t i; - /* Create first block. */ - split->lengths[0] = (uint32_t)self->block_size_; - split->types[0] = 0; - - for (i = 0; i < num_contexts; ++i) { - last_entropy[i] = - BitsEntropy(histograms[i].data_, self->alphabet_size_); - last_entropy[num_contexts + i] = last_entropy[i]; - } - ++self->num_blocks_; - ++split->num_types; - self->curr_histogram_ix_ += num_contexts; - if (self->curr_histogram_ix_ < *self->histograms_size_) { - ClearHistogramsLiteral( - &self->histograms_[self->curr_histogram_ix_], self->num_contexts_); - } - self->block_size_ = 0; - } else if (self->block_size_ > 0) { - /* Try merging the set of histograms for the current block type with the - respective set of histograms for the last and second last block types. - Decide over the split based on the total reduction of entropy across - all contexts. */ - double entropy[BROTLI_MAX_STATIC_CONTEXTS]; - HistogramLiteral* combined_histo = - BROTLI_ALLOC(m, HistogramLiteral, 2 * num_contexts); - double combined_entropy[2 * BROTLI_MAX_STATIC_CONTEXTS]; - double diff[2] = { 0.0 }; - size_t i; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(combined_histo)) return; - for (i = 0; i < num_contexts; ++i) { - size_t curr_histo_ix = self->curr_histogram_ix_ + i; - size_t j; - entropy[i] = BitsEntropy(histograms[curr_histo_ix].data_, - self->alphabet_size_); - for (j = 0; j < 2; ++j) { - size_t jx = j * num_contexts + i; - size_t last_histogram_ix = self->last_histogram_ix_[j] + i; - combined_histo[jx] = histograms[curr_histo_ix]; - HistogramAddHistogramLiteral(&combined_histo[jx], - &histograms[last_histogram_ix]); - combined_entropy[jx] = BitsEntropy( - &combined_histo[jx].data_[0], self->alphabet_size_); - diff[j] += combined_entropy[jx] - entropy[i] - last_entropy[jx]; - } - } - - if (split->num_types < self->max_block_types_ && - diff[0] > self->split_threshold_ && - diff[1] > self->split_threshold_) { - /* Create new block. */ - split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; - split->types[self->num_blocks_] = (uint8_t)split->num_types; - self->last_histogram_ix_[1] = self->last_histogram_ix_[0]; - self->last_histogram_ix_[0] = split->num_types * num_contexts; - for (i = 0; i < num_contexts; ++i) { - last_entropy[num_contexts + i] = last_entropy[i]; - last_entropy[i] = entropy[i]; - } - ++self->num_blocks_; - ++split->num_types; - self->curr_histogram_ix_ += num_contexts; - if (self->curr_histogram_ix_ < *self->histograms_size_) { - ClearHistogramsLiteral( - &self->histograms_[self->curr_histogram_ix_], self->num_contexts_); - } - self->block_size_ = 0; - self->merge_last_count_ = 0; - self->target_block_size_ = self->min_block_size_; - } else if (diff[1] < diff[0] - 20.0) { - /* Combine this block with second last block. */ - split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; - split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; - BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1); - for (i = 0; i < num_contexts; ++i) { - histograms[self->last_histogram_ix_[0] + i] = - combined_histo[num_contexts + i]; - last_entropy[num_contexts + i] = last_entropy[i]; - last_entropy[i] = combined_entropy[num_contexts + i]; - HistogramClearLiteral(&histograms[self->curr_histogram_ix_ + i]); - } - ++self->num_blocks_; - self->block_size_ = 0; - self->merge_last_count_ = 0; - self->target_block_size_ = self->min_block_size_; - } else { - /* Combine this block with last block. */ - split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_; - for (i = 0; i < num_contexts; ++i) { - histograms[self->last_histogram_ix_[0] + i] = combined_histo[i]; - last_entropy[i] = combined_entropy[i]; - if (split->num_types == 1) { - last_entropy[num_contexts + i] = last_entropy[i]; - } - HistogramClearLiteral(&histograms[self->curr_histogram_ix_ + i]); - } - self->block_size_ = 0; - if (++self->merge_last_count_ > 1) { - self->target_block_size_ += self->min_block_size_; - } - } - BROTLI_FREE(m, combined_histo); - } - if (is_final) { - *self->histograms_size_ = split->num_types * num_contexts; - split->num_blocks = self->num_blocks_; - } -} - -/* Adds the next symbol to the current block type and context. When the - current block reaches the target size, decides on merging the block. */ -static void ContextBlockSplitterAddSymbol( - ContextBlockSplitter* self, MemoryManager* m, - size_t symbol, size_t context) { - HistogramAddLiteral(&self->histograms_[self->curr_histogram_ix_ + context], - symbol); - ++self->block_size_; - if (self->block_size_ == self->target_block_size_) { - ContextBlockSplitterFinishBlock(self, m, /* is_final = */ BROTLI_FALSE); - if (BROTLI_IS_OOM(m)) return; - } -} - -static void MapStaticContexts(MemoryManager* m, - size_t num_contexts, - const uint32_t* static_context_map, - MetaBlockSplit* mb) { - size_t i; - BROTLI_DCHECK(mb->literal_context_map == 0); - mb->literal_context_map_size = - mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS; - mb->literal_context_map = - BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->literal_context_map)) return; - - for (i = 0; i < mb->literal_split.num_types; ++i) { - uint32_t offset = (uint32_t)(i * num_contexts); - size_t j; - for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS); ++j) { - mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] = - offset + static_context_map[j]; - } - } -} - -typedef struct GreedyMetablockArena { - union { - BlockSplitterLiteral plain; - ContextBlockSplitter ctx; - } lit_blocks; - BlockSplitterCommand cmd_blocks; - BlockSplitterDistance dist_blocks; -} GreedyMetablockArena; - -static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal( - MemoryManager* m, GreedyMetablockArena* arena, const uint8_t* ringbuffer, - size_t pos, size_t mask, uint8_t prev_byte, uint8_t prev_byte2, - ContextLut literal_context_lut, const size_t num_contexts, - const uint32_t* static_context_map, const Command* commands, - size_t n_commands, MetaBlockSplit* mb) { - size_t num_literals = 0; - size_t i; - for (i = 0; i < n_commands; ++i) { - num_literals += commands[i].insert_len_; - } - - if (num_contexts == 1) { - InitBlockSplitterLiteral(m, &arena->lit_blocks.plain, 256, 512, 400.0, - num_literals, &mb->literal_split, &mb->literal_histograms, - &mb->literal_histograms_size); - } else { - InitContextBlockSplitter(m, &arena->lit_blocks.ctx, 256, num_contexts, 512, - 400.0, num_literals, &mb->literal_split, &mb->literal_histograms, - &mb->literal_histograms_size); - } - if (BROTLI_IS_OOM(m)) return; - InitBlockSplitterCommand(m, &arena->cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, - 1024, 500.0, n_commands, &mb->command_split, &mb->command_histograms, - &mb->command_histograms_size); - if (BROTLI_IS_OOM(m)) return; - InitBlockSplitterDistance(m, &arena->dist_blocks, 64, 512, 100.0, n_commands, - &mb->distance_split, &mb->distance_histograms, - &mb->distance_histograms_size); - if (BROTLI_IS_OOM(m)) return; - - for (i = 0; i < n_commands; ++i) { - const Command cmd = commands[i]; - size_t j; - BlockSplitterAddSymbolCommand(&arena->cmd_blocks, cmd.cmd_prefix_); - for (j = cmd.insert_len_; j != 0; --j) { - uint8_t literal = ringbuffer[pos & mask]; - if (num_contexts == 1) { - BlockSplitterAddSymbolLiteral(&arena->lit_blocks.plain, literal); - } else { - size_t context = - BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut); - ContextBlockSplitterAddSymbol(&arena->lit_blocks.ctx, m, literal, - static_context_map[context]); - if (BROTLI_IS_OOM(m)) return; - } - prev_byte2 = prev_byte; - prev_byte = literal; - ++pos; - } - pos += CommandCopyLen(&cmd); - if (CommandCopyLen(&cmd)) { - prev_byte2 = ringbuffer[(pos - 2) & mask]; - prev_byte = ringbuffer[(pos - 1) & mask]; - if (cmd.cmd_prefix_ >= 128) { - BlockSplitterAddSymbolDistance( - &arena->dist_blocks, cmd.dist_prefix_ & 0x3FF); - } - } - } - - if (num_contexts == 1) { - BlockSplitterFinishBlockLiteral( - &arena->lit_blocks.plain, /* is_final = */ BROTLI_TRUE); - } else { - ContextBlockSplitterFinishBlock( - &arena->lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE); - if (BROTLI_IS_OOM(m)) return; - } - BlockSplitterFinishBlockCommand( - &arena->cmd_blocks, /* is_final = */ BROTLI_TRUE); - BlockSplitterFinishBlockDistance( - &arena->dist_blocks, /* is_final = */ BROTLI_TRUE); - - if (num_contexts > 1) { - MapStaticContexts(m, num_contexts, static_context_map, mb); - } -} - -void BrotliBuildMetaBlockGreedy(MemoryManager* m, - const uint8_t* ringbuffer, - size_t pos, - size_t mask, - uint8_t prev_byte, - uint8_t prev_byte2, - ContextLut literal_context_lut, - size_t num_contexts, - const uint32_t* static_context_map, - const Command* commands, - size_t n_commands, - MetaBlockSplit* mb) { - GreedyMetablockArena* arena = BROTLI_ALLOC(m, GreedyMetablockArena, 1); - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; - if (num_contexts == 1) { - BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask, - prev_byte, prev_byte2, literal_context_lut, 1, NULL, commands, - n_commands, mb); - } else { - BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask, - prev_byte, prev_byte2, literal_context_lut, num_contexts, - static_context_map, commands, n_commands, mb); - } - BROTLI_FREE(m, arena); -} - -void BrotliOptimizeHistograms(uint32_t num_distance_codes, - MetaBlockSplit* mb) { - uint8_t good_for_rle[BROTLI_NUM_COMMAND_SYMBOLS]; - size_t i; - for (i = 0; i < mb->literal_histograms_size; ++i) { - BrotliOptimizeHuffmanCountsForRle(256, mb->literal_histograms[i].data_, - good_for_rle); - } - for (i = 0; i < mb->command_histograms_size; ++i) { - BrotliOptimizeHuffmanCountsForRle(BROTLI_NUM_COMMAND_SYMBOLS, - mb->command_histograms[i].data_, - good_for_rle); - } - for (i = 0; i < mb->distance_histograms_size; ++i) { - BrotliOptimizeHuffmanCountsForRle(num_distance_codes, - mb->distance_histograms[i].data_, - good_for_rle); - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/metablock.h b/src/deps/brotli/enc/metablock.h deleted file mode 100644 index db38f8fd217a9..0000000000000 --- a/src/deps/brotli/enc/metablock.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Algorithms for distributing the literals and commands of a metablock between - block types and contexts. */ - -#ifndef BROTLI_ENC_METABLOCK_H_ -#define BROTLI_ENC_METABLOCK_H_ - -#include - -#include "../common/context.h" -#include "../common/platform.h" -#include "block_splitter.h" -#include "command.h" -#include "histogram.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct MetaBlockSplit { - BlockSplit literal_split; - BlockSplit command_split; - BlockSplit distance_split; - uint32_t* literal_context_map; - size_t literal_context_map_size; - uint32_t* distance_context_map; - size_t distance_context_map_size; - HistogramLiteral* literal_histograms; - size_t literal_histograms_size; - HistogramCommand* command_histograms; - size_t command_histograms_size; - HistogramDistance* distance_histograms; - size_t distance_histograms_size; -} MetaBlockSplit; - -static BROTLI_INLINE void InitMetaBlockSplit(MetaBlockSplit* mb) { - BrotliInitBlockSplit(&mb->literal_split); - BrotliInitBlockSplit(&mb->command_split); - BrotliInitBlockSplit(&mb->distance_split); - mb->literal_context_map = 0; - mb->literal_context_map_size = 0; - mb->distance_context_map = 0; - mb->distance_context_map_size = 0; - mb->literal_histograms = 0; - mb->literal_histograms_size = 0; - mb->command_histograms = 0; - mb->command_histograms_size = 0; - mb->distance_histograms = 0; - mb->distance_histograms_size = 0; -} - -static BROTLI_INLINE void DestroyMetaBlockSplit( - MemoryManager* m, MetaBlockSplit* mb) { - BrotliDestroyBlockSplit(m, &mb->literal_split); - BrotliDestroyBlockSplit(m, &mb->command_split); - BrotliDestroyBlockSplit(m, &mb->distance_split); - BROTLI_FREE(m, mb->literal_context_map); - BROTLI_FREE(m, mb->distance_context_map); - BROTLI_FREE(m, mb->literal_histograms); - BROTLI_FREE(m, mb->command_histograms); - BROTLI_FREE(m, mb->distance_histograms); -} - -/* Uses the slow shortest-path block splitter and does context clustering. - The distance parameters are dynamically selected based on the commands - which get recomputed under the new distance parameters. The new distance - parameters are stored into *params. */ -BROTLI_INTERNAL void BrotliBuildMetaBlock(MemoryManager* m, - const uint8_t* ringbuffer, - const size_t pos, - const size_t mask, - BrotliEncoderParams* params, - uint8_t prev_byte, - uint8_t prev_byte2, - Command* cmds, - size_t num_commands, - ContextType literal_context_mode, - MetaBlockSplit* mb); - -/* Uses a fast greedy block splitter that tries to merge current block with the - last or the second last block and uses a static context clustering which - is the same for all block types. */ -BROTLI_INTERNAL void BrotliBuildMetaBlockGreedy( - MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask, - uint8_t prev_byte, uint8_t prev_byte2, ContextLut literal_context_lut, - size_t num_contexts, const uint32_t* static_context_map, - const Command* commands, size_t n_commands, MetaBlockSplit* mb); - -BROTLI_INTERNAL void BrotliOptimizeHistograms(uint32_t num_distance_codes, - MetaBlockSplit* mb); - -BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliDistanceParams* params, - uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_METABLOCK_H_ */ diff --git a/src/deps/brotli/enc/metablock_inc.h b/src/deps/brotli/enc/metablock_inc.h deleted file mode 100644 index f9393869ab1a6..0000000000000 --- a/src/deps/brotli/enc/metablock_inc.h +++ /dev/null @@ -1,185 +0,0 @@ -/* NOLINT(build/header_guard) */ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* template parameters: FN */ - -#define HistogramType FN(Histogram) - -/* Greedy block splitter for one block category (literal, command or distance). -*/ -typedef struct FN(BlockSplitter) { - /* Alphabet size of particular block category. */ - size_t alphabet_size_; - /* We collect at least this many symbols for each block. */ - size_t min_block_size_; - /* We merge histograms A and B if - entropy(A+B) < entropy(A) + entropy(B) + split_threshold_, - where A is the current histogram and B is the histogram of the last or the - second last block type. */ - double split_threshold_; - - size_t num_blocks_; - BlockSplit* split_; /* not owned */ - HistogramType* histograms_; /* not owned */ - size_t* histograms_size_; /* not owned */ - - /* Temporary storage for BlockSplitterFinishBlock. */ - HistogramType combined_histo[2]; - - /* The number of symbols that we want to collect before deciding on whether - or not to merge the block with a previous one or emit a new block. */ - size_t target_block_size_; - /* The number of symbols in the current histogram. */ - size_t block_size_; - /* Offset of the current histogram. */ - size_t curr_histogram_ix_; - /* Offset of the histograms of the previous two block types. */ - size_t last_histogram_ix_[2]; - /* Entropy of the previous two block types. */ - double last_entropy_[2]; - /* The number of times we merged the current block with the last one. */ - size_t merge_last_count_; -} FN(BlockSplitter); - -static void FN(InitBlockSplitter)( - MemoryManager* m, FN(BlockSplitter)* self, size_t alphabet_size, - size_t min_block_size, double split_threshold, size_t num_symbols, - BlockSplit* split, HistogramType** histograms, size_t* histograms_size) { - size_t max_num_blocks = num_symbols / min_block_size + 1; - /* We have to allocate one more histogram than the maximum number of block - types for the current histogram when the meta-block is too big. */ - size_t max_num_types = - BROTLI_MIN(size_t, max_num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + 1); - self->alphabet_size_ = alphabet_size; - self->min_block_size_ = min_block_size; - self->split_threshold_ = split_threshold; - self->num_blocks_ = 0; - self->split_ = split; - self->histograms_size_ = histograms_size; - self->target_block_size_ = min_block_size; - self->block_size_ = 0; - self->curr_histogram_ix_ = 0; - self->merge_last_count_ = 0; - BROTLI_ENSURE_CAPACITY(m, uint8_t, - split->types, split->types_alloc_size, max_num_blocks); - BROTLI_ENSURE_CAPACITY(m, uint32_t, - split->lengths, split->lengths_alloc_size, max_num_blocks); - if (BROTLI_IS_OOM(m)) return; - self->split_->num_blocks = max_num_blocks; - BROTLI_DCHECK(*histograms == 0); - *histograms_size = max_num_types; - *histograms = BROTLI_ALLOC(m, HistogramType, *histograms_size); - self->histograms_ = *histograms; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(*histograms)) return; - /* Clear only current histogram. */ - FN(HistogramClear)(&self->histograms_[0]); - self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0; -} - -/* Does either of three things: - (1) emits the current block with a new block type; - (2) emits the current block with the type of the second last block; - (3) merges the current block with the last block. */ -static void FN(BlockSplitterFinishBlock)( - FN(BlockSplitter)* self, BROTLI_BOOL is_final) { - BlockSplit* split = self->split_; - double* last_entropy = self->last_entropy_; - HistogramType* histograms = self->histograms_; - self->block_size_ = - BROTLI_MAX(size_t, self->block_size_, self->min_block_size_); - if (self->num_blocks_ == 0) { - /* Create first block. */ - split->lengths[0] = (uint32_t)self->block_size_; - split->types[0] = 0; - last_entropy[0] = - BitsEntropy(histograms[0].data_, self->alphabet_size_); - last_entropy[1] = last_entropy[0]; - ++self->num_blocks_; - ++split->num_types; - ++self->curr_histogram_ix_; - if (self->curr_histogram_ix_ < *self->histograms_size_) - FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); - self->block_size_ = 0; - } else if (self->block_size_ > 0) { - double entropy = BitsEntropy(histograms[self->curr_histogram_ix_].data_, - self->alphabet_size_); - double combined_entropy[2]; - double diff[2]; - size_t j; - for (j = 0; j < 2; ++j) { - size_t last_histogram_ix = self->last_histogram_ix_[j]; - self->combined_histo[j] = histograms[self->curr_histogram_ix_]; - FN(HistogramAddHistogram)(&self->combined_histo[j], - &histograms[last_histogram_ix]); - combined_entropy[j] = BitsEntropy( - &self->combined_histo[j].data_[0], self->alphabet_size_); - diff[j] = combined_entropy[j] - entropy - last_entropy[j]; - } - - if (split->num_types < BROTLI_MAX_NUMBER_OF_BLOCK_TYPES && - diff[0] > self->split_threshold_ && - diff[1] > self->split_threshold_) { - /* Create new block. */ - split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; - split->types[self->num_blocks_] = (uint8_t)split->num_types; - self->last_histogram_ix_[1] = self->last_histogram_ix_[0]; - self->last_histogram_ix_[0] = (uint8_t)split->num_types; - last_entropy[1] = last_entropy[0]; - last_entropy[0] = entropy; - ++self->num_blocks_; - ++split->num_types; - ++self->curr_histogram_ix_; - if (self->curr_histogram_ix_ < *self->histograms_size_) - FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); - self->block_size_ = 0; - self->merge_last_count_ = 0; - self->target_block_size_ = self->min_block_size_; - } else if (diff[1] < diff[0] - 20.0) { - /* Combine this block with second last block. */ - split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; - split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; - BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1); - histograms[self->last_histogram_ix_[0]] = self->combined_histo[1]; - last_entropy[1] = last_entropy[0]; - last_entropy[0] = combined_entropy[1]; - ++self->num_blocks_; - self->block_size_ = 0; - FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); - self->merge_last_count_ = 0; - self->target_block_size_ = self->min_block_size_; - } else { - /* Combine this block with last block. */ - split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_; - histograms[self->last_histogram_ix_[0]] = self->combined_histo[0]; - last_entropy[0] = combined_entropy[0]; - if (split->num_types == 1) { - last_entropy[1] = last_entropy[0]; - } - self->block_size_ = 0; - FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); - if (++self->merge_last_count_ > 1) { - self->target_block_size_ += self->min_block_size_; - } - } - } - if (is_final) { - *self->histograms_size_ = split->num_types; - split->num_blocks = self->num_blocks_; - } -} - -/* Adds the next symbol to the current histogram. When the current histogram - reaches the target size, decides on merging the block. */ -static void FN(BlockSplitterAddSymbol)(FN(BlockSplitter)* self, size_t symbol) { - FN(HistogramAdd)(&self->histograms_[self->curr_histogram_ix_], symbol); - ++self->block_size_; - if (self->block_size_ == self->target_block_size_) { - FN(BlockSplitterFinishBlock)(self, /* is_final = */ BROTLI_FALSE); - } -} - -#undef HistogramType diff --git a/src/deps/brotli/enc/params.h b/src/deps/brotli/enc/params.h deleted file mode 100644 index 78b2ab60828ef..0000000000000 --- a/src/deps/brotli/enc/params.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Parameters for the Brotli encoder with chosen quality levels. */ - -#ifndef BROTLI_ENC_PARAMS_H_ -#define BROTLI_ENC_PARAMS_H_ - -#include - -#include "encoder_dict.h" - -typedef struct BrotliHasherParams { - int type; - int bucket_bits; - int block_bits; - int num_last_distances_to_check; -} BrotliHasherParams; - -typedef struct BrotliDistanceParams { - uint32_t distance_postfix_bits; - uint32_t num_direct_distance_codes; - uint32_t alphabet_size_max; - uint32_t alphabet_size_limit; - size_t max_distance; -} BrotliDistanceParams; - -/* Encoding parameters */ -typedef struct BrotliEncoderParams { - BrotliEncoderMode mode; - int quality; - int lgwin; - int lgblock; - size_t stream_offset; - size_t size_hint; - BROTLI_BOOL disable_literal_context_modeling; - BROTLI_BOOL large_window; - BrotliHasherParams hasher; - BrotliDistanceParams dist; - /* TODO(eustas): rename to BrotliShared... */ - SharedEncoderDictionary dictionary; -} BrotliEncoderParams; - -#endif /* BROTLI_ENC_PARAMS_H_ */ diff --git a/src/deps/brotli/enc/prefix.h b/src/deps/brotli/enc/prefix.h deleted file mode 100644 index 0f006f1614f7e..0000000000000 --- a/src/deps/brotli/enc/prefix.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions for encoding of integers into prefix codes the amount of extra - bits, and the actual values of the extra bits. */ - -#ifndef BROTLI_ENC_PREFIX_H_ -#define BROTLI_ENC_PREFIX_H_ - -#include - -#include "../common/constants.h" -#include "../common/platform.h" -#include "fast_log.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* Here distance_code is an intermediate code, i.e. one of the special codes or - the actual distance increased by BROTLI_NUM_DISTANCE_SHORT_CODES - 1. */ -static BROTLI_INLINE void PrefixEncodeCopyDistance(size_t distance_code, - size_t num_direct_codes, - size_t postfix_bits, - uint16_t* code, - uint32_t* extra_bits) { - if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes) { - *code = (uint16_t)distance_code; - *extra_bits = 0; - return; - } else { - size_t dist = ((size_t)1 << (postfix_bits + 2u)) + - (distance_code - BROTLI_NUM_DISTANCE_SHORT_CODES - num_direct_codes); - size_t bucket = Log2FloorNonZero(dist) - 1; - size_t postfix_mask = (1u << postfix_bits) - 1; - size_t postfix = dist & postfix_mask; - size_t prefix = (dist >> bucket) & 1; - size_t offset = (2 + prefix) << bucket; - size_t nbits = bucket - postfix_bits; - *code = (uint16_t)((nbits << 10) | - (BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes + - ((2 * (nbits - 1) + prefix) << postfix_bits) + postfix)); - *extra_bits = (uint32_t)((dist - offset) >> postfix_bits); - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_PREFIX_H_ */ diff --git a/src/deps/brotli/enc/quality.h b/src/deps/brotli/enc/quality.h deleted file mode 100644 index ffdfd72fb4a17..0000000000000 --- a/src/deps/brotli/enc/quality.h +++ /dev/null @@ -1,202 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Constants and formulas that affect speed-ratio trade-offs and thus define - quality levels. */ - -#ifndef BROTLI_ENC_QUALITY_H_ -#define BROTLI_ENC_QUALITY_H_ - -#include - -#include "../common/platform.h" -#include "params.h" - -#define FAST_ONE_PASS_COMPRESSION_QUALITY 0 -#define FAST_TWO_PASS_COMPRESSION_QUALITY 1 -#define ZOPFLIFICATION_QUALITY 10 -#define HQ_ZOPFLIFICATION_QUALITY 11 - -#define MAX_QUALITY_FOR_STATIC_ENTROPY_CODES 2 -#define MIN_QUALITY_FOR_BLOCK_SPLIT 4 -#define MIN_QUALITY_FOR_NONZERO_DISTANCE_PARAMS 4 -#define MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS 4 -#define MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH 5 -#define MIN_QUALITY_FOR_CONTEXT_MODELING 5 -#define MIN_QUALITY_FOR_HQ_CONTEXT_MODELING 7 -#define MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING 10 - -/* For quality below MIN_QUALITY_FOR_BLOCK_SPLIT there is no block splitting, - so we buffer at most this much literals and commands. */ -#define MAX_NUM_DELAYED_SYMBOLS 0x2FFF - -/* Returns hash-table size for quality levels 0 and 1. */ -static BROTLI_INLINE size_t MaxHashTableSize(int quality) { - return quality == FAST_ONE_PASS_COMPRESSION_QUALITY ? 1 << 15 : 1 << 17; -} - -/* The maximum length for which the zopflification uses distinct distances. */ -#define MAX_ZOPFLI_LEN_QUALITY_10 150 -#define MAX_ZOPFLI_LEN_QUALITY_11 325 - -/* Do not thoroughly search when a long copy is found. */ -#define BROTLI_LONG_COPY_QUICK_STEP 16384 - -static BROTLI_INLINE size_t MaxZopfliLen(const BrotliEncoderParams* params) { - return params->quality <= 10 ? - MAX_ZOPFLI_LEN_QUALITY_10 : - MAX_ZOPFLI_LEN_QUALITY_11; -} - -/* Number of best candidates to evaluate to expand Zopfli chain. */ -static BROTLI_INLINE size_t MaxZopfliCandidates( - const BrotliEncoderParams* params) { - return params->quality <= 10 ? 1 : 5; -} - -static BROTLI_INLINE void SanitizeParams(BrotliEncoderParams* params) { - params->quality = BROTLI_MIN(int, BROTLI_MAX_QUALITY, - BROTLI_MAX(int, BROTLI_MIN_QUALITY, params->quality)); - if (params->quality <= MAX_QUALITY_FOR_STATIC_ENTROPY_CODES) { - params->large_window = BROTLI_FALSE; - } - if (params->lgwin < BROTLI_MIN_WINDOW_BITS) { - params->lgwin = BROTLI_MIN_WINDOW_BITS; - } else { - int max_lgwin = params->large_window ? BROTLI_LARGE_MAX_WINDOW_BITS : - BROTLI_MAX_WINDOW_BITS; - if (params->lgwin > max_lgwin) params->lgwin = max_lgwin; - } -} - -/* Returns optimized lg_block value. */ -static BROTLI_INLINE int ComputeLgBlock(const BrotliEncoderParams* params) { - int lgblock = params->lgblock; - if (params->quality == FAST_ONE_PASS_COMPRESSION_QUALITY || - params->quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { - lgblock = params->lgwin; - } else if (params->quality < MIN_QUALITY_FOR_BLOCK_SPLIT) { - lgblock = 14; - } else if (lgblock == 0) { - lgblock = 16; - if (params->quality >= 9 && params->lgwin > lgblock) { - lgblock = BROTLI_MIN(int, 18, params->lgwin); - } - } else { - lgblock = BROTLI_MIN(int, BROTLI_MAX_INPUT_BLOCK_BITS, - BROTLI_MAX(int, BROTLI_MIN_INPUT_BLOCK_BITS, lgblock)); - } - return lgblock; -} - -/* Returns log2 of the size of main ring buffer area. - Allocate at least lgwin + 1 bits for the ring buffer so that the newly - added block fits there completely and we still get lgwin bits and at least - read_block_size_bits + 1 bits because the copy tail length needs to be - smaller than ring-buffer size. */ -static BROTLI_INLINE int ComputeRbBits(const BrotliEncoderParams* params) { - return 1 + BROTLI_MAX(int, params->lgwin, params->lgblock); -} - -static BROTLI_INLINE size_t MaxMetablockSize( - const BrotliEncoderParams* params) { - int bits = - BROTLI_MIN(int, ComputeRbBits(params), BROTLI_MAX_INPUT_BLOCK_BITS); - return (size_t)1 << bits; -} - -/* When searching for backward references and have not seen matches for a long - time, we can skip some match lookups. Unsuccessful match lookups are very - expensive and this kind of a heuristic speeds up compression quite a lot. - At first 8 byte strides are taken and every second byte is put to hasher. - After 4x more literals stride by 16 bytes, every put 4-th byte to hasher. - Applied only to qualities 2 to 9. */ -static BROTLI_INLINE size_t LiteralSpreeLengthForSparseSearch( - const BrotliEncoderParams* params) { - return params->quality < 9 ? 64 : 512; -} - -/* Quality to hasher mapping: - - - q02: h02 (longest_match_quickly), b16, l5 - - - q03: h03 (longest_match_quickly), b17, l5 - - - q04: h04 (longest_match_quickly), b17, l5 - - q04: h54 (longest_match_quickly), b20, l7 | for large files - - - q05: h05 (longest_match ), b14, l4 - - q05: h06 (longest_match64 ), b15, l5 | for large files - - q05: h40 (forgetful_chain ), b15, l4 | for small window - - - q06: h05 (longest_match ), b14, l4 - - q06: h06 (longest_match64 ), b15, l5 | for large files - - q06: h40 (forgetful_chain ), b15, l4 | for small window - - - q07: h05 (longest_match ), b15, l4 - - q07: h06 (longest_match64 ), b15, l5 | for large files - - q07: h41 (forgetful_chain ), b15, l4 | for small window - - - q08: h05 (longest_match ), b15, l4 - - q08: h06 (longest_match64 ), b15, l5 | for large files - - q08: h41 (forgetful_chain ), b15, l4 | for small window - - - q09: h05 (longest_match ), b15, l4 - - q09: h06 (longest_match64 ), b15, l5 | for large files - - q09: h42 (forgetful_chain ), b15, l4 | for small window - - - q10: t10 (to_binary_tree ), b17, l128 - - - q11: t10 (to_binary_tree ), b17, l128 - - Where "q" is quality, "h" is hasher type, "b" is bucket bits, - "l" is source len. */ -static BROTLI_INLINE void ChooseHasher(const BrotliEncoderParams* params, - BrotliHasherParams* hparams) { - if (params->quality > 9) { - hparams->type = 10; - } else if (params->quality == 4 && params->size_hint >= (1 << 20)) { - hparams->type = 54; - } else if (params->quality < 5) { - hparams->type = params->quality; - } else if (params->lgwin <= 16) { - hparams->type = params->quality < 7 ? 40 : params->quality < 9 ? 41 : 42; - } else if (params->size_hint >= (1 << 20) && params->lgwin >= 19) { - hparams->type = 6; - hparams->block_bits = params->quality - 1; - hparams->bucket_bits = 15; - hparams->num_last_distances_to_check = - params->quality < 7 ? 4 : params->quality < 9 ? 10 : 16; - } else { - /* TODO(eustas): often previous setting (H6) is faster and denser; consider - adding an option to use it. */ - hparams->type = 5; - hparams->block_bits = params->quality - 1; - hparams->bucket_bits = params->quality < 7 ? 14 : 15; - hparams->num_last_distances_to_check = - params->quality < 7 ? 4 : params->quality < 9 ? 10 : 16; - } - - if (params->lgwin > 24) { - /* Different hashers for large window brotli: not for qualities <= 2, - these are too fast for large window. Not for qualities >= 10: their - hasher already works well with large window. So the changes are: - H3 --> H35: for quality 3. - H54 --> H55: for quality 4 with size hint > 1MB - H6 --> H65: for qualities 5, 6, 7, 8, 9. */ - if (hparams->type == 3) { - hparams->type = 35; - } - if (hparams->type == 54) { - hparams->type = 55; - } - if (hparams->type == 6) { - hparams->type = 65; - } - } -} - -#endif /* BROTLI_ENC_QUALITY_H_ */ diff --git a/src/deps/brotli/enc/ringbuffer.h b/src/deps/brotli/enc/ringbuffer.h deleted file mode 100644 index 27245b7f3906e..0000000000000 --- a/src/deps/brotli/enc/ringbuffer.h +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Sliding window over the input data. */ - -#ifndef BROTLI_ENC_RINGBUFFER_H_ -#define BROTLI_ENC_RINGBUFFER_H_ - -#include /* memcpy */ - -#include - -#include "../common/platform.h" -#include "memory.h" -#include "quality.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* A RingBuffer(window_bits, tail_bits) contains `1 << window_bits' bytes of - data in a circular manner: writing a byte writes it to: - `position() % (1 << window_bits)'. - For convenience, the RingBuffer array contains another copy of the - first `1 << tail_bits' bytes: - buffer_[i] == buffer_[i + (1 << window_bits)], if i < (1 << tail_bits), - and another copy of the last two bytes: - buffer_[-1] == buffer_[(1 << window_bits) - 1] and - buffer_[-2] == buffer_[(1 << window_bits) - 2]. */ -typedef struct RingBuffer { - /* Size of the ring-buffer is (1 << window_bits) + tail_size_. */ - const uint32_t size_; - const uint32_t mask_; - const uint32_t tail_size_; - const uint32_t total_size_; - - uint32_t cur_size_; - /* Position to write in the ring buffer. */ - uint32_t pos_; - /* The actual ring buffer containing the copy of the last two bytes, the data, - and the copy of the beginning as a tail. */ - uint8_t* data_; - /* The start of the ring-buffer. */ - uint8_t* buffer_; -} RingBuffer; - -static BROTLI_INLINE void RingBufferInit(RingBuffer* rb) { - rb->cur_size_ = 0; - rb->pos_ = 0; - rb->data_ = 0; - rb->buffer_ = 0; -} - -static BROTLI_INLINE void RingBufferSetup( - const BrotliEncoderParams* params, RingBuffer* rb) { - int window_bits = ComputeRbBits(params); - int tail_bits = params->lgblock; - *(uint32_t*)&rb->size_ = 1u << window_bits; - *(uint32_t*)&rb->mask_ = (1u << window_bits) - 1; - *(uint32_t*)&rb->tail_size_ = 1u << tail_bits; - *(uint32_t*)&rb->total_size_ = rb->size_ + rb->tail_size_; -} - -static BROTLI_INLINE void RingBufferFree(MemoryManager* m, RingBuffer* rb) { - BROTLI_FREE(m, rb->data_); -} - -/* Allocates or re-allocates data_ to the given length + plus some slack - region before and after. Fills the slack regions with zeros. */ -static BROTLI_INLINE void RingBufferInitBuffer( - MemoryManager* m, const uint32_t buflen, RingBuffer* rb) { - static const size_t kSlackForEightByteHashingEverywhere = 7; - uint8_t* new_data = BROTLI_ALLOC( - m, uint8_t, 2 + buflen + kSlackForEightByteHashingEverywhere); - size_t i; - if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_data)) return; - if (rb->data_) { - memcpy(new_data, rb->data_, - 2 + rb->cur_size_ + kSlackForEightByteHashingEverywhere); - BROTLI_FREE(m, rb->data_); - } - rb->data_ = new_data; - rb->cur_size_ = buflen; - rb->buffer_ = rb->data_ + 2; - rb->buffer_[-2] = rb->buffer_[-1] = 0; - for (i = 0; i < kSlackForEightByteHashingEverywhere; ++i) { - rb->buffer_[rb->cur_size_ + i] = 0; - } -} - -static BROTLI_INLINE void RingBufferWriteTail( - const uint8_t* bytes, size_t n, RingBuffer* rb) { - const size_t masked_pos = rb->pos_ & rb->mask_; - if (BROTLI_PREDICT_FALSE(masked_pos < rb->tail_size_)) { - /* Just fill the tail buffer with the beginning data. */ - const size_t p = rb->size_ + masked_pos; - memcpy(&rb->buffer_[p], bytes, - BROTLI_MIN(size_t, n, rb->tail_size_ - masked_pos)); - } -} - -/* Push bytes into the ring buffer. */ -static BROTLI_INLINE void RingBufferWrite( - MemoryManager* m, const uint8_t* bytes, size_t n, RingBuffer* rb) { - if (rb->pos_ == 0 && n < rb->tail_size_) { - /* Special case for the first write: to process the first block, we don't - need to allocate the whole ring-buffer and we don't need the tail - either. However, we do this memory usage optimization only if the - first write is less than the tail size, which is also the input block - size, otherwise it is likely that other blocks will follow and we - will need to reallocate to the full size anyway. */ - rb->pos_ = (uint32_t)n; - RingBufferInitBuffer(m, rb->pos_, rb); - if (BROTLI_IS_OOM(m)) return; - memcpy(rb->buffer_, bytes, n); - return; - } - if (rb->cur_size_ < rb->total_size_) { - /* Lazily allocate the full buffer. */ - RingBufferInitBuffer(m, rb->total_size_, rb); - if (BROTLI_IS_OOM(m)) return; - /* Initialize the last two bytes to zero, so that we don't have to worry - later when we copy the last two bytes to the first two positions. */ - rb->buffer_[rb->size_ - 2] = 0; - rb->buffer_[rb->size_ - 1] = 0; - /* Initialize tail; might be touched by "best_len++" optimization when - ring buffer is "full". */ - rb->buffer_[rb->size_] = 241; - } - { - const size_t masked_pos = rb->pos_ & rb->mask_; - /* The length of the writes is limited so that we do not need to worry - about a write */ - RingBufferWriteTail(bytes, n, rb); - if (BROTLI_PREDICT_TRUE(masked_pos + n <= rb->size_)) { - /* A single write fits. */ - memcpy(&rb->buffer_[masked_pos], bytes, n); - } else { - /* Split into two writes. - Copy into the end of the buffer, including the tail buffer. */ - memcpy(&rb->buffer_[masked_pos], bytes, - BROTLI_MIN(size_t, n, rb->total_size_ - masked_pos)); - /* Copy into the beginning of the buffer */ - memcpy(&rb->buffer_[0], bytes + (rb->size_ - masked_pos), - n - (rb->size_ - masked_pos)); - } - } - { - BROTLI_BOOL not_first_lap = (rb->pos_ & (1u << 31)) != 0; - uint32_t rb_pos_mask = (1u << 31) - 1; - rb->buffer_[-2] = rb->buffer_[rb->size_ - 2]; - rb->buffer_[-1] = rb->buffer_[rb->size_ - 1]; - rb->pos_ = (rb->pos_ & rb_pos_mask) + (uint32_t)(n & rb_pos_mask); - if (not_first_lap) { - /* Wrap, but preserve not-a-first-lap feature. */ - rb->pos_ |= 1u << 31; - } - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_RINGBUFFER_H_ */ diff --git a/src/deps/brotli/enc/state.h b/src/deps/brotli/enc/state.h deleted file mode 100644 index cb82987701ffb..0000000000000 --- a/src/deps/brotli/enc/state.h +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright 2022 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Encoder state. */ - -#ifndef BROTLI_ENC_STATE_H_ -#define BROTLI_ENC_STATE_H_ - -#include - -#include "command.h" -#include "compress_fragment.h" -#include "compress_fragment_two_pass.h" -#include "hash.h" -#include "memory.h" -#include "params.h" -#include "ringbuffer.h" - -typedef enum BrotliEncoderStreamState { - /* Default state. */ - BROTLI_STREAM_PROCESSING = 0, - /* Intermediate state; after next block is emitted, byte-padding should be - performed before getting back to default state. */ - BROTLI_STREAM_FLUSH_REQUESTED = 1, - /* Last metablock was produced; no more input is acceptable. */ - BROTLI_STREAM_FINISHED = 2, - /* Flushing compressed block and writing meta-data block header. */ - BROTLI_STREAM_METADATA_HEAD = 3, - /* Writing metadata block body. */ - BROTLI_STREAM_METADATA_BODY = 4 -} BrotliEncoderStreamState; - -typedef enum BrotliEncoderFlintState { - BROTLI_FLINT_NEEDS_2_BYTES = 2, - BROTLI_FLINT_NEEDS_1_BYTE = 1, - BROTLI_FLINT_WAITING_FOR_PROCESSING = 0, - BROTLI_FLINT_WAITING_FOR_FLUSHING = -1, - BROTLI_FLINT_DONE = -2 -} BrotliEncoderFlintState; - -typedef struct BrotliEncoderStateStruct { - BrotliEncoderParams params; - - MemoryManager memory_manager_; - - uint64_t input_pos_; - RingBuffer ringbuffer_; - size_t cmd_alloc_size_; - Command* commands_; - size_t num_commands_; - size_t num_literals_; - size_t last_insert_len_; - uint64_t last_flush_pos_; - uint64_t last_processed_pos_; - int dist_cache_[BROTLI_NUM_DISTANCE_SHORT_CODES]; - int saved_dist_cache_[4]; - uint16_t last_bytes_; - uint8_t last_bytes_bits_; - /* "Flint" is a tiny uncompressed block emitted before the continuation - block to unwire literal context from previous data. Despite being int8_t, - field is actually BrotliEncoderFlintState enum. */ - int8_t flint_; - uint8_t prev_byte_; - uint8_t prev_byte2_; - size_t storage_size_; - uint8_t* storage_; - - Hasher hasher_; - - /* Hash table for FAST_ONE_PASS_COMPRESSION_QUALITY mode. */ - int small_table_[1 << 10]; /* 4KiB */ - int* large_table_; /* Allocated only when needed */ - size_t large_table_size_; - - BrotliOnePassArena* one_pass_arena_; - BrotliTwoPassArena* two_pass_arena_; - - /* Command and literal buffers for FAST_TWO_PASS_COMPRESSION_QUALITY. */ - uint32_t* command_buf_; - uint8_t* literal_buf_; - - uint64_t total_in_; - uint8_t* next_out_; - size_t available_out_; - uint64_t total_out_; - /* Temporary buffer for padding flush bits or metadata block header / body. */ - union { - uint64_t u64[2]; - uint8_t u8[16]; - } tiny_buf_; - uint32_t remaining_metadata_bytes_; - BrotliEncoderStreamState stream_state_; - - BROTLI_BOOL is_last_block_emitted_; - BROTLI_BOOL is_initialized_; -} BrotliEncoderStateStruct; - -typedef struct BrotliEncoderStateStruct BrotliEncoderStateInternal; -#define BrotliEncoderState BrotliEncoderStateInternal - -#endif // BROTLI_ENC_STATE_H_ diff --git a/src/deps/brotli/enc/static_dict.c b/src/deps/brotli/enc/static_dict.c deleted file mode 100644 index 291d283354692..0000000000000 --- a/src/deps/brotli/enc/static_dict.c +++ /dev/null @@ -1,542 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -#include "static_dict.h" - -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "../common/transform.h" -#include "encoder_dict.h" -#include "find_match_length.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static BROTLI_INLINE uint32_t Hash(const uint8_t* data) { - uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kDictHashMul32; - /* The higher bits contain more mixture from the multiplication, - so we take our results from there. */ - return h >> (32 - kDictNumBits); -} - -static BROTLI_INLINE void AddMatch(size_t distance, size_t len, size_t len_code, - uint32_t* matches) { - uint32_t match = (uint32_t)((distance << 5) + len_code); - matches[len] = BROTLI_MIN(uint32_t, matches[len], match); -} - -static BROTLI_INLINE size_t DictMatchLength(const BrotliDictionary* dictionary, - const uint8_t* data, - size_t id, - size_t len, - size_t maxlen) { - const size_t offset = dictionary->offsets_by_length[len] + len * id; - return FindMatchLengthWithLimit(&dictionary->data[offset], data, - BROTLI_MIN(size_t, len, maxlen)); -} - -static BROTLI_INLINE BROTLI_BOOL IsMatch(const BrotliDictionary* dictionary, - DictWord w, const uint8_t* data, size_t max_length) { - if (w.len > max_length) { - return BROTLI_FALSE; - } else { - const size_t offset = dictionary->offsets_by_length[w.len] + - (size_t)w.len * (size_t)w.idx; - const uint8_t* dict = &dictionary->data[offset]; - if (w.transform == 0) { - /* Match against base dictionary word. */ - return - TO_BROTLI_BOOL(FindMatchLengthWithLimit(dict, data, w.len) == w.len); - } else if (w.transform == 10) { - /* Match against uppercase first transform. - Note that there are only ASCII uppercase words in the lookup table. */ - return TO_BROTLI_BOOL(dict[0] >= 'a' && dict[0] <= 'z' && - (dict[0] ^ 32) == data[0] && - FindMatchLengthWithLimit(&dict[1], &data[1], w.len - 1u) == - w.len - 1u); - } else { - /* Match against uppercase all transform. - Note that there are only ASCII uppercase words in the lookup table. */ - size_t i; - for (i = 0; i < w.len; ++i) { - if (dict[i] >= 'a' && dict[i] <= 'z') { - if ((dict[i] ^ 32) != data[i]) return BROTLI_FALSE; - } else { - if (dict[i] != data[i]) return BROTLI_FALSE; - } - } - return BROTLI_TRUE; - } - } -} - -/* Finds matches for a single static dictionary */ -static BROTLI_BOOL BrotliFindAllStaticDictionaryMatchesFor( - const BrotliEncoderDictionary* dictionary, const uint8_t* data, - size_t min_length, size_t max_length, uint32_t* matches) { - BROTLI_BOOL has_found_match = BROTLI_FALSE; -#if defined(BROTLI_EXPERIMENTAL) - if (dictionary->has_words_heavy) { - const BrotliTrieNode* node = &dictionary->trie.root; - size_t l = 0; - while (node && l < max_length) { - uint8_t c; - if (l >= min_length && node->len_) { - AddMatch(node->idx_, l, node->len_, matches); - has_found_match = BROTLI_TRUE; - } - c = data[l++]; - node = BrotliTrieSub(&dictionary->trie, node, c); - } - return has_found_match; - } -#endif /* BROTLI_EXPERIMENTAL */ - { - size_t offset = dictionary->buckets[Hash(data)]; - BROTLI_BOOL end = !offset; - while (!end) { - DictWord w = dictionary->dict_words[offset++]; - const size_t l = w.len & 0x1F; - const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; - const size_t id = w.idx; - end = !!(w.len & 0x80); - w.len = (uint8_t)l; - if (w.transform == 0) { - const size_t matchlen = - DictMatchLength(dictionary->words, data, id, l, max_length); - const uint8_t* s; - size_t minlen; - size_t maxlen; - size_t len; - /* Transform "" + BROTLI_TRANSFORM_IDENTITY + "" */ - if (matchlen == l) { - AddMatch(id, l, l, matches); - has_found_match = BROTLI_TRUE; - } - /* Transforms "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "" and - "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "ing " */ - if (matchlen >= l - 1) { - AddMatch(id + 12 * n, l - 1, l, matches); - if (l + 2 < max_length && - data[l - 1] == 'i' && data[l] == 'n' && data[l + 1] == 'g' && - data[l + 2] == ' ') { - AddMatch(id + 49 * n, l + 3, l, matches); - } - has_found_match = BROTLI_TRUE; - } - /* Transform "" + BROTLI_TRANSFORM_OMIT_LAST_# + "" (# = 2 .. 9) */ - minlen = min_length; - if (l > 9) minlen = BROTLI_MAX(size_t, minlen, l - 9); - maxlen = BROTLI_MIN(size_t, matchlen, l - 2); - for (len = minlen; len <= maxlen; ++len) { - size_t cut = l - len; - size_t transform_id = (cut << 2) + - (size_t)((dictionary->cutoffTransforms >> (cut * 6)) & 0x3F); - AddMatch(id + transform_id * n, len, l, matches); - has_found_match = BROTLI_TRUE; - } - if (matchlen < l || l + 6 >= max_length) { - continue; - } - s = &data[l]; - /* Transforms "" + BROTLI_TRANSFORM_IDENTITY + */ - if (s[0] == ' ') { - AddMatch(id + n, l + 1, l, matches); - if (s[1] == 'a') { - if (s[2] == ' ') { - AddMatch(id + 28 * n, l + 3, l, matches); - } else if (s[2] == 's') { - if (s[3] == ' ') AddMatch(id + 46 * n, l + 4, l, matches); - } else if (s[2] == 't') { - if (s[3] == ' ') AddMatch(id + 60 * n, l + 4, l, matches); - } else if (s[2] == 'n') { - if (s[3] == 'd' && s[4] == ' ') { - AddMatch(id + 10 * n, l + 5, l, matches); - } - } - } else if (s[1] == 'b') { - if (s[2] == 'y' && s[3] == ' ') { - AddMatch(id + 38 * n, l + 4, l, matches); - } - } else if (s[1] == 'i') { - if (s[2] == 'n') { - if (s[3] == ' ') AddMatch(id + 16 * n, l + 4, l, matches); - } else if (s[2] == 's') { - if (s[3] == ' ') AddMatch(id + 47 * n, l + 4, l, matches); - } - } else if (s[1] == 'f') { - if (s[2] == 'o') { - if (s[3] == 'r' && s[4] == ' ') { - AddMatch(id + 25 * n, l + 5, l, matches); - } - } else if (s[2] == 'r') { - if (s[3] == 'o' && s[4] == 'm' && s[5] == ' ') { - AddMatch(id + 37 * n, l + 6, l, matches); - } - } - } else if (s[1] == 'o') { - if (s[2] == 'f') { - if (s[3] == ' ') AddMatch(id + 8 * n, l + 4, l, matches); - } else if (s[2] == 'n') { - if (s[3] == ' ') AddMatch(id + 45 * n, l + 4, l, matches); - } - } else if (s[1] == 'n') { - if (s[2] == 'o' && s[3] == 't' && s[4] == ' ') { - AddMatch(id + 80 * n, l + 5, l, matches); - } - } else if (s[1] == 't') { - if (s[2] == 'h') { - if (s[3] == 'e') { - if (s[4] == ' ') AddMatch(id + 5 * n, l + 5, l, matches); - } else if (s[3] == 'a') { - if (s[4] == 't' && s[5] == ' ') { - AddMatch(id + 29 * n, l + 6, l, matches); - } - } - } else if (s[2] == 'o') { - if (s[3] == ' ') AddMatch(id + 17 * n, l + 4, l, matches); - } - } else if (s[1] == 'w') { - if (s[2] == 'i' && s[3] == 't' && s[4] == 'h' && s[5] == ' ') { - AddMatch(id + 35 * n, l + 6, l, matches); - } - } - } else if (s[0] == '"') { - AddMatch(id + 19 * n, l + 1, l, matches); - if (s[1] == '>') { - AddMatch(id + 21 * n, l + 2, l, matches); - } - } else if (s[0] == '.') { - AddMatch(id + 20 * n, l + 1, l, matches); - if (s[1] == ' ') { - AddMatch(id + 31 * n, l + 2, l, matches); - if (s[2] == 'T' && s[3] == 'h') { - if (s[4] == 'e') { - if (s[5] == ' ') AddMatch(id + 43 * n, l + 6, l, matches); - } else if (s[4] == 'i') { - if (s[5] == 's' && s[6] == ' ') { - AddMatch(id + 75 * n, l + 7, l, matches); - } - } - } - } - } else if (s[0] == ',') { - AddMatch(id + 76 * n, l + 1, l, matches); - if (s[1] == ' ') { - AddMatch(id + 14 * n, l + 2, l, matches); - } - } else if (s[0] == '\n') { - AddMatch(id + 22 * n, l + 1, l, matches); - if (s[1] == '\t') { - AddMatch(id + 50 * n, l + 2, l, matches); - } - } else if (s[0] == ']') { - AddMatch(id + 24 * n, l + 1, l, matches); - } else if (s[0] == '\'') { - AddMatch(id + 36 * n, l + 1, l, matches); - } else if (s[0] == ':') { - AddMatch(id + 51 * n, l + 1, l, matches); - } else if (s[0] == '(') { - AddMatch(id + 57 * n, l + 1, l, matches); - } else if (s[0] == '=') { - if (s[1] == '"') { - AddMatch(id + 70 * n, l + 2, l, matches); - } else if (s[1] == '\'') { - AddMatch(id + 86 * n, l + 2, l, matches); - } - } else if (s[0] == 'a') { - if (s[1] == 'l' && s[2] == ' ') { - AddMatch(id + 84 * n, l + 3, l, matches); - } - } else if (s[0] == 'e') { - if (s[1] == 'd') { - if (s[2] == ' ') AddMatch(id + 53 * n, l + 3, l, matches); - } else if (s[1] == 'r') { - if (s[2] == ' ') AddMatch(id + 82 * n, l + 3, l, matches); - } else if (s[1] == 's') { - if (s[2] == 't' && s[3] == ' ') { - AddMatch(id + 95 * n, l + 4, l, matches); - } - } - } else if (s[0] == 'f') { - if (s[1] == 'u' && s[2] == 'l' && s[3] == ' ') { - AddMatch(id + 90 * n, l + 4, l, matches); - } - } else if (s[0] == 'i') { - if (s[1] == 'v') { - if (s[2] == 'e' && s[3] == ' ') { - AddMatch(id + 92 * n, l + 4, l, matches); - } - } else if (s[1] == 'z') { - if (s[2] == 'e' && s[3] == ' ') { - AddMatch(id + 100 * n, l + 4, l, matches); - } - } - } else if (s[0] == 'l') { - if (s[1] == 'e') { - if (s[2] == 's' && s[3] == 's' && s[4] == ' ') { - AddMatch(id + 93 * n, l + 5, l, matches); - } - } else if (s[1] == 'y') { - if (s[2] == ' ') AddMatch(id + 61 * n, l + 3, l, matches); - } - } else if (s[0] == 'o') { - if (s[1] == 'u' && s[2] == 's' && s[3] == ' ') { - AddMatch(id + 106 * n, l + 4, l, matches); - } - } - } else { - /* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and - is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL) - transform. */ - const BROTLI_BOOL is_all_caps = - TO_BROTLI_BOOL(w.transform != BROTLI_TRANSFORM_UPPERCASE_FIRST); - const uint8_t* s; - if (!IsMatch(dictionary->words, w, data, max_length)) { - continue; - } - /* Transform "" + kUppercase{First,All} + "" */ - AddMatch(id + (is_all_caps ? 44 : 9) * n, l, l, matches); - has_found_match = BROTLI_TRUE; - if (l + 1 >= max_length) { - continue; - } - /* Transforms "" + kUppercase{First,All} + */ - s = &data[l]; - if (s[0] == ' ') { - AddMatch(id + (is_all_caps ? 68 : 4) * n, l + 1, l, matches); - } else if (s[0] == '"') { - AddMatch(id + (is_all_caps ? 87 : 66) * n, l + 1, l, matches); - if (s[1] == '>') { - AddMatch(id + (is_all_caps ? 97 : 69) * n, l + 2, l, matches); - } - } else if (s[0] == '.') { - AddMatch(id + (is_all_caps ? 101 : 79) * n, l + 1, l, matches); - if (s[1] == ' ') { - AddMatch(id + (is_all_caps ? 114 : 88) * n, l + 2, l, matches); - } - } else if (s[0] == ',') { - AddMatch(id + (is_all_caps ? 112 : 99) * n, l + 1, l, matches); - if (s[1] == ' ') { - AddMatch(id + (is_all_caps ? 107 : 58) * n, l + 2, l, matches); - } - } else if (s[0] == '\'') { - AddMatch(id + (is_all_caps ? 94 : 74) * n, l + 1, l, matches); - } else if (s[0] == '(') { - AddMatch(id + (is_all_caps ? 113 : 78) * n, l + 1, l, matches); - } else if (s[0] == '=') { - if (s[1] == '"') { - AddMatch(id + (is_all_caps ? 105 : 104) * n, l + 2, l, matches); - } else if (s[1] == '\'') { - AddMatch(id + (is_all_caps ? 116 : 108) * n, l + 2, l, matches); - } - } - } - } - } - /* Transforms with prefixes " " and "." */ - if (max_length >= 5 && (data[0] == ' ' || data[0] == '.')) { - BROTLI_BOOL is_space = TO_BROTLI_BOOL(data[0] == ' '); - size_t offset = dictionary->buckets[Hash(&data[1])]; - BROTLI_BOOL end = !offset; - while (!end) { - DictWord w = dictionary->dict_words[offset++]; - const size_t l = w.len & 0x1F; - const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; - const size_t id = w.idx; - end = !!(w.len & 0x80); - w.len = (uint8_t)l; - if (w.transform == 0) { - const uint8_t* s; - if (!IsMatch(dictionary->words, w, &data[1], max_length - 1)) { - continue; - } - /* Transforms " " + BROTLI_TRANSFORM_IDENTITY + "" and - "." + BROTLI_TRANSFORM_IDENTITY + "" */ - AddMatch(id + (is_space ? 6 : 32) * n, l + 1, l, matches); - has_found_match = BROTLI_TRUE; - if (l + 2 >= max_length) { - continue; - } - /* Transforms " " + BROTLI_TRANSFORM_IDENTITY + and - "." + BROTLI_TRANSFORM_IDENTITY + - */ - s = &data[l + 1]; - if (s[0] == ' ') { - AddMatch(id + (is_space ? 2 : 77) * n, l + 2, l, matches); - } else if (s[0] == '(') { - AddMatch(id + (is_space ? 89 : 67) * n, l + 2, l, matches); - } else if (is_space) { - if (s[0] == ',') { - AddMatch(id + 103 * n, l + 2, l, matches); - if (s[1] == ' ') { - AddMatch(id + 33 * n, l + 3, l, matches); - } - } else if (s[0] == '.') { - AddMatch(id + 71 * n, l + 2, l, matches); - if (s[1] == ' ') { - AddMatch(id + 52 * n, l + 3, l, matches); - } - } else if (s[0] == '=') { - if (s[1] == '"') { - AddMatch(id + 81 * n, l + 3, l, matches); - } else if (s[1] == '\'') { - AddMatch(id + 98 * n, l + 3, l, matches); - } - } - } - } else if (is_space) { - /* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and - is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL) - transform. */ - const BROTLI_BOOL is_all_caps = - TO_BROTLI_BOOL(w.transform != BROTLI_TRANSFORM_UPPERCASE_FIRST); - const uint8_t* s; - if (!IsMatch(dictionary->words, w, &data[1], max_length - 1)) { - continue; - } - /* Transforms " " + kUppercase{First,All} + "" */ - AddMatch(id + (is_all_caps ? 85 : 30) * n, l + 1, l, matches); - has_found_match = BROTLI_TRUE; - if (l + 2 >= max_length) { - continue; - } - /* Transforms " " + kUppercase{First,All} + */ - s = &data[l + 1]; - if (s[0] == ' ') { - AddMatch(id + (is_all_caps ? 83 : 15) * n, l + 2, l, matches); - } else if (s[0] == ',') { - if (!is_all_caps) { - AddMatch(id + 109 * n, l + 2, l, matches); - } - if (s[1] == ' ') { - AddMatch(id + (is_all_caps ? 111 : 65) * n, l + 3, l, matches); - } - } else if (s[0] == '.') { - AddMatch(id + (is_all_caps ? 115 : 96) * n, l + 2, l, matches); - if (s[1] == ' ') { - AddMatch(id + (is_all_caps ? 117 : 91) * n, l + 3, l, matches); - } - } else if (s[0] == '=') { - if (s[1] == '"') { - AddMatch(id + (is_all_caps ? 110 : 118) * n, l + 3, l, matches); - } else if (s[1] == '\'') { - AddMatch(id + (is_all_caps ? 119 : 120) * n, l + 3, l, matches); - } - } - } - } - } - if (max_length >= 6) { - /* Transforms with prefixes "e ", "s ", ", " and "\xC2\xA0" */ - if ((data[1] == ' ' && - (data[0] == 'e' || data[0] == 's' || data[0] == ',')) || - (data[0] == 0xC2 && data[1] == 0xA0)) { - size_t offset = dictionary->buckets[Hash(&data[2])]; - BROTLI_BOOL end = !offset; - while (!end) { - DictWord w = dictionary->dict_words[offset++]; - const size_t l = w.len & 0x1F; - const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; - const size_t id = w.idx; - end = !!(w.len & 0x80); - w.len = (uint8_t)l; - if (w.transform == 0 && - IsMatch(dictionary->words, w, &data[2], max_length - 2)) { - if (data[0] == 0xC2) { - AddMatch(id + 102 * n, l + 2, l, matches); - has_found_match = BROTLI_TRUE; - } else if (l + 2 < max_length && data[l + 2] == ' ') { - size_t t = data[0] == 'e' ? 18 : (data[0] == 's' ? 7 : 13); - AddMatch(id + t * n, l + 3, l, matches); - has_found_match = BROTLI_TRUE; - } - } - } - } - } - if (max_length >= 9) { - /* Transforms with prefixes " the " and ".com/" */ - if ((data[0] == ' ' && data[1] == 't' && data[2] == 'h' && - data[3] == 'e' && data[4] == ' ') || - (data[0] == '.' && data[1] == 'c' && data[2] == 'o' && - data[3] == 'm' && data[4] == '/')) { - size_t offset = dictionary->buckets[Hash(&data[5])]; - BROTLI_BOOL end = !offset; - while (!end) { - DictWord w = dictionary->dict_words[offset++]; - const size_t l = w.len & 0x1F; - const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; - const size_t id = w.idx; - end = !!(w.len & 0x80); - w.len = (uint8_t)l; - if (w.transform == 0 && - IsMatch(dictionary->words, w, &data[5], max_length - 5)) { - AddMatch(id + (data[0] == ' ' ? 41 : 72) * n, l + 5, l, matches); - has_found_match = BROTLI_TRUE; - if (l + 5 < max_length) { - const uint8_t* s = &data[l + 5]; - if (data[0] == ' ') { - if (l + 8 < max_length && - s[0] == ' ' && s[1] == 'o' && s[2] == 'f' && s[3] == ' ') { - AddMatch(id + 62 * n, l + 9, l, matches); - if (l + 12 < max_length && - s[4] == 't' && s[5] == 'h' && s[6] == 'e' && s[7] == ' ') { - AddMatch(id + 73 * n, l + 13, l, matches); - } - } - } - } - } - } - } - } - return has_found_match; -} - -/* Finds matches for one or more dictionaries, if multiple are present - in the contextual dictionary */ -BROTLI_BOOL BrotliFindAllStaticDictionaryMatches( - const BrotliEncoderDictionary* dictionary, const uint8_t* data, - size_t min_length, size_t max_length, uint32_t* matches) { - BROTLI_BOOL has_found_match = - BrotliFindAllStaticDictionaryMatchesFor( - dictionary, data, min_length, max_length, matches); - - if (!!dictionary->parent && dictionary->parent->num_dictionaries > 1) { - uint32_t matches2[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1]; - int l; - const BrotliEncoderDictionary* dictionary2 = dictionary->parent->dict[0]; - if (dictionary2 == dictionary) { - dictionary2 = dictionary->parent->dict[1]; - } - - for (l = 0; l < BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1; l++) { - matches2[l] = kInvalidMatch; - } - - has_found_match |= BrotliFindAllStaticDictionaryMatchesFor( - dictionary2, data, min_length, max_length, matches2); - - for (l = 0; l < BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1; l++) { - if (matches2[l] != kInvalidMatch) { - uint32_t dist = (uint32_t)(matches2[l] >> 5); - uint32_t len_code = matches2[l] & 31; - uint32_t skipdist = (uint32_t)((uint32_t)(1 << dictionary->words-> - size_bits_by_length[len_code]) & ~1u) * - (uint32_t)dictionary->num_transforms; - /* TODO(lode): check for dist overflow */ - dist += skipdist; - AddMatch(dist, (size_t)l, len_code, matches); - } - } - } - return has_found_match; -} -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/static_dict.h b/src/deps/brotli/enc/static_dict.h deleted file mode 100644 index ab832207d108d..0000000000000 --- a/src/deps/brotli/enc/static_dict.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Class to model the static dictionary. */ - -#ifndef BROTLI_ENC_STATIC_DICT_H_ -#define BROTLI_ENC_STATIC_DICT_H_ - -#include - -#include "../common/dictionary.h" -#include "../common/platform.h" -#include "encoder_dict.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN 37 -static const uint32_t kInvalidMatch = 0xFFFFFFF; - -/* Matches data against static dictionary words, and for each length l, - for which a match is found, updates matches[l] to be the minimum possible - (distance << 5) + len_code. - Returns 1 if matches have been found, otherwise 0. - Prerequisites: - matches array is at least BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1 long - all elements are initialized to kInvalidMatch */ -BROTLI_INTERNAL BROTLI_BOOL BrotliFindAllStaticDictionaryMatches( - const BrotliEncoderDictionary* dictionary, - const uint8_t* data, size_t min_length, size_t max_length, - uint32_t* matches); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_STATIC_DICT_H_ */ diff --git a/src/deps/brotli/enc/static_dict_lut.h b/src/deps/brotli/enc/static_dict_lut.h deleted file mode 100644 index a465ffde7431a..0000000000000 --- a/src/deps/brotli/enc/static_dict_lut.h +++ /dev/null @@ -1,5866 +0,0 @@ -/* Copyright 2015 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Lookup table for static dictionary and transforms. */ - -#ifndef BROTLI_ENC_STATIC_DICT_LUT_H_ -#define BROTLI_ENC_STATIC_DICT_LUT_H_ - -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct DictWord { - /* Highest bit is used to indicate end of bucket. */ - uint8_t len; - uint8_t transform; - uint16_t idx; -} DictWord; - -/* GENERATED CODE START */ -static const int kDictNumBits = 15; -static const uint32_t kDictHashMul32 = 0x1E35A7BD; - -static const uint16_t kStaticDictionaryBuckets[32768] = { -1,0,0,0,0,0,0,0,0,3,6,0,0,0,0,0,20,0,0,0,21,0,22,0,0,0,0,0,0,0,0,23,0,0,25,0,29, -0,53,0,0,0,0,0,0,55,0,0,0,0,0,0,61,76,0,0,0,94,0,0,0,0,0,0,96,0,97,0,98,0,0,0,0, -0,0,0,99,101,106,108,0,0,0,0,0,110,0,111,112,0,113,118,124,0,0,0,0,0,125,128,0,0 -,0,0,129,0,0,131,0,0,0,0,0,0,132,0,0,135,0,0,0,137,0,0,0,0,0,138,139,0,0,0,0,0,0 -,0,142,143,144,0,0,0,0,0,145,0,0,0,146,149,151,152,0,0,153,0,0,0,0,0,0,0,0,0,0,0 -,0,0,0,0,154,0,0,0,0,0,0,155,0,0,0,0,160,182,0,0,0,0,0,0,183,0,0,0,188,189,0,0, -192,0,0,0,0,0,0,194,0,0,0,0,0,0,0,0,197,202,209,0,0,210,0,224,0,0,0,225,0,0,0,0, -0,0,0,0,0,0,231,0,0,0,232,0,240,0,0,242,0,0,0,0,0,0,0,0,0,0,0,244,0,0,0,246,0,0, -249,251,253,0,0,0,0,0,258,0,0,261,263,0,0,0,267,0,0,268,0,269,0,0,0,0,0,0,0,0,0, -271,0,0,0,0,0,0,272,0,273,0,277,0,278,286,0,0,0,0,287,0,289,290,291,0,0,0,295,0, -0,296,297,0,0,0,0,0,0,0,0,0,0,298,0,0,0,299,0,0,305,0,324,0,0,0,0,0,327,0,328, -329,0,0,0,0,336,0,0,340,0,341,342,343,0,0,346,0,348,0,0,0,0,0,0,349,351,0,0,355, -0,363,0,364,0,368,369,0,370,0,0,0,0,0,0,0,372,0,0,0,0,0,0,0,0,0,0,0,373,0,375,0, -0,0,0,376,377,0,0,394,395,396,0,0,398,0,0,0,0,400,0,0,408,0,0,0,0,420,0,0,0,0,0, -0,421,0,0,422,423,0,0,429,435,436,442,0,0,443,0,444,445,453,456,0,457,0,0,0,0,0, -458,0,0,0,459,0,0,0,460,0,462,463,465,0,0,0,0,0,0,466,469,0,0,0,0,0,0,470,0,0,0, -474,0,476,0,0,0,0,483,0,485,0,0,0,486,0,0,488,491,492,0,0,497,499,500,0,501,0,0, -0,505,0,0,506,0,0,0,507,0,0,0,509,0,0,0,0,511,512,519,0,0,0,0,0,0,529,530,0,0,0, -534,0,0,0,0,543,0,0,0,0,0,0,0,0,0,553,0,0,0,0,557,560,0,0,0,0,0,0,561,0,564,0,0, -0,0,0,0,565,566,0,575,0,619,0,620,0,0,623,624,0,0,0,625,0,0,626,627,0,0,628,0,0, -0,0,630,0,631,0,0,0,0,0,0,0,0,0,641,0,0,0,0,643,656,668,0,0,0,673,0,0,0,674,0,0, -0,0,0,0,0,0,682,0,687,0,690,0,693,699,700,0,0,0,0,0,0,704,705,0,0,0,0,707,710,0, -711,0,0,0,0,726,0,0,729,0,0,0,730,731,0,0,0,0,0,752,0,0,0,762,0,763,0,0,767,0,0, -0,770,774,0,0,775,0,0,0,0,0,0,0,0,0,0,776,0,0,0,777,783,0,0,0,785,788,0,0,0,0, -790,0,0,0,793,0,0,0,0,794,0,0,804,819,821,0,827,0,0,0,834,0,0,835,0,0,0,841,0, -844,0,850,851,859,0,860,0,0,0,0,0,0,0,874,0,876,0,877,890,0,0,0,0,0,0,0,0,893, -894,898,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,899,0,0,0,900,904,906,0,0,0,907,0,908,909, -0,910,0,0,0,0,911,0,0,0,0,0,916,0,0,0,922,925,0,930,0,934,0,0,0,0,0,943,0,0,944, -0,953,954,0,0,0,0,0,0,955,0,962,963,0,0,976,0,0,977,978,979,980,0,981,0,0,0,0, -984,0,0,985,0,0,987,989,991,0,0,0,0,0,0,0,0,0,992,0,0,0,993,0,0,0,0,0,0,996,0,0, -0,1000,0,0,0,0,0,1002,0,0,0,0,1005,1007,0,0,0,1009,0,0,0,1010,0,0,0,0,0,0,1011,0 -,1012,0,0,0,0,1014,1016,0,0,0,1020,0,1021,0,0,0,0,1022,0,0,0,1024,0,0,0,0,0,0, -1025,0,0,1026,1027,0,0,0,0,0,1031,0,1033,0,0,0,0,1034,0,0,0,1037,1040,0,0,0,1042 -,1043,0,0,1053,0,1054,0,0,1057,0,0,0,1058,0,0,1060,0,0,0,0,0,0,0,1061,0,0,1062,0 -,0,0,0,1063,0,0,0,0,1064,0,0,0,0,0,1065,0,0,0,0,1066,1067,0,0,0,1069,1070,1072,0 -,0,0,0,0,0,1073,0,1075,0,0,0,0,0,0,1080,1084,0,0,0,0,1088,0,0,0,0,0,0,1094,0, -1095,0,1107,0,0,0,1112,1114,0,1119,0,1122,0,0,1126,0,1129,0,1130,0,0,0,0,0,1132, -0,0,0,0,0,0,1144,0,0,1145,1146,0,1148,1149,0,0,1150,1151,0,0,0,0,1152,0,1153,0,0 -,0,0,0,1154,0,1163,0,0,0,1164,0,0,0,0,0,1165,0,1167,0,1170,0,0,0,0,0,1171,1172,0 -,0,0,0,0,0,0,0,1173,1175,1177,0,1186,0,0,0,0,0,0,0,0,0,0,1195,0,0,1221,0,0,1224, -0,0,1227,0,0,0,0,0,1228,1229,0,0,1230,0,0,0,0,0,0,0,0,0,1231,0,0,0,1233,0,0,1243 -,1244,1246,1248,0,0,0,0,1254,1255,1258,1259,0,0,0,1260,0,0,1261,0,0,0,1262,1264, -0,0,1265,0,0,0,0,0,0,0,0,0,0,0,0,1266,0,1267,0,0,0,0,1273,1274,1276,1289,0,0, -1291,1292,1293,0,0,1294,1295,1296,0,0,0,0,1302,0,1304,0,0,0,0,0,0,0,0,0,1311, -1312,0,1314,0,1316,1320,1321,0,0,0,0,0,0,0,1322,1323,1324,0,1335,0,1336,0,0,0,0, -1341,1342,0,1346,0,1357,0,0,0,1358,1360,0,0,0,0,0,0,1361,0,0,0,1362,1365,0,1366, -0,0,0,0,0,0,0,1379,0,0,0,0,0,0,0,0,0,0,0,0,1386,0,1388,0,0,0,0,0,0,0,0,0,0,0,0,0 -,0,1395,0,0,0,0,1403,0,1405,0,0,1407,0,0,0,0,0,1408,1409,0,1410,0,0,0,1412,1413, -1416,0,0,1429,1451,0,0,1454,0,0,0,0,0,0,0,1455,0,0,0,0,0,0,0,1456,0,0,0,0,1459, -1460,1461,1475,0,0,0,0,0,0,1477,0,1480,0,1481,0,0,1486,0,0,1495,0,0,0,1496,0,0, -1498,1499,1501,1520,1521,0,0,0,1526,0,0,0,0,1528,1529,0,1533,1536,0,0,0,1537, -1538,1549,0,1550,1558,1559,1572,0,1573,0,0,0,0,0,0,0,0,0,1575,0,0,0,0,0,1579,0, -1599,0,1603,0,1604,0,1605,0,0,0,0,0,1608,1610,0,0,0,0,1611,0,1615,0,1616,1618,0, -1619,0,0,1622,0,0,0,0,1634,0,0,0,1635,0,0,0,1641,0,0,0,0,0,0,0,0,0,1643,0,0,0, -1650,0,0,1652,0,0,0,0,0,1653,0,0,0,1654,0,0,0,0,1655,0,1662,0,0,1663,1664,0,0, -1668,0,0,1669,1670,0,1672,1673,0,0,0,0,0,1674,0,0,0,1675,1676,1680,0,1682,0,0, -1687,0,0,0,0,0,1704,0,0,1705,0,0,1721,0,0,0,0,1734,1735,0,0,0,0,1737,0,0,0,0, -1739,0,0,1740,0,0,0,0,0,0,0,0,0,0,1741,1743,0,0,0,0,1745,0,0,0,1749,0,0,0,1751,0 -,0,0,0,0,0,1760,0,0,0,0,1765,0,0,0,0,0,1784,0,1785,1787,0,0,0,0,1788,1789,0,0,0, -0,1790,1791,1793,0,1798,1799,0,0,0,0,1801,0,1803,1805,0,0,0,1806,1811,0,1812, -1814,0,1821,0,0,0,0,0,1822,1833,0,0,0,0,0,0,1848,0,0,0,0,0,0,1857,0,0,0,1859,0,0 -,0,0,1861,0,0,0,0,0,0,0,1866,0,1921,1925,0,0,0,1929,1930,0,0,0,0,0,0,0,0,0,1931, -0,0,0,0,1932,0,0,0,1934,0,0,0,0,0,0,0,0,1946,0,0,1948,0,0,0,0,1950,0,1957,0,1958 -,0,0,0,0,0,1965,1967,0,0,0,0,1968,0,1969,0,1971,1972,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -,0,1973,0,0,0,0,1975,0,0,0,0,1976,1979,0,1982,0,0,0,0,1984,1988,0,0,0,0,1990, -2004,2008,0,0,0,2012,2013,0,0,0,0,0,0,0,0,0,0,2015,0,2016,2017,0,0,0,0,2021,0,0, -2025,0,0,0,0,0,2029,2036,2040,0,2042,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2043,0,0,0,0,0, -2045,0,0,0,0,0,0,0,2046,2047,0,2048,2049,0,2059,0,0,2063,0,2064,2065,0,0,2066,0, -0,0,0,0,0,2069,0,0,0,0,2070,0,2071,0,2072,0,0,0,0,2080,2082,2083,0,0,0,0,0,2085, -0,2086,2088,2089,2105,0,0,0,0,2107,0,0,2116,2117,0,2120,0,0,2122,0,0,0,0,0,2123, -0,0,2125,2127,2128,0,0,0,2130,0,0,0,2137,2139,2140,2141,0,0,0,0,0,0,0,0,0,2144, -2145,0,0,2146,2149,0,0,0,0,2150,0,0,2151,2158,0,2159,0,2160,0,0,0,0,0,0,2161, -2162,0,0,2194,2202,0,0,0,0,0,0,2205,2217,0,2220,0,2221,0,2222,2224,0,0,0,0,2237, -0,0,0,0,0,2238,0,2239,2241,0,0,2242,0,0,0,0,0,2243,0,0,0,0,0,0,2252,0,0,2253,0,0 -,0,2257,2258,0,0,0,2260,0,0,0,0,0,0,0,2262,0,2264,0,0,0,0,0,2269,2270,0,0,0,0,0, -0,0,0,0,2271,0,2273,0,0,0,0,2277,0,0,0,0,2278,0,0,0,0,2279,0,2280,0,2283,0,0,0,0 -,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2287,0,0,0,0,0,0,0,2289,2290,0,0,0,0,2291,0,2292,0, -0,0,2293,2295,2296,0,0,0,0,0,0,0,2298,0,0,0,0,0,2303,0,2305,0,0,2306,0,2307,0,0, -0,0,0,0,0,0,0,0,0,0,2313,2314,2315,2316,0,0,2318,0,2319,0,2322,0,0,2323,0,2324,0 -,2326,0,0,0,0,0,0,0,2335,0,2336,2338,2339,0,2340,0,0,0,2355,0,2375,0,2382,2386,0 -,2387,0,0,2394,0,0,0,0,2395,0,2397,0,0,0,0,0,2398,0,0,0,0,0,0,0,2399,2402,2404, -2408,2411,0,0,0,2413,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2415,0,0,2416,2417,2419,0,2420, -0,0,0,0,0,2425,0,0,0,2426,0,0,0,0,0,0,0,0,0,0,0,0,2427,2428,0,2429,0,0,2430,2434 -,0,2436,0,0,0,0,0,0,2441,2442,0,2445,0,0,2446,2457,0,2459,0,0,2462,0,2464,0,2477 -,0,2478,2486,0,0,0,2491,0,0,2493,0,0,2494,0,2495,0,2513,2523,0,0,0,0,2524,0,0,0, -0,0,0,2528,2529,2530,0,0,2531,0,2533,0,0,2534,2535,0,2536,2537,0,2538,0,2539, -2540,0,0,0,2545,2546,0,0,0,0,0,0,0,2548,0,0,2549,0,2550,2555,0,0,0,0,0,2557,0, -2560,0,0,0,0,0,0,0,0,0,0,0,2561,0,2576,0,0,0,0,0,0,0,0,0,2577,2578,0,0,0,2579,0, -0,0,0,0,0,0,2580,0,0,0,0,2581,0,0,0,0,2583,0,2584,0,2588,2590,0,0,0,2591,0,0,0,0 -,2593,2594,0,2595,0,2601,2602,0,0,2603,0,2605,0,0,0,2606,2607,2611,0,2615,0,0,0, -2617,0,0,0,0,0,0,0,0,0,0,0,0,0,2619,0,0,2620,0,0,0,2621,0,2623,0,2625,0,0,2628, -2629,0,0,2635,2636,2637,0,0,2639,0,0,0,2642,0,0,0,0,2643,0,2644,0,2649,0,0,0,0,0 -,0,2655,2656,0,0,2657,0,0,0,0,0,2658,0,0,0,0,0,2659,0,0,0,0,2664,2685,0,2687,0, -2688,0,0,2689,0,0,2694,0,2695,0,0,2698,0,2701,2706,0,0,0,2707,0,2709,2710,2711,0 -,0,0,2720,2730,2735,0,0,0,0,2738,2740,0,0,0,0,2747,0,0,0,0,0,0,2748,0,0,2749,0,0 -,0,0,0,2750,0,0,2752,2754,0,0,0,0,0,2758,0,0,0,0,2762,0,0,0,0,2763,0,0,0,0,0,0,0 -,2764,2767,0,0,0,0,2768,0,0,2770,0,0,0,0,0,0,0,2771,0,0,0,0,0,0,0,0,0,2772,0,0,0 -,0,0,2773,2776,0,0,2783,0,0,2784,0,2789,0,2790,0,0,0,2792,0,0,0,0,0,0,0,0,0,0, -2793,2795,0,0,0,0,0,0,2796,0,0,0,0,0,0,2797,2799,0,0,0,0,2803,0,0,0,0,2806,0, -2807,2808,2817,2819,0,0,0,0,0,2821,0,0,0,0,2822,2823,0,0,0,0,0,0,0,2824,0,0,2828 -,0,2834,0,0,0,0,0,0,2836,0,2838,0,0,2839,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2841, -0,0,0,2842,0,0,0,0,0,2843,2844,0,0,0,0,2846,0,0,2847,0,2849,0,2853,0,0,0,0,0, -2857,0,0,0,0,2858,0,2859,0,0,2860,0,2862,2868,0,0,0,0,2875,0,2876,0,0,2877,2878, -2884,2889,2890,0,0,2891,0,0,2892,0,0,0,2906,2912,0,2913,0,0,0,0,0,0,0,0,2916,0, -2934,0,0,0,0,0,2935,0,0,0,0,2939,0,2940,0,0,0,0,0,0,0,2941,0,0,0,2946,0,2949,0,0 -,2950,2954,2955,0,0,0,2959,2961,0,0,2962,0,2963,0,0,0,0,0,0,2964,2965,2966,2967, -0,0,0,0,0,0,0,2969,0,0,0,0,0,2970,2975,0,2982,2983,2984,0,0,0,0,0,2989,0,0,2990, -0,0,0,0,0,0,0,2991,0,0,0,0,0,0,0,0,2998,0,3000,3001,0,0,3002,0,0,0,3003,0,0,3012 -,0,0,3022,0,0,3024,0,0,3025,3027,0,0,0,3030,0,0,0,0,3034,3035,0,0,3036,0,3039,0, -3049,0,0,3050,0,0,0,0,0,0,3051,0,3053,0,0,0,0,3057,0,3058,0,0,0,0,0,0,0,0,3063,0 -,0,3073,3074,3078,3079,0,3080,3086,0,0,0,0,0,0,0,0,3087,0,3092,0,3095,0,3099,0,0 -,0,3100,0,3101,3102,0,3122,0,0,0,3124,0,3125,0,0,0,0,0,0,3132,3134,0,0,3136,0,0, -0,0,0,0,0,3147,0,0,3149,0,0,0,0,0,3150,3151,3152,0,0,0,0,3158,0,0,3160,0,0,3161, -0,0,3162,0,3163,3166,3168,0,0,3169,3170,0,0,3171,0,0,0,0,0,0,0,3182,0,3184,0,0, -3188,0,0,3194,0,0,0,0,0,0,3204,0,0,0,0,3209,0,0,0,0,0,0,0,0,0,0,0,3216,3217,0,0, -0,0,0,0,0,3219,0,0,3220,3222,0,3223,0,0,0,0,3224,0,3225,3226,0,3228,3233,0,3239, -3241,3242,0,0,3251,3252,3253,3255,0,0,0,0,0,0,0,0,3260,0,0,3261,0,0,0,3267,0,0,0 -,0,0,0,0,0,3271,0,0,0,3278,0,3282,0,0,0,3284,0,0,0,3285,3286,0,0,0,0,0,0,0,3287, -3292,0,0,0,0,3294,3296,0,0,3299,3300,3301,0,3302,0,0,0,0,0,3304,3306,0,0,0,0,0,0 -,3308,0,0,0,0,0,0,0,0,0,3311,0,0,0,0,0,0,0,0,3312,3314,3315,0,3318,0,0,0,0,0,0,0 -,0,3319,0,0,0,0,0,3321,0,0,0,0,0,0,0,0,0,3322,0,0,3324,3325,0,0,3326,0,0,3328, -3329,3331,0,0,3335,0,0,3337,0,3338,0,0,0,0,3343,3347,0,0,0,3348,0,0,3351,0,0,0,0 -,0,0,3354,0,0,0,0,0,0,0,0,0,0,3355,0,0,3365,3366,3367,0,0,0,0,0,0,3368,3369,0, -3370,0,0,3373,0,0,3376,0,0,3377,0,3379,3387,0,0,0,0,0,3390,0,0,0,0,0,0,0,3402,0, -3403,3436,3437,3439,0,0,3441,0,0,0,3442,0,0,3449,0,0,0,3450,0,0,0,0,0,0,0,3451,0 -,0,3452,0,3453,3456,0,3457,0,0,3458,0,3459,0,0,0,0,0,0,0,0,0,3460,0,0,3469,3470, -0,0,3475,0,0,0,3480,3487,3489,0,3490,0,0,3491,3499,0,3500,0,0,3501,0,0,0,3502,0, -3514,0,0,0,3516,3517,0,0,0,3518,0,0,0,0,3520,3521,3522,0,0,3526,3530,0,0,0,0, -3531,0,0,0,0,3536,0,0,0,0,0,0,0,3539,3541,0,0,3542,3544,0,3547,3548,0,0,3550,0, -3553,0,0,0,0,0,0,0,3554,0,3555,0,3558,0,3559,0,0,0,0,0,0,0,0,3563,0,3581,0,0,0, -3599,0,0,0,3600,0,3601,0,3602,3603,0,0,3606,3608,0,3610,3611,0,0,0,0,0,0,0,0,0, -3612,3616,3619,0,0,0,0,0,0,0,0,0,0,0,0,0,3624,3628,0,3629,3634,3635,0,0,0,0,0,0, -3636,0,3637,0,0,3638,3651,0,0,0,0,0,0,3652,3653,0,0,0,0,3656,3657,0,0,0,0,0,3658 -,0,0,0,0,3659,0,3661,3663,3664,0,3665,0,3692,0,0,0,3694,3696,0,0,0,0,0,0,0,0,0,0 -,0,0,3698,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3700,0,0,3701,0,0,0,3708,3709,0,0,0,3711 -,3712,0,0,0,0,0,3723,0,3724,3725,0,0,3726,0,0,0,0,0,0,3728,3729,0,3734,3735,3737 -,0,0,0,3743,0,3745,0,0,3746,0,0,3747,3748,0,3757,0,3759,3766,3767,0,3768,0,0,0,0 -,3769,0,0,3771,0,3774,0,0,0,0,0,0,3775,0,0,0,0,0,0,3776,0,3777,3786,0,3788,3789, -0,0,0,0,0,0,0,0,0,3791,0,3811,0,0,0,0,0,3814,3815,3816,3820,0,0,0,0,0,0,0,3821,0 -,0,3825,0,0,0,0,3835,0,0,3848,3849,0,0,0,0,3850,3851,3853,0,0,0,0,3859,0,3860, -3862,0,0,0,0,0,3863,0,0,0,0,0,0,0,0,3873,0,3874,0,3875,3886,0,3887,0,0,0,0,3892, -3913,0,3914,0,0,0,3925,3931,0,0,0,0,3934,3941,3942,0,0,0,0,3943,0,0,0,3944,0,0,0 -,0,0,3945,0,3947,0,0,0,3956,3957,0,0,0,0,0,0,0,0,0,3958,0,3959,3965,0,0,0,0,3966 -,0,0,0,3967,0,0,0,3968,3974,0,0,0,0,0,3975,3977,3978,0,0,0,0,3980,0,3985,0,0,0,0 -,0,0,0,0,3986,4011,0,0,4017,0,0,0,0,0,0,0,0,0,0,0,4018,0,0,0,0,4019,0,4023,0,0,0 -,4027,4028,0,0,0,0,0,0,0,0,4031,4034,0,0,4035,4037,4039,4040,0,0,0,0,0,4059,0, -4060,4061,0,4062,4063,4066,0,0,4072,0,0,0,0,0,0,0,0,0,0,0,0,0,4088,0,0,0,0,0, -4091,0,0,0,0,4094,4095,0,0,4096,0,0,0,0,0,4098,4099,0,0,0,4101,0,4104,0,0,0,4105 -,4108,0,4113,0,0,4115,4116,0,4126,0,0,4127,0,0,0,0,0,0,0,4128,4132,4133,0,4134,0 -,0,0,4137,0,0,4141,0,0,0,0,4144,4146,4147,0,0,0,0,4148,0,0,4311,0,0,0,4314,4329, -0,4331,4332,0,4333,0,4334,0,0,0,4335,0,4336,0,0,0,4337,0,0,0,4342,4345,4346,4350 -,0,4351,4352,0,4354,4355,0,0,4364,0,0,0,0,4369,0,0,0,4373,0,4374,0,0,0,0,4377,0, -0,0,0,4378,0,0,0,4380,0,0,0,4381,4382,0,0,0,0,0,0,0,4384,0,0,0,0,4385,0,0,0,4386 -,0,0,0,4391,4398,0,0,0,0,4407,4409,0,0,0,0,4410,0,0,4411,0,4414,4415,4418,0,4427 -,4428,4430,0,4431,0,4448,0,0,0,0,0,4449,0,0,0,4451,4452,0,4453,4454,0,4456,0,0,0 -,0,0,0,0,4459,0,4463,0,0,0,0,0,4466,0,4467,0,4469,0,0,0,0,0,0,0,0,0,0,0,0,0,4470 -,4471,0,4473,0,0,4475,0,0,0,0,4477,4478,0,0,0,4479,4481,0,4482,0,4484,0,0,0,0,0, -0,0,4486,0,0,4488,0,0,4497,0,4508,0,0,4510,4511,0,4520,4523,0,4524,0,4525,0,4527 -,0,0,4528,0,0,0,0,4530,0,4531,0,0,4532,0,0,0,4533,0,0,0,0,0,4535,0,0,0,4536,0,0, -0,0,0,4541,4543,4544,4545,4547,0,4548,0,0,0,0,4550,4551,0,4553,0,0,0,0,4562,0,0, -4571,0,0,0,4574,0,0,0,4575,0,4576,0,4577,0,0,0,4581,0,0,0,0,0,4582,0,0,4586,0,0, -0,4588,0,0,4597,0,4598,0,0,0,0,4616,4617,0,4618,0,0,0,0,4619,0,4620,0,0,4621,0, -4624,0,0,0,0,0,4625,0,0,0,0,4657,0,4659,0,4667,0,0,0,4668,4670,0,4672,0,0,0,0,0, -4673,4676,0,0,0,0,4687,0,0,0,0,4697,0,0,0,0,4699,0,4701,0,0,0,0,4702,0,0,4706,0, -0,4713,0,0,0,4714,4715,4716,0,0,0,0,0,0,0,0,0,0,0,0,4717,0,0,4720,0,4721,4729, -4735,0,0,0,4737,0,0,0,4739,0,0,0,4740,0,0,0,4741,0,0,0,0,0,4742,0,4745,4746,4747 -,0,0,0,0,0,0,0,0,4748,0,0,0,4749,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4751, -4786,0,4787,0,4788,4796,0,0,4797,4798,0,4799,4806,4807,0,0,0,0,4809,4810,0,0,0,0 -,0,0,4811,0,0,0,0,0,4812,0,4813,0,0,4815,0,4821,4822,0,0,0,0,4823,0,0,0,0,0,0,0, -0,0,0,4824,0,0,0,0,4826,0,0,0,4828,0,4829,0,0,0,4843,0,0,4847,0,4853,4855,4858,0 -,0,0,0,0,4859,0,4864,0,0,4879,0,0,0,0,4880,0,0,0,0,4881,0,4882,0,0,0,0,0,0,0,0,0 -,4883,0,0,0,0,4884,0,0,0,0,0,4886,4887,4888,4894,4896,0,4902,0,0,4905,0,0,4915,0 -,0,0,0,0,0,0,4916,4917,4919,4921,0,0,0,0,0,4926,0,0,0,0,4927,0,0,0,0,0,0,0,0, -4929,0,4930,4931,0,4938,0,4952,0,4953,4957,4960,4964,0,0,0,0,0,0,0,5019,5020, -5022,0,0,0,0,0,5023,0,0,0,5024,0,0,0,5025,0,0,0,0,5028,0,0,0,0,5029,5030,5031,0, -5033,0,0,0,0,0,0,0,0,0,5034,5035,0,5036,0,0,5037,0,0,0,0,5038,0,0,5039,0,0,0, -5041,5042,0,0,0,0,5044,5049,5054,0,5055,0,5057,0,0,0,5060,0,0,0,0,0,5063,0,5064, -5065,0,5067,0,0,0,5068,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5076,0,0,0,0,0,0, -0,5077,0,0,5078,5080,0,0,5083,0,0,0,0,0,0,0,0,5085,0,0,0,0,0,0,5098,5099,5101, -5105,5107,0,5108,0,5109,0,0,0,0,0,0,0,5110,0,0,0,0,0,5117,5118,0,5121,0,5122,0,0 -,5130,0,0,0,5137,0,0,0,5148,0,0,0,0,0,0,0,5151,5154,0,0,0,5155,0,0,5156,5159, -5161,0,0,0,0,5162,0,0,0,0,5163,5164,0,5166,0,0,0,0,0,0,0,0,0,0,5167,0,0,0,5172,0 -,0,0,0,0,0,5178,5179,0,0,5190,0,0,5191,5192,5194,0,0,5198,5201,0,0,0,0,0,5203,0, -5206,5209,0,0,0,0,0,0,5213,0,5214,5216,0,0,0,0,0,5217,0,0,0,0,0,0,0,0,5218,5219, -0,5231,0,0,5244,5249,0,5254,0,5255,0,0,5257,0,0,0,0,0,5258,0,5260,5270,0,5277,0, -0,0,0,0,0,5280,5281,5282,5283,0,0,0,0,0,5284,0,5285,0,0,0,0,0,5287,5288,0,0,0,0, -0,0,0,0,0,0,5289,5291,0,0,5294,0,0,5295,0,0,0,0,0,0,0,5304,0,0,5306,5307,5308,0, -5309,0,0,5310,0,0,0,0,5311,5312,0,5313,0,0,0,0,0,5316,0,0,0,5317,0,0,0,0,0,0,0,0 -,0,5325,0,0,0,0,0,0,5326,0,5327,5329,0,5332,0,0,0,0,5338,0,0,0,0,0,0,0,0,5340,0, -0,5341,0,0,0,5342,0,5343,5344,0,0,5345,0,0,0,0,0,0,5347,5348,0,0,0,0,0,0,0,0,0, -5349,0,5350,0,5354,0,0,0,0,5358,0,0,5359,0,0,5361,0,0,5365,0,5367,0,5373,0,0,0, -5379,0,0,0,5380,0,0,0,5382,0,5384,0,0,0,0,0,0,5385,0,0,0,0,5387,0,0,0,0,0,0,5388 -,5390,5393,0,0,0,0,0,0,0,0,0,0,0,5396,0,0,0,0,5397,5402,0,0,0,0,0,5403,0,0,0, -5404,5405,0,0,0,0,0,0,0,0,0,0,0,0,5406,0,0,0,0,5410,0,0,5411,0,5415,0,0,0,0,5416 -,5434,0,0,0,0,0,0,0,0,0,0,0,5438,0,5440,0,0,0,0,0,0,5441,5442,0,0,0,5443,5444, -5447,0,0,5448,5449,5451,0,0,0,5456,5457,0,0,0,5459,0,0,0,5461,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,5464,0,5466,0,0,5467,0,5470,0,0,5473,0,0,5474,0,0,5476,0,0,0,0,0,0,0,0 -,0,0,0,5477,0,0,0,0,0,0,0,5484,0,0,5485,5486,0,0,0,0,0,5488,0,0,0,0,0,0,0,5489,0 -,0,0,0,0,5507,0,0,0,5510,0,5511,0,0,5512,0,0,0,5513,0,5515,0,0,5516,5517,0,5518, -0,0,5522,0,0,0,0,0,5534,5535,0,0,5536,0,5538,0,0,5543,0,5544,0,0,5545,0,5547,0, -5557,0,0,5558,0,5560,5567,0,0,0,0,5568,0,0,0,5571,5573,0,5574,0,5575,0,0,0,0, -5577,0,0,5598,0,0,0,0,0,0,0,0,0,5600,5609,0,0,0,0,5610,0,0,5612,0,5624,0,5625,0, -0,0,5629,0,5641,0,5642,5643,0,0,0,0,0,0,5651,0,0,0,5652,5653,0,5661,5662,5678,0, -5679,0,0,0,0,5685,5686,0,0,0,0,0,5690,5692,0,5703,0,0,0,0,0,5706,0,0,0,0,5707,0, -0,0,0,0,0,5708,0,0,5709,0,5710,0,0,0,5712,0,5733,0,5734,5735,0,0,5744,5751,0,0,0 -,0,0,0,0,0,0,0,0,0,5752,0,5754,0,0,0,0,0,0,5757,5758,0,5760,5761,0,0,0,0,5763, -5764,5765,0,5766,0,5767,5768,0,5770,0,0,0,0,5776,5780,0,0,0,0,5782,0,0,0,0,5784, -0,0,5788,0,0,0,0,0,0,0,0,0,0,0,5797,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5799,0,0,5801, -0,0,0,5811,0,0,0,0,0,0,5816,0,0,5827,0,0,0,0,0,0,0,0,5830,5831,0,0,5832,0,0,5833 -,0,5835,5844,5845,0,5846,0,0,0,0,0,5850,0,0,0,0,0,5852,0,5855,5857,0,0,5859,0, -5861,0,0,5863,0,5865,0,0,0,5873,5875,0,0,0,5877,0,5879,0,0,0,5888,0,0,5889,5891, -0,5894,0,0,0,0,0,0,5895,0,5897,0,0,0,0,0,0,5907,0,5911,0,0,5912,0,5913,5922,5924 -,0,5927,5928,0,0,0,0,5929,5930,0,5933,0,0,0,0,5949,0,0,5951,0,0,0,0,0,0,0,0,5953 -,0,0,5954,0,5959,5960,5961,0,5964,0,0,0,5976,5978,5987,5990,0,0,0,0,0,5991,0, -5992,0,0,0,5994,5995,0,0,5996,0,0,6001,6003,0,0,0,0,6007,0,0,0,0,0,6008,0,0,6009 -,0,6010,0,0,0,6011,6015,0,6017,0,6019,0,6023,0,0,0,0,0,0,0,6025,0,0,0,0,0,0,0,0, -0,0,6026,0,6030,0,0,6032,0,0,0,6033,6038,6040,0,0,0,6041,6045,0,0,6046,0,0,6053, -0,0,6054,0,6055,0,0,0,0,0,0,6057,0,6063,0,0,0,6064,0,6066,6071,6072,0,0,0,0,0,0, -6075,6076,0,0,6077,0,0,0,0,0,0,0,0,0,6078,6079,0,0,0,0,0,0,0,0,6080,0,6083,0,0,0 -,0,0,6084,0,0,6088,0,6089,0,0,6093,6105,0,0,6107,0,6110,0,0,0,6111,6125,6126,0,0 -,0,6129,0,0,0,0,6130,0,0,0,6131,6134,0,0,0,0,0,0,6142,0,0,0,0,0,6144,0,0,6146, -6151,6153,0,6156,0,6163,0,6180,6181,0,0,0,0,0,6182,0,0,0,0,6184,6195,0,0,6206,0, -6208,0,0,6212,6213,6214,0,6215,0,0,0,6228,0,0,0,6234,0,0,0,0,0,0,6235,6240,0, -6242,6243,6244,0,6250,6255,0,0,0,0,0,6257,0,0,0,6258,6278,0,6284,0,0,0,6285,0,0, -0,0,0,0,0,0,6286,0,0,0,6320,0,0,6322,6332,0,0,0,0,0,0,0,0,6334,0,0,0,0,0,0,0, -6335,0,0,6337,0,6338,0,6339,6340,0,0,6356,6357,6369,0,0,0,6370,6371,6372,0,6373, -0,0,0,0,0,6376,0,0,0,0,0,6382,6383,6384,0,0,0,0,6386,0,6389,6397,6400,6411,0, -6414,0,0,0,0,0,0,0,6415,6416,0,0,0,0,0,0,6417,0,0,0,0,6418,0,0,0,0,0,0,0,6420,0, -6421,6423,6425,0,6429,6430,0,6433,6438,0,0,0,0,0,0,0,0,0,0,6439,6440,0,0,6441,0, -0,6444,0,0,0,0,6446,0,0,0,0,6447,6448,0,0,6450,0,0,0,6454,0,0,6455,0,6461,0,0,0, -0,0,0,6462,0,0,6463,0,6464,0,6465,6467,0,0,0,6468,0,6479,6480,0,0,0,0,0,0,0,6481 -,0,0,6485,6487,0,0,0,0,0,0,6493,0,0,0,0,0,0,0,0,6494,6495,6496,0,0,0,0,0,6498,0, -0,0,6507,6508,0,0,0,0,0,0,0,0,0,0,6511,6512,0,0,0,0,6513,0,0,0,6514,0,0,0,0,0, -6516,0,0,6517,6518,0,0,0,6519,6520,6521,0,6523,0,0,0,0,6524,6528,0,6530,0,0,6532 -,0,6578,0,0,0,6583,0,6584,0,0,0,6587,0,0,0,6590,0,6591,0,0,0,0,0,6592,0,0,0,0, -6593,6594,0,0,0,0,0,6599,6600,0,0,6601,6602,6604,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -6608,0,0,0,0,0,0,0,0,6610,6611,0,6615,0,6616,6618,6620,0,6637,0,0,0,0,6639,0,0,0 -,0,6641,0,6642,0,0,0,6647,0,6660,6663,0,6664,0,6666,6669,0,6675,6676,6677,0,0,0, -0,0,0,0,0,0,6678,0,0,0,6679,0,6680,0,0,0,0,0,0,0,6693,0,0,0,0,0,0,0,0,0,6704, -6705,6706,0,0,6711,6713,0,0,0,0,0,6716,0,0,0,6717,0,6719,6724,0,0,0,0,0,0,0,0, -6725,6726,0,0,0,0,0,6728,6729,6735,0,6737,6742,0,0,6743,6750,0,6751,0,0,6752, -6753,0,0,0,0,0,0,6754,0,0,0,0,0,6756,0,0,0,0,0,0,6763,0,0,6764,6765,0,0,0,6770,0 -,0,0,6776,6780,0,6781,0,0,0,6783,0,6784,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -6785,0,0,0,6792,0,0,0,6793,0,0,6802,0,0,0,0,0,6803,0,0,0,6804,0,0,0,6812,0,0, -6823,0,6824,6839,0,0,0,0,6852,0,0,6854,0,6856,6857,0,0,0,0,0,0,0,0,0,6867,0,6868 -,6870,6872,0,0,0,6873,6874,0,0,0,0,0,6875,0,0,6877,0,0,0,0,0,0,0,6878,0,0,0,6879 -,0,6880,0,0,0,0,0,0,0,0,0,0,6887,0,6888,6891,6893,0,6895,0,0,0,0,0,0,0,0,6899,0, -0,0,0,6901,0,0,0,0,6910,0,6911,0,0,6912,0,0,6913,6914,0,0,0,6915,0,0,0,6916,6919 -,0,0,0,0,0,0,6924,0,6925,0,0,0,6926,6927,6928,0,6929,0,6930,0,0,6931,6935,0,6936 -,0,0,0,0,6939,6940,6941,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6942,6948,6949,0,0,0,0,0,0 -,0,6952,6954,6963,6965,6966,0,0,6967,6968,0,0,0,0,0,0,0,0,0,6969,0,0,6970,6979,0 -,0,6980,0,0,6983,0,0,0,0,0,6984,0,0,0,0,0,0,0,6988,6990,6992,0,0,0,0,0,0,0,6995, -0,0,0,7012,0,0,0,0,0,0,0,0,0,7019,0,0,0,0,0,0,0,0,7021,0,0,7022,7023,7028,0,7030 -,7033,0,0,0,0,0,0,7038,0,0,0,0,0,0,0,0,0,0,7039,0,0,0,0,0,7046,0,7047,0,0,0,0,0, -0,0,0,0,0,0,7048,7052,0,0,0,0,0,7054,0,7060,0,0,0,0,7061,0,7065,0,0,0,0,7067, -7069,0,7070,7071,7072,0,0,7078,0,7080,7081,0,7083,0,0,0,7084,7087,7088,0,0,7090, -0,7093,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7107,0,0,7108,0,0,0,0,0,0,0,0,7110,0,7114,0 -,0,0,0,0,0,0,7115,0,7116,0,0,0,0,0,7117,0,0,7118,0,0,7124,0,7125,0,0,7126,0,0,0, -0,7128,0,0,0,0,0,7129,0,7130,0,7132,7133,0,0,7134,0,0,7139,0,7148,7150,0,0,0,0, -7152,0,0,0,7153,7156,7157,0,0,0,0,0,7158,0,0,0,0,0,0,0,0,0,0,7163,7165,7169,0, -7171,0,0,0,0,0,0,0,0,0,7172,0,7173,7181,0,0,0,0,0,7182,7185,0,0,0,0,7187,0,7201, -7204,0,0,0,0,0,7206,7207,0,0,0,0,7211,7216,0,7218,0,0,0,0,7226,7228,7230,7232, -7233,7235,7237,0,0,0,0,7238,7241,0,7242,0,0,7247,0,0,0,7266,0,0,0,0,0,0,0,7289,0 -,0,7290,7291,0,0,7292,0,7297,0,0,0,0,0,0,0,0,0,0,7300,0,7301,0,0,0,0,0,0,0,0,0,0 -,0,0,7302,0,0,0,0,7305,0,0,0,0,7307,0,7308,0,7310,0,7335,0,0,0,0,0,0,0,7337,0, -7343,7347,0,0,0,0,0,7348,0,7349,7350,7352,7354,0,0,0,0,7357,0,7358,7366,0,7367, -7368,0,0,7373,0,0,0,7374,0,0,0,0,0,0,0,7376,0,0,0,7377,0,0,0,0,0,7378,0,7379, -7380,0,0,0,0,0,7383,0,0,7386,0,0,0,0,7398,0,0,0,7399,7400,0,7401,0,0,0,0,0,0,0, -7402,0,0,0,0,0,7405,0,0,0,0,0,7406,0,0,0,0,0,0,0,0,7421,7427,7429,0,0,0,7435,0,0 -,7436,0,0,0,7437,0,0,0,0,0,0,7438,7443,0,7446,0,7448,0,0,0,0,0,0,0,0,0,0,7456,0, -0,0,0,0,7457,0,0,7461,0,0,0,0,0,7462,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7463,7466,7472, -0,7476,0,0,7490,0,7491,0,0,7493,0,0,0,7498,7499,0,0,7508,0,0,0,0,0,7512,0,0,0, -7513,7514,7516,0,0,0,0,7518,0,0,7519,7521,7522,0,0,0,7526,0,0,7529,0,0,7531,0, -7536,0,7538,0,7539,0,0,7541,7542,7546,0,0,0,0,0,7547,0,7548,0,0,0,0,0,7550,0,0, -7552,7553,0,0,0,0,0,0,0,0,0,0,7554,7563,0,7573,0,0,0,0,0,0,7574,7576,0,7578,7581 -,7583,0,0,0,7584,0,7587,0,0,0,0,0,7589,0,0,0,7594,0,0,7595,0,0,7600,7602,7610,0, -0,0,0,0,7612,0,7613,7614,0,0,7615,0,0,7616,0,7620,0,7621,7622,0,7623,0,0,0,0, -7626,0,0,0,0,7627,7629,7631,0,0,7633,0,0,0,0,0,7639,0,7640,7642,0,0,7643,0,0,0,0 -,7644,0,0,0,0,0,0,0,7645,0,0,0,0,0,7661,7662,7663,7665,0,7666,0,7667,0,7684,7688 -,7690,0,7691,0,0,0,0,0,0,7692,0,0,7700,0,7707,0,7708,0,7709,0,7721,0,0,0,7722,0, -7724,0,0,0,0,0,0,7729,7731,0,7732,0,7733,7735,0,0,0,0,0,0,0,7739,0,0,7741,7745,0 -,7748,0,0,0,7751,0,0,0,7752,0,0,0,0,0,0,0,7753,0,0,7756,0,7757,0,7759,0,7760,0,0 -,0,0,7761,7768,0,0,7769,0,0,7770,0,0,7771,0,0,7772,0,0,7773,0,0,0,0,0,7778,7783, -0,0,0,0,0,7784,7785,0,7790,0,0,0,0,7792,0,7798,0,0,0,0,0,7799,0,7810,0,0,7813,0, -7814,0,7816,0,7818,7824,7825,7826,0,7828,7830,0,0,0,7840,0,7842,0,7843,0,0,0,0, -7844,0,0,0,0,0,0,0,7846,0,0,0,0,0,7856,7857,7858,7862,0,7865,0,0,7866,0,0,7913,0 -,0,0,0,7914,0,0,7915,7917,7918,7919,0,7920,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7921, -7922,0,7924,0,0,7925,0,0,7927,0,7930,7935,0,0,7937,0,0,0,0,0,0,7939,0,7940,0,0,0 -,0,0,7941,0,0,0,0,7945,0,0,0,0,7949,0,0,0,0,0,0,0,0,7950,0,7953,0,0,0,0,0,0,0, -7968,0,0,0,0,7969,7972,7992,0,7993,0,0,0,0,0,0,0,0,0,0,0,7994,0,0,0,0,8007,8008, -0,0,0,0,0,0,0,0,0,0,0,0,8010,0,0,0,8012,0,0,0,0,0,0,0,0,8018,0,8028,8029,0,0, -8030,0,0,8032,8033,0,0,8034,8036,0,0,0,0,0,0,0,0,0,0,8037,0,0,0,8043,8052,8059, -8060,0,0,8061,0,0,0,8062,0,8063,0,8064,0,8066,8068,0,0,0,8080,8081,0,8089,0,0,0, -0,0,8092,0,0,0,0,0,0,8093,8110,0,0,0,0,0,0,0,8111,0,0,0,0,0,8112,8115,0,8117,0,0 -,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8120,8121,8122,8128,8129,8130,8131,0,0,8139,0,0, -8144,0,0,0,0,8145,8146,8153,0,0,0,0,0,0,0,0,8154,0,8157,8160,8162,0,8164,8165,0, -0,0,0,8166,8167,0,0,8179,0,0,0,8185,0,0,0,8186,0,0,8187,0,0,0,8188,0,0,0,0,0, -8204,0,0,0,0,8210,0,0,0,0,0,8213,0,8214,0,0,8215,0,0,0,0,0,0,8218,0,0,0,0,0,0,0, -0,0,8219,0,8221,0,0,8222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8225,0,0,0,8233,0,0, -8242,0,0,0,0,0,0,0,0,0,0,0,8247,0,8248,8252,0,8256,8257,0,0,8261,0,8264,8265,0,0 -,0,0,8267,0,0,0,8269,0,0,0,0,0,0,0,0,0,8270,0,0,0,8278,0,8279,8283,0,0,8285,8286 -,8289,8292,0,0,0,0,8293,8295,8299,8300,8301,0,0,0,0,0,0,8304,8307,0,0,0,0,0,0,0, -8321,0,0,0,8322,8323,8325,8326,8327,0,0,8332,8338,0,0,8340,0,0,0,0,0,8350,0,0, -8351,0,8354,8355,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8360,8372,0,0,0,0,0,0,0,0,8377,0,0, -0,0,8380,0,0,0,8383,0,8384,0,0,0,0,8386,8392,0,0,8394,0,0,0,0,0,0,0,8396,8397,0, -8398,0,8399,0,0,0,0,0,8400,0,8401,8410,8411,0,8412,8413,8422,0,0,0,0,8423,0,0,0, -0,8424,0,0,8425,0,0,0,0,0,0,0,8441,8442,0,0,0,0,0,0,8443,0,0,8444,0,8447,0,0,0,0 -,8451,0,8458,0,8462,0,0,8468,0,8469,0,0,0,8470,0,8473,8479,8480,0,0,0,0,8481, -8483,0,0,0,0,0,0,0,0,0,8484,0,0,8490,0,0,0,0,0,0,8491,8493,8494,0,8528,0,0,0,0,0 -,0,0,8530,0,0,0,0,0,0,0,0,8534,8538,8540,0,0,8541,0,0,8545,0,8557,0,0,8569,8570, -0,0,8571,8574,8575,8579,0,8583,0,0,0,0,8591,0,0,0,0,0,0,0,0,8606,0,8607,0,0,0,0, -0,0,0,0,0,8608,0,0,8609,0,0,0,8610,0,0,0,8611,0,0,8613,8617,8621,0,0,8622,0,8623 -,0,8624,8625,0,0,0,0,0,0,0,0,0,8637,8638,8639,8650,0,0,0,0,8652,8654,8655,0,0,0, -0,0,0,0,0,0,0,8656,0,0,0,0,0,8657,0,0,0,0,0,0,0,0,0,8658,0,0,8659,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,8660,0,0,0,0,0,0,8661,8663,8664,0,0,0,0,8665,0,8669,0, -0,0,0,0,0,0,8671,8674,0,8684,0,8686,0,0,0,8689,0,0,0,8690,0,8706,0,0,0,0,0,0,0,0 -,0,0,0,8710,0,8711,8713,8714,8724,8727,8728,8733,8736,0,8737,8739,0,0,0,0,8742, -8743,8745,8754,0,0,0,0,8756,0,0,0,0,0,0,8757,8760,0,0,0,0,0,8762,8763,8764,0, -8766,8769,8770,8773,0,8774,0,8779,0,0,0,0,8780,0,0,8781,0,0,8783,0,0,0,0,0,0,0,0 -,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8784,0,0,0,0,0,0,0,0,8785,0,0,0,0,8786,0,0,0,0,8788 -,8790,0,0,0,8803,0,8813,8814,0,0,0,0,0,8815,8816,0,0,0,0,8818,0,0,0,0,8822,8828, -8829,0,8831,0,0,0,0,8833,0,0,0,8834,0,0,0,8835,0,8836,0,0,0,8837,0,0,0,0,0,0, -8838,8839,0,0,0,0,0,0,0,0,0,0,0,8840,0,0,0,8841,0,8842,0,0,0,8846,0,0,0,0,0,0,0, -8847,0,8848,0,0,8864,0,0,8866,0,0,8870,8872,0,0,8873,8874,0,0,0,0,0,0,8875,0, -8876,0,0,0,0,8896,8900,0,0,0,0,8901,0,0,0,0,0,8904,0,8907,0,0,0,0,8911,8912,8913 -,0,0,0,8914,0,8915,0,0,0,0,0,0,0,0,0,0,0,0,8916,0,0,0,8929,0,0,0,0,0,0,0,0,0,0, -8930,0,8932,0,8943,0,0,0,8945,8947,0,0,0,0,8949,0,8950,0,8954,8957,0,0,8970,0,0, -0,0,8971,0,8996,0,0,0,0,8997,9000,0,0,0,0,9001,9002,0,9004,9009,9024,0,0,0,0,0,0 -,0,0,0,0,0,0,9027,9082,0,0,9083,9089,0,0,0,0,0,0,9090,0,0,0,9092,0,0,9093,0,9095 -,0,0,9096,9097,9101,9102,0,0,0,0,0,0,0,0,9112,0,0,0,0,0,0,9114,0,0,9120,0,9121, -9122,0,0,0,9123,9124,0,0,9125,0,0,9126,0,9127,0,0,9129,9131,0,0,0,9132,0,0,9136, -0,9144,0,0,9148,0,0,0,0,0,0,9149,0,9152,9163,0,0,9165,0,0,0,0,0,0,0,0,0,0,0,0,0, -9166,0,9169,0,0,0,0,0,0,0,9170,0,0,0,0,9172,0,9174,9175,9176,0,9177,0,0,0,0,0,0, -0,0,9186,0,9187,0,0,0,9188,9189,0,0,9190,0,0,0,0,9191,0,0,0,9193,0,0,0,0,9197, -9198,0,0,0,9208,9211,0,0,0,0,9216,9217,0,9220,0,0,0,0,9221,9222,9223,0,9224,9225 -,0,0,9227,0,9228,9229,0,0,9230,0,9232,0,9233,0,0,0,0,0,9234,9235,0,0,9237,0,0,0, -0,0,0,0,0,9238,9240,0,0,9241,0,0,0,0,9244,0,0,0,0,9247,0,0,0,0,0,0,0,0,0,0,9248, -0,0,0,9249,0,0,0,0,0,9250,0,0,0,0,9251,0,0,9252,9255,0,0,0,9256,0,0,0,0,0,0,0, -9257,0,0,9258,0,0,0,0,0,0,9259,0,0,0,0,0,9262,9263,0,0,9265,9266,0,0,0,0,0,0,0,0 -,9268,9271,0,0,0,0,0,0,0,0,0,9273,0,0,0,9276,9277,9279,0,0,0,0,0,0,0,9280,0,0, -9293,0,0,0,0,0,9297,9301,0,0,0,0,0,0,0,0,0,0,0,9308,9309,9313,9321,9322,0,9326, -9327,0,0,9477,0,9479,0,0,0,0,9482,0,0,0,9483,0,9484,0,0,0,0,0,0,0,0,0,9485,0,0, -9486,0,0,0,9489,0,0,0,0,9490,9491,0,0,0,0,9493,0,9495,9496,0,0,0,0,0,0,0,0,9500, -0,9502,0,0,0,0,0,9504,9507,0,9509,0,9511,0,0,9513,0,0,0,0,0,0,0,0,9515,0,0,0,0,0 -,0,9516,9517,0,0,0,0,9532,0,0,9533,0,0,9538,0,9539,9540,0,0,0,0,9541,0,0,0,9542, -0,0,0,0,0,0,0,0,9544,9545,0,9546,0,0,0,0,0,0,9547,9548,0,0,0,9550,0,9557,0,9558, -0,9561,0,9563,9570,0,9572,9574,9575,0,0,0,9577,9592,0,0,9596,0,0,0,9598,0,9600,0 -,9601,0,0,0,0,0,0,9608,0,9638,9639,0,0,0,0,0,0,0,9641,0,0,9643,9644,9645,9646,0, -0,0,9648,0,0,0,0,0,0,0,9650,9654,0,0,0,0,0,0,0,0,9655,0,0,0,0,0,9656,0,9657,0,0, -0,0,9658,0,0,9659,0,0,9664,0,0,9665,0,9667,9669,0,0,0,0,0,0,0,0,0,0,0,0,9671,0, -9673,9681,0,0,0,0,9682,9683,9684,0,0,0,0,9686,9698,0,0,9700,9701,9702,0,9703, -9717,0,0,0,0,9718,0,9726,0,0,0,0,9727,0,0,0,9728,0,9742,0,9744,0,0,0,9750,0,9754 -,9755,0,0,0,0,0,9756,0,9757,9768,0,9769,0,0,0,9770,9771,0,9773,0,9774,0,9775,0,0 -,0,9776,9777,9784,0,0,0,9786,0,9789,0,0,0,0,9793,9794,0,0,0,9808,0,0,0,0,0,9811, -0,0,0,0,0,0,0,0,0,0,0,0,9812,0,9820,0,9823,0,9828,0,0,0,0,9830,0,0,9833,9836,0,0 -,0,9840,0,0,0,9841,0,0,9842,0,9845,0,0,0,9847,9848,0,0,9855,0,0,0,0,0,0,9856, -9863,9865,0,0,0,0,0,0,0,0,9866,9867,9868,9873,9875,0,0,0,0,0,0,9880,0,9886,0,0,0 -,9887,0,0,9891,0,0,0,0,0,0,0,9906,9907,9908,0,0,0,9909,0,0,0,0,0,0,9910,0,0,0,0, -9913,0,0,0,0,9914,0,0,0,0,0,9922,0,0,0,0,9923,9925,0,0,0,0,0,0,9930,0,0,0,9931,0 -,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9932,0,9939,0,0,9940,9962,9966,0,9969,9970,0,0,9974 -,0,9979,9981,9982,0,0,0,9985,0,0,0,0,0,0,9987,0,0,0,0,0,0,0,9988,9993,0,0,9994,0 -,0,0,9997,0,10004,0,0,0,0,0,10007,10019,10020,10022,0,0,0,10031,0,0,0,0,0,10032, -0,0,10034,0,10036,0,0,0,0,10038,0,10039,10040,10041,10042,0,0,0,0,0,10043,0,0,0, -0,0,10045,10054,0,0,0,0,10055,0,0,10057,10058,0,0,0,0,0,0,10059,0,0,0,0,0,0,0, -10060,0,0,0,0,0,0,0,10063,0,10066,0,0,0,10070,0,10072,0,0,10076,10077,0,0,10084, -0,10087,10090,10091,0,0,0,10094,10097,0,0,0,0,0,0,10098,0,0,0,0,0,0,10103,0, -10104,0,10108,0,0,0,0,0,0,0,0,10120,0,0,0,10122,0,0,10125,0,0,0,0,10127,10128,0, -0,10134,0,10135,10136,0,10137,0,0,10147,0,10149,10150,0,0,10156,0,10158,10159, -10160,10168,0,0,10171,0,10173,0,0,0,10176,0,0,0,0,10177,0,0,0,0,10178,0,0,0,0, -10194,0,10202,0,0,10203,10204,0,10205,10206,0,10207,0,0,0,0,10209,0,0,0,0,0,0,0, -10213,0,0,0,0,0,0,10217,0,10229,0,10230,10231,0,0,10232,0,0,10237,10238,10244,0, -0,0,0,0,10250,0,10252,0,0,0,0,0,0,10255,0,0,10257,0,0,0,0,0,0,10258,0,10259,0,0, -0,0,0,0,0,0,10260,0,0,0,0,0,0,0,10284,10288,10289,0,0,0,10290,0,10296,0,0,0,0,0, -10297,0,0,0,0,0,0,10298,0,0,0,0,10299,10303,0,0,0,0,0,10306,0,0,0,10307,0,10308, -0,0,0,0,10311,0,0,0,0,0,0,0,10315,10317,0,0,0,10318,10319,0,10321,0,10326,0, -10328,0,0,0,0,10329,0,0,10331,0,10332,0,0,0,0,0,0,10334,0,0,10335,10338,0,0,0,0, -0,10339,10349,0,0,0,0,0,0,10351,0,10353,0,0,0,0,0,0,10362,0,10368,0,10369,0,0,0, -10372,10373,0,0,0,0,0,10374,0,0,0,10375,0,10376,0,0,10386,10388,10390,0,0,0,0,0, -0,0,10391,0,0,10392,10394,0,0,10396,0,10397,0,10403,0,0,0,0,0,0,0,0,10404,0, -10405,10410,0,0,10411,0,10412,0,0,0,0,0,0,0,10421,10422,10423,0,0,0,0,0,0,0,0,0, -10425,0,0,10427,0,0,10430,0,0,0,0,0,10432,0,10433,10434,0,0,0,0,10436,10437,0, -10438,0,10439,0,10444,10446,0,0,0,0,0,10448,0,0,0,0,0,10449,0,0,0,0,0,0,0,10451, -0,10453,0,0,0,10454,10457,0,0,10459,0,10469,0,0,0,0,0,10472,10481,0,0,0,0,0, -10482,10483,0,10492,0,0,0,0,0,0,0,0,0,0,10499,0,0,0,10502,0,0,10510,0,10521, -10524,0,0,10525,10526,10528,0,0,0,0,0,0,0,0,10530,0,0,0,0,10533,0,10534,0,0,0,0, -0,0,0,0,0,0,10535,10536,0,0,10544,0,10553,10556,0,10557,10559,0,0,0,0,0,10562, -10563,10564,0,10565,0,0,0,10566,0,10567,0,0,0,0,10575,0,0,10576,0,10578,0,0,0,0, -0,0,0,0,0,0,10585,10586,10587,10589,0,10590,0,0,10594,0,0,0,0,0,10598,0,0,10601, -0,0,0,10602,0,10603,0,10604,0,10605,0,0,10607,0,10626,0,10627,0,0,0,0,0,10629, -10630,10631,0,0,0,10646,0,0,0,10647,0,10650,0,10651,0,0,0,10652,10653,10655,0, -10658,0,0,10659,0,10667,0,0,0,0,10669,0,0,0,0,0,0,0,0,0,10670,0,0,0,10671,0,0,0, -0,10672,10673,0,10674,0,0,0,10676,0,0,0,0,0,0,10678,0,10682,0,0,10692,0,10697,0, -0,0,0,10698,0,0,0,10700,0,0,0,0,0,10703,0,10704,0,0,0,0,0,0,0,10705,0,10715, -10718,10720,0,0,10722,0,0,0,0,0,0,0,0,10723,0,0,0,0,10726,0,0,0,0,0,10727,10730, -10743,0,0,0,0,0,0,10744,0,0,10745,0,0,0,0,0,0,10748,0,0,0,0,10750,0,0,10752, -10753,0,0,0,10756,0,0,0,0,0,0,10758,0,0,0,10759,0,10769,0,0,10772,0,0,0,0,0,0, -10773,0,0,0,10777,0,0,10779,0,0,0,0,0,0,0,0,10780,10784,0,0,0,10789,0,0,0,10791, -0,0,0,0,0,0,0,0,0,10795,0,0,10796,0,10808,0,10809,0,0,0,10810,0,0,0,10812,0,0, -10814,0,0,0,0,0,0,0,0,0,10815,0,0,0,0,10816,10817,0,0,0,0,10819,0,10820,0,0,0,0, -10821,10822,10823,0,10826,10849,0,0,0,0,10850,0,0,10852,0,10853,0,0,10856,0,0, -10857,10858,10859,10860,0,0,0,0,0,0,10863,0,10866,10867,10872,10890,0,0,10891, -10892,0,0,0,0,0,10893,0,0,0,10896,10899,0,0,10900,10902,0,0,0,0,0,10903,0,0,0,0, -0,0,0,0,0,0,0,0,10905,0,10906,0,0,0,0,10908,10911,0,10912,0,0,10916,0,0,0,0,0, -10917,0,10918,0,0,0,10923,0,0,0,0,0,10924,0,0,10928,10929,0,0,10930,0,0,0,10932, -0,0,0,0,10939,0,0,10945,0,0,0,10947,0,0,10948,0,0,0,0,0,0,0,0,0,0,0,0,10958,0, -10960,10962,0,0,10964,0,0,0,10966,0,0,0,0,0,0,0,0,0,0,10967,0,0,0,10968,0,0,0, -10973,0,0,0,0,0,10975,0,0,0,10976,10978,0,0,10982,10984,10987,0,0,10988,0,10989, -0,0,10991,0,0,0,0,10992,0,0,0,10993,0,10995,0,0,0,10996,10997,0,0,0,10998,0, -10999,0,11001,0,0,0,0,0,0,11010,11012,0,11013,11016,11017,0,0,11019,11020,11021, -0,0,0,0,0,0,0,0,0,0,0,0,11022,0,0,11023,11029,0,0,0,0,11031,0,0,0,11034,0,0,0,0, -11055,0,0,0,0,0,11056,11060,0,0,0,0,0,0,11061,0,0,11064,11065,0,11066,0,11069,0, -11085,0,0,0,0,0,11086,0,0,0,11088,0,0,0,11094,0,0,0,11095,11096,0,0,0,0,0,0, -11097,11098,0,0,0,0,0,0,11099,0,0,11102,11108,0,0,0,11109,0,11114,11119,0,11131, -0,0,0,11142,0,0,11143,0,11146,0,11147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11148,0, -11149,11152,11153,11154,0,11156,0,11157,0,0,0,11158,0,0,11159,11160,0,0,0,0,0,0, -0,0,0,0,0,0,11163,0,0,11164,11166,0,0,0,11172,11174,0,0,0,11176,0,0,0,0,0,11182, -11183,0,0,0,11184,11187,0,0,11188,11189,0,0,0,0,0,0,11194,0,0,0,0,0,0,0,11200, -11202,0,0,0,0,0,0,11203,0,11204,0,0,0,0,0,11205,0,0,0,11206,0,11207,0,0,11209,0, -11211,0,11214,0,0,11231,0,0,0,11293,11295,0,0,11296,11297,11302,0,0,0,11307,0,0, -0,0,11309,11310,0,11311,0,0,0,11313,0,11314,0,0,0,0,11334,0,11338,0,0,0,11339,0, -0,0,0,0,11340,0,11341,11342,0,11344,0,11345,0,0,0,11348,11349,0,0,11350,0,0,0, -11355,0,0,0,0,0,0,11356,0,11357,11370,0,0,11371,0,11374,11376,0,0,0,11377,0,0, -11378,11383,0,11386,11399,0,11400,11406,0,0,0,11408,0,0,11409,11412,0,0,0,0, -11417,0,0,0,11418,0,11421,0,11426,11429,0,0,0,0,0,11430,0,11437,0,11438,0,0,0,0, -0,11440,11453,0,0,0,0,0,0,11454,0,0,0,0,11455,0,0,11456,11460,11461,11463,0, -11469,0,11473,0,0,0,0,11474,0,0,0,11475,0,11476,11477,11480,0,0,0,0,11481,0,0, -11484,0,0,11487,0,0,0,0,0,0,0,0,0,0,11497,0,0,11502,0,11509,0,0,11510,11511, -11513,0,0,0,0,0,0,0,0,0,0,11515,0,0,0,0,11516,0,11520,11521,0,0,0,0,0,0,0,0,0,0, -0,11529,11530,11531,11534,0,0,11543,0,0,0,0,0,11547,0,11548,0,0,0,0,0,11552, -11556,0,11557,0,0,11559,0,11560,0,0,0,0,0,0,11561,0,0,11563,11564,0,11565,0,0,0, -0,11567,0,0,0,11569,0,11574,0,11575,0,0,0,11577,0,11578,0,0,0,11580,11581,0,0,0, -11582,11584,0,0,0,0,0,0,0,11587,0,11588,11591,0,11595,0,0,0,0,0,0,0,0,11596,0, -11597,0,0,0,0,11598,11601,0,0,0,11602,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11603, -11604,0,11606,0,0,11608,0,0,0,0,11610,0,0,11611,0,0,0,0,11613,0,11622,0,0,0, -11623,0,0,0,0,11625,0,0,11626,11627,11628,11630,0,0,0,0,0,0,11639,0,0,11646,0, -11648,11649,0,11650,0,0,0,0,0,0,0,0,0,11651,0,0,11652,11653,11656,0,0,11677, -11679,0,0,0,0,11680,0,0,11681,0,11685,0,0,0,0,0,0,0,0,11688,0,0,0,11716,0,11719, -0,0,0,0,0,11721,0,0,11724,11743,0,0,0,0,0,0,0,0,11745,11748,11750,0,0,0,0,0, -11751,0,0,0,11752,11754,0,11755,0,0,0,0,0,0,0,11759,0,0,0,0,0,0,11760,0,0,0, -11761,0,0,0,0,0,0,11766,11767,0,11772,11773,0,11774,0,0,11775,0,11777,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,11778,11780,0,0,0,0,0,0,0,11783,0,11784,0,0,0,11785, -0,0,0,11786,0,0,0,0,11788,0,0,11789,11791,11792,0,0,0,0,11795,11834,11835,11836, -0,0,11837,0,0,0,11838,0,0,11846,11851,0,11852,0,11869,0,0,0,11871,0,0,0,11872, -11874,0,0,0,0,0,0,11875,0,11876,11877,0,0,0,0,0,0,0,0,0,0,11883,0,0,0,0,0,0,0, -11884,0,11885,0,11886,0,0,11887,0,11894,11895,11897,11909,11910,0,11912,11918,0, -0,11920,0,11922,11924,11927,11928,0,0,0,0,11929,0,11934,0,0,0,0,0,11941,11943, -11944,0,11945,0,0,0,0,11948,11949,0,0,0,0,11953,0,11954,0,11955,0,11956,0,0,0,0, -0,11957,0,0,11959,0,0,0,0,0,0,0,0,11961,0,0,0,0,0,11978,0,0,0,11979,11980,11986, -11987,0,11992,0,0,0,0,0,11993,0,0,0,11994,0,11999,12004,12005,12006,0,0,0,0,0, -12011,0,0,12012,12014,0,0,12015,0,0,12019,12028,0,0,12029,0,0,12032,12033,0,0,0, -0,12034,0,12041,12043,0,0,12044,0,0,0,0,0,0,0,12046,0,0,0,0,0,0,0,12054,12055,0, -12056,0,0,0,12060,12064,0,0,0,0,0,12065,12067,12068,0,0,0,0,0,0,0,0,12074,0,0,0, -12075,12076,0,0,0,12079,0,12081,12086,12087,0,0,12088,0,0,0,0,12089,0,12092,0,0, -0,0,12097,0,0,0,0,0,0,0,0,12098,0,0,0,0,0,0,0,0,0,0,0,0,0,12102,12103,12104, -12111,0,0,12114,12116,0,0,0,12118,0,0,0,12119,12120,12128,0,0,0,0,12130,0,0,0,0, -0,0,12131,0,0,0,12132,12134,0,0,0,0,12137,0,12139,0,12141,0,0,12142,0,0,0,12144, -0,0,0,0,0,12145,0,12148,0,12153,0,0,0,0,12154,12171,12173,0,0,0,12175,0,0,0,0, -12178,0,0,0,0,0,0,0,12183,0,0,0,0,0,0,0,0,12184,0,0,0,12186,0,0,0,0,0,12187, -12188,0,0,12189,0,12196,0,12197,0,0,12198,0,12201,0,0,0,0,12203,0,12209,0,0,0,0, -12210,12211,12212,12213,0,12217,12218,0,0,0,0,0,0,0,0,0,12222,0,0,0,0,0,0,0, -12223,0,0,12229,0,0,0,0,12233,0,0,0,0,12234,0,0,12236,12242,0,0,0,12243,0,0,0, -12244,12253,0,12254,12256,0,12257,0,0,12275,0,0,0,0,0,12277,0,0,0,0,0,12278,0, -12289,0,0,12290,0,12292,12293,0,0,12294,0,12295,0,0,12296,0,12297,0,12298,0,0,0, -0,12301,0,0,0,0,0,0,0,0,0,0,0,0,0,12309,0,12338,12340,0,0,0,0,12341,0,0,0,0,0,0, -0,0,12342,12343,0,12344,0,0,0,0,0,0,0,0,0,12345,0,0,0,0,0,0,0,0,12346,0,0,0,0, -12348,0,0,0,0,0,0,0,0,0,0,0,0,12350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12351,0,12355, -12356,12357,0,0,12367,12370,12371,0,0,0,0,0,12372,12376,0,0,0,0,0,0,0,0,12379,0, -12382,0,12383,0,0,12384,0,0,0,0,12393,0,0,12394,0,0,0,0,12398,12403,0,0,12404,0, -0,0,0,0,0,0,0,0,0,0,0,0,12410,0,0,0,12411,0,0,0,12412,0,0,0,0,12420,0,12421,0,0, -0,0,0,12423,0,12425,12429,0,0,0,12431,12432,0,0,0,0,0,0,0,0,0,0,0,0,12434,0,0,0, -0,0,12435,12436,0,0,0,0,0,0,0,0,12437,0,0,0,0,0,12438,0,0,0,0,0,0,0,0,12445,0,0, -0,12450,12451,0,0,0,0,0,0,0,0,12452,12475,0,0,12493,12494,0,0,0,12495,0,0,0,0, -12496,12502,12509,0,0,0,0,12510,0,12512,12513,0,0,0,0,12514,0,0,0,12515,0,12520, -0,0,0,12524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12527,0,0,0,12528,0,0,0,12529,0,0,0, -0,0,12530,0,12535,0,0,12536,0,12538,0,0,0,0,0,0,0,0,0,0,0,0,12540,0,12548,0,0,0, -0,0,12550,0,0,0,12551,12552,0,0,0,12554,0,0,0,0,0,0,0,0,12555,0,0,12562,0,12565, -0,12566,0,0,0,0,0,0,0,0,0,0,0,0,12569,0,0,0,12571,12574,0,0,0,0,0,0,0,12577,0,0, -0,0,0,0,0,12578,12579,12603,0,12608,0,0,12611,0,12612,0,12615,0,12625,0,0,0,0, -12627,12646,0,12648,0,0,12657,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12670,0,0,12671,0, -12673,12677,0,0,0,0,0,0,0,0,0,0,0,12679,0,12681,0,12682,12693,0,12694,0,12697,0, -12701,0,0,0,12703,12704,0,0,0,0,12707,12737,0,0,12739,0,0,12740,0,0,12742,12743, -0,0,0,0,0,0,0,0,0,12745,0,12746,12747,0,12748,0,0,12759,12767,0,0,0,0,12773,0, -12774,12778,0,0,0,0,0,0,0,12779,0,0,0,0,0,12780,12793,0,12824,0,12825,0,12836,0, -0,0,0,12839,0,12842,0,0,0,0,0,0,0,0,0,0,0,0,12843,12845,0,12846,0,0,0,0,12847,0, -0,12850,12852,12853,0,0,0,12854,0,0,0,12855,0,12856,0,12858,0,0,12859,0,12862,0, -12863,0,0,12866,0,12869,12872,12873,0,0,0,0,0,0,0,0,0,12875,0,12877,0,0,12878,0, -0,0,0,0,0,0,0,0,12884,12885,12888,0,12889,0,0,0,0,12893,0,0,0,12895,12896,12898, -0,0,0,0,0,0,0,12902,0,12909,12910,0,12926,0,12928,0,0,0,12929,0,12930,0,0,0,0, -12931,0,12932,12933,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12934,0,12942,0,0,0,0,12944, -0,0,0,0,0,0,0,0,12946,0,0,12948,0,0,12949,0,0,0,0,12950,0,0,0,0,12951,0,12952,0, -12953,0,0,0,12954,12958,12959,0,0,0,0,0,12960,12964,0,0,0,0,0,12966,0,0,0,0,0,0, -0,0,12970,0,12971,0,0,0,0,0,0,12972,0,0,12982,0,0,0,12984,12985,0,12986,12996, -12997,13001,13002,0,0,0,0,13004,0,0,13005,0,0,13007,13009,0,13017,0,0,0,13020,0, -13021,0,0,0,0,0,0,0,0,0,0,13022,0,0,0,0,0,0,0,0,13024,13027,0,0,0,0,0,13028,0,0, -13029,0,0,0,0,0,0,0,13032,0,13037,0,0,0,0,0,0,13040,0,0,13041,0,0,0,13043,13044, -13046,0,0,0,0,13047,0,0,0,0,0,0,0,13049,13054,0,13056,0,0,13060,13061,0,0,0,0,0, -13067,0,0,13068,0,13071,0,0,0,0,0,13077,13078,0,0,0,0,0,13079,13080,13081,0, -13082,0,0,0,13085,0,0,0,0,0,0,0,13086,0,13087,13088,0,0,0,0,0,13094,0,13099,0, -13100,0,0,0,13101,0,13125,13126,13128,13129,0,0,13130,0,13131,0,0,0,0,0,0,13134, -0,0,0,0,0,0,0,0,0,0,0,13150,0,13168,0,0,0,0,0,0,0,0,0,13169,0,0,13170,0,0,0,0, -13174,0,0,0,13176,0,0,0,0,0,13177,0,13178,13183,13187,0,0,0,13189,0,0,13190,0,0, -13191,0,0,13206,0,0,0,13207,0,0,0,0,0,0,0,0,0,0,13212,0,0,13219,13232,0,0,0, -13241,0,13249,13253,0,0,0,0,0,13255,13259,0,13260,13261,0,13262,0,13272,0,0,0,0, -13276,0,0,0,0,13277,13299,0,0,13301,13302,0,0,13303,0,0,13305,0,13310,0,0,0, -13311,0,0,0,0,13325,0,13328,0,0,0,13329,0,0,0,0,0,0,13330,0,0,13331,0,13335,0,0, -13342,0,0,0,0,0,13343,0,13354,0,13362,0,13366,13367,13369,0,0,13371,13372,0, -13373,13374,0,13376,0,13380,13381,13386,0,13387,13388,0,13389,13391,13395,0,0,0, -0,0,13401,13409,0,13410,0,0,0,0,13420,0,0,0,0,0,13422,0,0,0,0,13423,0,0,0,0, -13425,0,0,0,0,0,13427,0,0,0,13428,0,0,13430,13438,0,13439,0,13445,0,13448,13449, -0,0,0,0,0,0,13451,0,13457,0,0,0,0,13458,13459,0,13460,0,0,0,0,13464,13465,13466, -13470,0,13471,13472,13474,13475,0,13476,0,0,13478,13479,0,13481,0,0,0,0,13487,0, -13490,0,13493,0,0,13494,0,0,13495,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13496,13497,0, -13500,0,0,13516,13522,0,0,13525,13528,0,0,0,13530,13535,0,13537,13539,0,13540,0, -13543,0,13544,0,0,0,0,0,0,13545,0,0,0,0,0,0,13547,0,0,0,13549,13555,0,0,0,13556, -13557,0,0,0,0,0,0,0,13558,0,13563,0,0,0,0,13564,0,0,0,0,0,0,0,0,13566,0,0,0,0,0, -0,13569,0,0,13571,0,0,0,0,13573,0,0,0,0,0,0,13578,0,0,0,0,0,0,0,0,0,0,13581,0, -13586,0,13595,0,13600,0,0,0,0,0,0,0,0,13601,13603,0,13604,13605,13606,13607,0,0, -13617,13618,0,0,0,0,0,0,0,13623,0,13625,13627,0,0,0,0,0,0,0,0,13629,0,0,0,13634, -0,0,0,13638,0,0,0,0,0,0,0,0,13654,0,0,0,0,0,0,0,0,0,0,13656,0,13659,0,0,13660,0, -0,13662,0,0,0,13663,0,13664,0,0,0,0,0,13668,0,13669,13671,0,0,13672,0,0,0,0,0,0, -13675,13685,0,13686,0,0,0,13687,0,0,0,13692,13694,13697,0,0,0,13702,0,0,0,0,0, -13705,0,0,0,0,13707,0,0,0,13714,0,0,0,0,0,0,0,0,0,13715,0,13716,13717,0,0,13719, -13724,13730,13731,0,0,0,0,0,0,0,0,13732,0,0,0,0,0,0,0,13734,0,13736,0,0,13737, -13738,13747,0,13751,0,0,13752,0,0,0,13753,0,13757,0,0,13762,13763,0,13764,13765, -0,13766,0,0,13767,0,0,0,13768,0,0,0,0,0,0,0,13769,0,0,13772,0,13775,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,13776,13778,13787,0,0,0,13797,0,13798,0,13801,0,13804, -13806,0,0,0,0,13816,13817,0,0,0,0,0,0,0,0,0,0,0,0,0,13834,0,13836,0,0,13838,0,0, -13839,0,13840,0,0,0,0,13842,0,0,0,0,0,0,13843,0,0,0,0,0,0,0,0,0,13845,0,0,0,0,0, -13858,0,0,13860,0,0,13861,0,0,13862,13863,0,13868,0,13869,13870,0,0,0,0,0,0,0,0, -0,0,13872,0,0,0,0,13873,13878,0,0,0,0,0,0,0,0,0,0,13886,0,13888,13889,13890,0,0, -13891,13894,0,13897,13899,13900,13904,0,0,13906,0,0,0,13909,0,0,0,13910,0,0,0, -13911,0,0,0,0,0,13912,13917,0,0,0,0,13918,0,13919,0,0,13920,0,0,0,13921,0,0, -13922,0,0,0,0,0,0,0,13924,0,13927,0,0,0,0,0,13932,0,13933,0,13934,0,0,13935,0, -13944,0,0,0,13954,0,0,13955,0,0,0,0,13956,0,13957,0,13967,13969,0,0,0,0,0,0,0,0, -0,0,0,0,13970,13990,0,13991,13994,0,13995,0,0,0,0,13996,0,0,13999,0,0,0,14018,0, -14019,0,14021,0,0,0,0,0,0,14041,0,0,0,0,0,0,0,0,14043,0,0,0,0,14046,0,0,0,14048, -14049,0,0,0,0,0,0,0,0,0,0,14051,0,0,14052,14056,0,14063,0,14064,14066,0,0,14067, -0,0,0,0,0,0,0,0,0,14068,0,0,0,14072,0,14074,14075,0,14076,14079,14085,14086, -14087,14093,0,0,0,0,14095,0,0,0,0,0,0,14096,14097,0,0,0,0,0,0,0,14098,0,14102,0, -0,0,0,0,14103,0,0,0,14104,0,0,14105,0,0,0,14107,14108,0,0,14109,0,0,0,0,0,0,0,0, -14117,0,0,0,0,14118,0,0,0,0,14119,0,0,14120,0,0,14121,0,14122,14127,0,14128, -14136,0,0,14138,0,14140,0,0,0,14141,14142,0,0,0,0,14146,0,0,14149,0,14151,0,0,0, -14152,0,0,14153,0,0,0,0,0,0,0,0,0,14154,0,14156,14157,0,0,14159,0,14161,0,0,0,0, -14162,0,0,0,0,0,0,14163,0,0,14173,0,0,0,0,0,0,14174,0,0,14176,0,0,14178,0,0, -14179,14181,0,0,14182,14185,14187,0,14190,0,0,14197,0,0,0,0,0,0,0,0,0,0,0,0, -14198,0,0,0,0,0,0,14199,14200,0,0,0,14204,0,0,14208,0,0,0,0,0,0,0,0,0,0,0,14231, -0,0,0,0,0,0,0,0,0,14234,0,0,14235,0,0,0,14240,14241,0,0,0,14246,0,0,0,14247,0, -14250,0,0,14251,0,0,14254,0,0,14256,0,0,0,14260,0,14261,0,0,0,0,14262,14267, -14269,0,0,14277,0,0,14278,0,14279,14282,0,0,0,14283,0,0,0,14284,14285,0,0,0,0, -14286,0,0,0,14288,0,0,0,14289,0,14290,0,14293,14301,14302,14304,14305,0,14307,0, -14308,14309,0,0,0,0,0,0,0,0,0,0,0,14311,14312,0,0,14317,0,0,0,0,0,0,0,14318,0,0, -0,0,14320,0,0,0,0,14321,14322,0,0,0,0,0,14326,14329,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -14330,14331,0,0,0,0,14332,0,0,0,14333,0,0,14337,14340,0,14341,0,0,14342,0,14345, -14346,0,0,14347,0,14362,0,0,0,0,0,14364,14365,14371,0,14373,0,0,14374,0,14379,0, -14400,0,0,0,0,0,14401,0,0,14405,0,14406,0,14408,14409,0,0,0,14417,0,0,14424,0,0, -0,0,0,0,0,0,0,14430,0,0,0,14431,0,0,14435,0,14440,0,0,0,0,0,0,14442,0,0,14443,0, -0,0,0,0,14446,0,0,0,0,0,0,0,14454,0,14457,0,14460,0,0,14466,0,0,0,0,0,14467,0,0, -0,0,0,0,14469,0,14477,0,0,0,0,0,0,14478,14482,0,0,0,14483,0,0,0,14485,14486,0,0, -0,14487,14488,14489,14492,14493,14494,14495,14496,14497,0,14499,0,14501,0,0,0,0, -0,0,0,0,0,0,14502,0,14507,14512,14513,14514,0,0,0,0,0,0,0,0,0,0,0,14515,14526, -14530,0,14537,0,14544,0,14547,0,0,14548,14550,14551,0,0,14552,0,0,0,14553,0, -14554,0,0,0,0,14556,14564,0,0,14565,14566,0,0,0,0,0,0,14568,0,0,14569,0,0,0, -14571,14576,0,0,14577,14578,14579,0,0,14580,0,0,0,0,14582,0,0,0,0,0,0,0,0,0,0,0, -0,14583,0,0,0,0,0,14587,0,14588,0,0,14600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,14601,0,0,14604,14605,14611,0,14613,0,0,0,0,14615,0,0,0,0,0,0,14627,0,14628,0, -0,0,0,14631,0,14633,14634,0,0,0,0,14635,0,0,0,0,0,0,0,0,14636,0,0,14639,14642,0, -0,0,0,14644,0,0,0,0,14645,14646,0,14653,0,0,14654,0,14658,0,14661,0,0,0,14665,0, -0,0,14668,0,0,0,0,0,0,0,0,0,14669,0,0,14670,0,0,0,14680,0,0,14681,0,0,0,0,0, -14682,14683,0,0,0,0,14686,0,0,0,0,14687,14697,0,0,0,0,14699,14705,14711,0,0,0,0, -0,0,0,0,0,0,14712,0,0,0,14713,0,0,0,0,14719,0,14720,14721,14726,0,0,0,14728, -14729,0,0,0,0,14731,0,0,0,0,0,0,0,14733,14736,14737,0,0,14740,14742,0,0,0,14744, -14753,0,0,0,0,14755,14758,14760,0,0,0,0,0,14761,14762,14765,14771,0,14772,0, -14773,14774,0,0,14775,0,0,14776,0,0,0,0,14777,0,14779,0,0,14782,0,0,14785,14786, -14788,0,0,0,0,0,14795,0,0,0,0,0,0,14798,0,14803,14804,14806,0,0,0,14809,0,0,0,0, -0,0,14810,0,0,0,0,14811,0,14812,0,0,0,0,0,14815,0,0,0,0,0,0,0,0,14816,0,14818,0, -0,0,0,0,0,14819,0,14820,0,14823,0,0,0,14824,0,0,14826,14827,0,0,0,0,0,0,0,0,0,0, -0,0,14830,0,0,0,0,0,14833,0,14845,0,0,0,0,0,14846,0,0,14847,14871,0,14873,0, -14876,0,14877,14878,14880,0,0,0,0,0,14881,0,14882,14894,0,0,0,0,14895,0,14907,0, -14908,0,0,0,0,0,0,0,14911,0,0,0,0,14920,0,0,14931,0,14932,14934,14935,0,0,14936, -0,14945,0,0,0,0,0,0,0,14947,0,0,14948,14949,14951,0,0,14952,0,0,0,14964,14973,0, -0,14990,0,0,0,0,14995,0,0,14998,15001,0,0,15002,15020,0,0,0,0,0,0,15021,0,15022, -0,0,0,0,15023,0,0,15025,15029,15033,0,0,0,15034,0,0,0,15035,0,0,0,0,0,15043, -15044,0,0,0,15045,15046,15048,15050,0,15065,0,0,0,0,15066,0,0,15075,15082,15084, -0,0,15085,15086,0,0,0,0,0,0,0,0,15088,0,0,0,15089,0,0,0,0,15094,0,15096,0,15097, -0,15100,0,0,15102,0,0,0,0,0,0,0,0,15105,0,0,15106,0,15109,15113,0,0,0,15115,0, -15118,0,0,0,0,0,0,15119,0,0,15120,0,0,0,0,0,15123,15129,0,0,0,15130,0,15131,0,0, -15134,0,15135,0,0,0,15137,15138,0,0,0,0,0,0,15139,0,0,0,0,0,15140,0,0,15154, -15162,0,15169,15170,0,15175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15177,0,15178,15179,0, -0,0,0,0,15183,0,0,0,0,0,0,0,0,0,0,0,0,15185,15187,0,15194,15195,15196,0,0,0,0,0, -0,0,15204,0,0,0,0,15206,0,0,0,0,0,15207,0,0,0,0,0,0,0,0,0,15213,0,15214,0,0,0,0, -0,0,0,15232,0,0,0,0,15234,0,15238,15240,0,15248,0,0,0,0,15250,15251,0,0,0,0,0,0, -0,15252,0,0,0,15255,15262,15266,0,0,0,15267,0,0,0,15277,15279,0,0,0,15280,15281, -15282,0,0,0,0,0,15285,0,0,0,0,15289,0,0,15291,0,0,0,0,0,0,0,15296,15297,0,0, -15304,0,0,0,0,15306,0,0,0,0,0,0,15307,15308,0,15309,0,0,15311,0,0,15312,15313,0, -0,0,0,0,0,0,0,0,0,0,0,15314,15317,0,0,0,15318,15319,0,0,0,0,15320,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,15321,0,0,0,0,0,15324,0,15325,15326,0,15330,0,0,0,0,15334,0, -15335,0,15341,0,0,15342,0,0,15343,15344,0,0,0,0,15345,0,0,0,0,15347,0,0,15348, -15349,15350,0,15356,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15357,0,15358,0,0,0,0,0,0,0, -15359,15360,15364,0,15380,0,0,0,0,0,15392,0,0,15393,0,15395,0,0,0,0,0,0,0,0, -15396,0,0,15397,15398,0,0,0,0,0,0,0,0,0,15399,0,15400,0,0,0,15402,0,15405,15410, -0,0,0,0,15411,0,0,0,15412,0,15416,0,0,0,0,0,0,0,15428,0,15435,0,0,15438,0,0,0,0, -15439,0,0,0,15440,0,0,0,15441,15449,15451,0,0,0,0,0,0,0,15452,0,0,15455,0,0,0, -15456,0,0,15458,0,15460,15461,0,0,0,0,0,15462,15464,0,15465,0,0,15466,0,0,15467, -0,0,0,0,0,15468,0,0,0,0,15481,0,0,15484,0,15485,15486,0,0,0,15487,0,0,0,0,0, -15488,0,15492,15498,0,0,0,15499,0,0,0,15500,0,15501,0,0,15512,0,15522,0,0,0, -15524,0,15525,15526,0,0,15527,0,0,15545,15546,0,15548,15552,0,15553,0,0,0,15554, -0,15555,0,15557,15565,15573,15577,15578,0,15582,0,15583,0,0,0,0,0,0,0,0,0,0,0,0, -0,15586,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15588,0,0,0,0,0,15589,0,0,0,0,0,0,0,15593, -15594,0,0,0,0,15595,0,0,0,0,0,0,15596,0,0,0,15597,0,0,0,0,15600,0,0,15601,0,0,0, -0,15602,15603,0,0,0,0,0,0,15604,0,15609,0,0,15612,0,0,15613,0,0,15615,15617, -15618,0,0,15620,0,15636,15637,0,0,15649,0,0,0,0,0,0,0,15650,0,0,15651,0,0,0, -15656,0,15658,0,0,0,15664,0,0,15665,0,0,15668,0,0,0,0,0,15669,0,0,15674,0,0, -15675,0,0,0,0,15676,0,0,0,0,0,0,0,0,0,0,0,15677,0,0,0,0,15678,0,0,0,0,0,15679,0, -0,15681,0,15686,0,0,0,0,15687,0,15688,0,0,15690,0,0,0,15697,0,15699,15700,0,0,0, -0,0,0,0,0,0,15701,0,15702,15703,0,15704,0,15705,0,15707,0,15709,0,15712,15716,0, -15717,0,15718,15720,0,0,0,0,0,15724,0,0,0,15725,0,15726,0,0,0,15740,0,15745, -15746,0,0,15747,0,15748,0,0,0,0,0,15749,0,0,0,15752,0,15753,0,0,0,0,0,0,15759,0, -0,0,15765,0,0,0,0,0,0,0,0,0,15767,0,0,0,15771,0,0,15784,0,0,0,0,15785,15790, -15791,0,0,15792,0,0,0,15807,0,15811,0,0,0,0,0,0,0,0,0,0,0,0,15818,0,0,0,15819,0, -0,0,0,15821,0,0,0,0,0,15822,15824,0,0,15827,0,0,15829,15831,0,15832,0,0,15833,0, -15835,15838,15839,15843,0,0,0,0,0,0,0,0,0,0,0,15844,0,0,0,0,15845,15851,15856,0, -0,0,0,0,0,0,15858,15860,0,15861,0,0,0,15864,0,0,0,0,15865,0,0,0,0,0,0,15866,0, -15872,0,0,15876,0,0,0,0,15877,15878,15883,15885,0,0,15888,0,0,0,0,0,15889,15890, -0,0,0,0,0,0,0,0,15892,0,0,0,0,0,0,0,15893,0,0,15894,0,0,0,15895,0,15896,15897,0, -15898,15901,15902,0,15911,15915,0,15916,0,15924,15935,0,15937,0,0,0,0,0,15950,0, -0,0,0,0,0,0,15958,0,0,0,15961,0,0,15966,0,15967,0,0,15977,0,0,15978,0,0,15981, -15982,15983,0,0,0,0,0,0,0,15986,0,0,0,15990,0,15991,15995,15998,0,15999,0,16000, -0,0,0,0,16008,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16009,16011,0,16013,0,0,0,0, -0,0,0,0,16014,0,0,16015,16023,16024,16025,0,0,16026,0,16030,0,16032,0,16033,0,0, -0,0,0,0,16035,16036,16037,0,0,0,0,0,16039,0,0,0,0,16041,0,0,0,0,0,16043,16044,0, -0,16047,0,0,0,16048,0,0,16049,16050,16052,0,0,0,0,0,16055,0,0,0,0,0,0,0,0,16056, -0,0,0,0,0,0,0,16058,16060,16061,0,0,16063,0,0,16064,0,0,0,16067,16068,0,0,16069, -16078,0,0,0,16079,0,0,0,16080,0,16081,0,0,0,16088,0,0,0,0,0,0,0,0,0,0,0,16089, -16093,0,16097,0,16103,0,16104,16105,0,0,16256,0,0,16259,0,0,0,0,0,0,0,16260, -16261,0,0,16262,0,0,16263,0,16268,0,0,0,0,0,0,0,16269,0,0,16270,16273,0,16274,0, -0,0,0,16275,16276,16277,16280,0,0,0,16281,16284,0,0,0,16286,0,16289,0,0,0,0,0,0, -0,0,0,16290,0,0,0,0,16291,0,0,0,0,0,0,0,16292,0,0,0,0,0,0,0,0,16293,16295,16297, -0,16302,0,16304,0,16305,0,16306,0,0,0,0,0,0,0,0,0,0,0,0,16307,16308,16312,0,0,0, -0,0,0,16313,16315,0,16318,0,0,0,16321,0,0,0,0,0,0,0,16326,16333,16336,0,0,0,0, -16337,16340,0,0,0,0,0,16345,0,0,16346,0,0,0,0,0,0,0,0,0,16347,0,0,16348,0,0,0,0, -16349,0,0,0,16350,0,16357,0,0,0,0,16359,16360,0,0,0,0,16362,16363,16364,16365,0, -0,16366,0,0,0,0,16367,16368,0,16369,16374,0,0,0,0,0,0,0,16376,0,0,0,0,16378, -16379,0,16380,0,0,0,16381,16383,0,0,0,0,0,16390,0,0,0,16399,0,16402,16404,16406, -16407,0,0,0,16409,16411,0,0,0,0,16412,0,16413,16415,16423,0,0,0,0,0,16424,0,0,0, -16428,16434,16435,16449,0,16450,16451,0,0,0,16453,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -16454,0,0,16456,16458,0,0,16459,0,0,16460,0,0,0,0,16462,0,16463,0,0,16466,0,0,0, -0,0,16479,0,0,16480,0,16481,16484,0,0,0,0,0,0,0,0,0,0,16485,0,0,0,0,0,0,16489,0, -0,0,0,0,16491,0,0,16498,0,0,16503,0,16505,0,0,0,0,0,0,0,0,16506,0,0,0,16508, -16509,0,0,0,0,0,0,0,0,16511,16513,0,0,0,16516,0,16517,0,16519,0,16529,0,0,16531, -0,0,0,0,0,0,16534,0,0,16541,16542,0,0,0,0,0,0,0,0,0,16543,16547,16548,0,0,0, -16551,0,16552,0,0,0,16553,0,0,16558,0,0,16562,16565,0,0,0,16570,0,0,0,16573, -16585,0,0,0,16586,16587,16595,0,16596,0,16598,0,0,0,16600,0,0,0,0,0,0,0,0,0,0,0, -0,0,16601,0,0,0,0,16603,0,0,0,0,0,0,0,16604,16612,0,0,0,0,16613,0,16618,0,0,0, -16640,0,0,16641,0,0,0,0,0,0,16645,0,0,0,0,16646,0,0,0,0,0,0,16651,0,0,0,0,16653, -16654,0,0,0,16655,0,0,16656,16667,0,0,0,0,16671,0,16672,0,0,0,16673,0,0,0,0,0, -16676,0,16686,0,0,0,0,16689,0,16690,0,16692,0,16693,0,16694,0,16696,0,0,0,16705, -0,0,0,0,0,0,16707,0,0,0,16709,0,0,0,0,16711,0,16712,16713,0,0,0,16715,0,0,0,0, -16716,0,0,0,0,0,0,0,0,0,16718,16724,0,0,16726,16727,0,0,0,0,0,0,0,16728,0,16729, -0,0,16730,0,0,0,0,0,16731,0,0,0,16732,0,0,0,0,16734,16738,0,0,0,0,0,0,0,0,16743, -0,0,16745,0,0,0,0,0,16749,0,16752,0,0,0,0,16756,0,0,16758,0,16759,0,0,0,0,0, -16760,0,0,0,0,0,0,0,16762,0,16769,0,16770,0,16772,0,0,0,16777,16780,0,0,0,0,0,0, -16781,0,0,16782,0,16784,0,0,16785,16787,16792,0,0,16794,0,0,0,16798,0,0,16809,0, -0,16814,16816,16817,0,16819,0,0,0,0,0,0,0,0,0,0,16820,0,0,16836,16839,0,0,16841, -16851,16857,0,0,16858,16859,0,0,16860,0,0,0,0,0,0,0,0,16862,0,16863,0,0,0,0,0,0, -0,16864,0,0,0,0,0,0,0,16876,0,16881,16882,0,16885,16886,0,16887,0,0,0,16889, -16891,0,0,0,0,0,16894,16895,0,0,0,0,0,0,0,0,0,0,0,16897,0,16898,0,0,0,0,0,16913, -0,0,16924,16925,16926,0,0,16927,0,0,0,16937,16938,0,0,0,16940,16941,0,0,0,16942, -16945,0,16946,16949,16950,0,0,0,16952,16955,0,0,0,16965,0,16969,0,0,16975,0,0, -16976,0,0,0,0,16978,0,0,16981,0,16983,16989,0,0,0,0,16990,0,0,16991,0,0,0,16993, -0,16994,16996,17000,0,0,0,0,0,17002,17004,0,17006,0,0,17007,0,0,0,0,17008,17013, -17014,0,0,0,0,0,0,0,0,0,17021,0,17031,0,0,0,0,0,17033,17036,0,17038,0,0,17039,0, -17045,0,0,17046,17047,0,0,0,0,17048,0,17049,17050,0,17051,17053,0,17054,0,17055, -0,0,0,0,0,17063,0,0,17064,0,0,0,0,0,0,0,17065,0,0,17068,0,0,0,0,0,17072,0,0,0,0, -0,0,17073,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17074,0,17080,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,17081,17083,17084,0,0,0,17085,0,0,0,0,17092,0,0,0,0,0,0,0, -0,0,17093,0,17095,17102,0,0,0,0,0,0,17103,0,0,17105,0,17107,0,0,0,0,17114,0,0,0, -0,0,17115,17125,17127,0,0,17128,0,0,0,17129,17130,0,17131,0,0,0,0,0,17132,17135, -17145,0,0,0,0,0,0,0,0,17146,0,17147,0,17148,0,0,0,0,0,0,17149,17150,0,17151, -17153,0,17155,0,0,0,0,17163,17171,0,17174,0,0,0,0,17179,0,0,17182,17185,0,0,0,0, -0,17186,0,0,17188,0,0,0,0,0,0,0,17189,17191,0,17194,0,0,0,0,0,0,0,0,0,17195, -17196,17203,17204,0,0,17205,17217,0,0,0,0,0,17218,0,0,0,0,17219,0,17220,0,17221, -0,0,17230,0,0,0,0,0,17236,0,17238,17239,0,0,0,17241,17244,0,0,17245,0,17248,0,0, -17251,0,17252,0,0,17264,0,17266,0,0,0,17268,0,0,0,0,17271,17272,0,17273,0,17295, -0,17302,0,17305,0,0,0,17306,0,0,0,0,0,0,0,17308,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -17309,0,17310,17313,0,0,0,0,17314,17315,0,17317,0,0,0,0,17318,0,0,0,0,0,0,0, -17320,0,0,0,0,0,0,17334,0,17344,17348,0,0,0,17350,17351,0,0,17353,0,0,17354,0,0, -0,0,0,0,0,0,0,17355,0,0,0,0,0,0,17356,17357,0,0,17359,0,0,0,17371,0,17372,0,0,0, -17393,0,0,0,0,17394,0,0,0,0,0,17395,0,0,17399,0,0,0,17401,17417,0,17418,0,17419, -0,0,0,0,0,17422,17423,0,0,0,0,0,17424,0,0,0,0,0,17428,17429,17433,0,0,0,17437,0, -0,17441,0,0,17442,0,0,17453,0,0,0,0,0,0,0,0,17454,17456,17462,0,0,17466,0,0, -17468,0,0,17469,0,0,0,0,17470,0,17475,0,0,0,0,0,17479,0,0,0,17483,17484,0,17485, -0,17486,0,17491,17492,0,0,17493,0,17494,17495,0,0,0,17496,0,0,0,17497,0,0,0, -17502,0,0,0,0,0,17503,0,17505,0,17507,0,0,0,17512,17513,17514,0,0,17515,0,0,0, -17519,0,0,0,17522,0,0,17523,0,0,0,0,0,0,0,0,0,17527,0,0,0,17528,0,0,0,17534,0,0, -0,0,17536,0,0,0,17539,0,17540,17543,17549,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17556, -0,0,17558,0,17559,0,0,17560,0,0,0,17563,0,0,0,0,0,0,17564,0,0,17565,17566,0, -17567,0,0,0,0,0,0,17569,17570,0,17575,0,0,0,0,0,0,0,0,0,0,0,17581,0,0,0,17582, -17583,0,17586,0,0,17587,0,0,0,0,0,0,0,17588,0,0,0,0,17596,17597,0,0,17598,17600, -0,0,0,0,0,0,17601,0,0,0,17604,0,0,17605,0,0,17607,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,17612,0,0,17618,0,17621,17622,0,0,0,0,17623,0,0,17624,0,0,17630,0,0, -17631,17633,17634,0,0,0,0,0,0,0,17635,0,0,17636,0,0,17637,0,17638,0,17640,0,0,0, -0,0,0,0,0,0,0,17641,0,0,0,0,0,0,0,0,0,0,17643,0,0,0,0,17645,0,0,0,0,0,0,0,0, -17646,17662,0,0,0,0,0,0,0,0,0,17663,17664,0,17665,17666,0,0,0,17669,17671,17673, -0,17679,0,0,0,0,0,0,0,17684,0,0,0,17686,0,17714,0,0,17720,17722,17726,0,0,17728, -0,0,17729,0,0,0,17732,0,17733,0,17734,0,0,0,17735,0,0,0,0,17737,0,0,0,0,17739,0, -0,0,17741,17742,0,0,0,0,17743,17744,17745,0,0,0,17749,0,17750,17751,17752,17754, -17761,17762,0,17763,0,17766,0,17772,0,0,0,0,0,17775,0,0,0,0,0,0,0,17776,0,0, -17777,0,0,17778,17779,0,17782,17783,0,0,0,0,0,0,0,0,0,0,17784,0,0,0,0,0,0,0, -17821,0,0,0,17822,0,0,0,17823,17825,0,0,0,0,0,17826,17831,17832,17833,0,0,17845, -0,0,0,17846,0,0,0,17848,17850,17854,0,17855,0,0,17859,0,0,0,0,0,0,17860,17861,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17870,17871,0,0,0,0,0,0,17872,0,0,0,17879,0, -0,0,17881,17883,0,17884,0,17885,0,0,17886,0,0,17887,17891,17953,0,0,0,0,17954,0, -0,17955,0,17968,0,0,17972,0,0,0,0,0,17974,0,0,0,0,17976,17978,0,0,17983,0,0,0,0, -18003,0,0,0,0,0,18007,0,0,0,0,0,18009,0,0,0,0,0,0,0,18010,0,0,0,0,0,0,18012,0,0, -18014,0,0,0,18015,0,0,0,18016,0,18017,0,0,0,18030,0,0,0,0,0,0,0,18031,0,0,18036, -18037,18038,0,0,18049,18056,0,18057,18058,0,18059,0,0,0,0,0,0,0,0,18062,0,0,0,0, -18064,0,0,0,0,0,0,0,0,18067,0,0,0,18068,0,0,18075,0,0,18078,18093,18094,0,0,0,0, -0,0,0,0,18097,0,0,0,0,0,18098,18100,0,0,0,18108,0,18111,0,0,18112,0,18113,0,0, -18115,18116,0,18118,0,0,0,0,18121,0,0,0,0,18123,0,0,0,0,0,0,0,0,0,18124,0,0,0,0, -18125,18126,0,18127,0,0,18128,18135,0,0,0,0,0,0,0,0,0,18150,0,0,0,0,0,18151, -18152,0,0,18156,18164,0,18166,18171,0,0,0,0,0,0,0,0,0,18172,18183,0,18184,0,0,0, -0,18185,0,18187,0,0,0,0,0,18188,0,0,0,0,0,0,0,0,18189,0,0,18190,0,0,18191,18192, -0,0,18194,18195,18196,0,0,0,18197,0,18203,0,18204,0,0,0,0,18205,0,0,0,18207, -18208,0,0,18214,0,0,0,18215,18216,0,0,0,18220,0,0,18222,0,0,0,0,0,18223,0,18225, -18231,0,18234,0,18235,0,0,0,0,18240,0,0,18241,18242,0,0,0,0,0,18243,18251,0, -18253,0,18254,0,0,0,18266,0,0,0,0,0,0,18269,18270,18271,18273,18281,0,0,0,0,0,0, -0,0,0,0,0,0,18282,0,18283,0,18284,0,0,0,0,0,0,18285,0,18287,18289,0,0,18290,0,0, -0,0,18308,0,0,0,18310,0,0,0,0,0,0,0,0,0,0,0,0,18311,0,18312,18313,0,18315,0,0, -18316,18320,0,18331,0,18332,0,18336,0,0,0,0,18337,0,18340,0,0,0,0,0,0,0,0,0, -18341,0,18344,18345,0,18346,0,0,0,0,0,18348,0,18351,0,0,18356,0,0,0,0,0,0,18357, -0,0,0,0,0,18367,0,0,0,18368,0,18369,0,18370,18371,0,0,0,18437,18444,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,18445,18450,0,0,0,0,18451,0,18452,0,0,0,18453,0,0,0,0,0,18455,0, -0,0,18456,0,18457,0,18460,0,0,18461,0,0,0,0,0,0,0,0,18466,0,0,18467,0,0,0,0, -18473,0,0,0,18476,0,18477,0,0,0,18478,18479,18480,0,0,0,18485,0,0,0,18486,0,0,0, -0,0,0,18488,18490,0,0,0,0,0,0,18491,0,0,0,0,0,18495,0,0,18496,0,0,0,0,0,0,18505, -0,18521,0,18522,18523,0,0,0,18525,18526,0,0,0,0,0,18527,0,0,0,0,18532,18533,0, -18534,0,0,0,0,0,0,18535,18537,0,18538,0,0,0,0,0,0,18540,18541,18542,18543,0, -18546,0,0,0,0,18553,18556,0,0,18558,0,0,18569,18571,0,0,0,18572,0,18574,0,0,0,0, -18586,0,0,0,0,0,18588,0,0,18589,0,0,0,0,0,0,18590,0,18592,0,0,0,0,18594,0,0,0, -18596,0,0,18597,18598,0,0,18601,0,0,0,0,18602,0,0,0,18603,18604,0,18605,0,0,0,0, -18608,0,0,18611,0,0,0,0,0,0,0,0,0,18612,0,18616,0,0,18617,18619,0,0,0,18628,0,0, -0,18629,0,0,18630,0,0,0,0,0,0,0,18631,0,18632,0,0,18635,18637,0,0,0,0,0,0,18641, -18643,18648,0,18652,0,0,18653,0,18655,18656,0,0,0,18657,0,0,18666,18674,0,0,0,0, -18677,18684,18685,0,0,18686,0,0,18690,0,0,0,0,0,0,0,18695,18696,0,0,0,0,0,0,0,0, -0,0,18697,0,0,18700,0,0,0,0,0,0,18702,0,18708,0,0,18709,0,18710,0,0,18711,0, -18714,0,0,18718,0,0,0,0,0,0,18719,0,0,18722,0,18726,0,0,0,0,0,0,0,0,0,0,0,0,0, -18731,0,0,0,0,0,18739,18741,0,0,18742,0,18743,18744,18746,18748,0,18752,18753,0, -0,18754,18763,0,18765,0,0,0,18766,0,0,0,18769,0,0,0,0,0,18773,18778,18779,18781, -0,0,18784,18787,0,18788,0,18793,0,0,0,0,0,0,18795,0,0,18800,0,0,0,0,0,18801, -18804,0,0,0,0,0,0,0,18806,0,0,0,18811,18815,18816,0,0,0,0,18825,0,0,18827,18829, -0,0,18830,0,0,0,0,18831,0,0,18832,0,0,0,0,18833,0,18840,0,18841,0,18842,0,0,0,0, -18843,0,18844,0,0,0,0,0,0,18845,18846,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -18848,0,0,0,18853,18860,0,0,18862,18866,0,0,18867,18869,0,0,18874,18881,18891,0, -0,0,0,0,0,0,0,0,0,18892,0,0,0,0,0,0,0,0,18895,0,18896,0,0,0,18900,0,0,0,18901,0, -18902,18915,18916,0,0,0,0,0,0,0,0,18919,0,0,0,0,0,18920,0,0,0,18921,18929,0,0,0, -0,18930,0,0,0,0,0,0,18932,0,0,0,0,18934,18942,0,0,0,18951,18957,0,0,0,0,18958,0, -0,0,0,18959,18960,0,0,18961,0,0,18962,0,0,0,0,18963,18964,0,0,0,18965,0,18967,0, -0,0,0,0,0,0,0,0,18968,0,18969,0,18970,18973,18976,0,0,0,0,0,0,18977,0,0,0,18981, -0,0,0,18990,0,18998,0,0,0,0,0,18999,19003,0,0,19005,0,0,0,19006,0,0,0,0,0,0, -19008,19011,0,0,19018,0,0,19019,0,19024,0,19031,19032,0,19039,0,19041,19050,0,0, -0,19051,19055,19056,0,19059,19063,19064,0,0,19088,0,0,0,19093,19094,0,0,0,0, -19095,0,19096,0,0,0,19097,0,0,19098,0,19099,19100,0,0,19103,0,0,0,0,0,0,0,19111, -0,0,0,0,0,0,19112,0,0,0,19116,19117,0,19121,19122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,19123,19124,0,0,0,0,0,0,0,19125,19126,0,19128,0,0,0,0,0,0,0,0,0,0, -19129,19130,19131,19132,0,0,19146,0,0,19147,19156,19158,0,0,0,0,0,0,0,0,19182, -19185,0,0,19187,0,0,0,19193,0,0,0,0,0,19194,0,19197,0,0,0,0,19198,0,0,0,0,0,0,0, -0,0,0,19202,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19203,0,19205,19210, -0,0,0,19213,0,19218,0,0,0,19223,19229,0,0,19230,0,0,19231,19232,19233,19239,0,0, -0,0,0,19240,0,19248,19249,0,0,0,0,19254,0,19256,19258,19259,0,0,19261,0,19266,0, -0,0,19272,0,19278,19281,19282,0,0,0,0,0,0,0,0,0,0,0,0,19283,0,0,19284,0,0,19285, -19287,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19288,19291,0,19292,0,0,0,0,19297,0,19298,0,0, -0,0,19302,19303,0,0,0,0,19304,19305,0,0,0,0,19314,0,0,19315,0,0,19321,0,0,0,0,0, -0,0,19322,0,19333,0,19334,19335,0,19336,19337,0,0,0,0,0,0,0,0,0,0,0,19346,0,0, -19353,0,19354,19362,0,19366,19367,0,0,19369,0,19375,0,19377,19380,19388,0,0,0,0, -0,19389,19390,0,0,0,0,19392,0,0,0,0,0,19402,0,0,0,0,0,0,0,0,19412,0,0,19413, -19422,0,19424,0,0,0,19425,0,0,0,19428,0,0,0,0,19431,0,0,0,0,0,19432,0,0,0,0,0, -19448,19459,0,0,19461,0,19462,19463,0,19467,19474,19482,0,0,0,0,19494,0,0,0,0, -19501,0,0,0,0,0,0,0,0,0,0,19502,19504,0,0,0,0,0,0,0,19505,0,0,0,0,19506,19507,0, -0,0,19508,0,0,19511,0,0,19514,0,19515,0,19516,0,19518,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,19530,0,19537,19538,0,19543,19546,0,19547,19551,0,0,0,0,0,0,19552, -19553,0,0,0,0,0,0,0,0,0,0,0,0,19555,0,0,19556,0,0,0,0,0,0,0,0,0,0,0,0,19560, -19561,0,0,19562,0,0,0,0,0,0,19565,19567,0,19568,0,0,0,19569,19570,0,19578,0,0,0, -0,19580,0,0,0,0,19581,19584,0,0,0,0,0,0,0,19585,19586,0,0,0,19587,19588,0,19589, -0,0,0,0,0,0,19592,19593,19599,0,19600,0,0,19604,0,0,19605,0,19606,19608,19610,0, -19613,19614,0,0,0,0,0,0,19616,19617,0,0,19618,0,0,19619,0,0,0,19620,19621,19631, -0,0,19632,19634,19636,0,19643,0,0,19644,19658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,19659,0,0,0,0,0,0,0,0,0,0,0,19675,19677,0,0,0,0,19679,0,19683,0,19684,0,0, -0,0,0,0,19687,0,0,0,0,0,0,0,0,19688,19689,19692,0,0,0,0,0,0,0,19695,19697,0,0,0, -0,0,19698,19699,0,0,19700,0,19702,0,0,19703,0,0,0,0,0,0,19704,19708,0,19710,0, -19713,0,0,0,19715,0,0,0,0,19718,0,0,0,0,0,0,0,19720,0,19722,0,0,19725,0,0,0,0,0, -0,0,0,0,0,0,0,0,19730,0,0,0,0,0,19731,0,19734,19735,19739,0,0,19740,0,19741,0,0, -0,19746,0,0,19747,0,19771,0,0,0,0,0,0,0,0,19772,19775,0,0,0,0,0,0,19778,0,0,0,0, -0,19779,0,0,19780,19790,0,19791,0,0,19792,0,0,0,19793,0,0,19796,19797,0,0,0, -19799,0,0,0,19801,0,0,0,0,19803,0,19804,0,19805,0,0,19807,0,0,0,19808,0,0,0,0,0, -0,19809,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19816,0,19821,0,19822,19830,19831,0,0, -0,19833,0,0,0,0,0,0,0,0,0,0,19838,0,0,0,0,19839,0,0,19843,0,0,0,0,19845,0,0,0,0, -19847,0,0,19848,0,19849,0,0,0,0,0,0,0,19851,0,0,0,19854,0,0,0,0,0,0,0,0,0,19864, -0,19865,0,19866,0,0,0,0,0,0,0,19868,0,0,19870,0,0,19871,0,0,19872,19873,19875,0, -19880,19882,19884,0,0,19885,19886,19888,0,0,0,0,0,0,0,0,0,0,0,0,19890,19892, -19893,0,0,19894,0,0,0,19895,0,19896,19902,0,0,19903,0,0,19905,0,0,0,19906,0, -19908,0,19909,19911,0,0,0,19913,19920,0,19938,19939,19940,0,0,0,0,0,0,0,19942,0, -19943,0,19945,0,0,0,19951,19952,19954,19960,0,19965,0,19971,0,0,0,0,0,19975,0, -19976,0,19990,0,0,19991,0,19993,0,19995,0,0,0,19998,19999,20001,0,20003,20005,0, -20011,20012,0,0,0,0,0,0,20014,0,20020,0,0,0,0,20021,0,0,0,0,0,20023,20024,0,0,0, -0,0,20025,0,0,20027,0,0,20029,0,0,20032,0,0,0,0,20044,20045,0,20048,20049,0,0, -20050,0,20052,0,0,20054,20057,0,0,0,0,0,0,0,0,0,20059,0,0,20061,0,20062,0,20064, -0,0,20066,0,0,20067,0,0,0,0,20069,0,0,0,0,0,0,20070,20071,0,0,0,0,0,0,0,0,0,0,0, -20072,0,0,20073,20074,0,0,0,0,0,20075,0,20078,0,0,0,0,20080,0,20081,0,0,0,0,0,0, -20095,0,20098,0,0,0,0,0,0,0,20107,0,0,0,0,0,0,0,0,20112,0,0,0,20113,20114,0,0,0, -20115,20123,20124,0,0,0,20131,20133,20134,0,0,0,0,20136,0,0,20137,20138,20150,0, -20152,0,0,0,20153,0,0,20154,0,0,0,20158,0,20163,0,0,20164,0,0,0,0,0,0,0,20166,0, -20168,0,20170,0,20175,0,0,20178,0,0,0,0,20223,0,0,0,0,20224,0,20226,0,0,20230,0, -20231,0,0,0,0,20232,0,0,20233,20234,0,20244,0,20247,0,0,0,0,0,0,20249,0,0,0, -20250,0,0,0,0,20251,0,20253,0,20254,0,0,0,0,20256,0,0,20264,0,0,0,0,20266,0,0,0, -20278,0,0,20279,20282,0,0,0,0,0,20283,0,20284,0,20285,0,20287,20290,0,0,0,0, -20292,0,0,0,0,20293,20297,0,0,0,0,0,0,20299,0,20300,20303,0,0,0,0,0,0,20307,0,0, -20308,0,20309,0,20310,0,0,0,0,0,0,20312,0,0,0,20314,0,0,0,0,20315,20316,0,20322, -0,0,0,0,0,0,20339,0,0,0,20342,0,0,0,0,20352,0,0,0,0,0,0,0,0,0,0,20362,0,0,20365, -0,20375,20377,0,0,0,0,0,0,0,0,0,0,0,20378,20379,0,20380,0,0,20381,0,20382,0, -20383,0,20388,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20390,20392,20393,0,0,20395,0,0,0,0,0, -20396,0,0,0,0,0,0,0,0,20398,20415,0,0,0,20417,0,0,20420,0,0,20426,20428,0,20431, -0,0,20432,0,20433,20434,20435,0,0,0,0,20440,0,0,0,0,0,20442,0,20443,0,20446,0,0, -0,0,20448,0,20451,0,0,0,0,0,0,0,0,0,20452,20453,0,0,20454,0,0,0,0,0,0,20457,0, -20458,0,0,0,20465,0,0,0,0,0,20469,0,0,0,20473,0,20476,0,0,0,0,0,0,0,0,20477,0,0, -20485,0,0,20486,0,0,20487,0,20496,0,20497,0,0,20498,0,0,0,0,0,0,0,0,0,0,20499, -20500,0,20501,0,0,0,0,0,20520,20527,0,20529,0,0,0,0,20539,0,0,20540,0,0,0,20543, -0,0,0,20546,0,0,0,0,0,20548,0,0,20563,0,0,20564,0,20566,0,0,0,0,0,20589,0,0,0,0, -20590,0,0,20593,20594,0,0,0,0,20595,0,20597,20598,0,0,0,20618,20620,0,0,0,0, -20621,0,0,0,0,20627,0,0,0,0,0,20628,0,0,0,20629,0,20630,0,0,20639,0,0,0,0,0, -20707,0,0,20709,0,0,0,20713,20714,0,0,0,0,0,20724,20725,0,0,0,0,20726,20728, -20729,0,20733,0,20734,0,20735,20736,0,20737,0,0,20744,0,20745,0,20748,0,0,20749, -0,0,0,0,0,0,0,0,20750,0,0,0,0,20754,0,0,0,20761,0,0,20763,0,0,0,0,0,0,0,20766,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,20767,0,0,0,0,20768,0,20769,20777,0,0,0,0,0,0,20785,0, -0,0,20786,20795,20801,0,20802,0,20807,0,0,20808,0,0,20810,0,0,20811,0,20812,0,0, -0,0,0,20813,0,0,20818,20820,20821,0,0,0,20822,0,20823,0,0,0,20826,0,0,0,0,0,0,0, -20829,20830,20831,0,20832,20836,0,0,20839,0,0,20840,20842,0,20843,0,20844,0, -20854,0,0,0,20855,0,0,0,0,20856,0,0,0,20869,0,0,20871,0,0,0,0,0,0,0,20873,0,0,0, -0,0,20876,0,0,0,0,0,20880,0,0,20882,0,0,0,0,20883,20884,0,0,20890,0,0,0,0,0,0,0, -0,0,20891,0,0,0,0,0,20905,0,20906,20910,0,0,20912,20915,0,0,0,0,0,20916,0,20917, -0,20919,20920,20922,0,20927,0,20928,20929,20930,0,0,20935,0,0,20939,0,0,20941,0, -0,0,20943,0,0,0,20946,20947,0,0,0,0,0,20950,0,20954,0,0,20955,20964,0,0,20967,0, -0,0,0,0,20973,20975,0,0,0,20984,0,20987,20988,0,0,0,0,0,20989,0,0,0,20995,0, -20998,0,20999,0,0,0,0,21000,21001,0,0,0,0,21008,0,21010,0,21016,0,0,0,21017, -21018,0,0,0,0,0,21021,21026,21027,21028,0,0,21029,0,0,0,0,0,21030,0,0,0,0,0,0,0, -0,0,0,0,0,0,21031,21032,0,0,0,0,0,21037,0,0,21038,0,0,0,0,0,0,0,0,0,21039,0, -21041,0,21046,21047,0,0,0,21049,21053,0,0,21057,21064,21065,0,0,21066,21067,0,0, -0,21069,0,0,0,21071,21072,0,0,21073,0,21074,0,0,21078,0,0,0,0,21079,0,0,21080, -21081,0,0,21086,21087,0,21089,0,0,0,0,0,0,0,21091,0,21093,0,21094,0,0,0,0,0,0,0, -0,21095,0,0,0,0,0,21096,0,21098,0,0,0,0,0,0,0,21099,0,0,21100,21101,21102,0,0,0, -0,0,21103,0,21104,0,0,0,0,0,21105,21108,21109,0,0,21112,21113,0,0,0,0,0,0,21115, -21122,21123,0,0,0,0,0,21125,0,0,0,0,0,0,0,0,21129,21131,0,0,21134,0,0,0,21137, -21142,0,21143,0,0,21144,0,21145,21146,0,21152,21154,21155,21156,0,0,0,21160,0,0, -0,0,0,0,21161,0,21164,0,21166,0,0,0,0,21170,0,0,0,0,21171,0,0,21172,0,21174,0, -21175,0,0,0,0,0,21176,21179,21188,0,0,0,21189,0,0,21190,0,0,0,21192,0,0,21193,0, -0,0,21198,0,21212,0,0,21213,0,0,0,0,0,0,21215,21216,0,0,21223,21225,0,21226,0,0, -0,0,21227,21228,0,0,21229,0,0,0,0,21230,21236,0,0,0,0,0,0,0,0,0,0,0,0,0,21237,0, -0,21238,21239,0,0,0,0,21256,0,0,0,0,0,21257,0,0,0,0,0,0,0,21259,0,0,0,21263,0, -21272,0,21274,0,21282,0,0,0,0,0,0,0,0,21283,0,0,0,0,0,0,0,0,21294,0,0,21297,0,0, -0,0,21298,0,0,0,21299,0,21300,21302,0,21316,0,21318,21322,21323,0,21324,0,21326, -0,0,0,21327,21328,0,0,0,21352,0,0,21354,21361,0,0,0,0,0,0,0,0,0,0,0,0,0,21362,0, -0,0,21363,0,0,0,0,0,0,0,0,0,21366,0,0,21367,21372,21374,0,0,0,21375,21377,0, -21378,0,0,0,21380,0,0,0,0,0,0,0,0,0,0,21381,0,0,0,0,0,0,21382,0,21383,0,0,21384, -0,0,21385,0,0,0,0,21389,21390,0,0,0,0,0,0,0,0,0,0,0,0,0,21397,21398,0,0,0,0,0,0, -0,0,0,0,21399,0,21400,0,0,0,0,21402,0,0,0,21403,21404,0,21405,21406,0,0,0,21407, -0,0,0,0,0,0,0,0,0,0,0,0,21408,0,0,0,0,21409,0,21421,0,21422,0,0,0,21425,21428,0, -0,0,0,21429,0,0,0,0,0,21433,0,0,0,0,0,0,0,0,0,0,21434,0,21443,0,21444,21449,0, -21452,0,21453,21454,0,0,0,21457,0,0,21458,0,0,0,21460,21461,0,0,21464,0,0,0, -21473,21478,0,0,21479,0,0,21481,21483,0,0,0,0,0,0,0,0,21484,0,0,21485,21486,0,0, -21488,0,0,0,0,0,0,21523,0,0,21525,0,0,0,0,0,0,0,21526,0,0,0,0,0,0,21529,21530,0, -0,21531,0,0,21533,0,0,21539,21564,0,21567,0,0,0,0,0,0,0,0,21575,0,0,0,0,21577,0, -0,0,0,0,21591,0,0,21604,0,0,0,0,0,0,0,0,0,21605,0,21606,0,0,21617,21618,21619, -21620,0,0,0,0,0,0,0,0,0,0,0,0,0,21623,0,0,0,0,21631,0,21635,0,0,0,0,21639,21646, -21653,21662,0,0,21663,21664,0,21666,0,0,21667,0,21670,21672,21673,0,21674,21683, -0,0,0,0,0,21684,0,21694,0,0,0,0,21695,21700,0,21703,0,21704,0,0,21709,0,0,0, -21710,0,0,0,0,0,0,0,0,21711,0,0,0,21712,0,21717,0,21730,0,0,0,21731,21733,0,0,0, -0,21737,21741,21742,0,21747,0,0,0,21749,0,0,0,0,0,0,0,0,0,0,0,0,0,21750,0,0,0,0, -0,21752,0,0,0,0,21753,0,0,0,0,0,0,21755,21756,0,21757,0,0,0,0,0,0,21760,0,0, -21763,0,0,0,0,0,0,0,0,0,21764,0,0,21766,0,0,21767,0,0,0,0,0,0,0,0,0,21773,0, -21774,0,0,21775,0,0,0,0,21776,0,0,21777,0,0,0,0,0,0,0,0,0,21780,21787,21788, -21791,0,0,0,21797,0,0,0,0,0,21805,0,0,0,0,21806,0,21807,21809,0,21810,21811,0, -21817,21819,21820,0,21823,0,21824,0,0,21825,0,0,21826,21832,0,0,0,0,0,21833, -21848,21849,0,0,21867,21870,21871,21873,0,0,0,21874,0,0,0,0,0,0,0,0,0,21875,0, -21878,0,0,0,21879,0,21881,21886,0,0,0,0,21887,0,0,21888,21894,21895,21897,0, -21901,0,21904,0,0,21906,0,0,0,21909,21910,21911,0,0,21912,0,0,21913,21914,21915, -0,21919,0,0,0,0,0,0,0,21921,0,0,21922,21933,21939,0,0,0,0,0,0,0,0,0,0,0,21944,0, -0,0,0,0,21945,0,21947,0,0,0,0,0,0,0,0,0,0,21949,0,0,0,21950,0,0,0,0,0,0,0,0,0,0, -0,0,0,21951,0,21952,0,0,0,0,0,0,0,0,0,21954,21957,0,0,0,0,21958,0,21959,0,0,0,0, -0,0,21962,21963,0,0,0,0,0,0,0,0,21964,21965,0,0,21969,21970,0,0,0,21974,0,0, -21980,21981,0,21982,0,0,0,0,0,21985,0,21988,0,21992,0,21999,0,0,0,0,0,0,22001,0, -22002,0,0,0,0,0,0,22003,0,0,0,0,0,22004,0,0,0,22008,0,22009,22015,0,0,22016,0,0, -0,22017,22019,0,0,0,0,0,0,0,0,0,22020,0,0,0,0,0,0,0,0,0,0,22021,22037,0,22039,0, -0,0,22040,0,0,0,22048,22049,0,0,22053,22055,22056,22059,0,0,22060,22061,0,0, -22064,0,0,0,0,22066,0,0,0,0,0,0,0,22073,0,0,0,22074,22075,0,0,0,0,0,0,0,22076,0, -0,0,0,22077,22084,22099,0,0,0,0,0,0,0,22104,0,0,22107,0,22108,0,22109,0,22110,0, -0,0,0,0,0,0,22111,22119,0,22120,22122,0,0,0,0,22125,0,0,0,22128,22129,0,0,0,0,0, -0,22141,0,0,0,22142,0,0,22144,22146,0,22148,22149,22151,22154,0,0,0,22162,0,0,0, -0,22164,22177,0,0,0,0,22179,0,22182,22183,0,0,22184,22188,0,0,0,0,0,0,0,0,22190, -0,22194,22201,0,0,22208,0,22209,0,22212,0,0,22215,0,22223,22231,0,0,22232,0, -22234,0,0,22235,22236,0,22237,0,22240,0,0,0,0,0,22241,0,0,0,22242,22246,22247,0, -0,0,22259,22268,0,22269,0,0,0,0,0,0,0,22270,0,0,0,0,22271,0,22272,0,22277,0,0,0, -0,0,22278,22280,22283,22286,0,0,22287,22289,0,0,22290,0,22293,0,0,0,0,0,0,0,0,0, -0,22295,0,22301,22302,0,0,0,22305,0,22308,0,0,0,0,0,0,0,0,0,0,22315,0,0,0,22317, -0,22334,0,0,0,22335,0,0,0,0,0,22336,0,22338,22344,0,22347,22349,0,22350,0,0,0,0, -0,0,0,22357,0,0,0,0,0,22358,0,0,0,0,0,0,0,0,0,0,22359,22360,0,0,0,0,0,0,0,0, -22361,22366,0,0,22369,0,22370,22373,0,0,0,0,0,22375,0,22377,0,0,0,0,0,22378,0,0, -0,0,22381,0,0,0,0,22382,0,22383,0,0,0,0,0,0,0,0,0,22391,0,0,22392,22395,22396, -22402,0,0,0,0,0,0,0,0,0,0,0,0,0,22405,0,0,22406,0,0,22408,0,0,22409,22410,0,0,0, -0,0,0,22424,0,0,0,0,22426,0,0,0,22427,0,22428,0,22432,0,22435,22442,22443,0,0,0, -0,22444,0,0,0,0,0,22446,0,22454,0,22455,0,0,0,22465,0,22470,0,22471,0,0,0,0, -22472,22473,0,22487,0,0,0,22488,0,0,0,0,22489,0,0,22499,0,0,0,0,0,0,22514,0,0, -22515,0,0,0,0,0,0,0,22516,0,0,0,22517,22520,0,0,0,22534,0,0,22535,0,0,22536,0, -22540,22553,0,22555,0,0,0,0,22561,0,0,22562,0,0,0,0,0,0,0,0,0,0,0,22566,0,0,0,0, -22567,22568,0,0,22575,0,22579,0,22582,22583,22585,0,0,0,0,0,22586,0,0,22587,0,0, -22590,0,0,0,0,0,22591,0,22592,0,0,0,0,0,22593,0,22602,0,0,22604,0,0,22609,0,0, -22618,0,0,0,0,0,0,22619,0,22624,22625,0,0,22638,0,0,0,0,0,22639,0,0,22640,0,0,0, -0,0,0,0,22644,0,22645,22647,0,0,0,0,22652,22653,0,0,0,22654,0,22655,0,0,0,22656, -0,0,0,0,0,0,0,0,0,0,22673,22675,22676,0,0,22678,22679,0,22691,0,0,0,0,0,0,0, -22693,0,0,22696,0,22699,22707,22708,0,0,0,0,0,0,0,0,22718,0,22719,0,0,0,0,22723, -0,0,0,22724,22725,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22726,22728,0,0,0,0,0,0,0,0,22729, -0,0,22731,0,0,0,0,22732,22735,22736,0,0,0,0,22739,0,22749,0,0,22751,0,0,0,0,0,0, -0,0,0,0,0,22758,0,0,0,0,0,22760,0,0,0,0,0,22764,22765,22766,0,22768,0,0,0,0,0, -22769,22770,0,0,0,0,0,0,22771,0,0,22772,22775,0,22776,22777,22780,0,0,22782, -22784,0,22787,0,22789,22796,0,0,0,0,0,22798,0,0,0,0,0,0,22802,0,22803,22804,0,0, -0,0,0,0,0,0,0,0,22805,0,0,22810,22811,22814,22816,0,22825,22826,0,22831,22833,0, -0,0,0,0,0,0,0,0,22834,0,22836,22838,0,22839,0,0,0,0,0,22840,0,22847,0,0,0,0,0, -22856,22857,0,22858,22859,0,0,22862,0,0,22864,0,0,0,0,22865,0,0,0,0,0,0,0,0,0,0, -0,22866,0,22867,22868,0,0,0,0,22869,0,22871,0,22872,0,22873,22881,22882,22884, -22885,0,0,0,0,0,0,0,22886,22887,0,22894,0,22895,0,0,0,22900,0,22901,0,0,0,0, -22904,0,0,0,0,22905,22907,0,0,0,22915,22917,0,0,22918,0,0,0,22920,0,0,0,22929, -22930,0,0,0,22941,22942,0,0,0,22943,0,0,0,22944,0,0,0,0,0,0,0,22946,0,22947,0,0, -22954,0,22956,0,0,22962,0,0,0,0,0,0,0,22963,0,0,22964,0,0,0,0,0,0,0,22965,0, -22968,0,0,0,22969,0,0,0,0,0,22970,0,22971,0,0,0,0,0,22978,0,0,22979,0,22987,0,0, -22989,0,0,0,0,0,0,22990,0,23005,0,0,0,0,0,0,0,23006,23007,23008,0,0,23023,23024, -23029,0,0,0,0,23030,0,0,0,0,0,23032,0,0,0,0,0,23035,0,0,0,0,23038,0,0,0,23048,0, -23049,23052,23053,23060,23061,0,23063,0,0,0,0,23067,23068,0,0,0,23069,23073,0,0, -0,23127,0,23128,0,0,0,0,0,23129,0,23138,23141,0,23149,0,0,23150,0,0,0,23152,0,0, -0,0,0,0,0,0,23154,0,0,0,0,23157,23159,23160,0,0,0,0,0,0,0,0,0,0,0,0,23180,0,0,0, -0,23181,0,0,23188,0,23189,0,0,0,0,0,0,0,0,0,0,0,0,23195,0,0,23196,23199,0,0,0,0, -0,0,0,0,0,23202,0,23204,0,23207,0,23209,23210,0,0,0,0,0,0,23227,23229,0,0,23230, -23234,23238,0,0,0,23245,23246,23248,0,0,0,0,23249,23254,0,0,0,23265,0,0,0,0,0,0, -0,23268,0,23276,0,0,0,0,23277,0,23297,0,23298,0,0,0,0,23299,0,23302,0,0,23303, -23312,0,0,23314,0,23320,0,0,0,0,23324,0,23325,0,23328,0,23334,0,0,0,23337,0,0,0, -0,23343,23344,23346,0,23348,0,0,0,0,0,0,0,0,23353,0,0,0,0,23355,0,23356,23358,0, -0,0,23359,23360,0,23361,0,23367,0,23369,0,0,23373,0,23378,23379,0,23382,23383,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,23387,0,0,0,0,0,0,23388,23390,0,0,23393,23398,0,0,0, -23399,0,0,0,23400,0,0,0,0,23401,0,0,0,23415,0,0,0,0,0,0,0,0,23416,0,23422,0, -23443,23444,0,0,0,0,23448,0,23454,0,0,0,0,0,0,23456,0,0,23458,23464,0,0,0,0,0,0, -23465,0,0,0,23470,23471,0,0,23472,0,0,0,23473,23496,0,0,0,0,0,0,0,0,23497,0, -23499,0,0,23502,0,0,23503,0,0,23513,0,0,23515,0,0,0,23517,0,0,0,0,23518,23519, -23521,23524,0,23525,23528,23539,0,0,0,0,0,23541,0,0,23544,0,0,23556,0,0,23557,0, -0,0,0,0,0,0,0,0,0,0,0,0,23559,0,23560,0,0,23561,0,0,23566,0,0,0,0,0,23568,23569, -23570,0,0,0,0,23571,0,23574,0,0,0,0,0,0,0,0,0,0,0,23575,0,23579,0,0,23581,0,0,0, -0,0,0,23587,0,0,0,0,0,0,0,23596,23598,0,0,0,0,23602,23606,0,0,23607,0,23608,0,0, -0,23614,23616,0,0,0,0,0,23618,0,0,23619,0,0,0,0,23621,23626,0,23627,0,0,0,0,0,0, -0,23629,0,23630,0,0,0,0,23634,0,23636,0,0,0,0,0,0,23638,0,0,0,0,23640,23667,0, -23669,0,0,0,23681,0,0,0,0,0,0,0,23682,0,23683,0,0,0,0,0,23684,0,0,0,23685,23689, -0,23693,23694,23700,0,23702,0,23709,0,0,0,0,0,0,0,23712,0,0,0,0,0,23714,0,0, -23715,0,0,0,0,23718,0,0,23720,0,0,0,0,23722,0,0,0,23726,23729,0,23741,23746,0, -23748,0,0,0,0,23749,0,0,0,0,0,23750,0,0,0,0,23751,0,23753,0,0,0,0,23757,23765,0, -0,0,23770,0,0,0,0,0,0,0,23771,0,23772,23781,0,0,23796,0,0,0,0,23798,0,23799,0,0, -0,23802,0,0,23806,0,23807,0,0,23808,0,23809,0,23819,0,0,0,23821,0,23827,0,0,0, -23829,0,0,0,0,0,0,0,23830,0,0,0,0,0,0,23832,23833,23834,23835,0,0,0,0,23837, -23838,0,0,0,0,0,23846,0,0,0,0,0,0,23847,0,0,0,0,0,23879,23881,0,0,23882,23883, -23895,0,23899,0,0,0,0,23901,0,0,0,0,0,0,23902,0,0,0,0,0,23903,23905,0,23906,0, -23907,23918,23919,23920,0,23922,0,23924,0,23927,0,23934,0,23937,23941,0,23942, -23946,0,0,0,0,0,23955,23956,23958,0,0,0,0,0,0,23959,0,23962,23965,0,23966,0,0,0, -0,23967,23968,0,0,23973,0,0,23974,0,0,0,0,23975,0,23976,0,0,0,0,0,0,0,0,0,0,0,0, -0,23977,0,0,0,0,0,0,0,0,23980,0,0,23984,0,23985,0,0,23987,0,0,23988,23990,23991, -0,0,0,0,0,0,23992,0,0,0,0,0,0,0,0,23994,0,0,0,23998,0,0,0,0,0,0,0,0,0,23999,0,0, -24003,0,24004,0,24006,0,0,0,24007,0,0,24008,0,0,0,0,0,0,0,24009,0,0,24010,0,0, -24011,0,0,24013,24014,0,0,24015,24016,24027,0,24028,24029,0,24030,0,0,0,0,0, -24033,24034,0,24035,0,0,24036,0,0,24044,0,24048,24049,24063,24067,0,24068,24070, -0,0,24071,24078,24087,0,24090,0,0,0,24095,0,24098,24101,24104,24106,0,24107,0,0, -0,24108,0,0,0,0,24110,24111,0,24113,0,0,24115,24120,0,0,0,0,0,0,24124,0,24125,0, -24126,0,24127,0,0,0,0,0,24135,0,0,24136,0,24137,24142,0,0,0,24146,0,0,24147, -24149,24154,0,24163,0,0,0,24165,24166,24167,0,0,0,0,0,0,0,0,0,0,24169,24170, -24175,0,0,0,24178,0,0,24179,0,0,24181,0,24184,24197,0,24201,24204,0,0,0,0,0,0, -24206,24212,24220,0,0,0,24224,0,0,0,0,0,0,0,0,24226,0,24234,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,24235,0,24236,0,0,0,0,0,24239,24240,24241,0,0,24248,0,0,24249,0, -24251,0,0,0,0,0,0,24253,0,24268,0,0,0,24269,0,24271,24272,0,0,0,0,24273,0,0, -24274,0,0,24279,0,0,0,0,0,0,0,24280,0,24293,24294,0,0,0,0,0,0,24296,0,0,24323,0, -0,0,24329,24330,24331,24339,0,24351,0,0,24369,24370,0,0,0,24371,0,0,0,0,24372, -24373,24374,0,0,0,0,0,24378,0,0,0,0,24379,0,24381,0,24383,24389,0,24390,0,0, -24394,24395,24400,0,0,0,24401,24402,0,24406,0,0,0,24411,0,0,0,24415,0,24416,0,0, -0,0,0,24417,0,24419,0,24422,0,24423,24428,0,24435,0,0,0,24439,0,0,0,24440,24442, -24446,0,0,0,24447,24448,24449,24452,0,0,0,0,24453,24457,0,0,24458,24459,24460,0, -24465,0,0,0,0,0,0,0,24470,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24471,0,24473, -24474,24475,24476,0,24478,0,0,0,0,24480,0,0,0,0,0,0,0,0,0,0,24481,0,0,0,0,0,0,0, -0,0,0,24482,24485,0,0,0,0,24486,0,0,0,24488,0,0,0,24494,0,0,0,0,24497,0,0,24498, -0,0,0,24499,24506,0,0,0,24507,0,0,24511,0,0,24513,24514,0,0,0,0,0,24517,0,24518, -0,24520,0,24521,24524,24525,0,0,0,0,0,24527,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24528,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24537,24539,0,24540,0,0,0,24548,0,0,0,0,0,24549, -24550,0,0,0,24553,24554,0,24555,0,24556,0,24558,0,0,0,0,0,24560,0,0,0,24561,0,0, -0,0,0,24562,0,0,0,0,0,0,0,0,0,0,0,0,0,24567,0,0,0,0,0,24569,0,0,0,24574,0,24575, -0,0,0,0,0,0,0,0,0,0,0,24577,24581,0,24584,0,0,0,0,0,24585,0,0,0,0,0,24586,0,0, -24587,0,24588,0,0,0,0,0,0,0,0,0,0,24590,24591,0,0,0,0,24592,0,0,0,0,0,0,0,24594, -0,0,0,0,0,0,0,24596,24597,0,0,0,0,24602,24603,0,0,0,0,24604,0,0,24605,0,24610,0, -0,24611,0,0,0,0,24612,24615,24616,24624,0,0,0,24627,0,24638,24639,0,0,0,0,24640, -0,0,0,24655,24656,24657,0,0,0,0,0,0,0,0,24662,0,24663,24664,0,0,0,0,0,24665,0,0, -0,0,24667,0,0,0,0,0,0,24668,24669,0,24670,24674,0,0,0,24675,0,24678,0,0,24679,0, -0,0,24681,0,24683,0,0,0,0,24684,0,24685,0,0,24686,0,0,24688,24689,0,0,0,0,24690, -24691,0,0,0,0,0,0,0,24697,0,24698,0,0,0,0,0,0,0,0,24709,0,0,0,0,0,24710,0,24712, -0,0,0,0,0,0,24713,24714,0,24715,0,24716,24718,0,24719,0,0,0,0,24720,0,0,24725,0, -0,24738,0,24749,24750,0,0,0,24752,0,0,0,24753,0,0,0,24758,0,0,0,0,0,24762,0, -24763,0,0,0,0,0,0,0,24764,0,0,0,0,0,24765,24767,24768,0,24772,0,0,0,0,24773,0,0, -0,0,24777,0,0,0,0,0,24785,0,24786,24788,0,0,0,24789,0,0,0,0,24794,24798,0,24799, -24800,0,0,0,24803,0,24804,24806,0,24807,0,0,0,24810,0,0,0,0,0,0,24827,24828,0, -24835,0,0,0,0,0,0,24836,0,0,0,0,0,24839,0,24843,24844,0,0,0,0,0,0,0,0,0,0,24847, -0,0,24848,0,0,0,0,0,0,24849,0,24850,24851,0,0,0,24852,0,24853,0,0,0,0,0,0,0,0,0, -24854,0,24855,0,0,24868,0,0,0,24883,0,0,0,24884,0,24895,24897,0,0,0,0,0,24899,0, -0,0,0,0,24900,0,24913,0,0,0,0,0,0,24914,0,0,24917,24930,24931,0,0,0,24932,0,0, -24939,0,0,24942,0,0,0,0,0,0,0,0,0,24945,24950,0,24951,0,0,24953,0,0,0,24954,0, -24959,0,0,0,24961,0,0,24962,0,24964,24968,24970,24972,0,0,0,0,0,24976,0,0,0, -24977,0,24982,0,0,24983,0,0,24984,0,0,0,24993,0,0,0,24994,0,0,25001,0,0,0,25003, -0,0,25018,0,0,25023,0,0,0,25034,0,0,25035,25036,0,25037,0,0,0,0,0,0,0,25039,0,0, -0,0,0,25040,0,0,0,0,0,0,0,25042,0,0,25043,25045,0,0,0,0,0,0,25049,0,0,25051,0, -25052,25053,0,0,25054,0,0,0,25055,0,0,0,0,25057,25059,0,0,25060,25064,0,25065, -25069,25070,0,0,0,0,25072,0,25073,0,25090,0,0,25092,25093,25101,0,0,0,0,0,0, -25105,25108,0,0,25113,0,0,25115,25116,0,0,0,0,0,0,25117,0,0,0,25120,25121,0,0,0, -0,0,0,0,25125,0,0,0,25126,0,25130,25134,0,25139,0,25143,0,0,0,25151,0,25161,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25163,0,0,0,0,0,0,0,25174,0,25175,0,25207,0,0, -0,25209,0,0,0,0,25213,0,25219,0,25223,0,25225,0,0,0,25227,0,0,0,25228,0,0,0, -25229,0,0,0,0,0,0,0,25231,25233,0,0,0,0,25237,25239,0,0,0,25243,0,0,0,25252,0, -25257,25258,0,0,0,0,25260,25265,0,25268,0,0,25273,25324,0,25325,0,25326,0,0,0,0, -0,0,0,0,25327,0,0,0,0,0,25328,0,0,0,0,0,0,25332,0,0,0,25333,0,0,0,25336,25337, -25338,0,0,25343,0,25350,0,0,0,0,0,0,0,25352,0,25354,0,25375,0,25379,0,0,0,0, -25384,0,0,0,0,0,0,0,0,0,25386,0,25388,0,25390,0,0,25399,0,0,25401,0,0,0,25402,0, -0,0,25407,0,0,0,0,0,0,0,0,0,0,0,25413,25415,0,0,25417,0,0,0,0,0,0,0,25419,0,0,0, -25421,0,0,0,25424,0,0,0,0,25433,0,0,0,0,0,0,0,0,0,25435,0,0,0,0,0,0,25436,0,0,0, -25437,0,0,25440,0,0,0,0,0,0,25442,0,0,25443,0,25446,0,0,25449,0,0,0,25450,0,0,0, -0,25452,0,25453,25454,25455,0,0,0,25456,0,25457,0,0,0,25459,0,25461,0,25468,0,0, -0,0,0,0,0,0,25469,0,0,0,0,0,25471,0,0,0,0,0,25474,0,0,0,0,0,0,0,0,25475,0,0,0,0, -25477,0,0,0,0,25483,0,0,0,0,0,25484,0,0,0,0,0,0,0,0,0,0,0,0,25485,0,25497,0,0, -25498,0,25504,0,25510,0,25512,0,0,25513,25514,0,0,0,0,0,0,25517,25518,25519,0, -25520,0,0,0,0,0,0,0,25521,0,25522,25527,25534,0,25536,0,25537,0,0,25548,25550,0, -0,25551,0,25552,0,0,0,0,0,25554,0,25555,0,25556,25557,25568,0,0,0,25570,25571,0, -0,0,0,0,0,25574,0,0,0,0,25579,0,0,0,25581,0,0,0,25582,0,0,0,0,0,0,0,0,0,25588,0, -0,0,0,25589,0,0,0,0,25590,0,25591,25592,25593,0,25594,0,0,0,25596,0,25597,25615, -0,0,0,0,0,25618,0,0,0,0,25619,25623,0,0,25629,0,0,25631,0,0,0,25635,25636,0,0, -25649,0,0,0,0,25654,0,0,0,25661,25663,0,0,25671,0,0,25678,25698,0,25699,25702, -25703,0,0,0,0,0,0,0,0,25704,0,0,0,0,0,25706,0,0,25710,0,25711,0,25712,0,25715, -25716,25717,0,0,25718,25728,25732,0,0,0,25734,0,0,0,0,0,0,0,0,0,25737,0,0,25739, -0,0,0,25740,0,25741,25745,0,25746,0,25748,25772,25778,0,0,0,0,0,25780,0,0,0,0, -25781,0,25782,25784,25785,0,0,0,25789,0,0,0,0,0,0,25797,25801,0,0,0,25808,25809, -0,0,25811,25814,25815,0,0,25817,0,0,0,0,0,0,0,0,25820,0,0,0,0,25832,25833,0,0,0, -25846,0,0,0,25847,25848,0,0,0,0,0,0,0,0,0,25849,25850,0,0,25851,0,0,25852,0, -25862,0,0,0,25863,25865,0,0,0,0,0,0,0,25867,25868,0,25869,25874,0,25875,0,25876, -25877,0,0,0,0,25878,25902,0,0,0,0,0,0,0,25903,25904,25905,0,0,0,25908,25909,0,0, -0,0,25910,0,0,0,0,0,0,0,25912,0,25913,0,0,0,0,0,0,0,0,25914,0,0,25916,0,0,0,0,0, -25917,25927,0,0,0,0,25928,0,0,25930,0,0,0,25933,0,0,25938,25942,0,0,0,0,0,0,0, -25945,0,25950,0,25956,0,0,25961,25962,0,0,25963,0,25964,25965,25966,0,0,0,0,0, -25967,0,0,0,0,25968,0,0,0,25969,25971,0,0,0,0,0,25973,25975,0,0,0,0,0,0,0,25978, -0,25981,0,0,0,25982,0,0,0,25984,0,0,0,0,0,0,0,25993,0,0,0,0,0,0,0,0,0,0,0,0,0, -26002,0,0,0,26005,0,0,0,26006,26007,0,0,26014,26015,26016,0,0,0,0,0,0,26017, -26018,26020,0,26022,26023,0,0,0,26024,26028,0,26029,26033,26034,26044,0,0,0,0,0, -26046,0,0,26047,0,0,26049,0,26050,0,26051,0,0,0,0,0,26053,0,0,0,0,26054,26059,0, -0,0,0,0,0,26060,0,26066,0,0,0,0,0,0,0,0,0,0,0,0,26067,0,26069,0,0,26071,0,0,0, -26073,0,26074,26077,0,0,0,0,26078,0,0,0,26079,0,26090,0,0,26094,0,0,0,0,0,0,0,0, -26095,0,0,0,0,0,0,0,0,0,0,0,26096,26101,0,26107,26122,0,26124,0,0,26125,0,0,0,0, -0,0,26136,26141,26155,0,0,0,0,0,0,0,0,0,26164,26166,0,0,0,26167,0,26170,26171,0, -0,26172,0,0,26174,0,0,0,0,0,0,0,0,0,0,0,0,0,26175,0,0,0,26176,26177,0,26321, -26322,0,26323,0,0,26324,0,0,0,0,0,0,0,26325,0,26331,0,0,0,0,0,0,26335,0,0,0, -26350,0,0,0,26379,0,0,26382,26383,26385,0,0,26392,26406,0,0,0,0,26411,0,0,0,0,0, -26412,0,0,26420,0,0,26423,0,26424,26426,26432,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -26435,0,26436,0,0,0,0,0,26441,0,26444,0,0,0,26446,0,0,0,0,26447,0,0,0,0,26449,0, -26450,26452,0,26453,26454,0,0,0,26455,0,0,0,26456,0,0,26458,0,0,26460,0,26463,0, -0,0,0,0,0,0,0,26464,26470,0,0,0,0,0,0,0,0,0,26473,0,0,26474,0,0,0,0,0,0,0,26475, -0,0,0,0,0,0,0,26477,0,26485,0,0,26486,0,26487,0,0,26488,26493,26494,0,0,26495,0, -26497,26504,26506,0,0,0,0,0,26507,0,0,0,0,0,26509,0,0,26510,0,0,0,0,0,0,0,0,0,0, -0,0,0,26512,0,26513,26515,0,0,0,26518,0,0,0,26519,0,26524,26526,0,0,0,26527,0, -26532,0,26533,26537,26558,0,0,0,26559,0,0,0,26571,0,0,26573,0,26588,0,26593,0,0, -0,0,0,0,26603,0,26604,0,0,0,0,0,0,0,0,0,0,26606,0,0,0,0,0,0,0,26607,26609,26611, -26614,0,0,0,26616,26620,0,26621,0,0,0,0,0,26627,0,26629,0,0,26630,0,0,26632, -26643,0,0,0,26644,0,0,0,0,0,0,0,0,0,26646,26647,0,0,0,26650,0,0,26656,0,0,0,0, -26663,26670,26671,0,0,0,26685,26686,26687,0,26689,0,0,0,0,26744,0,26745,0,26747, -26748,0,26749,26750,26751,0,0,0,0,26752,26755,0,0,0,26756,26769,0,0,0,26774,0,0, -0,0,0,26775,0,26777,26778,0,26786,0,0,0,26787,0,0,0,0,0,0,0,0,0,0,0,0,0,26788,0, -0,26789,0,0,0,0,0,26791,0,26792,26793,0,0,0,26794,0,26797,26798,0,0,0,26800,0,0, -26803,0,26804,0,0,0,0,0,0,0,0,0,26805,0,0,26808,0,0,26809,0,0,0,0,0,0,0,26812,0, -26825,0,0,0,0,0,0,0,26826,0,0,26827,26829,26834,0,0,0,0,26835,0,0,26849,0,26851, -0,0,0,0,0,0,0,0,0,26852,0,26853,26857,0,26858,0,26859,0,0,0,0,0,0,0,26876,0, -26878,26882,26883,0,0,0,0,26890,26894,0,0,0,0,26895,26896,0,0,0,0,0,26900,0,0,0, -0,0,0,0,26911,26913,26914,26915,26916,26919,0,0,0,26921,26922,0,0,26925,0,0,0, -26928,0,0,26929,26930,0,0,0,26931,0,26932,0,0,0,0,0,26933,0,0,0,0,0,0,26937,0,0, -26943,0,0,26944,0,0,0,26946,0,0,0,0,0,0,0,26956,0,26958,0,0,26963,0,0,0,0,0,0,0, -26965,0,26969,26970,26972,0,0,0,0,0,26973,0,26974,0,26978,0,26980,0,0,0,0,0,0, -26982,0,26986,26987,0,26990,0,0,0,0,27003,27006,0,0,27007,27010,27012,27013,0,0, -0,0,0,0,0,0,27014,27015,27018,0,27019,0,0,0,0,0,27025,0,0,0,27026,0,0,0,0,27029, -27030,27031,27034,0,0,27036,27037,0,0,0,27038,27042,0,0,0,27044,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,27045,0,0,0,0,0,0,0,27046,0,0,0,0,0,0,0,27047,27049,0,27050,0,0,0, -27051,27052,0,27055,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27056,27058,27059,0, -27061,0,27064,0,0,0,0,0,27069,0,0,27070,0,0,0,0,0,0,0,27072,0,0,0,0,0,0,0,0, -27076,0,0,0,0,0,27078,0,27079,0,0,0,27081,0,0,0,0,0,0,27082,0,27083,27086,0,0,0, -0,27087,0,0,0,0,0,27088,27090,0,27094,0,0,27095,0,27099,27102,0,0,0,27103,0,0,0, -0,27105,0,0,0,27106,0,0,0,0,0,0,27107,0,0,0,0,27108,27117,0,0,0,0,27118,0,0, -27124,0,27126,0,0,27130,27131,0,0,0,0,0,0,27147,0,0,0,0,27148,27149,0,0,0,0, -27150,27151,0,27152,0,27159,0,0,0,27164,0,0,0,0,0,0,0,27175,0,27189,0,0,27191,0, -27193,0,27195,0,27198,0,0,0,0,0,27200,0,0,0,0,27202,0,0,0,0,27203,0,0,27204,0,0, -27206,0,27207,0,0,0,0,27209,0,0,0,27213,0,0,27216,27219,27220,27222,27223,0, -27224,0,27225,27226,0,0,27233,0,0,0,0,27235,0,27237,0,27238,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,27239,0,27242,27243,0,27250,0,0,0,27251,0,27253,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,27254,27255,27258,0,0,0,27259,0,0,0,0,0,0,27267,0,27276,27278, -0,0,0,0,0,0,0,0,0,27296,27297,27301,0,0,0,0,0,0,27302,0,0,0,0,0,0,27312,27313,0, -0,0,0,0,27318,0,27320,0,27329,0,27330,27331,0,27332,0,0,0,0,27340,0,0,0,27348,0, -0,0,0,0,0,27350,0,27351,0,0,0,0,27355,0,0,27358,27359,27361,0,0,0,27365,0,27367, -0,27376,27378,0,0,27379,0,0,0,0,0,0,27396,0,27397,27404,0,0,0,0,0,27408,0,0,0,0, -27453,0,0,0,27456,0,0,0,27458,0,0,0,0,0,0,0,27459,0,0,0,27460,0,0,27461,0,27465, -27467,0,0,27469,0,27470,0,27471,0,27477,27482,0,0,0,0,0,0,27484,0,0,0,0,0,0, -27485,0,0,0,0,0,27493,0,27494,27502,0,0,0,0,0,0,0,0,0,0,0,0,27511,27532,0,0,0, -27533,27545,0,0,0,27546,0,0,0,0,0,0,0,0,0,0,27547,0,0,27549,27550,0,27551,0,0,0, -0,0,0,0,27555,0,0,27571,0,27573,27574,27575,27577,0,27578,0,0,27579,27585,0,0,0, -0,0,27586,0,0,27588,27589,0,0,0,0,27596,0,0,27600,0,0,0,0,0,0,0,0,0,0,0,27608,0, -0,0,0,0,0,0,0,0,0,0,27610,0,0,0,27618,0,0,27620,0,0,0,27631,0,0,27632,27634,0, -27636,27638,0,0,0,27643,0,27644,27649,0,0,0,0,0,0,0,0,0,0,0,0,0,27651,27660,0, -27661,0,0,0,0,0,0,0,27662,0,0,27664,0,27665,0,0,0,27669,0,27671,0,0,0,27673, -27674,0,0,0,27682,0,0,0,27711,0,27712,27713,27719,27720,0,0,27728,0,27729,0,0,0, -0,0,0,0,0,0,27731,0,0,27732,0,27733,0,27738,0,0,0,27742,0,0,0,27743,27744,0,0,0, -0,0,0,27745,27746,0,0,0,27747,27748,27751,27752,0,0,0,27768,27770,0,0,0,27774, -27775,0,27776,27777,0,0,27781,0,27784,0,27786,0,0,27791,0,27792,27793,27804,0, -27812,27813,0,0,0,0,0,0,0,0,27814,0,27825,0,27827,0,0,0,0,27828,27861,27862,0,0, -0,27864,0,0,0,27865,27884,0,27889,0,0,0,0,0,27890,0,27891,0,0,0,27892,0,0,0,0,0, -27897,27898,0,0,27899,0,0,0,27901,27905,0,0,27920,0,0,27921,0,27922,0,0,0,27931, -27934,0,0,0,0,0,0,0,0,0,0,27941,0,27942,0,27945,0,27947,27954,0,0,0,0,27960, -27963,0,0,0,0,0,0,0,0,27964,27965,0,0,0,27967,0,27969,27975,0,27976,27977,0, -27981,0,27983,28051,28052,0,0,0,0,0,28056,0,0,0,0,0,0,28058,28059,0,0,28061,0,0, -0,0,0,0,0,28063,0,0,0,0,0,0,28066,0,0,0,0,0,0,28069,28070,28072,0,28073,0,0, -28074,0,0,0,0,28075,0,0,0,0,0,0,0,28078,0,0,0,0,28085,0,0,0,0,28086,0,0,0,0,0,0, -28088,0,0,0,0,0,0,0,0,28090,0,28097,28114,28115,0,0,0,0,0,0,0,28116,0,0,0,0,0, -28118,0,28129,0,28131,0,0,28135,0,0,0,28140,28141,0,0,0,28146,0,0,0,0,28152,0,0, -0,0,28155,28157,28161,0,0,0,0,28166,0,28167,0,0,0,0,0,0,0,0,0,0,0,28172,0,0,0,0, -0,0,28173,0,0,28175,0,0,0,0,0,0,0,0,0,28178,28188,0,28190,0,0,0,0,0,28191,0, -28193,28206,0,0,28207,28209,0,28211,0,28213,0,0,0,28215,28216,28217,0,28222,0, -28223,28225,0,0,0,28226,0,28227,28229,28232,0,0,0,0,0,0,0,0,0,28235,0,28241,0,0, -28242,0,0,0,0,28243,0,0,0,28245,0,0,0,28248,28250,0,28251,28252,0,0,0,0,0,0, -28253,0,0,28254,28255,0,0,28256,0,0,28258,0,0,0,0,0,28259,0,0,28260,0,0,28261,0, -0,0,0,28262,28263,0,0,28264,0,0,0,28266,0,28268,28269,0,28270,28272,28274,0, -28277,28278,0,0,0,28279,0,28280,28281,28283,0,28292,0,28294,0,28297,0,0,0,0, -28299,0,0,0,0,0,28300,0,0,0,0,0,0,0,28301,0,0,0,0,0,0,0,0,0,0,0,0,0,28302,28303, -0,0,0,0,28304,0,0,28305,0,28312,0,28313,28314,0,0,0,0,0,0,28315,0,0,0,28320, -28321,0,0,28328,0,0,0,28329,28338,0,28339,0,0,28344,0,0,0,0,0,0,0,0,28347,0,0,0, -0,0,0,0,0,28348,0,0,0,0,0,28411,0,28412,28413,0,28416,0,0,0,28420,0,0,0,0,0, -28421,0,0,0,0,28423,0,0,0,28424,0,0,28428,0,0,0,0,0,28429,0,0,0,28431,28434,0, -28458,0,0,0,0,0,0,0,0,0,0,0,28464,0,0,0,0,28465,0,28467,0,0,0,0,0,0,28471,0,0,0, -0,28474,0,28480,0,28481,0,0,28485,0,0,0,0,28486,28488,0,0,28489,0,0,0,0,28492,0, -0,0,28495,0,28497,0,28499,0,0,0,0,28500,0,0,28502,28503,0,0,0,28508,0,0,0,28510, -0,0,28512,28513,28514,28521,0,28526,0,28527,28528,0,0,0,0,28529,0,0,28532,0,0, -28537,28538,0,0,0,28539,0,28548,0,28553,28554,0,0,0,0,0,0,0,0,0,0,0,0,28560, -28563,0,0,28564,0,0,0,0,28565,0,0,0,0,0,0,0,28566,28568,0,0,0,0,0,0,28569,0,0,0, -28570,0,28572,28573,0,0,0,0,28575,0,0,0,0,28576,28581,28588,0,0,28589,0,0,0, -28590,28595,0,28598,0,0,28601,0,0,28605,0,0,0,0,28614,28615,28619,0,0,0,0,0,0, -28620,0,28626,0,0,28628,0,28631,0,28632,0,0,0,0,0,0,28635,0,0,0,28637,28638,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28639,0,28643,0,0,28652,0,0,0,28662,0, -28670,28671,0,0,0,0,0,0,0,0,0,28672,28673,28675,28676,0,0,0,0,0,0,0,28691,0,0,0, -28695,0,0,0,28696,0,28697,28698,0,28705,0,28707,28708,28710,0,0,0,0,0,0,0,28711, -28728,0,0,0,28736,0,0,0,28737,0,0,0,0,0,0,0,0,0,28738,0,28739,0,28741,0,0,28742, -0,0,0,0,0,0,0,0,0,0,0,28745,0,0,0,0,0,0,28749,28750,28752,28754,28756,0,28757,0, -0,0,0,28759,28760,0,0,0,0,0,0,28762,0,0,0,28764,0,0,0,0,0,0,28766,0,28767,28768, -0,0,0,0,28769,28770,0,0,0,0,0,0,0,0,0,0,0,0,0,28771,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,28772,0,28773,0,28782,0,0,0,0,0,0,28784,0,28785,0,28786,0,0,0,28787,0,0,0, -28797,0,0,0,0,0,0,28799,0,0,28801,0,0,0,0,28802,0,28805,0,0,28806,0,0,28807,0,0, -0,0,0,0,0,28808,0,0,0,0,0,28810,28812,0,0,28816,28819,0,0,28821,0,28826,0,0,0, -28842,28852,0,0,28853,0,28854,28855,0,0,0,28857,0,0,0,28858,0,28867,28868,28869, -0,0,0,28874,28880,28882,28890,28892,0,0,0,0,0,0,0,28895,0,0,0,28898,28899,0,0,0, -28900,0,0,28904,0,28906,0,0,0,0,28907,0,0,0,0,0,0,28908,0,0,0,28910,0,28914,0,0, -0,0,0,0,0,28915,28916,28919,0,0,28920,0,28921,0,0,0,0,0,0,0,0,28924,0,0,0,0, -28926,28929,0,0,0,28930,0,28936,0,28939,0,0,0,0,28942,0,0,0,0,0,0,28956,0,0,0, -28966,0,0,0,0,28967,0,0,0,0,0,0,0,0,0,28968,0,28971,0,28975,28976,0,28982,28983, -0,0,28984,28989,28996,28997,28998,0,0,0,0,0,0,28999,0,0,0,0,0,29000,0,29001,0,0, -0,29009,0,0,29011,0,0,29021,0,0,0,0,29024,0,29025,0,0,0,0,0,29026,0,0,0,29036,0, -0,0,29037,0,0,0,0,29038,0,29045,0,29047,0,0,0,0,0,0,0,0,0,29051,0,0,0,29054, -29056,29062,0,29070,29082,0,0,0,29083,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29084,0,0, -0,0,29085,29088,0,0,0,0,0,0,0,29090,29097,0,0,0,29103,0,0,0,0,0,0,0,0,29105,0,0, -0,0,0,29107,0,29109,0,0,0,29115,0,0,29120,0,0,29138,29140,0,0,0,0,0,0,0,0,0, -29152,0,29160,29174,0,29176,0,0,29180,0,29181,0,0,0,0,0,0,0,0,29228,0,0,29229,0, -0,29230,0,0,0,0,0,0,0,0,0,0,29234,0,0,0,29241,0,29245,0,29248,0,29250,29256, -29280,0,29282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29285,0,0,29286,29291,29292,0,0,0,0, -29294,0,29295,0,0,0,0,0,29296,29297,29298,29300,0,29302,0,0,29304,29307,0,29312, -0,0,0,29322,0,0,29323,0,0,29324,29326,29328,0,29335,0,0,0,0,0,0,0,29338,29339,0, -0,0,0,0,29341,29343,0,0,0,0,29344,0,0,0,0,0,29345,0,0,0,0,29346,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,29347,29348,29349,0,0,29354,0,0,29355,0,0,0,0,0,0,0,0,29357,0,0, -0,0,29364,0,29365,0,0,0,0,0,0,0,29366,0,0,29368,0,0,0,0,0,0,0,0,29378,0,29381,0, -0,0,0,0,0,0,0,29386,0,0,0,0,0,0,29389,0,0,0,29390,0,0,29391,29397,0,29398,29412, -29414,29418,29419,0,0,0,0,0,0,0,29420,0,0,0,0,0,0,0,29423,0,0,0,29435,0,0,0, -29437,0,0,29439,0,29441,0,0,0,0,29443,0,29446,29450,29452,0,0,0,0,0,29456,0,0,0, -0,0,29461,0,0,0,29464,0,0,0,0,0,0,0,0,29468,0,29473,0,0,0,29486,0,0,0,29490,0,0, -0,29491,29492,0,0,29497,0,0,0,29498,0,29499,0,29502,29505,0,29509,0,0,0,29510,0, -0,0,29512,0,0,0,29516,0,0,0,0,0,0,0,0,29518,0,29519,0,0,0,0,0,29520,29521,29529, -0,0,0,0,0,0,0,0,29530,0,0,29531,29538,0,29540,0,0,0,29542,0,29543,29544,29547,0, -0,29548,0,0,0,29549,0,0,0,29550,0,0,29552,0,0,0,0,29558,29561,0,29562,29564,0,0, -29565,0,0,29566,0,0,0,0,0,0,0,0,0,0,29578,29584,29586,29591,0,0,0,0,29593,29594, -0,0,29597,0,0,29613,0,29614,0,29615,0,0,0,0,29616,29617,0,0,29625,0,0,0,29632,0, -0,0,0,0,0,0,29633,0,0,0,0,0,29634,29635,29637,0,29638,0,29641,29643,0,0,0,0,0,0, -29644,0,29645,0,29649,0,0,0,29650,0,29653,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29656, -29659,0,0,29660,0,0,0,29661,0,0,0,0,0,29664,0,0,0,29671,29673,0,0,0,0,0,0,0, -29675,0,29677,29679,0,0,29684,0,0,0,0,0,29685,0,0,0,29687,0,0,0,29688,0,29689, -29690,29700,0,29701,0,0,0,29702,0,29706,0,0,0,0,0,0,0,29720,0,29721,0,29727,0, -29733,29734,0,29750,29761,0,29763,0,0,0,0,0,29764,0,0,29765,0,0,0,29771,0,0,0,0, -0,0,0,0,0,0,0,0,29772,0,0,0,29773,29774,29775,0,0,0,0,0,0,0,0,0,0,0,29822,0,0,0, -29824,0,29825,0,0,0,0,0,29827,0,0,0,0,0,0,0,0,29829,0,29832,29834,0,0,29835,0,0, -29837,29838,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29843,0,0,0,0,29844,29845,0,0,0, -0,0,0,0,0,0,29849,0,0,29869,29872,29890,29905,0,0,0,0,0,29907,29921,0,29922,0,0, -29923,29926,29944,29946,0,0,0,0,0,0,0,29947,29948,0,0,0,29951,0,0,0,0,0,29953,0, -0,29956,0,29957,0,0,29962,0,0,0,0,29971,0,0,0,29972,0,0,0,0,0,29978,0,29979, -29992,30007,30008,30010,0,0,0,30013,0,0,0,0,30014,30016,0,0,0,0,0,0,0,0,0,0,0, -30017,0,0,0,0,0,30023,30031,0,0,30033,0,0,0,0,0,0,0,0,0,0,30034,0,30038,0,30039, -0,30040,0,0,0,0,0,0,30067,30068,0,0,0,30069,0,30072,0,0,0,30073,0,0,0,0,30075,0, -0,0,0,0,0,30079,0,0,30080,0,0,0,0,0,30082,0,0,0,0,0,0,0,0,0,0,0,30084,30090,0,0, -30091,0,0,0,0,30098,30118,0,30119,0,30121,30130,0,0,0,0,0,0,0,0,0,0,0,0,0,30131, -30132,30133,0,0,0,0,0,0,30135,0,0,0,0,0,0,0,0,0,0,0,30136,0,0,30137,30138,0,0,0, -30139,30146,0,0,0,0,0,30147,0,0,30148,30151,0,0,0,30168,0,30172,30173,0,0,0,0,0, -0,0,0,30180,30181,0,30192,0,0,0,0,0,0,0,30194,30196,0,0,30199,0,0,30202,0,0,0,0, -30203,0,0,0,0,0,0,0,0,0,0,30213,0,0,0,30216,0,0,30217,0,0,0,30218,0,0,0,0,30219, -0,30220,0,30222,30227,0,0,0,0,0,30231,0,0,30233,30235,0,0,0,0,30238,0,30240, -30243,30245,0,30250,30252,0,0,0,30269,0,0,30271,30272,0,0,0,30278,30280,0,0, -30282,0,30284,0,30294,0,0,0,0,30295,30296,0,0,0,0,0,30298,30299,30302,30304, -30306,0,0,0,0,0,0,30316,30317,0,0,0,30318,0,0,0,30319,0,30320,30322,30326,0,0,0, -0,0,30327,0,30332,30348,30349,0,0,30356,0,0,0,0,0,0,0,0,30357,0,30358,0,30359, -30360,0,0,30365,30366,30378,0,0,0,0,30379,0,0,30381,0,30385,0,30388,30397,0,0,0, -30401,0,0,0,0,30403,0,0,0,0,0,30404,0,0,30405,0,30406,30408,0,30409,0,30410,0,0, -0,30417,0,0,30418,30419,0,30420,0,30424,0,0,0,30427,30430,30432,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,30433,0,0,0,0,0,0,0,30436,0,30437,30438,0,30441,30442,0,0, -0,30445,0,0,0,0,30452,30456,30457,0,0,0,30458,0,30464,0,0,0,0,0,0,30467,0,30469, -0,0,0,0,0,30477,0,0,30484,0,0,0,0,0,30485,0,0,0,0,0,30486,30487,30497,30498,0,0, -0,0,0,0,0,0,0,0,30505,0,30508,0,0,0,30509,30510,0,30514,30516,0,0,0,0,0,0,0,0,0, -0,0,30523,0,30524,0,30525,0,0,0,0,30537,0,0,30538,0,0,0,0,0,30553,0,0,30555, -30556,30558,30559,30560,0,0,30561,0,30562,0,0,0,0,0,0,0,0,30563,30570,30571,0, -30586,30587,0,0,30590,0,0,30594,0,0,0,0,30611,30612,30623,30634,0,0,30636,30640, -30655,30656,0,30657,0,0,30658,30669,0,30670,0,30676,30678,0,0,0,0,0,0,0,30679,0, -0,0,0,0,0,0,0,0,0,0,30695,0,0,30698,0,0,0,0,30700,0,0,0,0,30701,0,30702,30703,0, -0,0,0,30707,0,0,0,30709,0,0,30710,30719,30729,0,0,0,0,0,0,0,0,0,30731,0,0,30733, -0,0,0,30734,0,0,0,0,0,30736,30737,0,0,0,30740,0,0,0,30743,0,30746,0,30747,30748, -0,0,30751,30752,30753,0,0,0,30754,0,0,30760,0,0,0,0,0,0,0,30763,0,30764,0,0, -30766,0,30769,30770,30771,30774,30777,0,0,30779,30780,30781,0,0,0,0,30790,0,0,0, -30792,0,0,0,0,30810,0,0,0,0,0,0,0,30812,30819,0,0,30823,30824,0,30825,0,30827,0, -0,0,0,0,0,30828,0,0,30830,0,0,0,30834,0,30835,0,30837,30838,0,30845,0,0,0,0,0, -30846,30847,0,0,30849,0,30851,0,0,0,0,0,30852,30858,0,0,30859,0,30865,0,0,30866, -0,0,30868,0,0,30869,0,0,0,30881,30883,0,0,0,0,0,30889,0,30891,0,0,0,0,30894,0, -30895,0,30897,0,30898,0,0,0,30904,30906,0,30909,0,0,0,0,0,0,30910,0,0,0,30915, -30933,30942,0,0,0,0,30943,0,0,30945,0,0,0,0,0,0,30946,0,0,30947,0,0,30955,30956, -0,0,30960,0,0,30961,30962,30966,0,0,30969,30974,0,0,0,30976,0,0,30977,0,30978, -30982,0,0,0,0,0,0,0,30994,30995,30998,0,31000,0,0,31001,0,0,31003,31005,0,0, -31006,31011,0,0,31014,0,31016,0,0,0,0,31018,0,0,31020,31023,31024,31025,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,31027,31028,31029,0,0,0,0,0,0,31032,0,0,0,0,0,0,0,0,0,0,0, -31036,31037,31038,0,0,0,31041,31043,31045,0,31047,0,0,0,31048,0,31049,0,0,0, -31053,31054,31055,0,0,31063,0,0,0,0,0,31066,0,31068,31071,0,0,0,31072,31073,0,0, -0,0,31075,0,0,31076,0,0,0,31077,31079,0,31080,0,0,0,0,0,0,0,0,0,0,31087,0,31142, -0,31144,0,0,31145,31146,31147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31149,0,31151,31152,0, -0,0,0,0,0,0,31162,31171,31174,31175,0,0,0,31176,0,0,0,0,0,0,0,31179,0,0,0,31186, -0,0,0,31192,31195,0,0,31196,0,0,0,0,0,0,0,0,31198,0,0,0,0,0,31199,0,0,0,31205,0, -0,0,0,31211,31215,0,0,0,0,31231,0,31232,0,0,0,0,0,0,0,0,0,0,31233,31236,31253,0, -31254,0,0,0,0,0,0,31255,0,0,31257,0,0,0,0,0,0,0,0,0,31258,31259,0,0,31260,0, -31261,0,0,0,0,0,31262,31263,0,0,31264,0,31266,0,31267,0,0,0,0,0,31281,0,31282,0, -31284,0,0,31285,31287,31288,0,0,31290,0,0,0,31292,31295,0,31299,0,31300,0,0,0,0, -0,31302,0,0,0,0,31303,0,0,0,0,0,0,31304,0,0,0,0,0,31305,31308,31309,31315,0, -31317,0,0,0,0,0,31323,0,31324,0,0,0,0,0,31325,31327,0,0,31331,0,0,0,0,0,31333,0, -0,0,0,0,31336,0,0,31337,0,0,0,0,0,0,31338,0,0,0,0,0,0,0,0,0,0,0,0,31339,0,0,0,0, -0,0,0,31342,0,0,0,0,31345,0,0,0,0,0,0,0,0,31347,0,0,0,0,0,0,31348,0,0,31350, -31351,0,31352,0,0,31354,0,0,0,0,31355,0,0,31356,0,0,0,0,0,0,0,0,0,0,31363,0, -31372,0,0,31373,0,0,0,0,0,0,0,0,0,31376,0,31388,0,31389,0,31392,0,31401,0,31405, -31407,31408,0,31409,0,0,0,0,0,0,31413,31415,0,0,0,31416,31418,0,0,0,0,0,0,31422, -31423,0,0,31424,0,31425,31432,0,0,0,0,0,0,0,0,0,31433,0,0,0,0,0,0,0,0,31434,0,0, -0,0,0,0,31435,0,0,0,0,31438,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31442,0,31444,0, -31448,0,0,31451,0,0,0,0,31452,0,31461,31465,0,0,31466,0,0,31467,0,0,31468,0,0,0, -31469,31473,0,31476,0,0,0,0,31489,31490,0,0,0,0,0,0,0,31492,31493,31494,0,0,0,0, -31501,31504,31505,0,0,0,0,0,0,0,0,0,31509,0,0,0,0,31510,0,0,31511,0,0,31513,0,0, -0,0,0,0,0,0,0,31514,0,31522,31536,31539,31540,0,31541,0,0,0,0,0,0,31546,31553, -31559,0,0,0,31560,31561,31562,0,0,31564,31567,0,31569,0,0,0,31570,0,0,0,0,31571, -0,0,0,0,0,0,31572,31574,31580,31581,0,0,31582,31584,31585,31586,31595,0,31596,0, -0,0,0,31597,0,31599,0,31600,31601,0,0,31603,31604,0,0,31608,31610,0,0,0,31611,0, -31615,0,0,0,0,31616,0,0,0,0,0,0,31617,0,0,0,0,0,31618,0,0,0,0,0,0,31621,0,0,0,0, -0,0,0,0,0,31622,31625,0,0,0,0,31627,0,31641,0,0,31642,0,0,31643,0,0,0,0,0,0,0,0, -0,31644,0,31646,0,0,0,0,31648,0,0,0,31652,0,0,0,31657,0,0,31676,0,0,0,0,0,0,0, -31689,31691,31692,0,31694,0,0,0,31696,0,31702,0,31703,0}; - -static const DictWord kStaticDictionaryWords[31705] = { -{0,0,0},{8,0,1002},{136,0,1015},{4,0,683},{4,10,325},{138,10,125},{7,11,572},{9, -11,592},{11,11,680},{11,11,842},{11,11,924},{12,11,356},{12,11,550},{13,11,317}, -{13,11,370},{13,11,469},{13,11,471},{14,11,397},{18,11,69},{146,11,145},{134,0, -1265},{136,11,534},{134,0,1431},{11,0,138},{140,0,40},{4,0,155},{7,0,1689},{4,10 -,718},{135,10,1216},{4,0,245},{5,0,151},{5,0,741},{6,0,1147},{7,0,498},{7,0,870} -,{7,0,1542},{12,0,213},{14,0,36},{14,0,391},{17,0,111},{18,0,6},{18,0,46},{18,0, -151},{19,0,36},{20,0,32},{20,0,56},{20,0,69},{20,0,102},{21,0,4},{22,0,8},{22,0, -10},{22,0,14},{150,0,31},{4,0,624},{135,0,1752},{5,10,124},{5,10,144},{6,10,548} -,{7,10,15},{7,10,153},{137,10,629},{6,0,503},{9,0,586},{13,0,468},{14,0,66},{16, -0,58},{7,10,1531},{8,10,416},{9,10,275},{10,10,100},{11,10,658},{11,10,979},{12, -10,86},{14,10,207},{15,10,20},{143,10,25},{5,0,603},{7,0,1212},{9,0,565},{14,0, -301},{5,10,915},{6,10,1783},{7,10,211},{7,10,1353},{9,10,83},{10,10,376},{10,10, -431},{11,10,543},{12,10,664},{13,10,280},{13,10,428},{14,10,128},{17,10,52},{145 -,10,81},{4,0,492},{133,0,451},{135,0,835},{141,0,70},{132,0,539},{7,11,748},{139 -,11,700},{7,11,1517},{11,11,597},{14,11,76},{14,11,335},{148,11,33},{6,0,113},{ -135,0,436},{4,10,338},{133,10,400},{136,0,718},{133,11,127},{133,11,418},{6,0, -1505},{7,0,520},{6,11,198},{11,10,892},{140,11,83},{4,10,221},{5,10,659},{5,10, -989},{7,10,697},{7,10,1211},{138,10,284},{135,0,1070},{5,11,276},{6,11,55},{135, -11,1369},{134,0,1515},{6,11,1752},{136,11,726},{138,10,507},{15,0,78},{4,10,188} -,{135,10,805},{5,10,884},{139,10,991},{133,11,764},{134,10,1653},{6,11,309},{7, -11,331},{138,11,550},{135,11,1861},{132,11,348},{135,11,986},{135,11,1573},{12,0 -,610},{13,0,431},{144,0,59},{9,11,799},{140,10,166},{134,0,1530},{132,0,750},{ -132,0,307},{133,0,964},{6,11,194},{7,11,133},{10,11,493},{10,11,570},{139,11,664 -},{5,11,24},{5,11,569},{6,11,3},{6,11,119},{6,11,143},{6,11,440},{7,11,295},{7, -11,599},{7,11,1686},{7,11,1854},{8,11,424},{9,11,43},{9,11,584},{9,11,760},{10, -11,148},{10,11,328},{11,11,159},{11,11,253},{11,11,506},{12,11,487},{12,11,531}, -{144,11,33},{136,10,760},{5,11,14},{5,11,892},{6,11,283},{7,11,234},{136,11,537} -,{135,11,1251},{4,11,126},{8,11,635},{147,11,34},{4,11,316},{135,11,1561},{6,0, -999},{6,0,1310},{137,11,861},{4,11,64},{5,11,352},{5,11,720},{6,11,368},{139,11, -359},{4,0,75},{5,0,180},{6,0,500},{7,0,58},{7,0,710},{10,0,645},{136,10,770},{ -133,0,649},{6,0,276},{7,0,282},{7,0,879},{7,0,924},{8,0,459},{9,0,599},{9,0,754} -,{11,0,574},{12,0,128},{12,0,494},{13,0,52},{13,0,301},{15,0,30},{143,0,132},{ -132,0,200},{4,10,89},{5,10,489},{6,10,315},{7,10,553},{7,10,1745},{138,10,243},{ -135,11,1050},{7,0,1621},{6,10,1658},{9,10,3},{10,10,154},{11,10,641},{13,10,85}, -{13,10,201},{141,10,346},{6,11,175},{137,11,289},{5,11,432},{133,11,913},{6,0, -225},{137,0,211},{7,0,718},{8,0,687},{139,0,374},{4,10,166},{133,10,505},{9,0, -110},{134,10,1670},{8,0,58},{9,0,724},{11,0,809},{13,0,113},{145,0,72},{6,0,345} -,{7,0,1247},{144,11,82},{5,11,931},{134,11,1698},{8,0,767},{8,0,803},{9,0,301},{ -137,0,903},{139,0,203},{134,0,1154},{7,0,1949},{136,0,674},{134,0,259},{135,0, -1275},{5,11,774},{6,11,1637},{6,11,1686},{134,11,1751},{134,0,1231},{7,10,445},{ -8,10,307},{8,10,704},{10,10,41},{10,10,439},{11,10,237},{11,10,622},{140,10,201} -,{136,0,254},{6,11,260},{135,11,1484},{139,0,277},{135,10,1977},{4,10,189},{5,10 -,713},{6,11,573},{136,10,57},{138,10,371},{132,10,552},{134,11,344},{133,0,248}, -{9,0,800},{10,0,693},{11,0,482},{11,0,734},{11,0,789},{134,11,240},{4,0,116},{5, -0,95},{5,0,445},{7,0,1688},{8,0,29},{9,0,272},{11,0,509},{11,0,915},{4,11,292},{ -4,11,736},{5,11,871},{6,11,171},{6,11,1689},{7,11,1324},{7,11,1944},{9,11,415},{ -9,11,580},{14,11,230},{146,11,68},{7,0,490},{13,0,100},{143,0,75},{135,0,1641},{ -133,0,543},{7,11,209},{8,11,661},{10,11,42},{11,11,58},{12,11,58},{12,11,118},{ -141,11,32},{5,0,181},{8,0,41},{6,11,63},{135,11,920},{133,0,657},{133,11,793},{ -138,0,709},{7,0,25},{8,0,202},{138,0,536},{5,11,665},{135,10,1788},{145,10,49},{ -9,0,423},{140,0,89},{5,11,67},{6,11,62},{6,11,374},{135,11,1391},{8,0,113},{9,0, -877},{10,0,554},{11,0,83},{12,0,136},{19,0,109},{9,11,790},{140,11,47},{138,10, -661},{4,0,963},{10,0,927},{14,0,442},{135,10,1945},{133,0,976},{132,0,206},{4,11 -,391},{135,11,1169},{134,0,2002},{6,0,696},{134,0,1008},{134,0,1170},{132,11,271 -},{7,0,13},{8,0,226},{10,0,537},{11,0,570},{11,0,605},{11,0,799},{11,0,804},{12, -0,85},{12,0,516},{12,0,623},{13,0,112},{13,0,361},{14,0,77},{14,0,78},{17,0,28}, -{19,0,110},{140,11,314},{132,0,769},{134,0,1544},{4,0,551},{137,0,678},{5,10,84} -,{134,10,163},{9,0,57},{9,0,459},{10,0,425},{11,0,119},{12,0,184},{12,0,371},{13 -,0,358},{145,0,51},{5,0,188},{5,0,814},{8,0,10},{9,0,421},{9,0,729},{10,0,609},{ -11,0,689},{4,11,253},{5,10,410},{5,11,544},{7,11,300},{137,11,340},{134,0,624},{ -138,11,321},{135,0,1941},{18,0,130},{5,10,322},{8,10,186},{9,10,262},{10,10,187} -,{142,10,208},{5,11,53},{5,11,541},{6,11,94},{6,11,499},{7,11,230},{139,11,321}, -{133,10,227},{4,0,378},{4,11,920},{5,11,25},{5,11,790},{6,11,457},{135,11,853},{ -137,0,269},{132,0,528},{134,0,1146},{7,10,1395},{8,10,486},{9,10,236},{9,10,878} -,{10,10,218},{11,10,95},{19,10,17},{147,10,31},{7,10,2043},{8,10,672},{141,10, -448},{134,0,1105},{134,0,1616},{134,11,1765},{140,11,163},{5,10,412},{133,11,822 -},{132,11,634},{6,0,656},{134,11,1730},{134,0,1940},{5,0,104},{6,0,173},{135,0, -1631},{136,10,562},{6,11,36},{7,11,658},{8,11,454},{147,11,86},{5,0,457},{134,10 -,1771},{7,0,810},{8,0,138},{8,0,342},{9,0,84},{10,0,193},{11,0,883},{140,0,359}, -{9,0,620},{135,10,1190},{137,10,132},{7,11,975},{137,11,789},{6,0,95},{6,0,1934} -,{136,0,967},{141,11,335},{6,0,406},{10,0,409},{10,0,447},{11,0,44},{140,0,100}, -{4,10,317},{135,10,1279},{132,0,477},{134,0,1268},{6,0,1941},{8,0,944},{5,10,63} -,{133,10,509},{132,0,629},{132,11,104},{4,0,246},{133,0,375},{6,0,1636},{132,10, -288},{135,11,1614},{9,0,49},{10,0,774},{8,10,89},{8,10,620},{11,10,628},{12,10, -322},{143,10,124},{4,0,282},{7,0,1034},{11,0,398},{11,0,634},{12,0,1},{12,0,79}, -{12,0,544},{14,0,237},{17,0,10},{146,0,20},{132,0,824},{7,11,45},{9,11,542},{9, -11,566},{138,11,728},{5,0,118},{5,0,499},{6,0,476},{6,0,665},{6,0,1176},{6,0, -1196},{7,0,600},{7,0,888},{135,0,1096},{7,0,296},{7,0,596},{8,0,560},{8,0,586},{ -9,0,612},{11,0,304},{12,0,46},{13,0,89},{14,0,112},{145,0,122},{5,0,894},{6,0, -1772},{9,0,1009},{138,10,120},{5,11,533},{7,11,755},{138,11,780},{151,10,1},{6,0 -,1474},{7,11,87},{142,11,288},{139,0,366},{137,10,461},{7,11,988},{7,11,1939},{9 -,11,64},{9,11,502},{12,11,7},{12,11,34},{13,11,12},{13,11,234},{147,11,77},{7,0, -1599},{7,0,1723},{8,0,79},{8,0,106},{8,0,190},{8,0,302},{8,0,383},{8,0,713},{9,0 -,119},{9,0,233},{9,0,419},{9,0,471},{10,0,181},{10,0,406},{11,0,57},{11,0,85},{ -11,0,120},{11,0,177},{11,0,296},{11,0,382},{11,0,454},{11,0,758},{11,0,999},{12, -0,27},{12,0,98},{12,0,131},{12,0,245},{12,0,312},{12,0,446},{12,0,454},{13,0,25} -,{13,0,98},{13,0,426},{13,0,508},{14,0,70},{14,0,163},{14,0,272},{14,0,277},{14, -0,370},{15,0,95},{15,0,138},{15,0,167},{17,0,38},{148,0,96},{135,10,1346},{10,0, -200},{19,0,2},{151,0,22},{135,11,141},{134,10,85},{134,0,1759},{138,0,372},{145, -0,16},{8,0,943},{132,11,619},{139,11,88},{5,11,246},{8,11,189},{9,11,355},{9,11, -512},{10,11,124},{10,11,453},{11,11,143},{11,11,416},{11,11,859},{141,11,341},{5 -,0,258},{134,0,719},{6,0,1798},{6,0,1839},{8,0,900},{10,0,874},{10,0,886},{12,0, -698},{12,0,732},{12,0,770},{16,0,106},{18,0,163},{18,0,170},{18,0,171},{152,0,20 -},{9,0,707},{11,0,326},{11,0,339},{12,0,423},{12,0,502},{20,0,62},{9,11,707},{11 -,11,326},{11,11,339},{12,11,423},{12,11,502},{148,11,62},{5,0,30},{7,0,495},{8,0 -,134},{9,0,788},{140,0,438},{133,11,678},{5,10,279},{6,10,235},{7,10,468},{8,10, -446},{9,10,637},{10,10,717},{11,10,738},{140,10,514},{5,11,35},{6,11,287},{7,11, -862},{7,11,1886},{138,11,179},{7,0,1948},{7,0,2004},{132,11,517},{5,10,17},{6,10 -,371},{137,10,528},{4,0,115},{5,0,669},{6,0,407},{8,0,311},{11,0,10},{141,0,5},{ -137,0,381},{5,0,50},{6,0,439},{7,0,780},{135,0,1040},{136,11,667},{11,11,403},{ -146,11,83},{5,0,1},{6,0,81},{138,0,520},{134,0,738},{5,0,482},{8,0,98},{9,0,172} -,{10,0,360},{10,0,700},{10,0,822},{11,0,302},{11,0,778},{12,0,50},{12,0,127},{12 -,0,396},{13,0,62},{13,0,328},{14,0,122},{147,0,72},{9,11,157},{10,11,131},{140, -11,72},{135,11,714},{135,11,539},{5,0,2},{6,0,512},{7,0,797},{7,0,1494},{8,0,253 -},{8,0,589},{9,0,77},{10,0,1},{10,0,129},{10,0,225},{11,0,118},{11,0,226},{11,0, -251},{11,0,430},{11,0,701},{11,0,974},{11,0,982},{12,0,64},{12,0,260},{12,0,488} -,{140,0,690},{5,11,394},{7,11,367},{7,11,487},{7,11,857},{7,11,1713},{8,11,246}, -{9,11,537},{10,11,165},{12,11,219},{140,11,561},{136,0,557},{5,10,779},{5,10,807 -},{6,10,1655},{134,10,1676},{4,10,196},{5,10,558},{133,10,949},{11,11,827},{12, -11,56},{14,11,34},{143,11,148},{137,0,347},{133,0,572},{134,0,832},{4,0,12},{7,0 -,504},{7,0,522},{7,0,809},{8,0,797},{141,0,88},{4,10,752},{133,11,449},{7,11,86} -,{8,11,103},{145,11,69},{7,11,2028},{138,11,641},{5,0,528},{6,11,1},{142,11,2},{ -134,0,861},{10,0,294},{4,10,227},{5,10,159},{5,10,409},{7,10,80},{10,10,479},{12 -,10,418},{14,10,50},{14,10,249},{142,10,295},{7,10,1470},{8,10,66},{8,10,137},{8 -,10,761},{9,10,638},{11,10,80},{11,10,212},{11,10,368},{11,10,418},{12,10,8},{13 -,10,15},{16,10,61},{17,10,59},{19,10,28},{148,10,84},{20,0,109},{135,11,1148},{6 -,11,277},{7,11,1274},{7,11,1386},{7,11,1392},{12,11,129},{146,11,87},{6,11,187}, -{7,11,39},{7,11,1203},{8,11,380},{8,11,542},{14,11,117},{149,11,28},{134,0,1187} -,{5,0,266},{9,0,290},{9,0,364},{10,0,293},{11,0,606},{142,0,45},{6,11,297},{7,11 -,793},{139,11,938},{4,0,50},{6,0,594},{9,0,121},{10,0,49},{10,0,412},{139,0,834} -,{136,0,748},{7,11,464},{8,11,438},{11,11,105},{11,11,363},{12,11,231},{14,11, -386},{15,11,102},{148,11,75},{132,0,466},{13,0,399},{14,0,337},{6,10,38},{7,10, -1220},{8,10,185},{8,10,256},{9,10,22},{9,10,331},{10,10,738},{11,10,205},{11,10, -540},{11,10,746},{13,10,465},{142,10,194},{9,0,378},{141,0,162},{137,0,519},{4, -10,159},{6,10,115},{7,10,252},{7,10,257},{7,10,1928},{8,10,69},{9,10,384},{10,10 -,91},{10,10,615},{12,10,375},{14,10,235},{18,10,117},{147,10,123},{5,11,604},{5, -10,911},{136,10,278},{132,0,667},{8,0,351},{9,0,322},{4,10,151},{135,10,1567},{ -134,0,902},{133,10,990},{12,0,180},{5,10,194},{7,10,1662},{137,10,90},{4,0,869}, -{134,0,1996},{134,0,813},{133,10,425},{137,11,761},{132,0,260},{133,10,971},{5, -11,20},{6,11,298},{7,11,659},{7,11,1366},{137,11,219},{4,0,39},{5,0,36},{7,0, -1843},{8,0,407},{11,0,144},{140,0,523},{4,0,510},{10,0,587},{139,10,752},{7,0,29 -},{7,0,66},{7,0,1980},{10,0,487},{138,0,809},{13,0,260},{14,0,82},{18,0,63},{137 -,10,662},{5,10,72},{6,10,264},{7,10,21},{7,10,46},{7,10,2013},{8,10,215},{8,10, -513},{10,10,266},{139,10,22},{134,0,570},{6,0,565},{7,0,1667},{4,11,439},{10,10, -95},{11,10,603},{12,11,242},{13,10,443},{14,10,160},{143,10,4},{134,0,1464},{134 -,10,431},{9,0,372},{15,0,2},{19,0,10},{19,0,18},{5,10,874},{6,10,1677},{143,10,0 -},{132,0,787},{6,0,380},{12,0,399},{21,0,19},{7,10,939},{7,10,1172},{7,10,1671}, -{9,10,540},{10,10,696},{11,10,265},{11,10,732},{11,10,928},{11,10,937},{141,10, -438},{137,0,200},{132,11,233},{132,0,516},{134,11,577},{132,0,844},{11,0,887},{ -14,0,365},{142,0,375},{132,11,482},{8,0,821},{140,0,44},{7,0,1655},{136,0,305},{ -5,10,682},{135,10,1887},{135,11,346},{132,10,696},{4,0,10},{7,0,917},{139,0,786} -,{5,11,795},{6,11,1741},{8,11,417},{137,11,782},{4,0,1016},{134,0,2031},{5,0,684 -},{4,10,726},{133,10,630},{6,0,1021},{134,0,1480},{8,10,802},{136,10,838},{134,0 -,27},{134,0,395},{135,11,622},{7,11,625},{135,11,1750},{4,11,203},{135,11,1936}, -{6,10,118},{7,10,215},{7,10,1521},{140,10,11},{132,0,813},{136,0,511},{7,10,615} -,{138,10,251},{135,10,1044},{145,0,56},{133,10,225},{6,0,342},{6,0,496},{8,0,275 -},{137,0,206},{4,0,909},{133,0,940},{132,0,891},{7,11,311},{9,11,308},{140,11, -255},{4,10,370},{5,10,756},{135,10,1326},{4,0,687},{134,0,1596},{134,0,1342},{6, -10,1662},{7,10,48},{8,10,771},{10,10,116},{13,10,104},{14,10,105},{14,10,184},{ -15,10,168},{19,10,92},{148,10,68},{138,10,209},{4,11,400},{5,11,267},{135,11,232 -},{151,11,12},{6,0,41},{141,0,160},{141,11,314},{134,0,1718},{136,0,778},{142,11 -,261},{134,0,1610},{133,0,115},{132,0,294},{14,0,314},{132,10,120},{132,0,983},{ -5,0,193},{140,0,178},{138,10,429},{5,10,820},{135,10,931},{6,0,994},{6,0,1051},{ -6,0,1439},{7,0,174},{133,11,732},{4,11,100},{7,11,679},{8,11,313},{138,10,199},{ -6,10,151},{6,10,1675},{7,10,383},{151,10,10},{6,0,1796},{8,0,848},{8,0,867},{8,0 -,907},{10,0,855},{140,0,703},{140,0,221},{4,0,122},{5,0,796},{5,0,952},{6,0,1660 -},{6,0,1671},{8,0,567},{9,0,687},{9,0,742},{10,0,686},{11,0,682},{11,0,909},{140 -,0,281},{5,11,362},{5,11,443},{6,11,318},{7,11,1019},{139,11,623},{5,11,463},{ -136,11,296},{11,0,583},{13,0,262},{6,10,1624},{12,10,422},{142,10,360},{5,0,179} -,{7,0,1095},{135,0,1213},{4,10,43},{4,11,454},{5,10,344},{133,10,357},{4,0,66},{ -7,0,722},{135,0,904},{134,0,773},{7,0,352},{133,10,888},{5,11,48},{5,11,404},{6, -11,557},{7,11,458},{8,11,597},{10,11,455},{10,11,606},{11,11,49},{11,11,548},{12 -,11,476},{13,11,18},{141,11,450},{134,11,418},{132,10,711},{5,11,442},{135,11, -1984},{141,0,35},{137,0,152},{134,0,1197},{135,11,1093},{137,11,203},{137,10,440 -},{10,0,592},{10,0,753},{12,0,317},{12,0,355},{12,0,465},{12,0,469},{12,0,560},{ -12,0,578},{141,0,243},{133,0,564},{134,0,797},{5,10,958},{133,10,987},{5,11,55}, -{7,11,376},{140,11,161},{133,11,450},{134,0,556},{134,0,819},{11,10,276},{142,10 -,293},{7,0,544},{138,0,61},{8,0,719},{4,10,65},{5,10,479},{5,10,1004},{7,10,1913 -},{8,10,317},{9,10,302},{10,10,612},{141,10,22},{4,0,5},{5,0,498},{8,0,637},{9,0 -,521},{4,11,213},{4,10,261},{7,11,223},{7,10,510},{136,11,80},{5,0,927},{7,0,101 -},{4,10,291},{7,11,381},{7,11,806},{7,11,820},{8,11,354},{8,11,437},{8,11,787},{ -9,10,515},{9,11,657},{10,11,58},{10,11,339},{10,11,749},{11,11,914},{12,10,152}, -{12,11,162},{12,10,443},{13,11,75},{13,10,392},{14,11,106},{14,11,198},{14,11, -320},{14,10,357},{14,11,413},{146,11,43},{6,0,1153},{7,0,1441},{136,11,747},{4,0 -,893},{5,0,780},{133,0,893},{138,11,654},{133,11,692},{133,0,238},{134,11,191},{ -4,10,130},{135,10,843},{6,0,1296},{5,10,42},{5,10,879},{7,10,245},{7,10,324},{7, -10,1532},{11,10,463},{11,10,472},{13,10,363},{144,10,52},{134,0,1729},{6,0,1999} -,{136,0,969},{4,10,134},{133,10,372},{4,0,60},{7,0,941},{7,0,1800},{8,0,314},{9, -0,700},{139,0,487},{134,0,1144},{6,11,162},{7,11,1960},{136,11,831},{132,11,706} -,{135,0,1147},{138,11,426},{138,11,89},{7,0,1853},{138,0,437},{136,0,419},{135, -10,1634},{133,0,828},{5,0,806},{7,0,176},{7,0,178},{7,0,1240},{7,0,1976},{132,10 -,644},{135,11,1877},{5,11,420},{135,11,1449},{4,0,51},{5,0,39},{6,0,4},{7,0,591} -,{7,0,849},{7,0,951},{7,0,1613},{7,0,1760},{7,0,1988},{9,0,434},{10,0,754},{11,0 -,25},{139,0,37},{10,11,57},{138,11,277},{135,10,540},{132,11,204},{135,0,159},{ -139,11,231},{133,0,902},{7,0,928},{7,11,366},{9,11,287},{12,11,199},{12,11,556}, -{140,11,577},{6,10,623},{136,10,789},{4,10,908},{5,10,359},{5,10,508},{6,10,1723 -},{7,10,343},{7,10,1996},{135,10,2026},{134,0,270},{4,10,341},{135,10,480},{5,11 -,356},{135,11,224},{11,11,588},{11,11,864},{11,11,968},{143,11,160},{132,0,556}, -{137,0,801},{132,0,416},{142,0,372},{5,0,152},{5,0,197},{7,0,340},{7,0,867},{10, -0,548},{10,0,581},{11,0,6},{12,0,3},{12,0,19},{14,0,110},{142,0,289},{139,0,369} -,{7,11,630},{9,11,567},{11,11,150},{11,11,444},{141,11,119},{134,11,539},{7,10, -1995},{8,10,299},{11,10,890},{140,10,674},{7,0,34},{7,0,190},{8,0,28},{8,0,141}, -{8,0,444},{8,0,811},{9,0,468},{11,0,334},{12,0,24},{12,0,386},{140,0,576},{133,0 -,757},{7,0,1553},{136,0,898},{133,0,721},{136,0,1012},{4,0,789},{5,0,647},{135,0 -,1102},{132,0,898},{10,0,183},{4,10,238},{5,10,503},{6,10,179},{7,10,2003},{8,10 -,381},{8,10,473},{9,10,149},{10,10,788},{15,10,45},{15,10,86},{20,10,110},{150, -10,57},{9,0,136},{19,0,107},{4,10,121},{5,10,156},{5,10,349},{10,10,605},{142,10 -,342},{4,11,235},{135,11,255},{4,11,194},{5,11,584},{6,11,384},{7,11,583},{10,11 -,761},{11,11,760},{139,11,851},{6,10,80},{6,10,1694},{7,10,173},{7,10,1974},{9, -10,547},{10,10,730},{14,10,18},{150,10,39},{4,10,923},{134,10,1711},{5,0,277},{ -141,0,247},{132,0,435},{133,11,562},{134,0,1311},{5,11,191},{137,11,271},{132,10 -,595},{7,11,1537},{14,11,96},{143,11,73},{5,0,437},{7,0,502},{7,0,519},{7,0,1122 -},{7,0,1751},{14,0,211},{6,10,459},{7,10,1753},{7,10,1805},{8,10,658},{9,10,1},{ -11,10,959},{141,10,446},{6,0,814},{4,11,470},{5,11,473},{6,11,153},{7,11,1503},{ -7,11,1923},{10,11,701},{11,11,132},{11,11,168},{11,11,227},{11,11,320},{11,11, -436},{11,11,525},{11,11,855},{12,11,41},{12,11,286},{13,11,103},{13,11,284},{14, -11,255},{14,11,262},{15,11,117},{143,11,127},{5,0,265},{6,0,212},{135,0,28},{138 -,0,750},{133,11,327},{6,11,552},{7,11,1754},{137,11,604},{134,0,2012},{132,0,702 -},{5,11,80},{6,11,405},{7,11,403},{7,11,1502},{7,11,1626},{8,11,456},{9,11,487}, -{9,11,853},{9,11,889},{10,11,309},{11,11,721},{11,11,994},{12,11,430},{141,11, -165},{5,0,808},{135,0,2045},{5,0,166},{8,0,739},{140,0,511},{134,10,490},{4,11, -453},{5,11,887},{6,11,535},{8,11,6},{136,11,543},{4,0,119},{5,0,170},{5,0,447},{ -7,0,1708},{7,0,1889},{9,0,357},{9,0,719},{12,0,486},{140,0,596},{137,0,500},{7, -10,250},{136,10,507},{132,10,158},{6,0,809},{134,0,1500},{9,0,327},{11,0,350},{ -11,0,831},{13,0,352},{4,10,140},{7,10,362},{8,10,209},{9,10,10},{9,10,503},{9,10 -,614},{10,10,689},{11,10,327},{11,10,725},{12,10,252},{12,10,583},{13,10,192},{ -14,10,269},{14,10,356},{148,10,50},{135,11,741},{4,0,450},{7,0,1158},{19,10,1},{ -19,10,26},{150,10,9},{6,0,597},{135,0,1318},{134,0,1602},{6,10,228},{7,10,1341}, -{9,10,408},{138,10,343},{7,0,1375},{7,0,1466},{138,0,331},{132,0,754},{132,10, -557},{5,11,101},{6,11,88},{6,11,543},{7,11,1677},{9,11,100},{10,11,677},{14,11, -169},{14,11,302},{14,11,313},{15,11,48},{143,11,84},{134,0,1368},{4,11,310},{9, -11,795},{10,11,733},{11,11,451},{12,11,249},{14,11,115},{14,11,286},{143,11,100} -,{132,10,548},{10,0,557},{7,10,197},{8,10,142},{8,10,325},{9,10,150},{9,10,596}, -{10,10,353},{11,10,74},{11,10,315},{12,10,662},{12,10,681},{14,10,423},{143,10, -141},{133,11,587},{5,0,850},{136,0,799},{10,0,908},{12,0,701},{12,0,757},{142,0, -466},{4,0,62},{5,0,275},{18,0,19},{6,10,399},{6,10,579},{7,10,692},{7,10,846},{7 -,10,1015},{7,10,1799},{8,10,403},{9,10,394},{10,10,133},{12,10,4},{12,10,297},{ -12,10,452},{16,10,81},{18,10,25},{21,10,14},{22,10,12},{151,10,18},{12,0,459},{7 -,10,1546},{11,10,299},{142,10,407},{132,10,177},{132,11,498},{7,11,217},{8,11, -140},{138,11,610},{5,10,411},{135,10,653},{134,0,1802},{7,10,439},{10,10,727},{ -11,10,260},{139,10,684},{133,11,905},{11,11,580},{142,11,201},{134,0,1397},{5,10 -,208},{7,10,753},{135,10,1528},{7,0,238},{7,0,2033},{8,0,120},{8,0,188},{8,0,659 -},{9,0,598},{10,0,466},{12,0,342},{12,0,588},{13,0,503},{14,0,246},{143,0,92},{ -135,11,1041},{4,11,456},{7,11,105},{7,11,358},{7,11,1637},{8,11,643},{139,11,483 -},{6,0,1318},{134,0,1324},{4,0,201},{7,0,1744},{8,0,602},{11,0,247},{11,0,826},{ -17,0,65},{133,10,242},{8,0,164},{146,0,62},{133,10,953},{139,10,802},{133,0,615} -,{7,11,1566},{8,11,269},{9,11,212},{9,11,718},{14,11,15},{14,11,132},{142,11,227 -},{133,10,290},{132,10,380},{5,10,52},{7,10,277},{9,10,368},{139,10,791},{135,0, -1243},{133,11,539},{11,11,919},{141,11,409},{136,0,968},{133,11,470},{134,0,882} -,{132,0,907},{5,0,100},{10,0,329},{12,0,416},{149,0,29},{10,10,138},{139,10,476} -,{5,10,725},{5,10,727},{6,11,91},{7,11,435},{135,10,1811},{4,11,16},{5,11,316},{ -5,11,842},{6,11,370},{6,11,1778},{8,11,166},{11,11,812},{12,11,206},{12,11,351}, -{14,11,418},{16,11,15},{16,11,34},{18,11,3},{19,11,3},{19,11,7},{20,11,4},{149, -11,21},{132,0,176},{5,0,636},{5,0,998},{7,0,9},{7,0,1508},{8,0,26},{9,0,317},{9, -0,358},{10,0,210},{10,0,292},{10,0,533},{11,0,555},{12,0,526},{12,0,607},{13,0, -263},{13,0,459},{142,0,271},{6,0,256},{8,0,265},{4,10,38},{7,10,307},{7,10,999}, -{7,10,1481},{7,10,1732},{7,10,1738},{9,10,414},{11,10,316},{12,10,52},{13,10,420 -},{147,10,100},{135,10,1296},{4,11,611},{133,11,606},{4,0,643},{142,11,21},{133, -11,715},{133,10,723},{6,0,610},{135,11,597},{10,0,127},{141,0,27},{6,0,1995},{6, -0,2001},{8,0,119},{136,0,973},{4,11,149},{138,11,368},{12,0,522},{4,11,154},{5, -10,109},{6,10,1784},{7,11,1134},{7,10,1895},{8,11,105},{12,10,296},{140,10,302}, -{4,11,31},{6,11,429},{7,11,962},{9,11,458},{139,11,691},{10,0,553},{11,0,876},{ -13,0,193},{13,0,423},{14,0,166},{19,0,84},{4,11,312},{5,10,216},{7,10,1879},{9, -10,141},{9,10,270},{9,10,679},{10,10,159},{11,10,197},{12,10,538},{12,10,559},{ -14,10,144},{14,10,167},{143,10,67},{134,0,1582},{7,0,1578},{135,11,1578},{137,10 -,81},{132,11,236},{134,10,391},{134,0,795},{7,10,322},{136,10,249},{5,11,836},{5 -,11,857},{6,11,1680},{7,11,59},{147,11,53},{135,0,432},{10,11,68},{139,11,494},{ -4,11,81},{139,11,867},{7,0,126},{136,0,84},{142,11,280},{5,11,282},{8,11,650},{9 -,11,295},{9,11,907},{138,11,443},{136,0,790},{5,10,632},{138,10,526},{6,0,64},{ -12,0,377},{13,0,309},{14,0,141},{14,0,429},{14,11,141},{142,11,429},{134,0,1529} -,{6,0,321},{7,0,1857},{9,0,530},{19,0,99},{7,10,948},{7,10,1042},{8,10,235},{8, -10,461},{9,10,453},{10,10,354},{145,10,77},{7,0,1104},{11,0,269},{11,0,539},{11, -0,627},{11,0,706},{11,0,975},{12,0,248},{12,0,434},{12,0,600},{12,0,622},{13,0, -297},{13,0,485},{14,0,69},{14,0,409},{143,0,108},{4,10,362},{7,10,52},{7,10,303} -,{10,11,70},{12,11,26},{14,11,17},{14,11,178},{15,11,34},{149,11,12},{11,0,977}, -{141,0,507},{9,0,34},{139,0,484},{5,10,196},{6,10,486},{7,10,212},{8,10,309},{ -136,10,346},{6,0,1700},{7,0,26},{7,0,293},{7,0,382},{7,0,1026},{7,0,1087},{7,0, -2027},{8,0,24},{8,0,114},{8,0,252},{8,0,727},{8,0,729},{9,0,30},{9,0,199},{9,0, -231},{9,0,251},{9,0,334},{9,0,361},{9,0,712},{10,0,55},{10,0,60},{10,0,232},{10, -0,332},{10,0,384},{10,0,396},{10,0,504},{10,0,542},{10,0,652},{11,0,20},{11,0,48 -},{11,0,207},{11,0,291},{11,0,298},{11,0,342},{11,0,365},{11,0,394},{11,0,620},{ -11,0,705},{11,0,1017},{12,0,123},{12,0,340},{12,0,406},{12,0,643},{13,0,61},{13, -0,269},{13,0,311},{13,0,319},{13,0,486},{14,0,234},{15,0,62},{15,0,85},{16,0,71} -,{18,0,119},{20,0,105},{135,10,1912},{4,11,71},{5,11,376},{7,11,119},{138,11,665 -},{10,0,918},{10,0,926},{4,10,686},{136,11,55},{138,10,625},{136,10,706},{132,11 -,479},{4,10,30},{133,10,43},{6,0,379},{7,0,270},{8,0,176},{8,0,183},{9,0,432},{9 -,0,661},{12,0,247},{12,0,617},{18,0,125},{7,11,607},{8,11,99},{152,11,4},{5,0, -792},{133,0,900},{4,11,612},{133,11,561},{4,11,41},{4,10,220},{5,11,74},{7,10, -1535},{7,11,1627},{11,11,871},{140,11,619},{135,0,1920},{7,11,94},{11,11,329},{ -11,11,965},{12,11,241},{14,11,354},{15,11,22},{148,11,63},{9,11,209},{137,11,300 -},{134,0,771},{135,0,1979},{4,0,901},{133,0,776},{142,0,254},{133,11,98},{9,11, -16},{141,11,386},{133,11,984},{4,11,182},{6,11,205},{135,11,220},{7,10,1725},{7, -10,1774},{138,10,393},{5,10,263},{134,10,414},{4,11,42},{9,11,205},{9,11,786},{ -138,11,659},{14,0,140},{148,0,41},{8,0,440},{10,0,359},{6,10,178},{6,11,289},{6, -10,1750},{7,11,1670},{9,10,690},{10,10,155},{10,10,373},{11,10,698},{12,11,57},{ -13,10,155},{20,10,93},{151,11,4},{4,0,37},{5,0,334},{7,0,1253},{151,11,25},{4,0, -508},{4,11,635},{5,10,97},{137,10,393},{139,11,533},{4,0,640},{133,0,513},{134, -10,1639},{132,11,371},{4,11,272},{7,11,836},{7,11,1651},{145,11,89},{5,11,825},{ -6,11,444},{6,11,1640},{136,11,308},{4,10,191},{7,10,934},{8,10,647},{145,10,97}, -{12,0,246},{15,0,162},{19,0,64},{20,0,8},{20,0,95},{22,0,24},{152,0,17},{4,0,533 -},{5,10,165},{9,10,346},{138,10,655},{5,11,737},{139,10,885},{133,10,877},{8,10, -128},{139,10,179},{137,11,307},{140,0,752},{133,0,920},{135,0,1048},{5,0,153},{6 -,0,580},{6,10,1663},{7,10,132},{7,10,1154},{7,10,1415},{7,10,1507},{12,10,493},{ -15,10,105},{151,10,15},{5,10,459},{7,10,1073},{8,10,241},{136,10,334},{138,0,391 -},{135,0,1952},{133,11,525},{8,11,641},{11,11,388},{140,11,580},{142,0,126},{134 -,0,640},{132,0,483},{7,0,1616},{9,0,69},{6,10,324},{6,10,520},{7,10,338},{7,10, -1729},{8,10,228},{139,10,750},{5,11,493},{134,11,528},{135,0,734},{4,11,174},{ -135,11,911},{138,0,480},{9,0,495},{146,0,104},{135,10,705},{9,0,472},{4,10,73},{ -6,10,612},{7,10,927},{7,10,1330},{7,10,1822},{8,10,217},{9,10,765},{9,10,766},{ -10,10,408},{11,10,51},{11,10,793},{12,10,266},{15,10,158},{20,10,89},{150,10,32} -,{7,11,548},{137,11,58},{4,11,32},{5,11,215},{6,11,269},{7,11,1782},{7,11,1892}, -{10,11,16},{11,11,822},{11,11,954},{141,11,481},{132,0,874},{9,0,229},{5,10,389} -,{136,10,636},{7,11,1749},{136,11,477},{134,0,948},{5,11,308},{135,11,1088},{4,0 -,748},{139,0,1009},{136,10,21},{6,0,555},{135,0,485},{5,11,126},{8,11,297},{9,11 -,366},{9,11,445},{12,11,53},{12,11,374},{141,11,492},{7,11,1551},{139,11,361},{ -136,0,193},{136,0,472},{8,0,653},{13,0,93},{147,0,14},{132,0,984},{132,11,175},{ -5,0,172},{6,0,1971},{132,11,685},{149,11,8},{133,11,797},{13,0,83},{5,10,189},{7 -,10,442},{7,10,443},{8,10,281},{12,10,174},{141,10,261},{134,0,1568},{133,11,565 -},{139,0,384},{133,0,260},{7,0,758},{7,0,880},{7,0,1359},{9,0,164},{9,0,167},{10 -,0,156},{10,0,588},{12,0,101},{14,0,48},{15,0,70},{6,10,2},{7,10,1262},{7,10, -1737},{8,10,22},{8,10,270},{8,10,612},{9,10,312},{9,10,436},{10,10,311},{10,10, -623},{11,10,72},{11,10,330},{11,10,455},{12,10,321},{12,10,504},{12,10,530},{12, -10,543},{13,10,17},{13,10,156},{13,10,334},{17,10,60},{148,10,64},{4,11,252},{7, -11,1068},{10,11,434},{11,11,228},{11,11,426},{13,11,231},{18,11,106},{148,11,87} -,{7,10,354},{10,10,410},{139,10,815},{6,0,367},{7,10,670},{7,10,1327},{8,10,411} -,{8,10,435},{9,10,653},{9,10,740},{10,10,385},{11,10,222},{11,10,324},{11,10,829 -},{140,10,611},{7,0,1174},{6,10,166},{135,10,374},{146,0,121},{132,0,828},{5,11, -231},{138,11,509},{7,11,601},{9,11,277},{9,11,674},{10,11,178},{10,11,257},{10, -11,418},{11,11,531},{11,11,544},{11,11,585},{12,11,113},{12,11,475},{13,11,99},{ -142,11,428},{134,0,1541},{135,11,1779},{5,0,343},{134,10,398},{135,10,50},{135, -11,1683},{4,0,440},{7,0,57},{8,0,167},{8,0,375},{9,0,82},{9,0,561},{9,0,744},{10 -,0,620},{137,11,744},{134,0,926},{6,10,517},{7,10,1159},{10,10,621},{139,10,192} -,{137,0,827},{8,0,194},{136,0,756},{10,10,223},{139,10,645},{7,10,64},{136,10, -245},{4,11,399},{5,11,119},{5,11,494},{7,11,751},{137,11,556},{132,0,808},{135,0 -,22},{7,10,1763},{140,10,310},{5,0,639},{7,0,1249},{11,0,896},{134,11,584},{134, -0,1614},{135,0,860},{135,11,1121},{5,10,129},{6,10,61},{135,10,947},{4,0,102},{7 -,0,815},{7,0,1699},{139,0,964},{13,10,505},{141,10,506},{139,10,1000},{132,11, -679},{132,0,899},{132,0,569},{5,11,694},{137,11,714},{136,0,795},{6,0,2045},{139 -,11,7},{6,0,52},{9,0,104},{9,0,559},{12,0,308},{147,0,87},{4,0,301},{132,0,604}, -{133,10,637},{136,0,779},{5,11,143},{5,11,769},{6,11,1760},{7,11,682},{7,11,1992 -},{136,11,736},{137,10,590},{147,0,32},{137,11,527},{5,10,280},{135,10,1226},{ -134,0,494},{6,0,677},{6,0,682},{134,0,1044},{133,10,281},{135,10,1064},{7,0,508} -,{133,11,860},{6,11,422},{7,11,0},{7,11,1544},{9,11,577},{11,11,990},{12,11,141} -,{12,11,453},{13,11,47},{141,11,266},{134,0,1014},{5,11,515},{137,11,131},{134,0 -,957},{132,11,646},{6,0,310},{7,0,1849},{8,0,72},{8,0,272},{8,0,431},{9,0,12},{9 -,0,376},{10,0,563},{10,0,630},{10,0,796},{10,0,810},{11,0,367},{11,0,599},{11,0, -686},{140,0,672},{7,0,570},{4,11,396},{7,10,120},{7,11,728},{8,10,489},{9,11,117 -},{9,10,319},{10,10,820},{11,10,1004},{12,10,379},{12,10,679},{13,10,117},{13,11 -,202},{13,10,412},{14,10,25},{15,10,52},{15,10,161},{16,10,47},{20,11,51},{149, -10,2},{6,11,121},{6,11,124},{6,11,357},{7,11,1138},{7,11,1295},{8,11,162},{139, -11,655},{8,0,449},{4,10,937},{5,10,801},{136,11,449},{139,11,958},{6,0,181},{7,0 -,537},{8,0,64},{9,0,127},{10,0,496},{12,0,510},{141,0,384},{138,11,253},{4,0,244 -},{135,0,233},{133,11,237},{132,10,365},{6,0,1650},{10,0,702},{139,0,245},{5,10, -7},{139,10,774},{13,0,463},{20,0,49},{13,11,463},{148,11,49},{4,10,734},{5,10, -662},{134,10,430},{4,10,746},{135,10,1090},{5,10,360},{136,10,237},{137,0,338},{ -143,11,10},{7,11,571},{138,11,366},{134,0,1279},{9,11,513},{10,11,22},{10,11,39} -,{12,11,122},{140,11,187},{133,0,896},{146,0,178},{134,0,695},{137,0,808},{134, -11,587},{7,11,107},{7,11,838},{8,11,550},{138,11,401},{7,0,1117},{136,0,539},{4, -10,277},{5,10,608},{6,10,493},{7,10,457},{140,10,384},{133,11,768},{12,0,257},{7 -,10,27},{135,10,316},{140,0,1003},{4,0,207},{5,0,586},{5,0,676},{6,0,448},{8,0, -244},{11,0,1},{13,0,3},{16,0,54},{17,0,4},{18,0,13},{133,10,552},{4,10,401},{137 -,10,264},{5,0,516},{7,0,1883},{135,11,1883},{12,0,960},{132,11,894},{5,0,4},{5,0 -,810},{6,0,13},{6,0,538},{6,0,1690},{6,0,1726},{7,0,499},{7,0,1819},{8,0,148},{8 -,0,696},{8,0,791},{12,0,125},{143,0,9},{135,0,1268},{11,0,30},{14,0,315},{9,10, -543},{10,10,524},{12,10,524},{16,10,18},{20,10,26},{148,10,65},{6,0,748},{4,10, -205},{5,10,623},{7,10,104},{136,10,519},{11,0,542},{139,0,852},{140,0,6},{132,0, -848},{7,0,1385},{11,0,582},{11,0,650},{11,0,901},{11,0,949},{12,0,232},{12,0,236 -},{13,0,413},{13,0,501},{18,0,116},{7,10,579},{9,10,41},{9,10,244},{9,10,669},{ -10,10,5},{11,10,861},{11,10,951},{139,10,980},{4,0,945},{6,0,1811},{6,0,1845},{6 -,0,1853},{6,0,1858},{8,0,862},{12,0,782},{12,0,788},{18,0,160},{148,0,117},{132, -10,717},{4,0,925},{5,0,803},{8,0,698},{138,0,828},{134,0,1416},{132,0,610},{139, -0,992},{6,0,878},{134,0,1477},{135,0,1847},{138,11,531},{137,11,539},{134,11,272 -},{133,0,383},{134,0,1404},{132,10,489},{4,11,9},{5,11,128},{7,11,368},{11,11, -480},{148,11,3},{136,0,986},{9,0,660},{138,0,347},{135,10,892},{136,11,682},{7,0 -,572},{9,0,592},{11,0,680},{12,0,356},{140,0,550},{7,0,1411},{138,11,527},{4,11, -2},{7,11,545},{135,11,894},{137,10,473},{11,0,64},{7,11,481},{7,10,819},{9,10,26 -},{9,10,392},{9,11,792},{10,10,152},{10,10,226},{12,10,276},{12,10,426},{12,10, -589},{13,10,460},{15,10,97},{19,10,48},{148,10,104},{135,10,51},{136,11,445},{ -136,11,646},{135,0,606},{132,10,674},{6,0,1829},{134,0,1830},{132,10,770},{5,10, -79},{7,10,1027},{7,10,1477},{139,10,52},{5,11,530},{142,11,113},{134,10,1666},{7 -,0,748},{139,0,700},{134,10,195},{133,10,789},{9,0,87},{10,0,365},{4,10,251},{4, -10,688},{7,10,513},{135,10,1284},{136,11,111},{133,0,127},{6,0,198},{140,0,83},{ -133,11,556},{133,10,889},{4,10,160},{5,10,330},{7,10,1434},{136,10,174},{5,0,276 -},{6,0,55},{7,0,1369},{138,0,864},{8,11,16},{140,11,568},{6,0,1752},{136,0,726}, -{135,0,1066},{133,0,764},{6,11,186},{137,11,426},{11,0,683},{139,11,683},{6,0, -309},{7,0,331},{138,0,550},{133,10,374},{6,0,1212},{6,0,1852},{7,0,1062},{8,0, -874},{8,0,882},{138,0,936},{132,11,585},{134,0,1364},{7,0,986},{133,10,731},{6,0 -,723},{6,0,1408},{138,0,381},{135,0,1573},{134,0,1025},{4,10,626},{5,10,642},{6, -10,425},{10,10,202},{139,10,141},{4,11,93},{5,11,252},{6,11,229},{7,11,291},{9, -11,550},{139,11,644},{137,11,749},{137,11,162},{132,11,381},{135,0,1559},{6,0, -194},{7,0,133},{10,0,493},{10,0,570},{139,0,664},{5,0,24},{5,0,569},{6,0,3},{6,0 -,119},{6,0,143},{6,0,440},{7,0,295},{7,0,599},{7,0,1686},{7,0,1854},{8,0,424},{9 -,0,43},{9,0,584},{9,0,760},{10,0,148},{10,0,328},{11,0,159},{11,0,253},{11,0,506 -},{12,0,487},{140,0,531},{6,0,661},{134,0,1517},{136,10,835},{151,10,17},{5,0,14 -},{5,0,892},{6,0,283},{7,0,234},{136,0,537},{139,0,541},{4,0,126},{8,0,635},{147 -,0,34},{4,0,316},{4,0,495},{135,0,1561},{4,11,187},{5,11,184},{5,11,690},{7,11, -1869},{138,11,756},{139,11,783},{4,0,998},{137,0,861},{136,0,1009},{139,11,292}, -{5,11,21},{6,11,77},{6,11,157},{7,11,974},{7,11,1301},{7,11,1339},{7,11,1490},{7 -,11,1873},{137,11,628},{7,11,1283},{9,11,227},{9,11,499},{10,11,341},{11,11,325} -,{11,11,408},{14,11,180},{15,11,144},{18,11,47},{147,11,49},{4,0,64},{5,0,352},{ -5,0,720},{6,0,368},{139,0,359},{5,10,384},{8,10,455},{140,10,48},{5,10,264},{134 -,10,184},{7,0,1577},{10,0,304},{10,0,549},{12,0,365},{13,0,220},{13,0,240},{142, -0,33},{134,0,1107},{134,0,929},{135,0,1142},{6,0,175},{137,0,289},{5,0,432},{133 -,0,913},{6,0,279},{7,0,219},{5,10,633},{135,10,1323},{7,0,785},{7,10,359},{8,10, -243},{140,10,175},{139,0,595},{132,10,105},{8,11,398},{9,11,681},{139,11,632},{ -140,0,80},{5,0,931},{134,0,1698},{142,11,241},{134,11,20},{134,0,1323},{11,0,526 -},{11,0,939},{141,0,290},{5,0,774},{6,0,780},{6,0,1637},{6,0,1686},{6,0,1751},{8 -,0,559},{141,0,109},{141,0,127},{7,0,1167},{11,0,934},{13,0,391},{17,0,76},{135, -11,709},{135,0,963},{6,0,260},{135,0,1484},{134,0,573},{4,10,758},{139,11,941},{ -135,10,1649},{145,11,36},{4,0,292},{137,0,580},{4,0,736},{5,0,871},{6,0,1689},{ -135,0,1944},{7,11,945},{11,11,713},{139,11,744},{134,0,1164},{135,11,937},{6,0, -1922},{9,0,982},{15,0,173},{15,0,178},{15,0,200},{18,0,189},{18,0,207},{21,0,47} -,{135,11,1652},{7,0,1695},{139,10,128},{6,0,63},{135,0,920},{133,0,793},{143,11, -134},{133,10,918},{5,0,67},{6,0,62},{6,0,374},{135,0,1391},{9,0,790},{12,0,47},{ -4,11,579},{5,11,226},{5,11,323},{135,11,960},{10,11,784},{141,11,191},{4,0,391}, -{135,0,1169},{137,0,443},{13,11,232},{146,11,35},{132,10,340},{132,0,271},{137, -11,313},{5,11,973},{137,11,659},{134,0,1140},{6,11,135},{135,11,1176},{4,0,253}, -{5,0,544},{7,0,300},{137,0,340},{7,0,897},{5,10,985},{7,10,509},{145,10,96},{138 -,11,735},{135,10,1919},{138,0,890},{5,0,818},{134,0,1122},{5,0,53},{5,0,541},{6, -0,94},{6,0,499},{7,0,230},{139,0,321},{4,0,920},{5,0,25},{5,0,790},{6,0,457},{7, -0,853},{8,0,788},{142,11,31},{132,10,247},{135,11,314},{132,0,468},{7,0,243},{6, -10,337},{7,10,494},{8,10,27},{8,10,599},{138,10,153},{4,10,184},{5,10,390},{7,10 -,618},{7,10,1456},{139,10,710},{134,0,870},{134,0,1238},{134,0,1765},{10,0,853}, -{10,0,943},{14,0,437},{14,0,439},{14,0,443},{14,0,446},{14,0,452},{14,0,469},{14 -,0,471},{14,0,473},{16,0,93},{16,0,102},{16,0,110},{148,0,121},{4,0,605},{7,0, -518},{7,0,1282},{7,0,1918},{10,0,180},{139,0,218},{133,0,822},{4,0,634},{11,0, -916},{142,0,419},{6,11,281},{7,11,6},{8,11,282},{8,11,480},{8,11,499},{9,11,198} -,{10,11,143},{10,11,169},{10,11,211},{10,11,417},{10,11,574},{11,11,147},{11,11, -395},{12,11,75},{12,11,407},{12,11,608},{13,11,500},{142,11,251},{134,0,898},{6, -0,36},{7,0,658},{8,0,454},{150,11,48},{133,11,674},{135,11,1776},{4,11,419},{10, -10,227},{11,10,497},{11,10,709},{140,10,415},{6,10,360},{7,10,1664},{136,10,478} -,{137,0,806},{12,11,508},{14,11,102},{14,11,226},{144,11,57},{135,11,1123},{4,11 -,138},{7,11,1012},{7,11,1280},{137,11,76},{5,11,29},{140,11,638},{136,10,699},{ -134,0,1326},{132,0,104},{135,11,735},{132,10,739},{134,0,1331},{7,0,260},{135,11 -,260},{135,11,1063},{7,0,45},{9,0,542},{9,0,566},{10,0,728},{137,10,869},{4,10, -67},{5,10,422},{7,10,1037},{7,10,1289},{7,10,1555},{9,10,741},{145,10,108},{139, -0,263},{134,0,1516},{14,0,146},{15,0,42},{16,0,23},{17,0,86},{146,0,17},{138,0, -468},{136,0,1005},{4,11,17},{5,11,23},{7,11,995},{11,11,383},{11,11,437},{12,11, -460},{140,11,532},{7,0,87},{142,0,288},{138,10,96},{135,11,626},{144,10,26},{7,0 -,988},{7,0,1939},{9,0,64},{9,0,502},{12,0,22},{12,0,34},{13,0,12},{13,0,234},{ -147,0,77},{13,0,133},{8,10,203},{11,10,823},{11,10,846},{12,10,482},{13,10,277}, -{13,10,302},{13,10,464},{14,10,205},{142,10,221},{4,10,449},{133,10,718},{135,0, -141},{6,0,1842},{136,0,872},{8,11,70},{12,11,171},{141,11,272},{4,10,355},{6,10, -311},{9,10,256},{138,10,404},{132,0,619},{137,0,261},{10,11,233},{10,10,758},{ -139,11,76},{5,0,246},{8,0,189},{9,0,355},{9,0,512},{10,0,124},{10,0,453},{11,0, -143},{11,0,416},{11,0,859},{141,0,341},{134,11,442},{133,10,827},{5,10,64},{140, -10,581},{4,10,442},{7,10,1047},{7,10,1352},{135,10,1643},{134,11,1709},{5,0,678} -,{6,0,305},{7,0,775},{7,0,1065},{133,10,977},{11,11,69},{12,11,105},{12,11,117}, -{13,11,213},{14,11,13},{14,11,62},{14,11,177},{14,11,421},{15,11,19},{146,11,141 -},{137,11,309},{5,0,35},{7,0,862},{7,0,1886},{138,0,179},{136,0,285},{132,0,517} -,{7,11,976},{9,11,146},{10,11,206},{10,11,596},{13,11,218},{142,11,153},{132,10, -254},{6,0,214},{12,0,540},{4,10,275},{7,10,1219},{140,10,376},{8,0,667},{11,0, -403},{146,0,83},{12,0,74},{10,11,648},{11,11,671},{143,11,46},{135,0,125},{134, -10,1753},{133,0,761},{6,0,912},{4,11,518},{6,10,369},{6,10,502},{7,10,1036},{7, -11,1136},{8,10,348},{9,10,452},{10,10,26},{11,10,224},{11,10,387},{11,10,772},{ -12,10,95},{12,10,629},{13,10,195},{13,10,207},{13,10,241},{14,10,260},{14,10,270 -},{143,10,140},{10,0,131},{140,0,72},{132,10,269},{5,10,480},{7,10,532},{7,10, -1197},{7,10,1358},{8,10,291},{11,10,349},{142,10,396},{8,11,689},{137,11,863},{8 -,0,333},{138,0,182},{4,11,18},{7,11,145},{7,11,444},{7,11,1278},{8,11,49},{8,11, -400},{9,11,71},{9,11,250},{10,11,459},{12,11,160},{144,11,24},{14,11,35},{142,11 -,191},{135,11,1864},{135,0,1338},{148,10,15},{14,0,94},{15,0,65},{16,0,4},{16,0, -77},{16,0,80},{145,0,5},{12,11,82},{143,11,36},{133,11,1010},{133,0,449},{133,0, -646},{7,0,86},{8,0,103},{135,10,657},{7,0,2028},{138,0,641},{136,10,533},{134,0, -1},{139,11,970},{5,11,87},{7,11,313},{7,11,1103},{10,11,112},{10,11,582},{11,11, -389},{11,11,813},{12,11,385},{13,11,286},{14,11,124},{146,11,108},{6,0,869},{132 -,11,267},{6,0,277},{7,0,1274},{7,0,1386},{146,0,87},{6,0,187},{7,0,39},{7,0,1203 -},{8,0,380},{14,0,117},{149,0,28},{4,10,211},{4,10,332},{5,10,335},{6,10,238},{7 -,10,269},{7,10,811},{7,10,1797},{8,10,836},{9,10,507},{141,10,242},{4,0,785},{5, -0,368},{6,0,297},{7,0,793},{139,0,938},{7,0,464},{8,0,558},{11,0,105},{12,0,231} -,{14,0,386},{15,0,102},{148,0,75},{133,10,1009},{8,0,877},{140,0,731},{139,11, -289},{10,11,249},{139,11,209},{132,11,561},{134,0,1608},{132,11,760},{134,0,1429 -},{9,11,154},{140,11,485},{5,10,228},{6,10,203},{7,10,156},{8,10,347},{137,10, -265},{7,0,1010},{11,0,733},{11,0,759},{13,0,34},{14,0,427},{146,0,45},{7,10,1131 -},{135,10,1468},{136,11,255},{7,0,1656},{9,0,369},{10,0,338},{10,0,490},{11,0, -154},{11,0,545},{11,0,775},{13,0,77},{141,0,274},{133,11,621},{134,0,1038},{4,11 -,368},{135,11,641},{6,0,2010},{8,0,979},{8,0,985},{10,0,951},{138,0,1011},{134,0 -,1005},{19,0,121},{5,10,291},{5,10,318},{7,10,765},{9,10,389},{140,10,548},{5,0, -20},{6,0,298},{7,0,659},{137,0,219},{7,0,1440},{11,0,854},{11,0,872},{11,0,921}, -{12,0,551},{13,0,472},{142,0,367},{5,0,490},{6,0,615},{6,0,620},{135,0,683},{6,0 -,1070},{134,0,1597},{139,0,522},{132,0,439},{136,0,669},{6,0,766},{6,0,1143},{6, -0,1245},{10,10,525},{139,10,82},{9,11,92},{147,11,91},{6,0,668},{134,0,1218},{6, -11,525},{9,11,876},{140,11,284},{132,0,233},{136,0,547},{132,10,422},{5,10,355}, -{145,10,0},{6,11,300},{135,11,1515},{4,0,482},{137,10,905},{4,0,886},{7,0,346},{ -133,11,594},{133,10,865},{5,10,914},{134,10,1625},{135,0,334},{5,0,795},{6,0, -1741},{133,10,234},{135,10,1383},{6,11,1641},{136,11,820},{135,0,371},{7,11,1313 -},{138,11,660},{135,10,1312},{135,0,622},{7,0,625},{135,0,1750},{135,0,339},{4,0 -,203},{135,0,1936},{15,0,29},{16,0,38},{15,11,29},{144,11,38},{5,0,338},{135,0, -1256},{135,10,1493},{10,0,130},{6,10,421},{7,10,61},{7,10,1540},{138,10,501},{6, -11,389},{7,11,149},{9,11,142},{138,11,94},{137,10,341},{11,0,678},{12,0,307},{ -142,10,98},{6,11,8},{7,11,1881},{136,11,91},{135,0,2044},{6,0,770},{6,0,802},{6, -0,812},{7,0,311},{9,0,308},{12,0,255},{6,10,102},{7,10,72},{15,10,142},{147,10, -67},{151,10,30},{135,10,823},{135,0,1266},{135,11,1746},{135,10,1870},{4,0,400}, -{5,0,267},{135,0,232},{7,11,24},{11,11,542},{139,11,852},{135,11,1739},{4,11,503 -},{135,11,1661},{5,11,130},{7,11,1314},{9,11,610},{10,11,718},{11,11,601},{11,11 -,819},{11,11,946},{140,11,536},{10,11,149},{11,11,280},{142,11,336},{7,0,739},{ -11,0,690},{7,11,1946},{8,10,48},{8,10,88},{8,10,582},{8,10,681},{9,10,373},{9,10 -,864},{11,10,157},{11,10,843},{148,10,27},{134,0,990},{4,10,88},{5,10,137},{5,10 -,174},{5,10,777},{6,10,1664},{6,10,1725},{7,10,77},{7,10,426},{7,10,1317},{7,10, -1355},{8,10,126},{8,10,563},{9,10,523},{9,10,750},{10,10,310},{10,10,836},{11,10 -,42},{11,10,318},{11,10,731},{12,10,68},{12,10,92},{12,10,507},{12,10,692},{13, -10,81},{13,10,238},{13,10,374},{14,10,436},{18,10,138},{19,10,78},{19,10,111},{ -20,10,55},{20,10,77},{148,10,92},{141,10,418},{7,0,1831},{132,10,938},{6,0,776}, -{134,0,915},{138,10,351},{5,11,348},{6,11,522},{6,10,1668},{7,10,1499},{8,10,117 -},{9,10,314},{138,10,174},{135,10,707},{132,0,613},{133,10,403},{132,11,392},{5, -11,433},{9,11,633},{139,11,629},{133,0,763},{132,0,878},{132,0,977},{132,0,100}, -{6,0,463},{4,10,44},{5,10,311},{7,10,639},{7,10,762},{7,10,1827},{9,10,8},{9,10, -462},{148,10,83},{134,11,234},{4,10,346},{7,10,115},{9,10,180},{9,10,456},{138, -10,363},{5,0,362},{5,0,443},{6,0,318},{7,0,1019},{139,0,623},{5,0,463},{8,0,296} -,{7,11,140},{7,11,1950},{8,11,680},{11,11,817},{147,11,88},{7,11,1222},{138,11, -386},{142,0,137},{132,0,454},{7,0,1914},{6,11,5},{7,10,1051},{9,10,545},{11,11, -249},{12,11,313},{16,11,66},{145,11,26},{135,0,1527},{145,0,58},{148,11,59},{5,0 -,48},{5,0,404},{6,0,557},{7,0,458},{8,0,597},{10,0,455},{10,0,606},{11,0,49},{11 -,0,548},{12,0,476},{13,0,18},{141,0,450},{5,11,963},{134,11,1773},{133,0,729},{ -138,11,586},{5,0,442},{135,0,1984},{134,0,449},{144,0,40},{4,0,853},{7,11,180},{ -8,11,509},{136,11,792},{6,10,185},{7,10,1899},{9,10,875},{139,10,673},{134,11, -524},{12,0,227},{4,10,327},{5,10,478},{7,10,1332},{136,10,753},{6,0,1491},{5,10, -1020},{133,10,1022},{4,10,103},{133,10,401},{132,11,931},{4,10,499},{135,10,1421 -},{5,0,55},{7,0,376},{140,0,161},{133,0,450},{6,0,1174},{134,0,1562},{10,0,62},{ -13,0,400},{135,11,1837},{140,0,207},{135,0,869},{4,11,773},{5,11,618},{137,11, -756},{132,10,96},{4,0,213},{7,0,223},{8,0,80},{135,10,968},{4,11,90},{5,11,337}, -{5,11,545},{7,11,754},{9,11,186},{10,11,72},{10,11,782},{11,11,513},{11,11,577}, -{11,11,610},{11,11,889},{11,11,961},{12,11,354},{12,11,362},{12,11,461},{12,11, -595},{13,11,79},{143,11,121},{7,0,381},{7,0,806},{7,0,820},{8,0,354},{8,0,437},{ -8,0,787},{9,0,657},{10,0,58},{10,0,339},{10,0,749},{11,0,914},{12,0,162},{13,0, -75},{14,0,106},{14,0,198},{14,0,320},{14,0,413},{146,0,43},{136,0,747},{136,0, -954},{134,0,1073},{135,0,556},{7,11,151},{9,11,329},{139,11,254},{5,0,692},{134, -0,1395},{6,10,563},{137,10,224},{134,0,191},{132,0,804},{9,11,187},{10,11,36},{ -17,11,44},{146,11,64},{7,11,165},{7,11,919},{136,11,517},{4,11,506},{5,11,295},{ -7,11,1680},{15,11,14},{144,11,5},{4,0,706},{6,0,162},{7,0,1960},{136,0,831},{135 -,11,1376},{7,11,987},{9,11,688},{10,11,522},{11,11,788},{140,11,566},{150,0,35}, -{138,0,426},{135,0,1235},{135,11,1741},{7,11,389},{7,11,700},{7,11,940},{8,11, -514},{9,11,116},{9,11,535},{10,11,118},{11,11,107},{11,11,148},{11,11,922},{12, -11,254},{12,11,421},{142,11,238},{134,0,1234},{132,11,743},{4,10,910},{5,10,832} -,{135,11,1335},{141,0,96},{135,11,185},{146,0,149},{4,0,204},{137,0,902},{4,11, -784},{133,11,745},{136,0,833},{136,0,949},{7,0,366},{9,0,287},{12,0,199},{12,0, -556},{12,0,577},{5,11,81},{7,11,146},{7,11,1342},{7,11,1446},{8,11,53},{8,11,561 -},{8,11,694},{8,11,754},{9,11,97},{9,11,115},{9,11,894},{10,11,462},{10,11,813}, -{11,11,230},{11,11,657},{11,11,699},{11,11,748},{12,11,119},{12,11,200},{12,11, -283},{14,11,273},{145,11,15},{5,11,408},{137,11,747},{9,11,498},{140,11,181},{6, -0,2020},{136,0,992},{5,0,356},{135,0,224},{134,0,784},{7,0,630},{9,0,567},{11,0, -150},{11,0,444},{13,0,119},{8,10,528},{137,10,348},{134,0,539},{4,10,20},{133,10 -,616},{142,0,27},{7,11,30},{8,11,86},{8,11,315},{8,11,700},{9,11,576},{9,11,858} -,{11,11,310},{11,11,888},{11,11,904},{12,11,361},{141,11,248},{138,11,839},{134, -0,755},{134,0,1063},{7,10,1091},{135,10,1765},{134,11,428},{7,11,524},{8,11,169} -,{8,11,234},{9,11,480},{138,11,646},{139,0,814},{7,11,1462},{139,11,659},{4,10, -26},{5,10,429},{6,10,245},{7,10,704},{7,10,1379},{135,10,1474},{7,11,1205},{138, -11,637},{139,11,803},{132,10,621},{136,0,987},{4,11,266},{8,11,4},{9,11,39},{10, -11,166},{11,11,918},{12,11,635},{20,11,10},{22,11,27},{150,11,43},{4,0,235},{135 -,0,255},{4,0,194},{5,0,584},{6,0,384},{7,0,583},{10,0,761},{11,0,760},{139,0,851 -},{133,10,542},{134,0,1086},{133,10,868},{8,0,1016},{136,0,1018},{7,0,1396},{7, -11,1396},{136,10,433},{135,10,1495},{138,10,215},{141,10,124},{7,11,157},{8,11, -279},{9,11,759},{16,11,31},{16,11,39},{16,11,75},{18,11,24},{20,11,42},{152,11,1 -},{5,0,562},{134,11,604},{134,0,913},{5,0,191},{137,0,271},{4,0,470},{6,0,153},{ -7,0,1503},{7,0,1923},{10,0,701},{11,0,132},{11,0,227},{11,0,320},{11,0,436},{11, -0,525},{11,0,855},{11,0,873},{12,0,41},{12,0,286},{13,0,103},{13,0,284},{14,0, -255},{14,0,262},{15,0,117},{143,0,127},{7,0,475},{12,0,45},{147,10,112},{132,11, -567},{137,11,859},{6,0,713},{6,0,969},{6,0,1290},{134,0,1551},{133,0,327},{6,0, -552},{6,0,1292},{7,0,1754},{137,0,604},{4,0,223},{6,0,359},{11,0,3},{13,0,108},{ -14,0,89},{16,0,22},{5,11,762},{7,11,1880},{9,11,680},{139,11,798},{5,0,80},{6,0, -405},{7,0,403},{7,0,1502},{8,0,456},{9,0,487},{9,0,853},{9,0,889},{10,0,309},{11 -,0,721},{11,0,994},{12,0,430},{141,0,165},{133,11,298},{132,10,647},{134,0,2016} -,{18,10,10},{146,11,10},{4,0,453},{5,0,887},{6,0,535},{8,0,6},{8,0,543},{136,0, -826},{136,0,975},{10,0,961},{138,0,962},{138,10,220},{6,0,1891},{6,0,1893},{9,0, -916},{9,0,965},{9,0,972},{12,0,801},{12,0,859},{12,0,883},{15,0,226},{149,0,51}, -{132,10,109},{135,11,267},{7,11,92},{7,11,182},{8,11,453},{9,11,204},{11,11,950} -,{12,11,94},{12,11,644},{16,11,20},{16,11,70},{16,11,90},{147,11,55},{134,10, -1746},{6,11,71},{7,11,845},{7,11,1308},{8,11,160},{137,11,318},{5,0,101},{6,0,88 -},{7,0,263},{7,0,628},{7,0,1677},{8,0,349},{9,0,100},{10,0,677},{14,0,169},{14,0 -,302},{14,0,313},{15,0,48},{15,0,84},{7,11,237},{8,11,664},{9,11,42},{9,11,266}, -{9,11,380},{9,11,645},{10,11,177},{138,11,276},{138,11,69},{4,0,310},{7,0,708},{ -7,0,996},{9,0,795},{10,0,390},{10,0,733},{11,0,451},{12,0,249},{14,0,115},{14,0, -286},{143,0,100},{5,0,587},{4,10,40},{10,10,67},{11,10,117},{11,10,768},{139,10, -935},{6,0,1942},{7,0,512},{136,0,983},{7,10,992},{8,10,301},{9,10,722},{12,10,63 -},{13,10,29},{14,10,161},{143,10,18},{136,11,76},{139,10,923},{134,0,645},{134,0 -,851},{4,0,498},{132,11,293},{7,0,217},{8,0,140},{10,0,610},{14,11,352},{17,11, -53},{18,11,146},{18,11,152},{19,11,11},{150,11,54},{134,0,1448},{138,11,841},{ -133,0,905},{4,11,605},{7,11,518},{7,11,1282},{7,11,1918},{10,11,180},{139,11,218 -},{139,11,917},{135,10,825},{140,10,328},{4,0,456},{7,0,105},{7,0,358},{7,0,1637 -},{8,0,643},{139,0,483},{134,0,792},{6,11,96},{135,11,1426},{137,11,691},{4,11, -651},{133,11,289},{7,11,688},{8,11,35},{9,11,511},{10,11,767},{147,11,118},{150, -0,56},{5,0,243},{5,0,535},{6,10,204},{10,10,320},{10,10,583},{13,10,502},{14,10, -72},{14,10,274},{14,10,312},{14,10,344},{15,10,159},{16,10,62},{16,10,69},{17,10 -,30},{18,10,42},{18,10,53},{18,10,84},{18,10,140},{19,10,68},{19,10,85},{20,10,5 -},{20,10,45},{20,10,101},{22,10,7},{150,10,20},{4,10,558},{6,10,390},{7,10,162}, -{7,10,689},{9,10,360},{138,10,653},{146,11,23},{135,0,1748},{5,10,856},{6,10, -1672},{6,10,1757},{134,10,1781},{5,0,539},{5,0,754},{6,0,876},{132,11,704},{135, -11,1078},{5,10,92},{10,10,736},{140,10,102},{17,0,91},{5,10,590},{137,10,213},{ -134,0,1565},{6,0,91},{135,0,435},{4,0,939},{140,0,792},{134,0,1399},{4,0,16},{5, -0,316},{5,0,842},{6,0,370},{6,0,1778},{8,0,166},{11,0,812},{12,0,206},{12,0,351} -,{14,0,418},{16,0,15},{16,0,34},{18,0,3},{19,0,3},{19,0,7},{20,0,4},{21,0,21},{4 -,11,720},{133,11,306},{144,0,95},{133,11,431},{132,11,234},{135,0,551},{4,0,999} -,{6,0,1966},{134,0,2042},{7,0,619},{10,0,547},{11,0,122},{12,0,601},{15,0,7},{ -148,0,20},{5,11,464},{6,11,236},{7,11,276},{7,11,696},{7,11,914},{7,11,1108},{7, -11,1448},{9,11,15},{9,11,564},{10,11,14},{12,11,565},{13,11,449},{14,11,53},{15, -11,13},{16,11,64},{145,11,41},{6,0,884},{6,0,1019},{134,0,1150},{6,11,1767},{12, -11,194},{145,11,107},{136,10,503},{133,11,840},{7,0,671},{134,10,466},{132,0,888 -},{4,0,149},{138,0,368},{4,0,154},{7,0,1134},{136,0,105},{135,0,983},{9,11,642}, -{11,11,236},{142,11,193},{4,0,31},{6,0,429},{7,0,962},{9,0,458},{139,0,691},{6,0 -,643},{134,0,1102},{132,0,312},{4,11,68},{5,11,634},{6,11,386},{7,11,794},{8,11, -273},{9,11,563},{10,11,105},{10,11,171},{11,11,94},{139,11,354},{133,0,740},{135 -,0,1642},{4,11,95},{7,11,416},{8,11,211},{139,11,830},{132,0,236},{138,10,241},{ -7,11,731},{13,11,20},{143,11,11},{5,0,836},{5,0,857},{6,0,1680},{135,0,59},{10,0 -,68},{11,0,494},{152,11,6},{4,0,81},{139,0,867},{135,0,795},{133,11,689},{4,0, -1001},{5,0,282},{6,0,1932},{6,0,1977},{6,0,1987},{6,0,1992},{8,0,650},{8,0,919}, -{8,0,920},{8,0,923},{8,0,926},{8,0,927},{8,0,931},{8,0,939},{8,0,947},{8,0,956}, -{8,0,997},{9,0,907},{10,0,950},{10,0,953},{10,0,954},{10,0,956},{10,0,958},{10,0 -,959},{10,0,964},{10,0,970},{10,0,972},{10,0,973},{10,0,975},{10,0,976},{10,0, -980},{10,0,981},{10,0,984},{10,0,988},{10,0,990},{10,0,995},{10,0,999},{10,0, -1002},{10,0,1003},{10,0,1005},{10,0,1006},{10,0,1008},{10,0,1009},{10,0,1012},{ -10,0,1014},{10,0,1015},{10,0,1019},{10,0,1020},{10,0,1022},{12,0,959},{12,0,961} -,{12,0,962},{12,0,963},{12,0,964},{12,0,965},{12,0,967},{12,0,968},{12,0,969},{ -12,0,970},{12,0,971},{12,0,972},{12,0,973},{12,0,974},{12,0,975},{12,0,976},{12, -0,977},{12,0,979},{12,0,981},{12,0,982},{12,0,983},{12,0,984},{12,0,985},{12,0, -986},{12,0,987},{12,0,989},{12,0,990},{12,0,992},{12,0,993},{12,0,995},{12,0,998 -},{12,0,999},{12,0,1000},{12,0,1001},{12,0,1002},{12,0,1004},{12,0,1005},{12,0, -1006},{12,0,1007},{12,0,1008},{12,0,1009},{12,0,1010},{12,0,1011},{12,0,1012},{ -12,0,1014},{12,0,1015},{12,0,1016},{12,0,1017},{12,0,1018},{12,0,1019},{12,0, -1022},{12,0,1023},{14,0,475},{14,0,477},{14,0,478},{14,0,479},{14,0,480},{14,0, -482},{14,0,483},{14,0,484},{14,0,485},{14,0,486},{14,0,487},{14,0,488},{14,0,489 -},{14,0,490},{14,0,491},{14,0,492},{14,0,493},{14,0,494},{14,0,495},{14,0,496},{ -14,0,497},{14,0,498},{14,0,499},{14,0,500},{14,0,501},{14,0,502},{14,0,503},{14, -0,504},{14,0,506},{14,0,507},{14,0,508},{14,0,509},{14,0,510},{14,0,511},{16,0, -113},{16,0,114},{16,0,115},{16,0,117},{16,0,118},{16,0,119},{16,0,121},{16,0,122 -},{16,0,123},{16,0,124},{16,0,125},{16,0,126},{16,0,127},{18,0,242},{18,0,243},{ -18,0,244},{18,0,245},{18,0,248},{18,0,249},{18,0,250},{18,0,251},{18,0,252},{18, -0,253},{18,0,254},{18,0,255},{20,0,125},{20,0,126},{148,0,127},{7,11,1717},{7,11 -,1769},{138,11,546},{7,11,1127},{7,11,1572},{10,11,297},{10,11,422},{11,11,764}, -{11,11,810},{12,11,264},{13,11,102},{13,11,300},{13,11,484},{14,11,147},{14,11, -229},{17,11,71},{18,11,118},{147,11,120},{6,0,1148},{134,0,1586},{132,0,775},{ -135,10,954},{133,11,864},{133,11,928},{138,11,189},{135,10,1958},{6,10,549},{8, -10,34},{8,10,283},{9,10,165},{138,10,475},{5,10,652},{5,10,701},{135,10,449},{ -135,11,695},{4,10,655},{7,10,850},{17,10,75},{146,10,137},{140,11,682},{133,11, -523},{8,0,970},{136,10,670},{136,11,555},{7,11,76},{8,11,44},{9,11,884},{10,11, -580},{11,11,399},{11,11,894},{15,11,122},{18,11,144},{147,11,61},{6,10,159},{6, -10,364},{7,10,516},{7,10,1439},{137,10,518},{4,0,71},{5,0,376},{7,0,119},{138,0, -665},{141,10,151},{11,0,827},{14,0,34},{143,0,148},{133,11,518},{4,0,479},{135, -11,1787},{135,11,1852},{135,10,993},{7,0,607},{136,0,99},{134,0,1960},{132,0,793 -},{4,0,41},{5,0,74},{7,0,1627},{11,0,871},{140,0,619},{7,0,94},{11,0,329},{11,0, -965},{12,0,241},{14,0,354},{15,0,22},{148,0,63},{7,10,501},{9,10,111},{10,10,141 -},{11,10,332},{13,10,43},{13,10,429},{14,10,130},{14,10,415},{145,10,102},{9,0, -209},{137,0,300},{134,0,1497},{138,11,255},{4,11,934},{5,11,138},{136,11,610},{ -133,0,98},{6,0,1316},{10,11,804},{138,11,832},{8,11,96},{9,11,36},{10,11,607},{ -11,11,423},{11,11,442},{12,11,309},{14,11,199},{15,11,90},{145,11,110},{132,0, -463},{5,10,149},{136,10,233},{133,10,935},{4,11,652},{8,11,320},{9,11,13},{9,11, -398},{9,11,727},{10,11,75},{10,11,184},{10,11,230},{10,11,564},{10,11,569},{11, -11,973},{12,11,70},{12,11,189},{13,11,57},{13,11,257},{22,11,6},{150,11,16},{142 -,0,291},{12,10,582},{146,10,131},{136,10,801},{133,0,984},{145,11,116},{4,11,692 -},{133,11,321},{4,0,182},{6,0,205},{135,0,220},{4,0,42},{9,0,205},{9,0,786},{138 -,0,659},{6,0,801},{11,11,130},{140,11,609},{132,0,635},{5,11,345},{135,11,1016}, -{139,0,533},{132,0,371},{4,0,272},{135,0,836},{6,0,1282},{135,11,1100},{5,0,825} -,{134,0,1640},{135,11,1325},{133,11,673},{4,11,287},{133,11,1018},{135,0,357},{6 -,0,467},{137,0,879},{7,0,317},{135,0,569},{6,0,924},{134,0,1588},{5,11,34},{5,10 -,406},{10,11,724},{12,11,444},{13,11,354},{18,11,32},{23,11,24},{23,11,31},{152, -11,5},{6,0,1795},{6,0,1835},{6,0,1836},{6,0,1856},{8,0,844},{8,0,849},{8,0,854}, -{8,0,870},{8,0,887},{10,0,852},{138,0,942},{6,10,69},{135,10,117},{137,0,307},{4 -,0,944},{6,0,1799},{6,0,1825},{10,0,848},{10,0,875},{10,0,895},{10,0,899},{10,0, -902},{140,0,773},{11,0,43},{13,0,72},{141,0,142},{135,10,1830},{134,11,382},{4, -10,432},{135,10,824},{132,11,329},{7,0,1820},{139,11,124},{133,10,826},{133,0, -525},{132,11,906},{7,11,1940},{136,11,366},{138,11,10},{4,11,123},{4,11,649},{5, -11,605},{7,11,1509},{136,11,36},{6,0,110},{135,0,1681},{133,0,493},{133,11,767}, -{4,0,174},{135,0,911},{138,11,786},{8,0,417},{137,0,782},{133,10,1000},{7,0,733} -,{137,0,583},{4,10,297},{6,10,529},{7,10,152},{7,10,713},{7,10,1845},{8,10,710}, -{8,10,717},{12,10,639},{140,10,685},{4,0,32},{5,0,215},{6,0,269},{7,0,1782},{7,0 -,1892},{10,0,16},{11,0,822},{11,0,954},{141,0,481},{4,11,273},{5,11,658},{133,11 -,995},{136,0,477},{134,11,72},{135,11,1345},{5,0,308},{7,0,1088},{4,10,520},{135 -,10,575},{133,11,589},{5,0,126},{8,0,297},{9,0,366},{140,0,374},{7,0,1551},{139, -0,361},{5,11,117},{6,11,514},{6,11,541},{7,11,1164},{7,11,1436},{8,11,220},{8,11 -,648},{10,11,688},{139,11,560},{133,11,686},{4,0,946},{6,0,1807},{8,0,871},{10,0 -,854},{10,0,870},{10,0,888},{10,0,897},{10,0,920},{12,0,722},{12,0,761},{12,0, -763},{12,0,764},{14,0,454},{14,0,465},{16,0,107},{18,0,167},{18,0,168},{146,0, -172},{132,0,175},{135,0,1307},{132,0,685},{135,11,1834},{133,0,797},{6,0,745},{6 -,0,858},{134,0,963},{133,0,565},{5,10,397},{6,10,154},{7,11,196},{7,10,676},{8, -10,443},{8,10,609},{9,10,24},{9,10,325},{10,10,35},{10,11,765},{11,11,347},{11, -10,535},{11,11,552},{11,11,576},{11,10,672},{11,11,790},{11,10,1018},{12,11,263} -,{12,10,637},{13,11,246},{13,11,270},{13,11,395},{14,11,74},{14,11,176},{14,11, -190},{14,11,398},{14,11,412},{15,11,32},{15,11,63},{16,10,30},{16,11,88},{147,11 -,105},{13,11,84},{141,11,122},{4,0,252},{7,0,1068},{10,0,434},{11,0,228},{11,0, -426},{13,0,231},{18,0,106},{148,0,87},{137,0,826},{4,11,589},{139,11,282},{5,11, -381},{135,11,1792},{132,0,791},{5,0,231},{10,0,509},{133,10,981},{7,0,601},{9,0, -277},{9,0,674},{10,0,178},{10,0,418},{10,0,571},{11,0,531},{12,0,113},{12,0,475} -,{13,0,99},{142,0,428},{4,10,56},{7,11,616},{7,10,1791},{8,10,607},{8,10,651},{ -10,11,413},{11,10,465},{11,10,835},{12,10,337},{141,10,480},{7,0,1591},{144,0,43 -},{9,10,158},{138,10,411},{135,0,1683},{8,0,289},{11,0,45},{12,0,278},{140,0,537 -},{6,11,120},{7,11,1188},{7,11,1710},{8,11,286},{9,11,667},{11,11,592},{139,11, -730},{136,10,617},{135,0,1120},{135,11,1146},{139,10,563},{4,11,352},{4,10,369}, -{135,11,687},{143,11,38},{4,0,399},{5,0,119},{5,0,494},{7,0,751},{9,0,556},{14, -11,179},{15,11,151},{150,11,11},{4,11,192},{5,11,49},{6,11,200},{6,11,293},{6,11 -,1696},{135,11,488},{4,0,398},{133,0,660},{7,0,1030},{134,10,622},{135,11,595},{ -141,0,168},{132,11,147},{7,0,973},{10,10,624},{142,10,279},{132,10,363},{132,0, -642},{133,11,934},{134,0,1615},{7,11,505},{135,11,523},{7,0,594},{7,0,851},{7,0, -1858},{9,0,411},{9,0,574},{9,0,666},{9,0,737},{10,0,346},{10,0,712},{11,0,246},{ -11,0,432},{11,0,517},{11,0,647},{11,0,679},{11,0,727},{12,0,304},{12,0,305},{12, -0,323},{12,0,483},{12,0,572},{12,0,593},{12,0,602},{13,0,95},{13,0,101},{13,0, -171},{13,0,315},{13,0,378},{13,0,425},{13,0,475},{14,0,63},{14,0,380},{14,0,384} -,{15,0,133},{18,0,112},{148,0,72},{135,0,1093},{132,0,679},{8,0,913},{10,0,903}, -{10,0,915},{12,0,648},{12,0,649},{14,0,455},{16,0,112},{138,11,438},{137,0,203}, -{134,10,292},{134,0,1492},{7,0,1374},{8,0,540},{5,10,177},{6,10,616},{7,10,827}, -{9,10,525},{138,10,656},{135,0,1486},{9,0,714},{138,10,31},{136,0,825},{134,0, -1511},{132,11,637},{134,0,952},{4,10,161},{133,10,631},{5,0,143},{5,0,769},{6,0, -1760},{7,0,682},{7,0,1992},{136,0,736},{132,0,700},{134,0,1540},{132,11,777},{9, -11,867},{138,11,837},{7,0,1557},{135,10,1684},{133,0,860},{6,0,422},{7,0,0},{7,0 -,1544},{9,0,605},{11,0,990},{12,0,235},{12,0,453},{13,0,47},{13,0,266},{9,10,469 -},{9,10,709},{12,10,512},{14,10,65},{145,10,12},{11,0,807},{10,10,229},{11,10,73 -},{139,10,376},{6,11,170},{7,11,1080},{8,11,395},{8,11,487},{11,11,125},{141,11, -147},{5,0,515},{137,0,131},{7,0,1605},{11,0,962},{146,0,139},{132,0,646},{4,0, -396},{7,0,728},{9,0,117},{13,0,202},{148,0,51},{6,0,121},{6,0,124},{6,0,357},{7, -0,1138},{7,0,1295},{8,0,162},{8,0,508},{11,0,655},{4,11,535},{6,10,558},{7,10, -651},{8,11,618},{9,10,0},{10,10,34},{139,10,1008},{135,11,1245},{138,0,357},{150 -,11,23},{133,0,237},{135,0,1784},{7,10,1832},{138,10,374},{132,0,713},{132,11,46 -},{6,0,1536},{10,0,348},{5,11,811},{6,11,1679},{6,11,1714},{135,11,2032},{11,11, -182},{142,11,195},{6,0,523},{7,0,738},{7,10,771},{7,10,1731},{9,10,405},{138,10, -421},{7,11,1458},{9,11,407},{139,11,15},{6,11,34},{7,11,69},{7,11,640},{7,11, -1089},{8,11,708},{8,11,721},{9,11,363},{9,11,643},{10,11,628},{148,11,98},{133,0 -,434},{135,0,1877},{7,0,571},{138,0,366},{5,10,881},{133,10,885},{9,0,513},{10,0 -,25},{10,0,39},{12,0,122},{140,0,187},{132,0,580},{5,10,142},{134,10,546},{132, -11,462},{137,0,873},{5,10,466},{11,10,571},{12,10,198},{13,10,283},{14,10,186},{ -15,10,21},{143,10,103},{7,0,171},{4,10,185},{5,10,257},{5,10,839},{5,10,936},{9, -10,399},{10,10,258},{10,10,395},{10,10,734},{11,10,1014},{12,10,23},{13,10,350}, -{14,10,150},{147,10,6},{134,0,625},{7,0,107},{7,0,838},{8,0,550},{138,0,401},{5, -11,73},{6,11,23},{134,11,338},{4,0,943},{6,0,1850},{12,0,713},{142,0,434},{11,0, -588},{11,0,864},{11,0,936},{11,0,968},{12,0,73},{12,0,343},{12,0,394},{13,0,275} -,{14,0,257},{15,0,160},{7,10,404},{7,10,1377},{7,10,1430},{7,10,2017},{8,10,149} -,{8,10,239},{8,10,512},{8,10,793},{8,10,818},{9,10,474},{9,10,595},{10,10,122},{ -10,10,565},{10,10,649},{10,10,783},{11,10,239},{11,10,295},{11,10,447},{11,10, -528},{11,10,639},{11,10,800},{12,10,25},{12,10,157},{12,10,316},{12,10,390},{12, -10,391},{12,10,395},{12,10,478},{12,10,503},{12,10,592},{12,10,680},{13,10,50},{ -13,10,53},{13,10,132},{13,10,198},{13,10,322},{13,10,415},{13,10,511},{14,10,71} -,{14,10,395},{15,10,71},{15,10,136},{17,10,123},{18,10,93},{147,10,58},{133,0, -768},{11,0,103},{142,0,0},{136,10,712},{132,0,799},{132,0,894},{7,11,725},{8,11, -498},{139,11,268},{135,11,1798},{135,11,773},{141,11,360},{4,10,377},{152,10,13} -,{135,0,1673},{132,11,583},{134,0,1052},{133,11,220},{140,11,69},{132,11,544},{4 -,10,180},{135,10,1906},{134,0,272},{4,0,441},{134,0,1421},{4,0,9},{5,0,128},{7,0 -,368},{11,0,480},{148,0,3},{5,11,176},{6,11,437},{6,11,564},{11,11,181},{141,11, -183},{132,10,491},{7,0,1182},{141,11,67},{6,0,1346},{4,10,171},{138,10,234},{4, -10,586},{7,10,1186},{138,10,631},{136,0,682},{134,0,1004},{15,0,24},{143,11,24}, -{134,0,968},{4,0,2},{6,0,742},{6,0,793},{7,0,545},{7,0,894},{9,10,931},{10,10, -334},{148,10,71},{136,11,600},{133,10,765},{9,0,769},{140,0,185},{4,11,790},{5, -11,273},{134,11,394},{7,0,474},{137,0,578},{4,11,135},{6,11,127},{7,11,1185},{7, -11,1511},{8,11,613},{11,11,5},{12,11,133},{12,11,495},{12,11,586},{14,11,385},{ -15,11,118},{17,11,20},{146,11,98},{133,10,424},{5,0,530},{142,0,113},{6,11,230}, -{7,11,961},{7,11,1085},{136,11,462},{7,11,1954},{137,11,636},{136,10,714},{149, -11,6},{135,10,685},{9,10,420},{10,10,269},{10,10,285},{10,10,576},{11,10,397},{ -13,10,175},{145,10,90},{132,10,429},{5,0,556},{5,11,162},{136,11,68},{132,11,654 -},{4,11,156},{7,11,998},{7,11,1045},{7,11,1860},{9,11,48},{9,11,692},{11,11,419} -,{139,11,602},{6,0,1317},{8,0,16},{9,0,825},{12,0,568},{7,11,1276},{8,11,474},{ -137,11,652},{18,0,97},{7,10,18},{7,10,699},{7,10,1966},{8,10,752},{9,10,273},{9, -10,412},{9,10,703},{10,10,71},{10,10,427},{138,10,508},{10,0,703},{7,11,1454},{ -138,11,703},{4,10,53},{5,10,186},{135,10,752},{134,0,892},{134,0,1571},{8,10,575 -},{10,10,289},{139,10,319},{6,0,186},{137,0,426},{134,0,1101},{132,10,675},{132, -0,585},{6,0,1870},{137,0,937},{152,11,10},{9,11,197},{10,11,300},{12,11,473},{13 -,11,90},{141,11,405},{4,0,93},{5,0,252},{6,0,229},{7,0,291},{9,0,550},{139,0,644 -},{137,0,749},{9,0,162},{6,10,209},{8,10,468},{9,10,210},{11,10,36},{12,10,28},{ -12,10,630},{13,10,21},{13,10,349},{14,10,7},{145,10,13},{132,0,381},{132,11,606} -,{4,10,342},{135,10,1179},{7,11,1587},{7,11,1707},{10,11,528},{139,11,504},{12, -11,39},{13,11,265},{141,11,439},{4,10,928},{133,10,910},{7,10,1838},{7,11,1978}, -{136,11,676},{6,0,762},{6,0,796},{134,0,956},{4,10,318},{4,10,496},{7,10,856},{ -139,10,654},{137,11,242},{4,11,361},{133,11,315},{132,11,461},{132,11,472},{132, -0,857},{5,0,21},{6,0,77},{6,0,157},{7,0,974},{7,0,1301},{7,0,1339},{7,0,1490},{7 -,0,1873},{9,0,628},{7,10,915},{8,10,247},{147,10,0},{4,10,202},{5,10,382},{6,10, -454},{7,10,936},{7,10,1803},{8,10,758},{9,10,375},{9,10,895},{10,10,743},{10,10, -792},{11,10,978},{11,10,1012},{142,10,109},{7,11,617},{10,11,498},{11,11,501},{ -12,11,16},{140,11,150},{7,10,1150},{7,10,1425},{7,10,1453},{10,11,747},{140,10, -513},{133,11,155},{11,0,919},{141,0,409},{138,10,791},{10,0,633},{139,11,729},{7 -,11,163},{8,11,319},{9,11,402},{10,11,24},{10,11,681},{11,11,200},{11,11,567},{ -12,11,253},{12,11,410},{142,11,219},{5,11,475},{7,11,1780},{9,11,230},{11,11,297 -},{11,11,558},{14,11,322},{147,11,76},{7,0,332},{6,10,445},{137,10,909},{135,11, -1956},{136,11,274},{134,10,578},{135,0,1489},{135,11,1848},{5,11,944},{134,11, -1769},{132,11,144},{136,10,766},{4,0,832},{135,10,541},{8,0,398},{9,0,681},{139, -0,632},{136,0,645},{9,0,791},{10,0,93},{16,0,13},{17,0,23},{18,0,135},{19,0,12}, -{20,0,1},{20,0,12},{148,0,14},{6,11,247},{137,11,555},{134,0,20},{132,0,800},{ -135,0,1841},{139,10,983},{137,10,768},{132,10,584},{141,11,51},{6,0,1993},{4,11, -620},{138,11,280},{136,0,769},{11,0,290},{11,0,665},{7,11,1810},{11,11,866},{12, -11,103},{13,11,495},{17,11,67},{147,11,74},{134,0,1426},{139,0,60},{4,10,326},{ -135,10,1770},{7,0,1874},{9,0,641},{132,10,226},{6,0,644},{5,10,426},{8,10,30},{9 -,10,2},{11,10,549},{147,10,122},{5,11,428},{138,11,442},{135,11,1871},{135,0, -1757},{147,10,117},{135,0,937},{135,0,1652},{6,0,654},{134,0,1476},{133,11,99},{ -135,0,527},{132,10,345},{4,10,385},{4,11,397},{7,10,265},{135,10,587},{4,0,579}, -{5,0,226},{5,0,323},{135,0,960},{134,0,1486},{8,11,502},{144,11,9},{4,10,347},{5 -,10,423},{5,10,996},{135,10,1329},{7,11,727},{146,11,73},{4,11,485},{7,11,353},{ -7,10,1259},{7,11,1523},{9,10,125},{139,10,65},{6,0,325},{5,10,136},{6,11,366},{7 -,11,1384},{7,11,1601},{136,10,644},{138,11,160},{6,0,1345},{137,11,282},{18,0,91 -},{147,0,70},{136,0,404},{4,11,157},{133,11,471},{133,0,973},{6,0,135},{135,0, -1176},{8,11,116},{11,11,551},{142,11,159},{4,0,549},{4,10,433},{133,10,719},{136 -,0,976},{5,11,160},{7,11,363},{7,11,589},{10,11,170},{141,11,55},{144,0,21},{144 -,0,51},{135,0,314},{135,10,1363},{4,11,108},{7,11,405},{10,11,491},{139,11,498}, -{146,0,4},{4,10,555},{8,10,536},{10,10,288},{139,10,1005},{135,11,1005},{6,0,281 -},{7,0,6},{8,0,282},{8,0,480},{8,0,499},{9,0,198},{10,0,143},{10,0,169},{10,0, -211},{10,0,417},{10,0,574},{11,0,147},{11,0,395},{12,0,75},{12,0,407},{12,0,608} -,{13,0,500},{142,0,251},{6,0,1093},{6,0,1405},{9,10,370},{138,10,90},{4,11,926}, -{133,11,983},{135,0,1776},{134,0,1528},{132,0,419},{132,11,538},{6,11,294},{7,11 -,1267},{136,11,624},{135,11,1772},{138,11,301},{4,10,257},{135,10,2031},{4,0,138 -},{7,0,1012},{7,0,1280},{9,0,76},{135,10,1768},{132,11,757},{5,0,29},{140,0,638} -,{7,11,655},{135,11,1844},{7,0,1418},{6,11,257},{135,11,1522},{8,11,469},{138,11 -,47},{142,11,278},{6,10,83},{6,10,1733},{135,10,1389},{11,11,204},{11,11,243},{ -140,11,293},{135,11,1875},{6,0,1710},{135,0,2038},{137,11,299},{4,0,17},{5,0,23} -,{7,0,995},{11,0,383},{11,0,437},{12,0,460},{140,0,532},{133,0,862},{137,10,696} -,{6,0,592},{138,0,946},{138,11,599},{7,10,1718},{9,10,95},{9,10,274},{10,10,279} -,{10,10,317},{10,10,420},{11,10,303},{11,10,808},{12,10,134},{12,10,367},{13,10, -149},{13,10,347},{14,10,349},{14,10,406},{18,10,22},{18,10,89},{18,10,122},{147, -10,47},{8,0,70},{12,0,171},{141,0,272},{133,10,26},{132,10,550},{137,0,812},{10, -0,233},{139,0,76},{134,0,988},{134,0,442},{136,10,822},{7,0,896},{4,10,902},{5, -10,809},{134,10,122},{5,11,150},{7,11,106},{8,11,603},{9,11,593},{9,11,634},{10, -11,44},{10,11,173},{11,11,462},{11,11,515},{13,11,216},{13,11,288},{142,11,400}, -{136,0,483},{135,10,262},{6,0,1709},{133,10,620},{4,10,34},{5,10,574},{7,10,279} -,{7,10,1624},{136,10,601},{137,10,170},{147,0,119},{12,11,108},{141,11,291},{11, -0,69},{12,0,105},{12,0,117},{13,0,213},{14,0,13},{14,0,62},{14,0,177},{14,0,421} -,{15,0,19},{146,0,141},{137,0,309},{11,11,278},{142,11,73},{7,0,608},{7,0,976},{ -9,0,146},{10,0,206},{10,0,596},{13,0,218},{142,0,153},{133,10,332},{6,10,261},{8 -,10,182},{139,10,943},{4,11,493},{144,11,55},{134,10,1721},{132,0,768},{4,10,933 -},{133,10,880},{7,11,555},{7,11,1316},{7,11,1412},{7,11,1839},{9,11,192},{9,11, -589},{11,11,241},{11,11,676},{11,11,811},{11,11,891},{12,11,140},{12,11,346},{12 -,11,479},{13,11,30},{13,11,49},{13,11,381},{14,11,188},{15,11,150},{16,11,76},{ -18,11,30},{148,11,52},{4,0,518},{135,0,1136},{6,11,568},{7,11,112},{7,11,1804},{ -8,11,362},{8,11,410},{8,11,830},{9,11,514},{11,11,649},{142,11,157},{135,11,673} -,{8,0,689},{137,0,863},{4,0,18},{7,0,145},{7,0,444},{7,0,1278},{8,0,49},{8,0,400 -},{9,0,71},{9,0,250},{10,0,459},{12,0,160},{16,0,24},{132,11,625},{140,0,1020},{ -4,0,997},{6,0,1946},{6,0,1984},{134,0,1998},{6,11,16},{6,11,158},{7,11,43},{7,11 -,129},{7,11,181},{8,11,276},{8,11,377},{10,11,523},{11,11,816},{12,11,455},{13, -11,303},{142,11,135},{133,10,812},{134,0,658},{4,11,1},{7,11,1143},{7,11,1463},{ -8,11,61},{9,11,207},{9,11,390},{9,11,467},{139,11,836},{150,11,26},{140,0,106},{ -6,0,1827},{10,0,931},{18,0,166},{20,0,114},{4,10,137},{7,10,1178},{7,11,1319},{ -135,10,1520},{133,0,1010},{4,11,723},{5,11,895},{7,11,1031},{8,11,199},{8,11,340 -},{9,11,153},{9,11,215},{10,11,21},{10,11,59},{10,11,80},{10,11,224},{11,11,229} -,{11,11,652},{12,11,192},{13,11,146},{142,11,91},{132,11,295},{6,11,619},{7,11, -898},{7,11,1092},{8,11,485},{18,11,28},{147,11,116},{137,11,51},{6,10,1661},{7, -10,1975},{7,10,2009},{135,10,2011},{5,11,309},{140,11,211},{5,0,87},{7,0,313},{7 -,0,1103},{10,0,208},{10,0,582},{11,0,389},{11,0,813},{12,0,385},{13,0,286},{14,0 -,124},{146,0,108},{5,11,125},{8,11,77},{138,11,15},{132,0,267},{133,0,703},{137, -11,155},{133,11,439},{11,11,164},{140,11,76},{9,0,496},{5,10,89},{7,10,1915},{9, -10,185},{9,10,235},{10,10,64},{10,10,270},{10,10,403},{10,10,469},{10,10,529},{ -10,10,590},{11,10,140},{11,10,860},{13,10,1},{13,10,422},{14,10,341},{14,10,364} -,{17,10,93},{18,10,113},{19,10,97},{147,10,113},{133,10,695},{135,0,1121},{5,10, -6},{6,10,183},{7,10,680},{7,10,978},{7,10,1013},{7,10,1055},{12,10,230},{13,10, -172},{146,10,29},{4,11,8},{7,11,1152},{7,11,1153},{7,11,1715},{9,11,374},{10,11, -478},{139,11,648},{135,11,1099},{6,10,29},{139,10,63},{4,0,561},{10,0,249},{139, -0,209},{132,0,760},{7,11,799},{138,11,511},{136,11,87},{9,0,154},{140,0,485},{ -136,0,255},{132,0,323},{140,0,419},{132,10,311},{134,10,1740},{4,0,368},{135,0, -641},{7,10,170},{8,10,90},{8,10,177},{8,10,415},{11,10,714},{142,10,281},{4,11, -69},{5,11,122},{9,11,656},{138,11,464},{5,11,849},{134,11,1633},{8,0,522},{142,0 -,328},{11,10,91},{13,10,129},{15,10,101},{145,10,125},{7,0,562},{8,0,551},{4,10, -494},{6,10,74},{7,10,44},{11,11,499},{12,10,17},{15,10,5},{148,10,11},{4,10,276} -,{133,10,296},{9,0,92},{147,0,91},{4,10,7},{5,10,90},{5,10,158},{6,10,542},{7,10 -,221},{7,10,1574},{9,10,490},{10,10,540},{11,10,443},{139,10,757},{6,0,525},{6,0 -,1976},{8,0,806},{9,0,876},{140,0,284},{5,11,859},{7,10,588},{7,11,1160},{8,11, -107},{9,10,175},{9,11,291},{9,11,439},{10,10,530},{10,11,663},{11,11,609},{140, -11,197},{7,11,168},{13,11,196},{141,11,237},{139,0,958},{133,0,594},{135,10,580} -,{7,10,88},{136,10,627},{6,0,479},{6,0,562},{7,0,1060},{13,0,6},{5,10,872},{6,10 -,57},{7,10,471},{9,10,447},{137,10,454},{136,11,413},{145,11,19},{4,11,117},{6, -11,372},{7,11,1905},{142,11,323},{4,11,722},{139,11,471},{17,0,61},{5,10,31},{ -134,10,614},{8,10,330},{140,10,477},{7,10,1200},{138,10,460},{6,10,424},{135,10, -1866},{6,0,1641},{136,0,820},{6,0,1556},{134,0,1618},{9,11,5},{12,11,216},{12,11 -,294},{12,11,298},{12,11,400},{12,11,518},{13,11,229},{143,11,139},{15,11,155},{ -144,11,79},{4,0,302},{135,0,1766},{5,10,13},{134,10,142},{6,0,148},{7,0,1313},{7 -,10,116},{8,10,322},{8,10,755},{9,10,548},{10,10,714},{11,10,884},{141,10,324},{ -137,0,676},{9,11,88},{139,11,270},{5,11,12},{7,11,375},{137,11,438},{134,0,1674} -,{7,10,1472},{135,10,1554},{11,0,178},{7,10,1071},{7,10,1541},{7,10,1767},{7,10, -1806},{11,10,162},{11,10,242},{12,10,605},{15,10,26},{144,10,44},{6,0,389},{7,0, -149},{9,0,142},{138,0,94},{140,11,71},{145,10,115},{6,0,8},{7,0,1881},{8,0,91},{ -11,11,966},{12,11,287},{13,11,342},{13,11,402},{15,11,110},{143,11,163},{4,11, -258},{136,11,639},{6,11,22},{7,11,903},{138,11,577},{133,11,681},{135,10,1111},{ -135,11,1286},{9,0,112},{8,10,1},{138,10,326},{5,10,488},{6,10,527},{7,10,489},{7 -,10,1636},{8,10,121},{8,10,144},{8,10,359},{9,10,193},{9,10,241},{9,10,336},{9, -10,882},{11,10,266},{11,10,372},{11,10,944},{12,10,401},{140,10,641},{4,11,664}, -{133,11,804},{6,0,747},{134,0,1015},{135,0,1746},{9,10,31},{10,10,244},{10,10, -699},{12,10,149},{141,10,497},{133,10,377},{135,0,24},{6,0,1352},{5,11,32},{145, -10,101},{7,0,1530},{10,0,158},{13,0,13},{13,0,137},{13,0,258},{14,0,111},{14,0, -225},{14,0,253},{14,0,304},{14,0,339},{14,0,417},{146,0,33},{4,0,503},{135,0, -1661},{5,0,130},{6,0,845},{7,0,1314},{9,0,610},{10,0,718},{11,0,601},{11,0,819}, -{11,0,946},{140,0,536},{10,0,149},{11,0,280},{142,0,336},{134,0,1401},{135,0, -1946},{8,0,663},{144,0,8},{134,0,1607},{135,10,2023},{4,11,289},{7,11,629},{7,11 -,1698},{7,11,1711},{140,11,215},{6,11,450},{136,11,109},{10,0,882},{10,0,883},{ -10,0,914},{138,0,928},{133,10,843},{136,11,705},{132,10,554},{133,10,536},{5,0, -417},{9,10,79},{11,10,625},{145,10,7},{7,11,1238},{142,11,37},{4,0,392},{135,0, -1597},{5,0,433},{9,0,633},{11,0,629},{132,10,424},{7,10,336},{136,10,785},{134, -11,355},{6,0,234},{7,0,769},{9,0,18},{138,0,358},{4,10,896},{134,10,1777},{138, -11,323},{7,0,140},{7,0,1950},{8,0,680},{11,0,817},{147,0,88},{7,0,1222},{138,0, -386},{139,11,908},{11,0,249},{12,0,313},{16,0,66},{145,0,26},{134,0,5},{7,10,750 -},{9,10,223},{11,10,27},{11,10,466},{12,10,624},{14,10,265},{146,10,61},{134,11, -26},{134,0,1216},{5,0,963},{134,0,1773},{4,11,414},{5,11,467},{9,11,654},{10,11, -451},{12,11,59},{141,11,375},{135,11,17},{4,10,603},{133,10,661},{4,10,11},{6,10 -,128},{7,10,231},{7,10,1533},{138,10,725},{135,11,955},{7,0,180},{8,0,509},{136, -0,792},{132,10,476},{132,0,1002},{133,11,538},{135,10,1807},{132,0,931},{7,0,943 -},{11,0,614},{140,0,747},{135,0,1837},{9,10,20},{10,10,324},{10,10,807},{139,10, -488},{134,0,641},{6,11,280},{10,11,502},{11,11,344},{140,11,38},{5,11,45},{7,11, -1161},{11,11,448},{11,11,880},{13,11,139},{13,11,407},{15,11,16},{17,11,95},{18, -11,66},{18,11,88},{18,11,123},{149,11,7},{9,0,280},{138,0,134},{22,0,22},{23,0,5 -},{151,0,29},{136,11,777},{4,0,90},{5,0,545},{7,0,754},{9,0,186},{10,0,72},{10,0 -,782},{11,0,577},{11,0,610},{11,0,960},{12,0,354},{12,0,362},{12,0,595},{4,11, -410},{135,11,521},{135,11,1778},{5,10,112},{6,10,103},{134,10,150},{138,10,356}, -{132,0,742},{7,0,151},{9,0,329},{139,0,254},{8,0,853},{8,0,881},{8,0,911},{8,0, -912},{10,0,872},{12,0,741},{12,0,742},{152,0,18},{4,11,573},{136,11,655},{6,0, -921},{134,0,934},{9,0,187},{10,0,36},{11,0,1016},{17,0,44},{146,0,64},{7,0,833}, -{136,0,517},{4,0,506},{5,0,295},{135,0,1680},{4,10,708},{8,10,15},{9,10,50},{9, -10,386},{11,10,18},{11,10,529},{140,10,228},{7,0,251},{7,0,1701},{8,0,436},{4,10 -,563},{7,10,592},{7,10,637},{7,10,770},{8,10,463},{9,10,60},{9,10,335},{9,10,904 -},{10,10,73},{11,10,434},{12,10,585},{13,10,331},{18,10,110},{148,10,60},{132,10 -,502},{136,0,584},{6,10,347},{138,10,161},{7,0,987},{9,0,688},{10,0,522},{11,0, -788},{12,0,137},{12,0,566},{14,0,9},{14,0,24},{14,0,64},{7,11,899},{142,11,325}, -{4,0,214},{5,0,500},{5,10,102},{6,10,284},{7,10,1079},{7,10,1423},{7,10,1702},{8 -,10,470},{9,10,554},{9,10,723},{139,10,333},{7,10,246},{135,10,840},{6,10,10},{8 -,10,571},{9,10,739},{143,10,91},{133,10,626},{146,0,195},{134,0,1775},{7,0,389}, -{7,0,700},{7,0,940},{8,0,514},{9,0,116},{9,0,535},{10,0,118},{11,0,107},{11,0, -148},{11,0,922},{12,0,254},{12,0,421},{142,0,238},{5,10,18},{6,10,526},{13,10,24 -},{13,10,110},{19,10,5},{147,10,44},{132,0,743},{11,0,292},{4,10,309},{5,10,462} -,{7,10,970},{135,10,1097},{22,10,30},{150,10,33},{139,11,338},{135,11,1598},{7,0 -,1283},{9,0,227},{11,0,325},{11,0,408},{14,0,180},{146,0,47},{4,0,953},{6,0,1805 -},{6,0,1814},{6,0,1862},{140,0,774},{6,11,611},{135,11,1733},{135,11,1464},{5,0, -81},{7,0,146},{7,0,1342},{8,0,53},{8,0,561},{8,0,694},{8,0,754},{9,0,115},{9,0, -179},{9,0,894},{10,0,462},{10,0,813},{11,0,230},{11,0,657},{11,0,699},{11,0,748} -,{12,0,119},{12,0,200},{12,0,283},{142,0,273},{5,0,408},{6,0,789},{6,0,877},{6,0 -,1253},{6,0,1413},{137,0,747},{134,10,1704},{135,11,663},{6,0,1910},{6,0,1915},{ -6,0,1923},{9,0,913},{9,0,928},{9,0,950},{9,0,954},{9,0,978},{9,0,993},{12,0,812} -,{12,0,819},{12,0,831},{12,0,833},{12,0,838},{12,0,909},{12,0,928},{12,0,931},{ -12,0,950},{15,0,186},{15,0,187},{15,0,195},{15,0,196},{15,0,209},{15,0,215},{15, -0,236},{15,0,241},{15,0,249},{15,0,253},{18,0,180},{18,0,221},{18,0,224},{18,0, -227},{18,0,229},{149,0,60},{7,0,1826},{135,0,1938},{11,0,490},{18,0,143},{5,10, -86},{7,10,743},{9,10,85},{10,10,281},{10,10,432},{12,10,251},{13,10,118},{142,10 -,378},{5,10,524},{133,10,744},{141,11,442},{10,10,107},{140,10,436},{135,11,503} -,{134,0,1162},{132,10,927},{7,0,30},{8,0,86},{8,0,315},{8,0,700},{9,0,576},{9,0, -858},{10,0,414},{11,0,310},{11,0,888},{11,0,904},{12,0,361},{13,0,248},{13,0,371 -},{14,0,142},{12,10,670},{146,10,94},{134,0,721},{4,11,113},{5,11,163},{5,11,735 -},{7,11,1009},{7,10,1149},{9,11,9},{9,10,156},{9,11,771},{12,11,90},{13,11,138}, -{13,11,410},{143,11,128},{138,0,839},{133,10,778},{137,0,617},{133,10,502},{8,10 -,196},{10,10,283},{139,10,406},{6,0,428},{7,0,524},{8,0,169},{8,0,234},{9,0,480} -,{138,0,646},{133,10,855},{134,0,1648},{7,0,1205},{138,0,637},{7,0,1596},{4,11, -935},{133,11,823},{5,11,269},{7,11,434},{7,11,891},{8,11,339},{9,11,702},{11,11, -594},{11,11,718},{145,11,100},{7,11,878},{9,11,485},{141,11,264},{4,0,266},{8,0, -4},{9,0,39},{10,0,166},{11,0,918},{12,0,635},{20,0,10},{22,0,27},{22,0,43},{22,0 -,52},{134,11,1713},{7,10,1400},{9,10,446},{138,10,45},{135,11,900},{132,0,862},{ -134,0,1554},{135,11,1033},{19,0,16},{147,11,16},{135,11,1208},{7,0,157},{136,0, -279},{6,0,604},{136,0,391},{13,10,455},{15,10,99},{15,10,129},{144,10,68},{135, -10,172},{7,0,945},{11,0,713},{139,0,744},{4,0,973},{10,0,877},{10,0,937},{10,0, -938},{140,0,711},{139,0,1022},{132,10,568},{142,11,143},{4,0,567},{9,0,859},{132 -,10,732},{7,0,1846},{136,0,628},{136,10,733},{133,0,762},{4,10,428},{135,10,1789 -},{10,0,784},{13,0,191},{7,10,2015},{140,10,665},{133,0,298},{7,0,633},{7,0,905} -,{7,0,909},{7,0,1538},{9,0,767},{140,0,636},{138,10,806},{132,0,795},{139,0,301} -,{135,0,1970},{5,11,625},{135,11,1617},{135,11,275},{7,11,37},{8,11,425},{8,11, -693},{9,11,720},{10,11,380},{10,11,638},{11,11,273},{11,11,307},{11,11,473},{12, -11,61},{143,11,43},{135,11,198},{134,0,1236},{7,0,369},{12,0,644},{12,0,645},{ -144,0,90},{19,0,15},{149,0,27},{6,0,71},{7,0,845},{8,0,160},{9,0,318},{6,10,1623 -},{134,10,1681},{134,0,1447},{134,0,1255},{138,0,735},{8,0,76},{132,11,168},{6, -10,1748},{8,10,715},{9,10,802},{10,10,46},{10,10,819},{13,10,308},{14,10,351},{ -14,10,363},{146,10,67},{135,11,91},{6,0,474},{4,10,63},{133,10,347},{133,10,749} -,{138,0,841},{133,10,366},{6,0,836},{132,11,225},{135,0,1622},{135,10,89},{140,0 -,735},{134,0,1601},{138,11,145},{6,0,1390},{137,0,804},{142,0,394},{6,11,15},{7, -11,70},{10,11,240},{147,11,93},{6,0,96},{135,0,1426},{4,0,651},{133,0,289},{7,11 -,956},{7,10,977},{7,11,1157},{7,11,1506},{7,11,1606},{7,11,1615},{7,11,1619},{7, -11,1736},{7,11,1775},{8,11,590},{9,11,324},{9,11,736},{9,11,774},{9,11,776},{9, -11,784},{10,11,567},{10,11,708},{11,11,518},{11,11,613},{11,11,695},{11,11,716}, -{11,11,739},{11,11,770},{11,11,771},{11,11,848},{11,11,857},{11,11,931},{11,11, -947},{12,11,326},{12,11,387},{12,11,484},{12,11,528},{12,11,552},{12,11,613},{13 -,11,189},{13,11,256},{13,11,340},{13,11,432},{13,11,436},{13,11,440},{13,11,454} -,{14,11,174},{14,11,220},{14,11,284},{14,11,390},{145,11,121},{7,0,688},{8,0,35} -,{9,0,511},{10,0,767},{147,0,118},{134,0,667},{4,0,513},{5,10,824},{133,10,941}, -{7,10,440},{8,10,230},{139,10,106},{134,0,2034},{135,11,1399},{143,11,66},{135, -11,1529},{4,11,145},{6,11,176},{7,11,395},{9,11,562},{144,11,28},{132,11,501},{ -132,0,704},{134,0,1524},{7,0,1078},{134,11,464},{6,11,509},{10,11,82},{20,11,91} -,{151,11,13},{4,0,720},{133,0,306},{133,0,431},{7,0,1196},{4,10,914},{5,10,800}, -{133,10,852},{135,11,1189},{10,0,54},{141,10,115},{7,10,564},{142,10,168},{5,0, -464},{6,0,236},{7,0,696},{7,0,914},{7,0,1108},{7,0,1448},{9,0,15},{9,0,564},{10, -0,14},{12,0,565},{13,0,449},{14,0,53},{15,0,13},{16,0,64},{17,0,41},{4,10,918},{ -133,10,876},{6,0,1418},{134,10,1764},{4,10,92},{133,10,274},{134,0,907},{4,11, -114},{8,10,501},{9,11,492},{13,11,462},{142,11,215},{4,11,77},{5,11,361},{6,11, -139},{6,11,401},{6,11,404},{7,11,413},{7,11,715},{7,11,1716},{11,11,279},{12,11, -179},{12,11,258},{13,11,244},{142,11,358},{6,0,1767},{12,0,194},{145,0,107},{134 -,11,1717},{5,10,743},{142,11,329},{4,10,49},{7,10,280},{135,10,1633},{5,0,840},{ -7,11,1061},{8,11,82},{11,11,250},{12,11,420},{141,11,184},{135,11,724},{134,0, -900},{136,10,47},{134,0,1436},{144,11,0},{6,0,675},{7,0,1008},{7,0,1560},{9,0, -642},{11,0,236},{14,0,193},{5,10,272},{5,10,908},{5,10,942},{8,10,197},{9,10,47} -,{11,10,538},{139,10,742},{4,0,68},{5,0,628},{5,0,634},{6,0,386},{7,0,794},{8,0, -273},{9,0,563},{10,0,105},{10,0,171},{11,0,94},{139,0,354},{135,10,1911},{137,10 -,891},{4,0,95},{6,0,1297},{6,0,1604},{7,0,416},{139,0,830},{6,11,513},{135,11, -1052},{7,0,731},{13,0,20},{143,0,11},{137,11,899},{10,0,850},{140,0,697},{4,0, -662},{7,11,1417},{12,11,382},{17,11,48},{152,11,12},{133,0,736},{132,0,861},{4, -10,407},{132,10,560},{141,10,490},{6,11,545},{7,11,565},{7,11,1669},{10,11,114}, -{11,11,642},{140,11,618},{6,0,871},{134,0,1000},{5,0,864},{10,0,648},{11,0,671}, -{15,0,46},{133,11,5},{133,0,928},{11,0,90},{13,0,7},{4,10,475},{11,10,35},{13,10 -,71},{13,10,177},{142,10,422},{136,0,332},{135,11,192},{134,0,1055},{136,11,763} -,{11,0,986},{140,0,682},{7,0,76},{8,0,44},{9,0,884},{10,0,580},{11,0,399},{11,0, -894},{143,0,122},{135,11,1237},{135,10,636},{11,0,300},{6,10,222},{7,10,1620},{8 -,10,409},{137,10,693},{4,11,87},{5,11,250},{10,11,601},{13,11,298},{13,11,353},{ -141,11,376},{5,0,518},{10,0,340},{11,0,175},{149,0,16},{140,0,771},{6,0,1108},{ -137,0,831},{132,0,836},{135,0,1852},{4,0,957},{6,0,1804},{8,0,842},{8,0,843},{8, -0,851},{8,0,855},{140,0,767},{135,11,814},{4,11,57},{7,11,1195},{7,11,1438},{7, -11,1548},{7,11,1835},{7,11,1904},{9,11,757},{10,11,604},{139,11,519},{133,10,882 -},{138,0,246},{4,0,934},{5,0,202},{8,0,610},{7,11,1897},{12,11,290},{13,11,80},{ -13,11,437},{145,11,74},{8,0,96},{9,0,36},{10,0,607},{10,0,804},{10,0,832},{11,0, -423},{11,0,442},{12,0,309},{14,0,199},{15,0,90},{145,0,110},{132,10,426},{7,0, -654},{8,0,240},{6,10,58},{7,10,745},{7,10,1969},{8,10,675},{9,10,479},{9,10,731} -,{10,10,330},{10,10,593},{10,10,817},{11,10,32},{11,10,133},{11,10,221},{145,10, -68},{9,0,13},{9,0,398},{9,0,727},{10,0,75},{10,0,184},{10,0,230},{10,0,564},{10, -0,569},{11,0,973},{12,0,70},{12,0,189},{13,0,57},{141,0,257},{4,11,209},{135,11, -902},{7,0,391},{137,10,538},{134,0,403},{6,11,303},{7,11,335},{7,11,1437},{7,11, -1668},{8,11,553},{8,11,652},{8,11,656},{9,11,558},{11,11,743},{149,11,18},{132, -11,559},{11,0,75},{142,0,267},{6,0,815},{141,11,2},{141,0,366},{137,0,631},{133, -11,1017},{5,0,345},{135,0,1016},{133,11,709},{134,11,1745},{133,10,566},{7,0,952 -},{6,10,48},{9,10,139},{10,10,399},{11,10,469},{12,10,634},{141,10,223},{133,0, -673},{9,0,850},{7,11,8},{136,11,206},{6,0,662},{149,0,35},{4,0,287},{133,0,1018} -,{6,10,114},{7,10,1224},{7,10,1556},{136,10,3},{8,10,576},{137,10,267},{4,0,884} -,{5,0,34},{10,0,724},{12,0,444},{13,0,354},{18,0,32},{23,0,24},{23,0,31},{152,0, -5},{133,10,933},{132,11,776},{138,0,151},{136,0,427},{134,0,382},{132,0,329},{9, -0,846},{10,0,827},{138,11,33},{9,0,279},{10,0,407},{14,0,84},{22,0,18},{135,11, -1297},{136,11,406},{132,0,906},{136,0,366},{134,0,843},{134,0,1443},{135,0,1372} -,{138,0,992},{4,0,123},{5,0,605},{7,0,1509},{136,0,36},{132,0,649},{8,11,175},{ -10,11,168},{138,11,573},{133,0,767},{134,0,1018},{135,11,1305},{12,10,30},{13,10 -,148},{14,10,87},{14,10,182},{16,10,42},{148,10,70},{134,11,607},{4,0,273},{5,0, -658},{133,0,995},{6,0,72},{139,11,174},{10,0,483},{12,0,368},{7,10,56},{7,10, -1989},{8,10,337},{8,10,738},{9,10,600},{13,10,447},{142,10,92},{5,11,784},{138, -10,666},{135,0,1345},{139,11,882},{134,0,1293},{133,0,589},{134,0,1988},{5,0,117 -},{6,0,514},{6,0,541},{7,0,1164},{7,0,1436},{8,0,220},{8,0,648},{10,0,688},{139, -0,560},{136,0,379},{5,0,686},{7,10,866},{135,10,1163},{132,10,328},{9,11,14},{9, -11,441},{10,11,306},{139,11,9},{4,10,101},{135,10,1171},{5,10,833},{136,10,744}, -{5,11,161},{7,11,839},{135,11,887},{7,0,196},{10,0,765},{11,0,347},{11,0,552},{ -11,0,790},{12,0,263},{13,0,246},{13,0,270},{13,0,395},{14,0,176},{14,0,190},{14, -0,398},{14,0,412},{15,0,32},{15,0,63},{16,0,88},{147,0,105},{6,10,9},{6,10,397}, -{7,10,53},{7,10,1742},{10,10,632},{11,10,828},{140,10,146},{5,0,381},{135,0,1792 -},{134,0,1452},{135,11,429},{8,0,367},{10,0,760},{14,0,79},{20,0,17},{152,0,0},{ -7,0,616},{138,0,413},{11,10,417},{12,10,223},{140,10,265},{7,11,1611},{13,11,14} -,{15,11,44},{19,11,13},{148,11,76},{135,0,1229},{6,0,120},{7,0,1188},{7,0,1710}, -{8,0,286},{9,0,667},{11,0,592},{139,0,730},{135,11,1814},{135,0,1146},{4,10,186} -,{5,10,157},{8,10,168},{138,10,6},{4,0,352},{135,0,687},{4,0,192},{5,0,49},{6,0, -200},{6,0,293},{6,0,1696},{135,0,1151},{133,10,875},{5,10,773},{5,10,991},{6,10, -1635},{134,10,1788},{7,10,111},{136,10,581},{6,0,935},{134,0,1151},{134,0,1050}, -{132,0,650},{132,0,147},{11,0,194},{12,0,62},{12,0,88},{11,11,194},{12,11,62},{ -140,11,88},{6,0,339},{135,0,923},{134,10,1747},{7,11,643},{136,11,236},{133,0, -934},{7,10,1364},{7,10,1907},{141,10,158},{132,10,659},{4,10,404},{135,10,675},{ -7,11,581},{9,11,644},{137,11,699},{13,0,211},{14,0,133},{14,0,204},{15,0,64},{15 -,0,69},{15,0,114},{16,0,10},{19,0,23},{19,0,35},{19,0,39},{19,0,51},{19,0,71},{ -19,0,75},{152,0,15},{133,10,391},{5,11,54},{135,11,1513},{7,0,222},{8,0,341},{5, -10,540},{134,10,1697},{134,10,78},{132,11,744},{136,0,293},{137,11,701},{7,11, -930},{10,11,402},{10,11,476},{13,11,452},{18,11,55},{147,11,104},{132,0,637},{ -133,10,460},{8,11,50},{137,11,624},{132,11,572},{134,0,1159},{4,10,199},{139,10, -34},{134,0,847},{134,10,388},{6,11,43},{7,11,38},{8,11,248},{9,11,504},{138,11, -513},{9,0,683},{4,10,511},{6,10,608},{9,10,333},{10,10,602},{11,10,441},{11,10, -723},{11,10,976},{140,10,357},{9,0,867},{138,0,837},{6,0,944},{135,11,326},{135, -0,1809},{5,10,938},{7,11,783},{136,10,707},{133,11,766},{133,11,363},{6,0,170},{ -7,0,1080},{8,0,395},{8,0,487},{141,0,147},{6,11,258},{140,11,409},{4,0,535},{8,0 -,618},{5,11,249},{148,11,82},{6,0,1379},{149,11,15},{135,0,1625},{150,0,23},{5, -11,393},{6,11,378},{7,11,1981},{9,11,32},{9,11,591},{10,11,685},{10,11,741},{142 -,11,382},{133,11,788},{7,11,1968},{10,11,19},{139,11,911},{7,11,1401},{135,11, -1476},{4,11,61},{5,11,58},{5,11,171},{5,11,635},{5,11,683},{5,11,700},{6,11,291} -,{6,11,566},{7,11,1650},{11,11,523},{12,11,273},{12,11,303},{15,11,39},{143,11, -111},{6,10,469},{7,10,1709},{138,10,515},{4,0,778},{134,11,589},{132,0,46},{5,0, -811},{6,0,1679},{6,0,1714},{135,0,2032},{7,0,1458},{9,0,407},{11,0,15},{12,0,651 -},{149,0,37},{7,0,938},{132,10,500},{6,0,34},{7,0,69},{7,0,1089},{7,0,1281},{8,0 -,708},{8,0,721},{9,0,363},{148,0,98},{10,11,231},{147,11,124},{7,11,726},{152,11 -,9},{5,10,68},{134,10,383},{136,11,583},{4,11,917},{133,11,1005},{11,10,216},{ -139,10,340},{135,11,1675},{8,0,441},{10,0,314},{143,0,3},{132,11,919},{4,10,337} -,{6,10,353},{7,10,1934},{8,10,488},{137,10,429},{7,0,889},{7,10,1795},{8,10,259} -,{9,10,135},{9,10,177},{9,10,860},{10,10,825},{11,10,115},{11,10,370},{11,10,405 -},{11,10,604},{12,10,10},{12,10,667},{12,10,669},{13,10,76},{14,10,310},{15,10, -76},{15,10,147},{148,10,23},{4,10,15},{4,11,255},{5,10,22},{5,11,302},{6,11,132} -,{6,10,244},{7,10,40},{7,11,128},{7,10,200},{7,11,283},{7,10,906},{7,10,1199},{7 -,11,1299},{9,10,616},{10,11,52},{10,11,514},{10,10,716},{11,10,635},{11,10,801}, -{11,11,925},{12,10,458},{13,11,92},{142,11,309},{132,0,462},{137,11,173},{135,10 -,1735},{8,0,525},{5,10,598},{7,10,791},{8,10,108},{137,10,123},{5,0,73},{6,0,23} -,{134,0,338},{132,0,676},{132,10,683},{7,0,725},{8,0,498},{139,0,268},{12,0,21}, -{151,0,7},{135,0,773},{4,10,155},{135,10,1689},{4,0,164},{5,0,730},{5,10,151},{5 -,10,741},{6,11,210},{7,10,498},{7,10,870},{7,10,1542},{12,10,213},{14,10,36},{14 -,10,391},{17,10,111},{18,10,6},{18,10,46},{18,10,151},{19,10,36},{20,10,32},{20, -10,56},{20,10,69},{20,10,102},{21,10,4},{22,10,8},{22,10,10},{22,10,14},{150,10, -31},{4,10,624},{135,10,1752},{4,0,583},{9,0,936},{15,0,214},{18,0,199},{24,0,26} -,{134,11,588},{7,0,1462},{11,0,659},{4,11,284},{134,11,223},{133,0,220},{139,0, -803},{132,0,544},{4,10,492},{133,10,451},{16,0,98},{148,0,119},{4,11,218},{7,11, -526},{143,11,137},{135,10,835},{4,11,270},{5,11,192},{6,11,332},{7,11,1322},{13, -11,9},{13,10,70},{14,11,104},{142,11,311},{132,10,539},{140,11,661},{5,0,176},{6 -,0,437},{6,0,564},{11,0,181},{141,0,183},{135,0,1192},{6,10,113},{135,10,436},{ -136,10,718},{135,10,520},{135,0,1878},{140,11,196},{7,11,379},{8,11,481},{137,11 -,377},{5,11,1003},{6,11,149},{137,11,746},{8,11,262},{9,11,627},{10,11,18},{11, -11,214},{11,11,404},{11,11,457},{11,11,780},{11,11,849},{11,11,913},{13,11,330}, -{13,11,401},{142,11,200},{149,0,26},{136,11,304},{132,11,142},{135,0,944},{4,0, -790},{5,0,273},{134,0,394},{134,0,855},{4,0,135},{6,0,127},{7,0,1185},{7,0,1511} -,{8,0,613},{11,0,5},{12,0,336},{12,0,495},{12,0,586},{12,0,660},{12,0,668},{14,0 -,385},{15,0,118},{17,0,20},{146,0,98},{6,0,230},{9,0,752},{18,0,109},{12,10,610} -,{13,10,431},{144,10,59},{7,0,1954},{135,11,925},{4,11,471},{5,11,51},{6,11,602} -,{8,11,484},{10,11,195},{140,11,159},{132,10,307},{136,11,688},{132,11,697},{7, -11,812},{7,11,1261},{7,11,1360},{9,11,632},{140,11,352},{5,0,162},{8,0,68},{133, -10,964},{4,0,654},{136,11,212},{4,0,156},{7,0,998},{7,0,1045},{7,0,1860},{9,0,48 -},{9,0,692},{11,0,419},{139,0,602},{133,11,221},{4,11,373},{5,11,283},{6,11,480} -,{135,11,609},{142,11,216},{132,0,240},{6,11,192},{9,11,793},{145,11,55},{4,10, -75},{5,10,180},{6,10,500},{7,10,58},{7,10,710},{138,10,645},{4,11,132},{5,11,69} -,{5,10,649},{135,11,1242},{6,10,276},{7,10,282},{7,10,879},{7,10,924},{8,10,459} -,{9,10,599},{9,10,754},{11,10,574},{12,10,128},{12,10,494},{13,10,52},{13,10,301 -},{15,10,30},{143,10,132},{132,10,200},{4,11,111},{135,11,302},{9,0,197},{10,0, -300},{12,0,473},{13,0,90},{141,0,405},{132,11,767},{6,11,42},{7,11,1416},{7,11, -1590},{7,11,2005},{8,11,131},{8,11,466},{9,11,672},{13,11,252},{148,11,103},{8,0 -,958},{8,0,999},{10,0,963},{138,0,1001},{135,10,1621},{135,0,858},{4,0,606},{137 -,11,444},{6,11,44},{136,11,368},{139,11,172},{4,11,570},{133,11,120},{139,11,624 -},{7,0,1978},{8,0,676},{6,10,225},{137,10,211},{7,0,972},{11,0,102},{136,10,687} -,{6,11,227},{135,11,1589},{8,10,58},{9,10,724},{11,10,809},{13,10,113},{145,10, -72},{4,0,361},{133,0,315},{132,0,461},{6,10,345},{135,10,1247},{132,0,472},{8,10 -,767},{8,10,803},{9,10,301},{137,10,903},{135,11,1333},{135,11,477},{7,10,1949}, -{136,10,674},{6,0,905},{138,0,747},{133,0,155},{134,10,259},{7,0,163},{8,0,319}, -{9,0,402},{10,0,24},{10,0,681},{11,0,200},{12,0,253},{12,0,410},{142,0,219},{5,0 -,475},{7,0,1780},{9,0,230},{11,0,297},{11,0,558},{14,0,322},{19,0,76},{6,11,1667 -},{7,11,2036},{138,11,600},{136,10,254},{6,0,848},{135,0,1956},{6,11,511},{140, -11,132},{5,11,568},{6,11,138},{135,11,1293},{6,0,631},{137,0,838},{149,0,36},{4, -11,565},{8,11,23},{136,11,827},{5,0,944},{134,0,1769},{4,0,144},{6,0,842},{6,0, -1400},{4,11,922},{133,11,1023},{133,10,248},{9,10,800},{10,10,693},{11,10,482},{ -11,10,734},{139,10,789},{7,11,1002},{139,11,145},{4,10,116},{5,10,95},{5,10,445} -,{7,10,1688},{8,10,29},{9,10,272},{11,10,509},{139,10,915},{14,0,369},{146,0,72} -,{135,10,1641},{132,11,740},{133,10,543},{140,11,116},{6,0,247},{9,0,555},{5,10, -181},{136,10,41},{133,10,657},{136,0,996},{138,10,709},{7,0,189},{8,10,202},{138 -,10,536},{136,11,402},{4,11,716},{141,11,31},{10,0,280},{138,0,797},{9,10,423},{ -140,10,89},{8,10,113},{9,10,877},{10,10,554},{11,10,83},{12,10,136},{147,10,109} -,{133,10,976},{7,0,746},{132,10,206},{136,0,526},{139,0,345},{136,0,1017},{8,11, -152},{9,11,53},{9,11,268},{9,11,901},{10,11,518},{10,11,829},{11,11,188},{13,11, -74},{14,11,46},{15,11,17},{15,11,33},{17,11,40},{18,11,36},{19,11,20},{22,11,1}, -{152,11,2},{133,11,736},{136,11,532},{5,0,428},{138,0,651},{135,11,681},{135,0, -1162},{7,0,327},{13,0,230},{17,0,113},{8,10,226},{10,10,537},{11,10,570},{11,10, -605},{11,10,799},{11,10,804},{12,10,85},{12,10,516},{12,10,623},{12,11,677},{13, -10,361},{14,10,77},{14,10,78},{147,10,110},{4,0,792},{7,0,1717},{10,0,546},{132, -10,769},{4,11,684},{136,11,384},{132,10,551},{134,0,1203},{9,10,57},{9,10,459},{ -10,10,425},{11,10,119},{12,10,184},{12,10,371},{13,10,358},{145,10,51},{5,0,672} -,{5,10,814},{8,10,10},{9,10,421},{9,10,729},{10,10,609},{139,10,689},{138,0,189} -,{134,10,624},{7,11,110},{7,11,188},{8,11,290},{8,11,591},{9,11,382},{9,11,649}, -{11,11,71},{11,11,155},{11,11,313},{12,11,5},{13,11,325},{142,11,287},{133,0,99} -,{6,0,1053},{135,0,298},{7,11,360},{7,11,425},{9,11,66},{9,11,278},{138,11,644}, -{4,0,397},{136,0,555},{137,10,269},{132,10,528},{4,11,900},{133,11,861},{6,0, -1157},{5,11,254},{7,11,985},{136,11,73},{7,11,1959},{136,11,683},{12,0,398},{20, -0,39},{21,0,11},{150,0,41},{4,0,485},{7,0,353},{135,0,1523},{6,0,366},{7,0,1384} -,{135,0,1601},{138,0,787},{137,0,282},{5,10,104},{6,10,173},{135,10,1631},{139, -11,146},{4,0,157},{133,0,471},{134,0,941},{132,11,725},{7,0,1336},{8,10,138},{8, -10,342},{9,10,84},{10,10,193},{11,10,883},{140,10,359},{134,11,196},{136,0,116}, -{133,11,831},{134,0,787},{134,10,95},{6,10,406},{10,10,409},{10,10,447},{11,10, -44},{140,10,100},{5,0,160},{7,0,363},{7,0,589},{10,0,170},{141,0,55},{134,0,1815 -},{132,0,866},{6,0,889},{6,0,1067},{6,0,1183},{4,11,321},{134,11,569},{5,11,848} -,{134,11,66},{4,11,36},{6,10,1636},{7,11,1387},{10,11,205},{11,11,755},{141,11, -271},{132,0,689},{9,0,820},{4,10,282},{7,10,1034},{11,10,398},{11,10,634},{12,10 -,1},{12,10,79},{12,10,544},{14,10,237},{17,10,10},{146,10,20},{4,0,108},{7,0,804 -},{139,0,498},{132,11,887},{6,0,1119},{135,11,620},{6,11,165},{138,11,388},{5,0, -244},{5,10,499},{6,10,476},{7,10,600},{7,10,888},{135,10,1096},{140,0,609},{135, -0,1005},{4,0,412},{133,0,581},{4,11,719},{135,11,155},{7,10,296},{7,10,596},{8, -10,560},{8,10,586},{9,10,612},{11,10,304},{12,10,46},{13,10,89},{14,10,112},{145 -,10,122},{4,0,895},{133,0,772},{142,11,307},{135,0,1898},{4,0,926},{133,0,983},{ -4,11,353},{6,11,146},{6,11,1789},{7,11,288},{7,11,990},{7,11,1348},{9,11,665},{9 -,11,898},{11,11,893},{142,11,212},{132,0,538},{133,11,532},{6,0,294},{7,0,1267}, -{8,0,624},{141,0,496},{7,0,1325},{4,11,45},{135,11,1257},{138,0,301},{9,0,298},{ -12,0,291},{13,0,276},{14,0,6},{17,0,18},{21,0,32},{7,10,1599},{7,10,1723},{8,10, -79},{8,10,106},{8,10,190},{8,10,302},{8,10,383},{8,10,713},{9,10,119},{9,10,233} -,{9,10,419},{9,10,471},{10,10,181},{10,10,406},{11,10,57},{11,10,85},{11,10,120} -,{11,10,177},{11,10,296},{11,10,382},{11,10,454},{11,10,758},{11,10,999},{12,10, -27},{12,10,131},{12,10,245},{12,10,312},{12,10,446},{12,10,454},{13,10,98},{13, -10,426},{13,10,508},{14,10,163},{14,10,272},{14,10,277},{14,10,370},{15,10,95},{ -15,10,138},{15,10,167},{17,10,38},{148,10,96},{132,0,757},{134,0,1263},{4,0,820} -,{134,10,1759},{133,0,722},{136,11,816},{138,10,372},{145,10,16},{134,0,1039},{4 -,0,991},{134,0,2028},{133,10,258},{7,0,1875},{139,0,124},{6,11,559},{6,11,1691}, -{135,11,586},{5,0,324},{7,0,881},{8,10,134},{9,10,788},{140,10,438},{7,11,1823}, -{139,11,693},{6,0,1348},{134,0,1545},{134,0,911},{132,0,954},{8,0,329},{8,0,414} -,{7,10,1948},{135,10,2004},{5,0,517},{6,10,439},{7,10,780},{135,10,1040},{132,0, -816},{5,10,1},{6,10,81},{138,10,520},{9,0,713},{10,0,222},{5,10,482},{8,10,98},{ -10,10,700},{10,10,822},{11,10,302},{11,10,778},{12,10,50},{12,10,127},{12,10,396 -},{13,10,62},{13,10,328},{14,10,122},{147,10,72},{137,0,33},{5,10,2},{7,10,1494} -,{136,10,589},{6,10,512},{7,10,797},{8,10,253},{9,10,77},{10,10,1},{10,11,108},{ -10,10,129},{10,10,225},{11,11,116},{11,10,118},{11,10,226},{11,10,251},{11,10, -430},{11,10,701},{11,10,974},{11,10,982},{12,10,64},{12,10,260},{12,10,488},{140 -,10,690},{134,11,456},{133,11,925},{5,0,150},{7,0,106},{7,0,774},{8,0,603},{9,0, -593},{9,0,634},{10,0,44},{10,0,173},{11,0,462},{11,0,515},{13,0,216},{13,0,288}, -{142,0,400},{137,10,347},{5,0,748},{134,0,553},{12,0,108},{141,0,291},{7,0,420}, -{4,10,12},{7,10,522},{7,10,809},{8,10,797},{141,10,88},{6,11,193},{7,11,240},{7, -11,1682},{10,11,51},{10,11,640},{11,11,410},{13,11,82},{14,11,247},{14,11,331},{ -142,11,377},{133,10,528},{135,0,1777},{4,0,493},{144,0,55},{136,11,633},{139,0, -81},{6,0,980},{136,0,321},{148,10,109},{5,10,266},{9,10,290},{9,10,364},{10,10, -293},{11,10,606},{142,10,45},{6,0,568},{7,0,112},{7,0,1804},{8,0,362},{8,0,410}, -{8,0,830},{9,0,514},{11,0,649},{142,0,157},{4,0,74},{6,0,510},{6,10,594},{9,10, -121},{10,10,49},{10,10,412},{139,10,834},{134,0,838},{136,10,748},{132,10,466},{ -132,0,625},{135,11,1443},{4,11,237},{135,11,514},{9,10,378},{141,10,162},{6,0,16 -},{6,0,158},{7,0,43},{7,0,129},{7,0,181},{8,0,276},{8,0,377},{10,0,523},{11,0, -816},{12,0,455},{13,0,303},{142,0,135},{135,0,281},{4,0,1},{7,0,1143},{7,0,1463} -,{8,0,61},{9,0,207},{9,0,390},{9,0,467},{139,0,836},{6,11,392},{7,11,65},{135,11 -,2019},{132,10,667},{4,0,723},{5,0,895},{7,0,1031},{8,0,199},{8,0,340},{9,0,153} -,{9,0,215},{10,0,21},{10,0,59},{10,0,80},{10,0,224},{10,0,838},{11,0,229},{11,0, -652},{12,0,192},{13,0,146},{142,0,91},{132,0,295},{137,0,51},{9,11,222},{10,11, -43},{139,11,900},{5,0,309},{140,0,211},{5,0,125},{8,0,77},{138,0,15},{136,11,604 -},{138,0,789},{5,0,173},{4,10,39},{7,10,1843},{8,10,407},{11,10,144},{140,10,523 -},{138,11,265},{133,0,439},{132,10,510},{7,0,648},{7,0,874},{11,0,164},{12,0,76} -,{18,0,9},{7,10,1980},{10,10,487},{138,10,809},{12,0,111},{14,0,294},{19,0,45},{ -13,10,260},{146,10,63},{133,11,549},{134,10,570},{4,0,8},{7,0,1152},{7,0,1153},{ -7,0,1715},{9,0,374},{10,0,478},{139,0,648},{135,0,1099},{5,0,575},{6,0,354},{135 -,0,701},{7,11,36},{8,11,201},{136,11,605},{4,10,787},{136,11,156},{6,0,518},{149 -,11,13},{140,11,224},{134,0,702},{132,10,516},{5,11,724},{10,11,305},{11,11,151} -,{12,11,33},{12,11,121},{12,11,381},{17,11,3},{17,11,27},{17,11,78},{18,11,18},{ -19,11,54},{149,11,5},{8,0,87},{4,11,523},{5,11,638},{11,10,887},{14,10,365},{142 -,10,375},{138,0,438},{136,10,821},{135,11,1908},{6,11,242},{7,11,227},{7,11,1581 -},{8,11,104},{9,11,113},{9,11,220},{9,11,427},{10,11,74},{10,11,239},{11,11,579} -,{11,11,1023},{13,11,4},{13,11,204},{13,11,316},{18,11,95},{148,11,86},{4,0,69}, -{5,0,122},{5,0,849},{6,0,1633},{9,0,656},{138,0,464},{7,0,1802},{4,10,10},{139, -10,786},{135,11,861},{139,0,499},{7,0,476},{7,0,1592},{138,0,87},{133,10,684},{4 -,0,840},{134,10,27},{142,0,283},{6,0,1620},{7,11,1328},{136,11,494},{5,0,859},{7 -,0,1160},{8,0,107},{9,0,291},{9,0,439},{10,0,663},{11,0,609},{140,0,197},{7,11, -1306},{8,11,505},{9,11,482},{10,11,126},{11,11,225},{12,11,347},{12,11,449},{13, -11,19},{142,11,218},{5,11,268},{10,11,764},{12,11,120},{13,11,39},{145,11,127},{ -145,10,56},{7,11,1672},{10,11,472},{11,11,189},{143,11,51},{6,10,342},{6,10,496} -,{8,10,275},{137,10,206},{133,0,600},{4,0,117},{6,0,372},{7,0,1905},{142,0,323}, -{4,10,909},{5,10,940},{135,11,1471},{132,10,891},{4,0,722},{139,0,471},{4,11,384 -},{135,11,1022},{132,10,687},{9,0,5},{12,0,216},{12,0,294},{12,0,298},{12,0,400} -,{12,0,518},{13,0,229},{143,0,139},{135,11,1703},{7,11,1602},{10,11,698},{12,11, -212},{141,11,307},{6,10,41},{141,10,160},{135,11,1077},{9,11,159},{11,11,28},{ -140,11,603},{4,0,514},{7,0,1304},{138,0,477},{134,0,1774},{9,0,88},{139,0,270},{ -5,0,12},{7,0,375},{9,0,438},{134,10,1718},{132,11,515},{136,10,778},{8,11,632},{ -8,11,697},{137,11,854},{6,0,362},{6,0,997},{146,0,51},{7,0,816},{7,0,1241},{9,0, -283},{9,0,520},{10,0,213},{10,0,307},{10,0,463},{10,0,671},{10,0,746},{11,0,401} -,{11,0,794},{12,0,517},{18,0,107},{147,0,115},{133,10,115},{150,11,28},{4,11,136 -},{133,11,551},{142,10,314},{132,0,258},{6,0,22},{7,0,903},{7,0,1963},{8,0,639}, -{138,0,577},{5,0,681},{8,0,782},{13,0,130},{17,0,84},{5,10,193},{140,10,178},{9, -11,17},{138,11,291},{7,11,1287},{9,11,44},{10,11,552},{10,11,642},{11,11,839},{ -12,11,274},{12,11,275},{12,11,372},{13,11,91},{142,11,125},{135,10,174},{4,0,664 -},{5,0,804},{139,0,1013},{134,0,942},{6,0,1349},{6,0,1353},{6,0,1450},{7,11,1518 -},{139,11,694},{11,0,356},{4,10,122},{5,10,796},{5,10,952},{6,10,1660},{6,10, -1671},{8,10,567},{9,10,687},{9,10,742},{10,10,686},{11,10,682},{140,10,281},{5,0 -,32},{6,11,147},{7,11,886},{9,11,753},{138,11,268},{5,10,179},{7,10,1095},{135, -10,1213},{4,10,66},{7,10,722},{135,10,904},{135,10,352},{9,11,245},{138,11,137}, -{4,0,289},{7,0,629},{7,0,1698},{7,0,1711},{12,0,215},{133,11,414},{6,0,1975},{ -135,11,1762},{6,0,450},{136,0,109},{141,10,35},{134,11,599},{136,0,705},{133,0, -664},{134,11,1749},{11,11,402},{12,11,109},{12,11,431},{13,11,179},{13,11,206},{ -14,11,175},{14,11,217},{16,11,3},{148,11,53},{135,0,1238},{134,11,1627},{132,11, -488},{13,0,318},{10,10,592},{10,10,753},{12,10,317},{12,10,355},{12,10,465},{12, -10,469},{12,10,560},{140,10,578},{133,10,564},{132,11,83},{140,11,676},{6,0,1872 -},{6,0,1906},{6,0,1907},{9,0,934},{9,0,956},{9,0,960},{9,0,996},{12,0,794},{12,0 -,876},{12,0,880},{12,0,918},{15,0,230},{18,0,234},{18,0,238},{21,0,38},{149,0,62 -},{134,10,556},{134,11,278},{137,0,103},{7,10,544},{8,10,719},{138,10,61},{4,10, -5},{5,10,498},{8,10,637},{137,10,521},{7,0,777},{12,0,229},{12,0,239},{15,0,12}, -{12,11,229},{12,11,239},{143,11,12},{6,0,26},{7,11,388},{7,11,644},{139,11,781}, -{7,11,229},{8,11,59},{9,11,190},{9,11,257},{10,11,378},{140,11,191},{133,10,927} -,{135,10,1441},{4,10,893},{5,10,780},{133,10,893},{4,0,414},{5,0,467},{9,0,654}, -{10,0,451},{12,0,59},{141,0,375},{142,0,173},{135,0,17},{7,0,1350},{133,10,238}, -{135,0,955},{4,0,960},{10,0,887},{12,0,753},{18,0,161},{18,0,162},{152,0,19},{ -136,11,344},{6,10,1729},{137,11,288},{132,11,660},{4,0,217},{5,0,710},{7,0,760}, -{7,0,1926},{9,0,428},{9,0,708},{10,0,254},{10,0,296},{10,0,720},{11,0,109},{11,0 -,255},{12,0,165},{12,0,315},{13,0,107},{13,0,203},{14,0,54},{14,0,99},{14,0,114} -,{14,0,388},{16,0,85},{17,0,9},{17,0,33},{20,0,25},{20,0,28},{20,0,29},{21,0,9}, -{21,0,10},{21,0,34},{22,0,17},{4,10,60},{7,10,1800},{8,10,314},{9,10,700},{139, -10,487},{7,11,1035},{138,11,737},{7,11,690},{9,11,217},{9,11,587},{140,11,521},{ -6,0,919},{7,11,706},{7,11,1058},{138,11,538},{7,10,1853},{138,10,437},{136,10, -419},{6,0,280},{10,0,502},{11,0,344},{140,0,38},{5,0,45},{7,0,1161},{11,0,448},{ -11,0,880},{13,0,139},{13,0,407},{15,0,16},{17,0,95},{18,0,66},{18,0,88},{18,0, -123},{149,0,7},{11,11,92},{11,11,196},{11,11,409},{11,11,450},{11,11,666},{11,11 -,777},{12,11,262},{13,11,385},{13,11,393},{15,11,115},{16,11,45},{145,11,82},{ -136,0,777},{134,11,1744},{4,0,410},{7,0,521},{133,10,828},{134,0,673},{7,0,1110} -,{7,0,1778},{7,10,176},{135,10,178},{5,10,806},{7,11,268},{7,10,1976},{136,11, -569},{4,11,733},{9,11,194},{10,11,92},{11,11,198},{12,11,84},{12,11,87},{13,11, -128},{144,11,74},{5,0,341},{7,0,1129},{11,0,414},{4,10,51},{6,10,4},{7,10,591},{ -7,10,849},{7,10,951},{7,10,1613},{7,10,1760},{7,10,1988},{9,10,434},{10,10,754}, -{11,10,25},{139,10,37},{133,10,902},{135,10,928},{135,0,787},{132,0,436},{134,10 -,270},{7,0,1587},{135,0,1707},{6,0,377},{7,0,1025},{9,0,613},{145,0,104},{7,11, -982},{7,11,1361},{10,11,32},{143,11,56},{139,0,96},{132,0,451},{132,10,416},{142 -,10,372},{5,10,152},{5,10,197},{7,11,306},{7,10,340},{7,10,867},{10,10,548},{10, -10,581},{11,10,6},{12,10,3},{12,10,19},{14,10,110},{142,10,289},{134,0,680},{134 -,11,609},{7,0,483},{7,10,190},{8,10,28},{8,10,141},{8,10,444},{8,10,811},{9,10, -468},{11,10,334},{12,10,24},{12,10,386},{140,10,576},{10,0,916},{133,10,757},{5, -10,721},{135,10,1553},{133,11,178},{134,0,937},{132,10,898},{133,0,739},{147,0, -82},{135,0,663},{146,0,128},{5,10,277},{141,10,247},{134,0,1087},{132,10,435},{6 -,11,381},{7,11,645},{7,11,694},{136,11,546},{7,0,503},{135,0,1885},{6,0,1965},{8 -,0,925},{138,0,955},{4,0,113},{5,0,163},{5,0,735},{7,0,1009},{9,0,9},{9,0,771},{ -12,0,90},{13,0,138},{13,0,410},{143,0,128},{4,0,324},{138,0,104},{7,0,460},{5,10 -,265},{134,10,212},{133,11,105},{7,11,261},{7,11,1107},{7,11,1115},{7,11,1354},{ -7,11,1588},{7,11,1705},{7,11,1902},{9,11,465},{10,11,248},{10,11,349},{10,11,647 -},{11,11,527},{11,11,660},{11,11,669},{12,11,529},{141,11,305},{5,11,438},{9,11, -694},{12,11,627},{141,11,210},{152,11,11},{4,0,935},{133,0,823},{132,10,702},{5, -0,269},{7,0,434},{7,0,891},{8,0,339},{9,0,702},{11,0,594},{11,0,718},{17,0,100}, -{5,10,808},{135,10,2045},{7,0,1014},{9,0,485},{141,0,264},{134,0,1713},{7,0,1810 -},{11,0,866},{12,0,103},{13,0,495},{140,11,233},{4,0,423},{10,0,949},{138,0,1013 -},{135,0,900},{8,11,25},{138,11,826},{5,10,166},{8,10,739},{140,10,511},{134,0, -2018},{7,11,1270},{139,11,612},{4,10,119},{5,10,170},{5,10,447},{7,10,1708},{7, -10,1889},{9,10,357},{9,10,719},{12,10,486},{140,10,596},{12,0,574},{140,11,574}, -{132,11,308},{6,0,964},{6,0,1206},{134,0,1302},{4,10,450},{135,10,1158},{135,11, -150},{136,11,649},{14,0,213},{148,0,38},{9,11,45},{9,11,311},{141,11,42},{134,11 -,521},{7,10,1375},{7,10,1466},{138,10,331},{132,10,754},{5,11,339},{7,11,1442},{ -14,11,3},{15,11,41},{147,11,66},{136,11,378},{134,0,1022},{5,10,850},{136,10,799 -},{142,0,143},{135,0,2029},{134,11,1628},{8,0,523},{150,0,34},{5,0,625},{135,0, -1617},{7,0,275},{7,10,238},{7,10,2033},{8,10,120},{8,10,188},{8,10,659},{9,10, -598},{10,10,466},{12,10,342},{12,10,588},{13,10,503},{14,10,246},{143,10,92},{7, -0,37},{8,0,425},{8,0,693},{9,0,720},{10,0,380},{10,0,638},{11,0,273},{11,0,473}, -{12,0,61},{143,0,43},{135,11,829},{135,0,1943},{132,0,765},{5,11,486},{135,11, -1349},{7,11,1635},{8,11,17},{10,11,217},{138,11,295},{4,10,201},{7,10,1744},{8, -10,602},{11,10,247},{11,10,826},{145,10,65},{138,11,558},{11,0,551},{142,0,159}, -{8,10,164},{146,10,62},{139,11,176},{132,0,168},{136,0,1010},{134,0,1994},{135,0 -,91},{138,0,532},{135,10,1243},{135,0,1884},{132,10,907},{5,10,100},{10,10,329}, -{12,10,416},{149,10,29},{134,11,447},{132,10,176},{5,10,636},{5,10,998},{7,10,9} -,{7,10,1508},{8,10,26},{9,10,317},{9,10,358},{10,10,210},{10,10,292},{10,10,533} -,{11,10,555},{12,10,526},{12,10,607},{13,10,263},{13,10,459},{142,10,271},{4,11, -609},{135,11,756},{6,0,15},{7,0,70},{10,0,240},{147,0,93},{4,11,930},{133,11,947 -},{134,0,1227},{134,0,1534},{133,11,939},{133,11,962},{5,11,651},{8,11,170},{9, -11,61},{9,11,63},{10,11,23},{10,11,37},{10,11,834},{11,11,4},{11,11,187},{11,11, -281},{11,11,503},{11,11,677},{12,11,96},{12,11,130},{12,11,244},{14,11,5},{14,11 -,40},{14,11,162},{14,11,202},{146,11,133},{4,11,406},{5,11,579},{12,11,492},{150 -,11,15},{139,0,392},{6,10,610},{10,10,127},{141,10,27},{7,0,655},{7,0,1844},{136 -,10,119},{4,0,145},{6,0,176},{7,0,395},{137,0,562},{132,0,501},{140,11,145},{136 -,0,1019},{134,0,509},{139,0,267},{6,11,17},{7,11,16},{7,11,1001},{7,11,1982},{9, -11,886},{10,11,489},{10,11,800},{11,11,782},{12,11,320},{13,11,467},{14,11,145}, -{14,11,387},{143,11,119},{145,11,17},{6,0,1099},{133,11,458},{7,11,1983},{8,11,0 -},{8,11,171},{9,11,120},{9,11,732},{10,11,473},{11,11,656},{11,11,998},{18,11,0} -,{18,11,2},{147,11,21},{12,11,427},{146,11,38},{10,0,948},{138,0,968},{7,10,126} -,{136,10,84},{136,10,790},{4,0,114},{9,0,492},{13,0,462},{142,0,215},{6,10,64},{ -12,10,377},{141,10,309},{4,0,77},{5,0,361},{6,0,139},{6,0,401},{6,0,404},{7,0, -413},{7,0,715},{7,0,1716},{11,0,279},{12,0,179},{12,0,258},{13,0,244},{142,0,358 -},{134,0,1717},{7,0,772},{7,0,1061},{7,0,1647},{8,0,82},{11,0,250},{11,0,607},{ -12,0,311},{12,0,420},{13,0,184},{13,0,367},{7,10,1104},{11,10,269},{11,10,539},{ -11,10,627},{11,10,706},{11,10,975},{12,10,248},{12,10,434},{12,10,600},{12,10, -622},{13,10,297},{13,10,485},{14,10,69},{14,10,409},{143,10,108},{135,0,724},{4, -11,512},{4,11,519},{133,11,342},{134,0,1133},{145,11,29},{11,10,977},{141,10,507 -},{6,0,841},{6,0,1042},{6,0,1194},{10,0,993},{140,0,1021},{6,11,31},{7,11,491},{ -7,11,530},{8,11,592},{9,10,34},{11,11,53},{11,10,484},{11,11,779},{12,11,167},{ -12,11,411},{14,11,14},{14,11,136},{15,11,72},{16,11,17},{144,11,72},{4,0,1021},{ -6,0,2037},{133,11,907},{7,0,373},{8,0,335},{8,0,596},{9,0,488},{6,10,1700},{7,10 -,293},{7,10,382},{7,10,1026},{7,10,1087},{7,10,2027},{8,10,252},{8,10,727},{8,10 -,729},{9,10,30},{9,10,199},{9,10,231},{9,10,251},{9,10,334},{9,10,361},{9,10,712 -},{10,10,55},{10,10,60},{10,10,232},{10,10,332},{10,10,384},{10,10,396},{10,10, -504},{10,10,542},{10,10,652},{11,10,20},{11,10,48},{11,10,207},{11,10,291},{11, -10,298},{11,10,342},{11,10,365},{11,10,394},{11,10,620},{11,10,705},{11,10,1017} -,{12,10,123},{12,10,340},{12,10,406},{12,10,643},{13,10,61},{13,10,269},{13,10, -311},{13,10,319},{13,10,486},{14,10,234},{15,10,62},{15,10,85},{16,10,71},{18,10 -,119},{148,10,105},{150,0,37},{4,11,208},{5,11,106},{6,11,531},{8,11,408},{9,11, -188},{138,11,572},{132,0,564},{6,0,513},{135,0,1052},{132,0,825},{9,0,899},{140, -11,441},{134,0,778},{133,11,379},{7,0,1417},{12,0,382},{17,0,48},{152,0,12},{132 -,11,241},{7,0,1116},{6,10,379},{7,10,270},{8,10,176},{8,10,183},{9,10,432},{9,10 -,661},{12,10,247},{12,10,617},{146,10,125},{5,10,792},{133,10,900},{6,0,545},{7, -0,565},{7,0,1669},{10,0,114},{11,0,642},{140,0,618},{133,0,5},{138,11,7},{132,11 -,259},{135,0,192},{134,0,701},{136,0,763},{135,10,1979},{4,10,901},{133,10,776}, -{10,0,755},{147,0,29},{133,0,759},{4,11,173},{5,11,312},{5,11,512},{135,11,1285} -,{7,11,1603},{7,11,1691},{9,11,464},{11,11,195},{12,11,279},{12,11,448},{14,11, -11},{147,11,102},{7,0,370},{7,0,1007},{7,0,1177},{135,0,1565},{135,0,1237},{4,0, -87},{5,0,250},{141,0,298},{4,11,452},{5,11,583},{5,11,817},{6,11,433},{7,11,593} -,{7,11,720},{7,11,1378},{8,11,161},{9,11,284},{10,11,313},{139,11,886},{4,11,547 -},{135,11,1409},{136,11,722},{4,10,37},{5,10,334},{135,10,1253},{132,10,508},{12 -,0,107},{146,0,31},{8,11,420},{139,11,193},{135,0,814},{135,11,409},{140,0,991}, -{4,0,57},{7,0,1195},{7,0,1438},{7,0,1548},{7,0,1835},{7,0,1904},{9,0,757},{10,0, -604},{139,0,519},{132,0,540},{138,11,308},{132,10,533},{136,0,608},{144,11,65},{ -4,0,1014},{134,0,2029},{4,0,209},{7,0,902},{5,11,1002},{136,11,745},{134,0,2030} -,{6,0,303},{7,0,335},{7,0,1437},{7,0,1668},{8,0,553},{8,0,652},{8,0,656},{9,0, -558},{11,0,743},{149,0,18},{5,11,575},{6,11,354},{135,11,701},{4,11,239},{6,11, -477},{7,11,1607},{11,11,68},{139,11,617},{132,0,559},{8,0,527},{18,0,60},{147,0, -24},{133,10,920},{138,0,511},{133,0,1017},{133,0,675},{138,10,391},{11,0,156},{ -135,10,1952},{138,11,369},{132,11,367},{133,0,709},{6,0,698},{134,0,887},{142,10 -,126},{134,0,1745},{132,10,483},{13,11,299},{142,11,75},{133,0,714},{7,0,8},{136 -,0,206},{138,10,480},{4,11,694},{9,10,495},{146,10,104},{7,11,1248},{11,11,621}, -{139,11,702},{140,11,687},{132,0,776},{139,10,1009},{135,0,1272},{134,0,1059},{8 -,10,653},{13,10,93},{147,10,14},{135,11,213},{136,0,406},{133,10,172},{132,0,947 -},{8,0,175},{10,0,168},{138,0,573},{132,0,870},{6,0,1567},{151,11,28},{134,11, -472},{5,10,260},{136,11,132},{4,11,751},{11,11,390},{140,11,32},{4,11,409},{133, -11,78},{12,0,554},{6,11,473},{145,11,105},{133,0,784},{8,0,908},{136,11,306},{ -139,0,882},{6,0,358},{7,0,1393},{8,0,396},{10,0,263},{14,0,154},{16,0,48},{17,0, -8},{7,11,1759},{8,11,396},{10,11,263},{14,11,154},{16,11,48},{145,11,8},{13,11, -163},{13,11,180},{18,11,78},{148,11,35},{14,0,32},{18,0,85},{20,0,2},{152,0,16}, -{7,0,228},{10,0,770},{8,10,167},{8,10,375},{9,10,82},{9,10,561},{138,10,620},{ -132,0,845},{9,0,14},{9,0,441},{10,0,306},{139,0,9},{11,0,966},{12,0,287},{13,0, -342},{13,0,402},{15,0,110},{15,0,163},{8,10,194},{136,10,756},{134,0,1578},{4,0, -967},{6,0,1820},{6,0,1847},{140,0,716},{136,0,594},{7,0,1428},{7,0,1640},{7,0, -1867},{9,0,169},{9,0,182},{9,0,367},{9,0,478},{9,0,506},{9,0,551},{9,0,557},{9,0 -,648},{9,0,697},{9,0,705},{9,0,725},{9,0,787},{9,0,794},{10,0,198},{10,0,214},{ -10,0,267},{10,0,275},{10,0,456},{10,0,551},{10,0,561},{10,0,613},{10,0,627},{10, -0,668},{10,0,675},{10,0,691},{10,0,695},{10,0,707},{10,0,715},{11,0,183},{11,0, -201},{11,0,244},{11,0,262},{11,0,352},{11,0,439},{11,0,493},{11,0,572},{11,0,591 -},{11,0,608},{11,0,611},{11,0,646},{11,0,674},{11,0,711},{11,0,751},{11,0,761},{ -11,0,776},{11,0,785},{11,0,850},{11,0,853},{11,0,862},{11,0,865},{11,0,868},{11, -0,875},{11,0,898},{11,0,902},{11,0,903},{11,0,910},{11,0,932},{11,0,942},{11,0, -957},{11,0,967},{11,0,972},{12,0,148},{12,0,195},{12,0,220},{12,0,237},{12,0,318 -},{12,0,339},{12,0,393},{12,0,445},{12,0,450},{12,0,474},{12,0,505},{12,0,509},{ -12,0,533},{12,0,591},{12,0,594},{12,0,597},{12,0,621},{12,0,633},{12,0,642},{13, -0,59},{13,0,60},{13,0,145},{13,0,239},{13,0,250},{13,0,329},{13,0,344},{13,0,365 -},{13,0,372},{13,0,387},{13,0,403},{13,0,414},{13,0,456},{13,0,470},{13,0,478},{ -13,0,483},{13,0,489},{14,0,55},{14,0,57},{14,0,81},{14,0,90},{14,0,148},{14,0, -239},{14,0,266},{14,0,321},{14,0,326},{14,0,327},{14,0,330},{14,0,347},{14,0,355 -},{14,0,401},{14,0,404},{14,0,411},{14,0,414},{14,0,416},{14,0,420},{15,0,61},{ -15,0,74},{15,0,87},{15,0,88},{15,0,94},{15,0,96},{15,0,116},{15,0,149},{15,0,154 -},{16,0,50},{16,0,63},{16,0,73},{17,0,2},{17,0,66},{17,0,92},{17,0,103},{17,0, -112},{17,0,120},{18,0,50},{18,0,54},{18,0,82},{18,0,86},{18,0,90},{18,0,111},{18 -,0,115},{18,0,156},{19,0,40},{19,0,79},{20,0,78},{21,0,22},{135,11,883},{5,0,161 -},{135,0,839},{4,0,782},{13,11,293},{142,11,56},{133,11,617},{139,11,50},{135,10 -,22},{145,0,64},{5,10,639},{7,10,1249},{139,10,896},{138,0,998},{135,11,2042},{4 -,11,546},{142,11,233},{6,0,1043},{134,0,1574},{134,0,1496},{4,10,102},{7,10,815} -,{7,10,1699},{139,10,964},{12,0,781},{142,0,461},{4,11,313},{133,11,577},{6,0, -639},{6,0,1114},{137,0,817},{8,11,184},{141,11,433},{7,0,1814},{135,11,935},{10, -0,997},{140,0,958},{4,0,812},{137,11,625},{132,10,899},{136,10,795},{5,11,886},{ -6,11,46},{6,11,1790},{7,11,14},{7,11,732},{7,11,1654},{8,11,95},{8,11,327},{8,11 -,616},{10,11,598},{10,11,769},{11,11,134},{11,11,747},{12,11,378},{142,11,97},{ -136,0,139},{6,10,52},{9,10,104},{9,10,559},{12,10,308},{147,10,87},{133,11,1021} -,{132,10,604},{132,10,301},{136,10,779},{7,0,643},{136,0,236},{132,11,153},{134, -0,1172},{147,10,32},{133,11,798},{6,0,1338},{132,11,587},{6,11,598},{7,11,42},{8 -,11,695},{10,11,212},{11,11,158},{14,11,196},{145,11,85},{135,10,508},{5,11,957} -,{5,11,1008},{135,11,249},{4,11,129},{135,11,465},{5,0,54},{7,11,470},{7,11,1057 -},{7,11,1201},{9,11,755},{11,11,906},{140,11,527},{7,11,908},{146,11,7},{5,11, -148},{136,11,450},{144,11,1},{4,0,256},{135,0,1488},{9,0,351},{6,10,310},{7,10, -1849},{8,10,72},{8,10,272},{8,10,431},{9,10,12},{10,10,563},{10,10,630},{10,10, -796},{10,10,810},{11,10,367},{11,10,599},{11,10,686},{140,10,672},{6,0,1885},{6, -0,1898},{6,0,1899},{140,0,955},{4,0,714},{133,0,469},{6,0,1270},{134,0,1456},{ -132,0,744},{6,0,313},{7,10,537},{8,10,64},{9,10,127},{10,10,496},{12,10,510},{ -141,10,384},{4,11,217},{4,10,244},{5,11,710},{7,10,233},{7,11,1926},{9,11,428},{ -9,11,708},{10,11,254},{10,11,296},{10,11,720},{11,11,109},{11,11,255},{12,11,165 -},{12,11,315},{13,11,107},{13,11,203},{14,11,54},{14,11,99},{14,11,114},{14,11, -388},{16,11,85},{17,11,9},{17,11,33},{20,11,25},{20,11,28},{20,11,29},{21,11,9}, -{21,11,10},{21,11,34},{150,11,17},{138,0,402},{7,0,969},{146,0,55},{8,0,50},{137 -,0,624},{134,0,1355},{132,0,572},{134,10,1650},{10,10,702},{139,10,245},{10,0, -847},{142,0,445},{6,0,43},{7,0,38},{8,0,248},{138,0,513},{133,0,369},{137,10,338 -},{133,0,766},{133,0,363},{133,10,896},{8,11,392},{11,11,54},{13,11,173},{13,11, -294},{148,11,7},{134,0,678},{7,11,1230},{136,11,531},{6,0,258},{140,0,409},{5,0, -249},{148,0,82},{7,10,1117},{136,10,539},{5,0,393},{6,0,378},{7,0,1981},{9,0,32} -,{9,0,591},{10,0,685},{10,0,741},{142,0,382},{133,0,788},{134,0,1281},{134,0, -1295},{7,0,1968},{141,0,509},{4,0,61},{5,0,58},{5,0,171},{5,0,683},{6,0,291},{6, -0,566},{7,0,1650},{11,0,523},{12,0,273},{12,0,303},{15,0,39},{143,0,111},{6,0, -706},{134,0,1283},{134,0,589},{135,11,1433},{133,11,435},{7,0,1059},{13,0,54},{5 -,10,4},{5,10,810},{6,10,13},{6,10,538},{6,10,1690},{6,10,1726},{7,10,1819},{8,10 -,148},{8,10,696},{8,10,791},{12,10,125},{143,10,9},{135,10,1268},{5,11,85},{6,11 -,419},{7,11,134},{7,11,305},{7,11,361},{7,11,1337},{8,11,71},{140,11,519},{137,0 -,824},{140,11,688},{5,11,691},{7,11,345},{7,10,1385},{9,11,94},{11,10,582},{11, -10,650},{11,10,901},{11,10,949},{12,11,169},{12,10,232},{12,10,236},{13,10,413}, -{13,10,501},{146,10,116},{4,0,917},{133,0,1005},{7,0,1598},{5,11,183},{6,11,582} -,{9,11,344},{10,11,679},{140,11,435},{4,10,925},{5,10,803},{8,10,698},{138,10, -828},{132,0,919},{135,11,511},{139,10,992},{4,0,255},{5,0,302},{6,0,132},{7,0, -128},{7,0,283},{7,0,1299},{10,0,52},{10,0,514},{11,0,925},{13,0,92},{142,0,309}, -{134,0,1369},{135,10,1847},{134,0,328},{7,11,1993},{136,11,684},{133,10,383},{ -137,0,173},{134,11,583},{134,0,1411},{19,0,65},{5,11,704},{8,11,357},{10,11,745} -,{14,11,426},{17,11,94},{147,11,57},{9,10,660},{138,10,347},{4,11,179},{5,11,198 -},{133,11,697},{7,11,347},{7,11,971},{8,11,181},{138,11,711},{141,0,442},{11,0, -842},{11,0,924},{13,0,317},{13,0,370},{13,0,469},{13,0,471},{14,0,397},{18,0,69} -,{18,0,145},{7,10,572},{9,10,592},{11,10,680},{12,10,356},{140,10,550},{14,11,19 -},{14,11,28},{144,11,29},{136,0,534},{4,11,243},{5,11,203},{7,11,19},{7,11,71},{ -7,11,113},{10,11,405},{11,11,357},{142,11,240},{6,0,210},{10,0,845},{138,0,862}, -{7,11,1351},{9,11,581},{10,11,639},{11,11,453},{140,11,584},{7,11,1450},{139,11, -99},{10,0,892},{12,0,719},{144,0,105},{4,0,284},{6,0,223},{134,11,492},{5,11,134 -},{6,11,408},{6,11,495},{135,11,1593},{136,0,529},{137,0,807},{4,0,218},{7,0,526 -},{143,0,137},{6,0,1444},{142,11,4},{132,11,665},{4,0,270},{5,0,192},{6,0,332},{ -7,0,1322},{4,11,248},{7,11,137},{137,11,349},{140,0,661},{7,0,1517},{11,0,597},{ -14,0,76},{14,0,335},{20,0,33},{7,10,748},{139,10,700},{5,11,371},{135,11,563},{ -146,11,57},{133,10,127},{133,0,418},{4,11,374},{7,11,547},{7,11,1700},{7,11,1833 -},{139,11,858},{6,10,198},{140,10,83},{7,11,1812},{13,11,259},{13,11,356},{14,11 -,242},{147,11,114},{7,0,379},{8,0,481},{9,0,377},{5,10,276},{6,10,55},{135,10, -1369},{138,11,286},{5,0,1003},{6,0,149},{6,10,1752},{136,10,726},{8,0,262},{9,0, -627},{10,0,18},{11,0,214},{11,0,404},{11,0,457},{11,0,780},{11,0,913},{13,0,401} -,{14,0,200},{6,11,1647},{7,11,1552},{7,11,2010},{9,11,494},{137,11,509},{135,0, -742},{136,0,304},{132,0,142},{133,10,764},{6,10,309},{7,10,331},{138,10,550},{ -135,10,1062},{6,11,123},{7,11,214},{7,10,986},{9,11,728},{10,11,157},{11,11,346} -,{11,11,662},{143,11,106},{135,10,1573},{7,0,925},{137,0,799},{4,0,471},{5,0,51} -,{6,0,602},{8,0,484},{138,0,195},{136,0,688},{132,0,697},{6,0,1169},{6,0,1241},{ -6,10,194},{7,10,133},{10,10,493},{10,10,570},{139,10,664},{140,0,751},{7,0,929}, -{10,0,452},{11,0,878},{16,0,33},{5,10,24},{5,10,569},{6,10,3},{6,10,119},{6,10, -143},{6,10,440},{7,10,599},{7,10,1686},{7,10,1854},{8,10,424},{9,10,43},{9,10, -584},{9,10,760},{10,10,328},{11,10,159},{11,10,253},{12,10,487},{140,10,531},{4, -11,707},{13,11,106},{18,11,49},{147,11,41},{5,0,221},{5,11,588},{134,11,393},{ -134,0,1437},{6,11,211},{7,11,1690},{11,11,486},{140,11,369},{5,10,14},{5,10,892} -,{6,10,283},{7,10,234},{136,10,537},{4,0,988},{136,0,955},{135,0,1251},{4,10,126 -},{8,10,635},{147,10,34},{4,10,316},{135,10,1561},{137,10,861},{4,10,64},{5,10, -352},{5,10,720},{6,10,368},{139,10,359},{134,0,192},{4,0,132},{5,0,69},{135,0, -1242},{7,10,1577},{10,10,304},{10,10,549},{12,10,365},{13,10,220},{13,10,240},{ -142,10,33},{4,0,111},{7,0,865},{134,11,219},{5,11,582},{6,11,1646},{7,11,99},{7, -11,1962},{7,11,1986},{8,11,515},{8,11,773},{9,11,23},{9,11,491},{12,11,620},{14, -11,52},{145,11,50},{132,0,767},{7,11,568},{148,11,21},{6,0,42},{7,0,1416},{7,0, -2005},{8,0,131},{8,0,466},{9,0,672},{13,0,252},{20,0,103},{133,11,851},{135,0, -1050},{6,10,175},{137,10,289},{5,10,432},{133,10,913},{6,0,44},{136,0,368},{135, -11,784},{132,0,570},{133,0,120},{139,10,595},{140,0,29},{6,0,227},{135,0,1589},{ -4,11,98},{7,11,1365},{9,11,422},{9,11,670},{10,11,775},{11,11,210},{13,11,26},{ -13,11,457},{141,11,476},{140,10,80},{5,10,931},{134,10,1698},{133,0,522},{134,0, -1120},{135,0,1529},{12,0,739},{14,0,448},{142,0,467},{11,10,526},{11,10,939},{ -141,10,290},{5,10,774},{6,10,1637},{6,10,1686},{134,10,1751},{6,0,1667},{135,0, -2036},{7,10,1167},{11,10,934},{13,10,391},{145,10,76},{137,11,147},{6,10,260},{7 -,10,1484},{11,11,821},{12,11,110},{12,11,153},{18,11,41},{150,11,19},{6,0,511},{ -12,0,132},{134,10,573},{5,0,568},{6,0,138},{135,0,1293},{132,0,1020},{8,0,258},{ -9,0,208},{137,0,359},{4,0,565},{8,0,23},{136,0,827},{134,0,344},{4,0,922},{5,0, -1023},{13,11,477},{14,11,120},{148,11,61},{134,0,240},{5,11,209},{6,11,30},{11, -11,56},{139,11,305},{6,0,171},{7,0,1002},{7,0,1324},{9,0,415},{14,0,230},{18,0, -68},{4,10,292},{4,10,736},{5,10,871},{6,10,1689},{7,10,1944},{137,10,580},{9,11, -635},{139,11,559},{4,11,150},{5,11,303},{134,11,327},{6,10,63},{135,10,920},{133 -,10,793},{8,11,192},{10,11,78},{10,11,555},{11,11,308},{13,11,359},{147,11,95},{ -135,11,786},{135,11,1712},{136,0,402},{6,0,754},{6,11,1638},{7,11,79},{7,11,496} -,{9,11,138},{10,11,336},{11,11,12},{12,11,412},{12,11,440},{142,11,305},{4,0,716 -},{141,0,31},{133,0,982},{8,0,691},{8,0,731},{5,10,67},{6,10,62},{6,10,374},{135 -,10,1391},{9,10,790},{140,10,47},{139,11,556},{151,11,1},{7,11,204},{7,11,415},{ -8,11,42},{10,11,85},{11,11,33},{11,11,564},{12,11,571},{149,11,1},{8,0,888},{7, -11,610},{135,11,1501},{4,10,391},{135,10,1169},{5,0,847},{9,0,840},{138,0,803},{ -137,0,823},{134,0,785},{8,0,152},{9,0,53},{9,0,268},{9,0,901},{10,0,518},{10,0, -829},{11,0,188},{13,0,74},{14,0,46},{15,0,17},{15,0,33},{17,0,40},{18,0,36},{19, -0,20},{22,0,1},{152,0,2},{4,11,3},{5,11,247},{5,11,644},{7,11,744},{7,11,1207},{ -7,11,1225},{7,11,1909},{146,11,147},{136,0,532},{135,0,681},{132,10,271},{140,0, -314},{140,0,677},{4,0,684},{136,0,384},{5,11,285},{9,11,67},{13,11,473},{143,11, -82},{4,10,253},{5,10,544},{7,10,300},{137,10,340},{7,0,110},{7,0,447},{8,0,290}, -{8,0,591},{9,0,382},{9,0,649},{11,0,71},{11,0,155},{11,0,313},{12,0,5},{13,0,325 -},{142,0,287},{134,0,1818},{136,0,1007},{138,0,321},{7,0,360},{7,0,425},{9,0,66} -,{9,0,278},{138,0,644},{133,10,818},{5,0,385},{5,10,541},{6,10,94},{6,10,499},{7 -,10,230},{139,10,321},{4,10,920},{5,10,25},{5,10,790},{6,10,457},{7,10,853},{136 -,10,788},{4,0,900},{133,0,861},{5,0,254},{7,0,985},{136,0,73},{7,0,1959},{136,0, -683},{134,10,1765},{133,10,822},{132,10,634},{4,11,29},{6,11,532},{7,11,1628},{7 -,11,1648},{9,11,303},{9,11,350},{10,11,433},{11,11,97},{11,11,557},{11,11,745},{ -12,11,289},{12,11,335},{12,11,348},{12,11,606},{13,11,116},{13,11,233},{13,11, -466},{14,11,181},{14,11,209},{14,11,232},{14,11,236},{14,11,300},{16,11,41},{148 -,11,97},{19,0,86},{6,10,36},{7,10,658},{136,10,454},{135,11,1692},{132,0,725},{5 -,11,501},{7,11,1704},{9,11,553},{11,11,520},{12,11,557},{141,11,249},{134,0,196} -,{133,0,831},{136,0,723},{7,0,1897},{13,0,80},{13,0,437},{145,0,74},{4,0,992},{6 -,0,627},{136,0,994},{135,11,1294},{132,10,104},{5,0,848},{6,0,66},{136,0,764},{4 -,0,36},{7,0,1387},{10,0,205},{139,0,755},{6,0,1046},{134,0,1485},{134,0,950},{ -132,0,887},{14,0,450},{148,0,111},{7,0,620},{7,0,831},{9,10,542},{9,10,566},{138 -,10,728},{6,0,165},{138,0,388},{139,10,263},{4,0,719},{135,0,155},{138,10,468},{ -6,11,453},{144,11,36},{134,11,129},{5,0,533},{7,0,755},{138,0,780},{134,0,1465}, -{4,0,353},{6,0,146},{6,0,1789},{7,0,427},{7,0,990},{7,0,1348},{9,0,665},{9,0,898 -},{11,0,893},{142,0,212},{7,10,87},{142,10,288},{4,0,45},{135,0,1257},{12,0,7},{ -7,10,988},{7,10,1939},{9,10,64},{9,10,502},{12,10,34},{13,10,12},{13,10,234},{ -147,10,77},{4,0,607},{5,11,60},{6,11,504},{7,11,614},{7,11,1155},{140,11,0},{135 -,10,141},{8,11,198},{11,11,29},{140,11,534},{140,0,65},{136,0,816},{132,10,619}, -{139,0,88},{5,10,246},{8,10,189},{9,10,355},{9,10,512},{10,10,124},{10,10,453},{ -11,10,143},{11,10,416},{11,10,859},{141,10,341},{4,11,379},{135,11,1397},{4,0, -600},{137,0,621},{133,0,367},{134,0,561},{6,0,559},{134,0,1691},{6,0,585},{134, -11,585},{135,11,1228},{4,11,118},{5,10,678},{6,11,274},{6,11,361},{7,11,75},{141 -,11,441},{135,11,1818},{137,11,841},{5,0,573},{6,0,287},{7,10,862},{7,10,1886},{ -138,10,179},{132,10,517},{140,11,693},{5,11,314},{6,11,221},{7,11,419},{10,11, -650},{11,11,396},{12,11,156},{13,11,369},{14,11,333},{145,11,47},{140,10,540},{ -136,10,667},{11,10,403},{146,10,83},{6,0,672},{133,10,761},{9,0,157},{10,10,131} -,{140,10,72},{7,0,714},{134,11,460},{134,0,456},{133,0,925},{5,11,682},{135,11, -1887},{136,11,510},{136,11,475},{133,11,1016},{9,0,19},{7,11,602},{8,11,179},{10 -,11,781},{140,11,126},{6,11,329},{138,11,111},{6,0,822},{134,0,1473},{144,11,86} -,{11,0,113},{139,11,113},{5,11,821},{134,11,1687},{133,10,449},{7,0,463},{17,0, -69},{136,10,103},{7,10,2028},{138,10,641},{6,0,193},{7,0,240},{7,0,1682},{10,0, -51},{10,0,640},{11,0,410},{13,0,82},{14,0,247},{14,0,331},{142,0,377},{6,0,471}, -{11,0,411},{142,0,2},{5,11,71},{7,11,1407},{9,11,388},{9,11,704},{10,11,261},{10 -,11,619},{11,11,547},{11,11,619},{143,11,157},{136,0,633},{135,0,1148},{6,0,554} -,{7,0,1392},{12,0,129},{7,10,1274},{7,10,1386},{7,11,2008},{9,11,337},{10,11,517 -},{146,10,87},{7,0,803},{8,0,542},{6,10,187},{7,10,1203},{8,10,380},{14,10,117}, -{149,10,28},{6,10,297},{7,10,793},{139,10,938},{8,0,438},{11,0,363},{7,10,464},{ -11,10,105},{12,10,231},{14,10,386},{15,10,102},{148,10,75},{5,11,16},{6,11,86},{ -6,11,603},{7,11,292},{7,11,561},{8,11,257},{8,11,382},{9,11,721},{9,11,778},{11, -11,581},{140,11,466},{6,0,717},{4,11,486},{133,11,491},{132,0,875},{132,11,72},{ -6,11,265},{135,11,847},{4,0,237},{135,0,514},{6,0,392},{7,0,65},{135,0,2019},{ -140,11,261},{135,11,922},{137,11,404},{12,0,563},{14,0,101},{18,0,129},{7,10, -1010},{11,10,733},{11,10,759},{13,10,34},{146,10,45},{7,10,1656},{9,10,369},{10, -10,338},{10,10,490},{11,10,154},{11,10,545},{11,10,775},{13,10,77},{141,10,274}, -{4,0,444},{10,0,146},{140,0,9},{139,11,163},{7,0,1260},{135,0,1790},{9,0,222},{ -10,0,43},{139,0,900},{137,11,234},{138,0,971},{137,0,761},{134,0,699},{136,11, -434},{6,0,1116},{7,0,1366},{5,10,20},{6,11,197},{6,10,298},{7,10,659},{8,11,205} -,{137,10,219},{132,11,490},{11,11,820},{150,11,51},{7,10,1440},{11,10,854},{11, -10,872},{11,10,921},{12,10,551},{13,10,472},{142,10,367},{140,11,13},{132,0,829} -,{12,0,242},{132,10,439},{136,10,669},{6,0,593},{6,11,452},{7,11,312},{138,11, -219},{4,11,333},{9,11,176},{12,11,353},{141,11,187},{7,0,36},{8,0,201},{136,0, -605},{140,0,224},{132,10,233},{134,0,1430},{134,0,1806},{4,0,523},{133,0,638},{6 -,0,1889},{9,0,958},{9,0,971},{9,0,976},{12,0,796},{12,0,799},{12,0,808},{12,0, -835},{12,0,836},{12,0,914},{12,0,946},{15,0,216},{15,0,232},{18,0,183},{18,0,187 -},{18,0,194},{18,0,212},{18,0,232},{149,0,49},{132,10,482},{6,0,827},{134,0,1434 -},{135,10,346},{134,0,2043},{6,0,242},{7,0,227},{7,0,1581},{8,0,104},{9,0,113},{ -9,0,220},{9,0,427},{10,0,136},{10,0,239},{11,0,579},{11,0,1023},{13,0,4},{13,0, -204},{13,0,316},{148,0,86},{134,11,1685},{7,0,148},{8,0,284},{141,0,63},{142,0, -10},{135,11,584},{134,0,1249},{7,0,861},{135,10,334},{5,10,795},{6,10,1741},{137 -,11,70},{132,0,807},{7,11,135},{8,11,7},{8,11,62},{9,11,243},{10,11,658},{10,11, -697},{11,11,456},{139,11,756},{9,11,395},{138,11,79},{137,11,108},{147,0,94},{ -136,0,494},{135,11,631},{135,10,622},{7,0,1510},{135,10,1750},{4,10,203},{135,10 -,1936},{7,11,406},{7,11,459},{8,11,606},{139,11,726},{7,0,1306},{8,0,505},{9,0, -482},{10,0,126},{11,0,225},{12,0,347},{12,0,449},{13,0,19},{14,0,218},{142,0,435 -},{5,0,268},{10,0,764},{12,0,120},{13,0,39},{145,0,127},{142,11,68},{11,10,678}, -{140,10,307},{12,11,268},{12,11,640},{142,11,119},{135,10,2044},{133,11,612},{4, -11,372},{7,11,482},{8,11,158},{9,11,602},{9,11,615},{10,11,245},{10,11,678},{10, -11,744},{11,11,248},{139,11,806},{7,10,311},{9,10,308},{140,10,255},{4,0,384},{ -135,0,1022},{5,11,854},{135,11,1991},{135,10,1266},{4,10,400},{5,10,267},{135,10 -,232},{135,0,1703},{9,0,159},{11,0,661},{140,0,603},{4,0,964},{14,0,438},{14,0, -444},{14,0,456},{22,0,60},{22,0,63},{9,11,106},{9,11,163},{9,11,296},{10,11,167} -,{10,11,172},{10,11,777},{139,11,16},{136,0,583},{132,0,515},{8,0,632},{8,0,697} -,{137,0,854},{5,11,195},{135,11,1685},{6,0,1123},{134,0,1365},{134,11,328},{7,11 -,1997},{8,11,730},{139,11,1006},{4,0,136},{133,0,551},{134,0,1782},{7,0,1287},{9 -,0,44},{10,0,552},{10,0,642},{11,0,839},{12,0,274},{12,0,275},{12,0,372},{13,0, -91},{142,0,125},{5,11,751},{11,11,797},{140,11,203},{133,0,732},{7,0,679},{8,0, -313},{4,10,100},{135,11,821},{10,0,361},{142,0,316},{134,0,595},{6,0,147},{7,0, -886},{9,0,753},{138,0,268},{5,10,362},{5,10,443},{6,10,318},{7,10,1019},{139,10, -623},{5,10,463},{136,10,296},{4,10,454},{5,11,950},{5,11,994},{134,11,351},{138, -0,137},{5,10,48},{5,10,404},{6,10,557},{7,10,458},{8,10,597},{10,10,455},{10,10, -606},{11,10,49},{11,10,548},{12,10,476},{13,10,18},{141,10,450},{133,0,414},{135 -,0,1762},{5,11,421},{135,11,47},{5,10,442},{135,10,1984},{134,0,599},{134,0,1749 -},{134,0,1627},{4,0,488},{132,11,350},{137,11,751},{132,0,83},{140,0,676},{133, -11,967},{7,0,1639},{5,10,55},{140,10,161},{4,11,473},{7,11,623},{8,11,808},{9,11 -,871},{9,11,893},{11,11,38},{11,11,431},{12,11,112},{12,11,217},{12,11,243},{12, -11,562},{12,11,683},{13,11,141},{13,11,197},{13,11,227},{13,11,406},{13,11,487}, -{14,11,156},{14,11,203},{14,11,224},{14,11,256},{18,11,58},{150,11,0},{133,10, -450},{7,11,736},{139,11,264},{134,0,278},{4,11,222},{7,11,286},{136,11,629},{135 -,10,869},{140,0,97},{144,0,14},{134,0,1085},{4,10,213},{7,10,223},{136,10,80},{7 -,0,388},{7,0,644},{139,0,781},{132,0,849},{7,0,229},{8,0,59},{9,0,190},{10,0,378 -},{140,0,191},{7,10,381},{7,10,806},{7,10,820},{8,10,354},{8,10,437},{8,10,787}, -{9,10,657},{10,10,58},{10,10,339},{10,10,749},{11,10,914},{12,10,162},{13,10,75} -,{14,10,106},{14,10,198},{14,10,320},{14,10,413},{146,10,43},{141,11,306},{136, -10,747},{134,0,1115},{16,0,94},{16,0,108},{136,11,146},{6,0,700},{6,0,817},{134, -0,1002},{133,10,692},{4,11,465},{135,11,1663},{134,10,191},{6,0,1414},{135,11, -913},{132,0,660},{7,0,1035},{138,0,737},{6,10,162},{7,10,1960},{136,10,831},{132 -,10,706},{7,0,690},{9,0,217},{9,0,587},{140,0,521},{138,10,426},{135,10,1235},{6 -,11,82},{7,11,138},{7,11,517},{9,11,673},{139,11,238},{138,0,272},{5,11,495},{7, -11,834},{9,11,733},{139,11,378},{134,0,1744},{132,0,1011},{7,11,828},{142,11,116 -},{4,0,733},{9,0,194},{10,0,92},{11,0,198},{12,0,84},{13,0,128},{133,11,559},{10 -,0,57},{10,0,277},{6,11,21},{6,11,1737},{7,11,1444},{136,11,224},{4,10,204},{137 -,10,902},{136,10,833},{11,0,348},{12,0,99},{18,0,1},{18,0,11},{19,0,4},{7,10,366 -},{9,10,287},{12,10,199},{12,10,556},{140,10,577},{6,0,1981},{136,0,936},{21,0, -33},{150,0,40},{5,11,519},{138,11,204},{5,10,356},{135,10,224},{134,0,775},{135, -0,306},{7,10,630},{9,10,567},{11,10,150},{11,10,444},{141,10,119},{5,0,979},{134 -,10,539},{133,0,611},{4,11,402},{135,11,1679},{5,0,178},{7,11,2},{8,11,323},{136 -,11,479},{5,11,59},{135,11,672},{4,0,1010},{6,0,1969},{138,11,237},{133,11,412}, -{146,11,34},{7,11,1740},{146,11,48},{134,0,664},{139,10,814},{4,11,85},{135,11, -549},{133,11,94},{133,11,457},{132,0,390},{134,0,1510},{4,10,235},{135,10,255},{ -4,10,194},{5,10,584},{6,11,11},{6,10,384},{7,11,187},{7,10,583},{10,10,761},{11, -10,760},{139,10,851},{4,11,522},{139,11,802},{135,0,493},{10,11,776},{13,11,345} -,{142,11,425},{146,0,37},{4,11,52},{135,11,661},{134,0,724},{134,0,829},{133,11, -520},{133,10,562},{4,11,281},{5,11,38},{7,11,194},{7,11,668},{7,11,1893},{137,11 -,397},{5,10,191},{137,10,271},{7,0,1537},{14,0,96},{143,0,73},{5,0,473},{11,0, -168},{4,10,470},{6,10,153},{7,10,1503},{7,10,1923},{10,10,701},{11,10,132},{11, -10,227},{11,10,320},{11,10,436},{11,10,525},{11,10,855},{12,10,41},{12,10,286},{ -13,10,103},{13,10,284},{14,10,255},{14,10,262},{15,10,117},{143,10,127},{133,0, -105},{5,0,438},{9,0,694},{12,0,627},{141,0,210},{133,10,327},{6,10,552},{7,10, -1754},{137,10,604},{134,0,1256},{152,0,11},{5,11,448},{11,11,98},{139,11,524},{7 -,0,1626},{5,10,80},{6,10,405},{7,10,403},{7,10,1502},{8,10,456},{9,10,487},{9,10 -,853},{9,10,889},{10,10,309},{11,10,721},{11,10,994},{12,10,430},{13,10,165},{14 -,11,16},{146,11,44},{132,0,779},{8,0,25},{138,0,826},{4,10,453},{5,10,887},{6,10 -,535},{8,10,6},{8,10,543},{136,10,826},{137,11,461},{140,11,632},{132,0,308},{ -135,0,741},{132,0,671},{7,0,150},{8,0,649},{136,0,1020},{9,0,99},{6,11,336},{8, -11,552},{9,11,285},{10,11,99},{139,11,568},{134,0,521},{5,0,339},{14,0,3},{15,0, -41},{15,0,166},{147,0,66},{6,11,423},{7,11,665},{7,11,1210},{9,11,218},{141,11, -222},{6,0,543},{5,10,101},{5,11,256},{6,10,88},{7,10,1677},{9,10,100},{10,10,677 -},{14,10,169},{14,10,302},{14,10,313},{15,10,48},{143,10,84},{4,10,310},{7,10, -708},{7,10,996},{9,10,795},{10,10,390},{10,10,733},{11,10,451},{12,10,249},{14, -10,115},{14,10,286},{143,10,100},{133,10,587},{13,11,417},{14,11,129},{143,11,15 -},{134,0,1358},{136,11,554},{132,10,498},{7,10,217},{8,10,140},{138,10,610},{135 -,11,989},{135,11,634},{6,0,155},{140,0,234},{135,11,462},{132,11,618},{134,0, -1628},{132,0,766},{4,11,339},{5,10,905},{135,11,259},{135,0,829},{4,11,759},{141 -,11,169},{7,0,1445},{4,10,456},{7,10,358},{7,10,1637},{8,10,643},{139,10,483},{5 -,0,486},{135,0,1349},{5,11,688},{135,11,712},{7,0,1635},{8,0,17},{10,0,217},{10, -0,295},{12,0,2},{140,11,2},{138,0,558},{150,10,56},{4,11,278},{5,11,465},{135,11 -,1367},{136,11,482},{133,10,535},{6,0,1362},{6,0,1461},{10,11,274},{10,11,625},{ -139,11,530},{5,0,599},{5,11,336},{6,11,341},{6,11,478},{6,11,1763},{136,11,386}, -{7,10,1748},{137,11,151},{134,0,1376},{133,10,539},{135,11,73},{135,11,1971},{ -139,11,283},{9,0,93},{139,0,474},{6,10,91},{135,10,435},{6,0,447},{5,11,396},{ -134,11,501},{4,10,16},{5,10,316},{5,10,842},{6,10,370},{6,10,1778},{8,10,166},{ -11,10,812},{12,10,206},{12,10,351},{14,10,418},{16,10,15},{16,10,34},{18,10,3},{ -19,10,3},{19,10,7},{20,10,4},{149,10,21},{7,0,577},{7,0,1432},{9,0,475},{9,0,505 -},{9,0,526},{9,0,609},{9,0,689},{9,0,726},{9,0,735},{9,0,738},{10,0,556},{10,0, -674},{10,0,684},{11,0,89},{11,0,202},{11,0,272},{11,0,380},{11,0,415},{11,0,505} -,{11,0,537},{11,0,550},{11,0,562},{11,0,640},{11,0,667},{11,0,688},{11,0,847},{ -11,0,927},{11,0,930},{11,0,940},{12,0,144},{12,0,325},{12,0,329},{12,0,389},{12, -0,403},{12,0,451},{12,0,515},{12,0,604},{12,0,616},{12,0,626},{13,0,66},{13,0, -131},{13,0,167},{13,0,236},{13,0,368},{13,0,411},{13,0,434},{13,0,453},{13,0,461 -},{13,0,474},{14,0,59},{14,0,60},{14,0,139},{14,0,152},{14,0,276},{14,0,353},{14 -,0,402},{15,0,28},{15,0,81},{15,0,123},{15,0,152},{18,0,136},{148,0,88},{4,11, -929},{133,11,799},{136,11,46},{142,0,307},{4,0,609},{7,0,756},{9,0,544},{11,0, -413},{144,0,25},{10,0,687},{7,10,619},{10,10,547},{11,10,122},{140,10,601},{4,0, -930},{133,0,947},{133,0,939},{142,0,21},{4,11,892},{133,11,770},{133,0,962},{5,0 -,651},{8,0,170},{9,0,61},{9,0,63},{10,0,23},{10,0,37},{10,0,834},{11,0,4},{11,0, -187},{11,0,281},{11,0,503},{11,0,677},{12,0,96},{12,0,130},{12,0,244},{14,0,5},{ -14,0,40},{14,0,162},{14,0,202},{146,0,133},{4,0,406},{5,0,579},{12,0,492},{150,0 -,15},{135,11,158},{135,0,597},{132,0,981},{132,10,888},{4,10,149},{138,10,368},{ -132,0,545},{4,10,154},{7,10,1134},{136,10,105},{135,11,2001},{134,0,1558},{4,10, -31},{6,10,429},{7,10,962},{9,10,458},{139,10,691},{132,10,312},{135,10,1642},{6, -0,17},{6,0,1304},{7,0,16},{7,0,1001},{9,0,886},{10,0,489},{10,0,800},{11,0,782}, -{12,0,320},{13,0,467},{14,0,145},{14,0,387},{143,0,119},{135,0,1982},{17,0,17},{ -7,11,1461},{140,11,91},{4,10,236},{132,11,602},{138,0,907},{136,0,110},{7,0,272} -,{19,0,53},{5,10,836},{5,10,857},{134,10,1680},{5,0,458},{7,11,1218},{136,11,303 -},{7,0,1983},{8,0,0},{8,0,171},{9,0,120},{9,0,732},{10,0,473},{11,0,656},{11,0, -998},{18,0,0},{18,0,2},{19,0,21},{10,10,68},{139,10,494},{137,11,662},{4,11,13}, -{5,11,567},{7,11,1498},{9,11,124},{11,11,521},{140,11,405},{4,10,81},{139,10,867 -},{135,11,1006},{7,11,800},{7,11,1783},{138,11,12},{9,0,295},{10,0,443},{5,10, -282},{8,10,650},{137,10,907},{132,11,735},{4,11,170},{4,10,775},{135,11,323},{6, -0,1844},{10,0,924},{11,11,844},{12,11,104},{140,11,625},{5,11,304},{7,11,1403},{ -140,11,498},{134,0,1232},{4,0,519},{10,0,70},{12,0,26},{14,0,17},{14,0,178},{15, -0,34},{149,0,12},{132,0,993},{4,11,148},{133,11,742},{6,0,31},{7,0,491},{7,0,530 -},{8,0,592},{11,0,53},{11,0,779},{12,0,167},{12,0,411},{14,0,14},{14,0,136},{15, -0,72},{16,0,17},{144,0,72},{133,0,907},{134,0,733},{133,11,111},{4,10,71},{5,10, -376},{7,10,119},{138,10,665},{136,0,55},{8,0,430},{136,11,430},{4,0,208},{5,0, -106},{6,0,531},{8,0,408},{9,0,188},{138,0,572},{12,0,56},{11,10,827},{14,10,34}, -{143,10,148},{134,0,1693},{133,11,444},{132,10,479},{140,0,441},{9,0,449},{10,0, -192},{138,0,740},{134,0,928},{4,0,241},{7,10,607},{136,10,99},{8,11,123},{15,11, -6},{144,11,7},{6,11,285},{8,11,654},{11,11,749},{12,11,190},{12,11,327},{13,11, -120},{13,11,121},{13,11,327},{15,11,47},{146,11,40},{4,10,41},{5,10,74},{7,10, -1627},{11,10,871},{140,10,619},{7,0,1525},{11,10,329},{11,10,965},{12,10,241},{ -14,10,354},{15,10,22},{148,10,63},{132,0,259},{135,11,183},{9,10,209},{137,10, -300},{5,11,937},{135,11,100},{133,10,98},{4,0,173},{5,0,312},{5,0,512},{135,0, -1285},{141,0,185},{7,0,1603},{7,0,1691},{9,0,464},{11,0,195},{12,0,279},{12,0, -448},{14,0,11},{147,0,102},{135,0,1113},{133,10,984},{4,0,452},{5,0,583},{135,0, -720},{4,0,547},{5,0,817},{6,0,433},{7,0,593},{7,0,1378},{8,0,161},{9,0,284},{10, -0,313},{139,0,886},{8,0,722},{4,10,182},{6,10,205},{135,10,220},{150,0,13},{4,10 -,42},{9,10,205},{9,10,786},{138,10,659},{6,0,289},{7,0,1670},{12,0,57},{151,0,4} -,{132,10,635},{14,0,43},{146,0,21},{139,10,533},{135,0,1694},{8,0,420},{139,0, -193},{135,0,409},{132,10,371},{4,10,272},{135,10,836},{5,10,825},{134,10,1640},{ -5,11,251},{5,11,956},{8,11,268},{9,11,214},{146,11,142},{138,0,308},{6,0,1863},{ -141,11,37},{137,10,879},{7,10,317},{135,10,569},{132,11,294},{134,0,790},{5,0, -1002},{136,0,745},{5,11,346},{5,11,711},{136,11,390},{135,0,289},{5,0,504},{11,0 -,68},{137,10,307},{4,0,239},{6,0,477},{7,0,1607},{139,0,617},{149,0,13},{133,0, -609},{133,11,624},{5,11,783},{7,11,1998},{135,11,2047},{133,10,525},{132,0,367}, -{132,11,594},{6,0,528},{133,10,493},{4,10,174},{135,10,911},{8,10,417},{137,10, -782},{132,0,694},{7,0,548},{137,0,58},{4,10,32},{5,10,215},{6,10,269},{7,10,1782 -},{7,10,1892},{10,10,16},{11,10,822},{11,10,954},{141,10,481},{140,0,687},{7,0, -1749},{136,10,477},{132,11,569},{133,10,308},{135,10,1088},{4,0,661},{138,0,1004 -},{5,11,37},{6,11,39},{6,11,451},{7,11,218},{7,11,667},{7,11,1166},{7,11,1687},{ -8,11,662},{144,11,2},{9,0,445},{12,0,53},{13,0,492},{5,10,126},{8,10,297},{9,10, -366},{140,10,374},{7,10,1551},{139,10,361},{148,0,74},{134,11,508},{135,0,213},{ -132,10,175},{132,10,685},{6,0,760},{6,0,834},{134,0,1248},{7,11,453},{7,11,635}, -{7,11,796},{8,11,331},{9,11,328},{9,11,330},{9,11,865},{10,11,119},{10,11,235},{ -11,11,111},{11,11,129},{11,11,240},{12,11,31},{12,11,66},{12,11,222},{12,11,269} -,{12,11,599},{12,11,689},{13,11,186},{13,11,364},{142,11,345},{7,0,1672},{139,0, -189},{133,10,797},{133,10,565},{6,0,1548},{6,11,98},{7,11,585},{135,11,702},{9,0 -,968},{15,0,192},{149,0,56},{4,10,252},{6,11,37},{7,11,299},{7,10,1068},{7,11, -1666},{8,11,195},{8,11,316},{9,11,178},{9,11,276},{9,11,339},{9,11,536},{10,11, -102},{10,11,362},{10,10,434},{10,11,785},{11,11,55},{11,11,149},{11,10,228},{11, -10,426},{11,11,773},{13,10,231},{13,11,416},{13,11,419},{14,11,38},{14,11,41},{ -14,11,210},{18,10,106},{148,10,87},{4,0,751},{11,0,390},{140,0,32},{4,0,409},{ -133,0,78},{11,11,458},{12,11,15},{140,11,432},{7,0,1602},{10,0,257},{10,0,698},{ -11,0,544},{11,0,585},{12,0,212},{13,0,307},{5,10,231},{7,10,601},{9,10,277},{9, -10,674},{10,10,178},{10,10,418},{10,10,509},{11,10,531},{12,10,113},{12,10,475}, -{13,10,99},{142,10,428},{6,0,473},{145,0,105},{6,0,1949},{15,0,156},{133,11,645} -,{7,10,1591},{144,10,43},{135,0,1779},{135,10,1683},{4,11,290},{135,11,1356},{ -134,0,763},{6,11,70},{7,11,1292},{10,11,762},{139,11,288},{142,0,29},{140,11,428 -},{7,0,883},{7,11,131},{7,11,422},{8,11,210},{140,11,573},{134,0,488},{4,10,399} -,{5,10,119},{5,10,494},{7,10,751},{137,10,556},{133,0,617},{132,11,936},{139,0, -50},{7,0,1518},{139,0,694},{137,0,785},{4,0,546},{135,0,2042},{7,11,716},{13,11, -97},{141,11,251},{132,11,653},{145,0,22},{134,0,1016},{4,0,313},{133,0,577},{136 -,11,657},{8,0,184},{141,0,433},{135,0,935},{6,0,720},{9,0,114},{146,11,80},{12,0 -,186},{12,0,292},{14,0,100},{18,0,70},{7,10,594},{7,10,851},{7,10,1858},{9,10, -411},{9,10,574},{9,10,666},{9,10,737},{10,10,346},{10,10,712},{11,10,246},{11,10 -,432},{11,10,517},{11,10,647},{11,10,679},{11,10,727},{12,10,304},{12,10,305},{ -12,10,323},{12,10,483},{12,10,572},{12,10,593},{12,10,602},{13,10,95},{13,10,101 -},{13,10,171},{13,10,315},{13,10,378},{13,10,425},{13,10,475},{14,10,63},{14,10, -380},{14,10,384},{15,10,133},{18,10,112},{148,10,72},{135,10,1093},{135,11,1836} -,{132,10,679},{137,10,203},{11,0,402},{12,0,109},{12,0,431},{13,0,179},{13,0,206 -},{14,0,217},{16,0,3},{148,0,53},{7,11,1368},{8,11,232},{8,11,361},{10,11,682},{ -138,11,742},{137,10,714},{5,0,886},{6,0,46},{6,0,1790},{7,0,14},{7,0,732},{7,0, -1654},{8,0,95},{8,0,327},{8,0,616},{9,0,892},{10,0,598},{10,0,769},{11,0,134},{ -11,0,747},{12,0,378},{14,0,97},{137,11,534},{4,0,969},{136,10,825},{137,11,27},{ -6,0,727},{142,11,12},{133,0,1021},{134,0,1190},{134,11,1657},{5,10,143},{5,10, -769},{6,10,1760},{7,10,682},{7,10,1992},{136,10,736},{132,0,153},{135,11,127},{ -133,0,798},{132,0,587},{6,0,598},{7,0,42},{8,0,695},{10,0,212},{11,0,158},{14,0, -196},{145,0,85},{133,10,860},{6,0,1929},{134,0,1933},{5,0,957},{5,0,1008},{9,0, -577},{12,0,141},{6,10,422},{7,10,0},{7,10,1544},{8,11,364},{11,10,990},{12,10, -453},{13,10,47},{141,10,266},{134,0,1319},{4,0,129},{135,0,465},{7,0,470},{7,0, -1057},{7,0,1201},{9,0,755},{11,0,906},{140,0,527},{7,0,908},{146,0,7},{5,0,148}, -{136,0,450},{5,10,515},{137,10,131},{7,10,1605},{11,10,962},{146,10,139},{132,10 -,646},{134,0,1166},{4,10,396},{7,10,728},{9,10,117},{13,10,202},{148,10,51},{6, -10,121},{6,10,124},{6,10,357},{7,10,1138},{7,10,1295},{8,10,162},{139,10,655},{ -14,0,374},{142,11,374},{138,0,253},{139,0,1003},{5,11,909},{9,11,849},{138,11, -805},{133,10,237},{7,11,525},{7,11,1579},{8,11,497},{136,11,573},{137,0,46},{132 -,0,879},{134,0,806},{135,0,1868},{6,0,1837},{134,0,1846},{6,0,730},{134,0,881},{ -7,0,965},{7,0,1460},{7,0,1604},{7,11,193},{7,11,397},{7,11,1105},{8,11,124},{8, -11,619},{9,11,305},{10,11,264},{11,11,40},{12,11,349},{13,11,134},{13,11,295},{ -14,11,155},{15,11,120},{146,11,105},{136,0,506},{143,0,10},{4,11,262},{7,11,342} -,{7,10,571},{7,10,1877},{10,10,366},{141,11,23},{133,11,641},{10,0,22},{9,10,513 -},{10,10,39},{12,10,122},{140,10,187},{135,11,1431},{150,11,49},{4,11,99},{6,11, -250},{6,11,346},{8,11,127},{138,11,81},{6,0,2014},{8,0,928},{10,0,960},{10,0,979 -},{140,0,996},{134,0,296},{132,11,915},{5,11,75},{9,11,517},{10,11,470},{12,11, -155},{141,11,224},{137,10,873},{4,0,854},{140,11,18},{134,0,587},{7,10,107},{7, -10,838},{8,10,550},{138,10,401},{11,0,636},{15,0,145},{17,0,34},{19,0,50},{23,0, -20},{11,10,588},{11,10,864},{11,10,968},{143,10,160},{135,11,216},{7,0,982},{10, -0,32},{143,0,56},{133,10,768},{133,11,954},{6,11,304},{7,11,1114},{8,11,418},{10 -,11,345},{11,11,341},{11,11,675},{141,11,40},{9,11,410},{139,11,425},{136,0,941} -,{5,0,435},{132,10,894},{5,0,85},{6,0,419},{7,0,134},{7,0,305},{7,0,361},{7,0, -1337},{8,0,71},{140,0,519},{140,0,688},{135,0,740},{5,0,691},{7,0,345},{9,0,94}, -{140,0,169},{5,0,183},{6,0,582},{10,0,679},{140,0,435},{134,11,14},{6,0,945},{ -135,0,511},{134,11,1708},{5,11,113},{6,11,243},{7,11,1865},{11,11,161},{16,11,37 -},{145,11,99},{132,11,274},{137,0,539},{7,0,1993},{8,0,684},{134,10,272},{6,0, -659},{134,0,982},{4,10,9},{5,10,128},{7,10,368},{11,10,480},{148,10,3},{134,0, -583},{132,0,803},{133,0,704},{4,0,179},{5,0,198},{133,0,697},{7,0,347},{7,0,971} -,{8,0,181},{10,0,711},{135,11,166},{136,10,682},{4,10,2},{7,10,545},{7,10,894},{ -136,11,521},{135,0,481},{132,0,243},{5,0,203},{7,0,19},{7,0,71},{7,0,113},{10,0, -405},{11,0,357},{142,0,240},{5,11,725},{5,11,727},{135,11,1811},{6,0,826},{137, -11,304},{7,0,1450},{139,0,99},{133,11,654},{134,0,492},{5,0,134},{6,0,408},{6,0, -495},{7,0,1593},{6,11,273},{10,11,188},{13,11,377},{146,11,77},{9,10,769},{140, -10,185},{135,11,410},{142,0,4},{4,0,665},{134,11,1785},{4,0,248},{7,0,137},{137, -0,349},{5,10,530},{142,10,113},{7,0,1270},{139,0,612},{132,11,780},{5,0,371},{ -135,0,563},{135,0,826},{6,0,1535},{23,0,21},{151,0,23},{4,0,374},{7,0,547},{7,0, -1700},{7,0,1833},{139,0,858},{133,10,556},{7,11,612},{8,11,545},{8,11,568},{8,11 -,642},{9,11,717},{10,11,541},{10,11,763},{11,11,449},{12,11,489},{13,11,153},{13 -,11,296},{14,11,138},{14,11,392},{15,11,50},{16,11,6},{16,11,12},{148,11,9},{9,0 -,311},{141,0,42},{8,10,16},{140,10,568},{6,0,1968},{6,0,2027},{138,0,991},{6,0, -1647},{7,0,1552},{7,0,2010},{9,0,494},{137,0,509},{133,11,948},{6,10,186},{137, -10,426},{134,0,769},{134,0,642},{132,10,585},{6,0,123},{7,0,214},{9,0,728},{10,0 -,157},{11,0,346},{11,0,662},{143,0,106},{142,11,381},{135,0,1435},{4,11,532},{5, -11,706},{135,11,662},{5,11,837},{134,11,1651},{4,10,93},{5,10,252},{6,10,229},{7 -,10,291},{9,10,550},{139,10,644},{148,0,79},{137,10,749},{134,0,1425},{137,10, -162},{4,11,362},{7,11,52},{7,11,303},{140,11,166},{132,10,381},{4,11,330},{7,11, -933},{7,11,2012},{136,11,292},{135,11,767},{4,0,707},{5,0,588},{6,0,393},{13,0, -106},{18,0,49},{147,0,41},{6,0,211},{7,0,1690},{11,0,486},{140,0,369},{137,11, -883},{4,11,703},{135,11,207},{4,0,187},{5,0,184},{5,0,690},{7,0,1869},{10,0,756} -,{139,0,783},{132,11,571},{134,0,1382},{5,0,175},{6,10,77},{6,10,157},{7,10,974} -,{7,10,1301},{7,10,1339},{7,10,1490},{7,10,1873},{137,10,628},{134,0,1493},{5,11 -,873},{133,11,960},{134,0,1007},{12,11,93},{12,11,501},{13,11,362},{14,11,151},{ -15,11,40},{15,11,59},{16,11,46},{17,11,25},{18,11,14},{18,11,134},{19,11,25},{19 -,11,69},{20,11,16},{20,11,19},{20,11,66},{21,11,23},{21,11,25},{150,11,42},{11, -10,919},{141,10,409},{134,0,219},{5,0,582},{6,0,1646},{7,0,99},{7,0,1962},{7,0, -1986},{8,0,515},{8,0,773},{9,0,23},{9,0,491},{12,0,620},{142,0,93},{133,0,851},{ -5,11,33},{134,11,470},{135,11,1291},{134,0,1278},{135,11,1882},{135,10,1489},{ -132,0,1000},{138,0,982},{8,0,762},{8,0,812},{137,0,910},{6,11,47},{7,11,90},{7, -11,664},{7,11,830},{7,11,1380},{7,11,2025},{8,11,448},{136,11,828},{4,0,98},{4,0 -,940},{6,0,1819},{6,0,1834},{6,0,1841},{7,0,1365},{8,0,859},{8,0,897},{8,0,918}, -{9,0,422},{9,0,670},{10,0,775},{10,0,894},{10,0,909},{10,0,910},{10,0,935},{11,0 -,210},{12,0,750},{12,0,755},{13,0,26},{13,0,457},{13,0,476},{16,0,100},{16,0,109 -},{18,0,173},{18,0,175},{8,10,398},{9,10,681},{139,10,632},{9,11,417},{137,11, -493},{136,10,645},{138,0,906},{134,0,1730},{134,10,20},{133,11,1019},{134,0,1185 -},{10,0,40},{136,10,769},{9,0,147},{134,11,208},{140,0,650},{5,0,209},{6,0,30},{ -11,0,56},{139,0,305},{132,0,553},{138,11,344},{6,11,68},{7,11,398},{7,11,448},{7 -,11,1629},{7,11,1813},{8,11,387},{8,11,442},{9,11,710},{10,11,282},{138,11,722}, -{5,0,597},{14,0,20},{142,11,20},{135,0,1614},{135,10,1757},{4,0,150},{5,0,303},{ -6,0,327},{135,10,937},{16,0,49},{7,10,1652},{144,11,49},{8,0,192},{10,0,78},{141 -,0,359},{135,0,786},{143,0,134},{6,0,1638},{7,0,79},{7,0,496},{9,0,138},{10,0, -336},{11,0,12},{12,0,412},{12,0,440},{142,0,305},{136,11,491},{4,10,579},{5,10, -226},{5,10,323},{135,10,960},{7,0,204},{7,0,415},{8,0,42},{10,0,85},{139,0,564}, -{132,0,614},{4,11,403},{5,11,441},{7,11,450},{11,11,101},{12,11,193},{141,11,430 -},{135,11,1927},{135,11,1330},{4,0,3},{5,0,247},{5,0,644},{7,0,744},{7,0,1207},{ -7,0,1225},{7,0,1909},{146,0,147},{136,0,942},{4,0,1019},{134,0,2023},{5,11,679}, -{133,10,973},{5,0,285},{9,0,67},{13,0,473},{143,0,82},{7,11,328},{137,11,326},{ -151,0,8},{6,10,135},{135,10,1176},{135,11,1128},{134,0,1309},{135,11,1796},{135, -10,314},{4,11,574},{7,11,350},{7,11,1024},{8,11,338},{9,11,677},{10,11,808},{139 -,11,508},{7,11,818},{17,11,14},{17,11,45},{18,11,75},{148,11,18},{146,10,4},{135 -,11,1081},{4,0,29},{6,0,532},{7,0,1628},{7,0,1648},{9,0,350},{10,0,433},{11,0,97 -},{11,0,557},{11,0,745},{12,0,289},{12,0,335},{12,0,348},{12,0,606},{13,0,116},{ -13,0,233},{13,0,466},{14,0,181},{14,0,209},{14,0,232},{14,0,236},{14,0,300},{16, -0,41},{148,0,97},{7,0,318},{6,10,281},{8,10,282},{8,10,480},{8,10,499},{9,10,198 -},{10,10,143},{10,10,169},{10,10,211},{10,10,417},{10,10,574},{11,10,147},{11,10 -,395},{12,10,75},{12,10,407},{12,10,608},{13,10,500},{142,10,251},{135,11,1676}, -{135,11,2037},{135,0,1692},{5,0,501},{7,0,1704},{9,0,553},{11,0,520},{12,0,557}, -{141,0,249},{6,0,1527},{14,0,324},{15,0,55},{15,0,80},{14,11,324},{15,11,55},{ -143,11,80},{135,10,1776},{8,0,988},{137,11,297},{132,10,419},{142,0,223},{139,11 -,234},{7,0,1123},{12,0,508},{14,0,102},{14,0,226},{144,0,57},{4,10,138},{7,10, -1012},{7,10,1280},{137,10,76},{7,0,1764},{5,10,29},{140,10,638},{134,0,2015},{ -134,0,1599},{138,11,56},{6,11,306},{7,11,1140},{7,11,1340},{8,11,133},{138,11, -449},{139,11,1011},{6,10,1710},{135,10,2038},{7,11,1763},{140,11,310},{6,0,129}, -{4,10,17},{5,10,23},{7,10,995},{11,10,383},{11,10,437},{12,10,460},{140,10,532}, -{5,11,329},{136,11,260},{133,10,862},{132,0,534},{6,0,811},{135,0,626},{132,11, -657},{4,0,25},{5,0,60},{6,0,504},{7,0,614},{7,0,1155},{12,0,0},{152,11,7},{7,0, -1248},{11,0,621},{139,0,702},{137,0,321},{8,10,70},{12,10,171},{141,10,272},{10, -10,233},{139,10,76},{4,0,379},{7,0,1397},{134,10,442},{5,11,66},{7,11,1896},{136 -,11,288},{134,11,1643},{134,10,1709},{4,11,21},{5,11,91},{5,11,570},{5,11,648},{ -5,11,750},{5,11,781},{6,11,54},{6,11,112},{6,11,402},{6,11,1732},{7,11,315},{7, -11,749},{7,11,1347},{7,11,1900},{9,11,78},{9,11,508},{10,11,611},{11,11,510},{11 -,11,728},{13,11,36},{14,11,39},{16,11,83},{17,11,124},{148,11,30},{4,0,118},{6,0 -,274},{6,0,361},{7,0,75},{141,0,441},{10,11,322},{10,11,719},{139,11,407},{147, -10,119},{12,11,549},{14,11,67},{147,11,60},{11,10,69},{12,10,105},{12,10,117},{ -13,10,213},{14,10,13},{14,10,62},{14,10,177},{14,10,421},{15,10,19},{146,10,141} -,{9,0,841},{137,10,309},{7,10,608},{7,10,976},{8,11,125},{8,11,369},{8,11,524},{ -9,10,146},{10,10,206},{10,11,486},{10,10,596},{11,11,13},{11,11,381},{11,11,736} -,{11,11,766},{11,11,845},{13,11,114},{13,10,218},{13,11,292},{14,11,47},{142,10, -153},{12,0,693},{135,11,759},{5,0,314},{6,0,221},{7,0,419},{10,0,650},{11,0,396} -,{12,0,156},{13,0,369},{14,0,333},{145,0,47},{6,11,1684},{6,11,1731},{7,11,356}, -{7,11,1932},{8,11,54},{8,11,221},{9,11,225},{9,11,356},{10,11,77},{10,11,446},{ -10,11,731},{12,11,404},{141,11,491},{132,11,375},{4,10,518},{135,10,1136},{4,0, -913},{4,11,411},{11,11,643},{140,11,115},{4,11,80},{133,11,44},{8,10,689},{137, -10,863},{138,0,880},{4,10,18},{7,10,145},{7,10,444},{7,10,1278},{8,10,49},{8,10, -400},{9,10,71},{9,10,250},{10,10,459},{12,10,160},{144,10,24},{136,0,475},{5,0, -1016},{5,11,299},{135,11,1083},{7,0,602},{8,0,179},{10,0,781},{140,0,126},{6,0, -329},{138,0,111},{135,0,1864},{4,11,219},{7,11,1761},{137,11,86},{6,0,1888},{6,0 -,1892},{6,0,1901},{6,0,1904},{9,0,953},{9,0,985},{9,0,991},{9,0,1001},{12,0,818} -,{12,0,846},{12,0,847},{12,0,861},{12,0,862},{12,0,873},{12,0,875},{12,0,877},{ -12,0,879},{12,0,881},{12,0,884},{12,0,903},{12,0,915},{12,0,926},{12,0,939},{15, -0,182},{15,0,219},{15,0,255},{18,0,191},{18,0,209},{18,0,211},{149,0,41},{5,11, -328},{135,11,918},{137,0,780},{12,0,82},{143,0,36},{133,10,1010},{5,0,821},{134, -0,1687},{133,11,514},{132,0,956},{134,0,1180},{10,0,112},{5,10,87},{7,10,313},{7 -,10,1103},{10,10,582},{11,10,389},{11,10,813},{12,10,385},{13,10,286},{14,10,124 -},{146,10,108},{5,0,71},{7,0,1407},{9,0,704},{10,0,261},{10,0,619},{11,0,547},{ -11,0,619},{143,0,157},{4,0,531},{5,0,455},{5,11,301},{6,11,571},{14,11,49},{146, -11,102},{132,10,267},{6,0,385},{7,0,2008},{9,0,337},{138,0,517},{133,11,726},{ -133,11,364},{4,11,76},{7,11,1550},{9,11,306},{9,11,430},{9,11,663},{10,11,683},{ -11,11,427},{11,11,753},{12,11,334},{12,11,442},{14,11,258},{14,11,366},{143,11, -131},{6,0,1865},{6,0,1879},{6,0,1881},{6,0,1894},{6,0,1908},{9,0,915},{9,0,926}, -{9,0,940},{9,0,943},{9,0,966},{9,0,980},{9,0,989},{9,0,1005},{9,0,1010},{12,0, -813},{12,0,817},{12,0,840},{12,0,843},{12,0,855},{12,0,864},{12,0,871},{12,0,872 -},{12,0,899},{12,0,905},{12,0,924},{15,0,171},{15,0,181},{15,0,224},{15,0,235},{ -15,0,251},{146,0,184},{137,11,52},{5,0,16},{6,0,86},{6,0,603},{7,0,292},{7,0,561 -},{8,0,257},{8,0,382},{9,0,721},{9,0,778},{11,0,581},{140,0,466},{4,0,486},{5,0, -491},{135,10,1121},{4,0,72},{6,0,265},{135,0,1300},{135,11,1183},{10,10,249},{ -139,10,209},{132,10,561},{137,11,519},{4,11,656},{4,10,760},{135,11,779},{9,10, -154},{140,10,485},{135,11,1793},{135,11,144},{136,10,255},{133,0,621},{4,10,368} -,{135,10,641},{135,11,1373},{7,11,554},{7,11,605},{141,11,10},{137,0,234},{5,0, -815},{6,0,1688},{134,0,1755},{5,11,838},{5,11,841},{134,11,1649},{7,0,1987},{7,0 -,2040},{136,0,743},{133,11,1012},{6,0,197},{136,0,205},{6,0,314},{134,11,314},{ -144,11,53},{6,11,251},{7,11,365},{7,11,1357},{7,11,1497},{8,11,154},{141,11,281} -,{133,11,340},{6,0,452},{7,0,312},{138,0,219},{138,0,589},{4,0,333},{9,0,176},{ -12,0,353},{141,0,187},{9,10,92},{147,10,91},{134,0,1110},{11,0,47},{139,11,495}, -{6,10,525},{8,10,806},{9,10,876},{140,10,284},{8,11,261},{9,11,144},{9,11,466},{ -10,11,370},{12,11,470},{13,11,144},{142,11,348},{137,11,897},{8,0,863},{8,0,864} -,{8,0,868},{8,0,884},{10,0,866},{10,0,868},{10,0,873},{10,0,911},{10,0,912},{10, -0,944},{12,0,727},{6,11,248},{9,11,546},{10,11,535},{11,11,681},{141,11,135},{6, -0,300},{135,0,1515},{134,0,1237},{139,10,958},{133,10,594},{140,11,250},{134,0, -1685},{134,11,567},{7,0,135},{8,0,7},{8,0,62},{9,0,243},{10,0,658},{10,0,697},{ -11,0,456},{139,0,756},{9,0,395},{138,0,79},{6,10,1641},{136,10,820},{4,10,302},{ -135,10,1766},{134,11,174},{135,10,1313},{135,0,631},{134,10,1674},{134,11,395},{ -138,0,835},{7,0,406},{7,0,459},{8,0,606},{139,0,726},{134,11,617},{134,0,979},{6 -,10,389},{7,10,149},{9,10,142},{138,10,94},{5,11,878},{133,11,972},{6,10,8},{7, -10,1881},{8,10,91},{136,11,511},{133,0,612},{132,11,351},{4,0,372},{7,0,482},{8, -0,158},{9,0,602},{9,0,615},{10,0,245},{10,0,678},{10,0,744},{11,0,248},{139,0, -806},{5,0,854},{135,0,1991},{132,11,286},{135,11,344},{7,11,438},{7,11,627},{7, -11,1516},{8,11,40},{9,11,56},{9,11,294},{10,11,30},{10,11,259},{11,11,969},{146, -11,148},{135,0,1492},{5,11,259},{7,11,414},{7,11,854},{142,11,107},{135,10,1746} -,{6,0,833},{134,0,998},{135,10,24},{6,0,750},{135,0,1739},{4,10,503},{135,10, -1661},{5,10,130},{7,10,1314},{9,10,610},{10,10,718},{11,10,601},{11,10,819},{11, -10,946},{140,10,536},{10,10,149},{11,10,280},{142,10,336},{132,11,738},{135,10, -1946},{5,0,195},{135,0,1685},{7,0,1997},{8,0,730},{139,0,1006},{151,11,17},{133, -11,866},{14,0,463},{14,0,470},{150,0,61},{5,0,751},{8,0,266},{11,0,578},{4,10, -392},{135,10,1597},{5,10,433},{9,10,633},{139,10,629},{135,0,821},{6,0,715},{134 -,0,1325},{133,11,116},{6,0,868},{132,11,457},{134,0,959},{6,10,234},{138,11,199} -,{7,0,1053},{7,10,1950},{8,10,680},{11,10,817},{147,10,88},{7,10,1222},{138,10, -386},{5,0,950},{5,0,994},{6,0,351},{134,0,1124},{134,0,1081},{7,0,1595},{6,10,5} -,{11,10,249},{12,10,313},{16,10,66},{145,10,26},{148,0,59},{5,11,527},{6,11,189} -,{135,11,859},{5,10,963},{6,10,1773},{11,11,104},{11,11,554},{15,11,60},{143,11, -125},{135,0,47},{137,0,684},{134,11,116},{134,0,1606},{134,0,777},{7,0,1020},{8, -10,509},{136,10,792},{135,0,1094},{132,0,350},{133,11,487},{4,11,86},{5,11,667}, -{5,11,753},{6,11,316},{6,11,455},{135,11,946},{7,0,1812},{13,0,259},{13,0,356},{ -14,0,242},{147,0,114},{132,10,931},{133,0,967},{4,0,473},{7,0,623},{8,0,808},{9, -0,871},{9,0,893},{11,0,38},{11,0,431},{12,0,112},{12,0,217},{12,0,243},{12,0,562 -},{12,0,663},{12,0,683},{13,0,141},{13,0,197},{13,0,227},{13,0,406},{13,0,487},{ -14,0,156},{14,0,203},{14,0,224},{14,0,256},{18,0,58},{150,0,0},{138,0,286},{7,10 -,943},{139,10,614},{135,10,1837},{150,11,45},{132,0,798},{4,0,222},{7,0,286},{ -136,0,629},{4,11,79},{7,11,1773},{10,11,450},{11,11,589},{13,11,332},{13,11,493} -,{14,11,183},{14,11,334},{14,11,362},{14,11,368},{14,11,376},{14,11,379},{19,11, -90},{19,11,103},{19,11,127},{148,11,90},{5,0,337},{11,0,513},{11,0,889},{11,0, -961},{12,0,461},{13,0,79},{15,0,121},{4,10,90},{5,10,545},{7,10,754},{9,10,186}, -{10,10,72},{10,10,782},{11,10,577},{11,10,610},{12,10,354},{12,10,362},{140,10, -595},{141,0,306},{136,0,146},{7,0,1646},{9,10,329},{11,10,254},{141,11,124},{4,0 -,465},{135,0,1663},{132,0,525},{133,11,663},{10,0,299},{18,0,74},{9,10,187},{11, -10,1016},{145,10,44},{7,0,165},{7,0,919},{4,10,506},{136,10,517},{5,10,295},{135 -,10,1680},{133,11,846},{134,0,1064},{5,11,378},{7,11,1402},{7,11,1414},{8,11,465 -},{9,11,286},{10,11,185},{10,11,562},{10,11,635},{11,11,31},{11,11,393},{12,11, -456},{13,11,312},{18,11,65},{18,11,96},{147,11,89},{132,0,596},{7,10,987},{9,10, -688},{10,10,522},{11,10,788},{140,10,566},{6,0,82},{7,0,138},{7,0,517},{7,0,1741 -},{11,0,238},{4,11,648},{134,10,1775},{7,0,1233},{7,10,700},{7,10,940},{8,10,514 -},{9,10,116},{9,10,535},{10,10,118},{11,10,107},{11,10,148},{11,10,922},{12,10, -254},{12,10,421},{142,10,238},{4,0,962},{6,0,1824},{8,0,894},{12,0,708},{12,0, -725},{14,0,451},{20,0,94},{22,0,59},{150,0,62},{5,11,945},{6,11,1656},{6,11,1787 -},{7,11,167},{8,11,824},{9,11,391},{10,11,375},{139,11,185},{5,0,495},{7,0,834}, -{9,0,733},{139,0,378},{4,10,743},{135,11,1273},{6,0,1204},{7,11,1645},{8,11,352} -,{137,11,249},{139,10,292},{133,0,559},{132,11,152},{9,0,499},{10,0,341},{15,0, -144},{19,0,49},{7,10,1283},{9,10,227},{11,10,325},{11,10,408},{14,10,180},{146, -10,47},{6,0,21},{6,0,1737},{7,0,1444},{136,0,224},{133,11,1006},{7,0,1446},{9,0, -97},{17,0,15},{5,10,81},{7,10,146},{7,10,1342},{8,10,53},{8,10,561},{8,10,694},{ -8,10,754},{9,10,115},{9,10,894},{10,10,462},{10,10,813},{11,10,230},{11,10,657}, -{11,10,699},{11,10,748},{12,10,119},{12,10,200},{12,10,283},{142,10,273},{5,10, -408},{137,10,747},{135,11,431},{135,11,832},{6,0,729},{134,0,953},{4,0,727},{8,0 -,565},{5,11,351},{7,11,264},{136,11,565},{134,0,1948},{5,0,519},{5,11,40},{7,11, -598},{7,11,1638},{8,11,78},{9,11,166},{9,11,640},{9,11,685},{9,11,773},{11,11, -215},{13,11,65},{14,11,172},{14,11,317},{145,11,6},{8,11,60},{9,11,343},{139,11, -769},{137,11,455},{134,0,1193},{140,0,790},{7,11,1951},{8,11,765},{8,11,772},{ -140,11,671},{7,11,108},{8,11,219},{8,11,388},{9,11,639},{9,11,775},{11,11,275},{ -140,11,464},{132,11,468},{7,10,30},{8,10,86},{8,10,315},{8,10,700},{9,10,576},{9 -,10,858},{11,10,310},{11,10,888},{11,10,904},{12,10,361},{141,10,248},{5,11,15}, -{6,11,56},{7,11,1758},{8,11,500},{9,11,730},{11,11,331},{13,11,150},{142,11,282} -,{4,0,402},{7,0,2},{8,0,323},{136,0,479},{138,10,839},{11,0,580},{142,0,201},{5, -0,59},{135,0,672},{137,10,617},{146,0,34},{134,11,1886},{4,0,961},{136,0,896},{6 -,0,1285},{5,11,205},{6,11,438},{137,11,711},{134,10,428},{7,10,524},{8,10,169},{ -8,10,234},{9,10,480},{138,10,646},{148,0,46},{141,0,479},{133,11,534},{6,0,2019} -,{134,10,1648},{4,0,85},{7,0,549},{7,10,1205},{138,10,637},{4,0,663},{5,0,94},{7 -,11,235},{7,11,1475},{15,11,68},{146,11,120},{6,11,443},{9,11,237},{9,11,571},{9 -,11,695},{10,11,139},{11,11,715},{12,11,417},{141,11,421},{132,0,783},{4,0,682}, -{8,0,65},{9,10,39},{10,10,166},{11,10,918},{12,10,635},{20,10,10},{22,10,27},{22 -,10,43},{150,10,52},{6,0,11},{135,0,187},{132,0,522},{4,0,52},{135,0,661},{4,0, -383},{133,0,520},{135,11,546},{11,0,343},{142,0,127},{4,11,578},{7,10,157},{7,11 -,624},{7,11,916},{8,10,279},{10,11,256},{11,11,87},{139,11,703},{134,10,604},{4, -0,281},{5,0,38},{7,0,194},{7,0,668},{7,0,1893},{137,0,397},{7,10,945},{11,10,713 -},{139,10,744},{139,10,1022},{9,0,635},{139,0,559},{5,11,923},{7,11,490},{12,11, -553},{13,11,100},{14,11,118},{143,11,75},{132,0,975},{132,10,567},{137,10,859},{ -7,10,1846},{7,11,1846},{8,10,628},{136,11,628},{148,0,116},{138,11,750},{14,0,51 -},{14,11,51},{15,11,7},{148,11,20},{132,0,858},{134,0,1075},{4,11,924},{133,10, -762},{136,0,535},{133,0,448},{10,10,784},{141,10,191},{133,10,298},{7,0,610},{ -135,0,1501},{7,10,633},{7,10,905},{7,10,909},{7,10,1538},{9,10,767},{140,10,636} -,{4,11,265},{7,11,807},{135,11,950},{5,11,93},{12,11,267},{144,11,26},{136,0,191 -},{139,10,301},{135,10,1970},{135,0,267},{4,0,319},{5,0,699},{138,0,673},{6,0, -336},{7,0,92},{7,0,182},{8,0,453},{8,0,552},{9,0,204},{9,0,285},{10,0,99},{11,0, -568},{11,0,950},{12,0,94},{16,0,20},{16,0,70},{19,0,55},{12,10,644},{144,10,90}, -{6,0,551},{7,0,1308},{7,10,845},{7,11,994},{8,10,160},{137,10,318},{19,11,1},{19 -,11,26},{150,11,9},{7,0,1406},{9,0,218},{141,0,222},{5,0,256},{138,0,69},{5,11, -233},{5,11,320},{6,11,140},{7,11,330},{136,11,295},{6,0,1980},{136,0,952},{4,0, -833},{137,11,678},{133,11,978},{4,11,905},{6,11,1701},{137,11,843},{138,10,735}, -{136,10,76},{17,0,39},{148,0,36},{18,0,81},{146,11,81},{14,0,352},{17,0,53},{18, -0,146},{18,0,152},{19,0,11},{150,0,54},{135,0,634},{138,10,841},{132,0,618},{4,0 -,339},{7,0,259},{17,0,73},{4,11,275},{140,11,376},{132,11,509},{7,11,273},{139, -11,377},{4,0,759},{13,0,169},{137,10,804},{6,10,96},{135,10,1426},{4,10,651},{ -133,10,289},{7,0,1075},{8,10,35},{9,10,511},{10,10,767},{147,10,118},{6,0,649},{ -6,0,670},{136,0,482},{5,0,336},{6,0,341},{6,0,478},{6,0,1763},{136,0,386},{5,11, -802},{7,11,2021},{8,11,805},{14,11,94},{15,11,65},{16,11,4},{16,11,77},{16,11,80 -},{145,11,5},{6,0,1035},{5,11,167},{5,11,899},{6,11,410},{137,11,777},{134,11, -1705},{5,0,924},{133,0,969},{132,10,704},{135,0,73},{135,11,10},{135,10,1078},{5 -,11,11},{6,11,117},{6,11,485},{7,11,1133},{9,11,582},{9,11,594},{11,11,21},{11, -11,818},{12,11,535},{141,11,86},{135,0,1971},{4,11,264},{7,11,1067},{8,11,204},{ -8,11,385},{139,11,953},{6,0,1458},{135,0,1344},{5,0,396},{134,0,501},{4,10,720}, -{133,10,306},{4,0,929},{5,0,799},{8,0,46},{8,0,740},{133,10,431},{7,11,646},{7, -11,1730},{11,11,446},{141,11,178},{7,0,276},{5,10,464},{6,10,236},{7,10,696},{7, -10,914},{7,10,1108},{7,10,1448},{9,10,15},{9,10,564},{10,10,14},{12,10,565},{13, -10,449},{14,10,53},{15,10,13},{16,10,64},{145,10,41},{4,0,892},{133,0,770},{6,10 -,1767},{12,10,194},{145,10,107},{135,0,158},{5,10,840},{138,11,608},{134,0,1432} -,{138,11,250},{8,11,794},{9,11,400},{10,11,298},{142,11,228},{151,0,25},{7,11, -1131},{135,11,1468},{135,0,2001},{9,10,642},{11,10,236},{142,10,193},{4,10,68},{ -5,10,634},{6,10,386},{7,10,794},{8,10,273},{9,10,563},{10,10,105},{10,10,171},{ -11,10,94},{139,10,354},{136,11,724},{132,0,478},{11,11,512},{13,11,205},{19,11, -30},{22,11,36},{151,11,19},{7,0,1461},{140,0,91},{6,11,190},{7,11,768},{135,11, -1170},{4,0,602},{8,0,211},{4,10,95},{7,10,416},{139,10,830},{7,10,731},{13,10,20 -},{143,10,11},{6,0,1068},{135,0,1872},{4,0,13},{5,0,567},{7,0,1498},{9,0,124},{ -11,0,521},{12,0,405},{135,11,1023},{135,0,1006},{132,0,735},{138,0,812},{4,0,170 -},{135,0,323},{6,11,137},{9,11,75},{9,11,253},{10,11,194},{138,11,444},{5,0,304} -,{7,0,1403},{5,10,864},{10,10,648},{11,10,671},{143,10,46},{135,11,1180},{133,10 -,928},{4,0,148},{133,0,742},{11,10,986},{140,10,682},{133,0,523},{135,11,1743},{ -7,0,730},{18,0,144},{19,0,61},{8,10,44},{9,10,884},{10,10,580},{11,10,399},{11, -10,894},{143,10,122},{5,11,760},{7,11,542},{8,11,135},{136,11,496},{136,0,981},{ -133,0,111},{10,0,132},{11,0,191},{11,0,358},{139,0,460},{7,11,319},{7,11,355},{7 -,11,763},{10,11,389},{145,11,43},{134,0,890},{134,0,1420},{136,11,557},{133,10, -518},{133,0,444},{135,0,1787},{135,10,1852},{8,0,123},{15,0,6},{144,0,7},{6,0, -2041},{10,11,38},{139,11,784},{136,0,932},{5,0,937},{135,0,100},{6,0,995},{4,11, -58},{5,11,286},{6,11,319},{7,11,402},{7,11,1254},{7,11,1903},{8,11,356},{140,11, -408},{4,11,389},{9,11,181},{9,11,255},{10,11,8},{10,11,29},{10,11,816},{11,11, -311},{11,11,561},{12,11,67},{141,11,181},{138,0,255},{5,0,138},{4,10,934},{136, -10,610},{4,0,965},{10,0,863},{138,0,898},{10,10,804},{138,10,832},{12,0,631},{8, -10,96},{9,10,36},{10,10,607},{11,10,423},{11,10,442},{12,10,309},{14,10,199},{15 -,10,90},{145,10,110},{134,0,1394},{4,0,652},{8,0,320},{22,0,6},{22,0,16},{9,10, -13},{9,10,398},{9,10,727},{10,10,75},{10,10,184},{10,10,230},{10,10,564},{10,10, -569},{11,10,973},{12,10,70},{12,10,189},{13,10,57},{141,10,257},{6,0,897},{134,0 -,1333},{4,0,692},{133,0,321},{133,11,373},{135,0,922},{5,0,619},{133,0,698},{137 -,10,631},{5,10,345},{135,10,1016},{9,0,957},{9,0,1018},{12,0,828},{12,0,844},{12 -,0,897},{12,0,901},{12,0,943},{15,0,180},{18,0,197},{18,0,200},{18,0,213},{18,0, -214},{146,0,226},{5,0,917},{134,0,1659},{135,0,1100},{134,0,1173},{134,0,1930},{ -5,0,251},{5,0,956},{8,0,268},{9,0,214},{146,0,142},{133,10,673},{137,10,850},{4, -10,287},{133,10,1018},{132,11,672},{5,0,346},{5,0,711},{8,0,390},{11,11,752},{ -139,11,885},{5,10,34},{10,10,724},{12,10,444},{13,10,354},{18,10,32},{23,10,24}, -{23,10,31},{152,10,5},{4,11,710},{134,11,606},{134,0,744},{134,10,382},{133,11, -145},{4,10,329},{7,11,884},{140,11,124},{4,11,467},{5,11,405},{134,11,544},{9,10 -,846},{138,10,827},{133,0,624},{9,11,372},{15,11,2},{19,11,10},{147,11,18},{4,11 -,387},{135,11,1288},{5,0,783},{7,0,1998},{135,0,2047},{132,10,906},{136,10,366}, -{135,11,550},{4,10,123},{4,10,649},{5,10,605},{7,10,1509},{136,10,36},{134,0, -1125},{132,0,594},{133,10,767},{135,11,1227},{136,11,467},{4,11,576},{135,11, -1263},{4,0,268},{7,0,1534},{135,11,1534},{4,10,273},{5,10,658},{5,11,919},{5,10, -995},{134,11,1673},{133,0,563},{134,10,72},{135,10,1345},{4,11,82},{5,11,333},{5 -,11,904},{6,11,207},{7,11,325},{7,11,1726},{8,11,101},{10,11,778},{139,11,220},{ -5,0,37},{6,0,39},{6,0,451},{7,0,218},{7,0,667},{7,0,1166},{7,0,1687},{8,0,662},{ -16,0,2},{133,10,589},{134,0,1332},{133,11,903},{134,0,508},{5,10,117},{6,10,514} -,{6,10,541},{7,10,1164},{7,10,1436},{8,10,220},{8,10,648},{10,10,688},{11,10,560 -},{140,11,147},{6,11,555},{135,11,485},{133,10,686},{7,0,453},{7,0,635},{7,0,796 -},{8,0,331},{9,0,330},{9,0,865},{10,0,119},{10,0,235},{11,0,111},{11,0,129},{11, -0,240},{12,0,31},{12,0,66},{12,0,222},{12,0,269},{12,0,599},{12,0,684},{12,0,689 -},{12,0,691},{142,0,345},{135,0,1834},{4,11,705},{7,11,615},{138,11,251},{136,11 -,345},{137,0,527},{6,0,98},{7,0,702},{135,0,991},{11,0,576},{14,0,74},{7,10,196} -,{10,10,765},{11,10,347},{11,10,552},{11,10,790},{12,10,263},{13,10,246},{13,10, -270},{13,10,395},{14,10,176},{14,10,190},{14,10,398},{14,10,412},{15,10,32},{15, -10,63},{16,10,88},{147,10,105},{134,11,90},{13,0,84},{141,0,122},{6,0,37},{7,0, -299},{7,0,1666},{8,0,195},{8,0,316},{9,0,178},{9,0,276},{9,0,339},{9,0,536},{10, -0,102},{10,0,362},{10,0,785},{11,0,55},{11,0,149},{11,0,773},{13,0,416},{13,0, -419},{14,0,38},{14,0,41},{142,0,210},{5,10,381},{135,10,1792},{7,11,813},{12,11, -497},{141,11,56},{7,10,616},{138,10,413},{133,0,645},{6,11,125},{135,11,1277},{ -132,0,290},{6,0,70},{7,0,1292},{10,0,762},{139,0,288},{6,10,120},{7,10,1188},{7, -10,1710},{8,10,286},{9,10,667},{11,10,592},{139,10,730},{135,11,1784},{7,0,1315} -,{135,11,1315},{134,0,1955},{135,10,1146},{7,0,131},{7,0,422},{8,0,210},{140,0, -573},{4,10,352},{135,10,687},{139,0,797},{143,0,38},{14,0,179},{15,0,151},{150,0 -,11},{7,0,488},{4,10,192},{5,10,49},{6,10,200},{6,10,293},{134,10,1696},{132,0, -936},{135,11,703},{6,11,160},{7,11,1106},{9,11,770},{10,11,618},{11,11,112},{140 -,11,413},{5,0,453},{134,0,441},{135,0,595},{132,10,650},{132,10,147},{6,0,991},{ -6,0,1182},{12,11,271},{145,11,109},{133,10,934},{140,11,221},{132,0,653},{7,0, -505},{135,0,523},{134,0,903},{135,11,479},{7,11,304},{9,11,646},{9,11,862},{10, -11,262},{11,11,696},{12,11,208},{15,11,79},{147,11,108},{146,0,80},{135,11,981}, -{142,0,432},{132,0,314},{137,11,152},{7,0,1368},{8,0,232},{8,0,361},{10,0,682},{ -138,0,742},{135,11,1586},{9,0,534},{4,11,434},{11,11,663},{12,11,210},{13,11,166 -},{13,11,310},{14,11,373},{147,11,43},{7,11,1091},{135,11,1765},{6,11,550},{135, -11,652},{137,0,27},{142,0,12},{4,10,637},{5,11,553},{7,11,766},{138,11,824},{7, -11,737},{8,11,298},{136,11,452},{7,0,736},{139,0,264},{134,0,1657},{133,11,292}, -{138,11,135},{6,0,844},{134,0,1117},{135,0,127},{9,10,867},{138,10,837},{6,0, -1184},{134,0,1208},{134,0,1294},{136,0,364},{6,0,1415},{7,0,1334},{11,0,125},{6, -10,170},{7,11,393},{8,10,395},{8,10,487},{10,11,603},{11,11,206},{141,10,147},{ -137,11,748},{4,11,912},{137,11,232},{4,10,535},{136,10,618},{137,0,792},{7,11, -1973},{136,11,716},{135,11,98},{5,0,909},{9,0,849},{138,0,805},{4,0,630},{132,0, -699},{5,11,733},{14,11,103},{150,10,23},{12,11,158},{18,11,8},{19,11,62},{20,11, -6},{22,11,4},{23,11,2},{151,11,9},{132,0,968},{132,10,778},{132,10,46},{5,10,811 -},{6,10,1679},{6,10,1714},{135,10,2032},{6,0,1446},{7,10,1458},{9,10,407},{139, -10,15},{7,0,206},{7,0,397},{7,0,621},{7,0,640},{8,0,124},{8,0,619},{9,0,305},{9, -0,643},{10,0,264},{10,0,628},{11,0,40},{12,0,349},{13,0,134},{13,0,295},{14,0, -155},{15,0,120},{18,0,105},{6,10,34},{7,10,1089},{8,10,708},{8,10,721},{9,10,363 -},{148,10,98},{4,0,262},{5,0,641},{135,0,342},{137,11,72},{4,0,99},{6,0,250},{6, -0,346},{8,0,127},{138,0,81},{132,0,915},{5,0,75},{9,0,517},{10,0,470},{12,0,155} -,{141,0,224},{132,10,462},{11,11,600},{11,11,670},{141,11,245},{142,0,83},{5,10, -73},{6,10,23},{134,10,338},{6,0,1031},{139,11,923},{7,11,164},{7,11,1571},{9,11, -107},{140,11,225},{134,0,1470},{133,0,954},{6,0,304},{8,0,418},{10,0,345},{11,0, -341},{139,0,675},{9,0,410},{139,0,425},{4,11,27},{5,11,484},{5,11,510},{6,11,434 -},{7,11,1000},{7,11,1098},{8,11,2},{136,11,200},{134,0,734},{140,11,257},{7,10, -725},{8,10,498},{139,10,268},{134,0,1822},{135,0,1798},{135,10,773},{132,11,460} -,{4,11,932},{133,11,891},{134,0,14},{132,10,583},{7,10,1462},{8,11,625},{139,10, -659},{5,0,113},{6,0,243},{6,0,1708},{7,0,1865},{11,0,161},{16,0,37},{17,0,99},{ -133,10,220},{134,11,76},{5,11,461},{135,11,1925},{140,0,69},{8,11,92},{137,11, -221},{139,10,803},{132,10,544},{4,0,274},{134,0,922},{132,0,541},{5,0,627},{6,10 -,437},{6,10,564},{11,10,181},{141,10,183},{135,10,1192},{7,0,166},{132,11,763},{ -133,11,253},{134,0,849},{9,11,73},{10,11,110},{14,11,185},{145,11,119},{5,11,212 -},{12,11,35},{141,11,382},{133,0,717},{137,0,304},{136,0,600},{133,0,654},{6,0, -273},{10,0,188},{13,0,377},{146,0,77},{4,10,790},{5,10,273},{134,10,394},{132,0, -543},{135,0,410},{11,0,98},{11,0,524},{141,0,87},{132,0,941},{135,11,1175},{4,0, -250},{7,0,1612},{11,0,186},{12,0,133},{6,10,127},{7,10,1511},{8,10,613},{12,10, -495},{12,10,586},{12,10,660},{12,10,668},{14,10,385},{15,10,118},{17,10,20},{146 -,10,98},{6,0,1785},{133,11,816},{134,0,1339},{7,0,961},{7,0,1085},{7,0,1727},{8, -0,462},{6,10,230},{135,11,1727},{9,0,636},{135,10,1954},{132,0,780},{5,11,869},{ -5,11,968},{6,11,1626},{8,11,734},{136,11,784},{4,11,542},{6,11,1716},{6,11,1727} -,{7,11,1082},{7,11,1545},{8,11,56},{8,11,118},{8,11,412},{8,11,564},{9,11,888},{ -9,11,908},{10,11,50},{10,11,423},{11,11,685},{11,11,697},{11,11,933},{12,11,299} -,{13,11,126},{13,11,136},{13,11,170},{141,11,190},{134,11,226},{4,11,232},{9,11, -202},{10,11,474},{140,11,433},{137,11,500},{5,0,529},{136,10,68},{132,10,654},{4 -,10,156},{7,10,998},{7,10,1045},{7,10,1860},{9,10,48},{9,10,692},{11,10,419},{ -139,10,602},{7,0,1276},{8,0,474},{9,0,652},{6,11,108},{7,11,1003},{7,11,1181},{ -136,11,343},{7,11,1264},{7,11,1678},{11,11,945},{12,11,341},{12,11,471},{140,11, -569},{134,11,1712},{5,0,948},{12,0,468},{19,0,96},{148,0,24},{4,11,133},{7,11, -711},{7,11,1298},{7,11,1585},{135,11,1929},{6,0,753},{140,0,657},{139,0,941},{6, -11,99},{7,11,1808},{145,11,57},{6,11,574},{7,11,428},{7,11,1250},{10,11,669},{11 -,11,485},{11,11,840},{12,11,300},{142,11,250},{4,0,532},{5,0,706},{135,0,662},{5 -,0,837},{6,0,1651},{139,0,985},{7,0,1861},{9,10,197},{10,10,300},{12,10,473},{13 -,10,90},{141,10,405},{137,11,252},{6,11,323},{135,11,1564},{4,0,330},{4,0,863},{ -7,0,933},{7,0,2012},{8,0,292},{7,11,461},{8,11,775},{138,11,435},{132,10,606},{4 -,11,655},{7,11,850},{17,11,75},{146,11,137},{135,0,767},{7,10,1978},{136,10,676} -,{132,0,641},{135,11,1559},{134,0,1233},{137,0,242},{17,0,114},{4,10,361},{133, -10,315},{137,0,883},{132,10,461},{138,0,274},{134,0,2008},{134,0,1794},{4,0,703} -,{135,0,207},{12,0,285},{132,10,472},{132,0,571},{5,0,873},{5,0,960},{8,0,823},{ -9,0,881},{136,11,577},{7,0,617},{10,0,498},{11,0,501},{12,0,16},{140,0,150},{138 -,10,747},{132,0,431},{133,10,155},{11,0,283},{11,0,567},{7,10,163},{8,10,319},{9 -,10,402},{10,10,24},{10,10,681},{11,10,200},{12,10,253},{12,10,410},{142,10,219} -,{4,11,413},{5,11,677},{8,11,432},{140,11,280},{9,0,401},{5,10,475},{7,10,1780}, -{11,10,297},{11,10,558},{14,10,322},{147,10,76},{6,0,781},{9,0,134},{10,0,2},{10 -,0,27},{10,0,333},{11,0,722},{143,0,1},{5,0,33},{6,0,470},{139,0,424},{135,0, -2006},{12,0,783},{135,10,1956},{136,0,274},{135,0,1882},{132,0,794},{135,0,1848} -,{5,10,944},{134,10,1769},{6,0,47},{7,0,90},{7,0,664},{7,0,830},{7,0,1380},{7,0, -2025},{8,0,448},{136,0,828},{132,10,144},{134,0,1199},{4,11,395},{139,11,762},{ -135,11,1504},{9,0,417},{137,0,493},{9,11,174},{10,11,164},{11,11,440},{11,11,841 -},{143,11,98},{134,11,426},{139,11,1002},{134,0,295},{134,0,816},{6,10,247},{137 -,10,555},{133,0,1019},{4,0,620},{5,11,476},{10,10,280},{138,10,797},{139,0,464}, -{5,11,76},{6,11,458},{6,11,497},{7,11,764},{7,11,868},{9,11,658},{10,11,594},{11 -,11,173},{11,11,566},{12,11,20},{12,11,338},{141,11,200},{134,0,208},{4,11,526}, -{7,11,1029},{135,11,1054},{132,11,636},{6,11,233},{7,11,660},{7,11,1124},{17,11, -31},{19,11,22},{151,11,14},{10,0,442},{133,10,428},{10,0,930},{140,0,778},{6,0, -68},{7,0,448},{7,0,1629},{7,0,1769},{7,0,1813},{8,0,442},{8,0,516},{9,0,710},{10 -,0,282},{10,0,722},{7,10,1717},{138,10,546},{134,0,1128},{11,0,844},{12,0,104},{ -140,0,625},{4,11,432},{135,11,824},{138,10,189},{133,0,787},{133,10,99},{4,11, -279},{7,11,301},{137,11,362},{8,0,491},{4,10,397},{136,10,555},{4,11,178},{133, -11,399},{134,0,711},{144,0,9},{4,0,403},{5,0,441},{7,0,450},{10,0,840},{11,0,101 -},{12,0,193},{141,0,430},{135,11,1246},{12,10,398},{20,10,39},{21,10,11},{150,10 -,41},{4,10,485},{7,10,353},{135,10,1523},{6,10,366},{7,10,1384},{7,10,1601},{135 -,11,1912},{7,0,396},{10,0,160},{135,11,396},{137,10,282},{134,11,1692},{4,10,157 -},{5,10,471},{6,11,202},{10,11,448},{11,11,208},{12,11,360},{17,11,117},{17,11, -118},{18,11,27},{148,11,67},{133,0,679},{137,0,326},{136,10,116},{7,11,872},{10, -11,516},{139,11,167},{132,11,224},{5,11,546},{7,11,35},{8,11,11},{8,11,12},{9,11 -,315},{9,11,533},{10,11,802},{11,11,166},{12,11,525},{142,11,243},{7,0,1128},{ -135,11,1920},{5,11,241},{8,11,242},{9,11,451},{10,11,667},{11,11,598},{140,11, -429},{6,0,737},{5,10,160},{7,10,363},{7,10,589},{10,10,170},{141,10,55},{135,0, -1796},{142,11,254},{4,0,574},{7,0,350},{7,0,1024},{8,0,338},{9,0,677},{138,0,808 -},{134,0,1096},{137,11,516},{7,0,405},{10,0,491},{4,10,108},{4,11,366},{139,10, -498},{11,11,337},{142,11,303},{134,11,1736},{7,0,1081},{140,11,364},{7,10,1005}, -{140,10,609},{7,0,1676},{4,10,895},{133,10,772},{135,0,2037},{6,0,1207},{11,11, -916},{142,11,419},{14,11,140},{148,11,41},{6,11,331},{136,11,623},{9,0,944},{9,0 -,969},{9,0,1022},{12,0,913},{12,0,936},{15,0,177},{15,0,193},{4,10,926},{133,10, -983},{5,0,354},{135,11,506},{8,0,598},{9,0,664},{138,0,441},{4,11,640},{133,11, -513},{137,0,297},{132,10,538},{6,10,294},{7,10,1267},{136,10,624},{7,0,1772},{7, -11,1888},{8,11,289},{11,11,45},{12,11,278},{140,11,537},{135,10,1325},{138,0,751 -},{141,0,37},{134,0,1828},{132,10,757},{132,11,394},{6,0,257},{135,0,1522},{4,0, -582},{9,0,191},{135,11,1931},{7,11,574},{7,11,1719},{137,11,145},{132,11,658},{ -10,0,790},{132,11,369},{9,11,781},{10,11,144},{11,11,385},{13,11,161},{13,11,228 -},{13,11,268},{148,11,107},{8,0,469},{10,0,47},{136,11,374},{6,0,306},{7,0,1140} -,{7,0,1340},{8,0,133},{138,0,449},{139,0,1011},{7,10,1875},{139,10,124},{4,11, -344},{6,11,498},{139,11,323},{137,0,299},{132,0,837},{133,11,906},{5,0,329},{8,0 -,260},{138,0,10},{134,0,1320},{4,0,657},{146,0,158},{135,0,1191},{152,0,7},{6,0, -1939},{8,0,974},{138,0,996},{135,0,1665},{11,11,126},{139,11,287},{143,0,8},{14, -11,149},{14,11,399},{143,11,57},{5,0,66},{7,0,1896},{136,0,288},{7,0,175},{10,0, -494},{5,10,150},{8,10,603},{9,10,593},{9,10,634},{10,10,173},{11,10,462},{11,10, -515},{13,10,216},{13,10,288},{142,10,400},{134,0,1643},{136,11,21},{4,0,21},{5,0 -,91},{5,0,648},{5,0,750},{5,0,781},{6,0,54},{6,0,112},{6,0,402},{6,0,1732},{7,0, -315},{7,0,749},{7,0,1427},{7,0,1900},{9,0,78},{9,0,508},{10,0,611},{10,0,811},{ -11,0,510},{11,0,728},{13,0,36},{14,0,39},{16,0,83},{17,0,124},{148,0,30},{4,0, -668},{136,0,570},{10,0,322},{10,0,719},{139,0,407},{135,11,1381},{136,11,193},{ -12,10,108},{141,10,291},{132,11,616},{136,11,692},{8,0,125},{8,0,369},{8,0,524}, -{10,0,486},{11,0,13},{11,0,381},{11,0,736},{11,0,766},{11,0,845},{13,0,114},{13, -0,292},{142,0,47},{134,0,1247},{6,0,1684},{6,0,1731},{7,0,356},{8,0,54},{8,0,221 -},{9,0,225},{9,0,356},{10,0,77},{10,0,446},{10,0,731},{12,0,404},{141,0,491},{ -135,10,1777},{4,11,305},{4,10,493},{144,10,55},{4,0,951},{6,0,1809},{6,0,1849},{ -8,0,846},{8,0,866},{8,0,899},{10,0,896},{12,0,694},{142,0,468},{5,11,214},{7,11, -603},{8,11,611},{9,11,686},{10,11,88},{11,11,459},{11,11,496},{12,11,463},{12,11 -,590},{13,11,0},{142,11,214},{132,0,411},{4,0,80},{133,0,44},{140,11,74},{143,0, -31},{7,0,669},{6,10,568},{7,10,1804},{8,10,362},{8,10,410},{8,10,830},{9,10,514} -,{11,10,649},{142,10,157},{7,0,673},{134,11,1703},{132,10,625},{134,0,1303},{5,0 -,299},{135,0,1083},{138,0,704},{6,0,275},{7,0,408},{6,10,158},{7,10,129},{7,10, -181},{8,10,276},{8,10,377},{10,10,523},{11,10,816},{12,10,455},{13,10,303},{142, -10,135},{4,0,219},{7,0,367},{7,0,1713},{7,0,1761},{9,0,86},{9,0,537},{10,0,165}, -{12,0,219},{140,0,561},{8,0,216},{4,10,1},{4,11,737},{6,11,317},{7,10,1143},{7, -10,1463},{9,10,207},{9,10,390},{9,10,467},{10,11,98},{11,11,294},{11,10,836},{12 -,11,60},{12,11,437},{13,11,64},{13,11,380},{142,11,430},{6,11,1758},{8,11,520},{ -9,11,345},{9,11,403},{142,11,350},{5,11,47},{10,11,242},{138,11,579},{5,11,139}, -{7,11,1168},{138,11,539},{135,0,1319},{4,10,295},{4,10,723},{5,10,895},{7,10, -1031},{8,10,199},{8,10,340},{9,10,153},{9,10,215},{10,10,21},{10,10,59},{10,10, -80},{10,10,224},{10,10,838},{11,10,229},{11,10,652},{12,10,192},{13,10,146},{142 -,10,91},{140,0,428},{137,10,51},{133,0,514},{5,10,309},{140,10,211},{6,0,1010},{ -5,10,125},{8,10,77},{138,10,15},{4,0,55},{5,0,301},{6,0,571},{142,0,49},{146,0, -102},{136,11,370},{4,11,107},{7,11,613},{8,11,358},{8,11,439},{8,11,504},{9,11, -501},{10,11,383},{139,11,477},{132,11,229},{133,0,364},{133,10,439},{4,11,903},{ -135,11,1816},{11,0,379},{140,10,76},{4,0,76},{4,0,971},{7,0,1550},{9,0,306},{9,0 -,430},{9,0,663},{10,0,683},{10,0,921},{11,0,427},{11,0,753},{12,0,334},{12,0,442 -},{14,0,258},{14,0,366},{143,0,131},{137,0,52},{4,11,47},{6,11,373},{7,11,452},{ -7,11,543},{7,11,1714},{7,11,1856},{9,11,6},{11,11,257},{139,11,391},{4,10,8},{7, -10,1152},{7,10,1153},{7,10,1715},{9,10,374},{10,10,478},{139,10,648},{4,11,785}, -{133,11,368},{135,10,1099},{135,11,860},{5,11,980},{134,11,1754},{134,0,1258},{6 -,0,1058},{6,0,1359},{7,11,536},{7,11,1331},{136,11,143},{4,0,656},{135,0,779},{ -136,10,87},{5,11,19},{6,11,533},{146,11,126},{7,0,144},{138,10,438},{5,11,395},{ -5,11,951},{134,11,1776},{135,0,1373},{7,0,554},{7,0,605},{141,0,10},{4,10,69},{5 -,10,122},{9,10,656},{138,10,464},{5,10,849},{134,10,1633},{5,0,838},{5,0,841},{ -134,0,1649},{133,0,1012},{139,10,499},{7,10,476},{7,10,1592},{138,10,87},{6,0, -251},{7,0,365},{7,0,1357},{7,0,1497},{8,0,154},{141,0,281},{132,11,441},{132,11, -695},{7,11,497},{9,11,387},{147,11,81},{133,0,340},{14,10,283},{142,11,283},{134 -,0,810},{135,11,1894},{139,0,495},{5,11,284},{6,11,49},{6,11,350},{7,11,1},{7,11 -,377},{7,11,1693},{8,11,18},{8,11,678},{9,11,161},{9,11,585},{9,11,671},{9,11, -839},{11,11,912},{141,11,427},{5,10,859},{7,10,1160},{8,10,107},{9,10,291},{9,10 -,439},{10,10,663},{11,10,609},{140,10,197},{8,0,261},{9,0,144},{9,0,466},{10,0, -370},{12,0,470},{13,0,144},{142,0,348},{137,0,897},{6,0,248},{9,0,546},{10,0,535 -},{11,0,681},{141,0,135},{4,0,358},{135,0,1496},{134,0,567},{136,0,445},{4,10, -117},{6,10,372},{7,10,1905},{142,10,323},{4,10,722},{139,10,471},{6,0,697},{134, -0,996},{7,11,2007},{9,11,101},{9,11,450},{10,11,66},{10,11,842},{11,11,536},{140 -,11,587},{132,0,577},{134,0,1336},{9,10,5},{12,10,216},{12,10,294},{12,10,298},{ -12,10,400},{12,10,518},{13,10,229},{143,10,139},{6,0,174},{138,0,917},{134,10, -1774},{5,10,12},{7,10,375},{9,10,88},{9,10,438},{11,11,62},{139,10,270},{134,11, -1766},{6,11,0},{7,11,84},{7,10,816},{7,10,1241},{9,10,283},{9,10,520},{10,10,213 -},{10,10,307},{10,10,463},{10,10,671},{10,10,746},{11,10,401},{11,10,794},{11,11 -,895},{12,10,517},{17,11,11},{18,10,107},{147,10,115},{5,0,878},{133,0,972},{6, -11,1665},{7,11,256},{7,11,1388},{138,11,499},{4,10,258},{136,10,639},{4,11,22},{ -5,11,10},{6,10,22},{7,11,848},{7,10,903},{7,10,1963},{8,11,97},{138,10,577},{5, -10,681},{136,10,782},{133,11,481},{132,0,351},{4,10,664},{5,10,804},{139,10,1013 -},{6,11,134},{7,11,437},{7,11,959},{9,11,37},{14,11,285},{14,11,371},{144,11,60} -,{7,11,486},{8,11,155},{11,11,93},{140,11,164},{132,0,286},{7,0,438},{7,0,627},{ -7,0,1516},{8,0,40},{9,0,56},{9,0,294},{10,0,30},{11,0,969},{11,0,995},{146,0,148 -},{5,11,591},{135,11,337},{134,0,1950},{133,10,32},{138,11,500},{5,11,380},{5,11 -,650},{136,11,310},{4,11,364},{7,11,1156},{7,11,1187},{137,11,409},{4,0,738},{ -134,11,482},{4,11,781},{6,11,487},{7,11,926},{8,11,263},{139,11,500},{135,11,418 -},{6,0,2047},{10,0,969},{4,10,289},{7,10,629},{7,10,1698},{7,10,1711},{140,10, -215},{6,10,450},{136,10,109},{134,0,818},{136,10,705},{133,0,866},{4,11,94},{135 -,11,1265},{132,11,417},{134,0,1467},{135,10,1238},{4,0,972},{6,0,1851},{134,0, -1857},{134,0,355},{133,0,116},{132,0,457},{135,11,1411},{4,11,408},{4,11,741},{ -135,11,500},{134,10,26},{142,11,137},{5,0,527},{6,0,189},{7,0,859},{136,0,267},{ -11,0,104},{11,0,554},{15,0,60},{143,0,125},{134,0,1613},{4,10,414},{5,10,467},{9 -,10,654},{10,10,451},{12,10,59},{141,10,375},{135,10,17},{134,0,116},{135,11,541 -},{135,10,955},{6,11,73},{135,11,177},{133,11,576},{134,0,886},{133,0,487},{4,0, -86},{5,0,667},{5,0,753},{6,0,316},{6,0,455},{135,0,946},{142,11,231},{150,0,45}, -{134,0,863},{134,0,1953},{6,10,280},{10,10,502},{11,10,344},{140,10,38},{4,0,79} -,{7,0,1773},{10,0,450},{11,0,589},{13,0,332},{13,0,493},{14,0,183},{14,0,334},{ -14,0,362},{14,0,368},{14,0,376},{14,0,379},{19,0,90},{19,0,103},{19,0,127},{148, -0,90},{5,10,45},{7,10,1161},{11,10,448},{11,10,880},{13,10,139},{13,10,407},{15, -10,16},{17,10,95},{18,10,66},{18,10,88},{18,10,123},{149,10,7},{136,10,777},{4, -10,410},{135,10,521},{135,10,1778},{135,11,538},{142,0,381},{133,11,413},{134,0, -1142},{6,0,1189},{136,11,495},{5,0,663},{6,0,1962},{134,0,2003},{7,11,54},{8,11, -312},{10,11,191},{10,11,614},{140,11,567},{132,10,436},{133,0,846},{10,0,528},{ -11,0,504},{7,10,1587},{135,10,1707},{5,0,378},{8,0,465},{9,0,286},{10,0,185},{10 -,0,562},{10,0,635},{11,0,31},{11,0,393},{13,0,312},{18,0,65},{18,0,96},{147,0,89 -},{7,0,899},{14,0,325},{6,11,468},{7,11,567},{7,11,1478},{8,11,530},{142,11,290} -,{7,0,1880},{9,0,680},{139,0,798},{134,0,1770},{132,0,648},{150,11,35},{5,0,945} -,{6,0,1656},{6,0,1787},{7,0,167},{8,0,824},{9,0,391},{10,0,375},{139,0,185},{6, -11,484},{135,11,822},{134,0,2046},{7,0,1645},{8,0,352},{137,0,249},{132,0,152},{ -6,0,611},{135,0,1733},{6,11,1724},{135,11,2022},{133,0,1006},{141,11,96},{5,0, -420},{135,0,1449},{146,11,149},{135,0,832},{135,10,663},{133,0,351},{5,0,40},{7, -0,598},{7,0,1638},{8,0,78},{9,0,166},{9,0,640},{9,0,685},{9,0,773},{11,0,215},{ -13,0,65},{14,0,172},{14,0,317},{145,0,6},{8,0,60},{9,0,343},{139,0,769},{134,0, -1354},{132,0,724},{137,0,745},{132,11,474},{7,0,1951},{8,0,765},{8,0,772},{140,0 -,671},{7,0,108},{8,0,219},{8,0,388},{9,0,775},{11,0,275},{140,0,464},{137,0,639} -,{135,10,503},{133,11,366},{5,0,15},{6,0,56},{7,0,1758},{8,0,500},{9,0,730},{11, -0,331},{13,0,150},{14,0,282},{5,11,305},{9,11,560},{141,11,208},{4,10,113},{5,10 -,163},{5,10,735},{7,10,1009},{9,10,9},{9,10,771},{12,10,90},{13,10,138},{13,10, -410},{143,10,128},{4,10,324},{138,10,104},{135,11,466},{142,11,27},{134,0,1886}, -{5,0,205},{6,0,438},{9,0,711},{4,11,480},{6,11,167},{6,11,302},{6,11,1642},{7,11 -,130},{7,11,656},{7,11,837},{7,11,1547},{7,11,1657},{8,11,429},{9,11,228},{10,11 -,643},{13,11,289},{13,11,343},{147,11,101},{134,0,865},{6,0,2025},{136,0,965},{7 -,11,278},{10,11,739},{11,11,708},{141,11,348},{133,0,534},{135,11,1922},{137,0, -691},{4,10,935},{133,10,823},{6,0,443},{9,0,237},{9,0,571},{9,0,695},{10,0,139}, -{11,0,715},{12,0,417},{141,0,421},{5,10,269},{7,10,434},{7,10,891},{8,10,339},{9 -,10,702},{11,10,594},{11,10,718},{145,10,100},{6,0,1555},{7,0,878},{9,10,485},{ -141,10,264},{134,10,1713},{7,10,1810},{11,10,866},{12,10,103},{141,10,495},{135, -10,900},{6,0,1410},{9,11,316},{139,11,256},{4,0,995},{135,0,1033},{132,0,578},{ -10,0,881},{12,0,740},{12,0,743},{140,0,759},{132,0,822},{133,0,923},{142,10,143} -,{135,11,1696},{6,11,363},{7,11,1955},{136,11,725},{132,0,924},{133,0,665},{135, -10,2029},{135,0,1901},{4,0,265},{6,0,1092},{6,0,1417},{7,0,807},{135,0,950},{5,0 -,93},{12,0,267},{141,0,498},{135,0,1451},{5,11,813},{135,11,2046},{5,10,625},{ -135,10,1617},{135,0,747},{6,0,788},{137,0,828},{7,0,184},{11,0,307},{11,0,400},{ -15,0,130},{5,11,712},{7,11,1855},{8,10,425},{8,10,693},{9,10,720},{10,10,380},{ -10,10,638},{11,11,17},{11,10,473},{12,10,61},{13,11,321},{144,11,67},{135,0,198} -,{6,11,320},{7,11,781},{7,11,1921},{9,11,55},{10,11,186},{10,11,273},{10,11,664} -,{10,11,801},{11,11,996},{11,11,997},{13,11,157},{142,11,170},{136,11,271},{135, -0,994},{7,11,103},{7,11,863},{11,11,184},{14,11,299},{145,11,62},{11,10,551},{ -142,10,159},{5,0,233},{5,0,320},{6,0,140},{8,0,295},{8,0,615},{136,11,615},{133, -0,978},{4,0,905},{6,0,1701},{137,0,843},{132,10,168},{4,0,974},{8,0,850},{12,0, -709},{12,0,768},{140,0,786},{135,10,91},{152,0,6},{138,10,532},{135,10,1884},{ -132,0,509},{6,0,1307},{135,0,273},{5,11,77},{7,11,1455},{10,11,843},{19,11,73},{ -150,11,5},{132,11,458},{135,11,1420},{6,11,109},{138,11,382},{6,0,201},{6,11,330 -},{7,10,70},{7,11,1084},{10,10,240},{11,11,142},{147,10,93},{7,0,1041},{140,11, -328},{133,11,354},{134,0,1040},{133,0,693},{134,0,774},{139,0,234},{132,0,336},{ -7,0,1399},{139,10,392},{20,0,22},{148,11,22},{5,0,802},{7,0,2021},{136,0,805},{5 -,0,167},{5,0,899},{6,0,410},{137,0,777},{137,0,789},{134,0,1705},{7,10,655},{135 -,10,1844},{4,10,145},{6,10,176},{7,10,395},{137,10,562},{132,10,501},{135,0,10}, -{5,0,11},{6,0,117},{6,0,485},{7,0,1133},{9,0,582},{9,0,594},{10,0,82},{11,0,21}, -{11,0,818},{12,0,535},{13,0,86},{20,0,91},{23,0,13},{134,10,509},{4,0,264},{7,0, -1067},{8,0,204},{8,0,385},{139,0,953},{139,11,737},{138,0,56},{134,0,1917},{133, -0,470},{10,11,657},{14,11,297},{142,11,361},{135,11,412},{7,0,1198},{7,11,1198}, -{8,11,556},{14,11,123},{14,11,192},{143,11,27},{7,11,1985},{14,11,146},{15,11,42 -},{16,11,23},{17,11,86},{146,11,17},{11,0,1015},{136,11,122},{4,10,114},{9,10, -492},{13,10,462},{142,10,215},{4,10,77},{5,10,361},{6,10,139},{6,10,401},{6,10, -404},{7,10,413},{7,10,715},{7,10,1716},{11,10,279},{12,10,179},{12,10,258},{13, -10,244},{142,10,358},{134,10,1717},{7,10,1061},{8,10,82},{11,10,250},{12,10,420} -,{141,10,184},{133,0,715},{135,10,724},{9,0,919},{9,0,922},{9,0,927},{9,0,933},{ -9,0,962},{9,0,1000},{9,0,1002},{9,0,1021},{12,0,890},{12,0,907},{12,0,930},{15,0 -,207},{15,0,228},{15,0,238},{149,0,61},{8,0,794},{9,0,400},{10,0,298},{142,0,228 -},{5,11,430},{5,11,932},{6,11,131},{7,11,417},{9,11,522},{11,11,314},{141,11,390 -},{132,0,867},{8,0,724},{132,11,507},{137,11,261},{4,11,343},{133,11,511},{6,0, -190},{7,0,768},{135,0,1170},{6,10,513},{135,10,1052},{7,11,455},{138,11,591},{ -134,0,1066},{137,10,899},{14,0,67},{147,0,60},{4,0,948},{18,0,174},{146,0,176},{ -135,0,1023},{7,10,1417},{12,10,382},{17,10,48},{152,10,12},{134,11,575},{132,0, -764},{6,10,545},{7,10,565},{7,10,1669},{10,10,114},{11,10,642},{140,10,618},{6,0 -,137},{9,0,75},{9,0,253},{10,0,194},{138,0,444},{4,0,756},{133,10,5},{8,0,1008}, -{135,10,192},{132,0,842},{11,0,643},{12,0,115},{136,10,763},{139,0,67},{133,10, -759},{4,0,821},{5,0,760},{7,0,542},{8,0,135},{8,0,496},{135,11,580},{7,10,370},{ -7,10,1007},{7,10,1177},{135,10,1565},{135,10,1237},{140,0,736},{7,0,319},{7,0, -355},{7,0,763},{10,0,389},{145,0,43},{8,11,333},{138,11,182},{4,10,87},{5,10,250 -},{141,10,298},{138,0,786},{134,0,2044},{8,11,330},{140,11,477},{135,11,1338},{ -132,11,125},{134,0,1030},{134,0,1083},{132,11,721},{135,10,814},{7,11,776},{8,11 -,145},{147,11,56},{134,0,1226},{4,10,57},{7,10,1195},{7,10,1438},{7,10,1548},{7, -10,1835},{7,10,1904},{9,10,757},{10,10,604},{139,10,519},{7,11,792},{8,11,147},{ -10,11,821},{139,11,1021},{137,11,797},{4,0,58},{5,0,286},{6,0,319},{7,0,402},{7, -0,1254},{7,0,1903},{8,0,356},{140,0,408},{4,0,389},{4,0,815},{9,0,181},{9,0,255} -,{10,0,8},{10,0,29},{10,0,816},{11,0,311},{11,0,561},{12,0,67},{141,0,181},{7,11 -,1472},{135,11,1554},{7,11,1071},{7,11,1541},{7,11,1767},{7,11,1806},{7,11,1999} -,{9,11,248},{10,11,400},{11,11,162},{11,11,178},{11,11,242},{12,11,605},{15,11, -26},{144,11,44},{5,11,168},{5,11,930},{8,11,74},{9,11,623},{12,11,500},{12,11, -579},{13,11,41},{143,11,93},{6,11,220},{7,11,1101},{141,11,105},{5,0,474},{7,0, -507},{4,10,209},{7,11,507},{135,10,902},{132,0,427},{6,0,413},{7,10,335},{7,10, -1437},{7,10,1668},{8,10,553},{8,10,652},{8,10,656},{9,10,558},{11,10,743},{149, -10,18},{132,0,730},{6,11,19},{7,11,1413},{139,11,428},{133,0,373},{132,10,559},{ -7,11,96},{8,11,401},{137,11,896},{7,0,799},{7,0,1972},{5,10,1017},{138,10,511},{ -135,0,1793},{7,11,1961},{7,11,1965},{8,11,702},{136,11,750},{8,11,150},{8,11,737 -},{140,11,366},{132,0,322},{133,10,709},{8,11,800},{9,11,148},{9,11,872},{9,11, -890},{11,11,309},{11,11,1001},{13,11,267},{141,11,323},{134,10,1745},{7,0,290},{ -136,10,206},{7,0,1651},{145,0,89},{139,0,2},{132,0,672},{6,0,1860},{8,0,905},{10 -,0,844},{10,0,846},{10,0,858},{12,0,699},{12,0,746},{140,0,772},{135,11,424},{ -133,11,547},{133,0,737},{5,11,490},{6,11,615},{6,11,620},{135,11,683},{6,0,746}, -{134,0,1612},{132,10,776},{9,11,385},{149,11,17},{133,0,145},{135,10,1272},{7,0, -884},{140,0,124},{4,0,387},{135,0,1288},{5,11,133},{136,10,406},{136,11,187},{6, -0,679},{8,11,8},{138,11,0},{135,0,550},{135,11,798},{136,11,685},{7,11,1086},{ -145,11,46},{8,10,175},{10,10,168},{138,10,573},{135,0,1305},{4,0,576},{135,0, -1263},{6,0,686},{134,0,1563},{134,0,607},{5,0,919},{134,0,1673},{148,0,37},{8,11 -,774},{10,11,670},{140,11,51},{133,10,784},{139,10,882},{4,0,82},{5,0,333},{5,0, -904},{6,0,207},{7,0,325},{7,0,1726},{8,0,101},{10,0,778},{139,0,220},{135,11,371 -},{132,0,958},{133,0,903},{4,11,127},{5,11,350},{6,11,356},{8,11,426},{9,11,572} -,{10,11,247},{139,11,312},{140,0,147},{6,11,59},{7,11,885},{9,11,603},{141,11, -397},{10,0,367},{9,10,14},{9,10,441},{139,10,9},{11,10,966},{12,10,287},{13,10, -342},{13,10,402},{15,10,110},{143,10,163},{134,0,690},{132,0,705},{9,0,651},{11, -0,971},{13,0,273},{7,10,1428},{7,10,1640},{7,10,1867},{9,10,169},{9,10,182},{9, -10,367},{9,10,478},{9,10,506},{9,10,551},{9,10,557},{9,10,648},{9,10,697},{9,10, -705},{9,10,725},{9,10,787},{9,10,794},{10,10,198},{10,10,214},{10,10,267},{10,10 -,275},{10,10,456},{10,10,551},{10,10,561},{10,10,613},{10,10,627},{10,10,668},{ -10,10,675},{10,10,691},{10,10,695},{10,10,707},{10,10,715},{11,10,183},{11,10, -201},{11,10,262},{11,10,352},{11,10,439},{11,10,493},{11,10,572},{11,10,591},{11 -,10,608},{11,10,611},{11,10,646},{11,10,674},{11,10,711},{11,10,751},{11,10,761} -,{11,10,776},{11,10,785},{11,10,850},{11,10,853},{11,10,862},{11,10,865},{11,10, -868},{11,10,875},{11,10,898},{11,10,902},{11,10,903},{11,10,910},{11,10,932},{11 -,10,942},{11,10,957},{11,10,967},{11,10,972},{12,10,148},{12,10,195},{12,10,220} -,{12,10,237},{12,10,318},{12,10,339},{12,10,393},{12,10,445},{12,10,450},{12,10, -474},{12,10,505},{12,10,509},{12,10,533},{12,10,591},{12,10,594},{12,10,597},{12 -,10,621},{12,10,633},{12,10,642},{13,10,59},{13,10,60},{13,10,145},{13,10,239},{ -13,10,250},{13,10,329},{13,10,344},{13,10,365},{13,10,372},{13,10,387},{13,10, -403},{13,10,414},{13,10,456},{13,10,470},{13,10,478},{13,10,483},{13,10,489},{14 -,10,55},{14,10,57},{14,10,81},{14,10,90},{14,10,148},{14,10,239},{14,10,266},{14 -,10,321},{14,10,326},{14,10,327},{14,10,330},{14,10,347},{14,10,355},{14,10,401} -,{14,10,404},{14,10,411},{14,10,414},{14,10,416},{14,10,420},{15,10,61},{15,10, -74},{15,10,87},{15,10,88},{15,10,94},{15,10,96},{15,10,116},{15,10,149},{15,10, -154},{16,10,50},{16,10,63},{16,10,73},{17,10,2},{17,10,66},{17,10,92},{17,10,103 -},{17,10,112},{17,10,120},{18,10,50},{18,10,54},{18,10,82},{18,10,86},{18,10,90} -,{18,10,111},{18,10,115},{18,10,156},{19,10,40},{19,10,79},{20,10,78},{149,10,22 -},{7,0,887},{5,10,161},{135,10,839},{142,11,98},{134,0,90},{138,11,356},{135,11, -441},{6,11,111},{7,11,4},{8,11,163},{8,11,776},{138,11,566},{134,0,908},{134,0, -1261},{7,0,813},{12,0,497},{141,0,56},{134,0,1235},{135,0,429},{135,11,1994},{ -138,0,904},{6,0,125},{7,0,1277},{137,0,772},{151,0,12},{4,0,841},{5,0,386},{133, -11,386},{5,11,297},{135,11,1038},{6,0,860},{6,0,1069},{135,11,309},{136,0,946},{ -135,10,1814},{141,11,418},{136,11,363},{10,0,768},{139,0,787},{22,11,30},{150,11 -,33},{6,0,160},{7,0,1106},{9,0,770},{11,0,112},{140,0,413},{11,11,216},{139,11, -340},{136,10,139},{135,11,1390},{135,11,808},{132,11,280},{12,0,271},{17,0,109}, -{7,10,643},{136,10,236},{140,11,54},{4,11,421},{133,11,548},{11,0,719},{12,0,36} -,{141,0,337},{7,0,581},{9,0,644},{137,0,699},{11,11,511},{13,11,394},{14,11,298} -,{14,11,318},{146,11,103},{7,0,304},{9,0,646},{9,0,862},{11,0,696},{12,0,208},{ -15,0,79},{147,0,108},{4,0,631},{7,0,1126},{135,0,1536},{135,11,1527},{8,0,880},{ -10,0,869},{138,0,913},{7,0,1513},{5,10,54},{6,11,254},{9,11,109},{138,11,103},{ -135,0,981},{133,11,729},{132,10,744},{132,0,434},{134,0,550},{7,0,930},{10,0,476 -},{13,0,452},{19,0,104},{6,11,1630},{10,10,402},{146,10,55},{5,0,553},{138,0,824 -},{136,0,452},{8,0,151},{137,10,624},{132,10,572},{132,0,772},{133,11,671},{133, -0,292},{138,0,135},{132,11,889},{140,11,207},{9,0,504},{6,10,43},{7,10,38},{8,10 -,248},{138,10,513},{6,0,1089},{135,11,1910},{4,11,627},{133,11,775},{135,0,783}, -{133,10,766},{133,10,363},{7,0,387},{135,11,387},{7,0,393},{10,0,603},{11,0,206} -,{7,11,202},{11,11,362},{11,11,948},{140,11,388},{6,11,507},{7,11,451},{8,11,389 -},{12,11,490},{13,11,16},{13,11,215},{13,11,351},{18,11,132},{147,11,125},{4,0, -912},{9,0,232},{135,11,841},{6,10,258},{140,10,409},{5,10,249},{148,10,82},{136, -11,566},{6,0,977},{135,11,1214},{7,0,1973},{136,0,716},{135,0,98},{133,0,733},{5 -,11,912},{134,11,1695},{5,10,393},{6,10,378},{7,10,1981},{9,10,32},{9,10,591},{ -10,10,685},{10,10,741},{142,10,382},{133,10,788},{10,0,19},{11,0,911},{7,10,1968 -},{141,10,509},{5,0,668},{5,11,236},{6,11,572},{8,11,492},{11,11,618},{144,11,56 -},{135,11,1789},{4,0,360},{5,0,635},{5,0,700},{5,10,58},{5,10,171},{5,10,683},{6 -,10,291},{6,10,566},{7,10,1650},{11,10,523},{12,10,273},{12,10,303},{15,10,39},{ -143,10,111},{133,0,901},{134,10,589},{5,11,190},{136,11,318},{140,0,656},{7,0, -726},{152,0,9},{4,10,917},{133,10,1005},{135,10,1598},{134,11,491},{4,10,919},{ -133,11,434},{137,0,72},{6,0,1269},{6,0,1566},{134,0,1621},{9,0,463},{10,0,595},{ -4,10,255},{5,10,302},{6,10,132},{7,10,128},{7,10,283},{7,10,1299},{10,10,52},{10 -,10,514},{11,10,925},{13,10,92},{142,10,309},{135,0,1454},{134,0,1287},{11,0,600 -},{13,0,245},{137,10,173},{136,0,989},{7,0,164},{7,0,1571},{9,0,107},{140,0,225} -,{6,0,1061},{141,10,442},{4,0,27},{5,0,484},{5,0,510},{6,0,434},{7,0,1000},{7,0, -1098},{136,0,2},{7,11,85},{7,11,247},{8,11,585},{10,11,163},{138,11,316},{11,11, -103},{142,11,0},{134,0,1127},{4,0,460},{134,0,852},{134,10,210},{4,0,932},{133,0 -,891},{6,0,588},{147,11,83},{8,0,625},{4,10,284},{134,10,223},{134,0,76},{8,0,92 -},{137,0,221},{4,11,124},{10,11,457},{11,11,121},{11,11,169},{11,11,422},{11,11, -870},{12,11,214},{13,11,389},{14,11,187},{143,11,77},{9,11,618},{138,11,482},{4, -10,218},{7,10,526},{143,10,137},{13,0,9},{14,0,104},{14,0,311},{4,10,270},{5,10, -192},{6,10,332},{135,10,1322},{140,10,661},{135,11,1193},{6,11,107},{7,11,638},{ -7,11,1632},{137,11,396},{132,0,763},{4,0,622},{5,11,370},{134,11,1756},{133,0, -253},{135,0,546},{9,0,73},{10,0,110},{14,0,185},{17,0,119},{133,11,204},{7,0,624 -},{7,0,916},{10,0,256},{139,0,87},{7,10,379},{8,10,481},{137,10,377},{5,0,212},{ -12,0,35},{13,0,382},{5,11,970},{134,11,1706},{9,0,746},{5,10,1003},{134,10,149}, -{10,0,150},{11,0,849},{13,0,330},{8,10,262},{9,10,627},{11,10,214},{11,10,404},{ -11,10,457},{11,10,780},{11,10,913},{13,10,401},{142,10,200},{134,0,1466},{135,11 -,3},{6,0,1299},{4,11,35},{5,11,121},{5,11,483},{5,11,685},{6,11,489},{7,11,1204} -,{136,11,394},{135,10,742},{4,10,142},{136,10,304},{4,11,921},{133,11,1007},{134 -,0,1518},{6,0,1229},{135,0,1175},{133,0,816},{12,0,159},{4,10,471},{4,11,712},{5 -,10,51},{6,10,602},{7,10,925},{8,10,484},{138,10,195},{134,11,1629},{5,0,869},{5 -,0,968},{6,0,1626},{8,0,734},{136,0,784},{4,0,542},{6,0,1716},{6,0,1727},{7,0, -1082},{7,0,1545},{8,0,56},{8,0,118},{8,0,412},{8,0,564},{9,0,888},{9,0,908},{10, -0,50},{10,0,423},{11,0,685},{11,0,697},{11,0,933},{12,0,299},{13,0,126},{13,0, -136},{13,0,170},{13,0,190},{136,10,688},{132,10,697},{4,0,232},{9,0,202},{10,0, -474},{140,0,433},{136,0,212},{6,0,108},{7,0,1003},{7,0,1181},{8,0,111},{136,0, -343},{5,10,221},{135,11,1255},{133,11,485},{134,0,1712},{142,0,216},{5,0,643},{6 -,0,516},{4,11,285},{5,11,317},{6,11,301},{7,11,7},{8,11,153},{10,11,766},{11,11, -468},{12,11,467},{141,11,143},{4,0,133},{7,0,711},{7,0,1298},{135,0,1585},{134,0 -,650},{135,11,512},{6,0,99},{7,0,1808},{145,0,57},{6,0,246},{6,0,574},{7,0,428}, -{9,0,793},{10,0,669},{11,0,485},{11,0,840},{12,0,300},{14,0,250},{145,0,55},{4, -10,132},{5,10,69},{135,10,1242},{136,0,1023},{7,0,302},{132,10,111},{135,0,1871} -,{132,0,728},{9,0,252},{132,10,767},{6,0,461},{7,0,1590},{7,10,1416},{7,10,2005} -,{8,10,131},{8,10,466},{9,10,672},{13,10,252},{148,10,103},{6,0,323},{135,0,1564 -},{7,0,461},{136,0,775},{6,10,44},{136,10,368},{139,0,172},{132,0,464},{4,10,570 -},{133,10,120},{137,11,269},{6,10,227},{135,10,1589},{6,11,1719},{6,11,1735},{7, -11,2016},{7,11,2020},{8,11,837},{137,11,852},{7,0,727},{146,0,73},{132,0,1023},{ -135,11,852},{135,10,1529},{136,0,577},{138,11,568},{134,0,1037},{8,11,67},{138, -11,419},{4,0,413},{5,0,677},{8,0,432},{140,0,280},{10,0,600},{6,10,1667},{7,11, -967},{7,10,2036},{141,11,11},{6,10,511},{140,10,132},{6,0,799},{5,10,568},{6,10, -138},{135,10,1293},{8,0,159},{4,10,565},{136,10,827},{7,0,646},{7,0,1730},{11,0, -446},{141,0,178},{4,10,922},{133,10,1023},{135,11,11},{132,0,395},{11,0,145},{ -135,10,1002},{9,0,174},{10,0,164},{11,0,440},{11,0,514},{11,0,841},{15,0,98},{ -149,0,20},{134,0,426},{10,0,608},{139,0,1002},{7,11,320},{8,11,51},{12,11,481},{ -12,11,570},{148,11,106},{9,0,977},{9,0,983},{132,11,445},{138,0,250},{139,0,100} -,{6,0,1982},{136,10,402},{133,11,239},{4,10,716},{141,10,31},{5,0,476},{7,11,83} -,{7,11,1990},{8,11,130},{139,11,720},{8,10,691},{136,10,731},{5,11,123},{6,11, -530},{7,11,348},{135,11,1419},{5,0,76},{6,0,458},{6,0,497},{7,0,868},{9,0,658},{ -10,0,594},{11,0,173},{11,0,566},{12,0,20},{12,0,338},{141,0,200},{9,11,139},{10, -11,399},{11,11,469},{12,11,634},{141,11,223},{9,10,840},{138,10,803},{133,10,847 -},{11,11,223},{140,11,168},{132,11,210},{8,0,447},{9,10,53},{9,10,268},{9,10,901 -},{10,10,518},{10,10,829},{11,10,188},{13,10,74},{14,10,46},{15,10,17},{15,10,33 -},{17,10,40},{18,10,36},{19,10,20},{22,10,1},{152,10,2},{4,0,526},{7,0,1029},{ -135,0,1054},{19,11,59},{150,11,2},{4,0,636},{6,0,1875},{6,0,1920},{9,0,999},{12, -0,807},{12,0,825},{15,0,179},{15,0,190},{18,0,182},{136,10,532},{6,0,1699},{7,0, -660},{7,0,1124},{17,0,31},{19,0,22},{151,0,14},{135,10,681},{132,11,430},{140,10 -,677},{4,10,684},{136,10,384},{132,11,756},{133,11,213},{7,0,188},{7,10,110},{8, -10,290},{8,10,591},{9,10,382},{9,10,649},{11,10,71},{11,10,155},{11,10,313},{12, -10,5},{13,10,325},{142,10,287},{7,10,360},{7,10,425},{9,10,66},{9,10,278},{138, -10,644},{142,11,164},{4,0,279},{7,0,301},{137,0,362},{134,11,586},{135,0,1743},{ -4,0,178},{133,0,399},{4,10,900},{133,10,861},{5,10,254},{7,10,985},{136,10,73},{ -133,11,108},{7,10,1959},{136,10,683},{133,11,219},{4,11,193},{5,11,916},{7,11, -364},{10,11,398},{10,11,726},{11,11,317},{11,11,626},{12,11,142},{12,11,288},{12 -,11,678},{13,11,313},{15,11,113},{18,11,114},{21,11,30},{150,11,53},{6,11,241},{ -7,11,907},{8,11,832},{9,11,342},{10,11,729},{11,11,284},{11,11,445},{11,11,651}, -{11,11,863},{13,11,398},{146,11,99},{132,0,872},{134,0,831},{134,0,1692},{6,0, -202},{6,0,1006},{9,0,832},{10,0,636},{11,0,208},{12,0,360},{17,0,118},{18,0,27}, -{20,0,67},{137,11,734},{132,10,725},{7,11,993},{138,11,666},{134,0,1954},{134,10 -,196},{7,0,872},{10,0,516},{139,0,167},{133,10,831},{4,11,562},{9,11,254},{139, -11,879},{137,0,313},{4,0,224},{132,11,786},{11,0,24},{12,0,170},{136,10,723},{5, -0,546},{7,0,35},{8,0,11},{8,0,12},{9,0,315},{9,0,533},{10,0,802},{11,0,166},{12, -0,525},{142,0,243},{7,0,1937},{13,10,80},{13,10,437},{145,10,74},{5,0,241},{8,0, -242},{9,0,451},{10,0,667},{11,0,598},{140,0,429},{150,0,46},{6,0,1273},{137,0, -830},{5,10,848},{6,10,66},{136,10,764},{6,0,825},{134,0,993},{4,0,1006},{10,0, -327},{13,0,271},{4,10,36},{7,10,1387},{139,10,755},{134,0,1023},{135,0,1580},{4, -0,366},{137,0,516},{132,10,887},{6,0,1736},{135,0,1891},{6,11,216},{7,11,901},{7 -,11,1343},{136,11,493},{6,10,165},{138,10,388},{7,11,341},{139,11,219},{4,10,719 -},{135,10,155},{134,0,1935},{132,0,826},{6,0,331},{6,0,1605},{8,0,623},{11,0,139 -},{139,0,171},{135,11,1734},{10,11,115},{11,11,420},{12,11,154},{13,11,404},{14, -11,346},{15,11,54},{143,11,112},{7,0,288},{4,10,353},{6,10,146},{6,10,1789},{7, -10,990},{7,10,1348},{9,10,665},{9,10,898},{11,10,893},{142,10,212},{6,0,916},{ -134,0,1592},{7,0,1888},{4,10,45},{135,10,1257},{5,11,1011},{136,11,701},{139,11, -596},{4,11,54},{5,11,666},{7,11,1039},{7,11,1130},{9,11,195},{138,11,302},{134,0 -,1471},{134,0,1570},{132,0,394},{140,10,65},{136,10,816},{135,0,1931},{7,0,574}, -{135,0,1719},{134,11,467},{132,0,658},{9,0,781},{10,0,144},{11,0,385},{13,0,161} -,{13,0,228},{13,0,268},{20,0,107},{134,11,1669},{136,0,374},{135,0,735},{4,0,344 -},{6,0,498},{139,0,323},{7,0,586},{7,0,1063},{6,10,559},{134,10,1691},{137,0,155 -},{133,0,906},{7,11,122},{9,11,259},{10,11,84},{11,11,470},{12,11,541},{141,11, -379},{134,0,1139},{10,0,108},{139,0,116},{134,10,456},{133,10,925},{5,11,82},{5, -11,131},{7,11,1755},{8,11,31},{9,11,168},{9,11,764},{139,11,869},{134,11,605},{5 -,11,278},{137,11,68},{4,11,163},{5,11,201},{5,11,307},{5,11,310},{6,11,335},{7, -11,284},{136,11,165},{135,11,1660},{6,11,33},{135,11,1244},{4,0,616},{136,11,483 -},{8,0,857},{8,0,902},{8,0,910},{10,0,879},{12,0,726},{4,11,199},{139,11,34},{ -136,0,692},{6,10,193},{7,10,240},{7,10,1682},{10,10,51},{10,10,640},{11,10,410}, -{13,10,82},{14,10,247},{14,10,331},{142,10,377},{6,0,823},{134,0,983},{139,10, -411},{132,0,305},{136,10,633},{138,11,203},{134,0,681},{6,11,326},{7,11,677},{ -137,11,425},{5,0,214},{7,0,603},{8,0,611},{9,0,686},{10,0,88},{11,0,459},{11,0, -496},{12,0,463},{12,0,590},{141,0,0},{136,0,1004},{142,0,23},{134,0,1703},{147, -11,8},{145,11,56},{135,0,1443},{4,10,237},{135,10,514},{6,0,714},{145,0,19},{5, -11,358},{7,11,473},{7,11,1184},{10,11,662},{13,11,212},{13,11,304},{13,11,333},{ -145,11,98},{4,0,737},{10,0,98},{11,0,294},{12,0,60},{12,0,437},{13,0,64},{13,0, -380},{142,0,430},{6,10,392},{7,10,65},{135,10,2019},{6,0,1758},{8,0,520},{9,0, -345},{9,0,403},{142,0,350},{5,0,47},{10,0,242},{138,0,579},{5,0,139},{7,0,1168}, -{138,0,539},{134,0,1459},{13,0,388},{141,11,388},{134,0,253},{7,10,1260},{135,10 -,1790},{10,0,252},{9,10,222},{139,10,900},{140,0,745},{133,11,946},{4,0,107},{7, -0,613},{8,0,439},{8,0,504},{9,0,501},{10,0,383},{139,0,477},{135,11,1485},{132,0 -,871},{7,11,411},{7,11,590},{8,11,631},{9,11,323},{10,11,355},{11,11,491},{12,11 -,143},{12,11,402},{13,11,73},{14,11,408},{15,11,107},{146,11,71},{132,0,229},{ -132,0,903},{140,0,71},{133,0,549},{4,0,47},{6,0,373},{7,0,452},{7,0,543},{7,0, -1828},{7,0,1856},{9,0,6},{11,0,257},{139,0,391},{7,11,1467},{8,11,328},{10,11, -544},{11,11,955},{13,11,320},{145,11,83},{5,0,980},{134,0,1754},{136,0,865},{5,0 -,705},{137,0,606},{7,0,161},{8,10,201},{136,10,605},{143,11,35},{5,11,835},{6,11 -,483},{140,10,224},{7,0,536},{7,0,1331},{136,0,143},{134,0,1388},{5,0,724},{10,0 -,305},{11,0,151},{12,0,33},{12,0,121},{12,0,381},{17,0,3},{17,0,27},{17,0,78},{ -18,0,18},{19,0,54},{149,0,5},{4,10,523},{133,10,638},{5,0,19},{134,0,533},{5,0, -395},{5,0,951},{134,0,1776},{135,0,1908},{132,0,846},{10,0,74},{11,0,663},{12,0, -210},{13,0,166},{13,0,310},{14,0,373},{18,0,95},{19,0,43},{6,10,242},{7,10,227}, -{7,10,1581},{8,10,104},{9,10,113},{9,10,220},{9,10,427},{10,10,239},{11,10,579}, -{11,10,1023},{13,10,4},{13,10,204},{13,10,316},{148,10,86},{9,11,716},{11,11,108 -},{13,11,123},{14,11,252},{19,11,38},{21,11,3},{151,11,11},{8,0,372},{9,0,122},{ -138,0,175},{132,11,677},{7,11,1374},{136,11,540},{135,10,861},{132,0,695},{7,0, -497},{9,0,387},{147,0,81},{136,0,937},{134,0,718},{7,0,1328},{136,10,494},{132, -11,331},{6,0,1581},{133,11,747},{5,0,284},{6,0,49},{6,0,350},{7,0,1},{7,0,377},{ -7,0,1693},{8,0,18},{8,0,678},{9,0,161},{9,0,585},{9,0,671},{9,0,839},{11,0,912}, -{141,0,427},{7,10,1306},{8,10,505},{9,10,482},{10,10,126},{11,10,225},{12,10,347 -},{12,10,449},{13,10,19},{14,10,218},{142,10,435},{10,10,764},{12,10,120},{13,10 -,39},{145,10,127},{4,0,597},{133,10,268},{134,0,1094},{4,0,1008},{134,0,1973},{ -132,0,811},{139,0,908},{135,0,1471},{133,11,326},{4,10,384},{135,10,1022},{7,0, -1935},{8,0,324},{12,0,42},{4,11,691},{7,11,1935},{8,11,324},{9,11,35},{10,11,680 -},{11,11,364},{12,11,42},{13,11,357},{146,11,16},{135,0,2014},{7,0,2007},{9,0, -101},{9,0,450},{10,0,66},{10,0,842},{11,0,536},{12,0,587},{6,11,32},{7,11,385},{ -7,11,757},{7,11,1916},{8,11,37},{8,11,94},{8,11,711},{9,11,541},{10,11,162},{10, -11,795},{11,11,989},{11,11,1010},{12,11,14},{142,11,308},{139,0,586},{135,10, -1703},{7,0,1077},{11,0,28},{9,10,159},{140,10,603},{6,0,1221},{136,10,583},{6,11 -,152},{6,11,349},{6,11,1682},{7,11,1252},{8,11,112},{9,11,435},{9,11,668},{10,11 -,290},{10,11,319},{10,11,815},{11,11,180},{11,11,837},{12,11,240},{13,11,152},{ -13,11,219},{142,11,158},{139,0,62},{132,10,515},{8,10,632},{8,10,697},{137,10, -854},{134,0,1766},{132,11,581},{6,11,126},{7,11,573},{8,11,397},{142,11,44},{150 -,0,28},{11,0,670},{22,0,25},{4,10,136},{133,10,551},{6,0,1665},{7,0,256},{7,0, -1388},{138,0,499},{4,0,22},{5,0,10},{7,0,1576},{136,0,97},{134,10,1782},{5,0,481 -},{7,10,1287},{9,10,44},{10,10,552},{10,10,642},{11,10,839},{12,10,274},{12,10, -275},{12,10,372},{13,10,91},{142,10,125},{133,11,926},{7,11,1232},{137,11,531},{ -6,0,134},{7,0,437},{7,0,1824},{9,0,37},{14,0,285},{142,0,371},{7,0,486},{8,0,155 -},{11,0,93},{140,0,164},{6,0,1391},{134,0,1442},{133,11,670},{133,0,591},{6,10, -147},{7,10,886},{7,11,1957},{9,10,753},{138,10,268},{5,0,380},{5,0,650},{7,0, -1173},{136,0,310},{4,0,364},{7,0,1156},{7,0,1187},{137,0,409},{135,11,1621},{134 -,0,482},{133,11,506},{4,0,781},{6,0,487},{7,0,926},{8,0,263},{139,0,500},{138,10 -,137},{135,11,242},{139,11,96},{133,10,414},{135,10,1762},{134,0,804},{5,11,834} -,{7,11,1202},{8,11,14},{9,11,481},{137,11,880},{134,10,599},{4,0,94},{135,0,1265 -},{4,0,415},{132,0,417},{5,0,348},{6,0,522},{6,10,1749},{7,11,1526},{138,11,465} -,{134,10,1627},{132,0,1012},{132,10,488},{4,11,357},{6,11,172},{7,11,143},{137, -11,413},{4,10,83},{4,11,590},{146,11,76},{140,10,676},{7,11,287},{8,11,355},{9, -11,293},{137,11,743},{134,10,278},{6,0,1803},{18,0,165},{24,0,21},{5,11,169},{7, -11,333},{136,11,45},{12,10,97},{140,11,97},{4,0,408},{4,0,741},{135,0,500},{132, -11,198},{7,10,388},{7,10,644},{139,10,781},{4,11,24},{5,11,140},{5,11,185},{7,11 -,1500},{11,11,565},{139,11,838},{6,0,1321},{9,0,257},{7,10,229},{8,10,59},{9,10, -190},{10,10,378},{140,10,191},{4,11,334},{133,11,593},{135,11,1885},{134,0,1138} -,{4,0,249},{6,0,73},{135,0,177},{133,0,576},{142,0,231},{137,0,288},{132,10,660} -,{7,10,1035},{138,10,737},{135,0,1487},{6,0,989},{9,0,433},{7,10,690},{9,10,587} -,{140,10,521},{7,0,1264},{7,0,1678},{11,0,945},{12,0,341},{12,0,471},{140,0,569} -,{132,11,709},{133,11,897},{5,11,224},{13,11,174},{146,11,52},{135,11,1840},{134 -,10,1744},{12,0,87},{16,0,74},{4,10,733},{9,10,194},{10,10,92},{11,10,198},{12, -10,84},{141,10,128},{140,0,779},{135,0,538},{4,11,608},{133,11,497},{133,0,413}, -{7,11,1375},{7,11,1466},{138,11,331},{136,0,495},{6,11,540},{136,11,136},{7,0,54 -},{8,0,312},{10,0,191},{10,0,614},{140,0,567},{6,0,468},{7,0,567},{7,0,1478},{8, -0,530},{14,0,290},{133,11,999},{4,11,299},{7,10,306},{135,11,1004},{142,11,296}, -{134,0,1484},{133,10,979},{6,0,609},{9,0,815},{12,11,137},{14,11,9},{14,11,24},{ -142,11,64},{133,11,456},{6,0,484},{135,0,822},{133,10,178},{136,11,180},{132,11, -755},{137,0,900},{135,0,1335},{6,0,1724},{135,0,2022},{135,11,1139},{5,0,640},{ -132,10,390},{6,0,1831},{138,11,633},{135,11,566},{4,11,890},{5,11,805},{5,11,819 -},{5,11,961},{6,11,396},{6,11,1631},{6,11,1678},{7,11,1967},{7,11,2041},{9,11, -630},{11,11,8},{11,11,1019},{12,11,176},{13,11,225},{14,11,292},{149,11,24},{132 -,0,474},{134,0,1103},{135,0,1504},{134,0,1576},{6,0,961},{6,0,1034},{140,0,655}, -{11,11,514},{149,11,20},{5,0,305},{135,11,1815},{7,11,1505},{10,11,190},{10,11, -634},{11,11,792},{12,11,358},{140,11,447},{5,11,0},{6,11,536},{7,11,604},{13,11, -445},{145,11,126},{7,0,1236},{133,10,105},{4,0,480},{6,0,217},{6,0,302},{6,0, -1642},{7,0,130},{7,0,837},{7,0,1321},{7,0,1547},{7,0,1657},{8,0,429},{9,0,228},{ -13,0,289},{13,0,343},{19,0,101},{6,11,232},{6,11,412},{7,11,1074},{8,11,9},{8,11 -,157},{8,11,786},{9,11,196},{9,11,352},{9,11,457},{10,11,337},{11,11,232},{11,11 -,877},{12,11,480},{140,11,546},{5,10,438},{7,11,958},{9,10,694},{12,10,627},{13, -11,38},{141,10,210},{4,11,382},{136,11,579},{7,0,278},{10,0,739},{11,0,708},{141 -,0,348},{4,11,212},{135,11,1206},{135,11,1898},{6,0,708},{6,0,1344},{152,10,11}, -{137,11,768},{134,0,1840},{140,0,233},{8,10,25},{138,10,826},{6,0,2017},{133,11, -655},{6,0,1488},{139,11,290},{132,10,308},{134,0,1590},{134,0,1800},{134,0,1259} -,{16,0,28},{6,11,231},{7,11,95},{136,11,423},{133,11,300},{135,10,150},{136,10, -649},{7,11,1874},{137,11,641},{6,11,237},{7,11,611},{8,11,100},{9,11,416},{11,11 -,335},{12,11,173},{146,11,101},{137,0,45},{134,10,521},{17,0,36},{14,11,26},{146 -,11,150},{7,0,1442},{14,0,22},{5,10,339},{15,10,41},{15,10,166},{147,10,66},{8,0 -,378},{6,11,581},{135,11,1119},{134,0,1507},{147,11,117},{139,0,39},{134,0,1054} -,{6,0,363},{7,0,1955},{136,0,725},{134,0,2036},{133,11,199},{6,0,1871},{9,0,935} -,{9,0,961},{9,0,1004},{9,0,1016},{12,0,805},{12,0,852},{12,0,853},{12,0,869},{12 -,0,882},{12,0,896},{12,0,906},{12,0,917},{12,0,940},{15,0,170},{15,0,176},{15,0, -188},{15,0,201},{15,0,205},{15,0,212},{15,0,234},{15,0,244},{18,0,181},{18,0,193 -},{18,0,196},{18,0,201},{18,0,202},{18,0,210},{18,0,217},{18,0,235},{18,0,236},{ -18,0,237},{21,0,54},{21,0,55},{21,0,58},{21,0,59},{152,0,22},{134,10,1628},{137, -0,805},{5,0,813},{135,0,2046},{142,11,42},{5,0,712},{6,0,1240},{11,0,17},{13,0, -321},{144,0,67},{132,0,617},{135,10,829},{6,0,320},{7,0,781},{7,0,1921},{9,0,55} -,{10,0,186},{10,0,273},{10,0,664},{10,0,801},{11,0,996},{11,0,997},{13,0,157},{ -142,0,170},{136,0,271},{5,10,486},{135,10,1349},{18,11,91},{147,11,70},{10,0,445 -},{7,10,1635},{8,10,17},{138,10,295},{136,11,404},{7,0,103},{7,0,863},{11,0,184} -,{145,0,62},{138,10,558},{137,0,659},{6,11,312},{6,11,1715},{10,11,584},{11,11, -546},{11,11,692},{12,11,259},{12,11,295},{13,11,46},{141,11,154},{134,0,676},{ -132,11,588},{4,11,231},{5,11,61},{6,11,104},{7,11,729},{7,11,964},{7,11,1658},{ -140,11,414},{6,11,263},{138,11,757},{11,0,337},{142,0,303},{135,11,1363},{132,11 -,320},{140,0,506},{134,10,447},{5,0,77},{7,0,1455},{10,0,843},{147,0,73},{7,10, -577},{7,10,1432},{9,10,475},{9,10,505},{9,10,526},{9,10,609},{9,10,689},{9,10, -726},{9,10,735},{9,10,738},{10,10,556},{10,10,674},{10,10,684},{11,10,89},{11,10 -,202},{11,10,272},{11,10,380},{11,10,415},{11,10,505},{11,10,537},{11,10,550},{ -11,10,562},{11,10,640},{11,10,667},{11,10,688},{11,10,847},{11,10,927},{11,10, -930},{11,10,940},{12,10,144},{12,10,325},{12,10,329},{12,10,389},{12,10,403},{12 -,10,451},{12,10,515},{12,10,604},{12,10,616},{12,10,626},{13,10,66},{13,10,131}, -{13,10,167},{13,10,236},{13,10,368},{13,10,411},{13,10,434},{13,10,453},{13,10, -461},{13,10,474},{14,10,59},{14,10,60},{14,10,139},{14,10,152},{14,10,276},{14, -10,353},{14,10,402},{15,10,28},{15,10,81},{15,10,123},{15,10,152},{18,10,136},{ -148,10,88},{132,0,458},{135,0,1420},{6,0,109},{10,0,382},{4,11,405},{4,10,609},{ -7,10,756},{7,11,817},{9,10,544},{11,10,413},{14,11,58},{14,10,307},{16,10,25},{ -17,11,37},{146,11,124},{6,0,330},{7,0,1084},{11,0,142},{133,11,974},{4,10,930},{ -133,10,947},{5,10,939},{142,11,394},{16,0,91},{145,0,87},{5,11,235},{5,10,962},{ -7,11,1239},{11,11,131},{140,11,370},{11,0,492},{5,10,651},{8,10,170},{9,10,61},{ -9,10,63},{10,10,23},{10,10,37},{10,10,834},{11,10,4},{11,10,281},{11,10,503},{11 -,10,677},{12,10,96},{12,10,130},{12,10,244},{14,10,5},{14,10,40},{14,10,162},{14 -,10,202},{146,10,133},{4,10,406},{5,10,579},{12,10,492},{150,10,15},{9,11,137},{ -138,11,221},{134,0,1239},{11,0,211},{140,0,145},{7,11,390},{138,11,140},{135,11, -1418},{135,11,1144},{134,0,1049},{7,0,321},{6,10,17},{7,10,1001},{7,10,1982},{9, -10,886},{10,10,489},{10,10,800},{11,10,782},{12,10,320},{13,10,467},{14,10,145}, -{14,10,387},{143,10,119},{145,10,17},{5,11,407},{11,11,489},{19,11,37},{20,11,73 -},{150,11,38},{133,10,458},{135,0,1985},{7,10,1983},{8,10,0},{8,10,171},{9,10, -120},{9,10,732},{10,10,473},{11,10,656},{11,10,998},{18,10,0},{18,10,2},{147,10, -21},{5,11,325},{7,11,1483},{8,11,5},{8,11,227},{9,11,105},{10,11,585},{140,11, -614},{136,0,122},{132,0,234},{135,11,1196},{6,0,976},{6,0,1098},{134,0,1441},{7, -0,253},{136,0,549},{6,11,621},{13,11,504},{144,11,19},{132,10,519},{5,0,430},{5, -0,932},{6,0,131},{7,0,417},{9,0,522},{11,0,314},{141,0,390},{14,0,149},{14,0,399 -},{143,0,57},{5,10,907},{6,10,31},{6,11,218},{7,10,491},{7,10,530},{8,10,592},{ -11,10,53},{11,10,779},{12,10,167},{12,10,411},{14,10,14},{14,10,136},{15,10,72}, -{16,10,17},{144,10,72},{140,11,330},{7,11,454},{7,11,782},{136,11,768},{132,0, -507},{10,11,676},{140,11,462},{6,0,630},{9,0,811},{4,10,208},{5,10,106},{6,10, -531},{8,10,408},{9,10,188},{138,10,572},{4,0,343},{5,0,511},{134,10,1693},{134, -11,164},{132,0,448},{7,0,455},{138,0,591},{135,0,1381},{12,10,441},{150,11,50},{ -9,10,449},{10,10,192},{138,10,740},{6,0,575},{132,10,241},{134,0,1175},{134,0, -653},{134,0,1761},{134,0,1198},{132,10,259},{6,11,343},{7,11,195},{9,11,226},{10 -,11,197},{10,11,575},{11,11,502},{139,11,899},{7,0,1127},{7,0,1572},{10,0,297},{ -10,0,422},{11,0,764},{11,0,810},{12,0,264},{13,0,102},{13,0,300},{13,0,484},{14, -0,147},{14,0,229},{17,0,71},{18,0,118},{147,0,120},{135,11,666},{132,0,678},{4, -10,173},{5,10,312},{5,10,512},{135,10,1285},{7,10,1603},{7,10,1691},{9,10,464},{ -11,10,195},{12,10,279},{12,10,448},{14,10,11},{147,10,102},{16,0,99},{146,0,164} -,{7,11,1125},{9,11,143},{11,11,61},{14,11,405},{150,11,21},{137,11,260},{4,10, -452},{5,10,583},{5,10,817},{6,10,433},{7,10,593},{7,10,720},{7,10,1378},{8,10, -161},{9,10,284},{10,10,313},{139,10,886},{132,10,547},{136,10,722},{14,0,35},{ -142,0,191},{141,0,45},{138,0,121},{132,0,125},{134,0,1622},{133,11,959},{8,10, -420},{139,10,193},{132,0,721},{135,10,409},{136,0,145},{7,0,792},{8,0,147},{10,0 -,821},{11,0,970},{11,0,1021},{136,11,173},{134,11,266},{132,0,715},{7,0,1999},{ -138,10,308},{133,0,531},{5,0,168},{5,0,930},{8,0,74},{9,0,623},{12,0,500},{140,0 -,579},{144,0,65},{138,11,246},{6,0,220},{7,0,1101},{13,0,105},{142,11,314},{5,10 -,1002},{136,10,745},{134,0,960},{20,0,0},{148,11,0},{4,0,1005},{4,10,239},{6,10, -477},{7,10,1607},{11,10,68},{139,10,617},{6,0,19},{7,0,1413},{139,0,428},{149,10 -,13},{7,0,96},{8,0,401},{8,0,703},{9,0,896},{136,11,300},{134,0,1595},{145,0,116 -},{136,0,1021},{7,0,1961},{7,0,1965},{7,0,2030},{8,0,150},{8,0,702},{8,0,737},{8 -,0,750},{140,0,366},{11,11,75},{142,11,267},{132,10,367},{8,0,800},{9,0,148},{9, -0,872},{9,0,890},{11,0,309},{11,0,1001},{13,0,267},{13,0,323},{5,11,427},{5,11, -734},{7,11,478},{136,11,52},{7,11,239},{11,11,217},{142,11,165},{132,11,323},{ -140,11,419},{13,0,299},{142,0,75},{6,11,87},{6,11,1734},{7,11,20},{7,11,1056},{8 -,11,732},{9,11,406},{9,11,911},{138,11,694},{134,0,1383},{132,10,694},{133,11, -613},{137,0,779},{4,0,598},{140,10,687},{6,0,970},{135,0,424},{133,0,547},{7,11, -32},{7,11,984},{8,11,85},{8,11,709},{9,11,579},{9,11,847},{9,11,856},{10,11,799} -,{11,11,258},{11,11,1007},{12,11,331},{12,11,615},{13,11,188},{13,11,435},{14,11 -,8},{15,11,165},{16,11,27},{148,11,40},{6,0,1222},{134,0,1385},{132,0,876},{138, -11,151},{135,10,213},{4,11,167},{135,11,82},{133,0,133},{6,11,24},{7,11,74},{7, -11,678},{137,11,258},{5,11,62},{6,11,534},{7,11,684},{7,11,1043},{7,11,1072},{8, -11,280},{8,11,541},{8,11,686},{10,11,519},{11,11,252},{140,11,282},{136,0,187},{ -8,0,8},{10,0,0},{10,0,818},{139,0,988},{132,11,359},{11,0,429},{15,0,51},{135,10 -,1672},{136,0,685},{5,11,211},{7,11,88},{136,11,627},{134,0,472},{136,0,132},{6, -11,145},{141,11,336},{4,10,751},{11,10,390},{140,10,32},{6,0,938},{6,0,1060},{4, -11,263},{4,10,409},{133,10,78},{137,0,874},{8,0,774},{10,0,670},{12,0,51},{4,11, -916},{6,10,473},{7,10,1602},{10,10,698},{12,10,212},{13,10,307},{145,10,105},{ -146,0,92},{143,10,156},{132,0,830},{137,0,701},{4,11,599},{6,11,1634},{7,11,5},{ -7,11,55},{7,11,67},{7,11,97},{7,11,691},{7,11,979},{7,11,1697},{8,11,207},{8,11, -214},{8,11,231},{8,11,294},{8,11,336},{8,11,428},{8,11,451},{8,11,460},{8,11,471 -},{8,11,622},{8,11,626},{8,11,679},{8,11,759},{8,11,829},{9,11,11},{9,11,246},{9 -,11,484},{9,11,573},{9,11,706},{9,11,762},{9,11,798},{9,11,855},{9,11,870},{9,11 -,912},{10,11,303},{10,11,335},{10,11,424},{10,11,461},{10,11,543},{10,11,759},{ -10,11,814},{11,11,59},{11,11,199},{11,11,235},{11,11,475},{11,11,590},{11,11,929 -},{11,11,963},{12,11,114},{12,11,182},{12,11,226},{12,11,332},{12,11,439},{12,11 -,575},{12,11,598},{13,11,8},{13,11,125},{13,11,194},{13,11,287},{14,11,197},{14, -11,383},{15,11,53},{17,11,63},{19,11,46},{19,11,98},{19,11,106},{148,11,85},{4,0 -,127},{5,0,350},{6,0,356},{8,0,426},{9,0,572},{10,0,247},{139,0,312},{134,0,1215 -},{6,0,59},{9,0,603},{13,0,397},{7,11,1853},{138,11,437},{134,0,1762},{147,11, -126},{135,10,883},{13,0,293},{142,0,56},{133,10,617},{139,10,50},{5,11,187},{7, -10,1518},{139,10,694},{135,0,441},{6,0,111},{7,0,4},{8,0,163},{8,0,776},{138,0, -566},{132,0,806},{4,11,215},{9,11,38},{10,11,3},{11,11,23},{11,11,127},{139,11, -796},{14,0,233},{4,10,546},{135,10,2042},{135,0,1994},{134,0,1739},{135,11,1530} -,{136,0,393},{5,0,297},{7,0,1038},{14,0,359},{19,0,52},{148,0,47},{135,0,309},{4 -,10,313},{133,10,577},{8,10,184},{141,10,433},{135,10,935},{12,10,186},{12,10, -292},{14,10,100},{146,10,70},{136,0,363},{14,0,175},{11,10,402},{12,10,109},{12, -10,431},{13,10,179},{13,10,206},{14,10,217},{16,10,3},{148,10,53},{5,10,886},{6, -10,46},{6,10,1790},{7,10,14},{7,10,732},{7,10,1654},{8,10,95},{8,10,327},{8,10, -616},{9,10,892},{10,10,598},{10,10,769},{11,10,134},{11,10,747},{12,10,378},{142 -,10,97},{136,0,666},{135,0,1675},{6,0,655},{134,0,1600},{135,0,808},{133,10,1021 -},{4,11,28},{5,11,440},{7,11,248},{11,11,833},{140,11,344},{134,11,1654},{132,0, -280},{140,0,54},{4,0,421},{133,0,548},{132,10,153},{6,11,339},{135,11,923},{133, -11,853},{133,10,798},{132,10,587},{6,11,249},{7,11,1234},{139,11,573},{6,10,598} -,{7,10,42},{8,10,695},{10,10,212},{11,10,158},{14,10,196},{145,10,85},{7,0,249}, -{5,10,957},{133,10,1008},{4,10,129},{135,10,465},{6,0,254},{7,0,842},{7,0,1659}, -{9,0,109},{10,0,103},{7,10,908},{7,10,1201},{9,10,755},{11,10,906},{12,10,527},{ -146,10,7},{5,0,262},{136,10,450},{144,0,1},{10,11,201},{142,11,319},{7,11,49},{7 -,11,392},{8,11,20},{8,11,172},{8,11,690},{9,11,383},{9,11,845},{10,11,48},{11,11 -,293},{11,11,832},{11,11,920},{141,11,221},{5,11,858},{133,11,992},{134,0,805},{ -139,10,1003},{6,0,1630},{134,11,307},{7,11,1512},{135,11,1794},{6,11,268},{137, -11,62},{135,10,1868},{133,0,671},{4,0,989},{8,0,972},{136,0,998},{132,11,423},{ -132,0,889},{135,0,1382},{135,0,1910},{7,10,965},{7,10,1460},{135,10,1604},{4,0, -627},{5,0,775},{138,11,106},{134,11,348},{7,0,202},{11,0,362},{11,0,948},{140,0, -388},{138,11,771},{6,11,613},{136,11,223},{6,0,560},{7,0,451},{8,0,389},{12,0, -490},{13,0,16},{13,0,215},{13,0,351},{18,0,132},{147,0,125},{135,0,841},{136,0, -566},{136,0,938},{132,11,670},{5,0,912},{6,0,1695},{140,11,55},{9,11,40},{139,11 -,136},{7,0,1361},{7,10,982},{10,10,32},{143,10,56},{11,11,259},{140,11,270},{5,0 -,236},{6,0,572},{8,0,492},{11,0,618},{144,0,56},{8,11,572},{9,11,310},{9,11,682} -,{137,11,698},{134,0,1854},{5,0,190},{136,0,318},{133,10,435},{135,0,1376},{4,11 -,296},{6,11,352},{7,11,401},{7,11,1410},{7,11,1594},{7,11,1674},{8,11,63},{8,11, -660},{137,11,74},{7,0,349},{5,10,85},{6,10,419},{7,10,305},{7,10,361},{7,10,1337 -},{8,10,71},{140,10,519},{4,11,139},{4,11,388},{140,11,188},{6,0,1972},{6,0,2013 -},{8,0,951},{10,0,947},{10,0,974},{10,0,1018},{142,0,476},{140,10,688},{135,10, -740},{5,10,691},{7,10,345},{9,10,94},{140,10,169},{9,0,344},{5,10,183},{6,10,582 -},{10,10,679},{140,10,435},{135,10,511},{132,0,850},{8,11,441},{10,11,314},{143, -11,3},{7,10,1993},{136,10,684},{4,11,747},{6,11,290},{6,10,583},{7,11,649},{7,11 -,1479},{135,11,1583},{133,11,232},{133,10,704},{134,0,910},{4,10,179},{5,10,198} -,{133,10,697},{7,10,347},{7,10,971},{8,10,181},{138,10,711},{136,11,525},{14,0, -19},{14,0,28},{144,0,29},{7,0,85},{7,0,247},{8,0,585},{138,0,163},{4,0,487},{7, -11,472},{7,11,1801},{10,11,748},{141,11,458},{4,10,243},{5,10,203},{7,10,19},{7, -10,71},{7,10,113},{10,10,405},{11,10,357},{142,10,240},{7,10,1450},{139,10,99},{ -132,11,425},{138,0,145},{147,0,83},{6,10,492},{137,11,247},{4,0,1013},{134,0, -2033},{5,10,134},{6,10,408},{6,10,495},{135,10,1593},{135,0,1922},{134,11,1768}, -{4,0,124},{10,0,457},{11,0,121},{11,0,169},{11,0,870},{11,0,874},{12,0,214},{14, -0,187},{143,0,77},{5,0,557},{135,0,1457},{139,0,66},{5,11,943},{6,11,1779},{142, -10,4},{4,10,248},{4,10,665},{7,10,137},{137,10,349},{7,0,1193},{5,11,245},{6,11, -576},{7,11,582},{136,11,225},{144,0,82},{7,10,1270},{139,10,612},{5,0,454},{10,0 -,352},{138,11,352},{18,0,57},{5,10,371},{135,10,563},{135,0,1333},{6,0,107},{7,0 -,638},{7,0,1632},{9,0,396},{134,11,610},{5,0,370},{134,0,1756},{4,10,374},{7,10, -547},{7,10,1700},{7,10,1833},{139,10,858},{133,0,204},{6,0,1305},{9,10,311},{141 -,10,42},{5,0,970},{134,0,1706},{6,10,1647},{7,10,1552},{7,10,2010},{9,10,494},{ -137,10,509},{13,11,455},{15,11,99},{15,11,129},{144,11,68},{135,0,3},{4,0,35},{5 -,0,121},{5,0,483},{5,0,685},{6,0,489},{6,0,782},{6,0,1032},{7,0,1204},{136,0,394 -},{4,0,921},{133,0,1007},{8,11,360},{138,11,63},{135,0,1696},{134,0,1519},{132, -11,443},{135,11,944},{6,10,123},{7,10,214},{9,10,728},{10,10,157},{11,10,346},{ -11,10,662},{143,10,106},{137,0,981},{135,10,1435},{134,0,1072},{132,0,712},{134, -0,1629},{134,0,728},{4,11,298},{137,11,483},{6,0,1177},{6,0,1271},{5,11,164},{7, -11,121},{142,11,189},{7,0,1608},{4,10,707},{5,10,588},{6,10,393},{13,10,106},{18 -,10,49},{147,10,41},{23,0,16},{151,11,16},{6,10,211},{7,10,1690},{11,10,486},{ -140,10,369},{133,0,485},{19,11,15},{149,11,27},{4,11,172},{9,11,611},{10,11,436} -,{12,11,673},{141,11,255},{5,11,844},{10,11,484},{11,11,754},{12,11,457},{14,11, -171},{14,11,389},{146,11,153},{4,0,285},{5,0,27},{5,0,317},{6,0,301},{7,0,7},{8, -0,153},{10,0,766},{11,0,468},{12,0,467},{141,0,143},{134,0,1462},{9,11,263},{10, -11,147},{138,11,492},{133,11,537},{6,0,1945},{6,0,1986},{6,0,1991},{134,0,2038}, -{134,10,219},{137,11,842},{14,0,52},{17,0,50},{5,10,582},{6,10,1646},{7,10,99},{ -7,10,1962},{7,10,1986},{8,10,515},{8,10,773},{9,10,23},{9,10,491},{12,10,620},{ -142,10,93},{138,11,97},{20,0,21},{20,0,44},{133,10,851},{136,0,819},{139,0,917}, -{5,11,230},{5,11,392},{6,11,420},{8,10,762},{8,10,812},{9,11,568},{9,10,910},{ -140,11,612},{135,0,784},{15,0,135},{143,11,135},{10,0,454},{140,0,324},{4,11,0}, -{5,11,41},{7,11,1459},{7,11,1469},{7,11,1618},{7,11,1859},{9,11,549},{139,11,905 -},{4,10,98},{7,10,1365},{9,10,422},{9,10,670},{10,10,775},{11,10,210},{13,10,26} -,{13,10,457},{141,10,476},{6,0,1719},{6,0,1735},{7,0,2016},{7,0,2020},{8,0,837}, -{137,0,852},{133,11,696},{135,0,852},{132,0,952},{134,10,1730},{132,11,771},{138 -,0,568},{137,0,448},{139,0,146},{8,0,67},{138,0,419},{133,11,921},{137,10,147},{ -134,0,1826},{10,0,657},{14,0,297},{142,0,361},{6,0,666},{6,0,767},{134,0,1542},{ -139,0,729},{6,11,180},{7,11,1137},{8,11,751},{139,11,805},{4,11,183},{7,11,271}, -{11,11,824},{11,11,952},{13,11,278},{13,11,339},{13,11,482},{14,11,424},{148,11, -99},{4,0,669},{5,11,477},{5,11,596},{6,11,505},{7,11,1221},{11,11,907},{12,11, -209},{141,11,214},{135,11,1215},{5,0,402},{6,10,30},{11,10,56},{139,10,305},{7, -11,564},{142,11,168},{139,0,152},{7,0,912},{135,10,1614},{4,10,150},{5,10,303},{ -134,10,327},{7,0,320},{8,0,51},{9,0,868},{10,0,833},{12,0,481},{12,0,570},{148,0 -,106},{132,0,445},{7,11,274},{11,11,263},{11,11,479},{11,11,507},{140,11,277},{ -10,0,555},{11,0,308},{19,0,95},{6,11,1645},{8,10,192},{10,10,78},{141,10,359},{ -135,10,786},{6,11,92},{6,11,188},{7,11,1269},{7,11,1524},{7,11,1876},{10,11,228} -,{139,11,1020},{4,11,459},{133,11,966},{11,0,386},{6,10,1638},{7,10,79},{7,10, -496},{9,10,138},{10,10,336},{12,10,412},{12,10,440},{142,10,305},{133,0,239},{7, -0,83},{7,0,1990},{8,0,130},{139,0,720},{138,11,709},{4,0,143},{5,0,550},{133,0, -752},{5,0,123},{6,0,530},{7,0,348},{135,0,1419},{135,0,2024},{6,11,18},{7,11,179 -},{7,11,721},{7,11,932},{8,11,548},{8,11,757},{9,11,54},{9,11,65},{9,11,532},{9, -11,844},{10,11,113},{10,11,117},{10,11,236},{10,11,315},{10,11,430},{10,11,798}, -{11,11,153},{11,11,351},{11,11,375},{12,11,78},{12,11,151},{12,11,392},{14,11, -248},{143,11,23},{7,10,204},{7,10,415},{8,10,42},{10,10,85},{139,10,564},{134,0, -958},{133,11,965},{132,0,210},{135,11,1429},{138,11,480},{134,11,182},{139,11, -345},{10,11,65},{10,11,488},{138,11,497},{4,10,3},{5,10,247},{5,10,644},{7,10, -744},{7,10,1207},{7,10,1225},{7,10,1909},{146,10,147},{132,0,430},{5,10,285},{9, -10,67},{13,10,473},{143,10,82},{144,11,16},{7,11,1162},{9,11,588},{10,11,260},{ -151,10,8},{133,0,213},{138,0,7},{135,0,801},{134,11,1786},{135,11,308},{6,0,936} -,{134,0,1289},{133,0,108},{132,0,885},{133,0,219},{139,0,587},{4,0,193},{5,0,916 -},{6,0,1041},{7,0,364},{10,0,398},{10,0,726},{11,0,317},{11,0,626},{12,0,142},{ -12,0,288},{12,0,678},{13,0,313},{15,0,113},{146,0,114},{135,0,1165},{6,0,241},{9 -,0,342},{10,0,729},{11,0,284},{11,0,445},{11,0,651},{11,0,863},{13,0,398},{146,0 -,99},{7,0,907},{136,0,832},{9,0,303},{4,10,29},{6,10,532},{7,10,1628},{7,10,1648 -},{9,10,350},{10,10,433},{11,10,97},{11,10,557},{11,10,745},{12,10,289},{12,10, -335},{12,10,348},{12,10,606},{13,10,116},{13,10,233},{13,10,466},{14,10,181},{14 -,10,209},{14,10,232},{14,10,236},{14,10,300},{16,10,41},{148,10,97},{7,11,423},{ -7,10,1692},{136,11,588},{6,0,931},{134,0,1454},{5,10,501},{7,10,1704},{9,10,553} -,{11,10,520},{12,10,557},{141,10,249},{136,11,287},{4,0,562},{9,0,254},{139,0, -879},{132,0,786},{14,11,32},{18,11,85},{20,11,2},{152,11,16},{135,0,1294},{7,11, -723},{135,11,1135},{6,0,216},{7,0,901},{7,0,1343},{8,0,493},{134,11,403},{7,11, -719},{8,11,809},{136,11,834},{5,11,210},{6,11,213},{7,11,60},{10,11,364},{139,11 -,135},{7,0,341},{11,0,219},{5,11,607},{8,11,326},{136,11,490},{4,11,701},{5,11, -472},{5,11,639},{7,11,1249},{9,11,758},{139,11,896},{135,11,380},{135,11,1947},{ -139,0,130},{135,0,1734},{10,0,115},{11,0,420},{12,0,154},{13,0,404},{14,0,346},{ -143,0,54},{134,10,129},{4,11,386},{7,11,41},{8,11,405},{9,11,497},{11,11,110},{ -11,11,360},{15,11,37},{144,11,84},{141,11,282},{5,11,46},{7,11,1452},{7,11,1480} -,{8,11,634},{140,11,472},{4,11,524},{136,11,810},{10,11,238},{141,11,33},{133,0, -604},{5,0,1011},{136,0,701},{8,0,856},{8,0,858},{8,0,879},{12,0,702},{142,0,447} -,{4,0,54},{5,0,666},{7,0,1039},{7,0,1130},{9,0,195},{138,0,302},{4,10,25},{5,10, -60},{6,10,504},{7,10,614},{7,10,1155},{140,10,0},{7,10,1248},{11,10,621},{139,10 -,702},{133,11,997},{137,10,321},{134,0,1669},{134,0,1791},{4,10,379},{135,10, -1397},{138,11,372},{5,11,782},{5,11,829},{134,11,1738},{135,0,1228},{4,10,118},{ -6,10,274},{6,10,361},{7,10,75},{141,10,441},{132,0,623},{9,11,279},{10,11,407},{ -14,11,84},{150,11,18},{137,10,841},{135,0,798},{140,10,693},{5,10,314},{6,10,221 -},{7,10,419},{10,10,650},{11,10,396},{12,10,156},{13,10,369},{14,10,333},{145,10 -,47},{135,11,1372},{7,0,122},{9,0,259},{10,0,84},{11,0,470},{12,0,541},{141,0, -379},{134,0,837},{8,0,1013},{4,11,78},{5,11,96},{5,11,182},{7,11,1724},{7,11, -1825},{10,11,394},{10,11,471},{11,11,532},{14,11,340},{145,11,88},{134,0,577},{ -135,11,1964},{132,10,913},{134,0,460},{8,0,891},{10,0,901},{10,0,919},{10,0,932} -,{12,0,715},{12,0,728},{12,0,777},{14,0,457},{144,0,103},{5,0,82},{5,0,131},{7,0 -,1755},{8,0,31},{9,0,168},{9,0,764},{139,0,869},{136,10,475},{6,0,605},{5,10, -1016},{9,11,601},{9,11,619},{10,11,505},{10,11,732},{11,11,355},{140,11,139},{7, -10,602},{8,10,179},{10,10,781},{140,10,126},{134,0,1246},{6,10,329},{138,10,111} -,{6,11,215},{7,11,1028},{7,11,1473},{7,11,1721},{9,11,424},{138,11,779},{5,0,278 -},{137,0,68},{6,0,932},{6,0,1084},{144,0,86},{4,0,163},{5,0,201},{5,0,307},{5,0, -310},{6,0,335},{7,0,284},{7,0,1660},{136,0,165},{136,0,781},{134,0,707},{6,0,33} -,{135,0,1244},{5,10,821},{6,11,67},{6,10,1687},{7,11,258},{7,11,1630},{9,11,354} -,{9,11,675},{10,11,830},{14,11,80},{145,11,80},{6,11,141},{7,11,225},{9,11,59},{ -9,11,607},{10,11,312},{11,11,687},{12,11,555},{13,11,373},{13,11,494},{148,11,58 -},{134,0,1113},{9,0,388},{5,10,71},{7,10,1407},{9,10,704},{10,10,261},{10,10,619 -},{11,10,547},{11,10,619},{143,10,157},{7,0,1953},{136,0,720},{138,0,203},{7,10, -2008},{9,10,337},{138,10,517},{6,0,326},{7,0,677},{137,0,425},{139,11,81},{7,0, -1316},{7,0,1412},{7,0,1839},{9,0,589},{11,0,241},{11,0,676},{11,0,811},{11,0,891 -},{12,0,140},{12,0,346},{12,0,479},{13,0,140},{13,0,381},{14,0,188},{18,0,30},{ -148,0,108},{5,0,416},{6,10,86},{6,10,603},{7,10,292},{7,10,561},{8,10,257},{8,10 -,382},{9,10,721},{9,10,778},{11,10,581},{140,10,466},{4,10,486},{133,10,491},{ -134,0,1300},{132,10,72},{7,0,847},{6,10,265},{7,11,430},{139,11,46},{5,11,602},{ -6,11,106},{7,11,1786},{7,11,1821},{7,11,2018},{9,11,418},{137,11,763},{5,0,358}, -{7,0,535},{7,0,1184},{10,0,662},{13,0,212},{13,0,304},{13,0,333},{145,0,98},{5, -11,65},{6,11,416},{7,11,1720},{7,11,1924},{8,11,677},{10,11,109},{11,11,14},{11, -11,70},{11,11,569},{11,11,735},{15,11,153},{148,11,80},{6,0,1823},{8,0,839},{8,0 -,852},{8,0,903},{10,0,940},{12,0,707},{140,0,775},{135,11,1229},{6,0,1522},{140, -0,654},{136,11,595},{139,0,163},{141,0,314},{132,0,978},{4,0,601},{6,0,2035},{ -137,10,234},{5,10,815},{6,10,1688},{134,10,1755},{133,0,946},{136,0,434},{6,10, -197},{136,10,205},{7,0,411},{7,0,590},{8,0,631},{9,0,323},{10,0,355},{11,0,491}, -{12,0,143},{12,0,402},{13,0,73},{14,0,408},{15,0,107},{146,0,71},{7,0,1467},{8,0 -,328},{10,0,544},{11,0,955},{12,0,13},{13,0,320},{145,0,83},{142,0,410},{11,0, -511},{13,0,394},{14,0,298},{14,0,318},{146,0,103},{6,10,452},{7,10,312},{138,10, -219},{138,10,589},{4,10,333},{9,10,176},{12,10,353},{141,10,187},{135,11,329},{ -132,11,469},{5,0,835},{134,0,483},{134,11,1743},{5,11,929},{6,11,340},{8,11,376} -,{136,11,807},{134,10,1685},{132,0,677},{5,11,218},{7,11,1610},{138,11,83},{5,11 -,571},{135,11,1842},{132,11,455},{137,0,70},{135,0,1405},{7,10,135},{8,10,7},{8, -10,62},{9,10,243},{10,10,658},{10,10,697},{11,10,456},{139,10,756},{9,10,395},{ -138,10,79},{137,0,108},{6,11,161},{7,11,372},{137,11,597},{132,11,349},{132,0, -777},{132,0,331},{135,10,631},{133,0,747},{6,11,432},{6,11,608},{139,11,322},{ -138,10,835},{5,11,468},{7,11,1809},{10,11,325},{11,11,856},{12,11,345},{143,11, -104},{133,11,223},{7,10,406},{7,10,459},{8,10,606},{139,10,726},{132,11,566},{ -142,0,68},{4,11,59},{135,11,1394},{6,11,436},{139,11,481},{4,11,48},{5,11,271},{ -135,11,953},{139,11,170},{5,11,610},{136,11,457},{133,11,755},{135,11,1217},{133 -,10,612},{132,11,197},{132,0,505},{4,10,372},{7,10,482},{8,10,158},{9,10,602},{9 -,10,615},{10,10,245},{10,10,678},{10,10,744},{11,10,248},{139,10,806},{133,0,326 -},{5,10,854},{135,10,1991},{4,0,691},{146,0,16},{6,0,628},{9,0,35},{10,0,680},{ -10,0,793},{11,0,364},{13,0,357},{143,0,164},{138,0,654},{6,0,32},{7,0,385},{7,0, -757},{7,0,1916},{8,0,37},{8,0,94},{8,0,711},{9,0,541},{10,0,162},{10,0,795},{11, -0,989},{11,0,1010},{12,0,14},{142,0,308},{133,11,217},{6,0,152},{6,0,349},{6,0, -1682},{7,0,1252},{8,0,112},{9,0,435},{9,0,668},{10,0,290},{10,0,319},{10,0,815}, -{11,0,180},{11,0,837},{12,0,240},{13,0,152},{13,0,219},{142,0,158},{4,0,581},{ -134,0,726},{5,10,195},{135,10,1685},{6,0,126},{7,0,573},{8,0,397},{142,0,44},{ -138,0,89},{7,10,1997},{8,10,730},{139,10,1006},{134,0,1531},{134,0,1167},{5,0, -926},{12,0,203},{133,10,751},{4,11,165},{7,11,1398},{135,11,1829},{7,0,1232},{ -137,0,531},{135,10,821},{134,0,943},{133,0,670},{4,0,880},{139,0,231},{134,0, -1617},{135,0,1957},{5,11,9},{7,11,297},{7,11,966},{140,11,306},{6,0,975},{134,0, -985},{5,10,950},{5,10,994},{134,10,351},{12,11,21},{151,11,7},{5,11,146},{6,11, -411},{138,11,721},{7,0,242},{135,0,1942},{6,11,177},{135,11,467},{5,0,421},{7,10 -,47},{137,10,684},{5,0,834},{7,0,1202},{8,0,14},{9,0,481},{137,0,880},{138,0,465 -},{6,0,688},{9,0,834},{132,10,350},{132,0,855},{4,0,357},{6,0,172},{7,0,143},{ -137,0,413},{133,11,200},{132,0,590},{7,10,1812},{13,10,259},{13,10,356},{14,10, -242},{147,10,114},{133,10,967},{11,0,114},{4,10,473},{7,10,623},{8,10,808},{9,10 -,871},{9,10,893},{11,10,431},{12,10,112},{12,10,217},{12,10,243},{12,10,562},{12 -,10,663},{12,10,683},{13,10,141},{13,10,197},{13,10,227},{13,10,406},{13,10,487} -,{14,10,156},{14,10,203},{14,10,224},{14,10,256},{18,10,58},{150,10,0},{138,10, -286},{4,10,222},{7,10,286},{136,10,629},{5,0,169},{7,0,333},{136,0,45},{134,11, -481},{132,0,198},{4,0,24},{5,0,140},{5,0,185},{7,0,1500},{11,0,565},{11,0,838},{ -4,11,84},{7,11,1482},{10,11,76},{138,11,142},{133,0,585},{141,10,306},{133,11, -1015},{4,11,315},{5,11,507},{135,11,1370},{136,10,146},{6,0,691},{134,0,1503},{4 -,0,334},{133,0,593},{4,10,465},{135,10,1663},{142,11,173},{135,0,913},{12,0,116} -,{134,11,1722},{134,0,1360},{132,0,802},{8,11,222},{8,11,476},{9,11,238},{11,11, -516},{11,11,575},{15,11,109},{146,11,100},{6,0,308},{9,0,673},{7,10,138},{7,10, -517},{139,10,238},{132,0,709},{6,0,1876},{6,0,1895},{9,0,994},{9,0,1006},{12,0, -829},{12,0,888},{12,0,891},{146,0,185},{148,10,94},{4,0,228},{133,0,897},{7,0, -1840},{5,10,495},{7,10,834},{9,10,733},{139,10,378},{133,10,559},{6,10,21},{6,10 -,1737},{7,10,1444},{136,10,224},{4,0,608},{133,0,497},{6,11,40},{135,11,1781},{ -134,0,1573},{135,0,2039},{6,0,540},{136,0,136},{4,0,897},{5,0,786},{133,10,519}, -{6,0,1878},{6,0,1884},{9,0,938},{9,0,948},{9,0,955},{9,0,973},{9,0,1012},{12,0, -895},{12,0,927},{143,0,254},{134,0,1469},{133,0,999},{4,0,299},{135,0,1004},{4,0 -,745},{133,0,578},{136,11,574},{133,0,456},{134,0,1457},{7,0,1679},{132,10,402}, -{7,0,693},{8,0,180},{12,0,163},{8,10,323},{136,10,479},{11,10,580},{142,10,201}, -{5,10,59},{135,10,672},{132,11,354},{146,10,34},{4,0,755},{135,11,1558},{7,0, -1740},{146,0,48},{4,10,85},{135,10,549},{139,0,338},{133,10,94},{134,0,1091},{ -135,11,469},{12,0,695},{12,0,704},{20,0,113},{5,11,830},{14,11,338},{148,11,81}, -{135,0,1464},{6,10,11},{135,10,187},{135,0,975},{13,0,335},{132,10,522},{134,0, -1979},{5,11,496},{135,11,203},{4,10,52},{135,10,661},{7,0,1566},{8,0,269},{9,0, -212},{9,0,718},{14,0,15},{14,0,132},{142,0,227},{4,0,890},{5,0,805},{5,0,819},{5 -,0,961},{6,0,396},{6,0,1631},{6,0,1678},{7,0,1967},{7,0,2041},{9,0,630},{11,0,8} -,{11,0,1019},{12,0,176},{13,0,225},{14,0,292},{21,0,24},{4,10,383},{133,10,520}, -{134,11,547},{135,11,1748},{5,11,88},{137,11,239},{146,11,128},{7,11,650},{135, -11,1310},{4,10,281},{5,10,38},{7,10,194},{7,10,668},{7,10,1893},{137,10,397},{ -135,0,1815},{9,10,635},{139,10,559},{7,0,1505},{10,0,190},{10,0,634},{11,0,792}, -{12,0,358},{140,0,447},{5,0,0},{6,0,536},{7,0,604},{13,0,445},{145,0,126},{7,11, -1076},{9,11,80},{11,11,78},{11,11,421},{11,11,534},{140,11,545},{8,0,966},{10,0, -1023},{14,11,369},{146,11,72},{135,11,1641},{6,0,232},{6,0,412},{7,0,1074},{8,0, -9},{8,0,157},{8,0,786},{9,0,196},{9,0,352},{9,0,457},{10,0,337},{11,0,232},{11,0 -,877},{12,0,480},{140,0,546},{135,0,958},{4,0,382},{136,0,579},{4,0,212},{135,0, -1206},{4,11,497},{5,11,657},{135,11,1584},{132,0,681},{8,0,971},{138,0,965},{5, -10,448},{136,10,535},{14,0,16},{146,0,44},{11,0,584},{11,0,616},{14,0,275},{11, -11,584},{11,11,616},{142,11,275},{136,11,13},{7,10,610},{135,10,1501},{7,11,642} -,{8,11,250},{11,11,123},{11,11,137},{13,11,48},{142,11,95},{133,0,655},{17,0,67} -,{147,0,74},{134,0,751},{134,0,1967},{6,0,231},{136,0,423},{5,0,300},{138,0,1016 -},{4,10,319},{5,10,699},{138,10,673},{6,0,237},{7,0,611},{8,0,100},{9,0,416},{11 -,0,335},{12,0,173},{18,0,101},{6,10,336},{8,10,552},{9,10,285},{10,10,99},{139, -10,568},{134,0,1370},{7,10,1406},{9,10,218},{141,10,222},{133,10,256},{135,0, -1208},{14,11,213},{148,11,38},{6,0,1219},{135,11,1642},{13,0,417},{14,0,129},{ -143,0,15},{10,11,545},{140,11,301},{17,10,39},{148,10,36},{133,0,199},{4,11,904} -,{133,11,794},{12,0,427},{146,0,38},{134,0,949},{8,0,665},{135,10,634},{132,10, -618},{135,10,259},{132,10,339},{133,11,761},{141,10,169},{132,10,759},{5,0,688}, -{7,0,539},{135,0,712},{7,11,386},{138,11,713},{134,0,1186},{6,11,7},{6,11,35},{7 -,11,147},{7,11,1069},{7,11,1568},{7,11,1575},{7,11,1917},{8,11,43},{8,11,208},{9 -,11,128},{9,11,866},{10,11,20},{11,11,981},{147,11,33},{7,11,893},{8,10,482},{ -141,11,424},{6,0,312},{6,0,1715},{10,0,584},{11,0,546},{11,0,692},{12,0,259},{12 -,0,295},{13,0,46},{141,0,154},{5,10,336},{6,10,341},{6,10,478},{6,10,1763},{136, -10,386},{137,0,151},{132,0,588},{152,0,4},{6,11,322},{9,11,552},{11,11,274},{13, -11,209},{13,11,499},{14,11,85},{15,11,126},{145,11,70},{135,10,73},{4,0,231},{5, -0,61},{6,0,104},{7,0,729},{7,0,964},{7,0,1658},{140,0,414},{6,0,263},{138,0,757} -,{135,10,1971},{4,0,612},{133,0,561},{132,0,320},{135,10,1344},{8,11,83},{8,11, -817},{9,11,28},{9,11,29},{9,11,885},{10,11,387},{11,11,633},{11,11,740},{13,11, -235},{13,11,254},{15,11,143},{143,11,146},{5,10,396},{134,10,501},{140,11,49},{ -132,0,225},{4,10,929},{5,10,799},{8,10,46},{136,10,740},{4,0,405},{7,0,817},{14, -0,58},{17,0,37},{146,0,124},{133,0,974},{4,11,412},{133,11,581},{4,10,892},{133, -10,770},{4,0,996},{134,0,2026},{4,0,527},{5,0,235},{7,0,1239},{11,0,131},{140,0, -370},{9,0,16},{13,0,386},{135,11,421},{7,0,956},{7,0,1157},{7,0,1506},{7,0,1606} -,{7,0,1615},{7,0,1619},{7,0,1736},{7,0,1775},{8,0,590},{9,0,324},{9,0,736},{9,0, -774},{9,0,776},{9,0,784},{10,0,567},{10,0,708},{11,0,518},{11,0,613},{11,0,695}, -{11,0,716},{11,0,739},{11,0,770},{11,0,771},{11,0,848},{11,0,857},{11,0,931},{11 -,0,947},{12,0,326},{12,0,387},{12,0,484},{12,0,528},{12,0,552},{12,0,613},{13,0, -189},{13,0,256},{13,0,340},{13,0,432},{13,0,436},{13,0,440},{13,0,454},{14,0,174 -},{14,0,220},{14,0,284},{14,0,390},{145,0,121},{135,10,158},{9,0,137},{138,0,221 -},{4,11,110},{10,11,415},{10,11,597},{142,11,206},{141,11,496},{135,11,205},{151 -,10,25},{135,11,778},{7,11,1656},{7,10,2001},{9,11,369},{10,11,338},{10,11,490}, -{11,11,154},{11,11,545},{11,11,775},{13,11,77},{141,11,274},{4,11,444},{10,11, -146},{140,11,9},{7,0,390},{138,0,140},{135,0,1144},{134,0,464},{7,10,1461},{140, -10,91},{132,10,602},{4,11,283},{135,11,1194},{5,0,407},{11,0,204},{11,0,243},{11 -,0,489},{12,0,293},{19,0,37},{20,0,73},{150,0,38},{7,0,1218},{136,0,303},{5,0, -325},{8,0,5},{8,0,227},{9,0,105},{10,0,585},{12,0,614},{4,10,13},{5,10,567},{7, -10,1498},{9,10,124},{11,10,521},{140,10,405},{135,10,1006},{7,0,800},{10,0,12},{ -134,11,1720},{135,0,1783},{132,10,735},{138,10,812},{4,10,170},{135,10,323},{6,0 -,621},{13,0,504},{144,0,89},{5,10,304},{135,10,1403},{137,11,216},{6,0,920},{6,0 -,1104},{9,11,183},{139,11,286},{4,0,376},{133,10,742},{134,0,218},{8,0,641},{11, -0,388},{140,0,580},{7,0,454},{7,0,782},{8,0,768},{140,0,686},{137,11,33},{133,10 -,111},{144,0,0},{10,0,676},{140,0,462},{6,0,164},{136,11,735},{133,10,444},{150, -0,50},{7,11,1862},{12,11,491},{12,11,520},{13,11,383},{14,11,244},{146,11,12},{5 -,11,132},{9,11,486},{9,11,715},{10,11,458},{11,11,373},{11,11,668},{11,11,795},{ -11,11,897},{12,11,272},{12,11,424},{12,11,539},{12,11,558},{14,11,245},{14,11, -263},{14,11,264},{14,11,393},{142,11,403},{8,10,123},{15,10,6},{144,10,7},{6,0, -285},{8,0,654},{11,0,749},{12,0,190},{12,0,327},{13,0,120},{13,0,121},{13,0,327} -,{15,0,47},{146,0,40},{5,11,8},{6,11,89},{6,11,400},{7,11,1569},{7,11,1623},{7, -11,1850},{8,11,218},{8,11,422},{9,11,570},{138,11,626},{6,11,387},{7,11,882},{ -141,11,111},{6,0,343},{7,0,195},{9,0,226},{10,0,197},{10,0,575},{11,0,502},{11,0 -,899},{6,11,224},{7,11,877},{137,11,647},{5,10,937},{135,10,100},{135,11,790},{ -150,0,29},{147,0,8},{134,0,1812},{149,0,8},{135,11,394},{7,0,1125},{9,0,143},{11 -,0,61},{14,0,405},{150,0,21},{10,11,755},{147,11,29},{9,11,378},{141,11,162},{ -135,10,922},{5,10,619},{133,10,698},{134,0,1327},{6,0,1598},{137,0,575},{9,11, -569},{12,11,12},{12,11,81},{12,11,319},{13,11,69},{14,11,259},{16,11,87},{17,11, -1},{17,11,21},{17,11,24},{18,11,15},{18,11,56},{18,11,59},{18,11,127},{18,11,154 -},{19,11,19},{148,11,31},{6,0,895},{135,11,1231},{5,0,959},{7,11,124},{136,11,38 -},{5,11,261},{7,11,78},{7,11,199},{8,11,815},{9,11,126},{138,11,342},{5,10,917}, -{134,10,1659},{7,0,1759},{5,11,595},{135,11,1863},{136,0,173},{134,0,266},{142,0 -,261},{132,11,628},{5,10,251},{5,10,956},{8,10,268},{9,10,214},{146,10,142},{7, -11,266},{136,11,804},{135,11,208},{6,11,79},{7,11,1021},{135,11,1519},{11,11,704 -},{141,11,396},{5,10,346},{5,10,711},{136,10,390},{136,11,741},{134,11,376},{134 -,0,1427},{6,0,1033},{6,0,1217},{136,0,300},{133,10,624},{6,11,100},{7,11,244},{7 -,11,632},{7,11,1609},{8,11,178},{8,11,638},{141,11,58},{6,0,584},{5,10,783},{7, -10,1998},{135,10,2047},{5,0,427},{5,0,734},{7,0,478},{136,0,52},{7,0,239},{11,0, -217},{142,0,165},{134,0,1129},{6,0,168},{6,0,1734},{7,0,20},{7,0,1056},{8,0,732} -,{9,0,406},{9,0,911},{138,0,694},{132,10,594},{133,11,791},{7,11,686},{8,11,33}, -{8,11,238},{10,11,616},{11,11,467},{11,11,881},{13,11,217},{13,11,253},{142,11, -268},{137,11,476},{134,0,418},{133,0,613},{132,0,632},{132,11,447},{7,0,32},{7,0 -,984},{8,0,85},{8,0,709},{9,0,579},{9,0,847},{9,0,856},{10,0,799},{11,0,258},{11 -,0,1007},{12,0,331},{12,0,615},{13,0,188},{13,0,435},{14,0,8},{15,0,165},{16,0, -27},{20,0,40},{144,11,35},{4,11,128},{5,11,415},{6,11,462},{7,11,294},{7,11,578} -,{10,11,710},{139,11,86},{5,0,694},{136,0,909},{7,0,1109},{11,0,7},{5,10,37},{6, -10,39},{6,10,451},{7,10,218},{7,10,1166},{7,10,1687},{8,10,662},{144,10,2},{136, -11,587},{6,11,427},{7,11,1018},{138,11,692},{4,11,195},{6,10,508},{135,11,802},{ -4,0,167},{135,0,82},{5,0,62},{6,0,24},{6,0,534},{7,0,74},{7,0,678},{7,0,684},{7, -0,1043},{7,0,1072},{8,0,280},{8,0,541},{8,0,686},{9,0,258},{10,0,519},{11,0,252} -,{140,0,282},{138,0,33},{4,0,359},{133,11,738},{7,0,980},{9,0,328},{13,0,186},{ -13,0,364},{7,10,635},{7,10,796},{8,10,331},{9,10,330},{9,10,865},{10,10,119},{10 -,10,235},{11,10,111},{11,10,129},{11,10,240},{12,10,31},{12,10,66},{12,10,222},{ -12,10,269},{12,10,599},{12,10,684},{12,10,689},{12,10,691},{142,10,345},{137,10, -527},{6,0,596},{7,0,585},{135,10,702},{134,11,1683},{133,0,211},{6,0,145},{141,0 -,336},{134,0,1130},{7,0,873},{6,10,37},{7,10,1666},{8,10,195},{8,10,316},{9,10, -178},{9,10,276},{9,10,339},{9,10,536},{10,10,102},{10,10,362},{10,10,785},{11,10 -,55},{11,10,149},{11,10,773},{13,10,416},{13,10,419},{14,10,38},{14,10,41},{142, -10,210},{8,0,840},{136,0,841},{132,0,263},{5,11,3},{8,11,578},{9,11,118},{10,11, -705},{12,11,383},{141,11,279},{132,0,916},{133,11,229},{133,10,645},{15,0,155},{ -16,0,79},{8,11,102},{10,11,578},{10,11,672},{12,11,496},{13,11,408},{14,11,121}, -{145,11,106},{4,0,599},{5,0,592},{6,0,1634},{7,0,5},{7,0,55},{7,0,67},{7,0,97},{ -7,0,691},{7,0,979},{7,0,1600},{7,0,1697},{8,0,207},{8,0,214},{8,0,231},{8,0,294} -,{8,0,336},{8,0,428},{8,0,471},{8,0,622},{8,0,626},{8,0,679},{8,0,759},{8,0,829} -,{9,0,11},{9,0,246},{9,0,484},{9,0,573},{9,0,706},{9,0,762},{9,0,798},{9,0,855}, -{9,0,870},{9,0,912},{10,0,303},{10,0,335},{10,0,424},{10,0,461},{10,0,543},{10,0 -,759},{10,0,814},{11,0,59},{11,0,199},{11,0,235},{11,0,590},{11,0,631},{11,0,929 -},{11,0,963},{11,0,987},{12,0,114},{12,0,182},{12,0,226},{12,0,332},{12,0,439},{ -12,0,575},{12,0,598},{12,0,675},{13,0,8},{13,0,125},{13,0,194},{13,0,287},{14,0, -197},{14,0,383},{15,0,53},{17,0,63},{19,0,46},{19,0,98},{19,0,106},{148,0,85},{7 -,0,1356},{132,10,290},{6,10,70},{7,10,1292},{10,10,762},{139,10,288},{150,11,55} -,{4,0,593},{8,11,115},{8,11,350},{9,11,489},{10,11,128},{11,11,306},{12,11,373}, -{14,11,30},{17,11,79},{147,11,80},{135,11,1235},{134,0,1392},{4,11,230},{133,11, -702},{147,0,126},{7,10,131},{7,10,422},{8,10,210},{140,10,573},{134,0,1179},{139 -,11,435},{139,10,797},{134,11,1728},{4,0,162},{18,11,26},{19,11,42},{20,11,43},{ -21,11,0},{23,11,27},{152,11,14},{132,10,936},{6,0,765},{5,10,453},{134,10,441},{ -133,0,187},{135,0,1286},{6,0,635},{6,0,904},{6,0,1210},{134,0,1489},{4,0,215},{8 -,0,890},{9,0,38},{10,0,923},{11,0,23},{11,0,127},{139,0,796},{6,0,1165},{134,0, -1306},{7,0,716},{13,0,97},{141,0,251},{132,10,653},{136,0,657},{146,10,80},{5,11 -,622},{7,11,1032},{11,11,26},{11,11,213},{11,11,707},{12,11,380},{13,11,226},{ -141,11,355},{6,0,299},{5,11,70},{6,11,334},{9,11,171},{11,11,637},{12,11,202},{ -14,11,222},{145,11,42},{142,0,134},{4,11,23},{5,11,313},{5,11,1014},{6,11,50},{6 -,11,51},{7,11,142},{7,11,384},{9,11,783},{139,11,741},{4,11,141},{7,11,559},{8, -11,640},{9,11,460},{12,11,183},{141,11,488},{136,11,614},{7,10,1368},{8,10,232}, -{8,10,361},{10,10,682},{138,10,742},{137,10,534},{6,0,1082},{140,0,658},{137,10, -27},{135,0,2002},{142,10,12},{4,0,28},{5,0,440},{7,0,248},{11,0,833},{140,0,344} -,{7,10,736},{139,10,264},{134,10,1657},{134,0,1654},{138,0,531},{5,11,222},{9,11 -,140},{138,11,534},{6,0,634},{6,0,798},{134,0,840},{138,11,503},{135,10,127},{ -133,0,853},{5,11,154},{7,11,1491},{10,11,379},{138,11,485},{6,0,249},{7,0,1234}, -{139,0,573},{133,11,716},{7,11,1570},{140,11,542},{136,10,364},{138,0,527},{4,11 -,91},{5,11,388},{5,11,845},{6,11,206},{6,11,252},{6,11,365},{7,11,136},{7,11,531 -},{8,11,264},{136,11,621},{134,0,1419},{135,11,1441},{7,0,49},{7,0,392},{8,0,20} -,{8,0,172},{8,0,690},{9,0,383},{9,0,845},{10,0,48},{11,0,293},{11,0,832},{11,0, -920},{11,0,984},{141,0,221},{5,0,858},{133,0,992},{5,0,728},{137,10,792},{5,10, -909},{9,10,849},{138,10,805},{7,0,525},{7,0,1579},{8,0,497},{136,0,573},{6,0,268 -},{137,0,62},{135,11,576},{134,0,1201},{5,11,771},{5,11,863},{5,11,898},{6,11, -1632},{6,11,1644},{134,11,1780},{133,11,331},{7,0,193},{7,0,1105},{10,0,495},{7, -10,397},{8,10,124},{8,10,619},{9,10,305},{11,10,40},{12,10,349},{13,10,134},{13, -10,295},{14,10,155},{15,10,120},{146,10,105},{138,0,106},{6,0,859},{5,11,107},{7 -,11,201},{136,11,518},{6,11,446},{135,11,1817},{13,0,23},{4,10,262},{135,10,342} -,{133,10,641},{137,11,851},{6,0,925},{137,0,813},{132,11,504},{6,0,613},{136,0, -223},{4,10,99},{6,10,250},{6,10,346},{8,10,127},{138,10,81},{136,0,953},{132,10, -915},{139,11,892},{5,10,75},{9,10,517},{10,10,470},{12,10,155},{141,10,224},{4,0 -,666},{7,0,1017},{7,11,996},{138,11,390},{5,11,883},{133,11,975},{14,10,83},{142 -,11,83},{4,0,670},{5,11,922},{134,11,1707},{135,0,216},{9,0,40},{11,0,136},{135, -11,787},{5,10,954},{5,11,993},{7,11,515},{137,11,91},{139,0,259},{7,0,1114},{9,0 -,310},{9,0,682},{10,0,440},{13,0,40},{6,10,304},{8,10,418},{11,10,341},{139,10, -675},{14,0,296},{9,10,410},{139,10,425},{10,11,377},{12,11,363},{13,11,68},{13, -11,94},{14,11,108},{142,11,306},{7,0,1401},{135,0,1476},{4,0,296},{6,0,475},{7,0 -,401},{7,0,1410},{7,0,1594},{7,0,1674},{8,0,63},{8,0,660},{137,0,74},{4,0,139},{ -4,0,388},{140,0,188},{132,0,797},{132,11,766},{5,11,103},{7,11,921},{8,11,580},{ -8,11,593},{8,11,630},{138,11,28},{4,11,911},{5,11,867},{133,11,1013},{134,10,14} -,{134,0,1572},{134,10,1708},{21,0,39},{5,10,113},{6,10,243},{7,10,1865},{11,10, -161},{16,10,37},{145,10,99},{7,11,1563},{141,11,182},{5,11,135},{6,11,519},{7,11 -,1722},{10,11,271},{11,11,261},{145,11,54},{132,10,274},{134,0,1594},{4,11,300}, -{5,11,436},{135,11,484},{4,0,747},{6,0,290},{7,0,649},{7,0,1479},{135,0,1583},{ -133,11,535},{147,11,82},{133,0,232},{137,0,887},{135,10,166},{136,0,521},{4,0,14 -},{7,0,472},{7,0,1801},{10,0,748},{141,0,458},{134,0,741},{134,0,992},{16,0,111} -,{137,10,304},{4,0,425},{5,11,387},{7,11,557},{12,11,547},{142,11,86},{135,11, -1747},{5,10,654},{135,11,1489},{7,0,789},{4,11,6},{5,11,708},{136,11,75},{6,10, -273},{10,10,188},{13,10,377},{146,10,77},{6,0,1593},{4,11,303},{7,11,619},{10,11 -,547},{10,11,687},{11,11,122},{140,11,601},{134,0,1768},{135,10,410},{138,11,772 -},{11,0,233},{139,10,524},{5,0,943},{134,0,1779},{134,10,1785},{136,11,529},{132 -,0,955},{5,0,245},{6,0,576},{7,0,582},{136,0,225},{132,10,780},{142,0,241},{134, -0,1943},{4,11,106},{7,11,310},{7,11,1785},{10,11,690},{139,11,717},{134,0,1284}, -{5,11,890},{133,11,988},{6,11,626},{142,11,431},{10,11,706},{145,11,32},{137,11, -332},{132,11,698},{135,0,709},{5,10,948},{138,11,17},{136,0,554},{134,0,1564},{ -139,10,941},{132,0,443},{134,0,909},{134,11,84},{142,0,280},{4,10,532},{5,10,706 -},{135,10,662},{132,0,729},{5,10,837},{6,10,1651},{139,10,985},{135,10,1861},{4, -0,348},{152,11,3},{5,11,986},{6,11,130},{7,11,1582},{8,11,458},{10,11,101},{10, -11,318},{138,11,823},{134,0,758},{4,0,298},{137,0,848},{4,10,330},{7,10,933},{7, -10,2012},{136,10,292},{7,11,1644},{137,11,129},{6,0,1422},{9,0,829},{135,10,767} -,{5,0,164},{7,0,121},{142,0,189},{7,0,812},{7,0,1261},{7,0,1360},{9,0,632},{140, -0,352},{135,11,1788},{139,0,556},{135,11,997},{145,10,114},{4,0,172},{9,0,611},{ -10,0,436},{12,0,673},{13,0,255},{137,10,883},{11,0,530},{138,10,274},{133,0,844} -,{134,0,984},{13,0,232},{18,0,35},{4,10,703},{135,10,207},{132,10,571},{9,0,263} -,{10,0,147},{138,0,492},{7,11,1756},{137,11,98},{5,10,873},{5,10,960},{8,10,823} -,{137,10,881},{133,0,537},{132,0,859},{7,11,1046},{139,11,160},{137,0,842},{139, -10,283},{5,10,33},{6,10,470},{139,10,424},{6,11,45},{7,11,433},{8,11,129},{9,11, -21},{10,11,392},{11,11,79},{12,11,499},{13,11,199},{141,11,451},{135,0,1291},{ -135,10,1882},{7,11,558},{136,11,353},{134,0,1482},{5,0,230},{5,0,392},{6,0,420}, -{9,0,568},{140,0,612},{6,0,262},{7,10,90},{7,10,664},{7,10,830},{7,10,1380},{7, -10,2025},{8,11,81},{8,10,448},{8,10,828},{9,11,189},{9,11,201},{11,11,478},{11, -11,712},{141,11,338},{142,0,31},{5,11,353},{151,11,26},{132,0,753},{4,0,0},{5,0, -41},{7,0,1459},{7,0,1469},{7,0,1859},{9,0,549},{139,0,905},{9,10,417},{137,10, -493},{135,11,1113},{133,0,696},{141,11,448},{134,10,295},{132,0,834},{4,0,771},{ -5,10,1019},{6,11,25},{7,11,855},{7,11,1258},{144,11,32},{134,0,1076},{133,0,921} -,{133,0,674},{4,11,4},{7,11,1118},{7,11,1320},{7,11,1706},{8,11,277},{9,11,622}, -{10,11,9},{11,11,724},{12,11,350},{12,11,397},{13,11,28},{13,11,159},{15,11,89}, -{18,11,5},{19,11,9},{20,11,34},{150,11,47},{134,10,208},{6,0,444},{136,0,308},{6 -,0,180},{7,0,1137},{8,0,751},{139,0,805},{4,0,183},{7,0,271},{11,0,824},{11,0, -952},{13,0,278},{13,0,339},{13,0,482},{14,0,424},{148,0,99},{7,11,317},{135,11, -569},{4,0,19},{5,0,477},{5,0,596},{6,0,505},{7,0,1221},{11,0,907},{12,0,209},{ -141,0,214},{135,0,1215},{6,0,271},{7,0,398},{8,0,387},{10,0,344},{7,10,448},{7, -10,1629},{7,10,1813},{8,10,442},{9,10,710},{10,10,282},{138,10,722},{11,10,844}, -{12,10,104},{140,10,625},{134,11,255},{133,10,787},{134,0,1645},{11,11,956},{151 -,11,3},{6,0,92},{6,0,188},{7,0,209},{7,0,1269},{7,0,1524},{7,0,1876},{8,0,661},{ -10,0,42},{10,0,228},{11,0,58},{11,0,1020},{12,0,58},{12,0,118},{141,0,32},{4,0, -459},{133,0,966},{4,11,536},{7,11,1141},{10,11,723},{139,11,371},{140,0,330},{ -134,0,1557},{7,11,285},{135,11,876},{136,10,491},{135,11,560},{6,0,18},{7,0,179} -,{7,0,932},{8,0,548},{8,0,757},{9,0,54},{9,0,65},{9,0,532},{9,0,844},{10,0,113}, -{10,0,117},{10,0,315},{10,0,560},{10,0,622},{10,0,798},{11,0,153},{11,0,351},{11 -,0,375},{12,0,78},{12,0,151},{12,0,392},{12,0,666},{14,0,248},{143,0,23},{6,0, -1742},{132,11,690},{4,10,403},{5,10,441},{7,10,450},{10,10,840},{11,10,101},{12, -10,193},{141,10,430},{133,0,965},{134,0,182},{10,0,65},{10,0,488},{138,0,497},{ -135,11,1346},{6,0,973},{6,0,1158},{10,11,200},{19,11,2},{151,11,22},{4,11,190},{ -133,11,554},{133,10,679},{7,0,328},{137,10,326},{133,11,1001},{9,0,588},{138,0, -260},{133,11,446},{135,10,1128},{135,10,1796},{147,11,119},{134,0,1786},{6,0, -1328},{6,0,1985},{8,0,962},{138,0,1017},{135,0,308},{11,0,508},{4,10,574},{7,10, -350},{7,10,1024},{8,10,338},{9,10,677},{138,10,808},{138,11,752},{135,10,1081},{ -137,11,96},{7,10,1676},{135,10,2037},{136,0,588},{132,11,304},{133,0,614},{140,0 -,793},{136,0,287},{137,10,297},{141,10,37},{6,11,53},{6,11,199},{7,11,1408},{8, -11,32},{8,11,93},{9,11,437},{10,11,397},{10,11,629},{11,11,593},{11,11,763},{13, -11,326},{145,11,35},{134,11,105},{9,11,320},{10,11,506},{138,11,794},{5,11,114}, -{5,11,255},{141,11,285},{140,0,290},{7,11,2035},{8,11,19},{9,11,89},{138,11,831} -,{134,0,1136},{7,0,719},{8,0,796},{8,0,809},{8,0,834},{6,10,306},{7,10,1140},{7, -10,1340},{8,10,133},{138,10,449},{139,10,1011},{5,0,210},{6,0,213},{7,0,60},{10, -0,364},{139,0,135},{5,0,607},{8,0,326},{136,0,490},{138,11,176},{132,0,701},{5,0 -,472},{7,0,380},{137,0,758},{135,0,1947},{6,0,1079},{138,0,278},{138,11,391},{5, -10,329},{8,10,260},{139,11,156},{4,0,386},{7,0,41},{8,0,405},{8,0,728},{9,0,497} -,{11,0,110},{11,0,360},{15,0,37},{144,0,84},{5,0,46},{7,0,1452},{7,0,1480},{8,0, -634},{140,0,472},{136,0,961},{4,0,524},{136,0,810},{10,0,238},{141,0,33},{132,10 -,657},{152,10,7},{133,0,532},{5,0,997},{135,10,1665},{7,11,594},{7,11,851},{7,11 -,1858},{9,11,411},{9,11,574},{9,11,666},{9,11,737},{10,11,346},{10,11,712},{11, -11,246},{11,11,432},{11,11,517},{11,11,647},{11,11,679},{11,11,727},{12,11,304}, -{12,11,305},{12,11,323},{12,11,483},{12,11,572},{12,11,593},{12,11,602},{13,11, -95},{13,11,101},{13,11,171},{13,11,315},{13,11,378},{13,11,425},{13,11,475},{14, -11,63},{14,11,380},{14,11,384},{15,11,133},{18,11,112},{148,11,72},{5,11,955},{ -136,11,814},{134,0,1301},{5,10,66},{7,10,1896},{136,10,288},{133,11,56},{134,10, -1643},{6,0,1298},{148,11,100},{5,0,782},{5,0,829},{6,0,671},{6,0,1156},{6,0,1738 -},{137,11,621},{4,0,306},{5,0,570},{7,0,1347},{5,10,91},{5,10,648},{5,10,750},{5 -,10,781},{6,10,54},{6,10,112},{6,10,402},{6,10,1732},{7,10,315},{7,10,749},{7,10 -,1900},{9,10,78},{9,10,508},{10,10,611},{10,10,811},{11,10,510},{11,10,728},{13, -10,36},{14,10,39},{16,10,83},{17,10,124},{148,10,30},{8,10,570},{9,11,477},{141, -11,78},{4,11,639},{10,11,4},{10,10,322},{10,10,719},{11,10,407},{11,11,638},{12, -11,177},{148,11,57},{7,0,1823},{139,0,693},{7,0,759},{5,11,758},{8,10,125},{8,10 -,369},{8,10,524},{10,10,486},{11,10,13},{11,10,381},{11,10,736},{11,10,766},{11, -10,845},{13,10,114},{13,10,292},{142,10,47},{7,0,1932},{6,10,1684},{6,10,1731},{ -7,10,356},{8,10,54},{8,10,221},{9,10,225},{9,10,356},{10,10,77},{10,10,446},{10, -10,731},{12,10,404},{141,10,491},{135,11,552},{135,11,1112},{4,0,78},{5,0,96},{5 -,0,182},{6,0,1257},{7,0,1724},{7,0,1825},{10,0,394},{10,0,471},{11,0,532},{14,0, -340},{145,0,88},{139,11,328},{135,0,1964},{132,10,411},{4,10,80},{5,10,44},{137, -11,133},{5,11,110},{6,11,169},{6,11,1702},{7,11,400},{8,11,538},{9,11,184},{9,11 -,524},{140,11,218},{4,0,521},{5,10,299},{7,10,1083},{140,11,554},{6,11,133},{9, -11,353},{12,11,628},{146,11,79},{6,0,215},{7,0,584},{7,0,1028},{7,0,1473},{7,0, -1721},{9,0,424},{138,0,779},{7,0,857},{7,0,1209},{7,10,1713},{9,10,537},{10,10, -165},{12,10,219},{140,10,561},{4,10,219},{6,11,93},{7,11,1422},{7,10,1761},{7,11 -,1851},{8,11,673},{9,10,86},{9,11,529},{140,11,43},{137,11,371},{136,0,671},{5,0 -,328},{135,0,918},{132,0,529},{9,11,25},{10,11,467},{138,11,559},{4,11,335},{135 -,11,942},{134,0,716},{134,0,1509},{6,0,67},{7,0,258},{7,0,1630},{9,0,354},{9,0, -675},{10,0,830},{14,0,80},{17,0,80},{140,10,428},{134,0,1112},{6,0,141},{7,0,225 -},{9,0,59},{9,0,607},{10,0,312},{11,0,687},{12,0,555},{13,0,373},{13,0,494},{148 -,0,58},{133,10,514},{8,11,39},{10,11,773},{11,11,84},{12,11,205},{142,11,1},{8,0 -,783},{5,11,601},{133,11,870},{136,11,594},{4,10,55},{5,10,301},{6,10,571},{14, -10,49},{146,10,102},{132,11,181},{134,11,1652},{133,10,364},{4,11,97},{5,11,147} -,{6,11,286},{7,11,1362},{141,11,176},{4,10,76},{7,10,1550},{9,10,306},{9,10,430} -,{9,10,663},{10,10,683},{11,10,427},{11,10,753},{12,10,334},{12,10,442},{14,10, -258},{14,10,366},{143,10,131},{137,10,52},{6,0,955},{134,0,1498},{6,11,375},{7, -11,169},{7,11,254},{136,11,780},{7,0,430},{11,0,46},{14,0,343},{142,11,343},{135 -,0,1183},{5,0,602},{7,0,2018},{9,0,418},{9,0,803},{135,11,1447},{8,0,677},{135, -11,1044},{139,11,285},{4,10,656},{135,10,779},{135,10,144},{5,11,629},{135,11, -1549},{135,10,1373},{138,11,209},{7,10,554},{7,10,605},{141,10,10},{5,10,838},{5 -,10,841},{134,10,1649},{133,10,1012},{6,0,1357},{134,0,1380},{144,0,53},{6,0,590 -},{7,10,365},{7,10,1357},{7,10,1497},{8,10,154},{141,10,281},{133,10,340},{132, -11,420},{135,0,329},{147,11,32},{4,0,469},{10,11,429},{139,10,495},{8,10,261},{9 -,10,144},{9,10,466},{10,10,370},{12,10,470},{13,10,144},{142,10,348},{142,0,460} -,{4,11,325},{9,10,897},{138,11,125},{6,0,1743},{6,10,248},{9,10,546},{10,10,535} -,{11,10,681},{141,10,135},{4,0,990},{5,0,929},{6,0,340},{8,0,376},{8,0,807},{8,0 -,963},{8,0,980},{138,0,1007},{134,0,1603},{140,0,250},{4,11,714},{133,11,469},{ -134,10,567},{136,10,445},{5,0,218},{7,0,1610},{8,0,646},{10,0,83},{11,11,138},{ -140,11,40},{7,0,1512},{135,0,1794},{135,11,1216},{11,0,0},{16,0,78},{132,11,718} -,{133,0,571},{132,0,455},{134,0,1012},{5,11,124},{5,11,144},{6,11,548},{7,11,15} -,{7,11,153},{137,11,629},{142,11,10},{6,11,75},{7,11,1531},{8,11,416},{9,11,240} -,{9,11,275},{10,11,100},{11,11,658},{11,11,979},{12,11,86},{13,11,468},{14,11,66 -},{14,11,207},{15,11,20},{15,11,25},{144,11,58},{132,10,577},{5,11,141},{5,11, -915},{6,11,1783},{7,11,211},{7,11,698},{7,11,1353},{9,11,83},{9,11,281},{10,11, -376},{10,11,431},{11,11,543},{12,11,664},{13,11,280},{13,11,428},{14,11,61},{14, -11,128},{17,11,52},{145,11,81},{6,0,161},{7,0,372},{137,0,597},{132,0,349},{10, -11,702},{139,11,245},{134,0,524},{134,10,174},{6,0,432},{9,0,751},{139,0,322},{ -147,11,94},{4,11,338},{133,11,400},{5,0,468},{10,0,325},{11,0,856},{12,0,345},{ -143,0,104},{133,0,223},{132,0,566},{4,11,221},{5,11,659},{5,11,989},{7,11,697},{ -7,11,1211},{138,11,284},{135,11,1070},{4,0,59},{135,0,1394},{6,0,436},{11,0,481} -,{5,10,878},{133,10,972},{4,0,48},{5,0,271},{135,0,953},{5,0,610},{136,0,457},{4 -,0,773},{5,0,618},{137,0,756},{133,0,755},{135,0,1217},{138,11,507},{132,10,351} -,{132,0,197},{143,11,78},{4,11,188},{7,11,805},{11,11,276},{142,11,293},{5,11, -884},{139,11,991},{132,10,286},{10,0,259},{10,0,428},{7,10,438},{7,10,627},{7,10 -,1516},{8,10,40},{9,10,56},{9,10,294},{11,10,969},{11,10,995},{146,10,148},{4,0, -356},{5,0,217},{5,0,492},{5,0,656},{8,0,544},{136,11,544},{5,0,259},{6,0,1230},{ -7,0,414},{7,0,854},{142,0,107},{132,0,1007},{15,0,14},{144,0,5},{6,0,1580},{132, -10,738},{132,11,596},{132,0,673},{133,10,866},{6,0,1843},{135,11,1847},{4,0,165} -,{7,0,1398},{135,0,1829},{135,11,1634},{147,11,65},{6,0,885},{6,0,1009},{137,0, -809},{133,10,116},{132,10,457},{136,11,770},{9,0,498},{12,0,181},{10,11,361},{ -142,11,316},{134,11,595},{5,0,9},{7,0,297},{7,0,966},{140,0,306},{4,11,89},{5,11 -,489},{6,11,315},{7,11,553},{7,11,1745},{138,11,243},{134,0,1487},{132,0,437},{5 -,0,146},{6,0,411},{138,0,721},{5,10,527},{6,10,189},{135,10,859},{11,10,104},{11 -,10,554},{15,10,60},{143,10,125},{6,11,1658},{9,11,3},{10,11,154},{11,11,641},{ -13,11,85},{13,11,201},{141,11,346},{6,0,177},{135,0,467},{134,0,1377},{134,10, -116},{136,11,645},{4,11,166},{5,11,505},{6,11,1670},{137,11,110},{133,10,487},{4 -,10,86},{5,10,667},{5,10,753},{6,10,316},{6,10,455},{135,10,946},{133,0,200},{ -132,0,959},{6,0,1928},{134,0,1957},{139,11,203},{150,10,45},{4,10,79},{7,10,1773 -},{10,10,450},{11,10,589},{13,10,332},{13,10,493},{14,10,183},{14,10,334},{14,10 -,362},{14,10,368},{14,10,376},{14,10,379},{19,10,90},{19,10,103},{19,10,127},{ -148,10,90},{6,0,1435},{135,11,1275},{134,0,481},{7,11,445},{8,11,307},{8,11,704} -,{10,11,41},{10,11,439},{11,11,237},{11,11,622},{140,11,201},{135,11,869},{4,0, -84},{7,0,1482},{10,0,76},{138,0,142},{11,11,277},{144,11,14},{135,11,1977},{4,11 -,189},{5,11,713},{136,11,57},{133,0,1015},{138,11,371},{4,0,315},{5,0,507},{135, -0,1370},{4,11,552},{142,10,381},{9,0,759},{16,0,31},{16,0,39},{16,0,75},{18,0,24 -},{20,0,42},{152,0,1},{134,0,712},{134,0,1722},{133,10,663},{133,10,846},{8,0, -222},{8,0,476},{9,0,238},{11,0,516},{11,0,575},{15,0,109},{146,0,100},{7,0,1402} -,{7,0,1414},{12,0,456},{5,10,378},{8,10,465},{9,10,286},{10,10,185},{10,10,562}, -{10,10,635},{11,10,31},{11,10,393},{13,10,312},{18,10,65},{18,10,96},{147,10,89} -,{4,0,986},{6,0,1958},{6,0,2032},{8,0,934},{138,0,985},{7,10,1880},{9,10,680},{ -139,10,798},{134,10,1770},{145,11,49},{132,11,614},{132,10,648},{5,10,945},{6,10 -,1656},{6,10,1787},{7,10,167},{8,10,824},{9,10,391},{10,10,375},{139,10,185},{ -138,11,661},{7,0,1273},{135,11,1945},{7,0,706},{7,0,1058},{138,0,538},{7,10,1645 -},{8,10,352},{137,10,249},{132,10,152},{11,0,92},{11,0,196},{11,0,409},{11,0,450 -},{11,0,666},{11,0,777},{12,0,262},{13,0,385},{13,0,393},{15,0,115},{16,0,45},{ -145,0,82},{133,10,1006},{6,0,40},{135,0,1781},{9,11,614},{139,11,327},{5,10,420} -,{135,10,1449},{135,0,431},{10,0,97},{135,10,832},{6,0,423},{7,0,665},{135,0, -1210},{7,0,237},{8,0,664},{9,0,42},{9,0,266},{9,0,380},{9,0,645},{10,0,177},{138 -,0,276},{7,0,264},{133,10,351},{8,0,213},{5,10,40},{7,10,598},{7,10,1638},{9,10, -166},{9,10,640},{9,10,685},{9,10,773},{11,10,215},{13,10,65},{14,10,172},{14,10, -317},{145,10,6},{5,11,84},{134,11,163},{8,10,60},{9,10,343},{139,10,769},{137,0, -455},{133,11,410},{8,0,906},{12,0,700},{12,0,706},{140,0,729},{21,11,33},{150,11 -,40},{7,10,1951},{8,10,765},{8,10,772},{140,10,671},{7,10,108},{8,10,219},{8,10, -388},{9,10,639},{9,10,775},{11,10,275},{140,10,464},{5,11,322},{7,11,1941},{8,11 -,186},{9,11,262},{10,11,187},{14,11,208},{146,11,130},{139,0,624},{8,0,574},{5, -11,227},{140,11,29},{7,11,1546},{11,11,299},{142,11,407},{5,10,15},{6,10,56},{7, -10,1758},{8,10,500},{9,10,730},{11,10,331},{13,10,150},{142,10,282},{7,11,1395}, -{8,11,486},{9,11,236},{9,11,878},{10,11,218},{11,11,95},{19,11,17},{147,11,31},{ -135,11,2043},{4,0,354},{146,11,4},{140,11,80},{135,0,1558},{134,10,1886},{5,10, -205},{6,10,438},{137,10,711},{133,11,522},{133,10,534},{7,0,235},{7,0,1475},{15, -0,68},{146,0,120},{137,10,691},{4,0,942},{6,0,1813},{8,0,917},{10,0,884},{12,0, -696},{12,0,717},{12,0,723},{12,0,738},{12,0,749},{12,0,780},{16,0,97},{146,0,169 -},{6,10,443},{8,11,562},{9,10,237},{9,10,571},{9,10,695},{10,10,139},{11,10,715} -,{12,10,417},{141,10,421},{135,0,957},{133,0,830},{134,11,1771},{146,0,23},{5,0, -496},{6,0,694},{7,0,203},{7,11,1190},{137,11,620},{137,11,132},{6,0,547},{134,0, -1549},{8,11,258},{9,11,208},{137,11,359},{4,0,864},{5,0,88},{137,0,239},{135,11, -493},{4,11,317},{135,11,1279},{132,11,477},{4,10,578},{5,11,63},{133,11,509},{7, -0,650},{135,0,1310},{7,0,1076},{9,0,80},{11,0,78},{11,0,421},{11,0,534},{140,0, -545},{132,11,288},{12,0,553},{14,0,118},{133,10,923},{7,0,274},{11,0,479},{139,0 -,507},{8,11,89},{8,11,620},{9,11,49},{10,11,774},{11,11,628},{12,11,322},{143,11 -,124},{4,0,497},{135,0,1584},{7,0,261},{7,0,1115},{7,0,1354},{7,0,1404},{7,0, -1588},{7,0,1705},{7,0,1902},{9,0,465},{10,0,248},{10,0,349},{10,0,647},{11,0,527 -},{11,0,660},{11,0,669},{12,0,529},{13,0,305},{132,10,924},{133,10,665},{136,0, -13},{6,0,791},{138,11,120},{7,0,642},{8,0,250},{11,0,123},{11,0,137},{13,0,48},{ -142,0,95},{4,10,265},{7,10,807},{135,10,950},{5,10,93},{140,10,267},{135,0,1429} -,{4,0,949},{10,0,885},{10,0,891},{10,0,900},{10,0,939},{12,0,760},{142,0,449},{ -139,11,366},{132,0,818},{134,11,85},{135,10,994},{7,0,330},{5,10,233},{5,10,320} -,{6,10,140},{136,10,295},{4,0,1004},{8,0,982},{136,0,993},{133,10,978},{4,10,905 -},{6,10,1701},{137,10,843},{10,0,545},{140,0,301},{6,0,947},{134,0,1062},{134,0, -1188},{4,0,904},{5,0,794},{152,10,6},{134,0,1372},{135,11,608},{5,11,279},{6,11, -235},{7,11,468},{8,11,446},{9,11,637},{10,11,717},{11,11,738},{140,11,514},{132, -10,509},{5,11,17},{6,11,371},{137,11,528},{132,0,693},{4,11,115},{5,11,669},{6, -11,407},{8,11,311},{11,11,10},{141,11,5},{11,0,377},{7,10,273},{137,11,381},{135 -,0,695},{7,0,386},{138,0,713},{135,10,1041},{134,0,1291},{6,0,7},{6,0,35},{7,0, -147},{7,0,1069},{7,0,1568},{7,0,1575},{7,0,1917},{8,0,43},{8,0,208},{9,0,128},{9 -,0,866},{10,0,20},{11,0,981},{147,0,33},{7,0,893},{141,0,424},{139,10,234},{150, -11,56},{5,11,779},{5,11,807},{6,11,1655},{134,11,1676},{5,10,802},{7,10,2021},{ -136,10,805},{4,11,196},{5,10,167},{5,11,558},{5,10,899},{5,11,949},{6,10,410},{ -137,10,777},{137,10,789},{134,10,1705},{8,0,904},{140,0,787},{6,0,322},{9,0,552} -,{11,0,274},{13,0,209},{13,0,499},{14,0,85},{15,0,126},{145,0,70},{135,10,10},{5 -,10,11},{6,10,117},{6,10,485},{7,10,1133},{9,10,582},{9,10,594},{11,10,21},{11, -10,818},{12,10,535},{141,10,86},{4,10,264},{7,10,1067},{8,10,204},{8,10,385},{ -139,10,953},{132,11,752},{138,10,56},{133,10,470},{6,0,1808},{8,0,83},{8,0,742}, -{8,0,817},{9,0,28},{9,0,29},{9,0,885},{10,0,387},{11,0,633},{11,0,740},{13,0,235 -},{13,0,254},{15,0,143},{143,0,146},{140,0,49},{134,0,1832},{4,11,227},{5,11,159 -},{5,11,409},{7,11,80},{10,11,294},{10,11,479},{12,11,418},{14,11,50},{14,11,249 -},{142,11,295},{7,11,1470},{8,11,66},{8,11,137},{8,11,761},{9,11,638},{11,11,80} -,{11,11,212},{11,11,368},{11,11,418},{12,11,8},{13,11,15},{16,11,61},{17,11,59}, -{19,11,28},{148,11,84},{139,10,1015},{138,11,468},{135,0,421},{6,0,415},{7,0, -1049},{137,0,442},{6,11,38},{7,11,1220},{8,11,185},{8,11,256},{9,11,22},{9,11, -331},{10,11,738},{11,11,205},{11,11,540},{11,11,746},{13,11,399},{13,11,465},{14 -,11,88},{142,11,194},{139,0,289},{133,10,715},{4,0,110},{10,0,415},{10,0,597},{ -142,0,206},{4,11,159},{6,11,115},{7,11,252},{7,11,257},{7,11,1928},{8,11,69},{9, -11,384},{10,11,91},{10,11,615},{12,11,375},{14,11,235},{18,11,117},{147,11,123}, -{5,11,911},{136,11,278},{7,0,205},{7,0,2000},{8,10,794},{9,10,400},{10,10,298},{ -142,10,228},{135,11,1774},{4,11,151},{7,11,1567},{8,11,351},{137,11,322},{136,10 -,724},{133,11,990},{7,0,1539},{11,0,512},{13,0,205},{19,0,30},{22,0,36},{23,0,19 -},{135,11,1539},{5,11,194},{7,11,1662},{9,11,90},{140,11,180},{6,10,190},{7,10, -768},{135,10,1170},{134,0,1340},{4,0,283},{135,0,1194},{133,11,425},{133,11,971} -,{12,0,549},{14,10,67},{147,10,60},{135,10,1023},{134,0,1720},{138,11,587},{5,11 -,72},{6,11,264},{7,11,21},{7,11,46},{7,11,2013},{8,11,215},{8,11,513},{10,11,266 -},{139,11,22},{5,0,319},{135,0,534},{6,10,137},{9,10,75},{9,10,253},{10,10,194}, -{138,10,444},{7,0,1180},{20,0,112},{6,11,239},{7,11,118},{10,11,95},{11,11,603}, -{13,11,443},{14,11,160},{143,11,4},{134,11,431},{5,11,874},{6,11,1677},{11,10, -643},{12,10,115},{143,11,0},{134,0,967},{6,11,65},{7,11,939},{7,11,1172},{7,11, -1671},{9,11,540},{10,11,696},{11,11,265},{11,11,732},{11,11,928},{11,11,937},{12 -,11,399},{13,11,438},{149,11,19},{137,11,200},{135,0,1940},{5,10,760},{7,10,542} -,{8,10,135},{136,10,496},{140,11,44},{7,11,1655},{136,11,305},{7,10,319},{7,10, -355},{7,10,763},{10,10,389},{145,10,43},{136,0,735},{138,10,786},{137,11,19},{ -132,11,696},{5,0,132},{9,0,486},{9,0,715},{10,0,458},{11,0,373},{11,0,668},{11,0 -,795},{11,0,897},{12,0,272},{12,0,424},{12,0,539},{12,0,558},{14,0,245},{14,0, -263},{14,0,264},{14,0,393},{142,0,403},{10,0,38},{139,0,784},{132,0,838},{4,11, -302},{135,11,1766},{133,0,379},{5,0,8},{6,0,89},{6,0,400},{7,0,1569},{7,0,1623}, -{7,0,1850},{8,0,218},{8,0,422},{9,0,570},{10,0,626},{4,11,726},{133,11,630},{4,0 -,1017},{138,0,660},{6,0,387},{7,0,882},{141,0,111},{6,0,224},{7,0,877},{137,0, -647},{4,10,58},{5,10,286},{6,10,319},{7,10,402},{7,10,1254},{7,10,1903},{8,10, -356},{140,10,408},{135,0,790},{9,0,510},{10,0,53},{4,10,389},{9,10,181},{10,10, -29},{10,10,816},{11,10,311},{11,10,561},{12,10,67},{141,10,181},{142,0,458},{6, -11,118},{7,11,215},{7,11,1521},{140,11,11},{134,0,954},{135,0,394},{134,0,1367}, -{5,11,225},{133,10,373},{132,0,882},{7,0,1409},{135,10,1972},{135,10,1793},{4,11 -,370},{5,11,756},{135,11,1326},{150,11,13},{7,11,354},{10,11,410},{139,11,815},{ -6,11,1662},{7,11,48},{8,11,771},{10,11,116},{13,11,104},{14,11,105},{14,11,184}, -{15,11,168},{19,11,92},{148,11,68},{7,0,124},{136,0,38},{5,0,261},{7,0,78},{7,0, -199},{8,0,815},{9,0,126},{10,0,342},{140,0,647},{4,0,628},{140,0,724},{7,0,266}, -{8,0,804},{7,10,1651},{145,10,89},{135,0,208},{134,0,1178},{6,0,79},{135,0,1519} -,{132,10,672},{133,10,737},{136,0,741},{132,11,120},{4,0,710},{6,0,376},{134,0, -606},{134,0,1347},{134,0,1494},{6,0,850},{6,0,1553},{137,0,821},{5,10,145},{134, -11,593},{7,0,1311},{140,0,135},{4,0,467},{5,0,405},{134,0,544},{5,11,820},{135, -11,931},{6,0,100},{7,0,244},{7,0,632},{7,0,1609},{8,0,178},{8,0,638},{141,0,58}, -{4,10,387},{135,10,1288},{6,11,151},{6,11,1675},{7,11,383},{151,11,10},{132,0, -481},{135,10,550},{134,0,1378},{6,11,1624},{11,11,11},{12,11,422},{13,11,262},{ -142,11,360},{133,0,791},{4,11,43},{5,11,344},{133,11,357},{7,0,1227},{140,0,978} -,{7,0,686},{8,0,33},{8,0,238},{10,0,616},{11,0,467},{11,0,881},{13,0,217},{13,0, -253},{142,0,268},{137,0,857},{8,0,467},{8,0,1006},{7,11,148},{8,11,284},{141,11, -63},{4,10,576},{135,10,1263},{133,11,888},{5,10,919},{134,10,1673},{20,10,37},{ -148,11,37},{132,0,447},{132,11,711},{4,0,128},{5,0,415},{6,0,462},{7,0,294},{7,0 -,578},{10,0,710},{139,0,86},{4,10,82},{5,10,333},{5,10,904},{6,10,207},{7,10,325 -},{7,10,1726},{8,10,101},{10,10,778},{139,10,220},{136,0,587},{137,11,440},{133, -10,903},{6,0,427},{7,0,1018},{138,0,692},{4,0,195},{135,0,802},{140,10,147},{134 -,0,1546},{134,0,684},{132,10,705},{136,0,345},{11,11,678},{140,11,307},{133,0, -365},{134,0,1683},{4,11,65},{5,11,479},{5,11,1004},{7,11,1913},{8,11,317},{9,11, -302},{10,11,612},{141,11,22},{138,0,472},{4,11,261},{135,11,510},{134,10,90},{ -142,0,433},{151,0,28},{4,11,291},{7,11,101},{9,11,515},{12,11,152},{12,11,443},{ -13,11,392},{142,11,357},{140,0,997},{5,0,3},{8,0,578},{9,0,118},{10,0,705},{141, -0,279},{135,11,1266},{7,10,813},{12,10,497},{141,10,56},{133,0,229},{6,10,125},{ -135,10,1277},{8,0,102},{10,0,578},{10,0,672},{12,0,496},{13,0,408},{14,0,121},{ -17,0,106},{151,10,12},{6,0,866},{134,0,1080},{136,0,1022},{4,11,130},{135,11,843 -},{5,11,42},{5,11,879},{7,11,245},{7,11,324},{7,11,1532},{11,11,463},{11,11,472} -,{13,11,363},{144,11,52},{150,0,55},{8,0,115},{8,0,350},{9,0,489},{10,0,128},{11 -,0,306},{12,0,373},{14,0,30},{17,0,79},{19,0,80},{4,11,134},{133,11,372},{134,0, -657},{134,0,933},{135,11,1147},{4,0,230},{133,0,702},{134,0,1728},{4,0,484},{18, -0,26},{19,0,42},{20,0,43},{21,0,0},{23,0,27},{152,0,14},{7,0,185},{135,0,703},{6 -,0,417},{10,0,618},{7,10,1106},{9,10,770},{11,10,112},{140,10,413},{134,0,803},{ -132,11,644},{134,0,1262},{7,11,540},{12,10,271},{145,10,109},{135,11,123},{132,0 -,633},{134,11,623},{4,11,908},{5,11,359},{5,11,508},{6,11,1723},{7,11,343},{7,11 -,1996},{135,11,2026},{135,0,479},{10,0,262},{7,10,304},{9,10,646},{9,10,862},{11 -,10,696},{12,10,208},{15,10,79},{147,10,108},{4,11,341},{135,11,480},{134,0,830} -,{5,0,70},{5,0,622},{6,0,334},{7,0,1032},{9,0,171},{11,0,26},{11,0,213},{11,0, -637},{11,0,707},{12,0,202},{12,0,380},{13,0,226},{13,0,355},{14,0,222},{145,0,42 -},{135,10,981},{143,0,217},{137,11,114},{4,0,23},{4,0,141},{5,0,313},{5,0,1014}, -{6,0,50},{6,0,51},{7,0,142},{7,0,384},{7,0,559},{8,0,640},{9,0,460},{9,0,783},{ -11,0,741},{12,0,183},{141,0,488},{141,0,360},{7,0,1586},{7,11,1995},{8,11,299},{ -11,11,890},{140,11,674},{132,10,434},{7,0,652},{134,10,550},{7,0,766},{5,10,553} -,{138,10,824},{7,0,737},{8,0,298},{136,10,452},{4,11,238},{5,11,503},{6,11,179}, -{7,11,2003},{8,11,381},{8,11,473},{9,11,149},{10,11,183},{15,11,45},{143,11,86}, -{133,10,292},{5,0,222},{9,0,655},{138,0,534},{138,10,135},{4,11,121},{5,11,156}, -{5,11,349},{9,11,136},{10,11,605},{14,11,342},{147,11,107},{137,0,906},{6,0,1013 -},{134,0,1250},{6,0,1956},{6,0,2009},{8,0,991},{144,0,120},{135,11,1192},{138,0, -503},{5,0,154},{7,0,1491},{10,0,379},{138,0,485},{6,0,1867},{6,0,1914},{6,0,1925 -},{9,0,917},{9,0,925},{9,0,932},{9,0,951},{9,0,1007},{9,0,1013},{12,0,806},{12,0 -,810},{12,0,814},{12,0,816},{12,0,824},{12,0,832},{12,0,837},{12,0,863},{12,0, -868},{12,0,870},{12,0,889},{12,0,892},{12,0,900},{12,0,902},{12,0,908},{12,0,933 -},{12,0,942},{12,0,949},{12,0,954},{15,0,175},{15,0,203},{15,0,213},{15,0,218},{ -15,0,225},{15,0,231},{15,0,239},{15,0,248},{15,0,252},{18,0,190},{18,0,204},{18, -0,215},{18,0,216},{18,0,222},{18,0,225},{18,0,230},{18,0,239},{18,0,241},{21,0, -42},{21,0,43},{21,0,44},{21,0,45},{21,0,46},{21,0,53},{24,0,27},{152,0,31},{133, -0,716},{135,0,844},{4,0,91},{5,0,388},{5,0,845},{6,0,206},{6,0,252},{6,0,365},{7 -,0,136},{7,0,531},{136,0,621},{7,10,393},{10,10,603},{139,10,206},{6,11,80},{6, -11,1694},{7,11,173},{7,11,1974},{9,11,547},{10,11,730},{14,11,18},{150,11,39},{ -137,0,748},{4,11,923},{134,11,1711},{4,10,912},{137,10,232},{7,10,98},{7,10,1973 -},{136,10,716},{14,0,103},{133,10,733},{132,11,595},{12,0,158},{18,0,8},{19,0,62 -},{20,0,6},{22,0,4},{23,0,2},{23,0,9},{5,11,240},{6,11,459},{7,11,12},{7,11,114} -,{7,11,502},{7,11,1751},{7,11,1753},{7,11,1805},{8,11,658},{9,11,1},{11,11,959}, -{13,11,446},{142,11,211},{135,0,576},{5,0,771},{5,0,863},{5,0,898},{6,0,648},{6, -0,1632},{6,0,1644},{134,0,1780},{133,0,331},{7,11,633},{7,11,905},{7,11,909},{7, -11,1538},{9,11,767},{140,11,636},{140,0,632},{5,0,107},{7,0,201},{136,0,518},{6, -0,446},{7,0,1817},{134,11,490},{9,0,851},{141,0,510},{7,11,250},{8,11,506},{136, -11,507},{4,0,504},{137,10,72},{132,11,158},{4,11,140},{7,11,362},{8,11,209},{9, -11,10},{9,11,160},{9,11,503},{10,11,689},{11,11,350},{11,11,553},{11,11,725},{12 -,11,252},{12,11,583},{13,11,192},{13,11,352},{14,11,269},{14,11,356},{148,11,50} -,{6,11,597},{135,11,1318},{135,10,1454},{5,0,883},{5,0,975},{8,0,392},{148,0,7}, -{6,11,228},{7,11,1341},{9,11,408},{138,11,343},{11,11,348},{11,10,600},{12,11,99 -},{13,10,245},{18,11,1},{18,11,11},{147,11,4},{134,11,296},{5,0,922},{134,0,1707 -},{132,11,557},{4,11,548},{7,10,164},{7,10,1571},{9,10,107},{140,10,225},{7,11, -197},{8,11,142},{8,11,325},{9,11,150},{9,11,596},{10,11,350},{10,11,353},{11,11, -74},{11,11,315},{14,11,423},{143,11,141},{5,0,993},{7,0,515},{137,0,91},{4,0,131 -},{8,0,200},{5,10,484},{5,10,510},{6,10,434},{7,10,1000},{7,10,1098},{136,10,2}, -{152,0,10},{4,11,62},{5,11,83},{6,11,399},{6,11,579},{7,11,692},{7,11,846},{7,11 -,1015},{7,11,1799},{8,11,403},{9,11,394},{10,11,133},{12,11,4},{12,11,297},{12, -11,452},{16,11,81},{18,11,19},{18,11,25},{21,11,14},{22,11,12},{151,11,18},{140, -11,459},{132,11,177},{7,0,1433},{9,0,365},{137,11,365},{132,10,460},{5,0,103},{6 -,0,2004},{7,0,921},{8,0,580},{8,0,593},{8,0,630},{10,0,28},{5,11,411},{135,11, -653},{4,10,932},{133,10,891},{4,0,911},{5,0,867},{5,0,1013},{7,0,2034},{8,0,798} -,{136,0,813},{7,11,439},{10,11,727},{11,11,260},{139,11,684},{136,10,625},{5,11, -208},{7,11,753},{135,11,1528},{5,0,461},{7,0,1925},{12,0,39},{13,0,265},{13,0, -439},{134,10,76},{6,0,853},{8,10,92},{137,10,221},{5,0,135},{6,0,519},{7,0,1722} -,{10,0,271},{11,0,261},{145,0,54},{139,11,814},{14,0,338},{148,0,81},{4,0,300},{ -133,0,436},{5,0,419},{5,0,687},{7,0,864},{9,0,470},{135,11,864},{9,0,836},{133, -11,242},{134,0,1937},{4,10,763},{133,11,953},{132,10,622},{132,0,393},{133,10, -253},{8,0,357},{10,0,745},{14,0,426},{17,0,94},{19,0,57},{135,10,546},{5,11,615} -,{146,11,37},{9,10,73},{10,10,110},{14,10,185},{145,10,119},{11,0,703},{7,10,624 -},{7,10,916},{10,10,256},{139,10,87},{133,11,290},{5,10,212},{12,10,35},{141,10, -382},{132,11,380},{5,11,52},{7,11,277},{9,11,368},{139,11,791},{133,0,387},{10, -11,138},{139,11,476},{4,0,6},{5,0,708},{136,0,75},{7,0,1351},{9,0,581},{10,0,639 -},{11,0,453},{140,0,584},{132,0,303},{138,0,772},{135,10,1175},{4,0,749},{5,10, -816},{6,11,256},{7,11,307},{7,11,999},{7,11,1481},{7,11,1732},{7,11,1738},{8,11, -265},{9,11,414},{11,11,316},{12,11,52},{13,11,420},{147,11,100},{135,11,1296},{6 -,0,1065},{5,10,869},{5,10,968},{6,10,1626},{8,10,734},{136,10,784},{4,10,542},{6 -,10,1716},{6,10,1727},{7,10,1082},{7,10,1545},{8,10,56},{8,10,118},{8,10,412},{8 -,10,564},{9,10,888},{9,10,908},{10,10,50},{10,10,423},{11,10,685},{11,10,697},{ -11,10,933},{12,10,299},{13,10,126},{13,10,136},{13,10,170},{141,10,190},{134,0, -226},{4,0,106},{7,0,310},{11,0,717},{133,11,723},{5,0,890},{5,0,988},{4,10,232}, -{9,10,202},{10,10,474},{140,10,433},{6,0,626},{142,0,431},{10,0,706},{150,0,44}, -{13,0,51},{6,10,108},{7,10,1003},{7,10,1181},{8,10,111},{136,10,343},{132,0,698} -,{5,11,109},{6,11,1784},{7,11,1895},{12,11,296},{140,11,302},{134,0,828},{134,10 -,1712},{138,0,17},{7,0,1929},{4,10,133},{5,11,216},{7,10,711},{7,10,1298},{7,10, -1585},{7,11,1879},{9,11,141},{9,11,270},{9,11,679},{10,11,159},{10,11,553},{11, -11,197},{11,11,438},{12,11,538},{12,11,559},{13,11,193},{13,11,423},{14,11,144}, -{14,11,166},{14,11,167},{15,11,67},{147,11,84},{141,11,127},{7,11,1872},{137,11, -81},{6,10,99},{7,10,1808},{145,10,57},{134,11,391},{5,0,689},{6,0,84},{7,0,1250} -,{6,10,574},{7,10,428},{10,10,669},{11,10,485},{11,10,840},{12,10,300},{142,10, -250},{7,11,322},{136,11,249},{7,11,432},{135,11,1649},{135,10,1871},{137,10,252} -,{6,11,155},{140,11,234},{7,0,871},{19,0,27},{147,11,27},{140,0,498},{5,0,986},{ -6,0,130},{138,0,823},{6,0,1793},{7,0,1582},{8,0,458},{10,0,101},{10,0,318},{10,0 -,945},{12,0,734},{16,0,104},{18,0,177},{6,10,323},{135,10,1564},{5,11,632},{138, -11,526},{10,0,435},{7,10,461},{136,10,775},{6,11,144},{7,11,948},{7,11,1042},{7, -11,1857},{8,11,235},{8,11,461},{9,11,453},{9,11,530},{10,11,354},{17,11,77},{19, -11,99},{148,11,79},{138,0,966},{7,0,1644},{137,0,129},{135,0,997},{136,0,502},{5 -,11,196},{6,11,486},{7,11,212},{8,11,309},{136,11,346},{7,10,727},{146,10,73},{ -132,0,823},{132,11,686},{135,0,1927},{4,0,762},{7,0,1756},{137,0,98},{136,10,577 -},{24,0,8},{4,11,30},{5,11,43},{152,11,8},{7,0,1046},{139,0,160},{7,0,492},{4,10 -,413},{5,10,677},{7,11,492},{8,10,432},{140,10,280},{6,0,45},{7,0,433},{8,0,129} -,{9,0,21},{10,0,392},{11,0,79},{12,0,499},{13,0,199},{141,0,451},{7,0,558},{136, -0,353},{4,11,220},{7,11,1535},{9,11,93},{139,11,474},{7,10,646},{7,10,1730},{11, -10,446},{141,10,178},{133,0,785},{134,0,1145},{8,0,81},{9,0,189},{9,0,201},{11,0 -,478},{11,0,712},{141,0,338},{5,0,353},{151,0,26},{11,0,762},{132,10,395},{134,0 -,2024},{4,0,611},{133,0,606},{9,10,174},{10,10,164},{11,10,440},{11,10,841},{143 -,10,98},{134,10,426},{10,10,608},{139,10,1002},{138,10,250},{6,0,25},{7,0,855},{ -7,0,1258},{144,0,32},{7,11,1725},{138,11,393},{5,11,263},{134,11,414},{6,0,2011} -,{133,10,476},{4,0,4},{7,0,1118},{7,0,1320},{7,0,1706},{8,0,277},{9,0,622},{10,0 -,9},{11,0,724},{12,0,350},{12,0,397},{13,0,28},{13,0,159},{15,0,89},{18,0,5},{19 -,0,9},{20,0,34},{22,0,47},{6,11,178},{6,11,1750},{8,11,251},{9,11,690},{10,11, -155},{10,11,196},{10,11,373},{11,11,698},{13,11,155},{148,11,93},{5,11,97},{137, -11,393},{7,0,764},{11,0,461},{12,0,172},{5,10,76},{6,10,458},{6,10,497},{7,10, -868},{9,10,658},{10,10,594},{11,10,566},{12,10,338},{141,10,200},{134,0,1449},{ -138,11,40},{134,11,1639},{134,0,1445},{6,0,1168},{4,10,526},{7,10,1029},{135,10, -1054},{4,11,191},{7,11,934},{8,11,647},{145,11,97},{132,10,636},{6,0,233},{7,10, -660},{7,10,1124},{17,10,31},{19,10,22},{151,10,14},{6,10,1699},{136,11,110},{12, -11,246},{15,11,162},{19,11,64},{20,11,8},{20,11,95},{22,11,24},{152,11,17},{5,11 -,165},{9,11,346},{138,11,655},{5,11,319},{135,11,534},{134,0,255},{9,0,216},{8, -11,128},{139,11,179},{9,0,183},{139,0,286},{11,0,956},{151,0,3},{4,0,536},{7,0, -1141},{10,0,723},{139,0,371},{4,10,279},{7,10,301},{137,10,362},{7,0,285},{5,11, -57},{6,11,101},{6,11,1663},{7,11,132},{7,11,1048},{7,11,1154},{7,11,1415},{7,11, -1507},{12,11,493},{15,11,105},{151,11,15},{5,11,459},{7,11,1073},{7,10,1743},{8, -11,241},{136,11,334},{4,10,178},{133,10,399},{135,0,560},{132,0,690},{135,0,1246 -},{18,0,157},{147,0,63},{10,0,599},{11,0,33},{12,0,571},{149,0,1},{6,11,324},{6, -11,520},{7,11,338},{7,11,1616},{7,11,1729},{8,11,228},{9,11,69},{139,11,750},{7, -0,1862},{12,0,491},{12,0,520},{13,0,383},{142,0,244},{135,11,734},{134,10,1692}, -{10,0,448},{11,0,630},{17,0,117},{6,10,202},{7,11,705},{12,10,360},{17,10,118},{ -18,10,27},{148,10,67},{4,11,73},{6,11,612},{7,11,927},{7,11,1822},{8,11,217},{9, -11,472},{9,11,765},{9,11,766},{10,11,408},{11,11,51},{11,11,793},{12,11,266},{15 -,11,158},{20,11,89},{150,11,32},{4,0,190},{133,0,554},{133,0,1001},{5,11,389},{8 -,11,636},{137,11,229},{5,0,446},{7,10,872},{10,10,516},{139,10,167},{137,10,313} -,{132,10,224},{134,0,1313},{5,10,546},{7,10,35},{8,10,11},{8,10,12},{9,10,315},{ -9,10,533},{10,10,802},{11,10,166},{12,10,525},{142,10,243},{6,0,636},{137,0,837} -,{5,10,241},{8,10,242},{9,10,451},{10,10,667},{11,10,598},{140,10,429},{22,10,46 -},{150,11,46},{136,11,472},{11,0,278},{142,0,73},{141,11,185},{132,0,868},{134,0 -,972},{4,10,366},{137,10,516},{138,0,1010},{5,11,189},{6,10,1736},{7,11,442},{7, -11,443},{8,11,281},{12,11,174},{13,11,83},{141,11,261},{139,11,384},{6,11,2},{7, -11,191},{7,11,446},{7,11,758},{7,11,1262},{7,11,1737},{8,11,22},{8,11,270},{8,11 -,612},{9,11,4},{9,11,167},{9,11,312},{9,11,436},{10,11,156},{10,11,216},{10,11, -311},{10,11,623},{11,11,72},{11,11,330},{11,11,455},{12,11,101},{12,11,321},{12, -11,504},{12,11,530},{12,11,543},{13,11,17},{13,11,156},{13,11,334},{14,11,48},{ -15,11,70},{17,11,60},{148,11,64},{6,10,331},{136,10,623},{135,0,1231},{132,0,304 -},{6,11,60},{7,11,670},{7,11,1327},{8,11,411},{8,11,435},{9,11,653},{9,11,740},{ -10,11,385},{11,11,222},{11,11,324},{11,11,829},{140,11,611},{7,0,506},{6,11,166} -,{7,11,374},{135,11,1174},{14,11,43},{146,11,21},{135,11,1694},{135,10,1888},{5, -11,206},{134,11,398},{135,11,50},{150,0,26},{6,0,53},{6,0,199},{7,0,1408},{8,0, -32},{8,0,93},{10,0,397},{10,0,629},{11,0,593},{11,0,763},{13,0,326},{145,0,35},{ -134,0,105},{132,10,394},{4,0,843},{138,0,794},{11,0,704},{141,0,396},{5,0,114},{ -5,0,255},{141,0,285},{6,0,619},{7,0,898},{7,0,1092},{8,0,485},{18,0,28},{19,0, -116},{135,10,1931},{9,0,145},{7,10,574},{135,10,1719},{7,0,2035},{8,0,19},{9,0, -89},{138,0,831},{132,10,658},{6,11,517},{7,11,1159},{10,11,621},{139,11,192},{7, -0,1933},{7,11,1933},{9,10,781},{10,10,144},{11,10,385},{13,10,161},{13,10,228},{ -13,10,268},{148,10,107},{136,10,374},{10,11,223},{139,11,645},{135,0,1728},{7,11 -,64},{7,11,289},{136,11,245},{4,10,344},{6,10,498},{139,10,323},{136,0,746},{135 -,10,1063},{137,10,155},{4,0,987},{6,0,1964},{6,0,1974},{6,0,1990},{136,0,995},{ -133,11,609},{133,10,906},{134,0,1550},{134,0,874},{5,11,129},{6,11,61},{135,11, -947},{4,0,1018},{6,0,1938},{6,0,2021},{134,0,2039},{132,0,814},{11,0,126},{139,0 -,287},{134,0,1264},{5,0,955},{136,0,814},{141,11,506},{132,11,314},{6,0,981},{ -139,11,1000},{5,0,56},{8,0,892},{8,0,915},{140,0,776},{148,0,100},{10,0,4},{10,0 -,13},{11,0,638},{148,0,57},{148,11,74},{5,0,738},{132,10,616},{133,11,637},{136, -10,692},{133,0,758},{132,10,305},{137,11,590},{5,11,280},{135,11,1226},{134,11, -494},{135,0,1112},{133,11,281},{13,0,44},{14,0,214},{5,10,214},{7,10,603},{8,10, -611},{9,10,686},{10,10,88},{11,10,459},{11,10,496},{12,10,463},{140,10,590},{139 -,0,328},{135,11,1064},{137,0,133},{7,0,168},{13,0,196},{141,0,237},{134,10,1703} -,{134,0,1152},{135,0,1245},{5,0,110},{6,0,169},{6,0,1702},{7,0,400},{8,0,538},{9 -,0,184},{9,0,524},{140,0,218},{6,0,1816},{10,0,871},{12,0,769},{140,0,785},{132, -11,630},{7,11,33},{7,11,120},{8,11,489},{9,11,319},{10,11,820},{11,11,1004},{12, -11,379},{13,11,117},{13,11,412},{14,11,25},{15,11,52},{15,11,161},{16,11,47},{ -149,11,2},{6,0,133},{8,0,413},{9,0,353},{139,0,993},{145,10,19},{4,11,937},{133, -11,801},{134,0,978},{6,0,93},{6,0,1508},{7,0,1422},{7,0,1851},{8,0,673},{9,0,529 -},{140,0,43},{6,0,317},{10,0,512},{4,10,737},{11,10,294},{12,10,60},{12,10,437}, -{13,10,64},{13,10,380},{142,10,430},{9,0,371},{7,11,1591},{144,11,43},{6,10,1758 -},{8,10,520},{9,10,345},{9,10,403},{142,10,350},{5,0,526},{10,10,242},{138,10, -579},{9,0,25},{10,0,467},{138,0,559},{5,10,139},{7,10,1168},{138,10,539},{4,0, -335},{135,0,942},{140,0,754},{132,11,365},{11,0,182},{142,0,195},{142,11,29},{5, -11,7},{139,11,774},{4,11,746},{135,11,1090},{8,0,39},{10,0,773},{11,0,84},{12,0, -205},{142,0,1},{5,0,601},{5,0,870},{5,11,360},{136,11,237},{132,0,181},{136,0, -370},{134,0,1652},{8,0,358},{4,10,107},{7,10,613},{8,10,439},{8,10,504},{9,10, -501},{10,10,383},{139,10,477},{132,10,229},{137,11,785},{4,0,97},{5,0,147},{6,0, -286},{7,0,1362},{141,0,176},{6,0,537},{7,0,788},{7,0,1816},{132,10,903},{140,10, -71},{6,0,743},{134,0,1223},{6,0,375},{7,0,169},{7,0,254},{8,0,780},{135,11,1493} -,{7,0,1714},{4,10,47},{6,10,373},{7,10,452},{7,10,543},{7,10,1856},{9,10,6},{11, -10,257},{139,10,391},{6,0,896},{136,0,1003},{135,0,1447},{137,11,341},{5,10,980} -,{134,10,1754},{145,11,22},{4,11,277},{5,11,608},{6,11,493},{7,11,457},{140,11, -384},{7,10,536},{7,10,1331},{136,10,143},{140,0,744},{7,11,27},{135,11,316},{18, -0,126},{5,10,19},{134,10,533},{4,0,788},{11,0,41},{5,11,552},{5,11,586},{5,11, -676},{6,11,448},{8,11,244},{11,11,1},{11,11,41},{13,11,3},{16,11,54},{17,11,4},{ -146,11,13},{4,0,985},{6,0,1801},{4,11,401},{137,11,264},{5,10,395},{5,10,951},{ -134,10,1776},{5,0,629},{135,0,1549},{11,10,663},{12,10,210},{13,10,166},{13,10, -310},{14,10,373},{147,10,43},{9,11,543},{10,11,524},{11,11,30},{12,11,524},{14, -11,315},{16,11,18},{20,11,26},{148,11,65},{4,11,205},{5,11,623},{7,11,104},{136, -11,519},{5,0,293},{134,0,601},{7,11,579},{9,11,41},{9,11,244},{9,11,669},{10,11, -5},{11,11,861},{11,11,951},{139,11,980},{132,11,717},{132,10,695},{7,10,497},{9, -10,387},{147,10,81},{132,0,420},{142,0,37},{6,0,1134},{6,0,1900},{12,0,830},{12, -0,878},{12,0,894},{15,0,221},{143,0,245},{132,11,489},{7,0,1570},{140,0,542},{8, -0,933},{136,0,957},{6,0,1371},{7,0,31},{8,0,373},{5,10,284},{6,10,49},{6,10,350} -,{7,10,377},{7,10,1693},{8,10,678},{9,10,161},{9,10,585},{9,10,671},{9,10,839},{ -11,10,912},{141,10,427},{135,11,892},{4,0,325},{138,0,125},{139,11,47},{132,10, -597},{138,0,323},{6,0,1547},{7,11,1605},{9,11,473},{11,11,962},{146,11,139},{139 -,10,908},{7,11,819},{9,11,26},{9,11,392},{10,11,152},{10,11,226},{11,11,19},{12, -11,276},{12,11,426},{12,11,589},{13,11,460},{15,11,97},{19,11,48},{148,11,104},{ -135,11,51},{4,0,718},{135,0,1216},{6,0,1896},{6,0,1905},{6,0,1912},{9,0,947},{9, -0,974},{12,0,809},{12,0,850},{12,0,858},{12,0,874},{12,0,887},{12,0,904},{12,0, -929},{12,0,948},{12,0,952},{15,0,198},{15,0,206},{15,0,220},{15,0,227},{15,0,247 -},{18,0,188},{21,0,48},{21,0,50},{24,0,25},{24,0,29},{7,11,761},{7,11,1051},{137 -,11,545},{5,0,124},{5,0,144},{6,0,548},{7,0,15},{7,0,153},{137,0,629},{135,11, -606},{135,10,2014},{7,10,2007},{9,11,46},{9,10,101},{9,10,450},{10,10,66},{10,10 -,842},{11,10,536},{140,10,587},{6,0,75},{7,0,1531},{8,0,416},{9,0,240},{9,0,275} -,{10,0,100},{11,0,658},{11,0,979},{12,0,86},{14,0,207},{15,0,20},{143,0,25},{5,0 -,141},{5,0,915},{6,0,1783},{7,0,211},{7,0,698},{7,0,1353},{9,0,83},{9,0,281},{10 -,0,376},{10,0,431},{11,0,543},{12,0,664},{13,0,280},{13,0,428},{14,0,61},{14,0, -128},{17,0,52},{145,0,81},{132,11,674},{135,0,533},{149,0,6},{132,11,770},{133,0 -,538},{5,11,79},{7,11,1027},{7,11,1477},{139,11,52},{139,10,62},{4,0,338},{133,0 -,400},{5,11,789},{134,11,195},{4,11,251},{4,11,688},{7,11,513},{7,11,1284},{9,11 -,87},{138,11,365},{134,10,1766},{6,0,0},{7,0,84},{11,0,895},{145,0,11},{139,0, -892},{4,0,221},{5,0,659},{7,0,697},{7,0,1211},{138,0,284},{133,0,989},{133,11, -889},{4,11,160},{5,11,330},{7,11,1434},{136,11,174},{6,10,1665},{7,10,256},{7,10 -,1388},{10,10,499},{139,10,670},{7,0,848},{4,10,22},{5,10,10},{136,10,97},{138,0 -,507},{133,10,481},{4,0,188},{135,0,805},{5,0,884},{6,0,732},{139,0,991},{135,11 -,968},{11,11,636},{15,11,145},{17,11,34},{19,11,50},{151,11,20},{7,0,959},{16,0, -60},{6,10,134},{7,10,437},{9,10,37},{14,10,285},{142,10,371},{7,10,486},{8,10, -155},{11,10,93},{140,10,164},{134,0,1653},{7,0,337},{133,10,591},{6,0,1989},{8,0 -,922},{8,0,978},{133,11,374},{132,0,638},{138,0,500},{133,11,731},{5,10,380},{5, -10,650},{136,10,310},{138,11,381},{4,10,364},{7,10,1156},{7,10,1187},{137,10,409 -},{137,11,224},{140,0,166},{134,10,482},{4,11,626},{5,11,642},{6,11,425},{10,11, -202},{139,11,141},{4,10,781},{6,10,487},{7,10,926},{8,10,263},{139,10,500},{135, -0,418},{4,10,94},{135,10,1265},{136,0,760},{132,10,417},{136,11,835},{5,10,348}, -{134,10,522},{6,0,1277},{134,0,1538},{139,11,541},{135,11,1597},{5,11,384},{8,11 -,455},{140,11,48},{136,0,770},{5,11,264},{134,11,184},{4,0,89},{5,0,489},{6,0, -315},{7,0,553},{7,0,1745},{138,0,243},{4,10,408},{4,10,741},{135,10,500},{134,0, -1396},{133,0,560},{6,0,1658},{9,0,3},{10,0,154},{11,0,641},{13,0,85},{13,0,201}, -{141,0,346},{135,11,1595},{5,11,633},{6,11,28},{7,11,219},{135,11,1323},{9,11, -769},{140,11,185},{135,11,785},{7,11,359},{8,11,243},{140,11,175},{138,0,586},{7 -,0,1271},{134,10,73},{132,11,105},{4,0,166},{5,0,505},{134,0,1670},{133,10,576}, -{4,11,324},{138,11,104},{142,10,231},{6,0,637},{7,10,1264},{7,10,1678},{11,10, -945},{12,10,341},{12,10,471},{12,10,569},{23,11,21},{151,11,23},{8,11,559},{141, -11,109},{134,0,1947},{7,0,445},{8,0,307},{8,0,704},{10,0,41},{10,0,439},{11,0, -237},{11,0,622},{140,0,201},{135,11,963},{135,0,1977},{4,0,189},{5,0,713},{136,0 -,57},{138,0,371},{135,10,538},{132,0,552},{6,0,883},{133,10,413},{6,0,923},{132, -11,758},{138,11,215},{136,10,495},{7,10,54},{8,10,312},{10,10,191},{10,10,614},{ -140,10,567},{7,11,351},{139,11,128},{7,0,875},{6,10,468},{7,10,1478},{8,10,530}, -{142,10,290},{135,0,1788},{17,0,49},{133,11,918},{12,11,398},{20,11,39},{21,11, -11},{150,11,41},{10,0,661},{6,10,484},{135,10,822},{135,0,1945},{134,0,794},{137 -,10,900},{135,10,1335},{6,10,1724},{135,10,2022},{132,11,340},{134,0,1135},{4,0, -784},{133,0,745},{5,0,84},{134,0,163},{133,0,410},{4,0,976},{5,11,985},{7,11,509 -},{7,11,529},{145,11,96},{132,10,474},{134,0,703},{135,11,1919},{5,0,322},{8,0, -186},{9,0,262},{10,0,187},{142,0,208},{135,10,1504},{133,0,227},{9,0,560},{13,0, -208},{133,10,305},{132,11,247},{7,0,1395},{8,0,486},{9,0,236},{9,0,878},{10,0, -218},{11,0,95},{19,0,17},{147,0,31},{7,0,2043},{8,0,672},{141,0,448},{4,11,184}, -{5,11,390},{6,11,337},{7,11,23},{7,11,494},{7,11,618},{7,11,1456},{8,11,27},{8, -11,599},{10,11,153},{139,11,710},{135,0,466},{135,10,1236},{6,0,167},{7,0,186},{ -7,0,656},{10,0,643},{4,10,480},{6,10,302},{6,10,1642},{7,10,837},{7,10,1547},{7, -10,1657},{8,10,429},{9,10,228},{13,10,289},{13,10,343},{147,10,101},{134,0,1428} -,{134,0,1440},{5,0,412},{7,10,278},{10,10,739},{11,10,708},{141,10,348},{134,0, -1118},{136,0,562},{148,11,46},{9,0,316},{139,0,256},{134,0,1771},{135,0,1190},{ -137,0,132},{10,11,227},{11,11,497},{11,11,709},{140,11,415},{143,0,66},{6,11,360 -},{7,11,1664},{136,11,478},{144,10,28},{4,0,317},{135,0,1279},{5,0,63},{133,0, -509},{136,11,699},{145,10,36},{134,0,1475},{11,11,343},{142,11,127},{132,11,739} -,{132,0,288},{135,11,1757},{8,0,89},{8,0,620},{9,0,608},{11,0,628},{12,0,322},{ -143,0,124},{134,0,1225},{7,0,1189},{4,11,67},{5,11,422},{6,10,363},{7,11,1037},{ -7,11,1289},{7,11,1555},{7,10,1955},{8,10,725},{9,11,741},{145,11,108},{134,0, -1468},{6,0,689},{134,0,1451},{138,0,120},{151,0,1},{137,10,805},{142,0,329},{5, -10,813},{135,10,2046},{135,0,226},{138,11,96},{7,0,1855},{5,10,712},{11,10,17},{ -13,10,321},{144,10,67},{9,0,461},{6,10,320},{7,10,781},{7,10,1921},{9,10,55},{10 -,10,186},{10,10,273},{10,10,664},{10,10,801},{11,10,996},{11,10,997},{13,10,157} -,{142,10,170},{8,11,203},{8,10,271},{11,11,823},{11,11,846},{12,11,482},{13,11, -133},{13,11,277},{13,11,302},{13,11,464},{14,11,205},{142,11,221},{135,0,1346},{ -4,11,449},{133,11,718},{134,0,85},{14,0,299},{7,10,103},{7,10,863},{11,10,184},{ -145,10,62},{4,11,355},{6,11,311},{9,11,256},{138,11,404},{137,10,659},{138,11, -758},{133,11,827},{5,11,64},{140,11,581},{134,0,1171},{4,11,442},{7,11,1047},{7, -11,1352},{135,11,1643},{132,0,980},{5,11,977},{6,11,288},{7,11,528},{135,11,1065 -},{5,0,279},{6,0,235},{7,0,468},{8,0,446},{9,0,637},{10,0,717},{11,0,738},{140,0 -,514},{132,0,293},{11,10,337},{142,10,303},{136,11,285},{5,0,17},{6,0,371},{9,0, -528},{12,0,364},{132,11,254},{5,10,77},{7,10,1455},{10,10,843},{147,10,73},{150, -0,5},{132,10,458},{6,11,12},{7,11,1219},{145,11,73},{135,10,1420},{6,10,109},{ -138,10,382},{135,11,125},{6,10,330},{7,10,1084},{139,10,142},{6,11,369},{6,11, -502},{7,11,1036},{8,11,348},{9,11,452},{10,11,26},{11,11,224},{11,11,387},{11,11 -,772},{12,11,95},{12,11,629},{13,11,195},{13,11,207},{13,11,241},{14,11,260},{14 -,11,270},{143,11,140},{132,11,269},{5,11,480},{7,11,532},{7,11,1197},{7,11,1358} -,{8,11,291},{11,11,349},{142,11,396},{150,0,48},{10,0,601},{13,0,353},{141,0,376 -},{5,0,779},{5,0,807},{6,0,1655},{134,0,1676},{142,11,223},{4,0,196},{5,0,558},{ -133,0,949},{148,11,15},{135,11,1764},{134,0,1322},{132,0,752},{139,0,737},{135, -11,657},{136,11,533},{135,0,412},{4,0,227},{5,0,159},{5,0,409},{7,0,80},{8,0,556 -},{10,0,479},{12,0,418},{14,0,50},{14,0,123},{14,0,192},{14,0,249},{14,0,295},{ -143,0,27},{7,0,1470},{8,0,66},{8,0,137},{8,0,761},{9,0,638},{11,0,80},{11,0,212} -,{11,0,368},{11,0,418},{12,0,8},{13,0,15},{16,0,61},{17,0,59},{19,0,28},{148,0, -84},{135,10,1985},{4,11,211},{4,11,332},{5,11,335},{6,11,238},{7,11,269},{7,11, -811},{7,11,1797},{8,10,122},{8,11,836},{9,11,507},{141,11,242},{6,0,683},{134,0, -1252},{4,0,873},{132,10,234},{134,0,835},{6,0,38},{7,0,1220},{8,0,185},{8,0,256} -,{9,0,22},{9,0,331},{10,0,738},{11,0,205},{11,0,540},{11,0,746},{13,0,465},{14,0 -,88},{142,0,194},{138,0,986},{5,11,1009},{12,11,582},{146,11,131},{4,0,159},{6,0 -,115},{7,0,252},{7,0,257},{7,0,1928},{8,0,69},{9,0,384},{10,0,91},{10,0,615},{12 -,0,375},{14,0,235},{18,0,117},{147,0,123},{133,0,911},{136,0,278},{5,10,430},{5, -10,932},{6,10,131},{7,10,417},{9,10,522},{11,10,314},{141,10,390},{14,10,149},{ -14,10,399},{143,10,57},{4,0,151},{7,0,1567},{136,0,749},{5,11,228},{6,11,203},{7 -,11,156},{8,11,347},{137,11,265},{132,10,507},{10,0,989},{140,0,956},{133,0,990} -,{5,0,194},{6,0,927},{7,0,1662},{9,0,90},{140,0,564},{4,10,343},{133,10,511},{ -133,0,425},{7,10,455},{138,10,591},{4,0,774},{7,11,476},{7,11,1592},{138,11,87}, -{5,0,971},{135,10,1381},{5,11,318},{147,11,121},{5,11,291},{7,11,765},{9,11,389} -,{140,11,548},{134,10,575},{4,0,827},{12,0,646},{12,0,705},{12,0,712},{140,0,714 -},{139,0,752},{137,0,662},{5,0,72},{6,0,264},{7,0,21},{7,0,46},{7,0,2013},{8,0, -215},{8,0,513},{10,0,266},{139,0,22},{139,11,522},{6,0,239},{7,0,118},{10,0,95}, -{11,0,603},{13,0,443},{14,0,160},{143,0,4},{6,0,431},{134,0,669},{7,10,1127},{7, -10,1572},{10,10,297},{10,10,422},{11,10,764},{11,10,810},{12,10,264},{13,10,102} -,{13,10,300},{13,10,484},{14,10,147},{14,10,229},{17,10,71},{18,10,118},{147,10, -120},{5,0,874},{6,0,1677},{15,0,0},{10,11,525},{139,11,82},{6,0,65},{7,0,939},{7 -,0,1172},{7,0,1671},{9,0,540},{10,0,696},{11,0,265},{11,0,732},{11,0,928},{11,0, -937},{141,0,438},{134,0,1350},{136,11,547},{132,11,422},{5,11,355},{145,11,0},{ -137,11,905},{5,0,682},{135,0,1887},{132,0,809},{4,0,696},{133,11,865},{6,0,1074} -,{6,0,1472},{14,10,35},{142,10,191},{5,11,914},{134,11,1625},{133,11,234},{135, -11,1383},{137,11,780},{132,10,125},{4,0,726},{133,0,630},{8,0,802},{136,0,838},{ -132,10,721},{6,0,1337},{7,0,776},{19,0,56},{136,10,145},{132,0,970},{7,10,792},{ -8,10,147},{10,10,821},{139,10,1021},{139,10,970},{8,0,940},{137,0,797},{135,11, -1312},{9,0,248},{10,0,400},{7,11,816},{7,11,1241},{7,10,1999},{9,11,283},{9,11, -520},{10,11,213},{10,11,307},{10,11,463},{10,11,671},{10,11,746},{11,11,401},{11 -,11,794},{12,11,517},{18,11,107},{147,11,115},{6,0,1951},{134,0,2040},{135,11, -339},{13,0,41},{15,0,93},{5,10,168},{5,10,930},{8,10,74},{9,10,623},{12,10,500}, -{140,10,579},{6,0,118},{7,0,215},{7,0,1521},{140,0,11},{6,10,220},{7,10,1101},{ -141,10,105},{6,11,421},{7,11,61},{7,11,1540},{10,11,11},{138,11,501},{7,0,615},{ -138,0,251},{140,11,631},{135,0,1044},{6,10,19},{7,10,1413},{139,10,428},{133,0, -225},{7,10,96},{8,10,401},{8,10,703},{137,10,896},{145,10,116},{6,11,102},{7,11, -72},{15,11,142},{147,11,67},{7,10,1961},{7,10,1965},{8,10,702},{136,10,750},{7, -10,2030},{8,10,150},{8,10,737},{12,10,366},{151,11,30},{4,0,370},{5,0,756},{7,0, -1326},{135,11,823},{8,10,800},{9,10,148},{9,10,872},{9,10,890},{11,10,309},{11, -10,1001},{13,10,267},{141,10,323},{6,0,1662},{7,0,48},{8,0,771},{10,0,116},{13,0 -,104},{14,0,105},{14,0,184},{15,0,168},{19,0,92},{148,0,68},{10,0,209},{135,11, -1870},{7,11,68},{8,11,48},{8,11,88},{8,11,582},{8,11,681},{9,11,373},{9,11,864}, -{11,11,157},{11,11,336},{11,11,843},{148,11,27},{134,0,930},{4,11,88},{5,11,137} -,{5,11,174},{5,11,777},{6,11,1664},{6,11,1725},{7,11,77},{7,11,426},{7,11,1317}, -{7,11,1355},{8,11,126},{8,11,563},{9,11,523},{9,11,750},{10,11,310},{10,11,836}, -{11,11,42},{11,11,318},{11,11,731},{12,11,68},{12,11,92},{12,11,507},{12,11,692} -,{13,11,81},{13,11,238},{13,11,374},{18,11,138},{19,11,78},{19,11,111},{20,11,55 -},{20,11,77},{148,11,92},{4,11,938},{135,11,1831},{5,10,547},{7,10,424},{8,11, -617},{138,11,351},{6,0,1286},{6,11,1668},{7,11,1499},{8,11,117},{9,11,314},{138, -11,174},{6,0,759},{6,0,894},{7,11,707},{139,11,563},{4,0,120},{135,0,1894},{9,0, -385},{149,0,17},{138,0,429},{133,11,403},{5,0,820},{135,0,931},{10,0,199},{133, -10,133},{6,0,151},{6,0,1675},{7,0,383},{151,0,10},{6,0,761},{136,10,187},{8,0, -365},{10,10,0},{10,10,818},{139,10,988},{4,11,44},{5,11,311},{6,11,156},{7,11, -639},{7,11,762},{7,11,1827},{9,11,8},{9,11,462},{148,11,83},{4,11,346},{7,11,115 -},{9,11,180},{9,11,456},{138,11,363},{136,10,685},{7,0,1086},{145,0,46},{6,0, -1624},{11,0,11},{12,0,422},{13,0,444},{142,0,360},{6,0,1020},{6,0,1260},{134,0, -1589},{4,0,43},{5,0,344},{5,0,357},{14,0,472},{150,0,58},{6,0,1864},{6,0,1866},{ -6,0,1868},{6,0,1869},{6,0,1874},{6,0,1877},{6,0,1903},{6,0,1911},{9,0,920},{9,0, -921},{9,0,924},{9,0,946},{9,0,959},{9,0,963},{9,0,970},{9,0,997},{9,0,1008},{9,0 -,1017},{12,0,795},{12,0,797},{12,0,798},{12,0,800},{12,0,803},{12,0,811},{12,0, -820},{12,0,821},{12,0,839},{12,0,841},{12,0,848},{12,0,911},{12,0,921},{12,0,922 -},{12,0,925},{12,0,937},{12,0,944},{12,0,945},{12,0,953},{15,0,184},{15,0,191},{ -15,0,199},{15,0,237},{15,0,240},{15,0,243},{15,0,246},{18,0,203},{21,0,40},{21,0 -,52},{21,0,57},{24,0,23},{24,0,28},{152,0,30},{134,0,725},{145,11,58},{133,0,888 -},{137,10,874},{4,0,711},{8,10,774},{10,10,670},{140,10,51},{144,11,40},{6,11, -185},{7,11,1899},{139,11,673},{137,10,701},{137,0,440},{4,11,327},{5,11,478},{7, -11,1332},{8,11,753},{140,11,227},{4,10,127},{5,10,350},{6,10,356},{8,10,426},{9, -10,572},{10,10,247},{139,10,312},{5,11,1020},{133,11,1022},{4,11,103},{133,11, -401},{6,0,1913},{6,0,1926},{6,0,1959},{9,0,914},{9,0,939},{9,0,952},{9,0,979},{9 -,0,990},{9,0,998},{9,0,1003},{9,0,1023},{12,0,827},{12,0,834},{12,0,845},{12,0, -912},{12,0,935},{12,0,951},{15,0,172},{15,0,174},{18,0,198},{149,0,63},{5,0,958} -,{5,0,987},{4,11,499},{135,11,1421},{7,0,885},{6,10,59},{6,10,1762},{9,10,603},{ -141,10,397},{10,11,62},{141,11,164},{4,0,847},{135,0,326},{11,0,276},{142,0,293} -,{4,0,65},{5,0,479},{5,0,1004},{7,0,1913},{8,0,317},{9,0,302},{10,0,612},{13,0, -22},{132,11,96},{4,0,261},{135,0,510},{135,0,1514},{6,10,111},{7,10,4},{8,10,163 -},{8,10,776},{138,10,566},{4,0,291},{9,0,515},{12,0,152},{12,0,443},{13,0,392},{ -142,0,357},{7,11,399},{135,11,1492},{4,0,589},{139,0,282},{6,11,563},{135,10, -1994},{5,10,297},{135,10,1038},{4,0,130},{7,0,843},{135,0,1562},{5,0,42},{5,0, -879},{7,0,245},{7,0,324},{7,0,1532},{11,0,463},{11,0,472},{13,0,363},{144,0,52}, -{4,0,134},{133,0,372},{133,0,680},{136,10,363},{6,0,1997},{8,0,935},{136,0,977}, -{4,0,810},{135,0,1634},{135,10,1675},{7,0,1390},{4,11,910},{133,11,832},{7,10, -808},{8,11,266},{139,11,578},{132,0,644},{4,0,982},{138,0,867},{132,10,280},{135 -,0,540},{140,10,54},{135,0,123},{134,0,1978},{4,10,421},{133,10,548},{6,0,623},{ -136,0,789},{4,0,908},{5,0,359},{5,0,508},{6,0,1723},{7,0,343},{7,0,1996},{135,0, -2026},{134,0,1220},{4,0,341},{135,0,480},{6,10,254},{9,10,109},{138,10,103},{134 -,0,888},{8,11,528},{137,11,348},{7,0,1995},{8,0,299},{11,0,890},{12,0,674},{4,11 -,20},{133,11,616},{135,11,1094},{134,10,1630},{4,0,238},{5,0,503},{6,0,179},{7,0 -,2003},{8,0,381},{8,0,473},{9,0,149},{10,0,788},{15,0,45},{15,0,86},{20,0,110},{ -150,0,57},{133,10,671},{4,11,26},{5,11,429},{6,11,245},{7,11,704},{7,11,1379},{ -135,11,1474},{4,0,121},{5,0,156},{5,0,349},{9,0,431},{10,0,605},{142,0,342},{7, -11,943},{139,11,614},{132,10,889},{132,11,621},{7,10,1382},{7,11,1382},{135,10, -1910},{132,10,627},{133,10,775},{133,11,542},{133,11,868},{136,11,433},{6,0,1373 -},{7,0,1011},{11,10,362},{11,10,948},{140,10,388},{6,0,80},{7,0,173},{9,0,547},{ -10,0,730},{14,0,18},{22,0,39},{135,11,1495},{6,0,1694},{135,0,1974},{140,0,196}, -{4,0,923},{6,0,507},{6,0,1711},{7,10,451},{8,10,389},{12,10,490},{13,10,16},{13, -10,215},{13,10,351},{18,10,132},{147,10,125},{6,0,646},{134,0,1047},{135,10,841} -,{136,10,566},{6,0,1611},{135,0,1214},{139,0,926},{132,11,525},{132,0,595},{5,0, -240},{6,0,459},{7,0,12},{7,0,114},{7,0,949},{7,0,1753},{7,0,1805},{8,0,658},{9,0 -,1},{11,0,959},{141,0,446},{5,10,912},{134,10,1695},{132,0,446},{7,11,62},{12,11 -,45},{147,11,112},{5,10,236},{6,10,572},{8,10,492},{11,10,618},{144,10,56},{5,10 -,190},{136,10,318},{135,10,1376},{4,11,223},{6,11,359},{11,11,3},{13,11,108},{14 -,11,89},{144,11,22},{132,11,647},{134,0,490},{134,0,491},{134,0,1584},{135,11, -685},{138,11,220},{7,0,250},{136,0,507},{132,0,158},{4,0,140},{7,0,362},{8,0,209 -},{9,0,10},{9,0,160},{9,0,503},{9,0,614},{10,0,689},{11,0,327},{11,0,553},{11,0, -725},{11,0,767},{12,0,252},{12,0,583},{13,0,192},{14,0,269},{14,0,356},{148,0,50 -},{19,0,1},{19,0,26},{150,0,9},{132,11,109},{6,0,228},{7,0,1341},{9,0,408},{138, -0,343},{4,0,373},{5,0,283},{6,0,480},{7,0,609},{10,0,860},{138,0,878},{6,0,779}, -{134,0,1209},{4,0,557},{7,11,263},{7,11,628},{136,11,349},{132,0,548},{7,0,197}, -{8,0,142},{8,0,325},{9,0,150},{9,0,596},{10,0,350},{10,0,353},{11,0,74},{11,0, -315},{12,0,662},{12,0,681},{14,0,423},{143,0,141},{4,11,40},{10,11,67},{11,11, -117},{11,11,768},{139,11,935},{7,11,992},{8,11,301},{9,11,722},{12,11,63},{13,11 -,29},{14,11,161},{143,11,18},{6,0,1490},{138,11,532},{5,0,580},{7,0,378},{7,0, -674},{7,0,1424},{15,0,83},{16,0,11},{15,11,83},{144,11,11},{6,0,1057},{6,0,1335} -,{10,0,316},{7,10,85},{7,10,247},{8,10,585},{138,10,163},{4,0,169},{5,0,83},{6,0 -,399},{6,0,579},{6,0,1513},{7,0,692},{7,0,846},{7,0,1015},{7,0,1799},{8,0,403},{ -9,0,394},{10,0,133},{12,0,4},{12,0,297},{12,0,452},{16,0,81},{18,0,25},{21,0,14} -,{22,0,12},{151,0,18},{134,0,1106},{7,0,1546},{11,0,299},{142,0,407},{134,0,1192 -},{132,0,177},{5,0,411},{135,0,653},{7,0,439},{10,0,727},{11,0,260},{139,0,684}, -{138,10,145},{147,10,83},{5,0,208},{7,0,753},{135,0,1528},{137,11,617},{135,10, -1922},{135,11,825},{11,0,422},{13,0,389},{4,10,124},{10,10,457},{11,10,121},{11, -10,169},{11,10,870},{12,10,214},{14,10,187},{143,10,77},{11,0,615},{15,0,58},{11 -,11,615},{143,11,58},{9,0,618},{138,0,482},{6,0,1952},{6,0,1970},{142,0,505},{7, -10,1193},{135,11,1838},{133,0,242},{135,10,1333},{6,10,107},{7,10,638},{7,10, -1632},{137,10,396},{133,0,953},{5,10,370},{134,10,1756},{5,11,28},{6,11,204},{10 -,11,320},{10,11,583},{13,11,502},{14,11,72},{14,11,274},{14,11,312},{14,11,344}, -{15,11,159},{16,11,62},{16,11,69},{17,11,30},{18,11,42},{18,11,53},{18,11,84},{ -18,11,140},{19,11,68},{19,11,85},{20,11,5},{20,11,45},{20,11,101},{22,11,7},{150 -,11,20},{4,11,558},{6,11,390},{7,11,162},{7,11,689},{9,11,360},{138,11,653},{11, -0,802},{141,0,67},{133,10,204},{133,0,290},{5,10,970},{134,10,1706},{132,0,380}, -{5,0,52},{7,0,277},{9,0,368},{139,0,791},{5,11,856},{6,11,1672},{6,11,1757},{6, -11,1781},{7,11,1150},{7,11,1425},{7,11,1453},{140,11,513},{5,11,92},{7,10,3},{10 -,11,736},{140,11,102},{4,0,112},{5,0,653},{5,10,483},{5,10,685},{6,10,489},{7,10 -,1204},{136,10,394},{132,10,921},{6,0,1028},{133,10,1007},{5,11,590},{9,11,213}, -{145,11,91},{135,10,1696},{10,0,138},{139,0,476},{5,0,725},{5,0,727},{135,0,1811 -},{4,0,979},{6,0,1821},{6,0,1838},{8,0,876},{8,0,883},{8,0,889},{8,0,893},{8,0, -895},{10,0,934},{12,0,720},{14,0,459},{148,0,123},{135,11,551},{4,0,38},{6,0,435 -},{7,0,307},{7,0,999},{7,0,1481},{7,0,1732},{7,0,1738},{8,0,371},{9,0,414},{11,0 -,316},{12,0,52},{13,0,420},{147,0,100},{135,0,1296},{132,10,712},{134,10,1629},{ -133,0,723},{134,0,651},{136,11,191},{9,11,791},{10,11,93},{11,11,301},{16,11,13} -,{17,11,23},{18,11,135},{19,11,12},{20,11,1},{20,11,12},{148,11,14},{136,11,503} -,{6,11,466},{135,11,671},{6,0,1200},{134,0,1330},{135,0,1255},{134,0,986},{5,0, -109},{6,0,1784},{7,0,1895},{12,0,296},{140,0,302},{135,11,983},{133,10,485},{134 -,0,660},{134,0,800},{5,0,216},{5,0,294},{6,0,591},{7,0,1879},{9,0,141},{9,0,270} -,{9,0,679},{10,0,159},{11,0,197},{11,0,438},{12,0,538},{12,0,559},{14,0,144},{14 -,0,167},{15,0,67},{4,10,285},{5,10,317},{6,10,301},{7,10,7},{8,10,153},{10,10, -766},{11,10,468},{12,10,467},{141,10,143},{136,0,945},{134,0,1090},{137,0,81},{ -12,11,468},{19,11,96},{148,11,24},{134,0,391},{138,11,241},{7,0,322},{136,0,249} -,{134,0,1412},{135,11,795},{5,0,632},{138,0,526},{136,10,819},{6,0,144},{7,0,948 -},{7,0,1042},{8,0,235},{8,0,461},{9,0,453},{9,0,796},{10,0,354},{17,0,77},{135, -11,954},{139,10,917},{6,0,940},{134,0,1228},{4,0,362},{7,0,52},{135,0,303},{6,11 -,549},{8,11,34},{8,11,283},{9,11,165},{138,11,475},{7,11,370},{7,11,1007},{7,11, -1177},{135,11,1565},{5,11,652},{5,11,701},{135,11,449},{5,0,196},{6,0,486},{7,0, -212},{8,0,309},{136,0,346},{6,10,1719},{6,10,1735},{7,10,2016},{7,10,2020},{8,10 -,837},{137,10,852},{6,11,159},{6,11,364},{7,11,516},{7,11,1439},{137,11,518},{ -135,0,1912},{135,0,1290},{132,0,686},{141,11,151},{138,0,625},{136,0,706},{138, -10,568},{139,0,412},{4,0,30},{133,0,43},{8,10,67},{138,10,419},{7,0,967},{141,0, -11},{12,0,758},{14,0,441},{142,0,462},{10,10,657},{14,10,297},{142,10,361},{139, -10,729},{4,0,220},{135,0,1535},{7,11,501},{9,11,111},{10,11,141},{11,11,332},{13 -,11,43},{13,11,429},{14,11,130},{14,11,415},{145,11,102},{4,0,950},{6,0,1859},{7 -,0,11},{8,0,873},{12,0,710},{12,0,718},{12,0,748},{12,0,765},{148,0,124},{5,11, -149},{5,11,935},{136,11,233},{142,11,291},{134,0,1579},{7,0,890},{8,10,51},{9,10 -,868},{10,10,833},{12,10,481},{12,10,570},{148,10,106},{141,0,2},{132,10,445},{ -136,11,801},{135,0,1774},{7,0,1725},{138,0,393},{5,0,263},{134,0,414},{132,11, -322},{133,10,239},{7,0,456},{7,10,1990},{8,10,130},{139,10,720},{137,0,818},{5, -10,123},{6,10,530},{7,10,348},{135,10,1419},{135,10,2024},{6,0,178},{6,0,1750},{ -8,0,251},{9,0,690},{10,0,155},{10,0,196},{10,0,373},{11,0,698},{13,0,155},{148,0 -,93},{5,0,97},{137,0,393},{134,0,674},{11,0,223},{140,0,168},{132,10,210},{139, -11,464},{6,0,1639},{146,0,159},{139,11,2},{7,0,934},{8,0,647},{17,0,97},{19,0,59 -},{150,0,2},{132,0,191},{5,0,165},{9,0,346},{10,0,655},{11,0,885},{4,10,430},{ -135,11,357},{133,0,877},{5,10,213},{133,11,406},{8,0,128},{139,0,179},{6,11,69}, -{135,11,117},{135,0,1297},{11,11,43},{13,11,72},{141,11,142},{135,11,1830},{142, -0,164},{5,0,57},{6,0,101},{6,0,586},{6,0,1663},{7,0,132},{7,0,1154},{7,0,1415},{ -7,0,1507},{12,0,493},{15,0,105},{151,0,15},{5,0,459},{7,0,1073},{8,0,241},{136,0 -,334},{133,11,826},{133,10,108},{5,10,219},{10,11,132},{11,11,191},{11,11,358},{ -139,11,460},{6,0,324},{6,0,520},{7,0,338},{7,0,1729},{8,0,228},{139,0,750},{21,0 -,30},{22,0,53},{4,10,193},{5,10,916},{7,10,364},{10,10,398},{10,10,726},{11,10, -317},{11,10,626},{12,10,142},{12,10,288},{12,10,678},{13,10,313},{15,10,113},{ -146,10,114},{6,11,110},{135,11,1681},{135,0,910},{6,10,241},{7,10,907},{8,10,832 -},{9,10,342},{10,10,729},{11,10,284},{11,10,445},{11,10,651},{11,10,863},{13,10, -398},{146,10,99},{7,0,705},{9,0,734},{5,11,1000},{7,11,733},{137,11,583},{4,0,73 -},{6,0,612},{7,0,927},{7,0,1822},{8,0,217},{9,0,765},{9,0,766},{10,0,408},{11,0, -51},{11,0,793},{12,0,266},{15,0,158},{20,0,89},{150,0,32},{7,0,1330},{4,11,297}, -{6,11,529},{7,11,152},{7,11,713},{7,11,1845},{8,11,710},{8,11,717},{140,11,639}, -{5,0,389},{136,0,636},{134,0,1409},{4,10,562},{9,10,254},{139,10,879},{134,0,893 -},{132,10,786},{4,11,520},{135,11,575},{136,0,21},{140,0,721},{136,0,959},{7,11, -1428},{7,11,1640},{9,11,169},{9,11,182},{9,11,367},{9,11,478},{9,11,506},{9,11, -551},{9,11,648},{9,11,651},{9,11,697},{9,11,705},{9,11,725},{9,11,787},{9,11,794 -},{10,11,198},{10,11,214},{10,11,267},{10,11,275},{10,11,456},{10,11,551},{10,11 -,561},{10,11,613},{10,11,627},{10,11,668},{10,11,675},{10,11,691},{10,11,695},{ -10,11,707},{10,11,715},{11,11,183},{11,11,201},{11,11,244},{11,11,262},{11,11, -352},{11,11,439},{11,11,493},{11,11,572},{11,11,591},{11,11,608},{11,11,611},{11 -,11,646},{11,11,674},{11,11,711},{11,11,751},{11,11,761},{11,11,776},{11,11,785} -,{11,11,850},{11,11,853},{11,11,862},{11,11,865},{11,11,868},{11,11,898},{11,11, -902},{11,11,903},{11,11,910},{11,11,932},{11,11,942},{11,11,957},{11,11,967},{11 -,11,972},{12,11,148},{12,11,195},{12,11,220},{12,11,237},{12,11,318},{12,11,339} -,{12,11,393},{12,11,445},{12,11,450},{12,11,474},{12,11,509},{12,11,533},{12,11, -591},{12,11,594},{12,11,597},{12,11,621},{12,11,633},{12,11,642},{13,11,59},{13, -11,60},{13,11,145},{13,11,239},{13,11,250},{13,11,273},{13,11,329},{13,11,344},{ -13,11,365},{13,11,372},{13,11,387},{13,11,403},{13,11,414},{13,11,456},{13,11, -478},{13,11,483},{13,11,489},{14,11,55},{14,11,57},{14,11,81},{14,11,90},{14,11, -148},{14,11,239},{14,11,266},{14,11,321},{14,11,326},{14,11,327},{14,11,330},{14 -,11,347},{14,11,355},{14,11,401},{14,11,411},{14,11,414},{14,11,416},{14,11,420} -,{15,11,61},{15,11,74},{15,11,87},{15,11,88},{15,11,94},{15,11,96},{15,11,116},{ -15,11,149},{15,11,154},{16,11,50},{16,11,63},{16,11,73},{17,11,2},{17,11,66},{17 -,11,92},{17,11,103},{17,11,112},{18,11,50},{18,11,54},{18,11,82},{18,11,86},{18, -11,90},{18,11,111},{18,11,115},{18,11,156},{19,11,40},{19,11,79},{20,11,78},{149 -,11,22},{137,11,170},{134,0,1433},{135,11,1307},{139,11,411},{5,0,189},{7,0,442} -,{7,0,443},{8,0,281},{12,0,174},{141,0,261},{6,10,216},{7,10,901},{7,10,1343},{ -136,10,493},{5,11,397},{6,11,154},{7,10,341},{7,11,676},{8,11,443},{8,11,609},{9 -,11,24},{9,11,325},{10,11,35},{11,10,219},{11,11,535},{11,11,672},{11,11,1018},{ -12,11,637},{144,11,30},{6,0,2},{7,0,191},{7,0,446},{7,0,1262},{7,0,1737},{8,0,22 -},{8,0,270},{8,0,612},{9,0,4},{9,0,312},{9,0,436},{9,0,626},{10,0,216},{10,0,311 -},{10,0,521},{10,0,623},{11,0,72},{11,0,330},{11,0,455},{12,0,321},{12,0,504},{ -12,0,530},{12,0,543},{13,0,17},{13,0,156},{13,0,334},{14,0,131},{17,0,60},{148,0 -,64},{7,0,354},{10,0,410},{139,0,815},{139,10,130},{7,10,1734},{137,11,631},{12, -0,425},{15,0,112},{10,10,115},{11,10,420},{13,10,404},{14,10,346},{143,10,54},{6 -,0,60},{6,0,166},{7,0,374},{7,0,670},{7,0,1327},{8,0,411},{8,0,435},{9,0,653},{9 -,0,740},{10,0,385},{11,0,222},{11,0,324},{11,0,829},{140,0,611},{7,0,1611},{13,0 -,14},{15,0,44},{19,0,13},{148,0,76},{133,11,981},{4,11,56},{7,11,1791},{8,11,607 -},{8,11,651},{11,11,465},{11,11,835},{12,11,337},{141,11,480},{6,0,1478},{5,10, -1011},{136,10,701},{139,0,596},{5,0,206},{134,0,398},{4,10,54},{5,10,666},{7,10, -1039},{7,10,1130},{9,10,195},{138,10,302},{7,0,50},{9,11,158},{138,11,411},{135, -11,1120},{6,0,517},{7,0,1159},{10,0,621},{11,0,192},{134,10,1669},{4,0,592},{6,0 -,600},{135,0,1653},{10,0,223},{139,0,645},{136,11,139},{7,0,64},{136,0,245},{142 -,0,278},{6,11,622},{135,11,1030},{136,0,604},{134,0,1502},{138,0,265},{141,11, -168},{7,0,1763},{140,0,310},{7,10,798},{139,11,719},{7,11,160},{10,11,624},{142, -11,279},{132,11,363},{7,10,122},{9,10,259},{10,10,84},{11,10,470},{12,10,541},{ -141,10,379},{5,0,129},{6,0,61},{135,0,947},{134,0,1356},{135,11,1191},{13,0,505} -,{141,0,506},{11,0,1000},{5,10,82},{5,10,131},{7,10,1755},{8,10,31},{9,10,168},{ -9,10,764},{139,10,869},{134,0,966},{134,10,605},{134,11,292},{5,11,177},{6,11, -616},{7,11,827},{9,11,525},{138,11,656},{135,11,1486},{138,11,31},{5,10,278},{ -137,10,68},{4,10,163},{5,10,201},{5,10,307},{5,10,310},{6,10,335},{7,10,284},{ -136,10,165},{6,0,839},{135,10,1660},{136,10,781},{6,10,33},{135,10,1244},{133,0, -637},{4,11,161},{133,11,631},{137,0,590},{7,10,1953},{136,10,720},{5,0,280},{7,0 -,1226},{138,10,203},{134,0,1386},{5,0,281},{6,0,1026},{6,10,326},{7,10,677},{137 -,10,425},{7,11,1557},{135,11,1684},{135,0,1064},{9,11,469},{9,11,709},{12,11,512 -},{14,11,65},{145,11,12},{134,0,917},{10,11,229},{11,11,73},{11,11,376},{139,11, -433},{7,0,555},{9,0,192},{13,0,30},{13,0,49},{15,0,150},{16,0,76},{20,0,52},{7, -10,1316},{7,10,1412},{7,10,1839},{9,10,589},{11,10,241},{11,10,676},{11,10,811}, -{11,10,891},{12,10,140},{12,10,346},{12,10,479},{13,10,381},{14,10,188},{146,10, -30},{149,0,15},{6,0,1882},{6,0,1883},{6,0,1897},{9,0,945},{9,0,1014},{9,0,1020}, -{12,0,823},{12,0,842},{12,0,866},{12,0,934},{15,0,242},{146,0,208},{6,0,965},{ -134,0,1499},{7,0,33},{7,0,120},{8,0,489},{9,0,319},{10,0,820},{11,0,1004},{12,0, -379},{12,0,679},{13,0,117},{13,0,412},{14,0,25},{15,0,52},{15,0,161},{16,0,47},{ -149,0,2},{6,11,558},{7,11,651},{8,11,421},{9,11,0},{138,11,34},{4,0,937},{5,0, -801},{7,0,473},{5,10,358},{7,10,1184},{10,10,662},{13,10,212},{13,10,304},{13,10 -,333},{145,10,98},{132,0,877},{6,0,693},{134,0,824},{132,0,365},{7,11,1832},{138 -,11,374},{5,0,7},{139,0,774},{4,0,734},{5,0,662},{134,0,430},{4,0,746},{135,0, -1090},{5,0,360},{8,0,237},{10,0,231},{147,0,124},{138,11,348},{6,11,6},{7,11,81} -,{7,11,771},{7,11,1731},{9,11,405},{138,11,421},{6,0,740},{137,0,822},{133,10, -946},{7,0,1485},{136,0,929},{7,10,411},{8,10,631},{9,10,323},{10,10,355},{11,10, -491},{12,10,143},{12,10,402},{13,10,73},{14,10,408},{15,10,107},{146,10,71},{135 -,10,590},{5,11,881},{133,11,885},{150,11,25},{4,0,852},{5,11,142},{134,11,546},{ -7,10,1467},{8,10,328},{10,10,544},{11,10,955},{13,10,320},{145,10,83},{9,0,17},{ -10,0,291},{11,10,511},{13,10,394},{14,10,298},{14,10,318},{146,10,103},{5,11,466 -},{11,11,571},{12,11,198},{13,11,283},{14,11,186},{15,11,21},{143,11,103},{134,0 -,1001},{4,11,185},{5,11,257},{5,11,839},{5,11,936},{7,11,171},{9,11,399},{10,11, -258},{10,11,395},{10,11,734},{11,11,1014},{12,11,23},{13,11,350},{14,11,150},{ -147,11,6},{143,0,35},{132,0,831},{5,10,835},{134,10,483},{4,0,277},{5,0,608},{6, -0,493},{7,0,457},{12,0,384},{7,11,404},{7,11,1377},{7,11,1430},{7,11,2017},{8,11 -,149},{8,11,239},{8,11,512},{8,11,793},{8,11,818},{9,11,474},{9,11,595},{10,11, -122},{10,11,565},{10,11,649},{10,11,783},{11,11,239},{11,11,295},{11,11,447},{11 -,11,528},{11,11,639},{11,11,800},{11,11,936},{12,11,25},{12,11,73},{12,11,77},{ -12,11,157},{12,11,316},{12,11,390},{12,11,391},{12,11,394},{12,11,395},{12,11, -478},{12,11,503},{12,11,592},{12,11,680},{13,11,50},{13,11,53},{13,11,132},{13, -11,198},{13,11,275},{13,11,322},{13,11,415},{14,11,71},{14,11,257},{14,11,395},{ -15,11,71},{15,11,136},{17,11,123},{18,11,93},{147,11,58},{134,0,1351},{7,0,27},{ -135,0,316},{136,11,712},{136,0,984},{133,0,552},{137,0,264},{132,0,401},{6,0,710 -},{6,0,1111},{134,0,1343},{134,0,1211},{9,0,543},{10,0,524},{11,0,108},{11,0,653 -},{12,0,524},{13,0,123},{14,0,252},{16,0,18},{19,0,38},{20,0,26},{20,0,65},{21,0 -,3},{151,0,11},{4,0,205},{5,0,623},{7,0,104},{8,0,519},{137,0,716},{132,10,677}, -{4,11,377},{152,11,13},{135,11,1673},{7,0,579},{9,0,41},{9,0,244},{9,0,669},{10, -0,5},{11,0,861},{11,0,951},{139,0,980},{132,0,717},{136,0,1011},{132,0,805},{4, -11,180},{135,11,1906},{132,10,777},{132,10,331},{132,0,489},{6,0,1024},{4,11,491 -},{133,10,747},{135,11,1182},{4,11,171},{138,11,234},{4,11,586},{7,11,1186},{138 -,11,631},{135,0,892},{135,11,336},{9,11,931},{10,11,334},{148,11,71},{137,0,473} -,{6,0,864},{12,0,659},{139,11,926},{7,0,819},{9,0,26},{9,0,392},{10,0,152},{10,0 -,226},{11,0,19},{12,0,276},{12,0,426},{12,0,589},{13,0,460},{15,0,97},{19,0,48}, -{148,0,104},{135,0,51},{133,10,326},{4,10,691},{146,10,16},{9,0,130},{11,0,765}, -{10,10,680},{10,10,793},{141,10,357},{133,11,765},{8,0,229},{6,10,32},{7,10,385} -,{7,10,757},{7,10,1916},{8,10,94},{8,10,711},{9,10,541},{10,10,162},{10,10,795}, -{11,10,989},{11,10,1010},{12,10,14},{142,10,308},{7,11,474},{137,11,578},{132,0, -674},{132,0,770},{5,0,79},{7,0,1027},{7,0,1477},{139,0,52},{133,11,424},{134,0, -1666},{6,0,409},{6,10,349},{6,10,1682},{7,10,1252},{8,10,112},{8,11,714},{9,10, -435},{9,10,668},{10,10,290},{10,10,319},{10,10,815},{11,10,180},{11,10,837},{12, -10,240},{13,10,152},{13,10,219},{142,10,158},{5,0,789},{134,0,195},{4,0,251},{4, -0,688},{7,0,513},{135,0,1284},{132,10,581},{9,11,420},{10,11,269},{10,11,285},{ -10,11,576},{11,11,397},{13,11,175},{145,11,90},{6,10,126},{7,10,573},{8,10,397}, -{142,10,44},{132,11,429},{133,0,889},{4,0,160},{5,0,330},{7,0,1434},{136,0,174}, -{7,11,18},{7,11,699},{7,11,1966},{8,11,752},{9,11,273},{9,11,412},{9,11,703},{10 -,11,71},{10,11,427},{10,11,508},{146,11,97},{6,0,872},{134,0,899},{133,10,926},{ -134,0,1126},{134,0,918},{4,11,53},{5,11,186},{135,11,752},{7,0,268},{136,0,569}, -{134,0,1224},{6,0,1361},{7,10,1232},{137,10,531},{8,11,575},{10,11,289},{139,11, -319},{133,10,670},{132,11,675},{133,0,374},{135,10,1957},{133,0,731},{11,0,190}, -{15,0,49},{11,11,190},{143,11,49},{4,0,626},{5,0,506},{5,0,642},{6,0,425},{10,0, -202},{139,0,141},{137,0,444},{7,10,242},{135,10,1942},{6,11,209},{8,11,468},{9, -11,210},{11,11,36},{12,11,28},{12,11,630},{13,11,21},{13,11,349},{14,11,7},{145, -11,13},{4,11,342},{135,11,1179},{5,10,834},{7,10,1202},{8,10,14},{9,10,481},{137 -,10,880},{4,11,928},{133,11,910},{4,11,318},{4,11,496},{7,11,856},{139,11,654},{ -136,0,835},{7,0,1526},{138,10,465},{151,0,17},{135,0,477},{4,10,357},{6,10,172}, -{7,10,143},{137,10,413},{6,0,1374},{138,0,994},{18,0,76},{132,10,590},{7,0,287}, -{8,0,355},{9,0,293},{137,0,743},{134,0,1389},{7,11,915},{8,11,247},{147,11,0},{4 -,11,202},{5,11,382},{6,11,454},{7,11,936},{7,11,1803},{8,11,758},{9,11,375},{9, -11,895},{10,11,743},{10,11,792},{11,11,978},{11,11,1012},{142,11,109},{5,0,384}, -{8,0,455},{140,0,48},{132,11,390},{5,10,169},{7,10,333},{136,10,45},{5,0,264},{ -134,0,184},{138,11,791},{133,11,717},{132,10,198},{6,11,445},{7,11,332},{137,11, -909},{136,0,1001},{4,10,24},{5,10,140},{5,10,185},{7,10,1500},{11,10,565},{139, -10,838},{134,11,578},{5,0,633},{6,0,28},{135,0,1323},{132,0,851},{136,11,267},{7 -,0,359},{8,0,243},{140,0,175},{4,10,334},{133,10,593},{141,11,87},{136,11,766},{ -10,0,287},{12,0,138},{10,11,287},{140,11,138},{4,0,105},{132,0,740},{140,10,116} -,{134,0,857},{135,11,1841},{6,0,1402},{137,0,819},{132,11,584},{132,10,709},{133 -,10,897},{5,0,224},{13,0,174},{146,0,52},{135,10,1840},{4,10,608},{133,10,497},{ -139,11,60},{4,0,758},{135,0,1649},{4,11,226},{4,11,326},{135,11,1770},{5,11,426} -,{8,11,30},{9,11,2},{11,11,549},{147,11,122},{135,10,2039},{6,10,540},{136,10, -136},{4,0,573},{8,0,655},{4,10,897},{133,10,786},{7,0,351},{139,0,128},{133,10, -999},{4,10,299},{135,10,1004},{133,0,918},{132,11,345},{4,11,385},{7,11,265},{ -135,11,587},{133,10,456},{136,10,180},{6,0,687},{134,0,1537},{4,11,347},{5,11, -423},{5,11,996},{135,11,1329},{132,10,755},{7,11,1259},{9,11,125},{11,11,65},{ -140,11,285},{5,11,136},{6,11,136},{136,11,644},{134,0,1525},{4,0,1009},{135,0, -1139},{139,10,338},{132,0,340},{135,10,1464},{8,0,847},{10,0,861},{10,0,876},{10 -,0,889},{10,0,922},{10,0,929},{10,0,933},{12,0,784},{140,0,791},{139,0,176},{9, -11,134},{10,11,2},{10,11,27},{10,11,333},{11,11,722},{143,11,1},{4,11,433},{133, -11,719},{5,0,985},{7,0,509},{7,0,529},{145,0,96},{132,0,615},{4,10,890},{5,10, -805},{5,10,819},{5,10,961},{6,10,396},{6,10,1631},{6,10,1678},{7,10,1967},{7,10, -2041},{9,10,630},{11,10,8},{11,10,1019},{12,10,176},{13,10,225},{14,10,292},{149 -,10,24},{135,0,1919},{134,0,1131},{144,11,21},{144,11,51},{135,10,1815},{4,0,247 -},{7,10,1505},{10,10,190},{10,10,634},{11,10,792},{12,10,358},{140,10,447},{5,10 -,0},{6,10,536},{7,10,604},{13,10,445},{145,10,126},{4,0,184},{5,0,390},{6,0,337} -,{7,0,23},{7,0,494},{7,0,618},{7,0,1456},{8,0,27},{8,0,599},{10,0,153},{139,0, -710},{6,10,232},{6,10,412},{7,10,1074},{8,10,9},{8,10,157},{8,10,786},{9,10,196} -,{9,10,352},{9,10,457},{10,10,337},{11,10,232},{11,10,877},{12,10,480},{140,10, -546},{13,0,38},{135,10,958},{4,10,382},{136,10,579},{4,10,212},{135,10,1206},{4, -11,555},{8,11,536},{138,11,288},{11,11,139},{139,11,171},{9,11,370},{138,11,90}, -{132,0,1015},{134,0,1088},{5,10,655},{135,11,977},{134,0,1585},{17,10,67},{147, -10,74},{10,0,227},{11,0,497},{11,0,709},{140,0,415},{6,0,360},{7,0,1664},{136,0, -478},{7,0,95},{6,10,231},{136,10,423},{140,11,65},{4,11,257},{135,11,2031},{135, -11,1768},{133,10,300},{139,11,211},{136,0,699},{6,10,237},{7,10,611},{8,10,100}, -{9,10,416},{11,10,335},{12,10,173},{146,10,101},{14,0,26},{146,0,150},{6,0,581}, -{135,0,1119},{135,10,1208},{132,0,739},{6,11,83},{6,11,1733},{135,11,1389},{137, -0,869},{4,0,67},{5,0,422},{7,0,1037},{7,0,1289},{7,0,1555},{9,0,741},{145,0,108} -,{133,10,199},{12,10,427},{146,10,38},{136,0,464},{142,0,42},{10,0,96},{8,11,501 -},{137,11,696},{134,11,592},{4,0,512},{4,0,966},{5,0,342},{6,0,1855},{8,0,869},{ -8,0,875},{8,0,901},{144,0,26},{8,0,203},{11,0,823},{11,0,846},{12,0,482},{13,0, -277},{13,0,302},{13,0,464},{14,0,205},{142,0,221},{4,0,449},{133,0,718},{7,11, -1718},{9,11,95},{9,11,274},{10,11,279},{10,11,317},{10,11,420},{11,11,303},{11, -11,808},{12,11,134},{12,11,367},{13,11,149},{13,11,347},{14,11,349},{14,11,406}, -{18,11,22},{18,11,89},{18,11,122},{147,11,47},{133,11,26},{4,0,355},{6,0,311},{9 -,0,256},{138,0,404},{132,11,550},{10,0,758},{6,10,312},{6,10,1715},{10,10,584},{ -11,10,546},{11,10,692},{12,10,259},{12,10,295},{13,10,46},{141,10,154},{136,11, -822},{5,0,827},{4,11,902},{5,11,809},{6,11,122},{135,11,896},{5,0,64},{140,0,581 -},{4,0,442},{6,0,739},{7,0,1047},{7,0,1352},{7,0,1643},{7,11,1911},{9,11,449},{ -10,11,192},{138,11,740},{135,11,262},{132,10,588},{133,11,620},{5,0,977},{6,0, -288},{7,0,528},{4,11,34},{5,11,574},{7,11,279},{7,11,1624},{136,11,601},{6,0, -1375},{4,10,231},{5,10,61},{6,10,104},{7,10,729},{7,10,964},{7,10,1658},{140,10, -414},{6,10,263},{138,10,757},{132,10,320},{4,0,254},{7,0,1309},{5,11,332},{135, -11,1309},{6,11,261},{8,11,182},{139,11,943},{132,10,225},{6,0,12},{135,0,1219},{ -4,0,275},{12,0,376},{6,11,1721},{141,11,490},{4,11,933},{133,11,880},{6,0,951},{ -6,0,1109},{6,0,1181},{7,0,154},{4,10,405},{7,10,817},{14,10,58},{17,10,37},{146, -10,124},{6,0,1520},{133,10,974},{134,0,1753},{6,0,369},{6,0,502},{7,0,1036},{8,0 -,348},{9,0,452},{10,0,26},{11,0,224},{11,0,387},{11,0,772},{12,0,95},{12,0,629}, -{13,0,195},{13,0,207},{13,0,241},{14,0,260},{14,0,270},{143,0,140},{132,0,269},{ -5,0,480},{7,0,532},{7,0,1197},{7,0,1358},{8,0,291},{11,0,349},{142,0,396},{5,10, -235},{7,10,1239},{11,10,131},{140,10,370},{7,10,956},{7,10,1157},{7,10,1506},{7, -10,1606},{7,10,1615},{7,10,1619},{7,10,1736},{7,10,1775},{8,10,590},{9,10,324},{ -9,10,736},{9,10,774},{9,10,776},{9,10,784},{10,10,567},{10,10,708},{11,10,518},{ -11,10,613},{11,10,695},{11,10,716},{11,10,739},{11,10,770},{11,10,771},{11,10, -848},{11,10,857},{11,10,931},{11,10,947},{12,10,326},{12,10,387},{12,10,484},{12 -,10,528},{12,10,552},{12,10,613},{13,10,189},{13,10,256},{13,10,340},{13,10,432} -,{13,10,436},{13,10,440},{13,10,454},{14,10,174},{14,10,220},{14,10,284},{14,10, -390},{145,10,121},{8,11,598},{9,11,664},{138,11,441},{9,10,137},{138,10,221},{ -133,11,812},{148,0,15},{134,0,1341},{6,0,1017},{4,11,137},{7,11,1178},{135,11, -1520},{7,10,390},{138,10,140},{7,11,1260},{135,11,1790},{137,11,191},{135,10, -1144},{6,0,1810},{7,0,657},{8,0,886},{10,0,857},{14,0,440},{144,0,96},{8,0,533}, -{6,11,1661},{7,11,1975},{7,11,2009},{135,11,2011},{6,0,1453},{134,10,464},{132, -11,715},{5,10,407},{11,10,204},{11,10,243},{11,10,489},{12,10,293},{19,10,37},{ -20,10,73},{150,10,38},{133,11,703},{4,0,211},{7,0,1483},{5,10,325},{8,10,5},{8, -10,227},{9,10,105},{10,10,585},{140,10,614},{4,0,332},{5,0,335},{6,0,238},{7,0, -269},{7,0,811},{7,0,1797},{8,0,836},{9,0,507},{141,0,242},{5,11,89},{7,11,1915}, -{9,11,185},{9,11,235},{9,11,496},{10,11,64},{10,11,270},{10,11,403},{10,11,469}, -{10,11,529},{10,11,590},{11,11,140},{11,11,860},{13,11,1},{13,11,422},{14,11,341 -},{14,11,364},{17,11,93},{18,11,113},{19,11,97},{147,11,113},{133,11,695},{16,0, -19},{5,11,6},{6,11,183},{6,10,621},{7,11,680},{7,11,978},{7,11,1013},{7,11,1055} -,{12,11,230},{13,11,172},{13,10,504},{146,11,29},{136,0,156},{133,0,1009},{6,11, -29},{139,11,63},{134,0,820},{134,10,218},{7,10,454},{7,10,782},{8,10,768},{140, -10,686},{5,0,228},{6,0,203},{7,0,156},{8,0,347},{9,0,265},{18,0,39},{20,0,54},{ -21,0,31},{22,0,3},{23,0,0},{15,11,8},{18,11,39},{20,11,54},{21,11,31},{22,11,3}, -{151,11,0},{7,0,1131},{135,0,1468},{144,10,0},{134,0,1276},{10,10,676},{140,10, -462},{132,11,311},{134,11,1740},{7,11,170},{8,11,90},{8,11,177},{8,11,415},{11, -11,714},{142,11,281},{134,10,164},{6,0,1792},{138,0,849},{150,10,50},{5,0,291},{ -5,0,318},{7,0,765},{9,0,389},{12,0,548},{8,11,522},{142,11,328},{11,11,91},{13, -11,129},{15,11,101},{145,11,125},{4,11,494},{6,11,74},{7,11,44},{7,11,407},{8,11 -,551},{12,11,17},{15,11,5},{148,11,11},{4,11,276},{133,11,296},{6,10,343},{7,10, -195},{7,11,1777},{9,10,226},{10,10,197},{10,10,575},{11,10,502},{139,10,899},{10 -,0,525},{139,0,82},{14,0,453},{4,11,7},{5,11,90},{5,11,158},{6,11,542},{7,11,221 -},{7,11,1574},{9,11,490},{10,11,540},{11,11,443},{139,11,757},{135,0,666},{22,10 -,29},{150,11,29},{4,0,422},{147,10,8},{5,0,355},{145,0,0},{6,0,1873},{9,0,918},{ -7,11,588},{9,11,175},{138,11,530},{143,11,31},{11,0,165},{7,10,1125},{9,10,143}, -{14,10,405},{150,10,21},{9,0,260},{137,0,905},{5,11,872},{6,11,57},{6,11,479},{6 -,11,562},{7,11,471},{7,11,1060},{9,11,447},{9,11,454},{141,11,6},{138,11,704},{ -133,0,865},{5,0,914},{134,0,1625},{133,0,234},{7,0,1383},{5,11,31},{6,11,614},{ -145,11,61},{7,11,1200},{138,11,460},{6,11,424},{135,11,1866},{136,0,306},{5,10, -959},{12,11,30},{13,11,148},{14,11,87},{14,11,182},{16,11,42},{18,11,92},{148,11 -,70},{6,0,1919},{6,0,1921},{9,0,923},{9,0,930},{9,0,941},{9,0,949},{9,0,987},{9, -0,988},{9,0,992},{12,0,802},{12,0,815},{12,0,856},{12,0,885},{12,0,893},{12,0, -898},{12,0,919},{12,0,920},{12,0,941},{12,0,947},{15,0,183},{15,0,185},{15,0,189 -},{15,0,197},{15,0,202},{15,0,233},{18,0,218},{18,0,219},{18,0,233},{143,11,156} -,{135,10,1759},{136,10,173},{13,0,163},{13,0,180},{18,0,78},{20,0,35},{5,11,13}, -{134,11,142},{134,10,266},{6,11,97},{7,11,116},{8,11,322},{8,11,755},{9,11,548}, -{10,11,714},{11,11,884},{141,11,324},{135,0,1312},{9,0,814},{137,11,676},{133,0, -707},{135,0,1493},{6,0,421},{7,0,61},{7,0,1540},{10,0,11},{138,0,501},{12,0,733} -,{12,0,766},{7,11,866},{135,11,1163},{137,0,341},{142,0,98},{145,11,115},{135,11 -,1111},{136,10,300},{136,0,1014},{8,11,1},{9,11,112},{138,11,326},{132,11,730},{ -5,11,488},{6,11,527},{7,11,489},{7,11,1636},{8,11,121},{8,11,144},{8,11,359},{9, -11,193},{9,11,241},{9,11,336},{9,11,882},{11,11,266},{11,11,372},{11,11,944},{12 -,11,401},{140,11,641},{6,0,971},{134,0,1121},{6,0,102},{7,0,72},{15,0,142},{147, -0,67},{151,0,30},{135,0,823},{134,0,1045},{5,10,427},{5,10,734},{7,10,478},{136, -10,52},{7,0,1930},{11,10,217},{142,10,165},{6,0,1512},{135,0,1870},{9,11,31},{10 -,11,244},{10,11,699},{12,11,149},{141,11,497},{133,11,377},{145,11,101},{10,11, -158},{13,11,13},{13,11,137},{13,11,258},{14,11,111},{14,11,225},{14,11,253},{14, -11,304},{14,11,339},{14,11,417},{146,11,33},{6,0,87},{6,10,1734},{7,10,20},{7,10 -,1056},{8,10,732},{9,10,406},{9,10,911},{138,10,694},{134,0,1243},{137,0,245},{7 -,0,68},{8,0,48},{8,0,88},{8,0,582},{8,0,681},{9,0,373},{9,0,864},{11,0,157},{11, -0,336},{11,0,843},{148,0,27},{8,11,663},{144,11,8},{133,10,613},{4,0,88},{5,0, -137},{5,0,174},{5,0,777},{6,0,1664},{6,0,1725},{7,0,77},{7,0,426},{7,0,1317},{7, -0,1355},{8,0,126},{8,0,563},{9,0,523},{9,0,750},{10,0,310},{10,0,836},{11,0,42}, -{11,0,318},{11,0,731},{12,0,68},{12,0,92},{12,0,507},{12,0,692},{13,0,81},{13,0, -238},{13,0,374},{14,0,436},{18,0,138},{19,0,78},{19,0,111},{20,0,55},{20,0,77},{ -148,0,92},{141,0,418},{4,0,938},{137,0,625},{138,0,351},{5,11,843},{7,10,32},{7, -10,984},{8,10,85},{8,10,709},{9,10,579},{9,10,847},{9,10,856},{10,10,799},{11,10 -,258},{11,10,1007},{12,10,331},{12,10,615},{13,10,188},{13,10,435},{14,10,8},{15 -,10,165},{16,10,27},{148,10,40},{6,0,1668},{7,0,1499},{8,0,117},{9,0,314},{138,0 -,174},{135,0,707},{132,11,554},{133,11,536},{5,0,403},{5,11,207},{9,11,79},{11, -11,625},{145,11,7},{132,11,424},{136,11,785},{4,10,167},{135,10,82},{9,0,7},{23, -0,6},{9,11,7},{151,11,6},{6,0,282},{5,10,62},{6,10,534},{7,10,74},{7,10,678},{7, -10,684},{7,10,1043},{7,10,1072},{8,10,280},{8,10,541},{8,10,686},{9,10,258},{10, -10,519},{11,10,252},{140,10,282},{138,10,33},{132,10,359},{4,0,44},{5,0,311},{6, -0,156},{7,0,639},{7,0,762},{7,0,1827},{9,0,8},{9,0,462},{148,0,83},{7,11,769},{9 -,11,18},{138,11,358},{4,0,346},{7,0,115},{9,0,180},{9,0,456},{10,0,363},{4,11, -896},{134,11,1777},{133,10,211},{7,0,761},{7,0,1051},{137,0,545},{6,10,145},{141 -,10,336},{7,11,750},{9,11,223},{11,11,27},{11,11,466},{12,11,624},{14,11,265},{ -146,11,61},{6,0,752},{6,0,768},{6,0,1195},{6,0,1254},{6,0,1619},{137,0,835},{6,0 -,1936},{8,0,930},{136,0,960},{132,10,263},{132,11,249},{12,0,653},{132,10,916},{ -4,11,603},{133,11,661},{8,0,344},{4,11,11},{6,11,128},{7,11,231},{7,11,1533},{ -138,11,725},{134,0,1483},{134,0,875},{6,0,185},{7,0,1899},{9,0,875},{139,0,673}, -{15,10,155},{144,10,79},{7,0,93},{7,0,210},{7,0,1223},{8,0,451},{8,0,460},{11,0, -353},{11,0,475},{4,10,599},{6,10,1634},{7,10,67},{7,10,691},{7,10,979},{7,10, -1697},{8,10,207},{8,10,214},{8,10,231},{8,10,294},{8,10,336},{8,10,428},{8,10, -471},{8,10,622},{8,10,626},{8,10,679},{8,10,759},{8,10,829},{9,10,11},{9,10,246} -,{9,10,484},{9,10,573},{9,10,706},{9,10,762},{9,10,798},{9,10,855},{9,10,870},{9 -,10,912},{10,10,303},{10,10,335},{10,10,424},{10,10,461},{10,10,543},{10,10,759} -,{10,10,814},{11,10,59},{11,10,235},{11,10,590},{11,10,929},{11,10,963},{11,10, -987},{12,10,114},{12,10,182},{12,10,226},{12,10,332},{12,10,439},{12,10,575},{12 -,10,598},{12,10,675},{13,10,8},{13,10,125},{13,10,194},{13,10,287},{14,10,197},{ -14,10,383},{15,10,53},{17,10,63},{19,10,46},{19,10,98},{19,10,106},{148,10,85},{ -132,11,476},{4,0,327},{5,0,478},{7,0,1332},{136,0,753},{5,0,1020},{133,0,1022},{ -135,11,1807},{4,0,103},{133,0,401},{4,0,499},{135,0,1421},{10,0,207},{13,0,164}, -{147,10,126},{9,11,20},{10,11,324},{139,11,488},{132,0,96},{9,11,280},{138,11, -134},{135,0,968},{133,10,187},{135,10,1286},{5,11,112},{6,11,103},{134,11,150},{ -8,0,914},{10,0,3},{4,10,215},{9,10,38},{11,10,23},{11,10,127},{139,10,796},{135, -0,399},{6,0,563},{137,0,224},{6,0,704},{134,0,1214},{4,11,708},{8,11,15},{9,11, -50},{9,11,386},{11,11,18},{11,11,529},{140,11,228},{4,11,563},{7,11,109},{7,11, -592},{7,11,637},{7,11,770},{7,11,1701},{8,11,436},{8,11,463},{9,11,60},{9,11,335 -},{9,11,904},{10,11,73},{11,11,434},{12,11,585},{13,11,331},{18,11,110},{148,11, -60},{134,0,1559},{132,11,502},{6,11,347},{138,11,161},{4,11,33},{5,11,102},{5,11 -,500},{6,11,284},{7,11,1079},{7,11,1423},{7,11,1702},{8,11,470},{9,11,554},{9,11 -,723},{139,11,333},{7,11,246},{135,11,840},{6,11,10},{8,11,571},{9,11,739},{143, -11,91},{8,0,861},{10,0,905},{12,0,730},{12,0,789},{133,11,626},{134,0,946},{5,0, -746},{12,0,333},{14,0,332},{12,11,333},{142,11,332},{5,11,18},{6,11,526},{13,11, -24},{13,11,110},{19,11,5},{147,11,44},{4,0,910},{5,0,832},{135,10,2002},{10,11, -768},{139,11,787},{4,11,309},{5,11,462},{7,11,970},{135,11,1097},{4,10,28},{5,10 -,440},{7,10,248},{11,10,833},{140,10,344},{134,10,1654},{6,0,632},{6,0,652},{6,0 -,1272},{6,0,1384},{134,0,1560},{134,11,1704},{6,0,1393},{133,10,853},{6,10,249}, -{7,10,1234},{139,10,573},{5,11,86},{7,11,743},{9,11,85},{10,11,281},{10,11,432}, -{11,11,490},{12,11,251},{13,11,118},{14,11,378},{146,11,143},{5,11,524},{133,11, -744},{134,0,1514},{10,0,201},{142,0,319},{7,0,717},{10,0,510},{7,10,392},{8,10, -20},{8,10,172},{8,10,690},{9,10,383},{9,10,845},{11,10,293},{11,10,832},{11,10, -920},{11,10,984},{141,10,221},{134,0,1381},{5,10,858},{133,10,992},{8,0,528},{ -137,0,348},{10,11,107},{140,11,436},{4,0,20},{133,0,616},{134,0,1251},{132,11, -927},{10,11,123},{12,11,670},{13,11,371},{14,11,142},{146,11,94},{134,0,1163},{7 -,11,1149},{137,11,156},{134,0,307},{133,11,778},{7,0,1091},{135,0,1765},{5,11, -502},{6,10,268},{137,10,62},{8,11,196},{10,11,283},{139,11,406},{4,0,26},{5,0, -429},{6,0,245},{7,0,704},{7,0,1379},{135,0,1474},{133,11,855},{132,0,881},{4,0, -621},{135,11,1596},{7,11,1400},{9,11,446},{138,11,45},{6,0,736},{138,10,106},{ -133,0,542},{134,0,348},{133,0,868},{136,0,433},{135,0,1495},{138,0,771},{6,10, -613},{136,10,223},{138,0,215},{141,0,124},{136,11,391},{135,11,172},{132,10,670} -,{140,0,55},{9,10,40},{139,10,136},{7,0,62},{147,0,112},{132,0,856},{132,11,568} -,{12,0,270},{139,10,259},{8,0,572},{137,0,698},{4,11,732},{9,10,310},{137,10,682 -},{142,10,296},{134,0,939},{136,11,733},{135,11,1435},{7,10,1401},{135,10,1476}, -{6,0,352},{4,10,296},{7,10,401},{7,10,1410},{7,10,1594},{7,10,1674},{8,10,63},{8 -,10,660},{137,10,74},{4,11,428},{133,11,668},{4,10,139},{4,10,388},{140,10,188}, -{7,11,2015},{140,11,665},{132,0,647},{146,0,10},{138,0,220},{142,0,464},{132,0, -109},{134,0,1746},{6,0,515},{4,10,747},{6,11,1623},{6,11,1681},{7,10,649},{7,10, -1479},{135,10,1583},{133,10,232},{135,0,566},{137,10,887},{4,0,40},{10,0,67},{11 -,0,117},{11,0,768},{139,0,935},{132,0,801},{7,0,992},{8,0,301},{9,0,722},{12,0, -63},{13,0,29},{14,0,161},{143,0,18},{139,0,923},{6,11,1748},{8,11,715},{9,11,802 -},{10,11,46},{10,11,819},{13,11,308},{14,11,351},{14,11,363},{146,11,67},{137,11 -,745},{7,0,1145},{4,10,14},{7,10,1801},{10,10,748},{141,10,458},{4,11,63},{5,11, -347},{134,11,474},{135,0,568},{4,10,425},{7,11,577},{7,11,1432},{9,11,475},{9,11 -,505},{9,11,526},{9,11,609},{9,11,689},{9,11,726},{9,11,735},{9,11,738},{10,11, -556},{10,11,674},{10,11,684},{11,11,89},{11,11,202},{11,11,272},{11,11,380},{11, -11,415},{11,11,505},{11,11,537},{11,11,550},{11,11,562},{11,11,640},{11,11,667}, -{11,11,688},{11,11,847},{11,11,927},{11,11,930},{11,11,940},{12,11,144},{12,11, -325},{12,11,329},{12,11,389},{12,11,403},{12,11,451},{12,11,515},{12,11,604},{12 -,11,616},{12,11,626},{13,11,66},{13,11,131},{13,11,167},{13,11,236},{13,11,368}, -{13,11,411},{13,11,434},{13,11,453},{13,11,461},{13,11,474},{14,11,59},{14,11,60 -},{14,11,139},{14,11,152},{14,11,276},{14,11,353},{14,11,402},{15,11,28},{15,11, -81},{15,11,123},{15,11,152},{18,11,136},{148,11,88},{137,0,247},{135,11,1622},{9 -,11,544},{11,11,413},{144,11,25},{4,0,645},{7,0,825},{6,10,1768},{135,11,89},{ -140,0,328},{5,10,943},{134,10,1779},{134,0,1363},{5,10,245},{6,10,576},{7,10,582 -},{136,10,225},{134,0,1280},{5,11,824},{133,11,941},{7,11,440},{8,11,230},{139, -11,106},{5,0,28},{6,0,204},{10,0,320},{10,0,583},{13,0,502},{14,0,72},{14,0,274} -,{14,0,312},{14,0,344},{15,0,159},{16,0,62},{16,0,69},{17,0,30},{18,0,42},{18,0, -53},{18,0,84},{18,0,140},{19,0,68},{19,0,85},{20,0,5},{20,0,45},{20,0,101},{22,0 -,7},{150,0,20},{4,0,558},{6,0,390},{7,0,162},{7,0,689},{9,0,360},{138,0,653},{ -134,0,764},{6,0,862},{137,0,833},{5,0,856},{6,0,1672},{6,0,1757},{134,0,1781},{5 -,0,92},{10,0,736},{140,0,102},{6,0,1927},{6,0,1944},{8,0,924},{8,0,948},{10,0, -967},{138,0,978},{134,0,1479},{5,0,590},{8,0,360},{9,0,213},{138,0,63},{134,0, -1521},{6,0,709},{134,0,891},{132,10,443},{13,0,477},{14,0,120},{148,0,61},{4,11, -914},{5,11,800},{133,11,852},{10,11,54},{141,11,115},{4,11,918},{133,11,876},{ -139,11,152},{4,11,92},{133,11,274},{135,11,1901},{9,11,800},{10,11,693},{11,11, -482},{11,11,734},{139,11,789},{9,0,483},{132,10,298},{6,0,1213},{141,11,498},{ -135,11,1451},{133,11,743},{4,0,1022},{10,0,1000},{12,0,957},{12,0,980},{12,0, -1013},{14,0,481},{144,0,116},{8,0,503},{17,0,29},{4,11,49},{7,11,280},{135,11, -1633},{135,0,1712},{134,0,466},{136,11,47},{5,10,164},{7,10,121},{142,10,189},{7 -,10,812},{7,10,1261},{7,10,1360},{9,10,632},{140,10,352},{139,10,556},{132,0,731 -},{5,11,272},{5,11,908},{5,11,942},{7,11,1008},{7,11,1560},{8,11,197},{9,11,47}, -{11,11,538},{139,11,742},{4,10,172},{9,10,611},{10,10,436},{12,10,673},{141,10, -255},{133,10,844},{10,0,484},{11,0,754},{12,0,457},{14,0,171},{14,0,389},{146,0, -153},{9,10,263},{10,10,147},{138,10,492},{137,11,891},{138,0,241},{133,10,537},{ -6,0,2005},{136,0,964},{137,10,842},{151,11,8},{4,11,407},{132,11,560},{135,11, -1884},{6,0,1100},{134,0,1242},{135,0,954},{5,10,230},{5,10,392},{6,10,420},{9,10 -,568},{140,10,612},{4,11,475},{11,11,35},{11,11,90},{13,11,7},{13,11,71},{13,11, -177},{142,11,422},{136,11,332},{135,0,1958},{6,0,549},{8,0,34},{8,0,283},{9,0, -165},{138,0,475},{10,0,952},{12,0,966},{140,0,994},{5,0,652},{5,0,701},{135,0, -449},{4,0,655},{7,0,850},{17,0,75},{146,0,137},{4,0,146},{7,0,1618},{8,0,670},{5 -,10,41},{7,10,1459},{7,10,1469},{7,10,1859},{9,10,549},{139,10,905},{133,10,696} -,{6,0,159},{6,0,364},{7,0,516},{137,0,518},{135,0,1439},{6,11,222},{7,11,636},{7 -,11,1620},{8,11,409},{9,11,693},{139,11,77},{13,0,151},{141,11,45},{6,0,1027},{4 -,11,336},{132,10,771},{139,11,392},{10,11,121},{11,11,175},{149,11,16},{8,0,950} -,{138,0,983},{133,10,921},{135,0,993},{6,10,180},{7,10,1137},{8,10,751},{139,10, -805},{7,0,501},{9,0,111},{10,0,141},{11,0,332},{13,0,43},{13,0,429},{14,0,130},{ -14,0,415},{145,0,102},{4,10,183},{5,11,882},{7,10,271},{11,10,824},{11,10,952},{ -13,10,278},{13,10,339},{13,10,482},{14,10,424},{148,10,99},{4,10,19},{5,10,477}, -{5,10,596},{6,10,505},{7,10,1221},{11,10,907},{12,10,209},{141,10,214},{135,10, -1215},{133,0,452},{132,11,426},{5,0,149},{136,0,233},{133,0,935},{6,11,58},{7,11 -,654},{7,11,745},{7,11,1969},{8,11,240},{8,11,675},{9,11,479},{9,11,731},{10,11, -330},{10,11,593},{10,11,817},{11,11,32},{11,11,133},{11,11,221},{145,11,68},{12, -0,582},{18,0,131},{7,11,102},{137,11,538},{136,0,801},{134,10,1645},{132,0,70},{ -6,10,92},{6,10,188},{7,10,1269},{7,10,1524},{7,10,1876},{10,10,228},{139,10,1020 -},{4,10,459},{133,10,966},{138,0,369},{16,0,36},{140,10,330},{141,11,366},{7,0, -721},{10,0,236},{12,0,204},{6,10,18},{7,10,932},{8,10,757},{9,10,54},{9,10,65},{ -9,10,844},{10,10,113},{10,10,315},{10,10,798},{11,10,153},{12,10,151},{12,10,392 -},{12,10,666},{142,10,248},{7,0,241},{10,0,430},{8,10,548},{9,10,532},{10,10,117 -},{11,10,351},{11,10,375},{143,10,23},{134,10,1742},{133,10,965},{133,11,566},{6 -,11,48},{135,11,63},{134,10,182},{10,10,65},{10,10,488},{138,10,497},{6,11,114}, -{7,11,1224},{7,11,1556},{136,11,3},{134,0,1817},{8,11,576},{137,11,267},{6,0, -1078},{144,0,16},{9,10,588},{138,10,260},{138,0,1021},{5,0,406},{134,0,2022},{ -133,11,933},{6,0,69},{135,0,117},{7,0,1830},{136,11,427},{4,0,432},{135,0,824},{ -134,10,1786},{133,0,826},{139,11,67},{133,11,759},{135,10,308},{137,0,816},{133, -0,1000},{4,0,297},{6,0,529},{7,0,152},{7,0,713},{7,0,1845},{8,0,710},{8,0,717},{ -12,0,639},{140,0,685},{7,0,423},{136,10,588},{136,10,287},{136,0,510},{134,0, -1048},{6,0,618},{7,11,56},{7,11,1989},{8,11,337},{8,11,738},{9,11,600},{10,11, -483},{12,11,37},{13,11,447},{142,11,92},{4,0,520},{135,0,575},{8,0,990},{138,0, -977},{135,11,774},{9,11,347},{11,11,24},{140,11,170},{136,11,379},{140,10,290},{ -132,11,328},{4,0,321},{134,0,569},{4,11,101},{135,11,1171},{7,0,723},{7,0,1135}, -{5,11,833},{136,11,744},{7,10,719},{8,10,809},{136,10,834},{8,0,921},{136,10,796 -},{5,10,210},{6,10,213},{7,10,60},{10,10,364},{139,10,135},{5,0,397},{6,0,154},{ -7,0,676},{8,0,443},{8,0,609},{9,0,24},{9,0,325},{10,0,35},{11,0,535},{11,0,672}, -{11,0,1018},{12,0,637},{16,0,30},{5,10,607},{8,10,326},{136,10,490},{4,10,701},{ -5,10,472},{6,11,9},{6,11,397},{7,11,53},{7,11,1742},{9,10,758},{10,11,632},{11, -11,828},{140,11,146},{135,10,380},{135,10,1947},{148,11,109},{10,10,278},{138,11 -,278},{134,0,856},{7,0,139},{4,10,386},{8,10,405},{8,10,728},{9,10,497},{11,10, -110},{11,10,360},{15,10,37},{144,10,84},{141,0,282},{133,0,981},{5,0,288},{7,10, -1452},{7,10,1480},{8,10,634},{140,10,472},{7,0,1890},{8,11,367},{10,11,760},{14, -11,79},{20,11,17},{152,11,0},{4,10,524},{136,10,810},{4,0,56},{7,0,1791},{8,0, -607},{8,0,651},{11,0,465},{11,0,835},{12,0,337},{141,0,480},{10,10,238},{141,10, -33},{11,11,417},{12,11,223},{140,11,265},{9,0,158},{10,0,411},{140,0,261},{133, -10,532},{133,10,997},{12,11,186},{12,11,292},{14,11,100},{146,11,70},{6,0,1403}, -{136,0,617},{134,0,1205},{139,0,563},{4,0,242},{134,0,333},{4,11,186},{5,11,157} -,{8,11,168},{138,11,6},{132,0,369},{133,11,875},{5,10,782},{5,10,829},{134,10, -1738},{134,0,622},{135,11,1272},{6,0,1407},{7,11,111},{136,11,581},{7,10,1823},{ -139,10,693},{7,0,160},{10,0,624},{142,0,279},{132,0,363},{10,11,589},{12,11,111} -,{13,11,260},{14,11,82},{18,11,63},{147,11,45},{7,11,1364},{7,11,1907},{141,11, -158},{4,11,404},{4,11,659},{135,11,675},{13,11,211},{14,11,133},{14,11,204},{15, -11,64},{15,11,69},{15,11,114},{16,11,10},{19,11,23},{19,11,35},{19,11,39},{19,11 -,51},{19,11,71},{19,11,75},{152,11,15},{4,10,78},{5,10,96},{5,10,182},{7,10,1724 -},{7,10,1825},{10,10,394},{10,10,471},{11,10,532},{14,10,340},{145,10,88},{135, -10,1964},{133,11,391},{11,11,887},{14,11,365},{142,11,375},{5,11,540},{6,11,1697 -},{7,11,222},{136,11,341},{134,11,78},{9,0,601},{9,0,619},{10,0,505},{10,0,732}, -{11,0,355},{140,0,139},{134,0,292},{139,0,174},{5,0,177},{6,0,616},{7,0,827},{9, -0,525},{138,0,656},{10,0,31},{6,10,215},{7,10,1028},{7,10,1473},{7,10,1721},{9, -10,424},{138,10,779},{135,10,584},{136,11,293},{134,0,685},{135,11,1868},{133,11 -,460},{7,0,647},{6,10,67},{7,10,1630},{9,10,354},{9,10,675},{10,10,830},{14,10, -80},{145,10,80},{4,0,161},{133,0,631},{6,10,141},{7,10,225},{9,10,59},{9,10,607} -,{10,10,312},{11,10,687},{12,10,555},{13,10,373},{13,10,494},{148,10,58},{7,11, -965},{7,11,1460},{135,11,1604},{136,10,783},{134,11,388},{6,0,722},{6,0,1267},{4 -,11,511},{9,11,333},{9,11,379},{10,11,602},{11,11,441},{11,11,723},{11,11,976},{ -140,11,357},{134,0,1797},{135,0,1684},{9,0,469},{9,0,709},{12,0,512},{14,0,65},{ -17,0,12},{5,11,938},{136,11,707},{7,0,1230},{136,0,531},{10,0,229},{11,0,73},{11 -,0,376},{139,0,433},{12,0,268},{12,0,640},{142,0,119},{7,10,430},{139,10,46},{6, -0,558},{7,0,651},{8,0,421},{9,0,0},{10,0,34},{139,0,1008},{6,0,106},{7,0,1786},{ -7,0,1821},{9,0,102},{9,0,763},{5,10,602},{7,10,2018},{137,10,418},{5,0,65},{6,0, -416},{7,0,1720},{7,0,1924},{10,0,109},{11,0,14},{11,0,70},{11,0,569},{11,0,735}, -{15,0,153},{20,0,80},{136,10,677},{135,11,1625},{137,11,772},{136,0,595},{6,11, -469},{7,11,1709},{138,11,515},{7,0,1832},{138,0,374},{9,0,106},{9,0,163},{9,0, -296},{10,0,167},{10,0,172},{10,0,777},{139,0,16},{6,0,6},{7,0,81},{7,0,771},{7,0 -,1731},{9,0,405},{138,0,421},{4,11,500},{135,11,938},{5,11,68},{134,11,383},{5,0 -,881},{133,0,885},{6,0,854},{6,0,1132},{6,0,1495},{6,0,1526},{6,0,1533},{134,0, -1577},{4,11,337},{6,11,353},{7,11,1934},{8,11,488},{137,11,429},{7,11,236},{7,11 -,1795},{8,11,259},{9,11,135},{9,11,177},{10,11,825},{11,11,115},{11,11,370},{11, -11,405},{11,11,604},{12,11,10},{12,11,667},{12,11,669},{13,11,76},{14,11,310},{ -15,11,76},{15,11,147},{148,11,23},{5,0,142},{134,0,546},{4,11,15},{5,11,22},{6, -11,244},{7,11,40},{7,11,200},{7,11,906},{7,11,1199},{9,11,616},{10,11,716},{11, -11,635},{11,11,801},{140,11,458},{5,0,466},{11,0,571},{12,0,198},{13,0,283},{14, -0,186},{15,0,21},{15,0,103},{135,10,329},{4,0,185},{5,0,257},{5,0,839},{5,0,936} -,{9,0,399},{10,0,258},{10,0,395},{10,0,734},{11,0,1014},{12,0,23},{13,0,350},{14 -,0,150},{19,0,6},{135,11,1735},{12,11,36},{141,11,337},{5,11,598},{7,11,791},{8, -11,108},{137,11,123},{132,10,469},{7,0,404},{7,0,1377},{7,0,1430},{7,0,2017},{8, -0,149},{8,0,239},{8,0,512},{8,0,793},{8,0,818},{9,0,474},{9,0,595},{10,0,122},{ -10,0,565},{10,0,649},{10,0,783},{11,0,239},{11,0,295},{11,0,447},{11,0,528},{11, -0,639},{11,0,800},{12,0,25},{12,0,77},{12,0,157},{12,0,256},{12,0,316},{12,0,390 -},{12,0,391},{12,0,395},{12,0,478},{12,0,503},{12,0,592},{12,0,680},{13,0,50},{ -13,0,53},{13,0,132},{13,0,198},{13,0,322},{13,0,415},{13,0,511},{14,0,71},{14,0, -395},{15,0,71},{15,0,136},{17,0,123},{18,0,93},{147,0,58},{136,0,712},{134,10, -1743},{5,10,929},{6,10,340},{8,10,376},{136,10,807},{6,0,1848},{8,0,860},{10,0, -856},{10,0,859},{10,0,925},{10,0,941},{140,0,762},{6,0,629},{6,0,906},{9,0,810}, -{140,0,652},{5,10,218},{7,10,1610},{138,10,83},{7,10,1512},{135,10,1794},{4,0, -377},{24,0,13},{4,11,155},{7,11,1689},{11,10,0},{144,10,78},{4,11,164},{5,11,151 -},{5,11,730},{5,11,741},{7,11,498},{7,11,870},{7,11,1542},{12,11,213},{14,11,36} -,{14,11,391},{17,11,111},{18,11,6},{18,11,46},{18,11,151},{19,11,36},{20,11,32}, -{20,11,56},{20,11,69},{20,11,102},{21,11,4},{22,11,8},{22,11,10},{22,11,14},{150 -,11,31},{7,0,1842},{133,10,571},{4,10,455},{4,11,624},{135,11,1752},{134,0,1501} -,{4,11,492},{5,11,451},{6,10,161},{7,10,372},{137,10,597},{132,10,349},{4,0,180} -,{135,0,1906},{135,11,835},{141,11,70},{132,0,491},{137,10,751},{6,10,432},{139, -10,322},{4,0,171},{138,0,234},{6,11,113},{135,11,436},{4,0,586},{7,0,1186},{138, -0,631},{5,10,468},{10,10,325},{11,10,856},{12,10,345},{143,10,104},{5,10,223},{ -10,11,592},{10,11,753},{12,11,317},{12,11,355},{12,11,465},{12,11,469},{12,11, -560},{12,11,578},{141,11,243},{132,10,566},{135,11,520},{4,10,59},{135,10,1394}, -{6,10,436},{139,10,481},{9,0,931},{10,0,334},{20,0,71},{4,10,48},{5,10,271},{7, -10,953},{135,11,1878},{11,0,170},{5,10,610},{136,10,457},{133,10,755},{6,0,1587} -,{135,10,1217},{4,10,197},{149,11,26},{133,11,585},{137,11,521},{133,0,765},{133 -,10,217},{139,11,586},{133,0,424},{9,11,752},{12,11,610},{13,11,431},{16,11,59}, -{146,11,109},{136,0,714},{7,0,685},{132,11,307},{9,0,420},{10,0,269},{10,0,285}, -{10,0,576},{11,0,397},{13,0,175},{145,0,90},{132,0,429},{133,11,964},{9,11,463}, -{138,11,595},{7,0,18},{7,0,699},{7,0,1966},{8,0,752},{9,0,273},{9,0,412},{9,0, -703},{10,0,71},{10,0,427},{138,0,508},{4,10,165},{7,10,1398},{135,10,1829},{4,0, -53},{5,0,186},{7,0,752},{7,0,828},{142,0,116},{8,0,575},{10,0,289},{139,0,319},{ -132,0,675},{134,0,1424},{4,11,75},{5,11,180},{6,11,500},{7,11,58},{7,11,710},{ -138,11,645},{133,11,649},{6,11,276},{7,11,282},{7,11,879},{7,11,924},{8,11,459}, -{9,11,599},{9,11,754},{11,11,574},{12,11,128},{12,11,494},{13,11,52},{13,11,301} -,{15,11,30},{143,11,132},{6,0,647},{134,0,1095},{5,10,9},{7,10,297},{7,10,966},{ -140,10,306},{132,11,200},{134,0,1334},{5,10,146},{6,10,411},{138,10,721},{6,0, -209},{6,0,1141},{6,0,1288},{8,0,468},{9,0,210},{11,0,36},{12,0,28},{12,0,630},{ -13,0,21},{13,0,349},{14,0,7},{145,0,13},{6,10,177},{135,10,467},{4,0,342},{135,0 -,1179},{10,11,454},{140,11,324},{4,0,928},{133,0,910},{7,0,1838},{6,11,225},{137 -,11,211},{16,0,101},{20,0,115},{20,0,118},{148,0,122},{4,0,496},{135,0,856},{4,0 -,318},{11,0,654},{7,11,718},{139,11,102},{8,11,58},{9,11,724},{11,11,809},{13,11 -,113},{145,11,72},{5,10,200},{6,11,345},{135,11,1247},{8,11,767},{8,11,803},{9, -11,301},{137,11,903},{7,0,915},{8,0,247},{19,0,0},{7,11,1949},{136,11,674},{4,0, -202},{5,0,382},{6,0,454},{7,0,936},{7,0,1803},{8,0,758},{9,0,375},{9,0,895},{10, -0,743},{10,0,792},{11,0,978},{11,0,1012},{142,0,109},{7,0,1150},{7,0,1425},{7,0, -1453},{140,0,513},{134,11,259},{138,0,791},{11,0,821},{12,0,110},{12,0,153},{18, -0,41},{150,0,19},{134,10,481},{132,0,796},{6,0,445},{9,0,909},{136,11,254},{10,0 -,776},{13,0,345},{142,0,425},{4,10,84},{7,10,1482},{10,10,76},{138,10,142},{135, -11,742},{6,0,578},{133,10,1015},{6,0,1387},{4,10,315},{5,10,507},{135,10,1370},{ -4,0,438},{133,0,555},{136,0,766},{133,11,248},{134,10,1722},{4,11,116},{5,11,95} -,{5,11,445},{7,11,1688},{8,11,29},{9,11,272},{11,11,509},{139,11,915},{135,0,541 -},{133,11,543},{8,10,222},{8,10,476},{9,10,238},{11,10,516},{11,10,575},{15,10, -109},{146,10,100},{6,0,880},{134,0,1191},{5,11,181},{136,11,41},{134,0,1506},{ -132,11,681},{7,11,25},{8,11,202},{138,11,536},{139,0,983},{137,0,768},{132,0,584 -},{9,11,423},{140,11,89},{8,11,113},{9,11,877},{10,11,554},{11,11,83},{12,11,136 -},{147,11,109},{7,10,706},{7,10,1058},{138,10,538},{133,11,976},{4,11,206},{135, -11,746},{136,11,526},{140,0,737},{11,10,92},{11,10,196},{11,10,409},{11,10,450}, -{11,10,666},{11,10,777},{12,10,262},{13,10,385},{13,10,393},{15,10,115},{16,10, -45},{145,10,82},{4,0,226},{4,0,326},{7,0,1770},{4,11,319},{5,11,699},{138,11,673 -},{6,10,40},{135,10,1781},{5,0,426},{8,0,30},{9,0,2},{11,0,549},{147,0,122},{6,0 -,1161},{134,0,1329},{138,10,97},{6,10,423},{7,10,665},{135,10,1210},{7,11,13},{8 -,11,226},{10,11,537},{11,11,570},{11,11,605},{11,11,799},{11,11,804},{12,11,85}, -{12,11,516},{12,11,623},{13,11,112},{13,11,361},{14,11,77},{14,11,78},{17,11,28} -,{147,11,110},{132,11,769},{132,11,551},{132,11,728},{147,0,117},{9,11,57},{9,11 -,459},{10,11,425},{11,11,119},{12,11,184},{12,11,371},{13,11,358},{145,11,51},{5 -,11,188},{5,11,814},{8,11,10},{9,11,421},{9,11,729},{10,11,609},{139,11,689},{ -134,11,624},{135,11,298},{135,0,462},{4,0,345},{139,10,624},{136,10,574},{4,0, -385},{7,0,265},{135,0,587},{6,0,808},{132,11,528},{133,0,398},{132,10,354},{4,0, -347},{5,0,423},{5,0,996},{135,0,1329},{135,10,1558},{7,0,1259},{9,0,125},{139,0, -65},{5,0,136},{6,0,136},{136,0,644},{5,11,104},{6,11,173},{135,11,1631},{135,0, -469},{133,10,830},{4,0,278},{5,0,465},{135,0,1367},{7,11,810},{8,11,138},{8,11, -342},{9,11,84},{10,11,193},{11,11,883},{140,11,359},{5,10,496},{135,10,203},{4,0 -,433},{133,0,719},{6,11,95},{134,10,547},{5,10,88},{137,10,239},{6,11,406},{10, -11,409},{10,11,447},{11,11,44},{140,11,100},{134,0,1423},{7,10,650},{135,10,1310 -},{134,0,749},{135,11,1243},{135,0,1363},{6,0,381},{7,0,645},{7,0,694},{8,0,546} -,{7,10,1076},{9,10,80},{11,10,78},{11,10,421},{11,10,534},{140,10,545},{134,11, -1636},{135,11,1344},{12,0,277},{7,10,274},{11,10,479},{139,10,507},{6,0,705},{6, -0,783},{6,0,1275},{6,0,1481},{4,11,282},{7,11,1034},{11,11,398},{11,11,634},{12, -11,1},{12,11,79},{12,11,544},{14,11,237},{17,11,10},{146,11,20},{134,0,453},{4,0 -,555},{8,0,536},{10,0,288},{11,0,1005},{4,10,497},{135,10,1584},{5,11,118},{5,11 -,499},{6,11,476},{7,11,600},{7,11,888},{135,11,1096},{138,0,987},{7,0,1107},{7, -10,261},{7,10,1115},{7,10,1354},{7,10,1588},{7,10,1705},{7,10,1902},{9,10,465},{ -10,10,248},{10,10,349},{10,10,647},{11,10,527},{11,10,660},{11,10,669},{12,10, -529},{141,10,305},{7,11,296},{7,11,596},{8,11,560},{8,11,586},{9,11,612},{11,11, -100},{11,11,304},{12,11,46},{13,11,89},{14,11,112},{145,11,122},{9,0,370},{138,0 -,90},{136,10,13},{132,0,860},{7,10,642},{8,10,250},{11,10,123},{11,10,137},{13, -10,48},{142,10,95},{135,10,1429},{137,11,321},{132,0,257},{135,0,2031},{7,0,1768 -},{7,11,1599},{7,11,1723},{8,11,79},{8,11,106},{8,11,190},{8,11,302},{8,11,383}, -{9,11,119},{9,11,233},{9,11,298},{9,11,419},{9,11,471},{10,11,181},{10,11,406},{ -11,11,57},{11,11,85},{11,11,120},{11,11,177},{11,11,296},{11,11,382},{11,11,454} -,{11,11,758},{11,11,999},{12,11,27},{12,11,98},{12,11,131},{12,11,245},{12,11, -312},{12,11,446},{12,11,454},{13,11,25},{13,11,98},{13,11,426},{13,11,508},{14, -11,6},{14,11,163},{14,11,272},{14,11,277},{14,11,370},{15,11,95},{15,11,138},{15 -,11,167},{17,11,18},{17,11,38},{20,11,96},{149,11,32},{5,11,722},{134,11,1759},{ -145,11,16},{6,0,1071},{134,0,1561},{10,10,545},{140,10,301},{6,0,83},{6,0,1733}, -{135,0,1389},{4,0,835},{135,0,1818},{133,11,258},{4,10,904},{133,10,794},{134,0, -2006},{5,11,30},{7,11,495},{8,11,134},{9,11,788},{140,11,438},{135,11,2004},{137 -,0,696},{5,11,50},{6,11,439},{7,11,780},{135,11,1040},{7,11,772},{7,11,1104},{7, -11,1647},{11,11,269},{11,11,539},{11,11,607},{11,11,627},{11,11,706},{11,11,975} -,{12,11,248},{12,11,311},{12,11,434},{12,11,600},{12,11,622},{13,11,297},{13,11, -367},{13,11,485},{14,11,69},{14,11,409},{143,11,108},{5,11,1},{6,11,81},{138,11, -520},{7,0,1718},{9,0,95},{9,0,274},{10,0,279},{10,0,317},{10,0,420},{11,0,303},{ -11,0,808},{12,0,134},{12,0,367},{13,0,149},{13,0,347},{14,0,349},{14,0,406},{18, -0,22},{18,0,89},{18,0,122},{147,0,47},{5,11,482},{8,11,98},{9,11,172},{10,11,222 -},{10,11,700},{10,11,822},{11,11,302},{11,11,778},{12,11,50},{12,11,127},{12,11, -396},{13,11,62},{13,11,328},{14,11,122},{147,11,72},{7,10,386},{138,10,713},{6, -10,7},{6,10,35},{7,10,147},{7,10,1069},{7,10,1568},{7,10,1575},{7,10,1917},{8,10 -,43},{8,10,208},{9,10,128},{9,10,866},{10,10,20},{11,10,981},{147,10,33},{133,0, -26},{132,0,550},{5,11,2},{7,11,1494},{136,11,589},{6,11,512},{7,11,797},{8,11, -253},{9,11,77},{10,11,1},{10,11,129},{10,11,225},{11,11,118},{11,11,226},{11,11, -251},{11,11,430},{11,11,701},{11,11,974},{11,11,982},{12,11,64},{12,11,260},{12, -11,488},{140,11,690},{7,10,893},{141,10,424},{134,0,901},{136,0,822},{4,0,902},{ -5,0,809},{134,0,122},{6,0,807},{134,0,1366},{7,0,262},{5,11,748},{134,11,553},{ -133,0,620},{4,0,34},{5,0,574},{7,0,279},{7,0,1624},{136,0,601},{9,0,170},{6,10, -322},{9,10,552},{11,10,274},{13,10,209},{13,10,499},{14,10,85},{15,10,126},{145, -10,70},{132,0,537},{4,11,12},{7,11,420},{7,11,522},{7,11,809},{8,11,797},{141,11 -,88},{133,0,332},{8,10,83},{8,10,742},{8,10,817},{9,10,28},{9,10,29},{9,10,885}, -{10,10,387},{11,10,633},{11,10,740},{13,10,235},{13,10,254},{15,10,143},{143,10, -146},{6,0,1909},{9,0,964},{12,0,822},{12,0,854},{12,0,865},{12,0,910},{12,0,938} -,{15,0,169},{15,0,208},{15,0,211},{18,0,205},{18,0,206},{18,0,220},{18,0,223},{ -152,0,24},{140,10,49},{5,11,528},{135,11,1580},{6,0,261},{8,0,182},{139,0,943},{ -134,0,1721},{4,0,933},{133,0,880},{136,11,321},{5,11,266},{9,11,290},{9,11,364}, -{10,11,293},{11,11,606},{142,11,45},{6,0,1609},{4,11,50},{6,11,510},{6,11,594},{ -9,11,121},{10,11,49},{10,11,412},{139,11,834},{7,0,895},{136,11,748},{132,11,466 -},{4,10,110},{10,10,415},{10,10,597},{142,10,206},{133,0,812},{135,11,281},{6,0, -1890},{6,0,1902},{6,0,1916},{9,0,929},{9,0,942},{9,0,975},{9,0,984},{9,0,986},{9 -,0,1011},{9,0,1019},{12,0,804},{12,0,851},{12,0,867},{12,0,916},{12,0,923},{15,0 -,194},{15,0,204},{15,0,210},{15,0,222},{15,0,223},{15,0,229},{15,0,250},{18,0, -179},{18,0,186},{18,0,192},{7,10,205},{135,10,2000},{132,11,667},{135,0,778},{4, -0,137},{7,0,1178},{135,0,1520},{134,0,1314},{4,11,242},{134,11,333},{6,0,1661},{ -7,0,1975},{7,0,2009},{135,0,2011},{134,0,1591},{4,10,283},{135,10,1194},{11,0, -820},{150,0,51},{4,11,39},{5,11,36},{7,11,1843},{8,11,407},{11,11,144},{140,11, -523},{134,10,1720},{4,11,510},{7,11,29},{7,11,66},{7,11,1980},{10,11,487},{10,11 -,809},{146,11,9},{5,0,89},{7,0,1915},{9,0,185},{9,0,235},{10,0,64},{10,0,270},{ -10,0,403},{10,0,469},{10,0,529},{10,0,590},{11,0,140},{11,0,860},{13,0,1},{13,0, -422},{14,0,341},{14,0,364},{17,0,93},{18,0,113},{19,0,97},{147,0,113},{133,0,695 -},{6,0,987},{134,0,1160},{5,0,6},{6,0,183},{7,0,680},{7,0,978},{7,0,1013},{7,0, -1055},{12,0,230},{13,0,172},{146,0,29},{134,11,570},{132,11,787},{134,11,518},{6 -,0,29},{139,0,63},{132,11,516},{136,11,821},{132,0,311},{134,0,1740},{7,0,170},{ -8,0,90},{8,0,177},{8,0,415},{11,0,714},{14,0,281},{136,10,735},{134,0,1961},{135 -,11,1405},{4,11,10},{7,11,917},{139,11,786},{5,10,132},{9,10,486},{9,10,715},{10 -,10,458},{11,10,373},{11,10,668},{11,10,795},{11,10,897},{12,10,272},{12,10,424} -,{12,10,539},{12,10,558},{14,10,245},{14,10,263},{14,10,264},{14,10,393},{142,10 -,403},{11,0,91},{13,0,129},{15,0,101},{145,0,125},{135,0,1132},{4,0,494},{6,0,74 -},{7,0,44},{7,0,407},{12,0,17},{15,0,5},{148,0,11},{133,10,379},{5,0,270},{5,11, -684},{6,10,89},{6,10,400},{7,10,1569},{7,10,1623},{7,10,1850},{8,10,218},{8,10, -422},{9,10,570},{138,10,626},{4,0,276},{133,0,296},{6,0,1523},{134,11,27},{6,10, -387},{7,10,882},{141,10,111},{6,10,224},{7,10,877},{137,10,647},{135,10,790},{4, -0,7},{5,0,90},{5,0,158},{6,0,542},{7,0,221},{7,0,1574},{9,0,490},{10,0,540},{11, -0,443},{139,0,757},{7,0,588},{9,0,175},{138,0,530},{135,10,394},{142,11,23},{134 -,0,786},{135,0,580},{7,0,88},{136,0,627},{5,0,872},{6,0,57},{7,0,471},{9,0,447}, -{137,0,454},{6,11,342},{6,11,496},{8,11,275},{137,11,206},{4,11,909},{133,11,940 -},{6,0,735},{132,11,891},{8,0,845},{8,0,916},{135,10,1409},{5,0,31},{134,0,614}, -{11,0,458},{12,0,15},{140,0,432},{8,0,330},{140,0,477},{4,0,530},{5,0,521},{7,0, -1200},{10,0,460},{132,11,687},{6,0,424},{135,0,1866},{9,0,569},{12,0,12},{12,0, -81},{12,0,319},{13,0,69},{14,0,259},{16,0,87},{17,0,1},{17,0,21},{17,0,24},{18,0 -,15},{18,0,56},{18,0,59},{18,0,127},{18,0,154},{19,0,19},{148,0,31},{7,0,1302},{ -136,10,38},{134,11,253},{5,10,261},{7,10,78},{7,10,199},{8,10,815},{9,10,126},{ -138,10,342},{5,0,595},{135,0,1863},{6,11,41},{141,11,160},{5,0,13},{134,0,142},{ -6,0,97},{7,0,116},{8,0,322},{8,0,755},{9,0,548},{10,0,714},{11,0,884},{13,0,324} -,{7,11,1304},{138,11,477},{132,10,628},{134,11,1718},{7,10,266},{136,10,804},{ -135,10,208},{7,0,1021},{6,10,79},{135,10,1519},{7,0,1472},{135,0,1554},{6,11,362 -},{146,11,51},{7,0,1071},{7,0,1541},{7,0,1767},{7,0,1806},{11,0,162},{11,0,242}, -{11,0,452},{12,0,605},{15,0,26},{144,0,44},{136,10,741},{133,11,115},{145,0,115} -,{134,10,376},{6,0,1406},{134,0,1543},{5,11,193},{12,11,178},{13,11,130},{145,11 -,84},{135,0,1111},{8,0,1},{9,0,650},{10,0,326},{5,11,705},{137,11,606},{5,0,488} -,{6,0,527},{7,0,489},{7,0,1636},{8,0,121},{8,0,144},{8,0,359},{9,0,193},{9,0,241 -},{9,0,336},{9,0,882},{11,0,266},{11,0,372},{11,0,944},{12,0,401},{140,0,641},{ -135,11,174},{6,0,267},{7,10,244},{7,10,632},{7,10,1609},{8,10,178},{8,10,638},{ -141,10,58},{134,0,1983},{134,0,1155},{134,0,1575},{134,0,1438},{9,0,31},{10,0, -244},{10,0,699},{12,0,149},{141,0,497},{133,0,377},{4,11,122},{5,11,796},{5,11, -952},{6,11,1660},{6,11,1671},{8,11,567},{9,11,687},{9,11,742},{10,11,686},{11,11 -,356},{11,11,682},{140,11,281},{145,0,101},{11,11,0},{144,11,78},{5,11,179},{5, -10,791},{7,11,1095},{135,11,1213},{8,11,372},{9,11,122},{138,11,175},{7,10,686}, -{8,10,33},{8,10,238},{10,10,616},{11,10,467},{11,10,881},{13,10,217},{13,10,253} -,{142,10,268},{9,0,476},{4,11,66},{7,11,722},{135,11,904},{7,11,352},{137,11,684 -},{135,0,2023},{135,0,1836},{132,10,447},{5,0,843},{144,0,35},{137,11,779},{141, -11,35},{4,10,128},{5,10,415},{6,10,462},{7,10,294},{7,10,578},{10,10,710},{139, -10,86},{132,0,554},{133,0,536},{136,10,587},{5,0,207},{9,0,79},{11,0,625},{145,0 -,7},{7,0,1371},{6,10,427},{138,10,692},{4,0,424},{4,10,195},{135,10,802},{8,0, -785},{133,11,564},{135,0,336},{4,0,896},{6,0,1777},{134,11,556},{137,11,103},{ -134,10,1683},{7,11,544},{8,11,719},{138,11,61},{138,10,472},{4,11,5},{5,11,498}, -{136,11,637},{7,0,750},{9,0,223},{11,0,27},{11,0,466},{12,0,624},{14,0,265},{146 -,0,61},{12,0,238},{18,0,155},{12,11,238},{146,11,155},{151,10,28},{133,11,927},{ -12,0,383},{5,10,3},{8,10,578},{9,10,118},{10,10,705},{141,10,279},{4,11,893},{5, -11,780},{133,11,893},{4,0,603},{133,0,661},{4,0,11},{6,0,128},{7,0,231},{7,0, -1533},{10,0,725},{5,10,229},{5,11,238},{135,11,1350},{8,10,102},{10,10,578},{10, -10,672},{12,10,496},{13,10,408},{14,10,121},{145,10,106},{132,0,476},{134,0,1552 -},{134,11,1729},{8,10,115},{8,10,350},{9,10,489},{10,10,128},{11,10,306},{12,10, -373},{14,10,30},{17,10,79},{19,10,80},{150,10,55},{135,0,1807},{4,0,680},{4,11, -60},{7,11,760},{7,11,1800},{8,11,314},{9,11,700},{139,11,487},{4,10,230},{5,10, -702},{148,11,94},{132,11,228},{139,0,435},{9,0,20},{10,0,324},{10,0,807},{139,0, -488},{6,10,1728},{136,11,419},{4,10,484},{18,10,26},{19,10,42},{20,10,43},{21,10 -,0},{23,10,27},{152,10,14},{135,0,1431},{133,11,828},{5,0,112},{6,0,103},{6,0, -150},{7,0,1303},{9,0,292},{10,0,481},{20,0,13},{7,11,176},{7,11,178},{7,11,1110} -,{10,11,481},{148,11,13},{138,0,356},{4,11,51},{5,11,39},{6,11,4},{7,11,591},{7, -11,849},{7,11,951},{7,11,1129},{7,11,1613},{7,11,1760},{7,11,1988},{9,11,434},{ -10,11,754},{11,11,25},{11,11,37},{139,11,414},{6,0,1963},{134,0,2000},{132,10, -633},{6,0,1244},{133,11,902},{135,11,928},{140,0,18},{138,0,204},{135,11,1173},{ -134,0,867},{4,0,708},{8,0,15},{9,0,50},{9,0,386},{11,0,18},{11,0,529},{140,0,228 -},{134,11,270},{4,0,563},{7,0,109},{7,0,592},{7,0,637},{7,0,770},{8,0,463},{9,0, -60},{9,0,335},{9,0,904},{10,0,73},{11,0,434},{12,0,585},{13,0,331},{18,0,110},{ -148,0,60},{132,0,502},{14,11,359},{19,11,52},{148,11,47},{6,11,377},{7,11,1025}, -{9,11,613},{145,11,104},{6,0,347},{10,0,161},{5,10,70},{5,10,622},{6,10,334},{7, -10,1032},{9,10,171},{11,10,26},{11,10,213},{11,10,637},{11,10,707},{12,10,202},{ -12,10,380},{13,10,226},{13,10,355},{14,10,222},{145,10,42},{132,11,416},{4,0,33} -,{5,0,102},{6,0,284},{7,0,1079},{7,0,1423},{7,0,1702},{8,0,470},{9,0,554},{9,0, -723},{11,0,333},{142,11,372},{5,11,152},{5,11,197},{7,11,340},{7,11,867},{10,11, -548},{10,11,581},{11,11,6},{12,11,3},{12,11,19},{14,11,110},{142,11,289},{7,0, -246},{135,0,840},{6,0,10},{8,0,571},{9,0,739},{143,0,91},{6,0,465},{7,0,1465},{4 -,10,23},{4,10,141},{5,10,313},{5,10,1014},{6,10,50},{7,10,142},{7,10,559},{8,10, -640},{9,10,460},{9,10,783},{11,10,741},{12,10,183},{141,10,488},{133,0,626},{136 -,0,614},{138,0,237},{7,11,34},{7,11,190},{8,11,28},{8,11,141},{8,11,444},{8,11, -811},{9,11,468},{11,11,334},{12,11,24},{12,11,386},{140,11,576},{133,11,757},{5, -0,18},{6,0,526},{13,0,24},{13,0,110},{19,0,5},{147,0,44},{6,0,506},{134,11,506}, -{135,11,1553},{4,0,309},{5,0,462},{7,0,970},{7,0,1097},{22,0,30},{22,0,33},{7,11 -,1385},{11,11,582},{11,11,650},{11,11,901},{11,11,949},{12,11,232},{12,11,236},{ -13,11,413},{13,11,501},{146,11,116},{9,0,140},{5,10,222},{138,10,534},{6,0,1056} -,{137,10,906},{134,0,1704},{138,10,503},{134,0,1036},{5,10,154},{7,10,1491},{10, -10,379},{138,10,485},{4,11,383},{133,10,716},{134,0,1315},{5,0,86},{7,0,743},{9, -0,85},{10,0,281},{10,0,432},{11,0,825},{12,0,251},{13,0,118},{142,0,378},{8,0, -264},{4,10,91},{5,10,388},{5,10,845},{6,10,206},{6,10,252},{6,10,365},{7,10,136} -,{7,10,531},{136,10,621},{5,0,524},{133,0,744},{5,11,277},{141,11,247},{132,11, -435},{10,0,107},{140,0,436},{132,0,927},{10,0,123},{12,0,670},{146,0,94},{7,0, -1149},{9,0,156},{138,0,957},{5,11,265},{6,11,212},{135,11,28},{133,0,778},{133,0 -,502},{8,0,196},{10,0,283},{139,0,406},{135,10,576},{136,11,535},{134,0,1312},{5 -,10,771},{5,10,863},{5,10,898},{6,10,1632},{6,10,1644},{134,10,1780},{5,0,855},{ -5,10,331},{135,11,1487},{132,11,702},{5,11,808},{135,11,2045},{7,0,1400},{9,0, -446},{138,0,45},{140,10,632},{132,0,1003},{5,11,166},{8,11,739},{140,11,511},{5, -10,107},{7,10,201},{136,10,518},{6,10,446},{135,10,1817},{134,0,1532},{134,0, -1097},{4,11,119},{5,11,170},{5,11,447},{7,11,1708},{7,11,1889},{9,11,357},{9,11, -719},{12,11,486},{140,11,596},{9,10,851},{141,10,510},{7,0,612},{8,0,545},{8,0, -568},{8,0,642},{9,0,717},{10,0,541},{10,0,763},{11,0,449},{12,0,489},{13,0,153}, -{13,0,296},{14,0,138},{14,0,392},{15,0,50},{16,0,6},{16,0,12},{20,0,9},{132,10, -504},{4,11,450},{135,11,1158},{11,0,54},{13,0,173},{13,0,294},{5,10,883},{5,10, -975},{8,10,392},{148,10,7},{13,0,455},{15,0,99},{15,0,129},{144,0,68},{135,0,172 -},{132,11,754},{5,10,922},{134,10,1707},{134,0,1029},{17,11,39},{148,11,36},{4,0 -,568},{5,10,993},{7,10,515},{137,10,91},{132,0,732},{10,0,617},{138,11,617},{134 -,0,974},{7,0,989},{10,0,377},{12,0,363},{13,0,68},{13,0,94},{14,0,108},{142,0, -306},{136,0,733},{132,0,428},{7,0,1789},{135,11,1062},{7,0,2015},{140,0,665},{ -135,10,1433},{5,0,287},{7,10,921},{8,10,580},{8,10,593},{8,10,630},{138,10,28},{ -138,0,806},{4,10,911},{5,10,867},{5,10,1013},{7,10,2034},{8,10,798},{136,10,813} -,{134,0,1539},{8,11,523},{150,11,34},{135,11,740},{7,11,238},{7,11,2033},{8,11, -120},{8,11,188},{8,11,659},{9,11,598},{10,11,466},{12,11,342},{12,11,588},{13,11 -,503},{14,11,246},{143,11,92},{7,0,1563},{141,0,182},{5,10,135},{6,10,519},{7,10 -,1722},{10,10,271},{11,10,261},{145,10,54},{14,10,338},{148,10,81},{7,0,484},{4, -10,300},{133,10,436},{145,11,114},{6,0,1623},{134,0,1681},{133,11,640},{4,11,201 -},{7,11,1744},{8,11,602},{11,11,247},{11,11,826},{145,11,65},{8,11,164},{146,11, -62},{6,0,1833},{6,0,1861},{136,0,878},{134,0,1569},{8,10,357},{10,10,745},{14,10 -,426},{17,10,94},{147,10,57},{12,0,93},{12,0,501},{13,0,362},{14,0,151},{15,0,40 -},{15,0,59},{16,0,46},{17,0,25},{18,0,14},{18,0,134},{19,0,25},{19,0,69},{20,0, -16},{20,0,19},{20,0,66},{21,0,23},{21,0,25},{150,0,42},{6,0,1748},{8,0,715},{9,0 -,802},{10,0,46},{10,0,819},{13,0,308},{14,0,351},{14,0,363},{146,0,67},{132,0, -994},{4,0,63},{133,0,347},{132,0,591},{133,0,749},{7,11,1577},{10,11,304},{10,11 -,549},{11,11,424},{12,11,365},{13,11,220},{13,11,240},{142,11,33},{133,0,366},{7 -,0,557},{12,0,547},{14,0,86},{133,10,387},{135,0,1747},{132,11,907},{5,11,100},{ -10,11,329},{12,11,416},{149,11,29},{4,10,6},{5,10,708},{136,10,75},{7,10,1351},{ -9,10,581},{10,10,639},{11,10,453},{140,10,584},{7,0,89},{132,10,303},{138,10,772 -},{132,11,176},{5,11,636},{5,11,998},{8,11,26},{137,11,358},{7,11,9},{7,11,1508} -,{9,11,317},{10,11,210},{10,11,292},{10,11,533},{11,11,555},{12,11,526},{12,11, -607},{13,11,263},{13,11,459},{142,11,271},{134,0,1463},{6,0,772},{6,0,1137},{139 -,11,595},{7,0,977},{139,11,66},{138,0,893},{20,0,48},{148,11,48},{5,0,824},{133, -0,941},{134,11,295},{7,0,1543},{7,0,1785},{10,0,690},{4,10,106},{139,10,717},{7, -0,440},{8,0,230},{139,0,106},{5,10,890},{133,10,988},{6,10,626},{142,10,431},{10 -,11,127},{141,11,27},{17,0,32},{10,10,706},{150,10,44},{132,0,216},{137,0,332},{ -4,10,698},{136,11,119},{139,11,267},{138,10,17},{11,11,526},{11,11,939},{141,11, -290},{7,11,1167},{11,11,934},{13,11,391},{145,11,76},{139,11,39},{134,10,84},{4, -0,914},{5,0,800},{133,0,852},{10,0,416},{141,0,115},{7,0,564},{142,0,168},{4,0, -918},{133,0,876},{134,0,1764},{152,0,3},{4,0,92},{5,0,274},{7,11,126},{136,11,84 -},{140,10,498},{136,11,790},{8,0,501},{5,10,986},{6,10,130},{7,10,1582},{8,10, -458},{10,10,101},{10,10,318},{138,10,823},{6,11,64},{12,11,377},{141,11,309},{5, -0,743},{138,0,851},{4,0,49},{7,0,280},{135,0,1633},{134,0,879},{136,0,47},{7,10, -1644},{137,10,129},{132,0,865},{134,0,1202},{9,11,34},{139,11,484},{135,10,997}, -{5,0,272},{5,0,908},{5,0,942},{8,0,197},{9,0,47},{11,0,538},{139,0,742},{6,11, -1700},{7,11,26},{7,11,293},{7,11,382},{7,11,1026},{7,11,1087},{7,11,2027},{8,11, -24},{8,11,114},{8,11,252},{8,11,727},{8,11,729},{9,11,30},{9,11,199},{9,11,231}, -{9,11,251},{9,11,334},{9,11,361},{9,11,488},{9,11,712},{10,11,55},{10,11,60},{10 -,11,232},{10,11,332},{10,11,384},{10,11,396},{10,11,504},{10,11,542},{10,11,652} -,{11,11,20},{11,11,48},{11,11,207},{11,11,291},{11,11,298},{11,11,342},{11,11, -365},{11,11,394},{11,11,620},{11,11,705},{11,11,1017},{12,11,123},{12,11,340},{ -12,11,406},{12,11,643},{13,11,61},{13,11,269},{13,11,311},{13,11,319},{13,11,486 -},{14,11,234},{15,11,62},{15,11,85},{16,11,71},{18,11,119},{148,11,105},{6,0, -1455},{150,11,37},{135,10,1927},{135,0,1911},{137,0,891},{7,10,1756},{137,10,98} -,{7,10,1046},{139,10,160},{132,0,761},{6,11,379},{7,11,270},{7,11,1116},{8,11, -176},{8,11,183},{9,11,432},{9,11,661},{12,11,247},{12,11,617},{146,11,125},{6,10 -,45},{7,10,433},{8,10,129},{9,10,21},{10,10,392},{11,10,79},{12,10,499},{13,10, -199},{141,10,451},{4,0,407},{5,11,792},{133,11,900},{132,0,560},{135,0,183},{13, -0,490},{7,10,558},{136,10,353},{4,0,475},{6,0,731},{11,0,35},{13,0,71},{13,0,177 -},{14,0,422},{133,10,785},{8,10,81},{9,10,189},{9,10,201},{11,10,478},{11,10,712 -},{141,10,338},{4,0,418},{4,0,819},{133,10,353},{151,10,26},{4,11,901},{133,11, -776},{132,0,575},{7,0,818},{16,0,92},{17,0,14},{17,0,45},{18,0,75},{148,0,18},{6 -,0,222},{7,0,636},{7,0,1620},{8,0,409},{9,0,693},{139,0,77},{6,10,25},{7,10,855} -,{7,10,1258},{144,10,32},{6,0,1880},{6,0,1887},{6,0,1918},{6,0,1924},{9,0,967},{ -9,0,995},{9,0,1015},{12,0,826},{12,0,849},{12,0,857},{12,0,860},{12,0,886},{12,0 -,932},{18,0,228},{18,0,231},{146,0,240},{134,0,633},{134,0,1308},{4,11,37},{5,11 -,334},{135,11,1253},{10,0,86},{4,10,4},{7,10,1118},{7,10,1320},{7,10,1706},{8,10 -,277},{9,10,622},{11,10,724},{12,10,350},{12,10,397},{13,10,28},{13,10,159},{15, -10,89},{18,10,5},{19,10,9},{20,10,34},{150,10,47},{132,11,508},{137,11,448},{12, -11,107},{146,11,31},{132,0,817},{134,0,663},{133,0,882},{134,0,914},{132,11,540} -,{132,11,533},{136,11,608},{8,0,885},{138,0,865},{132,0,426},{6,0,58},{7,0,745}, -{7,0,1969},{8,0,399},{8,0,675},{9,0,479},{9,0,731},{10,0,330},{10,0,593},{10,0, -817},{11,0,32},{11,0,133},{11,0,221},{145,0,68},{134,10,255},{7,0,102},{137,0, -538},{137,10,216},{7,11,253},{136,11,549},{135,11,912},{9,10,183},{139,10,286},{ -11,10,956},{151,10,3},{8,11,527},{18,11,60},{147,11,24},{4,10,536},{7,10,1141},{ -10,10,723},{139,10,371},{133,11,920},{7,0,876},{135,10,285},{135,10,560},{132,10 -,690},{142,11,126},{11,10,33},{12,10,571},{149,10,1},{133,0,566},{9,0,139},{10,0 -,399},{11,0,469},{12,0,634},{13,0,223},{132,11,483},{6,0,48},{135,0,63},{18,0,12 -},{7,10,1862},{12,10,491},{12,10,520},{13,10,383},{142,10,244},{135,11,1665},{ -132,11,448},{9,11,495},{146,11,104},{6,0,114},{7,0,1224},{7,0,1556},{136,0,3},{4 -,10,190},{133,10,554},{8,0,576},{9,0,267},{133,10,1001},{133,10,446},{133,0,933} -,{139,11,1009},{8,11,653},{13,11,93},{147,11,14},{6,0,692},{6,0,821},{134,0,1077 -},{5,11,172},{135,11,801},{138,0,752},{4,0,375},{134,0,638},{134,0,1011},{140,11 -,540},{9,0,96},{133,11,260},{139,11,587},{135,10,1231},{12,0,30},{13,0,148},{14, -0,87},{14,0,182},{16,0,42},{20,0,70},{132,10,304},{6,0,1398},{7,0,56},{7,0,1989} -,{8,0,337},{8,0,738},{9,0,600},{12,0,37},{13,0,447},{142,0,92},{138,0,666},{5,0, -394},{7,0,487},{136,0,246},{9,0,437},{6,10,53},{6,10,199},{7,10,1408},{8,10,32}, -{8,10,93},{10,10,397},{10,10,629},{11,10,593},{11,10,763},{13,10,326},{145,10,35 -},{134,10,105},{9,0,320},{10,0,506},{138,10,794},{7,11,57},{8,11,167},{8,11,375} -,{9,11,82},{9,11,561},{10,11,620},{10,11,770},{11,10,704},{141,10,396},{6,0,1003 -},{5,10,114},{5,10,255},{141,10,285},{7,0,866},{135,0,1163},{133,11,531},{132,0, -328},{7,10,2035},{8,10,19},{9,10,89},{138,10,831},{8,11,194},{136,11,756},{136,0 -,1000},{5,11,453},{134,11,441},{4,0,101},{5,0,833},{7,0,1171},{136,0,744},{133,0 -,726},{136,10,746},{138,0,176},{6,0,9},{6,0,397},{7,0,53},{7,0,1742},{10,0,632}, -{11,0,828},{140,0,146},{135,11,22},{145,11,64},{132,0,839},{11,0,417},{12,0,223} -,{140,0,265},{4,11,102},{7,11,815},{7,11,1699},{139,11,964},{5,10,955},{136,10, -814},{6,0,1931},{6,0,2007},{18,0,246},{146,0,247},{8,0,198},{11,0,29},{140,0,534 -},{135,0,1771},{6,0,846},{7,11,1010},{11,11,733},{11,11,759},{12,11,563},{13,11, -34},{14,11,101},{18,11,45},{146,11,129},{4,0,186},{5,0,157},{8,0,168},{138,0,6}, -{132,11,899},{133,10,56},{148,10,100},{133,0,875},{5,0,773},{5,0,991},{6,0,1635} -,{134,0,1788},{6,0,1274},{9,0,477},{141,0,78},{4,0,639},{7,0,111},{8,0,581},{12, -0,177},{6,11,52},{9,11,104},{9,11,559},{10,10,4},{10,10,13},{11,10,638},{12,11, -308},{19,11,87},{148,10,57},{132,11,604},{4,11,301},{133,10,738},{133,10,758},{ -134,0,1747},{7,11,1440},{11,11,854},{11,11,872},{11,11,921},{12,11,551},{13,11, -472},{142,11,367},{7,0,1364},{7,0,1907},{141,0,158},{134,0,873},{4,0,404},{4,0, -659},{7,0,552},{135,0,675},{135,10,1112},{139,10,328},{7,11,508},{137,10,133},{ -133,0,391},{5,10,110},{6,10,169},{6,10,1702},{7,10,400},{8,10,538},{9,10,184},{9 -,10,524},{140,10,218},{6,11,310},{7,11,1849},{8,11,72},{8,11,272},{8,11,431},{9, -11,12},{9,11,351},{10,11,563},{10,11,630},{10,11,810},{11,11,367},{11,11,599},{ -11,11,686},{140,11,672},{5,0,540},{6,0,1697},{136,0,668},{132,0,883},{134,0,78}, -{12,0,628},{18,0,79},{6,10,133},{9,10,353},{139,10,993},{6,11,181},{7,11,537},{8 -,11,64},{9,11,127},{10,11,496},{12,11,510},{141,11,384},{6,10,93},{7,10,1422},{7 -,10,1851},{8,10,673},{9,10,529},{140,10,43},{137,10,371},{134,0,1460},{134,0,962 -},{4,11,244},{135,11,233},{9,10,25},{10,10,467},{138,10,559},{4,10,335},{135,10, -942},{133,0,460},{135,11,334},{134,11,1650},{4,0,199},{139,0,34},{5,10,601},{8, -10,39},{10,10,773},{11,10,84},{12,10,205},{142,10,1},{133,10,870},{134,0,388},{ -14,0,474},{148,0,120},{133,11,369},{139,0,271},{4,0,511},{9,0,333},{9,0,379},{10 -,0,602},{11,0,441},{11,0,723},{11,0,976},{12,0,357},{132,10,181},{134,0,608},{ -134,10,1652},{22,0,49},{137,11,338},{140,0,988},{134,0,617},{5,0,938},{136,0,707 -},{132,10,97},{5,10,147},{6,10,286},{7,10,1362},{141,10,176},{6,0,756},{134,0, -1149},{133,11,896},{6,10,375},{7,10,169},{7,10,254},{136,10,780},{134,0,1583},{ -135,10,1447},{139,0,285},{7,11,1117},{8,11,393},{136,11,539},{135,0,344},{6,0, -469},{7,0,1709},{138,0,515},{5,10,629},{135,10,1549},{5,11,4},{5,11,810},{6,11, -13},{6,11,538},{6,11,1690},{6,11,1726},{7,11,499},{7,11,1819},{8,11,148},{8,11, -696},{8,11,791},{12,11,125},{13,11,54},{143,11,9},{135,11,1268},{137,0,404},{132 -,0,500},{5,0,68},{134,0,383},{11,0,216},{139,0,340},{4,11,925},{5,11,803},{8,11, -698},{138,11,828},{4,0,337},{6,0,353},{7,0,1934},{8,0,488},{137,0,429},{7,0,236} -,{7,0,1795},{8,0,259},{9,0,135},{9,0,177},{9,0,860},{10,0,825},{11,0,115},{11,0, -370},{11,0,405},{11,0,604},{12,0,10},{12,0,667},{12,0,669},{13,0,76},{14,0,310}, -{15,0,76},{15,0,147},{148,0,23},{4,0,15},{4,0,490},{5,0,22},{6,0,244},{7,0,40},{ -7,0,200},{7,0,906},{7,0,1199},{9,0,616},{10,0,716},{11,0,635},{11,0,801},{140,0, -458},{12,0,756},{132,10,420},{134,0,1504},{6,0,757},{133,11,383},{6,0,1266},{135 -,0,1735},{5,0,598},{7,0,791},{8,0,108},{9,0,123},{7,10,1570},{140,10,542},{142, -11,410},{9,11,660},{138,11,347} -}; -/* GENERATED CODE END */ - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_STATIC_DICT_LUT_H_ */ diff --git a/src/deps/brotli/enc/utf8_util.c b/src/deps/brotli/enc/utf8_util.c deleted file mode 100644 index 65ec3f5c8d5b0..0000000000000 --- a/src/deps/brotli/enc/utf8_util.c +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Heuristics for deciding about the UTF8-ness of strings. */ - -#include "utf8_util.h" - -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static size_t BrotliParseAsUTF8( - int* symbol, const uint8_t* input, size_t size) { - /* ASCII */ - if ((input[0] & 0x80) == 0) { - *symbol = input[0]; - if (*symbol > 0) { - return 1; - } - } - /* 2-byte UTF8 */ - if (size > 1u && - (input[0] & 0xE0) == 0xC0 && - (input[1] & 0xC0) == 0x80) { - *symbol = (((input[0] & 0x1F) << 6) | - (input[1] & 0x3F)); - if (*symbol > 0x7F) { - return 2; - } - } - /* 3-byte UFT8 */ - if (size > 2u && - (input[0] & 0xF0) == 0xE0 && - (input[1] & 0xC0) == 0x80 && - (input[2] & 0xC0) == 0x80) { - *symbol = (((input[0] & 0x0F) << 12) | - ((input[1] & 0x3F) << 6) | - (input[2] & 0x3F)); - if (*symbol > 0x7FF) { - return 3; - } - } - /* 4-byte UFT8 */ - if (size > 3u && - (input[0] & 0xF8) == 0xF0 && - (input[1] & 0xC0) == 0x80 && - (input[2] & 0xC0) == 0x80 && - (input[3] & 0xC0) == 0x80) { - *symbol = (((input[0] & 0x07) << 18) | - ((input[1] & 0x3F) << 12) | - ((input[2] & 0x3F) << 6) | - (input[3] & 0x3F)); - if (*symbol > 0xFFFF && *symbol <= 0x10FFFF) { - return 4; - } - } - /* Not UTF8, emit a special symbol above the UTF8-code space */ - *symbol = 0x110000 | input[0]; - return 1; -} - -/* Returns 1 if at least min_fraction of the data is UTF8-encoded.*/ -BROTLI_BOOL BrotliIsMostlyUTF8( - const uint8_t* data, const size_t pos, const size_t mask, - const size_t length, const double min_fraction) { - size_t size_utf8 = 0; - size_t i = 0; - while (i < length) { - int symbol; - size_t bytes_read = - BrotliParseAsUTF8(&symbol, &data[(pos + i) & mask], length - i); - i += bytes_read; - if (symbol < 0x110000) size_utf8 += bytes_read; - } - return TO_BROTLI_BOOL((double)size_utf8 > min_fraction * (double)length); -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif diff --git a/src/deps/brotli/enc/utf8_util.h b/src/deps/brotli/enc/utf8_util.h deleted file mode 100644 index a38a95383ccd5..0000000000000 --- a/src/deps/brotli/enc/utf8_util.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Heuristics for deciding about the UTF8-ness of strings. */ - -#ifndef BROTLI_ENC_UTF8_UTIL_H_ -#define BROTLI_ENC_UTF8_UTIL_H_ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -static const double kMinUTF8Ratio = 0.75; - -/* Returns 1 if at least min_fraction of the bytes between pos and - pos + length in the (data, mask) ring-buffer is UTF8-encoded, otherwise - returns 0. */ -BROTLI_INTERNAL BROTLI_BOOL BrotliIsMostlyUTF8( - const uint8_t* data, const size_t pos, const size_t mask, - const size_t length, const double min_fraction); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_UTF8_UTIL_H_ */ diff --git a/src/deps/brotli/enc/write_bits.h b/src/deps/brotli/enc/write_bits.h deleted file mode 100644 index 242754b0ee506..0000000000000 --- a/src/deps/brotli/enc/write_bits.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2010 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Write bits into a byte array. */ - -#ifndef BROTLI_ENC_WRITE_BITS_H_ -#define BROTLI_ENC_WRITE_BITS_H_ - -#include - -#include "../common/platform.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* This function writes bits into bytes in increasing addresses, and within - a byte least-significant-bit first. - - The function can write up to 56 bits in one go with WriteBits - Example: let's assume that 3 bits (Rs below) have been written already: - - BYTE-0 BYTE+1 BYTE+2 - - 0000 0RRR 0000 0000 0000 0000 - - Now, we could write 5 or less bits in MSB by just shifting by 3 - and OR'ing to BYTE-0. - - For n bits, we take the last 5 bits, OR that with high bits in BYTE-0, - and locate the rest in BYTE+1, BYTE+2, etc. */ -static BROTLI_INLINE void BrotliWriteBits(size_t n_bits, - uint64_t bits, - size_t* BROTLI_RESTRICT pos, - uint8_t* BROTLI_RESTRICT array) { - BROTLI_LOG(("WriteBits %2d 0x%08x%08x %10d\n", (int)n_bits, - (uint32_t)(bits >> 32), (uint32_t)(bits & 0xFFFFFFFF), - (int)*pos)); - BROTLI_DCHECK((bits >> n_bits) == 0); - BROTLI_DCHECK(n_bits <= 56); -#if defined(BROTLI_LITTLE_ENDIAN) - /* This branch of the code can write up to 56 bits at a time, - 7 bits are lost by being perhaps already in *p and at least - 1 bit is needed to initialize the bit-stream ahead (i.e. if 7 - bits are in *p and we write 57 bits, then the next write will - access a byte that was never initialized). */ - { - uint8_t* p = &array[*pos >> 3]; - uint64_t v = (uint64_t)(*p); /* Zero-extend 8 to 64 bits. */ - v |= bits << (*pos & 7); - BROTLI_UNALIGNED_STORE64LE(p, v); /* Set some bits. */ - *pos += n_bits; - } -#else - /* implicit & 0xFF is assumed for uint8_t arithmetics */ - { - uint8_t* array_pos = &array[*pos >> 3]; - const size_t bits_reserved_in_first_byte = (*pos & 7); - size_t bits_left_to_write; - bits <<= bits_reserved_in_first_byte; - *array_pos++ |= (uint8_t)bits; - for (bits_left_to_write = n_bits + bits_reserved_in_first_byte; - bits_left_to_write >= 9; - bits_left_to_write -= 8) { - bits >>= 8; - *array_pos++ = (uint8_t)bits; - } - *array_pos = 0; - *pos += n_bits; - } -#endif -} - -static BROTLI_INLINE void BrotliWriteBitsPrepareStorage( - size_t pos, uint8_t* array) { - BROTLI_LOG(("WriteBitsPrepareStorage %10d\n", (int)pos)); - BROTLI_DCHECK((pos & 7) == 0); - array[pos >> 3] = 0; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_WRITE_BITS_H_ */ diff --git a/src/deps/brotli/include/brotli/decode.h b/src/deps/brotli/include/brotli/decode.h deleted file mode 100644 index af1aa23f60d7c..0000000000000 --- a/src/deps/brotli/include/brotli/decode.h +++ /dev/null @@ -1,409 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/** - * @file - * API for Brotli decompression. - */ - -#ifndef BROTLI_DEC_DECODE_H_ -#define BROTLI_DEC_DECODE_H_ - -#include -#include -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/** - * Opaque structure that holds decoder state. - * - * Allocated and initialized with ::BrotliDecoderCreateInstance. - * Cleaned up and deallocated with ::BrotliDecoderDestroyInstance. - */ -typedef struct BrotliDecoderStateStruct BrotliDecoderState; - -/** - * Result type for ::BrotliDecoderDecompress and - * ::BrotliDecoderDecompressStream functions. - */ -typedef enum { - /** Decoding error, e.g. corrupted input or memory allocation problem. */ - BROTLI_DECODER_RESULT_ERROR = 0, - /** Decoding successfully completed. */ - BROTLI_DECODER_RESULT_SUCCESS = 1, - /** Partially done; should be called again with more input. */ - BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2, - /** Partially done; should be called again with more output. */ - BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3 -} BrotliDecoderResult; - -/** - * Template that evaluates items of ::BrotliDecoderErrorCode. - * - * Example: @code {.cpp} - * // Log Brotli error code. - * switch (brotliDecoderErrorCode) { - * #define CASE_(PREFIX, NAME, CODE) \ - * case BROTLI_DECODER ## PREFIX ## NAME: \ - * LOG(INFO) << "error code:" << #NAME; \ - * break; - * #define NEWLINE_ - * BROTLI_DECODER_ERROR_CODES_LIST(CASE_, NEWLINE_) - * #undef CASE_ - * #undef NEWLINE_ - * default: LOG(FATAL) << "unknown brotli error code"; - * } - * @endcode - */ -#define BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \ - BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \ - /* Same as BrotliDecoderResult values */ \ - BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \ - BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \ - BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \ - \ - /* Errors caused by invalid input */ \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_FORMAT_, DISTANCE, -16) SEPARATOR \ - \ - /* -17 code is reserved */ \ - \ - BROTLI_ERROR_CODE(_ERROR_, COMPOUND_DICTIONARY, -18) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR \ - \ - /* Memory allocation problems */ \ - BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \ - /* Literal, insert and distance trees together */ \ - BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \ - /* -23..-24 codes are reserved for distinct tree groups */ \ - BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \ - BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \ - /* -28..-29 codes are reserved for dynamic ring-buffer allocation */ \ - BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \ - \ - /* "Impossible" states */ \ - BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31) - -/** - * Error code for detailed logging / production debugging. - * - * See ::BrotliDecoderGetErrorCode and ::BROTLI_LAST_ERROR_CODE. - */ -typedef enum { -#define BROTLI_COMMA_ , -#define BROTLI_ERROR_CODE_ENUM_ITEM_(PREFIX, NAME, CODE) \ - BROTLI_DECODER ## PREFIX ## NAME = CODE - BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_ENUM_ITEM_, BROTLI_COMMA_) -} BrotliDecoderErrorCode; -#undef BROTLI_ERROR_CODE_ENUM_ITEM_ -#undef BROTLI_COMMA_ - -/** - * The value of the last error code, negative integer. - * - * All other error code values are in the range from ::BROTLI_LAST_ERROR_CODE - * to @c -1. There are also 4 other possible non-error codes @c 0 .. @c 3 in - * ::BrotliDecoderErrorCode enumeration. - */ -#define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE - -/** Options to be used with ::BrotliDecoderSetParameter. */ -typedef enum BrotliDecoderParameter { - /** - * Disable "canny" ring buffer allocation strategy. - * - * Ring buffer is allocated according to window size, despite the real size of - * the content. - */ - BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION = 0, - /** - * Flag that determines if "Large Window Brotli" is used. - */ - BROTLI_DECODER_PARAM_LARGE_WINDOW = 1 -} BrotliDecoderParameter; - -/** - * Sets the specified parameter to the given decoder instance. - * - * @param state decoder instance - * @param param parameter to set - * @param value new parameter value - * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid - * @returns ::BROTLI_TRUE if value is accepted - */ -BROTLI_DEC_API BROTLI_BOOL BrotliDecoderSetParameter( - BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value); - -/** - * Adds LZ77 prefix dictionary, adds or replaces built-in static dictionary and - * transforms. - * - * Attached dictionary ownership is not transferred. - * Data provided to this method should be kept accessible until - * decoding is finished and decoder instance is destroyed. - * - * @note Dictionaries can NOT be attached after actual decoding is started. - * - * @param state decoder instance - * @param type dictionary data format - * @param data_size length of memory region pointed by @p data - * @param data dictionary data in format corresponding to @p type - * @returns ::BROTLI_FALSE if dictionary is corrupted, - * or dictionary count limit is reached - * @returns ::BROTLI_TRUE if dictionary is accepted / attached - */ -BROTLI_DEC_API BROTLI_BOOL BrotliDecoderAttachDictionary( - BrotliDecoderState* state, BrotliSharedDictionaryType type, - size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]); - -/** - * Creates an instance of ::BrotliDecoderState and initializes it. - * - * The instance can be used once for decoding and should then be destroyed with - * ::BrotliDecoderDestroyInstance, it cannot be reused for a new decoding - * session. - * - * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the - * case they are both zero, default memory allocators are used. @p opaque is - * passed to @p alloc_func and @p free_func when they are called. @p free_func - * has to return without doing anything when asked to free a NULL pointer. - * - * @param alloc_func custom memory allocation function - * @param free_func custom memory free function - * @param opaque custom memory manager handle - * @returns @c 0 if instance can not be allocated or initialized - * @returns pointer to initialized ::BrotliDecoderState otherwise - */ -BROTLI_DEC_API BrotliDecoderState* BrotliDecoderCreateInstance( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); - -/** - * Deinitializes and frees ::BrotliDecoderState instance. - * - * @param state decoder instance to be cleaned up and deallocated - */ -BROTLI_DEC_API void BrotliDecoderDestroyInstance(BrotliDecoderState* state); - -/** - * Performs one-shot memory-to-memory decompression. - * - * Decompresses the data in @p encoded_buffer into @p decoded_buffer, and sets - * @p *decoded_size to the decompressed length. - * - * @param encoded_size size of @p encoded_buffer - * @param encoded_buffer compressed data buffer with at least @p encoded_size - * addressable bytes - * @param[in, out] decoded_size @b in: size of @p decoded_buffer; \n - * @b out: length of decompressed data written to - * @p decoded_buffer - * @param decoded_buffer decompressed data destination buffer - * @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory - * allocation failed, or @p decoded_buffer is not large enough; - * @returns ::BROTLI_DECODER_RESULT_SUCCESS otherwise - */ -BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompress( - size_t encoded_size, - const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], - size_t* decoded_size, - uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]); - -/** - * Decompresses the input stream to the output stream. - * - * The values @p *available_in and @p *available_out must specify the number of - * bytes addressable at @p *next_in and @p *next_out respectively. - * When @p *available_out is @c 0, @p next_out is allowed to be @c NULL. - * - * After each call, @p *available_in will be decremented by the amount of input - * bytes consumed, and the @p *next_in pointer will be incremented by that - * amount. Similarly, @p *available_out will be decremented by the amount of - * output bytes written, and the @p *next_out pointer will be incremented by - * that amount. - * - * @p total_out, if it is not a null-pointer, will be set to the number - * of bytes decompressed since the last @p state initialization. - * - * @note Input is never overconsumed, so @p next_in and @p available_in could be - * passed to the next consumer after decoding is complete. - * - * @param state decoder instance - * @param[in, out] available_in @b in: amount of available input; \n - * @b out: amount of unused input - * @param[in, out] next_in pointer to the next compressed byte - * @param[in, out] available_out @b in: length of output buffer; \n - * @b out: remaining size of output buffer - * @param[in, out] next_out output buffer cursor; - * can be @c NULL if @p available_out is @c 0 - * @param[out] total_out number of bytes decompressed so far; can be @c NULL - * @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory - * allocation failed, arguments were invalid, etc.; - * use ::BrotliDecoderGetErrorCode to get detailed error code - * @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT decoding is blocked until - * more input data is provided - * @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT decoding is blocked until - * more output space is provided - * @returns ::BROTLI_DECODER_RESULT_SUCCESS decoding is finished, no more - * input might be consumed and no more output will be produced - */ -BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompressStream( - BrotliDecoderState* state, size_t* available_in, const uint8_t** next_in, - size_t* available_out, uint8_t** next_out, size_t* total_out); - -/** - * Checks if decoder has more output. - * - * @param state decoder instance - * @returns ::BROTLI_TRUE, if decoder has some unconsumed output - * @returns ::BROTLI_FALSE otherwise - */ -BROTLI_DEC_API BROTLI_BOOL BrotliDecoderHasMoreOutput( - const BrotliDecoderState* state); - -/** - * Acquires pointer to internal output buffer. - * - * This method is used to make language bindings easier and more efficient: - * -# push data to ::BrotliDecoderDecompressStream, - * until ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT is reported - * -# use ::BrotliDecoderTakeOutput to peek bytes and copy to language-specific - * entity - * - * Also this could be useful if there is an output stream that is able to - * consume all the provided data (e.g. when data is saved to file system). - * - * @attention After every call to ::BrotliDecoderTakeOutput @p *size bytes of - * output are considered consumed for all consecutive calls to the - * instance methods; returned pointer becomes invalidated as well. - * - * @note Decoder output is not guaranteed to be contiguous. This means that - * after the size-unrestricted call to ::BrotliDecoderTakeOutput, - * immediate next call to ::BrotliDecoderTakeOutput may return more data. - * - * @param state decoder instance - * @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if - * any amount could be handled; \n - * @b out: amount of data pointed by returned pointer and - * considered consumed; \n - * out value is never greater than in value, unless it is @c 0 - * @returns pointer to output data - */ -BROTLI_DEC_API const uint8_t* BrotliDecoderTakeOutput( - BrotliDecoderState* state, size_t* size); - -/** - * Checks if instance has already consumed input. - * - * Instance that returns ::BROTLI_FALSE is considered "fresh" and could be - * reused. - * - * @param state decoder instance - * @returns ::BROTLI_TRUE if decoder has already used some input bytes - * @returns ::BROTLI_FALSE otherwise - */ -BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* state); - -/** - * Checks if decoder instance reached the final state. - * - * @param state decoder instance - * @returns ::BROTLI_TRUE if decoder is in a state where it reached the end of - * the input and produced all of the output - * @returns ::BROTLI_FALSE otherwise - */ -BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished( - const BrotliDecoderState* state); - -/** - * Acquires a detailed error code. - * - * Should be used only after ::BrotliDecoderDecompressStream returns - * ::BROTLI_DECODER_RESULT_ERROR. - * - * See also ::BrotliDecoderErrorString - * - * @param state decoder instance - * @returns last saved error code - */ -BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode( - const BrotliDecoderState* state); - -/** - * Converts error code to a c-string. - */ -BROTLI_DEC_API const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c); - -/** - * Gets a decoder library version. - * - * Look at BROTLI_MAKE_HEX_VERSION for more information. - */ -BROTLI_DEC_API uint32_t BrotliDecoderVersion(void); - -/** - * Callback to fire on metadata block start. - * - * After this callback is fired, if @p size is not @c 0, it is followed by - * ::brotli_decoder_metadata_chunk_func as more metadata block contents become - * accessible. - * - * @param opaque callback handle - * @param size size of metadata block - */ -typedef void (*brotli_decoder_metadata_start_func)(void* opaque, size_t size); - -/** - * Callback to fire on metadata block chunk becomes available. - * - * This function can be invoked multiple times per metadata block; block should - * be considered finished when sum of @p size matches the announced metadata - * block size. Chunks contents pointed by @p data are transient and shouln not - * be accessed after leaving the callback. - * - * @param opaque callback handle - * @param data pointer to metadata contents - * @param size size of metadata block chunk, at least @c 1 - */ -typedef void (*brotli_decoder_metadata_chunk_func)(void* opaque, - const uint8_t* data, - size_t size); - -/** - * Sets callback for receiving metadata blocks. - * - * @param state decoder instance - * @param start_func callback on metadata block start - * @param chunk_func callback on metadata block chunk - * @param opaque callback handle - */ -BROTLI_DEC_API void BrotliDecoderSetMetadataCallbacks( - BrotliDecoderState* state, - brotli_decoder_metadata_start_func start_func, - brotli_decoder_metadata_chunk_func chunk_func, void* opaque); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_DEC_DECODE_H_ */ diff --git a/src/deps/brotli/include/brotli/encode.h b/src/deps/brotli/include/brotli/encode.h deleted file mode 100644 index dea9931ebb39f..0000000000000 --- a/src/deps/brotli/include/brotli/encode.h +++ /dev/null @@ -1,501 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/** - * @file - * API for Brotli compression. - */ - -#ifndef BROTLI_ENC_ENCODE_H_ -#define BROTLI_ENC_ENCODE_H_ - -#include -#include -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/** Minimal value for ::BROTLI_PARAM_LGWIN parameter. */ -#define BROTLI_MIN_WINDOW_BITS 10 -/** - * Maximal value for ::BROTLI_PARAM_LGWIN parameter. - * - * @note equal to @c BROTLI_MAX_DISTANCE_BITS constant. - */ -#define BROTLI_MAX_WINDOW_BITS 24 -/** - * Maximal value for ::BROTLI_PARAM_LGWIN parameter - * in "Large Window Brotli" (32-bit). - */ -#define BROTLI_LARGE_MAX_WINDOW_BITS 30 -/** Minimal value for ::BROTLI_PARAM_LGBLOCK parameter. */ -#define BROTLI_MIN_INPUT_BLOCK_BITS 16 -/** Maximal value for ::BROTLI_PARAM_LGBLOCK parameter. */ -#define BROTLI_MAX_INPUT_BLOCK_BITS 24 -/** Minimal value for ::BROTLI_PARAM_QUALITY parameter. */ -#define BROTLI_MIN_QUALITY 0 -/** Maximal value for ::BROTLI_PARAM_QUALITY parameter. */ -#define BROTLI_MAX_QUALITY 11 - -/** Options for ::BROTLI_PARAM_MODE parameter. */ -typedef enum BrotliEncoderMode { - /** - * Default compression mode. - * - * In this mode compressor does not know anything in advance about the - * properties of the input. - */ - BROTLI_MODE_GENERIC = 0, - /** Compression mode for UTF-8 formatted text input. */ - BROTLI_MODE_TEXT = 1, - /** Compression mode used in WOFF 2.0. */ - BROTLI_MODE_FONT = 2 -} BrotliEncoderMode; - -/** Default value for ::BROTLI_PARAM_QUALITY parameter. */ -#define BROTLI_DEFAULT_QUALITY 11 -/** Default value for ::BROTLI_PARAM_LGWIN parameter. */ -#define BROTLI_DEFAULT_WINDOW 22 -/** Default value for ::BROTLI_PARAM_MODE parameter. */ -#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC - -/** Operations that can be performed by streaming encoder. */ -typedef enum BrotliEncoderOperation { - /** - * Process input. - * - * Encoder may postpone producing output, until it has processed enough input. - */ - BROTLI_OPERATION_PROCESS = 0, - /** - * Produce output for all processed input. - * - * Actual flush is performed when input stream is depleted and there is enough - * space in output stream. This means that client should repeat - * ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and - * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired - * via ::BrotliEncoderTakeOutput, then operation should be repeated after - * output buffer is drained. - * - * @warning Until flush is complete, client @b SHOULD @b NOT swap, - * reduce or extend input stream. - * - * When flush is complete, output data will be sufficient for decoder to - * reproduce all the given input. - */ - BROTLI_OPERATION_FLUSH = 1, - /** - * Finalize the stream. - * - * Actual finalization is performed when input stream is depleted and there is - * enough space in output stream. This means that client should repeat - * ::BROTLI_OPERATION_FINISH operation until @p available_in becomes @c 0, and - * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired - * via ::BrotliEncoderTakeOutput, then operation should be repeated after - * output buffer is drained. - * - * @warning Until finalization is complete, client @b SHOULD @b NOT swap, - * reduce or extend input stream. - * - * Helper function ::BrotliEncoderIsFinished checks if stream is finalized and - * output fully dumped. - * - * Adding more input data to finalized stream is impossible. - */ - BROTLI_OPERATION_FINISH = 2, - /** - * Emit metadata block to stream. - * - * Metadata is opaque to Brotli: neither encoder, nor decoder processes this - * data or relies on it. It may be used to pass some extra information from - * encoder client to decoder client without interfering with main data stream. - * - * @note Encoder may emit empty metadata blocks internally, to pad encoded - * stream to byte boundary. - * - * @warning Until emitting metadata is complete client @b SHOULD @b NOT swap, - * reduce or extend input stream. - * - * @warning The whole content of input buffer is considered to be the content - * of metadata block. Do @b NOT @e append metadata to input stream, - * before it is depleted with other operations. - * - * Stream is soft-flushed before metadata block is emitted. Metadata block - * @b MUST be no longer than than 16MiB. - */ - BROTLI_OPERATION_EMIT_METADATA = 3 -} BrotliEncoderOperation; - -/** Options to be used with ::BrotliEncoderSetParameter. */ -typedef enum BrotliEncoderParameter { - /** - * Tune encoder for specific input. - * - * ::BrotliEncoderMode enumerates all available values. - */ - BROTLI_PARAM_MODE = 0, - /** - * The main compression speed-density lever. - * - * The higher the quality, the slower the compression. Range is - * from ::BROTLI_MIN_QUALITY to ::BROTLI_MAX_QUALITY. - */ - BROTLI_PARAM_QUALITY = 1, - /** - * Recommended sliding LZ77 window size. - * - * Encoder may reduce this value, e.g. if input is much smaller than - * window size. - * - * Window size is `(1 << value) - 16`. - * - * Range is from ::BROTLI_MIN_WINDOW_BITS to ::BROTLI_MAX_WINDOW_BITS. - */ - BROTLI_PARAM_LGWIN = 2, - /** - * Recommended input block size. - * - * Encoder may reduce this value, e.g. if input is much smaller than input - * block size. - * - * Range is from ::BROTLI_MIN_INPUT_BLOCK_BITS to - * ::BROTLI_MAX_INPUT_BLOCK_BITS. - * - * @note Bigger input block size allows better compression, but consumes more - * memory. \n The rough formula of memory used for temporary input - * storage is `3 << lgBlock`. - */ - BROTLI_PARAM_LGBLOCK = 3, - /** - * Flag that affects usage of "literal context modeling" format feature. - * - * This flag is a "decoding-speed vs compression ratio" trade-off. - */ - BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING = 4, - /** - * Estimated total input size for all ::BrotliEncoderCompressStream calls. - * - * The default value is 0, which means that the total input size is unknown. - */ - BROTLI_PARAM_SIZE_HINT = 5, - /** - * Flag that determines if "Large Window Brotli" is used. - */ - BROTLI_PARAM_LARGE_WINDOW = 6, - /** - * Recommended number of postfix bits (NPOSTFIX). - * - * Encoder may change this value. - * - * Range is from 0 to ::BROTLI_MAX_NPOSTFIX. - */ - BROTLI_PARAM_NPOSTFIX = 7, - /** - * Recommended number of direct distance codes (NDIRECT). - * - * Encoder may change this value. - * - * Range is from 0 to (15 << NPOSTFIX) in steps of (1 << NPOSTFIX). - */ - BROTLI_PARAM_NDIRECT = 8, - /** - * Number of bytes of input stream already processed by a different instance. - * - * @note It is important to configure all the encoder instances with same - * parameters (except this one) in order to allow all the encoded parts - * obey the same restrictions implied by header. - * - * If offset is not 0, then stream header is omitted. - * In any case output start is byte aligned, so for proper streams stitching - * "predecessor" stream must be flushed. - * - * Range is not artificially limited, but all the values greater or equal to - * maximal window size have the same effect. Values greater than 2**30 are not - * allowed. - */ - BROTLI_PARAM_STREAM_OFFSET = 9 -} BrotliEncoderParameter; - -/** - * Opaque structure that holds encoder state. - * - * Allocated and initialized with ::BrotliEncoderCreateInstance. - * Cleaned up and deallocated with ::BrotliEncoderDestroyInstance. - */ -typedef struct BrotliEncoderStateStruct BrotliEncoderState; - -/** - * Sets the specified parameter to the given encoder instance. - * - * @param state encoder instance - * @param param parameter to set - * @param value new parameter value - * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid - * @returns ::BROTLI_FALSE if value of parameter can not be changed at current - * encoder state (e.g. when encoding is started, window size might be - * already encoded and therefore it is impossible to change it) - * @returns ::BROTLI_TRUE if value is accepted - * @warning invalid values might be accepted in case they would not break - * encoding process. - */ -BROTLI_ENC_API BROTLI_BOOL BrotliEncoderSetParameter( - BrotliEncoderState* state, BrotliEncoderParameter param, uint32_t value); - -/** - * Creates an instance of ::BrotliEncoderState and initializes it. - * - * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the - * case they are both zero, default memory allocators are used. @p opaque is - * passed to @p alloc_func and @p free_func when they are called. @p free_func - * has to return without doing anything when asked to free a NULL pointer. - * - * @param alloc_func custom memory allocation function - * @param free_func custom memory free function - * @param opaque custom memory manager handle - * @returns @c 0 if instance can not be allocated or initialized - * @returns pointer to initialized ::BrotliEncoderState otherwise - */ -BROTLI_ENC_API BrotliEncoderState* BrotliEncoderCreateInstance( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); - -/** - * Deinitializes and frees ::BrotliEncoderState instance. - * - * @param state decoder instance to be cleaned up and deallocated - */ -BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state); - -/* Opaque type for pointer to different possible internal structures containing - dictionary prepared for the encoder */ -typedef struct BrotliEncoderPreparedDictionaryStruct - BrotliEncoderPreparedDictionary; - -/** - * Prepares a shared dictionary from the given file format for the encoder. - * - * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the - * case they are both zero, default memory allocators are used. @p opaque is - * passed to @p alloc_func and @p free_func when they are called. @p free_func - * has to return without doing anything when asked to free a NULL pointer. - * - * @param type type of dictionary stored in data - * @param data_size size of @p data buffer - * @param data pointer to the dictionary data - * @param quality the maximum Brotli quality to prepare the dictionary for, - * use BROTLI_MAX_QUALITY by default - * @param alloc_func custom memory allocation function - * @param free_func custom memory free function - * @param opaque custom memory manager handle - */ -BROTLI_ENC_API BrotliEncoderPreparedDictionary* -BrotliEncoderPrepareDictionary(BrotliSharedDictionaryType type, - size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)], - int quality, - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); - -BROTLI_ENC_API void BrotliEncoderDestroyPreparedDictionary( - BrotliEncoderPreparedDictionary* dictionary); - -/** - * Attaches a prepared dictionary of any type to the encoder. Can be used - * multiple times to attach multiple dictionaries. The dictionary type was - * determined by BrotliEncoderPrepareDictionary. Multiple raw prefix - * dictionaries and/or max 1 serialized dictionary with custom words can be - * attached. - * - * @returns ::BROTLI_FALSE in case of error - * @returns ::BROTLI_TRUE otherwise - */ -BROTLI_ENC_API BROTLI_BOOL BrotliEncoderAttachPreparedDictionary( - BrotliEncoderState* state, - const BrotliEncoderPreparedDictionary* dictionary); - -/** - * Calculates the output size bound for the given @p input_size. - * - * @warning Result is only valid if quality is at least @c 2 and, in - * case ::BrotliEncoderCompressStream was used, no flushes - * (::BROTLI_OPERATION_FLUSH) were performed. - * - * @param input_size size of projected input - * @returns @c 0 if result does not fit @c size_t - */ -BROTLI_ENC_API size_t BrotliEncoderMaxCompressedSize(size_t input_size); - -/** - * Performs one-shot memory-to-memory compression. - * - * Compresses the data in @p input_buffer into @p encoded_buffer, and sets - * @p *encoded_size to the compressed length. - * - * @note If ::BrotliEncoderMaxCompressedSize(@p input_size) returns non-zero - * value, then output is guaranteed to be no longer than that. - * - * @note If @p lgwin is greater than ::BROTLI_MAX_WINDOW_BITS then resulting - * stream might be incompatible with RFC 7932; to decode such streams, - * decoder should be configured with - * ::BROTLI_DECODER_PARAM_LARGE_WINDOW = @c 1 - * - * @param quality quality parameter value, e.g. ::BROTLI_DEFAULT_QUALITY - * @param lgwin lgwin parameter value, e.g. ::BROTLI_DEFAULT_WINDOW - * @param mode mode parameter value, e.g. ::BROTLI_DEFAULT_MODE - * @param input_size size of @p input_buffer - * @param input_buffer input data buffer with at least @p input_size - * addressable bytes - * @param[in, out] encoded_size @b in: size of @p encoded_buffer; \n - * @b out: length of compressed data written to - * @p encoded_buffer, or @c 0 if compression fails - * @param encoded_buffer compressed data destination buffer - * @returns ::BROTLI_FALSE in case of compression error - * @returns ::BROTLI_FALSE if output buffer is too small - * @returns ::BROTLI_TRUE otherwise - */ -BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompress( - int quality, int lgwin, BrotliEncoderMode mode, size_t input_size, - const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)], - size_t* encoded_size, - uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]); - -/** - * Compresses input stream to output stream. - * - * The values @p *available_in and @p *available_out must specify the number of - * bytes addressable at @p *next_in and @p *next_out respectively. - * When @p *available_out is @c 0, @p next_out is allowed to be @c NULL. - * - * After each call, @p *available_in will be decremented by the amount of input - * bytes consumed, and the @p *next_in pointer will be incremented by that - * amount. Similarly, @p *available_out will be decremented by the amount of - * output bytes written, and the @p *next_out pointer will be incremented by - * that amount. - * - * @p total_out, if it is not a null-pointer, will be set to the number - * of bytes compressed since the last @p state initialization. - * - * - * - * Internally workflow consists of 3 tasks: - * -# (optionally) copy input data to internal buffer - * -# actually compress data and (optionally) store it to internal buffer - * -# (optionally) copy compressed bytes from internal buffer to output stream - * - * Whenever all 3 tasks can't move forward anymore, or error occurs, this - * method returns the control flow to caller. - * - * @p op is used to perform flush, finish the stream, or inject metadata block. - * See ::BrotliEncoderOperation for more information. - * - * Flushing the stream means forcing encoding of all input passed to encoder and - * completing the current output block, so it could be fully decoded by stream - * decoder. To perform flush set @p op to ::BROTLI_OPERATION_FLUSH. - * Under some circumstances (e.g. lack of output stream capacity) this operation - * would require several calls to ::BrotliEncoderCompressStream. The method must - * be called again until both input stream is depleted and encoder has no more - * output (see ::BrotliEncoderHasMoreOutput) after the method is called. - * - * Finishing the stream means encoding of all input passed to encoder and - * adding specific "final" marks, so stream decoder could determine that stream - * is complete. To perform finish set @p op to ::BROTLI_OPERATION_FINISH. - * Under some circumstances (e.g. lack of output stream capacity) this operation - * would require several calls to ::BrotliEncoderCompressStream. The method must - * be called again until both input stream is depleted and encoder has no more - * output (see ::BrotliEncoderHasMoreOutput) after the method is called. - * - * @warning When flushing and finishing, @p op should not change until operation - * is complete; input stream should not be swapped, reduced or - * extended as well. - * - * @param state encoder instance - * @param op requested operation - * @param[in, out] available_in @b in: amount of available input; \n - * @b out: amount of unused input - * @param[in, out] next_in pointer to the next input byte - * @param[in, out] available_out @b in: length of output buffer; \n - * @b out: remaining size of output buffer - * @param[in, out] next_out compressed output buffer cursor; - * can be @c NULL if @p available_out is @c 0 - * @param[out] total_out number of bytes produced so far; can be @c NULL - * @returns ::BROTLI_FALSE if there was an error - * @returns ::BROTLI_TRUE otherwise - */ -BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompressStream( - BrotliEncoderState* state, BrotliEncoderOperation op, size_t* available_in, - const uint8_t** next_in, size_t* available_out, uint8_t** next_out, - size_t* total_out); - -/** - * Checks if encoder instance reached the final state. - * - * @param state encoder instance - * @returns ::BROTLI_TRUE if encoder is in a state where it reached the end of - * the input and produced all of the output - * @returns ::BROTLI_FALSE otherwise - */ -BROTLI_ENC_API BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* state); - -/** - * Checks if encoder has more output. - * - * @param state encoder instance - * @returns ::BROTLI_TRUE, if encoder has some unconsumed output - * @returns ::BROTLI_FALSE otherwise - */ -BROTLI_ENC_API BROTLI_BOOL BrotliEncoderHasMoreOutput( - BrotliEncoderState* state); - -/** - * Acquires pointer to internal output buffer. - * - * This method is used to make language bindings easier and more efficient: - * -# push data to ::BrotliEncoderCompressStream, - * until ::BrotliEncoderHasMoreOutput returns BROTLI_TRUE - * -# use ::BrotliEncoderTakeOutput to peek bytes and copy to language-specific - * entity - * - * Also this could be useful if there is an output stream that is able to - * consume all the provided data (e.g. when data is saved to file system). - * - * @attention After every call to ::BrotliEncoderTakeOutput @p *size bytes of - * output are considered consumed for all consecutive calls to the - * instance methods; returned pointer becomes invalidated as well. - * - * @note Encoder output is not guaranteed to be contiguous. This means that - * after the size-unrestricted call to ::BrotliEncoderTakeOutput, - * immediate next call to ::BrotliEncoderTakeOutput may return more data. - * - * @param state encoder instance - * @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if - * any amount could be handled; \n - * @b out: amount of data pointed by returned pointer and - * considered consumed; \n - * out value is never greater than in value, unless it is @c 0 - * @returns pointer to output data - */ -BROTLI_ENC_API const uint8_t* BrotliEncoderTakeOutput( - BrotliEncoderState* state, size_t* size); - -/* Returns the estimated peak memory usage (in bytes) of the BrotliCompress() - function, not counting the memory needed for the input and output. */ -BROTLI_ENC_EXTRA_API size_t BrotliEncoderEstimatePeakMemoryUsage( - int quality, int lgwin, size_t input_size); -/* Returns 0 if dictionary is not valid; otherwise returns allocation size. */ -BROTLI_ENC_EXTRA_API size_t BrotliEncoderGetPreparedDictionarySize( - const BrotliEncoderPreparedDictionary* dictionary); - -/** - * Gets an encoder library version. - * - * Look at BROTLI_MAKE_HEX_VERSION for more information. - */ -BROTLI_ENC_API uint32_t BrotliEncoderVersion(void); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_ENC_ENCODE_H_ */ diff --git a/src/deps/brotli/include/brotli/port.h b/src/deps/brotli/include/brotli/port.h deleted file mode 100644 index 0d50019042d1f..0000000000000 --- a/src/deps/brotli/include/brotli/port.h +++ /dev/null @@ -1,305 +0,0 @@ -/* Copyright 2016 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Macros for compiler / platform specific API declarations. */ - -#ifndef BROTLI_COMMON_PORT_H_ -#define BROTLI_COMMON_PORT_H_ - -/* The following macros were borrowed from https://github.com/nemequ/hedley - * with permission of original author - Evan Nemerson */ - -/* >>> >>> >>> hedley macros */ - -#define BROTLI_MAKE_VERSION(major, minor, revision) \ - (((major) * 1000000) + ((minor) * 1000) + (revision)) - -#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) -#define BROTLI_GNUC_VERSION \ - BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#elif defined(__GNUC__) -#define BROTLI_GNUC_VERSION BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, 0) -#endif - -#if defined(BROTLI_GNUC_VERSION) -#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) \ - (BROTLI_GNUC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) -#define BROTLI_MSVC_VERSION \ - BROTLI_MAKE_VERSION((_MSC_FULL_VER / 10000000), \ - (_MSC_FULL_VER % 10000000) / 100000, \ - (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) -#define BROTLI_MSVC_VERSION \ - BROTLI_MAKE_VERSION((_MSC_FULL_VER / 1000000), \ - (_MSC_FULL_VER % 1000000) / 10000, \ - (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) -#define BROTLI_MSVC_VERSION \ - BROTLI_MAKE_VERSION(_MSC_VER / 100, _MSC_VER % 100, 0) -#endif - -#if !defined(_MSC_VER) -#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) (0) -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) -#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \ - (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) -#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \ - (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) -#else -#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \ - (_MSC_VER >= ((major * 100) + (minor))) -#endif - -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) -#define BROTLI_INTEL_VERSION \ - BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, \ - __INTEL_COMPILER % 100, \ - __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) -#define BROTLI_INTEL_VERSION \ - BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) -#endif - -#if defined(BROTLI_INTEL_VERSION) -#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) \ - (BROTLI_INTEL_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__PGI) && \ - defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) -#define BROTLI_PGI_VERSION \ - BROTLI_MAKE_VERSION(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) -#endif - -#if defined(BROTLI_PGI_VERSION) -#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) \ - (BROTLI_PGI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) -#define BROTLI_SUNPRO_VERSION \ - BROTLI_MAKE_VERSION( \ - (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \ - (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), \ - (__SUNPRO_C & 0xf) * 10) -#elif defined(__SUNPRO_C) -#define BROTLI_SUNPRO_VERSION \ - BROTLI_MAKE_VERSION((__SUNPRO_C >> 8) & 0xf, \ - (__SUNPRO_C >> 4) & 0xf, \ - (__SUNPRO_C) & 0xf) -#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) -#define BROTLI_SUNPRO_VERSION \ - BROTLI_MAKE_VERSION( \ - (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \ - (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \ - (__SUNPRO_CC & 0xf) * 10) -#elif defined(__SUNPRO_CC) -#define BROTLI_SUNPRO_VERSION \ - BROTLI_MAKE_VERSION((__SUNPRO_CC >> 8) & 0xf, \ - (__SUNPRO_CC >> 4) & 0xf, \ - (__SUNPRO_CC) & 0xf) -#endif - -#if defined(BROTLI_SUNPRO_VERSION) -#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) \ - (BROTLI_SUNPRO_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) -#define BROTLI_ARM_VERSION \ - BROTLI_MAKE_VERSION((__ARMCOMPILER_VERSION / 1000000), \ - (__ARMCOMPILER_VERSION % 1000000) / 10000, \ - (__ARMCOMPILER_VERSION % 10000) / 100) -#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) -#define BROTLI_ARM_VERSION \ - BROTLI_MAKE_VERSION((__ARMCC_VERSION / 1000000), \ - (__ARMCC_VERSION % 1000000) / 10000, \ - (__ARMCC_VERSION % 10000) / 100) -#endif - -#if defined(BROTLI_ARM_VERSION) -#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) \ - (BROTLI_ARM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__ibmxl__) -#define BROTLI_IBM_VERSION \ - BROTLI_MAKE_VERSION(__ibmxl_version__, \ - __ibmxl_release__, \ - __ibmxl_modification__) -#elif defined(__xlC__) && defined(__xlC_ver__) -#define BROTLI_IBM_VERSION \ - BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) -#elif defined(__xlC__) -#define BROTLI_IBM_VERSION BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, 0) -#endif - -#if defined(BROTLI_IBM_VERSION) -#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) \ - (BROTLI_IBM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__TI_COMPILER_VERSION__) -#define BROTLI_TI_VERSION \ - BROTLI_MAKE_VERSION((__TI_COMPILER_VERSION__ / 1000000), \ - (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ - (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(BROTLI_TI_VERSION) -#define BROTLI_TI_VERSION_CHECK(major, minor, patch) \ - (BROTLI_TI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_TI_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__IAR_SYSTEMS_ICC__) -#if __VER__ > 1000 -#define BROTLI_IAR_VERSION \ - BROTLI_MAKE_VERSION((__VER__ / 1000000), \ - (__VER__ / 1000) % 1000, \ - (__VER__ % 1000)) -#else -#define BROTLI_IAR_VERSION BROTLI_MAKE_VERSION(VER / 100, __VER__ % 100, 0) -#endif -#endif - -#if defined(BROTLI_IAR_VERSION) -#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) \ - (BROTLI_IAR_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__TINYC__) -#define BROTLI_TINYC_VERSION \ - BROTLI_MAKE_VERSION(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) -#endif - -#if defined(BROTLI_TINYC_VERSION) -#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) \ - (BROTLI_TINYC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) -#else -#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(__has_attribute) -#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ - __has_attribute(attribute) -#else -#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ - BROTLI_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(__has_builtin) -#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \ - __has_builtin(builtin) -#else -#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \ - BROTLI_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(__has_feature) -#define BROTLI_HAS_FEATURE(feature) __has_feature(feature) -#else -#define BROTLI_HAS_FEATURE(feature) (0) -#endif - -#if defined(_WIN32) || defined(__CYGWIN__) -#define BROTLI_PUBLIC -#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \ - BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ - BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \ - BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - (BROTLI_TI_VERSION_CHECK(7, 3, 0) && \ - defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__)) -#define BROTLI_PUBLIC __attribute__ ((visibility ("default"))) -#else -#define BROTLI_PUBLIC -#endif - -/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */ -#if !defined(BROTLI_INTERNAL) -#if defined(_WIN32) || defined(__CYGWIN__) -#define BROTLI_INTERNAL -#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \ - BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ - BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ - BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ - BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \ - BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - (BROTLI_TI_VERSION_CHECK(7, 3, 0) && \ - defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__)) -#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden"))) -#else -#define BROTLI_INTERNAL -#endif -#endif - -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__STDC_NO_VLA__) && !defined(__cplusplus) && \ - !defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__) && \ - !defined(__clang__) -#define BROTLI_ARRAY_PARAM(name) (name) -#else -#define BROTLI_ARRAY_PARAM(name) -#endif - -/* <<< <<< <<< end of hedley macros. */ - -#if defined(BROTLI_SHARED_COMPILATION) -#if defined(_WIN32) -#if defined(BROTLICOMMON_SHARED_COMPILATION) -#define BROTLI_COMMON_API __declspec(dllexport) -#else -#define BROTLI_COMMON_API __declspec(dllimport) -#endif /* BROTLICOMMON_SHARED_COMPILATION */ -#if defined(BROTLIDEC_SHARED_COMPILATION) -#define BROTLI_DEC_API __declspec(dllexport) -#else -#define BROTLI_DEC_API __declspec(dllimport) -#endif /* BROTLIDEC_SHARED_COMPILATION */ -#if defined(BROTLIENC_SHARED_COMPILATION) -#define BROTLI_ENC_API __declspec(dllexport) -#else -#define BROTLI_ENC_API __declspec(dllimport) -#endif /* BROTLIENC_SHARED_COMPILATION */ -#else /* _WIN32 */ -#define BROTLI_COMMON_API BROTLI_PUBLIC -#define BROTLI_DEC_API BROTLI_PUBLIC -#define BROTLI_ENC_API BROTLI_PUBLIC -#endif /* _WIN32 */ -#else /* BROTLI_SHARED_COMPILATION */ -#define BROTLI_COMMON_API -#define BROTLI_DEC_API -#define BROTLI_ENC_API -#endif - -#if defined(BROTLI_BUILD_ENC_EXTRA_API) -#define BROTLI_ENC_EXTRA_API BROTLI_ENC_API -#else -#define BROTLI_ENC_EXTRA_API BROTLI_INTERNAL -#endif - -#endif /* BROTLI_COMMON_PORT_H_ */ diff --git a/src/deps/brotli/include/brotli/shared_dictionary.h b/src/deps/brotli/include/brotli/shared_dictionary.h deleted file mode 100644 index 2970c2dcc9b50..0000000000000 --- a/src/deps/brotli/include/brotli/shared_dictionary.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright 2017 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* (Opaque) Shared Dictionary definition and utilities. */ - -#ifndef BROTLI_COMMON_SHARED_DICTIONARY_H_ -#define BROTLI_COMMON_SHARED_DICTIONARY_H_ - -#include -#include - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH 4 -#define SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH 31 -#define SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS 64 -#define SHARED_BROTLI_MAX_COMPOUND_DICTS 15 - -/** - * Opaque structure that holds shared dictionary data. - * - * Allocated and initialized with ::BrotliSharedDictionaryCreateInstance. - * Cleaned up and deallocated with ::BrotliSharedDictionaryDestroyInstance. - */ -typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionary; - -/** - * Input data type for ::BrotliSharedDictionaryAttach. - */ -typedef enum BrotliSharedDictionaryType { - /** Raw LZ77 prefix dictionary. */ - BROTLI_SHARED_DICTIONARY_RAW = 0, - /** Serialized shared dictionary. - * - * DO NOT USE: methods accepting this value will fail. - */ - BROTLI_SHARED_DICTIONARY_SERIALIZED = 1 -} BrotliSharedDictionaryType; - -/** - * Creates an instance of ::BrotliSharedDictionary. - * - * Fresh instance has default word dictionary and transforms - * and no LZ77 prefix dictionary. - * - * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the - * case they are both zero, default memory allocators are used. @p opaque is - * passed to @p alloc_func and @p free_func when they are called. @p free_func - * has to return without doing anything when asked to free a NULL pointer. - * - * @param alloc_func custom memory allocation function - * @param free_func custom memory free function - * @param opaque custom memory manager handle - * @returns @c 0 if instance can not be allocated or initialized - * @returns pointer to initialized ::BrotliSharedDictionary otherwise - */ -BROTLI_COMMON_API BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance( - brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); - -/** - * Deinitializes and frees ::BrotliSharedDictionary instance. - * - * @param dict shared dictionary instance to be cleaned up and deallocated - */ -BROTLI_COMMON_API void BrotliSharedDictionaryDestroyInstance( - BrotliSharedDictionary* dict); - -/** - * Attaches dictionary to a given instance of ::BrotliSharedDictionary. - * - * Dictionary to be attached is represented in a serialized format as a region - * of memory. - * - * Provided data it partially referenced by a resulting (compound) dictionary, - * and should be kept untouched, while at least one compound dictionary uses it. - * This way memory overhead is kept minimal by the cost of additional resource - * management. - * - * @param dict dictionary to extend - * @param type type of dictionary to attach - * @param data_size size of @p data - * @param data serialized dictionary of type @p type, with at least @p data_size - * addressable bytes - * @returns ::BROTLI_TRUE if provided dictionary is successfully attached - * @returns ::BROTLI_FALSE otherwise - */ -BROTLI_COMMON_API BROTLI_BOOL BrotliSharedDictionaryAttach( - BrotliSharedDictionary* dict, BrotliSharedDictionaryType type, - size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_COMMON_SHARED_DICTIONARY_H_ */ diff --git a/src/deps/brotli/include/brotli/types.h b/src/deps/brotli/include/brotli/types.h deleted file mode 100644 index eff1a3cd07aef..0000000000000 --- a/src/deps/brotli/include/brotli/types.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/** - * @file - * Common types used in decoder and encoder API. - */ - -#ifndef BROTLI_COMMON_TYPES_H_ -#define BROTLI_COMMON_TYPES_H_ - -#include /* for size_t */ - -#if defined(_MSC_VER) && (_MSC_VER < 1600) -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -typedef __int64 int64_t; -#else -#include -#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */ - -/** - * A portable @c bool replacement. - * - * ::BROTLI_BOOL is a "documentation" type: actually it is @c int, but in API it - * denotes a type, whose only values are ::BROTLI_TRUE and ::BROTLI_FALSE. - * - * ::BROTLI_BOOL values passed to Brotli should either be ::BROTLI_TRUE or - * ::BROTLI_FALSE, or be a result of ::TO_BROTLI_BOOL macros. - * - * ::BROTLI_BOOL values returned by Brotli should not be tested for equality - * with @c true, @c false, ::BROTLI_TRUE, ::BROTLI_FALSE, but rather should be - * evaluated, for example: @code{.cpp} - * if (SomeBrotliFunction(encoder, BROTLI_TRUE) && - * !OtherBrotliFunction(decoder, BROTLI_FALSE)) { - * bool x = !!YetAnotherBrotliFunction(encoder, TO_BROLTI_BOOL(2 * 2 == 4)); - * DoSomething(x); - * } - * @endcode - */ -#define BROTLI_BOOL int -/** Portable @c true replacement. */ -#define BROTLI_TRUE 1 -/** Portable @c false replacement. */ -#define BROTLI_FALSE 0 -/** @c bool to ::BROTLI_BOOL conversion macros. */ -#define TO_BROTLI_BOOL(X) (!!(X) ? BROTLI_TRUE : BROTLI_FALSE) - -#define BROTLI_MAKE_UINT64_T(high, low) ((((uint64_t)(high)) << 32) | low) - -#define BROTLI_UINT32_MAX (~((uint32_t)0)) -#define BROTLI_SIZE_MAX (~((size_t)0)) - -/** - * Allocating function pointer type. - * - * @param opaque custom memory manager handle provided by client - * @param size requested memory region size; can not be @c 0 - * @returns @c 0 in the case of failure - * @returns a valid pointer to a memory region of at least @p size bytes - * long otherwise - */ -typedef void* (*brotli_alloc_func)(void* opaque, size_t size); - -/** - * Deallocating function pointer type. - * - * This function @b SHOULD do nothing if @p address is @c 0. - * - * @param opaque custom memory manager handle provided by client - * @param address memory region pointer returned by ::brotli_alloc_func, or @c 0 - */ -typedef void (*brotli_free_func)(void* opaque, void* address); - -#endif /* BROTLI_COMMON_TYPES_H_ */ diff --git a/src/deps/c-ares b/src/deps/c-ares deleted file mode 160000 index d1722e6e8acaf..0000000000000 --- a/src/deps/c-ares +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d1722e6e8acaf10eb73fa995798a9cd421d9f85e diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index df481f2990729..523907ae231e6 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -207,7 +207,7 @@ pub const struct_hostent = extern struct { const array = JSC.JSValue.createEmptyArray(globalThis, 1); const h_name_len = bun.len(this.h_name); const h_name_slice = this.h_name[0..h_name_len]; - array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(h_name_slice).toValueGC(globalThis)); + array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(h_name_slice).toJS(globalThis)); return array; } return JSC.JSValue.createEmptyArray(globalThis, 0); @@ -227,7 +227,7 @@ pub const struct_hostent = extern struct { while (this.h_aliases[count]) |alias| { const alias_len = bun.len(alias); const alias_slice = alias[0..alias_len]; - array.putIndex(globalThis, count, JSC.ZigString.fromUTF8(alias_slice).toValueGC(globalThis)); + array.putIndex(globalThis, count, JSC.ZigString.fromUTF8(alias_slice).toJS(globalThis)); count += 1; } @@ -313,17 +313,17 @@ pub const struct_nameinfo = extern struct { if (this.node != null) { const node_len = bun.len(this.node); const node_slice = this.node[0..node_len]; - array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(node_slice).toValueGC(globalThis)); + array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(node_slice).toJS(globalThis)); } else { - array.putIndex(globalThis, 0, JSC.JSValue.jsUndefined()); + array.putIndex(globalThis, 0, .undefined); } if (this.service != null) { const service_len = bun.len(this.service); const service_slice = this.service[0..service_len]; - array.putIndex(globalThis, 1, JSC.ZigString.fromUTF8(service_slice).toValueGC(globalThis)); + array.putIndex(globalThis, 1, JSC.ZigString.fromUTF8(service_slice).toJS(globalThis)); } else { - array.putIndex(globalThis, 1, JSC.JSValue.jsUndefined()); + array.putIndex(globalThis, 1, .undefined); } return array; @@ -388,7 +388,7 @@ pub const AddrInfo = extern struct { addr_info: *AddrInfo, globalThis: *JSC.JSGlobalObject, ) JSC.JSValue { - var node = addr_info.node.?; + var node = addr_info.node orelse return JSC.JSValue.createEmptyArray(globalThis, 0); const array = JSC.JSValue.createEmptyArray( globalThis, node.count(), @@ -775,7 +775,7 @@ pub const struct_ares_caa_reply = extern struct { const property = this.property[0..this.plength]; const value = this.value[0..this.length]; const property_str = JSC.ZigString.fromUTF8(property); - obj.put(globalThis, &property_str, JSC.ZigString.fromUTF8(value).toValueGC(globalThis)); + obj.put(globalThis, &property_str, JSC.ZigString.fromUTF8(value).toJS(globalThis)); return obj; } @@ -854,13 +854,13 @@ pub const struct_ares_srv_reply = extern struct { // name: 'service.example.com' // } - obj.put(globalThis, JSC.ZigString.static("priority"), JSC.JSValue.jsNumber(this.weight)); + obj.put(globalThis, JSC.ZigString.static("priority"), JSC.JSValue.jsNumber(this.priority)); obj.put(globalThis, JSC.ZigString.static("weight"), JSC.JSValue.jsNumber(this.weight)); obj.put(globalThis, JSC.ZigString.static("port"), JSC.JSValue.jsNumber(this.port)); const len = bun.len(this.host); const host = this.host[0..len]; - obj.put(globalThis, JSC.ZigString.static("name"), JSC.ZigString.fromUTF8(host).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("name"), JSC.ZigString.fromUTF8(host).toJS(globalThis)); return obj; } @@ -934,7 +934,7 @@ pub const struct_ares_mx_reply = extern struct { const host_len = bun.len(this.host); const host = this.host[0..host_len]; - obj.put(globalThis, JSC.ZigString.static("exchange"), JSC.ZigString.fromUTF8(host).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("exchange"), JSC.ZigString.fromUTF8(host).toJS(globalThis)); return obj; } @@ -1005,7 +1005,7 @@ pub const struct_ares_txt_reply = extern struct { pub fn toJS(this: *struct_ares_txt_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { const array = JSC.JSValue.createEmptyArray(globalThis, 1); const value = this.txt[0..this.length]; - array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(value).toValueGC(globalThis)); + array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(value).toJS(globalThis)); return array; } @@ -1090,19 +1090,19 @@ pub const struct_ares_naptr_reply = extern struct { const flags_len = bun.len(this.flags); const flags = this.flags[0..flags_len]; - obj.put(globalThis, JSC.ZigString.static("flags"), JSC.ZigString.fromUTF8(flags).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("flags"), JSC.ZigString.fromUTF8(flags).toJS(globalThis)); const service_len = bun.len(this.service); const service = this.service[0..service_len]; - obj.put(globalThis, JSC.ZigString.static("service"), JSC.ZigString.fromUTF8(service).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("service"), JSC.ZigString.fromUTF8(service).toJS(globalThis)); const regexp_len = bun.len(this.regexp); const regexp = this.regexp[0..regexp_len]; - obj.put(globalThis, JSC.ZigString.static("regexp"), JSC.ZigString.fromUTF8(regexp).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("regexp"), JSC.ZigString.fromUTF8(regexp).toJS(globalThis)); const replacement_len = bun.len(this.replacement); const replacement = this.replacement[0..replacement_len]; - obj.put(globalThis, JSC.ZigString.static("replacement"), JSC.ZigString.fromUTF8(replacement).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("replacement"), JSC.ZigString.fromUTF8(replacement).toJS(globalThis)); return obj; } @@ -1169,11 +1169,11 @@ pub const struct_ares_soa_reply = extern struct { const nsname_len = bun.len(this.nsname); const nsname = this.nsname[0..nsname_len]; - obj.put(globalThis, JSC.ZigString.static("nsname"), JSC.ZigString.fromUTF8(nsname).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("nsname"), JSC.ZigString.fromUTF8(nsname).toJS(globalThis)); const hostmaster_len = bun.len(this.hostmaster); const hostmaster = this.hostmaster[0..hostmaster_len]; - obj.put(globalThis, JSC.ZigString.static("hostmaster"), JSC.ZigString.fromUTF8(hostmaster).toValueGC(globalThis)); + obj.put(globalThis, JSC.ZigString.static("hostmaster"), JSC.ZigString.fromUTF8(hostmaster).toJS(globalThis)); return obj; } @@ -1316,8 +1316,33 @@ pub const Error = enum(i32) { ECANCELLED = ARES_ECANCELLED, ESERVICE = ARES_ESERVICE, + pub fn toJS(this: Error, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + const error_value = globalThis.createErrorInstance("{s}", .{this.label()}); + error_value.put( + globalThis, + JSC.ZigString.static("name"), + JSC.ZigString.init("DNSException").toJS(globalThis), + ); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init(this.code()).toJS(globalThis), + ); + error_value.put( + globalThis, + JSC.ZigString.static("errno"), + JSC.jsNumber(@intFromEnum(this)), + ); + return error_value; + } + pub fn initEAI(rc: i32) ?Error { if (comptime bun.Environment.isWindows) { + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/errors.js#L807-L815 + if (rc == libuv.UV_EAI_NODATA or rc == libuv.UV_EAI_NONAME) { + return Error.ENOTFOUND; + } + // TODO: revisit this return switch (rc) { 0 => null, @@ -1339,18 +1364,35 @@ pub const Error = enum(i32) { }; } - return switch (@as(std.posix.system.EAI, @enumFromInt(rc))) { + const eai: std.posix.system.EAI = @enumFromInt(rc); + + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/errors.js#L807-L815 + if (eai == .NODATA or eai == .NONAME) { + return Error.ENOTFOUND; + } + + if (comptime bun.Environment.isLinux) { + switch (eai) { + .SOCKTYPE => return Error.ECONNREFUSED, + .IDN_ENCODE => return Error.EBADSTR, + .ALLDONE => return Error.ENOTFOUND, + .INPROGRESS => return Error.ETIMEOUT, + .CANCELED => return Error.ECANCELLED, + .NOTCANCELED => return Error.ECANCELLED, + else => {}, + } + } + + return switch (eai) { @as(std.posix.system.EAI, @enumFromInt(0)) => return null, .ADDRFAMILY => Error.EBADFAMILY, .BADFLAGS => Error.EBADFLAGS, // Invalid hints .FAIL => Error.EBADRESP, .FAMILY => Error.EBADFAMILY, .MEMORY => Error.ENOMEM, - .NODATA => Error.ENODATA, - .NONAME => Error.ENONAME, .SERVICE => Error.ESERVICE, .SYSTEM => Error.ESERVFAIL, - else => unreachable, + else => bun.todo(@src(), Error.ENOTIMP), }; } @@ -1411,6 +1453,11 @@ pub const Error = enum(i32) { }); pub fn get(rc: i32) ?Error { + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/errors.js#L807-L815 + if (rc == ARES_ENODATA or rc == ARES_ENONAME) { + return get(ARES_ENOTFOUND); + } + return switch (rc) { 0 => null, 1...ARES_ESERVICE => @as(Error, @enumFromInt(rc)), @@ -1510,7 +1557,7 @@ pub const ares_addr_port_node = struct_ares_addr_port_node; pub export fn Bun__canonicalizeIP( ctx: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { +) callconv(JSC.conv) JSC.JSValue { JSC.markBinding(@src()); const globalThis = ctx.ptr(); @@ -1532,7 +1579,7 @@ pub export fn Bun__canonicalizeIP( const addr_slice = addr.toSlice(bun.default_allocator); const addr_str = addr_slice.slice(); if (addr_str.len >= INET6_ADDRSTRLEN) { - return JSC.JSValue.jsUndefined(); + return .undefined; } var ip_std_text: [INET6_ADDRSTRLEN + 1]u8 = undefined; @@ -1546,18 +1593,19 @@ pub export fn Bun__canonicalizeIP( if (ares_inet_pton(af, &ip_addr, &ip_std_text) != 1) { af = AF.INET6; if (ares_inet_pton(af, &ip_addr, &ip_std_text) != 1) { - return JSC.JSValue.jsUndefined(); + return .undefined; } } // ip_addr will contain the null-terminated string of the cannonicalized IP if (ares_inet_ntop(af, &ip_std_text, &ip_addr, @sizeOf(@TypeOf(ip_addr))) == null) { - return JSC.JSValue.jsUndefined(); + return .undefined; } // use the null-terminated size to return the string const size = bun.len(bun.cast([*:0]u8, &ip_addr)); - return JSC.ZigString.init(ip_addr[0..size]).toValueGC(globalThis); + return JSC.ZigString.init(ip_addr[0..size]).toJS(globalThis); } else { - globalThis.throwInvalidArguments("address must be a string", .{}); + if (!globalThis.hasException()) + globalThis.throwInvalidArguments("address must be a string", .{}); return .zero; } } diff --git a/src/deps/diffz/DiffMatchPatch.zig b/src/deps/diffz/DiffMatchPatch.zig index 405060238b2ce..e6dc26c17c3b0 100644 --- a/src/deps/diffz/DiffMatchPatch.zig +++ b/src/deps/diffz/DiffMatchPatch.zig @@ -432,24 +432,24 @@ fn diffBisect( after: []const u8, deadline: u64, ) DiffError!DiffList { - const before_length = @as(isize, @intCast(before.len)); - const after_length = @as(isize, @intCast(after.len)); - const max_d = @as(isize, @intCast((before.len + after.len + 1) / 2)); + const before_length: isize = @intCast(before.len); + const after_length: isize = @intCast(after.len); + const max_d: isize = @intCast((before.len + after.len + 1) / 2); const v_offset = max_d; const v_length = 2 * max_d; var v1 = try ArrayListUnmanaged(isize).initCapacity(allocator, @as(usize, @intCast(v_length))); - v1.items.len = @as(usize, @intCast(v_length)); + v1.items.len = @intCast(v_length); var v2 = try ArrayListUnmanaged(isize).initCapacity(allocator, @as(usize, @intCast(v_length))); - v2.items.len = @as(usize, @intCast(v_length)); + v2.items.len = @intCast(v_length); var x: usize = 0; while (x < v_length) : (x += 1) { v1.items[x] = -1; v2.items[x] = -1; } - v1.items[@as(usize, @intCast(v_offset + 1))] = 0; - v2.items[@as(usize, @intCast(v_offset + 1))] = 0; + v1.items[@intCast(v_offset + 1)] = 0; + v2.items[@intCast(v_offset + 1)] = 0; const delta = before_length - after_length; // If the total number of characters is odd, then the front path will // collide with the reverse path. @@ -474,20 +474,20 @@ fn diffBisect( const k1_offset = v_offset + k1; var x1: isize = 0; if (k1 == -d or (k1 != d and - v1.items[@as(usize, @intCast(k1_offset - 1))] < v1.items[@as(usize, @intCast(k1_offset + 1))])) + v1.items[@intCast(k1_offset - 1)] < v1.items[@intCast(k1_offset + 1)])) { - x1 = v1.items[@as(usize, @intCast(k1_offset + 1))]; + x1 = v1.items[@intCast(k1_offset + 1)]; } else { - x1 = v1.items[@as(usize, @intCast(k1_offset - 1))] + 1; + x1 = v1.items[@intCast(k1_offset - 1)] + 1; } var y1 = x1 - k1; while (x1 < before_length and - y1 < after_length and before[@as(usize, @intCast(x1))] == after[@as(usize, @intCast(y1))]) + y1 < after_length and before[@intCast(x1)] == after[@intCast(y1)]) { x1 += 1; y1 += 1; } - v1.items[@as(usize, @intCast(k1_offset))] = x1; + v1.items[@intCast(k1_offset)] = x1; if (x1 > before_length) { // Ran off the right of the graph. k1end += 2; @@ -496,9 +496,9 @@ fn diffBisect( k1start += 2; } else if (front) { const k2_offset = v_offset + delta - k1; - if (k2_offset >= 0 and k2_offset < v_length and v2.items[@as(usize, @intCast(k2_offset))] != -1) { + if (k2_offset >= 0 and k2_offset < v_length and v2.items[@intCast(k2_offset)] != -1) { // Mirror x2 onto top-left coordinate system. - const x2 = before_length - v2.items[@as(usize, @intCast(k2_offset))]; + const x2 = before_length - v2.items[@intCast(k2_offset)]; if (x1 >= x2) { // Overlap detected. return dmp.diffBisectSplit(allocator, before, after, x1, y1, deadline); @@ -513,21 +513,21 @@ fn diffBisect( const k2_offset = v_offset + k2; var x2: isize = 0; if (k2 == -d or (k2 != d and - v2.items[@as(usize, @intCast(k2_offset - 1))] < v2.items[@as(usize, @intCast(k2_offset + 1))])) + v2.items[@intCast(k2_offset - 1)] < v2.items[@intCast(k2_offset + 1)])) { - x2 = v2.items[@as(usize, @intCast(k2_offset + 1))]; + x2 = v2.items[@intCast(k2_offset + 1)]; } else { - x2 = v2.items[@as(usize, @intCast(k2_offset - 1))] + 1; + x2 = v2.items[@intCast(k2_offset - 1)] + 1; } var y2: isize = x2 - k2; while (x2 < before_length and y2 < after_length and - before[@as(usize, @intCast(before_length - x2 - 1))] == - after[@as(usize, @intCast(after_length - y2 - 1))]) + before[@intCast(before_length - x2 - 1)] == + after[@intCast(after_length - y2 - 1)]) { x2 += 1; y2 += 1; } - v2.items[@as(usize, @intCast(k2_offset))] = x2; + v2.items[@intCast(k2_offset)] = x2; if (x2 > before_length) { // Ran off the left of the graph. k2end += 2; @@ -536,11 +536,11 @@ fn diffBisect( k2start += 2; } else if (!front) { const k1_offset = v_offset + delta - k2; - if (k1_offset >= 0 and k1_offset < v_length and v1.items[@as(usize, @intCast(k1_offset))] != -1) { - const x1 = v1.items[@as(usize, @intCast(k1_offset))]; + if (k1_offset >= 0 and k1_offset < v_length and v1.items[@intCast(k1_offset)] != -1) { + const x1 = v1.items[@intCast(k1_offset)]; const y1 = v_offset + x1 - k1_offset; // Mirror x2 onto top-left coordinate system. - x2 = before_length - v2.items[@as(usize, @intCast(k2_offset))]; + x2 = before_length - v2.items[@intCast(k2_offset)]; if (x1 >= x2) { // Overlap detected. return dmp.diffBisectSplit(allocator, before, after, x1, y1, deadline); @@ -574,10 +574,10 @@ fn diffBisectSplit( y: isize, deadline: u64, ) DiffError!DiffList { - const text1a = text1[0..@as(usize, @intCast(x))]; - const text2a = text2[0..@as(usize, @intCast(y))]; - const text1b = text1[@as(usize, @intCast(x))..]; - const text2b = text2[@as(usize, @intCast(y))..]; + const text1a = text1[0..@intCast(x)]; + const text2a = text2[0..@intCast(y)]; + const text1b = text1[@intCast(x)..]; + const text2b = text2[@intCast(y)..]; // Compute both diffs serially. var diffs = try dmp.diffInternal(allocator, text1a, text2a, false, deadline); @@ -691,7 +691,7 @@ fn diffLinesToChars( text2: []const u8, ) DiffError!LinesToCharsResult { var line_array = ArrayListUnmanaged([]const u8){}; - var line_hash = std.StringHashMapUnmanaged(usize){}; + var line_hash = bun.StringHashMapUnmanaged(usize){}; // e.g. line_array[4] == "Hello\n" // e.g. line_hash.get("Hello\n") == 4 @@ -716,7 +716,7 @@ fn diffLinesToCharsMunge( allocator: std.mem.Allocator, text: []const u8, line_array: *ArrayListUnmanaged([]const u8), - line_hash: *std.StringHashMapUnmanaged(usize), + line_hash: *bun.StringHashMapUnmanaged(usize), max_lines: usize, ) DiffError![]const u8 { var line_start: isize = 0; @@ -728,22 +728,22 @@ fn diffLinesToCharsMunge( // Modifying text would create many large strings to garbage collect. while (line_end < @as(isize, @intCast(text.len)) - 1) { line_end = b: { - break :b @as(isize, @intCast(std.mem.indexOf(u8, text[@as(usize, @intCast(line_start))..], "\n") orelse - break :b @as(isize, @intCast(text.len - 1)))) + line_start; + break :b @as(isize, @intCast(std.mem.indexOf(u8, text[@intCast(line_start)..], "\n") orelse + break :b @intCast(text.len - 1))) + line_start; }; - line = text[@as(usize, @intCast(line_start)) .. @as(usize, @intCast(line_start)) + @as(usize, @intCast(line_end + 1 - line_start))]; + line = text[@intCast(line_start) .. @as(usize, @intCast(line_start)) + @as(usize, @intCast(line_end + 1 - line_start))]; if (line_hash.get(line)) |value| { - try chars.append(allocator, @as(u8, @intCast(value))); + try chars.append(allocator, @intCast(value)); } else { if (line_array.items.len == max_lines) { // Bail out at 255 because char 256 == char 0. - line = text[@as(usize, @intCast(line_start))..]; - line_end = @as(isize, @intCast(text.len)); + line = text[@intCast(line_start)..]; + line_end = @intCast(text.len); } try line_array.append(allocator, line); try line_hash.put(allocator, line, line_array.items.len - 1); - try chars.append(allocator, @as(u8, @intCast(line_array.items.len - 1))); + try chars.append(allocator, @intCast(line_array.items.len - 1)); } line_start = line_end + 1; } @@ -818,8 +818,9 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *DiffList) DiffError!vo var nt = try allocator.alloc(u8, diffs.items[ii].text.len + common_length); // try diffs.items[pointer - count_delete - count_insert - 1].text.append(allocator, text_insert.items[0..common_length]); - bun.copy(u8, nt, diffs.items[ii].text); - bun.copy(u8, nt[diffs.items[ii].text.len..], text_insert.items[0..common_length]); + const ot = diffs.items[ii].text; + @memcpy(nt[0..ot.len], ot); + @memcpy(nt[ot.len..], text_insert.items[0..common_length]); // allocator.free(diffs.items[ii].text); diffs.items[ii].text = nt; @@ -871,8 +872,9 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *DiffList) DiffError!vo var nt = try allocator.alloc(u8, diffs.items[pointer - 1].text.len + diffs.items[pointer].text.len); // try diffs.items[pointer - count_delete - count_insert - 1].text.append(allocator, text_insert.items[0..common_length]); - bun.copy(u8, nt, diffs.items[pointer - 1].text); - bun.copy(u8, nt[diffs.items[pointer - 1].text.len..], diffs.items[pointer].text); + const ot = diffs.items[pointer - 1].text; + @memcpy(nt[0..ot.len], ot); + @memcpy(nt[ot.len..], diffs.items[pointer].text); // allocator.free(diffs.items[pointer - 1].text); diffs.items[pointer - 1].text = nt; @@ -980,18 +982,18 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *DiffList) DiffError var length_insertions2: usize = 0; var length_deletions2: usize = 0; while (pointer < diffs.items.len) { - if (diffs.items[@as(usize, @intCast(pointer))].operation == .equal) { // Equality found. + if (diffs.items[@intCast(pointer)].operation == .equal) { // Equality found. try equalities.append(allocator, pointer); length_insertions1 = length_insertions2; length_deletions1 = length_deletions2; length_insertions2 = 0; length_deletions2 = 0; - last_equality = diffs.items[@as(usize, @intCast(pointer))].text; + last_equality = diffs.items[@intCast(pointer)].text; } else { // an insertion or deletion - if (diffs.items[@as(usize, @intCast(pointer))].operation == .insert) { - length_insertions2 += diffs.items[@as(usize, @intCast(pointer))].text.len; + if (diffs.items[@intCast(pointer)].operation == .insert) { + length_insertions2 += diffs.items[@intCast(pointer)].text.len; } else { - length_deletions2 += diffs.items[@as(usize, @intCast(pointer))].text.len; + length_deletions2 += diffs.items[@intCast(pointer)].text.len; } // Eliminate an equality that is smaller or equal to the edits on both // sides of it. @@ -1002,11 +1004,11 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *DiffList) DiffError // Duplicate record. try diffs.insert( allocator, - @as(usize, @intCast(equalities.items[equalities.items.len - 1])), + @intCast(equalities.items[equalities.items.len - 1]), Diff.init(.delete, try allocator.dupe(u8, last_equality.?)), ); // Change second copy to insert. - diffs.items[@as(usize, @intCast(equalities.items[equalities.items.len - 1] + 1))].operation = .insert; + diffs.items[@intCast(equalities.items[equalities.items.len - 1] + 1)].operation = .insert; // Throw away the equality we just deleted. _ = equalities.pop(); if (equalities.items.len > 0) { @@ -1038,11 +1040,11 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *DiffList) DiffError // Only extract an overlap if it is as big as the edit ahead or behind it. pointer = 1; while (pointer < diffs.items.len) { - if (diffs.items[@as(usize, @intCast(pointer - 1))].operation == .delete and - diffs.items[@as(usize, @intCast(pointer))].operation == .insert) + if (diffs.items[@intCast(pointer - 1)].operation == .delete and + diffs.items[@intCast(pointer)].operation == .insert) { - const deletion = diffs.items[@as(usize, @intCast(pointer - 1))].text; - const insertion = diffs.items[@as(usize, @intCast(pointer))].text; + const deletion = diffs.items[@intCast(pointer - 1)].text; + const insertion = diffs.items[@intCast(pointer)].text; const overlap_length1: usize = diffCommonOverlap(deletion, insertion); const overlap_length2: usize = diffCommonOverlap(insertion, deletion); if (overlap_length1 >= overlap_length2) { @@ -1053,12 +1055,12 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *DiffList) DiffError // Insert an equality and trim the surrounding edits. try diffs.insert( allocator, - @as(usize, @intCast(pointer)), + @intCast(pointer), Diff.init(.equal, try allocator.dupe(u8, insertion[0..overlap_length1])), ); - diffs.items[@as(usize, @intCast(pointer - 1))].text = + diffs.items[@intCast(pointer - 1)].text = try allocator.dupe(u8, deletion[0 .. deletion.len - overlap_length1]); - diffs.items[@as(usize, @intCast(pointer + 1))].text = + diffs.items[@intCast(pointer + 1)].text = try allocator.dupe(u8, insertion[overlap_length1..]); pointer += 1; } @@ -1070,14 +1072,14 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *DiffList) DiffError // Insert an equality and swap and trim the surrounding edits. try diffs.insert( allocator, - @as(usize, @intCast(pointer)), + @intCast(pointer), Diff.init(.equal, try allocator.dupe(u8, deletion[0..overlap_length2])), ); - diffs.items[@as(usize, @intCast(pointer - 1))].operation = .insert; - diffs.items[@as(usize, @intCast(pointer - 1))].text = + diffs.items[@intCast(pointer - 1)].operation = .insert; + diffs.items[@intCast(pointer - 1)].text = try allocator.dupe(u8, insertion[0 .. insertion.len - overlap_length2]); - diffs.items[@as(usize, @intCast(pointer + 1))].operation = .delete; - diffs.items[@as(usize, @intCast(pointer + 1))].text = + diffs.items[@intCast(pointer + 1)].operation = .delete; + diffs.items[@intCast(pointer + 1)].text = try allocator.dupe(u8, deletion[overlap_length2..]); pointer += 1; } diff --git a/src/deps/libarchive b/src/deps/libarchive deleted file mode 160000 index 313aa1fa10b65..0000000000000 --- a/src/deps/libarchive +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 313aa1fa10b657de791e3202c168a6c833bc3543 diff --git a/src/deps/libdeflate.zig b/src/deps/libdeflate.zig new file mode 100644 index 0000000000000..d38d6dcb9faa9 --- /dev/null +++ b/src/deps/libdeflate.zig @@ -0,0 +1,149 @@ +const std = @import("std"); +const bun = @import("root").bun; +pub const Options = extern struct { + sizeof_options: usize = @sizeOf(Options), + malloc_func: ?*const fn (usize) callconv(.C) ?*anyopaque = @import("std").mem.zeroes(?*const fn (usize) callconv(.C) ?*anyopaque), + free_func: ?*const fn (?*anyopaque) callconv(.C) void = @import("std").mem.zeroes(?*const fn (?*anyopaque) callconv(.C) void), +}; +pub extern fn libdeflate_alloc_compressor(compression_level: c_int) ?*Compressor; +pub extern fn libdeflate_alloc_compressor_ex(compression_level: c_int, options: ?*const Options) ?*Compressor; +pub extern fn libdeflate_deflate_compress(compressor: *Compressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize) usize; +pub extern fn libdeflate_deflate_compress_bound(compressor: *Compressor, in_nbytes: usize) usize; +pub extern fn libdeflate_zlib_compress(compressor: *Compressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize) usize; +pub extern fn libdeflate_zlib_compress_bound(compressor: *Compressor, in_nbytes: usize) usize; +pub extern fn libdeflate_gzip_compress(compressor: *Compressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize) usize; +pub extern fn libdeflate_gzip_compress_bound(compressor: *Compressor, in_nbytes: usize) usize; +pub extern fn libdeflate_free_compressor(compressor: *Compressor) void; + +fn load_once() void { + libdeflate_set_memory_allocator(bun.Mimalloc.mi_malloc, bun.Mimalloc.mi_free); +} + +var loaded_once = std.once(load_once); + +pub fn load() void { + loaded_once.call(); +} + +pub const Compressor = opaque { + pub fn alloc(compression_level: c_int) ?*Compressor { + return libdeflate_alloc_compressor(compression_level); + } + + pub fn alloc_ex(compression_level: c_int, options: ?*const Options) ?*Compressor { + return libdeflate_alloc_compressor_ex(compression_level, options); + } + + pub fn deinit(this: *Compressor) void { + return libdeflate_free_compressor(this); + } + + /// Compresses `input` into `output` and returns the number of bytes written. + pub fn inflate(this: *Compressor, input: []const u8, output: []u8) Result { + const written = libdeflate_deflate_compress(this, input.ptr, input.len, output.ptr, output.len); + return Result{ .read = input.len, .written = written, .status = Status.success }; + } + + pub fn maxBytesNeeded(this: *Compressor, input: []const u8, encoding: Encoding) usize { + return switch (encoding) { + Encoding.deflate => return libdeflate_deflate_compress_bound(this, input.len), + Encoding.zlib => return libdeflate_zlib_compress_bound(this, input.len), + Encoding.gzip => return libdeflate_gzip_compress_bound(this, input.len), + }; + } + + pub fn compress(this: *Compressor, input: []const u8, output: []u8, encoding: Encoding) Result { + switch (encoding) { + Encoding.deflate => return this.inflate(input, output), + Encoding.zlib => return this.zlib(input, output), + Encoding.gzip => return this.gzip(input, output), + } + } + + pub fn zlib(this: *Compressor, input: []const u8, output: []u8) Result { + const result = libdeflate_zlib_compress(this, input.ptr, input.len, output.ptr, output.len); + return Result{ .read = input.len, .written = result, .status = Status.success }; + } + + pub fn gzip(this: *Compressor, input: []const u8, output: []u8) Result { + const result = libdeflate_gzip_compress(this, input.ptr, input.len, output.ptr, output.len); + return Result{ .read = input.len, .written = result, .status = Status.success }; + } +}; + +pub const Decompressor = opaque { + pub fn alloc() ?*Decompressor { + return libdeflate_alloc_decompressor(); + } + + pub fn deinit(this: *Decompressor) void { + return libdeflate_free_decompressor(this); + } + + pub fn deflate(this: *Decompressor, input: []const u8, output: []u8) Result { + var actual_in_bytes_ret: usize = input.len; + var actual_out_bytes_ret: usize = output.len; + const result = libdeflate_deflate_decompress_ex(this, input.ptr, input.len, output.ptr, output.len, &actual_in_bytes_ret, &actual_out_bytes_ret); + return Result{ .read = actual_in_bytes_ret, .written = actual_out_bytes_ret, .status = result }; + } + + pub fn zlib(this: *Decompressor, input: []const u8, output: []u8) Result { + var actual_in_bytes_ret: usize = input.len; + var actual_out_bytes_ret: usize = output.len; + const result = libdeflate_zlib_decompress_ex(this, input.ptr, input.len, output.ptr, output.len, &actual_in_bytes_ret, &actual_out_bytes_ret); + return Result{ .read = actual_in_bytes_ret, .written = actual_out_bytes_ret, .status = result }; + } + + pub fn gzip(this: *Decompressor, input: []const u8, output: []u8) Result { + var actual_in_bytes_ret: usize = input.len; + var actual_out_bytes_ret: usize = output.len; + const result = libdeflate_gzip_decompress_ex(this, input.ptr, input.len, output.ptr, output.len, &actual_in_bytes_ret, &actual_out_bytes_ret); + return Result{ .read = actual_in_bytes_ret, .written = actual_out_bytes_ret, .status = result }; + } + + pub fn decompress(this: *Decompressor, input: []const u8, output: []u8, encoding: Encoding) Result { + switch (encoding) { + Encoding.deflate => return this.deflate(input, output), + Encoding.zlib => return this.zlib(input, output), + Encoding.gzip => return this.gzip(input, output), + } + } +}; + +pub const Result = struct { + read: usize, + written: usize, + status: Status, +}; + +pub const Encoding = enum { + deflate, + zlib, + gzip, +}; + +pub extern fn libdeflate_alloc_decompressor() ?*Decompressor; +pub extern fn libdeflate_alloc_decompressor_ex(options: ?*const Options) ?*Decompressor; +pub const LIBDEFLATE_SUCCESS = 0; +pub const LIBDEFLATE_BAD_DATA = 1; +pub const LIBDEFLATE_SHORT_OUTPUT = 2; +pub const LIBDEFLATE_INSUFFICIENT_SPACE = 3; +pub const Status = enum(c_uint) { + success = LIBDEFLATE_SUCCESS, + bad_data = LIBDEFLATE_BAD_DATA, + short_output = LIBDEFLATE_SHORT_OUTPUT, + insufficient_space = LIBDEFLATE_INSUFFICIENT_SPACE, +}; +pub extern fn libdeflate_deflate_decompress(decompressor: *Decompressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize, actual_out_nbytes_ret: *usize) Status; +pub extern fn libdeflate_deflate_decompress_ex(decompressor: *Decompressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize, actual_in_nbytes_ret: *usize, actual_out_nbytes_ret: *usize) Status; +pub extern fn libdeflate_zlib_decompress(decompressor: *Decompressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize, actual_out_nbytes_ret: *usize) Status; +pub extern fn libdeflate_zlib_decompress_ex(decompressor: *Decompressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize, actual_in_nbytes_ret: *usize, actual_out_nbytes_ret: *usize) Status; +pub extern fn libdeflate_gzip_decompress(decompressor: *Decompressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize, actual_out_nbytes_ret: *usize) Status; +pub extern fn libdeflate_gzip_decompress_ex(decompressor: *Decompressor, in: ?*const anyopaque, in_nbytes: usize, out: ?*anyopaque, out_nbytes_avail: usize, actual_in_nbytes_ret: *usize, actual_out_nbytes_ret: *usize) Status; +pub extern fn libdeflate_free_decompressor(decompressor: *Decompressor) void; +pub extern fn libdeflate_adler32(adler: u32, buffer: ?*const anyopaque, len: usize) u32; +pub extern fn libdeflate_crc32(crc: u32, buffer: ?*const anyopaque, len: usize) u32; +pub extern fn libdeflate_set_memory_allocator(malloc_func: ?*const fn (usize) callconv(.C) ?*anyopaque, free_func: ?*const fn (?*anyopaque) callconv(.C) void) void; +pub const libdeflate_compressor = Compressor; +pub const libdeflate_options = Options; +pub const libdeflate_decompressor = Decompressor; diff --git a/src/deps/libuv.zig b/src/deps/libuv.zig index 2aecf0ddb0d1c..0940fdf3de351 100644 --- a/src/deps/libuv.zig +++ b/src/deps/libuv.zig @@ -1304,14 +1304,15 @@ pub const Pipe = extern struct { return .{ .result = {} }; } - pub fn open(this: *Pipe, file: uv_file) Maybe(void) { - if (uv_pipe_open(this, file).toError(.open)) |err| return .{ .err = err }; + pub fn open(this: *Pipe, file: bun.FileDescriptor) Maybe(void) { + const uv_fd = bun.uvfdcast(file); + if (uv_pipe_open(this, uv_fd).toError(.open)) |err| return .{ .err = err }; return .{ .result = {} }; } pub fn listenNamedPipe(this: *@This(), named_pipe: []const u8, backlog: i32, context: anytype, comptime onClientConnect: *const (fn (@TypeOf(context), ReturnCode) void)) Maybe(void) { - if (this.bind(named_pipe, 0).asErr()) |err| { + if (this.bind(named_pipe, UV_PIPE_NO_TRUNCATE).asErr()) |err| { return .{ .err = err }; } return this.listen(backlog, context, onClientConnect); @@ -1331,8 +1332,7 @@ pub const Pipe = extern struct { onConnect(@ptrCast(@alignCast(handle.data)), status); } }; - - if (uv_pipe_connect2(req, this, @ptrCast(name.ptr), name.len, 0, &Wrapper.uvConnectCb).toError(.connect2)) |err| { + if (uv_pipe_connect2(req, this, @ptrCast(name.ptr), name.len, UV_PIPE_NO_TRUNCATE, &Wrapper.uvConnectCb).toError(.connect2)) |err| { return .{ .err = err }; } return .{ .result = {} }; @@ -2644,7 +2644,7 @@ pub fn translateUVErrorToE(code_in: anytype) bun.C.E { UV_EREMOTEIO => bun.C.E.REMOTEIO, UV_ECANCELED => bun.C.E.CANCELED, UV_ECHARSET => bun.C.E.CHARSET, - UV_EOF => bun.C.E.OF, + UV_EOF => bun.C.E.EOF, else => @enumFromInt(-code), }; } @@ -2749,7 +2749,7 @@ pub const ReturnCode = enum(c_int) { UV_EREMOTEIO => @intFromEnum(bun.C.E.REMOTEIO), UV_ECANCELED => @intFromEnum(bun.C.E.CANCELED), UV_ECHARSET => @intFromEnum(bun.C.E.CHARSET), - UV_EOF => @intFromEnum(bun.C.E.OF), + UV_EOF => @intFromEnum(bun.C.E.EOF), else => null, } else diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index e744c8859a321..bc9ff248f8c23 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -1,5 +1,6 @@ // clang-format off #include "_libusockets.h" +#include "libusockets.h" #include #include #include @@ -7,6 +8,8 @@ extern "C" const char* ares_inet_ntop(int af, const char *src, char *dst, size_t size); +#define uws_res_r uws_res_t* nonnull_arg + extern "C" { @@ -23,6 +26,20 @@ extern "C" return (uws_app_t *)new uWS::App(); } + void uws_app_clear_routes(int ssl, uws_app_t *app) + { + if (ssl) + { + uWS::SSLApp *uwsApp = (uWS::SSLApp *)app; + uwsApp->clearRoutes(); + } + else + { + uWS::App *uwsApp = (uWS::App *)app; + uwsApp->clearRoutes(); + } + } + void uws_app_get(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data) { if (ssl) @@ -180,8 +197,9 @@ extern "C" } } - void uws_app_head(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data) + void uws_app_head(int ssl, uws_app_t *app, const char *pattern_ptr, size_t pattern_len, uws_method_handler handler, void *user_data) { + std::string pattern = std::string(pattern_ptr, pattern_len); if (ssl) { uWS::SSLApp *uwsApp = (uWS::SSLApp *)app; @@ -191,7 +209,7 @@ extern "C" return; } uwsApp->head(pattern, [handler, user_data](auto *res, auto *req) - { handler((uws_res_t *)res, (uws_req_t *)req, user_data); }); + { handler((uws_res_t *)res, (uws_req_t *)req, user_data); }); } else { @@ -202,10 +220,9 @@ extern "C" return; } uwsApp->head(pattern, [handler, user_data](auto *res, auto *req) - { handler((uws_res_t *)res, (uws_req_t *)req, user_data); }); + { handler((uws_res_t *)res, (uws_req_t *)req, user_data); }); } } - void uws_app_connect(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data) { if (ssl) @@ -258,8 +275,9 @@ extern "C" } } - void uws_app_any(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data) + void uws_app_any(int ssl, uws_app_t *app, const char *pattern_ptr, size_t pattern_len, uws_method_handler handler, void *user_data) { + std::string pattern = std::string(pattern_ptr, pattern_len); if (ssl) { uWS::SSLApp *uwsApp = (uWS::SSLApp *)app; @@ -1015,44 +1033,40 @@ extern "C" return value.length(); } - void uws_res_end(int ssl, uws_res_t *res, const char *data, size_t length, + void uws_res_end(int ssl, uws_res_r res, const char *data, size_t length, bool close_connection) { if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; - uwsRes->getHttpResponseData()->onWritable = nullptr; - uwsRes->onAborted(nullptr); + uwsRes->clearOnWritableAndAborted(); uwsRes->end(std::string_view(data, length), close_connection); } else { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; - uwsRes->getHttpResponseData()->onWritable = nullptr; - uwsRes->onAborted(nullptr); + uwsRes->clearOnWritableAndAborted(); uwsRes->end(std::string_view(data, length), close_connection); } } - void uws_res_end_stream(int ssl, uws_res_t *res, bool close_connection) + void uws_res_end_stream(int ssl, uws_res_r res, bool close_connection) { if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; - uwsRes->getHttpResponseData()->onWritable = nullptr; - uwsRes->onAborted(nullptr); + uwsRes->clearOnWritableAndAborted(); uwsRes->sendTerminatingChunk(close_connection); } else { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; - uwsRes->getHttpResponseData()->onWritable = nullptr; - uwsRes->onAborted(nullptr); + uwsRes->clearOnWritableAndAborted(); uwsRes->sendTerminatingChunk(close_connection); } } - void uws_res_pause(int ssl, uws_res_t *res) + void uws_res_pause(int ssl, uws_res_r res) { if (ssl) { @@ -1066,7 +1080,7 @@ extern "C" } } - void uws_res_resume(int ssl, uws_res_t *res) + void uws_res_resume(int ssl, uws_res_r res) { if (ssl) { @@ -1080,7 +1094,7 @@ extern "C" } } - void uws_res_write_continue(int ssl, uws_res_t *res) + void uws_res_write_continue(int ssl, uws_res_r res) { if (ssl) { @@ -1094,7 +1108,7 @@ extern "C" } } - void uws_res_write_status(int ssl, uws_res_t *res, const char *status, + void uws_res_write_status(int ssl, uws_res_r res, const char *status, size_t length) { if (ssl) @@ -1109,7 +1123,7 @@ extern "C" } } - void uws_res_write_header(int ssl, uws_res_t *res, const char *key, + void uws_res_write_header(int ssl, uws_res_r res, const char *key, size_t key_length, const char *value, size_t value_length) { @@ -1126,7 +1140,7 @@ extern "C" std::string_view(value, value_length)); } } - void uws_res_write_header_int(int ssl, uws_res_t *res, const char *key, + void uws_res_write_header_int(int ssl, uws_res_r res, const char *key, size_t key_length, uint64_t value) { if (ssl) @@ -1141,8 +1155,47 @@ extern "C" uwsRes->writeHeader(std::string_view(key, key_length), value); } } + void uws_res_end_sendfile(int ssl, uws_res_r res, uint64_t offset, bool close_connection) + { + if (ssl) + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto *data = uwsRes->getHttpResponseData(); + data->offset = offset; + data->state |= uWS::HttpResponseData::HTTP_END_CALLED; + data->markDone(); + uwsRes->resetTimeout(); + } + else + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto *data = uwsRes->getHttpResponseData(); + data->offset = offset; + data->state |= uWS::HttpResponseData::HTTP_END_CALLED; + data->markDone(); + uwsRes->resetTimeout(); + } + } + void uws_res_reset_timeout(int ssl, uws_res_r res) { + if (ssl) { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->resetTimeout(); + } else { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->resetTimeout(); + } + } + void uws_res_timeout(int ssl, uws_res_r res, uint8_t seconds) { + if (ssl) { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->setTimeout(seconds); + } else { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->setTimeout(seconds); + } + } - void uws_res_end_without_body(int ssl, uws_res_t *res, bool close_connection) + void uws_res_end_without_body(int ssl, uws_res_r res, bool close_connection) { if (ssl) { @@ -1162,7 +1215,7 @@ extern "C" } data->state |= uWS::HttpResponseData::HTTP_END_CALLED; data->markDone(); - us_socket_timeout(true, (us_socket_t *)uwsRes, uWS::HTTP_TIMEOUT_S); + uwsRes->resetTimeout(); } else { @@ -1184,11 +1237,13 @@ extern "C" } data->state |= uWS::HttpResponseData::HTTP_END_CALLED; data->markDone(); - us_socket_timeout(false, (us_socket_t *)uwsRes, uWS::HTTP_TIMEOUT_S); + uwsRes->resetTimeout(); } } - bool uws_res_write(int ssl, uws_res_t *res, const char *data, size_t length) + bool uws_res_write(int ssl, uws_res_r res, const char *data, size_t length) nonnull_fn_decl; + + bool uws_res_write(int ssl, uws_res_r res, const char *data, size_t length) { if (ssl) { @@ -1198,7 +1253,8 @@ extern "C" uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; return uwsRes->write(std::string_view(data, length)); } - uint64_t uws_res_get_write_offset(int ssl, uws_res_t *res) + uint64_t uws_res_get_write_offset(int ssl, uws_res_r res) nonnull_fn_decl; + uint64_t uws_res_get_write_offset(int ssl, uws_res_r res) { if (ssl) { @@ -1209,7 +1265,8 @@ extern "C" return uwsRes->getWriteOffset(); } - bool uws_res_has_responded(int ssl, uws_res_t *res) + bool uws_res_has_responded(int ssl, uws_res_r res) nonnull_fn_decl; + bool uws_res_has_responded(int ssl, uws_res_r res) { if (ssl) { @@ -1220,26 +1277,26 @@ extern "C" return uwsRes->hasResponded(); } - void uws_res_on_writable(int ssl, uws_res_t *res, - bool (*handler)(uws_res_t *res, uint64_t, + void uws_res_on_writable(int ssl, uws_res_r res, + bool (*handler)(uws_res_r res, uint64_t, void *opcional_data), void *opcional_data) { if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; - uwsRes->onWritable([handler, res, opcional_data](uint64_t a) - { return handler(res, a, opcional_data); }); + auto onWritable = reinterpret_cast*, uint64_t, void*)>(handler); + uwsRes->onWritable(opcional_data, onWritable); } else { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; - uwsRes->onWritable([handler, res, opcional_data](uint64_t a) - { return handler(res, a, opcional_data); }); + auto onWritable = reinterpret_cast*, uint64_t, void*)>(handler); + uwsRes->onWritable(opcional_data, onWritable); } } - void uws_res_clear_on_writable(int ssl, uws_res_t *res) { + void uws_res_clear_on_writable(int ssl, uws_res_r res) { if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; uwsRes->clearOnWritable(); @@ -1249,42 +1306,72 @@ extern "C" } } - void uws_res_on_aborted(int ssl, uws_res_t *res, - void (*handler)(uws_res_t *res, void *opcional_data), + void uws_res_on_aborted(int ssl, uws_res_r res, + void (*handler)(uws_res_r res, void *opcional_data), void *opcional_data) { if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto* onAborted = reinterpret_cast*, void*)>(handler); if (handler) { - uwsRes->onAborted( - [handler, res, opcional_data] - { handler(res, opcional_data); }); + uwsRes->onAborted(opcional_data, onAborted); } else { - uwsRes->onAborted(nullptr); + uwsRes->clearOnAborted(); } } else { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto* onAborted = reinterpret_cast*, void*)>(handler); if (handler) { - uwsRes->onAborted( - [handler, res, opcional_data] - { handler(res, opcional_data); }); + uwsRes->onAborted(opcional_data, onAborted); } else { - uwsRes->onAborted(nullptr); + uwsRes->clearOnAborted(); } } } - void uws_res_on_data(int ssl, uws_res_t *res, - void (*handler)(uws_res_t *res, const char *chunk, + void uws_res_on_timeout(int ssl, uws_res_r res, + void (*handler)(uws_res_r res, void *opcional_data), + void *opcional_data) + { + if (ssl) + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto* onTimeout = reinterpret_cast*, void*)>(handler); + if (handler) + { + uwsRes->onTimeout(opcional_data, onTimeout); + } + else + { + uwsRes->clearOnTimeout(); + } + } + else + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto* onTimeout = reinterpret_cast*, void*)>(handler); + if (handler) + { + uwsRes->onTimeout(opcional_data, onTimeout); + } + else + { + uwsRes->clearOnTimeout(); + } + } + } + + void uws_res_on_data(int ssl, uws_res_r res, + void (*handler)(uws_res_r res, const char *chunk, size_t chunk_length, bool is_end, void *opcional_data), void *opcional_data) @@ -1292,21 +1379,21 @@ extern "C" if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto onData = reinterpret_cast* response, const char* chunk, size_t chunk_length, bool, void*)>(handler); if (handler) { - uwsRes->onData([handler, res, opcional_data](auto chunk, bool is_end) - { handler(res, chunk.data(), chunk.length(), is_end, opcional_data); }); + uwsRes->onData(opcional_data, onData); } else { - uwsRes->onData(nullptr); + uwsRes->onData(opcional_data, nullptr); } } else { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + auto onData = reinterpret_cast* response, const char* chunk, size_t chunk_length, bool, void*)>(handler); if (handler) { - uwsRes->onData([handler, res, opcional_data](auto chunk, bool is_end) - { handler(res, chunk.data(), chunk.length(), is_end, opcional_data); }); + uwsRes->onData(opcional_data, onData); } else { - uwsRes->onData(nullptr); + uwsRes->onData(opcional_data, nullptr); } } } @@ -1345,6 +1432,9 @@ extern "C" return value.length(); } +size_t uws_req_get_header(uws_req_t *res, const char *lower_case_header, + size_t lower_case_header_length, const char **dest) nonnull_fn_decl; + size_t uws_req_get_header(uws_req_t *res, const char *lower_case_header, size_t lower_case_header_length, const char **dest) { @@ -1384,7 +1474,7 @@ extern "C" return value.length(); } - void uws_res_upgrade(int ssl, uws_res_t *res, void *data, + void uws_res_upgrade(int ssl, uws_res_r res, void *data, const char *sec_web_socket_key, size_t sec_web_socket_key_length, const char *sec_web_socket_protocol, @@ -1456,9 +1546,12 @@ extern "C" { cb(ctx); }); } - void uws_res_write_headers(int ssl, uws_res_t *res, const StringPointer *names, + void uws_res_write_headers(int ssl, uws_res_r res, const StringPointer *names, const StringPointer *values, size_t count, - const char *buf) + const char *buf) nonnull_fn_decl; + void uws_res_write_headers(int ssl, uws_res_r res, const StringPointer *names, + const StringPointer *values, size_t count, + const char *buf) { if (ssl) { @@ -1480,7 +1573,7 @@ extern "C" } } - void uws_res_uncork(int ssl, uws_res_t *res) + void uws_res_uncork(int ssl, uws_res_r res) { if (ssl) { @@ -1494,15 +1587,15 @@ extern "C" } } - void us_socket_mark_needs_more_not_ssl(uws_res_t *res) + void us_socket_mark_needs_more_not_ssl(uws_res_r res) { - us_socket_t *s = (us_socket_t *)res; + us_socket_r s = (us_socket_t *)res; s->context->loop->data.last_write_failed = 1; us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); } - void uws_res_override_write_offset(int ssl, uws_res_t *res, uint64_t offset) + void uws_res_override_write_offset(int ssl, uws_res_r res, uint64_t offset) { if (ssl) { @@ -1514,7 +1607,11 @@ extern "C" } } - void uws_res_cork(int ssl, uws_res_t *res, void *ctx, +__attribute__((callback (corker, ctx))) + void uws_res_cork(int ssl, uws_res_r res, void *ctx, + void (*corker)(void *ctx)) nonnull_fn_decl; + + void uws_res_cork(int ssl, uws_res_r res, void *ctx, void (*corker)(void *ctx)) { if (ssl) @@ -1531,11 +1628,12 @@ extern "C" } } - void uws_res_prepare_for_sendfile(int ssl, uws_res_t *res) + void uws_res_prepare_for_sendfile(int ssl, uws_res_r res) { if (ssl) { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->writeMark(); auto pair = uwsRes->getSendBuffer(2); char *ptr = pair.first; ptr[0] = '\r'; @@ -1545,6 +1643,7 @@ extern "C" else { uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->writeMark(); auto pair = uwsRes->getSendBuffer(2); char *ptr = pair.first; ptr[0] = '\r'; @@ -1553,7 +1652,7 @@ extern "C" } } - bool uws_res_try_end(int ssl, uws_res_t *res, const char *bytes, size_t len, + bool uws_res_try_end(int ssl, uws_res_r res, const char *bytes, size_t len, size_t total_len, bool close) { if (ssl) @@ -1561,8 +1660,7 @@ extern "C" uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; auto pair = uwsRes->tryEnd(std::string_view(bytes, len), total_len, close); if (pair.first) { - uwsRes->getHttpResponseData()->onWritable = nullptr; - uwsRes->onAborted(nullptr); + uwsRes->clearOnWritableAndAborted(); } return pair.first; @@ -1572,15 +1670,14 @@ extern "C" uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; auto pair = uwsRes->tryEnd(std::string_view(bytes, len), total_len, close); if (pair.first) { - uwsRes->getHttpResponseData()->onWritable = nullptr; - uwsRes->onAborted(nullptr); + uwsRes->clearOnWritableAndAborted(); } return pair.first; } } - int uws_res_state(int ssl, uws_res_t *res) + int uws_res_state(int ssl, uws_res_r res) { if (ssl) { @@ -1594,7 +1691,7 @@ extern "C" } } - void *uws_res_get_native_handle(int ssl, uws_res_t *res) + void *uws_res_get_native_handle(int ssl, uws_res_r res) { if (ssl) { @@ -1608,14 +1705,14 @@ extern "C" } } - void us_socket_sendfile_needs_more(us_socket_t *s) { + void us_socket_sendfile_needs_more(us_socket_r s) { s->context->loop->data.last_write_failed = 1; us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); } // Gets the remote address and port // Returns 0 if failure / unix socket - uint64_t uws_res_get_remote_address_info(uws_res_t *res, const char **dest, int *port, bool *is_ipv6) + uint64_t uws_res_get_remote_address_info(uws_res_r res, const char **dest, int *port, bool *is_ipv6) { // This function is manual inlining + modification of // us_socket_remote_address @@ -1638,4 +1735,9 @@ extern "C" return strlen(*dest); } } + + // we need to manually call this at thread exit + extern "C" void bun_clear_loop_at_thread_exit() { + uWS::Loop::clearLoopAtThreadExit(); + } } diff --git a/src/deps/lol-html b/src/deps/lol-html deleted file mode 160000 index 8d4c273ded322..0000000000000 --- a/src/deps/lol-html +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d4c273ded322193d017042d1f48df2766b0f88b diff --git a/src/deps/ls-hpack b/src/deps/ls-hpack deleted file mode 160000 index 3d0f1fc1d6e66..0000000000000 --- a/src/deps/ls-hpack +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0 diff --git a/src/deps/mimalloc b/src/deps/mimalloc deleted file mode 160000 index 4c283af60cdae..0000000000000 --- a/src/deps/mimalloc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4c283af60cdae205df5a872530c77e2a6a307d43 diff --git a/src/deps/picohttpparser b/src/deps/picohttpparser deleted file mode 160000 index 066d2b1e9ab82..0000000000000 --- a/src/deps/picohttpparser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 066d2b1e9ab820703db0837a7255d92d30f0c9f5 diff --git a/src/deps/picohttpparser.c b/src/deps/picohttpparser.c deleted file mode 100644 index c762af9e83977..0000000000000 --- a/src/deps/picohttpparser.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, - * Shigeo Mitsunari - * - * The software is licensed under either the MIT License (below) or the Perl - * license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include -#ifdef __SSE4_2__ -#ifdef _MSC_VER -#include -#else -#include -#endif -#endif -#include "picohttpparser.h" - -#if __GNUC__ >= 3 -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#else -#define likely(x) (x) -#define unlikely(x) (x) -#endif - -#ifdef _MSC_VER -#define ALIGNED(n) _declspec(align(n)) -#else -#define ALIGNED(n) __attribute__((aligned(n))) -#endif - -#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) - -#define CHECK_EOF() \ - if (buf == buf_end) { \ - *ret = -2; \ - return NULL; \ - } - -#define EXPECT_CHAR_NO_CHECK(ch) \ - if (*buf++ != ch) { \ - *ret = -1; \ - return NULL; \ - } - -#define EXPECT_CHAR(ch) \ - CHECK_EOF(); \ - EXPECT_CHAR_NO_CHECK(ch); - -#define ADVANCE_TOKEN(tok, toklen) \ - do { \ - const char *tok_start = buf; \ - static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ - int found2; \ - buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ - if (!found2) { \ - CHECK_EOF(); \ - } \ - while (1) { \ - if (*buf == ' ') { \ - break; \ - } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ - if ((unsigned char)*buf < '\040' || *buf == '\177') { \ - *ret = -1; \ - return NULL; \ - } \ - } \ - ++buf; \ - CHECK_EOF(); \ - } \ - tok = tok_start; \ - toklen = buf - tok_start; \ - } while (0) - -static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\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\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" - "\0\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\0\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\1\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"; - -static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) -{ - *found = 0; -#if __SSE4_2__ - if (likely(buf_end - buf >= 16)) { - __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); - - size_t left = (buf_end - buf) & ~15; - do { - __m128i b16 = _mm_loadu_si128((const __m128i *)buf); - int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); - if (unlikely(r != 16)) { - buf += r; - *found = 1; - break; - } - buf += 16; - left -= 16; - } while (likely(left != 0)); - } -#else - /* suppress unused parameter warning */ - (void)buf_end; - (void)ranges; - (void)ranges_size; -#endif - return buf; -} - -static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) -{ - const char *token_start = buf; - -#ifdef __SSE4_2__ - static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ - "\012\037" /* allow SP and up to but not including DEL */ - "\177\177"; /* allow chars w. MSB set */ - int found; - buf = findchar_fast(buf, buf_end, ranges1, 6, &found); - if (found) - goto FOUND_CTL; -#else - /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ - while (likely(buf_end - buf >= 8)) { -#define DOIT() \ - do { \ - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ - goto NonPrintable; \ - ++buf; \ - } while (0) - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); -#undef DOIT - continue; - NonPrintable: - if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { - goto FOUND_CTL; - } - ++buf; - } -#endif - for (;; ++buf) { - CHECK_EOF(); - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { - if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { - goto FOUND_CTL; - } - } - } -FOUND_CTL: - if (likely(*buf == '\015')) { - ++buf; - EXPECT_CHAR('\012'); - *token_len = buf - 2 - token_start; - } else if (*buf == '\012') { - *token_len = buf - token_start; - ++buf; - } else { - *ret = -1; - return NULL; - } - *token = token_start; - - return buf; -} - -static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) -{ - int ret_cnt = 0; - buf = last_len < 3 ? buf : buf + last_len - 3; - - while (1) { - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - CHECK_EOF(); - EXPECT_CHAR('\012'); - ++ret_cnt; - } else if (*buf == '\012') { - ++buf; - ++ret_cnt; - } else { - ++buf; - ret_cnt = 0; - } - if (ret_cnt == 2) { - return buf; - } - } - - *ret = -2; - return NULL; -} - -#define PARSE_INT(valp_, mul_) \ - if (*buf < '0' || '9' < *buf) { \ - buf++; \ - *ret = -1; \ - return NULL; \ - } \ - *(valp_) = (mul_) * (*buf++ - '0'); - -#define PARSE_INT_3(valp_) \ - do { \ - int res_ = 0; \ - PARSE_INT(&res_, 100) \ - *valp_ = res_; \ - PARSE_INT(&res_, 10) \ - *valp_ += res_; \ - PARSE_INT(&res_, 1) \ - *valp_ += res_; \ - } while (0) - -/* returned pointer is always within [buf, buf_end), or null */ -static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char, - int *ret) -{ - /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128 - * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */ - static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */ - "\"\"" /* 0x22 */ - "()" /* 0x28,0x29 */ - ",," /* 0x2c */ - "//" /* 0x2f */ - ":@" /* 0x3a-0x40 */ - "[]" /* 0x5b-0x5d */ - "{\xff"; /* 0x7b-0xff */ - const char *buf_start = buf; - int found; - buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found); - if (!found) { - CHECK_EOF(); - } - while (1) { - if (*buf == next_char) { - break; - } else if (!token_char_map[(unsigned char)*buf]) { - *ret = -1; - return NULL; - } - ++buf; - CHECK_EOF(); - } - *token = buf_start; - *token_len = buf - buf_start; - return buf; -} - -/* returned pointer is always within [buf, buf_end), or null */ -static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret) -{ - /* we want at least [HTTP/1.] to try to parse */ - if (buf_end - buf < 9) { - *ret = -2; - return NULL; - } - EXPECT_CHAR_NO_CHECK('H'); - EXPECT_CHAR_NO_CHECK('T'); - EXPECT_CHAR_NO_CHECK('T'); - EXPECT_CHAR_NO_CHECK('P'); - EXPECT_CHAR_NO_CHECK('/'); - EXPECT_CHAR_NO_CHECK('1'); - EXPECT_CHAR_NO_CHECK('.'); - PARSE_INT(minor_version, 1); - return buf; -} - -static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, - size_t max_headers, int *ret) -{ - for (;; ++*num_headers) { - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - break; - } else if (*buf == '\012') { - ++buf; - break; - } - if (*num_headers == max_headers) { - *ret = -1; - return NULL; - } - if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { - /* parsing name, but do not discard SP before colon, see - * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ - if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) { - return NULL; - } - if (headers[*num_headers].name_len == 0) { - *ret = -1; - return NULL; - } - ++buf; - for (;; ++buf) { - CHECK_EOF(); - if (!(*buf == ' ' || *buf == '\t')) { - break; - } - } - } else { - headers[*num_headers].name = NULL; - headers[*num_headers].name_len = 0; - } - const char *value; - size_t value_len; - if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { - return NULL; - } - /* remove trailing SPs and HTABs */ - const char *value_end = value + value_len; - for (; value_end != value; --value_end) { - const char c = *(value_end - 1); - if (!(c == ' ' || c == '\t')) { - break; - } - } - headers[*num_headers].value = value; - headers[*num_headers].value_len = value_end - value; - } - return buf; -} - -static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, - size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, - size_t max_headers, int *ret) -{ - /* skip first empty line (some clients add CRLF after POST content) */ - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - } else if (*buf == '\012') { - ++buf; - } - - /* parse request line */ - if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) { - return NULL; - } - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - ADVANCE_TOKEN(*path, *path_len); - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - if (*method_len == 0 || *path_len == 0) { - *ret = -1; - return NULL; - } - if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { - return NULL; - } - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - } else if (*buf == '\012') { - ++buf; - } else { - *ret = -1; - return NULL; - } - - return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); -} - -int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, - size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf_start + len; - size_t max_headers = *num_headers; - int r; - - *method = NULL; - *method_len = 0; - *path = NULL; - *path_len = 0; - *minor_version = -1; - *num_headers = 0; - - /* if last_len != 0, check if the request is complete (a fast countermeasure - againt slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers, - &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg, - size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) -{ - /* parse "HTTP/1.x" */ - if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { - return NULL; - } - /* skip space */ - if (*buf != ' ') { - *ret = -1; - return NULL; - } - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - /* parse status code, we want at least [:digit:][:digit:][:digit:] to try to parse */ - if (buf_end - buf < 4) { - *ret = -2; - return NULL; - } - PARSE_INT_3(status); - - /* get message including preceding space */ - if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { - return NULL; - } - if (*msg_len == 0) { - /* ok */ - } else if (**msg == ' ') { - /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP - * before running past the end of the given buffer. */ - do { - ++*msg; - --*msg_len; - } while (**msg == ' '); - } else { - /* garbage found after status code */ - *ret = -1; - return NULL; - } - - return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); -} - -int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf + len; - size_t max_headers = *num_headers; - int r; - - *minor_version = -1; - *status = 0; - *msg = NULL; - *msg_len = 0; - *num_headers = 0; - - /* if last_len != 0, check if the response is complete (a fast countermeasure - against slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf + len; - size_t max_headers = *num_headers; - int r; - - *num_headers = 0; - - /* if last_len != 0, check if the response is complete (a fast countermeasure - against slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -enum { - CHUNKED_IN_CHUNK_SIZE, - CHUNKED_IN_CHUNK_EXT, - CHUNKED_IN_CHUNK_DATA, - CHUNKED_IN_CHUNK_CRLF, - CHUNKED_IN_TRAILERS_LINE_HEAD, - CHUNKED_IN_TRAILERS_LINE_MIDDLE -}; - -static int decode_hex(int ch) -{ - if ('0' <= ch && ch <= '9') { - return ch - '0'; - } else if ('A' <= ch && ch <= 'F') { - return ch - 'A' + 0xa; - } else if ('a' <= ch && ch <= 'f') { - return ch - 'a' + 0xa; - } else { - return -1; - } -} - -ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) -{ - size_t dst = 0, src = 0, bufsz = *_bufsz; - ssize_t ret = -2; /* incomplete */ - - while (1) { - switch (decoder->_state) { - case CHUNKED_IN_CHUNK_SIZE: - for (;; ++src) { - int v; - if (src == bufsz) - goto Exit; - if ((v = decode_hex(buf[src])) == -1) { - if (decoder->_hex_count == 0) { - ret = -1; - goto Exit; - } - break; - } - if (decoder->_hex_count == sizeof(size_t) * 2) { - ret = -1; - goto Exit; - } - decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; - ++decoder->_hex_count; - } - decoder->_hex_count = 0; - decoder->_state = CHUNKED_IN_CHUNK_EXT; - /* fallthru */ - case CHUNKED_IN_CHUNK_EXT: - /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] == '\012') - break; - } - ++src; - if (decoder->bytes_left_in_chunk == 0) { - if (decoder->consume_trailer) { - decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; - break; - } else { - goto Complete; - } - } - decoder->_state = CHUNKED_IN_CHUNK_DATA; - /* fallthru */ - case CHUNKED_IN_CHUNK_DATA: { - size_t avail = bufsz - src; - if (avail < decoder->bytes_left_in_chunk) { - if (dst != src) - memmove(buf + dst, buf + src, avail); - src += avail; - dst += avail; - decoder->bytes_left_in_chunk -= avail; - goto Exit; - } - if (dst != src) - memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); - src += decoder->bytes_left_in_chunk; - dst += decoder->bytes_left_in_chunk; - decoder->bytes_left_in_chunk = 0; - decoder->_state = CHUNKED_IN_CHUNK_CRLF; - } - /* fallthru */ - case CHUNKED_IN_CHUNK_CRLF: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] != '\015') - break; - } - if (buf[src] != '\012') { - ret = -1; - goto Exit; - } - ++src; - decoder->_state = CHUNKED_IN_CHUNK_SIZE; - break; - case CHUNKED_IN_TRAILERS_LINE_HEAD: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] != '\015') - break; - } - if (buf[src++] == '\012') - goto Complete; - decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; - /* fallthru */ - case CHUNKED_IN_TRAILERS_LINE_MIDDLE: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] == '\012') - break; - } - ++src; - decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; - break; - default: - assert(!"decoder is corrupt"); - } - } - -Complete: - ret = bufsz - src; -Exit: - if (dst != src) - memmove(buf + dst, buf + src, bufsz - src); - *_bufsz = dst; - return ret; -} - -int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) -{ - return decoder->_state == CHUNKED_IN_CHUNK_DATA; -} - -#undef CHECK_EOF -#undef EXPECT_CHAR -#undef ADVANCE_TOKEN \ No newline at end of file diff --git a/src/deps/picohttpparser.h b/src/deps/picohttpparser.h deleted file mode 100644 index 07537cf1ea0dc..0000000000000 --- a/src/deps/picohttpparser.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, - * Shigeo Mitsunari - * - * The software is licensed under either the MIT License (below) or the Perl - * license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef picohttpparser_h -#define picohttpparser_h - -#include - -#ifdef _MSC_VER -#define ssize_t intptr_t -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* contains name and value of a header (name == NULL if is a continuing line - * of a multiline header */ -struct phr_header { - const char *name; - size_t name_len; - const char *value; - size_t value_len; -}; - -/* returns number of bytes consumed if successful, -2 if request is partial, - * -1 if failed */ -int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, - int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* ditto */ -int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* ditto */ -int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* should be zero-filled before start */ -struct phr_chunked_decoder { - size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ - char consume_trailer; /* if trailing headers should be consumed */ - char _hex_count; - char _state; -}; - -/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- - * encoding headers. When the function returns without an error, bufsz is - * updated to the length of the decoded data available. Applications should - * repeatedly call the function while it returns -2 (incomplete) every time - * supplying newly arrived data. If the end of the chunked-encoded data is - * found, the function returns a non-negative number indicating the number of - * octets left undecoded, that starts from the offset returned by `*bufsz`. - * Returns -1 on error. - */ -ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); - -/* returns if the chunked decoder is in middle of chunked data */ -int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/deps/picohttpparser.h.gch b/src/deps/picohttpparser.h.gch deleted file mode 100644 index 00b76aaf61398..0000000000000 Binary files a/src/deps/picohttpparser.h.gch and /dev/null differ diff --git a/src/deps/tinycc b/src/deps/tinycc deleted file mode 160000 index ab631362d8393..0000000000000 --- a/src/deps/tinycc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ab631362d839333660a265d3084d8ff060b96753 diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 6f18945a00929..102858501ac26 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -13,6 +13,10 @@ pub const Socket = opaque {}; pub const ConnectingSocket = opaque {}; const debug = bun.Output.scoped(.uws, false); const uws = @This(); +const SSLWrapper = @import("../bun.js/api/bun/ssl_wrapper.zig").SSLWrapper; +const TextEncoder = @import("../bun.js/webcore/encoding.zig").Encoder; +const JSC = bun.JSC; +const EventLoopTimer = @import("../bun.js//api//Timer.zig").EventLoopTimer; pub const CloseCode = enum(i32) { normal = 0, @@ -35,6 +39,7 @@ pub const InternalLoopData = extern struct { last_write_failed: i32, head: ?*SocketContext, iterator: ?*SocketContext, + closed_context_head: ?*SocketContext, recv_buf: [*]u8, send_buf: [*]u8, ssl_data: ?*anyopaque, @@ -55,7 +60,7 @@ pub const InternalLoopData = extern struct { return this.recv_buf[0..LIBUS_RECV_BUFFER_LENGTH]; } - pub fn setParentEventLoop(this: *InternalLoopData, parent: bun.JSC.EventLoopHandle) void { + pub fn setParentEventLoop(this: *InternalLoopData, parent: JSC.EventLoopHandle) void { switch (parent) { .js => |ptr| { this.parent_tag = 1; @@ -68,25 +73,1075 @@ pub const InternalLoopData = extern struct { } } - pub fn getParent(this: *InternalLoopData) bun.JSC.EventLoopHandle { + pub fn getParent(this: *InternalLoopData) JSC.EventLoopHandle { const parent = this.parent_ptr orelse @panic("Parent loop not set - pointer is null"); return switch (this.parent_tag) { 0 => @panic("Parent loop not set - tag is zero"), - 1 => .{ .js = bun.cast(*bun.JSC.EventLoop, parent) }, - 2 => .{ .mini = bun.cast(*bun.JSC.MiniEventLoop, parent) }, + 1 => .{ .js = bun.cast(*JSC.EventLoop, parent) }, + 2 => .{ .mini = bun.cast(*JSC.MiniEventLoop, parent) }, else => @panic("Parent loop data corrupted - tag is invalid"), }; } }; +pub const UpgradedDuplex = struct { + pub const CertError = struct { + error_no: i32 = 0, + code: [:0]const u8 = "", + reason: [:0]const u8 = "", + + pub fn deinit(this: *CertError) void { + if (this.code.len > 0) { + bun.default_allocator.free(this.code); + } + if (this.reason.len > 0) { + bun.default_allocator.free(this.reason); + } + } + }; + + const WrapperType = SSLWrapper(*UpgradedDuplex); + + wrapper: ?WrapperType, + origin: JSC.Strong = .{}, // any duplex + ssl_error: CertError = .{}, + vm: *JSC.VirtualMachine, + handlers: Handlers, + + onDataCallback: JSC.Strong = .{}, + onEndCallback: JSC.Strong = .{}, + onWritableCallback: JSC.Strong = .{}, + onCloseCallback: JSC.Strong = .{}, + event_loop_timer: EventLoopTimer = .{ + .next = .{}, + .tag = .UpgradedDuplex, + }, + current_timeout: u32 = 0, + + pub const Handlers = struct { + ctx: *anyopaque, + onOpen: *const fn (*anyopaque) void, + onHandshake: *const fn (*anyopaque, bool, uws.us_bun_verify_error_t) void, + onData: *const fn (*anyopaque, []const u8) void, + onClose: *const fn (*anyopaque) void, + onEnd: *const fn (*anyopaque) void, + onWritable: *const fn (*anyopaque) void, + onError: *const fn (*anyopaque, JSC.JSValue) void, + onTimeout: *const fn (*anyopaque) void, + }; + + const log = bun.Output.scoped(.UpgradedDuplex, false); + fn onOpen(this: *UpgradedDuplex) void { + log("onOpen", .{}); + this.handlers.onOpen(this.handlers.ctx); + } + + fn onData(this: *UpgradedDuplex, decoded_data: []const u8) void { + log("onData ({})", .{decoded_data.len}); + this.handlers.onData(this.handlers.ctx, decoded_data); + } + + fn onHandshake(this: *UpgradedDuplex, handshake_success: bool, ssl_error: uws.us_bun_verify_error_t) void { + log("onHandshake", .{}); + + this.ssl_error = .{ + .error_no = ssl_error.error_no, + .code = if (ssl_error.code == null or ssl_error.error_no == 0) "" else bun.default_allocator.dupeZ(u8, ssl_error.code[0..bun.len(ssl_error.code) :0]) catch bun.outOfMemory(), + .reason = if (ssl_error.reason == null or ssl_error.error_no == 0) "" else bun.default_allocator.dupeZ(u8, ssl_error.reason[0..bun.len(ssl_error.reason) :0]) catch bun.outOfMemory(), + }; + this.handlers.onHandshake(this.handlers.ctx, handshake_success, ssl_error); + } + + fn onClose(this: *UpgradedDuplex) void { + log("onClose", .{}); + defer this.deinit(); + + this.handlers.onClose(this.handlers.ctx); + // closes the underlying duplex + this.callWriteOrEnd(null, false); + } + + fn callWriteOrEnd(this: *UpgradedDuplex, data: ?[]const u8, msg_more: bool) void { + if (this.vm.isShuttingDown()) { + return; + } + if (this.origin.get()) |duplex| { + const globalThis = this.origin.globalThis.?; + const writeOrEnd = if (msg_more) duplex.getFunction(globalThis, "write") catch return orelse return else duplex.getFunction(globalThis, "end") catch return orelse return; + if (data) |data_| { + const buffer = JSC.BinaryType.toJS(.Buffer, data_, globalThis); + buffer.ensureStillAlive(); + + _ = writeOrEnd.call(globalThis, duplex, &.{buffer}) catch |err| { + this.handlers.onError(this.handlers.ctx, globalThis.takeException(err)); + }; + } else { + _ = writeOrEnd.call(globalThis, duplex, &.{.null}) catch |err| { + this.handlers.onError(this.handlers.ctx, globalThis.takeException(err)); + }; + } + } + } + + fn internalWrite(this: *UpgradedDuplex, encoded_data: []const u8) void { + this.resetTimeout(); + + // Possible scenarios: + // Scenario 1: will not write if vm is shutting down (we cannot do anything about it) + // Scenario 2: will not write if a exception is thrown (will be handled by onError) + // Scenario 3: will be queued in memory and will be flushed later + // Scenario 4: no write/end function exists (will be handled by onError) + this.callWriteOrEnd(encoded_data, true); + } + + pub fn flush(this: *UpgradedDuplex) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.flush(); + } + } + + fn onInternalReceiveData(this: *UpgradedDuplex, data: []const u8) void { + if (this.wrapper) |*wrapper| { + this.resetTimeout(); + wrapper.receiveData(data); + } + } + + fn onReceivedData( + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) JSC.JSValue { + log("onReceivedData", .{}); + + const function = callframe.callee(); + const args = callframe.arguments(1); + + if (JSC.getFunctionData(function)) |self| { + const this = @as(*UpgradedDuplex, @ptrCast(@alignCast(self))); + if (args.len >= 1) { + const data_arg = args.ptr[0]; + if (this.origin.has()) { + if (data_arg.isEmptyOrUndefinedOrNull()) { + return JSC.JSValue.jsUndefined(); + } + if (data_arg.asArrayBuffer(globalObject)) |array_buffer| { + // yay we can read the data + const payload = array_buffer.slice(); + this.onInternalReceiveData(payload); + } else { + // node.js errors in this case with the same error, lets keep it consistent + const error_value = globalObject.ERR_STREAM_WRAP("Stream has StringDecoder set or is in objectMode", .{}).toJS(); + error_value.ensureStillAlive(); + this.handlers.onError(this.handlers.ctx, error_value); + } + } + } + } + return JSC.JSValue.jsUndefined(); + } + + fn onEnd( + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) void { + log("onEnd", .{}); + _ = globalObject; + const function = callframe.callee(); + + if (JSC.getFunctionData(function)) |self| { + const this = @as(*UpgradedDuplex, @ptrCast(@alignCast(self))); + + if (this.wrapper != null) { + this.handlers.onEnd(this.handlers.ctx); + } + } + } + + fn onWritable( + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) JSC.JSValue { + log("onWritable", .{}); + + _ = globalObject; + const function = callframe.callee(); + + if (JSC.getFunctionData(function)) |self| { + const this = @as(*UpgradedDuplex, @ptrCast(@alignCast(self))); + // flush pending data + if (this.wrapper) |*wrapper| { + _ = wrapper.flush(); + } + // call onWritable (will flush on demand) + this.handlers.onWritable(this.handlers.ctx); + } + + return JSC.JSValue.jsUndefined(); + } + + fn onCloseJS( + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) JSC.JSValue { + log("onCloseJS", .{}); + + _ = globalObject; + const function = callframe.callee(); + + if (JSC.getFunctionData(function)) |self| { + const this = @as(*UpgradedDuplex, @ptrCast(@alignCast(self))); + // flush pending data + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdown(true); + } + } + + return JSC.JSValue.jsUndefined(); + } + + pub fn onTimeout(this: *UpgradedDuplex) EventLoopTimer.Arm { + log("onTimeout", .{}); + + const has_been_cleared = this.event_loop_timer.state == .CANCELLED or this.vm.scriptExecutionStatus() != .running; + + this.event_loop_timer.state = .FIRED; + this.event_loop_timer.heap = .{}; + + if (has_been_cleared) { + return .disarm; + } + + this.handlers.onTimeout(this.handlers.ctx); + + return .disarm; + } + + pub fn from( + globalThis: *JSC.JSGlobalObject, + origin: JSC.JSValue, + handlers: UpgradedDuplex.Handlers, + ) UpgradedDuplex { + return UpgradedDuplex{ + .vm = globalThis.bunVM(), + .origin = JSC.Strong.create(origin, globalThis), + .wrapper = null, + .handlers = handlers, + }; + } + + pub fn getJSHandlers(this: *UpgradedDuplex, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + const array = JSC.JSValue.createEmptyArray(globalThis, 4); + array.ensureStillAlive(); + + { + const callback = this.onDataCallback.get() orelse brk: { + const dataCallback = JSC.NewFunctionWithData( + globalThis, + null, + 0, + onReceivedData, + false, + this, + ); + dataCallback.ensureStillAlive(); + + JSC.setFunctionData(dataCallback, this); + + this.onDataCallback = JSC.Strong.create(dataCallback, globalThis); + break :brk dataCallback; + }; + array.putIndex(globalThis, 0, callback); + } + + { + const callback = this.onEndCallback.get() orelse brk: { + const endCallback = JSC.NewFunctionWithData( + globalThis, + null, + 0, + onReceivedData, + false, + this, + ); + endCallback.ensureStillAlive(); + + JSC.setFunctionData(endCallback, this); + + this.onEndCallback = JSC.Strong.create(endCallback, globalThis); + break :brk endCallback; + }; + array.putIndex(globalThis, 1, callback); + } + + { + const callback = this.onWritableCallback.get() orelse brk: { + const writableCallback = JSC.NewFunctionWithData( + globalThis, + null, + 0, + onWritable, + false, + this, + ); + writableCallback.ensureStillAlive(); + + JSC.setFunctionData(writableCallback, this); + this.onWritableCallback = JSC.Strong.create(writableCallback, globalThis); + break :brk writableCallback; + }; + array.putIndex(globalThis, 2, callback); + } + + { + const callback = this.onCloseCallback.get() orelse brk: { + const closeCallback = JSC.NewFunctionWithData( + globalThis, + null, + 0, + onCloseJS, + false, + this, + ); + closeCallback.ensureStillAlive(); + + JSC.setFunctionData(closeCallback, this); + this.onCloseCallback = JSC.Strong.create(closeCallback, globalThis); + break :brk closeCallback; + }; + array.putIndex(globalThis, 3, callback); + } + + return array; + } + + pub fn startTLS(this: *UpgradedDuplex, ssl_options: JSC.API.ServerConfig.SSLConfig, is_client: bool) !void { + this.wrapper = try WrapperType.init(ssl_options, is_client, .{ + .ctx = this, + .onOpen = UpgradedDuplex.onOpen, + .onHandshake = UpgradedDuplex.onHandshake, + .onData = UpgradedDuplex.onData, + .onClose = UpgradedDuplex.onClose, + .write = UpgradedDuplex.internalWrite, + }); + + this.wrapper.?.start(); + } + + pub fn encodeAndWrite(this: *UpgradedDuplex, data: []const u8, is_end: bool) i32 { + log("encodeAndWrite (len: {} - is_end: {})", .{ data.len, is_end }); + if (this.wrapper) |*wrapper| { + return @as(i32, @intCast(wrapper.writeData(data) catch 0)); + } + return 0; + } + + pub fn rawWrite(this: *UpgradedDuplex, encoded_data: []const u8, _: bool) i32 { + this.internalWrite(encoded_data); + return @intCast(encoded_data.len); + } + + pub fn close(this: *UpgradedDuplex) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdown(true); + } + } + + pub fn shutdown(this: *UpgradedDuplex) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdown(false); + } + } + + pub fn shutdownRead(this: *UpgradedDuplex) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdownRead(); + } + } + + pub fn isShutdown(this: *UpgradedDuplex) bool { + if (this.wrapper) |wrapper| { + return wrapper.isShutdown(); + } + return true; + } + + pub fn isClosed(this: *UpgradedDuplex) bool { + if (this.wrapper) |wrapper| { + return wrapper.isClosed(); + } + return true; + } + + pub fn isEstablished(this: *UpgradedDuplex) bool { + return !this.isClosed(); + } + + pub fn ssl(this: *UpgradedDuplex) ?*BoringSSL.SSL { + if (this.wrapper) |wrapper| { + return wrapper.ssl; + } + return null; + } + + pub fn sslError(this: *UpgradedDuplex) us_bun_verify_error_t { + return .{ + .error_no = this.ssl_error.error_no, + .code = @ptrCast(this.ssl_error.code.ptr), + .reason = @ptrCast(this.ssl_error.reason.ptr), + }; + } + + pub fn resetTimeout(this: *UpgradedDuplex) void { + this.setTimeoutInMilliseconds(this.current_timeout); + } + pub fn setTimeoutInMilliseconds(this: *UpgradedDuplex, ms: c_uint) void { + if (this.event_loop_timer.state == .ACTIVE) { + this.vm.timer.remove(&this.event_loop_timer); + } + this.current_timeout = ms; + + // if the interval is 0 means that we stop the timer + if (ms == 0) { + return; + } + + // reschedule the timer + this.event_loop_timer.next = bun.timespec.msFromNow(ms); + this.vm.timer.insert(&this.event_loop_timer); + } + pub fn setTimeout(this: *UpgradedDuplex, seconds: c_uint) void { + log("setTimeout({d})", .{seconds}); + this.setTimeoutInMilliseconds(seconds * 1000); + } + + pub fn deinit(this: *UpgradedDuplex) void { + log("deinit", .{}); + // clear the timer + this.setTimeout(0); + + if (this.wrapper) |*wrapper| { + wrapper.deinit(); + this.wrapper = null; + } + + this.origin.deinit(); + if (this.onDataCallback.get()) |callback| { + JSC.setFunctionData(callback, null); + this.onDataCallback.deinit(); + } + if (this.onEndCallback.get()) |callback| { + JSC.setFunctionData(callback, null); + this.onEndCallback.deinit(); + } + if (this.onWritableCallback.get()) |callback| { + JSC.setFunctionData(callback, null); + this.onWritableCallback.deinit(); + } + if (this.onCloseCallback.get()) |callback| { + JSC.setFunctionData(callback, null); + this.onCloseCallback.deinit(); + } + var ssl_error = this.ssl_error; + ssl_error.deinit(); + this.ssl_error = .{}; + } +}; + +pub const WindowsNamedPipe = if (Environment.isWindows) struct { + pub const CertError = UpgradedDuplex.CertError; + + const WrapperType = SSLWrapper(*WindowsNamedPipe); + const uv = bun.windows.libuv; + wrapper: ?WrapperType, + pipe: if (Environment.isWindows) ?*uv.Pipe else void, // any duplex + vm: *bun.JSC.VirtualMachine, //TODO: create a timeout version that dont need the JSC VM + + writer: bun.io.StreamingWriter(WindowsNamedPipe, onWrite, onError, onWritable, onPipeClose) = .{}, + + incoming: bun.ByteList = .{}, // Maybe we should use IPCBuffer here as well + ssl_error: CertError = .{}, + handlers: Handlers, + connect_req: uv.uv_connect_t = std.mem.zeroes(uv.uv_connect_t), + + event_loop_timer: EventLoopTimer = .{ + .next = .{}, + .tag = .WindowsNamedPipe, + }, + current_timeout: u32 = 0, + flags: Flags = .{}, + + pub const Flags = packed struct { + disconnected: bool = true, + is_closed: bool = false, + is_client: bool = false, + is_ssl: bool = false, + }; + pub const Handlers = struct { + ctx: *anyopaque, + onOpen: *const fn (*anyopaque) void, + onHandshake: *const fn (*anyopaque, bool, uws.us_bun_verify_error_t) void, + onData: *const fn (*anyopaque, []const u8) void, + onClose: *const fn (*anyopaque) void, + onEnd: *const fn (*anyopaque) void, + onWritable: *const fn (*anyopaque) void, + onError: *const fn (*anyopaque, bun.sys.Error) void, + onTimeout: *const fn (*anyopaque) void, + }; + + const log = bun.Output.scoped(.WindowsNamedPipe, false); + + fn onWritable( + this: *WindowsNamedPipe, + ) void { + log("onWritable", .{}); + // flush pending data + this.flush(); + // call onWritable (will flush on demand) + this.handlers.onWritable(this.handlers.ctx); + } + + fn onPipeClose(this: *WindowsNamedPipe) void { + log("onPipeClose", .{}); + this.flags.disconnected = true; + this.pipe = null; + this.onClose(); + } + + fn onReadAlloc(this: *WindowsNamedPipe, suggested_size: usize) []u8 { + var available = this.incoming.available(); + if (available.len < suggested_size) { + this.incoming.ensureUnusedCapacity(bun.default_allocator, suggested_size) catch bun.outOfMemory(); + available = this.incoming.available(); + } + return available.ptr[0..suggested_size]; + } + + fn onRead(this: *WindowsNamedPipe, buffer: []const u8) void { + log("onRead ({})", .{buffer.len}); + this.incoming.len += @as(u32, @truncate(buffer.len)); + bun.assert(this.incoming.len <= this.incoming.cap); + bun.assert(bun.isSliceInBuffer(buffer, this.incoming.allocatedSlice())); + + const data = this.incoming.slice(); + + this.resetTimeout(); + + if (this.wrapper) |*wrapper| { + wrapper.receiveData(data); + } else { + this.handlers.onData(this.handlers.ctx, data); + } + this.incoming.len = 0; + } + + fn onWrite(this: *WindowsNamedPipe, amount: usize, status: bun.io.WriteStatus) void { + log("onWrite {d} {}", .{ amount, status }); + + switch (status) { + .pending => {}, + .drained => { + // unref after sending all data + if (this.writer.source) |source| { + source.pipe.unref(); + } + }, + .end_of_file => { + // we send FIN so we close after this + this.writer.close(); + }, + } + } + + fn onReadError(this: *WindowsNamedPipe, err: bun.C.E) void { + log("onReadError", .{}); + if (err == .EOF) { + // we received FIN but we dont allow half-closed connections right now + this.handlers.onEnd(this.handlers.ctx); + } else { + this.onError(bun.sys.Error.fromCode(err, .read)); + } + this.writer.close(); + } + + fn onError(this: *WindowsNamedPipe, err: bun.sys.Error) void { + log("onError", .{}); + this.handlers.onError(this.handlers.ctx, err); + this.close(); + } + + fn onOpen(this: *WindowsNamedPipe) void { + log("onOpen", .{}); + this.handlers.onOpen(this.handlers.ctx); + } + + fn onData(this: *WindowsNamedPipe, decoded_data: []const u8) void { + log("onData ({})", .{decoded_data.len}); + this.handlers.onData(this.handlers.ctx, decoded_data); + } + + fn onHandshake(this: *WindowsNamedPipe, handshake_success: bool, ssl_error: uws.us_bun_verify_error_t) void { + log("onHandshake", .{}); + + this.ssl_error = .{ + .error_no = ssl_error.error_no, + .code = if (ssl_error.code == null or ssl_error.error_no == 0) "" else bun.default_allocator.dupeZ(u8, ssl_error.code[0..bun.len(ssl_error.code) :0]) catch bun.outOfMemory(), + .reason = if (ssl_error.reason == null or ssl_error.error_no == 0) "" else bun.default_allocator.dupeZ(u8, ssl_error.reason[0..bun.len(ssl_error.reason) :0]) catch bun.outOfMemory(), + }; + this.handlers.onHandshake(this.handlers.ctx, handshake_success, ssl_error); + } + + fn onClose(this: *WindowsNamedPipe) void { + log("onClose", .{}); + if (!this.flags.is_closed) { + this.flags.is_closed = true; // only call onClose once + this.handlers.onClose(this.handlers.ctx); + this.deinit(); + } + } + + fn callWriteOrEnd(this: *WindowsNamedPipe, data: ?[]const u8, msg_more: bool) void { + if (data) |bytes| { + if (bytes.len > 0) { + // ref because we have pending data + if (this.writer.source) |source| { + source.pipe.ref(); + } + if (this.flags.disconnected) { + // enqueue to be sent after connecting + this.writer.outgoing.write(bytes) catch bun.outOfMemory(); + } else { + // write will enqueue the data if it cannot be sent + _ = this.writer.write(bytes); + } + } + } + + if (!msg_more) { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdown(false); + } + this.writer.end(); + } + } + + fn internalWrite(this: *WindowsNamedPipe, encoded_data: []const u8) void { + this.resetTimeout(); + + // Possible scenarios: + // Scenario 1: will not write if is not connected yet but will enqueue the data + // Scenario 2: will not write if a exception is thrown (will be handled by onError) + // Scenario 3: will be queued in memory and will be flushed later + // Scenario 4: no write/end function exists (will be handled by onError) + this.callWriteOrEnd(encoded_data, true); + } + + pub fn flush(this: *WindowsNamedPipe) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.flush(); + } + if (!this.flags.disconnected) { + _ = this.writer.flush(); + } + } + + fn onInternalReceiveData(this: *WindowsNamedPipe, data: []const u8) void { + if (this.wrapper) |*wrapper| { + this.resetTimeout(); + wrapper.receiveData(data); + } + } + + pub fn onTimeout(this: *WindowsNamedPipe) EventLoopTimer.Arm { + log("onTimeout", .{}); + + const has_been_cleared = this.event_loop_timer.state == .CANCELLED or this.vm.scriptExecutionStatus() != .running; + + this.event_loop_timer.state = .FIRED; + this.event_loop_timer.heap = .{}; + + if (has_been_cleared) { + return .disarm; + } + + this.handlers.onTimeout(this.handlers.ctx); + + return .disarm; + } + + pub fn from( + pipe: *uv.Pipe, + handlers: WindowsNamedPipe.Handlers, + vm: *JSC.VirtualMachine, + ) WindowsNamedPipe { + if (Environment.isPosix) { + @compileError("WindowsNamedPipe is not supported on POSIX systems"); + } + return WindowsNamedPipe{ + .vm = vm, + .pipe = pipe, + .wrapper = null, + .handlers = handlers, + }; + } + fn onConnect(this: *WindowsNamedPipe, status: uv.ReturnCode) void { + if (this.pipe) |pipe| { + _ = pipe.unref(); + } + + if (status.toError(.connect)) |err| { + this.onError(err); + return; + } + + this.flags.disconnected = false; + if (this.start(true)) { + if (this.isTLS()) { + if (this.wrapper) |*wrapper| { + // trigger onOpen and start the handshake + wrapper.start(); + } + } else { + // trigger onOpen + this.onOpen(); + } + } + this.flush(); + } + + pub fn getAcceptedBy(this: *WindowsNamedPipe, server: *uv.Pipe, ssl_ctx: ?*BoringSSL.SSL_CTX) JSC.Maybe(void) { + bun.assert(this.pipe != null); + this.flags.disconnected = true; + + if (ssl_ctx) |tls| { + this.flags.is_ssl = true; + this.wrapper = WrapperType.initWithCTX(tls, false, .{ + .ctx = this, + .onOpen = WindowsNamedPipe.onOpen, + .onHandshake = WindowsNamedPipe.onHandshake, + .onData = WindowsNamedPipe.onData, + .onClose = WindowsNamedPipe.onClose, + .write = WindowsNamedPipe.internalWrite, + }) catch { + return .{ + .err = .{ + .errno = @intFromEnum(bun.C.E.PIPE), + .syscall = .connect, + }, + }; + }; + // ref because we are accepting will unref when wrapper deinit + _ = BoringSSL.SSL_CTX_up_ref(tls); + } + const initResult = this.pipe.?.init(this.vm.uvLoop(), false); + if (initResult == .err) { + return initResult; + } + + const openResult = server.accept(this.pipe.?); + if (openResult == .err) { + return openResult; + } + + this.flags.disconnected = false; + if (this.start(false)) { + if (this.isTLS()) { + if (this.wrapper) |*wrapper| { + // trigger onOpen and start the handshake + wrapper.start(); + } + } else { + // trigger onOpen + this.onOpen(); + } + } + return .{ .result = {} }; + } + pub fn open(this: *WindowsNamedPipe, fd: bun.FileDescriptor, ssl_options: ?JSC.API.ServerConfig.SSLConfig) JSC.Maybe(void) { + bun.assert(this.pipe != null); + this.flags.disconnected = true; + + if (ssl_options) |tls| { + this.flags.is_ssl = true; + this.wrapper = WrapperType.init(tls, true, .{ + .ctx = this, + .onOpen = WindowsNamedPipe.onOpen, + .onHandshake = WindowsNamedPipe.onHandshake, + .onData = WindowsNamedPipe.onData, + .onClose = WindowsNamedPipe.onClose, + .write = WindowsNamedPipe.internalWrite, + }) catch { + return .{ + .err = .{ + .errno = @intFromEnum(bun.C.E.PIPE), + .syscall = .connect, + }, + }; + }; + } + const initResult = this.pipe.?.init(this.vm.uvLoop(), false); + if (initResult == .err) { + return initResult; + } + + const openResult = this.pipe.?.open(fd); + if (openResult == .err) { + return openResult; + } + + onConnect(this, uv.ReturnCode.zero); + return .{ .result = {} }; + } + + pub fn connect(this: *WindowsNamedPipe, path: []const u8, ssl_options: ?JSC.API.ServerConfig.SSLConfig) JSC.Maybe(void) { + bun.assert(this.pipe != null); + this.flags.disconnected = true; + // ref because we are connecting + _ = this.pipe.?.ref(); + + if (ssl_options) |tls| { + this.flags.is_ssl = true; + this.wrapper = WrapperType.init(tls, true, .{ + .ctx = this, + .onOpen = WindowsNamedPipe.onOpen, + .onHandshake = WindowsNamedPipe.onHandshake, + .onData = WindowsNamedPipe.onData, + .onClose = WindowsNamedPipe.onClose, + .write = WindowsNamedPipe.internalWrite, + }) catch { + return .{ + .err = .{ + .errno = @intFromEnum(bun.C.E.PIPE), + .syscall = .connect, + }, + }; + }; + } + const initResult = this.pipe.?.init(this.vm.uvLoop(), false); + if (initResult == .err) { + return initResult; + } + + this.connect_req.data = this; + return this.pipe.?.connect(&this.connect_req, path, this, onConnect); + } + pub fn startTLS(this: *WindowsNamedPipe, ssl_options: JSC.API.ServerConfig.SSLConfig, is_client: bool) !void { + this.flags.is_ssl = true; + if (this.start(is_client)) { + this.wrapper = try WrapperType.init(ssl_options, is_client, .{ + .ctx = this, + .onOpen = WindowsNamedPipe.onOpen, + .onHandshake = WindowsNamedPipe.onHandshake, + .onData = WindowsNamedPipe.onData, + .onClose = WindowsNamedPipe.onClose, + .write = WindowsNamedPipe.internalWrite, + }); + + this.wrapper.?.start(); + } + } + + pub fn start(this: *WindowsNamedPipe, is_client: bool) bool { + this.flags.is_client = is_client; + if (this.pipe == null) { + return false; + } + _ = this.pipe.?.unref(); + this.writer.setParent(this); + const startPipeResult = this.writer.startWithPipe(this.pipe.?); + if (startPipeResult == .err) { + this.onError(startPipeResult.err); + return false; + } + const stream = this.writer.getStream() orelse { + this.onError(bun.sys.Error.fromCode(bun.C.E.PIPE, .read)); + return false; + }; + + const readStartResult = stream.readStart(this, onReadAlloc, onReadError, onRead); + if (readStartResult == .err) { + this.onError(readStartResult.err); + return false; + } + return true; + } + + pub fn isTLS(this: *WindowsNamedPipe) bool { + return this.flags.is_ssl; + } + + pub fn encodeAndWrite(this: *WindowsNamedPipe, data: []const u8, is_end: bool) i32 { + log("encodeAndWrite (len: {} - is_end: {})", .{ data.len, is_end }); + if (this.wrapper) |*wrapper| { + return @as(i32, @intCast(wrapper.writeData(data) catch 0)); + } else { + this.internalWrite(data); + } + return @intCast(data.len); + } + + pub fn rawWrite(this: *WindowsNamedPipe, encoded_data: []const u8, _: bool) i32 { + this.internalWrite(encoded_data); + return @intCast(encoded_data.len); + } + + pub fn close(this: *WindowsNamedPipe) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdown(false); + } + this.writer.end(); + } + + pub fn shutdown(this: *WindowsNamedPipe) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdown(false); + } + } + + pub fn shutdownRead(this: *WindowsNamedPipe) void { + if (this.wrapper) |*wrapper| { + _ = wrapper.shutdownRead(); + } else { + if (this.writer.getStream()) |stream| { + _ = stream.readStop(); + } + } + } + + pub fn isShutdown(this: *WindowsNamedPipe) bool { + if (this.wrapper) |wrapper| { + return wrapper.isShutdown(); + } + + return this.flags.disconnected or this.writer.is_done; + } + + pub fn isClosed(this: *WindowsNamedPipe) bool { + if (this.wrapper) |wrapper| { + return wrapper.isClosed(); + } + return this.flags.disconnected; + } + + pub fn isEstablished(this: *WindowsNamedPipe) bool { + return !this.isClosed(); + } + + pub fn ssl(this: *WindowsNamedPipe) ?*BoringSSL.SSL { + if (this.wrapper) |wrapper| { + return wrapper.ssl; + } + return null; + } + + pub fn sslError(this: *WindowsNamedPipe) us_bun_verify_error_t { + return .{ + .error_no = this.ssl_error.error_no, + .code = @ptrCast(this.ssl_error.code.ptr), + .reason = @ptrCast(this.ssl_error.reason.ptr), + }; + } + + pub fn resetTimeout(this: *WindowsNamedPipe) void { + this.setTimeoutInMilliseconds(this.current_timeout); + } + pub fn setTimeoutInMilliseconds(this: *WindowsNamedPipe, ms: c_uint) void { + if (this.event_loop_timer.state == .ACTIVE) { + this.vm.timer.remove(&this.event_loop_timer); + } + this.current_timeout = ms; + + // if the interval is 0 means that we stop the timer + if (ms == 0) { + return; + } + + // reschedule the timer + this.event_loop_timer.next = bun.timespec.msFromNow(ms); + this.vm.timer.insert(&this.event_loop_timer); + } + pub fn setTimeout(this: *WindowsNamedPipe, seconds: c_uint) void { + log("setTimeout({d})", .{seconds}); + this.setTimeoutInMilliseconds(seconds * 1000); + } + /// Free internal resources, it can be called multiple times + pub fn deinit(this: *WindowsNamedPipe) void { + log("deinit", .{}); + // clear the timer + this.setTimeout(0); + if (this.writer.getStream()) |stream| { + _ = stream.readStop(); + } + this.writer.deinit(); + if (this.wrapper) |*wrapper| { + wrapper.deinit(); + this.wrapper = null; + } + var ssl_error = this.ssl_error; + ssl_error.deinit(); + this.ssl_error = .{}; + } +} else void; + pub const InternalSocket = union(enum) { done: *Socket, connecting: *ConnectingSocket, + detached: void, + upgradedDuplex: *UpgradedDuplex, + pipe: *WindowsNamedPipe, + pub fn isDetached(this: InternalSocket) bool { + return this == .detached; + } + pub fn isNamedPipe(this: InternalSocket) bool { + return this == .pipe; + } + pub fn detach(this: *InternalSocket) void { + this.* = .detached; + } + pub fn close(this: InternalSocket, comptime is_ssl: bool, code: CloseCode) void { + switch (this) { + .detached => {}, + .done => |socket| { + debug("us_socket_close({d})", .{@intFromPtr(socket)}); + _ = us_socket_close( + comptime @intFromBool(is_ssl), + socket, + code, + null, + ); + }, + .connecting => |socket| { + debug("us_connecting_socket_close({d})", .{@intFromPtr(socket)}); + _ = us_connecting_socket_close( + comptime @intFromBool(is_ssl), + socket, + ); + }, + .upgradedDuplex => |socket| { + socket.close(); + }, + .pipe => |pipe| { + if (Environment.isWindows) pipe.close(); + }, + } + } + + pub fn isClosed(this: InternalSocket, comptime is_ssl: bool) bool { + return switch (this) { + .done => |socket| us_socket_is_closed(@intFromBool(is_ssl), socket) > 0, + .connecting => |socket| us_connecting_socket_is_closed(@intFromBool(is_ssl), socket) > 0, + .detached => true, + .upgradedDuplex => |socket| socket.isClosed(), + .pipe => |pipe| if (Environment.isWindows) pipe.isClosed() else true, + }; + } pub fn get(this: @This()) ?*Socket { return switch (this) { .done => this.done, .connecting => null, + .detached => null, + .upgradedDuplex => null, + .pipe => null, }; } @@ -94,12 +1149,24 @@ pub const InternalSocket = union(enum) { return switch (this) { .done => switch (other) { .done => this.done == other.done, - .connecting => false, + .upgradedDuplex, .connecting, .detached, .pipe => false, }, .connecting => switch (other) { - .done => false, + .upgradedDuplex, .done, .detached, .pipe => false, .connecting => this.connecting == other.connecting, }, + .detached => switch (other) { + .detached => true, + .upgradedDuplex, .done, .connecting, .pipe => false, + }, + .upgradedDuplex => switch (other) { + .upgradedDuplex => this.upgradedDuplex == other.upgradedDuplex, + .done, .connecting, .detached, .pipe => false, + }, + .pipe => switch (other) { + .pipe => if (Environment.isWindows) other.pipe == other.pipe else false, + .done, .connecting, .detached, .upgradedDuplex => false, + }, }; } }; @@ -109,22 +1176,41 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const ssl_int: i32 = @intFromBool(is_ssl); socket: InternalSocket, const ThisSocket = @This(); - + pub const detached: NewSocketHandler(is_ssl) = NewSocketHandler(is_ssl){ .socket = .{ .detached = {} } }; + pub fn detach(this: *ThisSocket) void { + this.socket.detach(); + } + pub fn isDetached(this: ThisSocket) bool { + return this.socket.isDetached(); + } + pub fn isNamedPipe(this: ThisSocket) bool { + return this.socket.isNamedPipe(); + } pub fn verifyError(this: ThisSocket) us_bun_verify_error_t { - const socket = this.socket.get() orelse return std.mem.zeroes(us_bun_verify_error_t); - const ssl_error: us_bun_verify_error_t = uws.us_socket_verify_error(comptime ssl_int, socket); - return ssl_error; + switch (this.socket) { + .done => |socket| return uws.us_socket_verify_error(comptime ssl_int, socket), + .upgradedDuplex => |socket| return socket.sslError(), + .pipe => |pipe| if (Environment.isWindows) return pipe.sslError() else return std.mem.zeroes(us_bun_verify_error_t), + .connecting, .detached => return std.mem.zeroes(us_bun_verify_error_t), + } } pub fn isEstablished(this: ThisSocket) bool { - const socket = this.socket.get() orelse return false; - return us_socket_is_established(comptime ssl_int, socket) > 0; + switch (this.socket) { + .done => |socket| return us_socket_is_established(comptime ssl_int, socket) > 0, + .upgradedDuplex => |socket| return socket.isEstablished(), + .pipe => |pipe| if (Environment.isWindows) return pipe.isEstablished() else return false, + .connecting, .detached => return false, + } } pub fn timeout(this: ThisSocket, seconds: c_uint) void { switch (this.socket) { + .upgradedDuplex => |socket| socket.setTimeout(seconds), + .pipe => |pipe| if (Environment.isWindows) pipe.setTimeout(seconds), .done => |socket| us_socket_timeout(comptime ssl_int, socket, seconds), .connecting => |socket| us_connecting_socket_timeout(comptime ssl_int, socket, seconds), + .detached => {}, } } @@ -148,6 +1234,9 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { us_connecting_socket_long_timeout(comptime ssl_int, socket, 0); } }, + .detached => {}, + .upgradedDuplex => |socket| socket.setTimeout(seconds), + .pipe => |pipe| if (Environment.isWindows) pipe.setTimeout(seconds), } } @@ -161,19 +1250,25 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { us_connecting_socket_timeout(comptime ssl_int, socket, 0); us_connecting_socket_long_timeout(comptime ssl_int, socket, minutes); }, + .detached => {}, + .upgradedDuplex => |socket| socket.setTimeout(minutes * 60), + .pipe => |pipe| if (Environment.isWindows) pipe.setTimeout(minutes * 60), } } pub fn startTLS(this: ThisSocket, is_client: bool) void { - const socket = this.socket.get() orelse @panic("socket is not open"); + const socket = this.socket.get() orelse return; _ = us_socket_open(comptime ssl_int, socket, @intFromBool(is_client), null, 0); } - pub fn ssl(this: ThisSocket) *BoringSSL.SSL { + pub fn ssl(this: ThisSocket) ?*BoringSSL.SSL { if (comptime is_ssl) { - return @as(*BoringSSL.SSL, @ptrCast(this.getNativeHandle())); + if (this.getNativeHandle()) |handle| { + return @as(*BoringSSL.SSL, @ptrCast(handle)); + } + return null; } - @panic("socket is not a TLS socket"); + return null; } // Note: this assumes that the socket is non-TLS and will be adopted and wrapped with a new TLS context @@ -200,7 +1295,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } if (comptime deref_) { - return (TLSSocket.from(socket)).ext(ContextType).*; + return (TLSSocket.from(socket)).ext(ContextType).?.*; } return (TLSSocket.from(socket)).ext(ContextType); @@ -260,7 +1355,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } pub fn on_connect_error(socket: *Socket, code: i32) callconv(.C) ?*Socket { Fields.onConnectError( - TLSSocket.from(socket).ext(ContextType).*, + TLSSocket.from(socket).ext(ContextType).?.*, TLSSocket.from(socket), code, ); @@ -299,7 +1394,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { .on_long_timeout = SocketHandler.on_long_timeout, }; - const this_socket = this.socket.get() orelse @panic("socket is not open"); + const this_socket = this.socket.get() orelse return null; const socket = us_socket_wrap_with_tls(ssl_int, this_socket, options, events, socket_ext_size) orelse return null; return NewSocketHandler(true).from(socket); @@ -309,6 +1404,9 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { return @ptrCast(switch (this.socket) { .done => |socket| us_socket_get_native_handle(comptime ssl_int, socket), .connecting => |socket| us_connecting_socket_get_native_handle(comptime ssl_int, socket), + .detached => null, + .upgradedDuplex => |socket| if (is_ssl) @as(*anyopaque, @ptrCast(socket.ssl() orelse return null)) else null, + .pipe => |socket| if (is_ssl and Environment.isWindows) @as(*anyopaque, @ptrCast(socket.ssl() orelse return null)) else null, } orelse return null); } @@ -332,7 +1430,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { us_socket_sendfile_needs_more(socket); } - pub fn ext(this: ThisSocket, comptime ContextType: type) *ContextType { + pub fn ext(this: ThisSocket, comptime ContextType: type) ?*ContextType { const alignment = if (ContextType == *anyopaque) @sizeOf(usize) else @@ -341,6 +1439,9 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const ptr = switch (this.socket) { .done => |sock| us_socket_ext(comptime ssl_int, sock), .connecting => |sock| us_connecting_socket_ext(comptime ssl_int, sock), + .detached => return null, + .upgradedDuplex => return null, + .pipe => return null, }; return @as(*align(alignment) ContextType, @ptrCast(@alignCast(ptr))); @@ -351,44 +1452,78 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { switch (this.socket) { .done => |socket| return us_socket_context(comptime ssl_int, socket), .connecting => |socket| return us_connecting_socket_context(comptime ssl_int, socket), + .detached => return null, + .upgradedDuplex => return null, + .pipe => return null, } } pub fn flush(this: ThisSocket) void { - const socket = this.socket.get() orelse return; - return us_socket_flush( - comptime ssl_int, - socket, - ); + switch (this.socket) { + .upgradedDuplex => |socket| { + return socket.flush(); + }, + .pipe => |pipe| { + return if (Environment.isWindows) pipe.flush() else return; + }, + .done => |socket| { + return us_socket_flush( + comptime ssl_int, + socket, + ); + }, + .connecting, .detached => return, + } } + pub fn write(this: ThisSocket, data: []const u8, msg_more: bool) i32 { - const socket = this.socket.get() orelse return 0; - const result = us_socket_write( - comptime ssl_int, - socket, - data.ptr, - // truncate to 31 bits since sign bit exists - @as(i32, @intCast(@as(u31, @truncate(data.len)))), - @as(i32, @intFromBool(msg_more)), - ); + switch (this.socket) { + .upgradedDuplex => |socket| { + return socket.encodeAndWrite(data, msg_more); + }, + .pipe => |pipe| { + return if (Environment.isWindows) pipe.encodeAndWrite(data, msg_more) else 0; + }, + .done => |socket| { + const result = us_socket_write( + comptime ssl_int, + socket, + data.ptr, + // truncate to 31 bits since sign bit exists + @as(i32, @intCast(@as(u31, @truncate(data.len)))), + @as(i32, @intFromBool(msg_more)), + ); - if (comptime Environment.allow_assert) { - debug("us_socket_write({*}, {d}) = {d}", .{ this.getNativeHandle(), data.len, result }); - } + if (comptime Environment.allow_assert) { + debug("us_socket_write({*}, {d}) = {d}", .{ this.getNativeHandle(), data.len, result }); + } - return result; + return result; + }, + .connecting, .detached => return 0, + } } pub fn rawWrite(this: ThisSocket, data: []const u8, msg_more: bool) i32 { - const socket = this.socket.get() orelse return 0; - return us_socket_raw_write( - comptime ssl_int, - socket, - data.ptr, - // truncate to 31 bits since sign bit exists - @as(i32, @intCast(@as(u31, @truncate(data.len)))), - @as(i32, @intFromBool(msg_more)), - ); + switch (this.socket) { + .done => |socket| { + return us_socket_raw_write( + comptime ssl_int, + socket, + data.ptr, + // truncate to 31 bits since sign bit exists + @as(i32, @intCast(@as(u31, @truncate(data.len)))), + @as(i32, @intFromBool(msg_more)), + ); + }, + .connecting, .detached => return 0, + .upgradedDuplex => |socket| { + return socket.rawWrite(data, msg_more); + }, + .pipe => |pipe| { + return if (Environment.isWindows) pipe.rawWrite(data, msg_more) else 0; + }, + } } pub fn shutdown(this: ThisSocket) void { // debug("us_socket_shutdown({d})", .{@intFromPtr(this.socket)}); @@ -405,6 +1540,13 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { socket, ); }, + .detached => {}, + .upgradedDuplex => |socket| { + socket.shutdown(); + }, + .pipe => |pipe| { + if (Environment.isWindows) pipe.shutdown(); + }, } } @@ -424,6 +1566,13 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { socket, ); }, + .detached => {}, + .upgradedDuplex => |socket| { + socket.shutdownRead(); + }, + .pipe => |pipe| { + if (Environment.isWindows) pipe.shutdownRead(); + }, } } @@ -441,6 +1590,13 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { socket, ) > 0; }, + .detached => return true, + .upgradedDuplex => |socket| { + return socket.isShutdown(); + }, + .pipe => |pipe| { + return if (Environment.isWindows) pipe.isShutdown() else false; + }, } } @@ -466,64 +1622,49 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { socket, ); }, + .detached => return 0, + .upgradedDuplex => |socket| { + return socket.sslError().error_no; + }, + .pipe => |pipe| { + return if (Environment.isWindows) pipe.sslError().error_no else 0; + }, } } pub fn isClosed(this: ThisSocket) bool { + return this.socket.isClosed(comptime is_ssl); + } + + pub fn close(this: ThisSocket, code: CloseCode) void { + return this.socket.close(comptime is_ssl, code); + } + pub fn localPort(this: ThisSocket) i32 { switch (this.socket) { .done => |socket| { - return us_socket_is_closed( + return us_socket_local_port( comptime ssl_int, socket, - ) > 0; - }, - .connecting => |socket| { - return us_connecting_socket_is_closed( - comptime ssl_int, - socket, - ) > 0; + ); }, + .pipe, .upgradedDuplex, .connecting, .detached => return 0, } } - - pub fn close(this: ThisSocket, code: CloseCode) void { - // debug("us_socket_close({d})", .{@intFromPtr(this.socket)}); + pub fn remoteAddress(this: ThisSocket, buf: [*]u8, length: *i32) void { switch (this.socket) { .done => |socket| { - _ = us_socket_close( + return us_socket_remote_address( comptime ssl_int, socket, - code, - null, + buf, + length, ); }, - .connecting => |socket| { - _ = us_connecting_socket_close( - comptime ssl_int, - socket, - ); + .pipe, .upgradedDuplex, .connecting, .detached => return { + length.* = 0; }, } } - pub fn localPort(this: ThisSocket) i32 { - const socket = this.socket.get() orelse return 0; - return us_socket_local_port( - comptime ssl_int, - socket, - ); - } - pub fn remoteAddress(this: ThisSocket, buf: [*]u8, length: *i32) void { - const socket = this.socket.get() orelse { - length.* = 0; - return; - }; - return us_socket_remote_address( - comptime ssl_int, - socket, - buf, - length, - ); - } /// Get the local address of a socket in binary format. /// @@ -533,19 +1674,23 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { /// # Returns /// This function returns a slice of the buffer on success, or null on failure. pub fn localAddressBinary(this: ThisSocket, buf: []u8) ?[]const u8 { - const socket = this.socket.get() orelse return null; - var length: i32 = @intCast(buf.len); - us_socket_local_address( - comptime ssl_int, - socket, - buf.ptr, - &length, - ); + switch (this.socket) { + .done => |socket| { + var length: i32 = @intCast(buf.len); + us_socket_local_address( + comptime ssl_int, + socket, + buf.ptr, + &length, + ); - if (length <= 0) { - return null; + if (length <= 0) { + return null; + } + return buf[0..@intCast(length)]; + }, + .pipe, .upgradedDuplex, .connecting, .detached => return null, } - return buf[0..@intCast(length)]; } /// Get the local address of a socket in text format. @@ -633,6 +1778,21 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { return ctx; } + pub fn fromDuplex( + duplex: *UpgradedDuplex, + ) ThisSocket { + return ThisSocket{ .socket = .{ .upgradedDuplex = duplex } }; + } + + pub fn fromNamedPipe( + pipe: *WindowsNamedPipe, + ) ThisSocket { + if (Environment.isWindows) { + return ThisSocket{ .socket = .{ .pipe = pipe } }; + } + @compileError("WindowsNamedPipe is only available on Windows"); + } + pub fn fromFd( ctx: *SocketContext, handle: bun.FileDescriptor, @@ -642,8 +1802,9 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { ) ?ThisSocket { const socket_ = ThisSocket{ .socket = .{ .done = us_socket_from_fd(ctx, @sizeOf(*anyopaque), bun.socketcast(handle)) orelse return null } }; - const holder = socket_.ext(*anyopaque); - holder.* = this; + if (socket_.ext(*anyopaque)) |holder| { + holder.* = this; + } if (comptime socket_field_name) |field| { @field(this, field) = socket_; @@ -679,8 +1840,9 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { return error.FailedToOpenSocket; const socket_ = ThisSocket{ .socket = .{ .done = socket } }; - const holder = socket_.ext(*anyopaque); - holder.* = ctx; + if (socket_.ext(*anyopaque)) |holder| { + holder.* = ctx; + } return socket_; } @@ -721,9 +1883,9 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { ThisSocket{ .socket = .{ .connecting = @ptrCast(socket_ptr) }, }; - - const holder = socket.ext(*anyopaque); - holder.* = ptr; + if (socket.ext(*anyopaque)) |holder| { + holder.* = ptr; + } return socket; } @@ -751,7 +1913,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } if (comptime deref_) { - return (SocketHandlerType.from(socket)).ext(ContextType).*; + return (SocketHandlerType.from(socket)).ext(ContextType).?.*; } return (SocketHandlerType.from(socket)).ext(ContextType); @@ -806,7 +1968,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const val = if (comptime ContextType == anyopaque) us_connecting_socket_ext(comptime ssl_int, socket) else if (comptime deref_) - SocketHandlerType.fromConnecting(socket).ext(ContextType).* + SocketHandlerType.fromConnecting(socket).ext(ContextType).?.* else SocketHandlerType.fromConnecting(socket).ext(ContextType); Fields.onConnectError( @@ -820,7 +1982,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const val = if (comptime ContextType == anyopaque) us_socket_ext(comptime ssl_int, socket) else if (comptime deref_) - SocketHandlerType.from(socket).ext(ContextType).* + SocketHandlerType.from(socket).ext(ContextType).?.* else SocketHandlerType.from(socket).ext(ContextType); Fields.onConnectError( @@ -883,7 +2045,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } if (comptime deref_) { - return (ThisSocket.from(socket)).ext(ContextType).*; + return (ThisSocket.from(socket)).ext(ContextType).?.*; } return (ThisSocket.from(socket)).ext(ContextType); @@ -945,7 +2107,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const val = if (comptime ContextType == anyopaque) us_connecting_socket_ext(comptime ssl_int, socket) else if (comptime deref_) - ThisSocket.fromConnecting(socket).ext(ContextType).* + ThisSocket.fromConnecting(socket).ext(ContextType).?.* else ThisSocket.fromConnecting(socket).ext(ContextType); Fields.onConnectError( @@ -959,14 +2121,14 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const val = if (comptime ContextType == anyopaque) us_socket_ext(comptime ssl_int, socket) else if (comptime deref_) - ThisSocket.from(socket).ext(ContextType).* + ThisSocket.from(socket).ext(ContextType).?.* else ThisSocket.from(socket).ext(ContextType); // We close immediately in this case // uSockets doesn't know if this is a TLS socket or not. - // So we have to do that logic in here. - ThisSocket.from(socket).close(.failure); + // So we need to close it like a TCP socket. + NewSocketHandler(false).from(socket).close(.failure); Fields.onConnectError( val, @@ -1033,14 +2195,14 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const new_socket = us_socket_context_adopt_socket(comptime ssl_int, socket_ctx, socket, -1) orelse return false; bun.assert(new_socket == socket); var adopted = ThisSocket.from(new_socket); - const holder = adopted.ext(*anyopaque); - holder.* = ctx; + if (adopted.ext(*anyopaque)) |holder| { + holder.* = ctx; + } @field(ctx, socket_field_name) = adopted; return true; } }; } - pub const SocketTCP = NewSocketHandler(false); pub const SocketTLS = NewSocketHandler(true); @@ -1087,7 +2249,7 @@ pub const Timer = opaque { pub const SocketContext = opaque { pub fn getNativeHandle(this: *SocketContext, comptime ssl: bool) *anyopaque { - return us_socket_context_get_native_handle(comptime @as(i32, @intFromBool(ssl)), this).?; + return us_socket_context_get_native_handle(@intFromBool(ssl), this).?; } fn _deinit_ssl(this: *SocketContext) void { @@ -1144,10 +2306,7 @@ pub const SocketContext = opaque { } fn getLoop(this: *SocketContext, ssl: bool) ?*Loop { - if (ssl) { - return us_socket_context_loop(@as(i32, 1), this); - } - return us_socket_context_loop(@as(i32, 0), this); + return us_socket_context_loop(@intFromBool(ssl), this); } /// closes and deinit the SocketContexts @@ -1165,7 +2324,7 @@ pub const SocketContext = opaque { pub fn close(this: *SocketContext, ssl: bool) void { debug("us_socket_context_close({d})", .{@intFromPtr(this)}); - us_socket_context_close(@as(i32, @intFromBool(ssl)), this); + us_socket_context_close(@intFromBool(ssl), this); } pub fn ext(this: *SocketContext, ssl: bool, comptime ContextType: type) ?*ContextType { @@ -1366,11 +2525,11 @@ pub const us_bun_socket_context_options_t = extern struct { ca_file_name: [*c]const u8 = null, ssl_ciphers: [*c]const u8 = null, ssl_prefer_low_memory_usage: i32 = 0, - key: [*c][*c]const u8 = null, + key: ?[*]?[*:0]const u8 = null, key_count: u32 = 0, - cert: [*c][*c]const u8 = null, + cert: ?[*]?[*:0]const u8 = null, cert_count: u32 = 0, - ca: [*c][*c]const u8 = null, + ca: ?[*]?[*:0]const u8 = null, ca_count: u32 = 0, secure_options: u32 = 0, reject_unauthorized: i32 = 0, @@ -1378,12 +2537,14 @@ pub const us_bun_socket_context_options_t = extern struct { client_renegotiation_limit: u32 = 3, client_renegotiation_window: u32 = 600, }; +pub extern fn create_ssl_context_from_bun_options(options: us_bun_socket_context_options_t) ?*BoringSSL.SSL_CTX; pub const us_bun_verify_error_t = extern struct { error_no: i32 = 0, code: [*c]const u8 = null, reason: [*c]const u8 = null, }; +pub extern fn us_ssl_socket_verify_error_from_ssl(ssl: *BoringSSL.SSL) us_bun_verify_error_t; pub const us_socket_events_t = extern struct { on_open: ?*const fn (*Socket, i32, [*c]u8, i32) callconv(.C) ?*Socket = null, @@ -1410,6 +2571,8 @@ pub extern fn us_create_socket_context(ssl: i32, loop: ?*Loop, ext_size: i32, op pub extern fn us_create_bun_socket_context(ssl: i32, loop: ?*Loop, ext_size: i32, options: us_bun_socket_context_options_t) ?*SocketContext; pub extern fn us_bun_socket_context_add_server_name(ssl: i32, context: ?*SocketContext, hostname_pattern: [*c]const u8, options: us_bun_socket_context_options_t, ?*anyopaque) void; pub extern fn us_socket_context_free(ssl: i32, context: ?*SocketContext) void; +pub extern fn us_socket_context_ref(ssl: i32, context: ?*SocketContext) void; +pub extern fn us_socket_context_unref(ssl: i32, context: ?*SocketContext) void; extern fn us_socket_context_on_open(ssl: i32, context: ?*SocketContext, on_open: *const fn (*Socket, i32, [*c]u8, i32) callconv(.C) ?*Socket) void; extern fn us_socket_context_on_close(ssl: i32, context: ?*SocketContext, on_close: *const fn (*Socket, i32, ?*anyopaque) callconv(.C) ?*Socket) void; extern fn us_socket_context_on_data(ssl: i32, context: ?*SocketContext, on_data: *const fn (*Socket, [*c]u8, i32) callconv(.C) ?*Socket) void; @@ -1709,30 +2872,37 @@ pub const WebSocketBehavior = extern struct { const active_field_name = if (is_ssl) "ssl" else "tcp"; - pub fn _open(raw_ws: *RawWebSocket) callconv(.C) void { - var ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); + pub fn onOpen(raw_ws: *RawWebSocket) callconv(.C) void { + const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; - @call(bun.callmod_inline, Type.onOpen, .{ this, ws }); + @call(bun.callmod_inline, Type.onOpen, .{ + this, + ws, + }); } - pub fn _message(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize, opcode: Opcode) callconv(.C) void { - var ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); + + pub fn onMessage(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize, opcode: Opcode) callconv(.C) void { + const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; - @call( - .always_inline, - Type.onMessage, - .{ this, ws, if (length > 0) message[0..length] else "", opcode }, - ); + @call(.always_inline, Type.onMessage, .{ + this, + ws, + if (length > 0) message[0..length] else "", + opcode, + }); } - pub fn _drain(raw_ws: *RawWebSocket) callconv(.C) void { - var ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); + + pub fn onDrain(raw_ws: *RawWebSocket) callconv(.C) void { + const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; @call(bun.callmod_inline, Type.onDrain, .{ this, ws, }); } - pub fn _ping(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize) callconv(.C) void { - var ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); + + pub fn onPing(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize) callconv(.C) void { + const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; @call(bun.callmod_inline, Type.onPing, .{ this, @@ -1740,8 +2910,9 @@ pub const WebSocketBehavior = extern struct { if (length > 0) message[0..length] else "", }); } - pub fn _pong(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize) callconv(.C) void { - var ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); + + pub fn onPong(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize) callconv(.C) void { + const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; @call(bun.callmod_inline, Type.onPong, .{ this, @@ -1749,30 +2920,30 @@ pub const WebSocketBehavior = extern struct { if (length > 0) message[0..length] else "", }); } - pub fn _close(raw_ws: *RawWebSocket, code: i32, message: [*c]const u8, length: usize) callconv(.C) void { - var ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); + + pub fn onClose(raw_ws: *RawWebSocket, code: i32, message: [*c]const u8, length: usize) callconv(.C) void { + const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; - @call( - .always_inline, - Type.onClose, - .{ - this, - ws, - code, - if (length > 0 and message != null) message[0..length] else "", - }, - ); + @call(.always_inline, Type.onClose, .{ + this, + ws, + code, + if (length > 0 and message != null) message[0..length] else "", + }); } - pub fn _upgrade(ptr: *anyopaque, res: *uws_res, req: *Request, context: *uws_socket_context_t, id: usize) callconv(.C) void { - @call( - .always_inline, - Server.onWebSocketUpgrade, - .{ bun.cast(*Server, ptr), @as(*NewApp(is_ssl).Response, @ptrCast(res)), req, context, id }, - ); + + pub fn onUpgrade(ptr: *anyopaque, res: *uws_res, req: *Request, context: *uws_socket_context_t, id: usize) callconv(.C) void { + @call(.always_inline, Server.onWebSocketUpgrade, .{ + bun.cast(*Server, ptr), + @as(*NewApp(is_ssl).Response, @ptrCast(res)), + req, + context, + id, + }); } pub fn apply(behavior: WebSocketBehavior) WebSocketBehavior { - return WebSocketBehavior{ + return .{ .compression = behavior.compression, .maxPayloadLength = behavior.maxPayloadLength, .idleTimeout = behavior.idleTimeout, @@ -1781,13 +2952,13 @@ pub const WebSocketBehavior = extern struct { .resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend, .sendPingsAutomatically = behavior.sendPingsAutomatically, .maxLifetime = behavior.maxLifetime, - .upgrade = _upgrade, - .open = _open, - .message = _message, - .drain = _drain, - .ping = _ping, - .pong = _pong, - .close = _close, + .upgrade = onUpgrade, + .open = onOpen, + .message = if (@hasDecl(Type, "onMessage")) onMessage else null, + .drain = if (@hasDecl(Type, "onDrain")) onDrain else null, + .ping = if (@hasDecl(Type, "onPing")) onPing else null, + .pong = if (@hasDecl(Type, "onPong")) onPong else null, + .close = onClose, }; } }; @@ -1861,40 +3032,193 @@ pub const SocketAddress = struct { is_ipv6: bool, }; +pub const AnyResponse = union(enum) { + SSL: *NewApp(true).Response, + TCP: *NewApp(false).Response, + + pub fn timeout(this: AnyResponse, seconds: u8) void { + switch (this) { + .SSL => |resp| resp.timeout(seconds), + .TCP => |resp| resp.timeout(seconds), + } + } + + pub fn writeStatus(this: AnyResponse, status: []const u8) void { + return switch (this) { + .SSL => |resp| resp.writeStatus(status), + .TCP => |resp| resp.writeStatus(status), + }; + } + + pub fn writeHeader(this: AnyResponse, key: []const u8, value: []const u8) void { + return switch (this) { + .SSL => |resp| resp.writeHeader(key, value), + .TCP => |resp| resp.writeHeader(key, value), + }; + } + + pub fn write(this: AnyResponse, data: []const u8) void { + return switch (this) { + .SSL => |resp| resp.write(data), + .TCP => |resp| resp.write(data), + }; + } + + pub fn end(this: AnyResponse, data: []const u8, close_connection: bool) void { + return switch (this) { + .SSL => |resp| resp.end(data, close_connection), + .TCP => |resp| resp.end(data, close_connection), + }; + } + + pub fn shouldCloseConnection(this: AnyResponse) bool { + return switch (this) { + .SSL => |resp| resp.shouldCloseConnection(), + .TCP => |resp| resp.shouldCloseConnection(), + }; + } + + pub fn tryEnd(this: AnyResponse, data: []const u8, total_size: usize, close_connection: bool) bool { + return switch (this) { + .SSL => |resp| resp.tryEnd(data, total_size, close_connection), + .TCP => |resp| resp.tryEnd(data, total_size, close_connection), + }; + } + + pub fn pause(this: AnyResponse) void { + return switch (this) { + .SSL => |resp| resp.pause(), + .TCP => |resp| resp.pause(), + }; + } + + pub fn @"resume"(this: AnyResponse) void { + return switch (this) { + .SSL => |resp| resp.@"resume"(), + .TCP => |resp| resp.@"resume"(), + }; + } + + pub fn writeHeaderInt(this: AnyResponse, key: []const u8, value: u64) void { + return switch (this) { + .SSL => |resp| resp.writeHeaderInt(key, value), + .TCP => |resp| resp.writeHeaderInt(key, value), + }; + } + + pub fn endWithoutBody(this: AnyResponse, close_connection: bool) void { + return switch (this) { + .SSL => |resp| resp.endWithoutBody(close_connection), + .TCP => |resp| resp.endWithoutBody(close_connection), + }; + } + + pub fn onWritable(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType, u64, AnyResponse) bool, opcional_data: UserDataType) void { + const wrapper = struct { + pub fn ssl_handler(user_data: UserDataType, offset: u64, resp: *NewApp(true).Response) bool { + return handler(user_data, offset, .{ .SSL = resp }); + } + + pub fn tcp_handler(user_data: UserDataType, offset: u64, resp: *NewApp(false).Response) bool { + return handler(user_data, offset, .{ .TCP = resp }); + } + }; + return switch (this) { + .SSL => |resp| resp.onWritable(UserDataType, wrapper.ssl_handler, opcional_data), + .TCP => |resp| resp.onWritable(UserDataType, wrapper.tcp_handler, opcional_data), + }; + } + + pub fn onAborted(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType, AnyResponse) void, opcional_data: UserDataType) void { + const wrapper = struct { + pub fn ssl_handler(user_data: UserDataType, resp: *NewApp(true).Response) void { + handler(user_data, .{ .SSL = resp }); + } + pub fn tcp_handler(user_data: UserDataType, resp: *NewApp(false).Response) void { + handler(user_data, .{ .TCP = resp }); + } + }; + return switch (this) { + .SSL => |resp| resp.onAborted(UserDataType, wrapper.ssl_handler, opcional_data), + .TCP => |resp| resp.onAborted(UserDataType, wrapper.tcp_handler, opcional_data), + }; + } + + pub fn clearAborted(this: AnyResponse) void { + return switch (this) { + .SSL => |resp| resp.clearAborted(), + .TCP => |resp| resp.clearAborted(), + }; + } + pub fn clearTimeout(this: AnyResponse) void { + return switch (this) { + .SSL => |resp| resp.clearTimeout(), + .TCP => |resp| resp.clearTimeout(), + }; + } + + pub fn clearOnWritable(this: AnyResponse) void { + return switch (this) { + .SSL => |resp| resp.clearOnWritable(), + .TCP => |resp| resp.clearOnWritable(), + }; + } + + pub fn clearOnData(this: AnyResponse) void { + return switch (this) { + .SSL => |resp| resp.clearOnData(), + .TCP => |resp| resp.clearOnData(), + }; + } + + pub fn endStream(this: AnyResponse, close_connection: bool) void { + return switch (this) { + .SSL => |resp| resp.endStream(close_connection), + .TCP => |resp| resp.endStream(close_connection), + }; + } + + pub fn corked(this: AnyResponse, comptime handler: anytype, args_tuple: anytype) void { + return switch (this) { + .SSL => |resp| resp.corked(handler, args_tuple), + .TCP => |resp| resp.corked(handler, args_tuple), + }; + } + + pub fn runCorkedWithType(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType) void, opcional_data: UserDataType) void { + return switch (this) { + .SSL => |resp| resp.runCorkedWithType(UserDataType, handler, opcional_data), + .TCP => |resp| resp.runCorkedWithType(UserDataType, handler, opcional_data), + }; + } +}; pub fn NewApp(comptime ssl: bool) type { return opaque { const ssl_flag = @as(i32, @intFromBool(ssl)); const ThisApp = @This(); pub fn close(this: *ThisApp) void { - if (comptime is_bindgen) { - unreachable; - } - return uws_app_close(ssl_flag, @as(*uws_app_s, @ptrCast(this))); } pub fn create(opts: us_bun_socket_context_options_t) *ThisApp { - if (comptime is_bindgen) { - unreachable; - } return @as(*ThisApp, @ptrCast(uws_create_app(ssl_flag, opts))); } pub fn destroy(app: *ThisApp) void { + return uws_app_destroy(ssl_flag, @as(*uws_app_s, @ptrCast(app))); + } + + pub fn clearRoutes(app: *ThisApp) void { if (comptime is_bindgen) { unreachable; } - return uws_app_destroy(ssl_flag, @as(*uws_app_s, @ptrCast(app))); + return uws_app_clear_routes(ssl_flag, @as(*uws_app_t, @ptrCast(app))); } fn RouteHandler(comptime UserDataType: type, comptime handler: fn (UserDataType, *Request, *Response) void) type { return struct { pub fn handle(res: *uws_res, req: *Request, user_data: ?*anyopaque) callconv(.C) void { - if (comptime is_bindgen) { - unreachable; - } - if (comptime UserDataType == void) { return @call( .always_inline, @@ -1922,15 +3246,9 @@ pub fn NewApp(comptime ssl: bool) type { pub const ListenSocket = opaque { pub inline fn close(this: *ThisApp.ListenSocket) void { - if (comptime is_bindgen) { - unreachable; - } return us_listen_socket_close(ssl_flag, @as(*uws.ListenSocket, @ptrCast(this))); } pub inline fn getLocalPort(this: *ThisApp.ListenSocket) i32 { - if (comptime is_bindgen) { - unreachable; - } return us_socket_local_port(ssl_flag, @as(*uws.Socket, @ptrCast(this))); } @@ -1946,10 +3264,7 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_get(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_get(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn post( app: *ThisApp, @@ -1958,10 +3273,7 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_post(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_post(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn options( app: *ThisApp, @@ -1970,10 +3282,7 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_options(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_options(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn delete( app: *ThisApp, @@ -1982,10 +3291,7 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_delete(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_delete(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn patch( app: *ThisApp, @@ -1994,10 +3300,7 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_patch(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_patch(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn put( app: *ThisApp, @@ -2006,22 +3309,16 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_put(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_put(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn head( app: *ThisApp, - pattern: [:0]const u8, + pattern: []const u8, comptime UserDataType: type, user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_head(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_head(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern.ptr, pattern.len, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn connect( app: *ThisApp, @@ -2030,10 +3327,7 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_connect(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_connect(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn trace( app: *ThisApp, @@ -2042,30 +3336,21 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_trace(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_trace(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn any( app: *ThisApp, - pattern: [:0]const u8, + pattern: []const u8, comptime UserDataType: type, user_data: UserDataType, comptime handler: (fn (UserDataType, *Request, *Response) void), ) void { - if (comptime is_bindgen) { - unreachable; - } - uws_app_any(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data); + uws_app_any(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern.ptr, pattern.len, RouteHandler(UserDataType, handler).handle, if (UserDataType == void) null else user_data); } pub fn domain(app: *ThisApp, pattern: [:0]const u8) void { uws_app_domain(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern); } pub fn run(app: *ThisApp) void { - if (comptime is_bindgen) { - unreachable; - } return uws_app_run(ssl_flag, @as(*uws_app_t, @ptrCast(app))); } pub fn listen( @@ -2075,9 +3360,6 @@ pub fn NewApp(comptime ssl: bool) type { user_data: UserData, comptime handler: fn (UserData, ?*ThisApp.ListenSocket, uws_app_listen_config_t) void, ) void { - if (comptime is_bindgen) { - unreachable; - } const Wrapper = struct { pub fn handle(socket: ?*uws.ListenSocket, conf: uws_app_listen_config_t, data: ?*anyopaque) callconv(.C) void { if (comptime UserData == void) { @@ -2200,6 +3482,10 @@ pub fn NewApp(comptime ssl: bool) type { return uws_res_state(ssl_flag, @as(*const uws_res, @ptrCast(@alignCast(res)))); } + pub fn shouldCloseConnection(this: *const Response) bool { + return this.state().isHttpConnectionClose(); + } + pub fn prepareForSendfile(res: *Response) void { return uws_res_prepare_for_sendfile(ssl_flag, res.downcast()); } @@ -2231,6 +3517,15 @@ pub fn NewApp(comptime ssl: bool) type { pub fn endWithoutBody(res: *Response, close_connection: bool) void { uws_res_end_without_body(ssl_flag, res.downcast(), close_connection); } + pub fn endSendFile(res: *Response, write_offset: u64, close_connection: bool) void { + uws_res_end_sendfile(ssl_flag, res.downcast(), write_offset, close_connection); + } + pub fn timeout(res: *Response, seconds: u8) void { + uws_res_timeout(ssl_flag, res.downcast(), seconds); + } + pub fn resetTimeout(res: *Response) void { + uws_res_reset_timeout(ssl_flag, res.downcast()); + } pub fn write(res: *Response, data: []const u8) bool { return uws_res_write(ssl_flag, res.downcast(), data.ptr, data.len); } @@ -2282,7 +3577,7 @@ pub fn NewApp(comptime ssl: bool) type { pub fn onWritable( res: *Response, comptime UserDataType: type, - comptime handler: fn (UserDataType, u64, *Response) callconv(.C) bool, + comptime handler: fn (UserDataType, u64, *Response) bool, user_data: UserDataType, ) void { const Wrapper = struct { @@ -2325,7 +3620,22 @@ pub fn NewApp(comptime ssl: bool) type { pub fn clearAborted(res: *Response) void { uws_res_on_aborted(ssl_flag, res.downcast(), null, null); } + pub fn onTimeout(res: *Response, comptime UserDataType: type, comptime handler: fn (UserDataType, *Response) void, opcional_data: UserDataType) void { + const Wrapper = struct { + pub fn handle(this: *uws_res, user_data: ?*anyopaque) callconv(.C) void { + if (comptime UserDataType == void) { + @call(bun.callmod_inline, handler, .{ {}, castRes(this), {} }); + } else { + @call(bun.callmod_inline, handler, .{ @as(UserDataType, @ptrCast(@alignCast(user_data.?))), castRes(this) }); + } + } + }; + uws_res_on_timeout(ssl_flag, res.downcast(), Wrapper.handle, opcional_data); + } + pub fn clearTimeout(res: *Response) void { + uws_res_on_timeout(ssl_flag, res.downcast(), null, null); + } pub fn clearOnData(res: *Response) void { uws_res_on_data(ssl_flag, res.downcast(), null, null); } @@ -2365,22 +3675,19 @@ pub fn NewApp(comptime ssl: bool) type { pub fn corked( res: *Response, - comptime Function: anytype, - args: anytype, - ) @typeInfo(@TypeOf(Function)).Fn.return_type.? { + comptime handler: anytype, + args_tuple: anytype, + ) void { const Wrapper = struct { - opts: @TypeOf(args), - result: @typeInfo(@TypeOf(Function)).Fn.return_type.? = undefined, - pub fn run(this: *@This()) void { - this.result = Function(this.opts); + const handler_fn = handler; + const Args = *@TypeOf(args_tuple); + pub fn handle(user_data: ?*anyopaque) callconv(.C) void { + const args: Args = @alignCast(@ptrCast(user_data.?)); + @call(.always_inline, handler_fn, args.*); } }; - var wrapped = Wrapper{ - .opts = args, - .result = undefined, - }; - runCorkedWithType(res, *Wrapper, Wrapper.run, &wrapped); - return wrapped.result; + + uws_res_cork(ssl_flag, res.downcast(), @constCast(@ptrCast(&args_tuple)), Wrapper.handle); } pub fn runCorkedWithType( @@ -2576,10 +3883,10 @@ extern fn uws_app_options(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, hand extern fn uws_app_delete(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; extern fn uws_app_patch(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; extern fn uws_app_put(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; -extern fn uws_app_head(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; +extern fn uws_app_head(ssl: i32, app: *uws_app_t, pattern: [*]const u8, pattern_len: usize, handler: uws_method_handler, user_data: ?*anyopaque) void; extern fn uws_app_connect(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; extern fn uws_app_trace(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; -extern fn uws_app_any(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void; +extern fn uws_app_any(ssl: i32, app: *uws_app_t, pattern: [*]const u8, pattern_len: usize, handler: uws_method_handler, user_data: ?*anyopaque) void; extern fn uws_app_run(ssl: i32, *uws_app_t) void; extern fn uws_app_domain(ssl: i32, app: *uws_app_t, domain: [*c]const u8) void; extern fn uws_app_listen(ssl: i32, app: *uws_app_t, port: i32, handler: uws_listen_handler, user_data: ?*anyopaque) void; @@ -2642,6 +3949,9 @@ extern fn uws_res_write_status(ssl: i32, res: *uws_res, status: [*c]const u8, le extern fn uws_res_write_header(ssl: i32, res: *uws_res, key: [*c]const u8, key_length: usize, value: [*c]const u8, value_length: usize) void; extern fn uws_res_write_header_int(ssl: i32, res: *uws_res, key: [*c]const u8, key_length: usize, value: u64) void; extern fn uws_res_end_without_body(ssl: i32, res: *uws_res, close_connection: bool) void; +extern fn uws_res_end_sendfile(ssl: i32, res: *uws_res, write_offset: u64, close_connection: bool) void; +extern fn uws_res_timeout(ssl: i32, res: *uws_res, timeout: u8) void; +extern fn uws_res_reset_timeout(ssl: i32, res: *uws_res) void; extern fn uws_res_write(ssl: i32, res: *uws_res, data: [*c]const u8, length: usize) bool; extern fn uws_res_get_write_offset(ssl: i32, res: *uws_res) u64; extern fn uws_res_override_write_offset(ssl: i32, res: *uws_res, u64) void; @@ -2649,6 +3959,8 @@ extern fn uws_res_has_responded(ssl: i32, res: *uws_res) bool; extern fn uws_res_on_writable(ssl: i32, res: *uws_res, handler: ?*const fn (*uws_res, u64, ?*anyopaque) callconv(.C) bool, user_data: ?*anyopaque) void; extern fn uws_res_clear_on_writable(ssl: i32, res: *uws_res) void; extern fn uws_res_on_aborted(ssl: i32, res: *uws_res, handler: ?*const fn (*uws_res, ?*anyopaque) callconv(.C) void, opcional_data: ?*anyopaque) void; +extern fn uws_res_on_timeout(ssl: i32, res: *uws_res, handler: ?*const fn (*uws_res, ?*anyopaque) callconv(.C) void, opcional_data: ?*anyopaque) void; + extern fn uws_res_on_data( ssl: i32, res: *uws_res, @@ -2721,16 +4033,17 @@ pub const SendStatus = enum(c_uint) { dropped = 2, }; pub const uws_app_listen_config_t = extern struct { - port: i32, - host: [*c]const u8 = null, - options: i32, + port: c_int, + host: ?[*:0]const u8 = null, + options: c_int = 0, }; +pub const AppListenConfig = uws_app_listen_config_t; extern fn us_socket_mark_needs_more_not_ssl(socket: ?*uws_res) void; extern fn uws_res_state(ssl: c_int, res: *const uws_res) State; -pub const State = enum(i32) { +pub const State = enum(u8) { HTTP_STATUS_CALLED = 1, HTTP_WRITE_CALLED = 2, HTTP_END_CALLED = 4, @@ -2914,6 +4227,108 @@ pub fn newSocketFromPair(ctx: *SocketContext, ext_size: c_int, fds: *[2]LIBUS_SO extern fn us_socket_get_error(ssl_flag: c_int, socket: *Socket) c_int; +pub const AnySocket = union(enum) { + SocketTCP: SocketTCP, + SocketTLS: SocketTLS, + + pub fn setTimeout(this: AnySocket, seconds: c_uint) void { + switch (this) { + .SocketTCP => this.SocketTCP.setTimeout(seconds), + .SocketTLS => this.SocketTLS.setTimeout(seconds), + } + } + + pub fn shutdown(this: AnySocket) void { + debug("us_socket_shutdown({d})", .{@intFromPtr(this.socket())}); + return us_socket_shutdown( + @intFromBool(this.isSSL()), + this.socket(), + ); + } + pub fn shutdownRead(this: AnySocket) void { + debug("us_socket_shutdown_read({d})", .{@intFromPtr(this.socket())}); + return us_socket_shutdown_read( + @intFromBool(this.isSSL()), + this.socket(), + ); + } + pub fn isShutdown(this: AnySocket) bool { + return switch (this) { + .SocketTCP => this.SocketTCP.isShutdown(), + .SocketTLS => this.SocketTLS.isShutdown(), + }; + } + pub fn isClosed(this: AnySocket) bool { + return switch (this) { + inline else => |s| s.isClosed(), + }; + } + pub fn close(this: AnySocket) void { + switch (this) { + inline else => |s| s.close(.normal), + } + } + + pub fn terminate(this: AnySocket) void { + switch (this) { + inline else => |s| s.close(.failure), + } + } + + pub fn write(this: AnySocket, data: []const u8, msg_more: bool) i32 { + return switch (this) { + .SocketTCP => return this.SocketTCP.write(data, msg_more), + .SocketTLS => return this.SocketTLS.write(data, msg_more), + }; + } + + pub fn getNativeHandle(this: AnySocket) ?*anyopaque { + return switch (this.socket()) { + .done => |sock| us_socket_get_native_handle( + @intFromBool(this.isSSL()), + sock, + ).?, + else => null, + }; + } + + pub fn localPort(this: AnySocket) i32 { + return us_socket_local_port( + @intFromBool(this.isSSL()), + this.socket(), + ); + } + + pub fn isSSL(this: AnySocket) bool { + return switch (this) { + .SocketTCP => false, + .SocketTLS => true, + }; + } + + pub fn socket(this: AnySocket) InternalSocket { + return switch (this) { + .SocketTCP => this.SocketTCP.socket, + .SocketTLS => this.SocketTLS.socket, + }; + } + + pub fn ext(this: AnySocket, comptime ContextType: type) ?*ContextType { + const ptr = us_socket_ext( + this.isSSL(), + this.socket(), + ) orelse return null; + + return @ptrCast(@alignCast(ptr)); + } + pub fn context(this: AnySocket) *SocketContext { + return us_socket_context( + this.isSSL(), + this.socket(), + ).?; + } +}; + pub const udp = struct { pub const Socket = opaque { const This = @This(); @@ -2989,3 +4404,10 @@ pub const udp = struct { extern fn us_udp_packet_buffer_payload(buf: ?*PacketBuffer, index: c_int) [*]u8; extern fn us_udp_packet_buffer_payload_length(buf: ?*PacketBuffer, index: c_int) c_int; }; + +extern fn bun_clear_loop_at_thread_exit() void; +pub fn onThreadExit() void { + bun_clear_loop_at_thread_exit(); +} + +extern fn uws_app_clear_routes(ssl_flag: c_int, app: *uws_app_t) void; diff --git a/src/deps/zig b/src/deps/zig deleted file mode 160000 index 98814a6069149..0000000000000 --- a/src/deps/zig +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 98814a60691499f1f35b182921c3af484002aba2 diff --git a/src/deps/zlib b/src/deps/zlib deleted file mode 160000 index 886098f3f3396..0000000000000 --- a/src/deps/zlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 886098f3f339617b4243b286f5ed364b9989e245 diff --git a/src/deps/zlib.posix.zig b/src/deps/zlib.posix.zig index b38cf143796c9..0cb5dec403704 100644 --- a/src/deps/zlib.posix.zig +++ b/src/deps/zlib.posix.zig @@ -38,7 +38,7 @@ pub const zStream_struct = extern struct { total_out: uLong, /// last error message, NULL if no error - err_msg: [*c]const u8, + err_msg: ?[*:0]const u8, /// not visible by applications internal_state: ?*struct_internal_state, diff --git a/src/deps/zstd b/src/deps/zstd deleted file mode 160000 index 794ea1b0afca0..0000000000000 --- a/src/deps/zstd +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 794ea1b0afca0f020f4e57b6732332231fb23c70 diff --git a/src/dns.zig b/src/dns.zig index e3ed3e16f5f85..a72765b4b3d68 100644 --- a/src/dns.zig +++ b/src/dns.zig @@ -42,7 +42,12 @@ pub const GetAddrInfo = struct { pub const Options = packed struct { family: Family = .unspecified, - socktype: SocketType = .unspecified, + /// Leaving this unset leads to many duplicate addresses returned. + /// Node hardcodes to `SOCK_STREAM`. + /// There don't seem to be any issues in Node's repo about this + /// So I think it's likely that nobody actually needs `SOCK_DGRAM` as a flag + /// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/cares_wrap.cc#L1609 + socktype: SocketType = .stream, protocol: Protocol = .unspecified, backend: Backend = Backend.default, flags: i32 = 0, @@ -171,7 +176,8 @@ pub const GetAddrInfo = struct { pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !SocketType { if (value.isEmptyOrUndefinedOrNull()) - return .unspecified; + // Default to .stream + return .stream; if (value.isNumber()) { return switch (value.to(i32)) { diff --git a/src/env.zig b/src/env.zig index 583afa64e028b..2f6f528abc016 100644 --- a/src/env.zig +++ b/src/env.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const bun = @import("root").bun; pub const BuildTarget = enum { native, wasm, wasi }; pub const build_target: BuildTarget = brk: { @@ -26,26 +27,26 @@ pub const isX86 = @import("builtin").target.cpu.arch.isX86(); pub const isX64 = @import("builtin").target.cpu.arch == .x86_64; pub const allow_assert = isDebug or isTest or std.builtin.Mode.ReleaseSafe == @import("builtin").mode; -const BuildOptions = if (isTest) struct { - pub const baseline = false; - pub const sha = "0000000000000000000000000000000000000000"; - pub const is_canary = false; - pub const base_path = "/tmp"; - pub const canary_revision = 0; -} else @import("root").build_options; +pub const build_options = @import("build_options"); -pub const baseline = BuildOptions.baseline; +pub const reported_nodejs_version = build_options.reported_nodejs_version; +pub const baseline = build_options.baseline; pub const enableSIMD: bool = !baseline; -pub const git_sha = BuildOptions.sha; -pub const git_sha_short = if (BuildOptions.sha.len > 0) BuildOptions.sha[0..9] else ""; -pub const git_sha_shorter = if (BuildOptions.sha.len > 0) BuildOptions.sha[0..6] else ""; -pub const is_canary = BuildOptions.is_canary; -pub const canary_revision = if (is_canary) BuildOptions.canary_revision else ""; +pub const git_sha = build_options.sha; +pub const git_sha_short = if (build_options.sha.len > 0) build_options.sha[0..9] else ""; +pub const git_sha_shorter = if (build_options.sha.len > 0) build_options.sha[0..6] else ""; +pub const is_canary = build_options.is_canary; +pub const canary_revision = if (is_canary) build_options.canary_revision else ""; pub const dump_source = isDebug and !isTest; -pub const base_path = BuildOptions.base_path ++ "/"; -pub const enable_logs = BuildOptions.enable_logs; +pub const base_path = build_options.base_path; +pub const enable_logs = build_options.enable_logs or isDebug; -pub const version: std.SemanticVersion = BuildOptions.version; +/// See -Dforce_embed_code +pub const embed_code = build_options.embed_code; + +pub const codegen_path = build_options.codegen_path; + +pub const version: std.SemanticVersion = build_options.version; pub const version_string = std.fmt.comptimePrint("{d}.{d}.{d}", .{ version.major, version.minor, version.patch }); pub inline fn onlyMac() void { @@ -61,26 +62,23 @@ pub const OperatingSystem = enum { // wAsM is nOt aN oPeRaTiNg SyStEm wasm, - pub const names = @import("root").bun.ComptimeStringMap( - OperatingSystem, - &.{ - .{ "windows", OperatingSystem.windows }, - .{ "win32", OperatingSystem.windows }, - .{ "win", OperatingSystem.windows }, - .{ "win64", OperatingSystem.windows }, - .{ "win_x64", OperatingSystem.windows }, - .{ "darwin", OperatingSystem.mac }, - .{ "macos", OperatingSystem.mac }, - .{ "macOS", OperatingSystem.mac }, - .{ "mac", OperatingSystem.mac }, - .{ "apple", OperatingSystem.mac }, - .{ "linux", OperatingSystem.linux }, - .{ "Linux", OperatingSystem.linux }, - .{ "linux-gnu", OperatingSystem.linux }, - .{ "gnu/linux", OperatingSystem.linux }, - .{ "wasm", OperatingSystem.wasm }, - }, - ); + pub const names = bun.ComptimeStringMap(OperatingSystem, &.{ + .{ "windows", .windows }, + .{ "win32", .windows }, + .{ "win", .windows }, + .{ "win64", .windows }, + .{ "win_x64", .windows }, + .{ "darwin", .mac }, + .{ "macos", .mac }, + .{ "macOS", .mac }, + .{ "mac", .mac }, + .{ "apple", .mac }, + .{ "linux", .linux }, + .{ "Linux", .linux }, + .{ "linux-gnu", .linux }, + .{ "gnu/linux", .linux }, + .{ "wasm", .wasm }, + }); /// user-facing name with capitalization pub fn displayString(self: OperatingSystem) []const u8 { @@ -123,23 +121,23 @@ pub const OperatingSystem = enum { }; pub const os: OperatingSystem = if (isMac) - OperatingSystem.mac + .mac else if (isLinux) - OperatingSystem.linux + .linux else if (isWindows) - OperatingSystem.windows + .windows else if (isWasm) - OperatingSystem.wasm + .wasm else @compileError("Please add your OS to the OperatingSystem enum"); -pub const Archictecture = enum { +pub const Architecture = enum { x64, arm64, wasm, /// npm package name, `@oven-sh/bun-{os}-{arch}` - pub fn npmName(this: Archictecture) []const u8 { + pub fn npmName(this: Architecture) []const u8 { return switch (this) { .x64 => "x64", .arm64 => "aarch64", @@ -147,22 +145,21 @@ pub const Archictecture = enum { }; } - pub const names = @import("root").bun.ComptimeStringMap( - Archictecture, - &.{ - .{ "x86_64", Archictecture.x64 }, - .{ "x64", Archictecture.x64 }, - .{ "amd64", Archictecture.x64 }, - .{ "aarch64", Archictecture.arm64 }, - .{ "arm64", Archictecture.arm64 }, - .{ "wasm", Archictecture.wasm }, - }, - ); + pub const names = bun.ComptimeStringMap(Architecture, &.{ + .{ "x86_64", .x64 }, + .{ "x64", .x64 }, + .{ "amd64", .x64 }, + .{ "aarch64", .arm64 }, + .{ "arm64", .arm64 }, + .{ "wasm", .wasm }, + }); }; -pub const arch = if (isX64) - Archictecture.x64 +pub const arch: Architecture = if (isWasm) + .wasm +else if (isX64) + .x64 else if (isAarch64) - Archictecture.arm64 + .arm64 else - @compileError("Please add your architecture to the Archictecture enum"); + @compileError("Please add your architecture to the Architecture enum"); diff --git a/src/env_loader.zig b/src/env_loader.zig index 24325a0f2022b..21f8d208789be 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -38,7 +38,7 @@ pub const Loader = struct { @".env": ?logger.Source = null, // only populated with files specified explicitely (e.g. --env-file arg) - custom_files_loaded: std.StringArrayHashMap(logger.Source), + custom_files_loaded: bun.StringArrayHashMap(logger.Source), quiet: bool = false, @@ -451,7 +451,7 @@ pub const Loader = struct { return Loader{ .map = map, .allocator = allocator, - .custom_files_loaded = std.StringArrayHashMap(logger.Source).init(allocator), + .custom_files_loaded = bun.StringArrayHashMap(logger.Source).init(allocator), }; } @@ -1088,7 +1088,7 @@ const Parser = struct { }; pub const Map = struct { - const HashTableValue = struct { + pub const HashTableValue = struct { value: string, conditional: bool, }; @@ -1096,7 +1096,7 @@ pub const Map = struct { // An issue with this exact implementation is unicode characters can technically appear in these // keys, and we use a simple toLowercase function that only applies to ascii, so this will make // some strings collide. - const HashTable = (if (Environment.isWindows) bun.CaseInsensitiveASCIIStringArrayHashMap else bun.StringArrayHashMap)(HashTableValue); + pub const HashTable = (if (Environment.isWindows) bun.CaseInsensitiveASCIIStringArrayHashMap else bun.StringArrayHashMap)(HashTableValue); const GetOrPutResult = HashTable.GetOrPutResult; diff --git a/src/fallback.ts b/src/fallback.ts index 1893fde366e71..0e0f735ceaa56 100644 --- a/src/fallback.ts +++ b/src/fallback.ts @@ -1,6 +1,5 @@ declare var document: any; import { ByteBuffer } from "peechy"; -import { FallbackStep } from "./api/schema"; import { decodeFallbackMessageContainer, FallbackMessageContainer } from "./api/schema"; function getFallbackInfo(): FallbackMessageContainer { diff --git a/src/fd.zig b/src/fd.zig index 27e9745e2ef11..3bab075c3a6b0 100644 --- a/src/fd.zig +++ b/src/fd.zig @@ -170,14 +170,29 @@ pub const FDImpl = packed struct { return switch (env.os) { else => numberToHandle(this.value.as_system), .windows => switch (this.kind) { - .system => std.debug.panic( - \\Cast {} -> FDImpl.UV makes closing impossible! - \\ - \\The supplier of this FileDescriptor should call 'bun.toLibUVOwnedFD' - \\or 'FDImpl.makeLibUVOwned', probably where open() was called. - , - .{this}, - ), + .system => { + const w = std.os.windows; + + const S = struct { + fn is_stdio_handle(id: w.DWORD, handle: w.HANDLE) bool { + const h = w.GetStdHandle(id) catch return false; + return handle == h; + } + }; + const handle = this.encode().cast(); + if (S.is_stdio_handle(w.STD_INPUT_HANDLE, handle)) return 0; + if (S.is_stdio_handle(w.STD_OUTPUT_HANDLE, handle)) return 1; + if (S.is_stdio_handle(w.STD_ERROR_HANDLE, handle)) return 2; + + std.debug.panic( + \\Cast {} -> FDImpl.UV makes closing impossible! + \\ + \\The supplier of this FileDescriptor should call 'bun.toLibUVOwnedFD' + \\or 'FDImpl.makeLibUVOwned', probably where open() was called. + , + .{this}, + ); + }, .uv => this.value.as_uv, }, }; @@ -272,7 +287,6 @@ pub const FDImpl = packed struct { if (env.isDebug) { if (result) |err| { if (err.errno == @intFromEnum(posix.E.BADF)) { - // TODO(@paperdave): Zig Compiler Bug, if you remove `this` from the log. An error is correctly printed, but with the wrong reference trace bun.Output.debugWarn("close({s}) = EBADF. This is an indication of a file descriptor UAF", .{this_fmt}); } else { log("close({s}) = {}", .{ this_fmt, err }); @@ -287,8 +301,12 @@ pub const FDImpl = packed struct { /// This "fails" if not given an int32, returning null in that case pub fn fromJS(value: JSValue) ?FDImpl { - if (!value.isInt32()) return null; - const fd = value.asInt32(); + if (!value.isAnyInt()) return null; + const fd64 = value.toInt64(); + if (fd64 < 0 or fd64 > std.math.maxInt(i32)) { + return null; + } + const fd: i32 = @intCast(fd64); if (comptime env.isWindows) { return switch (bun.FDTag.get(fd)) { .stdin => FDImpl.decode(bun.STDIN_FD), @@ -303,11 +321,12 @@ pub const FDImpl = packed struct { // If a non-number is given, returns null. // If the given number is not an fd (negative), an error is thrown and error.JSException is returned. pub fn fromJSValidated(value: JSValue, global: *JSC.JSGlobalObject, exception_ref: JSC.C.ExceptionRef) !?FDImpl { - if (!value.isInt32()) return null; - const fd = value.asInt32(); - if (!JSC.Node.Valid.fileDescriptor(fd, global, exception_ref)) { + if (!value.isAnyInt()) return null; + const fd64 = value.toInt64(); + if (!JSC.Node.Valid.fileDescriptor(fd64, global, exception_ref)) { return error.JSException; } + const fd: i32 = @intCast(fd64); if (comptime env.isWindows) { return switch (bun.FDTag.get(fd)) { diff --git a/src/feature_flags.zig b/src/feature_flags.zig index a53ad08ed16f7..0e93fb2a32a95 100644 --- a/src/feature_flags.zig +++ b/src/feature_flags.zig @@ -1,64 +1,38 @@ const env = @import("env.zig"); const bun = @import("root").bun; -pub const strong_etags_for_built_files = true; -pub const keep_alive = false; - -// Debug helpers -pub const print_ast = false; -pub const disable_printing_null = false; +/// Enable breaking changes for the next major release of Bun +// TODO: Make this a CLI flag / runtime var so that we can verify disabled code paths can compile +pub const breaking_changes_1_2 = false; /// Store and reuse file descriptors during module resolution /// This was a ~5% performance improvement pub const store_file_descriptors = !env.isBrowser; -pub const css_in_js_import_behavior = CSSInJSImportBehavior.facade; - -pub const only_output_esm = true; - pub const jsx_runtime_is_cjs = true; -pub const bundle_node_modules = true; - pub const tracing = true; +/// Disabled due to bugs pub const minify_javascript_string_length = false; +// TODO: remove this flag, it should use bun.Output.scoped pub const verbose_watcher = false; pub const css_supports_fence = true; pub const enable_entry_cache = true; -pub const enable_bytecode_caching = false; - -pub const dev_only = true; +// TODO: remove this flag, it should use bun.Output.scoped pub const verbose_fs = false; pub const watch_directories = true; -pub const tailwind_css_at_keyword = true; - -pub const bundle_dynamic_import = true; - // This feature flag exists so when you have defines inside package.json, you can use single quotes in nested strings. pub const allow_json_single_quotes = true; pub const react_specific_warnings = true; -pub const log_allocations = false; - -pub const CSSInJSImportBehavior = enum { - // When you import a .css file and you reference the import in JavaScript - // Just return whatever the property key they referenced was - facade, - facade_onimportcss, -}; - -// having issues compiling WebKit with this enabled -pub const remote_inspector = false; -pub const auto_import_buffer = false; - pub const is_macro_enabled = !env.isWasm and !env.isWasi; // pretend everything is always the macro environment @@ -67,17 +41,12 @@ pub const force_macro = false; pub const include_filename_in_jsx = false; -pub const verbose_analytics = false; - pub const disable_compression_in_http_client = false; pub const enable_keepalive = true; pub const atomic_file_watcher = env.isLinux; -pub const node_streams = false; -pub const simd = true; - // This change didn't seem to make a meaningful difference in microbenchmarks pub const latin1_is_now_ascii = false; @@ -102,8 +71,6 @@ pub const inline_properties_in_transpiler = true; pub const same_target_becomes_destructuring = true; -pub const react_server_components = true; - pub const help_catch_memory_issues = bun.Environment.allow_assert; /// This performs similar transforms as https://github.com/rollup/plugins/tree/master/packages/commonjs @@ -144,8 +111,6 @@ pub const help_catch_memory_issues = bun.Environment.allow_assert; /// In that case, we wrap it again in the printer. pub const unwrap_commonjs_to_esm = true; -pub const boundary_based_chunking = true; - /// https://sentry.engineering/blog/the-case-for-debug-ids /// https://github.com/mitsuhiko/source-map-rfc/blob/proposals/debug-id/proposals/debug-id.md /// https://github.com/source-map/source-map-rfc/pull/20 @@ -157,8 +122,7 @@ pub const export_star_redirect = false; pub const streaming_file_uploads_for_http_client = true; -// TODO: fix concurrent transpiler on Windows -pub const concurrent_transpiler = !env.isWindows; +pub const concurrent_transpiler = true; // https://github.com/oven-sh/bun/issues/5426#issuecomment-1813865316 pub const disable_auto_js_to_ts_in_node_modules = true; @@ -173,11 +137,26 @@ pub const runtime_transpiler_cache = true; /// order to isolate your bug. pub const windows_bunx_fast_path = true; -/// Enable breaking changes for the next major release of Bun -// TODO: Make this a CLI flag / runtime var so that we can verify disabled code paths can compile -pub const breaking_changes_1_2 = false; - // This causes strange bugs where writing via console.log (sync) has a different // order than via Bun.file.writer() so we turn it off until there's a unified, // buffered writer abstraction shared throughout Bun pub const nonblocking_stdout_and_stderr_on_posix = false; + +pub const postgresql = env.is_canary or env.isDebug; + +// TODO: fix Windows-only test failures in fetch-preconnect.test.ts +pub const is_fetch_preconnect_supported = env.isPosix; + +pub const libdeflate_supported = env.isNative; + +// Mostly exists as a way to turn it off later, if necessary. +pub fn isLibdeflateEnabled() bool { + if (!libdeflate_supported) { + return false; + } + + return !bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_NO_LIBDEFLATE"); +} + +/// Enable experimental bundler tools, codenamed "bun kit" +pub const kit = env.is_canary or env.isDebug; diff --git a/src/fmt.zig b/src/fmt.zig index f4d9ccde8a0fa..f2b9ec6f6301f 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -10,6 +10,102 @@ const Environment = bun.Environment; pub usingnamespace std.fmt; +pub fn Table( + comptime column_color: []const u8, + comptime column_left_pad: usize, + comptime column_right_pad: usize, + comptime enable_ansi_colors: bool, +) type { + return struct { + column_names: []const []const u8, + column_inside_lengths: []const usize, + + pub fn topLeftSep(_: *const @This()) string { + return if (enable_ansi_colors) "┌" else "|"; + } + pub fn topRightSep(_: *const @This()) string { + return if (enable_ansi_colors) "┐" else "|"; + } + pub fn topColumnSep(_: *const @This()) string { + return if (enable_ansi_colors) "┬" else "-"; + } + + pub fn bottomLeftSep(_: *const @This()) string { + return if (enable_ansi_colors) "└" else "|"; + } + pub fn bottomRightSep(_: *const @This()) string { + return if (enable_ansi_colors) "┘" else "|"; + } + pub fn bottomColumnSep(_: *const @This()) string { + return if (enable_ansi_colors) "┴" else "-"; + } + + pub fn middleLeftSep(_: *const @This()) string { + return if (enable_ansi_colors) "├" else "|"; + } + pub fn middleRightSep(_: *const @This()) string { + return if (enable_ansi_colors) "┤" else "|"; + } + pub fn middleColumnSep(_: *const @This()) string { + return if (enable_ansi_colors) "┼" else "|"; + } + + pub fn horizontalEdge(_: *const @This()) string { + return if (enable_ansi_colors) "─" else "-"; + } + pub fn verticalEdge(_: *const @This()) string { + return if (enable_ansi_colors) "│" else "|"; + } + + pub fn init(column_names_: []const []const u8, column_inside_lengths_: []const usize) @This() { + return .{ + .column_names = column_names_, + .column_inside_lengths = column_inside_lengths_, + }; + } + + pub fn printTopLineSeparator(this: *const @This()) void { + this.printLine(this.topLeftSep(), this.topRightSep(), this.topColumnSep()); + } + + pub fn printBottomLineSeparator(this: *const @This()) void { + this.printLine(this.bottomLeftSep(), this.bottomRightSep(), this.bottomColumnSep()); + } + + pub fn printLineSeparator(this: *const @This()) void { + this.printLine(this.middleLeftSep(), this.middleRightSep(), this.middleColumnSep()); + } + + pub fn printLine(this: *const @This(), left_edge_separator: string, right_edge_separator: string, column_separator: string) void { + for (this.column_inside_lengths, 0..) |column_inside_length, i| { + if (i == 0) { + Output.pretty("{s}", .{left_edge_separator}); + } else { + Output.pretty("{s}", .{column_separator}); + } + + for (0..column_left_pad + column_inside_length + column_right_pad) |_| Output.pretty("{s}", .{this.horizontalEdge()}); + + if (i == this.column_inside_lengths.len - 1) { + Output.pretty("{s}\n", .{right_edge_separator}); + } + } + } + + pub fn printColumnNames(this: *const @This()) void { + for (this.column_inside_lengths, 0..) |column_inside_length, i| { + Output.pretty("{s}", .{this.verticalEdge()}); + for (0..column_left_pad) |_| Output.pretty(" ", .{}); + Output.pretty("<" ++ column_color ++ ">{s}", .{this.column_names[i]}); + for (this.column_names[i].len..column_inside_length + column_right_pad) |_| Output.pretty(" ", .{}); + if (i == this.column_inside_lengths.len - 1) { + Output.pretty("{s}\n", .{this.verticalEdge()}); + } + } + } + }; +} + const SharedTempBuffer = [32 * 1024]u8; fn getSharedBuffer() []u8 { return std.mem.asBytes(shared_temp_buffer_ptr orelse brk: { @@ -97,6 +193,25 @@ pub inline fn utf16(slice_: []const u16) FormatUTF16 { return FormatUTF16{ .buf = slice_ }; } +/// Debug, this does not handle invalid utf32 +pub inline fn debugUtf32PathFormatter(path: []const u32) DebugUTF32PathFormatter { + return DebugUTF32PathFormatter{ .path = path }; +} + +pub const DebugUTF32PathFormatter = struct { + path: []const u32, + pub fn format(this: @This(), comptime _: []const u8, _: anytype, writer: anytype) !void { + var path_buf: bun.PathBuffer = undefined; + const result = bun.simdutf.convert.utf32.to.utf8.with_errors.le(this.path, &path_buf); + const converted = if (result.isSuccessful()) + path_buf[0..result.count] + else + "Invalid UTF32!"; + + try writer.writeAll(converted); + } +}; + pub const FormatUTF16 = struct { buf: []const u16, path_fmt_opts: ?PathFormatOptions = null, @@ -534,140 +649,78 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { pub fn colorCode(this: Keyword) ColorCode { return switch (this) { - Keyword.abstract => ColorCode.blue, - Keyword.as => ColorCode.blue, - Keyword.@"async" => ColorCode.magenta, - Keyword.@"await" => ColorCode.magenta, - Keyword.case => ColorCode.magenta, - Keyword.@"catch" => ColorCode.magenta, - Keyword.class => ColorCode.magenta, - Keyword.@"const" => ColorCode.magenta, - Keyword.@"continue" => ColorCode.magenta, - Keyword.debugger => ColorCode.magenta, - Keyword.default => ColorCode.magenta, - Keyword.delete => ColorCode.red, - Keyword.do => ColorCode.magenta, - Keyword.@"else" => ColorCode.magenta, - Keyword.@"break" => ColorCode.magenta, - Keyword.undefined => ColorCode.orange, - Keyword.@"enum" => ColorCode.blue, - Keyword.@"export" => ColorCode.magenta, - Keyword.extends => ColorCode.magenta, - Keyword.false => ColorCode.orange, - Keyword.finally => ColorCode.magenta, - Keyword.@"for" => ColorCode.magenta, - Keyword.function => ColorCode.magenta, - Keyword.@"if" => ColorCode.magenta, - Keyword.implements => ColorCode.blue, - Keyword.import => ColorCode.magenta, - Keyword.in => ColorCode.magenta, - Keyword.instanceof => ColorCode.magenta, - Keyword.interface => ColorCode.blue, - Keyword.let => ColorCode.magenta, - Keyword.new => ColorCode.magenta, - Keyword.null => ColorCode.orange, - Keyword.package => ColorCode.magenta, - Keyword.private => ColorCode.blue, - Keyword.protected => ColorCode.blue, - Keyword.public => ColorCode.blue, - Keyword.@"return" => ColorCode.magenta, - Keyword.static => ColorCode.magenta, - Keyword.super => ColorCode.magenta, - Keyword.@"switch" => ColorCode.magenta, - Keyword.this => ColorCode.orange, - Keyword.throw => ColorCode.magenta, - Keyword.true => ColorCode.orange, - Keyword.@"try" => ColorCode.magenta, - Keyword.type => ColorCode.blue, - Keyword.typeof => ColorCode.magenta, - Keyword.@"var" => ColorCode.magenta, - Keyword.void => ColorCode.magenta, - Keyword.@"while" => ColorCode.magenta, - Keyword.with => ColorCode.magenta, - Keyword.yield => ColorCode.magenta, - Keyword.string => ColorCode.blue, - Keyword.number => ColorCode.blue, - Keyword.boolean => ColorCode.blue, - Keyword.symbol => ColorCode.blue, - Keyword.any => ColorCode.blue, - Keyword.object => ColorCode.blue, - Keyword.unknown => ColorCode.blue, - Keyword.never => ColorCode.blue, - Keyword.namespace => ColorCode.blue, - Keyword.declare => ColorCode.blue, - Keyword.readonly => ColorCode.blue, + .abstract => .blue, + .as => .blue, + .@"async" => .magenta, + .@"await" => .magenta, + .case => .magenta, + .@"catch" => .magenta, + .class => .magenta, + .@"const" => .magenta, + .@"continue" => .magenta, + .debugger => .magenta, + .default => .magenta, + .delete => .red, + .do => .magenta, + .@"else" => .magenta, + .@"break" => .magenta, + .undefined => .orange, + .@"enum" => .blue, + .@"export" => .magenta, + .extends => .magenta, + .false => .orange, + .finally => .magenta, + .@"for" => .magenta, + .function => .magenta, + .@"if" => .magenta, + .implements => .blue, + .import => .magenta, + .in => .magenta, + .instanceof => .magenta, + .interface => .blue, + .let => .magenta, + .new => .magenta, + .null => .orange, + .package => .magenta, + .private => .blue, + .protected => .blue, + .public => .blue, + .@"return" => .magenta, + .static => .magenta, + .super => .magenta, + .@"switch" => .magenta, + .this => .orange, + .throw => .magenta, + .true => .orange, + .@"try" => .magenta, + .type => .blue, + .typeof => .magenta, + .@"var" => .magenta, + .void => .magenta, + .@"while" => .magenta, + .with => .magenta, + .yield => .magenta, + .string => .blue, + .number => .blue, + .boolean => .blue, + .symbol => .blue, + .any => .blue, + .object => .blue, + .unknown => .blue, + .never => .blue, + .namespace => .blue, + .declare => .blue, + .readonly => .blue, }; } }; - pub const Keywords = ComptimeStringMap(Keyword, .{ - .{ "abstract", Keyword.abstract }, - .{ "any", Keyword.any }, - .{ "as", Keyword.as }, - .{ "async", Keyword.@"async" }, - .{ "await", Keyword.@"await" }, - .{ "boolean", Keyword.boolean }, - .{ "break", Keyword.@"break" }, - .{ "case", Keyword.case }, - .{ "catch", Keyword.@"catch" }, - .{ "class", Keyword.class }, - .{ "const", Keyword.@"const" }, - .{ "continue", Keyword.@"continue" }, - .{ "debugger", Keyword.debugger }, - .{ "declare", Keyword.declare }, - .{ "default", Keyword.default }, - .{ "delete", Keyword.delete }, - .{ "do", Keyword.do }, - .{ "else", Keyword.@"else" }, - .{ "enum", Keyword.@"enum" }, - .{ "export", Keyword.@"export" }, - .{ "extends", Keyword.extends }, - .{ "false", Keyword.false }, - .{ "finally", Keyword.finally }, - .{ "for", Keyword.@"for" }, - .{ "function", Keyword.function }, - .{ "if", Keyword.@"if" }, - .{ "implements", Keyword.implements }, - .{ "import", Keyword.import }, - .{ "in", Keyword.in }, - .{ "instanceof", Keyword.instanceof }, - .{ "interface", Keyword.interface }, - .{ "let", Keyword.let }, - .{ "namespace", Keyword.namespace }, - .{ "never", Keyword.never }, - .{ "new", Keyword.new }, - .{ "null", Keyword.null }, - .{ "number", Keyword.number }, - .{ "object", Keyword.object }, - .{ "package", Keyword.package }, - .{ "private", Keyword.private }, - .{ "protected", Keyword.protected }, - .{ "public", Keyword.public }, - .{ "readonly", Keyword.readonly }, - .{ "return", Keyword.@"return" }, - .{ "static", Keyword.static }, - .{ "string", Keyword.string }, - .{ "super", Keyword.super }, - .{ "switch", Keyword.@"switch" }, - .{ "symbol", Keyword.symbol }, - .{ "this", Keyword.this }, - .{ "throw", Keyword.throw }, - .{ "true", Keyword.true }, - .{ "try", Keyword.@"try" }, - .{ "type", Keyword.type }, - .{ "typeof", Keyword.typeof }, - .{ "undefined", Keyword.undefined }, - .{ "unknown", Keyword.unknown }, - .{ "var", Keyword.@"var" }, - .{ "void", Keyword.void }, - .{ "while", Keyword.@"while" }, - .{ "with", Keyword.with }, - .{ "yield", Keyword.yield }, - }); + pub const Keywords = bun.ComptimeEnumMap(Keyword); - pub fn format(this: @This(), comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void { - const text = this.text; + pub fn format(this: @This(), comptime unused_fmt: []const u8, _: fmt.FormatOptions, writer: anytype) !void { + comptime bun.assert(unused_fmt.len == 0); + var text = this.text; if (this.limited) { if (!this.enable_colors or text.len > 2048 or text.len == 0 or !strings.isAllASCII(text)) { try writer.writeAll(text); @@ -675,22 +728,21 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } } - var remain = text; var prev_keyword: ?Keyword = null; - outer: while (remain.len > 0) { - if (js_lexer.isIdentifierStart(remain[0])) { + outer: while (text.len > 0) { + if (js_lexer.isIdentifierStart(text[0])) { var i: usize = 1; - while (i < remain.len and js_lexer.isIdentifierContinue(remain[i])) { + while (i < text.len and js_lexer.isIdentifierContinue(text[i])) { i += 1; } - if (Keywords.get(remain[0..i])) |keyword| { + if (Keywords.get(text[0..i])) |keyword| { if (keyword != .as) prev_keyword = keyword; const code = keyword.colorCode(); - try writer.print(Output.prettyFmt("{s}{s}", true), .{ code.color(), remain[0..i] }); + try writer.print(Output.prettyFmt("{s}{s}", true), .{ code.color(), text[0..i] }); } else { write: { if (prev_keyword) |prev| { @@ -698,20 +750,20 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { .new => { prev_keyword = null; - if (i < remain.len and remain[i] == '(') { - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); + if (i < text.len and text[i] == '(') { + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); break :write; } }, .abstract, .namespace, .declare, .type, .interface => { - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); prev_keyword = null; break :write; }, .import => { - if (strings.eqlComptime(remain[0..i], "from")) { + if (strings.eqlComptime(text[0..i], "from")) { const code = ColorCode.magenta; - try writer.print(Output.prettyFmt("{s}{s}", true), .{ code.color(), remain[0..i] }); + try writer.print(Output.prettyFmt("{s}{s}", true), .{ code.color(), text[0..i] }); prev_keyword = null; break :write; @@ -721,25 +773,25 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } } - try writer.writeAll(remain[0..i]); + try writer.writeAll(text[0..i]); } } - remain = remain[i..]; + text = text[i..]; } else { - switch (remain[0]) { + switch (text[0]) { '0'...'9' => { prev_keyword = null; var i: usize = 1; - if (remain.len > 1 and remain[0] == '0' and remain[1] == 'x') { + if (text.len > 1 and text[0] == '0' and text[1] == 'x') { i += 1; - while (i < remain.len and switch (remain[i]) { + while (i < text.len and switch (text[i]) { '0'...'9', 'a'...'f', 'A'...'F' => true, else => false, }) { i += 1; } } else { - while (i < remain.len and switch (remain[i]) { + while (i < text.len and switch (text[i]) { '0'...'9', '.', 'e', 'E', 'x', 'X', 'b', 'B', 'o', 'O' => true, else => false, }) { @@ -747,30 +799,30 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } } - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); - remain = remain[i..]; + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; }, inline '`', '"', '\'' => |char| { prev_keyword = null; var i: usize = 1; - while (i < remain.len and remain[i] != char) { - if (comptime char == '`') { - if (remain[i] == '$' and i + 1 < remain.len and remain[i + 1] == '{') { + while (i < text.len and text[i] != char) { + if (char == '`') { + if (text[i] == '$' and i + 1 < text.len and text[i + 1] == '{') { const curly_start = i; i += 2; - while (i < remain.len and remain[i] != '}') { - if (remain[i] == '\\') { + while (i < text.len and text[i] != '}') { + if (text[i] == '\\') { i += 1; } i += 1; } - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..curly_start]}); + try writer.print(Output.prettyFmt("{s}", true), .{text[0..curly_start]}); try writer.writeAll("${"); const curly_remain = QuickAndDirtyJavaScriptSyntaxHighlighter{ - .text = remain[curly_start + 2 .. i], + .text = text[curly_start + 2 .. i], .enable_colors = this.enable_colors, .limited = false, }; @@ -779,22 +831,22 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { try curly_remain.format("", .{}, writer); } - if (i < remain.len and remain[i] == '}') { + if (i < text.len and text[i] == '}') { i += 1; } try writer.writeAll("}"); - remain = remain[i..]; + text = text[i..]; i = 0; - if (remain.len > 0 and remain[0] == char) { + if (text.len > 0 and text[0] == char) { try writer.writeAll(Output.prettyFmt("`", true)); - remain = remain[1..]; + text = text[1..]; continue :outer; } continue; } } - if (i + 1 < remain.len and remain[i] == '\\') { + if (i + 1 < text.len and text[i] == '\\') { i += 1; } @@ -802,58 +854,58 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } // Include the trailing quote, if any - i += @as(usize, @intFromBool(i > 1 and i < remain.len and remain[i] == char)); + i += @intFromBool(i < text.len); - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); - remain = remain[i..]; + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; }, '/' => { prev_keyword = null; var i: usize = 1; // the start of a line comment - if (i < remain.len and remain[i] == '/') { - while (i < remain.len and remain[i] != '\n') { + if (i < text.len and text[i] == '/') { + while (i < text.len and text[i] != '\n') { i += 1; } - const remain_to_print = remain[0..i]; - if (i < remain.len and remain[i] == '\n') { + const remain_to_print = text[0..i]; + if (i < text.len and text[i] == '\n') { i += 1; } - if (i < remain.len and remain[i] == '\r') { + if (i < text.len and text[i] == '\r') { i += 1; } try writer.print(Output.prettyFmt("{s}", true), .{remain_to_print}); - remain = remain[i..]; + text = text[i..]; continue; } as_multiline_comment: { - if (i < remain.len and remain[i] == '*') { + if (i < text.len and text[i] == '*') { i += 1; - while (i + 2 < remain.len and !strings.eqlComptime(remain[i..][0..2], "*/")) { + while (i + 2 < text.len and !strings.eqlComptime(text[i..][0..2], "*/")) { i += 1; } - if (i + 2 < remain.len and strings.eqlComptime(remain[i..][0..2], "*/")) { + if (i + 2 < text.len and strings.eqlComptime(text[i..][0..2], "*/")) { i += 2; } else { i = 1; break :as_multiline_comment; } - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); - remain = remain[i..]; + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; continue; } } - try writer.writeAll(remain[0..i]); - remain = remain[i..]; + try writer.writeAll(text[0..i]); + text = text[i..]; }, '}', '{' => { // support potentially highlighting "from" in an import statement @@ -861,39 +913,39 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { prev_keyword = null; } - try writer.writeAll(remain[0..1]); - remain = remain[1..]; + try writer.writeAll(text[0..1]); + text = text[1..]; }, '[', ']' => { prev_keyword = null; - try writer.writeAll(remain[0..1]); - remain = remain[1..]; + try writer.writeAll(text[0..1]); + text = text[1..]; }, ';' => { prev_keyword = null; try writer.print(Output.prettyFmt(";", true), .{}); - remain = remain[1..]; + text = text[1..]; }, '.' => { prev_keyword = null; var i: usize = 1; - if (remain.len > 1 and (js_lexer.isIdentifierStart(remain[1]) or remain[1] == '#')) { + if (text.len > 1 and (js_lexer.isIdentifierStart(text[1]) or text[1] == '#')) { i = 2; - while (i < remain.len and js_lexer.isIdentifierContinue(remain[i])) { + while (i < text.len and js_lexer.isIdentifierContinue(text[i])) { i += 1; } - if (i < remain.len and (remain[i] == '(')) { - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); - remain = remain[i..]; + if (i < text.len and (text[i] == '(')) { + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; continue; } i = 1; } - try writer.writeAll(remain[0..1]); - remain = remain[1..]; + try writer.writeAll(text[0..1]); + text = text[1..]; }, '<' => { @@ -901,78 +953,49 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { // JSX jsx: { - if (remain.len > 1 and remain[0] == '/') { + if (text.len > 1 and text[0] == '/') { i = 2; } prev_keyword = null; - while (i < remain.len and js_lexer.isIdentifierContinue(remain[i])) { + while (i < text.len and js_lexer.isIdentifierContinue(text[i])) { i += 1; } else { i = 1; break :jsx; } - while (i < remain.len and remain[i] != '>') { + while (i < text.len and text[i] != '>') { i += 1; - if (i < remain.len and remain[i] == '<') { + if (i < text.len and text[i] == '<') { i = 1; break :jsx; } } - if (i < remain.len and remain[i] == '>') { + if (i < text.len and text[i] == '>') { i += 1; - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); - remain = remain[i..]; + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; continue; } i = 1; } - try writer.print(Output.prettyFmt("{s}", true), .{remain[0..i]}); - remain = remain[i..]; + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; }, else => { - try writer.writeAll(remain[0..1]); - remain = remain[1..]; + try writer.writeAll(text[0..1]); + text = text[1..]; }, } } } } - - /// Function for testing in highlighter.test.ts - pub fn jsFunctionSyntaxHighlight(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(.C) bun.JSC.JSValue { - const args = callframe.arguments(1); - if (args.len < 1) { - globalThis.throwNotEnoughArguments("code", 1, 0); - } - - const code = args.ptr[0].toSliceOrNull(globalThis) orelse return .zero; - defer code.deinit(); - var buffer = bun.MutableString.initEmpty(bun.default_allocator); - defer buffer.deinit(); - var writer = buffer.bufferedWriter(); - var formatter = bun.fmt.fmtJavaScript(code.slice(), true); - formatter.limited = false; - std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { - globalThis.throwError(err, "Error formatting code"); - return .zero; - }; - - writer.flush() catch |err| { - globalThis.throwError(err, "Error formatting code"); - return .zero; - }; - - var str = bun.String.createUTF8(buffer.list.items); - defer str.deref(); - return str.toJS(globalThis); - } }; pub fn quote(self: string) bun.fmt.QuotedFormatter { @@ -1078,17 +1101,30 @@ pub fn fastDigitCount(x: u64) u64 { pub const SizeFormatter = struct { value: usize = 0, + opts: Options, + + pub const Options = struct { + space_between_number_and_unit: bool = true, + }; pub fn format(self: SizeFormatter, comptime _: []const u8, opts: fmt.FormatOptions, writer: anytype) !void { const math = std.math; const value = self.value; if (value == 0) { - return writer.writeAll("0 KB"); + if (self.opts.space_between_number_and_unit) { + return writer.writeAll("0 KB"); + } else { + return writer.writeAll("0KB"); + } } if (value < 512) { try fmt.formatInt(self.value, 10, .lower, opts, writer); - return writer.writeAll(" bytes"); + if (self.opts.space_between_number_and_unit) { + return writer.writeAll(" bytes"); + } else { + return writer.writeByte('B'); + } } const mags_si = " KMGTPEZY"; @@ -1098,21 +1134,31 @@ pub const SizeFormatter = struct { const suffix = mags_si[magnitude]; if (suffix == ' ') { - try writer.print("{d:.2} KB", .{new_value / 1000.0}); + if (self.opts.space_between_number_and_unit) { + try writer.print("{d:.2} KB", .{new_value / 1000.0}); + } else { + try writer.print("{d:.2}KB", .{new_value / 1000.0}); + } return; } const precision: usize = if (std.math.approxEqAbs(f64, new_value, @trunc(new_value), 0.100)) 1 else 2; try fmt.formatType(new_value, "d", .{ .precision = precision }, writer, 0); - try writer.writeAll(&.{ ' ', suffix, 'B' }); + if (self.opts.space_between_number_and_unit) { + try writer.writeAll(&.{ ' ', suffix, 'B' }); + } else { + try writer.writeAll(&.{ suffix, 'B' }); + } } }; -pub fn size(value: anytype) SizeFormatter { - return switch (@TypeOf(value)) { - f64, f32, f128 => SizeFormatter{ - .value = @as(u64, @intFromFloat(value)), +pub fn size(value: anytype, opts: SizeFormatter.Options) SizeFormatter { + return .{ + .value = switch (@TypeOf(value)) { + f64, f32, f128 => @intFromFloat(value), + i64, isize => @intCast(value), + else => value, }, - else => SizeFormatter{ .value = value }, + .opts = opts, }; } @@ -1275,18 +1321,18 @@ fn FormatSlice(comptime T: type, comptime delim: []const u8) type { } /// Uses WebKit's double formatter -pub fn fmtDouble(number: f64) FormatDouble { +pub fn double(number: f64) FormatDouble { return .{ .number = number }; } pub const FormatDouble = struct { number: f64, - extern fn WTF__dtoa(buf_124_bytes: *[124]u8, number: f64) void; + extern fn WTF__dtoa(buf_124_bytes: *[124]u8, number: f64) usize; pub fn dtoa(buf: *[124]u8, number: f64) []const u8 { - WTF__dtoa(buf, number); - return bun.sliceTo(buf, 0); + const len = WTF__dtoa(buf, number); + return buf[0..len]; } pub fn dtoaWithNegativeZero(buf: *[124]u8, number: f64) []const u8 { @@ -1294,8 +1340,8 @@ pub const FormatDouble = struct { return "-0"; } - WTF__dtoa(buf, number); - return bun.sliceTo(buf, 0); + const len = WTF__dtoa(buf, number); + return buf[0..len]; } pub fn format(self: @This(), comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void { @@ -1323,3 +1369,130 @@ pub fn NullableFallback(comptime T: type) type { } }; } + +pub fn escapePowershell(str: []const u8) std.fmt.Formatter(escapePowershellImpl) { + return .{ .data = str }; +} + +fn escapePowershellImpl(str: []const u8, comptime f: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + comptime bun.assert(f.len == 0); + var remain = str; + while (bun.strings.indexOfAny(remain, "\"`")) |i| { + try writer.writeAll(remain[0..i]); + try writer.writeAll("`"); + try writer.writeByte(remain[i]); + remain = remain[i + 1 ..]; + } + try writer.writeAll(remain); +} + +pub const fmt_js_test_bindings = struct { + const Formatter = enum { + fmtJavaScript, + escapePowershell, + }; + + /// Internal function for testing in highlighter.test.ts + pub fn jsFunctionStringFormatter(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(bun.JSC.conv) bun.JSC.JSValue { + const args = callframe.arguments(2); + if (args.len < 2) { + globalThis.throwNotEnoughArguments("code", 1, 0); + } + + const code = args.ptr[0].toSliceOrNull(globalThis) orelse + return .zero; + defer code.deinit(); + + var buffer = bun.MutableString.initEmpty(bun.default_allocator); + defer buffer.deinit(); + var writer = buffer.bufferedWriter(); + + const formatter_id: Formatter = @enumFromInt(args.ptr[1].toInt32()); + switch (formatter_id) { + .fmtJavaScript => { + var formatter = bun.fmt.fmtJavaScript(code.slice(), true); + formatter.limited = false; + std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { + globalThis.throwError(err, "Error formatting"); + return .zero; + }; + }, + .escapePowershell => { + std.fmt.format(writer.writer(), "{}", .{escapePowershell(code.slice())}) catch |err| { + globalThis.throwError(err, "Error formatting"); + return .zero; + }; + }, + } + + writer.flush() catch |err| { + globalThis.throwError(err, "Error formatting"); + return .zero; + }; + + var str = bun.String.createUTF8(buffer.list.items); + defer str.deref(); + return str.toJS(globalThis); + } +}; + +fn NewOutOfRangeFormatter(comptime T: type) type { + return struct { + value: T, + min: i64 = std.math.maxInt(i64), + max: i64 = std.math.maxInt(i64), + field_name: []const u8, + + pub fn format(self: @This(), comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void { + try writer.writeAll("The value of \""); + try writer.writeAll(self.field_name); + try writer.writeAll("\" "); + const min = self.min; + const max = self.max; + + if (min != std.math.maxInt(i64) and max != std.math.maxInt(i64)) { + try std.fmt.format(writer, "must be >= {d} and <= {d}.", .{ min, max }); + } else if (min != std.math.maxInt(i64)) { + try std.fmt.format(writer, "must be >= {d}.", .{min}); + } else if (max != std.math.maxInt(i64)) { + try std.fmt.format(writer, "must be <= {d}.", .{max}); + } else { + try writer.writeAll("must be within the range of values for type "); + try writer.writeAll(comptime @typeName(T)); + try writer.writeAll("."); + } + + if (comptime T == f64 or T == f32) { + try std.fmt.format(writer, " Received: {}", .{double(self.value)}); + } else if (comptime T == []const u8) { + try std.fmt.format(writer, " Received: {s}", .{self.value}); + } else { + try std.fmt.format(writer, " Received: {d}", .{self.value}); + } + } + }; +} + +const DoubleOutOfRangeFormatter = NewOutOfRangeFormatter(f64); +const IntOutOfRangeFormatter = NewOutOfRangeFormatter(i64); +const StringOutOfRangeFormatter = NewOutOfRangeFormatter([]const u8); + +fn OutOfRangeFormatter(comptime T: type) type { + if (T == f64 or T == f32) { + return DoubleOutOfRangeFormatter; + } else if (T == []const u8) { + return StringOutOfRangeFormatter; + } + + return IntOutOfRangeFormatter; +} + +pub const OutOfRangeOptions = struct { + min: i64 = std.math.maxInt(i64), + max: i64 = std.math.maxInt(i64), + field_name: []const u8, +}; + +pub fn outOfRange(value: anytype, options: OutOfRangeOptions) OutOfRangeFormatter(@TypeOf(value)) { + return .{ .value = value, .min = options.min, .max = options.max, .field_name = options.field_name }; +} diff --git a/src/fs.zig b/src/fs.zig index 0649fb4c66d5e..20c9317d6f526 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -19,6 +19,7 @@ const Fs = @This(); const path_handler = @import("./resolver/resolve_path.zig"); const PathString = bun.PathString; const allocators = bun.allocators; +const OOM = bun.OOM; const MAX_PATH_BYTES = bun.MAX_PATH_BYTES; const PathBuffer = bun.PathBuffer; @@ -36,7 +37,7 @@ pub const Preallocate = struct { }; pub const FileSystem = struct { - top_level_dir: string = if (Environment.isWindows) "C:\\" else "/", + top_level_dir: string, // used on subsequent updates top_level_dir_buf: bun.PathBuffer = undefined, @@ -46,8 +47,6 @@ pub const FileSystem = struct { dirname_store: *DirnameStore, filename_store: *FilenameStore, - _tmpdir: ?std.fs.Dir = null, - threadlocal var tmpdir_handle: ?std.fs.Dir = null; pub fn topLevelDirWithoutTrailingSlash(this: *const FileSystem) []const u8 { @@ -136,7 +135,6 @@ pub const FileSystem = struct { }; instance_loaded = true; - instance.fs.parent_fs = &instance; _ = DirEntry.EntryStore.init(allocator); } @@ -204,7 +202,7 @@ pub const FileSystem = struct { .base_ = name, .base_lowercase_ = name_lowercased, .dir = dir.dir, - .mutex = Mutex.init(), + .mutex = .{}, // Call "stat" lazily for performance. The "@material-ui/icons" package // contains a directory with over 11,000 entries in it and running "stat" // for each entry was a big performance issue for that package. @@ -533,10 +531,9 @@ pub const FileSystem = struct { } pub const RealFS = struct { - entries_mutex: Mutex = Mutex.init(), + entries_mutex: Mutex = .{}, entries: *EntriesOption.Map, cwd: string, - parent_fs: *FileSystem = undefined, file_limit: usize = 32, file_quota: usize = 32, @@ -617,7 +614,7 @@ pub const FileSystem = struct { var existing = this.entries.atIndex(index) orelse return null; if (existing.* == .entries) { if (existing.entries.generation < generation) { - var handle = bun.openDirA(std.fs.cwd(), existing.entries.dir) catch |err| { + var handle = bun.openDirForIteration(std.fs.cwd(), existing.entries.dir) catch |err| { existing.entries.data.clearAndFree(bun.fs_allocator); return this.readDirectoryError(existing.entries.dir, err) catch unreachable; @@ -991,7 +988,7 @@ pub const FileSystem = struct { return dir; } - fn readDirectoryError(fs: *RealFS, dir: string, err: anyerror) !*EntriesOption { + fn readDirectoryError(fs: *RealFS, dir: string, err: anyerror) OOM!*EntriesOption { if (comptime FeatureFlags.enable_entry_cache) { var get_or_put_result = try fs.entries.getOrPut(dir); const opt = try fs.entries.put(&get_or_put_result, EntriesOption{ @@ -1092,8 +1089,7 @@ pub const FileSystem = struct { iterator, ) catch |err| { if (in_place) |existing| existing.data.clearAndFree(bun.fs_allocator); - - return fs.readDirectoryError(dir, err) catch bun.outOfMemory(); + return try fs.readDirectoryError(dir, err); }; if (comptime FeatureFlags.enable_entry_cache) { @@ -1616,7 +1612,7 @@ pub const PathName = struct { dir = _path[0 .. dir.len + 2]; } - return PathName{ + return .{ .dir = dir, .base = base, .ext = ext, @@ -1652,6 +1648,14 @@ pub const Path = struct { return hasher.final(); } + /// This hash is used by the hot-module-reloading client in order to + /// identify modules. Since that code is JavaScript, the hash must remain in + /// range [-MAX_SAFE_INTEGER, MAX_SAFE_INTEGER] or else information is lost + /// due to floating-point precision. + pub fn hashForKit(path: Path) u52 { + return @truncate(path.hashKey()); + } + pub fn packageName(this: *const Path) ?string { var name_to_use = this.pretty; if (strings.lastIndexOf(this.text, std.fs.path.sep_str ++ "node_modules" ++ std.fs.path.sep_str)) |node_modules| { diff --git a/src/futex.zig b/src/futex.zig index 7b20ea915044c..49f4e83e77c8a 100644 --- a/src/futex.zig +++ b/src/futex.zig @@ -13,7 +13,7 @@ const Futex = @This(); const target = builtin.target; const single_threaded = builtin.single_threaded; -const assert = @import("root").bun.assert; +const assert = bun.assert; const testing = std.testing; const Atomic = std.atomic.Value; diff --git a/src/generated_versions_list.zig b/src/generated_versions_list.zig index c6354e9c4c756..96170d7b5f424 100644 --- a/src/generated_versions_list.zig +++ b/src/generated_versions_list.zig @@ -1,14 +1,15 @@ // AUTO-GENERATED FILE. Created via .scripts/write-versions.sh pub const boringssl = "29a2cd359458c9384694b75456026e4b57e3e567"; -pub const libarchive = "313aa1fa10b657de791e3202c168a6c833bc3543"; +pub const libarchive = "898dc8319355b7e985f68a9819f182aaed61b53a"; pub const mimalloc = "4c283af60cdae205df5a872530c77e2a6a307d43"; pub const picohttpparser = "066d2b1e9ab820703db0837a7255d92d30f0c9f5"; -pub const webkit = "64d04ec1a65d91326c5f2298b9c7d05b56125252"; +pub const webkit = "13bb88da0b791154dca60910f301dcd70c321f72"; pub const zig = @import("std").fmt.comptimePrint("{}", .{@import("builtin").zig_version}); pub const zlib = "886098f3f339617b4243b286f5ed364b9989e245"; pub const tinycc = "ab631362d839333660a265d3084d8ff060b96753"; pub const lolhtml = "8d4c273ded322193d017042d1f48df2766b0f88b"; pub const c_ares = "d1722e6e8acaf10eb73fa995798a9cd421d9f85e"; +pub const libdeflate = "dc76454a39e7e83b68c3704b6e3784654f8d5ac5"; pub const zstd = "794ea1b0afca0f020f4e57b6732332231fb23c70"; pub const lshpack = "3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0"; diff --git a/src/glob.zig b/src/glob.zig index ee171a68045dc..3b97954620f3a 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -1366,7 +1366,7 @@ pub fn GlobWalker_( return matchImpl( codepoints, filepath, - ); + ).matches(); } fn componentStringUnicode(this: *GlobWalker, pattern_component: *Component) []const u32 { @@ -1831,6 +1831,18 @@ const BraceStack = struct { } }; +pub const MatchResult = enum { + no_match, + match, + + negate_no_match, + negate_match, + + pub fn matches(this: MatchResult) bool { + return this == .match or this == .negate_match; + } +}; + /// This function checks returns a boolean value if the pathname `path` matches /// the pattern `glob`. /// @@ -1858,7 +1870,7 @@ const BraceStack = struct { /// Multiple "!" characters negate the pattern multiple times. /// "\" /// Used to escape any of the special characters above. -pub fn matchImpl(glob: []const u32, path: []const u8) bool { +pub fn matchImpl(glob: []const u32, path: []const u8) MatchResult { const path_iter = CodepointIterator.init(path); // This algorithm is based on https://research.swtch.com/glob @@ -1943,7 +1955,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { (glob[state.glob_index] == ',' or glob[state.glob_index] == '}')) { if (state.skipBraces(glob, false) == .Invalid) - return false; // invalid pattern! + return .no_match; // invalid pattern! } continue; @@ -1974,7 +1986,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { while (state.glob_index < glob.len and (first or glob[state.glob_index] != ']')) { var low = glob[state.glob_index]; if (!unescape(&low, glob, &state.glob_index)) - return false; // Invalid pattern + return .no_match; // Invalid pattern state.glob_index += 1; // If there is a - and the following character is not ], @@ -1985,7 +1997,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { state.glob_index += 1; var h = glob[state.glob_index]; if (!unescape(&h, glob, &state.glob_index)) - return false; // Invalid pattern! + return .no_match; // Invalid pattern! state.glob_index += 1; break :blk h; } else low; @@ -1995,7 +2007,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { first = false; } if (state.glob_index >= glob.len) - return false; // Invalid pattern! + return .no_match; // Invalid pattern! state.glob_index += 1; if (is_match != class_negated) { state.path_index.bump(&path_iter); @@ -2004,7 +2016,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { }, '{' => if (state.path_index.cursor.i < path.len) { if (brace_stack.len >= brace_stack.stack.len) - return false; // Invalid pattern! Too many nested braces. + return .no_match; // Invalid pattern! Too many nested braces. // Push old state to the stack, and reset current state. state = brace_stack.push(&state); @@ -2037,7 +2049,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { var cc = c; // Match escaped characters as literals. if (!unescape(&cc, glob, &state.glob_index)) - return false; // Invalid pattern; + return .no_match; // Invalid pattern; const is_match = if (cc == '/') isSeparator(state.path_index.cursor.c) @@ -2073,7 +2085,7 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { if (brace_stack.len > 0) { // If in braces, find next option and reset path to index where we saw the '{' switch (state.skipBraces(glob, true)) { - .Invalid => return false, + .Invalid => return .no_match, .Comma => { state.path_index = brace_stack.last().path_index; continue; @@ -2097,10 +2109,10 @@ pub fn matchImpl(glob: []const u32, path: []const u8) bool { } } - return negated; + return if (negated) .negate_match else .no_match; } - return !negated; + return if (!negated) .match else .negate_no_match; } pub inline fn isSeparator(c: Codepoint) bool { diff --git a/src/heap_breakdown.zig b/src/heap_breakdown.zig index 6dba5d508bd56..692018d1f1dd3 100644 --- a/src/heap_breakdown.zig +++ b/src/heap_breakdown.zig @@ -1,60 +1,59 @@ const bun = @import("root").bun; const std = @import("std"); -const HeapBreakdown = @This(); +const Environment = bun.Environment; +const Allocator = std.mem.Allocator; +const vm_size_t = usize; + +pub const enabled = Environment.allow_assert and Environment.isMac; + +fn heapLabel(comptime T: type) [:0]const u8 { + const base_name = if (@hasDecl(T, "heap_label")) + T.heap_label + else + bun.meta.typeBaseName(@typeName(T)); + return base_name; +} pub fn allocator(comptime T: type) std.mem.Allocator { - return malloc_zone_t.get(T).getAllocator(); + return namedAllocator(comptime heapLabel(T)); +} +pub fn namedAllocator(comptime name: [:0]const u8) std.mem.Allocator { + return getZone("Bun__" ++ name).allocator(); } -pub const malloc_zone_t = opaque { - const Allocator = std.mem.Allocator; - const vm_size_t = usize; - - pub extern fn malloc_default_zone() *malloc_zone_t; - pub extern fn malloc_create_zone(start_size: vm_size_t, flags: c_uint) *malloc_zone_t; - pub extern fn malloc_destroy_zone(zone: *malloc_zone_t) void; - pub extern fn malloc_zone_malloc(zone: *malloc_zone_t, size: usize) ?*anyopaque; - pub extern fn malloc_zone_calloc(zone: *malloc_zone_t, num_items: usize, size: usize) ?*anyopaque; - pub extern fn malloc_zone_valloc(zone: *malloc_zone_t, size: usize) ?*anyopaque; - pub extern fn malloc_zone_free(zone: *malloc_zone_t, ptr: ?*anyopaque) void; - pub extern fn malloc_zone_realloc(zone: *malloc_zone_t, ptr: ?*anyopaque, size: usize) ?*anyopaque; - pub extern fn malloc_zone_from_ptr(ptr: ?*const anyopaque) *malloc_zone_t; - pub extern fn malloc_zone_memalign(zone: *malloc_zone_t, alignment: usize, size: usize) ?*anyopaque; - pub extern fn malloc_zone_batch_malloc(zone: *malloc_zone_t, size: usize, results: [*]?*anyopaque, num_requested: c_uint) c_uint; - pub extern fn malloc_zone_batch_free(zone: *malloc_zone_t, to_be_freed: [*]?*anyopaque, num: c_uint) void; - pub extern fn malloc_default_purgeable_zone() *malloc_zone_t; - pub extern fn malloc_make_purgeable(ptr: ?*anyopaque) void; - pub extern fn malloc_make_nonpurgeable(ptr: ?*anyopaque) c_int; - pub extern fn malloc_zone_register(zone: *malloc_zone_t) void; - pub extern fn malloc_zone_unregister(zone: *malloc_zone_t) void; - pub extern fn malloc_set_zone_name(zone: *malloc_zone_t, name: ?[*:0]const u8) void; - pub extern fn malloc_get_zone_name(zone: *malloc_zone_t) ?[*:0]const u8; - pub extern fn malloc_zone_pressure_relief(zone: *malloc_zone_t, goal: usize) usize; - - pub fn get(comptime T: type) *malloc_zone_t { - const Holder = struct { - pub var zone_t: std.atomic.Value(?*malloc_zone_t) = std.atomic.Value(?*malloc_zone_t).init(null); - pub var zone_t_lock: bun.Lock = bun.Lock.init(); - }; - return Holder.zone_t.load(.monotonic) orelse brk: { - Holder.zone_t_lock.lock(); - defer Holder.zone_t_lock.unlock(); +pub fn getZoneT(comptime T: type) *Zone { + return getZone(comptime heapLabel(T)); +} - if (Holder.zone_t.load(.monotonic)) |z| { - break :brk z; - } +pub fn getZone(comptime name: [:0]const u8) *Zone { + comptime bun.assert(enabled); - const z = malloc_zone_t.create(T); - Holder.zone_t.store(z, .monotonic); - break :brk z; - }; + const static = struct { + pub var zone: *Zone = undefined; + pub fn initOnce() void { + zone = Zone.init(name); + } + + pub var once = std.once(initOnce); + }; + + static.once.call(); + return static.zone; +} + +pub const Zone = opaque { + pub fn init(comptime name: [:0]const u8) *Zone { + const zone = malloc_create_zone(0, 0); + + malloc_set_zone_name(zone, name.ptr); + + return zone; } - fn alignedAlloc(zone: *malloc_zone_t, len: usize, alignment: usize) ?[*]u8 { + fn alignedAlloc(zone: *Zone, len: usize, alignment: usize) ?[*]u8 { // The posix_memalign only accepts alignment values that are a // multiple of the pointer size const eff_alignment = @max(alignment, @sizeOf(usize)); - const ptr = malloc_zone_memalign(zone, eff_alignment, len); return @as(?[*]u8, @ptrCast(ptr)); } @@ -63,9 +62,9 @@ pub const malloc_zone_t = opaque { return std.c.malloc_size(ptr); } - fn alloc(ptr: *anyopaque, len: usize, log2_align: u8, _: usize) ?[*]u8 { - const alignment = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_align)); - return alignedAlloc(@ptrCast(ptr), len, alignment); + fn rawAlloc(zone: *anyopaque, len: usize, log2_align: u8, _: usize) ?[*]u8 { + const alignment = @as(usize, 1) << @intCast(log2_align); + return alignedAlloc(@ptrCast(zone), len, alignment); } fn resize(_: *anyopaque, buf: []u8, _: u8, new_len: usize, _: usize) bool { @@ -81,32 +80,57 @@ pub const malloc_zone_t = opaque { return false; } - fn free(ptr: *anyopaque, buf: [*]u8, _: u8, _: usize) void { - malloc_zone_free(@ptrCast(ptr), @ptrCast(buf)); + fn rawFree(zone: *anyopaque, buf: []u8, _: u8, _: usize) void { + malloc_zone_free(@ptrCast(zone), @ptrCast(buf.ptr)); } - pub const VTable = std.mem.Allocator.VTable{ - .alloc = @ptrCast(&alloc), - .resize = @ptrCast(&resize), - .free = @ptrCast(&free), + pub const vtable = std.mem.Allocator.VTable{ + .alloc = &rawAlloc, + .resize = &resize, + .free = &rawFree, }; - pub fn create(comptime T: type) *malloc_zone_t { - const zone = malloc_create_zone(0, 0); - const title = struct { - const base_name = if (@hasDecl(T, "heap_label")) T.heap_label else bun.meta.typeBaseName(@typeName(T)); - pub const title_: []const u8 = "Bun__" ++ base_name ++ .{0}; - pub const title: [:0]const u8 = title_[0 .. title_.len - 1 :0]; - }.title; - malloc_set_zone_name(zone, title.ptr); + pub fn allocator(zone: *Zone) std.mem.Allocator { + return .{ + .vtable = &vtable, + .ptr = zone, + }; + } - return zone; + /// Create a single-item pointer with initialized data. + pub inline fn create(zone: *Zone, comptime T: type, data: T) *T { + const align_of_t: usize = @alignOf(T); + const log2_align_of_t = @ctz(align_of_t); + const ptr: *T = @alignCast(@ptrCast( + rawAlloc(zone, @sizeOf(T), log2_align_of_t, @returnAddress()) orelse bun.outOfMemory(), + )); + ptr.* = data; + return ptr; } - pub fn getAllocator(zone: *malloc_zone_t) std.mem.Allocator { - return Allocator{ - .vtable = &VTable, - .ptr = zone, - }; + /// Free a single-item pointer + pub inline fn destroy(zone: *Zone, comptime T: type, ptr: *T) void { + malloc_zone_free(zone, @ptrCast(ptr)); } + + pub extern fn malloc_default_zone() *Zone; + pub extern fn malloc_create_zone(start_size: vm_size_t, flags: c_uint) *Zone; + pub extern fn malloc_destroy_zone(zone: *Zone) void; + pub extern fn malloc_zone_malloc(zone: *Zone, size: usize) ?*anyopaque; + pub extern fn malloc_zone_calloc(zone: *Zone, num_items: usize, size: usize) ?*anyopaque; + pub extern fn malloc_zone_valloc(zone: *Zone, size: usize) ?*anyopaque; + pub extern fn malloc_zone_free(zone: *Zone, ptr: ?*anyopaque) void; + pub extern fn malloc_zone_realloc(zone: *Zone, ptr: ?*anyopaque, size: usize) ?*anyopaque; + pub extern fn malloc_zone_from_ptr(ptr: ?*const anyopaque) *Zone; + pub extern fn malloc_zone_memalign(zone: *Zone, alignment: usize, size: usize) ?*anyopaque; + pub extern fn malloc_zone_batch_malloc(zone: *Zone, size: usize, results: [*]?*anyopaque, num_requested: c_uint) c_uint; + pub extern fn malloc_zone_batch_free(zone: *Zone, to_be_freed: [*]?*anyopaque, num: c_uint) void; + pub extern fn malloc_default_purgeable_zone() *Zone; + pub extern fn malloc_make_purgeable(ptr: ?*anyopaque) void; + pub extern fn malloc_make_nonpurgeable(ptr: ?*anyopaque) c_int; + pub extern fn malloc_zone_register(zone: *Zone) void; + pub extern fn malloc_zone_unregister(zone: *Zone) void; + pub extern fn malloc_set_zone_name(zone: *Zone, name: ?[*:0]const u8) void; + pub extern fn malloc_get_zone_name(zone: *Zone) ?[*:0]const u8; + pub extern fn malloc_zone_pressure_relief(zone: *Zone, goal: usize) usize; }; diff --git a/src/hive_array.zig b/src/hive_array.zig index 05df5f19557fe..c3479da816bdb 100644 --- a/src/hive_array.zig +++ b/src/hive_array.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const assert = @import("root").bun.assert; +const bun = @import("root").bun; +const assert = bun.assert; const mem = std.mem; const testing = std.testing; @@ -66,7 +67,7 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { } pub const Fallback = struct { - hive: HiveArray(T, capacity), + hive: if (capacity > 0) HiveArray(T, capacity) else void, allocator: std.mem.Allocator, pub const This = @This(); @@ -74,37 +75,53 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { pub fn init(allocator: std.mem.Allocator) This { return .{ .allocator = allocator, - .hive = HiveArray(T, capacity).init(), + .hive = if (capacity > 0) HiveArray(T, capacity).init() else {}, }; } pub fn get(self: *This) *T { - if (self.hive.get()) |value| { - return value; + if (comptime capacity > 0) { + if (self.hive.get()) |value| { + return value; + } } return self.allocator.create(T) catch unreachable; } pub fn getAndSeeIfNew(self: *This, new: *bool) *T { - if (self.hive.get()) |value| { - new.* = false; - return value; + if (comptime capacity > 0) { + if (self.hive.get()) |value| { + new.* = false; + return value; + } } return self.allocator.create(T) catch unreachable; } pub fn tryGet(self: *This) !*T { - if (self.hive.get()) |value| { - return value; + if (comptime capacity > 0) { + if (self.hive.get()) |value| { + return value; + } } return try self.allocator.create(T); } + pub fn in(self: *const This, value: *const T) bool { + if (comptime capacity > 0) { + if (self.hive.in(value)) return true; + } + + return false; + } + pub fn put(self: *This, value: *T) void { - if (self.hive.put(value)) return; + if (comptime capacity > 0) { + if (self.hive.put(value)) return; + } self.allocator.destroy(value); } diff --git a/src/hmac.zig b/src/hmac.zig new file mode 100644 index 0000000000000..f33a1357df7d5 --- /dev/null +++ b/src/hmac.zig @@ -0,0 +1,21 @@ +const bun = @import("root").bun; + +const std = @import("std"); +const boring = bun.BoringSSL; + +pub fn generate(key: []const u8, data: []const u8, algorithm: bun.JSC.API.Bun.Crypto.EVP.Algorithm, out: *[boring.EVP_MAX_MD_SIZE]u8) ?[]const u8 { + var outlen: c_uint = boring.EVP_MAX_MD_SIZE; + if (boring.HMAC( + algorithm.md() orelse bun.Output.panic("Expected BoringSSL algorithm for HMAC", .{}), + key.ptr, + key.len, + data.ptr, + data.len, + out, + &outlen, + ) == null) { + return null; + } + + return out[0..outlen]; +} diff --git a/src/http.zig b/src/http.zig index e320919b4fef0..1cfb72daee52f 100644 --- a/src/http.zig +++ b/src/http.zig @@ -33,6 +33,7 @@ const BoringSSL = bun.BoringSSL; const Progress = bun.Progress; const X509 = @import("./bun.js/api/bun/x509.zig"); const SSLConfig = @import("./bun.js/api/server.zig").ServerConfig.SSLConfig; +const SSLWrapper = @import("./bun.js/api/bun/ssl_wrapper.zig").SSLWrapper; const URLBufferPool = ObjectPool([8192]u8, null, false, 10); const uws = bun.uws; @@ -53,6 +54,11 @@ var async_http_id: std.atomic.Value(u32) = std.atomic.Value(u32).init(0); const MAX_REDIRECT_URL_LENGTH = 128 * 1024; var custom_ssl_context_map = std.AutoArrayHashMap(*SSLConfig, *NewHTTPContext(true)).init(bun.default_allocator); +pub var max_http_header_size: usize = 16 * 1024; +comptime { + @export(max_http_header_size, .{ .name = "BUN_DEFAULT_MAX_HTTP_HEADER_SIZE" }); +} + const print_every = 0; var print_every_i: usize = 0; @@ -194,132 +200,313 @@ pub const Sendfile = struct { }; }; -const ProxySSLData = struct { - buffer: std.ArrayList(u8), - partial: bool, - temporary_slice: ?[]const u8, - pub fn init() !ProxySSLData { - const buffer = try std.ArrayList(u8).initCapacity(bun.default_allocator, 16 * 1024); - - return ProxySSLData{ .buffer = buffer, .partial = false, .temporary_slice = null }; - } +const ProxyTunnel = struct { + wrapper: ?ProxyTunnelWrapper = null, + shutdown_err: anyerror = error.ConnectionClosed, + // active socket is the socket that is currently being used + socket: union(enum) { + tcp: NewHTTPContext(false).HTTPSocket, + ssl: NewHTTPContext(true).HTTPSocket, + none: void, + } = .{ .none = {} }, + write_buffer: bun.io.StreamBuffer = .{}, + ref_count: u32 = 1, + + const ProxyTunnelWrapper = SSLWrapper(*HTTPClient); + + usingnamespace bun.NewRefCounted(ProxyTunnel, ProxyTunnel.deinit); + + fn onOpen(this: *HTTPClient) void { + this.state.response_stage = .proxy_handshake; + this.state.request_stage = .proxy_handshake; + if (this.proxy_tunnel) |proxy| { + proxy.ref(); + defer proxy.deref(); + if (proxy.wrapper) |*wrapper| { + var ssl_ptr = wrapper.ssl orelse return; + const _hostname = this.hostname orelse this.url.hostname; + + var hostname: [:0]const u8 = ""; + var hostname_needs_free = false; + if (!strings.isIPAddress(_hostname)) { + if (_hostname.len < temp_hostname.len) { + @memcpy(temp_hostname[0.._hostname.len], _hostname); + temp_hostname[_hostname.len] = 0; + hostname = temp_hostname[0.._hostname.len :0]; + } else { + hostname = bun.default_allocator.dupeZ(u8, _hostname) catch unreachable; + hostname_needs_free = true; + } + } - pub fn slice(this: *@This()) []const u8 { - if (this.temporary_slice) |data| { - return data; + defer if (hostname_needs_free) bun.default_allocator.free(hostname); + ssl_ptr.configureHTTPClient(hostname); + } } - const data = this.buffer.toOwnedSliceSentinel(0) catch unreachable; - this.temporary_slice = data; - return data; } - pub fn deinit(this: @This()) void { - this.buffer.deinit(); - if (this.temporary_slice) |data| { - bun.default_allocator.free(data); + fn onData(this: *HTTPClient, decoded_data: []const u8) void { + if (decoded_data.len == 0) return; + log("onData decoded {}", .{decoded_data.len}); + + if (this.proxy_tunnel) |proxy| { + proxy.ref(); + defer proxy.deref(); + switch (this.state.response_stage) { + .body => { + if (decoded_data.len == 0) return; + const report_progress = this.handleResponseBody(decoded_data, false) catch |err| { + proxy.close(err); + return; + }; + + if (report_progress) { + switch (proxy.socket) { + .ssl => |socket| { + this.progressUpdate(true, &http_thread.https_context, socket); + }, + .tcp => |socket| { + this.progressUpdate(false, &http_thread.http_context, socket); + }, + .none => {}, + } + return; + } + }, + .body_chunk => { + if (decoded_data.len == 0) return; + const report_progress = this.handleResponseBodyChunkedEncoding(decoded_data) catch |err| { + proxy.close(err); + return; + }; + + if (report_progress) { + switch (proxy.socket) { + .ssl => |socket| { + this.progressUpdate(true, &http_thread.https_context, socket); + }, + .tcp => |socket| { + this.progressUpdate(false, &http_thread.http_context, socket); + }, + .none => {}, + } + return; + } + }, + .proxy_headers => { + switch (proxy.socket) { + .ssl => |socket| { + this.handleOnDataHeaders(true, decoded_data, &http_thread.https_context, socket); + }, + .tcp => |socket| { + this.handleOnDataHeaders(false, decoded_data, &http_thread.http_context, socket); + }, + .none => {}, + } + }, + else => { + this.state.pending_response = null; + proxy.close(error.UnexpectedData); + }, + } } } -}; -const ProxyTunnel = struct { - ssl_ctx: *BoringSSL.SSL_CTX, - ssl: *BoringSSL.SSL, - out_bio: *BoringSSL.BIO, - in_bio: *BoringSSL.BIO, - partial_data: ?ProxySSLData, - read_buffer: []u8, - - pub fn init(comptime is_ssl: bool, client: *HTTPClient, socket: NewHTTPContext(is_ssl).HTTPSocket) ProxyTunnel { - BoringSSL.load(); - const context = BoringSSL.SSL_CTX.init(); - - if (context) |ssl_context| { - const ssl_ctx = ssl_context; - var ssl = BoringSSL.SSL.init(ssl_context); - ssl.setIsClient(true); - var out_bio: *BoringSSL.BIO = undefined; - if (comptime is_ssl) { - //TLS -> TLS - const proxy_ssl: *BoringSSL.SSL = @as(*BoringSSL.SSL, @ptrCast(socket.getNativeHandle())); - //create new SSL BIO - out_bio = BoringSSL.BIO_new(BoringSSL.BIO_f_ssl()) orelse unreachable; - //chain SSL bio with proxy BIO - const proxy_bio = BoringSSL.SSL_get_wbio(proxy_ssl); - _ = BoringSSL.BIO_push(out_bio, proxy_bio); + fn onHandshake(this: *HTTPClient, handshake_success: bool, ssl_error: uws.us_bun_verify_error_t) void { + if (this.proxy_tunnel) |proxy| { + proxy.ref(); + defer proxy.deref(); + this.state.response_stage = .proxy_headers; + this.state.request_stage = .proxy_headers; + this.state.request_sent_len = 0; + const handshake_error = HTTPCertError{ + .error_no = ssl_error.error_no, + .code = if (ssl_error.code == null) "" else ssl_error.code[0..bun.len(ssl_error.code) :0], + .reason = if (ssl_error.code == null) "" else ssl_error.reason[0..bun.len(ssl_error.reason) :0], + }; + if (handshake_success) { + // handshake completed but we may have ssl errors + this.flags.did_have_handshaking_error = handshake_error.error_no != 0; + if (this.flags.reject_unauthorized) { + // only reject the connection if reject_unauthorized == true + if (this.flags.did_have_handshaking_error) { + proxy.close(BoringSSL.getCertErrorFromNo(handshake_error.error_no)); + return; + } + + // if checkServerIdentity returns false, we dont call open this means that the connection was rejected + bun.assert(proxy.wrapper != null); + const ssl_ptr = proxy.wrapper.?.ssl orelse return; + + switch (proxy.socket) { + .ssl => |socket| { + if (!this.checkServerIdentity(true, socket, handshake_error, ssl_ptr, false)) { + this.flags.did_have_handshaking_error = true; + this.unregisterAbortTracker(); + return; + } + }, + .tcp => |socket| { + if (!this.checkServerIdentity(false, socket, handshake_error, ssl_ptr, false)) { + this.flags.did_have_handshaking_error = true; + this.unregisterAbortTracker(); + return; + } + }, + .none => {}, + } + } + + switch (proxy.socket) { + .ssl => |socket| { + this.onWritable(true, true, socket); + }, + .tcp => |socket| { + this.onWritable(true, false, socket); + }, + .none => {}, + } } else { - // socket output bio for non-TLS -> TLS - const fd = @as(c_int, @intCast(@intFromPtr(socket.getNativeHandle()))); - out_bio = BoringSSL.BIO_new_fd(fd, BoringSSL.BIO_NOCLOSE); + // if we are here is because server rejected us, and the error_no is the cause of this + // if we set reject_unauthorized == false this means the server requires custom CA aka NODE_EXTRA_CA_CERTS + if (this.flags.did_have_handshaking_error) { + proxy.close(BoringSSL.getCertErrorFromNo(handshake_error.error_no)); + return; + } + // if handshake_success it self is false, this means that the connection was rejected + proxy.close(error.ConnectionRefused); + return; } + } + } - // in memory bio to control input flow from onData handler - const in_bio = BoringSSL.BIO.init() catch { - unreachable; + pub fn write(this: *HTTPClient, encoded_data: []const u8) void { + if (this.proxy_tunnel) |proxy| { + const written = switch (proxy.socket) { + .ssl => |socket| socket.write(encoded_data, true), + .tcp => |socket| socket.write(encoded_data, true), + .none => 0, }; - _ = BoringSSL.BIO_set_mem_eof_return(in_bio, -1); - ssl.setBIO(in_bio, out_bio); - - const hostname = bun.default_allocator.dupeZ(u8, client.hostname orelse client.url.hostname) catch unreachable; - defer bun.default_allocator.free(hostname); - - ssl.configureHTTPClient(hostname); - BoringSSL.SSL_CTX_set_verify(ssl_ctx, BoringSSL.SSL_VERIFY_NONE, null); - BoringSSL.SSL_set_verify(ssl, BoringSSL.SSL_VERIFY_NONE, null); - // TODO: change this to ssl_renegotiate_explicit for optimization - // if we allow renegotiation, we need to set the mode here - // https://github.com/oven-sh/bun/issues/6197 - // https://github.com/oven-sh/bun/issues/5363 - // renegotiation is only valid for <= TLS1_2_VERSION - BoringSSL.SSL_set_renegotiate_mode(ssl, BoringSSL.ssl_renegotiate_freely); - return ProxyTunnel{ .ssl = ssl, .ssl_ctx = ssl_ctx, .in_bio = in_bio, .out_bio = out_bio, .read_buffer = bun.default_allocator.alloc(u8, 16 * 1024) catch unreachable, .partial_data = null }; + const pending = encoded_data[@intCast(written)..]; + if (pending.len > 0) { + // lets flush when we are trully writable + proxy.write_buffer.write(pending) catch bun.outOfMemory(); + } } - unreachable; } - pub fn getSSLData(this: *@This(), incoming_data: ?[]const u8) !ProxySSLData { - if (incoming_data) |data| { - _ = this.in_bio.write(data) catch { - return error.OutOfMemory; - }; + fn onClose(this: *HTTPClient) void { + if (this.proxy_tunnel) |proxy| { + proxy.ref(); + // defer the proxy deref the proxy tunnel may still be in use after triggering the close callback + defer http_thread.scheduleProxyDeref(proxy); + const err = proxy.shutdown_err; + switch (proxy.socket) { + .ssl => |socket| { + this.closeAndFail(err, true, socket); + }, + .tcp => |socket| { + this.closeAndFail(err, false, socket); + }, + .none => {}, + } + proxy.detachSocket(); } + } + + fn start(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket, ssl_options: JSC.API.ServerConfig.SSLConfig) void { + const proxy_tunnel = ProxyTunnel.new(.{}); + + var custom_options = ssl_options; + // we always request the cert so we can verify it and also we manually abort the connection if the hostname doesn't match + custom_options.reject_unauthorized = 0; + custom_options.request_cert = 1; + proxy_tunnel.wrapper = SSLWrapper(*HTTPClient).init(custom_options, true, .{ + .onOpen = ProxyTunnel.onOpen, + .onData = ProxyTunnel.onData, + .onHandshake = ProxyTunnel.onHandshake, + .onClose = ProxyTunnel.onClose, + .write = ProxyTunnel.write, + .ctx = this, + }) catch |err| { + if (err == error.OutOfMemory) { + bun.outOfMemory(); + } - var data: ProxySSLData = undefined; - if (this.partial_data) |partial| { - data = partial; - data.partial = false; + // invalid TLS Options + proxy_tunnel.detachAndDeref(); + this.closeAndFail(error.ConnectionRefused, is_ssl, socket); + return; + }; + this.proxy_tunnel = proxy_tunnel; + if (is_ssl) { + proxy_tunnel.socket = .{ .ssl = socket }; } else { - data = try ProxySSLData.init(); + proxy_tunnel.socket = .{ .tcp = socket }; } + proxy_tunnel.wrapper.?.start(); + } - var writer = data.buffer.writer(); - while (true) { - const read_size = this.ssl.read(this.read_buffer) catch |err| { - // handshake needed - if (err == error.WantWrite) { - //needs handshake - data.partial = true; - this.partial_data = data; - return data; - } + pub fn close(this: *ProxyTunnel, err: anyerror) void { + this.shutdown_err = err; + if (this.wrapper) |*wrapper| { + // fast shutdown the connection + _ = wrapper.shutdown(true); + } + } - break; - }; - // no more data - if (read_size == 0) { - break; - } - _ = writer.write(this.read_buffer[0..read_size]) catch 0; + pub fn onWritable(this: *ProxyTunnel, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { + this.ref(); + defer this.deref(); + const encoded_data = this.write_buffer.slice(); + if (encoded_data.len == 0) { + return; + } + const written = socket.write(encoded_data, true); + if (written == encoded_data.len) { + this.write_buffer.reset(); + return; + } + + this.write_buffer.cursor += @intCast(written); + if (this.wrapper) |*wrapper| { + // Cycle to through the SSL state machine + _ = wrapper.flush(); + } + } + + pub fn receiveData(this: *ProxyTunnel, buf: []const u8) void { + this.ref(); + defer this.deref(); + if (this.wrapper) |*wrapper| { + wrapper.receiveData(buf); } - return data; } - pub fn deinit(this: @This()) void { - this.ssl.deinit(); - this.ssl_ctx.deinit(); - if (this.partial_data) |ssl_data| { - ssl_data.deinit(); + + pub fn writeData(this: *ProxyTunnel, buf: []const u8) !usize { + if (this.wrapper) |*wrapper| { + return try wrapper.writeData(buf); + } + return 0; + } + + pub fn detachSocket(this: *ProxyTunnel) void { + this.socket = .{ .none = {} }; + } + + pub fn detachAndDeref(this: *ProxyTunnel) void { + this.detachSocket(); + this.deref(); + } + + pub fn deinit(this: *ProxyTunnel) void { + this.socket = .{ .none = {} }; + if (this.wrapper) |*wrapper| { + wrapper.deinit(); + this.wrapper = null; } - bun.default_allocator.free(this.read_buffer); - // no need to call BIO_free because of ssl.setBIO + this.write_buffer.deinit(); + this.destroy(); } }; @@ -342,7 +529,9 @@ fn NewHTTPContext(comptime ssl: bool) type { }; pub fn markSocketAsDead(socket: HTTPSocket) void { - socket.ext(**anyopaque).* = bun.cast(**anyopaque, ActiveSocket.init(&dead_socket).ptr()); + if (socket.ext(**anyopaque)) |ctx| { + ctx.* = bun.cast(**anyopaque, ActiveSocket.init(&dead_socket).ptr()); + } } fn terminateSocket(socket: HTTPSocket) void { @@ -350,6 +539,11 @@ fn NewHTTPContext(comptime ssl: bool) type { socket.close(.failure); } + fn closeSocket(socket: HTTPSocket) void { + markSocketAsDead(socket); + socket.close(.normal); + } + fn getTagged(ptr: *anyopaque) ActiveSocket { return ActiveSocket.from(bun.cast(**anyopaque, ptr).*); } @@ -456,7 +650,9 @@ fn NewHTTPContext(comptime ssl: bool) type { if (hostname.len <= MAX_KEEPALIVE_HOSTNAME and !socket.isClosedOrHasError() and socket.isEstablished()) { if (this.pending_sockets.get()) |pending| { - socket.ext(**anyopaque).* = bun.cast(**anyopaque, ActiveSocket.init(pending).ptr()); + if (socket.ext(**anyopaque)) |ctx| { + ctx.* = bun.cast(**anyopaque, ActiveSocket.init(pending).ptr()); + } socket.flush(); socket.timeout(0); socket.setTimeoutMinutes(5); @@ -472,8 +668,7 @@ fn NewHTTPContext(comptime ssl: bool) type { } } - markSocketAsDead(socket); - socket.close(.normal); + closeSocket(socket); } pub const Handler = struct { @@ -487,7 +682,7 @@ fn NewHTTPContext(comptime ssl: bool) type { } if (active.get(PooledSocket)) |pooled| { - assert(context().pending_sockets.put(pooled)); + addMemoryBackToPool(pooled); return; } @@ -500,56 +695,73 @@ fn NewHTTPContext(comptime ssl: bool) type { success: i32, ssl_error: uws.us_bun_verify_error_t, ) void { - const authorized = if (success == 1) true else false; + const handshake_success = if (success == 1) true else false; const handshake_error = HTTPCertError{ .error_no = ssl_error.error_no, .code = if (ssl_error.code == null) "" else ssl_error.code[0..bun.len(ssl_error.code) :0], .reason = if (ssl_error.code == null) "" else ssl_error.reason[0..bun.len(ssl_error.reason) :0], }; - // log("onHandshake(0x{}) authorized: {} error: {s}", .{ bun.fmt.hexIntUpper(@intFromPtr(socket.socket)), authorized, handshake_error.code }); const active = getTagged(ptr); if (active.get(HTTPClient)) |client| { - if (handshake_error.error_no != 0 and (client.reject_unauthorized or !authorized)) { - client.closeAndFail(BoringSSL.getCertErrorFromNo(handshake_error.error_no), comptime ssl, socket); - return; - } - // no handshake_error at this point - if (authorized) { - client.did_have_handshaking_error = handshake_error.error_no != 0; - - // if checkServerIdentity returns false, we dont call open this means that the connection was rejected - if (!client.checkServerIdentity(comptime ssl, socket, handshake_error)) { - client.did_have_handshaking_error = true; + // handshake completed but we may have ssl errors + client.flags.did_have_handshaking_error = handshake_error.error_no != 0; + if (handshake_success) { + if (client.flags.reject_unauthorized) { + // only reject the connection if reject_unauthorized == true + if (client.flags.did_have_handshaking_error) { + client.closeAndFail(BoringSSL.getCertErrorFromNo(handshake_error.error_no), comptime ssl, socket); + return; + } - return; + // if checkServerIdentity returns false, we dont call open this means that the connection was rejected + const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(socket.getNativeHandle())); + if (!client.checkServerIdentity(comptime ssl, socket, handshake_error, ssl_ptr, true)) { + client.flags.did_have_handshaking_error = true; + client.unregisterAbortTracker(); + if (!socket.isClosed()) terminateSocket(socket); + return; + } } return client.firstCall(comptime ssl, socket); } else { - // if authorized it self is false, this means that the connection was rejected - markSocketAsDead(socket); - if (client.state.stage != .done and client.state.stage != .fail) - client.fail(error.ConnectionRefused); + // if we are here is because server rejected us, and the error_no is the cause of this + // if we set reject_unauthorized == false this means the server requires custom CA aka NODE_EXTRA_CA_CERTS + if (client.flags.did_have_handshaking_error) { + client.closeAndFail(BoringSSL.getCertErrorFromNo(handshake_error.error_no), comptime ssl, socket); + return; + } + // if handshake_success it self is false, this means that the connection was rejected + client.closeAndFail(error.ConnectionRefused, comptime ssl, socket); return; } } - // we can reach here if we are aborted - if (!socket.isClosed()) { + if (socket.isClosed()) { + markSocketAsDead(socket); if (active.get(PooledSocket)) |pooled| { - assert(context().pending_sockets.put(pooled)); - return; + addMemoryBackToPool(pooled); } - terminateSocket(socket); - } else { - if (active.get(PooledSocket)) |pooled| { - assert(context().pending_sockets.put(pooled)); + return; + } + + if (handshake_success) { + if (active.is(PooledSocket)) { + // Allow pooled sockets to be reused if the handshake was successful. + socket.setTimeout(0); + socket.setTimeoutMinutes(5); return; } } + + if (active.get(PooledSocket)) |pooled| { + addMemoryBackToPool(pooled); + } + + terminateSocket(socket); } pub fn onClose( ptr: *anyopaque, @@ -565,11 +777,16 @@ fn NewHTTPContext(comptime ssl: bool) type { } if (tagged.get(PooledSocket)) |pooled| { - assert(context().pending_sockets.put(pooled)); + addMemoryBackToPool(pooled); } return; } + + fn addMemoryBackToPool(pooled: *PooledSocket) void { + assert(context().pending_sockets.put(pooled)); + } + pub fn onData( ptr: *anyopaque, socket: HTTPSocket, @@ -620,14 +837,12 @@ fn NewHTTPContext(comptime ssl: bool) type { socket: HTTPSocket, ) void { const tagged = getTagged(ptr); - if (tagged.get(HTTPClient)) |client| { - return client.onTimeout( - comptime ssl, - socket, - ); + return client.onTimeout(comptime ssl, socket); } else if (tagged.get(PooledSocket)) |pooled| { - assert(context().pending_sockets.put(pooled)); + // If a socket has been sitting around for 5 minutes + // Let's close it and remove it from the pool. + addMemoryBackToPool(pooled); } terminateSocket(socket); @@ -640,23 +855,23 @@ fn NewHTTPContext(comptime ssl: bool) type { const tagged = getTagged(ptr); markSocketAsDead(socket); if (tagged.get(HTTPClient)) |client| { - return client.onConnectError( - comptime ssl, - socket, - ); + client.onConnectError(); } else if (tagged.get(PooledSocket)) |pooled| { - assert(context().pending_sockets.put(pooled)); + addMemoryBackToPool(pooled); } - - if (comptime Environment.isDebug) - // caller should already have closed it. - bun.debugAssert(socket.isClosed()); + // us_connecting_socket_close is always called internally by uSockets } pub fn onEnd( _: *anyopaque, socket: HTTPSocket, ) void { - // TCP fin gets closed immediately. + // TCP fin must be closed, but we must keep the original tagged + // pointer so that their onClose callback is called. + // + // Three possible states: + // 1. HTTP Keep-Alive socket: it must be removed from the pool + // 2. HTTP Client socket: it might need to be retried + // 3. Dead socket: it is already marked as dead socket.close(.failure); } }; @@ -720,8 +935,10 @@ fn NewHTTPContext(comptime ssl: bool) type { client.connected_url.hostname = hostname; if (client.isKeepAlivePossible()) { - if (this.existingSocket(client.reject_unauthorized, hostname, port)) |sock| { - sock.ext(**anyopaque).* = bun.cast(**anyopaque, ActiveSocket.init(client).ptr()); + if (this.existingSocket(client.flags.reject_unauthorized, hostname, port)) |sock| { + if (sock.ext(**anyopaque)) |ctx| { + ctx.* = bun.cast(**anyopaque, ActiveSocket.init(client).ptr()); + } client.allow_retry = true; client.onOpen(comptime ssl, sock); if (comptime ssl) { @@ -748,8 +965,6 @@ const Queue = UnboundedQueue(AsyncHTTP, .next); const ShutdownQueue = UnboundedQueue(AsyncHTTP, .next); pub const HTTPThread = struct { - var http_thread_loaded: std.atomic.Value(bool) = std.atomic.Value(bool).init(false); - loop: *JSC.MiniEventLoop, http_context: NewHTTPContext(false), https_context: NewHTTPContext(true), @@ -757,23 +972,40 @@ pub const HTTPThread = struct { queued_tasks: Queue = Queue{}, queued_shutdowns: std.ArrayListUnmanaged(ShutdownMessage) = std.ArrayListUnmanaged(ShutdownMessage){}, - queued_shutdowns_lock: bun.Lock = bun.Lock.init(), + queued_shutdowns_lock: bun.Lock = .{}, + + queued_proxy_deref: std.ArrayListUnmanaged(*ProxyTunnel) = std.ArrayListUnmanaged(*ProxyTunnel){}, has_awoken: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), timer: std.time.Timer, + lazy_libdeflater: ?*LibdeflateState = null, + + const threadlog = Output.scoped(.HTTPThread, true); + const ShutdownMessage = struct { async_http_id: u32, is_tls: bool, }; - const threadlog = Output.scoped(.HTTPThread, true); + pub const LibdeflateState = struct { + decompressor: *bun.libdeflate.Decompressor = undefined, + shared_buffer: [512 * 1024]u8 = undefined, - pub fn init() !void { - if (http_thread_loaded.swap(true, .seq_cst)) { - return; + pub usingnamespace bun.New(@This()); + }; + + pub fn deflater(this: *@This()) *LibdeflateState { + if (this.lazy_libdeflater == null) { + this.lazy_libdeflater = LibdeflateState.new(.{ + .decompressor = bun.libdeflate.Decompressor.alloc() orelse bun.outOfMemory(), + }); } + return this.lazy_libdeflater.?; + } + + fn initOnce() void { http_thread = .{ .loop = undefined, .http_context = .{ @@ -784,16 +1016,21 @@ pub const HTTPThread = struct { }, .timer = std.time.Timer.start() catch unreachable, }; - - const thread = try std.Thread.spawn( + bun.libdeflate.load(); + const thread = std.Thread.spawn( .{ .stack_size = bun.default_thread_stack_size, }, onStart, .{}, - ); + ) catch |err| Output.panic("Failed to start HTTP Client thread: {s}", .{@errorName(err)}); thread.detach(); } + var init_once = std.once(initOnce); + + pub fn init() void { + init_once.call(); + } pub fn onStart() void { Output.Source.configureNamedThread("HTTP Client"); @@ -827,29 +1064,48 @@ pub const HTTPThread = struct { for (custom_ssl_context_map.keys()) |other_config| { if (requested_config.isSame(other_config)) { // we free the callers config since we have a existing one - requested_config.deinit(); - bun.default_allocator.destroy(requested_config); + if (requested_config != client.tls_props) { + requested_config.deinit(); + bun.default_allocator.destroy(requested_config); + } client.tls_props = other_config; - return try custom_ssl_context_map.get(other_config).?.connect(client, client.url.hostname, client.url.getPortAuto()); + if (client.http_proxy) |url| { + return try custom_ssl_context_map.get(other_config).?.connect(client, url.hostname, url.getPortAuto()); + } else { + return try custom_ssl_context_map.get(other_config).?.connect(client, client.url.hostname, client.url.getPortAuto()); + } } } // we need the config so dont free it var custom_context = try bun.default_allocator.create(NewHTTPContext(is_ssl)); custom_context.initWithClientConfig(client) catch |err| { - requested_config.deinit(); client.tls_props = null; + + requested_config.deinit(); + bun.default_allocator.destroy(requested_config); bun.default_allocator.destroy(custom_context); return err; }; try custom_ssl_context_map.put(requested_config, custom_context); // We might deinit the socket context, so we disable keepalive to make sure we don't // free it while in use. - client.disable_keepalive = true; + client.flags.disable_keepalive = true; + if (client.http_proxy) |url| { + // https://github.com/oven-sh/bun/issues/11343 + if (url.protocol.len == 0 or strings.eqlComptime(url.protocol, "https") or strings.eqlComptime(url.protocol, "http")) { + return try this.context(is_ssl).connect(client, url.hostname, url.getPortAuto()); + } + return error.UnsupportedProxyProtocol; + } return try custom_context.connect(client, client.url.hostname, client.url.getPortAuto()); } } if (client.http_proxy) |url| { - return try this.context(is_ssl).connect(client, url.hostname, url.getPortAuto()); + // https://github.com/oven-sh/bun/issues/11343 + if (url.protocol.len == 0 or strings.eqlComptime(url.protocol, "https") or strings.eqlComptime(url.protocol, "http")) { + return try this.context(is_ssl).connect(client, url.hostname, url.getPortAuto()); + } + return error.UnsupportedProxyProtocol; } return try this.context(is_ssl).connect(client, client.url.hostname, client.url.getPortAuto()); } @@ -866,18 +1122,21 @@ pub const HTTPThread = struct { if (socket_async_http_abort_tracker.fetchSwapRemove(http.async_http_id)) |socket_ptr| { if (http.is_tls) { const socket = uws.SocketTLS.fromAny(socket_ptr.value); - socket.shutdown(); - socket.shutdownRead(); + // do a fast shutdown here since we are aborting and we dont want to wait for the close_notify from the other side + socket.close(.failure); } else { const socket = uws.SocketTCP.fromAny(socket_ptr.value); - socket.shutdown(); - socket.shutdownRead(); + socket.close(.failure); } } } this.queued_shutdowns.clearRetainingCapacity(); } + while (this.queued_proxy_deref.popOrNull()) |http| { + http.deref(); + } + var count: usize = 0; var active = AsyncHTTP.active_requests_count.load(.monotonic); const max = AsyncHTTP.max_simultaneous_requests.load(.monotonic); @@ -890,10 +1149,11 @@ pub const HTTPThread = struct { } while (this.queued_tasks.pop()) |http| { - var cloned = default_allocator.create(AsyncHTTP) catch unreachable; - cloned.* = http.*; - cloned.real = http; - cloned.onStart(); + var cloned = ThreadlocalAsyncHTTP.new(.{ + .async_http = http.*, + }); + cloned.async_http.real = http; + cloned.async_http.onStart(); if (comptime Environment.allow_assert) { count += 1; } @@ -947,6 +1207,15 @@ pub const HTTPThread = struct { this.loop.loop.wakeup(); } + pub fn scheduleProxyDeref(this: *@This(), proxy: *ProxyTunnel) void { + // this is always called on the http thread + { + this.queued_proxy_deref.append(bun.default_allocator, proxy) catch bun.outOfMemory(); + } + if (this.has_awoken.load(.monotonic)) + this.loop.loop.wakeup(); + } + pub fn wakeup(this: *@This()) void { if (this.has_awoken.load(.monotonic)) this.loop.loop.wakeup(); @@ -978,13 +1247,11 @@ pub fn checkServerIdentity( comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket, certError: HTTPCertError, + sslPtr: *BoringSSL.SSL, + allowProxyUrl: bool, ) bool { - if (comptime is_ssl == false) { - @panic("checkServerIdentity called on non-ssl socket"); - } - if (client.reject_unauthorized) { - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(socket.getNativeHandle())); - if (BoringSSL.SSL_get_peer_cert_chain(ssl_ptr)) |cert_chain| { + if (client.flags.reject_unauthorized) { + if (BoringSSL.SSL_get_peer_cert_chain(sslPtr)) |cert_chain| { if (BoringSSL.sk_X509_value(cert_chain, 0)) |x509| { // check if we need to report the error (probably to `checkServerIdentity` was informed from JS side) @@ -998,8 +1265,10 @@ pub fn checkServerIdentity( assert(result_size == cert_size); var hostname = client.hostname orelse client.url.hostname; - if (client.http_proxy) |proxy| { - hostname = proxy.hostname; + if (allowProxyUrl) { + if (client.http_proxy) |proxy| { + hostname = proxy.hostname; + } } client.state.certificate_info = .{ @@ -1013,7 +1282,7 @@ pub fn checkServerIdentity( }; // we inform the user that the cert is invalid - client.progressUpdate(true, &http_thread.https_context, socket); + client.progressUpdate(is_ssl, if (is_ssl) &http_thread.https_context else &http_thread.http_context, socket); // continue until we are aborted or not return true; } else { @@ -1021,8 +1290,10 @@ pub fn checkServerIdentity( // fast path var hostname = client.hostname orelse client.url.hostname; - if (client.http_proxy) |proxy| { - hostname = proxy.hostname; + if (allowProxyUrl) { + if (client.http_proxy) |proxy| { + hostname = proxy.hostname; + } } if (BoringSSL.checkX509ServerIdentity(x509, hostname)) { @@ -1039,6 +1310,24 @@ pub fn checkServerIdentity( return true; } +fn registerAbortTracker( + client: *HTTPClient, + comptime is_ssl: bool, + socket: NewHTTPContext(is_ssl).HTTPSocket, +) void { + if (client.signals.aborted != null) { + socket_async_http_abort_tracker.put(client.async_http_id, socket.socket) catch unreachable; + } +} + +fn unregisterAbortTracker( + client: *HTTPClient, +) void { + if (client.signals.aborted != null) { + _ = socket_async_http_abort_tracker.swapRemove(client.async_http_id); + } +} + pub fn onOpen( client: *HTTPClient, comptime is_ssl: bool, @@ -1051,9 +1340,7 @@ pub fn onOpen( assert(is_ssl == client.url.isHTTPS()); } } - if (client.signals.aborted != null) { - socket_async_http_abort_tracker.put(client.async_http_id, socket.socket) catch unreachable; - } + client.registerAbortTracker(is_ssl, socket); log("Connected {s} \n", .{client.url.href}); if (client.signals.get(.aborted)) { @@ -1096,6 +1383,13 @@ pub fn firstCall( comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket, ) void { + if (comptime FeatureFlags.is_fetch_preconnect_supported) { + if (client.flags.is_preconnect_only) { + client.onPreconnect(is_ssl, socket); + return; + } + } + if (client.state.request_stage == .pending) { client.onWritable(true, comptime is_ssl, socket); } @@ -1106,8 +1400,19 @@ pub fn onClose( socket: NewHTTPContext(is_ssl).HTTPSocket, ) void { log("Closed {s}\n", .{client.url.href}); + // the socket is closed, we need to unregister the abort tracker + client.unregisterAbortTracker(); - const in_progress = client.state.stage != .done and client.state.stage != .fail; + if (client.signals.get(.aborted)) { + client.fail(error.Aborted); + return; + } + if (client.proxy_tunnel) |tunnel| { + client.proxy_tunnel = null; + // always detach the socket from the tunnel onClose (timeout, connectError will call fail that will do the same) + tunnel.detachAndDeref(); + } + const in_progress = client.state.stage != .done and client.state.stage != .fail and client.state.flags.is_redirect_pending == false; if (in_progress) { // if the peer closed after a full chunk, treat this @@ -1117,14 +1422,14 @@ pub fn onClose( if (picohttp.phr_decode_chunked_is_in_data(&client.state.chunked_decoder) == 0) { const buf = client.state.getBodyBuffer(); if (buf.list.items.len > 0) { - client.state.received_last_chunk = true; + client.state.flags.received_last_chunk = true; client.progressUpdate(comptime is_ssl, if (is_ssl) &http_thread.https_context else &http_thread.http_context, socket); return; } } } else if (client.state.content_length == null and client.state.response_stage == .body) { // no content length informed so we are done here - client.state.received_last_chunk = true; + client.state.flags.received_last_chunk = true; client.progressUpdate(comptime is_ssl, if (is_ssl) &http_thread.https_context else &http_thread.http_context, socket); return; } @@ -1132,12 +1437,14 @@ pub fn onClose( if (client.allow_retry) { client.allow_retry = false; + // we need to retry the request, clean up the response message buffer and start again + client.state.response_message_buffer.deinit(); client.start(client.state.original_request_body, client.state.body_out_str.?); return; } if (in_progress) { - client.closeAndFail(error.ConnectionClosed, is_ssl, socket); + client.fail(error.ConnectionClosed); } } pub fn onTimeout( @@ -1145,21 +1452,17 @@ pub fn onTimeout( comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket, ) void { - _ = socket; + if (client.flags.disable_timeout) return; log("Timeout {s}\n", .{client.url.href}); - if (client.state.stage != .done and client.state.stage != .fail) { - client.fail(error.Timeout); - } + defer NewHTTPContext(is_ssl).terminateSocket(socket); + client.fail(error.Timeout); } pub fn onConnectError( client: *HTTPClient, - comptime is_ssl: bool, - _: NewHTTPContext(is_ssl).HTTPSocket, ) void { log("onConnectError {s}\n", .{client.url.href}); - if (client.state.stage != .done and client.state.stage != .fail) - client.fail(error.ConnectionRefused); + client.fail(error.ConnectionRefused); } pub inline fn getAllocator() std.mem.Allocator { @@ -1280,7 +1583,6 @@ pub const HTTPStage = enum { done, proxy_handshake, proxy_headers, - proxy_decoded_headers, proxy_body, }; @@ -1404,11 +1706,8 @@ pub const InternalState = struct { /// this can happen after await fetch(...) and the body can continue streaming when this is already null /// the user will receive only chunks of the body stored in body_out_str cloned_metadata: ?HTTPResponseMetadata = null, + flags: InternalStateFlags = InternalStateFlags{}, - allow_keepalive: bool = true, - received_last_chunk: bool = false, - did_set_content_encoding: bool = false, - is_redirect_pending: bool = false, transfer_encoding: Encoding = Encoding.identity, encoding: Encoding = Encoding.identity, content_encoding_i: u8 = std.math.maxInt(u8), @@ -1428,6 +1727,15 @@ pub const InternalState = struct { response_stage: HTTPStage = .pending, certificate_info: ?CertificateInfo = null, + pub const InternalStateFlags = packed struct { + allow_keepalive: bool = true, + received_last_chunk: bool = false, + did_set_content_encoding: bool = false, + is_redirect_pending: bool = false, + is_libdeflate_fast_path_disabled: bool = false, + resend_request_body_on_redirect: bool = false, + }; + pub fn init(body: HTTPRequestBody, body_out_str: *MutableString) InternalState { return .{ .original_request_body = body, @@ -1471,7 +1779,7 @@ pub const InternalState = struct { .original_request_body = .{ .bytes = "" }, .request_body = "", .certificate_info = null, - .is_redirect_pending = false, + .flags = .{}, }; } @@ -1485,7 +1793,7 @@ pub const InternalState = struct { fn isDone(this: *InternalState) bool { if (this.isChunkedEncoding()) { - return this.received_last_chunk; + return this.flags.received_last_chunk; } if (this.content_length) |content_length| { @@ -1493,43 +1801,96 @@ pub const InternalState = struct { } // Content-Type: text/event-stream we should be done only when Close/End/Timeout connection - return this.received_last_chunk; + return this.flags.received_last_chunk; } - fn decompressBytes(this: *InternalState, buffer: []const u8, body_out_str: *MutableString) !void { - log("Decompressing {d} bytes\n", .{buffer.len}); - + fn decompressBytes(this: *InternalState, buffer: []const u8, body_out_str: *MutableString, is_final_chunk: bool) !void { defer this.compressed_body.reset(); var gzip_timer: std.time.Timer = undefined; if (extremely_verbose) gzip_timer = std.time.Timer.start() catch @panic("Timer failure"); - try this.decompressor.updateBuffers(this.encoding, buffer, body_out_str); - this.decompressor.readAll(this.isDone()) catch |err| { - if (this.isDone() or error.ShortRead != err) { - Output.prettyErrorln("Decompression error: {s}", .{bun.asByteSlice(@errorName(err))}); - Output.flush(); - return err; + var still_needs_to_decompress = true; + + if (FeatureFlags.isLibdeflateEnabled()) { + // Fast-path: use libdeflate + if (is_final_chunk and !this.flags.is_libdeflate_fast_path_disabled and this.encoding.canUseLibDeflate() and this.isDone()) libdeflate: { + this.flags.is_libdeflate_fast_path_disabled = true; + + log("Decompressing {d} bytes with libdeflate\n", .{buffer.len}); + var deflater = http_thread.deflater(); + + // gzip stores the size of the uncompressed data in the last 4 bytes of the stream + // But it's only valid if the stream is less than 4.7 GB, since it's 4 bytes. + // If we know that the stream is going to be larger than our + // pre-allocated buffer, then let's dynamically allocate the exact + // size. + if (this.encoding == Encoding.gzip and buffer.len > 16 and buffer.len < 1024 * 1024 * 1024) { + const estimated_size: u32 = @bitCast(buffer[buffer.len - 4 ..][0..4].*); + // Since this is arbtirary input from the internet, let's set an upper bound of 32 MB for the allocation size. + if (estimated_size > deflater.shared_buffer.len and estimated_size < 32 * 1024 * 1024) { + try body_out_str.list.ensureTotalCapacityPrecise(body_out_str.allocator, estimated_size); + const result = deflater.decompressor.decompress(buffer, body_out_str.list.allocatedSlice(), .gzip); + + if (result.status == .success) { + body_out_str.list.items.len = result.written; + still_needs_to_decompress = false; + } + + break :libdeflate; + } + } + + const result = deflater.decompressor.decompress(buffer, &deflater.shared_buffer, switch (this.encoding) { + .gzip => .gzip, + .deflate => .deflate, + else => unreachable, + }); + + if (result.status == .success) { + try body_out_str.list.ensureTotalCapacityPrecise(body_out_str.allocator, result.written); + body_out_str.list.appendSliceAssumeCapacity(deflater.shared_buffer[0..result.written]); + still_needs_to_decompress = false; + } } - }; + } + + // Slow path, or brotli: use the .decompressor + if (still_needs_to_decompress) { + log("Decompressing {d} bytes\n", .{buffer.len}); + if (body_out_str.list.capacity == 0) { + const min = @min(@ceil(@as(f64, @floatFromInt(buffer.len)) * 1.5), @as(f64, 1024 * 1024 * 2)); + try body_out_str.growBy(@max(@as(usize, @intFromFloat(min)), 32)); + } + + try this.decompressor.updateBuffers(this.encoding, buffer, body_out_str); + + this.decompressor.readAll(this.isDone()) catch |err| { + if (this.isDone() or error.ShortRead != err) { + Output.prettyErrorln("Decompression error: {s}", .{bun.asByteSlice(@errorName(err))}); + Output.flush(); + return err; + } + }; + } if (extremely_verbose) this.gzip_elapsed = gzip_timer.read(); } - fn decompress(this: *InternalState, buffer: MutableString, body_out_str: *MutableString) !void { - try this.decompressBytes(buffer.list.items, body_out_str); + fn decompress(this: *InternalState, buffer: MutableString, body_out_str: *MutableString, is_final_chunk: bool) !void { + try this.decompressBytes(buffer.list.items, body_out_str, is_final_chunk); } - pub fn processBodyBuffer(this: *InternalState, buffer: MutableString) !bool { - if (this.is_redirect_pending) return false; + pub fn processBodyBuffer(this: *InternalState, buffer: MutableString, is_final_chunk: bool) !bool { + if (this.flags.is_redirect_pending) return false; var body_out_str = this.body_out_str.?; switch (this.encoding) { Encoding.brotli, Encoding.gzip, Encoding.deflate => { - try this.decompress(buffer, body_out_str); + try this.decompress(buffer, body_out_str, is_final_chunk); }, else => { if (!body_out_str.owns(buffer.list.items)) { @@ -1554,6 +1915,18 @@ pub const HTTPVerboseLevel = enum { curl, }; +pub const Flags = packed struct { + disable_timeout: bool = false, + disable_keepalive: bool = false, + disable_decompression: bool = false, + did_have_handshaking_error: bool = false, + force_last_modified: bool = false, + redirected: bool = false, + proxy_tunneling: bool = false, + reject_unauthorized: bool = true, + is_preconnect_only: bool = false, +}; + // TODO: reduce the size of this struct // Many of these fields can be moved to a packed struct and use less space method: Method, @@ -1567,32 +1940,25 @@ remaining_redirect_count: i8 = default_redirect_count, allow_retry: bool = false, redirect_type: FetchRedirect = FetchRedirect.follow, redirect: []u8 = &.{}, -timeout: usize = 0, progress_node: ?*Progress.Node = null, -disable_timeout: bool = false, -disable_keepalive: bool = false, -disable_decompression: bool = false, -state: InternalState = .{}, -did_have_handshaking_error: bool = false, +flags: Flags = Flags{}, + +state: InternalState = .{}, tls_props: ?*SSLConfig = null, result_callback: HTTPClientResult.Callback = undefined, /// Some HTTP servers (such as npm) report Last-Modified times but ignore If-Modified-Since. /// This is a workaround for that. -force_last_modified: bool = false, if_modified_since: string = "", request_content_len_buf: ["-4294967295".len]u8 = undefined, http_proxy: ?URL = null, proxy_authorization: ?[]u8 = null, -proxy_tunneling: bool = false, -proxy_tunnel: ?ProxyTunnel = null, +proxy_tunnel: ?*ProxyTunnel = null, signals: Signals = .{}, async_http_id: u32 = 0, hostname: ?[]u8 = null, -reject_unauthorized: bool = true, - unix_socket_path: JSC.ZigString.Slice = JSC.ZigString.Slice.empty, pub fn deinit(this: *HTTPClient) void { @@ -1605,8 +1971,8 @@ pub fn deinit(this: *HTTPClient) void { this.proxy_authorization = null; } if (this.proxy_tunnel) |tunnel| { - tunnel.deinit(); this.proxy_tunnel = null; + tunnel.detachAndDeref(); } this.unix_socket_path.deinit(); this.unix_socket_path = JSC.ZigString.Slice.empty; @@ -1622,7 +1988,7 @@ pub fn isKeepAlivePossible(this: *HTTPClient) bool { } //check state - if (this.state.allow_keepalive and !this.disable_keepalive) return true; + if (this.state.flags.allow_keepalive and !this.flags.disable_keepalive) return true; } return false; } @@ -1673,6 +2039,13 @@ pub const Encoding = enum { brotli, chunked, + pub fn canUseLibDeflate(this: Encoding) bool { + return switch (this) { + .gzip, .deflate => true, + else => false, + }; + } + pub fn isCompressed(this: Encoding) bool { return switch (this) { .brotli, .gzip, .deflate => true, @@ -1745,14 +2118,14 @@ pub const AsyncHTTP = struct { task: ThreadPool.Task = ThreadPool.Task{ .callback = &startAsyncHTTP }, result_callback: HTTPClientResult.Callback = undefined, - /// Timeout in nanoseconds - timeout: usize = 0, redirected: bool = false, response_encoding: Encoding = Encoding.identity, verbose: HTTPVerboseLevel = .none, client: HTTPClient = undefined, + waitingDeffered: bool = false, + finalized: bool = false, err: ?anyerror = null, async_http_id: u32 = 0, @@ -1834,6 +2207,51 @@ pub const AsyncHTTP = struct { tls_props: ?*SSLConfig = null, }; + const Preconnect = struct { + async_http: AsyncHTTP, + response_buffer: MutableString, + url: bun.URL, + is_url_owned: bool, + + pub usingnamespace bun.New(@This()); + + pub fn onResult(this: *Preconnect, _: *AsyncHTTP, _: HTTPClientResult) void { + this.response_buffer.deinit(); + this.async_http.clearData(); + this.async_http.client.deinit(); + if (this.is_url_owned) { + bun.default_allocator.free(this.url.href); + } + + this.destroy(); + } + }; + + pub fn preconnect( + url: URL, + is_url_owned: bool, + ) void { + if (!FeatureFlags.is_fetch_preconnect_supported) { + if (is_url_owned) { + bun.default_allocator.free(url.href); + } + + return; + } + + var this = Preconnect.new(.{ + .async_http = undefined, + .response_buffer = MutableString{ .allocator = default_allocator, .list = .{} }, + .url = url, + .is_url_owned = is_url_owned, + }); + + this.async_http = AsyncHTTP.init(bun.default_allocator, .GET, url, .{}, "", &this.response_buffer, "", HTTPClientResult.Callback.New(*Preconnect, Preconnect.onResult).init(this), .manual, .{}); + this.async_http.client.flags.is_preconnect_only = true; + + http_thread.schedule(Batch.from(&this.async_http.task)); + } + pub fn init( allocator: std.mem.Allocator, method: Method, @@ -1842,7 +2260,6 @@ pub const AsyncHTTP = struct { headers_buf: string, response_buffer: *MutableString, request_body: []const u8, - timeout: usize, callback: HTTPClientResult.Callback, redirect_type: FetchRedirect, options: Options, @@ -1859,7 +2276,6 @@ pub const AsyncHTTP = struct { .http_proxy = options.http_proxy, .signals = options.signals orelse .{}, .async_http_id = if (options.signals != null and options.signals.?.aborted != null) async_http_id.fetchAdd(1, .monotonic) else 0, - .timeout = timeout, }; this.client = .{ @@ -1871,7 +2287,6 @@ pub const AsyncHTTP = struct { .hostname = options.hostname, .signals = options.signals orelse this.signals, .async_http_id = this.async_http_id, - .timeout = timeout, .http_proxy = this.http_proxy, .redirect_type = redirect_type, }; @@ -1880,19 +2295,19 @@ pub const AsyncHTTP = struct { this.client.unix_socket_path = val; } if (options.disable_timeout) |val| { - this.client.disable_timeout = val; + this.client.flags.disable_timeout = val; } if (options.verbose) |val| { this.client.verbose = val; } if (options.disable_decompression) |val| { - this.client.disable_decompression = val; + this.client.flags.disable_decompression = val; } if (options.disable_keepalive) |val| { - this.client.disable_keepalive = val; + this.client.flags.disable_keepalive = val; } if (options.reject_unauthorized) |val| { - this.client.reject_unauthorized = val; + this.client.flags.reject_unauthorized = val; } if (options.tls_props) |val| { this.client.tls_props = val; @@ -1957,24 +2372,21 @@ pub const AsyncHTTP = struct { return this; } - pub fn initSync(allocator: std.mem.Allocator, method: Method, url: URL, headers: Headers.Entries, headers_buf: string, response_buffer: *MutableString, request_body: []const u8, timeout: usize, http_proxy: ?URL, hostname: ?[]u8, redirect_type: FetchRedirect) AsyncHTTP { - return @This().init(allocator, method, url, headers, headers_buf, response_buffer, request_body, timeout, undefined, redirect_type, .{ + pub fn initSync(allocator: std.mem.Allocator, method: Method, url: URL, headers: Headers.Entries, headers_buf: string, response_buffer: *MutableString, request_body: []const u8, http_proxy: ?URL, hostname: ?[]u8, redirect_type: FetchRedirect) AsyncHTTP { + return @This().init(allocator, method, url, headers, headers_buf, response_buffer, request_body, undefined, redirect_type, .{ .http_proxy = http_proxy, .hostname = hostname, }); } fn reset(this: *AsyncHTTP) !void { - const timeout = this.timeout; const aborted = this.client.aborted; this.client = try HTTPClient.init(this.allocator, this.method, this.client.url, this.client.header_entries, this.client.header_buf, aborted); - this.client.timeout = timeout; this.client.http_proxy = this.http_proxy; - this.timeout = timeout; if (this.http_proxy) |proxy| { //TODO: need to understand how is possible to reuse Proxy with TSL, so disable keepalive if url is HTTPS - this.client.disable_keepalive = this.url.isHTTPS(); + this.client.flags.disable_keepalive = this.url.isHTTPS(); // Username between 0 and 4096 chars if (proxy.username.len > 0 and proxy.username.len < 4096) { // Password between 0 and 4096 chars @@ -2045,7 +2457,7 @@ pub const AsyncHTTP = struct { } pub fn sendSync(this: *AsyncHTTP, comptime _: bool) anyerror!picohttp.Response { - try HTTPThread.init(); + HTTPThread.init(); var ctx = try bun.default_allocator.create(SingleHTTPChannel); ctx.* = SingleHTTPChannel.init(); @@ -2075,7 +2487,7 @@ pub const AsyncHTTP = struct { // TODO: this condition seems wrong: if we started with a non-default value, we might // report a redirect even if none happened - this.redirected = this.client.remaining_redirect_count != default_redirect_count; + this.redirected = this.client.flags.redirected; if (result.isSuccess()) { this.err = null; if (result.metadata) |metadata| { @@ -2088,12 +2500,23 @@ pub const AsyncHTTP = struct { this.state.store(State.fail, .monotonic); } + if (comptime Environment.enable_logs) { + if (socket_async_http_abort_tracker.count() > 0) { + log("socket_async_http_abort_tracker count: {d}", .{socket_async_http_abort_tracker.count()}); + } + } + + if (socket_async_http_abort_tracker.capacity() > 10_000 and socket_async_http_abort_tracker.count() < 100) { + socket_async_http_abort_tracker.shrinkAndFree(socket_async_http_abort_tracker.count()); + } + if (result.has_more) { callback.function(callback.ctx, async_http, result); } else { { this.client.deinit(); - defer default_allocator.destroy(this); + var threadlocal_http: *ThreadlocalAsyncHTTP = @fieldParentPtr("async_http", async_http); + defer threadlocal_http.destroy(); log("onAsyncHTTPCallback: {any}", .{bun.fmt.fmtDuration(this.elapsed)}); callback.function(callback.ctx, async_http, result); } @@ -2102,8 +2525,8 @@ pub const AsyncHTTP = struct { assert(active_requests > 0); } - if (AsyncHTTP.active_requests_count.load(.monotonic) < AsyncHTTP.max_simultaneous_requests.load(.monotonic)) { - http_thread.drainEvents(); + if (!http_thread.queued_tasks.isEmpty() and AsyncHTTP.active_requests_count.load(.monotonic) < AsyncHTTP.max_simultaneous_requests.load(.monotonic)) { + http_thread.loop.loop.wakeup(); } } @@ -2125,8 +2548,6 @@ pub const AsyncHTTP = struct { this.response_buffer.allocator = default_allocator; } this.client.start(this.request_body, this.response_buffer); - - log("onStart: {any}", .{bun.fmt.fmtDuration(this.elapsed)}); } }; @@ -2154,7 +2575,7 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request { hashHeaderConst("Content-Length"), => continue, hashHeaderConst("if-modified-since") => { - if (this.force_last_modified and this.if_modified_since.len == 0) { + if (this.flags.force_last_modified and this.if_modified_since.len == 0) { this.if_modified_since = this.headerStr(header_values[i]); } }, @@ -2215,7 +2636,7 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request { header_count += 1; } - if (!override_accept_encoding and !this.disable_decompression) { + if (!override_accept_encoding and !this.flags.disable_decompression) { request_headers_buf[header_count] = accept_encoding_header; header_count += 1; @@ -2237,30 +2658,40 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request { }; } -pub fn doRedirect(this: *HTTPClient, comptime is_ssl: bool, ctx: *NewHTTPContext(is_ssl), socket: NewHTTPContext(is_ssl).HTTPSocket) void { - this.unix_socket_path.deinit(); - this.unix_socket_path = JSC.ZigString.Slice.empty; - +pub fn doRedirect( + this: *HTTPClient, + comptime is_ssl: bool, + ctx: *NewHTTPContext(is_ssl), + socket: NewHTTPContext(is_ssl).HTTPSocket, +) void { + this.unix_socket_path.deinit(); + this.unix_socket_path = JSC.ZigString.Slice.empty; + const request_body = if (this.state.flags.resend_request_body_on_redirect and this.state.original_request_body == .bytes) + this.state.original_request_body.bytes + else + ""; + this.state.response_message_buffer.deinit(); + + const body_out_str = this.state.body_out_str.?; + this.remaining_redirect_count -|= 1; + this.flags.redirected = true; + assert(this.redirect_type == FetchRedirect.follow); + this.unregisterAbortTracker(); + // we need to clean the client reference before closing the socket because we are going to reuse the same ref in a another request - socket.ext(**anyopaque).* = bun.cast(**anyopaque, NewHTTPContext(is_ssl).ActiveSocket.init(&dead_socket).ptr()); if (this.isKeepAlivePossible()) { assert(this.connected_url.hostname.len > 0); ctx.releaseSocket( socket, - this.did_have_handshaking_error and !this.reject_unauthorized, + this.flags.did_have_handshaking_error and !this.flags.reject_unauthorized, this.connected_url.hostname, this.connected_url.getPortAuto(), ); } else { - NewHTTPContext(is_ssl).markSocketAsDead(socket); - socket.close(.normal); + NewHTTPContext(is_ssl).closeSocket(socket); } - this.connected_url = URL{}; - const body_out_str = this.state.body_out_str.?; - this.remaining_redirect_count -|= 1; - assert(this.redirect_type == FetchRedirect.follow); // TODO: should this check be before decrementing the redirect count? // the current logic will allow one less redirect than requested @@ -2270,16 +2701,13 @@ pub fn doRedirect(this: *HTTPClient, comptime is_ssl: bool, ctx: *NewHTTPContext } this.state.reset(this.allocator); // also reset proxy to redirect - this.proxy_tunneling = false; - if (this.proxy_tunnel != null) { - var tunnel = this.proxy_tunnel.?; - tunnel.deinit(); + this.flags.proxy_tunneling = false; + if (this.proxy_tunnel) |tunnel| { this.proxy_tunnel = null; + tunnel.detachAndDeref(); } - if (this.signals.aborted != null) { - _ = socket_async_http_abort_tracker.swapRemove(this.async_http_id); - } - return this.start(.{ .bytes = "" }, body_out_str); + + return this.start(.{ .bytes = request_body }, body_out_str); } pub fn isHTTPS(this: *HTTPClient) bool { if (this.http_proxy) |proxy| { @@ -2315,7 +2743,7 @@ fn start_(this: *HTTPClient, comptime is_ssl: bool) void { // Aborted before connecting if (this.signals.get(.aborted)) { - this.fail(error.Aborted); + this.fail(error.AbortedBeforeConnecting); return; } @@ -2327,8 +2755,8 @@ fn start_(this: *HTTPClient, comptime is_ssl: bool) void { }; if (socket.isClosed() and (this.state.response_stage != .done and this.state.response_stage != .fail)) { + NewHTTPContext(is_ssl).markSocketAsDead(socket); this.fail(error.ConnectionClosed); - assert(this.state.fail != null); return; } } @@ -2369,12 +2797,42 @@ fn printResponse(response: picohttp.Response) void { Output.flush(); } +pub fn onPreconnect(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { + log("onPreconnect({})", .{this.url}); + this.unregisterAbortTracker(); + const ctx = if (comptime is_ssl) &http_thread.https_context else &http_thread.http_context; + ctx.releaseSocket( + socket, + this.flags.did_have_handshaking_error and !this.flags.reject_unauthorized, + this.url.hostname, + this.url.getPortAuto(), + ); + + this.state.reset(this.allocator); + this.state.response_stage = .done; + this.state.request_stage = .done; + this.state.stage = .done; + this.flags.proxy_tunneling = false; + this.result_callback.run(@fieldParentPtr("client", this), HTTPClientResult{ .fail = null, .metadata = null, .has_more = false }); +} + pub fn onWritable(this: *HTTPClient, comptime is_first_call: bool, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { if (this.signals.get(.aborted)) { this.closeAndAbort(is_ssl, socket); return; } + if (comptime FeatureFlags.is_fetch_preconnect_supported) { + if (this.flags.is_preconnect_only) { + this.onPreconnect(is_ssl, socket); + return; + } + } + + if (this.proxy_tunnel) |proxy| { + proxy.onWritable(is_ssl, socket); + } + switch (this.state.request_stage) { .pending, .headers => { var stack_fallback = std.heap.stackFallback(16384, default_allocator); @@ -2391,7 +2849,7 @@ pub fn onWritable(this: *HTTPClient, comptime is_first_call: bool, comptime is_s if (this.url.isHTTPS()) { //DO the tunneling! - this.proxy_tunneling = true; + this.flags.proxy_tunneling = true; writeProxyConnect(@TypeOf(writer), writer, this) catch { this.closeAndFail(error.OutOfMemory, is_ssl, socket); return; @@ -2422,7 +2880,7 @@ pub fn onWritable(this: *HTTPClient, comptime is_first_call: bool, comptime is_s const headers_len = list.items.len; assert(list.items.len == writer.context.items.len); - if (this.state.request_body.len > 0 and list.capacity - list.items.len > 0 and !this.proxy_tunneling) { + if (this.state.request_body.len > 0 and list.capacity - list.items.len > 0 and !this.flags.proxy_tunneling) { var remain = list.items.ptr[list.items.len..list.capacity]; const wrote = @min(remain.len, this.state.request_body.len); assert(wrote > 0); @@ -2455,7 +2913,7 @@ pub fn onWritable(this: *HTTPClient, comptime is_first_call: bool, comptime is_s const has_sent_headers = this.state.request_sent_len >= headers_len; if (has_sent_headers and this.verbose != .none) { - printRequest(request, this.url.href, !this.reject_unauthorized, this.state.request_body, this.verbose == .curl); + printRequest(request, this.url.href, !this.flags.reject_unauthorized, this.state.request_body, this.verbose == .curl); } if (has_sent_headers and this.state.request_body.len > 0) { @@ -2468,12 +2926,16 @@ pub fn onWritable(this: *HTTPClient, comptime is_first_call: bool, comptime is_s false; if (has_sent_headers and has_sent_body) { - this.state.request_stage = .done; + if (this.flags.proxy_tunneling) { + this.state.request_stage = .proxy_handshake; + } else { + this.state.request_stage = .body; + } return; } if (has_sent_headers) { - if (this.proxy_tunneling) { + if (this.flags.proxy_tunneling) { this.state.request_stage = .proxy_handshake; } else { this.state.request_stage = .body; @@ -2537,180 +2999,107 @@ pub fn onWritable(this: *HTTPClient, comptime is_first_call: bool, comptime is_s if (this.state.original_request_body != .bytes) { @panic("sendfile is only supported without SSL. This code should never have been reached!"); } - var proxy = this.proxy_tunnel orelse return; - - this.setTimeout(socket, 5); - - const to_send = this.state.request_body; - const amount = proxy.ssl.write(to_send) catch |err| { - if (err == error.WantWrite) //just wait and retry when onWritable! - return; + if (this.proxy_tunnel) |proxy| { + this.setTimeout(socket, 5); - this.closeAndFail(error.WriteFailed, is_ssl, socket); - return; - }; + const to_send = this.state.request_body; + const amount = proxy.writeData(to_send) catch return; // just wait and retry when onWritable! if closed internally will call proxy.onClose - this.state.request_sent_len += @as(usize, @intCast(amount)); - this.state.request_body = this.state.request_body[@as(usize, @intCast(amount))..]; + this.state.request_sent_len += @as(usize, @intCast(amount)); + this.state.request_body = this.state.request_body[@as(usize, @intCast(amount))..]; - if (this.state.request_body.len == 0) { - this.state.request_stage = .done; - return; + if (this.state.request_body.len == 0) { + this.state.request_stage = .done; + return; + } } }, .proxy_headers => { - const proxy = this.proxy_tunnel orelse return; - - this.setTimeout(socket, 5); - var stack_fallback = std.heap.stackFallback(16384, default_allocator); - const allocator = stack_fallback.get(); - var list = std.ArrayList(u8).initCapacity(allocator, stack_fallback.buffer.len) catch unreachable; - defer if (list.capacity > stack_fallback.buffer.len) list.deinit(); - const writer = &list.writer(); - - const request = this.buildRequest(this.state.request_body.len); - writeRequest( - @TypeOf(writer), - writer, - request, - ) catch { - this.closeAndFail(error.OutOfMemory, is_ssl, socket); - return; - }; - - const headers_len = list.items.len; - assert(list.items.len == writer.context.items.len); - if (this.state.request_body.len > 0 and list.capacity - list.items.len > 0) { - var remain = list.items.ptr[list.items.len..list.capacity]; - const wrote = @min(remain.len, this.state.request_body.len); - assert(wrote > 0); - @memcpy(remain[0..wrote], this.state.request_body[0..wrote]); - list.items.len += wrote; - } - - const to_send = list.items[this.state.request_sent_len..]; - if (comptime Environment.allow_assert) { - assert(!socket.isShutdown()); - assert(!socket.isClosed()); - } + if (this.proxy_tunnel) |proxy| { + this.setTimeout(socket, 5); + var stack_fallback = std.heap.stackFallback(16384, default_allocator); + const allocator = stack_fallback.get(); + var list = std.ArrayList(u8).initCapacity(allocator, stack_fallback.buffer.len) catch unreachable; + defer if (list.capacity > stack_fallback.buffer.len) list.deinit(); + const writer = &list.writer(); - const amount = proxy.ssl.write(to_send) catch |err| { - if (err == error.WantWrite) //just wait and retry when onWritable! + const request = this.buildRequest(this.state.request_body.len); + writeRequest( + @TypeOf(writer), + writer, + request, + ) catch { + this.closeAndFail(error.OutOfMemory, is_ssl, socket); return; + }; - this.closeAndFail(error.WriteFailed, is_ssl, socket); - return; - }; + const headers_len = list.items.len; + assert(list.items.len == writer.context.items.len); + if (this.state.request_body.len > 0 and list.capacity - list.items.len > 0) { + var remain = list.items.ptr[list.items.len..list.capacity]; + const wrote = @min(remain.len, this.state.request_body.len); + assert(wrote > 0); + @memcpy(remain[0..wrote], this.state.request_body[0..wrote]); + list.items.len += wrote; + } - if (comptime is_first_call) { - if (amount == 0) { - // don't worry about it - return; + const to_send = list.items[this.state.request_sent_len..]; + if (comptime Environment.allow_assert) { + assert(!socket.isShutdown()); + assert(!socket.isClosed()); } - } + const amount = proxy.writeData(to_send) catch return; // just wait and retry when onWritable! if closed internally will call proxy.onClose - this.state.request_sent_len += @as(usize, @intCast(amount)); - const has_sent_headers = this.state.request_sent_len >= headers_len; + if (comptime is_first_call) { + if (amount == 0) { + // don't worry about it + return; + } + } - if (has_sent_headers and this.state.request_body.len > 0) { - this.state.request_body = this.state.request_body[this.state.request_sent_len - headers_len ..]; - } + this.state.request_sent_len += @as(usize, @intCast(amount)); + const has_sent_headers = this.state.request_sent_len >= headers_len; + + if (has_sent_headers and this.state.request_body.len > 0) { + this.state.request_body = this.state.request_body[this.state.request_sent_len - headers_len ..]; + } - const has_sent_body = this.state.request_body.len == 0; + const has_sent_body = this.state.request_body.len == 0; - if (has_sent_headers and has_sent_body) { - this.state.request_stage = .done; - return; - } + if (has_sent_headers and has_sent_body) { + this.state.request_stage = .done; + return; + } - if (has_sent_headers) { - this.state.request_stage = .proxy_body; - assert(this.state.request_body.len > 0); + if (has_sent_headers) { + this.state.request_stage = .proxy_body; + assert(this.state.request_body.len > 0); - // we sent everything, but there's some body leftover - if (amount == @as(c_int, @intCast(to_send.len))) { - this.onWritable(false, is_ssl, socket); + // we sent everything, but there's some body leftover + if (amount == @as(c_int, @intCast(to_send.len))) { + this.onWritable(false, is_ssl, socket); + } + } else { + this.state.request_stage = .proxy_headers; } - } else { - this.state.request_stage = .proxy_headers; } }, - else => { - //Just check if need to call SSL_read if requested to be writable - var proxy = this.proxy_tunnel orelse return; - this.setTimeout(socket, 5); - var data = proxy.getSSLData(null) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - if (data.partial) return; - //only deinit if is not partial - defer data.deinit(); - const decoded_data = data.slice(); - if (decoded_data.len == 0) return; - this.onData(is_ssl, decoded_data, if (comptime is_ssl) &http_thread.https_context else &http_thread.http_context, socket); - }, + else => {}, } } pub fn closeAndFail(this: *HTTPClient, err: anyerror, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { - if (this.state.stage != .fail and this.state.stage != .done) { - log("closeAndFail: {s}", .{@errorName(err)}); - if (!socket.isClosed()) { - socket.ext(**anyopaque).* = bun.cast(**anyopaque, NewHTTPContext(is_ssl).ActiveSocket.init(&dead_socket).ptr()); - socket.close(.failure); - } - this.fail(err); + log("closeAndFail: {s}", .{@errorName(err)}); + if (!socket.isClosed()) { + NewHTTPContext(is_ssl).terminateSocket(socket); } + this.fail(err); } -fn startProxySendHeaders(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { - this.state.response_stage = .proxy_headers; - this.state.request_stage = .proxy_headers; - this.state.request_sent_len = 0; - this.onWritable(true, is_ssl, socket); -} - -fn retryProxyHandshake(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { - const proxy = this.proxy_tunnel orelse return; - if (proxy.ssl.isInitFinished()) { - this.startProxySendHeaders(is_ssl, socket); - return; - } - proxy.ssl.handshake() catch |err| { - switch (err) { - error.WantWrite, error.WantRead => { - return; - }, - else => { - log("Error performing SSL handshake with host through proxy {any}\n", .{err}); - this.closeAndFail(err, is_ssl, socket); - return; - }, - } - }; - this.startProxySendHeaders(is_ssl, socket); -} fn startProxyHandshake(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket) void { - this.state.response_stage = .proxy_handshake; - this.state.request_stage = .proxy_handshake; - const proxy = ProxyTunnel.init(is_ssl, this, socket); - this.proxy_tunnel = proxy; - - proxy.ssl.handshake() catch |err| { - switch (err) { - error.WantWrite, error.WantRead => { - //Wait and Pull - return; - }, - else => { - log("Error performing SSL handshake with host through proxy {any}\n", .{err}); - this.closeAndFail(err, is_ssl, socket); - return; - }, - } - }; - this.startProxySendHeaders(is_ssl, socket); + // if we have options we pass them (ca, reject_unauthorized, etc) otherwise use the default + const ssl_options = if (this.tls_props != null) this.tls_props.?.* else JSC.API.ServerConfig.SSLConfig.zero; + ProxyTunnel.start(this, is_ssl, socket, ssl_options); } inline fn handleShortRead( @@ -2731,170 +3120,167 @@ inline fn handleShortRead( this.setTimeout(socket, 5); } -pub fn onData(this: *HTTPClient, comptime is_ssl: bool, incoming_data: []const u8, ctx: *NewHTTPContext(is_ssl), socket: NewHTTPContext(is_ssl).HTTPSocket) void { - log("onData {}", .{incoming_data.len}); - if (this.signals.get(.aborted)) { - this.closeAndAbort(is_ssl, socket); +pub fn handleOnDataHeaders( + this: *HTTPClient, + comptime is_ssl: bool, + incoming_data: []const u8, + ctx: *NewHTTPContext(is_ssl), + socket: NewHTTPContext(is_ssl).HTTPSocket, +) void { + var to_read = incoming_data; + var amount_read: usize = 0; + var needs_move = true; + if (this.state.response_message_buffer.list.items.len > 0) { + // this one probably won't be another chunk, so we use appendSliceExact() to avoid over-allocating + this.state.response_message_buffer.appendSliceExact(incoming_data) catch bun.outOfMemory(); + to_read = this.state.response_message_buffer.list.items; + needs_move = false; + } + + // we reset the pending_response each time wich means that on parse error this will be always be empty + this.state.pending_response = picohttp.Response{}; + + // minimal http/1.1 request size is 16 bytes without headers and 26 with Host header + // if is less than 16 will always be a ShortRead + if (to_read.len < 16) { + this.handleShortRead(is_ssl, incoming_data, socket, needs_move); return; } - switch (this.state.response_stage) { - .pending, .headers, .proxy_decoded_headers => { - var to_read = incoming_data; - var amount_read: usize = 0; - var needs_move = true; - if (this.state.response_message_buffer.list.items.len > 0) { - // this one probably won't be another chunk, so we use appendSliceExact() to avoid over-allocating - this.state.response_message_buffer.appendSliceExact(incoming_data) catch bun.outOfMemory(); - to_read = this.state.response_message_buffer.list.items; - needs_move = false; - } - - // we reset the pending_response each time wich means that on parse error this will be always be empty - this.state.pending_response = picohttp.Response{}; - - // minimal http/1.1 request size is 16 bytes without headers and 26 with Host header - // if is less than 16 will always be a ShortRead - if (to_read.len < 16) { + + var response = picohttp.Response.parseParts( + to_read, + &shared_response_headers_buf, + &amount_read, + ) catch |err| { + switch (err) { + error.ShortRead => { this.handleShortRead(is_ssl, incoming_data, socket, needs_move); - return; - } + }, + else => { + this.closeAndFail(err, is_ssl, socket); + }, + } + return; + }; - var response = picohttp.Response.parseParts( - to_read, - &shared_response_headers_buf, - &amount_read, - ) catch |err| { - switch (err) { - error.ShortRead => { - this.handleShortRead(is_ssl, incoming_data, socket, needs_move); - }, - else => { - this.closeAndFail(err, is_ssl, socket); - }, - } - return; - }; + // we save the successful parsed response + this.state.pending_response = response; + + const body_buf = to_read[@min(@as(usize, @intCast(response.bytes_read)), to_read.len)..]; + // handle the case where we have a 100 Continue + if (response.status_code == 100) { + // we still can have the 200 OK in the same buffer sometimes + if (body_buf.len > 0) { + this.onData(is_ssl, body_buf, ctx, socket); + } + return; + } + const should_continue = this.handleResponseMetadata( + &response, + ) catch |err| { + this.closeAndFail(err, is_ssl, socket); + return; + }; - // we save the successful parsed response - this.state.pending_response = response; + if (this.state.content_encoding_i < response.headers.len and !this.state.flags.did_set_content_encoding) { + // if it compressed with this header, it is no longer because we will decompress it + const mutable_headers = std.ArrayListUnmanaged(picohttp.Header){ .items = response.headers, .capacity = response.headers.len }; + this.state.flags.did_set_content_encoding = true; + response.headers = mutable_headers.items; + this.state.content_encoding_i = std.math.maxInt(@TypeOf(this.state.content_encoding_i)); + // we need to reset the pending response because we removed a header + this.state.pending_response = response; + } - const body_buf = to_read[@min(@as(usize, @intCast(response.bytes_read)), to_read.len)..]; - // handle the case where we have a 100 Continue - if (response.status_code == 100) { - // we still can have the 200 OK in the same buffer sometimes - if (body_buf.len > 0) { - this.onData(is_ssl, body_buf, ctx, socket); - } - return; - } + if (should_continue == .finished) { + if (this.state.flags.is_redirect_pending) { + this.doRedirect(is_ssl, ctx, socket); + return; + } + // this means that the request ended + // clone metadata and return the progress at this point + this.cloneMetadata(); + // if is chuncked but no body is expected we mark the last chunk + this.state.flags.received_last_chunk = true; + // if is not we ignore the content_length + this.state.content_length = 0; + this.progressUpdate(is_ssl, ctx, socket); + return; + } + + if (this.flags.proxy_tunneling and this.proxy_tunnel == null) { + // we are proxing we dont need to cloneMetadata yet + this.startProxyHandshake(is_ssl, socket); + return; + } + + // we have body data incoming so we clone metadata and keep going + this.cloneMetadata(); + + if (body_buf.len == 0) { + // no body data yet, but we can report the headers + if (this.signals.get(.header_progress)) { + this.progressUpdate(is_ssl, ctx, socket); + } + return; + } - const should_continue = this.handleResponseMetadata( - &response, - ) catch |err| { + if (this.state.response_stage == .body) { + { + const report_progress = this.handleResponseBody(body_buf, true) catch |err| { this.closeAndFail(err, is_ssl, socket); return; }; - if (this.state.content_encoding_i < response.headers.len and !this.state.did_set_content_encoding) { - // if it compressed with this header, it is no longer because we will decompress it - const mutable_headers = std.ArrayListUnmanaged(picohttp.Header){ .items = response.headers, .capacity = response.headers.len }; - this.state.did_set_content_encoding = true; - response.headers = mutable_headers.items; - this.state.content_encoding_i = std.math.maxInt(@TypeOf(this.state.content_encoding_i)); - // we need to reset the pending response because we removed a header - this.state.pending_response = response; - } - - if (should_continue == .finished) { - if (this.state.is_redirect_pending) { - this.doRedirect(is_ssl, ctx, socket); - return; - } - // this means that the request ended - // clone metadata and return the progress at this point - this.cloneMetadata(); - // if is chuncked but no body is expected we mark the last chunk - this.state.received_last_chunk = true; - // if is not we ignore the content_length - this.state.content_length = 0; + if (report_progress) { this.progressUpdate(is_ssl, ctx, socket); return; } - - if (this.proxy_tunneling and this.proxy_tunnel == null) { - // we are proxing we dont need to cloneMetadata yet - this.startProxyHandshake(is_ssl, socket); + } + } else if (this.state.response_stage == .body_chunk) { + this.setTimeout(socket, 5); + { + const report_progress = this.handleResponseBodyChunkedEncoding(body_buf) catch |err| { + this.closeAndFail(err, is_ssl, socket); return; - } - - // we have body data incoming so we clone metadata and keep going - this.cloneMetadata(); + }; - if (body_buf.len == 0) { - // no body data yet, but we can report the headers - if (this.signals.get(.header_progress)) { - this.progressUpdate(is_ssl, ctx, socket); - } + if (report_progress) { + this.progressUpdate(is_ssl, ctx, socket); return; } + } + } - if (this.state.response_stage == .body) { - { - const report_progress = this.handleResponseBody(body_buf, true) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - - if (report_progress) { - this.progressUpdate(is_ssl, ctx, socket); - return; - } - } - } else if (this.state.response_stage == .body_chunk) { - this.setTimeout(socket, 5); - { - const report_progress = this.handleResponseBodyChunkedEncoding(body_buf) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - - if (report_progress) { - this.progressUpdate(is_ssl, ctx, socket); - return; - } - } - } + // if not reported we report partially now + if (this.signals.get(.header_progress)) { + this.progressUpdate(is_ssl, ctx, socket); + return; + } +} +pub fn onData( + this: *HTTPClient, + comptime is_ssl: bool, + incoming_data: []const u8, + ctx: *NewHTTPContext(is_ssl), + socket: NewHTTPContext(is_ssl).HTTPSocket, +) void { + log("onData {}", .{incoming_data.len}); + if (this.signals.get(.aborted)) { + this.closeAndAbort(is_ssl, socket); + return; + } - // if not reported we report partially now - if (this.signals.get(.header_progress)) { - this.progressUpdate(is_ssl, ctx, socket); - return; - } + switch (this.state.response_stage) { + .pending, .headers => { + this.handleOnDataHeaders(is_ssl, incoming_data, ctx, socket); }, - .body => { this.setTimeout(socket, 5); - if (this.proxy_tunnel != null) { - var proxy = this.proxy_tunnel.?; - var data = proxy.getSSLData(incoming_data) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - if (data.partial) return; - defer data.deinit(); - const decoded_data = data.slice(); - if (decoded_data.len == 0) return; - const report_progress = this.handleResponseBody(decoded_data, false) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - - if (report_progress) { - this.progressUpdate(is_ssl, ctx, socket); - return; - } + if (this.proxy_tunnel) |proxy| { + proxy.receiveData(incoming_data); } else { const report_progress = this.handleResponseBody(incoming_data, false) catch |err| { this.closeAndFail(err, is_ssl, socket); @@ -2911,26 +3297,8 @@ pub fn onData(this: *HTTPClient, comptime is_ssl: bool, incoming_data: []const u .body_chunk => { this.setTimeout(socket, 5); - if (this.proxy_tunnel != null) { - var proxy = this.proxy_tunnel.?; - var data = proxy.getSSLData(incoming_data) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - if (data.partial) return; - defer data.deinit(); - const decoded_data = data.slice(); - if (decoded_data.len == 0) return; - - const report_progress = this.handleResponseBodyChunkedEncoding(decoded_data) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - - if (report_progress) { - this.progressUpdate(is_ssl, ctx, socket); - return; - } + if (this.proxy_tunnel) |proxy| { + proxy.receiveData(incoming_data); } else { const report_progress = this.handleResponseBodyChunkedEncoding(incoming_data) catch |err| { this.closeAndFail(err, is_ssl, socket); @@ -2945,32 +3313,11 @@ pub fn onData(this: *HTTPClient, comptime is_ssl: bool, incoming_data: []const u }, .fail => {}, - .proxy_headers => { - this.setTimeout(socket, 5); - var proxy = this.proxy_tunnel orelse return; - var data = proxy.getSSLData(incoming_data) catch |err| { - this.closeAndFail(err, is_ssl, socket); - return; - }; - if (data.partial) return; - //only deinit if is not partial - defer data.deinit(); - const decoded_data = data.slice(); - if (decoded_data.len == 0) return; - this.proxy_tunneling = false; - this.state.response_stage = .proxy_decoded_headers; - //actual do the header parsing! - this.onData(is_ssl, decoded_data, ctx, socket); - }, - .proxy_handshake => { + .proxy_headers, .proxy_handshake => { this.setTimeout(socket, 5); - - // put more data into SSL - const proxy = this.proxy_tunnel orelse return; - _ = proxy.in_bio.write(incoming_data) catch 0; - - //retry again! - this.retryProxyHandshake(is_ssl, socket); + if (this.proxy_tunnel) |proxy| { + proxy.receiveData(incoming_data); + } return; }, else => { @@ -2986,21 +3333,26 @@ pub fn closeAndAbort(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPCo } fn fail(this: *HTTPClient, err: anyerror) void { - if (this.signals.aborted != null) { - _ = socket_async_http_abort_tracker.swapRemove(this.async_http_id); - } + this.unregisterAbortTracker(); - this.state.request_stage = .fail; - this.state.response_stage = .fail; - this.state.fail = err; - this.state.stage = .fail; + if (this.proxy_tunnel) |tunnel| { + this.proxy_tunnel = null; + // always detach the socket from the tunnel in case of fail + tunnel.detachAndDeref(); + } + if (this.state.stage != .done and this.state.stage != .fail) { + this.state.request_stage = .fail; + this.state.response_stage = .fail; + this.state.fail = err; + this.state.stage = .fail; - const callback = this.result_callback; - const result = this.toResult(); - this.state.reset(this.allocator); - this.proxy_tunneling = false; + const callback = this.result_callback; + const result = this.toResult(); + this.state.reset(this.allocator); + this.flags.proxy_tunneling = false; - callback.run(@fieldParentPtr("client", this), result); + callback.run(@fieldParentPtr("client", this), result); + } } // We have to clone metadata immediately after use @@ -3037,7 +3389,7 @@ fn cloneMetadata(this: *HTTPClient) void { } pub fn setTimeout(this: *HTTPClient, socket: anytype, minutes: c_uint) void { - if (this.disable_timeout) { + if (this.flags.disable_timeout) { socket.timeout(0); socket.setTimeoutMinutes(0); return; @@ -3049,42 +3401,40 @@ pub fn setTimeout(this: *HTTPClient, socket: anytype, minutes: c_uint) void { pub fn progressUpdate(this: *HTTPClient, comptime is_ssl: bool, ctx: *NewHTTPContext(is_ssl), socket: NewHTTPContext(is_ssl).HTTPSocket) void { if (this.state.stage != .done and this.state.stage != .fail) { - const out_str = this.state.body_out_str.?; - const body = out_str.*; - const result = this.toResult(); - const is_done = !result.has_more; - if (this.state.is_redirect_pending and this.state.fail == null) { + if (this.state.flags.is_redirect_pending and this.state.fail == null) { if (this.state.isDone()) { this.doRedirect(is_ssl, ctx, socket); } return; } - if (this.signals.aborted != null and is_done) { - _ = socket_async_http_abort_tracker.swapRemove(this.async_http_id); - } + const out_str = this.state.body_out_str.?; + const body = out_str.*; + const result = this.toResult(); + const is_done = !result.has_more; log("progressUpdate {}", .{is_done}); const callback = this.result_callback; if (is_done) { + this.unregisterAbortTracker(); + if (this.isKeepAlivePossible() and !socket.isClosedOrHasError()) { ctx.releaseSocket( socket, - this.did_have_handshaking_error and !this.reject_unauthorized, + this.flags.did_have_handshaking_error and !this.flags.reject_unauthorized, this.connected_url.hostname, this.connected_url.getPortAuto(), ); } else if (!socket.isClosed()) { - NewHTTPContext(is_ssl).markSocketAsDead(socket); - socket.close(.normal); + NewHTTPContext(is_ssl).closeSocket(socket); } this.state.reset(this.allocator); this.state.response_stage = .done; this.state.request_stage = .done; this.state.stage = .done; - this.proxy_tunneling = false; + this.flags.proxy_tunneling = false; } result.body.?.* = body; @@ -3105,6 +3455,8 @@ pub fn progressUpdate(this: *HTTPClient, comptime is_ssl: bool, ctx: *NewHTTPCon pub const HTTPClientResult = struct { body: ?*MutableString = null, has_more: bool = false, + redirected: bool = false, + fail: ?anyerror = null, /// Owns the response metadata aka headers, url and status code @@ -3115,9 +3467,20 @@ pub const HTTPClientResult = struct { /// If chunked encoded this will represent the total received size (ignoring the chunk headers) /// If is not chunked encoded and Content-Length is not provided this will be unknown body_size: BodySize = .unknown, - redirected: bool = false, certificate_info: ?CertificateInfo = null, + pub fn abortReason(this: *const HTTPClientResult) ?JSC.CommonAbortReason { + if (this.isTimeout()) { + return .Timeout; + } + + if (this.isAbort()) { + return .UserAbort; + } + + return null; + } + pub const BodySize = union(enum) { total_received: usize, content_length: usize, @@ -3133,7 +3496,7 @@ pub const HTTPClientResult = struct { } pub fn isAbort(this: *const HTTPClientResult) bool { - return if (this.fail) |e| e == error.Aborted else false; + return if (this.fail) |e| (e == error.Aborted or e == error.AbortedBeforeConnecting) else false; } pub const Callback = struct { @@ -3183,10 +3546,10 @@ pub fn toResult(this: *HTTPClient) HTTPClientResult { return HTTPClientResult{ .metadata = metadata, .body = this.state.body_out_str, - .redirected = this.remaining_redirect_count != default_redirect_count, + .redirected = this.flags.redirected, .fail = this.state.fail, // check if we are reporting cert errors, do not have a fail state and we are not done - .has_more = this.state.fail == null and !this.state.isDone(), + .has_more = certificate_info != null or (this.state.fail == null and !this.state.isDone()), .body_size = body_size, .certificate_info = null, }; @@ -3194,6 +3557,7 @@ pub fn toResult(this: *HTTPClient) HTTPClientResult { return HTTPClientResult{ .body = this.state.body_out_str, .metadata = null, + .redirected = this.flags.redirected, .fail = this.state.fail, // check if we are reporting cert errors, do not have a fail state and we are not done .has_more = certificate_info != null or (this.state.fail == null and !this.state.isDone()), @@ -3232,17 +3596,10 @@ fn handleResponseBodyFromSinglePacket(this: *HTTPClient, incoming_data: []const } } // we can ignore the body data in redirects - if (this.state.is_redirect_pending) return; + if (this.state.flags.is_redirect_pending) return; if (this.state.encoding.isCompressed()) { - var body_buffer = this.state.body_out_str.?; - if (body_buffer.list.capacity == 0) { - const min = @min(@ceil(@as(f64, @floatFromInt(incoming_data.len)) * 1.5), @as(f64, 1024 * 1024 * 2)); - try body_buffer.growBy(@max(@as(usize, @intFromFloat(min)), 32)); - } - - // assert(!body_buffer.owns(b)); - try this.state.decompressBytes(incoming_data, body_buffer); + try this.state.decompressBytes(incoming_data, this.state.body_out_str.?, true); } else { try this.state.getBodyBuffer().appendSliceExact(incoming_data); } @@ -3271,7 +3628,7 @@ fn handleResponseBodyFromMultiplePackets(this: *HTTPClient, incoming_data: []con } // we can ignore the body data in redirects - if (!this.state.is_redirect_pending) { + if (!this.state.flags.is_redirect_pending) { if (buffer.list.items.len == 0 and incoming_data.len < preallocate_max) { buffer.list.ensureTotalCapacityPrecise(buffer.allocator, incoming_data.len) catch {}; } @@ -3290,7 +3647,12 @@ fn handleResponseBodyFromMultiplePackets(this: *HTTPClient, incoming_data: []con // done or streaming const is_done = content_length != null and this.state.total_body_received >= content_length.?; if (is_done or this.signals.get(.body_streaming) or content_length == null) { - const processed = try this.state.processBodyBuffer(buffer.*); + const is_final_chunk = is_done; + const processed = try this.state.processBodyBuffer(buffer.*, is_final_chunk); + + // We can only use the libdeflate fast path when we are not streaming + // If we ever call processBodyBuffer again, it cannot go through the fast path. + this.state.flags.is_libdeflate_fast_path_disabled = true; if (this.progress_node) |progress| { progress.activate(); @@ -3355,16 +3717,19 @@ fn handleResponseBodyChunkedEncodingFromMultiplePackets( } // streaming chunks if (this.signals.get(.body_streaming)) { - return try this.state.processBodyBuffer(buffer); + // If we're streaming, we cannot use the libdeflate fast path + this.state.flags.is_libdeflate_fast_path_disabled = true; + return try this.state.processBodyBuffer(buffer, false); } return false; }, // Done else => { - this.state.received_last_chunk = true; + this.state.flags.received_last_chunk = true; _ = try this.state.processBodyBuffer( buffer, + true, ); if (this.progress_node) |progress| { @@ -3433,15 +3798,17 @@ fn handleResponseBodyChunkedEncodingFromSinglePacket( // streaming chunks if (this.signals.get(.body_streaming)) { - return try this.state.processBodyBuffer(body_buffer.*); + // If we're streaming, we cannot use the libdeflate fast path + this.state.flags.is_libdeflate_fast_path_disabled = true; + + return try this.state.processBodyBuffer(body_buffer.*, true); } return false; }, // Done else => { - this.state.received_last_chunk = true; - + this.state.flags.received_last_chunk = true; try this.handleResponseBodyFromSinglePacket(buffer); assert(this.state.body_out_str.?.list.items.ptr != buffer.ptr); if (this.progress_node) |progress| { @@ -3486,7 +3853,7 @@ pub fn handleResponseMetadata( } }, hashHeaderConst("Content-Encoding") => { - if (!this.disable_decompression) { + if (!this.flags.disable_decompression) { if (strings.eqlComptime(header.value, "gzip")) { this.state.encoding = Encoding.gzip; this.state.content_encoding_i = @as(u8, @truncate(header_i)); @@ -3501,15 +3868,15 @@ pub fn handleResponseMetadata( }, hashHeaderConst("Transfer-Encoding") => { if (strings.eqlComptime(header.value, "gzip")) { - if (!this.disable_decompression) { + if (!this.flags.disable_decompression) { this.state.transfer_encoding = Encoding.gzip; } } else if (strings.eqlComptime(header.value, "deflate")) { - if (!this.disable_decompression) { + if (!this.flags.disable_decompression) { this.state.transfer_encoding = Encoding.deflate; } } else if (strings.eqlComptime(header.value, "br")) { - if (!this.disable_decompression) { + if (!this.flags.disable_decompression) { this.state.transfer_encoding = .brotli; } } else if (strings.eqlComptime(header.value, "identity")) { @@ -3526,12 +3893,12 @@ pub fn handleResponseMetadata( hashHeaderConst("Connection") => { if (response.status_code >= 200 and response.status_code <= 299) { if (!strings.eqlComptime(header.value, "keep-alive")) { - this.state.allow_keepalive = false; + this.state.flags.allow_keepalive = false; } } }, hashHeaderConst("Last-Modified") => { - pretend_304 = this.force_last_modified and response.status_code > 199 and response.status_code < 300 and this.if_modified_since.len > 0 and strings.eql(this.if_modified_since, header.value); + pretend_304 = this.flags.force_last_modified and response.status_code > 199 and response.status_code < 300 and this.if_modified_since.len > 0 and strings.eql(this.if_modified_since, header.value); }, else => {}, @@ -3547,7 +3914,7 @@ pub fn handleResponseMetadata( } // Don't do this for proxies because those connections will be open for awhile. - if (!this.proxy_tunneling) { + if (!this.flags.proxy_tunneling) { // according to RFC 7230 section 3.3.3: // 1. Any response to a HEAD request and any response with a 1xx (Informational), @@ -3569,18 +3936,18 @@ pub fn handleResponseMetadata( // // but, we must only do this IF the status code allows it to contain a body. else if (this.state.content_length == null and this.state.transfer_encoding != .chunked) { - this.state.allow_keepalive = false; + this.state.flags.allow_keepalive = false; } } - if (this.proxy_tunneling and this.proxy_tunnel == null) { + if (this.flags.proxy_tunneling and this.proxy_tunnel == null) { if (response.status_code == 200) { // signal to continue the proxing return ShouldContinue.continue_streaming; } //proxy denied connection so return proxy result (407, 403 etc) - this.proxy_tunneling = false; + this.flags.proxy_tunneling = false; } const status_code = response.status_code; @@ -3785,7 +4152,10 @@ pub fn handleResponseMetadata( } } - this.state.is_redirect_pending = true; + this.state.flags.is_redirect_pending = true; + if (this.method.hasRequestBody()) { + this.state.flags.resend_request_body_on_redirect = true; + } }, else => {}, } @@ -3803,7 +4173,7 @@ pub fn handleResponseMetadata( log("handleResponseMetadata: content_length is null and transfer_encoding {}", .{this.state.transfer_encoding}); } - if (this.method.hasBody() and (content_length == null or content_length.? > 0 or !this.state.allow_keepalive or this.state.transfer_encoding == .chunked or is_server_sent_events)) { + if (this.method.hasBody() and (content_length == null or content_length.? > 0 or !this.state.flags.allow_keepalive or this.state.transfer_encoding == .chunked or is_server_sent_events)) { return ShouldContinue.continue_streaming; } else { return ShouldContinue.finished; @@ -3811,3 +4181,9 @@ pub fn handleResponseMetadata( } const assert = bun.assert; + +// Exists for heap stats reasons. +const ThreadlocalAsyncHTTP = struct { + async_http: AsyncHTTP, + pub usingnamespace bun.New(@This()); +}; diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig index 876aa53b1a541..c2c78cc093f53 100644 --- a/src/http/websocket_http_client.zig +++ b/src/http/websocket_http_client.zig @@ -346,11 +346,15 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { } pub fn cancel(this: *HTTPClient) callconv(.C) void { this.clearData(); - - var tcp = this.tcp orelse return; + this.outgoing_websocket = null; + const tcp = this.tcp orelse return; this.tcp = null; - - tcp.close(.failure); + // no need to be .failure we still wanna to send pending SSL buffer + close_notify + if (comptime ssl) { + tcp.close(.normal); + } else { + tcp.close(.failure); + } } pub fn fail(this: *HTTPClient, code: ErrorCode) void { @@ -375,7 +379,6 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { ws.didAbruptClose(ErrorCode.ended); } - this.destroy(); } @@ -388,18 +391,20 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { pub fn handleHandshake(this: *HTTPClient, socket: Socket, success: i32, ssl_error: uws.us_bun_verify_error_t) void { log("onHandshake({d})", .{success}); - const authorized = if (success == 1) true else false; + const handshake_success = if (success == 1) true else false; var reject_unauthorized = false; if (this.outgoing_websocket) |ws| { reject_unauthorized = ws.rejectUnauthorized(); } - if (ssl_error.error_no != 0 and (reject_unauthorized or !authorized)) { - this.fail(ErrorCode.tls_handshake_failed); - return; - } - if (authorized) { + if (handshake_success) { + // handshake completed but we may have ssl errors if (reject_unauthorized) { + // only reject the connection if reject_unauthorized == true + if (ssl_error.error_no != 0) { + this.fail(ErrorCode.tls_handshake_failed); + return; + } const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(socket.getNativeHandle())); if (BoringSSL.SSL_get_servername(ssl_ptr, 0)) |servername| { const hostname = servername[0..bun.len(servername)]; @@ -408,6 +413,10 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { } } } + } else { + // if we are here is because server rejected us, and the error_no is the cause of this + // if we set reject_unauthorized == false this means the server requires custom CA aka NODE_EXTRA_CA_CERTS + this.fail(ErrorCode.tls_handshake_failed); } } @@ -436,6 +445,13 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { this.to_send = this.input_body_buf[@as(usize, @intCast(wrote))..]; } + pub fn isSameSocket(this: *HTTPClient, socket: Socket) bool { + if (this.tcp) |tcp| { + return socket.socket.eq(tcp.socket); + } + return false; + } + pub fn handleData(this: *HTTPClient, socket: Socket, data: []const u8) void { log("onData", .{}); if (this.outgoing_websocket == null) { @@ -443,7 +459,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { return; } - bun.assert(socket.socket.eq(this.tcp.?.socket)); + bun.assert(this.isSameSocket(socket)); if (comptime Environment.allow_assert) bun.assert(!socket.isShutdown()); @@ -485,7 +501,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { pub fn handleEnd(this: *HTTPClient, socket: Socket) void { log("onEnd", .{}); - bun.assert(socket.socket.eq(this.tcp.?.socket)); + bun.assert(this.isSameSocket(socket)); this.terminate(ErrorCode.ended); } @@ -601,17 +617,19 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { this.clearData(); JSC.markBinding(@src()); - this.tcp.?.timeout(0); - log("onDidConnect", .{}); + if (this.tcp != null and this.outgoing_websocket != null) { + this.tcp.?.timeout(0); + log("onDidConnect", .{}); - this.outgoing_websocket.?.didConnect(this.tcp.?.socket.get().?, overflow.ptr, overflow.len); + this.outgoing_websocket.?.didConnect(this.tcp.?.socket.get().?, overflow.ptr, overflow.len); + } } pub fn handleWritable( this: *HTTPClient, socket: Socket, ) void { - bun.assert(socket.socket.eq(this.tcp.?.socket)); + bun.assert(this.isSameSocket(socket)); if (this.to_send.len == 0) return; @@ -932,7 +950,7 @@ const Copy = union(enum) { pub fn NewWebSocketClient(comptime ssl: bool) type { return struct { pub const Socket = uws.NewSocketHandler(ssl); - tcp: Socket, + tcp: ?Socket = null, outgoing_websocket: ?*CppWebSocket = null, receive_state: ReceiveState = ReceiveState.need_header, @@ -1013,11 +1031,14 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { pub fn cancel(this: *WebSocket) callconv(.C) void { log("cancel", .{}); this.clearData(); - - if (this.tcp.isClosed() or this.tcp.isShutdown()) - return; - - this.tcp.close(.failure); + const tcp = this.tcp orelse return; + this.tcp = null; + // no need to be .failure we still wanna to send pending SSL buffer + close_notify + if (comptime ssl) { + tcp.close(.normal); + } else { + tcp.close(.failure); + } } pub fn fail(this: *WebSocket, code: ErrorCode) void { @@ -1190,7 +1211,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { bun.assert(this.initial_data_handler == null); // If we disconnected for any reason in the re-entrant case, we should just ignore the data - if (this.outgoing_websocket == null or this.tcp.isShutdown() or this.tcp.isClosed()) + if (this.outgoing_websocket == null or this.tcp == null or this.tcp.?.isShutdown() or this.tcp.?.isClosed()) return; } @@ -1501,7 +1522,9 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { } pub fn sendClose(this: *WebSocket) void { - this.sendCloseWithBody(this.tcp, 1000, null, 0); + if (this.tcp) |tcp| { + this.sendCloseWithBody(tcp, 1000, null, 0); + } } fn enqueueEncodedBytes( @@ -1545,9 +1568,11 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { if (do_write) { if (comptime Environment.allow_assert) { - bun.assert(!this.tcp.isShutdown()); - bun.assert(!this.tcp.isClosed()); - bun.assert(this.tcp.isEstablished()); + if (this.tcp) |tcp| { + bun.assert(!tcp.isShutdown()); + bun.assert(!tcp.isClosed()); + bun.assert(tcp.isEstablished()); + } } return this.sendBuffer(this.send_buffer.readableSlice(0)); } @@ -1561,7 +1586,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { ) bool { bun.assert(out_buf.len > 0); // Do not set MSG_MORE, see https://github.com/oven-sh/bun/issues/4010 - const wrote = this.tcp.write(out_buf, false); + const tcp = this.tcp orelse return false; + const wrote = tcp.write(out_buf, false); if (wrote < 0) { this.terminate(ErrorCode.failed_to_write); return false; @@ -1650,9 +1676,15 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { this.clearData(); } } + pub fn isSameSocket(this: *WebSocket, socket: Socket) bool { + if (this.tcp) |tcp| { + return socket.socket.eq(tcp.socket); + } + return false; + } pub fn handleEnd(this: *WebSocket, socket: Socket) void { - bun.assert(socket.socket.eq(this.tcp.socket)); + bun.assert(this.isSameSocket(socket)); this.terminate(ErrorCode.ended); } @@ -1661,7 +1693,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { socket: Socket, ) void { if (this.close_received) return; - bun.assert(socket.socket.eq(this.tcp.socket)); + bun.assert(this.isSameSocket(socket)); const send_buf = this.send_buffer.readableSlice(0); if (send_buf.len == 0) return; @@ -1687,7 +1719,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { len: usize, op: u8, ) callconv(.C) void { - if (this.tcp.isClosed() or this.tcp.isShutdown() or op > 0xF) { + if (!this.hasTCP() or op > 0xF) { this.dispatchAbruptClose(); return; } @@ -1700,12 +1732,18 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { if (!this.hasBackpressure() and frame_size < stack_frame_size) { var inline_buf: [stack_frame_size]u8 = undefined; bytes.copy(this.globalThis, inline_buf[0..frame_size], slice.len, opcode); - _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]); + _ = this.enqueueEncodedBytes(this.tcp.?, inline_buf[0..frame_size]); return; } _ = this.sendData(bytes, !this.hasBackpressure(), opcode); } + fn hasTCP(this: *WebSocket) bool { + if (this.tcp) |tcp| { + return !tcp.isClosed() and !tcp.isShutdown(); + } + return false; + } pub fn writeString( this: *WebSocket, @@ -1713,10 +1751,11 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { op: u8, ) callconv(.C) void { const str = str_.*; - if (this.tcp.isClosed() or this.tcp.isShutdown()) { + if (!this.hasTCP()) { this.dispatchAbruptClose(); return; } + const tcp = this.tcp.?; // Note: 0 is valid @@ -1731,7 +1770,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { const frame_size = bytes.len(&byte_len); if (!this.hasBackpressure() and frame_size < stack_frame_size) { bytes.copy(this.globalThis, inline_buf[0..frame_size], byte_len, opcode); - _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]); + _ = this.enqueueEncodedBytes(tcp, inline_buf[0..frame_size]); return; } // max length of a utf16 -> utf8 conversion is 4 times the length of the utf16 string @@ -1741,7 +1780,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { const frame_size = bytes.len(&byte_len); bun.assert(frame_size <= stack_frame_size); bytes.copy(this.globalThis, inline_buf[0..frame_size], byte_len, opcode); - _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]); + _ = this.enqueueEncodedBytes(tcp, inline_buf[0..frame_size]); return; } } @@ -1773,21 +1812,21 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { } pub fn close(this: *WebSocket, code: u16, reason: ?*const JSC.ZigString) callconv(.C) void { - if (this.tcp.isClosed() or this.tcp.isShutdown()) + if (!this.hasTCP()) return; - + const tcp = this.tcp.?; var close_reason_buf: [128]u8 = undefined; if (reason) |str| { inner: { var fixed_buffer = std.heap.FixedBufferAllocator.init(&close_reason_buf); const allocator = fixed_buffer.allocator(); const wrote = std.fmt.allocPrint(allocator, "{}", .{str.*}) catch break :inner; - this.sendCloseWithBody(this.tcp, code, wrote.ptr[0..125], wrote.len); + this.sendCloseWithBody(tcp, code, wrote.ptr[0..125], wrote.len); return; } } - this.sendCloseWithBody(this.tcp, code, null, 0); + this.sendCloseWithBody(tcp, code, null, 0); } const InitialDataHandler = struct { @@ -1804,8 +1843,9 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { var ws = this.ws; defer ws.unref(); - if (this_socket.outgoing_websocket != null) - this_socket.handleData(this_socket.tcp, this.slice); + if (this_socket.outgoing_websocket != null and this_socket.tcp != null) { + this_socket.handleData(this_socket.tcp.?, this.slice); + } } pub fn handle(this: *@This()) void { @@ -1877,11 +1917,14 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { this.clearData(); this.outgoing_websocket = null; - - if (this.tcp.isClosed()) - return; - - this.tcp.close(.normal); + const tcp = this.tcp orelse return; + this.tcp = null; + // no need to be .failure we still wanna to send pending SSL buffer + close_notify + if (comptime ssl) { + tcp.close(.normal); + } else { + tcp.close(.failure); + } } pub const Export = shim.exportFunctions(.{ diff --git a/src/import_record.zig b/src/import_record.zig index 93e18c124d6a3..c21e2223c8f18 100644 --- a/src/import_record.zig +++ b/src/import_record.zig @@ -7,7 +7,6 @@ const Index = @import("ast/base.zig").Index; const Api = @import("./api/schema.zig").Api; pub const ImportKind = enum(u8) { - // An entry point provided by the user entry_point, diff --git a/src/ini.zig b/src/ini.zig new file mode 100644 index 0000000000000..bcdb70e16b4d6 --- /dev/null +++ b/src/ini.zig @@ -0,0 +1,1248 @@ +const std = @import("std"); +const bun = @import("root").bun; +const Allocator = std.mem.Allocator; +const E = bun.JSAst.E; +const Expr = bun.JSAst.Expr; +const Loc = bun.logger.Loc; +const js_ast = bun.JSAst; +const Rope = js_ast.E.Object.Rope; +const Output = bun.Output; +const Global = bun.Global; +const Registry = bun.install.Npm.Registry; + +pub const Parser = struct { + opts: Options = .{}, + source: bun.logger.Source, + src: []const u8, + out: Expr, + logger: bun.logger.Log, + arena: std.heap.ArenaAllocator, + env: *bun.DotEnv.Loader, + + const Options = struct { + bracked_array: bool = true, + }; + + pub fn init(allocator: Allocator, path: []const u8, src: []const u8, env: *bun.DotEnv.Loader) Parser { + return .{ + .logger = bun.logger.Log.init(allocator), + .src = src, + .out = Expr.init(E.Object, E.Object{}, Loc.Empty), + .source = bun.logger.Source.initPathString(path, src), + .arena = std.heap.ArenaAllocator.init(allocator), + .env = env, + }; + } + + pub fn deinit(this: *Parser) void { + this.logger.deinit(); + this.arena.deinit(); + } + + pub fn parse(this: *Parser, arena_allocator: Allocator) !void { + try this.parseImpl(arena_allocator); + } + + inline fn shouldSkipLine(line: []const u8) bool { + if (line.len == 0 or + // comments + line[0] == ';' or + line[0] == '#') return true; + + // check the rest is whitespace + for (line) |c| { + switch (c) { + ' ', '\t', '\n', '\r' => {}, + '#', ';' => return true, + else => return false, + } + } + return true; + } + + fn parseImpl(this: *Parser, arena_allocator: Allocator) !void { + var iter = std.mem.splitScalar(u8, this.src, '\n'); + var head: *E.Object = this.out.data.e_object; + + // var duplicates = bun.StringArrayHashMapUnmanaged(u32){}; + // defer duplicates.deinit(allocator); + + var rope_stack = std.heap.stackFallback(@sizeOf(Rope) * 6, arena_allocator); + const ropealloc = rope_stack.get(); + + var skip_until_next_section: bool = false; + + while (iter.next()) |line_| { + const line = if (line_.len > 0 and line_[line_.len - 1] == '\r') line_[0 .. line_.len - 1] else line_; + if (shouldSkipLine(line)) continue; + + // Section + // [foo] + if (line[0] == '[') treat_as_key: { + skip_until_next_section = false; + const close_bracket_idx = std.mem.indexOfScalar(u8, line[0..], ']') orelse continue; + // Make sure the rest is just whitespace + if (close_bracket_idx + 1 < line.len) { + for (line[close_bracket_idx + 1 ..]) |c| if (switch (c) { + ' ', '\t' => false, + else => true, + }) break :treat_as_key; + } + const section: *Rope = try this.prepareStr(arena_allocator, ropealloc, line[1..close_bracket_idx], @as(i32, @intCast(@intFromPtr(line.ptr) - @intFromPtr(this.src.ptr))) + 1, .section); + defer rope_stack.fixed_buffer_allocator.reset(); + const parent_object = this.out.data.e_object.getOrPutObject(section, arena_allocator) catch |e| switch (e) { + error.OutOfMemory => bun.outOfMemory(), + error.Clobber => { + // We're in here if key exists but it is not an object + // + // This is possible if someone did: + // + // ```ini + // foo = 'bar' + // + // [foo] + // hello = 420 + // ``` + // + // In the above case, `this.out[section]` would be a string. + // So what should we do in that case? + // + // npm/ini's will chug along happily trying to assign keys to the string. + // + // In JS assigning keys to string does nothing. + // + // Technically, this would have an effect if the value was an array: + // + // ```ini + // foo[] = 0 + // foo[] = 1 + // + // [foo] + // 0 = 420 + // ``` + // + // This would result in `foo` being `[420, 1]`. + // + // To be honest this is kind of crazy behavior so we're just going to skip this for now. + skip_until_next_section = true; + continue; + }, + }; + head = parent_object.data.e_object; + continue; + } + if (skip_until_next_section) continue; + + // Otherwise it's a key val here + + const line_offset: i32 = @intCast(@intFromPtr(line.ptr) - @intFromPtr(this.src.ptr)); + + const maybe_eq_sign_idx = std.mem.indexOfScalar(u8, line, '='); + + const key_raw: []const u8 = try this.prepareStr(arena_allocator, ropealloc, line[0 .. maybe_eq_sign_idx orelse line.len], line_offset, .key); + const is_array: bool = brk: { + break :brk key_raw.len > 2 and bun.strings.endsWith(key_raw, "[]"); + // Commenting out because options are not supported but we might + // support them. + // if (this.opts.bracked_array) { + // break :brk key_raw.len > 2 and bun.strings.endsWith(key_raw, "[]"); + // } else { + // // const gop = try duplicates.getOrPut(allocator, key_raw); + // // if (gop.found_existing) { + // // gop.value_ptr.* = 1; + // // } else gop.value_ptr.* += 1; + // // break :brk gop.value_ptr.* > 1; + // @panic("We don't support this right now"); + // } + }; + + const key = if (is_array and bun.strings.endsWith(key_raw, "[]")) + key_raw[0 .. key_raw.len - 2] + else + key_raw; + + if (bun.strings.eql(key, "__proto__")) continue; + + const value_raw: Expr = brk: { + if (maybe_eq_sign_idx) |eq_sign_idx| { + if (eq_sign_idx + 1 < line.len) break :brk try this.prepareStr( + arena_allocator, + ropealloc, + line[eq_sign_idx + 1 ..], + @intCast(line_offset + @as(i32, @intCast(eq_sign_idx)) + 1), + .value, + ); + break :brk Expr.init(E.String, E.String{ .data = "" }, Loc.Empty); + } + break :brk Expr.init(E.Boolean, E.Boolean{ .value = true }, Loc.Empty); + }; + + const value: Expr = switch (value_raw.data) { + .e_string => |s| if (bun.strings.eqlComptime(s.data, "true")) + Expr.init(E.Boolean, E.Boolean{ .value = true }, Loc.Empty) + else if (bun.strings.eqlComptime(s.data, "false")) + Expr.init(E.Boolean, E.Boolean{ .value = false }, Loc.Empty) + else if (bun.strings.eqlComptime(s.data, "null")) + Expr.init(E.Null, E.Null{}, Loc.Empty) + else + value_raw, + else => value_raw, + }; + + if (is_array) { + if (head.get(key)) |val| { + if (val.data != .e_array) { + var arr = E.Array{}; + arr.push(arena_allocator, val) catch bun.outOfMemory(); + head.put(arena_allocator, key, Expr.init(E.Array, arr, Loc.Empty)) catch bun.outOfMemory(); + } + } else { + head.put(arena_allocator, key, Expr.init(E.Array, E.Array{}, Loc.Empty)) catch bun.outOfMemory(); + } + } + + // safeguard against resetting a previously defined + // array by accidentally forgetting the brackets + var was_already_array = false; + if (head.get(key)) |val| { + if (val.data == .e_array) { + was_already_array = true; + val.data.e_array.push(arena_allocator, value) catch bun.outOfMemory(); + head.put(arena_allocator, key, val) catch bun.outOfMemory(); + } + } + if (!was_already_array) { + head.put(arena_allocator, key, value) catch bun.outOfMemory(); + } + } + } + + fn prepareStr( + this: *Parser, + arena_allocator: Allocator, + ropealloc: Allocator, + val_: []const u8, + offset_: i32, + comptime usage: enum { section, key, value }, + ) !switch (usage) { + .value => Expr, + .section => *Rope, + .key => []const u8, + } { + var offset = offset_; + var val = std.mem.trim(u8, val_, " \n\r\t"); + + if (isQuoted(val)) out: { + // remove single quotes before calling JSON.parse + if (val.len > 0 and val[0] == '\'') { + val = if (val.len > 1) val[1 .. val.len - 1] else val[1..]; + offset += 1; + } + const src = bun.logger.Source.initPathString(this.source.path.text, val); + var log = bun.logger.Log.init(arena_allocator); + defer log.deinit(); + // Try to parse it and it if fails will just treat it as a string + const json_val: Expr = bun.JSON.ParseJSONUTF8Impl(&src, &log, arena_allocator, true) catch { + break :out; + }; + + if (json_val.asString(arena_allocator)) |str| { + if (comptime usage == .value) return Expr.init(E.String, E.String.init(str), Loc{ .start = @intCast(offset) }); + if (comptime usage == .section) return strToRope(ropealloc, str); + return str; + } + + if (comptime usage == .value) return json_val; + + // unfortunately, we need to match npm/ini behavior here, + // which requires us to turn these into a string, + // same behavior as doing this: + // ``` + // let foo = {} + // const json_val = { hi: 'hello' } + // foo[json_val] = 'nice' + // ``` + switch (json_val.data) { + .e_object => { + if (comptime usage == .section) return singleStrRope(ropealloc, "[Object object]"); + return "[Object object]"; + }, + else => { + const str = std.fmt.allocPrint(arena_allocator, "{}", .{ToStringFormatter{ .d = json_val.data }}) catch |e| { + this.logger.addErrorFmt(&this.source, Loc{ .start = offset }, arena_allocator, "failed to stringify value: {s}", .{@errorName(e)}) catch bun.outOfMemory(); + return error.ParserError; + }; + if (comptime usage == .section) return singleStrRope(ropealloc, str); + return str; + }, + } + } else { + const STACK_BUF_SIZE = 1024; + // walk the val to find the first non-escaped comment character (; or #) + var did_any_escape: bool = false; + var esc = false; + var sfb = std.heap.stackFallback(STACK_BUF_SIZE, arena_allocator); + var unesc = try std.ArrayList(u8).initCapacity(sfb.get(), STACK_BUF_SIZE); + + const RopeT = if (comptime usage == .section) *Rope else struct {}; + var rope: ?RopeT = if (comptime usage == .section) null else undefined; + + var i: usize = 0; + while (i < val.len) : (i += 1) { + const c = val[i]; + if (esc) { + switch (c) { + '\\' => try unesc.appendSlice(&[_]u8{ '\\', '\\' }), + ';', '#', '$' => try unesc.append(c), + '.' => { + if (comptime usage == .section) { + try unesc.append('.'); + } else { + try unesc.appendSlice("\\."); + } + }, + else => { + try unesc.appendSlice(switch (bun.strings.utf8ByteSequenceLength(c)) { + 1 => brk: { + break :brk &[_]u8{ '\\', c }; + }, + 2 => brk: { + defer i += 1; + break :brk &[_]u8{ '\\', c, val[i + 1] }; + }, + 3 => brk: { + defer i += 2; + break :brk &[_]u8{ '\\', c, val[i + 1], val[i + 2] }; + }, + 4 => brk: { + defer i += 3; + break :brk &[_]u8{ '\\', c, val[i + 1], val[i + 2], val[i + 3] }; + }, + // this means invalid utf8 + else => unreachable, + }); + }, + } + + esc = false; + } else switch (c) { + '$' => { + not_env_substitution: { + if (comptime usage != .value) break :not_env_substitution; + + if (this.parseEnvSubstitution(val, i, i, &unesc)) |new_i| { + // set to true so we heap alloc + did_any_escape = true; + i = new_i; + continue; + } + + break :not_env_substitution; + } + try unesc.append('$'); + }, + ';', '#' => break, + '\\' => { + esc = true; + did_any_escape = true; + }, + '.' => { + if (comptime usage == .section) { + this.commitRopePart(arena_allocator, ropealloc, &unesc, &rope); + } else { + try unesc.append('.'); + } + }, + else => try unesc.appendSlice(switch (bun.strings.utf8ByteSequenceLength(c)) { + 1 => brk: { + break :brk &[_]u8{c}; + }, + 2 => brk: { + defer i += 1; + break :brk &[_]u8{ c, val[i + 1] }; + }, + 3 => brk: { + defer i += 2; + break :brk &[_]u8{ c, val[i + 1], val[i + 2] }; + }, + 4 => brk: { + defer i += 3; + break :brk &[_]u8{ c, val[i + 1], val[i + 2], val[i + 3] }; + }, + // this means invalid utf8 + else => unreachable, + }), + } + } + + if (esc) + try unesc.append('\\'); + + switch (usage) { + .section => { + this.commitRopePart(arena_allocator, ropealloc, &unesc, &rope); + return rope.?; + }, + .value => { + if (!did_any_escape) return Expr.init(E.String, E.String.init(val[0..]), Loc{ .start = offset }); + if (unesc.items.len <= STACK_BUF_SIZE) return Expr.init( + E.String, + E.String.init(try arena_allocator.dupe(u8, unesc.items[0..])), + Loc{ .start = offset }, + ); + return Expr.init(E.String, E.String.init(unesc.items[0..]), Loc{ .start = offset }); + }, + .key => { + const thestr: []const u8 = thestr: { + if (!did_any_escape) break :thestr try arena_allocator.dupe(u8, val[0..]); + if (unesc.items.len <= STACK_BUF_SIZE) break :thestr try arena_allocator.dupe(u8, unesc.items[0..]); + break :thestr unesc.items[0..]; + }; + return thestr; + }, + } + } + if (comptime usage == .value) return Expr.init(E.String, E.String.init(val[0..]), Loc{ .start = offset }); + if (comptime usage == .key) return val[0..]; + return strToRope(ropealloc, val[0..]); + } + + /// Returns index to skip or null if not an env substitution + /// Invariants: + /// - `i` must be an index into `val` that points to a '$' char + /// + /// npm/ini uses a regex pattern that will select the inner most ${...} + fn parseEnvSubstitution(this: *Parser, val: []const u8, start: usize, i: usize, unesc: *std.ArrayList(u8)) ?usize { + bun.debugAssert(val[i] == '$'); + var esc = false; + if (i + "{}".len < val.len and val[i + 1] == '{') { + var found_closing = false; + var j = i + 2; + while (j < val.len) : (j += 1) { + switch (val[j]) { + '\\' => esc = !esc, + '$' => if (!esc) return this.parseEnvSubstitution(val, start, j, unesc), + '{' => if (!esc) return null, + '}' => if (!esc) { + found_closing = true; + break; + }, + else => {}, + } + } + + if (!found_closing) return null; + + if (start != i) { + const missed = val[start..i]; + unesc.appendSlice(missed) catch bun.outOfMemory(); + } + + const env_var = val[i + 2 .. j]; + const expanded = this.expandEnvVar(env_var); + unesc.appendSlice(expanded) catch bun.outOfMemory(); + + return j; + } + return null; + } + + fn expandEnvVar(this: *Parser, name: []const u8) []const u8 { + return this.env.get(name) orelse ""; + } + + fn singleStrRope(ropealloc: Allocator, str: []const u8) *Rope { + const rope = ropealloc.create(Rope) catch bun.outOfMemory(); + rope.* = .{ + .head = Expr.init(E.String, E.String.init(str), Loc.Empty), + }; + return rope; + } + + fn nextDot(key: []const u8) ?usize { + return std.mem.indexOfScalar(u8, key, '.'); + } + + fn commitRopePart(this: *Parser, arena_allocator: Allocator, ropealloc: Allocator, unesc: *std.ArrayList(u8), existing_rope: *?*Rope) void { + _ = this; // autofix + const slice = arena_allocator.dupe(u8, unesc.items[0..]) catch bun.outOfMemory(); + const expr = Expr.init(E.String, E.String{ .data = slice }, Loc.Empty); + if (existing_rope.*) |_r| { + const r: *Rope = _r; + _ = r.append(expr, ropealloc) catch bun.outOfMemory(); + } else { + existing_rope.* = ropealloc.create(Rope) catch bun.outOfMemory(); + existing_rope.*.?.* = Rope{ + .head = expr, + }; + } + unesc.clearRetainingCapacity(); + } + + fn strToRope(ropealloc: Allocator, key: []const u8) *Rope { + var dot_idx = nextDot(key) orelse { + const rope = ropealloc.create(Rope) catch bun.outOfMemory(); + rope.* = .{ + .head = Expr.init(E.String, E.String.init(key), Loc.Empty), + }; + return rope; + }; + var rope = ropealloc.create(Rope) catch bun.outOfMemory(); + const head = rope; + rope.* = .{ + .head = Expr.init(E.String, E.String.init(key[0..dot_idx]), Loc.Empty), + .next = null, + }; + + while (dot_idx + 1 < key.len) { + const next_dot_idx = dot_idx + 1 + (nextDot(key[dot_idx + 1 ..]) orelse { + const rest = key[dot_idx + 1 ..]; + rope = rope.append(Expr.init(E.String, E.String.init(rest), Loc.Empty), ropealloc) catch bun.outOfMemory(); + break; + }); + const part = key[dot_idx + 1 .. next_dot_idx]; + rope = rope.append(Expr.init(E.String, E.String.init(part), Loc.Empty), ropealloc) catch bun.outOfMemory(); + dot_idx = next_dot_idx; + } + + return head; + } + + fn isQuoted(val: []const u8) bool { + return (bun.strings.startsWithChar(val, '"') and bun.strings.endsWithChar(val, '"')) or + (bun.strings.startsWithChar(val, '\'') and bun.strings.endsWithChar(val, '\'')); + } +}; + +/// Used in JS tests, see `internal-for-testing.ts` and shell tests. +pub const IniTestingAPIs = struct { + const JSC = bun.JSC; + + pub fn loadNpmrcFromJS( + globalThis: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSC.JSValue { + const arg = callframe.argument(0); + const npmrc_contents = arg.toBunString(globalThis); + defer npmrc_contents.deref(); + const npmrc_utf8 = npmrc_contents.toUTF8(bun.default_allocator); + defer npmrc_utf8.deinit(); + const source = bun.logger.Source.initPathString("", npmrc_utf8.slice()); + + var log = bun.logger.Log.init(bun.default_allocator); + defer log.deinit(); + + var arena = bun.ArenaAllocator.init(bun.default_allocator); + const allocator = arena.allocator(); + defer arena.deinit(); + + const envjs = callframe.argument(1); + const env = if (envjs.isEmptyOrUndefinedOrNull()) globalThis.bunVM().bundler.env else brk: { + var envmap = bun.DotEnv.Map.HashTable.init(allocator); + var object_iter = JSC.JSPropertyIterator(.{ + .skip_empty_name = false, + .include_value = true, + }).init(globalThis, envjs); + defer object_iter.deinit(); + + envmap.ensureTotalCapacity(object_iter.len) catch bun.outOfMemory(); + + while (object_iter.next()) |key| { + const keyslice = key.toOwnedSlice(allocator) catch bun.outOfMemory(); + var value = object_iter.value; + if (value == .undefined) continue; + + const value_str = value.getZigString(globalThis); + const slice = value_str.toOwnedSlice(allocator) catch bun.outOfMemory(); + + envmap.put(keyslice, .{ + .value = slice, + .conditional = false, + }) catch bun.outOfMemory(); + } + + const map = allocator.create(bun.DotEnv.Map) catch bun.outOfMemory(); + map.* = .{ + .map = envmap, + }; + + const env = bun.DotEnv.Loader.init(map, allocator); + const envstable = allocator.create(bun.DotEnv.Loader) catch bun.outOfMemory(); + envstable.* = env; + break :brk envstable; + }; + + const install = allocator.create(bun.Schema.Api.BunInstall) catch bun.outOfMemory(); + install.* = std.mem.zeroes(bun.Schema.Api.BunInstall); + loadNpmrc(allocator, install, env, false, &log, &source) catch { + return log.toJS(globalThis, allocator, "error"); + }; + + var obj = JSC.JSValue.createEmptyObject(globalThis, 3); + obj.protect(); + defer obj.unprotect(); + + const default_registry_url, const default_registry_token, const default_registry_username, const default_registry_password = brk: { + const default_registry = install.default_registry orelse break :brk .{ bun.String.static(Registry.default_url[0..]), bun.String.empty, bun.String.empty, bun.String.empty }; + + break :brk .{ + bun.String.fromBytes(default_registry.url), + bun.String.fromBytes(default_registry.token), + bun.String.fromBytes(default_registry.username), + bun.String.fromBytes(default_registry.password), + }; + }; + defer { + default_registry_url.deref(); + default_registry_token.deref(); + default_registry_username.deref(); + default_registry_password.deref(); + } + + obj.put( + globalThis, + bun.String.static("default_registry_url"), + default_registry_url.toJS(globalThis), + ); + obj.put( + globalThis, + bun.String.static("default_registry_token"), + default_registry_token.toJS(globalThis), + ); + obj.put( + globalThis, + bun.String.static("default_registry_username"), + default_registry_username.toJS(globalThis), + ); + obj.put( + globalThis, + bun.String.static("default_registry_password"), + default_registry_password.toJS(globalThis), + ); + + return obj; + } + + pub fn parse( + globalThis: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSC.JSValue { + const arguments_ = callframe.arguments(1); + const arguments = arguments_.slice(); + + const jsstr = arguments[0]; + const bunstr = jsstr.toBunString(globalThis); + defer bunstr.deref(); + const utf8str = bunstr.toUTF8(bun.default_allocator); + defer utf8str.deinit(); + + var parser = Parser.init(bun.default_allocator, "", utf8str.slice(), globalThis.bunVM().bundler.env); + defer parser.deinit(); + + parser.parse(parser.arena.allocator()) catch |e| { + if (parser.logger.errors > 0) { + parser.logger.printForLogLevel(bun.Output.writer()) catch bun.outOfMemory(); + } else globalThis.throwError(e, "failed to parse"); + return .undefined; + }; + + return parser.out.toJS(bun.default_allocator, globalThis, .{ .decode_escape_sequences = true }) catch |e| { + globalThis.throwError(e, "failed to turn AST into JS"); + return .undefined; + }; + } +}; + +pub const ToStringFormatter = struct { + d: js_ast.Expr.Data, + + pub fn format(this: *const @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + switch (this.d) { + .e_array => { + const last = this.d.e_array.items.len -| 1; + for (this.d.e_array.items.slice(), 0..) |*e, i| { + const is_last = i == last; + try writer.print("{}{s}", .{ ToStringFormatter{ .d = e.data }, if (is_last) "" else "," }); + } + }, + .e_object => try writer.print("[Object object]", .{}), + .e_boolean => try writer.print("{s}", .{if (this.d.e_boolean.value) "true" else "false"}), + .e_number => try writer.print("{d}", .{this.d.e_number.value}), + .e_string => try writer.print("{s}", .{this.d.e_string.data}), + .e_null => try writer.print("null", .{}), + .e_utf8_string => try writer.print("{s}", .{this.d.e_utf8_string.data}), + + else => |tag| if (bun.Environment.isDebug) { + Output.panic("Unexpected AST node: {s}", .{@tagName(tag)}); + }, + } + } +}; + +pub fn Option(comptime T: type) type { + return union(enum) { + some: T, + none, + + pub fn get(this: @This()) ?T { + return switch (this) { + .some => this.some, + .none => null, + }; + } + }; +} + +pub const ConfigIterator = struct { + allocator: Allocator, + config: *E.Object, + source: *const bun.logger.Source, + log: *bun.logger.Log, + + prop_idx: usize = 0, + + pub const Item = struct { + registry_url: []const u8, + optname: Opt, + value: []const u8, + loc: Loc, + + pub const Opt = enum { + /// `${username}:${password}` encoded in base64 + _auth, + + /// authentication string + _authToken, + + username, + + /// this is encoded as base64 in .npmrc + _password, + + email, + + /// path to certificate file + certfile, + + /// path to key file + keyfile, + + pub fn isBase64Encoded(this: Opt) bool { + return switch (this) { + ._auth, ._password => true, + else => false, + }; + } + }; + + /// Duplicate the value, decoding it if it is base64 encoded. + pub fn dupeValueDecoded( + this: *const Item, + allocator: Allocator, + log: *bun.logger.Log, + source: *const bun.logger.Source, + ) ?[]const u8 { + if (this.optname.isBase64Encoded()) { + if (this.value.len == 0) return ""; + const len = bun.base64.decodeLen(this.value); + var slice = allocator.alloc(u8, len) catch bun.outOfMemory(); + const result = bun.base64.decode(slice[0..], this.value); + if (result.status != .success) { + log.addErrorFmt( + source, + this.loc, + allocator, + "{s} is not valid base64", + .{@tagName(this.optname)}, + ) catch bun.outOfMemory(); + return null; + } + return allocator.dupe(u8, slice[0..result.count]) catch bun.outOfMemory(); + } + return allocator.dupe(u8, this.value) catch bun.outOfMemory(); + } + + pub fn format(this: *const @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + try writer.print("//{s}:{s}={s}", .{ this.registry_url, @tagName(this.optname), this.value }); + } + }; + + pub fn next(this: *ConfigIterator) error{ParserError}!?Option(Item) { + if (this.prop_idx >= this.config.properties.len) return null; + defer this.prop_idx += 1; + + const prop = this.config.properties.ptr[this.prop_idx]; + + if (prop.key) |keyexpr| { + if (keyexpr.asUtf8StringLiteral()) |key| { + if (bun.strings.hasPrefixComptime(key, "//")) { + const optnames = comptime brk: { + const names = std.meta.fieldNames(Item.Opt); + var names2: [names.len][:0]const u8 = undefined; + // we need to make sure to reverse this + // because _auth could match when it actually had _authToken + // so go backwards since _authToken is last + for (0..names.len) |i| { + names2[names2.len - i - 1] = names[i]; + } + break :brk names2; + }; + + inline for (optnames) |name| { + var buf: [name.len + 1]u8 = undefined; + buf[0] = ':'; + @memcpy(buf[1 .. name.len + 1], name); + const name_with_eq = buf[0..]; + + if (std.mem.lastIndexOf(u8, key, name_with_eq)) |index| { + const url_part = key[2..index]; + if (prop.value) |value_expr| { + if (value_expr.asUtf8StringLiteral()) |value| { + return .{ + .some = Item{ + .registry_url = url_part, + .value = value, + .optname = std.meta.stringToEnum(Item.Opt, name).?, + .loc = prop.key.?.loc, + }, + }; + } + } + } + } + } + } + } + + return .none; + } +}; + +pub const ScopeIterator = struct { + allocator: Allocator, + config: *E.Object, + source: *const bun.logger.Source, + log: *bun.logger.Log, + + prop_idx: usize = 0, + count: bool = false, + + const Error = error{ + no_value, + }; + + const Item = struct { scope: []const u8, registry: bun.Schema.Api.NpmRegistry }; + + pub fn next(this: *ScopeIterator) error{ParserError}!?Option(Item) { + if (this.prop_idx >= this.config.properties.len) return null; + defer this.prop_idx += 1; + + const prop = this.config.properties.ptr[this.prop_idx]; + + if (prop.key) |keyexpr| { + if (keyexpr.asUtf8StringLiteral()) |key| { + if (bun.strings.hasPrefixComptime(key, "@") and bun.strings.endsWith(key, ":registry")) { + if (!this.count) { + return .{ + .some = .{ + .scope = key[1 .. key.len - ":registry".len], + .registry = brk: { + if (prop.value) |value| { + if (value.asUtf8StringLiteral()) |str| { + var parser = bun.Schema.Api.NpmRegistry.Parser{ + .log = this.log, + .source = this.source, + .allocator = this.allocator, + }; + break :brk parser.parseRegistryURLStringImpl(str) catch |e| { + if (e == error.OutOfMemory) bun.outOfMemory(); + return error.ParserError; + }; + } + } + return .none; + }, + }, + }; + } + } + } + } + + return .none; + } +}; + +pub fn loadNpmrcFromFile( + allocator: std.mem.Allocator, + install: *bun.Schema.Api.BunInstall, + env: *bun.DotEnv.Loader, + auto_loaded: bool, +) void { + var log = bun.logger.Log.init(allocator); + defer log.deinit(); + const npmrc_file = switch (bun.sys.openat(bun.FD.cwd(), ".npmrc", bun.O.RDONLY, 0)) { + .result => |fd| fd, + .err => |err| { + if (auto_loaded) return; + Output.prettyErrorln("{}\nwhile opening .npmrc \"{s}\"", .{ + err, + ".npmrc", + }); + Global.exit(1); + }, + }; + defer _ = bun.sys.close(npmrc_file); + + const source = switch (bun.sys.File.toSource(".npmrc", allocator)) { + .result => |s| s, + .err => |e| { + Output.prettyErrorln("{}\nwhile reading .npmrc \"{s}\"", .{ + e, + ".npmrc", + }); + Global.exit(1); + }, + }; + defer allocator.free(source.contents); + + loadNpmrc(allocator, install, env, auto_loaded, &log, &source) catch { + if (log.errors == 1) + Output.warn("Encountered an error while reading .npmrc:\n", .{}) + else + Output.warn("Encountered errors while reading .npmrc:\n", .{}); + }; + log.printForLogLevel(Output.errorWriter()) catch bun.outOfMemory(); +} + +pub fn loadNpmrc( + allocator: std.mem.Allocator, + install: *bun.Schema.Api.BunInstall, + env: *bun.DotEnv.Loader, + auto_loaded: bool, + log: *bun.logger.Log, + source: *const bun.logger.Source, +) !void { + var parser = bun.ini.Parser.init(allocator, ".npmrc", source.contents, env); + defer parser.deinit(); + parser.parse(parser.arena.allocator()) catch |e| { + if (e == error.ParserError) { + parser.logger.printForLogLevel(Output.errorWriter()) catch unreachable; + return e; + } + if (auto_loaded) { + Output.warn("{}\nwhile reading .npmrc \"{s}\"", .{ + e, + ".npmrc", + }); + return; + } + Output.prettyErrorln("{}\nwhile reading .npmrc \"{s}\"", .{ + e, + ".npmrc", + }); + Global.exit(1); + }; + + // Need to be very, very careful here with strings. + // They are allocated in the Parser's arena, which of course gets + // deinitialized at the end of the scope. + // We need to dupe all strings + const out = parser.out; + + if (out.asProperty("registry")) |query| { + if (query.expr.asUtf8StringLiteral()) |str| { + var p = bun.Schema.Api.NpmRegistry.Parser{ + .allocator = allocator, + .log = log, + .source = source, + }; + install.default_registry = p.parseRegistryURLStringImpl(allocator.dupe(u8, str) catch bun.outOfMemory()) catch |e| { + if (e == error.OutOfMemory) bun.outOfMemory(); + return error.ParserError; + }; + } + } + + if (out.asProperty("cache")) |query| { + if (query.expr.asUtf8StringLiteral()) |str| { + install.cache_directory = allocator.dupe(u8, str) catch bun.outOfMemory(); + } else if (query.expr.asBool()) |b| { + install.disable_cache = !b; + } + } + + if (out.asProperty("dry-run")) |query| { + if (query.expr.asUtf8StringLiteral()) |str| { + install.dry_run = bun.strings.eqlComptime(str, "true"); + } else if (query.expr.asBool()) |b| { + install.dry_run = b; + } + } + + var registry_map = install.scoped orelse bun.Schema.Api.NpmRegistryMap{}; + + // Process scopes + { + var iter = bun.ini.ScopeIterator{ + .config = parser.out.data.e_object, + .count = true, + .source = source, + .log = log, + .allocator = allocator, + }; + + const scope_count = brk: { + var count: usize = 0; + while (iter.next() catch { + const prop_idx = iter.prop_idx -| 1; + const prop = iter.config.properties.at(prop_idx); + const loc = prop.key.?.loc; + log.addErrorFmt(source, loc, parser.arena.allocator(), "Found an invalid registry option:", .{}) catch bun.outOfMemory(); + return error.ParserError; + }) |o| { + if (o == .some) { + count += 1; + } + } + break :brk count; + }; + + defer install.scoped = registry_map; + registry_map.scopes.ensureUnusedCapacity(allocator, scope_count) catch bun.outOfMemory(); + + iter.prop_idx = 0; + iter.count = false; + + while (iter.next() catch unreachable) |val| { + if (val.get()) |result| { + const registry = result.registry.dupe(allocator); + registry_map.scopes.put( + allocator, + allocator.dupe(u8, result.scope) catch bun.outOfMemory(), + registry, + ) catch bun.outOfMemory(); + } + } + } + + // Process registry configuration + out: { + const count = brk: { + var count: usize = 0; + for (parser.out.data.e_object.properties.slice()) |prop| { + if (prop.key) |keyexpr| { + if (keyexpr.asUtf8StringLiteral()) |key| { + if (bun.strings.hasPrefixComptime(key, "//")) { + count += 1; + } + } + } + } + + break :brk count; + }; + + if (count == 0) break :out; + + const default_registry_url: bun.URL = brk: { + if (install.default_registry) |dr| + break :brk bun.URL.parse(dr.url); + + break :brk bun.URL.parse(Registry.default_url); + }; + + // I don't like having to do this but we'll need a mapping of scope -> bun.URL + // Because we need to check different parts of the URL, for instance in this + // example .npmrc: + _ = + \\ @myorg:registry=https://somewhere-else.com/myorg + \\ @another:registry=https://somewhere-else.com/another + \\ + \\ //somewhere-else.com/myorg/:_authToken=MYTOKEN1 + \\ + \\ //somewhere-else.com/:username=foobar + \\ + ; + // The line that sets the auth token should only apply to the @myorg scope + // The line that sets the username would apply to both @myorg and @another + var url_map = url_map: { + var url_map = bun.StringArrayHashMap(bun.URL).init(parser.arena.allocator()); + url_map.ensureTotalCapacity(registry_map.scopes.keys().len) catch bun.outOfMemory(); + + for (registry_map.scopes.keys(), registry_map.scopes.values()) |*k, *v| { + const url = bun.URL.parse(v.url); + url_map.put(k.*, url) catch bun.outOfMemory(); + } + + break :url_map url_map; + }; + + defer url_map.deinit(); + + var iter = bun.ini.ConfigIterator{ + .config = parser.out.data.e_object, + .source = source, + .log = log, + .allocator = allocator, + }; + + while (iter.next() catch { + const prop_idx = iter.prop_idx -| 1; + const prop = iter.config.properties.at(prop_idx); + const loc = prop.key.?.loc; + log.addErrorFmt(source, loc, parser.arena.allocator(), "Found an invalid registry option:", .{}) catch bun.outOfMemory(); + return error.ParserError; + }) |val| { + if (val.get()) |conf_item_| { + // `conf_item` will look like: + // + // - localhost:4873/ + // - somewhere-else.com/myorg/ + // + // Scoped registries are set like this: + // - @myorg:registry=https://somewhere-else.com/myorg + const conf_item: bun.ini.ConfigIterator.Item = conf_item_; + switch (conf_item.optname) { + .email, .certfile, .keyfile => { + log.addWarningFmt( + source, + iter.config.properties.at(iter.prop_idx - 1).key.?.loc, + allocator, + "The following .npmrc registry option was not applied:\n\n {s}\n\nBecause we currently don't support the {s} option.", + .{ + conf_item, + @tagName(conf_item.optname), + }, + ) catch bun.outOfMemory(); + continue; + }, + else => {}, + } + const conf_item_url = bun.URL.parse(conf_item.registry_url); + + if (std.mem.eql(u8, bun.strings.withoutTrailingSlash(default_registry_url.host), bun.strings.withoutTrailingSlash(conf_item_url.host))) { + const v: *bun.Schema.Api.NpmRegistry = brk: { + if (install.default_registry) |*r| break :brk r; + install.default_registry = bun.Schema.Api.NpmRegistry{ + .password = "", + .token = "", + .username = "", + .url = Registry.default_url, + }; + break :brk &install.default_registry.?; + }; + + switch (conf_item.optname) { + ._authToken => { + if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.token = x; + }, + .username => { + if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.username = x; + }, + ._password => { + if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.password = x; + }, + ._auth => { + _ = @"handle _auth"(allocator, v, &conf_item, log, source); + }, + .email, .certfile, .keyfile => unreachable, + } + continue; + } + + var matched_at_least_one = false; + for (registry_map.scopes.keys(), registry_map.scopes.values()) |*k, *v| { + const url = url_map.get(k.*) orelse unreachable; + + if (std.mem.eql(u8, bun.strings.withoutTrailingSlash(url.host), bun.strings.withoutTrailingSlash(conf_item_url.host))) { + if (conf_item_url.hostname.len > 0) { + if (!std.mem.eql(u8, bun.strings.withoutTrailingSlash(url.hostname), bun.strings.withoutTrailingSlash(conf_item_url.hostname))) { + continue; + } + } + matched_at_least_one = true; + switch (conf_item.optname) { + ._authToken => { + if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.token = x; + }, + .username => { + if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.username = x; + }, + ._password => { + if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.password = x; + }, + ._auth => { + _ = @"handle _auth"(allocator, v, &conf_item, log, source); + }, + .email, .certfile, .keyfile => unreachable, + } + // We have to keep going as it could match multiple scopes + continue; + } + } + + if (!matched_at_least_one) { + log.addWarningFmt( + source, + iter.config.properties.at(iter.prop_idx - 1).key.?.loc, + allocator, + "The following .npmrc registry option was not applied:\n\n {s}\n\nBecause we couldn't find the registry: {s}.", + .{ + conf_item, + conf_item.registry_url, + }, + ) catch bun.outOfMemory(); + } + } + } + } + + const had_errors = log.hasErrors(); + if (had_errors) { + return error.ParserError; + } +} + +fn @"handle _auth"( + allocator: Allocator, + v: *bun.Schema.Api.NpmRegistry, + conf_item: *const ConfigIterator.Item, + log: *bun.logger.Log, + source: *const bun.logger.Source, +) void { + if (conf_item.value.len == 0) { + log.addErrorFmt( + source, + conf_item.loc, + allocator, + "invalid _auth value, expected it to be \"\\:\\\" encoded in base64, but got an empty string", + .{}, + ) catch bun.outOfMemory(); + return; + } + const decode_len = bun.base64.decodeLen(conf_item.value); + const decoded = allocator.alloc(u8, decode_len) catch bun.outOfMemory(); + const result = bun.base64.decode(decoded[0..], conf_item.value); + if (!result.isSuccessful()) { + defer allocator.free(decoded); + log.addErrorFmt(source, conf_item.loc, allocator, "invalid base64", .{}) catch bun.outOfMemory(); + return; + } + const @"username:password" = decoded[0..result.count]; + const colon_idx = std.mem.indexOfScalar(u8, @"username:password", ':') orelse { + defer allocator.free(decoded); + log.addErrorFmt(source, conf_item.loc, allocator, "invalid _auth value, expected it to be \"\\:\\\" encoded in base 64, but got:\n\n{s}", .{decoded}) catch bun.outOfMemory(); + return; + }; + const username = @"username:password"[0..colon_idx]; + if (colon_idx + 1 >= @"username:password".len) { + defer allocator.free(decoded); + log.addErrorFmt(source, conf_item.loc, allocator, "invalid _auth value, expected it to be \"\\:\\\" encoded in base64, but got:\n\n{s}", .{decoded}) catch bun.outOfMemory(); + return; + } + const password = @"username:password"[colon_idx + 1 ..]; + v.username = username; + v.password = password; + return; +} diff --git a/src/install/bin.zig b/src/install/bin.zig index 36f8c69312cde..8361b90bc9106 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -7,14 +7,16 @@ const Global = bun.Global; const std = @import("std"); const strings = bun.strings; const Environment = @import("../env.zig"); -const Path = @import("../resolver/resolve_path.zig"); const C = @import("../c.zig"); const Fs = @import("../fs.zig"); const stringZ = bun.stringZ; const Resolution = @import("./resolution.zig").Resolution; const bun = @import("root").bun; +const path = bun.path; const string = bun.string; -const PackageInstall = @import("./install.zig").PackageInstall; +const Install = @import("./install.zig"); +const PackageInstall = Install.PackageInstall; +const Dependency = @import("./dependency.zig"); /// Normalized `bin` field in [package.json](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bin) /// Can be a: @@ -184,7 +186,7 @@ pub const Bin = extern struct { done: bool = false, dir_iterator: ?std.fs.Dir.Iterator = null, package_name: String, - package_installed_node_modules: std.fs.Dir = bun.invalid_fd.asDir(), + destination_node_modules: std.fs.Dir = bun.invalid_fd.asDir(), buf: bun.PathBuffer = undefined, string_buffer: []const u8, extern_string_buf: []const ExternalString, @@ -198,9 +200,9 @@ pub const Bin = extern struct { } var parts = [_][]const u8{ this.package_name.slice(this.string_buffer), target }; - const dir = this.package_installed_node_modules; + const dir = this.destination_node_modules; - const joined = Path.joinStringBuf(&this.buf, &parts, .auto); + const joined = path.joinStringBuf(&this.buf, &parts, .auto); this.buf[joined.len] = 0; const joined_: [:0]u8 = this.buf[0..joined.len :0]; var child_dir = try bun.openDir(dir, joined_); @@ -266,11 +268,39 @@ pub const Bin = extern struct { } }; + pub const PriorityQueueContext = struct { + dependencies: *const std.ArrayListUnmanaged(Dependency), + string_buf: *const std.ArrayListUnmanaged(u8), + + pub fn lessThan(this: PriorityQueueContext, a: Install.DependencyID, b: Install.DependencyID) std.math.Order { + const deps = this.dependencies.items; + const buf = this.string_buf.items; + const a_name = deps[a].name.slice(buf); + const b_name = deps[b].name.slice(buf); + return strings.order(a_name, b_name); + } + }; + + pub const PriorityQueue = std.PriorityQueue(Install.DependencyID, PriorityQueueContext, PriorityQueueContext.lessThan); + + // https://github.com/npm/npm-normalize-package-bin/blob/574e6d7cd21b2f3dee28a216ec2053c2551f7af9/lib/index.js#L38 + pub fn normalizedBinName(name: []const u8) []const u8 { + if (std.mem.lastIndexOfAny(u8, name, "/\\:")) |i| { + return name[i + 1 ..]; + } + + return name; + } + pub const Linker = struct { bin: Bin, - package_installed_node_modules: bun.FileDescriptor = bun.invalid_fd, - root_node_modules_folder: bun.FileDescriptor = bun.invalid_fd, + // Hash map of seen destination paths for this `node_modules/.bin` folder. PackageInstaller will reset it before + // linking each tree. + seen: ?*bun.StringHashMap(void), + + node_modules: bun.FileDescriptor, + node_modules_path: []const u8, /// Used for generating relative paths package_name: strings.StringOrTinyString, @@ -278,21 +308,19 @@ pub const Bin = extern struct { global_bin_dir: std.fs.Dir, global_bin_path: stringZ = "", - relative_path_to_bin_for_windows_global_link_offset: usize = 0, - string_buf: []const u8, extern_string_buf: []const ExternalString, + abs_target_buf: []u8, + abs_dest_buf: []u8, + rel_buf: []u8, + err: ?anyerror = null, pub var umask: bun.C.Mode = 0; var has_set_umask = false; - pub const Error = error{ - NotImplementedYet, - } || std.posix.SymLinkError || bun.OpenError || std.posix.RealPathError; - pub fn ensureUmask() void { if (!has_set_umask) { has_set_umask = true; @@ -300,395 +328,365 @@ pub const Bin = extern struct { } } - fn unscopedPackageName(name: []const u8) []const u8 { - if (name[0] != '@') return name; - var name_ = name; - name_ = name[1..]; - return name_[(strings.indexOfChar(name_, '/') orelse return name) + 1 ..]; - } + fn unlinkBinOrShim(abs_dest: [:0]const u8) void { + if (comptime !Environment.isWindows) { + _ = bun.sys.unlink(abs_dest); + return; + } - fn setPermissions(folder: std.posix.fd_t, target: [:0]const u8) void { - // we use fchmodat to avoid any issues with current working directory - _ = C.fchmodat(folder, target, @intCast(umask | 0o777), 0); + var dest_buf: bun.WPathBuffer = undefined; + const abs_dest_w = strings.convertUTF8toUTF16InBuffer(&dest_buf, abs_dest); + @memcpy(dest_buf[abs_dest_w.len..][0..".bunx\x00".len], comptime strings.literal(u16, ".bunx\x00")); + const abs_bunx_file: [:0]const u16 = dest_buf[0 .. abs_dest_w.len + ".bunx".len :0]; + _ = bun.sys.unlinkW(abs_bunx_file); + @memcpy(dest_buf[abs_dest_w.len..][0..".exe\x00".len], comptime strings.literal(u16, ".exe\x00")); + const abs_exe_file: [:0]const u16 = dest_buf[0 .. abs_dest_w.len + ".exe".len :0]; + _ = bun.sys.unlinkW(abs_exe_file); } - fn setSymlinkAndPermissions(this: *Linker, target_path: [:0]const u8, dest_path: [:0]const u8, link_global: bool) void { - if (comptime !Environment.isWindows) { - const node_modules = this.package_installed_node_modules.asDir(); - std.posix.symlinkatZ(target_path, node_modules.fd, dest_path) catch |err| { - // Silently ignore PathAlreadyExists if the symlink is valid. - // Most likely, the symlink was already created by another package - if (err == error.PathAlreadyExists) { - if (PackageInstall.isDanglingSymlink(dest_path)) { - // this case is hit if the package was previously and the bin is located in a different directory - node_modules.deleteFileZ(dest_path) catch |err2| { - this.err = err2; - return; - }; + fn linkBinOrCreateShim(this: *Linker, abs_target: [:0]const u8, abs_dest: [:0]const u8, global: bool) void { + bun.assertWithLocation(std.fs.path.isAbsoluteZ(abs_target), @src()); + bun.assertWithLocation(std.fs.path.isAbsoluteZ(abs_dest), @src()); + bun.assertWithLocation(abs_target[abs_target.len - 1] != std.fs.path.sep, @src()); + bun.assertWithLocation(abs_dest[abs_dest.len - 1] != std.fs.path.sep, @src()); + + if (this.seen) |seen| { + // Skip seen destinations for this tree + // https://github.com/npm/cli/blob/22731831e22011e32fa0ca12178e242c2ee2b33d/node_modules/bin-links/lib/link-gently.js#L30 + const entry = seen.getOrPut(abs_dest) catch bun.outOfMemory(); + if (entry.found_existing) { + return; + } + entry.key_ptr.* = seen.allocator.dupe(u8, abs_dest) catch bun.outOfMemory(); + } - std.posix.symlinkatZ(target_path, node_modules.fd, dest_path) catch |err2| { - this.err = err2; - return; - }; + // Skip if the target does not exist. This is important because placing a dangling + // shim in path might break a postinstall + if (!bun.sys.exists(abs_target)) { + return; + } - setPermissions(node_modules.fd, dest_path); - return; - } + bun.Analytics.Features.binlinks += 1; - setPermissions(node_modules.fd, dest_path); - var target_path_trim = target_path; - if (strings.hasPrefix(target_path_trim, "../")) { - target_path_trim = target_path_trim[3..]; - } - setPermissions(node_modules.fd, target_path_trim); - return; - } + if (comptime Environment.isWindows) + this.createWindowsShim(abs_target, abs_dest, global) + else + this.createSymlink(abs_target, abs_dest, global); - this.err = err; - return; - }; - setPermissions(node_modules.fd, dest_path); + if (this.err != null) { + // cleanup on error just in case + unlinkBinOrShim(abs_dest); return; - } else { - const WinBinLinkingShim = @import("./windows-shim/BinLinkingShim.zig"); + } - const node_modules = if (link_global) - this.global_bin_dir - else - this.package_installed_node_modules.asDir(); + if (comptime !Environment.isWindows) { + // any error here is ignored + const bin = bun.sys.File.openat(bun.invalid_fd, abs_target, bun.O.RDWR, 0o664).unwrap() catch return; + defer bin.close(); + + var shebang_buf: [1024]u8 = undefined; + const read = bin.read(&shebang_buf).unwrap() catch return; + const chunk = shebang_buf[0..read]; + // 123 4 5 + // #!a\r\n + if (chunk.len < 5 or chunk[0] != '#' or chunk[1] != '!') return; + + if (strings.indexOfChar(chunk, '\n')) |newline| { + if (newline > 0 and chunk[newline - 1] == '\r') { + const pos = newline - 1; + bin.handle.asFile().seekTo(pos) catch return; + bin.writeAll("\n").unwrap() catch return; + } + } + } + } - var shim_buf: [65536]u8 = undefined; - var read_in_buf: [WinBinLinkingShim.Shebang.max_shebang_input_length]u8 = undefined; - var filename1_buf: bun.WPathBuffer = undefined; - var filename2_buf: bun.WPathBuffer = undefined; - var filename3_buf: bun.WPathBuffer = undefined; + fn createWindowsShim(this: *Linker, abs_target: [:0]const u8, abs_dest: [:0]const u8, global: bool) void { + const WinBinLinkingShim = @import("./windows-shim/BinLinkingShim.zig"); - if (comptime Environment.allow_assert) { - bun.assert(strings.hasPrefixComptime(target_path, "..\\")); + var shim_buf: [65536]u8 = undefined; + var read_in_buf: [WinBinLinkingShim.Shebang.max_shebang_input_length]u8 = undefined; + var dest_buf: bun.WPathBuffer = undefined; + var target_buf: bun.WPathBuffer = undefined; + + const abs_dest_w = strings.convertUTF8toUTF16InBuffer(&dest_buf, abs_dest); + @memcpy(dest_buf[abs_dest_w.len..][0..".bunx\x00".len], comptime strings.literal(u16, ".bunx\x00")); + + const abs_bunx_file: [:0]const u16 = dest_buf[0 .. abs_dest_w.len + ".bunx".len :0]; + + const bunx_file = bun.sys.File.openatOSPath(bun.invalid_fd, abs_bunx_file, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664).unwrap() catch |err| bunx_file: { + if (err != error.ENOENT or global) { + this.err = err; + return; } - const target_wpath = bun.strings.toWPathNormalized(&filename1_buf, target_path[3..]); - var destination_wpath: []u16 = bun.strings.convertUTF8toUTF16InBuffer(&filename2_buf, dest_path); - - destination_wpath.len += 5; - @memcpy(destination_wpath[destination_wpath.len - 5 ..], &[_]u16{ '.', 'b', 'u', 'n', 'x' }); - { - const file = node_modules.createFileW(destination_wpath, .{ - .truncate = true, - .exclusive = true, - }) catch |open_err| fd: { - if (open_err == error.PathAlreadyExists) { - // we need to verify this link is valid, otherwise regenerate it - if (PackageInstall.isDanglingWindowsBinLink(bun.toFD(node_modules.fd), destination_wpath, &shim_buf)) { - break :fd node_modules.createFileW(destination_wpath, .{ - .truncate = true, - }) catch |second_open_err| { - this.err = second_open_err; - return; - }; - } - - // otherwise it is ok to skip the rest - return; - } - this.err = open_err; - return; - }; - defer file.close(); - - const shebang = shebang: { - const first_content_chunk = contents: { - const fd = bun.sys.openatWindows( - this.package_installed_node_modules, - if (link_global) - bun.strings.toWPathNormalized( - &filename3_buf, - target_path[this.relative_path_to_bin_for_windows_global_link_offset..], - ) - else - target_wpath, - bun.O.RDONLY, - ).unwrap() catch break :contents null; - defer _ = bun.sys.close(fd); - const reader = fd.asFile().reader(); - const read = reader.read(&read_in_buf) catch break :contents null; - if (read == 0) { - break :contents null; - } - break :contents read_in_buf[0..read]; - }; + bun.makePath(this.node_modules.asDir(), ".bin") catch {}; + break :bunx_file bun.sys.File.openatOSPath(bun.invalid_fd, abs_bunx_file, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664).unwrap() catch |real_err| { + this.err = real_err; + return; + }; + }; + defer bunx_file.close(); - if (first_content_chunk) |chunk| { - break :shebang WinBinLinkingShim.Shebang.parse(chunk, target_wpath) catch { - this.err = error.InvalidBinContent; - return; - }; - } else { - break :shebang WinBinLinkingShim.Shebang.parseFromBinPath(target_wpath); - } - }; + const rel_target = path.relativeBufZ(this.rel_buf, path.dirname(abs_dest, .auto), abs_target); + bun.assertWithLocation(strings.hasPrefixComptime(rel_target, "..\\"), @src()); - const shim = WinBinLinkingShim{ - .bin_path = target_wpath, - .shebang = shebang, - }; + const rel_target_w = strings.toWPathNormalized(&target_buf, rel_target["..\\".len..]); - const len = shim.encodedLength(); - if (len > shim_buf.len) { - this.err = error.InvalidBinContent; - return; - } - const metadata = shim_buf[0..len]; - shim.encodeInto(metadata) catch { - this.err = error.InvalidBinContent; + const shebang = shebang: { + const first_content_chunk = contents: { + const target = bun.openFileZ(abs_target, .{ .mode = .read_only }) catch |err| { + // it should exist, this error is real + this.err = err; return; }; + defer target.close(); + const reader = target.reader(); + const read = reader.read(&read_in_buf) catch break :contents null; + if (read == 0) break :contents null; + break :contents read_in_buf[0..read]; + }; - file.writer().writeAll(metadata) catch |err| { - this.err = err; + if (first_content_chunk) |chunk| { + break :shebang WinBinLinkingShim.Shebang.parse(chunk, rel_target_w) catch { + this.err = error.InvalidBinCount; return; }; + } else { + break :shebang WinBinLinkingShim.Shebang.parseFromBinPath(rel_target_w); } + }; - destination_wpath.len -= 1; - @memcpy(destination_wpath[destination_wpath.len - 3 ..], &[_]u16{ 'e', 'x', 'e' }); + const shim = WinBinLinkingShim{ + .bin_path = rel_target_w, + .shebang = shebang, + }; - // truncate=false is intentional so that the exe is always rewritten. this helps - // - you upgrade to a new version of bin_shim_impl (unlikely but possible) - // - if otherwise corrupt it yourself - if (node_modules.createFileW(destination_wpath, .{})) |exe_file| { - defer exe_file.close(); - exe_file.writer().writeAll(WinBinLinkingShim.embedded_executable_data) catch |err| { - this.err = err; - return; - }; - } else |err| { - this.err = err; - } + const len = shim.encodedLength(); + if (len > shim_buf.len) { + this.err = error.InvalidBinContent; + return; } - } - const dot_bin = ".bin" ++ std.fs.path.sep_str; - - // It is important that we use symlinkat(2) with relative paths instead of symlink() - // That way, if you move your node_modules folder around, the symlinks in .bin still work - // If we used absolute paths for the symlinks, you'd end up with broken symlinks - pub fn link(this: *Linker, link_global: bool) void { - var target_buf: bun.PathBuffer = undefined; - var dest_buf: bun.PathBuffer = undefined; - var from_remain: []u8 = &target_buf; - var remain: []u8 = &dest_buf; - - if (!link_global) { - const root_dir = this.package_installed_node_modules.asDir(); - const from = root_dir.realpath(dot_bin, &target_buf) catch |realpath_err| brk: { - if (realpath_err == error.FileNotFound) { - if (comptime Environment.isWindows) { - std.posix.mkdiratW(root_dir.fd, comptime bun.OSPathLiteral(".bin"), 0) catch |err| { - this.err = err; - return; - }; - } else { - root_dir.makeDirZ(".bin") catch |err| { - this.err = err; - return; - }; - } + const metadata = shim_buf[0..len]; + shim.encodeInto(metadata) catch { + this.err = error.InvalidBinContent; + return; + }; - break :brk root_dir.realpath(dot_bin, &target_buf) catch |err| { - this.err = err; - return; - }; - } + bunx_file.writer().writeAll(metadata) catch |err| { + this.err = err; + return; + }; - this.err = realpath_err; - return; - }; - const to = bun.getFdPath(this.package_installed_node_modules, &dest_buf) catch |err| { - this.err = err; - return; - }; - const rel = Path.relative(from, to); - bun.copy(u8, remain, rel); - remain = remain[rel.len..]; - remain[0] = std.fs.path.sep; - remain = remain[1..]; - from_remain[0..dot_bin.len].* = dot_bin.*; - from_remain = from_remain[dot_bin.len..]; - } else { - if (bun.toFD(this.global_bin_dir.fd) == bun.invalid_fd) { - this.err = error.MissingGlobalBinDir; + @memcpy(dest_buf[abs_dest_w.len..][0..".exe\x00".len], comptime strings.literal(u16, ".exe\x00")); + const abs_exe_file: [:0]const u16 = dest_buf[0 .. abs_dest_w.len + ".exe".len :0]; + + bun.sys.File.writeFile(bun.invalid_fd, abs_exe_file, WinBinLinkingShim.embedded_executable_data).unwrap() catch |err| { + if (err == error.EBUSY) { + // exe is most likely running. bunx file has already been updated, ignore error return; } - if (comptime Environment.isWindows) { - const from = this.global_bin_path; - const to = bun.getFdPath(this.package_installed_node_modules, &dest_buf) catch |err| { - this.err = err; + this.err = err; + return; + }; + } + + fn createSymlink(this: *Linker, abs_target: [:0]const u8, abs_dest: [:0]const u8, global: bool) void { + defer { + if (this.err == null) { + _ = bun.sys.chmod(abs_target, umask | 0o777); + } + } + + const abs_dest_dir = path.dirname(abs_dest, .auto); + const rel_target = path.relativeBufZ(this.rel_buf, abs_dest_dir, abs_target); + + bun.assertWithLocation(strings.hasPrefixComptime(rel_target, ".."), @src()); + + switch (bun.sys.symlink(rel_target, abs_dest)) { + .err => |err| { + if (err.getErrno() != .EXIST and err.getErrno() != .NOENT) { + this.err = err.toZigErr(); return; - }; + } - const rel = Path.relative(from, to); - @memcpy(remain[0..rel.len], rel); - remain = remain[rel.len..]; - remain[0] = std.fs.path.sep; - remain = remain[1..]; - } else { - bun.copy(u8, &target_buf, this.global_bin_path); - from_remain = target_buf[this.global_bin_path.len..]; - from_remain[0] = std.fs.path.sep; - from_remain = from_remain[1..]; - const abs = bun.getFdPath(this.root_node_modules_folder, &dest_buf) catch |err| { - this.err = err; + // ENOENT means `.bin` hasn't been created yet. Should only happen if this isn't global + if (err.getErrno() == .NOENT) { + if (global) { + this.err = err.toZigErr(); + return; + } + + bun.makePath(this.node_modules.asDir(), ".bin") catch {}; + switch (bun.sys.symlink(rel_target, abs_dest)) { + .err => |real_error| { + // It was just created, no need to delete destination and symlink again + this.err = real_error.toZigErr(); + return; + }, + .result => return, + } + bun.sys.symlink(rel_target, abs_dest).unwrap() catch |real_err| { + this.err = real_err; + }; return; - }; - remain = remain[abs.len..]; - remain[0] = std.fs.path.sep; - remain = remain[1..]; - } + } - this.root_node_modules_folder = bun.toFD(this.global_bin_dir.fd); + // beyond this error can only be `.EXIST` + bun.assertWithLocation(err.getErrno() == .EXIST, @src()); + }, + .result => return, } - if (comptime Environment.isWindows and link_global) { - this.relative_path_to_bin_for_windows_global_link_offset = dest_buf.len - remain.len; - } + // delete and try again + std.fs.deleteTreeAbsolute(abs_dest) catch {}; + bun.sys.symlink(rel_target, abs_dest).unwrap() catch |err| { + this.err = err; + }; + } + + /// uses `this.abs_target_buf` + pub fn buildTargetPackageDir(this: *const Linker) []const u8 { + const dest_dir_without_trailing_slash = strings.withoutTrailingSlash(this.node_modules_path); - const name = this.package_name.slice(); - bun.copy(u8, remain, name); - remain = remain[name.len..]; + var remain = this.abs_target_buf; + + @memcpy(remain[0..dest_dir_without_trailing_slash.len], dest_dir_without_trailing_slash); + remain = remain[dest_dir_without_trailing_slash.len..]; remain[0] = std.fs.path.sep; + remain = remain[1..]; + const package_name = this.package_name.slice(); + @memcpy(remain[0..package_name.len], package_name); + remain = remain[package_name.len..]; + remain[0] = std.fs.path.sep; remain = remain[1..]; + return this.abs_target_buf[0 .. @intFromPtr(remain.ptr) - @intFromPtr(this.abs_target_buf.ptr)]; + } + + pub fn buildDestinationDir(this: *const Linker, global: bool) []u8 { + const dest_dir_without_trailing_slash = strings.withoutTrailingSlash(this.node_modules_path); + + var remain = this.abs_dest_buf; + if (global) { + const global_bin_path_without_trailing_slash = strings.withoutTrailingSlash(this.global_bin_path); + @memcpy(remain[0..global_bin_path_without_trailing_slash.len], global_bin_path_without_trailing_slash); + remain = remain[global_bin_path_without_trailing_slash.len..]; + remain[0] = std.fs.path.sep; + remain = remain[1..]; + } else { + @memcpy(remain[0..dest_dir_without_trailing_slash.len], dest_dir_without_trailing_slash); + remain = remain[dest_dir_without_trailing_slash.len..]; + @memcpy(remain[0.."/.bin/".len], std.fs.path.sep_str ++ ".bin" ++ std.fs.path.sep_str); + remain = remain["/.bin/".len..]; + } + + return remain; + } + + pub fn link(this: *Linker, global: bool) void { + const package_dir = this.buildTargetPackageDir(); + var abs_dest_buf_remain = this.buildDestinationDir(global); + + bun.assertWithLocation(this.bin.tag != .none, @src()); + switch (this.bin.tag) { - .none => { - if (Environment.allow_assert) { - @panic("unexpected .null when linking binary"); - } - }, + .none => {}, .file => { - var target = this.bin.value.file.slice(this.string_buf); + const target = this.bin.value.file.slice(this.string_buf); + if (target.len == 0) return; - if (strings.hasPrefixComptime(target, "./")) { - target = target["./".len..]; - } - bun.copy(u8, remain, target); - remain = remain[target.len..]; - remain[0] = 0; - const target_len = @intFromPtr(remain.ptr) - @intFromPtr(&dest_buf); - remain = remain[1..]; - - const target_path: [:0]u8 = dest_buf[0..target_len :0]; - // we need to use the unscoped package name here - // this is why @babel/parser would fail to link - const unscoped_name = unscopedPackageName(name); - bun.copy(u8, from_remain, unscoped_name); - from_remain = from_remain[unscoped_name.len..]; - from_remain[0] = 0; - const dest_path: [:0]u8 = target_buf[0 .. @intFromPtr(from_remain.ptr) - @intFromPtr(&target_buf) :0]; - - this.setSymlinkAndPermissions(target_path, dest_path, link_global); + // for normalizing `target` + const abs_target = path.joinAbsStringZ(package_dir, &.{target}, .auto); + + const unscoped_package_name = Dependency.unscopedPackageName(this.package_name.slice()); + @memcpy(abs_dest_buf_remain[0..unscoped_package_name.len], unscoped_package_name); + abs_dest_buf_remain = abs_dest_buf_remain[unscoped_package_name.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; + + this.linkBinOrCreateShim(abs_target, abs_dest, global); }, .named_file => { - var target = this.bin.value.named_file[1].slice(this.string_buf); - if (strings.hasPrefixComptime(target, "./")) { - target = target["./".len..]; - } - bun.copy(u8, remain, target); - remain = remain[target.len..]; - remain[0] = 0; - const target_len = @intFromPtr(remain.ptr) - @intFromPtr(&dest_buf); - remain = remain[1..]; - - const target_path: [:0]u8 = dest_buf[0..target_len :0]; - const name_to_use = this.bin.value.named_file[0].slice(this.string_buf); - bun.copy(u8, from_remain, name_to_use); - from_remain = from_remain[name_to_use.len..]; - from_remain[0] = 0; - const dest_path: [:0]u8 = target_buf[0 .. @intFromPtr(from_remain.ptr) - @intFromPtr(&target_buf) :0]; - - this.setSymlinkAndPermissions(target_path, dest_path, link_global); + const name = this.bin.value.named_file[0].slice(this.string_buf); + const normalized_name = normalizedBinName(name); + const target = this.bin.value.named_file[1].slice(this.string_buf); + if (normalized_name.len == 0 or target.len == 0) return; + + // for normalizing `target` + const abs_target = path.joinAbsStringZ(package_dir, &.{target}, .auto); + + @memcpy(abs_dest_buf_remain[0..normalized_name.len], normalized_name); + abs_dest_buf_remain = abs_dest_buf_remain[normalized_name.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; + + this.linkBinOrCreateShim(abs_target, abs_dest, global); }, .map => { - var extern_string_i: u32 = this.bin.value.map.off; - const end = this.bin.value.map.len + extern_string_i; - const _from_remain = from_remain; - const _remain = remain; - - while (extern_string_i < end) : (extern_string_i += 2) { - from_remain = _from_remain; - remain = _remain; - const name_in_terminal = this.extern_string_buf[extern_string_i]; - const name_in_filesystem = this.extern_string_buf[extern_string_i + 1]; - - var target = name_in_filesystem.slice(this.string_buf); - if (strings.hasPrefixComptime(target, "./")) { - target = target["./".len..]; - } - bun.copy(u8, remain, target); - remain = remain[target.len..]; - remain[0] = 0; - const target_len = @intFromPtr(remain.ptr) - @intFromPtr(&dest_buf); - remain = remain[1..]; - - const target_path: [:0]u8 = dest_buf[0..target_len :0]; - const name_to_use = name_in_terminal.slice(this.string_buf); - bun.copy(u8, from_remain, name_to_use); - from_remain = from_remain[name_to_use.len..]; - from_remain[0] = 0; - const dest_path: [:0]u8 = target_buf[0 .. @intFromPtr(from_remain.ptr) - @intFromPtr(&target_buf) :0]; - - this.setSymlinkAndPermissions(target_path, dest_path, link_global); - } - }, - .dir => { - var target = this.bin.value.dir.slice(this.string_buf); - if (strings.hasPrefixComptime(target, "./")) { - target = target["./".len..]; - } + var i = this.bin.value.map.begin(); + const end = this.bin.value.map.end(); - var parts = [_][]const u8{ name, target }; + const abs_dest_dir_end = abs_dest_buf_remain; - bun.copy(u8, remain, target); - remain = remain[target.len..]; + while (i < end) : (i += 2) { + const bin_dest = this.extern_string_buf[i].slice(this.string_buf); + const normalized_bin_dest = normalizedBinName(bin_dest); + const bin_target = this.extern_string_buf[i + 1].slice(this.string_buf); + if (bin_target.len == 0 or normalized_bin_dest.len == 0) continue; - const dir = this.package_installed_node_modules.asDir(); + const abs_target = path.joinAbsStringZ(package_dir, &.{bin_target}, .auto); - var joined = Path.joinStringBuf(&target_buf, &parts, .auto); - @as([*]u8, @ptrFromInt(@intFromPtr(joined.ptr)))[joined.len] = 0; - const joined_: [:0]const u8 = joined.ptr[0..joined.len :0]; - var child_dir = bun.openDir(dir, joined_) catch |err| { - this.err = err; - return; - }; - defer child_dir.close(); + abs_dest_buf_remain = abs_dest_dir_end; + @memcpy(abs_dest_buf_remain[0..normalized_bin_dest.len], normalized_bin_dest); + abs_dest_buf_remain = abs_dest_buf_remain[normalized_bin_dest.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; - var iter = child_dir.iterate(); + this.linkBinOrCreateShim(abs_target, abs_dest, global); + } + }, + .dir => { + const target = this.bin.value.dir.slice(this.string_buf); + if (target.len == 0) return; + + // for normalizing `target` + const abs_target_dir = path.joinAbsStringZ(package_dir, &.{target}, .auto); - const basedir_path = bun.getFdPath(child_dir.fd, &target_buf) catch |err| { + var target_dir = bun.openDirAbsolute(abs_target_dir) catch |err| { + if (err == error.ENOENT) { + // https://github.com/npm/cli/blob/366c07e2f3cb9d1c6ddbd03e624a4d73fbd2676e/node_modules/bin-links/lib/link-gently.js#L43 + // avoid erroring when the directory does not exist + return; + } this.err = err; return; }; - target_buf[basedir_path.len] = std.fs.path.sep; - var target_buf_remain = target_buf[basedir_path.len + 1 ..]; - const prev_target_buf_remain = target_buf_remain; + defer target_dir.close(); + + const abs_dest_dir_end = abs_dest_buf_remain; - while (iter.next() catch null) |entry_| { - const entry: std.fs.Dir.Entry = entry_; + var iter = target_dir.iterate(); + while (iter.next() catch null) |entry| { switch (entry.kind) { - std.fs.Dir.Entry.Kind.sym_link, std.fs.Dir.Entry.Kind.file => { - target_buf_remain = prev_target_buf_remain; - bun.copy(u8, target_buf_remain, entry.name); - target_buf_remain = target_buf_remain[entry.name.len..]; - target_buf_remain[0] = 0; - const from_path: [:0]u8 = target_buf[0 .. @intFromPtr(target_buf_remain.ptr) - @intFromPtr(&target_buf) :0]; - const to_path = if (!link_global) - std.fmt.bufPrintZ(&dest_buf, dot_bin ++ "{s}", .{entry.name}) catch continue - else - std.fmt.bufPrintZ(&dest_buf, "{s}", .{entry.name}) catch continue; - - this.setSymlinkAndPermissions(from_path, to_path, link_global); + .sym_link, .file => { + // `this.abs_target_buf` is available now because `path.joinAbsStringZ` copied everything into `parse_join_input_buffer` + const abs_target = path.joinAbsStringBufZ(abs_target_dir, this.abs_target_buf, &.{entry.name}, .auto); + + abs_dest_buf_remain = abs_dest_dir_end; + @memcpy(abs_dest_buf_remain[0..entry.name.len], entry.name); + abs_dest_buf_remain = abs_dest_buf_remain[entry.name.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; + + this.linkBinOrCreateShim(abs_target, abs_dest, global); }, else => {}, } @@ -697,149 +695,84 @@ pub const Bin = extern struct { } } - pub fn unlink(this: *Linker, link_global: bool) void { - var target_buf: bun.PathBuffer = undefined; - var dest_buf: bun.PathBuffer = undefined; - var from_remain: []u8 = &target_buf; - var remain: []u8 = &dest_buf; - - if (!link_global) { - target_buf[0..dot_bin.len].* = dot_bin.*; - from_remain = target_buf[dot_bin.len..]; - dest_buf[0.."../".len].* = "../".*; - remain = dest_buf["../".len..]; - } else { - if (bun.toFD(this.global_bin_dir.fd) == bun.invalid_fd) { - this.err = error.MissingGlobalBinDir; - return; - } + pub fn unlink(this: *Linker, global: bool) void { + const package_dir = this.buildTargetPackageDir(); + var abs_dest_buf_remain = this.buildDestinationDir(global); - @memcpy(target_buf[0..this.global_bin_path.len], this.global_bin_path); - from_remain = target_buf[this.global_bin_path.len..]; - from_remain[0] = std.fs.path.sep; - from_remain = from_remain[1..]; - const abs = bun.getFdPath(this.root_node_modules_folder, &dest_buf) catch |err| { - this.err = err; - return; - }; - remain = remain[abs.len..]; - remain[0] = std.fs.path.sep; - remain = remain[1..]; - - this.root_node_modules_folder = bun.toFD(this.global_bin_dir.fd); - } - - const name = this.package_name.slice(); - bun.copy(u8, remain, name); - remain = remain[name.len..]; - remain[0] = std.fs.path.sep; - remain = remain[1..]; + bun.assertWithLocation(this.bin.tag != .none, @src()); switch (this.bin.tag) { - .none => { - if (comptime Environment.isDebug) { - unreachable; - } - }, + .none => {}, .file => { - // we need to use the unscoped package name here - // this is why @babel/parser would fail to link - const unscoped_name = unscopedPackageName(name); - bun.copy(u8, from_remain, unscoped_name); - from_remain = from_remain[unscoped_name.len..]; - from_remain[0] = 0; - const dest_path: [:0]u8 = target_buf[0 .. @intFromPtr(from_remain.ptr) - @intFromPtr(&target_buf) :0]; - - std.posix.unlinkatZ(this.root_node_modules_folder.cast(), dest_path, 0) catch {}; + const unscoped_package_name = Dependency.unscopedPackageName(this.package_name.slice()); + @memcpy(abs_dest_buf_remain[0..unscoped_package_name.len], unscoped_package_name); + abs_dest_buf_remain = abs_dest_buf_remain[unscoped_package_name.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; + + unlinkBinOrShim(abs_dest); }, .named_file => { - const name_to_use = this.bin.value.named_file[0].slice(this.string_buf); - bun.copy(u8, from_remain, name_to_use); - from_remain = from_remain[name_to_use.len..]; - from_remain[0] = 0; - const dest_path: [:0]u8 = target_buf[0 .. @intFromPtr(from_remain.ptr) - @intFromPtr(&target_buf) :0]; + const name = this.bin.value.named_file[0].slice(this.string_buf); + const normalized_name = normalizedBinName(name); + if (normalized_name.len == 0) return; - std.posix.unlinkatZ(this.root_node_modules_folder.cast(), dest_path, 0) catch {}; + @memcpy(abs_dest_buf_remain[0..normalized_name.len], normalized_name); + abs_dest_buf_remain = abs_dest_buf_remain[normalized_name.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; + + unlinkBinOrShim(abs_dest); }, .map => { - var extern_string_i: u32 = this.bin.value.map.off; - const end = this.bin.value.map.len + extern_string_i; - const _from_remain = from_remain; - const _remain = remain; - while (extern_string_i < end) : (extern_string_i += 2) { - from_remain = _from_remain; - remain = _remain; - const name_in_terminal = this.extern_string_buf[extern_string_i]; - const name_in_filesystem = this.extern_string_buf[extern_string_i + 1]; - - var target = name_in_filesystem.slice(this.string_buf); - if (strings.hasPrefix(target, "./")) { - target = target[2..]; - } - bun.copy(u8, remain, target); - remain = remain[target.len..]; - remain[0] = 0; - remain = remain[1..]; - - const name_to_use = name_in_terminal.slice(this.string_buf); - bun.copy(u8, from_remain, name_to_use); - from_remain = from_remain[name_to_use.len..]; - from_remain[0] = 0; - const dest_path: [:0]u8 = target_buf[0 .. @intFromPtr(from_remain.ptr) - @intFromPtr(&target_buf) :0]; - - std.posix.unlinkatZ(this.root_node_modules_folder.cast(), dest_path, 0) catch {}; - } - }, - .dir => { - var target = this.bin.value.dir.slice(this.string_buf); - if (strings.hasPrefix(target, "./")) { - target = target[2..]; - } + var i = this.bin.value.map.begin(); + const end = this.bin.value.map.end(); - var parts = [_][]const u8{ name, target }; + const abs_dest_dir_end = abs_dest_buf_remain; - bun.copy(u8, remain, target); - remain = remain[target.len..]; + while (i < end) : (i += 2) { + const bin_dest = this.extern_string_buf[i].slice(this.string_buf); + const normalized_bin_dest = normalizedBinName(bin_dest); + if (normalized_bin_dest.len == 0) continue; - const dir = this.package_installed_node_modules.asDir(); + abs_dest_buf_remain = abs_dest_dir_end; + @memcpy(abs_dest_buf_remain[0..normalized_bin_dest.len], normalized_bin_dest); + abs_dest_buf_remain = abs_dest_buf_remain[normalized_bin_dest.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; - var joined = Path.joinStringBuf(&target_buf, &parts, .auto); - @as([*]u8, @ptrFromInt(@intFromPtr(joined.ptr)))[joined.len] = 0; - const joined_: [:0]const u8 = joined.ptr[0..joined.len :0]; - var child_dir = bun.openDir(dir, joined_) catch |err| { - this.err = err; - return; - }; - defer child_dir.close(); + unlinkBinOrShim(abs_dest); + } + }, + .dir => { + const target = this.bin.value.dir.slice(this.string_buf); + if (target.len == 0) return; - var iter = child_dir.iterate(); + const abs_target_dir = path.joinAbsStringZ(package_dir, &.{target}, .auto); - const basedir_path = bun.getFdPath(child_dir.fd, &target_buf) catch |err| { + var target_dir = bun.openDirAbsolute(abs_target_dir) catch |err| { this.err = err; return; }; - target_buf[basedir_path.len] = std.fs.path.sep; - var target_buf_remain = target_buf[basedir_path.len + 1 ..]; - const prev_target_buf_remain = target_buf_remain; + defer target_dir.close(); + + const abs_dest_dir_end = abs_dest_buf_remain; - while (iter.next() catch null) |entry_| { - const entry: std.fs.Dir.Entry = entry_; + var iter = target_dir.iterate(); + while (iter.next() catch null) |entry| { switch (entry.kind) { - std.fs.Dir.Entry.Kind.sym_link, std.fs.Dir.Entry.Kind.file => { - target_buf_remain = prev_target_buf_remain; - bun.copy(u8, target_buf_remain, entry.name); - target_buf_remain = target_buf_remain[entry.name.len..]; - target_buf_remain[0] = 0; - const to_path = if (!link_global) - std.fmt.bufPrintZ(&dest_buf, dot_bin ++ "{s}", .{entry.name}) catch continue - else - std.fmt.bufPrintZ(&dest_buf, "{s}", .{entry.name}) catch continue; - - std.posix.unlinkatZ( - this.root_node_modules_folder.cast(), - to_path, - 0, - ) catch continue; + .sym_link, .file => { + abs_dest_buf_remain = abs_dest_dir_end; + @memcpy(abs_dest_buf_remain[0..entry.name.len], entry.name); + abs_dest_buf_remain = abs_dest_buf_remain[entry.name.len..]; + abs_dest_buf_remain[0] = 0; + const abs_dest_len = @intFromPtr(abs_dest_buf_remain.ptr) - @intFromPtr(this.abs_dest_buf.ptr); + const abs_dest: [:0]const u8 = this.abs_dest_buf[0..abs_dest_len :0]; + + unlinkBinOrShim(abs_dest); }, else => {}, } diff --git a/src/install/dependency.zig b/src/install/dependency.zig index 1b9707cdb676a..d21b8be3198d9 100644 --- a/src/install/dependency.zig +++ b/src/install/dependency.zig @@ -15,6 +15,7 @@ const std = @import("std"); const string = @import("../string_types.zig").string; const strings = @import("../string_immutable.zig"); const Dependency = @This(); +const JSC = bun.JSC; const URI = union(Tag) { local: String, @@ -187,6 +188,8 @@ pub inline fn isSCPLikePath(dependency: string) bool { return false; } +/// `isGitHubShorthand` from npm +/// https://github.com/npm/cli/blob/22731831e22011e32fa0ca12178e242c2ee2b33d/node_modules/hosted-git-info/lib/from-url.js#L6 pub inline fn isGitHubRepoPath(dependency: string) bool { // Shortest valid expression: u/r if (dependency.len < 3) return false; @@ -274,11 +277,78 @@ pub fn splitNameAndVersion(str: string) struct { string, ?string } { return .{ str, null }; } +pub fn unscopedPackageName(name: []const u8) []const u8 { + if (name[0] != '@') return name; + var name_ = name; + name_ = name[1..]; + return name_[(strings.indexOfChar(name_, '/') orelse return name) + 1 ..]; +} + pub const Version = struct { tag: Tag = .uninitialized, literal: String = .{}, value: Value = .{ .uninitialized = {} }, + pub fn toJS(dep: *const Version, buf: []const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + const object = JSC.JSValue.createEmptyObject(globalThis, 2); + object.put(globalThis, "type", bun.String.static(@tagName(dep.tag)).toJS(globalThis)); + + switch (dep.tag) { + .dist_tag => { + object.put(globalThis, "name", dep.value.dist_tag.name.toJS(buf, globalThis)); + object.put(globalThis, "tag", dep.value.dist_tag.tag.toJS(buf, globalThis)); + }, + .folder => { + object.put(globalThis, "folder", dep.value.folder.toJS(buf, globalThis)); + }, + .git => { + object.put(globalThis, "owner", dep.value.git.owner.toJS(buf, globalThis)); + object.put(globalThis, "repo", dep.value.git.repo.toJS(buf, globalThis)); + object.put(globalThis, "ref", dep.value.git.committish.toJS(buf, globalThis)); + }, + .github => { + object.put(globalThis, "owner", dep.value.github.owner.toJS(buf, globalThis)); + object.put(globalThis, "repo", dep.value.github.repo.toJS(buf, globalThis)); + object.put(globalThis, "ref", dep.value.github.committish.toJS(buf, globalThis)); + }, + .npm => { + object.put(globalThis, "name", dep.value.npm.name.toJS(buf, globalThis)); + var version_str = bun.String.createFormat("{}", .{dep.value.npm.version.fmt(buf)}) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + object.put( + globalThis, + "version", + version_str.transferToJS(globalThis), + ); + object.put(globalThis, "alias", JSC.JSValue.jsBoolean(dep.value.npm.is_alias)); + }, + .symlink => { + object.put(globalThis, "path", dep.value.symlink.toJS(buf, globalThis)); + }, + .workspace => { + object.put(globalThis, "name", dep.value.workspace.toJS(buf, globalThis)); + }, + .tarball => { + object.put(globalThis, "name", dep.value.tarball.package_name.toJS(buf, globalThis)); + switch (dep.value.tarball.uri) { + .local => |*local| { + object.put(globalThis, "path", local.toJS(buf, globalThis)); + }, + .remote => |*remote| { + object.put(globalThis, "url", remote.toJS(buf, globalThis)); + }, + } + }, + else => { + globalThis.throwTODO("Unsupported dependency type"); + return .zero; + }, + } + + return object; + } pub inline fn npm(this: *const Version) ?NpmInfo { return if (this.tag == .npm) this.value.npm else null; } @@ -403,6 +473,18 @@ pub const Version = struct { /// GitHub Repository (via REST API) github = 8, + pub const map = bun.ComptimeStringMap(Tag, .{ + .{ "npm", .npm }, + .{ "dist_tag", .dist_tag }, + .{ "tarball", .tarball }, + .{ "folder", .folder }, + .{ "symlink", .symlink }, + .{ "workspace", .workspace }, + .{ "git", .git }, + .{ "github", .github }, + }); + pub const fromJS = map.fromJS; + pub fn cmp(this: Tag, other: Tag) std.math.Order { // TODO: align with yarn return std.math.order(@intFromEnum(this), @intFromEnum(other)); @@ -577,7 +659,6 @@ pub const Version = struct { } } - if (url.len > 4 and strings.eqlComptime(url[0.."git@".len], "git@")) { url = url["git@".len..]; } @@ -662,6 +743,17 @@ pub const Version = struct { return .npm; } + + pub fn inferFromJS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(1).slice(); + if (arguments.len == 0 or !arguments[0].isString()) { + return .undefined; + } + + const tag = Tag.fromJS(globalObject, arguments[0]) orelse return .undefined; + var str = bun.String.init(@tagName(tag)); + return str.transferToJS(globalObject); + } }; pub const NpmInfo = struct { @@ -817,17 +909,9 @@ pub fn parseWithTag( input, sliced.sub(input), ) catch |err| { - if (log_) |log| log.addErrorFmt( - null, - logger.Loc.Empty, - allocator, - "{s} parsing version \"{s}\"", - .{ - @errorName(err), - dependency, - }, - ) catch unreachable; - return null; + switch (err) { + error.OutOfMemory => bun.outOfMemory(), + } }; const result = Version{ @@ -1131,6 +1215,65 @@ pub fn parseWithTag( } } +pub fn fromJS(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { + const arguments = callframe.arguments(2).slice(); + if (arguments.len == 1) { + return bun.install.PackageManager.UpdateRequest.fromJS(globalThis, arguments[0]); + } + var arena = std.heap.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + var stack = std.heap.stackFallback(1024, arena.allocator()); + const allocator = stack.get(); + + const alias_value = if (arguments.len > 0) arguments[0] else .undefined; + + if (!alias_value.isString()) { + return .undefined; + } + const alias_slice = alias_value.toSlice(globalThis, allocator); + defer alias_slice.deinit(); + + if (alias_slice.len == 0) { + return .undefined; + } + + const name_value = if (arguments.len > 1) arguments[1] else .undefined; + const name_slice = name_value.toSlice(globalThis, allocator); + defer name_slice.deinit(); + + var name = alias_slice.slice(); + var alias = alias_slice.slice(); + + var buf = alias; + + if (name_value.isString()) { + var builder = bun.StringBuilder.initCapacity(allocator, name_slice.len + alias_slice.len) catch bun.outOfMemory(); + name = builder.append(name_slice.slice()); + alias = builder.append(alias_slice.slice()); + buf = builder.allocatedSlice(); + } + + var log = logger.Log.init(allocator); + const sliced = SlicedString.init(buf, name); + + const dep: Version = Dependency.parse(allocator, SlicedString.init(buf, alias).value(), null, buf, &sliced, &log) orelse { + if (log.msgs.items.len > 0) { + globalThis.throwValue(log.toJS(globalThis, bun.default_allocator, "Failed to parse dependency")); + return .zero; + } + + return .undefined; + }; + + if (log.msgs.items.len > 0) { + globalThis.throwValue(log.toJS(globalThis, bun.default_allocator, "Failed to parse dependency")); + return .zero; + } + log.deinit(); + + return dep.toJS(buf, globalThis); +} + pub const Behavior = packed struct(u8) { pub const uninitialized: Behavior = .{}; diff --git a/src/install/extract_tarball.zig b/src/install/extract_tarball.zig index c4ac31829e633..da159b0d56d6c 100644 --- a/src/install/extract_tarball.zig +++ b/src/install/extract_tarball.zig @@ -160,7 +160,13 @@ threadlocal var json_path_buf: bun.PathBuffer = undefined; fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractData { const tmpdir = this.temp_dir; var tmpname_buf: if (Environment.isWindows) bun.WPathBuffer else bun.PathBuffer = undefined; - const name = this.name.slice(); + const name = if (this.name.slice().len > 0) this.name.slice() else brk: { + // Not sure where this case hits yet. + // BUN-2WQ + Output.warn("Extracting nameless packages is not supported yet. Please open an issue on GitHub with reproduction steps.", .{}); + bun.debugAssert(false); + break :brk "unnamed-package"; + }; const basename = brk: { var tmp = name; if (tmp[0] == '@') { @@ -175,10 +181,6 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD } } - if (comptime Environment.allow_assert) { - bun.assert(tmp.len > 0); - } - break :brk tmp; }; @@ -198,28 +200,70 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD defer extract_destination.close(); - if (PackageManager.verbose_install) { - Output.prettyErrorln("[{s}] Start extracting {s}", .{ name, tmpname }); - Output.flush(); - } - const Archive = @import("../libarchive/libarchive.zig").Archive; const Zlib = @import("../zlib.zig"); var zlib_pool = Npm.Registry.BodyPool.get(default_allocator); zlib_pool.data.reset(); defer Npm.Registry.BodyPool.release(zlib_pool); - var zlib_entry = try Zlib.ZlibReaderArrayList.init(tgz_bytes, &zlib_pool.data.list, default_allocator); - zlib_entry.readAll() catch |err| { - this.package_manager.log.addErrorFmt( - null, - logger.Loc.Empty, - this.package_manager.allocator, - "{s} decompressing \"{s}\" to \"{}\"", - .{ @errorName(err), name, bun.fmt.fmtPath(u8, std.mem.span(tmpname), .{}) }, - ) catch unreachable; - return error.InstallFailed; - }; + var esimated_output_size: usize = 0; + + const time_started_for_verbose_logs: u64 = if (PackageManager.verbose_install) bun.getRoughTickCount().ns() else 0; + + { + // Last 4 bytes of a gzip-compressed file are the uncompressed size. + if (tgz_bytes.len > 16) { + // If the file claims to be larger than 16 bytes and smaller than 64 MB, we'll preallocate the buffer. + // If it's larger than that, we'll do it incrementally. We want to avoid OOMing. + const last_4_bytes: u32 = @bitCast(tgz_bytes[tgz_bytes.len - 4 ..][0..4].*); + if (last_4_bytes > 16 and last_4_bytes < 64 * 1024 * 1024) { + // It's okay if this fails. We will just allocate as we go and that will error if we run out of memory. + esimated_output_size = last_4_bytes; + if (zlib_pool.data.list.capacity == 0) { + zlib_pool.data.list.ensureTotalCapacityPrecise(zlib_pool.data.allocator, last_4_bytes) catch {}; + } else { + zlib_pool.data.ensureUnusedCapacity(last_4_bytes) catch {}; + } + } + } + } + + var needs_to_decompress = true; + if (bun.FeatureFlags.isLibdeflateEnabled() and zlib_pool.data.list.capacity > 16 and esimated_output_size > 0) use_libdeflate: { + const decompressor = bun.libdeflate.Decompressor.alloc() orelse break :use_libdeflate; + defer decompressor.deinit(); + + const result = decompressor.gzip(tgz_bytes, zlib_pool.data.list.allocatedSlice()); + + if (result.status == .success) { + zlib_pool.data.list.items.len = result.written; + needs_to_decompress = false; + } + + // If libdeflate fails for any reason, fallback to zlib. + } + + if (needs_to_decompress) { + zlib_pool.data.list.clearRetainingCapacity(); + var zlib_entry = try Zlib.ZlibReaderArrayList.init(tgz_bytes, &zlib_pool.data.list, default_allocator); + zlib_entry.readAll() catch |err| { + this.package_manager.log.addErrorFmt( + null, + logger.Loc.Empty, + this.package_manager.allocator, + "{s} decompressing \"{s}\" to \"{}\"", + .{ @errorName(err), name, bun.fmt.fmtPath(u8, std.mem.span(tmpname), .{}) }, + ) catch unreachable; + return error.InstallFailed; + }; + } + + if (PackageManager.verbose_install) { + const decompressing_ended_at: u64 = bun.getRoughTickCount().ns(); + const elapsed = decompressing_ended_at - time_started_for_verbose_logs; + Output.prettyErrorln("[{s}] Extract {s} (decompressed {} tgz file in {})", .{ name, tmpname, bun.fmt.size(tgz_bytes.len, .{}), bun.fmt.fmtDuration(elapsed) }); + } + switch (this.resolution.tag) { .github => { const DirnameReader = struct { @@ -278,7 +322,8 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD } if (PackageManager.verbose_install) { - Output.prettyErrorln("[{s}] Extracted", .{name}); + const elapsed = bun.getRoughTickCount().ns() - time_started_for_verbose_logs; + Output.prettyErrorln("[{s}] Extracted to {s} ({})", .{ name, tmpname, bun.fmt.fmtDuration(elapsed) }); Output.flush(); } } @@ -398,7 +443,13 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD } } - if (bun.sys.renameatConcurrently(bun.toFD(tmpdir.fd), src, bun.toFD(cache_dir.fd), folder_name).asErr()) |err| { + if (bun.sys.renameatConcurrently( + bun.toFD(tmpdir.fd), + src, + bun.toFD(cache_dir.fd), + folder_name, + .{ .move_fallback = true }, + ).asErr()) |err| { this.package_manager.log.addErrorFmt( null, logger.Loc.Empty, diff --git a/src/install/install.zig b/src/install/install.zig index 3adb36936b228..3b12363ff8d2e 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -46,7 +46,7 @@ const HeaderBuilder = HTTP.HeaderBuilder; const Integrity = @import("./integrity.zig").Integrity; const clap = bun.clap; const ExtractTarball = @import("./extract_tarball.zig"); -const Npm = @import("./npm.zig"); +pub const Npm = @import("./npm.zig"); const Bitset = bun.bit_set.DynamicBitSetUnmanaged; const z_allocator = @import("../memory_allocator.zig").z_allocator; const Syscall = bun.sys; @@ -95,8 +95,8 @@ pub fn initializeStore() void { } initialized_store = true; - JSAst.Expr.Data.Store.create(default_allocator); - JSAst.Stmt.Data.Store.create(default_allocator); + JSAst.Expr.Data.Store.create(); + JSAst.Stmt.Data.Store.create(); } /// The default store we use pre-allocates around 16 MB of memory per thread @@ -140,8 +140,8 @@ const String = Semver.String; const GlobalStringBuilder = @import("../string_builder.zig"); const SlicedString = Semver.SlicedString; const Repository = @import("./repository.zig").Repository; -const Bin = @import("./bin.zig").Bin; -const Dependency = @import("./dependency.zig"); +pub const Bin = @import("./bin.zig").Bin; +pub const Dependency = @import("./dependency.zig"); const Behavior = @import("./dependency.zig").Behavior; const FolderResolution = @import("./resolvers/folder_resolver.zig").FolderResolution; @@ -426,10 +426,10 @@ const NetworkTask = struct { this.allocator = allocator; const url = URL.parse(this.url_buf); - this.http = AsyncHTTP.init(allocator, .GET, url, header_builder.entries, header_builder.content.ptr.?[0..header_builder.content.len], &this.response_buffer, "", 0, this.getCompletionCallback(), HTTP.FetchRedirect.follow, .{ + this.http = AsyncHTTP.init(allocator, .GET, url, header_builder.entries, header_builder.content.ptr.?[0..header_builder.content.len], &this.response_buffer, "", this.getCompletionCallback(), HTTP.FetchRedirect.follow, .{ .http_proxy = this.package_manager.httpProxy(url), }); - this.http.client.reject_unauthorized = this.package_manager.tlsRejectUnauthorized(); + this.http.client.flags.reject_unauthorized = this.package_manager.tlsRejectUnauthorized(); if (PackageManager.verbose_install) { this.http.client.verbose = .headers; @@ -449,7 +449,7 @@ const NetworkTask = struct { // Incase the ETag causes invalidation, we fallback to the last modified date. if (last_modified.len != 0 and bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_LAST_MODIFIED_PRETEND_304")) { - this.http.client.force_last_modified = true; + this.http.client.flags.force_last_modified = true; this.http.client.if_modified_since = last_modified; } } @@ -510,10 +510,10 @@ const NetworkTask = struct { const url = URL.parse(this.url_buf); - this.http = AsyncHTTP.init(allocator, .GET, url, header_builder.entries, header_buf, &this.response_buffer, "", 0, this.getCompletionCallback(), HTTP.FetchRedirect.follow, .{ + this.http = AsyncHTTP.init(allocator, .GET, url, header_builder.entries, header_buf, &this.response_buffer, "", this.getCompletionCallback(), HTTP.FetchRedirect.follow, .{ .http_proxy = this.package_manager.httpProxy(url), }); - this.http.client.reject_unauthorized = this.package_manager.tlsRejectUnauthorized(); + this.http.client.flags.reject_unauthorized = this.package_manager.tlsRejectUnauthorized(); if (PackageManager.verbose_install) { this.http.client.verbose = .headers; } @@ -758,13 +758,13 @@ pub const Task = struct { const dir = brk: { if (Repository.tryHTTPS(url)) |https| break :brk Repository.download( manager.allocator, - manager.env, + this.request.git_clone.env, manager.log, manager.getCacheDirectory(), this.id, name, https, - attempt + attempt, ) catch |err| { // Exit early if git checked and could // not find the repository, skip ssh @@ -782,13 +782,13 @@ pub const Task = struct { break :brk null; } orelse if (Repository.trySSH(url)) |ssh| Repository.download( manager.allocator, - manager.env, + this.request.git_clone.env, manager.log, manager.getCacheDirectory(), this.id, name, ssh, - attempt + attempt, ) catch |err| { this.err = err; this.status = Status.fail; @@ -799,8 +799,6 @@ pub const Task = struct { return; }; - manager.git_repositories.put(manager.allocator, this.id, bun.toFD(dir.fd)) catch unreachable; - this.data = .{ .git_clone = bun.toFD(dir.fd), }; @@ -810,7 +808,7 @@ pub const Task = struct { const git_checkout = &this.request.git_checkout; const data = Repository.checkout( manager.allocator, - manager.env, + this.request.git_checkout.env, manager.log, manager.getCacheDirectory(), git_checkout.repo_dir.asDir(), @@ -891,6 +889,7 @@ pub const Task = struct { git_clone: struct { name: strings.StringOrTinyString, url: strings.StringOrTinyString, + env: DotEnv.Map, }, git_checkout: struct { repo_dir: bun.FileDescriptor, @@ -899,6 +898,7 @@ pub const Task = struct { url: strings.StringOrTinyString, resolved: strings.StringOrTinyString, resolution: Resolution, + env: DotEnv.Map, }, local_tarball: struct { tarball: ExtractTarball, @@ -1207,8 +1207,13 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { if (!package_json_checker.has_found_version and resolution_tag != .workspace) return false; const found_version = package_json_checker.found_version; + + // exclude build tags from comparsion + // https://github.com/oven-sh/bun/issues/13563 + const found_version_end = strings.lastIndexOfChar(found_version, '+') orelse found_version.len; + const expected_version_end = strings.lastIndexOfChar(this.package_version, '+') orelse this.package_version.len; // Check if the version matches - if (!strings.eql(found_version, this.package_version)) { + if (!strings.eql(found_version[0..found_version_end], this.package_version[0..expected_version_end])) { const offset = brk: { // ASCII only. for (0..found_version.len) |c| { @@ -1446,7 +1451,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { state.cached_package_dir = (if (comptime Environment.isWindows) if (method == .symlink) - bun.openDirNoRenamingOrDeletingWindows(this.cache_dir, this.cache_dir_subpath) + bun.openDirNoRenamingOrDeletingWindows(bun.toFD(this.cache_dir), this.cache_dir_subpath) else bun.openDir(this.cache_dir, this.cache_dir_subpath) else @@ -1624,7 +1629,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { _ = C.fchmod(outfile.handle, @intCast(stat.mode)); } - bun.copyFileWithState(in_file.handle, outfile.handle, ©_file_state) catch |err| { + bun.copyFileWithState(in_file.handle, outfile.handle, ©_file_state).unwrap() catch |err| { if (do_progress) { progress_.root.end(); progress_.refresh(); @@ -2586,6 +2591,10 @@ pub const PackageManager = struct { progress_name_buf_dynamic: []u8 = &[_]u8{}, cpu_count: u32 = 0, + track_installed_bin: TrackInstalledBin = .{ + .none = {}, + }, + // progress bar stuff when not stack allocated root_progress_node: *Progress.Node = undefined, @@ -2703,6 +2712,19 @@ pub const PackageManager = struct { this.root_package_id.id = null; } + pub fn crash(this: *PackageManager) noreturn { + if (this.options.log_level != .silent) { + this.log.printForLogLevel(Output.errorWriter()) catch {}; + } + Global.crash(); + } + + const TrackInstalledBin = union(enum) { + none: void, + pending: void, + basename: []const u8, + }; + // maybe rename to `PackageJSONCache` if we cache more than workspaces pub const WorkspacePackageJSONCache = struct { const js_ast = bun.JSAst; @@ -2711,6 +2733,7 @@ pub const PackageManager = struct { pub const MapEntry = struct { root: Expr, source: logger.Source, + indentation: JSPrinter.Options.Indentation = .{}, }; pub const Map = bun.StringHashMapUnmanaged(MapEntry); @@ -2718,6 +2741,7 @@ pub const PackageManager = struct { pub const GetJSONOptions = struct { init_reset_store: bool = true, always_decode_escape_sequences: bool = true, + guess_indentation: bool = false, }; pub const GetResult = union(enum) { @@ -2738,8 +2762,14 @@ pub const PackageManager = struct { /// Given an absolute path to a workspace package.json, return the AST /// and contents of the file. If the package.json is not present in the /// cache, it will be read from disk and parsed, and stored in the cache. - pub fn getWithPath(this: *@This(), allocator: std.mem.Allocator, log: *logger.Log, abs_package_json_path: anytype, comptime opts: GetJSONOptions) GetResult { - bun.assert(std.fs.path.isAbsolute(abs_package_json_path)); + pub fn getWithPath( + this: *@This(), + allocator: std.mem.Allocator, + log: *logger.Log, + abs_package_json_path: anytype, + comptime opts: GetJSONOptions, + ) GetResult { + bun.assertWithLocation(std.fs.path.isAbsolute(abs_package_json_path), @src()); var buf: if (Environment.isWindows) bun.PathBuffer else void = undefined; const path = if (comptime !Environment.isWindows) @@ -2762,16 +2792,26 @@ pub const PackageManager = struct { if (comptime opts.init_reset_store) initializeStore(); - const _json = if (comptime opts.always_decode_escape_sequences) - json_parser.ParsePackageJSONUTF8AlwaysDecode(&source, log, allocator) - else - json_parser.ParsePackageJSONUTF8(&source, log, allocator); - - const json = _json catch |err| return .{ .parse_err = err }; + const json = json_parser.ParsePackageJSONUTF8WithOpts( + &source, + log, + allocator, + .{ + .is_json = true, + .allow_comments = true, + .allow_trailing_commas = true, + .always_decode_escape_sequences = opts.always_decode_escape_sequences, + .guess_indentation = opts.guess_indentation, + }, + ) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + return .{ .parse_err = err }; + }; entry.value_ptr.* = .{ - .root = json.deepClone(allocator) catch bun.outOfMemory(), + .root = json.root.deepClone(bun.default_allocator) catch bun.outOfMemory(), .source = source, + .indentation = json.indentation, }; entry.key_ptr.* = key; @@ -2787,7 +2827,7 @@ pub const PackageManager = struct { source: logger.Source, comptime opts: GetJSONOptions, ) GetResult { - bun.assert(std.fs.path.isAbsolute(source.path.text)); + bun.assertWithLocation(std.fs.path.isAbsolute(source.path.text), @src()); var buf: if (Environment.isWindows) bun.PathBuffer else void = undefined; const path = if (comptime !Environment.isWindows) @@ -2806,15 +2846,25 @@ pub const PackageManager = struct { if (comptime opts.init_reset_store) initializeStore(); - const _json = if (comptime opts.always_decode_escape_sequences) - json_parser.ParsePackageJSONUTF8AlwaysDecode(&source, log, allocator) - else - json_parser.ParsePackageJSONUTF8(&source, log, allocator); - const json = _json catch |err| return .{ .parse_err = err }; + const json_result = json_parser.ParsePackageJSONUTF8WithOpts( + &source, + log, + allocator, + .{ + .is_json = true, + .allow_comments = true, + .allow_trailing_commas = true, + .always_decode_escape_sequences = opts.always_decode_escape_sequences, + .guess_indentation = opts.guess_indentation, + }, + ); + + const json = json_result catch |err| return .{ .parse_err = err }; entry.value_ptr.* = .{ - .root = json.deepClone(allocator) catch bun.outOfMemory(), + .root = json.root.deepClone(allocator) catch bun.outOfMemory(), .source = source, + .indentation = json.indentation, }; entry.key_ptr.* = allocator.dupe(u8, path) catch bun.outOfMemory(); @@ -4720,6 +4770,7 @@ pub const PackageManager = struct { *FileSystem.FilenameStore, &FileSystem.FilenameStore.instance, ) catch unreachable, + .env = Repository.shared_env.get(this.allocator, this.env), }, }, .id = task_id, @@ -4775,6 +4826,7 @@ pub const PackageManager = struct { *FileSystem.FilenameStore, &FileSystem.FilenameStore.instance, ) catch unreachable, + .env = Repository.shared_env.get(this.allocator, this.env), }, }, .apply_patch_task = if (patch_name_and_version_hash) |h| brk: { @@ -4925,6 +4977,8 @@ pub const PackageManager = struct { comptime successFn: SuccessFn, comptime failFn: ?FailFn, ) !void { + if (dependency.behavior.isOptionalPeer()) return; + var name = dependency.realname(); var name_hash = switch (dependency.version.tag) { @@ -4960,15 +5014,17 @@ pub const PackageManager = struct { if (dependency.version.tag != .npm or !dependency.version.value.npm.is_alias and this.lockfile.hasOverrides()) { if (this.lockfile.overrides.get(name_hash)) |new| { debug("override: {s} -> {s}", .{ this.lockfile.str(&dependency.version.literal), this.lockfile.str(&new.literal) }); - name = switch (new.tag) { - .dist_tag => new.value.dist_tag.name, - .git => new.value.git.package_name, - .github => new.value.github.package_name, - .npm => new.value.npm.name, - .tarball => new.value.tarball.package_name, - else => name, + name, name_hash = switch (new.tag) { + // only get name hash for npm and dist_tag. git, github, tarball don't have names until after extracting tarball + .dist_tag => .{ new.value.dist_tag.name, String.Builder.stringHash(this.lockfile.str(&new.value.dist_tag.name)) }, + .npm => .{ new.value.npm.name, String.Builder.stringHash(this.lockfile.str(&new.value.npm.name)) }, + .git => .{ new.value.git.package_name, name_hash }, + .github => .{ new.value.github.package_name, name_hash }, + .tarball => .{ new.value.tarball.package_name, name_hash }, + else => .{ name, name_hash }, }; - name_hash = String.Builder.stringHash(this.lockfile.str(&name)); + + // `name_hash` stays the same break :version new; } } @@ -5193,7 +5249,7 @@ pub const PackageManager = struct { this.enqueueNetworkTask(network_task); } } else { - if (this.options.do.install_peer_dependencies and !dependency.behavior.isOptionalPeer()) { + if (this.options.do.install_peer_dependencies) { try this.peer_dependencies.writeItem(id); return; } @@ -5268,7 +5324,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies and !dependency.behavior.isOptionalPeer()) { + if (this.options.do.install_peer_dependencies) { try this.peer_dependencies.writeItem(id); } return; @@ -5293,7 +5349,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies and !dependency.behavior.isOptionalPeer()) { + if (this.options.do.install_peer_dependencies) { try this.peer_dependencies.writeItem(id); } return; @@ -5345,7 +5401,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies and !dependency.behavior.isOptionalPeer()) { + if (this.options.do.install_peer_dependencies) { try this.peer_dependencies.writeItem(id); } return; @@ -5533,7 +5589,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies and !dependency.behavior.isOptionalPeer()) { + if (this.options.do.install_peer_dependencies) { try this.peer_dependencies.writeItem(id); } return; @@ -5574,7 +5630,7 @@ pub const PackageManager = struct { } } - fn flushNetworkQueue(this: *PackageManager) void { + pub fn flushNetworkQueue(this: *PackageManager) void { var network = &this.network_task_fifo; while (network.readItem()) |network_task| { @@ -6116,19 +6172,21 @@ pub const PackageManager = struct { } } - const response = task.http.response orelse { + if (!has_network_error and task.http.response == null) { + has_network_error = true; + const min = manager.options.min_simultaneous_requests; + const max = AsyncHTTP.max_simultaneous_requests.load(.monotonic); + if (max > min) { + AsyncHTTP.max_simultaneous_requests.store(@max(min, max / 2), .monotonic); + } + } + + // Handle retry-able errors. + if (task.http.response == null or task.http.response.?.status_code > 499) { const err = task.http.err orelse error.HTTPError; if (task.retried < manager.options.max_retry_count) { task.retried += 1; - if (!has_network_error) { - has_network_error = true; - const min = manager.options.min_simultaneous_requests; - const max = AsyncHTTP.max_simultaneous_requests.load(.monotonic); - if (max > min) { - AsyncHTTP.max_simultaneous_requests.store(@max(min, max / 2), .monotonic); - } - } manager.enqueueNetworkTask(task); if (manager.options.log_level.isVerbose()) { @@ -6136,13 +6194,18 @@ pub const PackageManager = struct { null, logger.Loc.Empty, manager.allocator, - "{s} downloading package manifest {s}", - .{ bun.span(@errorName(err)), name.slice() }, + "{s} downloading package manifest {s}. Retry {d}/{d}...", + .{ bun.span(@errorName(err)), name.slice(), task.retried, manager.options.max_retry_count }, ) catch unreachable; } continue; } + } + + const response = task.http.response orelse { + // Handle non-retry-able errors. + const err = task.http.err orelse error.HTTPError; if (@TypeOf(callbacks.onPackageManifestError) != void) { callbacks.onPackageManifestError( @@ -6265,6 +6328,10 @@ pub const PackageManager = struct { manager.getCacheDirectory(), ); + if (@hasField(@TypeOf(callbacks), "manifests_only") and callbacks.manifests_only) { + continue; + } + const dependency_list_entry = manager.task_queue.getEntry(task.task_id).?; const dependency_list = dependency_list_entry.value_ptr.*; @@ -6285,19 +6352,20 @@ pub const PackageManager = struct { manager.task_batch.push(ThreadPool.Batch.from(manager.enqueueParseNPMPackage(task.task_id, name, task))); }, .extract => |*extract| { - const response = task.http.response orelse { + if (!has_network_error and task.http.response == null) { + has_network_error = true; + const min = manager.options.min_simultaneous_requests; + const max = AsyncHTTP.max_simultaneous_requests.load(.monotonic); + if (max > min) { + AsyncHTTP.max_simultaneous_requests.store(@max(min, max / 2), .monotonic); + } + } + + if (task.http.response == null or task.http.response.?.status_code > 499) { const err = task.http.err orelse error.TarballFailedToDownload; if (task.retried < manager.options.max_retry_count) { task.retried += 1; - if (!has_network_error) { - has_network_error = true; - const min = manager.options.min_simultaneous_requests; - const max = AsyncHTTP.max_simultaneous_requests.load(.monotonic); - if (max > min) { - AsyncHTTP.max_simultaneous_requests.store(@max(min, max / 2), .monotonic); - } - } manager.enqueueNetworkTask(task); if (manager.options.log_level.isVerbose()) { @@ -6305,17 +6373,23 @@ pub const PackageManager = struct { null, logger.Loc.Empty, manager.allocator, - "warn: {s} downloading tarball {s}@{s}", + "warn: {s} downloading tarball {s}@{s}. Retrying {d}/{d}...", .{ bun.span(@errorName(err)), extract.name.slice(), extract.resolution.fmt(manager.lockfile.buffers.string_bytes.items, .auto), + task.retried, + manager.options.max_retry_count, }, ) catch unreachable; } continue; } + } + + const response = task.http.response orelse { + const err = task.http.err orelse error.TarballFailedToDownload; if (@TypeOf(callbacks.onPackageDownloadError) != void) { const package_id = manager.lockfile.buffers.resolutions.items[extract.dependency_id]; @@ -6434,7 +6508,7 @@ pub const PackageManager = struct { if (comptime log_level.isVerbose()) { Output.prettyError(" ", .{}); Output.printElapsed(@as(f64, @floatCast(@as(f64, @floatFromInt(task.http.elapsed)) / std.time.ns_per_ms))); - Output.prettyError("Downloaded {s} tarball\n", .{extract.name.slice()}); + Output.prettyError(" Downloaded {s} tarball\n", .{extract.name.slice()}); Output.flush(); } @@ -6499,6 +6573,10 @@ pub const PackageManager = struct { try manager.manifests.insert(manifest.pkg.name.hash, manifest); + if (@hasField(@TypeOf(callbacks), "manifests_only") and callbacks.manifests_only) { + continue; + } + const dependency_list_entry = manager.task_queue.getEntry(task.id).?; const dependency_list = dependency_list_entry.value_ptr.*; dependency_list_entry.value_ptr.* = .{}; @@ -6588,13 +6666,19 @@ pub const PackageManager = struct { .dependency, .root_dependency => |id| { var version = &manager.lockfile.buffers.dependencies.items[id].version; switch (version.tag) { + .git => { + version.value.git.package_name = pkg.name; + }, .github => { version.value.github.package_name = pkg.name; }, .tarball => { version.value.tarball.package_name = pkg.name; }, - else => unreachable, + + // `else` is reachable if this package is from `overrides`. Version in `lockfile.buffer.dependencies` + // will still have the original. + else => {}, } try manager.processDependencyListItem(dep, &any_root, install_peer); }, @@ -6638,6 +6722,8 @@ pub const PackageManager = struct { const name = clone.name.slice(); const url = clone.url.slice(); + manager.git_repositories.put(manager.allocator, task.id, task.data.git_clone) catch unreachable; + if (task.status == .fail) { const err = task.err orelse error.Failed; @@ -6808,15 +6894,12 @@ pub const PackageManager = struct { patches_dir: string, }, } = .{ .nothing = .{} }, - // The idea here is: - // 1. package has a platform-specific binary to install - // 2. To prevent downloading & installing incompatible versions, they stick the "real" one in optionalDependencies - // 3. The real one we want to link is in another package - // 4. Therefore, we remap the "bin" specified in the real package - // to the target package which is the one which is: - // 1. In optionalDependencies - // 2. Has a platform and/or os specified, which evaluates to not disabled - native_bin_link_allowlist: []const PackageNameHash = &default_native_bin_link_allowlist, + + filter_patterns: []const string = &.{}, + pack_destination: string = "", + pack_gzip_level: ?string = null, + // json_output: bool = false, + max_retry_count: u16 = 5, min_simultaneous_requests: usize = 4, @@ -6826,26 +6909,6 @@ pub const PackageManager = struct { return this.log_level != .silent and this.do.summary; } - pub fn isBinPathInPATH(this: *const Options) bool { - // must be absolute - if (this.bin_path[0] != std.fs.path.sep) return false; - var tokenizer = std.mem.split(bun.getenvZ("PATH") orelse "", ":"); - const spanned = bun.span(this.bin_path); - while (tokenizer.next()) |token| { - if (strings.eql(token, spanned)) return true; - } - return false; - } - - const default_native_bin_link_allowlist = [_]PackageNameHash{ - String.Builder.stringHash("esbuild"), - String.Builder.stringHash("turbo"), - String.Builder.stringHash("bun"), - String.Builder.stringHash("rome"), - String.Builder.stringHash("zig"), - String.Builder.stringHash("@oven-sh/zig"), - }; - pub const LogLevel = enum { default, verbose, @@ -6969,8 +7032,8 @@ pub const PackageManager = struct { } if (bun_install_) |bun_install| { if (bun_install.scoped) |scoped| { - for (scoped.scopes, 0..) |name, i| { - var registry = scoped.registries[i]; + for (scoped.scopes.keys(), scoped.scopes.values()) |name, *registry_| { + var registry = registry_.*; if (registry.url.len == 0) registry.url = base.url; try this.registries.put(allocator, Npm.Registry.Scope.hash(name), try Npm.Registry.Scope.fromAPI(name, registry, allocator, env)); } @@ -6989,14 +7052,6 @@ pub const PackageManager = struct { this.enable.force_install = true; } - if (bun_install.native_bin_links.len > 0) { - var buf = try allocator.alloc(u64, bun_install.native_bin_links.len); - for (bun_install.native_bin_links, 0..) |name, i| { - buf[i] = String.Builder.stringHash(name); - } - this.native_bin_link_allowlist = buf; - } - if (bun_install.save_yarn_lockfile orelse false) { this.do.save_yarn_lock = true; } @@ -7133,22 +7188,6 @@ pub const PackageManager = struct { if (std.fmt.parseInt(u16, retry_count, 10)) |int| this.max_retry_count = int else |_| {} } - if (env.get("BUN_CONFIG_LINK_NATIVE_BINS")) |native_packages| { - const len = std.mem.count(u8, native_packages, " "); - if (len > 0) { - var all = try allocator.alloc(PackageNameHash, this.native_bin_link_allowlist.len + len); - bun.copy(PackageNameHash, all, this.native_bin_link_allowlist); - var remain = all[this.native_bin_link_allowlist.len..]; - var splitter = std.mem.split(u8, native_packages, " "); - var i: usize = 0; - while (splitter.next()) |name| { - remain[i] = String.Builder.stringHash(name); - i += 1; - } - this.native_bin_link_allowlist = all; - } - } - AsyncHTTP.loadEnv(allocator, log, env); if (env.get("BUN_CONFIG_SKIP_SAVE_LOCKFILE")) |check_bool| { @@ -7190,6 +7229,11 @@ pub const PackageManager = struct { this.do.summary = false; } + this.filter_patterns = cli.filters; + this.pack_destination = cli.pack_destination; + this.pack_gzip_level = cli.pack_gzip_level; + // this.json_output = cli.json_output; + if (cli.no_cache) { this.enable.manifest_cache = false; this.enable.manifest_cache_control = false; @@ -7230,16 +7274,6 @@ pub const PackageManager = struct { this.do.save_yarn_lock = true; } - if (cli.link_native_bins.len > 0) { - var all = try allocator.alloc(PackageNameHash, this.native_bin_link_allowlist.len + cli.link_native_bins.len); - bun.copy(PackageNameHash, all, this.native_bin_link_allowlist); - var remain = all[this.native_bin_link_allowlist.len..]; - for (cli.link_native_bins, 0..) |name, i| { - remain[i] = String.Builder.stringHash(name); - } - this.native_bin_link_allowlist = all; - } - if (cli.backend) |backend| { PackageInstall.supported_method = backend; } @@ -7575,6 +7609,12 @@ pub const PackageManager = struct { current_package_json: *Expr, options: EditOptions, ) !void { + // using data store is going to result in undefined memory issues as + // the store is cleared in some workspace situations. the solution + // is to always avoid the store + Expr.Disabler.disable(); + defer Expr.Disabler.enable(); + const allocator = manager.allocator; inline for (dependency_groups) |group| { @@ -7628,13 +7668,9 @@ pub const PackageManager = struct { else allocator.dupe(u8, "latest") catch bun.outOfMemory(); - dep.value = Expr.init( - E.String, - E.String{ - .data = temp_version, - }, - logger.Loc.Empty, - ).clone(allocator) catch bun.outOfMemory(); + dep.value = Expr.allocate(allocator, E.String, .{ + .data = temp_version, + }, logger.Loc.Empty); } } } else { @@ -7705,29 +7741,21 @@ pub const PackageManager = struct { // negative because the real package might have a scope // e.g. "dep": "npm:@foo/bar@1.2.3" if (strings.lastIndexOfChar(dep_literal, '@')) |at_index| { - dep.value = try Expr.init( - E.String, - E.String{ - .data = try std.fmt.allocPrint(allocator, "{s}@{s}", .{ - dep_literal[0..at_index], - new_version, - }), - }, - logger.Loc.Empty, - ).clone(allocator); + dep.value = Expr.allocate(allocator, E.String, .{ + .data = try std.fmt.allocPrint(allocator, "{s}@{s}", .{ + dep_literal[0..at_index], + new_version, + }), + }, logger.Loc.Empty); break :updated; } // fallthrough and replace entire version. } - dep.value = try Expr.init( - E.String, - E.String{ - .data = new_version, - }, - logger.Loc.Empty, - ).clone(allocator); + dep.value = Expr.allocate(allocator, E.String, .{ + .data = new_version, + }, logger.Loc.Empty); break :updated; } } @@ -7748,6 +7776,12 @@ pub const PackageManager = struct { dependency_list: string, options: EditOptions, ) !void { + // using data store is going to result in undefined memory issues as + // the store is cleared in some workspace situations. the solution + // is to always avoid the store + Expr.Disabler.disable(); + defer Expr.Disabler.enable(); + const allocator = manager.allocator; var remaining = updates.len; var replacing: usize = 0; @@ -7889,13 +7923,9 @@ pub const PackageManager = struct { while (i > 0) { i -= 1; if (deps[i].data == .e_missing) { - deps[i] = try Expr.init( - E.String, - E.String{ - .data = package_name, - }, - logger.Loc.Empty, - ).clone(allocator); + deps[i] = Expr.allocate(allocator, E.String, .{ + .data = package_name, + }, logger.Loc.Empty); break; } } @@ -7955,27 +7985,19 @@ pub const PackageManager = struct { } if (new_dependencies[k].key == null) { - new_dependencies[k].key = try JSAst.Expr.init( - JSAst.E.String, - JSAst.E.String{ - .data = try allocator.dupe(u8, if (request.is_aliased) - request.name - else if (request.resolved_name.isEmpty()) - request.version.literal.slice(request.version_buf) - else - request.resolved_name.slice(request.version_buf)), - }, - logger.Loc.Empty, - ).clone(allocator); + new_dependencies[k].key = JSAst.Expr.allocate(allocator, JSAst.E.String, .{ + .data = try allocator.dupe(u8, if (request.is_aliased) + request.name + else if (request.resolved_name.isEmpty()) + request.version.literal.slice(request.version_buf) + else + request.resolved_name.slice(request.version_buf)), + }, logger.Loc.Empty); - new_dependencies[k].value = try JSAst.Expr.init( - JSAst.E.String, - JSAst.E.String{ - // we set it later - .data = "", - }, - logger.Loc.Empty, - ).clone(allocator); + new_dependencies[k].value = JSAst.Expr.allocate(allocator, JSAst.E.String, .{ + // we set it later + .data = "", + }, logger.Loc.Empty); request.e_string = new_dependencies[k].value.?.data.e_string; @@ -7994,13 +8016,9 @@ pub const PackageManager = struct { } } - break :brk JSAst.Expr.init( - JSAst.E.Object, - JSAst.E.Object{ - .properties = JSAst.G.Property.List.init(new_dependencies), - }, - logger.Loc.Empty, - ); + break :brk JSAst.Expr.allocate(allocator, JSAst.E.Object, .{ + .properties = JSAst.G.Property.List.init(new_dependencies), + }, logger.Loc.Empty); }; dependencies_object.data.e_object.properties = JSAst.G.Property.List.init(new_dependencies); @@ -8020,13 +8038,9 @@ pub const PackageManager = struct { } } - break :brk Expr.init( - E.Array, - E.Array{ - .items = JSAst.ExprNodeList.init(new_trusted_deps), - }, - logger.Loc.Empty, - ); + break :brk Expr.allocate(allocator, E.Array, .{ + .items = JSAst.ExprNodeList.init(new_trusted_deps), + }, logger.Loc.Empty); }; if (options.add_trusted_dependencies and trusted_dependencies_to_add > 0) { @@ -8039,81 +8053,55 @@ pub const PackageManager = struct { if (current_package_json.data != .e_object or current_package_json.data.e_object.properties.len == 0) { var root_properties = try allocator.alloc(JSAst.G.Property, if (options.add_trusted_dependencies) 2 else 1); root_properties[0] = JSAst.G.Property{ - .key = JSAst.Expr.init( - JSAst.E.String, - JSAst.E.String{ - .data = dependency_list, - }, - logger.Loc.Empty, - ), + .key = JSAst.Expr.allocate(allocator, JSAst.E.String, .{ + .data = dependency_list, + }, logger.Loc.Empty), .value = dependencies_object, }; if (options.add_trusted_dependencies) { root_properties[1] = JSAst.G.Property{ - .key = Expr.init( - E.String, - E.String{ - .data = trusted_dependencies_string, - }, - logger.Loc.Empty, - ), + .key = Expr.allocate(allocator, E.String, .{ + .data = trusted_dependencies_string, + }, logger.Loc.Empty), .value = trusted_dependencies_array, }; } - current_package_json.* = JSAst.Expr.init( - JSAst.E.Object, - JSAst.E.Object{ .properties = JSAst.G.Property.List.init(root_properties) }, - logger.Loc.Empty, - ); + current_package_json.* = JSAst.Expr.allocate(allocator, JSAst.E.Object, .{ + .properties = JSAst.G.Property.List.init(root_properties), + }, logger.Loc.Empty); } else { if (needs_new_dependency_list and needs_new_trusted_dependencies_list) { var root_properties = try allocator.alloc(G.Property, current_package_json.data.e_object.properties.len + 2); @memcpy(root_properties[0..current_package_json.data.e_object.properties.len], current_package_json.data.e_object.properties.slice()); root_properties[root_properties.len - 2] = .{ - .key = Expr.init(E.String, E.String{ + .key = Expr.allocate(allocator, E.String, E.String{ .data = dependency_list, }, logger.Loc.Empty), .value = dependencies_object, }; root_properties[root_properties.len - 1] = .{ - .key = Expr.init( - E.String, - E.String{ - .data = trusted_dependencies_string, - }, - logger.Loc.Empty, - ), + .key = Expr.allocate(allocator, E.String, .{ + .data = trusted_dependencies_string, + }, logger.Loc.Empty), .value = trusted_dependencies_array, }; - current_package_json.* = Expr.init( - E.Object, - E.Object{ - .properties = G.Property.List.init(root_properties), - }, - logger.Loc.Empty, - ); + current_package_json.* = Expr.allocate(allocator, E.Object, .{ + .properties = G.Property.List.init(root_properties), + }, logger.Loc.Empty); } else if (needs_new_dependency_list or needs_new_trusted_dependencies_list) { var root_properties = try allocator.alloc(JSAst.G.Property, current_package_json.data.e_object.properties.len + 1); @memcpy(root_properties[0..current_package_json.data.e_object.properties.len], current_package_json.data.e_object.properties.slice()); root_properties[root_properties.len - 1] = .{ - .key = JSAst.Expr.init( - JSAst.E.String, - JSAst.E.String{ - .data = if (needs_new_dependency_list) dependency_list else trusted_dependencies_string, - }, - logger.Loc.Empty, - ), + .key = JSAst.Expr.allocate(allocator, JSAst.E.String, .{ + .data = if (needs_new_dependency_list) dependency_list else trusted_dependencies_string, + }, logger.Loc.Empty), .value = if (needs_new_dependency_list) dependencies_object else trusted_dependencies_array, }; - current_package_json.* = JSAst.Expr.init( - JSAst.E.Object, - JSAst.E.Object{ - .properties = JSAst.G.Property.List.init(root_properties), - }, - logger.Loc.Empty, - ); + current_package_json.* = JSAst.Expr.allocate(allocator, JSAst.E.Object, .{ + .properties = JSAst.G.Property.List.init(root_properties), + }, logger.Loc.Empty); } } } @@ -8226,20 +8214,32 @@ pub const PackageManager = struct { unlink, patch, @"patch-commit", - }; + outdated, + pack, - pub fn init(ctx: Command.Context, comptime subcommand: Subcommand) !*PackageManager { - const cli = try CommandLineArguments.parse(ctx.allocator, subcommand); - return initWithCLI(ctx, cli, subcommand); - } + pub fn canGloballyInstallPackages(this: Subcommand) bool { + return switch (this) { + .install, .update, .add => true, + else => false, + }; + } + + pub fn supportsWorkspaceFiltering(this: Subcommand) bool { + return switch (this) { + .outdated => true, + // .pack => true, + else => false, + }; + } + }; - fn initWithCLI( + pub fn init( ctx: Command.Context, cli: CommandLineArguments, - comptime subcommand: Subcommand, - ) !*PackageManager { + subcommand: Subcommand, + ) !struct { *PackageManager, string } { // assume that spawning a thread will take a lil so we do that asap - try HTTP.HTTPThread.init(); + HTTP.HTTPThread.init(); if (cli.global) { var explicit_global_dir: string = ""; @@ -8265,6 +8265,7 @@ pub const PackageManager = struct { var original_package_json_path: stringZ = original_package_json_path_buf.items[0 .. top_level_dir_no_trailing_slash.len + "/package.json".len :0]; const original_cwd = strings.withoutSuffixComptime(original_package_json_path, std.fs.path.sep_str ++ "package.json"); + const original_cwd_clone = ctx.allocator.dupe(u8, original_cwd) catch bun.outOfMemory(); var workspace_names = Package.WorkspaceMap.init(ctx.allocator); var workspace_package_json_cache: WorkspacePackageJSONCache = .{ @@ -8328,7 +8329,7 @@ pub const PackageManager = struct { }; } - if (comptime subcommand == .install) { + if (subcommand == .install) { if (cli.positionals.len > 1) { // this is `bun add `. // @@ -8344,7 +8345,7 @@ pub const PackageManager = struct { return error.MissingPackageJSON; }; - bun.assert(strings.eqlLong(original_package_json_path_buf.items[0..this_cwd.len], this_cwd, true)); + bun.assertWithLocation(strings.eqlLong(original_package_json_path_buf.items[0..this_cwd.len], this_cwd, true), @src()); original_package_json_path_buf.items.len = this_cwd.len; original_package_json_path_buf.appendSliceAssumeCapacity(std.fs.path.sep_str ++ "package.json"); original_package_json_path_buf.appendAssumeCapacity(0); @@ -8354,7 +8355,7 @@ pub const PackageManager = struct { // Check if this is a workspace; if so, use root package var found = false; - if (comptime subcommand != .link) { + if (subcommand != .link) { if (!created_package_json) { while (std.fs.path.dirname(this_cwd)) |parent| : (this_cwd = parent) { const parent_without_trailing_slash = strings.withoutTrailingSlash(parent); @@ -8456,6 +8457,19 @@ pub const PackageManager = struct { env.loadProcess(); try env.load(entries_option.entries, &[_][]u8{}, .production, false); + initializeStore(); + bun.ini.loadNpmrcFromFile( + ctx.allocator, + ctx.install orelse brk: { + const install_ = ctx.allocator.create(Api.BunInstall) catch bun.outOfMemory(); + install_.* = std.mem.zeroes(Api.BunInstall); + ctx.install = install_; + break :brk install_; + }, + env, + true, + ); + var cpu_count = @as(u32, @truncate(((try std.Thread.getCpuCount()) + 1))); if (env.get("GOMAXPROCS")) |max_procs| { @@ -8552,7 +8566,10 @@ pub const PackageManager = struct { break :brk @truncate(@as(u64, @intCast(@max(std.time.timestamp(), 0)))); }; - return manager; + return .{ + manager, + original_cwd_clone, + }; } pub fn initWithRuntime( @@ -8709,29 +8726,29 @@ pub const PackageManager = struct { // parse dependency of positional arg string (may include name@version for example) // get the precise version from the lockfile (there may be multiple) // copy the contents into a temp folder - pub inline fn patch(ctx: Command.Context) !void { + pub fn patch(ctx: Command.Context) !void { try updatePackageJSONAndInstallCatchError(ctx, .patch); } - pub inline fn patchCommit(ctx: Command.Context) !void { + pub fn patchCommit(ctx: Command.Context) !void { try updatePackageJSONAndInstallCatchError(ctx, .@"patch-commit"); } - pub inline fn update(ctx: Command.Context) !void { + pub fn update(ctx: Command.Context) !void { try updatePackageJSONAndInstallCatchError(ctx, .update); } - pub inline fn add(ctx: Command.Context) !void { + pub fn add(ctx: Command.Context) !void { try updatePackageJSONAndInstallCatchError(ctx, .add); } - pub inline fn remove(ctx: Command.Context) !void { + pub fn remove(ctx: Command.Context) !void { try updatePackageJSONAndInstallCatchError(ctx, .remove); } pub fn updatePackageJSONAndInstallCatchError( ctx: Command.Context, - comptime subcommand: Subcommand, + subcommand: Subcommand, ) !void { updatePackageJSONAndInstall(ctx, subcommand) catch |err| { switch (err) { @@ -8748,15 +8765,17 @@ pub const PackageManager = struct { }; } - pub inline fn link(ctx: Command.Context) !void { - var manager = PackageManager.init(ctx, .link) catch |err| brk: { + pub fn link(ctx: Command.Context) !void { + const cli = try CommandLineArguments.parse(ctx.allocator, .link); + var manager, const original_cwd = PackageManager.init(ctx, cli, .link) catch |err| brk: { if (err == error.MissingPackageJSON) { try attemptToCreatePackageJSON(); - break :brk try PackageManager.init(ctx, .link); + break :brk try PackageManager.init(ctx, cli, .link); } return err; }; + defer ctx.allocator.free(original_cwd); if (manager.options.shouldPrintCommandName()) { Output.prettyErrorln("bun link v" ++ Global.package_json_version_with_sha ++ "\n", .{}); @@ -8866,17 +8885,30 @@ pub const PackageManager = struct { // Step 3b. Link any global bins if (package.bin.tag != .none) { + var link_target_buf: bun.PathBuffer = undefined; + var link_dest_buf: bun.PathBuffer = undefined; + var link_rel_buf: bun.PathBuffer = undefined; + var node_modules_path_buf: bun.PathBuffer = undefined; var bin_linker = Bin.Linker{ .bin = package.bin, - .package_installed_node_modules = bun.toFD(node_modules.fd), + .node_modules = bun.toFD(node_modules.fd), + .node_modules_path = bun.getFdPath(node_modules, &node_modules_path_buf) catch |err| { + if (manager.options.log_level != .silent) { + Output.err(err, "failed to link binary", .{}); + } + Global.crash(); + }, .global_bin_path = manager.options.bin_path, .global_bin_dir = manager.options.global_bin_dir, // .destination_dir_subpath = destination_dir_subpath, - .root_node_modules_folder = bun.toFD(node_modules.fd), .package_name = strings.StringOrTinyString.init(name), .string_buf = lockfile.buffers.string_bytes.items, .extern_string_buf = lockfile.buffers.extern_strings.items, + .seen = null, + .abs_target_buf = &link_target_buf, + .abs_dest_buf = &link_dest_buf, + .rel_buf = &link_rel_buf, }; bin_linker.link(true); @@ -8916,15 +8948,17 @@ pub const PackageManager = struct { } } - pub inline fn unlink(ctx: Command.Context) !void { - var manager = PackageManager.init(ctx, .unlink) catch |err| brk: { + pub fn unlink(ctx: Command.Context) !void { + const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .unlink); + var manager, const original_cwd = PackageManager.init(ctx, cli, .unlink) catch |err| brk: { if (err == error.MissingPackageJSON) { try attemptToCreatePackageJSON(); - break :brk try PackageManager.init(ctx, .unlink); + break :brk try PackageManager.init(ctx, cli, .unlink); } return err; }; + defer ctx.allocator.free(original_cwd); if (manager.options.shouldPrintCommandName()) { Output.prettyErrorln("bun unlink v" ++ Global.package_json_version_with_sha ++ "\n", .{}); @@ -8997,17 +9031,29 @@ pub const PackageManager = struct { // Step 3b. Link any global bins if (package.bin.tag != .none) { + var link_target_buf: bun.PathBuffer = undefined; + var link_dest_buf: bun.PathBuffer = undefined; + var link_rel_buf: bun.PathBuffer = undefined; + var node_modules_path_buf: bun.PathBuffer = undefined; + var bin_linker = Bin.Linker{ .bin = package.bin, - .package_installed_node_modules = bun.toFD(node_modules.fd), + .node_modules = bun.toFD(node_modules.fd), + .node_modules_path = bun.getFdPath(node_modules, &node_modules_path_buf) catch |err| { + if (manager.options.log_level != .silent) { + Output.err(err, "failed to link binary", .{}); + } + Global.crash(); + }, .global_bin_path = manager.options.bin_path, .global_bin_dir = manager.options.global_bin_dir, - - // .destination_dir_subpath = destination_dir_subpath, - .root_node_modules_folder = bun.toFD(node_modules.fd), .package_name = strings.StringOrTinyString.init(name), .string_buf = lockfile.buffers.string_bytes.items, .extern_string_buf = lockfile.buffers.extern_strings.items, + .seen = null, + .abs_target_buf = &link_target_buf, + .abs_dest_buf = &link_dest_buf, + .rel_buf = &link_rel_buf, }; bin_linker.unlink(true); } @@ -9056,58 +9102,73 @@ pub const PackageManager = struct { clap.parseParam("--backend Platform-specific optimizations for installing dependencies. " ++ platform_specific_backend_label) catch unreachable, clap.parseParam("--link-native-bins ... Link \"bin\" from a matching platform-specific \"optionalDependencies\" instead. Default: esbuild, turbo") catch unreachable, clap.parseParam("--concurrent-scripts Maximum number of concurrent jobs for lifecycle scripts (default 5)") catch unreachable, - // clap.parseParam("--omit ... Skip installing dependencies of a certain type. \"dev\", \"optional\", or \"peer\"") catch unreachable, clap.parseParam("-h, --help Print this help menu") catch unreachable, }; - pub const install_params = install_params_ ++ [_]ParamType{ + pub const install_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam("-d, --dev Add dependency to \"devDependencies\"") catch unreachable, clap.parseParam("-D, --development") catch unreachable, clap.parseParam("--optional Add dependency to \"optionalDependencies\"") catch unreachable, clap.parseParam("-E, --exact Add the exact version instead of the ^range") catch unreachable, clap.parseParam(" ... ") catch unreachable, - }; + }); - pub const update_params = install_params_ ++ [_]ParamType{ + pub const update_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam("--latest Update packages to their latest versions") catch unreachable, clap.parseParam(" ... \"name\" of packages to update") catch unreachable, - }; + }); - pub const pm_params = install_params_ ++ [_]ParamType{ + pub const pm_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam("-a, --all") catch unreachable, + // clap.parseParam("--filter ... Pack each matching workspace") catch unreachable, + clap.parseParam("--destination The directory the tarball will be saved in") catch unreachable, + clap.parseParam("--gzip-level Specify a custom compression level for gzip. Default is 9.") catch unreachable, clap.parseParam(" ... ") catch unreachable, - }; + }); - pub const add_params = install_params_ ++ [_]ParamType{ + pub const add_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam("-d, --dev Add dependency to \"devDependencies\"") catch unreachable, clap.parseParam("-D, --development") catch unreachable, clap.parseParam("--optional Add dependency to \"optionalDependencies\"") catch unreachable, clap.parseParam("-E, --exact Add the exact version instead of the ^range") catch unreachable, clap.parseParam(" ... \"name\" or \"name@version\" of package(s) to install") catch unreachable, - }; + }); - pub const remove_params = install_params_ ++ [_]ParamType{ + pub const remove_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam(" ... \"name\" of package(s) to remove from package.json") catch unreachable, - }; + }); - pub const link_params = install_params_ ++ [_]ParamType{ + pub const link_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam(" ... \"name\" install package as a link") catch unreachable, - }; + }); - pub const unlink_params = install_params_ ++ [_]ParamType{ + pub const unlink_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam(" ... \"name\" uninstall package as a link") catch unreachable, - }; + }); - const patch_params = install_params_ ++ [_]ParamType{ + const patch_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam(" ... \"name\" of the package to patch") catch unreachable, clap.parseParam("--commit Install a package containing modifications in `dir`") catch unreachable, clap.parseParam("--patches-dir The directory to put the patch file in (only if --commit is used)") catch unreachable, - }; + }); - const patch_commit_params = install_params_ ++ [_]ParamType{ + const patch_commit_params: []const ParamType = &(install_params_ ++ [_]ParamType{ clap.parseParam(" ... \"dir\" containing changes to a package") catch unreachable, clap.parseParam("--patches-dir The directory to put the patch file") catch unreachable, - }; + }); + + const outdated_params: []const ParamType = &(install_params_ ++ [_]ParamType{ + // clap.parseParam("--json Output outdated information in JSON format") catch unreachable, + clap.parseParam("--filter ... Display outdated dependencies for each matching workspace") catch unreachable, + clap.parseParam(" ... Package patterns to filter by") catch unreachable, + }); + + const pack_params: []const ParamType = &(install_params_ ++ [_]ParamType{ + // clap.parseParam("--filter ... Pack each matching workspace") catch unreachable, + clap.parseParam("--destination The directory the tarball will be saved in") catch unreachable, + clap.parseParam("--gzip-level Specify a custom compression level for gzip. Default is 9.") catch unreachable, + clap.parseParam(" ... ") catch unreachable, + }); pub const CommandLineArguments = struct { registry: string = "", @@ -9136,6 +9197,11 @@ pub const PackageManager = struct { trusted: bool = false, no_summary: bool = false, latest: bool = false, + // json_output: bool = false, + filters: []const string = &.{}, + + pack_destination: string = "", + pack_gzip_level: ?string = null, link_native_bins: []const string = &[_]string{}, @@ -9173,7 +9239,7 @@ pub const PackageManager = struct { } }; - pub fn printHelp(comptime subcommand: Subcommand) void { + pub fn printHelp(subcommand: Subcommand) void { switch (subcommand) { // fall back to HelpCommand.printWithReason Subcommand.install => { @@ -9196,7 +9262,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\n\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.install_params); + clap.simpleHelp(PackageManager.install_params); Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9223,7 +9289,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.update_params); + clap.simpleHelp(PackageManager.update_params); Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9239,7 +9305,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.patch_params); + clap.simpleHelp(PackageManager.patch_params); // Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9264,7 +9330,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.patch_params); + clap.simpleHelp(PackageManager.patch_params); // Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9294,7 +9360,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\n\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.add_params); + clap.simpleHelp(PackageManager.add_params); Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9316,7 +9382,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.remove_params); + clap.simpleHelp(PackageManager.remove_params); Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9340,7 +9406,7 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.link_params); + clap.simpleHelp(PackageManager.link_params); Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9361,7 +9427,54 @@ pub const PackageManager = struct { Output.flush(); Output.pretty("\nFlags:", .{}); Output.flush(); - clap.simpleHelp(&PackageManager.unlink_params); + clap.simpleHelp(PackageManager.unlink_params); + Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); + Output.flush(); + }, + .outdated => { + const intro_text = + \\Usage: bun outdated [flags] + ; + + const outro_text = + \\Examples: + \\ Display outdated dependencies in the current workspace. + \\ bun outdated + \\ + \\ Use --filter to include more than one workspace. + \\ bun outdated --filter="*" + \\ bun outdated --filter="./app/*" + \\ bun outdated --filter="!frontend" + \\ + \\ Filter dependencies with name patterns. + \\ bun outdated jquery + \\ bun outdated "is-*" + \\ bun outdated "!is-even" + \\ + ; + + Output.pretty("\n" ++ intro_text ++ "\n", .{}); + Output.flush(); + Output.pretty("\nFlags:", .{}); + clap.simpleHelp(PackageManager.outdated_params); + Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); + Output.flush(); + }, + .pack => { + const intro_text = + \\Usage: bun pack [flags] + ; + + const outro_text = + \\Examples: + \\ bun pack + \\ + ; + + Output.pretty("\n" ++ intro_text ++ "\n", .{}); + Output.flush(); + Output.pretty("\nFlags:", .{}); + clap.simpleHelp(PackageManager.pack_params); Output.pretty("\n\n" ++ outro_text ++ "\n", .{}); Output.flush(); }, @@ -9371,7 +9484,7 @@ pub const PackageManager = struct { pub fn parse(allocator: std.mem.Allocator, comptime subcommand: Subcommand) !CommandLineArguments { Output.is_verbose = Output.isVerbose(); - const params: []const ParamType = &switch (subcommand) { + const params: []const ParamType = switch (subcommand) { .install => install_params, .update => update_params, .pm => pm_params, @@ -9381,6 +9494,8 @@ pub const PackageManager = struct { .unlink => unlink_params, .patch => patch_params, .@"patch-commit" => patch_commit_params, + .outdated => outdated_params, + .pack => pack_params, }; var diag = clap.Diagnostic{}; @@ -9416,6 +9531,27 @@ pub const PackageManager = struct { cli.trusted = args.flag("--trust"); cli.no_summary = args.flag("--no-summary"); + // commands that support --filter + if (comptime subcommand.supportsWorkspaceFiltering()) { + cli.filters = args.options("--filter"); + } + + if (comptime subcommand == .outdated) { + // fake --dry-run, we don't actually resolve+clean the lockfile + cli.dry_run = true; + // cli.json_output = args.flag("--json"); + } + + if (comptime subcommand == .pack or subcommand == .pm) { + if (args.option("--destination")) |dest| { + cli.pack_destination = dest; + } + + if (args.option("--gzip-level")) |level| { + cli.pack_gzip_level = level; + } + } + // link and unlink default to not saving, all others default to // saving. if (comptime subcommand == .link or subcommand == .unlink) { @@ -9446,8 +9582,6 @@ pub const PackageManager = struct { }; } - if (comptime subcommand == .@"patch-commit") {} - if (args.option("--config")) |opt| { cli.config = opt; } @@ -9461,23 +9595,9 @@ pub const PackageManager = struct { } if (args.option("--concurrent-scripts")) |concurrency| { - // var buf: [] cli.concurrent_scripts = std.fmt.parseInt(usize, concurrency, 10) catch null; } - // for (args.options("--omit")) |omit| { - // if (strings.eqlComptime(omit, "dev")) { - // cli.omit.dev = true; - // } else if (strings.eqlComptime(omit, "optional")) { - // cli.omit.optional = true; - // } else if (strings.eqlComptime(omit, "peer")) { - // cli.omit.peer = true; - // } else { - // Output.prettyErrorln("error: Invalid argument \"--omit\" must be one of \"dev\", \"optional\", or \"peer\". ", .{}); - // Global.crash(); - // } - // } - if (args.option("--cwd")) |cwd_| { var buf: bun.PathBuffer = undefined; var buf2: bun.PathBuffer = undefined; @@ -9577,6 +9697,64 @@ pub const PackageManager = struct { this.resolved_name.slice(this.version_buf); } + pub fn fromJS(globalThis: *JSC.JSGlobalObject, input: JSC.JSValue) JSC.JSValue { + var arena = std.heap.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + var stack = std.heap.stackFallback(1024, arena.allocator()); + const allocator = stack.get(); + var all_positionals = std.ArrayList([]const u8).init(allocator); + + var log = logger.Log.init(allocator); + + if (input.isString()) { + var input_str = input.toSliceCloneWithAllocator( + globalThis, + allocator, + ) orelse return .zero; + if (input_str.len > 0) + all_positionals.append(input_str.slice()) catch bun.outOfMemory(); + } else if (input.isArray()) { + var iter = input.arrayIterator(globalThis); + while (iter.next()) |item| { + const slice = item.toSliceCloneWithAllocator(globalThis, allocator) orelse return .zero; + if (globalThis.hasException()) return .zero; + if (slice.len == 0) continue; + all_positionals.append(slice.slice()) catch bun.outOfMemory(); + } + if (globalThis.hasException()) return .zero; + } else { + return .undefined; + } + + if (all_positionals.items.len == 0) { + return .undefined; + } + + var array = Array{}; + + const update_requests = parseWithError(allocator, &log, all_positionals.items, &array, .add, false) catch { + globalThis.throwValue(log.toJS(globalThis, bun.default_allocator, "Failed to parse dependencies")); + return .zero; + }; + if (update_requests.len == 0) return .undefined; + + if (log.msgs.items.len > 0) { + globalThis.throwValue(log.toJS(globalThis, bun.default_allocator, "Failed to parse dependencies")); + return .zero; + } + + if (update_requests[0].failed) { + globalThis.throw("Failed to parse dependencies", .{}); + return .zero; + } + + var object = JSC.JSValue.createEmptyObject(globalThis, 2); + var name_str = bun.String.init(update_requests[0].name); + object.put(globalThis, "name", name_str.transferToJS(globalThis)); + object.put(globalThis, "version", update_requests[0].version.toJS(update_requests[0].version_buf, globalThis)); + return object; + } + pub fn parse( allocator: std.mem.Allocator, log: *logger.Log, @@ -9584,11 +9762,22 @@ pub const PackageManager = struct { update_requests: *Array, subcommand: Subcommand, ) []UpdateRequest { + return parseWithError(allocator, log, positionals, update_requests, subcommand, true) catch Global.crash(); + } + + fn parseWithError( + allocator: std.mem.Allocator, + log: *logger.Log, + positionals: []const string, + update_requests: *Array, + subcommand: Subcommand, + fatal: bool, + ) ![]UpdateRequest { // first one is always either: // add // remove outer: for (positionals) |positional| { - var input: []u8 = @constCast(std.mem.trim(u8, positional, " \n\r\t")); + var input: []u8 = bun.default_allocator.dupe(u8, std.mem.trim(u8, positional, " \n\r\t")) catch bun.outOfMemory(); { var temp: [2048]u8 = undefined; const len = std.mem.replace(u8, input, "\\\\", "/", &temp); @@ -9629,10 +9818,17 @@ pub const PackageManager = struct { &SlicedString.init(input, value), log, ) orelse { - Output.prettyErrorln("error: unrecognised dependency format: {s}", .{ - positional, - }); - Global.crash(); + if (fatal) { + Output.errGeneric("unrecognised dependency format: {s}", .{ + positional, + }); + } else { + log.addErrorFmt(null, logger.Loc.Empty, allocator, "unrecognised dependency format: {s}", .{ + positional, + }) catch bun.outOfMemory(); + } + + return error.UnrecognizedDependencyFormat; }; if (alias != null and version.tag == .git) { if (Dependency.parseWithOptionalTag( @@ -9653,10 +9849,17 @@ pub const PackageManager = struct { .npm => version.value.npm.name.eql(placeholder, input, input), else => false, }) { - Output.prettyErrorln("error: unrecognised dependency format: {s}", .{ - positional, - }); - Global.crash(); + if (fatal) { + Output.errGeneric("unrecognised dependency format: {s}", .{ + positional, + }); + } else { + log.addErrorFmt(null, logger.Loc.Empty, allocator, "unrecognised dependency format: {s}", .{ + positional, + }) catch bun.outOfMemory(); + } + + return error.UnrecognizedDependencyFormat; } var request = UpdateRequest{ @@ -9685,38 +9888,50 @@ pub const PackageManager = struct { fn updatePackageJSONAndInstall( ctx: Command.Context, - comptime subcommand: Subcommand, + subcommand: Subcommand, ) !void { - var manager = init(ctx, subcommand) catch |err| brk: { + const cli = switch (subcommand) { + inline else => |cmd| try PackageManager.CommandLineArguments.parse(ctx.allocator, cmd), + }; + var manager, const original_cwd = init(ctx, cli, subcommand) catch |err| brk: { if (err == error.MissingPackageJSON) { switch (subcommand) { .update => { - Output.prettyErrorln("No package.json, so nothing to update\n", .{}); + Output.prettyErrorln("No package.json, so nothing to update", .{}); Global.crash(); }, .remove => { - Output.prettyErrorln("No package.json, so nothing to remove\n", .{}); + Output.prettyErrorln("No package.json, so nothing to remove", .{}); Global.crash(); }, .patch, .@"patch-commit" => { - Output.prettyErrorln("No package.json, so nothing to patch\n", .{}); + Output.prettyErrorln("No package.json, so nothing to patch", .{}); Global.crash(); }, else => { try attemptToCreatePackageJSON(); - break :brk try PackageManager.init(ctx, subcommand); + break :brk try PackageManager.init(ctx, cli, subcommand); }, } } return err; }; + defer ctx.allocator.free(original_cwd); if (manager.options.shouldPrintCommandName()) { - Output.prettyErrorln("bun " ++ @tagName(subcommand) ++ " v" ++ Global.package_json_version_with_sha ++ "\n", .{}); + Output.prettyErrorln("bun {s} v" ++ Global.package_json_version_with_sha ++ "\n", .{@tagName(subcommand)}); Output.flush(); } + // When you run `bun add -g ` or `bun install -g ` and the global bin dir is not in $PATH + // We should tell the user to add it to $PATH so they don't get confused. + if (subcommand.canGloballyInstallPackages()) { + if (manager.options.global and manager.options.log_level != .silent) { + manager.track_installed_bin = .{ .pending = {} }; + } + } + switch (manager.options.log_level) { inline else => |log_level| try manager.updatePackageJSONAndInstallWithManager(ctx, log_level), } @@ -9728,6 +9943,122 @@ pub const PackageManager = struct { if (manager.any_failed_to_install) { Global.exit(1); } + + // Check if we need to print a warning like: + // + // > warn: To run "vite", add the global bin folder to $PATH: + // > + // > fish_add_path "/private/tmp/test" + // + if (subcommand.canGloballyInstallPackages()) { + if (manager.options.global) { + if (manager.options.bin_path.len > 0 and manager.track_installed_bin == .basename) { + const needs_to_print = if (bun.getenvZ("PATH")) |PATH| + // This is not perfect + // + // If you already have a different binary of the same + // name, it will not detect that case. + // + // The problem is there are too many edgecases with filesystem paths. + // + // We want to veer towards false negative than false + // positive. It would be annoying if this message + // appears unnecessarily. It's kind of okay if it doesn't appear + // when it should. + // + // If you set BUN_INSTALL_BIN to "/tmp/woo" on macOS and + // we just checked for "/tmp/woo" in $PATH, it would + // incorrectly print a warning because /tmp/ on macOS is + // aliased to /private/tmp/ + // + // Another scenario is case-insensitive filesystems. If you + // have a binary called "esbuild" in /tmp/TeST and you + // install esbuild, it will not detect that case if we naively + // just checked for "esbuild" in $PATH where "$PATH" is /tmp/test + bun.which( + &package_json_cwd_buf, + PATH, + bun.fs.FileSystem.instance.top_level_dir, + manager.track_installed_bin.basename, + ) == null + else + true; + + if (needs_to_print) { + const MoreInstructions = struct { + shell: bun.CLI.ShellCompletions.Shell = .unknown, + folder: []const u8, + + // Convert "/Users/Jarred Sumner" => "/Users/Jarred\ Sumner" + const ShellPathFormatter = struct { + folder: []const u8, + + pub fn format(instructions: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + var remaining = instructions.folder; + while (bun.strings.indexOfChar(remaining, ' ')) |space| { + try writer.print( + "{}", + .{bun.fmt.fmtPath(u8, remaining[0..space], .{ + .escape_backslashes = true, + .path_sep = if (Environment.isWindows) .windows else .posix, + })}, + ); + try writer.writeAll("\\ "); + remaining = remaining[@min(space + 1, remaining.len)..]; + } + + try writer.print( + "{}", + .{bun.fmt.fmtPath(u8, remaining, .{ + .escape_backslashes = true, + .path_sep = if (Environment.isWindows) .windows else .posix, + })}, + ); + } + }; + + pub fn format(instructions: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + const path = ShellPathFormatter{ .folder = instructions.folder }; + switch (instructions.shell) { + .unknown => { + // Unfortunately really difficult to do this in one line on PowerShell. + try writer.print("{}", .{path}); + }, + .bash => { + try writer.print("export PATH=\"{}:$PATH\"", .{path}); + }, + .zsh => { + try writer.print("export PATH=\"{}:$PATH\"", .{path}); + }, + .fish => { + // Regular quotes will do here. + try writer.print("fish_add_path {}", .{bun.fmt.quote(instructions.folder)}); + }, + .pwsh => { + try writer.print("$env:PATH += \";{}\"", .{path}); + }, + } + } + }; + + Output.prettyError("\n", .{}); + + Output.warn( + \\To run {}, add the global bin folder to $PATH: + \\ + \\{} + \\ + , + .{ + bun.fmt.quote(manager.track_installed_bin.basename), + MoreInstructions{ .shell = bun.CLI.ShellCompletions.Shell.fromEnv([]const u8, bun.getenvZ("SHELL") orelse ""), .folder = manager.options.bin_path }, + }, + ); + Output.flush(); + } + } + } + } } fn updatePackageJSONAndInstallWithManager( @@ -9762,21 +10093,19 @@ pub const PackageManager = struct { &[_]UpdateRequest{} else UpdateRequest.parse(ctx.allocator, ctx.log, manager.options.positionals[1..], &update_requests, manager.subcommand); - switch (manager.subcommand) { - inline else => |subcommand| try manager.updatePackageJSONAndInstallWithManagerWithUpdates( - ctx, - updates, - subcommand, - log_level, - ), - } + try manager.updatePackageJSONAndInstallWithManagerWithUpdates( + ctx, + updates, + manager.subcommand, + log_level, + ); } fn updatePackageJSONAndInstallWithManagerWithUpdates( manager: *PackageManager, ctx: Command.Context, updates: []UpdateRequest, - comptime subcommand: Subcommand, + subcommand: Subcommand, comptime log_level: Options.LogLevel, ) !void { if (manager.log.errors > 0) { @@ -9796,6 +10125,7 @@ pub const PackageManager = struct { manager.original_package_json_path, .{ .always_decode_escape_sequences = false, + .guess_indentation = true, }, )) { .parse_err => |err| { @@ -9819,6 +10149,7 @@ pub const PackageManager = struct { }, .entry => |entry| entry, }; + const current_package_json_indent = current_package_json.indentation; // If there originally was a newline at the end of their package.json, preserve it // so that we don't cause unnecessary diffs in their git history. @@ -9828,17 +10159,17 @@ pub const PackageManager = struct { if (subcommand == .remove) { if (current_package_json.root.data != .e_object) { - Output.errGeneric("package.json is not an Object {{}}, so there's nothing to " ++ @tagName(subcommand) ++ "!", .{}); + Output.errGeneric("package.json is not an Object {{}}, so there's nothing to {s}!", .{@tagName(subcommand)}); Global.crash(); } else if (current_package_json.root.data.e_object.properties.len == 0) { - Output.errGeneric("package.json is empty {{}}, so there's nothing to " ++ @tagName(subcommand) ++ "!", .{}); + Output.errGeneric("package.json is empty {{}}, so there's nothing to {s}!", .{@tagName(subcommand)}); Global.crash(); } else if (current_package_json.root.asProperty("devDependencies") == null and current_package_json.root.asProperty("dependencies") == null and current_package_json.root.asProperty("optionalDependencies") == null and current_package_json.root.asProperty("peerDependencies") == null) { - Output.prettyErrorln("package.json doesn't have dependencies, there's nothing to " ++ @tagName(subcommand) ++ "!", .{}); + Output.prettyErrorln("package.json doesn't have dependencies, there's nothing to {s}!", .{@tagName(subcommand)}); Global.exit(0); } } @@ -9851,6 +10182,7 @@ pub const PackageManager = struct { "dependencies"; var any_changes = false; + var not_in_workspace_root: ?PatchCommitResult = null; switch (subcommand) { .remove => { // if we're removing, they don't have to specify where it is installed in the dependencies list @@ -9929,12 +10261,18 @@ pub const PackageManager = struct { if (manager.options.patch_features == .commit) { var pathbuf: bun.PathBuffer = undefined; if (try manager.doPatchCommit(&pathbuf, log_level)) |stuff| { - try PackageJSONEditor.editPatchedDependencies( - manager, - ¤t_package_json.root, - stuff.patch_key, - stuff.patchfile_path, - ); + // we're inside a workspace package, we need to edit the + // root json, not the `current_package_json` + if (stuff.not_in_workspace_root) { + not_in_workspace_root = stuff; + } else { + try PackageJSONEditor.editPatchedDependencies( + manager, + ¤t_package_json.root, + stuff.patch_key, + stuff.patchfile_path, + ); + } } } }, @@ -9948,7 +10286,15 @@ pub const PackageManager = struct { buffer_writer.append_newline = preserve_trailing_newline_at_eof_for_package_json; var package_json_writer = JSPrinter.BufferPrinter.init(buffer_writer); - var written = JSPrinter.printJSON(@TypeOf(&package_json_writer), &package_json_writer, current_package_json.root, ¤t_package_json.source) catch |err| { + var written = JSPrinter.printJSON( + @TypeOf(&package_json_writer), + &package_json_writer, + current_package_json.root, + ¤t_package_json.source, + .{ + .indent = current_package_json_indent, + }, + ) catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); Global.crash(); }; @@ -9968,35 +10314,77 @@ pub const PackageManager = struct { // may or may not be the package json we are editing const top_level_dir_without_trailing_slash = strings.withoutTrailingSlash(FileSystem.instance.top_level_dir); + var root_package_json_path_buf: bun.PathBuffer = undefined; - @memcpy(root_package_json_path_buf[0..top_level_dir_without_trailing_slash.len], top_level_dir_without_trailing_slash); - @memcpy(root_package_json_path_buf[top_level_dir_without_trailing_slash.len..][0.."/package.json".len], "/package.json"); - const root_package_json_path = root_package_json_path_buf[0 .. top_level_dir_without_trailing_slash.len + "/package.json".len]; + const root_package_json_source, const root_package_json_path = brk: { + @memcpy(root_package_json_path_buf[0..top_level_dir_without_trailing_slash.len], top_level_dir_without_trailing_slash); + @memcpy(root_package_json_path_buf[top_level_dir_without_trailing_slash.len..][0.."/package.json".len], "/package.json"); + const root_package_json_path = root_package_json_path_buf[0 .. top_level_dir_without_trailing_slash.len + "/package.json".len]; + root_package_json_path_buf[root_package_json_path.len] = 0; + + // The lifetime of this pointer is only valid until the next call to `getWithPath`, which can happen after this scope. + // https://github.com/oven-sh/bun/issues/12288 + const root_package_json = switch (manager.workspace_package_json_cache.getWithPath( + manager.allocator, + manager.log, + root_package_json_path, + .{ + .guess_indentation = true, + }, + )) { + .parse_err => |err| { + switch (Output.enable_ansi_colors) { + inline else => |enable_ansi_colors| { + manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; + }, + } + Output.errGeneric("failed to parse package.json \"{s}\": {s}", .{ + root_package_json_path, + @errorName(err), + }); + Global.crash(); + }, + .read_err => |err| { + Output.errGeneric("failed to read package.json \"{s}\": {s}", .{ + manager.original_package_json_path, + @errorName(err), + }); + Global.crash(); + }, + .entry => |entry| entry, + }; - const root_package_json = switch (manager.workspace_package_json_cache.getWithPath(manager.allocator, manager.log, root_package_json_path, .{})) { - .parse_err => |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; + if (not_in_workspace_root) |stuff| { + try PackageJSONEditor.editPatchedDependencies( + manager, + &root_package_json.root, + stuff.patch_key, + stuff.patchfile_path, + ); + var buffer_writer2 = try JSPrinter.BufferWriter.init(manager.allocator); + try buffer_writer2.buffer.list.ensureTotalCapacity(manager.allocator, root_package_json.source.contents.len + 1); + buffer_writer2.append_newline = preserve_trailing_newline_at_eof_for_package_json; + var package_json_writer2 = JSPrinter.BufferPrinter.init(buffer_writer2); + + _ = JSPrinter.printJSON( + @TypeOf(&package_json_writer2), + &package_json_writer2, + root_package_json.root, + &root_package_json.source, + .{ + .indent = root_package_json.indentation, }, - } - Output.errGeneric("failed to parse package.json \"{s}\": {s}", .{ - root_package_json_path, - @errorName(err), - }); - Global.crash(); - }, - .read_err => |err| { - Output.errGeneric("failed to read package.json \"{s}\": {s}", .{ - manager.original_package_json_path, - @errorName(err), - }); - Global.crash(); - }, - .entry => |entry| entry, + ) catch |err| { + Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); + Global.crash(); + }; + root_package_json.source.contents = try manager.allocator.dupe(u8, package_json_writer2.ctx.writtenWithoutTrailingZero()); + } + + break :brk .{ root_package_json.source.contents, root_package_json_path_buf[0..root_package_json_path.len :0] }; }; - try manager.installWithManager(ctx, root_package_json.source.contents, log_level); + try manager.installWithManager(ctx, root_package_json_source, log_level); if (subcommand == .update or subcommand == .add or subcommand == .link) { for (updates) |request| { @@ -10046,6 +10434,9 @@ pub const PackageManager = struct { &package_json_writer_two, new_package_json, &source, + .{ + .indent = current_package_json_indent, + }, ) catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); Global.crash(); @@ -10055,17 +10446,22 @@ pub const PackageManager = struct { } if (manager.options.do.write_package_json) { + const source, const path = if (manager.options.patch_features == .commit) + .{ root_package_json_source, root_package_json_path } + else + .{ new_package_json_source, manager.original_package_json_path }; + // Now that we've run the install step // We can save our in-memory package.json to disk const workspace_package_json_file = (try bun.sys.File.openat( bun.invalid_fd, - manager.original_package_json_path, + path, bun.O.RDWR, 0, ).unwrap()).handle.asFile(); - try workspace_package_json_file.pwriteAll(new_package_json_source, 0); - std.posix.ftruncate(workspace_package_json_file.handle, new_package_json_source.len) catch {}; + try workspace_package_json_file.pwriteAll(source, 0); + std.posix.ftruncate(workspace_package_json_file.handle, source.len) catch {}; workspace_package_json_file.close(); if (subcommand == .remove) { @@ -10127,6 +10523,16 @@ pub const PackageManager = struct { } } + fn nodeModulesFolderForDependencyIDs(iterator: *Lockfile.Tree.Iterator, ids: []const IdPair) !?Lockfile.Tree.NodeModulesFolder { + while (iterator.nextNodeModulesFolder(null)) |node_modules| { + for (ids) |id| { + _ = std.mem.indexOfScalar(DependencyID, node_modules.dependencies, id[0]) orelse continue; + return node_modules; + } + } + return null; + } + fn nodeModulesFolderForDependencyID(iterator: *Lockfile.Tree.Iterator, dependency_id: DependencyID) !?Lockfile.Tree.NodeModulesFolder { while (iterator.nextNodeModulesFolder(null)) |node_modules| { _ = std.mem.indexOfScalar(DependencyID, node_modules.dependencies, dependency_id) orelse continue; @@ -10136,65 +10542,154 @@ pub const PackageManager = struct { return null; } - fn pkgDepIdForNameAndVersion( + const IdPair = struct { DependencyID, PackageID }; + + fn pkgInfoForNameAndVersion( lockfile: *Lockfile, + iterator: *Lockfile.Tree.Iterator, pkg_maybe_version_to_patch: []const u8, name: []const u8, version: ?[]const u8, - ) struct { PackageID, DependencyID } { + ) struct { PackageID, Lockfile.Tree.NodeModulesFolder } { + var sfb = std.heap.stackFallback(@sizeOf(IdPair) * 4, lockfile.allocator); + var pairs = std.ArrayList(IdPair).initCapacity(sfb.get(), 8) catch bun.outOfMemory(); + defer pairs.deinit(); + const name_hash = String.Builder.stringHash(name); const strbuf = lockfile.buffers.string_bytes.items; - const dependency_id: DependencyID, const pkg_id: PackageID = brk: { - var buf: [1024]u8 = undefined; - const dependencies = lockfile.buffers.dependencies.items; - - var matches_found: u32 = 0; - var maybe_first_match: ?struct { DependencyID, PackageID } = null; - for (dependencies, 0..) |dep, dep_id| { - if (dep.name_hash != name_hash) continue; - matches_found += 1; - const pkg_id = lockfile.buffers.resolutions.items[dep_id]; - if (pkg_id == invalid_package_id) continue; - const pkg = lockfile.packages.get(pkg_id); - if (version) |v| { - const label = std.fmt.bufPrint(buf[0..], "{}", .{pkg.resolution.fmt(strbuf, .posix)}) catch @panic("Resolution name too long"); - if (std.mem.eql(u8, label, v)) break :brk .{ @intCast(dep_id), pkg_id }; - } else maybe_first_match = .{ @intCast(dep_id), pkg_id }; + var buf: [1024]u8 = undefined; + const dependencies = lockfile.buffers.dependencies.items; + + for (dependencies, 0..) |dep, dep_id| { + if (dep.name_hash != name_hash) continue; + const pkg_id = lockfile.buffers.resolutions.items[dep_id]; + if (pkg_id == invalid_package_id) continue; + const pkg = lockfile.packages.get(pkg_id); + if (version) |v| { + const label = std.fmt.bufPrint(buf[0..], "{}", .{pkg.resolution.fmt(strbuf, .posix)}) catch @panic("Resolution name too long"); + if (std.mem.eql(u8, label, v)) { + pairs.append(.{ @intCast(dep_id), pkg_id }) catch bun.outOfMemory(); + } + } else { + pairs.append(.{ @intCast(dep_id), pkg_id }) catch bun.outOfMemory(); + } + } + + if (pairs.items.len == 0) { + Output.prettyErrorln("\nerror: package {s} not found", .{pkg_maybe_version_to_patch}); + Global.crash(); + return; + } + + // user supplied a version e.g. `is-even@1.0.0` + if (version != null) { + if (pairs.items.len == 1) { + const dep_id, const pkg_id = pairs.items[0]; + const folder = (try nodeModulesFolderForDependencyID(iterator, dep_id)) orelse { + Output.prettyError( + "error: could not find the folder for {s} in node_modules\n", + .{pkg_maybe_version_to_patch}, + ); + Global.crash(); + }; + return .{ + pkg_id, + folder, + }; } - const first_match = maybe_first_match orelse { - Output.prettyErrorln("\nerror: package {s} not found", .{pkg_maybe_version_to_patch}); + // we found multiple dependents of the supplied pkg + version + // the final package in the node_modules might be hoisted + // so we are going to try looking for each dep id in node_modules + _, const pkg_id = pairs.items[0]; + const folder = (try nodeModulesFolderForDependencyIDs(iterator, pairs.items)) orelse { + Output.prettyError( + "error: could not find the folder for {s} in node_modules\n", + .{pkg_maybe_version_to_patch}, + ); Global.crash(); - return; }; - if (matches_found > 1) { - Output.prettyErrorln( - "\nerror: Found multiple versions of {s}, please specify a precise version from the following list:\n", - .{name}, + return .{ + pkg_id, + folder, + }; + } + + // Otherwise the user did not supply a version, just the pkg name + + // Only one match, let's use it + if (pairs.items.len == 1) { + const dep_id, const pkg_id = pairs.items[0]; + const folder = (try nodeModulesFolderForDependencyID(iterator, dep_id)) orelse { + Output.prettyError( + "error: could not find the folder for {s} in node_modules\n", + .{pkg_maybe_version_to_patch}, ); - var i: usize = 0; - const pkg_hashes = lockfile.packages.items(.name_hash); - while (i < lockfile.packages.len) { - if (std.mem.indexOfScalar(u64, pkg_hashes[i..], name_hash)) |idx| { - defer i += idx + 1; - const pkg_id = i + idx; - const pkg = lockfile.packages.get(pkg_id); - if (!std.mem.eql(u8, pkg.name.slice(strbuf), name)) continue; - - Output.prettyError(" {s}@{}\n", .{ pkg.name.slice(strbuf), pkg.resolution.fmt(strbuf, .posix) }); - } else break; - } Global.crash(); - return; - } + }; + return .{ + pkg_id, + folder, + }; + } + + // Otherwise we have multiple matches + // + // There are two cases: + // a) the multiple matches are all the same underlying package (this happens because there could be multiple dependents of the same package) + // b) the matches are actually different packages, we'll prompt the user to select which one - break :brk .{ first_match[0], first_match[1] }; + _, const pkg_id = pairs.items[0]; + const count = count: { + var count: u32 = 0; + for (pairs.items) |pair| { + if (pair[1] == pkg_id) count += 1; + } + break :count count; }; - return .{ pkg_id, dependency_id }; + // Disambiguate case a) from b) + if (count == pairs.items.len) { + // It may be hoisted, so we'll try the first one that matches + const folder = (try nodeModulesFolderForDependencyIDs(iterator, pairs.items)) orelse { + Output.prettyError( + "error: could not find the folder for {s} in node_modules\n", + .{pkg_maybe_version_to_patch}, + ); + Global.crash(); + }; + return .{ + pkg_id, + folder, + }; + } + + Output.prettyErrorln( + "\nerror: Found multiple versions of {s}, please specify a precise version from the following list:\n", + .{name}, + ); + var i: usize = 0; + while (i < pairs.items.len) : (i += 1) { + _, const pkgid = pairs.items[i]; + if (pkgid == invalid_package_id) + continue; + + const pkg = lockfile.packages.get(pkgid); + + Output.prettyError(" {s}@{}\n", .{ pkg.name.slice(strbuf), pkg.resolution.fmt(strbuf, .posix) }); + + if (i + 1 < pairs.items.len) { + for (pairs.items[i + 1 ..]) |*p| { + if (p[1] == pkgid) { + p[1] = invalid_package_id; + } + } + } + } + Global.crash(); } const PatchArgKind = enum { @@ -10202,16 +10697,20 @@ pub const PackageManager = struct { name_and_version, pub fn fromArg(argument: []const u8) PatchArgKind { - if (bun.strings.hasPrefixComptime(argument, "node_modules/")) return .path; - if (bun.path.Platform.auto.isAbsolute(argument) and bun.strings.contains(argument, "node_modules/")) return .path; - if (comptime bun.Environment.isWindows) { - if (bun.strings.hasPrefix(argument, "node_modules\\")) return .path; - if (bun.path.Platform.auto.isAbsolute(argument) and bun.strings.contains(argument, "node_modules\\")) return .path; - } + if (bun.strings.containsComptime(argument, "node_modules/")) return .path; + if (bun.Environment.isWindows and bun.strings.hasPrefix(argument, "node_modules\\")) return .path; return .name_and_version; } }; + fn pathArgumentRelativeToRootWorkspacePackage(manager: *PackageManager, lockfile: *const Lockfile, argument: []const u8) ?[]const u8 { + const workspace_package_id = manager.root_package_id.get(lockfile, manager.workspace_name_hash); + if (workspace_package_id == 0) return null; + const workspace_res = lockfile.packages.items(.resolution)[workspace_package_id]; + const rel_path: []const u8 = workspace_res.value.workspace.slice(lockfile.buffers.string_bytes.items); + return bun.default_allocator.dupe(u8, bun.path.join(&[_][]const u8{ rel_path, argument }, .posix)) catch bun.outOfMemory(); + } + /// 1. Arg is either: /// - name and possibly version (e.g. "is-even" or "is-even@1.0.0") /// - path to package in node_modules @@ -10220,7 +10719,7 @@ pub const PackageManager = struct { /// 4. Print to user fn preparePatch(manager: *PackageManager) !void { const strbuf = manager.lockfile.buffers.string_bytes.items; - const argument = manager.options.positionals[1]; + var argument = manager.options.positionals[1]; const arg_kind: PatchArgKind = PatchArgKind.fromArg(argument); @@ -10230,9 +10729,24 @@ pub const PackageManager = struct { var win_normalizer: if (bun.Environment.isWindows) bun.PathBuffer else struct {} = undefined; + const not_in_workspace_root = manager.root_package_id.get(manager.lockfile, manager.workspace_name_hash) != 0; + var free_argument = false; + argument = if (arg_kind == .path and + not_in_workspace_root and + (!bun.path.Platform.posix.isAbsolute(argument) or (bun.Environment.isWindows and !bun.path.Platform.windows.isAbsolute(argument)))) + brk: { + if (pathArgumentRelativeToRootWorkspacePackage(manager, manager.lockfile, argument)) |rel_path| { + free_argument = true; + break :brk rel_path; + } + break :brk argument; + } else argument; + defer if (free_argument) manager.allocator.free(argument); + const cache_dir: std.fs.Dir, const cache_dir_subpath: []const u8, const module_folder: []const u8, const pkg_name: []const u8 = switch (arg_kind) { .path => brk: { var lockfile = manager.lockfile; + const package_json_source: logger.Source = src: { const package_json_path = bun.path.joinZ(&[_][]const u8{ argument, "package.json" }, .auto); @@ -10331,17 +10845,7 @@ pub const PackageManager = struct { .name_and_version => brk: { const pkg_maybe_version_to_patch = argument; const name, const version = Dependency.splitNameAndVersion(pkg_maybe_version_to_patch); - const result = pkgDepIdForNameAndVersion(manager.lockfile, pkg_maybe_version_to_patch, name, version); - const pkg_id = result[0]; - const dependency_id = result[1]; - - const folder = (try nodeModulesFolderForDependencyID(&iterator, dependency_id)) orelse { - Output.prettyError( - "error: could not find the folder for {s} in node_modules\n", - .{pkg_maybe_version_to_patch}, - ); - Global.crash(); - }; + const pkg_id, const folder = pkgInfoForNameAndVersion(manager.lockfile, &iterator, pkg_maybe_version_to_patch, name, version); const pkg = manager.lockfile.packages.get(pkg_id); const pkg_name = pkg.name.slice(strbuf); @@ -10392,8 +10896,14 @@ pub const PackageManager = struct { Global.crash(); }; - Output.pretty("\nTo patch {s}, edit the following folder:\n\n {s}\n", .{ pkg_name, module_folder }); - Output.pretty("\nOnce you're done with your changes, run:\n\n bun patch --commit '{s}'\n", .{module_folder}); + if (not_in_workspace_root) { + var bufn: bun.PathBuffer = undefined; + Output.pretty("\nTo patch {s}, edit the following folder:\n\n {s}\n", .{ pkg_name, bun.path.joinStringBuf(bufn[0..], &[_][]const u8{ bun.fs.FileSystem.instance.topLevelDirWithoutTrailingSlash(), module_folder }, .posix) }); + Output.pretty("\nOnce you're done with your changes, run:\n\n bun patch --commit '{s}'\n", .{bun.path.joinStringBuf(bufn[0..], &[_][]const u8{ bun.fs.FileSystem.instance.topLevelDirWithoutTrailingSlash(), module_folder }, .posix)}); + } else { + Output.pretty("\nTo patch {s}, edit the following folder:\n\n {s}\n", .{ pkg_name, module_folder }); + Output.pretty("\nOnce you're done with your changes, run:\n\n bun patch --commit '{s}'\n", .{module_folder}); + } return; } @@ -10421,6 +10931,7 @@ pub const PackageManager = struct { out_dir: if (bun.Environment.isWindows) []const u16 else void, buf1: if (bun.Environment.isWindows) []u16 else void, buf2: if (bun.Environment.isWindows) []u16 else void, + tmpdir_in_node_modules: if (bun.Environment.isWindows) std.fs.Dir else void, ) !u32 { var real_file_count: u32 = 0; @@ -10435,10 +10946,10 @@ pub const PackageManager = struct { const openFile = std.fs.Dir.openFile; const createFile = std.fs.Dir.createFile; - // - rename node_modules/$PKG/$FILE -> node_modules/$PKG/$TMPNAME - // - create node_modules/$PKG/$FILE - // - copy: cache/$PKG/$FILE -> node_modules/$PKG/$FILE - // - unlink: $TMPDIR/$FILE + // 1. rename original file in node_modules to tmp_dir_in_node_modules + // 2. create the file again + // 3. copy cache flie to the newly re-created file + // 4. profit if (comptime bun.Environment.isWindows) { var tmpbuf: [1024]u8 = undefined; const basename = bun.strings.fromWPath(pathbuf2[0..], entry.basename); @@ -10454,8 +10965,9 @@ pub const PackageManager = struct { if (bun.sys.renameatConcurrently( bun.toFD(destination_dir_.fd), entrypathZ, - bun.toFD(destination_dir_.fd), + bun.toFD(tmpdir_in_node_modules.fd), tmpname, + .{ .move_fallback = true }, ).asErr()) |e| { Output.prettyError("error: copying file {}", .{e}); Global.crash(); @@ -10470,7 +10982,7 @@ pub const PackageManager = struct { const infile_path = bun.path.joinStringBufWZ(buf1, &[_][]const u16{ in_dir, entry.path }, .auto); const outfile_path = bun.path.joinStringBufWZ(buf2, &[_][]const u16{ out_dir, entry.path }, .auto); - bun.copyFileWithState(infile_path, outfile_path, ©_file_state) catch |err| { + bun.copyFileWithState(infile_path, outfile_path, ©_file_state).unwrap() catch |err| { Output.prettyError("{s}: copying file {}", .{ @errorName(err), bun.fmt.fmtOSPath(entry.path, .{}) }); Global.crash(); }; @@ -10495,7 +11007,7 @@ pub const PackageManager = struct { const stat = in_file.stat() catch continue; _ = C.fchmod(outfile.handle, @intCast(stat.mode)); - bun.copyFileWithState(in_file.handle, outfile.handle, ©_file_state) catch |err| { + bun.copyFileWithState(in_file.handle, outfile.handle, ©_file_state).unwrap() catch |err| { Output.prettyError("{s}: copying file {}", .{ @errorName(err), bun.fmt.fmtOSPath(entry.path, .{}) }); Global.crash(); }; @@ -10533,6 +11045,15 @@ pub const PackageManager = struct { Global.crash(); } out_dir = buf2[0..outlen]; + var tmpbuf: [1024]u8 = undefined; + const tmpname = bun.span(bun.fs.FileSystem.instance.tmpname("tffbp", tmpbuf[0..], bun.fastRandom()) catch |e| { + Output.prettyError("error: copying file {s}", .{@errorName(e)}); + Global.crash(); + }); + const temp_folder_in_node_modules = try node_modules_folder.makeOpenPath(tmpname, .{}); + defer { + node_modules_folder.deleteTree(tmpname) catch {}; + } _ = try FileCopier.copy( node_modules_folder, &walker, @@ -10540,6 +11061,7 @@ pub const PackageManager = struct { out_dir, &buf1, &buf2, + temp_folder_in_node_modules, ); } else if (Environment.isPosix) { _ = try FileCopier.copy( @@ -10549,6 +11071,7 @@ pub const PackageManager = struct { {}, {}, {}, + {}, ); } } @@ -10556,6 +11079,7 @@ pub const PackageManager = struct { const PatchCommitResult = struct { patch_key: []const u8, patchfile_path: []const u8, + not_in_workspace_root: bool = false, }; /// - Arg is the dir containing the package with changes OR name and version @@ -10606,10 +11130,23 @@ pub const PackageManager = struct { .ok => {}, } - const argument = manager.options.positionals[1]; - + var argument = manager.options.positionals[1]; const arg_kind: PatchArgKind = PatchArgKind.fromArg(argument); + const not_in_workspace_root = manager.root_package_id.get(lockfile, manager.workspace_name_hash) != 0; + var free_argument = false; + argument = if (arg_kind == .path and + not_in_workspace_root and + (!bun.path.Platform.posix.isAbsolute(argument) or (bun.Environment.isWindows and !bun.path.Platform.windows.isAbsolute(argument)))) + brk: { + if (pathArgumentRelativeToRootWorkspacePackage(manager, lockfile, argument)) |rel_path| { + free_argument = true; + break :brk rel_path; + } + break :brk argument; + } else argument; + defer if (free_argument) manager.allocator.free(argument); + // Attempt to open the existing node_modules folder var root_node_modules = switch (bun.sys.openatOSPath(bun.FD.cwd(), bun.OSPathLiteral("node_modules"), bun.O.DIRECTORY | bun.O.RDONLY, 0o755)) { .result => |fd| std.fs.Dir{ .fd = fd.cast() }, @@ -10707,19 +11244,8 @@ pub const PackageManager = struct { }, .name_and_version => brk: { const name, const version = Dependency.splitNameAndVersion(argument); - const result = pkgDepIdForNameAndVersion(lockfile, argument, name, version); - const pkg_id: PackageID = result[0]; - const dependency_id: DependencyID = result[1]; - const node_modules = (try nodeModulesFolderForDependencyID( - &iterator, - dependency_id, - )) orelse { - Output.prettyError( - "error: could not find the folder for {s} in node_modules\n", - .{argument}, - ); - Global.crash(); - }; + const pkg_id, const node_modules = pkgInfoForNameAndVersion(lockfile, &iterator, argument, name, version); + const changes_dir = bun.path.joinZBuf(pathbuf[0..], &[_][]const u8{ node_modules.relative_path, name, @@ -10768,7 +11294,7 @@ pub const PackageManager = struct { }, .posix); }; - const random_tempdir = bun.span(bun.fs.FileSystem.instance.tmpname(name, buf2[0..], bun.fastRandom()) catch |e| { + const random_tempdir = bun.span(bun.fs.FileSystem.instance.tmpname("node_modules_tmp", buf2[0..], bun.fastRandom()) catch |e| { Output.prettyError( "error: failed to make tempdir {s}\n", .{@errorName(e)}, @@ -10796,12 +11322,13 @@ pub const PackageManager = struct { "node_modules", bun.toFD(root_node_modules.fd), random_tempdir, + .{ .move_fallback = true }, ).asErr()) |_| break :has_nested_node_modules false; break :has_nested_node_modules true; }; - const patch_tag_tmpname = bun.span(bun.fs.FileSystem.instance.tmpname(name, buf3[0..], bun.fastRandom()) catch |e| { + const patch_tag_tmpname = bun.span(bun.fs.FileSystem.instance.tmpname("patch_tmp", buf3[0..], bun.fastRandom()) catch |e| { Output.prettyError( "error: failed to make tempdir {s}\n", .{@errorName(e)}, @@ -10836,6 +11363,7 @@ pub const PackageManager = struct { patch_tag, bun.toFD(root_node_modules.fd), patch_tag_tmpname, + .{ .move_fallback = true }, ).asErr()) |e| { Output.warn("failed renaming the bun patch tag, this may cause issues: {}", .{e}); break :has_bun_patch_tag null; @@ -10859,6 +11387,7 @@ pub const PackageManager = struct { random_tempdir, bun.toFD(new_folder_handle.fd), "node_modules", + .{ .move_fallback = true }, ).asErr()) |e| { Output.warn("failed renaming nested node_modules folder, this may cause issues: {}", .{e}); } @@ -10870,6 +11399,7 @@ pub const PackageManager = struct { patch_tag_tmpname, bun.toFD(new_folder_handle.fd), patch_tag, + .{ .move_fallback = true }, ).asErr()) |e| { Output.warn("failed renaming the bun patch tag, this may cause issues: {}", .{e}); } @@ -11026,6 +11556,7 @@ pub const PackageManager = struct { tempfile_name, bun.FD.cwd(), path_in_patches_dir, + .{ .move_fallback = true }, ).asErr()) |e| { Output.prettyError( "error: failed renaming patch file to patches dir {}\n", @@ -11041,6 +11572,7 @@ pub const PackageManager = struct { return .{ .patch_key = patch_key, .patchfile_path = patchfile_path, + .not_in_workspace_root = not_in_workspace_root, }; } @@ -11120,8 +11652,9 @@ pub const PackageManager = struct { var package_json_cwd_buf: bun.PathBuffer = undefined; pub var package_json_cwd: string = ""; - pub inline fn install(ctx: Command.Context) !void { - var manager = try init(ctx, .install); + pub fn install(ctx: Command.Context) !void { + const cli = try CommandLineArguments.parse(ctx.allocator, .install); + var manager, _ = try init(ctx, cli, .install); // switch to `bun add ` if (manager.options.positionals.len > 1) { @@ -11195,6 +11728,28 @@ pub const PackageManager = struct { } }; + pub const TreeContext = struct { + /// Each tree (other than the root tree) can accumulate packages it cannot install until + /// each parent tree has installed their packages. We keep arrays of these pending + /// packages for each tree, and drain them when a tree is completed (each of it's immediate + /// dependencies are installed). + /// + /// Trees are drained breadth first because if the current tree is completed from + /// the remaining pending installs, then any child tree has a higher chance of + /// being able to install it's dependencies + pending_installs: std.ArrayListUnmanaged(DependencyInstallContext) = .{}, + + binaries: Bin.PriorityQueue, + + /// Number of installed dependencies. Could be successful or failure. + install_count: usize = 0, + + pub fn deinit(this: *TreeContext, allocator: std.mem.Allocator) void { + this.pending_installs.deinit(allocator); + this.binaries.deinit(); + } + }; + pub const PackageInstaller = struct { manager: *PackageManager, lockfile: *Lockfile, @@ -11214,7 +11769,6 @@ pub const PackageManager = struct { bins: []const Bin, resolutions: []Resolution, node: *Progress.Node, - global_bin_dir: std.fs.Dir, destination_dir_subpath_buf: bun.PathBuffer = undefined, folder_path_buf: bun.PathBuffer = undefined, successfully_installed: Bitset, @@ -11226,8 +11780,6 @@ pub const PackageManager = struct { // /// set of completed tree ids completed_trees: Bitset, - /// tree id to number of successfully installed deps for id. when count == tree.dependencies.len, mark as complete above - tree_install_counts: []usize, /// the tree ids a tree depends on before it can run the lifecycle scripts of it's immediate dependencies tree_ids_to_trees_the_id_depends_on: Bitset.List, pending_lifecycle_scripts: std.ArrayListUnmanaged(struct { @@ -11235,25 +11787,29 @@ pub const PackageManager = struct { tree_id: Lockfile.Tree.Id, }) = .{}, - pending_installs_to_tree_id: []std.ArrayListUnmanaged(DependencyInstallContext), - trusted_dependencies_from_update_requests: std.AutoArrayHashMapUnmanaged(TruncatedPackageNameHash, void), + // uses same ids as lockfile.trees + trees: []TreeContext, + + seen_bin_links: bun.StringHashMap(void), + /// Increments the number of installed packages for a tree id and runs available scripts /// if the tree is finished. pub fn incrementTreeInstallCount( this: *PackageInstaller, tree_id: Lockfile.Tree.Id, + maybe_destination_dir: ?std.fs.Dir, comptime should_install_packages: bool, comptime log_level: Options.LogLevel, ) void { if (comptime Environment.allow_assert) { - bun.assert(tree_id != Lockfile.Tree.invalid_id); + bun.assertWithLocation(tree_id != Lockfile.Tree.invalid_id, @src()); } - const trees = this.lockfile.buffers.trees.items; - const current_count = this.tree_install_counts[tree_id]; - const max = trees[tree_id].dependencies.len; + const tree = &this.trees[tree_id]; + const current_count = tree.install_count; + const max = this.lockfile.buffers.trees.items[tree_id].dependencies.len; if (current_count == std.math.maxInt(usize)) { if (comptime Environment.allow_assert) @@ -11264,15 +11820,125 @@ pub const PackageManager = struct { const is_not_done = current_count + 1 < max; - this.tree_install_counts[tree_id] = if (is_not_done) current_count + 1 else std.math.maxInt(usize); + this.trees[tree_id].install_count = if (is_not_done) current_count + 1 else std.math.maxInt(usize); - if (!is_not_done) { - this.completed_trees.set(tree_id); - if (comptime should_install_packages) { - const force = false; - this.installAvailablePackages(log_level, force); + if (is_not_done) return; + + this.completed_trees.set(tree_id); + + if (maybe_destination_dir orelse (this.node_modules.makeAndOpenDir(this.root_node_modules_folder) catch null)) |_destination_dir| { + var destination_dir = _destination_dir; + defer { + if (maybe_destination_dir == null) { + destination_dir.close(); + } + } + + this.seen_bin_links.clearRetainingCapacity(); + + if (tree.binaries.count() > 0) { + var link_target_buf: bun.PathBuffer = undefined; + var link_dest_buf: bun.PathBuffer = undefined; + var link_rel_buf: bun.PathBuffer = undefined; + this.linkTreeBins(tree, destination_dir, &link_target_buf, &link_dest_buf, &link_rel_buf, log_level); + } + } + + if (comptime should_install_packages) { + const force = false; + this.installAvailablePackages(log_level, force); + } + this.runAvailableScripts(log_level); + } + + pub fn linkTreeBins( + this: *PackageInstaller, + tree: *TreeContext, + destination_dir: std.fs.Dir, + link_target_buf: []u8, + link_dest_buf: []u8, + link_rel_buf: []u8, + log_level: Options.LogLevel, + ) void { + const lockfile = this.lockfile; + const string_buf = lockfile.buffers.string_bytes.items; + while (tree.binaries.removeOrNull()) |dep_id| { + bun.assertWithLocation(dep_id < lockfile.buffers.dependencies.items.len, @src()); + const package_id = lockfile.buffers.resolutions.items[dep_id]; + bun.assertWithLocation(package_id != invalid_package_id, @src()); + const bin = this.bins[package_id]; + bun.assertWithLocation(bin.tag != .none, @src()); + + const alias = lockfile.buffers.dependencies.items[dep_id].name.slice(string_buf); + + var bin_linker: Bin.Linker = .{ + .bin = bin, + .global_bin_path = this.options.bin_path, + .global_bin_dir = this.options.global_bin_dir, + .package_name = strings.StringOrTinyString.init(alias), + .string_buf = string_buf, + .extern_string_buf = lockfile.buffers.extern_strings.items, + .seen = &this.seen_bin_links, + .node_modules_path = this.node_modules.path.items, + .node_modules = bun.toFD(destination_dir), + .abs_target_buf = link_target_buf, + .abs_dest_buf = link_dest_buf, + .rel_buf = link_rel_buf, + }; + + bin_linker.link(this.manager.options.global); + if (bin_linker.err) |err| { + if (log_level != .silent) { + this.manager.log.addErrorFmtNoLoc( + this.manager.allocator, + "Failed to link {s}: {s}", + .{ alias, @errorName(err) }, + ) catch bun.outOfMemory(); + } + + if (this.options.enable.fail_early) { + this.manager.crash(); + } + } + } + } + + pub fn linkRemainingBins(this: *PackageInstaller, comptime log_level: Options.LogLevel) void { + var depth_buf: Lockfile.Tree.Iterator.DepthBuf = undefined; + var node_modules_rel_path_buf: bun.PathBuffer = undefined; + @memcpy(node_modules_rel_path_buf[0.."node_modules".len], "node_modules"); + + var link_target_buf: bun.PathBuffer = undefined; + var link_dest_buf: bun.PathBuffer = undefined; + var link_rel_buf: bun.PathBuffer = undefined; + const lockfile = this.lockfile; + + for (this.trees, 0..) |*tree, tree_id| { + if (tree.binaries.count() > 0) { + this.seen_bin_links.clearRetainingCapacity(); + this.node_modules.path.items.len = strings.withoutTrailingSlash(FileSystem.instance.top_level_dir).len + 1; + const rel_path, _ = Lockfile.Tree.relativePathAndDepth( + lockfile, + @intCast(tree_id), + &node_modules_rel_path_buf, + &depth_buf, + ); + + this.node_modules.path.appendSlice(rel_path) catch bun.outOfMemory(); + + var destination_dir = this.node_modules.openDir(this.root_node_modules_folder) catch |err| { + if (log_level != .silent) { + Output.err(err, "Failed to open node_modules folder at {s}", .{ + bun.fmt.fmtPath(u8, this.node_modules.path.items, .{}), + }); + } + + continue; + }; + defer destination_dir.close(); + + this.linkTreeBins(tree, destination_dir, &link_target_buf, &link_dest_buf, &link_rel_buf, log_level); } - this.runAvailableScripts(log_level); } } @@ -11322,15 +11988,15 @@ pub const PackageManager = struct { const lockfile = this.lockfile; const resolutions = lockfile.buffers.resolutions.items; - for (this.pending_installs_to_tree_id, 0..) |*pending_installs, i| { + for (this.trees, 0..) |*tree, i| { if (force or this.canInstallPackageForTree(this.lockfile.buffers.trees.items, @intCast(i))) { - defer pending_installs.clearRetainingCapacity(); + defer tree.pending_installs.clearRetainingCapacity(); // If installing these packages completes the tree, we don't allow it // to call `installAvailablePackages` recursively. Starting at id 0 and // going up ensures we will reach any trees that will be able to install // packages upon completing the current tree - for (pending_installs.items) |context| { + for (tree.pending_installs.items) |context| { const package_id = resolutions[context.dependency_id]; const name = lockfile.str(&this.names[package_id]); const resolution = &this.resolutions[package_id]; @@ -11434,12 +12100,11 @@ pub const PackageManager = struct { pub fn deinit(this: *PackageInstaller) void { const allocator = this.manager.allocator; this.pending_lifecycle_scripts.deinit(this.manager.allocator); - for (this.pending_installs_to_tree_id) |*pending_installs| { - pending_installs.deinit(this.manager.allocator); - } - this.manager.allocator.free(this.pending_installs_to_tree_id); this.completed_trees.deinit(allocator); - allocator.free(this.tree_install_counts); + for (this.trees) |*node| { + node.deinit(allocator); + } + allocator.free(this.trees); this.tree_ids_to_trees_the_id_depends_on.deinit(allocator); this.node_modules.deinit(); this.trusted_dependencies_from_update_requests.deinit(allocator); @@ -11452,7 +12117,6 @@ pub const PackageManager = struct { this.names = packages.items(.name); this.bins = packages.items(.bin); this.resolutions = packages.items(.resolution); - this.tree_iterator.reload(this.lockfile); } /// Install versions of a package which are waiting on a network request @@ -11476,7 +12140,11 @@ pub const PackageManager = struct { if (!this.installEnqueuedPackagesImpl(name, task_id, log_level)) { if (comptime Environment.allow_assert) { - Output.panic("Ran callback to install enqueued packages, but there was no task associated with it. {d} {any}", .{ dependency_id, data.* }); + Output.panic("Ran callback to install enqueued packages, but there was no task associated with it. {}:{} (dependency_id: {d})", .{ + bun.fmt.quote(name), + bun.fmt.quote(data.url), + dependency_id, + }); } } } @@ -11538,8 +12206,8 @@ pub const PackageManager = struct { comptime log_level: Options.LogLevel, ) usize { if (comptime Environment.allow_assert) { - bun.assert(resolution_tag != .root); - bun.assert(package_id != 0); + bun.assertWithLocation(resolution_tag != .root, @src()); + bun.assertWithLocation(package_id != 0, @src()); } var count: usize = 0; const scripts = brk: { @@ -11575,7 +12243,7 @@ pub const PackageManager = struct { }; if (comptime Environment.allow_assert) { - bun.assert(scripts.filled); + bun.assertWithLocation(scripts.filled, @src()); } switch (resolution_tag) { @@ -11784,7 +12452,8 @@ pub const PackageManager = struct { Output.flush(); this.summary.fail += 1; - if (!installer.patch.isNull()) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + + if (!installer.patch.isNull()) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); return; }; @@ -11815,7 +12484,7 @@ pub const PackageManager = struct { if (comptime Environment.allow_assert) { @panic("Internal assertion failure: unexpected resolution tag"); } - if (!installer.patch.isNull()) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (!installer.patch.isNull()) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); return; }, } @@ -11830,7 +12499,7 @@ pub const PackageManager = struct { if (needs_install) { if (!remove_patch and resolution.tag.canEnqueueInstallTask() and installer.packageMissingFromCache(this.manager, package_id)) { if (comptime Environment.allow_assert) { - bun.assert(resolution.canEnqueueInstallTask()); + bun.assertWithLocation(resolution.canEnqueueInstallTask(), @src()); } const context: TaskCallbackContext = .{ @@ -11901,7 +12570,7 @@ pub const PackageManager = struct { if (comptime Environment.allow_assert) { @panic("unreachable, handled above"); } - if (!installer.patch.isNull()) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (!installer.patch.isNull()) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); this.summary.fail += 1; }, } @@ -11930,7 +12599,7 @@ pub const PackageManager = struct { } if (!is_pending_package_install and !this.canInstallPackageForTree(this.lockfile.buffers.trees.items, this.current_tree_id)) { - this.pending_installs_to_tree_id[this.current_tree_id].append(this.manager.allocator, .{ + this.trees[this.current_tree_id].pending_installs.append(this.manager.allocator, .{ .dependency_id = dependency_id, .tree_id = this.current_tree_id, .path = this.node_modules.path.clone() catch bun.outOfMemory(), @@ -11947,7 +12616,7 @@ pub const PackageManager = struct { }); } this.summary.fail += 1; - if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); return; }; @@ -11988,47 +12657,8 @@ pub const PackageManager = struct { this.node.completeOne(); } - const bin = this.bins[package_id]; - if (bin.tag != .none) { - const bin_task_id = Task.Id.forBinLink(package_id); - const task_queue = this.manager.task_queue.getOrPut(this.manager.allocator, bin_task_id) catch unreachable; - if (!task_queue.found_existing) { - var bin_linker = Bin.Linker{ - .bin = bin, - .package_installed_node_modules = bun.toFD(destination_dir), - .global_bin_path = this.options.bin_path, - .global_bin_dir = this.options.global_bin_dir, - - // .destination_dir_subpath = destination_dir_subpath, - .root_node_modules_folder = bun.toFD(this.root_node_modules_folder), - .package_name = strings.StringOrTinyString.init(alias), - .string_buf = buf, - .extern_string_buf = this.lockfile.buffers.extern_strings.items, - }; - - bin_linker.link(this.manager.options.global); - if (bin_linker.err) |err| { - if (comptime log_level != .silent) { - const fmt = "\nerror: linking {s}: {s}\n"; - const args = .{ alias, @errorName(err) }; - - if (comptime log_level.showProgress()) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - this.progress.log(comptime Output.prettyFmt(fmt, enable_ansi_colors), args); - }, - } - } else { - Output.prettyErrorln(fmt, args); - } - } - - if (this.manager.options.enable.fail_early) { - installer.uninstall(destination_dir); - Global.crash(); - } - } - } + if (this.bins[package_id].tag != .none) { + this.trees[this.current_tree_id].binaries.add(dependency_id) catch bun.outOfMemory(); } const name_hash: TruncatedPackageNameHash = @truncate(this.lockfile.buffers.dependencies.items[dependency_id].name_hash); @@ -12077,7 +12707,7 @@ pub const PackageManager = struct { } } - if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, destination_dir, !is_pending_package_install, log_level); }, .fail => |cause| { if (comptime Environment.allow_assert) { @@ -12086,7 +12716,7 @@ pub const PackageManager = struct { // even if the package failed to install, we still need to increment the install // counter for this tree - if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, destination_dir, !is_pending_package_install, log_level); if (cause.err == error.DanglingSymlink) { Output.prettyErrorln( @@ -12145,7 +12775,9 @@ pub const PackageManager = struct { }, } } else { - defer if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (this.bins[package_id].tag != .none) { + this.trees[this.current_tree_id].binaries.add(dependency_id) catch bun.outOfMemory(); + } var destination_dir = this.node_modules.makeAndOpenDir(this.root_node_modules_folder) catch |err| { if (log_level != .silent) { @@ -12155,6 +12787,7 @@ pub const PackageManager = struct { }); } this.summary.fail += 1; + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); return; }; @@ -12162,6 +12795,8 @@ pub const PackageManager = struct { if (std.fs.cwd().fd != destination_dir.fd) destination_dir.close(); } + defer if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, destination_dir, !is_pending_package_install, log_level); + const name_hash: TruncatedPackageNameHash = @truncate(this.lockfile.buffers.dependencies.items[dependency_id].name_hash); const is_trusted, const is_trusted_through_update_request, const add_to_lockfile = brk: { // trusted through a --trust dependency. need to enqueue scripts, write to package.json, and add to lockfile @@ -12286,7 +12921,18 @@ pub const PackageManager = struct { if (comptime log_level.showProgress()) { this.node.completeOne(); } - if (comptime increment_tree_count) this.incrementTreeInstallCount(this.current_tree_id, !is_pending_package_install, log_level); + if (comptime log_level.isVerbose()) { + const name = this.lockfile.str(&this.names[package_id]); + if (!meta.os.isMatch() and !meta.arch.isMatch()) { + Output.prettyErrorln("Skip installing '{s}' cpu & os mismatch", .{name}); + } else if (!meta.os.isMatch()) { + Output.prettyErrorln("Skip installing '{s}' os mismatch", .{name}); + } else if (!meta.arch.isMatch()) { + Output.prettyErrorln("Skip installing '{s}' cpu mismatch", .{name}); + } + } + + if (comptime increment_tree_count) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); return; } @@ -12566,14 +13212,8 @@ pub const PackageManager = struct { Bin.Linker.ensureUmask(); } var installer: PackageInstaller = brk: { - // These slices potentially get resized during iteration - // so we want to make sure they're not accessible to the rest of this function - // to make mistakes harder - var parts = this.lockfile.packages.slice(); - - const trees = this.lockfile.buffers.trees.items; - - const completed_trees, const tree_ids_to_trees_the_id_depends_on, const tree_install_counts = trees: { + const completed_trees, const tree_ids_to_trees_the_id_depends_on = trees: { + const trees = this.lockfile.buffers.trees.items; const completed_trees = try Bitset.initEmpty(this.allocator, trees.len); var tree_ids_to_trees_the_id_depends_on = try Bitset.List.initEmpty(this.allocator, trees.len, trees.len); @@ -12596,47 +13236,40 @@ pub const PackageManager = struct { } } - const tree_install_counts = try this.allocator.alloc(usize, trees.len); - @memset(tree_install_counts, 0); - if (comptime Environment.allow_assert) { if (trees.len > 0) { - // last tree should not depend on another except for itself - bun.assert(tree_ids_to_trees_the_id_depends_on.at(trees.len - 1).count() == 1 and tree_ids_to_trees_the_id_depends_on.at(trees.len - 1).isSet(trees.len - 1)); + // last tree should only depend on one other + bun.assertWithLocation(tree_ids_to_trees_the_id_depends_on.at(trees.len - 1).count() == 1, @src()); + // and it should be itself + bun.assertWithLocation(tree_ids_to_trees_the_id_depends_on.at(trees.len - 1).isSet(trees.len - 1), @src()); + // root tree should always depend on all trees - bun.assert(tree_ids_to_trees_the_id_depends_on.at(0).count() == trees.len); + bun.assertWithLocation(tree_ids_to_trees_the_id_depends_on.at(0).count() == trees.len, @src()); } // a tree should always depend on itself for (0..trees.len) |j| { - bun.assert(tree_ids_to_trees_the_id_depends_on.at(j).isSet(j)); + bun.assertWithLocation(tree_ids_to_trees_the_id_depends_on.at(j).isSet(j), @src()); } } break :trees .{ completed_trees, tree_ids_to_trees_the_id_depends_on, - tree_install_counts, }; }; - // Each tree (other than the root tree) can accumulate packages it cannot install until - // each of it's parent trees have installed their packages. We keep arrays of these pending - // packages for each tree, and drain them when a tree is completed (each of it's immediate - // dependencies are installed). - // - // Trees are drained breadth first because if the current tree is completed from - // the remaining pending installs, then any child tree has a higher chance of - // being able to install it's dependencies - const pending_installs_to_tree_id = this.allocator.alloc(std.ArrayListUnmanaged(DependencyInstallContext), trees.len) catch bun.outOfMemory(); - @memset(pending_installs_to_tree_id, .{}); + // These slices potentially get resized during iteration + // so we want to make sure they're not accessible to the rest of this function + // to make mistakes harder + var parts = this.lockfile.packages.slice(); const trusted_dependencies_from_update_requests: std.AutoArrayHashMapUnmanaged(TruncatedPackageNameHash, void) = trusted_deps: { // find all deps originating from --trust packages from cli var set: std.AutoArrayHashMapUnmanaged(TruncatedPackageNameHash, void) = .{}; if (this.options.do.trust_dependencies_from_args and this.lockfile.packages.len > 0) { - const root_deps = this.lockfile.packages.items(.dependencies)[0]; + const root_deps = parts.items(.dependencies)[this.root_package_id.get(this.lockfile, this.workspace_name_hash)]; var dep_id = root_deps.off; const end = dep_id +| root_deps.len; while (dep_id < end) : (dep_id += 1) { @@ -12648,7 +13281,7 @@ pub const PackageManager = struct { const entry = set.getOrPut(this.lockfile.allocator, @truncate(root_dep.name_hash)) catch bun.outOfMemory(); if (!entry.found_existing) { - const dependency_slice = this.lockfile.packages.items(.dependencies)[package_id]; + const dependency_slice = parts.items(.dependencies)[package_id]; addDependenciesToSet(&set, this.lockfile, dependency_slice); } break; @@ -12684,7 +13317,6 @@ pub const PackageManager = struct { .skip_verify_installed_version_number = skip_verify_installed_version_number, .skip_delete = skip_delete, .summary = &summary, - .global_bin_dir = this.options.global_bin_dir, .force_install = options.enable.force_install, .successfully_installed = try Bitset.initEmpty( this.allocator, @@ -12694,9 +13326,20 @@ pub const PackageManager = struct { .command_ctx = ctx, .tree_ids_to_trees_the_id_depends_on = tree_ids_to_trees_the_id_depends_on, .completed_trees = completed_trees, - .tree_install_counts = tree_install_counts, + .trees = trees: { + const trees = this.allocator.alloc(TreeContext, this.lockfile.buffers.trees.items.len) catch bun.outOfMemory(); + for (0..this.lockfile.buffers.trees.items.len) |i| { + trees[i] = .{ + .binaries = Bin.PriorityQueue.init(this.allocator, .{ + .dependencies = &this.lockfile.buffers.dependencies, + .string_buf = &this.lockfile.buffers.string_bytes, + }), + }; + } + break :trees trees; + }, .trusted_dependencies_from_update_requests = trusted_dependencies_from_update_requests, - .pending_installs_to_tree_id = pending_installs_to_tree_id, + .seen_bin_links = bun.StringHashMap(void).init(this.allocator), }; }; @@ -12823,9 +13466,9 @@ pub const PackageManager = struct { this.tickLifecycleScripts(); } - for (installer.pending_installs_to_tree_id) |pending_installs| { + for (installer.trees) |tree| { if (comptime Environment.allow_assert) { - bun.assert(pending_installs.items.len == 0); + bun.assert(tree.pending_installs.items.len == 0); } const force = true; installer.installAvailablePackages(log_level, force); @@ -12840,6 +13483,9 @@ pub const PackageManager = struct { summary.successfully_installed = installer.successfully_installed; + // need to make sure bins are linked before completing any remaining scripts. + // this can happen if a package fails to download + installer.linkRemainingBins(log_level); installer.completeRemainingScripts(log_level); while (this.pending_lifecycle_script_tasks.load(.monotonic) > 0) { @@ -12874,10 +13520,8 @@ pub const PackageManager = struct { pub fn setupGlobalDir(manager: *PackageManager, ctx: Command.Context) !void { manager.options.global_bin_dir = try Options.openGlobalBinDir(ctx.install); var out_buffer: bun.PathBuffer = undefined; - const result = try bun.getFdPath(manager.options.global_bin_dir.fd, &out_buffer); - out_buffer[result.len] = 0; - const result_: [:0]u8 = out_buffer[0..result.len :0]; - manager.options.bin_path = bun.cstring(try FileSystem.instance.dirname_store.append([:0]u8, result_)); + const result = try bun.getFdPathZ(manager.options.global_bin_dir.fd, &out_buffer); + manager.options.bin_path = bun.cstring(try FileSystem.instance.dirname_store.append([:0]u8, result)); } pub fn startProgressBarIfNone(manager: *PackageManager) void { @@ -13723,7 +14367,7 @@ pub const PackageManager = struct { Output.pretty("{d} package{s} installed ", .{ pkgs_installed, if (pkgs_installed == 1) "" else "s" }); Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp()); printed_timestamp = true; - printBlockedPackagesInfo(install_summary); + printBlockedPackagesInfo(install_summary, manager.options.global); if (manager.summary.remove > 0) { Output.pretty("Removed: {d}\n", .{manager.summary.remove}); @@ -13738,7 +14382,7 @@ pub const PackageManager = struct { Output.pretty("{d} package{s} removed ", .{ manager.summary.remove, if (manager.summary.remove == 1) "" else "s" }); Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp()); printed_timestamp = true; - printBlockedPackagesInfo(install_summary); + printBlockedPackagesInfo(install_summary, manager.options.global); } else if (install_summary.skipped > 0 and install_summary.fail == 0 and manager.update_requests.len == 0) { const count = @as(PackageID, @truncate(manager.lockfile.packages.len)); if (count != install_summary.skipped) { @@ -13750,7 +14394,7 @@ pub const PackageManager = struct { }); Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp()); printed_timestamp = true; - printBlockedPackagesInfo(install_summary); + printBlockedPackagesInfo(install_summary, manager.options.global); } else { Output.pretty("Done! Checked {d} package{s} (no changes) ", .{ install_summary.skipped, @@ -13758,7 +14402,7 @@ pub const PackageManager = struct { }); Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp()); printed_timestamp = true; - printBlockedPackagesInfo(install_summary); + printBlockedPackagesInfo(install_summary, manager.options.global); } } @@ -13786,7 +14430,7 @@ pub const PackageManager = struct { Output.flush(); } - fn printBlockedPackagesInfo(summary: PackageInstall.Summary) void { + fn printBlockedPackagesInfo(summary: PackageInstall.Summary, global: bool) void { const packages_count = summary.packages_with_blocked_scripts.count(); var scripts_count: usize = 0; for (summary.packages_with_blocked_scripts.values()) |count| scripts_count += count; @@ -13799,9 +14443,10 @@ pub const PackageManager = struct { } if (packages_count > 0) { - Output.prettyln("\n\nBlocked {d} postinstall{s}. Run `bun pm untrusted` for details.\n", .{ + Output.prettyln("\n\nBlocked {d} postinstall{s}. Run `bun pm {s}untrusted` for details.\n", .{ scripts_count, if (scripts_count > 1) "s" else "", + if (global) "-g " else "", }); } else { Output.pretty("\n", .{}); @@ -13855,14 +14500,6 @@ pub const PackageManager = struct { if (any_failed) this.crash(); } - pub fn crash(this: *const PackageManager) noreturn { - if (this.options.log_level != .silent) { - this.log.printForLogLevel(Output.errorWriter()) catch {}; - } - - Global.crash(); - } - pub fn spawnPackageLifecycleScripts( this: *PackageManager, ctx: Command.Context, @@ -13946,7 +14583,7 @@ pub const bun_install_js_bindings = struct { return obj; } - pub fn jsParseLockfile(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + pub fn jsParseLockfile(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) JSValue { const allocator = bun.default_allocator; var log = logger.Log.init(allocator); defer log.deinit(); diff --git a/src/install/lifecycle_script_runner.zig b/src/install/lifecycle_script_runner.zig index f981c64152246..f9df8bfd639f2 100644 --- a/src/install/lifecycle_script_runner.zig +++ b/src/install/lifecycle_script_runner.zig @@ -106,7 +106,7 @@ pub const LifecycleScriptSubprocess = struct { const manager = this.manager; const original_script = this.scripts.items[next_script_index].?; - const cwd = bun.path.z(this.scripts.cwd, &cwd_z_buf); + const cwd = this.scripts.cwd; const env = manager.env; this.stdout.setParent(this); this.stderr.setParent(this); @@ -353,15 +353,15 @@ pub const LifecycleScriptSubprocess = struct { }, .signaled => |signal| { this.printOutput(); + const signal_code = bun.SignalCode.from(signal); + Output.prettyErrorln("error: {s} script from \"{s}\" terminated by {}", .{ this.scriptName(), this.package_name, - - bun.SignalCode.from(signal).fmt(Output.enable_ansi_colors_stderr), + signal_code.fmt(Output.enable_ansi_colors_stderr), }); - Global.raiseIgnoringPanicHandler(@intFromEnum(signal)); - return; + Global.raiseIgnoringPanicHandler(signal); }, .err => |err| { Output.prettyErrorln("error: Failed to run {s} script from \"{s}\" due to\n{}", .{ @@ -372,7 +372,6 @@ pub const LifecycleScriptSubprocess = struct { this.deinit(); Output.flush(); Global.exit(1); - return; }, else => { Output.panic("error: Failed to run {s} script from \"{s}\" due to unexpected status\n{any}", .{ diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 1d28bff4ea753..a62279adab244 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -357,71 +357,59 @@ pub const Tree = struct { pub const max_depth = (bun.MAX_PATH_BYTES / "node_modules".len) + 1; pub const Iterator = struct { - trees: []const Tree, - dependency_ids: []const DependencyID, - dependencies: []const Dependency, - resolutions: []const PackageID, tree_id: Id, path_buf: bun.PathBuffer = undefined, - path_buf_len: usize = 0, last_parent: Id = invalid_id, - string_buf: string, - depth_stack: [max_depth]Id = undefined, + lockfile: *const Lockfile, + + depth_stack: DepthBuf = undefined, + + pub const DepthBuf = [max_depth]Id; pub fn init(lockfile: *const Lockfile) Iterator { var iter = Iterator{ - .trees = lockfile.buffers.trees.items, .tree_id = 0, - .dependency_ids = lockfile.buffers.hoisted_dependencies.items, - .dependencies = lockfile.buffers.dependencies.items, - .resolutions = lockfile.buffers.resolutions.items, - .string_buf = lockfile.buffers.string_bytes.items, + .lockfile = lockfile, }; @memcpy(iter.path_buf[0.."node_modules".len], "node_modules"); return iter; } - pub fn reload(this: *Iterator, lockfile: *const Lockfile) void { - this.trees = lockfile.buffers.trees.items; - this.dependency_ids = lockfile.buffers.hoisted_dependencies.items; - this.dependencies = lockfile.buffers.dependencies.items; - this.resolutions = lockfile.buffers.resolutions.items; - this.string_buf = lockfile.buffers.string_bytes.items; - } - pub fn reset(this: *Iterator) void { this.tree_id = 0; } pub fn nextNodeModulesFolder(this: *Iterator, completed_trees: ?*Bitset) ?NodeModulesFolder { - if (this.tree_id >= this.trees.len) return null; + const trees = this.lockfile.buffers.trees.items; + + if (this.tree_id >= trees.len) return null; - while (this.trees[this.tree_id].dependencies.len == 0) { + while (trees[this.tree_id].dependencies.len == 0) { if (completed_trees) |_completed_trees| { _completed_trees.set(this.tree_id); } this.tree_id += 1; - if (this.tree_id >= this.trees.len) return null; + if (this.tree_id >= trees.len) return null; } - const tree = this.trees[this.tree_id]; + const current_tree_id = this.tree_id; + const tree = trees[current_tree_id]; + const tree_dependencies = tree.dependencies.get(this.lockfile.buffers.hoisted_dependencies.items); - const relative_path, const depth = tree.relativePathAndDepth( - this.trees, - this.dependencies, - this.string_buf, + const relative_path, const depth = relativePathAndDepth( + this.lockfile, + current_tree_id, &this.path_buf, &this.depth_stack, ); this.tree_id += 1; - this.path_buf_len = relative_path.len; return .{ .relative_path = relative_path, - .dependencies = tree.dependencies.get(this.dependency_ids), - .tree_id = tree.id, + .dependencies = tree_dependencies, + .tree_id = current_tree_id, .depth = depth, }; } @@ -429,35 +417,41 @@ pub const Tree = struct { /// Returns relative path and the depth of the tree pub fn relativePathAndDepth( - tree: *const Tree, - trees: []const Tree, - dependencies: []const Dependency, - string_buf: string, + lockfile: *const Lockfile, + tree_id: Id, path_buf: *bun.PathBuffer, - depth_buf: *[max_depth]Id, + depth_buf: *Iterator.DepthBuf, ) struct { stringZ, usize } { + const trees = lockfile.buffers.trees.items; var depth: usize = 0; + const tree = trees[tree_id]; + var parent_id = tree.id; var path_written: usize = "node_modules".len; depth_buf[0] = 0; if (tree.id > 0) { + const dependencies = lockfile.buffers.dependencies.items; + const buf = lockfile.buffers.string_bytes.items; var depth_buf_len: usize = 1; + while (parent_id > 0 and parent_id < trees.len) { depth_buf[depth_buf_len] = parent_id; parent_id = trees[parent_id].parent; depth_buf_len += 1; } + depth_buf_len -= 1; + depth = depth_buf_len; while (depth_buf_len > 0) : (depth_buf_len -= 1) { path_buf[path_written] = std.fs.path.sep; path_written += 1; const id = depth_buf[depth_buf_len]; - const name = dependencies[trees[id].dependency_id].name.slice(string_buf); + const name = dependencies[trees[id].dependency_id].name.slice(buf); @memcpy(path_buf[path_written..][0..name.len], name); path_written += name.len; @@ -1302,6 +1296,7 @@ pub const Printer = struct { const loader = try allocator.create(DotEnv.Loader); loader.* = DotEnv.Loader.init(map, allocator); + loader.quiet = true; break :brk loader; }; @@ -1723,24 +1718,36 @@ pub const Printer = struct { .extern_string_buf = this.lockfile.buffers.extern_strings.items, }; - const fmt = comptime Output.prettyFmt("installed {s}@{} with binaries:\n", enable_ansi_colors); - - try writer.print( - fmt, - .{ - package_name, - resolved[package_id].fmt(string_buf, .posix), - }, - ); + { + const fmt = comptime Output.prettyFmt("installed {s}@{} with binaries:\n", enable_ansi_colors); - while (iterator.next() catch null) |bin_name| { try writer.print( - comptime Output.prettyFmt(" - {s}\n", enable_ansi_colors), + fmt, .{ - bin_name, + package_name, + resolved[package_id].fmt(string_buf, .posix), }, ); } + + { + const fmt = comptime Output.prettyFmt(" - {s}\n", enable_ansi_colors); + var manager = &bun.PackageManager.instance; + + if (manager.track_installed_bin == .pending) { + if (iterator.next() catch null) |bin_name| { + manager.track_installed_bin = .{ + .basename = bun.default_allocator.dupe(u8, bin_name) catch bun.outOfMemory(), + }; + + try writer.print(fmt, .{bin_name}); + } + } + + while (iterator.next() catch null) |bin_name| { + try writer.print(fmt, .{bin_name}); + } + } }, } } @@ -2761,7 +2768,7 @@ pub const Package = extern struct { items: [Lockfile.Scripts.names.len]?Lockfile.Scripts.Entry, first_index: u8, total: u8, - cwd: string, + cwd: stringZ, package_name: string, pub fn printScripts( @@ -2949,7 +2956,7 @@ pub const Package = extern struct { this: *const Package.Scripts, lockfile: *Lockfile, lockfile_buf: []const u8, - cwd: string, + cwd_: string, package_name: string, resolution_tag: Resolution.Tag, add_node_gyp_rebuild_script: bool, @@ -2957,11 +2964,26 @@ pub const Package = extern struct { const allocator = lockfile.allocator; const first_index, const total, const scripts = getScriptEntries(this, lockfile, lockfile_buf, resolution_tag, add_node_gyp_rebuild_script); if (first_index != -1) { + var cwd_buf: if (Environment.isWindows) bun.PathBuffer else void = undefined; + + const cwd = if (comptime !Environment.isWindows) + cwd_ + else brk: { + @memcpy(cwd_buf[0..cwd_.len], cwd_); + cwd_buf[cwd_.len] = 0; + const cwd_handle = bun.openDirNoRenamingOrDeletingWindows(bun.invalid_fd, cwd_buf[0..cwd_.len :0]) catch break :brk cwd_; + + var buf: bun.WPathBuffer = undefined; + const new_cwd = bun.windows.GetFinalPathNameByHandle(cwd_handle.fd, .{}, &buf) catch break :brk cwd_; + + break :brk strings.convertUTF16toUTF8InBuffer(&cwd_buf, new_cwd) catch break :brk cwd_; + }; + return .{ .items = scripts, .first_index = @intCast(first_index), .total = total, - .cwd = allocator.dupe(u8, cwd) catch bun.outOfMemory(), + .cwd = allocator.dupeZ(u8, cwd) catch bun.outOfMemory(), .package_name = package_name, }; } @@ -3827,7 +3849,7 @@ pub const Package = extern struct { var workspace = Package{}; - const json = PackageManager.instance.workspace_package_json_cache.getWithSource(allocator, log, source, .{}).unwrap() catch break :brk false; + const json = PackageManager.instance.workspace_package_json_cache.getWithSource(bun.default_allocator, log, source, .{}).unwrap() catch break :brk false; try workspace.parseWithJSON( to_lockfile, @@ -4002,10 +4024,18 @@ pub const Package = extern struct { if (trimmed.len != 1 or (trimmed[0] != '*' and trimmed[0] != '^' and trimmed[0] != '~')) { const at = strings.lastIndexOfChar(input, '@') orelse 0; if (at > 0) { - workspace_range = Semver.Query.parse(allocator, input[at + 1 ..], sliced) catch return error.InstallFailed; + workspace_range = Semver.Query.parse(allocator, input[at + 1 ..], sliced) catch |err| { + switch (err) { + error.OutOfMemory => bun.outOfMemory(), + } + }; break :brk String.Builder.stringHash(input[0..at]); } - workspace_range = Semver.Query.parse(allocator, input, sliced) catch null; + workspace_range = Semver.Query.parse(allocator, input, sliced) catch |err| { + switch (err) { + error.OutOfMemory => bun.outOfMemory(), + } + }; } break :brk external_alias.hash; } else external_alias.hash, @@ -4300,6 +4330,7 @@ pub const Package = extern struct { ) !WorkspaceEntry { const workspace_json = try json_cache.getWithPath(allocator, log, abs_package_json_path, .{ .init_reset_store = false, + .guess_indentation = true, }).unwrap(); const name_expr = workspace_json.root.get("name") orelse return error.MissingPackageName; @@ -4351,6 +4382,8 @@ pub const Package = extern struct { return error.InvalidPackageJSON; }; + if (input_path.len == 0 or input_path.len == 1 and input_path[0] == '.') continue; + if (bun.glob.detectGlobSyntax(input_path)) { workspace_globs.append(input_path) catch bun.outOfMemory(); continue; @@ -4363,12 +4396,16 @@ pub const Package = extern struct { .auto, ); + // skip root package.json + if (strings.eqlLong(bun.path.dirname(abs_package_json_path, .auto), source.path.name.dir, true)) continue; + const workspace_entry = processWorkspaceName( allocator, json_cache, abs_package_json_path, log, ) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); switch (err) { error.EISNOTDIR, error.EISDIR, error.EACCESS, error.EPERM, error.ENOENT, error.FileNotFound => { log.addErrorFmt( @@ -4484,6 +4521,10 @@ pub const Package = extern struct { }, }) |matched_path| { const entry_dir: []const u8 = Path.dirname(matched_path, .auto); + + // skip root package.json + if (strings.eqlComptime(matched_path, "package.json")) continue; + debug("matched path: {s}, dirname: {s}\n", .{ matched_path, entry_dir }); const abs_package_json_path = Path.joinAbsStringBufZ( @@ -4500,6 +4541,8 @@ pub const Package = extern struct { abs_package_json_path, log, ) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + const entry_base: []const u8 = Path.basename(matched_path); switch (err) { error.FileNotFound, error.PermissionDenied => continue, @@ -5568,20 +5611,54 @@ const Buffers = struct { var reader = stream.reader(); const start_pos = try reader.readInt(u64, .little); + + // If its 0xDEADBEEF, then that means the value was never written in the lockfile. + if (start_pos == 0xDEADBEEF) { + return error.CorruptLockfile; + } + + // These are absolute numbers, it shouldn't be zero. + // There's a prefix before any of the arrays, so it can never be zero here. + if (start_pos == 0) { + return error.CorruptLockfile; + } + + // We shouldn't be going backwards. + if (start_pos < (stream.pos -| @sizeOf(u64))) { + return error.CorruptLockfile; + } + const end_pos = try reader.readInt(u64, .little); - stream.pos = end_pos; + // If its 0xDEADBEEF, then that means the value was never written in the lockfile. + // That shouldn't happen. + if (end_pos == 0xDEADBEEF) { + return error.CorruptLockfile; + } + + // These are absolute numbers, it shouldn't be zero. + if (end_pos == 0) { + return error.CorruptLockfile; + } + + // Prevent integer overflow. + if (start_pos > end_pos) { + return error.CorruptLockfile; + } + + // Prevent buffer overflow. + if (end_pos > stream.buffer.len) { + return error.CorruptLockfile; + } + const byte_len = end_pos - start_pos; + stream.pos = end_pos; if (byte_len == 0) return ArrayList{ .items = &[_]PointerType{}, .capacity = 0, }; - if (stream.pos > stream.buffer.len) { - return error.BufferOverflow; - } - const misaligned = std.mem.bytesAsSlice(PointerType, stream.buffer[start_pos..end_pos]); return ArrayList{ @@ -6628,12 +6705,10 @@ pub fn jsonStringify(this: *const Lockfile, w: anytype) !void { try w.beginArray(); defer w.endArray() catch {}; - const trees = this.buffers.trees.items; - const string_buf = this.buffers.string_bytes.items; const dependencies = this.buffers.dependencies.items; const hoisted_deps = this.buffers.hoisted_dependencies.items; const resolutions = this.buffers.resolutions.items; - var depth_buf: [Tree.max_depth]Tree.Id = undefined; + var depth_buf: Tree.Iterator.DepthBuf = undefined; var path_buf: bun.PathBuffer = undefined; @memcpy(path_buf[0.."node_modules".len], "node_modules"); @@ -6646,10 +6721,9 @@ pub fn jsonStringify(this: *const Lockfile, w: anytype) !void { try w.objectField("id"); try w.write(tree_id); - const relative_path, const depth = tree.relativePathAndDepth( - trees, - dependencies, - string_buf, + const relative_path, const depth = Lockfile.Tree.relativePathAndDepth( + this, + @intCast(tree_id), &path_buf, &depth_buf, ); diff --git a/src/install/migration.zig b/src/install/migration.zig index 0f4cb9e5fd5c6..aa8043126e1e5 100644 --- a/src/install/migration.zig +++ b/src/install/migration.zig @@ -97,9 +97,9 @@ pub fn detectAndLoadOtherLockfile( return LoadFromDiskResult{ .not_found = {} }; } -const ResolvedURLsMap = std.StringHashMapUnmanaged(string); +const ResolvedURLsMap = bun.StringHashMapUnmanaged(string); -const IdMap = std.StringHashMapUnmanaged(IdMapValue); +const IdMap = bun.StringHashMapUnmanaged(IdMapValue); const IdMapValue = struct { /// index into the old package-lock.json package entries. old_json_index: u32, @@ -515,23 +515,31 @@ pub fn migrateNPMLockfile( .origin = if (package_id == 0) .local else .npm, .arch = if (pkg.get("cpu")) |cpu_array| arch: { + var arch = Npm.Architecture.none.negatable(); if (cpu_array.data != .e_array) return error.InvalidNPMLockfile; - var arch: Npm.Architecture = .none; + if (cpu_array.data.e_array.items.len == 0) { + break :arch arch.combine(); + } + for (cpu_array.data.e_array.items.slice()) |item| { if (item.data != .e_string) return error.InvalidNPMLockfile; - arch = arch.apply(item.data.e_string.data); + arch.apply(item.data.e_string.data); } - break :arch arch; + break :arch arch.combine(); } else .all, .os = if (pkg.get("os")) |cpu_array| arch: { + var os = Npm.OperatingSystem.none.negatable(); if (cpu_array.data != .e_array) return error.InvalidNPMLockfile; - var os: Npm.OperatingSystem = .none; + if (cpu_array.data.e_array.items.len == 0) { + break :arch .all; + } + for (cpu_array.data.e_array.items.slice()) |item| { if (item.data != .e_string) return error.InvalidNPMLockfile; - os = os.apply(item.data.e_string.data); + os.apply(item.data.e_string.data); } - break :arch os; + break :arch os.combine(); } else .all, .man_dir = String{}, @@ -702,7 +710,7 @@ pub fn migrateNPMLockfile( } if (expr.data != .e_array) return error.InvalidNPMLockfile; const arr: *E.Array = expr.data.e_array; - var map = std.StringArrayHashMapUnmanaged(void){}; + var map = bun.StringArrayHashMapUnmanaged(void){}; try map.ensureTotalCapacity(allocator, arr.items.len); for (arr.items.slice()) |item| { map.putAssumeCapacity(item.asString(allocator) orelse return error.InvalidNPMLockfile, {}); diff --git a/src/install/npm.zig b/src/install/npm.zig index 62b9e517ad2ae..77b7f55aa246c 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -274,6 +274,7 @@ pub const Registry = struct { if (try PackageManifest.parse( allocator, + scope, log, body, package_name, @@ -319,6 +320,72 @@ const ExternVersionMap = extern struct { } }; +fn Negatable(comptime T: type) type { + return struct { + added: T = T.none, + removed: T = T.none, + had_wildcard: bool = false, + had_unrecognized_values: bool = false, + + // https://github.com/pnpm/pnpm/blob/1f228b0aeec2ef9a2c8577df1d17186ac83790f9/config/package-is-installable/src/checkPlatform.ts#L56-L86 + // https://github.com/npm/cli/blob/fefd509992a05c2dfddbe7bc46931c42f1da69d7/node_modules/npm-install-checks/lib/index.js#L2-L96 + pub fn combine(this: Negatable(T)) T { + const added = if (this.had_wildcard) T.all_value else @intFromEnum(this.added); + const removed = @intFromEnum(this.removed); + + // If none were added or removed, all are allowed + if (added == 0 and removed == 0) { + if (this.had_unrecognized_values) { + return T.none; + } + + // [] + return T.all; + } + + // If none were added, but some were removed, return the inverse of the removed + if (added == 0 and removed != 0) { + // ["!linux", "!darwin"] + return @enumFromInt(T.all_value & ~removed); + } + + if (removed == 0) { + // ["linux", "darwin"] + return @enumFromInt(added); + } + + // - ["linux", "!darwin"] + return @enumFromInt(added & ~removed); + } + + pub fn apply(this: *Negatable(T), str: []const u8) void { + if (str.len == 0) { + return; + } + + if (strings.eqlComptime(str, "any")) { + this.had_wildcard = true; + return; + } + + const is_not = str[0] == '!'; + const offset: usize = @intFromBool(is_not); + + const field: u16 = T.NameMap.get(str[offset..]) orelse { + if (!is_not) + this.had_unrecognized_values = true; + return; + }; + + if (is_not) { + this.* = .{ .added = this.added, .removed = @enumFromInt(@intFromEnum(this.removed) | field) }; + } else { + this.* = .{ .added = @enumFromInt(@intFromEnum(this.added) | field), .removed = this.removed }; + } + } + }; +} + /// https://nodejs.org/api/os.html#osplatform pub const OperatingSystem = enum(u16) { none = 0, @@ -337,16 +404,15 @@ pub const OperatingSystem = enum(u16) { pub const all_value: u16 = aix | darwin | freebsd | linux | openbsd | sunos | win32 | android; + pub const current: OperatingSystem = switch (Environment.os) { + .linux => @enumFromInt(linux), + .mac => @enumFromInt(darwin), + .windows => @enumFromInt(win32), + else => @compileError("Unsupported operating system: " ++ @tagName(Environment.os)), + }; + pub fn isMatch(this: OperatingSystem) bool { - if (comptime Environment.isLinux) { - return (@intFromEnum(this) & linux) != 0; - } else if (comptime Environment.isMac) { - return (@intFromEnum(this) & darwin) != 0; - } else if (comptime Environment.isWindows) { - return (@intFromEnum(this) & win32) != 0; - } else { - return false; - } + return (@intFromEnum(this) & @intFromEnum(current)) != 0; } pub inline fn has(this: OperatingSystem, other: u16) bool { @@ -364,32 +430,43 @@ pub const OperatingSystem = enum(u16) { .{ "android", android }, }); - pub fn apply(this_: OperatingSystem, str: []const u8) OperatingSystem { - if (str.len == 0) { - return this_; - } - const this = @intFromEnum(this_); - - const is_not = str[0] == '!'; - const offset: usize = if (str[0] == '!') 1 else 0; + pub const current_name = switch (Environment.os) { + .linux => "linux", + .mac => "darwin", + .windows => "win32", + else => @compileError("Unsupported operating system: " ++ @tagName(current)), + }; - const field: u16 = NameMap.get(str[offset..]) orelse return this_; + pub fn negatable(this: OperatingSystem) Negatable(OperatingSystem) { + return .{ .added = this, .removed = .none }; + } - if (is_not) { - return @as(OperatingSystem, @enumFromInt(this & ~field)); - } else { - return @as(OperatingSystem, @enumFromInt(this | field)); + const JSC = bun.JSC; + pub fn jsFunctionOperatingSystemIsMatch(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const args = callframe.arguments(1); + var operating_system = negatable(.none); + var iter = args.ptr[0].arrayIterator(globalObject); + while (iter.next()) |item| { + const slice = item.toSlice(globalObject, bun.default_allocator); + defer slice.deinit(); + operating_system.apply(slice.slice()); + if (globalObject.hasException()) return .zero; } + if (globalObject.hasException()) return .zero; + return JSC.JSValue.jsBoolean(operating_system.combine().isMatch()); } }; pub const Libc = enum(u8) { none = 0, + all = all_value, _, pub const glibc: u8 = 1 << 1; pub const musl: u8 = 1 << 2; + pub const all_value: u8 = glibc | musl; + pub const NameMap = bun.ComptimeStringMap(u8, .{ .{ "glibc", glibc }, .{ "musl", musl }, @@ -399,22 +476,26 @@ pub const Libc = enum(u8) { return (@intFromEnum(this) & other) != 0; } - pub fn apply(this_: Libc, str: []const u8) Libc { - if (str.len == 0) { - return this_; - } - const this = @intFromEnum(this_); - - const is_not = str[0] == '!'; - const offset: usize = if (str[0] == '!') 1 else 0; - - const field: u8 = NameMap.get(str[offset..]) orelse return this_; + pub fn negatable(this: Libc) Negatable(Libc) { + return .{ .added = this, .removed = .none }; + } - if (is_not) { - return @as(Libc, @enumFromInt(this & ~field)); - } else { - return @as(Libc, @enumFromInt(this | field)); + // TODO: + pub const current: Libc = @intFromEnum(glibc); + + const JSC = bun.JSC; + pub fn jsFunctionLibcIsMatch(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const args = callframe.arguments(1); + var libc = negatable(.none); + var iter = args.ptr[0].arrayIterator(globalObject); + while (iter.next()) |item| { + const slice = item.toSlice(globalObject, bun.default_allocator); + defer slice.deinit(); + libc.apply(slice.slice()); + if (globalObject.hasException()) return .zero; } + if (globalObject.hasException()) return .zero; + return JSC.JSValue.jsBoolean(libc.combine().isMatch()); } }; @@ -439,6 +520,18 @@ pub const Architecture = enum(u16) { pub const all_value: u16 = arm | arm64 | ia32 | mips | mipsel | ppc | ppc64 | s390 | s390x | x32 | x64; + pub const current: Architecture = switch (Environment.arch) { + .arm64 => @enumFromInt(arm64), + .x64 => @enumFromInt(x64), + else => @compileError("Specify architecture: " ++ Environment.arch), + }; + + pub const current_name = switch (Environment.arch) { + .arm64 => "arm64", + .x64 => "x64", + else => @compileError("Unsupported architecture: " ++ @tagName(current)), + }; + pub const NameMap = bun.ComptimeStringMap(u16, .{ .{ "arm", arm }, .{ "arm64", arm64 }, @@ -458,34 +551,29 @@ pub const Architecture = enum(u16) { } pub fn isMatch(this: Architecture) bool { - if (comptime Environment.isAarch64) { - return (@intFromEnum(this) & arm64) != 0; - } else if (comptime Environment.isX64) { - return (@intFromEnum(this) & x64) != 0; - } else { - return false; - } + return @intFromEnum(this) & @intFromEnum(current) != 0; } - pub fn apply(this_: Architecture, str: []const u8) Architecture { - if (str.len == 0) { - return this_; - } - const this = @intFromEnum(this_); - - const is_not = str[0] == '!'; - const offset: usize = if (str[0] == '!') 1 else 0; - const input = str[offset..]; - - const field: u16 = NameMap.get(input) orelse return this_; + pub fn negatable(this: Architecture) Negatable(Architecture) { + return .{ .added = this, .removed = .none }; + } - if (is_not) { - return @as(Architecture, @enumFromInt(this & ~field)); - } else { - return @as(Architecture, @enumFromInt(this | field)); + const JSC = bun.JSC; + pub fn jsFunctionArchitectureIsMatch(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { + const args = callframe.arguments(1); + var architecture = negatable(.none); + var iter = args.ptr[0].arrayIterator(globalObject); + while (iter.next()) |item| { + const slice = item.toSlice(globalObject, bun.default_allocator); + defer slice.deinit(); + architecture.apply(slice.slice()); + if (globalObject.hasException()) return .zero; } + if (globalObject.hasException()) return .zero; + return JSC.JSValue.jsBoolean(architecture.combine().isMatch()); } }; + const BigExternalString = Semver.BigExternalString; pub const PackageVersion = extern struct { @@ -591,7 +679,8 @@ pub const PackageManifest = struct { pub const Serializer = struct { // - version 3: added serialization of registry url. it's used to invalidate when it changes - pub const version = "bun-npm-manifest-cache-v0.0.3\n"; + // - version 4: fixed bug with cpu & os tag not being added correctly + pub const version = "bun-npm-manifest-cache-v0.0.4\n"; const header_bytes: string = "#!/usr/bin/env bun\n" ++ version; pub const sizes = blk: { @@ -607,8 +696,8 @@ pub const PackageManifest = struct { alignment: usize, }; var data: [fields.len]Data = undefined; - for (fields, 0..) |field_info, i| { - data[i] = .{ + for (fields, &data) |field_info, *dat| { + dat.* = .{ .size = @sizeOf(field_info.type), .name = field_info.name, .alignment = if (@sizeOf(field_info.type) == 0) 1 else field_info.alignment, @@ -622,9 +711,9 @@ pub const PackageManifest = struct { std.sort.pdq(Data, &data, {}, Sort.lessThan); var sizes_bytes: [fields.len]usize = undefined; var names: [fields.len][]const u8 = undefined; - for (data, 0..) |elem, i| { - sizes_bytes[i] = elem.size; - names[i] = elem.name; + for (data, &sizes_bytes, &names) |elem, *size_, *name_| { + size_.* = elem.size; + name_.* = elem.name; } break :blk .{ .bytes = sizes_bytes, @@ -658,7 +747,11 @@ pub const PackageManifest = struct { } stream.pos += Aligner.skipAmount(Type, stream.pos); - const result_bytes = stream.buffer[stream.pos..][0..byte_len]; + const remaining = stream.buffer[@min(stream.pos, stream.buffer.len)..]; + if (remaining.len < byte_len) { + return error.BufferTooSmall; + } + const result_bytes = remaining[0..byte_len]; const result = @as([*]const Type, @ptrCast(@alignCast(result_bytes.ptr)))[0 .. result_bytes.len / @sizeOf(Type)]; stream.pos += result_bytes.len; return result; @@ -904,10 +997,14 @@ pub const PackageManifest = struct { .{ .mode = .read_only }, ) catch return null; defer cache_file.close(); - const bytes = try cache_file.readToEndAllocOptions( + return loadByFile(allocator, scope, cache_file); + } + + pub fn loadByFile(allocator: std.mem.Allocator, scope: *const Registry.Scope, manifest_file: std.fs.File) !?PackageManifest { + const bytes = try manifest_file.readToEndAllocOptions( allocator, std.math.maxInt(u32), - cache_file.getEndPos() catch null, + manifest_file.getEndPos() catch null, @alignOf(u8), null, ); @@ -955,6 +1052,96 @@ pub const PackageManifest = struct { } }; + pub const bindings = struct { + const JSC = bun.JSC; + const JSValue = JSC.JSValue; + const JSGlobalObject = JSC.JSGlobalObject; + const CallFrame = JSC.CallFrame; + const ZigString = JSC.ZigString; + + pub fn generate(global: *JSGlobalObject) JSValue { + const obj = JSValue.createEmptyObject(global, 1); + const parseManifestString = ZigString.static("parseManifest"); + obj.put(global, parseManifestString, JSC.createCallback(global, parseManifestString, 2, jsParseManifest)); + return obj; + } + + pub fn jsParseManifest(global: *JSGlobalObject, callFrame: *CallFrame) JSValue { + const args = callFrame.arguments(2).slice(); + if (args.len < 2 or !args[0].isString() or !args[1].isString()) { + global.throw("expected manifest filename and registry string arguments", .{}); + return .zero; + } + + const manifest_filename_str = args[0].toBunString(global); + defer manifest_filename_str.deref(); + + const manifest_filename = manifest_filename_str.toUTF8(bun.default_allocator); + defer manifest_filename.deinit(); + + const registry_str = args[1].toBunString(global); + defer registry_str.deref(); + + const registry = registry_str.toUTF8(bun.default_allocator); + defer registry.deinit(); + + const manifest_file = std.fs.openFileAbsolute(manifest_filename.slice(), .{}) catch |err| { + global.throw("failed to open manifest file \"{s}\": {s}", .{ manifest_filename.slice(), @errorName(err) }); + return .zero; + }; + defer manifest_file.close(); + + const scope: Registry.Scope = .{ + .url_hash = Registry.Scope.hash(strings.withoutTrailingSlash(registry.slice())), + .url = .{ + .host = strings.withoutTrailingSlash(strings.withoutPrefixComptime(registry.slice(), "http://")), + .hostname = strings.withoutTrailingSlash(strings.withoutPrefixComptime(registry.slice(), "http://")), + .href = registry.slice(), + .origin = strings.withoutTrailingSlash(registry.slice()), + .protocol = if (strings.indexOfChar(registry.slice(), ':')) |colon| registry.slice()[0..colon] else "", + }, + }; + + const maybe_package_manifest = Serializer.loadByFile(bun.default_allocator, &scope, manifest_file) catch |err| { + global.throw("failed to load manifest file: {s}", .{@errorName(err)}); + return .zero; + }; + + const package_manifest: PackageManifest = maybe_package_manifest orelse { + global.throw("manifest is invalid ", .{}); + return .zero; + }; + + var buf: std.ArrayListUnmanaged(u8) = .{}; + const writer = buf.writer(bun.default_allocator); + + // TODO: we can add more information. for now just versions is fine + + writer.print("{{\"name\":\"{s}\",\"versions\":[", .{package_manifest.name()}) catch { + global.throwOutOfMemory(); + return .zero; + }; + + for (package_manifest.versions, 0..) |version, i| { + if (i == package_manifest.versions.len - 1) + writer.print("\"{}\"]}}", .{version.fmt(package_manifest.string_buf)}) catch { + global.throwOutOfMemory(); + return .zero; + } + else + writer.print("\"{}\",", .{version.fmt(package_manifest.string_buf)}) catch { + global.throwOutOfMemory(); + return .zero; + }; + } + + var result = bun.String.fromUTF8(buf.items); + defer result.deref(); + + return result.toJSByParseJSON(global); + } + }; + pub fn str(self: *const PackageManifest, external: *const ExternalString) string { return external.slice(self.string_buf); } @@ -1078,8 +1265,9 @@ pub const PackageManifest = struct { const ExternalStringMapDeduper = std.HashMap(u64, ExternalStringList, IdentityContext(u64), 80); /// This parses [Abbreviated metadata](https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-metadata-format) - pub fn parse( + fn parse( allocator: std.mem.Allocator, + scope: *const Registry.Scope, log: *logger.Log, json_buffer: []const u8, expected_name: []const u8, @@ -1120,69 +1308,20 @@ pub const PackageManifest = struct { .string_pool = string_pool, }; - if (json.asProperty("name")) |name_q| { - const received_name = name_q.expr.asString(allocator) orelse return null; - - // This is intentionally a case insensitive comparision. If the registry is running on a system - // with a case insensitive filesystem, you'll be able to install dependencies with casing that doesn't match. - // - // e.g. - // { - // "dependencies": { - // // will install successfully, even though the package name in the registry is `jquery` - // "jQuery": "3.7.1" - // } - // } - // - // https://github.com/oven-sh/bun/issues/5189 - const equal = if (expected_name.len == 0 or expected_name[0] != '@') - // Unscoped package, just normal case insensitive comparison - strings.eqlCaseInsensitiveASCII(expected_name, received_name, true) - else brk: { - // Scoped package. The registry might url encode the package name changing either or both `@` and `/` into `%40` and `%2F`. - // e.g. "name": "@std%2fsemver" // real world example from crash report - - // Expected name `@` exists, check received has either `@` or `%40` - var received_remain = received_name; - if (received_remain.len > 0 and received_remain[0] == '@') { - received_remain = received_remain[1..]; - } else if (received_remain.len > 2 and strings.eqlComptime(received_remain[0..3], "%40")) { - received_remain = received_remain[3..]; - } else { - break :brk false; - } - - var expected_remain = expected_name[1..]; - - // orelse is invalid because scoped package is missing `/`, but we allow just in case - const slash_index = strings.indexOfChar(expected_remain, '/') orelse break :brk strings.eqlCaseInsensitiveASCII(expected_remain, received_remain, true); - - if (slash_index >= received_remain.len) break :brk false; - - if (!strings.eqlCaseInsensitiveASCIIIgnoreLength(expected_remain[0..slash_index], received_remain[0..slash_index])) break :brk false; - expected_remain = expected_remain[slash_index + 1 ..]; - - // Expected name `/` exists, check that received is either `/`, `%2f`, or `%2F` - received_remain = received_remain[slash_index..]; - if (received_remain.len > 0 and received_remain[0] == '/') { - received_remain = received_remain[1..]; - } else if (received_remain.len > 2 and strings.eqlCaseInsensitiveASCIIIgnoreLength(received_remain[0..3], "%2f")) { - received_remain = received_remain[3..]; - } else { - break :brk false; + if (PackageManager.verbose_install) { + if (json.asProperty("name")) |name_q| { + const received_name = name_q.expr.asString(allocator) orelse return null; + // If this manifest is coming from the default registry, make sure it's the expected one. If it's not + // from the default registry we don't check because the registry might have a different name in the manifest. + // https://github.com/oven-sh/bun/issues/4925 + if (scope.url_hash == Registry.default_url_hash and !strings.eqlLong(expected_name, received_name, true)) { + Output.warn("Package name mismatch. Expected \"{s}\" but received \"{s}\"", .{ expected_name, received_name }); } - - break :brk strings.eqlCaseInsensitiveASCII(expected_remain, received_remain, true); - }; - - if (!equal) { - Output.panic("internal: Package name mismatch. Expected \"{s}\" but received \"{s}\"", .{ expected_name, received_name }); - return null; } - - string_builder.count(expected_name); } + string_builder.count(expected_name); + if (json.asProperty("modified")) |name_q| { const field = name_q.expr.asString(allocator) orelse return null; @@ -1212,7 +1351,7 @@ pub const PackageManifest = struct { const sliced_version = SlicedString.init(version_name, version_name); const parsed_version = Semver.Version.parse(sliced_version); - if (Environment.allow_assert) assert(parsed_version.valid); + if (Environment.allow_assert) bun.assertWithLocation(parsed_version.valid, @src()); if (!parsed_version.valid) { log.addErrorFmt(&source, prop.value.?.loc, allocator, "Failed to parse dependency {s}", .{version_name}) catch unreachable; continue; @@ -1374,9 +1513,8 @@ pub const PackageManifest = struct { string_buf = ptr[0..string_builder.cap]; } - // Using `expected_name` instead of the name from the manifest. We've already - // checked that they are equal above, but `expected_name` will not have `@` - // or `/` changed to `%40` or `%2f`, ensuring lookups will work later + // Using `expected_name` instead of the name from the manifest. Custom registries might + // have a different name than the dependency name in package.json. result.pkg.name = string_builder.append(ExternalString, expected_name); get_versions: { @@ -1401,85 +1539,85 @@ pub const PackageManifest = struct { var sliced_version = SlicedString.init(version_name, version_name); var parsed_version = Semver.Version.parse(sliced_version); - if (Environment.allow_assert) assert(parsed_version.valid); + if (Environment.allow_assert) bun.assertWithLocation(parsed_version.valid, @src()); // We only need to copy the version tags if it contains pre and/or build if (parsed_version.version.tag.hasBuild() or parsed_version.version.tag.hasPre()) { const version_string = string_builder.append(String, version_name); sliced_version = version_string.sliced(string_buf); parsed_version = Semver.Version.parse(sliced_version); if (Environment.allow_assert) { - assert(parsed_version.valid); - assert(parsed_version.version.tag.hasBuild() or parsed_version.version.tag.hasPre()); + bun.assertWithLocation(parsed_version.valid, @src()); + bun.assertWithLocation(parsed_version.version.tag.hasBuild() or parsed_version.version.tag.hasPre(), @src()); } } if (!parsed_version.valid) continue; var package_version: PackageVersion = empty_version; - if (prop.value.?.asProperty("cpu")) |cpu| { - package_version.cpu = Architecture.all; + if (prop.value.?.asProperty("cpu")) |cpu_q| { + var cpu = Architecture.none.negatable(); - switch (cpu.expr.data) { + switch (cpu_q.expr.data) { .e_array => |arr| { const items = arr.slice(); if (items.len > 0) { - package_version.cpu = Architecture.none; for (items) |item| { if (item.asString(allocator)) |cpu_str_| { - package_version.cpu = package_version.cpu.apply(cpu_str_); + cpu.apply(cpu_str_); } } } }, .e_string => |stri| { - package_version.cpu = Architecture.apply(Architecture.none, stri.data); + cpu.apply(stri.data); }, else => {}, } + package_version.cpu = cpu.combine(); } - if (prop.value.?.asProperty("os")) |os| { - package_version.os = OperatingSystem.all; + if (prop.value.?.asProperty("os")) |os_q| { + var os = OperatingSystem.none.negatable(); - switch (os.expr.data) { + switch (os_q.expr.data) { .e_array => |arr| { const items = arr.slice(); if (items.len > 0) { - package_version.os = OperatingSystem.none; for (items) |item| { if (item.asString(allocator)) |cpu_str_| { - package_version.os = package_version.os.apply(cpu_str_); + os.apply(cpu_str_); } } } }, .e_string => |stri| { - package_version.os = OperatingSystem.apply(OperatingSystem.none, stri.data); + os.apply(stri.data); }, else => {}, } + package_version.os = os.combine(); } if (prop.value.?.asProperty("libc")) |libc| { - package_version.libc = Libc.none; + var libc_ = Libc.none.negatable(); switch (libc.expr.data) { .e_array => |arr| { const items = arr.slice(); if (items.len > 0) { - package_version.libc = Libc.none; for (items) |item| { if (item.asString(allocator)) |libc_str_| { - package_version.libc = package_version.libc.apply(libc_str_); + libc_.apply(libc_str_); } } } }, .e_string => |stri| { - package_version.libc = Libc.apply(.none, stri.data); + libc_.apply(stri.data); }, else => {}, } + package_version.libc = libc_.combine(); } if (prop.value.?.asProperty("hasInstallScript")) |has_install_script| { @@ -1774,13 +1912,13 @@ pub const PackageManifest = struct { if (comptime Environment.allow_assert) { const dependencies_list = @field(package_version, pair.field); - assert(dependencies_list.name.off < all_extern_strings.len); - assert(dependencies_list.value.off < all_extern_strings.len); - assert(dependencies_list.name.off + dependencies_list.name.len < all_extern_strings.len); - assert(dependencies_list.value.off + dependencies_list.value.len < all_extern_strings.len); + bun.assertWithLocation(dependencies_list.name.off < all_extern_strings.len, @src()); + bun.assertWithLocation(dependencies_list.value.off < all_extern_strings.len, @src()); + bun.assertWithLocation(dependencies_list.name.off + dependencies_list.name.len < all_extern_strings.len, @src()); + bun.assertWithLocation(dependencies_list.value.off + dependencies_list.value.len < all_extern_strings.len, @src()); - assert(std.meta.eql(dependencies_list.name.get(all_extern_strings), this_names)); - assert(std.meta.eql(dependencies_list.value.get(version_extern_strings), this_versions)); + bun.assertWithLocation(std.meta.eql(dependencies_list.name.get(all_extern_strings), this_names), @src()); + bun.assertWithLocation(std.meta.eql(dependencies_list.value.get(version_extern_strings), this_versions), @src()); var j: usize = 0; const name_dependencies = dependencies_list.name.get(all_extern_strings); @@ -1788,31 +1926,31 @@ pub const PackageManifest = struct { if (optional_peer_dep_names.items.len == 0) { while (j < name_dependencies.len) : (j += 1) { const dep_name = name_dependencies[j]; - assert(std.mem.eql(u8, dep_name.slice(string_buf), this_names[j].slice(string_buf))); - assert(std.mem.eql(u8, dep_name.slice(string_buf), items[j].key.?.asString(allocator).?)); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), this_names[j].slice(string_buf)), @src()); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), items[j].key.?.asString(allocator).?), @src()); } j = 0; while (j < dependencies_list.value.len) : (j += 1) { const dep_name = dependencies_list.value.get(version_extern_strings)[j]; - assert(std.mem.eql(u8, dep_name.slice(string_buf), this_versions[j].slice(string_buf))); - assert(std.mem.eql(u8, dep_name.slice(string_buf), items[j].value.?.asString(allocator).?)); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), this_versions[j].slice(string_buf)), @src()); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), items[j].value.?.asString(allocator).?), @src()); } } } else { while (j < name_dependencies.len) : (j += 1) { const dep_name = name_dependencies[j]; - assert(std.mem.eql(u8, dep_name.slice(string_buf), this_names[j].slice(string_buf))); - assert(std.mem.eql(u8, dep_name.slice(string_buf), items[j].key.?.asString(allocator).?)); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), this_names[j].slice(string_buf)), @src()); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), items[j].key.?.asString(allocator).?), @src()); } j = 0; while (j < dependencies_list.value.len) : (j += 1) { const dep_name = dependencies_list.value.get(version_extern_strings)[j]; - assert(std.mem.eql(u8, dep_name.slice(string_buf), this_versions[j].slice(string_buf))); - assert(std.mem.eql(u8, dep_name.slice(string_buf), items[j].value.?.asString(allocator).?)); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), this_versions[j].slice(string_buf)), @src()); + bun.assertWithLocation(std.mem.eql(u8, dep_name.slice(string_buf), items[j].value.?.asString(allocator).?), @src()); } } } @@ -1865,8 +2003,8 @@ pub const PackageManifest = struct { }; if (comptime Environment.allow_assert) { - assert(std.meta.eql(result.pkg.dist_tags.versions.get(all_semver_versions), dist_tag_versions[0..dist_tag_i])); - assert(std.meta.eql(result.pkg.dist_tags.tags.get(all_extern_strings), extern_strings_slice[0..dist_tag_i])); + bun.assertWithLocation(std.meta.eql(result.pkg.dist_tags.versions.get(all_semver_versions), dist_tag_versions[0..dist_tag_i]), @src()); + bun.assertWithLocation(std.meta.eql(result.pkg.dist_tags.tags.get(all_extern_strings), extern_strings_slice[0..dist_tag_i]), @src()); } extern_strings = extern_strings[dist_tag_i..]; @@ -1975,13 +2113,13 @@ pub const PackageManifest = struct { const first = semver_versions_[0]; const second = semver_versions_[1]; const order = second.order(first, string_buf, string_buf); - assert(order == .gt); + bun.assertWithLocation(order == .gt, @src()); } } } }, else => { - assert(max_versions_count == 0); + bun.assertWithLocation(max_versions_count == 0, @src()); }, } @@ -1989,7 +2127,7 @@ pub const PackageManifest = struct { const src = std.mem.sliceAsBytes(all_tarball_url_strings[0 .. all_tarball_url_strings.len - tarball_url_strings.len]); if (src.len > 0) { var dst = std.mem.sliceAsBytes(all_extern_strings[all_extern_strings.len - extern_strings.len ..]); - assert(dst.len >= src.len); + bun.assertWithLocation(dst.len >= src.len, @src()); @memcpy(dst[0..src.len], src); } diff --git a/src/install/patch_install.zig b/src/install/patch_install.zig index 1feb8f79604ee..946bedb2d3fbf 100644 --- a/src/install/patch_install.zig +++ b/src/install/patch_install.zig @@ -367,7 +367,7 @@ pub const PatchTask = struct { )) { .result => |fd| fd, .err => |e| { - return try log.addErrorFmtNoLoc(this.manager.allocator, "{}", .{e}); + return try log.addErrorFmtNoLoc(this.manager.allocator, "failed adding bun tag: {}", .{e.withPath(buntagbuf[0 .. bun_tag_prefix.len + hashlen :0])}); }, }; _ = bun.sys.close(buntagfd); @@ -386,7 +386,8 @@ pub const PatchTask = struct { path_in_tmpdir, bun.toFD(this.callback.apply.cache_dir.fd), this.callback.apply.cache_dir_subpath, - ).asErr()) |e| return try log.addErrorFmtNoLoc(this.manager.allocator, "{}", .{e}); + .{ .move_fallback = true }, + ).asErr()) |e| return try log.addErrorFmtNoLoc(this.manager.allocator, "renaming changes to cache dir: {}", .{e.withPath(this.callback.apply.cache_dir_subpath)}); } pub fn calcHash(this: *PatchTask) ?u64 { diff --git a/src/install/repository.zig b/src/install/repository.zig index 2beb0f4c80c4d..2069d640c4502 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -16,12 +16,126 @@ const string = @import("../string_types.zig").string; const strings = @import("../string_immutable.zig"); const GitSHA = String; const Path = bun.path; +const File = bun.sys.File; threadlocal var final_path_buf: bun.PathBuffer = undefined; threadlocal var ssh_path_buf: bun.PathBuffer = undefined; threadlocal var folder_name_buf: bun.PathBuffer = undefined; threadlocal var json_path_buf: bun.PathBuffer = undefined; +const SloppyGlobalGitConfig = struct { + has_askpass: bool = false, + has_ssh_command: bool = false, + + var holder: SloppyGlobalGitConfig = .{}; + var load_and_parse_once = std.once(loadAndParse); + + pub fn get() SloppyGlobalGitConfig { + load_and_parse_once.call(); + return holder; + } + + pub fn loadAndParse() void { + const home_dir_path = brk: { + if (comptime Environment.isWindows) { + if (bun.getenvZ("USERPROFILE")) |env| + break :brk env; + } else { + if (bun.getenvZ("HOME")) |env| + break :brk env; + } + + // won't find anything + return; + }; + + var config_file_path_buf: bun.PathBuffer = undefined; + const config_file_path = bun.path.joinAbsStringBufZ(home_dir_path, &config_file_path_buf, &.{".gitconfig"}, .auto); + var stack_fallback = std.heap.stackFallback(4096, bun.default_allocator); + const allocator = stack_fallback.get(); + var source = File.toSource(config_file_path, allocator).unwrap() catch { + return; + }; + defer allocator.free(source.contents); + + if (comptime Environment.isWindows) { + if (strings.BOM.detect(source.contents)) |bom| { + source.contents = bom.removeAndConvertToUTF8AndFree(allocator, @constCast(source.contents)) catch bun.outOfMemory(); + } + } + + var remaining = bun.strings.split(source.contents, "\n"); + var found_askpass = false; + var found_ssh_command = false; + var @"[core]" = false; + while (remaining.next()) |line_| { + if (found_askpass and found_ssh_command) break; + + const line = strings.trim(line_, "\t \r"); + + if (line.len == 0) continue; + // skip comments + if (line[0] == '#') continue; + + if (line[0] == '[') { + if (strings.indexOfChar(line, ']')) |end_bracket| { + if (strings.eqlComptime(line[0 .. end_bracket + 1], "[core]")) { + @"[core]" = true; + continue; + } + } + @"[core]" = false; + continue; + } + + if (@"[core]") { + if (!found_askpass) { + if (line.len > "askpass".len and strings.eqlCaseInsensitiveASCIIIgnoreLength(line[0.."askpass".len], "askpass") and switch (line["askpass".len]) { + ' ', '\t', '=' => true, + else => false, + }) { + found_askpass = true; + continue; + } + } + + if (!found_ssh_command) { + if (line.len > "sshCommand".len and strings.eqlCaseInsensitiveASCIIIgnoreLength(line[0.."sshCommand".len], "sshCommand") and switch (line["sshCommand".len]) { + ' ', '\t', '=' => true, + else => false, + }) { + found_ssh_command = true; + } + } + } else { + if (!found_askpass) { + if (line.len > "core.askpass".len and strings.eqlCaseInsensitiveASCIIIgnoreLength(line[0.."core.askpass".len], "core.askpass") and switch (line["sshCommand".len]) { + ' ', '\t', '=' => true, + else => false, + }) { + found_askpass = true; + continue; + } + } + + if (!found_ssh_command) { + if (line.len > "core.sshCommand".len and strings.eqlCaseInsensitiveASCIIIgnoreLength(line[0.."core.sshCommand".len], "core.sshCommand") and switch (line["sshCommand".len]) { + ' ', '\t', '=' => true, + else => false, + }) { + found_ssh_command = true; + } + } + } + } + + holder = .{ + .has_askpass = found_askpass, + .has_ssh_command = found_ssh_command, + }; + } +}; + pub const Repository = extern struct { owner: String = .{}, repo: String = .{}, @@ -29,6 +143,38 @@ pub const Repository = extern struct { resolved: GitSHA = .{}, package_name: String = .{}, + pub var shared_env: struct { + env: ?DotEnv.Map = null, + pub fn get(this: *@This(), allocator: std.mem.Allocator, other: *DotEnv.Loader) DotEnv.Map { + return this.env orelse brk: { + // Note: currently if the user sets this to some value that causes + // a prompt for a password, the stdout of the prompt will be masked + // by further output of the rest of the install process. + // A value can still be entered, but we need to find a workaround + // so the user can see what is being prompted. By default the settings + // below will cause no prompt and throw instead. + var cloned = other.map.cloneWithAllocator(allocator) catch bun.outOfMemory(); + + if (cloned.get("GIT_ASKPASS") == null) { + const config = SloppyGlobalGitConfig.get(); + if (!config.has_askpass) { + cloned.put("GIT_ASKPASS", "echo") catch bun.outOfMemory(); + } + } + + if (cloned.get("GIT_SSH_COMMAND") == null) { + const config = SloppyGlobalGitConfig.get(); + if (!config.has_ssh_command) { + cloned.put("GIT_SSH_COMMAND", "ssh -oStrictHostKeyChecking=accept-new") catch bun.outOfMemory(); + } + } + + this.env = cloned; + break :brk this.env.?; + }; + } + } = .{}; + pub const Hosts = bun.ComptimeStringMap(string, .{ .{ "bitbucket", ".org" }, .{ "github", ".com" }, @@ -43,11 +189,27 @@ pub const Repository = extern struct { ) []u8 { const buf = lockfile.buffers.string_bytes.items; const dep = lockfile.buffers.dependencies.items[dep_id]; - const version_literal = dep.version.literal.slice(buf); const repo_name = repository.repo; const repo_name_str = lockfile.str(&repo_name); - if (repo_name_str.len == 0) { + const name = brk: { + var remain = repo_name_str; + + if (strings.indexOfChar(remain, '#')) |hash_index| { + remain = remain[0..hash_index]; + } + + if (remain.len == 0) break :brk remain; + + if (strings.lastIndexOfChar(remain, '/')) |slash_index| { + remain = remain[slash_index + 1 ..]; + } + + break :brk remain; + }; + + if (name.len == 0) { + const version_literal = dep.version.literal.slice(buf); const name_buf = allocator.alloc(u8, bun.sha.EVP.SHA1.digest) catch bun.outOfMemory(); var sha1 = bun.sha.SHA1.init(); defer sha1.deinit(); @@ -56,26 +218,7 @@ pub const Repository = extern struct { return name_buf[0..bun.sha.SHA1.digest]; } - var len: usize = 0; - var remain = repo_name_str; - while (strings.indexOfChar(remain, '@')) |at_index| { - len += remain[0..at_index].len; - remain = remain[at_index + 1 ..]; - } - len += remain.len; - - const name_buf = allocator.alloc(u8, len) catch bun.outOfMemory(); - var name = name_buf; - len = 0; - remain = repo_name_str; - while (strings.indexOfChar(remain, '@')) |at_index| { - @memcpy(name[0..at_index], remain[0..at_index]); - name = name[at_index + 1 ..]; - remain = remain[at_index + 1 ..]; - } - - @memcpy(name[0..remain.len], remain); - return name_buf[0..name_buf.len]; + return allocator.dupe(u8, name) catch bun.outOfMemory(); } pub fn order(lhs: *const Repository, rhs: *const Repository, lhs_buf: []const u8, rhs_buf: []const u8) std.math.Order { @@ -150,40 +293,13 @@ pub const Repository = extern struct { fn exec( allocator: std.mem.Allocator, - env: *DotEnv.Loader, + _env: DotEnv.Map, argv: []const string, ) !string { - // Note: currently if the user sets this to some value that causes - // a prompt for a password, the stdout of the prompt will be masked - // by further output of the rest of the install process. - // A value can still be entered, but we need to find a workaround - // so the user can see what is being prompted. By default the settings - // below will cause no prompt and throw instead. - const askpass_entry = env.map.getOrPutWithoutValue("GIT_ASKPASS") catch bun.outOfMemory(); - if (!askpass_entry.found_existing) { - askpass_entry.key_ptr.* = allocator.dupe(u8, "GIT_ASKPASS") catch bun.outOfMemory(); - askpass_entry.value_ptr.* = .{ - .value = allocator.dupe(u8, "echo") catch bun.outOfMemory(), - .conditional = false, - }; - } - - const ssh_command_entry = env.map.getOrPutWithoutValue("GIT_SSH_COMMAND") catch bun.outOfMemory(); - if (!ssh_command_entry.found_existing) { - ssh_command_entry.key_ptr.* = allocator.dupe(u8, "GIT_SSH_COMMAND") catch bun.outOfMemory(); - ssh_command_entry.value_ptr.* = .{ - .value = allocator.dupe(u8, "ssh -oStrictHostKeyChecking=accept-new") catch bun.outOfMemory(), - .conditional = false, - }; - } + var env = _env; + var std_map = try env.stdEnvMap(allocator); - var std_map = try env.map.stdEnvMap(allocator); - - defer { - if (!askpass_entry.found_existing) env.map.remove("GIT_ASKPASS"); - if (!ssh_command_entry.found_existing) env.map.remove("GIT_SSH_COMMAND"); - std_map.deinit(); - } + defer std_map.deinit(); const result = if (comptime Environment.isWindows) try std.process.Child.run(.{ @@ -203,7 +319,11 @@ pub const Repository = extern struct { // remote: The page could not be found <-- for non git // remote: Repository not found. <-- for git // remote: fatal repository '' does not exist <-- for git - (strings.containsComptime(result.stderr, "remote:") and strings.containsComptime(result.stderr, "not") and strings.containsComptime(result.stderr, "found")) or strings.containsComptime(result.stderr, "does not exist")) { + (strings.containsComptime(result.stderr, "remote:") and + strings.containsComptime(result.stderr, "not") and + strings.containsComptime(result.stderr, "found")) or + strings.containsComptime(result.stderr, "does not exist")) + { return error.RepositoryNotFound; }, else => {}, @@ -287,7 +407,16 @@ pub const Repository = extern struct { return null; } - pub fn download(allocator: std.mem.Allocator, env: *DotEnv.Loader, log: *logger.Log, cache_dir: std.fs.Dir, task_id: u64, name: string, url: string, attempt: u8) !std.fs.Dir { + pub fn download( + allocator: std.mem.Allocator, + env: DotEnv.Map, + log: *logger.Log, + cache_dir: std.fs.Dir, + task_id: u64, + name: string, + url: string, + attempt: u8, + ) !std.fs.Dir { bun.Analytics.Features.git_dependencies += 1; const folder_name = try std.fmt.bufPrintZ(&folder_name_buf, "{any}.git", .{ bun.fmt.hexIntLower(task_id), @@ -358,7 +487,7 @@ pub const Repository = extern struct { return std.mem.trim(u8, exec( allocator, - env, + shared_env.get(allocator, env), if (committish.len > 0) &[_]string{ "git", "-C", path, "log", "--format=%H", "-1", committish } else @@ -377,7 +506,7 @@ pub const Repository = extern struct { pub fn checkout( allocator: std.mem.Allocator, - env: *DotEnv.Loader, + env: DotEnv.Map, log: *logger.Log, cache_dir: std.fs.Dir, repo_dir: std.fs.Dir, diff --git a/src/install/resolution.zig b/src/install/resolution.zig index 39e3decda1c8d..2fecc21aff94d 100644 --- a/src/install/resolution.zig +++ b/src/install/resolution.zig @@ -247,7 +247,7 @@ pub const Resolution = extern struct { pub fn format(formatter: DebugFormatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void { try writer.writeAll("Resolution{ ."); - try writer.writeAll(std.enums.tagName(Tag, formatter.resolution.tag) orelse "invalid"); + try writer.writeAll(bun.tagName(Tag, formatter.resolution.tag) orelse "invalid"); try writer.writeAll(" = "); switch (formatter.resolution.tag) { .npm => try formatter.resolution.value.npm.version.fmt(formatter.buf).format(layout, opts, writer), diff --git a/src/install/semver.zig b/src/install/semver.zig index 9d0f818dbd811..ef93a68be5f35 100644 --- a/src/install/semver.zig +++ b/src/install/semver.zig @@ -252,6 +252,11 @@ pub const String = extern struct { return @as(Pointer, @bitCast(@as(u64, @as(u63, @truncate(@as(u64, @bitCast(this))))))); } + pub fn toJS(this: *const String, buffer: []const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var str = bun.String.init(this.slice(buffer)); + return str.transferToJS(globalThis); + } + // String must be a pointer because we reference it as a slice. It will become a dead pointer if it is copied. pub fn slice(this: *const String, buf: string) string { switch (this.bytes[max_inline_len - 1] & 128) { @@ -553,7 +558,7 @@ pub const SlicedString = struct { slice: string, pub inline fn init(buf: string, slice: string) SlicedString { - if (Environment.allow_assert) { + if (Environment.allow_assert and !@inComptime()) { if (@intFromPtr(buf.ptr) > @intFromPtr(slice.ptr)) { @panic("SlicedString.init buf is not in front of slice"); } @@ -609,6 +614,10 @@ pub const Version = extern struct { return this.patch == 0 and this.minor == 0 and this.major == 0; } + pub fn parseUTF8(slice: []const u8) ParseResult { + return parse(.{ .buf = slice, .slice = slice }); + } + pub fn cloneInto(this: Version, slice: []const u8, buf: *[]u8) Version { return .{ .major = this.major, @@ -622,10 +631,175 @@ pub const Version = extern struct { return this.tag.build.len + this.tag.pre.len; } + pub const Formatter = struct { + version: Version, + input: string, + + pub fn format(formatter: Formatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + const self = formatter.version; + try std.fmt.format(writer, "{?d}.{?d}.{?d}", .{ self.major, self.minor, self.patch }); + + if (self.tag.hasPre()) { + const pre = self.tag.pre.slice(formatter.input); + try writer.writeAll("-"); + try writer.writeAll(pre); + } + + if (self.tag.hasBuild()) { + const build = self.tag.build.slice(formatter.input); + try writer.writeAll("+"); + try writer.writeAll(build); + } + } + }; + pub fn fmt(this: Version, input: string) Formatter { return .{ .version = this, .input = input }; } + pub const DiffFormatter = struct { + version: Version, + buf: string, + other: Version, + other_buf: string, + + pub fn format(this: DiffFormatter, comptime fmt_: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + if (!Output.enable_ansi_colors) { + // print normally if no colors + const formatter: Formatter = .{ .version = this.version, .input = this.buf }; + return Formatter.format(formatter, fmt_, options, writer); + } + + const diff = this.version.whichVersionIsDifferent(this.other, this.buf, this.other_buf) orelse .none; + + switch (diff) { + .major => try writer.print(Output.prettyFmt("{d}.{d}.{d}", true), .{ + this.version.major, this.version.minor, this.version.patch, + }), + .minor => { + if (this.version.major == 0) { + try writer.print(Output.prettyFmt("{d}.{d}.{d}", true), .{ + this.version.major, this.version.minor, this.version.patch, + }); + } else { + try writer.print(Output.prettyFmt("{d}.{d}.{d}", true), .{ + this.version.major, this.version.minor, this.version.patch, + }); + } + }, + .patch => { + if (this.version.major == 0 and this.version.minor == 0) { + try writer.print(Output.prettyFmt("{d}.{d}.{d}", true), .{ + this.version.major, this.version.minor, this.version.patch, + }); + } else { + try writer.print(Output.prettyFmt("{d}.{d}.{d}", true), .{ + this.version.major, this.version.minor, this.version.patch, + }); + } + }, + .none, .pre, .build => try writer.print(Output.prettyFmt("{d}.{d}.{d}", true), .{ + this.version.major, this.version.minor, this.version.patch, + }), + } + + // might be pre or build. loop through all characters, and insert on + // first diff. + + var set_color = false; + if (this.version.tag.hasPre()) { + if (this.other.tag.hasPre()) { + const pre = this.version.tag.pre.slice(this.buf); + const other_pre = this.other.tag.pre.slice(this.other_buf); + + var first = true; + for (pre, 0..) |c, i| { + if (!set_color and i < other_pre.len and c != other_pre[i]) { + set_color = true; + try writer.writeAll(Output.prettyFmt("", true)); + } + if (first) { + first = false; + try writer.writeByte('-'); + } + try writer.writeByte(c); + } + } else { + try writer.print(Output.prettyFmt("-{}", true), .{this.version.tag.pre.fmt(this.buf)}); + set_color = true; + } + } + + if (this.version.tag.hasBuild()) { + if (this.other.tag.hasBuild()) { + const build = this.version.tag.build.slice(this.buf); + const other_build = this.other.tag.build.slice(this.other_buf); + + var first = true; + for (build, 0..) |c, i| { + if (!set_color and i < other_build.len and c != other_build[i]) { + set_color = true; + try writer.writeAll(Output.prettyFmt("", true)); + } + if (first) { + first = false; + try writer.writeByte('+'); + } + try writer.writeByte(c); + } + } else { + if (!set_color) { + try writer.print(Output.prettyFmt("+{}", true), .{this.version.tag.build.fmt(this.buf)}); + } else { + try writer.print("+{}", .{this.version.tag.build.fmt(this.other_buf)}); + } + } + } + + try writer.writeAll(Output.prettyFmt("", true)); + } + }; + + pub fn diffFmt(this: Version, other: Version, this_buf: string, other_buf: string) DiffFormatter { + return .{ + .version = this, + .buf = this_buf, + .other = other, + .other_buf = other_buf, + }; + } + + pub const ChangedVersion = enum { + major, + minor, + patch, + pre, + build, + none, + }; + + pub fn whichVersionIsDifferent( + left: Version, + right: Version, + left_buf: string, + right_buf: string, + ) ?ChangedVersion { + if (left.major != right.major) return .major; + if (left.minor != right.minor) return .minor; + if (left.patch != right.patch) return .patch; + + if (left.tag.hasPre() != right.tag.hasPre()) return .pre; + if (!left.tag.hasPre() and !right.tag.hasPre()) return null; + if (left.tag.orderPre(right.tag, left_buf, right_buf) != .eq) return .pre; + + if (left.tag.hasBuild() != right.tag.hasBuild()) return .build; + if (!left.tag.hasBuild() and !right.tag.hasBuild()) return null; + return if (left.tag.build.order(&right.tag.build, left_buf, right_buf) != .eq) + .build + else + null; + } + pub fn count(this: *const Version, buf: []const u8, comptime StringBuilder: type, builder: StringBuilder) void { if (this.tag.hasPre() and !this.tag.pre.isInline()) builder.count(this.tag.pre.slice(buf)); if (this.tag.hasBuild() and !this.tag.build.isInline()) builder.count(this.tag.build.slice(buf)); @@ -685,28 +859,6 @@ pub const Version = extern struct { return bun.Wyhash.hash(0, bytes); } - pub const Formatter = struct { - version: Version, - input: string, - - pub fn format(formatter: Formatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - const self = formatter.version; - try std.fmt.format(writer, "{?d}.{?d}.{?d}", .{ self.major, self.minor, self.patch }); - - if (self.tag.hasPre()) { - const pre = self.tag.pre.slice(formatter.input); - try writer.writeAll("-"); - try writer.writeAll(pre); - } - - if (self.tag.hasBuild()) { - const build = self.tag.build.slice(formatter.input); - try writer.writeAll("+"); - try writer.writeAll(build); - } - } - }; - pub fn eql(lhs: Version, rhs: Version) bool { return lhs.major == rhs.major and lhs.minor == rhs.minor and lhs.patch == rhs.patch and rhs.tag.eql(lhs.tag); } @@ -1864,18 +2016,18 @@ pub const Query = struct { try std.json.encodeJsonString(temp, .{}, writer); } - pub fn deinit(this: *Group) void { + pub fn deinit(this: *const Group) void { var list = this.head; var allocator = this.allocator; while (list.next) |next| { var query = list.head; while (query.next) |next_query| { - allocator.destroy(next_query); query = next_query.*; + allocator.destroy(next_query); } - allocator.destroy(next); list = next.*; + allocator.destroy(next); } } @@ -2241,11 +2393,13 @@ pub const Query = struct { }; }; + const ParseError = error{OutOfMemory}; + pub fn parse( allocator: Allocator, input: string, sliced: SlicedString, - ) !Group { + ) ParseError!Group { var i: usize = 0; var list = Group{ .allocator = allocator, @@ -2497,7 +2651,7 @@ pub const SemverObject = struct { pub fn order( globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var arena = std.heap.ArenaAllocator.init(bun.default_allocator); defer arena.deinit(); var stack_fallback = std.heap.stackFallback(512, arena.allocator()); @@ -2549,7 +2703,7 @@ pub const SemverObject = struct { pub fn satisfies( globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { var arena = std.heap.ArenaAllocator.init(bun.default_allocator); defer arena.deinit(); var stack_fallback = std.heap.stackFallback(512, arena.allocator()); @@ -2586,7 +2740,15 @@ pub const SemverObject = struct { allocator, right.slice(), SlicedString.init(right.slice(), right.slice()), - ) catch return .false; + ) catch |err| { + switch (err) { + error.OutOfMemory => { + globalThis.throwOutOfMemory(); + return .zero; + }, + } + }; + defer right_group.deinit(); const right_version = right_group.getExactVersion(); diff --git a/src/install/windows-shim/build.zig b/src/install/windows-shim/build.zig deleted file mode 100644 index cc52098147ff0..0000000000000 --- a/src/install/windows-shim/build.zig +++ /dev/null @@ -1,45 +0,0 @@ -const std = @import("std"); - -/// TODO(@paperdave): properly integrate this with the rest of Bun's build system. -/// There is no reason that this is a separate Zig project with build artifacts committed. -pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{ - .default_target = .{ - .cpu_model = .{ .explicit = &std.Target.x86.cpu.nehalem }, - .os_tag = .windows, - }, - }); - - std.debug.assert(target.result.os.tag == .windows); - - const exe = b.addExecutable(.{ - .name = "bun_shim_impl", - .root_source_file = .{ .path = "bun_shim_impl.zig" }, - .target = target, - .optimize = .ReleaseFast, - .use_llvm = true, - .use_lld = true, - .unwind_tables = false, - .omit_frame_pointer = true, - .strip = true, - .linkage = .static, - .sanitize_thread = false, - .single_threaded = true, - .link_libc = false, - }); - - const dbg = b.addExecutable(.{ - .name = "bun_shim_debug", - .root_source_file = .{ .path = "bun_shim_impl.zig" }, - .target = target, - .optimize = .Debug, - .use_llvm = true, - .use_lld = true, - .linkage = .static, - .single_threaded = true, - .link_libc = false, - }); - - b.installArtifact(exe); - b.installArtifact(dbg); -} diff --git a/src/io/PipeWriter.zig b/src/io/PipeWriter.zig index 54f4f19e460e2..fd41ae637c3ab 100644 --- a/src/io/PipeWriter.zig +++ b/src/io/PipeWriter.zig @@ -891,6 +891,7 @@ pub fn WindowsBufferedWriter( ) type { return struct { source: ?Source = null, + owns_fd: bool = true, parent: *Parent = undefined, is_done: bool = false, // we use only one write_req, any queued data in outgoing will be flushed after this ends @@ -1003,6 +1004,7 @@ pub fn WindowsBufferedWriter( this.is_done = true; if (this.pending_payload_size == 0) { // will auto close when pending stuff get written + if (!this.owns_fd) return; this.close(); } } @@ -1131,6 +1133,9 @@ pub fn WindowsStreamingWriter( ) type { return struct { source: ?Source = null, + /// if the source of this writer is a file descriptor, calling end() will not close it. + /// if it is a path, then we claim ownership and the backing fd will be closed by end(). + owns_fd: bool = true, parent: *Parent = undefined, is_done: bool = false, // we use only one write_req, any queued data in outgoing will be flushed after this ends @@ -1150,9 +1155,11 @@ pub fn WindowsStreamingWriter( fn onCloseSource(this: *WindowsWriter) void { this.source = null; - if (!this.closed_without_reporting) { - onClose(this.parent); + if (this.closed_without_reporting) { + this.closed_without_reporting = false; + return; } + onClose(this.parent); } pub fn startWithCurrentPipe(this: *WindowsWriter) bun.JSC.Maybe(void) { @@ -1301,7 +1308,7 @@ pub fn WindowsStreamingWriter( // clean both buffers if needed this.outgoing.deinit(); this.current_payload.deinit(); - this.close(); + this.closeWithoutReporting(); } fn writeInternal(this: *WindowsWriter, buffer: anytype, comptime writeFn: anytype) WriteResult { @@ -1378,10 +1385,13 @@ pub fn WindowsStreamingWriter( return; } - this.is_done = true; this.closed_without_reporting = false; - // if we are done we can call close if not we wait all the data to be flushed - if (this.isDone()) { + this.is_done = true; + + if (!this.hasPendingData()) { + if (!this.owns_fd) { + return; + } this.close(); } } diff --git a/src/io/io.zig b/src/io/io.zig index 240eba96ec5d7..7a4f36d3c2505 100644 --- a/src/io/io.zig +++ b/src/io/io.zig @@ -25,40 +25,43 @@ pub const Loop = struct { active: usize = 0, var loop: Loop = undefined; - var has_loaded_loop: bool = false; + + fn load() void { + loop = Loop{ + .waker = bun.Async.Waker.init() catch @panic("failed to initialize waker"), + }; + if (comptime Environment.isLinux) { + loop.epoll_fd = bun.toFD(std.posix.epoll_create1(std.os.linux.EPOLL.CLOEXEC | 0) catch @panic("Failed to create epoll file descriptor")); + + { + var epoll = std.mem.zeroes(std.os.linux.epoll_event); + epoll.events = std.os.linux.EPOLL.IN | std.os.linux.EPOLL.ERR | std.os.linux.EPOLL.HUP; + epoll.data.ptr = @intFromPtr(&loop); + const rc = std.os.linux.epoll_ctl(loop.epoll_fd.cast(), std.os.linux.EPOLL.CTL_ADD, loop.waker.getFd().cast(), &epoll); + + switch (bun.C.getErrno(rc)) { + .SUCCESS => {}, + else => |err| bun.Output.panic("Failed to wait on epoll {s}", .{@tagName(err)}), + } + } + } + var thread = std.Thread.spawn(.{ + .allocator = bun.default_allocator, + + // smaller thread, since it's not doing much. + .stack_size = 1024 * 1024 * 2, + }, onSpawnIOThread, .{}) catch @panic("Failed to spawn IO watcher thread"); + thread.detach(); + } + var once = std.once(load); pub fn get() *Loop { if (Environment.isWindows) { @panic("Do not use this API on windows"); } - if (!@atomicRmw(bool, &has_loaded_loop, std.builtin.AtomicRmwOp.Xchg, true, .monotonic)) { - loop = Loop{ - .waker = bun.Async.Waker.init() catch @panic("failed to initialize waker"), - }; - if (comptime Environment.isLinux) { - loop.epoll_fd = bun.toFD(std.posix.epoll_create1(std.os.linux.EPOLL.CLOEXEC | 0) catch @panic("Failed to create epoll file descriptor")); - - { - var epoll = std.mem.zeroes(std.os.linux.epoll_event); - epoll.events = std.os.linux.EPOLL.IN | std.os.linux.EPOLL.ERR | std.os.linux.EPOLL.HUP; - epoll.data.ptr = @intFromPtr(&loop); - const rc = std.os.linux.epoll_ctl(loop.epoll_fd.cast(), std.os.linux.EPOLL.CTL_ADD, loop.waker.getFd().cast(), &epoll); - - switch (bun.C.getErrno(rc)) { - .SUCCESS => {}, - else => |err| bun.Output.panic("Failed to wait on epoll {s}", .{@tagName(err)}), - } - } - } - var thread = std.Thread.spawn(.{ - .allocator = bun.default_allocator, + once.call(); - // smaller thread, since it's not doing much. - .stack_size = 1024 * 1024 * 2, - }, onSpawnIOThread, .{}) catch @panic("Failed to spawn IO watcher thread"); - thread.detach(); - } return &loop; } @@ -123,8 +126,14 @@ pub const Loop = struct { } }, .close => |close| { - close.poll.unregisterWithFd(this.pollfd(), close.fd); - this.active -= 1; + log("close({}, registered={any})", .{ close.fd, close.poll.flags.contains(.registered) }); + // Only remove from the interest list if it was previously registered. + // Otherwise, epoll gets confused. + // This state can happen if polling for readable/writable previously failed. + if (close.poll.flags.contains(.was_ever_registered)) { + close.poll.unregisterWithFd(this.pollfd(), close.fd); + this.active -= 1; + } close.onDone(close.ctx); }, } @@ -563,7 +572,6 @@ pub const Poll = struct { fd.cast(), null, ); - this.flags.remove(.was_ever_registered); this.flags.remove(.registered); } @@ -641,7 +649,7 @@ pub const Poll = struct { var event = linux.epoll_event{ .events = flags, .data = .{ .u64 = @intFromPtr(Pollable.init(tag, this).ptr()) } }; - const op: u32 = if (this.flags.contains(.registered) or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD; + const op: u32 = if (this.flags.contains(.was_ever_registered) or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD; const ctl = linux.epoll_ctl( watcher_fd.cast(), @@ -649,11 +657,15 @@ pub const Poll = struct { fd.cast(), &event, ); - this.flags.insert(.registered); - this.flags.insert(.was_ever_registered); + if (JSC.Maybe(void).errnoSys(ctl, .epoll_ctl)) |errno| { return errno; } + // Only mark if it successfully registered. + // If it failed to register, we don't want to unregister it later if + // it never had done so in the first place. + this.flags.insert(.registered); + this.flags.insert(.was_ever_registered); } else { @compileError("epoll not supported on this platform"); } diff --git a/src/io/io_darwin.cpp b/src/io/io_darwin.cpp index 61e3f16243e1c..3f4744454c07e 100644 --- a/src/io/io_darwin.cpp +++ b/src/io/io_darwin.cpp @@ -7,18 +7,36 @@ // errno #include +#include "wtf/Assertions.h" + extern "C" mach_port_t io_darwin_create_machport(uint64_t wakeup, int32_t fd, void *wakeup_buffer_, size_t nbytes) { mach_port_t port; - // Create a Mach port that will be used to wake up the pump - kern_return_t kr = - mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); - if (kr != KERN_SUCCESS) { - return 0; + mach_port_t self = mach_task_self(); + kern_return_t kr = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &port); + + if (UNLIKELY(kr != KERN_SUCCESS)) { + return 0; + } + + // Insert a send right into the port since we also use this to send + kr = mach_port_insert_right(self, port, port, MACH_MSG_TYPE_MAKE_SEND); + if (UNLIKELY(kr != KERN_SUCCESS)) { + return 0; } + // Modify the port queue size to be 1 because we are only + // using it for notifications and not for any other purpose. + mach_port_limits_t limits = { .mpl_qlimit = 1 }; + kr = mach_port_set_attributes(self, port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT); + + if (UNLIKELY(kr != KERN_SUCCESS)) { + return 0; + } + + // Configure the event to directly receive the Mach message as part of the // kevent64() call. kevent64_s event{}; @@ -58,22 +76,46 @@ extern "C" bool getaddrinfo_send_reply(mach_port_t port, } extern "C" bool io_darwin_schedule_wakeup(mach_port_t waker) { - mach_msg_empty_send_t message{}; - message.header.msgh_size = sizeof(message); - message.header.msgh_bits = - MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND_ONCE); - message.header.msgh_remote_port = waker; - kern_return_t kr = mach_msg_send(&message.header); - if (kr != KERN_SUCCESS) { - // If io_darwin_schedule_wakeup() is being called by other threads faster - // than the pump can dispatch work, the kernel message queue for the wakeup - // port can fill The kernel does return a SEND_ONCE right in the case of - // failure, which must be destroyed to avoid leaking. - mach_msg_destroy(&message.header); - return false; - } - - return true; + mach_msg_header_t msg = { + .msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0), + .msgh_size = sizeof(mach_msg_header_t), + .msgh_remote_port = waker, + .msgh_local_port = MACH_PORT_NULL, + .msgh_voucher_port = 0, + .msgh_id = 0, + }; + + mach_msg_return_t kr = mach_msg( + &msg, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + msg.msgh_size, + 0, + MACH_PORT_NULL, + 0, // Fail instantly if the port is full + MACH_PORT_NULL + ); + + switch (kr) { + case MACH_MSG_SUCCESS: { + return true; + } + + // This means that the send would've blocked because the + // queue is full. We assume success because the port is full. + case MACH_SEND_TIMED_OUT: { + return true; + } + + // No space means it will wake up. + case MACH_SEND_NO_BUFFER: { + return true; + } + + default: { + ASSERT_NOT_REACHED_WITH_MESSAGE("mach_msg failed with %x", kr); + return false; + } + } } #else diff --git a/src/io/source.zig b/src/io/source.zig index 2501e902053fe..99447a44b8291 100644 --- a/src/io/source.zig +++ b/src/io/source.zig @@ -113,9 +113,7 @@ pub const Source = union(enum) { else => {}, } - const file_fd = bun.uvfdcast(fd); - - return switch (pipe.open(file_fd)) { + return switch (pipe.open(fd)) { .err => |err| .{ .err = err, }, diff --git a/src/js/builtins.d.ts b/src/js/builtins.d.ts index 54cb496c42d6c..12d7d7e7f9bd8 100644 --- a/src/js/builtins.d.ts +++ b/src/js/builtins.d.ts @@ -1,3 +1,4 @@ +/// // Typedefs for JSC intrinsics. Instead of @, we use $ type TODO = any; @@ -539,3 +540,9 @@ declare var $Buffer: { declare interface Error { code?: string; } + +/** + * -- Error Codes with manual messages + */ +declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedType: string, actualValue: string): TypeError; +declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedTypes: any[], actualValue: string): TypeError; diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 226a340ae364f..fbec2c56bdb83 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -14,6 +14,7 @@ #include #include #include +#include "BunBuiltinNames+extras.h" namespace WebCore { @@ -81,6 +82,7 @@ using namespace JSC; macro(encoding) \ macro(end) \ macro(errno) \ + macro(makeErrorWithCode) \ macro(errorSteps) \ macro(evaluateCommonJSModule) \ macro(evaluated) \ @@ -93,7 +95,7 @@ using namespace JSC; macro(fetch) \ macro(fetchRequest) \ macro(file) \ - macro(filePath) \ + macro(filename) \ macro(fillFromJS) \ macro(finishConsumingStream) \ macro(flush) \ @@ -151,6 +153,7 @@ using namespace JSC; macro(password) \ macro(patch) \ macro(path) \ + macro(paths) \ macro(pathname) \ macro(pause) \ macro(pendingAbortRequest) \ @@ -194,7 +197,6 @@ using namespace JSC; macro(requireESM) \ macro(requireMap) \ macro(requireNativeModule) \ - macro(resolve) \ macro(resolveSync) \ macro(resume) \ macro(self) \ @@ -220,9 +222,11 @@ using namespace JSC; macro(stream) \ macro(structuredCloneForStream) \ macro(syscall) \ + macro(textDecoder) \ macro(textDecoderStreamDecoder) \ macro(textDecoderStreamTransform) \ macro(textEncoderStreamEncoder) \ + macro(TextEncoderStreamEncoder) \ macro(textEncoderStreamTransform) \ macro(toNamespacedPath) \ macro(trace) \ @@ -250,6 +254,8 @@ using namespace JSC; macro(writeRequests) \ macro(writing) \ macro(written) \ + BUN_ADDITIONAL_BUILTIN_NAMES(macro) +// --- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME --- class BunBuiltinNames { public: @@ -268,6 +274,8 @@ class BunBuiltinNames { BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_BUILTIN_IDENTIFIER_ACCESSOR) + const JSC::Identifier& resolvePublicName() const { return m_vm.propertyNames->resolve;} + private: JSC::VM& m_vm; BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_BUILTIN_NAMES) diff --git a/src/js/builtins/ConsoleObject.ts b/src/js/builtins/ConsoleObject.ts index 1e35d8de78083..bf1dabf1aa45d 100644 --- a/src/js/builtins/ConsoleObject.ts +++ b/src/js/builtins/ConsoleObject.ts @@ -596,8 +596,7 @@ export function createConsoleConstructor(console: typeof globalThis.console) { // It only makes sense to clear if _stdout is a TTY. // Otherwise, do nothing. if (this._stdout.isTTY && process.env.TERM !== "dumb") { - this._stdout.write("\x1b[2J"); - this._stdout.write("\x1b[0f"); + this._stdout.write("\x1B[2J\x1B[3J\x1B[H"); } }, diff --git a/src/js/builtins/JSBufferPrototype.ts b/src/js/builtins/JSBufferPrototype.ts index cf5b3fa3a7ec4..986e0992ba321 100644 --- a/src/js/builtins/JSBufferPrototype.ts +++ b/src/js/builtins/JSBufferPrototype.ts @@ -400,56 +400,6 @@ export function writeBigUInt64BE(this: BufferExt, value, offset) { return offset + 8; } -export function utf8Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "utf8"); -} -export function ucs2Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "ucs2"); -} -export function utf16leWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "utf16le"); -} -export function latin1Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "latin1"); -} -export function asciiWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "ascii"); -} -export function base64Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "base64"); -} -export function base64urlWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "base64url"); -} -export function hexWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "hex"); -} - -export function utf8Slice(this: BufferExt, start, end) { - return this.toString("utf8", start, end); -} -export function ucs2Slice(this: BufferExt, start, end) { - return this.toString("ucs2", start, end); -} -export function utf16leSlice(this: BufferExt, start, end) { - return this.toString("utf16le", start, end); -} -export function latin1Slice(this: BufferExt, start, end) { - return this.toString("latin1", start, end); -} -export function asciiSlice(this: BufferExt, start, end) { - return this.toString("ascii", start, end); -} -export function base64Slice(this: BufferExt, start, end) { - return this.toString("base64", start, end); -} -export function base64urlSlice(this: BufferExt, start, end) { - return this.toString("base64url", start, end); -} -export function hexSlice(this: BufferExt, start, end) { - return this.toString("hex", start, end); -} - export function toJSON(this: BufferExt) { const type = "Buffer"; const data = Array.from(this); diff --git a/src/js/builtins/ProcessObjectInternals.ts b/src/js/builtins/ProcessObjectInternals.ts index 93b8975de727d..ec6cae506f8b2 100644 --- a/src/js/builtins/ProcessObjectInternals.ts +++ b/src/js/builtins/ProcessObjectInternals.ts @@ -403,3 +403,21 @@ export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array { if (underlyingSource !== undefined) { return $readableStreamToTextDirect(stream, underlyingSource); } + if ($isReadableStreamLocked(stream)) return Promise.$reject($makeTypeError("ReadableStream is locked")); + + const result = $tryUseReadableStreamBufferedFastPath(stream, "text"); + + if (result) { + return result; + } + return $readableStreamIntoText(stream); } @@ -129,19 +139,79 @@ $linkTimeConstant; export function readableStreamToArrayBuffer(stream: ReadableStream): Promise | ArrayBuffer { // this is a direct stream var underlyingSource = $getByIdDirectPrivate(stream, "underlyingSource"); - if (underlyingSource !== undefined) { return $readableStreamToArrayBufferDirect(stream, underlyingSource, false); } + if ($isReadableStreamLocked(stream)) return Promise.$reject($makeTypeError("ReadableStream is locked")); - var result = Bun.readableStreamToArray(stream); - if ($isPromise(result)) { - // `result` is an InternalPromise, which doesn't have a `.then` method - // but `.then` isn't user-overridable, so we can use it safely. - return result.then(x => Bun.concatArrayBuffers(x)); + let result = $tryUseReadableStreamBufferedFastPath(stream, "arrayBuffer"); + + if (result) { + return result; } - return Bun.concatArrayBuffers(result); + result = Bun.readableStreamToArray(stream); + + function toArrayBuffer(result: unknown[]) { + switch (result.length) { + case 0: { + return new ArrayBuffer(0); + } + case 1: { + const view = result[0]; + if (view instanceof ArrayBuffer || view instanceof SharedArrayBuffer) { + return view; + } + + if (ArrayBuffer.isView(view)) { + const buffer = view.buffer; + const byteOffset = view.byteOffset; + const byteLength = view.byteLength; + if (byteOffset === 0 && byteLength === buffer.byteLength) { + return buffer; + } + + return buffer.slice(byteOffset, byteOffset + byteLength); + } + + if (typeof view === "string") { + return new TextEncoder().encode(view); + } + } + default: { + let anyStrings = false; + for (const chunk of result) { + if (typeof chunk === "string") { + anyStrings = true; + break; + } + } + + if (!anyStrings) { + return Bun.concatArrayBuffers(result, false); + } + + const sink = new Bun.ArrayBufferSink(); + sink.start(); + + for (const chunk of result) { + sink.write(chunk); + } + + return sink.end() as Uint8Array; + } + } + } + + if ($isPromise(result)) { + const completedResult = Bun.peek(result); + if (completedResult !== result) { + result = completedResult; + } else { + return result.then(toArrayBuffer); + } + } + return $createFulfilledPromise(toArrayBuffer(result)); } $linkTimeConstant; @@ -152,15 +222,74 @@ export function readableStreamToBytes(stream: ReadableStream): Prom if (underlyingSource !== undefined) { return $readableStreamToArrayBufferDirect(stream, underlyingSource, true); } + if ($isReadableStreamLocked(stream)) return Promise.$reject($makeTypeError("ReadableStream is locked")); + + let result = $tryUseReadableStreamBufferedFastPath(stream, "bytes"); + + if (result) { + return result; + } + + result = Bun.readableStreamToArray(stream); + + function toBytes(result: unknown[]) { + switch (result.length) { + case 0: { + return new Uint8Array(0); + } + case 1: { + const view = result[0]; + if (view instanceof Uint8Array) { + return view; + } + + if (ArrayBuffer.isView(view)) { + return new Uint8Array(view.buffer, view.byteOffset, view.byteLength); + } + + if (view instanceof ArrayBuffer || view instanceof SharedArrayBuffer) { + return new Uint8Array(view); + } + + if (typeof view === "string") { + return new TextEncoder().encode(view); + } + } + default: { + let anyStrings = false; + for (const chunk of result) { + if (typeof chunk === "string") { + anyStrings = true; + break; + } + } + + if (!anyStrings) { + return Bun.concatArrayBuffers(result, true); + } + + const sink = new Bun.ArrayBufferSink(); + sink.start({ asUint8Array: true }); + + for (const chunk of result) { + sink.write(chunk); + } + + return sink.end() as Uint8Array; + } + } + } - var result = Bun.readableStreamToArray(stream); if ($isPromise(result)) { - // `result` is an InternalPromise, which doesn't have a `.then` method - // but `.then` isn't user-overridable, so we can use it safely. - return result.then(x => Bun.concatArrayBuffers(x, Infinity, true)); + const completedResult = Bun.peek(result); + if (completedResult !== result) { + result = completedResult; + } else { + return result.then(toBytes); + } } - return Bun.concatArrayBuffers(result, Infinity, true); + return $createFulfilledPromise(toBytes(result)); } $linkTimeConstant; @@ -168,6 +297,7 @@ export function readableStreamToFormData( stream: ReadableStream, contentType: string | ArrayBuffer | ArrayBufferView, ): Promise { + if ($isReadableStreamLocked(stream)) return Promise.$reject($makeTypeError("ReadableStream is locked")); return Bun.readableStreamToBlob(stream).then(blob => { return FormData.from(blob, contentType); }); @@ -175,12 +305,33 @@ export function readableStreamToFormData( $linkTimeConstant; export function readableStreamToJSON(stream: ReadableStream): unknown { - return Promise.resolve(Bun.readableStreamToText(stream)).then(globalThis.JSON.parse); + if ($isReadableStreamLocked(stream)) return Promise.$reject($makeTypeError("ReadableStream is locked")); + let result = $tryUseReadableStreamBufferedFastPath(stream, "json"); + if (result) { + return result; + } + + let text = Bun.readableStreamToText(stream); + const peeked = Bun.peek(text); + if (peeked !== text) { + try { + return $createFulfilledPromise(globalThis.JSON.parse(peeked)); + } catch (e) { + return Promise.reject(e); + } + } + + return text.then(globalThis.JSON.parse); } $linkTimeConstant; export function readableStreamToBlob(stream: ReadableStream): Promise { - return Promise.resolve(Bun.readableStreamToArray(stream)).then(array => new Blob(array)); + if ($isReadableStreamLocked(stream)) return Promise.$reject($makeTypeError("ReadableStream is locked")); + + return ( + $tryUseReadableStreamBufferedFastPath(stream, "blob") || + Promise.resolve(Bun.readableStreamToArray(stream)).then(array => new Blob(array)) + ); } $linkTimeConstant; diff --git a/src/js/builtins/ReadableStreamDefaultController.ts b/src/js/builtins/ReadableStreamDefaultController.ts index 912cd1acb6f11..7c1c9ace77d6b 100644 --- a/src/js/builtins/ReadableStreamDefaultController.ts +++ b/src/js/builtins/ReadableStreamDefaultController.ts @@ -41,7 +41,6 @@ export function enqueue(this, chunk) { export function error(this, err) { if (!$isReadableStreamDefaultController(this)) throw $makeThisTypeError("ReadableStreamDefaultController", "error"); - $readableStreamDefaultControllerError(this, err); } diff --git a/src/js/builtins/ReadableStreamInternals.ts b/src/js/builtins/ReadableStreamInternals.ts index 2fdc118f343e7..7f95f39ee9b9d 100644 --- a/src/js/builtins/ReadableStreamInternals.ts +++ b/src/js/builtins/ReadableStreamInternals.ts @@ -65,7 +65,7 @@ export function privateInitializeReadableStreamDefaultController(this, stream, u export function readableStreamDefaultControllerError(controller, error) { const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - if ($getByIdDirectPrivate(stream, "state") !== $streamReadable) return; + if (!$isObject(stream) || $getByIdDirectPrivate(stream, "state") !== $streamReadable) return; $putByIdDirectPrivate(controller, "queue", $newQueue()); $readableStreamError(stream, error); @@ -490,6 +490,14 @@ export function pipeToFinalize(pipeState) { else pipeState.promiseCapability.resolve.$call(); } +const enum TeeStateFlags { + canceled1 = 1 << 0, + canceled2 = 1 << 1, + reading = 1 << 2, + closedOrErrored = 1 << 3, + readAgain = 1 << 4, +} + export function readableStreamTee(stream, shouldClone) { $assert($isReadableStream(stream)); $assert(typeof shouldClone === "boolean"); @@ -503,34 +511,41 @@ export function readableStreamTee(stream, shouldClone) { const reader = new $ReadableStreamDefaultReader(stream); const teeState = { - closedOrErrored: false, - canceled1: false, - canceled2: false, + stream, + flags: 0, reason1: undefined, reason2: undefined, + branch1Source: undefined, + branch2Source: undefined, + branch1: undefined, + branch2: undefined, + cancelPromiseCapability: $newPromiseCapability(Promise), }; - teeState.cancelPromiseCapability = $newPromiseCapability(Promise); - const pullFunction = $readableStreamTeePullFunction(teeState, reader, shouldClone); - const branch1Source = {}; - $putByIdDirectPrivate(branch1Source, "pull", pullFunction); - $putByIdDirectPrivate(branch1Source, "cancel", $readableStreamTeeBranch1CancelFunction(teeState, stream)); + const branch1Source = { + $pull: pullFunction, + $cancel: $readableStreamTeeBranch1CancelFunction(teeState, stream), + }; - const branch2Source = {}; - $putByIdDirectPrivate(branch2Source, "pull", pullFunction); - $putByIdDirectPrivate(branch2Source, "cancel", $readableStreamTeeBranch2CancelFunction(teeState, stream)); + const branch2Source = { + $pull: pullFunction, + $cancel: $readableStreamTeeBranch2CancelFunction(teeState, stream), + }; const branch1 = new $ReadableStream(branch1Source); const branch2 = new $ReadableStream(branch2Source); $getByIdDirectPrivate(reader, "closedPromiseCapability").promise.$then(undefined, function (e) { - if (teeState.closedOrErrored) return; + const flags = teeState.flags; + if (flags & TeeStateFlags.closedOrErrored) return; $readableStreamDefaultControllerError(branch1.$readableStreamController, e); $readableStreamDefaultControllerError(branch2.$readableStreamController, e); - teeState.closedOrErrored = true; - if (!teeState.canceled1 || !teeState.canceled2) teeState.cancelPromiseCapability.resolve.$call(); + teeState.flags |= TeeStateFlags.closedOrErrored; + + if (teeState.fllags & (TeeStateFlags.canceled1 | TeeStateFlags.canceled2)) + teeState.cancelPromiseCapability.resolve.$call(); }); // Additional fields compared to the spec, as they are needed within pull/cancel functions. @@ -541,36 +556,76 @@ export function readableStreamTee(stream, shouldClone) { } export function readableStreamTeePullFunction(teeState, reader, shouldClone) { - return function () { - Promise.prototype.$then.$call($readableStreamDefaultReaderRead(reader), function (result) { - $assert($isObject(result)); - $assert(typeof result.done === "boolean"); - if (result.done && !teeState.closedOrErrored) { - if (!teeState.canceled1) $readableStreamDefaultControllerClose(teeState.branch1.$readableStreamController); - if (!teeState.canceled2) $readableStreamDefaultControllerClose(teeState.branch2.$readableStreamController); - teeState.closedOrErrored = true; - if (!teeState.canceled1 || !teeState.canceled2) teeState.cancelPromiseCapability.resolve.$call(); - } - if (teeState.closedOrErrored) return; - if (!teeState.canceled1) - $readableStreamDefaultControllerEnqueue(teeState.branch1.$readableStreamController, result.value); - if (!teeState.canceled2) - $readableStreamDefaultControllerEnqueue( - teeState.branch2.$readableStreamController, - shouldClone ? $structuredCloneForStream(result.value) : result.value, - ); - }); + "use strict"; + + const pullAlgorithm = function () { + if (teeState.flags & TeeStateFlags.reading) { + teeState.flags |= TeeStateFlags.readAgain; + return $Promise.$resolve(); + } + teeState.flags |= TeeStateFlags.reading; + $Promise.prototype.$then.$call( + $readableStreamDefaultReaderRead(reader), + function (result) { + $assert($isObject(result)); + $assert(typeof result.done === "boolean"); + const { done, value } = result; + if (done) { + // close steps. + teeState.flags &= ~TeeStateFlags.reading; + if (!(teeState.flags & TeeStateFlags.canceled1)) + $readableStreamDefaultControllerClose(teeState.branch1.$readableStreamController); + if (!(teeState.flags & TeeStateFlags.canceled2)) + $readableStreamDefaultControllerClose(teeState.branch2.$readableStreamController); + if (!(teeState.flags & TeeStateFlags.canceled1) || !(teeState.flags & TeeStateFlags.canceled2)) + teeState.cancelPromiseCapability.resolve.$call(); + return; + } + // chunk steps. + teeState.flags &= ~TeeStateFlags.readAgain; + let chunk1 = value; + let chunk2 = value; + if (!(teeState.flags & TeeStateFlags.canceled2) && shouldClone) { + try { + chunk2 = $structuredCloneForStream(value); + } catch (e) { + $readableStreamDefaultControllerError(teeState.branch1.$readableStreamController, e); + $readableStreamDefaultControllerError(teeState.branch2.$readableStreamController, e); + $readableStreamCancel(teeState.stream, e).$then( + teeState.cancelPromiseCapability.resolve, + teeState.cancelPromiseCapability.reject, + ); + return; + } + } + if (!(teeState.flags & TeeStateFlags.canceled1)) + $readableStreamDefaultControllerEnqueue(teeState.branch1.$readableStreamController, chunk1); + if (!(teeState.flags & TeeStateFlags.canceled2)) + $readableStreamDefaultControllerEnqueue(teeState.branch2.$readableStreamController, chunk2); + teeState.flags &= ~TeeStateFlags.reading; + + $Promise.$resolve().$then(() => { + if (teeState.flags & TeeStateFlags.readAgain) pullAlgorithm(); + }); + }, + () => { + // error steps. + teeState.flags &= ~TeeStateFlags.reading; + }, + ); + return $Promise.$resolve(); }; + return pullAlgorithm; } export function readableStreamTeeBranch1CancelFunction(teeState, stream) { return function (r) { - teeState.canceled1 = true; + teeState.flags |= TeeStateFlags.canceled1; teeState.reason1 = r; - if (teeState.canceled2) { + if (teeState.flags & TeeStateFlags.canceled2) { $readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).$then( - teeState.cancelPromiseCapability.$resolve, - teeState.cancelPromiseCapability.$reject, + teeState.cancelPromiseCapability.resolve, + teeState.cancelPromiseCapability.reject, ); } return teeState.cancelPromiseCapability.promise; @@ -579,18 +634,19 @@ export function readableStreamTeeBranch1CancelFunction(teeState, stream) { export function readableStreamTeeBranch2CancelFunction(teeState, stream) { return function (r) { - teeState.canceled2 = true; + teeState.flags |= TeeStateFlags.canceled2; teeState.reason2 = r; - if (teeState.canceled1) { + if (teeState.flags & TeeStateFlags.canceled1) { $readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).$then( - teeState.cancelPromiseCapability.$resolve, - teeState.cancelPromiseCapability.$reject, + teeState.cancelPromiseCapability.resolve, + teeState.cancelPromiseCapability.reject, ); } return teeState.cancelPromiseCapability.promise; }; } +$alwaysInline = true; export function isReadableStream(stream) { // Spec tells to return true only if stream has a readableStreamController internal slot. // However, since it is a private slot, it cannot be checked using hasOwnProperty(). @@ -598,6 +654,7 @@ export function isReadableStream(stream) { return $isObject(stream) && $getByIdDirectPrivate(stream, "readableStreamController") !== undefined; } +$alwaysInline = true; export function isReadableStreamDefaultReader(reader) { // Spec tells to return true only if reader has a readRequests internal slot. // However, since it is a private slot, it cannot be checked using hasOwnProperty(). @@ -605,6 +662,7 @@ export function isReadableStreamDefaultReader(reader) { return $isObject(reader) && !!$getByIdDirectPrivate(reader, "readRequests"); } +$alwaysInline = true; export function isReadableStreamDefaultController(controller) { // Spec tells to return true only if controller has an underlyingSource internal slot. // However, since it is a private slot, it cannot be checked using hasOwnProperty(). @@ -617,10 +675,13 @@ export function readDirectStream(stream, sink, underlyingSource) { $putByIdDirectPrivate(stream, "underlyingSource", undefined); $putByIdDirectPrivate(stream, "start", undefined); function close(stream, reason) { - if (reason && underlyingSource?.cancel) { + const cancelFn = underlyingSource?.cancel; + if (cancelFn) { try { - var prom = underlyingSource.cancel(reason); - $markPromiseAsHandled(prom); + var prom = cancelFn.$call(underlyingSource, reason); + if ($isPromise(prom)) { + $markPromiseAsHandled(prom); + } } catch (e) {} underlyingSource = undefined; @@ -694,10 +755,27 @@ export function assignToStream(stream, sink) { export async function readStreamIntoSink(stream, sink, isNative) { var didClose = false; var didThrow = false; + var started = false; + const highWaterMark = $getByIdDirectPrivate(stream, "highWaterMark") || 0; + try { var reader = stream.getReader(); var many = reader.readMany(); + function onSinkClose(stream, reason) { + if (!didThrow && !didClose && stream && stream.$state !== $streamClosed) { + $readableStreamCancel(stream, reason); + } + } + if (many && $isPromise(many)) { + // Some time may pass before this Promise is fulfilled. The sink may + // abort, for example. So we have to start it, if only so that we can + // receive a notification when it closes or cancels. + // https://github.com/oven-sh/bun/issues/6758 + if (isNative) $startDirectStream.$call(sink, stream, undefined, onSinkClose, stream.$asyncContext); + sink.start({ highWaterMark }); + started = true; + many = await many; } if (many.done) { @@ -705,17 +783,11 @@ export async function readStreamIntoSink(stream, sink, isNative) { return sink.end(); } var wroteCount = many.value.length; - const highWaterMark = $getByIdDirectPrivate(stream, "highWaterMark"); - if (isNative) - $startDirectStream.$call( - sink, - stream, - undefined, - () => !didThrow && stream.$state !== $streamClosed && $markPromiseAsHandled(stream.cancel()), - stream.$asyncContext, - ); - sink.start({ highWaterMark: highWaterMark || 0 }); + if (!started) { + if (isNative) $startDirectStream.$call(sink, stream, undefined, onSinkClose, stream.$asyncContext); + sink.start({ highWaterMark }); + } for (var i = 0, values = many.value, length = many.value.length; i < length; i++) { sink.write(values[i]); @@ -742,7 +814,9 @@ export async function readStreamIntoSink(stream, sink, isNative) { try { reader = undefined; const prom = stream.cancel(e); - $markPromiseAsHandled(prom); + if ($isPromise(prom)) { + $markPromiseAsHandled(prom); + } } catch (j) {} if (sink && !didClose) { @@ -905,6 +979,44 @@ export function onReadableStreamDirectControllerClosed(reason) { $throwTypeError("ReadableStreamDirectController is now closed"); } +export function tryUseReadableStreamBufferedFastPath(stream, method) { + // -- Fast path for Blob.prototype.stream(), fetch body streams, and incoming Request body streams -- + const ptr = stream.$bunNativePtr; + if ( + // only available on native streams + ptr && + // don't even attempt it if the stream was used in some way + !$isReadableStreamDisturbed(stream) && + // feature-detect if supported + $isCallable(ptr[method]) + ) { + const promise = ptr[method](); + // if it throws, let it throw without setting $disturbed + stream.$disturbed = true; + + // Clear the lazy load function. + $putByIdDirectPrivate(stream, "start", undefined); + $putByIdDirectPrivate(stream, "reader", {}); + + if (Bun.peek.status(promise) === "fulfilled") { + stream.$reader = undefined; + $readableStreamCloseIfPossible(stream); + return promise; + } + + return promise + .catch(e => { + stream.$reader = undefined; + $readableStreamCancel(stream, e); + return Promise.$reject(e); + }) + .finally(() => { + stream.$reader = undefined; + $readableStreamCloseIfPossible(stream); + }); + } +} + export function onCloseDirectStream(reason) { var stream = this.$controlledReadableStream; if (!stream || $getByIdDirectPrivate(stream, "state") !== $streamReadable) return; @@ -1308,7 +1420,6 @@ export function readableStreamDefaultControllerCallPullIfNeeded(controller) { $assert(!$getByIdDirectPrivate(controller, "pullAgain")); $putByIdDirectPrivate(controller, "pulling", true); - $getByIdDirectPrivate(controller, "pullAlgorithm") .$call(undefined) .$then( @@ -1326,9 +1437,16 @@ export function readableStreamDefaultControllerCallPullIfNeeded(controller) { ); } +$alwaysInline = true; export function isReadableStreamLocked(stream) { $assert($isReadableStream(stream)); - return !!$getByIdDirectPrivate(stream, "reader") || stream.$bunNativePtr === -1; + return ( + // Case 1. Is there a reader actively using it? + !!$getByIdDirectPrivate(stream, "reader") || + // Case 2. Has the native reader been released? + // Case 3. Has it been converted into a Node.js NativeReadable? + stream.$bunNativePtr === -1 + ); } export function readableStreamDefaultControllerGetDesiredSize(controller) { @@ -1341,6 +1459,7 @@ export function readableStreamDefaultControllerGetDesiredSize(controller) { return $getByIdDirectPrivate(controller, "strategy").highWaterMark - $getByIdDirectPrivate(controller, "queue").size; } +$alwaysInline = true; export function readableStreamReaderGenericCancel(reader, reason) { const stream = $getByIdDirectPrivate(reader, "ownerReadableStream"); $assert(!!stream); @@ -1366,6 +1485,7 @@ export function readableStreamCancel(stream, reason) { $throwTypeError("ReadableStreamController has no cancel or close method"); } +$alwaysInline = true; export function readableStreamDefaultControllerCancel(controller, reason) { $putByIdDirectPrivate(controller, "queue", $newQueue()); return $getByIdDirectPrivate(controller, "cancelAlgorithm").$call(undefined, reason); @@ -1551,8 +1671,20 @@ export function readableStreamFromAsyncIterator(target, fn) { cancelled = true; if (iter) { - iter.throw?.((reason ||= new DOMException("ReadableStream has been cancelled", "AbortError"))); + const thisIter = iter; iter = undefined; + if (reason) { + // We return the value so that the caller can await it. + return thisIter.throw?.(reason); + } else { + // undefined === Abort. + // + // We don't want to throw here because it will almost + // inevitably become an uncatchable exception. So instead, we call the + // synthetic return method if it exists to signal that the stream is + // done. + return thisIter?.return?.(); + } } }, @@ -1566,6 +1698,7 @@ export function readableStreamFromAsyncIterator(target, fn) { try { while (!cancelled && !done) { const promise = iter.next(controller); + if (cancelled) { return; } @@ -1595,10 +1728,11 @@ export function readableStreamFromAsyncIterator(target, fn) { } finally { clearImmediate(immediateTask); immediateTask = undefined; + // "iter" will be undefined if the stream was closed above. // Stream was closed before we tried writing to it. if (closingError?.code === "ERR_INVALID_THIS") { - await iter.return?.(); + await iter?.return?.(); return; } @@ -1611,7 +1745,9 @@ export function readableStreamFromAsyncIterator(target, fn) { } } else { await controller.end(); - await iter.return?.(); + if (iter) { + await iter.return?.(); + } } iter = undefined; } @@ -1819,18 +1955,22 @@ export function readableStreamIntoArray(stream) { var manyResult = reader.readMany(); async function processManyResult(result) { - if (result.done) { - return []; - } + let { done, value } = result; + var chunks = value || []; - var chunks = result.value || []; + while (!done) { + var thisResult = reader.readMany(); + if ($isPromise(thisResult)) { + thisResult = await thisResult; + } - while (true) { - var thisResult = await reader.read(); - if (thisResult.done) { - break; + ({ done, value = [] } = thisResult); + const length = value.length || 0; + if (length > 1) { + chunks = chunks.concat(value); + } else if (length === 1) { + chunks.push(value[0]); } - chunks = chunks.concat(thisResult.value); } return chunks; @@ -2002,7 +2142,10 @@ export function readableStreamDefineLazyIterators(prototype) { reader.releaseLock(); if (!preventCancel && !$isReadableStreamLocked(stream)) { - stream.cancel(deferredError); + const promise = stream.cancel(deferredError); + if (Bun.peek.status(promise) === "rejected") { + $markPromiseAsHandled(promise); + } } if (deferredError) { diff --git a/src/js/builtins/TextDecoderStream.ts b/src/js/builtins/TextDecoderStream.ts new file mode 100644 index 0000000000000..2a5f1e528d92a --- /dev/null +++ b/src/js/builtins/TextDecoderStream.ts @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export function initializeTextDecoderStream() { + const label = arguments.length >= 1 ? arguments[0] : "utf-8"; + const options = arguments.length >= 2 ? arguments[1] : {}; + + const startAlgorithm = () => { + return Promise.$resolve(); + }; + const transformAlgorithm = chunk => { + const decoder = $getByIdDirectPrivate(this, "textDecoder"); + let buffer; + try { + buffer = decoder.decode(chunk, { stream: true }); + } catch (e) { + return Promise.$reject(e); + } + if (buffer) { + const transformStream = $getByIdDirectPrivate(this, "textDecoderStreamTransform"); + const controller = $getByIdDirectPrivate(transformStream, "controller"); + $transformStreamDefaultControllerEnqueue(controller, buffer); + } + return Promise.$resolve(); + }; + const flushAlgorithm = () => { + const decoder = $getByIdDirectPrivate(this, "textDecoder"); + let buffer; + try { + buffer = decoder.decode(undefined, { stream: false }); + } catch (e) { + return Promise.$reject(e); + } + if (buffer) { + const transformStream = $getByIdDirectPrivate(this, "textDecoderStreamTransform"); + const controller = $getByIdDirectPrivate(transformStream, "controller"); + $transformStreamDefaultControllerEnqueue(controller, buffer); + } + return Promise.$resolve(); + }; + + const transform = $createTransformStream(startAlgorithm, transformAlgorithm, flushAlgorithm); + $putByIdDirectPrivate(this, "textDecoderStreamTransform", transform); + + const fatal = !!options.fatal; + const ignoreBOM = !!options.ignoreBOM; + const decoder = new TextDecoder(label, { fatal, ignoreBOM }); + + $putByIdDirectPrivate(this, "fatal", fatal); + $putByIdDirectPrivate(this, "ignoreBOM", ignoreBOM); + $putByIdDirectPrivate(this, "encoding", decoder.encoding); + $putByIdDirectPrivate(this, "textDecoder", decoder); + + return this; +} + +$getter; +export function encoding() { + if (!$getByIdDirectPrivate(this, "textDecoderStreamTransform")) + throw $makeThisTypeError("TextDecoderStream", "encoding"); + + return $getByIdDirectPrivate(this, "encoding"); +} + +$getter; +export function fatal() { + if (!$getByIdDirectPrivate(this, "textDecoderStreamTransform")) + throw $makeThisTypeError("TextDecoderStream", "fatal"); + + return $getByIdDirectPrivate(this, "fatal"); +} + +$getter; +export function ignoreBOM() { + if (!$getByIdDirectPrivate(this, "textDecoderStreamTransform")) + throw $makeThisTypeError("TextDecoderStream", "ignoreBOM"); + + return $getByIdDirectPrivate(this, "ignoreBOM"); +} + +$getter; +export function readable() { + const transform = $getByIdDirectPrivate(this, "textDecoderStreamTransform"); + if (!transform) throw $makeThisTypeError("TextDecoderStream", "readable"); + + return $getByIdDirectPrivate(transform, "readable"); +} + +$getter; +export function writable() { + const transform = $getByIdDirectPrivate(this, "textDecoderStreamTransform"); + if (!transform) throw $makeThisTypeError("TextDecoderStream", "writable"); + + return $getByIdDirectPrivate(transform, "writable"); +} diff --git a/src/js/builtins/TextEncoderStream.ts b/src/js/builtins/TextEncoderStream.ts new file mode 100644 index 0000000000000..4aa0c895dd1c9 --- /dev/null +++ b/src/js/builtins/TextEncoderStream.ts @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export function initializeTextEncoderStream() { + const startAlgorithm = () => { + return Promise.$resolve(); + }; + const transformAlgorithm = chunk => { + const encoder = $getByIdDirectPrivate(this, "textEncoderStreamEncoder"); + try { + var buffer = encoder.encode(chunk); + } catch (e) { + return Promise.$reject(e); + } + if (buffer.length) { + const transformStream = $getByIdDirectPrivate(this, "textEncoderStreamTransform"); + const controller = $getByIdDirectPrivate(transformStream, "controller"); + $transformStreamDefaultControllerEnqueue(controller, buffer); + } + return Promise.$resolve(); + }; + const flushAlgorithm = () => { + const encoder = $getByIdDirectPrivate(this, "textEncoderStreamEncoder"); + const buffer = encoder.flush(); + if (buffer.length) { + const transformStream = $getByIdDirectPrivate(this, "textEncoderStreamTransform"); + const controller = $getByIdDirectPrivate(transformStream, "controller"); + $transformStreamDefaultControllerEnqueue(controller, buffer); + } + return Promise.$resolve(); + }; + + const transform = $createTransformStream(startAlgorithm, transformAlgorithm, flushAlgorithm); + $putByIdDirectPrivate(this, "textEncoderStreamTransform", transform); + $putByIdDirectPrivate(this, "textEncoderStreamEncoder", new $TextEncoderStreamEncoder()); + + return this; +} + +$getter; +export function encoding() { + if (!$getByIdDirectPrivate(this, "textEncoderStreamTransform")) + throw $makeThisTypeError("TextEncoderStream", "encoding"); + + return "utf-8"; +} + +$getter; +export function readable() { + const transform = $getByIdDirectPrivate(this, "textEncoderStreamTransform"); + if (!transform) throw $makeThisTypeError("TextEncoderStream", "readable"); + + return $getByIdDirectPrivate(transform, "readable"); +} + +$getter; +export function writable() { + const transform = $getByIdDirectPrivate(this, "textEncoderStreamTransform"); + if (!transform) throw $makeThisTypeError("TextEncoderStream", "writable"); + + return $getByIdDirectPrivate(transform, "writable"); +} diff --git a/src/js/bun/ffi.ts b/src/js/bun/ffi.ts index 17dfcb0217f86..f6978c0966f9b 100644 --- a/src/js/bun/ffi.ts +++ b/src/js/bun/ffi.ts @@ -55,6 +55,8 @@ const FFIType = { function: 17, callback: 17, fn: 17, + napi_env: 18, + napi_value: 19, }; const suffix = process.platform === "win32" ? "dll" : process.platform === "darwin" ? "dylib" : "so"; @@ -153,7 +155,7 @@ Object.defineProperty(globalThis, "__GlobalBunCString", { configurable: false, }); -const ffiWrappers = new Array(18); +const ffiWrappers = new Array(20); var char = "val|0"; ffiWrappers.fill(char); @@ -255,15 +257,15 @@ ffiWrappers[FFIType.uint16_t] = `{ ffiWrappers[FFIType.double] = `{ if (typeof val === "bigint") { if (val.valueOf() < BigInt(Number.MAX_VALUE)) { - return Math.abs(Number(val).valueOf()) + 0.00000000000001 - 0.00000000000001; + return Math.abs(Number(val).valueOf()) + (0.00 - 0.00); } } if (!val) { - return 0 + 0.00000000000001 - 0.00000000000001; + return 0 + (0.00 - 0.00); } - return val + 0.00000000000001 - 0.00000000000001; + return val + (0.00 - 0.00); }`; ffiWrappers[FFIType.float] = ffiWrappers[10] = `{ @@ -399,7 +401,9 @@ const native = { }, }; -function dlopen(path, options) { +const ccFn = $newZigFunction("ffi.zig", "Bun__FFI__cc", 1); + +function normalizePath(path) { if (typeof path === "string" && path?.startsWith?.("file:")) { // import.meta.url returns a file: URL // https://github.com/oven-sh/bun/issues/10304 @@ -416,6 +420,12 @@ function dlopen(path, options) { } } + return path; +} + +function dlopen(path, options) { + path = normalizePath(path); + const result = nativeDLOpen(path, options); if (result instanceof Error) throw result; @@ -446,6 +456,54 @@ function dlopen(path, options) { return result; } +function cc(options) { + if (!$isObject(options)) { + throw new Error("Expected options to be an object"); + } + + let path = options?.source; + if (!path) { + throw new Error("Expected source to be a string to a file path"); + } + if ($isJSArray(path)) { + for (let i = 0; i < path.length; i++) { + path[i] = normalizePath(path[i]); + } + } else { + path = normalizePath(path); + } + options.source = path; + + const result = ccFn(options); + if (result instanceof Error) throw result; + + for (let key in result.symbols) { + var symbol = result.symbols[key]; + if (options[key]?.args?.length || FFIType[options[key]?.returns as string] === FFIType.cstring) { + result.symbols[key] = FFIBuilder( + options[key].args ?? [], + options[key].returns ?? FFIType.void, + symbol, + // in stacktraces: + // instead of + // "/usr/lib/sqlite3.so" + // we want + // "sqlite3_get_version() - sqlit3.so" + path.includes("/") ? `${key} (${path.split("/").pop()})` : `${key} (${path})`, + ); + } else { + // consistentcy + result.symbols[key].native = result.symbols[key]; + } + } + + // Bind it because it's a breaking change to not do so + // Previously, it didn't need to be bound + result.close = result.close.bind(result); + + return result; +} + function linkSymbols(options) { const result = nativeLinkSymbols(options); @@ -503,4 +561,5 @@ export default { toArrayBuffer, toBuffer, viewSource, + cc, }; diff --git a/src/js/bun/sql.ts b/src/js/bun/sql.ts new file mode 100644 index 0000000000000..7c1c275b4431a --- /dev/null +++ b/src/js/bun/sql.ts @@ -0,0 +1,426 @@ +const cmds = ["", "INSERT", "DELETE", "UPDATE", "MERGE", "SELECT", "MOVE", "FETCH", "COPY"]; + +const PublicArray = globalThis.Array; + +class SQLResultArray extends PublicArray { + static [Symbol.toStringTag] = "SQLResults"; + + statement; + command; + count; +} + +const queryStatus_active = 1 << 1; +const queryStatus_cancelled = 1 << 2; +const queryStatus_error = 1 << 3; +const queryStatus_executed = 1 << 4; + +const rawMode_values = 1; +const rawMode_objects = 2; + +const _resolve = Symbol("resolve"); +const _reject = Symbol("reject"); +const _handle = Symbol("handle"); +const _run = Symbol("run"); +const _queryStatus = Symbol("status"); +const _handler = Symbol("handler"); +const PublicPromise = Promise; + +const { + createConnection: _createConnection, + createQuery, + PostgresSQLConnection, + init, +} = $zig("postgres.zig", "createBinding"); + +class Query extends PublicPromise { + [_resolve]; + [_reject]; + [_handle]; + [_handler]; + [_queryStatus] = 0; + + constructor(handle, handler) { + var resolve_, reject_; + super((resolve, reject) => { + resolve_ = resolve; + reject_ = reject; + }); + this[_resolve] = resolve_; + this[_reject] = reject_; + this[_handle] = handle; + this[_handler] = handler; + this[_queryStatus] = handle ? 0 : queryStatus_cancelled; + } + + async [_run]() { + const { [_handle]: handle, [_handler]: handler, [_queryStatus]: status } = this; + + if (status & (queryStatus_executed | queryStatus_cancelled)) { + return; + } + + this[_queryStatus] |= queryStatus_executed; + await 1; + return handler(this, handle); + } + + get active() { + return (this[_queryStatus] & queryStatus_active) !== 0; + } + + set active(value) { + const status = this[_queryStatus]; + if (status & (queryStatus_cancelled | queryStatus_error)) { + return; + } + + if (value) { + this[_queryStatus] |= queryStatus_active; + } else { + this[_queryStatus] &= ~queryStatus_active; + } + } + + get cancelled() { + return (this[_queryStatus] & queryStatus_cancelled) !== 0; + } + + resolve(x) { + this[_queryStatus] &= ~queryStatus_active; + this[_handle].done(); + return this[_resolve](x); + } + + reject(x) { + this[_queryStatus] &= ~queryStatus_active; + this[_queryStatus] |= queryStatus_error; + this[_handle].done(); + + return this[_reject](x); + } + + cancel() { + var status = this[_queryStatus]; + if (status & queryStatus_cancelled) { + return this; + } + this[_queryStatus] |= queryStatus_cancelled; + + if (status & queryStatus_executed) { + this[_handle].cancel(); + } + + return this; + } + + execute() { + this[_run](); + return this; + } + + raw() { + this[_handle].raw = rawMode_objects; + return this; + } + + values() { + this[_handle].raw = rawMode_values; + return this; + } + + then() { + this[_run](); + return super.$then.$apply(this, arguments); + } + + catch() { + this[_run](); + return super.catch.$apply(this, arguments); + } + + finally() { + this[_run](); + return super.finally.$apply(this, arguments); + } +} +Object.defineProperty(Query, Symbol.species, { value: PublicPromise }); +Object.defineProperty(Query, Symbol.toStringTag, { value: "Query" }); +init( + function (query, result, commandTag, count) { + $assert(result instanceof SQLResultArray, "Invalid result array"); + if (typeof commandTag === "string") { + if (commandTag.length > 0) { + result.command = commandTag; + } + } else { + result.command = cmds[commandTag]; + } + + result.count = count || 0; + + try { + query.resolve(result); + } catch (e) { + console.log(e); + } + }, + function (query, reject) { + try { + query.reject(reject); + } catch (e) { + console.log(e); + } + }, +); + +function createConnection({ hostname, port, username, password, tls, query, database }, onConnected, onClose) { + return _createConnection( + hostname, + Number(port), + username || "", + password || "", + database || "", + tls || null, + query || "", + onConnected, + onClose, + ); +} + +function normalizeStrings(strings) { + if ($isJSArray(strings)) { + const count = strings.length; + if (count === 0) { + return ""; + } + + var out = strings[0]; + for (var i = 1; i < count; i++) { + out += "$" + i; + out += strings[i]; + } + return out; + } + + return strings + ""; +} + +function loadOptions(o) { + var hostname, port, username, password, database, tls, url, query, adapter; + const env = Bun.env; + + if (o === undefined || (typeof o === "string" && o.length === 0)) { + const urlString = env.POSTGRES_URL || env.DATABASE_URL || env.PGURL || env.PG_URL; + if (urlString) { + url = new URL(urlString); + o = {}; + } + } else if (o && typeof o === "object") { + if (o instanceof URL) { + url = o; + } else if (o?.url) { + const _url = o.url; + if (typeof _url === "string") { + url = new URL(_url); + } else if (_url && typeof _url === "object" && _url instanceof URL) { + url = _url; + } + } + } else if (typeof o === "string") { + url = new URL(o); + } + + if (url) { + ({ hostname, port, username, password, protocol: adapter } = o = url); + if (adapter[adapter.length - 1] === ":") { + adapter = adapter.slice(0, -1); + } + const queryObject = url.searchParams.toJSON(); + query = ""; + for (const key in queryObject) { + query += `${encodeURIComponent(key)}=${encodeURIComponent(queryObject[key])} `; + } + query = query.trim(); + } + + if (!o) { + o = {}; + } + + hostname ||= o.hostname || o.host || env.PGHOST || "localhost"; + port ||= Number(o.port || env.PGPORT || 5432); + username ||= o.username || o.user || env.PGUSERNAME || env.PGUSER || env.USER || env.USERNAME || "postgres"; + database ||= o.database || o.db || (url?.pathname ?? "").slice(1) || env.PGDATABASE || username; + password ||= o.password || o.pass || env.PGPASSWORD || ""; + tls ||= o.tls || o.ssl; + adapter ||= o.adapter || "postgres"; + + port = Number(port); + + if (!Number.isSafeInteger(port) || port < 1 || port > 65535) { + throw new Error(`Invalid port: ${port}`); + } + + if (adapter && !(adapter === "postgres" || adapter === "postgresql")) { + throw new Error(`Unsupported adapter: ${adapter}. Only \"postgres\" is supported for now`); + } + + return { hostname, port, username, password, database, tls, query }; +} + +function SQL(o) { + var connection, + connected = false, + connecting = false, + closed = false, + onConnect: any[] = [], + connectionInfo = loadOptions(o); + + function connectedHandler(query, handle, err) { + if (err) { + return query.reject(err); + } + + if (!connected) { + return query.reject(new Error("Not connected")); + } + + if (query.cancelled) { + return query.reject(new Error("Query cancelled")); + } + + handle.run(connection, query); + } + + function pendingConnectionHandler(query, handle) { + onConnect.push(err => connectedHandler(query, handle, err)); + if (!connecting) { + connecting = true; + connection = createConnection(connectionInfo, onConnected, onClose); + } + } + + function closedConnectionHandler(query, handle) { + query.reject(new Error("Connection closed")); + } + + function onConnected(err, result) { + connected = !err; + for (const handler of onConnect) { + handler(err); + } + onConnect = []; + } + + function onClose(err) { + closed = true; + onConnected(err, undefined); + } + + function connectedSQL(strings, values) { + return new Query(createQuery(normalizeStrings(strings), values, new SQLResultArray()), connectedHandler); + } + + function closedSQL(strings, values) { + return new Query(undefined, closedConnectionHandler); + } + + function pendingSQL(strings, values) { + return new Query(createQuery(normalizeStrings(strings), values, new SQLResultArray()), pendingConnectionHandler); + } + + function sql(strings, ...values) { + if (closed) { + return closedSQL(strings, values); + } + + if (connected) { + return connectedSQL(strings, values); + } + + return pendingSQL(strings, values); + } + + sql.connect = () => { + if (closed) { + return Promise.reject(new Error("Connection closed")); + } + + if (connected) { + return Promise.resolve(sql); + } + + var { resolve, reject, promise } = Promise.withResolvers(); + onConnect.push(err => (err ? reject(err) : resolve(sql))); + if (!connecting) { + connecting = true; + connection = createConnection(connectionInfo, onConnected, onClose); + } + + return promise; + }; + + sql.close = () => { + if (closed) { + return Promise.resolve(); + } + + var { resolve, promise } = Promise.withResolvers(); + onConnect.push(resolve); + connection.close(); + return promise; + }; + + sql[Symbol.asyncDispose] = () => sql.close(); + + sql.flush = () => { + if (closed || !connected) { + return; + } + + connection.flush(); + }; + sql.options = connectionInfo; + + sql.then = () => { + if (closed) { + return Promise.reject(new Error("Connection closed")); + } + + if (connected) { + return Promise.resolve(sql); + } + + const { resolve, reject, promise } = Promise.withResolvers(); + onConnect.push(err => (err ? reject(err) : resolve(sql))); + if (!connecting) { + connecting = true; + connection = createConnection(connectionInfo, onConnected, onClose); + } + + return promise; + }; + + return sql; +} + +var lazyDefaultSQL; +var defaultSQLObject = function sql(strings, ...values) { + if (!lazyDefaultSQL) { + lazyDefaultSQL = SQL(undefined); + Object.assign(defaultSQLObject, lazyDefaultSQL); + exportsObject.default = exportsObject.sql = lazyDefaultSQL; + } + return lazyDefaultSQL(strings, ...values); +}; + +var exportsObject = { + sql: defaultSQLObject, + default: defaultSQLObject, + SQL, + Query, + postgres: SQL, +}; + +export default exportsObject; diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index 0e22d5b775da1..992286bb4a276 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -7,11 +7,13 @@ /// -export const quickAndDirtyJavaScriptSyntaxHighlighter = $newZigFunction( - "fmt.zig", - "QuickAndDirtyJavaScriptSyntaxHighlighter.jsFunctionSyntaxHighlight", - 2, -) as (code: string) => string; +const fmtBinding = $newZigFunction("fmt.zig", "fmt_js_test_bindings.jsFunctionStringFormatter", 2) as ( + code: string, + id: number, +) => string; + +export const quickAndDirtyJavaScriptSyntaxHighlighter = (code: string) => fmtBinding(code, 0); +export const escapePowershell = (code: string) => fmtBinding(code, 1); export const TLSBinding = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); @@ -23,9 +25,12 @@ export const patchInternals = { makeDiff: $newZigFunction("patch.zig", "TestingAPIs.makeDiff", 2), }; +const shellLex = $newZigFunction("shell.zig", "TestingAPIs.shellLex", 2); +const shellParse = $newZigFunction("shell.zig", "TestingAPIs.shellParse", 2); + export const shellInternals = { - lex: (a, ...b) => $newZigFunction("shell.zig", "TestingAPIs.shellLex", 2)(a.raw, b), - parse: (a, ...b) => $newZigFunction("shell.zig", "TestingAPIs.shellParse", 2)(a.raw, b), + lex: (a, ...b) => shellLex(a.raw, b), + parse: (a, ...b) => shellParse(a.raw, b), /** * Checks if the given builtin is disabled on the current platform * @@ -37,12 +42,27 @@ export const shellInternals = { builtinDisabled: $newZigFunction("shell.zig", "TestingAPIs.disabledOnThisPlatform", 1), }; +export const iniInternals = { + parse: $newZigFunction("ini.zig", "IniTestingAPIs.parse", 1), + // loadNpmrc: ( + // src: string, + // env?: Record, + // ): { + // default_registry_url: string; + // default_registry_token: string; + // default_registry_username: string; + // default_registry_password: string; + // } => $newZigFunction("ini.zig", "IniTestingAPIs.loadNpmrcFromJS", 2)(src, env), + loadNpmrc: $newZigFunction("ini.zig", "IniTestingAPIs.loadNpmrcFromJS", 2), +}; + export const crash_handler = $zig("crash_handler.zig", "js_bindings.generate") as { getMachOImageZeroOffset: () => number; segfault: () => void; panic: () => void; rootError: () => void; outOfMemory: () => void; + raiseIgnoringPanicHandler: () => void; }; export const upgrade_test_helpers = $zig("upgrade_command.zig", "upgrade_js_bindings.generate") as { @@ -63,3 +83,46 @@ export const nativeFrameForTesting: (callback: () => void) => void = $cpp( "CallSite.cpp", "createNativeFrameForTesting", ); + +// Linux-only. Create an in-memory file descriptor with a preset size. +// You should call fs.closeSync(fd) when you're done with it. +export const memfd_create: (size: number) => number = $newZigFunction( + "node_fs_binding.zig", + "createMemfdForTesting", + 1, +); + +export const setSyntheticAllocationLimitForTesting: (limit: number) => number = $newZigFunction( + "javascript.zig", + "Bun__setSyntheticAllocationLimitForTesting", + 1, +); + +export const npm_manifest_test_helpers = $zig("npm.zig", "PackageManifest.bindings.generate") as { + /** + * Returns the parsed manifest file. Currently only returns an array of available versions. + */ + parseManifest: (manifestFileName: string, registryUrl: string) => any; +}; + +// Like npm-package-arg, sort of https://www.npmjs.com/package/npm-package-arg +export const npa: (name: string) => Dependency = $newZigFunction("dependency.zig", "fromJS", 1); + +export const npmTag: ( + name: string, +) => undefined | "npm" | "dist_tag" | "tarball" | "folder" | "symlink" | "workspace" | "git" | "github" = + $newZigFunction("dependency.zig", "Version.Tag.inferFromJS", 1); + +export const readTarball: (tarball: string) => any = $newZigFunction("pack_command.zig", "bindings.jsReadTarball", 1); + +export const isArchitectureMatch: (architecture: string[]) => boolean = $newZigFunction( + "npm.zig", + "Architecture.jsFunctionArchitectureIsMatch", + 1, +); + +export const isOperatingSystemMatch: (operatingSystem: string[]) => boolean = $newZigFunction( + "npm.zig", + "OperatingSystem.jsFunctionOperatingSystemIsMatch", + 1, +); diff --git a/src/js/internal/cluster/RoundRobinHandle.ts b/src/js/internal/cluster/RoundRobinHandle.ts new file mode 100644 index 0000000000000..2184cc04680b1 --- /dev/null +++ b/src/js/internal/cluster/RoundRobinHandle.ts @@ -0,0 +1,139 @@ +const net = require("node:net"); +const { append, init, isEmpty, peek, remove } = require("internal/linkedlist"); +const { kHandle } = require("internal/shared"); + +const sendHelper = $newZigFunction("node_cluster_binding.zig", "sendHelperPrimary", 4); + +const ArrayIsArray = Array.isArray; + +const UV_TCP_IPV6ONLY = 1; +const assert_fail = () => { + throw new Error("ERR_INTERNAL_ASSERTION"); +}; + +export default class RoundRobinHandle { + key; + all; + free; + handles; + handle; + server; + + constructor(key, address, { port, fd, flags, backlog, readableAll, writableAll }) { + this.key = key; + this.all = new Map(); + this.free = new Map(); + this.handles = init({ __proto__: null }); + this.handle = null; + this.server = net.createServer(assert_fail); + + if (fd >= 0) this.server.listen({ fd, backlog }); + else if (port >= 0) { + this.server.listen({ + port, + host: address, + // Currently, net module only supports `ipv6Only` option in `flags`. + ipv6Only: Boolean(flags & UV_TCP_IPV6ONLY), + backlog, + }); + } else + this.server.listen({ + path: address, + backlog, + readableAll, + writableAll, + }); // UNIX socket path. + this.server.once("listening", () => { + this.handle = this.server._handle; + this.handle.onconnection = (err, handle) => this.distribute(err, handle); + this.server._handle = null; + this.server = null; + }); + } + + add(worker, send) { + // $assert(this.all.has(worker.id) === false); + this.all.set(worker.id, worker); + + const done = () => { + if (this.handle.getsockname) { + const out = {}; + this.handle.getsockname(out); + // TODO(bnoordhuis) Check err. + send(null, { sockname: out }, null); + } else { + send(null, null, null); // UNIX socket. + } + + this.handoff(worker); // In case there are connections pending. + }; + + if (this.server === null) return done(); + + // Still busy binding. + this.server.once("listening", done); + this.server.once("error", err => { + send(err.errno, null); + }); + } + + remove(worker) { + const existed = this.all.delete(worker.id); + + if (!existed) return false; + + this.free.delete(worker.id); + + if (this.all.size !== 0) return false; + + while (!isEmpty(this.handles)) { + const handle = peek(this.handles); + handle.close(); + remove(handle); + } + + this.handle.close(); + this.handle = null; + return true; + } + + distribute(err, handle) { + // If `accept` fails just skip it (handle is undefined) + if (err) { + return; + } + append(this.handles, handle); + // eslint-disable-next-line node-core/no-array-destructuring + const [workerEntry] = this.free; // this.free is a SafeMap + + if (ArrayIsArray(workerEntry)) { + const { 0: workerId, 1: worker } = workerEntry; + this.free.delete(workerId); + this.handoff(worker); + } + } + + handoff(worker) { + if (!this.all.has(worker.id)) { + return; // Worker is closing (or has closed) the server. + } + + const handle = peek(this.handles); + + if (handle === null) { + this.free.set(worker.id, worker); // Add to ready queue again. + return; + } + + remove(handle); + + const message = { act: "newconn", key: this.key }; + + sendHelper(worker.process[kHandle], message, handle, reply => { + if (reply.accepted) handle.close(); + else this.distribute(0, handle); // Worker is shutting down. Send to another. + + this.handoff(worker); + }); + } +} diff --git a/src/js/internal/cluster/Worker.ts b/src/js/internal/cluster/Worker.ts new file mode 100644 index 0000000000000..14ff825c4dbd2 --- /dev/null +++ b/src/js/internal/cluster/Worker.ts @@ -0,0 +1,44 @@ +const EventEmitter = require("node:events"); + +const ObjectFreeze = Object.freeze; +const ObjectSetPrototypeOf = Object.setPrototypeOf; + +const kEmptyObject = ObjectFreeze({ __proto__: null }); + +function Worker(options) { + if (!(this instanceof Worker)) return new Worker(options); + + EventEmitter.$apply(this, []); + + if (options === null || typeof options !== "object") options = kEmptyObject; + + this.exitedAfterDisconnect = undefined; + + this.state = options.state || "none"; + this.id = options.id | 0; + + if (options.process) { + this.process = options.process; + this.process.on("error", (code, signal) => this.emit("error", code, signal)); + this.process.on("message", (message, handle) => this.emit("message", message, handle)); + } +} +Worker.prototype = Object.create(EventEmitter.prototype); + +Worker.prototype.kill = function () { + this.destroy.$apply(this, arguments); +}; + +Worker.prototype.send = function () { + return this.process.send.$apply(this.process, arguments); +}; + +Worker.prototype.isDead = function () { + return this.process.exitCode != null || this.process.signalCode != null; +}; + +Worker.prototype.isConnected = function () { + return this.process.connected; +}; + +export default Worker; diff --git a/src/js/internal/cluster/child.ts b/src/js/internal/cluster/child.ts new file mode 100644 index 0000000000000..c1e6ebdb38b18 --- /dev/null +++ b/src/js/internal/cluster/child.ts @@ -0,0 +1,281 @@ +const EventEmitter = require("node:events"); +const Worker = require("internal/cluster/Worker"); +const path = require("node:path"); + +const sendHelper = $newZigFunction("node_cluster_binding.zig", "sendHelperChild", 3); +const onInternalMessage = $newZigFunction("node_cluster_binding.zig", "onInternalMessageChild", 2); + +const FunctionPrototype = Function.prototype; +const ArrayPrototypeJoin = Array.prototype.join; +const ObjectAssign = Object.assign; + +const cluster = new EventEmitter(); +const handles = new Map(); +const indexes = new Map(); +const noop = FunctionPrototype; +const TIMEOUT_MAX = 2 ** 31 - 1; +const kNoFailure = 0; +const owner_symbol = Symbol("owner_symbol"); + +export default cluster; + +cluster.isWorker = true; +cluster.isMaster = false; // Deprecated alias. Must be same as isPrimary. +cluster.isPrimary = false; +cluster.worker = null; +cluster.Worker = Worker; + +cluster._setupWorker = function () { + const worker = new Worker({ + id: +process.env.NODE_UNIQUE_ID | 0, + process: process, + state: "online", + }); + + cluster.worker = worker; + + process.once("disconnect", () => { + worker.emit("disconnect"); + + if (!worker.exitedAfterDisconnect) { + // Unexpected disconnect, primary exited, or some such nastiness, so + // worker exits immediately. + process.exit(kNoFailure); + } + }); + + onInternalMessage(worker, onmessage); + send({ act: "online" }); + + function onmessage(message, handle) { + if (message.act === "newconn") onconnection(message, handle); + else if (message.act === "disconnect") worker._disconnect(true); + } +}; + +// `obj` is a net#Server or a dgram#Socket object. +cluster._getServer = function (obj, options, cb) { + let address = options.address; + + // Resolve unix socket paths to absolute paths + if (options.port < 0 && typeof address === "string" && process.platform !== "win32") address = path.resolve(address); + + const indexesKey = ArrayPrototypeJoin.$call([address, options.port, options.addressType, options.fd], ":"); + + let indexSet = indexes.get(indexesKey); + + if (indexSet === undefined) { + indexSet = { nextIndex: 0, set: new Set() }; + indexes.set(indexesKey, indexSet); + } + const index = indexSet.nextIndex++; + indexSet.set.add(index); + + const message = { + act: "queryServer", + index, + data: null, + ...options, + }; + + message.address = address; + + // Set custom data on handle (i.e. tls tickets key) + if (obj._getServerData) message.data = obj._getServerData(); + + send(message, (reply, handle) => { + if (typeof obj._setServerData === "function") obj._setServerData(reply.data); + + if (handle) { + // Shared listen socket + shared(reply, { handle, indexesKey, index }, cb); + } else { + // Round-robin. + rr(reply, { indexesKey, index }, cb); + } + }); + + obj.once("listening", () => { + // short-lived sockets might have been closed + if (!indexes.has(indexesKey)) { + return; + } + cluster.worker.state = "listening"; + const address = obj.address(); + message.act = "listening"; + message.port = (address && address.port) || options.port; + send(message); + }); +}; + +function removeIndexesKey(indexesKey, index) { + const indexSet = indexes.get(indexesKey); + if (!indexSet) { + return; + } + + indexSet.set.delete(index); + if (indexSet.set.size === 0) { + indexes.delete(indexesKey); + } +} + +// Shared listen socket. +function shared(message, { handle, indexesKey, index }, cb) { + const key = message.key; + // Monkey-patch the close() method so we can keep track of when it's + // closed. Avoids resource leaks when the handle is short-lived. + const close = handle.close; + + handle.close = function () { + send({ act: "close", key }); + handles.delete(key); + removeIndexesKey(indexesKey, index); + return close.$apply(handle, arguments); + }; + $assert(handles.has(key) === false); + handles.set(key, handle); + cb(message.errno, handle); +} + +// Round-robin. Master distributes handles across workers. +function rr(message, { indexesKey, index }, cb) { + if (message.errno) return cb(message.errno, null); + + let key = message.key; + + let fakeHandle: number | null = null; + + function ref() { + if (!fakeHandle) { + fakeHandle = setInterval(noop, TIMEOUT_MAX); + } + } + + function unref() { + if (fakeHandle) { + clearInterval(fakeHandle); + fakeHandle = null; + } + } + + function listen(backlog) { + // TODO(bnoordhuis) Send a message to the primary that tells it to + // update the backlog size. The actual backlog should probably be + // the largest requested size by any worker. + return 0; + } + + function close() { + // lib/net.js treats server._handle.close() as effectively synchronous. + // That means there is a time window between the call to close() and + // the ack by the primary process in which we can still receive handles. + // onconnection() below handles that by sending those handles back to + // the primary. + if (key === undefined) return; + unref(); + // If the handle is the last handle in process, + // the parent process will delete the handle when worker process exits. + // So it is ok if the close message get lost. + // See the comments of https://github.com/nodejs/node/pull/46161 + send({ act: "close", key }); + handles.delete(key); + removeIndexesKey(indexesKey, index); + key = undefined; + } + + function getsockname(out) { + if (key) ObjectAssign(out, message.sockname); + + return 0; + } + + // Faux handle. net.Server is not associated with handle, + // so we control its state(ref or unref) by setInterval. + const handle = { close, listen, ref, unref }; + handle.ref(); + if (message.sockname) { + handle.getsockname = getsockname; // TCP handles only. + } + + $assert(handles.has(key) === false); + handles.set(key, handle); + cb(0, handle); +} + +// Round-robin connection. +function onconnection(message, handle) { + const key = message.key; + const server = handles.get(key); + let accepted = server !== undefined; + + if (accepted && server[owner_symbol]) { + const self = server[owner_symbol]; + if (self.maxConnections != null && self._connections >= self.maxConnections) { + accepted = false; + } + } + + send({ ack: message.seq, accepted }); + + if (accepted) server.onconnection(0, handle); + else handle.close(); +} + +function send(message, cb?) { + return sendHelper(message, null, cb); +} + +// Extend generic Worker with methods specific to worker processes. +Worker.prototype.disconnect = function () { + if (this.state !== "disconnecting" && this.state !== "destroying") { + this.state = "disconnecting"; + this._disconnect(); + } + + return this; +}; + +Worker.prototype._disconnect = function (this: typeof Worker, primaryInitiated?) { + this.exitedAfterDisconnect = true; + let waitingCount = 1; + + function checkWaitingCount() { + waitingCount--; + + if (waitingCount === 0) { + // If disconnect is worker initiated, wait for ack to be sure + // exitedAfterDisconnect is properly set in the primary, otherwise, if + // it's primary initiated there's no need to send the + // exitedAfterDisconnect message + if (primaryInitiated) { + process.disconnect(); + } else { + send({ act: "exitedAfterDisconnect" }, () => process.disconnect()); + } + } + } + + handles.forEach(handle => { + waitingCount++; + + if (handle[owner_symbol]) handle[owner_symbol].close(checkWaitingCount); + else handle.close(checkWaitingCount); + }); + + handles.clear(); + checkWaitingCount(); +}; + +Worker.prototype.destroy = function () { + if (this.state === "destroying") return; + + this.exitedAfterDisconnect = true; + if (!this.isConnected()) { + process.exit(kNoFailure); + } else { + this.state = "destroying"; + send({ act: "exitedAfterDisconnect" }, () => process.disconnect()); + process.once("disconnect", () => process.exit(kNoFailure)); + } +}; diff --git a/src/js/internal/cluster/isPrimary.ts b/src/js/internal/cluster/isPrimary.ts new file mode 100644 index 0000000000000..f7116833fb00c --- /dev/null +++ b/src/js/internal/cluster/isPrimary.ts @@ -0,0 +1,4 @@ +// tiny module to shortcut getting access to this boolean without loading the entire node:cluster module +export default { + isPrimary: Bun.env.NODE_UNIQUE_ID == null, +}; diff --git a/src/js/internal/cluster/primary.ts b/src/js/internal/cluster/primary.ts new file mode 100644 index 0000000000000..e3a0d4f84492b --- /dev/null +++ b/src/js/internal/cluster/primary.ts @@ -0,0 +1,319 @@ +const EventEmitter = require("node:events"); +const child_process = require("node:child_process"); +const Worker = require("internal/cluster/Worker"); +const RoundRobinHandle = require("internal/cluster/RoundRobinHandle"); +const path = require("node:path"); +const { throwNotImplemented, kHandle } = require("internal/shared"); + +const sendHelper = $newZigFunction("node_cluster_binding.zig", "sendHelperPrimary", 4); +const onInternalMessage = $newZigFunction("node_cluster_binding.zig", "onInternalMessagePrimary", 3); + +const ArrayPrototypeSlice = Array.prototype.slice; +const ObjectValues = Object.values; +const ObjectKeys = Object.keys; + +const cluster = new EventEmitter(); +const intercom = new EventEmitter(); +const SCHED_NONE = 1; +const SCHED_RR = 2; + +export default cluster; + +const handles = new Map(); +cluster.isWorker = false; +cluster.isMaster = true; // Deprecated alias. Must be same as isPrimary. +cluster.isPrimary = true; +cluster.Worker = Worker; +cluster.workers = {}; +cluster.settings = {}; +cluster.SCHED_NONE = SCHED_NONE; // Leave it to the operating system. +cluster.SCHED_RR = SCHED_RR; // Primary distributes connections. + +let ids = 0; +let initialized = false; + +// XXX(bnoordhuis) Fold cluster.schedulingPolicy into cluster.settings? +const schedulingPolicyEnv = process.env.NODE_CLUSTER_SCHED_POLICY; +let schedulingPolicy = 0; +if (schedulingPolicyEnv === "rr") schedulingPolicy = SCHED_RR; +else if (schedulingPolicyEnv === "none") schedulingPolicy = SCHED_NONE; +else if (process.platform === "win32") { + // // Round-robin doesn't perform well on + // // Windows due to the way IOCP is wired up. + // schedulingPolicy = SCHED_NONE; + // TODO + schedulingPolicy = SCHED_RR; +} else schedulingPolicy = SCHED_RR; +cluster.schedulingPolicy = schedulingPolicy; + +cluster.setupPrimary = function (options) { + const settings = { + args: ArrayPrototypeSlice.$call(process.argv, 2), + exec: process.argv[1], + execArgv: process.execArgv, + silent: false, + ...cluster.settings, + ...options, + }; + + cluster.settings = settings; + + if (initialized === true) return process.nextTick(setupSettingsNT, settings); + + initialized = true; + schedulingPolicy = cluster.schedulingPolicy; // Freeze policy. + if (!(schedulingPolicy === SCHED_NONE || schedulingPolicy === SCHED_RR)) + throw new Error(`Bad cluster.schedulingPolicy: ${schedulingPolicy}`); + + process.nextTick(setupSettingsNT, settings); +}; + +// Deprecated alias must be same as setupPrimary +cluster.setupMaster = cluster.setupPrimary; + +function setupSettingsNT(settings) { + cluster.emit("setup", settings); +} + +function createWorkerProcess(id, env) { + const workerEnv = { ...process.env, ...env, NODE_UNIQUE_ID: `${id}` }; + const execArgv = [...cluster.settings.execArgv]; + + // if (cluster.settings.inspectPort === null) { + // throw new ERR_SOCKET_BAD_PORT("Port", null, true); + // } + // if (isUsingInspector(cluster.settings.execArgv)) { + // ArrayPrototypePush(execArgv, `--inspect-port=${getInspectPort(cluster.settings.inspectPort)}`); + // } + + return child_process.fork(cluster.settings.exec, cluster.settings.args, { + cwd: cluster.settings.cwd, + env: workerEnv, + serialization: cluster.settings.serialization, + silent: cluster.settings.silent, + windowsHide: cluster.settings.windowsHide, + execArgv: execArgv, + stdio: cluster.settings.stdio, + gid: cluster.settings.gid, + uid: cluster.settings.uid, + }); +} + +function removeWorker(worker) { + if (!worker) throw new Error("ERR_INTERNAL_ASSERTION"); + delete cluster.workers[worker.id]; + + if (ObjectKeys(cluster.workers).length === 0) { + if (!(handles.size === 0)) throw new Error("Resource leak detected."); + intercom.emit("disconnect"); + } +} + +function removeHandlesForWorker(worker) { + if (!worker) throw new Error("ERR_INTERNAL_ASSERTION"); + + handles.forEach((handle, key) => { + if (handle.remove(worker)) handles.delete(key); + }); +} + +cluster.fork = function (env) { + cluster.setupPrimary(); + const id = ++ids; + const workerProcess = createWorkerProcess(id, env); + const worker = new Worker({ + id: id, + process: workerProcess, + }); + + worker.on("message", function (message, handle) { + cluster.emit("message", this, message, handle); + }); + + // FIXME: throwing an error in this function does not get caught + // at least in the cases where #handle has become null + // may be always; don't have time to investigate right now + worker.process.once("exit", (exitCode, signalCode) => { + /* + * Remove the worker from the workers list only + * if it has disconnected, otherwise we might + * still want to access it. + */ + if (!worker.isConnected()) { + removeHandlesForWorker(worker); + removeWorker(worker); + } + + worker.exitedAfterDisconnect = !!worker.exitedAfterDisconnect; + worker.state = "dead"; + worker.emit("exit", exitCode, signalCode); + cluster.emit("exit", worker, exitCode, signalCode); + }); + + worker.process.once("disconnect", () => { + /* + * Now is a good time to remove the handles + * associated with this worker because it is + * not connected to the primary anymore. + */ + removeHandlesForWorker(worker); + + /* + * Remove the worker from the workers list only + * if its process has exited. Otherwise, we might + * still want to access it. + */ + if (worker.isDead()) removeWorker(worker); + + worker.exitedAfterDisconnect = !!worker.exitedAfterDisconnect; + worker.state = "disconnected"; + worker.emit("disconnect"); + cluster.emit("disconnect", worker); + }); + + onInternalMessage(worker.process[kHandle], worker, onmessage); + process.nextTick(emitForkNT, worker); + cluster.workers[worker.id] = worker; + return worker; +}; + +function emitForkNT(worker) { + cluster.emit("fork", worker); +} + +cluster.disconnect = function (cb) { + const workers = ObjectKeys(cluster.workers); + + if (workers.length === 0) { + process.nextTick(() => intercom.emit("disconnect")); + } else { + for (const worker of ObjectValues(cluster.workers)) { + if (worker.isConnected()) { + worker.disconnect(); + } + } + } + + if (typeof cb === "function") intercom.once("disconnect", cb); +}; + +const methodMessageMapping = { + close, + exitedAfterDisconnect, + listening, + online, + queryServer, +}; + +function onmessage(message, handle) { + const worker = this; + + const fn = methodMessageMapping[message.act]; + + if (typeof fn === "function") fn(worker, message); +} + +function online(worker) { + worker.state = "online"; + worker.emit("online"); + cluster.emit("online", worker); +} + +function exitedAfterDisconnect(worker, message) { + worker.exitedAfterDisconnect = true; + send(worker, { ack: message.seq }); +} + +function queryServer(worker, message) { + // Stop processing if worker already disconnecting + if (worker.exitedAfterDisconnect) return; + + const key = `${message.address}:${message.port}:${message.addressType}:` + `${message.fd}:${message.index}`; + let handle = handles.get(key); + + if (handle === undefined) { + let address = message.address; + + // Find shortest path for unix sockets because of the ~100 byte limit + if (message.port < 0 && typeof address === "string" && process.platform !== "win32") { + address = path.relative(process.cwd(), address); + + if (message.address.length < address.length) address = message.address; + } + + // UDP is exempt from round-robin connection balancing for what should + // be obvious reasons: it's connectionless. There is nothing to send to + // the workers except raw datagrams and that's pointless. + if (schedulingPolicy !== SCHED_RR || message.addressType === "udp4" || message.addressType === "udp6") { + throwNotImplemented("node:cluster SCHED_NONE"); + } else { + handle = new RoundRobinHandle(key, address, message); + } + + handles.set(key, handle); + } + + if (!handle.data) handle.data = message.data; + + // Set custom server data + handle.add(worker, (errno, reply, handle) => { + const { data } = handles.get(key); + + if (errno) handles.delete(key); // Gives other workers a chance to retry. + + send( + worker, + { + errno, + key, + ack: message.seq, + data, + ...reply, + }, + handle, + ); + }); +} + +function listening(worker, message) { + const info = { + addressType: message.addressType, + address: message.address, + port: message.port, + fd: message.fd, + }; + + worker.state = "listening"; + worker.emit("listening", info); + cluster.emit("listening", worker, info); +} + +// Server in worker is closing, remove from list. The handle may have been +// removed by a prior call to removeHandlesForWorker() so guard against that. +function close(worker, message) { + const key = message.key; + const handle = handles.get(key); + + if (handle && handle.remove(worker)) handles.delete(key); +} + +function send(worker, message, handle?, cb?) { + return sendHelper(worker.process[kHandle], message, handle, cb); +} + +// Extend generic Worker with methods specific to the primary process. +Worker.prototype.disconnect = function () { + this.exitedAfterDisconnect = true; + send(this, { act: "disconnect" }); + this.process.disconnect(); + removeHandlesForWorker(this); + removeWorker(this); + return this; +}; + +Worker.prototype.destroy = function (signo) { + const proc = this.process; + const signal = signo || "SIGTERM"; + + proc.kill(signal); +}; diff --git a/src/js/internal/debugger.ts b/src/js/internal/debugger.ts index 2c049d9550832..6c4b8376dcb19 100644 --- a/src/js/internal/debugger.ts +++ b/src/js/internal/debugger.ts @@ -1,4 +1,4 @@ -import type { Server as WebSocketServer, WebSocketHandler, ServerWebSocket, SocketHandler, Socket } from "bun"; +import type { ServerWebSocket, Socket, SocketHandler, WebSocketHandler, Server as WebSocketServer } from "bun"; export default function ( executionContextId: string, diff --git a/src/js/internal/errors.ts b/src/js/internal/errors.ts new file mode 100644 index 0000000000000..dedce8f38ad62 --- /dev/null +++ b/src/js/internal/errors.ts @@ -0,0 +1,9 @@ +export default { + ERR_INVALID_ARG_TYPE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_INVALID_ARG_TYPE", 3), + ERR_OUT_OF_RANGE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_OUT_OF_RANGE", 3), + ERR_IPC_DISCONNECTED: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_IPC_DISCONNECTED", 0), + ERR_SERVER_NOT_RUNNING: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_SERVER_NOT_RUNNING", 0), + ERR_IPC_CHANNEL_CLOSED: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_IPC_CHANNEL_CLOSED", 0), + ERR_SOCKET_BAD_TYPE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_SOCKET_BAD_TYPE", 0), + ERR_INVALID_PROTOCOL: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_INVALID_PROTOCOL", 0), +}; diff --git a/src/js/internal/linkedlist.ts b/src/js/internal/linkedlist.ts new file mode 100644 index 0000000000000..b5ed1b4246aae --- /dev/null +++ b/src/js/internal/linkedlist.ts @@ -0,0 +1,45 @@ +export function init(list) { + list._idleNext = list; + list._idlePrev = list; + return list; +} + +// Show the most idle item. +export function peek(list) { + if (list._idlePrev === list) return null; + return list._idlePrev; +} + +// Remove an item from its list. +export function remove(item) { + if (item._idleNext) { + item._idleNext._idlePrev = item._idlePrev; + } + + if (item._idlePrev) { + item._idlePrev._idleNext = item._idleNext; + } + + item._idleNext = null; + item._idlePrev = null; +} + +// Remove an item from its list and place at the end. +export function append(list, item) { + if (item._idleNext || item._idlePrev) { + remove(item); + } + + // Items are linked with _idleNext -> (older) and _idlePrev -> (newer). + // Note: This linkage (next being older) may seem counter-intuitive at first. + item._idleNext = list._idleNext; + item._idlePrev = list; + + // The list _idleNext points to tail (newest) and _idlePrev to head (oldest). + list._idleNext._idlePrev = item; + list._idleNext = item; +} + +export function isEmpty(list) { + return list._idleNext === list; +} diff --git a/src/js/internal/net.ts b/src/js/internal/net.ts index a258aa45d4050..9016f6072d15c 100644 --- a/src/js/internal/net.ts +++ b/src/js/internal/net.ts @@ -1,3 +1,3 @@ -const [addServerName] = $zig("socket.zig", "createNodeTLSBinding"); +const [addServerName, upgradeDuplexToTLS, isNamedPipeSocket] = $zig("socket.zig", "createNodeTLSBinding"); -export default { addServerName }; +export default { addServerName, upgradeDuplexToTLS, isNamedPipeSocket }; diff --git a/src/js/internal/primordials.js b/src/js/internal/primordials.js index 8293100b841b8..95745088b5b79 100644 --- a/src/js/internal/primordials.js +++ b/src/js/internal/primordials.js @@ -196,7 +196,10 @@ export default { SymbolPrototypeValueOf: uncurryThis(Symbol.prototype.valueOf), FunctionPrototypeToString: uncurryThis(Function.prototype.toString), FunctionPrototypeBind: uncurryThis(Function.prototype.bind), + SymbolDispose: Symbol.dispose, + SymbolAsyncDispose: Symbol.asyncDispose, SymbolIterator: Symbol.iterator, + SymbolAsyncIterator: Symbol.asyncIterator, SymbolFor: Symbol.for, SymbolToStringTag: Symbol.toStringTag, TypedArrayPrototypeGetLength: getGetter(Uint8Array, "length"), @@ -208,6 +211,7 @@ export default { Int8Array, Int16Array, Int32Array, + Float16Array, Float32Array, Float64Array, BigUint64Array, diff --git a/src/js/internal/promisify.ts b/src/js/internal/promisify.ts new file mode 100644 index 0000000000000..76a6ecefc60ef --- /dev/null +++ b/src/js/internal/promisify.ts @@ -0,0 +1,79 @@ +const kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom"); +const kCustomPromisifyArgsSymbol = Symbol("customPromisifyArgs"); + +function defineCustomPromisify(target, callback) { + Object.defineProperty(target, kCustomPromisifiedSymbol, { + value: callback, + __proto__: null, + configurable: true, + }); + + return callback; +} + +function defineCustomPromisifyArgs(target, args) { + Object.defineProperty(target, kCustomPromisifyArgsSymbol, { + __proto__: null, + value: args, + enumerable: false, + }); + return args; +} + +var promisify = function promisify(original) { + if (typeof original !== "function") throw new TypeError('The "original" argument must be of type Function'); + const custom = original[kCustomPromisifiedSymbol]; + if (custom) { + if (typeof custom !== "function") { + throw new TypeError('The "util.promisify.custom" argument must be of type Function'); + } + // ensure that we don't create another promisified function wrapper + return defineCustomPromisify(custom, custom); + } + + const callbackArgs = original[kCustomPromisifyArgsSymbol]; + + function fn(...originalArgs) { + const { promise, resolve, reject } = Promise.withResolvers(); + try { + original.$apply(this, [ + ...originalArgs, + function (err, ...values) { + if (err) { + return reject(err); + } + + if (callbackArgs !== undefined && values.length > 0) { + if (!Array.isArray(callbackArgs)) { + throw new TypeError('The "customPromisifyArgs" argument must be of type Array'); + } + if (callbackArgs.length !== values.length) { + throw new Error("Mismatched length in promisify callback args"); + } + const result = {}; + for (let i = 0; i < callbackArgs.length; i++) { + result[callbackArgs[i]] = values[i]; + } + resolve(result); + } else { + resolve(values[0]); + } + }, + ]); + } catch (err) { + reject(err); + } + + return promise; + } + Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); + defineCustomPromisify(fn, fn); + return Object.defineProperties(fn, Object.getOwnPropertyDescriptors(original)); +}; +promisify.custom = kCustomPromisifiedSymbol; + +export default { + defineCustomPromisify, + defineCustomPromisifyArgs, + promisify, +}; diff --git a/src/js/internal/shared.ts b/src/js/internal/shared.ts index 729e71df2a262..df0f652ee9a52 100644 --- a/src/js/internal/shared.ts +++ b/src/js/internal/shared.ts @@ -44,10 +44,49 @@ function warnNotImplementedOnce(feature: string, issue?: number) { const fileSinkSymbol = Symbol("fileSink"); +// + +let util; +class ExceptionWithHostPort extends Error { + errno: number; + syscall: string; + port?: number; + + constructor(err, syscall, address, port) { + // TODO(joyeecheung): We have to use the type-checked + // getSystemErrorName(err) to guard against invalid arguments from users. + // This can be replaced with [ code ] = errmap.get(err) when this method + // is no longer exposed to user land. + util ??= require("node:util"); + const code = util.getSystemErrorName(err); + let details = ""; + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + + super(`${syscall} ${code}${details}`); + + this.errno = err; + this.code = code; + this.syscall = syscall; + this.address = address; + if (port) { + this.port = port; + } + } +} + +// + export default { NotImplementedError, throwNotImplemented, hideFromStack, warnNotImplementedOnce, fileSinkSymbol, + ExceptionWithHostPort, + kHandle: Symbol("kHandle"), + kAutoDestroyed: Symbol("kAutoDestroyed"), }; diff --git a/src/js/internal/url.ts b/src/js/internal/url.ts new file mode 100644 index 0000000000000..4349f82b6025c --- /dev/null +++ b/src/js/internal/url.ts @@ -0,0 +1,23 @@ +function urlToHttpOptions(url) { + const options = { + protocol: url.protocol, + hostname: + typeof url.hostname === "string" && url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname, + hash: url.hash, + search: url.search, + pathname: url.pathname, + path: `${url.pathname || ""}${url.search || ""}`, + href: url.href, + }; + if (url.port !== "") { + options.port = Number(url.port); + } + if (url.username || url.password) { + options.auth = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`; + } + return options; +} + +export default { + urlToHttpOptions, +}; diff --git a/src/js/internal/util/inspect.js b/src/js/internal/util/inspect.js index 4bfc829bda045..f4b3a1228266d 100644 --- a/src/js/internal/util/inspect.js +++ b/src/js/internal/util/inspect.js @@ -2637,7 +2637,7 @@ function previewEntries(val, isIterator = false) { const iteratedObject = $getInternalField(val, 1 /*iteratorFieldIteratedObject*/); // for Maps: 0 = keys, 1 = values, 2 = entries // for Sets: 1 = keys|values, 2 = entries - const kind = $getInternalField(val, 2 /*iteratorFieldKind*/); + const kind = $getInternalField(val, 3 /*iteratorFieldKind*/); const isEntries = kind === 2; // TODO(bun): improve performance by not using Array.from and instead using the iterator directly to only get the first // few entries which will actually be displayed (this requires changing some logic in the call sites of this function) diff --git a/src/js/node/child_process.ts b/src/js/node/child_process.ts index fe48fcefaea0b..655272b80e146 100644 --- a/src/js/node/child_process.ts +++ b/src/js/node/child_process.ts @@ -2,12 +2,13 @@ const EventEmitter = require("node:events"); const StreamModule = require("node:stream"); const OsModule = require("node:os"); +const { ERR_INVALID_ARG_TYPE, ERR_IPC_DISCONNECTED } = require("internal/errors"); +const { kHandle } = require("internal/shared"); var NetModule; var ObjectCreate = Object.create; var ObjectAssign = Object.assign; -var ObjectDefineProperty = Object.defineProperty; var BufferConcat = Buffer.concat; var BufferIsEncoding = Buffer.isEncoding; @@ -21,7 +22,6 @@ var ArrayPrototypeIncludes = Array.prototype.includes; var ArrayPrototypeSlice = Array.prototype.slice; var ArrayPrototypeUnshift = Array.prototype.unshift; -// var ArrayBuffer = ArrayBuffer; var ArrayBufferIsView = ArrayBuffer.isView; var NumberIsInteger = Number.isInteger; @@ -32,6 +32,7 @@ var StringPrototypeSlice = String.prototype.slice; var Uint8ArrayPrototypeIncludes = Uint8Array.prototype.includes; const MAX_BUFFER = 1024 * 1024; +const kFromNode = Symbol("kFromNode"); // Pass DEBUG_CHILD_PROCESS=1 to enable debug output if ($debug) { @@ -104,17 +105,6 @@ var ReadableFromWeb; // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -function spawnTimeoutFunction(child, timeoutHolder) { - var timeoutId = timeoutHolder.timeoutId; - if (timeoutId > -1) { - try { - child.kill(killSignal); - } catch (err) { - child.emit("error", err); - } - timeoutHolder.timeoutId = -1; - } -} /** * Spawns a new process using the given `file`. * @param {string} file @@ -145,11 +135,12 @@ function spawn(file, args, options) { const child = new ChildProcess(); $debug("spawn", options); + options[kFromNode] = true; child.spawn(options); const timeout = options.timeout; if (timeout && timeout > 0) { - let timeoutId = setTimeout(() => { + let timeoutId: Timer | null = setTimeout(() => { if (timeoutId) { timeoutId = null; @@ -266,7 +257,7 @@ function execFile(file, args, options, callback) { let cmd = file; - function exitHandler(code, signal) { + function exitHandler(code = 0, signal?: number | null) { if (exited) return; exited = true; @@ -998,7 +989,6 @@ class ChildProcess extends EventEmitter { #closesNeeded = 1; #closesGot = 0; - connected = false; signalCode = null; exitCode = null; spawnfile; @@ -1049,10 +1039,6 @@ class ChildProcess extends EventEmitter { } } - if (this.#handle) { - this.#handle = null; - } - if (err) { if (this.spawnfile) err.path = this.spawnfile; err.spawnargs = ArrayPrototypeSlice.$call(this.spawnargs, 1); @@ -1200,6 +1186,16 @@ class ChildProcess extends EventEmitter { return (this.#stdioObject ??= this.#createStdioObject()); } + get connected() { + const handle = this.#handle; + if (handle === null) return false; + return handle.connected ?? false; + } + + get [kHandle]() { + return this.#handle; + } + spawn(options) { validateObject(options, "options"); @@ -1226,21 +1222,20 @@ class ChildProcess extends EventEmitter { const bunStdio = getBunStdioFromOptions(stdio); const argv0 = file || options.argv0; - // TODO: better ipc support - const ipc = $isJSArray(stdio) && stdio[3] === "ipc"; - var env = options.envPairs || undefined; + const has_ipc = $isJSArray(stdio) && stdio[3] === "ipc"; + var env = options.envPairs || process.env; + const detachedOption = options.detached; this.#encoding = options.encoding || undefined; this.#stdioOptions = bunStdio; const stdioCount = stdio.length; const hasSocketsToEagerlyLoad = stdioCount >= 3; - this.#closesNeeded = 1; this.#handle = Bun.spawn({ cmd: spawnargs, stdio: bunStdio, cwd: options.cwd || undefined, - env: env || process.env, + env: env, detached: typeof detachedOption !== "undefined" ? !!detachedOption : false, onExit: (handle, exitCode, signalCode, err) => { if (hasSocketsToEagerlyLoad) { @@ -1258,7 +1253,8 @@ class ChildProcess extends EventEmitter { ); }, lazy: true, - ipc: ipc ? this.#emitIpcMessage.bind(this) : undefined, + ipc: has_ipc ? this.#emitIpcMessage.bind(this) : undefined, + onDisconnect: has_ipc ? ok => this.#disconnect(ok) : undefined, serialization, argv0, windowsHide: !!options.windowsHide, @@ -1270,9 +1266,10 @@ class ChildProcess extends EventEmitter { onSpawnNT(this); - if (ipc) { + if (has_ipc) { this.send = this.#send; this.disconnect = this.#disconnect; + if (options[kFromNode]) this.#closesNeeded += 1; } if (hasSocketsToEagerlyLoad) { @@ -1324,13 +1321,18 @@ class ChildProcess extends EventEmitter { } } - #disconnect() { - if (!this.connected) { - this.emit("error", new TypeError("Process was closed while trying to send message")); + #disconnect(ok) { + if (ok == null) { + $assert(this.connected); + this.#handle.disconnect(); + } else if (!ok) { + this.emit("error", ERR_IPC_DISCONNECTED()); return; } - this.connected = false; this.#handle.disconnect(); + $assert(!this.connected); + process.nextTick(() => this.emit("disconnect")); + this.#maybeClose(); } kill(sig?) { @@ -1943,12 +1945,6 @@ function ERR_UNKNOWN_SIGNAL(name) { return err; } -function ERR_INVALID_ARG_TYPE(name, type, value) { - const err = new TypeError(`The "${name}" argument must be of type ${type}. Received ${value?.toString()}`); - err.code = "ERR_INVALID_ARG_TYPE"; - return err; -} - function ERR_INVALID_OPT_VALUE(name, value) { const err = new TypeError(`The value "${value}" is invalid for option "${name}"`); err.code = "ERR_INVALID_OPT_VALUE"; diff --git a/src/js/node/cluster.ts b/src/js/node/cluster.ts index 3b71f7439cb92..a3f7b0595bff2 100644 --- a/src/js/node/cluster.ts +++ b/src/js/node/cluster.ts @@ -1,40 +1,22 @@ // Hardcoded module "node:cluster" -// This is a stub -// We leave it in here to provide a better error message -// TODO: implement node cluster -const EventEmitter = require("node:events"); -const { throwNotImplemented } = require("internal/shared"); -// TODO: is it okay for this to be a class? -class Cluster extends EventEmitter { - isWorker = false; - isPrimary = true; - isMaster = true; - workers = {}; - settings = {}; - SCHED_NONE = 1; - SCHED_RR = 2; - schedulingPolicy = 2; +const { isPrimary } = require("internal/cluster/isPrimary"); +const cluster = isPrimary ? require("internal/cluster/primary") : require("internal/cluster/child"); +export default cluster; - Worker = function Worker() { - throwNotImplemented("node:cluster Worker", 2428); - }; +// +// - setupPrimary() { - throwNotImplemented("node:cluster", 2428); - } - - setupMaster() { - throwNotImplemented("node:cluster", 2428); - } +function initializeClusterIPC() { + if (process.argv[1] && process.env.NODE_UNIQUE_ID) { + cluster._setupWorker(); + // Make sure it's not accidentally inherited by child processes. + delete process.env.NODE_UNIQUE_ID; - fork() { - throwNotImplemented("node:cluster", 2428); - } - - disconnect() { - throwNotImplemented("node:cluster", 2428); + process.channel.unref(); } } -export default new Cluster(); +if (Bun.isMainThread) { + initializeClusterIPC(); +} diff --git a/src/js/node/crypto.ts b/src/js/node/crypto.ts index 7027ace99e342..96aceb97b3e04 100644 --- a/src/js/node/crypto.ts +++ b/src/js/node/crypto.ts @@ -18,6 +18,8 @@ const { generateKeyPairSync, sign: nativeSign, verify: nativeVerify, + publicEncrypt, + privateDecrypt, } = $cpp("KeyObject.cpp", "createNodeCryptoBinding"); const { @@ -11610,16 +11612,10 @@ var require_privateDecrypt = __commonJS({ var require_browser10 = __commonJS({ "node_modules/public-encrypt/browser.js"(exports) { var publicEncrypt = require_publicEncrypt(); - exports.publicEncrypt = function (key, buf, options) { - return publicEncrypt(getKeyFrom(key, "public"), buf, options); - }; - var privateDecrypt = require_privateDecrypt(); - exports.privateDecrypt = function (key, buf, options) { - return privateDecrypt(getKeyFrom(key, "private"), buf, options); - }; exports.privateEncrypt = function (key, buf) { return publicEncrypt(getKeyFrom(key, "private"), buf, !0); }; + var privateDecrypt = require_privateDecrypt(); exports.publicDecrypt = function (key, buf) { return privateDecrypt(getKeyFrom(key, "public"), buf, !0); }; @@ -11721,10 +11717,8 @@ var require_crypto_browserify2 = __commonJS({ exports.Verify = sign.Verify; exports.createECDH = require_browser9(); var publicEncrypt = require_browser10(); - exports.publicEncrypt = publicEncrypt.publicEncrypt; exports.privateEncrypt = publicEncrypt.privateEncrypt; exports.publicDecrypt = publicEncrypt.publicDecrypt; - exports.privateDecrypt = publicEncrypt.privateDecrypt; exports.getRandomValues = values => crypto.getRandomValues(values); var rf = require_browser11(); exports.randomFill = rf.randomFill; @@ -11832,8 +11826,9 @@ class KeyObject { } this.$bunNativePtr = key; } - toString() { - return "[object KeyObject]"; + + get [Symbol.toStringTag]() { + return "KeyObject"; } static from(key) { @@ -11934,14 +11929,17 @@ function _generateKeyPairSync(algorithm, options) { } crypto_exports.generateKeyPairSync = _generateKeyPairSync; -crypto_exports.generateKeyPair = function (algorithm, options, callback) { +function _generateKeyPair(algorithm, options, callback) { try { const result = _generateKeyPairSync(algorithm, options); typeof callback === "function" && callback(null, result.publicKey, result.privateKey); } catch (err) { typeof callback === "function" && callback(err); } -}; +} +const { defineCustomPromisifyArgs } = require("internal/promisify"); +defineCustomPromisifyArgs(_generateKeyPair, ["publicKey", "privateKey"]); +crypto_exports.generateKeyPair = _generateKeyPair; crypto_exports.createSecretKey = function (key, encoding) { if (key instanceof KeyObject || key instanceof CryptoKey) { @@ -12030,7 +12028,7 @@ function _createPublicKey(key) { } return KeyObject.from( createPublicKey({ - key: createPrivateKey({ key: actual_key, format: key.format, passphrase: key.passphrase }), + key: createPrivateKey({ key: actual_key, format: key.format || "pem", passphrase: key.passphrase }), format: "", }), ); @@ -12162,6 +12160,51 @@ crypto_exports.verify = function (algorithm, data, key, signature, callback) { } }; +// We are not allowed to call createPublicKey/createPrivateKey when we're already working with a +// KeyObject/CryptoKey of the same type (public/private). +function toCryptoKey(key, asPublic) { + // Top level CryptoKey. + if (key instanceof KeyObject || key instanceof CryptoKey) { + if (asPublic && key.type === "private") { + return _createPublicKey(key).$bunNativePtr; + } + return key.$bunNativePtr || key; + } + + // Nested CryptoKey. + if (key.key instanceof KeyObject || key.key instanceof CryptoKey) { + if (asPublic && key.key.type === "private") { + return _createPublicKey(key.key).$bunNativePtr; + } + return key.key.$bunNativePtr || key.key; + } + + // One of string, ArrayBuffer, Buffer, TypedArray, DataView, or Object. + return asPublic ? _createPublicKey(key).$bunNativePtr : _createPrivateKey(key).$bunNativePtr; +} + +function doAsymmetricCipher(key, message, operation, isEncrypt) { + // Our crypto bindings expect the key to be a `JSCryptoKey` property within an object. + const cryptoKey = toCryptoKey(key, isEncrypt); + const oaepLabel = typeof key.oaepLabel === "string" ? Buffer.from(key.oaepLabel, key.encoding) : key.oaepLabel; + const keyObject = { + key: cryptoKey, + oaepHash: key.oaepHash, + oaepLabel, + padding: key.padding, + }; + const buffer = typeof message === "string" ? Buffer.from(message, key.encoding) : message; + return operation(keyObject, buffer); +} + +crypto_exports.publicEncrypt = function (key, message) { + return doAsymmetricCipher(key, message, publicEncrypt, true); +}; + +crypto_exports.privateDecrypt = function (key, message) { + return doAsymmetricCipher(key, message, privateDecrypt, false); +}; + __export(crypto_exports, { DEFAULT_ENCODING: () => DEFAULT_ENCODING, getRandomValues: () => getRandomValues, diff --git a/src/js/node/dgram.ts b/src/js/node/dgram.ts index b3fb856096889..4093197f11794 100644 --- a/src/js/node/dgram.ts +++ b/src/js/node/dgram.ts @@ -34,6 +34,7 @@ const kStateSymbol = Symbol("state symbol"); const async_id_symbol = Symbol("async_id_symbol"); const { hideFromStack, throwNotImplemented } = require("internal/shared"); +const { ERR_SOCKET_BAD_TYPE } = require("internal/errors"); const { FunctionPrototypeBind, @@ -139,7 +140,7 @@ function validateString(value, name) { } hideFromStack(validateString); -function validateNumber(value, name, min = undefined, max) { +function validateNumber(value, name, min?, max?) { if (typeof value !== "number") throw new ERR_INVALID_ARG_TYPE(name, "number", value); if ( @@ -247,6 +248,7 @@ function Socket(type, listener) { ipv6Only: options && options.ipv6Only, recvBufferSize, sendBufferSize, + unrefOnBind: false, }; if (options?.signal !== undefined) { @@ -399,6 +401,10 @@ Socket.prototype.bind = function (port_, address_ /* , callback */) { }, }).$then( socket => { + if (state.unrefOnBind) { + socket.unref(); + state.unrefOnBind = false; + } state.handle.socket = socket; state.receiving = true; state.bindState = BIND_STATE_BOUND; @@ -934,7 +940,11 @@ Socket.prototype.ref = function () { Socket.prototype.unref = function () { const socket = this[kStateSymbol].handle?.socket; - if (socket) socket.unref(); + if (socket) { + socket.unref(); + } else { + this[kStateSymbol].unrefOnBind = true; + } return this; }; diff --git a/src/js/node/dns.ts b/src/js/node/dns.ts index d657f609eb37f..cfde60acd3426 100644 --- a/src/js/node/dns.ts +++ b/src/js/node/dns.ts @@ -47,6 +47,7 @@ function lookup(domain, options, callback) { } dns.lookup(domain, options).then(res => { + throwIfEmpty(res); res.sort((a, b) => a.family - b.family); if (options?.all) { @@ -333,18 +334,33 @@ var { function setDefaultResultOrder() {} function setServers() {} -const promisifyLookup = res => { - res.sort((a, b) => a.family - b.family); - const [{ address, family }] = res; +const mapLookupAll = res => { + const { address, family } = res; return { address, family }; }; -const mapLookupAll = res => { - const { address, family } = res; +function throwIfEmpty(res) { + if (res.length === 0) { + const err = new Error("No records found"); + err.name = "DNSException"; + err.code = "ENODATA"; + // Hardcoded errno + err.errno = 1; + err.syscall = "getaddrinfo"; + throw err; + } +} +Object.defineProperty(throwIfEmpty, "name", { value: "::bunternal::" }); + +const promisifyLookup = res => { + throwIfEmpty(res); + res.sort((a, b) => a.family - b.family); + const [{ address, family }] = res; return { address, family }; }; const promisifyLookupAll = res => { + throwIfEmpty(res); res.sort((a, b) => a.family - b.family); return res.map(mapLookupAll); }; diff --git a/src/js/node/events.ts b/src/js/node/events.ts index 75f610921fefa..ca9cce2d3bc5e 100644 --- a/src/js/node/events.ts +++ b/src/js/node/events.ts @@ -424,20 +424,34 @@ function on(emitter, event, options = {}) { return iterator(); } -const toStringTag = Symbol.toStringTag; +const getEventListenersForEventTarget = $newCppFunction( + "JSEventTargetNode.cpp", + "jsFunctionNodeEventsGetEventListeners", + 1, +); + function getEventListeners(emitter, type) { - if (emitter?.[toStringTag] === "EventTarget") { - throwNotImplemented("getEventListeners with an EventTarget", 2678); + if ($isCallable(emitter?.listeners)) { + return emitter.listeners(type); } - return emitter.listeners(type); + + return getEventListenersForEventTarget(emitter, type); } -function setMaxListeners(n, ...eventTargets) { +// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/events.js#L315-L339 +function setMaxListeners(n = defaultMaxListeners, ...eventTargets) { validateNumber(n, "setMaxListeners", 0); - var length; - if (eventTargets && (length = eventTargets.length)) { - for (let i = 0; i < length; i++) { - eventTargets[i].setMaxListeners(n); + const length = eventTargets?.length; + if (length) { + for (let eventTargetOrEmitter of eventTargets) { + // TODO: EventTarget setMaxListeners is not implemented yet. + // Only EventEmitter has it. + if ($isCallable(eventTargetOrEmitter?.setMaxListeners)) { + eventTargetOrEmitter.setMaxListeners(n); + } else if ($isObject(eventTargetOrEmitter) && eventTargetOrEmitter instanceof EventTarget) { + // This is a fake number so that the number can be checked against with getMaxListeners() + eventTargetOrEmitter[eventTargetMaxListenersSymbol] = n; + } } } else { defaultMaxListeners = n; @@ -521,9 +535,9 @@ function validateBoolean(value, name) { let AsyncResource = null; +const eventTargetMaxListenersSymbol = Symbol("EventTarget.maxListeners"); function getMaxListeners(emitterOrTarget) { - // TODO: apparently EventTarget in Node can have a max number of listeners? - return emitterOrTarget?._maxListeners ?? defaultMaxListeners; + return emitterOrTarget?.[eventTargetMaxListenersSymbol] ?? emitterOrTarget?._maxListeners ?? defaultMaxListeners; } // Copy-pasta from Node.js source code diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts index c6951eb4172e7..5960cc01bf8a0 100644 --- a/src/js/node/fs.promises.ts +++ b/src/js/node/fs.promises.ts @@ -200,6 +200,15 @@ const exports = { }, writeFile: function (fileHandleOrFdOrPath, ...args) { fileHandleOrFdOrPath = fileHandleOrFdOrPath?.[kFd] ?? fileHandleOrFdOrPath; + if ( + !$isTypedArrayView(args[0]) && + typeof args[0] !== "string" && + ($isCallable(args[0]?.[Symbol.iterator]) || $isCallable(args[0]?.[Symbol.asyncIterator])) + ) { + $debug("fs.promises.writeFile async iterator slow path!"); + // Node accepts an arbitrary async iterator here + return writeFileAsyncIterator(fileHandleOrFdOrPath, ...args); + } return _writeFile(fileHandleOrFdOrPath, ...args); }, readlink: fs.readlink.bind(fs), @@ -276,6 +285,12 @@ export default exports; return this[kFd]; } + [kCloseResolve]; + [kFd]; + [kFlag]; + [kClosePromise]; + [kRefs]; + async appendFile(data, options: object | string | undefined) { const fd = this[kFd]; throwEBADFIfNecessary(writeFile, fd); @@ -564,3 +579,70 @@ function throwEBADFIfNecessary(fn, fd) { throw err; } } + +async function writeFileAsyncIteratorInner(fd, iterable, encoding) { + const writer = Bun.file(fd).writer(); + + const mustRencode = !(encoding === "utf8" || encoding === "utf-8" || encoding === "binary" || encoding === "buffer"); + let totalBytesWritten = 0; + + try { + for await (let chunk of iterable) { + if (mustRencode && typeof chunk === "string") { + $debug("Re-encoding chunk to", encoding); + chunk = Buffer.from(chunk, encoding); + } + + const prom = writer.write(chunk); + if (prom && $isPromise(prom)) { + totalBytesWritten += await prom; + } else { + totalBytesWritten += prom; + } + } + } finally { + await writer.end(); + } + + return totalBytesWritten; +} + +async function writeFileAsyncIterator(fdOrPath, iterable, optionsOrEncoding, flag, mode) { + let encoding; + if (typeof optionsOrEncoding === "object") { + encoding = optionsOrEncoding?.encoding ?? (encoding || "utf8"); + flag = optionsOrEncoding?.flag ?? (flag || "w"); + mode = optionsOrEncoding?.mode ?? (mode || 0o666); + } else if (typeof optionsOrEncoding === "string" || optionsOrEncoding == null) { + encoding = optionsOrEncoding || "utf8"; + flag ??= "w"; + mode ??= 0o666; + } + + if (!Buffer.isEncoding(encoding)) { + // ERR_INVALID_OPT_VALUE_ENCODING was removed in Node v15. + throw new TypeError(`Unknown encoding: ${encoding}`); + } + + let mustClose = typeof fdOrPath === "string"; + if (mustClose) { + // Rely on fs.open for further argument validaiton. + fdOrPath = await fs.open(fdOrPath, flag, mode); + } + + let totalBytesWritten = 0; + + try { + totalBytesWritten = await writeFileAsyncIteratorInner(fdOrPath, iterable, encoding); + } finally { + if (mustClose) { + try { + if (typeof flag === "string" && !flag.includes("a")) { + await fs.ftruncate(fdOrPath, totalBytesWritten); + } + } finally { + await fs.close(fdOrPath); + } + } + } +} diff --git a/src/js/node/fs.ts b/src/js/node/fs.ts index fa1e3025fafc2..3b6f205101b4c 100644 --- a/src/js/node/fs.ts +++ b/src/js/node/fs.ts @@ -23,7 +23,7 @@ var _fs = Symbol.for("#fs"); function ensureCallback(callback) { if (!$isCallable(callback)) { - const err = new TypeError("Callback must be a function"); + const err = new TypeError('The "cb" argument must be of type function. Received ' + typeof callback); err.code = "ERR_INVALID_ARG_TYPE"; throw err; } @@ -31,6 +31,17 @@ function ensureCallback(callback) { return callback; } +// Micro-optimization: avoid creating a new function for every call +// bind() is slightly more optimized in JSC +// This code is equivalent to: +// +// function () { callback(null); } +// +function nullcallback(callback) { + return FunctionPrototypeBind.$call(callback, undefined, null); +} +const FunctionPrototypeBind = nullcallback.bind; + class FSWatcher extends EventEmitter { #watcher; #listener; @@ -137,11 +148,25 @@ class StatWatcher extends EventEmitter { } } -var access = function access(...args) { - callbackify(fs.access, args); +var access = function access(path, mode, callback) { + if ($isCallable(mode)) { + callback = mode; + mode = undefined; + } + + ensureCallback(callback); + + fs.access(path, mode).then(nullcallback(callback), callback); }, - appendFile = function appendFile(...args) { - callbackify(fs.appendFile, args); + appendFile = function appendFile(path, data, options, callback) { + if (!$isCallable(callback)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.appendFile(path, data, options).then(nullcallback(callback), callback); }, close = function close(fd, callback) { if ($isCallable(callback)) { @@ -149,27 +174,39 @@ var access = function access(...args) { } else if (callback == undefined) { fs.close(fd).then(() => {}); } else { - const err = new TypeError("Callback must be a function"); - err.code = "ERR_INVALID_ARG_TYPE"; - throw err; + callback = ensureCallback(callback); } }, - rm = function rm(...args) { - callbackify(fs.rm, args); + rm = function rm(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + fs.rm(path, options).then(nullcallback(callback), callback); }, - rmdir = function rmdir(...args) { - callbackify(fs.rmdir, args); + rmdir = function rmdir(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + fs.rmdir(path, options).then(nullcallback(callback), callback); }, - copyFile = function copyFile(...args) { - const callback = ensureCallback(args[args.length - 1]); - fs.copyFile(...args).then(result => callback(null, result), callback); + copyFile = function copyFile(src, dest, mode, callback) { + if ($isCallable(mode)) { + callback = mode; + mode = 0; + } + + ensureCallback(callback); + + fs.copyFile(src, dest, mode).then(nullcallback(callback), callback); }, exists = function exists(path, callback) { - if (typeof callback !== "function") { - const err = new TypeError("Callback must be a function"); - err.code = "ERR_INVALID_ARG_TYPE"; - throw err; - } + ensureCallback(callback); + try { fs.exists.$apply(fs, [path]).then( existed => callback(existed), @@ -179,50 +216,111 @@ var access = function access(...args) { callback(false); } }, - chown = function chown(...args) { - callbackify(fs.chown, args); + chown = function chown(path, uid, gid, callback) { + ensureCallback(callback); + + fs.chown(path, uid, gid).then(nullcallback(callback), callback); }, - chmod = function chmod(...args) { - callbackify(fs.chmod, args); + chmod = function chmod(path, mode, callback) { + ensureCallback(callback); + + fs.chmod(path, mode).then(nullcallback(callback), callback); }, - fchmod = function fchmod(...args) { - callbackify(fs.fchmod, args); + fchmod = function fchmod(fd, mode, callback) { + ensureCallback(callback); + + fs.fchmod(fd, mode).then(nullcallback(callback), callback); }, - fchown = function fchown(...args) { - callbackify(fs.fchown, args); + fchown = function fchown(fd, uid, gid, callback) { + ensureCallback(callback); + + fs.fchown(fd, uid, gid).then(nullcallback(callback), callback); }, - fstat = function fstat(...args) { - callbackify(fs.fstat, args); + fstat = function fstat(fd, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + fs.fstat(fd, options).then(function (stats) { + callback(null, stats); + }, callback); }, - fsync = function fsync(...args) { - callbackify(fs.fsync, args); + fsync = function fsync(fd, callback) { + ensureCallback(callback); + + fs.fsync(fd).then(nullcallback(callback), callback); }, - ftruncate = function ftruncate(...args) { - callbackify(fs.ftruncate, args); + ftruncate = function ftruncate(fd, len, callback) { + if ($isCallable(len)) { + callback = len; + len = undefined; + } + + ensureCallback(callback); + + fs.ftruncate(fd, len).then(nullcallback(callback), callback); }, - futimes = function futimes(...args) { - callbackify(fs.futimes, args); + futimes = function futimes(fd, atime, mtime, callback) { + ensureCallback(callback); + + fs.futimes(fd, atime, mtime).then(nullcallback(callback), callback); }, - lchmod = function lchmod(...args) { - callbackify(fs.lchmod, args); + lchmod = function lchmod(path, mode, callback) { + ensureCallback(callback); + + fs.lchmod(path, mode).then(nullcallback(callback), callback); }, - lchown = function lchown(...args) { - callbackify(fs.lchown, args); + lchown = function lchown(path, uid, gid, callback) { + ensureCallback(callback); + + fs.lchown(path, uid, gid).then(nullcallback(callback), callback); }, - link = function link(...args) { - callbackify(fs.link, args); + link = function link(existingPath, newPath, callback) { + ensureCallback(callback); + + fs.link(existingPath, newPath).then(nullcallback(callback), callback); }, - mkdir = function mkdir(...args) { - callbackify(fs.mkdir, args); + mkdir = function mkdir(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.mkdir(path, options).then(nullcallback(callback), callback); }, - mkdtemp = function mkdtemp(...args) { - callbackify(fs.mkdtemp, args); + mkdtemp = function mkdtemp(prefix, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.mkdtemp(prefix, options).then(function (folder) { + callback(null, folder); + }, callback); }, - open = function open(...args) { - callbackify(fs.open, args); + open = function open(path, flags, mode, callback) { + if (arguments.length < 3) { + callback = flags; + } else if ($isCallable(mode)) { + callback = mode; + mode = undefined; + } + + ensureCallback(callback); + + fs.open(path, flags, mode).then(function (fd) { + callback(null, fd); + }, callback); }, - fdatasync = function fdatasync(...args) { - callbackify(fs.fdatasync, args); + fdatasync = function fdatasync(fd, callback) { + ensureCallback(callback); + + fs.fdatasync(fd).then(nullcallback(callback), callback); }, read = function read(fd, buffer, offsetOrOptions, length, position, callback) { let offset = offsetOrOptions; @@ -255,60 +353,151 @@ var access = function access(...args) { err => callback(err), ); }, - write = function write(...args) { - const callback = ensureCallback(args[args.length - 1]); - const promise = fs.write(...args.slice(0, -1)); - const bufferOrString = args[1]; + write = function write(fd, buffer, offsetOrOptions, length, position, callback) { + function wrapper(bytesWritten) { + callback(null, bytesWritten, buffer); + } - promise.then( - bytesWritten => callback(null, bytesWritten, bufferOrString), - err => callback(err), - ); + if ($isTypedArrayView(buffer)) { + callback ||= position || length || offsetOrOptions; + ensureCallback(callback); + + fs.write(fd, buffer, offsetOrOptions, length, position).then(wrapper, callback); + return; + } + + if (!$isCallable(position)) { + if ($isCallable(offsetOrOptions)) { + position = offsetOrOptions; + offsetOrOptions = undefined; + } else { + position = length; + } + length = "utf8"; + } + + callback = position; + ensureCallback(callback); + + fs.write(fd, buffer, offsetOrOptions, length).then(wrapper, callback); }, - readdir = function readdir(...args) { - const callback = ensureCallback(args[args.length - 1]); + readdir = function readdir(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); - fs.readdir(...args.slice(0, -1)).then(result => callback(null, result), callback); + fs.readdir(path, options).then(function (files) { + callback(null, files); + }, callback); }, - readFile = function readFile(...args) { - const callback = ensureCallback(args[args.length - 1]); - fs.readFile(...args.slice(0, -1)).then(result => callback(null, result), callback); + readFile = function readFile(path, options, callback) { + callback ||= options; + ensureCallback(callback); + + fs.readFile(path, options).then(function (data) { + callback(null, data); + }, callback); }, - writeFile = function writeFile(...args) { - callbackify(fs.writeFile, args); + writeFile = function writeFile(path, data, options, callback) { + callback ||= options; + ensureCallback(callback); + + fs.writeFile(path, data, options).then(nullcallback(callback), callback); }, - readlink = function readlink(...args) { - callbackify(fs.readlink, args); + readlink = function readlink(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.readlink(path, options).then(function (linkString) { + callback(null, linkString); + }, callback); }, - realpath = function realpath(...args) { - const callback = ensureCallback(args[args.length - 1]); - fs.realpath(...args.slice(0, -1)).then(result => callback(null, result), callback); + realpath = function realpath(p, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.realpath(p, options).then(function (resolvedPath) { + callback(null, resolvedPath); + }, callback); }, - rename = function rename(...args) { - callbackify(fs.rename, args); + rename = function rename(oldPath, newPath, callback) { + ensureCallback(callback); + + fs.rename(oldPath, newPath).then(nullcallback(callback), callback); }, - lstat = function lstat(...args) { - const callback = ensureCallback(args[args.length - 1]); - fs.lstat(...args.slice(0, -1)).then(result => callback(null, result), callback); + lstat = function lstat(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.lstat(path, options).then(function (stats) { + callback(null, stats); + }, callback); }, - stat = function stat(...args) { - const callback = ensureCallback(args[args.length - 1]); - fs.stat(...args.slice(0, -1)).then(result => callback(null, result), callback); + stat = function stat(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + fs.stat(path, options).then(function (stats) { + callback(null, stats); + }, callback); }, - symlink = function symlink(...args) { - callbackify(fs.symlink, args); + symlink = function symlink(target, path, type, callback) { + if (callback === undefined) { + callback = type; + ensureCallback(callback); + type = undefined; + } + + fs.symlink(target, path, type).then(callback, callback); }, - truncate = function truncate(...args) { - callbackify(fs.truncate, args); + truncate = function truncate(path, len, callback) { + if (typeof path === "number") { + // Apparently, node supports this + ftruncate(path, len, callback); + return; + } + + if ($isCallable(len)) { + callback = len; + len = undefined; + } + + ensureCallback(callback); + fs.truncate(path, len).then(nullcallback(callback), callback); }, - unlink = function unlink(...args) { - callbackify(fs.unlink, args); + unlink = function unlink(path, callback) { + ensureCallback(callback); + + fs.unlink(path).then(nullcallback(callback), callback); }, - utimes = function utimes(...args) { - callbackify(fs.utimes, args); + utimes = function utimes(path, atime, mtime, callback) { + ensureCallback(callback); + + fs.utimes(path, atime, mtime).then(nullcallback(callback), callback); }, - lutimes = function lutimes(...args) { - callbackify(fs.lutimes, args); + lutimes = function lutimes(path, atime, mtime, callback) { + ensureCallback(callback); + + fs.lutimes(path, atime, mtime).then(nullcallback(callback), callback); }, accessSync = fs.accessSync.bind(fs), appendFileSync = fs.appendFileSync.bind(fs), @@ -361,9 +550,7 @@ var access = function access(...args) { position = null; } - if (!$isCallable(callback)) { - throw new TypeError("callback must be a function"); - } + callback = ensureCallback(callback); fs.writev(fd, buffers, position).$then(bytesWritten => callback(null, bytesWritten, buffers), callback); }, @@ -374,9 +561,7 @@ var access = function access(...args) { position = null; } - if (!$isCallable(callback)) { - throw new TypeError("callback must be a function"); - } + callback = ensureCallback(callback); fs.readv(fd, buffers, position).$then(bytesRead => callback(null, bytesRead, buffers), callback); }, @@ -386,8 +571,17 @@ var access = function access(...args) { watch = function watch(path, options, listener) { return new FSWatcher(path, options, listener); }, - opendir = function opendir(...args) { - callbackify(promises.opendir, args); + opendir = function opendir(path, options, callback) { + if ($isCallable(options)) { + callback = options; + options = undefined; + } + + ensureCallback(callback); + + promises.opendir(path, options).then(function (dir) { + callback(null, dir); + }, callback); }; // TODO: make symbols a separate export somewhere @@ -668,8 +862,7 @@ function ReadStream(this: typeof ReadStream, pathOrFd, options) { $assert(overridden_fs); this[kFs] = overridden_fs; } -ReadStream.prototype = {}; -ObjectSetPrototypeOf(ReadStream.prototype, NativeReadable.prototype); +ReadStream.prototype = Object.create(NativeReadable.prototype); ReadStream.prototype._construct = function (callback) { if (NativeReadablePrototype._construct) { @@ -1323,4 +1516,108 @@ export default { // get ReadStream() { // return getLazyReadStream(); // }, + F_OK: 0, + R_OK: 4, + W_OK: 2, + X_OK: 1, }; + +// Preserve the names +function setName(fn, value) { + Object.$defineProperty(fn, "name", { value, enumerable: false, configurable: true }); +} +setName(Dirent, "Dirent"); +setName(FSWatcher, "FSWatcher"); +setName(ReadStream, "ReadStream"); +setName(Stats, "Stats"); +setName(WriteStream, "WriteStream"); +setName(_toUnixTimestamp, "_toUnixTimestamp"); +setName(access, "access"); +setName(accessSync, "accessSync"); +setName(appendFile, "appendFile"); +setName(appendFileSync, "appendFileSync"); +setName(chmod, "chmod"); +setName(chmodSync, "chmodSync"); +setName(chown, "chown"); +setName(chownSync, "chownSync"); +setName(close, "close"); +setName(closeSync, "closeSync"); +setName(constants, "constants"); +setName(copyFile, "copyFile"); +setName(copyFileSync, "copyFileSync"); +setName(cp, "cp"); +setName(cpSync, "cpSync"); +setName(createReadStream, "createReadStream"); +setName(createWriteStream, "createWriteStream"); +setName(exists, "exists"); +setName(existsSync, "existsSync"); +setName(fchmod, "fchmod"); +setName(fchmodSync, "fchmodSync"); +setName(fchown, "fchown"); +setName(fchownSync, "fchownSync"); +setName(fstat, "fstat"); +setName(fstatSync, "fstatSync"); +setName(fsync, "fsync"); +setName(fsyncSync, "fsyncSync"); +setName(ftruncate, "ftruncate"); +setName(ftruncateSync, "ftruncateSync"); +setName(futimes, "futimes"); +setName(futimesSync, "futimesSync"); +setName(lchmod, "lchmod"); +setName(lchmodSync, "lchmodSync"); +setName(lchown, "lchown"); +setName(lchownSync, "lchownSync"); +setName(link, "link"); +setName(linkSync, "linkSync"); +setName(lstat, "lstat"); +setName(lstatSync, "lstatSync"); +setName(lutimes, "lutimes"); +setName(lutimesSync, "lutimesSync"); +setName(mkdir, "mkdir"); +setName(mkdirSync, "mkdirSync"); +setName(mkdtemp, "mkdtemp"); +setName(mkdtempSync, "mkdtempSync"); +setName(open, "open"); +setName(openSync, "openSync"); +setName(promises, "promises"); +setName(read, "read"); +setName(readFile, "readFile"); +setName(readFileSync, "readFileSync"); +setName(readSync, "readSync"); +setName(readdir, "readdir"); +setName(readdirSync, "readdirSync"); +setName(readlink, "readlink"); +setName(readlinkSync, "readlinkSync"); +setName(readv, "readv"); +setName(readvSync, "readvSync"); +setName(realpath, "realpath"); +setName(realpathSync, "realpathSync"); +setName(rename, "rename"); +setName(renameSync, "renameSync"); +setName(rm, "rm"); +setName(rmSync, "rmSync"); +setName(rmdir, "rmdir"); +setName(rmdirSync, "rmdirSync"); +setName(stat, "stat"); +setName(statSync, "statSync"); +setName(symlink, "symlink"); +setName(symlinkSync, "symlinkSync"); +setName(truncate, "truncate"); +setName(truncateSync, "truncateSync"); +setName(unlink, "unlink"); +setName(unlinkSync, "unlinkSync"); +setName(unwatchFile, "unwatchFile"); +setName(utimes, "utimes"); +setName(utimesSync, "utimesSync"); +setName(watch, "watch"); +setName(watchFile, "watchFile"); +setName(write, "write"); +setName(writeFile, "writeFile"); +setName(writeFileSync, "writeFileSync"); +setName(writeSync, "writeSync"); +setName(writev, "writev"); +setName(writevSync, "writevSync"); +setName(fdatasync, "fdatasync"); +setName(fdatasyncSync, "fdatasyncSync"); +setName(openAsBlob, "openAsBlob"); +setName(opendir, "opendir"); diff --git a/src/js/node/http.ts b/src/js/node/http.ts index 87e1a473387b8..9a62f32822fc8 100644 --- a/src/js/node/http.ts +++ b/src/js/node/http.ts @@ -1,16 +1,48 @@ // Hardcoded module "node:http" const EventEmitter = require("node:events"); const { isTypedArray } = require("node:util/types"); -const { Duplex, Readable, Writable, ERR_STREAM_WRITE_AFTER_END, ERR_STREAM_ALREADY_FINISHED } = require("node:stream"); +const { Duplex, Readable, Writable } = require("node:stream"); +const { ERR_INVALID_ARG_TYPE, ERR_INVALID_PROTOCOL } = require("internal/errors"); +const { isPrimary } = require("internal/cluster/isPrimary"); +const { kAutoDestroyed } = require("internal/shared"); +const { urlToHttpOptions } = require("internal/url"); const { getHeader, setHeader, assignHeaders: assignHeadersFast, -} = $cpp("NodeHTTP.cpp", "createNodeHTTPInternalBinding"); + assignEventCallback, + setRequestTimeout, + setServerIdleTimeout, + Response, + Request, + Headers, + Blob, + headersTuple, +} = $cpp("NodeHTTP.cpp", "createNodeHTTPInternalBinding") as { + getHeader: (headers: Headers, name: string) => string | undefined; + setHeader: (headers: Headers, name: string, value: string) => void; + assignHeaders: (object: any, req: Request, headersTuple: any) => boolean; + assignEventCallback: (req: Request, callback: (event: number) => void) => void; + setRequestTimeout: (req: Request, timeout: number) => void; + setServerIdleTimeout: (server: any, timeout: number) => void; + Response: (typeof globalThis)["Response"]; + Request: (typeof globalThis)["Request"]; + Headers: (typeof globalThis)["Headers"]; + Blob: (typeof globalThis)["Blob"]; + headersTuple: any; +}; + +let cluster; +const sendHelper = $newZigFunction("node_cluster_binding.zig", "sendHelperChild", 3); +const getBunServerAllClosedPromise = $newZigFunction("node_http_binding.zig", "getBunServerAllClosedPromise", 1); + +// TODO: make this more robust. +function isAbortError(err) { + return err?.name === "AbortError"; +} const ObjectDefineProperty = Object.defineProperty; -const ObjectSetPrototypeOf = Object.setPrototypeOf; const GlobalPromise = globalThis.Promise; const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; @@ -46,27 +78,6 @@ function ERR_HTTP_SOCKET_ASSIGNED() { return new Error(`ServerResponse has an already assigned socket`); } -// Cheaper to duplicate this than to import it from node:net -function isIPv6(input) { - const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; - const v4Str = `(${v4Seg}[.]){3}${v4Seg}`; - const v6Seg = "(?:[0-9a-fA-F]{1,4})"; - const IPv6Reg = new RegExp( - "^(" + - `(?:${v6Seg}:){7}(?:${v6Seg}|:)|` + - `(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` + - `(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` + - `(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` + - `(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` + - `(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` + - `(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` + - `(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` + - ")(%[0-9a-zA-Z-.:]{1,})?$", - ); - - return IPv6Reg.test(input); -} - // TODO: add primordial for URL // Importing from node:url is unnecessary const { URL } = globalThis; @@ -77,13 +88,10 @@ const fetch = Bun.fetch; const nop = () => {}; const kEmptyObject = Object.freeze(Object.create(null)); -const kOutHeaders = Symbol.for("kOutHeaders"); const kEndCalled = Symbol.for("kEndCalled"); const kAbortController = Symbol.for("kAbortController"); const kClearTimeout = Symbol("kClearTimeout"); - -const kCorked = Symbol.for("kCorked"); -const searchParamsSymbol = Symbol.for("query"); // This is the symbol used in Node +const kRealListen = Symbol("kRealListen"); // Primordials const StringPrototypeSlice = String.prototype.slice; @@ -96,9 +104,9 @@ const INVALID_PATH_REGEX = /[^\u0021-\u00ff]/; const NODE_HTTP_WARNING = "WARN: Agent is mostly unused in Bun's implementation of http. If you see strange behavior, this is probably the cause."; -var _defaultHTTPSAgent; var kInternalRequest = Symbol("kInternalRequest"); const kInternalSocketData = Symbol.for("::bunternal::"); +const serverSymbol = Symbol.for("::bunternal::"); const kfakeSocket = Symbol("kfakeSocket"); const kEmptyBuffer = Buffer.alloc(0); @@ -116,23 +124,16 @@ function isValidTLSArray(obj) { return false; } -class ERR_INVALID_ARG_TYPE extends TypeError { - constructor(name, expected, actual) { - super(`The ${name} argument must be of type ${expected}. Received type ${typeof actual}`); - this.code = "ERR_INVALID_ARG_TYPE"; - } -} - function validateMsecs(numberlike: any, field: string) { if (typeof numberlike !== "number" || numberlike < 0) { - throw new ERR_INVALID_ARG_TYPE(field, "number", numberlike); + throw ERR_INVALID_ARG_TYPE(field, "number", numberlike); } return numberlike; } function validateFunction(callable: any, field: string) { if (typeof callable !== "function") { - throw new ERR_INVALID_ARG_TYPE(field, "Function", callable); + throw ERR_INVALID_ARG_TYPE(field, "Function", callable); } return callable; @@ -140,7 +141,7 @@ function validateFunction(callable: any, field: string) { type FakeSocket = InstanceType; var FakeSocket = class Socket extends Duplex { - [kInternalSocketData]!: [import("bun").Server, typeof OutgoingMessage, typeof Request]; + [kInternalSocketData]!: [typeof Server, typeof OutgoingMessage, typeof Request]; bytesRead = 0; bytesWritten = 0; connecting = false; @@ -151,7 +152,8 @@ var FakeSocket = class Socket extends Duplex { address() { // Call server.requestIP() without doing any propety getter twice. var internalData; - return (this.#address ??= (internalData = this[kInternalSocketData])?.[0]?.requestIP(internalData[2]) ?? {}); + return (this.#address ??= + (internalData = this[kInternalSocketData])?.[0]?.[serverSymbol].requestIP(internalData[2]) ?? {}); } get bufferSize() { @@ -162,7 +164,11 @@ var FakeSocket = class Socket extends Duplex { return this; } - _destroy(err, callback) {} + _destroy(err, callback) { + const socketData = this[kInternalSocketData]; + if (!socketData) return; // sometimes 'this' is Socket not FakeSocket + if (!socketData[1]["req"][kAutoDestroyed]) socketData[1].end(); + } _final(callback) {} @@ -233,6 +239,11 @@ var FakeSocket = class Socket extends Duplex { } setTimeout(timeout, callback) { + const socketData = this[kInternalSocketData]; + if (!socketData) return; // sometimes 'this' is Socket not FakeSocket + + const [server, http_res, req] = socketData; + http_res?.req?.setTimeout(timeout, callback); return this; } @@ -259,9 +270,9 @@ function Agent(options = kEmptyObject) { if (options.noDelay === undefined) options.noDelay = true; // Don't confuse net and make it think that we're connecting to a pipe - this.requests = kEmptyObject; - this.sockets = kEmptyObject; - this.freeSockets = kEmptyObject; + this.requests = Object.create(null); + this.sockets = Object.create(null); + this.freeSockets = Object.create(null); this.keepAliveMsecs = options.keepAliveMsecs || 1000; this.keepAlive = options.keepAlive || false; @@ -273,8 +284,7 @@ function Agent(options = kEmptyObject) { this.defaultPort = options.defaultPort || 80; this.protocol = options.protocol || "http:"; } -Agent.prototype = {}; -ObjectSetPrototypeOf(Agent.prototype, EventEmitter.prototype); +Agent.prototype = Object.create(EventEmitter.prototype); ObjectDefineProperty(Agent, "globalAgent", { get: function () { @@ -331,35 +341,25 @@ Agent.prototype.destroy = function () { $debug(`${NODE_HTTP_WARNING}\n`, "WARN: Agent.destroy is a no-op"); }; -function emitListeningNextTick(self, onListen, err, hostname, port) { - if (typeof onListen === "function") { - try { - onListen.$apply(self, [err, hostname, port]); - } catch (err) { - self.emit("error", err); - } - } - - self.listening = !err; - - if (err) { - self.emit("error", err); - } else { - self.emit("listening", hostname, port); +function emitListeningNextTick(self, hostname, port) { + if ((self.listening = !!self[serverSymbol])) { + // TODO: remove the arguments + // Note does not pass any arguments. + self.emit("listening", null, hostname, port); } } var tlsSymbol = Symbol("tls"); var isTlsSymbol = Symbol("is_tls"); var optionsSymbol = Symbol("options"); -var serverSymbol = Symbol("server"); + function Server(options, callback) { if (!(this instanceof Server)) return new Server(options, callback); EventEmitter.$call(this); this.listening = false; this._unref = false; - this[serverSymbol] = undefined; + this[kInternalSocketData] = undefined; if (typeof options === "function") { callback = options; @@ -431,200 +431,300 @@ function Server(options, callback) { if (callback) this.on("request", callback); return this; } -Object.setPrototypeOf((Server.prototype = {}), EventEmitter.prototype); -Server.prototype.constructor = Server; // Re-add constructor which got lost when setting prototype -Object.setPrototypeOf(Server, EventEmitter); - -Server.prototype.ref = function () { - this._unref = false; - this[serverSymbol]?.ref?.(); - return this; -}; -Server.prototype.unref = function () { - this._unref = true; - this[serverSymbol]?.unref?.(); - return this; -}; - -Server.prototype.closeAllConnections = function () { - const server = this[serverSymbol]; - if (!server) { - return; +function onRequestEvent(event) { + const [server, http_res, req] = this.socket[kInternalSocketData]; + if (!http_res[finishedSymbol]) { + switch (event) { + case 0: // timeout + this.emit("timeout"); + server.emit("timeout", req.socket); + break; + case 1: // abort + this.complete = true; + this.emit("close"); + http_res[finishedSymbol] = true; + break; + } } - this[serverSymbol] = undefined; - server.stop(true); - this.emit("close"); -}; +} -Server.prototype.closeIdleConnections = function () { - // not actually implemented -}; +Server.prototype = { + ref() { + this._unref = false; + this[serverSymbol]?.ref?.(); + return this; + }, -Server.prototype.close = function (optionalCallback?) { - const server = this[serverSymbol]; - if (!server) { - if (typeof optionalCallback === "function") process.nextTick(optionalCallback, new Error("Server is not running")); - return; - } - this[serverSymbol] = undefined; - if (typeof optionalCallback === "function") this.once("close", optionalCallback); - server.stop(); - this.emit("close"); -}; + unref() { + this._unref = true; + this[serverSymbol]?.unref?.(); + return this; + }, -Server.prototype[Symbol.asyncDispose] = function () { - const { resolve, reject, promise } = Promise.withResolvers(); - this.close(function (err, ...args) { - if (err) reject(err); - else resolve(...args); - }); - return promise; -}; + closeAllConnections() { + const server = this[serverSymbol]; + if (!server) { + return; + } + this[serverSymbol] = undefined; + server.stop(true); + }, -Server.prototype.address = function () { - if (!this[serverSymbol]) return null; - return this[serverSymbol].address; -}; + closeIdleConnections() { + // not actually implemented + }, -Server.prototype.listen = function (port, host, backlog, onListen) { - const server = this; - let socketPath; - if (typeof port == "string" && !Number.isSafeInteger(Number(port))) { - socketPath = port; - } - if (typeof host === "function") { - onListen = host; - host = undefined; - } + close(optionalCallback?) { + const server = this[serverSymbol]; + if (!server) { + if (typeof optionalCallback === "function") + process.nextTick(optionalCallback, new Error("Server is not running")); + return; + } + this[serverSymbol] = undefined; + if (typeof optionalCallback === "function") this.once("close", optionalCallback); + server.stop(); + }, - if (typeof port === "function") { - onListen = port; - } else if (typeof port === "object") { - port?.signal?.addEventListener("abort", () => { - this.close(); + [Symbol.asyncDispose]() { + const { resolve, reject, promise } = Promise.withResolvers(); + this.close(function (err, ...args) { + if (err) reject(err); + else resolve(...args); }); + return promise; + }, - host = port?.host; - port = port?.port; + address() { + if (!this[serverSymbol]) return null; + return this[serverSymbol].address; + }, - if (typeof port?.callback === "function") onListen = port?.callback; - } + listen() { + const server = this; + let port, host, onListen; + let socketPath; + let tls = this[tlsSymbol]; + + // This logic must align with: + // - https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/net.js#L274-L307 + if (arguments.length > 0) { + if (($isObject(arguments[0]) || $isCallable(arguments[0])) && arguments[0] !== null) { + // (options[...][, cb]) + port = arguments[0].port; + host = arguments[0].host; + socketPath = arguments[0].path; + + const otherTLS = arguments[0].tls; + if (otherTLS && $isObject(otherTLS)) { + tls = otherTLS; + } + } else if (typeof arguments[0] === "string" && !(Number(arguments[0]) >= 0)) { + // (path[...][, cb]) + socketPath = arguments[0]; + } else { + // ([port][, host][...][, cb]) + port = arguments[0]; + if (arguments.length > 1 && typeof arguments[1] === "string") { + host = arguments[1]; + } + } + } - if (typeof backlog === "function") { - onListen = backlog; - } + // Bun defaults to port 3000. + // Node defaults to port 0. + if (port === undefined && !socketPath) { + port = 0; + } - const ResponseClass = this[optionsSymbol].ServerResponse || ServerResponse; - const RequestClass = this[optionsSymbol].IncomingMessage || IncomingMessage; - let isHTTPS = false; + if (typeof port === "string") { + const portNumber = parseInt(port); + if (!Number.isNaN(portNumber)) { + port = portNumber; + } + } - try { - const tls = this[tlsSymbol]; - if (tls) { - this.serverName = tls.serverName || host || "localhost"; - } - this[serverSymbol] = Bun.serve({ - tls, - port, - hostname: host, - unix: socketPath, - // Bindings to be used for WS Server - websocket: { - open(ws) { - ws.data.open(ws); - }, - message(ws, message) { - ws.data.message(ws, message); - }, - close(ws, code, reason) { - ws.data.close(ws, code, reason); - }, - drain(ws) { - ws.data.drain(ws); - }, - ping(ws, data) { - ws.data.ping(ws, data); - }, - pong(ws, data) { - ws.data.pong(ws, data); - }, - }, - maxRequestBodySize: Number.MAX_SAFE_INTEGER, - // Be very careful not to access (web) Request object - // properties: - // - request.url - // - request.headers - // - // We want to avoid triggering the getter for these properties because - // that will cause the data to be cloned twice, which costs memory & performance. - fetch(req, _server) { - var pendingResponse; - var pendingError; - var reject = err => { - if (pendingError) return; - pendingError = err; - if (rejectFunction) rejectFunction(err); - }; + if ($isCallable(arguments[arguments.length - 1])) { + onListen = arguments[arguments.length - 1]; + } + + try { + // listenInCluster + + if (isPrimary) { + server[kRealListen](tls, port, host, socketPath, false, onListen); + return this; + } - var reply = function (resp) { - if (pendingResponse) return; - pendingResponse = resp; - if (resolveFunction) resolveFunction(resp); + if (cluster === undefined) cluster = require("node:cluster"); + + // TODO: our net.Server and http.Server use different Bun APIs and our IPC doesnt support sending and receiving handles yet. use reusePort instead for now. + + // const serverQuery = { + // // address: address, + // port: port, + // addressType: 4, + // // fd: fd, + // // flags, + // // backlog, + // // ...options, + // }; + // cluster._getServer(server, serverQuery, function listenOnPrimaryHandle(err, handle) { + // // err = checkBindError(err, port, handle); + // // if (err) { + // // throw new ExceptionWithHostPort(err, "bind", address, port); + // // } + // if (err) { + // throw err; + // } + // server[kRealListen](port, host, socketPath, onListen); + // }); + + server.once("listening", () => { + cluster.worker.state = "listening"; + const address = server.address(); + const message = { + act: "listening", + port: (address && address.port) || port, + data: null, + addressType: 4, }; + sendHelper(message, null); + }); - const prevIsNextIncomingMessageHTTPS = isNextIncomingMessageHTTPS; - isNextIncomingMessageHTTPS = isHTTPS; - const http_req = new RequestClass(req); - isNextIncomingMessageHTTPS = prevIsNextIncomingMessageHTTPS; + server[kRealListen](tls, port, host, socketPath, true, onListen); + } catch (err) { + setTimeout(() => server.emit("error", err), 1); + } - const upgrade = http_req.headers.upgrade; + return this; + }, - const http_res = new ResponseClass(http_req, reply); + [kRealListen](tls, port, host, socketPath, reusePort, onListen) { + { + const ResponseClass = this[optionsSymbol].ServerResponse || ServerResponse; + const RequestClass = this[optionsSymbol].IncomingMessage || IncomingMessage; + let isHTTPS = false; + let server = this; - http_req.socket[kInternalSocketData] = [_server, http_res, req]; - server.emit("connection", http_req.socket); + if (tls) { + this.serverName = tls.serverName || host || "localhost"; + } + this[serverSymbol] = Bun.serve({ + idleTimeout: 0, // nodejs dont have a idleTimeout by default + tls, + port, + hostname: host, + unix: socketPath, + reusePort, + // Bindings to be used for WS Server + websocket: { + open(ws) { + ws.data.open(ws); + }, + message(ws, message) { + ws.data.message(ws, message); + }, + close(ws, code, reason) { + ws.data.close(ws, code, reason); + }, + drain(ws) { + ws.data.drain(ws); + }, + ping(ws, data) { + ws.data.ping(ws, data); + }, + pong(ws, data) { + ws.data.pong(ws, data); + }, + }, + maxRequestBodySize: Number.MAX_SAFE_INTEGER, + // Be very careful not to access (web) Request object + // properties: + // - request.url + // - request.headers + // + // We want to avoid triggering the getter for these properties because + // that will cause the data to be cloned twice, which costs memory & performance. + fetch(req, _server) { + var pendingResponse; + var pendingError; + var reject = err => { + if (pendingError) return; + pendingError = err; + if (rejectFunction) rejectFunction(err); + }; + + var reply = function (resp) { + if (pendingResponse) return; + pendingResponse = resp; + if (resolveFunction) resolveFunction(resp); + }; + + const prevIsNextIncomingMessageHTTPS = isNextIncomingMessageHTTPS; + isNextIncomingMessageHTTPS = isHTTPS; + const http_req = new RequestClass(req); + assignEventCallback(req, onRequestEvent.bind(http_req)); + isNextIncomingMessageHTTPS = prevIsNextIncomingMessageHTTPS; + + const upgrade = http_req.headers.upgrade; + + const http_res = new ResponseClass(http_req, reply); + + http_req.socket[kInternalSocketData] = [server, http_res, req]; + server.emit("connection", http_req.socket); + + const rejectFn = err => reject(err); + http_req.once("error", rejectFn); + http_res.once("error", rejectFn); + + if (upgrade) { + server.emit("upgrade", http_req, http_req.socket, kEmptyBuffer); + } else { + server.emit("request", http_req, http_res); + } - const rejectFn = err => reject(err); - http_req.once("error", rejectFn); - http_res.once("error", rejectFn); + if (pendingError) { + throw pendingError; + } - if (upgrade) { - server.emit("upgrade", http_req, http_req.socket, kEmptyBuffer); - } else { - server.emit("request", http_req, http_res); - } + if (pendingResponse) { + return pendingResponse; + } - if (pendingError) { - throw pendingError; - } + var { promise, resolve: resolveFunction, reject: rejectFunction } = $newPromiseCapability(GlobalPromise); + return promise; + }, + }); + getBunServerAllClosedPromise(this[serverSymbol]).$then(emitCloseNTServer.bind(this)); + isHTTPS = this[serverSymbol].protocol === "https"; - if (pendingResponse) { - return pendingResponse; - } + if (this?._unref) { + this[serverSymbol]?.unref?.(); + } - var { promise, resolve: resolveFunction, reject: rejectFunction } = $newPromiseCapability(GlobalPromise); - return promise; - }, - }); - isHTTPS = this[serverSymbol].protocol === "https"; + if ($isCallable(onListen)) { + this.once("listening", onListen); + } - if (this?._unref) { - this[serverSymbol]?.unref?.(); + setTimeout(emitListeningNextTick, 1, this, this[serverSymbol].hostname, this[serverSymbol].port); } + }, - setTimeout(emitListeningNextTick, 1, this, onListen, null, this[serverSymbol].hostname, this[serverSymbol].port); - } catch (err) { - server.emit("error", err); - } - - return this; -}; + setTimeout(msecs, callback) { + const server = this[serverSymbol]; + if (server) { + setServerIdleTimeout(server, Math.ceil(msecs / 1000)); + typeof callback === "function" && this.once("timeout", callback); + } + return this; + }, -Server.prototype.setTimeout = function (msecs, callback) { - // TODO: - return this; + constructor: Server, }; +$setPrototypeDirect.$call(Server.prototype, EventEmitter.prototype); +$setPrototypeDirect.$call(Server, EventEmitter); function assignHeadersSlow(object, req) { const headers = req.headers; @@ -664,10 +764,13 @@ function assignHeadersSlow(object, req) { function assignHeaders(object, req) { // This fast path is an 8% speedup for a "hello world" node:http server, and a 7% speedup for a "hello world" express server - const tuple = assignHeadersFast(req, object); - if (tuple !== null) { - object.headers = $getInternalField(tuple, 0); - object.rawHeaders = $getInternalField(tuple, 1); + if (assignHeadersFast(req, object, headersTuple)) { + const headers = $getInternalField(headersTuple, 0); + const rawHeaders = $getInternalField(headersTuple, 1); + $putInternalField(headersTuple, 0, undefined); + $putInternalField(headersTuple, 1, undefined); + object.headers = headers; + object.rawHeaders = rawHeaders; return true; } else { assignHeadersSlow(object, req); @@ -677,10 +780,6 @@ function assignHeaders(object, req) { var defaultIncomingOpts = { type: "request" }; -function getDefaultHTTPSAgent() { - return (_defaultHTTPSAgent ??= new Agent({ defaultPort: 443, protocol: "https:" })); -} - function requestHasNoBody(method, req) { if ("GET" === method || "HEAD" === method || "TRACE" === method || "CONNECT" === method || "OPTIONS" === method) return true; @@ -705,6 +804,7 @@ function IncomingMessage(req, defaultIncomingOpts) { this._dumped = false; this[noBodySymbol] = false; this[abortedSymbol] = false; + this.complete = false; Readable.$call(this); var { type = "request", [kInternalRequest]: nodeReq } = defaultIncomingOpts || {}; @@ -734,137 +834,178 @@ function IncomingMessage(req, defaultIncomingOpts) { type === "request" // TODO: Add logic for checking for body on response ? requestHasNoBody(this.method, this) : false; - - this.complete = !!this[noBodySymbol]; } -Object.setPrototypeOf((IncomingMessage.prototype = {}), Readable.prototype); -IncomingMessage.prototype.constructor = IncomingMessage; // Re-add constructor which got lost when setting prototype -Object.setPrototypeOf(IncomingMessage, Readable); +IncomingMessage.prototype = { + constructor: IncomingMessage, + _construct(callback) { + // TODO: streaming + if (this[typeSymbol] === "response" || this[noBodySymbol]) { + callback(); + return; + } -IncomingMessage.prototype._construct = function (callback) { - // TODO: streaming - if (this[typeSymbol] === "response" || this[noBodySymbol]) { - callback(); - return; - } + const contentLength = this.headers["content-length"]; + const length = contentLength ? parseInt(contentLength, 10) : 0; + if (length === 0) { + this[noBodySymbol] = true; + callback(); + return; + } - const contentLength = this.headers["content-length"]; - const length = contentLength ? parseInt(contentLength, 10) : 0; - if (length === 0) { - this[noBodySymbol] = true; callback(); - return; - } - - callback(); -}; + }, + _read(size) { + if (this[noBodySymbol]) { + this.complete = true; + this.push(null); + } else if (this[bodyStreamSymbol] == null) { + const reader = this[reqSymbol].body?.getReader() as ReadableStreamDefaultReader; + if (!reader) { + this.complete = true; + this.push(null); + return; + } + this[bodyStreamSymbol] = reader; + consumeStream(this, reader); + } + }, + _destroy(err, cb) { + if (!this.readableEnded || !this.complete) { + this[abortedSymbol] = true; + // IncomingMessage emits 'aborted'. + // Client emits 'abort'. + this.emit("aborted"); + } -async function consumeStream(self, reader: ReadableStreamDefaultReader) { - while (true) { - var { done, value } = await reader.readMany(); - if (self[abortedSymbol]) return; - if (done) { - self.complete = true; - self.push(null); - break; + // Suppress "AbortError" from fetch() because we emit this in the 'aborted' event + if (isAbortError(err)) { + err = undefined; } - for (var v of value) { - self.push(v); + + const stream = this[bodyStreamSymbol]; + this[bodyStreamSymbol] = undefined; + const streamState = stream?.$state; + + if (streamState === $streamReadable || streamState === $streamWaiting || streamState === $streamWritable) { + stream?.cancel?.().catch(nop); } - } -} -IncomingMessage.prototype._read = function (size) { - if (this[noBodySymbol]) { - this.complete = true; - this.push(null); - } else if (this[bodyStreamSymbol] == null) { - const reader = this[reqSymbol].body?.getReader() as ReadableStreamDefaultReader; - if (!reader) { - this.complete = true; - this.push(null); - return; + const socket = this[fakeSocketSymbol]; + if (socket) { + socket.destroy(err); } - this[bodyStreamSymbol] = reader; - consumeStream(this, reader); - } -}; -Object.defineProperty(IncomingMessage.prototype, "aborted", { - get() { + if (cb) { + emitErrorNextTick(this, err, cb); + } + }, + get aborted() { return this[abortedSymbol]; }, -}); - -Object.defineProperty(IncomingMessage.prototype, "connection", { - get() { + set aborted(value) { + this[abortedSymbol] = value; + }, + get connection() { return (this[fakeSocketSymbol] ??= new FakeSocket()); }, -}); - -Object.defineProperty(IncomingMessage.prototype, "statusCode", { - get() { + get statusCode() { return this[reqSymbol].status; }, - set(v) { - if (!(v in STATUS_CODES)) return; - this[reqSymbol].status = v; + set statusCode(value) { + if (!(value in STATUS_CODES)) return; + this[reqSymbol].status = value; }, -}); - -Object.defineProperty(IncomingMessage.prototype, "statusMessage", { - get() { + get statusMessage() { return STATUS_CODES[this[reqSymbol].status]; }, - set(v) { - //noop + set statusMessage(value) { + // noop }, -}); - -Object.defineProperty(IncomingMessage.prototype, "httpVersion", { - get() { + get httpVersion() { return "1.1"; }, -}); - -Object.defineProperty(IncomingMessage.prototype, "rawTrailers", { - get() { - return []; + set httpVersion(value) { + // noop }, -}); - -Object.defineProperty(IncomingMessage.prototype, "httpVersionMajor", { - get() { + get httpVersionMajor() { return 1; }, -}); - -Object.defineProperty(IncomingMessage.prototype, "httpVersionMinor", { - get() { + set httpVersionMajor(value) { + // noop + }, + get httpVersionMinor() { return 1; }, -}); - -Object.defineProperty(IncomingMessage.prototype, "trailers", { - get() { + set httpVersionMinor(value) { + // noop + }, + get rawTrailers() { + return []; + }, + set rawTrailers(value) { + // noop + }, + get trailers() { return kEmptyObject; }, -}); - -Object.defineProperty(IncomingMessage.prototype, "socket", { - get() { + set trailers(value) { + // noop + }, + setTimeout(msecs, callback) { + const req = this[reqSymbol]; + if (req) { + setRequestTimeout(req, Math.ceil(msecs / 1000)); + typeof callback === "function" && this.once("timeout", callback); + } + return this; + }, + get socket() { return (this[fakeSocketSymbol] ??= new FakeSocket()); }, - set(val) { - this[fakeSocketSymbol] = val; + set socket(value) { + this[fakeSocketSymbol] = value; }, -}); - -IncomingMessage.prototype.setTimeout = function (msecs, callback) { - // TODO: - return this; }; +$setPrototypeDirect.$call(IncomingMessage.prototype, Readable.prototype); +$setPrototypeDirect.$call(IncomingMessage, Readable); + +async function consumeStream(self, reader: ReadableStreamDefaultReader) { + var done = false, + value, + aborted = false; + try { + while (true) { + const result = reader.readMany(); + if ($isPromise(result)) { + ({ done, value } = await result); + } else { + ({ done, value } = result); + } + + if (self.destroyed || (aborted = self[abortedSymbol])) { + break; + } + for (var v of value) { + self.push(v); + } + + if (self.destroyed || (aborted = self[abortedSymbol]) || done) { + break; + } + } + } catch (err) { + if (aborted || self.destroyed) return; + self.destroy(err); + } finally { + reader?.cancel?.().catch?.(nop); + } + + if (!self.complete) { + self.complete = true; + self.push(null); + } +} const headersSymbol = Symbol("headers"); const finishedSymbol = Symbol("finished"); @@ -879,9 +1020,9 @@ function OutgoingMessage(options) { this[kAbortController] = null; } -Object.setPrototypeOf((OutgoingMessage.prototype = {}), Writable.prototype); +$setPrototypeDirect.$call((OutgoingMessage.prototype = {}), Writable.prototype); OutgoingMessage.prototype.constructor = OutgoingMessage; // Re-add constructor which got lost when setting prototype -Object.setPrototypeOf(OutgoingMessage, Writable); +$setPrototypeDirect.$call(OutgoingMessage, Writable); // Express "compress" package uses this OutgoingMessage.prototype._implicitHeader = function () {}; @@ -1024,6 +1165,16 @@ Object.defineProperty(OutgoingMessage.prototype, "finished", { }, }); +function emitContinueAndSocketNT(self) { + if (self.destroyed) return; + // Ref: https://github.com/nodejs/node/blob/f63e8b7fa7a4b5e041ddec67307609ec8837154f/lib/_http_client.js#L803-L839 + self.emit("socket", self.socket); + + //Emit continue event for the client (internally we auto handle it) + if (!self._closed && self.getHeader("expect") === "100-continue") { + self.emit("continue"); + } +} function emitCloseNT(self) { if (!self._closed) { self._closed = true; @@ -1031,6 +1182,10 @@ function emitCloseNT(self) { } } +function emitRequestCloseNT(self) { + self.emit("close"); +} + function onServerResponseClose() { // EventEmitter.emit makes a copy of the 'close' listeners array before // calling the listeners. detachSocket() unregisters onServerResponseClose @@ -1083,9 +1238,9 @@ function ServerResponse(req, reply) { // https://github.com/nodejs/node/blob/cf8c6994e0f764af02da4fa70bc5962142181bf3/lib/_http_server.js#L192 if (req.method === "HEAD") this._hasBody = false; } -Object.setPrototypeOf((ServerResponse.prototype = {}), OutgoingMessage.prototype); +$setPrototypeDirect.$call((ServerResponse.prototype = {}), OutgoingMessage.prototype); ServerResponse.prototype.constructor = ServerResponse; // Re-add constructor which got lost when setting prototype -Object.setPrototypeOf(ServerResponse, OutgoingMessage); +$setPrototypeDirect.$call(ServerResponse, OutgoingMessage); // Express "compress" package uses this ServerResponse.prototype._implicitHeader = function () { @@ -1162,10 +1317,14 @@ function drainHeadersIfObservable() { } ServerResponse.prototype._final = function (callback) { + const req = this.req; + const shouldEmitClose = req && req.emit && !this[finishedSymbol]; + if (!this.headersSent) { var data = this[firstWriteSymbol] || ""; this[firstWriteSymbol] = undefined; this[finishedSymbol] = true; + this.headersSent = true; // https://github.com/oven-sh/bun/issues/3458 drainHeadersIfObservable.$call(this); this._reply( new Response(data, { @@ -1174,6 +1333,10 @@ ServerResponse.prototype._final = function (callback) { statusText: this.statusMessage ?? STATUS_CODES[this.statusCode], }), ); + if (shouldEmitClose) { + req.complete = true; + process.nextTick(emitRequestCloseNT, req); + } callback && callback(); return; } @@ -1181,7 +1344,10 @@ ServerResponse.prototype._final = function (callback) { this[finishedSymbol] = true; ensureReadableStreamController.$call(this, controller => { controller.end(); - + if (shouldEmitClose) { + req.complete = true; + process.nextTick(emitRequestCloseNT, req); + } callback(); const deferred = this[deferredSymbol]; if (deferred) { @@ -1372,14 +1538,13 @@ class ClientRequest extends OutgoingMessage { this.#bodyChunks.push(...chunks); callback(); } + _destroy(err, callback) { this.destroyed = true; // If request is destroyed we abort the current response this[kAbortController]?.abort?.(); - if (err) { - this.emit("error", err); - } - callback(); + this.socket.destroy(); + emitErrorNextTick(this, err, callback); } _ensureTls() { @@ -1390,11 +1555,16 @@ class ClientRequest extends OutgoingMessage { _final(callback) { this.#finished = true; this[kAbortController] = new AbortController(); - this[kAbortController].signal.addEventListener("abort", () => { - this.emit("abort"); - this[kClearTimeout](); - this.destroy(); - }); + this[kAbortController].signal.addEventListener( + "abort", + () => { + this[kClearTimeout]?.(); + if (this.destroyed) return; + this.emit("abort"); + this.destroy(); + }, + { once: true }, + ); if (this.#signal?.aborted) { this[kAbortController].abort(); } @@ -1451,6 +1621,10 @@ class ClientRequest extends OutgoingMessage { //@ts-ignore this.#fetchRequest = fetch(url, fetchOptions) .then(response => { + if (this.aborted) { + return; + } + const prevIsHTTPS = isNextIncomingMessageHTTPS; isNextIncomingMessageHTTPS = response.url.startsWith("https:"); var res = (this.#res = new IncomingMessage(response, { @@ -1463,7 +1637,7 @@ class ClientRequest extends OutgoingMessage { .catch(err => { // Node treats AbortError separately. // The "abort" listener on the abort controller should have called this - if (err?.name === "AbortError") { + if (isAbortError(err)) { return; } @@ -1485,13 +1659,19 @@ class ClientRequest extends OutgoingMessage { } get aborted() { - return this.#signal?.aborted || !!this[kAbortController]?.signal.aborted; + return this[abortedSymbol] || this.#signal?.aborted || !!this[kAbortController]?.signal.aborted; + } + + set aborted(value) { + this[abortedSymbol] = value; } abort() { if (this.aborted) return; + this[abortedSymbol] = true; + process.nextTick(emitAbortNextTick, this); this[kAbortController]?.abort?.(); - // TODO: Close stream if body streaming + this.destroy(); } constructor(input, options, cb) { @@ -1521,38 +1701,26 @@ class ClientRequest extends OutgoingMessage { options = ObjectAssign(input || {}, options); } - var defaultAgent = options._defaultAgent || Agent.globalAgent; - - let protocol = options.protocol; - if (!protocol) { - if (options.port === 443) { - protocol = "https:"; - } else { - protocol = defaultAgent.protocol || "http:"; - } + let agent = options.agent; + const defaultAgent = options._defaultAgent || Agent.globalAgent; + if (agent === false) { + agent = new defaultAgent.constructor(); + } else if (agent == null) { + agent = defaultAgent; + } else if (typeof agent.addRequest !== "function") { + throw ERR_INVALID_ARG_TYPE("options.agent", "Agent-like Object, undefined, or false", agent); } - this.#protocol = protocol; + this.#agent = agent; - switch (this.#agent?.protocol) { - case undefined: { - break; - } - case "http:": { - if (protocol === "https:") { - defaultAgent = this.#agent = getDefaultHTTPSAgent(); - break; - } - } - case "https:": { - if (protocol === "https") { - defaultAgent = this.#agent = Agent.globalAgent; - break; - } - } - default: { - break; - } + const protocol = options.protocol || defaultAgent.protocol; + let expectedProtocol = defaultAgent.protocol; + if (this.agent.protocol) { + expectedProtocol = this.agent.protocol; } + if (protocol !== expectedProtocol) { + throw ERR_INVALID_PROTOCOL(protocol, expectedProtocol); + } + this.#protocol = protocol; if (options.path) { const path = String(options.path); @@ -1563,16 +1731,8 @@ class ClientRequest extends OutgoingMessage { } } - // Since we don't implement Agent, we don't need this - if (protocol !== "http:" && protocol !== "https:" && protocol) { - const expectedProtocol = defaultAgent?.protocol ?? "http:"; - throw new Error(`Protocol mismatch. Expected: ${expectedProtocol}. Got: ${protocol}`); - // throw new ERR_INVALID_PROTOCOL(protocol, expectedProtocol); - } - - const defaultPort = protocol === "https:" ? 443 : 80; - - this.#port = options.port || options.defaultPort || this.#agent?.defaultPort || defaultPort; + const defaultPort = options.defaultPort || this.#agent.defaultPort; + this.#port = options.port || defaultPort || 80; this.#useDefaultPort = this.#port === defaultPort; const host = (this.#host = @@ -1594,7 +1754,7 @@ class ClientRequest extends OutgoingMessage { let method = options.method; const methodIsString = typeof method === "string"; if (method !== null && method !== undefined && !methodIsString) { - // throw new ERR_INVALID_ARG_TYPE("options.method", "string", method); + // throw ERR_INVALID_ARG_TYPE("options.method", "string", method); throw new Error("ERR_INVALID_ARG_TYPE: options.method"); } @@ -1706,9 +1866,9 @@ class ClientRequest extends OutgoingMessage { this.setTimeout(timeout, undefined); } + const { headers } = options; const headersArray = $isJSArray(headers); if (!headersArray) { - var headers = options.headers; if (headers) { for (let key in headers) { this.setHeader(key, headers[key]); @@ -1765,11 +1925,7 @@ class ClientRequest extends OutgoingMessage { this._httpMessage = this; - process.nextTick(() => { - // Ref: https://github.com/nodejs/node/blob/f63e8b7fa7a4b5e041ddec67307609ec8837154f/lib/_http_client.js#L803-L839 - if (this.destroyed) return; - this.emit("socket", this.socket); - }); + process.nextTick(emitContinueAndSocketNT, this); } setSocketKeepAlive(enable = true, initialDelay = 0) { @@ -1823,27 +1979,9 @@ class ClientRequest extends OutgoingMessage { } } -function urlToHttpOptions(url) { - var { protocol, hostname, hash, search, pathname, href, port, username, password } = url; - return { - protocol, - hostname: - typeof hostname === "string" && StringPrototypeStartsWith.$call(hostname, "[") - ? StringPrototypeSlice.$call(hostname, 1, -1) - : hostname, - hash, - search, - pathname, - path: `${pathname || ""}${search || ""}`, - href, - port: port ? Number(port) : protocol === "https:" ? 443 : protocol === "http:" ? 80 : undefined, - auth: username || password ? `${decodeURIComponent(username)}:${decodeURIComponent(password)}` : undefined, - }; -} - function validateHost(host, name) { if (host !== null && host !== undefined && typeof host !== "string") { - // throw new ERR_INVALID_ARG_TYPE( + // throw ERR_INVALID_ARG_TYPE( // `options.${name}`, // ["string", "undefined", "null"], // host, @@ -2032,7 +2170,7 @@ function _writeHead(statusCode, reason, obj, response) { } else { // writeHead(statusCode[, headers]) if (!response.statusMessage) response.statusMessage = STATUS_CODES[statusCode] || "unknown"; - obj = reason; + obj ??= reason; } response.statusCode = statusCode; @@ -2071,6 +2209,10 @@ function _writeHead(statusCode, reason, obj, response) { // consisting only of the Status-Line and optional headers, and is // terminated by an empty line. response._hasBody = false; + const req = response.req; + if (req) { + req.complete = true; + } } } @@ -2085,6 +2227,13 @@ function request(url, options, cb) { return new ClientRequest(url, options, cb); } +function emitCloseServer(self: Server) { + self.emit("close"); +} +function emitCloseNTServer(this: Server) { + process.nextTick(emitCloseServer, this); +} + /** * Makes a `GET` HTTP request. * @param {string | URL} url @@ -2098,6 +2247,25 @@ function get(url, options, cb) { return req; } +function onError(self, error, cb) { + if (error) { + cb(error); + } else { + cb(); + } +} + +function emitErrorNextTick(self, err, cb) { + process.nextTick(onError, self, err, cb); +} + +function emitAbortNextTick(self) { + self.emit("abort"); +} + +const setMaxHTTPHeaderSize = $newZigFunction("node_http_binding.zig", "setMaxHTTPHeaderSize", 1); +const getMaxHTTPHeaderSize = $newZigFunction("node_http_binding.zig", "getMaxHTTPHeaderSize", 0); + var globalAgent = new Agent(); export default { Agent, @@ -2109,7 +2277,12 @@ export default { IncomingMessage, request, get, - maxHeaderSize: 16384, + get maxHeaderSize() { + return getMaxHTTPHeaderSize(); + }, + set maxHeaderSize(value) { + setMaxHTTPHeaderSize(value); + }, validateHeaderName, validateHeaderValue, setMaxIdleHTTPParsers(max) { diff --git a/src/js/node/http2.ts b/src/js/node/http2.ts index 384281b45a88f..8a17aa5fb27db 100644 --- a/src/js/node/http2.ts +++ b/src/js/node/http2.ts @@ -852,9 +852,9 @@ class ClientHttp2Session extends Http2Session { process.nextTick(emitWantTrailersNT, self.#streams, streamId); } }, - goaway(self: ClientHttp2Session, errorCode: number, lastStreamId: number, opaqueData: Buffer) { + goaway(self: ClientHttp2Session, errorCode: number, lastStreamId: number, opaqueData?: Buffer) { if (!self) return; - self.emit("goaway", errorCode, lastStreamId, opaqueData); + self.emit("goaway", errorCode, lastStreamId, opaqueData || Buffer.allocUnsafe(0)); if (errorCode !== 0) { for (let [_, stream] of self.#streams) { stream.rstCode = errorCode; diff --git a/src/js/node/https.ts b/src/js/node/https.ts index d75fb3132ea7b..9f6c66abbdde5 100644 --- a/src/js/node/https.ts +++ b/src/js/node/https.ts @@ -1,14 +1,30 @@ // Hardcoded module "node:https" const http = require("node:http"); +const { urlToHttpOptions } = require("internal/url"); -function request(input, options, cb) { - if (input && typeof input === "object" && !(input instanceof URL)) { - input.protocol ??= "https:"; - } else if (typeof options === "object") { - options.protocol ??= "https:"; +const ObjectSetPrototypeOf = Object.setPrototypeOf; +const ArrayPrototypeShift = Array.prototype.shift; +const ObjectAssign = Object.assign; +const ArrayPrototypeUnshift = Array.prototype.unshift; + +function request(...args) { + let options = {}; + + if (typeof args[0] === "string") { + const urlStr = ArrayPrototypeShift.$call(args); + options = urlToHttpOptions(new URL(urlStr)); + } else if (args[0] instanceof URL) { + options = urlToHttpOptions(ArrayPrototypeShift.$call(args)); + } + + if (args[0] && typeof args[0] !== "function") { + ObjectAssign.$call(null, options, ArrayPrototypeShift.$call(args)); } - return http.request(input, options, cb); + options._defaultAgent = https.globalAgent; + ArrayPrototypeUnshift.$call(args, options); + + return new http.ClientRequest(...args); } function get(input, options, cb) { @@ -17,8 +33,24 @@ function get(input, options, cb) { return req; } -export default { - ...http, +function Agent(options) { + if (!(this instanceof Agent)) return new Agent(options); + + http.Agent.$apply(this, [options]); + this.defaultPort = 443; + this.protocol = "https:"; + this.maxCachedSessions = this.options.maxCachedSessions; + if (this.maxCachedSessions === undefined) this.maxCachedSessions = 100; +} +Agent.prototype = Object.create(http.Agent.prototype); +Agent.prototype.createConnection = http.createConnection; + +var https = { + Agent, + globalAgent: new Agent({ keepAlive: true, scheduling: "lifo", timeout: 5000 }), + Server: http.Server, + createServer: http.createServer, get, request, }; +export default https; diff --git a/src/js/node/net.ts b/src/js/node/net.ts index ee6e63a115a17..408b38f4ec20d 100644 --- a/src/js/node/net.ts +++ b/src/js/node/net.ts @@ -21,7 +21,9 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. const { Duplex } = require("node:stream"); const EventEmitter = require("node:events"); -const { addServerName } = require("../internal/net"); +const { addServerName, upgradeDuplexToTLS, isNamedPipeSocket } = require("../internal/net"); +const { ExceptionWithHostPort } = require("internal/shared"); +const { ERR_SERVER_NOT_RUNNING } = require("internal/errors"); // IPv4 Segment const v4Seg = "(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"; @@ -32,6 +34,9 @@ var IPv4Reg; const v6Seg = "(?:[0-9a-fA-F]{1,4})"; var IPv6Reg; +const DEFAULT_IPV4_ADDR = "0.0.0.0"; +const DEFAULT_IPV6_ADDR = "::"; + function isIPv4(s) { return (IPv4Reg ??= new RegExp(`^${v4Str}$`)).test(s); } @@ -66,15 +71,18 @@ const bunSocketServerConnections = Symbol.for("::bunnetserverconnections::"); const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::"); const bunSocketInternal = Symbol.for("::bunnetsocketinternal::"); +const kServerSocket = Symbol("kServerSocket"); const bunTLSConnectOptions = Symbol.for("::buntlsconnectoptions::"); -function closeNT(self) { - self.emit("close"); -} +const kRealListen = Symbol("kRealListen"); + function endNT(socket, callback, err) { socket.end(); callback(err); } +function closeNT(callback, err) { + callback(err); +} var SocketClass; const Socket = (function (InternalSocket) { @@ -109,7 +117,7 @@ const Socket = (function (InternalSocket) { queue.push(buffer); }, drain: Socket.#Drain, - end: Socket.#Close, + end: Socket.#End, error(socket, error) { const self = socket.data; if (!self) return; @@ -125,7 +133,8 @@ const Socket = (function (InternalSocket) { const self = socket.data; if (!self) return; - socket.timeout(self.timeout); + socket.timeout(Math.ceil(self.timeout / 1000)); + if (self.#unrefOnConnected) socket.unref(); self[bunSocketInternal] = socket; self.connecting = false; @@ -187,17 +196,37 @@ const Socket = (function (InternalSocket) { binaryType: "buffer", }; + static #End(socket) { + const self = socket.data; + if (!self) return; + self.#ended = true; + const queue = self.#readQueue; + if (queue.isEmpty()) { + if (self.push(null)) { + return; + } + } + queue.push(null); + } static #Close(socket) { const self = socket.data; if (!self || self.#closed) return; self.#closed = true; //socket cannot be used after close self[bunSocketInternal] = null; - const queue = self.#readQueue; - if (queue.isEmpty()) { - if (self.push(null)) return; + const finalCallback = self.#final_callback; + if (finalCallback) { + self.#final_callback = null; + finalCallback(); + return; + } + if (!self.#ended) { + const queue = self.#readQueue; + if (queue.isEmpty()) { + if (self.push(null)) return; + } + queue.push(null); } - queue.push(null); } static #Drain(socket) { @@ -223,14 +252,15 @@ const Socket = (function (InternalSocket) { data: Socket.#Handlers.data, close(socket) { Socket.#Handlers.close(socket); - this.data[bunSocketServerConnections]--; + this.data.server[bunSocketServerConnections]--; + this.data.server._emitCloseIfDrained(); }, end(socket) { Socket.#Handlers.end(socket); - this.data[bunSocketServerConnections]--; }, open(socket) { const self = this.data; + socket[kServerSocket] = self[bunSocketInternal]; const options = self[bunSocketServerOptions]; const { pauseOnConnect, connectionListener, InternalSocketClass, requestCert, rejectUnauthorized } = options; const _socket = new InternalSocketClass({}); @@ -276,6 +306,7 @@ const Socket = (function (InternalSocket) { _socket.resume(); } }, + handshake(socket, success, verifyError) { const { data: self } = socket; self._securePending = false; @@ -322,6 +353,8 @@ const Socket = (function (InternalSocket) { bytesRead = 0; bytesWritten = 0; #closed = false; + #ended = false; + #final_callback = null; connecting = false; localAddress = "127.0.0.1"; #readQueue = $createFIFO(); @@ -402,7 +435,7 @@ const Socket = (function (InternalSocket) { #attach(port, socket) { this.remotePort = port; socket.data = this; - socket.timeout(this.timeout); + socket.timeout(Math.ceil(this.timeout / 1000)); if (this.#unrefOnConnected) socket.unref(); this[bunSocketInternal] = socket; this.connecting = false; @@ -419,13 +452,14 @@ const Socket = (function (InternalSocket) { connection[bunSocketInternal] = null; connection.unref(); connection.destroy(); - process.nextTick(closeNT, connection); } connect(...args) { const [options, connectListener] = normalizeArgs(args); let connection = this.#socket; + let upgradeDuplex = false; + let { fd, port, @@ -509,7 +543,11 @@ const Socket = (function (InternalSocket) { !(connection instanceof Socket) || typeof connection[bunTlsSymbol] === "function" ) { - throw new TypeError("socket must be an instance of net.Socket"); + if (connection instanceof Duplex) { + upgradeDuplex = true; + } else { + throw new TypeError("socket must be an instance of net.Socket or Duplex"); + } } } this.authorized = false; @@ -524,33 +562,27 @@ const Socket = (function (InternalSocket) { try { if (connection) { const socket = connection[bunSocketInternal]; - - if (socket) { + if (!upgradeDuplex && socket) { + // if is named pipe socket we can upgrade it using the same wrapper than we use for duplex + upgradeDuplex = isNamedPipeSocket(socket); + } + if (upgradeDuplex) { this.connecting = true; this.#upgraded = connection; - const result = socket.upgradeTLS({ + const [result, events] = upgradeDuplexToTLS(connection, { data: this, tls, socket: this.#handlers, }); - if (result) { - const [raw, tls] = result; - // replace socket - connection[bunSocketInternal] = raw; - raw.timeout(raw.timeout); - this.once("end", this.#closeRawConnection); - raw.connecting = false; - this[bunSocketInternal] = tls; - } else { - this[bunSocketInternal] = null; - throw new Error("Invalid socket"); - } - } else { - // wait to be connected - connection.once("connect", () => { - const socket = connection[bunSocketInternal]; - if (!socket) return; + connection.on("data", events[0]); + connection.on("end", events[1]); + connection.on("drain", events[2]); + connection.on("close", events[3]); + + this[bunSocketInternal] = result; + } else { + if (socket) { this.connecting = true; this.#upgraded = connection; const result = socket.upgradeTLS({ @@ -558,12 +590,10 @@ const Socket = (function (InternalSocket) { tls, socket: this.#handlers, }); - if (result) { const [raw, tls] = result; // replace socket connection[bunSocketInternal] = raw; - raw.timeout(raw.timeout); this.once("end", this.#closeRawConnection); raw.connecting = false; this[bunSocketInternal] = tls; @@ -571,7 +601,53 @@ const Socket = (function (InternalSocket) { this[bunSocketInternal] = null; throw new Error("Invalid socket"); } - }); + } else { + // wait to be connected + connection.once("connect", () => { + const socket = connection[bunSocketInternal]; + if (!upgradeDuplex && socket) { + // if is named pipe socket we can upgrade it using the same wrapper than we use for duplex + upgradeDuplex = isNamedPipeSocket(socket); + } + if (upgradeDuplex) { + this.connecting = true; + this.#upgraded = connection; + + const [result, events] = upgradeDuplexToTLS(connection, { + data: this, + tls, + socket: this.#handlers, + }); + + connection.on("data", events[0]); + connection.on("end", events[1]); + connection.on("drain", events[2]); + connection.on("close", events[3]); + + this[bunSocketInternal] = result; + } else { + this.connecting = true; + this.#upgraded = connection; + const result = socket.upgradeTLS({ + data: this, + tls, + socket: this.#handlers, + }); + + if (result) { + const [raw, tls] = result; + // replace socket + connection[bunSocketInternal] = raw; + this.once("end", this.#closeRawConnection); + raw.connecting = false; + this[bunSocketInternal] = tls; + } else { + this[bunSocketInternal] = null; + throw new Error("Invalid socket"); + } + } + }); + } } } else if (path) { // start using unix socket @@ -610,13 +686,29 @@ const Socket = (function (InternalSocket) { _destroy(err, callback) { const socket = this[bunSocketInternal]; - socket && process.nextTick(endNT, socket, callback, err); + if (socket) { + this[bunSocketInternal] = null; + // we still have a socket, call end before destroy + process.nextTick(endNT, socket, callback, err); + return; + } + // no socket, just destroy + process.nextTick(closeNT, callback, err); } _final(callback) { - this[bunSocketInternal]?.end(); - callback(); - process.nextTick(closeNT, this); + const socket = this[bunSocketInternal]; + // already closed call destroy + if (!socket) return callback(); + + if (this.allowHalfOpen) { + // wait socket close event + this.#final_callback = callback; + } else { + // emit FIN not allowing half open + this[bunSocketInternal] = null; + process.nextTick(endNT, socket, callback); + } } get localFamily() { @@ -655,9 +747,10 @@ const Socket = (function (InternalSocket) { const socket = this[bunSocketInternal]; if (!socket) { this.#unrefOnConnected = false; - return; + return this; } socket.ref(); + return this; } get remoteAddress() { @@ -683,7 +776,9 @@ const Socket = (function (InternalSocket) { } setTimeout(timeout, callback) { - this[bunSocketInternal]?.timeout(timeout); + // internally or timeouts are in seconds + // we use Math.ceil because 0 would disable the timeout and less than 1 second but greater than 1ms would be 1 second (the minimum) + this[bunSocketInternal]?.timeout(Math.ceil(timeout / 1000)); this.timeout = timeout; if (callback) this.once("timeout", callback); return this; @@ -693,9 +788,18 @@ const Socket = (function (InternalSocket) { const socket = this[bunSocketInternal]; if (!socket) { this.#unrefOnConnected = true; - return; + return this; } socket.unref(); + return this; + } + + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/net.js#L785 + destroySoon() { + if (this.writable) this.end(); + + if (this.writableFinished) this.destroy(); + else this.once("finish", this.destroy); } _write(chunk, encoding, callback) { @@ -772,26 +876,44 @@ class Server extends EventEmitter { } close(callback) { - if (this[bunSocketInternal]) { - this[bunSocketInternal].stop(true); - this[bunSocketInternal] = null; - this[bunSocketServerConnections] = 0; - this.emit("close"); - if (typeof callback === "function") { - callback(); + if (typeof callback === "function") { + if (!this[bunSocketInternal]) { + this.once("close", function close() { + callback(new ERR_SERVER_NOT_RUNNING()); + }); + } else { + this.once("close", callback); } - - return this; } - if (typeof callback === "function") { - const error = new Error("Server is not running"); - error.code = "ERR_SERVER_NOT_RUNNING"; - callback(error); + if (this[bunSocketInternal]) { + this[bunSocketInternal].stop(false); + this[bunSocketInternal] = null; } + + this._emitCloseIfDrained(); + return this; } + [Symbol.asyncDispose]() { + const { resolve, reject, promise } = Promise.withResolvers(); + this.close(function (err, ...args) { + if (err) reject(err); + else resolve(...args); + }); + return promise; + } + + _emitCloseIfDrained() { + if (this[bunSocketInternal] || this[bunSocketServerConnections] > 0) { + return; + } + process.nextTick(() => { + this.emit("close"); + }); + } + address() { const server = this[bunSocketInternal]; if (server) { @@ -922,45 +1044,76 @@ class Server extends EventEmitter { } else { options.InternalSocketClass = SocketClass; } - this[bunSocketInternal] = Bun.listen( - path - ? { - exclusive, - unix: path, - tls, - socket: SocketClass[bunSocketServerHandlers], - } - : { - exclusive, - port, - hostname, - tls, - socket: SocketClass[bunSocketServerHandlers], - }, + + listenInCluster( + this, + null, + port, + 4, + backlog, + undefined, + exclusive, + undefined, + undefined, + path, + hostname, + tls, + contexts, + onListen, ); + } catch (err) { + setTimeout(emitErrorNextTick, 1, this, err); + } + return this; + } + + [kRealListen](path, port, hostname, exclusive, tls, contexts, onListen) { + if (path) { + this[bunSocketInternal] = Bun.listen({ + unix: path, + tls, + socket: SocketClass[bunSocketServerHandlers], + }); + } else { + this[bunSocketInternal] = Bun.listen({ + exclusive, + port, + hostname, + tls, + socket: SocketClass[bunSocketServerHandlers], + }); + } - //make this instance available on handlers - this[bunSocketInternal].data = this; + //make this instance available on handlers + this[bunSocketInternal].data = this; - if (contexts) { - for (const [name, context] of contexts) { - addServerName(this[bunSocketInternal], name, context); - } + if (contexts) { + for (const [name, context] of contexts) { + addServerName(this[bunSocketInternal], name, context); } - - // We must schedule the emitListeningNextTick() only after the next run of - // the event loop's IO queue. Otherwise, the server may not actually be listening - // when the 'listening' event is emitted. - // - // That leads to all sorts of confusion. - // - // process.nextTick() is not sufficient because it will run before the IO queue. - setTimeout(emitListeningNextTick, 1, this, onListen); - } catch (err) { - setTimeout(emitErrorNextTick, 1, this, err); } + + // We must schedule the emitListeningNextTick() only after the next run of + // the event loop's IO queue. Otherwise, the server may not actually be listening + // when the 'listening' event is emitted. + // + // That leads to all sorts of confusion. + // + // process.nextTick() is not sufficient because it will run before the IO queue. + setTimeout(emitListeningNextTick, 1, this, onListen?.bind(this)); + } + + get _handle() { return this; } + set _handle(new_handle) { + //nothing + } + + getsockname(out) { + out.port = this.address().port; + return out; + } } function emitErrorNextTick(self, error) { @@ -975,7 +1128,7 @@ function emitErrorAndCloseNextTick(self, error) { function emitListeningNextTick(self, onListen) { if (typeof onListen === "function") { try { - onListen(); + onListen.$call(self); } catch (err) { self.emit("error", err); } @@ -983,6 +1136,50 @@ function emitListeningNextTick(self, onListen) { self.emit("listening"); } +let cluster; +function listenInCluster( + server, + address, + port, + addressType, + backlog, + fd, + exclusive, + flags, + options, + path, + hostname, + tls, + contexts, + onListen, +) { + exclusive = !!exclusive; + + if (cluster === undefined) cluster = require("node:cluster"); + + if (cluster.isPrimary || exclusive) { + server[kRealListen](path, port, hostname, exclusive, tls, contexts, onListen); + return; + } + + const serverQuery = { + address: address, + port: port, + addressType: addressType, + fd: fd, + flags, + backlog, + ...options, + }; + cluster._getServer(server, serverQuery, function listenOnPrimaryHandle(err, handle) { + err = checkBindError(err, port, handle); + if (err) { + throw new ExceptionWithHostPort(err, "bind", address, port); + } + server[kRealListen](path, port, hostname, exclusive, tls, contexts, onListen); + }); +} + function createServer(options, connectionListener) { return new Server(options, connectionListener); } @@ -1016,6 +1213,23 @@ function normalizeArgs(args) { return arr; } +function checkBindError(err, port, handle) { + // EADDRINUSE may not be reported until we call listen() or connect(). + // To complicate matters, a failed bind() followed by listen() or connect() + // will implicitly bind to a random port. Ergo, check that the socket is + // bound to the expected port before calling listen() or connect(). + if (err === 0 && port > 0 && handle.getsockname) { + const out = {}; + err = handle.getsockname(out); + if (err === 0 && port !== out.port) { + $debug(`checkBindError, bound to ${out.port} instead of ${port}`); + const UV_EADDRINUSE = -4091; + err = UV_EADDRINUSE; + } + } + return err; +} + function isPipeName(s) { return typeof s === "string" && toNumber(s) === false; } @@ -1053,4 +1267,6 @@ export default { setDefaultAutoSelectFamilyAttemptTimeout: $zig("node_net_binding.zig", "setDefaultAutoSelectFamilyAttemptTimeout"), BlockList, + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/net.js#L2456 + Stream: Socket, }; diff --git a/src/js/node/perf_hooks.ts b/src/js/node/perf_hooks.ts index b23e40b860d70..f975d8355c4cb 100644 --- a/src/js/node/perf_hooks.ts +++ b/src/js/node/perf_hooks.ts @@ -11,17 +11,30 @@ var { } = globalThis; var constants = { - NODE_PERFORMANCE_GC_MAJOR: 4, - NODE_PERFORMANCE_GC_MINOR: 1, - NODE_PERFORMANCE_GC_INCREMENTAL: 8, - NODE_PERFORMANCE_GC_WEAKCB: 16, - NODE_PERFORMANCE_GC_FLAGS_NO: 0, - NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED: 2, - NODE_PERFORMANCE_GC_FLAGS_FORCED: 4, - NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING: 8, + NODE_PERFORMANCE_ENTRY_TYPE_DNS: 4, + NODE_PERFORMANCE_ENTRY_TYPE_GC: 0, + NODE_PERFORMANCE_ENTRY_TYPE_HTTP: 1, + NODE_PERFORMANCE_ENTRY_TYPE_HTTP2: 2, + NODE_PERFORMANCE_ENTRY_TYPE_NET: 3, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE: 16, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY: 32, + NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED: 2, + NODE_PERFORMANCE_GC_FLAGS_FORCED: 4, + NODE_PERFORMANCE_GC_FLAGS_NO: 0, NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE: 64, + NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING: 8, + NODE_PERFORMANCE_GC_INCREMENTAL: 8, + NODE_PERFORMANCE_GC_MAJOR: 4, + NODE_PERFORMANCE_GC_MINOR: 1, + NODE_PERFORMANCE_GC_WEAKCB: 16, + NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE: 7, + NODE_PERFORMANCE_MILESTONE_ENVIRONMENT: 2, + NODE_PERFORMANCE_MILESTONE_LOOP_EXIT: 6, + NODE_PERFORMANCE_MILESTONE_LOOP_START: 5, + NODE_PERFORMANCE_MILESTONE_NODE_START: 3, + NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN_TIMESTAMP: 0, + NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN: 1, + NODE_PERFORMANCE_MILESTONE_V8_START: 4, }; // PerformanceEntry is not a valid constructor, so we have to fake it. diff --git a/src/js/node/readline.ts b/src/js/node/readline.ts index 916f115b32118..0c9ae8b2d0c0b 100644 --- a/src/js/node/readline.ts +++ b/src/js/node/readline.ts @@ -258,14 +258,6 @@ var NodeError = getNodeErrorByName("Error"); var NodeTypeError = getNodeErrorByName("TypeError"); var NodeRangeError = getNodeErrorByName("RangeError"); -class ERR_INVALID_ARG_TYPE extends NodeTypeError { - constructor(name, type, value) { - super(`The "${name}" argument must be of type ${type}. Received type ${typeof value}`, { - code: "ERR_INVALID_ARG_TYPE", - }); - } -} - class ERR_INVALID_ARG_VALUE extends NodeTypeError { constructor(name, value, reason = "not specified") { super(`The value "${String(value)}" is invalid for argument '${name}'. Reason: ${reason}`, { @@ -315,7 +307,7 @@ class AbortError extends Error { * @returns {asserts value is Function} */ function validateFunction(value, name) { - if (typeof value !== "function") throw new ERR_INVALID_ARG_TYPE(name, "Function", value); + if (typeof value !== "function") throw $ERR_INVALID_ARG_TYPE(name, "Function", value); } /** @@ -325,7 +317,7 @@ function validateFunction(value, name) { */ function validateAbortSignal(signal, name) { if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + throw $ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); } } @@ -339,7 +331,7 @@ function validateAbortSignal(signal, name) { function validateArray(value, name, minLength = 0) { // var validateArray = hideStackFrames((value, name, minLength = 0) => { if (!$isJSArray(value)) { - throw new ERR_INVALID_ARG_TYPE(name, "Array", value); + throw $ERR_INVALID_ARG_TYPE(name, "Array", value); } if (value.length < minLength) { var reason = `must be longer than ${minLength}`; @@ -354,7 +346,7 @@ function validateArray(value, name, minLength = 0) { * @returns {asserts value is string} */ function validateString(value, name) { - if (typeof value !== "string") throw new ERR_INVALID_ARG_TYPE(name, "string", value); + if (typeof value !== "string") throw $ERR_INVALID_ARG_TYPE(name, "string", value); } /** @@ -364,7 +356,7 @@ function validateString(value, name) { * @returns {asserts value is boolean} */ function validateBoolean(value, name) { - if (typeof value !== "boolean") throw new ERR_INVALID_ARG_TYPE(name, "boolean", value); + if (typeof value !== "boolean") throw $ERR_INVALID_ARG_TYPE(name, "boolean", value); } /** @@ -387,7 +379,7 @@ function validateObject(value, name, options = null) { (!allowArray && $isJSArray.$call(null, value)) || (typeof value !== "object" && (!allowFunction || typeof value !== "function")) ) { - throw new ERR_INVALID_ARG_TYPE(name, "object", value); + throw $ERR_INVALID_ARG_TYPE(name, "object", value); } } @@ -400,7 +392,7 @@ function validateObject(value, name, options = null) { * @returns {asserts value is number} */ function validateInteger(value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) { - if (typeof value !== "number") throw new ERR_INVALID_ARG_TYPE(name, "number", value); + if (typeof value !== "number") throw $ERR_INVALID_ARG_TYPE(name, "number", value); if (!NumberIsInteger(value)) throw new ERR_OUT_OF_RANGE(name, "an integer", value); if (value < min || value > max) throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); } @@ -414,7 +406,7 @@ function validateInteger(value, name, min = NumberMIN_SAFE_INTEGER, max = Number */ function validateUint32(value, name, positive = false) { if (typeof value !== "number") { - throw new ERR_INVALID_ARG_TYPE(name, "number", value); + throw $ERR_INVALID_ARG_TYPE(name, "number", value); } if (!NumberIsInteger(value)) { @@ -2897,7 +2889,7 @@ class Readline { constructor(stream, options = undefined) { isWritable ??= require("node:stream").isWritable; - if (!isWritable(stream)) throw new ERR_INVALID_ARG_TYPE("stream", "Writable", stream); + if (!isWritable(stream)) throw $ERR_INVALID_ARG_TYPE("stream", "Writable", stream); this.#stream = stream; if (options?.autoCommit != null) { validateBoolean(options.autoCommit, "options.autoCommit"); diff --git a/src/js/node/stream.ts b/src/js/node/stream.ts index ab4a9dffc1643..ec669f047c51e 100644 --- a/src/js/node/stream.ts +++ b/src/js/node/stream.ts @@ -27,6 +27,8 @@ const kPaused = Symbol("kPaused"); // END moved from require_readable const StringDecoder = require("node:string_decoder").StringDecoder; +const transferToNativeReadable = $newCppFunction("ReadableStream.cpp", "jsFunctionTransferToNativeReadableStream", 1); +const { kAutoDestroyed } = require("internal/shared"); const ObjectSetPrototypeOf = Object.setPrototypeOf; @@ -3573,6 +3575,7 @@ var require_readable = __commonJS({ const wState = stream._writableState; const autoDestroy = !wState || (wState.autoDestroy && (wState.finished || wState.writable === false)); if (autoDestroy) { + stream[kAutoDestroyed] = true; // workaround for node:http Server not using node:net Server stream.destroy(); } } @@ -3888,6 +3891,10 @@ var require_writable = __commonJS({ return writeOrBuffer(stream, state, chunk, encoding, cb); } Writable.prototype.write = function (chunk, encoding, cb) { + if ($isCallable(encoding)) { + cb = encoding; + encoding = null; + } return _write(this, chunk, encoding, cb) === true; }; Writable.prototype.cork = function () { @@ -5720,7 +5727,7 @@ function createNativeStreamReadable(Readable) { ProcessNextTick(() => { this.push(null); }); - return view?.byteLength ?? 0 > 0 ? view : undefined; + return (view?.byteLength ?? 0 > 0) ? view : undefined; } else if ($isTypedArrayView(result)) { if (result.byteLength >= this[highWaterMark] && !this[hasResized] && !isClosed) { this[_adjustHighWaterMark](); @@ -5819,9 +5826,13 @@ function getNativeReadableStream(Readable, stream, options) { $assert(typeof ptr === "object", "Invalid native ptr"); const NativeReadable = getNativeReadableStreamPrototype(type, Readable); - stream.$bunNativePtr = -1; - stream.$bunNativeType = 0; - stream.$disturbed = true; + // https://github.com/oven-sh/bun/pull/12801 + // https://github.com/oven-sh/bun/issues/9555 + // There may be a ReadableStream.Strong handle to the ReadableStream. + // We can't update those handles to point to the NativeReadable from JS + // So we instead mark it as no longer usable, and create a new NativeReadable + transferToNativeReadable(stream); + return new NativeReadable(ptr, options); } diff --git a/src/js/node/stream.web.ts b/src/js/node/stream.web.ts index 25160c9de4c0d..a0d92215bd5ef 100644 --- a/src/js/node/stream.web.ts +++ b/src/js/node/stream.web.ts @@ -13,8 +13,8 @@ export default { WritableStreamDefaultController, ByteLengthQueuingStrategy, CountQueuingStrategy, - TextEncoderStream: undefined, - TextDecoderStream: undefined, + TextEncoderStream, + TextDecoderStream, CompressionStream: undefined, DecompressionStream: undefined, }; diff --git a/src/js/node/timers.promises.ts b/src/js/node/timers.promises.ts index eb171941a597d..e9b8a290af84a 100644 --- a/src/js/node/timers.promises.ts +++ b/src/js/node/timers.promises.ts @@ -84,10 +84,9 @@ function setTimeoutPromise(after = 1, value, options = {}) { signal.addEventListener("abort", onCancel); } }); - if (typeof onCancel !== "undefined") { - returnValue.finally(() => signal.removeEventListener("abort", onCancel)); - } - return returnValue; + return typeof onCancel !== "undefined" + ? returnValue.finally(() => signal.removeEventListener("abort", onCancel)) + : returnValue; } function setImmediatePromise(value, options = {}) { @@ -124,10 +123,9 @@ function setImmediatePromise(value, options = {}) { signal.addEventListener("abort", onCancel); } }); - if (typeof onCancel !== "undefined") { - returnValue.finally(() => signal.removeEventListener("abort", onCancel)); - } - return returnValue; + return typeof onCancel !== "undefined" + ? returnValue.finally(() => signal.removeEventListener("abort", onCancel)) + : returnValue; } function setIntervalPromise(after = 1, value, options = {}) { diff --git a/src/js/node/timers.ts b/src/js/node/timers.ts index 6150ce4579b28..91f8eb1609e54 100644 --- a/src/js/node/timers.ts +++ b/src/js/node/timers.ts @@ -1,4 +1,33 @@ -// Hardcoded module "node:timers" +const { throwNotImplemented } = require("internal/shared"); +const { defineCustomPromisify } = require("internal/promisify"); + +// Lazily load node:timers/promises promisified functions onto the global timers. +{ + const { setTimeout: timeout, setImmediate: immediate, setInterval: interval } = globalThis; + + if (timeout && $isCallable(timeout)) { + defineCustomPromisify(timeout, function setTimeout(arg1) { + const fn = defineCustomPromisify(timeout, require("node:timers/promises").setTimeout); + return fn.$apply(this, arguments); + }); + } + + if (immediate && $isCallable(immediate)) { + defineCustomPromisify(immediate, function setImmediate(arg1) { + const fn = defineCustomPromisify(immediate, require("node:timers/promises").setImmediate); + return fn.$apply(this, arguments); + }); + } + + if (interval && $isCallable(interval)) { + defineCustomPromisify(interval, function setInterval(arg1) { + const fn = defineCustomPromisify(interval, require("node:timers/promises").setInterval); + return fn.$apply(this, arguments); + }); + } +} +var timersPromisesValue; + export default { setTimeout, clearTimeout, @@ -6,4 +35,33 @@ export default { setImmediate, clearInterval, clearImmediate, + get promises() { + return (timersPromisesValue ??= require("node:timers/promises")); + }, + set promises(value) { + timersPromisesValue = value; + }, + active(timer) { + if ($isCallable(timer?.refresh)) { + timer.refresh(); + } else { + throwNotImplemented("'timers.active'"); + } + }, + unenroll(timer) { + if ($isCallable(timer?.refresh)) { + clearTimeout(timer); + return; + } + + throwNotImplemented("'timers.unenroll'"); + }, + enroll(timer, msecs) { + if ($isCallable(timer?.refresh)) { + timer.refresh(); + return; + } + + throwNotImplemented("'timers.enroll'"); + }, }; diff --git a/src/js/node/url.ts b/src/js/node/url.ts index a8dd68a8e537a..d969ab08ff27c 100644 --- a/src/js/node/url.ts +++ b/src/js/node/url.ts @@ -25,6 +25,7 @@ const { URL, URLSearchParams } = globalThis; const [domainToASCII, domainToUnicode] = $cpp("NodeURL.cpp", "Bun::createNodeURLBinding"); +const { urlToHttpOptions } = require("internal/url"); function Url() { this.protocol = null; @@ -803,25 +804,6 @@ Url.prototype.parseHost = function () { this.hostname = host; } }; -function urlToHttpOptions(url) { - const options = { - protocol: url.protocol, - hostname: - typeof url.hostname === "string" && url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname, - hash: url.hash, - search: url.search, - pathname: url.pathname, - path: `${url.pathname || ""}${url.search || ""}`, - href: url.href, - }; - if (url.port !== "") { - options.port = Number(url.port); - } - if (url.username || url.password) { - options.auth = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`; - } - return options; -} export default { parse: urlParse, diff --git a/src/js/node/util.ts b/src/js/node/util.ts index c237a1915fd97..58fb2f9277923 100644 --- a/src/js/node/util.ts +++ b/src/js/node/util.ts @@ -2,8 +2,14 @@ const types = require("node:util/types"); /** @type {import('node-inspect-extracted')} */ const utl = require("internal/util/inspect"); +const { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE } = require("internal/errors"); +const { promisify } = require("internal/promisify"); -var cjs_exports = {}; +const internalErrorName = $newZigFunction("node_util_binding.zig", "internalErrorName", 1); + +const NumberIsSafeInteger = Number.isSafeInteger; + +var cjs_exports; function isBuffer(value) { return Buffer.isBuffer(value); @@ -130,15 +136,19 @@ var log = function log() { console.log("%s - %s", timestamp(), format.$apply(cjs_exports, arguments)); }; var inherits = function inherits(ctor, superCtor) { + if (ctor === undefined || ctor === null) { + throw ERR_INVALID_ARG_TYPE("ctor", "Function", ctor); + } + + if (superCtor === undefined || superCtor === null) { + throw ERR_INVALID_ARG_TYPE("superCtor", "Function", superCtor); + } + + if (superCtor.prototype === undefined) { + throw ERR_INVALID_ARG_TYPE("superCtor.prototype", "Object", superCtor.prototype); + } ctor.super_ = superCtor; - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true, - }, - }); + Object.setPrototypeOf(ctor.prototype, superCtor.prototype); }; var _extend = function (origin, add) { if (!add || !isObject(add)) return origin; @@ -149,80 +159,7 @@ var _extend = function (origin, add) { } return origin; }; -var kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom"); -function defineCustomPromisify(target, callback) { - Object.defineProperty(target, kCustomPromisifiedSymbol, { - value: callback, - __proto__: null, - configurable: true, - }); - - return callback; -} - -// Lazily load node:timers/promises promisifed functions onto the global timers. -// This is not a complete solution, as one could load these without loading the "util" module -// But it is better than nothing. -{ - const { setTimeout: timeout, setImmediate: immediate, setInterval: interval } = globalThis; - - if (timeout && $isCallable(timeout)) { - defineCustomPromisify(timeout, function setTimeout(arg1) { - const fn = defineCustomPromisify(timeout, require("node:timers/promises").setTimeout); - return fn.$apply(this, arguments); - }); - } - - if (immediate && $isCallable(immediate)) { - defineCustomPromisify(immediate, function setImmediate(arg1) { - const fn = defineCustomPromisify(immediate, require("node:timers/promises").setImmediate); - return fn.$apply(this, arguments); - }); - } - - if (interval && $isCallable(interval)) { - defineCustomPromisify(interval, function setInterval(arg1) { - const fn = defineCustomPromisify(interval, require("node:timers/promises").setInterval); - return fn.$apply(this, arguments); - }); - } -} - -var promisify = function promisify(original) { - if (typeof original !== "function") throw new TypeError('The "original" argument must be of type Function'); - const custom = original[kCustomPromisifiedSymbol]; - if (custom) { - if (typeof custom !== "function") { - throw new TypeError('The "util.promisify.custom" argument must be of type Function'); - } - // ensure that we don't create another promisified function wrapper - return defineCustomPromisify(custom, custom); - } - - function fn(...originalArgs) { - const { promise, resolve, reject } = Promise.withResolvers(); - try { - original.$apply(this, [ - ...originalArgs, - function (err, ...values) { - if (err) { - return reject(err); - } - - resolve(values[0]); - }, - ]); - } catch (err) { - reject(err); - } - return promise; - } - Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); - defineCustomPromisify(fn, fn); - return Object.defineProperties(fn, getOwnPropertyDescriptors(original)); -}; -promisify.custom = kCustomPromisifiedSymbol; function callbackifyOnRejected(reason, cb) { if (!reason) { var newReason = new Error("Promise was rejected with a falsy value"); @@ -280,7 +217,68 @@ function styleText(format, text) { return `\u001b[${formatCodes[0]}m${text}\u001b[${formatCodes[1]}m`; } -export default Object.assign(cjs_exports, { +function getSystemErrorName(err: any) { + if (typeof err !== "number") throw ERR_INVALID_ARG_TYPE("err", "number", err); + if (err >= 0 || !NumberIsSafeInteger(err)) throw ERR_OUT_OF_RANGE("err", "a negative integer", err); + return internalErrorName(err); +} + +let lazyAbortedRegistry: FinalizationRegistry<{ + ref: WeakRef; + unregisterToken: (...args: any[]) => void; +}>; +function onAbortedCallback(resolveFn: Function) { + lazyAbortedRegistry.unregister(resolveFn); + + resolveFn(); +} + +function aborted(signal: AbortSignal, resource: object) { + if (!$isObject(signal) || !(signal instanceof AbortSignal)) { + throw ERR_INVALID_ARG_TYPE("signal", "AbortSignal", signal); + } + + if (!$isObject(resource)) { + throw ERR_INVALID_ARG_TYPE("resource", "object", resource); + } + + if (signal.aborted) { + return Promise.resolve(); + } + + const { promise, resolve } = $newPromiseCapability(Promise); + const unregisterToken = onAbortedCallback.bind(undefined, resolve); + signal.addEventListener( + "abort", + // Do not leak the current scope into the listener. + // Instead, create a new function. + unregisterToken, + { once: true }, + ); + + if (!lazyAbortedRegistry) { + lazyAbortedRegistry = new FinalizationRegistry(({ ref, unregisterToken }) => { + const signal = ref.deref(); + if (signal) signal.removeEventListener("abort", unregisterToken); + }); + } + + // When the resource is garbage collected, clear the listener from the + // AbortSignal so we do not cause the AbortSignal itself to leak (AbortSignal + // keeps alive until it is signaled). + lazyAbortedRegistry.register( + resource, + { + ref: new WeakRef(signal), + unregisterToken, + }, + unregisterToken, + ); + + return promise; +} + +cjs_exports = { format, formatWithOptions, stripVTControlCharacters, @@ -315,4 +313,8 @@ export default Object.assign(cjs_exports, { TextEncoder, parseArgs, styleText, -}); + getSystemErrorName, + aborted, +}; + +export default cjs_exports; diff --git a/src/js/node/v8.ts b/src/js/node/v8.ts index a4968eebc6825..ada998d747c75 100644 --- a/src/js/node/v8.ts +++ b/src/js/node/v8.ts @@ -31,24 +31,44 @@ function cachedDataVersionTag() { function getHeapSnapshot() { notimpl("getHeapSnapshot"); } + +let totalmem_ = -1; + +function totalmem() { + if (totalmem_ === -1) { + totalmem_ = require("node:os").totalmem(); + } + return totalmem_; +} + function getHeapStatistics() { const stats = jsc.heapStats(); - // this is not very correct + const memory = jsc.memoryUsage(); + + // These numbers need to be plausible, even if incorrect + // From npm's codebase: + // + // > static #heapLimit = Math.floor(getHeapStatistics().heap_size_limit) + // return { - total_heap_size: stats.heapCapacity, - total_heap_size_executable: 0, - total_physical_size: stats.heapSize, - total_available_size: Infinity, + total_heap_size: stats.heapSize, + total_heap_size_executable: stats.heapSize >> 1, + total_physical_size: memory.peak, + total_available_size: totalmem() - stats.heapSize, used_heap_size: stats.heapSize, - heap_size_limit: Infinity, + heap_size_limit: Math.min(memory.peak * 10, totalmem()), malloced_memory: stats.heapSize, - peak_malloced_memory: Infinity, + peak_malloced_memory: memory.peak, + + // -- Copied from Node: does_zap_garbage: 0, - number_of_native_contexts: Infinity, - number_of_detached_contexts: Infinity, - total_global_handles_size: Infinity, - used_global_handles_size: Infinity, - external_memory: Infinity, + number_of_native_contexts: 1, + number_of_detached_contexts: 0, + total_global_handles_size: 8192, + used_global_handles_size: 2208, + // ---- End of copied from Node + + external_memory: stats.extraMemorySize, }; } function getHeapSpaceStatistics() { @@ -119,6 +139,8 @@ export default { startupSnapshot, Deserializer, Serializer, + DefaultDeserializer, + DefaultSerializer, }; hideFromStack( @@ -140,4 +162,6 @@ hideFromStack( DefaultDeserializer, DefaultSerializer, GCProfiler, + DefaultDeserializer, + DefaultSerializer, ); diff --git a/src/js/node/worker_threads.ts b/src/js/node/worker_threads.ts index 4de9fe81e8390..4cd282398f67b 100644 --- a/src/js/node/worker_threads.ts +++ b/src/js/node/worker_threads.ts @@ -216,9 +216,15 @@ class Worker extends EventEmitter { const builtinsGeneratorHatesEval = "ev" + "a" + "l"[0]; if (options && builtinsGeneratorHatesEval in options) { + if (options[builtinsGeneratorHatesEval]) { + const blob = new Blob([filename], { type: "" }); + this.#urlToRevoke = filename = URL.createObjectURL(blob); + } else { + // if options.eval = false, allow the constructor below to fail, if + // we convert the code to a blob, it will succeed. + this.#urlToRevoke = filename; + } delete options[builtinsGeneratorHatesEval]; - const blob = new Blob([filename], { type: "" }); - this.#urlToRevoke = filename = URL.createObjectURL(blob); } try { this.#worker = new WebWorker(filename, options); diff --git a/src/js/node/zlib.ts b/src/js/node/zlib.ts index 65a57448b967a..72f5e5ed7091d 100644 --- a/src/js/node/zlib.ts +++ b/src/js/node/zlib.ts @@ -1,4602 +1,466 @@ // Hardcoded module "node:zlib" +const assert = require("node:assert"); const stream = require("node:stream"); +const BufferModule = require("node:buffer"); +const { ERR_INVALID_ARG_TYPE } = require("internal/errors"); + +const ObjectDefineProperty = Object.defineProperty; -const ObjectSetPrototypeOf = Object.setPrototypeOf; +const createBrotliEncoder = $newZigFunction("node_zlib_binding.zig", "createBrotliEncoder", 3); +const createBrotliDecoder = $newZigFunction("node_zlib_binding.zig", "createBrotliDecoder", 3); +const createZlibEncoder = $newZigFunction("node_zlib_binding.zig", "createZlibEncoder", 3); +const createZlibDecoder = $newZigFunction("node_zlib_binding.zig", "createZlibDecoder", 3); -const createBrotliEncoder = $zig("node_zlib_binding.zig", "createBrotliEncoder"); -const createBrotliDecoder = $zig("node_zlib_binding.zig", "createBrotliDecoder"); +const maxOutputLengthDefault = $requireMap.$get("buffer")?.exports.kMaxLength ?? BufferModule.kMaxLength; -function brotliCompress(buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; +// + +const kHandle = Symbol("kHandle"); +const kFlushFlag = Symbol("kFlushFlag"); +const kFlushBuffers: Buffer[] = []; +{ + const dummyArrayBuffer = new ArrayBuffer(); + for (const flushFlag of [0, 1, 2, 3, 4, 5]) { + kFlushBuffers[flushFlag] = Buffer.from(dummyArrayBuffer); + kFlushBuffers[flushFlag][kFlushFlag] = flushFlag; } - if (typeof callback !== "function") throw new TypeError("BrotliEncoder callback is not callable"); - const encoder = createBrotliEncoder(opts, {}, callback); - encoder.encode(buffer, undefined, true); } -function brotliDecompress(buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; +// TODO: this doesn't match node exactly so improve this more later +const alias = function (proto, to, from) { + ObjectDefineProperty(proto, to, { + get: function () { + return this[kHandle][from]; + }, + set: function (v) {}, // changing these would be a bug + enumerable: true, + }); +}; + +// + +function ZlibBase(options, method) { + if (options == null) options = {}; + if ($isObject(options)) { + options.maxOutputLength ??= maxOutputLengthDefault; + + if (options.encoding || options.objectMode || options.writableObjectMode) { + options = { ...options }; + options.encoding = null; + options.objectMode = false; + options.writableObjectMode = false; + } } - if (typeof callback !== "function") throw new TypeError("BrotliDecoder callback is not callable"); - const decoder = createBrotliDecoder(opts, {}, callback); - decoder.decode(buffer, undefined, true); + const [, , private_constructor] = methods[method]; + this[kHandle] = private_constructor(options, {}, null, method); + stream.Transform.$call(this, options); } +ZlibBase.prototype = Object.create(stream.Transform.prototype); +ObjectDefineProperty(ZlibBase.prototype, "_handle", { + get: function () { + return this[kHandle]; + }, + set: function (newval) { + //noop + }, +}); +alias(ZlibBase.prototype, "bytesWritten", "bytesWritten"); +alias(ZlibBase.prototype, "bytesRead", "bytesRead"); +alias(ZlibBase.prototype, "_closed", "closed"); +alias(ZlibBase.prototype, "_chunkSize", "chunkSize"); +alias(ZlibBase.prototype, "_defaultFlushFlag", "flush"); +alias(ZlibBase.prototype, "_finishFlushFlag", "finishFlush"); +alias(ZlibBase.prototype, "_defaultFullFlushFlag", "fullFlush"); +alias(ZlibBase.prototype, "_maxOutputLength", "maxOutputLength"); +ZlibBase.prototype.flush = function (kind, callback) { + if (typeof kind === "function" || (kind === undefined && !callback)) { + callback = kind; + kind = this._defaultFullFlushFlag; + } + if (this.writableFinished) { + if (callback) process.nextTick(callback); + } else if (this.writableEnded) { + if (callback) this.once("end", callback); + } else { + this.write(kFlushBuffers[kind], "", callback); + } +}; +ZlibBase.prototype.reset = function () { + assert(this[kHandle], "zlib binding closed"); + return this[kHandle].reset(); +}; +ZlibBase.prototype.close = function (callback) { + if (callback) stream.finished(this, callback); + this.destroy(); +}; +ZlibBase.prototype._transform = function _transform(chunk, encoding, callback) { + try { + callback(undefined, this[kHandle].transformSync(chunk, encoding, false)); + } catch (err) { + callback(err, undefined); + } +}; +ZlibBase.prototype._flush = function _flush(callback) { + try { + callback(undefined, this[kHandle].transformSync("", undefined, true)); + } catch (err) { + callback(err, undefined); + } +}; +ZlibBase.prototype._final = function (callback) { + callback(); +}; +ZlibBase.prototype._processChunk = function (chunk, flushFlag, cb) { + // _processChunk() is left for backwards compatibility + if (typeof cb === "function") processChunk(this, chunk, flushFlag, cb); + else return processChunkSync(this, chunk, flushFlag); +}; -function brotliCompressSync(buffer, opts) { - const encoder = createBrotliEncoder(opts, {}, null); - return encoder.encodeSync(buffer, undefined, true); +function processChunkSync(self, chunk, flushFlag) { + return self[kHandle].transformSync(chunk, undefined, false, flushFlag); } -function brotliDecompressSync(buffer, opts) { - const decoder = createBrotliDecoder(opts, {}, null); - return decoder.decodeSync(buffer, undefined, true); +function processChunk(self, chunk, flushFlag, cb) { + if (self._closed) return process.nextTick(cb); + self[kHandle].transformSync(chunk, undefined, false, flushFlag); } -function createBrotliCompress(opts) { - return new BrotliCompress(opts); +// + +function Zlib(options, method) { + ZlibBase.$call(this, options, method); } +Zlib.prototype = Object.create(ZlibBase.prototype); +alias(Zlib.prototype, "_level", "level"); +alias(Zlib.prototype, "_strategy", "strategy"); +Zlib.prototype.params = function (level, strategy, callback) { + return this[kHandle].params(level, strategy, callback); +}; +Zlib.prototype._transform = function _transform(chunk, encoding, callback) { + try { + this[kHandle].transformWith(chunk, encoding, this, false); + callback(); + } catch (err) { + callback(err, undefined); + } +}; -const kHandle = Symbol("kHandle"); +// function BrotliCompress(opts) { if (!(this instanceof BrotliCompress)) return new BrotliCompress(opts); - this[kHandle] = createBrotliEncoder(opts, {}, null); - stream.Transform.$apply(this, arguments); + ZlibBase.$call(this, opts, BROTLI_ENCODE); } -BrotliCompress.prototype = {}; -ObjectSetPrototypeOf(BrotliCompress.prototype, stream.Transform.prototype); +BrotliCompress.prototype = Object.create(ZlibBase.prototype); -BrotliCompress.prototype._transform = function _transform(chunk, encoding, callback) { - callback(undefined, this[kHandle].encodeSync(chunk, encoding, false)); -}; -BrotliCompress.prototype._flush = function _flush(callback) { - callback(undefined, this[kHandle].encodeSync("", undefined, true)); -}; - -function createBrotliDecompress(opts) { - return new BrotliDecompress(opts); -} +// function BrotliDecompress(opts) { if (!(this instanceof BrotliDecompress)) return new BrotliDecompress(opts); - this[kHandle] = createBrotliDecoder(opts, {}, null); - stream.Transform.$apply(this, arguments); + ZlibBase.$call(this, opts, BROTLI_DECODE); } -BrotliDecompress.prototype = {}; -ObjectSetPrototypeOf(BrotliDecompress.prototype, stream.Transform.prototype); +BrotliDecompress.prototype = Object.create(ZlibBase.prototype); -BrotliDecompress.prototype._transform = function (chunk, encoding, callback) { - callback(undefined, this[kHandle].decodeSync(chunk, encoding, false)); -}; -BrotliDecompress.prototype._flush = function (callback) { - callback(undefined, this[kHandle].decodeSync("", undefined, true)); -}; +// -// TODO: **use a native binding from Bun for this!!** -// This is a very slow module! -// It should really be fixed. It will show up in benchmarking. It also loads -// slowly. We need to fix it! -const assert = require("node:assert"); -const BufferModule = require("node:buffer"); -const StreamModule = require("node:stream"); -const Util = require("node:util"); -const { isAnyArrayBuffer, isArrayBufferView } = require("node:util/types"); +function Deflate(opts) { + if (!(this instanceof Deflate)) return new Deflate(opts); + Zlib.$call(this, opts, DEFLATE); +} +Deflate.prototype = Object.create(Zlib.prototype); -var __getOwnPropNames = Object.getOwnPropertyNames; -var __commonJS = (cb, mod: typeof module | undefined = undefined) => - function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; - }; +// -// node_modules/pako/lib/zlib/zstream.js -var require_zstream = __commonJS({ - "node_modules/pako/lib/zlib/zstream.js"(exports, module2) { - "use strict"; - function ZStream() { - this.input = null; - this.next_in = 0; - this.avail_in = 0; - this.total_in = 0; - this.output = null; - this.next_out = 0; - this.avail_out = 0; - this.total_out = 0; - this.msg = ""; - this.state = null; - this.data_type = 2; - this.adler = 0; - } - module2.exports = ZStream; - }, -}); +function Inflate(opts) { + if (!(this instanceof Inflate)) return new Inflate(opts); + Zlib.$call(this, opts, INFLATE); +} +Inflate.prototype = Object.create(Zlib.prototype); -// node_modules/pako/lib/utils/common.js -var require_common = __commonJS({ - "node_modules/pako/lib/utils/common.js"(exports) { - "use strict"; - var TYPED_OK = - typeof Uint8Array !== "undefined" && typeof Uint16Array !== "undefined" && typeof Int32Array !== "undefined"; - function _has(obj, key) { - return Object.prototype.hasOwnProperty.$call(obj, key); - } - exports.assign = function (obj) { - var sources = Array.prototype.slice.$call(arguments, 1); - while (sources.length) { - var source = sources.shift(); - if (!source) { - continue; - } - if (typeof source !== "object") { - throw new TypeError(source + "must be non-object"); - } - for (var p in source) { - if (_has(source, p)) { - obj[p] = source[p]; - } - } - } - return obj; - }; - exports.shrinkBuf = function (buf, size) { - if (buf.length === size) { - return buf; - } - if (buf.subarray) { - return buf.subarray(0, size); - } - buf.length = size; - return buf; - }; - var fnTyped = { - arraySet: function (dest, src, src_offs, len, dest_offs) { - if (src.subarray && dest.subarray) { - dest.set(src.subarray(src_offs, src_offs + len), dest_offs); - return; - } - for (var i = 0; i < len; i++) { - dest[dest_offs + i] = src[src_offs + i]; - } - }, - flattenChunks: function (chunks) { - var i, l, len, pos, chunk, result; - len = 0; - for (i = 0, l = chunks.length; i < l; i++) { - len += chunks[i].length; - } - result = new Uint8Array(len); - pos = 0; - for (i = 0, l = chunks.length; i < l; i++) { - chunk = chunks[i]; - result.set(chunk, pos); - pos += chunk.length; - } - return result; - }, - }; - var fnUntyped = { - arraySet: function (dest, src, src_offs, len, dest_offs) { - for (var i = 0; i < len; i++) { - dest[dest_offs + i] = src[src_offs + i]; - } - }, - flattenChunks: function (chunks) { - return [].concat.$apply([], chunks); - }, - }; - exports.setTyped = function (on) { - if (on) { - exports.Buf8 = Uint8Array; - exports.Buf16 = Uint16Array; - exports.Buf32 = Int32Array; - exports.assign(exports, fnTyped); - } else { - exports.Buf8 = Array; - exports.Buf16 = Array; - exports.Buf32 = Array; - exports.assign(exports, fnUntyped); - } - }; - exports.setTyped(TYPED_OK); - }, -}); +// -// node_modules/pako/lib/zlib/trees.js -var require_trees = __commonJS({ - "node_modules/pako/lib/zlib/trees.js"(exports) { - "use strict"; - var utils = require_common(); - var Z_FIXED = 4; - var Z_BINARY = 0; - var Z_TEXT = 1; - var Z_UNKNOWN = 2; - function zero(buf) { - var len = buf.length; - while (--len >= 0) { - buf[len] = 0; - } - } - var STORED_BLOCK = 0; - var STATIC_TREES = 1; - var DYN_TREES = 2; - var MIN_MATCH = 3; - var MAX_MATCH = 258; - var LENGTH_CODES = 29; - var LITERALS = 256; - var L_CODES = LITERALS + 1 + LENGTH_CODES; - var D_CODES = 30; - var BL_CODES = 19; - var HEAP_SIZE = 2 * L_CODES + 1; - var MAX_BITS = 15; - var Buf_size = 16; - var MAX_BL_BITS = 7; - var END_BLOCK = 256; - var REP_3_6 = 16; - var REPZ_3_10 = 17; - var REPZ_11_138 = 18; - var extra_lbits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]; - var extra_dbits = [ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, - ]; - var extra_blbits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]; - var bl_order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - var DIST_CODE_LEN = 512; - var static_ltree = new Array((L_CODES + 2) * 2); - zero(static_ltree); - var static_dtree = new Array(D_CODES * 2); - zero(static_dtree); - var _dist_code = new Array(DIST_CODE_LEN); - zero(_dist_code); - var _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); - zero(_length_code); - var base_length = new Array(LENGTH_CODES); - zero(base_length); - var base_dist = new Array(D_CODES); - zero(base_dist); - function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { - this.static_tree = static_tree; - this.extra_bits = extra_bits; - this.extra_base = extra_base; - this.elems = elems; - this.max_length = max_length; - this.has_stree = static_tree && static_tree.length; - } - var static_l_desc; - var static_d_desc; - var static_bl_desc; - function TreeDesc(dyn_tree, stat_desc) { - this.dyn_tree = dyn_tree; - this.max_code = 0; - this.stat_desc = stat_desc; - } - function d_code(dist) { - return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; - } - function put_short(s, w) { - s.pending_buf[s.pending++] = w & 255; - s.pending_buf[s.pending++] = (w >>> 8) & 255; - } - function send_bits(s, value, length) { - if (s.bi_valid > Buf_size - length) { - s.bi_buf |= (value << s.bi_valid) & 65535; - put_short(s, s.bi_buf); - s.bi_buf = value >> (Buf_size - s.bi_valid); - s.bi_valid += length - Buf_size; - } else { - s.bi_buf |= (value << s.bi_valid) & 65535; - s.bi_valid += length; - } - } - function send_code(s, c, tree) { - send_bits(s, tree[c * 2], tree[c * 2 + 1]); - } - function bi_reverse(code, len) { - var res = 0; - do { - res |= code & 1; - code >>>= 1; - res <<= 1; - } while (--len > 0); - return res >>> 1; - } - function bi_flush(s) { - if (s.bi_valid === 16) { - put_short(s, s.bi_buf); - s.bi_buf = 0; - s.bi_valid = 0; - } else if (s.bi_valid >= 8) { - s.pending_buf[s.pending++] = s.bi_buf & 255; - s.bi_buf >>= 8; - s.bi_valid -= 8; - } - } - function gen_bitlen(s, desc) { - var tree = desc.dyn_tree; - var max_code = desc.max_code; - var stree = desc.stat_desc.static_tree; - var has_stree = desc.stat_desc.has_stree; - var extra = desc.stat_desc.extra_bits; - var base = desc.stat_desc.extra_base; - var max_length = desc.stat_desc.max_length; - var h; - var n, m; - var bits; - var xbits; - var f; - var overflow = 0; - for (bits = 0; bits <= MAX_BITS; bits++) { - s.bl_count[bits] = 0; - } - tree[s.heap[s.heap_max] * 2 + 1] = 0; - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; - if (bits > max_length) { - bits = max_length; - overflow++; - } - tree[n * 2 + 1] = bits; - if (n > max_code) { - continue; - } - s.bl_count[bits]++; - xbits = 0; - if (n >= base) { - xbits = extra[n - base]; - } - f = tree[n * 2]; - s.opt_len += f * (bits + xbits); - if (has_stree) { - s.static_len += f * (stree[n * 2 + 1] + xbits); - } - } - if (overflow === 0) { - return; - } - do { - bits = max_length - 1; - while (s.bl_count[bits] === 0) { - bits--; - } - s.bl_count[bits]--; - s.bl_count[bits + 1] += 2; - s.bl_count[max_length]--; - overflow -= 2; - } while (overflow > 0); - for (bits = max_length; bits !== 0; bits--) { - n = s.bl_count[bits]; - while (n !== 0) { - m = s.heap[--h]; - if (m > max_code) { - continue; - } - if (tree[m * 2 + 1] !== bits) { - s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2]; - tree[m * 2 + 1] = bits; - } - n--; - } - } - } - function gen_codes(tree, max_code, bl_count) { - var next_code = new Array(MAX_BITS + 1); - var code = 0; - var bits; - var n; - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits - 1]) << 1; - } - for (n = 0; n <= max_code; n++) { - var len = tree[n * 2 + 1]; - if (len === 0) { - continue; - } - tree[n * 2] = bi_reverse(next_code[len]++, len); - } - } - function tr_static_init() { - var n; - var bits; - var length; - var code; - var dist; - var bl_count = new Array(MAX_BITS + 1); - length = 0; - for (code = 0; code < LENGTH_CODES - 1; code++) { - base_length[code] = length; - for (n = 0; n < 1 << extra_lbits[code]; n++) { - _length_code[length++] = code; - } - } - _length_code[length - 1] = code; - dist = 0; - for (code = 0; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < 1 << extra_dbits[code]; n++) { - _dist_code[dist++] = code; - } - } - dist >>= 7; - for (; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < 1 << (extra_dbits[code] - 7); n++) { - _dist_code[256 + dist++] = code; - } - } - for (bits = 0; bits <= MAX_BITS; bits++) { - bl_count[bits] = 0; - } - n = 0; - while (n <= 143) { - static_ltree[n * 2 + 1] = 8; - n++; - bl_count[8]++; - } - while (n <= 255) { - static_ltree[n * 2 + 1] = 9; - n++; - bl_count[9]++; - } - while (n <= 279) { - static_ltree[n * 2 + 1] = 7; - n++; - bl_count[7]++; - } - while (n <= 287) { - static_ltree[n * 2 + 1] = 8; - n++; - bl_count[8]++; - } - gen_codes(static_ltree, L_CODES + 1, bl_count); - for (n = 0; n < D_CODES; n++) { - static_dtree[n * 2 + 1] = 5; - static_dtree[n * 2] = bi_reverse(n, 5); - } - static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); - static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); - static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); - } - function init_block(s) { - var n; - for (n = 0; n < L_CODES; n++) { - s.dyn_ltree[n * 2] = 0; - } - for (n = 0; n < D_CODES; n++) { - s.dyn_dtree[n * 2] = 0; - } - for (n = 0; n < BL_CODES; n++) { - s.bl_tree[n * 2] = 0; - } - s.dyn_ltree[END_BLOCK * 2] = 1; - s.opt_len = s.static_len = 0; - s.last_lit = s.matches = 0; - } - function bi_windup(s) { - if (s.bi_valid > 8) { - put_short(s, s.bi_buf); - } else if (s.bi_valid > 0) { - s.pending_buf[s.pending++] = s.bi_buf; - } - s.bi_buf = 0; - s.bi_valid = 0; - } - function copy_block(s, buf, len, header) { - bi_windup(s); - if (header) { - put_short(s, len); - put_short(s, ~len); - } - utils.arraySet(s.pending_buf, s.window, buf, len, s.pending); - s.pending += len; - } - function smaller(tree, n, m, depth) { - var _n2 = n * 2; - var _m2 = m * 2; - return tree[_n2] < tree[_m2] || (tree[_n2] === tree[_m2] && depth[n] <= depth[m]); - } - function pqdownheap(s, tree, k) { - var v = s.heap[k]; - var j = k << 1; - while (j <= s.heap_len) { - if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { - j++; - } - if (smaller(tree, v, s.heap[j], s.depth)) { - break; - } - s.heap[k] = s.heap[j]; - k = j; - j <<= 1; - } - s.heap[k] = v; - } - function compress_block(s, ltree, dtree) { - var dist; - var lc; - var lx = 0; - var code; - var extra; - if (s.last_lit !== 0) { - do { - dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | s.pending_buf[s.d_buf + lx * 2 + 1]; - lc = s.pending_buf[s.l_buf + lx]; - lx++; - if (dist === 0) { - send_code(s, lc, ltree); - } else { - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); - extra = extra_lbits[code]; - if (extra !== 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); - } - dist--; - code = d_code(dist); - send_code(s, code, dtree); - extra = extra_dbits[code]; - if (extra !== 0) { - dist -= base_dist[code]; - send_bits(s, dist, extra); - } - } - } while (lx < s.last_lit); - } - send_code(s, END_BLOCK, ltree); - } - function build_tree(s, desc) { - var tree = desc.dyn_tree; - var stree = desc.stat_desc.static_tree; - var has_stree = desc.stat_desc.has_stree; - var elems = desc.stat_desc.elems; - var n, m; - var max_code = -1; - var node; - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - for (n = 0; n < elems; n++) { - if (tree[n * 2] !== 0) { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - } else { - tree[n * 2 + 1] = 0; - } - } - while (s.heap_len < 2) { - node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0; - tree[node * 2] = 1; - s.depth[node] = 0; - s.opt_len--; - if (has_stree) { - s.static_len -= stree[node * 2 + 1]; - } - } - desc.max_code = max_code; - for (n = s.heap_len >> 1; n >= 1; n--) { - pqdownheap(s, tree, n); - } - node = elems; - do { - n = s.heap[1]; - s.heap[1] = s.heap[s.heap_len--]; - pqdownheap(s, tree, 1); - m = s.heap[1]; - s.heap[--s.heap_max] = n; - s.heap[--s.heap_max] = m; - tree[node * 2] = tree[n * 2] + tree[m * 2]; - s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; - tree[n * 2 + 1] = tree[m * 2 + 1] = node; - s.heap[1] = node++; - pqdownheap(s, tree, 1); - } while (s.heap_len >= 2); - s.heap[--s.heap_max] = s.heap[1]; - gen_bitlen(s, desc); - gen_codes(tree, max_code, s.bl_count); - } - function scan_tree(s, tree, max_code) { - var n; - var prevlen = -1; - var curlen; - var nextlen = tree[0 * 2 + 1]; - var count = 0; - var max_count = 7; - var min_count = 4; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - tree[(max_code + 1) * 2 + 1] = 65535; - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen === nextlen) { - continue; - } else if (count < min_count) { - s.bl_tree[curlen * 2] += count; - } else if (curlen !== 0) { - if (curlen !== prevlen) { - s.bl_tree[curlen * 2]++; - } - s.bl_tree[REP_3_6 * 2]++; - } else if (count <= 10) { - s.bl_tree[REPZ_3_10 * 2]++; - } else { - s.bl_tree[REPZ_11_138 * 2]++; - } - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - } else { - max_count = 7; - min_count = 4; - } - } - } - function send_tree(s, tree, max_code) { - var n; - var prevlen = -1; - var curlen; - var nextlen = tree[0 * 2 + 1]; - var count = 0; - var max_count = 7; - var min_count = 4; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen === nextlen) { - continue; - } else if (count < min_count) { - do { - send_code(s, curlen, s.bl_tree); - } while (--count !== 0); - } else if (curlen !== 0) { - if (curlen !== prevlen) { - send_code(s, curlen, s.bl_tree); - count--; - } - send_code(s, REP_3_6, s.bl_tree); - send_bits(s, count - 3, 2); - } else if (count <= 10) { - send_code(s, REPZ_3_10, s.bl_tree); - send_bits(s, count - 3, 3); - } else { - send_code(s, REPZ_11_138, s.bl_tree); - send_bits(s, count - 11, 7); - } - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - } else { - max_count = 7; - min_count = 4; - } - } - } - function build_bl_tree(s) { - var max_blindex; - scan_tree(s, s.dyn_ltree, s.l_desc.max_code); - scan_tree(s, s.dyn_dtree, s.d_desc.max_code); - build_tree(s, s.bl_desc); - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { - if (s.bl_tree[bl_order[max_blindex] * 2 + 1] !== 0) { - break; - } - } - s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - return max_blindex; - } - function send_all_trees(s, lcodes, dcodes, blcodes) { - var rank; - send_bits(s, lcodes - 257, 5); - send_bits(s, dcodes - 1, 5); - send_bits(s, blcodes - 4, 4); - for (rank = 0; rank < blcodes; rank++) { - send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1], 3); - } - send_tree(s, s.dyn_ltree, lcodes - 1); - send_tree(s, s.dyn_dtree, dcodes - 1); - } - function detect_data_type(s) { - var black_mask = 4093624447; - var n; - for (n = 0; n <= 31; n++, black_mask >>>= 1) { - if (black_mask & 1 && s.dyn_ltree[n * 2] !== 0) { - return Z_BINARY; - } - } - if (s.dyn_ltree[9 * 2] !== 0 || s.dyn_ltree[10 * 2] !== 0 || s.dyn_ltree[13 * 2] !== 0) { - return Z_TEXT; - } - for (n = 32; n < LITERALS; n++) { - if (s.dyn_ltree[n * 2] !== 0) { - return Z_TEXT; - } - } - return Z_BINARY; - } - var static_init_done = false; - function _tr_init(s) { - if (!static_init_done) { - tr_static_init(); - static_init_done = true; - } - s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); - s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); - s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); - s.bi_buf = 0; - s.bi_valid = 0; - init_block(s); - } - function _tr_stored_block(s, buf, stored_len, last) { - send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); - copy_block(s, buf, stored_len, true); - } - function _tr_align(s) { - send_bits(s, STATIC_TREES << 1, 3); - send_code(s, END_BLOCK, static_ltree); - bi_flush(s); - } - function _tr_flush_block(s, buf, stored_len, last) { - var opt_lenb, static_lenb; - var max_blindex = 0; - if (s.level > 0) { - if (s.strm.data_type === Z_UNKNOWN) { - s.strm.data_type = detect_data_type(s); - } - build_tree(s, s.l_desc); - build_tree(s, s.d_desc); - max_blindex = build_bl_tree(s); - opt_lenb = (s.opt_len + 3 + 7) >>> 3; - static_lenb = (s.static_len + 3 + 7) >>> 3; - if (static_lenb <= opt_lenb) { - opt_lenb = static_lenb; - } - } else { - opt_lenb = static_lenb = stored_len + 5; - } - if (stored_len + 4 <= opt_lenb && buf !== -1) { - _tr_stored_block(s, buf, stored_len, last); - } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { - send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); - compress_block(s, static_ltree, static_dtree); - } else { - send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); - send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); - compress_block(s, s.dyn_ltree, s.dyn_dtree); - } - init_block(s); - if (last) { - bi_windup(s); - } - } - function _tr_tally(s, dist, lc) { - s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 255; - s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 255; - s.pending_buf[s.l_buf + s.last_lit] = lc & 255; - s.last_lit++; - if (dist === 0) { - s.dyn_ltree[lc * 2]++; - } else { - s.matches++; - dist--; - s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]++; - s.dyn_dtree[d_code(dist) * 2]++; - } - return s.last_lit === s.lit_bufsize - 1; - } - exports._tr_init = _tr_init; - exports._tr_stored_block = _tr_stored_block; - exports._tr_flush_block = _tr_flush_block; - exports._tr_tally = _tr_tally; - exports._tr_align = _tr_align; - }, -}); +function DeflateRaw(opts) { + if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); + Zlib.$call(this, opts, DEFLATERAW); +} +DeflateRaw.prototype = Object.create(Zlib.prototype); -// node_modules/pako/lib/zlib/adler32.js -var require_adler32 = __commonJS({ - "node_modules/pako/lib/zlib/adler32.js"(exports, module2) { - "use strict"; - function adler32(adler, buf, len, pos) { - var s1 = (adler & 65535) | 0, - s2 = ((adler >>> 16) & 65535) | 0, - n = 0; - while (len !== 0) { - n = len > 2e3 ? 2e3 : len; - len -= n; - do { - s1 = (s1 + buf[pos++]) | 0; - s2 = (s2 + s1) | 0; - } while (--n); - s1 %= 65521; - s2 %= 65521; - } - return s1 | (s2 << 16) | 0; - } - module2.exports = adler32; - }, -}); +// -// node_modules/pako/lib/zlib/crc32.js -var require_crc32 = __commonJS({ - "node_modules/pako/lib/zlib/crc32.js"(exports, module2) { - "use strict"; - function makeTable() { - var c, - table = []; - for (var n = 0; n < 256; n++) { - c = n; - for (var k = 0; k < 8; k++) { - c = c & 1 ? 3988292384 ^ (c >>> 1) : c >>> 1; - } - table[n] = c; - } - return table; - } - var crcTable = makeTable(); - function crc32(crc, buf, len, pos) { - var t = crcTable, - end = pos + len; - crc ^= -1; - for (var i = pos; i < end; i++) { - crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 255]; - } - return crc ^ -1; - } - module2.exports = crc32; - }, -}); +function InflateRaw(opts) { + if (!(this instanceof InflateRaw)) return new InflateRaw(opts); + Zlib.$call(this, opts, INFLATERAW); +} +InflateRaw.prototype = Object.create(Zlib.prototype); -// node_modules/pako/lib/zlib/messages.js -var require_messages = __commonJS({ - "node_modules/pako/lib/zlib/messages.js"(exports, module2) { - "use strict"; - module2.exports = { - 2: "need dictionary", - 1: "stream end", - 0: "", - "-1": "file error", - "-2": "stream error", - "-3": "data error", - "-4": "insufficient memory", - "-5": "buffer error", - "-6": "incompatible version", - }; - }, -}); +// -// node_modules/pako/lib/zlib/deflate.js -var require_deflate = __commonJS({ - "node_modules/pako/lib/zlib/deflate.js"(exports) { - "use strict"; - var utils = require_common(); - var trees = require_trees(); - var adler32 = require_adler32(); - var crc32 = require_crc32(); - var msg = require_messages(); - var Z_NO_FLUSH = 0; - var Z_PARTIAL_FLUSH = 1; - var Z_FULL_FLUSH = 3; - var Z_FINISH = 4; - var Z_BLOCK = 5; - var Z_OK = 0; - var Z_STREAM_END = 1; - var Z_STREAM_ERROR = -2; - var Z_DATA_ERROR = -3; - var Z_BUF_ERROR = -5; - var Z_DEFAULT_COMPRESSION = -1; - var Z_FILTERED = 1; - var Z_HUFFMAN_ONLY = 2; - var Z_RLE = 3; - var Z_FIXED = 4; - var Z_DEFAULT_STRATEGY = 0; - var Z_UNKNOWN = 2; - var Z_DEFLATED = 8; - var MAX_MEM_LEVEL = 9; - var MAX_WBITS = 15; - var DEF_MEM_LEVEL = 8; - var LENGTH_CODES = 29; - var LITERALS = 256; - var L_CODES = LITERALS + 1 + LENGTH_CODES; - var D_CODES = 30; - var BL_CODES = 19; - var HEAP_SIZE = 2 * L_CODES + 1; - var MAX_BITS = 15; - var MIN_MATCH = 3; - var MAX_MATCH = 258; - var MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; - var PRESET_DICT = 32; - var INIT_STATE = 42; - var EXTRA_STATE = 69; - var NAME_STATE = 73; - var COMMENT_STATE = 91; - var HCRC_STATE = 103; - var BUSY_STATE = 113; - var FINISH_STATE = 666; - var BS_NEED_MORE = 1; - var BS_BLOCK_DONE = 2; - var BS_FINISH_STARTED = 3; - var BS_FINISH_DONE = 4; - var OS_CODE = 3; - function err(strm, errorCode) { - strm.msg = msg[errorCode]; - return errorCode; - } - function rank(f) { - return (f << 1) - (f > 4 ? 9 : 0); - } - function zero(buf) { - var len = buf.length; - while (--len >= 0) { - buf[len] = 0; - } - } - function flush_pending(strm) { - var s = strm.state; - var len = s.pending; - if (len > strm.avail_out) { - len = strm.avail_out; - } - if (len === 0) { - return; - } - utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out); - strm.next_out += len; - s.pending_out += len; - strm.total_out += len; - strm.avail_out -= len; - s.pending -= len; - if (s.pending === 0) { - s.pending_out = 0; - } - } - function flush_block_only(s, last) { - trees._tr_flush_block(s, s.block_start >= 0 ? s.block_start : -1, s.strstart - s.block_start, last); - s.block_start = s.strstart; - flush_pending(s.strm); - } - function put_byte(s, b) { - s.pending_buf[s.pending++] = b; - } - function putShortMSB(s, b) { - s.pending_buf[s.pending++] = (b >>> 8) & 255; - s.pending_buf[s.pending++] = b & 255; - } - function read_buf(strm, buf, start, size) { - var len = strm.avail_in; - if (len > size) { - len = size; - } - if (len === 0) { - return 0; - } - strm.avail_in -= len; - utils.arraySet(buf, strm.input, strm.next_in, len, start); - if (strm.state.wrap === 1) { - strm.adler = adler32(strm.adler, buf, len, start); - } else if (strm.state.wrap === 2) { - strm.adler = crc32(strm.adler, buf, len, start); - } - strm.next_in += len; - strm.total_in += len; - return len; - } - function longest_match(s, cur_match) { - var chain_length = s.max_chain_length; - var scan = s.strstart; - var match; - var len; - var best_len = s.prev_length; - var nice_match = s.nice_match; - var limit = s.strstart > s.w_size - MIN_LOOKAHEAD ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0; - var _win = s.window; - var wmask = s.w_mask; - var prev = s.prev; - var strend = s.strstart + MAX_MATCH; - var scan_end1 = _win[scan + best_len - 1]; - var scan_end = _win[scan + best_len]; - if (s.prev_length >= s.good_match) { - chain_length >>= 2; - } - if (nice_match > s.lookahead) { - nice_match = s.lookahead; - } - do { - match = cur_match; - if ( - _win[match + best_len] !== scan_end || - _win[match + best_len - 1] !== scan_end1 || - _win[match] !== _win[scan] || - _win[++match] !== _win[scan + 1] - ) { - continue; - } - scan += 2; - match++; - do {} while ( - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && - scan < strend - ); - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - if (len > best_len) { - s.match_start = cur_match; - best_len = len; - if (len >= nice_match) { - break; - } - scan_end1 = _win[scan + best_len - 1]; - scan_end = _win[scan + best_len]; - } - } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); - if (best_len <= s.lookahead) { - return best_len; - } - return s.lookahead; - } - function fill_window(s) { - var _w_size = s.w_size; - var p, n, m, more, str; - do { - more = s.window_size - s.lookahead - s.strstart; - if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { - utils.arraySet(s.window, s.window, _w_size, _w_size, 0); - s.match_start -= _w_size; - s.strstart -= _w_size; - s.block_start -= _w_size; - n = s.hash_size; - p = n; - do { - m = s.head[--p]; - s.head[p] = m >= _w_size ? m - _w_size : 0; - } while (--n); - n = _w_size; - p = n; - do { - m = s.prev[--p]; - s.prev[p] = m >= _w_size ? m - _w_size : 0; - } while (--n); - more += _w_size; - } - if (s.strm.avail_in === 0) { - break; - } - n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); - s.lookahead += n; - if (s.lookahead + s.insert >= MIN_MATCH) { - str = s.strstart - s.insert; - s.ins_h = s.window[str]; - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask; - while (s.insert) { - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; - s.prev[str & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = str; - str++; - s.insert--; - if (s.lookahead + s.insert < MIN_MATCH) { - break; - } - } - } - } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); - } - function deflate_stored(s, flush) { - var max_block_size = 65535; - if (max_block_size > s.pending_buf_size - 5) { - max_block_size = s.pending_buf_size - 5; - } - for (;;) { - if (s.lookahead <= 1) { - fill_window(s); - if (s.lookahead === 0 && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; - } - } - s.strstart += s.lookahead; - s.lookahead = 0; - var max_start = s.block_start + max_block_size; - if (s.strstart === 0 || s.strstart >= max_start) { - s.lookahead = s.strstart - max_start; - s.strstart = max_start; - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - if (s.strstart - s.block_start >= s.w_size - MIN_LOOKAHEAD) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - } - s.insert = 0; - if (flush === Z_FINISH) { - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - return BS_FINISH_DONE; - } - if (s.strstart > s.block_start) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - return BS_NEED_MORE; - } - function deflate_fast(s, flush) { - var hash_head; - var bflush; - for (;;) { - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; - } - } - hash_head = 0; - if (s.lookahead >= MIN_MATCH) { - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - } - if (hash_head !== 0 && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD) { - s.match_length = longest_match(s, hash_head); - } - if (s.match_length >= MIN_MATCH) { - bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); - s.lookahead -= s.match_length; - if (s.match_length <= s.max_lazy_match && s.lookahead >= MIN_MATCH) { - s.match_length--; - do { - s.strstart++; - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - } while (--s.match_length !== 0); - s.strstart++; - } else { - s.strstart += s.match_length; - s.match_length = 0; - s.ins_h = s.window[s.strstart]; - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask; - } - } else { - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - s.lookahead--; - s.strstart++; - } - if (bflush) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - } - s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; - if (flush === Z_FINISH) { - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - return BS_FINISH_DONE; - } - if (s.last_lit) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - return BS_BLOCK_DONE; - } - function deflate_slow(s, flush) { - var hash_head; - var bflush; - var max_insert; - for (;;) { - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; - } - } - hash_head = 0; - if (s.lookahead >= MIN_MATCH) { - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - } - s.prev_length = s.match_length; - s.prev_match = s.match_start; - s.match_length = MIN_MATCH - 1; - if (hash_head !== 0 && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD) { - s.match_length = longest_match(s, hash_head); - if ( - s.match_length <= 5 && - (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096)) - ) { - s.match_length = MIN_MATCH - 1; - } - } - if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { - max_insert = s.strstart + s.lookahead - MIN_MATCH; - bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); - s.lookahead -= s.prev_length - 1; - s.prev_length -= 2; - do { - if (++s.strstart <= max_insert) { - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - } - } while (--s.prev_length !== 0); - s.match_available = 0; - s.match_length = MIN_MATCH - 1; - s.strstart++; - if (bflush) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - } else if (s.match_available) { - bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); - if (bflush) { - flush_block_only(s, false); - } - s.strstart++; - s.lookahead--; - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } else { - s.match_available = 1; - s.strstart++; - s.lookahead--; - } - } - if (s.match_available) { - bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); - s.match_available = 0; - } - s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; - if (flush === Z_FINISH) { - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - return BS_FINISH_DONE; - } - if (s.last_lit) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - return BS_BLOCK_DONE; - } - function deflate_rle(s, flush) { - var bflush; - var prev; - var scan, strend; - var _win = s.window; - for (;;) { - if (s.lookahead <= MAX_MATCH) { - fill_window(s); - if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; - } - } - s.match_length = 0; - if (s.lookahead >= MIN_MATCH && s.strstart > 0) { - scan = s.strstart - 1; - prev = _win[scan]; - if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { - strend = s.strstart + MAX_MATCH; - do {} while ( - prev === _win[++scan] && - prev === _win[++scan] && - prev === _win[++scan] && - prev === _win[++scan] && - prev === _win[++scan] && - prev === _win[++scan] && - prev === _win[++scan] && - prev === _win[++scan] && - scan < strend - ); - s.match_length = MAX_MATCH - (strend - scan); - if (s.match_length > s.lookahead) { - s.match_length = s.lookahead; - } - } - } - if (s.match_length >= MIN_MATCH) { - bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH); - s.lookahead -= s.match_length; - s.strstart += s.match_length; - s.match_length = 0; - } else { - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - s.lookahead--; - s.strstart++; - } - if (bflush) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - } - s.insert = 0; - if (flush === Z_FINISH) { - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - return BS_FINISH_DONE; - } - if (s.last_lit) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - return BS_BLOCK_DONE; - } - function deflate_huff(s, flush) { - var bflush; - for (;;) { - if (s.lookahead === 0) { - fill_window(s); - if (s.lookahead === 0) { - if (flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - break; - } - } - s.match_length = 0; - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - s.lookahead--; - s.strstart++; - if (bflush) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - } - s.insert = 0; - if (flush === Z_FINISH) { - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - return BS_FINISH_DONE; - } - if (s.last_lit) { - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } - return BS_BLOCK_DONE; - } - function Config(good_length, max_lazy, nice_length, max_chain, func) { - this.good_length = good_length; - this.max_lazy = max_lazy; - this.nice_length = nice_length; - this.max_chain = max_chain; - this.func = func; - } - var configuration_table; - configuration_table = [ - new Config(0, 0, 0, 0, deflate_stored), - new Config(4, 4, 8, 4, deflate_fast), - new Config(4, 5, 16, 8, deflate_fast), - new Config(4, 6, 32, 32, deflate_fast), - new Config(4, 4, 16, 16, deflate_slow), - new Config(8, 16, 32, 32, deflate_slow), - new Config(8, 16, 128, 128, deflate_slow), - new Config(8, 32, 128, 256, deflate_slow), - new Config(32, 128, 258, 1024, deflate_slow), - new Config(32, 258, 258, 4096, deflate_slow), - ]; - function lm_init(s) { - s.window_size = 2 * s.w_size; - zero(s.head); - s.max_lazy_match = configuration_table[s.level].max_lazy; - s.good_match = configuration_table[s.level].good_length; - s.nice_match = configuration_table[s.level].nice_length; - s.max_chain_length = configuration_table[s.level].max_chain; - s.strstart = 0; - s.block_start = 0; - s.lookahead = 0; - s.insert = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - s.ins_h = 0; - } - function DeflateState() { - this.strm = null; - this.status = 0; - this.pending_buf = null; - this.pending_buf_size = 0; - this.pending_out = 0; - this.pending = 0; - this.wrap = 0; - this.gzhead = null; - this.gzindex = 0; - this.method = Z_DEFLATED; - this.last_flush = -1; - this.w_size = 0; - this.w_bits = 0; - this.w_mask = 0; - this.window = null; - this.window_size = 0; - this.prev = null; - this.head = null; - this.ins_h = 0; - this.hash_size = 0; - this.hash_bits = 0; - this.hash_mask = 0; - this.hash_shift = 0; - this.block_start = 0; - this.match_length = 0; - this.prev_match = 0; - this.match_available = 0; - this.strstart = 0; - this.match_start = 0; - this.lookahead = 0; - this.prev_length = 0; - this.max_chain_length = 0; - this.max_lazy_match = 0; - this.level = 0; - this.strategy = 0; - this.good_match = 0; - this.nice_match = 0; - this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2); - this.dyn_dtree = new utils.Buf16((2 * D_CODES + 1) * 2); - this.bl_tree = new utils.Buf16((2 * BL_CODES + 1) * 2); - zero(this.dyn_ltree); - zero(this.dyn_dtree); - zero(this.bl_tree); - this.l_desc = null; - this.d_desc = null; - this.bl_desc = null; - this.bl_count = new utils.Buf16(MAX_BITS + 1); - this.heap = new utils.Buf16(2 * L_CODES + 1); - zero(this.heap); - this.heap_len = 0; - this.heap_max = 0; - this.depth = new utils.Buf16(2 * L_CODES + 1); - zero(this.depth); - this.l_buf = 0; - this.lit_bufsize = 0; - this.last_lit = 0; - this.d_buf = 0; - this.opt_len = 0; - this.static_len = 0; - this.matches = 0; - this.insert = 0; - this.bi_buf = 0; - this.bi_valid = 0; - } - function deflateResetKeep(strm) { - var s; - if (!strm || !strm.state) { - return err(strm, Z_STREAM_ERROR); - } - strm.total_in = strm.total_out = 0; - strm.data_type = Z_UNKNOWN; - s = strm.state; - s.pending = 0; - s.pending_out = 0; - if (s.wrap < 0) { - s.wrap = -s.wrap; - } - s.status = s.wrap ? INIT_STATE : BUSY_STATE; - strm.adler = s.wrap === 2 ? 0 : 1; - s.last_flush = Z_NO_FLUSH; - trees._tr_init(s); - return Z_OK; - } - function deflateReset(strm) { - var ret = deflateResetKeep(strm); - if (ret === Z_OK) { - lm_init(strm.state); - } - return ret; - } - function deflateSetHeader(strm, head) { - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - if (strm.state.wrap !== 2) { - return Z_STREAM_ERROR; - } - strm.state.gzhead = head; - return Z_OK; - } - function deflateInit2(strm, level, method, windowBits, memLevel, strategy) { - if (!strm) { - return Z_STREAM_ERROR; - } - var wrap = 1; - if (level === Z_DEFAULT_COMPRESSION) { - level = 6; - } - if (windowBits < 0) { - wrap = 0; - windowBits = -windowBits; - } else if (windowBits > 15) { - wrap = 2; - windowBits -= 16; - } - if ( - memLevel < 1 || - memLevel > MAX_MEM_LEVEL || - method !== Z_DEFLATED || - windowBits < 8 || - windowBits > 15 || - level < 0 || - level > 9 || - strategy < 0 || - strategy > Z_FIXED - ) { - return err(strm, Z_STREAM_ERROR); - } - if (windowBits === 8) { - windowBits = 9; - } - var s = new DeflateState(); - strm.state = s; - s.strm = strm; - s.wrap = wrap; - s.gzhead = null; - s.w_bits = windowBits; - s.w_size = 1 << s.w_bits; - s.w_mask = s.w_size - 1; - s.hash_bits = memLevel + 7; - s.hash_size = 1 << s.hash_bits; - s.hash_mask = s.hash_size - 1; - s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); - s.window = new utils.Buf8(s.w_size * 2); - s.head = new utils.Buf16(s.hash_size); - s.prev = new utils.Buf16(s.w_size); - s.lit_bufsize = 1 << (memLevel + 6); - s.pending_buf_size = s.lit_bufsize * 4; - s.pending_buf = new utils.Buf8(s.pending_buf_size); - s.d_buf = 1 * s.lit_bufsize; - s.l_buf = (1 + 2) * s.lit_bufsize; - s.level = level; - s.strategy = strategy; - s.method = method; - return deflateReset(strm); - } - function deflateInit(strm, level) { - return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - } - function deflate(strm, flush) { - var old_flush, s; - var beg, val; - if (!strm || !strm.state || flush > Z_BLOCK || flush < 0) { - return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; - } - s = strm.state; - if (!strm.output || (!strm.input && strm.avail_in !== 0) || (s.status === FINISH_STATE && flush !== Z_FINISH)) { - return err(strm, strm.avail_out === 0 ? Z_BUF_ERROR : Z_STREAM_ERROR); - } - s.strm = strm; - old_flush = s.last_flush; - s.last_flush = flush; - if (s.status === INIT_STATE) { - if (s.wrap === 2) { - strm.adler = 0; - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (!s.gzhead) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0); - put_byte(s, OS_CODE); - s.status = BUSY_STATE; - } else { - put_byte( - s, - (s.gzhead.text ? 1 : 0) + - (s.gzhead.hcrc ? 2 : 0) + - (!s.gzhead.extra ? 0 : 4) + - (!s.gzhead.name ? 0 : 8) + - (!s.gzhead.comment ? 0 : 16), - ); - put_byte(s, s.gzhead.time & 255); - put_byte(s, (s.gzhead.time >> 8) & 255); - put_byte(s, (s.gzhead.time >> 16) & 255); - put_byte(s, (s.gzhead.time >> 24) & 255); - put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0); - put_byte(s, s.gzhead.os & 255); - if (s.gzhead.extra && s.gzhead.extra.length) { - put_byte(s, s.gzhead.extra.length & 255); - put_byte(s, (s.gzhead.extra.length >> 8) & 255); - } - if (s.gzhead.hcrc) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0); - } - s.gzindex = 0; - s.status = EXTRA_STATE; - } - } else { - var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; - var level_flags = -1; - if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { - level_flags = 0; - } else if (s.level < 6) { - level_flags = 1; - } else if (s.level === 6) { - level_flags = 2; - } else { - level_flags = 3; - } - header |= level_flags << 6; - if (s.strstart !== 0) { - header |= PRESET_DICT; - } - header += 31 - (header % 31); - s.status = BUSY_STATE; - putShortMSB(s, header); - if (s.strstart !== 0) { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 65535); - } - strm.adler = 1; - } - } - if (s.status === EXTRA_STATE) { - if (s.gzhead.extra) { - beg = s.pending; - while (s.gzindex < (s.gzhead.extra.length & 65535)) { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - break; - } - } - put_byte(s, s.gzhead.extra[s.gzindex] & 255); - s.gzindex++; - } - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (s.gzindex === s.gzhead.extra.length) { - s.gzindex = 0; - s.status = NAME_STATE; - } - } else { - s.status = NAME_STATE; - } - } - if (s.status === NAME_STATE) { - if (s.gzhead.name) { - beg = s.pending; - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - if (s.gzindex < s.gzhead.name.length) { - val = s.gzhead.name.charCodeAt(s.gzindex++) & 255; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.gzindex = 0; - s.status = COMMENT_STATE; - } - } else { - s.status = COMMENT_STATE; - } - } - if (s.status === COMMENT_STATE) { - if (s.gzhead.comment) { - beg = s.pending; - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - if (s.gzindex < s.gzhead.comment.length) { - val = s.gzhead.comment.charCodeAt(s.gzindex++) & 255; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.status = HCRC_STATE; - } - } else { - s.status = HCRC_STATE; - } - } - if (s.status === HCRC_STATE) { - if (s.gzhead.hcrc) { - if (s.pending + 2 > s.pending_buf_size) { - flush_pending(strm); - } - if (s.pending + 2 <= s.pending_buf_size) { - put_byte(s, strm.adler & 255); - put_byte(s, (strm.adler >> 8) & 255); - strm.adler = 0; - s.status = BUSY_STATE; - } - } else { - s.status = BUSY_STATE; - } - } - if (s.pending !== 0) { - flush_pending(strm); - if (strm.avail_out === 0) { - s.last_flush = -1; - return Z_OK; - } - } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH) { - return err(strm, Z_BUF_ERROR); - } - if (s.status === FINISH_STATE && strm.avail_in !== 0) { - return err(strm, Z_BUF_ERROR); - } - if (strm.avail_in !== 0 || s.lookahead !== 0 || (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { - var bstate = - s.strategy === Z_HUFFMAN_ONLY - ? deflate_huff(s, flush) - : s.strategy === Z_RLE - ? deflate_rle(s, flush) - : configuration_table[s.level].func(s, flush); - if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { - s.status = FINISH_STATE; - } - if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { - if (strm.avail_out === 0) { - s.last_flush = -1; - } - return Z_OK; - } - if (bstate === BS_BLOCK_DONE) { - if (flush === Z_PARTIAL_FLUSH) { - trees._tr_align(s); - } else if (flush !== Z_BLOCK) { - trees._tr_stored_block(s, 0, 0, false); - if (flush === Z_FULL_FLUSH) { - zero(s.head); - if (s.lookahead === 0) { - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - } - } - flush_pending(strm); - if (strm.avail_out === 0) { - s.last_flush = -1; - return Z_OK; - } - } - } - if (flush !== Z_FINISH) { - return Z_OK; - } - if (s.wrap <= 0) { - return Z_STREAM_END; - } - if (s.wrap === 2) { - put_byte(s, strm.adler & 255); - put_byte(s, (strm.adler >> 8) & 255); - put_byte(s, (strm.adler >> 16) & 255); - put_byte(s, (strm.adler >> 24) & 255); - put_byte(s, strm.total_in & 255); - put_byte(s, (strm.total_in >> 8) & 255); - put_byte(s, (strm.total_in >> 16) & 255); - put_byte(s, (strm.total_in >> 24) & 255); - } else { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 65535); - } - flush_pending(strm); - if (s.wrap > 0) { - s.wrap = -s.wrap; - } - return s.pending !== 0 ? Z_OK : Z_STREAM_END; - } - function deflateEnd(strm) { - var status; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - status = strm.state.status; - if ( - status !== INIT_STATE && - status !== EXTRA_STATE && - status !== NAME_STATE && - status !== COMMENT_STATE && - status !== HCRC_STATE && - status !== BUSY_STATE && - status !== FINISH_STATE - ) { - return err(strm, Z_STREAM_ERROR); - } - strm.state = null; - return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; - } - function deflateSetDictionary(strm, dictionary) { - var dictLength = dictionary.length; - var s; - var str, n; - var wrap; - var avail; - var next; - var input; - var tmpDict; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - s = strm.state; - wrap = s.wrap; - if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { - return Z_STREAM_ERROR; - } - if (wrap === 1) { - strm.adler = adler32(strm.adler, dictionary, dictLength, 0); - } - s.wrap = 0; - if (dictLength >= s.w_size) { - if (wrap === 0) { - zero(s.head); - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - tmpDict = new utils.Buf8(s.w_size); - utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0); - dictionary = tmpDict; - dictLength = s.w_size; - } - avail = strm.avail_in; - next = strm.next_in; - input = strm.input; - strm.avail_in = dictLength; - strm.next_in = 0; - strm.input = dictionary; - fill_window(s); - while (s.lookahead >= MIN_MATCH) { - str = s.strstart; - n = s.lookahead - (MIN_MATCH - 1); - do { - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; - s.prev[str & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = str; - str++; - } while (--n); - s.strstart = str; - s.lookahead = MIN_MATCH - 1; - fill_window(s); - } - s.strstart += s.lookahead; - s.block_start = s.strstart; - s.insert = s.lookahead; - s.lookahead = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - strm.next_in = next; - strm.input = input; - strm.avail_in = avail; - s.wrap = wrap; - return Z_OK; - } - exports.deflateInit = deflateInit; - exports.deflateInit2 = deflateInit2; - exports.deflateReset = deflateReset; - exports.deflateResetKeep = deflateResetKeep; - exports.deflateSetHeader = deflateSetHeader; - exports.deflate = deflate; - exports.deflateEnd = deflateEnd; - exports.deflateSetDictionary = deflateSetDictionary; - exports.deflateInfo = "pako deflate (from Nodeca project)"; - }, -}); +function Gzip(opts) { + if (!(this instanceof Gzip)) return new Gzip(opts); + Zlib.$call(this, opts, GZIP); +} +Gzip.prototype = Object.create(Zlib.prototype); -// node_modules/pako/lib/zlib/inffast.js -var require_inffast = __commonJS({ - "node_modules/pako/lib/zlib/inffast.js"(exports, module2) { - "use strict"; - var BAD = 30; - var TYPE = 12; - module2.exports = function inflate_fast(strm, start) { - var state; - var _in; - var last; - var _out; - var beg; - var end; - var dmax; - var wsize; - var whave; - var wnext; - var s_window; - var hold; - var bits; - var lcode; - var dcode; - var lmask; - var dmask; - var here; - var op; - var len; - var dist; - var from; - var from_source; - var input, output; - state = strm.state; - _in = strm.next_in; - input = strm.input; - last = _in + (strm.avail_in - 5); - _out = strm.next_out; - output = strm.output; - beg = _out - (start - strm.avail_out); - end = _out + (strm.avail_out - 257); - dmax = state.dmax; - wsize = state.wsize; - whave = state.whave; - wnext = state.wnext; - s_window = state.window; - hold = state.hold; - bits = state.bits; - lcode = state.lencode; - dcode = state.distcode; - lmask = (1 << state.lenbits) - 1; - dmask = (1 << state.distbits) - 1; - top: do { - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - here = lcode[hold & lmask]; - dolen: for (;;) { - op = here >>> 24; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 255; - if (op === 0) { - output[_out++] = here & 65535; - } else if (op & 16) { - len = here & 65535; - op &= 15; - if (op) { - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - len += hold & ((1 << op) - 1); - hold >>>= op; - bits -= op; - } - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - here = dcode[hold & dmask]; - dodist: for (;;) { - op = here >>> 24; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 255; - if (op & 16) { - dist = here & 65535; - op &= 15; - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - } - dist += hold & ((1 << op) - 1); - if (dist > dmax) { - strm.msg = "invalid distance too far back"; - state.mode = BAD; - break top; - } - hold >>>= op; - bits -= op; - op = _out - beg; - if (dist > op) { - op = dist - op; - if (op > whave) { - if (state.sane) { - strm.msg = "invalid distance too far back"; - state.mode = BAD; - break top; - } - } - from = 0; - from_source = s_window; - if (wnext === 0) { - from += wsize - op; - if (op < len) { - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; - from_source = output; - } - } else if (wnext < op) { - from += wsize + wnext - op; - op -= wnext; - if (op < len) { - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = 0; - if (wnext < len) { - op = wnext; - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; - from_source = output; - } - } - } else { - from += wnext - op; - if (op < len) { - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; - from_source = output; - } - } - while (len > 2) { - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - len -= 3; - } - if (len) { - output[_out++] = from_source[from++]; - if (len > 1) { - output[_out++] = from_source[from++]; - } - } - } else { - from = _out - dist; - do { - output[_out++] = output[from++]; - output[_out++] = output[from++]; - output[_out++] = output[from++]; - len -= 3; - } while (len > 2); - if (len) { - output[_out++] = output[from++]; - if (len > 1) { - output[_out++] = output[from++]; - } - } - } - } else if ((op & 64) === 0) { - here = dcode[(here & 65535) + (hold & ((1 << op) - 1))]; - continue dodist; - } else { - strm.msg = "invalid distance code"; - state.mode = BAD; - break top; - } - break; - } - } else if ((op & 64) === 0) { - here = lcode[(here & 65535) + (hold & ((1 << op) - 1))]; - continue dolen; - } else if (op & 32) { - state.mode = TYPE; - break top; - } else { - strm.msg = "invalid literal/length code"; - state.mode = BAD; - break top; - } - break; - } - } while (_in < last && _out < end); - len = bits >> 3; - _in -= len; - bits -= len << 3; - hold &= (1 << bits) - 1; - strm.next_in = _in; - strm.next_out = _out; - strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last); - strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end); - state.hold = hold; - state.bits = bits; - return; - }; - }, -}); +// -// node_modules/pako/lib/zlib/inftrees.js -var require_inftrees = __commonJS({ - "node_modules/pako/lib/zlib/inftrees.js"(exports, module2) { - "use strict"; - var utils = require_common(); - var MAXBITS = 15; - var ENOUGH_LENS = 852; - var ENOUGH_DISTS = 592; - var CODES = 0; - var LENS = 1; - var DISTS = 2; - var lbase = [ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, - 0, 0, - ]; - var lext = [ - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, - 16, 72, 78, - ]; - var dbase = [ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0, - ]; - var dext = [ - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, - 29, 29, 64, 64, - ]; - module2.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) { - var bits = opts.bits; - var len = 0; - var sym = 0; - var min = 0, - max = 0; - var root = 0; - var curr = 0; - var drop = 0; - var left = 0; - var used = 0; - var huff = 0; - var incr; - var fill; - var low; - var mask; - var next; - var base: number[] | null = null; - var base_index = 0; - var end; - var count = new utils.Buf16(MAXBITS + 1); - var offs = new utils.Buf16(MAXBITS + 1); - var extra: number[] | null = null; - var extra_index = 0; - var here_bits, here_op, here_val; - for (len = 0; len <= MAXBITS; len++) { - count[len] = 0; - } - for (sym = 0; sym < codes; sym++) { - count[lens[lens_index + sym]]++; - } - root = bits; - for (max = MAXBITS; max >= 1; max--) { - if (count[max] !== 0) { - break; - } - } - if (root > max) { - root = max; - } - if (max === 0) { - table[table_index++] = (1 << 24) | (64 << 16) | 0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - opts.bits = 1; - return 0; - } - for (min = 1; min < max; min++) { - if (count[min] !== 0) { - break; - } - } - if (root < min) { - root = min; - } - left = 1; - for (len = 1; len <= MAXBITS; len++) { - left <<= 1; - left -= count[len]; - if (left < 0) { - return -1; - } - } - if (left > 0 && (type === CODES || max !== 1)) { - return -1; - } - offs[1] = 0; - for (len = 1; len < MAXBITS; len++) { - offs[len + 1] = offs[len] + count[len]; - } - for (sym = 0; sym < codes; sym++) { - if (lens[lens_index + sym] !== 0) { - work[offs[lens[lens_index + sym]]++] = sym; - } - } - if (type === CODES) { - base = extra = work; - end = 19; - } else if (type === LENS) { - base = lbase; - base_index -= 257; - extra = lext; - extra_index -= 257; - end = 256; - } else { - base = dbase; - extra = dext; - end = -1; - } - huff = 0; - sym = 0; - len = min; - next = table_index; - curr = root; - drop = 0; - low = -1; - used = 1 << root; - mask = used - 1; - if ((type === LENS && used > ENOUGH_LENS) || (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - for (;;) { - here_bits = len - drop; - if (work[sym] < end) { - here_op = 0; - here_val = work[sym]; - } else if (work[sym] > end) { - here_op = extra![extra_index + work[sym]]; - here_val = base![base_index + work[sym]]; - } else { - here_op = 32 + 64; - here_val = 0; - } - incr = 1 << (len - drop); - fill = 1 << curr; - min = fill; - do { - fill -= incr; - table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val | 0; - } while (fill !== 0); - incr = 1 << (len - 1); - while (huff & incr) { - incr >>= 1; - } - if (incr !== 0) { - huff &= incr - 1; - huff += incr; - } else { - huff = 0; - } - sym++; - if (--count[len] === 0) { - if (len === max) { - break; - } - len = lens[lens_index + work[sym]]; - } - if (len > root && (huff & mask) !== low) { - if (drop === 0) { - drop = root; - } - next += min; - curr = len - drop; - left = 1 << curr; - while (curr + drop < max) { - left -= count[curr + drop]; - if (left <= 0) { - break; - } - curr++; - left <<= 1; - } - used += 1 << curr; - if ((type === LENS && used > ENOUGH_LENS) || (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - low = huff & mask; - table[low] = (root << 24) | (curr << 16) | (next - table_index) | 0; - } - } - if (huff !== 0) { - table[next + huff] = ((len - drop) << 24) | (64 << 16) | 0; - } - opts.bits = root; - return 0; - }; - }, -}); +function Gunzip(opts) { + if (!(this instanceof Gunzip)) return new Gunzip(opts); + Zlib.$call(this, opts, GUNZIP); +} +Gunzip.prototype = Object.create(Zlib.prototype); -// node_modules/pako/lib/zlib/inflate.js -var require_inflate = __commonJS({ - "node_modules/pako/lib/zlib/inflate.js"(exports) { - "use strict"; - var utils = require_common(); - var adler32 = require_adler32(); - var crc32 = require_crc32(); - var inflate_fast = require_inffast(); - var inflate_table = require_inftrees(); - var CODES = 0; - var LENS = 1; - var DISTS = 2; - var Z_FINISH = 4; - var Z_BLOCK = 5; - var Z_TREES = 6; - var Z_OK = 0; - var Z_STREAM_END = 1; - var Z_NEED_DICT = 2; - var Z_STREAM_ERROR = -2; - var Z_DATA_ERROR = -3; - var Z_MEM_ERROR = -4; - var Z_BUF_ERROR = -5; - var Z_DEFLATED = 8; - var HEAD = 1; - var FLAGS = 2; - var TIME = 3; - var OS = 4; - var EXLEN = 5; - var EXTRA = 6; - var NAME = 7; - var COMMENT = 8; - var HCRC = 9; - var DICTID = 10; - var DICT = 11; - var TYPE = 12; - var TYPEDO = 13; - var STORED = 14; - var COPY_ = 15; - var COPY = 16; - var TABLE = 17; - var LENLENS = 18; - var CODELENS = 19; - var LEN_ = 20; - var LEN = 21; - var LENEXT = 22; - var DIST = 23; - var DISTEXT = 24; - var MATCH = 25; - var LIT = 26; - var CHECK = 27; - var LENGTH = 28; - var DONE = 29; - var BAD = 30; - var MEM = 31; - var SYNC = 32; - var ENOUGH_LENS = 852; - var ENOUGH_DISTS = 592; - var MAX_WBITS = 15; - var DEF_WBITS = MAX_WBITS; - function zswap32(q) { - return ((q >>> 24) & 255) + ((q >>> 8) & 65280) + ((q & 65280) << 8) + ((q & 255) << 24); - } - function InflateState() { - this.mode = 0; - this.last = false; - this.wrap = 0; - this.havedict = false; - this.flags = 0; - this.dmax = 0; - this.check = 0; - this.total = 0; - this.head = null; - this.wbits = 0; - this.wsize = 0; - this.whave = 0; - this.wnext = 0; - this.window = null; - this.hold = 0; - this.bits = 0; - this.length = 0; - this.offset = 0; - this.extra = 0; - this.lencode = null; - this.distcode = null; - this.lenbits = 0; - this.distbits = 0; - this.ncode = 0; - this.nlen = 0; - this.ndist = 0; - this.have = 0; - this.next = null; - this.lens = new utils.Buf16(320); - this.work = new utils.Buf16(288); - this.lendyn = null; - this.distdyn = null; - this.sane = 0; - this.back = 0; - this.was = 0; - } - function inflateResetKeep(strm) { - var state; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - state = strm.state; - strm.total_in = strm.total_out = state.total = 0; - strm.msg = ""; - if (state.wrap) { - strm.adler = state.wrap & 1; - } - state.mode = HEAD; - state.last = 0; - state.havedict = 0; - state.dmax = 32768; - state.head = null; - state.hold = 0; - state.bits = 0; - state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); - state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); - state.sane = 1; - state.back = -1; - return Z_OK; - } - function inflateReset(strm) { - var state; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - state = strm.state; - state.wsize = 0; - state.whave = 0; - state.wnext = 0; - return inflateResetKeep(strm); - } - function inflateReset2(strm, windowBits) { - var wrap; - var state; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - state = strm.state; - if (windowBits < 0) { - wrap = 0; - windowBits = -windowBits; - } else { - wrap = (windowBits >> 4) + 1; - if (windowBits < 48) { - windowBits &= 15; - } - } - if (windowBits && (windowBits < 8 || windowBits > 15)) { - return Z_STREAM_ERROR; - } - if (state.window !== null && state.wbits !== windowBits) { - state.window = null; - } - state.wrap = wrap; - state.wbits = windowBits; - return inflateReset(strm); - } - function inflateInit2(strm, windowBits) { - var ret; - var state; - if (!strm) { - return Z_STREAM_ERROR; - } - state = new InflateState(); - strm.state = state; - state.window = null; - ret = inflateReset2(strm, windowBits); - if (ret !== Z_OK) { - strm.state = null; - } - return ret; - } - function inflateInit(strm) { - return inflateInit2(strm, DEF_WBITS); - } - var virgin = true; - var lenfix; - var distfix; - function fixedtables(state) { - if (virgin) { - var sym; - lenfix = new utils.Buf32(512); - distfix = new utils.Buf32(32); - sym = 0; - while (sym < 144) { - state.lens[sym++] = 8; - } - while (sym < 256) { - state.lens[sym++] = 9; - } - while (sym < 280) { - state.lens[sym++] = 7; - } - while (sym < 288) { - state.lens[sym++] = 8; - } - inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { - bits: 9, - }); - sym = 0; - while (sym < 32) { - state.lens[sym++] = 5; - } - inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { - bits: 5, - }); - virgin = false; - } - state.lencode = lenfix; - state.lenbits = 9; - state.distcode = distfix; - state.distbits = 5; - } - function updatewindow(strm, src, end, copy) { - var dist; - var state = strm.state; - if (state.window === null) { - state.wsize = 1 << state.wbits; - state.wnext = 0; - state.whave = 0; - state.window = new utils.Buf8(state.wsize); - } - if (copy >= state.wsize) { - utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0); - state.wnext = 0; - state.whave = state.wsize; - } else { - dist = state.wsize - state.wnext; - if (dist > copy) { - dist = copy; - } - utils.arraySet(state.window, src, end - copy, dist, state.wnext); - copy -= dist; - if (copy) { - utils.arraySet(state.window, src, end - copy, copy, 0); - state.wnext = copy; - state.whave = state.wsize; - } else { - state.wnext += dist; - if (state.wnext === state.wsize) { - state.wnext = 0; - } - if (state.whave < state.wsize) { - state.whave += dist; - } - } - } - return 0; - } - function inflate(strm, flush) { - var state; - var input, output; - var next; - var put; - var have, left; - var hold; - var bits; - var _in, _out; - var copy; - var from; - var from_source; - var here = 0; - var here_bits, here_op, here_val; - var last_bits, last_op, last_val; - var len; - var ret; - var hbuf = new utils.Buf8(4); - var opts; - var n; - var order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - if (!strm || !strm.state || !strm.output || (!strm.input && strm.avail_in !== 0)) { - return Z_STREAM_ERROR; - } - state = strm.state; - if (state.mode === TYPE) { - state.mode = TYPEDO; - } - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - _in = have; - _out = left; - ret = Z_OK; - inf_leave: for (;;) { - switch (state.mode) { - case HEAD: - if (state.wrap === 0) { - state.mode = TYPEDO; - break; - } - while (bits < 16) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (state.wrap & 2 && hold === 35615) { - state.check = 0; - hbuf[0] = hold & 255; - hbuf[1] = (hold >>> 8) & 255; - state.check = crc32(state.check, hbuf, 2, 0); - hold = 0; - bits = 0; - state.mode = FLAGS; - break; - } - state.flags = 0; - if (state.head) { - state.head.done = false; - } - if (!(state.wrap & 1) || (((hold & 255) << 8) + (hold >> 8)) % 31) { - strm.msg = "incorrect header check"; - state.mode = BAD; - break; - } - if ((hold & 15) !== Z_DEFLATED) { - strm.msg = "unknown compression method"; - state.mode = BAD; - break; - } - hold >>>= 4; - bits -= 4; - len = (hold & 15) + 8; - if (state.wbits === 0) { - state.wbits = len; - } else if (len > state.wbits) { - strm.msg = "invalid window size"; - state.mode = BAD; - break; - } - state.dmax = 1 << len; - strm.adler = state.check = 1; - state.mode = hold & 512 ? DICTID : TYPE; - hold = 0; - bits = 0; - break; - case FLAGS: - while (bits < 16) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.flags = hold; - if ((state.flags & 255) !== Z_DEFLATED) { - strm.msg = "unknown compression method"; - state.mode = BAD; - break; - } - if (state.flags & 57344) { - strm.msg = "unknown header flags set"; - state.mode = BAD; - break; - } - if (state.head) { - state.head.text = (hold >> 8) & 1; - } - if (state.flags & 512) { - hbuf[0] = hold & 255; - hbuf[1] = (hold >>> 8) & 255; - state.check = crc32(state.check, hbuf, 2, 0); - } - hold = 0; - bits = 0; - state.mode = TIME; - case TIME: - while (bits < 32) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (state.head) { - state.head.time = hold; - } - if (state.flags & 512) { - hbuf[0] = hold & 255; - hbuf[1] = (hold >>> 8) & 255; - hbuf[2] = (hold >>> 16) & 255; - hbuf[3] = (hold >>> 24) & 255; - state.check = crc32(state.check, hbuf, 4, 0); - } - hold = 0; - bits = 0; - state.mode = OS; - case OS: - while (bits < 16) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (state.head) { - state.head.xflags = hold & 255; - state.head.os = hold >> 8; - } - if (state.flags & 512) { - hbuf[0] = hold & 255; - hbuf[1] = (hold >>> 8) & 255; - state.check = crc32(state.check, hbuf, 2, 0); - } - hold = 0; - bits = 0; - state.mode = EXLEN; - case EXLEN: - if (state.flags & 1024) { - while (bits < 16) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.length = hold; - if (state.head) { - state.head.extra_len = hold; - } - if (state.flags & 512) { - hbuf[0] = hold & 255; - hbuf[1] = (hold >>> 8) & 255; - state.check = crc32(state.check, hbuf, 2, 0); - } - hold = 0; - bits = 0; - } else if (state.head) { - state.head.extra = null; - } - state.mode = EXTRA; - case EXTRA: - if (state.flags & 1024) { - copy = state.length; - if (copy > have) { - copy = have; - } - if (copy) { - if (state.head) { - len = state.head.extra_len - state.length; - if (!state.head.extra) { - state.head.extra = new Array(state.head.extra_len); - } - utils.arraySet(state.head.extra, input, next, copy, len); - } - if (state.flags & 512) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - state.length -= copy; - } - if (state.length) { - break inf_leave; - } - } - state.length = 0; - state.mode = NAME; - case NAME: - if (state.flags & 2048) { - if (have === 0) { - break inf_leave; - } - copy = 0; - do { - len = input[next + copy++]; - if (state.head && len && state.length < 65536) { - state.head.name += String.fromCharCode(len); - } - } while (len && copy < have); - if (state.flags & 512) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { - break inf_leave; - } - } else if (state.head) { - state.head.name = null; - } - state.length = 0; - state.mode = COMMENT; - case COMMENT: - if (state.flags & 4096) { - if (have === 0) { - break inf_leave; - } - copy = 0; - do { - len = input[next + copy++]; - if (state.head && len && state.length < 65536) { - state.head.comment += String.fromCharCode(len); - } - } while (len && copy < have); - if (state.flags & 512) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { - break inf_leave; - } - } else if (state.head) { - state.head.comment = null; - } - state.mode = HCRC; - case HCRC: - if (state.flags & 512) { - while (bits < 16) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (hold !== (state.check & 65535)) { - strm.msg = "header crc mismatch"; - state.mode = BAD; - break; - } - hold = 0; - bits = 0; - } - if (state.head) { - state.head.hcrc = (state.flags >> 9) & 1; - state.head.done = true; - } - strm.adler = state.check = 0; - state.mode = TYPE; - break; - case DICTID: - while (bits < 32) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - strm.adler = state.check = zswap32(hold); - hold = 0; - bits = 0; - state.mode = DICT; - case DICT: - if (state.havedict === 0) { - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - return Z_NEED_DICT; - } - strm.adler = state.check = 1; - state.mode = TYPE; - case TYPE: - if (flush === Z_BLOCK || flush === Z_TREES) { - break inf_leave; - } - case TYPEDO: - if (state.last) { - hold >>>= bits & 7; - bits -= bits & 7; - state.mode = CHECK; - break; - } - while (bits < 3) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.last = hold & 1; - hold >>>= 1; - bits -= 1; - switch (hold & 3) { - case 0: - state.mode = STORED; - break; - case 1: - fixedtables(state); - state.mode = LEN_; - if (flush === Z_TREES) { - hold >>>= 2; - bits -= 2; - break inf_leave; - } - break; - case 2: - state.mode = TABLE; - break; - case 3: - strm.msg = "invalid block type"; - state.mode = BAD; - } - hold >>>= 2; - bits -= 2; - break; - case STORED: - hold >>>= bits & 7; - bits -= bits & 7; - while (bits < 32) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if ((hold & 65535) !== ((hold >>> 16) ^ 65535)) { - strm.msg = "invalid stored block lengths"; - state.mode = BAD; - break; - } - state.length = hold & 65535; - hold = 0; - bits = 0; - state.mode = COPY_; - if (flush === Z_TREES) { - break inf_leave; - } - case COPY_: - state.mode = COPY; - case COPY: - copy = state.length; - if (copy) { - if (copy > have) { - copy = have; - } - if (copy > left) { - copy = left; - } - if (copy === 0) { - break inf_leave; - } - utils.arraySet(output, input, next, copy, put); - have -= copy; - next += copy; - left -= copy; - put += copy; - state.length -= copy; - break; - } - state.mode = TYPE; - break; - case TABLE: - while (bits < 14) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.nlen = (hold & 31) + 257; - hold >>>= 5; - bits -= 5; - state.ndist = (hold & 31) + 1; - hold >>>= 5; - bits -= 5; - state.ncode = (hold & 15) + 4; - hold >>>= 4; - bits -= 4; - if (state.nlen > 286 || state.ndist > 30) { - strm.msg = "too many length or distance symbols"; - state.mode = BAD; - break; - } - state.have = 0; - state.mode = LENLENS; - case LENLENS: - while (state.have < state.ncode) { - while (bits < 3) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.lens[order[state.have++]] = hold & 7; - hold >>>= 3; - bits -= 3; - } - while (state.have < 19) { - state.lens[order[state.have++]] = 0; - } - state.lencode = state.lendyn; - state.lenbits = 7; - opts = { bits: state.lenbits }; - ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); - state.lenbits = opts.bits; - if (ret) { - strm.msg = "invalid code lengths set"; - state.mode = BAD; - break; - } - state.have = 0; - state.mode = CODELENS; - case CODELENS: - while (state.have < state.nlen + state.ndist) { - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 255; - here_val = here & 65535; - if (here_bits <= bits) { - break; - } - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (here_val < 16) { - hold >>>= here_bits; - bits -= here_bits; - state.lens[state.have++] = here_val; - } else { - if (here_val === 16) { - n = here_bits + 2; - while (bits < n) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - hold >>>= here_bits; - bits -= here_bits; - if (state.have === 0) { - strm.msg = "invalid bit length repeat"; - state.mode = BAD; - break; - } - len = state.lens[state.have - 1]; - copy = 3 + (hold & 3); - hold >>>= 2; - bits -= 2; - } else if (here_val === 17) { - n = here_bits + 3; - while (bits < n) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - hold >>>= here_bits; - bits -= here_bits; - len = 0; - copy = 3 + (hold & 7); - hold >>>= 3; - bits -= 3; - } else { - n = here_bits + 7; - while (bits < n) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - hold >>>= here_bits; - bits -= here_bits; - len = 0; - copy = 11 + (hold & 127); - hold >>>= 7; - bits -= 7; - } - if (state.have + copy > state.nlen + state.ndist) { - strm.msg = "invalid bit length repeat"; - state.mode = BAD; - break; - } - while (copy--) { - state.lens[state.have++] = len; - } - } - } - if (state.mode === BAD) { - break; - } - if (state.lens[256] === 0) { - strm.msg = "invalid code -- missing end-of-block"; - state.mode = BAD; - break; - } - state.lenbits = 9; - opts = { bits: state.lenbits }; - ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); - state.lenbits = opts.bits; - if (ret) { - strm.msg = "invalid literal/lengths set"; - state.mode = BAD; - break; - } - state.distbits = 6; - state.distcode = state.distdyn; - opts = { bits: state.distbits }; - ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); - state.distbits = opts.bits; - if (ret) { - strm.msg = "invalid distances set"; - state.mode = BAD; - break; - } - state.mode = LEN_; - if (flush === Z_TREES) { - break inf_leave; - } - case LEN_: - state.mode = LEN; - case LEN: - if (have >= 6 && left >= 258) { - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - inflate_fast(strm, _out); - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - if (state.mode === TYPE) { - state.back = -1; - } - break; - } - state.back = 0; - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 255; - here_val = here & 65535; - if (here_bits <= bits) { - break; - } - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (here_op && (here_op & 240) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.lencode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1)) >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 255; - here_val = here & 65535; - if (last_bits + here_bits <= bits) { - break; - } - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - hold >>>= last_bits; - bits -= last_bits; - state.back += last_bits; - } - hold >>>= here_bits; - bits -= here_bits; - state.back += here_bits; - state.length = here_val; - if (here_op === 0) { - state.mode = LIT; - break; - } - if (here_op & 32) { - state.back = -1; - state.mode = TYPE; - break; - } - if (here_op & 64) { - strm.msg = "invalid literal/length code"; - state.mode = BAD; - break; - } - state.extra = here_op & 15; - state.mode = LENEXT; - case LENEXT: - if (state.extra) { - n = state.extra; - while (bits < n) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.length += hold & ((1 << state.extra) - 1); - hold >>>= state.extra; - bits -= state.extra; - state.back += state.extra; - } - state.was = state.length; - state.mode = DIST; - case DIST: - for (;;) { - here = state.distcode[hold & ((1 << state.distbits) - 1)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 255; - here_val = here & 65535; - if (here_bits <= bits) { - break; - } - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if ((here_op & 240) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.distcode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1)) >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 255; - here_val = here & 65535; - if (last_bits + here_bits <= bits) { - break; - } - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - hold >>>= last_bits; - bits -= last_bits; - state.back += last_bits; - } - hold >>>= here_bits; - bits -= here_bits; - state.back += here_bits; - if (here_op & 64) { - strm.msg = "invalid distance code"; - state.mode = BAD; - break; - } - state.offset = here_val; - state.extra = here_op & 15; - state.mode = DISTEXT; - case DISTEXT: - if (state.extra) { - n = state.extra; - while (bits < n) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - state.offset += hold & ((1 << state.extra) - 1); - hold >>>= state.extra; - bits -= state.extra; - state.back += state.extra; - } - if (state.offset > state.dmax) { - strm.msg = "invalid distance too far back"; - state.mode = BAD; - break; - } - state.mode = MATCH; - case MATCH: - if (left === 0) { - break inf_leave; - } - copy = _out - left; - if (state.offset > copy) { - copy = state.offset - copy; - if (copy > state.whave) { - if (state.sane) { - strm.msg = "invalid distance too far back"; - state.mode = BAD; - break; - } - } - if (copy > state.wnext) { - copy -= state.wnext; - from = state.wsize - copy; - } else { - from = state.wnext - copy; - } - if (copy > state.length) { - copy = state.length; - } - from_source = state.window; - } else { - from_source = output; - from = put - state.offset; - copy = state.length; - } - if (copy > left) { - copy = left; - } - left -= copy; - state.length -= copy; - do { - output[put++] = from_source[from++]; - } while (--copy); - if (state.length === 0) { - state.mode = LEN; - } - break; - case LIT: - if (left === 0) { - break inf_leave; - } - output[put++] = state.length; - left--; - state.mode = LEN; - break; - case CHECK: - if (state.wrap) { - while (bits < 32) { - if (have === 0) { - break inf_leave; - } - have--; - hold |= input[next++] << bits; - bits += 8; - } - _out -= left; - strm.total_out += _out; - state.total += _out; - if (_out) { - strm.adler = state.check = state.flags - ? crc32(state.check, output, _out, put - _out) - : adler32(state.check, output, _out, put - _out); - } - _out = left; - if ((state.flags ? hold : zswap32(hold)) !== state.check) { - strm.msg = "incorrect data check"; - state.mode = BAD; - break; - } - hold = 0; - bits = 0; - } - state.mode = LENGTH; - case LENGTH: - if (state.wrap && state.flags) { - while (bits < 32) { - if (have === 0) { - break inf_leave; - } - have--; - hold += input[next++] << bits; - bits += 8; - } - if (hold !== (state.total & 4294967295)) { - strm.msg = "incorrect length check"; - state.mode = BAD; - break; - } - hold = 0; - bits = 0; - } - state.mode = DONE; - case DONE: - ret = Z_STREAM_END; - break inf_leave; - case BAD: - ret = Z_DATA_ERROR; - break inf_leave; - case MEM: - return Z_MEM_ERROR; - case SYNC: - default: - return Z_STREAM_ERROR; - } - } - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH))) { - if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { - state.mode = MEM; - return Z_MEM_ERROR; - } - } - _in -= strm.avail_in; - _out -= strm.avail_out; - strm.total_in += _in; - strm.total_out += _out; - state.total += _out; - if (state.wrap && _out) { - strm.adler = state.check = state.flags - ? crc32(state.check, output, _out, strm.next_out - _out) - : adler32(state.check, output, _out, strm.next_out - _out); - } - strm.data_type = - state.bits + - (state.last ? 64 : 0) + - (state.mode === TYPE ? 128 : 0) + - (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); - if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { - ret = Z_BUF_ERROR; - } - return ret; - } - function inflateEnd(strm) { - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - var state = strm.state; - if (state.window) { - state.window = null; - } - strm.state = null; - return Z_OK; - } - function inflateGetHeader(strm, head) { - var state; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - state = strm.state; - if ((state.wrap & 2) === 0) { - return Z_STREAM_ERROR; - } - state.head = head; - head.done = false; - return Z_OK; - } - function inflateSetDictionary(strm, dictionary) { - var dictLength = dictionary.length; - var state; - var dictid; - var ret; - if (!strm || !strm.state) { - return Z_STREAM_ERROR; - } - state = strm.state; - if (state.wrap !== 0 && state.mode !== DICT) { - return Z_STREAM_ERROR; - } - if (state.mode === DICT) { - dictid = 1; - dictid = adler32(dictid, dictionary, dictLength, 0); - if (dictid !== state.check) { - return Z_DATA_ERROR; - } - } - ret = updatewindow(strm, dictionary, dictLength, dictLength); - if (ret) { - state.mode = MEM; - return Z_MEM_ERROR; - } - state.havedict = 1; - return Z_OK; - } - exports.inflateReset = inflateReset; - exports.inflateReset2 = inflateReset2; - exports.inflateResetKeep = inflateResetKeep; - exports.inflateInit = inflateInit; - exports.inflateInit2 = inflateInit2; - exports.inflate = inflate; - exports.inflateEnd = inflateEnd; - exports.inflateGetHeader = inflateGetHeader; - exports.inflateSetDictionary = inflateSetDictionary; - exports.inflateInfo = "pako inflate (from Nodeca project)"; - }, -}); +// -// node_modules/pako/lib/zlib/constants.js -var require_constants = __commonJS({ - "node_modules/pako/lib/zlib/constants.js"(exports, module2) { - "use strict"; - module2.exports = { - Z_NO_FLUSH: 0, - Z_PARTIAL_FLUSH: 1, - Z_SYNC_FLUSH: 2, - Z_FULL_FLUSH: 3, - Z_FINISH: 4, - Z_BLOCK: 5, - Z_TREES: 6, - Z_OK: 0, - Z_STREAM_END: 1, - Z_NEED_DICT: 2, - Z_ERRNO: -1, - Z_STREAM_ERROR: -2, - Z_DATA_ERROR: -3, - Z_MEM_ERROR: -4, - Z_BUF_ERROR: -5, - Z_VERSION_ERROR: -6, - Z_NO_COMPRESSION: 0, - Z_BEST_SPEED: 1, - Z_BEST_COMPRESSION: 9, - Z_DEFAULT_COMPRESSION: -1, - Z_FILTERED: 1, - Z_HUFFMAN_ONLY: 2, - Z_RLE: 3, - Z_FIXED: 4, - Z_DEFAULT_STRATEGY: 0, - Z_BINARY: 0, - Z_TEXT: 1, - Z_ASCII: 1, - Z_UNKNOWN: 2, - Z_DEFLATED: 8, - DEFLATE: 1, - INFLATE: 2, - GZIP: 3, - GUNZIP: 4, - DEFLATERAW: 5, - INFLATERAW: 6, - UNZIP: 7, - BROTLI_DECODE: 8, - BROTLI_ENCODE: 9, - Z_MIN_WINDOWBITS: 8, - Z_MAX_WINDOWBITS: 15, - Z_DEFAULT_WINDOWBITS: 15, - Z_MIN_CHUNK: 64, - Z_MAX_CHUNK: Infinity, - Z_DEFAULT_CHUNK: 16384, - Z_MIN_MEMLEVEL: 1, - Z_MAX_MEMLEVEL: 9, - Z_DEFAULT_MEMLEVEL: 8, - Z_MIN_LEVEL: -1, - Z_MAX_LEVEL: 9, - Z_DEFAULT_LEVEL: -1, - BROTLI_OPERATION_PROCESS: 0, - BROTLI_OPERATION_FLUSH: 1, - BROTLI_OPERATION_FINISH: 2, - BROTLI_OPERATION_EMIT_METADATA: 3, - BROTLI_PARAM_MODE: 0, - BROTLI_MODE_GENERIC: 0, - BROTLI_MODE_TEXT: 1, - BROTLI_MODE_FONT: 2, - BROTLI_DEFAULT_MODE: 0, - BROTLI_PARAM_QUALITY: 1, - BROTLI_MIN_QUALITY: 0, - BROTLI_MAX_QUALITY: 11, - BROTLI_DEFAULT_QUALITY: 11, - BROTLI_PARAM_LGWIN: 2, - BROTLI_MIN_WINDOW_BITS: 10, - BROTLI_MAX_WINDOW_BITS: 24, - BROTLI_LARGE_MAX_WINDOW_BITS: 30, - BROTLI_DEFAULT_WINDOW: 22, - BROTLI_PARAM_LGBLOCK: 3, - BROTLI_MIN_INPUT_BLOCK_BITS: 16, - BROTLI_MAX_INPUT_BLOCK_BITS: 24, - BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: 4, - BROTLI_PARAM_SIZE_HINT: 5, - BROTLI_PARAM_LARGE_WINDOW: 6, - BROTLI_PARAM_NPOSTFIX: 7, - BROTLI_PARAM_NDIRECT: 8, - BROTLI_DECODER_RESULT_ERROR: 0, - BROTLI_DECODER_RESULT_SUCCESS: 1, - BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: 2, - BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: 3, - BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: 0, - BROTLI_DECODER_PARAM_LARGE_WINDOW: 1, - BROTLI_DECODER_NO_ERROR: 0, - BROTLI_DECODER_SUCCESS: 1, - BROTLI_DECODER_NEEDS_MORE_INPUT: 2, - BROTLI_DECODER_NEEDS_MORE_OUTPUT: 3, - BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: -1, - BROTLI_DECODER_ERROR_FORMAT_RESERVED: -2, - BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: -3, - BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: -4, - BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: -5, - BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: -6, - BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: -7, - BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: -8, - BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: -9, - BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: -10, - BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: -11, - BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: -12, - BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: -13, - BROTLI_DECODER_ERROR_FORMAT_PADDING_1: -14, - BROTLI_DECODER_ERROR_FORMAT_PADDING_2: -15, - BROTLI_DECODER_ERROR_FORMAT_DISTANCE: -16, - BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: -19, - BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: -20, - BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: -21, - BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: -22, - BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: -25, - BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: -26, - BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: -27, - BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: -30, - BROTLI_DECODER_ERROR_UNREACHABLE: -31, - }; - }, -}); +function Unzip(opts) { + if (!(this instanceof Unzip)) return new Unzip(opts); + Zlib.$call(this, opts, UNZIP); +} +Unzip.prototype = Object.create(Zlib.prototype); + +// + +const constants = { + Z_NO_FLUSH: 0, + Z_PARTIAL_FLUSH: 1, + Z_SYNC_FLUSH: 2, + Z_FULL_FLUSH: 3, + Z_FINISH: 4, + Z_BLOCK: 5, + Z_TREES: 6, + Z_OK: 0, + Z_STREAM_END: 1, + Z_NEED_DICT: 2, + Z_ERRNO: -1, + Z_STREAM_ERROR: -2, + Z_DATA_ERROR: -3, + Z_MEM_ERROR: -4, + Z_BUF_ERROR: -5, + Z_VERSION_ERROR: -6, + Z_NO_COMPRESSION: 0, + Z_BEST_SPEED: 1, + Z_BEST_COMPRESSION: 9, + Z_DEFAULT_COMPRESSION: -1, + Z_FILTERED: 1, + Z_HUFFMAN_ONLY: 2, + Z_RLE: 3, + Z_FIXED: 4, + Z_DEFAULT_STRATEGY: 0, + Z_BINARY: 0, + Z_TEXT: 1, + Z_ASCII: 1, + Z_UNKNOWN: 2, + Z_DEFLATED: 8, + DEFLATE: 1, + INFLATE: 2, + GZIP: 3, + GUNZIP: 4, + DEFLATERAW: 5, + INFLATERAW: 6, + UNZIP: 7, + BROTLI_DECODE: 8, + BROTLI_ENCODE: 9, + Z_MIN_WINDOWBITS: 8, + Z_MAX_WINDOWBITS: 15, + Z_DEFAULT_WINDOWBITS: 15, + Z_MIN_CHUNK: 64, + Z_MAX_CHUNK: Infinity, + Z_DEFAULT_CHUNK: 16384, + Z_MIN_MEMLEVEL: 1, + Z_MAX_MEMLEVEL: 9, + Z_DEFAULT_MEMLEVEL: 8, + Z_MIN_LEVEL: -1, + Z_MAX_LEVEL: 9, + Z_DEFAULT_LEVEL: -1, + BROTLI_OPERATION_PROCESS: 0, + BROTLI_OPERATION_FLUSH: 1, + BROTLI_OPERATION_FINISH: 2, + BROTLI_OPERATION_EMIT_METADATA: 3, + BROTLI_PARAM_MODE: 0, + BROTLI_MODE_GENERIC: 0, + BROTLI_MODE_TEXT: 1, + BROTLI_MODE_FONT: 2, + BROTLI_DEFAULT_MODE: 0, + BROTLI_PARAM_QUALITY: 1, + BROTLI_MIN_QUALITY: 0, + BROTLI_MAX_QUALITY: 11, + BROTLI_DEFAULT_QUALITY: 11, + BROTLI_PARAM_LGWIN: 2, + BROTLI_MIN_WINDOW_BITS: 10, + BROTLI_MAX_WINDOW_BITS: 24, + BROTLI_LARGE_MAX_WINDOW_BITS: 30, + BROTLI_DEFAULT_WINDOW: 22, + BROTLI_PARAM_LGBLOCK: 3, + BROTLI_MIN_INPUT_BLOCK_BITS: 16, + BROTLI_MAX_INPUT_BLOCK_BITS: 24, + BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: 4, + BROTLI_PARAM_SIZE_HINT: 5, + BROTLI_PARAM_LARGE_WINDOW: 6, + BROTLI_PARAM_NPOSTFIX: 7, + BROTLI_PARAM_NDIRECT: 8, + BROTLI_DECODER_RESULT_ERROR: 0, + BROTLI_DECODER_RESULT_SUCCESS: 1, + BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: 2, + BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: 3, + BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: 0, + BROTLI_DECODER_PARAM_LARGE_WINDOW: 1, + BROTLI_DECODER_NO_ERROR: 0, + BROTLI_DECODER_SUCCESS: 1, + BROTLI_DECODER_NEEDS_MORE_INPUT: 2, + BROTLI_DECODER_NEEDS_MORE_OUTPUT: 3, + BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: -1, + BROTLI_DECODER_ERROR_FORMAT_RESERVED: -2, + BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: -3, + BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: -4, + BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: -5, + BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: -6, + BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: -7, + BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: -8, + BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: -9, + BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: -10, + BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: -11, + BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: -12, + BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: -13, + BROTLI_DECODER_ERROR_FORMAT_PADDING_1: -14, + BROTLI_DECODER_ERROR_FORMAT_PADDING_2: -15, + BROTLI_DECODER_ERROR_FORMAT_DISTANCE: -16, + BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: -19, + BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: -20, + BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: -21, + BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: -22, + BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: -25, + BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: -26, + BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: -27, + BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: -30, + BROTLI_DECODER_ERROR_UNREACHABLE: -31, +}; +const { DEFLATE, INFLATE, GZIP, GUNZIP, DEFLATERAW, INFLATERAW, UNZIP, BROTLI_DECODE, BROTLI_ENCODE } = constants; + +// Translation table for return codes. +const codes = { + Z_OK: constants.Z_OK, + Z_STREAM_END: constants.Z_STREAM_END, + Z_NEED_DICT: constants.Z_NEED_DICT, + Z_ERRNO: constants.Z_ERRNO, + Z_STREAM_ERROR: constants.Z_STREAM_ERROR, + Z_DATA_ERROR: constants.Z_DATA_ERROR, + Z_MEM_ERROR: constants.Z_MEM_ERROR, + Z_BUF_ERROR: constants.Z_BUF_ERROR, + Z_VERSION_ERROR: constants.Z_VERSION_ERROR, +}; -// node_modules/browserify-zlib/lib/binding.js -var require_binding = __commonJS({ - "node_modules/browserify-zlib/lib/binding.js"(exports) { - "use strict"; +for (const ckey of Object.keys(codes)) { + codes[codes[ckey]] = ckey; +} - var Zstream = require_zstream(); - var zlib_deflate = require_deflate(); - var zlib_inflate = require_inflate(); - var constants = require_constants(); - for (key in constants) { - exports[key] = constants[key]; - } - var key; - exports.NONE = 0; - exports.DEFLATE = 1; - exports.INFLATE = 2; - exports.GZIP = 3; - exports.GUNZIP = 4; - exports.DEFLATERAW = 5; - exports.INFLATERAW = 6; - exports.UNZIP = 7; - var GZIP_HEADER_ID1 = 31; - var GZIP_HEADER_ID2 = 139; - function Zlib(mode) { - if (typeof mode !== "number" || mode < exports.DEFLATE || mode > exports.UNZIP) { - throw new TypeError("Bad argument"); - } - this.dictionary = null; - this.err = 0; - this.flush = 0; - this.init_done = false; - this.level = 0; - this.memLevel = 0; - this.mode = mode; - this.strategy = 0; - this.windowBits = 0; - this.write_in_progress = false; - this.pending_close = false; - this.gzip_id_bytes_read = 0; - } - Zlib.prototype = {}; - Zlib.prototype.close = function () { - if (this.write_in_progress) { - this.pending_close = true; - return; - } - this.pending_close = false; - assert(this.init_done, "close before init"); - assert(this.mode <= exports.UNZIP); - if (this.mode === exports.DEFLATE || this.mode === exports.GZIP || this.mode === exports.DEFLATERAW) { - zlib_deflate.deflateEnd(this.strm); - } else if ( - this.mode === exports.INFLATE || - this.mode === exports.GUNZIP || - this.mode === exports.INFLATERAW || - this.mode === exports.UNZIP - ) { - zlib_inflate.inflateEnd(this.strm); - } - this.mode = exports.NONE; - this.dictionary = null; - }; - Zlib.prototype.write = function (flush, input, in_off, in_len, out, out_off, out_len) { - return this._write(true, flush, input, in_off, in_len, out, out_off, out_len); - }; - Zlib.prototype.writeSync = function (flush, input, in_off, in_len, out, out_off, out_len) { - return this._write(false, flush, input, in_off, in_len, out, out_off, out_len); - }; - Zlib.prototype._write = function (async, flush, input, in_off, in_len, out, out_off, out_len) { - assert.equal(arguments.length, 8); - assert(this.init_done, "write before init"); - assert(this.mode !== exports.NONE, "already finalized"); - assert.equal(false, this.write_in_progress, "write already in progress"); - assert.equal(false, this.pending_close, "close is pending"); - this.write_in_progress = true; - assert.equal(false, flush === void 0, "must provide flush value"); - this.write_in_progress = true; - if ( - flush !== exports.Z_NO_FLUSH && - flush !== exports.Z_PARTIAL_FLUSH && - flush !== exports.Z_SYNC_FLUSH && - flush !== exports.Z_FULL_FLUSH && - flush !== exports.Z_FINISH && - flush !== exports.Z_BLOCK - ) { - throw new Error("Invalid flush value"); - } - if (input == null) { - input = Buffer.alloc(0); - in_len = 0; - in_off = 0; - } - this.strm.avail_in = in_len; - this.strm.input = input; - this.strm.next_in = in_off; - this.strm.avail_out = out_len; - this.strm.output = out; - this.strm.next_out = out_off; - this.flush = flush; - if (!async) { - this._process(); - if (this._checkError()) { - return this._afterSync(); - } - return; - } - var self = this; - process.nextTick(function () { - self._process(); - self._after(); - }); - return this; - }; - Zlib.prototype._afterSync = function () { - var avail_out = this.strm.avail_out; - var avail_in = this.strm.avail_in; - this.write_in_progress = false; - return [avail_in, avail_out]; - }; - Zlib.prototype._process = function () { - var next_expected_header_byte = null; - switch (this.mode) { - case exports.DEFLATE: - case exports.GZIP: - case exports.DEFLATERAW: - this.err = zlib_deflate.deflate(this.strm, this.flush); - break; - case exports.UNZIP: - if (this.strm.avail_in > 0) { - next_expected_header_byte = this.strm.next_in; - } - switch (this.gzip_id_bytes_read) { - case 0: - if (next_expected_header_byte === null) { - break; - } - if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID1) { - this.gzip_id_bytes_read = 1; - next_expected_header_byte++; - if (this.strm.avail_in === 1) { - break; - } - } else { - this.mode = exports.INFLATE; - break; - } - case 1: - if (next_expected_header_byte === null) { - break; - } - if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID2) { - this.gzip_id_bytes_read = 2; - this.mode = exports.GUNZIP; - } else { - this.mode = exports.INFLATE; - } - break; - default: - throw new Error("invalid number of gzip magic number bytes read"); - } - case exports.INFLATE: - case exports.GUNZIP: - case exports.INFLATERAW: - this.err = zlib_inflate.inflate(this.strm, this.flush); - if (this.err === exports.Z_NEED_DICT && this.dictionary) { - this.err = zlib_inflate.inflateSetDictionary(this.strm, this.dictionary); - if (this.err === exports.Z_OK) { - this.err = zlib_inflate.inflate(this.strm, this.flush); - } else if (this.err === exports.Z_DATA_ERROR) { - this.err = exports.Z_NEED_DICT; - } - } - while ( - this.strm.avail_in > 0 && - this.mode === exports.GUNZIP && - this.err === exports.Z_STREAM_END && - this.strm.next_in[0] !== 0 - ) { - this.reset(); - this.err = zlib_inflate.inflate(this.strm, this.flush); - } - break; - default: - throw new Error("Unknown mode " + this.mode); - } - }; - Zlib.prototype._checkError = function () { - switch (this.err) { - case exports.Z_OK: - case exports.Z_BUF_ERROR: - if (this.strm.avail_out !== 0 && this.flush === exports.Z_FINISH) { - this._error("unexpected end of file"); - return false; - } - break; - case exports.Z_STREAM_END: - break; - case exports.Z_NEED_DICT: - if (this.dictionary == null) { - this._error("Missing dictionary"); - } else { - this._error("Bad dictionary"); - } - return false; - default: - this._error("Zlib error"); - return false; - } - return true; - }; - Zlib.prototype._after = function () { - if (!this._checkError()) { - return; - } - var avail_out = this.strm.avail_out; - var avail_in = this.strm.avail_in; - this.write_in_progress = false; - this.callback(avail_in, avail_out); - if (this.pending_close) { - this.close(); - } - }; - Zlib.prototype._error = function (message) { - if (this.strm.msg) { - message = this.strm.msg; - } - this.onerror(message, this.err); - this.write_in_progress = false; - if (this.pending_close) { - this.close(); - } - }; - Zlib.prototype.init = function (windowBits, level, memLevel, strategy, dictionary) { - assert( - arguments.length === 4 || arguments.length === 5, - "init(windowBits, level, memLevel, strategy, [dictionary])", - ); - assert(windowBits >= 8 && windowBits <= 15, "invalid windowBits"); - assert(level >= -1 && level <= 9, "invalid compression level"); - assert(memLevel >= 1 && memLevel <= 9, "invalid memlevel"); - assert( - strategy === exports.Z_FILTERED || - strategy === exports.Z_HUFFMAN_ONLY || - strategy === exports.Z_RLE || - strategy === exports.Z_FIXED || - strategy === exports.Z_DEFAULT_STRATEGY, - "invalid strategy", - ); - this._init(level, windowBits, memLevel, strategy, dictionary); - this._setDictionary(); - }; - Zlib.prototype.params = function () { - throw new Error("deflateParams Not supported"); - }; - Zlib.prototype.reset = function () { - this._reset(); - this._setDictionary(); - }; - Zlib.prototype._init = function (level, windowBits, memLevel, strategy, dictionary) { - this.level = level; - this.windowBits = windowBits; - this.memLevel = memLevel; - this.strategy = strategy; - this.flush = exports.Z_NO_FLUSH; - this.err = exports.Z_OK; - if (this.mode === exports.GZIP || this.mode === exports.GUNZIP) { - this.windowBits += 16; - } - if (this.mode === exports.UNZIP) { - this.windowBits += 32; - } - if (this.mode === exports.DEFLATERAW || this.mode === exports.INFLATERAW) { - this.windowBits = -1 * this.windowBits; - } - this.strm = new Zstream(); - switch (this.mode) { - case exports.DEFLATE: - case exports.GZIP: - case exports.DEFLATERAW: - this.err = zlib_deflate.deflateInit2( - this.strm, - this.level, - exports.Z_DEFLATED, - this.windowBits, - this.memLevel, - this.strategy, - ); - break; - case exports.INFLATE: - case exports.GUNZIP: - case exports.INFLATERAW: - case exports.UNZIP: - this.err = zlib_inflate.inflateInit2(this.strm, this.windowBits); - break; - default: - throw new Error("Unknown mode " + this.mode); - } - if (this.err !== exports.Z_OK) { - this._error("Init error"); - } - this.dictionary = dictionary; - this.write_in_progress = false; - this.init_done = true; - }; - Zlib.prototype._setDictionary = function () { - if (this.dictionary == null) { - return; - } - this.err = exports.Z_OK; - switch (this.mode) { - case exports.DEFLATE: - case exports.DEFLATERAW: - this.err = zlib_deflate.deflateSetDictionary(this.strm, this.dictionary); - break; - default: - break; - } - if (this.err !== exports.Z_OK) { - this._error("Failed to set dictionary"); - } - }; - Zlib.prototype._reset = function () { - this.err = exports.Z_OK; - switch (this.mode) { - case exports.DEFLATE: - case exports.DEFLATERAW: - case exports.GZIP: - this.err = zlib_deflate.deflateReset(this.strm); - break; - case exports.INFLATE: - case exports.INFLATERAW: - case exports.GUNZIP: - this.err = zlib_inflate.inflateReset(this.strm); - break; - default: - break; - } - if (this.err !== exports.Z_OK) { - this._error("Failed to reset stream"); - } - }; - exports.Zlib = Zlib; - }, -}); +const methods = [ + [], + [Deflate, true, createZlibEncoder], + [Inflate, false, createZlibDecoder], + [Gzip, true, createZlibEncoder], + [Gunzip, false, createZlibDecoder], + [DeflateRaw, true, createZlibEncoder], + [InflateRaw, false, createZlibDecoder], + [Unzip, false, createZlibDecoder], + [BrotliDecompress, false, createBrotliDecoder], + [BrotliCompress, true, createBrotliEncoder], +] as const; + +function createConvenienceMethod(method: number, is_sync: boolean) { + const [, , private_constructor] = methods[method]; + + switch (is_sync) { + case false: + return function (buffer, options, callback) { + if (typeof options === "function") { + callback = options; + options = {}; + } + if (options == null) options = {}; + if ($isObject(options)) options.maxOutputLength ??= maxOutputLengthDefault; + if (typeof callback !== "function") throw ERR_INVALID_ARG_TYPE("callback", "function", callback); + const coder = private_constructor(options, {}, callback, method); + coder.transform(buffer, undefined, true); + }; + case true: + return function (buffer, options) { + if (options == null) options = {}; + if ($isObject(options)) options.maxOutputLength ??= maxOutputLengthDefault; + const coder = private_constructor(options, {}, null, method); + return coder.transformSync(buffer, undefined, true); + }; + } +} -// node_modules/browserify-zlib/lib/index.js -var require_lib = __commonJS({ - "node_modules/browserify-zlib/lib/index.js"(exports) { - "use strict"; - var Buffer2 = BufferModule.Buffer; - var Transform = StreamModule.Transform; - var binding = require_binding(); - var util = Util; - var kMaxLength = BufferModule.kMaxLength; - var kRangeErrorMessage = - "Cannot create final Buffer. It would be larger than 0x" + kMaxLength.toString(16) + " bytes"; - binding.Z_MIN_WINDOWBITS = 8; - binding.Z_MAX_WINDOWBITS = 15; - binding.Z_DEFAULT_WINDOWBITS = 15; - binding.Z_MIN_CHUNK = 64; - binding.Z_MAX_CHUNK = Infinity; - binding.Z_DEFAULT_CHUNK = 16 * 1024; - binding.Z_MIN_MEMLEVEL = 1; - binding.Z_MAX_MEMLEVEL = 9; - binding.Z_DEFAULT_MEMLEVEL = 8; - binding.Z_MIN_LEVEL = -1; - binding.Z_MAX_LEVEL = 9; - binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION; - var bkeys = Object.keys(binding); - for (bk = 0; bk < bkeys.length; bk++) { - bkey = bkeys[bk]; - if (bkey.match(/^Z/)) { - Object.defineProperty(exports, bkey, { - enumerable: true, - value: binding[bkey], - writable: false, - }); - } - } - var bkey; - var bk; - var codes = { - Z_OK: binding.Z_OK, - Z_STREAM_END: binding.Z_STREAM_END, - Z_NEED_DICT: binding.Z_NEED_DICT, - Z_ERRNO: binding.Z_ERRNO, - Z_STREAM_ERROR: binding.Z_STREAM_ERROR, - Z_DATA_ERROR: binding.Z_DATA_ERROR, - Z_MEM_ERROR: binding.Z_MEM_ERROR, - Z_BUF_ERROR: binding.Z_BUF_ERROR, - Z_VERSION_ERROR: binding.Z_VERSION_ERROR, - }; - var ckeys = Object.keys(codes); - for (ck = 0; ck < ckeys.length; ck++) { - ckey = ckeys[ck]; - codes[codes[ckey]] = ckey; - } - var ckey; - var ck; - Object.defineProperty(exports, "codes", { - enumerable: true, - value: Object.freeze(codes), - writable: false, - }); - exports.constants = require_constants(); - exports.Deflate = Deflate; - exports.Inflate = Inflate; - exports.Gzip = Gzip; - exports.Gunzip = Gunzip; - exports.DeflateRaw = DeflateRaw; - exports.InflateRaw = InflateRaw; - exports.Unzip = Unzip; - exports.createDeflate = function (o) { - return new Deflate(o); - }; - exports.createInflate = function (o) { - return new Inflate(o); - }; - exports.createDeflateRaw = function (o) { - return new DeflateRaw(o); - }; - exports.createInflateRaw = function (o) { - return new InflateRaw(o); - }; - exports.createGzip = function (o) { - return new Gzip(o); - }; - exports.createGunzip = function (o) { - return new Gunzip(o); - }; - exports.createUnzip = function (o) { - return new Unzip(o); - }; - exports.deflate = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new Deflate(opts), buffer, callback); - }; - exports.deflateSync = function (buffer, opts) { - return zlibBufferSync(new Deflate(opts), buffer); - }; - exports.gzip = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new Gzip(opts), buffer, callback); - }; - exports.gzipSync = function (buffer, opts) { - return zlibBufferSync(new Gzip(opts), buffer); - }; - exports.deflateRaw = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new DeflateRaw(opts), buffer, callback); - }; - exports.deflateRawSync = function (buffer, opts) { - return zlibBufferSync(new DeflateRaw(opts), buffer); - }; - exports.unzip = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new Unzip(opts), buffer, callback); - }; - exports.unzipSync = function (buffer, opts) { - return zlibBufferSync(new Unzip(opts), buffer); - }; - exports.inflate = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new Inflate(opts), buffer, callback); - }; - exports.inflateSync = function (buffer, opts) { - return zlibBufferSync(new Inflate(opts), buffer); - }; - exports.gunzip = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new Gunzip(opts), buffer, callback); - }; - exports.gunzipSync = function (buffer, opts) { - return zlibBufferSync(new Gunzip(opts), buffer); - }; - exports.inflateRaw = function (buffer, opts, callback) { - if (typeof opts === "function") { - callback = opts; - opts = {}; - } - return zlibBuffer(new InflateRaw(opts), buffer, callback); - }; - exports.inflateRawSync = function (buffer, opts) { - return zlibBufferSync(new InflateRaw(opts), buffer); - }; +function createCreator(method: number) { + const Constructor = methods[method][0]; + return function (opts) { + return new Constructor(opts); + }; +} - exports.brotliCompress = brotliCompress; - exports.brotliDecompress = brotliDecompress; - exports.brotliCompressSync = brotliCompressSync; - exports.brotliDecompressSync = brotliDecompressSync; - exports.createBrotliCompress = createBrotliCompress; - exports.BrotliCompress = BrotliCompress; - exports.createBrotliDecompress = createBrotliDecompress; - exports.BrotliDecompress = BrotliDecompress; +const functions = { + crc32: $newZigFunction("node_zlib_binding.zig", "crc32", 1), + + deflate: createConvenienceMethod(DEFLATE, false), + deflateSync: createConvenienceMethod(DEFLATE, true), + gzip: createConvenienceMethod(GZIP, false), + gzipSync: createConvenienceMethod(GZIP, true), + deflateRaw: createConvenienceMethod(DEFLATERAW, false), + deflateRawSync: createConvenienceMethod(DEFLATERAW, true), + unzip: createConvenienceMethod(UNZIP, false), + unzipSync: createConvenienceMethod(UNZIP, true), + inflate: createConvenienceMethod(INFLATE, false), + inflateSync: createConvenienceMethod(INFLATE, true), + gunzip: createConvenienceMethod(GUNZIP, false), + gunzipSync: createConvenienceMethod(GUNZIP, true), + inflateRaw: createConvenienceMethod(INFLATERAW, false), + inflateRawSync: createConvenienceMethod(INFLATERAW, true), + brotliCompress: createConvenienceMethod(BROTLI_ENCODE, false), + brotliCompressSync: createConvenienceMethod(BROTLI_ENCODE, true), + brotliDecompress: createConvenienceMethod(BROTLI_DECODE, false), + brotliDecompressSync: createConvenienceMethod(BROTLI_DECODE, true), + + createDeflate: createCreator(DEFLATE), + createInflate: createCreator(INFLATE), + createDeflateRaw: createCreator(DEFLATERAW), + createInflateRaw: createCreator(INFLATERAW), + createGzip: createCreator(GZIP), + createGunzip: createCreator(GUNZIP), + createUnzip: createCreator(UNZIP), + createBrotliCompress: createCreator(BROTLI_ENCODE), + createBrotliDecompress: createCreator(BROTLI_DECODE), +}; +for (const f in functions) { + Object.defineProperty(functions[f], "name", { + value: f, + }); +} - function zlibBuffer(engine, buffer, callback) { - var buffers = []; - var nread = 0; - engine.on("error", onError); - engine.on("end", onEnd); - engine.end(buffer); - flow(); - function flow() { - var chunk; - while (null !== (chunk = engine.read())) { - buffers.push(chunk); - nread += chunk.length; - } - engine.once("readable", flow); - } - function onError(err) { - engine.removeListener("end", onEnd); - engine.removeListener("readable", flow); - callback(err); - } - function onEnd() { - var buf; - var err = null; - if (nread >= kMaxLength) { - err = new RangeError(kRangeErrorMessage); - } else { - buf = Buffer2.concat(buffers, nread); - } - buffers = []; - engine.close(); - callback(err, buf); - } - } - function zlibBufferSync(engine, buffer) { - if (typeof buffer === "string") { - buffer = Buffer2.from(buffer); - } else if (!isArrayBufferView(buffer)) { - if (!isAnyArrayBuffer(buffer)) { - throw new ERR_INVALID_ARG_TYPE( - "buffer", - ["string", "Buffer", "TypedArray", "DataView", "ArrayBuffer"], - buffer, - ); - } - buffer = Buffer2.from(buffer); - } - var flushFlag = engine._finishFlushFlag; - return engine._processChunk(buffer, flushFlag); - } - function Deflate(opts) { - if (!(this instanceof Deflate)) return new Deflate(opts); - Zlib.$call(this, opts, binding.DEFLATE); - } - function Inflate(opts) { - if (!(this instanceof Inflate)) return new Inflate(opts); - Zlib.$call(this, opts, binding.INFLATE); - } - function Gzip(opts) { - if (!(this instanceof Gzip)) return new Gzip(opts); - Zlib.$call(this, opts, binding.GZIP); - } - function Gunzip(opts) { - if (!(this instanceof Gunzip)) return new Gunzip(opts); - Zlib.$call(this, opts, binding.GUNZIP); - } - function DeflateRaw(opts) { - if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); - Zlib.$call(this, opts, binding.DEFLATERAW); - } - function InflateRaw(opts) { - if (!(this instanceof InflateRaw)) return new InflateRaw(opts); - Zlib.$call(this, opts, binding.INFLATERAW); - } - function Unzip(opts) { - if (!(this instanceof Unzip)) return new Unzip(opts); - Zlib.$call(this, opts, binding.UNZIP); - } - function isValidFlushFlag(flag) { - return ( - flag === binding.Z_NO_FLUSH || - flag === binding.Z_PARTIAL_FLUSH || - flag === binding.Z_SYNC_FLUSH || - flag === binding.Z_FULL_FLUSH || - flag === binding.Z_FINISH || - flag === binding.Z_BLOCK - ); - } - function Zlib(opts, mode) { - var _this = this; - this._opts = opts = opts || {}; - this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK; - Transform.$call(this, opts); - if (opts.flush && !isValidFlushFlag(opts.flush)) { - throw new Error("Invalid flush flag: " + opts.flush); - } - if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { - throw new Error("Invalid flush flag: " + opts.finishFlush); - } - this._flushFlag = opts.flush || binding.Z_NO_FLUSH; - this._finishFlushFlag = typeof opts.finishFlush !== "undefined" ? opts.finishFlush : binding.Z_FINISH; - if (opts.chunkSize) { - if (opts.chunkSize < exports.Z_MIN_CHUNK || opts.chunkSize > exports.Z_MAX_CHUNK) { - throw new Error("Invalid chunk size: " + opts.chunkSize); - } - } - if (opts.windowBits) { - if (opts.windowBits < exports.Z_MIN_WINDOWBITS || opts.windowBits > exports.Z_MAX_WINDOWBITS) { - throw new Error("Invalid windowBits: " + opts.windowBits); - } - } - if (opts.level) { - if (opts.level < exports.Z_MIN_LEVEL || opts.level > exports.Z_MAX_LEVEL) { - throw new Error("Invalid compression level: " + opts.level); - } - } - if (opts.memLevel) { - if (opts.memLevel < exports.Z_MIN_MEMLEVEL || opts.memLevel > exports.Z_MAX_MEMLEVEL) { - throw new Error("Invalid memLevel: " + opts.memLevel); - } - } - if (opts.strategy) { - if ( - opts.strategy != exports.Z_FILTERED && - opts.strategy != exports.Z_HUFFMAN_ONLY && - opts.strategy != exports.Z_RLE && - opts.strategy != exports.Z_FIXED && - opts.strategy != exports.Z_DEFAULT_STRATEGY - ) { - throw new Error("Invalid strategy: " + opts.strategy); - } - } - if (opts.dictionary) { - if (!Buffer2.isBuffer(opts.dictionary)) { - throw new Error("Invalid dictionary: it should be a Buffer instance"); - } - } - this._handle = new binding.Zlib(mode); - var self = this; - this._hadError = false; - this._handle.onerror = function (message, errno) { - _close(self); - self._hadError = true; - var error = new Error(message); - error.errno = errno; - error.code = exports.codes[errno]; - self.emit("error", error); - }; - var level = exports.Z_DEFAULT_COMPRESSION; - if (typeof opts.level === "number") level = opts.level; - var strategy = exports.Z_DEFAULT_STRATEGY; - if (typeof opts.strategy === "number") strategy = opts.strategy; - this._handle.init( - opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, - level, - opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, - strategy, - opts.dictionary, - ); - this._buffer = Buffer2.allocUnsafe(this._chunkSize); - this._offset = 0; - this._level = level; - this._strategy = strategy; - this.once("end", this.close); - Object.defineProperty(this, "_closed", { - get: function () { - return !_this._handle; - }, - configurable: true, - enumerable: true, - }); - } - util.inherits(Zlib, Transform); - Zlib.prototype.params = function (level, strategy, callback) { - if (level < exports.Z_MIN_LEVEL || level > exports.Z_MAX_LEVEL) { - throw new RangeError("Invalid compression level: " + level); - } - if ( - strategy != exports.Z_FILTERED && - strategy != exports.Z_HUFFMAN_ONLY && - strategy != exports.Z_RLE && - strategy != exports.Z_FIXED && - strategy != exports.Z_DEFAULT_STRATEGY - ) { - throw new TypeError("Invalid strategy: " + strategy); - } - if (this._level !== level || this._strategy !== strategy) { - var self = this; - this.flush(binding.Z_SYNC_FLUSH, function () { - assert(self._handle, "zlib binding closed"); - self._handle.params(level, strategy); - if (!self._hadError) { - self._level = level; - self._strategy = strategy; - if (callback) callback(); - } - }); - } else { - process.nextTick(callback); - } - }; - Zlib.prototype.reset = function () { - assert(this._handle, "zlib binding closed"); - return this._handle.reset(); - }; - Zlib.prototype._flush = function (callback) { - this._transform(Buffer2.alloc(0), "", callback); - }; - Zlib.prototype.flush = function (kind, callback) { - var _this2 = this; - var ws = this._writableState; - if (typeof kind === "function" || (kind === void 0 && !callback)) { - callback = kind; - kind = binding.Z_FULL_FLUSH; - } - if (ws.ended) { - if (callback) process.nextTick(callback); - } else if (ws.ending) { - if (callback) this.once("end", callback); - } else if (ws.needDrain) { - if (callback) { - this.once("drain", function () { - return _this2.flush(kind, callback); - }); - } - } else { - this._flushFlag = kind; - this.write(Buffer2.alloc(0), "", callback); - } - }; - Zlib.prototype.close = function (callback) { - _close(this, callback); - process.nextTick(emitCloseNT, this); - }; - function _close(engine, callback?) { - if (callback) process.nextTick(callback); - if (!engine._handle) return; - engine._handle.close(); - engine._handle = null; - } - function emitCloseNT(self) { - self.emit("close"); - } - Zlib.prototype._transform = function (chunk, encoding, cb) { - var flushFlag; - var ws = this._writableState; - var ending = ws.ending || ws.ended; - var last = ending && (!chunk || ws.length === chunk.length); - if (chunk !== null && !Buffer2.isBuffer(chunk)) return cb(new Error("invalid input")); - if (!this._handle) return cb(new Error("zlib binding closed")); - if (last) flushFlag = this._finishFlushFlag; - else { - flushFlag = this._flushFlag; - if (chunk.length >= ws.length) { - this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH; - } - } - this._processChunk(chunk, flushFlag, cb); - }; - Zlib.prototype._processChunk = function (chunk, flushFlag, cb) { - var availInBefore = chunk && chunk.length; - var availOutBefore = this._chunkSize - this._offset; - var inOff = 0; - var self = this; - var async = typeof cb === "function"; - if (!async) { - var buffers = []; - var nread = 0; - var error; - this.on("error", function (er) { - error = er; - }); - assert(this._handle, "zlib binding closed"); - do { - var res = this._handle.writeSync( - flushFlag, - chunk, - inOff, - availInBefore, - this._buffer, - this._offset, - availOutBefore, - ); - } while (!this._hadError && callback(res[0], res[1])); - if (this._hadError) { - throw error; - } - if (nread >= kMaxLength) { - _close(this); - throw new RangeError(kRangeErrorMessage); - } - var buf = Buffer2.concat(buffers, nread); - _close(this); - return buf; - } - assert(this._handle, "zlib binding closed"); - var req = this._handle.write(flushFlag, chunk, inOff, availInBefore, this._buffer, this._offset, availOutBefore); - req.buffer = chunk; - req.callback = callback; - function callback(availInAfter, availOutAfter) { - if (this) { - this.buffer = null; - this.callback = null; - } - if (self._hadError) return; - var have = availOutBefore - availOutAfter; - assert(have >= 0, "have should not go down"); - if (have > 0) { - var out = self._buffer.slice(self._offset, self._offset + have); - self._offset += have; - if (async) { - self.push(out); - } else { - buffers.push(out); - nread += out.length; - } - } - if (availOutAfter === 0 || self._offset >= self._chunkSize) { - availOutBefore = self._chunkSize; - self._offset = 0; - self._buffer = Buffer2.allocUnsafe(self._chunkSize); - } - if (availOutAfter === 0) { - inOff += availInBefore - availInAfter; - availInBefore = availInAfter; - if (!async) return true; - var newReq = self._handle.write( - flushFlag, - chunk, - inOff, - availInBefore, - self._buffer, - self._offset, - self._chunkSize, - ); - newReq.callback = callback; - newReq.buffer = chunk; - return; - } - if (!async) return false; - cb(); - } - }; - util.inherits(Deflate, Zlib); - util.inherits(Inflate, Zlib); - util.inherits(Gzip, Zlib); - util.inherits(Gunzip, Zlib); - util.inherits(DeflateRaw, Zlib); - util.inherits(InflateRaw, Zlib); - util.inherits(Unzip, Zlib); - }, +const zlib = { + Deflate, + Inflate, + Gzip, + Gunzip, + DeflateRaw, + InflateRaw, + Unzip, + BrotliCompress, + BrotliDecompress, + + ...functions, +}; +Object.defineProperty(zlib, "constants", { + writable: false, + configurable: false, + enumerable: true, + value: Object.freeze(constants), +}); +Object.defineProperty(zlib, "codes", { + writable: false, + configurable: false, + enumerable: true, + value: Object.freeze(codes), }); -function ERR_INVALID_ARG_TYPE(name, type, value) { - const err = new TypeError(`The "${name}" argument must be of type ${type}. Received ${value?.toString()}`); - err.code = "ERR_INVALID_ARG_TYPE"; - return err; -} - -// zlib.js -export default require_lib(); +export default zlib; diff --git a/src/js/thirdparty/node-fetch.ts b/src/js/thirdparty/node-fetch.ts index 788ee6600fac9..d2b5831690c7f 100644 --- a/src/js/thirdparty/node-fetch.ts +++ b/src/js/thirdparty/node-fetch.ts @@ -1,13 +1,13 @@ import type * as s from "stream"; -const { - Headers: WebHeaders, - Request: WebRequest, - Response: WebResponse, - Blob, - File = Blob, - FormData, -} = globalThis as any; +// Users may override the global fetch implementation, so we need to ensure these are the originals. +const bindings = $cpp("NodeFetch.cpp", "createNodeFetchInternalBinding"); +const WebResponse: typeof globalThis.Response = bindings[0]; +const WebRequest: typeof globalThis.Request = bindings[1]; +const Blob: typeof globalThis.Blob = bindings[2]; +const WebHeaders: typeof globalThis.Headers = bindings[3]; +const FormData: typeof globalThis.FormData = bindings[4]; +const File: typeof globalThis.File = bindings[5]; const nativeFetch = Bun.fetch; // node-fetch extends from URLSearchParams in their implementation... diff --git a/src/js/thirdparty/undici.js b/src/js/thirdparty/undici.js index ec0096ff84220..b194df4db5fb0 100644 --- a/src/js/thirdparty/undici.js +++ b/src/js/thirdparty/undici.js @@ -7,19 +7,22 @@ const ObjectCreate = Object.create; const kEmptyObject = ObjectCreate(null); var fetch = Bun.fetch; -var Response = globalThis.Response; -var Headers = globalThis.Headers; -var Request = globalThis.Request; -var URLSearchParams = globalThis.URLSearchParams; -var URL = globalThis.URL; -class File extends Blob {} +const bindings = $cpp("Undici.cpp", "createUndiciInternalBinding"); +const Response = bindings[0]; +const Request = bindings[1]; +const Headers = bindings[2]; +const FormData = bindings[3]; +const File = bindings[4]; +const URL = bindings[5]; +const AbortSignal = bindings[6]; +const URLSearchParams = bindings[7]; + class FileReader extends EventTarget { constructor() { throw new Error("Not implemented yet!"); } } -var FormData = globalThis.FormData; function notImplemented() { throw new Error("Not implemented in bun"); } @@ -301,28 +304,28 @@ Undici.buildConnector = Undici.fetch = fetch; export default { + Agent, + BalancedPool, + Client, + connect, + Dispatcher, fetch, - Response, - Headers, - Request, - URLSearchParams, - URL, File, FileReader, FormData, - request, - stream, - pipeline, - connect, - upgrade, - MockClient, - MockPool, + Headers, MockAgent, + MockClient, mockErrors, - Dispatcher, + MockPool, + pipeline, Pool, - BalancedPool, - Client, - Agent, + request, + Request, + Response, + stream, Undici, + upgrade, + URL, + URLSearchParams, }; diff --git a/src/js/thirdparty/ws.js b/src/js/thirdparty/ws.js index 9fa8ffd818b37..64ea131afd031 100644 --- a/src/js/thirdparty/ws.js +++ b/src/js/thirdparty/ws.js @@ -29,6 +29,11 @@ function emitWarning(type, message) { // TODO: add private method on WebSocket to avoid these allocations function normalizeData(data, opts) { const isBinary = opts?.binary; + + if (typeof data === "number") { + data = data.toString(); + } + if (isBinary === true && typeof data === "string") { data = Buffer.from(data); } else if (isBinary === false && $isTypedArrayView(data)) { @@ -147,14 +152,21 @@ class BunWebSocket extends EventEmitter { } send(data, opts, cb) { + if ($isCallable(opts)) { + cb = opts; + opts = undefined; + } + try { this.#ws.send(normalizeData(data, opts), opts?.compress); } catch (error) { - typeof cb === "function" && cb(error); + // Node.js APIs expect callback arguments to be called after the current stack pops + typeof cb === "function" && process.nextTick(cb, error); return; } // deviation: this should be called once the data is written, not immediately - typeof cb === "function" && cb(); + // Node.js APIs expect callback arguments to be called after the current stack pops + typeof cb === "function" && process.nextTick(cb, null); } close(code, reason) { @@ -727,13 +739,22 @@ class BunWebSocketMocked extends EventEmitter { } send(data, opts, cb) { + if ($isCallable(opts)) { + cb = opts; + opts = undefined; + } + if (this.#state === 1) { const compress = opts?.compress; data = normalizeData(data, opts); + // send returns: + // 1+ - The number of bytes sent is always the byte length of the data never less + // 0 - dropped due to backpressure (not sent) + // -1 - enqueue the data internaly + // we dont need to do anything with the return value here const written = this.#ws.send(data, compress); - - if (written == -1) { - // backpressure + if (written === 0) { + // dropped this.#enquedMessages.push([data, compress, cb]); this.#bufferedAmount += data.length; return; @@ -1099,7 +1120,7 @@ class WebSocketServer extends EventEmitter { * @private */ completeUpgrade(extensions, key, protocols, request, socket, head, cb) { - const [server, response, req] = socket[kBunInternals]; + const [{ [kBunInternals]: server }, response, req] = socket[kBunInternals]; if (this._state > RUNNING) return abortHandshake(response, 503); let protocol = ""; diff --git a/src/js_ast.zig b/src/js_ast.zig index 6eb3fdae933df..bbd318cbe6093 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -36,189 +36,165 @@ const MimeType = bun.http.MimeType; /// although it may contain no statements if there is nothing to export. pub const namespace_export_part_index = 0; -pub fn NewBaseStore(comptime Union: anytype, comptime count: usize) type { - var max_size = 0; - var max_align = 1; - for (Union) |kind| { - max_size = @max(@sizeOf(kind), max_size); - max_align = if (@sizeOf(kind) == 0) max_align else @max(@alignOf(kind), max_align); - } - - const UnionValueType = [max_size]u8; - const SizeType = std.math.IntFittingRange(0, (count + 1)); - const MaxAlign = max_align; +/// This "Store" is a specialized memory allocation strategy very similar to an +/// arena, used for allocating expression and statement nodes during JavaScript +/// parsing and visiting. Allocations are grouped into large blocks, where each +/// block is treated as a fixed-buffer allocator. When a block runs out of +/// space, a new one is created; all blocks are joined as a linked list. +/// +/// Similarly to an arena, you can call .reset() to reset state, reusing memory +/// across operations. +pub fn NewStore(comptime types: []const type, comptime count: usize) type { + const largest_size, const largest_align = brk: { + var largest_size = 0; + var largest_align = 1; + for (types) |T| { + if (@sizeOf(T) == 0) { + @compileError("NewStore does not support 0 size type: " ++ @typeName(T)); + } + largest_size = @max(@sizeOf(T), largest_size); + largest_align = @max(@alignOf(T), largest_align); + } + break :brk .{ largest_size, largest_align }; + }; - return struct { - const Allocator = std.mem.Allocator; - const Self = @This(); - pub const WithBase = struct { - head: Block = Block{}, - store: Self, - }; + const backing_allocator = bun.default_allocator; - pub const Block = struct { - used: SizeType = 0, - items: [count]UnionValueType align(MaxAlign) = undefined, + const log = Output.scoped(.Store, true); - pub inline fn isFull(block: *const Block) bool { - return block.used >= @as(SizeType, count); - } + return struct { + const Store = @This(); - pub fn append(block: *Block, comptime ValueType: type, value: ValueType) *UnionValueType { - if (comptime Environment.allow_assert) bun.assert(block.used < count); - const index = block.used; - block.items[index][0..value.len].* = value.*; - block.used +|= 1; - return &block.items[index]; - } - }; + current: *Block, + debug_lock: std.debug.SafetyLock = .{}, - const Overflow = struct { - const max = 4096 * 3; - const UsedSize = std.math.IntFittingRange(0, max + 1); - used: UsedSize = 0, - allocated: UsedSize = 0, - allocator: Allocator = default_allocator, - ptrs: [max]*Block = undefined, - - pub fn tail(this: *Overflow) *Block { - if (this.ptrs[this.used].isFull()) { - this.used +%= 1; - if (this.allocated > this.used) { - this.ptrs[this.used].used = 0; - } - } - - if (this.allocated <= this.used) { - var new_ptrs = this.allocator.alloc(Block, 2) catch unreachable; - new_ptrs[0] = Block{}; - new_ptrs[1] = Block{}; - this.ptrs[this.allocated] = &new_ptrs[0]; - this.ptrs[this.allocated + 1] = &new_ptrs[1]; - this.allocated +%= 2; + pub const Block = struct { + pub const size = largest_size * count * 2; + pub const Size = std.math.IntFittingRange(0, size + largest_size); + + buffer: [size]u8 align(largest_align) = undefined, + bytes_used: Size = 0, + next: ?*Block = null, + + pub fn tryAlloc(block: *Block, comptime T: type) ?*T { + const start = std.mem.alignForward(usize, block.bytes_used, @alignOf(T)); + if (start + @sizeOf(T) > block.buffer.len) return null; + defer block.bytes_used = @intCast(start + @sizeOf(T)); + + // it's simpler to use @ptrCast, but as a sanity check, we also + // try to compute the slice. Zig will report an out of bounds + // panic if the null detection logic above is wrong + if (Environment.isDebug) { + _ = block.buffer[block.bytes_used..][0..@sizeOf(T)]; } - return this.ptrs[this.used]; - } - - pub inline fn slice(this: *Overflow) []*Block { - return this.ptrs[0..this.used]; + return @alignCast(@ptrCast(&block.buffer[start])); } }; - overflow: Overflow = Overflow{}, - - pub threadlocal var _self: ?*Self = null; - - pub fn reclaim() []*Block { - var overflow = &_self.?.overflow; + const PreAlloc = struct { + metadata: Store, + first_block: Block, + }; - if (overflow.used == 0) { - if (overflow.allocated == 0 or overflow.ptrs[0].used == 0) { - return &.{}; - } - } + pub fn firstBlock(store: *Store) *Block { + return &@as(*PreAlloc, @fieldParentPtr("metadata", store)).first_block; + } - var to_move = overflow.ptrs[0..overflow.allocated][overflow.used..]; + pub fn init() *Store { + log("init", .{}); + const prealloc = backing_allocator.create(PreAlloc) catch bun.outOfMemory(); - // This returns the list of maxed out blocks - var used_list = overflow.slice(); + prealloc.first_block.bytes_used = 0; + prealloc.first_block.next = null; - // The last block may be partially used. - if (overflow.allocated > overflow.used and to_move.len > 0 and to_move.ptr[0].used > 0) { - to_move = to_move[1..]; - used_list.len += 1; - } + prealloc.metadata = .{ + .current = &prealloc.first_block, + }; - const used = overflow.allocator.dupe(*Block, used_list) catch unreachable; + return &prealloc.metadata; + } - for (to_move, overflow.ptrs[0..to_move.len]) |b, *out| { - b.* = Block{ - .items = undefined, - .used = 0, - }; - out.* = b; + pub fn deinit(store: *Store) void { + log("deinit", .{}); + var it = store.firstBlock().next; // do not free `store.head` + while (it) |next| { + if (Environment.isDebug) + @memset(next.buffer, undefined); + it = next.next; + backing_allocator.destroy(next); } - overflow.allocated = @as(Overflow.UsedSize, @truncate(to_move.len)); - overflow.used = 0; - - return used; + const prealloc: PreAlloc = @fieldParentPtr("metadata", store); + bun.assert(&prealloc.first_block == store.head); + backing_allocator.destroy(prealloc); } - /// Reset all AST nodes, allowing the memory to be reused for the next parse. - /// Only call this when we're done with ALL AST nodes, or you risk - /// undefined memory bugs. - /// - /// Nested parsing should either use the same store, or call - /// Store.reclaim. - pub fn reset() void { - const blocks = _self.?.overflow.slice(); - for (blocks) |b| { - if (comptime Environment.isDebug) { - // ensure we crash if we use a freed value - const bytes = std.mem.asBytes(&b.items); - @memset(bytes, undefined); + pub fn reset(store: *Store) void { + store.debug_lock.assertUnlocked(); + log("reset", .{}); + + if (Environment.isDebug) { + var it: ?*Block = store.firstBlock(); + while (it) |next| : (it = next.next) { + next.bytes_used = undefined; + @memset(&next.buffer, undefined); } - b.used = 0; } - _self.?.overflow.used = 0; + + store.current = store.firstBlock(); + store.current.bytes_used = 0; } - pub fn init(allocator: std.mem.Allocator) *Self { - var base = allocator.create(WithBase) catch unreachable; - base.* = WithBase{ .store = .{ .overflow = Overflow{ .allocator = allocator } } }; - var instance = &base.store; - instance.overflow.ptrs[0] = &base.head; - instance.overflow.allocated = 1; + fn allocate(store: *Store, comptime T: type) *T { + comptime bun.assert(@sizeOf(T) > 0); // don't allocate! + comptime if (!supportsType(T)) { + @compileError("Store does not know about type: " ++ @typeName(T)); + }; - _self = instance; + store.debug_lock.assertUnlocked(); + + if (store.current.tryAlloc(T)) |ptr| + return ptr; + + // a new block is needed + const next_block = if (store.current.next) |next| brk: { + next.bytes_used = 0; + break :brk next; + } else brk: { + const new_block = backing_allocator.create(Block) catch + bun.outOfMemory(); + new_block.next = null; + new_block.bytes_used = 0; + store.current.next = new_block; + break :brk new_block; + }; - return _self.?; - } + store.current = next_block; - pub fn onThreadExit(_: *anyopaque) callconv(.C) void { - deinit(); + return next_block.tryAlloc(T) orelse + unreachable; // newly initialized blocks must have enough space for at least one } - fn deinit() void { - if (_self) |this| { - _self = null; - const sliced = this.overflow.slice(); - var allocator = this.overflow.allocator; - - if (sliced.len > 1) { - var i: usize = 1; - const end = sliced.len; - while (i < end) { - const ptrs = @as(*[2]Block, @ptrCast(sliced[i])); - allocator.free(ptrs); - i += 2; - } - this.overflow.allocated = 1; - } - var base_store: *WithBase = @fieldParentPtr("store", this); - if (this.overflow.ptrs[0] == &base_store.head) { - allocator.destroy(base_store); - } + pub inline fn append(store: *Store, comptime T: type, data: T) *T { + const ptr = store.allocate(T); + if (Environment.isDebug) { + log("append({s}) -> 0x{x}", .{ bun.meta.typeName(T), @intFromPtr(ptr) }); } + ptr.* = data; + return ptr; } - pub fn append(comptime Disabler: type, comptime ValueType: type, value: ValueType) *ValueType { - Disabler.assert(); - return _self.?._append(ValueType, value); + pub fn lock(store: *Store) void { + store.debug_lock.lock(); } - inline fn _append(self: *Self, comptime ValueType: type, value: ValueType) *ValueType { - const bytes = std.mem.asBytes(&value); - const BytesAsSlice = @TypeOf(bytes); - - var block = self.overflow.tail(); + pub fn unlock(store: *Store) void { + store.debug_lock.unlock(); + } - return @as( - *ValueType, - @ptrCast(@alignCast(block.append(BytesAsSlice, bytes))), - ); + fn supportsType(T: type) bool { + return std.mem.indexOfScalar(type, types, T) != null; } }; } @@ -263,12 +239,11 @@ pub const BindingNodeList = []Binding; pub const ImportItemStatus = enum(u2) { none, - - // The linker doesn't report import/export mismatch errors + /// The linker doesn't report import/export mismatch errors generated, - // The printer will replace this import with "undefined" - + /// The printer will replace this import with "undefined" missing, + pub fn jsonStringify(self: @This(), writer: anytype) !void { return try writer.write(@tagName(self)); } @@ -295,8 +270,6 @@ pub const Flags = struct { pub const JSXElement = enum { is_key_after_spread, has_any_dynamic, - can_be_inlined, - can_be_hoisted, pub const Bitset = std.enums.EnumSet(JSXElement); }; @@ -330,10 +303,6 @@ pub const Flags = struct { /// Only applicable to function statements. is_export, - /// Used for Hot Module Reloading's wrapper function - /// "iife" stands for "immediately invoked function expression" - print_as_iife, - pub inline fn init(fields: Fields) Set { return Set.init(fields); } @@ -383,7 +352,6 @@ pub const Binding = struct { .b_missing => { return Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = loc }; }, - .b_identifier => |b| { return wrapper.wrapIdentifier(loc, b.ref); }, @@ -431,16 +399,12 @@ pub const Binding = struct { loc, ); }, - else => { - Global.panic("Internal error", .{}); - }, } } pub const Tag = enum(u5) { b_identifier, b_array, - b_property, b_object, b_missing, @@ -460,9 +424,6 @@ pub const Binding = struct { *B.Array => { return Binding{ .loc = loc, .data = B{ .b_array = t } }; }, - *B.Property => { - return Binding{ .loc = loc, .data = B{ .b_property = t } }; - }, *B.Object => { return Binding{ .loc = loc, .data = B{ .b_object = t } }; }, @@ -488,11 +449,6 @@ pub const Binding = struct { data.* = t; return Binding{ .loc = loc, .data = B{ .b_array = data } }; }, - B.Property => { - const data = allocator.create(B.Property) catch unreachable; - data.* = t; - return Binding{ .loc = loc, .data = B{ .b_property = data } }; - }, B.Object => { const data = allocator.create(B.Object) catch unreachable; data.* = t; @@ -508,13 +464,31 @@ pub const Binding = struct { } }; -/// B is for Binding! -/// These are the types of bindings that can be used in the AST. +/// B is for Binding! Bindings are on the left side of variable +/// declarations (s_local), which is how destructuring assignments +/// are represented in memory. Consider a basic example. +/// +/// let hello = world; +/// ^ ^ +/// | E.Identifier +/// B.Identifier +/// +/// Bindings can be nested +/// +/// B.Array +/// | B.Identifier +/// | | +/// let { foo: [ bar ] } = ... +/// ---------------- +/// B.Object pub const B = union(Binding.Tag) { + // let x = ... b_identifier: *B.Identifier, + // let [a, b] = ... b_array: *B.Array, - b_property: *B.Property, + // let { a, b: c } = ... b_object: *B.Object, + // this is used to represent array holes b_missing: B.Missing, pub const Identifier = struct { @@ -524,11 +498,16 @@ pub const B = union(Binding.Tag) { pub const Property = struct { flags: Flags.Property.Set = Flags.Property.None, key: ExprNodeIndex, - value: BindingNodeIndex, - default_value: ?ExprNodeIndex = null, + value: Binding, + default_value: ?Expr = null, }; - pub const Object = struct { properties: []Property, is_single_line: bool = false }; + pub const Object = struct { + properties: []B.Property, + is_single_line: bool = false, + + pub const Property = B.Property; + }; pub const Array = struct { items: []ArrayBinding, @@ -537,6 +516,39 @@ pub const B = union(Binding.Tag) { }; pub const Missing = struct {}; + + /// This hash function is currently only used for React Fast Refresh transform. + /// This doesn't include the `is_single_line` properties, as they only affect whitespace. + pub fn writeToHasher(b: B, hasher: anytype, symbol_table: anytype) void { + switch (b) { + .b_identifier => |id| { + const original_name = id.ref.getSymbol(symbol_table).original_name; + writeAnyToHasher(hasher, .{ std.meta.activeTag(b), original_name.len }); + }, + .b_array => |array| { + writeAnyToHasher(hasher, .{ std.meta.activeTag(b), array.has_spread, array.items.len }); + for (array.items) |item| { + writeAnyToHasher(hasher, .{item.default_value != null}); + if (item.default_value) |default| { + default.data.writeToHasher(hasher, symbol_table); + } + item.binding.data.writeToHasher(hasher, symbol_table); + } + }, + .b_object => |object| { + writeAnyToHasher(hasher, .{ std.meta.activeTag(b), object.properties.len }); + for (object.properties) |property| { + writeAnyToHasher(hasher, .{ property.default_value != null, property.flags }); + if (property.default_value) |default| { + default.data.writeToHasher(hasher, symbol_table); + } + property.key.data.writeToHasher(hasher, symbol_table); + property.value.data.writeToHasher(hasher, symbol_table); + } + }, + .b_missing => {}, + } + } }; pub const ClauseItem = struct { @@ -802,7 +814,7 @@ pub const G = struct { switch (val.data) { .e_arrow, .e_function => {}, else => { - if (!val.canBeConstValue()) { + if (!val.canBeMoved()) { return false; } }, @@ -837,11 +849,11 @@ pub const G = struct { // class Foo { a = 1 } // initializer: ?ExprNodeIndex = null, - kind: Kind = Kind.normal, + kind: Kind = .normal, flags: Flags.Property.Set = Flags.Property.None, class_static_block: ?*ClassStaticBlock = null, - ts_decorators: ExprNodeList = ExprNodeList{}, + ts_decorators: ExprNodeList = .{}, // Key is optional for spread key: ?ExprNodeIndex = null, @@ -878,6 +890,7 @@ pub const G = struct { set, spread, declare, + abstract, class_static_block, pub fn jsonStringify(self: @This(), writer: anytype) !void { @@ -894,10 +907,10 @@ pub const G = struct { pub const Fn = struct { name: ?LocRef = null, open_parens_loc: logger.Loc = logger.Loc.Empty, - args: []Arg = &([_]Arg{}), + args: []Arg = &.{}, // This was originally nullable, but doing so I believe caused a miscompilation // Specifically, the body was always null. - body: FnBody = FnBody{ .loc = logger.Loc.Empty, .stmts = &([_]StmtNodeIndex{}) }, + body: FnBody = .{ .loc = logger.Loc.Empty, .stmts = &.{} }, arguments_ref: ?Ref = null, flags: Flags.Function.Set = Flags.Function.None, @@ -1395,6 +1408,7 @@ pub const Symbol = struct { } } + /// Equivalent to followSymbols in esbuild pub fn follow(symbols: *const Map, ref: Ref) Ref { var symbol = symbols.get(ref) orelse return ref; if (!symbol.hasLink()) { @@ -1432,13 +1446,12 @@ pub const Symbol = struct { }; pub const OptionalChain = enum(u1) { - - // "a?.b" + /// "a?.b" start, - // "a?.b.c" => ".c" is OptionalChainContinue - // "(a?.b).c" => ".c" is OptionalChain null - ccontinue, + /// "a?.b.c" => ".c" is .continuation + /// "(a?.b).c" => ".c" is null + continuation, pub fn jsonStringify(self: @This(), writer: anytype) !void { return try writer.write(@tagName(self)); @@ -1446,6 +1459,9 @@ pub const OptionalChain = enum(u1) { }; pub const E = struct { + pub const ToJsOpts = struct { + decode_escape_sequences: bool = true, + }; pub const Array = struct { items: ExprNodeList = ExprNodeList{}, comma_after_spread: ?logger.Loc = null, @@ -1503,13 +1519,13 @@ pub const E = struct { return ExprNodeList.init(out[0 .. out.len - remain.len]); } - pub fn toJS(this: @This(), allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { + pub fn toJS(this: @This(), allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject, comptime opts: ToJsOpts) ToJSError!JSC.JSValue { const items = this.items.slice(); var array = JSC.JSValue.createEmptyArray(globalObject, items.len); array.protect(); defer array.unprotect(); for (items, 0..) |expr, j| { - array.putIndex(globalObject, @as(u32, @truncate(j)), try expr.data.toJS(allocator, globalObject)); + array.putIndex(globalObject, @as(u32, @truncate(j)), try expr.data.toJS(allocator, globalObject, opts)); } return array; @@ -1572,6 +1588,12 @@ pub const E = struct { range: logger.Range, }; pub const ImportMeta = struct {}; + pub const ImportMetaMain = struct { + /// If we want to print `!import.meta.main`, set this flag to true + /// instead of wrapping in a unary not. This way, the printer can easily + /// print `require.main != module` instead of `!(require.main == module)` + inverted: bool = false, + }; pub const Call = struct { // Node: @@ -1702,8 +1724,23 @@ pub const E = struct { was_originally_identifier: bool = false, }; + /// This is a dot expression on exports, such as `exports.`. It is given + /// it's own AST node to allow CommonJS unwrapping, in which this can just be + /// the identifier in the Ref pub const CommonJSExportIdentifier = struct { ref: Ref = Ref.None, + base: Base = .exports, + + /// The original variant of the dot expression must be known so that in the case that we + /// - fail to convert this to ESM + /// - ALSO see an assignment to `module.exports` (commonjs_module_exports_assigned_deoptimized) + /// It must be known if `exports` or `module.exports` was written in source + /// code, as the distinction will alter behavior. The fixup happens in the printer when + /// printing this node. + pub const Base = enum { + exports, + module_dot_exports, + }; }; // This is similar to EIdentifier but it represents class-private fields and @@ -1785,28 +1822,28 @@ pub const E = struct { const neg_double_digit = [_]string{ "-0", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", "-10", "-11", "-12", "-13", "-14", "-15", "-16", "-17", "-18", "-19", "-20", "-21", "-22", "-23", "-24", "-25", "-26", "-27", "-28", "-29", "-30", "-31", "-32", "-33", "-34", "-35", "-36", "-37", "-38", "-39", "-40", "-41", "-42", "-43", "-44", "-45", "-46", "-47", "-48", "-49", "-50", "-51", "-52", "-53", "-54", "-55", "-56", "-57", "-58", "-59", "-60", "-61", "-62", "-63", "-64", "-65", "-66", "-67", "-68", "-69", "-70", "-71", "-72", "-73", "-74", "-75", "-76", "-77", "-78", "-79", "-80", "-81", "-82", "-83", "-84", "-85", "-86", "-87", "-88", "-89", "-90", "-91", "-92", "-93", "-94", "-95", "-96", "-97", "-98", "-99", "-100" }; /// String concatenation with numbers is required by the TypeScript compiler for - /// "constant expression" handling in enums. However, we don't want to introduce - /// correctness bugs by accidentally stringifying a number differently than how - /// a real JavaScript VM would do it. So we are conservative and we only do this - /// when we know it'll be the same result. - pub fn toStringSafely(this: Number, allocator: std.mem.Allocator) ?string { - return toStringFromF64Safe(this.value, allocator); - } - - pub fn toStringFromF64Safe(value: f64, allocator: std.mem.Allocator) ?string { - if (comptime !Environment.isWasm) { - if (value == @trunc(value) and (value < std.math.maxInt(i32) and value > std.math.minInt(i32))) { - const int_value = @as(i64, @intFromFloat(value)); - const abs = @as(u64, @intCast(@abs(int_value))); - if (abs < double_digit.len) { - return if (int_value < 0) - neg_double_digit[abs] - else - double_digit[abs]; - } + /// "constant expression" handling in enums. We can match the behavior of a JS VM + /// by calling out to the APIs in WebKit which are responsible for this operation. + /// + /// This can return `null` in wasm builds to avoid linking JSC + pub fn toString(this: Number, allocator: std.mem.Allocator) ?string { + return toStringFromF64(this.value, allocator); + } + + pub fn toStringFromF64(value: f64, allocator: std.mem.Allocator) ?string { + if (value == @trunc(value) and (value < std.math.maxInt(i32) and value > std.math.minInt(i32))) { + const int_value = @as(i64, @intFromFloat(value)); + const abs = @as(u64, @intCast(@abs(int_value))); - return std.fmt.allocPrint(allocator, "{d}", .{@as(i32, @intCast(int_value))}) catch return null; + // do not allocate for a small set of constant numbers: -100 through 100 + if (abs < double_digit.len) { + return if (int_value < 0) + neg_double_digit[abs] + else + double_digit[abs]; } + + return std.fmt.allocPrint(allocator, "{d}", .{@as(i32, @intCast(int_value))}) catch return null; } if (std.math.isNan(value)) { @@ -1821,6 +1858,13 @@ pub const E = struct { return "Infinity"; } + if (Environment.isNative) { + var buf: [124]u8 = undefined; + return allocator.dupe(u8, bun.fmt.FormatDouble.dtoa(&buf, value)) catch bun.outOfMemory(); + } else { + // do not attempt to implement the spec here, it would be error prone. + } + return null; } @@ -1888,9 +1932,7 @@ pub const E = struct { } const rope = try allocator.create(Rope); - rope.* = .{ - .head = expr, - }; + rope.* = .{ .head = expr }; this.next = rope; return rope; } @@ -1926,7 +1968,7 @@ pub const E = struct { return if (asProperty(self, key)) |query| query.expr else @as(?Expr, null); } - pub fn toJS(this: *Object, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { + pub fn toJS(this: *Object, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject, comptime opts: ToJsOpts) ToJSError!JSC.JSValue { var obj = JSC.JSValue.createEmptyObject(globalObject, this.properties.len); obj.protect(); defer obj.unprotect(); @@ -1936,7 +1978,7 @@ pub const E = struct { return error.@"Cannot convert argument type to JS"; } var key = prop.key.?.data.e_string.toZigString(allocator); - obj.put(globalObject, &key, try prop.value.?.toJS(allocator, globalObject)); + obj.put(globalObject, &key, try prop.value.?.toJS(allocator, globalObject, opts)); } return obj; @@ -2245,17 +2287,18 @@ pub const E = struct { return bun.js_lexer.isIdentifier(this.slice(allocator)); } - pub var class = E.String{ .data = "class" }; + pub const class = E.String{ .data = "class" }; + pub fn push(this: *String, other: *String) void { bun.assert(this.isUTF8()); bun.assert(other.isUTF8()); if (other.rope_len == 0) { - other.rope_len = @as(u32, @truncate(other.data.len)); + other.rope_len = @truncate(other.data.len); } if (this.rope_len == 0) { - this.rope_len = @as(u32, @truncate(this.data.len)); + this.rope_len = @truncate(this.data.len); } this.rope_len += other.rope_len; @@ -2270,6 +2313,28 @@ pub const E = struct { } } + /// Cloning the rope string is rarely needed, see `foldStringAddition`'s + /// comments and the 'edgecase/EnumInliningRopeStringPoison' test + pub fn cloneRopeNodes(s: String) String { + var root = s; + + if (root.next != null) { + var current: ?*String = &root; + while (true) { + const node = current.?; + if (node.next) |next| { + node.next = Expr.Data.Store.append(String, next.*); + current = node.next; + } else { + root.end = node; + break; + } + } + } + + return root; + } + pub fn toUTF8(this: *String, allocator: std.mem.Allocator) !void { if (!this.is_utf16) return; this.data = try strings.toUTF8Alloc(allocator, this.slice16()); @@ -2288,6 +2353,16 @@ pub const E = struct { return .{ .data = value }; } + /// E.String containing non-ascii characters may not fully work. + /// https://github.com/oven-sh/bun/issues/11963 + /// More investigation is needed. + pub fn initReEncodeUTF8(utf8: []const u8, allocator: std.mem.Allocator) String { + return if (bun.strings.isAllASCII(utf8)) + init(utf8) + else + init(bun.strings.toUTF16AllocForReal(allocator, utf8, false, false) catch bun.outOfMemory()); + } + pub fn slice16(this: *const String) []const u16 { bun.assert(this.is_utf16); return @as([*]const u16, @ptrCast(@alignCast(this.data.ptr)))[0..this.data.len]; @@ -2295,13 +2370,13 @@ pub const E = struct { pub fn resolveRopeIfNeeded(this: *String, allocator: std.mem.Allocator) void { if (this.next == null or !this.isUTF8()) return; - var str = this.next; - var bytes = std.ArrayList(u8).initCapacity(allocator, this.rope_len) catch unreachable; + var bytes = std.ArrayList(u8).initCapacity(allocator, this.rope_len) catch bun.outOfMemory(); bytes.appendSliceAssumeCapacity(this.data); - while (str) |strin| { - bytes.appendSlice(strin.data) catch unreachable; - str = strin.next; + var str = this.next; + while (str) |part| { + bytes.appendSlice(part.data) catch bun.outOfMemory(); + str = part.next; } this.data = bytes.items; this.next = null; @@ -2309,7 +2384,7 @@ pub const E = struct { pub fn slice(this: *String, allocator: std.mem.Allocator) []const u8 { this.resolveRopeIfNeeded(allocator); - return this.string(allocator) catch unreachable; + return this.string(allocator) catch bun.outOfMemory(); } pub var empty = String{}; @@ -2416,7 +2491,7 @@ pub const E = struct { } } - pub fn eqlComptime(s: *const String, comptime value: anytype) bool { + pub fn eqlComptime(s: *const String, comptime value: []const u8) bool { return if (s.isUTF8()) strings.eqlComptime(s.data, value) else @@ -2457,6 +2532,12 @@ pub const E = struct { } } + pub fn stringDecodedUTF8(s: *const String, allocator: std.mem.Allocator) !bun.string { + const utf16_decode = try bun.js_lexer.decodeStringLiteralEscapeSequencesToUTF16(try s.string(allocator), allocator); + defer allocator.free(utf16_decode); + return try bun.strings.toUTF8Alloc(allocator, utf16_decode); + } + pub fn hash(s: *const String) u64 { if (s.isBlank()) return 0; @@ -2469,7 +2550,7 @@ pub const E = struct { } } - pub fn toJS(s: *String, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + pub fn toJS(s: *String, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject, comptime opts: ToJsOpts) JSC.JSValue { if (!s.isPresent()) { var emp = bun.String.empty; return emp.toJS(globalObject); @@ -2482,7 +2563,7 @@ pub const E = struct { return out.toJS(globalObject); } - { + if (comptime opts.decode_escape_sequences) { s.resolveRopeIfNeeded(allocator); const decoded = js_lexer.decodeStringLiteralEscapeSequencesToUTF16(s.slice(allocator), allocator) catch unreachable; @@ -2493,6 +2574,8 @@ pub const E = struct { @memcpy(chars, decoded); return out.toJS(globalObject); + } else { + return JSC.ZigString.fromUTF8(s.data).toValueGC(globalObject); } } @@ -2500,7 +2583,35 @@ pub const E = struct { if (s.isUTF8()) { return JSC.ZigString.fromUTF8(s.slice(allocator)); } else { - return JSC.ZigString.init16(s.slice16()); + return JSC.ZigString.initUTF16(s.slice16()); + } + } + + pub fn format(s: String, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + comptime bun.assert(fmt.len == 0); + + try writer.writeAll("E.String"); + if (s.next == null) { + try writer.writeAll("("); + if (s.isUTF8()) { + try writer.print("\"{s}\"", .{s.data}); + } else { + try writer.print("\"{}\"", .{bun.fmt.utf16(s.slice16())}); + } + try writer.writeAll(")"); + } else { + try writer.writeAll("(rope: ["); + var it: ?*const String = &s; + while (it) |part| { + if (part.isUTF8()) { + try writer.print("\"{s}\"", .{part.data}); + } else { + try writer.print("\"{}\"", .{bun.fmt.utf16(part.slice16())}); + } + it = part.next; + if (it != null) try writer.writeAll(" "); + } + try writer.writeAll("])"); } } @@ -2528,7 +2639,7 @@ pub const E = struct { pub const Template = struct { tag: ?ExprNodeIndex = null, - parts: []TemplatePart = &([_]TemplatePart{}), + parts: []TemplatePart = &.{}, head: Contents, pub const Contents = union(Tag) { @@ -2539,6 +2650,10 @@ pub const E = struct { cooked, raw, }; + + pub fn isUTF8(contents: Contents) bool { + return contents == .cooked and contents.cooked.isUTF8(); + } }; /// "`a${'b'}c`" => "`abc`" @@ -2550,9 +2665,7 @@ pub const E = struct { if (this.tag != null or (this.head == .cooked and !this.head.cooked.isUTF8())) { // we only fold utf-8/ascii for now return Expr{ - .data = .{ - .e_template = this, - }, + .data = .{ .e_template = this }, .loc = loc, }; } @@ -2565,13 +2678,15 @@ pub const E = struct { var parts = std.ArrayList(TemplatePart).initCapacity(allocator, this.parts.len) catch unreachable; var head = Expr.init(E.String, this.head.cooked, loc); - for (this.parts) |part_| { - var part = part_; + for (this.parts) |part_src| { + var part = part_src; bun.assert(part.tail == .cooked); + part.value = part.value.unwrapInlined(); + switch (part.value.data) { .e_number => { - if (part.value.data.e_number.toStringSafely(allocator)) |s| { + if (part.value.data.e_number.toString(allocator)) |s| { part.value = Expr.init(E.String, E.String.init(s), part.value.loc); } }, @@ -2587,6 +2702,9 @@ pub const E = struct { .e_undefined => { part.value = Expr.init(E.String, E.String.init("undefined"), part.value.loc); }, + .e_big_int => |value| { + part.value = Expr.init(E.String, E.String.init(value.value), part.value.loc); + }, else => {}, } @@ -2628,15 +2746,11 @@ pub const E = struct { return head; } - return Expr.init( - E.Template, - E.Template{ - .tag = null, - .parts = parts.items, - .head = .{ .cooked = head.data.e_string.* }, - }, - loc, - ); + return Expr.init(E.Template, .{ + .tag = null, + .parts = parts.items, + .head = .{ .cooked = head.data.e_string.* }, + }, loc); } }; @@ -2714,16 +2828,20 @@ pub const E = struct { pub const RequireResolveString = struct { import_record_index: u32 = 0, - /// TODO: - close_paren_loc: logger.Loc = logger.Loc.Empty, + // close_paren_loc: logger.Loc = logger.Loc.Empty, + }; + + pub const InlinedEnum = struct { + value: ExprNodeIndex, + comment: string, }; pub const Import = struct { expr: ExprNodeIndex, + options: ExprNodeIndex = Expr.empty, import_record_index: u32, - // This will be dynamic at some point. - type_attribute: TypeAttribute = .none, + /// TODO: /// Comments inside "import()" expressions have special meaning for Webpack. /// Preserving comments inside these expressions makes it possible to use /// esbuild as a TypeScript-to-JavaScript frontend for Webpack to improve @@ -2731,30 +2849,43 @@ pub const E = struct { /// because esbuild is not Webpack. But we do preserve them since doing so is /// harmless, easy to maintain, and useful to people. See the Webpack docs for /// more info: https://webpack.js.org/api/module-methods/#magic-comments. - /// TODO: - leading_interior_comments: []G.Comment = &([_]G.Comment{}), + // leading_interior_comments: []G.Comment = &([_]G.Comment{}), pub fn isImportRecordNull(this: *const Import) bool { return this.import_record_index == std.math.maxInt(u32); } - pub const TypeAttribute = enum { - none, - json, - toml, - text, - file, - - pub fn tag(this: TypeAttribute) ImportRecord.Tag { - return switch (this) { - .none => .none, - .json => .with_type_json, - .toml => .with_type_toml, - .text => .with_type_text, - .file => .with_type_file, + pub fn importRecordTag(import: *const Import) ?ImportRecord.Tag { + const obj = import.options.data.as(.e_object) orelse + return null; + const with = obj.get("with") orelse obj.get("assert") orelse + return null; + const with_obj = with.data.as(.e_object) orelse + return null; + const str = (with_obj.get("type") orelse + return null).data.as(.e_string) orelse + return null; + + if (str.eqlComptime("json")) { + return .with_type_json; + } else if (str.eqlComptime("toml")) { + return .with_type_toml; + } else if (str.eqlComptime("text")) { + return .with_type_text; + } else if (str.eqlComptime("file")) { + return .with_type_file; + } else if (str.eqlComptime("sqlite")) { + const embed = brk: { + const embed = with_obj.get("embed") orelse break :brk false; + const embed_str = embed.data.as(.e_string) orelse break :brk false; + break :brk embed_str.eqlComptime("true"); }; + + return if (embed) .with_type_sqlite_embedded else .with_type_sqlite; } - }; + + return null; + } }; }; @@ -2964,6 +3095,10 @@ pub const Stmt = struct { }; } + pub fn allocateExpr(allocator: std.mem.Allocator, expr: Expr) Stmt { + return Stmt.allocate(allocator, S.SExpr, S.SExpr{ .value = expr }, expr.loc); + } + pub const Tag = enum(u6) { s_block, s_break, @@ -3049,7 +3184,7 @@ pub const Stmt = struct { s_lazy_export: Expr.Data, pub const Store = struct { - const Union = [_]type{ + const StoreType = NewStore(&.{ S.Block, S.Break, S.Class, @@ -3077,53 +3212,47 @@ pub const Stmt = struct { S.Switch, S.Throw, S.Try, - S.TypeScript, S.While, S.With, - }; - const All = NewBaseStore(Union, 128); - pub threadlocal var memory_allocator: ?*ASTMemoryAllocator = null; + }, 128); - threadlocal var has_inited = false; + pub threadlocal var instance: ?*StoreType = null; + pub threadlocal var memory_allocator: ?*ASTMemoryAllocator = null; pub threadlocal var disable_reset = false; - pub fn create(allocator: std.mem.Allocator) void { - if (has_inited or memory_allocator != null) { + + pub fn create() void { + if (instance != null or memory_allocator != null) { return; } - has_inited = true; - _ = All.init(allocator); + instance = StoreType.init(); } pub fn reset() void { if (disable_reset or memory_allocator != null) return; - All.reset(); + instance.?.reset(); } pub fn deinit() void { - if (!has_inited or memory_allocator != null) return; - All.deinit(); - has_inited = false; + if (instance == null or memory_allocator != null) return; + instance.?.deinit(); + instance = null; } pub inline fn assert() void { if (comptime Environment.allow_assert) { - if (!has_inited and memory_allocator == null) + if (instance == null and memory_allocator == null) bun.unreachablePanic("Store must be init'd", .{}); } } - pub fn append(comptime ValueType: type, value: anytype) *ValueType { + pub fn append(comptime T: type, value: T) *T { if (memory_allocator) |allocator| { - return allocator.append(ValueType, value); + return allocator.append(T, value); } - return All.append(Disabler, ValueType, value); - } - - pub fn toOwnedSlice() []*Store.All.Block { - if (!has_inited or Store.All._self.?.overflow.used == 0 or disable_reset) return &[_]*Store.All.Block{}; - return Store.All.reclaim(); + Disabler.assert(); + return instance.?.append(T, value); } }; }; @@ -3135,7 +3264,7 @@ pub const Stmt = struct { }, .s_local => |local| { - return local.kind != S.Kind.k_var; + return local.kind != .k_var; }, else => { return true; @@ -3196,10 +3325,20 @@ pub const Expr = struct { else => true, }; } + pub fn canBeConstValue(this: Expr) bool { return this.data.canBeConstValue(); } + pub fn canBeMoved(expr: Expr) bool { + return expr.data.canBeMoved(); + } + + pub fn unwrapInlined(expr: Expr) Expr { + if (expr.data.as(.e_inlined_enum)) |inlined| return inlined.value; + return expr; + } + pub fn fromBlob( blob: *const JSC.WebCore.Blob, allocator: std.mem.Allocator, @@ -3284,8 +3423,8 @@ pub const Expr = struct { return false; } - pub fn toJS(this: Expr, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { - return this.data.toJS(allocator, globalObject); + pub fn toJS(this: Expr, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject, comptime opts: E.ToJsOpts) ToJSError!JSC.JSValue { + return this.data.toJS(allocator, globalObject, opts); } pub fn get(expr: *const Expr, name: string) ?Expr { @@ -3359,6 +3498,14 @@ pub const Expr = struct { return ArrayIterator{ .array = array, .index = 0 }; } + pub inline fn asUtf8StringLiteral(expr: *const Expr) ?string { + if (expr.data == .e_string) { + bun.debugAssert(expr.data.e_string.next == null); + return expr.data.e_string.data; + } + return null; + } + pub inline fn asStringLiteral(expr: *const Expr, allocator: std.mem.Allocator) ?string { if (std.meta.activeTag(expr.data) != .e_string) return null; return expr.data.e_string.string(allocator) catch null; @@ -3545,11 +3692,10 @@ pub const Expr = struct { } pub fn extractNumericValues(left: Expr.Data, right: Expr.Data) ?[2]f64 { - if (!(@as(Expr.Tag, left) == .e_number and @as(Expr.Tag, right) == .e_number)) { - return null; - } - - return [2]f64{ left.e_number.value, right.e_number.value }; + return .{ + left.extractNumericValue() orelse return null, + right.extractNumericValue() orelse return null, + }; } pub var icount: usize = 0; @@ -4175,6 +4321,7 @@ pub const Expr = struct { .data = Data{ .e_commonjs_export_identifier = .{ .ref = st.ref, + .base = st.base, }, }, }; @@ -4324,6 +4471,9 @@ pub const Expr = struct { }, }; }, + E.InlinedEnum => return .{ .loc = loc, .data = .{ + .e_inlined_enum = Data.Store.append(@TypeOf(st), st), + } }, else => { @compileError("Invalid type passed to Expr.init: " ++ @typeName(Type)); @@ -4368,6 +4518,7 @@ pub const Expr = struct { e_import_identifier, e_private_identifier, e_commonjs_export_identifier, + e_module_dot_exports, e_boolean, e_number, e_big_int, @@ -4383,13 +4534,13 @@ pub const Expr = struct { e_undefined, e_new_target, e_import_meta, + e_import_meta_main, + e_require_main, + e_inlined_enum, /// A string that is UTF-8 encoded without escaping for use in JavaScript. e_utf8_string, - // This should never make it to the printer - inline_identifier, - // object, regex and array may have had side effects pub fn isPrimitiveLiteral(tag: Tag) bool { return switch (tag) { @@ -4942,6 +5093,9 @@ pub const Expr = struct { else => {}, } }, + .e_inlined_enum => |inlined| { + return maybeSimplifyNot(inlined.value, allocator); + }, else => {}, } @@ -4949,6 +5103,35 @@ pub const Expr = struct { return null; } + pub fn toStringExprWithoutSideEffects(expr: Expr, allocator: std.mem.Allocator) ?Expr { + const unwrapped = expr.unwrapInlined(); + const slice = switch (unwrapped.data) { + .e_null => "null", + .e_string => return expr, + .e_undefined => "undefined", + .e_boolean => |data| if (data.value) "true" else "false", + .e_big_int => |bigint| bigint.value, + .e_number => |num| if (num.toString(allocator)) |str| + str + else + null, + .e_reg_exp => |regexp| regexp.value, + .e_dot => |dot| @as(?[]const u8, brk: { + // This is dumb but some JavaScript obfuscators use this to generate string literals + if (bun.strings.eqlComptime(dot.name, "constructor")) { + break :brk switch (dot.target.data) { + .e_string => "function String() { [native code] }", + .e_reg_exp => "function RegExp() { [native code] }", + else => null, + }; + } + break :brk null; + }), + else => null, + }; + return if (slice) |s| Expr.init(E.String, E.String.init(s), expr.loc) else null; + } + pub fn isOptionalChain(self: *const @This()) bool { return switch (self.data) { .e_dot => self.data.e_dot.optional_chain != null, @@ -4997,9 +5180,6 @@ pub const Expr = struct { else .mixed; } - - // This can be used when the returned type is either one or the other - }; pub const Data = union(Tag) { @@ -5030,6 +5210,7 @@ pub const Expr = struct { e_import_identifier: E.ImportIdentifier, e_private_identifier: E.PrivateIdentifier, e_commonjs_export_identifier: E.CommonJSExportIdentifier, + e_module_dot_exports, e_boolean: E.Boolean, e_number: E.Number, @@ -5038,8 +5219,8 @@ pub const Expr = struct { e_require_string: E.RequireString, e_require_resolve_string: E.RequireResolveString, - e_require_call_target: void, - e_require_resolve_call_target: void, + e_require_call_target, + e_require_resolve_call_target, e_missing: E.Missing, e_this: E.This, @@ -5049,11 +5230,19 @@ pub const Expr = struct { e_new_target: E.NewTarget, e_import_meta: E.ImportMeta, + e_import_meta_main: E.ImportMetaMain, + e_require_main, + + e_inlined_enum: *E.InlinedEnum, e_utf8_string: *E.UTF8String, - // This type should not exist outside of MacroContext - // If it ends up in JSParser or JSPrinter, it is a bug. - inline_identifier: i32, + comptime { + bun.assert_eql(@sizeOf(Data), 24); // Do not increase the size of Expr + } + + pub fn as(data: Data, comptime tag: Tag) ?std.meta.FieldType(Data, tag) { + return if (data == tag) @field(data, @tagName(tag)) else null; + } pub fn clone(this: Expr.Data, allocator: std.mem.Allocator) !Data { return switch (this) { @@ -5167,6 +5356,11 @@ pub const Expr = struct { item.* = el.*; return .{ .e_string = item }; }, + .e_inlined_enum => |el| { + const item = try allocator.create(std.meta.Child(@TypeOf(this.e_inlined_enum))); + item.* = el.*; + return .{ .e_inlined_enum = item }; + }, else => this, }; } @@ -5355,9 +5549,8 @@ pub const Expr = struct { .e_import => |el| { const item = bun.create(allocator, E.Import, .{ .expr = try el.expr.deepClone(allocator), + .options = try el.options.deepClone(allocator), .import_record_index = el.import_record_index, - .type_attribute = el.type_attribute, - .leading_interior_comments = el.leading_interior_comments, }); return .{ .e_import = item }; }, @@ -5378,13 +5571,160 @@ pub const Expr = struct { }); return .{ .e_string = item }; }, + .e_inlined_enum => |el| { + const item = bun.create(allocator, E.InlinedEnum, .{ + .value = el.value, + .comment = el.comment, + }); + return .{ .e_inlined_enum = item }; + }, else => this, }; } + /// `hasher` should be something with 'pub fn update([]const u8) void'; + /// symbol table is passed to serialize `Ref` as an identifier names instead of a nondeterministic numbers + pub fn writeToHasher(this: Expr.Data, hasher: anytype, symbol_table: anytype) void { + writeAnyToHasher(hasher, std.meta.activeTag(this)); + switch (this) { + .e_array => |e| { + writeAnyToHasher(hasher, .{ + e.is_single_line, + e.is_parenthesized, + e.was_originally_macro, + e.items.len, + }); + for (e.items.slice()) |item| { + item.data.writeToHasher(hasher, symbol_table); + } + }, + .e_unary => |e| { + writeAnyToHasher(hasher, .{e.op}); + e.value.data.writeToHasher(hasher, symbol_table); + }, + .e_binary => |e| { + writeAnyToHasher(hasher, .{e.op}); + e.left.data.writeToHasher(hasher, symbol_table); + e.right.data.writeToHasher(hasher, symbol_table); + }, + .e_class => |e| { + _ = e; // autofix + }, + inline .e_new, .e_call => |e| { + _ = e; // autofix + }, + .e_function => |e| { + _ = e; // autofix + }, + .e_dot => |e| { + writeAnyToHasher(hasher, .{ e.optional_chain, e.name.len }); + e.target.data.writeToHasher(hasher, symbol_table); + hasher.update(e.name); + }, + .e_index => |e| { + writeAnyToHasher(hasher, .{e.optional_chain}); + e.target.data.writeToHasher(hasher, symbol_table); + e.index.data.writeToHasher(hasher, symbol_table); + }, + .e_arrow => |e| { + _ = e; // autofix + }, + .e_jsx_element => |e| { + _ = e; // autofix + }, + .e_object => |e| { + _ = e; // autofix + }, + inline .e_spread, .e_await => |e| { + e.value.data.writeToHasher(hasher, symbol_table); + }, + inline .e_yield => |e| { + writeAnyToHasher(hasher, .{ e.is_star, e.value }); + if (e.value) |value| + value.data.writeToHasher(hasher, symbol_table); + }, + .e_template_part => { + // TODO: delete e_template_part as hit has zero usages + }, + .e_template => |e| { + _ = e; // autofix + }, + .e_if => |e| { + _ = e; // autofix + }, + .e_import => |e| { + _ = e; // autofix + + }, + inline .e_identifier, + .e_import_identifier, + .e_private_identifier, + .e_commonjs_export_identifier, + => |e| { + const symbol = e.ref.getSymbol(symbol_table); + hasher.update(symbol.original_name); + }, + inline .e_boolean, .e_number => |e| { + writeAnyToHasher(hasher, e.value); + }, + inline .e_big_int, .e_reg_exp => |e| { + hasher.update(e.value); + }, + + .e_string => |e| { + var next: ?*E.String = e; + if (next) |current| { + if (current.isUTF8()) { + hasher.update(current.data); + } else { + hasher.update(bun.reinterpretSlice(u8, current.slice16())); + } + next = current.next; + hasher.update("\x00"); + } + }, + inline .e_require_string, .e_require_resolve_string => |e| { + writeAnyToHasher(hasher, e.import_record_index); // preferably, i'd like to write the filepath + }, + + .e_import_meta_main => |e| { + writeAnyToHasher(hasher, e.inverted); + }, + .e_inlined_enum => |e| { + // pretend there is no comment + e.value.data.writeToHasher(hasher, symbol_table); + }, + .e_utf8_string => |e| { + hasher.update(e.data); + }, + + // no data + .e_require_call_target, + .e_require_resolve_call_target, + .e_missing, + .e_this, + .e_super, + .e_null, + .e_undefined, + .e_new_target, + .e_require_main, + .e_import_meta, + .e_module_dot_exports, + => {}, + } + } + + /// "const values" here refers to expressions that can participate in constant + /// inlining, as they have no side effects on instantiation, and there would be + /// no observable difference if duplicated. This is a subset of canBeMoved() pub fn canBeConstValue(this: Expr.Data) bool { return switch (this) { - .e_number, .e_boolean, .e_null, .e_undefined => true, + .e_number, + .e_boolean, + .e_null, + .e_undefined, + .e_inlined_enum, + => true, .e_string => |str| str.next == null, .e_array => |array| array.was_originally_macro, .e_object => |object| object.was_originally_macro, @@ -5392,6 +5732,44 @@ pub const Expr = struct { }; } + /// Expressions that can be moved are those that do not have side + /// effects on their own. This is used to determine what can be moved + /// outside of a module wrapper (__esm/__commonJS). + pub fn canBeMoved(data: Expr.Data) bool { + return switch (data) { + // TODO: identifiers can be removed if unused, however code that + // moves expressions around sometimes does so incorrectly when + // doing destructures. test case: https://github.com/oven-sh/bun/issues/14027 + // .e_identifier => |id| id.can_be_removed_if_unused, + + .e_class => |class| class.canBeMoved(), + + .e_arrow, + .e_function, + + .e_number, + .e_boolean, + .e_null, + .e_undefined, + // .e_reg_exp, + .e_big_int, + .e_string, + .e_inlined_enum, + .e_import_meta, + .e_utf8_string, + => true, + + .e_template => |template| template.tag == null and template.parts.len == 0, + + .e_array => |array| array.was_originally_macro, + .e_object => |object| object.was_originally_macro, + + // TODO: experiment with allowing some e_binary, e_unary, e_if as movable + + else => false, + }; + } + pub fn knownPrimitive(data: Expr.Data) PrimitiveType { return switch (data) { .e_big_int => .bigint, @@ -5502,6 +5880,9 @@ pub const Expr = struct { else => PrimitiveType.unknown, }, + + .e_inlined_enum => |inlined| inlined.value.data.knownPrimitive(), + else => PrimitiveType.unknown, }; } @@ -5521,8 +5902,24 @@ pub const Expr = struct { return switch (data) { .e_null => 0, .e_undefined => std.math.nan(f64), + .e_string => |str| { + if (str.next != null) return null; + + // +'1' => 1 + return stringToEquivalentNumberValue(str.data); + }, .e_boolean => @as(f64, if (data.e_boolean.value) 1.0 else 0.0), .e_number => data.e_number.value, + .e_inlined_enum => |inlined| switch (inlined.value.data) { + .e_number => |num| num.value, + .e_string => |str| { + if (str.next != null) return null; + + // +'1' => 1 + return stringToEquivalentNumberValue(str.data); + }, + else => null, + }, else => null, }; } @@ -5534,6 +5931,24 @@ pub const Expr = struct { data.e_number.value else null, + .e_inlined_enum => |inlined| switch (inlined.value.data) { + .e_number => |num| if (std.math.isFinite(num.value)) + num.value + else + null, + else => null, + }, + else => null, + }; + } + + pub fn extractNumericValue(data: Expr.Data) ?f64 { + return switch (data) { + .e_number => data.e_number.value, + .e_inlined_enum => |inlined| switch (inlined.value.data) { + .e_number => |num| num.value, + else => null, + }, else => null, }; } @@ -5542,6 +5957,14 @@ pub const Expr = struct { equal: bool = false, ok: bool = false, + /// This extra flag is unfortunately required for the case of visiting the expression + /// `require.main === module` (and any combination of !==, ==, !=, either ordering) + /// + /// We want to replace this with the dedicated import_meta_main node, which: + /// - Stops this module from having p.require_ref, allowing conversion to ESM + /// - Allows us to inline `import.meta.main`'s value, if it is known (bun build --compile) + is_require_main_and_module: bool = false, + pub const @"true" = Equality{ .ok = true, .equal = true }; pub const @"false" = Equality{ .ok = true, .equal = false }; pub const unknown = Equality{ .ok = false }; @@ -5553,11 +5976,15 @@ pub const Expr = struct { pub fn eql( left: Expr.Data, right: Expr.Data, - allocator: std.mem.Allocator, + p: anytype, comptime kind: enum { loose, strict }, ) Equality { + comptime bun.assert(@typeInfo(@TypeOf(p)).Pointer.size == .One); // pass *Parser + // https://dorey.github.io/JavaScript-Equality-Table/ switch (left) { + .e_inlined_enum => |inlined| return inlined.value.data.eql(right, p, kind), + .e_null, .e_undefined => { const ok = switch (@as(Expr.Tag, right)) { .e_null, .e_undefined => true, @@ -5616,6 +6043,12 @@ pub const Expr = struct { .equal = l.value == r.value, }; }, + .e_inlined_enum => |r| if (r.value.data == .e_number) { + return .{ + .ok = true, + .equal = l.value == r.value.data.e_number.value, + }; + }, .e_boolean => |r| { if (comptime kind == .loose) { return .{ @@ -5661,13 +6094,26 @@ pub const Expr = struct { .e_string => |l| { switch (right) { .e_string => |r| { - r.resolveRopeIfNeeded(allocator); - l.resolveRopeIfNeeded(allocator); + r.resolveRopeIfNeeded(p.allocator); + l.resolveRopeIfNeeded(p.allocator); return .{ .ok = true, .equal = r.eql(E.String, l), }; }, + .e_inlined_enum => |inlined| { + if (inlined.value.data == .e_string) { + const r = inlined.value.data.e_string; + + r.resolveRopeIfNeeded(p.allocator); + l.resolveRopeIfNeeded(p.allocator); + + return .{ + .ok = true, + .equal = r.eql(E.String, l), + }; + } + }, .e_null, .e_undefined => { return Equality.false; }, @@ -5691,18 +6137,31 @@ pub const Expr = struct { else => {}, } }, - else => {}, + + else => { + // Do not need to check left because e_require_main is + // always re-ordered to the right side. + if (right == .e_require_main) { + if (left.as(.e_identifier)) |id| { + if (id.ref.eql(p.module_ref)) return .{ + .ok = true, + .equal = true, + .is_require_main_and_module = true, + }; + } + } + }, } return Equality.unknown; } - pub fn toJS(this: Data, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { + pub fn toJS(this: Data, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject, comptime opts: E.ToJsOpts) ToJSError!JSC.JSValue { return switch (this) { - .e_array => |e| e.toJS(allocator, globalObject), - .e_object => |e| e.toJS(allocator, globalObject), - .e_string => |e| e.toJS(allocator, globalObject), - .e_utf8_string => |e| JSC.ZigString.fromUTF8(e.data).toValueGC(globalObject), + .e_array => |e| e.toJS(allocator, globalObject, opts), + .e_object => |e| e.toJS(allocator, globalObject, opts), + .e_string => |e| e.toJS(allocator, globalObject, opts), + .e_utf8_string => |e| JSC.ZigString.fromUTF8(e.data).toJS(globalObject), .e_null => JSC.JSValue.null, .e_undefined => JSC.JSValue.undefined, .e_boolean => |boolean| if (boolean.value) @@ -5712,9 +6171,10 @@ pub const Expr = struct { .e_number => |e| e.toJS(), // .e_big_int => |e| e.toJS(ctx, exception), + .e_inlined_enum => |inlined| inlined.value.data.toJS(allocator, globalObject, .{}), + .e_identifier, .e_import_identifier, - .inline_identifier, .e_private_identifier, .e_commonjs_export_identifier, => error.@"Cannot convert identifier to JS. Try a statically-known value", @@ -5732,83 +6192,72 @@ pub const Expr = struct { } pub const Store = struct { - const often = 512; - const medium = 256; - const rare = 24; - - const All = NewBaseStore( - &([_]type{ - E.Array, - E.Unary, - E.Binary, - E.Class, - E.New, - E.Function, - E.Call, - E.Dot, - E.Index, - E.Arrow, - E.RegExp, - - E.PrivateIdentifier, - E.JSXElement, - E.Number, - E.BigInt, - E.Object, - E.Spread, - E.String, - E.TemplatePart, - E.Template, - E.Await, - E.Yield, - E.If, - E.Import, - }), - 512, - ); + const StoreType = NewStore(&.{ + E.Array, + E.Arrow, + E.Await, + E.BigInt, + E.Binary, + E.Call, + E.Class, + E.Dot, + E.Function, + E.If, + E.Import, + E.Index, + E.InlinedEnum, + E.JSXElement, + E.New, + E.Number, + E.Object, + E.PrivateIdentifier, + E.RegExp, + E.Spread, + E.String, + E.Template, + E.TemplatePart, + E.Unary, + E.UTF8String, + E.Yield, + }, 512); + pub threadlocal var instance: ?*StoreType = null; pub threadlocal var memory_allocator: ?*ASTMemoryAllocator = null; - - threadlocal var has_inited = false; pub threadlocal var disable_reset = false; - pub fn create(allocator: std.mem.Allocator) void { - if (has_inited or memory_allocator != null) { + + pub fn create() void { + if (instance != null or memory_allocator != null) { return; } - has_inited = true; - _ = All.init(allocator); + instance = StoreType.init(); } pub fn reset() void { if (disable_reset or memory_allocator != null) return; - All.reset(); + instance.?.reset(); } pub fn deinit() void { - if (!has_inited or memory_allocator != null) return; - All.deinit(); - has_inited = false; + if (instance == null or memory_allocator != null) return; + instance.?.deinit(); + instance = null; } pub inline fn assert() void { if (comptime Environment.allow_assert) { - if (!has_inited and memory_allocator == null) + if (instance == null and memory_allocator == null) bun.unreachablePanic("Store must be init'd", .{}); } } - pub fn append(comptime ValueType: type, value: anytype) *ValueType { + pub fn append(comptime T: type, value: T) *T { if (memory_allocator) |allocator| { - return allocator.append(ValueType, value); + return allocator.append(T, value); } - return All.append(Disabler, ValueType, value); - } - - pub fn toOwnedSlice() []*Store.All.Block { - if (!has_inited or Store.All._self.?.overflow.used == 0 or disable_reset or memory_allocator != null) return &[_]*Store.All.Block{}; - return Store.All.reclaim(); + Disabler.assert(); + return instance.?.append(T, value); } }; @@ -5821,8 +6270,12 @@ pub const Expr = struct { pub const EnumValue = struct { loc: logger.Loc, ref: Ref, - name: E.String, + name: []const u8, value: ?ExprNodeIndex, + + pub fn nameAsEString(enum_value: EnumValue, allocator: std.mem.Allocator) E.String { + return E.String.initReEncodeUTF8(enum_value.name, allocator); + } }; pub const S = struct { @@ -5879,11 +6332,7 @@ pub const S = struct { pub fn canBeMoved(self: *const ExportDefault) bool { return switch (self.value) { - .expr => |e| switch (e.data) { - .e_class => |class| class.canBeMoved(), - .e_arrow, .e_function => true, - else => e.canBeConstValue(), - }, + .expr => |e| e.canBeMoved(), .stmt => |s| switch (s.data) { .s_class => |class| class.class.canBeMoved(), .s_function => true, @@ -6026,6 +6475,10 @@ pub const S = struct { pub fn isUsing(self: Kind) bool { return self == .k_using or self == .k_await_using; } + + pub fn isReassignable(kind: Kind) bool { + return kind == .k_var or kind == .k_let; + } }; }; @@ -6056,10 +6509,10 @@ pub const Op = struct { // If you add a new token, remember to add it to "Table" too pub const Code = enum { // Prefix - un_pos, - un_neg, - un_cpl, - un_not, + un_pos, // +expr + un_neg, // -expr + un_cpl, // ~expr + un_not, // !expr un_void, un_typeof, un_delete, @@ -6361,6 +6814,7 @@ pub const Ast = struct { uses_exports_ref: bool = false, uses_module_ref: bool = false, uses_require_ref: bool = false, + commonjs_module_exports_assigned_deoptimized: bool = false, force_cjs_to_esm: bool = false, exports_kind: ExportsKind = ExportsKind.none, @@ -6406,13 +6860,13 @@ pub const Ast = struct { /// Only populated when bundling target: bun.options.Target = .browser, - - const_values: ConstValuesMap = .{}, + // const_values: ConstValuesMap = .{}, + ts_enums: TsEnumsMap = .{}, /// Not to be confused with `commonjs_named_exports` /// This is a list of named exports that may exist in a CommonJS module /// We use this with `commonjs_at_runtime` to re-export CommonJS - commonjs_export_names: []string = &([_]string{}), + has_commonjs_export_names: bool = false, import_meta_ref: Ref = Ref.None, pub const CommonJSNamedExport = struct { @@ -6424,6 +6878,7 @@ pub const Ast = struct { pub const NamedImports = std.ArrayHashMap(Ref, NamedImport, RefHashCtx, true); pub const NamedExports = bun.StringArrayHashMap(NamedExport); pub const ConstValuesMap = std.ArrayHashMapUnmanaged(Ref, Expr, RefHashCtx, false); + pub const TsEnumsMap = std.ArrayHashMapUnmanaged(Ref, bun.StringHashMapUnmanaged(InlinedEnumValue), RefHashCtx, false); pub fn fromParts(parts: []Part) Ast { return Ast{ @@ -6508,7 +6963,8 @@ pub const BundledAst = struct { /// Only populated when bundling target: bun.options.Target = .browser, - const_values: ConstValuesMap = .{}, + // const_values: ConstValuesMap = .{}, + ts_enums: Ast.TsEnumsMap = .{}, flags: BundledAst.Flags = .{}, @@ -6518,33 +6974,24 @@ pub const BundledAst = struct { pub const CommonJSNamedExports = Ast.CommonJSNamedExports; pub const ConstValuesMap = Ast.ConstValuesMap; - pub const Flags = packed struct { + pub const Flags = packed struct(u8) { // This is a list of CommonJS features. When a file uses CommonJS features, // it's not a candidate for "flat bundling" and must be wrapped in its own // closure. uses_exports_ref: bool = false, uses_module_ref: bool = false, // uses_require_ref: bool = false, - uses_export_keyword: bool = false, - has_char_freq: bool = false, force_cjs_to_esm: bool = false, has_lazy_export: bool = false, + commonjs_module_exports_assigned_deoptimized: bool = false, + + _: u1 = 0, }; pub const empty = BundledAst.init(Ast.empty); - pub inline fn uses_exports_ref(this: *const BundledAst) bool { - return this.flags.uses_exports_ref; - } - pub inline fn uses_module_ref(this: *const BundledAst) bool { - return this.flags.uses_module_ref; - } - // pub inline fn uses_require_ref(this: *const BundledAst) bool { - // return this.flags.uses_require_ref; - // } - pub fn toAST(this: *const BundledAst) Ast { return .{ .approximate_newline_count = this.approximate_newline_count, @@ -6584,7 +7031,8 @@ pub const BundledAst = struct { .target = this.target, - .const_values = this.const_values, + // .const_values = this.const_values, + .ts_enums = this.ts_enums, .uses_exports_ref = this.flags.uses_exports_ref, .uses_module_ref = this.flags.uses_module_ref, @@ -6592,6 +7040,7 @@ pub const BundledAst = struct { .export_keyword = .{ .len = if (this.flags.uses_export_keyword) 1 else 0, .loc = .{} }, .force_cjs_to_esm = this.flags.force_cjs_to_esm, .has_lazy_export = this.flags.has_lazy_export, + .commonjs_module_exports_assigned_deoptimized = this.flags.commonjs_module_exports_assigned_deoptimized, }; } @@ -6634,7 +7083,8 @@ pub const BundledAst = struct { .target = ast.target, - .const_values = ast.const_values, + // .const_values = ast.const_values, + .ts_enums = ast.ts_enums, .flags = .{ .uses_exports_ref = ast.uses_exports_ref, @@ -6644,6 +7094,7 @@ pub const BundledAst = struct { .has_char_freq = ast.char_freq != null, .force_cjs_to_esm = ast.force_cjs_to_esm, .has_lazy_export = ast.has_lazy_export, + .commonjs_module_exports_assigned_deoptimized = ast.commonjs_module_exports_assigned_deoptimized, }, }; } @@ -6654,6 +7105,178 @@ pub const Span = struct { range: logger.Range = .{}, }; +/// This is for TypeScript "enum" and "namespace" blocks. Each block can +/// potentially be instantiated multiple times. The exported members of each +/// block are merged into a single namespace while the non-exported code is +/// still scoped to just within that block: +/// +/// let x = 1; +/// namespace Foo { +/// let x = 2; +/// export let y = 3; +/// } +/// namespace Foo { +/// console.log(x); // 1 +/// console.log(y); // 3 +/// } +/// +/// Doing this also works inside an enum: +/// +/// enum Foo { +/// A = 3, +/// B = A + 1, +/// } +/// enum Foo { +/// C = A + 2, +/// } +/// console.log(Foo.B) // 4 +/// console.log(Foo.C) // 5 +/// +/// This is a form of identifier lookup that works differently than the +/// hierarchical scope-based identifier lookup in JavaScript. Lookup now needs +/// to search sibling scopes in addition to parent scopes. This is accomplished +/// by sharing the map of exported members between all matching sibling scopes. +pub const TSNamespaceScope = struct { + /// This is specific to this namespace block. It's the argument of the + /// immediately-invoked function expression that the namespace block is + /// compiled into: + /// + /// var ns; + /// (function (ns2) { + /// ns2.x = 123; + /// })(ns || (ns = {})); + /// + /// This variable is "ns2" in the above example. It's the symbol to use when + /// generating property accesses off of this namespace when it's in scope. + arg_ref: Ref, + + /// This is shared between all sibling namespace blocks + exported_members: *TSNamespaceMemberMap, + + /// This is a lazily-generated map of identifiers that actually represent + /// property accesses to this namespace's properties. For example: + /// + /// namespace x { + /// export let y = 123 + /// } + /// namespace x { + /// export let z = y + /// } + /// + /// This should be compiled into the following code: + /// + /// var x; + /// (function(x2) { + /// x2.y = 123; + /// })(x || (x = {})); + /// (function(x3) { + /// x3.z = x3.y; + /// })(x || (x = {})); + /// + /// When we try to find the symbol "y", we instead return one of these lazily + /// generated proxy symbols that represent the property access "x3.y". This + /// map is unique per namespace block because "x3" is the argument symbol that + /// is specific to that particular namespace block. + property_accesses: bun.StringArrayHashMapUnmanaged(Ref) = .{}, + + /// Even though enums are like namespaces and both enums and namespaces allow + /// implicit references to properties of sibling scopes, they behave like + /// separate, er, namespaces. Implicit references only work namespace-to- + /// namespace and enum-to-enum. They do not work enum-to-namespace. And I'm + /// not sure what's supposed to happen for the namespace-to-enum case because + /// the compiler crashes: https://github.com/microsoft/TypeScript/issues/46891. + /// So basically these both work: + /// + /// enum a { b = 1 } + /// enum a { c = b } + /// + /// namespace x { export let y = 1 } + /// namespace x { export let z = y } + /// + /// This doesn't work: + /// + /// enum a { b = 1 } + /// namespace a { export let c = b } + /// + /// And this crashes the TypeScript compiler: + /// + /// namespace a { export let b = 1 } + /// enum a { c = b } + /// + /// Therefore we only allow enum/enum and namespace/namespace interactions. + is_enum_scope: bool, +}; + +pub const TSNamespaceMemberMap = bun.StringArrayHashMapUnmanaged(TSNamespaceMember); + +pub const TSNamespaceMember = struct { + loc: logger.Loc, + data: Data, + + pub const Data = union(enum) { + /// "namespace ns { export let it }" + property, + /// "namespace ns { export namespace it {} }" + namespace: *TSNamespaceMemberMap, + /// "enum ns { it }" + enum_number: f64, + /// "enum ns { it = 'it' }" + enum_string: *E.String, + /// "enum ns { it = something() }" + enum_property: void, + + pub fn isEnum(data: Data) bool { + return switch (data) { + inline else => |_, tag| comptime std.mem.startsWith(u8, @tagName(tag), "enum_"), + }; + } + }; +}; + +/// Inlined enum values can only be numbers and strings +/// This type special cases an encoding similar to JSValue, where nan-boxing is used +/// to encode both a 64-bit pointer or a 64-bit float using 64 bits. +pub const InlinedEnumValue = packed struct { + raw_data: u64, + + pub const Decoded = union(enum) { + string: *E.String, + number: f64, + }; + + /// See JSCJSValue.h in WebKit for more details + const double_encode_offset = 1 << 49; + /// See PureNaN.h in WebKit for more details + const pure_nan: f64 = @bitCast(@as(u64, 0x7ff8000000000000)); + + fn purifyNaN(value: f64) f64 { + return if (std.math.isNan(value)) pure_nan else value; + } + + pub fn encode(decoded: Decoded) InlinedEnumValue { + const encoded: InlinedEnumValue = .{ .raw_data = switch (decoded) { + .string => |ptr| @as(u48, @truncate(@intFromPtr(ptr))), + .number => |num| @as(u64, @bitCast(purifyNaN(num))) + double_encode_offset, + } }; + if (Environment.allow_assert) { + bun.assert(switch (encoded.decode()) { + .string => |str| str == decoded.string, + .number => |num| @as(u64, @bitCast(num)) == + @as(u64, @bitCast(purifyNaN(decoded.number))), + }); + } + return encoded; + } + + pub fn decode(encoded: InlinedEnumValue) Decoded { + if (encoded.raw_data > 0x0000FFFFFFFFFFFF) { + return .{ .number = @bitCast(encoded.raw_data - double_encode_offset) }; + } else { + return .{ .string = @ptrFromInt(encoded.raw_data) }; + } + } +}; + pub const ExportsKind = enum { // This file doesn't have any kind of export, so it's impossible to say what // kind of file this is. An empty file is in this category, for example. @@ -6827,40 +7450,45 @@ pub const Part = struct { stmts: []Stmt = &([_]Stmt{}), scopes: []*Scope = &([_]*Scope{}), - // Each is an index into the file-level import record list + /// Each is an index into the file-level import record list import_record_indices: ImportRecordIndices = .{}, - // All symbols that are declared in this part. Note that a given symbol may - // have multiple declarations, and so may end up being declared in multiple - // parts (e.g. multiple "var" declarations with the same name). Also note - // that this list isn't deduplicated and may contain duplicates. + /// All symbols that are declared in this part. Note that a given symbol may + /// have multiple declarations, and so may end up being declared in multiple + /// parts (e.g. multiple "var" declarations with the same name). Also note + /// that this list isn't deduplicated and may contain duplicates. declared_symbols: DeclaredSymbol.List = .{}, - // An estimate of the number of uses of all symbols used within this part. - symbol_uses: SymbolUseMap = SymbolUseMap{}, + /// An estimate of the number of uses of all symbols used within this part. + symbol_uses: SymbolUseMap = .{}, - // The indices of the other parts in this file that are needed if this part - // is needed. + /// This tracks property accesses off of imported symbols. We don't know + /// during parsing if an imported symbol is going to be an inlined enum + /// value or not. This is only known during linking. So we defer adding + /// a dependency on these imported symbols until we know whether the + /// property access is an inlined enum value or not. + import_symbol_property_uses: SymbolPropertyUseMap = .{}, + + /// The indices of the other parts in this file that are needed if this part + /// is needed. dependencies: Dependency.List = .{}, - // If true, this part can be removed if none of the declared symbols are - // used. If the file containing this part is imported, then all parts that - // don't have this flag enabled must be included. + /// If true, this part can be removed if none of the declared symbols are + /// used. If the file containing this part is imported, then all parts that + /// don't have this flag enabled must be included. can_be_removed_if_unused: bool = false, - // This is used for generated parts that we don't want to be present if they - // aren't needed. This enables tree shaking for these parts even if global - // tree shaking isn't enabled. + /// This is used for generated parts that we don't want to be present if they + /// aren't needed. This enables tree shaking for these parts even if global + /// tree shaking isn't enabled. force_tree_shaking: bool = false, - // This is true if this file has been marked as live by the tree shaking - // algorithm. + /// This is true if this file has been marked as live by the tree shaking + /// algorithm. is_live: bool = false, tag: Tag = Tag.none, - valid_in_development: if (bun.Environment.allow_assert) bool else void = bun.DebugOnlyDefault(true), - pub const Tag = enum { none, jsx_import, @@ -6875,6 +7503,8 @@ pub const Part = struct { }; pub const SymbolUseMap = std.ArrayHashMapUnmanaged(Ref, Symbol.Use, RefHashCtx, false); + pub const SymbolPropertyUseMap = std.ArrayHashMapUnmanaged(Ref, bun.StringHashMapUnmanaged(Symbol.Use), RefHashCtx, false); + pub fn jsonStringify(self: *const Part, writer: anytype) !void { return writer.write(self.stmts); } @@ -6887,8 +7517,19 @@ pub const Result = union(enum) { }; pub const StmtOrExpr = union(enum) { - stmt: StmtNodeIndex, - expr: ExprNodeIndex, + stmt: Stmt, + expr: Expr, + + pub fn toExpr(stmt_or_expr: StmtOrExpr) Expr { + return switch (stmt_or_expr) { + .expr => |expr| expr, + .stmt => |stmt| switch (stmt.data) { + .s_function => |s| Expr.init(E.Function, .{ .func = s.func }, stmt.loc), + .s_class => |s| Expr.init(E.Class, s.class, stmt.loc), + else => Output.panic("Unexpected statement type in default export: .{s}", .{@tagName(stmt.data)}), + }, + }; + } }; pub const NamedImport = struct { @@ -6954,6 +7595,9 @@ pub const Scope = struct { is_after_const_local_prefix: bool = false, + // This will be non-null if this is a TypeScript "namespace" or "enum" + ts_namespace: ?*TSNamespaceScope = null, + pub const NestedScopeMap = std.AutoArrayHashMap(u32, bun.BabyList(*Scope)); pub fn getMemberHash(name: []const u8) u64 { @@ -7014,6 +7658,7 @@ pub const Scope = struct { become_private_get_set_pair, become_private_static_get_set_pair, }; + pub fn canMergeSymbols( scope: *Scope, existing: Symbol.Kind, @@ -7046,9 +7691,12 @@ pub const Scope = struct { // "enum Foo {} namespace Foo { ... }" if (new == .ts_namespace) { switch (existing) { - .ts_namespace, .hoisted_function, .generator_or_async_function, .ts_enum, .class => { - return .keep_existing; - }, + .ts_namespace, + .ts_enum, + .hoisted_function, + .generator_or_async_function, + .class, + => return .keep_existing, else => {}, } } @@ -7327,7 +7975,7 @@ pub const Macro = struct { defer resolver.opts.transform_options = old_transform_options; // JSC needs to be initialized if building from CLI - JSC.initialize(); + JSC.initialize(false); var _vm = try JavaScript.VirtualMachine.init(.{ .allocator = default_allocator, @@ -7345,18 +7993,17 @@ pub const Macro = struct { vm.enableMacroMode(); - var loaded_result = try vm.loadMacroEntryPoint(input_specifier, function_name, specifier, hash); + const loaded_result = try vm.loadMacroEntryPoint(input_specifier, function_name, specifier, hash); - if (loaded_result.status(vm.global.vm()) == JSC.JSPromise.Status.Rejected) { - _ = vm.unhandledRejection(vm.global, loaded_result.result(vm.global.vm()), loaded_result.asValue()); - vm.disableMacroMode(); - return error.MacroLoadError; + switch (loaded_result.unwrap(vm.jsc, .leave_unhandled)) { + .rejected => |result| { + _ = vm.unhandledRejection(vm.global, result, loaded_result.asValue()); + vm.disableMacroMode(); + return error.MacroLoadError; + }, + else => {}, } - // We don't need to do anything with the result. - // We just want to make sure the promise is finished. - _ = loaded_result.result(vm.global.vm()); - return Macro{ .vm = vm, .resolver = resolver, @@ -7652,8 +8299,9 @@ pub const Macro = struct { const promise = value.asAnyPromise() orelse @panic("Unexpected promise type"); this.macro.vm.waitForPromise(promise); - const promise_result = promise.result(this.global.vm()); - const rejected = promise.status(this.global.vm()) == .Rejected; + + const promise_result = promise.result(this.macro.vm.jsc); + const rejected = promise.status(this.macro.vm.jsc) == .rejected; if (promise_result.isUndefined() and this.is_top_level) { this.is_top_level = false; @@ -7719,6 +8367,7 @@ pub const Macro = struct { const value = in.toJS( allocator, globalObject, + .{}, ) catch |e| { // Keeping a separate variable instead of modifying js_args.len // due to allocator.free call in defer @@ -7918,6 +8567,16 @@ pub const GlobalStoreHandle = struct { } }; +extern fn JSC__jsToNumber(latin1_ptr: [*]const u8, len: usize) f64; + +fn stringToEquivalentNumberValue(str: []const u8) f64 { + // +"" -> 0 + if (str.len == 0) return 0; + if (!bun.strings.isAllASCII(str)) + return std.math.nan(f64); + return JSC__jsToNumber(str.ptr, str.len); +} + // test "Binding.init" { // var binding = Binding.alloc( // std.heap.page_allocator, @@ -8113,3 +8772,19 @@ const ToJSError = error{ MacroError, OutOfMemory, }; + +fn assertNoPointers(T: type) void { + switch (@typeInfo(T)) { + .Pointer => @compileError("no pointers!"), + .Struct => |s| for (s.fields) |field| { + assertNoPointers(field.type); + }, + .Array => |a| assertNoPointers(a.child), + else => {}, + } +} + +inline fn writeAnyToHasher(hasher: anytype, thing: anytype) void { + comptime assertNoPointers(@TypeOf(thing)); // catch silly mistakes + hasher.update(std.mem.asBytes(&thing)); +} diff --git a/src/js_lexer.zig b/src/js_lexer.zig index af5f9f6382d19..7e7a41298e5b8 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -17,6 +17,7 @@ const default_allocator = bun.default_allocator; const C = bun.C; const FeatureFlags = @import("feature_flags.zig"); const JavascriptString = []const u16; +const Indentation = bun.js_printer.Options.Indentation; const unicode = std.unicode; @@ -31,7 +32,7 @@ pub const TypeScriptAccessibilityModifier = tables.TypeScriptAccessibilityModifi pub const ChildlessJSXTags = tables.ChildlessJSXTags; fn notimpl() noreturn { - Global.panic("not implemented yet!", .{}); + Output.panic("not implemented yet!", .{}); } pub var emptyJavaScriptString = ([_]u16{0}); @@ -75,6 +76,8 @@ pub const JSONOptions = struct { was_originally_macro: bool = false, always_decode_escape_sequences: bool = false, + + guess_indentation: bool = false, }; pub fn decodeStringLiteralEscapeSequencesToUTF16(bytes: string, allocator: std.mem.Allocator) ![]const u16 { @@ -102,6 +105,7 @@ pub fn NewLexer( json_options.json_warn_duplicate_keys, json_options.was_originally_macro, json_options.always_decode_escape_sequences, + json_options.guess_indentation, ); } @@ -114,6 +118,7 @@ fn NewLexer_( comptime json_options_json_warn_duplicate_keys: bool, comptime json_options_was_originally_macro: bool, comptime json_options_always_decode_escape_sequences: bool, + comptime json_options_guess_indentation: bool, ) type { const json_options = JSONOptions{ .is_json = json_options_is_json, @@ -124,6 +129,7 @@ fn NewLexer_( .json_warn_duplicate_keys = json_options_json_warn_duplicate_keys, .was_originally_macro = json_options_was_originally_macro, .always_decode_escape_sequences = json_options_always_decode_escape_sequences, + .guess_indentation = json_options_guess_indentation, }; return struct { const LexerType = @This(); @@ -189,6 +195,16 @@ fn NewLexer_( track_comments: bool = false, all_comments: std.ArrayList(logger.Range), + indent_info: if (json_options.guess_indentation) + struct { + guess: Indentation = .{}, + first_newline: bool = true, + } + else + void = if (json_options.guess_indentation) + .{} + else {}, + pub fn clone(self: *const LexerType) LexerType { return LexerType{ .log = self.log, @@ -1211,8 +1227,36 @@ fn NewLexer_( } }, '\r', '\n', 0x2028, 0x2029 => { - lexer.step(); lexer.has_newline_before = true; + + if (comptime json_options.guess_indentation) { + if (lexer.indent_info.first_newline and lexer.code_point == '\n') { + while (lexer.code_point == '\n' or lexer.code_point == '\r') { + lexer.step(); + } + + if (lexer.code_point != ' ' and lexer.code_point != '\t') { + // try to get the next one. this handles cases where the file starts + // with a newline + continue; + } + + lexer.indent_info.first_newline = false; + + const indent_character = lexer.code_point; + var count: usize = 0; + while (lexer.code_point == indent_character) { + lexer.step(); + count += 1; + } + + lexer.indent_info.guess.character = if (indent_character == ' ') .space else .tab; + lexer.indent_info.guess.scalar = count; + continue; + } + } + + lexer.step(); continue; }, '\t', ' ' => { diff --git a/src/js_lexer/identifier.zig b/src/js_lexer/identifier.zig index 71bbd7c4ae31c..b8da1da65faa5 100644 --- a/src/js_lexer/identifier.zig +++ b/src/js_lexer/identifier.zig @@ -48,6 +48,7 @@ pub const JumpTable = struct { // explicitly tell LLVM's optimizer about values we know will not be in the range of this switch statement 0xaa...0xffd7 => isIdentifierPartSlow16(@as(u16, @intCast(codepoint))), (0xffd7 + 1)...0xe01ef => isIdentifierPartSlow32(codepoint), + else => false, }; } @@ -108,6 +109,8 @@ pub const JumpTable = struct { 'A'...'Z', 'a'...'z', '0'...'9', '$', '_' => true, else => if (codepoint < 128) return false + else if (codepoint == 0x200C or codepoint == 0x200D) + return true else return isIdentifierPartSlow(codepoint), }; @@ -1848,7 +1851,7 @@ pub const JumpTableInline = struct { // } // } -// std.debug.print( +// .print( // \\---- Unicode text ----- // \\ // \\Timings (sum of running {d} times each, lower is better): @@ -1985,7 +1988,7 @@ pub const JumpTableInline = struct { // } // } -// std.debug.print( +// ( // \\---- ASCII text ----- // \\ // \\Timings (sum of running {d} times each, lower is better): diff --git a/src/js_lexer/identifier_data.zig b/src/js_lexer/identifier_data.zig index fc474bf553303..b9ce7afa2d6ca 100644 --- a/src/js_lexer/identifier_data.zig +++ b/src/js_lexer/identifier_data.zig @@ -1,9 +1,11 @@ const std = @import("std"); +const ZWJ = .{ 0x200C, 0x200D }; + // "unicodeESNextIdentifierStart" pub const start_codepoints = [_]i32{ 65, 90, 97, 122, 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 895, 895, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1488, 1514, 1519, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2144, 2154, 2208, 2228, 2230, 2237, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2432, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2556, 2556, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2809, 2809, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3133, 3160, 3162, 3168, 3169, 3200, 3200, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3412, 3414, 3423, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6264, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6430, 6480, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7401, 7404, 7406, 7411, 7413, 7414, 7418, 7418, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42653, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43261, 43262, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43488, 43492, 43494, 43503, 43514, 43518, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43646, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66335, 66349, 66378, 66384, 66421, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68096, 68112, 68115, 68117, 68119, 68121, 68149, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68324, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68899, 69376, 69404, 69415, 69415, 69424, 69445, 69600, 69622, 69635, 69687, 69763, 69807, 69840, 69864, 69891, 69926, 69956, 69956, 69968, 70002, 70006, 70006, 70019, 70066, 70081, 70084, 70106, 70106, 70108, 70108, 70144, 70161, 70163, 70187, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70366, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70461, 70461, 70480, 70480, 70493, 70497, 70656, 70708, 70727, 70730, 70751, 70751, 70784, 70831, 70852, 70853, 70855, 70855, 71040, 71086, 71128, 71131, 71168, 71215, 71236, 71236, 71296, 71338, 71352, 71352, 71424, 71450, 71680, 71723, 71840, 71903, 71935, 71935, 72096, 72103, 72106, 72144, 72161, 72161, 72163, 72163, 72192, 72192, 72203, 72242, 72250, 72250, 72272, 72272, 72284, 72329, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72750, 72768, 72768, 72818, 72847, 72960, 72966, 72968, 72969, 72971, 73008, 73030, 73030, 73056, 73061, 73063, 73064, 73066, 73097, 73112, 73112, 73440, 73458, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92880, 92909, 92928, 92975, 92992, 92995, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94032, 94032, 94099, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 123136, 123180, 123191, 123197, 123214, 123214, 123584, 123627, 124928, 125124, 125184, 125251, 125259, 125259, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101 }; // "unicodeESNextIdentifierPart" -pub const part_codepoints = [_]i32{ 48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2208, 2228, 2230, 2237, 2259, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3328, 3331, 3333, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7673, 7675, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43047, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69376, 69404, 69415, 69415, 69424, 69456, 69600, 69622, 69632, 69702, 69734, 69743, 69759, 69818, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69958, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70096, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70206, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70751, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71680, 71738, 71840, 71913, 71935, 71935, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123584, 123641, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101, 917760, 917999 }; +pub const part_codepoints = ZWJ ++ [_]i32{ 48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2208, 2228, 2230, 2237, 2259, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3328, 3331, 3333, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7673, 7675, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43047, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69376, 69404, 69415, 69415, 69424, 69456, 69600, 69622, 69632, 69702, 69734, 69743, 69759, 69818, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69958, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70096, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70206, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70751, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71680, 71738, 71840, 71913, 71935, 71935, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123584, 123641, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101, 917760, 917999 }; const start_codepoints_including_ascii = [_]i32{ 'a', diff --git a/src/js_lexer_tables.zig b/src/js_lexer_tables.zig index c9107f1ab8170..bc87fb0e60c91 100644 --- a/src/js_lexer_tables.zig +++ b/src/js_lexer_tables.zig @@ -7,6 +7,7 @@ const unicode = std.unicode; const default_allocator = bun.default_allocator; const string = @import("string_types.zig").string; const CodePoint = @import("string_types.zig").CodePoint; +const ComptimeStringMap = bun.ComptimeStringMap; pub const T = enum(u8) { t_end_of_file, @@ -159,7 +160,7 @@ pub const T = enum(u8) { } }; -pub const Keywords = std.StaticStringMap(T).initComptime(.{ +pub const Keywords = ComptimeStringMap(T, .{ .{ "break", .t_break }, .{ "case", .t_case }, .{ "catch", .t_catch }, @@ -198,7 +199,7 @@ pub const Keywords = std.StaticStringMap(T).initComptime(.{ .{ "with", .t_with }, }); -pub const StrictModeReservedWords = std.StaticStringMap(void).initComptime(.{ +pub const StrictModeReservedWords = ComptimeStringMap(void, .{ .{ "implements", {} }, .{ "interface", {} }, .{ "let", {} }, @@ -210,7 +211,7 @@ pub const StrictModeReservedWords = std.StaticStringMap(void).initComptime(.{ .{ "yield", {} }, }); -pub const StrictModeReservedWordsRemap = std.StaticStringMap(string).initComptime(.{ +pub const StrictModeReservedWordsRemap = ComptimeStringMap(string, .{ .{ "implements", "_implements" }, .{ "interface", "_interface" }, .{ "let", "_let" }, @@ -235,7 +236,7 @@ pub const PropertyModifierKeyword = enum { p_set, p_static, - pub const List = std.StaticStringMap(PropertyModifierKeyword).initComptime(.{ + pub const List = ComptimeStringMap(PropertyModifierKeyword, .{ .{ "abstract", .p_abstract }, .{ "async", .p_async }, .{ "declare", .p_declare }, @@ -250,7 +251,7 @@ pub const PropertyModifierKeyword = enum { }); }; -pub const TypeScriptAccessibilityModifier = std.StaticStringMap(void).initComptime(.{ +pub const TypeScriptAccessibilityModifier = ComptimeStringMap(void, .{ .{ "override", void }, .{ "private", void }, .{ "protected", void }, @@ -519,7 +520,7 @@ pub const TypescriptStmtKeyword = enum { ts_stmt_global, ts_stmt_declare, - pub const List = std.StaticStringMap(TypescriptStmtKeyword).initComptime(.{ + pub const List = ComptimeStringMap(TypescriptStmtKeyword, .{ .{ "type", TypescriptStmtKeyword.ts_stmt_type, @@ -552,7 +553,7 @@ pub const TypescriptStmtKeyword = enum { }; // Error: meta is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`. -pub const ChildlessJSXTags = std.StaticStringMap(void).initComptime(.{ +pub const ChildlessJSXTags = ComptimeStringMap(void, .{ .{ "area", void }, .{ "base", void }, .{ "br", void }, @@ -572,7 +573,7 @@ pub const ChildlessJSXTags = std.StaticStringMap(void).initComptime(.{ }); // In a microbenchmark, this outperforms -pub const jsxEntity = std.StaticStringMap(CodePoint).initComptime(.{ +pub const jsxEntity = ComptimeStringMap(CodePoint, .{ .{ "Aacute", @as(CodePoint, 0x00C1) }, .{ "aacute", @as(CodePoint, 0x00E1) }, .{ "Acirc", @as(CodePoint, 0x00C2) }, diff --git a/src/js_parser.zig b/src/js_parser.zig index 44390b32d66c9..6b03e651aa669 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -46,7 +46,7 @@ const JSC = bun.JSC; const Index = @import("./ast/base.zig").Index; fn _disabledAssert(_: bool) void { - if (!Environment.allow_assert) @compileLog("assert is missing an if (Environment.allow_assert)"); + if (!Environment.allow_assert) @compileError("assert is missing an if (Environment.allow_assert)"); unreachable; } @@ -206,31 +206,222 @@ const Substitution = union(enum) { continue_: Expr, }; -fn foldStringAddition(lhs: Expr, rhs: Expr) ?Expr { +/// Concatenate two `E.String`s, mutating BOTH inputs +/// unless `has_inlined_enum_poison` is set. +/// +/// Currently inlined enum poison refers to where mutation would cause output +/// bugs due to inlined enum values sharing `E.String`s. If a new use case +/// besides inlined enums comes up to set this to true, please rename the +/// variable and document it. +fn joinStrings(left: *const E.String, right: *const E.String, has_inlined_enum_poison: bool) E.String { + var new = if (has_inlined_enum_poison) + // Inlined enums can be shared by multiple call sites. In + // this case, we need to ensure that the ENTIRE rope is + // cloned. In other situations, the lhs doesn't have any + // other owner, so it is fine to mutate `lhs.data.end.next`. + // + // Consider the following case: + // const enum A { + // B = "a" + "b", + // D = B + "d", + // }; + // console.log(A.B, A.D); + left.cloneRopeNodes() + else + left.*; + + // Similarly, the right side has to be cloned for an enum rope too. + // + // Consider the following case: + // const enum A { + // B = "1" + "2", + // C = ("3" + B) + "4", + // }; + // console.log(A.B, A.C); + const rhs_clone = Expr.Data.Store.append(E.String, if (has_inlined_enum_poison) + right.cloneRopeNodes() + else + right.*); + + new.push(rhs_clone); + new.prefer_template = new.prefer_template or rhs_clone.prefer_template; + + return new; +} + +/// Transforming the left operand into a string is not safe if it comes from a +/// nested AST node. +const FoldStringAdditionKind = enum { + // "x" + "y" -> "xy" + // 1 + "y" -> "1y" + normal, + // a + "x" + "y" -> a + "xy" + // a + 1 + "y" -> a + 1 + y + nested_left, +}; + +// NOTE: unlike esbuild's js_ast_helpers.FoldStringAddition, this does mutate +// the input AST in the case of rope strings +fn foldStringAddition(l: Expr, r: Expr, allocator: std.mem.Allocator, kind: FoldStringAdditionKind) ?Expr { + // "See through" inline enum constants + // TODO: implement foldAdditionPreProcess to fold some more things :) + var lhs = l.unwrapInlined(); + var rhs = r.unwrapInlined(); + + if (kind != .nested_left) { + // See comment on `FoldStringAdditionKind` for examples + switch (rhs.data) { + .e_string, .e_template => { + if (lhs.toStringExprWithoutSideEffects(allocator)) |str| { + lhs = str; + } + }, + else => {}, + } + } + switch (lhs.data) { .e_string => |left| { - if (rhs.data == .e_string and left.isUTF8() and rhs.data.e_string.isUTF8()) { - var orig = lhs.data.e_string.*; - const rhs_clone = Expr.init(E.String, rhs.data.e_string.*, rhs.loc); - orig.push( - rhs_clone.data.e_string, - ); + if (rhs.toStringExprWithoutSideEffects(allocator)) |str| { + rhs = str; + } + + if (left.isUTF8()) { + switch (rhs.data) { + // "bar" + "baz" => "barbaz" + .e_string => |right| { + if (right.isUTF8()) { + const has_inlined_enum_poison = + l.data == .e_inlined_enum or + r.data == .e_inlined_enum; + + return Expr.init(E.String, joinStrings( + left, + right, + has_inlined_enum_poison, + ), lhs.loc); + } + }, + // "bar" + `baz${bar}` => `barbaz${bar}` + .e_template => |right| { + if (right.head.isUTF8()) { + return Expr.init(E.Template, E.Template{ + .parts = right.parts, + .head = .{ .cooked = joinStrings( + left, + &right.head.cooked, + l.data == .e_inlined_enum, + ) }, + }, l.loc); + } + }, + else => { + // other constant-foldable ast nodes would have been converted to .e_string + }, + } - orig.prefer_template = orig.prefer_template or rhs_clone.data.e_string.prefer_template; + // "'x' + `y${z}`" => "`xy${z}`" + if (rhs.data == .e_template and rhs.data.e_template.tag == null) {} + } - return Expr.init(E.String, orig, lhs.loc); + if (left.len() == 0 and rhs.knownPrimitive() == .string) { + return rhs; } + + return null; }, - .e_binary => |bin| { - // 123 + "bar" + "baz" - if (bin.op == .bin_add) { - if (foldStringAddition(bin.right, rhs)) |out| { - return Expr.init(E.Binary, E.Binary{ .op = bin.op, .left = bin.left, .right = out }, lhs.loc); + .e_template => |left| { + // "`${x}` + 0" => "`${x}` + '0'" + if (rhs.toStringExprWithoutSideEffects(allocator)) |str| { + rhs = str; + } + + if (left.tag == null) { + switch (rhs.data) { + // `foo${bar}` + "baz" => `foo${bar}baz` + .e_string => |right| { + if (right.isUTF8()) { + // Mutation of this node is fine because it will be not + // be shared by other places. Note that e_template will + // be treated by enums as strings, but will not be + // inlined unless they could be converted into + // .e_string. + if (left.parts.len > 0) { + const i = left.parts.len - 1; + const last = left.parts[i]; + if (last.tail.isUTF8()) { + left.parts[i].tail = .{ .cooked = joinStrings( + &last.tail.cooked, + right, + r.data == .e_inlined_enum, + ) }; + return lhs; + } + } else { + if (left.head.isUTF8()) { + left.head = .{ .cooked = joinStrings( + &left.head.cooked, + right, + r.data == .e_inlined_enum, + ) }; + return lhs; + } + } + } + }, + // `foo${bar}` + `a${hi}b` => `foo${bar}a${hi}b` + .e_template => |right| { + if (right.tag == null and right.head.isUTF8()) { + if (left.parts.len > 0) { + const i = left.parts.len - 1; + const last = left.parts[i]; + if (last.tail.isUTF8() and right.head.isUTF8()) { + left.parts[i].tail = .{ .cooked = joinStrings( + &last.tail.cooked, + &right.head.cooked, + r.data == .e_inlined_enum, + ) }; + + left.parts = if (right.parts.len == 0) + left.parts + else + std.mem.concat( + allocator, + E.TemplatePart, + &.{ left.parts, right.parts }, + ) catch bun.outOfMemory(); + return lhs; + } + } else { + if (left.head.isUTF8() and right.head.isUTF8()) { + left.head = .{ .cooked = joinStrings( + &left.head.cooked, + &right.head.cooked, + r.data == .e_inlined_enum, + ) }; + left.parts = right.parts; + return lhs; + } + } + } + }, + else => { + // other constant-foldable ast nodes would have been converted to .e_string + }, } } }, - else => {}, + + else => { + // other constant-foldable ast nodes would have been converted to .e_string + }, + } + + if (rhs.data.as(.e_string)) |right| { + if (right.len() == 0 and lhs.knownPrimitive() == .string) { + return lhs; + } } return null; @@ -261,36 +452,29 @@ const VisitArgsOpts = struct { is_unique_formal_parameters: bool = false, }; -const BunJSX = struct { - pub threadlocal var bun_jsx_identifier: E.Identifier = undefined; -}; pub fn ExpressionTransposer( - comptime Kontext: type, - comptime visitor: fn (ptr: *Kontext, arg: Expr, state: anytype) Expr, + comptime ContextType: type, + comptime StateType: type, + comptime visitor: fn (ptr: *ContextType, arg: Expr, state: StateType) Expr, ) type { return struct { - pub const Context = Kontext; + pub const Context = ContextType; pub const This = @This(); + context: *Context, pub fn init(c: *Context) This { - return This{ - .context = c, - }; + return .{ .context = c }; } - pub fn maybeTransposeIf(self: *This, arg: Expr, state: anytype) Expr { + pub fn maybeTransposeIf(self: *This, arg: Expr, state: StateType) Expr { switch (arg.data) { .e_if => |ex| { - return Expr.init( - E.If, - E.If{ - .yes = self.maybeTransposeIf(ex.yes, state), - .no = self.maybeTransposeIf(ex.no, state), - .test_ = ex.test_, - }, - arg.loc, - ); + return Expr.init(E.If, .{ + .yes = self.maybeTransposeIf(ex.yes, state), + .no = self.maybeTransposeIf(ex.no, state), + .test_ = ex.test_, + }, arg.loc); }, else => { return visitor(self.context, arg, state); @@ -298,16 +482,12 @@ pub fn ExpressionTransposer( } } - pub fn tranposeKnownToBeIf(self: *This, arg: Expr, state: anytype) Expr { - return Expr.init( - E.If, - E.If{ - .yes = self.maybeTransposeIf(arg.data.e_if.yes, state), - .no = self.maybeTransposeIf(arg.data.e_if.no, state), - .test_ = arg.data.e_if.test_, - }, - arg.loc, - ); + pub fn transposeKnownToBeIf(self: *This, arg: Expr, state: StateType) Expr { + return Expr.init(E.If, .{ + .yes = self.maybeTransposeIf(arg.data.e_if.yes, state), + .no = self.maybeTransposeIf(arg.data.e_if.no, state), + .test_ = arg.data.e_if.test_, + }, arg.loc); } }; } @@ -327,14 +507,8 @@ const TransposeState = struct { is_then_catch_target: bool = false, is_require_immediately_assigned_to_decl: bool = false, loc: logger.Loc = logger.Loc.Empty, - type_attribute: E.Import.TypeAttribute = .none, -}; - -var true_args = &[_]Expr{ - .{ - .data = .{ .e_boolean = .{ .value = true } }, - .loc = logger.Loc.Empty, - }, + import_record_tag: ?ImportRecord.Tag = null, + import_options: Expr = Expr.empty, }; const JSXTag = struct { @@ -823,7 +997,7 @@ pub const TypeScript = struct { else => return null, } } - pub const IMap = std.StaticStringMap(Kind).initComptime(.{ + pub const IMap = bun.ComptimeStringMap(Kind, .{ .{ "unique", .unique }, .{ "abstract", .abstract }, .{ "asserts", .asserts }, @@ -876,30 +1050,26 @@ pub const TypeScript = struct { }; }; -// We must prevent collisions from generated names. -// We want to avoid adding a pass over all the symbols in the file. -// To do that: -// For every generated symbol, we reserve two backup symbol names -// If any usages of the preferred ref, we swap original_name with the backup -// If any usages of the backup ref, we swap original_name with the internal -// We *assume* the internal name is never used. -// In practice, it is possible. But, the internal names are so crazy long you'd have to be deliberately trying to use them. -const GeneratedSymbol = @import("./runtime.zig").Runtime.GeneratedSymbol; - pub const ImportScanner = struct { - stmts: []Stmt = &([_]Stmt{}), - + stmts: []Stmt = &.{}, kept_import_equals: bool = false, removed_import_equals: bool = false, - pub fn scan(comptime P: type, p: *P, stmts: []Stmt, will_transform_to_common_js: bool) !ImportScanner { + + pub fn scan( + comptime P: type, + p: *P, + stmts: []Stmt, + will_transform_to_common_js: bool, + comptime hot_module_reloading_transformations: bool, + hot_module_reloading_context: if (hot_module_reloading_transformations) *P.ConvertESMExportsForHmr else void, + ) !ImportScanner { var scanner = ImportScanner{}; var stmts_end: usize = 0; const allocator = p.allocator; const is_typescript_enabled: bool = comptime P.parser_features.typescript; for (stmts) |_stmt| { - // zls needs the hint, it seems. - var stmt: Stmt = _stmt; + var stmt = _stmt; // copy switch (stmt.data) { .s_import => |st__| { var st = st__.*; @@ -1187,7 +1357,6 @@ pub const ImportScanner = struct { ); } } else { - // ESM requires live bindings // CommonJS does not require live bindings // We load ESM in browsers & in Bun.js @@ -1246,10 +1415,6 @@ pub const ImportScanner = struct { if (st.func.name) |name| { const original_name = p.symbols.items[name.ref.?.innerIndex()].original_name; try p.recordExport(name.loc, original_name, name.ref.?); - - if (p.options.features.hot_module_reloading) { - st.func.flags.remove(.is_export); - } } else { try p.log.addRangeError(p.source, logger.Range{ .loc = st.func.open_parens_loc, .len = 2 }, "Exported functions must have a name"); } @@ -1259,10 +1424,6 @@ pub const ImportScanner = struct { if (st.is_export) { if (st.class.class_name) |name| { try p.recordExport(name.loc, p.symbols.items[name.ref.?.innerIndex()].original_name, name.ref.?); - - if (p.options.features.hot_module_reloading) { - st.is_export = false; - } } else { try p.log.addRangeError(p.source, logger.Range{ .loc = st.class.body_loc, .len = 0 }, "Exported classes must have a name"); } @@ -1308,13 +1469,9 @@ pub const ImportScanner = struct { } } - // We must do this at the end to not mess up import = - if (p.options.features.hot_module_reloading and st.is_export) { - st.is_export = false; - } - // when bundling, all top-level variables become var - if (p.options.bundle and !st.kind.isUsing()) { + // TODO(@paperdave): we already do this earlier in visiting? + if (!hot_module_reloading_transformations and p.options.bundle and !st.kind.isUsing()) { st.kind = .k_var; } }, @@ -1329,129 +1486,8 @@ pub const ImportScanner = struct { // Rewrite this export to be: // exports.default = // But only if it's anonymous - if (p.options.features.hot_module_reloading) { - - // export default can be: - // - an expression - // - a function - // - a class - // it cannot be a declaration! - // we want to avoid adding a new name - // but we must remove the export default clause. - transform_export_default_when_its_anonymous: { - switch (st.value) { - .expr => |ex| { - switch (ex.data) { - .e_identifier => { - continue; - }, - .e_import_identifier => |import_ident| { - st.default_name.ref = import_ident.ref; - continue; - }, - .e_function => |func| { - if (func.func.name) |name_ref| { - if (name_ref.ref != null) { - stmt = p.s(S.Function{ .func = func.func }, ex.loc); - st.default_name.ref = name_ref.ref.?; - break :transform_export_default_when_its_anonymous; - } - } - }, - .e_class => |class| { - if (class.class_name) |name_ref| { - if (name_ref.ref != null) { - stmt = p.s( - S.Class{ - .class = class.*, - }, - ex.loc, - ); - st.default_name.ref = name_ref.ref.?; - break :transform_export_default_when_its_anonymous; - } - } - }, - else => {}, - } - var decls = try allocator.alloc(G.Decl, 1); - decls[0] = G.Decl{ .binding = p.b(B.Identifier{ .ref = st.default_name.ref.? }, stmt.loc), .value = ex }; - - stmt = p.s(S.Local{ - .decls = G.Decl.List.init(decls), - .kind = S.Local.Kind.k_var, - .is_export = false, - }, ex.loc); - }, - .stmt => |class_or_func| { - switch (class_or_func.data) { - .s_function => |func| { - if (func.func.name) |name_ref| { - if (name_ref.ref != null) { - stmt = class_or_func; - st.default_name.ref = name_ref.ref.?; - break :transform_export_default_when_its_anonymous; - } - } - - var decls = try allocator.alloc(G.Decl, 1); - decls[0] = G.Decl{ .binding = p.b(B.Identifier{ .ref = st.default_name.ref.? }, stmt.loc), .value = p.newExpr(E.Function{ .func = func.func }, stmt.loc) }; - - stmt = p.s(S.Local{ - .decls = Decl.List.init(decls), - .kind = S.Local.Kind.k_var, - .is_export = false, - }, stmt.loc); - }, - .s_class => |class| { - if (class.class.class_name) |name_ref| { - if (name_ref.ref != null) { - stmt = class_or_func; - st.default_name.ref = name_ref.ref.?; - break :transform_export_default_when_its_anonymous; - } - } - - var decls = try allocator.alloc(G.Decl, 1); - decls[0] = G.Decl{ - .binding = p.b(B.Identifier{ .ref = st.default_name.ref.? }, stmt.loc), - .value = p.newExpr(E.Class{ - .class_keyword = class.class.class_keyword, - .ts_decorators = class.class.ts_decorators, - .class_name = class.class.class_name, - .extends = class.class.extends, - .body_loc = class.class.body_loc, - .properties = class.class.properties, - .close_brace_loc = class.class.close_brace_loc, - }, stmt.loc), - }; - - stmt = p.s(S.Local{ - .decls = Decl.List.init(decls), - .kind = S.Local.Kind.k_var, - .is_export = false, - }, stmt.loc); - }, - else => unreachable, - } - }, - } - } - } else if (will_transform_to_common_js) { - const expr: js_ast.Expr = switch (st.value) { - .expr => |exp| exp, - .stmt => |s2| brk2: { - switch (s2.data) { - .s_function => |func| { - break :brk2 p.newExpr(E.Function{ .func = func.func }, s2.loc); - }, - .s_class => |class| { - break :brk2 p.newExpr(class.class, s2.loc); - }, - else => unreachable, - } - }, - }; + if (!hot_module_reloading_transformations and will_transform_to_common_js) { + const expr = st.value.toExpr(); var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable; export_default_args[0] = p.@"module.exports"(expr.loc); export_default_args[1] = expr; @@ -1462,11 +1498,6 @@ pub const ImportScanner = struct { for (st.items) |item| { try p.recordExport(item.alias_loc, item.alias, item.name.ref.?); } - - // export clauses simply disappear when we have HMR on, we use NamedExports to regenerate it at the end - if (p.options.features.hot_module_reloading) { - continue; - } }, .s_export_star => |st| { try p.import_records_for_current_part.append(allocator, st.import_record_index); @@ -1518,85 +1549,48 @@ pub const ImportScanner = struct { else => {}, } - stmts[stmts_end] = stmt; - stmts_end += 1; + if (hot_module_reloading_transformations) { + try hot_module_reloading_context.convertStmt(p, stmt); + } else { + stmts[stmts_end] = stmt; + stmts_end += 1; + } } - scanner.stmts = stmts[0..stmts_end]; + + if (!hot_module_reloading_transformations) + scanner.stmts = stmts[0..stmts_end]; + return scanner; } }; +/// We must prevent collisions from generated names with user's names. +/// +/// When transpiling for the runtime, we want to avoid adding a pass over all +/// the symbols in the file (we do this in the bundler since there is more than +/// one file, and user symbols from different files may collide with each +/// other). +/// +/// The solution: For every generated symbol, we reserve two backup symbol names: +/// - If any usages of `.primary`, fall back to `.backup` +/// - If any usages of `.backup`, fall back to `.internal` +/// - We *assume* the internal name is never used. In practice, it is possible. But, the +/// internal names are so crazy long you'd have to be deliberately trying to use them. const StaticSymbolName = struct { - internal: string, primary: string, backup: string, + internal: string, - pub const List = struct { - fn NewStaticSymbol(comptime basename: string) StaticSymbolName { - const hash_value = bun.hash(basename); - return comptime StaticSymbolName{ - .internal = basename ++ "_" ++ std.fmt.comptimePrint("{any}", .{bun.fmt.hexIntLower(hash_value)}), - .primary = basename, - .backup = "_" ++ basename ++ "$", - }; - } - - fn NewStaticSymbolWithBackup(comptime basename: string, comptime backup: string) StaticSymbolName { - const hash_value = bun.hash(basename); - return comptime StaticSymbolName{ - .internal = basename ++ "_" ++ std.fmt.comptimePrint("{any}", .{bun.fmt.hexIntLower(hash_value)}), - .primary = basename, - .backup = backup, - }; - } - - pub const jsx = NewStaticSymbol("$jsx"); - pub const jsxs = NewStaticSymbol("jsxs"); - pub const ImportSource = NewStaticSymbol("JSX"); - pub const ClassicImportSource = NewStaticSymbol("JSXClassic"); - pub const jsxFilename = NewStaticSymbolWithBackup("fileName", "jsxFileName"); - pub const REACT_ELEMENT_TYPE = NewStaticSymbolWithBackup("$$typeof", "$$reactEl"); - pub const Symbol = NewStaticSymbolWithBackup("Symbol", "Symbol"); - pub const Factory = NewStaticSymbol("jsxEl"); - pub const Refresher = NewStaticSymbol("FastRefresh"); - pub const Fragment = NewStaticSymbol("JSXFrag"); - - pub const __name = NewStaticSymbol("__name"); - pub const __toModule = NewStaticSymbol("__toModule"); - pub const __require = NewStaticSymbol("require"); - pub const __cJS2eSM = NewStaticSymbol("__cJS2eSM"); - pub const __export = NewStaticSymbol("__export"); - pub const __load = NewStaticSymbol("__load"); - pub const @"$$lzy" = NewStaticSymbol("$$lzy"); - pub const __HMRModule = NewStaticSymbol("HMR"); - pub const __HMRClient = NewStaticSymbol("Bun"); - pub const __FastRefreshModule = NewStaticSymbol("FastHMR"); - pub const __FastRefreshRuntime = NewStaticSymbol("FastRefresh"); - pub const __legacyDecorateClassTS = NewStaticSymbol("__legacyDecorateClassTS"); - pub const __legacyDecorateParamTS = NewStaticSymbol("__legacyDecorateParamTS"); - pub const __legacyMetadataTS = NewStaticSymbol("__legacyMetadataTS"); - pub const @"$$typeof" = NewStaticSymbol("$$typeof"); - - pub const @"$$m" = NewStaticSymbol("$$m"); - - pub const __exportValue = NewStaticSymbol("__exportValue"); - pub const __exportDefault = NewStaticSymbol("__exportDefault"); - pub const hmr = NewStaticSymbol("hmr"); - - pub const insert = NewStaticSymbol("insert"); - pub const template = NewStaticSymbol("template"); - pub const wrap = NewStaticSymbol("wrap"); - pub const createComponent = NewStaticSymbol("createComponent"); - pub const setAttribute = NewStaticSymbol("setAttribute"); - pub const effect = NewStaticSymbol("effect"); - pub const delegateEvents = NewStaticSymbol("delegateEvents"); - - pub const __merge = NewStaticSymbol("__merge"); - - pub const __using = NewStaticSymbol("__using"); - pub const __callDispose = NewStaticSymbol("__callDispose"); - }; + fn init(comptime basename: string) StaticSymbolName { + const hash_value = bun.hash(basename); + return comptime .{ + .internal = std.fmt.comptimePrint("{s}_{}", .{ basename, bun.fmt.hexIntLower(hash_value) }), + .primary = basename, + .backup = "_" ++ basename ++ "$", + }; + } }; +const GeneratedSymbol = @import("./runtime.zig").Runtime.GeneratedSymbol; pub const SideEffects = enum(u1) { could_have_side_effects, @@ -1657,15 +1651,36 @@ pub const SideEffects = enum(u1) { pub fn isPrimitiveToReorder(data: Expr.Data) bool { return switch (data) { - .e_null, .e_undefined, .e_string, .e_boolean, .e_number, .e_big_int => true, + .e_null, + .e_undefined, + .e_string, + .e_boolean, + .e_number, + .e_big_int, + .e_inlined_enum, + .e_require_main, + => true, else => false, }; } - pub fn simpifyUnusedExpr(p: anytype, expr: Expr) ?Expr { + pub fn simplifyUnusedExpr(p: anytype, expr: Expr) ?Expr { if (!p.options.features.dead_code_elimination) return expr; switch (expr.data) { - .e_null, .e_undefined, .e_missing, .e_boolean, .e_number, .e_big_int, .e_string, .e_this, .e_reg_exp, .e_function, .e_arrow, .e_import_meta => { + .e_null, + .e_undefined, + .e_missing, + .e_boolean, + .e_number, + .e_big_int, + .e_string, + .e_this, + .e_reg_exp, + .e_function, + .e_arrow, + .e_import_meta, + .e_inlined_enum, + => { return null; }, @@ -1684,12 +1699,12 @@ pub const SideEffects = enum(u1) { } }, .e_if => |__if__| { - __if__.yes = simpifyUnusedExpr(p, __if__.yes) orelse __if__.yes.toEmpty(); - __if__.no = simpifyUnusedExpr(p, __if__.no) orelse __if__.no.toEmpty(); + __if__.yes = simplifyUnusedExpr(p, __if__.yes) orelse __if__.yes.toEmpty(); + __if__.no = simplifyUnusedExpr(p, __if__.no) orelse __if__.no.toEmpty(); // "foo() ? 1 : 2" => "foo()" if (__if__.yes.isEmpty() and __if__.no.isEmpty()) { - return simpifyUnusedExpr(p, __if__.test_); + return simplifyUnusedExpr(p, __if__.test_); } // "foo() ? 1 : bar()" => "foo() || bar()" @@ -1717,7 +1732,7 @@ pub const SideEffects = enum(u1) { // such as "toString" or "valueOf". They must also never throw any exceptions. switch (un.op) { .un_void, .un_not => { - return simpifyUnusedExpr(p, un.value); + return simplifyUnusedExpr(p, un.value); }, .un_typeof => { // "typeof x" must not be transformed into if "x" since doing so could @@ -1727,20 +1742,21 @@ pub const SideEffects = enum(u1) { return null; } - return simpifyUnusedExpr(p, un.value); + return simplifyUnusedExpr(p, un.value); }, else => {}, } }, - .e_call => |call| { - + inline .e_call, .e_new => |call| { // A call that has been marked "__PURE__" can be removed if all arguments // can be removed. The annotation causes us to ignore the target. if (call.can_be_unwrapped_if_unused) { if (call.args.len > 0) { - return Expr.joinAllWithCommaCallback(call.args.slice(), @TypeOf(p), p, comptime simpifyUnusedExpr, p.allocator); + return Expr.joinAllWithCommaCallback(call.args.slice(), @TypeOf(p), p, comptime simplifyUnusedExpr, p.allocator); + } else { + return Expr.empty; } } }, @@ -1749,13 +1765,10 @@ pub const SideEffects = enum(u1) { switch (bin.op) { // These operators must not have any type conversions that can execute code // such as "toString" or "valueOf". They must also never throw any exceptions. - .bin_strict_eq, .bin_strict_ne, .bin_comma => { - return Expr.joinWithComma( - simpifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(), - simpifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(), - p.allocator, - ); - }, + .bin_strict_eq, + .bin_strict_ne, + .bin_comma, + => return simplifyUnusedBinaryCommaExpr(p, expr), // We can simplify "==" and "!=" even though they can call "toString" and/or // "valueOf" if we can statically determine that the types of both sides are @@ -1765,18 +1778,18 @@ pub const SideEffects = enum(u1) { .bin_loose_ne, => { if (isPrimitiveWithSideEffects(bin.left.data) and isPrimitiveWithSideEffects(bin.right.data)) { - return Expr.joinWithComma(simpifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(), simpifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(), p.allocator); + return Expr.joinWithComma(simplifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(), simplifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(), p.allocator); } }, .bin_logical_and, .bin_logical_or, .bin_nullish_coalescing => { - bin.right = simpifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(); + bin.right = simplifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(); // Preserve short-circuit behavior: the left expression is only unused if // the right expression can be completely removed. Otherwise, the left // expression is important for the branch. if (bin.right.isEmpty()) - return simpifyUnusedExpr(p, bin.left); + return simplifyUnusedExpr(p, bin.left); }, else => {}, @@ -1796,7 +1809,7 @@ pub const SideEffects = enum(u1) { for (properties_slice) |prop_| { var prop = prop_; if (prop_.kind != .spread) { - const value = simpifyUnusedExpr(p, prop.value.?); + const value = simplifyUnusedExpr(p, prop.value.?); if (value != null) { prop.value = value; } else if (!prop.flags.contains(.is_computed)) { @@ -1836,7 +1849,7 @@ pub const SideEffects = enum(u1) { ); } result = result.joinWithComma( - simpifyUnusedExpr(p, prop.value.?) orelse prop.value.?.toEmpty(), + simplifyUnusedExpr(p, prop.value.?) orelse prop.value.?.toEmpty(), p.allocator, ); } @@ -1868,34 +1881,67 @@ pub const SideEffects = enum(u1) { items, @TypeOf(p), p, - comptime simpifyUnusedExpr, + comptime simplifyUnusedExpr, p.allocator, ); }, - .e_new => |call| { - // A constructor call that has been marked "__PURE__" can be removed if all arguments - // can be removed. The annotation causes us to ignore the target. - if (call.can_be_unwrapped_if_unused) { - if (call.args.len > 0) { - return Expr.joinAllWithCommaCallback( - call.args.slice(), - @TypeOf(p), - p, - comptime simpifyUnusedExpr, - p.allocator, - ); - } - - return null; - } - }, else => {}, } return expr; } + const BinaryExpressionSimplifyVisitor = struct { + bin: *E.Binary, + }; + + /// + fn simplifyUnusedBinaryCommaExpr(p: anytype, expr: Expr) ?Expr { + if (Environment.allow_assert) { + assert(expr.data == .e_binary); + assert(switch (expr.data.e_binary.op) { + .bin_strict_eq, + .bin_strict_ne, + .bin_comma, + => true, + else => false, + }); + } + const stack: *std.ArrayList(BinaryExpressionSimplifyVisitor) = &p.binary_expression_simplify_stack; + const stack_bottom = stack.items.len; + defer stack.shrinkRetainingCapacity(stack_bottom); + + stack.append(.{ .bin = expr.data.e_binary }) catch bun.outOfMemory(); + + // Build stack up of expressions + var left: Expr = expr.data.e_binary.left; + while (left.data.as(.e_binary)) |left_bin| { + switch (left_bin.op) { + .bin_strict_eq, + .bin_strict_ne, + .bin_comma, + => { + stack.append(.{ .bin = left_bin }) catch bun.outOfMemory(); + left = left_bin.left; + }, + else => break, + } + } + + // Ride the stack downwards + var i = stack.items.len; + var result = simplifyUnusedExpr(p, left) orelse Expr.empty; + while (i > stack_bottom) { + i -= 1; + const top = stack.items[i]; + const visited_right = simplifyUnusedExpr(p, top.bin.right) orelse Expr.empty; + result = result.joinWithComma(visited_right, p.allocator); + } + + return if (result.isMissing()) Expr.empty else result; + } + fn findIdentifiers(binding: Binding, decls: *std.ArrayList(G.Decl)) void { switch (binding.data) { .b_identifier => { @@ -2014,7 +2060,14 @@ pub const SideEffects = enum(u1) { // cannot be removed due to side effects. pub fn isPrimitiveWithSideEffects(data: Expr.Data) bool { switch (data) { - .e_null, .e_undefined, .e_boolean, .e_number, .e_big_int, .e_string => { + .e_null, + .e_undefined, + .e_boolean, + .e_number, + .e_big_int, + .e_string, + .e_inlined_enum, + => { return true; }, .e_unary => |e| { @@ -2124,7 +2177,7 @@ pub const SideEffects = enum(u1) { return Result{ .value = false, .side_effects = .could_have_side_effects, .ok = true }; }, - // always anull or undefined + // always a null or undefined .e_null, .e_undefined => { return Result{ .value = true, .side_effects = .no_side_effects, .ok = true }; }, @@ -2209,6 +2262,9 @@ pub const SideEffects = enum(u1) { else => {}, } }, + .e_inlined_enum => |inlined| { + return toNullOrUndefined(p, inlined.value.data); + }, else => {}, } @@ -2317,6 +2373,9 @@ pub const SideEffects = enum(u1) { else => {}, } }, + .e_inlined_enum => |inlined| { + return toBoolean(p, inlined.value.data); + }, else => {}, } @@ -2337,7 +2396,7 @@ const AsyncPrefixExpression = enum(u2) { is_async, is_await, - const map = std.StaticStringMap(AsyncPrefixExpression).initComptime(.{ + const map = bun.ComptimeStringMap(AsyncPrefixExpression, .{ .{ "yield", .is_yield }, .{ "await", .is_await }, .{ "async", .is_async }, @@ -2476,7 +2535,7 @@ pub const StmtsKind = enum { }; fn notimpl() noreturn { - Global.panic("Not implemented yet!!", .{}); + Output.panic("Not implemented yet!!", .{}); } const ExprBindingTuple = struct { @@ -2528,7 +2587,7 @@ const InvalidLoc = struct { pub const Tag = enum { spread, - parenthese, + parentheses, getter, setter, method, @@ -2539,7 +2598,7 @@ const InvalidLoc = struct { @setCold(true); const text = switch (loc.kind) { .spread => "Unexpected trailing comma after rest element", - .parenthese => "Unexpected parentheses in binding pattern", + .parentheses => "Unexpected parentheses in binding pattern", .getter => "Unexpected getter in binding pattern", .setter => "Unexpected setter in binding pattern", .method => "Unexpected method in binding pattern", @@ -2589,7 +2648,8 @@ const StringVoidMap = struct { pub const Node = Pool.Node; }; const RefCtx = @import("./ast/base.zig").RefCtx; -const SymbolUseMap = std.HashMapUnmanaged(Ref, js_ast.Symbol.Use, RefCtx, 80); +const SymbolUseMap = js_ast.Part.SymbolUseMap; +const SymbolPropertyUseMap = js_ast.Part.SymbolPropertyUseMap; const StringBoolMap = bun.StringHashMapUnmanaged(bool); const RefMap = std.HashMapUnmanaged(Ref, void, RefCtx, 80); const RefArrayMap = std.ArrayHashMapUnmanaged(Ref, void, @import("./ast/base.zig").RefHashCtx, false); @@ -2832,22 +2892,23 @@ pub const Parser = struct { warn_about_unbundled_modules: bool = true, - legacy_transform_require_to_import: bool = true, - module_type: options.ModuleType = .unknown, transform_only: bool = false, + /// Used for inlining the state of import.meta.main during visiting + import_meta_main_value: ?bool = null, + lower_import_meta_main_for_node_js: bool = false, + pub fn hashForRuntimeTranspiler(this: *const Options, hasher: *std.hash.Wyhash, did_use_jsx: bool) void { bun.assert(!this.bundle); if (did_use_jsx) { if (this.jsx.parse) { this.jsx.hashForRuntimeTranspiler(hasher); - const jsx_optimizations = [_]bool{ - this.features.jsx_optimization_inline, - this.features.jsx_optimization_hoist, - }; + // this holds the values for the jsx optimizaiton flags, which have both been removed + // as the optimizations break newer versions of react, see https://github.com/oven-sh/bun/issues/11025 + const jsx_optimizations = [_]bool{ false, false }; hasher.update(std.mem.asBytes(&jsx_optimizations)); } else { hasher.update("NO_JSX"); @@ -2860,6 +2921,10 @@ pub const Parser = struct { hasher.update("NO_TS"); } + if (this.ignore_dce_annotations) { + hasher.update("no_dce"); + } + this.features.hashForRuntimeTranspiler(hasher); } @@ -2965,7 +3030,14 @@ pub const Parser = struct { var p: JavaScriptParser = undefined; try JavaScriptParser.init(this.allocator, this.log, this.source, this.define, this.lexer, this.options, &p); p.lexer.track_comments = this.options.features.minify_identifiers; - p.should_fold_typescript_constant_expressions = this.options.features.should_fold_typescript_constant_expressions; + // Instead of doing "should_fold_typescript_constant_expressions or features.minify_syntax" + // Let's enable this flag file-wide + if (p.options.features.minify_syntax or + p.options.features.inlining) + { + p.should_fold_typescript_constant_expressions = true; + } + defer p.lexer.deinit(); const result: js_ast.Result = undefined; _ = result; @@ -3006,7 +3078,7 @@ pub const Parser = struct { } break :brk .none; }; - return .{ .ast = try p.toAST(parts, exports_kind, .{ .none = {} }, "") }; + return .{ .ast = try p.toAST(parts, exports_kind, .none, "") }; } pub fn parse(self: *Parser) !js_ast.Result { @@ -3030,7 +3102,6 @@ pub const Parser = struct { pub fn analyze(self: *Parser, context: *anyopaque, callback: *const fn (*anyopaque, *TSXParser, []js_ast.Part) anyerror!void) anyerror!void { var p: TSXParser = undefined; try TSXParser.init(self.allocator, self.log, self.source, self.define, self.lexer, self.options, &p); - p.should_fold_typescript_constant_expressions = false; defer p.lexer.deinit(); @@ -3096,16 +3167,47 @@ pub const Parser = struct { } fn _parse(self: *Parser, comptime ParserType: type) !js_ast.Result { + const prev_action = bun.crash_handler.current_action; + defer bun.crash_handler.current_action = prev_action; + bun.crash_handler.current_action = .{ .parse = self.source.path.text }; + var p: ParserType = undefined; const orig_error_count = self.log.errors; try ParserType.init(self.allocator, self.log, self.source, self.define, self.lexer, self.options, &p); - p.should_fold_typescript_constant_expressions = self.options.features.should_fold_typescript_constant_expressions; + + if (p.options.features.hot_module_reloading) { + bun.assert(!p.options.tree_shaking); + } + + // Instead of doing "should_fold_typescript_constant_expressions or features.minify_syntax" + // Let's enable this flag file-wide + if (p.options.features.minify_syntax or + p.options.features.inlining) + { + p.should_fold_typescript_constant_expressions = true; + } + defer p.lexer.deinit(); - var binary_expression_stack_heap = std.heap.stackFallback(1024, bun.default_allocator); - p.binary_expression_stack = std.ArrayList(ParserType.BinaryExpressionVisitor).init(binary_expression_stack_heap.get()); + var binary_expression_stack_heap = std.heap.stackFallback(42 * @sizeOf(ParserType.BinaryExpressionVisitor), bun.default_allocator); + p.binary_expression_stack = std.ArrayList(ParserType.BinaryExpressionVisitor).initCapacity( + binary_expression_stack_heap.get(), + 41, // one less in case of unlikely alignment between the stack buffer and reality + ) catch unreachable; // stack allocation cannot fail defer p.binary_expression_stack.clearAndFree(); + var binary_expression_simplify_stack_heap = std.heap.stackFallback(48 * @sizeOf(SideEffects.BinaryExpressionSimplifyVisitor), bun.default_allocator); + p.binary_expression_simplify_stack = std.ArrayList(SideEffects.BinaryExpressionSimplifyVisitor).initCapacity( + binary_expression_simplify_stack_heap.get(), + 47, + ) catch unreachable; // stack allocation cannot fail + defer p.binary_expression_simplify_stack.clearAndFree(); + + if (Environment.allow_assert) { + bun.assert(binary_expression_stack_heap.fixed_buffer_allocator.ownsPtr(@ptrCast(p.binary_expression_stack.items))); + bun.assert(binary_expression_simplify_stack_heap.fixed_buffer_allocator.ownsPtr(@ptrCast(p.binary_expression_simplify_stack.items))); + } + // defer { // if (p.allocated_names_pool) |pool| { // pool.data = p.allocated_names; @@ -3162,6 +3264,8 @@ pub const Parser = struct { return error.SyntaxError; } + bun.crash_handler.current_action = .{ .visit = self.source.path.text }; + const visit_tracer = bun.tracy.traceNamed(@src(), "JSParser.visit"); try p.prepareForVisitPass(); @@ -3193,6 +3297,12 @@ pub const Parser = struct { before.deinit(); } + if (p.options.bundle) { + // The bundler requires a part for generated module wrappers. This + // part must be at the start as it is referred to by index. + before.append(js_ast.Part{}) catch bun.outOfMemory(); + } + // --inspect-brk if (p.options.features.set_breakpoint_on_first_line) { var debugger_stmts = try p.allocator.alloc(Stmt, 1); @@ -3204,14 +3314,7 @@ pub const Parser = struct { js_ast.Part{ .stmts = debugger_stmts, }, - ) catch unreachable; - } - - if (p.options.bundle) { - // allocate an empty part for the bundle - before.append( - js_ast.Part{}, - ) catch unreachable; + ) catch bun.outOfMemory(); } // When "using" declarations appear at the top level, we change all TDZ @@ -3251,6 +3354,11 @@ pub const Parser = struct { // that we're processing and expect to be able to access top-level variables. p.will_wrap_module_in_try_catch_for_using = p.shouldLowerUsingDeclarations(stmts); + // Bind symbols in a second pass over the AST. I started off doing this in a + // single pass, but it turns out it's pretty much impossible to do this + // correctly while handling arrow functions because of the grammar + // ambiguities. + // // Note that top-level lowered "using" declarations disable tree-shaking // because we only do tree-shaking on top-level statements and lowering // a top-level "using" declaration moves all top-level statements into a @@ -3259,6 +3367,34 @@ pub const Parser = struct { // When tree shaking is disabled, everything comes in a single part try p.appendPart(&parts, stmts); } else { + // Preprocess TypeScript enums to improve code generation. Otherwise + // uses of an enum before that enum has been declared won't be inlined: + // + // console.log(Foo.FOO) // We want "FOO" to be inlined here + // const enum Foo { FOO = 0 } + // + // The TypeScript compiler itself contains code with this pattern, so + // it's important to implement this optimization. + + var preprocessed_enums: std.ArrayListUnmanaged([]js_ast.Part) = .{}; + var preprocessed_enum_i: usize = 0; + if (p.scopes_in_order_for_enum.count() > 0) { + for (stmts) |*stmt| { + if (stmt.data == .s_enum) { + const old_scopes_in_order = p.scope_order_to_visit; + defer p.scope_order_to_visit = old_scopes_in_order; + + p.scope_order_to_visit = p.scopes_in_order_for_enum.get(stmt.loc).?; + + var enum_parts = ListManaged(js_ast.Part).init(p.allocator); + var sliced = try ListManaged(Stmt).initCapacity(p.allocator, 1); + sliced.appendAssumeCapacity(stmt.*); + try p.appendPart(&enum_parts, sliced.items); + try preprocessed_enums.append(p.allocator, enum_parts.items); + } + } + } + // When tree shaking is enabled, each top-level statement is potentially a separate part. for (stmts) |stmt| { switch (stmt.data) { @@ -3358,10 +3494,14 @@ pub const Parser = struct { parts.items.len -= 1; } }, + .s_enum => { + try parts.appendSlice(preprocessed_enums.items[preprocessed_enum_i]); + preprocessed_enum_i += 1; + p.scope_order_to_visit = p.scope_order_to_visit[1..]; + }, else => { var sliced = try ListManaged(Stmt).initCapacity(p.allocator, 1); - sliced.items.len = 1; - sliced.items[0] = stmt; + sliced.appendAssumeCapacity(stmt); try p.appendPart(&parts, sliced.items); }, } @@ -3441,7 +3581,7 @@ pub const Parser = struct { // https://github.com/lodash/lodash/issues/5660 var force_esm = false; - if (comptime FeatureFlags.unwrap_commonjs_to_esm) { + if (p.shouldUnwrapCommonJSToESM()) { if (p.imports_to_convert_from_require.items.len > 0) { const all_stmts = p.allocator.alloc(Stmt, p.imports_to_convert_from_require.items.len) catch unreachable; before.ensureUnusedCapacity(p.imports_to_convert_from_require.items.len) catch unreachable; @@ -3449,10 +3589,12 @@ pub const Parser = struct { var remaining_stmts = all_stmts; for (p.imports_to_convert_from_require.items) |deferred_import| { - var stmts_ = remaining_stmts[0..1]; + var import_part_stmts = remaining_stmts[0..1]; remaining_stmts = remaining_stmts[1..]; - stmts_[0] = Stmt.alloc( + p.module_scope.generated.push(p.allocator, deferred_import.namespace.ref.?) catch bun.outOfMemory(); + + import_part_stmts[0] = Stmt.alloc( S.Import, S.Import{ .star_name_loc = deferred_import.namespace.loc, @@ -3464,10 +3606,11 @@ pub const Parser = struct { var declared_symbols = DeclaredSymbol.List.initCapacity(p.allocator, 1) catch unreachable; declared_symbols.appendAssumeCapacity(.{ .ref = deferred_import.namespace.ref.?, .is_top_level = true }); before.appendAssumeCapacity(.{ - .stmts = stmts_, + .stmts = import_part_stmts, .declared_symbols = declared_symbols, .tag = .import_to_convert_from_require, - .can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(stmts_), + // This part has a single symbol, so it may be removed if unused. + .can_be_removed_if_unused = true, }); } bun.assert(remaining_stmts.len == 0); @@ -3745,7 +3888,7 @@ pub const Parser = struct { const uses_module_ref = p.symbols.items[p.module_ref.innerIndex()].use_count_estimate > 0; - var wrapper_expr: CommonJSWrapper = .{ .none = {} }; + var wrap_mode: WrapMode = .none; if (p.isDeoptimizedCommonJS()) { exports_kind = .cjs; @@ -3754,9 +3897,7 @@ pub const Parser = struct { } else if (uses_exports_ref or uses_module_ref or p.has_top_level_return or p.has_with_scope) { exports_kind = .cjs; if (p.options.features.commonjs_at_runtime) { - wrapper_expr = .{ - .bun_js = {}, - }; + wrap_mode = .bun_commonjs; const import_record: ?*const ImportRecord = brk: { for (p.import_records.items) |*import_record| { @@ -3803,28 +3944,6 @@ pub const Parser = struct { try p.log.addRangeErrorWithNotes(p.source, record.range, "Cannot use import statement with CommonJS-only features", notes.items); } - } else if (!p.options.bundle and !p.options.features.commonjs_at_runtime and (!p.options.transform_only or p.options.features.use_import_meta_require)) { - if (p.options.legacy_transform_require_to_import or p.options.features.use_import_meta_require) { - const args = p.allocator.alloc(Expr, 2) catch unreachable; - - if (p.runtime_imports.__exportDefault == null and p.has_export_default) { - p.runtime_imports.__exportDefault = try p.declareGeneratedSymbol(.other, "__exportDefault"); - p.resolveGeneratedSymbol(&p.runtime_imports.__exportDefault.?); - } - - wrapper_expr = .{ .bun_dev = p.callRuntime(logger.Loc.Empty, "__cJS2eSM", args) }; - p.resolveGeneratedSymbol(&p.runtime_imports.__cJS2eSM.?); - - // Disable HMR if we're wrapping it in CommonJS - // It's technically possible to support this. - // But we need to cut scope for the v0. - p.options.features.hot_module_reloading = false; - p.options.features.react_fast_refresh = false; - p.runtime_imports.__HMRModule = null; - p.runtime_imports.__FastRefreshModule = null; - p.runtime_imports.__FastRefreshRuntime = null; - p.runtime_imports.__HMRClient = null; - } } } else { switch (p.options.module_type) { @@ -3868,9 +3987,7 @@ pub const Parser = struct { } if (exports_kind == .cjs and p.options.features.commonjs_at_runtime) { - wrapper_expr = .{ - .bun_js = {}, - }; + wrap_mode = .bun_commonjs; } } @@ -4011,28 +4128,6 @@ pub const Parser = struct { } } - if (p.legacy_cjs_import_stmts.items.len > 0 and p.options.legacy_transform_require_to_import) { - var import_records = try bun.BabyList(u32).initCapacity(p.allocator, p.legacy_cjs_import_stmts.items.len); - var declared_symbols = DeclaredSymbol.List{}; - try declared_symbols.ensureTotalCapacity(p.allocator, p.legacy_cjs_import_stmts.items.len); - - for (p.legacy_cjs_import_stmts.items) |entry| { - const import_statement: *S.Import = entry.data.s_import; - import_records.appendAssumeCapacity(import_statement.import_record_index); - declared_symbols.appendAssumeCapacity(.{ - .ref = import_statement.namespace_ref, - .is_top_level = true, - }); - } - - before.append(js_ast.Part{ - .stmts = p.legacy_cjs_import_stmts.items, - .declared_symbols = declared_symbols, - .import_record_indices = import_records, - .tag = .cjs_imports, - }) catch unreachable; - } - if (p.has_called_runtime) { var runtime_imports: [RuntimeImports.all.len]u8 = undefined; var iter = p.runtime_imports.iter(); @@ -4100,6 +4195,10 @@ pub const Parser = struct { } } + if (p.react_refresh.register_used or p.react_refresh.signature_used) { + try p.generateReactRefreshImport(&before); + } + var parts_slice: []js_ast.Part = &([_]js_ast.Part{}); if (before.items.len > 0 or after.items.len > 0) { @@ -4153,7 +4252,7 @@ pub const Parser = struct { } } - return js_ast.Result{ .ast = try p.toAST(parts_slice, exports_kind, wrapper_expr, hashbang) }; + return js_ast.Result{ .ast = try p.toAST(parts_slice, exports_kind, wrap_mode, hashbang) }; } pub fn init(_options: Options, log: *logger.Log, source: *const logger.Source, define: *Define, allocator: Allocator) !Parser { @@ -4288,11 +4387,11 @@ pub const Prefill = struct { pub const Zero = Expr.Data{ .e_number = Value.Zero }; }; pub const Runtime = struct { - pub var JSXFilename = "__jsxFilename"; - pub var MarkAsModule = "__markAsModule"; - pub var CommonJS = "__commonJS"; - pub var ToModule = "__toModule"; - const JSXShortname = "jsx"; + // pub var JSXFilename = "__jsxFilename"; + // pub var MarkAsModule = "__markAsModule"; + // pub var CommonJS = "__commonJS"; + // pub var ToModule = "__toModule"; + // const JSXShortname = "jsx"; }; }; @@ -4318,62 +4417,10 @@ const JSXTransformType = enum { const ParserFeatures = struct { typescript: bool = false, - jsx: JSXTransformType = JSXTransformType.none, + jsx: JSXTransformType = .none, scan_only: bool = false, - - // *** How React Fast Refresh works *** - // - // Implementations: - // [0]: https://github.com/facebook/react/blob/master/packages/react-refresh/src/ReactFreshBabelPlugin.js - // [1]: https://github.com/swc-project/swc/blob/master/ecmascript/transforms/react/src/refresh/mod.rs - // - // Additional reading: - // - https://github.com/facebook/react/issues/16604#issuecomment-528663101 - // - https://github.com/facebook/react/blob/master/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js - // - // From reading[0] and Dan Abramov's comment, there are really five parts. - // 1. At the top of the file: - // 1. Declare a $RefreshReg$ if it doesn't exist - // - This really just does "RefreshRuntime.register(ComponentIdentifier, ComponentIdentifier.name);" - // 2. Run "var _s${componentIndex} = $RefreshSig$()" to generate a function for updating react refresh scoped to the component. So it's one per *component*. - // - This really just does "RefreshRuntime.createSignatureFunctionForTransform();" - // 2. Register all React components[2] defined in the module scope by calling the equivalent of $RefreshReg$(ComponentIdentifier, "ComponentName") - // 3. For each registered component: - // 1. Call "_s()" to mark the first render of this component for "react-refresh/runtime". Call this at the start of the React component's function body - // 2. Track every call expression to a hook[3] inside the component, including: - // - Identifier of the hook function - // - Arguments passed - // 3. For each hook's call expression, generate a signature key which is - // - The hook's identifier ref - // - The S.Decl ("VariableDeclarator")'s source - // "var [foo, bar] = useFooBar();" - // ^--------^ This region, I think. Judging from this line: https://github.com/facebook/react/blob/master/packages/react-refresh/src/ReactFreshBabelPlugin.js#L407 - // - For the "useState" hook, also hash the source of the first argument if it exists e.g. useState(foo => true); - // - For the "useReducer" hook, also hash the source of the second argument if it exists e.g. useReducer({}, () => ({})); - // 4. If the hook component is not builtin and is defined inside a component, always reset the component state - // - See this test: https://github.com/facebook/react/blob/568dc3532e25b30eee5072de08503b1bbc4f065d/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js#L909 - // 4. From the signature key generated in 3., call one of the following: - // - _s(ComponentIdentifier, hash(signature)); - // - _s(ComponentIdentifier, hash(signature), true /* forceReset */); - // - _s(ComponentIdentifier, hash(signature), false /* forceReset */, () => [customHook1, customHook2, customHook3]); - // Note: This step is only strictly required on rebuild. - // 5. if (isReactComponentBoundary(exports)) enqueueUpdateAndHandleErrors(); - // **** FAQ **** - // [2]: Q: From a parser's perspective, what's a component? - // A: typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z -- https://github.com/facebook/react/blob/568dc3532e25b30eee5072de08503b1bbc4f065d/packages/react-refresh/src/ReactFreshBabelPlugin.js#L42-L44 - // [3]: Q: From a parser's perspective, what's a hook? - // A: /^use[A-Z]/ -- https://github.com/facebook/react/blob/568dc3532e25b30eee5072de08503b1bbc4f065d/packages/react-refresh/src/ReactFreshBabelPlugin.js#L390 - // - // - // - // react_fast_refresh: bool = false, }; -// Our implementation diverges somewhat from the official implementation -// Specifically, we use a subclass of HMRModule - FastRefreshModule -// Instead of creating a globally-scoped -const FastRefresh = struct {}; - const ImportItemForNamespaceMap = bun.StringArrayHashMap(LocRef); pub const KnownGlobal = enum { @@ -4688,14 +4735,12 @@ fn NewParser_( is_file_considered_to_have_esm_exports: bool = false, - hmr_module: GeneratedSymbol = GeneratedSymbol{ .primary = Ref.None, .backup = Ref.None, .ref = Ref.None }, - has_called_runtime: bool = false, legacy_cjs_import_stmts: std.ArrayList(Stmt), injected_define_symbols: List(Ref) = .{}, - symbol_uses: js_ast.Part.SymbolUseMap = .{}, + symbol_uses: SymbolUseMap = .{}, declared_symbols: DeclaredSymbol.List = .{}, declared_symbols_for_reuse: DeclaredSymbol.List = .{}, runtime_imports: RuntimeImports = RuntimeImports{}, @@ -4706,6 +4751,7 @@ fn NewParser_( commonjs_named_exports: js_ast.Ast.CommonJSNamedExports = .{}, commonjs_named_exports_deoptimized: bool = false, + commonjs_module_exports_assigned_deoptimized: bool = false, commonjs_named_exports_needs_conversion: u32 = std.math.maxInt(u32), had_commonjs_named_exports_this_visit: bool = false, commonjs_replacement_stmts: StmtNodeList = &.{}, @@ -4713,7 +4759,7 @@ fn NewParser_( parse_pass_symbol_uses: ParsePassSymbolUsageType = undefined, /// Used by commonjs_at_runtime - commonjs_export_names: bun.StringArrayHashMapUnmanaged(void) = .{}, + has_commonjs_export_names: bool = false, /// When this flag is enabled, we attempt to fold all expressions that /// TypeScript would consider to be "constant expressions". This flag is @@ -4742,11 +4788,13 @@ fn NewParser_( /// we don't implement certain items in this list. For example, we don't do all /// number-to-string conversions since ours might differ from how JavaScript /// would do it, which would be a correctness issue. + /// + /// This flag is also set globally when minify_syntax is enabled, in which this means + /// we always fold constant expressions. should_fold_typescript_constant_expressions: bool = false, emitted_namespace_vars: RefMap = RefMap{}, is_exported_inside_namespace: RefRefMap = .{}, - known_enum_values: Map(Ref, StringHashMapUnmanaged(f64)) = .{}, local_type_names: StringBoolMap = StringBoolMap{}, // This is the reference to the generated function argument for the namespace, @@ -4772,13 +4820,11 @@ fn NewParser_( jsx_automatic: GeneratedSymbol = GeneratedSymbol{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None }, jsxs_runtime: GeneratedSymbol = GeneratedSymbol{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None }, jsx_classic: GeneratedSymbol = GeneratedSymbol{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None }, - jsx_imports: JSXImport.Symbols = .{}, - // only applicable when is_react_fast_refresh_enabled - jsx_refresh_runtime: GeneratedSymbol = GeneratedSymbol{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None }, - - bun_jsx_ref: Ref = Ref.None, + // only applicable when `.options.features.react_fast_refresh` is set. + // populated before visit pass starts. + react_refresh: ReactRefresh = .{}, jest: Jest = .{}, @@ -4786,6 +4832,7 @@ fn NewParser_( import_records: ImportRecordList, import_records_for_current_part: List(u32) = .{}, export_star_import_records: List(u32) = .{}, + import_symbol_property_uses: SymbolPropertyUseMap = .{}, // These are for handling ES6 imports and exports esm_import_keyword: logger.Range = logger.Range.None, @@ -4820,6 +4867,7 @@ fn NewParser_( // symbols to handle declaring a hoisted "var" symbol in a nested scope and // binding a name to it in a parent or sibling scope. scopes_in_order: ScopeOrderList = .{}, + scope_order_to_visit: []ScopeOrder = &.{}, // These properties are for the visit pass, which runs after the parse pass. // The visit pass binds identifiers to declared symbols, does constant @@ -4830,6 +4878,7 @@ fn NewParser_( delete_target: Expr.Data, loop_body: Stmt.Data, module_scope: *js_ast.Scope = undefined, + module_scope_directive_loc: logger.Loc = .{}, is_control_flow_dead: bool = false, /// We must be careful to avoid revisiting nodes that have scopes. @@ -4934,46 +4983,205 @@ fn NewParser_( require_transposer: RequireTransposer, require_resolve_transposer: RequireResolveTransposer, - // This is a general place to put lots of Expr objects - expr_list: List(Expr) = .{}, - - scope_order_to_visit: []ScopeOrder = &([_]ScopeOrder{}), - const_values: js_ast.Ast.ConstValuesMap = .{}, - binary_expression_stack: std.ArrayList(BinaryExpressionVisitor) = undefined, + // These are backed by stack fallback allocators in _parse, and are uninitialized until then. + binary_expression_stack: ListManaged(BinaryExpressionVisitor) = undefined, + binary_expression_simplify_stack: ListManaged(SideEffects.BinaryExpressionSimplifyVisitor) = undefined, + + /// We build up enough information about the TypeScript namespace hierarchy to + /// be able to resolve scope lookups and property accesses for TypeScript enum + /// and namespace features. Each JavaScript scope object inside a namespace + /// has a reference to a map of exported namespace members from sibling scopes. + /// + /// In addition, there is a map from each relevant symbol reference to the data + /// associated with that namespace or namespace member: "ref_to_ts_namespace_member". + /// This gives enough info to be able to resolve queries into the namespace. + ref_to_ts_namespace_member: std.AutoHashMapUnmanaged(Ref, js_ast.TSNamespaceMember.Data) = .{}, + /// When visiting expressions, namespace metadata is associated with the most + /// recently visited node. If namespace metadata is present, "tsNamespaceTarget" + /// will be set to the most recently visited node (as a way to mark that this + /// node has metadata) and "tsNamespaceMemberData" will be set to the metadata. + ts_namespace: RecentlyVisitedTSNamespace = .{}, + top_level_enums: std.ArrayListUnmanaged(Ref) = .{}, + + scopes_in_order_for_enum: std.AutoArrayHashMapUnmanaged(logger.Loc, []ScopeOrder) = .{}, // If this is true, then all top-level statements are wrapped in a try/catch will_wrap_module_in_try_catch_for_using: bool = false, - /// use this instead of checking p.source.index - /// because when not bundling, p.source.index is `0` - inline fn isSourceRuntime(p: *const P) bool { - return p.options.bundle and p.source.index.isRuntime(); - } + /// Used for react refresh, it must be able to insert `const _s = $RefreshSig$();` + nearest_stmt_list: ?*ListManaged(Stmt) = null, - pub fn transposeImport(p: *P, arg: Expr, state: anytype) Expr { - // The argument must be a string - if (@as(Expr.Tag, arg.data) == .e_string) { - // Ignore calls to import() if the control flow is provably dead here. - // We don't want to spend time scanning the required files if they will - // never be used. - if (p.is_control_flow_dead) { - return p.newExpr(E.Null{}, arg.loc); - } + const RecentlyVisitedTSNamespace = struct { + expr: Expr.Data = Expr.empty.data, + map: ?*js_ast.TSNamespaceMemberMap = null, - const import_record_index = p.addImportRecord(.dynamic, arg.loc, arg.data.e_string.slice(p.allocator)); + const ExpressionData = union(enum) { + ref: Ref, + ptr: *E.Dot, + }; + }; + + /// "Fast Refresh" is React's solution for hot-module-reloading in the context of the UI framework + /// user guide: https://reactnative.dev/docs/fast-refresh (applies to react-dom and native) + /// + /// This depends on performing a couple extra transformations at bundle time, as well as + /// including the `react-refresh` NPM package, which is able to do the heavy lifting, + /// integrating with `react` and `react-dom`. + /// + /// Prior implementations: + /// [1]: https://github.com/facebook/react/blob/main/packages/react-refresh/src/ReactFreshBabelPlugin.js + /// [2]: https://github.com/swc-project/swc/blob/main/crates/swc_ecma_transforms_react/src/refresh/mod.rs + /// + /// Additional reading: + /// [3] https://github.com/facebook/react/issues/16604#issuecomment-528663101 + /// [4] https://github.com/facebook/react/blob/master/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js + /// + /// Instead of a plugin which visits the tree separately, Bun's implementation of fast refresh + /// happens in tandem with the visit pass. The responsibilities of the transform are as follows: + /// + /// 1. For all Components (which is defined as any top-level function/function variable, that is + /// named with a capital letter; see `isComponentishName`), register them to the runtime using + /// `$RefreshReg$(ComponentFunction, "Component");`. Implemented in `p.handleReactRefreshRegister` + /// HOC components are also registered, but only through a special case for `export default` + /// + /// 2. For all functions which call a Hook (a hook is an identifier matching /^use[A-Z]/): + /// a. Outside of the function, create a signature function `const _s = $RefreshSig$();` + /// b. At the start of the function, call `_s()` + /// c. Record all of the hooks called, the variables they are assigned to, and + /// arguments depending on which hook has been used. `useState` and `useReducer`, + /// for example, are special-cased. + /// d. Directly after the function, call `_s(hook, "", forceReset)` + /// - If a user-defined hook is called, the alterate form is used: + /// `_s(hook, "", forceReset, () => [useCustom1, useCustom2])` + /// + /// The upstream transforms do not declare `$RefreshReg$` or `$RefreshSig$`. A typical + /// implementation might look like this, prepending this data to the module start: + /// + /// import * as Refresh from 'react-refresh/runtime'; + /// const $RefreshReg$ = (type, id) => Refresh.register(type, "" + id); + /// const $RefreshSig$ = Refresh.createSignatureFunctionForTransform; + /// + /// Since Bun is a transpiler *and* bundler, we take a slightly different approach. Aside + /// from including the link to the refresh runtime, our notation of $RefreshReg$ is just + /// pointing at `Refresh.register`, which means when we call it, the second argument has + /// to be a string containing the filepath, not just the component name. + const ReactRefresh = struct { + // Set if this JSX/TSX file uses the refresh runtime. If so, + // we must insert an import statement to it. + register_used: bool = false, + signature_used: bool = false, + + /// $RefreshReg$ is called on all top-level variables that are + /// components, as well as HOCs found in the `export default` clause. + register_ref: Ref = Ref.None, + + /// $RefreshSig$ is called to create a signature function, which is + /// used by the refresh runtime to perform smart hook tracking. + create_signature_ref: Ref = Ref.None, + + /// If a comment with '@refresh reset' is seen, we will forward a + /// force refresh to the refresh runtime. This lets you reset the + /// state of hooks on an update on a per-component basis. + // TODO: this is never set + force_reset: bool = false, + + /// The last hook that was scanned. This is used when visiting + /// `.s_local`, as we must hash the variable destructure if the + /// hook's result is assigned directly to a local. + last_hook_seen: ?*E.Call = null, + + /// Every function sets up stack memory to hold data related to it's + /// hook tracking. This is a pointer to that ?HookContext, where an + /// inner null means there are no hook calls. + /// + /// The inner value is initialized when the first hook .e_call is + /// visited, where the '_s' symbol is reserved. Additional hook calls + /// append to the `hasher` and `user_hooks` as needed. + /// + /// When a function is done visiting, the stack location is checked, + /// and then it will insert `var _s = ...`, add the `_s()` call at + /// the start of the function, and then add the call to `_s(func, ...)`. + hook_ctx_storage: ?*?HookContext = null, + + pub const HookContext = struct { + hasher: std.hash.Wyhash, + signature_cb: Ref, + user_hooks: std.AutoArrayHashMapUnmanaged(Ref, Expr), + }; + + // https://github.com/facebook/react/blob/d1afcb43fd506297109c32ff462f6f659f9110ae/packages/react-refresh/src/ReactFreshBabelPlugin.js#L42 + pub fn isComponentishName(id: []const u8) bool { + if (id.len == 0) return false; + return switch (id[0]) { + 'A'...'Z' => true, + else => false, + }; + } + + // https://github.com/facebook/react/blob/d1afcb43fd506297109c32ff462f6f659f9110ae/packages/react-refresh/src/ReactFreshBabelPlugin.js#L408 + pub fn isHookName(id: []const u8) bool { + return id.len >= 4 and + strings.hasPrefixComptime(id, "use") and + switch (id[3]) { + 'A'...'Z' => true, + else => false, + }; + } + + pub const built_in_hooks = bun.ComptimeEnumMap(enum { + useState, + useReducer, + useEffect, + useLayoutEffect, + useMemo, + useCallback, + useRef, + useContext, + useImperativeHandle, + useDebugValue, + useId, + useDeferredValue, + useTransition, + useInsertionEffect, + useSyncExternalStore, + useFormStatus, + useFormState, + useActionState, + useOptimistic, + }); + }; + + /// use this instead of checking p.source.index + /// because when not bundling, p.source.index is `0` + inline fn isSourceRuntime(p: *const P) bool { + return p.options.bundle and p.source.index.isRuntime(); + } + + pub fn transposeImport(p: *P, arg: Expr, state: *const TransposeState) Expr { + // The argument must be a string + if (arg.data.as(.e_string)) |str| { + // Ignore calls to import() if the control flow is provably dead here. + // We don't want to spend time scanning the required files if they will + // never be used. + if (p.is_control_flow_dead) { + return p.newExpr(E.Null{}, arg.loc); + } + + const import_record_index = p.addImportRecord(.dynamic, arg.loc, str.slice(p.allocator)); + + if (state.import_record_tag) |tag| { + p.import_records.items[import_record_index].tag = tag; + } + + p.import_records.items[import_record_index].handles_import_errors = (state.is_await_target and p.fn_or_arrow_data_visit.try_body_count != 0) or state.is_then_catch_target; + p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable; - if (state.type_attribute.tag() != .none) { - p.import_records.items[import_record_index].tag = state.type_attribute.tag(); - } - p.import_records.items[import_record_index].handles_import_errors = (state.is_await_target and p.fn_or_arrow_data_visit.try_body_count != 0) or state.is_then_catch_target; - p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable; return p.newExpr(E.Import{ .expr = arg, - .import_record_index = Ref.toInt(import_record_index), - .type_attribute = state.type_attribute, - // .leading_interior_comments = arg.getString(). + .import_record_index = @intCast(import_record_index), + .options = state.import_options, }, state.loc); } @@ -4985,12 +5193,12 @@ fn NewParser_( return p.newExpr(E.Import{ .expr = arg, + .options = state.import_options, .import_record_index = std.math.maxInt(u32), - .type_attribute = state.type_attribute, }, state.loc); } - pub fn transposeRequireResolve(p: *P, arg: Expr, require_resolve_ref: anytype) Expr { + pub fn transposeRequireResolve(p: *P, arg: Expr, require_resolve_ref: Expr) Expr { // The argument must be a string if (arg.data == .e_string) { return p.transposeRequireResolveKnownString(arg); @@ -5027,14 +5235,14 @@ fn NewParser_( return p.newExpr( E.RequireResolveString{ - .import_record_index = Ref.toInt(import_record_index), + .import_record_index = import_record_index, // .leading_interior_comments = arg.getString(). }, arg.loc, ); } - pub fn transposeRequire(p: *P, arg: Expr, state: anytype) Expr { + pub fn transposeRequire(p: *P, arg: Expr, state: *const TransposeState) Expr { if (!p.options.features.allow_runtime) { const args = p.allocator.alloc(Expr, 1) catch bun.outOfMemory(); args[0] = arg; @@ -5062,23 +5270,24 @@ fn NewParser_( const handles_import_errors = p.fn_or_arrow_data_visit.try_body_count != 0; - if ( // For unwrapping CommonJS into ESM to fully work // we must also unwrap requires into imports. - (p.unwrap_all_requires or p.options.features.shouldUnwrapRequire(path.packageName() orelse "")) and - + const should_unwrap_require = !p.options.features.hot_module_reloading and + (p.unwrap_all_requires or + if (path.packageName()) |pkg| p.options.features.shouldUnwrapRequire(pkg) else false) and // We cannot unwrap a require wrapped in a try/catch because // import statements cannot be wrapped in a try/catch and // require cannot return a promise. - !handles_import_errors) - { + !handles_import_errors; + + if (should_unwrap_require) { const import_record_index = p.addImportRecordByRangeAndPath(.stmt, p.source.rangeOfString(arg.loc), path); p.import_records.items[import_record_index].handles_import_errors = handles_import_errors; // Note that this symbol may be completely removed later. - var path_name = fs.PathName.init(strings.append(p.allocator, "import_", path.text) catch unreachable); - const name = path_name.nonUniqueNameString(p.allocator) catch unreachable; - const namespace_ref = p.newSymbol(.other, name) catch unreachable; + var path_name = fs.PathName.init(path.text); + const name = path_name.nonUniqueNameString(p.allocator) catch bun.outOfMemory(); + const namespace_ref = p.newSymbol(.other, name) catch bun.outOfMemory(); p.imports_to_convert_from_require.append(p.allocator, .{ .namespace = .{ @@ -5086,8 +5295,8 @@ fn NewParser_( .loc = arg.loc, }, .import_record_id = import_record_index, - }) catch unreachable; - p.import_items_for_namespace.put(p.allocator, namespace_ref, ImportItemForNamespaceMap.init(p.allocator)) catch unreachable; + }) catch bun.outOfMemory(); + p.import_items_for_namespace.put(p.allocator, namespace_ref, ImportItemForNamespaceMap.init(p.allocator)) catch bun.outOfMemory(); p.recordUsage(namespace_ref); if (!state.is_require_immediately_assigned_to_decl) { @@ -5109,60 +5318,7 @@ fn NewParser_( p.import_records.items[import_record_index].handles_import_errors = handles_import_errors; p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable; - if (!p.options.legacy_transform_require_to_import) { - return p.newExpr(E.RequireString{ .import_record_index = import_record_index }, arg.loc); - } - - p.import_records.items[import_record_index].was_originally_require = true; - p.import_records.items[import_record_index].contains_import_star = true; - - const symbol_name = p.import_records.items[import_record_index].path.name.nonUniqueNameString(p.allocator) catch unreachable; - const hash_value = @as( - u16, - @truncate(bun.hash(p.import_records.items[import_record_index].path.text)), - ); - - const cjs_import_name = std.fmt.allocPrint( - p.allocator, - "{s}_{any}_{d}", - .{ - symbol_name, - bun.fmt.hexIntLower(hash_value), - p.legacy_cjs_import_stmts.items.len, - }, - ) catch unreachable; - - const namespace_ref = p.declareSymbol(.hoisted, arg.loc, cjs_import_name) catch unreachable; - - p.legacy_cjs_import_stmts.append( - p.s( - S.Import{ - .namespace_ref = namespace_ref, - .star_name_loc = arg.loc, - .is_single_line = true, - .import_record_index = import_record_index, - }, - arg.loc, - ), - ) catch unreachable; - - const args = p.allocator.alloc(Expr, 1) catch unreachable; - args[0] = p.newExpr( - E.ImportIdentifier{ - .ref = namespace_ref, - }, - arg.loc, - ); - - // require(import_object_assign) - p.recordUsageOfRuntimeRequire(); - return p.newExpr( - E.Call{ - .target = p.valueForRequire(arg.loc), - .args = ExprNodeList.init(args), - }, - arg.loc, - ); + return p.newExpr(E.RequireString{ .import_record_index = import_record_index }, arg.loc); }, else => { p.recordUsageOfRuntimeRequire(); @@ -5179,6 +5335,11 @@ fn NewParser_( } } + pub fn shouldUnwrapCommonJSToESM(p: *P) bool { + // hot module loading opts out of this because we want to produce a cjs bundle at the end + return FeatureFlags.unwrap_commonjs_to_esm and !p.options.features.hot_module_reloading; + } + fn isBindingUsed(p: *P, binding: Binding, default_export_ref: Ref) bool { switch (binding.data) { .b_identifier => |ident| { @@ -5212,10 +5373,6 @@ fn NewParser_( return false; }, - .b_property => |prop| { - return p.isBindingUsed(prop.value, default_export_ref); - }, - .b_missing => return false, } } @@ -5350,9 +5507,9 @@ fn NewParser_( } } - const ImportTransposer = ExpressionTransposer(P, P.transposeImport); - const RequireTransposer = ExpressionTransposer(P, P.transposeRequire); - const RequireResolveTransposer = ExpressionTransposer(P, P.transposeRequireResolve); + const ImportTransposer = ExpressionTransposer(P, *const TransposeState, P.transposeImport); + const RequireTransposer = ExpressionTransposer(P, *const TransposeState, P.transposeRequire); + const RequireResolveTransposer = ExpressionTransposer(P, Expr, P.transposeRequireResolve); const Binding2ExprWrapper = struct { pub const Namespace = Binding.ToExpr(P, P.wrapIdentifierNamespace); @@ -5370,20 +5527,13 @@ fn NewParser_( const declared_refs = part.declared_symbols.refs(); for (declared_refs) |declared| { symbols[declared.innerIndex()].use_count_estimate = 0; - // } } } pub fn s(_: *P, t: anytype, loc: logger.Loc) Stmt { const Type = @TypeOf(t); - comptime { - if (!is_typescript_enabled and (Type == S.TypeScript or Type == *S.TypeScript)) { - @compileError("Attempted to use TypeScript syntax in a non-TypeScript environment"); - } - } - if (!is_typescript_enabled and (Type == S.TypeScript or Type == *S.TypeScript)) { - unreachable; + @compileError("Attempted to use TypeScript syntax in a non-TypeScript environment"); } // Output.print("\nStmt: {s} - {d}\n", .{ @typeName(@TypeOf(t)), loc.start }); @@ -5520,24 +5670,6 @@ fn NewParser_( } } - pub fn deinit(parser: *P) void { - parser.allocated_names.deinit(); - parser.scopes_for_current_part.deinit(); - parser.symbols.deinit(); - parser.ts_use_counts.deinit(); - parser.declared_symbols.deinit(); - parser.known_enum_values.deinit(); - parser.import_records.deinit(); - parser.import_records_for_current_part.deinit(); - parser.export_star_import_records.deinit(); - parser.import_items_for_namespace.deinit(); - parser.named_imports.deinit(); - parser.import_namespace_cc_map.deinit(); - parser.scopes_in_order.deinit(); - parser.temp_refs_to_declare.deinit(); - parser.relocated_top_level_vars.deinit(); - } - pub fn findSymbol(p: *P, loc: logger.Loc, name: string) !FindSymbolResult { return findSymbolWithRecordUsage(p, loc, name, true); } @@ -5552,22 +5684,21 @@ fn NewParser_( const allocator = p.allocator; const ref: Ref = brk: { - var _scope: ?*Scope = p.current_scope; - - var did_forbid_argumen = false; + var current: ?*Scope = p.current_scope; - while (_scope) |scope| : (_scope = _scope.?.parent) { + var did_forbid_arguments = false; + while (current) |scope| : (current = current.?.parent) { // Track if we're inside a "with" statement body if (scope.kind == .with) { is_inside_with_scope = true; } // Forbid referencing "arguments" inside class bodies - if (scope.forbid_arguments and !did_forbid_argumen and strings.eqlComptime(name, "arguments")) { + if (scope.forbid_arguments and !did_forbid_arguments and strings.eqlComptime(name, "arguments")) { const r = js_lexer.rangeOfIdentifier(p.source, loc); p.log.addRangeErrorFmt(p.source, r, allocator, "Cannot access \"{s}\" here", .{name}) catch unreachable; - did_forbid_argumen = true; + did_forbid_arguments = true; } // Is the symbol a member of this scope? @@ -5575,6 +5706,29 @@ fn NewParser_( declare_loc = member.loc; break :brk member.ref; } + + // Is the symbol a member of this scope's TypeScript namespace? + if (scope.ts_namespace) |ts_namespace| { + if (ts_namespace.exported_members.get(name)) |member| { + if (member.data.isEnum() == ts_namespace.is_enum_scope) { + declare_loc = member.loc; + // If this is an identifier from a sibling TypeScript namespace, then we're + // going to have to generate a property access instead of a simple reference. + // Lazily-generate an identifier that represents this property access. + const gop = try ts_namespace.property_accesses.getOrPut(p.allocator, name); + if (!gop.found_existing) { + const ref = try p.newSymbol(.other, name); + gop.value_ptr.* = ref; + p.symbols.items[ref.inner_index].namespace_alias = .{ + .namespace_ref = ts_namespace.arg_ref, + .alias = name, + }; + break :brk ref; + } + break :brk gop.value_ptr.*; + } + } + } } // Allocate an "unbound" symbol @@ -5640,9 +5794,6 @@ fn NewParser_( p.recordExportedBinding(prop.value); } }, - else => { - p.panic("Unexpected binding export type {any}", .{binding}); - }, } } @@ -5723,7 +5874,7 @@ fn NewParser_( } /// This function is very very hot. - pub fn handleIdentifier(p: *P, loc: logger.Loc, ident: E.Identifier, _original_name: ?string, opts: IdentifierOpts) Expr { + pub fn handleIdentifier(p: *P, loc: logger.Loc, ident: E.Identifier, original_name: ?string, opts: IdentifierOpts) Expr { const ref = ident.ref; if (p.options.features.inlining) { @@ -5733,18 +5884,58 @@ fn NewParser_( } } + // Create an error for assigning to an import namespace if ((opts.assign_target != .none or opts.is_delete_target) and p.symbols.items[ref.innerIndex()].kind == .import) { - // Create an error for assigning to an import namespace const r = js_lexer.rangeOfIdentifier(p.source, loc); p.log.addRangeErrorFmt(p.source, r, p.allocator, "Cannot assign to import \"{s}\"", .{ p.symbols.items[ref.innerIndex()].original_name, }) catch unreachable; } - // TODO: TypeScript namespace - // if (opts.assign_target == .none and !opts.is_delete_target and p.options.bundle) { + // Substitute an EImportIdentifier now if this has a namespace alias + if (opts.assign_target == .none and !opts.is_delete_target) { + const symbol = &p.symbols.items[ref.inner_index]; + if (symbol.namespace_alias) |ns_alias| { + if (p.ref_to_ts_namespace_member.get(ns_alias.namespace_ref)) |ts_member_data| { + if (ts_member_data == .namespace) { + if (ts_member_data.namespace.get(ns_alias.alias)) |member| { + switch (member.data) { + .enum_number => |num| return p.wrapInlinedEnum( + .{ .loc = loc, .data = .{ .e_number = .{ .value = num } } }, + p.symbols.items[ref.inner_index].original_name, + ), - // } + .enum_string => |str| return p.wrapInlinedEnum( + .{ .loc = loc, .data = .{ .e_string = str } }, + p.symbols.items[ref.inner_index].original_name, + ), + + .namespace => |map| { + const expr = p.newExpr(E.Dot{ + .target = p.newExpr(E.Identifier.init(ns_alias.namespace_ref), loc), + .name = ns_alias.alias, + .name_loc = loc, + }, loc); + p.ts_namespace = .{ + .expr = expr.data, + .map = map, + }; + return expr; + }, + + else => {}, + } + } + } + } + + return p.newExpr(E.Dot{ + .target = p.newExpr(E.Identifier.init(ns_alias.namespace_ref), loc), + .name = ns_alias.alias, + .name_loc = loc, + }, loc); + } + } // Substitute an EImportIdentifier now if this is an import item if (p.is_import_item.contains(ref)) { @@ -5754,36 +5945,67 @@ fn NewParser_( ); } - // Substitute a namespace export reference now if appropriate if (is_typescript_enabled) { - if (p.is_exported_inside_namespace.get(ref)) |ns_ref| { - const name = p.symbols.items[ref.innerIndex()].original_name; + if (p.ref_to_ts_namespace_member.get(ref)) |member_data| { + switch (member_data) { + .enum_number => |num| return p.wrapInlinedEnum( + .{ .loc = loc, .data = .{ .e_number = .{ .value = num } } }, + p.symbols.items[ref.inner_index].original_name, + ), - // If this is a known enum value, inline the value of the enum - if (p.known_enum_values.get(ns_ref)) |enum_values| { - if (enum_values.get(name)) |number| { - return p.newExpr(E.Number{ .value = number }, loc); - } + .enum_string => |str| return p.wrapInlinedEnum( + .{ .loc = loc, .data = .{ .e_string = str } }, + p.symbols.items[ref.inner_index].original_name, + ), + + .namespace => |map| { + const expr: Expr = .{ + .data = .{ .e_identifier = ident }, + .loc = loc, + }; + + p.ts_namespace = .{ + .expr = expr.data, + .map = map, + }; + + return expr; + }, + + else => {}, } + } + + // Substitute a namespace export reference now if appropriate + if (p.is_exported_inside_namespace.get(ref)) |ns_ref| { + const name = p.symbols.items[ref.innerIndex()].original_name; - // Otherwise, create a property access on the namespace p.recordUsage(ns_ref); + const prop = p.newExpr(E.Dot{ + .target = p.newExpr(E.Identifier.init(ns_ref), loc), + .name = name, + .name_loc = loc, + }, loc); + + if (p.ts_namespace.expr == .e_identifier and + p.ts_namespace.expr.e_identifier.ref.eql(ident.ref)) + { + p.ts_namespace.expr = prop.data; + } - return p.newExpr(E.Dot{ .target = p.newExpr(E.Identifier{ .ref = ns_ref }, loc), .name = name, .name_loc = loc }, loc); + return prop; } } - if (_original_name) |original_name| { - const result = p.findSymbol(loc, original_name) catch unreachable; - var _ident = ident; - _ident.ref = result.ref; - return p.newExpr(_ident, loc); + if (original_name) |name| { + const result = p.findSymbol(loc, name) catch unreachable; + var id_clone = ident; + id_clone.ref = result.ref; + return p.newExpr(id_clone, loc); } - return Expr{ - .data = .{ - .e_identifier = ident, - }, + return .{ + .data = .{ .e_identifier = ident }, .loc = loc, }; } @@ -5843,6 +6065,7 @@ fn NewParser_( .namespace_ref = namespace_ref, .items = clause_items, .import_record_index = import_record_i, + .is_single_line = true, }, logger.Loc{}, ); @@ -5853,8 +6076,9 @@ fn NewParser_( var import_records = try allocator.alloc(@TypeOf(import_record_i), 1); import_records[0] = import_record_i; - // Append a single import to the end of the file (ES6 imports are hoisted - // so we don't need to worry about where the import statement goes) + // This import is placed in a part before the main code, however + // the bundler ends up re-ordering this to be after... The order + // does not matter as ESM imports are always hoisted. parts.append(js_ast.Part{ .stmts = stmts, .declared_symbols = declared_symbols, @@ -5863,6 +6087,103 @@ fn NewParser_( }) catch unreachable; } + pub fn generateReactRefreshImport(p: *P, parts: *ListManaged(js_ast.Part)) !void { + switch (p.options.features.hot_module_reloading) { + inline else => |hmr| try p.generateReactRefreshImportHmr(parts, hmr), + } + } + + fn generateReactRefreshImportHmr(p: *P, parts: *ListManaged(js_ast.Part), comptime hot_module_reloading: bool) !void { + // If `hot_module_reloading`, we are going to generate a require call: + // + // const { $RefreshSig$, $RefreshReg$ } = require("react-refresh/runtime")` + // + // Otherwise we are going to settle on an import statement. Using + // require is fine in HMR bundling because `react-refresh` itself is + // already a CommonJS module, and it will actually be more efficient + // at runtime this way. + const allocator = p.allocator; + const import_record_index = p.addImportRecordByRange(.stmt, logger.Range.None, "react-refresh/runtime"); + + const Item = if (hot_module_reloading) B.Object.Property else js_ast.ClauseItem; + + const len = 1 + @as(usize, @intFromBool(p.react_refresh.register_used)) + + @as(usize, @intFromBool(p.react_refresh.signature_used)); + var items = try List(Item).initCapacity(allocator, len); + + const stmts = try allocator.alloc(Stmt, 1); + var declared_symbols = DeclaredSymbol.List{}; + try declared_symbols.ensureTotalCapacity(allocator, len); + + const namespace_ref = try p.newSymbol(.other, "RefreshRuntime"); + declared_symbols.appendAssumeCapacity(.{ + .ref = namespace_ref, + .is_top_level = true, + }); + try p.module_scope.generated.push(allocator, namespace_ref); + + inline for (.{ + .{ + .name = "register", + .enabled = p.react_refresh.register_used, + .ref = p.react_refresh.register_ref, + }, + .{ + .name = "createSignatureFunctionForTransform", + .enabled = p.react_refresh.signature_used, + .ref = p.react_refresh.create_signature_ref, + }, + }) |entry| { + if (entry.enabled) { + items.appendAssumeCapacity(if (hot_module_reloading) .{ + .key = p.newExpr(E.String{ .data = entry.name }, logger.Loc.Empty), + .value = p.b(B.Identifier{ .ref = entry.ref }, logger.Loc.Empty), + } else .{ + .alias = entry.name, + .original_name = entry.name, + .alias_loc = logger.Loc{}, + .name = LocRef{ .ref = entry.ref, .loc = logger.Loc{} }, + }); + declared_symbols.appendAssumeCapacity(.{ .ref = entry.ref, .is_top_level = true }); + try p.module_scope.generated.push(allocator, entry.ref); + try p.is_import_item.put(allocator, entry.ref, {}); + try p.named_imports.put(entry.ref, .{ + .alias = entry.name, + .alias_loc = logger.Loc.Empty, + .namespace_ref = namespace_ref, + .import_record_index = import_record_index, + }); + } + } + + stmts[0] = p.s(if (hot_module_reloading) + S.Local{ + .kind = .k_const, + .decls = try Decl.List.fromSlice(p.allocator, &.{.{ + .binding = p.b(B.Object{ + .properties = items.items, + }, logger.Loc.Empty), + .value = p.newExpr(E.RequireString{ + .import_record_index = import_record_index, + }, logger.Loc.Empty), + }}), + } + else + S.Import{ + .namespace_ref = namespace_ref, + .items = items.items, + .import_record_index = import_record_index, + .is_single_line = false, + }, logger.Loc.Empty); + + try parts.append(.{ + .stmts = stmts, + .declared_symbols = declared_symbols, + .import_record_indices = try bun.BabyList(u32).fromSlice(allocator, &.{import_record_index}), + .tag = .runtime, + }); + } + fn substituteSingleUseSymbolInStmt(p: *P, stmt: Stmt, ref: Ref, replacement: Expr) bool { const expr: *Expr = brk: { switch (stmt.data) { @@ -6415,12 +6736,8 @@ fn NewParser_( var generated_symbols_count: u32 = 3; - if (p.options.features.hot_module_reloading) { + if (p.options.features.react_fast_refresh) { generated_symbols_count += 3; - - if (p.options.features.react_fast_refresh) { - generated_symbols_count += 1; - } } if (is_jsx_enabled) { @@ -6451,27 +6768,10 @@ fn NewParser_( p.jest.afterAll = try p.declareCommonJSSymbol(.unbound, "afterAll"); } - if (p.options.features.hot_module_reloading) { - p.hmr_module = try p.declareGeneratedSymbol(.other, "hmr"); - if (p.options.features.react_fast_refresh) { - if (p.options.jsx.use_embedded_refresh_runtime) { - p.runtime_imports.__FastRefreshRuntime = try p.declareGeneratedSymbol(.other, "__FastRefreshRuntime"); - p.recordUsage(p.runtime_imports.__FastRefreshRuntime.?.ref); - p.jsx_refresh_runtime = p.runtime_imports.__FastRefreshRuntime.?; - } else { - p.jsx_refresh_runtime = try p.declareGeneratedSymbol(.other, "Refresher"); - } - - p.runtime_imports.__FastRefreshModule = try p.declareGeneratedSymbol(.other, "__FastRefreshModule"); - p.recordUsage(p.runtime_imports.__FastRefreshModule.?.ref); - } else { - p.runtime_imports.__HMRModule = try p.declareGeneratedSymbol(.other, "__HMRModule"); - p.recordUsage(p.runtime_imports.__HMRModule.?.ref); - } - - p.runtime_imports.__HMRClient = try p.declareGeneratedSymbol(.other, "__HMRClient"); - p.recordUsage(p.hmr_module.ref); - p.recordUsage(p.runtime_imports.__HMRClient.?.ref); + if (p.options.features.react_fast_refresh) { + // this is .. obviously.. not correct + p.react_refresh.create_signature_ref = (try p.declareGeneratedSymbol(.other, "$RefreshSig$")).primary; + p.react_refresh.register_ref = (try p.declareGeneratedSymbol(.other, "$RefreshReg$")).primary; } // "React.createElement" and "createElement" become: @@ -6479,7 +6779,6 @@ fn NewParser_( // "Foo.Bar.createElement" becomes: // import { Bar } from 'foo'; // Usages become Bar.createElement - switch (comptime jsx_transform_type) { .react => { if (!p.options.bundle) { @@ -6522,10 +6821,11 @@ fn NewParser_( fn ensureRequireSymbol(p: *P) void { if (p.runtime_imports.__require != null) return; - p.runtime_imports.__require = GeneratedSymbol{ - .backup = declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, StaticSymbolName.List.__require.backup, true) catch unreachable, + const static_symbol = comptime StaticSymbolName.init("__require"); + p.runtime_imports.__require = .{ + .backup = declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, static_symbol.backup, true) catch bun.outOfMemory(), .primary = p.require_ref, - .ref = declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, StaticSymbolName.List.__require.internal, true) catch unreachable, + .ref = declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, static_symbol.internal, true) catch bun.outOfMemory(), }; p.runtime_imports.put("__require", p.runtime_imports.__require.?); } @@ -6542,26 +6842,11 @@ fn NewParser_( } pub fn resolveBundlingSymbols(p: *P) void { - p.recordUsage(p.runtime_imports.@"$$m".?.ref); - - p.resolveGeneratedSymbol(&p.runtime_imports.@"$$m".?); - p.resolveGeneratedSymbol(&p.runtime_imports.@"$$lzy".?); p.resolveGeneratedSymbol(&p.runtime_imports.__export.?); p.resolveGeneratedSymbol(&p.runtime_imports.__exportValue.?); p.resolveGeneratedSymbol(&p.runtime_imports.__exportDefault.?); } - pub fn resolveHMRSymbols(p: *P) void { - p.resolveGeneratedSymbol(&p.hmr_module); - if (p.runtime_imports.__FastRefreshModule != null) { - p.resolveGeneratedSymbol(&p.runtime_imports.__FastRefreshModule.?); - if (p.options.jsx.use_embedded_refresh_runtime) - p.resolveGeneratedSymbol(&p.runtime_imports.__FastRefreshRuntime.?); - } - if (p.runtime_imports.__HMRModule != null) p.resolveGeneratedSymbol(&p.runtime_imports.__HMRModule.?); - if (p.runtime_imports.__HMRClient != null) p.resolveGeneratedSymbol(&p.runtime_imports.__HMRClient.?); - } - pub fn resolveStaticJSXSymbols(p: *P) void { if (p.options.bundle) return; @@ -6776,17 +7061,18 @@ fn NewParser_( } fn pushScopeForVisitPass(p: *P, kind: js_ast.Scope.Kind, loc: logger.Loc) anyerror!void { - // Output.print("\n+Loc: {d}\n", .{loc.start}); - // for (p.scopes_in_order.items[p.scopes_in_order_visitor_index..p.scopes_in_order.items.len]) |scope_order, i| { - // if (scope_order) |ord| { - // Output.print("Scope ({d}, {d})\n", .{ @intFromEnum(ord.scope.kind), ord.loc.start }); - // } - // } const order = p.nextScopeInOrderForVisitPass(); // Sanity-check that the scopes generated by the first and second passes match - if (order.loc.start != loc.start or order.scope.kind != kind) { - p.panic("Expected scope ({any}, {d}) in {s}, found scope ({any}, {d})", .{ kind, loc.start, p.source.path.pretty, order.scope.kind, order.loc.start }); + if (bun.Environment.allow_assert and + order.loc.start != loc.start or order.scope.kind != kind) + { + p.log.level = .verbose; + + p.log.addDebugFmt(p.source, loc, p.allocator, "Expected this scope (.{s})", .{@tagName(kind)}) catch bun.outOfMemory(); + p.log.addDebugFmt(p.source, order.loc, p.allocator, "Found this scope (.{s})", .{@tagName(order.scope.kind)}) catch bun.outOfMemory(); + + p.panic("Scope mismatch while visiting", .{}); } p.current_scope = order.scope; @@ -6819,7 +7105,7 @@ fn NewParser_( } } - if (comptime !Environment.isRelease) { + if (comptime Environment.isDebug) { // Enforce that scope locations are strictly increasing to help catch bugs // where the pushed scopes are mismatched between the first and second passes if (p.scopes_in_order.items.len > 0) { @@ -6830,6 +7116,9 @@ fn NewParser_( if (p.scopes_in_order.items[last_i]) |prev_scope| { if (prev_scope.loc.start >= loc.start) { + p.log.level = .verbose; + p.log.addDebugFmt(p.source, prev_scope.loc, p.allocator, "Previous Scope", .{}) catch bun.outOfMemory(); + p.log.addDebugFmt(p.source, loc, p.allocator, "Next Scope", .{}) catch bun.outOfMemory(); p.panic("Scope location {d} must be greater than {d}", .{ loc.start, prev_scope.loc.start }); } } @@ -6885,7 +7174,7 @@ fn NewParser_( if (ex.is_parenthesized) { invalid_loc.append(.{ .loc = p.source.rangeOfOperatorBefore(expr.loc, "(").loc, - .kind = .parenthese, + .kind = .parentheses, }) catch unreachable; } @@ -6922,7 +7211,7 @@ fn NewParser_( } if (ex.is_parenthesized) { - invalid_loc.append(.{ .loc = p.source.rangeOfOperatorBefore(expr.loc, "(").loc, .kind = .parenthese }) catch unreachable; + invalid_loc.append(.{ .loc = p.source.rangeOfOperatorBefore(expr.loc, "(").loc, .kind = .parentheses }) catch unreachable; } // p.markSyntaxFeature(compat.Destructuring, p.source.RangeOfOperatorAfter(expr.Loc, "{")) @@ -7078,12 +7367,18 @@ fn NewParser_( // "(1, 2)" => "2" // "(sideEffects(), 2)" => "(sideEffects(), 2)" if (p.options.features.minify_syntax) { - e_.left = SideEffects.simpifyUnusedExpr(p, e_.left) orelse return e_.right; + e_.left = SideEffects.simplifyUnusedExpr(p, e_.left) orelse return e_.right; } }, .bin_loose_eq => { - const equality = e_.left.data.eql(e_.right.data, p.allocator, .loose); + const equality = e_.left.data.eql(e_.right.data, p, .loose); if (equality.ok) { + if (equality.is_require_main_and_module) { + p.ignoreUsageOfRuntimeRequire(); + p.ignoreUsage(p.module_ref); + return p.valueForImportMetaMain(false, v.loc); + } + return p.newExpr( E.Boolean{ .value = equality.equal }, v.loc, @@ -7105,8 +7400,14 @@ fn NewParser_( }, .bin_strict_eq => { - const equality = e_.left.data.eql(e_.right.data, p.allocator, .strict); + const equality = e_.left.data.eql(e_.right.data, p, .strict); if (equality.ok) { + if (equality.is_require_main_and_module) { + p.ignoreUsage(p.module_ref); + p.ignoreUsageOfRuntimeRequire(); + return p.valueForImportMetaMain(false, v.loc); + } + return p.newExpr(E.Boolean{ .value = equality.equal }, v.loc); } @@ -7115,8 +7416,14 @@ fn NewParser_( // TODO: warn about typeof string }, .bin_loose_ne => { - const equality = e_.left.data.eql(e_.right.data, p.allocator, .loose); + const equality = e_.left.data.eql(e_.right.data, p, .loose); if (equality.ok) { + if (equality.is_require_main_and_module) { + p.ignoreUsage(p.module_ref); + p.ignoreUsageOfRuntimeRequire(); + return p.valueForImportMetaMain(true, v.loc); + } + return p.newExpr(E.Boolean{ .value = !equality.equal }, v.loc); } // const after_op_loc = locAfterOp(e_.); @@ -7129,8 +7436,14 @@ fn NewParser_( } }, .bin_strict_ne => { - const equality = e_.left.data.eql(e_.right.data, p.allocator, .strict); + const equality = e_.left.data.eql(e_.right.data, p, .strict); if (equality.ok) { + if (equality.is_require_main_and_module) { + p.ignoreUsage(p.module_ref); + p.ignoreUsageOfRuntimeRequire(); + return p.valueForImportMetaMain(true, v.loc); + } + return p.newExpr(E.Boolean{ .value = !equality.equal }, v.loc); } }, @@ -7188,10 +7501,24 @@ fn NewParser_( if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { return p.newExpr(E.Number{ .value = vals[0] + vals[1] }, v.loc); } - } - if (foldStringAddition(e_.left, e_.right)) |res| { - return res; + // "'abc' + 'xyz'" => "'abcxyz'" + if (foldStringAddition(e_.left, e_.right, p.allocator, .normal)) |res| { + return res; + } + + // "(x + 'abc') + 'xyz'" => "'abcxyz'" + if (e_.left.data.as(.e_binary)) |left| { + if (left.op == .bin_add) { + if (foldStringAddition(left.right, e_.right, p.allocator, .nested_left)) |result| { + return p.newExpr(E.Binary{ + .left = left.left, + .right = result, + .op = .bin_add, + }, e_.left.loc); + } + } + } } }, .bin_sub => { @@ -7235,109 +7562,75 @@ fn NewParser_( } }, .bin_shl => { - // TODO: - // if (p.should_fold_typescript_constant_expressions) { - // if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { - // return p.newExpr(E.Number{ .value = ((@intFromFloat(i32, vals[0]) << @intFromFloat(u32, vals[1])) & 31) }, expr.loc); - // } - // } + if (p.should_fold_typescript_constant_expressions) { + if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { + const left = floatToInt32(vals[0]); + const right: u8 = @intCast(@as(u32, @bitCast(floatToInt32(vals[1]))) % 32); + const result: i32 = @bitCast(std.math.shl(i32, left, right)); + return p.newExpr(E.Number{ + .value = @floatFromInt(result), + }, v.loc); + } + } }, .bin_shr => { - // TODO: - // if (p.should_fold_typescript_constant_expressions) { - // if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { - // return p.newExpr(E.Number{ .value = ((@intFromFloat(i32, vals[0]) >> @intFromFloat(u32, vals[1])) & 31) }, expr.loc); - // } - // } + if (p.should_fold_typescript_constant_expressions) { + if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { + const left = floatToInt32(vals[0]); + const right: u8 = @intCast(@as(u32, @bitCast(floatToInt32(vals[1]))) % 32); + const result: i32 = @bitCast(std.math.shr(i32, left, right)); + return p.newExpr(E.Number{ + .value = @floatFromInt(result), + }, v.loc); + } + } }, .bin_u_shr => { - // TODO: - // if (p.should_fold_typescript_constant_expressions) { - // if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { - // return p.newExpr(E.Number{ .value = ((@intFromFloat(i32, vals[0]) >> @intFromFloat(u32, vals[1])) & 31) }, expr.loc); - // } - // } + if (p.should_fold_typescript_constant_expressions) { + if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { + const left: u32 = @bitCast(floatToInt32(vals[0])); + const right: u8 = @intCast(@as(u32, @bitCast(floatToInt32(vals[1]))) % 32); + const result: u32 = std.math.shr(u32, left, right); + return p.newExpr(E.Number{ + .value = @floatFromInt(result), + }, v.loc); + } + } }, .bin_bitwise_and => { - // TODO: - // if (p.should_fold_typescript_constant_expressions) { - // if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { - // return p.newExpr(E.Number{ .value = ((@intFromFloat(i32, vals[0]) >> @intFromFloat(u32, vals[1])) & 31) }, expr.loc); - // } - // } + if (p.should_fold_typescript_constant_expressions) { + if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { + return p.newExpr(E.Number{ + .value = @floatFromInt((floatToInt32(vals[0]) & floatToInt32(vals[1]))), + }, v.loc); + } + } }, .bin_bitwise_or => { - // TODO: - // if (p.should_fold_typescript_constant_expressions) { - // if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { - // return p.newExpr(E.Number{ .value = ((@intFromFloat(i32, vals[0]) >> @intFromFloat(u32, vals[1])) & 31) }, expr.loc); - // } - // } + if (p.should_fold_typescript_constant_expressions) { + if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { + return p.newExpr(E.Number{ + .value = @floatFromInt((floatToInt32(vals[0]) | floatToInt32(vals[1]))), + }, v.loc); + } + } }, .bin_bitwise_xor => { - // TODO: - // if (p.should_fold_typescript_constant_expressions) { - // if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { - // return p.newExpr(E.Number{ .value = ((@intFromFloat(i32, vals[0]) >> @intFromFloat(u32, vals[1])) & 31) }, expr.loc); - // } - // } + if (p.should_fold_typescript_constant_expressions) { + if (Expr.extractNumericValues(e_.left.data, e_.right.data)) |vals| { + return p.newExpr(E.Number{ + .value = @floatFromInt((floatToInt32(vals[0]) ^ floatToInt32(vals[1]))), + }, v.loc); + } + } }, // --------------------------------------------------------------------------------------------------- - // --------------------------------------------------------------------------------------------------- - // --------------------------------------------------------------------------------------------------- - // --------------------------------------------------------------------------------------------------- .bin_assign => { - // Optionally preserve the name - if (@as(Expr.Tag, e_.left.data) == .e_identifier) { + if (e_.left.data == .e_identifier) { e_.right = p.maybeKeepExprSymbolName(e_.right, p.symbols.items[e_.left.data.e_identifier.ref.innerIndex()].original_name, was_anonymous_named_expr); } }, - .bin_add_assign => { - // notimpl(); - }, - .bin_sub_assign => { - // notimpl(); - }, - .bin_mul_assign => { - // notimpl(); - }, - .bin_div_assign => { - // notimpl(); - }, - .bin_rem_assign => { - // notimpl(); - }, - .bin_pow_assign => { - // notimpl(); - }, - .bin_shl_assign => { - // notimpl(); - }, - .bin_shr_assign => { - // notimpl(); - }, - .bin_u_shr_assign => { - // notimpl(); - }, - .bin_bitwise_or_assign => { - // notimpl(); - }, - .bin_bitwise_and_assign => { - // notimpl(); - }, - .bin_bitwise_xor_assign => { - // notimpl(); - }, - .bin_nullish_coalescing_assign => { - // notimpl(); - }, - .bin_logical_and_assign => { - // notimpl(); - }, - .bin_logical_or_assign => { - // notimpl(); - }, else => {}, } @@ -7560,7 +7853,7 @@ fn NewParser_( fn parseFn(p: *P, name: ?js_ast.LocRef, opts: FnOrArrowDataParse) anyerror!G.Fn { // if data.allowAwait and data.allowYield { - // p.markSyntaxFeature(compat.AsyncGenerator, data.asyncRange) + // p.markSyntaxFeature(compat.AsyncGenerator, data.asyncRange) // } var func = G.Fn{ @@ -8286,11 +8579,7 @@ fn NewParser_( else => { if (comptime get_metadata) { const find_result = p.findSymbol(logger.Loc.Empty, p.lexer.identifier) catch unreachable; - if (p.known_enum_values.contains(find_result.ref)) { - result.* = .m_number; - } else { - result.* = .{ .m_identifier = find_result.ref }; - } + result.* = .{ .m_identifier = find_result.ref }; } try p.lexer.next(); @@ -8727,8 +9016,8 @@ fn NewParser_( }) catch unreachable; } } else { - var path_name = fs.PathName.init(strings.append(p.allocator, "import_", path.text) catch unreachable); - const name = try path_name.nonUniqueNameString(p.allocator); + var path_name = fs.PathName.init(path.text); + const name = try strings.append(p.allocator, "import_", try path_name.nonUniqueNameString(p.allocator)); stmt.namespace_ref = try p.newSymbol(.other, name); var scope: *Scope = p.current_scope; try scope.generated.push(p.allocator, stmt.namespace_ref); @@ -8752,6 +9041,18 @@ fn NewParser_( name_loc.ref = ref; try p.is_import_item.put(p.allocator, ref, {}); + // ensure every e_import_identifier holds the namespace + if (p.options.features.hot_module_reloading) { + const symbol = &p.symbols.items[ref.inner_index]; + if (symbol.namespace_alias == null) { + symbol.namespace_alias = .{ + .namespace_ref = stmt.namespace_ref, + .alias = "default", + .import_record_index = stmt.import_record_index, + }; + } + } + if (macro_remap) |*remap| { if (remap.get("default")) |remapped_path| { const new_import_id = p.addImportRecord(.stmt, path.loc, remapped_path); @@ -8803,6 +9104,18 @@ fn NewParser_( try p.is_import_item.put(p.allocator, ref, {}); p.checkForNonBMPCodePoint(item.alias_loc, item.alias); + // ensure every e_import_identifier holds the namespace + if (p.options.features.hot_module_reloading) { + const symbol = &p.symbols.items[ref.inner_index]; + if (symbol.namespace_alias == null) { + symbol.namespace_alias = .{ + .namespace_ref = stmt.namespace_ref, + .alias = name, + .import_record_index = stmt.import_record_index, + }; + } + } + if (macro_remap) |*remap| { if (remap.get(item.alias)) |remapped_path| { const new_import_id = p.addImportRecord(.stmt, path.loc, remapped_path); @@ -9053,7 +9366,7 @@ fn NewParser_( return Ref{ .inner_index = inner_index, - .source_index = Ref.toInt(p.source.index.get()), + .source_index = @intCast(p.source.index.get()), .tag = .symbol, }; } @@ -9139,41 +9452,6 @@ fn NewParser_( }, loc); } - // For HMR, we must convert syntax like this: - // export function leftPad() { - // export const guy = GUY_FIERI_ASCII_ART; - // export class Bacon {} - // export default GuyFieriAsciiArt; - // export {Bacon}; - // export {Bacon as default}; - // to: - // var __hmr__module = new __hmr_HMRModule(file_id, import.meta); - // (__hmr__module._load = function() { - // __hmr__module.exports.leftPad = function () {}; - // __hmr__module.exports.npmProgressBar33 = true; - // __hmr__module.exports.Bacon = class {}; - // })(); - // export { __hmr__module.exports.leftPad as leftPad, __hmr__module.exports.npmProgressBar33 as npmProgressBar33, __hmr__module } - // - // - // - // At bottom of the file: - // - - // var __hmr__exports = new HMRModule({ - // leftPad: () => leftPad, - // npmProgressBar33 () => npmProgressBar33, - // default: () => GuyFieriAsciiArt, - // [__hmr_ModuleIDSymbol]: - //}); - // export { __hmr__exports.leftPad as leftPad, __hmr__ } - // - - // Then: - // if () { - // - // } - - // pub fn maybeRewriteExportSymbol(p: *P, ) - fn defaultNameForExpr(p: *P, expr: Expr, loc: logger.Loc) LocRef { switch (expr.data) { .e_function => |func_container| { @@ -9712,9 +9990,7 @@ fn NewParser_( try p.requireInitializers(.k_const, decls.items); } - // When HMR is enabled, replace all const/let exports with var - const kind = if (p.options.features.hot_module_reloading and opts.is_export) S.Local.Kind.k_var else S.Local.Kind.k_const; - return p.s(S.Local{ .kind = kind, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); + return p.s(S.Local{ .kind = .k_const, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); }, .t_if => { try p.lexer.next(); @@ -10569,14 +10845,21 @@ fn NewParser_( const name_text = p.lexer.identifier; try p.lexer.next(); + // Generate the namespace object + const ts_namespace = p.getOrCreateExportedNamespaceMembers(name_text, opts.is_export, false); + const exported_members = ts_namespace.exported_members; + const ns_member_data = js_ast.TSNamespaceMember.Data{ .namespace = exported_members }; + + // Declare the namespace and create the scope var name = LocRef{ .loc = name_loc, .ref = null }; const scope_index = try p.pushScopeForParsePass(.entry, loc); + p.current_scope.ts_namespace = ts_namespace; const old_has_non_local_export_declare_inside_namespace = p.has_non_local_export_declare_inside_namespace; p.has_non_local_export_declare_inside_namespace = false; + // Parse the statements inside the namespace var stmts: ListManaged(Stmt) = ListManaged(Stmt).init(p.allocator); - if (p.lexer.token == .t_dot) { const dot_loc = p.lexer.loc(); try p.lexer.next(); @@ -10601,13 +10884,79 @@ fn NewParser_( const has_non_local_export_declare_inside_namespace = p.has_non_local_export_declare_inside_namespace; p.has_non_local_export_declare_inside_namespace = old_has_non_local_export_declare_inside_namespace; + // Add any exported members from this namespace's body as members of the + // associated namespace object. + for (stmts.items) |stmt| { + switch (stmt.data) { + .s_function => |func| { + if (func.func.flags.contains(.is_export)) { + const locref = func.func.name.?; + const fn_name = p.symbols.items[locref.ref.?.inner_index].original_name; + try exported_members.put(p.allocator, fn_name, .{ + .loc = locref.loc, + .data = .property, + }); + try p.ref_to_ts_namespace_member.put( + p.allocator, + locref.ref.?, + .property, + ); + } + }, + .s_class => |class| { + if (class.is_export) { + const locref = class.class.class_name.?; + const class_name = p.symbols.items[locref.ref.?.inner_index].original_name; + try exported_members.put(p.allocator, class_name, .{ + .loc = locref.loc, + .data = .property, + }); + try p.ref_to_ts_namespace_member.put( + p.allocator, + locref.ref.?, + .property, + ); + } + }, + inline .s_namespace, .s_enum => |ns| { + if (ns.is_export) { + if (p.ref_to_ts_namespace_member.get(ns.name.ref.?)) |member_data| { + try exported_members.put( + p.allocator, + p.symbols.items[ns.name.ref.?.inner_index].original_name, + .{ + .data = member_data, + .loc = ns.name.loc, + }, + ); + try p.ref_to_ts_namespace_member.put( + p.allocator, + ns.name.ref.?, + member_data, + ); + } + } + }, + .s_local => |local| { + if (local.is_export) { + for (local.decls.slice()) |decl| { + try p.defineExportedNamespaceBinding( + exported_members, + decl.binding, + ); + } + } + }, + else => {}, + } + } + // Import assignments may be only used in type expressions, not value // expressions. If this is the case, the TypeScript compiler removes // them entirely from the output. That can cause the namespace itself // to be considered empty and thus be removed. var import_equal_count: usize = 0; - const _stmts: []Stmt = stmts.items; - for (_stmts) |stmt| { + for (stmts.items) |stmt| { switch (stmt.data) { .s_local => |local| { if (local.was_ts_import_equals and !local.is_export) { @@ -10636,7 +10985,7 @@ fn NewParser_( return p.s(S.TypeScript{}, loc); } - var arg_ref: ?Ref = null; + var arg_ref = Ref.None; if (!opts.is_typescript_declare) { // Avoid a collision with the namespace closure argument variable if the // namespace exports a symbol with the same name as the namespace itself: @@ -10659,21 +11008,57 @@ fn NewParser_( // run the renamer. For external-facing things the renamer will avoid // collisions automatically so this isn't important for correctness. arg_ref = p.newSymbol(.hoisted, strings.cat(p.allocator, "_", name_text) catch unreachable) catch unreachable; - p.current_scope.generated.push(p.allocator, arg_ref.?) catch unreachable; + p.current_scope.generated.push(p.allocator, arg_ref) catch unreachable; } else { arg_ref = p.newSymbol(.hoisted, name_text) catch unreachable; } + ts_namespace.arg_ref = arg_ref; } p.popScope(); if (!opts.is_typescript_declare) { - name.ref = p.declareSymbol(.ts_namespace, name_loc, name_text) catch unreachable; + name.ref = p.declareSymbol(.ts_namespace, name_loc, name_text) catch bun.outOfMemory(); + try p.ref_to_ts_namespace_member.put(p.allocator, name.ref.?, ns_member_data); } - return p.s( - S.Namespace{ .name = name, .arg = arg_ref orelse Ref.None, .stmts = stmts.items, .is_export = opts.is_export }, - loc, - ); + return p.s(S.Namespace{ + .name = name, + .arg = arg_ref, + .stmts = stmts.items, + .is_export = opts.is_export, + }, loc); + } + + fn defineExportedNamespaceBinding( + p: *P, + exported_members: *js_ast.TSNamespaceMemberMap, + binding: Binding, + ) !void { + switch (binding.data) { + .b_missing => {}, + .b_identifier => |id| { + const name = p.symbols.items[id.ref.inner_index].original_name; + try exported_members.put(p.allocator, name, .{ + .loc = binding.loc, + .data = .property, + }); + try p.ref_to_ts_namespace_member.put( + p.allocator, + id.ref, + .property, + ); + }, + .b_object => |obj| { + for (obj.properties) |prop| { + try p.defineExportedNamespaceBinding(exported_members, prop.value); + } + }, + .b_array => |obj| { + for (obj.items) |prop| { + try p.defineExportedNamespaceBinding(exported_members, prop.binding); + } + }, + } } fn skipTypeScriptInterfaceStmt(p: *P, opts: *ParseStatementOptions) anyerror!void { @@ -10984,8 +11369,7 @@ fn NewParser_( return ExprOrLetStmt{ .stmt_or_expr = js_ast.StmtOrExpr{ .stmt = p.s(S.Local{ - // Replace all "export let" with "export var" when HMR is enabled - .kind = if (opts.is_export and p.options.features.hot_module_reloading) .k_var else .k_let, + .kind = .k_let, .decls = G.Decl.List.fromList(decls), .is_export = opts.is_export, }, token_range.loc), @@ -11423,14 +11807,25 @@ fn NewParser_( const name_text = p.lexer.identifier; try p.lexer.expect(.t_identifier); var name = LocRef{ .loc = name_loc, .ref = Ref.None }; - var arg_ref = Ref.None; + + // Generate the namespace object + var arg_ref: Ref = undefined; + const ts_namespace = p.getOrCreateExportedNamespaceMembers(name_text, opts.is_export, true); + const exported_members = ts_namespace.exported_members; + const enum_member_data = js_ast.TSNamespaceMember.Data{ .namespace = exported_members }; + + // Declare the enum and create the scope + const scope_index = p.scopes_in_order.items.len; if (!opts.is_typescript_declare) { name.ref = try p.declareSymbol(.ts_enum, name_loc, name_text); _ = try p.pushScopeForParsePass(.entry, loc); + p.current_scope.ts_namespace = ts_namespace; + p.ref_to_ts_namespace_member.putNoClobber(p.allocator, name.ref.?, enum_member_data) catch bun.outOfMemory(); } try p.lexer.expect(.t_open_brace); + // Parse the body var values = std.ArrayList(js_ast.EnumValue).init(p.allocator); while (p.lexer.token != .t_close_brace) { var value = js_ast.EnumValue{ .loc = p.lexer.loc(), .ref = Ref.None, .name = undefined, .value = null }; @@ -11438,13 +11833,10 @@ fn NewParser_( // Parse the name if (p.lexer.token == .t_string_literal) { - value.name = p.lexer.toEString(); + value.name = p.lexer.toUTF8EString().data; + needs_symbol = js_lexer.isIdentifier(value.name); } else if (p.lexer.isIdentifierOrKeyword()) { - const id = p.lexer.identifier; - value.name = if (bun.strings.isAllASCII(id)) - .{ .data = id } - else - E.String.init(try bun.strings.toUTF16AllocForReal(p.allocator, id, false, false)); + value.name = p.lexer.identifier; needs_symbol = true; } else { try p.lexer.expect(.t_identifier); @@ -11453,7 +11845,7 @@ fn NewParser_( // Identifiers can be referenced by other values if (!opts.is_typescript_declare and needs_symbol) { - value.ref = try p.declareSymbol(.other, value.loc, try value.name.string(p.allocator)); + value.ref = try p.declareSymbol(.other, value.loc, value.name); } // Parse the initializer @@ -11464,6 +11856,11 @@ fn NewParser_( values.append(value) catch unreachable; + exported_members.put(p.allocator, value.name, .{ + .loc = value.loc, + .data = .enum_property, + }) catch bun.outOfMemory(); + if (p.lexer.token != .t_comma and p.lexer.token != .t_semicolon) { break; } @@ -11500,7 +11897,6 @@ fn NewParser_( // (function (foo) { // foo[foo["bar"] = foo] = "bar"; // })(foo || (foo = {})); - // if (p.current_scope.members.contains(name_text)) { // Add a "_" to make tests easier to read, since non-bundler tests don't // run the renamer. For external-facing things the renamer will avoid @@ -11510,6 +11906,8 @@ fn NewParser_( } else { arg_ref = p.declareSymbol(.hoisted, name_loc, name_text) catch unreachable; } + p.ref_to_ts_namespace_member.put(p.allocator, arg_ref, enum_member_data) catch bun.outOfMemory(); + ts_namespace.arg_ref = arg_ref; p.popScope(); } @@ -11524,6 +11922,30 @@ fn NewParser_( return p.s(S.TypeScript{}, loc); } + // Save these for when we do out-of-order enum visiting + // + // Make a copy of "scopesInOrder" instead of a slice or index since + // the original array may be flattened in the future by + // "popAndFlattenScope" + p.scopes_in_order_for_enum.putNoClobber( + p.allocator, + loc, + scope_order_clone: { + var count: usize = 0; + for (p.scopes_in_order.items[scope_index..]) |i| { + if (i != null) count += 1; + } + + const items = p.allocator.alloc(ScopeOrder, count) catch bun.outOfMemory(); + var i: usize = 0; + for (p.scopes_in_order.items[scope_index..]) |item| { + items[i] = item orelse continue; + i += 1; + } + break :scope_order_clone items; + }, + ) catch bun.outOfMemory(); + return p.s(S.Enum{ .name = name, .arg = arg_ref, @@ -11532,6 +11954,59 @@ fn NewParser_( }, loc); } + // Generate a TypeScript namespace object for this namespace's scope. If this + // namespace is another block that is to be merged with an existing namespace, + // use that earlier namespace's object instead. + pub fn getOrCreateExportedNamespaceMembers(p: *P, name: []const u8, is_export: bool, is_enum_scope: bool) *js_ast.TSNamespaceScope { + const map = brk: { + + // Merge with a sibling namespace from the same scope + if (p.current_scope.members.get(name)) |existing_member| { + if (p.ref_to_ts_namespace_member.get(existing_member.ref)) |member_data| { + if (member_data == .namespace) + break :brk member_data.namespace; + } + } + + // Merge with a sibling namespace from a different scope + if (is_export) { + if (p.current_scope.ts_namespace) |ns| { + if (ns.exported_members.get(name)) |member| { + if (member.data == .namespace) + break :brk member.data.namespace; + } + } + } + + break :brk null; + }; + + if (map) |existing| { + return bun.create(p.allocator, js_ast.TSNamespaceScope, .{ + .exported_members = existing, + .is_enum_scope = is_enum_scope, + .arg_ref = Ref.None, + }); + } + + // Otherwise, generate a new namespace object + // Batch the allocation of the namespace object and the map into a single allocation. + const Pair = struct { + map: js_ast.TSNamespaceMemberMap, + scope: js_ast.TSNamespaceScope, + }; + + var pair = p.allocator.create(Pair) catch bun.outOfMemory(); + pair.map = .{}; + pair.scope = .{ + .exported_members = &pair.map, + .is_enum_scope = is_enum_scope, + .arg_ref = Ref.None, + }; + + return &pair.scope; + } + fn parseExportClause(p: *P) !ExportClauseResult { var items = ListManaged(js_ast.ClauseItem).initCapacity(p.allocator, 1) catch unreachable; try p.lexer.expect(.t_open_brace); @@ -11868,6 +12343,8 @@ fn NewParser_( skip = true; // Track "use strict" directives p.current_scope.strict_mode = .explicit_strict_mode; + if (p.current_scope == p.module_scope) + p.module_scope_directive_loc = stmt.loc; } else if (str.eqlComptime("use asm")) { skip = true; stmt.data = Prefill.Data.SEmpty; @@ -12023,17 +12500,17 @@ fn NewParser_( } fn declareGeneratedSymbol(p: *P, kind: Symbol.Kind, comptime name: string) !GeneratedSymbol { - const static = @field(StaticSymbolName.List, name); + const static = comptime StaticSymbolName.init(name); if (p.options.bundle) { const ref = try declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, static.primary, true); - return GeneratedSymbol{ + return .{ .backup = ref, .primary = ref, .ref = ref, }; } - return GeneratedSymbol{ + return .{ .backup = try declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, static.backup, true), .primary = try declareSymbolMaybeGenerated(p, .other, logger.Loc.Empty, static.primary, true), .ref = try declareSymbolMaybeGenerated(p, kind, logger.Loc.Empty, static.internal, true), @@ -12048,7 +12525,6 @@ fn NewParser_( // p.checkForNonBMPCodePoint(loc, name) if (comptime !is_generated) { - // Forbid declaring a symbol with a reserved word in strict mode if (p.isStrictMode() and name.ptr != arguments_str.ptr and js_lexer.StrictModeReservedWords.has(name)) { try p.markStrictModeFeature(.reserved_word, js_lexer.rangeOfIdentifier(p.source, loc), name); @@ -12070,9 +12546,11 @@ fn NewParser_( try p.log.addSymbolAlreadyDeclaredError(p.allocator, p.source, symbol.original_name, loc, existing.loc); return existing.ref; }, + .keep_existing => { ref = existing.ref; }, + .replace_with_new => { symbol.link = ref; @@ -12081,17 +12559,18 @@ fn NewParser_( symbol.remove_overwritten_function_declaration = true; } }, + .become_private_get_set_pair => { ref = existing.ref; symbol.kind = .private_get_set_pair; }, + .become_private_static_get_set_pair => { ref = existing.ref; symbol.kind = .private_static_get_set_pair; }, .overwrite_with_new => {}, - // else => unreachable, } } else { p.symbols.items[ref.innerIndex()].link = existing.ref; @@ -12245,41 +12724,19 @@ fn NewParser_( bind.ref = try p.declareSymbol(kind, binding.loc, p.loadNameFromRef(bind.ref)); } }, - .b_array => |bind| { for (bind.items) |*item| { p.declareBinding(kind, &item.binding, opts) catch unreachable; } }, - .b_object => |bind| { for (bind.properties) |*prop| { p.declareBinding(kind, &prop.value, opts) catch unreachable; } }, - - else => { - // @compileError("Missing binding type"); - }, } } - // This is where the allocate memory to the heap for AST objects. - // This is a short name to keep the code more readable. - // It also swallows errors, but I think that's correct here. - // We can handle errors via the log. - // We'll have to deal with @wasmHeapGrow or whatever that thing is. - pub inline fn mm(self: *P, comptime ast_object_type: type, instance: anytype) *ast_object_type { - const obj = self.allocator.create(ast_object_type) catch unreachable; - obj.* = instance; - return obj; - } - - // mmmm memory allocation - pub inline fn m(self: *P, kind: anytype) *@TypeOf(kind) { - return self.mm(@TypeOf(kind), kind); - } - pub fn storeNameInRef(p: *P, name: string) !Ref { if (comptime track_symbol_usage_during_parse_pass) { if (p.parse_pass_symbol_uses.getPtr(name)) |res| { @@ -12288,13 +12745,19 @@ fn NewParser_( } if (@intFromPtr(p.source.contents.ptr) <= @intFromPtr(name.ptr) and (@intFromPtr(name.ptr) + name.len) <= (@intFromPtr(p.source.contents.ptr) + p.source.contents.len)) { - const start = Ref.toInt(@intFromPtr(name.ptr) - @intFromPtr(p.source.contents.ptr)); - const end = Ref.toInt(name.len); - return Ref.initSourceEnd(.{ .source_index = start, .inner_index = end, .tag = .source_contents_slice }); + return Ref.initSourceEnd(.{ + .source_index = @intCast(@intFromPtr(name.ptr) - @intFromPtr(p.source.contents.ptr)), + .inner_index = @intCast(name.len), + .tag = .source_contents_slice, + }); } else { - const inner_index = Ref.toInt(p.allocated_names.items.len); + const inner_index: u31 = @intCast(p.allocated_names.items.len); try p.allocated_names.append(p.allocator, name); - return Ref.init(inner_index, p.source.index.get(), false); + return Ref.init( + inner_index, + p.source.index.get(), + false, + ); } } @@ -12587,12 +13050,6 @@ fn NewParser_( .e_new => |ex| { ex.can_be_unwrapped_if_unused = true; }, - - // this is specifically added only to support our implementation - // of '__require' for --target=node, for /* @__PURE__ */ import.meta.url - .e_dot => |ex| { - ex.can_be_removed_if_unused = true; - }, else => {}, } } @@ -12864,7 +13321,13 @@ fn NewParser_( if (opts.is_class and is_typescript_enabled and !opts.is_ts_abstract and strings.eqlComptime(raw, "abstract")) { opts.is_ts_abstract = true; const scope_index = p.scopes_in_order.items.len; - _ = try p.parseProperty(kind, opts, null); + if (try p.parseProperty(kind, opts, null)) |_prop| { + var prop = _prop; + if (prop.kind == .normal and prop.value == null and opts.ts_decorators.len > 0) { + prop.kind = .abstract; + return prop; + } + } p.discardScopesUpTo(scope_index); return null; } @@ -12913,6 +13376,13 @@ fn NewParser_( } } + // Handle invalid identifiers in property names + // https://github.com/oven-sh/bun/issues/12039 + if (p.lexer.token == .t_syntax_error) { + p.log.addRangeErrorFmt(p.source, name_range, p.allocator, "Unexpected {}", .{bun.fmt.quote(name)}) catch bun.outOfMemory(); + return error.SyntaxError; + } + key = p.newExpr(E.String{ .data = name }, name_range.loc); // Parse a shorthand property @@ -13620,8 +14090,8 @@ fn NewParser_( } // Only continue if we have started - if ((optional_start orelse .ccontinue) == .start) { - optional_chain = .ccontinue; + if ((optional_start orelse .continuation) == .start) { + optional_chain = .continuation; } }, .t_no_substitution_template_literal => { @@ -14257,16 +14727,32 @@ fn NewParser_( } }; - pub fn panic(p: *P, comptime str: string, args: anytype) noreturn { + pub fn panic(p: *P, comptime fmt: string, args: anytype) noreturn { + p.panicLoc(fmt, args, null); @setCold(true); + } + + pub fn panicLoc(p: *P, comptime fmt: string, args: anytype, loc: ?logger.Loc) noreturn { var panic_buffer = p.allocator.alloc(u8, 32 * 1024) catch unreachable; var panic_stream = std.io.fixedBufferStream(panic_buffer); - p.log.addRangeErrorFmt(p.source, p.lexer.range(), p.allocator, str, args) catch unreachable; - p.log.printForLogLevel( - panic_stream.writer(), - ) catch unreachable; - Global.panic("{s}", .{panic_buffer[0..panic_stream.pos]}); + // panic during visit pass leaves the lexer at the end, which + // would make this location absolutely useless. + const location = loc orelse p.lexer.loc(); + if (location.start < p.lexer.source.contents.len and !location.isEmpty()) { + p.log.addRangeErrorFmt( + p.source, + .{ .loc = location }, + p.allocator, + "panic here", + .{}, + ) catch bun.outOfMemory(); + } + + p.log.level = .verbose; + p.log.printForLogLevel(panic_stream.writer()) catch unreachable; + + Output.panic(fmt ++ "\n{s}", args ++ .{panic_buffer[0..panic_stream.pos]}); } pub fn parsePrefix(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr { @@ -15002,40 +15488,17 @@ fn NewParser_( const value = try p.parseExpr(.comma); - var type_attribute = E.Import.TypeAttribute.none; - + var import_options = Expr.empty; if (p.lexer.token == .t_comma) { // "import('./foo.json', )" try p.lexer.next(); if (p.lexer.token != .t_close_paren) { - // for now, we silently strip import assertions // "import('./foo.json', { assert: { type: 'json' } })" - const import_expr = try p.parseExpr(.comma); - if (import_expr.data == .e_object) { - if (import_expr.data.e_object.get("with") orelse import_expr.data.e_object.get("assert")) |with| { - if (with.data == .e_object) { - const with_object = with.data.e_object; - if (with_object.get("type")) |field| { - if (field.data == .e_string) { - const str = field.data.e_string; - if (str.eqlComptime("json")) { - type_attribute = .json; - } else if (str.eqlComptime("toml")) { - type_attribute = .toml; - } else if (str.eqlComptime("text")) { - type_attribute = .text; - } else if (str.eqlComptime("file")) { - type_attribute = .file; - } - } - } - } - } - } + import_options = try p.parseExpr(.comma); if (p.lexer.token == .t_comma) { - // "import('./foo.json', { assert: { type: 'json' } }, , )" + // "import('./foo.json', { assert: { type: 'json' } }, )" try p.lexer.next(); } } @@ -15051,18 +15514,20 @@ fn NewParser_( return p.newExpr(E.Import{ .expr = value, - .leading_interior_comments = comments, + // .leading_interior_comments = comments, .import_record_index = import_record_index, - .type_attribute = type_attribute, + .options = import_options, }, loc); } } + _ = comments; // TODO: leading_interior comments + return p.newExpr(E.Import{ .expr = value, - .type_attribute = type_attribute, - .leading_interior_comments = comments, + // .leading_interior_comments = comments, .import_record_index = std.math.maxInt(u32), + .options = import_options, }, loc); } @@ -15103,13 +15568,11 @@ fn NewParser_( var key_prop_i: i32 = -1; var flags = Flags.JSXElement.Bitset{}; var start_tag: ?ExprNodeIndex = null; - var can_be_inlined = false; // Fragments don't have props // Fragments of the form "React.Fragment" are not parsed as fragments. if (@as(JSXTag.TagType, tag.data) == .tag) { start_tag = tag.data.tag; - can_be_inlined = p.options.features.jsx_optimization_inline; var spread_loc: logger.Loc = logger.Loc.Empty; var props = ListManaged(G.Property).init(p.allocator); @@ -15136,8 +15599,6 @@ fn NewParser_( key_prop_i = i; } - can_be_inlined = can_be_inlined and special_prop != .ref; - const prop_name = p.newExpr(E.String{ .data = prop_name_literal }, key_range.loc); // Parse the value @@ -15161,7 +15622,6 @@ fn NewParser_( switch (p.lexer.token) { .t_dot_dot_dot => { try p.lexer.next(); - can_be_inlined = false; if (first_spread_prop_i == -1) first_spread_prop_i = i; spread_loc = p.lexer.loc(); @@ -15268,10 +15728,6 @@ fn NewParser_( try p.lexer.expected(.t_greater_than); } - if (can_be_inlined) { - flags.insert(.can_be_inlined); - } - return p.newExpr(E.JSXElement{ .tag = start_tag, .properties = properties, @@ -15346,10 +15802,6 @@ fn NewParser_( try p.lexer.expected(.t_greater_than); } - if (can_be_inlined) { - flags.insert(.can_be_inlined); - } - return p.newExpr(E.JSXElement{ .tag = end_tag.data.asExpr(), .children = ExprNodeList.fromList(children), @@ -15386,6 +15838,7 @@ fn NewParser_( p.declared_symbols.clearRetainingCapacity(); p.scopes_for_current_part.clearRetainingCapacity(); p.import_records_for_current_part.clearRetainingCapacity(); + p.import_symbol_property_uses.clearRetainingCapacity(); p.had_commonjs_named_exports_this_visit = false; @@ -15393,8 +15846,6 @@ fn NewParser_( var opts = PrependTempRefsOpts{}; var partStmts = ListManaged(Stmt).fromOwnedSlice(allocator, stmts); - // - try p.visitStmtsAndPrependTempRefs(&partStmts, &opts); // Insert any relocated variable statements now @@ -15427,11 +15878,12 @@ fn NewParser_( } if (partStmts.items.len > 0) { - const _stmts = partStmts.items; + const final_stmts = partStmts.items; try parts.append(js_ast.Part{ - .stmts = _stmts, + .stmts = final_stmts, .symbol_uses = p.symbol_uses, + .import_symbol_property_uses = p.import_symbol_property_uses, .declared_symbols = p.declared_symbols.toOwnedSlice(), .import_record_indices = bun.BabyList(u32).init( p.import_records_for_current_part.toOwnedSlice( @@ -15439,13 +15891,13 @@ fn NewParser_( ) catch unreachable, ), .scopes = try p.scopes_for_current_part.toOwnedSlice(p.allocator), - .can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(_stmts), + .can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(final_stmts), .tag = if (p.had_commonjs_named_exports_this_visit) js_ast.Part.Tag.commonjs_named_export else .none, }); p.symbol_uses = .{}; + p.import_symbol_property_uses = .{}; p.had_commonjs_named_exports_this_visit = false; } else if (p.declared_symbols.len() > 0 or p.symbol_uses.count() > 0) { - // if the part is dead, invalidate all the usage counts p.clearSymbolUsagesFromDeadPart(.{ .stmts = undefined, .declared_symbols = p.declared_symbols, .symbol_uses = p.symbol_uses }); p.declared_symbols.clearRetainingCapacity(); @@ -15504,22 +15956,25 @@ fn NewParser_( // can remove a SImport statement. Otherwise the import must be kept for // its side effects. .s_import => {}, + .s_class => |st| { if (!p.classCanBeRemovedIfUnused(&st.class)) { return false; } }, + .s_expr => |st| { if (st.does_not_affect_tree_shaking) { // Expressions marked with this are automatically generated and have // no side effects by construction. - break; + continue; } if (!p.exprCanBeRemovedIfUnused(&st.value)) { return false; } }, + .s_local => |st| { // "await" is a side effect because it affects code timing if (st.kind == .k_await_using) return false; @@ -15570,7 +16025,7 @@ fn NewParser_( } }, else => { - Global.panic("Unexpected type in export default: {any}", .{s2}); + Output.panic("Unexpected type in export default: {any}", .{s2}); }, } }, @@ -15581,7 +16036,10 @@ fn NewParser_( }, } }, + else => { + // Assume that all statements not explicitly special-cased here have side + // effects, and cannot be removed even if unused return false; }, } @@ -15602,12 +16060,12 @@ fn NewParser_( try p.visitStmts(stmts, opts.kind); // Prepend values for "this" and "arguments" - if (opts.fn_body_loc != null) { + if (opts.fn_body_loc) |fn_body_loc| { // Capture "this" if (p.fn_only_data_visit.this_capture_ref) |ref| { try p.temp_refs_to_declare.append(p.allocator, TempRef{ .ref = ref, - .value = p.newExpr(E.This{}, opts.fn_body_loc orelse p.panic("Internal error: Expected opts.fn_body_loc to exist", .{})), + .value = p.newExpr(E.This{}, fn_body_loc), }); } } @@ -15668,6 +16126,16 @@ fn NewParser_( var stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, body.stmts); var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body, .fn_body_loc = body.loc }; p.visitStmtsAndPrependTempRefs(&stmts, &temp_opts) catch unreachable; + + if (p.options.features.react_fast_refresh) { + const hook_storage = p.react_refresh.hook_ctx_storage orelse + unreachable; // caller did not init hook storage. any function can have react hooks! + + if (hook_storage.*) |*hook| { + p.handleReactRefreshPostVisitFunctionBody(&stmts, hook); + } + } + func.body = G.FnBody{ .stmts = stmts.items, .loc = body.loc }; p.popScope(); @@ -15675,6 +16143,7 @@ fn NewParser_( p.fn_or_arrow_data_visit = old_fn_or_arrow_data; p.fn_only_data_visit = old_fn_only_data; + return func; } @@ -15742,7 +16211,6 @@ fn NewParser_( // p.log.addRangeError(p.source, target.range, "Cannot use \"new.target\" here") catch unreachable; // } }, - .e_string => { // If you're using this, you're probably not using 0-prefixed legacy octal notation @@ -15763,7 +16231,6 @@ fn NewParser_( // return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EIdentifier{Ref: p.captureThis()}}, exprOut{} // } }, - .e_import_meta => { // TODO: delete import.meta might not work const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta; @@ -15801,33 +16268,39 @@ fn NewParser_( e_.ref = result.ref; // Handle assigning to a constant - if (in.assign_target != .none and p.symbols.items[result.ref.innerIndex()].kind == .constant) { - const r = js_lexer.rangeOfIdentifier(p.source, expr.loc); - var notes = p.allocator.alloc(logger.Data, 1) catch unreachable; - notes[0] = logger.Data{ - .text = std.fmt.allocPrint(p.allocator, "The symbol \"{s}\" was declared a constant here:", .{name}) catch unreachable, - .location = logger.Location.initOrNull(p.source, js_lexer.rangeOfIdentifier(p.source, result.declare_loc.?)), - }; + if (in.assign_target != .none) { + if (p.symbols.items[result.ref.innerIndex()].kind == .constant) { + const r = js_lexer.rangeOfIdentifier(p.source, expr.loc); + var notes = p.allocator.alloc(logger.Data, 1) catch unreachable; + notes[0] = logger.Data{ + .text = std.fmt.allocPrint(p.allocator, "The symbol \"{s}\" was declared a constant here:", .{name}) catch unreachable, + .location = logger.Location.initOrNull(p.source, js_lexer.rangeOfIdentifier(p.source, result.declare_loc.?)), + }; - const is_error = p.const_values.contains(result.ref) or p.options.bundle; - switch (is_error) { - true => p.log.addRangeErrorFmtWithNotes( - p.source, - r, - p.allocator, - notes, - "Cannot assign to \"{s}\" because it is a constant", - .{name}, - ) catch unreachable, + const is_error = p.const_values.contains(result.ref) or p.options.bundle; + switch (is_error) { + true => p.log.addRangeErrorFmtWithNotes( + p.source, + r, + p.allocator, + notes, + "Cannot assign to \"{s}\" because it is a constant", + .{name}, + ) catch unreachable, - false => p.log.addRangeErrorFmtWithNotes( - p.source, - r, - p.allocator, - notes, - "This assignment will throw because \"{s}\" is a constant", - .{name}, - ) catch unreachable, + false => p.log.addRangeErrorFmtWithNotes( + p.source, + r, + p.allocator, + notes, + "This assignment will throw because \"{s}\" is a constant", + .{name}, + ) catch unreachable, + } + } else if (p.exports_ref.eql(e_.ref)) { + // Assigning to `exports` in a CommonJS module must be tracked to undo the + // `module.exports` -> `exports` optimization. + p.commonjs_module_exports_assigned_deoptimized = true; } } @@ -15879,7 +16352,6 @@ fn NewParser_( .was_originally_identifier = true, }); }, - .e_jsx_element => |e_| { switch (comptime jsx_transform_type) { .react => { @@ -16025,185 +16497,54 @@ fn NewParser_( .value = e_.children.ptr[0], }) catch bun.outOfMemory(); } - // --- These must be done in all cases -- - - // Trivial elements can be inlined, removing the call to createElement or jsx() - if (p.options.features.jsx_optimization_inline and e_.flags.contains(.can_be_inlined)) { - // The output object should look like this: - // https://babeljs.io/repl/#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=false&spec=false&loose=false&code_lz=FAMwrgdgxgLglgewgAgLIE8DCCC2AHJAUwhgAoBvAIwEMAvAXwEplzhl3kAnQmMTlADwAxBAmQA-AIwAmAMxsOAFgCsANgEB6EQnEBuYPWBA&debug=false&forceAllTransforms=false&shippedProposals=true&circleciRepo=&evaluate=false&fileSize=true&timeTravel=false&sourceType=module&lineWrap=true&presets=react%2Ctypescript&prettier=true&targets=&version=7.18.4&externalPlugins=%40babel%2Fplugin-transform-flow-strip-types%407.16.7%2C%40babel%2Fplugin-transform-react-inline-elements%407.16.7&assumptions=%7B%22arrayLikeIsIterable%22%3Atrue%2C%22constantReexports%22%3Atrue%2C%22constantSuper%22%3Atrue%2C%22enumerableModuleMeta%22%3Atrue%2C%22ignoreFunctionLength%22%3Atrue%2C%22ignoreToPrimitiveHint%22%3Atrue%2C%22mutableTemplateObject%22%3Atrue%2C%22iterableIsArray%22%3Atrue%2C%22noClassCalls%22%3Atrue%2C%22noNewArrows%22%3Atrue%2C%22noDocumentAll%22%3Atrue%2C%22objectRestNoSymbols%22%3Atrue%2C%22privateFieldsAsProperties%22%3Atrue%2C%22pureGetters%22%3Atrue%2C%22setComputedProperties%22%3Atrue%2C%22setClassMethods%22%3Atrue%2C%22setSpreadProperties%22%3Atrue%2C%22setPublicClassFields%22%3Atrue%2C%22skipForOfIteratorClosing%22%3Atrue%2C%22superIsCallableConstructor%22%3Atrue%7D - // return { - // $$typeof: REACT_ELEMENT_TYPE, - // type: type, - // key: void 0 === key ? null : "" + key, - // ref: null, - // props: props, - // _owner: null - // }; - // - const key = if (maybe_key_value) |key_value| brk: { - // key: void 0 === key ? null : "" + key, - break :brk switch (key_value.data) { - .e_string => break :brk key_value, - .e_undefined, .e_null => p.newExpr(E.Null{}, key_value.loc), - else => p.newExpr(E.If{ - .test_ = p.newExpr(E.Binary{ - .left = p.newExpr(E.Undefined{}, key_value.loc), - .op = Op.Code.bin_strict_eq, - .right = key_value, - }, key_value.loc), - .yes = p.newExpr(E.Null{}, key_value.loc), - .no = p.newExpr( - E.Binary{ - .op = Op.Code.bin_add, - .left = p.newExpr(&E.String.empty, key_value.loc), - .right = key_value, - }, - key_value.loc, - ), - }, key_value.loc), - }; - } else p.newExpr(E.Null{}, expr.loc); - var jsx_element = p.allocator.alloc(G.Property, 6) catch unreachable; - const props_object = p.newExpr( - E.Object{ - .properties = G.Property.List.fromList(props), - .close_brace_loc = e_.close_tag_loc, - }, - expr.loc, - ); - const props_expression = brk: { - // we must check for default props - if (tag.data != .e_string) { - // We assume defaultProps is supposed to _not_ have side effects - // We do not support "key" or "ref" in defaultProps. - const defaultProps = p.newExpr( - E.Dot{ - .name = "defaultProps", - .name_loc = tag.loc, - .target = tag, - .can_be_removed_if_unused = true, - .call_can_be_unwrapped_if_unused = true, - }, - tag.loc, - ); - // props: MyComponent.defaultProps || {} - if (props.items.len == 0) { - break :brk p.newExpr(E.Binary{ .op = Op.Code.bin_logical_or, .left = defaultProps, .right = props_object }, defaultProps.loc); - } else { - var call_args = p.allocator.alloc(Expr, 2) catch unreachable; - call_args[0..2].* = .{ - props_object, - defaultProps, - }; - // __merge(props, MyComponent.defaultProps) - // originally, we always inlined here - // see https://twitter.com/jarredsumner/status/1534084541236686848 - // but, that breaks for defaultProps - // we assume that most components do not have defaultProps - // so __merge quickly checks if it needs to merge any props - // and if not, it passes along the props object - // this skips an extra allocation - break :brk p.callRuntime(tag.loc, "__merge", call_args); - } - } + // Either: + // jsxDEV(type, arguments, key, isStaticChildren, source, self) + // jsx(type, arguments, key) + const args = p.allocator.alloc(Expr, if (p.options.jsx.development) @as(usize, 6) else @as(usize, 2) + @as(usize, @intFromBool(maybe_key_value != null))) catch unreachable; + args[0] = tag; - break :brk props_object; - }; + args[1] = p.newExpr(E.Object{ + .properties = G.Property.List.fromList(props), + }, expr.loc); - jsx_element[0..6].* = - [_]G.Property{ - G.Property{ - .key = Expr{ .data = Prefill.Data.@"$$typeof", .loc = tag.loc }, - .value = p.runtimeIdentifier(tag.loc, "$$typeof"), - }, - G.Property{ - .key = Expr{ .data = Prefill.Data.type, .loc = tag.loc }, - .value = tag, - }, - G.Property{ - .key = Expr{ .data = Prefill.Data.key, .loc = key.loc }, - .value = key, - }, - // this is a de-opt - // any usage of ref should make it impossible for this code to be reached - G.Property{ - .key = Expr{ .data = Prefill.Data.ref, .loc = expr.loc }, - .value = p.newExpr(E.Null{}, expr.loc), - }, - G.Property{ - .key = Expr{ .data = Prefill.Data.props, .loc = expr.loc }, - .value = props_expression, - }, - G.Property{ - .key = Expr{ .data = Prefill.Data._owner, .loc = key.loc }, - .value = p.newExpr( - E.Null{}, - expr.loc, - ), + if (maybe_key_value) |key| { + args[2] = key; + } else if (p.options.jsx.development) { + // if (maybeKey !== undefined) + args[2] = Expr{ + .loc = expr.loc, + .data = .{ + .e_undefined = E.Undefined{}, }, }; + } - const output = p.newExpr( - E.Object{ - .properties = G.Property.List.init(jsx_element), - .close_brace_loc = e_.close_tag_loc, - }, - expr.loc, - ); - - return output; - } else { - // -- The typical jsx automatic transform happens here -- - - // Either: - // jsxDEV(type, arguments, key, isStaticChildren, source, self) - // jsx(type, arguments, key) - const args = p.allocator.alloc(Expr, if (p.options.jsx.development) @as(usize, 6) else @as(usize, 2) + @as(usize, @intFromBool(maybe_key_value != null))) catch unreachable; - args[0] = tag; - - args[1] = p.newExpr(E.Object{ - .properties = G.Property.List.fromList(props), - }, expr.loc); - - if (maybe_key_value) |key| { - args[2] = key; - } else if (p.options.jsx.development) { - // if (maybeKey !== undefined) - args[2] = Expr{ - .loc = expr.loc, - .data = .{ - .e_undefined = E.Undefined{}, - }, - }; - } - - if (p.options.jsx.development) { - // is the return type of the first child an array? - // It's dynamic - // Else, it's static - args[3] = Expr{ - .loc = expr.loc, - .data = .{ - .e_boolean = .{ - .value = is_static_jsx, - }, + if (p.options.jsx.development) { + // is the return type of the first child an array? + // It's dynamic + // Else, it's static + args[3] = Expr{ + .loc = expr.loc, + .data = .{ + .e_boolean = .{ + .value = is_static_jsx, }, - }; - - args[4] = p.newExpr(E.Undefined{}, expr.loc); - args[5] = Expr{ .data = Prefill.Data.This, .loc = expr.loc }; - } + }, + }; - return p.newExpr(E.Call{ - .target = p.jsxImportAutomatic(expr.loc, is_static_jsx), - .args = ExprNodeList.init(args), - // Enable tree shaking - .can_be_unwrapped_if_unused = !p.options.ignore_dce_annotations, - .was_jsx_element = true, - .close_paren_loc = e_.close_tag_loc, - }, expr.loc); + args[4] = p.newExpr(E.Undefined{}, expr.loc); + args[5] = Expr{ .data = Prefill.Data.This, .loc = expr.loc }; } + + return p.newExpr(E.Call{ + .target = p.jsxImportAutomatic(expr.loc, is_static_jsx), + .args = ExprNodeList.init(args), + // Enable tree shaking + .can_be_unwrapped_if_unused = !p.options.ignore_dce_annotations, + .was_jsx_element = true, + .close_paren_loc = e_.close_tag_loc, + }, expr.loc); } else { unreachable; } @@ -16211,7 +16552,6 @@ fn NewParser_( else => unreachable, } }, - .e_template => |e_| { if (e_.tag) |tag| { e_.tag = p.visitExpr(tag); @@ -16270,35 +16610,16 @@ fn NewParser_( return e_.fold(p.allocator, expr.loc); } }, + .e_binary => |e_| { - .inline_identifier => |id| { - const ref = p.macro.imports.get(id) orelse { - p.panic("Internal error: missing identifier from macro: {d}", .{id}); - }; - - if (!p.is_control_flow_dead) { - p.recordUsage(ref); - } - - return p.newExpr( - E.ImportIdentifier{ - .was_originally_identifier = false, - .ref = ref, - }, - expr.loc, - ); - }, - - .e_binary => |e_| { - - // The handling of binary expressions is convoluted because we're using - // iteration on the heap instead of recursion on the call stack to avoid - // stack overflow for deeply-nested ASTs. - var v = BinaryExpressionVisitor{ - .e = e_, - .loc = expr.loc, - .in = in, - .left_in = ExprIn{}, + // The handling of binary expressions is convoluted because we're using + // iteration on the heap instead of recursion on the call stack to avoid + // stack overflow for deeply-nested ASTs. + var v = BinaryExpressionVisitor{ + .e = e_, + .loc = expr.loc, + .in = in, + .left_in = ExprIn{}, }; // Everything uses a single stack to reduce allocation overhead. This stack @@ -16360,38 +16681,41 @@ fn NewParser_( return current; }, .e_index => |e_| { - const is_call_target = std.meta.activeTag(p.call_target) == .e_index and expr.data.e_index == p.call_target.e_index; - const is_delete_target = std.meta.activeTag(p.delete_target) == .e_index and expr.data.e_index == p.delete_target.e_index; - - if (p.options.features.minify_syntax) { - if (e_.index.data == .e_string and e_.index.data.e_string.isUTF8() and e_.index.data.e_string.isIdentifier(p.allocator)) { - const dot = p.newExpr( - E.Dot{ - .name = e_.index.data.e_string.slice(p.allocator), - .name_loc = e_.index.loc, - .target = e_.target, - .optional_chain = e_.optional_chain, - }, - expr.loc, - ); - - if (is_call_target) { - p.call_target = dot.data; - } + const is_call_target = p.call_target == .e_index and expr.data.e_index == p.call_target.e_index; + const is_delete_target = p.delete_target == .e_index and expr.data.e_index == p.delete_target.e_index; + + // "a['b']" => "a.b" + if (p.options.features.minify_syntax and + e_.index.data == .e_string and + e_.index.data.e_string.isUTF8() and + e_.index.data.e_string.isIdentifier(p.allocator)) + { + const dot = p.newExpr( + E.Dot{ + .name = e_.index.data.e_string.slice(p.allocator), + .name_loc = e_.index.loc, + .target = e_.target, + .optional_chain = e_.optional_chain, + }, + expr.loc, + ); - if (is_delete_target) { - p.delete_target = dot.data; - } + if (is_call_target) { + p.call_target = dot.data; + } - return p.visitExprInOut(dot, in); + if (is_delete_target) { + p.delete_target = dot.data; } + + return p.visitExprInOut(dot, in); } - const target = p.visitExprInOut(e_.target, ExprIn{ - // this is awkward due to a zig compiler bug - .has_chain_parent = (e_.optional_chain orelse js_ast.OptionalChain.start) == js_ast.OptionalChain.ccontinue, + const target_visited = p.visitExprInOut(e_.target, ExprIn{ + .has_chain_parent = e_.optional_chain == .continuation, }); - e_.target = target; + e_.target = target_visited; + switch (e_.index.data) { .e_private_identifier => |_private| { var private = _private; @@ -16423,80 +16747,102 @@ fn NewParser_( else => { const index = p.visitExpr(e_.index); e_.index = index; + + const unwrapped = e_.index.unwrapInlined(); + if (unwrapped.data == .e_string and + unwrapped.data.e_string.isUTF8()) + { + // "a['b' + '']" => "a.b" + // "enum A { B = 'b' }; a[A.B]" => "a.b" + if (p.options.features.minify_syntax and + unwrapped.data.e_string.isIdentifier(p.allocator)) + { + const dot = p.newExpr( + E.Dot{ + .name = unwrapped.data.e_string.slice(p.allocator), + .name_loc = unwrapped.loc, + .target = e_.target, + .optional_chain = e_.optional_chain, + }, + expr.loc, + ); + + if (is_call_target) { + p.call_target = dot.data; + } + + if (is_delete_target) { + p.delete_target = dot.data; + } + + return p.visitExprInOut(dot, in); + } + + // Handle property rewrites to ensure things + // like .e_import_identifier tracking works + // Reminder that this can only be done after + // `target` is visited. + if (p.maybeRewritePropertyAccess( + expr.loc, + e_.target, + unwrapped.data.e_string.data, + unwrapped.loc, + .{ + .is_call_target = is_call_target, + // .is_template_tag = is_template_tag, + .is_delete_target = is_delete_target, + .assign_target = in.assign_target, + }, + )) |rewrite| { + return rewrite; + } + } }, } - if (e_.optional_chain == null and e_.index.data == .e_string and e_.index.data.e_string.isUTF8()) { - const literal = e_.index.data.e_string.slice(p.allocator); - if (p.maybeRewritePropertyAccess( - expr.loc, - e_.target, - literal, - e_.index.loc, - .{ - .is_call_target = is_call_target, - // .is_template_tag = is_template_tag, - .is_delete_target = is_delete_target, - .assign_target = in.assign_target, - }, - )) |val| { - return val; - } - - // delete process.env["NODE_ENV"] - // shouldn't be transformed into - // delete undefined - if (!is_delete_target and !is_call_target and in.assign_target == .none) { - // We check for defines here as well - // esbuild doesn't do this - // In a lot of codebases, people will sometimes do: - // process.env["NODE_ENV"] - // Often not intentionally - // So we want to be able to detect this and still Do The Right Thing - if (p.define.dots.get(literal)) |parts| { - for (parts) |define| { - if (p.isDotDefineMatch(expr, define.parts)) { - if (!define.data.valueless) { - return p.valueForDefine(expr.loc, in.assign_target, is_delete_target, &define.data); + const target = e_.target.unwrapInlined(); + const index = e_.index.unwrapInlined(); + + if (p.options.features.minify_syntax) { + if (index.data.as(.e_number)) |number| { + if (number.value >= 0 and + number.value < std.math.maxInt(usize) and + @mod(number.value, 1) == 0) + { + // "foo"[2] -> "o" + if (target.data.as(.e_string)) |str| { + if (str.isUTF8()) { + const literal = str.slice(p.allocator); + const num: usize = index.data.e_number.toUsize(); + if (Environment.allow_assert) { + bun.assert(bun.strings.isAllASCII(literal)); } + if (num < literal.len) { + return p.newExpr(E.String{ .data = literal[num .. num + 1] }, expr.loc); + } + } + } else if (target.data.as(.e_array)) |array| { + // [x][0] -> x + if (array.items.len == 1 and number.value == 0) { + const inlined = target.data.e_array.items.at(0).*; + if (inlined.canBeInlinedFromPropertyAccess()) + return inlined; + } + + // ['a', 'b', 'c'][1] -> 'b' + const int: usize = @intFromFloat(number.value); + if (int < array.items.len and p.exprCanBeRemovedIfUnused(&target)) { + const inlined = target.data.e_array.items.at(int).*; + // ['a', , 'c'][1] -> undefined + if (inlined.data == .e_missing) return p.newExpr(E.Undefined{}, inlined.loc); + if (Environment.allow_assert) assert(inlined.canBeInlinedFromPropertyAccess()); + return inlined; } } } } - // "foo"[2] - } else if ((comptime FeatureFlags.inline_properties_in_transpiler) and - e_.optional_chain == null and - target.data == .e_string and - e_.index.data == .e_number and - target.data.e_string.isUTF8() and - e_.index.data.e_number.value >= 0) - { - const literal = target.data.e_string.slice(p.allocator); - const index = e_.index.data.e_number.toUsize(); - if (literal.len > index) { - return p.newExpr(E.String{ .data = literal[index .. index + 1] }, expr.loc); - } - } else if ((comptime FeatureFlags.inline_properties_in_transpiler) and - // Input: - // - // [123][0] - // - // Output: - // - // 123 - in.assign_target == .none and - !is_delete_target and - !is_call_target and - // target should already be on the stack - target.data == .e_array and - target.data.e_array.items.len == 1 and - e_.index.data == .e_number and - e_.index.data.e_number.value == 0.0 and - e_.optional_chain == null and - target.data.e_array.items.ptr[0].canBeInlinedFromPropertyAccess()) - { - return target.data.e_array.items.ptr[0]; } + // Create an error for assigning to an import namespace when bundling. Even // though this is a run-time error, we make it a compile-time error when // bundling because scope hoisting means these will no longer be run-time @@ -16520,9 +16866,9 @@ fn NewParser_( .e_unary => |e_| { switch (e_.op) { .un_typeof => { - const id_before = std.meta.activeTag(e_.value.data) == Expr.Tag.e_identifier; + const id_before = e_.value.data == .e_identifier; e_.value = p.visitExprInOut(e_.value, ExprIn{ .assign_target = e_.op.unaryAssignTarget() }); - const id_after = std.meta.activeTag(e_.value.data) == Expr.Tag.e_identifier; + const id_after = e_.value.data == .e_identifier; // The expression "typeof (0, x)" must not become "typeof x" if "x" // is unbound because that could suppress a ReferenceError from "x" @@ -16534,6 +16880,11 @@ fn NewParser_( ); } + if (e_.value.data == .e_require_call_target) { + p.ignoreUsageOfRuntimeRequire(); + return p.newExpr(E.String{ .data = "function" }, expr.loc); + } + if (SideEffects.typeof(e_.value.data)) |typeof| { return p.newExpr(E.String{ .data = typeof }, expr.loc); } @@ -16545,7 +16896,6 @@ fn NewParser_( e_.value = p.visitExprInOut(e_.value, ExprIn{ .assign_target = e_.op.unaryAssignTarget() }); // Post-process the unary expression - switch (e_.op) { .un_not => { if (p.options.features.minify_syntax) @@ -16560,6 +16910,19 @@ fn NewParser_( if (e_.value.maybeSimplifyNot(p.allocator)) |exp| { return exp; } + if (e_.value.data == .e_import_meta_main) { + e_.value.data.e_import_meta_main.inverted = !e_.value.data.e_import_meta_main.inverted; + return e_.value; + } + } + }, + .un_cpl => { + if (p.should_fold_typescript_constant_expressions) { + if (SideEffects.toNumber(e_.value.data)) |value| { + return p.newExpr(E.Number{ + .value = @floatFromInt(~floatToInt32(value)), + }, expr.loc); + } } }, .un_void => { @@ -16733,7 +17096,7 @@ fn NewParser_( p.is_control_flow_dead = old; if (side_effects.side_effects == .could_have_side_effects) { - return Expr.joinWithComma(SideEffects.simpifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.yes, p.allocator); + return Expr.joinWithComma(SideEffects.simplifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.yes, p.allocator); } // "(1 ? fn : 2)()" => "fn()" @@ -16754,7 +17117,7 @@ fn NewParser_( // "(a, false) ? b : c" => "a, c" if (side_effects.side_effects == .could_have_side_effects) { - return Expr.joinWithComma(SideEffects.simpifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.no, p.allocator); + return Expr.joinWithComma(SideEffects.simplifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.no, p.allocator); } // "(1 ? fn : 2)()" => "fn()" @@ -16830,7 +17193,7 @@ fn NewParser_( var has_proto = false; for (e_.properties.slice()) |*property| { if (property.kind != .spread) { - property.key = p.visitExpr(property.key orelse Global.panic("Expected property key", .{})); + property.key = p.visitExpr(property.key orelse Output.panic("Expected property key", .{})); const key = property.key.?; // Forbid duplicate "__proto__" properties according to the specification if (!property.flags.contains(.is_computed) and @@ -16887,22 +17250,45 @@ fn NewParser_( } }, .e_import => |e_| { - const state = TransposeState{ - // we must check that the await_target is an e_import or it will crash - // example from next.js where not checking causes a panic: - // ``` - // const { - // normalizeLocalePath, - // } = require('../shared/lib/i18n/normalize-locale-path') as typeof import('../shared/lib/i18n/normalize-locale-path') - // ``` - .is_await_target = if (p.await_target != null) p.await_target.? == .e_import and p.await_target.?.e_import == e_ else false, - .is_then_catch_target = p.then_catch_chain.has_catch and std.meta.activeTag(p.then_catch_chain.next_target) == .e_import and expr.data.e_import == p.then_catch_chain.next_target.e_import, - .loc = e_.expr.loc, - .type_attribute = e_.type_attribute, - }; + // We want to forcefully fold constants inside of imports + // even when minification is disabled, so that if we have an + // import based on a string template, it does not cause a + // bundle error. This is especially relevant for bundling NAPI + // modules with 'bun build --compile': + // + // const binding = await import(`./${process.platform}-${process.arch}.node`); + // + const prev_should_fold_typescript_constant_expressions = true; + defer p.should_fold_typescript_constant_expressions = prev_should_fold_typescript_constant_expressions; + p.should_fold_typescript_constant_expressions = true; e_.expr = p.visitExpr(e_.expr); - return p.import_transposer.maybeTransposeIf(e_.expr, state); + e_.options = p.visitExpr(e_.options); + + // Import transposition is able to duplicate the options structure, so + // only perform it if the expression is side effect free. + // + // TODO: make this more like esbuild by emitting warnings that explain + // why this import was not analyzed. (see esbuild 'unsupported-dynamic-import') + if (p.exprCanBeRemovedIfUnused(&e_.options)) { + const state = TransposeState{ + .is_await_target = if (p.await_target) |await_target| + await_target == .e_import and await_target.e_import == e_ + else + false, + + .is_then_catch_target = p.then_catch_chain.has_catch and + p.then_catch_chain.next_target == .e_import and + expr.data.e_import == p.then_catch_chain.next_target.e_import, + + .import_options = e_.options, + + .loc = e_.expr.loc, + .import_record_tag = e_.importRecordTag(), + }; + + return p.import_transposer.maybeTransposeIf(e_.expr, &state); + } }, .e_call => |e_| { p.call_target = e_.target.data; @@ -16914,8 +17300,8 @@ fn NewParser_( }; const target_was_identifier_before_visit = e_.target.data == .e_identifier; - e_.target = p.visitExprInOut(e_.target, ExprIn{ - .has_chain_parent = (e_.optional_chain orelse js_ast.OptionalChain.start) == .ccontinue, + e_.target = p.visitExprInOut(e_.target, .{ + .has_chain_parent = e_.optional_chain == .continuation, }); // Copy the call side effect flag over if this is a known target @@ -16969,8 +17355,23 @@ fn NewParser_( { const old_ce = p.options.ignore_dce_annotations; defer p.options.ignore_dce_annotations = old_ce; - if (is_macro_ref) + const old_should_fold_typescript_constant_expressions = p.should_fold_typescript_constant_expressions; + defer p.should_fold_typescript_constant_expressions = old_should_fold_typescript_constant_expressions; + + // We want to forcefully fold constants inside of + // certain calls even when minification is disabled, so + // that if we have an import based on a string template, + // it does not cause a bundle error. This is relevant for + // macros, as they require constant known values, but also + // for `require` and `require.resolve`, as they go through + // the module resolver. + if (is_macro_ref or + e_.target.data == .e_require_call_target or + e_.target.data == .e_require_resolve_call_target) + { p.options.ignore_dce_annotations = true; + p.should_fold_typescript_constant_expressions = true; + } for (e_.args.slice()) |*arg| { arg.* = p.visitExpr(arg.*); @@ -16986,17 +17387,18 @@ fn NewParser_( if (e_.args.len == 1) { const first = e_.args.first_(); const state = TransposeState{ - .is_require_immediately_assigned_to_decl = in.is_immediately_assigned_to_decl and first.data == .e_string, + .is_require_immediately_assigned_to_decl = in.is_immediately_assigned_to_decl and + first.data == .e_string, }; switch (first.data) { .e_string => { // require(FOO) => require(FOO) - return p.transposeRequire(first, state); + return p.transposeRequire(first, &state); }, .e_if => { // require(FOO ? '123' : '456') => FOO ? require('123') : require('456') // This makes static analysis later easier - return p.require_transposer.tranposeKnownToBeIf(first, state); + return p.require_transposer.transposeKnownToBeIf(first, &state); }, else => {}, } @@ -17042,7 +17444,7 @@ fn NewParser_( // => // FOO ? require.resolve('123') : require.resolve('456') // This makes static analysis later easier - return p.require_resolve_transposer.tranposeKnownToBeIf(first, e_.target); + return p.require_resolve_transposer.transposeKnownToBeIf(first, e_.target); }, else => {}, } @@ -17075,8 +17477,7 @@ fn NewParser_( const copied = Expr{ .loc = expr.loc, .data = .{ .e_call = e_ } }; const start_error_count = p.log.msgs.items.len; p.macro_call_count += 1; - const macro_result = - p.options.macro_context.call( + const macro_result = p.options.macro_context.call( record.path.text, p.source.path.sourceDir(), p.log, @@ -17101,11 +17502,30 @@ fn NewParser_( } } + // In fast refresh, any function call that looks like a hook (/^use[A-Z]/) is a + // hook, even if it is not the value of `SExpr` or `SLocal`. It can be anywhere + // in the function call. This makes sense for some weird situations with `useCallback`, + // where it is not assigned to a variable. + // + // When we see a hook call, we need to hash it, and then mark a flag so that if + // it is assigned to a variable, that variable also get's hashed. + if (p.options.features.react_fast_refresh) try_record_hook: { + const original_name = switch (e_.target.data) { + inline .e_identifier, + .e_import_identifier, + .e_commonjs_export_identifier, + => |id| p.symbols.items[id.ref.innerIndex()].original_name, + .e_dot => |dot| dot.name, + else => break :try_record_hook, + }; + if (!ReactRefresh.isHookName(original_name)) break :try_record_hook; + p.handleReactRefreshHookCall(e_, original_name); + } + return expr; }, .e_new => |e_| { e_.target = p.visitExpr(e_.target); - // p.warnA for (e_.args.slice()) |*arg| { arg.* = p.visitExpr(arg.*); @@ -17143,8 +17563,13 @@ fn NewParser_( }); p.pushScopeForVisitPass(.function_body, e_.body.loc) catch unreachable; + var react_hook_data: ?ReactRefresh.HookContext = null; + const prev = p.react_refresh.hook_ctx_storage; + defer p.react_refresh.hook_ctx_storage = prev; + p.react_refresh.hook_ctx_storage = &react_hook_data; + var stmts_list = ListManaged(Stmt).fromOwnedSlice(p.allocator, dupe); - var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body }; + var temp_opts = PrependTempRefsOpts{ .kind = .fn_body }; p.visitStmtsAndPrependTempRefs(&stmts_list, &temp_opts) catch unreachable; p.allocator.free(e_.body.stmts); e_.body.stmts = stmts_list.items; @@ -17153,16 +17578,42 @@ fn NewParser_( p.fn_only_data_visit.is_inside_async_arrow_fn = old_inside_async_arrow_fn; p.fn_or_arrow_data_visit = std.mem.bytesToValue(@TypeOf(p.fn_or_arrow_data_visit), &old_fn_or_arrow_data); + + if (react_hook_data) |*hook| try_mark_hook: { + const stmts = p.nearest_stmt_list orelse break :try_mark_hook; + stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)) catch bun.outOfMemory(); + + p.handleReactRefreshPostVisitFunctionBody(&stmts_list, hook); + e_.body.stmts = stmts_list.items; + + return p.getReactRefreshHookSignalInit(hook, expr); + } }, .e_function => |e_| { if (p.is_revisit_for_substitution) { return expr; } + var react_hook_data: ?ReactRefresh.HookContext = null; + const prev = p.react_refresh.hook_ctx_storage; + defer p.react_refresh.hook_ctx_storage = prev; + p.react_refresh.hook_ctx_storage = &react_hook_data; + e_.func = p.visitFunc(e_.func, expr.loc); + + var final_expr = expr; + + if (react_hook_data) |*hook| try_mark_hook: { + const stmts = p.nearest_stmt_list orelse break :try_mark_hook; + stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)) catch bun.outOfMemory(); + final_expr = p.getReactRefreshHookSignalInit(hook, expr); + } + if (e_.func.name) |name| { - return p.keepExprSymbolName(expr, p.symbols.items[name.ref.?.innerIndex()].original_name); + final_expr = p.keepExprSymbolName(final_expr, p.symbols.items[name.ref.?.innerIndex()].original_name); } + + return final_expr; }, .e_class => |e_| { if (p.is_revisit_for_substitution) { @@ -17188,6 +17639,16 @@ fn NewParser_( } } + fn ignoreUsageOfRuntimeRequire(p: *P) void { + if (!p.options.features.use_import_meta_require and + p.options.features.allow_runtime) + { + bun.assert(p.runtime_imports.__require != null); + p.ignoreUsage(p.runtimeIdentifierRef(logger.Loc.Empty, "__require")); + p.symbols.items[p.require_ref.innerIndex()].use_count_estimate -|= 1; + } + } + inline fn valueForRequire(p: *P, loc: logger.Loc) Expr { bun.assert(!p.isSourceRuntime()); return Expr{ @@ -17198,6 +17659,32 @@ fn NewParser_( }; } + inline fn valueForImportMetaMain(p: *P, inverted: bool, loc: logger.Loc) Expr { + if (p.options.import_meta_main_value) |known| { + return .{ .loc = loc, .data = .{ .e_boolean = .{ .value = if (inverted) !known else known } } }; + } else { + // Node.js does not have import.meta.main, so we end up lowering + // this to `require.main === module`, but with the ESM format, + // both `require` and `module` are not present, so the code + // generation we need is: + // + // import { createRequire } from "node:module"; + // var __require = createRequire(import.meta.url); + // var import_meta_main = __require.main === __require.module; + // + // The printer can handle this for us, but we need to reference + // a handle to the `__require` function. + if (p.options.lower_import_meta_main_for_node_js) { + p.recordUsageOfRuntimeRequire(); + } + + return .{ + .loc = loc, + .data = .{ .e_import_meta_main = .{ .inverted = inverted } }, + }; + } + } + fn visitArgs(p: *P, args: []G.Arg, opts: VisitArgsOpts) void { const strict_loc = fnBodyContainsUseStrict(opts.body); const has_simple_args = isSimpleParameterList(args, opts.has_rest_arg); @@ -17356,6 +17843,8 @@ fn NewParser_( return true; }, + .e_inlined_enum => |e| return p.exprCanBeRemovedIfUnused(&e.value), + .e_dot => |ex| { return ex.can_be_removed_if_unused; }, @@ -17363,6 +17852,8 @@ fn NewParser_( return p.classCanBeRemovedIfUnused(ex); }, .e_identifier => |ex| { + bun.assert(!ex.ref.isSourceContentsSlice()); // was not visited + if (ex.must_keep_due_to_with_stmt) { return false; } @@ -17452,7 +17943,6 @@ fn NewParser_( return true; }, .e_call => |ex| { - // A call that has been marked "__PURE__" can be removed if all arguments // can be removed. The annotation causes us to ignore the target. if (ex.can_be_unwrapped_if_unused) { @@ -17846,11 +18336,18 @@ fn NewParser_( } fn selectLocalKind(p: *P, kind: S.Local.Kind) S.Local.Kind { + // When using Kit's HMR implementation, we need to preserve the local kind + // if possible, as more efficient code can be generated if something is known + // not to be an ESM live binding. + if (p.options.features.hot_module_reloading) { + return kind; + } + // Use "var" instead of "let" and "const" if the variable declaration may // need to be separated from the initializer. This allows us to safely move // this declaration into a nested scope. if ((p.options.bundle or p.will_wrap_module_in_try_catch_for_using) and - (p.current_scope.parent == null and !kind.isUsing())) + p.current_scope.parent == null and !kind.isUsing()) { return .k_var; } @@ -17901,24 +18398,6 @@ fn NewParser_( return .{ .stmt = p.s(S.SExpr{ .value = value }, value.loc), .ok = true }; } - // fn maybeInlineMacroObject(p: *P, decl: *G.Decl, macro: Expr) void { - // if (decl.value == null) return; - // switch (decl.binding.data) { - // .b_identifier => |ident| { - // if (macro.get(p.loadNameFromRef(ident.ref))) |val| { - // decl - // } - // } - // } - // } - // if (comptime allow_macros) { - // if (p.macro_call_count and data.decls[i].value != null and - // data.decls[i].value.?.data == .e_object and data.decls[i].value.?.data.e_object.was_originally_macro) - // { - // p.maybeInlineMacroObject(&data.decls[i], data.decls[i].value.?); - // } - // } - // EDot nodes represent a property access. This function may return an // expression to replace the property access with. It assumes that the // target of the EDot expression has already been visited. @@ -17932,7 +18411,6 @@ fn NewParser_( ) ?Expr { switch (target.data) { .e_identifier => |id| { - // Rewrite property accesses on explicit namespace imports as an identifier. // This lets us replace them easily in the printer to rebind them to // something else without paying the cost of a whole-tree traversal during @@ -17977,7 +18455,15 @@ fn NewParser_( name_loc, E.Identifier{ .ref = ref }, name, - identifier_opts, + .{ + .assign_target = identifier_opts.assign_target, + .is_call_target = identifier_opts.is_call_target, + .is_delete_target = identifier_opts.is_delete_target, + + // If this expression is used as the target of a call expression, make + // sure the value of "this" is preserved. + .was_originally_identifier = false, + }, ); } } @@ -17990,6 +18476,9 @@ fn NewParser_( p.ignoreUsage(p.module_ref); return p.valueForRequire(name_loc); } else if (!p.commonjs_named_exports_deoptimized and strings.eqlComptime(name, "exports")) { + if (identifier_opts.assign_target != .none) { + p.commonjs_module_exports_assigned_deoptimized = true; + } // Detect if we are doing // @@ -18069,7 +18558,7 @@ fn NewParser_( for (props) |prop| { const key = prop.key.?.data.e_string.string(p.allocator) catch unreachable; const visited_value = p.visitExpr(prop.value.?); - const value = SideEffects.simpifyUnusedExpr(p, visited_value) orelse visited_value; + const value = SideEffects.simplifyUnusedExpr(p, visited_value) orelse visited_value; // We are doing `module.exports = { ... }` // lets rewrite it to a series of what will become export assignments @@ -18155,7 +18644,7 @@ fn NewParser_( } // rewrite `module.exports` to `exports` - return p.newExpr(E.Identifier{ .ref = p.exports_ref }, name_loc); + return .{ .data = .e_module_dot_exports, .loc = name_loc }; } else if (p.options.bundle and strings.eqlComptime(name, "id") and identifier_opts.assign_target == .none) { // inline module.id p.ignoreUsage(p.module_ref); @@ -18171,7 +18660,7 @@ fn NewParser_( } } - if (comptime FeatureFlags.unwrap_commonjs_to_esm) { + if (p.shouldUnwrapCommonJSToESM()) { if (!p.is_control_flow_dead and id.ref.eql(p.exports_ref)) { if (!p.commonjs_named_exports_deoptimized) { if (identifier_opts.is_delete_target) { @@ -18208,28 +18697,28 @@ fn NewParser_( name_loc, ); } else if (p.options.features.commonjs_at_runtime and identifier_opts.assign_target != .none) { - // Record this CommonJS export name for use later. - _ = p.commonjs_export_names.getOrPut(p.allocator, name) catch unreachable; + p.has_commonjs_export_names = true; } } } - // If this is a known enum value, inline the value of the enum - if (is_typescript_enabled) { - if (p.known_enum_values.get(id.ref)) |enum_value_map| { - if (enum_value_map.get(name)) |enum_value| { - return p.newExpr(E.Number{ .value = enum_value }, loc); - } - } + // Handle references to namespaces or namespace members + if (p.ts_namespace.expr == .e_identifier and + id.ref.eql(p.ts_namespace.expr.e_identifier.ref) and + identifier_opts.assign_target == .none and + !identifier_opts.is_delete_target) + { + return p.maybeRewritePropertyAccessForNamespace(name, &target, loc, name_loc); } }, + // TODO: e_inlined_enum -> .e_string -> "length" should inline the length .e_string => |str| { // Disable until https://github.com/oven-sh/bun/issues/4217 is fixed if (comptime FeatureFlags.minify_javascript_string_length) { if (p.options.features.minify_syntax) { // minify "long-string".length to 11 if (strings.eqlComptime(name, "length")) { - return p.newExpr(E.Number{ .value = @as(f64, @floatFromInt(str.javascriptLength())) }, loc); + return p.newExpr(E.Number{ .value = @floatFromInt(str.javascriptLength()) }, loc); } } } @@ -18237,7 +18726,6 @@ fn NewParser_( .e_object => |obj| { if (comptime FeatureFlags.inline_properties_in_transpiler) { if (p.options.features.minify_syntax) { - // // Rewrite a property access like this: // { f: () => {} }.f // To: @@ -18255,7 +18743,8 @@ fn NewParser_( prop.flags.count() == 0 and prop.key != null and prop.key.?.data == .e_string and - prop.key.?.data.e_string.eql([]const u8, name)) + prop.key.?.data.e_string.eql([]const u8, name) and + !bun.strings.eqlComptime(name, "__proto__")) { return prop.value.?; } @@ -18276,6 +18765,90 @@ fn NewParser_( target.loc, ); } + + if (strings.eqlComptime(name, "main")) { + return p.valueForImportMetaMain(false, target.loc); + } + }, + .e_require_call_target => { + if (strings.eqlComptime(name, "main")) { + return .{ .loc = loc, .data = .e_require_main }; + } + }, + .e_import_identifier => |id| { + // Symbol uses due to a property access off of an imported symbol are tracked + // specially. This lets us do tree shaking for cross-file TypeScript enums. + if (p.options.bundle and !p.is_control_flow_dead) { + const use = p.symbol_uses.getPtr(id.ref).?; + use.count_estimate -|= 1; + // note: this use is not removed as we assume it exists later + + // Add a special symbol use instead + const gop = p.import_symbol_property_uses.getOrPutValue( + p.allocator, + id.ref, + .{}, + ) catch bun.outOfMemory(); + const inner_use = gop.value_ptr.getOrPutValue( + p.allocator, + name, + .{}, + ) catch bun.outOfMemory(); + inner_use.value_ptr.count_estimate += 1; + } + }, + inline .e_dot, .e_index => |data, tag| { + if (p.ts_namespace.expr == tag and + data == @field(p.ts_namespace.expr, @tagName(tag)) and + identifier_opts.assign_target == .none and + !identifier_opts.is_delete_target) + { + return p.maybeRewritePropertyAccessForNamespace(name, &target, loc, name_loc); + } + }, + .e_module_dot_exports => { + if (p.shouldUnwrapCommonJSToESM()) { + if (!p.is_control_flow_dead) { + if (!p.commonjs_named_exports_deoptimized) { + if (identifier_opts.is_delete_target) { + p.deoptimizeCommonJSNamedExports(); + return null; + } + + const named_export_entry = p.commonjs_named_exports.getOrPut(p.allocator, name) catch unreachable; + if (!named_export_entry.found_existing) { + const new_ref = p.newSymbol( + .other, + std.fmt.allocPrint(p.allocator, "${any}", .{bun.fmt.fmtIdentifier(name)}) catch unreachable, + ) catch unreachable; + p.module_scope.generated.push(p.allocator, new_ref) catch unreachable; + named_export_entry.value_ptr.* = .{ + .loc_ref = LocRef{ + .loc = name_loc, + .ref = new_ref, + }, + .needs_decl = true, + }; + if (p.commonjs_named_exports_needs_conversion == std.math.maxInt(u32)) + p.commonjs_named_exports_needs_conversion = @as(u32, @truncate(p.commonjs_named_exports.count() - 1)); + } + + const ref = named_export_entry.value_ptr.*.loc_ref.ref.?; + p.recordUsage(ref); + + return p.newExpr( + E.CommonJSExportIdentifier{ + .ref = ref, + // Record this as from module.exports + .base = .module_dot_exports, + }, + name_loc, + ); + } else if (p.options.features.commonjs_at_runtime and identifier_opts.assign_target != .none) { + p.has_commonjs_export_names = true; + } + } + } }, else => {}, } @@ -18283,6 +18856,63 @@ fn NewParser_( return null; } + fn maybeRewritePropertyAccessForNamespace( + p: *P, + name: string, + target: *const Expr, + loc: logger.Loc, + name_loc: logger.Loc, + ) ?Expr { + if (p.ts_namespace.map.?.get(name)) |value| { + switch (value.data) { + .enum_number => |num| { + p.ignoreUsageOfIdentifierInDotChain(target.*); + return p.wrapInlinedEnum( + .{ .loc = loc, .data = .{ .e_number = .{ .value = num } } }, + name, + ); + }, + + .enum_string => |str| { + p.ignoreUsageOfIdentifierInDotChain(target.*); + return p.wrapInlinedEnum( + .{ .loc = loc, .data = .{ .e_string = str } }, + name, + ); + }, + + .namespace => |namespace| { + // If this isn't a constant, return a clone of this property access + // but with the namespace member data associated with it so that + // more property accesses off of this property access are recognized. + const expr = if (js_lexer.isIdentifier(name)) + p.newExpr(E.Dot{ + .target = target.*, + .name = name, + .name_loc = name_loc, + }, loc) + else + p.newExpr(E.Dot{ + .target = target.*, + .name = name, + .name_loc = name_loc, + }, loc); + + p.ts_namespace = .{ + .expr = expr.data, + .map = namespace, + }; + + return expr; + }, + + else => {}, + } + } + + return null; + } + pub fn ignoreUsage(p: *P, ref: Ref) void { if (!p.is_control_flow_dead and !p.is_revisit_for_substitution) { if (comptime Environment.allow_assert) assert(@as(usize, ref.innerIndex()) < p.symbols.items.len); @@ -18300,6 +18930,30 @@ fn NewParser_( // the value is ignored because that's what the TypeScript compiler does. } + pub fn ignoreUsageOfIdentifierInDotChain(p: *P, expr: Expr) void { + var current = expr; + while (true) { + switch (current.data) { + .e_identifier => |id| { + p.ignoreUsage(id.ref); + }, + .e_dot => |dot| { + current = dot.target; + continue; + }, + .e_index => |index| { + if (index.index.isString()) { + current = index.target; + continue; + } + }, + else => return, + } + + return; + } + } + fn visitAndAppendStmt(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt) anyerror!void { // By default any statement ends the const local prefix const was_after_after_const_local_prefix = p.current_scope.is_after_const_local_prefix; @@ -18307,7 +18961,6 @@ fn NewParser_( switch (stmt.data) { // These don't contain anything to traverse - .s_debugger, .s_empty, .s_comment => { p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; }, @@ -18456,7 +19109,6 @@ fn NewParser_( } }, .s_export_star => |data| { - // "export * from 'path'" const name = p.loadNameFromRef(data.namespace_ref); data.namespace_ref = try p.newSymbol(.other, name); @@ -18580,8 +19232,24 @@ fn NewParser_( name = js_ast.ClauseItem.default_alias; } + var react_hook_data: ?ReactRefresh.HookContext = null; + const prev = p.react_refresh.hook_ctx_storage; + defer p.react_refresh.hook_ctx_storage = prev; + p.react_refresh.hook_ctx_storage = &react_hook_data; + func.func = p.visitFunc(func.func, func.func.open_parens_loc); + if (react_hook_data) |*hook| { + stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)) catch bun.outOfMemory(); + + data.value = .{ + .expr = p.getReactRefreshHookSignalInit(hook, p.newExpr( + E.Function{ .func = func.func }, + stmt.loc, + )), + }; + } + if (p.is_control_flow_dead) { return; } @@ -18658,13 +19326,10 @@ fn NewParser_( } }, .s_export_equals => |data| { - // "module.exports = value" stmts.append( Stmt.assign( - p.@"module.exports"( - stmt.loc, - ), + p.@"module.exports"(stmt.loc), p.visitExpr(data.value), ), ) catch unreachable; @@ -18673,7 +19338,7 @@ fn NewParser_( }, .s_break => |data| { if (data.label) |*label| { - const name = p.loadNameFromRef(label.ref orelse p.panic("Expected label to have a ref", .{})); + const name = p.loadNameFromRef(label.ref orelse p.panicLoc("Expected label to have a ref", .{}, label.loc)); const res = p.findLabelSymbol(label.loc, name); if (res.found) { label.ref = res.ref; @@ -18687,7 +19352,7 @@ fn NewParser_( }, .s_continue => |data| { if (data.label) |*label| { - const name = p.loadNameFromRef(label.ref orelse p.panic("Expected continue label to have a ref", .{})); + const name = p.loadNameFromRef(label.ref orelse p.panicLoc("Expected continue label to have a ref", .{}, label.loc)); const res = p.findLabelSymbol(label.loc, name); label.ref = res.ref; if (res.found and !res.is_loop) { @@ -18778,15 +19443,35 @@ fn NewParser_( return; } } - }, - .s_expr => |data| { - const should_trim_primitive = p.options.features.dead_code_elimination and + + try stmts.append(stmt.*); + + if (p.options.features.react_fast_refresh and p.current_scope == p.module_scope) { + for (data.decls.slice()) |decl| try_register: { + const val = decl.value orelse break :try_register; + switch (val.data) { + .e_arrow, .e_function => {}, + else => break :try_register, + } + const id = switch (decl.binding.data) { + .b_identifier => |id| id.ref, + else => break :try_register, + }; + const original_name = p.symbols.items[id.innerIndex()].original_name; + try p.handleReactRefreshRegister(stmts, original_name, id); + } + } + + return; + }, + .s_expr => |data| { + const should_trim_primitive = p.options.features.dead_code_elimination and (p.options.features.minify_syntax and data.value.isPrimitiveLiteral()); p.stmt_expr_value = data.value.data; defer p.stmt_expr_value = .{ .e_missing = .{} }; const is_top_level = p.current_scope == p.module_scope; - if (comptime FeatureFlags.unwrap_commonjs_to_esm) { + if (p.shouldUnwrapCommonJSToESM()) { p.commonjs_named_exports_needs_conversion = if (is_top_level) std.math.maxInt(u32) else @@ -18800,9 +19485,9 @@ fn NewParser_( } // simplify unused - data.value = SideEffects.simpifyUnusedExpr(p, data.value) orelse return; + data.value = SideEffects.simplifyUnusedExpr(p, data.value) orelse return; - if (comptime FeatureFlags.unwrap_commonjs_to_esm) { + if (p.shouldUnwrapCommonJSToESM()) { if (is_top_level) { if (data.value.data == .e_binary) { const to_convert = p.commonjs_named_exports_needs_conversion; @@ -18883,7 +19568,6 @@ fn NewParser_( data.value = p.visitExpr(data.value); }, .s_return => |data| { - // Forbid top-level return inside modules with ECMAScript-style exports if (p.fn_or_arrow_data_visit.is_outside_fn_or_arrow) { const where = where: { @@ -19012,7 +19696,7 @@ fn NewParser_( if (data.no == null or !SideEffects.shouldKeepStmtInDeadControlFlow(p, data.no.?, p.allocator)) { if (effects.side_effects == .could_have_side_effects) { // Keep the condition if it could have side effects (but is still known to be truthy) - if (SideEffects.simpifyUnusedExpr(p, data.test_)) |test_| { + if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| { stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable; } } @@ -19026,7 +19710,7 @@ fn NewParser_( if (!SideEffects.shouldKeepStmtInDeadControlFlow(p, data.yes, p.allocator)) { if (effects.side_effects == .could_have_side_effects) { // Keep the condition if it could have side effects (but is still known to be truthy) - if (SideEffects.simpifyUnusedExpr(p, data.test_)) |test_| { + if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| { stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable; } } @@ -19273,44 +19957,58 @@ fn NewParser_( } } + var react_hook_data: ?ReactRefresh.HookContext = null; + const prev = p.react_refresh.hook_ctx_storage; + defer p.react_refresh.hook_ctx_storage = prev; + p.react_refresh.hook_ctx_storage = &react_hook_data; + data.func = p.visitFunc(data.func, data.func.open_parens_loc); + const name_ref = data.func.name.?.ref.?; + bun.assert(name_ref.tag == .symbol); + const name_symbol = &p.symbols.items[name_ref.innerIndex()]; + const original_name = name_symbol.original_name; + // Handle exporting this function from a namespace if (data.func.flags.contains(.is_export) and p.enclosing_namespace_arg_ref != null) { data.func.flags.remove(.is_export); - const enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref orelse unreachable; - stmts.ensureUnusedCapacity(3) catch unreachable; + const enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref orelse bun.outOfMemory(); + stmts.ensureUnusedCapacity(3) catch bun.outOfMemory(); stmts.appendAssumeCapacity(stmt.*); stmts.appendAssumeCapacity(Stmt.assign( p.newExpr(E.Dot{ .target = p.newExpr(E.Identifier{ .ref = enclosing_namespace_arg_ref }, stmt.loc), - .name = p.loadNameFromRef(data.func.name.?.ref.?), + .name = original_name, .name_loc = data.func.name.?.loc, }, stmt.loc), p.newExpr(E.Identifier{ .ref = data.func.name.?.ref.? }, data.func.name.?.loc), )); } else if (!mark_as_dead) { - if (p.symbols.items[data.func.name.?.ref.?.innerIndex()].remove_overwritten_function_declaration) { + if (name_symbol.remove_overwritten_function_declaration) { return; } - stmts.append(stmt.*) catch unreachable; + stmts.append(stmt.*) catch bun.outOfMemory(); } else if (mark_as_dead) { - const name = data.func.name.?.ref.?; - if (p.options.features.replace_exports.getPtr(p.loadNameFromRef(name))) |replacement| { - _ = p.injectReplacementExport(stmts, name, data.func.name.?.loc, replacement); + if (p.options.features.replace_exports.getPtr(original_name)) |replacement| { + _ = p.injectReplacementExport(stmts, name_ref, data.func.name.?.loc, replacement); + } + } + + if (p.options.features.react_fast_refresh) { + if (react_hook_data) |*hook| { + try stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)); + try stmts.append(p.s(S.SExpr{ + .value = p.getReactRefreshHookSignalInit(hook, Expr.initIdentifier(name_ref, logger.Loc.Empty)), + }, logger.Loc.Empty)); + } + + if (p.current_scope == p.module_scope) { + try p.handleReactRefreshRegister(stmts, original_name, name_ref); } } - // stmts.appendAssumeCapacity( - // // i wonder if this will crash - // p.keepStmtSymbolName( - // data.func.name.?.loc, - // data.func.name.?.ref.?, - // p.symbols.items[data.func.name.?.ref.?.innerIndex()].original_name, - // ), - // ); return; }, .s_class => |data| { @@ -19375,10 +20073,25 @@ fn NewParser_( return; }, .s_enum => |data| { - p.recordDeclaredSymbol(data.name.ref.?) catch unreachable; - p.pushScopeForVisitPass(.entry, stmt.loc) catch unreachable; + // Do not end the const local prefix after TypeScript enums. We process + // them first within their scope so that they are inlined into all code in + // that scope. We don't want that to cause the const local prefix to end. + p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; + + // Track cross-module enum constants during bundling. This + // part of the code is different from esbuilt in that we are + // only storing a list of enum indexes. At the time of + // referencing, `esbuild` builds a separate hash map of hash + // maps. We are avoiding that to reduce memory usage, since + // enum inlining already uses alot of hash maps. + if (p.current_scope == p.module_scope and p.options.bundle) { + try p.top_level_enums.append(p.allocator, data.name.ref.?); + } + + p.recordDeclaredSymbol(data.name.ref.?) catch bun.outOfMemory(); + p.pushScopeForVisitPass(.entry, stmt.loc) catch bun.outOfMemory(); defer p.popScope(); - p.recordDeclaredSymbol(data.arg) catch unreachable; + p.recordDeclaredSymbol(data.arg) catch bun.outOfMemory(); const allocator = p.allocator; // Scan ahead for any variables inside this namespace. This must be done @@ -19386,79 +20099,132 @@ fn NewParser_( // because we may end up visiting the uses before the declarations. // We need to convert the uses into property accesses on the namespace. for (data.values) |value| { - if (!value.ref.isNull()) { - p.is_exported_inside_namespace.put(allocator, value.ref, data.arg) catch unreachable; + if (value.ref.isValid()) { + p.is_exported_inside_namespace.put(allocator, value.ref, data.arg) catch bun.outOfMemory(); } } // Values without initializers are initialized to one more than the // previous value if the previous value is numeric. Otherwise values // without initializers are initialized to undefined. - var next_numeric_value: f64 = 0.0; - var has_numeric_value = true; + var next_numeric_value: ?f64 = 0.0; - var value_exprs = ListManaged(Expr).initCapacity(allocator, data.values.len) catch unreachable; + var value_exprs = ListManaged(Expr).initCapacity(allocator, data.values.len) catch bun.outOfMemory(); - // Track values so they can be used by constant folding. We need to follow - // links here in case the enum was merged with a preceding namespace - var values_so_far = StringHashMapUnmanaged(f64){}; + var all_values_are_pure = true; - p.known_enum_values.put(allocator, data.name.ref orelse p.panic("Expected data.name.ref", .{}), values_so_far) catch unreachable; - p.known_enum_values.put(allocator, data.arg, values_so_far) catch unreachable; + const exported_members = p.current_scope.ts_namespace.?.exported_members; // We normally don't fold numeric constants because they might increase code // size, but it's important to fold numeric constants inside enums since // that's what the TypeScript compiler does. const old_should_fold_typescript_constant_expressions = p.should_fold_typescript_constant_expressions; p.should_fold_typescript_constant_expressions = true; - for (data.values) |*enum_value| { - // gotta allocate here so it lives after this function stack frame goes poof - const name = enum_value.name; - var assign_target: Expr = Expr{ .loc = logger.Loc.Empty, .data = Prefill.Data.EMissing }; + + // Create an assignment for each enum value + for (data.values) |*value| { + const name = value.name; + var has_string_value = false; + if (value.value) |enum_value| { + next_numeric_value = null; - if (enum_value.value != null) { - enum_value.value = p.visitExpr(enum_value.value.?); - switch (enum_value.value.?.data) { + const visited = p.visitExpr(enum_value); + + // "See through" any wrapped comments + const underlying_value = if (visited.data == .e_inlined_enum) + visited.data.e_inlined_enum.value + else + visited; + value.value = underlying_value; + + switch (underlying_value.data) { .e_number => |num| { + exported_members.getPtr(name).?.data = .{ .enum_number = num.value }; + + p.ref_to_ts_namespace_member.put( + p.allocator, + value.ref, + .{ .enum_number = num.value }, + ) catch bun.outOfMemory(); - // prob never allocates in practice - values_so_far.put(allocator, name.string(allocator) catch unreachable, num.value) catch unreachable; - has_numeric_value = true; next_numeric_value = num.value + 1.0; }, - .e_string => { + .e_string => |str| { has_string_value = true; + + exported_members.getPtr(name).?.data = .{ .enum_string = str }; + + p.ref_to_ts_namespace_member.put( + p.allocator, + value.ref, + .{ .enum_string = str }, + ) catch bun.outOfMemory(); + }, + else => { + if (visited.knownPrimitive() == .string) { + has_string_value = true; + } + + if (!p.exprCanBeRemovedIfUnused(&visited)) { + all_values_are_pure = false; + } }, - else => {}, } - } else if (has_numeric_value) { - enum_value.value = p.newExpr(E.Number{ .value = next_numeric_value }, enum_value.loc); - values_so_far.put(allocator, name.string(allocator) catch unreachable, next_numeric_value) catch unreachable; - next_numeric_value += 1; + } else if (next_numeric_value) |num| { + value.value = p.newExpr(E.Number{ .value = num }, value.loc); + + next_numeric_value = num + 1; + + exported_members.getPtr(name).?.data = .{ .enum_number = num }; + + p.ref_to_ts_namespace_member.put( + p.allocator, + value.ref, + .{ .enum_number = num }, + ) catch bun.outOfMemory(); } else { - enum_value.value = p.newExpr(E.Undefined{}, enum_value.loc); - } - // "Enum['Name'] = value" - assign_target = Expr.assign( - p.newExpr(E.Index{ - .target = p.newExpr( - E.Identifier{ .ref = data.arg }, - enum_value.loc, - ), - .index = p.newExpr( - enum_value.name, - enum_value.loc, - ), - }, enum_value.loc), - enum_value.value orelse unreachable, - ); + value.value = p.newExpr(E.Undefined{}, value.loc); + } + + const is_assign_target = p.options.features.minify_syntax and bun.js_lexer.isIdentifier(value.name); + + const name_as_e_string = if (!is_assign_target or !has_string_value) + p.newExpr(value.nameAsEString(allocator), value.loc) + else + null; + + const assign_target = if (is_assign_target) + // "Enum.Name = value" + Expr.assign( + p.newExpr(E.Dot{ + .target = p.newExpr( + E.Identifier{ .ref = data.arg }, + value.loc, + ), + .name = value.name, + .name_loc = value.loc, + }, value.loc), + value.value.?, + ) + else + // "Enum['Name'] = value" + Expr.assign( + p.newExpr(E.Index{ + .target = p.newExpr( + E.Identifier{ .ref = data.arg }, + value.loc, + ), + .index = name_as_e_string.?, + }, value.loc), + value.value.?, + ); p.recordUsage(data.arg); // String-valued enums do not form a two-way map if (has_string_value) { - value_exprs.append(assign_target) catch unreachable; + value_exprs.append(assign_target) catch bun.outOfMemory(); } else { // "Enum[assignTarget] = 'Name'" value_exprs.append( @@ -19466,15 +20232,15 @@ fn NewParser_( p.newExpr(E.Index{ .target = p.newExpr( E.Identifier{ .ref = data.arg }, - enum_value.loc, + value.loc, ), .index = assign_target, - }, enum_value.loc), - p.newExpr(enum_value.name, enum_value.loc), + }, value.loc), + name_as_e_string.?, ), - ) catch unreachable; + ) catch bun.outOfMemory(); + p.recordUsage(data.arg); } - p.recordUsage(data.arg); } p.should_fold_typescript_constant_expressions = old_should_fold_typescript_constant_expressions; @@ -19493,6 +20259,7 @@ fn NewParser_( data.name.ref.?, data.arg, value_stmts.items, + all_values_are_pure, ); return; }, @@ -19533,6 +20300,7 @@ fn NewParser_( data.name.ref.?, data.arg, prepend_list.items, + false, ); return; }, @@ -19575,6 +20343,10 @@ fn NewParser_( } } + if (p.options.features.react_fast_refresh) { + p.react_refresh.last_hook_seen = null; + } + if (only_scan_imports_and_do_not_visit) { @compileError("only_scan_imports_and_do_not_visit must not run this."); } @@ -19582,7 +20354,18 @@ fn NewParser_( .is_immediately_assigned_to_decl = true, }); - if (comptime FeatureFlags.unwrap_commonjs_to_esm) { + if (p.options.features.react_fast_refresh) { + // When hooks are immediately assigned to something, we need to hash the binding. + if (p.react_refresh.last_hook_seen) |last_hook| { + if (decl.value.?.data.as(.e_call)) |call| { + if (last_hook == call) { + decl.binding.data.writeToHasher(&p.react_refresh.hook_ctx_storage.?.*.?.hasher, p.symbols.items); + } + } + } + } + + if (p.shouldUnwrapCommonJSToESM()) { if (prev_require_to_convert_count < p.imports_to_convert_from_require.items.len) { if (decl.binding.data == .b_identifier) { const ref = decl.binding.data.b_identifier.ref; @@ -19852,9 +20635,6 @@ fn NewParser_( p.markExportedBindingInsideNamespace(ref, item.value); } }, - else => { - Global.panic("Unexpected binding type in namespace. This is a bug. {any}", .{binding}); - }, } } @@ -19864,11 +20644,13 @@ fn NewParser_( stmt_loc: logger.Loc, is_export: bool, name_loc: logger.Loc, - _name_ref: Ref, + original_name_ref: Ref, arg_ref: Ref, stmts_inside_closure: []Stmt, + all_values_are_pure: bool, ) anyerror!void { - var name_ref = _name_ref; + var name_ref = original_name_ref; + // Follow the link chain in case symbols were merged var symbol: Symbol = p.symbols.items[name_ref.innerIndex()]; while (symbol.hasLink()) { @@ -19880,49 +20662,47 @@ fn NewParser_( // Make sure to only emit a variable once for a given namespace, since there // can be multiple namespace blocks for the same namespace - if (symbol.kind == .ts_namespace or symbol.kind == .ts_enum and !p.emitted_namespace_vars.contains(name_ref)) { - p.emitted_namespace_vars.put(allocator, name_ref, {}) catch unreachable; + if ((symbol.kind == .ts_namespace or symbol.kind == .ts_enum) and + !p.emitted_namespace_vars.contains(name_ref)) + { + p.emitted_namespace_vars.putNoClobber(allocator, name_ref, {}) catch bun.outOfMemory(); - var decls = allocator.alloc(G.Decl, 1) catch unreachable; + var decls = allocator.alloc(G.Decl, 1) catch bun.outOfMemory(); decls[0] = G.Decl{ .binding = p.b(B.Identifier{ .ref = name_ref }, name_loc) }; if (p.enclosing_namespace_arg_ref == null) { - // Top-level namespace + // Top-level namespace: "var" stmts.append( - p.s( - S.Local{ - .kind = .k_var, - .decls = G.Decl.List.init(decls), - .is_export = is_export, - }, - stmt_loc, - ), - ) catch unreachable; + p.s(S.Local{ + .kind = .k_var, + .decls = G.Decl.List.init(decls), + .is_export = is_export, + }, stmt_loc), + ) catch bun.outOfMemory(); } else { - // Nested namespace + // Nested namespace: "let" stmts.append( - p.s( - S.Local{ - .kind = .k_let, - .decls = G.Decl.List.init(decls), - }, - stmt_loc, - ), - ) catch unreachable; + p.s(S.Local{ + .kind = .k_let, + .decls = G.Decl.List.init(decls), + }, stmt_loc), + ) catch bun.outOfMemory(); } } - var arg_expr: Expr = undefined; + const arg_expr: Expr = arg_expr: { + // TODO: unsupportedJSFeatures.has(.logical_assignment) + // If the "||=" operator is supported, our minified output can be slightly smaller + if (is_export) if (p.enclosing_namespace_arg_ref) |namespace| { + const name = p.symbols.items[name_ref.innerIndex()].original_name; - if (is_export and p.enclosing_namespace_arg_ref != null) { - const namespace = p.enclosing_namespace_arg_ref.?; - // "name = enclosing.name || (enclosing.name = {})" - const name = p.symbols.items[name_ref.innerIndex()].original_name; - arg_expr = Expr.assign( - Expr.initIdentifier(name_ref, name_loc), - p.newExpr( - E.Binary{ - .op = .bin_logical_or, + // "name = (enclosing.name ||= {})" + p.recordUsage(namespace); + p.recordUsage(name_ref); + break :arg_expr Expr.assign( + Expr.initIdentifier(name_ref, name_loc), + p.newExpr(E.Binary{ + .op = .bin_logical_or_assign, .left = p.newExpr( E.Dot{ .target = Expr.initIdentifier(namespace, name_loc), @@ -19931,75 +20711,80 @@ fn NewParser_( }, name_loc, ), - .right = Expr.assign( - p.newExpr( - E.Dot{ - .target = Expr.initIdentifier(namespace, name_loc), - .name = name, - .name_loc = name_loc, - }, - name_loc, - ), - p.newExpr(E.Object{}, name_loc), - ), - }, - name_loc, - ), - ); - p.recordUsage(namespace); - p.recordUsage(namespace); + .right = p.newExpr(E.Object{}, name_loc), + }, name_loc), + ); + }; + + // "name ||= {}" p.recordUsage(name_ref); - } else { - // "name || (name = {})" - arg_expr = p.newExpr(E.Binary{ - .op = .bin_logical_or, + break :arg_expr p.newExpr(E.Binary{ + .op = .bin_logical_or_assign, .left = Expr.initIdentifier(name_ref, name_loc), - .right = Expr.assign( - Expr.initIdentifier(name_ref, name_loc), - p.newExpr( - E.Object{}, - name_loc, - ), - ), + .right = p.newExpr(E.Object{}, name_loc), }, name_loc); - p.recordUsage(name_ref); - p.recordUsage(name_ref); - } + }; - var func_args = allocator.alloc(G.Arg, 1) catch unreachable; + var func_args = allocator.alloc(G.Arg, 1) catch bun.outOfMemory(); func_args[0] = .{ .binding = p.b(B.Identifier{ .ref = arg_ref }, name_loc) }; - var args_list = allocator.alloc(ExprNodeIndex, 1) catch unreachable; + + var args_list = allocator.alloc(ExprNodeIndex, 1) catch bun.outOfMemory(); args_list[0] = arg_expr; - const func = G.Fn{ - .args = func_args, - .name = null, - .open_parens_loc = stmt_loc, - .body = G.FnBody{ - .loc = stmt_loc, - .stmts = try allocator.dupe(StmtNodeIndex, stmts_inside_closure), - }, + + // TODO: if unsupported features includes arrow functions + // const target = p.newExpr( + // E.Function{ .func = .{ + // .args = func_args, + // .name = null, + // .open_parens_loc = stmt_loc, + // .body = G.FnBody{ + // .loc = stmt_loc, + // .stmts = try allocator.dupe(StmtNodeIndex, stmts_inside_closure), + // }, + // } }, + // stmt_loc, + // ); + + const target = target: { + // "(() => { foo() })()" => "(() => foo())()" + if (p.options.features.minify_syntax and stmts_inside_closure.len == 1) { + if (stmts_inside_closure[0].data == .s_expr) { + stmts_inside_closure[0] = p.s(S.Return{ + .value = stmts_inside_closure[0].data.s_expr.value, + }, stmts_inside_closure[0].loc); + } + } + + break :target p.newExpr(E.Arrow{ + .args = func_args, + .body = .{ + .loc = stmt_loc, + .stmts = try allocator.dupe(StmtNodeIndex, stmts_inside_closure), + }, + .prefer_expr = true, + }, stmt_loc); }; - const target = p.newExpr( - E.Function{ - .func = func, - }, - stmt_loc, - ); + // Call the closure with the name object const call = p.newExpr( E.Call{ .target = target, .args = ExprNodeList.init(args_list), + // TODO: make these fully tree-shakable. this annotation + // as-is is incorrect. This would be done by changing all + // enum wrappers into `var Enum = ...` instead of two + // separate statements. This way, the @__PURE__ annotation + // is attached to the variable binding. + // + // .can_be_unwrapped_if_unused = all_values_are_pure, }, stmt_loc, ); - const closure = p.s( - S.SExpr{ - .value = call, - }, - stmt_loc, - ); + const closure = p.s(S.SExpr{ + .value = call, + .does_not_affect_tree_shaking = all_values_are_pure, + }, stmt_loc); stmts.append(closure) catch unreachable; } @@ -20591,6 +21376,19 @@ fn NewParser_( return Expr.initIdentifier(ref, loc); } + fn wrapInlinedEnum(p: *P, value: Expr, comment: string) Expr { + if (bun.strings.containsComptime(comment, "*/")) { + // Don't wrap with a comment + return value; + } + + // Wrap with a comment + return p.newExpr(E.InlinedEnum{ + .value = value, + .comment = comment, + }, value.loc); + } + fn valueForDefine(p: *P, loc: logger.Loc, assign_target: js_ast.AssignTarget, is_delete_target: bool, define_data: *const DefineData) Expr { switch (define_data.value) { .e_identifier => { @@ -20741,9 +21539,6 @@ fn NewParser_( } } }, - else => { - p.panic("Unexpected binding {any}", .{binding}); - }, } } @@ -21067,19 +21862,12 @@ fn NewParser_( } fn keepStmtSymbolName(p: *P, loc: logger.Loc, ref: Ref, name: string) Stmt { - p.expr_list.ensureUnusedCapacity(2) catch unreachable; - const start = p.expr_list.items.len; - p.expr_list.appendAssumeCapacity(p.newExpr(E.Identifier{ - .ref = ref, - }, loc)); - p.expr_list.appendAssumeCapacity(p.newExpr(E.String{ .data = name }, loc)); - return p.s(S.SExpr{ - // I believe that this is a spot we can do $RefreshReg$(name) - .value = p.callRuntime(loc, "__name", p.expr_list.items[start..p.expr_list.items.len]), - - // Make sure tree shaking removes this if the function is never used - .does_not_affect_tree_shaking = true, - }, loc); + _ = p; + _ = loc; + _ = ref; + _ = name; + // TODO: + @compileError("not implemented"); } fn runtimeIdentifierRef(p: *P, loc: logger.Loc, comptime name: string) Ref { @@ -21136,7 +21924,7 @@ fn NewParser_( @compileError("only_scan_imports_and_do_not_visit must not run this."); } - const initial_scope: *Scope = if (comptime Environment.allow_assert) p.current_scope else undefined; + const initial_scope = if (comptime Environment.allow_assert) p.current_scope else {}; { // Save the current control-flow liveness. This represents if we are @@ -21145,7 +21933,37 @@ fn NewParser_( defer p.is_control_flow_dead = old_is_control_flow_dead; var before = ListManaged(Stmt).init(p.allocator); + defer before.deinit(); + var after = ListManaged(Stmt).init(p.allocator); + defer after.deinit(); + + // Preprocess TypeScript enums to improve code generation. Otherwise + // uses of an enum before that enum has been declared won't be inlined: + // + // console.log(Foo.FOO) // We want "FOO" to be inlined here + // const enum Foo { FOO = 0 } + // + // The TypeScript compiler itself contains code with this pattern, so + // it's important to implement this optimization. + var preprocessed_enums: std.ArrayListUnmanaged([]Stmt) = .{}; + defer preprocessed_enums.deinit(p.allocator); + if (p.scopes_in_order_for_enum.count() > 0) { + var found: usize = 0; + for (stmts.items) |*stmt| { + if (stmt.data == .s_enum) { + const old_scopes_in_order = p.scope_order_to_visit; + defer p.scope_order_to_visit = old_scopes_in_order; + + p.scope_order_to_visit = p.scopes_in_order_for_enum.get(stmt.loc).?; + + var temp = ListManaged(Stmt).init(p.allocator); + try p.visitAndAppendStmt(&temp, stmt); + try preprocessed_enums.append(p.allocator, temp.items); + found += 1; + } + } + } if (p.current_scope == p.module_scope) { p.macro.prepend_stmts = &before; @@ -21153,10 +21971,13 @@ fn NewParser_( // visit all statements first var visited = try ListManaged(Stmt).initCapacity(p.allocator, stmts.items.len); - - defer before.deinit(); defer visited.deinit(); - defer after.deinit(); + + const prev_nearest_stmt_list = p.nearest_stmt_list; + defer p.nearest_stmt_list = prev_nearest_stmt_list; + p.nearest_stmt_list = &before; + + var preprocessed_enum_i: usize = 0; for (stmts.items) |*stmt| { const list = list_getter: { @@ -21169,18 +21990,23 @@ fn NewParser_( }, .s_function => |data| { if ( - // Hoist module-level functions when - ((FeatureFlags.unwrap_commonjs_to_esm and p.current_scope == p.module_scope and !data.func.flags.contains(.is_export)) or - - // Manually hoist block-level function declarations to preserve semantics. - // This is only done for function declarations that are not generators - // or async functions, since this is a backwards-compatibility hack from - // Annex B of the JavaScript standard. - !p.current_scope.kindStopsHoisting()) and p.symbols.items[data.func.name.?.ref.?.innerIndex()].kind == .hoisted_function) + // Manually hoist block-level function declarations to preserve semantics. + // This is only done for function declarations that are not generators + // or async functions, since this is a backwards-compatibility hack from + // Annex B of the JavaScript standard. + !p.current_scope.kindStopsHoisting() and + p.symbols.items[data.func.name.?.ref.?.innerIndex()].kind == .hoisted_function) { break :list_getter &before; } }, + .s_enum => { + const enum_stmts = preprocessed_enums.items[preprocessed_enum_i]; + preprocessed_enum_i += 1; + try visited.appendSlice(enum_stmts); + p.scope_order_to_visit = p.scope_order_to_visit[1..]; + continue; + }, else => {}, } break :list_getter &visited; @@ -21218,11 +22044,11 @@ fn NewParser_( // Merge the two identifiers back into a single one p.symbols.items[hoisted_ref.innerIndex()].link = name_ref; } - non_fn_stmts.append(stmt) catch unreachable; + non_fn_stmts.append(stmt) catch bun.outOfMemory(); continue; } - const gpe = fn_stmts.getOrPut(name_ref) catch unreachable; + const gpe = fn_stmts.getOrPut(name_ref) catch bun.outOfMemory(); var index = gpe.value_ptr.*; if (!gpe.found_existing) { index = @as(u32, @intCast(let_decls.items.len)); @@ -21247,7 +22073,7 @@ fn NewParser_( }, data.func.name.?.loc, ), - }) catch unreachable; + }) catch bun.outOfMemory(); } } @@ -21641,7 +22467,7 @@ fn NewParser_( fn extractDeclsForBinding(binding: Binding, decls: *ListManaged(G.Decl)) anyerror!void { switch (binding.data) { - .b_property, .b_missing => {}, + .b_missing => {}, .b_identifier => { try decls.append(G.Decl{ .binding = binding }); }, @@ -21896,8 +22722,10 @@ fn NewParser_( /// When not transpiling we dont use the renamer, so our solution is to generate really /// hard to collide with variables, instead of actually making things collision free pub fn generateTempRef(p: *P, default_name: ?string) Ref { - var scope = p.current_scope; + return p.generateTempRefWithScope(default_name, p.current_scope); + } + pub fn generateTempRefWithScope(p: *P, default_name: ?string, scope: *Scope) Ref { const name = (if (p.willUseRenamer()) default_name else null) orelse brk: { p.temp_ref_count += 1; break :brk std.fmt.allocPrint(p.allocator, "__bun_temp_ref_{x}$", .{p.temp_ref_count}) catch bun.outOfMemory(); @@ -21913,6 +22741,38 @@ fn NewParser_( return ref; } + pub fn computeTsEnumsMap(p: *const P, allocator: Allocator) !js_ast.Ast.TsEnumsMap { + // When hot module reloading is enabled, we disable enum inlining + // to avoid making the HMR graph more complicated. + if (p.options.features.hot_module_reloading) + return .{}; + + const InlinedEnumValue = js_ast.InlinedEnumValue; + var map: js_ast.Ast.TsEnumsMap = .{}; + try map.ensureTotalCapacity(allocator, @intCast(p.top_level_enums.items.len)); + for (p.top_level_enums.items) |ref| { + const entry = p.ref_to_ts_namespace_member.getEntry(ref).?; + const namespace = entry.value_ptr.namespace; + var inner_map: bun.StringHashMapUnmanaged(InlinedEnumValue) = .{}; + try inner_map.ensureTotalCapacity(allocator, @intCast(namespace.count())); + for (namespace.keys(), namespace.values()) |key, val| { + switch (val.data) { + .enum_number => |num| inner_map.putAssumeCapacityNoClobber( + key, + InlinedEnumValue.encode(.{ .number = num }), + ), + .enum_string => |str| inner_map.putAssumeCapacityNoClobber( + key, + InlinedEnumValue.encode(.{ .string = str }), + ), + else => continue, + } + } + map.putAssumeCapacity(entry.key_ptr.*, inner_map); + } + return map; + } + fn shouldLowerUsingDeclarations(p: *const P, stmts: []Stmt) bool { // TODO: We do not support lowering await, but when we do this needs to point to that var const lower_await = false; @@ -22199,15 +23059,177 @@ fn NewParser_( } }; + pub fn handleReactRefreshRegister(p: *P, stmts: *ListManaged(Stmt), original_name: []const u8, ref: Ref) !void { + bun.assert(p.options.features.react_fast_refresh); + bun.assert(p.current_scope == p.module_scope); + + if (ReactRefresh.isComponentishName(original_name)) { + // $RefreshReg$(component, "file.ts:Original Name") + const loc = logger.Loc.Empty; + try stmts.append(p.s(S.SExpr{ .value = p.newExpr(E.Call{ + .target = Expr.initIdentifier(p.react_refresh.register_ref, loc), + .args = try ExprNodeList.fromSlice(p.allocator, &.{ + Expr.initIdentifier(ref, loc), + p.newExpr(E.String{ + .data = try bun.strings.concat(p.allocator, &.{ + p.source.path.pretty, + ":", + original_name, + }), + }, loc), + }), + }, loc) }, loc)); + + p.react_refresh.register_used = true; + } + } + + pub fn handleReactRefreshHookCall(p: *P, hook_call: *E.Call, original_name: []const u8) void { + bun.assert(p.options.features.react_fast_refresh); + bun.assert(ReactRefresh.isHookName(original_name)); + const ctx_storage = p.react_refresh.hook_ctx_storage orelse + return; // not in a function, ignore this hook call. + + // if this function has no hooks recorded, initialize a hook context + // every function visit provides stack storage, which it will inspect at visit finish. + const ctx: *ReactRefresh.HookContext = if (ctx_storage.*) |*ctx| ctx else init: { + p.react_refresh.signature_used = true; + + var scope = p.current_scope; + while (scope.kind != .function_body and scope.kind != .block and scope.kind != .entry) { + scope = scope.parent orelse break; + } + + ctx_storage.* = .{ + .hasher = std.hash.Wyhash.init(0), + .signature_cb = p.generateTempRefWithScope("_s", scope), + .user_hooks = .{}, + }; + + break :init &(ctx_storage.*.?); + }; + + ctx.hasher.update(original_name); + + if (ReactRefresh.built_in_hooks.get(original_name)) |built_in_hook| hash_arg: { + const arg_index: usize = switch (built_in_hook) { + // useState first argument is initial state. + .useState => 0, + // useReducer second argument is initial state. + .useReducer => 1, + else => break :hash_arg, + }; + if (hook_call.args.len <= arg_index) break :hash_arg; + const arg = hook_call.args.slice()[arg_index]; + arg.data.writeToHasher(&ctx.hasher, p.symbols.items); + } else switch (hook_call.target.data) { + inline .e_identifier, + .e_import_identifier, + .e_commonjs_export_identifier, + => |id| { + const gop = ctx.user_hooks.getOrPut(p.allocator, id.ref) catch bun.outOfMemory(); + if (!gop.found_existing) { + gop.value_ptr.* = Expr.initIdentifier(id.ref, logger.Loc.Empty); + } + }, + else => {}, + } + + ctx.hasher.update("\x00"); + } + + pub fn handleReactRefreshPostVisitFunctionBody(p: *P, stmts: *ListManaged(Stmt), hook: *ReactRefresh.HookContext) void { + bun.assert(p.options.features.react_fast_refresh); + + // We need to prepend `_s();` as a statement. + if (stmts.items.len == stmts.capacity) { + // If the ArrayList does not have enough capacity, it is + // re-allocated entirely to fit. Only one slot of new capacity + // is used since we know this statement list is not going to be + // appended to afterwards; This function is a post-visit handler. + const new_stmts = p.allocator.alloc(Stmt, stmts.items.len + 1) catch bun.outOfMemory(); + @memcpy(new_stmts[1..], stmts.items); + stmts.deinit(); + stmts.* = ListManaged(Stmt).fromOwnedSlice(p.allocator, new_stmts); + } else { + // The array has enough capacity, so there is no possibility of + // allocation failure. We just move all of the statements over + // by one, and increase the length using `addOneAssumeCapacity` + _ = stmts.addOneAssumeCapacity(); + bun.copy(Stmt, stmts.items[1..], stmts.items[0 .. stmts.items.len - 1]); + } + + const loc = logger.Loc.Empty; + const prepended_stmt = p.s(S.SExpr{ .value = p.newExpr(E.Call{ + .target = Expr.initIdentifier(hook.signature_cb, loc), + }, loc) }, loc); + stmts.items[0] = prepended_stmt; + } + + pub fn getReactRefreshHookSignalDecl(p: *P, signal_cb_ref: Ref) Stmt { + const loc = logger.Loc.Empty; + // var s_ = $RefreshSig$(); + return p.s(S.Local{ .decls = G.Decl.List.fromSlice(p.allocator, &.{.{ + .binding = p.b(B.Identifier{ .ref = signal_cb_ref }, loc), + .value = p.newExpr(E.Call{ + .target = Expr.initIdentifier(p.react_refresh.create_signature_ref, loc), + }, loc), + }}) catch bun.outOfMemory() }, loc); + } + + pub fn getReactRefreshHookSignalInit(p: *P, ctx: *ReactRefresh.HookContext, function_with_hook_calls: Expr) Expr { + const loc = logger.Loc.Empty; + + const final = ctx.hasher.final(); + const hash_data = p.allocator.alloc(u8, comptime bun.base64.encodeLenFromSize(@sizeOf(@TypeOf(final)))) catch bun.outOfMemory(); + bun.assert(bun.base64.encode(hash_data, std.mem.asBytes(&final)) == hash_data.len); + + const have_custom_hooks = ctx.user_hooks.count() > 0; + const have_force_arg = have_custom_hooks or p.react_refresh.force_reset; + + const args = p.allocator.alloc( + Expr, + 2 + + @as(usize, @intFromBool(have_force_arg)) + + @as(usize, @intFromBool(have_custom_hooks)), + ) catch bun.outOfMemory(); + + args[0] = function_with_hook_calls; + args[1] = p.newExpr(E.String{ .data = hash_data }, loc); + + if (have_force_arg) args[2] = p.newExpr(E.Boolean{ .value = p.react_refresh.force_reset }, loc); + + if (have_custom_hooks) { + // () => [useCustom1, useCustom2] + args[3] = p.newExpr(E.Arrow{ + .body = .{ + .stmts = p.allocator.dupe(Stmt, &.{ + p.s(S.Return{ .value = p.newExpr(E.Array{ + .items = ExprNodeList.init(ctx.user_hooks.values()), + }, loc) }, loc), + }) catch bun.outOfMemory(), + .loc = loc, + }, + .prefer_expr = true, + }, loc); + } + + // _s(func, "", force, () => [useCustom]) + return p.newExpr(E.Call{ + .target = Expr.initIdentifier(ctx.signature_cb, loc), + .args = ExprNodeList.init(args), + }, loc); + } + pub fn toAST( p: *P, - _parts: []js_ast.Part, + input_parts: []js_ast.Part, exports_kind: js_ast.ExportsKind, - commonjs_wrapper_expr: CommonJSWrapper, + wrap_mode: WrapMode, hashbang: []const u8, ) !js_ast.Ast { const allocator = p.allocator; - var parts = _parts; + var parts = input_parts; // if (p.options.tree_shaking and p.options.features.trim_unused_imports) { // p.treeShake(&parts, false); @@ -22215,644 +23237,183 @@ fn NewParser_( const bundling = p.options.bundle; var parts_end: usize = @as(usize, @intFromBool(bundling)); - // Handle import paths after the whole file has been visited because we need - // symbol usage counts to be able to remove unused type-only imports in - // TypeScript code. - while (true) { - var kept_import_equals = false; - var removed_import_equals = false; - - const begin = parts_end; - // Potentially remove some statements, then filter out parts to remove any - // with no statements - for (parts[begin..]) |part_| { - var part = part_; - p.import_records_for_current_part.clearRetainingCapacity(); - p.declared_symbols.clearRetainingCapacity(); - - const result = try ImportScanner.scan(P, p, part.stmts, commonjs_wrapper_expr != .none); - kept_import_equals = kept_import_equals or result.kept_import_equals; - removed_import_equals = removed_import_equals or result.removed_import_equals; - - part.stmts = result.stmts; - if (part.stmts.len > 0) { - if (p.module_scope.contains_direct_eval and part.declared_symbols.len() > 0) { - // If this file contains a direct call to "eval()", all parts that - // declare top-level symbols must be kept since the eval'd code may - // reference those symbols. - part.can_be_removed_if_unused = false; - } - if (part.declared_symbols.len() == 0) { - part.declared_symbols = p.declared_symbols.clone(p.allocator) catch unreachable; - } else { - part.declared_symbols.appendList(p.allocator, p.declared_symbols) catch unreachable; - } - if (part.import_record_indices.len == 0) { - part.import_record_indices = @TypeOf(part.import_record_indices).init( - (p.import_records_for_current_part.clone(p.allocator) catch unreachable).items, - ); - } else { - part.import_record_indices.append(p.allocator, p.import_records_for_current_part.items) catch unreachable; - } + // When bundling with HMR, we need every module to be just a + // single part, as we later wrap each module into a function, + // which requires a single part. Otherwise, you'll end up with + // multiple instances of a module, each with different parts of + // the file. That is also why tree-shaking is disabled. + if (p.options.features.hot_module_reloading) { + bun.assert(!p.options.tree_shaking); + bun.assert(p.options.features.hot_module_reloading); + + var hmr_transform_ctx = ConvertESMExportsForHmr{ .last_part = &parts[parts.len - 1] }; + try hmr_transform_ctx.stmts.ensureTotalCapacity(p.allocator, prealloc_count: { + // get a estimate on how many statements there are going to be + var count: usize = 0; + for (parts) |part| count += part.stmts.len; + break :prealloc_count count + 2; + }); - parts[parts_end] = part; - parts_end += 1; - } + for (parts) |part| { + // Kit does not care about 'import =', as it handles it on it's own + _ = try ImportScanner.scan(P, p, part.stmts, wrap_mode != .none, true, &hmr_transform_ctx); } - // We need to iterate multiple times if an import-equals statement was - // removed and there are more import-equals statements that may be removed - if (!kept_import_equals or !removed_import_equals) { - break; + parts = try hmr_transform_ctx.finalize(p, parts); + } else { + // Handle import paths after the whole file has been visited because we need + // symbol usage counts to be able to remove unused type-only imports in + // TypeScript code. + while (true) { + var kept_import_equals = false; + var removed_import_equals = false; + + const begin = parts_end; + // Potentially remove some statements, then filter out parts to remove any + // with no statements + for (parts[begin..]) |part_| { + var part = part_; + p.import_records_for_current_part.clearRetainingCapacity(); + p.declared_symbols.clearRetainingCapacity(); + + const result = try ImportScanner.scan(P, p, part.stmts, wrap_mode != .none, false, {}); + kept_import_equals = kept_import_equals or result.kept_import_equals; + removed_import_equals = removed_import_equals or result.removed_import_equals; + + part.stmts = result.stmts; + if (part.stmts.len > 0) { + if (p.module_scope.contains_direct_eval and part.declared_symbols.len() > 0) { + // If this file contains a direct call to "eval()", all parts that + // declare top-level symbols must be kept since the eval'd code may + // reference those symbols. + part.can_be_removed_if_unused = false; + } + if (part.declared_symbols.len() == 0) { + part.declared_symbols = p.declared_symbols.clone(p.allocator) catch unreachable; + } else { + part.declared_symbols.appendList(p.allocator, p.declared_symbols) catch unreachable; + } + + if (part.import_record_indices.len == 0) { + part.import_record_indices = @TypeOf(part.import_record_indices).init( + (p.import_records_for_current_part.clone(p.allocator) catch unreachable).items, + ); + } else { + part.import_record_indices.append(p.allocator, p.import_records_for_current_part.items) catch unreachable; + } + + parts[parts_end] = part; + parts_end += 1; + } + } + + // We need to iterate multiple times if an import-equals statement was + // removed and there are more import-equals statements that may be removed + if (!kept_import_equals or !removed_import_equals) { + break; + } } - } - // leave the first part in there for namespace export when bundling - parts = parts[0..parts_end]; + // leave the first part in there for namespace export when bundling + parts = parts[0..parts_end]; - // Do a second pass for exported items now that imported items are filled out - for (parts) |part| { - for (part.stmts) |stmt| { - switch (stmt.data) { - .s_export_clause => |clause| { - for (clause.items) |item| { - if (p.named_imports.getEntry(item.name.ref.?)) |_import| { - _import.value_ptr.is_exported = true; + // Do a second pass for exported items now that imported items are filled out. + // This isn't done for HMR because it already deletes all `.s_export_clause`s + for (parts) |part| { + for (part.stmts) |stmt| { + switch (stmt.data) { + .s_export_clause => |clause| { + for (clause.items) |item| { + if (p.named_imports.getEntry(item.name.ref.?)) |_import| { + _import.value_ptr.is_exported = true; + } } - } - }, - else => {}, + }, + else => {}, + } } } } - switch (commonjs_wrapper_expr) { - .bun_dev => |commonjs_wrapper| { - var require_function_args = allocator.alloc(Arg, 2) catch unreachable; - var final_part_stmts_count: usize = 0; + if (wrap_mode == .bun_commonjs and !p.options.features.remove_cjs_module_wrapper) { + // This transforms the user's code into. + // + // (function (exports, require, module, __filename, __dirname) { + // ... + // }) + // + // which is then called in `evaluateCommonJSModuleOnce` + var args = allocator.alloc(Arg, 5 + @as(usize, @intFromBool(p.has_import_meta))) catch bun.outOfMemory(); + args[0..5].* = .{ + Arg{ .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty) }, + Arg{ .binding = p.b(B.Identifier{ .ref = p.require_ref }, logger.Loc.Empty) }, + Arg{ .binding = p.b(B.Identifier{ .ref = p.module_ref }, logger.Loc.Empty) }, + Arg{ .binding = p.b(B.Identifier{ .ref = p.filename_ref }, logger.Loc.Empty) }, + Arg{ .binding = p.b(B.Identifier{ .ref = p.dirname_ref }, logger.Loc.Empty) }, + }; + if (p.has_import_meta) { + p.import_meta_ref = p.newSymbol(.other, "$Bun_import_meta") catch bun.outOfMemory(); + args[5] = Arg{ .binding = p.b(B.Identifier{ .ref = p.import_meta_ref }, logger.Loc.Empty) }; + } - var imports_count: u32 = 0; - // We have to also move export from, since we will preserve those - var exports_from_count: u32 = 0; + var total_stmts_count: usize = 0; + for (parts) |part| { + total_stmts_count += part.stmts.len; + } - // Two passes. First pass just counts. - for (parts) |part| { - for (part.stmts) |stmt| { - imports_count += switch (stmt.data) { - .s_import => @as(u32, 1), - else => @as(u32, 0), - }; + const preserve_strict_mode = p.module_scope.strict_mode == .explicit_strict_mode and + !(parts.len > 0 and + parts[0].stmts.len > 0 and + parts[0].stmts[0].data == .s_directive); - exports_from_count += switch (stmt.data) { - .s_export_star, .s_export_from => @as(u32, 1), - else => @as(u32, 0), - }; + total_stmts_count += @as(usize, @intCast(@intFromBool(preserve_strict_mode))); - final_part_stmts_count += switch (stmt.data) { - .s_import, .s_export_star, .s_export_from => @as(usize, 0), - else => @as(usize, 1), - }; - } + const stmts_to_copy = allocator.alloc(Stmt, total_stmts_count) catch bun.outOfMemory(); + { + var remaining_stmts = stmts_to_copy; + if (preserve_strict_mode) { + remaining_stmts[0] = p.s( + S.Directive{ + .value = "use strict", + }, + p.module_scope_directive_loc, + ); + remaining_stmts = remaining_stmts[1..]; } - var new_stmts_list = allocator.alloc(Stmt, exports_from_count + imports_count + 1) catch unreachable; - const final_stmts_list = allocator.alloc(Stmt, final_part_stmts_count) catch unreachable; - var remaining_final_stmts = final_stmts_list; - var imports_list = new_stmts_list[0..imports_count]; - - var exports_list: []Stmt = if (exports_from_count > 0) new_stmts_list[imports_list.len + 1 ..] else &[_]Stmt{}; - - require_function_args[0] = G.Arg{ .binding = p.b(B.Identifier{ .ref = p.module_ref }, logger.Loc.Empty) }; - require_function_args[1] = G.Arg{ .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty) }; - - var imports_list_i: u32 = 0; - var exports_list_i: u32 = 0; - for (parts) |part| { - for (part.stmts) |*stmt| { - switch (stmt.data) { - .s_import => { - imports_list[imports_list_i] = stmt.*; - stmt.loc = imports_list[imports_list_i].loc; - imports_list_i += 1; - }, - - .s_export_star, .s_export_from => { - exports_list[exports_list_i] = stmt.*; - stmt.loc = exports_list[exports_list_i].loc; - exports_list_i += 1; - }, - else => { - remaining_final_stmts[0] = stmt.*; - remaining_final_stmts = remaining_final_stmts[1..]; - }, - } - stmt.* = Stmt.empty(); + for (part.stmts, remaining_stmts[0..part.stmts.len]) |src, *dest| { + dest.* = src; } + remaining_stmts = remaining_stmts[part.stmts.len..]; } + } - commonjs_wrapper.data.e_call.args.ptr[0] = p.newExpr( - E.Function{ .func = G.Fn{ + const wrapper = p.newExpr( + E.Function{ + .func = G.Fn{ .name = null, .open_parens_loc = logger.Loc.Empty, - .args = require_function_args, - .body = .{ .loc = logger.Loc.Empty, .stmts = final_stmts_list }, - .flags = Flags.Function.init(.{ .is_export = true }), - } }, - logger.Loc.Empty, - ); - var sourcefile_name = p.source.path.pretty; - if (strings.lastIndexOf(sourcefile_name, "node_modules")) |node_modules_i| { - // 1 for the separator - const end = node_modules_i + 1 + "node_modules".len; - // If you were to name your file "node_modules.js" it shouldn't appear as ".js" - if (end < sourcefile_name.len) { - sourcefile_name = sourcefile_name[end..]; - } - } - commonjs_wrapper.data.e_call.args.ptr[1] = p.newExpr(E.String{ .data = sourcefile_name }, logger.Loc.Empty); - - new_stmts_list[imports_list.len] = p.s( - S.ExportDefault{ - .value = .{ - .expr = commonjs_wrapper, - }, - .default_name = LocRef{ .ref = null, .loc = logger.Loc.Empty }, + .args = args, + .body = .{ .loc = logger.Loc.Empty, .stmts = stmts_to_copy }, + .flags = Flags.Function.init(.{ .is_export = false }), }, - logger.Loc.Empty, - ); - parts[parts.len - 1].stmts = new_stmts_list; - }, - - .bun_js => { - // if remove_cjs_module_wrapper is true, `evaluateCommonJSModuleOnce` will put exports, require, module, __filename, and - // __dirname on the globalObject. - if (!p.options.features.remove_cjs_module_wrapper) { - // This transforms the user's code into. - // - // (function (exports, require, module, __filename, __dirname) { - // ... - // }) - // - // which is then called in `evaluateCommonJSModuleOnce` - var args = allocator.alloc(Arg, 5 + @as(usize, @intFromBool(p.has_import_meta))) catch bun.outOfMemory(); - args[0..5].* = .{ - Arg{ .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty) }, - Arg{ .binding = p.b(B.Identifier{ .ref = p.require_ref }, logger.Loc.Empty) }, - Arg{ .binding = p.b(B.Identifier{ .ref = p.module_ref }, logger.Loc.Empty) }, - Arg{ .binding = p.b(B.Identifier{ .ref = p.filename_ref }, logger.Loc.Empty) }, - Arg{ .binding = p.b(B.Identifier{ .ref = p.dirname_ref }, logger.Loc.Empty) }, - }; - if (p.has_import_meta) { - p.import_meta_ref = p.newSymbol(.other, "$Bun_import_meta") catch bun.outOfMemory(); - args[5] = Arg{ .binding = p.b(B.Identifier{ .ref = p.import_meta_ref }, logger.Loc.Empty) }; - } - - var total_stmts_count: usize = 0; - for (parts) |part| { - total_stmts_count += part.stmts.len; - } - - const stmts_to_copy = allocator.alloc(Stmt, total_stmts_count) catch bun.outOfMemory(); - { - var remaining_stmts = stmts_to_copy; - for (parts) |part| { - for (part.stmts, remaining_stmts[0..part.stmts.len]) |src, *dest| { - dest.* = src; - } - remaining_stmts = remaining_stmts[part.stmts.len..]; - } - } - - const wrapper = p.newExpr( - E.Function{ - .func = G.Fn{ - .name = null, - .open_parens_loc = logger.Loc.Empty, - .args = args, - .body = .{ .loc = logger.Loc.Empty, .stmts = stmts_to_copy }, - .flags = Flags.Function.init(.{ .is_export = false }), - }, - }, - logger.Loc.Empty, - ); - - var top_level_stmts = p.allocator.alloc(Stmt, 1) catch bun.outOfMemory(); - parts[0].stmts = top_level_stmts; - top_level_stmts[0] = p.s( - S.SExpr{ - .value = wrapper, - }, - logger.Loc.Empty, - ); - parts.len = 1; - } - }, - - .none => { - if (p.options.features.hot_module_reloading and p.options.features.allow_runtime) { - const named_exports_count: usize = p.named_exports.count(); - const named_imports: js_ast.Ast.NamedImports = p.named_imports; - - // To transform to something HMR'able, we must: - // 1. Wrap the top level code in an IIFE - // 2. Move imports to the top of the file (preserving the order) - // 3. Remove export clauses (done during ImportScanner) - // 4. Move export * from and export from to the bottom of the file (or the top, it doesn't matter I don't think) - // 5. Export everything as getters in our HMR module - // 6. Call the HMRModule's exportAll function like so: - // __hmrModule.exportAll({ - // exportAlias: () => identifier, - // exportAlias: () => identifier, - // }); - // This has the unfortunate property of making property accesses of exports slower at runtime. - // But, I'm not sure there's a way to use regular properties without breaking stuff. - var imports_count: usize = 0; - // We have to also move export from, since we will preserve those - var exports_from_count: usize = 0; - // Two passes. First pass just counts. - for (parts[parts.len - 1].stmts) |stmt| { - imports_count += switch (stmt.data) { - .s_import => @as(usize, 1), - else => @as(usize, 0), - }; - exports_from_count += switch (stmt.data) { - .s_export_star, .s_export_from => @as(usize, 1), - else => @as(usize, 0), - }; - } - var part = &parts[parts.len - 1]; - - const end_iife_stmts_count = part.stmts.len - imports_count - exports_from_count + 1; - // Why 7? - // 1. HMRClient.activate(${isDebug}); - // 2. var __hmrModule = new HMMRModule(id, file_path), __exports = __hmrModule.exports; - // 3. (__hmrModule.load = function() { - // ${...end_iffe_stmts_count - 1} - // ${end_iffe_stmts_count} - // __hmrModule.exportAll({exportAlias: () => identifier}) <-- ${named_exports_count} - // (); - // 4. var __hmrExport_exportName = __hmrModule.exports.exportName, - // 5. export { __hmrExport_exportName as blah, ... } - // 6. __hmrModule.onSetExports = (newExports) => { - // $named_exports_count __hmrExport_exportName = newExports.exportName; <-- ${named_exports_count} - // } - - // if there are no exports: - // - there shouldn't be an export statement - // - we don't need the S.Local for wrapping the exports - // We still call exportAll just with an empty object. - const has_any_exports = named_exports_count > 0; - - const toplevel_stmts_count = 3 + (@as(usize, @intCast(@intFromBool(has_any_exports))) * 2); - var _stmts = allocator.alloc( - Stmt, - end_iife_stmts_count + toplevel_stmts_count + (named_exports_count * 2) + imports_count + exports_from_count, - ) catch unreachable; - // Normally, we'd have to grow that inner function's stmts list by one - // But we can avoid that by just making them all use this same array. - var curr_stmts = _stmts; - - // in debug: crash in the printer due to undefined memory - // in release: print ";" instead. - // this should never happen regardless, but i'm just being cautious here. - if (comptime !Environment.isDebug) { - @memset(_stmts, Stmt.empty()); - } - - // Second pass: move any imports from the part's stmts array to the new stmts - var imports_list = curr_stmts[0..imports_count]; - curr_stmts = curr_stmts[imports_list.len..]; - var toplevel_stmts = curr_stmts[0..toplevel_stmts_count]; - curr_stmts = curr_stmts[toplevel_stmts.len..]; - var exports_from = curr_stmts[0..exports_from_count]; - curr_stmts = curr_stmts[exports_from.len..]; - // This is used for onSetExports - var update_function_stmts = curr_stmts[0..named_exports_count]; - curr_stmts = curr_stmts[update_function_stmts.len..]; - var export_all_function_body_stmts = curr_stmts[0..named_exports_count]; - curr_stmts = curr_stmts[export_all_function_body_stmts.len..]; - // This is the original part statements + 1 - var part_stmts = curr_stmts; - if (comptime Environment.allow_assert) assert(part_stmts.len == end_iife_stmts_count); - var part_stmts_i: usize = 0; - - var import_list_i: usize = 0; - var export_list_i: usize = 0; - - // We must always copy it into the new stmts array - for (part.stmts) |stmt| { - switch (stmt.data) { - .s_import => { - imports_list[import_list_i] = stmt; - import_list_i += 1; - }, - .s_export_star, .s_export_from => { - exports_from[export_list_i] = stmt; - export_list_i += 1; - }, - else => { - part_stmts[part_stmts_i] = stmt; - part_stmts_i += 1; - }, - } - } - - const new_call_args_count: usize = if (p.options.features.react_fast_refresh) 3 else 2; - var call_args = try allocator.alloc(Expr, new_call_args_count + 1); - var new_call_args = call_args[0..new_call_args_count]; - const hmr_module_ident = p.newExpr(E.Identifier{ .ref = p.hmr_module.ref }, logger.Loc.Empty); - - new_call_args[0] = p.newExpr(E.Number{ .value = @as(f64, @floatFromInt(p.options.filepath_hash_for_hmr)) }, logger.Loc.Empty); - // This helps us provide better error messages - new_call_args[1] = p.newExpr(E.String{ .data = p.source.path.pretty }, logger.Loc.Empty); - if (p.options.features.react_fast_refresh) { - new_call_args[2] = p.newExpr(E.Identifier{ .ref = p.jsx_refresh_runtime.ref }, logger.Loc.Empty); - } - - var toplevel_stmts_i: u8 = 0; - - var decls = try allocator.alloc(G.Decl, 2 + named_exports_count); - var first_decl = decls[0..2]; - // We cannot rely on import.meta.url because if we import it within a blob: url, it will be nonsensical - // var __hmrModule = new HMRModule(123123124, "/index.js"), __exports = __hmrModule.exports; - const hmr_import_module_ = if (p.options.features.react_fast_refresh) - p.runtime_imports.__FastRefreshModule.? - else - p.runtime_imports.__HMRModule.?; - - const hmr_import_ref = hmr_import_module_.ref; - first_decl[0] = G.Decl{ - .binding = p.b(B.Identifier{ .ref = p.hmr_module.ref }, logger.Loc.Empty), - .value = p.newExpr(E.New{ - .args = ExprNodeList.init(new_call_args), - .target = p.newExpr( - E.Identifier{ - .ref = hmr_import_ref, - }, - logger.Loc.Empty, - ), - .close_parens_loc = logger.Loc.Empty, - }, logger.Loc.Empty), - }; - first_decl[1] = G.Decl{ - .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty), - .value = p.newExpr(E.Dot{ - .target = p.newExpr(E.Identifier{ .ref = p.hmr_module.ref }, logger.Loc.Empty), - .name = "exports", - .name_loc = logger.Loc.Empty, - }, logger.Loc.Empty), - }; - - var export_clauses = try allocator.alloc(js_ast.ClauseItem, named_exports_count); - var named_export_i: usize = 0; - var named_exports_iter = p.named_exports.iterator(); - var export_properties = try allocator.alloc(G.Property, named_exports_count); - - var export_name_string_length: usize = 0; - while (named_exports_iter.next()) |named_export| { - export_name_string_length += named_export.key_ptr.len + "$$hmr_".len; - } - - const export_name_string_all = try allocator.alloc(u8, export_name_string_length); - var export_name_string_remainder = export_name_string_all; - const hmr_module_exports_dot = p.newExpr( - E.Dot{ - .target = hmr_module_ident, - .name = "exports", - .name_loc = logger.Loc.Empty, - }, - logger.Loc.Empty, - ); - var exports_decls = decls[first_decl.len..]; - named_exports_iter = p.named_exports.iterator(); - var update_function_args = try allocator.alloc(G.Arg, 1); - const exports_ident = p.newExpr(E.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty); - update_function_args[0] = G.Arg{ .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty) }; - - while (named_exports_iter.next()) |named_export| { - const named_export_value = named_export.value_ptr.*; - - // Do not try to HMR export {foo} from 'bar'; - if (named_imports.get(named_export_value.ref)) |named_import| { - if (named_import.is_exported) continue; - } - - const named_export_symbol: Symbol = p.symbols.items[named_export_value.ref.innerIndex()]; - - var export_name_string = export_name_string_remainder[0 .. named_export.key_ptr.len + "$$hmr_".len]; - export_name_string_remainder = export_name_string_remainder[export_name_string.len..]; - bun.copy(u8, export_name_string, "$$hmr_"); - bun.copy(u8, export_name_string["$$hmr_".len..], named_export.key_ptr.*); - - const name_ref = try p.declareSymbol(.other, logger.Loc.Empty, export_name_string); - - var body_stmts = export_all_function_body_stmts[named_export_i .. named_export_i + 1]; - body_stmts[0] = p.s( - // was this originally a named import? - // preserve the identifier - S.Return{ .value = if (named_export_symbol.namespace_alias != null) - p.newExpr(E.ImportIdentifier{ - .ref = named_export_value.ref, - .was_originally_identifier = true, - }, logger.Loc.Empty) - else - p.newExpr(E.Identifier{ - .ref = named_export_value.ref, - }, logger.Loc.Empty) }, - logger.Loc.Empty, - ); - export_clauses[named_export_i] = js_ast.ClauseItem{ - .original_name = "", - .alias = named_export.key_ptr.*, - .alias_loc = named_export_value.alias_loc, - .name = .{ .ref = name_ref, .loc = logger.Loc.Empty }, - }; - - const decl_value = p.newExpr( - E.Dot{ .target = hmr_module_exports_dot, .name = named_export.key_ptr.*, .name_loc = logger.Loc.Empty }, - logger.Loc.Empty, - ); - exports_decls[named_export_i] = G.Decl{ - .binding = p.b(B.Identifier{ .ref = name_ref }, logger.Loc.Empty), - .value = decl_value, - }; - - update_function_stmts[named_export_i] = Stmt.assign( - p.newExpr( - E.Identifier{ .ref = name_ref }, - logger.Loc.Empty, - ), - p.newExpr(E.Dot{ - .target = exports_ident, - .name = named_export.key_ptr.*, - .name_loc = logger.Loc.Empty, - }, logger.Loc.Empty), - ); - - export_properties[named_export_i] = G.Property{ - .key = p.newExpr(E.String{ .data = named_export.key_ptr.* }, logger.Loc.Empty), - .value = p.newExpr( - E.Arrow{ - .args = &[_]G.Arg{}, - .body = .{ - .stmts = body_stmts, - .loc = logger.Loc.Empty, - }, - .prefer_expr = true, - }, - logger.Loc.Empty, - ), - }; - named_export_i += 1; - } - var export_all_args = call_args[new_call_args.len..]; - export_all_args[0] = p.newExpr( - E.Object{ .properties = Property.List.init(export_properties[0..named_export_i]) }, - logger.Loc.Empty, - ); - - part_stmts[part_stmts.len - 1] = p.s( - S.SExpr{ - .value = p.newExpr( - E.Call{ - .target = p.newExpr( - E.Dot{ - .target = hmr_module_ident, - .name = "exportAll", - .name_loc = logger.Loc.Empty, - }, - logger.Loc.Empty, - ), - .args = ExprNodeList.init(export_all_args), - }, - logger.Loc.Empty, - ), - }, - logger.Loc.Empty, - ); - - toplevel_stmts[toplevel_stmts_i] = p.s( - S.Local{ - .decls = G.Decl.List.init(first_decl), - }, - logger.Loc.Empty, - ); - - toplevel_stmts_i += 1; - - const is_async = !p.top_level_await_keyword.isEmpty(); - - const func = p.newExpr( - E.Function{ - .func = .{ - .body = .{ .loc = logger.Loc.Empty, .stmts = part_stmts[0 .. part_stmts_i + 1] }, - .name = null, - .open_parens_loc = logger.Loc.Empty, - .flags = Flags.Function.init(.{ - .print_as_iife = true, - .is_async = is_async, - }), - }, - }, - logger.Loc.Empty, - ); - - const call_load = p.newExpr( - E.Call{ - .target = Expr.assign( - p.newExpr( - E.Dot{ - .name = "_load", - .target = hmr_module_ident, - .name_loc = logger.Loc.Empty, - }, - logger.Loc.Empty, - ), - func, - ), - }, - logger.Loc.Empty, - ); - // (__hmrModule._load = function())() - toplevel_stmts[toplevel_stmts_i] = p.s( - S.SExpr{ - .value = if (is_async) - p.newExpr(E.Await{ .value = call_load }, logger.Loc.Empty) - else - call_load, - }, - logger.Loc.Empty, - ); - - toplevel_stmts_i += 1; - - if (has_any_exports) { - if (named_export_i > 0) { - toplevel_stmts[toplevel_stmts_i] = p.s( - S.Local{ - .decls = G.Decl.List.init(exports_decls[0..named_export_i]), - }, - logger.Loc.Empty, - ); - } else { - toplevel_stmts[toplevel_stmts_i] = p.s( - S.Empty{}, - logger.Loc.Empty, - ); - } - - toplevel_stmts_i += 1; - } - - toplevel_stmts[toplevel_stmts_i] = p.s( - S.SExpr{ - .value = Expr.assign( - p.newExpr( - E.Dot{ - .name = "_update", - .target = hmr_module_ident, - .name_loc = logger.Loc.Empty, - }, - logger.Loc.Empty, - ), - p.newExpr( - E.Function{ - .func = .{ - .body = .{ .loc = logger.Loc.Empty, .stmts = if (named_export_i > 0) update_function_stmts[0..named_export_i] else &.{} }, - .name = null, - .args = update_function_args, - .open_parens_loc = logger.Loc.Empty, - }, - }, - logger.Loc.Empty, - ), - ), - }, - logger.Loc.Empty, - ); - toplevel_stmts_i += 1; - if (has_any_exports) { - if (named_export_i > 0) { - toplevel_stmts[toplevel_stmts_i] = p.s( - S.ExportClause{ - .items = export_clauses[0..named_export_i], - }, - logger.Loc.Empty, - ); - } else { - toplevel_stmts[toplevel_stmts_i] = p.s( - S.Empty{}, - logger.Loc.Empty, - ); - } - } + }, + logger.Loc.Empty, + ); - part.stmts = _stmts[0 .. imports_list.len + toplevel_stmts.len + exports_from.len]; - } - }, + var top_level_stmts = p.allocator.alloc(Stmt, 1) catch bun.outOfMemory(); + parts[0].stmts = top_level_stmts; + top_level_stmts[0] = p.s( + S.SExpr{ + .value = wrapper, + }, + logger.Loc.Empty, + ); + parts.len = 1; } + var top_level_symbols_to_parts = js_ast.Ast.TopLevelSymbolToParts{}; var top_level = &top_level_symbols_to_parts; @@ -22909,26 +23470,27 @@ fn NewParser_( } const wrapper_ref: Ref = brk: { - if (p.options.bundle) { + if (p.options.bundle and p.needsWrapperRef(parts)) { break :brk p.newSymbol( .other, std.fmt.allocPrint( p.allocator, "require_{any}", - .{ - p.source.fmtIdentifier(), - }, - ) catch unreachable, - ) catch unreachable; + .{p.source.fmtIdentifier()}, + ) catch bun.outOfMemory(), + ) catch bun.outOfMemory(); } break :brk Ref.None; }; + var parts_list = bun.BabyList(js_ast.Part).init(parts); + parts_list.cap = @intCast(input_parts.len); + return .{ .allocator = p.allocator, .runtime_imports = p.runtime_imports, - .parts = bun.BabyList(js_ast.Part).init(parts), + .parts = parts_list, .module_scope = p.module_scope.*, .symbols = js_ast.Symbol.List.init(p.symbols.items), .exports_ref = p.exports_ref, @@ -22960,25 +23522,383 @@ fn NewParser_( p.require_ref, .force_cjs_to_esm = p.unwrap_all_requires or exports_kind == .esm_with_dynamic_fallback_from_cjs, - .uses_module_ref = (p.symbols.items[p.module_ref.innerIndex()].use_count_estimate > 0), - .uses_exports_ref = (p.symbols.items[p.exports_ref.innerIndex()].use_count_estimate > 0), - .uses_require_ref = if (p.runtime_imports.__require != null) - (p.symbols.items[p.runtime_imports.__require.?.ref.innerIndex()].use_count_estimate > 0) - else - false, + .uses_module_ref = p.symbols.items[p.module_ref.inner_index].use_count_estimate > 0, + .uses_exports_ref = p.symbols.items[p.exports_ref.inner_index].use_count_estimate > 0, + .uses_require_ref = p.runtime_imports.__require != null and + p.symbols.items[p.runtime_imports.__require.?.ref.inner_index].use_count_estimate > 0, + .commonjs_module_exports_assigned_deoptimized = p.commonjs_module_exports_assigned_deoptimized, // .top_Level_await_keyword = p.top_level_await_keyword, .commonjs_named_exports = p.commonjs_named_exports, - .commonjs_export_names = p.commonjs_export_names.keys(), + .has_commonjs_export_names = p.has_commonjs_export_names, .hashbang = hashbang, - // TODO: + // TODO: cross-module constant inlining // .const_values = p.const_values, + .ts_enums = try p.computeTsEnumsMap(allocator), .import_meta_ref = p.import_meta_ref, }; } + /// The bundler will generate wrappers to contain top-level side effects using + /// the '__esm' helper. Example: + /// + /// var init_foo = __esm(() => { + /// someExport = Math.random(); + /// }); + /// + /// This wrapper can be removed if all of the constructs get moved + /// outside of the file. Due to paralleization, we can't retroactively + /// delete the `init_foo` symbol, but instead it must be known far in + /// advance if the symbol is needed or not. + /// + /// The logic in this function must be in sync with the hoisting + /// logic in `LinkerContext.generateCodeForFileInChunkJS` + fn needsWrapperRef(p: *const P, parts: []const js_ast.Part) bool { + bun.assert(p.options.bundle); + for (parts) |part| { + for (part.stmts) |stmt| { + switch (stmt.data) { + .s_function => {}, + .s_class => |class| if (!class.class.canBeMoved()) return true, + .s_local => |local| { + if (local.was_commonjs_export or p.commonjs_named_exports.count() == 0) { + for (local.decls.slice()) |decl| { + if (decl.value) |value| + if (value.data != .e_missing and !value.canBeMoved()) + return true; + } + continue; + } + return true; + }, + .s_export_default => |ed| { + if (!ed.canBeMoved()) + return true; + }, + .s_export_equals => |e| { + if (!e.value.canBeMoved()) + return true; + }, + else => return true, + } + } + } + return false; + } + + const ConvertESMExportsForHmr = struct { + last_part: *js_ast.Part, + imports_seen: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, + export_props: std.ArrayListUnmanaged(G.Property) = .{}, + stmts: std.ArrayListUnmanaged(Stmt) = .{}, + + fn convertStmt(ctx: *ConvertESMExportsForHmr, p: *P, stmt: Stmt) !void { + const new_stmt = switch (stmt.data) { + else => stmt, + .s_local => |st| stmt: { + if (!st.is_export) break :stmt stmt; + + st.is_export = false; + + if (st.kind.isReassignable()) { + for (st.decls.slice()) |decl| { + try ctx.visitBindingForKitModuleExports(p, decl.binding, true); + } + } else { + // TODO: remove this dupe + var dupe_decls = try std.ArrayListUnmanaged(G.Decl).initCapacity(p.allocator, st.decls.len); + + for (st.decls.slice()) |decl| { + bun.assert(decl.value != null); // const must be initialized + + switch (decl.binding.data) { + .b_missing => @panic("binding missing"), + + .b_identifier => |id| { + const symbol = p.symbols.items[id.ref.inner_index]; + + // if the symbol is not used, we don't need to preserve + // a binding in this scope. we can move it to the exports object. + if (symbol.use_count_estimate != 0 or !decl.value.?.canBeMoved()) { + dupe_decls.appendAssumeCapacity(decl); + } + + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ .data = symbol.original_name }, decl.binding.loc), + .value = decl.value, + }); + }, + + else => { + dupe_decls.appendAssumeCapacity(decl); + try ctx.visitBindingForKitModuleExports(p, decl.binding, false); + }, + } + } + + if (dupe_decls.items.len == 0) { + return; + } + + st.decls = G.Decl.List.fromList(dupe_decls); + } + + break :stmt stmt; + }, + .s_export_default => |st| stmt: { + // Simple case: we can move this to the default property of the exports object + if (st.canBeMoved()) { + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ .data = "default" }, stmt.loc), + .value = st.value.toExpr(), + }); + // no statement emitted + return; + } + + // Otherwise, we need a temporary + const temp_id = p.generateTempRef("default_export"); + try ctx.last_part.declared_symbols.append(p.allocator, .{ .ref = temp_id, .is_top_level = true }); + try ctx.last_part.symbol_uses.putNoClobber(p.allocator, temp_id, .{ .count_estimate = 1 }); + try p.module_scope.generated.push(p.allocator, temp_id); + + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ .data = "default" }, stmt.loc), + .value = Expr.initIdentifier(temp_id, stmt.loc), + }); + + break :stmt Stmt.alloc(S.Local, .{ + .kind = .k_const, + .decls = try G.Decl.List.fromSlice(p.allocator, &.{ + .{ + .binding = Binding.alloc(p.allocator, B.Identifier{ .ref = temp_id }, stmt.loc), + .value = st.value.toExpr(), + }, + }), + }, stmt.loc); + }, + .s_class => |st| stmt: { + // Strip the "export" keyword + if (!st.is_export) break :stmt stmt; + + // Export as CommonJS + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ + .data = p.symbols.items[st.class.class_name.?.ref.?.inner_index].original_name, + }, stmt.loc), + .value = Expr.initIdentifier(st.class.class_name.?.ref.?, stmt.loc), + }); + + st.is_export = false; + + break :stmt stmt; + }, + .s_function => |st| stmt: { + // Strip the "export" keyword + if (!st.func.flags.contains(.is_export)) break :stmt stmt; + + st.func.flags.remove(.is_export); + + // Export as CommonJS + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ + .data = p.symbols.items[st.func.name.?.ref.?.inner_index].original_name, + }, stmt.loc), + .value = Expr.initIdentifier(st.func.name.?.ref.?, stmt.loc), + }); + + break :stmt stmt; + }, + .s_export_clause => |st| { + for (st.items) |item| { + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ + .data = item.alias, + }, stmt.loc), + .value = Expr.initIdentifier(item.name.ref.?, item.name.loc), + }); + } + + return; // do not emit a statement here + }, + + .s_export_from => |st| { + _ = st; // autofix + @panic("TODO s_export_from"); + }, + .s_export_star => |st| { + _ = st; // autofix + @panic("TODO s_export_star"); + }, + + // De-duplicate import statements. It is okay to disregard + // named/default imports here as we always rewrite them as + // full qualified property accesses (need to so live-bindings) + .s_import => |st| stmt: { + const gop = try ctx.imports_seen.getOrPut(p.allocator, st.import_record_index); + if (gop.found_existing) return; + break :stmt stmt; + }, + }; + + try ctx.stmts.append(p.allocator, new_stmt); + } + + fn visitBindingForKitModuleExports( + ctx: *ConvertESMExportsForHmr, + p: *P, + binding: Binding, + is_live_binding: bool, + ) !void { + switch (binding.data) { + .b_missing => @panic("missing!"), + .b_identifier => |id| { + try ctx.visitRefForKitModuleExports(p, id.ref, binding.loc, is_live_binding); + }, + .b_array => |array| { + for (array.items) |item| { + try ctx.visitBindingForKitModuleExports(p, item.binding, is_live_binding); + } + }, + .b_object => |object| { + for (object.properties) |item| { + try ctx.visitBindingForKitModuleExports(p, item.value, is_live_binding); + } + }, + } + } + + fn visitRefForKitModuleExports( + ctx: *ConvertESMExportsForHmr, + p: *P, + ref: Ref, + loc: logger.Loc, + is_live_binding: bool, + ) !void { + const symbol = p.symbols.items[ref.inner_index]; + const id = Expr.initIdentifier(ref, loc); + if (is_live_binding) { + const key = Expr.init(E.String, .{ + .data = symbol.original_name, + }, loc); + + // This is technically incorrect in that we've marked this as a + // top level symbol. but all we care about is preventing name + // collisions, not necessarily the best minificaiton (dev only) + const arg1 = p.generateTempRef(symbol.original_name); + try ctx.last_part.declared_symbols.append(p.allocator, .{ .ref = arg1, .is_top_level = true }); + try ctx.last_part.symbol_uses.putNoClobber(p.allocator, arg1, .{ .count_estimate = 1 }); + try p.module_scope.generated.push(p.allocator, arg1); + + // Live bindings need to update the value internally and externally. + // 'get abc() { return abc }' + try ctx.export_props.append(p.allocator, .{ + .kind = .get, + .key = key, + .value = Expr.init(E.Function, .{ .func = .{ + .body = .{ + .stmts = try p.allocator.dupe(Stmt, &.{ + Stmt.alloc(S.Return, .{ .value = id }, loc), + }), + .loc = loc, + }, + } }, loc), + }); + // 'set abc(abc2) { abc = abc2 }' + try ctx.export_props.append(p.allocator, .{ + .kind = .set, + .key = key, + .value = Expr.init(E.Function, .{ .func = .{ + .args = try p.allocator.dupe(G.Arg, &.{.{ + .binding = Binding.alloc(p.allocator, B.Identifier{ .ref = arg1 }, loc), + }}), + .body = .{ + .stmts = try p.allocator.dupe(Stmt, &.{ + Stmt.alloc(S.SExpr, .{ + .value = Expr.assign(id, Expr.initIdentifier(arg1, loc)), + }, loc), + }), + .loc = loc, + }, + } }, loc), + }); + } else { + // 'abc,' + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ + .data = symbol.original_name, + }, loc), + .value = id, + }); + } + } + + pub fn finalize(ctx: *ConvertESMExportsForHmr, p: *P, all_parts: []js_ast.Part) ![]js_ast.Part { + if (ctx.export_props.items.len > 0) { + // add a marker for the client runtime to tell that this is an ES module + try ctx.stmts.append(p.allocator, Stmt.alloc(S.SExpr, .{ + .value = Expr.assign( + Expr.init(E.Dot, .{ + .target = Expr.initIdentifier(p.module_ref, logger.Loc.Empty), + .name = "__esModule", + .name_loc = logger.Loc.Empty, + }, logger.Loc.Empty), + Expr.init(E.Boolean, .{ .value = true }, logger.Loc.Empty), + ), + }, logger.Loc.Empty)); + + try ctx.stmts.append(p.allocator, Stmt.alloc(S.SExpr, .{ + .value = Expr.assign( + Expr.init(E.Dot, .{ + .target = Expr.initIdentifier(p.module_ref, logger.Loc.Empty), + .name = "exports", + .name_loc = logger.Loc.Empty, + }, logger.Loc.Empty), + Expr.init(E.Object, .{ + .properties = G.Property.List.fromList(ctx.export_props), + }, logger.Loc.Empty), + ), + }, logger.Loc.Empty)); + + // mark a dependency on module_ref so it is renamed + try ctx.last_part.symbol_uses.put(p.allocator, p.module_ref, .{ .count_estimate = 1 }); + try ctx.last_part.declared_symbols.append(p.allocator, .{ .ref = p.module_ref, .is_top_level = true }); + } + + // TODO: this is a tiny mess. it is honestly trying to hard to merge all parts into one + for (all_parts[0 .. all_parts.len - 1]) |*part| { + try ctx.last_part.declared_symbols.appendList(p.allocator, part.declared_symbols); + try ctx.last_part.import_record_indices.append(p.allocator, part.import_record_indices.slice()); + for (part.symbol_uses.keys(), part.symbol_uses.values()) |k, v| { + const gop = try ctx.last_part.symbol_uses.getOrPut(p.allocator, k); + if (!gop.found_existing) { + gop.value_ptr.* = v; + } else { + gop.value_ptr.count_estimate += v.count_estimate; + } + } + part.stmts = &.{}; + part.declared_symbols.entries.len = 0; + part.tag = .dead_due_to_inlining; + part.dependencies.clearRetainingCapacity(); + try part.dependencies.push(p.allocator, .{ + .part_index = @intCast(all_parts.len - 1), + .source_index = p.source.index, + }); + } + + try ctx.last_part.import_record_indices.append(p.allocator, p.import_records_for_current_part.items); + try ctx.last_part.declared_symbols.appendList(p.allocator, p.declared_symbols); + + ctx.last_part.stmts = ctx.stmts.items; + ctx.last_part.tag = .none; + + return all_parts; + } + }; + pub fn init( allocator: Allocator, log: *logger.Log, @@ -22991,9 +23911,9 @@ fn NewParser_( var scope_order = try ScopeOrderList.initCapacity(allocator, 1); const scope = try allocator.create(Scope); scope.* = Scope{ - .members = @TypeOf(scope.members){}, - .children = @TypeOf(scope.children){}, - .generated = @TypeOf(scope.generated){}, + .members = .{}, + .children = .{}, + .generated = .{}, .kind = .entry, .label_ref = null, .parent = null, @@ -23009,7 +23929,6 @@ fn NewParser_( .call_target = nullExprData, .delete_target = nullExprData, .stmt_expr_value = nullExprData, - .expr_list = .{}, .loop_body = nullStmtData, .define = define, .import_records = undefined, @@ -23171,8 +24090,18 @@ pub fn newLazyExportAST( return result.ast; } -const CommonJSWrapper = union(enum) { - none: void, - bun_dev: Expr, - bun_js: void, +const WrapMode = enum { + none, + bun_commonjs, }; + +/// Equivalent of esbuild's js_ast_helpers.ToInt32 +fn floatToInt32(f: f64) i32 { + // Special-case non-finite numbers + if (!std.math.isFinite(f)) + return 0; + + const uint: u32 = @intFromFloat(@mod(@abs(f), std.math.maxInt(u32) + 1)); + const int: i32 = @bitCast(uint); + return if (f < 0) @as(i32, 0) -% int else int; +} diff --git a/src/js_printer.zig b/src/js_printer.zig index 1861990846877..976a29ddd751b 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -65,8 +65,8 @@ const ascii_only_always_on_unless_minifying = true; fn formatUnsignedIntegerBetween(comptime len: u16, buf: *[len]u8, val: u64) void { comptime var i: u16 = len; var remainder = val; - // Write out the number from the end to the front + // Write out the number from the end to the front inline while (i > 0) { comptime i -= 1; buf[comptime i] = @as(u8, @intCast((remainder % 10))) + '0'; @@ -88,7 +88,8 @@ pub fn canPrintWithoutEscape(comptime CodePointType: type, c: CodePointType, com } } -const indentation_buf = [_]u8{' '} ** 128; +const indentation_space_buf = [_]u8{' '} ** 128; +const indentation_tab_buf = [_]u8{'\t'} ** 128; pub fn bestQuoteCharForString(comptime Type: type, str: []const Type, allow_backtick: bool) u8 { var single_cost: usize = 0; @@ -516,7 +517,7 @@ pub const Options = struct { to_esm_ref: Ref = Ref.None, require_ref: ?Ref = null, import_meta_ref: Ref = Ref.None, - indent: usize = 0, + indent: Indentation = .{}, externals: []u32 = &[_]u32{}, runtime_imports: runtime.Runtime.Imports = runtime.Runtime.Imports{}, module_hash: u32 = 0, @@ -525,26 +526,33 @@ pub const Options = struct { source_map_handler: ?SourceMapHandler = null, source_map_builder: ?*bun.sourcemap.Chunk.Builder = null, css_import_behavior: Api.CssInJsBehavior = Api.CssInJsBehavior.facade, + target: options.Target = .browser, runtime_transpiler_cache: ?*bun.JSC.RuntimeTranspilerCache = null, + input_files_for_kit: ?[]logger.Source = null, commonjs_named_exports: js_ast.Ast.CommonJSNamedExports = .{}, commonjs_named_exports_deoptimized: bool = false, + commonjs_module_exports_assigned_deoptimized: bool = false, commonjs_named_exports_ref: Ref = Ref.None, + commonjs_module_ref: Ref = Ref.None, minify_whitespace: bool = false, minify_identifiers: bool = false, minify_syntax: bool = false, + print_dce_annotations: bool = true, + transform_only: bool = false, inline_require_and_import_errors: bool = true, has_run_symbol_renamer: bool = false, require_or_import_meta_for_source_callback: RequireOrImportMeta.Callback = .{}, - module_type: options.OutputFormat = .preserve, + module_type: options.Format = .esm, - /// Used for cross-module inlining of import items when bundling - const_values: std.HashMapUnmanaged(Ref, Expr, Ref.HashCtx, 80) = .{}, + // /// Used for cross-module inlining of import items when bundling + // const_values: Ast.ConstValuesMap = .{}, + ts_enums: Ast.TsEnumsMap = .{}, // TODO: remove this // The reason for this is: @@ -566,9 +574,14 @@ pub const Options = struct { // us do binary search on to figure out what line a given AST node came from line_offset_tables: ?SourceMap.LineOffsetTable.List = null, - pub inline fn unindent(self: *Options) void { - self.indent -|= 1; - } + // Default indentation is 2 spaces + pub const Indentation = struct { + scalar: usize = 2, + count: usize = 0, + character: Character = .space, + + pub const Character = enum { tab, space }; + }; pub fn requireOrImportMetaForSource( self: *const Options, @@ -1004,16 +1017,25 @@ fn NewPrinter( } } - pub inline fn unsafePrint(p: *Printer, str: string) void { - p.print(str); + pub inline fn unindent(p: *Printer) void { + p.options.indent.count -|= 1; + } + + pub inline fn indent(p: *Printer) void { + p.options.indent.count += 1; } pub fn printIndent(p: *Printer) void { - if (p.options.indent == 0 or p.options.minify_whitespace) { + if (p.options.indent.count == 0 or p.options.minify_whitespace) { return; } - var i: usize = p.options.indent * 2; + const indentation_buf = switch (p.options.indent.character) { + .space => indentation_space_buf, + .tab => indentation_tab_buf, + }; + + var i: usize = p.options.indent.count * p.options.indent.scalar; while (i > 0) { const amt = @min(i, indentation_buf.len); @@ -1085,7 +1107,14 @@ fn NewPrinter( p.print("="); p.printSpaceBeforeIdentifier(); if (comptime Statement == void) { - p.printRequireOrImportExpr(import.import_record_index, false, &.{}, Level.lowest, ExprFlag.None()); + p.printRequireOrImportExpr( + import.import_record_index, + false, + &.{}, + Expr.empty, + Level.lowest, + ExprFlag.None(), + ); } else { p.print(statement); } @@ -1099,7 +1128,14 @@ fn NewPrinter( p.printSymbol(default.ref.?); if (comptime Statement == void) { p.@"print = "(); - p.printRequireOrImportExpr(import.import_record_index, false, &.{}, Level.lowest, ExprFlag.None()); + p.printRequireOrImportExpr( + import.import_record_index, + false, + &.{}, + Expr.empty, + Level.lowest, + ExprFlag.None(), + ); } else { p.@"print = "(); p.print(statement); @@ -1112,7 +1148,7 @@ fn NewPrinter( if (!import.is_single_line) { p.printNewline(); - p.options.indent += 1; + p.indent(); p.printIndent(); } @@ -1132,7 +1168,7 @@ fn NewPrinter( if (!import.is_single_line) { p.printNewline(); - p.options.unindent(); + p.unindent(); } else { p.printSpace(); } @@ -1141,7 +1177,7 @@ fn NewPrinter( if (import.star_name_loc == null and import.default_name == null) { if (comptime Statement == void) { - p.printRequireOrImportExpr(import.import_record_index, false, &.{}, Level.lowest, ExprFlag.None()); + p.printRequireOrImportExpr(import.import_record_index, false, &.{}, Expr.empty, Level.lowest, ExprFlag.None()); } else { p.print(statement); } @@ -1204,9 +1240,9 @@ fn NewPrinter( }, else => { p.printNewline(); - p.options.indent += 1; + p.indent(); p.printStmt(stmt) catch unreachable; - p.options.unindent(); + p.unindent(); }, } } @@ -1223,9 +1259,9 @@ fn NewPrinter( p.print("{"); p.printNewline(); - p.options.indent += 1; + p.indent(); p.printBlockBody(stmts); - p.options.unindent(); + p.unindent(); p.needs_semicolon = false; p.printIndent(); @@ -1240,10 +1276,10 @@ fn NewPrinter( p.print("{"); p.printNewline(); - p.options.indent += 1; + p.indent(); p.printBlockBody(prepend); p.printBlockBody(stmts); - p.options.unindent(); + p.unindent(); p.needs_semicolon = false; p.printIndent(); @@ -1483,7 +1519,7 @@ fn NewPrinter( p.addSourceMapping(class.body_loc); p.print("{"); p.printNewline(); - p.options.indent += 1; + p.indent(); for (class.properties) |item| { p.printSemicolonIfNeeded(); @@ -1507,7 +1543,7 @@ fn NewPrinter( } p.needs_semicolon = false; - p.options.unindent(); + p.unindent(); p.printIndent(); if (class.close_brace_loc.start > class.body_loc.start) p.addSourceMapping(class.close_brace_loc); @@ -1896,9 +1932,12 @@ fn NewPrinter( import_record_index: u32, was_unwrapped_require: bool, leading_interior_comments: []G.Comment, + import_options: Expr, level_: Level, flags: ExprFlag.Set, ) void { + _ = leading_interior_comments; // TODO: + var level = level_; const wrap = level.gte(.new) or flags.contains(.forbid_call); if (wrap) p.print("("); @@ -1978,12 +2017,12 @@ fn NewPrinter( } defer if (record.kind == .dynamic) p.printDotThenSuffix(); - // Make sure the comma operator is propertly wrapped - - if (meta.exports_ref.isValid() and level.gte(.comma)) { - p.print("("); - } - defer if (meta.exports_ref.isValid() and level.gte(.comma)) p.print(")"); + // Make sure the comma operator is properly wrapped + const wrap_comma_operator = meta.exports_ref.isValid() and + meta.wrapper_ref.isValid() and + level.gte(.comma); + if (wrap_comma_operator) p.print("("); + defer if (wrap_comma_operator) p.print(")"); // Wrap this with a call to "__toESM()" if this is a CommonJS file const wrap_with_to_esm = record.wrap_with_to_esm; @@ -1993,18 +2032,31 @@ fn NewPrinter( p.print("("); } - if (!meta.was_unwrapped_require) { - - // Call the wrapper + if (p.options.input_files_for_kit) |input_files| { + bun.assert(p.options.module_type == .internal_kit_dev); p.printSpaceBeforeIdentifier(); - p.printSymbol(meta.wrapper_ref); - p.print("()"); + p.printSymbol(p.options.commonjs_module_ref); + p.print(".require("); + { + const path = input_files[record.source_index.get()].path; + p.printInlinedEnum(.{ .number = @floatFromInt(path.hashForKit()) }, path.pretty, level); + } + p.print(")"); + } else if (!meta.was_unwrapped_require) { + // Call the wrapper + if (meta.wrapper_ref.isValid()) { + p.printSpaceBeforeIdentifier(); + p.printSymbol(meta.wrapper_ref); + p.print("()"); + + if (meta.exports_ref.isValid()) { + p.print(","); + p.printSpace(); + } + } // Return the namespace object if this is an ESM file if (meta.exports_ref.isValid()) { - p.print(","); - p.printSpace(); - // Wrap this with a call to "__toCommonJS()" if this is an ESM file const wrap_with_to_cjs = record.wrap_with_to_commonjs; if (wrap_with_to_cjs) { @@ -2070,14 +2122,14 @@ fn NewPrinter( } // External import() - if (leading_interior_comments.len > 0) { - p.printNewline(); - p.options.indent += 1; - for (leading_interior_comments) |comment| { - p.printIndentedComment(comment.text); - } - p.printIndent(); - } + // if (leading_interior_comments.len > 0) { + // p.printNewline(); + // p.indent(); + // for (leading_interior_comments) |comment| { + // p.printIndentedComment(comment.text); + // } + // p.printIndent(); + // } p.addSourceMapping(record.range.loc); p.printSpaceBeforeIdentifier(); @@ -2086,49 +2138,30 @@ fn NewPrinter( p.print("import("); p.printImportRecordPath(record); - switch (record.tag) { - .with_type_sqlite, .with_type_sqlite_embedded => { - // we do not preserve "embed": "true" since it is not necessary - p.printWhitespacer(ws(", { with: { type: \"sqlite\" } }")); - }, - .with_type_text => { - if (comptime is_bun_platform) { - p.printWhitespacer(ws(", { with: { type: \"text\" } }")); - } - }, - .with_type_json => { - // backwards compatibility: previously, we always stripped type json - if (comptime is_bun_platform) { - p.printWhitespacer(ws(", { with: { type: \"json\" } }")); - } - }, - .with_type_toml => { - // backwards compatibility: previously, we always stripped type - if (comptime is_bun_platform) { - p.printWhitespacer(ws(", { with: { type: \"toml\" } }")); - } - }, - .with_type_file => { - // backwards compatibility: previously, we always stripped type - if (comptime is_bun_platform) { - p.printWhitespacer(ws(", { with: { type: \"file\" } }")); - } - }, - else => {}, + if (!import_options.isMissing()) { + // since we previously stripped type, it is a breaking change to + // enable this for non-bun platforms + if (is_bun_platform or bun.FeatureFlags.breaking_changes_1_2) { + p.printWhitespacer(ws(", ")); + p.printExpr(import_options, .comma, .{}); + } } + p.print(")"); - if (leading_interior_comments.len > 0) { - p.printNewline(); - p.options.unindent(); - p.printIndent(); - } + // if (leading_interior_comments.len > 0) { + // p.printNewline(); + // p.unindent(); + // p.printIndent(); + // } return; } - // noop for now - pub inline fn printPure(_: *Printer) void {} + pub inline fn printPure(p: *Printer) void { + if (Environment.allow_assert) assert(p.options.print_dce_annotations); + p.printWhitespacer(ws("/* @__PURE__ */ ")); + } pub fn printQuotedUTF8(p: *Printer, str: string, allow_backtick: bool) void { const quote = if (comptime !is_json) @@ -2259,8 +2292,8 @@ fn NewPrinter( } } - pub fn printExpr(p: *Printer, expr: Expr, level: Level, _flags: ExprFlag.Set) void { - var flags = _flags; + pub fn printExpr(p: *Printer, expr: Expr, level: Level, in_flags: ExprFlag.Set) void { + var flags = in_flags; switch (expr.data) { .e_missing => {}, @@ -2295,7 +2328,10 @@ fn NewPrinter( .e_import_meta => { p.printSpaceBeforeIdentifier(); p.addSourceMapping(expr.loc); - if (!p.options.import_meta_ref.isValid()) { + if (p.options.module_type == .internal_kit_dev) { + p.printSymbol(p.options.commonjs_module_ref); + p.print(".importMeta()"); + } else if (!p.options.import_meta_ref.isValid()) { // Most of the time, leave it in there p.print("import.meta"); } else { @@ -2310,6 +2346,64 @@ fn NewPrinter( p.printSymbol(p.options.import_meta_ref); } }, + .e_import_meta_main => |data| { + if (p.options.module_type == .esm and p.options.target != .node) { + // Node.js doesn't support import.meta.main + // Most of the time, leave it in there + if (data.inverted) { + p.addSourceMapping(expr.loc); + p.print("!"); + } else { + p.printSpaceBeforeIdentifier(); + p.addSourceMapping(expr.loc); + } + p.print("import.meta.main"); + } else { + bun.assert(p.options.module_type != .internal_kit_dev); + + p.printSpaceBeforeIdentifier(); + p.addSourceMapping(expr.loc); + + if (p.options.require_ref) |require| + p.printSymbol(require) + else + p.print("require"); + + if (data.inverted) + p.printWhitespacer(ws(".main != ")) + else + p.printWhitespacer(ws(".main == ")); + + if (p.options.target == .node) { + // "__require.module" + if (p.options.require_ref) |require| + p.printSymbol(require) + else + p.print("require"); + + p.print(".module"); + } else if (p.options.commonjs_module_ref.isValid()) { + p.printSymbol(p.options.commonjs_module_ref); + } else { + p.print("module"); + } + } + }, + .e_module_dot_exports => { + p.printSpaceBeforeIdentifier(); + p.addSourceMapping(expr.loc); + + if (p.options.commonjs_module_exports_assigned_deoptimized) { + if (p.options.commonjs_module_ref.isValid()) { + p.printSymbol(p.options.commonjs_module_ref); + } else { + p.print("module"); + } + p.print(".exports"); + } else { + p.printSymbol(p.options.commonjs_named_exports_ref); + } + }, .e_commonjs_export_identifier => |id| { p.printSpaceBeforeIdentifier(); p.addSourceMapping(expr.loc); @@ -2317,7 +2411,16 @@ fn NewPrinter( for (p.options.commonjs_named_exports.keys(), p.options.commonjs_named_exports.values()) |key, value| { if (value.loc_ref.ref.?.eql(id.ref)) { if (p.options.commonjs_named_exports_deoptimized or value.needs_decl) { - p.printSymbol(p.options.commonjs_named_exports_ref); + if (p.options.commonjs_module_exports_assigned_deoptimized and + id.base == .module_dot_exports and + p.options.commonjs_module_ref.isValid()) + { + p.printSymbol(p.options.commonjs_module_ref); + p.print(".exports"); + } else { + p.printSymbol(p.options.commonjs_named_exports_ref); + } + if (p.canPrintIdentifier(key)) { p.print("."); p.print(key); @@ -2334,7 +2437,7 @@ fn NewPrinter( } }, .e_new => |e| { - const has_pure_comment = e.can_be_unwrapped_if_unused; + const has_pure_comment = e.can_be_unwrapped_if_unused and p.options.print_dce_annotations; const wrap = level.gte(.call) or (has_pure_comment and level.gte(.postfix)); if (wrap) { @@ -2384,7 +2487,7 @@ fn NewPrinter( wrap = true; } - const has_pure_comment = e.can_be_unwrapped_if_unused; + const has_pure_comment = e.can_be_unwrapped_if_unused and p.options.print_dce_annotations; if (has_pure_comment and level.gte(.postfix)) { wrap = true; } @@ -2438,6 +2541,19 @@ fn NewPrinter( p.print(")"); } }, + .e_require_main => { + p.printSpaceBeforeIdentifier(); + p.addSourceMapping(expr.loc); + + if (p.options.module_type == .esm and is_bun_platform) { + p.print("import.meta.require.main"); + } else if (p.options.require_ref) |require_ref| { + p.printSymbol(require_ref); + p.print(".main"); + } else { + p.print("require.main"); + } + }, .e_require_call_target => { p.printSpaceBeforeIdentifier(); p.addSourceMapping(expr.loc); @@ -2465,7 +2581,14 @@ fn NewPrinter( }, .e_require_string => |e| { if (!rewrite_esm_to_cjs) { - p.printRequireOrImportExpr(e.import_record_index, e.unwrapped_id != std.math.maxInt(u32), &([_]G.Comment{}), level, flags); + p.printRequireOrImportExpr( + e.import_record_index, + e.unwrapped_id != std.math.maxInt(u32), + &([_]G.Comment{}), + Expr.empty, + level, + flags, + ); } }, .e_require_resolve_string => |e| { @@ -2494,7 +2617,6 @@ fn NewPrinter( } }, .e_import => |e| { - // Handle non-string expressions if (e.isImportRecordNull()) { const wrap = level.gte(.new) or flags.contains(.forbid_call); @@ -2505,57 +2627,59 @@ fn NewPrinter( p.printSpaceBeforeIdentifier(); p.addSourceMapping(expr.loc); p.print("import("); - if (e.leading_interior_comments.len > 0) { - p.printNewline(); - p.options.indent += 1; - for (e.leading_interior_comments) |comment| { - p.printIndentedComment(comment.text); - } - p.printIndent(); - } + // TODO: + // if (e.leading_interior_comments.len > 0) { + // p.printNewline(); + // p.indent(); + // for (e.leading_interior_comments) |comment| { + // p.printIndentedComment(comment.text); + // } + // p.printIndent(); + // } p.printExpr(e.expr, .comma, ExprFlag.None()); - if (comptime is_bun_platform) { + if (!e.options.isMissing()) { // since we previously stripped type, it is a breaking change to // enable this for non-bun platforms - switch (e.type_attribute) { - .none => {}, - .text => { - p.printWhitespacer(ws(", { with: { type: \"text\" } }")); - }, - .json => { - p.printWhitespacer(ws(", { with: { type: \"json\" } }")); - }, - .toml => { - p.printWhitespacer(ws(", { with: { type: \"toml\" } }")); - }, - .file => { - p.printWhitespacer(ws(", { with: { type: \"file\" } }")); - }, + if (is_bun_platform or bun.FeatureFlags.breaking_changes_1_2) { + p.printWhitespacer(ws(", ")); + p.printExpr(e.options, .comma, .{}); } } - if (e.leading_interior_comments.len > 0) { - p.printNewline(); - p.options.unindent(); - p.printIndent(); - } + // TODO: + // if (e.leading_interior_comments.len > 0) { + // p.printNewline(); + // p.unindent(); + // p.printIndent(); + // } p.print(")"); if (wrap) { p.print(")"); } } else { - p.printRequireOrImportExpr(e.import_record_index, false, e.leading_interior_comments, level, flags); + p.printRequireOrImportExpr( + e.import_record_index, + false, + &.{}, // e.leading_interior_comments, + e.options, + level, + flags, + ); } }, .e_dot => |e| { - // Ironic Zig compiler bug: e.optional_chain == null or e.optional_chain == .start causes broken LLVM IR - // https://github.com/ziglang/zig/issues/6059 - const isOptionalChain = (e.optional_chain orelse js_ast.OptionalChain.ccontinue) == js_ast.OptionalChain.start; + const isOptionalChain = e.optional_chain == .start; var wrap = false; if (e.optional_chain == null) { flags.insert(.has_non_optional_chain_parent); + + // Inline cross-module TypeScript enum references here + if (p.tryToGetImportedEnumValue(e.target, e.name)) |inlined| { + p.printInlinedEnum(inlined, e.name, level); + return; + } } else { if (flags.contains(.has_non_optional_chain_parent)) { wrap = true; @@ -2609,6 +2733,15 @@ fn NewPrinter( var wrap = false; if (e.optional_chain == null) { flags.insert(.has_non_optional_chain_parent); + + if (e.index.data.as(.e_string)) |str| { + str.resolveRopeIfNeeded(p.options.allocator); + + if (str.isUTF8()) if (p.tryToGetImportedEnumValue(e.target, str.data)) |value| { + p.printInlinedEnum(value, str.data, level); + return; + }; + } } else { if (flags.contains(.has_non_optional_chain_parent)) { wrap = true; @@ -2619,10 +2752,7 @@ fn NewPrinter( p.printExpr(e.target, .postfix, flags); - // Zig compiler bug: e.optional_chain == null or e.optional_chain == .start causes broken LLVM IR - // https://github.com/ziglang/zig/issues/6059 - const is_optional_chain_start = (e.optional_chain orelse js_ast.OptionalChain.ccontinue) == js_ast.OptionalChain.start; - + const is_optional_chain_start = e.optional_chain == .start; if (is_optional_chain_start) { p.print("?."); } @@ -2738,7 +2868,7 @@ fn NewPrinter( if (e.func.name) |sym| { p.printSpaceBeforeIdentifier(); p.addSourceMapping(sym.loc); - p.printSymbol(sym.ref orelse Global.panic("internal error: expected E.Function's name symbol to have a ref\n{any}", .{e.func})); + p.printSymbol(sym.ref orelse Output.panic("internal error: expected E.Function's name symbol to have a ref\n{any}", .{e.func})); } p.printFunc(e.func); @@ -2759,7 +2889,7 @@ fn NewPrinter( if (e.class_name) |name| { p.print(" "); p.addSourceMapping(name.loc); - p.printSymbol(name.ref orelse Global.panic("internal error: expected E.Class's name symbol to have a ref\n{any}", .{e})); + p.printSymbol(name.ref orelse Output.panic("internal error: expected E.Class's name symbol to have a ref\n{any}", .{e})); } p.printClass(e.*); if (wrap) { @@ -2772,7 +2902,7 @@ fn NewPrinter( const items = e.items.slice(); if (items.len > 0) { if (!e.is_single_line) { - p.options.indent += 1; + p.indent(); } for (items, 0..) |item, i| { @@ -2795,7 +2925,7 @@ fn NewPrinter( } if (!e.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); } @@ -2821,7 +2951,9 @@ fn NewPrinter( p.print("{"); const props = expr.data.e_object.properties.slice(); if (props.len > 0) { - p.options.indent += @as(usize, @intFromBool(!e.is_single_line)); + if (!e.is_single_line) { + p.indent(); + } if (e.is_single_line) { p.printSpace(); @@ -2846,7 +2978,7 @@ fn NewPrinter( } if (!e.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); } else { @@ -2949,75 +3081,8 @@ fn NewPrinter( p.print('n'); }, .e_number => |e| { - const value = e.value; p.addSourceMapping(expr.loc); - - const absValue = @abs(value); - - if (std.math.isNan(value)) { - p.printSpaceBeforeIdentifier(); - - p.print("NaN"); - } else if (std.math.isPositiveInf(value) or std.math.isNegativeInf(value)) { - const wrap = ((!p.options.has_run_symbol_renamer or p.options.minify_syntax) and level.gte(.multiply)) or - (std.math.isNegativeInf(value) and level.gte(.prefix)); - - if (wrap) { - p.print("("); - } - - if (std.math.isNegativeInf(value)) { - p.printSpaceBeforeOperator(.un_neg); - p.print("-"); - } else { - p.printSpaceBeforeIdentifier(); - } - - // If we are not running the symbol renamer, we must not print "Infinity". - // Some code may assign `Infinity` to another idenitifier. - // - // We do not want: - // - // const Infinity = 1 / 0 - // - // to be transformed into: - // - // const Infinity = Infinity - // - if (is_json or (!p.options.minify_syntax and p.options.has_run_symbol_renamer)) { - p.print("Infinity"); - } else if (p.options.minify_whitespace) { - p.print("1/0"); - } else { - p.print("1 / 0"); - } - - if (wrap) { - p.print(")"); - } - } else if (!std.math.signbit(value)) { - p.printSpaceBeforeIdentifier(); - - p.printNonNegativeFloat(absValue); - - // Remember the end of the latest number - p.prev_num_end = p.writer.written; - } else if (level.gte(.prefix)) { - // Expressions such as "(-1).toString" need to wrap negative numbers. - // Instead of testing for "value < 0" we test for "signbit(value)" and - // "!isNaN(value)" because we need this to be true for "-0" and "-0 < 0" - // is false. - p.print("(-"); - p.printNonNegativeFloat(absValue); - p.print(")"); - } else { - p.printSpaceBeforeOperator(Op.Code.un_neg); - p.print("-"); - p.printNonNegativeFloat(absValue); - - // Remember the end of the latest number - p.prev_num_end = p.writer.written; - } + p.printNumber(e.value, level); }, .e_identifier => |e| { const name = p.renamer.nameForSymbol(e.ref); @@ -3039,7 +3104,10 @@ fn NewPrinter( // Potentially use a property access instead of an identifier var didPrint = false; - const ref = p.symbols().follow(e.ref); + const ref = if (p.options.module_type != .internal_kit_dev) + p.symbols().follow(e.ref) + else + e.ref; const symbol = p.symbols().get(ref).?; if (symbol.import_item_status == .missing) { @@ -3112,18 +3180,18 @@ fn NewPrinter( p.print(")"); } } - } else if (p.options.const_values.count() > 0) { - if (p.options.const_values.get(ref)) |const_value| { - p.printSpaceBeforeIdentifier(); - // TODO: addSourceMappingForName - // p.addSourceMappingForName(renamer.nameForSymbol(e.ref)); - p.addSourceMapping(expr.loc); - p.printExpr(const_value, level, flags); - didPrint = true; - } } + // else if (p.options.const_values.get(ref)) |const_value| { + // p.printSpaceBeforeIdentifier(); + // // TODO: addSourceMappingForName + // // p.addSourceMappingForName(renamer.nameForSymbol(e.ref)); + // p.addSourceMapping(expr.loc); + // p.printExpr(const_value, level, flags); + // didPrint = true; + // } if (!didPrint) { + // assert(p.options.module_type != .internal_kit_dev); p.printSpaceBeforeIdentifier(); p.addSourceMapping(expr.loc); p.printSymbol(e.ref); @@ -3246,8 +3314,21 @@ fn NewPrinter( last.visitRightAndFinish(p); } }, - else => { - // Global.panic("Unexpected expression of type {any}", .{std.meta.activeTag(expr.data}); + .e_inlined_enum => |e| { + p.printExpr(e.value, level, flags); + if (!p.options.minify_whitespace and !p.options.minify_identifiers) { + p.print(" /* "); + p.print(e.comment); + p.print(" */"); + } + }, + + .e_jsx_element, + .e_private_identifier, + .e_template_part, + => { + if (Environment.isDebug) + Output.panic("Unexpected expression of type .{s}", .{@tagName(expr.data)}); }, } } @@ -3465,7 +3546,8 @@ fn NewPrinter( p.prev_reg_exp_end = p.writer.written; } - pub fn printProperty(p: *Printer, item: G.Property) void { + pub fn printProperty(p: *Printer, item_in: G.Property) void { + var item = item_in; if (comptime !is_json) { if (item.kind == .spread) { if (comptime is_json and Environment.allow_assert) @@ -3475,6 +3557,28 @@ fn NewPrinter( return; } + // Handle key syntax compression for cross-module constant inlining of enums + if (p.options.minify_syntax and item.flags.contains(.is_computed)) { + if (item.key.?.data.as(.e_dot)) |dot| { + if (p.tryToGetImportedEnumValue(dot.target, dot.name)) |value| { + switch (value) { + .string => |str| { + item.key.?.data = .{ .e_string = str }; + + // Problematic key names must stay computed for correctness + if (!str.eqlComptime("__proto__") and !str.eqlComptime("constructor") and !str.eqlComptime("prototype")) { + item.flags.setPresent(.is_computed, false); + } + }, + .number => |num| { + item.key.?.data = .{ .e_number = .{ .value = num } }; + item.flags.setPresent(.is_computed, false); + }, + } + } + } + } + if (item.flags.contains(.is_static)) { if (comptime is_json and Environment.allow_assert) unreachable; @@ -3536,11 +3640,7 @@ fn NewPrinter( const _key = item.key.?; - if (item.flags.contains(.is_computed)) { - if (comptime is_json) { - unreachable; - } - + if (!is_json and item.flags.contains(.is_computed)) { p.print("["); p.printExpr(_key, .comma, ExprFlag.None()); p.print("]"); @@ -3610,10 +3710,11 @@ fn NewPrinter( } } }, - .e_import_identifier => |e| inner: { + // .e_import_identifier => |e| inner: { + .e_import_identifier => |e| { const ref = p.symbols().follow(e.ref); - if (p.options.const_values.count() > 0 and p.options.const_values.contains(ref)) - break :inner; + // if (p.options.const_values.count() > 0 and p.options.const_values.contains(ref)) + // break :inner; if (p.symbols().get(ref)) |symbol| { if (symbol.namespace_alias == null and strings.eql(key.data, p.renamer.nameForSymbol(e.ref))) { @@ -3650,11 +3751,12 @@ fn NewPrinter( } // if (strings) {} }, - .e_import_identifier => |e| inner: { + // .e_import_identifier => |e| inner: { + .e_import_identifier => |e| { const ref = p.symbols().follow(e.ref); - if (p.options.const_values.count() > 0 and p.options.const_values.contains(ref)) - break :inner; + // if (p.options.const_values.count() > 0 and p.options.const_values.contains(ref)) + // break :inner; if (p.symbols().get(ref)) |symbol| { if (symbol.namespace_alias == null and strings.utf16EqlString(key.slice16(), p.renamer.nameForSymbol(e.ref))) { @@ -3742,7 +3844,9 @@ fn NewPrinter( .b_array => |b| { p.print("["); if (b.items.len > 0) { - p.options.indent += @as(usize, @intFromBool(!b.is_single_line)); + if (!b.is_single_line) { + p.indent(); + } for (b.items, 0..) |*item, i| { if (i != 0) { @@ -3773,7 +3877,7 @@ fn NewPrinter( } if (!b.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); } @@ -3784,8 +3888,9 @@ fn NewPrinter( .b_object => |b| { p.print("{"); if (b.properties.len > 0) { - p.options.indent += - @as(usize, @intFromBool(!b.is_single_line)); + if (!b.is_single_line) { + p.indent(); + } for (b.properties, 0..) |*property, i| { if (i != 0) { @@ -3874,7 +3979,7 @@ fn NewPrinter( } if (!b.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); } else { @@ -3883,9 +3988,6 @@ fn NewPrinter( } p.print("}"); }, - else => { - Global.panic("Unexpected binding of type {any}", .{binding}); - }, } } @@ -3913,8 +4015,8 @@ fn NewPrinter( .s_function => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); - const name = s.func.name orelse Global.panic("Internal error: expected func to have a name ref\n{any}", .{s}); - const nameRef = name.ref orelse Global.panic("Internal error: expected func to have a name\n{any}", .{s}); + const name = s.func.name orelse Output.panic("Internal error: expected func to have a name ref\n{any}", .{s}); + const nameRef = name.ref orelse Output.panic("Internal error: expected func to have a name\n{any}", .{s}); if (s.func.flags.contains(.is_export)) { if (!rewrite_esm_to_cjs) { @@ -3987,7 +4089,7 @@ fn NewPrinter( } }, .s_empty => { - if (p.prev_stmt_tag == .s_empty and p.options.indent == 0) return; + if (p.prev_stmt_tag == .s_empty and p.options.indent.count == 0) return; p.printIndent(); p.print(";"); @@ -4038,7 +4140,7 @@ fn NewPrinter( if (class.class.class_name) |name| { p.print("class "); - p.printSymbol(name.ref orelse Global.panic("Internal error: Expected class to have a name ref\n{any}", .{class})); + p.printSymbol(name.ref orelse Output.panic("Internal error: Expected class to have a name ref\n{any}", .{class})); } else { p.print("class"); } @@ -4048,7 +4150,7 @@ fn NewPrinter( p.printNewline(); }, else => { - Global.panic("Internal error: unexpected export default stmt data {any}", .{s}); + Output.panic("Internal error: unexpected export default stmt data {any}", .{s}); }, } }, @@ -4205,7 +4307,7 @@ fn NewPrinter( p.print("{"); if (!s.is_single_line) { - p.options.indent += 1; + p.indent(); } else { p.printSpace(); } @@ -4227,7 +4329,7 @@ fn NewPrinter( } if (!s.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); } else { @@ -4302,7 +4404,7 @@ fn NewPrinter( p.printWhitespacer(ws("export {")); if (!s.is_single_line) { - p.options.indent += 1; + p.indent(); } else { p.printSpace(); } @@ -4323,7 +4425,7 @@ fn NewPrinter( } if (!s.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); } else { @@ -4369,10 +4471,10 @@ fn NewPrinter( }, else => { p.printNewline(); - p.options.indent += 1; + p.indent(); p.printStmt(s.body) catch unreachable; p.printSemicolonIfNeeded(); - p.options.unindent(); + p.unindent(); p.printIndent(); }, } @@ -4439,12 +4541,12 @@ fn NewPrinter( p.printBody(s.body); }, .s_label => |s| { - if (!p.options.minify_whitespace and p.options.indent > 0) { + if (!p.options.minify_whitespace and p.options.indent.count > 0) { p.addSourceMapping(stmt.loc); p.printIndent(); } p.printSpaceBeforeIdentifier(); - p.printSymbol(s.name.ref orelse Global.panic("Internal error: expected label to have a name {any}", .{s})); + p.printSymbol(s.name.ref orelse Output.panic("Internal error: expected label to have a name {any}", .{s})); p.print(":"); p.printBody(s.stmt); }, @@ -4517,7 +4619,7 @@ fn NewPrinter( p.printSpace(); p.print("{"); p.printNewline(); - p.options.indent += 1; + p.indent(); for (s.cases) |c| { p.printSemicolonIfNeeded(); @@ -4546,15 +4648,15 @@ fn NewPrinter( } p.printNewline(); - p.options.indent += 1; + p.indent(); for (c.body) |st| { p.printSemicolonIfNeeded(); p.printStmt(st) catch unreachable; } - p.options.unindent(); + p.unindent(); } - p.options.unindent(); + p.unindent(); p.printIndent(); p.print("}"); p.printNewline(); @@ -4640,8 +4742,6 @@ fn NewPrinter( else => {}, } - var item_count: usize = 0; - p.printIndent(); p.printSpaceBeforeIdentifier(); @@ -4760,6 +4860,8 @@ fn NewPrinter( p.print("import"); + var item_count: usize = 0; + if (s.default_name) |name| { p.print(" "); p.printSymbol(name.ref.?); @@ -4774,7 +4876,9 @@ fn NewPrinter( p.print("{"); if (!s.is_single_line) { - p.options.unindent(); + p.indent(); + } else { + p.printSpace(); } for (s.items, 0..) |item, i| { @@ -4794,9 +4898,11 @@ fn NewPrinter( } if (!s.is_single_line) { - p.options.unindent(); + p.unindent(); p.printNewline(); p.printIndent(); + } else { + p.printSpace(); } p.print("}"); item_count += 1; @@ -4815,7 +4921,11 @@ fn NewPrinter( } if (item_count > 0) { - p.print(" "); + if (!p.options.minify_whitespace or + record.contains_import_star or + s.items.len == 0) + p.print(" "); + p.printWhitespacer(ws("from ")); } @@ -4915,7 +5025,7 @@ fn NewPrinter( p.printSemicolonAfterStatement(); }, .s_expr => |s| { - if (!p.options.minify_whitespace and p.options.indent > 0) { + if (!p.options.minify_whitespace and p.options.indent.count > 0) { p.addSourceMapping(stmt.loc); p.printIndent(); } @@ -4929,9 +5039,9 @@ fn NewPrinter( const to_print: []const u8 = if (slice.len > 1024) slice[slice.len - 1024 ..] else slice; if (to_print.len > 0) { - Global.panic("\nvoluntary crash while printing:\n{s}\n---This is a bug. Not your fault.\n", .{to_print}); + Output.panic("\nvoluntary crash while printing:\n{s}\n---This is a bug. Not your fault.\n", .{to_print}); } else { - Global.panic("\nvoluntary crash while printing. This is a bug. Not your fault.\n", .{}); + Output.panic("\nvoluntary crash while printing. This is a bug. Not your fault.\n", .{}); } }, } @@ -5072,7 +5182,7 @@ fn NewPrinter( } inline fn printDisabledImport(p: *Printer) void { - p.print("(()=>({}))"); + p.printWhitespacer(ws("(() => ({}))")); } pub fn printLoadFromBundleWithoutCall(p: *Printer, import_record_index: u32) void { @@ -5158,7 +5268,7 @@ fn NewPrinter( // for(;) .s_empty => {}, else => { - Global.panic("Internal error: Unexpected stmt in for loop {any}", .{initSt}); + Output.panic("Internal error: Unexpected stmt in for loop {any}", .{initSt}); }, } } @@ -5187,9 +5297,9 @@ fn NewPrinter( p.print("{"); p.printNewline(); - p.options.indent += 1; + p.indent(); p.printStmt(s.yes) catch unreachable; - p.options.unindent(); + p.unindent(); p.needs_semicolon = false; p.printIndent(); @@ -5202,9 +5312,9 @@ fn NewPrinter( } } else { p.printNewline(); - p.options.indent += 1; + p.indent(); p.printStmt(s.yes) catch unreachable; - p.options.unindent(); + p.unindent(); if (s.no != null) { p.printIndent(); @@ -5229,9 +5339,9 @@ fn NewPrinter( }, else => { p.printNewline(); - p.options.indent += 1; + p.indent(); p.printStmt(no_block) catch unreachable; - p.options.unindent(); + p.unindent(); }, } } @@ -5270,6 +5380,47 @@ fn NewPrinter( } } + pub fn tryToGetImportedEnumValue(p: *Printer, target: Expr, name: []const u8) ?js_ast.InlinedEnumValue.Decoded { + if (target.data.as(.e_import_identifier)) |id| { + const ref = p.symbols().follow(id.ref); + if (p.symbols().get(ref)) |symbol| { + if (symbol.kind == .ts_enum) { + if (p.options.ts_enums.get(ref)) |enum_value| { + if (enum_value.get(name)) |value| + return value.decode(); + } + } + } + } + return null; + } + + pub fn printInlinedEnum( + p: *Printer, + inlined: js_ast.InlinedEnumValue.Decoded, + comment: []const u8, + level: Level, + ) void { + switch (inlined) { + .number => |num| p.printNumber(num, level), + + // TODO: extract printString + .string => |str| p.printExpr(.{ + .data = .{ .e_string = str }, + .loc = logger.Loc.Empty, + }, level, .{}), + } + + if (!p.options.minify_whitespace and !p.options.minify_identifiers) { + // TODO: rewrite this to handle + if (!bun.strings.containsComptime(comment, "*/")) { + p.print(" /* "); + p.print(comment); + p.print(" */"); + } + } + } + pub fn printDeclStmt(p: *Printer, is_export: bool, comptime keyword: string, decls: []G.Decl) void { p.printIndent(); p.printSpaceBeforeIdentifier(); @@ -5414,6 +5565,73 @@ fn NewPrinter( } } + pub fn printNumber(p: *Printer, value: f64, level: Level) void { + const absValue = @abs(value); + if (std.math.isNan(value)) { + p.printSpaceBeforeIdentifier(); + p.print("NaN"); + } else if (std.math.isPositiveInf(value) or std.math.isNegativeInf(value)) { + const wrap = ((!p.options.has_run_symbol_renamer or p.options.minify_syntax) and level.gte(.multiply)) or + (std.math.isNegativeInf(value) and level.gte(.prefix)); + + if (wrap) { + p.print("("); + } + + if (std.math.isNegativeInf(value)) { + p.printSpaceBeforeOperator(.un_neg); + p.print("-"); + } else { + p.printSpaceBeforeIdentifier(); + } + + // If we are not running the symbol renamer, we must not print "Infinity". + // Some code may assign `Infinity` to another idenitifier. + // + // We do not want: + // + // const Infinity = 1 / 0 + // + // to be transformed into: + // + // const Infinity = Infinity + // + if (is_json or (!p.options.minify_syntax and p.options.has_run_symbol_renamer)) { + p.print("Infinity"); + } else if (p.options.minify_whitespace) { + p.print("1/0"); + } else { + p.print("1 / 0"); + } + + if (wrap) { + p.print(")"); + } + } else if (!std.math.signbit(value)) { + p.printSpaceBeforeIdentifier(); + + p.printNonNegativeFloat(absValue); + + // Remember the end of the latest number + p.prev_num_end = p.writer.written; + } else if (level.gte(.prefix)) { + // Expressions such as "(-1).toString" need to wrap negative numbers. + // Instead of testing for "value < 0" we test for "signbit(value)" and + // "!isNaN(value)" because we need this to be true for "-0" and "-0 < 0" + // is false. + p.print("(-"); + p.printNonNegativeFloat(absValue); + p.print(")"); + } else { + p.printSpaceBeforeOperator(Op.Code.un_neg); + p.print("-"); + p.printNonNegativeFloat(absValue); + + // Remember the end of the latest number + p.prev_num_end = p.writer.written; + } + } + pub fn printIndentedComment(p: *Printer, _text: string) void { var text = _text; if (strings.startsWith(text, "/*")) { @@ -5557,12 +5775,6 @@ pub fn NewWriter( } pub inline fn print(writer: *Self, comptime ValueType: type, str: ValueType) void { - if (FeatureFlags.disable_printing_null) { - if (str == 0) { - Global.panic("Attempted to print null char", .{}); - } - } - switch (ValueType) { comptime_int, u16, u8 => { const written = writeByte(&writer.ctx, @as(u8, @intCast(str))) catch |err| brk: { @@ -5812,6 +6024,7 @@ pub const BufferWriter = struct { pub fn reset(ctx: *BufferWriter) void { ctx.buffer.reset(); ctx.approximate_newline_count = 0; + ctx.written = &.{}; } pub fn writtenWithoutTrailingZero(ctx: *const BufferWriter) []u8 { @@ -6019,6 +6232,11 @@ pub fn printAst( renamer, getSourceMapBuilder(if (generate_source_map) .lazy else .disable, ascii_only, opts, source, &tree), ); + defer { + if (comptime generate_source_map) { + printer.source_map_builder.line_offset_tables.deinit(opts.allocator); + } + } var bin_stack_heap = std.heap.stackFallback(1024, bun.default_allocator); printer.binary_expression_stack = std.ArrayList(PrinterType.BinaryExpressionVisitor).init(bin_stack_heap.get()); defer printer.binary_expression_stack.clearAndFree(); @@ -6074,6 +6292,7 @@ pub fn printJSON( _writer: Writer, expr: Expr, source: *const logger.Source, + opts: Options, ) !usize { const PrinterType = NewPrinter(false, Writer, false, false, true, false); const writer = _writer; @@ -6091,7 +6310,7 @@ pub fn printJSON( var printer = PrinterType.init( writer, ast.import_records.slice(), - .{}, + opts, renamer.toRenamer(), undefined, ); @@ -6180,6 +6399,10 @@ pub fn printWithWriterAndPlatform( renamer: bun.renamer.Renamer, comptime generate_source_maps: bool, ) PrintResult { + const prev_action = bun.crash_handler.current_action; + defer bun.crash_handler.current_action = prev_action; + bun.crash_handler.current_action = .{ .print = source.path.text }; + const PrinterType = NewPrinter( // if it's bun, it is also ascii_only is_bun_platform, @@ -6207,15 +6430,30 @@ pub fn printWithWriterAndPlatform( imported_module_ids_list = printer.imported_module_ids; } - for (parts) |part| { - for (part.stmts) |stmt| { - printer.printStmt(stmt) catch |err| { - return .{ .err = err }; - }; - if (printer.writer.getError()) {} else |err| { - return .{ .err = err }; + if (opts.module_type == .internal_kit_dev) { + printer.indent(); + printer.printIndent(); + printer.fmt("{d}", .{source.path.hashForKit()}) catch bun.outOfMemory(); + printer.print(": function"); + printer.printFunc(parts[0].stmts[0].data.s_expr.value.data.e_function.func); + printer.print(",\n"); + } else { + // The IIFE wrapper is done in `postProcessJSChunk`, so we just manually + // trigger an indent. + if (opts.module_type == .iife) { + printer.indent(); + } + + for (parts) |part| { + for (part.stmts) |stmt| { + printer.printStmt(stmt) catch |err| { + return .{ .err = err }; + }; + if (printer.writer.getError()) {} else |err| { + return .{ .err = err }; + } + printer.printSemicolonIfNeeded(); } - printer.printSemicolonIfNeeded(); } } @@ -6248,6 +6486,10 @@ pub fn printCommonJS( opts: Options, comptime generate_source_map: bool, ) !usize { + const prev_action = bun.crash_handler.current_action; + defer bun.crash_handler.current_action = prev_action; + bun.crash_handler.current_action = .{ .print = source.path.text }; + const PrinterType = NewPrinter(ascii_only, Writer, true, false, false, generate_source_map); const writer = _writer; var renamer = rename.NoOpRenamer.init(symbols, source); diff --git a/src/jsc.zig b/src/jsc.zig index 872a47e5f6888..c49313cb80559 100644 --- a/src/jsc.zig +++ b/src/jsc.zig @@ -49,9 +49,12 @@ pub const API = struct { pub const UDPSocket = @import("./bun.js/api/bun/udp_socket.zig").UDPSocket; pub const Listener = @import("./bun.js/api/bun/socket.zig").Listener; pub const H2FrameParser = @import("./bun.js/api/bun/h2_frame_parser.zig").H2FrameParser; - pub const BrotliEncoder = @import("./bun.js/api/brotli.zig").BrotliEncoder; - pub const BrotliDecoder = @import("./bun.js/api/brotli.zig").BrotliDecoder; + pub const BrotliEncoder = @import("./bun.js/api/js_brotli.zig").BrotliEncoder; + pub const BrotliDecoder = @import("./bun.js/api/js_brotli.zig").BrotliDecoder; + pub const ZlibEncoder = @import("./bun.js/api/js_zlib.zig").ZlibEncoder; + pub const ZlibDecoder = @import("./bun.js/api/js_zlib.zig").ZlibDecoder; }; +pub const Postgres = @import("./sql/postgres.zig"); pub const DNS = @import("./bun.js/api/bun/dns_resolver.zig"); pub const FFI = @import("./bun.js/api/ffi.zig").FFI; pub const Node = struct { @@ -67,12 +70,6 @@ pub const Node = struct { }; }; -comptime { - if (!is_bindgen) { - @export(Node.Util.parseArgs, .{ .name = "Bun__NodeUtil__jsParseArgs" }); - } -} - const std = @import("std"); const Syscall = @import("./sys.zig"); const Output = @import("./output.zig"); @@ -90,10 +87,6 @@ pub inline fn markBinding(src: std.builtin.SourceLocation) void { pub const Subprocess = API.Bun.Subprocess; pub const ResourceUsage = API.Bun.ResourceUsage; -/// Generated code! To regenerate, run: -/// -/// make codegen -/// /// This file is generated by: /// 1. `bun src/bun.js/scripts/generate-classes.ts` /// 2. Scan for **/*.classes.ts files in src/bun.js/src @@ -114,3 +107,17 @@ pub const Codegen = struct { pub const GeneratedClassesList = @import("./bun.js/bindings/generated_classes_list.zig").Classes; pub const RuntimeTranspilerCache = @import("./bun.js/RuntimeTranspilerCache.zig").RuntimeTranspilerCache; + +/// The calling convention used for JavaScript functions <> Native +const bun = @import("root").bun; +pub const conv = if (bun.Environment.isWindows and bun.Environment.isX64) + std.builtin.CallingConvention.SysV +else + std.builtin.CallingConvention.C; + +pub const Error = @import("ErrorCode").Error; + +pub const MAX_SAFE_INTEGER = std.math.maxInt(i52); +pub const MIN_SAFE_INTEGER = std.math.minInt(i52); +pub const MAX_NUMBER = std.math.maxInt(f64); +pub const MIN_NUMBER = std.math.minInt(f64); diff --git a/src/json_parser.zig b/src/json_parser.zig index bbb7111d3bfc0..bf46ea15edfe0 100644 --- a/src/json_parser.zig +++ b/src/json_parser.zig @@ -41,6 +41,7 @@ const Level = js_ast.Op.Level; const Op = js_ast.Op; const Scope = js_ast.Scope; const locModuleScope = logger.Loc.Empty; +const Indentation = js_printer.Options.Indentation; const LEXER_DEBUGGER_WORKAROUND = false; @@ -115,6 +116,7 @@ fn JSONLikeParser(comptime opts: js_lexer.JSONOptions) type { opts.json_warn_duplicate_keys, opts.was_originally_macro, opts.always_decode_escape_sequences, + opts.guess_indentation, ); } @@ -127,6 +129,7 @@ fn JSONLikeParser_( comptime opts_json_warn_duplicate_keys: bool, comptime opts_was_originally_macro: bool, comptime opts_always_decode_escape_sequences: bool, + comptime opts_guess_indentation: bool, ) type { const opts = js_lexer.JSONOptions{ .is_json = opts_is_json, @@ -137,6 +140,7 @@ fn JSONLikeParser_( .json_warn_duplicate_keys = opts_json_warn_duplicate_keys, .was_originally_macro = opts_was_originally_macro, .always_decode_escape_sequences = opts_always_decode_escape_sequences, + .guess_indentation = opts_guess_indentation, }; return struct { const Lexer = js_lexer.NewLexer(if (LEXER_DEBUGGER_WORKAROUND) js_lexer.JSONOptions{} else opts); @@ -810,6 +814,50 @@ pub fn ParsePackageJSONUTF8AlwaysDecode( return try parser.parseExpr(false, true); } +const JsonResult = struct { + root: Expr, + indentation: Indentation = .{}, +}; + +pub fn ParsePackageJSONUTF8WithOpts( + source: *const logger.Source, + log: *logger.Log, + allocator: std.mem.Allocator, + comptime opts: js_lexer.JSONOptions, +) !JsonResult { + const len = source.contents.len; + + switch (len) { + // This is to be consisntent with how disabled JS files are handled + 0 => { + return .{ + .root = Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_object_data }, + }; + }, + // This is a fast pass I guess + 2 => { + if (strings.eqlComptime(source.contents[0..1], "\"\"") or strings.eqlComptime(source.contents[0..1], "''")) { + return .{ .root = Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_string_data } }; + } else if (strings.eqlComptime(source.contents[0..1], "{}")) { + return .{ .root = Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_object_data } }; + } else if (strings.eqlComptime(source.contents[0..1], "[]")) { + return .{ .root = Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_array_data } }; + } + }, + else => {}, + } + + var parser = try JSONLikeParser(opts).init(allocator, source.*, log); + bun.assert(parser.source().contents.len > 0); + + const root = try parser.parseExpr(false, true); + + return .{ + .root = root, + .indentation = if (comptime opts.guess_indentation) parser.lexer.indent_info.guess else .{}, + }; +} + /// Parse Package JSON /// Allow trailing commas & comments. /// This eagerly transcodes UTF-16 strings into UTF-8 strings @@ -819,6 +867,15 @@ pub fn ParseJSONUTF8( source: *const logger.Source, log: *logger.Log, allocator: std.mem.Allocator, +) !Expr { + return try ParseJSONUTF8Impl(source, log, allocator, false); +} + +pub fn ParseJSONUTF8Impl( + source: *const logger.Source, + log: *logger.Log, + allocator: std.mem.Allocator, + comptime check_len: bool, ) !Expr { const len = source.contents.len; @@ -843,9 +900,14 @@ pub fn ParseJSONUTF8( var parser = try JSONParser.init(allocator, source.*, log); bun.assert(parser.source().contents.len > 0); - return try parser.parseExpr(false, true); + const result = try parser.parseExpr(false, true); + if (comptime check_len) { + if (parser.lexer.end >= source.contents.len) return result; + try parser.lexer.unexpected(); + return error.ParserError; + } + return result; } - pub fn ParseJSONForMacro(source: *const logger.Source, log: *logger.Log, allocator: std.mem.Allocator) !Expr { switch (source.contents.len) { // This is to be consisntent with how disabled JS files are handled @@ -1013,12 +1075,12 @@ fn expectPrintedJSON(_contents: string, expected: string) !void { const expr = try ParseJSON(&source, &log, default_allocator); if (log.msgs.items.len > 0) { - Global.panic("--FAIL--\nExpr {s}\nLog: {s}\n--FAIL--", .{ expr, log.msgs.items[0].data.text }); + Output.panic("--FAIL--\nExpr {s}\nLog: {s}\n--FAIL--", .{ expr, log.msgs.items[0].data.text }); } const buffer_writer = try js_printer.BufferWriter.init(default_allocator); var writer = js_printer.BufferPrinter.init(buffer_writer); - const written = try js_printer.printJSON(@TypeOf(&writer), &writer, expr, &source); + const written = try js_printer.printJSON(@TypeOf(&writer), &writer, expr, &source, .{}); var js = writer.ctx.buffer.list.items.ptr[0 .. written + 1]; if (js.len > 1) { diff --git a/src/kit/DevServer.zig b/src/kit/DevServer.zig new file mode 100644 index 0000000000000..804a8803b492c --- /dev/null +++ b/src/kit/DevServer.zig @@ -0,0 +1,925 @@ +//! Instance of the development server. Controls an event loop, web server, +//! bundling threads, and JavaScript VM instance. All data is held in memory. +//! +//! Currently does not have a `deinit()`, as it is assumed to be alive for the +//! remainder of this process' lifespan. +pub const DevServer = @This(); + +pub const Options = struct { + cwd: []u8, + routes: []Route, + listen_config: uws.AppListenConfig = .{ .port = 3000 }, + dump_sources: ?[]const u8 = if (Environment.isDebug) ".kit-debug" else null, + verbose_watcher: bool = false, + // TODO: make it possible to inherit a js VM +}; + +/// Accepting a custom allocator for all of DevServer would be misleading +/// as there are many functions which will use default_allocator. +const default_allocator = bun.default_allocator; + +cwd: []const u8, +dump_dir: ?std.fs.Dir, + +// UWS App +app: *App, +routes: []Route, +address: struct { + port: u16, + hostname: [*:0]const u8, +}, +listener: ?*App.ListenSocket, + +// Server Runtime +server_global: *DevGlobalObject, +vm: *VirtualMachine, + +// Bundling +bundle_thread: BundleThread, + +// // Watch + HMR +// bun_watcher: *HotReloader.Watcher, +/// Required by `bun.JSC.NewHotReloader` +bundler: Bundler, +/// Required by `Bundler` +log_do_not_use: Log, + +pub const internal_prefix = "/_bun"; +pub const client_prefix = internal_prefix ++ "/client"; + +pub const Route = struct { + // Config + pattern: [:0]const u8, + entry_point: []const u8, + + server_bundle: BundlePromise(ServerBundle) = .unqueued, + client_bundle: BundlePromise(ClientBundle) = .unqueued, + + /// Assigned in DevServer.init + dev: *DevServer = undefined, + client_bundled_url: []u8 = undefined, + + pub fn clientPublicPath(route: *const Route) []const u8 { + return route.client_bundled_url[0 .. route.client_bundled_url.len - "/client.js".len]; + } +}; + +/// Prepared server-side bundle and loaded JavaScript module +const ServerBundle = struct { + files: []OutputFile, + server_request_callback: JSC.JSValue, +}; + +/// Preparred client-side bundle. +/// Namespaced to URL: `/_bun/client/:route_index/:file_path` +const ClientBundle = struct { + files: []OutputFile, + /// Indexes into this are indexes into `files`. + /// This is case insensitive because URL paths should be case insensitive. + files_index: bun.CaseInsensitiveASCIIStringArrayHashMapUnmanaged(void), + + pub fn getFile(bundle: *ClientBundle, filename: []const u8) ?*OutputFile { + return if (bundle.files_index.getIndex(filename)) |i| + &bundle.files[i] + else + null; + } +}; + +pub fn init(options: Options) *DevServer { + if (JSC.VirtualMachine.VMHolder.vm != null) + @panic("Cannot initialize kit.DevServer on a thread with an active JSC.VirtualMachine"); + + const dump_dir = if (options.dump_sources) |dir| + std.fs.cwd().makeOpenPath(dir, .{}) catch |err| dir: { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + Output.warn("Could not open directory for dumping sources: {}", .{err}); + break :dir null; + } + else + null; + + const app = App.create(.{}); + + const dev = bun.new(DevServer, .{ + .cwd = options.cwd, + .app = app, + .routes = options.routes, + .address = .{ + .port = @intCast(options.listen_config.port), + .hostname = options.listen_config.host orelse "localhost", + }, + .listener = null, + .bundle_thread = BundleThread.uninitialized, + .server_global = undefined, + .vm = undefined, + .dump_dir = dump_dir, + // .bun_watcher = undefined, + .bundler = undefined, + .log_do_not_use = Log.init(bun.failing_allocator), + }); + + dev.bundler = bun.Bundler.init( + default_allocator, + &dev.log_do_not_use, + std.mem.zeroes(bun.Schema.Api.TransformOptions), + null, // TODO: + ) catch bun.outOfMemory(); + + const loaders = bun.options.loadersFromTransformOptions(default_allocator, null, .bun) catch + bun.outOfMemory(); + + dev.bundler.options = .{ + .entry_points = &.{}, + .define = dev.bundler.options.define, + .loaders = loaders, + .log = &dev.log_do_not_use, + .output_dir = "", // this disables filesystem output + .output_format = .internal_kit_dev, + .out_extensions = bun.StringHashMap([]const u8).init(bun.failing_allocator), + + // unused by all code + .resolve_mode = .dev, + // technically used (in macro) but should be removed + .transform_options = std.mem.zeroes(bun.Schema.Api.TransformOptions), + }; + dev.bundler.configureLinker(); + dev.bundler.resolver.opts = dev.bundler.options; + + // const fs = bun.fs.FileSystem.init(options.cwd) catch @panic("Failed to init FileSystem"); + // dev.bun_watcher = HotReloader.init(dev, fs, options.verbose_watcher, false); + // dev.bundler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); + + dev.vm = VirtualMachine.initKit(.{ + .allocator = default_allocator, + .args = std.mem.zeroes(bun.Schema.Api.TransformOptions), + }) catch |err| + Output.panic("Failed to create Global object: {}", .{err}); + dev.server_global = c.KitCreateDevGlobal(dev, dev.vm.console); + dev.vm.global = dev.server_global.js(); + dev.vm.regular_event_loop.global = dev.vm.global; + dev.vm.jsc = dev.vm.global.vm(); + dev.vm.event_loop.ensureWaker(); + + _ = JSC.WorkPool.get(); + const thread = dev.bundle_thread.spawn() catch |err| + Output.panic("Failed to spawn bundler thread: {}", .{err}); + thread.detach(); + + var has_fallback = false; + + for (options.routes, 0..) |*route, i| { + app.any(route.pattern, *Route, route, onServerRequestInit); + + route.dev = dev; + route.client_bundled_url = std.fmt.allocPrint( + default_allocator, + client_prefix ++ "/{d}/client.js", + .{i}, + ) catch bun.outOfMemory(); + + if (bun.strings.eqlComptime(route.pattern, "/*")) + has_fallback = true; + } + + app.get(client_prefix ++ "/:route/:asset", *DevServer, dev, onAssetRequestInit); + + app.ws( + internal_prefix ++ "/hmr", + dev, + 0, + uws.WebSocketBehavior.Wrap(DevServer, DevWebSocket, false).apply(.{}), + ); + + if (!has_fallback) + app.any("/*", void, {}, onFallbackRoute); + + app.listenWithConfig(*DevServer, dev, onListen, options.listen_config); + + return dev; +} + +pub fn runLoopForever(dev: *DevServer) noreturn { + const lock = dev.vm.jsc.getAPILock(); + defer lock.release(); + + while (true) { + dev.vm.tick(); + dev.vm.eventLoop().autoTickActive(); + } +} + +// uws handlers + +fn onListen(ctx: *DevServer, maybe_listen: ?*App.ListenSocket) void { + const listen: *App.ListenSocket = maybe_listen orelse { + @panic("TODO: handle listen failure"); + }; + + ctx.listener = listen; + ctx.address.port = @intCast(listen.getLocalPort()); + + Output.prettyErrorln("--\\> http://{s}:{d}\n", .{ + bun.span(ctx.address.hostname), + ctx.address.port, + }); + Output.flush(); +} + +fn onAssetRequestInit(dev: *DevServer, req: *Request, resp: *Response) void { + const route = route: { + const route_id = req.parameter(0); + const i = std.fmt.parseInt(u16, route_id, 10) catch + return req.setYield(true); + if (i >= dev.routes.len) + return req.setYield(true); + break :route &dev.routes[i]; + }; + const asset_name = req.parameter(1); + dev.getOrEnqueueBundle(resp, route, .client, .{ .file_name = asset_name }); +} + +fn onServerRequestInit(route: *Route, req: *Request, resp: *Response) void { + _ = req; + route.dev.getOrEnqueueBundle(resp, route, .server, .{}); +} + +// uws with bundle handlers + +fn onAssetRequestWithBundle(route: *Route, resp: *Response, ctx: BundleKind.client.Context(), bundle: *ClientBundle) void { + _ = route; + + const file = bundle.getFile(ctx.file_name) orelse + return sendBuiltInNotFound(resp); + + sendOutputFile(file, resp); +} + +fn onServerRequestWithBundle(route: *Route, resp: *Response, ctx: BundleKind.server.Context(), bundle: *ServerBundle) void { + _ = ctx; // autofix + const dev = route.dev; + const global = dev.server_global.js(); + + const context = JSValue.createEmptyObject(global, 1); + context.put( + dev.server_global.js(), + bun.String.static("clientEntryPoint"), + bun.String.init(route.client_bundled_url).toJS(global), + ); + + const result = bundle.server_request_callback.call( + global, + .undefined, + &.{context}, + ) catch |err| { + const exception = global.takeException(err); + const fail: Failure = .{ .request_handler = exception }; + fail.printToConsole(route, .server); + fail.sendAsHttpResponse(resp, route, .server); + return; + }; + + // TODO: This interface and implementation is very poor. but fine until API + // considerations become important (as of writing, there are 3 dozen todo + // items before it) + // + // It probably should use code from `server.zig`, but most importantly it should + // not have a tie to DevServer, but instead be generic with a context structure + // containing just a *uws.App, *JSC.EventLoop, and JSValue response object. + // + // This would allow us to support all of the nice things `new Response` allows + + const bun_string = result.toBunString(dev.server_global.js()); + if (bun_string.tag == .Dead) @panic("TODO NOT STRING"); + defer bun_string.deref(); + + const utf8 = bun_string.toUTF8(default_allocator); + defer utf8.deinit(); + + resp.writeStatus("200 OK"); + resp.writeHeader("Content-Type", MimeType.html.value); + resp.end(utf8.slice(), true); // TODO: You should never call res.end(huge buffer) +} + +fn onFallbackRoute(_: void, _: *Request, resp: *Response) void { + sendBuiltInNotFound(resp); +} + +// http helper functions + +fn sendOutputFile(file: *const OutputFile, resp: *Response) void { + switch (file.value) { + .buffer => |buffer| { + if (buffer.bytes.len == 0) { + resp.writeStatus("202 No Content"); + resp.writeHeaderInt("Content-Length", 0); + resp.end("", true); + return; + } + + resp.writeStatus("200 OK"); + // TODO: CSS, Sourcemap + resp.writeHeader("Content-Type", MimeType.javascript.value); + resp.end(buffer.bytes, true); // TODO: You should never call res.end(huge buffer) + }, + else => |unhandled_tag| Output.panic("TODO: unhandled tag .{s}", .{@tagName(unhandled_tag)}), + } +} + +fn sendBuiltInNotFound(resp: *Response) void { + const message = "404 Not Found"; + resp.writeStatus("404 Not Found"); + resp.end(message, true); +} + +// bundling + +const BundleKind = enum { + client, + server, + + fn Bundle(kind: BundleKind) type { + return switch (kind) { + .client => ClientBundle, + .server => ServerBundle, + }; + } + + /// Routing information from uws.Request is stack allocated. + /// This union has no type tag because it can be inferred from surrounding data. + fn Context(kind: BundleKind) type { + return switch (kind) { + .client => struct { file_name: []const u8 }, + .server => struct {}, + }; + } + + inline fn completionFunction(comptime kind: BundleKind) fn (*Route, *Response, kind.Context(), *kind.Bundle()) void { + return switch (kind) { + .client => onAssetRequestWithBundle, + .server => onServerRequestWithBundle, + }; + } + + const AnyContext: type = @Type(.{ + .Union = .{ + .layout = .auto, + .tag_type = null, + .fields = &fields: { + const values = std.enums.values(BundleKind); + var fields: [values.len]std.builtin.Type.UnionField = undefined; + for (&fields, values) |*field, kind| { + field.* = .{ + .name = @tagName(kind), + .type = kind.Context(), + .alignment = @alignOf(kind.Context()), + }; + } + break :fields fields; + }, + .decls = &.{}, + }, + }); + + inline fn initAnyContext(comptime kind: BundleKind, data: kind.Context()) AnyContext { + return @unionInit(AnyContext, @tagName(kind), data); + } +}; + +/// This will either immediately call `kind.completionFunction()`, or schedule a +/// task to call it when the bundle is ready. The completion function is allowed +/// to use yield. +fn getOrEnqueueBundle( + dev: *DevServer, + resp: *Response, + route: *Route, + comptime kind: BundleKind, + ctx: kind.Context(), +) void { + // const bundler = &dev.bundler; + const bundle = switch (kind) { + .client => &route.client_bundle, + .server => &route.server_bundle, + }; + + switch (bundle.*) { + .unqueued => { + // TODO: use an object pool for this. `bun.ObjectPool` needs a refactor before it can be used + const cb = BundleTask.DeferredRequest.newNode(resp, kind.initAnyContext(ctx)); + + const task = bun.new(BundleTask, .{ + .owner = dev, + .route = route, + .kind = kind, + .plugins = null, + .handlers = .{ .first = cb }, + }); + bundle.* = .{ .pending = task }; + dev.bundle_thread.enqueue(task); + }, + .pending => |task| { + const cb = BundleTask.DeferredRequest.newNode(resp, kind.initAnyContext(ctx)); + // This is not a data race, since this list is drained on + // the same thread as this function is called. + task.handlers.prepend(cb); + }, + .failed => |fail| { + fail.sendAsHttpResponse(resp, route, kind); + }, + .value => |*val| { + kind.completionFunction()(route, resp, ctx, val); + }, + } +} + +const BundleThread = bun.bundle_v2.BundleThread(BundleTask); + +/// A request to bundle something for development. Has one or more pending HTTP requests. +pub const BundleTask = struct { + owner: *DevServer, + route: *Route, + kind: BundleKind, + // env: *bun.DotEnv.Loader, // TODO + plugins: ?*JSC.API.JSBundler.Plugin, + handlers: DeferredRequest.List, + + next: ?*BundleTask = null, + result: BundleV2.Result = .{ .pending = {} }, + + // initialized in the task itself: + concurrent_task: JSC.EventLoopTask = undefined, + bundler: *BundleV2 = undefined, + log: Log = undefined, + + /// There is no function pointer, route, or context on this struct as all of + /// this information is inferable from the associated BundleTask + const DeferredRequest = struct { + /// When cancelled, this is set to null + resp: ?*Response, + /// Only valid if req is non-null + ctx: BundleKind.AnyContext, + + fn newNode(resp: *Response, ctx: BundleKind.AnyContext) *DeferredRequest.List.Node { + const node = bun.new(DeferredRequest.List.Node, .{ + .data = .{ + .resp = resp, + .ctx = ctx, + }, + }); + resp.onAborted(*DeferredRequest, onCancel, &node.data); + return node; + } + + fn onCancel(node: *DeferredRequest, resp: *Response) void { + node.resp = null; + node.ctx = undefined; + _ = resp; + } + + const List = std.SinglyLinkedList(DeferredRequest); + }; + + pub fn completeOnMainThread(task: *BundleTask) void { + switch (task.kind) { + inline else => |kind| task.completeOnMainThreadWithKind(kind), + } + } + + fn completeOnMainThreadWithKind(task: *BundleTask, comptime kind: BundleKind) void { + const route = task.route; + const bundle = switch (kind) { + .client => &route.client_bundle, + .server => &route.server_bundle, + }; + + assert(bundle.* == .pending); + + if (task.result == .err) { + const fail = Failure.fromLog(&task.log); + fail.printToConsole(route, kind); + task.finishHttpRequestsFailure(&fail); + bundle.* = .{ .failed = fail }; + return; + } + + if (task.log.hasAny()) { + Output.warn("Warnings {s} for {s}", .{ + @tagName(task.kind), + route.pattern, + }); + task.log.printForLogLevel(Output.errorWriter()) catch {}; + } + + const files = task.result.value.output_files.items; + bun.assert(files.len > 0); + + const dev = route.dev; + if (dev.dump_dir) |dump_dir| { + dumpBundle(dump_dir, route, kind, files) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + Output.warn("Could not dump bundle: {}", .{err}); + }; + } + + switch (kind) { + .client => { + // Set the capacity to the exact size required to avoid over-allocation + var files_index: bun.CaseInsensitiveASCIIStringArrayHashMapUnmanaged(void) = .{}; + files_index.entries.setCapacity(default_allocator, files.len) catch bun.outOfMemory(); + files_index.entries.len = files.len; + for (files_index.keys(), files) |*index_key, file| { + var dest_path = file.dest_path; + if (bun.strings.hasPrefixComptime(dest_path, "./")) { + dest_path = dest_path[2..]; + } + index_key.* = dest_path; + } + files_index.reIndex(default_allocator) catch bun.outOfMemory(); + + bundle.* = .{ .value = .{ + .files = files, + .files_index = files_index, + } }; + }, + .server => { + const entry_point = files[0]; + const code = entry_point.value.buffer.bytes; + + const server_code = c.KitLoadServerCode(dev.server_global, bun.String.createLatin1(code)); + dev.vm.waitForPromise(.{ .internal = server_code.promise }); + + switch (server_code.promise.unwrap(dev.vm.jsc, .mark_handled)) { + .pending => unreachable, // promise is settled + .rejected => |err| { + const fail = Failure.fromJSServerLoad(err, dev.server_global.js()); + fail.printToConsole(task.route, .server); + task.finishHttpRequestsFailure(&fail); + bundle.* = .{ .failed = fail }; + return; + }, + .fulfilled => |v| bun.assert(v == .undefined), + } + + const handler = c.KitGetRequestHandlerFromModule(dev.server_global, server_code.key); + + if (!handler.isCallable(dev.vm.jsc)) { + @panic("TODO: handle not callable"); + } + + bundle.* = .{ .value = .{ + .files = files, + .server_request_callback = handler, + } }; + }, + } + + task.finishHttpRequestsSuccess(kind, &bundle.value); + } + + fn finishHttpRequestsSuccess(task: *BundleTask, comptime kind: BundleKind, bundle: *kind.Bundle()) void { + const func = comptime kind.completionFunction(); + + while (task.handlers.popFirst()) |node| { + defer bun.destroy(node); + if (node.data.resp) |resp| { + func(task.route, resp, @field(node.data.ctx, @tagName(kind)), bundle); + } + } + } + + fn finishHttpRequestsFailure(task: *BundleTask, failure: *const Failure) void { + while (task.handlers.popFirst()) |node| { + defer bun.destroy(node); + if (node.data.resp) |resp| { + failure.sendAsHttpResponse(resp, task.route, task.kind); + } + } + } + + pub fn configureBundler(task: *BundleTask, bundler: *Bundler, allocator: Allocator) !void { + const dev = task.route.dev; + + bundler.* = try bun.Bundler.init( + allocator, + &task.log, + std.mem.zeroes(bun.Schema.Api.TransformOptions), + null, // TODO: + ); + + const define = bundler.options.define; + bundler.options = dev.bundler.options; + + bundler.options.define = define; + bundler.options.entry_points = (&task.route.entry_point)[0..1]; + bundler.options.log = &task.log; + bundler.options.output_dir = ""; // this disables filesystem outpu; + bundler.options.output_format = .internal_kit_dev; + bundler.options.out_extensions = bun.StringHashMap([]const u8).init(bundler.allocator); + bundler.options.react_fast_refresh = task.kind == .client; + + bundler.options.public_path = switch (task.kind) { + .client => task.route.clientPublicPath(), + .server => task.route.dev.cwd, + }; + bundler.options.target = switch (task.kind) { + .client => .browser, + .server => .bun, + }; + bundler.options.entry_naming = switch (task.kind) { + // Always name it "client.{js/css}" so that the server can know + // the entry-point script without waiting on a client bundle. + .client => "client.[ext]", + // For uniformity + .server => "server.[ext]", + }; + bundler.options.tree_shaking = false; + bundler.options.minify_syntax = true; + + bundler.configureLinker(); + try bundler.configureDefines(); + + // The following are from Vite: https://vitejs.dev/guide/env-and-mode + // TODO: MODE, BASE_URL + try bundler.options.define.insert( + allocator, + "import.meta.env.DEV", + Define.Data.initBoolean(true), + ); + try bundler.options.define.insert( + allocator, + "import.meta.env.PROD", + Define.Data.initBoolean(false), + ); + try bundler.options.define.insert( + allocator, + "import.meta.env.SSR", + Define.Data.initBoolean(task.kind == .server), + ); + + bundler.resolver.opts = bundler.options; + bundler.resolver.watcher = dev.bundler.resolver.watcher; + } + + pub fn completeMini(task: *BundleTask, _: *void) void { + task.completeOnMainThread(); + } + + pub fn completeOnBundleThread(task: *BundleTask) void { + task.route.dev.vm.event_loop.enqueueTaskConcurrent(task.concurrent_task.js.from(task, .manual_deinit)); + } +}; + +/// Bundling should be concurrent, deduplicated, and cached. +/// This acts as a sort of "native promise" +fn BundlePromise(T: type) type { + return union(enum) { + unqueued, + pending: *BundleTask, + failed: Failure, + value: T, + }; +} + +/// Represents an error from loading or server sided runtime. Information on +/// what this error is from, such as the associated Route, is inferred from +/// surrounding context. +/// +/// In the case a route was not able to fully compile, the `Failure` is stored +/// so that a browser refreshing the page can display this failure. +const Failure = union(enum) { + /// Bundler and module resolution use `bun.logger` to report multiple errors at once. + bundler: std.ArrayList(bun.logger.Msg), + /// Thrown JavaScript exception while loading server code. + server_load: JSC.Strong, + /// Never stored; the current request handler threw an error. + request_handler: JSValue, + + /// Consumes the Log data, resetting it. + pub fn fromLog(log: *Log) Failure { + const fail: Failure = .{ .bundler = log.msgs }; + log.* = .{ + .msgs = std.ArrayList(bun.logger.Msg).init(log.msgs.allocator), + .level = log.level, + }; + return fail; + } + + pub fn fromJSServerLoad(js: JSValue, global: *JSC.JSGlobalObject) Failure { + return .{ .server_load = JSC.Strong.create(js, global) }; + } + + // TODO: deduplicate the two methods here. that isnt trivial because one has to + // style with ansi codes, and the other has to style with HTML. + + fn printToConsole(fail: *const Failure, route: *const Route, kind: BundleKind) void { + defer Output.flush(); + + Output.prettyErrorln("", .{}); + + switch (fail.*) { + .bundler => |msgs| { + Output.prettyErrorln("Errors while bundling {s}-side for '{s}'", .{ + @tagName(kind), + route.pattern, + }); + Output.flush(); + + var log: Log = .{ .msgs = msgs, .errors = 1, .level = .err }; + log.printForLogLevelColorsRuntime( + Output.errorWriter(), + Output.enable_ansi_colors_stderr, + ) catch {}; + }, + .server_load => |strong| { + Output.prettyErrorln("Server route handler for '{s}' threw while loading", .{ + route.pattern, + }); + Output.flush(); + + const err = strong.get() orelse unreachable; + route.dev.vm.printErrorLikeObjectToConsole(err); + }, + .request_handler => |err| { + Output.prettyErrorln("Request to handler '{s}' failed SSR", .{ + route.pattern, + }); + Output.flush(); + + route.dev.vm.printErrorLikeObjectToConsole(err); + }, + } + } + + fn sendAsHttpResponse(fail: *const Failure, resp: *Response, route: *const Route, kind: BundleKind) void { + resp.writeStatus("500 Internal Server Error"); + var buffer: [32768]u8 = undefined; + + const message = message: { + var fbs = std.io.fixedBufferStream(&buffer); + const writer = fbs.writer(); + + switch (fail.*) { + .bundler => |msgs| { + writer.print("Errors while bundling {s}-side for '{s}'\n\n", .{ + @tagName(kind), + route.pattern, + }) catch break :message null; + + var log: Log = .{ .msgs = msgs, .errors = 1, .level = .err }; + log.printForLogLevelWithEnableAnsiColors(writer, false) catch + break :message null; + }, + .server_load => |strong| { + writer.print("Server route handler for '{s}' threw while loading\n\n", .{ + route.pattern, + }) catch break :message null; + const err = strong.get() orelse unreachable; + route.dev.vm.printErrorLikeObjectSimple(err, writer, false); + }, + .request_handler => |err| { + writer.print("Server route handler for '{s}' threw while loading\n\n", .{ + route.pattern, + }) catch break :message null; + route.dev.vm.printErrorLikeObjectSimple(err, writer, false); + }, + } + + break :message fbs.getWritten(); + } orelse message: { + const suffix = "...truncated"; + @memcpy(buffer[buffer.len - suffix.len ..], suffix); + break :message &buffer; + }; + resp.end(message, true); // TODO: "You should never call res.end(huge buffer)" + } +}; + +// For debugging, it is helpful to be able to see bundles. +fn dumpBundle(dump_dir: std.fs.Dir, route: *Route, kind: BundleKind, files: []OutputFile) !void { + for (files) |file| { + const name = bun.path.joinAbsString("/", &.{ + route.pattern, + @tagName(kind), + file.dest_path, + }, .auto)[1..]; + var inner_dir = try dump_dir.makeOpenPath(bun.Dirname.dirname(u8, name).?, .{}); + defer inner_dir.close(); + + switch (file.value) { + .buffer => |buf| { + try inner_dir.writeFile(.{ .data = buf.bytes, .sub_path = bun.path.basename(name) }); + }, + else => |t| Output.panic("TODO: implement dumping .{s}", .{@tagName(t)}), + } + } +} + +/// This function is required by `HotReloader` +pub fn eventLoop(dev: *DevServer) *JSC.EventLoop { + return dev.vm.eventLoop(); +} + +pub fn onWebSocketUpgrade( + dev: *DevServer, + res: *Response, + req: *Request, + upgrade_ctx: *uws.uws_socket_context_t, + id: usize, +) void { + assert(id == 0); + + const dw = bun.new(DevWebSocket, .{ .dev = dev }); + res.upgrade( + *DevWebSocket, + dw, + req.header("sec-websocket-key") orelse "", + req.header("sec-websocket-protocol") orelse "", + req.header("sec-websocket-extension") orelse "", + upgrade_ctx, + ); +} + +const DevWebSocket = struct { + dev: *DevServer, + + pub fn onOpen(dw: *DevWebSocket, ws: AnyWebSocket) void { + _ = ws.send("bun!", .binary, false, false); + std.debug.print("open {*} {}\n", .{ dw, ws }); + } + + pub fn onMessage(dw: *DevWebSocket, ws: AnyWebSocket, msg: []const u8, opcode: uws.Opcode) void { + std.debug.print("message {*} {} {} '{s}'\n", .{ dw, ws, opcode, msg }); + } + + pub fn onClose(dw: *DevWebSocket, ws: AnyWebSocket, exit_code: i32, message: []const u8) void { + defer bun.destroy(dw); + + std.debug.print("close {*} {} {} '{s}'\n", .{ dw, ws, exit_code, message }); + } +}; + +/// Kit uses a special global object extending Zig::GlobalObject +pub const DevGlobalObject = opaque { + /// Safe downcast to use other Bun APIs + pub fn js(ptr: *DevGlobalObject) *JSC.JSGlobalObject { + return @ptrCast(ptr); + } + + pub fn vm(ptr: *DevGlobalObject) *JSC.VM { + return ptr.js().vm(); + } +}; + +pub const KitSourceProvider = opaque {}; + +pub const c = struct { + // KitDevGlobalObject.cpp + extern fn KitCreateDevGlobal(owner: *DevServer, console: *JSC.ConsoleObject) *DevGlobalObject; + + // KitSourceProvider.cpp + const LoadServerCodeResult = extern struct { promise: *JSInternalPromise, key: *JSC.JSString }; + extern fn KitLoadServerCode(global: *DevGlobalObject, code: bun.String) LoadServerCodeResult; + extern fn KitGetRequestHandlerFromModule(global: *DevGlobalObject, module: *JSC.JSString) JSValue; +}; + +pub fn reload(dev: *DevServer) void { + // TODO: given no arguments, this method is absolutely useless. The watcher + // must be augmented with more information. + _ = dev; + Output.warn("TODO: initiate hot reload", .{}); +} + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const bun = @import("root").bun; +const Environment = bun.Environment; +const assert = bun.assert; + +const Log = bun.logger.Log; + +const Bundler = bun.bundler.Bundler; +const BundleV2 = bun.bundle_v2.BundleV2; +const Define = bun.options.Define; +const OutputFile = bun.options.OutputFile; + +// TODO: consider if using system output is not fit +const Output = bun.Output; + +const uws = bun.uws; +const App = uws.NewApp(false); +const AnyWebSocket = uws.AnyWebSocket; +const Request = uws.Request; +const Response = App.Response; + +const MimeType = bun.http.MimeType; + +const JSC = bun.JSC; +const JSValue = JSC.JSValue; +const VirtualMachine = JSC.VirtualMachine; +const JSModuleLoader = JSC.JSModuleLoader; +const EventLoopHandle = JSC.EventLoopHandle; +const JSInternalPromise = JSC.JSInternalPromise; + +pub const HotReloader = JSC.NewHotReloader(DevServer, JSC.EventLoop, false); +pub const HotReloadTask = HotReloader.HotReloadTask; diff --git a/src/kit/KitDevGlobalObject.cpp b/src/kit/KitDevGlobalObject.cpp new file mode 100644 index 0000000000000..e797f3f03bd1e --- /dev/null +++ b/src/kit/KitDevGlobalObject.cpp @@ -0,0 +1,82 @@ +#include "KitDevGlobalObject.h" +#include "JavaScriptCore/GlobalObjectMethodTable.h" +#include "JSNextTickQueue.h" +#include "headers-handwritten.h" + +namespace Kit { + +#define INHERIT_HOOK_METHOD(name) Zig::GlobalObject::s_globalObjectMethodTable. name + +const JSC::GlobalObjectMethodTable DevGlobalObject::s_globalObjectMethodTable = { + INHERIT_HOOK_METHOD(supportsRichSourceInfo), + INHERIT_HOOK_METHOD(shouldInterruptScript), + INHERIT_HOOK_METHOD(javaScriptRuntimeFlags), + INHERIT_HOOK_METHOD(queueMicrotaskToEventLoop), + INHERIT_HOOK_METHOD(shouldInterruptScriptBeforeTimeout), + INHERIT_HOOK_METHOD(moduleLoaderImportModule), + INHERIT_HOOK_METHOD(moduleLoaderResolve), + INHERIT_HOOK_METHOD(moduleLoaderFetch), + INHERIT_HOOK_METHOD(moduleLoaderCreateImportMetaProperties), + INHERIT_HOOK_METHOD(moduleLoaderEvaluate), + INHERIT_HOOK_METHOD(promiseRejectionTracker), + INHERIT_HOOK_METHOD(reportUncaughtExceptionAtEventLoop), + INHERIT_HOOK_METHOD(currentScriptExecutionOwner), + INHERIT_HOOK_METHOD(scriptExecutionStatus), + INHERIT_HOOK_METHOD(reportViolationForUnsafeEval), + INHERIT_HOOK_METHOD(defaultLanguage), + INHERIT_HOOK_METHOD(compileStreaming), + INHERIT_HOOK_METHOD(instantiateStreaming), + INHERIT_HOOK_METHOD(deriveShadowRealmGlobalObject), + INHERIT_HOOK_METHOD(codeForEval), + INHERIT_HOOK_METHOD(canCompileStrings), +}; + +DevGlobalObject* DevGlobalObject::create(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable) +{ + DevGlobalObject* ptr = new (NotNull, JSC::allocateCell(vm)) DevGlobalObject(vm, structure, methodTable); + ptr->finishCreation(vm); + return ptr; +} + +void DevGlobalObject::finishCreation(JSC::VM &vm) { + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +extern "C" BunVirtualMachine* Bun__getVM(); + +// A lot of this function is taken from 'Zig__GlobalObject__create' +extern "C" DevGlobalObject* KitCreateDevGlobal(DevServer* owner, void* console) { + JSC::VM& vm = JSC::VM::create(JSC::HeapType::Large).leakRef(); + vm.heap.acquireAccess(); + JSC::JSLockHolder locker(vm); + BunVirtualMachine* bunVM = Bun__getVM(); + WebCore::JSVMClientData::create(&vm, bunVM); + + JSC::Structure* structure = DevGlobalObject::createStructure(vm); + DevGlobalObject* global = DevGlobalObject::create(vm, structure, &DevGlobalObject::s_globalObjectMethodTable); + if (!global) + BUN_PANIC("Failed to create DevGlobalObject"); + + global->m_devServer = owner; + global->m_bunVM = bunVM; + + JSC::gcProtect(global); + + global->setConsole(console); + global->setStackTraceLimit(10); // Node.js defaults to 10 + + // vm.setOnComputeErrorInfo(computeErrorInfoWrapper); + vm.setOnEachMicrotaskTick([global](JSC::VM& vm) -> void { + if (auto nextTickQueue = global->m_nextTickQueue.get()) { + global->resetOnEachMicrotaskTick(); + Bun::JSNextTickQueue* queue = jsCast(nextTickQueue); + queue->drain(vm, global); + return; + } + }); + + return global; +} + +}; // namespace Kit diff --git a/src/kit/KitDevGlobalObject.h b/src/kit/KitDevGlobalObject.h new file mode 100644 index 0000000000000..07126aca3a1e4 --- /dev/null +++ b/src/kit/KitDevGlobalObject.h @@ -0,0 +1,42 @@ +#pragma once +#include "root.h" +#include "ZigGlobalObject.h" + +namespace Kit { + +struct DevServer; // DevServer.zig +struct Route; // DevServer.zig +struct BunVirtualMachine; + +class DevGlobalObject : public Zig::GlobalObject { +public: + using Base = Zig::GlobalObject; + + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForKitGlobalScope.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForKitGlobalScope = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForKitGlobalScope.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForKitGlobalScope = std::forward(space); }, + [](auto& server) -> JSC::HeapCellType& { return server.m_heapCellTypeForJSWorkerGlobalScope; }); + } + + static const JSC::GlobalObjectMethodTable s_globalObjectMethodTable; + static DevGlobalObject* create(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable); + + DevServer* m_devServer; + + void finishCreation(JSC::VM& vm); + + DevGlobalObject(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable) + : Zig::GlobalObject(vm, structure, methodTable) { } +}; + +// Zig API +extern "C" DevGlobalObject* KitCreateDevGlobal(DevServer* owner, void* console); + +}; // namespace Kit diff --git a/src/kit/KitSourceProvider.cpp b/src/kit/KitSourceProvider.cpp new file mode 100644 index 0000000000000..e28e1c4fd175e --- /dev/null +++ b/src/kit/KitSourceProvider.cpp @@ -0,0 +1,55 @@ +// clang-format off +#include "KitSourceProvider.h" +#include "JavaScriptCore/Completion.h" +#include "JavaScriptCore/Identifier.h" +#include "JavaScriptCore/JSCJSValue.h" +#include "JavaScriptCore/JSCast.h" +#include "JavaScriptCore/JSLock.h" +#include "JavaScriptCore/JSMap.h" +#include "JavaScriptCore/JSModuleLoader.h" +#include "JavaScriptCore/JSString.h" +#include "JavaScriptCore/JSModuleNamespaceObject.h" +#include "KitDevGlobalObject.h" + +namespace Kit { + + +extern "C" LoadServerCodeResult KitLoadServerCode(DevGlobalObject* global, BunString source) { + String string = "kit://server/0/index.js"_s; + JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(string)); + JSC::SourceCode sourceCode = JSC::SourceCode(KitSourceProvider::create( + source.toWTFString(), + origin, + WTFMove(string), + WTF::TextPosition(), + JSC::SourceProviderSourceType::Module + )); + + JSC::JSString* key = JSC::jsString(global->vm(), string); + global->moduleLoader()->provideFetch(global, key, sourceCode); + + return { + global->moduleLoader()->loadAndEvaluateModule(global, key, JSC::jsUndefined(), JSC::jsUndefined()), + key + }; +} + +extern "C" JSC::EncodedJSValue KitGetRequestHandlerFromModule( + DevGlobalObject* global, + JSC::JSString* key +) { + JSC::VM&vm = global->vm(); + JSC::JSMap* map = JSC::jsCast( + global->moduleLoader()->getDirect( + vm, JSC::Identifier::fromString(global->vm(), "registry"_s) + )); + JSC::JSValue entry = map->get(global, key); + ASSERT(entry.isObject()); // should have called KitLoadServerCode and wait for that promise + JSC::JSValue module = entry.getObject()->get(global, JSC::Identifier::fromString(global->vm(), "module"_s)); + ASSERT(module.isCell()); + JSC::JSModuleNamespaceObject* namespaceObject = global->moduleLoader()->getModuleNamespaceObject(global, module); + ASSERT(namespaceObject); + return JSC::JSValue::encode(namespaceObject->get(global, vm.propertyNames->defaultKeyword)); +} + +} // namespace Kit diff --git a/src/kit/KitSourceProvider.h b/src/kit/KitSourceProvider.h new file mode 100644 index 0000000000000..1aacb369c7510 --- /dev/null +++ b/src/kit/KitSourceProvider.h @@ -0,0 +1,47 @@ +#pragma once +#include "root.h" +#include "headers-handwritten.h" +#include "KitDevGlobalObject.h" +#include "JavaScriptCore/SourceOrigin.h" + +namespace Kit { + +struct LoadServerCodeResult { + JSC::JSInternalPromise* promise; + JSC::JSString* key; +}; + +class KitSourceProvider final : public JSC::StringSourceProvider { +public: + static Ref create( + const String& source, + const JSC::SourceOrigin& sourceOrigin, + String&& sourceURL, + const TextPosition& startPosition, + JSC::SourceProviderSourceType sourceType + ) { + return adoptRef(*new KitSourceProvider(source, sourceOrigin, WTFMove(sourceURL), startPosition, sourceType)); + } + +private: + KitSourceProvider( + const String& source, + const JSC::SourceOrigin& sourceOrigin, + String&& sourceURL, + const TextPosition& startPosition, + JSC::SourceProviderSourceType sourceType + ) : StringSourceProvider( + source, + sourceOrigin, + JSC::SourceTaintedOrigin::Untainted, + WTFMove(sourceURL), + startPosition, + sourceType + ) {} +}; + +// Zig API +extern "C" LoadServerCodeResult KitLoadServerCode(DevGlobalObject* global, BunString source); +extern "C" JSC::EncodedJSValue KitGetRequestHandlerFromModule(DevGlobalObject* global, JSC::JSString* encodedModule); + +} // namespace Kit diff --git a/src/kit/client/overlay.css b/src/kit/client/overlay.css new file mode 100644 index 0000000000000..6f4cc360ab8b5 --- /dev/null +++ b/src/kit/client/overlay.css @@ -0,0 +1,18 @@ +/* + * This file is mounted within Shadow DOM so interference with + * the user's application causes no issue. This sheet is used to + * style error popups and other elements provided by DevServer. + */ + +* { + box-sizing: border-box; +} + +main { + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +.error { + padding: 1rem; + background-color: rgba(255, 0, 0, 0.2); +} \ No newline at end of file diff --git a/src/kit/client/overlay.ts b/src/kit/client/overlay.ts new file mode 100644 index 0000000000000..9ada1d768dc48 --- /dev/null +++ b/src/kit/client/overlay.ts @@ -0,0 +1,27 @@ +import { css } from '../macros' with { type: 'macro' }; + +// Create a root element to contain all our our DOM nodes. +var root!: HTMLElement; +var mount; + +if (mode === 'client') { + mount = function mount() { + const wrap = document.createElement('bun-hmr'); + wrap.setAttribute('style', 'position:absolute;display:block;top:0;left:0;width:100%;height:100%;background:transparent'); + const shadow = wrap.attachShadow({ mode: 'open' }); + + const sheet = new CSSStyleSheet(); + sheet.replace(css('client/overlay.css', IS_BUN_DEVELOPMENT)); + shadow.adoptedStyleSheets = [ sheet ]; + + root = document.createElement('main'); + shadow.appendChild(root); + document.body.appendChild(wrap); + } +} + +export function showErrorOverlay(e) { + mount(); + console.error(e); + root.innerHTML = `

oh no, a client side error happened:

${e?.message ? `${e?.name ?? (e?.constructor?.name) ?? 'Error'}: ${e.message}\n` : JSON.stringify(e)}${e?.message ? e?.stack : ''}
`; +} diff --git a/src/kit/hmr-module.ts b/src/kit/hmr-module.ts new file mode 100644 index 0000000000000..440e1998cf9f7 --- /dev/null +++ b/src/kit/hmr-module.ts @@ -0,0 +1,65 @@ +import * as runtimeHelpers from '../runtime.bun.js'; + +const registry = new Map() + +export type ModuleLoadFunction = (module: HotModule) => void; +export type ExportsCallbackFunction = (new_exports: any) => void; + +/** + * This object is passed as the CommonJS "module", but has a bunch of + * non-standard properties that are used for implementing hot-module + * reloading. It is unacceptable to depend + */ +export class HotModule { + exports: any = {}; + + _ext_exports = undefined; + __esModule = false; + _import_meta?: ImportMeta; + + constructor(public id: Id) {} + + require(id: Id, onReload: null | ExportsCallbackFunction) { + return loadModule(id).exports; + } + + importSync(id: Id, onReload: null | ExportsCallbackFunction) { + const module = loadModule(id); + const { exports, __esModule } = module; + return __esModule + ? exports + : module._ext_exports ??= { ...exports, default: exports }; + } + + importMeta() { + return this._import_meta ??= initImportMeta(this); + } +} + +function initImportMeta(m: HotModule): ImportMeta { + throw new Error("TODO: import meta object"); +} + +// { +// const runtime = new HotModule(0); +// runtime.exports = runtimeHelpers; +// runtime.__esModule = true; +// registry.set(0, runtime); +// } + +export function loadModule(key: Id): HotModule { + let module = registry.get(key); + if (module) return module; + module = new HotModule(key); + registry.set(key, module); + const load = input_graph[key]; + if (!load) { + throw new Error(`Failed to load bundled module '${key}'. This is not a dynamic import, and therefore is a bug in Bun`); + } + load(module); + return module; +} + +runtimeHelpers.__name(HotModule.prototype.importSync, ' importSync') +runtimeHelpers.__name(HotModule.prototype.require, ' require') +runtimeHelpers.__name(loadModule, ' loadModule') diff --git a/src/kit/hmr-runtime-types.d.ts b/src/kit/hmr-runtime-types.d.ts new file mode 100644 index 0000000000000..1a830ed220da4 --- /dev/null +++ b/src/kit/hmr-runtime-types.d.ts @@ -0,0 +1,35 @@ +/* + * A module id is an unsigned 52-bit numeric hash of the filepath. + * + * TODO: how resistant to hash collision is this? if it is not, an alternate approach must be taken. + */ +type Id = number; + +interface Config { + main: Id; + /** If available, this is the Id of `react-refresh/runtime` */ + refresh: Id; +} + +/** + * All modules for the initial bundle. + */ +declare const input_graph: Record; + +declare const config: Config; + +/** + * The runtime is bundled for server and client, which influences + * how hmr connection should be established, as well if there is + * a window to visually display errors with. +*/ +declare const mode: 'client' | 'server'; + +/* What should be `export default`'d */ +declare var server_fetch_function: any; + +/* + * If you are running a debug build of Bun. These debug builds should provide + * helpful information to someone working on the bundler itself. + */ +declare const IS_BUN_DEVELOPMENT: any; diff --git a/src/kit/hmr-runtime.ts b/src/kit/hmr-runtime.ts new file mode 100644 index 0000000000000..fe7ba48d26f5a --- /dev/null +++ b/src/kit/hmr-runtime.ts @@ -0,0 +1,46 @@ +// This file is the entrypoint to the hot-module-reloading runtime +// In the browser, this uses a WebSocket to communicate with the bundler. +// On the server, communication is facilitated using a secret global. +import { loadModule } from './hmr-module'; +import { showErrorOverlay } from './client/overlay'; + +if (typeof IS_BUN_DEVELOPMENT !== 'boolean') { throw new Error('DCE is configured incorrectly') } + +// Initialize client-side features. +if (mode === 'client') { + const { refresh } = config; + if(refresh) { + const runtime = loadModule(refresh).exports; + runtime.injectIntoGlobalHook(window); + } +} + +// Load the entry point module +try { + const main = loadModule(config.main); + + // export it on the server side + if (mode === 'server') + server_fetch_function = main.exports.default; + + if (mode === 'client') { + const ws = new WebSocket('/_bun/hmr'); + ws.onopen = (ev) => { + console.log(ev); + } + ws.onmessage = (ev) => { + console.log(ev); + } + ws.onclose = (ev) => { + console.log(ev); + } + ws.onerror = (ev) => { + console.log(ev); + } + } +} catch (e) { + if (mode !== 'client') throw e; + showErrorOverlay(e); +} + +export {} diff --git a/src/kit/kit.zig b/src/kit/kit.zig new file mode 100644 index 0000000000000..1ab5e7b4f654e --- /dev/null +++ b/src/kit/kit.zig @@ -0,0 +1,109 @@ +//! Kit is the code name for the work-in-progress "Framework API [SOON]" for Bun. + +/// Temporary function to invoke dev server via JavaScript. Will be +/// replaced with a user-facing API. Refs the event loop forever. +/// +/// Requires one argument object for configuration. Very little is +/// exposed over the JS api as it is not intended to be used for +/// real applications yet. +/// ```ts +/// interface WipDevServerOptions { +/// routes: WipDevServerRoute[] +/// } +/// interface WipDevServerRoute { +/// pattern: string; +/// entrypoint: string; +/// } +/// ``` +pub fn jsWipDevServer(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + if (!bun.FeatureFlags.kit) return .undefined; + + bun.Output.warn( + \\Be advised that Kit is highly experimental, and its API is subject to change + , .{}); + bun.Output.flush(); + + const options = devServerOptionsFromJs(global, callframe.argument(0)) catch { + if (!global.hasException()) + global.throwInvalidArguments("invalid arguments", .{}); + return .zero; + }; + + const t = std.Thread.spawn(.{}, wipDevServer, .{options}) catch @panic("Failed to start"); + t.detach(); + + { + var futex = std.atomic.Value(u32).init(0); + while (true) std.Thread.Futex.wait(&futex, 0); + } +} + +// TODO: this function leaks memory and bad error handling, but that is OK since +// this API is not finalized. +fn devServerOptionsFromJs(global: *JSC.JSGlobalObject, options: JSValue) !DevServer.Options { + if (!options.isObject()) return error.Invalid; + const routes_js = try options.getArray(global, "routes") orelse return error.Invalid; + + const len = routes_js.getLength(global); + const routes = try bun.default_allocator.alloc(DevServer.Route, len); + + var it = routes_js.arrayIterator(global); + var i: usize = 0; + while (it.next()) |route| : (i += 1) { + if (!route.isObject()) return error.Invalid; + + const pattern_js = route.get(global, "pattern") orelse return error.Invalid; + if (!pattern_js.isString()) return error.Invalid; + const entry_point_js = route.get(global, "entrypoint") orelse return error.Invalid; + if (!entry_point_js.isString()) return error.Invalid; + + const pattern = pattern_js.toBunString(global).toUTF8(bun.default_allocator); + defer pattern.deinit(); + // this dupe is stupid + const pattern_z = try bun.default_allocator.dupeZ(u8, pattern.slice()); + const entry_point = entry_point_js.toBunString(global).toUTF8(bun.default_allocator).slice(); // leak + + routes[i] = .{ + .pattern = pattern_z, + .entry_point = entry_point, + }; + } + + return .{ + .cwd = bun.getcwdAlloc(bun.default_allocator) catch bun.outOfMemory(), + .routes = routes, + }; +} + +export fn Bun__getTemporaryDevServer(global: *JSC.JSGlobalObject) JSValue { + if (!bun.FeatureFlags.kit) return .undefined; + return JSC.JSFunction.create(global, "wipDevServer", bun.JSC.toJSHostFunction(jsWipDevServer), 0, .{}); +} + +pub fn wipDevServer(options: DevServer.Options) noreturn { + bun.Output.Source.configureNamedThread("Dev Server"); + + const dev = DevServer.init(options); + dev.runLoopForever(); +} + +pub fn getHmrRuntime(mode: enum { server, client }) []const u8 { + return if (Environment.embed_code) + switch (mode) { + .client => @embedFile("kit-codegen/kit.client.js"), + .server => @embedFile("kit-codegen/kit.server.js"), + } + else switch (mode) { + inline else => |m| bun.runtimeEmbedFile(.codegen, "kit." ++ @tagName(m) ++ ".js"), + }; +} + +pub const DevServer = @import("./DevServer.zig"); + +const std = @import("std"); + +const bun = @import("root").bun; +const Environment = bun.Environment; + +const JSC = bun.JSC; +const JSValue = JSC.JSValue; diff --git a/src/kit/macros.ts b/src/kit/macros.ts new file mode 100644 index 0000000000000..dd3f2aa8de531 --- /dev/null +++ b/src/kit/macros.ts @@ -0,0 +1,11 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +export function css(file: string, is_development: boolean): string { + const contents = readFileSync(resolve(import.meta.dir, file), 'utf-8'); + if (!is_development) { + // TODO: minify + return contents; + } + return contents; +} diff --git a/src/kit/tsconfig.json b/src/kit/tsconfig.json new file mode 100644 index 0000000000000..36e3ffb7e8af5 --- /dev/null +++ b/src/kit/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "lib": ["DOM", "ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "strict": true, + "noImplicitAny": false, + "allowJs": true, + "downlevelIteration": true, + "esModuleInterop": true, + "skipLibCheck": true, + "jsx": "react-jsx", + }, + "include": ["./hmr-runtime-types.d.ts", "*.ts"] +} \ No newline at end of file diff --git a/src/libarchive/libarchive-bindings.zig b/src/libarchive/libarchive-bindings.zig index 33b0766b297e1..924cd7b031e95 100644 --- a/src/libarchive/libarchive-bindings.zig +++ b/src/libarchive/libarchive-bindings.zig @@ -4,7 +4,6 @@ pub const la_int64_t = i64; pub const la_ssize_t = isize; pub const struct_archive = opaque {}; pub const struct_archive_entry = opaque {}; -pub const archive = struct_archive; pub const archive_entry = struct_archive_entry; const mode_t = bun.Mode; const FILE = @import("std").c.FILE; @@ -132,14 +131,231 @@ pub const ARCHIVE_MATCH_NEWER = @as(c_int, 0x0001); pub const ARCHIVE_MATCH_OLDER = @as(c_int, 0x0002); pub const ARCHIVE_MATCH_EQUAL = @as(c_int, 0x0010); -pub extern fn archive_version_number() c_int; -pub extern fn archive_version_string() [*c]const u8; -pub extern fn archive_version_details() [*c]const u8; -pub extern fn archive_zlib_version() [*c]const u8; -pub extern fn archive_liblzma_version() [*c]const u8; -pub extern fn archive_bzlib_version() [*c]const u8; -pub extern fn archive_liblz4_version() [*c]const u8; -pub extern fn archive_libzstd_version() [*c]const u8; +pub const Archive = opaque { + pub const Result = enum(i32) { + eof = ARCHIVE_EOF, + ok = ARCHIVE_OK, + retry = ARCHIVE_RETRY, + warn = ARCHIVE_WARN, + failed = ARCHIVE_FAILED, + fatal = ARCHIVE_FATAL, + }; + + extern fn archive_version_number() c_int; + pub fn versionNumber() i32 { + return archive_version_number(); + } + extern fn archive_version_string() [*c]const u8; + pub fn versionString() []const u8 { + return bun.sliceTo(archive_version_string(), 0); + } + extern fn archive_version_details() [*c]const u8; + pub fn versionDetails() []const u8 { + return bun.sliceTo(archive_version_details(), 0); + } + extern fn archive_zlib_version() [*c]const u8; + pub fn zlibVersion() []const u8 { + return bun.sliceTo(archive_zlib_version(), 0); + } + extern fn archive_liblzma_version() [*c]const u8; + pub fn liblzmaVersion() []const u8 { + return bun.sliceTo(archive_liblzma_version(), 0); + } + extern fn archive_bzlib_version() [*c]const u8; + pub fn bzlibVersion() []const u8 { + return bun.sliceTo(archive_bzlib_version(), 0); + } + extern fn archive_liblz4_version() [*c]const u8; + pub fn liblz4Version() []const u8 { + return bun.sliceTo(archive_liblz4_version(), 0); + } + extern fn archive_libzstd_version() [*c]const u8; + pub fn libzstdVersion() []const u8 { + return bun.sliceTo(archive_libzstd_version(), 0); + } + + extern fn archive_error_string(*Archive) [*c]const u8; + pub fn errorString(archive: *Archive) []const u8 { + return bun.sliceTo(archive_error_string(archive), 0); + } + + extern fn archive_write_new() *Archive; + pub fn writeNew() *Archive { + return archive_write_new(); + } + + extern fn archive_write_close(*Archive) Result; + pub fn writeClose(archive: *Archive) Result { + return archive_write_close(archive); + } + + pub extern fn archive_write_finish(*Archive) Result; + pub fn writeFinish(archive: *Archive) Result { + return archive_write_finish(archive); + } + + extern fn archive_free(*Archive) Result; + pub fn free(archive: *Archive) Result { + return archive_free(archive); + } + + pub extern fn archive_write_set_options(_a: *Archive, opts: [*c]const u8) Result; + pub fn writeSetOptions(archive: *Archive, opts: [:0]const u8) Result { + return archive_write_set_options(archive, opts); + } + + extern fn archive_write_set_format_pax_restricted(*Archive) Result; + pub fn writeSetFormatPaxRestricted(archive: *Archive) Result { + return archive_write_set_format_pax_restricted(archive); + } + + pub extern fn archive_write_set_format_gnutar(*Archive) Result; + pub fn writeSetFormatGnutar(archive: *Archive) Result { + return archive_write_set_format_gnutar(archive); + } + + pub extern fn archive_write_set_format_7zip(*Archive) Result; + pub fn writeSetFormat7zip(archive: *Archive) Result { + return archive_write_set_format_7zip(archive); + } + + pub extern fn archive_write_set_format_pax(*Archive) Result; + pub fn writeSetFormatPax(archive: *Archive) Result { + return archive_write_set_format_pax(archive); + } + + pub extern fn archive_write_set_format_ustar(*Archive) Result; + pub fn writeSetFormatUstar(archive: *Archive) Result { + return archive_write_set_format_ustar(archive); + } + + pub extern fn archive_write_set_format_zip(*Archive) Result; + pub fn writeSetFormatZip(archive: *Archive) Result { + return archive_write_set_format_zip(archive); + } + + pub extern fn archive_write_set_format_shar(*Archive) Result; + pub fn writeSetFormatShar(archive: *Archive) Result { + return archive_write_set_format_shar(archive); + } + + extern fn archive_write_set_compression_gzip(*Archive) Result; + pub fn writeSetCompressionGzip(archive: *Archive) Result { + return archive_write_set_compression_gzip(archive); + } + + extern fn archive_write_add_filter_gzip(*Archive) Result; + pub fn writeAddFilterGzip(archive: *Archive) Result { + return archive_write_add_filter_gzip(archive); + } + + extern fn archive_write_set_filter_option(*Archive, [*c]const u8, [*c]const u8, [*c]const u8) Result; + pub fn writeSetFilterOption(archive: *Archive, m: ?[:0]const u8, o: [:0]const u8, v: [:0]const u8) Result { + return archive_write_set_filter_option(archive, m orelse null, o, v); + } + + extern fn archive_write_open_filename(*Archive, [*c]const u8) Result; + pub fn writeOpenFilename(archive: *Archive, filename: [:0]const u8) Result { + return archive_write_open_filename(archive, filename); + } + + pub extern fn archive_write_open_fd(*Archive, _fd: c_int) Result; + pub fn writeOpenFd(archive: *Archive, fd: bun.FileDescriptor) Result { + return archive_write_open_fd(archive, fd.cast()); + } + + pub extern fn archive_write_open_memory(*Archive, _buffer: ?*anyopaque, _buffSize: usize, _used: [*c]usize) Result; + pub fn writeOpenMemory(archive: *Archive, buf: ?*anyopaque, buf_size: usize, used: *usize) Result { + return archive_write_open_memory(archive, buf, buf_size, used); + } + + extern fn archive_write_header(*Archive, *Entry) Result; + pub fn writeHeader(archive: *Archive, entry: *Entry) Result { + return archive_write_header(archive, entry); + } + + extern fn archive_write_data(*Archive, ?*const anyopaque, usize) isize; + pub fn writeData(archive: *Archive, data: []const u8) isize { + return archive_write_data(archive, data.ptr, data.len); + } + + pub extern fn archive_write_finish_entry(*Archive) Result; + pub fn writeFinishEntry(archive: *Archive) Result { + return archive_write_finish_entry(archive); + } + + pub extern fn archive_write_free(*Archive) Result; + pub fn writeFree(archive: *Archive) Result { + return archive_write_free(archive); + } + + pub const Entry = opaque { + extern fn archive_entry_new() *Entry; + pub fn new() *Entry { + return archive_entry_new(); + } + + pub extern fn archive_entry_new2(*Archive) *Entry; + pub fn new2(archive: *Archive) *Entry { + return archive_entry_new2(archive); + } + + extern fn archive_entry_free(*Entry) void; + pub fn free(entry: *Entry) void { + archive_entry_free(entry); + } + + extern fn archive_entry_set_pathname(*Entry, [*c]const u8) void; + pub fn setPathname(entry: *Entry, pathname: [:0]const u8) void { + archive_entry_set_pathname(entry, pathname); + } + + extern fn archive_entry_set_pathname_utf8(*Entry, [*c]const u8) void; + pub fn setPathnameUtf8(entry: *Entry, pathname: [:0]const u8) void { + archive_entry_set_pathname_utf8(entry, pathname); + } + + extern fn archive_entry_copy_pathname(*Entry, [*c]const u8) void; + pub fn copyPathname(entry: *Entry, pathname: [:0]const u8) void { + return archive_entry_copy_pathname(entry, pathname); + } + + pub extern fn archive_entry_copy_pathname_w(*Entry, [*c]const u16) void; + pub fn copyPathnameW(entry: *Entry, pathname: [:0]const u16) void { + return archive_entry_copy_pathname_w(entry, pathname); + } + + extern fn archive_entry_set_size(*Entry, i64) void; + pub fn setSize(entry: *Entry, size: i64) void { + archive_entry_set_size(entry, size); + } + + extern fn archive_entry_set_filetype(*Entry, c_uint) void; + pub fn setFiletype(entry: *Entry, filetype: u32) void { + archive_entry_set_filetype(entry, filetype); + } + + extern fn archive_entry_set_perm(*Entry, bun.Mode) void; + pub fn setPerm(entry: *Entry, perm: bun.Mode) void { + archive_entry_set_perm(entry, perm); + } + + pub extern fn archive_entry_set_mode(*Entry, bun.Mode) void; + pub fn setMode(entry: *Entry, mode: bun.Mode) void { + archive_entry_set_mode(entry, mode); + } + + extern fn archive_entry_set_mtime(*Entry, isize, c_long) void; + pub fn setMtime(entry: *Entry, secs: isize, nsecs: c_long) void { + archive_entry_set_mtime(entry, secs, nsecs); + } + + extern fn archive_entry_clear(*Entry) *Entry; + pub fn clear(entry: *Entry) *Entry { + return archive_entry_clear(entry); + } + }; +}; pub const archive_read_callback = *const fn (*struct_archive, *anyopaque, [*c]*const anyopaque) callconv(.C) la_ssize_t; pub const archive_skip_callback = *const fn (*struct_archive, *anyopaque, la_int64_t) callconv(.C) la_int64_t; @@ -250,7 +466,6 @@ pub extern fn archive_read_extract_set_skip_file(*struct_archive, la_int64_t, la pub extern fn archive_read_close(*struct_archive) c_int; pub extern fn archive_read_free(*struct_archive) c_int; pub extern fn archive_read_finish(*struct_archive) c_int; -pub extern fn archive_write_new() *struct_archive; pub extern fn archive_write_set_bytes_per_block(*struct_archive, bytes_per_block: c_int) c_int; pub extern fn archive_write_get_bytes_per_block(*struct_archive) c_int; pub extern fn archive_write_set_bytes_in_last_block(*struct_archive, bytes_in_last_block: c_int) c_int; @@ -258,7 +473,6 @@ pub extern fn archive_write_get_bytes_in_last_block(*struct_archive) c_int; pub extern fn archive_write_set_skip_file(*struct_archive, la_int64_t, la_int64_t) c_int; pub extern fn archive_write_set_compression_bzip2(*struct_archive) c_int; pub extern fn archive_write_set_compression_compress(*struct_archive) c_int; -pub extern fn archive_write_set_compression_gzip(*struct_archive) c_int; pub extern fn archive_write_set_compression_lzip(*struct_archive) c_int; pub extern fn archive_write_set_compression_lzma(*struct_archive) c_int; pub extern fn archive_write_set_compression_none(*struct_archive) c_int; @@ -270,7 +484,6 @@ pub extern fn archive_write_add_filter_b64encode(*struct_archive) c_int; pub extern fn archive_write_add_filter_bzip2(*struct_archive) c_int; pub extern fn archive_write_add_filter_compress(*struct_archive) c_int; pub extern fn archive_write_add_filter_grzip(*struct_archive) c_int; -pub extern fn archive_write_add_filter_gzip(*struct_archive) c_int; pub extern fn archive_write_add_filter_lrzip(*struct_archive) c_int; pub extern fn archive_write_add_filter_lz4(*struct_archive) c_int; pub extern fn archive_write_add_filter_lzip(*struct_archive) c_int; @@ -283,7 +496,6 @@ pub extern fn archive_write_add_filter_xz(*struct_archive) c_int; pub extern fn archive_write_add_filter_zstd(*struct_archive) c_int; pub extern fn archive_write_set_format(*struct_archive, format_code: c_int) c_int; pub extern fn archive_write_set_format_by_name(*struct_archive, name: [*c]const u8) c_int; -pub extern fn archive_write_set_format_7zip(*struct_archive) c_int; pub extern fn archive_write_set_format_ar_bsd(*struct_archive) c_int; pub extern fn archive_write_set_format_ar_svr4(*struct_archive) c_int; pub extern fn archive_write_set_format_cpio(*struct_archive) c_int; @@ -291,44 +503,27 @@ pub extern fn archive_write_set_format_cpio_bin(*struct_archive) c_int; pub extern fn archive_write_set_format_cpio_newc(*struct_archive) c_int; pub extern fn archive_write_set_format_cpio_odc(*struct_archive) c_int; pub extern fn archive_write_set_format_cpio_pwb(*struct_archive) c_int; -pub extern fn archive_write_set_format_gnutar(*struct_archive) c_int; pub extern fn archive_write_set_format_iso9660(*struct_archive) c_int; pub extern fn archive_write_set_format_mtree(*struct_archive) c_int; pub extern fn archive_write_set_format_mtree_classic(*struct_archive) c_int; -pub extern fn archive_write_set_format_pax(*struct_archive) c_int; -pub extern fn archive_write_set_format_pax_restricted(*struct_archive) c_int; pub extern fn archive_write_set_format_raw(*struct_archive) c_int; -pub extern fn archive_write_set_format_shar(*struct_archive) c_int; pub extern fn archive_write_set_format_shar_dump(*struct_archive) c_int; -pub extern fn archive_write_set_format_ustar(*struct_archive) c_int; pub extern fn archive_write_set_format_v7tar(*struct_archive) c_int; pub extern fn archive_write_set_format_warc(*struct_archive) c_int; pub extern fn archive_write_set_format_xar(*struct_archive) c_int; -pub extern fn archive_write_set_format_zip(*struct_archive) c_int; pub extern fn archive_write_set_format_filter_by_ext(a: *struct_archive, filename: [*c]const u8) c_int; pub extern fn archive_write_set_format_filter_by_ext_def(a: *struct_archive, filename: [*c]const u8, def_ext: [*c]const u8) c_int; pub extern fn archive_write_zip_set_compression_deflate(*struct_archive) c_int; pub extern fn archive_write_zip_set_compression_store(*struct_archive) c_int; pub extern fn archive_write_open(*struct_archive, ?*anyopaque, ?archive_open_callback, ?archive_write_callback, ?archive_close_callback) c_int; pub extern fn archive_write_open2(*struct_archive, ?*anyopaque, ?archive_open_callback, ?archive_write_callback, ?archive_close_callback, ?archive_free_callback) c_int; -pub extern fn archive_write_open_fd(*struct_archive, _fd: c_int) c_int; -pub extern fn archive_write_open_filename(*struct_archive, _file: [*c]const u8) c_int; pub extern fn archive_write_open_filename_w(*struct_archive, _file: [*c]const wchar_t) c_int; pub extern fn archive_write_open_file(*struct_archive, _file: [*c]const u8) c_int; pub extern fn archive_write_open_FILE(*struct_archive, [*c]FILE) c_int; -pub extern fn archive_write_open_memory(*struct_archive, _buffer: ?*anyopaque, _buffSize: usize, _used: [*c]usize) c_int; -pub extern fn archive_write_header(*struct_archive, *struct_archive_entry) c_int; -pub extern fn archive_write_data(*struct_archive, ?*const anyopaque, usize) la_ssize_t; pub extern fn archive_write_data_block(*struct_archive, ?*const anyopaque, usize, la_int64_t) la_ssize_t; -pub extern fn archive_write_finish_entry(*struct_archive) c_int; -pub extern fn archive_write_close(*struct_archive) c_int; pub extern fn archive_write_fail(*struct_archive) c_int; -pub extern fn archive_write_free(*struct_archive) c_int; -pub extern fn archive_write_finish(*struct_archive) c_int; pub extern fn archive_write_set_format_option(_a: *struct_archive, m: [*c]const u8, o: [*c]const u8, v: [*c]const u8) c_int; -pub extern fn archive_write_set_filter_option(_a: *struct_archive, m: [*c]const u8, o: [*c]const u8, v: [*c]const u8) c_int; pub extern fn archive_write_set_option(_a: *struct_archive, m: [*c]const u8, o: [*c]const u8, v: [*c]const u8) c_int; -pub extern fn archive_write_set_options(_a: *struct_archive, opts: [*c]const u8) c_int; pub extern fn archive_write_set_passphrase(_a: *struct_archive, p: [*c]const u8) c_int; pub extern fn archive_write_set_passphrase_callback(*struct_archive, client_data: ?*anyopaque, ?archive_passphrase_callback) c_int; pub extern fn archive_write_disk_new() *struct_archive; @@ -360,7 +555,6 @@ pub extern fn archive_read_disk_set_atime_restored(*struct_archive) c_int; pub extern fn archive_read_disk_set_behavior(*struct_archive, flags: c_int) c_int; pub extern fn archive_read_disk_set_matching(*struct_archive, _matching: *struct_archive, _excluded_func: ?*const fn (*struct_archive, ?*anyopaque, *struct_archive_entry) callconv(.C) void, _client_data: ?*anyopaque) c_int; pub extern fn archive_read_disk_set_metadata_filter_callback(*struct_archive, _metadata_filter_func: ?*const fn (*struct_archive, ?*anyopaque, *struct_archive_entry) callconv(.C) c_int, _client_data: ?*anyopaque) c_int; -pub extern fn archive_free(*struct_archive) c_int; pub extern fn archive_filter_count(*struct_archive) c_int; pub extern fn archive_filter_bytes(*struct_archive, c_int) la_int64_t; pub extern fn archive_filter_code(*struct_archive, c_int) c_int; @@ -370,7 +564,6 @@ pub extern fn archive_position_uncompressed(*struct_archive) la_int64_t; pub extern fn archive_compression_name(*struct_archive) [*c]const u8; pub extern fn archive_compression(*struct_archive) c_int; pub extern fn archive_errno(*struct_archive) c_int; -pub extern fn archive_error_string(*struct_archive) [*c]const u8; pub extern fn archive_format_name(*struct_archive) [*c]const u8; pub extern fn archive_format(*struct_archive) c_int; pub extern fn archive_clear_error(*struct_archive) void; @@ -409,11 +602,7 @@ pub extern fn archive_match_include_gname(*struct_archive, [*c]const u8) c_int; pub extern fn archive_match_include_gname_w(*struct_archive, [*c]const wchar_t) c_int; pub extern fn archive_utility_string_sort([*c][*c]u8) c_int; -pub extern fn archive_entry_clear(*struct_archive_entry) *struct_archive_entry; pub extern fn archive_entry_clone(*struct_archive_entry) *struct_archive_entry; -pub extern fn archive_entry_free(*struct_archive_entry) void; -pub extern fn archive_entry_new() *struct_archive_entry; -pub extern fn archive_entry_new2(*struct_archive) *struct_archive_entry; pub extern fn archive_entry_atime(*struct_archive_entry) time_t; pub extern fn archive_entry_atime_nsec(*struct_archive_entry) c_long; pub extern fn archive_entry_atime_is_set(*struct_archive_entry) c_int; @@ -477,7 +666,6 @@ pub extern fn archive_entry_unset_ctime(*struct_archive_entry) void; pub extern fn archive_entry_set_dev(*struct_archive_entry, dev_t) void; pub extern fn archive_entry_set_devmajor(*struct_archive_entry, dev_t) void; pub extern fn archive_entry_set_devminor(*struct_archive_entry, dev_t) void; -pub extern fn archive_entry_set_filetype(*struct_archive_entry, c_uint) void; pub extern fn archive_entry_set_fflags(*struct_archive_entry, u64, u64) void; pub extern fn archive_entry_copy_fflags_text(*struct_archive_entry, [*c]const u8) [*c]const u8; pub extern fn archive_entry_copy_fflags_text_w(*struct_archive_entry, [*c]const wchar_t) [*c]const wchar_t; @@ -499,20 +687,12 @@ pub extern fn archive_entry_set_link_utf8(*struct_archive_entry, [*c]const u8) v pub extern fn archive_entry_copy_link(*struct_archive_entry, [*c]const u8) void; pub extern fn archive_entry_copy_link_w(*struct_archive_entry, [*c]const wchar_t) void; pub extern fn archive_entry_update_link_utf8(*struct_archive_entry, [*c]const u8) c_int; -pub extern fn archive_entry_set_mode(*struct_archive_entry, mode_t) void; -pub extern fn archive_entry_set_mtime(*struct_archive_entry, time_t, c_long) void; pub extern fn archive_entry_unset_mtime(*struct_archive_entry) void; pub extern fn archive_entry_set_nlink(*struct_archive_entry, c_uint) void; -pub extern fn archive_entry_set_pathname(*struct_archive_entry, [*c]const u8) void; -pub extern fn archive_entry_set_pathname_utf8(*struct_archive_entry, [*c]const u8) void; -pub extern fn archive_entry_copy_pathname(*struct_archive_entry, [*c]const u8) void; -pub extern fn archive_entry_copy_pathname_w(*struct_archive_entry, [*c]const wchar_t) void; pub extern fn archive_entry_update_pathname_utf8(*struct_archive_entry, [*c]const u8) c_int; -pub extern fn archive_entry_set_perm(*struct_archive_entry, mode_t) void; pub extern fn archive_entry_set_rdev(*struct_archive_entry, dev_t) void; pub extern fn archive_entry_set_rdevmajor(*struct_archive_entry, dev_t) void; pub extern fn archive_entry_set_rdevminor(*struct_archive_entry, dev_t) void; -pub extern fn archive_entry_set_size(*struct_archive_entry, la_int64_t) void; pub extern fn archive_entry_unset_size(*struct_archive_entry) void; pub extern fn archive_entry_copy_sourcepath(*struct_archive_entry, [*c]const u8) void; pub extern fn archive_entry_copy_sourcepath_w(*struct_archive_entry, [*c]const wchar_t) void; diff --git a/src/libarchive/libarchive.zig b/src/libarchive/libarchive.zig index a3bc7787d15ba..5443964f95a4d 100644 --- a/src/libarchive/libarchive.zig +++ b/src/libarchive/libarchive.zig @@ -1,6 +1,6 @@ // @link "../deps/libarchive.a" -const lib = @import("./libarchive-bindings.zig"); +pub const lib = @import("./libarchive-bindings.zig"); const bun = @import("root").bun; const string = bun.string; const Output = bun.Output; @@ -740,7 +740,7 @@ pub const Archive = struct { }, else => { if (options.log) { - const archive_error = std.mem.span(lib.archive_error_string(archive)); + const archive_error = bun.sliceTo(lib.Archive.errorString(@ptrCast(archive)), 0); Output.err("libarchive error", "extracting {}: {s}", .{ bun.fmt.fmtOSPath(path_slice, .{}), archive_error, diff --git a/src/linker.lds b/src/linker.lds index 27c44da3121f8..6914b11cd4430 100644 --- a/src/linker.lds +++ b/src/linker.lds @@ -2,9 +2,15 @@ BUN_1.1 { global: napi*; node_api_*; + node_module_register; + + uv_os_getpid; + uv_os_getppid; + extern "C++" { v8::*; node::*; + JSC::CallFrame::describeFrame; }; local: *; diff --git a/src/linux_c.zig b/src/linux_c.zig index bfb2aca505821..64b46ea1787e7 100644 --- a/src/linux_c.zig +++ b/src/linux_c.zig @@ -152,11 +152,11 @@ pub const SystemErrno = enum(u8) { return @enumFromInt(code); } - pub fn label(this: SystemErrno) ?[]const u8 { + pub fn label(this: SystemErrno) ?[:0]const u8 { return labels.get(this) orelse null; } - const LabelMap = std.EnumMap(SystemErrno, []const u8); + const LabelMap = std.EnumMap(SystemErrno, [:0]const u8); pub const labels: LabelMap = brk: { var map: LabelMap = LabelMap.initFull(""); @@ -293,6 +293,75 @@ pub const SystemErrno = enum(u8) { }; }; +pub const UV_E2BIG: i32 = @intFromEnum(SystemErrno.E2BIG); +pub const UV_EACCES: i32 = @intFromEnum(SystemErrno.EACCES); +pub const UV_EADDRINUSE: i32 = @intFromEnum(SystemErrno.EADDRINUSE); +pub const UV_EADDRNOTAVAIL: i32 = @intFromEnum(SystemErrno.EADDRNOTAVAIL); +pub const UV_EAFNOSUPPORT: i32 = @intFromEnum(SystemErrno.EAFNOSUPPORT); +pub const UV_EAGAIN: i32 = @intFromEnum(SystemErrno.EAGAIN); +pub const UV_EALREADY: i32 = @intFromEnum(SystemErrno.EALREADY); +pub const UV_EBADF: i32 = @intFromEnum(SystemErrno.EBADF); +pub const UV_EBUSY: i32 = @intFromEnum(SystemErrno.EBUSY); +pub const UV_ECANCELED: i32 = @intFromEnum(SystemErrno.ECANCELED); +pub const UV_ECHARSET: i32 = -bun.windows.libuv.UV_ECHARSET; +pub const UV_ECONNABORTED: i32 = @intFromEnum(SystemErrno.ECONNABORTED); +pub const UV_ECONNREFUSED: i32 = @intFromEnum(SystemErrno.ECONNREFUSED); +pub const UV_ECONNRESET: i32 = @intFromEnum(SystemErrno.ECONNRESET); +pub const UV_EDESTADDRREQ: i32 = @intFromEnum(SystemErrno.EDESTADDRREQ); +pub const UV_EEXIST: i32 = @intFromEnum(SystemErrno.EEXIST); +pub const UV_EFAULT: i32 = @intFromEnum(SystemErrno.EFAULT); +pub const UV_EHOSTUNREACH: i32 = @intFromEnum(SystemErrno.EHOSTUNREACH); +pub const UV_EINTR: i32 = @intFromEnum(SystemErrno.EINTR); +pub const UV_EINVAL: i32 = @intFromEnum(SystemErrno.EINVAL); +pub const UV_EIO: i32 = @intFromEnum(SystemErrno.EIO); +pub const UV_EISCONN: i32 = @intFromEnum(SystemErrno.EISCONN); +pub const UV_EISDIR: i32 = @intFromEnum(SystemErrno.EISDIR); +pub const UV_ELOOP: i32 = @intFromEnum(SystemErrno.ELOOP); +pub const UV_EMFILE: i32 = @intFromEnum(SystemErrno.EMFILE); +pub const UV_EMSGSIZE: i32 = @intFromEnum(SystemErrno.EMSGSIZE); +pub const UV_ENAMETOOLONG: i32 = @intFromEnum(SystemErrno.ENAMETOOLONG); +pub const UV_ENETDOWN: i32 = @intFromEnum(SystemErrno.ENETDOWN); +pub const UV_ENETUNREACH: i32 = @intFromEnum(SystemErrno.ENETUNREACH); +pub const UV_ENFILE: i32 = @intFromEnum(SystemErrno.ENFILE); +pub const UV_ENOBUFS: i32 = @intFromEnum(SystemErrno.ENOBUFS); +pub const UV_ENODEV: i32 = @intFromEnum(SystemErrno.ENODEV); +pub const UV_ENOENT: i32 = @intFromEnum(SystemErrno.ENOENT); +pub const UV_ENOMEM: i32 = @intFromEnum(SystemErrno.ENOMEM); +pub const UV_ENONET: i32 = @intFromEnum(SystemErrno.ENONET); +pub const UV_ENOSPC: i32 = @intFromEnum(SystemErrno.ENOSPC); +pub const UV_ENOSYS: i32 = @intFromEnum(SystemErrno.ENOSYS); +pub const UV_ENOTCONN: i32 = @intFromEnum(SystemErrno.ENOTCONN); +pub const UV_ENOTDIR: i32 = @intFromEnum(SystemErrno.ENOTDIR); +pub const UV_ENOTEMPTY: i32 = @intFromEnum(SystemErrno.ENOTEMPTY); +pub const UV_ENOTSOCK: i32 = @intFromEnum(SystemErrno.ENOTSOCK); +pub const UV_ENOTSUP: i32 = @intFromEnum(SystemErrno.ENOTSUP); +pub const UV_EPERM: i32 = @intFromEnum(SystemErrno.EPERM); +pub const UV_EPIPE: i32 = @intFromEnum(SystemErrno.EPIPE); +pub const UV_EPROTO: i32 = @intFromEnum(SystemErrno.EPROTO); +pub const UV_EPROTONOSUPPORT: i32 = @intFromEnum(SystemErrno.EPROTONOSUPPORT); +pub const UV_EPROTOTYPE: i32 = @intFromEnum(SystemErrno.EPROTOTYPE); +pub const UV_EROFS: i32 = @intFromEnum(SystemErrno.EROFS); +pub const UV_ESHUTDOWN: i32 = @intFromEnum(SystemErrno.ESHUTDOWN); +pub const UV_ESPIPE: i32 = @intFromEnum(SystemErrno.ESPIPE); +pub const UV_ESRCH: i32 = @intFromEnum(SystemErrno.ESRCH); +pub const UV_ETIMEDOUT: i32 = @intFromEnum(SystemErrno.ETIMEDOUT); +pub const UV_ETXTBSY: i32 = @intFromEnum(SystemErrno.ETXTBSY); +pub const UV_EXDEV: i32 = @intFromEnum(SystemErrno.EXDEV); +pub const UV_EFBIG: i32 = @intFromEnum(SystemErrno.EFBIG); +pub const UV_ENOPROTOOPT: i32 = @intFromEnum(SystemErrno.ENOPROTOOPT); +pub const UV_ERANGE: i32 = @intFromEnum(SystemErrno.ERANGE); +pub const UV_ENXIO: i32 = @intFromEnum(SystemErrno.ENXIO); +pub const UV_EMLINK: i32 = @intFromEnum(SystemErrno.EMLINK); +pub const UV_EHOSTDOWN: i32 = @intFromEnum(SystemErrno.EHOSTDOWN); +pub const UV_EREMOTEIO: i32 = @intFromEnum(SystemErrno.EREMOTEIO); +pub const UV_ENOTTY: i32 = @intFromEnum(SystemErrno.ENOTTY); +pub const UV_EFTYPE: i32 = -bun.windows.libuv.UV_EFTYPE; +pub const UV_EILSEQ: i32 = @intFromEnum(SystemErrno.EILSEQ); +pub const UV_EOVERFLOW: i32 = @intFromEnum(SystemErrno.EOVERFLOW); +pub const UV_ESOCKTNOSUPPORT: i32 = @intFromEnum(SystemErrno.ESOCKTNOSUPPORT); +pub const UV_ENODATA: i32 = @intFromEnum(SystemErrno.ENODATA); +pub const UV_EUNATCH: i32 = @intFromEnum(SystemErrno.EUNATCH); + pub const preallocate_length = 2048 * 1024; pub fn preallocate_file(fd: std.posix.fd_t, offset: std.posix.off_t, len: std.posix.off_t) anyerror!void { // https://gist.github.com/Jarred-Sumner/b37b93399b63cbfd86e908c59a0a37df @@ -698,3 +767,7 @@ pub const RENAME_WHITEOUT = 1 << 2; pub extern "C" fn quick_exit(code: c_int) noreturn; pub extern "C" fn memrchr(ptr: [*]const u8, val: c_int, len: usize) ?[*]const u8; + +pub const netdb = @cImport({ + @cInclude("netdb.h"); +}); diff --git a/src/linux_memfd_allocator.zig b/src/linux_memfd_allocator.zig index eba502454a3d2..c2ec4f7f07cda 100644 --- a/src/linux_memfd_allocator.zig +++ b/src/linux_memfd_allocator.zig @@ -134,27 +134,15 @@ pub const LinuxMemFdAllocator = struct { unreachable; } - const rc = brk: { - var label_buf: [128]u8 = undefined; - const label = std.fmt.bufPrintZ(&label_buf, "memfd-num-{d}", .{memfd_counter.fetchAdd(1, .monotonic)}) catch ""; + var label_buf: [128]u8 = undefined; + const label = std.fmt.bufPrintZ(&label_buf, "memfd-num-{d}", .{memfd_counter.fetchAdd(1, .monotonic)}) catch ""; - // Using huge pages was slower. - const code = std.c.memfd_create(label.ptr, std.os.linux.MFD.CLOEXEC | 0); - - bun.sys.syslog("memfd_create({s}) = {d}", .{ label, code }); - break :brk code; + // Using huge pages was slower. + const fd = switch (bun.sys.memfd_create(label, std.os.linux.MFD.CLOEXEC)) { + .err => |err| return .{ .err = bun.sys.Error.fromCode(err.getErrno(), .open) }, + .result => |fd| fd, }; - switch (bun.C.getErrno(rc)) { - .SUCCESS => {}, - else => |errno| { - bun.sys.syslog("Failed to create memfd: {s}", .{@tagName(errno)}); - return .{ .err = bun.sys.Error.fromCode(errno, .open) }; - }, - } - - const fd = bun.toFD(rc); - if (bytes.len > 0) // Hint at the size of the file _ = bun.sys.ftruncate(fd, @intCast(bytes.len)); diff --git a/src/lock.zig b/src/lock.zig index 1becb95387bfe..43e9763bb344a 100644 --- a/src/lock.zig +++ b/src/lock.zig @@ -103,11 +103,7 @@ pub const Mutex = struct { }; pub const Lock = struct { - mutex: Mutex, - - pub fn init() Lock { - return Lock{ .mutex = Mutex{} }; - } + mutex: Mutex = .{}, pub inline fn lock(this: *Lock) void { this.mutex.acquire(); @@ -117,11 +113,27 @@ pub const Lock = struct { this.mutex.release(); } - pub inline fn assertUnlocked(this: *Lock, comptime message: []const u8) void { + pub inline fn releaseAssertUnlocked(this: *Lock, comptime message: []const u8) void { if (this.mutex.state.load(.monotonic) != 0) { @panic(message); } } + + pub inline fn assertUnlocked(this: *Lock) void { + if (std.debug.runtime_safety) { + if (this.mutex.state.load(.monotonic) != 0) { + @panic("Mutex is expected to be unlocked"); + } + } + } + + pub inline fn assertLocked(this: *Lock) void { + if (std.debug.runtime_safety) { + if (this.mutex.state.load(.monotonic) == 0) { + @panic("Mutex is expected to be locked"); + } + } + } }; pub fn spinCycle() void {} diff --git a/src/logger.zig b/src/logger.zig index 23c74879f11b4..0824f0e6a1d46 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -549,7 +549,7 @@ pub const Msg = struct { } } - pub fn formatNoWriter(msg: *const Msg, comptime formatterFunc: @TypeOf(Global.panic)) void { + pub fn formatNoWriter(msg: *const Msg, comptime formatterFunc: @TypeOf(Output.panic)) void { formatterFunc("\n\n{s}: {s}\n{s}\n{s}:{}:{} ({d})", .{ msg.kind.string(), msg.data.text, @@ -713,11 +713,11 @@ pub const Log = struct { pub fn toJS(this: Log, global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, fmt: string) JSC.JSValue { const msgs: []const Msg = this.msgs.items; - var errors_stack: [256]*anyopaque = undefined; + var errors_stack: [256]JSC.JSValue = undefined; const count = @as(u16, @intCast(@min(msgs.len, errors_stack.len))); switch (count) { - 0 => return JSC.JSValue.jsUndefined(), + 0 => return .undefined, 1 => { const msg = msgs[0]; return switch (msg.metadata) { @@ -728,12 +728,12 @@ pub const Log = struct { else => { for (msgs[0..count], 0..) |msg, i| { errors_stack[i] = switch (msg.metadata) { - .build => JSC.BuildMessage.create(global, allocator, msg).asVoid(), - .resolve => JSC.ResolveMessage.create(global, allocator, msg, "").asVoid(), + .build => JSC.BuildMessage.create(global, allocator, msg), + .resolve => JSC.ResolveMessage.create(global, allocator, msg, ""), }; } const out = JSC.ZigString.init(fmt); - const agg = global.createAggregateError(errors_stack[0..count].ptr, count, &out); + const agg = global.createAggregateError(errors_stack[0..count], &out); return agg; }, } @@ -1221,7 +1221,7 @@ pub const Log = struct { }; } - pub fn printForLogLevelWithEnableAnsiColors(self: *Log, to: anytype, comptime enable_ansi_colors: bool) !void { + pub fn printForLogLevelWithEnableAnsiColors(self: *const Log, to: anytype, comptime enable_ansi_colors: bool) !void { var needs_newline = false; if (self.warnings > 0 and self.errors > 0) { // Print warnings at the top diff --git a/src/main.zig b/src/main.zig index 4488bcb90038d..9c4df6072f75e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,14 +1,12 @@ const std = @import("std"); const builtin = @import("builtin"); -pub const build_options = @import("build_options"); - const bun = @import("root").bun; const Output = bun.Output; const Environment = bun.Environment; pub const panic = bun.crash_handler.panic; pub const std_options = std.Options{ - .enable_segfault_handler = !bun.crash_handler.enable, + .enable_segfault_handler = false, }; pub const io_mode = .blocking; @@ -72,3 +70,7 @@ pub const overrides = struct { } }; }; + +pub export fn Bun__panic(msg: [*]const u8, len: usize) noreturn { + Output.panic("{s}", .{msg[0..len]}); +} diff --git a/src/memory_allocator.zig b/src/memory_allocator.zig index 159cbc15d2aa7..18f7e651cce2b 100644 --- a/src/memory_allocator.zig +++ b/src/memory_allocator.zig @@ -1,7 +1,10 @@ const mem = @import("std").mem; const builtin = @import("std").builtin; const std = @import("std"); - +const bun = @import("root").bun; +const log = bun.Output.scoped(.mimalloc, true); +const assert = bun.assert; +const Allocator = mem.Allocator; const mimalloc = @import("./allocators/mimalloc.zig"); const FeatureFlags = @import("./feature_flags.zig"); const Environment = @import("./env.zig"); @@ -12,6 +15,8 @@ fn mimalloc_free( buf_align: u8, _: usize, ) void { + if (comptime Environment.enable_logs) + log("mi_free({d})", .{buf.len}); // mi_free_size internally just asserts the size // so it's faster if we don't pass that value through // but its good to have that assertion @@ -27,38 +32,12 @@ fn mimalloc_free( } } -const c = struct { - pub const malloc_size = mimalloc.mi_malloc_size; - pub const malloc_usable_size = mimalloc.mi_malloc_usable_size; - pub const malloc = struct { - pub inline fn malloc_wrapped(size: usize) ?*anyopaque { - if (comptime FeatureFlags.log_allocations) std.debug.print("Malloc: {d}\n", .{size}); - - return mimalloc.mi_malloc(size); - } - }.malloc_wrapped; - pub inline fn free(ptr: anytype) void { - if (comptime Environment.isDebug) { - assert(mimalloc.mi_is_in_heap_region(ptr)); - } - - mimalloc.mi_free(ptr); - } - pub const posix_memalign = struct { - pub inline fn mi_posix_memalign(p: [*c]?*anyopaque, alignment: usize, size: usize) c_int { - if (comptime FeatureFlags.log_allocations) std.debug.print("Posix_memalign: {d}\n", .{std.mem.alignForward(size, alignment)}); - return mimalloc.mi_posix_memalign(p, alignment, size); - } - }.mi_posix_memalign; -}; -const Allocator = mem.Allocator; -const assert = @import("root").bun.assert; const CAllocator = struct { - const malloc_size = c.malloc_size; pub const supports_posix_memalign = true; fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 { - if (comptime FeatureFlags.log_allocations) std.debug.print("Malloc: {d}\n", .{len}); + if (comptime Environment.enable_logs) + log("mi_alloc({d}, {d})", .{ len, alignment }); const ptr: ?*anyopaque = if (mimalloc.canUseAlignedAlloc(len, alignment)) mimalloc.mi_malloc_aligned(len, alignment) @@ -78,7 +57,7 @@ const CAllocator = struct { } fn alignedAllocSize(ptr: [*]u8) usize { - return CAllocator.malloc_size(ptr); + return mimalloc.mi_malloc_size(ptr); } fn alloc(_: *anyopaque, len: usize, log2_align: u8, _: usize) ?[*]u8 { @@ -117,11 +96,10 @@ const c_allocator_vtable = Allocator.VTable{ }; const ZAllocator = struct { - const malloc_size = c.malloc_size; pub const supports_posix_memalign = true; fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 { - if (comptime FeatureFlags.log_allocations) std.debug.print("Malloc: {d}\n", .{len}); + log("ZAllocator.alignedAlloc: {d}\n", .{len}); const ptr = if (mimalloc.canUseAlignedAlloc(len, alignment)) mimalloc.mi_zalloc_aligned(len, alignment) @@ -141,7 +119,7 @@ const ZAllocator = struct { } fn alignedAllocSize(ptr: [*]u8) usize { - return CAllocator.malloc_size(ptr); + return mimalloc.mi_malloc_size(ptr); } fn alloc(_: *anyopaque, len: usize, ptr_align: u8, _: usize) ?[*]u8 { diff --git a/src/mimalloc_arena.zig b/src/mimalloc_arena.zig index a32a0ce3d032b..1c2db8c09d17c 100644 --- a/src/mimalloc_arena.zig +++ b/src/mimalloc_arena.zig @@ -8,6 +8,7 @@ const FeatureFlags = @import("./feature_flags.zig"); const Allocator = mem.Allocator; const assert = bun.assert; const bun = @import("root").bun; +const log = bun.Output.scoped(.mimalloc, true); pub const GlobalArena = struct { arena: Arena, @@ -202,7 +203,7 @@ pub const Arena = struct { pub const supports_posix_memalign = true; fn alignedAlloc(heap: *mimalloc.Heap, len: usize, alignment: usize) ?[*]u8 { - if (comptime FeatureFlags.log_allocations) std.debug.print("Malloc: {d}\n", .{len}); + log("Malloc: {d}\n", .{len}); const ptr: ?*anyopaque = if (mimalloc.canUseAlignedAlloc(len, alignment)) mimalloc.mi_heap_malloc_aligned(heap, len, alignment) diff --git a/src/multi_array_list.zig b/src/multi_array_list.zig index 2fcd7453f397a..8a85525034a89 100644 --- a/src/multi_array_list.zig +++ b/src/multi_array_list.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = @import("root").bun.assert; +const bun = @import("root").bun; +const assert = bun.assert; const meta = std.meta; const mem = std.mem; const Allocator = mem.Allocator; diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 05f892a0caf93..96d1fe096da92 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -64,12 +64,60 @@ pub const Ref = opaque { extern fn napi_delete_reference_internal(ref: *Ref) void; extern fn napi_set_ref(ref: *Ref, value: JSC.JSValue) void; }; -pub const napi_handle_scope = napi_env; -pub const napi_escapable_handle_scope = napi_env; +pub const NapiHandleScope = opaque { + pub extern fn NapiHandleScope__push(globalObject: *JSC.JSGlobalObject, escapable: bool) ?*NapiHandleScope; + pub extern fn NapiHandleScope__pop(globalObject: *JSC.JSGlobalObject, current: ?*NapiHandleScope) void; + extern fn NapiHandleScope__append(globalObject: *JSC.JSGlobalObject, value: JSC.JSValueReprInt) void; + extern fn NapiHandleScope__escape(handleScope: *NapiHandleScope, value: JSC.JSValueReprInt) bool; + + pub fn push(env: napi_env, escapable: bool) ?*NapiHandleScope { + return NapiHandleScope__push(env, escapable); + } + + pub fn pop(self: ?*NapiHandleScope, env: napi_env) void { + NapiHandleScope__pop(env, self); + } + + pub fn append(env: napi_env, value: JSC.JSValue) void { + NapiHandleScope__append(env, @intFromEnum(value)); + } + + pub fn escape(self: *NapiHandleScope, value: JSC.JSValue) error{EscapeCalledTwice}!void { + if (!NapiHandleScope__escape(self, @intFromEnum(value))) { + return error.EscapeCalledTwice; + } + } +}; + +pub const napi_handle_scope = ?*NapiHandleScope; +pub const napi_escapable_handle_scope = ?*NapiHandleScope; pub const napi_callback_info = *JSC.CallFrame; pub const napi_deferred = *JSC.JSPromise.Strong; -pub const napi_value = JSC.JSValue; +/// To ensure napi_values are not collected prematurely after being returned into a native module, +/// you must use these functions rather than convert between napi_value and JSC.JSValue directly +pub const napi_value = enum(JSC.JSValueReprInt) { + _, + + pub fn set( + self: *napi_value, + env: napi_env, + val: JSC.JSValue, + ) void { + NapiHandleScope.append(env, val); + self.* = @enumFromInt(@intFromEnum(val)); + } + + pub fn get(self: *const napi_value) JSC.JSValue { + return @enumFromInt(@intFromEnum(self.*)); + } + + pub fn create(env: napi_env, val: JSC.JSValue) napi_value { + NapiHandleScope.append(env, val); + return @enumFromInt(@intFromEnum(val)); + } +}; + pub const struct_napi_escapable_handle_scope__ = opaque {}; const char16_t = u16; @@ -205,29 +253,29 @@ pub const napi_type_tag = extern struct { upper: u64, }; pub extern fn napi_get_last_error_info(env: napi_env, result: [*c][*c]const napi_extended_error_info) napi_status; -pub export fn napi_get_undefined(_: napi_env, result_: ?*napi_value) napi_status { +pub export fn napi_get_undefined(env: napi_env, result_: ?*napi_value) napi_status { log("napi_get_undefined", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsUndefined(); + result.set(env, JSValue.jsUndefined()); return .ok; } -pub export fn napi_get_null(_: napi_env, result_: ?*napi_value) napi_status { +pub export fn napi_get_null(env: napi_env, result_: ?*napi_value) napi_status { log("napi_get_null", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsNull(); + result.set(env, JSValue.jsNull()); return .ok; } pub extern fn napi_get_global(env: napi_env, result: *napi_value) napi_status; -pub export fn napi_get_boolean(_: napi_env, value: bool, result_: ?*napi_value) napi_status { +pub export fn napi_get_boolean(env: napi_env, value: bool, result_: ?*napi_value) napi_status { log("napi_get_boolean", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsBoolean(value); + result.set(env, JSValue.jsBoolean(value)); return .ok; } pub export fn napi_create_array(env: napi_env, result_: ?*napi_value) napi_status { @@ -235,7 +283,7 @@ pub export fn napi_create_array(env: napi_env, result_: ?*napi_value) napi_statu const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.createEmptyArray(env, 0); + result.set(env, JSValue.createEmptyArray(env, 0)); return .ok; } const prefilled_undefined_args_array: [128]JSC.JSValue = brk: { @@ -262,38 +310,34 @@ pub export fn napi_create_array_with_length(env: napi_env, length: usize, result } array.ensureStillAlive(); - result.* = array; + result.set(env, array); return .ok; } pub extern fn napi_create_double(_: napi_env, value: f64, result: *napi_value) napi_status; -pub export fn napi_create_int32(_: napi_env, value: i32, result_: ?*napi_value) napi_status { +pub export fn napi_create_int32(env: napi_env, value: i32, result_: ?*napi_value) napi_status { log("napi_create_int32", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsNumber(value); + result.set(env, JSValue.jsNumber(value)); return .ok; } -pub export fn napi_create_uint32(_: napi_env, value: u32, result_: ?*napi_value) napi_status { +pub export fn napi_create_uint32(env: napi_env, value: u32, result_: ?*napi_value) napi_status { log("napi_create_uint32", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsNumber(value); + result.set(env, JSValue.jsNumber(value)); return .ok; } -pub export fn napi_create_int64(_: napi_env, value: i64, result_: ?*napi_value) napi_status { +pub export fn napi_create_int64(env: napi_env, value: i64, result_: ?*napi_value) napi_status { log("napi_create_int64", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsNumber(value); + result.set(env, JSValue.jsNumber(value)); return .ok; } -inline fn setNapiValue(result: *napi_value, value: JSValue) void { - value.ensureStillAlive(); - result.* = value; -} pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status { const result: *napi_value = result_ orelse { return invalidArg(); @@ -315,7 +359,7 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length log("napi_create_string_latin1: {s}", .{slice}); if (slice.len == 0) { - setNapiValue(result, bun.String.empty.toJS(env)); + result.set(env, bun.String.empty.toJS(env)); return .ok; } @@ -324,7 +368,7 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length @memcpy(bytes, slice); - setNapiValue(result, string.toJS(env)); + result.set(env, string.toJS(env)); return .ok; } pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status { @@ -352,7 +396,7 @@ pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length: } defer string.deref(); - setNapiValue(result, string.toJS(env)); + result.set(env, string.toJS(env)); return .ok; } pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, length: usize, result_: ?*napi_value) napi_status { @@ -377,7 +421,7 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l log("napi_create_string_utf16: {d} {any}", .{ slice.len, bun.fmt.FormatUTF16{ .buf = slice[0..@min(slice.len, 512)] } }); if (slice.len == 0) { - setNapiValue(result, bun.String.empty.toJS(env)); + result.set(env, bun.String.empty.toJS(env)); } var string, const chars = bun.String.createUninitialized(.utf16, slice.len); @@ -385,7 +429,7 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l @memcpy(chars, slice); - setNapiValue(result, string.toJS(env)); + result.set(env, string.toJS(env)); return .ok; } pub extern fn napi_create_symbol(env: napi_env, description: napi_value, result: *napi_value) napi_status; @@ -394,44 +438,48 @@ pub extern fn napi_create_type_error(env: napi_env, code: napi_value, msg: napi_ pub extern fn napi_create_range_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; pub extern fn napi_typeof(env: napi_env, value: napi_value, result: *napi_valuetype) napi_status; pub extern fn napi_get_value_double(env: napi_env, value: napi_value, result: *f64) napi_status; -pub export fn napi_get_value_int32(_: napi_env, value: napi_value, result_: ?*i32) napi_status { +pub export fn napi_get_value_int32(_: napi_env, value_: napi_value, result_: ?*i32) napi_status { log("napi_get_value_int32", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); if (!value.isNumber()) { return .number_expected; } result.* = value.to(i32); return .ok; } -pub export fn napi_get_value_uint32(_: napi_env, value: napi_value, result_: ?*u32) napi_status { +pub export fn napi_get_value_uint32(_: napi_env, value_: napi_value, result_: ?*u32) napi_status { log("napi_get_value_uint32", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); if (!value.isNumber()) { return .number_expected; } result.* = value.to(u32); return .ok; } -pub export fn napi_get_value_int64(_: napi_env, value: napi_value, result_: ?*i64) napi_status { +pub export fn napi_get_value_int64(_: napi_env, value_: napi_value, result_: ?*i64) napi_status { log("napi_get_value_int64", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); if (!value.isNumber()) { return .number_expected; } result.* = value.to(i64); return .ok; } -pub export fn napi_get_value_bool(_: napi_env, value: napi_value, result_: ?*bool) napi_status { +pub export fn napi_get_value_bool(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_get_value_bool", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = value.to(bool); return .ok; @@ -441,8 +489,9 @@ inline fn maybeAppendNull(ptr: anytype, doit: bool) void { ptr.* = 0; } } -pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf_ptr_: ?[*:0]c_char, bufsize: usize, result_ptr: ?*usize) napi_status { +pub export fn napi_get_value_string_latin1(env: napi_env, value_: napi_value, buf_ptr_: ?[*:0]c_char, bufsize: usize, result_ptr: ?*usize) napi_status { log("napi_get_value_string_latin1", .{}); + const value = value_.get(); defer value.ensureStillAlive(); const buf_ptr = @as(?[*:0]u8, @ptrCast(buf_ptr_)); @@ -498,8 +547,9 @@ pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf /// via the result parameter. /// The result argument is optional unless buf is NULL. pub extern fn napi_get_value_string_utf8(env: napi_env, value: napi_value, buf_ptr: [*c]u8, bufsize: usize, result_ptr: ?*usize) napi_status; -pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_ptr: ?[*]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status { +pub export fn napi_get_value_string_utf16(env: napi_env, value_: napi_value, buf_ptr: ?[*]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status { log("napi_get_value_string_utf16", .{}); + const value = value_.get(); defer value.ensureStillAlive(); const str = value.toBunString(env); defer str.deref(); @@ -546,40 +596,44 @@ pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_ return .ok; } -pub export fn napi_coerce_to_bool(env: napi_env, value: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_coerce_to_bool(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { log("napi_coerce_to_bool", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.jsBoolean(value.coerce(bool, env)); + const value = value_.get(); + result.set(env, JSValue.jsBoolean(value.coerce(bool, env))); return .ok; } -pub export fn napi_coerce_to_number(env: napi_env, value: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_coerce_to_number(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { log("napi_coerce_to_number", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSC.JSValue.jsNumber(JSC.C.JSValueToNumber(env.ref(), value.asObjectRef(), TODO_EXCEPTION)); + const value = value_.get(); + result.set(env, JSC.JSValue.jsNumber(JSC.C.JSValueToNumber(env.ref(), value.asObjectRef(), TODO_EXCEPTION))); return .ok; } -pub export fn napi_coerce_to_object(env: napi_env, value: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_coerce_to_object(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { log("napi_coerce_to_object", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.c(JSC.C.JSValueToObject(env.ref(), value.asObjectRef(), TODO_EXCEPTION)); + const value = value_.get(); + result.set(env, JSValue.c(JSC.C.JSValueToObject(env.ref(), value.asObjectRef(), TODO_EXCEPTION))); return .ok; } -pub export fn napi_get_prototype(env: napi_env, object: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_get_prototype(env: napi_env, object_: napi_value, result_: ?*napi_value) napi_status { log("napi_get_prototype", .{}); const result = result_ orelse { return invalidArg(); }; + const object = object_.get(); if (!object.isObject()) { return .object_expected; } - result.* = JSValue.c(JSC.C.JSObjectGetPrototype(env.ref(), object.asObjectRef())); + result.set(env, JSValue.c(JSC.C.JSObjectGetPrototype(env.ref(), object.asObjectRef()))); return .ok; } // TODO: bind JSC::ownKeys @@ -591,8 +645,10 @@ pub export fn napi_get_prototype(env: napi_env, object: napi_value, result_: ?*n // result.* = // } -pub export fn napi_set_element(env: napi_env, object: napi_value, index: c_uint, value: napi_value) napi_status { +pub export fn napi_set_element(env: napi_env, object_: napi_value, index: c_uint, value_: napi_value) napi_status { log("napi_set_element", .{}); + const object = object_.get(); + const value = value_.get(); if (!object.jsType().isIndexable()) { return .array_expected; } @@ -601,11 +657,12 @@ pub export fn napi_set_element(env: napi_env, object: napi_value, index: c_uint, JSC.C.JSObjectSetPropertyAtIndex(env.ref(), object.asObjectRef(), index, value.asObjectRef(), TODO_EXCEPTION); return .ok; } -pub export fn napi_has_element(env: napi_env, object: napi_value, index: c_uint, result_: ?*bool) napi_status { +pub export fn napi_has_element(env: napi_env, object_: napi_value, index: c_uint, result_: ?*bool) napi_status { log("napi_has_element", .{}); const result = result_ orelse { return invalidArg(); }; + const object = object_.get(); if (!object.jsType().isIndexable()) { return .array_expected; @@ -617,19 +674,21 @@ pub export fn napi_has_element(env: napi_env, object: napi_value, index: c_uint, pub extern fn napi_get_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status; pub extern fn napi_delete_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status; pub extern fn napi_define_properties(env: napi_env, object: napi_value, property_count: usize, properties: [*c]const napi_property_descriptor) napi_status; -pub export fn napi_is_array(_: napi_env, value: napi_value, result_: ?*bool) napi_status { +pub export fn napi_is_array(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_array", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = value.jsType().isArray(); return .ok; } -pub export fn napi_get_array_length(env: napi_env, value: napi_value, result_: [*c]u32) napi_status { +pub export fn napi_get_array_length(env: napi_env, value_: napi_value, result_: [*c]u32) napi_status { log("napi_get_array_length", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); if (!value.jsType().isArray()) { return .array_expected; @@ -638,52 +697,24 @@ pub export fn napi_get_array_length(env: napi_env, value: napi_value, result_: [ result.* = @as(u32, @truncate(value.getLength(env))); return .ok; } -pub export fn napi_strict_equals(env: napi_env, lhs: napi_value, rhs: napi_value, result_: ?*bool) napi_status { +pub export fn napi_strict_equals(env: napi_env, lhs_: napi_value, rhs_: napi_value, result_: ?*bool) napi_status { log("napi_strict_equals", .{}); const result = result_ orelse { return invalidArg(); }; + const lhs, const rhs = .{ lhs_.get(), rhs_.get() }; // there is some nuance with NaN here i'm not sure about result.* = lhs.isSameValue(rhs, env); return .ok; } pub extern fn napi_call_function(env: napi_env, recv: napi_value, func: napi_value, argc: usize, argv: [*c]const napi_value, result: *napi_value) napi_status; -pub export fn napi_new_instance(env: napi_env, constructor: napi_value, argc: usize, argv: [*c]const napi_value, result_: ?*napi_value) napi_status { - log("napi_new_instance", .{}); - JSC.markBinding(@src()); - - if (argc > 0 and argv == null) { - return invalidArg(); - } - - const result = result_ orelse { - return invalidArg(); - }; - - var exception = [_]JSC.C.JSValueRef{null}; - result.* = JSValue.c( - JSC.C.JSObjectCallAsConstructor( - env.ref(), - constructor.asObjectRef(), - argc, - if (argv != null) - @as([*]const JSC.C.JSValueRef, @ptrCast(argv)) - else - null, - &exception, - ), - ); - if (exception[0] != null) { - return genericFailure(); - } - - return .ok; -} -pub export fn napi_instanceof(env: napi_env, object: napi_value, constructor: napi_value, result_: ?*bool) napi_status { +pub extern fn napi_new_instance(env: napi_env, constructor: napi_value, argc: usize, argv: [*c]const napi_value, result_: ?*napi_value) napi_status; +pub export fn napi_instanceof(env: napi_env, object_: napi_value, constructor_: napi_value, result_: ?*bool) napi_status { log("napi_instanceof", .{}); const result = result_ orelse { return invalidArg(); }; + const object, const constructor = .{ object_.get(), constructor_.get() }; // TODO: does this throw object_expected in node? result.* = object.isObject() and object.isInstanceOf(env, constructor); return .ok; @@ -713,20 +744,21 @@ pub extern fn napi_reference_unref(env: napi_env, ref: *Ref, result: [*c]u32) na pub extern fn napi_get_reference_value(env: napi_env, ref: *Ref, result: *napi_value) napi_status; pub extern fn napi_get_reference_value_internal(ref: *Ref) JSC.JSValue; -// JSC scans the stack -// we don't need this pub export fn napi_open_handle_scope(env: napi_env, result_: ?*napi_handle_scope) napi_status { log("napi_open_handle_scope", .{}); const result = result_ orelse { return invalidArg(); }; - result.* = env; + result.* = NapiHandleScope.push(env, false); return .ok; } -// JSC scans the stack -// we don't need this -pub export fn napi_close_handle_scope(_: napi_env, _: napi_handle_scope) napi_status { + +pub export fn napi_close_handle_scope(env: napi_env, handle_scope: napi_handle_scope) napi_status { log("napi_close_handle_scope", .{}); + if (handle_scope) |scope| { + scope.pop(env); + } + return .ok; } @@ -744,26 +776,28 @@ pub export fn napi_async_destroy(_: napi_env, _: *anyopaque) napi_status { } // this is just a regular function call -pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv: napi_value, func: napi_value, arg_count: usize, args: ?[*]const napi_value, result: ?*napi_value) napi_status { +pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv_: napi_value, func_: napi_value, arg_count: usize, args: ?[*]const napi_value, maybe_result: ?*napi_value) napi_status { log("napi_make_callback", .{}); + const recv, const func = .{ recv_.get(), func_.get() }; if (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.vm())) { return .function_expected; } - const res = func.callWithThis( + const res = func.call( env, if (recv != .zero) recv else - JSC.JSValue.jsUndefined(), + .undefined, if (arg_count > 0 and args != null) @as([*]const JSC.JSValue, @ptrCast(args.?))[0..arg_count] else &.{}, - ); + ) catch |err| // TODO: handle errors correctly + env.takeException(err); - if (result) |result_| { - result_.* = res; + if (maybe_result) |result| { + result.set(env, res); } // TODO: this is likely incorrect @@ -791,26 +825,31 @@ fn notImplementedYet(comptime name: []const u8) void { ); } -// JSC stack scanning will handle this -pub export fn napi_open_escapable_handle_scope(env: napi_env, handle_: ?*napi_escapable_handle_scope) napi_status { +pub export fn napi_open_escapable_handle_scope(env: napi_env, result_: ?*napi_escapable_handle_scope) napi_status { log("napi_open_escapable_handle_scope", .{}); - const handle = handle_ orelse { + const result = result_ orelse { return invalidArg(); }; - handle.* = env; + result.* = NapiHandleScope.push(env, true); return .ok; } -pub export fn napi_close_escapable_handle_scope(_: napi_env, _: napi_escapable_handle_scope) napi_status { +pub export fn napi_close_escapable_handle_scope(env: napi_env, scope: napi_escapable_handle_scope) napi_status { log("napi_close_escapable_handle_scope", .{}); + if (scope) |s| { + s.pop(env); + } return .ok; } -pub export fn napi_escape_handle(_: napi_env, _: napi_escapable_handle_scope, value: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_escape_handle(_: napi_env, scope_: napi_escapable_handle_scope, escapee: napi_value, result_: ?*napi_value) napi_status { log("napi_escape_handle", .{}); const result = result_ orelse { return invalidArg(); }; - value.ensureStillAlive(); - result.* = value; + const scope = scope_ orelse { + return invalidArg(); + }; + scope.escape(escapee.get()) catch return .escape_called_twice; + result.* = escapee; return .ok; } pub export fn napi_type_tag_object(_: napi_env, _: napi_value, _: [*c]const napi_type_tag) napi_status { @@ -837,18 +876,20 @@ pub extern fn napi_throw(env: napi_env, @"error": napi_value) napi_status; pub extern fn napi_throw_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; pub extern fn napi_throw_type_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; pub extern fn napi_throw_range_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; -pub export fn napi_is_error(_: napi_env, value: napi_value, result: *bool) napi_status { +pub export fn napi_is_error(_: napi_env, value_: napi_value, result: *bool) napi_status { log("napi_is_error", .{}); + const value = value_.get(); result.* = value.isAnyError(); return .ok; } pub extern fn napi_is_exception_pending(env: napi_env, result: *bool) napi_status; pub extern fn napi_get_and_clear_last_exception(env: napi_env, result: *napi_value) napi_status; -pub export fn napi_is_arraybuffer(_: napi_env, value: napi_value, result_: ?*bool) napi_status { +pub export fn napi_is_arraybuffer(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_arraybuffer", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = !value.isNumber() and value.jsTypeLoose() == .ArrayBuffer; return .ok; } @@ -856,8 +897,9 @@ pub extern fn napi_create_arraybuffer(env: napi_env, byte_length: usize, data: [ pub extern fn napi_create_external_arraybuffer(env: napi_env, external_data: ?*anyopaque, byte_length: usize, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; -pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer: napi_value, data: ?*[*]u8, byte_length: ?*usize) napi_status { +pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer_: napi_value, data: ?*[*]u8, byte_length: ?*usize) napi_status { log("napi_get_arraybuffer_info", .{}); + const arraybuffer = arraybuffer_.get(); const array_buffer = arraybuffer.asArrayBuffer(env) orelse return .arraybuffer_expected; const slice = array_buffer.slice(); if (data) |dat| @@ -866,18 +908,20 @@ pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer: napi_value, len.* = slice.len; return .ok; } -pub export fn napi_is_typedarray(_: napi_env, value: napi_value, result: ?*bool) napi_status { +pub export fn napi_is_typedarray(_: napi_env, value_: napi_value, result: ?*bool) napi_status { log("napi_is_typedarray", .{}); + const value = value_.get(); if (result != null) result.?.* = value.jsTypeLoose().isTypedArray(); return if (result != null) .ok else invalidArg(); } -pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_type, length: usize, arraybuffer: napi_value, byte_offset: usize, result_: ?*napi_value) napi_status { +pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_type, length: usize, arraybuffer_: napi_value, byte_offset: usize, result_: ?*napi_value) napi_status { log("napi_create_typedarray", .{}); + const arraybuffer = arraybuffer_.get(); const result = result_ orelse { return invalidArg(); }; - result.* = JSValue.c( + result.set(env, JSValue.c( JSC.C.JSObjectMakeTypedArrayWithArrayBufferAndOffset( env.ref(), @"type".toC(), @@ -886,64 +930,74 @@ pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_typ length, TODO_EXCEPTION, ), - ); + )); return .ok; } pub export fn napi_get_typedarray_info( env: napi_env, - typedarray: napi_value, - @"type": ?*napi_typedarray_type, - length: ?*usize, - data: ?*[*]u8, - arraybuffer: ?*napi_value, - byte_offset: ?*usize, + typedarray_: napi_value, + maybe_type: ?*napi_typedarray_type, + maybe_length: ?*usize, + maybe_data: ?*[*]u8, + maybe_arraybuffer: ?*napi_value, + maybe_byte_offset: ?*usize, ) napi_status { log("napi_get_typedarray_info", .{}); + const typedarray = typedarray_.get(); if (typedarray.isEmptyOrUndefinedOrNull()) return invalidArg(); defer typedarray.ensureStillAlive(); const array_buffer = typedarray.asArrayBuffer(env) orelse return invalidArg(); - if (@"type" != null) - @"type".?.* = napi_typedarray_type.fromJSType(array_buffer.typed_array_type) orelse return invalidArg(); + if (maybe_type) |@"type"| + @"type".* = napi_typedarray_type.fromJSType(array_buffer.typed_array_type) orelse return invalidArg(); // TODO: handle detached - if (data != null) - data.?.* = array_buffer.ptr; + if (maybe_data) |data| + data.* = array_buffer.ptr; - if (length != null) - length.?.* = array_buffer.len; + if (maybe_length) |length| + length.* = array_buffer.len; - if (arraybuffer != null) - arraybuffer.?.* = JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), typedarray.asObjectRef(), null)); + if (maybe_arraybuffer) |arraybuffer| + arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), typedarray.asObjectRef(), null))); - if (byte_offset != null) - byte_offset.?.* = array_buffer.offset; + if (maybe_byte_offset) |byte_offset| + byte_offset.* = array_buffer.offset; return .ok; } pub extern fn napi_create_dataview(env: napi_env, length: usize, arraybuffer: napi_value, byte_offset: usize, result: *napi_value) napi_status; -pub export fn napi_is_dataview(_: napi_env, value: napi_value, result_: ?*bool) napi_status { +pub export fn napi_is_dataview(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_dataview", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = !value.isEmptyOrUndefinedOrNull() and value.jsTypeLoose() == .DataView; return .ok; } -pub export fn napi_get_dataview_info(env: napi_env, dataview: napi_value, bytelength: ?*usize, data: ?*[*]u8, arraybuffer: ?*napi_value, byte_offset: ?*usize) napi_status { +pub export fn napi_get_dataview_info( + env: napi_env, + dataview_: napi_value, + maybe_bytelength: ?*usize, + maybe_data: ?*[*]u8, + maybe_arraybuffer: ?*napi_value, + maybe_byte_offset: ?*usize, +) napi_status { log("napi_get_dataview_info", .{}); + const dataview = dataview_.get(); const array_buffer = dataview.asArrayBuffer(env) orelse return .object_expected; - if (bytelength != null) - bytelength.?.* = array_buffer.byte_len; + if (maybe_bytelength) |bytelength| + bytelength.* = array_buffer.byte_len; - if (data != null) - data.?.* = array_buffer.ptr; + if (maybe_data) |data| + data.* = array_buffer.ptr; - if (arraybuffer != null) - arraybuffer.?.* = JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), dataview.asObjectRef(), null)); + if (maybe_arraybuffer) |arraybuffer| + arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), dataview.asObjectRef(), null))); - if (byte_offset != null) - byte_offset.?.* = array_buffer.offset; + if (maybe_byte_offset) |byte_offset| + byte_offset.* = array_buffer.offset; return .ok; } @@ -965,34 +1019,36 @@ pub export fn napi_create_promise(env: napi_env, deferred_: ?*napi_deferred, pro }; deferred.* = bun.default_allocator.create(JSC.JSPromise.Strong) catch @panic("failed to allocate napi_deferred"); deferred.*.* = JSC.JSPromise.Strong.init(env); - promise.* = deferred.*.get().asValue(env); + promise.set(env, deferred.*.get().asValue(env)); return .ok; } -pub export fn napi_resolve_deferred(env: napi_env, deferred: napi_deferred, resolution: napi_value) napi_status { +pub export fn napi_resolve_deferred(env: napi_env, deferred: napi_deferred, resolution_: napi_value) napi_status { log("napi_resolve_deferred", .{}); + const resolution = resolution_.get(); var prom = deferred.get(); prom.resolve(env, resolution); - deferred.*.strong.deinit(); + deferred.deinit(); bun.default_allocator.destroy(deferred); return .ok; } -pub export fn napi_reject_deferred(env: napi_env, deferred: napi_deferred, rejection: napi_value) napi_status { +pub export fn napi_reject_deferred(env: napi_env, deferred: napi_deferred, rejection_: napi_value) napi_status { log("napi_reject_deferred", .{}); + const rejection = rejection_.get(); var prom = deferred.get(); prom.reject(env, rejection); - deferred.*.strong.deinit(); + deferred.deinit(); bun.default_allocator.destroy(deferred); return .ok; } -pub export fn napi_is_promise(_: napi_env, value: napi_value, is_promise_: ?*bool) napi_status { +pub export fn napi_is_promise(_: napi_env, value_: napi_value, is_promise_: ?*bool) napi_status { log("napi_is_promise", .{}); + const value = value_.get(); const is_promise = is_promise_ orelse { return invalidArg(); }; - if (value.isEmptyOrUndefinedOrNull()) { - is_promise.* = false; - return .ok; + if (value.isEmpty()) { + return invalidArg(); } is_promise.* = value.asAnyPromise() != null; @@ -1006,14 +1062,15 @@ pub export fn napi_create_date(env: napi_env, time: f64, result_: ?*napi_value) return invalidArg(); }; var args = [_]JSC.C.JSValueRef{JSC.JSValue.jsNumber(time).asObjectRef()}; - result.* = JSValue.c(JSC.C.JSObjectMakeDate(env.ref(), 1, &args, TODO_EXCEPTION)); + result.set(env, JSValue.c(JSC.C.JSObjectMakeDate(env.ref(), 1, &args, TODO_EXCEPTION))); return .ok; } -pub export fn napi_is_date(_: napi_env, value: napi_value, is_date_: ?*bool) napi_status { +pub export fn napi_is_date(_: napi_env, value_: napi_value, is_date_: ?*bool) napi_status { log("napi_is_date", .{}); const is_date = is_date_ orelse { return invalidArg(); }; + const value = value_.get(); is_date.* = value.jsTypeLoose() == .JSDate; return .ok; } @@ -1024,7 +1081,7 @@ pub export fn napi_create_bigint_int64(env: napi_env, value: i64, result_: ?*nap const result = result_ orelse { return invalidArg(); }; - result.* = JSC.JSValue.fromInt64NoTruncate(env, value); + result.set(env, JSC.JSValue.fromInt64NoTruncate(env, value)); return .ok; } pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*napi_value) napi_status { @@ -1032,25 +1089,27 @@ pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*na const result = result_ orelse { return invalidArg(); }; - result.* = JSC.JSValue.fromUInt64NoTruncate(env, value); + result.set(env, JSC.JSValue.fromUInt64NoTruncate(env, value)); return .ok; } pub extern fn napi_create_bigint_words(env: napi_env, sign_bit: c_int, word_count: usize, words: [*c]const u64, result: *napi_value) napi_status; // TODO: lossless -pub export fn napi_get_value_bigint_int64(_: napi_env, value: napi_value, result_: ?*i64, _: *bool) napi_status { +pub export fn napi_get_value_bigint_int64(_: napi_env, value_: napi_value, result_: ?*i64, _: *bool) napi_status { log("napi_get_value_bigint_int64", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = value.toInt64(); return .ok; } // TODO: lossless -pub export fn napi_get_value_bigint_uint64(_: napi_env, value: napi_value, result_: ?*u64, _: *bool) napi_status { +pub export fn napi_get_value_bigint_uint64(_: napi_env, value_: napi_value, result_: ?*u64, _: *bool) napi_status { log("napi_get_value_bigint_uint64", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = value.toUInt64NoTruncate(); return .ok; } @@ -1144,6 +1203,8 @@ pub const napi_async_work = struct { } pub fn runFromJS(this: *napi_async_work) void { + const handle_scope = NapiHandleScope.push(this.global, false); + defer if (handle_scope) |scope| scope.pop(this.global); this.complete.?( this.global, if (this.status.load(.seq_cst) == @intFromEnum(Status.cancelled)) @@ -1169,13 +1230,15 @@ pub const napi_node_version = extern struct { major: u32, minor: u32, patch: u32, - release: [*c]const u8, + release: [*:0]const u8, + + const parsed_nodejs_version = std.SemanticVersion.parse(bun.Environment.reported_nodejs_version) catch @panic("Invalid reported Node.js version"); pub const global: napi_node_version = .{ - .major = 17, - .minor = 7, - .patch = 17, - .release = "Bun!!!", + .major = parsed_nodejs_version.major, + .minor = parsed_nodejs_version.minor, + .patch = parsed_nodejs_version.patch, + .release = "node", }; }; pub const struct_napi_async_cleanup_hook_handle__ = opaque {}; @@ -1212,10 +1275,10 @@ pub export fn napi_fatal_error(location_ptr: ?[*:0]const u8, location_len: usize const location = napiSpan(location_ptr, location_len); if (location.len > 0) { - bun.Global.panic("napi: {s}\n {s}", .{ message, location }); + bun.Output.panic("napi: {s}\n {s}", .{ message, location }); } - bun.Global.panic("napi: {s}", .{message}); + bun.Output.panic("napi: {s}", .{message}); } pub export fn napi_create_buffer(env: napi_env, length: usize, data: ?**anyopaque, result: *napi_value) napi_status { log("napi_create_buffer: {d}", .{length}); @@ -1225,7 +1288,7 @@ pub export fn napi_create_buffer(env: napi_env, length: usize, data: ?**anyopaqu ptr.* = buffer.asArrayBuffer(env).?.ptr; } } - result.* = buffer; + result.set(env, buffer); return .ok; } pub extern fn napi_create_external_buffer(env: napi_env, length: usize, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; @@ -1244,20 +1307,22 @@ pub export fn napi_create_buffer_copy(env: napi_env, length: usize, data: [*]u8, } } - result.* = buffer; + result.set(env, buffer); return .ok; } -pub export fn napi_is_buffer(env: napi_env, value: napi_value, result_: ?*bool) napi_status { +pub export fn napi_is_buffer(env: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_buffer", .{}); const result = result_ orelse { return invalidArg(); }; + const value = value_.get(); result.* = value.isBuffer(env); return .ok; } -pub export fn napi_get_buffer_info(env: napi_env, value: napi_value, data: ?*[*]u8, length: ?*usize) napi_status { +pub export fn napi_get_buffer_info(env: napi_env, value_: napi_value, data: ?*[*]u8, length: ?*usize) napi_status { log("napi_get_buffer_info", .{}); + const value = value_.get(); const array_buf = value.asArrayBuffer(env) orelse { // TODO: is invalid_arg what to return here? return .arraybuffer_expected; @@ -1334,13 +1399,16 @@ pub export fn napi_get_node_version(_: napi_env, version_: ?**const napi_node_ve version.* = &napi_node_version.global; return .ok; } -pub export fn napi_get_uv_event_loop(env: napi_env, loop_: ?**JSC.EventLoop) napi_status { +const napi_event_loop = if (bun.Environment.isWindows) *bun.windows.libuv.Loop else *JSC.EventLoop; +pub export fn napi_get_uv_event_loop(env: napi_env, loop_: ?*napi_event_loop) napi_status { log("napi_get_uv_event_loop", .{}); const loop = loop_ orelse { return invalidArg(); }; if (bun.Environment.isWindows) { - loop.* = @ptrCast(@alignCast(env.bunVM().uvLoop())); + // alignment error is incorrect. + @setRuntimeSafety(false); + loop.* = JSC.VirtualMachine.get().uvLoop(); } else { // there is no uv event loop on posix, we use our event loop handle. loop.* = env.bunVM().eventLoop(); @@ -1361,28 +1429,24 @@ pub export fn napi_add_env_cleanup_hook(env: napi_env, fun: ?*const fn (?*anyopa } pub export fn napi_remove_env_cleanup_hook(env: napi_env, fun: ?*const fn (?*anyopaque) callconv(.C) void, arg: ?*anyopaque) napi_status { log("napi_remove_env_cleanup_hook", .{}); - if (env.bunVM().rare_data == null or fun == null) - return .ok; - var rare_data = env.bunVM().rare_data.?; - var hook = rare_data.cleanup_hook orelse return .ok; - const cmp = JSC.RareData.CleanupHook.from(env, arg, fun.?); - if (hook.eql(cmp)) { - env.bunVM().allocator.destroy(hook); - rare_data.cleanup_hook = null; - rare_data.tail_cleanup_hook = null; + // Avoid looking up env.bunVM(). + if (bun.Global.isExiting()) { + return .ok; } - while (hook.next) |current| { + + const vm = JSC.VirtualMachine.get(); + + if (vm.rare_data == null or fun == null or vm.isShuttingDown()) + return .ok; + + var rare_data = vm.rare_data.?; + const cmp = JSC.RareData.CleanupHook.init(env, arg, fun.?); + for (rare_data.cleanup_hooks.items, 0..) |*hook, i| { if (hook.eql(cmp)) { - if (current.next) |next| { - hook.next = next; - } else { - hook.next = null; - } - env.bunVM().allocator.destroy(current); - return .ok; + _ = rare_data.cleanup_hooks.orderedRemove(i); + break; } - hook = current; } return .ok; @@ -1415,12 +1479,12 @@ pub const ThreadSafeFunction = struct { poll_ref: Async.KeepAlive, thread_count: usize = 0, - owning_thread_lock: Lock = Lock.init(), + owning_thread_lock: Lock = .{}, event_loop: *JSC.EventLoop, + tracker: JSC.AsyncTaskTracker, env: napi_env, - finalizer_task: JSC.AnyTask = undefined, finalizer: Finalizer = Finalizer{ .fun = null, .data = null }, channel: Queue, @@ -1498,18 +1562,30 @@ pub const ThreadSafeFunction = struct { pub fn call(this: *ThreadSafeFunction) void { const task = this.channel.tryReadItem() catch null orelse return; + const globalObject = this.env; + + this.tracker.willDispatch(globalObject); + defer this.tracker.didDispatch(globalObject); + switch (this.callback) { .js => |js_function| { if (js_function.isEmptyOrUndefinedOrNull()) { return; } - const err = js_function.call(this.env, &.{}); - if (err.isAnyError()) { - _ = this.env.bunVM().uncaughtException(this.env, err, false); - } + + _ = js_function.call(globalObject, .undefined, &.{}) catch |err| + globalObject.reportActiveExceptionAsUnhandled(err); }, .c => |cb| { - cb.napi_threadsafe_function_call_js(this.env, cb.js, this.ctx, task); + if (comptime bun.Environment.isDebug) { + const str = cb.js.toBunString(globalObject); + defer str.deref(); + log("call() {}", .{str}); + } + + const handle_scope = NapiHandleScope.push(globalObject, false); + defer if (handle_scope) |scope| scope.pop(globalObject); + cb.napi_threadsafe_function_call_js(globalObject, napi_value.create(globalObject, cb.js), this.ctx, task); }, } } @@ -1528,6 +1604,8 @@ pub const ThreadSafeFunction = struct { pub fn finalize(opaq: *anyopaque) void { var this = bun.cast(*ThreadSafeFunction, opaq); + this.unref(); + if (this.finalizer.fun) |fun| { fun(this.event_loop.global, this.finalizer.data, this.ctx); } @@ -1579,7 +1657,6 @@ pub const ThreadSafeFunction = struct { } if (mode == .abort or this.thread_count == 0) { - this.finalizer_task = JSC.AnyTask{ .ctx = this, .callback = finalize }; this.event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, finalize)); } @@ -1589,7 +1666,7 @@ pub const ThreadSafeFunction = struct { pub export fn napi_create_threadsafe_function( env: napi_env, - func: napi_value, + func_: napi_value, _: napi_value, _: napi_value, max_queue_size: usize, @@ -1604,6 +1681,7 @@ pub export fn napi_create_threadsafe_function( const result = result_ orelse { return invalidArg(); }; + const func = func_.get(); if (call_js_cb == null and (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.vm()))) { return napi_status.function_expected; @@ -1613,27 +1691,30 @@ pub export fn napi_create_threadsafe_function( func.protect(); } + const vm = env.bunVM(); var function = bun.default_allocator.create(ThreadSafeFunction) catch return genericFailure(); function.* = .{ - .event_loop = env.bunVM().eventLoop(), + .event_loop = vm.eventLoop(), .env = env, .callback = if (call_js_cb) |c| .{ .c = .{ .napi_threadsafe_function_call_js = c, - .js = if (func == .zero) JSC.JSValue.jsUndefined() else func, + .js = if (func == .zero) .undefined else func.withAsyncContextIfNeeded(env), }, } else .{ - .js = if (func == .zero) JSC.JSValue.jsUndefined() else func, + .js = if (func == .zero) .undefined else func.withAsyncContextIfNeeded(env), }, .ctx = context, .channel = ThreadSafeFunction.Queue.init(max_queue_size, bun.default_allocator), .thread_count = initial_thread_count, .poll_ref = Async.KeepAlive.init(), + .tracker = JSC.AsyncTaskTracker.init(vm), }; function.finalizer = .{ .data = thread_finalize_data, .fun = thread_finalize_cb }; // nodejs by default keeps the event loop alive until the thread-safe function is unref'd function.ref(); + function.tracker.didSchedule(vm.global); result.* = function; return .ok; @@ -1668,14 +1749,12 @@ pub export fn napi_release_threadsafe_function(func: napi_threadsafe_function, m pub export fn napi_unref_threadsafe_function(env: napi_env, func: napi_threadsafe_function) napi_status { log("napi_unref_threadsafe_function", .{}); bun.assert(func.event_loop.global == env); - func.unref(); return .ok; } pub export fn napi_ref_threadsafe_function(env: napi_env, func: napi_threadsafe_function) napi_status { log("napi_ref_threadsafe_function", .{}); bun.assert(func.event_loop.global == env); - func.ref(); return .ok; } @@ -1709,18 +1788,131 @@ const V8API = if (!bun.Environment.isWindows) struct { pub extern fn _ZN2v87Isolate17GetCurrentContextEv() *anyopaque; pub extern fn _ZN4node25AddEnvironmentCleanupHookEPN2v87IsolateEPFvPvES3_() *anyopaque; pub extern fn _ZN4node28RemoveEnvironmentCleanupHookEPN2v87IsolateEPFvPvES3_() *anyopaque; + pub extern fn _ZN2v86Number3NewEPNS_7IsolateEd() *anyopaque; + pub extern fn _ZNK2v86Number5ValueEv() *anyopaque; + pub extern fn _ZN2v86String11NewFromUtf8EPNS_7IsolateEPKcNS_13NewStringTypeEi() *anyopaque; + pub extern fn _ZNK2v86String9WriteUtf8EPNS_7IsolateEPciPii() *anyopaque; + pub extern fn _ZN2v812api_internal12ToLocalEmptyEv() *anyopaque; + pub extern fn _ZNK2v86String6LengthEv() *anyopaque; + pub extern fn _ZN2v88External3NewEPNS_7IsolateEPv() *anyopaque; + pub extern fn _ZNK2v88External5ValueEv() *anyopaque; + pub extern fn _ZN2v86Object3NewEPNS_7IsolateE() *anyopaque; + pub extern fn _ZN2v86Object3SetENS_5LocalINS_7ContextEEENS1_INS_5ValueEEES5_() *anyopaque; + pub extern fn _ZN2v86Object16SetInternalFieldEiNS_5LocalINS_4DataEEE() *anyopaque; + pub extern fn _ZN2v86Object20SlowGetInternalFieldEi() *anyopaque; + pub extern fn _ZN2v811HandleScope12CreateHandleEPNS_8internal7IsolateEm() *anyopaque; + pub extern fn _ZN2v811HandleScopeC1EPNS_7IsolateE() *anyopaque; + pub extern fn _ZN2v811HandleScopeD1Ev() *anyopaque; + pub extern fn _ZN2v811HandleScopeD2Ev() *anyopaque; + pub extern fn _ZN2v816FunctionTemplate11GetFunctionENS_5LocalINS_7ContextEEE() *anyopaque; + pub extern fn _ZN2v816FunctionTemplate3NewEPNS_7IsolateEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEENS_5LocalIS4_EENSA_INS_9SignatureEEEiNS_19ConstructorBehaviorENS_14SideEffectTypeEPKNS_9CFunctionEttt() *anyopaque; + pub extern fn _ZN2v814ObjectTemplate11NewInstanceENS_5LocalINS_7ContextEEE() *anyopaque; + pub extern fn _ZN2v814ObjectTemplate21SetInternalFieldCountEi() *anyopaque; + pub extern fn _ZNK2v814ObjectTemplate18InternalFieldCountEv() *anyopaque; + pub extern fn _ZN2v814ObjectTemplate3NewEPNS_7IsolateENS_5LocalINS_16FunctionTemplateEEE() *anyopaque; + pub extern fn _ZN2v824EscapableHandleScopeBase10EscapeSlotEPm() *anyopaque; + pub extern fn _ZN2v824EscapableHandleScopeBaseC2EPNS_7IsolateE() *anyopaque; + pub extern fn _ZN2v88internal35IsolateFromNeverReadOnlySpaceObjectEm() *anyopaque; + pub extern fn _ZN2v85Array3NewEPNS_7IsolateEPNS_5LocalINS_5ValueEEEm() *anyopaque; + pub extern fn _ZN2v88Function7SetNameENS_5LocalINS_6StringEEE() *anyopaque; + pub extern fn _ZNK2v85Value9IsBooleanEv() *anyopaque; + pub extern fn _ZNK2v87Boolean5ValueEv() *anyopaque; + pub extern fn _ZNK2v85Value10FullIsTrueEv() *anyopaque; + pub extern fn _ZNK2v85Value11FullIsFalseEv() *anyopaque; + pub extern fn _ZN2v820EscapableHandleScopeC1EPNS_7IsolateE() *anyopaque; + pub extern fn _ZN2v820EscapableHandleScopeC2EPNS_7IsolateE() *anyopaque; + pub extern fn _ZN2v820EscapableHandleScopeD1Ev() *anyopaque; + pub extern fn _ZN2v820EscapableHandleScopeD2Ev() *anyopaque; + pub extern fn _ZNK2v85Value8IsObjectEv() *anyopaque; + pub extern fn _ZNK2v85Value8IsNumberEv() *anyopaque; + pub extern fn _ZNK2v85Value8IsUint32Ev() *anyopaque; + pub extern fn _ZNK2v85Value11Uint32ValueENS_5LocalINS_7ContextEEE() *anyopaque; + pub extern fn _ZNK2v85Value11IsUndefinedEv() *anyopaque; + pub extern fn _ZNK2v85Value6IsNullEv() *anyopaque; + pub extern fn _ZNK2v85Value17IsNullOrUndefinedEv() *anyopaque; + pub extern fn _ZNK2v85Value6IsTrueEv() *anyopaque; + pub extern fn _ZNK2v85Value7IsFalseEv() *anyopaque; + pub extern fn _ZNK2v85Value8IsStringEv() *anyopaque; + pub extern fn _ZN2v87Boolean3NewEPNS_7IsolateEb() *anyopaque; + pub extern fn _ZN2v86Object16GetInternalFieldEi() *anyopaque; + pub extern fn _ZN2v87Context10GetIsolateEv() *anyopaque; + pub extern fn _ZN2v86String14NewFromOneByteEPNS_7IsolateEPKhNS_13NewStringTypeEi() *anyopaque; + pub extern fn _ZNK2v86String10Utf8LengthEPNS_7IsolateE() *anyopaque; + pub extern fn _ZNK2v86String10IsExternalEv() *anyopaque; + pub extern fn _ZNK2v86String17IsExternalOneByteEv() *anyopaque; + pub extern fn _ZNK2v86String17IsExternalTwoByteEv() *anyopaque; + pub extern fn _ZNK2v86String9IsOneByteEv() *anyopaque; + pub extern fn _ZNK2v86String19ContainsOnlyOneByteEv() *anyopaque; + pub extern fn _ZN2v812api_internal18GlobalizeReferenceEPNS_8internal7IsolateEm() *anyopaque; + pub extern fn _ZN2v812api_internal13DisposeGlobalEPm() *anyopaque; + pub extern fn uv_os_getpid() *anyopaque; + pub extern fn uv_os_getppid() *anyopaque; } else struct { // MSVC name mangling is different than it is on unix. // To make this easier to deal with, I have provided a script to generate the list of functions. // - // dumpbin .\build\CMakeFiles\bun-debug.dir\src\bun.js\bindings\v8.cpp.obj /symbols | where-object { $_.Contains(' node::') -or $_.Contains(' v8::') } | foreach-object { (($_ -split "\|")[1] -split " ")[1] } | ForEach-Object { "extern fn @`"${_}`"() *anyopaque;" } + // dumpbin .\build\CMakeFiles\bun-debug.dir\src\bun.js\bindings\v8\*.cpp.obj /symbols | where-object { $_.Contains(' node::') -or $_.Contains(' v8::') } | foreach-object { (($_ -split "\|")[1] -split " ")[1] } | ForEach-Object { "extern fn @`"${_}`"() *anyopaque;" } // // Bug @paperdave if you get stuck here pub extern fn @"?TryGetCurrent@Isolate@v8@@SAPEAV12@XZ"() *anyopaque; pub extern fn @"?GetCurrent@Isolate@v8@@SAPEAV12@XZ"() *anyopaque; - pub extern fn @"?GetCurrentContext@Isolate@v8@@QEAA?AV?$Local@VJSGlobalObject@JSC@@@2@XZ"() *anyopaque; + pub extern fn @"?GetCurrentContext@Isolate@v8@@QEAA?AV?$Local@VContext@v8@@@2@XZ"() *anyopaque; pub extern fn @"?AddEnvironmentCleanupHook@node@@YAXPEAVIsolate@v8@@P6AXPEAX@Z1@Z"() *anyopaque; pub extern fn @"?RemoveEnvironmentCleanupHook@node@@YAXPEAVIsolate@v8@@P6AXPEAX@Z1@Z"() *anyopaque; + pub extern fn @"?New@Number@v8@@SA?AV?$Local@VNumber@v8@@@2@PEAVIsolate@2@N@Z"() *anyopaque; + pub extern fn @"?Value@Number@v8@@QEBANXZ"() *anyopaque; + pub extern fn @"?NewFromUtf8@String@v8@@SA?AV?$MaybeLocal@VString@v8@@@2@PEAVIsolate@2@PEBDW4NewStringType@2@H@Z"() *anyopaque; + pub extern fn @"?WriteUtf8@String@v8@@QEBAHPEAVIsolate@2@PEADHPEAHH@Z"() *anyopaque; + pub extern fn @"?ToLocalEmpty@api_internal@v8@@YAXXZ"() *anyopaque; + pub extern fn @"?Length@String@v8@@QEBAHXZ"() *anyopaque; + pub extern fn @"?New@External@v8@@SA?AV?$Local@VExternal@v8@@@2@PEAVIsolate@2@PEAX@Z"() *anyopaque; + pub extern fn @"?Value@External@v8@@QEBAPEAXXZ"() *anyopaque; + pub extern fn @"?New@Object@v8@@SA?AV?$Local@VObject@v8@@@2@PEAVIsolate@2@@Z"() *anyopaque; + pub extern fn @"?Set@Object@v8@@QEAA?AV?$Maybe@_N@2@V?$Local@VContext@v8@@@2@V?$Local@VValue@v8@@@2@1@Z"() *anyopaque; + pub extern fn @"?SetInternalField@Object@v8@@QEAAXHV?$Local@VData@v8@@@2@@Z"() *anyopaque; + pub extern fn @"?SlowGetInternalField@Object@v8@@AEAA?AV?$Local@VData@v8@@@2@H@Z"() *anyopaque; + pub extern fn @"?CreateHandle@HandleScope@v8@@KAPEA_KPEAVIsolate@internal@2@_K@Z"() *anyopaque; + pub extern fn @"??0HandleScope@v8@@QEAA@PEAVIsolate@1@@Z"() *anyopaque; + pub extern fn @"??1HandleScope@v8@@QEAA@XZ"() *anyopaque; + pub extern fn @"?GetFunction@FunctionTemplate@v8@@QEAA?AV?$MaybeLocal@VFunction@v8@@@2@V?$Local@VContext@v8@@@2@@Z"() *anyopaque; + pub extern fn @"?New@FunctionTemplate@v8@@SA?AV?$Local@VFunctionTemplate@v8@@@2@PEAVIsolate@2@P6AXAEBV?$FunctionCallbackInfo@VValue@v8@@@2@@ZV?$Local@VValue@v8@@@2@V?$Local@VSignature@v8@@@2@HW4ConstructorBehavior@2@W4SideEffectType@2@PEBVCFunction@2@GGG@Z"() *anyopaque; + pub extern fn @"?NewInstance@ObjectTemplate@v8@@QEAA?AV?$MaybeLocal@VObject@v8@@@2@V?$Local@VContext@v8@@@2@@Z"() *anyopaque; + pub extern fn @"?SetInternalFieldCount@ObjectTemplate@v8@@QEAAXH@Z"() *anyopaque; + pub extern fn @"?InternalFieldCount@ObjectTemplate@v8@@QEBAHXZ"() *anyopaque; + pub extern fn @"?New@ObjectTemplate@v8@@SA?AV?$Local@VObjectTemplate@v8@@@2@PEAVIsolate@2@V?$Local@VFunctionTemplate@v8@@@2@@Z"() *anyopaque; + pub extern fn @"?EscapeSlot@EscapableHandleScopeBase@v8@@IEAAPEA_KPEA_K@Z"() *anyopaque; + pub extern fn @"??0EscapableHandleScopeBase@v8@@QEAA@PEAVIsolate@1@@Z"() *anyopaque; + pub extern fn @"?IsolateFromNeverReadOnlySpaceObject@internal@v8@@YAPEAVIsolate@12@_K@Z"() *anyopaque; + pub extern fn @"?New@Array@v8@@SA?AV?$Local@VArray@v8@@@2@PEAVIsolate@2@PEAV?$Local@VValue@v8@@@2@_K@Z"() *anyopaque; + pub extern fn @"?SetName@Function@v8@@QEAAXV?$Local@VString@v8@@@2@@Z"() *anyopaque; + pub extern fn @"?IsBoolean@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?Value@Boolean@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?FullIsTrue@Value@v8@@AEBA_NXZ"() *anyopaque; + pub extern fn @"?FullIsFalse@Value@v8@@AEBA_NXZ"() *anyopaque; + pub extern fn @"??1EscapableHandleScope@v8@@QEAA@XZ"() *anyopaque; + pub extern fn @"??0EscapableHandleScope@v8@@QEAA@PEAVIsolate@1@@Z"() *anyopaque; + pub extern fn @"?IsObject@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsNumber@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsUint32@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?Uint32Value@Value@v8@@QEBA?AV?$Maybe@I@2@V?$Local@VContext@v8@@@2@@Z"() *anyopaque; + pub extern fn @"?IsUndefined@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsNull@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsNullOrUndefined@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsTrue@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsFalse@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsString@Value@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?New@Boolean@v8@@SA?AV?$Local@VBoolean@v8@@@2@PEAVIsolate@2@_N@Z"() *anyopaque; + pub extern fn @"?GetInternalField@Object@v8@@QEAA?AV?$Local@VData@v8@@@2@H@Z"() *anyopaque; + pub extern fn @"?GetIsolate@Context@v8@@QEAAPEAVIsolate@2@XZ"() *anyopaque; + pub extern fn @"?NewFromOneByte@String@v8@@SA?AV?$MaybeLocal@VString@v8@@@2@PEAVIsolate@2@PEBEW4NewStringType@2@H@Z"() *anyopaque; + pub extern fn @"?IsExternal@String@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsExternalOneByte@String@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsExternalTwoByte@String@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?IsOneByte@String@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?Utf8Length@String@v8@@QEBAHPEAVIsolate@2@@Z"() *anyopaque; + pub extern fn @"?ContainsOnlyOneByte@String@v8@@QEBA_NXZ"() *anyopaque; + pub extern fn @"?GlobalizeReference@api_internal@v8@@YAPEA_KPEAVIsolate@internal@2@_K@Z"() *anyopaque; + pub extern fn @"?DisposeGlobal@api_internal@v8@@YAXPEA_K@Z"() *anyopaque; }; pub fn fixDeadCodeElimination() void { diff --git a/src/node-fallbacks/events.js b/src/node-fallbacks/events.js index 70f47eb539ec8..321f14c204018 100644 --- a/src/node-fallbacks/events.js +++ b/src/node-fallbacks/events.js @@ -478,6 +478,5 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) { } export default EventEmitter; -export { once }; -export { EventEmitter }; +export { EventEmitter, once }; export var prototype = EventEmitter.prototype; diff --git a/src/node-fallbacks/querystring.js b/src/node-fallbacks/querystring.js index 00404cc1bfbea..8c71d38b8ad54 100644 --- a/src/node-fallbacks/querystring.js +++ b/src/node-fallbacks/querystring.js @@ -1,2 +1 @@ -export { unescapeBuffer, unescape, escape, stringify, encode, parse, decode } from "querystring-es3"; -export { default } from "querystring-es3"; +export { decode, default, encode, escape, parse, stringify, unescape, unescapeBuffer } from "querystring-es3"; diff --git a/src/node-fallbacks/tty.js b/src/node-fallbacks/tty.js index 914833c562007..3844312ca4541 100644 --- a/src/node-fallbacks/tty.js +++ b/src/node-fallbacks/tty.js @@ -5,5 +5,5 @@ function WriteStream() { function ReadStream() { throw new Error("tty.ReadStream is not implemented for browsers"); } -export { isatty, ReadStream, WriteStream }; +export { ReadStream, WriteStream, isatty }; export default { isatty, ReadStream, WriteStream }; diff --git a/src/node-fallbacks/url.js b/src/node-fallbacks/url.js index 78cc1e860b1b4..571d30b9345df 100644 --- a/src/node-fallbacks/url.js +++ b/src/node-fallbacks/url.js @@ -732,12 +732,15 @@ Url.prototype.parseHost = function () { if (host) this.hostname = host; }; -export { URL, URLSearchParams }; -export { urlParse as parse }; -export { urlResolve as resolve }; -export { urlResolveObject as resolveObject }; -export { urlFormat as format }; -export { Url as Url }; +export { + URL, + URLSearchParams, + Url as Url, + urlFormat as format, + urlParse as parse, + urlResolve as resolve, + urlResolveObject as resolveObject, +}; export default { parse: urlParse, diff --git a/src/node-fallbacks/util.js b/src/node-fallbacks/util.js index 1ad352a2ae2dc..59730686861d6 100644 --- a/src/node-fallbacks/util.js +++ b/src/node-fallbacks/util.js @@ -3,5 +3,5 @@ export * from "util"; const TextEncoder = globalThis.TextEncoder; const TextDecoder = globalThis.TextDecoder; -export { TextEncoder, TextDecoder }; +export { TextDecoder, TextEncoder }; export default { TextEncoder, TextDecoder }; diff --git a/src/open.zig b/src/open.zig index 3c867f902693c..bb83d9b60d57d 100644 --- a/src/open.zig +++ b/src/open.zig @@ -66,7 +66,7 @@ pub const Editor = enum(u8) { const StringMap = std.EnumMap(Editor, string); const StringArrayMap = std.EnumMap(Editor, []const [:0]const u8); - const name_map = std.StaticStringMap(Editor).initComptime(.{ + const name_map = bun.ComptimeStringMap(Editor, .{ .{ "sublime", .sublime }, .{ "subl", .sublime }, .{ "vscode", .vscode }, diff --git a/src/options.zig b/src/options.zig index 5cdd3412e8a22..d3cf1bfde745d 100644 --- a/src/options.zig +++ b/src/options.zig @@ -29,6 +29,8 @@ const Analytics = @import("./analytics/analytics_thread.zig"); const MacroRemap = @import("./resolver/package_json.zig").MacroMap; const DotEnv = @import("./env_loader.zig"); +pub const Define = defines.Define; + const assert = bun.assert; pub const WriteDestination = enum { @@ -298,7 +300,7 @@ pub const ExternalModules = struct { "zlib", }; - pub const NodeBuiltinsMap = std.StaticStringMap(void).initComptime(.{ + pub const NodeBuiltinsMap = bun.ComptimeStringMap(void, .{ .{ "_http_agent", {} }, .{ "_http_client", {} }, .{ "_http_common", {} }, @@ -370,7 +372,7 @@ pub const ModuleType = enum { cjs, esm, - pub const List = std.StaticStringMap(ModuleType).initComptime(.{ + pub const List = bun.ComptimeStringMap(ModuleType, .{ .{ "commonjs", ModuleType.cjs }, .{ "module", ModuleType.esm }, }); @@ -589,14 +591,45 @@ pub const Target = enum { }; pub const Format = enum { + /// ES module format + /// This is the default format esm, - cjs, + + /// Immediately-invoked function expression + /// (function(){ + /// ... + /// })(); iife, + /// CommonJS + cjs, + + /// Kit's uses a special module format for Hot-module-reloading. It includes a + /// runtime payload, sourced from src/kit/hmr-runtime.ts. + /// + /// ((input_graph, entry_point_key) => { + /// ... runtime code ... + /// })([ + /// "module1.ts"(require, module) { ... }, + /// "module2.ts"(require, module) { ... }, + /// ], "module1.ts"); + internal_kit_dev, + + pub fn keepES6ImportExportSyntax(this: Format) bool { + return this == .esm; + } + + pub inline fn isESM(this: Format) bool { + return this == .esm; + } + pub const Map = bun.ComptimeStringMap(Format, .{ .{ "esm", .esm }, .{ "cjs", .cjs }, .{ "iife", .iife }, + + // TODO: Disable this outside of debug builds + .{ "internal_kit_dev", .internal_kit_dev }, }); pub fn fromJS(global: *JSC.JSGlobalObject, format: JSC.JSValue, exception: JSC.C.ExceptionRef) ?Format { @@ -1240,7 +1273,7 @@ pub fn definesFromTransformOptions( } } - const resolved_defines = try defines.DefineData.from_input(user_defines, log, allocator); + const resolved_defines = try defines.DefineData.fromInput(user_defines, log, allocator); return try defines.Define.init( allocator, @@ -1308,7 +1341,7 @@ pub const ResolveFileExtensions = struct { }; }; -pub fn loadersFromTransformOptions(allocator: std.mem.Allocator, _loaders: ?Api.LoaderMap, target: Target) !bun.StringArrayHashMap(Loader) { +pub fn loadersFromTransformOptions(allocator: std.mem.Allocator, _loaders: ?Api.LoaderMap, target: Target) std.mem.Allocator.Error!bun.StringArrayHashMap(Loader) { const input_loaders = _loaders orelse std.mem.zeroes(Api.LoaderMap); const loader_values = try allocator.alloc(Loader, input_loaders.loaders.len); @@ -1377,27 +1410,29 @@ pub const SourceMapOption = enum { }); }; -pub const OutputFormat = enum { - preserve, - - /// ES module format - /// This is the default format - esm, - /// Immediately-invoked function expression - /// ( - /// function(){} - /// )(); - iife, - /// CommonJS - cjs, +pub const PackagesOption = enum { + bundle, + external, - pub fn keepES6ImportExportSyntax(this: OutputFormat) bool { - return this == .esm; + pub fn fromApi(packages: ?Api.PackagesMode) PackagesOption { + return switch (packages orelse .bundle) { + .external => .external, + .bundle => .bundle, + else => .bundle, + }; } - pub inline fn isESM(this: OutputFormat) bool { - return this == .esm; + pub fn toAPI(packages: ?PackagesOption) Api.PackagesMode { + return switch (packages orelse .bundle) { + .external => .external, + .bundle => .bundle, + }; } + + pub const Map = bun.ComptimeStringMap(PackagesOption, .{ + .{ "external", .external }, + .{ "bundle", .bundle }, + }); }; /// BundleOptions is used when ResolveMode is not set to "disable". @@ -1418,6 +1453,7 @@ pub const BundleOptions = struct { react_server_components: bool = false, react_server_components_boundary: string = "", hot_module_reloading: bool = false, + react_fast_refresh: bool = false, inject: ?[]string = null, origin: URL = URL{}, output_dir_handle: ?Dir = null, @@ -1435,13 +1471,10 @@ pub const BundleOptions = struct { serve: bool = false, // only used by bundle_v2 - output_format: OutputFormat = .esm, + output_format: Format = .esm, append_package_version_in_query_string: bool = false, - jsx_optimization_inline: ?bool = null, - jsx_optimization_hoist: ?bool = null, - resolve_mode: api.Api.ResolveMode, tsconfig_override: ?string = null, target: Target = Target.browser, @@ -1475,6 +1508,7 @@ pub const BundleOptions = struct { tree_shaking: bool = false, code_splitting: bool = false, source_map: SourceMapOption = SourceMapOption.none, + packages: PackagesOption = PackagesOption.bundle, disable_transpilation: bool = false, @@ -1484,11 +1518,15 @@ pub const BundleOptions = struct { install: ?*Api.BunInstall = null, inlining: bool = false, + inline_entrypoint_import_meta_main: bool = false, minify_whitespace: bool = false, minify_syntax: bool = false, minify_identifiers: bool = false, dead_code_elimination: bool = true, + ignore_dce_annotations: bool = false, + emit_dce_annotations: bool = false, + code_coverage: bool = false, debugger: bool = false, @@ -1519,7 +1557,6 @@ pub const BundleOptions = struct { "react-client", "react-server", "react-refresh", - "__bun-test-unwrap-commonjs__", }; pub inline fn cssImportBehavior(this: *const BundleOptions) Api.CssInJsBehavior { @@ -1737,6 +1774,8 @@ pub const BundleOptions = struct { opts.source_map = SourceMapOption.fromApi(transform.source_map orelse .none); + opts.packages = PackagesOption.fromApi(transform.packages orelse .bundle); + opts.tree_shaking = opts.target.isBun() or opts.production; opts.inlining = opts.tree_shaking; if (opts.inlining) @@ -1754,7 +1793,6 @@ pub const BundleOptions = struct { opts.polyfill_node_globals = opts.target == .browser; Analytics.Features.filesystem_router += @as(usize, @intFromBool(opts.routes.routes_enabled)); - Analytics.Features.origin += @as(usize, @intFromBool(opts.origin.href.len > 0)); Analytics.Features.macros += @as(usize, @intFromBool(opts.target == .bun_macro)); Analytics.Features.external += @as(usize, @intFromBool(transform.external.len > 0)); return opts; @@ -2023,7 +2061,6 @@ pub const OutputFile = struct { const fd_out = file_out.handle; var do_close = false; - // TODO: close file_out on error const fd_in = (try std.fs.openFileAbsolute(file.src_path.text, .{ .mode = .read_only })).handle; if (Environment.isWindows) { @@ -2037,12 +2074,12 @@ pub const OutputFile = struct { defer { if (do_close) { - std.posix.close(fd_out); - std.posix.close(fd_in); + _ = bun.sys.close(bun.toFD(fd_out)); + _ = bun.sys.close(bun.toFD(fd_in)); } } - try bun.copyFile(fd_in, fd_out); + try bun.copyFile(fd_in, fd_out).unwrap(); } pub fn toJS( @@ -2598,7 +2635,7 @@ pub const PathTemplate = struct { placeholder: Placeholder = .{}, pub fn needs(this: *const PathTemplate, comptime field: std.meta.FieldEnum(Placeholder)) bool { - return strings.contains(this.data, comptime "[" ++ @tagName(field) ++ "]"); + return strings.containsComptime(this.data, "[" ++ @tagName(field) ++ "]"); } inline fn writeReplacingSlashesOnWindows(w: anytype, slice: []const u8) !void { @@ -2666,7 +2703,33 @@ pub const PathTemplate = struct { try writeReplacingSlashesOnWindows(writer, remain); } - pub const hashFormatter = bun.fmt.hexIntLower; + pub fn hashFormatter(int: u64) std.fmt.Formatter(hashFormatterImpl) { + return .{ .data = int }; + } + + fn hashFormatterImpl(int: u64, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + // esbuild has an 8 character truncation of a base32 encoded bytes. this + // is not exactly that, but it will appear as such. the character list + // chosen omits similar characters in the unlikely case someone is + // trying to memorize a hash. + // + // reminder: this cannot be base64 or any encoding which is case + // sensitive as these hashes are often used in file paths, in which + // Windows and some macOS systems treat as case-insensitive. + comptime assert(fmt.len == 0); + const in_bytes = std.mem.asBytes(&int); + const chars = "0123456789abcdefghjkmnpqrstvwxyz"; + try writer.writeAll(&.{ + chars[in_bytes[0] & 31], + chars[in_bytes[1] & 31], + chars[in_bytes[2] & 31], + chars[in_bytes[3] & 31], + chars[in_bytes[4] & 31], + chars[in_bytes[5] & 31], + chars[in_bytes[6] & 31], + chars[in_bytes[7] & 31], + }); + } pub const Placeholder = struct { dir: []const u8 = "", diff --git a/src/output.zig b/src/output.zig index 9ddaddc3d6231..73683d04cd1fe 100644 --- a/src/output.zig +++ b/src/output.zig @@ -354,7 +354,7 @@ pub fn disableBuffering() void { if (comptime Environment.isNative) enable_buffering = false; } -pub noinline fn panic(comptime fmt: string, args: anytype) noreturn { +pub fn panic(comptime fmt: string, args: anytype) noreturn { @setCold(true); if (Output.isEmojiEnabled()) { @@ -387,17 +387,17 @@ pub fn resetTerminal() void { } if (enable_ansi_colors_stderr) { - _ = source.error_stream.write("\x1b[H\x1b[2J").unwrap() catch 0; + _ = source.error_stream.write("\x1B[2J\x1B[3J\x1B[H").unwrap() catch 0; } else { - _ = source.stream.write("\x1b[H\x1b[2J").unwrap() catch 0; + _ = source.stream.write("\x1B[2J\x1B[3J\x1B[H").unwrap() catch 0; } } pub fn resetTerminalAll() void { if (enable_ansi_colors_stderr) - _ = source.error_stream.write("\x1b[H\x1b[2J").unwrap() catch 0; + _ = source.error_stream.write("\x1B[2J\x1B[3J\x1B[H").unwrap() catch 0; if (enable_ansi_colors_stdout) - _ = source.stream.write("\x1b[H\x1b[2J").unwrap() catch 0; + _ = source.stream.write("\x1B[2J\x1B[3J\x1B[H").unwrap() catch 0; } /// Write buffered stdout & stderr to the terminal. @@ -700,7 +700,7 @@ pub const color_map = ComptimeStringMap(string, .{ &.{ "yellow", ED ++ "33m" }, }); const RESET: string = "\x1b[0m"; -pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) string { +pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) [:0]const u8 { if (comptime bun.fast_debug_build_mode) return fmt; @@ -778,8 +778,7 @@ pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) string { } }; - const fmt_data = comptime new_fmt[0..new_fmt_i].*; - return &fmt_data; + return comptime (new_fmt[0..new_fmt_i].* ++ .{0})[0..new_fmt_i :0]; } pub noinline fn prettyWithPrinter(comptime fmt: string, args: anytype, comptime printer: anytype, comptime l: Destination) void { @@ -867,8 +866,6 @@ pub const DebugTimer = struct { if (comptime Environment.isDebug) { var timer = self.timer; w.print("{d:.3}ms", .{@as(f64, @floatFromInt(timer.read())) / std.time.ns_per_ms}) catch unreachable; - } else { - @compileError("DebugTimer.format() should only be called in debug mode"); } } }; @@ -1017,6 +1014,11 @@ pub inline fn errGeneric(comptime fmt: []const u8, args: anytype) void { prettyErrorln("error: " ++ fmt, args); } +/// Print a red error message with "error: " as the prefix and a formatted message. +pub inline fn errFmt(formatter: anytype) void { + return errGeneric("{}", .{formatter}); +} + /// This struct is a workaround a Windows terminal bug. /// TODO: when https://github.com/microsoft/terminal/issues/16606 is resolved, revert this commit. pub var buffered_stdin = std.io.BufferedReader(4096, File.Reader){ diff --git a/src/patch.zig b/src/patch.zig index 950ab014c4672..be170ab0840a2 100644 --- a/src/patch.zig +++ b/src/patch.zig @@ -611,11 +611,11 @@ fn patchFileSecondPass(files: []FileDeets) ParseErr!PatchFile { result.parts.append(bun.default_allocator, .{ .file_deletion = bun.new(FileDeletion, FileDeletion{ .hunk = if (file.hunks.items.len > 0) brk: { - var value = file.hunks.items[0]; + const value = file.hunks.items[0]; file.hunks.items[0] = .{ .header = Hunk.Header.zeroes, }; - break :brk bun.dupe(Hunk, &value); + break :brk bun.new(Hunk, value); } else null, .path = path, .mode = parseFileMode(file.deleted_file_mode.?) orelse { @@ -632,11 +632,11 @@ fn patchFileSecondPass(files: []FileDeets) ParseErr!PatchFile { result.parts.append(bun.default_allocator, .{ .file_creation = bun.new(FileCreation, FileCreation{ .hunk = if (file.hunks.items.len > 0) brk: { - var value = file.hunks.items[0]; + const value = file.hunks.items[0]; file.hunks.items[0] = .{ .header = Hunk.Header.zeroes, }; - break :brk bun.dupe(Hunk, &value); + break :brk bun.new(Hunk, value); } else null, .path = path, .mode = parseFileMode(file.new_file_mode.?) orelse { @@ -1043,7 +1043,10 @@ const PatchLinesParser = struct { if (b_part_start >= line.len) return null; const lmao_bro = line[b_part_start..]; std.mem.doNotOptimizeAway(lmao_bro); - const b_part_end = if (std.mem.indexOfAny(u8, line[b_part_start..], " \n\r\t")) |pos| pos + b_part_start else line.len; + const b_part_end = if (bun.strings.indexAnyComptime(line[b_part_start..], " \n\r\t")) |pos| + pos + b_part_start + else + line.len; const b_part = line[b_part_start..b_part_end]; for (a_part) |c| if (!VALID_CHARS.isSet(c)) return null; @@ -1091,7 +1094,7 @@ const PatchLinesParser = struct { }; pub const TestingAPIs = struct { - pub fn makeDiff(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn makeDiff(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments_ = callframe.arguments(2); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); @@ -1143,7 +1146,7 @@ pub const TestingAPIs = struct { } } }; - pub fn apply(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn apply(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { var args = switch (parseApplyArgs(globalThis, callframe)) { .err => |e| return e, .result => |a| a, @@ -1158,7 +1161,7 @@ pub const TestingAPIs = struct { return .true; } /// Used in JS tests, see `internal-for-testing.ts` and patch tests. - pub fn parse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn parse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments_ = callframe.arguments(2); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); diff --git a/src/pool.zig b/src/pool.zig index ec5a07388b278..7d994c752f1c6 100644 --- a/src/pool.zig +++ b/src/pool.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const bun = @import("root").bun; fn SinglyLinkedList(comptime T: type, comptime Parent: type) type { return struct { @@ -157,7 +158,7 @@ pub fn ObjectPool( pub fn push(allocator: std.mem.Allocator, pooled: Type) void { if (comptime @import("./env.zig").allow_assert) - @import("root").bun.assert(!full()); + bun.assert(!full()); const new_node = allocator.create(LinkedList.Node) catch unreachable; new_node.* = LinkedList.Node{ diff --git a/src/react-refresh.js b/src/react-refresh.js deleted file mode 100644 index d3453d5916d1f..0000000000000 --- a/src/react-refresh.js +++ /dev/null @@ -1,348 +0,0 @@ -// This is based on v0.11.0 of react-refresh -// The following changes: -// - Removed __DEV__ checks -// - inlined REACT_MEMO_TYPE & REACT_FORWARD_REF_TYPE -// - minified - -const F = "for" in Symbol ? Symbol.for("react.forward_ref") : 60112, - C = "for" in Symbol ? Symbol.for("react.memo") : 60115, - O = typeof WeakMap == "function" ? WeakMap : Map, - T = new Map(), - k = new O(), - m = new O(), - M = new O(); -let g = []; -const b = new Map(), - w = new Map(), - c = new Set(), - p = new Set(), - R = typeof WeakMap == "function" ? new WeakMap() : null; -let S = !1; -function _(e) { - if (e.fullKey !== null) return e.fullKey; - let t = e.ownKey, - n; - try { - n = e.getCustomHooks(); - } catch { - return (e.forceReset = !0), (e.fullKey = t), t; - } - for (let o = 0; o < n.length; o++) { - const l = n[o]; - if (typeof l != "function") return (e.forceReset = !0), (e.fullKey = t), t; - const s = m.get(l); - if (s === void 0) continue; - const r = _(s); - s.forceReset && (e.forceReset = !0), - (t += - ` ---- -` + r); - } - return (e.fullKey = t), t; -} -function D(e, t) { - const n = m.get(e), - o = m.get(t); - return n === void 0 && o === void 0 - ? !0 - : !(n === void 0 || o === void 0 || _(n) !== _(o) || o.forceReset); -} -function B(e) { - return e.prototype && e.prototype.isReactComponent; -} -function v(e, t) { - return B(e) || B(t) ? !1 : !!D(e, t); -} -function I(e) { - return M.get(e); -} -function P(e) { - const t = new Map(); - return ( - e.forEach((n, o) => { - t.set(o, n); - }), - t - ); -} -function L(e) { - const t = new Set(); - return ( - e.forEach((n) => { - t.add(n); - }), - t - ); -} -function H(e, t) { - try { - return e[t]; - } catch { - return; - } -} -function j() { - if (g.length === 0 || S) return null; - S = !0; - try { - const e = new Set(), - t = new Set(), - n = g; - (g = []), - n.forEach((f) => { - let [i, u] = f; - const a = i.current; - M.set(a, i), - M.set(u, i), - (i.current = u), - v(a, u) ? t.add(i) : e.add(i); - }); - const o = { updatedFamilies: t, staleFamilies: e }; - b.forEach((f) => { - f.setRefreshHandler(I); - }); - let l = !1, - s = null; - const r = L(p), - h = L(c), - d = P(w); - if ( - (r.forEach((f) => { - const i = d.get(f); - if (i === void 0) - throw new Error( - "Could not find helpers for a root. This is a bug in React Refresh.", - ); - if ((!p.has(f), R === null || !R.has(f))) return; - const u = R.get(f); - try { - i.scheduleRoot(f, u); - } catch (a) { - l || ((l = !0), (s = a)); - } - }), - h.forEach((f) => { - const i = d.get(f); - if (i === void 0) - throw new Error( - "Could not find helpers for a root. This is a bug in React Refresh.", - ); - !c.has(f); - try { - i.scheduleRefresh(f, o); - } catch (u) { - l || ((l = !0), (s = u)); - } - }), - l) - ) - throw s; - return o; - } finally { - S = !1; - } -} -function K(e, t) { - if ( - e === null || - (typeof e != "function" && typeof e != "object") || - k.has(e) - ) - return; - let n = T.get(t); - if ( - (n === void 0 ? ((n = { current: e }), T.set(t, n)) : g.push([n, e]), - k.set(e, n), - typeof e == "object" && e !== null) - ) - switch (H(e, "$$typeof")) { - case F: - K(e.render, t + "$render"); - break; - case C: - K(e.type, t + "$type"); - break; - } -} -function E(e, t) { - let n = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : !1, - o = arguments.length > 3 ? arguments[3] : void 0; - if ( - (m.has(e) || - m.set(e, { - forceReset: n, - ownKey: t, - fullKey: null, - getCustomHooks: o || (() => []), - }), - typeof e == "object" && e !== null) - ) - switch (H(e, "$$typeof")) { - case F: - E(e.render, t, n, o); - break; - case C: - E(e.type, t, n, o); - break; - } -} -function A(e) { - const t = m.get(e); - t !== void 0 && _(t); -} -function $(e) { - return T.get(e); -} -function W(e) { - return k.get(e); -} -function x(e) { - const t = new Set(); - return ( - c.forEach((n) => { - const o = w.get(n); - if (o === void 0) - throw new Error( - "Could not find helpers for a root. This is a bug in React Refresh.", - ); - o.findHostInstancesForRefresh(n, e).forEach((s) => { - t.add(s); - }); - }), - t - ); -} -function z(e) { - let t = e.__REACT_DEVTOOLS_GLOBAL_HOOK__; - if (t === void 0) { - let s = 0; - e.__REACT_DEVTOOLS_GLOBAL_HOOK__ = t = { - renderers: new Map(), - supportsFiber: !0, - inject(r) { - return s++; - }, - onScheduleFiberRoot(r, h, d) {}, - onCommitFiberRoot(r, h, d, f) {}, - onCommitFiberUnmount() {}, - }; - } - if (t.isDisabled) { - console.warn( - "Something has shimmed the React DevTools global hook (__REACT_DEVTOOLS_GLOBAL_HOOK__). Fast Refresh is not compatible with this shim and will be disabled.", - ); - return; - } - const n = t.inject; - (t.inject = function (s) { - const r = n.apply(this, arguments); - return ( - typeof s.scheduleRefresh == "function" && - typeof s.setRefreshHandler == "function" && - b.set(r, s), - r - ); - }), - t.renderers.forEach((s, r) => { - typeof s.scheduleRefresh == "function" && - typeof s.setRefreshHandler == "function" && - b.set(r, s); - }); - const o = t.onCommitFiberRoot, - l = t.onScheduleFiberRoot || (() => {}); - (t.onScheduleFiberRoot = function (s, r, h) { - return ( - S || (p.delete(r), R !== null && R.set(r, h)), l.apply(this, arguments) - ); - }), - (t.onCommitFiberRoot = function (s, r, h, d) { - const f = b.get(s); - if (f !== void 0) { - w.set(r, f); - const i = r.current, - u = i.alternate; - if (u !== null) { - const a = - u.memoizedState != null && - u.memoizedState.element != null && - c.has(r), - y = i.memoizedState != null && i.memoizedState.element != null; - !a && y - ? (c.add(r), p.delete(r)) - : (a && y) || - (a && !y - ? (c.delete(r), d ? p.add(r) : w.delete(r)) - : !a && !y && d && p.add(r)); - } else c.add(r); - } - return o.apply(this, arguments); - }); -} -function G() { - return !1; -} -function N() { - return c.size; -} -function U() { - let e, - t, - n = !1; - return function (o, l, s, r) { - if (typeof l == "string") - return ( - e || ((e = o), (t = typeof r == "function")), - o != null && - (typeof o == "function" || typeof o == "object") && - E(o, l, s, r), - o - ); - !n && t && ((n = !0), A(e)); - }; -} -function V(e) { - switch (typeof e) { - case "function": { - if (e.prototype != null) { - if (e.prototype.isReactComponent) return !0; - const n = Object.getOwnPropertyNames(e.prototype); - if ( - n.length > 1 || - n[0] !== "constructor" || - e.prototype.__proto__ !== Object.prototype - ) - return !1; - } - const t = e.name || e.displayName; - return typeof t == "string" && /^[A-Z]/.test(t); - } - case "object": { - if (e != null) - switch (H(e, "$$typeof")) { - case F: - case C: - return !0; - default: - return !1; - } - return !1; - } - default: - return !1; - } -} -export { - N as _getMountedRootCount, - A as collectCustomHooksForSignature, - U as createSignatureFunctionForTransform, - x as findAffectedHostInstances, - $ as getFamilyByID, - W as getFamilyByType, - G as hasUnrecoverableErrors, - z as injectIntoGlobalHook, - V as isLikelyComponentType, - j as performReactRefresh, - K as register, - E as setSignature, -}; diff --git a/src/renamer.zig b/src/renamer.zig index 3f3b5725463c2..30c6a21ecf22a 100644 --- a/src/renamer.zig +++ b/src/renamer.zig @@ -35,7 +35,7 @@ pub const NoOpRenamer = struct { if (renamer.symbols.getConst(resolved)) |symbol| { return symbol.original_name; } else { - Global.panic("Invalid symbol {s} in {s}", .{ ref, renamer.source.path.text }); + Output.panic("Invalid symbol {s} in {s}", .{ ref, renamer.source.path.text }); } } @@ -383,6 +383,7 @@ pub fn assignNestedScopeSlots(allocator: std.mem.Allocator, module_scope: *js_as pub fn assignNestedScopeSlotsHelper(sorted_members: *std.ArrayList(u32), scope: *js_ast.Scope, symbols: []js_ast.Symbol, slot_to_copy: js_ast.SlotCounts) js_ast.SlotCounts { var slot = slot_to_copy; + // Sort member map keys for determinism { sorted_members.clearRetainingCapacity(); diff --git a/src/resolver/package_json.zig b/src/resolver/package_json.zig index c74338ecf7ab3..baa90d2f52410 100644 --- a/src/resolver/package_json.zig +++ b/src/resolver/package_json.zig @@ -37,7 +37,6 @@ const FolderResolver = @import("../install/resolvers/folder_resolver.zig"); const Architecture = @import("../install/npm.zig").Architecture; const OperatingSystem = @import("../install/npm.zig").OperatingSystem; -pub const SideEffectsMap = std.HashMapUnmanaged(bun.StringHashMapUnowned.Key, void, bun.StringHashMapUnowned.Adapter, 80); pub const DependencyMap = struct { map: HashMap = .{}, source_buf: []const u8 = "", @@ -57,6 +56,8 @@ pub const PackageJSON = struct { production, }; + pub usingnamespace bun.New(@This()); + pub fn generateHash(package_json: *PackageJSON) void { var hashy: [1024]u8 = undefined; @memset(&hashy, 0); @@ -112,11 +113,7 @@ pub const PackageJSON = struct { package_manager_package_id: Install.PackageID = Install.invalid_package_id, dependencies: DependencyMap = .{}, - side_effects: union(enum) { - unspecified: void, - false: void, - map: SideEffectsMap, - } = .{ .unspecified = {} }, + side_effects: SideEffects = .unspecified, // Present if the "browser" field is present. This field is intended to be // used by bundlers and lets you redirect the paths of certain 3rd-party @@ -148,6 +145,33 @@ pub const PackageJSON = struct { exports: ?ExportsMap = null, imports: ?ExportsMap = null, + pub const SideEffects = union(enum) { + /// either `package.json` is missing "sideEffects", it is true, or some + /// other unsupported value. Treat all files as side effects + unspecified: void, + /// "sideEffects": false + false: void, + /// "sideEffects": ["file.js", "other.js"] + map: Map, + // /// "sideEffects": ["side_effects/*.js"] + // glob: TODO, + + pub const Map = std.HashMapUnmanaged( + bun.StringHashMapUnowned.Key, + void, + bun.StringHashMapUnowned.Adapter, + 80, + ); + + pub fn hasSideEffects(side_effects: SideEffects, path: []const u8) bool { + return switch (side_effects) { + .unspecified => true, + .false => false, + .map => |map| map.contains(bun.StringHashMapUnowned.Key.init(path)), + }; + } + }; + pub inline fn isAppPackage(this: *const PackageJSON) bool { return this.hash == 0xDEADBEEF; } @@ -776,7 +800,7 @@ pub const PackageJSON = struct { } else if (side_effects_field.asArray()) |array_| { var array = array_; // TODO: switch to only storing hashes - var map = SideEffectsMap{}; + var map = SideEffects.Map{}; map.ensureTotalCapacity(allocator, array.array.items.len) catch unreachable; while (array.next()) |item| { if (item.asString(allocator)) |name| { @@ -850,34 +874,30 @@ pub const PackageJSON = struct { } } if (json.get("cpu")) |os_field| { - var first = true; if (os_field.asArray()) |array_const| { var array = array_const; + var arch = Architecture.none.negatable(); while (array.next()) |item| { if (item.asString(bun.default_allocator)) |str| { - if (first) { - package_json.arch = Architecture.none; - first = false; - } - package_json.arch = package_json.arch.apply(str); + arch.apply(str); } } + + package_json.arch = arch.combine(); } } if (json.get("os")) |os_field| { - var first = true; var tmp = os_field.asArray(); if (tmp) |*array| { + var os = OperatingSystem.none.negatable(); while (array.next()) |item| { if (item.asString(bun.default_allocator)) |str| { - if (first) { - package_json.os = OperatingSystem.none; - first = false; - } - package_json.os = package_json.os.apply(str); + os.apply(str); } } + + package_json.os = os.combine(); } } diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index 332dffab8e0c7..6a9f7fb992206 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -74,7 +74,13 @@ pub fn isParentOrEqual(parent_: []const u8, child: []const u8) ParentEqual { while (parent.len > 0 and isSepAny(parent[parent.len - 1])) { parent = parent[0 .. parent.len - 1]; } - if (std.mem.indexOf(u8, child, parent) != 0) return .unrelated; + + const contains = if (comptime !bun.Environment.isLinux) + strings.containsCaseInsensitiveASCII + else + strings.contains; + if (!contains(child, parent)) return .unrelated; + if (child.len == parent.len) return .equal; if (isSepAny(child[parent.len])) return .parent; return .unrelated; @@ -299,7 +305,7 @@ pub fn longestCommonPathPosix(input: []const []const u8) []const u8 { return longestCommonPathGeneric(input, .posix); } -threadlocal var relative_to_common_path_buf: [4096]u8 = undefined; +threadlocal var relative_to_common_path_buf: bun.PathBuffer = undefined; /// Find a relative path from a common path // Loosely based on Node.js' implementation of path.relative @@ -454,7 +460,7 @@ pub fn relativeToCommonPath( return out_slice; } -pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { +pub fn relativeNormalizedBuf(buf: []u8, from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { if ((if (platform == .windows) strings.eqlCaseInsensitiveASCII(from, to, true) else @@ -466,7 +472,11 @@ pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: P const two = [_][]const u8{ from, to }; const common_path = longestCommonPathGeneric(&two, platform); - return relativeToCommonPath(common_path, from, to, &relative_to_common_path_buf, always_copy, platform); + return relativeToCommonPath(common_path, from, to, buf, always_copy, platform); +} + +pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { + return relativeNormalizedBuf(&relative_to_common_path_buf, from, to, platform, always_copy); } pub fn dirname(str: []const u8, comptime platform: Platform) []const u8 { @@ -507,7 +517,17 @@ pub fn relative(from: []const u8, to: []const u8) []const u8 { return relativePlatform(from, to, .auto, false); } -pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { +pub fn relativeZ(from: []const u8, to: []const u8) [:0]const u8 { + return relativeBufZ(&relative_to_common_path_buf, from, to, .auto, true); +} + +pub fn relativeBufZ(buf: []u8, from: []const u8, to: []const u8) [:0]const u8 { + const rel = relativePlatformBuf(buf, from, to, .auto, true); + buf[rel.len] = 0; + return buf[0..rel.len :0]; +} + +pub fn relativePlatformBuf(buf: []u8, from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { const normalized_from = if (platform.isAbsolute(from)) brk: { if (platform == .loose and bun.Environment.isWindows) { // we want to invoke the windows resolution behavior but end up with a @@ -548,7 +568,11 @@ pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Pla platform, ); - return relativeNormalized(normalized_from, normalized_to, platform, always_copy); + return relativeNormalizedBuf(buf, normalized_from, normalized_to, platform, always_copy); +} + +pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { + return relativePlatformBuf(&relative_to_common_path_buf, from, to, platform, always_copy); } pub fn relativeAlloc(allocator: std.mem.Allocator, from: []const u8, to: []const u8) ![]const u8 { @@ -590,10 +614,10 @@ fn windowsVolumeNameLenT(comptime T: type, path: []const T) struct { usize, usiz } } } else { - if (bun.strings.indexAnyComptimeT(T, path[3..], comptime strings.literal(T, "/\\"))) |idx| { + if (bun.strings.indexAnyComptimeT(T, path[3..], strings.literal(T, "/\\"))) |idx| { // TODO: handle input "//abc//def" should be picked up as a unc path if (path.len > idx + 4 and !Platform.windows.isSeparatorT(T, path[idx + 4])) { - if (bun.strings.indexAnyComptimeT(T, path[idx + 4 ..], comptime strings.literal(T, "/\\"))) |idx2| { + if (bun.strings.indexAnyComptimeT(T, path[idx + 4 ..], strings.literal(T, "/\\"))) |idx2| { return .{ idx + idx2 + 4, idx + 3 }; } else { return .{ path.len, idx + 3 }; @@ -663,7 +687,7 @@ pub fn windowsFilesystemRootT(comptime T: type, path: []const T) []const T { { if (bun.strings.indexAnyComptimeT(T, path[3..], "/\\")) |idx| { if (bun.strings.indexAnyComptimeT(T, path[4 + idx ..], "/\\")) |idx_second| { - return path[0 .. idx + idx_second + 4]; + return path[0 .. idx + idx_second + 4 + 1]; // +1 to skip second separator } return path[0..]; } @@ -743,7 +767,7 @@ pub fn normalizeStringGenericTZ( // // since it is theoretically possible to get here in release // we will not do this check in release. - assert(!strings.hasPrefixComptimeType(T, path_, comptime strings.literal(T, ":\\"))); + assert(!strings.hasPrefixComptimeType(T, path_, strings.literal(T, ":\\"))); } var buf_i: usize = 0; @@ -758,16 +782,16 @@ pub fn normalizeStringGenericTZ( if (isWindows and !options.allow_above_root) { if (volLen > 0) { if (options.add_nt_prefix) { - @memcpy(buf[buf_i .. buf_i + 4], comptime strings.literal(T, "\\??\\")); + @memcpy(buf[buf_i .. buf_i + 4], strings.literal(T, "\\??\\")); buf_i += 4; } if (path_[1] != ':') { // UNC paths if (options.add_nt_prefix) { - @memcpy(buf[buf_i .. buf_i + 4], comptime strings.literal(T, "UNC" ++ sep_str)); + @memcpy(buf[buf_i .. buf_i + 4], strings.literal(T, "UNC" ++ sep_str)); buf_i += 2; } else { - @memcpy(buf[buf_i .. buf_i + 2], comptime strings.literal(T, sep_str ++ sep_str)); + @memcpy(buf[buf_i .. buf_i + 2], strings.literal(T, sep_str ++ sep_str)); } @memcpy(buf[buf_i + 2 .. buf_i + indexOfThirdUNCSlash + 1], path_[2 .. indexOfThirdUNCSlash + 1]); buf[buf_i + indexOfThirdUNCSlash] = options.separator; @@ -809,7 +833,7 @@ pub fn normalizeStringGenericTZ( if (isWindows and options.allow_above_root) { if (path_.len >= 2 and path_[1] == ':') { if (options.add_nt_prefix) { - @memcpy(buf[buf_i .. buf_i + 4], &comptime strings.literalBuf(T, "\\??\\")); + @memcpy(buf[buf_i .. buf_i + 4], &strings.literalBuf(T, "\\??\\")); buf_i += 4; } buf[buf_i] = path_[0]; @@ -866,10 +890,10 @@ pub fn normalizeStringGenericTZ( } } else if (options.allow_above_root) { if (buf_i > buf_start) { - buf[buf_i..][0..3].* = (comptime strings.literal(T, sep_str ++ "..")).*; + buf[buf_i..][0..3].* = (strings.literal(T, sep_str ++ "..")).*; buf_i += 3; } else { - buf[buf_i..][0..2].* = (comptime strings.literal(T, "..")).*; + buf[buf_i..][0..2].* = (strings.literal(T, "..")).*; buf_i += 2; } dotdot = buf_i; @@ -914,7 +938,7 @@ pub fn normalizeStringGenericTZ( const result = if (options.zero_terminate) buf[0..buf_i :0] else buf[0..buf_i]; if (bun.Environment.allow_assert and isWindows) { - assert(!strings.hasPrefixComptimeType(T, result, comptime strings.literal(T, "\\:\\"))); + assert(!strings.hasPrefixComptimeType(T, result, strings.literal(T, "\\:\\"))); } return result; @@ -1598,7 +1622,7 @@ pub fn lastIndexOfSeparatorWindows(slice: []const u8) ?usize { } pub fn lastIndexOfSeparatorWindowsT(comptime T: type, slice: []const T) ?usize { - return std.mem.lastIndexOfAny(T, slice, comptime strings.literal(T, "\\/")); + return std.mem.lastIndexOfAny(T, slice, strings.literal(T, "\\/")); } pub fn lastIndexOfSeparatorPosix(slice: []const u8) ?usize { diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index e9da354bfaa57..11052982e723a 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -114,6 +114,7 @@ const bufs = struct { pub threadlocal var path_in_global_disk_cache: bun.PathBuffer = undefined; pub threadlocal var abs_to_rel: bun.PathBuffer = undefined; pub threadlocal var node_modules_paths_buf: bun.PathBuffer = undefined; + pub threadlocal var import_path_for_standalone_module_graph: bun.PathBuffer = undefined; pub inline fn bufs(comptime field: std.meta.DeclEnum(@This())) *@TypeOf(@field(@This(), @tagName(field))) { return &@field(@This(), @tagName(field)); @@ -171,9 +172,9 @@ pub const SideEffects = enum { /// known to not have side effects. no_side_effects__pure_data, - // / Same as above but it came from a plugin. We don't want to warn about - // / unused imports to these files since running the plugin is a side effect. - // / Removing the import would not call the plugin which is observable. + // /// Same as above but it came from a plugin. We don't want to warn about + // /// unused imports to these files since running the plugin is a side effect. + // /// Removing the import would not call the plugin which is observable. // no_side_effects__pure_data_from_plugin, }; @@ -453,7 +454,7 @@ var resolver_Mutex_loaded: bool = false; const BinFolderArray = std.BoundedArray(string, 128); var bin_folders: BinFolderArray = undefined; -var bin_folders_lock: Mutex = Mutex.init(); +var bin_folders_lock: Mutex = .{}; var bin_folders_loaded: bool = false; const Timer = @import("../system_timer.zig").Timer; @@ -563,7 +564,7 @@ pub const Resolver = struct { pub fn getPackageManager(this: *Resolver) *PackageManager { return this.package_manager orelse brk: { - bun.HTTPThread.init() catch unreachable; + bun.HTTPThread.init(); const pm = PackageManager.initWithRuntime( this.log, this.opts.install, @@ -606,7 +607,7 @@ pub const Resolver = struct { opts: options.BundleOptions, ) ThisResolver { if (!resolver_Mutex_loaded) { - resolver_Mutex = Mutex.init(); + resolver_Mutex = .{}; resolver_Mutex_loaded = true; } @@ -625,6 +626,9 @@ pub const Resolver = struct { } pub fn isExternalPattern(r: *ThisResolver, import_path: string) bool { + if (r.opts.packages == .external and isPackagePath(import_path)) { + return true; + } for (r.opts.external.patterns) |pattern| { if (import_path.len >= pattern.prefix.len + pattern.suffix.len and (strings.startsWith( import_path, @@ -658,9 +662,8 @@ pub const Resolver = struct { comptime preference: PackageJSON.LoadFramework, comptime load_defines: bool, ) !void { - // We want to enable developers to integrate frameworks without waiting on official support. - // But, we still want the command to do the actual framework integration to be succint + // But, we still want the command to do the actual framework integration to be succinct // This lets users type "--use next" instead of "--use bun-framework-next" // If they're using a local file path, we skip this. if (isPackagePath(package)) { @@ -797,6 +800,26 @@ pub const Resolver = struct { const tracer = bun.tracy.traceNamed(@src(), "ModuleResolver.resolve"); defer tracer.end(); + // Only setting 'current_action' in debug mode because module resolution + // is done very often, and has a very low crash rate. + const prev_action = if (Environment.isDebug) bun.crash_handler.current_action; + if (Environment.isDebug) bun.crash_handler.current_action = .{ .resolver = .{ + .source_dir = source_dir, + .import_path = import_path, + .kind = kind, + } }; + defer if (Environment.isDebug) { + bun.crash_handler.current_action = prev_action; + }; + + if (Environment.isDebug and bun.CLI.debug_flags.hasResolveBreakpoint(import_path)) { + bun.Output.debug("Resolving {s} from {s}", .{ + import_path, + source_dir, + }); + @breakpoint(); + } + const original_order = r.extension_order; defer r.extension_order = original_order; r.extension_order = switch (kind) { @@ -843,8 +866,10 @@ pub const Resolver = struct { } } - // Certain types of URLs default to being external for convenience - if (r.isExternalPattern(import_path) or + // Certain types of URLs default to being external for convenience, + // while these rules should not be applied to the entrypoint as it is never external (#12734) + if (kind != .entry_point and + (r.isExternalPattern(import_path) or // "fill: url(#filter);" (kind.isFromCSS() and strings.startsWith(import_path, "#")) or @@ -855,7 +880,7 @@ pub const Resolver = struct { strings.startsWith(import_path, "https://") or // "background: url(//example.com/images/image.png);" - strings.startsWith(import_path, "//")) + strings.startsWith(import_path, "//"))) { if (r.debug_logs) |*debug| { debug.addNote("Marking this path as implicitly external"); @@ -909,11 +934,13 @@ pub const Resolver = struct { // relative to our special /$bunfs/ directory. // // It's always relative to the current working directory of the project root. + // + // ...unless you pass a relative path that exists in the standalone module graph executable. var source_dir_resolver: bun.path.PosixToWinNormalizer = .{}; const source_dir_normalized = brk: { if (r.standalone_module_graph) |graph| { if (bun.StandaloneModuleGraph.isBunStandaloneFilePath(import_path)) { - if (graph.files.contains(import_path)) { + if (graph.findAssumeStandalonePath(import_path) != null) { return .{ .success = Result{ .import_kind = kind, @@ -928,6 +955,24 @@ pub const Resolver = struct { return .{ .not_found = {} }; } else if (bun.StandaloneModuleGraph.isBunStandaloneFilePath(source_dir)) { + if (import_path.len > 2 and isDotSlash(import_path[0..2])) { + const buf = bufs(.import_path_for_standalone_module_graph); + const joined = bun.path.joinAbsStringBuf(source_dir, buf, &.{import_path}, .loose); + + // Support relative paths in the graph + if (graph.findAssumeStandalonePath(joined)) |file| { + return .{ + .success = Result{ + .import_kind = kind, + .path_pair = PathPair{ + .primary = Path.init(file.name), + }, + .is_standalone_module = true, + .module_type = .esm, + }, + }; + } + } break :brk Fs.FileSystem.instance.top_level_dir; } } @@ -1133,13 +1178,13 @@ pub const Resolver = struct { pub fn resolveWithoutSymlinks( r: *ThisResolver, source_dir: string, - import_path_: string, + input_import_path: string, kind: ast.ImportKind, global_cache: GlobalCache, ) Result.Union { assert(std.fs.path.isAbsolute(source_dir)); - var import_path = import_path_; + var import_path = input_import_path; // This implements the module resolution algorithm from node.js, which is // described here: https://nodejs.org/api/modules.html#modules_all_together @@ -1365,7 +1410,10 @@ pub const Resolver = struct { } // Check for external packages first - if (r.opts.external.node_modules.count() > 0) { + if (r.opts.external.node_modules.count() > 0 and + // Imports like "process/" need to resolve to the filesystem, not a builtin + !strings.hasSuffixComptime(import_path, "/")) + { var query = import_path; while (true) { if (r.opts.external.node_modules.contains(query)) { @@ -2471,7 +2519,7 @@ pub const Resolver = struct { const source = logger.Source.initPathString(key_path.text, entry.contents); const file_dir = source.path.sourceDir(); - var result = (try TSConfigJSON.parse(bun.fs_allocator, r.log, source, &r.caches.json)) orelse return null; + var result = (try TSConfigJSON.parse(bun.default_allocator, r.log, source, &r.caches.json)) orelse return null; if (result.hasBaseURL()) { @@ -2531,9 +2579,7 @@ pub const Resolver = struct { ) orelse return null; } - const _pkg = try bun.default_allocator.create(PackageJSON); - _pkg.* = pkg; - return _pkg; + return PackageJSON.new(pkg); } fn dirInfoCached( @@ -3333,7 +3379,7 @@ pub const Resolver = struct { }; } - pub export fn Resolver__nodeModulePathsForJS(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(.C) bun.JSC.JSValue { + pub export fn Resolver__nodeModulePathsForJS(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { bun.JSC.markBinding(@src()); const argument: bun.JSC.JSValue = callframe.argument(0); @@ -3348,7 +3394,7 @@ pub const Resolver = struct { return nodeModulePathsJSValue(r, in_str, globalThis); } - pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) bun.JSC.JSValue { + pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) JSC.JSValue { bun.JSC.markBinding(@src()); const in_str = bun.String.createUTF8("."); @@ -3826,16 +3872,21 @@ pub const Resolver = struct { // https://github.com/microsoft/TypeScript/issues/4595 if (strings.lastIndexOfChar(base, '.')) |last_dot| { const ext = base[last_dot..base.len]; - if ((strings.eqlComptime(ext, ".js") or strings.eqlComptime(ext, ".jsx") and (!FeatureFlags.disable_auto_js_to_ts_in_node_modules or !strings.pathContainsNodeModulesFolder(path)))) { + if ((strings.eqlComptime(ext, ".js") or strings.eqlComptime(ext, ".jsx") or strings.eqlComptime(ext, ".mjs") and + (!FeatureFlags.disable_auto_js_to_ts_in_node_modules or !strings.pathContainsNodeModulesFolder(path)))) + { const segment = base[0..last_dot]; var tail = bufs(.load_as_file)[path.len - base.len ..]; bun.copy(u8, tail, segment); - const exts = .{ ".ts", ".tsx" }; + const exts: []const string = if (strings.eqlComptime(ext, ".mjs")) + &.{".mts"} + else + &.{ ".ts", ".tsx", ".mts" }; - inline for (exts) |ext_to_replace| { + for (exts) |ext_to_replace| { var buffer = tail[0 .. segment.len + ext_to_replace.len]; - buffer[segment.len..buffer.len][0..ext_to_replace.len].* = ext_to_replace.*; + @memcpy(buffer[segment.len..buffer.len][0..ext_to_replace.len], ext_to_replace); if (entries.get(buffer)) |query| { if (query.entry.kind(rfs, r.store_fd) == .file) { diff --git a/src/resolver/tsconfig_json.zig b/src/resolver/tsconfig_json.zig index bc4c96927fae3..22dd62cdc6659 100644 --- a/src/resolver/tsconfig_json.zig +++ b/src/resolver/tsconfig_json.zig @@ -57,6 +57,7 @@ pub const TSConfigJSON = struct { emit_decorator_metadata: bool = false, + pub usingnamespace bun.New(@This()); pub fn hasBaseURL(tsconfig: *const TSConfigJSON) bool { return tsconfig.base_url.len > 0; } @@ -67,7 +68,7 @@ pub const TSConfigJSON = struct { remove, invalid, - pub const List = std.StaticStringMap(ImportsNotUsedAsValue).initComptime(.{ + pub const List = bun.ComptimeStringMap(ImportsNotUsedAsValue, .{ .{ "preserve", .preserve }, .{ "error", .err }, .{ "remove", .remove }, @@ -323,13 +324,7 @@ pub const TSConfigJSON = struct { assert(result.base_url.len > 0); } - const _result = allocator.create(TSConfigJSON) catch unreachable; - _result.* = result; - - if (Environment.isDebug and has_base_url) { - assert(_result.base_url.len > 0); - } - return _result; + return TSConfigJSON.new(result); } pub fn isValidTSConfigPathPattern(text: string, log: *logger.Log, source: *const logger.Source, loc: logger.Loc, allocator: std.mem.Allocator) bool { diff --git a/src/runtime.js b/src/runtime.js index 460a1768f7e29..b311e9f15f492 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -1,17 +1,17 @@ -var tagSymbol; -var cjsRequireSymbol; +// Since runtime.js loads first in the bundler, Ref.none will point at this +// value. And since it isnt exported, it will always be tree-shaken away. +var __INVALID__REF__; + +// This ordering is deliberate so that the printer optimizes +// them into a single destructuring assignment. var __create = Object.create; var __descs = Object.getOwnPropertyDescriptors; -var __defProp = Object.defineProperty; var __getProtoOf = Object.getPrototypeOf; +var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - -// This order is deliberate so that the printer does the {} optimization here var __hasOwnProp = Object.prototype.hasOwnProperty; -export var __markAsModule = target => __defProp(target, "__esModule", { value: true, configurable: true }); - // This is used to implement "export * from" statements. It copies properties // from the imported module to the current module's ESM export object. If the // current module is an entry point and the target format is CommonJS, we @@ -63,122 +63,28 @@ export var __toESM = (mod, isNodeMode, target) => { // Converts the module from ESM to CommonJS. This clones the input module // object with the addition of a non-enumerable "__esModule" property set // to "true", which overwrites any existing export named "__esModule". +var __moduleCache = /* @__PURE__ */ new WeakMap(); export var __toCommonJS = /* @__PURE__ */ from => { - const moduleCache = (__toCommonJS.moduleCache ??= new WeakMap()); - var cached = moduleCache.get(from); - if (cached) return cached; - var to = __defProp({}, "__esModule", { value: true }); - var desc = { enumerable: false }; + var entry = __moduleCache.get(from), + desc; + if (entry) return entry; + entry = __defProp({}, "__esModule", { value: true }); if ((from && typeof from === "object") || typeof from === "function") - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key)) - __defProp(to, key, { + __getOwnPropNames(from).map( + key => + !__hasOwnProp.call(entry, key) && + __defProp(entry, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable, - }); - - moduleCache.set(from, to); - return to; -}; - -// lazy require to prevent loading one icon from a design system -export var $$lzy = (target, mod, props) => { - for (let key in props) { - if (!__hasOwnProp.call(target, key)) - __defProp(target, key, { - get: () => mod()[props[key]], - enumerable: true, - configurable: true, - }); - } - return target; + }), + ); + __moduleCache.set(from, entry); + return entry; }; // When you do know the module is CJS export var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); -// When you don't know if the module is going to be ESM or CJS -export var __cJS2eSM = (cb, name) => { - var mod; - var origExports; - var has_run = false; - tagSymbol ??= Symbol.for("CommonJSTransformed"); - cjsRequireSymbol ??= Symbol.for("CommonJS"); - - const requireFunction = function load() { - if (has_run) { - return mod.exports; - } - - has_run = true; - cb(((mod = { exports: {} }), mod), mod.exports); - - var mod_exports = (origExports = mod.exports); - - const kind = typeof mod_exports; - - if ((kind === "object" || kind === "function") && !mod_exports[tagSymbol]) { - const extensible = Object.isExtensible(mod_exports); - if (!extensible) { - // slow path: it's a function we need to wrap - // example: webpack - if (kind === "function") { - mod_exports = function () { - return origExports.apply(this, arguments); - }; - Object.setPrototypeOf(mod_exports, __getProtoOf(origExports)); - Object.defineProperties(mod_exports, Object.getOwnPropertyDescriptors(origExports)); - } else { - mod_exports = __create(__getProtoOf(mod_exports), Object.getOwnPropertyDescriptors(mod_exports)); - } - } - - Object.defineProperty(mod_exports, tagSymbol, { - value: true, - enumerable: false, - configurable: false, - }); - - if (!("default" in mod_exports)) { - Object.defineProperty(mod_exports, "default", { - get() { - return origExports; - }, - set(v) { - if (v === mod.exports) return; - origExports = v; - return true; - }, - // enumerable: false is important here - enumerable: false, - configurable: true, - }); - } - - if (!extensible) { - // can only be frozen if it's not extensible - if (Object.isFrozen(origExports)) { - Object.freeze(mod_exports); - } else { - Object.preventExtensions(mod_exports); - } - } - } - - return mod_exports; - }; - - requireFunction[cjsRequireSymbol] = 1; - return requireFunction; -}; - -export var __internalIsCommonJSNamespace = /* @__PURE__ */ namespace => - namespace != null && - typeof namespace === "object" && - ((namespace.default && namespace.default[cjsRequireSymbol]) || namespace[cjsRequireSymbol]); - -export var $$m = __commonJS; - export var __name = (target, name) => { Object.defineProperty(target, "name", { value: name, @@ -222,12 +128,12 @@ export var __exportDefault = (target, value) => { }); }; -function hasAnyProps(obj) { +function __hasAnyProps(obj) { for (let key in obj) return true; return false; } -function mergeDefaultProps(props, defaultProps) { +function __mergeDefaultProps(props, defaultProps) { var result = __create(defaultProps, __descs(props)); for (let key in defaultProps) { @@ -238,11 +144,11 @@ function mergeDefaultProps(props, defaultProps) { return result; } export var __merge = (props, defaultProps) => { - return !hasAnyProps(defaultProps) + return !__hasAnyProps(defaultProps) ? props - : !hasAnyProps(props) + : !__hasAnyProps(props) ? defaultProps - : mergeDefaultProps(props, defaultProps); + : __mergeDefaultProps(props, defaultProps); }; export var __legacyDecorateClassTS = function (decorators, target, key, desc) { diff --git a/src/runtime.zig b/src/runtime.zig index 8892cad44404c..d42d657c7d03e 100644 --- a/src/runtime.zig +++ b/src/runtime.zig @@ -52,8 +52,6 @@ pub const ErrorCSS = struct { } }; -pub const ReactRefresh = @embedFile("./react-refresh.js"); - pub const ErrorJS = struct { pub inline fn sourceContent() string { if (comptime Environment.isDebug) { @@ -117,7 +115,7 @@ pub const Fallback = struct { pub inline fn scriptContent() string { if (comptime Environment.isDebug) { - const dirpath = comptime bun.Environment.base_path ++ (bun.Dirname.dirname(u8, @src().file) orelse ""); + const dirpath = comptime bun.Environment.base_path ++ "/" ++ (bun.Dirname.dirname(u8, @src().file) orelse ""); var buf: bun.PathBuffer = undefined; const user = bun.getUserName(&buf) orelse ""; const dir = std.mem.replaceOwned( @@ -208,8 +206,17 @@ pub const Runtime = struct { } pub const Features = struct { + /// Enable the React Fast Refresh transform. What this does exactly + /// is documented in js_parser, search for `const ReactRefresh` react_fast_refresh: bool = false, + + /// `hot_module_reloading` is specific to if we are using bun.kit.DevServer. + /// It can be enabled on the command line with --format=internal_kit_dev + /// + /// Standalone usage of this flag / usage of this flag + /// without '--format' set is an unsupported use case. hot_module_reloading: bool = false, + is_macro_runtime: bool = false, top_level_await: bool = false, auto_import_jsx: bool = false, @@ -228,30 +235,19 @@ pub const Runtime = struct { set_breakpoint_on_first_line: bool = false, - /// Instead of jsx("div", {}, void 0) - /// -> - /// { - /// "type": "div", - /// "props": {}, - /// "children": [], - /// key: void 0, - /// $$typeof: Symbol.for("react.element"), - /// } - /// See also https://github.com/babel/babel/commit/3cad2872335e2130f2ff6335027617ebbe9b5a46 - /// See also https://github.com/babel/babel/pull/2972 - /// See also https://github.com/facebook/react/issues/5138 - jsx_optimization_inline: bool = false, - jsx_optimization_hoist: bool = false, - trim_unused_imports: bool = false, - should_fold_typescript_constant_expressions: bool = false, /// Use `import.meta.require()` instead of require()? - /// This is only supported in Bun. + /// This is only supported with --target=bun use_import_meta_require: bool = false, replace_exports: ReplaceableExport.Map = .{}, + /// Scan for '// @bun' at the top of this file, halting a parse if it is + /// seen. This is used in `bun run` after a `bun build --target=bun`, + /// and you know the contents is already correct. + /// + /// This comment must never be used manually. dont_bundle_twice: bool = false, /// This is a list of packages which even when require() is used, we will @@ -286,7 +282,6 @@ pub const Runtime = struct { .dead_code_elimination, .set_breakpoint_on_first_line, .trim_unused_imports, - .should_fold_typescript_constant_expressions, .use_import_meta_require, .dont_bundle_twice, .commonjs_at_runtime, @@ -327,29 +322,25 @@ pub const Runtime = struct { pub const ActivateFunction = "activate"; }; + /// See js_parser.StaticSymbolName pub const GeneratedSymbol = struct { primary: Ref, backup: Ref, ref: Ref, + + pub const empty: GeneratedSymbol = .{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None }; }; - // If you change this, remember to update "runtime.footer.js" and rebuild the runtime.js + // If you change this, remember to update "runtime.js" pub const Imports = struct { __name: ?GeneratedSymbol = null, - __toModule: ?GeneratedSymbol = null, - __cJS2eSM: ?GeneratedSymbol = null, __require: ?GeneratedSymbol = null, __export: ?GeneratedSymbol = null, __reExport: ?GeneratedSymbol = null, - __load: ?GeneratedSymbol = null, - @"$$m": ?GeneratedSymbol = null, - @"$$lzy": ?GeneratedSymbol = null, - __HMRModule: ?GeneratedSymbol = null, - __HMRClient: ?GeneratedSymbol = null, - __FastRefreshModule: ?GeneratedSymbol = null, __exportValue: ?GeneratedSymbol = null, __exportDefault: ?GeneratedSymbol = null, - __FastRefreshRuntime: ?GeneratedSymbol = null, + // __refreshRuntime: ?GeneratedSymbol = null, + // __refreshSig: ?GeneratedSymbol = null, // $RefreshSig$ __merge: ?GeneratedSymbol = null, __legacyDecorateClassTS: ?GeneratedSymbol = null, __legacyDecorateParamTS: ?GeneratedSymbol = null, @@ -359,23 +350,12 @@ pub const Runtime = struct { __callDispose: ?GeneratedSymbol = null, pub const all = [_][]const u8{ - // __HMRClient goes first - // This is so we can call Bun.activate(true) as soon as possible - "__HMRClient", "__name", - "__toModule", "__require", - "__cJS2eSM", "__export", "__reExport", - "__load", - "$$m", - "$$lzy", - "__HMRModule", - "__FastRefreshModule", "__exportValue", "__exportDefault", - "__FastRefreshRuntime", "__merge", "__legacyDecorateClassTS", "__legacyDecorateParamTS", @@ -430,7 +410,7 @@ pub const Runtime = struct { defer this.i += 1; switch (this.i) { - inline 0...21 => |t| { + inline 0...all.len - 1 => |t| { if (@field(this.runtime_imports, all[t])) |val| { return Entry{ .key = t, .value = val.ref }; } @@ -446,7 +426,7 @@ pub const Runtime = struct { }; pub fn iter(imports: *Imports) Iterator { - return Iterator{ .runtime_imports = imports }; + return .{ .runtime_imports = imports }; } pub fn contains(imports: *const Imports, comptime key: string) bool { @@ -479,7 +459,7 @@ pub const Runtime = struct { key: anytype, ) ?Ref { return switch (key) { - inline 0...21 => |t| (@field(imports, all[t]) orelse return null).ref, + inline 0...all.len - 1 => |t| (@field(imports, all[t]) orelse return null).ref, else => null, }; } diff --git a/src/runtime/errors.ts b/src/runtime/errors.ts index c5968e4228e35..830ed3881d819 100644 --- a/src/runtime/errors.ts +++ b/src/runtime/errors.ts @@ -76,4 +76,4 @@ var __ImportKind; __ImportKind = ImportKind; } -export { __ResolveLog as ResolveMessage, __BuildLog as BuildMessage, __ImportKind as ImportKind }; +export { __BuildLog as BuildMessage, __ImportKind as ImportKind, __ResolveLog as ResolveMessage }; diff --git a/src/runtime/hmr.ts b/src/runtime/hmr.ts index 5aff4389dc8a4..61dc9a93a5b38 100644 --- a/src/runtime/hmr.ts +++ b/src/runtime/hmr.ts @@ -1669,4 +1669,4 @@ if (typeof window !== "undefined") { globalThis["__BUN_ERROR"] = BunError; } -export { __HMRModule, __FastRefreshModule, __HMRClient, __injectFastRefresh }; +export { __FastRefreshModule, __HMRClient, __HMRModule, __injectFastRefresh }; diff --git a/src/runtime/index-with-refresh.ts b/src/runtime/index-with-refresh.ts index abea4c0f4c071..670caa8bf1ae9 100644 --- a/src/runtime/index-with-refresh.ts +++ b/src/runtime/index-with-refresh.ts @@ -1,8 +1,8 @@ // @ts-nocheck -export * from "./hmr"; +export * as __FastRefreshRuntime from "../react-refresh"; export * from "./errors"; +export * from "./hmr"; export * from "./index-without-hmr"; -export * as __FastRefreshRuntime from "../react-refresh"; globalThis.process ||= { env: {}, diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 3c35e361cec51..313abe8f5b142 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -1,6 +1,6 @@ // @ts-nocheck -export * from "./hmr"; export * from "./errors"; +export * from "./hmr"; export * from "./index-without-hmr"; globalThis.process ||= { diff --git a/src/sha.zig b/src/sha.zig index 0fac3db29c91a..9447f4bb7adf8 100644 --- a/src/sha.zig +++ b/src/sha.zig @@ -196,125 +196,3 @@ const labels = [_][]const u8{ "Blake2", "Blake3", }; -pub fn main() anyerror!void { - var file = try std.fs.cwd().openFileZ(bun.argv[bun.argv.len - 1], .{}); - const bytes = try file.readToEndAlloc(std.heap.c_allocator, std.math.maxInt(usize)); - - const engine = BoringSSL.ENGINE_new().?; - - std.debug.print( - "Hashing {any:3}\n\n", - .{bun.fmt.size(bytes.len)}, - ); - - { - var clock1 = try std.time.Timer.start(); - std.mem.doNotOptimizeAway(bun.hash(bytes)); - const zig_time = clock1.read(); - std.debug.print( - "Wyhash:\n\n zig: {any}\n\n", - .{std.fmt.fmtDuration(zig_time)}, - ); - } - - { - var clock1 = try std.time.Timer.start(); - std.mem.doNotOptimizeAway(std.hash.XxHash64.hash(0, bytes)); - const zig_time = clock1.read(); - std.debug.print( - "xxhash:\n\n zig: {any}\n\n", - .{std.fmt.fmtDuration(zig_time)}, - ); - } - - { - var clock1 = try std.time.Timer.start(); - std.mem.doNotOptimizeAway(std.hash.Murmur2_64.hash(bytes)); - const zig_time = clock1.read(); - std.debug.print( - "Murmur2_64:\n\n zig: {any}\n\n", - .{std.fmt.fmtDuration(zig_time)}, - ); - } - - inline for (evp, 0..) |BoringHasher, i| { - const ZigHasher = zig[i]; - std.debug.print( - comptime labels[i] ++ ":\n\n", - .{}, - ); - const DigestType = if (BoringHasher != void) BoringHasher.Digest else [32]u8; - var digest1 = std.mem.zeroes(DigestType); - var digest2 = std.mem.zeroes(DigestType); - var digest3 = std.mem.zeroes(DigestType); - var digest4 = std.mem.zeroes(DigestType); - defer { - std.mem.doNotOptimizeAway(&digest1); - std.mem.doNotOptimizeAway(&digest2); - std.mem.doNotOptimizeAway(&digest3); - std.mem.doNotOptimizeAway(&digest4); - } - - var clock1 = try std.time.Timer.start(); - ZigHasher.hash(bytes, &digest1, .{}); - const zig_time = clock1.read(); - - const boring_time = brk: { - if (BoringHasher != void) { - var clock2 = try std.time.Timer.start(); - BoringHasher.hash(bytes, &digest2, engine); - break :brk clock2.read(); - } else { - break :brk 0; - } - }; - - const evp_time: usize = brk: { - if (evp[i] != void) { - var clock3 = try std.time.Timer.start(); - evp[i].hash(bytes, &digest3, engine); - break :brk clock3.read(); - } - - break :brk 0; - }; - - const evp_in_time: usize = brk: { - if (evp[i] != void) { - var evp_in = evp[i].init(); - var clock4 = try std.time.Timer.start(); - evp_in.update(bytes); - evp_in.final(&digest4); - break :brk clock4.read(); - } - - break :brk 0; - }; - - std.debug.print( - " zig: {}\n", - .{std.fmt.fmtDuration(zig_time)}, - ); - - if (boring_time > 0) - std.debug.print( - " boring: {}\n", - .{std.fmt.fmtDuration(boring_time)}, - ); - if (evp_time > 0) - std.debug.print( - " evp: {}\n", - .{std.fmt.fmtDuration(evp_time)}, - ); - - if (evp_in_time > 0) - std.debug.print( - " evp in: {}\n\n", - .{std.fmt.fmtDuration(evp_in_time)}, - ); - - if (!std.mem.eql(u8, &digest3, &digest2)) { - @panic("\ndigests don't match! for " ++ labels[i]); - } - } -} diff --git a/src/shell/interpreter.zig b/src/shell/interpreter.zig index d5c0eca5465cc..dcf6ccb95f58c 100644 --- a/src/shell/interpreter.zig +++ b/src/shell/interpreter.zig @@ -67,6 +67,33 @@ const log = bun.Output.scoped(.SHELL, false); const assert = bun.assert; +/// This is a zero-sized type returned by `.needsIO()`, designed to ensure +/// functions which rely on IO are not called when they do don't need it. +/// +/// For example the .enqueue(), .enqueueFmtBltn(), etc functions. +/// +/// It is used like this: +/// +/// ```zig +/// if (this.bltn.stdout.needsIO()) |safeguard| { +/// this.bltn.stdout.enqueue(this, chunk, safeguard); +/// return .cont; +/// } +// _ = this.bltn.writeNoIO(.stdout, chunk); +/// ``` +/// +/// The compiler optimizes away this type so it has zero runtime cost. +/// +/// You should never instantiate this type directly, unless you know +/// from previous context that the output needs IO. +/// +/// Functions which accept a `_: OutputNeedsIOSafeGuard` parameter can +/// safely assume the stdout/stderr they are working with require IO. +pub const OutputNeedsIOSafeGuard = struct { + /// Dummy zero sized field to prevent it from being trivial to create this type (by doing `.{}`) + __i_know_what_i_am_doing_it_needs_io_yes: u0, +}; + pub const ExitCode = u16; pub const StateKind = enum(u8) { @@ -313,8 +340,8 @@ pub const IO = struct { comptime kind: ?Interpreter.Builtin.Kind, comptime fmt_: []const u8, args: anytype, + _: OutputNeedsIOSafeGuard, ) void { - if (bun.Environment.allow_assert) assert(this.* == .fd); this.fd.writer.enqueueFmtBltn(ptr, this.fd.captured, kind, fmt_, args); } @@ -664,7 +691,7 @@ pub const ParsedShellScript = struct { pub fn finalize( this: *ParsedShellScript, - ) callconv(.C) void { + ) void { this.this_jsvalue = .zero; log("ParsedShellScript(0x{x}) finalize", .{@intFromPtr(this)}); if (this.export_env) |*env| env.deinit(); @@ -676,7 +703,7 @@ pub const ParsedShellScript = struct { bun.destroy(this); } - pub fn setCwd(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setCwd(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const arguments_ = callframe.arguments(2); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); const str_js = arguments.nextEat() orelse { @@ -688,13 +715,13 @@ pub const ParsedShellScript = struct { return .undefined; } - pub fn setQuiet(this: *ParsedShellScript, _: *JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setQuiet(this: *ParsedShellScript, _: *JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { log("Interpreter(0x{x}) setQuiet()", .{@intFromPtr(this)}); this.quiet = true; return .undefined; } - pub fn setEnv(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setEnv(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { var env = if (this.export_env) |*env| brk: { @@ -741,7 +768,7 @@ pub const ParsedShellScript = struct { pub fn createParsedShellScript( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { var shargs = ShellArgs.init(); const arguments_ = callframe.arguments(2); @@ -836,8 +863,10 @@ pub const Interpreter = struct { root_shell: ShellState, root_io: IO, - has_pending_activity: std.atomic.Value(usize) = std.atomic.Value(usize).init(0), + has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), started: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), + // Necessary for builtin commands. + keep_alive: bun.Async.KeepAlive = .{}, vm_args_utf8: std.ArrayList(JSC.ZigString.Slice), async_commands_executing: u32 = 0, @@ -1151,7 +1180,7 @@ pub const Interpreter = struct { fn toJSC(this: ShellErrorCtx, globalThis: *JSGlobalObject) JSValue { return switch (this) { .syscall => |err| err.toJSC(globalThis), - .other => |err| bun.JSC.ZigString.fromBytes(@errorName(err)).toValueGC(globalThis), + .other => |err| bun.JSC.ZigString.fromBytes(@errorName(err)).toJS(globalThis), }; } }; @@ -1159,7 +1188,7 @@ pub const Interpreter = struct { pub fn createShellInterpreter( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSValue { + ) JSValue { const allocator = bun.default_allocator; const arguments_ = callframe.arguments(3); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); @@ -1225,19 +1254,29 @@ pub const Interpreter = struct { if (cwd) |*cc| cc.deref(); shargs.deinit(); throwShellErr(e, .{ .js = globalThis.bunVM().event_loop }); - return .undefined; + return .zero; }, }; - interpreter.flags.quiet = quiet; + if (globalThis.hasException()) { + jsobjs.deinit(); + if (export_env) |*ee| ee.deinit(); + if (cwd) |*cc| cc.deref(); + shargs.deinit(); + interpreter.finalize(); + return .zero; + } + interpreter.flags.quiet = quiet; interpreter.globalThis = globalThis; - interpreter.this_jsvalue = JSC.Codegen.JSShellInterpreter.toJS(interpreter, globalThis); - JSC.Codegen.JSShellInterpreter.resolveSetCached(interpreter.this_jsvalue, globalThis, resolve); - JSC.Codegen.JSShellInterpreter.rejectSetCached(interpreter.this_jsvalue, globalThis, reject); + const js_value = JSC.Codegen.JSShellInterpreter.toJS(interpreter, globalThis); + interpreter.this_jsvalue = js_value; + JSC.Codegen.JSShellInterpreter.resolveSetCached(js_value, globalThis, resolve); + JSC.Codegen.JSShellInterpreter.rejectSetCached(js_value, globalThis, reject); + interpreter.keep_alive.ref(globalThis.bunVM()); bun.Analytics.Features.shell += 1; - return interpreter.this_jsvalue; + return js_value; } pub fn parse( @@ -1603,7 +1642,7 @@ pub const Interpreter = struct { return Maybe(void).success; } - pub fn runFromJS(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn runFromJS(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSValue { _ = callframe; // autofix if (this.setupIOBeforeRun().asErr()) |e| { @@ -1621,18 +1660,14 @@ pub const Interpreter = struct { return .undefined; } - fn ioToJSValue(this: *ThisInterpreter, buf: *bun.ByteList) JSValue { + fn ioToJSValue(globalThis: *JSGlobalObject, buf: *bun.ByteList) JSValue { const bytelist = buf.*; buf.* = .{}; - const value = JSC.MarkedArrayBuffer.toNodeBuffer( - .{ - .allocator = bun.default_allocator, - .buffer = JSC.ArrayBuffer.fromBytes(@constCast(bytelist.slice()), .Uint8Array), - }, - this.event_loop.js.global, - ); - - return value; + const buffer: JSC.Buffer = .{ + .allocator = bun.default_allocator, + .buffer = JSC.ArrayBuffer.fromBytes(@constCast(bytelist.slice()), .Uint8Array), + }; + return buffer.toNodeBuffer(globalThis); } fn asyncCmdDone(this: *ThisInterpreter, @"async": *Async) void { @@ -1663,10 +1698,21 @@ pub const Interpreter = struct { defer this.deinitAfterJSRun(); this.exit_code = exit_code; if (this.this_jsvalue != .zero) { - if (JSC.Codegen.JSShellInterpreter.resolveGetCached(this.this_jsvalue)) |resolve| { - _ = resolve.call(this.globalThis, &.{ JSValue.jsNumberFromU16(exit_code), this.getBufferedStdout(), this.getBufferedStderr() }); - JSC.Codegen.JSShellInterpreter.resolveSetCached(this.this_jsvalue, this.globalThis, .undefined); - JSC.Codegen.JSShellInterpreter.rejectSetCached(this.this_jsvalue, this.globalThis, .undefined); + const this_jsvalue = this.this_jsvalue; + if (JSC.Codegen.JSShellInterpreter.resolveGetCached(this_jsvalue)) |resolve| { + this.this_jsvalue = .zero; + const globalThis = this.globalThis; + const loop = this.event_loop.js; + this.keep_alive.disable(); + loop.enter(); + _ = resolve.call(globalThis, .undefined, &.{ + JSValue.jsNumberFromU16(exit_code), + this.getBufferedStdout(globalThis), + this.getBufferedStderr(globalThis), + }) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); + JSC.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .undefined); + JSC.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .undefined); + loop.exit(); } } } else { @@ -1680,12 +1726,24 @@ pub const Interpreter = struct { defer decrPendingActivityFlag(&this.has_pending_activity); if (this.event_loop == .js) { - if (this.this_jsvalue != .zero) { - if (JSC.Codegen.JSShellInterpreter.rejectGetCached(this.this_jsvalue)) |reject| { - reject.call(this.globalThis, &[_]JSValue{ JSValue.jsNumberFromChar(1), this.getBufferedStdout(), this.getBufferedStderr() }); - JSC.Codegen.JSShellInterpreter.resolveSetCached(this.this_jsvalue, this.globalThis, .undefined); - JSC.Codegen.JSShellInterpreter.rejectSetCached(this.this_jsvalue, this.globalThis, .undefined); + const this_jsvalue = this.this_jsvalue; + if (this_jsvalue != .zero) { + if (JSC.Codegen.JSShellInterpreter.rejectGetCached(this_jsvalue)) |reject| { + const loop = this.event_loop.js; + const globalThis = this.globalThis; this.this_jsvalue = .zero; + this.keep_alive.disable(); + + loop.enter(); + _ = reject.call(globalThis, &[_]JSValue{ + JSValue.jsNumberFromChar(1), + this.getBufferedStdout(globalThis), + this.getBufferedStderr(globalThis), + }) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); + JSC.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .undefined); + JSC.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .undefined); + + loop.exit(); } } } @@ -1697,6 +1755,7 @@ pub const Interpreter = struct { jsobj.unprotect(); } this.root_io.deref(); + this.keep_alive.disable(); this.root_shell.deinitImpl(false, false); this.this_jsvalue = .zero; } @@ -1727,13 +1786,13 @@ pub const Interpreter = struct { this.allocator.destroy(this); } - pub fn setQuiet(this: *ThisInterpreter, _: *JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setQuiet(this: *ThisInterpreter, _: *JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { log("Interpreter(0x{x}) setQuiet()", .{@intFromPtr(this)}); this.flags.quiet = true; return .undefined; } - pub fn setCwd(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setCwd(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const value = callframe.argument(0); const str = bun.String.fromJS(value, globalThis); @@ -1749,7 +1808,7 @@ pub const Interpreter = struct { return .undefined; } - pub fn setEnv(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn setEnv(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { const value1 = callframe.argument(0); if (!value1.isObject()) { globalThis.throwInvalidArguments("env must be an object", .{}); @@ -1790,7 +1849,7 @@ pub const Interpreter = struct { this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { _ = globalThis; // autofix _ = callframe; // autofix @@ -1801,7 +1860,7 @@ pub const Interpreter = struct { this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { _ = globalThis; // autofix _ = callframe; // autofix @@ -1810,37 +1869,37 @@ pub const Interpreter = struct { pub fn getBufferedStdout( this: *ThisInterpreter, + globalThis: *JSGlobalObject, ) JSC.JSValue { - const stdout = this.ioToJSValue(this.root_shell.buffered_stdout()); - return stdout; + return ioToJSValue(globalThis, this.root_shell.buffered_stdout()); } pub fn getBufferedStderr( this: *ThisInterpreter, + globalThis: *JSGlobalObject, ) JSC.JSValue { - const stdout = this.ioToJSValue(this.root_shell.buffered_stderr()); - return stdout; + return ioToJSValue(globalThis, this.root_shell.buffered_stderr()); } pub fn finalize( this: *ThisInterpreter, - ) callconv(.C) void { + ) void { log("Interpreter(0x{x}) finalize", .{@intFromPtr(this)}); this.deinitFromFinalizer(); } - pub fn hasPendingActivity(this: *ThisInterpreter) callconv(.C) bool { + pub fn hasPendingActivity(this: *ThisInterpreter) bool { @fence(.seq_cst); return this.has_pending_activity.load(.seq_cst) > 0; } - fn incrPendingActivityFlag(has_pending_activity: *std.atomic.Value(usize)) void { + fn incrPendingActivityFlag(has_pending_activity: *std.atomic.Value(u32)) void { @fence(.seq_cst); _ = has_pending_activity.fetchAdd(1, .seq_cst); log("Interpreter incr pending activity {d}", .{has_pending_activity.load(.seq_cst)}); } - fn decrPendingActivityFlag(has_pending_activity: *std.atomic.Value(usize)) void { + fn decrPendingActivityFlag(has_pending_activity: *std.atomic.Value(u32)) void { @fence(.seq_cst); _ = has_pending_activity.fetchSub(1, .seq_cst); log("Interpreter decr pending activity {d}", .{has_pending_activity.load(.seq_cst)}); @@ -2616,7 +2675,7 @@ pub const Interpreter = struct { pub fn toJSC(this: Err, globalThis: *JSGlobalObject) JSValue { return switch (this) { .syscall => |err| err.toJSC(globalThis), - .unknown => |err| JSC.ZigString.fromBytes(@errorName(err)).toValueGC(globalThis), + .unknown => |err| JSC.ZigString.fromBytes(@errorName(err)).toJS(globalThis), }; } }; @@ -5333,26 +5392,35 @@ pub const Interpreter = struct { } } - pub fn needsIO(this: *Output) bool { + pub fn needsIO(this: *Output) ?OutputNeedsIOSafeGuard { return switch (this.*) { - .fd => true, - else => false, + .fd => OutputNeedsIOSafeGuard{ + .__i_know_what_i_am_doing_it_needs_io_yes = 0, + }, + else => null, }; } + /// You must check that `.needsIO() == true` before calling this! + /// e.g. + /// + /// ```zig + /// if (this.stderr.neesdIO()) |safeguard| { + /// this.bltn.stderr.enqueueFmtBltn(this, .cd, fmt, args, safeguard); + /// } + /// ``` pub fn enqueueFmtBltn( this: *@This(), ptr: anytype, comptime kind: ?Interpreter.Builtin.Kind, comptime fmt_: []const u8, args: anytype, + _: OutputNeedsIOSafeGuard, ) void { - if (bun.Environment.allow_assert) assert(this.* == .fd); this.fd.writer.enqueueFmtBltn(ptr, this.fd.captured, kind, fmt_, args); } - pub fn enqueue(this: *@This(), ptr: anytype, buf: []const u8) void { - if (bun.Environment.allow_assert) assert(this.* == .fd); + pub fn enqueue(this: *@This(), ptr: anytype, buf: []const u8, _: OutputNeedsIOSafeGuard) void { this.fd.writer.enqueue(ptr, this.fd.captured, buf); } }; @@ -5867,9 +5935,9 @@ pub const Interpreter = struct { } = .idle, pub fn writeFailingError(this: *Cat, buf: []const u8, exit_code: ExitCode) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .waiting_write_err; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); return Maybe(void).success; } @@ -5920,12 +5988,13 @@ pub const Interpreter = struct { if (!this.bltn.stdin.needsIO()) { this.state.exec_stdin.in_done = true; const buf = this.bltn.readStdinNoIO(); - if (!this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { + this.bltn.stdout.enqueue(this, buf, safeguard); + } else { _ = this.bltn.writeNoIO(.stdout, buf); this.bltn.done(0); return; } - this.bltn.stdout.enqueue(this, buf); return; } this.bltn.stdin.fd.addReader(this); @@ -6031,17 +6100,17 @@ pub const Interpreter = struct { debug("onIOReaderChunk(0x{x}, {s}, chunk_len={d})", .{ @intFromPtr(this), @tagName(this.state), chunk.len }); switch (this.state) { .exec_stdin => { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state.exec_stdin.chunks_queued += 1; - this.bltn.stdout.enqueue(this, chunk); + this.bltn.stdout.enqueue(this, chunk, safeguard); return .cont; } _ = this.bltn.writeNoIO(.stdout, chunk); }, .exec_filepath_args => { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state.exec_filepath_args.chunks_queued += 1; - this.bltn.stdout.enqueue(this, chunk); + this.bltn.stdout.enqueue(this, chunk, safeguard); return .cont; } _ = this.bltn.writeNoIO(.stdout, chunk); @@ -6063,21 +6132,21 @@ pub const Interpreter = struct { this.state.exec_stdin.errno = errno; this.state.exec_stdin.in_done = true; if (errno != 0) { - if ((this.state.exec_stdin.chunks_done >= this.state.exec_stdin.chunks_queued) or !this.bltn.stdout.needsIO()) { + if ((this.state.exec_stdin.chunks_done >= this.state.exec_stdin.chunks_queued) or this.bltn.stdout.needsIO() == null) { this.bltn.done(errno); return; } this.bltn.stdout.fd.writer.cancelChunks(this); return; } - if ((this.state.exec_stdin.chunks_done >= this.state.exec_stdin.chunks_queued) or !this.bltn.stdout.needsIO()) { + if ((this.state.exec_stdin.chunks_done >= this.state.exec_stdin.chunks_queued) or this.bltn.stdout.needsIO() == null) { this.bltn.done(0); } }, .exec_filepath_args => { this.state.exec_filepath_args.in_done = true; if (errno != 0) { - if (this.state.exec_filepath_args.out_done or !this.bltn.stdout.needsIO()) { + if (this.state.exec_filepath_args.out_done or this.bltn.stdout.needsIO() == null) { this.state.exec_filepath_args.deinit(); this.bltn.done(errno); return; @@ -6085,7 +6154,7 @@ pub const Interpreter = struct { this.bltn.stdout.fd.writer.cancelChunks(this); return; } - if (this.state.exec_filepath_args.out_done or (this.state.exec_filepath_args.chunks_done >= this.state.exec_filepath_args.chunks_queued) or !this.bltn.stdout.needsIO()) { + if (this.state.exec_filepath_args.out_done or (this.state.exec_filepath_args.chunks_done >= this.state.exec_filepath_args.chunks_queued) or this.bltn.stdout.needsIO() == null) { this.next(); } }, @@ -6277,9 +6346,9 @@ pub const Interpreter = struct { } pub fn writeFailingError(this: *Touch, buf: []const u8, exit_code: ExitCode) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .waiting_write_err; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); return Maybe(void).success; } @@ -6321,9 +6390,9 @@ pub const Interpreter = struct { const ShellTouchOutputTaskVTable = struct { pub fn writeErr(this: *Touch, childptr: anytype, errbuf: []const u8) CoroutineResult { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; - this.bltn.stderr.enqueue(childptr, errbuf); + this.bltn.stderr.enqueue(childptr, errbuf, safeguard); return .yield; } _ = this.bltn.writeNoIO(.stderr, errbuf); @@ -6335,11 +6404,11 @@ pub const Interpreter = struct { } pub fn writeOut(this: *Touch, childptr: anytype, output: *OutputSrc) CoroutineResult { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; const slice = output.slice(); log("THE SLICE: {d} {s}", .{ slice.len, slice }); - this.bltn.stdout.enqueue(childptr, slice); + this.bltn.stdout.enqueue(childptr, slice, safeguard); return .yield; } _ = this.bltn.writeNoIO(.stdout, output.slice()); @@ -6605,9 +6674,9 @@ pub const Interpreter = struct { this.next(); } pub fn writeFailingError(this: *Mkdir, buf: []const u8, exit_code: ExitCode) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .waiting_write_err; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); return Maybe(void).success; } @@ -6709,9 +6778,9 @@ pub const Interpreter = struct { const ShellMkdirOutputTaskVTable = struct { pub fn writeErr(this: *Mkdir, childptr: anytype, errbuf: []const u8) CoroutineResult { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; - this.bltn.stderr.enqueue(childptr, errbuf); + this.bltn.stderr.enqueue(childptr, errbuf, safeguard); return .yield; } _ = this.bltn.writeNoIO(.stderr, errbuf); @@ -6723,11 +6792,11 @@ pub const Interpreter = struct { } pub fn writeOut(this: *Mkdir, childptr: anytype, output: *OutputSrc) CoroutineResult { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; const slice = output.slice(); log("THE SLICE: {d} {s}", .{ slice.len, slice }); - this.bltn.stdout.enqueue(childptr, slice); + this.bltn.stdout.enqueue(childptr, slice, safeguard); return .yield; } _ = this.bltn.writeNoIO(.stdout, output.slice()); @@ -6965,16 +7034,16 @@ pub const Interpreter = struct { }; pub fn writeOutput(this: *Export, comptime io_kind: @Type(.EnumLiteral), comptime fmt: []const u8, args: anytype) Maybe(void) { - if (!this.bltn.stdout.needsIO()) { - const buf = this.bltn.fmtErrorArena(.@"export", fmt, args); - _ = this.bltn.writeNoIO(io_kind, buf); - this.bltn.done(0); + if (this.bltn.stdout.needsIO()) |safeguard| { + var output: *BuiltinIO.Output = &@field(this.bltn, @tagName(io_kind)); + this.printing = true; + output.enqueueFmtBltn(this, .@"export", fmt, args, safeguard); return Maybe(void).success; } - var output: *BuiltinIO.Output = &@field(this.bltn, @tagName(io_kind)); - this.printing = true; - output.enqueueFmtBltn(this, .@"export", fmt, args); + const buf = this.bltn.fmtErrorArena(.@"export", fmt, args); + _ = this.bltn.writeNoIO(io_kind, buf); + this.bltn.done(0); return Maybe(void).success; } @@ -7025,15 +7094,15 @@ pub const Interpreter = struct { } } - if (!this.bltn.stdout.needsIO()) { - _ = this.bltn.writeNoIO(.stdout, buf); - this.bltn.done(0); + if (this.bltn.stdout.needsIO()) |safeguard| { + this.printing = true; + this.bltn.stdout.enqueue(this, buf, safeguard); + return Maybe(void).success; } - this.printing = true; - this.bltn.stdout.enqueue(this, buf); - + _ = this.bltn.writeNoIO(.stdout, buf); + this.bltn.done(0); return Maybe(void).success; } @@ -7098,15 +7167,14 @@ pub const Interpreter = struct { if (!has_leading_newline) this.output.append('\n') catch bun.outOfMemory(); - if (!this.bltn.stdout.needsIO()) { - _ = this.bltn.writeNoIO(.stdout, this.output.items[0..]); - this.state = .done; - this.bltn.done(0); + if (this.bltn.stdout.needsIO()) |safeguard| { + this.state = .waiting; + this.bltn.stdout.enqueue(this, this.output.items[0..], safeguard); return Maybe(void).success; } - - this.state = .waiting; - this.bltn.stdout.enqueue(this, this.output.items[0..]); + _ = this.bltn.writeNoIO(.stdout, this.output.items[0..]); + this.state = .done; + this.bltn.done(0); return Maybe(void).success; } @@ -7155,17 +7223,17 @@ pub const Interpreter = struct { pub fn start(this: *Which) Maybe(void) { const args = this.bltn.argsSlice(); if (args.len == 0) { - if (!this.bltn.stdout.needsIO()) { - _ = this.bltn.writeNoIO(.stdout, "\n"); - this.bltn.done(1); + if (this.bltn.stdout.needsIO()) |safeguard| { + this.state = .one_arg; + this.bltn.stdout.enqueue(this, "\n", safeguard); return Maybe(void).success; } - this.state = .one_arg; - this.bltn.stdout.enqueue(this, "\n"); + _ = this.bltn.writeNoIO(.stdout, "\n"); + this.bltn.done(1); return Maybe(void).success; } - if (!this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO() == null) { var path_buf: bun.PathBuffer = undefined; const PATH = this.bltn.parentCmd().base.shell.export_env.get(EnvStr.initSlice("PATH")) orelse EnvStr.initSlice(""); var had_not_found = false; @@ -7211,27 +7279,28 @@ pub const Interpreter = struct { const resolved = which(&path_buf, PATH.slice(), this.bltn.parentCmd().base.shell.cwdZ(), arg) orelse { multiargs.had_not_found = true; - if (!this.bltn.stdout.needsIO()) { - const buf = this.bltn.fmtErrorArena(null, "{s} not found\n", .{arg}); - _ = this.bltn.writeNoIO(.stdout, buf); - this.argComplete(); + if (this.bltn.stdout.needsIO()) |safeguard| { + multiargs.state = .waiting_write; + this.bltn.stdout.enqueueFmtBltn(this, null, "{s} not found\n", .{arg}, safeguard); + // yield execution return; } - multiargs.state = .waiting_write; - this.bltn.stdout.enqueueFmtBltn(this, null, "{s} not found\n", .{arg}); - // yield execution - return; - }; - if (!this.bltn.stdout.needsIO()) { - const buf = this.bltn.fmtErrorArena(null, "{s}\n", .{resolved}); + const buf = this.bltn.fmtErrorArena(null, "{s} not found\n", .{arg}); _ = this.bltn.writeNoIO(.stdout, buf); this.argComplete(); return; + }; + + if (this.bltn.stdout.needsIO()) |safeguard| { + multiargs.state = .waiting_write; + this.bltn.stdout.enqueueFmtBltn(this, null, "{s}\n", .{resolved}, safeguard); + return; } - multiargs.state = .waiting_write; - this.bltn.stdout.enqueueFmtBltn(this, null, "{s}\n", .{resolved}); + const buf = this.bltn.fmtErrorArena(null, "{s}\n", .{resolved}); + _ = this.bltn.writeNoIO(.stdout, buf); + this.argComplete(); return; } @@ -7287,7 +7356,14 @@ pub const Interpreter = struct { fn writeStderrNonBlocking(this: *Cd, comptime fmt: []const u8, args: anytype) void { this.state = .waiting_write_stderr; - this.bltn.stderr.enqueueFmtBltn(this, .cd, fmt, args); + if (this.bltn.stderr.needsIO()) |safeguard| { + this.bltn.stderr.enqueueFmtBltn(this, .cd, fmt, args, safeguard); + } else { + const buf = this.bltn.fmtErrorArena(.cd, fmt, args); + _ = this.bltn.writeNoIO(.stderr, buf); + this.state = .done; + this.bltn.done(1); + } } pub fn start(this: *Cd) Maybe(void) { @@ -7298,31 +7374,34 @@ pub const Interpreter = struct { return Maybe(void).success; } - const first_arg = args[0][0..std.mem.len(args[0]) :0]; - switch (first_arg[0]) { - '-' => { - switch (this.bltn.parentCmd().base.shell.changePrevCwd(this.bltn.parentCmd().base.interpreter)) { - .result => {}, - .err => |err| { - return this.handleChangeCwdErr(err, this.bltn.parentCmd().base.shell.prevCwdZ()); - }, - } - }, - '~' => { - const homedir = this.bltn.parentCmd().base.shell.getHomedir(); - homedir.deref(); - switch (this.bltn.parentCmd().base.shell.changeCwd(this.bltn.parentCmd().base.interpreter, homedir.slice())) { - .result => {}, - .err => |err| return this.handleChangeCwdErr(err, homedir.slice()), - } - }, - else => { - switch (this.bltn.parentCmd().base.shell.changeCwd(this.bltn.parentCmd().base.interpreter, first_arg)) { - .result => {}, - .err => |err| return this.handleChangeCwdErr(err, first_arg), - } - }, + if (args.len == 1) { + const first_arg = args[0][0..std.mem.len(args[0]) :0]; + switch (first_arg[0]) { + '-' => { + switch (this.bltn.parentCmd().base.shell.changePrevCwd(this.bltn.parentCmd().base.interpreter)) { + .result => {}, + .err => |err| { + return this.handleChangeCwdErr(err, this.bltn.parentCmd().base.shell.prevCwdZ()); + }, + } + }, + '~' => { + const homedir = this.bltn.parentCmd().base.shell.getHomedir(); + homedir.deref(); + switch (this.bltn.parentCmd().base.shell.changeCwd(this.bltn.parentCmd().base.interpreter, homedir.slice())) { + .result => {}, + .err => |err| return this.handleChangeCwdErr(err, homedir.slice()), + } + }, + else => { + switch (this.bltn.parentCmd().base.shell.changeCwd(this.bltn.parentCmd().base.interpreter, first_arg)) { + .result => {}, + .err => |err| return this.handleChangeCwdErr(err, first_arg), + } + }, + } } + this.bltn.done(0); return Maybe(void).success; } @@ -7332,7 +7411,7 @@ pub const Interpreter = struct { switch (errno) { @as(usize, @intFromEnum(bun.C.E.NOTDIR)) => { - if (!this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO() == null) { const buf = this.bltn.fmtErrorArena(.cd, "not a directory: {s}\n", .{new_cwd_}); _ = this.bltn.writeNoIO(.stderr, buf); this.state = .done; @@ -7345,7 +7424,7 @@ pub const Interpreter = struct { return Maybe(void).success; }, @as(usize, @intFromEnum(bun.C.E.NOENT)) => { - if (!this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO() == null) { const buf = this.bltn.fmtErrorArena(.cd, "not a directory: {s}\n", .{new_cwd_}); _ = this.bltn.writeNoIO(.stderr, buf); this.state = .done; @@ -7397,9 +7476,9 @@ pub const Interpreter = struct { const args = this.bltn.argsSlice(); if (args.len > 0) { const msg = "pwd: too many arguments\n"; - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .{ .waiting_io = .{ .kind = .stderr } }; - this.bltn.stderr.enqueue(this, msg); + this.bltn.stderr.enqueue(this, msg, safeguard); return Maybe(void).success; } @@ -7409,9 +7488,9 @@ pub const Interpreter = struct { } const cwd_str = this.bltn.parentCmd().base.shell.cwd(); - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state = .{ .waiting_io = .{ .kind = .stdout } }; - this.bltn.stdout.enqueueFmtBltn(this, null, "{s}\n", .{cwd_str}); + this.bltn.stdout.enqueueFmtBltn(this, null, "{s}\n", .{cwd_str}, safeguard); return Maybe(void).success; } const buf = this.bltn.fmtErrorArena(null, "{s}\n", .{cwd_str}); @@ -7491,9 +7570,9 @@ pub const Interpreter = struct { } pub fn writeFailingError(this: *Ls, buf: []const u8, exit_code: ExitCode) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .waiting_write_err; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); return Maybe(void).success; } @@ -7614,9 +7693,9 @@ pub const Interpreter = struct { const ShellLsOutputTaskVTable = struct { pub fn writeErr(this: *Ls, childptr: anytype, errbuf: []const u8) CoroutineResult { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; - this.bltn.stderr.enqueue(childptr, errbuf); + this.bltn.stderr.enqueue(childptr, errbuf, safeguard); return .yield; } _ = this.bltn.writeNoIO(.stderr, errbuf); @@ -7628,9 +7707,9 @@ pub const Interpreter = struct { } pub fn writeOut(this: *Ls, childptr: anytype, output: *OutputSrc) CoroutineResult { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; - this.bltn.stdout.enqueue(childptr, output.slice()); + this.bltn.stdout.enqueue(childptr, output.slice(), safeguard); return .yield; } _ = this.bltn.writeNoIO(.stdout, output.slice()); @@ -8425,9 +8504,9 @@ pub const Interpreter = struct { } pub fn writeFailingError(this: *Mv, buf: []const u8, exit_code: ExitCode) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .{ .waiting_write_err = .{ .exit_code = exit_code } }; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); return Maybe(void).success; } @@ -8874,9 +8953,9 @@ pub const Interpreter = struct { // string if (parse_opts.idx >= parse_opts.args_slice.len) { const error_string = Builtin.Kind.usageString(.rm); - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { parse_opts.state = .wait_write_err; - this.bltn.stderr.enqueue(this, error_string); + this.bltn.stderr.enqueue(this, error_string, safeguard); return Maybe(void).success; } @@ -8903,9 +8982,9 @@ pub const Interpreter = struct { if (this.opts.prompt_behaviour != .never) { const buf = "rm: \"-i\" is not supported yet"; - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { parse_opts.state = .wait_write_err; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); continue; } @@ -8939,9 +9018,9 @@ pub const Interpreter = struct { }; if (is_root) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { parse_opts.state = .wait_write_err; - this.bltn.stderr.enqueueFmtBltn(this, .rm, "\"{s}\" may not be removed\n", .{resolved_path}); + this.bltn.stderr.enqueueFmtBltn(this, .rm, "\"{s}\" may not be removed\n", .{resolved_path}, safeguard); return Maybe(void).success; } @@ -8971,9 +9050,9 @@ pub const Interpreter = struct { }, .illegal_option => { const error_string = "rm: illegal option -- -\n"; - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { parse_opts.state = .wait_write_err; - this.bltn.stderr.enqueue(this, error_string); + this.bltn.stderr.enqueue(this, error_string, safeguard); return Maybe(void).success; } @@ -8984,9 +9063,9 @@ pub const Interpreter = struct { }, .illegal_option_with_flag => { const flag = arg; - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { parse_opts.state = .wait_write_err; - this.bltn.stderr.enqueueFmtBltn(this, .rm, "illegal option -- {s}\n", .{flag[1..]}); + this.bltn.stderr.enqueueFmtBltn(this, .rm, "illegal option -- {s}\n", .{flag[1..]}, safeguard); return Maybe(void).success; } const error_string = this.bltn.fmtErrorArena(.rm, "illegal option -- {s}\n", .{flag[1..]}); @@ -9185,13 +9264,13 @@ pub const Interpreter = struct { if (task.err) |err| { exec.err = err; const error_string = this.bltn.taskErrorToString(.rm, err); - if (!this.bltn.stderr.needsIO()) { - _ = this.bltn.writeNoIO(.stderr, error_string); - } else { + if (this.bltn.stderr.needsIO()) |safeguard| { log("Rm(0x{x}) task=0x{x} ERROR={s}", .{ @intFromPtr(this), @intFromPtr(task), error_string }); exec.incrementOutputCount(.output_count); - this.bltn.stderr.enqueue(this, error_string); + this.bltn.stderr.enqueue(this, error_string, safeguard); return; + } else { + _ = this.bltn.writeNoIO(.stderr, error_string); } } break :brk amt; @@ -9210,7 +9289,11 @@ pub const Interpreter = struct { } fn writeVerbose(this: *Rm, verbose: *ShellRmTask.DirTask) void { - if (!this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { + const buf = verbose.takeDeletedEntries(); + defer buf.deinit(); + this.bltn.stdout.enqueue(this, buf.items, safeguard); + } else { _ = this.bltn.writeNoIO(.stdout, verbose.deleted_entries.items); _ = this.state.exec.incrementOutputCount(.output_done); if (this.state.exec.state.tasksDone() >= this.state.exec.total_tasks and this.state.exec.getOutputCount(.output_done) >= this.state.exec.getOutputCount(.output_count)) { @@ -9219,9 +9302,6 @@ pub const Interpreter = struct { } return; } - const buf = verbose.takeDeletedEntries(); - defer buf.deinit(); - this.bltn.stdout.enqueue(this, buf.items); } pub const ShellRmTask = struct { @@ -9238,7 +9318,7 @@ pub const Interpreter = struct { root_is_absolute: bool, error_signal: *std.atomic.Value(bool), - err_mutex: bun.Lock = bun.Lock.init(), + err_mutex: bun.Lock = .{}, err: ?Syscall.Error = null, event_loop: JSC.EventLoopHandle, @@ -9985,9 +10065,9 @@ pub const Interpreter = struct { } fn fail(this: *Exit, msg: string) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .waiting_io; - this.bltn.stderr.enqueue(this, msg); + this.bltn.stderr.enqueue(this, msg, safeguard); return Maybe(void).success; } _ = this.bltn.writeNoIO(.stderr, msg); @@ -10078,32 +10158,33 @@ pub const Interpreter = struct { this.expletive = std.mem.sliceTo(args[0], 0); } - if (!this.bltn.stdout.needsIO()) { - var res: Maybe(usize) = undefined; - while (true) { - res = this.bltn.writeNoIO(.stdout, this.expletive); - if (res == .err) { - this.bltn.done(1); - return Maybe(void).success; - } - res = this.bltn.writeNoIO(.stdout, "\n"); - if (res == .err) { - this.bltn.done(1); - return Maybe(void).success; - } + if (this.bltn.stdout.needsIO()) |safeguard| { + const evtloop = this.bltn.eventLoop(); + this.task = .{ + .evtloop = evtloop, + .concurrent_task = JSC.EventLoopTask.fromEventLoop(evtloop), + }; + this.state = .waiting_io; + this.bltn.stdout.enqueue(this, this.expletive, safeguard); + this.bltn.stdout.enqueue(this, "\n", safeguard); + this.task.enqueue(); + return Maybe(void).success; + } + + var res: Maybe(usize) = undefined; + while (true) { + res = this.bltn.writeNoIO(.stdout, this.expletive); + if (res == .err) { + this.bltn.done(1); + return Maybe(void).success; + } + res = this.bltn.writeNoIO(.stdout, "\n"); + if (res == .err) { + this.bltn.done(1); + return Maybe(void).success; } - @compileError(unreachable); } - const evtloop = this.bltn.eventLoop(); - this.task = .{ - .evtloop = evtloop, - .concurrent_task = JSC.EventLoopTask.fromEventLoop(evtloop), - }; - this.state = .waiting_io; - this.bltn.stdout.enqueue(this, this.expletive); - this.bltn.stdout.enqueue(this, "\n"); - this.task.enqueue(); - return Maybe(void).success; + @compileError(unreachable); } pub fn onIOWriterChunk(this: *@This(), _: usize, maybe_e: ?JSC.SystemError) void { @@ -10136,8 +10217,9 @@ pub const Interpreter = struct { pub fn runFromMainThread(this: *@This()) void { const yes: *Yes = @fieldParentPtr("task", this); - yes.bltn.stdout.enqueue(yes, yes.expletive); - yes.bltn.stdout.enqueue(yes, "\n"); + // Manually make safeguard since this task should not be created if output does not need IO + yes.bltn.stdout.enqueue(yes, yes.expletive, OutputNeedsIOSafeGuard{ .__i_know_what_i_am_doing_it_needs_io_yes = 0 }); + yes.bltn.stdout.enqueue(yes, "\n", OutputNeedsIOSafeGuard{ .__i_know_what_i_am_doing_it_needs_io_yes = 0 }); this.enqueue(); } @@ -10224,9 +10306,9 @@ pub const Interpreter = struct { } fn fail(this: *@This(), msg: string) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .err; - this.bltn.stderr.enqueue(this, msg); + this.bltn.stderr.enqueue(this, msg, safeguard); return Maybe(void).success; } _ = this.bltn.writeNoIO(.stderr, msg); @@ -10248,8 +10330,8 @@ pub const Interpreter = struct { _ = this.print(this.terminator); this.state = .done; - if (this.bltn.stdout.needsIO()) { - this.bltn.stdout.enqueue(this, this.buf.items); + if (this.bltn.stdout.needsIO()) |safeguard| { + this.bltn.stdout.enqueue(this, this.buf.items, safeguard); } else { this.bltn.done(0); } @@ -10257,7 +10339,7 @@ pub const Interpreter = struct { } fn print(this: *@This(), msg: string) Maybe(void) { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO() != null) { this.buf.appendSlice(bun.default_allocator, msg) catch bun.outOfMemory(); return Maybe(void).success; } @@ -10305,8 +10387,8 @@ pub const Interpreter = struct { } this.state = .done; - if (this.bltn.stdout.needsIO()) { - this.bltn.stdout.enqueue(this, this.buf.items); + if (this.bltn.stdout.needsIO()) |safeguard| { + this.bltn.stdout.enqueue(this, this.buf.items, safeguard); } else { this.bltn.done(0); } @@ -10319,9 +10401,9 @@ pub const Interpreter = struct { } fn fail(this: *@This(), msg: string) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .err; - this.bltn.stderr.enqueue(this, msg); + this.bltn.stderr.enqueue(this, msg, safeguard); return Maybe(void).success; } _ = this.bltn.writeNoIO(.stderr, msg); @@ -10330,7 +10412,7 @@ pub const Interpreter = struct { } fn print(this: *@This(), msg: string) Maybe(void) { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO() != null) { this.buf.appendSlice(bun.default_allocator, msg) catch bun.outOfMemory(); return Maybe(void).success; } @@ -10373,8 +10455,8 @@ pub const Interpreter = struct { } this.state = .done; - if (this.bltn.stdout.needsIO()) { - this.bltn.stdout.enqueue(this, this.buf.items); + if (this.bltn.stdout.needsIO()) |safeguard| { + this.bltn.stdout.enqueue(this, this.buf.items, safeguard); } else { this.bltn.done(0); } @@ -10387,9 +10469,9 @@ pub const Interpreter = struct { } fn fail(this: *@This(), msg: string) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .err; - this.bltn.stderr.enqueue(this, msg); + this.bltn.stderr.enqueue(this, msg, safeguard); return Maybe(void).success; } _ = this.bltn.writeNoIO(.stderr, msg); @@ -10398,7 +10480,7 @@ pub const Interpreter = struct { } fn print(this: *@This(), msg: string) Maybe(void) { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO() != null) { this.buf.appendSlice(bun.default_allocator, msg) catch bun.outOfMemory(); return Maybe(void).success; } @@ -10474,8 +10556,8 @@ pub const Interpreter = struct { /// threadpool. const EbusyState = struct { tasks: std.ArrayListUnmanaged(*ShellCpTask) = .{}, - absolute_targets: std.StringArrayHashMapUnmanaged(void) = .{}, - absolute_srcs: std.StringArrayHashMapUnmanaged(void) = .{}, + absolute_targets: bun.StringArrayHashMapUnmanaged(void) = .{}, + absolute_srcs: bun.StringArrayHashMapUnmanaged(void) = .{}, pub fn deinit(this: *EbusyState) void { // The tasks themselves are freed in `ignoreEbusyErrorIfPossible()` @@ -10611,9 +10693,9 @@ pub const Interpreter = struct { } pub fn writeFailingError(this: *Cp, buf: []const u8, exit_code: ExitCode) Maybe(void) { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state = .waiting_write_err; - this.bltn.stderr.enqueue(this, buf); + this.bltn.stderr.enqueue(this, buf, safeguard); return Maybe(void).success; } @@ -10697,9 +10779,9 @@ pub const Interpreter = struct { const ShellCpOutputTaskVTable = struct { pub fn writeErr(this: *Cp, childptr: anytype, errbuf: []const u8) CoroutineResult { - if (this.bltn.stderr.needsIO()) { + if (this.bltn.stderr.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; - this.bltn.stderr.enqueue(childptr, errbuf); + this.bltn.stderr.enqueue(childptr, errbuf, safeguard); return .yield; } _ = this.bltn.writeNoIO(.stderr, errbuf); @@ -10711,9 +10793,9 @@ pub const Interpreter = struct { } pub fn writeOut(this: *Cp, childptr: anytype, output: *OutputSrc) CoroutineResult { - if (this.bltn.stdout.needsIO()) { + if (this.bltn.stdout.needsIO()) |safeguard| { this.state.exec.output_waiting += 1; - this.bltn.stdout.enqueue(childptr, output.slice()); + this.bltn.stdout.enqueue(childptr, output.slice(), safeguard); return .yield; } _ = this.bltn.writeNoIO(.stdout, output.slice()); @@ -12013,8 +12095,8 @@ fn closefd(fd: bun.FileDescriptor) void { } const CmdEnvIter = struct { - env: *const std.StringArrayHashMap([:0]const u8), - iter: std.StringArrayHashMap([:0]const u8).Iterator, + env: *const bun.StringArrayHashMap([:0]const u8), + iter: bun.StringArrayHashMap([:0]const u8).Iterator, const Entry = struct { key: Key, @@ -12041,7 +12123,7 @@ const CmdEnvIter = struct { } }; - pub fn fromEnv(env: *const std.StringArrayHashMap([:0]const u8)) CmdEnvIter { + pub fn fromEnv(env: *const bun.StringArrayHashMap([:0]const u8)) CmdEnvIter { const iter = env.iterator(); return .{ .env = env, diff --git a/src/shell/shell.zig b/src/shell/shell.zig index a8358bfe6f27d..871cab7f92a13 100644 --- a/src/shell/shell.zig +++ b/src/shell/shell.zig @@ -2124,20 +2124,6 @@ pub const Token = union(TokenTag) { .Eof => "EOF", }; } - - // pub fn debug(self: Token, buf: []const u8) void { - // switch (self) { - // .Var => |txt| { - // std.debug.print("(var) {s}\n", .{buf[txt.start..txt.end]}); - // }, - // .Text => |txt| { - // std.debug.print("(txt) {s}\n", .{buf[txt.start..txt.end]}); - // }, - // else => { - // std.debug.print("{s}\n", .{@tagName(self)}); - // }, - // } - // } }; pub const LexerAscii = NewLexer(.ascii); @@ -3330,14 +3316,6 @@ pub fn NewLexer(comptime encoding: StringEncoding) type { fn read_char(self: *@This()) ?InputChar { return self.chars.read_char(); } - - // fn debug_tokens(self: *const @This()) void { - // std.debug.print("Tokens: \n", .{}); - // for (self.tokens.items, 0..) |tok, i| { - // std.debug.print("{d}: ", .{i}); - // tok.debug(self.strpool.items[0..self.strpool.items.len]); - // } - // } }; } @@ -3629,8 +3607,8 @@ pub fn hasEqSignAsciiSlow(str: []const u8) ?u32 { } pub const CmdEnvIter = struct { - env: *const std.StringArrayHashMap([:0]const u8), - iter: std.StringArrayHashMap([:0]const u8).Iterator, + env: *const bun.StringArrayHashMap([:0]const u8), + iter: bun.StringArrayHashMap([:0]const u8).Iterator, const Entry = struct { key: Key, @@ -3657,7 +3635,7 @@ pub const CmdEnvIter = struct { } }; - pub fn fromEnv(env: *const std.StringArrayHashMap([:0]const u8)) CmdEnvIter { + pub fn fromEnv(env: *const bun.StringArrayHashMap([:0]const u8)) CmdEnvIter { const iter = env.iterator(); return .{ .env = env, @@ -4023,7 +4001,7 @@ const SPECIAL_CHARS_TABLE: std.bit_set.IntegerBitSet(256) = brk: { break :brk table; }; pub fn assertSpecialChar(c: u8) void { - comptime bun.assert(@inComptime()); + bun.assertComptime(); bun.assert(SPECIAL_CHARS_TABLE.isSet(c)); } /// Characters that need to be backslashed inside double quotes @@ -4369,14 +4347,14 @@ pub fn SmolList(comptime T: type, comptime INLINED_MAX: comptime_int) type { /// Used in JS tests, see `internal-for-testing.ts` and shell tests. pub const TestingAPIs = struct { - pub fn disabledOnThisPlatform(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub fn disabledOnThisPlatform(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue { if (comptime bun.Environment.isWindows) return JSValue.false; const arguments_ = callframe.arguments(1); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); const string = arguments.nextEat() orelse { globalThis.throw("shellInternals.disabledOnPosix: expected 1 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; const bunstr = string.toBunString(globalThis); @@ -4395,12 +4373,12 @@ pub const TestingAPIs = struct { pub fn shellLex( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments_ = callframe.arguments(2); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); const string_args = arguments.nextEat() orelse { globalThis.throw("shell_parse: expected 2 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; var arena = std.heap.ArenaAllocator.init(bun.default_allocator); @@ -4485,12 +4463,12 @@ pub const TestingAPIs = struct { pub fn shellParse( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) JSC.JSValue { const arguments_ = callframe.arguments(2); var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); const string_args = arguments.nextEat() orelse { globalThis.throw("shell_parse: expected 2 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); + return .undefined; }; var arena = bun.ArenaAllocator.init(bun.default_allocator); diff --git a/src/shell/subproc.zig b/src/shell/subproc.zig index b8583e4fefcb1..1eed3bdf53e8e 100644 --- a/src/shell/subproc.zig +++ b/src/shell/subproc.zig @@ -202,7 +202,7 @@ pub const ShellSubprocess = struct { .memfd, .path, .ignore => { return Writable{ .ignore = {} }; }, - .capture => { + .ipc, .capture => { return Writable{ .ignore = {} }; }, } @@ -240,7 +240,7 @@ pub const ShellSubprocess = struct { .path, .ignore => { return Writable{ .ignore = {} }; }, - .capture => { + .ipc, .capture => { return Writable{ .ignore = {} }; }, } @@ -375,7 +375,7 @@ pub const ShellSubprocess = struct { if (Environment.isWindows) { return switch (stdio) { .inherit => Readable{ .inherit = {} }, - .dup2, .ignore => Readable{ .ignore = {} }, + .ipc, .dup2, .ignore => Readable{ .ignore = {} }, .path => Readable{ .ignore = {} }, .fd => |fd| Readable{ .fd = fd }, // blobs are immutable, so we should only ever get the case @@ -396,7 +396,7 @@ pub const ShellSubprocess = struct { return switch (stdio) { .inherit => Readable{ .inherit = {} }, - .dup2, .ignore => Readable{ .ignore = {} }, + .ipc, .dup2, .ignore => Readable{ .ignore = {} }, .path => Readable{ .ignore = {} }, .fd => Readable{ .fd = result.? }, // blobs are immutable, so we should only ever get the case diff --git a/src/sourcemap/CodeCoverage.zig b/src/sourcemap/CodeCoverage.zig index 78197c16b1428..eb3b4e0343d70 100644 --- a/src/sourcemap/CodeCoverage.zig +++ b/src/sourcemap/CodeCoverage.zig @@ -3,6 +3,7 @@ const std = @import("std"); const LineOffsetTable = bun.sourcemap.LineOffsetTable; const SourceMap = bun.sourcemap; const Bitset = bun.bit_set.DynamicBitSetUnmanaged; +const LinesHits = @import("../baby_list.zig").BabyList(u32); const Output = bun.Output; const prettyFmt = Output.prettyFmt; @@ -24,17 +25,13 @@ pub const CodeCoverageReport = struct { source_url: bun.JSC.ZigString.Slice, executable_lines: Bitset, lines_which_have_executed: Bitset, + line_hits: LinesHits = .{}, functions: std.ArrayListUnmanaged(Block), functions_which_have_executed: Bitset, stmts_which_have_executed: Bitset, stmts: std.ArrayListUnmanaged(Block), total_lines: u32 = 0, - pub const Block = struct { - start_line: u32 = 0, - end_line: u32 = 0, - }; - pub fn linesCoverageFraction(this: *const CodeCoverageReport) f64 { var intersected = this.executable_lines.clone(bun.default_allocator) catch bun.outOfMemory(); defer intersected.deinit(bun.default_allocator); @@ -68,156 +65,213 @@ pub const CodeCoverageReport = struct { return (@as(f64, @floatFromInt(this.functions_which_have_executed.count())) / total_count); } - pub fn writeFormatWithValues( - filename: []const u8, - max_filename_length: usize, - vals: CoverageFraction, - failing: CoverageFraction, - failed: bool, - writer: anytype, - indent_name: bool, - comptime enable_colors: bool, - ) !void { - if (comptime enable_colors) { - if (failed) { - try writer.writeAll(comptime prettyFmt("", true)); - } else { - try writer.writeAll(comptime prettyFmt("", true)); + pub const Text = struct { + pub fn writeFormatWithValues( + filename: []const u8, + max_filename_length: usize, + vals: CoverageFraction, + failing: CoverageFraction, + failed: bool, + writer: anytype, + indent_name: bool, + comptime enable_colors: bool, + ) !void { + if (comptime enable_colors) { + if (failed) { + try writer.writeAll(comptime prettyFmt("", true)); + } else { + try writer.writeAll(comptime prettyFmt("", true)); + } } - } - if (indent_name) { - try writer.writeAll(" "); - } + if (indent_name) { + try writer.writeAll(" "); + } - try writer.writeAll(filename); - try writer.writeByteNTimes(' ', (max_filename_length - filename.len + @as(usize, @intFromBool(!indent_name)))); - try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); + try writer.writeAll(filename); + try writer.writeByteNTimes(' ', (max_filename_length - filename.len + @as(usize, @intFromBool(!indent_name)))); + try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); - if (comptime enable_colors) { - if (vals.functions < failing.functions) { - try writer.writeAll(comptime prettyFmt("", true)); - } else { - try writer.writeAll(comptime prettyFmt("", true)); + if (comptime enable_colors) { + if (vals.functions < failing.functions) { + try writer.writeAll(comptime prettyFmt("", true)); + } else { + try writer.writeAll(comptime prettyFmt("", true)); + } } - } - try writer.print("{d: >7.2}", .{vals.functions * 100.0}); - // try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); - // if (comptime enable_colors) { - // // if (vals.stmts < failing.stmts) { - // try writer.writeAll(comptime prettyFmt("", true)); - // // } else { - // // try writer.writeAll(comptime prettyFmt("", true)); - // // } - // } - // try writer.print("{d: >8.2}", .{vals.stmts * 100.0}); - try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); - - if (comptime enable_colors) { - if (vals.lines < failing.lines) { - try writer.writeAll(comptime prettyFmt("", true)); - } else { - try writer.writeAll(comptime prettyFmt("", true)); + try writer.print("{d: >7.2}", .{vals.functions * 100.0}); + // try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); + // if (comptime enable_colors) { + // // if (vals.stmts < failing.stmts) { + // try writer.writeAll(comptime prettyFmt("", true)); + // // } else { + // // try writer.writeAll(comptime prettyFmt("", true)); + // // } + // } + // try writer.print("{d: >8.2}", .{vals.stmts * 100.0}); + try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); + + if (comptime enable_colors) { + if (vals.lines < failing.lines) { + try writer.writeAll(comptime prettyFmt("", true)); + } else { + try writer.writeAll(comptime prettyFmt("", true)); + } } + + try writer.print("{d: >7.2}", .{vals.lines * 100.0}); } - try writer.print("{d: >7.2}", .{vals.lines * 100.0}); - } + pub fn writeFormat( + report: *const CodeCoverageReport, + max_filename_length: usize, + fraction: *CoverageFraction, + base_path: []const u8, + writer: anytype, + comptime enable_colors: bool, + ) !void { + const failing = fraction.*; + const fns = report.functionCoverageFraction(); + const lines = report.linesCoverageFraction(); + const stmts = report.stmtsCoverageFraction(); + fraction.functions = fns; + fraction.lines = lines; + fraction.stmts = stmts; + + const failed = fns < failing.functions or lines < failing.lines; // or stmts < failing.stmts; + fraction.failing = failed; + + var filename = report.source_url.slice(); + if (base_path.len > 0) { + filename = bun.path.relative(base_path, filename); + } - pub fn writeFormat( - report: *const CodeCoverageReport, - max_filename_length: usize, - fraction: *CoverageFraction, - base_path: []const u8, - writer: anytype, - comptime enable_colors: bool, - ) !void { - const failing = fraction.*; - const fns = report.functionCoverageFraction(); - const lines = report.linesCoverageFraction(); - const stmts = report.stmtsCoverageFraction(); - fraction.functions = fns; - fraction.lines = lines; - fraction.stmts = stmts; - - const failed = fns < failing.functions or lines < failing.lines; // or stmts < failing.stmts; - fraction.failing = failed; - - var filename = report.source_url.slice(); - if (base_path.len > 0) { - filename = bun.path.relative(base_path, filename); - } + try writeFormatWithValues( + filename, + max_filename_length, + fraction.*, + failing, + failed, + writer, + true, + enable_colors, + ); + + try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); + + var executable_lines_that_havent_been_executed = report.lines_which_have_executed.clone(bun.default_allocator) catch bun.outOfMemory(); + defer executable_lines_that_havent_been_executed.deinit(bun.default_allocator); + executable_lines_that_havent_been_executed.toggleAll(); + + // This sets statements in executed scopes + executable_lines_that_havent_been_executed.setIntersection(report.executable_lines); + + var iter = executable_lines_that_havent_been_executed.iterator(.{}); + var start_of_line_range: usize = 0; + var prev_line: usize = 0; + var is_first = true; + + while (iter.next()) |next_line| { + if (next_line == (prev_line + 1)) { + prev_line = next_line; + continue; + } else if (is_first and start_of_line_range == 0 and prev_line == 0) { + start_of_line_range = next_line; + prev_line = next_line; + continue; + } + + if (is_first) { + is_first = false; + } else { + try writer.print(comptime prettyFmt(",", enable_colors), .{}); + } + + if (start_of_line_range == prev_line) { + try writer.print(comptime prettyFmt("{d}", enable_colors), .{start_of_line_range + 1}); + } else { + try writer.print(comptime prettyFmt("{d}-{d}", enable_colors), .{ start_of_line_range + 1, prev_line + 1 }); + } - try writeFormatWithValues( - filename, - max_filename_length, - fraction.*, - failing, - failed, - writer, - true, - enable_colors, - ); - - try writer.writeAll(comptime prettyFmt(" | ", enable_colors)); - - var executable_lines_that_havent_been_executed = report.lines_which_have_executed.clone(bun.default_allocator) catch bun.outOfMemory(); - defer executable_lines_that_havent_been_executed.deinit(bun.default_allocator); - executable_lines_that_havent_been_executed.toggleAll(); - - // This sets statements in executed scopes - executable_lines_that_havent_been_executed.setIntersection(report.executable_lines); - - var iter = executable_lines_that_havent_been_executed.iterator(.{}); - var start_of_line_range: usize = 0; - var prev_line: usize = 0; - var is_first = true; - - while (iter.next()) |next_line| { - if (next_line == (prev_line + 1)) { prev_line = next_line; - continue; - } else if (is_first and start_of_line_range == 0 and prev_line == 0) { start_of_line_range = next_line; - prev_line = next_line; - continue; } - if (is_first) { - is_first = false; - } else { - try writer.print(comptime prettyFmt(",", enable_colors), .{}); - } + if (prev_line != start_of_line_range) { + if (is_first) { + is_first = false; + } else { + try writer.print(comptime prettyFmt(",", enable_colors), .{}); + } - if (start_of_line_range == prev_line) { - try writer.print(comptime prettyFmt("{d}", enable_colors), .{start_of_line_range + 1}); - } else { - try writer.print(comptime prettyFmt("{d}-{d}", enable_colors), .{ start_of_line_range + 1, prev_line + 1 }); + if (start_of_line_range == prev_line) { + try writer.print(comptime prettyFmt("{d}", enable_colors), .{start_of_line_range + 1}); + } else { + try writer.print(comptime prettyFmt("{d}-{d}", enable_colors), .{ start_of_line_range + 1, prev_line + 1 }); + } } - - prev_line = next_line; - start_of_line_range = next_line; } + }; - if (prev_line != start_of_line_range) { - if (is_first) { - is_first = false; - } else { - try writer.print(comptime prettyFmt(",", enable_colors), .{}); + pub const Lcov = struct { + pub fn writeFormat( + report: *const CodeCoverageReport, + base_path: []const u8, + writer: anytype, + ) !void { + var filename = report.source_url.slice(); + if (base_path.len > 0) { + filename = bun.path.relative(base_path, filename); } - if (start_of_line_range == prev_line) { - try writer.print(comptime prettyFmt("{d}", enable_colors), .{start_of_line_range + 1}); - } else { - try writer.print(comptime prettyFmt("{d}-{d}", enable_colors), .{ start_of_line_range + 1, prev_line + 1 }); + // TN: test name + // Empty value appears fine. For example, `TN:`. + try writer.writeAll("TN:\n"); + + // SF: Source File path + // For example, `SF:path/to/source.ts` + try writer.print("SF:{s}\n", .{filename}); + + // ** Per-function coverage not supported yet, since JSC does not support function names yet. ** + // FN: line number,function name + + // FNF: functions found + try writer.print("FNF:{d}\n", .{report.functions.items.len}); + + // FNH: functions hit + try writer.print("FNH:{d}\n", .{report.functions_which_have_executed.count()}); + + // ** Track all executable lines ** + // Executable lines that were not hit should be marked as 0 + var executable_lines = report.executable_lines.clone(bun.default_allocator) catch bun.outOfMemory(); + defer executable_lines.deinit(bun.default_allocator); + var iter = executable_lines.iterator(.{}); + + // ** Branch coverage not supported yet, since JSC does not support those yet. ** // + // BRDA: line, block, (expressions,count)+ + // BRF: branches found + // BRH: branches hit + const line_hits = report.line_hits.slice(); + while (iter.next()) |line| { + // DA: line number, hit count + try writer.print("DA:{d},{d}\n", .{ line + 1, line_hits[line] }); } + + // LF: lines found + try writer.print("LF:{d}\n", .{report.total_lines}); + + // LH: lines hit + try writer.print("LH:{d}\n", .{report.lines_which_have_executed.count()}); + + try writer.writeAll("end_of_record\n"); } - } + }; pub fn deinit(this: *CodeCoverageReport, allocator: std.mem.Allocator) void { this.executable_lines.deinit(allocator); this.lines_which_have_executed.deinit(allocator); + this.line_hits.deinitWithAllocator(allocator); this.functions.deinit(allocator); this.stmts.deinit(allocator); this.functions_which_have_executed.deinit(allocator); @@ -260,7 +314,7 @@ pub const CodeCoverageReport = struct { return; } - this.result.* = this.byte_range_mapping.generateCodeCoverageReportFromBlocks( + this.result.* = this.byte_range_mapping.generateReportFromBlocks( this.allocator, this.byte_range_mapping.source_url, blocks, @@ -357,7 +411,7 @@ pub const ByteRangeMapping = struct { return entry; } - pub fn generateCodeCoverageReportFromBlocks( + pub fn generateReportFromBlocks( this: *ByteRangeMapping, allocator: std.mem.Allocator, source_url: bun.JSC.ZigString.Slice, @@ -370,8 +424,9 @@ pub const ByteRangeMapping = struct { var executable_lines: Bitset = Bitset{}; var lines_which_have_executed: Bitset = Bitset{}; const parsed_mappings_ = bun.JSC.VirtualMachine.get().source_mappings.get(source_url.slice()); + var line_hits = LinesHits{}; - var functions = std.ArrayListUnmanaged(CodeCoverageReport.Block){}; + var functions = std.ArrayListUnmanaged(Block){}; try functions.ensureTotalCapacityPrecise(allocator, function_blocks.len); errdefer functions.deinit(allocator); var functions_which_have_executed: Bitset = try Bitset.initEmpty(allocator, function_blocks.len); @@ -379,7 +434,7 @@ pub const ByteRangeMapping = struct { var stmts_which_have_executed: Bitset = try Bitset.initEmpty(allocator, blocks.len); errdefer stmts_which_have_executed.deinit(allocator); - var stmts = std.ArrayListUnmanaged(CodeCoverageReport.Block){}; + var stmts = std.ArrayListUnmanaged(Block){}; try stmts.ensureTotalCapacityPrecise(allocator, function_blocks.len); errdefer stmts.deinit(allocator); @@ -391,6 +446,13 @@ pub const ByteRangeMapping = struct { line_count = @truncate(line_starts.len); executable_lines = try Bitset.initEmpty(allocator, line_count); lines_which_have_executed = try Bitset.initEmpty(allocator, line_count); + line_hits = try LinesHits.initCapacity(allocator, line_count); + line_hits.len = line_count; + const line_hits_slice = line_hits.slice(); + @memset(line_hits_slice, 0); + + errdefer line_hits.deinitWithAllocator(allocator); + for (blocks, 0..) |block, i| { if (block.endOffset < 0 or block.startOffset < 0) continue; // does not map to anything @@ -415,6 +477,7 @@ pub const ByteRangeMapping = struct { executable_lines.set(line); if (has_executed) { lines_which_have_executed.set(line); + line_hits_slice[line] += 1; } } @@ -455,6 +518,7 @@ pub const ByteRangeMapping = struct { // functions that have executed have non-executable lines in them and thats fine. if (!did_fn_execute) { const end = @min(max_line, line_count); + @memset(line_hits_slice[min_line..end], 0); for (min_line..end) |line| { executable_lines.set(line); lines_which_have_executed.unset(line); @@ -473,6 +537,11 @@ pub const ByteRangeMapping = struct { line_count = @as(u32, @truncate(parsed_mapping.input_line_count)) + 1; executable_lines = try Bitset.initEmpty(allocator, line_count); lines_which_have_executed = try Bitset.initEmpty(allocator, line_count); + line_hits = try LinesHits.initCapacity(allocator, line_count); + line_hits.len = line_count; + const line_hits_slice = line_hits.slice(); + @memset(line_hits_slice, 0); + errdefer line_hits.deinitWithAllocator(allocator); for (blocks, 0..) |block, i| { if (block.endOffset < 0 or block.startOffset < 0) continue; // does not map to anything @@ -499,6 +568,7 @@ pub const ByteRangeMapping = struct { executable_lines.set(line); if (has_executed) { lines_which_have_executed.set(line); + line_hits_slice[line] += 1; } min_line = @min(min_line, line); @@ -557,6 +627,7 @@ pub const ByteRangeMapping = struct { for (min_line..end) |line| { executable_lines.set(line); lines_which_have_executed.unset(line); + line_hits_slice[line] = 0; } } @@ -571,11 +642,12 @@ pub const ByteRangeMapping = struct { unreachable; } - return CodeCoverageReport{ + return .{ .source_url = source_url, .functions = functions, .executable_lines = executable_lines, .lines_which_have_executed = lines_which_have_executed, + .line_hits = line_hits, .total_lines = line_count, .stmts = stmts, .functions_which_have_executed = functions_which_have_executed, @@ -600,7 +672,7 @@ pub const ByteRangeMapping = struct { } var url_slice = source_url.toUTF8(bun.default_allocator); defer url_slice.deinit(); - var report = this.generateCodeCoverageReportFromBlocks(bun.default_allocator, url_slice, blocks, function_blocks, ignore_sourcemap) catch { + var report = this.generateReportFromBlocks(bun.default_allocator, url_slice, blocks, function_blocks, ignore_sourcemap) catch { globalThis.throwOutOfMemory(); return .zero; }; @@ -613,7 +685,7 @@ pub const ByteRangeMapping = struct { var buffered_writer = mutable_str.bufferedWriter(); var writer = buffered_writer.writer(); - report.writeFormat(source_url.utf8ByteLength(), &coverage_fraction, "", &writer, false) catch { + CodeCoverageReport.Text.writeFormat(&report, source_url.utf8ByteLength(), &coverage_fraction, "", &writer, false) catch { globalThis.throwOutOfMemory(); return .zero; }; @@ -655,3 +727,8 @@ pub const CoverageFraction = struct { failing: bool = false, }; + +pub const Block = struct { + start_line: u32 = 0, + end_line: u32 = 0, +}; diff --git a/src/sourcemap/sourcemap.zig b/src/sourcemap/sourcemap.zig index 6dde80eaef03e..8277ae948ee64 100644 --- a/src/sourcemap/sourcemap.zig +++ b/src/sourcemap/sourcemap.zig @@ -17,6 +17,7 @@ const URL = bun.URL; const FileSystem = bun.fs.FileSystem; const SourceMap = @This(); +const debug = bun.Output.scoped(.SourceMap, false); /// Coordinates in source maps are stored using relative offsets for size /// reasons. When joining together chunks of a source map that were emitted @@ -52,7 +53,7 @@ pub const ParseUrlResultHint = union(enum) { pub const ParseUrl = struct { /// Populated when `mappings_only` or `all`. - map: ?*Mapping.ParsedSourceMap = null, + map: ?*ParsedSourceMap = null, /// Populated when `all` /// May be `null` even when requested. mapping: ?Mapping = null, @@ -76,7 +77,9 @@ pub fn parseUrl( ) !ParseUrl { const json_bytes = json_bytes: { const data_prefix = "data:application/json"; + if (bun.strings.hasPrefixComptime(source, data_prefix) and source.len > (data_prefix.len + 1)) try_data_url: { + debug("parse (data url, {d} bytes)", .{source.len}); switch (source[data_prefix.len]) { ';' => { const encoding = bun.sliceTo(source[data_prefix.len + 1 ..], ','); @@ -118,16 +121,20 @@ pub fn parseJSON( var log = bun.logger.Log.init(arena); defer log.deinit(); - var json = bun.JSON.ParseJSON(&json_src, &log, arena) catch { - return error.InvalidJSON; - }; - // the allocator given to the JS parser is not respected for all parts // of the parse, so we need to remember to reset the ast store + bun.JSAst.Expr.Data.Store.reset(); + bun.JSAst.Stmt.Data.Store.reset(); defer { + // the allocator given to the JS parser is not respected for all parts + // of the parse, so we need to remember to reset the ast store bun.JSAst.Expr.Data.Store.reset(); bun.JSAst.Stmt.Data.Store.reset(); } + debug("parse (JSON, {d} bytes)", .{source.len}); + var json = bun.JSON.ParseJSON(&json_src, &log, arena) catch { + return error.InvalidJSON; + }; if (json.get("version")) |version| { if (version.data != .e_number or version.data.e_number.value != 3.0) { @@ -192,12 +199,11 @@ pub fn parseJSON( .fail => |fail| return fail.err, }; - const ptr = bun.default_allocator.create(Mapping.ParsedSourceMap) catch bun.outOfMemory(); - ptr.* = map_data; + const ptr = ParsedSourceMap.new(map_data); ptr.external_source_names = source_paths_slice.?; break :map ptr; } else null; - errdefer if (map) |m| m.deinit(bun.default_allocator); + errdefer if (map) |m| m.deref(); const mapping, const source_index = switch (hint) { .source_only => |index| .{ null, index }, @@ -242,27 +248,37 @@ pub const Mapping = struct { original: LineColumnOffset, source_index: i32, + pub const List = bun.MultiArrayList(Mapping); + pub const Lookup = struct { mapping: Mapping, - source_map: *ParsedSourceMap, + source_map: ?*ParsedSourceMap = null, /// Owned by default_allocator always /// use `getSourceCode` to access this as a Slice prefetched_source_code: ?[]const u8, /// This creates a bun.String if the source remap *changes* the source url, - /// a case that happens only when the source map points to another file. + /// which is only possible if the executed file differs from the source file: + /// + /// - `bun build --sourcemap`, it is another file on disk + /// - `bun build --compile --sourcemap`, it is an embedded file. pub fn displaySourceURLIfNeeded(lookup: Lookup, base_filename: []const u8) ?bun.String { + const source_map = lookup.source_map orelse return null; // See doc comment on `external_source_names` - if (lookup.source_map.external_source_names.len == 0) + if (source_map.external_source_names.len == 0) return null; - if (lookup.mapping.source_index >= lookup.source_map.external_source_names.len) + if (lookup.mapping.source_index >= source_map.external_source_names.len) return null; - const name = lookup.source_map.external_source_names[@intCast(lookup.mapping.source_index)]; + const name = source_map.external_source_names[@intCast(lookup.mapping.source_index)]; + + if (source_map.is_standalone_module_graph) { + return bun.String.createUTF8(name); + } if (std.fs.path.isAbsolute(base_filename)) { const dir = bun.path.dirname(base_filename, .auto); - return bun.String.init(bun.path.joinAbs(dir, .auto, name)); + return bun.String.createUTF8(bun.path.joinAbs(dir, .auto, name)); } return bun.String.init(name); @@ -270,30 +286,47 @@ pub const Mapping = struct { /// Only valid if `lookup.source_map.isExternal()` /// This has the possibility of invoking a call to the filesystem. + /// + /// This data is freed after printed on the assumption that printing + /// errors to the console are rare (this isnt used for error.stack) pub fn getSourceCode(lookup: Lookup, base_filename: []const u8) ?bun.JSC.ZigString.Slice { const bytes = bytes: { - assert(lookup.source_map.isExternal()); if (lookup.prefetched_source_code) |code| { break :bytes code; } - const provider = lookup.source_map.underlying_provider.provider() orelse + const source_map = lookup.source_map orelse return null; + assert(source_map.isExternal()); + + const provider = source_map.underlying_provider.provider() orelse return null; const index = lookup.mapping.source_index; + // Standalone module graph source maps are stored (in memory) compressed. + // They are decompressed on demand. + if (source_map.is_standalone_module_graph) { + const serialized = source_map.standaloneModuleGraphData(); + if (index >= source_map.external_source_names.len) + return null; + + const code = serialized.sourceFileContents(@intCast(index)); + + return bun.JSC.ZigString.Slice.fromUTF8NeverFree(code orelse return null); + } + if (provider.getSourceMap( base_filename, - lookup.source_map.underlying_provider.load_hint, + source_map.underlying_provider.load_hint, .{ .source_only = @intCast(index) }, )) |parsed| if (parsed.source_contents) |contents| break :bytes contents; - if (index >= lookup.source_map.external_source_names.len) + if (index >= source_map.external_source_names.len) return null; - const name = lookup.source_map.external_source_names[@intCast(index)]; + const name = source_map.external_source_names[@intCast(index)]; var buf: bun.PathBuffer = undefined; const normalized = bun.path.joinAbsStringBufZ( @@ -316,8 +349,6 @@ pub const Mapping = struct { } }; - pub const List = std.MultiArrayList(Mapping); - pub inline fn generatedLine(mapping: Mapping) i32 { return mapping.generated.lines; } @@ -379,6 +410,8 @@ pub const Mapping = struct { sources_count: i32, input_line_count: usize, ) ParseResult { + debug("parse mappings ({d} bytes)", .{bytes.len}); + var mapping = Mapping.List{}; if (estimated_mapping_count) |count| { mapping.ensureTotalCapacity(allocator, count) catch unreachable; @@ -567,114 +600,129 @@ pub const Mapping = struct { }, }; } +}; - pub const ParseResult = union(enum) { - fail: struct { - loc: Logger.Loc, - err: anyerror, - value: i32 = 0, - msg: []const u8 = "", - - pub fn toData(this: @This(), path: []const u8) Logger.Data { - return Logger.Data{ - .location = Logger.Location{ - .file = path, - .offset = this.loc.toUsize(), - }, - .text = this.msg, - }; - } - }, - success: ParsedSourceMap, - }; +pub const ParseResult = union(enum) { + fail: struct { + loc: Logger.Loc, + err: anyerror, + value: i32 = 0, + msg: []const u8 = "", + + pub fn toData(this: @This(), path: []const u8) Logger.Data { + return Logger.Data{ + .location = Logger.Location{ + .file = path, + .offset = this.loc.toUsize(), + }, + .text = this.msg, + }; + } + }, + success: ParsedSourceMap, +}; - pub const ParsedSourceMap = struct { - input_line_count: usize = 0, - mappings: Mapping.List = .{}, - /// If this is empty, this implies that the source code is a single file - /// transpiled on-demand. If there are items, then it means this is a file - /// loaded without transpilation but with external sources. This array - /// maps `source_index` to the correct filename. - external_source_names: []const []const u8 = &.{}, - /// In order to load source contents from a source-map after the fact, - /// a handle to the underying source provider is stored. Within this pointer, - /// a flag is stored if it is known to be an inline or external source map. - /// - /// Source contents are large, we don't preserve them in memory. This has - /// the downside of repeatedly re-decoding sourcemaps if multiple errors - /// are emitted (specifically with Bun.inspect / unhandled; the ones that - /// rely on source contents) - underlying_provider: SourceContentPtr = .{ .data = 0 }, - - const SourceContentPtr = packed struct(u64) { - load_hint: SourceMapLoadHint = .none, - data: u62, - - fn fromProvider(p: *SourceProviderMap) SourceContentPtr { - return .{ .data = @intCast(@intFromPtr(p)) }; - } +pub const ParsedSourceMap = struct { + input_line_count: usize = 0, + mappings: Mapping.List = .{}, + /// If this is empty, this implies that the source code is a single file + /// transpiled on-demand. If there are items, then it means this is a file + /// loaded without transpilation but with external sources. This array + /// maps `source_index` to the correct filename. + external_source_names: []const []const u8 = &.{}, + /// In order to load source contents from a source-map after the fact, + // / a handle to the underlying source provider is stored. Within this pointer, + /// a flag is stored if it is known to be an inline or external source map. + /// + /// Source contents are large, we don't preserve them in memory. This has + /// the downside of repeatedly re-decoding sourcemaps if multiple errors + /// are emitted (specifically with Bun.inspect / unhandled; the ones that + /// rely on source contents) + underlying_provider: SourceContentPtr = .{ .data = 0 }, - pub fn provider(sc: SourceContentPtr) ?*SourceProviderMap { - return @ptrFromInt(sc.data); - } - }; + ref_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(1), - pub fn isExternal(psm: *ParsedSourceMap) bool { - return psm.external_source_names.len != 0; - } + is_standalone_module_graph: bool = false, - pub fn deinit(this: *ParsedSourceMap, allocator: std.mem.Allocator) void { - this.mappings.deinit(allocator); + pub usingnamespace bun.NewThreadSafeRefCounted(ParsedSourceMap, deinitFn); - if (this.external_source_names.len > 0) { - for (this.external_source_names) |name| - allocator.free(name); - allocator.free(this.external_source_names); - } + const SourceContentPtr = packed struct(u64) { + load_hint: SourceMapLoadHint = .none, + data: u62, - allocator.destroy(this); + fn fromProvider(p: *SourceProviderMap) SourceContentPtr { + return .{ .data = @intCast(@intFromPtr(p)) }; } - pub fn writeVLQs(map: ParsedSourceMap, writer: anytype) !void { - var last_col: i32 = 0; - var last_src: i32 = 0; - var last_ol: i32 = 0; - var last_oc: i32 = 0; - var current_line: i32 = 0; - for ( - map.mappings.items(.generated), - map.mappings.items(.original), - map.mappings.items(.source_index), - 0.., - ) |gen, orig, source_index, i| { - if (current_line != gen.lines) { - assert(gen.lines > current_line); - const inc = gen.lines - current_line; - try writer.writeByteNTimes(';', @intCast(inc)); - current_line = gen.lines; - last_col = 0; - } else if (i != 0) { - try writer.writeByte(','); - } - try encodeVLQ(gen.columns - last_col).writeTo(writer); - last_col = gen.columns; - try encodeVLQ(source_index - last_src).writeTo(writer); - last_src = source_index; - try encodeVLQ(orig.lines - last_ol).writeTo(writer); - last_ol = orig.lines; - try encodeVLQ(orig.columns - last_oc).writeTo(writer); - last_oc = orig.columns; - } + pub fn provider(sc: SourceContentPtr) ?*SourceProviderMap { + return @ptrFromInt(sc.data); } + }; - pub fn formatVLQs(map: *const ParsedSourceMap) std.fmt.Formatter(formatVLQsImpl) { - return .{ .data = map }; + pub fn isExternal(psm: *ParsedSourceMap) bool { + return psm.external_source_names.len != 0; + } + + fn deinitFn(this: *ParsedSourceMap) void { + this.deinitWithAllocator(bun.default_allocator); + } + + fn deinitWithAllocator(this: *ParsedSourceMap, allocator: std.mem.Allocator) void { + this.mappings.deinit(allocator); + + if (this.external_source_names.len > 0) { + for (this.external_source_names) |name| + allocator.free(name); + allocator.free(this.external_source_names); } - fn formatVLQsImpl(map: *const ParsedSourceMap, comptime _: []const u8, _: std.fmt.FormatOptions, w: anytype) !void { - try map.writeVLQs(w); + this.destroy(); + } + + fn standaloneModuleGraphData(this: *ParsedSourceMap) *bun.StandaloneModuleGraph.SerializedSourceMap.Loaded { + bun.assert(this.is_standalone_module_graph); + return @ptrFromInt(this.underlying_provider.data); + } + + pub fn writeVLQs(map: ParsedSourceMap, writer: anytype) !void { + var last_col: i32 = 0; + var last_src: i32 = 0; + var last_ol: i32 = 0; + var last_oc: i32 = 0; + var current_line: i32 = 0; + for ( + map.mappings.items(.generated), + map.mappings.items(.original), + map.mappings.items(.source_index), + 0.., + ) |gen, orig, source_index, i| { + if (current_line != gen.lines) { + assert(gen.lines > current_line); + const inc = gen.lines - current_line; + try writer.writeByteNTimes(';', @intCast(inc)); + current_line = gen.lines; + last_col = 0; + } else if (i != 0) { + try writer.writeByte(','); + } + try encodeVLQ(gen.columns - last_col).writeTo(writer); + last_col = gen.columns; + try encodeVLQ(source_index - last_src).writeTo(writer); + last_src = source_index; + try encodeVLQ(orig.lines - last_ol).writeTo(writer); + last_ol = orig.lines; + try encodeVLQ(orig.columns - last_oc).writeTo(writer); + last_oc = orig.columns; } - }; + } + + pub fn formatVLQs(map: *const ParsedSourceMap) std.fmt.Formatter(formatVLQsImpl) { + return .{ .data = map }; + } + + fn formatVLQsImpl(map: *const ParsedSourceMap, comptime _: []const u8, _: std.fmt.FormatOptions, w: anytype) !void { + try map.writeVLQs(w); + } }; /// For some sourcemap loading code, this enum is used as a hint if it should @@ -726,6 +774,7 @@ pub const SourceProviderMap = opaque { var sfb = std.heap.stackFallback(65536, bun.default_allocator); var arena = bun.ArenaAllocator.init(sfb.get()); defer arena.deinit(); + const allocator = arena.allocator(); const new_load_hint: SourceMapLoadHint, const parsed = parsed: { var inline_err: ?anyerror = null; @@ -737,9 +786,9 @@ pub const SourceProviderMap = opaque { bun.assert(source.tag == .ZigString); const found_url = (if (source.is8Bit()) - findSourceMappingURL(u8, source.latin1(), arena.allocator()) + findSourceMappingURL(u8, source.latin1(), allocator) else - findSourceMappingURL(u16, source.utf16(), arena.allocator())) orelse + findSourceMappingURL(u16, source.utf16(), allocator)) orelse break :try_inline; defer found_url.deinit(); @@ -747,7 +796,7 @@ pub const SourceProviderMap = opaque { .is_inline_map, parseUrl( bun.default_allocator, - arena.allocator(), + allocator, found_url.slice(), result, ) catch |err| { @@ -766,7 +815,7 @@ pub const SourceProviderMap = opaque { @memcpy(load_path_buf[source_filename.len..][0..4], ".map"); const load_path = load_path_buf[0 .. source_filename.len + 4]; - const data = switch (bun.sys.File.readFrom(std.fs.cwd(), load_path, arena.allocator())) { + const data = switch (bun.sys.File.readFrom(std.fs.cwd(), load_path, allocator)) { .err => break :try_external, .result => |data| data, }; @@ -775,7 +824,7 @@ pub const SourceProviderMap = opaque { .is_external_map, parseJSON( bun.default_allocator, - arena.allocator(), + allocator, data, result, ) catch |err| { @@ -806,7 +855,7 @@ pub const SourceProviderMap = opaque { return null; }; if (parsed.map) |ptr| { - ptr.underlying_provider = Mapping.ParsedSourceMap.SourceContentPtr.fromProvider(provider); + ptr.underlying_provider = ParsedSourceMap.SourceContentPtr.fromProvider(provider); ptr.underlying_provider.load_hint = new_load_hint; } return parsed; @@ -1910,3 +1959,5 @@ pub const DebugIDFormatter = struct { }; const assert = bun.assert; + +pub usingnamespace @import("./CodeCoverage.zig"); diff --git a/src/sourcemap/vlq_bench.zig b/src/sourcemap/vlq_bench.zig deleted file mode 100644 index 1d6c40f2fbd0d..0000000000000 --- a/src/sourcemap/vlq_bench.zig +++ /dev/null @@ -1,323 +0,0 @@ -const std = @import("std"); - -const SourceMap = struct { - const base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - const vlq_lookup_table: [256]VLQ = brk: { - var entries: [256]VLQ = undefined; - var i: usize = 0; - var j: i32 = 0; - while (i < 256) : (i += 1) { - entries[i] = encodeVLQ(j); - j += 1; - } - break :brk entries; - }; - - const vlq_max_in_bytes = 8; - pub const VLQ = struct { - // We only need to worry about i32 - // That means the maximum VLQ-encoded value is 8 bytes - // because there are only 4 bits of number inside each VLQ value - // and it expects i32 - // therefore, it can never be more than 32 bits long - // I believe the actual number is 7 bytes long, however we can add an extra byte to be more cautious - bytes: [vlq_max_in_bytes]u8, - len: u4 = 0, - }; - - pub fn encodeVLQWithLookupTable( - value: i32, - ) VLQ { - return if (value >= 0 and value <= 255) - vlq_lookup_table[@as(usize, @intCast(value))] - else - encodeVLQ(value); - } - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the continuation - // bit. The continuation bit tells us whether there are more digits in this - // value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - // - pub fn encodeVLQ( - value: i32, - ) VLQ { - var len: u4 = 0; - var bytes: [vlq_max_in_bytes]u8 = undefined; - - var vlq: u32 = if (value >= 0) - @as(u32, @bitCast(value << 1)) - else - @as(u32, @bitCast((-value << 1) | 1)); - - // source mappings are limited to i32 - comptime var i: usize = 0; - inline while (i < vlq_max_in_bytes) : (i += 1) { - var digit = vlq & 31; - vlq >>= 5; - - // If there are still more digits in this value, we must make sure the - // continuation bit is marked - if (vlq != 0) { - digit |= 32; - } - - bytes[len] = base64[digit]; - len += 1; - - if (vlq == 0) { - return VLQ{ - .bytes = bytes, - .len = len, - }; - } - } - - return .{ .bytes = bytes, .len = 0 }; - } - - pub const VLQResult = struct { - value: i32 = 0, - start: usize = 0, - }; - - // base64 stores values up to 7 bits - const base64_lut: [std.math.maxInt(u7)]u7 = brk: { - @setEvalBranchQuota(9999); - var bytes = [_]u7{std.math.maxInt(u7)} ** std.math.maxInt(u7); - - for (base64, 0..) |c, i| { - bytes[c] = i; - } - - break :brk bytes; - }; - - pub fn decodeVLQ(encoded: []const u8, start: usize) VLQResult { - var shift: u8 = 0; - var vlq: u32 = 0; - - // hint to the compiler what the maximum value is - const encoded_ = encoded[start..][0..@min(encoded.len - start, comptime (vlq_max_in_bytes + 1))]; - - // inlining helps for the 1 or 2 byte case, hurts a little for larger - comptime var i: usize = 0; - inline while (i < vlq_max_in_bytes + 1) : (i += 1) { - const index = @as(u32, base64_lut[@as(u7, @truncate(encoded_[i]))]); - - // decode a byte - vlq |= (index & 31) << @as(u5, @truncate(shift)); - shift += 5; - - // Stop if there's no continuation bit - if ((index & 32) == 0) { - return VLQResult{ - .start = i + start, - .value = if ((vlq & 1) == 0) - @as(i32, @intCast(vlq >> 1)) - else - -@as(i32, @intCast((vlq >> 1))), - }; - } - } - - return VLQResult{ .start = start + encoded_.len, .value = 0 }; - } -}; - -pub fn main() anyerror!void { - const args = try std.process.argsAlloc(std.heap.c_allocator); - const how_many = try std.fmt.parseInt(u64, args[args.len - 1], 10); - - var numbers = try std.heap.c_allocator.alloc(i32, how_many); - var results = try std.heap.c_allocator.alloc(SourceMap.VLQ, how_many); - var leb_buf = try std.heap.c_allocator.alloc(u8, how_many * 8); - const byte_size = std.mem.sliceAsBytes(numbers).len; - - var rand = std.rand.DefaultPrng.init(0); - - std.debug.print("Random values:\n\n", .{}); - - for (numbers, 0..) |_, i| { - numbers[i] = rand.random().int(i32); - } - - { - var timer = try std.time.Timer.start(); - - for (numbers, 0..) |n, i| { - results[i] = SourceMap.encodeVLQ(n); - } - const elapsed = timer.read(); - std.debug.print("[{d}] encode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - - for (numbers, 0..) |n, i| { - results[i] = SourceMap.encodeVLQWithLookupTable(n); - } - const elapsed = timer.read(); - std.debug.print("[{d}] encodeWithLookupTable: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - - for (results, 0..) |n, i| { - numbers[i] = SourceMap.decodeVLQ(n.bytes[0..n.len], 0).value; - } - - const elapsed = timer.read(); - std.debug.print("[{d}] decode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - var stream = std.io.fixedBufferStream(leb_buf); - var writer = stream.writer(); - for (numbers) |n| { - std.leb.writeILEB128(writer, n) catch unreachable; - } - const elapsed = timer.read(); - std.debug.print("[{d}] ILEB128 encode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - var stream = std.io.fixedBufferStream(leb_buf); - var reader = stream.reader(); - for (numbers, 0..) |_, i| { - numbers[i] = std.leb.readILEB128(i32, reader) catch unreachable; - } - const elapsed = timer.read(); - std.debug.print("[{d}] ILEB128 decode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - std.debug.print("\nNumbers between 0 - 8192:\n\n", .{}); - - for (numbers, 0..) |_, i| { - numbers[i] = rand.random().intRangeAtMost(i32, 0, 8192); - } - - { - var timer = try std.time.Timer.start(); - - for (numbers, 0..) |n, i| { - results[i] = SourceMap.encodeVLQ(n); - } - const elapsed = timer.read(); - std.debug.print("[{d}] encode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - - for (numbers, 0..) |n, i| { - results[i] = SourceMap.encodeVLQWithLookupTable(n); - } - const elapsed = timer.read(); - std.debug.print("[{d}] encodeWithLookupTable: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - - for (results, 0..) |n, i| { - numbers[i] = SourceMap.decodeVLQ(n.bytes[0..n.len], 0).value; - } - - const elapsed = timer.read(); - std.debug.print("[{d}] decode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - var stream = std.io.fixedBufferStream(leb_buf); - var writer = stream.writer(); - for (numbers) |n| { - std.leb.writeILEB128(writer, n) catch unreachable; - } - const elapsed = timer.read(); - std.debug.print("[{d}] ILEB128 encode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - var stream = std.io.fixedBufferStream(leb_buf); - var reader = stream.reader(); - for (numbers, 0..) |_, i| { - numbers[i] = std.leb.readILEB128(i32, reader) catch unreachable; - } - const elapsed = timer.read(); - std.debug.print("[{d}] ILEB128 decode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - std.debug.print("\nNumbers between 0 - 255:\n\n", .{}); - - for (numbers, 0..) |_, i| { - numbers[i] = rand.random().intRangeAtMost(i32, 0, 255); - } - - { - var timer = try std.time.Timer.start(); - - for (numbers, 0..) |n, i| { - results[i] = SourceMap.encodeVLQ(n); - } - const elapsed = timer.read(); - std.debug.print("[{d}] encode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - - for (numbers, 0..) |n, i| { - results[i] = SourceMap.encodeVLQWithLookupTable(n); - } - const elapsed = timer.read(); - std.debug.print("[{d}] encodeWithLookupTable: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - - for (results, 0..) |n, i| { - numbers[i] = SourceMap.decodeVLQ(n.bytes[0..n.len], 0).value; - } - - const elapsed = timer.read(); - std.debug.print("[{d}] decode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - var stream = std.io.fixedBufferStream(leb_buf); - var writer = stream.writer(); - for (numbers) |n| { - std.leb.writeILEB128(writer, n) catch unreachable; - } - const elapsed = timer.read(); - std.debug.print("[{d}] ILEB128 encode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } - - { - var timer = try std.time.Timer.start(); - var stream = std.io.fixedBufferStream(leb_buf); - var reader = stream.reader(); - for (numbers, 0..) |_, i| { - numbers[i] = std.leb.readILEB128(i32, reader) catch unreachable; - } - const elapsed = timer.read(); - std.debug.print("[{d}] ILEB128 decode: {} in {}\n", .{ how_many, std.fmt.fmtIntSizeDec(byte_size), std.fmt.fmtDuration(elapsed) }); - } -} diff --git a/src/sql/postgres.zig b/src/sql/postgres.zig new file mode 100644 index 0000000000000..009146cdf9b47 --- /dev/null +++ b/src/sql/postgres.zig @@ -0,0 +1,4218 @@ +const bun = @import("root").bun; +const JSC = bun.JSC; +const String = bun.String; +const uws = bun.uws; +const std = @import("std"); +const debug = bun.Output.scoped(.Postgres, false); +const int4 = u32; +const PostgresInt32 = int4; +const short = u16; +const PostgresShort = u16; +const Crypto = JSC.API.Bun.Crypto; +const JSValue = JSC.JSValue; + +const Data = union(enum) { + owned: bun.ByteList, + temporary: []const u8, + empty: void, + + pub fn toOwned(this: @This()) !bun.ByteList { + return switch (this) { + .owned => this.owned, + .temporary => bun.ByteList.init(try bun.default_allocator.dupe(u8, this.temporary)), + .empty => bun.ByteList.init(&.{}), + }; + } + + pub fn deinit(this: *@This()) void { + switch (this.*) { + .owned => this.owned.deinitWithAllocator(bun.default_allocator), + .temporary => {}, + .empty => {}, + } + } + + /// Zero bytes before deinit + /// Generally, for security reasons. + pub fn zdeinit(this: *@This()) void { + switch (this.*) { + .owned => { + + // Zero bytes before deinit + @memset(this.owned.slice(), 0); + + this.owned.deinitWithAllocator(bun.default_allocator); + }, + .temporary => {}, + .empty => {}, + } + } + + pub fn slice(this: @This()) []const u8 { + return switch (this) { + .owned => this.owned.slice(), + .temporary => this.temporary, + .empty => "", + }; + } + + pub fn substring(this: @This(), start_index: usize, end_index: usize) Data { + return switch (this) { + .owned => .{ .temporary = this.owned.slice()[start_index..end_index] }, + .temporary => .{ .temporary = this.temporary[start_index..end_index] }, + .empty => .{ .empty = {} }, + }; + } + + pub fn sliceZ(this: @This()) [:0]const u8 { + return switch (this) { + .owned => this.owned.slice()[0..this.owned.len :0], + .temporary => this.temporary[0..this.temporary.len :0], + .empty => "", + }; + } +}; + +pub const protocol = struct { + pub const ArrayList = struct { + array: *std.ArrayList(u8), + + pub fn offset(this: @This()) usize { + return this.array.items.len; + } + + pub fn write(this: @This(), bytes: []const u8) anyerror!void { + try this.array.appendSlice(bytes); + } + + pub fn pwrite(this: @This(), bytes: []const u8, i: usize) anyerror!void { + @memcpy(this.array.items[i..][0..bytes.len], bytes); + } + + pub const Writer = NewWriter(@This()); + }; + + pub const StackReader = struct { + buffer: []const u8 = "", + offset: *usize, + message_start: *usize, + + pub fn markMessageStart(this: @This()) void { + this.message_start.* = this.offset.*; + } + + pub fn ensureLength(this: @This(), length: usize) bool { + return this.buffer.len >= (this.offset.* + length); + } + + pub fn init(buffer: []const u8, offset: *usize, message_start: *usize) protocol.NewReader(StackReader) { + return .{ + .wrapped = .{ + .buffer = buffer, + .offset = offset, + .message_start = message_start, + }, + }; + } + + pub fn peek(this: StackReader) []const u8 { + return this.buffer[this.offset.*..]; + } + pub fn skip(this: StackReader, count: usize) void { + if (this.offset.* + count > this.buffer.len) { + this.offset.* = this.buffer.len; + return; + } + + this.offset.* += count; + } + pub fn ensureCapacity(this: StackReader, count: usize) bool { + return this.buffer.len >= (this.offset.* + count); + } + pub fn read(this: StackReader, count: usize) anyerror!Data { + const offset = this.offset.*; + if (!this.ensureCapacity(count)) { + return error.ShortRead; + } + + this.skip(count); + return Data{ + .temporary = this.buffer[offset..this.offset.*], + }; + } + pub fn readZ(this: StackReader) anyerror!Data { + const remaining = this.peek(); + if (bun.strings.indexOfChar(remaining, 0)) |zero| { + this.skip(zero + 1); + return Data{ + .temporary = remaining[0..zero], + }; + } + + return error.ShortRead; + } + }; + + pub fn NewWriterWrap( + comptime Context: type, + comptime offsetFn_: (fn (ctx: Context) usize), + comptime writeFunction_: (fn (ctx: Context, bytes: []const u8) anyerror!void), + comptime pwriteFunction_: (fn (ctx: Context, bytes: []const u8, offset: usize) anyerror!void), + ) type { + return struct { + wrapped: Context, + + const writeFn = writeFunction_; + const pwriteFn = pwriteFunction_; + const offsetFn = offsetFn_; + pub const Ctx = Context; + + pub const WrappedWriter = @This(); + + pub inline fn write(this: @This(), data: []const u8) anyerror!void { + try writeFn(this.wrapped, data); + } + + pub const LengthWriter = struct { + index: usize, + context: WrappedWriter, + + pub fn write(this: LengthWriter) anyerror!void { + try this.context.pwrite(&Int32(this.context.offset() - this.index), this.index); + } + + pub fn writeExcludingSelf(this: LengthWriter) anyerror!void { + try this.context.pwrite(&Int32(this.context.offset() -| (this.index + 4)), this.index); + } + }; + + pub inline fn length(this: @This()) anyerror!LengthWriter { + const i = this.offset(); + try this.int4(0); + return LengthWriter{ + .index = i, + .context = this, + }; + } + + pub inline fn offset(this: @This()) usize { + return offsetFn(this.wrapped); + } + + pub inline fn pwrite(this: @This(), data: []const u8, i: usize) anyerror!void { + try pwriteFn(this.wrapped, data, i); + } + + pub fn int4(this: @This(), value: PostgresInt32) !void { + try this.write(std.mem.asBytes(&@byteSwap(value))); + } + + pub fn sint4(this: @This(), value: i32) !void { + try this.write(std.mem.asBytes(&@byteSwap(value))); + } + + pub fn @"f64"(this: @This(), value: f64) !void { + try this.write(std.mem.asBytes(&@byteSwap(@as(u64, @bitCast(value))))); + } + + pub fn @"f32"(this: @This(), value: f32) !void { + try this.write(std.mem.asBytes(&@byteSwap(@as(u32, @bitCast(value))))); + } + + pub fn short(this: @This(), value: anytype) !void { + try this.write(std.mem.asBytes(&@byteSwap(@as(u16, @intCast(value))))); + } + + pub fn string(this: @This(), value: []const u8) !void { + try this.write(value); + if (value.len == 0 or value[value.len - 1] != 0) + try this.write(&[_]u8{0}); + } + + pub fn bytes(this: @This(), value: []const u8) !void { + try this.write(value); + if (value.len == 0 or value[value.len - 1] != 0) + try this.write(&[_]u8{0}); + } + + pub fn @"bool"(this: @This(), value: bool) !void { + try this.write(if (value) "t" else "f"); + } + + pub fn @"null"(this: @This()) !void { + try this.int4(std.math.maxInt(PostgresInt32)); + } + + pub fn String(this: @This(), value: bun.String) !void { + if (value.isEmpty()) { + try this.write(&[_]u8{0}); + return; + } + + var sliced = value.toUTF8(bun.default_allocator); + defer sliced.deinit(); + const slice = sliced.slice(); + + try this.write(slice); + if (slice.len == 0 or slice[slice.len - 1] != 0) + try this.write(&[_]u8{0}); + } + }; + } + + pub const FieldType = enum(u8) { + /// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a localized translation of one of these. Always present. + S = 'S', + + /// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message). This is identical to the S field except that the contents are never localized. This is present only in messages generated by PostgreSQL versions 9.6 and later. + V = 'V', + + /// Code: the SQLSTATE code for the error (see Appendix A). Not localizable. Always present. + C = 'C', + + /// Message: the primary human-readable error message. This should be accurate but terse (typically one line). Always present. + M = 'M', + + /// Detail: an optional secondary error message carrying more detail about the problem. Might run to multiple lines. + D = 'D', + + /// Hint: an optional suggestion what to do about the problem. This is intended to differ from Detail in that it offers advice (potentially inappropriate) rather than hard facts. Might run to multiple lines. + H = 'H', + + /// Position: the field value is a decimal ASCII integer, indicating an error cursor position as an index into the original query string. The first character has index 1, and positions are measured in characters not bytes. + P = 'P', + + /// Internal position: this is defined the same as the P field, but it is used when the cursor position refers to an internally generated command rather than the one submitted by the client. The q field will always appear when this field appears. + p = 'p', + + /// Internal query: the text of a failed internally-generated command. This could be, for example, an SQL query issued by a PL/pgSQL function. + q = 'q', + + /// Where: an indication of the context in which the error occurred. Presently this includes a call stack traceback of active procedural language functions and internally-generated queries. The trace is one entry per line, most recent first. + W = 'W', + + /// Schema name: if the error was associated with a specific database object, the name of the schema containing that object, if any. + s = 's', + + /// Table name: if the error was associated with a specific table, the name of the table. (Refer to the schema name field for the name of the table's schema.) + t = 't', + + /// Column name: if the error was associated with a specific table column, the name of the column. (Refer to the schema and table name fields to identify the table.) + c = 'c', + + /// Data type name: if the error was associated with a specific data type, the name of the data type. (Refer to the schema name field for the name of the data type's schema.) + d = 'd', + + /// Constraint name: if the error was associated with a specific constraint, the name of the constraint. Refer to fields listed above for the associated table or domain. (For this purpose, indexes are treated as constraints, even if they weren't created with constraint syntax.) + n = 'n', + + /// File: the file name of the source-code location where the error was reported. + F = 'F', + + /// Line: the line number of the source-code location where the error was reported. + L = 'L', + + /// Routine: the name of the source-code routine reporting the error. + R = 'R', + + _, + }; + + pub const FieldMessage = union(FieldType) { + S: String, + V: String, + C: String, + M: String, + D: String, + H: String, + P: String, + p: String, + q: String, + W: String, + s: String, + t: String, + c: String, + d: String, + n: String, + F: String, + L: String, + R: String, + + pub fn format(this: FieldMessage, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + switch (this) { + inline else => |str| { + try std.fmt.format(writer, "{}", .{str}); + }, + } + } + + pub fn deinit(this: *FieldMessage) void { + switch (this.*) { + inline else => |*message| { + message.deref(); + }, + } + } + + pub fn decodeList(comptime Context: type, reader: NewReader(Context)) !std.ArrayListUnmanaged(FieldMessage) { + var messages = std.ArrayListUnmanaged(FieldMessage){}; + while (true) { + const field_int = try reader.int(u8); + if (field_int == 0) break; + const field: FieldType = @enumFromInt(field_int); + + var message = try reader.readZ(); + defer message.deinit(); + if (message.slice().len == 0) break; + + try messages.append(bun.default_allocator, FieldMessage.init(field, message.slice()) catch continue); + } + + return messages; + } + + pub fn init(tag: FieldType, message: []const u8) !FieldMessage { + return switch (tag) { + .S => FieldMessage{ .S = String.createUTF8(message) }, + .V => FieldMessage{ .V = String.createUTF8(message) }, + .C => FieldMessage{ .C = String.createUTF8(message) }, + .M => FieldMessage{ .M = String.createUTF8(message) }, + .D => FieldMessage{ .D = String.createUTF8(message) }, + .H => FieldMessage{ .H = String.createUTF8(message) }, + .P => FieldMessage{ .P = String.createUTF8(message) }, + .p => FieldMessage{ .p = String.createUTF8(message) }, + .q => FieldMessage{ .q = String.createUTF8(message) }, + .W => FieldMessage{ .W = String.createUTF8(message) }, + .s => FieldMessage{ .s = String.createUTF8(message) }, + .t => FieldMessage{ .t = String.createUTF8(message) }, + .c => FieldMessage{ .c = String.createUTF8(message) }, + .d => FieldMessage{ .d = String.createUTF8(message) }, + .n => FieldMessage{ .n = String.createUTF8(message) }, + .F => FieldMessage{ .F = String.createUTF8(message) }, + .L => FieldMessage{ .L = String.createUTF8(message) }, + .R => FieldMessage{ .R = String.createUTF8(message) }, + else => error.UnknownFieldType, + }; + } + }; + + pub fn NewReaderWrap( + comptime Context: type, + comptime markMessageStartFn_: (fn (ctx: Context) void), + comptime peekFn_: (fn (ctx: Context) []const u8), + comptime skipFn_: (fn (ctx: Context, count: usize) void), + comptime ensureCapacityFn_: (fn (ctx: Context, count: usize) bool), + comptime readFunction_: (fn (ctx: Context, count: usize) anyerror!Data), + comptime readZ_: (fn (ctx: Context) anyerror!Data), + ) type { + return struct { + wrapped: Context, + const readFn = readFunction_; + const readZFn = readZ_; + const ensureCapacityFn = ensureCapacityFn_; + const skipFn = skipFn_; + const peekFn = peekFn_; + const markMessageStartFn = markMessageStartFn_; + + pub const Ctx = Context; + + pub inline fn markMessageStart(this: @This()) void { + markMessageStartFn(this.wrapped); + } + + pub inline fn read(this: @This(), count: usize) anyerror!Data { + return try readFn(this.wrapped, count); + } + + pub inline fn eatMessage(this: @This(), comptime msg_: anytype) anyerror!void { + const msg = msg_[1..]; + try this.ensureCapacity(msg.len); + + var input = try readFn(this.wrapped, msg.len); + defer input.deinit(); + if (bun.strings.eqlComptime(input.slice(), msg)) return; + return error.InvalidMessage; + } + + pub fn skip(this: @This(), count: usize) anyerror!void { + skipFn(this.wrapped, count); + } + + pub fn peek(this: @This()) []const u8 { + return peekFn(this.wrapped); + } + + pub inline fn readZ(this: @This()) anyerror!Data { + return try readZFn(this.wrapped); + } + + pub inline fn ensureCapacity(this: @This(), count: usize) anyerror!void { + if (!ensureCapacityFn(this.wrapped, count)) { + return error.ShortRead; + } + } + + pub fn int(this: @This(), comptime Int: type) !Int { + var data = try this.read(@sizeOf((Int))); + defer data.deinit(); + if (comptime Int == u8) { + return @as(Int, data.slice()[0]); + } + return @byteSwap(@as(Int, @bitCast(data.slice()[0..@sizeOf(Int)].*))); + } + + pub fn peekInt(this: @This(), comptime Int: type) ?Int { + const remain = this.peek(); + if (remain.len < @sizeOf(Int)) { + return null; + } + return @byteSwap(@as(Int, @bitCast(remain[0..@sizeOf(Int)].*))); + } + + pub fn expectInt(this: @This(), comptime Int: type, comptime value: comptime_int) !bool { + const actual = try this.int(Int); + return actual == value; + } + + pub fn int4(this: @This()) !PostgresInt32 { + return this.int(PostgresInt32); + } + + pub fn short(this: @This()) !PostgresShort { + return this.int(PostgresShort); + } + + pub fn length(this: @This()) !PostgresInt32 { + const expected = try this.int(PostgresInt32); + if (expected > -1) { + try this.ensureCapacity(@intCast(expected -| 4)); + } + + return expected; + } + + pub const bytes = read; + + pub fn String(this: @This()) !bun.String { + var result = try this.readZ(); + defer result.deinit(); + return bun.String.fromUTF8(result.slice()); + } + }; + } + + pub fn NewReader(comptime Context: type) type { + return NewReaderWrap(Context, Context.markMessageStart, Context.peek, Context.skip, Context.ensureLength, Context.read, Context.readZ); + } + + pub fn NewWriter(comptime Context: type) type { + return NewWriterWrap(Context, Context.offset, Context.write, Context.pwrite); + } + + fn decoderWrap(comptime Container: type, comptime decodeFn: anytype) type { + return struct { + pub fn decode(this: *Container, context: anytype) anyerror!void { + const Context = @TypeOf(context); + try decodeFn(this, Context, NewReader(Context){ .wrapped = context }); + } + }; + } + + fn writeWrap(comptime Container: type, comptime writeFn: anytype) type { + return struct { + pub fn write(this: *Container, context: anytype) anyerror!void { + const Context = @TypeOf(context); + try writeFn(this, Context, NewWriter(Context){ .wrapped = context }); + } + }; + } + + pub const Authentication = union(enum) { + Ok: void, + ClearTextPassword: struct {}, + MD5Password: struct { + salt: [4]u8, + }, + KerberosV5: struct {}, + SCMCredential: struct {}, + GSS: struct {}, + GSSContinue: struct { + data: Data, + }, + SSPI: struct {}, + SASL: struct {}, + SASLContinue: struct { + data: Data, + r: []const u8, + s: []const u8, + i: []const u8, + + pub fn iterationCount(this: *const @This()) !u32 { + return try std.fmt.parseInt(u32, this.i, 0); + } + }, + SASLFinal: struct { + data: Data, + }, + Unknown: void, + + pub fn deinit(this: *@This()) void { + switch (this.*) { + .MD5Password => {}, + .SASL => {}, + .SASLContinue => { + this.SASLContinue.data.zdeinit(); + }, + .SASLFinal => { + this.SASLFinal.data.zdeinit(); + }, + else => {}, + } + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + const message_length = try reader.length(); + + switch (try reader.int4()) { + 0 => { + if (message_length != 8) return error.InvalidMessageLength; + this.* = .{ .Ok = {} }; + }, + 2 => { + if (message_length != 8) return error.InvalidMessageLength; + this.* = .{ + .KerberosV5 = .{}, + }; + }, + 3 => { + if (message_length != 8) return error.InvalidMessageLength; + this.* = .{ + .ClearTextPassword = .{}, + }; + }, + 5 => { + if (message_length != 12) return error.InvalidMessageLength; + if (!try reader.expectInt(u32, 5)) { + return error.InvalidMessage; + } + var salt_data = try reader.bytes(4); + defer salt_data.deinit(); + this.* = .{ + .MD5Password = .{ + .salt = salt_data.slice()[0..4].*, + }, + }; + }, + 7 => { + if (message_length != 8) return error.InvalidMessageLength; + this.* = .{ + .GSS = .{}, + }; + }, + + 8 => { + if (message_length < 9) return error.InvalidMessageLength; + const bytes = try reader.read(message_length - 8); + this.* = .{ + .GSSContinue = .{ + .data = bytes, + }, + }; + }, + 9 => { + if (message_length != 8) return error.InvalidMessageLength; + this.* = .{ + .SSPI = .{}, + }; + }, + + 10 => { + if (message_length < 9) return error.InvalidMessageLength; + try reader.skip(message_length - 8); + this.* = .{ + .SASL = .{}, + }; + }, + + 11 => { + if (message_length < 9) return error.InvalidMessageLength; + var bytes = try reader.bytes(message_length - 8); + errdefer { + bytes.deinit(); + } + + var iter = bun.strings.split(bytes.slice(), ","); + var r: ?[]const u8 = null; + var i: ?[]const u8 = null; + var s: ?[]const u8 = null; + + while (iter.next()) |item| { + if (item.len > 2) { + const key = item[0]; + const after_equals = item[2..]; + if (key == 'r') { + r = after_equals; + } else if (key == 's') { + s = after_equals; + } else if (key == 'i') { + i = after_equals; + } + } + } + + if (r == null) { + debug("Missing r", .{}); + } + + if (s == null) { + debug("Missing s", .{}); + } + + if (i == null) { + debug("Missing i", .{}); + } + + this.* = .{ + .SASLContinue = .{ + .data = bytes, + .r = r orelse return error.InvalidMessage, + .s = s orelse return error.InvalidMessage, + .i = i orelse return error.InvalidMessage, + }, + }; + }, + + 12 => { + if (message_length < 9) return error.InvalidMessageLength; + const remaining: usize = message_length - 8; + + const bytes = try reader.read(remaining); + this.* = .{ + .SASLFinal = .{ + .data = bytes, + }, + }; + }, + + else => { + this.* = .{ .Unknown = {} }; + }, + } + } + + pub const decode = decoderWrap(Authentication, decodeInternal).decode; + }; + + pub const ParameterStatus = struct { + name: Data = .{ .empty = {} }, + value: Data = .{ .empty = {} }, + + pub fn deinit(this: *@This()) void { + this.name.deinit(); + this.value.deinit(); + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + const length = try reader.length(); + bun.assert(length >= 4); + + this.* = .{ + .name = try reader.readZ(), + .value = try reader.readZ(), + }; + } + + pub const decode = decoderWrap(ParameterStatus, decodeInternal).decode; + }; + + pub const BackendKeyData = struct { + process_id: u32 = 0, + secret_key: u32 = 0, + pub const decode = decoderWrap(BackendKeyData, decodeInternal).decode; + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + if (!try reader.expectInt(u32, 12)) { + return error.InvalidBackendKeyData; + } + + this.* = .{ + .process_id = @bitCast(try reader.int4()), + .secret_key = @bitCast(try reader.int4()), + }; + } + }; + + pub const ErrorResponse = struct { + messages: std.ArrayListUnmanaged(FieldMessage) = .{}, + + pub fn format(formatter: ErrorResponse, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + for (formatter.messages.items) |message| { + try std.fmt.format(writer, "{}\n", .{message}); + } + } + + pub fn deinit(this: *ErrorResponse) void { + for (this.messages.items) |*message| { + message.deinit(); + } + this.messages.deinit(bun.default_allocator); + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + var remaining_bytes = try reader.length(); + if (remaining_bytes < 4) return error.InvalidMessageLength; + remaining_bytes -|= 4; + + if (remaining_bytes > 0) { + this.* = .{ + .messages = try FieldMessage.decodeList(Container, reader), + }; + } + } + + pub const decode = decoderWrap(ErrorResponse, decodeInternal).decode; + + pub fn toJS(this: ErrorResponse, globalObject: *JSC.JSGlobalObject) JSValue { + var b = bun.StringBuilder{}; + defer b.deinit(bun.default_allocator); + + for (this.messages.items) |msg| { + b.cap += switch (msg) { + inline else => |m| m.utf8ByteLength(), + } + 1; + } + b.allocate(bun.default_allocator) catch {}; + + for (this.messages.items) |msg| { + var str = switch (msg) { + inline else => |m| m.toUTF8(bun.default_allocator), + }; + defer str.deinit(); + _ = b.append(str.slice()); + _ = b.append("\n"); + } + + return globalObject.createSyntaxErrorInstance("Postgres error occurred\n{s}", .{b.allocatedSlice()[0..b.len]}); + } + }; + + pub const PortalOrPreparedStatement = union(enum) { + portal: []const u8, + prepared_statement: []const u8, + + pub fn slice(this: @This()) []const u8 { + return switch (this) { + .portal => this.portal, + .prepared_statement => this.prepared_statement, + }; + } + + pub fn tag(this: @This()) u8 { + return switch (this) { + .portal => 'P', + .prepared_statement => 'S', + }; + } + }; + + /// Close (F) + /// Byte1('C') + /// - Identifies the message as a Close command. + /// Int32 + /// - Length of message contents in bytes, including self. + /// Byte1 + /// - 'S' to close a prepared statement; or 'P' to close a portal. + /// String + /// - The name of the prepared statement or portal to close (an empty string selects the unnamed prepared statement or portal). + pub const Close = struct { + p: PortalOrPreparedStatement, + + fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const p = this.p; + const count: u32 = @sizeOf((u32)) + 1 + p.slice().len + 1; + const header = [_]u8{ + 'C', + } ++ @byteSwap(count) ++ [_]u8{ + p.tag(), + }; + try writer.write(&header); + try writer.write(p.slice()); + try writer.write(&[_]u8{0}); + } + + pub const write = writeWrap(@This(), writeInternal); + }; + + pub const CloseComplete = [_]u8{'3'} ++ toBytes(Int32(4)); + pub const EmptyQueryResponse = [_]u8{'I'} ++ toBytes(Int32(4)); + pub const Terminate = [_]u8{'X'} ++ toBytes(Int32(4)); + + fn Int32(value: anytype) [4]u8 { + return @bitCast(@byteSwap(@as(int4, @intCast(value)))); + } + + const toBytes = std.mem.toBytes; + + pub const TransactionStatusIndicator = enum(u8) { + /// if idle (not in a transaction block) + I = 'I', + + /// if in a transaction block + T = 'T', + + /// if in a failed transaction block + E = 'E', + + _, + }; + + pub const ReadyForQuery = struct { + status: TransactionStatusIndicator = .I, + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + const length = try reader.length(); + bun.assert(length >= 4); + + const status = try reader.int(u8); + this.* = .{ + .status = @enumFromInt(status), + }; + } + + pub const decode = decoderWrap(ReadyForQuery, decodeInternal).decode; + }; + + pub const FormatCode = enum { + text, + binary, + + pub fn from(value: short) !FormatCode { + return switch (value) { + 0 => .text, + 1 => .binary, + else => error.UnknownFormatCode, + }; + } + }; + + pub const null_int4 = 4294967295; + + pub const DataRow = struct { + pub fn decode(context: anytype, comptime ContextType: type, reader: NewReader(ContextType), comptime forEach: fn (@TypeOf(context), index: u32, bytes: ?*Data) anyerror!bool) anyerror!void { + var remaining_bytes = try reader.length(); + remaining_bytes -|= 4; + + const remaining_fields: usize = @intCast(@max(try reader.short(), 0)); + + for (0..remaining_fields) |index| { + const byte_length = try reader.int4(); + switch (byte_length) { + 0 => break, + null_int4 => { + if (!try forEach(context, @intCast(index), null)) break; + }, + else => { + var bytes = try reader.bytes(@intCast(byte_length)); + if (!try forEach(context, @intCast(index), &bytes)) break; + }, + } + } + } + }; + + pub const BindComplete = [_]u8{'2'} ++ toBytes(Int32(4)); + + pub const FieldDescription = struct { + name: Data = .{ .empty = {} }, + table_oid: int4 = 0, + column_index: short = 0, + type_oid: int4 = 0, + + pub fn typeTag(this: @This()) types.Tag { + return @enumFromInt(@as(short, @truncate(this.type_oid))); + } + + pub fn deinit(this: *@This()) void { + this.name.deinit(); + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + var name = try reader.readZ(); + errdefer { + name.deinit(); + } + // If the field can be identified as a column of a specific table, the object ID of the table; otherwise zero. + // Int16 + // If the field can be identified as a column of a specific table, the attribute number of the column; otherwise zero. + // Int32 + // The object ID of the field's data type. + // Int16 + // The data type size (see pg_type.typlen). Note that negative values denote variable-width types. + // Int32 + // The type modifier (see pg_attribute.atttypmod). The meaning of the modifier is type-specific. + // Int16 + // The format code being used for the field. Currently will be zero (text) or one (binary). In a RowDescription returned from the statement variant of Describe, the format code is not yet known and will always be zero. + this.* = .{ + .table_oid = try reader.int4(), + .column_index = try reader.short(), + .type_oid = try reader.int4(), + .name = .{ .owned = try name.toOwned() }, + }; + + try reader.skip(2 + 4 + 2); + } + + pub const decode = decoderWrap(FieldDescription, decodeInternal).decode; + }; + + pub const RowDescription = struct { + fields: []const FieldDescription = &[_]FieldDescription{}, + pub fn deinit(this: *@This()) void { + for (this.fields) |*field| { + @constCast(field).deinit(); + } + + bun.default_allocator.free(this.fields); + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + var remaining_bytes = try reader.length(); + remaining_bytes -|= 4; + + const field_count: usize = @intCast(@max(try reader.short(), 0)); + var fields = try bun.default_allocator.alloc( + FieldDescription, + field_count, + ); + var remaining = fields; + errdefer { + for (fields[0 .. field_count - remaining.len]) |*field| { + field.deinit(); + } + + bun.default_allocator.free(fields); + } + while (remaining.len > 0) { + try remaining[0].decodeInternal(Container, reader); + remaining = remaining[1..]; + } + this.* = .{ + .fields = fields, + }; + } + + pub const decode = decoderWrap(RowDescription, decodeInternal).decode; + }; + + pub const ParameterDescription = struct { + parameters: []int4 = &[_]int4{}, + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + var remaining_bytes = try reader.length(); + remaining_bytes -|= 4; + + const count = try reader.short(); + const parameters = try bun.default_allocator.alloc(int4, @intCast(@max(count, 0))); + + var data = try reader.read(@as(usize, @intCast(@max(count, 0))) * @sizeOf((int4))); + defer data.deinit(); + const input_params: []align(1) const int4 = toInt32Slice(int4, data.slice()); + for (input_params, parameters) |src, *dest| { + dest.* = @byteSwap(src); + } + + this.* = .{ + .parameters = parameters, + }; + } + + pub const decode = decoderWrap(ParameterDescription, decodeInternal).decode; + }; + + // workaround for zig compiler TODO + fn toInt32Slice(comptime Int: type, slice: []const u8) []align(1) const Int { + return @as([*]align(1) const Int, @ptrCast(slice.ptr))[0 .. slice.len / @sizeOf((Int))]; + } + + pub const NotificationResponse = struct { + pid: int4 = 0, + channel: bun.ByteList = .{}, + payload: bun.ByteList = .{}, + + pub fn deinit(this: *@This()) void { + this.channel.deinitWithAllocator(bun.default_allocator); + this.payload.deinitWithAllocator(bun.default_allocator); + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + const length = try reader.length(); + bun.assert(length >= 4); + + this.* = .{ + .pid = try reader.int4(), + .channel = (try reader.readZ()).toOwned(), + .payload = (try reader.readZ()).toOwned(), + }; + } + + pub const decode = decoderWrap(NotificationResponse, decodeInternal).decode; + }; + + pub const CommandComplete = struct { + command_tag: Data = .{ .empty = {} }, + + pub fn deinit(this: *@This()) void { + this.command_tag.deinit(); + } + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + const length = try reader.length(); + bun.assert(length >= 4); + + const tag = try reader.readZ(); + this.* = .{ + .command_tag = tag, + }; + } + + pub const decode = decoderWrap(CommandComplete, decodeInternal).decode; + }; + + pub const Parse = struct { + name: []const u8 = "", + query: []const u8 = "", + params: []const int4 = &.{}, + + pub fn deinit(this: *Parse) void { + _ = this; + } + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const parameters = this.params; + const count: usize = @sizeOf((u32)) + @sizeOf(u16) + (parameters.len * @sizeOf(u32)) + @max(zCount(this.name), 1) + @max(zCount(this.query), 1); + const header = [_]u8{ + 'P', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.string(this.name); + try writer.string(this.query); + try writer.short(parameters.len); + for (parameters) |parameter| { + try writer.int4(parameter); + } + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const ParseComplete = [_]u8{'1'} ++ toBytes(Int32(4)); + + pub const PasswordMessage = struct { + password: Data = .{ .empty = {} }, + + pub fn deinit(this: *PasswordMessage) void { + this.password.deinit(); + } + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const password = this.password.slice(); + const count: usize = @sizeOf((u32)) + password.len + 1; + const header = [_]u8{ + 'p', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.string(password); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const CopyData = struct { + data: Data = .{ .empty = {} }, + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + const length = try reader.length(); + + const data = try reader.read(@intCast(length -| 5)); + this.* = .{ + .data = data, + }; + } + + pub const decode = decoderWrap(CopyData, decodeInternal).decode; + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const data = this.data.slice(); + const count: u32 = @sizeOf((u32)) + data.len + 1; + const header = [_]u8{ + 'd', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.string(data); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const CopyDone = [_]u8{'c'} ++ toBytes(Int32(4)); + pub const Sync = [_]u8{'S'} ++ toBytes(Int32(4)); + pub const Flush = [_]u8{'H'} ++ toBytes(Int32(4)); + pub const SSLRequest = toBytes(Int32(8)) ++ toBytes(Int32(80877103)); + pub const NoData = [_]u8{'n'} ++ toBytes(Int32(4)); + + pub const SASLInitialResponse = struct { + mechanism: Data = .{ .empty = {} }, + data: Data = .{ .empty = {} }, + + pub fn deinit(this: *SASLInitialResponse) void { + this.mechanism.deinit(); + this.data.deinit(); + } + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const mechanism = this.mechanism.slice(); + const data = this.data.slice(); + const count: usize = @sizeOf(u32) + mechanism.len + 1 + data.len + @sizeOf(u32); + const header = [_]u8{ + 'p', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.string(mechanism); + try writer.int4(@truncate(data.len)); + try writer.write(data); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const SASLResponse = struct { + data: Data = .{ .empty = {} }, + + pub fn deinit(this: *SASLResponse) void { + this.data.deinit(); + } + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const data = this.data.slice(); + const count: usize = @sizeOf(u32) + data.len; + const header = [_]u8{ + 'p', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.write(data); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const StartupMessage = struct { + user: Data, + database: Data, + options: Data = Data{ .empty = {} }, + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const user = this.user.slice(); + const database = this.database.slice(); + const options = this.options.slice(); + + const count: usize = @sizeOf((int4)) + @sizeOf((int4)) + zFieldCount("user", user) + zFieldCount("database", database) + zFieldCount("client_encoding", "UTF8") + zFieldCount("", options) + 1; + + const header = toBytes(Int32(@as(u32, @truncate(count)))); + try writer.write(&header); + try writer.int4(196608); + + try writer.string("user"); + if (user.len > 0) + try writer.string(user); + + try writer.string("database"); + + if (database.len == 0) { + // The database to connect to. Defaults to the user name. + try writer.string(user); + } else { + try writer.string(database); + } + + try writer.string("client_encoding"); + try writer.string("UTF8"); + + if (options.len > 0) + try writer.string(options); + + try writer.write(&[_]u8{0}); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + fn zCount(slice: []const u8) usize { + return if (slice.len > 0) slice.len + 1 else 0; + } + + fn zFieldCount(prefix: []const u8, slice: []const u8) usize { + if (slice.len > 0) { + return zCount(prefix) + zCount(slice); + } + + return zCount(prefix); + } + + pub const Execute = struct { + max_rows: int4 = 0, + p: PortalOrPreparedStatement, + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + try writer.write("E"); + const length = try writer.length(); + if (this.p == .portal) + try writer.string(this.p.portal) + else + try writer.write(&[_]u8{0}); + try writer.int4(this.max_rows); + try length.write(); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const Describe = struct { + p: PortalOrPreparedStatement, + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const message = this.p.slice(); + try writer.write(&[_]u8{ + 'D', + }); + const length = try writer.length(); + try writer.write(&[_]u8{ + this.p.tag(), + }); + try writer.string(message); + try length.write(); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const Query = struct { + message: Data = .{ .empty = {} }, + + pub fn deinit(this: *@This()) void { + this.message.deinit(); + } + + pub fn writeInternal( + this: *const @This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const message = this.message.slice(); + const count: u32 = @sizeOf((u32)) + message.len + 1; + const header = [_]u8{ + 'Q', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.string(message); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const NegotiateProtocolVersion = struct { + version: int4 = 0, + unrecognized_options: std.ArrayListUnmanaged(String) = .{}, + + pub fn decodeInternal( + this: *@This(), + comptime Container: type, + reader: NewReader(Container), + ) !void { + const length = try reader.length(); + bun.assert(length >= 4); + + const version = try reader.int4(); + this.* = .{ + .version = version, + }; + + const unrecognized_options_count: u32 = @intCast(@max(try reader.int4(), 0)); + try this.unrecognized_options.ensureTotalCapacity(bun.default_allocator, unrecognized_options_count); + errdefer { + for (this.unrecognized_options.items) |*option| { + option.deinit(); + } + this.unrecognized_options.deinit(bun.default_allocator); + } + for (0..unrecognized_options_count) |_| { + var option = try reader.readZ(); + if (option.slice().len == 0) break; + defer option.deinit(); + this.unrecognized_options.appendAssumeCapacity( + String.fromUTF8(option), + ); + } + } + }; + + pub const NoticeResponse = struct { + messages: std.ArrayListUnmanaged(FieldMessage) = .{}, + pub fn deinit(this: *NoticeResponse) void { + for (this.messages.items) |*message| { + message.deinit(); + } + this.messages.deinit(bun.default_allocator); + } + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + var remaining_bytes = try reader.length(); + remaining_bytes -|= 4; + + if (remaining_bytes > 0) { + this.* = .{ + .messages = try FieldMessage.decodeList(Container, reader), + }; + } + } + pub const decode = decoderWrap(NoticeResponse, decodeInternal).decode; + }; + + pub const CopyFail = struct { + message: Data = .{ .empty = {} }, + + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + _ = try reader.int4(); + + const message = try reader.readZ(); + this.* = .{ + .message = message, + }; + } + + pub const decode = decoderWrap(CopyFail, decodeInternal).decode; + + pub fn writeInternal( + this: *@This(), + comptime Context: type, + writer: NewWriter(Context), + ) !void { + const message = this.message.slice(); + const count: u32 = @sizeOf((u32)) + message.len + 1; + const header = [_]u8{ + 'f', + } ++ toBytes(Int32(count)); + try writer.write(&header); + try writer.string(message); + } + + pub const write = writeWrap(@This(), writeInternal).write; + }; + + pub const CopyInResponse = struct { + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + _ = reader; + _ = this; + TODO(@This()); + } + + pub const decode = decoderWrap(CopyInResponse, decodeInternal).decode; + }; + + pub const CopyOutResponse = struct { + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + _ = reader; + _ = this; + TODO(@This()); + } + + pub const decode = decoderWrap(CopyInResponse, decodeInternal).decode; + }; + + fn TODO(comptime Type: type) !void { + std.debug.panic("TODO: not implemented {s}", .{bun.meta.typeBaseName(@typeName(Type))}); + } +}; + +pub const types = struct { + // select b.typname, b.oid, b.typarray + // from pg_catalog.pg_type a + // left join pg_catalog.pg_type b on b.oid = a.typelem + // where a.typcategory = 'A' + // group by b.oid, b.typarray + // order by b.oid + // ; + // typname | oid | typarray + // ---------------------------------------+-------+---------- + // bool | 16 | 1000 + // bytea | 17 | 1001 + // char | 18 | 1002 + // name | 19 | 1003 + // int8 | 20 | 1016 + // int2 | 21 | 1005 + // int2vector | 22 | 1006 + // int4 | 23 | 1007 + // regproc | 24 | 1008 + // text | 25 | 1009 + // oid | 26 | 1028 + // tid | 27 | 1010 + // xid | 28 | 1011 + // cid | 29 | 1012 + // oidvector | 30 | 1013 + // pg_type | 71 | 210 + // pg_attribute | 75 | 270 + // pg_proc | 81 | 272 + // pg_class | 83 | 273 + // json | 114 | 199 + // xml | 142 | 143 + // point | 600 | 1017 + // lseg | 601 | 1018 + // path | 602 | 1019 + // box | 603 | 1020 + // polygon | 604 | 1027 + // line | 628 | 629 + // cidr | 650 | 651 + // float4 | 700 | 1021 + // float8 | 701 | 1022 + // circle | 718 | 719 + // macaddr8 | 774 | 775 + // money | 790 | 791 + // macaddr | 829 | 1040 + // inet | 869 | 1041 + // aclitem | 1033 | 1034 + // bpchar | 1042 | 1014 + // varchar | 1043 | 1015 + // date | 1082 | 1182 + // time | 1083 | 1183 + // timestamp | 1114 | 1115 + // timestamptz | 1184 | 1185 + // interval | 1186 | 1187 + // pg_database | 1248 | 12052 + // timetz | 1266 | 1270 + // bit | 1560 | 1561 + // varbit | 1562 | 1563 + // numeric | 1700 | 1231 + pub const Tag = enum(short) { + bool = 16, + bytea = 17, + char = 18, + name = 19, + int8 = 20, + int2 = 21, + int2vector = 22, + int4 = 23, + // regproc = 24, + text = 25, + // oid = 26, + // tid = 27, + // xid = 28, + // cid = 29, + // oidvector = 30, + // pg_type = 71, + // pg_attribute = 75, + // pg_proc = 81, + // pg_class = 83, + json = 114, + xml = 142, + point = 600, + lseg = 601, + path = 602, + box = 603, + polygon = 604, + line = 628, + cidr = 650, + float4 = 700, + float8 = 701, + circle = 718, + macaddr8 = 774, + money = 790, + macaddr = 829, + inet = 869, + aclitem = 1033, + bpchar = 1042, + varchar = 1043, + date = 1082, + time = 1083, + timestamp = 1114, + timestamptz = 1184, + interval = 1186, + pg_database = 1248, + timetz = 1266, + bit = 1560, + varbit = 1562, + numeric = 1700, + uuid = 2950, + + bool_array = 1000, + bytea_array = 1001, + char_array = 1002, + name_array = 1003, + int8_array = 1016, + int2_array = 1005, + int2vector_array = 1006, + int4_array = 1007, + // regproc_array = 1008, + text_array = 1009, + oid_array = 1028, + tid_array = 1010, + xid_array = 1011, + cid_array = 1012, + // oidvector_array = 1013, + // pg_type_array = 210, + // pg_attribute_array = 270, + // pg_proc_array = 272, + // pg_class_array = 273, + json_array = 199, + xml_array = 143, + point_array = 1017, + lseg_array = 1018, + path_array = 1019, + box_array = 1020, + polygon_array = 1027, + line_array = 629, + cidr_array = 651, + float4_array = 1021, + float8_array = 1022, + circle_array = 719, + macaddr8_array = 775, + money_array = 791, + macaddr_array = 1040, + inet_array = 1041, + aclitem_array = 1034, + bpchar_array = 1014, + varchar_array = 1015, + date_array = 1182, + time_array = 1183, + timestamp_array = 1115, + timestamptz_array = 1185, + interval_array = 1187, + pg_database_array = 12052, + timetz_array = 1270, + bit_array = 1561, + varbit_array = 1563, + numeric_array = 1231, + _, + + pub fn isBinaryFormatSupported(this: Tag) bool { + return switch (this) { + // TODO: .int2_array, .float8_array, + .int4_array, .float4_array, .int4, .float8, .float4, .bytea, .numeric => true, + + else => false, + }; + } + + pub fn formatCode(this: Tag) short { + if (this.isBinaryFormatSupported()) { + return 1; + } + + return 0; + } + + fn PostgresBinarySingleDimensionArray(comptime T: type) type { + return extern struct { + // struct array_int4 { + // int4_t ndim; /* Number of dimensions */ + // int4_t _ign; /* offset for data, removed by libpq */ + // Oid elemtype; /* type of element in the array */ + + // /* First dimension */ + // int4_t size; /* Number of elements */ + // int4_t index; /* Index of first element */ + // int4_t first_value; /* Beginning of integer data */ + // }; + + ndim: i32, + offset_for_data: i32, + element_type: i32, + + len: i32, + index: i32, + first_value: T, + + pub fn slice(this: *@This()) []T { + if (this.len == 0) return &.{}; + + var head = @as([*]T, @ptrCast(&this.first_value)); + var current = head; + const len: usize = @intCast(this.len); + for (0..len) |i| { + // Skip every other value as it contains the size of the element + current = current[1..]; + + const val = current[0]; + const Int = std.meta.Int(.unsigned, @bitSizeOf(T)); + const swapped = @byteSwap(@as(Int, @bitCast(val))); + + head[i] = @bitCast(swapped); + + current = current[1..]; + } + + return head[0..len]; + } + + pub fn init(bytes: []const u8) *@This() { + const this: *@This() = @alignCast(@ptrCast(@constCast(bytes.ptr))); + this.ndim = @byteSwap(this.ndim); + this.offset_for_data = @byteSwap(this.offset_for_data); + this.element_type = @byteSwap(this.element_type); + this.len = @byteSwap(this.len); + this.index = @byteSwap(this.index); + return this; + } + }; + } + + pub fn toJSTypedArrayType(comptime T: Tag) JSValue.JSType { + return comptime switch (T) { + .int4_array => .Int32Array, + // .int2_array => .Uint2Array, + .float4_array => .Float32Array, + // .float8_array => .Float64Array, + else => @compileError("TODO: not implemented"), + }; + } + + pub fn byteArrayType(comptime T: Tag) type { + return comptime switch (T) { + .int4_array => i32, + // .int2_array => i16, + .float4_array => f32, + // .float8_array => f64, + else => @compileError("TODO: not implemented"), + }; + } + + pub fn unsignedByteArrayType(comptime T: Tag) type { + return comptime switch (T) { + .int4_array => u32, + // .int2_array => u16, + .float4_array => f32, + // .float8_array => f64, + else => @compileError("TODO: not implemented"), + }; + } + + pub fn pgArrayType(comptime T: Tag) type { + return PostgresBinarySingleDimensionArray(byteArrayType(T)); + } + + fn toJSWithType( + tag: Tag, + globalObject: *JSC.JSGlobalObject, + comptime Type: type, + value: Type, + ) anyerror!JSValue { + switch (tag) { + .numeric => { + return numeric.toJS(globalObject, value); + }, + + .float4, .float8 => { + return numeric.toJS(globalObject, value); + }, + + .json => { + return json.toJS(globalObject, value); + }, + + .bool => { + return @"bool".toJS(globalObject, value); + }, + + .timestamp, .timestamptz => { + return date.toJS(globalObject, value); + }, + + .bytea => { + return bytea.toJS(globalObject, value); + }, + + .int8 => { + return JSValue.fromInt64NoTruncate(globalObject, value); + }, + + .int4 => { + return numeric.toJS(globalObject, value); + }, + + else => { + return string.toJS(globalObject, value); + }, + } + } + + pub fn toJS( + tag: Tag, + globalObject: *JSC.JSGlobalObject, + value: anytype, + ) anyerror!JSValue { + return toJSWithType(tag, globalObject, @TypeOf(value), value); + } + + pub fn fromJS(globalObject: *JSC.JSGlobalObject, value: JSValue) anyerror!Tag { + if (value.isEmptyOrUndefinedOrNull()) { + return Tag.numeric; + } + + if (value.isCell()) { + const tag = value.jsType(); + if (tag.isStringLike()) { + return .text; + } + + if (tag == .JSDate) { + return .timestamp; + } + + if (tag.isTypedArray()) { + if (tag == .Int32Array) + return .int4_array; + + return .bytea; + } + + if (tag == .HeapBigInt) { + return .int8; + } + + if (tag.isArrayLike() and value.getLength(globalObject) > 0) { + return Tag.fromJS(globalObject, value.getIndex(globalObject, 0)); + } + + // Ban these types: + if (tag == .NumberObject) { + return error.JSError; + } + + if (tag == .BooleanObject) { + return error.JSError; + } + + // It's something internal + if (!tag.isIndexable()) { + return error.JSError; + } + + // We will JSON.stringify anything else. + if (tag.isObject()) { + return .json; + } + } + + if (value.isInt32()) { + return .int4; + } + + if (value.isNumber()) { + return .float8; + } + + if (value.isBoolean()) { + return .bool; + } + + return .numeric; + } + }; + + pub const string = struct { + pub const to = 25; + pub const from = [_]short{1002}; + + pub fn toJSWithType( + globalThis: *JSC.JSGlobalObject, + comptime Type: type, + value: Type, + ) anyerror!JSValue { + switch (comptime Type) { + [:0]u8, []u8, []const u8, [:0]const u8 => { + var str = String.fromUTF8(value); + defer str.deinit(); + return str.toJS(globalThis); + }, + + bun.String => { + return value.toJS(globalThis); + }, + + *Data => { + var str = String.fromUTF8(value.slice()); + defer str.deinit(); + defer value.deinit(); + return str.toJS(globalThis); + }, + + else => { + @compileError("unsupported type " ++ @typeName(Type)); + }, + } + } + + pub fn toJS( + globalThis: *JSC.JSGlobalObject, + value: anytype, + ) !JSValue { + var str = try toJSWithType(globalThis, @TypeOf(value), value); + defer str.deinit(); + return str.toJS(globalThis); + } + }; + + pub const numeric = struct { + pub const to = 0; + pub const from = [_]short{ 21, 23, 26, 700, 701 }; + + pub fn toJS( + _: *JSC.JSGlobalObject, + value: anytype, + ) anyerror!JSValue { + return JSValue.jsNumber(value); + } + }; + + pub const json = struct { + pub const to = 114; + pub const from = [_]short{ 114, 3802 }; + + pub fn toJS( + globalObject: *JSC.JSGlobalObject, + value: *Data, + ) anyerror!JSValue { + defer value.deinit(); + var str = bun.String.fromUTF8(value.slice()); + defer str.deref(); + const parse_result = JSValue.parseJSON(str.toJS(globalObject), globalObject); + if (parse_result.isAnyError()) { + globalObject.throwValue(parse_result); + return error.JSError; + } + + return parse_result; + } + }; + + pub const @"bool" = struct { + pub const to = 16; + pub const from = [_]short{16}; + + pub fn toJS( + _: *JSC.JSGlobalObject, + value: bool, + ) anyerror!JSValue { + return JSValue.jsBoolean(value); + } + }; + + pub const date = struct { + pub const to = 1184; + pub const from = [_]short{ 1082, 1114, 1184 }; + + pub fn toJS( + globalObject: *JSC.JSGlobalObject, + value: *Data, + ) anyerror!JSValue { + defer value.deinit(); + return JSValue.fromDateString(globalObject, value.sliceZ().ptr); + } + }; + + pub const bytea = struct { + pub const to = 17; + pub const from = [_]short{17}; + + pub fn toJS( + globalObject: *JSC.JSGlobalObject, + value: *Data, + ) anyerror!JSValue { + defer value.deinit(); + + // var slice = value.slice()[@min(1, value.len)..]; + // _ = slice; + return JSValue.createBuffer(globalObject, value.slice(), null); + } + }; +}; + +const Socket = uws.AnySocket; +const PreparedStatementsMap = std.HashMapUnmanaged(u64, *PostgresSQLStatement, bun.IdentityContext(u64), 80); + +pub const PostgresSQLContext = struct { + tcp: ?*uws.SocketContext = null, + + onQueryResolveFn: JSC.Strong = .{}, + onQueryRejectFn: JSC.Strong = .{}, + + pub fn init(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + var ctx = &globalObject.bunVM().rareData().postgresql_context; + ctx.onQueryResolveFn.set(globalObject, callframe.argument(0)); + ctx.onQueryRejectFn.set(globalObject, callframe.argument(1)); + + return .undefined; + } + + comptime { + if (!JSC.is_bindgen) { + @export(init, .{ + .name = "PostgresSQLContext__init", + }); + } + } +}; + +pub const PostgresSQLQuery = struct { + statement: ?*PostgresSQLStatement = null, + query: bun.String = bun.String.empty, + cursor_name: bun.String = bun.String.empty, + thisValue: JSValue = .undefined, + target: JSC.Strong = JSC.Strong.init(), + status: Status = Status.pending, + is_done: bool = false, + ref_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(1), + binary: bool = false, + pending_value: JSC.Strong = .{}, + + pub usingnamespace JSC.Codegen.JSPostgresSQLQuery; + + pub const Status = enum(u8) { + pending, + written, + running, + binding, + success, + fail, + + pub fn isRunning(this: Status) bool { + return this == .running or this == .binding; + } + }; + + pub fn hasPendingActivity(this: *@This()) bool { + return this.ref_count.load(.monotonic) > 1; + } + + pub fn deinit(this: *@This()) void { + if (this.statement) |statement| { + statement.deref(); + } + this.query.deref(); + this.cursor_name.deref(); + this.target.deinit(); + this.pending_value.deinit(); + + bun.default_allocator.destroy(this); + } + + pub fn finalize(this: *@This()) void { + debug("PostgresSQLQuery finalize", .{}); + this.thisValue = .zero; + this.deref(); + } + + pub fn deref(this: *@This()) void { + const ref_count = this.ref_count.fetchSub(1, .monotonic); + + if (ref_count == 1) { + this.deinit(); + } + } + + pub fn ref(this: *@This()) void { + bun.assert(this.ref_count.fetchAdd(1, .monotonic) > 0); + } + + pub fn onNoData(this: *@This(), globalObject: *JSC.JSGlobalObject) void { + this.status = .success; + defer this.deref(); + + const thisValue = this.thisValue; + const targetValue = this.target.trySwap() orelse JSValue.zero; + if (thisValue == .zero or targetValue == .zero) { + return; + } + + const vm = JSC.VirtualMachine.get(); + const function = vm.rareData().postgresql_context.onQueryResolveFn.get().?; + const event_loop = vm.eventLoop(); + event_loop.runCallback(function, globalObject, thisValue, &.{ + targetValue, + this.pending_value.trySwap() orelse .undefined, + JSValue.jsNumber(0), + JSValue.jsNumber(0), + }); + } + pub fn onWriteFail(this: *@This(), err: anyerror, globalObject: *JSC.JSGlobalObject) void { + this.status = .fail; + this.pending_value.deinit(); + const thisValue = this.thisValue; + const targetValue = this.target.trySwap() orelse JSValue.zero; + if (thisValue == .zero or targetValue == .zero) { + return; + } + + const instance = globalObject.createErrorInstance("Failed to bind query: {s}", .{@errorName(err)}); + + // TODO: error handling + const vm = JSC.VirtualMachine.get(); + const function = vm.rareData().postgresql_context.onQueryRejectFn.get().?; + const event_loop = vm.eventLoop(); + event_loop.runCallback(function, globalObject, thisValue, &.{ + targetValue, + instance, + }); + } + + pub fn onError(this: *@This(), err: protocol.ErrorResponse, globalObject: *JSC.JSGlobalObject) void { + this.status = .fail; + defer this.deref(); + + const thisValue = this.thisValue; + const targetValue = this.target.trySwap() orelse JSValue.zero; + if (thisValue == .zero or targetValue == .zero) { + return; + } + + // TODO: error handling + var vm = JSC.VirtualMachine.get(); + const function = vm.rareData().postgresql_context.onQueryRejectFn.get().?; + globalObject.queueMicrotask(function, &[_]JSValue{ targetValue, err.toJS(globalObject) }); + } + + const CommandTag = union(enum) { + // For an INSERT command, the tag is INSERT oid rows, where rows is the + // number of rows inserted. oid used to be the object ID of the inserted + // row if rows was 1 and the target table had OIDs, but OIDs system + // columns are not supported anymore; therefore oid is always 0. + INSERT: u64, + // For a DELETE command, the tag is DELETE rows where rows is the number + // of rows deleted. + DELETE: u64, + // For an UPDATE command, the tag is UPDATE rows where rows is the + // number of rows updated. + UPDATE: u64, + // For a MERGE command, the tag is MERGE rows where rows is the number + // of rows inserted, updated, or deleted. + MERGE: u64, + // For a SELECT or CREATE TABLE AS command, the tag is SELECT rows where + // rows is the number of rows retrieved. + SELECT: u64, + // For a MOVE command, the tag is MOVE rows where rows is the number of + // rows the cursor's position has been changed by. + MOVE: u64, + // For a FETCH command, the tag is FETCH rows where rows is the number + // of rows that have been retrieved from the cursor. + FETCH: u64, + // For a COPY command, the tag is COPY rows where rows is the number of + // rows copied. (Note: the row count appears only in PostgreSQL 8.2 and + // later.) + COPY: u64, + + other: []const u8, + + pub fn toJSTag(this: CommandTag, globalObject: *JSC.JSGlobalObject) JSValue { + return switch (this) { + .INSERT => JSValue.jsNumber(1), + .DELETE => JSValue.jsNumber(2), + .UPDATE => JSValue.jsNumber(3), + .MERGE => JSValue.jsNumber(4), + .SELECT => JSValue.jsNumber(5), + .MOVE => JSValue.jsNumber(6), + .FETCH => JSValue.jsNumber(7), + .COPY => JSValue.jsNumber(8), + .other => |tag| JSC.ZigString.init(tag).toJS(globalObject), + }; + } + + pub fn toJSNumber(this: CommandTag) JSValue { + return switch (this) { + .other => JSValue.jsNumber(0), + inline else => |val| JSValue.jsNumber(val), + }; + } + + const KnownCommand = enum { + INSERT, + DELETE, + UPDATE, + MERGE, + SELECT, + MOVE, + FETCH, + COPY, + + pub const Map = bun.ComptimeEnumMap(KnownCommand); + }; + + pub fn init(tag: []const u8) CommandTag { + const first_space_index = bun.strings.indexOfChar(tag, ' ') orelse return .{ .other = tag }; + const cmd = KnownCommand.Map.get(tag[0..first_space_index]) orelse return .{ + .other = tag, + }; + + const number = brk: { + switch (cmd) { + .INSERT => { + var remaining = tag[@min(first_space_index + 1, tag.len)..]; + const second_space = bun.strings.indexOfChar(remaining, ' ') orelse return .{ .other = tag }; + remaining = remaining[@min(second_space + 1, remaining.len)..]; + break :brk std.fmt.parseInt(u64, remaining, 0) catch |err| { + debug("CommandTag failed to parse number: {s}", .{@errorName(err)}); + return .{ .other = tag }; + }; + }, + else => { + const after_tag = tag[@min(first_space_index + 1, tag.len)..]; + break :brk std.fmt.parseInt(u64, after_tag, 0) catch |err| { + debug("CommandTag failed to parse number: {s}", .{@errorName(err)}); + return .{ .other = tag }; + }; + }, + } + }; + + switch (cmd) { + inline else => |t| return @unionInit(CommandTag, @tagName(t), number), + } + } + }; + + pub fn onSuccess(this: *@This(), command_tag_str: []const u8, globalObject: *JSC.JSGlobalObject) void { + this.status = .success; + defer this.deref(); + + const thisValue = this.thisValue; + const targetValue = this.target.trySwap() orelse JSValue.zero; + if (thisValue == .zero or targetValue == .zero) { + this.pending_value.deinit(); + return; + } + + const tag = CommandTag.init(command_tag_str); + + const vm = JSC.VirtualMachine.get(); + const function = vm.rareData().postgresql_context.onQueryResolveFn.get().?; + const event_loop = vm.eventLoop(); + + event_loop.runCallback(function, globalObject, thisValue, &.{ + targetValue, + this.pending_value.trySwap() orelse .undefined, + tag.toJSTag(globalObject), + tag.toJSNumber(), + }); + } + + pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) ?*PostgresSQLQuery { + _ = callframe; + globalThis.throw("PostgresSQLQuery cannot be constructed directly", .{}); + return null; + } + + pub fn estimatedSize(this: *PostgresSQLQuery) usize { + _ = this; + return @sizeOf(PostgresSQLQuery); + } + + pub fn call(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + const arguments = callframe.arguments(3).slice(); + const query = arguments[0]; + const values = arguments[1]; + + if (!query.isString()) { + globalThis.throw("query must be a string", .{}); + return .zero; + } + + if (values.jsType() != .Array) { + globalThis.throw("values must be an array", .{}); + return .zero; + } + + const pending_value = arguments[2]; + if (!pending_value.jsType().isArrayLike()) { + globalThis.throwInvalidArgumentType("query", "pendingValue", "Array"); + return .zero; + } + + var ptr = bun.default_allocator.create(PostgresSQLQuery) catch |err| { + globalThis.throwError(err, "failed to allocate query"); + return .zero; + }; + + const this_value = ptr.toJS(globalThis); + this_value.ensureStillAlive(); + + ptr.* = .{ + .query = query.toBunString(globalThis), + .thisValue = this_value, + }; + ptr.query.ref(); + + PostgresSQLQuery.bindingSetCached(this_value, globalThis, values); + PostgresSQLQuery.pendingValueSetCached(this_value, globalThis, pending_value); + ptr.pending_value.set(globalThis, pending_value); + + return this_value; + } + + pub fn push(this: *PostgresSQLQuery, globalThis: *JSC.JSGlobalObject, value: JSValue) void { + var pending_value = this.pending_value.get() orelse return; + pending_value.push(globalThis, value); + } + + pub fn doDone(this: *@This(), globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + _ = globalObject; + this.is_done = true; + return .undefined; + } + + pub fn doRun(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + var arguments_ = callframe.arguments(2); + const arguments = arguments_.slice(); + var connection = arguments[0].as(PostgresSQLConnection) orelse { + globalObject.throw("connection must be a PostgresSQLConnection", .{}); + return .zero; + }; + var query = arguments[1]; + + if (!query.isObject()) { + globalObject.throwInvalidArgumentType("run", "query", "Query"); + return .zero; + } + + this.target.set(globalObject, query); + const binding_value = PostgresSQLQuery.bindingGetCached(callframe.this()) orelse .zero; + var query_str = this.query.toUTF8(bun.default_allocator); + defer query_str.deinit(); + + var signature = Signature.generate(globalObject, query_str.slice(), binding_value) catch |err| { + globalObject.throwError(err, "failed to generate signature"); + return .zero; + }; + + var writer = connection.writer(); + + const entry = connection.statements.getOrPut(bun.default_allocator, bun.hash(signature.name)) catch |err| { + globalObject.throwError(err, "failed to allocate statement"); + signature.deinit(); + return .zero; + }; + + const has_params = signature.fields.len > 0; + var did_write = false; + + enqueue: { + if (entry.found_existing) { + this.statement = entry.value_ptr.*; + this.statement.?.ref(); + signature.deinit(); + + if (has_params and this.statement.?.status == .parsing) { + // if it has params, we need to wait for ParamDescription to be received before we can write the data + } else { + this.binary = this.statement.?.fields.len > 0; + + PostgresRequest.bindAndExecute(globalObject, this.statement.?, binding_value, PostgresSQLConnection.Writer, writer) catch |err| { + globalObject.throwError(err, "failed to bind and execute query"); + + return .zero; + }; + did_write = true; + } + + break :enqueue; + } + + // If it does not have params, we can write and execute immediately in one go + if (!has_params) { + PostgresRequest.prepareAndQueryWithSignature(globalObject, query_str.slice(), binding_value, PostgresSQLConnection.Writer, writer, &signature) catch |err| { + globalObject.throwError(err, "failed to prepare and query"); + signature.deinit(); + return .zero; + }; + did_write = true; + } else { + PostgresRequest.writeQuery(query_str.slice(), signature.name, signature.fields, PostgresSQLConnection.Writer, writer) catch |err| { + globalObject.throwError(err, "failed to write query"); + signature.deinit(); + return .zero; + }; + writer.write(&protocol.Sync) catch |err| { + globalObject.throwError(err, "failed to flush"); + signature.deinit(); + return .zero; + }; + } + + { + const stmt = bun.default_allocator.create(PostgresSQLStatement) catch |err| { + globalObject.throwError(err, "failed to allocate statement"); + return .zero; + }; + + stmt.* = .{ .signature = signature, .ref_count = 2, .status = PostgresSQLStatement.Status.parsing }; + this.statement = stmt; + entry.value_ptr.* = stmt; + } + } + + connection.requests.writeItem(this) catch {}; + this.ref(); + this.status = if (did_write) .binding else .pending; + + if (connection.is_ready_for_query) + connection.flushData(); + + return .undefined; + } + + pub fn doCancel(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + _ = callframe; + _ = globalObject; + _ = this; + + return .undefined; + } + + comptime { + if (!JSC.is_bindgen) { + @export(call, .{ .name = "PostgresSQLQuery__createInstance" }); + } + } +}; + +pub const PostgresRequest = struct { + pub fn writeBind( + name: []const u8, + cursor_name: bun.String, + globalObject: *JSC.JSGlobalObject, + values_array: JSValue, + result_fields: []const protocol.FieldDescription, + comptime Context: type, + writer: protocol.NewWriter(Context), + ) !void { + try writer.write("B"); + const length = try writer.length(); + + try writer.String(cursor_name); + try writer.string(name); + + var iter = JSC.JSArrayIterator.init(values_array, globalObject); + + // The number of parameter format codes that follow (denoted C + // below). This can be zero to indicate that there are no + // parameters or that the parameters all use the default format + // (text); or one, in which case the specified format code is + // applied to all parameters; or it can equal the actual number + // of parameters. + try writer.short(iter.len); + + while (iter.next()) |value| { + const tag = try types.Tag.fromJS(globalObject, value); + + try writer.short( + tag.formatCode(), + ); + } + + // The number of parameter values that follow (possibly zero). This + // must match the number of parameters needed by the query. + try writer.short(iter.len); + + iter = JSC.JSArrayIterator.init(values_array, globalObject); + + debug("Bind: {} ({d} args)", .{ bun.fmt.quote(name), iter.len }); + + while (iter.next()) |value| { + if (value.isUndefinedOrNull()) { + debug(" -> NULL", .{}); + // As a special case, -1 indicates a + // NULL parameter value. No value bytes follow in the NULL case. + try writer.int4(@bitCast(@as(i32, -1))); + continue; + } + + const tag = try types.Tag.fromJS(globalObject, value); + + debug(" -> {s}", .{@tagName(tag)}); + switch (tag) { + .json => { + var str = bun.String.empty; + defer str.deref(); + value.jsonStringify(globalObject, 0, &str); + const slice = str.toUTF8WithoutRef(bun.default_allocator); + defer slice.deinit(); + const l = try writer.length(); + try writer.write(slice.slice()); + try l.writeExcludingSelf(); + }, + .bool => { + const l = try writer.length(); + try writer.bool(value.toBoolean()); + try l.writeExcludingSelf(); + }, + .time, .timestamp, .timestamptz => { + var buf = std.mem.zeroes([28]u8); + const str = value.toISOString(globalObject, &buf); + const l = try writer.length(); + try writer.write(str); + try l.writeExcludingSelf(); + }, + .bytea => { + var bytes: []const u8 = ""; + if (value.asArrayBuffer(globalObject)) |buf| { + bytes = buf.byteSlice(); + } + const l = try writer.length(); + debug(" {d} bytes", .{bytes.len}); + + try writer.write(bytes); + try l.writeExcludingSelf(); + }, + .int4 => { + const l = try writer.length(); + try writer.int4(@bitCast(value.coerceToInt32(globalObject))); + try l.writeExcludingSelf(); + }, + .int4_array => { + const l = try writer.length(); + try writer.int4(@bitCast(value.coerceToInt32(globalObject))); + try l.writeExcludingSelf(); + }, + .float8 => { + const l = try writer.length(); + try writer.f64(@bitCast(value.coerceToDouble(globalObject))); + try l.writeExcludingSelf(); + }, + else => { + const str = String.fromJSRef(value, globalObject); + defer str.deref(); + const slice = str.toUTF8WithoutRef(bun.default_allocator); + defer slice.deinit(); + const l = try writer.length(); + try writer.write(slice.slice()); + try l.writeExcludingSelf(); + }, + } + } + + var any_non_text_fields: bool = false; + for (result_fields) |field| { + if (field.typeTag().isBinaryFormatSupported()) { + any_non_text_fields = true; + break; + } + } + + if (any_non_text_fields) { + try writer.short(result_fields.len); + for (result_fields) |field| { + try writer.short( + field.typeTag().formatCode(), + ); + } + } else { + try writer.short(0); + } + + try length.write(); + } + + pub fn writeQuery( + query: []const u8, + name: []const u8, + params: []const int4, + comptime Context: type, + writer: protocol.NewWriter(Context), + ) !void { + { + var q = protocol.Parse{ + .name = name, + .params = params, + .query = query, + }; + try q.writeInternal(Context, writer); + debug("Parse: {}", .{bun.fmt.quote(query)}); + } + + { + var d = protocol.Describe{ + .p = .{ + .prepared_statement = name, + }, + }; + try d.writeInternal(Context, writer); + debug("Describe: {}", .{bun.fmt.quote(name)}); + } + } + + pub fn prepareAndQueryWithSignature( + globalObject: *JSC.JSGlobalObject, + query: []const u8, + array_value: JSValue, + comptime Context: type, + writer: protocol.NewWriter(Context), + signature: *Signature, + ) !void { + try writeQuery(query, signature.name, signature.fields, Context, writer); + try writeBind(signature.name, bun.String.empty, globalObject, array_value, &.{}, Context, writer); + var exec = protocol.Execute{ + .p = .{ + .prepared_statement = signature.name, + }, + }; + try exec.writeInternal(Context, writer); + + try writer.write(&protocol.Flush); + try writer.write(&protocol.Sync); + } + + pub fn prepareAndQuery( + globalObject: *JSC.JSGlobalObject, + query: bun.String, + array_value: JSValue, + comptime Context: type, + writer: protocol.NewWriter(Context), + ) !Signature { + var query_ = query.toUTF8(bun.default_allocator); + defer query_.deinit(); + var signature = try Signature.generate(globalObject, query_.slice(), array_value); + errdefer { + signature.deinit(); + } + + try prepareAndQueryWithSignature(globalObject, query_.slice(), array_value, Context, writer, &signature); + + return signature; + } + + pub fn bindAndExecute( + globalObject: *JSC.JSGlobalObject, + statement: *PostgresSQLStatement, + array_value: JSValue, + comptime Context: type, + writer: protocol.NewWriter(Context), + ) !void { + try writeBind(statement.signature.name, bun.String.empty, globalObject, array_value, statement.fields, Context, writer); + var exec = protocol.Execute{ + .p = .{ + .prepared_statement = statement.signature.name, + }, + }; + try exec.writeInternal(Context, writer); + + try writer.write(&protocol.Flush); + try writer.write(&protocol.Sync); + } + + pub fn onData( + connection: *PostgresSQLConnection, + comptime Context: type, + reader: protocol.NewReader(Context), + ) !void { + while (true) { + reader.markMessageStart(); + + switch (try reader.int(u8)) { + 'D' => try connection.on(.DataRow, Context, reader), + 'd' => try connection.on(.CopyData, Context, reader), + 'S' => try connection.on(.ParameterStatus, Context, reader), + 'Z' => try connection.on(.ReadyForQuery, Context, reader), + 'C' => try connection.on(.CommandComplete, Context, reader), + '2' => try connection.on(.BindComplete, Context, reader), + '1' => try connection.on(.ParseComplete, Context, reader), + 't' => try connection.on(.ParameterDescription, Context, reader), + 'T' => try connection.on(.RowDescription, Context, reader), + 'R' => try connection.on(.Authentication, Context, reader), + 'n' => try connection.on(.NoData, Context, reader), + 'K' => try connection.on(.BackendKeyData, Context, reader), + 'E' => try connection.on(.ErrorResponse, Context, reader), + 's' => try connection.on(.PortalSuspended, Context, reader), + '3' => try connection.on(.CloseComplete, Context, reader), + 'G' => try connection.on(.CopyInResponse, Context, reader), + 'N' => try connection.on(.NoticeResponse, Context, reader), + 'I' => try connection.on(.EmptyQueryResponse, Context, reader), + 'H' => try connection.on(.CopyOutResponse, Context, reader), + 'c' => try connection.on(.CopyDone, Context, reader), + 'W' => try connection.on(.CopyBothResponse, Context, reader), + + else => |c| { + debug("Unknown message: {d}", .{c}); + const to_skip = try reader.length() -| 1; + try reader.skip(@intCast(@max(to_skip, 0))); + }, + } + } + } + + pub const Queue = std.fifo.LinearFifo(*PostgresSQLQuery, .Dynamic); +}; + +pub const PostgresSQLConnection = struct { + socket: Socket, + status: Status = Status.connecting, + ref_count: u32 = 1, + + write_buffer: bun.OffsetByteList = .{}, + read_buffer: bun.OffsetByteList = .{}, + last_message_start: u32 = 0, + requests: PostgresRequest.Queue, + + poll_ref: bun.Async.KeepAlive = .{}, + globalObject: *JSC.JSGlobalObject, + + statements: PreparedStatementsMap, + pending_activity_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + js_value: JSValue = JSValue.undefined, + + is_ready_for_query: bool = false, + + backend_parameters: bun.StringMap = bun.StringMap.init(bun.default_allocator, true), + backend_key_data: protocol.BackendKeyData = .{}, + + pending_disconnect: bool = false, + + on_connect: JSC.Strong = .{}, + on_close: JSC.Strong = .{}, + + database: []const u8 = "", + user: []const u8 = "", + password: []const u8 = "", + options: []const u8 = "", + options_buf: []const u8 = "", + + authentication_state: AuthenticationState = .{ .pending = {} }, + + pub const AuthenticationState = union(enum) { + pending: void, + SASL: SASL, + ok: void, + + pub fn zero(this: *AuthenticationState) void { + const bytes = std.mem.asBytes(this); + @memset(bytes, 0); + } + + pub const SASL = struct { + const nonce_byte_len = 18; + const nonce_base64_len = bun.base64.encodeLenFromSize(nonce_byte_len); + + const server_signature_byte_len = 32; + const server_signature_base64_len = bun.base64.encodeLenFromSize(server_signature_byte_len); + + const salted_password_byte_len = 32; + + nonce_base64_bytes: [nonce_base64_len]u8 = .{0} ** nonce_base64_len, + nonce_len: u8 = 0, + + server_signature_base64_bytes: [server_signature_base64_len]u8 = .{0} ** server_signature_base64_len, + server_signature_len: u8 = 0, + + salted_password_bytes: [salted_password_byte_len]u8 = .{0} ** salted_password_byte_len, + salted_password_created: bool = false, + + status: SASLStatus = .init, + + pub const SASLStatus = enum { + init, + @"continue", + }; + + fn hmac(password: []const u8, data: []const u8) ?[32]u8 { + var buf = std.mem.zeroes([bun.BoringSSL.EVP_MAX_MD_SIZE]u8); + + // TODO: I don't think this is failable. + const result = bun.hmac.generate(password, data, .sha256, &buf) orelse return null; + + assert(result.len == 32); + return buf[0..32].*; + } + + pub fn computeSaltedPassword(this: *SASL, salt_bytes: []const u8, iteration_count: u32, connection: *PostgresSQLConnection) !void { + this.salted_password_created = true; + if (Crypto.EVP.pbkdf2(&this.salted_password_bytes, connection.password, salt_bytes, iteration_count, .sha256) == null) { + return error.PBKDF2Failed; + } + } + + pub fn saltedPassword(this: *const SASL) []const u8 { + assert(this.salted_password_created); + return this.salted_password_bytes[0..salted_password_byte_len]; + } + + pub fn serverSignature(this: *const SASL) []const u8 { + assert(this.server_signature_len > 0); + return this.server_signature_base64_bytes[0..this.server_signature_len]; + } + + pub fn computeServerSignature(this: *SASL, auth_string: []const u8) !void { + assert(this.server_signature_len == 0); + + const server_key = hmac(this.saltedPassword(), "Server Key") orelse return error.InvalidServerKey; + const server_signature_bytes = hmac(&server_key, auth_string) orelse return error.InvalidServerSignature; + this.server_signature_len = @intCast(bun.base64.encode(&this.server_signature_base64_bytes, &server_signature_bytes)); + } + + pub fn clientKey(this: *const SASL) [32]u8 { + return hmac(this.saltedPassword(), "Client Key").?; + } + + pub fn clientKeySignature(_: *const SASL, client_key: []const u8, auth_string: []const u8) [32]u8 { + var sha_digest = std.mem.zeroes(bun.sha.SHA256.Digest); + bun.sha.SHA256.hash(client_key, &sha_digest, JSC.VirtualMachine.get().rareData().boringEngine()); + return hmac(&sha_digest, auth_string).?; + } + + pub fn nonce(this: *SASL) []const u8 { + if (this.nonce_len == 0) { + var bytes: [nonce_byte_len]u8 = .{0} ** nonce_byte_len; + bun.rand(&bytes); + this.nonce_len = @intCast(bun.base64.encode(&this.nonce_base64_bytes, &bytes)); + } + return this.nonce_base64_bytes[0..this.nonce_len]; + } + + pub fn deinit(this: *SASL) void { + this.nonce_len = 0; + this.salted_password_created = false; + this.server_signature_len = 0; + this.status = .init; + } + }; + }; + + pub const Status = enum { + disconnected, + connecting, + connected, + failed, + }; + + pub usingnamespace JSC.Codegen.JSPostgresSQLConnection; + + pub fn hasPendingActivity(this: *PostgresSQLConnection) bool { + @fence(.acquire); + return this.pending_activity_count.load(.acquire) > 0; + } + + fn updateHasPendingActivity(this: *PostgresSQLConnection) void { + @fence(.release); + const a: u32 = if (this.requests.readableLength() > 0) 1 else 0; + const b: u32 = if (this.status != .disconnected) 1 else 0; + this.pending_activity_count.store(a + b, .release); + } + + pub fn setStatus(this: *PostgresSQLConnection, status: Status) void { + defer this.updateHasPendingActivity(); + + if (this.status == status) return; + + this.status = status; + switch (status) { + .connected => { + const on_connect = this.on_connect.swap(); + if (on_connect == .zero) return; + const js_value = this.js_value; + js_value.ensureStillAlive(); + this.globalObject.queueMicrotask(on_connect, &[_]JSValue{ JSValue.jsNull(), js_value }); + this.poll_ref.unref(this.globalObject.bunVM()); + this.updateHasPendingActivity(); + }, + else => {}, + } + } + + pub fn finalize(this: *PostgresSQLConnection) void { + debug("PostgresSQLConnection finalize", .{}); + this.js_value = .zero; + this.deref(); + } + + pub fn flushData(this: *PostgresSQLConnection) void { + const chunk = this.write_buffer.remaining(); + if (chunk.len == 0) return; + const wrote = this.socket.write(chunk, false); + if (wrote > 0) { + this.write_buffer.consume(@intCast(wrote)); + } + } + + pub fn fail(this: *PostgresSQLConnection, message: []const u8, err: anyerror) void { + defer this.updateHasPendingActivity(); + if (this.status == .failed) return; + debug("failed: {s}: {s}", .{ message, @errorName(err) }); + + this.status = .failed; + if (!this.socket.isClosed()) this.socket.close(); + const on_close = this.on_close.swap(); + if (on_close == .zero) return; + const instance = this.globalObject.createErrorInstance("{s}", .{message}); + instance.put(this.globalObject, JSC.ZigString.static("code"), String.init(@errorName(err)).toJS(this.globalObject)); + _ = on_close.call( + this.globalObject, + this.js_value, + &[_]JSValue{ + instance, + }, + ) catch |e| this.globalObject.reportActiveExceptionAsUnhandled(e); + } + + pub fn onClose(this: *PostgresSQLConnection) void { + var vm = this.globalObject.bunVM(); + defer vm.drainMicrotasks(); + this.fail("Connection closed", error.ConnectionClosed); + } + + pub fn onOpen(this: *PostgresSQLConnection, socket: uws.AnySocket) void { + this.socket = socket; + + this.poll_ref.ref(this.globalObject.bunVM()); + this.updateHasPendingActivity(); + + var msg = protocol.StartupMessage{ .user = Data{ .temporary = this.user }, .database = Data{ .temporary = this.database }, .options = Data{ .temporary = this.options } }; + msg.writeInternal(Writer, this.writer()) catch |err| { + socket.close(); + this.fail("Failed to write startup message", err); + }; + + this.flushData(); + } + + pub fn onTimeout(this: *PostgresSQLConnection) void { + var vm = this.globalObject.bunVM(); + defer vm.drainMicrotasks(); + debug("onTimeout", .{}); + } + + pub fn onDrain(this: *PostgresSQLConnection) void { + var vm = this.globalObject.bunVM(); + defer vm.drainMicrotasks(); + this.flushData(); + } + + pub fn onData(this: *PostgresSQLConnection, data: []const u8) void { + var vm = this.globalObject.bunVM(); + defer vm.drainMicrotasks(); + if (this.read_buffer.remaining().len == 0) { + var consumed: usize = 0; + var offset: usize = 0; + const reader = protocol.StackReader.init(data, &consumed, &offset); + PostgresRequest.onData(this, protocol.StackReader, reader) catch |err| { + if (err == error.ShortRead) { + if (comptime bun.Environment.allow_assert) { + // if (@errorReturnTrace()) |trace| { + // debug("Received short read: last_message_start: {d}, head: {d}, len: {d}\n{}", .{ + // offset, + // consumed, + // data.len, + // trace, + // }); + // } else { + debug("Received short read: last_message_start: {d}, head: {d}, len: {d}", .{ + offset, + consumed, + data.len, + }); + // } + } + + this.read_buffer.head = 0; + this.last_message_start = 0; + this.read_buffer.byte_list.len = 0; + this.read_buffer.write(bun.default_allocator, data[offset..]) catch @panic("failed to write to read buffer"); + } else { + if (comptime bun.Environment.allow_assert) { + if (@errorReturnTrace()) |trace| { + debug("Error: {s}\n{}", .{ @errorName(err), trace }); + } + } + this.fail("Failed to read data", err); + } + }; + return; + } + + { + this.read_buffer.head = this.last_message_start; + this.read_buffer.write(bun.default_allocator, data) catch @panic("failed to write to read buffer"); + PostgresRequest.onData(this, Reader, this.bufferedReader()) catch |err| { + if (err != error.ShortRead) { + if (comptime bun.Environment.allow_assert) { + if (@errorReturnTrace()) |trace| { + debug("Error: {s}\n{}", .{ @errorName(err), trace }); + } + } + this.fail("Failed to read data", err); + return; + } + + if (comptime bun.Environment.allow_assert) { + // if (@errorReturnTrace()) |trace| { + // debug("Received short read: last_message_start: {d}, head: {d}, len: {d}\n{}", .{ + // this.last_message_start, + // this.read_buffer.head, + // this.read_buffer.byte_list.len, + // trace, + // }); + // } else { + debug("Received short read: last_message_start: {d}, head: {d}, len: {d}", .{ + this.last_message_start, + this.read_buffer.head, + this.read_buffer.byte_list.len, + }); + // } + } + + return; + }; + + this.last_message_start = 0; + this.read_buffer.head = 0; + } + } + + pub fn constructor(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) ?*PostgresSQLConnection { + _ = callframe; + globalObject.throw("PostgresSQLConnection cannot be constructed directly", .{}); + return null; + } + + comptime { + if (!JSC.is_bindgen) { + @export(call, .{ .name = "PostgresSQLConnection__createInstance" }); + } + } + + pub fn call(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + var vm = globalObject.bunVM(); + const arguments = callframe.arguments(9).slice(); + const hostname_str = arguments[0].toBunString(globalObject); + defer hostname_str.deref(); + const port = arguments[1].coerce(i32, globalObject); + + const username_str = arguments[2].toBunString(globalObject); + defer username_str.deref(); + const password_str = arguments[3].toBunString(globalObject); + defer password_str.deref(); + const database_str = arguments[4].toBunString(globalObject); + defer database_str.deref(); + const tls_object = arguments[5]; + var username: []const u8 = ""; + var password: []const u8 = ""; + var database: []const u8 = ""; + var options: []const u8 = ""; + + const options_str = arguments[6].toBunString(globalObject); + defer options_str.deref(); + + const options_buf: []u8 = brk: { + var b = bun.StringBuilder{}; + b.cap += username_str.utf8ByteLength() + 1 + password_str.utf8ByteLength() + 1 + database_str.utf8ByteLength() + 1 + options_str.utf8ByteLength() + 1; + + b.allocate(bun.default_allocator) catch {}; + var u = username_str.toUTF8WithoutRef(bun.default_allocator); + defer u.deinit(); + username = b.append(u.slice()); + + var p = password_str.toUTF8WithoutRef(bun.default_allocator); + defer p.deinit(); + password = b.append(p.slice()); + + var d = database_str.toUTF8WithoutRef(bun.default_allocator); + defer d.deinit(); + database = b.append(d.slice()); + + var o = options_str.toUTF8WithoutRef(bun.default_allocator); + defer o.deinit(); + options = b.append(o.slice()); + + break :brk b.allocatedSlice(); + }; + + const on_connect = arguments[7]; + const on_close = arguments[8]; + var ptr = bun.default_allocator.create(PostgresSQLConnection) catch |err| { + globalObject.throwError(err, "failed to allocate connection"); + return .zero; + }; + + ptr.* = PostgresSQLConnection{ + .globalObject = globalObject, + .on_connect = JSC.Strong.create(on_connect, globalObject), + .on_close = JSC.Strong.create(on_close, globalObject), + .database = database, + .user = username, + .password = password, + .options = options, + .options_buf = options_buf, + .socket = undefined, + .requests = PostgresRequest.Queue.init(bun.default_allocator), + .statements = PreparedStatementsMap{}, + }; + + ptr.updateHasPendingActivity(); + ptr.poll_ref.ref(vm); + const js_value = ptr.toJS(globalObject); + js_value.ensureStillAlive(); + ptr.js_value = js_value; + + { + const hostname = hostname_str.toUTF8(bun.default_allocator); + defer hostname.deinit(); + if (tls_object.isEmptyOrUndefinedOrNull()) { + const ctx = vm.rareData().postgresql_context.tcp orelse brk: { + const ctx_ = uws.us_create_bun_socket_context(0, vm.uwsLoop(), @sizeOf(*PostgresSQLConnection), uws.us_bun_socket_context_options_t{}).?; + uws.NewSocketHandler(false).configure(ctx_, true, *PostgresSQLConnection, SocketHandler(false)); + vm.rareData().postgresql_context.tcp = ctx_; + break :brk ctx_; + }; + ptr.socket = .{ + .SocketTCP = uws.SocketTCP.connectAnon(hostname.slice(), port, ctx, ptr) catch |err| { + globalObject.throwError(err, "failed to connect to postgresql"); + ptr.deinit(); + return .zero; + }, + }; + } else { + // TODO: + globalObject.throwTODO("TLS is not supported yet"); + ptr.deinit(); + return .zero; + } + } + + return js_value; + } + + fn SocketHandler(comptime ssl: bool) type { + return struct { + const SocketType = uws.NewSocketHandler(ssl); + fn _socket(s: SocketType) Socket { + if (comptime ssl) { + return Socket{ .SocketTLS = s }; + } + + return Socket{ .SocketTCP = s }; + } + pub fn onOpen(this: *PostgresSQLConnection, socket: SocketType) void { + this.onOpen(_socket(socket)); + } + + pub fn onClose(this: *PostgresSQLConnection, socket: SocketType, _: i32, _: ?*anyopaque) void { + _ = socket; + this.onClose(); + } + + pub fn onEnd(this: *PostgresSQLConnection, socket: SocketType) void { + _ = socket; + this.onClose(); + } + + pub fn onConnectError(this: *PostgresSQLConnection, socket: SocketType, _: i32) void { + _ = socket; + this.onClose(); + } + + pub fn onTimeout(this: *PostgresSQLConnection, socket: SocketType) void { + _ = socket; + this.onTimeout(); + } + + pub fn onData(this: *PostgresSQLConnection, socket: SocketType, data: []const u8) void { + _ = socket; + this.onData(data); + } + + pub fn onWritable(this: *PostgresSQLConnection, socket: SocketType) void { + _ = socket; + this.onDrain(); + } + }; + } + + pub fn ref(this: *@This()) void { + bun.assert(this.ref_count > 0); + this.ref_count += 1; + } + + pub fn doRef(this: *@This(), _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + this.poll_ref.ref(this.globalObject.bunVM()); + this.updateHasPendingActivity(); + return .undefined; + } + + pub fn doUnref(this: *@This(), _: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + this.poll_ref.unref(this.globalObject.bunVM()); + this.updateHasPendingActivity(); + return .undefined; + } + + pub fn deref(this: *@This()) void { + const ref_count = this.ref_count; + this.ref_count -= 1; + + if (ref_count == 1) { + this.disconnect(); + this.deinit(); + } + } + + pub fn doClose(this: *@This(), globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSValue { + _ = globalObject; + this.disconnect(); + this.write_buffer.deinit(bun.default_allocator); + + return .undefined; + } + + pub fn deinit(this: *@This()) void { + var iter = this.statements.valueIterator(); + while (iter.next()) |stmt_ptr| { + var stmt = stmt_ptr.*; + stmt.deref(); + } + this.statements.deinit(bun.default_allocator); + this.write_buffer.deinit(bun.default_allocator); + this.read_buffer.deinit(bun.default_allocator); + this.on_close.deinit(); + this.on_connect.deinit(); + this.backend_parameters.deinit(); + bun.default_allocator.free(this.options_buf); + bun.default_allocator.destroy(this); + } + + pub fn disconnect(this: *@This()) void { + if (this.status == .connected) { + this.status = .disconnected; + this.poll_ref.disable(); + this.socket.close(); + } + } + + fn current(this: *PostgresSQLConnection) ?*PostgresSQLQuery { + if (this.requests.readableLength() == 0) { + return null; + } + + return this.requests.peekItem(0); + } + + pub const Writer = struct { + connection: *PostgresSQLConnection, + + pub fn write(this: Writer, data: []const u8) anyerror!void { + var buffer = &this.connection.write_buffer; + try buffer.write(bun.default_allocator, data); + } + + pub fn pwrite(this: Writer, data: []const u8, index: usize) anyerror!void { + @memcpy(this.connection.write_buffer.byte_list.slice()[index..][0..data.len], data); + } + + pub fn offset(this: Writer) usize { + return this.connection.write_buffer.len(); + } + }; + + pub fn writer(this: *PostgresSQLConnection) protocol.NewWriter(Writer) { + return .{ + .wrapped = .{ + .connection = this, + }, + }; + } + + pub const Reader = struct { + connection: *PostgresSQLConnection, + + pub fn markMessageStart(this: Reader) void { + this.connection.last_message_start = this.connection.read_buffer.head; + } + + pub const ensureLength = ensureCapacity; + + pub fn peek(this: Reader) []const u8 { + return this.connection.read_buffer.remaining(); + } + pub fn skip(this: Reader, count: usize) void { + this.connection.read_buffer.head = @min(this.connection.read_buffer.head + @as(u32, @truncate(count)), this.connection.read_buffer.byte_list.len); + } + pub fn ensureCapacity(this: Reader, count: usize) bool { + return @as(usize, this.connection.read_buffer.head) + count <= @as(usize, this.connection.read_buffer.byte_list.len); + } + pub fn read(this: Reader, count: usize) anyerror!Data { + var remaining = this.connection.read_buffer.remaining(); + if (@as(usize, remaining.len) < count) { + return error.ShortRead; + } + + this.skip(count); + return Data{ + .temporary = remaining[0..count], + }; + } + pub fn readZ(this: Reader) anyerror!Data { + const remain = this.connection.read_buffer.remaining(); + + if (bun.strings.indexOfChar(remain, 0)) |zero| { + this.skip(zero + 1); + return Data{ + .temporary = remain[0..zero], + }; + } + + return error.ShortRead; + } + }; + + pub fn bufferedReader(this: *PostgresSQLConnection) protocol.NewReader(Reader) { + return .{ + .wrapped = .{ .connection = this }, + }; + } + + pub const DataCell = extern struct { + tag: Tag, + + value: Value, + free_value: u8 = 0, + + pub const Tag = enum(u8) { + null = 0, + string = 1, + float8 = 2, + int4 = 3, + int8 = 4, + bool = 5, + date = 6, + bytea = 7, + json = 8, + array = 9, + typed_array = 10, + }; + + pub const Value = extern union { + null: u8, + string: bun.WTF.StringImpl, + float8: f64, + int4: i32, + int8: i64, + bool: u8, + date: f64, + bytea: [2]usize, + json: bun.WTF.StringImpl, + array: Array, + typed_array: TypedArray, + }; + + pub const Array = extern struct { + ptr: ?[*]DataCell = null, + len: u32, + + pub fn slice(this: *Array) []DataCell { + const ptr = this.ptr orelse return &.{}; + return ptr[0..this.len]; + } + }; + pub const TypedArray = extern struct { + head_ptr: ?[*]u8 = null, + ptr: ?[*]u8 = null, + len: u32, + byte_len: u32, + type: JSValue.JSType, + + pub fn slice(this: *TypedArray) []u8 { + const ptr = this.ptr orelse return &.{}; + return ptr[0..this.len]; + } + + pub fn byteSlice(this: *TypedArray) []u8 { + const ptr = this.head_ptr orelse return &.{}; + return ptr[0..this.len]; + } + }; + + pub fn deinit(this: *DataCell) void { + if (this.free_value == 0) return; + + switch (this.tag) { + .string => { + this.value.string.deref(); + }, + .json => { + this.value.json.deref(); + }, + .bytea => { + if (this.value.bytea[1] == 0) return; + const slice = @as([*]u8, @ptrFromInt(this.value.bytea[0]))[0..this.value.bytea[1]]; + bun.default_allocator.free(slice); + }, + .array => { + for (this.value.array.slice()) |*cell| { + cell.deinit(); + } + bun.default_allocator.free(this.value.array.slice()); + }, + .typed_array => { + bun.default_allocator.free(this.value.typed_array.byteSlice()); + }, + + else => {}, + } + } + + pub fn fromBytes(binary: bool, oid: int4, bytes: []const u8, globalObject: *JSC.JSGlobalObject) anyerror!DataCell { + switch (@as(types.Tag, @enumFromInt(@as(short, @intCast(oid))))) { + // TODO: .int2_array, .float8_array + inline .int4_array, .float4_array => |tag| { + if (binary) { + if (bytes.len < 16) { + return error.InvalidBinaryData; + } + // https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/arrayfuncs.c#L1549-L1645 + const dimensions_raw: int4 = @bitCast(bytes[0..4].*); + const contains_nulls: int4 = @bitCast(bytes[4..8].*); + + const dimensions = @byteSwap(dimensions_raw); + if (dimensions > 1) { + return error.MultidimensionalArrayNotSupportedYet; + } + + if (contains_nulls != 0) { + return error.NullsInArrayNotSupportedYet; + } + + if (dimensions == 0) { + return DataCell{ + .tag = .typed_array, + .value = .{ + .typed_array = .{ + .ptr = null, + .len = 0, + .byte_len = 0, + .type = tag.toJSTypedArrayType(), + }, + }, + }; + } + + const elements = tag.pgArrayType().init(bytes).slice(); + + return DataCell{ + .tag = .typed_array, + .value = .{ + .typed_array = .{ + .head_ptr = if (bytes.len > 0) @constCast(bytes.ptr) else null, + .ptr = if (elements.len > 0) @ptrCast(elements.ptr) else null, + .len = @truncate(elements.len), + .byte_len = @truncate(bytes.len), + .type = tag.toJSTypedArrayType(), + }, + }, + }; + } else { + // TODO: + return fromBytes(false, @intFromEnum(types.Tag.bytea), bytes, globalObject); + } + }, + .int4 => { + if (binary) { + return DataCell{ .tag = .int4, .value = .{ .int4 = try parseBinary(.int4, i32, bytes) } }; + } else { + return DataCell{ .tag = .int4, .value = .{ .int4 = bun.fmt.parseInt(i32, bytes, 0) catch 0 } }; + } + }, + .float8 => { + if (binary and bytes.len == 8) { + return DataCell{ .tag = .float8, .value = .{ .float8 = try parseBinary(.float8, f64, bytes) } }; + } else { + const float8: f64 = bun.parseDouble(bytes) catch std.math.nan(f64); + return DataCell{ .tag = .float8, .value = .{ .float8 = float8 } }; + } + }, + .float4 => { + if (binary and bytes.len == 4) { + return DataCell{ .tag = .float8, .value = .{ .float8 = try parseBinary(.float4, f32, bytes) } }; + } else { + const float4: f64 = bun.parseDouble(bytes) catch std.math.nan(f64); + return DataCell{ .tag = .float8, .value = .{ .float8 = float4 } }; + } + }, + .json => { + return DataCell{ .tag = .json, .value = .{ .json = String.createUTF8(bytes).value.WTFStringImpl }, .free_value = 1 }; + }, + .bool => { + return DataCell{ .tag = .bool, .value = .{ .bool = @intFromBool(bytes.len > 0 and bytes[0] == 't') } }; + }, + .time, .timestamp, .timestamptz => { + var str = bun.String.init(bytes); + defer str.deref(); + return DataCell{ .tag = .date, .value = .{ .date = str.parseDate(globalObject) } }; + }, + .bytea => { + if (binary) { + return DataCell{ .tag = .bytea, .value = .{ .bytea = .{ @intFromPtr(bytes.ptr), bytes.len } } }; + } else { + if (bun.strings.hasPrefixComptime(bytes, "\\x")) { + const hex = bytes[2..]; + const len = hex.len / 2; + const buf = try bun.default_allocator.alloc(u8, len); + errdefer bun.default_allocator.free(buf); + + return DataCell{ + .tag = .bytea, + .value = .{ + .bytea = .{ + @intFromPtr(buf.ptr), + try bun.strings.decodeHexToBytes(buf, u8, hex), + }, + }, + .free_value = 1, + }; + } else { + return error.UnsupportedByteaFormat; + } + } + }, + else => { + return DataCell{ .tag = .string, .value = .{ .string = bun.String.createUTF8(bytes).value.WTFStringImpl }, .free_value = 1 }; + }, + } + } + + // #define pg_hton16(x) (x) + // #define pg_hton32(x) (x) + // #define pg_hton64(x) (x) + + // #define pg_ntoh16(x) (x) + // #define pg_ntoh32(x) (x) + // #define pg_ntoh64(x) (x) + + fn pg_ntoT(comptime IntSize: usize, i: anytype) std.meta.Int(.unsigned, IntSize) { + @setRuntimeSafety(false); + const T = @TypeOf(i); + if (@typeInfo(T) == .Array) { + return pg_ntoT(IntSize, @as(std.meta.Int(.unsigned, IntSize), @bitCast(i))); + } + + const casted: std.meta.Int(.unsigned, IntSize) = @intCast(i); + return @byteSwap(casted); + } + fn pg_ntoh16(x: anytype) u16 { + return pg_ntoT(16, x); + } + + fn pg_ntoh32(x: anytype) u32 { + return pg_ntoT(32, x); + } + + pub fn parseBinary(comptime tag: types.Tag, comptime ReturnType: type, bytes: []const u8) anyerror!ReturnType { + switch (comptime tag) { + .float8 => { + return @as(f64, @bitCast(try parseBinary(.int8, i64, bytes))); + }, + .int8 => { + // pq_getmsgfloat8 + if (bytes.len != 8) return error.InvalidBinaryData; + return @byteSwap(@as(i64, @bitCast(bytes[0..8].*))); + }, + .int4 => { + // pq_getmsgint + switch (bytes.len) { + 1 => { + return bytes[0]; + }, + 2 => { + return pg_ntoh16(@as(u16, @bitCast(bytes[0..2].*))); + }, + 4 => { + return @bitCast(pg_ntoh32(@as(u32, @bitCast(bytes[0..4].*)))); + }, + else => { + return error.UnsupportedIntegerSize; + }, + } + }, + .int2 => { + // pq_getmsgint + switch (bytes.len) { + 1 => { + return bytes[0]; + }, + 2 => { + return pg_ntoh16(@as(u16, @bitCast(bytes[0..2].*))); + }, + else => { + return error.UnsupportedIntegerSize; + }, + } + }, + .float4 => { + // pq_getmsgfloat4 + return @as(f32, @bitCast(try parseBinary(.int4, i32, bytes))); + }, + else => @compileError("TODO"), + } + } + + pub const Putter = struct { + list: []DataCell, + fields: []const protocol.FieldDescription, + binary: bool = false, + count: usize = 0, + globalObject: *JSC.JSGlobalObject, + + extern fn JSC__constructObjectFromDataCell(*JSC.JSGlobalObject, JSValue, JSValue, [*]DataCell, u32) JSValue; + pub fn toJS(this: *Putter, globalObject: *JSC.JSGlobalObject, array: JSValue, structure: JSValue) JSValue { + return JSC__constructObjectFromDataCell(globalObject, array, structure, this.list.ptr, @truncate(this.fields.len)); + } + + pub fn put(this: *Putter, index: u32, optional_bytes: ?*Data) anyerror!bool { + const oid = this.fields[index].type_oid; + debug("index: {d}, oid: {d}", .{ index, oid }); + + this.list[index] = if (optional_bytes) |data| + try DataCell.fromBytes(this.binary, oid, data.slice(), this.globalObject) + else + DataCell{ + .tag = .null, + .value = .{ + .null = 0, + }, + }; + this.count += 1; + return true; + } + }; + }; + + fn advance(this: *PostgresSQLConnection) !bool { + defer this.updateRef(); + var any = false; + + while (this.requests.readableLength() > 0) { + var req: *PostgresSQLQuery = this.requests.peekItem(0); + switch (req.status) { + .pending => { + const stmt = req.statement orelse return error.ExpectedStatement; + if (stmt.status == .failed) { + req.onError(stmt.error_response, this.globalObject); + this.requests.discard(1); + any = true; + } else { + break; + } + }, + .success, .fail => { + this.requests.discard(1); + req.deref(); + any = true; + }, + else => break, + } + } + + while (this.requests.readableLength() > 0) { + var req: *PostgresSQLQuery = this.requests.peekItem(0); + const stmt = req.statement orelse return error.ExpectedStatement; + + switch (stmt.status) { + .prepared => { + if (req.status == .pending and stmt.status == .prepared) { + const binding_value = PostgresSQLQuery.bindingGetCached(req.thisValue) orelse .zero; + PostgresRequest.bindAndExecute(this.globalObject, stmt, binding_value, PostgresSQLConnection.Writer, this.writer()) catch |err| { + req.onWriteFail(err, this.globalObject); + req.deref(); + this.requests.discard(1); + continue; + }; + req.status = .binding; + req.binary = stmt.fields.len > 0; + any = true; + } else { + break; + } + }, + else => break, + } + } + + return any; + } + + pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.EnumLiteral), comptime Context: type, reader: protocol.NewReader(Context)) !void { + debug("on({s})", .{@tagName(MessageType)}); + if (comptime MessageType != .ReadyForQuery) { + this.is_ready_for_query = false; + } + + switch (comptime MessageType) { + .DataRow => { + const request = this.current() orelse return error.ExpectedRequest; + var statement = request.statement orelse return error.ExpectedStatement; + + var putter = DataCell.Putter{ + .list = &.{}, + .fields = statement.fields, + .binary = request.binary, + .globalObject = this.globalObject, + }; + + var stack_buf: [64]DataCell = undefined; + var cells: []DataCell = stack_buf[0..@min(statement.fields.len, stack_buf.len)]; + defer { + for (cells[0..putter.count]) |*cell| { + cell.deinit(); + } + } + + var free_cells = false; + defer if (free_cells) bun.default_allocator.free(cells); + if (statement.fields.len >= 64) { + cells = try bun.default_allocator.alloc(DataCell, statement.fields.len); + free_cells = true; + } + putter.list = cells; + + try protocol.DataRow.decode( + &putter, + Context, + reader, + DataCell.Putter.put, + ); + + const pending_value = PostgresSQLQuery.pendingValueGetCached(request.thisValue) orelse .zero; + pending_value.ensureStillAlive(); + const result = putter.toJS(this.globalObject, pending_value, statement.structure(this.js_value, this.globalObject)); + + if (pending_value == .zero) { + PostgresSQLQuery.pendingValueSetCached(request.thisValue, this.globalObject, result); + } + }, + .CopyData => { + var copy_data: protocol.CopyData = undefined; + try copy_data.decodeInternal(Context, reader); + copy_data.data.deinit(); + }, + .ParameterStatus => { + var parameter_status: protocol.ParameterStatus = undefined; + try parameter_status.decodeInternal(Context, reader); + defer { + parameter_status.deinit(); + } + try this.backend_parameters.insert(parameter_status.name.slice(), parameter_status.value.slice()); + }, + .ReadyForQuery => { + var ready_for_query: protocol.ReadyForQuery = undefined; + try ready_for_query.decodeInternal(Context, reader); + + if (this.pending_disconnect) { + this.disconnect(); + return; + } + + this.setStatus(.connected); + this.is_ready_for_query = true; + this.socket.setTimeout(300); + + if (try this.advance() or this.is_ready_for_query) { + this.flushData(); + } + }, + .CommandComplete => { + var request = this.current() orelse return error.ExpectedRequest; + + var cmd: protocol.CommandComplete = undefined; + try cmd.decodeInternal(Context, reader); + defer { + cmd.deinit(); + } + debug("-> {s}", .{cmd.command_tag.slice()}); + _ = this.requests.discard(1); + defer this.updateRef(); + request.onSuccess(cmd.command_tag.slice(), this.globalObject); + }, + .BindComplete => { + try reader.eatMessage(protocol.BindComplete); + var request = this.current() orelse return error.ExpectedRequest; + if (request.status == .binding) { + request.status = .running; + } + }, + .ParseComplete => { + try reader.eatMessage(protocol.ParseComplete); + const request = this.current() orelse return error.ExpectedRequest; + if (request.statement) |statement| { + if (statement.status == .parsing) { + statement.status = .prepared; + } + } + }, + .ParameterDescription => { + var description: protocol.ParameterDescription = undefined; + try description.decodeInternal(Context, reader); + const request = this.current() orelse return error.ExpectedRequest; + var statement = request.statement orelse return error.ExpectedStatement; + statement.parameters = description.parameters; + }, + .RowDescription => { + var description: protocol.RowDescription = undefined; + try description.decodeInternal(Context, reader); + errdefer description.deinit(); + const request = this.current() orelse return error.ExpectedRequest; + var statement = request.statement orelse return error.ExpectedStatement; + statement.fields = description.fields; + }, + .Authentication => { + var auth: protocol.Authentication = undefined; + try auth.decodeInternal(Context, reader); + defer auth.deinit(); + + switch (auth) { + .SASL => { + if (this.authentication_state != .SASL) { + this.authentication_state = .{ .SASL = .{} }; + } + + var mechanism_buf: [128]u8 = undefined; + const mechanism = std.fmt.bufPrintZ(&mechanism_buf, "n,,n=*,r={s}", .{this.authentication_state.SASL.nonce()}) catch unreachable; + var response = protocol.SASLInitialResponse{ + .mechanism = .{ + .temporary = "SCRAM-SHA-256", + }, + .data = .{ + .temporary = mechanism, + }, + }; + + try response.writeInternal(PostgresSQLConnection.Writer, this.writer()); + debug("SASL", .{}); + this.flushData(); + }, + .SASLContinue => |*cont| { + if (this.authentication_state != .SASL) { + debug("Unexpected SASLContinue for authentiation state: {s}", .{@tagName(std.meta.activeTag(this.authentication_state))}); + return error.UnexpectedMessage; + } + var sasl = &this.authentication_state.SASL; + + if (sasl.status != .init) { + debug("Unexpected SASLContinue for SASL state: {s}", .{@tagName(sasl.status)}); + return error.UnexpectedMessage; + } + debug("SASLContinue", .{}); + + const iteration_count = try cont.iterationCount(); + + const server_salt_decoded_base64 = try bun.base64.decodeAlloc(bun.z_allocator, cont.s); + defer bun.z_allocator.free(server_salt_decoded_base64); + try sasl.computeSaltedPassword(server_salt_decoded_base64, iteration_count, this); + + const auth_string = try std.fmt.allocPrint( + bun.z_allocator, + "n=*,r={s},r={s},s={s},i={s},c=biws,r={s}", + .{ + sasl.nonce(), + cont.r, + cont.s, + cont.i, + cont.r, + }, + ); + defer bun.z_allocator.free(auth_string); + try sasl.computeServerSignature(auth_string); + + const client_key = sasl.clientKey(); + const client_key_signature = sasl.clientKeySignature(&client_key, auth_string); + var client_key_xor_buffer: [32]u8 = undefined; + for (&client_key_xor_buffer, client_key, client_key_signature) |*out, a, b| { + out.* = a ^ b; + } + + var client_key_xor_base64_buf = std.mem.zeroes([bun.base64.encodeLenFromSize(32)]u8); + const xor_base64_len = bun.base64.encode(&client_key_xor_base64_buf, &client_key_xor_buffer); + + const payload = try std.fmt.allocPrint( + bun.z_allocator, + "c=biws,r={s},p={s}", + .{ cont.r, client_key_xor_base64_buf[0..xor_base64_len] }, + ); + defer bun.z_allocator.free(payload); + + var response = protocol.SASLResponse{ + .data = .{ + .temporary = payload, + }, + }; + + try response.writeInternal(PostgresSQLConnection.Writer, this.writer()); + sasl.status = .@"continue"; + this.flushData(); + }, + .SASLFinal => |final| { + if (this.authentication_state != .SASL) { + debug("SASLFinal - Unexpected SASLContinue for authentiation state: {s}", .{@tagName(std.meta.activeTag(this.authentication_state))}); + return error.UnexpectedMessage; + } + var sasl = &this.authentication_state.SASL; + + if (sasl.status != .@"continue") { + debug("SASLFinal - Unexpected SASLContinue for SASL state: {s}", .{@tagName(sasl.status)}); + return error.UnexpectedMessage; + } + + if (sasl.server_signature_len == 0) { + debug("SASLFinal - Server signature is empty", .{}); + return error.UnexpectedMessage; + } + + const server_signature = sasl.serverSignature(); + + // This will usually start with "v=" + const comparison_signature = final.data.slice(); + + if (comparison_signature.len < 2 or !bun.strings.eqlLong(server_signature, comparison_signature[2..], true)) { + debug("SASLFinal - SASL Server signature mismatch\nExpected: {s}\nActual: {s}", .{ server_signature, comparison_signature[2..] }); + this.fail("The server did not return the correct signature", error.SASL_SIGNATURE_MISMATCH); + } else { + debug("SASLFinal - SASL Server signature match", .{}); + this.authentication_state.zero(); + } + }, + .Ok => { + debug("Authentication OK", .{}); + this.authentication_state.zero(); + this.authentication_state = .{ .ok = {} }; + }, + + .Unknown => { + this.fail("Unknown authentication method", error.UNKNOWN_AUTHENTICATION_METHOD); + }, + + else => { + debug("TODO auth: {s}", .{@tagName(std.meta.activeTag(auth))}); + }, + } + }, + .NoData => { + try reader.eatMessage(protocol.NoData); + var request = this.current() orelse return error.ExpectedRequest; + if (request.status == .binding) { + request.status = .running; + } + }, + .BackendKeyData => { + try this.backend_key_data.decodeInternal(Context, reader); + }, + .ErrorResponse => { + var err: protocol.ErrorResponse = undefined; + try err.decodeInternal(Context, reader); + + if (this.status == .connecting) { + this.status = .failed; + defer { + err.deinit(); + this.poll_ref.unref(this.globalObject.bunVM()); + this.updateHasPendingActivity(); + } + + const on_connect = this.on_connect.swap(); + if (on_connect == .zero) return; + const js_value = this.js_value; + js_value.ensureStillAlive(); + this.globalObject.queueMicrotask(on_connect, &[_]JSValue{ err.toJS(this.globalObject), js_value }); + + // it shouldn't enqueue any requests while connecting + bun.assert(this.requests.count == 0); + return; + } + + var request = this.current() orelse { + debug("ErrorResponse: {}", .{err}); + return error.ExpectedRequest; + }; + var is_error_owned = true; + defer { + if (is_error_owned) { + err.deinit(); + } + } + if (request.statement) |stmt| { + if (stmt.status == PostgresSQLStatement.Status.parsing) { + stmt.status = PostgresSQLStatement.Status.failed; + stmt.error_response = err; + is_error_owned = false; + if (this.statements.remove(bun.hash(stmt.signature.name))) { + stmt.deref(); + } + } + } + _ = this.requests.discard(1); + this.updateRef(); + + request.onError(err, this.globalObject); + }, + .PortalSuspended => { + // try reader.eatMessage(&protocol.PortalSuspended); + // var request = this.current() orelse return error.ExpectedRequest; + // _ = request; + // _ = this.requests.discard(1); + debug("TODO PortalSuspended", .{}); + }, + .CloseComplete => { + try reader.eatMessage(protocol.CloseComplete); + var request = this.current() orelse return error.ExpectedRequest; + _ = this.requests.discard(1); + request.onSuccess("CLOSECOMPLETE", this.globalObject); + }, + .CopyInResponse => { + debug("TODO CopyInResponse", .{}); + }, + .NoticeResponse => { + debug("UNSUPPORTED NoticeResponse", .{}); + var resp: protocol.NoticeResponse = undefined; + + try resp.decodeInternal(Context, reader); + resp.deinit(); + }, + .EmptyQueryResponse => { + try reader.eatMessage(protocol.EmptyQueryResponse); + var request = this.current() orelse return error.ExpectedRequest; + _ = this.requests.discard(1); + this.updateRef(); + request.onSuccess("", this.globalObject); + }, + .CopyOutResponse => { + debug("TODO CopyOutResponse", .{}); + }, + .CopyDone => { + debug("TODO CopyDone", .{}); + }, + .CopyBothResponse => { + debug("TODO CopyBothResponse", .{}); + }, + else => @compileError("Unknown message type: " ++ @tagName(MessageType)), + } + } + + pub fn updateRef(this: *PostgresSQLConnection) void { + this.updateHasPendingActivity(); + if (this.pending_activity_count.raw > 0) { + this.poll_ref.ref(this.globalObject.bunVM()); + } else { + this.poll_ref.unref(this.globalObject.bunVM()); + } + } + + pub fn doFlush(this: *PostgresSQLConnection, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + _ = callframe; + _ = globalObject; + _ = this; + + return .undefined; + } + + pub fn createQuery(this: *PostgresSQLConnection, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { + _ = callframe; + _ = globalObject; + _ = this; + + return .undefined; + } + + pub fn getConnected(this: *PostgresSQLConnection, _: *JSC.JSGlobalObject) JSValue { + return JSValue.jsBoolean(this.status == Status.connected); + } +}; + +pub const PostgresSQLStatement = struct { + cached_structure: JSC.Strong = .{}, + ref_count: u32 = 1, + fields: []const protocol.FieldDescription = &[_]protocol.FieldDescription{}, + parameters: []const int4 = &[_]int4{}, + signature: Signature, + status: Status = Status.parsing, + error_response: protocol.ErrorResponse = .{}, + + pub const Status = enum { + parsing, + prepared, + failed, + }; + pub fn ref(this: *@This()) void { + bun.assert(this.ref_count > 0); + this.ref_count += 1; + } + + pub fn deref(this: *@This()) void { + const ref_count = this.ref_count; + this.ref_count -= 1; + + if (ref_count == 1) { + this.deinit(); + } + } + + pub fn deinit(this: *PostgresSQLStatement) void { + debug("PostgresSQLStatement deinit", .{}); + + bun.assert(this.ref_count == 0); + + for (this.fields) |*field| { + @constCast(field).deinit(); + } + bun.default_allocator.free(this.fields); + bun.default_allocator.free(this.parameters); + this.cached_structure.deinit(); + this.error_response.deinit(); + this.signature.deinit(); + bun.default_allocator.destroy(this); + } + + pub fn structure(this: *PostgresSQLStatement, owner: JSValue, globalObject: *JSC.JSGlobalObject) JSValue { + return this.cached_structure.get() orelse { + const names = bun.default_allocator.alloc(bun.String, this.fields.len) catch return .undefined; + defer { + for (names) |*name| { + name.deref(); + } + bun.default_allocator.free(names); + } + for (this.fields, names) |*field, *name| { + name.* = String.fromUTF8(field.name.slice()); + } + const structure_ = JSC.JSObject.createStructure( + globalObject, + owner, + @truncate(this.fields.len), + names.ptr, + ); + this.cached_structure.set(globalObject, structure_); + return structure_; + }; + } +}; + +const Signature = struct { + fields: []const int4, + name: []const u8, + query: []const u8, + + pub fn deinit(this: *Signature) void { + bun.default_allocator.free(this.fields); + bun.default_allocator.free(this.name); + bun.default_allocator.free(this.query); + } + + pub fn hash(this: *const Signature) u64 { + var hasher = std.hash.Wyhash.init(0); + hasher.update(this.name); + hasher.update(std.mem.sliceAsBytes(this.fields)); + return hasher.final(); + } + + pub fn generate(globalObject: *JSC.JSGlobalObject, query: []const u8, array_value: JSValue) !Signature { + var fields = std.ArrayList(int4).init(bun.default_allocator); + var name = try std.ArrayList(u8).initCapacity(bun.default_allocator, query.len); + + name.appendSliceAssumeCapacity(query); + + errdefer { + fields.deinit(); + name.deinit(); + } + + var iter = JSC.JSArrayIterator.init(array_value, globalObject); + + while (iter.next()) |value| { + if (value.isUndefinedOrNull()) { + try fields.append(0); + try name.appendSlice(".null"); + continue; + } + + const tag = try types.Tag.fromJS(globalObject, value); + try fields.append(@intFromEnum(tag)); + + switch (tag) { + .int8 => try name.appendSlice(".int8"), + .int4 => try name.appendSlice(".int4"), + // .int4_array => try name.appendSlice(".int4_array"), + .int2 => try name.appendSlice(".int2"), + .float8 => try name.appendSlice(".float8"), + .float4 => try name.appendSlice(".float4"), + .numeric => try name.appendSlice(".numeric"), + .json => try name.appendSlice(".json"), + .bool => try name.appendSlice(".bool"), + .timestamp => try name.appendSlice(".timestamp"), + .timestamptz => try name.appendSlice(".timestamptz"), + .time => try name.appendSlice(".time"), + .bytea => try name.appendSlice(".bytea"), + else => try name.appendSlice(".string"), + } + } + + return Signature{ + .name = name.items, + .fields = fields.items, + .query = try bun.default_allocator.dupe(u8, query), + }; + } +}; + +pub fn createBinding(globalObject: *JSC.JSGlobalObject) JSValue { + const binding = JSValue.createEmptyObjectWithNullPrototype(globalObject); + binding.put(globalObject, ZigString.static("PostgresSQLConnection"), PostgresSQLConnection.getConstructor(globalObject)); + binding.put(globalObject, ZigString.static("init"), JSC.JSFunction.create(globalObject, "init", PostgresSQLContext.init, 0, .{})); + binding.put( + globalObject, + ZigString.static("createQuery"), + JSC.JSFunction.create(globalObject, "createQuery", PostgresSQLQuery.call, 2, .{}), + ); + + binding.put( + globalObject, + ZigString.static("createConnection"), + JSC.JSFunction.create(globalObject, "createQuery", PostgresSQLConnection.call, 2, .{}), + ); + + return binding; +} + +const ZigString = JSC.ZigString; + +const assert = bun.assert; diff --git a/src/string.zig b/src/string.zig index d6814ca0f5219..63db7a55ec9e4 100644 --- a/src/string.zig +++ b/src/string.zig @@ -3,6 +3,7 @@ const bun = @import("root").bun; const JSC = bun.JSC; const JSValue = bun.JSC.JSValue; const Parent = @This(); +const OOM = bun.OOM; pub const BufferOwnership = enum(u32) { BufferInternal, @@ -92,7 +93,7 @@ pub const WTFStringImplStruct = extern struct { if (this.is8Bit()) { return ZigString.init(this.latin1Slice()); } else { - return ZigString.init16(this.utf16Slice()); + return ZigString.initUTF16(this.utf16Slice()); } } @@ -143,6 +144,8 @@ pub const WTFStringImplStruct = extern struct { ); } + pub const max = std.math.maxInt(u32); + pub fn toUTF8WithoutRef(this: WTFStringImpl, allocator: std.mem.Allocator) ZigString.Slice { if (this.is8Bit()) { if (bun.strings.toUTF8FromLatin1(allocator, this.latin1Slice()) catch bun.outOfMemory()) |utf8| { @@ -277,7 +280,7 @@ pub const Tag = enum(u8) { /// into a WTF::String. /// Can be in either `utf8` or `utf16le` encodings. ZigString = 2, - /// Static memory that is guarenteed to never be freed. When converted to WTF::String, + /// Static memory that is guaranteed to never be freed. When converted to WTF::String, /// the memory is not cloned, but instead referenced with WTF::ExternalStringImpl. /// Can be in either `utf8` or `utf16le` encodings. StaticZigString = 3, @@ -314,6 +317,10 @@ pub const String = extern struct { extern fn BunString__fromLatin1Unitialized(len: usize) String; extern fn BunString__fromUTF16Unitialized(len: usize) String; + pub fn ascii(bytes: []const u8) String { + return String{ .tag = .ZigString, .value = .{ .ZigString = ZigString.init(bytes) } }; + } + pub fn isGlobal(this: String) bool { return this.tag == Tag.ZigString and this.value.ZigString.isGloballyAllocated(); } @@ -399,6 +406,8 @@ pub const String = extern struct { /// /// This is not allowed on zero-length strings, in this case you should /// check earlier and use String.empty in that case. + /// + /// If the length is too large, this will return a dead string. pub fn createUninitialized( comptime kind: WTFStringEncoding, len: usize, @@ -430,7 +439,11 @@ pub const String = extern struct { return BunString__fromUTF16(bytes.ptr, bytes.len); } - pub fn createFormat(comptime fmt: []const u8, args: anytype) !String { + pub fn createFormat(comptime fmt: [:0]const u8, args: anytype) OOM!String { + if (comptime std.meta.fieldNames(@TypeOf(args)).len == 0) { + return String.static(fmt); + } + var sba = std.heap.stackFallback(16384, bun.default_allocator); const alloc = sba.get(); const buf = try std.fmt.allocPrint(alloc, fmt, args); @@ -466,7 +479,9 @@ pub const String = extern struct { if (this.isUTF16()) { const new, const bytes = createUninitialized(.utf16, this.length()); - @memcpy(bytes, this.value.ZigString.utf16Slice()); + if (new.tag != .Dead) { + @memcpy(bytes, this.value.ZigString.utf16Slice()); + } return new; } @@ -566,7 +581,7 @@ pub const String = extern struct { }; } - pub fn static(input: []const u8) String { + pub fn static(input: [:0]const u8) String { return .{ .tag = .StaticZigString, .value = .{ .StaticZigString = ZigString.init(input) }, @@ -578,6 +593,16 @@ pub const String = extern struct { return JSC__createError(globalObject, this); } + pub fn toTypeErrorInstance(this: *const String, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + defer this.deref(); + return JSC__createTypeError(globalObject, this); + } + + pub fn toRangeErrorInstance(this: *const String, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + defer this.deref(); + return JSC__createRangeError(globalObject, this); + } + extern fn BunString__createExternal( bytes: [*]const u8, len: usize, @@ -585,18 +610,39 @@ pub const String = extern struct { ptr: ?*anyopaque, callback: ?*const fn (*anyopaque, *anyopaque, u32) callconv(.C) void, ) String; + extern fn BunString__createStaticExternal( + bytes: [*]const u8, + len: usize, + isLatin1: bool, + ) String; /// ctx is the pointer passed into `createExternal` /// buffer is the pointer to the buffer, either [*]u8 or [*]u16 /// len is the number of characters in that buffer. pub const ExternalStringImplFreeFunction = fn (ctx: *anyopaque, buffer: *anyopaque, len: u32) callconv(.C) void; - pub fn createExternal(bytes: []const u8, isLatin1: bool, ctx: ?*anyopaque, callback: ?*const ExternalStringImplFreeFunction) String { + pub fn createExternal(bytes: []const u8, isLatin1: bool, ctx: *anyopaque, callback: ?*const ExternalStringImplFreeFunction) String { JSC.markBinding(@src()); bun.assert(bytes.len > 0); + if (bytes.len > max_length()) { + if (callback) |cb| { + cb(ctx, @ptrCast(@constCast(bytes.ptr)), @truncate(bytes.len)); + } + return dead; + } return BunString__createExternal(bytes.ptr, bytes.len, isLatin1, ctx, callback); } + /// This should rarely be used. The WTF::StringImpl* will never be freed. + /// + /// So this really only makes sense when you need to dynamically allocate a + /// string that will never be freed. + pub fn createStaticExternal(bytes: []const u8, isLatin1: bool) String { + JSC.markBinding(@src()); + bun.assert(bytes.len > 0); + return BunString__createStaticExternal(bytes.ptr, bytes.len, isLatin1); + } + extern fn BunString__createExternalGloballyAllocatedLatin1( bytes: [*]u8, len: usize, @@ -607,10 +653,22 @@ pub const String = extern struct { len: usize, ) String; + /// Max WTFStringImpl length. + /// **Not** in bytes. In characters. + pub inline fn max_length() usize { + return JSC.string_allocation_limit; + } + + /// If the allocation fails, this will free the bytes and return a dead string. pub fn createExternalGloballyAllocated(comptime kind: WTFStringEncoding, bytes: []kind.Byte()) String { JSC.markBinding(@src()); bun.assert(bytes.len > 0); + if (bytes.len > max_length()) { + bun.default_allocator.free(bytes); + return dead; + } + return switch (comptime kind) { .latin1 => BunString__createExternalGloballyAllocatedLatin1(bytes.ptr, bytes.len), .utf16 => BunString__createExternalGloballyAllocatedUTF16(bytes.ptr, bytes.len), @@ -621,6 +679,10 @@ pub const String = extern struct { return String.init(ZigString.initUTF8(value)); } + pub fn fromUTF16(value: []const u16) String { + return String.init(ZigString.initUTF16(value)); + } + pub fn fromBytes(value: []const u8) String { return String.init(ZigString.fromBytes(value)); } @@ -640,6 +702,17 @@ pub const String = extern struct { } } + pub fn fromJSRef(value: bun.JSC.JSValue, globalObject: *JSC.JSGlobalObject) String { + JSC.markBinding(@src()); + + var out: String = String.dead; + if (BunString__fromJSRef(globalObject, value, &out)) { + return out; + } else { + return String.dead; + } + } + pub fn tryFromJS(value: bun.JSC.JSValue, globalObject: *JSC.JSGlobalObject) ?String { JSC.markBinding(@src()); @@ -848,7 +921,7 @@ pub const String = extern struct { if (this.value.WTFStringImpl.is8Bit()) { return String.init(ZigString.init(this.value.WTFStringImpl.latin1Slice()[start_index..end_index])); } else { - return String.init(ZigString.init16(this.value.WTFStringImpl.utf16Slice()[start_index..end_index])); + return String.init(ZigString.initUTF16(this.value.WTFStringImpl.utf16Slice()[start_index..end_index])); } }, else => return this, @@ -959,8 +1032,15 @@ pub const String = extern struct { extern fn BunString__toJS(globalObject: *JSC.JSGlobalObject, in: *const String) JSC.JSValue; extern fn BunString__toJSWithLength(globalObject: *JSC.JSGlobalObject, in: *const String, usize) JSC.JSValue; extern fn BunString__toJSDOMURL(globalObject: *JSC.JSGlobalObject, in: *String) JSC.JSValue; + extern fn Bun__parseDate(*JSC.JSGlobalObject, *String) f64; + extern fn BunString__fromJSRef(globalObject: *JSC.JSGlobalObject, value: bun.JSC.JSValue, out: *String) bool; extern fn BunString__toWTFString(this: *String) void; + pub fn parseDate(this: *String, globalObject: *JSC.JSGlobalObject) f64 { + JSC.markBinding(@src()); + return Bun__parseDate(globalObject, this); + } + pub fn ref(this: String) void { switch (this.tag) { .WTFStringImpl => this.value.WTFStringImpl.ref(), @@ -1189,6 +1269,8 @@ pub const String = extern struct { } extern fn JSC__createError(*JSC.JSGlobalObject, str: *const String) JSC.JSValue; + extern fn JSC__createTypeError(*JSC.JSGlobalObject, str: *const String) JSC.JSValue; + extern fn JSC__createRangeError(*JSC.JSGlobalObject, str: *const String) JSC.JSValue; fn concat(comptime n: usize, allocator: std.mem.Allocator, strings: *const [n]String) !String { var num_16bit: usize = 0; @@ -1231,7 +1313,7 @@ pub const String = extern struct { return try concat(strings.len, allocator, strings); } - pub export fn jsGetStringWidth(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub export fn jsGetStringWidth(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or !args.ptr[0].isString()) { diff --git a/src/string_builder.zig b/src/string_builder.zig index 60b46dbd320c8..e5e3d0bd478a9 100644 --- a/src/string_builder.zig +++ b/src/string_builder.zig @@ -67,7 +67,7 @@ pub fn append16(this: *StringBuilder, slice: []const u16, fallback_allocator: st return buf[0..result.count :0]; } else { var list = std.ArrayList(u8).init(fallback_allocator); - var out = bun.strings.toUTF8ListWithTypeBun(&list, []const u16, slice) catch return null; + var out = bun.strings.toUTF8ListWithTypeBun(&list, []const u16, slice, false) catch return null; out.append(0) catch return null; return list.items[0 .. list.items.len - 1 :0]; } @@ -104,6 +104,17 @@ pub fn append(this: *StringBuilder, slice: string) string { return result; } +pub fn addConcat(this: *StringBuilder, slices: []const string) bun.StringPointer { + var remain = this.allocatedSlice()[this.len..]; + var len: usize = 0; + for (slices) |slice| { + @memcpy(remain[0..slice.len], slice); + remain = remain[slice.len..]; + len += slice.len; + } + return this.add(len); +} + pub fn add(this: *StringBuilder, len: usize) bun.StringPointer { if (comptime Environment.allow_assert) { assert(this.len <= this.cap); // didn't count everything @@ -134,6 +145,25 @@ pub fn appendCount(this: *StringBuilder, slice: string) bun.StringPointer { return bun.StringPointer{ .offset = @as(u32, @truncate(start)), .length = @as(u32, @truncate(slice.len)) }; } +pub fn appendCountZ(this: *StringBuilder, slice: string) bun.StringPointer { + if (comptime Environment.allow_assert) { + assert(this.len <= this.cap); // didn't count everything + assert(this.ptr != null); // must call allocate first + } + + const start = this.len; + bun.copy(u8, this.ptr.?[this.len..this.cap], slice); + this.ptr.?[this.len + slice.len] = 0; + const result = this.ptr.?[this.len..this.cap][0..slice.len]; + _ = result; + this.len += slice.len; + this.len += 1; + + if (comptime Environment.allow_assert) assert(this.len <= this.cap); + + return bun.StringPointer{ .offset = @as(u32, @truncate(start)), .length = @as(u32, @truncate(slice.len)) }; +} + pub fn fmt(this: *StringBuilder, comptime str: string, args: anytype) string { if (comptime Environment.allow_assert) { assert(this.len <= this.cap); // didn't count everything @@ -168,6 +198,26 @@ pub fn fmtAppendCount(this: *StringBuilder, comptime str: string, args: anytype) }; } +pub fn fmtAppendCountZ(this: *StringBuilder, comptime str: string, args: anytype) bun.StringPointer { + if (comptime Environment.allow_assert) { + assert(this.len <= this.cap); // didn't count everything + assert(this.ptr != null); // must call allocate first + } + + const buf = this.ptr.?[this.len..this.cap]; + const out = std.fmt.bufPrintZ(buf, str, args) catch unreachable; + const off = this.len; + this.len += out.len; + this.len += 1; + + if (comptime Environment.allow_assert) assert(this.len <= this.cap); + + return bun.StringPointer{ + .offset = @as(u32, @truncate(off)), + .length = @as(u32, @truncate(out.len)), + }; +} + pub fn fmtCount(this: *StringBuilder, comptime str: string, args: anytype) void { this.cap += std.fmt.count(str, args); } diff --git a/src/string_immutable.zig b/src/string_immutable.zig index ec37a6934ebbf..3fd2aa4728aa1 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -36,6 +36,17 @@ pub inline fn containsT(comptime T: type, self: []const T, str: []const T) bool return indexOfT(T, self, str) != null; } +pub inline fn containsCaseInsensitiveASCII(self: string, str: string) bool { + var start: usize = 0; + while (start + str.len <= self.len) { + if (eqlCaseInsensitiveASCIIIgnoreLength(self[start..][0..str.len], str)) { + return true; + } + start += 1; + } + return false; +} + pub inline fn removeLeadingDotSlash(slice: []const u8) []const u8 { if (slice.len >= 2) { if ((@as(u16, @bitCast(slice[0..2].*)) == comptime std.mem.readInt(u16, "./", .little)) or @@ -51,21 +62,19 @@ pub inline fn removeLeadingDotSlash(slice: []const u8) []const u8 { pub const w = toUTF16Literal; pub fn toUTF16Literal(comptime str: []const u8) [:0]const u16 { - return comptime literal(u16, str); + return literal(u16, str); } pub fn literal(comptime T: type, comptime str: []const u8) *const [literalLength(T, str):0]T { - if (!@inComptime()) @compileError("strings.literal() must be called in a comptime context"); - return comptime switch (T) { - u8 => brk: { - var data: [str.len:0]u8 = undefined; - @memcpy(&data, str); - const final = data[0..].*; - break :brk &final; - }, - u16 => return std.unicode.utf8ToUtf16LeStringLiteral(str), - else => @compileError("unsupported type " ++ @typeName(T) ++ " in strings.literal() call."), + const Holder = struct { + pub const value = switch (T) { + u8 => (str[0..str.len].* ++ .{0})[0..str.len :0], + u16 => std.unicode.utf8ToUtf16LeStringLiteral(str), + else => @compileError("unsupported type " ++ @typeName(T) ++ " in strings.literal() call."), + }; }; + + return Holder.value; } fn literalLength(comptime T: type, comptime str: string) usize { @@ -80,7 +89,7 @@ fn literalLength(comptime T: type, comptime str: string) usize { pub const toUTF16LiteralZ = toUTF16Literal; pub const OptionalUsize = std.meta.Int(.unsigned, @bitSizeOf(usize) - 1); -pub fn indexOfAny(slice: string, comptime str: anytype) ?OptionalUsize { +pub fn indexOfAny(slice: string, comptime str: []const u8) ?OptionalUsize { switch (comptime str.len) { 0 => @compileError("str cannot be empty"), 1 => return indexOfChar(slice, str[0]), @@ -119,9 +128,15 @@ pub fn indexOfAny(slice: string, comptime str: anytype) ?OptionalUsize { return null; } + pub fn indexOfAny16(self: []const u16, comptime str: anytype) ?OptionalUsize { - for (self, 0..) |c, i| { - inline for (str) |a| { + return indexOfAnyT(u16, self, str); +} + +pub fn indexOfAnyT(comptime T: type, str: []const T, comptime chars: anytype) ?OptionalUsize { + if (T == u8) return indexOfAny(str, chars); + for (str, 0..) |c, i| { + inline for (chars) |a| { if (c == a) { return @as(OptionalUsize, @intCast(i)); } @@ -130,23 +145,29 @@ pub fn indexOfAny16(self: []const u16, comptime str: anytype) ?OptionalUsize { return null; } + pub inline fn containsComptime(self: string, comptime str: string) bool { - var remain = self; + if (comptime str.len == 0) @compileError("Don't call this with an empty string plz."); + + const start = std.mem.indexOfScalar(u8, self, str[0]) orelse return false; + var remain = self[start..]; const Int = std.meta.Int(.unsigned, str.len * 8); while (remain.len >= comptime str.len) { if (@as(Int, @bitCast(remain.ptr[0..str.len].*)) == @as(Int, @bitCast(str.ptr[0..str.len].*))) { return true; } - remain = remain[str.len..]; + + const next_start = std.mem.indexOfScalar(u8, remain[1..], str[0]) orelse return false; + remain = remain[1 + next_start ..]; } return false; } pub const includes = contains; -pub fn inMapCaseInsensitive(self: string, comptime ComptimeStringMap: anytype) ?ComptimeStringMap.Value { - return bun.String.static(self).inMapCaseInsensitive(ComptimeStringMap); +pub fn inMapCaseInsensitive(self: []const u8, comptime ComptimeStringMap: anytype) ?ComptimeStringMap.Value { + return bun.String.ascii(self).inMapCaseInsensitive(ComptimeStringMap); } pub inline fn containsAny(in: anytype, target: string) bool { @@ -161,7 +182,7 @@ pub inline fn containsAny(in: anytype, target: string) bool { /// - The name ends up being part of a URL, an argument on the command line, and /// a folder name. Therefore, the name can't contain any non-URL-safe /// characters. -pub inline fn isNPMPackageName(target: string) bool { +pub fn isNPMPackageName(target: string) bool { if (target.len == 0) return false; if (target.len > 214) return false; @@ -195,12 +216,7 @@ pub inline fn isNPMPackageName(target: string) bool { return !scoped or slash_index > 0 and slash_index + 1 < target.len; } -pub inline fn indexAny(in: anytype, target: string) ?usize { - for (in, 0..) |str, i| if (indexOf(str, target) != null) return i; - return null; -} - -pub inline fn indexAnyComptime(target: string, comptime chars: string) ?usize { +pub fn indexAnyComptime(target: string, comptime chars: string) ?usize { for (target, 0..) |parent, i| { inline for (chars) |char| { if (char == parent) return i; @@ -209,7 +225,7 @@ pub inline fn indexAnyComptime(target: string, comptime chars: string) ?usize { return null; } -pub inline fn indexAnyComptimeT(comptime T: type, target: []const T, comptime chars: []const T) ?usize { +pub fn indexAnyComptimeT(comptime T: type, target: []const T, comptime chars: []const T) ?usize { for (target, 0..) |parent, i| { inline for (chars) |char| { if (char == parent) return i; @@ -218,7 +234,7 @@ pub inline fn indexAnyComptimeT(comptime T: type, target: []const T, comptime ch return null; } -pub inline fn indexEqualAny(in: anytype, target: string) ?usize { +pub fn indexEqualAny(in: anytype, target: string) ?usize { for (in, 0..) |str, i| if (eqlLong(str, target, true)) return i; return null; } @@ -744,8 +760,12 @@ pub fn eql(self: string, other: anytype) bool { return true; } -pub inline fn eqlInsensitive(self: string, other: anytype) bool { - return std.ascii.eqlIgnoreCase(self, other); +pub fn eqlComptimeT(comptime T: type, self: []const T, comptime alt: anytype) bool { + if (T == u16) { + return eqlComptimeUTF16(self, alt); + } + + return eqlComptime(self, alt); } pub fn eqlComptime(self: string, comptime alt: anytype) bool { @@ -784,8 +804,9 @@ pub fn hasSuffixComptime(self: string, comptime alt: anytype) bool { return self.len >= alt.len and eqlComptimeCheckLenWithType(u8, self[self.len - alt.len ..], alt, false); } -inline fn eqlComptimeCheckLenU8(a: []const u8, comptime b: []const u8, comptime check_len: bool) bool { +fn eqlComptimeCheckLenU8(a: []const u8, comptime b: []const u8, comptime check_len: bool) bool { @setEvalBranchQuota(9999); + if (comptime check_len) { if (a.len != b.len) return false; } @@ -822,7 +843,7 @@ inline fn eqlComptimeCheckLenU8(a: []const u8, comptime b: []const u8, comptime return true; } -inline fn eqlComptimeCheckLenWithKnownType(comptime Type: type, a: []const Type, comptime b: []const Type, comptime check_len: bool) bool { +fn eqlComptimeCheckLenWithKnownType(comptime Type: type, a: []const Type, comptime b: []const Type, comptime check_len: bool) bool { if (comptime Type != u8) { return eqlComptimeCheckLenU8(std.mem.sliceAsBytes(a), comptime std.mem.sliceAsBytes(b), comptime check_len); } @@ -833,18 +854,18 @@ inline fn eqlComptimeCheckLenWithKnownType(comptime Type: type, a: []const Type, /// /// strings.eqlComptime(input, "hello world"); /// strings.eqlComptime(input, "hai"); -pub inline fn eqlComptimeCheckLenWithType(comptime Type: type, a: []const Type, comptime b: anytype, comptime check_len: bool) bool { +pub fn eqlComptimeCheckLenWithType(comptime Type: type, a: []const Type, comptime b: anytype, comptime check_len: bool) bool { return eqlComptimeCheckLenWithKnownType(comptime Type, a, if (@typeInfo(@TypeOf(b)) != .Pointer) &b else b, comptime check_len); } -pub inline fn eqlCaseInsensitiveASCIIIgnoreLength( +pub fn eqlCaseInsensitiveASCIIIgnoreLength( a: string, b: string, ) bool { return eqlCaseInsensitiveASCII(a, b, false); } -pub inline fn eqlCaseInsensitiveASCIIICheckLength( +pub fn eqlCaseInsensitiveASCIIICheckLength( a: string, b: string, ) bool { @@ -875,7 +896,7 @@ pub fn eqlLong(a_str: string, b_str: string, comptime check_len: bool) bool { return false; } } else { - if (comptime Environment.allow_assert) assert(b_str.len == a_str.len); + if (comptime Environment.allow_assert) assert(b_str.len <= a_str.len); } const end = b_str.ptr + len; @@ -1269,6 +1290,17 @@ pub fn withoutUTF8BOM(bytes: []const u8) []const u8 { } } +// https://github.com/WebKit/WebKit/blob/443e796d1538654c34f2690e39600c70c8052b63/Source/WebCore/PAL/pal/text/TextCodecUTF8.cpp#L69 +pub fn nonASCIISequenceLength(first_byte: u8) u3 { + return switch (first_byte) { + 0...193 => 0, + 194...223 => 2, + 224...239 => 3, + 240...244 => 4, + 245...255 => 0, + }; +} + /// Convert a UTF-8 string to a UTF-16 string IF there are any non-ascii characters /// If there are no non-ascii characters, this returns null /// This is intended to be used for strings that go to JavaScript @@ -1319,15 +1351,7 @@ pub fn toUTF16Alloc(allocator: std.mem.Allocator, bytes: []const u8, comptime fa var remaining = bytes[i..]; { - const sequence: [4]u8 = switch (remaining.len) { - 0 => unreachable, - 1 => [_]u8{ remaining[0], 0, 0, 0 }, - 2 => [_]u8{ remaining[0], remaining[1], 0, 0 }, - 3 => [_]u8{ remaining[0], remaining[1], remaining[2], 0 }, - else => remaining[0..4].*, - }; - - const replacement = strings.convertUTF8BytesIntoUTF16(&sequence); + const replacement = strings.convertUTF8BytesIntoUTF16(remaining); if (comptime fail_if_invalid) { if (replacement.fail) { if (comptime Environment.allow_assert) assert(replacement.code_point == unicode_replacement); @@ -1354,15 +1378,7 @@ pub fn toUTF16Alloc(allocator: std.mem.Allocator, bytes: []const u8, comptime fa strings.copyU8IntoU16(output.items[end..][0..j], remaining[0..j]); remaining = remaining[j..]; - const sequence: [4]u8 = switch (remaining.len) { - 0 => unreachable, - 1 => [_]u8{ remaining[0], 0, 0, 0 }, - 2 => [_]u8{ remaining[0], remaining[1], 0, 0 }, - 3 => [_]u8{ remaining[0], remaining[1], remaining[2], 0 }, - else => remaining[0..4].*, - }; - - const replacement = strings.convertUTF8BytesIntoUTF16(&sequence); + const replacement = strings.convertUTF8BytesIntoUTF16(remaining); if (comptime fail_if_invalid) { if (replacement.fail) { if (comptime Environment.allow_assert) assert(replacement.code_point == unicode_replacement); @@ -1415,6 +1431,101 @@ pub fn toUTF16AllocForReal(allocator: std.mem.Allocator, bytes: []const u8, comp }; } +pub fn toUTF16AllocMaybeBuffered( + allocator: std.mem.Allocator, + bytes: []const u8, + comptime fail_if_invalid: bool, + comptime flush: bool, +) error{ OutOfMemory, InvalidByteSequence }!?struct { []u16, [3]u8, u2 } { + const first_non_ascii = strings.firstNonASCII(bytes) orelse return null; + + var output: std.ArrayListUnmanaged(u16) = if (comptime bun.FeatureFlags.use_simdutf) output: { + const out_length = bun.simdutf.length.utf16.from.utf8(bytes); + + if (out_length == 0) { + break :output .{}; + } + + var out = try allocator.alloc(u16, out_length); + + const res = bun.simdutf.convert.utf8.to.utf16.with_errors.le(bytes, out); + if (res.status == .success) { + log("toUTF16 {d} UTF8 -> {d} UTF16", .{ bytes.len, out_length }); + return .{ out, .{0} ** 3, 0 }; + } + + var list = std.ArrayListUnmanaged(u16).fromOwnedSlice(out[0..first_non_ascii]); + list.capacity = out.len; + + break :output list; + } else .{}; + errdefer output.deinit(allocator); + + const start = if (output.items.len > 0) first_non_ascii else 0; + var remaining = bytes[start..]; + + var non_ascii: ?u32 = 0; + while (non_ascii) |i| : (non_ascii = strings.firstNonASCII(remaining)) { + { + const end = output.items.len; + try output.ensureUnusedCapacity(allocator, i + 2); // +2 for UTF16 codepoint + output.items.len += i; + strings.copyU8IntoU16(output.items[end..][0..i], remaining[0..i]); + remaining = remaining[i..]; + } + + const sequence: [4]u8 = switch (remaining.len) { + 0 => unreachable, + 1 => .{ remaining[0], 0, 0, 0 }, + 2 => .{ remaining[0], remaining[1], 0, 0 }, + 3 => .{ remaining[0], remaining[1], remaining[2], 0 }, + else => remaining[0..4].*, + }; + + const converted_length = strings.nonASCIISequenceLength(sequence[0]); + + const converted = strings.convertUTF8BytesIntoUTF16WithLength(&sequence, converted_length, remaining.len); + + if (comptime !flush) { + if (converted.fail and converted.can_buffer and converted_length > remaining.len) { + const buffered: [3]u8 = switch (remaining.len) { + else => unreachable, + 1 => .{ remaining[0], 0, 0 }, + 2 => .{ remaining[0], remaining[1], 0 }, + 3 => .{ remaining[0], remaining[1], remaining[2] }, + }; + return .{ output.items, buffered, @intCast(remaining.len) }; + } + } + + if (comptime fail_if_invalid) { + if (converted.fail) { + if (comptime Environment.allow_assert) { + bun.assert(converted.code_point == unicode_replacement); + } + return error.InvalidByteSequence; + } + } + + remaining = remaining[@max(converted.len, 1)..]; + + // #define U16_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2) + switch (converted.code_point) { + 0...0xffff => |c| output.appendAssumeCapacity(@intCast(c)), + else => |c| output.appendSliceAssumeCapacity(&.{ strings.u16Lead(c), strings.u16Trail(c) }), + } + } + + if (remaining.len > 0) { + try output.ensureTotalCapacityPrecise(allocator, output.items.len + remaining.len); + output.items.len += remaining.len; + strings.copyU8IntoU16(output.items[output.items.len - remaining.len ..], remaining); + } + + log("toUTF16 {d} UTF8 -> {d} UTF16", .{ bytes.len, output.items.len }); + return .{ output.items, .{0} ** 3, 0 }; +} + pub fn toUTF16AllocNoTrim(allocator: std.mem.Allocator, bytes: []const u8, comptime fail_if_invalid: bool, comptime _: bool) !?[]u16 { if (strings.firstNonASCII(bytes)) |i| { const output_: ?std.ArrayList(u16) = if (comptime bun.FeatureFlags.use_simdutf) simd: { @@ -1453,15 +1564,7 @@ pub fn toUTF16AllocNoTrim(allocator: std.mem.Allocator, bytes: []const u8, compt var remaining = bytes[i..]; { - const sequence: [4]u8 = switch (remaining.len) { - 0 => unreachable, - 1 => [_]u8{ remaining[0], 0, 0, 0 }, - 2 => [_]u8{ remaining[0], remaining[1], 0, 0 }, - 3 => [_]u8{ remaining[0], remaining[1], remaining[2], 0 }, - else => remaining[0..4].*, - }; - - const replacement = strings.convertUTF8BytesIntoUTF16(&sequence); + const replacement = strings.convertUTF8BytesIntoUTF16(remaining); if (comptime fail_if_invalid) { if (replacement.fail) { if (comptime Environment.allow_assert) assert(replacement.code_point == unicode_replacement); @@ -1488,15 +1591,7 @@ pub fn toUTF16AllocNoTrim(allocator: std.mem.Allocator, bytes: []const u8, compt strings.copyU8IntoU16(output.items[end..][0..j], remaining[0..j]); remaining = remaining[j..]; - const sequence: [4]u8 = switch (remaining.len) { - 0 => unreachable, - 1 => [_]u8{ remaining[0], 0, 0, 0 }, - 2 => [_]u8{ remaining[0], remaining[1], 0, 0 }, - 3 => [_]u8{ remaining[0], remaining[1], remaining[2], 0 }, - else => remaining[0..4].*, - }; - - const replacement = strings.convertUTF8BytesIntoUTF16(&sequence); + const replacement = strings.convertUTF8BytesIntoUTF16(remaining); if (comptime fail_if_invalid) { if (replacement.fail) { if (comptime Environment.allow_assert) assert(replacement.code_point == unicode_replacement); @@ -1530,13 +1625,18 @@ pub fn toUTF16AllocNoTrim(allocator: std.mem.Allocator, bytes: []const u8, compt } pub fn utf16CodepointWithFFFD(comptime Type: type, input: Type) UTF16Replacement { - const c0 = @as(u21, input[0]); + return utf16CodepointWithFFFDAndFirstInputChar(Type, input[0], input); +} + +fn utf16CodepointWithFFFDAndFirstInputChar(comptime Type: type, char: std.meta.Elem(Type), input: Type) UTF16Replacement { + const c0 = @as(u21, char); if (c0 & ~@as(u21, 0x03ff) == 0xd800) { // surrogate pair if (input.len == 1) return .{ .len = 1, + .is_lead = true, }; //error.DanglingSurrogateHalf; const c1 = @as(u21, input[1]); @@ -1550,6 +1650,7 @@ pub fn utf16CodepointWithFFFD(comptime Type: type, input: Type) UTF16Replacement .fail = true, .len = 1, .code_point = unicode_replacement, + .is_lead = true, }; }; // return error.ExpectedSecondSurrogateHalf; @@ -1641,8 +1742,16 @@ pub fn toNTPath(wbuf: []u16, utf8: []const u8) [:0]const u16 { return toWPathNormalized(wbuf, utf8); } - wbuf[0..4].* = bun.windows.nt_object_prefix; - return wbuf[0 .. toWPathNormalized(wbuf[4..], utf8).len + 4 :0]; + // UNC absolute path, replace leading '\\' with '\??\UNC\' + if (strings.hasPrefixComptime(utf8, "\\\\")) { + const prefix = bun.windows.nt_unc_object_prefix; + wbuf[0..prefix.len].* = prefix; + return wbuf[0 .. toWPathNormalized(wbuf[prefix.len..], utf8[2..]).len + prefix.len :0]; + } + + const prefix = bun.windows.nt_object_prefix; + wbuf[0..prefix.len].* = prefix; + return wbuf[0 .. toWPathNormalized(wbuf[prefix.len..], utf8).len + prefix.len :0]; } pub fn addNTPathPrefix(wbuf: []u16, utf16: []const u16) [:0]const u16 { @@ -1736,7 +1845,9 @@ pub fn toWDirPath(wbuf: []u16, utf8: []const u8) [:0]const u16 { pub fn assertIsValidWindowsPath(comptime T: type, path: []const T) void { if (Environment.allow_assert and Environment.isWindows) { if (bun.path.Platform.windows.isAbsoluteT(T, path) and - isWindowsAbsolutePathMissingDriveLetter(T, path)) + isWindowsAbsolutePathMissingDriveLetter(T, path) and + // is it a null device path? that's not an error. it's just a weird file path. + !eqlComptimeT(T, path, "\\\\.\\NUL") and !eqlComptimeT(T, path, "\\\\.\\nul") and !eqlComptimeT(T, path, "\\nul") and !eqlComptimeT(T, path, "\\NUL")) { std.debug.panic("Internal Error: Do not pass posix paths to Windows APIs, was given '{s}'" ++ if (Environment.isDebug) " (missing a root like 'C:\\', see PosixToWinNormalizer for why this is an assertion)" else ". Please open an issue on GitHub with a reproduction.", .{ if (T == u8) path else bun.fmt.utf16(path), @@ -1776,7 +1887,7 @@ pub fn convertUTF16ToUTF8(list_: std.ArrayList(u8), comptime Type: type, utf16: ); if (result.status == .surrogate) { // Slow path: there was invalid UTF-16, so we need to convert it without simdutf. - return toUTF8ListWithTypeBun(&list, Type, utf16); + return toUTF8ListWithTypeBun(&list, Type, utf16, false); } list.items.len = result.count; @@ -1791,7 +1902,7 @@ pub fn convertUTF16ToUTF8Append(list: *std.ArrayList(u8), utf16: []const u16) !v if (result.status == .surrogate) { // Slow path: there was invalid UTF-16, so we need to convert it without simdutf. - _ = try toUTF8ListWithTypeBun(list, []const u16, utf16); + _ = try toUTF8ListWithTypeBun(list, []const u16, utf16, false); return; } @@ -1865,14 +1976,15 @@ pub fn toUTF8FromLatin1Z(allocator: std.mem.Allocator, latin1: []const u8) !?std return list1; } -pub fn toUTF8ListWithTypeBun(list: *std.ArrayList(u8), comptime Type: type, utf16: Type) !std.ArrayList(u8) { +pub fn toUTF8ListWithTypeBun(list: *std.ArrayList(u8), comptime Type: type, utf16: Type, comptime skip_trailing_replacement: bool) !(if (skip_trailing_replacement) ?u16 else std.ArrayList(u8)) { var utf16_remaining = utf16; while (firstNonASCII16(Type, utf16_remaining)) |i| { const to_copy = utf16_remaining[0..i]; utf16_remaining = utf16_remaining[i..]; + const token = utf16_remaining[0]; - const replacement = utf16CodepointWithFFFD(Type, utf16_remaining); + const replacement = utf16CodepointWithFFFDAndFirstInputChar(Type, token, utf16_remaining); utf16_remaining = utf16_remaining[replacement.len..]; const count: usize = replacement.utf8Width(); @@ -1889,8 +2001,13 @@ pub fn toUTF8ListWithTypeBun(list: *std.ArrayList(u8), comptime Type: type, utf1 to_copy, ); - list.items.len += count; + if (comptime skip_trailing_replacement) { + if (replacement.is_lead and utf16_remaining.len == 0) { + return token; + } + } + list.items.len += count; _ = encodeWTF8RuneT( list.items.ptr[list.items.len - count .. list.items.len - count + 4][0..4], u32, @@ -1907,6 +2024,9 @@ pub fn toUTF8ListWithTypeBun(list: *std.ArrayList(u8), comptime Type: type, utf1 log("UTF16 {d} -> {d} UTF8", .{ utf16.len, list.items.len }); + if (comptime skip_trailing_replacement) { + return null; + } return list.*; } @@ -2053,7 +2173,10 @@ pub const UTF16Replacement = struct { /// and a genuine error. fail: bool = false, - pub inline fn utf8Width(replacement: UTF16Replacement) usize { + can_buffer: bool = true, + is_lead: bool = false, + + pub inline fn utf8Width(replacement: UTF16Replacement) u3 { return switch (replacement.code_point) { 0...0x7F => 1, (0x7F + 1)...0x7FF => 2, @@ -2063,10 +2186,8 @@ pub const UTF16Replacement = struct { } }; -// This variation matches WebKit behavior. -pub fn convertUTF8BytesIntoUTF16(sequence: *const [4]u8) UTF16Replacement { +fn convertUTF8BytesIntoUTF16WithLength(sequence: *const [4]u8, len: u3, remaining_len: usize) UTF16Replacement { if (comptime Environment.allow_assert) assert(sequence[0] > 127); - const len = wtf8ByteSequenceLengthWithInvalid(sequence[0]); switch (len) { 2 => { if (comptime Environment.allow_assert) { @@ -2074,7 +2195,7 @@ pub fn convertUTF8BytesIntoUTF16(sequence: *const [4]u8) UTF16Replacement { bun.assert(sequence[0] <= 0xDF); } if (sequence[1] < 0x80 or sequence[1] > 0xBF) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } return .{ .len = len, .code_point = ((@as(u32, sequence[0]) << 6) + @as(u32, sequence[1])) - 0x00003080 }; }, @@ -2086,22 +2207,22 @@ pub fn convertUTF8BytesIntoUTF16(sequence: *const [4]u8) UTF16Replacement { switch (sequence[0]) { 0xE0 => { if (sequence[1] < 0xA0 or sequence[1] > 0xBF) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } }, 0xED => { if (sequence[1] < 0x80 or sequence[1] > 0x9F) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } }, else => { if (sequence[1] < 0x80 or sequence[1] > 0xBF) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } }, } if (sequence[2] < 0x80 or sequence[2] > 0xBF) { - return .{ .len = 2, .fail = true }; + return .{ .len = 2, .fail = true, .can_buffer = remaining_len < 3 }; } return .{ .len = len, @@ -2112,36 +2233,36 @@ pub fn convertUTF8BytesIntoUTF16(sequence: *const [4]u8) UTF16Replacement { switch (sequence[0]) { 0xF0 => { if (sequence[1] < 0x90 or sequence[1] > 0xBF) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } }, 0xF4 => { if (sequence[1] < 0x80 or sequence[1] > 0x8F) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } }, // invalid code point // this used to be an assertion 0...(0xF0 - 1), 0xF4 + 1...std.math.maxInt(@TypeOf(sequence[0])) => { - return UTF16Replacement{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = false }; }, else => { if (sequence[1] < 0x80 or sequence[1] > 0xBF) { - return .{ .len = 1, .fail = true }; + return .{ .len = 1, .fail = true, .can_buffer = remaining_len < 2 }; } }, } if (sequence[2] < 0x80 or sequence[2] > 0xBF) { - return .{ .len = 2, .fail = true }; + return .{ .len = 2, .fail = true, .can_buffer = remaining_len < 3 }; } if (sequence[3] < 0x80 or sequence[3] > 0xBF) { - return .{ .len = 3, .fail = true }; + return .{ .len = 3, .fail = true, .can_buffer = remaining_len < 4 }; } return .{ - .len = 4, + .len = len, .code_point = ((@as(u32, sequence[0]) << 18) + (@as(u32, sequence[1]) << 12) + (@as(u32, sequence[2]) << 6) + @as(u32, sequence[3])) - 0x03C82080, @@ -2153,6 +2274,21 @@ pub fn convertUTF8BytesIntoUTF16(sequence: *const [4]u8) UTF16Replacement { } } +// This variation matches WebKit behavior. +// fn convertUTF8BytesIntoUTF16(sequence: *const [4]u8, remaining_len: usize) UTF16Replacement { +fn convertUTF8BytesIntoUTF16(bytes: []const u8) UTF16Replacement { + const sequence: [4]u8 = switch (bytes.len) { + 0 => unreachable, + 1 => [_]u8{ bytes[0], 0, 0, 0 }, + 2 => [_]u8{ bytes[0], bytes[1], 0, 0 }, + 3 => [_]u8{ bytes[0], bytes[1], bytes[2], 0 }, + else => bytes[0..4].*, + }; + if (comptime Environment.allow_assert) assert(sequence[0] > 127); + const sequence_length = nonASCIISequenceLength(sequence[0]); + return convertUTF8BytesIntoUTF16WithLength(&sequence, sequence_length, bytes.len); +} + pub fn copyLatin1IntoUTF8(buf_: []u8, comptime Type: type, latin1_: Type) EncodeIntoResult { return copyLatin1IntoUTF8StopOnNonASCII(buf_, Type, latin1_, false); } @@ -2565,8 +2701,7 @@ pub fn escapeHTMLForLatin1Input(allocator: std.mem.Allocator, latin1: []const u8 buf = try std.ArrayList(u8).initCapacity(allocator, latin1.len + 6); const copy_len = @intFromPtr(remaining.ptr) - @intFromPtr(latin1.ptr); - @memcpy(buf.items[0..copy_len], latin1[0..copy_len]); - buf.items.len = copy_len; + buf.appendSliceAssumeCapacity(latin1[0..copy_len]); any_needs_escape = true; inline for (0..ascii_vector_size) |i| { switch (vec[i]) { @@ -3315,6 +3450,35 @@ pub fn encodeWTF8RuneT(p: *[4]u8, comptime R: type, r: R) u3 { } } +pub fn wtf8Sequence(code_point: u32) [4]u8 { + return switch (code_point) { + 0...0x7f => .{ + @intCast(code_point), + 0, + 0, + 0, + }, + (0x7f + 1)...0x7ff => .{ + @truncate(0xc0 | (code_point >> 6)), + @truncate(0x80 | (code_point & 0x3f)), + 0, + 0, + }, + (0x7ff + 1)...0xffff => .{ + @truncate(0xe0 | (code_point >> 12)), + @truncate(0x80 | ((code_point >> 6) & 0x3f)), + @truncate(0x80 | (code_point & 0x3f)), + 0, + }, + else => .{ + @truncate(0xf0 | (code_point >> 18)), + @truncate(0x80 | ((code_point >> 12) & 0x3f)), + @truncate(0x80 | ((code_point >> 6) & 0x3f)), + @truncate(0x80 | (code_point & 0x3f)), + }, + }; +} + pub inline fn wtf8ByteSequenceLength(first_byte: u8) u3 { return switch (first_byte) { 0 => 0, @@ -3499,16 +3663,36 @@ pub fn isAllASCII(slice: []const u8) bool { return true; } -//#define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) +// #define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) pub inline fn u16Lead(supplementary: anytype) u16 { - return @as(u16, @intCast((supplementary >> 10) + 0xd7c0)); + return @intCast((supplementary >> 10) + 0xd7c0); } -//#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) +// #define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) pub inline fn u16Trail(supplementary: anytype) u16 { - return @as(u16, @intCast((supplementary & 0x3ff) | 0xdc00)); + return @intCast((supplementary & 0x3ff) | 0xdc00); +} + +// #define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00) +pub inline fn u16IsTrail(supplementary: u16) bool { + return (@as(u32, @intCast(supplementary)) & 0xfffffc00) == 0xdc00; +} + +// #define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800) +pub inline fn u16IsLead(supplementary: u16) bool { + return (@as(u32, @intCast(supplementary)) & 0xfffffc00) == 0xd800; +} + +// #define U16_GET_SUPPLEMENTARY(lead, trail) \ +// (((UChar32)(lead)<<10UL)+(UChar32)(trail)-U16_SURROGATE_OFFSET) +pub inline fn u16GetSupplementary(lead: u32, trail: u32) u32 { + const shifted = lead << 10; + return (shifted + trail) - u16_surrogate_offset; } +// #define U16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000) +pub const u16_surrogate_offset = 56613888; + pub fn firstNonASCII(slice: []const u8) ?u32 { return firstNonASCIIWithType([]const u8, slice); } @@ -5996,6 +6180,13 @@ pub fn withoutPrefixComptime(input: []const u8, comptime prefix: []const u8) []c return input; } +pub fn withoutPrefixIfPossibleComptime(input: string, comptime prefix: string) ?string { + if (hasPrefixComptime(input, prefix)) { + return input[prefix.len..]; + } + return null; +} + // extern "C" bool icu_hasBinaryProperty(UChar32 cp, unsigned int prop) extern fn icu_hasBinaryProperty(c: u32, which: c_uint) bool; diff --git a/src/string_mutable.zig b/src/string_mutable.zig index 02ac07cbb3d75..c894eaf2e9b4c 100644 --- a/src/string_mutable.zig +++ b/src/string_mutable.zig @@ -45,6 +45,7 @@ pub const MutableString = struct { } pub fn write(self: *MutableString, bytes: anytype) !usize { + bun.debugAssert(bytes.len == 0 or !bun.isSliceInBuffer(bytes, self.list.allocatedSlice())); try self.list.appendSlice(self.allocator, bytes); return bytes.len; } diff --git a/src/symbols.def b/src/symbols.def index 1aa19a0cbfde7..71f031ae9b34d 100644 --- a/src/symbols.def +++ b/src/symbols.def @@ -567,3 +567,62 @@ EXPORTS napi_is_detached_arraybuffer napi_create_external_buffer napi_fatal_exception + ?TryGetCurrent@Isolate@v8@@SAPEAV12@XZ + ?GetCurrent@Isolate@v8@@SAPEAV12@XZ + ?GetCurrentContext@Isolate@v8@@QEAA?AV?$Local@VContext@v8@@@2@XZ + ?AddEnvironmentCleanupHook@node@@YAXPEAVIsolate@v8@@P6AXPEAX@Z1@Z + ?RemoveEnvironmentCleanupHook@node@@YAXPEAVIsolate@v8@@P6AXPEAX@Z1@Z + ?New@Number@v8@@SA?AV?$Local@VNumber@v8@@@2@PEAVIsolate@2@N@Z + ?Value@Number@v8@@QEBANXZ + ?NewFromUtf8@String@v8@@SA?AV?$MaybeLocal@VString@v8@@@2@PEAVIsolate@2@PEBDW4NewStringType@2@H@Z + ?WriteUtf8@String@v8@@QEBAHPEAVIsolate@2@PEADHPEAHH@Z + ?ToLocalEmpty@api_internal@v8@@YAXXZ + ?Length@String@v8@@QEBAHXZ + ?New@External@v8@@SA?AV?$Local@VExternal@v8@@@2@PEAVIsolate@2@PEAX@Z + ?Value@External@v8@@QEBAPEAXXZ + ?New@Object@v8@@SA?AV?$Local@VObject@v8@@@2@PEAVIsolate@2@@Z + ?Set@Object@v8@@QEAA?AV?$Maybe@_N@2@V?$Local@VContext@v8@@@2@V?$Local@VValue@v8@@@2@1@Z + ?SetInternalField@Object@v8@@QEAAXHV?$Local@VData@v8@@@2@@Z + ?SlowGetInternalField@Object@v8@@AEAA?AV?$Local@VData@v8@@@2@H@Z + ?CreateHandle@HandleScope@v8@@KAPEA_KPEAVIsolate@internal@2@_K@Z + ??0HandleScope@v8@@QEAA@PEAVIsolate@1@@Z + ??1HandleScope@v8@@QEAA@XZ + ?GetFunction@FunctionTemplate@v8@@QEAA?AV?$MaybeLocal@VFunction@v8@@@2@V?$Local@VContext@v8@@@2@@Z + ?New@FunctionTemplate@v8@@SA?AV?$Local@VFunctionTemplate@v8@@@2@PEAVIsolate@2@P6AXAEBV?$FunctionCallbackInfo@VValue@v8@@@2@@ZV?$Local@VValue@v8@@@2@V?$Local@VSignature@v8@@@2@HW4ConstructorBehavior@2@W4SideEffectType@2@PEBVCFunction@2@GGG@Z + ?NewInstance@ObjectTemplate@v8@@QEAA?AV?$MaybeLocal@VObject@v8@@@2@V?$Local@VContext@v8@@@2@@Z + ?SetInternalFieldCount@ObjectTemplate@v8@@QEAAXH@Z + ?InternalFieldCount@ObjectTemplate@v8@@QEBAHXZ + ?New@ObjectTemplate@v8@@SA?AV?$Local@VObjectTemplate@v8@@@2@PEAVIsolate@2@V?$Local@VFunctionTemplate@v8@@@2@@Z + ?EscapeSlot@EscapableHandleScopeBase@v8@@IEAAPEA_KPEA_K@Z + ??0EscapableHandleScopeBase@v8@@QEAA@PEAVIsolate@1@@Z + ?IsolateFromNeverReadOnlySpaceObject@internal@v8@@YAPEAVIsolate@12@_K@Z + ?New@Array@v8@@SA?AV?$Local@VArray@v8@@@2@PEAVIsolate@2@PEAV?$Local@VValue@v8@@@2@_K@Z + ?SetName@Function@v8@@QEAAXV?$Local@VString@v8@@@2@@Z + ?IsBoolean@Value@v8@@QEBA_NXZ + ?Value@Boolean@v8@@QEBA_NXZ + ?FullIsTrue@Value@v8@@AEBA_NXZ + ?FullIsFalse@Value@v8@@AEBA_NXZ + ??1EscapableHandleScope@v8@@QEAA@XZ + ??0EscapableHandleScope@v8@@QEAA@PEAVIsolate@1@@Z + ?IsObject@Value@v8@@QEBA_NXZ + ?IsNumber@Value@v8@@QEBA_NXZ + ?IsUint32@Value@v8@@QEBA_NXZ + ?Uint32Value@Value@v8@@QEBA?AV?$Maybe@I@2@V?$Local@VContext@v8@@@2@@Z + ?IsUndefined@Value@v8@@QEBA_NXZ + ?IsNull@Value@v8@@QEBA_NXZ + ?IsNullOrUndefined@Value@v8@@QEBA_NXZ + ?IsTrue@Value@v8@@QEBA_NXZ + ?IsFalse@Value@v8@@QEBA_NXZ + ?IsString@Value@v8@@QEBA_NXZ + ?New@Boolean@v8@@SA?AV?$Local@VBoolean@v8@@@2@PEAVIsolate@2@_N@Z + ?GetInternalField@Object@v8@@QEAA?AV?$Local@VData@v8@@@2@H@Z + ?GetIsolate@Context@v8@@QEAAPEAVIsolate@2@XZ + ?NewFromOneByte@String@v8@@SA?AV?$MaybeLocal@VString@v8@@@2@PEAVIsolate@2@PEBEW4NewStringType@2@H@Z + ?IsExternal@String@v8@@QEBA_NXZ + ?IsExternalOneByte@String@v8@@QEBA_NXZ + ?IsExternalTwoByte@String@v8@@QEBA_NXZ + ?IsOneByte@String@v8@@QEBA_NXZ + ?Utf8Length@String@v8@@QEBAHPEAVIsolate@2@@Z + ?ContainsOnlyOneByte@String@v8@@QEBA_NXZ + ?GlobalizeReference@api_internal@v8@@YAPEA_KPEAVIsolate@internal@2@_K@Z + ?DisposeGlobal@api_internal@v8@@YAXPEA_K@Z diff --git a/src/symbols.dyn b/src/symbols.dyn index 0be0db03ca0b0..32e163fa5a52a 100644 --- a/src/symbols.dyn +++ b/src/symbols.dyn @@ -51,6 +51,7 @@ _napi_define_class; _napi_define_properties; _napi_delete_async_work; + _napi_delete_element; _napi_delete_property; _napi_delete_reference; _napi_detach_arraybuffer; @@ -67,7 +68,6 @@ _napi_get_dataview_info; _napi_get_date_value; _napi_get_element; - _napi_delete_element; _napi_get_global; _napi_get_instance_data; _napi_get_last_error_info; @@ -144,9 +144,75 @@ _napi_unref_threadsafe_function; _napi_unwrap; _napi_wrap; + _node_api_create_external_string_latin1; + _node_api_create_external_string_utf16; _node_api_create_syntax_error; _node_api_symbol_for; _node_api_throw_syntax_error; - _node_api_create_external_string_latin1; - _node_api_create_external_string_utf16; -}; \ No newline at end of file + __ZN2v87Isolate10GetCurrentEv; + __ZN2v87Isolate13TryGetCurrentEv; + __ZN2v87Isolate17GetCurrentContextEv; + __ZN4node25AddEnvironmentCleanupHookEPN2v87IsolateEPFvPvES3_; + __ZN4node28RemoveEnvironmentCleanupHookEPN2v87IsolateEPFvPvES3_; + __ZN2v86Number3NewEPNS_7IsolateEd; + __ZNK2v86Number5ValueEv; + __ZN2v86String11NewFromUtf8EPNS_7IsolateEPKcNS_13NewStringTypeEi; + __ZNK2v86String9WriteUtf8EPNS_7IsolateEPciPii; + __ZN2v812api_internal12ToLocalEmptyEv; + __ZNK2v86String6LengthEv; + __ZN2v88External3NewEPNS_7IsolateEPv; + __ZNK2v88External5ValueEv; + __ZN2v86Object3NewEPNS_7IsolateE; + __ZN2v86Object3SetENS_5LocalINS_7ContextEEENS1_INS_5ValueEEES5_; + __ZN2v86Object16SetInternalFieldEiNS_5LocalINS_4DataEEE; + __ZN2v86Object20SlowGetInternalFieldEi; + __ZN2v811HandleScope12CreateHandleEPNS_8internal7IsolateEm; + __ZN2v811HandleScopeC1EPNS_7IsolateE; + __ZN2v811HandleScopeD1Ev; + __ZN2v811HandleScopeD2Ev; + __ZN2v816FunctionTemplate11GetFunctionENS_5LocalINS_7ContextEEE; + __ZN2v816FunctionTemplate3NewEPNS_7IsolateEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEENS_5LocalIS4_EENSA_INS_9SignatureEEEiNS_19ConstructorBehaviorENS_14SideEffectTypeEPKNS_9CFunctionEttt; + __ZN2v814ObjectTemplate11NewInstanceENS_5LocalINS_7ContextEEE; + __ZN2v814ObjectTemplate21SetInternalFieldCountEi; + __ZNK2v814ObjectTemplate18InternalFieldCountEv; + __ZN2v814ObjectTemplate3NewEPNS_7IsolateENS_5LocalINS_16FunctionTemplateEEE; + __ZN2v824EscapableHandleScopeBase10EscapeSlotEPm; + __ZN2v824EscapableHandleScopeBaseC2EPNS_7IsolateE; + __ZN2v88internal35IsolateFromNeverReadOnlySpaceObjectEm; + _node_module_register; + __ZN3JSC9CallFrame13describeFrameEv; + __ZN2v85Array3NewEPNS_7IsolateEPNS_5LocalINS_5ValueEEEm; + __ZN2v88Function7SetNameENS_5LocalINS_6StringEEE; + __ZNK2v85Value9IsBooleanEv; + __ZNK2v87Boolean5ValueEv; + __ZNK2v85Value10FullIsTrueEv; + __ZNK2v85Value11FullIsFalseEv; + __ZN2v820EscapableHandleScopeC1EPNS_7IsolateE; + __ZN2v820EscapableHandleScopeC2EPNS_7IsolateE; + __ZN2v820EscapableHandleScopeD1Ev; + __ZN2v820EscapableHandleScopeD2Ev; + __ZNK2v85Value8IsObjectEv; + __ZNK2v85Value8IsNumberEv; + __ZNK2v85Value8IsUint32Ev; + __ZNK2v85Value11Uint32ValueENS_5LocalINS_7ContextEEE; + __ZNK2v85Value11IsUndefinedEv; + __ZNK2v85Value6IsNullEv; + __ZNK2v85Value17IsNullOrUndefinedEv; + __ZNK2v85Value6IsTrueEv; + __ZNK2v85Value7IsFalseEv; + __ZNK2v85Value8IsStringEv; + __ZN2v87Boolean3NewEPNS_7IsolateEb; + __ZN2v86Object16GetInternalFieldEi; + __ZN2v87Context10GetIsolateEv; + __ZN2v86String14NewFromOneByteEPNS_7IsolateEPKhNS_13NewStringTypeEi; + __ZNK2v86String10Utf8LengthEPNS_7IsolateE; + __ZNK2v86String10IsExternalEv; + __ZNK2v86String17IsExternalOneByteEv; + __ZNK2v86String17IsExternalTwoByteEv; + __ZNK2v86String9IsOneByteEv; + __ZNK2v86String19ContainsOnlyOneByteEv; + __ZN2v812api_internal18GlobalizeReferenceEPNS_8internal7IsolateEm; + __ZN2v812api_internal13DisposeGlobalEPm; + _uv_os_getpid; + _uv_os_getppid; +}; diff --git a/src/symbols.txt b/src/symbols.txt index 577035fa93d8b..7935b0c1061e5 100644 --- a/src/symbols.txt +++ b/src/symbols.txt @@ -153,3 +153,64 @@ __ZN2v87Isolate13TryGetCurrentEv __ZN2v87Isolate17GetCurrentContextEv __ZN4node25AddEnvironmentCleanupHookEPN2v87IsolateEPFvPvES3_ __ZN4node28RemoveEnvironmentCleanupHookEPN2v87IsolateEPFvPvES3_ +__ZN2v86Number3NewEPNS_7IsolateEd +__ZNK2v86Number5ValueEv +__ZN2v86String11NewFromUtf8EPNS_7IsolateEPKcNS_13NewStringTypeEi +__ZNK2v86String9WriteUtf8EPNS_7IsolateEPciPii +__ZN2v812api_internal12ToLocalEmptyEv +__ZNK2v86String6LengthEv +__ZN2v88External3NewEPNS_7IsolateEPv +__ZNK2v88External5ValueEv +__ZN2v86Object3NewEPNS_7IsolateE +__ZN2v86Object3SetENS_5LocalINS_7ContextEEENS1_INS_5ValueEEES5_ +__ZN2v86Object16SetInternalFieldEiNS_5LocalINS_4DataEEE +__ZN2v86Object20SlowGetInternalFieldEi +__ZN2v811HandleScope12CreateHandleEPNS_8internal7IsolateEm +__ZN2v811HandleScopeC1EPNS_7IsolateE +__ZN2v811HandleScopeD1Ev +__ZN2v811HandleScopeD2Ev +__ZN2v816FunctionTemplate11GetFunctionENS_5LocalINS_7ContextEEE +__ZN2v816FunctionTemplate3NewEPNS_7IsolateEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEENS_5LocalIS4_EENSA_INS_9SignatureEEEiNS_19ConstructorBehaviorENS_14SideEffectTypeEPKNS_9CFunctionEttt +__ZN2v814ObjectTemplate11NewInstanceENS_5LocalINS_7ContextEEE +__ZN2v814ObjectTemplate21SetInternalFieldCountEi +__ZNK2v814ObjectTemplate18InternalFieldCountEv +__ZN2v814ObjectTemplate3NewEPNS_7IsolateENS_5LocalINS_16FunctionTemplateEEE +__ZN2v824EscapableHandleScopeBase10EscapeSlotEPm +__ZN2v824EscapableHandleScopeBaseC2EPNS_7IsolateE +__ZN2v88internal35IsolateFromNeverReadOnlySpaceObjectEm +_node_module_register +__ZN3JSC9CallFrame13describeFrameEv +__ZN2v85Array3NewEPNS_7IsolateEPNS_5LocalINS_5ValueEEEm +__ZN2v88Function7SetNameENS_5LocalINS_6StringEEE +__ZNK2v85Value9IsBooleanEv +__ZNK2v87Boolean5ValueEv +__ZNK2v85Value10FullIsTrueEv +__ZNK2v85Value11FullIsFalseEv +__ZN2v820EscapableHandleScopeC1EPNS_7IsolateE +__ZN2v820EscapableHandleScopeC2EPNS_7IsolateE +__ZN2v820EscapableHandleScopeD1Ev +__ZN2v820EscapableHandleScopeD2Ev +__ZNK2v85Value8IsObjectEv +__ZNK2v85Value8IsNumberEv +__ZNK2v85Value8IsUint32Ev +__ZNK2v85Value11Uint32ValueENS_5LocalINS_7ContextEEE +__ZNK2v85Value11IsUndefinedEv +__ZNK2v85Value6IsNullEv +__ZNK2v85Value17IsNullOrUndefinedEv +__ZNK2v85Value6IsTrueEv +__ZNK2v85Value7IsFalseEv +__ZNK2v85Value8IsStringEv +__ZN2v87Boolean3NewEPNS_7IsolateEb +__ZN2v86Object16GetInternalFieldEi +__ZN2v87Context10GetIsolateEv +__ZN2v86String14NewFromOneByteEPNS_7IsolateEPKhNS_13NewStringTypeEi +__ZNK2v86String10Utf8LengthEPNS_7IsolateE +__ZNK2v86String10IsExternalEv +__ZNK2v86String17IsExternalOneByteEv +__ZNK2v86String17IsExternalTwoByteEv +__ZNK2v86String9IsOneByteEv +__ZNK2v86String19ContainsOnlyOneByteEv +__ZN2v812api_internal18GlobalizeReferenceEPNS_8internal7IsolateEm +__ZN2v812api_internal13DisposeGlobalEPm +_uv_os_getpid +_uv_os_getppid diff --git a/src/sys.zig b/src/sys.zig index 5d70b8c1323b6..ad887dc605e00 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -194,6 +194,7 @@ pub const Tag = enum(u8) { mkdir, mkdtemp, fnctl, + memfd_create, mmap, munmap, open, @@ -365,12 +366,12 @@ pub const Error = struct { break :brk @as(C.SystemErrno, @enumFromInt(this.errno)); }; - if (std.enums.tagName(bun.C.SystemErrno, system_errno)) |errname| { + if (bun.tagName(bun.C.SystemErrno, system_errno)) |errname| { return errname; } } else if (this.errno > 0 and this.errno < C.SystemErrno.max) { const system_errno = @as(C.SystemErrno, @enumFromInt(this.errno)); - if (std.enums.tagName(bun.C.SystemErrno, system_errno)) |errname| { + if (bun.tagName(bun.C.SystemErrno, system_errno)) |errname| { return errname; } } @@ -378,6 +379,10 @@ pub const Error = struct { return "UNKNOWN"; } + pub fn toZigErr(this: Error) anyerror { + return bun.errnoToZigErr(this.errno); + } + pub fn toSystemError(this: Error) SystemError { var err = SystemError{ .errno = @as(c_int, this.errno) * -1, @@ -403,7 +408,7 @@ pub const Error = struct { break :brk @as(C.SystemErrno, @enumFromInt(this.errno)); }; - if (std.enums.tagName(bun.C.SystemErrno, system_errno)) |errname| { + if (bun.tagName(bun.C.SystemErrno, system_errno)) |errname| { err.code = bun.String.static(errname); if (C.SystemErrno.labels.get(system_errno)) |label| { err.message = bun.String.static(label); @@ -484,6 +489,15 @@ pub fn fchmodat(fd: bun.FileDescriptor, path: [:0]const u8, mode: bun.Mode, flag Maybe(void).success; } +pub fn chmod(path: [:0]const u8, mode: bun.Mode) Maybe(void) { + if (comptime Environment.isWindows) { + return sys_uv.chmod(path, mode); + } + + return Maybe(void).errnoSysP(C.chmod(path.ptr, mode), .chmod, path) orelse + Maybe(void).success; +} + pub fn chdirOSPath(destination: bun.OSPathSliceZ) Maybe(void) { assertIsValidWindowsPath(bun.OSPathChar, destination); @@ -590,8 +604,7 @@ pub fn fstat(fd: bun.FileDescriptor) Maybe(bun.Stat) { const dec = bun.FDImpl.decode(fd); if (dec.kind == .system) { const uvfd = bun.toLibUVOwnedFD(fd) catch return .{ .err = Error.fromCode(.MFILE, .uv_open_osfhandle) }; - defer _ = bun.sys.close(uvfd); - return sys_uv.fstat(fd); + return sys_uv.fstat(uvfd); } else return sys_uv.fstat(fd); } @@ -643,7 +656,16 @@ pub fn mkdiratW(dir_fd: bun.FileDescriptor, file_path: []const u16, _: i32) Mayb } pub fn fstatat(fd: bun.FileDescriptor, path: [:0]const u8) Maybe(bun.Stat) { - if (Environment.isWindows) @compileError("Use fstat on Windows"); + if (Environment.isWindows) { + return switch (openatWindowsA(fd, path, 0)) { + .result => |file| { + // :( + defer _ = close(file); + return fstat(file); + }, + .err => |err| Maybe(bun.Stat){ .err = err }, + }; + } var stat_ = mem.zeroes(bun.Stat); if (Maybe(bun.Stat).errnoSys(sys.fstatat(fd.int(), path, &stat_, 0), .fstatat)) |err| { log("fstatat({}, {s}) = {s}", .{ fd, path, @tagName(err.getErrno()) }); @@ -762,10 +784,39 @@ pub fn normalizePathWindows( var path = if (T == u16) path_ else bun.strings.convertUTF8toUTF16InBuffer(&wbuf, path_); if (std.fs.path.isAbsoluteWindowsWTF16(path)) { + // handle the special "nul" device + // we technically should handle the other DOS devices too. + if (path_.len >= "\\nul".len and + (bun.strings.eqlComptimeT(T, path_[path_.len - "\\nul".len ..], "\\nul") or + bun.strings.eqlComptimeT(T, path_[path_.len - "\\NUL".len ..], "\\NUL"))) + { + @memcpy(buf[0..bun.strings.w("\\??\\NUL").len], bun.strings.w("\\??\\NUL")); + buf[bun.strings.w("\\??\\NUL").len] = 0; + return .{ .result = buf[0..bun.strings.w("\\??\\NUL").len :0] }; + } + const norm = bun.path.normalizeStringGenericTZ(u16, path, buf, .{ .add_nt_prefix = true, .zero_terminate = true }); return .{ .result = norm }; } + if (bun.strings.indexOfAnyT(T, path_, &.{ '\\', '/', '.' }) == null) { + if (buf.len < path.len) { + return .{ + .err = .{ + .errno = @intFromEnum(bun.C.E.NOMEM), + .syscall = .open, + }, + }; + } + + // Skip the system call to get the final path name if it doesn't have any of the above characters. + @memcpy(buf[0..path.len], path); + buf[path.len] = 0; + return .{ + .result = buf[0..path.len :0], + }; + } + const base_fd = if (dir_fd == bun.invalid_fd) std.fs.cwd().fd else @@ -1731,14 +1782,14 @@ pub fn readlink(in: [:0]const u8, buf: []u8) Maybe([:0]u8) { pub fn readlinkat(fd: bun.FileDescriptor, in: [:0]const u8, buf: []u8) Maybe([:0]const u8) { while (true) { - const rc = sys.readlinkat(fd, in, buf.ptr, buf.len); + const rc = sys.readlinkat(fd.cast(), in, buf.ptr, buf.len); - if (Maybe(usize).errnoSys(rc, .readlink)) |err| { + if (Maybe([:0]const u8).errnoSys(rc, .readlink)) |err| { if (err.getErrno() == .INTR) continue; return err; } buf[@intCast(rc)] = 0; - return Maybe(usize){ .result = buf[0..@intCast(rc)] }; + return Maybe([:0]const u8){ .result = buf[0..@intCast(rc) :0] }; } } @@ -1791,8 +1842,31 @@ pub const RenameAt2Flags = packed struct { } }; -// NOTE: that this _does not_ handle moving across filesystems. For that, check if the return error is XDEV and then use `bun.C.moveFileZWithHandle` -pub fn renameatConcurrently(from_dir_fd: bun.FileDescriptor, from: [:0]const u8, to_dir_fd: bun.FileDescriptor, to: [:0]const u8) Maybe(void) { +pub fn renameatConcurrently( + from_dir_fd: bun.FileDescriptor, + from: [:0]const u8, + to_dir_fd: bun.FileDescriptor, + to: [:0]const u8, + comptime opts: struct { move_fallback: bool = false }, +) Maybe(void) { + switch (renameatConcurrentlyWithoutFallback(from_dir_fd, from, to_dir_fd, to)) { + .result => return Maybe(void).success, + .err => |e| { + if (opts.move_fallback and e.getErrno() == bun.C.E.XDEV) { + bun.Output.debugWarn("renameatConcurrently() failed with E.XDEV, falling back to moveFileZSlowMaybe()", .{}); + return bun.C.moveFileZSlowMaybe(from_dir_fd, from, to_dir_fd, to); + } + return .{ .err = e }; + }, + } +} + +pub fn renameatConcurrentlyWithoutFallback( + from_dir_fd: bun.FileDescriptor, + from: [:0]const u8, + to_dir_fd: bun.FileDescriptor, + to: [:0]const u8, +) Maybe(void) { var did_atomically_replace = false; attempt_atomic_rename_and_fallback_to_racy_delete: { @@ -1802,33 +1876,38 @@ pub fn renameatConcurrently(from_dir_fd: bun.FileDescriptor, from: [:0]const u8, var err = switch (bun.sys.renameat2(from_dir_fd, from, to_dir_fd, to, .{ .exclude = true, })) { - .err => |err| err, + // if ENOENT don't retry + .err => |err| if (err.getErrno() == .NOENT) return .{ .err = err } else err, .result => break :attempt_atomic_rename_and_fallback_to_racy_delete, }; - // Fallback path: the folder exists in the cache dir, it might be in a strange state - // let's attempt to atomically replace it with the temporary folder's version - if (if (comptime bun.Environment.isPosix) switch (err.getErrno()) { - .EXIST, .NOTEMPTY, .OPNOTSUPP => true, - else => false, - } else switch (err.getErrno()) { - .EXIST, .NOTEMPTY => true, - else => false, - }) { - did_atomically_replace = true; - switch (bun.sys.renameat2(from_dir_fd, from, to_dir_fd, to, .{ - .exchange = true, - })) { - .err => {}, - .result => break :attempt_atomic_rename_and_fallback_to_racy_delete, + // Windows doesn't have any equivalent with renameat with swap + if (!bun.Environment.isWindows) { + // Fallback path: the folder exists in the cache dir, it might be in a strange state + // let's attempt to atomically replace it with the temporary folder's version + if (switch (err.getErrno()) { + .EXIST, .NOTEMPTY, .OPNOTSUPP => true, + else => false, + }) { + did_atomically_replace = true; + switch (bun.sys.renameat2(from_dir_fd, from, to_dir_fd, to, .{ + .exchange = true, + })) { + .err => {}, + .result => break :attempt_atomic_rename_and_fallback_to_racy_delete, + } + did_atomically_replace = false; } - did_atomically_replace = false; } } // sad path: let's try to delete the folder and then rename it - var to_dir = to_dir_fd.asDir(); - to_dir.deleteTree(from) catch {}; + if (to_dir_fd.isValid()) { + var to_dir = to_dir_fd.asDir(); + to_dir.deleteTree(to) catch {}; + } else { + std.fs.deleteTreeAbsolute(to) catch {}; + } switch (bun.sys.renameat(from_dir_fd, from, to_dir_fd, to)) { .err => |err| { return .{ .err = err }; @@ -2215,6 +2294,17 @@ pub fn munmap(memory: []align(mem.page_size) const u8) Maybe(void) { } else return Maybe(void).success; } +pub fn memfd_create(name: [:0]const u8, flags: u32) Maybe(bun.FileDescriptor) { + if (comptime !Environment.isLinux) @compileError("linux only!"); + + const rc = std.os.linux.memfd_create(name, flags); + + log("memfd_create({s}, {d}) = {d}", .{ name, flags, rc }); + + return Maybe(bun.FileDescriptor).errnoSys(rc, .memfd_create) orelse + .{ .result = bun.toFD(@as(c_int, @intCast(rc))) }; +} + pub fn setPipeCapacityOnLinux(fd: bun.FileDescriptor, capacity: usize) Maybe(usize) { if (comptime !Environment.isLinux) @compileError("Linux-only"); bun.assert(capacity > 0); @@ -2452,7 +2542,8 @@ pub fn directoryExistsAt(dir_: anytype, subpath: anytype) JSC.Maybe(bool) { } const is_dir = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY != 0; + basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY != 0 and + basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0; syslog("NtQueryAttributesFile({}, {}, O_DIRECTORY | O_RDONLY, 0) = {d}", .{ dir_fd, bun.fmt.fmtOSPath(path, .{}), @intFromBool(is_dir) }); return .{ @@ -2518,7 +2609,10 @@ pub fn existsAt(fd: bun.FileDescriptor, subpath: [:0]const u8) bool { } const is_regular_file = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_NORMAL != 0; + // from libuv: directories cannot be read-only + // https://github.com/libuv/libuv/blob/eb5af8e3c0ea19a6b0196d5db3212dae1785739b/src/win/fs.c#L2144-L2146 + (basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY == 0 or + basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0); syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = {d}", .{ bun.fmt.fmtOSPath(path, .{}), @intFromBool(is_regular_file) }); return is_regular_file; @@ -2917,6 +3011,17 @@ pub const File = struct { }; } + pub fn open(path: anytype, flags: bun.Mode, mode: bun.Mode) Maybe(File) { + return File.openat(bun.FD.cwd(), path, flags, mode); + } + + pub fn openatOSPath(other: anytype, path: bun.OSPathSliceZ, flags: bun.Mode, mode: bun.Mode) Maybe(File) { + return switch (This.openatOSPath(bun.toFD(other), path, flags, mode)) { + .result => |fd| .{ .result = .{ .handle = fd } }, + .err => |err| .{ .err = err }, + }; + } + pub fn from(other: anytype) File { const T = @TypeOf(other); @@ -2981,6 +3086,23 @@ pub const File = struct { return .{ .result = {} }; } + pub fn writeFile( + relative_dir_or_cwd: anytype, + path: bun.OSPathSliceZ, + data: []const u8, + ) Maybe(void) { + const file = switch (File.openatOSPath(relative_dir_or_cwd, path, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664)) { + .err => |err| return .{ .err = err }, + .result => |fd| fd, + }; + defer file.close(); + switch (file.writeAll(data)) { + .err => |err| return .{ .err = err }, + .result => {}, + } + return .{ .result = {} }; + } + pub const ReadError = anyerror; pub fn closeAndMoveTo(this: File, src: [:0]const u8, dest: [:0]const u8) !void { @@ -3044,6 +3166,52 @@ pub const File = struct { return fstat(self.handle); } + /// Be careful about using this on Linux or macOS. + /// + /// This calls stat() internally. + pub fn kind(self: File) Maybe(std.fs.File.Kind) { + if (Environment.isWindows) { + const rt = windows.GetFileType(self.handle.cast()); + if (rt == windows.FILE_TYPE_UNKNOWN) { + switch (bun.windows.GetLastError()) { + .SUCCESS => {}, + else => |err| { + return .{ .err = Error.fromCode((bun.C.SystemErrno.init(err) orelse bun.C.SystemErrno.EUNKNOWN).toE(), .fstat) }; + }, + } + } + + return .{ + .result = switch (rt) { + windows.FILE_TYPE_CHAR => .character_device, + windows.FILE_TYPE_REMOTE, windows.FILE_TYPE_DISK => .file, + windows.FILE_TYPE_PIPE => .named_pipe, + windows.FILE_TYPE_UNKNOWN => .unknown, + else => .file, + }, + }; + } + + const st = switch (self.stat()) { + .err => |err| return .{ .err = err }, + .result => |s| s, + }; + + const m = st.mode & posix.S.IFMT; + switch (m) { + posix.S.IFBLK => return .{ .result = .block_device }, + posix.S.IFCHR => return .{ .result = .character_device }, + posix.S.IFDIR => return .{ .result = .directory }, + posix.S.IFIFO => return .{ .result = .named_pipe }, + posix.S.IFLNK => return .{ .result = .sym_link }, + posix.S.IFREG => return .{ .result = .file }, + posix.S.IFSOCK => return .{ .result = .unix_domain_socket }, + else => { + return .{ .result = .file }; + }, + } + } + pub const ReadToEndResult = struct { bytes: std.ArrayList(u8) = std.ArrayList(u8).init(default_allocator), err: ?Error = null, diff --git a/src/sys_uv.zig b/src/sys_uv.zig index 2704584189ddf..68f1c7f20eacc 100644 --- a/src/sys_uv.zig +++ b/src/sys_uv.zig @@ -413,3 +413,5 @@ pub inline fn write(fd: FileDescriptor, buf: []const u8) Maybe(usize) { var bufs: [1]bun.PlatformIOVec = .{bun.platformIOVecCreate(buf)}; return writev(fd, &bufs); } + +pub const Tag = @import("./sys.zig").Tag; diff --git a/src/tagged_pointer.zig b/src/tagged_pointer.zig index fdcfb3d3044a4..ef9122582ff0f 100644 --- a/src/tagged_pointer.zig +++ b/src/tagged_pointer.zig @@ -126,6 +126,10 @@ pub fn TaggedPointerUnion(comptime Types: anytype) type { return null; } + pub fn typeName(this: This) ?[]const u8 { + return @tagName(this.tag()); + } + const This = @This(); pub fn assert_type(comptime Type: type) void { const name = comptime typeBaseName(@typeName(Type)); @@ -133,7 +137,7 @@ pub fn TaggedPointerUnion(comptime Types: anytype) type { @compileError("TaggedPointerUnion does not have " ++ name ++ "."); } } - pub inline fn get(this: This, comptime Type: anytype) ?*Type { + pub inline fn get(this: This, comptime Type: type) ?*Type { comptime assert_type(Type); return if (this.is(Type)) this.as(Type) else null; diff --git a/src/tcc.zig b/src/tcc.zig index 10a69954cff16..a5ed153337446 100644 --- a/src/tcc.zig +++ b/src/tcc.zig @@ -1,27 +1,27 @@ pub const TCCState = opaque {}; -pub const TCCErrorFunc = ?*const fn (?*anyopaque, [*c]const u8) callconv(.C) void; +pub const TCCErrorFunc = ?*const fn (?*anyopaque, [*:0]const u8) callconv(.C) void; pub extern fn tcc_new() ?*TCCState; pub extern fn tcc_delete(s: *TCCState) void; -pub extern fn tcc_set_lib_path(s: *TCCState, path: [*c]const u8) void; +pub extern fn tcc_set_lib_path(s: *TCCState, path: [*:0]const u8) void; pub extern fn tcc_set_error_func(s: *TCCState, error_opaque: ?*anyopaque, error_func: TCCErrorFunc) void; pub extern fn tcc_get_error_func(s: *TCCState) TCCErrorFunc; pub extern fn tcc_get_error_opaque(s: *TCCState) ?*anyopaque; -pub extern fn tcc_set_options(s: *TCCState, str: [*c]const u8) void; -pub extern fn tcc_add_include_path(s: *TCCState, pathname: [*c]const u8) c_int; -pub extern fn tcc_add_sysinclude_path(s: *TCCState, pathname: [*c]const u8) c_int; -pub extern fn tcc_define_symbol(s: *TCCState, sym: [*c]const u8, value: [*c]const u8) void; -pub extern fn tcc_undefine_symbol(s: *TCCState, sym: [*c]const u8) void; -pub extern fn tcc_add_file(s: *TCCState, filename: [*c]const u8) c_int; -pub extern fn tcc_compile_string(s: *TCCState, buf: [*c]const u8) c_int; +pub extern fn tcc_set_options(s: *TCCState, str: [*:0]const u8) void; +pub extern fn tcc_add_include_path(s: *TCCState, pathname: [*:0]const u8) c_int; +pub extern fn tcc_add_sysinclude_path(s: *TCCState, pathname: [*:0]const u8) c_int; +pub extern fn tcc_define_symbol(s: *TCCState, sym: [*:0]const u8, value: [*:0]const u8) void; +pub extern fn tcc_undefine_symbol(s: *TCCState, sym: [*:0]const u8) void; +pub extern fn tcc_add_file(s: *TCCState, filename: [*:0]const u8) c_int; +pub extern fn tcc_compile_string(s: *TCCState, buf: [*:0]const u8) c_int; pub extern fn tcc_set_output_type(s: *TCCState, output_type: c_int) c_int; -pub extern fn tcc_add_library_path(s: *TCCState, pathname: [*c]const u8) c_int; -pub extern fn tcc_add_library(s: *TCCState, libraryname: [*c]const u8) c_int; +pub extern fn tcc_add_library_path(s: *TCCState, pathname: [*:0]const u8) c_int; +pub extern fn tcc_add_library(s: *TCCState, libraryname: [*:0]const u8) c_int; pub extern fn tcc_add_symbol(s: *TCCState, name: [*:0]const u8, val: *const anyopaque) c_int; -pub extern fn tcc_output_file(s: *TCCState, filename: [*c]const u8) c_int; +pub extern fn tcc_output_file(s: *TCCState, filename: [*:0]const u8) c_int; pub extern fn tcc_run(s: *TCCState, argc: c_int, argv: [*c][*c]u8) c_int; pub extern fn tcc_relocate(s1: *TCCState, ptr: ?*anyopaque) c_int; -pub extern fn tcc_get_symbol(s: *TCCState, name: [*c]const u8) ?*anyopaque; -pub extern fn tcc_list_symbols(s: *TCCState, ctx: ?*anyopaque, symbol_cb: ?*const fn (?*anyopaque, [*c]const u8, ?*const anyopaque) callconv(.C) void) void; +pub extern fn tcc_get_symbol(s: *TCCState, name: [*:0]const u8) ?*anyopaque; +pub extern fn tcc_list_symbols(s: *TCCState, ctx: ?*anyopaque, symbol_cb: ?*const fn (?*anyopaque, [*:0]const u8, ?*const anyopaque) callconv(.C) void) void; pub const TCC_OUTPUT_MEMORY = @as(c_int, 1); pub const TCC_OUTPUT_EXE = @as(c_int, 2); pub const TCC_OUTPUT_DLL = @as(c_int, 3); diff --git a/src/thread_pool.zig b/src/thread_pool.zig index 8d5f859d44538..e7e8b8d1071fe 100644 --- a/src/thread_pool.zig +++ b/src/thread_pool.zig @@ -134,14 +134,10 @@ pub const Batch = struct { pub const WaitGroup = struct { mutex: std.Thread.Mutex = .{}, counter: u32 = 0, - event: std.Thread.ResetEvent, + event: std.Thread.ResetEvent = .{}, pub fn init(self: *WaitGroup) void { - self.* = .{ - .mutex = .{}, - .counter = 0, - .event = undefined, - }; + self.* = .{}; } pub fn deinit(self: *WaitGroup) void { diff --git a/src/url.zig b/src/url.zig index 30513299e729d..38e3b6aab1f47 100644 --- a/src/url.zig +++ b/src/url.zig @@ -130,7 +130,7 @@ pub const URL = struct { } pub fn hasValidPort(this: *const URL) bool { - return (this.getPort() orelse 0) > 1; + return (this.getPort() orelse 0) > 0; } pub fn isEmpty(this: *const URL) bool { @@ -980,7 +980,7 @@ pub const FormData = struct { pub fn jsFunctionFromMultipartData( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { + ) callconv(JSC.conv) JSC.JSValue { JSC.markBinding(@src()); const args_ = callframe.arguments(2); diff --git a/src/util.zig b/src/util.zig index 30a42a3b3f7ce..fbf2e5838d5cf 100644 --- a/src/util.zig +++ b/src/util.zig @@ -256,22 +256,22 @@ pub fn Batcher(comptime Type: type) type { return @This(){ .head = all }; } - pub inline fn done(this: *@This()) void { - bun.assert(this.head.len == 0); + pub fn done(this: *@This()) void { + bun.assert(this.head.len == 0); // count to init() was too large, overallocation } - pub inline fn eat(this: *@This(), value: Type) *Type { + pub fn eat(this: *@This(), value: Type) *Type { return @as(*Type, @ptrCast(&this.head.eat1(value).ptr)); } - pub inline fn eat1(this: *@This(), value: Type) []Type { + pub fn eat1(this: *@This(), value: Type) []Type { var prev = this.head[0..1]; prev[0] = value; this.head = this.head[1..]; return prev; } - pub inline fn next(this: *@This(), values: anytype) []Type { + pub fn next(this: *@This(), values: anytype) []Type { this.head[0..values.len].* = values; const prev = this.head[0..values.len]; this.head = this.head[values.len..]; diff --git a/src/watcher.zig b/src/watcher.zig index a3c2cd6034fc4..b8bfbc2574d19 100644 --- a/src/watcher.zig +++ b/src/watcher.zig @@ -236,7 +236,7 @@ const DarwinWatcher = struct { }; const WindowsWatcher = struct { - mutex: Mutex = Mutex.init(), + mutex: Mutex = .{}, iocp: w.HANDLE = undefined, watcher: DirWatcher = undefined, @@ -535,6 +535,8 @@ pub fn getHash(filepath: string) HashType { return @as(HashType, @truncate(bun.hash(filepath))); } +// TODO: this should not be a function with a generic context. every function +// besides `watchLoop` does not refer to context. pub fn NewWatcher(comptime ContextType: type) type { return struct { const Watcher = @This(); @@ -573,7 +575,7 @@ pub fn NewWatcher(comptime ContextType: type) type { .watched_count = 0, .ctx = ctx, .watchlist = WatchList{}, - .mutex = Mutex.init(), + .mutex = .{}, .cwd = fs.top_level_dir, }; @@ -595,7 +597,7 @@ pub fn NewWatcher(comptime ContextType: type) type { this.running = false; } else { // if the mutex is locked, then that's now a UAF. - this.mutex.assertUnlocked("Internal consistency error: watcher mutex is locked when it should not be."); + this.mutex.releaseAssertUnlocked("Watcher mutex is locked when it should not be."); if (close_descriptors and this.running) { const fds = this.watchlist.items(.fd); @@ -700,7 +702,7 @@ pub fn NewWatcher(comptime ContextType: type) type { null, ); - // Give the events more time to coallesce + // Give the events more time to coalesce if (count_ < 128 / 2) { const remain = 128 - count_; var timespec = std.posix.timespec{ .tv_sec = 0, .tv_nsec = 100_000 }; @@ -1276,5 +1278,9 @@ pub fn NewWatcher(comptime ContextType: type) type { } } } + + pub fn getResolveWatcher(watcher: *Watcher) bun.resolver.AnyResolveWatcher { + return bun.resolver.ResolveWatcher(*@This(), @typeInfo(ContextType).Pointer.child.onMaybeWatchDirectory).init(watcher); + } }; } diff --git a/src/windows.zig b/src/windows.zig index de41aca60e4eb..08573d4b30f80 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -69,6 +69,7 @@ pub const advapi32 = windows.advapi32; pub const INVALID_FILE_ATTRIBUTES: u32 = std.math.maxInt(u32); pub const nt_object_prefix = [4]u16{ '\\', '?', '?', '\\' }; +pub const nt_unc_object_prefix = [8]u16{ '\\', '?', '?', '\\', 'U', 'N', 'C', '\\' }; pub const nt_maxpath_prefix = [4]u16{ '\\', '\\', '?', '\\' }; const std = @import("std"); @@ -97,9 +98,18 @@ pub extern "kernel32" fn CommandLineToArgvW( pNumArgs: *c_int, ) callconv(windows.WINAPI) ?[*]win32.LPWSTR; -pub extern fn GetFileType( - hFile: win32.HANDLE, -) callconv(windows.WINAPI) win32.DWORD; +pub fn GetFileType(hFile: win32.HANDLE) win32.DWORD { + const function = struct { + pub extern fn GetFileType( + hFile: win32.HANDLE, + ) callconv(windows.WINAPI) win32.DWORD; + }.GetFileType; + + const rc = function(hFile); + if (comptime Environment.enable_logs) + bun.sys.syslog("GetFileType({}) = {d}", .{ bun.toFD(hFile), rc }); + return rc; +} /// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfiletype#return-value pub const FILE_TYPE_UNKNOWN = 0x0000; @@ -3047,6 +3057,7 @@ pub fn translateNTStatusToErrno(err: win32.NTSTATUS) bun.C.E { .DIRECTORY_NOT_EMPTY => .NOTEMPTY, .FILE_TOO_LARGE => .@"2BIG", .NOT_SAME_DEVICE => .XDEV, + .DELETE_PENDING => .BUSY, .SHARING_VIOLATION => if (comptime Environment.isDebug) brk: { bun.Output.debugWarn("Received SHARING_VIOLATION, indicates file handle should've been opened with FILE_SHARE_DELETE", .{}); break :brk .BUSY; @@ -3058,9 +3069,9 @@ pub fn translateNTStatusToErrno(err: win32.NTSTATUS) bun.C.E { } else .INVAL, else => |t| { - // if (bun.Environment.isDebug) { - bun.Output.warn("Called translateNTStatusToErrno with {s} which does not have a mapping to errno.", .{@tagName(t)}); - // } + if (bun.Environment.isDebug) { + bun.Output.warn("Called translateNTStatusToErrno with {s} which does not have a mapping to errno.", .{@tagName(t)}); + } return .UNKNOWN; }, }; @@ -3396,8 +3407,31 @@ pub fn GetFinalPathNameByHandle( fmt: std.os.windows.GetFinalPathNameByHandleFormat, out_buffer: []u16, ) std.os.windows.GetFinalPathNameByHandleError![]u16 { - bun.sys.syslog("GetFinalPathNameByHandle({*p})", .{hFile}); - return std.os.windows.GetFinalPathNameByHandle(hFile, fmt, out_buffer); + const return_length = bun.windows.GetFinalPathNameByHandleW(hFile, out_buffer.ptr, @truncate(out_buffer.len), switch (fmt.volume_name) { + .Dos => win32.FILE_NAME_NORMALIZED | win32.VOLUME_NAME_DOS, + .Nt => win32.FILE_NAME_NORMALIZED | win32.VOLUME_NAME_NT, + }); + + if (return_length == 0) { + bun.sys.syslog("GetFinalPathNameByHandleW({*p}) = {}", .{ hFile, bun.windows.GetLastError() }); + return error.FileNotFound; + } + + var ret = out_buffer[0..@intCast(return_length)]; + + bun.sys.syslog("GetFinalPathNameByHandleW({*p}) = {}", .{ hFile, bun.fmt.utf16(ret) }); + + if (bun.strings.hasPrefixComptimeType(u16, ret, nt_maxpath_prefix)) { + // '\\?\C:\absolute\path' -> 'C:\absolute\path' + ret = ret[4..]; + if (bun.strings.hasPrefixComptimeUTF16(ret, "UNC\\")) { + // '\\?\UNC\absolute\path' -> '\\absolute\path' + ret[2] = '\\'; + ret = ret[2..]; + } + } + + return ret; } extern "kernel32" fn GetModuleHandleExW( diff --git a/src/windows_c.zig b/src/windows_c.zig index d465ac0ecf155..9bc134f0915f0 100644 --- a/src/windows_c.zig +++ b/src/windows_c.zig @@ -814,11 +814,11 @@ pub const SystemErrno = enum(u16) { return @as(SystemErrno, @enumFromInt(code)); } - pub fn label(this: SystemErrno) ?[]const u8 { + pub fn label(this: SystemErrno) ?[:0]const u8 { return labels.get(this) orelse null; } - const LabelMap = std.enums.EnumMap(SystemErrno, []const u8); + const LabelMap = std.enums.EnumMap(SystemErrno, [:0]const u8); pub const labels: LabelMap = brk: { var map: LabelMap = LabelMap.initFull(""); @@ -958,6 +958,75 @@ pub const SystemErrno = enum(u16) { }; }; +pub const UV_E2BIG = -uv.UV_E2BIG; +pub const UV_EACCES = -uv.UV_EACCES; +pub const UV_EADDRINUSE = -uv.UV_EADDRINUSE; +pub const UV_EADDRNOTAVAIL = -uv.UV_EADDRNOTAVAIL; +pub const UV_EAFNOSUPPORT = -uv.UV_EAFNOSUPPORT; +pub const UV_EAGAIN = -uv.UV_EAGAIN; +pub const UV_EALREADY = -uv.UV_EALREADY; +pub const UV_EBADF = -uv.UV_EBADF; +pub const UV_EBUSY = -uv.UV_EBUSY; +pub const UV_ECANCELED = -uv.UV_ECANCELED; +pub const UV_ECHARSET = -uv.UV_ECHARSET; +pub const UV_ECONNABORTED = -uv.UV_ECONNABORTED; +pub const UV_ECONNREFUSED = -uv.UV_ECONNREFUSED; +pub const UV_ECONNRESET = -uv.UV_ECONNRESET; +pub const UV_EDESTADDRREQ = -uv.UV_EDESTADDRREQ; +pub const UV_EEXIST = -uv.UV_EEXIST; +pub const UV_EFAULT = -uv.UV_EFAULT; +pub const UV_EHOSTUNREACH = -uv.UV_EHOSTUNREACH; +pub const UV_EINTR = -uv.UV_EINTR; +pub const UV_EINVAL = -uv.UV_EINVAL; +pub const UV_EIO = -uv.UV_EIO; +pub const UV_EISCONN = -uv.UV_EISCONN; +pub const UV_EISDIR = -uv.UV_EISDIR; +pub const UV_ELOOP = -uv.UV_ELOOP; +pub const UV_EMFILE = -uv.UV_EMFILE; +pub const UV_EMSGSIZE = -uv.UV_EMSGSIZE; +pub const UV_ENAMETOOLONG = -uv.UV_ENAMETOOLONG; +pub const UV_ENETDOWN = -uv.UV_ENETDOWN; +pub const UV_ENETUNREACH = -uv.UV_ENETUNREACH; +pub const UV_ENFILE = -uv.UV_ENFILE; +pub const UV_ENOBUFS = -uv.UV_ENOBUFS; +pub const UV_ENODEV = -uv.UV_ENODEV; +pub const UV_ENOENT = -uv.UV_ENOENT; +pub const UV_ENOMEM = -uv.UV_ENOMEM; +pub const UV_ENONET = -uv.UV_ENONET; +pub const UV_ENOSPC = -uv.UV_ENOSPC; +pub const UV_ENOSYS = -uv.UV_ENOSYS; +pub const UV_ENOTCONN = -uv.UV_ENOTCONN; +pub const UV_ENOTDIR = -uv.UV_ENOTDIR; +pub const UV_ENOTEMPTY = -uv.UV_ENOTEMPTY; +pub const UV_ENOTSOCK = -uv.UV_ENOTSOCK; +pub const UV_ENOTSUP = -uv.UV_ENOTSUP; +pub const UV_EPERM = -uv.UV_EPERM; +pub const UV_EPIPE = -uv.UV_EPIPE; +pub const UV_EPROTO = -uv.UV_EPROTO; +pub const UV_EPROTONOSUPPORT = -uv.UV_EPROTONOSUPPORT; +pub const UV_EPROTOTYPE = -uv.UV_EPROTOTYPE; +pub const UV_EROFS = -uv.UV_EROFS; +pub const UV_ESHUTDOWN = -uv.UV_ESHUTDOWN; +pub const UV_ESPIPE = -uv.UV_ESPIPE; +pub const UV_ESRCH = -uv.UV_ESRCH; +pub const UV_ETIMEDOUT = -uv.UV_ETIMEDOUT; +pub const UV_ETXTBSY = -uv.UV_ETXTBSY; +pub const UV_EXDEV = -uv.UV_EXDEV; +pub const UV_EFBIG = -uv.UV_EFBIG; +pub const UV_ENOPROTOOPT = -uv.UV_ENOPROTOOPT; +pub const UV_ERANGE = -uv.UV_ERANGE; +pub const UV_ENXIO = -uv.UV_ENXIO; +pub const UV_EMLINK = -uv.UV_EMLINK; +pub const UV_EHOSTDOWN = -uv.UV_EHOSTDOWN; +pub const UV_EREMOTEIO = -uv.UV_EREMOTEIO; +pub const UV_ENOTTY = -uv.UV_ENOTTY; +pub const UV_EFTYPE = -uv.UV_EFTYPE; +pub const UV_EILSEQ = -uv.UV_EILSEQ; +pub const UV_EOVERFLOW = -uv.UV_EOVERFLOW; +pub const UV_ESOCKTNOSUPPORT = -uv.UV_ESOCKTNOSUPPORT; +pub const UV_ENODATA = -uv.UV_ENODATA; +pub const UV_EUNATCH = -uv.UV_EUNATCH; + pub const off_t = i64; pub fn preallocate_file(_: posix.fd_t, _: off_t, _: off_t) !void {} @@ -1100,7 +1169,7 @@ pub const E = enum(u16) { HWPOISON = 133, UNKNOWN = 134, CHARSET = 135, - OF = 136, + EOF = 136, UV_E2BIG = -uv.UV_E2BIG, UV_EACCES = -uv.UV_EACCES, diff --git a/src/work_pool.zig b/src/work_pool.zig index 5cbad488ff22c..b9e1bd157315b 100644 --- a/src/work_pool.zig +++ b/src/work_pool.zig @@ -14,13 +14,23 @@ pub fn NewWorkPool(comptime max_threads: ?usize) type { @setCold(true); pool = ThreadPool.init(.{ - .max_threads = max_threads orelse @max(@as(u32, @truncate(std.Thread.getCpuCount() catch 0)), 2), + .max_threads = max_threads orelse @max(2, max_threads: { + if (bun.getenvZ("GOMAXPROCS")) |max_procs| try_override: { + break :max_threads std.fmt.parseInt(u32, max_procs, 10) catch + break :try_override; + } + + break :max_threads @as(u32, @truncate(std.Thread.getCpuCount() catch 0)); + }), .stack_size = ThreadPool.default_thread_stack_size, }); return &pool; } + + /// Initialization of WorkPool is not thread-safe, as it is + /// assumed a single main thread sets everything up. Calling + /// this afterwards is thread-safe. pub inline fn get() *ThreadPool { - // lil racy if (loaded) return &pool; loaded = true; diff --git a/src/wyhash.zig b/src/wyhash.zig index 1d3a1a0ad8f80..b4b72410aaf37 100644 --- a/src/wyhash.zig +++ b/src/wyhash.zig @@ -1,7 +1,7 @@ // // this file is a copy of Wyhash from the zig standard library, version v0.11.0-dev.2609+5e19250a1 // -const assert = if (@hasDecl(@import("root"), "bun")) @import("root").bun.assert else @import("std").debug.assert; +const assert = if (@hasDecl(@import("root"), "bun")) (@import("root").bun).assert else @import("std").debug.assert; const std = @import("std"); const mem = std.mem; diff --git a/src/zlib.zig b/src/zlib.zig index 05a42be769e46..b141b9137b961 100644 --- a/src/zlib.zig +++ b/src/zlib.zig @@ -5,9 +5,10 @@ const bun = @import("root").bun; const mimalloc = @import("./allocators/mimalloc.zig"); +pub const MIN_WBITS = 8; pub const MAX_WBITS = 15; -pub extern fn zlibVersion() [*c]const u8; +pub extern fn zlibVersion() [*:0]const u8; pub extern fn compress(dest: [*]Bytef, destLen: *uLongf, source: [*]const Bytef, sourceLen: uLong) c_int; pub extern fn compress2(dest: [*]Bytef, destLen: *uLongf, source: [*]const Bytef, sourceLen: uLong, level: c_int) c_int; @@ -37,9 +38,10 @@ const z_crc_t = c_uint; // typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); // typedef void (*free_func) OF((voidpf opaque, voidpf address)); +const internal = @import("zlib-internal"); const zStream_struct = @import("zlib-internal").zStream_struct; -const z_stream = @import("zlib-internal").z_stream; -const z_streamp = @import("zlib-internal").z_streamp; +pub const z_stream = @import("zlib-internal").z_stream; +pub const z_streamp = @import("zlib-internal").z_streamp; // typedef struct z_stream_s { // z_const Bytef *next_in; /* next input byte */ @@ -75,6 +77,24 @@ pub const ReturnCode = @import("zlib-internal").ReturnCode; pub extern fn inflateInit_(strm: z_streamp, version: [*c]const u8, stream_size: c_int) ReturnCode; pub extern fn inflateInit2_(strm: z_streamp, window_size: c_int, version: [*c]const u8, stream_size: c_int) ReturnCode; +/// Initializes the compression dictionary from the given byte sequence without producing any compressed output. This function must be called immediately after deflateInit, deflateInit2 or deflateReset, before any call of deflate. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). without producing any compressed output. When using the zlib format, this function must be called immediately after deflateInit, deflateInit2 or deflateReset, and before any call of deflate. When doing raw deflate, this function must be called either before any call of deflate, or immediately after the completion of a deflate block, i.e. after all input has been consumed and all output has been delivered when using any of the flush options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). +/// The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. +/// +/// Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. +/// +/// Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the Adler-32 value is not computed and strm->adler is not set. +/// +/// deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (such as NULL dictionary) or the stream state is inconsistent (for example if deflate has already been called for this stream or if not at a block boundary for raw deflate). deflateSetDictionary does not perform any compression: this will be done by deflate(). +pub extern fn deflateSetDictionary(strm: z_streamp, dictionary: ?[*]const u8, length: c_uint) ReturnCode; + +/// Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the strategy is changed, and if there have been any deflate() calls since the state was initialized or reset, then the input available so far is compressed with the old level and strategy using deflate(strm, Z_BLOCK). There are three approaches for the compression levels 0, 1..3, and 4..9 respectively. The new level and strategy will take effect at the next call of deflate(). +/// If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not take effect. In this case, deflateParams() can be called again with the same parameters and more output space to try again. +/// +/// In order to assure a change in the parameters on the first try, the deflate stream should be flushed using deflate() with Z_BLOCK or other flush request until strm.avail_out is not zero, before calling deflateParams(). Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be applied to the data compressed after deflateParams(). +/// +/// deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if there was not enough output space to complete the compression of the available input data before a change in the strategy or approach. Note that in the case of a Z_BUF_ERROR, the parameters are not changed. A return value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be retried with more output space. +pub extern fn deflateParams(strm: z_streamp, level: c_int, strategy: c_int) ReturnCode; + /// inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. /// The detailed semantics are as follows. inflate performs one or both of the following actions: /// @@ -98,16 +118,15 @@ pub extern fn inflateInit2_(strm: z_streamp, window_size: c_int, version: [*c]co /// inflate() will decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output produced so far. The CRC-32 is checked against the gzip trailer, as is the uncompressed length, modulo 2^32. /// /// inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value, in which case strm->msg points to a string with a more specific error), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL, or the state was inadvertently written over by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is to be attempted. -extern fn inflate(stream: [*c]zStream_struct, flush: FlushValue) ReturnCode; +pub extern fn inflate(stream: *zStream_struct, flush: FlushValue) ReturnCode; /// inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state was inconsistent. -const InflateEndResult = enum(c_int) { - Ok = 0, - StreamEnd = 1, -}; - /// All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. -extern fn inflateEnd(stream: [*c]zStream_struct) InflateEndResult; +pub extern fn inflateEnd(stream: *zStream_struct) ReturnCode; + +pub extern fn inflateReset(stream: *zStream_struct) ReturnCode; + +pub extern fn crc32(crc: uLong, buf: [*]const Bytef, len: uInt) uLong; pub fn NewZlibReader(comptime Writer: type, comptime buffer_size: usize) type { return struct { @@ -290,17 +309,17 @@ pub const ZlibError = error{ const ZlibAllocator = struct { pub fn alloc(_: *anyopaque, items: uInt, len: uInt) callconv(.C) *anyopaque { - if (comptime bun.is_heap_breakdown_enabled) { - const zone = bun.HeapBreakdown.malloc_zone_t.get(ZlibAllocator); - return zone.malloc_zone_calloc(items, len).?; + if (bun.heap_breakdown.enabled) { + const zone = bun.heap_breakdown.getZone("zlib"); + return zone.malloc_zone_calloc(items, len) orelse bun.outOfMemory(); } - return mimalloc.mi_calloc(items, len) orelse unreachable; + return mimalloc.mi_calloc(items, len) orelse bun.outOfMemory(); } pub fn free(_: *anyopaque, data: *anyopaque) callconv(.C) void { - if (comptime bun.is_heap_breakdown_enabled) { - const zone = bun.HeapBreakdown.malloc_zone_t.get(ZlibAllocator); + if (bun.heap_breakdown.enabled) { + const zone = bun.heap_breakdown.getZone("zlib"); zone.malloc_zone_free(data); return; } @@ -520,7 +539,7 @@ pub const Options = struct { /// with the version assumed by the caller (ZLIB_VERSION). msg is set to null /// if there is no error message. deflateInit does not perform any compression: /// this will be done by deflate(). -extern fn deflateInit_(strm: z_stream, level: c_int, stream_size: c_int) c_int; +pub extern fn deflateInit_(strm: z_streamp, level: c_int, version: [*:0]const u8, stream_size: c_int) ReturnCode; /// /// deflate compresses as much data as possible, and stops when the input @@ -626,7 +645,7 @@ extern fn deflateInit_(strm: z_stream, level: c_int, stream_size: c_int) c_int; /// fatal, and deflate() can be called again with more input and more output /// space to continue compressing. /// -extern fn deflate(strm: z_streamp, flush: FlushValue) ReturnCode; +pub extern fn deflate(strm: z_streamp, flush: FlushValue) ReturnCode; /// /// All dynamically allocated data structures for this stream are freed. @@ -638,7 +657,9 @@ extern fn deflate(strm: z_streamp, flush: FlushValue) ReturnCode; /// prematurely (some input or output was discarded). In the error case, msg /// may be set but then points to a static string (which must not be /// deallocated). -extern fn deflateEnd(stream: z_streamp) ReturnCode; +pub extern fn deflateEnd(stream: z_streamp) ReturnCode; + +pub extern fn deflateReset(stream: z_streamp) c_int; // deflateBound() returns an upper bound on the compressed size after // deflation of sourceLen bytes. It must be called after deflateInit() or @@ -650,7 +671,7 @@ extern fn deflateEnd(stream: z_streamp) ReturnCode; // to return Z_STREAM_END. Note that it is possible for the compressed size to // be larger than the value returned by deflateBound() if flush options other // than Z_FINISH or Z_NO_FLUSH are used. -extern fn deflateBound(strm: z_streamp, sourceLen: u64) u64; +pub extern fn deflateBound(strm: z_streamp, sourceLen: u64) u64; /// /// This is another version of deflateInit with more compression options. The @@ -704,7 +725,25 @@ extern fn deflateBound(strm: z_streamp, sourceLen: u64) u64; /// incompatible with the version assumed by the caller (ZLIB_VERSION). msg is /// set to null if there is no error message. deflateInit2 does not perform any /// compression: this will be done by deflate(). -extern fn deflateInit2_(strm: z_streamp, level: c_int, method: c_int, windowBits: c_int, memLevel: c_int, strategy: c_int, version: [*c]const u8, stream_size: c_int) ReturnCode; +pub extern fn deflateInit2_(strm: z_streamp, level: c_int, method: c_int, windowBits: c_int, memLevel: c_int, strategy: c_int, version: [*c]const u8, stream_size: c_int) ReturnCode; + +/// Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the window and there is already data in the window, then the provided dictionary will amend what's there. The application must insure that the dictionary that was used for compression is provided. +/// +/// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (such as NULL dictionary) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). +pub extern fn inflateSetDictionary(strm: z_streamp, dictionary: ?[*]const u8, length: c_uint) ReturnCode; + +pub const NodeMode = enum(u8) { + NONE = 0, + DEFLATE = 1, + INFLATE = 2, + GZIP = 3, + GUNZIP = 4, + DEFLATERAW = 5, + INFLATERAW = 6, + UNZIP = 7, + BROTLI_DECODE = 8, + BROTLI_ENCODE = 9, +}; /// Not for streaming! pub const ZlibCompressorArrayList = struct { @@ -901,3 +940,280 @@ pub const ZlibCompressorArrayList = struct { } } }; + +const CHUNK = 1024 * 64; + +pub const ZlibCompressorStreaming = struct { + mode: NodeMode, + state: z_stream = std.mem.zeroes(z_stream), + chunkSize: c_uint, + flush: FlushValue = .NoFlush, + finishFlush: FlushValue = .Finish, + fullFlush: FlushValue = .FullFlush, + level: c_int, + windowBits: c_int, + memLevel: c_int, + strategy: c_int, + dictionary: []const u8, + err: ReturnCode = .Ok, + err_msg: ?[*:0]const u8 = null, + + pub fn init(this: *ZlibCompressorStreaming) !void { + const ret_code = deflateInit2_(&this.state, this.level, 8, this.windowBits, this.memLevel, this.strategy, zlibVersion(), @sizeOf(z_stream)); + if (ret_code != .Ok) return error.ZlibError9; + + this.setDictionary() catch {}; + this.err_msg = null; + return; + } + + fn setDictionary(this: *ZlibCompressorStreaming) !void { + switch (this.mode) { + .DEFLATE, .DEFLATERAW => { + this.err = deflateSetDictionary(&this.state, this.dictionary.ptr, @intCast(this.dictionary.len)); + }, + .INFLATERAW => { + this.err = inflateSetDictionary(&this.state, this.dictionary.ptr, @intCast(this.dictionary.len)); + }, + else => {}, + } + if (this.err != .Ok) { + return error.ZlibFailedSetDictionary; + } + } + + pub fn params(this: *ZlibCompressorStreaming, level: c_int, strategy: c_int) void { + const err = deflateParams(&this.state, level, strategy); + bun.debugAssert(err == .Ok); + this.level = level; + this.strategy = strategy; + } + + pub fn write(this: *ZlibCompressorStreaming, bytes: []const u8, output: *std.ArrayListUnmanaged(u8), process_all_input: bool) !void { + const state = &this.state; + state.next_in = bytes.ptr; + state.avail_in = @intCast(bytes.len); + if (state.avail_in == 0) state.next_in = null; + + if (!process_all_input) return; + while (true) { + if (try this.doWork(output, this.flush)) { + break; + } + } + // bun.assert(state.avail_in == 0); + } + + pub fn doWork(this: *ZlibCompressorStreaming, output: *std.ArrayListUnmanaged(u8), flush: FlushValue) !bool { + const state = &this.state; + var out: [CHUNK]u8 = undefined; + state.avail_out = CHUNK; + state.next_out = &out; + + this.err = deflate(state, flush); + bun.assert(this.err != .StreamError); + const have = CHUNK - state.avail_out; + try output.appendSlice(bun.default_allocator, out[0..have]); + if (state.avail_out == 0) return false; + return true; + } + + pub fn end(this: *ZlibCompressorStreaming, output: *std.ArrayListUnmanaged(u8)) !void { + const state = &this.state; + state.next_in = null; + state.avail_in = 0; + + const done = try this.doWork(output, this.finishFlush); + bun.assert(done); + // bun.assert(state.avail_in == 0); + + const ret = deflateEnd(&this.state); + bun.assert(ret == .Ok or ret == .StreamEnd); + if (this.err != .StreamEnd and this.finishFlush == .Finish) return error.ZlibError10; + } +}; + +pub const ZlibDecompressorStreaming = struct { + mode: NodeMode, + state: z_stream = std.mem.zeroes(z_stream), + chunkSize: c_uint, + next_expected_header_byte: ?[*]const u8 = null, + gzip_id_bytes_read: u16 = 0, + flush: FlushValue = .NoFlush, + finishFlush: FlushValue = .Finish, + fullFlush: FlushValue = .FullFlush, + windowBits: c_int, + dictionary: []const u8, + err: ReturnCode = .Ok, + err_msg: ?[*:0]const u8 = null, + do_inflate_loop: bool = true, + + pub fn init(this: *ZlibDecompressorStreaming) !void { + const ret_code = inflateInit2_(&this.state, this.windowBits, zlibVersion(), @sizeOf(z_stream)); + if (ret_code != .Ok) return error.ZlibError1; + + this.setDictionary() catch {}; + this.err_msg = null; + return; + } + + fn setDictionary(this: *ZlibDecompressorStreaming) !void { + this.err = .Ok; + const dictionary = this.dictionary; + switch (this.mode) { + .DEFLATE, .DEFLATERAW => { + if (dictionary.len > 0) this.err = deflateSetDictionary(&this.state, dictionary.ptr, @intCast(dictionary.len)); + }, + .INFLATERAW => { + if (dictionary.len > 0) this.err = inflateSetDictionary(&this.state, dictionary.ptr, @intCast(dictionary.len)); + }, + else => {}, + } + if (this.err != .Ok) { + return this.error_for_message("Failed to set dictionary"); + } + } + + fn error_for_message(this: *ZlibDecompressorStreaming, default: [*:0]const u8) error{ZlibError} { + var message = default; + if (this.state.err_msg) |msg| message = msg; + this.err_msg = message; + return error.ZlibError; + } + + pub fn writeAll(this: *ZlibDecompressorStreaming, bytes: []const u8, output: *std.ArrayListUnmanaged(u8), process_all_input: bool) !void { + var index: usize = 0; + while (index != bytes.len) { + index += try this.write(bytes[index..], output, process_all_input); + } + } + + fn write(this: *ZlibDecompressorStreaming, bytes: []const u8, output: *std.ArrayListUnmanaged(u8), process_all_input: bool) !usize { + const state = &this.state; + state.next_in = bytes.ptr; + state.avail_in = @intCast(bytes.len); + if (state.avail_in == 0) state.next_in = null; + + // UNZIP mode allows the input to be either gzip or deflate data and we do a two-byte header detection in order to disambiguate. + // the ordering of this logic is a bit abstract because we dont know ahead of time how large 'bytes' will be. + // additionally, if the first byte is "correct" but the second is not, we don't want to lose it from it being consumed. + // Ref: https://github.com/nodejs/node/blob/v22.8.0/src/node_zlib.cc#L777 + if (this.mode == .UNZIP) { + var redd: usize = 0; + this.do_inflate_loop = false; + + if (bytes.len > 0) { + this.next_expected_header_byte = state.next_in; + } + if (this.gzip_id_bytes_read == 0) { + if (this.next_expected_header_byte == null) { + return 0; + } + + if (this.next_expected_header_byte.?[redd] == GZIP_HEADER_ID1) { + this.gzip_id_bytes_read += 1; + redd += 1; + // next_expected_header_byte++; + + if (bytes.len == 1) { + // The only available byte was already read. + return 1; + } + } else { + // the stream did not match the gzip header, bail. + this.mode = .INFLATE; + return 0; + } + } + if (this.gzip_id_bytes_read == 1) { + if (this.next_expected_header_byte == null) { + return 0; + } + + if (this.next_expected_header_byte.?[redd] == GZIP_HEADER_ID2) { + this.gzip_id_bytes_read += 1; + redd += 1; + // next_expected_header_byte++; + + // the gzip header was found. send the header to inflate() and tell writeAll how much we read to do this detection + // if we continued to doWork right now GZIP_HEADER_ID2 might get processed twice. + this.mode = .GUNZIP; + { + const header = &[_]u8{ GZIP_HEADER_ID1, GZIP_HEADER_ID2 }; + state.next_in = header.ptr; + state.avail_in = @intCast(header.len); + var out: [1]u8 = .{0}; + state.avail_out = 0; + state.next_out = &out; // passing a null pointer here causes it to return Z_STREAM_ERROR so we send zero-length instead. + const ret = inflate(state, this.flush); + bun.assert(ret == .Ok); + } + } else { + // There is no actual difference between INFLATE and INFLATERAW (after initialization). + // the stream only partially matched the gzip header, bail. + this.mode = .INFLATE; + } + return redd; + } + + bun.assert(false); // invalid number of gzip magic number bytes read + } + + // we're passed the header or there was no header. it is now safe to send everying to inflate(). + this.do_inflate_loop = true; + if (!process_all_input) return bytes.len; + while (true) { + if (try this.doWork(output, this.flush)) { + break; + } + } + // bun.assert(state.avail_in == 0); + + return bytes.len; + } + + pub fn doWork(this: *ZlibDecompressorStreaming, output: *std.ArrayListUnmanaged(u8), flush: FlushValue) !bool { + const state = &this.state; + var out: [CHUNK]u8 = undefined; + const len = @min(CHUNK, this.chunkSize); + state.avail_out = len; + state.next_out = &out; + + this.err = inflate(state, flush); + const ret = this.err; + bun.assert(ret != .StreamError); + if (ret == .NeedDict) return this.error_for_message((if (this.dictionary.len == 0) "Missing dictionary" else "Bad dictionary").ptr); + if (ret == .DataError) return this.error_for_message("Zlib error"); + if (ret == .MemError) return error.ZlibError4; + const have = len - state.avail_out; + try output.appendSlice(bun.default_allocator, out[0..have]); + if (ret == .StreamEnd and this.mode == .GUNZIP and state.avail_in > 0 and state.next_in.?[0] != 0) { + _ = inflateReset(state); + return false; + } + if (ret == .StreamEnd) return true; + if (state.avail_out == 0) return false; + if (ret == .BufError and flush == .Finish) return this.error_for_message("unexpected end of file"); + return true; + } + + pub fn end(this: *ZlibDecompressorStreaming, output: *std.ArrayListUnmanaged(u8)) !void { + const state = &this.state; + state.next_in = null; + state.avail_in = 0; + + const done = try this.doWork(output, this.finishFlush); + bun.assert(done); + // bun.assert(state.avail_in == 0); + + const ret = inflateEnd(&this.state); + bun.assert(ret == .Ok or ret == .StreamEnd); + if (this.err != .StreamEnd and this.finishFlush == .Finish) return error.ZlibError8; + } +}; + +// +// + +const GZIP_HEADER_ID1: u8 = 0x1f; +const GZIP_HEADER_ID2: u8 = 0x8b; diff --git a/test/.prettierignore b/test/.prettierignore index d8d94cdf27744..29277a7e1331e 100644 --- a/test/.prettierignore +++ b/test/.prettierignore @@ -2,3 +2,5 @@ node_modules snapshots js/deno *.min.js +snippets +js/node/test/fixtures diff --git a/test/bun.lockb b/test/bun.lockb index 7d3b0e3b24b54..175deff005569 100755 Binary files a/test/bun.lockb and b/test/bun.lockb differ diff --git a/test/bundler/__snapshots__/bun-build-api.test.ts.snap b/test/bundler/__snapshots__/bun-build-api.test.ts.snap index 30746b67d61c0..ee8156b900e34 100644 --- a/test/bundler/__snapshots__/bun-build-api.test.ts.snap +++ b/test/bundler/__snapshots__/bun-build-api.test.ts.snap @@ -1,117 +1,571 @@ -// Bun Snapshot v1, https://goo.gl/fbAQLP - -exports[`Bun.build BuildArtifact properties: hash 1`] = `"e4885a8bc2de343a"`; - -exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"cb8abf3391c2971f"`; - -exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"e4885a8bc2de343a"`; - -exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"0000000000000000"`; - -exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` -"var __defProp = Object.defineProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { - get: all[name], - enumerable: true, - configurable: true, - set: (newValue) => all[name] = () => newValue - }); -}; -var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); - -// test/bundler/fixtures/trivial/fn.js -var exports_fn = {}; -__export(exports_fn, { - fn: () => { - { - return fn; - } - } -}); -function fn(a) { - return a + 42; -} -var init_fn = __esm(() => { -}); - -// test/bundler/fixtures/trivial/index.js -var NS = Promise.resolve().then(() => (init_fn(), exports_fn)); -NS.then(({ fn: fn2 }) => { - console.log(fn2(42)); -}); -" -`; - -exports[`Bun.build outdir + reading out blobs works 1`] = ` -"var __defProp = Object.defineProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { - get: all[name], - enumerable: true, - configurable: true, - set: (newValue) => all[name] = () => newValue - }); -}; -var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); - -// test/bundler/fixtures/trivial/fn.js -var exports_fn = {}; -__export(exports_fn, { - fn: () => { - { - return fn; - } - } -}); -function fn(a) { - return a + 42; -} -var init_fn = __esm(() => { -}); - -// test/bundler/fixtures/trivial/index.js -var NS = Promise.resolve().then(() => (init_fn(), exports_fn)); -NS.then(({ fn: fn2 }) => { - console.log(fn2(42)); -}); -" -`; - -exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` -"var __defProp = Object.defineProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { - get: all[name], - enumerable: true, - configurable: true, - set: (newValue) => all[name] = () => newValue - }); -}; -var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); - -// test/bundler/fixtures/trivial/fn.js -var exports_fn = {}; -__export(exports_fn, { - fn: () => { - { - return fn; - } - } -}); -function fn(a) { - return a + 42; -} -var init_fn = __esm(() => { -}); - -// test/bundler/fixtures/trivial/index.js -var NS = Promise.resolve().then(() => (init_fn(), exports_fn)); -NS.then(({ fn: fn2 }) => { - console.log(fn2(42)); -}); -" -`; +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build BuildArtifact properties: hash 1`] = `"r6c8x1cc"`; + +exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"vanwb97w"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"r6c8x1cc"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`; + +exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build BuildArtifact properties: hash 1`] = `"r6c8x1cc"`; + +exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"vanwb97w"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"r6c8x1cc"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`; + +exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build BuildArtifact properties: hash 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"e1cnkf2m"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`; + +exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build BuildArtifact properties: hash 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"e1cnkf2m"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`; + +exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build BuildArtifact properties: hash 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"e1cnkf2m"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`; + +exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build Bun.write(BuildArtifact) 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build BuildArtifact properties: hash 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"e1cnkf2m"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"5909xc4p"`; + +exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`; + +exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = ` +"var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; + +// test/bundler/fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => fn +}); +function fn(a) { + return a + 42; +} + +// test/bundler/fixtures/trivial/index.js +var NS = Promise.resolve().then(() => exports_fn); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index 5293d15183ae7..9ef848cc09ed3 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -1,7 +1,7 @@ -import { test, expect, describe } from "bun:test"; -import { readFileSync } from "fs"; -import { bunEnv, bunExe } from "harness"; -import { join } from "path"; +import { describe, expect, test } from "bun:test"; +import { readFileSync, writeFileSync } from "fs"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import path, { join } from "path"; describe("Bun.build", () => { test("passing undefined doesnt segfault", () => { @@ -14,6 +14,43 @@ describe("Bun.build", () => { throw new Error("should have thrown"); }); + // https://github.com/oven-sh/bun/issues/12818 + test("sourcemap + build error crash case", async () => { + const dir = tempDirWithFiles("build", { + "/src/file1.ts": ` + import { A } from './dir'; + console.log(A); + `, + "/src/dir/index.ts": ` + import { B } from "./file3"; + export const A = [B] + `, + "/src/dir/file3.ts": ` + import { C } from "../file1"; // error + export const B = C; + `, + "/src/package.json": ` + { "type": "module" } + `, + "/src/tsconfig.json": ` + { + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "types": [] + } + } + `, + }); + const y = await Bun.build({ + entrypoints: [join(dir, "src/file1.ts")], + outdir: join(dir, "out"), + sourcemap: "external", + external: ["@minecraft"], + }); + }); + test("invalid options throws", async () => { expect(() => Bun.build({} as any)).toThrow(); expect(() => @@ -68,19 +105,26 @@ describe("Bun.build", () => { test("Bun.write(BuildArtifact)", async () => { Bun.gc(true); + const tmpdir = tempDirWithFiles("bun-build-api-write", { + "package.json": `{}`, + }); const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], }); - await Bun.write("/tmp/bun-build-test-write.js", x.outputs[0]); - expect(readFileSync("/tmp/bun-build-test-write.js", "utf-8")).toMatchSnapshot(); + await Bun.write(path.join(tmpdir, "index.js"), x.outputs[0]); + expect(readFileSync(path.join(tmpdir, "index.js"), "utf-8")).toMatchSnapshot(); Bun.gc(true); }); test("rebuilding busts the directory entries cache", () => { Bun.gc(true); + const tmpdir = tempDirWithFiles("rebuild-bust-dirent-cache", { + "package.json": `{}`, + }); + const { exitCode, stderr } = Bun.spawnSync({ - cmd: [bunExe(), join(import.meta.dir, "bundler-reloader-script.ts")], - env: bunEnv, + cmd: [bunExe(), join(import.meta.dir, "fixtures", "bundler-reloader-script.ts")], + env: { ...bunEnv, BUNDLER_RELOADER_SCRIPT_TMP_DIR: tmpdir }, stderr: "pipe", stdout: "inherit", }); @@ -93,9 +137,12 @@ describe("Bun.build", () => { test("outdir + reading out blobs works", async () => { Bun.gc(true); + const fixture = tempDirWithFiles("build-outdir", { + "package.json": `{}`, + }); const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], - outdir: "/tmp/bun-build-test-read-out", + outdir: fixture, }); expect(await x.outputs.values().next().value?.text()).toMatchSnapshot(); Bun.gc(true); @@ -103,14 +150,18 @@ describe("Bun.build", () => { test("BuildArtifact properties", async () => { Bun.gc(true); + const outdir = tempDirWithFiles("build-artifact-properties", { + "package.json": `{}`, + }); const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + outdir, }); const [blob] = x.outputs; expect(blob).toBeTruthy(); expect(blob.type).toBe("text/javascript;charset=utf-8"); expect(blob.size).toBeGreaterThan(1); - expect(blob.path).toBe("./index.js"); + expect(path.relative(outdir, blob.path)).toBe("index.js"); expect(blob.hash).toBeTruthy(); expect(blob.hash).toMatchSnapshot("hash"); expect(blob.kind).toBe("entry-point"); @@ -121,17 +172,21 @@ describe("Bun.build", () => { test("BuildArtifact properties + entry.naming", async () => { Bun.gc(true); + const outdir = tempDirWithFiles("build-artifact-properties-entry-naming", { + "package.json": `{}`, + }); const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], naming: { entry: "hello", }, + outdir, }); const [blob] = x.outputs; expect(blob).toBeTruthy(); expect(blob.type).toBe("text/javascript;charset=utf-8"); expect(blob.size).toBeGreaterThan(1); - expect(blob.path).toBe("./hello"); + expect(path.relative(outdir, blob.path)).toBe("hello"); expect(blob.hash).toBeTruthy(); expect(blob.hash).toMatchSnapshot("hash"); expect(blob.kind).toBe("entry-point"); @@ -142,14 +197,18 @@ describe("Bun.build", () => { test("BuildArtifact properties sourcemap", async () => { Bun.gc(true); + const outdir = tempDirWithFiles("build-artifact-properties-sourcemap", { + "package.json": `{}`, + }); const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], sourcemap: "external", + outdir, }); const [blob, map] = x.outputs; expect(blob.type).toBe("text/javascript;charset=utf-8"); expect(blob.size).toBeGreaterThan(1); - expect(blob.path).toBe("./index.js"); + expect(path.relative(outdir, blob.path)).toBe("index.js"); expect(blob.hash).toBeTruthy(); expect(blob.hash).toMatchSnapshot("hash index.js"); expect(blob.kind).toBe("entry-point"); @@ -158,7 +217,7 @@ describe("Bun.build", () => { expect(map.type).toBe("application/json;charset=utf-8"); expect(map.size).toBeGreaterThan(1); - expect(map.path).toBe("./index.js.map"); + expect(path.relative(outdir, map.path)).toBe("index.js.map"); expect(map.hash).toBeTruthy(); expect(map.hash).toMatchSnapshot("hash index.js.map"); expect(map.kind).toBe("sourcemap"); @@ -201,6 +260,7 @@ describe("Bun.build", () => { test("new Response(BuildArtifact) sets content type", async () => { const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + outdir: tempDirWithFiles("response-buildartifact", {}), }); const response = new Response(x.outputs[0]); expect(response.headers.get("content-type")).toBe("text/javascript;charset=utf-8"); @@ -210,6 +270,7 @@ describe("Bun.build", () => { test.todo("new Response(BuildArtifact) sets etag", async () => { const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + outdir: tempDirWithFiles("response-buildartifact-etag", {}), }); const response = new Response(x.outputs[0]); expect(response.headers.get("etag")).toBeTruthy(); @@ -242,6 +303,7 @@ describe("Bun.build", () => { test("errors are returned as an array", async () => { const x = await Bun.build({ entrypoints: [join(import.meta.dir, "does-not-exist.ts")], + outdir: tempDirWithFiles("errors-are-returned-as-an-array", {}), }); expect(x.success).toBe(false); expect(x.logs).toHaveLength(1); @@ -253,6 +315,7 @@ describe("Bun.build", () => { test("warnings do not fail a build", async () => { const x = await Bun.build({ entrypoints: [join(import.meta.dir, "./fixtures/jsx-warning/index.jsx")], + outdir: tempDirWithFiles("warnings-do-not-fail-a-build", {}), }); expect(x.success).toBe(true); expect(x.logs).toHaveLength(1); @@ -263,34 +326,6 @@ describe("Bun.build", () => { expect(x.logs[0].position).toBeTruthy(); }); - test("test bun target", async () => { - const x = await Bun.build({ - entrypoints: [join(import.meta.dir, "./fixtures/trivial/bundle-ws.ts")], - target: "bun", - }); - expect(x.success).toBe(true); - const [blob] = x.outputs; - const content = await blob.text(); - - // use bun's ws - expect(content).toContain('import {WebSocket} from "ws"'); - expect(content).not.toContain("var websocket = __toESM(require_websocket(), 1);"); - }); - - test("test node target, issue #3844", async () => { - const x = await Bun.build({ - entrypoints: [join(import.meta.dir, "./fixtures/trivial/bundle-ws.ts")], - target: "node", - }); - expect(x.success).toBe(true); - const [blob] = x.outputs; - const content = await blob.text(); - - expect(content).not.toContain('import {WebSocket} from "ws"'); - // depends on the ws package in the test/node_modules. - expect(content).toContain("var websocket = __toESM(require_websocket(), 1);"); - }); - test("module() throws error", async () => { expect(() => Bun.build({ @@ -313,4 +348,106 @@ describe("Bun.build", () => { }), ).toThrow(); }); + + test("hash considers cross chunk imports", async () => { + Bun.gc(true); + const fixture = tempDirWithFiles("build-hash-cross-chunk-imports", { + "entry1.ts": ` + import { bar } from './bar' + export const entry1 = () => { + console.log('FOO') + bar() + } + `, + "entry2.ts": ` + import { bar } from './bar' + export const entry1 = () => { + console.log('FOO') + bar() + } + `, + "bar.ts": ` + export const bar = () => { + console.log('BAR') + } + `, + }); + const first = await Bun.build({ + entrypoints: [join(fixture, "entry1.ts"), join(fixture, "entry2.ts")], + outdir: join(fixture, "out"), + target: "browser", + splitting: true, + minify: false, + naming: "[dir]/[name]-[hash].[ext]", + }); + if (!first.success) throw new AggregateError(first.logs); + expect(first.outputs.length).toBe(3); + + writeFileSync(join(fixture, "bar.ts"), readFileSync(join(fixture, "bar.ts"), "utf8").replace("BAR", "BAZ")); + + const second = await Bun.build({ + entrypoints: [join(fixture, "entry1.ts"), join(fixture, "entry2.ts")], + outdir: join(fixture, "out2"), + target: "browser", + splitting: true, + minify: false, + naming: "[dir]/[name]-[hash].[ext]", + }); + if (!second.success) throw new AggregateError(second.logs); + expect(second.outputs.length).toBe(3); + + const totalUniqueHashes = new Set(); + const allFiles = [...first.outputs, ...second.outputs]; + for (const out of allFiles) totalUniqueHashes.add(out.hash); + + expect( + totalUniqueHashes.size, + "number of unique hashes should be 6: three per bundle. the changed foo.ts affects all chunks", + ).toBe(6); + + // ensure that the hashes are in the path + for (const out of allFiles) { + expect(out.path).toInclude(out.hash!); + } + + Bun.gc(true); + }); + + test("ignoreDCEAnnotations works", async () => { + const fixture = tempDirWithFiles("build-ignore-dce-annotations", { + "package.json": `{}`, + "entry.ts": ` + /* @__PURE__ */ console.log(1) + `, + }); + + const bundle = await Bun.build({ + entrypoints: [join(fixture, "entry.ts")], + ignoreDCEAnnotations: true, + minify: true, + outdir: path.join(fixture, "out"), + }); + if (!bundle.success) throw new AggregateError(bundle.logs); + + expect(await bundle.outputs[0].text()).toBe("console.log(1);\n"); + }); + + test("emitDCEAnnotations works", async () => { + const fixture = tempDirWithFiles("build-emit-dce-annotations", { + "package.json": `{}`, + "entry.ts": ` + export const OUT = /* @__PURE__ */ console.log(1) + `, + }); + + const bundle = await Bun.build({ + entrypoints: [join(fixture, "entry.ts")], + emitDCEAnnotations: true, + minify: true, + outdir: path.join(fixture, "out"), + }); + if (!bundle.success) throw new AggregateError(bundle.logs); + + expect(await bundle.outputs[0].text()).toBe("var o=/*@__PURE__*/console.log(1);export{o as OUT};\n"); + }); }); diff --git a/test/bundler/bundler_browser.test.ts b/test/bundler/bundler_browser.test.ts index 879f90fb2c61f..40ddd09226cae 100644 --- a/test/bundler/bundler_browser.test.ts +++ b/test/bundler/bundler_browser.test.ts @@ -1,7 +1,6 @@ import assert from "assert"; -import dedent from "dedent"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { const nodePolyfillList = { diff --git a/test/bundler/bundler_bun.test.ts b/test/bundler/bundler_bun.test.ts index f8fb7159c5ab4..63820dd4c3b8d 100644 --- a/test/bundler/bundler_bun.test.ts +++ b/test/bundler/bundler_bun.test.ts @@ -1,9 +1,6 @@ -import assert from "assert"; -import dedent from "dedent"; -import { ESBUILD, itBundled, testForFile } from "./expectBundled"; import { Database } from "bun:sqlite"; -import { isWindows } from "harness"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { itBundled("bun/embedded-sqlite-file", { @@ -82,9 +79,8 @@ describe("bundler", () => { run: { exitCode: 1, validate({ stderr }) { - assert( - stderr.startsWith( - `1 | // this file has comments and weird whitespace, intentionally + expect(stderr).toStartWith( + `1 | // this file has comments and weird whitespace, intentionally 2 | // to make it obvious if sourcemaps were generated and mapped properly 3 | if (true) code(); 4 | function code() { @@ -92,7 +88,6 @@ describe("bundler", () => { 6 | throw new ^ error: Hello World`, - ) || void console.error(stderr), ); expect(stderr).toInclude("entry.ts:6:19"); }, diff --git a/test/bundler/bundler_cjs2esm.test.ts b/test/bundler/bundler_cjs2esm.test.ts index 01a17356b60a7..0063ce5189fb0 100644 --- a/test/bundler/bundler_cjs2esm.test.ts +++ b/test/bundler/bundler_cjs2esm.test.ts @@ -1,7 +1,5 @@ -import assert from "assert"; -import dedent from "dedent"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { itBundled } from "./expectBundled"; const fakeReactNodeModules = { "/node_modules/react/index.js": /* js */ ` @@ -240,19 +238,19 @@ describe("bundler", () => { "/entry.js": /* js */ ` const react = require("react"); console.log(react.react); - + const react1 = (console.log(require("react").react), require("react")); console.log(react1.react); - + const react2 = (require("react"), console.log(require("react").react)); console.log(react2); - + let x = {}; x.react = require("react"); console.log(x.react.react); - + console.log(require("react").react); - + let y = {}; y[require("react")] = require("react"); console.log(y[require("react")].react); @@ -286,4 +284,112 @@ describe("bundler", () => { stdout: "react\nreact\nreact\nreact\nundefined\nreact\nreact\nreact\nreact\nreact\nreact\n1 react\nreact\nreact", }, }); + itBundled("cjs2esm/ReactSpecificUnwrapping", { + files: { + "/entry.js": /* js */ ` + import { renderToReadableStream } from "react"; + console.log(renderToReadableStream()); + `, + "/node_modules/react/index.js": /* js */ ` + console.log('side effect'); + module.exports = require('./main'); + `, + "/node_modules/react/main.js": /* js */ ` + "use strict"; + var REACT_ELEMENT_TYPE = Symbol.for("pass"); + exports.renderToReadableStream = (e, t) => { + return REACT_ELEMENT_TYPE; + } + `, + }, + run: { + stdout: "side effect\nSymbol(pass)", + }, + minifySyntax: true, + }); + itBundled("cjs2esm/ReactSpecificUnwrapping2", { + files: { + "/entry.js": /* js */ ` + import * as react from "react-dom"; + console.log(react); + `, + "/node_modules/react-dom/index.js": /* js */ ` + export const stuff = [ + require('./a.js'), + require('./b.js') + ]; + `, + "/node_modules/react-dom/a.js": /* js */ ` + (function () { + var React = require('react'); + var stream = require('stream'); + + console.log([React, stream]); + + exports.version = null; + })(); + `, + "/node_modules/react-dom/b.js": /* js */ ` + (function () { + var React = require('react'); + var util = require('util'); + + console.log([React, util]); + + exports.version = null; + })(); + `, + "/node_modules/react/index.js": /* js */ ` + module.exports = 123; + `, + }, + run: true, + minifyIdentifiers: true, + target: "bun", + }); + itBundled("cjs2esm/ModuleExportsRenamingNoDeopt", { + files: { + "/entry.js": /* js */ ` + eval('exports = { xyz: 123 }; module.exports = { xyz: 456 }'); + let w = () => [module.exports, module.exports.xyz]; // rewrite to exports, exports.xyz + let x = () => [exports, exports.xyz]; // keep as is + console.log(JSON.stringify([w(), x()])); + `, + }, + run: { + stdout: '[[{"xyz":123},123],[{"xyz":123},123]]', + }, + }); + itBundled("cjs2esm/ModuleExportsRenamingAssignDeOpt", { + files: { + "/entry.js": /* js */ ` + eval('exports = { xyz: 123 }'); + let w = () => [module.exports, module.exports.xyz]; // keep as is + let x = () => [exports, exports.xyz]; // keep as is + module.exports = { xyz: 456 }; + let y = () => [module.exports, module.exports.xyz]; // keep as is + let z = () => [exports, exports.xyz]; // keep as is + console.log(JSON.stringify([w(), x(), y(), z()])); + `, + }, + run: { + stdout: '[[{"xyz":456},456],[{"xyz":123},123],[{"xyz":456},456],[{"xyz":123},123]]', + }, + }); + itBundled("cjs2esm/ModuleExportsRenamingAssignExportsDeOpt", { + files: { + "/entry.js": /* js */ ` + eval('module.exports = { xyz: 456 }'); + let w = () => [module.exports, module.exports.xyz]; + let x = () => [exports, exports.xyz]; + exports = { xyz: 123 }; + let y = () => [module.exports, module.exports.xyz]; + let z = () => [exports, exports.xyz]; + console.log(JSON.stringify([w(), x(), y(), z()])); + `, + }, + run: { + stdout: '[[{"xyz":456},456],[{"xyz":123},123],[{"xyz":456},456],[{"xyz":123},123]]', + }, + }); }); diff --git a/test/bundler/bundler_compile.test.ts b/test/bundler/bundler_compile.test.ts index 171b482ef3ed0..078f9bb3c22c9 100644 --- a/test/bundler/bundler_compile.test.ts +++ b/test/bundler/bundler_compile.test.ts @@ -1,9 +1,7 @@ -import assert from "assert"; -import dedent from "dedent"; -import { ESBUILD, itBundled, testForFile } from "./expectBundled"; import { Database } from "bun:sqlite"; -import { fillRepeating } from "harness"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { rmSync } from "fs"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { itBundled("compile/HelloWorld", { @@ -31,6 +29,113 @@ describe("bundler", () => { outfile: "dist/out", run: { stdout: "Hello, world!" }, }); + itBundled("compile/WorkerRelativePathNoExtension", { + compile: true, + files: { + "/entry.ts": /* js */ ` + import {rmSync} from 'fs'; + // Verify we're not just importing from the filesystem + rmSync("./worker.ts", {force: true}); + + console.log("Hello, world!"); + new Worker("./worker"); + `, + "/worker.ts": /* js */ ` + console.log("Worker loaded!"); + `.trim(), + }, + entryPointsRaw: ["./entry.ts", "./worker.ts"], + outfile: "dist/out", + run: { stdout: "Hello, world!\nWorker loaded!\n", file: "dist/out", setCwd: true }, + }); + itBundled("compile/WorkerRelativePathTSExtension", { + compile: true, + files: { + "/entry.ts": /* js */ ` + import {rmSync} from 'fs'; + // Verify we're not just importing from the filesystem + rmSync("./worker.ts", {force: true}); + console.log("Hello, world!"); + new Worker("./worker.ts"); + `, + "/worker.ts": /* js */ ` + console.log("Worker loaded!"); + `.trim(), + }, + entryPointsRaw: ["./entry.ts", "./worker.ts"], + outfile: "dist/out", + run: { stdout: "Hello, world!\nWorker loaded!\n", file: "dist/out", setCwd: true }, + }); + itBundled("compile/Bun.embeddedFiles", { + compile: true, + // TODO: this shouldn't be necessary, or we should add a map aliasing files. + assetNaming: "[name].[ext]", + + files: { + "/entry.ts": /* js */ ` + import {rmSync} from 'fs'; + import {createRequire} from 'module'; + import './foo.file'; + import './1.embed'; + import './2.embed'; + rmSync('./foo.file', {force: true}); + rmSync('./1.embed', {force: true}); + rmSync('./2.embed', {force: true}); + const names = { + "1.embed": "1.embed", + "2.embed": "2.embed", + "foo.file": "foo.file", + } + // We want to verify it omits source code. + for (let f of Bun.embeddedFiles) { + const name = f.name; + if (!names[name]) { + throw new Error("Unexpected embedded file: " + name); + } + } + + if (Bun.embeddedFiles.length !== 3) throw "fail"; + if ((await Bun.file(createRequire(import.meta.url).resolve('./1.embed')).text()).trim() !== "abcd") throw "fail"; + if ((await Bun.file(createRequire(import.meta.url).resolve('./2.embed')).text()).trim() !== "abcd") throw "fail"; + if ((await Bun.file(createRequire(import.meta.url).resolve('./foo.file')).text()).trim() !== "abcd") throw "fail"; + if ((await Bun.file(import.meta.require.resolve('./1.embed')).text()).trim() !== "abcd") throw "fail"; + if ((await Bun.file(import.meta.require.resolve('./2.embed')).text()).trim() !== "abcd") throw "fail"; + if ((await Bun.file(import.meta.require.resolve('./foo.file')).text()).trim() !== "abcd") throw "fail"; + console.log("Hello, world!"); + `, + "/1.embed": /* js */ ` + abcd + `.trim(), + "/2.embed": /* js */ ` + abcd + `.trim(), + "/foo.file": /* js */ ` + abcd + `.trim(), + }, + outfile: "dist/out", + run: { stdout: "Hello, world!", setCwd: true }, + }); + itBundled("compile/ResolveEmbeddedFileOutfile", { + compile: true, + // TODO: this shouldn't be necessary, or we should add a map aliasing files. + assetNaming: "[name].[ext]", + + files: { + "/entry.ts": /* js */ ` + import {rmSync} from 'fs'; + import './foo.file'; + rmSync('./foo.file', {force: true}); + if ((await Bun.file(import.meta.require.resolve('./foo.file')).text()).trim() !== "abcd") throw "fail"; + console.log("Hello, world!"); + `, + "/foo.file": /* js */ ` + abcd + `.trim(), + }, + outfile: "dist/out", + run: { stdout: "Hello, world!" }, + }); itBundled("compile/pathToFileURLWorks", { compile: true, files: { @@ -299,4 +404,94 @@ describe("bundler", () => { }, run: { stdout: '{"\u{6211}":"\u{6211}"}' }, }); + itBundled("compile/ImportMetaMain", { + compile: true, + files: { + "/entry.ts": /* js */ ` + // test toString on function to observe what the inlined value was + console.log((() => import.meta.main).toString().includes('true')); + console.log((() => !import.meta.main).toString().includes('false')); + console.log((() => !!import.meta.main).toString().includes('true')); + console.log((() => require.main == module).toString().includes('true')); + console.log((() => require.main === module).toString().includes('true')); + console.log((() => require.main !== module).toString().includes('false')); + console.log((() => require.main !== module).toString().includes('false')); + `, + }, + run: { stdout: new Array(7).fill("true").join("\n") }, + }); + itBundled("compile/SourceMap", { + target: "bun", + compile: true, + files: { + "/entry.ts": /* js */ ` + // this file has comments and weird whitespace, intentionally + // to make it obvious if sourcemaps were generated and mapped properly + if (true) code(); + function code() { + // hello world + throw new + Error("Hello World"); + } + `, + }, + sourceMap: "external", + onAfterBundle(api) { + rmSync(api.join("entry.ts"), {}); // Hide the source files for errors + }, + run: { + exitCode: 1, + validate({ stderr }) { + expect(stderr).toStartWith( + `1 | // this file has comments and weird whitespace, intentionally +2 | // to make it obvious if sourcemaps were generated and mapped properly +3 | if (true) code(); +4 | function code() { +5 | // hello world +6 | throw new + ^ +error: Hello World`, + ); + expect(stderr).toInclude("entry.ts:6:19"); + }, + }, + }); + itBundled("compile/SourceMapBigFile", { + target: "bun", + compile: true, + files: { + "/entry.ts": /* js */ `import * as ReactDom from ${JSON.stringify(require.resolve("react-dom/server"))}; + +// this file has comments and weird whitespace, intentionally +// to make it obvious if sourcemaps were generated and mapped properly +if (true) code(); +function code() { + // hello world + throw new + Error("Hello World"); +} + +console.log(ReactDom);`, + }, + sourceMap: "external", + onAfterBundle(api) { + rmSync(api.join("entry.ts"), {}); // Hide the source files for errors + }, + run: { + exitCode: 1, + validate({ stderr }) { + expect(stderr).toStartWith( + `3 | // this file has comments and weird whitespace, intentionally +4 | // to make it obvious if sourcemaps were generated and mapped properly +5 | if (true) code(); +6 | function code() { +7 | // hello world +8 | throw new + ^ +error: Hello World`, + ); + expect(stderr).toInclude("entry.ts:8:19"); + }, + }, + }); }); diff --git a/test/bundler/bundler_decorator_metadata.test.ts b/test/bundler/bundler_decorator_metadata.test.ts index a4145877b50c8..8005e41521582 100644 --- a/test/bundler/bundler_decorator_metadata.test.ts +++ b/test/bundler/bundler_decorator_metadata.test.ts @@ -1,6 +1,5 @@ -import assert from "assert"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "./expectBundled"; const reflectMetadata = ` var Reflect2; diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts index 26a52b900b2de..866c98d0ea4f7 100644 --- a/test/bundler/bundler_edgecase.test.ts +++ b/test/bundler/bundler_edgecase.test.ts @@ -1,8 +1,6 @@ -import assert from "assert"; -import dedent from "dedent"; -import { sep, join } from "path"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { join } from "node:path"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { itBundled("edgecase/EmptyFile", { @@ -37,7 +35,7 @@ describe("bundler", () => { }, target: "bun", run: { - stdout: `a${sep}b`, + stdout: join("a", "b"), }, }); itBundled("edgecase/ImportStarFunction", { @@ -670,6 +668,29 @@ describe("bundler", () => { "": ['ModuleNotFound resolving "/entry.js" (entry point)'], }, }); + itBundled("edgecase/AssetEntryPoint", { + files: { + "/entry.zig": ` + const std = @import("std"); + + pub fn main() void { + std.log.info("Hello, world!\\n", .{}); + } + `, + }, + outdir: "/out", + entryPointsRaw: ["./entry.zig"], + runtimeFiles: { + "/exec.js": ` + import assert from 'node:assert'; + import the_path from './out/entry.js'; + assert.strictEqual(the_path, './entry-z5artd5z.zig'); + `, + }, + run: { + file: "./exec.js", + }, + }); itBundled("edgecase/ExportDefaultUndefined", { files: { "/entry.ts": /* ts */ ` @@ -1171,6 +1192,645 @@ describe("bundler", () => { expect(count, "should only emit two constructors: " + content).toBe(2); }, }); + itBundled("edgecase/EnumInliningRopeStringPoison", { + files: { + "/entry.ts": ` + const enum A1 { + B = "1" + "2", + C = "3" + B, + }; + console.log(A1.B, A1.C); + + const enum A2 { + B = "1" + "2", + C = ("3" + B) + "4", + }; + console.log(A2.B, A2.C); + `, + }, + run: { + stdout: "12 312\n12 3124", + }, + }); + itBundled("edgecase/ProtoNullProtoInlining", { + files: { + "/entry.ts": ` + console.log({ __proto__: null }.__proto__ !== void 0) + `, + }, + run: { + stdout: "false", + }, + }); + itBundled("edgecase/ImportOptionsArgument", { + files: { + "/entry.js": ` + import('ext', { with: { get ''() { KEEP } } }) + .then(function (error) { + console.log(error); + }); + `, + }, + dce: true, + external: ["ext"], + target: "bun", + }); + itBundled("edgecase/ConstantFoldingShiftOperations", { + files: { + "/entry.ts": ` + capture(421 >> -542) + capture(421 >>> -542) + capture(1 << 32) + capture(1 >> 32) + capture(1 >>> 32) + capture(47849312 << 34) + capture(-9 >> 1) + capture(-5 >> 1) + `, + }, + minifySyntax: true, + capture: ["105", "105", "1", "1", "1", "191397248", "-5", "-3"], + }); + itBundled("edgecase/ConstantFoldingBitwiseCoersion", { + files: { + "/entry.ts": ` + capture(0 | 0) + capture(12582912 | 0) + capture(0xc00000 | 0) + capture(Infinity | 0) + capture(-Infinity | 0) + capture(NaN | 0) + // u32 limits + capture(-4294967295 | 0) + capture(-4294967296 | 0) + capture(-4294967297 | 0) + capture(4294967295 | 0) + capture(4294967296 | 0) + capture(4294967297 | 0) + // i32 limits + capture(-2147483647 | 0) + capture(-2147483648 | 0) + capture(-2147483649 | 0) + capture(2147483647 | 0) + capture(2147483648 | 0) + capture(2147483649 | 0) + capture(0.5 | 0) + `, + }, + minifySyntax: true, + capture: [ + "0", + "12582912", + "12582912", + "0", + "0", + "0", + "1", + "0", + "-1", + "-1", + "0", + "1", + "-2147483647", + "-2147483648", + "2147483647", + "2147483647", + "-2147483648", + "-2147483647", + "0", + ], + }); + itBundled("edgecase/EnumInliningNanBoxedEncoding", { + files: { + "/main.ts": ` + import { Enum } from './other.ts'; + capture(Enum.a); + capture(Enum.b); + capture(Enum.c); + capture(Enum.d); + capture(Enum.e); + capture(Enum.f); + capture(Enum.g); + `, + "/other.ts": ` + export const enum Enum { + a = 0, + b = NaN, + c = (0 / 0) + 1, + d = Infinity, + e = -Infinity, + f = 3e450, + // https://float.exposed/0xffefffffffffffff + g = -1.79769313486231570815e+308, + } + `, + }, + minifySyntax: true, + capture: [ + "0 /* a */", + "NaN /* b */", + "NaN /* c */", + "1 / 0 /* d */", + "-1 / 0 /* e */", + "1 / 0 /* f */", + // should probably fix this + "-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 /* g */", + ], + }); + // Stack overflow possibility + itBundled("edgecase/AwsCdkLib", { + files: { + "entry.js": `import * as aws from ${JSON.stringify(require.resolve("aws-cdk-lib"))}; aws;`, + }, + target: "bun", + run: true, + }); + itBundled("edgecase/PackageExternalDoNotBundleNodeModules", { + files: { + "/entry.ts": /* ts */ ` + import { a } from "foo"; + console.log(a); + `, + }, + packages: "external", + target: "bun", + runtimeFiles: { + "/node_modules/foo/index.js": `export const a = "Hello World";`, + "/node_modules/foo/package.json": /* json */ ` + { + "name": "foo", + "version": "2.0.0", + "main": "index.js" + } + `, + }, + run: { + stdout: ` + Hello World + `, + }, + }); + itBundled("edgecase/EntrypointWithoutPrefixSlashOrDotIsNotConsideredExternal#12734", { + files: { + "/src/entry.ts": /* ts */ ` + import { helloWorld } from "./second.ts"; + console.log(helloWorld); + `, + "/src/second.ts": /* ts */ ` + export const helloWorld = "Hello World"; + `, + }, + root: "/src", + entryPointsRaw: ["src/entry.ts"], + packages: "external", + target: "bun", + run: { + file: "/src/entry.ts", + stdout: ` + Hello World + `, + }, + }); + itBundled("edgecase/IntegerUnderflow#12547", { + files: { + "/entry.js": ` + import { a } from 'external'; + + function func() { + const b = 1 + a.c; + return b; + } + `, + }, + minifySyntax: true, + minifyWhitespace: true, + minifyIdentifiers: true, + external: ["external"], + onAfterBundle(api) { + // DCE is not yet able to eliminate the `a` or even the `as c`. Equivalent to esbuild as of 2024-07-15 + api.expectFile("/out.js").toBe(`import{a as c}from"external";\n`); + }, + }); + itBundled("edgecase/TypeScriptNamespaceSiblingFunction", { + files: { + "/entry.ts": ` + namespace X { + export function Y() { + return 2; + } + export namespace Y { + export const Z = 1; + } + } + console.log(X, X.Y(), X.Y.Z); + `, + }, + run: { + stdout: "{\n Y: [Function: Y],\n} 2 1", + }, + }); + itBundled("edgecase/TypeScriptNamespaceSiblingClass", { + files: { + "/entry.ts": ` + namespace X { + export class Y { + constructor(v) { + this.value = v; + } + + toJSON() { + return this.value; + } + } + export namespace Y { + export const Z = 1; + } + } + console.log(X, new X.Y(2).toJSON(), X.Y.Z); + `, + }, + run: { + stdout: "{\n Y: [class Y],\n} 2 1", + }, + }); + itBundled("edgecase/TypeScriptNamespaceSiblingEnum", { + files: { + "/entry.ts": ` + namespace X { + export enum Y { + A, + B, + } + export namespace Y { + export const Z = 1; + } + } + console.log(JSON.stringify([X, X.Y.A, X.Y.Z])); + `, + }, + run: { + stdout: '[{"Y":{"0":"A","1":"B","A":0,"B":1,"Z":1}},0,1]', + }, + }); + itBundled("edgecase/TypeScriptNamespaceSiblingVariable", { + files: { + "/entry.ts": ` + namespace X { + export let Y = {}; + export namespace Y { + export const Z = 1; + } + } + `, + }, + bundleErrors: { + "/entry.ts": [`"Y" has already been declared`], + }, + }); + // This specifically only happens with 'export { ... } from ...' syntax + itBundled("edgecase/EsmSideEffectsFalseWithSideEffectsExportFrom", { + files: { + "/file1.js": ` + import("./file2.js"); + `, + "/file2.js": ` + export { a } from './file3.js'; + `, + "/file3.js": ` + export function a(input) { + return 42; + } + console.log('side effect'); + `, + "/package.json": ` + { + "name": "my-package", + "sideEffects": false + } + `, + }, + run: { + stdout: "side effect", + }, + }); + itBundled("edgecase/EsmSideEffectsFalseWithSideEffectsExportFromCodeSplitting", { + files: { + "/file1.js": ` + import("./file2.js"); + console.log('file1'); + `, + "/file1b.js": ` + import("./file2.js"); + console.log('file2'); + `, + "/file2.js": ` + export { a } from './file3.js'; + `, + "/file3.js": ` + export function a(input) { + return 42; + } + console.log('side effect'); + `, + "/package.json": ` + { + "name": "my-package", + "sideEffects": false + } + `, + }, + splitting: true, + outdir: "out", + entryPoints: ["./file1.js", "./file1b.js"], + run: [ + { + file: "/out/file1.js", + stdout: "file1\nside effect", + }, + { + file: "/out/file1b.js", + stdout: "file2\nside effect", + }, + ], + }); + itBundled("edgecase/RequireSideEffectsFalseWithSideEffectsExportFrom", { + files: { + "/file1.js": ` + require("./file2.js"); + `, + "/file2.js": ` + export { a } from './file3.js'; + `, + "/file3.js": ` + export function a(input) { + return 42; + } + console.log('side effect'); + `, + "/package.json": ` + { + "name": "my-package", + "sideEffects": false + } + `, + }, + run: { + stdout: "side effect", + }, + }); + itBundled("edgecase/SideEffectsFalseWithSideEffectsExportFrom", { + files: { + "/file1.js": ` + import("./file2.js"); + `, + "/file2.js": ` + import * as foo from './file3.js'; + export default foo; + `, + "/file3.js": ` + export function a(input) { + return 42; + } + console.log('side effect'); + `, + "/package.json": ` + { + "name": "my-package", + "sideEffects": false + } + `, + }, + run: { + stdout: "side effect", + }, + }); + itBundled("edgecase/BuiltinWithTrailingSlash", { + files: { + "/entry.js": ` + import * as process from 'process/'; + console.log(JSON.stringify(process)); + `, + "/node_modules/process/index.js": ` + export default { hello: 'world' }; + `, + }, + run: { + stdout: `{"default":{"hello":"world"}}`, + }, + }); + itBundled("edgecase/EsmWrapperClassHoisting", { + files: { + "/entry.ts": ` + async function hi() { + const { default: MyInherited } = await import('./hello'); + const myInstance = new MyInherited(); + console.log(myInstance.greet()) + } + + hi(); + `, + "/hello.ts": ` + const MyReassignedSuper = class MySuper { + greet() { + return 'Hello, world!'; + } + }; + + class MyInherited extends MyReassignedSuper {}; + + export default MyInherited; + `, + }, + run: { + stdout: "Hello, world!", + }, + }); + itBundled("edgecase/EsmWrapperElimination1", { + files: { + "/entry.ts": ` + async function load() { + return import('./hello'); + } + load().then(({ default: def }) => console.log(def())); + `, + "/hello.ts": ` + export var x = 123; + export var y = function() { return x; }; + export function z() { return y(); } + function a() { return z(); } + export default function c() { return a(); } + `, + }, + run: { + stdout: "123", + }, + }); + itBundled("edgecase/TsEnumTreeShakingUseAndInlineClass", { + files: { + "/entry.ts": ` + import { TestEnum } from './enum'; + + class TestClass { + constructor() { + console.log(JSON.stringify(TestEnum)); + } + + testMethod(name: TestEnum) { + return name === TestEnum.A; + } + } + + // This must use wrapper class + console.log(new TestClass()); + // This must inline + console.log(TestClass.prototype.testMethod.toString().includes('TestEnum')); + `, + "/enum.ts": ` + export enum TestEnum { + A, + B, + } + `, + }, + dce: true, + run: { + stdout: ` + {"0":"A","1":"B","A":0,"B":1} + TestClass { + testMethod: [Function: testMethod], + } + false + `, + }, + }); + // this test checks that visit order doesnt matter (inline then use, above is use then inline) + itBundled("edgecase/TsEnumTreeShakingUseAndInlineClass2", { + files: { + "/entry.ts": ` + import { TestEnum } from './enum'; + + class TestClass { + testMethod(name: TestEnum) { + return name === TestEnum.A; + } + + constructor() { + console.log(JSON.stringify(TestEnum)); + } + } + + // This must use wrapper class + console.log(new TestClass()); + // This must inline + console.log(TestClass.prototype.testMethod.toString().includes('TestEnum')); + `, + "/enum.ts": ` + export enum TestEnum { + A, + B, + } + `, + }, + dce: true, + run: { + stdout: ` + {"0":"A","1":"B","A":0,"B":1} + TestClass { + testMethod: [Function: testMethod], + } + false + `, + }, + }); + itBundled("edgecase/TsEnumTreeShakingUseAndInlineNamespace", { + files: { + "/entry.ts": ` + import { TestEnum } from './enum'; + + namespace TestClass { + console.log(JSON.stringify(TestEnum)); + console.log((() => TestEnum.A).toString().includes('TestEnum')); + } + `, + "/enum.ts": ` + export enum TestEnum { + A, + B, + } + `, + }, + dce: true, + run: { + stdout: ` + {"0":"A","1":"B","A":0,"B":1} + false + `, + }, + }); + itBundled("edgecase/ImportMetaMain", { + files: { + "/entry.ts": /* js */ ` + import {other} from './other'; + console.log(capture(import.meta.main), capture(require.main === module), ...other); + `, + "/other.ts": ` + globalThis['ca' + 'pture'] = x => x; + + export const other = [capture(require.main === module), capture(import.meta.main)]; + `, + }, + capture: ["false", "false", "import.meta.main", "import.meta.main"], + onAfterBundle(api) { + // This should not be marked as a CommonJS module + api.expectFile("/out.js").not.toContain("require"); + api.expectFile("/out.js").not.toContain("module"); + }, + }); + itBundled("edgecase/ImportMetaMainTargetNode", { + files: { + "/entry.ts": /* js */ ` + import {other} from './other'; + console.log(capture(import.meta.main), capture(require.main === module), ...other); + `, + "/other.ts": ` + globalThis['ca' + 'pture'] = x => x; + + export const other = [capture(require.main === module), capture(import.meta.main)]; + `, + }, + target: "node", + capture: ["false", "false", "__require.main == __require.module", "__require.main == __require.module"], + onAfterBundle(api) { + // This should not be marked as a CommonJS module + api.expectFile("/out.js").not.toMatch(/\brequire\b/); // __require is ok + api.expectFile("/out.js").not.toMatch(/[^\.:]module/); // `.module` and `node:module` are ok. + }, + }); + itBundled("edgecase/IdentifierInEnum#13081", { + files: { + "/entry.ts": ` + let ZZZZZZZZZ = 1; + enum B { + C = ZZZZZZZZZ, + } + console.log(B.C); + `, + }, + run: { stdout: "1" }, + }); + itBundled("edgecase/DoNotMoveTaggedTemplateLiterals", { + files: { + "/entry.ts": ` + globalThis.z = () => console.log(2) + const y = await import('./second.ts'); + `, + "/second.ts": ` + console.log(1); + export const y = z\`zyx\`; + `, + }, + run: { stdout: "1\n2" }, + }); // TODO(@paperdave): test every case of this. I had already tested it manually, but it may break later const requireTranspilationListESM = [ diff --git a/test/bundler/bundler_jsx.test.ts b/test/bundler/bundler_jsx.test.ts index 5288701d66d06..fbdcf18ef4476 100644 --- a/test/bundler/bundler_jsx.test.ts +++ b/test/bundler/bundler_jsx.test.ts @@ -1,7 +1,5 @@ -import assert from "assert"; -import dedent from "dedent"; -import { BundlerTestInput, itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { BundlerTestInput, itBundled } from "./expectBundled"; const helpers = { "/node_modules/bun-test-helpers/index.js": /* js */ ` @@ -160,8 +158,8 @@ describe("bundler", () => { {"$$typeof":"Symbol(jsxdev)","type":"div","props":{"className":"container","children":{"$$typeof":"Symbol(jsxdev)","type":"hello","props":{"prop":2,"children":{"$$typeof":"Symbol(jsxdev)","type":"h1","props":{"onClick":"Function:onClick","children":"hello"},"key":"undefined","source":false,"self":"undefined"}},"key":"undefined","source":false,"self":"undefined"}},"key":"undefined","source":false,"self":"undefined"} `, prodStdout: ` - {"$$typeof":"Symbol(react.element)","type":"div","key":"null","ref":"null","props":{"children":"Hello World"},"_owner":"null"} - {"$$typeof":"Symbol(react.element)","type":"div","key":"null","ref":"null","props":{"className":"container","children":{"$$typeof":"Symbol(react.element)","type":"hello","key":"null","ref":"null","props":{"prop":2,"children":{"$$typeof":"Symbol(react.element)","type":"h1","key":"null","ref":"null","props":{"onClick":"Function:onClick","children":"hello"},"_owner":"null"}},"_owner":"null"}},"_owner":"null"} + {"$$typeof":"Symbol(jsx)","type":"div","props":{"children":"Hello World"},"key":"undefined"} + {"$$typeof":"Symbol(jsx)","type":"div","props":{"className":"container","children":{"$$typeof":"Symbol(jsx)","type":"hello","props":{"prop":2,"children":{"$$typeof":"Symbol(jsx)","type":"h1","props":{"onClick":"Function:onClick","children":"hello"},"key":"undefined"}},"key":"undefined"}},"key":"undefined"} `, }); // bun does not do the production transform for fragments as good as it could be right now. @@ -182,7 +180,7 @@ describe("bundler", () => { {"$$typeof":"Symbol(jsxdev)","type":"Symbol(jsxdev.fragment)","props":{"children":"Fragment"},"key":"undefined","source":false,"self":"undefined"} `, prodStdout: ` - {"$$typeof":"Symbol(react.element)","type":"Symbol("jsx.fragment")","key":"null","ref":"null","props":{"children":"Fragment"},"_owner":"null"} + {"$$typeof":"Symbol(jsx)","type":"Symbol("jsx.fragment")","key":"null","ref":"null","props":{"children":"Fragment"},"_owner":"null"} `, }); itBundledDevAndProd("jsx/ImportSource", { diff --git a/test/bundler/bundler_kit_dev.test.ts b/test/bundler/bundler_kit_dev.test.ts new file mode 100644 index 0000000000000..753a33c671ef7 --- /dev/null +++ b/test/bundler/bundler_kit_dev.test.ts @@ -0,0 +1,39 @@ +import { itBundled } from "./expectBundled"; +import { describe, expect } from "bun:test"; + +describe("bundler", async () => { + itBundled('kit_dev/HelloWorld', { + files: { + '/a.js': `console.log("Hello, world!")`, + }, + format: 'internal_kit_dev', + target: 'bun', + run: { stdout: 'Hello, world!' }, + onAfterBundle(api) { + // `importSync` is one of the functions the runtime includes. + // it is on a property access so it will not be mangled + api.expectFile('out.js').toContain('importSync'); + }, + }); + itBundled('kit_dev/SimpleCommonJS', { + files: { + '/a.js': `console.log(require('./b').message)`, + '/b.js': `module.exports = { message: "Hello, world!" }`, + }, + format: 'internal_kit_dev', + target: 'bun', + run: { stdout: 'Hello, world!' }, + }); + itBundled('kit_dev/SimpleESM', { + files: { + '/a.js': ` + import message from './b'; + console.log(message); + `, + '/b.js': `export default "Hello, world!"`, + }, + format: 'internal_kit_dev', + target: 'bun', + run: { stdout: 'Hello, world!' }, + }); +}); diff --git a/test/bundler/bundler_loader.test.ts b/test/bundler/bundler_loader.test.ts index 26c1c5bd511dc..7abc86bd04a68 100644 --- a/test/bundler/bundler_loader.test.ts +++ b/test/bundler/bundler_loader.test.ts @@ -1,6 +1,6 @@ -import { fileURLToPath, pathToFileURL } from "bun"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { fileURLToPath } from "bun"; +import { describe } from "bun:test"; +import { itBundled } from "./expectBundled"; describe("bundler", async () => { for (let target of ["bun", "node"] as const) { diff --git a/test/bundler/bundler_minify.test.ts b/test/bundler/bundler_minify.test.ts index a22c2a6d0dc50..97c4107bb0aca 100644 --- a/test/bundler/bundler_minify.test.ts +++ b/test/bundler/bundler_minify.test.ts @@ -1,6 +1,5 @@ -import assert from "assert"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { itBundled("minify/TemplateStringFolding", { @@ -56,6 +55,17 @@ describe("bundler", () => { minifySyntax: true, target: "bun", }); + itBundled("minify/StringAdditionFolding", { + files: { + "/entry.js": /* js */ ` + capture("Objects are not valid as a React child (found: " + (childString === "[object Object]" ? "object with keys {" + Object.keys(node).join(", ") + "}" : childString) + "). " + "If you meant to render a collection of children, use an array " + "instead.") + `, + }, + capture: [ + '"Objects are not valid as a React child (found: " + (childString === "[object Object]" ? "object with keys {" + Object.keys(node).join(", ") + "}" : childString) + "). If you meant to render a collection of children, use an array instead."', + ], + minifySyntax: true, + }); itBundled("minify/FunctionExpressionRemoveName", { todo: true, files: { @@ -122,7 +132,7 @@ describe("bundler", () => { run: { stdout: "4 2 3\n4 5 3\n4 5 6" }, onAfterBundle(api) { const code = api.readFile("/out.js"); - assert([...code.matchAll(/var /g)].length === 1, "expected only 1 variable declaration statement"); + expect([...code.matchAll(/var /g)]).toHaveLength(1); }, }); itBundled("minify/Infinity", { @@ -157,8 +167,8 @@ describe("bundler", () => { "NaN", "1 / 0", "-1 / 0", - "~(1 / 0)", - "~(-1 / 0)", + "-1", + "-1", ], minifySyntax: true, }); @@ -181,22 +191,7 @@ describe("bundler", () => { capture(~-Infinity); `, }, - capture: [ - "1/0", - "-1/0", - "1/0", - "-1/0", - "1/0", - "-1/0", - "NaN", - "NaN", - "NaN", - "NaN", - "1/0", - "1/0", - "~(1/0)", - "~(-1/0)", - ], + capture: ["1/0", "-1/0", "1/0", "-1/0", "1/0", "-1/0", "NaN", "NaN", "NaN", "NaN", "1/0", "1/0", "-1", "-1"], minifySyntax: true, minifyWhitespace: true, }); @@ -402,4 +397,74 @@ describe("bundler", () => { stdout: "PASS", }, }); + itBundled("minify/RequireInDeadBranch", { + files: { + "/entry.ts": /* js */ ` + if (0 !== 0) { + require; + } + `, + }, + outfile: "/out.js", + minifySyntax: true, + onAfterBundle(api) { + // This should not be marked as a CommonJS module + api.expectFile("/out.js").not.toContain("require"); + api.expectFile("/out.js").not.toContain("module"); + }, + }); + itBundled("minify/TypeOfRequire", { + files: { + "/entry.ts": /* js */ ` + capture(typeof require); + `, + }, + outfile: "/out.js", + capture: ['"function"'], + minifySyntax: true, + onAfterBundle(api) { + // This should not be marked as a CommonJS module + api.expectFile("/out.js").not.toContain("require"); + api.expectFile("/out.js").not.toContain("module"); + }, + }); + itBundled("minify/RequireMainToImportMetaMain", { + files: { + "/entry.ts": /* js */ ` + capture(require.main === module); + capture(require.main !== module); + capture(require.main == module); + capture(require.main != module); + capture(!(require.main === module)); + capture(!(require.main !== module)); + capture(!(require.main == module)); + capture(!(require.main != module)); + capture(!!(require.main === module)); + capture(!!(require.main !== module)); + capture(!!(require.main == module)); + capture(!!(require.main != module)); + `, + }, + outfile: "/out.js", + capture: [ + "import.meta.main", + "!import.meta.main", + "import.meta.main", + "!import.meta.main", + "!import.meta.main", + "import.meta.main", + "!import.meta.main", + "import.meta.main", + "import.meta.main", + "!import.meta.main", + "import.meta.main", + "!import.meta.main", + ], + minifySyntax: true, + onAfterBundle(api) { + // This should not be marked as a CommonJS module + api.expectFile("/out.js").not.toContain("require"); + api.expectFile("/out.js").not.toContain("module"); + }, + }); }); diff --git a/test/bundler/bundler_naming.test.ts b/test/bundler/bundler_naming.test.ts index 262719daace36..8f4aeb52815cc 100644 --- a/test/bundler/bundler_naming.test.ts +++ b/test/bundler/bundler_naming.test.ts @@ -1,7 +1,5 @@ -import assert from "assert"; -import dedent from "dedent"; -import { ESBUILD, itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { ESBUILD, itBundled } from "./expectBundled"; describe("bundler", () => { itBundled("naming/EntryNamingCollission", { diff --git a/test/bundler/bundler_npm.test.ts b/test/bundler/bundler_npm.test.ts index fe4a806b5d104..b765e58598072 100644 --- a/test/bundler/bundler_npm.test.ts +++ b/test/bundler/bundler_npm.test.ts @@ -1,9 +1,10 @@ -import { ESBUILD, itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { isWindows } from "harness"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { itBundled("npm/ReactSSR", { - todo: process.platform === "win32", // TODO(@paperdave) + todo: isWindows, // TODO(@paperdave) install: ["react@18.3.1", "react-dom@18.3.1"], files: { "/entry.tsx": /* tsx */ ` @@ -25,14 +26,14 @@ describe("bundler", () => { ); - const port = 42001; + const port = 0; using server = Bun.serve({ port, async fetch(req) { return new Response(await renderToReadableStream(), headers); }, }); - const res = await fetch("http://localhost:" + port); + const res = await fetch("http://localhost:" + server.port); if (res.status !== 200) throw "status error"; console.log(await res.text()); `, @@ -56,15 +57,18 @@ describe("bundler", () => { "../entry.tsx", ], mappings: [ - ["react.development.js:524:'getContextName'", "1:5404:r1"], - ["react.development.js:2495:'actScopeDepth'", "1:26072:GJ++"], - ["react.development.js:696:''Component'", '1:7470:\'Component "%s"'], - ["entry.tsx:6:'\"Content-Type\"'", '1:221669:"Content-Type"'], - ["entry.tsx:11:''", "1:221925:void"], - ["entry.tsx:23:'await'", "1:222030:await"], + ["react.development.js:524:'getContextName'", "1:5428:Y1"], + ["react.development.js:2495:'actScopeDepth'", "1:26053:GJ++"], + ["react.development.js:696:''Component'", '1:7490:\'Component "%s"'], + ["entry.tsx:6:'\"Content-Type\"'", '1:221655:"Content-Type"'], + ["entry.tsx:11:''", "1:221911:void"], + ["entry.tsx:23:'await'", "1:222013:await"], ], }, }, + expectExactFilesize: { + "out/entry.js": 222283, + }, run: { stdout: "

Hello World

This is an example.

", }, diff --git a/test/bundler/bundler_plugin.test.ts b/test/bundler/bundler_plugin.test.ts index 88481054fe43d..0918ff33a91fd 100644 --- a/test/bundler/bundler_plugin.test.ts +++ b/test/bundler/bundler_plugin.test.ts @@ -1,8 +1,6 @@ -import assert from "assert"; -import dedent from "dedent"; -import path from "path"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { dirname, join, resolve } from "node:path"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { const loadFixture = { @@ -35,7 +33,7 @@ describe("bundler", () => { plugins(builder) { builder.onResolve({ filter: /\.magic$/ }, args => { return { - path: path.resolve(path.dirname(args.importer), args.path.replace(/\.magic$/, ".ts")), + path: resolve(dirname(args.importer), args.path.replace(/\.magic$/, ".ts")), }; }); }, @@ -817,7 +815,7 @@ describe("bundler", () => { plugins(build) { const opts = (build as any).initialOptions; expect(opts.bundle).toEqual(true); - expect(opts.entryPoints).toEqual([root + path.sep + "index.ts"]); + expect(opts.entryPoints).toEqual([join(root, "index.ts")]); expect(opts.external).toEqual(["esbuild"]); expect(opts.format).toEqual(undefined); expect(opts.minify).toEqual(false); diff --git a/test/bundler/bundler_regressions.test.ts b/test/bundler/bundler_regressions.test.ts index 615f431104d65..315515b0d9a09 100644 --- a/test/bundler/bundler_regressions.test.ts +++ b/test/bundler/bundler_regressions.test.ts @@ -1,7 +1,5 @@ -import assert from "assert"; -import dedent from "dedent"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "./expectBundled"; describe("bundler", () => { // https://x.com/jeroendotdot/status/1740651288239460384?s=46&t=0Uhw6mmGT650_9M2pXUsCw @@ -226,4 +224,54 @@ describe("bundler", () => { }, entryPointsRaw: ["test/entry.ts", "--external", "*"], }); + + itBundled("regression/NamespaceTracking#12337", { + files: { + "/entry.ts": /* ts */ ` + (0, eval)('globalThis.ca' + 'pture = () => {};') + + export namespace Test { + export function anInstance(): Test { + return { + level1: { + level2: { + level3: Level1.Level2.Level3.anInstance(), + } + }, + } + } + + export namespace Level1 { + export namespace Level2 { + export function anInstance(): Level2 { + return { + level3: Level3.anInstance(), + } + } + export enum Enum { + Value = 1, + } + export namespace Level3 { + export type Value = Level3['value'] + export function anInstance(): Level3 { + return { + value: 'Hello, World!', + } + } + capture(Enum.Value); + } + } + capture(Level2.Enum.Value); + } + } + + if(Test.anInstance().level1.level2.level3.value !== 'Hello, World!') + throw new Error('fail') + + capture(Test.Level1.Level2.Enum.Value); + `, + }, + run: true, + capture: ["1 /* Value */", "1 /* Value */", "1 /* Value */"], + }); }); diff --git a/test/bundler/bundler_string.test.ts b/test/bundler/bundler_string.test.ts index 6d2e56f068425..2b5901d782686 100644 --- a/test/bundler/bundler_string.test.ts +++ b/test/bundler/bundler_string.test.ts @@ -1,7 +1,5 @@ -import assert from "assert"; -import dedent from "dedent"; -import { itBundled, testForFile } from "./expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { dedent, itBundled } from "./expectBundled"; interface TemplateStringTest { expr: string; @@ -82,6 +80,12 @@ const templateStringTests: Record = { FoldNested6: { expr: "`a\0${5}c\\${{$${`d`}e`", print: true }, EscapedDollar: { expr: "`\\${'a'}`", captureRaw: "\"${'a'}\"" }, EscapedDollar2: { expr: "`\\${'a'}\\${'b'}`", captureRaw: "\"${'a'}${'b'}\"" }, + StringAddition: { expr: "`${1}\u2796` + 'rest'", print: true }, + StringAddition2: { expr: "`\u2796${1}` + `a${Number(1)}b`", print: true }, + StringAddition3: { expr: '`0${"\u2796"}` + `a${Number(1)}b`', print: true }, + StringAddition4: { expr: "`${1}z` + `\u2796${Number(1)}rest`", print: true }, + StringAddition5: { expr: "`\u2796${1}z` + `\u2796${Number(1)}rest`", print: true }, + StringAddition6: { expr: "`${1}` + '\u2796rest'", print: true }, }; describe("bundler", () => { diff --git a/test/bundler/cli.test.ts b/test/bundler/cli.test.ts index ccfaa4e646a9b..4830840f388da 100644 --- a/test/bundler/cli.test.ts +++ b/test/bundler/cli.test.ts @@ -1,7 +1,6 @@ -import { bunEnv, bunExe, tmpdirSync } from "harness"; import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; import fs from "node:fs"; -import { tmpdir } from "node:os"; import path from "node:path"; describe("bun build", () => { @@ -18,34 +17,34 @@ describe("bun build", () => { test("generating a standalone binary in nested path, issue #4195", () => { function testCompile(outfile: string) { - const { exitCode } = Bun.spawnSync({ - cmd: [ - bunExe(), - "build", - path.join(import.meta.dir, "./fixtures/trivial/index.js"), - "--compile", - "--outfile", - outfile, - ], - env: bunEnv, - }); - expect(exitCode).toBe(0); + expect([ + "build", + path.join(import.meta.dir, "./fixtures/trivial/index.js"), + "--compile", + "--outfile", + outfile, + ]).toRun(); } function testExec(outfile: string) { - const { exitCode } = Bun.spawnSync({ + const { exitCode, stderr } = Bun.spawnSync({ cmd: [outfile], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", }); + expect(stderr.toString("utf8")).toBeEmpty(); expect(exitCode).toBe(0); } + const tmpdir = tmpdirSync(); { - const baseDir = `${tmpdir()}/bun-build-outfile-${Date.now()}`; + const baseDir = `${tmpdir}/bun-build-outfile-${Date.now()}`; const outfile = path.join(baseDir, "index.exe"); testCompile(outfile); testExec(outfile); fs.rmSync(baseDir, { recursive: true, force: true }); } { - const baseDir = `${tmpdir()}/bun-build-outfile2-${Date.now()}`; + const baseDir = `${tmpdir}/bun-build-outfile2-${Date.now()}`; const outfile = path.join(baseDir, "b/u/n", "index.exe"); testCompile(outfile); testExec(outfile); @@ -57,15 +56,12 @@ describe("bun build", () => { const tmp = tmpdirSync(); const src = path.join(tmp, "index.js"); fs.writeFileSync(src, '\ufeffconsole.log("hello world");', { encoding: "utf8" }); - const { exitCode } = Bun.spawnSync({ - cmd: [bunExe(), "build", src], - env: bunEnv, - }); - expect(exitCode).toBe(0); + expect(["build", src]).toRun(); }); test("__dirname and __filename are printed correctly", () => { - const baseDir = `${tmpdir()}/bun-build-dirname-filename-${Date.now()}`; + const tmpdir = tmpdirSync(); + const baseDir = `${tmpdir}/bun-build-dirname-filename-${Date.now()}`; fs.mkdirSync(baseDir, { recursive: true }); fs.mkdirSync(path.join(baseDir, "我")), { recursive: true }; fs.writeFileSync(path.join(baseDir, "我", "我.ts"), "console.log(__dirname); console.log(__filename);"); diff --git a/test/bundler/esbuild/css.test.ts b/test/bundler/esbuild/css.test.ts index f1bfde5694552..ee52bbe2c2366 100644 --- a/test/bundler/esbuild/css.test.ts +++ b/test/bundler/esbuild/css.test.ts @@ -1,527 +1,527 @@ -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_css_test.go // For debug, all files are written to $TEMP/bun-bundle-tests/css -// describe("bundler", () => { -// itBundled("css/CSSEntryPoint", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// body { -// background: white; -// color: black } -// `, -// }, -// }); -// itBundled("css/CSSAtImportMissing", { -// files: { -// "/entry.css": `@import "./missing.css";`, -// }, -// bundleErrors: { -// "/entry.css": ['Could not resolve "./missing.css"'], -// }, -// }); -// itBundled("css/CSSAtImportExternal", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// @import "./internal.css"; -// @import "./external1.css"; -// @import "./external2.css"; -// @import "./charset1.css"; -// @import "./charset2.css"; -// @import "./external5.css" screen; -// `, -// "/internal.css": /* css */ ` -// @import "./external5.css" print; -// .before { color: red } -// `, -// "/charset1.css": /* css */ ` -// @charset "UTF-8"; -// @import "./external3.css"; -// @import "./external4.css"; -// @import "./external5.css"; -// @import "https://www.example.com/style1.css"; -// @import "https://www.example.com/style2.css"; -// @import "https://www.example.com/style3.css" print; -// .middle { color: green } -// `, -// "/charset2.css": /* css */ ` -// @charset "UTF-8"; -// @import "./external3.css"; -// @import "./external5.css" screen; -// @import "https://www.example.com/style1.css"; -// @import "https://www.example.com/style3.css"; -// .after { color: blue } -// `, -// }, -// }); -// itBundled("css/CSSAtImport", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// @import "./a.css"; -// @import "./b.css"; -// .entry { color: red } -// `, -// "/a.css": /* css */ ` -// @import "./shared.css"; -// .a { color: green } -// `, -// "/b.css": /* css */ ` -// @import "./shared.css"; -// .b { color: blue } -// `, -// "/shared.css": `.shared { color: black }`, -// }, -// }); -// itBundled("css/CSSFromJSMissingImport", { -// // GENERATED -// files: { -// "/entry.js": /* js */ ` -// import {missing} from "./a.css" -// console.log(missing) -// `, -// "/a.css": `.a { color: red }`, -// }, -// /* TODO FIX expectedCompileLog: `entry.js: ERROR: No matching export in "a.css" for import "missing" -// `, */ -// }); -// itBundled("css/CSSFromJSMissingStarImport", { -// // GENERATED -// files: { -// "/entry.js": /* js */ ` -// import * as ns from "./a.css" -// console.log(ns.missing) -// `, -// "/a.css": `.a { color: red }`, -// }, -// }); -// itBundled("css/ImportCSSFromJS", { -// // GENERATED -// files: { -// "/entry.js": /* js */ ` -// import "./a.js" -// import "./b.js" -// `, -// "/a.js": /* js */ ` -// import "./a.css"; -// console.log('a') -// `, -// "/a.css": `.a { color: red }`, -// "/b.js": /* js */ ` -// import "./b.css"; -// console.log('b') -// `, -// "/b.css": `.b { color: blue }`, -// }, -// }); -// itBundled("css/ImportCSSFromJSWriteToStdout", { -// // GENERATED -// files: { -// "/entry.js": `import "./entry.css"`, -// "/entry.css": `.entry { color: red }`, -// }, -// /* TODO FIX expectedScanLog: `entry.js: ERROR: Cannot import "entry.css" into a JavaScript file without an output path configured -// `, */ -// }); -// itBundled("css/ImportJSFromCSS", { -// // GENERATED -// files: { -// "/entry.js": `export default 123`, -// "/entry.css": `@import "./entry.js";`, -// }, -// entryPoints: ["/entry.css"], -// /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.js" into a CSS file -// NOTE: An "@import" rule can only be used to import another CSS file, and "entry.js" is not a CSS file (it was loaded with the "js" loader). -// `, */ -// }); -// itBundled("css/ImportJSONFromCSS", { -// // GENERATED -// files: { -// "/entry.json": `{}`, -// "/entry.css": `@import "./entry.json";`, -// }, -// entryPoints: ["/entry.css"], -// /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.json" into a CSS file -// NOTE: An "@import" rule can only be used to import another CSS file, and "entry.json" is not a CSS file (it was loaded with the "json" loader). -// `, */ -// }); -// itBundled("css/MissingImportURLInCSS", { -// // GENERATED -// files: { -// "/src/entry.css": /* css */ ` -// a { background: url(./one.png); } -// b { background: url("./two.png"); } -// `, -// }, -// /* TODO FIX expectedScanLog: `src/entry.css: ERROR: Could not resolve "./one.png" -// src/entry.css: ERROR: Could not resolve "./two.png" -// `, */ -// }); -// itBundled("css/ExternalImportURLInCSS", { -// // GENERATED -// files: { -// "/src/entry.css": /* css */ ` -// div:after { -// content: 'If this is recognized, the path should become "../src/external.png"'; -// background: url(./external.png); -// } +describe.todo("bundler", () => { + itBundled("css/CSSEntryPoint", { + // GENERATED + files: { + "/entry.css": /* css */ ` + body { + background: white; + color: black } + `, + }, + }); + itBundled("css/CSSAtImportMissing", { + files: { + "/entry.css": `@import "./missing.css";`, + }, + bundleErrors: { + "/entry.css": ['Could not resolve "./missing.css"'], + }, + }); + itBundled("css/CSSAtImportExternal", { + // GENERATED + files: { + "/entry.css": /* css */ ` + @import "./internal.css"; + @import "./external1.css"; + @import "./external2.css"; + @import "./charset1.css"; + @import "./charset2.css"; + @import "./external5.css" screen; + `, + "/internal.css": /* css */ ` + @import "./external5.css" print; + .before { color: red } + `, + "/charset1.css": /* css */ ` + @charset "UTF-8"; + @import "./external3.css"; + @import "./external4.css"; + @import "./external5.css"; + @import "https://www.example.com/style1.css"; + @import "https://www.example.com/style2.css"; + @import "https://www.example.com/style3.css" print; + .middle { color: green } + `, + "/charset2.css": /* css */ ` + @charset "UTF-8"; + @import "./external3.css"; + @import "./external5.css" screen; + @import "https://www.example.com/style1.css"; + @import "https://www.example.com/style3.css"; + .after { color: blue } + `, + }, + }); + itBundled("css/CSSAtImport", { + // GENERATED + files: { + "/entry.css": /* css */ ` + @import "./a.css"; + @import "./b.css"; + .entry { color: red } + `, + "/a.css": /* css */ ` + @import "./shared.css"; + .a { color: green } + `, + "/b.css": /* css */ ` + @import "./shared.css"; + .b { color: blue } + `, + "/shared.css": `.shared { color: black }`, + }, + }); + itBundled("css/CSSFromJSMissingImport", { + // GENERATED + files: { + "/entry.js": /* js */ ` + import {missing} from "./a.css" + console.log(missing) + `, + "/a.css": `.a { color: red }`, + }, + /* TODO FIX expectedCompileLog: `entry.js: ERROR: No matching export in "a.css" for import "missing" + `, */ + }); + itBundled("css/CSSFromJSMissingStarImport", { + // GENERATED + files: { + "/entry.js": /* js */ ` + import * as ns from "./a.css" + console.log(ns.missing) + `, + "/a.css": `.a { color: red }`, + }, + }); + itBundled("css/ImportCSSFromJS", { + // GENERATED + files: { + "/entry.js": /* js */ ` + import "./a.js" + import "./b.js" + `, + "/a.js": /* js */ ` + import "./a.css"; + console.log('a') + `, + "/a.css": `.a { color: red }`, + "/b.js": /* js */ ` + import "./b.css"; + console.log('b') + `, + "/b.css": `.b { color: blue }`, + }, + }); + itBundled("css/ImportCSSFromJSWriteToStdout", { + // GENERATED + files: { + "/entry.js": `import "./entry.css"`, + "/entry.css": `.entry { color: red }`, + }, + /* TODO FIX expectedScanLog: `entry.js: ERROR: Cannot import "entry.css" into a JavaScript file without an output path configured + `, */ + }); + itBundled("css/ImportJSFromCSS", { + // GENERATED + files: { + "/entry.js": `export default 123`, + "/entry.css": `@import "./entry.js";`, + }, + entryPoints: ["/entry.css"], + /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.js" into a CSS file + NOTE: An "@import" rule can only be used to import another CSS file, and "entry.js" is not a CSS file (it was loaded with the "js" loader). + `, */ + }); + itBundled("css/ImportJSONFromCSS", { + // GENERATED + files: { + "/entry.json": `{}`, + "/entry.css": `@import "./entry.json";`, + }, + entryPoints: ["/entry.css"], + /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.json" into a CSS file + NOTE: An "@import" rule can only be used to import another CSS file, and "entry.json" is not a CSS file (it was loaded with the "json" loader). + `, */ + }); + itBundled("css/MissingImportURLInCSS", { + // GENERATED + files: { + "/src/entry.css": /* css */ ` + a { background: url(./one.png); } + b { background: url("./two.png"); } + `, + }, + /* TODO FIX expectedScanLog: `src/entry.css: ERROR: Could not resolve "./one.png" + src/entry.css: ERROR: Could not resolve "./two.png" + `, */ + }); + itBundled("css/ExternalImportURLInCSS", { + // GENERATED + files: { + "/src/entry.css": /* css */ ` + div:after { + content: 'If this is recognized, the path should become "../src/external.png"'; + background: url(./external.png); + } -// /* These URLs should be external automatically */ -// a { background: url(http://example.com/images/image.png) } -// b { background: url(https://example.com/images/image.png) } -// c { background: url(//example.com/images/image.png) } -// d { background: url(data:image/png;base64,iVBORw0KGgo=) } -// path { fill: url(#filter) } -// `, -// }, -// }); -// itBundled("css/InvalidImportURLInCSS", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { -// background: url(./js.js); -// background: url("./jsx.jsx"); -// background: url(./ts.ts); -// background: url('./tsx.tsx'); -// background: url(./json.json); -// background: url(./css.css); -// } -// `, -// "/js.js": `export default 123`, -// "/jsx.jsx": `export default 123`, -// "/ts.ts": `export default 123`, -// "/tsx.tsx": `export default 123`, -// "/json.json": `{ "test": true }`, -// "/css.css": `a { color: red }`, -// }, -// /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot use "js.js" as a URL -// NOTE: You can't use a "url()" token to reference the file "js.js" because it was loaded with the "js" loader, which doesn't provide a URL to embed in the resulting CSS. -// entry.css: ERROR: Cannot use "jsx.jsx" as a URL -// NOTE: You can't use a "url()" token to reference the file "jsx.jsx" because it was loaded with the "jsx" loader, which doesn't provide a URL to embed in the resulting CSS. -// entry.css: ERROR: Cannot use "ts.ts" as a URL -// NOTE: You can't use a "url()" token to reference the file "ts.ts" because it was loaded with the "ts" loader, which doesn't provide a URL to embed in the resulting CSS. -// entry.css: ERROR: Cannot use "tsx.tsx" as a URL -// NOTE: You can't use a "url()" token to reference the file "tsx.tsx" because it was loaded with the "tsx" loader, which doesn't provide a URL to embed in the resulting CSS. -// entry.css: ERROR: Cannot use "json.json" as a URL -// NOTE: You can't use a "url()" token to reference the file "json.json" because it was loaded with the "json" loader, which doesn't provide a URL to embed in the resulting CSS. -// entry.css: ERROR: Cannot use "css.css" as a URL -// NOTE: You can't use a "url()" token to reference a CSS file, and "css.css" is a CSS file (it was loaded with the "css" loader). -// `, */ -// }); -// itBundled("css/TextImportURLInCSSText", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { -// background: url(./example.txt); -// } -// `, -// "/example.txt": `This is some text.`, -// }, -// }); -// itBundled("css/DataURLImportURLInCSS", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { -// background: url(./example.png); -// } -// `, -// "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`, -// }, -// }); -// itBundled("css/BinaryImportURLInCSS", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { -// background: url(./example.png); -// } -// `, -// "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`, -// }, -// }); -// itBundled("css/Base64ImportURLInCSS", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { -// background: url(./example.png); -// } -// `, -// "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`, -// }, -// }); -// itBundled("css/FileImportURLInCSS", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// @import "./one.css"; -// @import "./two.css"; -// `, -// "/one.css": `a { background: url(./example.data) }`, -// "/two.css": `b { background: url(./example.data) }`, -// "/example.data": `This is some data.`, -// }, -// }); -// itBundled("css/IgnoreURLsInAtRulePrelude", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// /* This should not generate a path resolution error */ -// @supports (background: url(ignored.png)) { -// a { color: red } -// } -// `, -// }, -// }); -// itBundled("css/PackageURLsInCSS", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// @import "test.css"; + /* These URLs should be external automatically */ + a { background: url(http://example.com/images/image.png) } + b { background: url(https://example.com/images/image.png) } + c { background: url(//example.com/images/image.png) } + d { background: url(data:image/png;base64,iVBORw0KGgo=) } + path { fill: url(#filter) } + `, + }, + }); + itBundled("css/InvalidImportURLInCSS", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { + background: url(./js.js); + background: url("./jsx.jsx"); + background: url(./ts.ts); + background: url('./tsx.tsx'); + background: url(./json.json); + background: url(./css.css); + } + `, + "/js.js": `export default 123`, + "/jsx.jsx": `export default 123`, + "/ts.ts": `export default 123`, + "/tsx.tsx": `export default 123`, + "/json.json": `{ "test": true }`, + "/css.css": `a { color: red }`, + }, + /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot use "js.js" as a URL + NOTE: You can't use a "url()" token to reference the file "js.js" because it was loaded with the "js" loader, which doesn't provide a URL to embed in the resulting CSS. + entry.css: ERROR: Cannot use "jsx.jsx" as a URL + NOTE: You can't use a "url()" token to reference the file "jsx.jsx" because it was loaded with the "jsx" loader, which doesn't provide a URL to embed in the resulting CSS. + entry.css: ERROR: Cannot use "ts.ts" as a URL + NOTE: You can't use a "url()" token to reference the file "ts.ts" because it was loaded with the "ts" loader, which doesn't provide a URL to embed in the resulting CSS. + entry.css: ERROR: Cannot use "tsx.tsx" as a URL + NOTE: You can't use a "url()" token to reference the file "tsx.tsx" because it was loaded with the "tsx" loader, which doesn't provide a URL to embed in the resulting CSS. + entry.css: ERROR: Cannot use "json.json" as a URL + NOTE: You can't use a "url()" token to reference the file "json.json" because it was loaded with the "json" loader, which doesn't provide a URL to embed in the resulting CSS. + entry.css: ERROR: Cannot use "css.css" as a URL + NOTE: You can't use a "url()" token to reference a CSS file, and "css.css" is a CSS file (it was loaded with the "css" loader). + `, */ + }); + itBundled("css/TextImportURLInCSSText", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { + background: url(./example.txt); + } + `, + "/example.txt": `This is some text.`, + }, + }); + itBundled("css/DataURLImportURLInCSS", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { + background: url(./example.png); + } + `, + "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`, + }, + }); + itBundled("css/BinaryImportURLInCSS", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { + background: url(./example.png); + } + `, + "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`, + }, + }); + itBundled("css/Base64ImportURLInCSS", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { + background: url(./example.png); + } + `, + "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`, + }, + }); + itBundled("css/FileImportURLInCSS", { + // GENERATED + files: { + "/entry.css": /* css */ ` + @import "./one.css"; + @import "./two.css"; + `, + "/one.css": `a { background: url(./example.data) }`, + "/two.css": `b { background: url(./example.data) }`, + "/example.data": `This is some data.`, + }, + }); + itBundled("css/IgnoreURLsInAtRulePrelude", { + // GENERATED + files: { + "/entry.css": /* css */ ` + /* This should not generate a path resolution error */ + @supports (background: url(ignored.png)) { + a { color: red } + } + `, + }, + }); + itBundled("css/PackageURLsInCSS", { + // GENERATED + files: { + "/entry.css": /* css */ ` + @import "test.css"; -// a { background: url(a/1.png); } -// b { background: url(b/2.png); } -// c { background: url(c/3.png); } -// `, -// "/test.css": `.css { color: red }`, -// "/a/1.png": `a-1`, -// "/node_modules/b/2.png": `b-2-node_modules`, -// "/c/3.png": `c-3`, -// "/node_modules/c/3.png": `c-3-node_modules`, -// }, -// }); -// itBundled("css/CSSAtImportExtensionOrderCollision", { -// // GENERATED -// files: { -// "/entry.css": `@import "./test";`, -// "/test.js": `console.log('js')`, -// "/test.css": `.css { color: red }`, -// }, -// outfile: "/out.css", -// extensionOrder: [".js", ".css"], -// }); -// itBundled("css/CSSAtImportExtensionOrderCollisionUnsupported", { -// // GENERATED -// files: { -// "/entry.css": `@import "./test";`, -// "/test.js": `console.log('js')`, -// "/test.sass": `// some code`, -// }, -// outfile: "/out.css", -// extensionOrder: [".js", ".sass"], -// bundleErrors: { -// "/entry.css": ['ERROR: No loader is configured for ".sass" files: test.sass'], -// }, -// }); -// itBundled("css/CSSAtImportConditionsNoBundle", { -// // GENERATED -// files: { -// "/entry.css": `@import "./print.css" print;`, -// }, -// mode: "passthrough", -// }); -// itBundled("css/CSSAtImportConditionsBundleExternal", { -// // GENERATED -// files: { -// "/entry.css": `@import "https://example.com/print.css" print;`, -// }, -// }); -// itBundled("css/CSSAtImportConditionsBundleExternalConditionWithURL", { -// // GENERATED -// files: { -// "/entry.css": `@import "https://example.com/foo.css" (foo: url("foo.png")) and (bar: url("bar.png"));`, -// }, -// }); -// itBundled("css/CSSAtImportConditionsBundle", { -// // GENERATED -// files: { -// "/entry.css": `@import "./print.css" print;`, -// "/print.css": `body { color: red }`, -// }, -// /* TODO FIX expectedScanLog: `entry.css: ERROR: Bundling with conditional "@import" rules is not currently supported -// `, */ -// }); -// itBundled("css/CSSAndJavaScriptCodeSplittingESBuildIssue1064", { -// // GENERATED -// files: { -// "/a.js": /* js */ ` -// import shared from './shared.js' -// console.log(shared() + 1) -// `, -// "/b.js": /* js */ ` -// import shared from './shared.js' -// console.log(shared() + 2) -// `, -// "/c.css": /* css */ ` -// @import "./shared.css"; -// body { color: red } -// `, -// "/d.css": /* css */ ` -// @import "./shared.css"; -// body { color: blue } -// `, -// "/shared.js": `export default function() { return 3 }`, -// "/shared.css": `body { background: black }`, -// }, -// entryPoints: ["/a.js", "/b.js", "/c.css", "/d.css"], -// format: "esm", -// splitting: true, -// }); -// itBundled("css/CSSExternalQueryAndHashNoMatchESBuildIssue1822", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { background: url(foo/bar.png?baz) } -// b { background: url(foo/bar.png#baz) } -// `, -// }, -// outfile: "/out.css", -// /* TODO FIX expectedScanLog: `entry.css: ERROR: Could not resolve "foo/bar.png?baz" -// NOTE: You can mark the path "foo/bar.png?baz" as external to exclude it from the bundle, which will remove this error. -// entry.css: ERROR: Could not resolve "foo/bar.png#baz" -// NOTE: You can mark the path "foo/bar.png#baz" as external to exclude it from the bundle, which will remove this error. -// `, */ -// }); -// itBundled("css/CSSExternalQueryAndHashMatchESBuildIssue1822", { -// // GENERATED -// files: { -// "/entry.css": /* css */ ` -// a { background: url(foo/bar.png?baz) } -// b { background: url(foo/bar.png#baz) } -// `, -// }, -// outfile: "/out.css", -// }); -// itBundled("css/CSSNestingOldBrowser", { -// // GENERATED -// files: { -// "/nested-@layer.css": `a { @layer base { color: red; } }`, -// "/nested-@media.css": `a { @media screen { color: red; } }`, -// "/nested-ampersand-twice.css": `a { &, & { color: red; } }`, -// "/nested-ampersand-first.css": `a { &, b { color: red; } }`, -// "/nested-attribute.css": `a { [href] { color: red; } }`, -// "/nested-colon.css": `a { :hover { color: red; } }`, -// "/nested-dot.css": `a { .cls { color: red; } }`, -// "/nested-greaterthan.css": `a { > b { color: red; } }`, -// "/nested-hash.css": `a { #id { color: red; } }`, -// "/nested-plus.css": `a { + b { color: red; } }`, -// "/nested-tilde.css": `a { ~ b { color: red; } }`, -// "/toplevel-ampersand-twice.css": `&, & { color: red; }`, -// "/toplevel-ampersand-first.css": `&, a { color: red; }`, -// "/toplevel-ampersand-second.css": `a, & { color: red; }`, -// "/toplevel-attribute.css": `[href] { color: red; }`, -// "/toplevel-colon.css": `:hover { color: red; }`, -// "/toplevel-dot.css": `.cls { color: red; }`, -// "/toplevel-greaterthan.css": `> b { color: red; }`, -// "/toplevel-hash.css": `#id { color: red; }`, -// "/toplevel-plus.css": `+ b { color: red; }`, -// "/toplevel-tilde.css": `~ b { color: red; }`, -// }, -// entryPoints: [ -// "/nested-@layer.css", -// "/nested-@media.css", -// "/nested-ampersand-twice.css", -// "/nested-ampersand-first.css", -// "/nested-attribute.css", -// "/nested-colon.css", -// "/nested-dot.css", -// "/nested-greaterthan.css", -// "/nested-hash.css", -// "/nested-plus.css", -// "/nested-tilde.css", -// "/toplevel-ampersand-twice.css", -// "/toplevel-ampersand-first.css", -// "/toplevel-ampersand-second.css", -// "/toplevel-attribute.css", -// "/toplevel-colon.css", -// "/toplevel-dot.css", -// "/toplevel-greaterthan.css", -// "/toplevel-hash.css", -// "/toplevel-plus.css", -// "/toplevel-tilde.css", -// ], -// unsupportedCSSFeatures: "Nesting", -// /* TODO FIX expectedScanLog: `nested-@layer.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-@media.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-attribute.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-colon.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-dot.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-hash.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// nested-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-ampersand-second.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// toplevel-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) -// `, */ -// }); -// itBundled("css/MetafileCSSBundleTwoToOne", { -// files: { -// "/foo/entry.js": /* js */ ` -// import '../common.css' -// console.log('foo') -// `, -// "/bar/entry.js": /* js */ ` -// import '../common.css' -// console.log('bar') -// `, -// "/common.css": `body { color: red }`, -// }, -// metafile: true, -// entryPoints: ["/foo/entry.js", "/bar/entry.js"], -// entryNaming: "[ext]/[hash]", -// outdir: "/", -// }); -// itBundled("css/DeduplicateRules", { -// // GENERATED -// files: { -// "/yes0.css": `a { color: red; color: green; color: red }`, -// "/yes1.css": `a { color: red } a { color: green } a { color: red }`, -// "/yes2.css": `@media screen { a { color: red } } @media screen { a { color: red } }`, -// "/no0.css": `@media screen { a { color: red } } @media screen { & a { color: red } }`, -// "/no1.css": `@media screen { a { color: red } } @media screen { a[x] { color: red } }`, -// "/no2.css": `@media screen { a { color: red } } @media screen { a.x { color: red } }`, -// "/no3.css": `@media screen { a { color: red } } @media screen { a#x { color: red } }`, -// "/no4.css": `@media screen { a { color: red } } @media screen { a:x { color: red } }`, -// "/no5.css": `@media screen { a:x { color: red } } @media screen { a:x(y) { color: red } }`, -// "/no6.css": `@media screen { a b { color: red } } @media screen { a + b { color: red } }`, -// "/across-files.css": `@import 'across-files-0.css'; @import 'across-files-1.css'; @import 'across-files-2.css';`, -// "/across-files-0.css": `a { color: red; color: red }`, -// "/across-files-1.css": `a { color: green }`, -// "/across-files-2.css": `a { color: red }`, -// "/across-files-url.css": `@import 'across-files-url-0.css'; @import 'across-files-url-1.css'; @import 'across-files-url-2.css';`, -// "/across-files-url-0.css": `@import 'http://example.com/some.css'; @font-face { src: url(http://example.com/some.font); }`, -// "/across-files-url-1.css": `@font-face { src: url(http://example.com/some.other.font); }`, -// "/across-files-url-2.css": `@font-face { src: url(http://example.com/some.font); }`, -// }, -// entryPoints: [ -// "/yes0.css", -// "/yes1.css", -// "/yes2.css", -// "/no0.css", -// "/no1.css", -// "/no2.css", -// "/no3.css", -// "/no4.css", -// "/no5.css", -// "/no6.css", -// "/across-files.css", -// "/across-files-url.css", -// ], -// }); -// }); + a { background: url(a/1.png); } + b { background: url(b/2.png); } + c { background: url(c/3.png); } + `, + "/test.css": `.css { color: red }`, + "/a/1.png": `a-1`, + "/node_modules/b/2.png": `b-2-node_modules`, + "/c/3.png": `c-3`, + "/node_modules/c/3.png": `c-3-node_modules`, + }, + }); + itBundled("css/CSSAtImportExtensionOrderCollision", { + // GENERATED + files: { + "/entry.css": `@import "./test";`, + "/test.js": `console.log('js')`, + "/test.css": `.css { color: red }`, + }, + outfile: "/out.css", + extensionOrder: [".js", ".css"], + }); + itBundled("css/CSSAtImportExtensionOrderCollisionUnsupported", { + // GENERATED + files: { + "/entry.css": `@import "./test";`, + "/test.js": `console.log('js')`, + "/test.sass": `// some code`, + }, + outfile: "/out.css", + extensionOrder: [".js", ".sass"], + bundleErrors: { + "/entry.css": ['ERROR: No loader is configured for ".sass" files: test.sass'], + }, + }); + itBundled("css/CSSAtImportConditionsNoBundle", { + // GENERATED + files: { + "/entry.css": `@import "./print.css" print;`, + }, + mode: "passthrough", + }); + itBundled("css/CSSAtImportConditionsBundleExternal", { + // GENERATED + files: { + "/entry.css": `@import "https://example.com/print.css" print;`, + }, + }); + itBundled("css/CSSAtImportConditionsBundleExternalConditionWithURL", { + // GENERATED + files: { + "/entry.css": `@import "https://example.com/foo.css" (foo: url("foo.png")) and (bar: url("bar.png"));`, + }, + }); + itBundled("css/CSSAtImportConditionsBundle", { + // GENERATED + files: { + "/entry.css": `@import "./print.css" print;`, + "/print.css": `body { color: red }`, + }, + /* TODO FIX expectedScanLog: `entry.css: ERROR: Bundling with conditional "@import" rules is not currently supported + `, */ + }); + itBundled("css/CSSAndJavaScriptCodeSplittingESBuildIssue1064", { + // GENERATED + files: { + "/a.js": /* js */ ` + import shared from './shared.js' + console.log(shared() + 1) + `, + "/b.js": /* js */ ` + import shared from './shared.js' + console.log(shared() + 2) + `, + "/c.css": /* css */ ` + @import "./shared.css"; + body { color: red } + `, + "/d.css": /* css */ ` + @import "./shared.css"; + body { color: blue } + `, + "/shared.js": `export default function() { return 3 }`, + "/shared.css": `body { background: black }`, + }, + entryPoints: ["/a.js", "/b.js", "/c.css", "/d.css"], + format: "esm", + splitting: true, + }); + itBundled("css/CSSExternalQueryAndHashNoMatchESBuildIssue1822", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { background: url(foo/bar.png?baz) } + b { background: url(foo/bar.png#baz) } + `, + }, + outfile: "/out.css", + /* TODO FIX expectedScanLog: `entry.css: ERROR: Could not resolve "foo/bar.png?baz" + NOTE: You can mark the path "foo/bar.png?baz" as external to exclude it from the bundle, which will remove this error. + entry.css: ERROR: Could not resolve "foo/bar.png#baz" + NOTE: You can mark the path "foo/bar.png#baz" as external to exclude it from the bundle, which will remove this error. + `, */ + }); + itBundled("css/CSSExternalQueryAndHashMatchESBuildIssue1822", { + // GENERATED + files: { + "/entry.css": /* css */ ` + a { background: url(foo/bar.png?baz) } + b { background: url(foo/bar.png#baz) } + `, + }, + outfile: "/out.css", + }); + itBundled("css/CSSNestingOldBrowser", { + // GENERATED + files: { + "/nested-@layer.css": `a { @layer base { color: red; } }`, + "/nested-@media.css": `a { @media screen { color: red; } }`, + "/nested-ampersand-twice.css": `a { &, & { color: red; } }`, + "/nested-ampersand-first.css": `a { &, b { color: red; } }`, + "/nested-attribute.css": `a { [href] { color: red; } }`, + "/nested-colon.css": `a { :hover { color: red; } }`, + "/nested-dot.css": `a { .cls { color: red; } }`, + "/nested-greaterthan.css": `a { > b { color: red; } }`, + "/nested-hash.css": `a { #id { color: red; } }`, + "/nested-plus.css": `a { + b { color: red; } }`, + "/nested-tilde.css": `a { ~ b { color: red; } }`, + "/toplevel-ampersand-twice.css": `&, & { color: red; }`, + "/toplevel-ampersand-first.css": `&, a { color: red; }`, + "/toplevel-ampersand-second.css": `a, & { color: red; }`, + "/toplevel-attribute.css": `[href] { color: red; }`, + "/toplevel-colon.css": `:hover { color: red; }`, + "/toplevel-dot.css": `.cls { color: red; }`, + "/toplevel-greaterthan.css": `> b { color: red; }`, + "/toplevel-hash.css": `#id { color: red; }`, + "/toplevel-plus.css": `+ b { color: red; }`, + "/toplevel-tilde.css": `~ b { color: red; }`, + }, + entryPoints: [ + "/nested-@layer.css", + "/nested-@media.css", + "/nested-ampersand-twice.css", + "/nested-ampersand-first.css", + "/nested-attribute.css", + "/nested-colon.css", + "/nested-dot.css", + "/nested-greaterthan.css", + "/nested-hash.css", + "/nested-plus.css", + "/nested-tilde.css", + "/toplevel-ampersand-twice.css", + "/toplevel-ampersand-first.css", + "/toplevel-ampersand-second.css", + "/toplevel-attribute.css", + "/toplevel-colon.css", + "/toplevel-dot.css", + "/toplevel-greaterthan.css", + "/toplevel-hash.css", + "/toplevel-plus.css", + "/toplevel-tilde.css", + ], + unsupportedCSSFeatures: "Nesting", + /* TODO FIX expectedScanLog: `nested-@layer.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-@media.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-attribute.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-colon.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-dot.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-hash.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + nested-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-ampersand-second.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + toplevel-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10) + `, */ + }); + itBundled("css/MetafileCSSBundleTwoToOne", { + files: { + "/foo/entry.js": /* js */ ` + import '../common.css' + console.log('foo') + `, + "/bar/entry.js": /* js */ ` + import '../common.css' + console.log('bar') + `, + "/common.css": `body { color: red }`, + }, + metafile: true, + entryPoints: ["/foo/entry.js", "/bar/entry.js"], + entryNaming: "[ext]/[hash]", + outdir: "/", + }); + itBundled("css/DeduplicateRules", { + // GENERATED + files: { + "/yes0.css": `a { color: red; color: green; color: red }`, + "/yes1.css": `a { color: red } a { color: green } a { color: red }`, + "/yes2.css": `@media screen { a { color: red } } @media screen { a { color: red } }`, + "/no0.css": `@media screen { a { color: red } } @media screen { & a { color: red } }`, + "/no1.css": `@media screen { a { color: red } } @media screen { a[x] { color: red } }`, + "/no2.css": `@media screen { a { color: red } } @media screen { a.x { color: red } }`, + "/no3.css": `@media screen { a { color: red } } @media screen { a#x { color: red } }`, + "/no4.css": `@media screen { a { color: red } } @media screen { a:x { color: red } }`, + "/no5.css": `@media screen { a:x { color: red } } @media screen { a:x(y) { color: red } }`, + "/no6.css": `@media screen { a b { color: red } } @media screen { a + b { color: red } }`, + "/across-files.css": `@import 'across-files-0.css'; @import 'across-files-1.css'; @import 'across-files-2.css';`, + "/across-files-0.css": `a { color: red; color: red }`, + "/across-files-1.css": `a { color: green }`, + "/across-files-2.css": `a { color: red }`, + "/across-files-url.css": `@import 'across-files-url-0.css'; @import 'across-files-url-1.css'; @import 'across-files-url-2.css';`, + "/across-files-url-0.css": `@import 'http://example.com/some.css'; @font-face { src: url(http://example.com/some.font); }`, + "/across-files-url-1.css": `@font-face { src: url(http://example.com/some.other.font); }`, + "/across-files-url-2.css": `@font-face { src: url(http://example.com/some.font); }`, + }, + entryPoints: [ + "/yes0.css", + "/yes1.css", + "/yes2.css", + "/no0.css", + "/no1.css", + "/no2.css", + "/no3.css", + "/no4.css", + "/no5.css", + "/no6.css", + "/across-files.css", + "/across-files-url.css", + ], + }); +}); diff --git a/test/bundler/esbuild/dce.test.ts b/test/bundler/esbuild/dce.test.ts index 618eb5d281b9c..f718bd96708e8 100644 --- a/test/bundler/esbuild/dce.test.ts +++ b/test/bundler/esbuild/dce.test.ts @@ -1,7 +1,6 @@ -import assert from "assert"; -import dedent from "dedent"; -import { ESBUILD, itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { isWindows } from "harness"; +import { dedent, itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_dce_test.go @@ -109,7 +108,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsTrueKeepES6", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import "demo-pkg" @@ -131,7 +129,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsTrueKeepCommonJS", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import "demo-pkg" @@ -153,7 +150,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseKeepBareImportAndRequireES6", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import "demo-pkg" @@ -176,7 +172,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseKeepBareImportAndRequireCommonJS", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import "demo-pkg" @@ -199,7 +194,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseRemoveBareImportES6", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import "demo-pkg" @@ -221,7 +215,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseRemoveBareImportCommonJS", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import "demo-pkg" @@ -243,7 +236,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseRemoveNamedImportES6", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -265,7 +257,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseRemoveNamedImportCommonJS", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -287,7 +278,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseRemoveStarImportES6", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import * as ns from "demo-pkg" @@ -309,7 +299,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseRemoveStarImportCommonJS", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import * as ns from "demo-pkg" @@ -353,7 +342,7 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsArrayKeep", { - todo: true, + todo: isWindows, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -431,7 +420,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsArrayKeepMainImplicitModule", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -459,7 +447,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsArrayKeepMainImplicitMain", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -492,7 +479,7 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsArrayKeepModuleUseModule", { - todo: true, + todo: isWindows, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -520,7 +507,7 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsArrayKeepModuleUseMain", { - todo: true, + todo: isWindows, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -548,7 +535,7 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsArrayKeepModuleImplicitModule", { - todo: true, + todo: isWindows, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "demo-pkg" @@ -780,7 +767,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesDiamond", { - todo: true, files: { "/Users/user/project/src/entry.js": /* js */ ` import {foo} from "a" @@ -809,7 +795,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseOneFork", { - todo: true, files: { "/Users/user/project/src/entry.js": `import("a").then(x => console.log(x.foo))`, "/Users/user/project/node_modules/a/index.js": `export {foo} from "b"`, @@ -830,7 +815,6 @@ describe("bundler", () => { }, }); itBundled("dce/PackageJsonSideEffectsFalseAllFork", { - todo: true, files: { "/Users/user/project/src/entry.js": `import("a").then(x => console.log(x.foo))`, "/Users/user/project/node_modules/a/index.js": `export {foo} from "b"`, @@ -853,7 +837,6 @@ describe("bundler", () => { }, }); itBundled("dce/JSONLoaderRemoveUnused", { - todo: true, files: { "/entry.js": /* js */ ` import unused from "./example.json" @@ -867,7 +850,6 @@ describe("bundler", () => { }, }); itBundled("dce/TextLoaderRemoveUnused", { - todo: true, files: { "/entry.js": /* js */ ` import unused from "./example.txt" @@ -936,7 +918,6 @@ describe("bundler", () => { }, }); itBundled("dce/RemoveUnusedImportMeta", { - todo: true, files: { "/entry.js": /* js */ ` function foo() { @@ -950,109 +931,119 @@ describe("bundler", () => { stdout: "foo is unused", }, }); - itBundled("dce/RemoveUnusedPureCommentCalls", { - todo: true, - // in this test, the bundler must drop all `_yes` variables entirely, and then - // preserve the pure comments in the same way esbuild does - files: { - "/entry.js": /* js */ ` - function bar() {} - let bare = foo(bar); - - let at_yes = /* @__PURE__ */ foo(bar); - let at_no = /* @__PURE__ */ foo(bar()); - let new_at_yes = /* @__PURE__ */ new foo(bar); - let new_at_no = /* @__PURE__ */ new foo(bar()); - - let nospace_at_yes = /*@__PURE__*/ foo(bar); - let nospace_at_no = /*@__PURE__*/ foo(bar()); - let nospace_new_at_yes = /*@__PURE__*/ new foo(bar); - let nospace_new_at_no = /*@__PURE__*/ new foo(bar()); - - let num_yes = /* #__PURE__ */ foo(bar); - let num_no = /* #__PURE__ */ foo(bar()); - let new_num_yes = /* #__PURE__ */ new foo(bar); - let new_num_no = /* #__PURE__ */ new foo(bar()); - - let nospace_num_yes = /*#__PURE__*/ foo(bar); - let nospace_num_no = /*#__PURE__*/ foo(bar()); - let nospace_new_num_yes = /*#__PURE__*/ new foo(bar); - let nospace_new_num_no = /*#__PURE__*/ new foo(bar()); - - let dot_yes = /* @__PURE__ */ foo(sideEffect()).dot(bar); - let dot_no = /* @__PURE__ */ foo(sideEffect()).dot(bar()); - let new_dot_yes = /* @__PURE__ */ new foo(sideEffect()).dot(bar); - let new_dot_no = /* @__PURE__ */ new foo(sideEffect()).dot(bar()); - - let nested_yes = [1, /* @__PURE__ */ foo(bar), 2]; - let nested_no = [1, /* @__PURE__ */ foo(bar()), 2]; - let new_nested_yes = [1, /* @__PURE__ */ new foo(bar), 2]; - let new_nested_no = [1, /* @__PURE__ */ new foo(bar()), 2]; - - let single_at_yes = // @__PURE__ - foo(bar); - let single_at_no = // @__PURE__ - foo(bar()); - let new_single_at_yes = // @__PURE__ - new foo(bar); - let new_single_at_no = // @__PURE__ - new foo(bar()); - - let single_num_yes = // #__PURE__ - foo(bar); - let single_num_no = // #__PURE__ - foo(bar()); - let new_single_num_yes = // #__PURE__ - new foo(bar); - let new_single_num_no = // #__PURE__ - new foo(bar()); - - let bad_no = /* __PURE__ */ foo(bar); - let new_bad_no = /* __PURE__ */ new foo(bar); - - let parens_no = (/* @__PURE__ */ foo)(bar); - let new_parens_no = new (/* @__PURE__ */ foo)(bar); - - let exp_no = /* @__PURE__ */ foo() ** foo(); - let new_exp_no = /* @__PURE__ */ new foo() ** foo(); - `, - }, - onAfterBundle(api) { - const code = api.readFile("/out.js"); - assert(!code.includes("_yes"), "should not contain any *_yes variables"); - assert(code.includes("var bare = foo(bar)"), "should contain `var bare = foo(bar)`"); - const keep = [ - ["at_no", true], - ["new_at_no", true], - ["nospace_at_no", true], - ["nospace_new_at_no", true], - ["num_no", true], - ["new_num_no", true], - ["nospace_num_no", true], - ["nospace_new_num_no", true], - ["dot_no", true], - ["new_dot_no", true], - ["nested_no", true], - ["new_nested_no", true], - ["single_at_no", true], - ["new_single_at_no", true], - ["single_num_no", true], - ["new_single_num_no", true], - ["bad_no", false], - ["new_bad_no", false], - ["parens_no", false], - ["new_parens_no", false], - ["exp_no", true], - ["new_exp_no", true], - ]; - for (const [name, pureComment] of keep) { - const regex = new RegExp(`${name}\\s*=[^\/\n]*(\\/\\*.*?\\*\\/)?`, "g"); - const match = regex.exec(code); - assert(!!match, `should contain ${name}`); - assert(pureComment ? !!match[1] : !match[1], `should contain a pure comment for ${name}`); - } - }, - }); + for (const { minify, emitDCEAnnotations, name } of [ + { minify: false, emitDCEAnnotations: false, name: "dce/RemoveUnusedPureCommentCalls" }, + { minify: true, emitDCEAnnotations: false, name: "dce/RemoveUnusedPureCommentCallsMinify" }, + { minify: true, emitDCEAnnotations: true, name: "dce/RemoveUnusedPureCommentCallsMinifyExplitOn" }, + ]) { + itBundled(name, { + // in this test, the bundler must drop all `_yes` variables entirely, and then + // preserve the pure comments in the same way esbuild does + files: { + "/entry.js": /* js */ ` + function bar() {} + let bare = foo(bar); + + let at_yes = /* @__PURE__ */ foo(bar); + let at_no = /* @__PURE__ */ foo(bar()); + let new_at_yes = /* @__PURE__ */ new foo(bar); + let new_at_no = /* @__PURE__ */ new foo(bar()); + + let nospace_at_yes = /*@__PURE__*/ foo(bar); + let nospace_at_no = /*@__PURE__*/ foo(bar()); + let nospace_new_at_yes = /*@__PURE__*/ new foo(bar); + let nospace_new_at_no = /*@__PURE__*/ new foo(bar()); + + let num_yes = /* #__PURE__ */ foo(bar); + let num_no = /* #__PURE__ */ foo(bar()); + let new_num_yes = /* #__PURE__ */ new foo(bar); + let new_num_no = /* #__PURE__ */ new foo(bar()); + + let nospace_num_yes = /*#__PURE__*/ foo(bar); + let nospace_num_no = /*#__PURE__*/ foo(bar()); + let nospace_new_num_yes = /*#__PURE__*/ new foo(bar); + let nospace_new_num_no = /*#__PURE__*/ new foo(bar()); + + let dot_yes = /* @__PURE__ */ foo(sideEffect()).dot(bar); + let dot_no = /* @__PURE__ */ foo(sideEffect()).dot(bar()); + let new_dot_yes = /* @__PURE__ */ new foo(sideEffect()).dot(bar); + let new_dot_no = /* @__PURE__ */ new foo(sideEffect()).dot(bar()); + + let nested_yes = [1, /* @__PURE__ */ foo(bar), 2]; + let nested_no = [1, /* @__PURE__ */ foo(bar()), 2]; + let new_nested_yes = [1, /* @__PURE__ */ new foo(bar), 2]; + let new_nested_no = [1, /* @__PURE__ */ new foo(bar()), 2]; + + let single_at_yes = // @__PURE__ + foo(bar); + let single_at_no = // @__PURE__ + foo(bar()); + let new_single_at_yes = // @__PURE__ + new foo(bar); + let new_single_at_no = // @__PURE__ + new foo(bar()); + + let single_num_yes = // #__PURE__ + foo(bar); + let single_num_no = // #__PURE__ + foo(bar()); + let new_single_num_yes = // #__PURE__ + new foo(bar); + let new_single_num_no = // #__PURE__ + new foo(bar()); + + let bad_no = /* __PURE__ */ foo(bar); + let new_bad_no = /* __PURE__ */ new foo(bar); + + let parens_no = (/* @__PURE__ */ foo)(bar); + let new_parens_no = new (/* @__PURE__ */ foo)(bar); + + let exp_no = /* @__PURE__ */ foo() ** foo(); + let new_exp_no = /* @__PURE__ */ new foo() ** foo(); + `, + }, + minifyWhitespace: minify, + emitDCEAnnotations: emitDCEAnnotations, + onAfterBundle(api) { + const code = api.readFile("/out.js"); + expect(code).not.toContain("_yes"); // should not contain any *_yes variables + expect(code).toContain(minify ? "var bare=foo(bar)" : "var bare = foo(bar)"); + const keep = [ + ["at_no", true], + ["new_at_no", true], + ["nospace_at_no", true], + ["nospace_new_at_no", true], + ["num_no", true], + ["new_num_no", true], + ["nospace_num_no", true], + ["nospace_new_num_no", true], + ["dot_no", true], + ["new_dot_no", true], + ["nested_no", true], + ["new_nested_no", true], + ["single_at_no", true], + ["new_single_at_no", true], + ["single_num_no", true], + ["new_single_num_no", true], + ["parens_no", false], + ["new_parens_no", false], + ["exp_no", true], + ["new_exp_no", true], + ]; + for (const [name, pureComment] of keep) { + const regex = new RegExp(`${name}\\s*=[^\/\n;]*(\\/\\*[^\/\n;]*?PURE[^\/\n;]*?\\*\\/)?`, "g"); + const match = regex.exec(code)!; + expect(match).toBeTruthy(); // should contain ${name} + + if ((emitDCEAnnotations || !minify) && pureComment) { + expect(match[1], "should contain pure comment for " + name).toBeTruthy(); + } else { + expect(match[1], "should not contain pure comment for " + name).toBeFalsy(); + } + } + }, + }); + } itBundled("dce/TreeShakingReactElements", { files: { "/entry.jsx": /* jsx */ ` @@ -1204,10 +1195,7 @@ describe("bundler", () => { dce: true, onAfterBundle(api) { const code = api.readFile("/out.js"); - assert( - [...code.matchAll(/return/g)].length === 2, - "should remove 3 trailing returns and the arrow function return", - ); + expect([...code.matchAll(/return/g)]).toHaveLength(2); // should remove 3 trailing returns and the arrow function return }, }); itBundled("dce/ImportReExportOfNamespaceImport", { @@ -2613,7 +2601,6 @@ describe("bundler", () => { }, }); itBundled("dce/CrossModuleConstantFolding", { - todo: true, files: { "/enum-constants.ts": /* ts */ ` export enum remove { @@ -2723,7 +2710,6 @@ describe("bundler", () => { dce: true, }); itBundled("dce/MultipleDeclarationTreeShaking", { - todo: true, files: { "/var2.js": /* js */ ` var x = 1 @@ -2762,7 +2748,6 @@ describe("bundler", () => { ], }); itBundled("dce/MultipleDeclarationTreeShakingMinifySyntax", { - todo: true, files: { "/var2.js": /* js */ ` var x = 1 @@ -2813,7 +2798,7 @@ describe("bundler", () => { dce: true, onAfterBundle(api) { const code = api.readFile("/out.js"); - assert([...code.matchAll(/\[\.\.\.args\]/g)].length === 2, "spread should be preserved"); + expect([...code.matchAll(/\[\.\.\.args\]/g)]).toHaveLength(2); // spread should be preserved }, }); itBundled("dce/TopLevelFunctionInliningWithSpread", { @@ -2969,6 +2954,62 @@ describe("bundler", () => { stdout: "foo\nbar", }, }); + itBundled("dce/CallWithNoArg", { + files: { + "/entry.js": /* js */ ` + /* @__PURE__ */ noSideEffects(); + `, + }, + run: { + stdout: "", + }, + }); + itBundled("dce/ConstructWithNoArg", { + files: { + "/entry.js": /* js */ ` + /* @__PURE__ */ new NoSideEffects(); + `, + }, + run: { + stdout: "", + }, + }); + itBundled("dce/IgnoreAnnotations", { + files: { + "/entry.js": /* js */ ` + function noSideEffects() { console.log("PASS"); } + /* @__PURE__ */ noSideEffects(1); + `, + }, + ignoreDCEAnnotations: true, + run: { + stdout: "PASS", + }, + }); + itBundled("dce/IgnoreAnnotationsDoesNotApplyToRuntime", { + files: { + "/entry.js": /* js */ ` + import("./other.js"); + `, + "/other.js": /* js */ ` + export function foo() { } + `, + }, + ignoreDCEAnnotations: true, + onAfterBundle(api) { + // These symbols technically have side effects, and we use dce annotations + // to let them tree-shake User-specified --ignore-annotations should not + // apply to our code. + api.expectFile("/out.js").not.toContain("__dispose"); + api.expectFile("/out.js").not.toContain("__asyncDispose"); + api.expectFile("/out.js").not.toContain("__require"); + + // This assertion catches if the bundler changes in that the runtime is no + // longer included. If this fails, just adjust the code snippet so some + // part of runtime.js is used + api.expectFile("/out.js").toContain("__defProp"); + }, + }); // itBundled("dce/TreeShakingJSWithAssociatedCSS", { // // TODO: css assertions. this should contain both button and menu // files: { diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts index 59cf373a95cc4..7c60b5d447858 100644 --- a/test/bundler/esbuild/default.test.ts +++ b/test/bundler/esbuild/default.test.ts @@ -1,9 +1,8 @@ import assert from "assert"; -import dedent from "dedent"; - -import { ESBUILD_PATH, RUN_UNCHECKED_TESTS, itBundled, testForFile } from "../expectBundled"; +import { describe, expect } from "bun:test"; import { osSlashes } from "harness"; -var { describe, test, expect } = testForFile(import.meta.path); +import { dedent, ESBUILD_PATH, itBundled } from "../expectBundled"; + // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_default_test.go diff --git a/test/bundler/esbuild/extra.test.ts b/test/bundler/esbuild/extra.test.ts index f96f348501da2..fae84dd21964e 100644 --- a/test/bundler/esbuild/extra.test.ts +++ b/test/bundler/esbuild/extra.test.ts @@ -1,7 +1,5 @@ -import assert from "assert"; -import dedent from "dedent"; -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild diff --git a/test/bundler/esbuild/importstar.test.ts b/test/bundler/esbuild/importstar.test.ts index cabe81a4b1970..3cec66e2b82e3 100644 --- a/test/bundler/esbuild/importstar.test.ts +++ b/test/bundler/esbuild/importstar.test.ts @@ -1,6 +1,5 @@ -import assert from "assert"; -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_importstar_test.go diff --git a/test/bundler/esbuild/importstar_ts.test.ts b/test/bundler/esbuild/importstar_ts.test.ts index 45ae1cbb3eefa..35ce54499d819 100644 --- a/test/bundler/esbuild/importstar_ts.test.ts +++ b/test/bundler/esbuild/importstar_ts.test.ts @@ -1,6 +1,5 @@ -import assert from "assert"; -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_importstar_ts_test.go diff --git a/test/bundler/esbuild/loader.test.ts b/test/bundler/esbuild/loader.test.ts index ec4e29535b4fb..bb765099ff832 100644 --- a/test/bundler/esbuild/loader.test.ts +++ b/test/bundler/esbuild/loader.test.ts @@ -1,6 +1,5 @@ -import fs from "fs"; -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_loader_test.go diff --git a/test/bundler/esbuild/lower.test.ts b/test/bundler/esbuild/lower.test.ts index b73c30b08c7e7..428af0d689bfc 100644 --- a/test/bundler/esbuild/lower.test.ts +++ b/test/bundler/esbuild/lower.test.ts @@ -1,13 +1,12 @@ -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_lower_test.go // For debug, all files are written to $TEMP/bun-bundle-tests/lower -describe("bundler", () => { - return; +describe.todo("bundler", () => { itBundled("lower/LowerOptionalCatchNameCollisionNoBundle", { // GENERATED files: { diff --git a/test/bundler/esbuild/packagejson.test.ts b/test/bundler/esbuild/packagejson.test.ts index 0b065f697b7b5..6983b3162a294 100644 --- a/test/bundler/esbuild/packagejson.test.ts +++ b/test/bundler/esbuild/packagejson.test.ts @@ -1,5 +1,5 @@ -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_packagejson_test.go diff --git a/test/bundler/esbuild/splitting.test.ts b/test/bundler/esbuild/splitting.test.ts index 69cdf712b26c2..2f31caf0e2c9f 100644 --- a/test/bundler/esbuild/splitting.test.ts +++ b/test/bundler/esbuild/splitting.test.ts @@ -1,7 +1,7 @@ import assert from "assert"; +import { describe, expect } from "bun:test"; import { readdirSync } from "fs"; -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_splitting_test.go diff --git a/test/bundler/esbuild/ts.test.ts b/test/bundler/esbuild/ts.test.ts index eef29b4c5b403..196fa9a24263b 100644 --- a/test/bundler/esbuild/ts.test.ts +++ b/test/bundler/esbuild/ts.test.ts @@ -1,6 +1,6 @@ import assert from "assert"; -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, expect } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_ts_test.go @@ -14,7 +14,7 @@ describe("bundler", () => { declare const require: any declare const exports: any; declare const module: any - + declare const foo: any let foo = bar() `, @@ -33,7 +33,7 @@ describe("bundler", () => { declare let require: any declare let exports: any; declare let module: any - + declare let foo: any let foo = bar() `, @@ -51,7 +51,7 @@ describe("bundler", () => { declare var require: any declare var exports: any; declare var module: any - + declare var foo: any let foo = bar() `, @@ -69,7 +69,7 @@ describe("bundler", () => { declare class require {} declare class exports {}; declare class module {} - + declare class foo {} let foo = bar() `, @@ -105,7 +105,7 @@ describe("bundler", () => { declare b: number [(() => null, c)] = 3 declare [(() => null, d)]: number - + static A = 5 static declare B: number static [(() => null, C)] = 7 @@ -121,7 +121,7 @@ describe("bundler", () => { declare b [(() => null, c)] declare [(() => null, d)] - + static A static declare B static [(() => null, C)] @@ -154,7 +154,7 @@ describe("bundler", () => { declare function require(): void declare function exports(): void; declare function module(): void - + declare function foo() {} let foo = bar() `, @@ -173,7 +173,7 @@ describe("bundler", () => { declare namespace require {} declare namespace exports {}; declare namespace module {} - + declare namespace foo {} let foo = bar() `, @@ -192,7 +192,7 @@ describe("bundler", () => { declare enum require {} declare enum exports {}; declare enum module {} - + declare enum foo {} let foo = bar() `, @@ -211,7 +211,7 @@ describe("bundler", () => { declare const enum require {} declare const enum exports {}; declare const enum module {} - + declare const enum foo {} let foo = bar() `, @@ -226,9 +226,6 @@ describe("bundler", () => { }, }); itBundled("ts/ConstEnumComments", { - // When it comes time to implement this inlining, we may decide we do NOT - // want to insert helper comments. - todo: true, files: { "/bar.ts": /* ts */ ` export const enum Foo { @@ -383,12 +380,11 @@ describe("bundler", () => { }, }); itBundled("ts/MinifyEnum", { - todo: true, files: { "/a.ts": `enum Foo { A, B, C = Foo }\ncapture(Foo)`, - // "/b.ts": `export enum Foo { X, Y, Z = Foo }`, + "/b.ts": `export enum Foo { X, Y, Z = Foo }`, }, - entryPoints: ["/a.ts"], + entryPoints: ["/a.ts", "./b.ts"], minifySyntax: true, minifyWhitespace: true, minifyIdentifiers: true, @@ -396,20 +392,20 @@ describe("bundler", () => { onAfterBundle(api) { const a = api.readFile("/out.js"); api.writeFile("/out.edited.js", a.replace(/capture\((.*?)\)/, `export const Foo = $1`)); - // const b = api.readFile("/out/b.js"); + const b = api.readFile("/out/b.js"); // make sure the minification trick "enum[enum.K=V]=K" is used, but `enum` assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.A=0]=["']A["']/), "should be using enum minification trick (1)"); assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.B=1]=["']B["']/), "should be using enum minification trick (2)"); assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.C=[a-zA-Z$]]=["']C["']/), "should be using enum minification trick (3)"); - // assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.X=0]=["']X["']/), "should be using enum minification trick (4)"); - // assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Y=1]=["']Y["']/), "should be using enum minification trick (5)"); - // assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Z=[a-zA-Z$]]=["']Z["']/), "should be using enum minification trick (6)"); + assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.X=0]=["']X["']/), "should be using enum minification trick (4)"); + assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Y=1]=["']Y["']/), "should be using enum minification trick (5)"); + assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Z=[a-zA-Z$]]=["']Z["']/), "should be using enum minification trick (6)"); }, runtimeFiles: { "/test.js": /* js */ ` import {Foo as FooA} from './out/a.edited.js' - // import {Foo as FooB} from './out/b.js' + import {Foo as FooB} from './out/b.js' import assert from 'assert'; assert.strictEqual(FooA.A, 0, 'a.ts Foo.A') assert.strictEqual(FooA.B, 1, 'a.ts Foo.B') @@ -417,17 +413,16 @@ describe("bundler", () => { assert.strictEqual(FooA[0], 'A', 'a.ts Foo[0]') assert.strictEqual(FooA[1], 'B', 'a.ts Foo[1]') assert.strictEqual(FooA[FooA], 'C', 'a.ts Foo[Foo]') - // assert.strictEqual(FooB.X, 0, 'b.ts Foo.X') - // assert.strictEqual(FooB.Y, 1, 'b.ts Foo.Y') - // assert.strictEqual(FooB.Z, FooB, 'b.ts Foo.Z') - // assert.strictEqual(FooB[0], 'X', 'b.ts Foo[0]') - // assert.strictEqual(FooB[1], 'Y', 'b.ts Foo[1]') - // assert.strictEqual(FooB[FooB], 'Z', 'b.ts Foo[Foo]') + assert.strictEqual(FooB.X, 0, 'b.ts Foo.X') + assert.strictEqual(FooB.Y, 1, 'b.ts Foo.Y') + assert.strictEqual(FooB.Z, FooB, 'b.ts Foo.Z') + assert.strictEqual(FooB[0], 'X', 'b.ts Foo[0]') + assert.strictEqual(FooB[1], 'Y', 'b.ts Foo[1]') + assert.strictEqual(FooB[FooB], 'Z', 'b.ts Foo[Foo]') `, }, }); itBundled("ts/MinifyEnumExported", { - todo: true, files: { "/b.ts": `export enum Foo { X, Y, Z = Foo }`, }, @@ -724,11 +719,11 @@ describe("bundler", () => { import a = foo.a import b = a.b import c = b.c - + import x = foo.x import y = x.y import z = y.z - + export let bar = c `, }, @@ -831,7 +826,6 @@ describe("bundler", () => { stdout: '[123,{"test":true}]', }, }); - // TODO: all situations with decorators are currently not runtime-checked. as of writing bun crashes when hitting them at all. itBundled("ts/TypeScriptDecoratorsSimpleCase", { files: { "/entry.ts": /* ts */ ` @@ -888,8 +882,9 @@ describe("bundler", () => { @x @y mDef = 1 @x @y method(@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo } @x @y declare mDecl + @x @y declare mAbst constructor(@x0 @y0 arg0, @x1 @y1 arg1) {} - + @x @y static sUndef @x @y static sDef = new Foo @x @y static sMethod(@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo } @@ -904,13 +899,14 @@ describe("bundler", () => { @x @y [mDef()] = 1 @x @y [method()](@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo } @x @y declare [mDecl()] - + @x @y abstract [mAbst()] + // Side effect order must be preserved even for fields without decorators [xUndef()] [xDef()] = 2 static [yUndef()] static [yDef()] = 3 - + @x @y static [sUndef()] @x @y static [sDef()] = new Foo @x @y static [sMethod()](@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo } @@ -1015,7 +1011,7 @@ describe("bundler", () => { method1(@dec(foo) foo = 2) {} method2(@dec(() => foo) foo = 3) {} } - + class Bar { static x = class { static y = () => { @@ -1071,14 +1067,14 @@ describe("bundler", () => { import tn_def, { bar as tn } from './keep/type-nested' import vn_def, { bar as vn } from './keep/value-namespace' import vnm_def, { bar as vnm } from './keep/value-namespace-merged' - + import i_def, { bar as i } from './remove/interface' import ie_def, { bar as ie } from './remove/interface-exported' import t_def, { bar as t } from './remove/type' import te_def, { bar as te } from './remove/type-exported' import ton_def, { bar as ton } from './remove/type-only-namespace' import tone_def, { bar as tone } from './remove/type-only-namespace-exported' - + export default [ dc_def, dc, dl_def, dl, @@ -1087,7 +1083,7 @@ describe("bundler", () => { tn_def, tn, vn_def, vn, vnm_def, vnm, - + i, ie, t, @@ -1324,7 +1320,7 @@ describe("bundler", () => { foo(x = this) { return [x, this]; } static bar(x = this) { return [x, this]; } } - + assert.deepEqual(bar('bun'), ['bun', undefined]); assert.deepEqual(bar.call('this'), ['this', 'this']); assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']); @@ -1391,7 +1387,7 @@ describe("bundler", () => { foo(x = this) { return [x, this]; } static bar(x = this) { return [x, this]; } } - + assert.deepEqual(bar('bun'), ['bun', undefined]); assert.deepEqual(bar.call('this'), ['this', 'this']); assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']); @@ -1459,7 +1455,7 @@ describe("bundler", () => { foo(x = this) { return [x, this]; } static bar(x = this) { return [x, this]; } } - + assert.deepEqual(bar('bun'), ['bun', undefined]); assert.deepEqual(bar.call('this'), ['this', 'this']); assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']); @@ -1527,7 +1523,7 @@ describe("bundler", () => { foo(x = this) { return [x, this]; } static bar(x = this) { return [x, this]; } } - + assert.deepEqual(bar('bun'), ['bun', undefined]); assert.deepEqual(bar.call('this'), ['this', 'this']); assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']); @@ -1737,7 +1733,6 @@ describe("bundler", () => { useDefineForClassFields: true, }); itBundled("ts/ImportMTS", { - todo: true, files: { "/entry.ts": `import './imported.mjs'`, "/imported.mts": `console.log('works')`, @@ -1780,7 +1775,6 @@ describe("bundler", () => { }, }); itBundled("ts/SiblingNamespaceLet", { - todo: true, files: { "/let.ts": /* ts */ ` export namespace x { export let y = 123 } @@ -1798,7 +1792,6 @@ describe("bundler", () => { }, }); itBundled("ts/SiblingNamespaceFunction", { - todo: true, files: { "/function.ts": /* ts */ ` export namespace x { export function y() {} } @@ -1816,14 +1809,13 @@ describe("bundler", () => { }, }); itBundled("ts/SiblingNamespaceClass", { - todo: true, files: { "/let.ts": /* ts */ ` export namespace x { export class y {} } export namespace x { export let z = y } `, }, - entryPoints: ["/function.ts"], + entryPoints: ["/let.ts"], bundling: false, runtimeFiles: { "/test.js": /* js */ ` @@ -1834,7 +1826,6 @@ describe("bundler", () => { }, }); itBundled("ts/SiblingNamespaceNamespace", { - todo: true, files: { "/namespace.ts": /* ts */ ` export namespace x { export namespace y { 0 } } @@ -1852,7 +1843,6 @@ describe("bundler", () => { }, }); itBundled("ts/SiblingNamespaceEnum", { - todo: true, files: { "/enum.ts": /* ts */ ` export namespace x { export enum y {} } @@ -1868,10 +1858,9 @@ describe("bundler", () => { assert(m.x === m.z, "it worked.ts worked") `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens }); itBundled("ts/SiblingEnum", { - todo: true, - // GENERATED files: { "/number.ts": /* ts */ ` (0, eval)('globalThis.y = 1234'); @@ -1879,7 +1868,7 @@ describe("bundler", () => { export enum x { y, yy = y } export enum x { z = y + 1 } - + declare let y: any, z: any export namespace x { console.log(y, z) } console.log(x.y, x.z) @@ -1890,7 +1879,7 @@ describe("bundler", () => { export enum x { y = 'a', yy = y } export enum x { z = y } - + declare let y: any, z: any export namespace x { console.log(y, z) } console.log(x.y, x.z) @@ -1911,7 +1900,7 @@ describe("bundler", () => { (0, eval)('globalThis.z = 2345'); export namespace foo { export enum x { y, yy = y } } export namespace foo { export enum x { z = y + 1 } } - + declare let y: any, z: any export namespace foo.x { console.log(y, z) @@ -1924,7 +1913,7 @@ describe("bundler", () => { export namespace foo { export enum x { y = 'a', yy = y } } export namespace foo { export enum x { z = y } } - + declare let y: any, z: any export namespace foo.x { console.log(y, z) @@ -1963,9 +1952,9 @@ describe("bundler", () => { { file: "/out/nested-string.js", stdout: "1234 2345\na a" }, { file: "/out/nested-propagation.js", stdout: "100 100 100 625 625 625" }, ], + minifySyntax: false, // intentionally disabled. enum inlining always happens }); itBundled("ts/EnumTreeShaking", { - todo: true, files: { "/simple-member.ts": /* ts */ ` enum x_DROP { y_DROP = 123 } @@ -2031,8 +2020,11 @@ describe("bundler", () => { { file: "/out/namespace-before.js", stdout: "{} 1234" }, { file: "/out/namespace-after.js", stdout: '{"123":"y","y":123} 1234' }, ], + minifySyntax: false, // intentionally disabled. enum inlining always happens }); itBundled("ts/EnumJSX", { + // Blocking: + // - jsx bugs (configuration does not seem to be respected) todo: true, files: { "/element.tsx": /* tsx */ ` @@ -2043,19 +2035,19 @@ describe("bundler", () => { `, "/fragment.tsx": /* tsx */ ` import { create } from 'not-react' - + export enum React { Fragment = 'div' } console.log(JSON.stringify(<>test)) `, "/nested-element.tsx": /* tsx */ ` import { create } from 'not-react' - + namespace x.y { export enum Foo { Div = 'div' } } namespace x.y { console.log(JSON.stringify()) } `, "/nested-fragment.tsx": /* tsx */ ` import { create } from 'not-react' - + namespace x.y { export enum React { Fragment = 'div' } } namespace x.y { console.log(JSON.stringify(<>test)) } `, @@ -2072,6 +2064,7 @@ describe("bundler", () => { export const create = (tag, props, ...children) => [tag, props, children] `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens run: [ { file: "/out/element.js", stdout: '["div",null,[]]' }, { file: "/out/fragment.js", stdout: '["div",null,["test"]]' }, @@ -2083,17 +2076,17 @@ describe("bundler", () => { todo: true, files: { "/entry.ts": ` - enum a { b = 123, c = d } - console.log(a.b, a.c) + enum a { b = 123, c = d } + console.log(a.b, a.c) `, }, define: { d: "b", }, + minifySyntax: false, // intentionally disabled. enum inlining always happens run: { stdout: "123 123" }, }); itBundled("ts/EnumSameModuleInliningAccess", { - todo: true, files: { "/entry.ts": /* ts */ ` enum a_drop { x = 123 } @@ -2111,10 +2104,10 @@ describe("bundler", () => { `, }, dce: true, + minifySyntax: false, // intentionally disabled. enum inlining always happens run: { stdout: '[123,123,123,123,{"123":"x","x":123}]' }, }); itBundled("ts/EnumCrossModuleInliningAccess", { - todo: true, files: { "/entry.ts": /* ts */ ` import { drop_a, drop_b, c, d, e } from './enums' @@ -2134,14 +2127,14 @@ describe("bundler", () => { export enum e { x = 123 } `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens dce: true, }); itBundled("ts/EnumCrossModuleInliningDefinitions", { - todo: true, files: { "/entry.ts": /* ts */ ` import { a } from './enums' - (0, eval)('globalThis.capture = x => x'); + (0, eval)('globalThis.["captu" + "re"] = x => x'); console.log(JSON.stringify([ capture(a.implicit_number), capture(a.explicit_number), @@ -2160,12 +2153,12 @@ describe("bundler", () => { } `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens onAfterBundle(api) { expect(api.captureFile("/out.js").map(x => x.replace(/\/\*.*\*\//g, "").trim())).toEqual(["0", "123", '"xyz"']); }, }); itBundled("ts/EnumCrossModuleInliningReExport", { - todo: true, files: { "/entry.js": /* js */ ` import { a } from './re-export' @@ -2185,12 +2178,12 @@ describe("bundler", () => { export enum c { x = 'c' } `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens onAfterBundle(api) { expect(api.captureFile("/out.js").map(x => x.replace(/\/\*.*\*\//g, "").trim())).toEqual(['"a"', '"b"', '"c"']); }, }); itBundled("ts/EnumCrossModuleTreeShaking", { - todo: true, files: { "/entry.ts": /* ts */ ` import { @@ -2198,15 +2191,15 @@ describe("bundler", () => { b_DROP, c_DROP, } from './enums' - + console.log([ capture(a_DROP.x), capture(b_DROP['x']), capture(c_DROP.x), ]) - + import { a, b, c, d, e } from './enums' - + console.log([ capture(a.x), capture(b.x), @@ -2219,7 +2212,7 @@ describe("bundler", () => { export enum a_DROP { x = 1 } // test a dot access export enum b_DROP { x = 2 } // test an index access export enum c_DROP { x = '' } // test a string enum - + export enum a { x = false } // false is not inlinable export enum b { x = foo } // foo has side effects export enum c { x = 3 } // this enum object is captured @@ -2227,6 +2220,7 @@ describe("bundler", () => { export let e = {} // non-enum properties should be kept `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens onAfterBundle(api) { expect(api.captureFile("/out.js").map(x => x.replace(/\/\*.*\*\//g, "").trim())).toEqual([ "1", @@ -2241,7 +2235,6 @@ describe("bundler", () => { }, }); itBundled("ts/EnumExportClause", { - todo: true, files: { "/entry.ts": /* ts */ ` import { @@ -2250,7 +2243,7 @@ describe("bundler", () => { C as c, d as dd, } from './enums' - + console.log([ capture(A.A), capture(B.B), @@ -2266,23 +2259,12 @@ describe("bundler", () => { export { B, D as d } `, }, + minifySyntax: false, // intentionally disabled. enum inlining always happens onAfterBundle(api) { expect(api.captureFile("/out.js").map(x => x.replace(/\/\*.*\*\//g, "").trim())).toEqual(["1", "2", "3", "4"]); }, }); - // itBundled("ts/CommonJSVariableInESMTypeModule", { - // // GENERATED - // files: { - // "/entry.ts": `module.exports = null`, - // "/package.json": `{ "type": "module" }`, - // }, - // /* TODO FIX expectedScanLog: `entry.ts: WARNING: The CommonJS "module" variable is treated as a global variable in an ECMAScript module and may not work as expected - // package.json: NOTE: This file is considered to be an ECMAScript module because the enclosing "package.json" file sets the type of this file to "module": - // NOTE: Node's package format requires that CommonJS files in a "type": "module" package use the ".cjs" file extension. If you are using TypeScript, you can use the ".cts" file extension with esbuild instead. - // `, */ - // }); itBundled("ts/EnumRulesFrom_TypeScript_5_0", { - // GENERATED files: { "/supported.ts": ` @@ -2422,43 +2404,42 @@ describe("bundler", () => { ])) `, "/not-supported.ts": /* ts */ ` - (0, eval)('globalThis.capture = x => x'); + (0, eval)('globalThis["captu" + "re"] = x => x'); - const enum NonIntegerNumberToString { - SUPPORTED = '' + 1, - UNSUPPORTED = '' + 1.5, + const enum NumberToString { + DROP_One = '' + 1, + DROP_OnePointFive = '' + 1.5, + DROP_Other = '' + 4132879497321892437432187943789312894378237491578123414321431, + DROP_Billion = '' + 1_000_000_000, + DROP_Trillion = '' + 1_000_000_000_000, } console.log( - capture(NonIntegerNumberToString.SUPPORTED), - capture(NonIntegerNumberToString.UNSUPPORTED), + capture(NumberToString.DROP_One), + capture(NumberToString.DROP_OnePointFive), + capture(NumberToString.DROP_Other), + capture(NumberToString.DROP_Billion), + capture(NumberToString.DROP_Trillion), ) - - const enum OutOfBoundsNumberToString { - SUPPORTED = '' + 1_000_000_000, - UNSUPPORTED = '' + 1_000_000_000_000, - } - console.log( - capture(OutOfBoundsNumberToString.SUPPORTED), - capture(OutOfBoundsNumberToString.UNSUPPORTED), - ) - - const enum TemplateExpressions { + + const enum DROP_TemplateExpressions { // TypeScript enums don't handle any of these NULL = '' + null, TRUE = '' + true, FALSE = '' + false, BIGINT = '' + 123n, + BIGINT_2 = '' + 4132879497321892437432187943789312894378237491578123414321431n, } console.log( - capture(TemplateExpressions.NULL), - capture(TemplateExpressions.TRUE), - capture(TemplateExpressions.FALSE), - capture(TemplateExpressions.BIGINT), + capture(DROP_TemplateExpressions.NULL), + capture(DROP_TemplateExpressions.TRUE), + capture(DROP_TemplateExpressions.FALSE), + capture(DROP_TemplateExpressions.BIGINT), + capture(DROP_TemplateExpressions.BIGINT_2), ) `, }, - // dce: true, + dce: true, entryPoints: ["/supported.ts", "/not-supported.ts"], run: [ { @@ -2469,32 +2450,32 @@ describe("bundler", () => { { file: "/out/not-supported.js", stdout: ` - 1 1.5 - 1000000000 1000000000000 - null true false 123 + 1 1.5 4.1328794973218926e+60 1000000000 1000000000000 + null true false 123 4132879497321892437432187943789312894378237491578123414321431 `, }, ], onAfterBundle(api) { - // expect(api.captureFile("/out/not-supported.js").map(x => x.replace(/\/\*.*\*\//g, "").trim())).toEqual([ - // '"1"', - // "NonIntegerNumberToString.UNSUPPORTED", - // '"1000000000"', - // "OutOfBoundsNumberToString.UNSUPPORTED", - // "TemplateExpressions.NULL", - // "TemplateExpressions.TRUE", - // "TemplateExpressions.FALSE", - // "TemplateExpressions.BIGINT", - // ]); + expect(api.captureFile("/out/not-supported.js").map(x => x.replace(/\/\*.*\*\//g, "").trim())).toEqual([ + '"1"', + '"1.5"', + '"4.1328794973218926e+60"', + '"1000000000"', + '"1000000000000"', + '"null"', + '"true"', + '"false"', + '"123"', + '"4132879497321892437432187943789312894378237491578123414321431"', + ]); }, }); itBundled("ts/EnumUseBeforeDeclare", { - todo: true, files: { "/entry.ts": /* ts */ ` before(); after(); - + export function before() { console.log(JSON.stringify(Foo), Foo.FOO) } diff --git a/test/bundler/esbuild/tsconfig.test.ts b/test/bundler/esbuild/tsconfig.test.ts index b09a0d2814232..b62a6d76947e4 100644 --- a/test/bundler/esbuild/tsconfig.test.ts +++ b/test/bundler/esbuild/tsconfig.test.ts @@ -1,5 +1,5 @@ -import { itBundled, testForFile } from "../expectBundled"; -var { describe, test, expect } = testForFile(import.meta.path); +import { describe, test } from "bun:test"; +import { itBundled } from "../expectBundled"; // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_tsconfig_test.go @@ -393,7 +393,9 @@ describe("bundler", () => { onAfterBundle(api) { api .expectFile("/Users/user/project/out.js") - .toContain(`console.log(R.c(R.F, null, R.c(\"div\", null), R.c(\"div\", null)));\n`); + .toContain( + `console.log(/* @__PURE__ */ R.c(R.F, null, /* @__PURE__ */ R.c(\"div\", null), /* @__PURE__ */ R.c(\"div\", null)));\n`, + ); }, }); itBundled("tsconfig/ReactJSXNotReact", { diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index 86398d6fc0c4a..558e211237ad6 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -1,19 +1,18 @@ /** * See `./expectBundled.md` for how this works. */ -import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, readdirSync, realpathSync } from "fs"; -import path from "path"; -import { bunEnv, bunExe, joinP } from "harness"; -import { tmpdir } from "os"; +import { BuildConfig, BunPlugin, fileURLToPath, PluginBuilder } from "bun"; import { callerSourceOrigin } from "bun:jsc"; -import { BuildConfig, BunPlugin, fileURLToPath } from "bun"; import type { Matchers } from "bun:test"; -import { PluginBuilder } from "bun"; import * as esbuild from "esbuild"; +import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs"; +import { bunEnv, bunExe, isDebug } from "harness"; +import { tmpdir } from "os"; +import path from "path"; import { SourceMapConsumer } from "source-map"; /** Dedent module does a bit too much with their stuff. we will be much simpler */ -function dedent(str: string | TemplateStringsArray, ...args: any[]) { +export function dedent(str: string | TemplateStringsArray, ...args: any[]) { // https://github.com/tc39/proposal-string-cooked#motivation let single_string = String.raw({ raw: str }, ...args); single_string = single_string.trim(); @@ -159,10 +158,13 @@ export interface BundlerTestInput { extensionOrder?: string[]; /** Replaces "{{root}}" with the file root */ external?: string[]; + /** Defaults to "bundle" */ + packages?: "bundle" | "external"; /** Defaults to "esm" */ - format?: "esm" | "cjs" | "iife"; + format?: "esm" | "cjs" | "iife" | "internal_kit_dev"; globalName?: string; ignoreDCEAnnotations?: boolean; + emitDCEAnnotations?: boolean; inject?: string[]; jsx?: { runtime?: "automatic" | "classic"; @@ -196,7 +198,7 @@ export interface BundlerTestInput { unsupportedJSFeatures?: string[]; /** if set to true or false, create or edit tsconfig.json to set compilerOptions.useDefineForClassFields */ useDefineForClassFields?: boolean; - sourceMap?: "inline" | "external" | "linked" | "none"; + sourceMap?: "inline" | "external" | "linked" | "none" | "linked"; plugins?: BunPlugin[] | ((builder: PluginBuilder) => void | Promise); install?: string[]; @@ -265,6 +267,8 @@ export interface BundlerTestInput { skipIfWeDidNotImplementWildcardSideEffects?: boolean; snapshotSourceMap?: Record; + + expectExactFilesize?: Record; } export interface SourceMapTests { @@ -280,7 +284,7 @@ export interface SourceMapTests { mappingsExactMatch?: string; } -/** Keep in mind this is an array/tuple, NOT AN OBJECT. This keeps things more consise */ +/** Keep in mind this is an array/tuple, NOT AN OBJECT. This keeps things more concise */ export type MappingSnapshot = [ // format a string like "file:line:col", for example // "index.ts:5:2" @@ -297,6 +301,7 @@ export interface BundlerTestBundleAPI { outfile: string; outdir: string; + join(subPath: string): string; readFile(file: string): string; writeFile(file: string, contents: string): void; prependFile(file: string, contents: string): void; @@ -332,6 +337,8 @@ export interface BundlerTestRunOptions { */ errorLineMatch?: RegExp; + env?: Record; + runtime?: "bun" | "node"; setCwd?: boolean; @@ -400,6 +407,7 @@ function expectBundled( entryPointsRaw, env, external, + packages, files, format, globalName, @@ -435,8 +443,11 @@ function expectBundled( unsupportedCSSFeatures, unsupportedJSFeatures, useDefineForClassFields, + ignoreDCEAnnotations, + emitDCEAnnotations, // @ts-expect-error _referenceFn, + expectExactFilesize, ...unknownProps } = opts; @@ -468,8 +479,8 @@ function expectBundled( if (bundling === false && entryPoints.length > 1) { throw new Error("bundling:false only supports a single entry point"); } - if (!ESBUILD && format !== "esm") { - throw new Error("formats besides esm not implemented in bun build"); + if (!ESBUILD && (format === "cjs" || format === 'iife')) { + throw new Error(`format ${format} not implemented in bun build`); } if (!ESBUILD && metafile) { throw new Error("metafile not implemented in bun build"); @@ -564,7 +575,8 @@ function expectBundled( cwd: root, }); if (!installProcess.success) { - throw new Error("Failed to install dependencies"); + const reason = installProcess.signalCode || `code ${installProcess.exitCode}`; + throw new Error(`Failed to install dependencies: ${reason}`); } } for (const [file, contents] of Object.entries(files)) { @@ -618,8 +630,9 @@ function expectBundled( outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`, define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]), `--target=${target}`, - // `--format=${format}`, + `--format=${format}`, external && external.map(x => ["--external", x]), + packages && ["--packages", packages], conditions && conditions.map(x => ["--conditions", x]), minifyIdentifiers && `--minify-identifiers`, minifySyntax && `--minify-syntax`, @@ -637,6 +650,8 @@ function expectBundled( splitting && `--splitting`, serverComponents && "--server-components", outbase && `--root=${outbase}`, + ignoreDCEAnnotations && `--ignore-dce-annotations`, + emitDCEAnnotations && `--emit-dce-annotations`, // inject && inject.map(x => ["--inject", path.join(root, x)]), // jsx.preserve && "--jsx=preserve", // legalComments && `--legal-comments=${legalComments}`, @@ -657,6 +672,7 @@ function expectBundled( minifyWhitespace && `--minify-whitespace`, globalName && `--global-name=${globalName}`, external && external.map(x => `--external:${x}`), + packages && ["--packages", packages], conditions && `--conditions=${conditions.join(",")}`, inject && inject.map(x => `--inject:${path.join(root, x)}`), define && Object.entries(define).map(([k, v]) => `--define:${k}=${v}`), @@ -678,6 +694,7 @@ function expectBundled( sourceMap && `--sourcemap=${sourceMap}`, banner && `--banner:js=${banner}`, legalComments && `--legal-comments=${legalComments}`, + ignoreDCEAnnotations && `--ignore-annotations`, splitting && `--splitting`, treeShaking && `--tree-shaking`, outbase && `--outbase=${outbase}`, @@ -864,10 +881,11 @@ function expectBundled( if (!ESBUILD) { const warningText = stderr!.toUnixString(); const allWarnings = warnParser(warningText).map(([error, source]) => { + if(!source) return; const [_str2, fullFilename, line, col] = source.match(/bun-build-tests[\/\\](.*):(\d+):(\d+)/)!; const file = fullFilename.slice(id.length + path.basename(tempDirectory).length + 1).replaceAll("\\", "/"); return { error, file, line, col }; - }); + }).filter(Boolean); const expectedWarnings = bundleWarnings ? Object.entries(bundleWarnings).flatMap(([file, v]) => v.map(error => ({ file, error }))) : null; @@ -926,6 +944,7 @@ function expectBundled( const buildConfig = { entrypoints: [...entryPaths, ...(entryPointsRaw ?? [])], external, + packages, minify: { whitespace: minifyWhitespace, identifiers: minifyIdentifiers, @@ -943,6 +962,8 @@ function expectBundled( splitting, target, publicPath, + emitDCEAnnotations, + ignoreDCEAnnotations, } as BuildConfig; if (conditions?.length) { @@ -1082,6 +1103,7 @@ for (const [key, blob] of build.outputs) { root, outfile: outfile!, outdir: outdir!, + join: (...paths: string[]) => path.join(root, ...paths), readFile, writeFile, expectFile: file => expect(readFile(file)), @@ -1252,7 +1274,7 @@ for (const [key, blob] of build.outputs) { const outfiletext = api.readFile(path.relative(root, outfile ?? outputPaths[0])); const regex = /\/\/\s+(.+?)\nvar\s+([a-zA-Z0-9_$]+)\s+=\s+__commonJS/g; const matches = [...outfiletext.matchAll(regex)].map(match => ("/" + match[1]).replaceAll("\\", "/")); - const expectedMatches = (cjs2esm === true ? [] : cjs2esm.unhandled ?? []).map(a => a.replaceAll("\\", "/")); + const expectedMatches = (cjs2esm === true ? [] : (cjs2esm.unhandled ?? [])).map(a => a.replaceAll("\\", "/")); try { expect(matches.sort()).toEqual(expectedMatches.sort()); } catch (error) { @@ -1371,6 +1393,15 @@ for (const [key, blob] of build.outputs) { } } + if (expectExactFilesize) { + for (const [key, expected] of Object.entries(expectExactFilesize)) { + const actual = api.readFile(key).length; + if (actual !== expected) { + throw new Error(`Expected file ${key} to be ${expected} bytes but was ${actual} bytes.`); + } + } + } + // Runtime checks! if (run) { const runs = Array.isArray(run) ? run : [run]; @@ -1546,7 +1577,7 @@ export function itBundled( id, () => expectBundled(id, opts as any), // sourcemap code is slow - opts.snapshotSourceMap ? 20_000 : undefined, + isDebug ? Infinity : opts.snapshotSourceMap ? 30_000 : undefined, ); } return ref; diff --git a/test/bundler/bundler-reloader-script.ts b/test/bundler/fixtures/bundler-reloader-script.ts similarity index 86% rename from test/bundler/bundler-reloader-script.ts rename to test/bundler/fixtures/bundler-reloader-script.ts index 28604206a1e9f..e901067a021cb 100644 --- a/test/bundler/bundler-reloader-script.ts +++ b/test/bundler/fixtures/bundler-reloader-script.ts @@ -5,11 +5,12 @@ // That way, if the developer changes a file, we will see the change. // // 2. Checks the file descriptor count to make sure we're not leaking any files between re-builds. + +import { closeSync, openSync, realpathSync, unlinkSync } from "fs"; import { tmpdir } from "os"; -import { realpathSync, unlinkSync } from "fs"; import { join } from "path"; -import { openSync, closeSync } from "fs"; -const tmp = realpathSync(tmpdir()); + +const tmp = realpathSync(process.env.BUNDLER_RELOADER_SCRIPT_TMP_DIR || tmpdir()); const input = join(tmp, "input.js"); const mutate = join(tmp, "mutate.js"); try { @@ -17,11 +18,15 @@ try { } catch (e) {} await Bun.write(input, "import value from './mutate.js';\n" + `export default value;` + "\n"); +await Bun.sleep(1000); + await Bun.build({ entrypoints: [input], }); await Bun.write(mutate, "export default 1;\n"); +await Bun.sleep(1000); + const maxfd = openSync(process.execPath, 0); closeSync(maxfd); const { outputs: second } = await Bun.build({ diff --git a/test/bundler/large_asset_regression.test.ts b/test/bundler/large_asset_regression.test.ts deleted file mode 100644 index ffeb49ec850af..0000000000000 --- a/test/bundler/large_asset_regression.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { test, expect, beforeAll, describe, afterAll } from "bun:test"; -import { bunExe, tempDirWithFiles } from "harness"; -import path from "path"; -import { rm } from "fs/promises"; -import { $ } from "bun"; -import { readdirSync, statSync } from "fs"; - -// https://github.com/oven-sh/bun/issues/10139 -describe("https://github.com/oven-sh/bun/issues/10139", async () => { - let temp = ""; - beforeAll(async () => { - temp = tempDirWithFiles("issue-10132", { - "huge-asset.js": ` - import huge from './1.png'; - if (!huge.startsWith("https://example.com/huge")) { - throw new Error("Unexpected public path: " + huge); - } - `, - // Note: the SIGBUS only seemed to reproduce at >= 768 MB - // However, that causes issues in CI. CI does not like writing 1 GB files - // to disk. So we shrink it down to 128 MB instead, which still causes the - // test to fail in Bun v1.1.2 and earlier. - "1.png": new Buffer(1024 * 1024 * 128), - }); - }); - - afterAll(async () => { - rm(temp, { recursive: true, force: true }); - }); - - test("Bun.build", async () => { - const results = await Bun.build({ - entrypoints: [path.join(temp, "huge-asset.js")], - outdir: path.join(temp, "out"), - sourcemap: "external", - }); - var sourceMapCount = 0; - for (const output of results.outputs) { - const size = output?.sourcemap?.size || 0; - expect(size).toBeLessThan(1024); - sourceMapCount += Number(Number(size) > 0); - } - await rm(path.join(temp, "out"), { force: true, recursive: true }); - expect(sourceMapCount).toBe(1); - }); - - test("CLI", async () => { - $.cwd(temp); - await $`${bunExe()} build ./huge-asset.js --outdir=out --sourcemap=external --minify`; - readdirSync(path.join(temp, "out")).forEach(file => { - const size = statSync(path.join(temp, "out", file)).size; - if (file.includes(".map")) { - expect(size).toBeLessThan(1024); - } - }); - await rm(path.join(temp, "out"), { recursive: true, force: true }); - }); -}); diff --git a/test/bundler/acorn.patch b/test/bundler/scripts/acorn.patch similarity index 100% rename from test/bundler/acorn.patch rename to test/bundler/scripts/acorn.patch diff --git a/test/bundler/acorn.sh b/test/bundler/scripts/acorn.sh similarity index 100% rename from test/bundler/acorn.sh rename to test/bundler/scripts/acorn.sh diff --git a/test/bundler/transpiler/__snapshots__/transpiler.test.js.snap b/test/bundler/transpiler/__snapshots__/transpiler.test.js.snap new file mode 100644 index 0000000000000..50a51491f427e --- /dev/null +++ b/test/bundler/transpiler/__snapshots__/transpiler.test.js.snap @@ -0,0 +1,735 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`Bun.Transpiler using statements work right 1`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 0); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; +} finally { +__callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +}" +`; + +exports[`Bun.Transpiler using statements work right 2`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 1); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; +} finally { +var __bun_temp_ref_5$ = __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +__bun_temp_ref_5$ && await __bun_temp_ref_5$; +}" +`; + +exports[`Bun.Transpiler using statements work right 3`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 4`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 5`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 6`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 7`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 8`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 9`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 10`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using top level 1`] = ` +"import { +__callDispose as __callDispose, +__using as __using +} from "bun:wrap"; +export function c(e) { + let __bun_temp_ref_1$ = []; + try { + const f = __using(__bun_temp_ref_1$, g(a), 0); + return f.h; + } catch (__bun_temp_ref_2$) { + var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; + } finally { + __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); + } +} +import {using} from "n"; +let __bun_temp_ref_5$ = []; +try { + var a = __using(__bun_temp_ref_5$, b, 0); + var j = __using(__bun_temp_ref_5$, c(i), 1); + var k = __using(__bun_temp_ref_5$, l(m), 0); + var o = __using(__bun_temp_ref_5$, using, 0); + var p = __using(__bun_temp_ref_5$, await using, 1); + var q = r; +} catch (__bun_temp_ref_6$) { + var __bun_temp_ref_7$ = __bun_temp_ref_6$, __bun_temp_ref_8$ = 1; +} finally { + var __bun_temp_ref_9$ = __callDispose(__bun_temp_ref_5$, __bun_temp_ref_7$, __bun_temp_ref_8$); + __bun_temp_ref_9$ && await __bun_temp_ref_9$; +} + +export { + k, + q +}; +" +`; + +exports[`Bun.Transpiler using statements work right 1`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 0); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; +} finally { +__callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +}" +`; + +exports[`Bun.Transpiler using statements work right 2`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 1); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; +} finally { +var __bun_temp_ref_5$ = __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +__bun_temp_ref_5$ && await __bun_temp_ref_5$; +}" +`; + +exports[`Bun.Transpiler using statements work right 3`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 4`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 5`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 6`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 7`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 8`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 9`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 10`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using top level 1`] = ` +"import { +__callDispose as __callDispose, +__using as __using +} from "bun:wrap"; +export function c(e) { + let __bun_temp_ref_1$ = []; + try { + const f = __using(__bun_temp_ref_1$, g(a), 0); + return f.h; + } catch (__bun_temp_ref_2$) { + var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; + } finally { + __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); + } +} +import {using} from "n"; +let __bun_temp_ref_5$ = []; +try { + var a = __using(__bun_temp_ref_5$, b, 0); + var j = __using(__bun_temp_ref_5$, c(i), 1); + var k = __using(__bun_temp_ref_5$, l(m), 0); + var o = __using(__bun_temp_ref_5$, using, 0); + var p = __using(__bun_temp_ref_5$, await using, 1); + var q = r; +} catch (__bun_temp_ref_6$) { + var __bun_temp_ref_7$ = __bun_temp_ref_6$, __bun_temp_ref_8$ = 1; +} finally { + var __bun_temp_ref_9$ = __callDispose(__bun_temp_ref_5$, __bun_temp_ref_7$, __bun_temp_ref_8$); + __bun_temp_ref_9$ && await __bun_temp_ref_9$; +} + +export { + k, + q +}; +" +`; + +exports[`Bun.Transpiler using statements work right 1`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 0); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, +__bun_temp_ref_4$ = 1; +} finally { +__callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +}" +`; + +exports[`Bun.Transpiler using statements work right 2`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 1); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, +__bun_temp_ref_4$ = 1; +} finally { +var __bun_temp_ref_5$ = __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +__bun_temp_ref_5$ && await __bun_temp_ref_5$; +}" +`; + +exports[`Bun.Transpiler using statements work right 3`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 4`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 5`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 6`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 7`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 8`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 9`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 10`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, +__bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using top level 1`] = ` +"import { __callDispose as __callDispose, __using as __using } from "bun:wrap"; +export function c(e) { + let __bun_temp_ref_1$ = []; + try { + const f = __using(__bun_temp_ref_1$, g(a), 0); + return f.h; + } catch (__bun_temp_ref_2$) { + var __bun_temp_ref_3$ = __bun_temp_ref_2$, + __bun_temp_ref_4$ = 1; + } finally { + __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); + } +} +import { using } from "n"; +let __bun_temp_ref_5$ = []; +try { + var a = __using(__bun_temp_ref_5$, b, 0); + var j = __using(__bun_temp_ref_5$, c(i), 1); + var k = __using(__bun_temp_ref_5$, l(m), 0); + var o = __using(__bun_temp_ref_5$, using, 0); + var p = __using(__bun_temp_ref_5$, await using, 1); + var q = r; +} catch (__bun_temp_ref_6$) { + var __bun_temp_ref_7$ = __bun_temp_ref_6$, + __bun_temp_ref_8$ = 1; +} finally { + var __bun_temp_ref_9$ = __callDispose(__bun_temp_ref_5$, __bun_temp_ref_7$, __bun_temp_ref_8$); + __bun_temp_ref_9$ && await __bun_temp_ref_9$; +} + +export { + k, + q +}; +" +`; + +exports[`Bun.Transpiler using statements work right 1`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 0); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; +} finally { +__callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +}" +`; + +exports[`Bun.Transpiler using statements work right 2`] = ` +"let __bun_temp_ref_1$ = []; +try { +const x = __using(__bun_temp_ref_1$, a, 1); +} catch (__bun_temp_ref_2$) { +var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; +} finally { +var __bun_temp_ref_5$ = __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); +__bun_temp_ref_5$ && await __bun_temp_ref_5$; +}" +`; + +exports[`Bun.Transpiler using statements work right 3`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 4`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 5`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 6`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 7`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 8`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 0); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +__callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +} +}" +`; + +exports[`Bun.Transpiler using statements work right 9`] = ` +"for (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using statements work right 10`] = ` +"for await (const __bun_temp_ref_1$ of b) { +let __bun_temp_ref_2$ = []; +try { +const a = __using(__bun_temp_ref_2$, __bun_temp_ref_1$, 1); +c(a); +a(c); +} catch (__bun_temp_ref_3$) { +var __bun_temp_ref_4$ = __bun_temp_ref_3$, __bun_temp_ref_5$ = 1; +} finally { +var __bun_temp_ref_6$ = __callDispose(__bun_temp_ref_2$, __bun_temp_ref_4$, __bun_temp_ref_5$); +__bun_temp_ref_6$ && await __bun_temp_ref_6$; +} +}" +`; + +exports[`Bun.Transpiler using top level 1`] = ` +"import { __callDispose as __callDispose, __using as __using } from "bun:wrap"; +export function c(e) { + let __bun_temp_ref_1$ = []; + try { + const f = __using(__bun_temp_ref_1$, g(a), 0); + return f.h; + } catch (__bun_temp_ref_2$) { + var __bun_temp_ref_3$ = __bun_temp_ref_2$, __bun_temp_ref_4$ = 1; + } finally { + __callDispose(__bun_temp_ref_1$, __bun_temp_ref_3$, __bun_temp_ref_4$); + } +} +import { using } from "n"; +let __bun_temp_ref_5$ = []; +try { + var a = __using(__bun_temp_ref_5$, b, 0); + var j = __using(__bun_temp_ref_5$, c(i), 1); + var k = __using(__bun_temp_ref_5$, l(m), 0); + var o = __using(__bun_temp_ref_5$, using, 0); + var p = __using(__bun_temp_ref_5$, await using, 1); + var q = r; +} catch (__bun_temp_ref_6$) { + var __bun_temp_ref_7$ = __bun_temp_ref_6$, __bun_temp_ref_8$ = 1; +} finally { + var __bun_temp_ref_9$ = __callDispose(__bun_temp_ref_5$, __bun_temp_ref_7$, __bun_temp_ref_8$); + __bun_temp_ref_9$ && await __bun_temp_ref_9$; +} + +export { + k, + q +}; +" +`; diff --git a/test/transpiler/async-transpiler-entry.js b/test/bundler/transpiler/async-transpiler-entry.js similarity index 100% rename from test/transpiler/async-transpiler-entry.js rename to test/bundler/transpiler/async-transpiler-entry.js diff --git a/test/transpiler/async-transpiler-imported.js b/test/bundler/transpiler/async-transpiler-imported.js similarity index 100% rename from test/transpiler/async-transpiler-imported.js rename to test/bundler/transpiler/async-transpiler-imported.js diff --git a/test/transpiler/decorator-export-default-class-fixture-anon.ts b/test/bundler/transpiler/decorator-export-default-class-fixture-anon.ts similarity index 100% rename from test/transpiler/decorator-export-default-class-fixture-anon.ts rename to test/bundler/transpiler/decorator-export-default-class-fixture-anon.ts diff --git a/test/transpiler/decorator-export-default-class-fixture.ts b/test/bundler/transpiler/decorator-export-default-class-fixture.ts similarity index 100% rename from test/transpiler/decorator-export-default-class-fixture.ts rename to test/bundler/transpiler/decorator-export-default-class-fixture.ts diff --git a/test/transpiler/decorator-metadata.test.ts b/test/bundler/transpiler/decorator-metadata.test.ts similarity index 100% rename from test/transpiler/decorator-metadata.test.ts rename to test/bundler/transpiler/decorator-metadata.test.ts diff --git a/test/transpiler/decorators.test.ts b/test/bundler/transpiler/decorators.test.ts similarity index 99% rename from test/transpiler/decorators.test.ts rename to test/bundler/transpiler/decorators.test.ts index fd33ce3f9c7d6..e51fc524aff9a 100644 --- a/test/transpiler/decorators.test.ts +++ b/test/bundler/transpiler/decorators.test.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import { test, expect, describe } from "bun:test"; +import { describe, expect, test } from "bun:test"; import DecoratedClass from "./decorator-export-default-class-fixture"; import DecoratedAnonClass from "./decorator-export-default-class-fixture-anon"; diff --git a/test/transpiler/export-default-with-static-initializer.js b/test/bundler/transpiler/export-default-with-static-initializer.js similarity index 100% rename from test/transpiler/export-default-with-static-initializer.js rename to test/bundler/transpiler/export-default-with-static-initializer.js diff --git a/test/transpiler/export-default.test.js b/test/bundler/transpiler/export-default.test.js similarity index 78% rename from test/transpiler/export-default.test.js rename to test/bundler/transpiler/export-default.test.js index e557ffe00cddd..2ab9d08c814c9 100644 --- a/test/transpiler/export-default.test.js +++ b/test/bundler/transpiler/export-default.test.js @@ -1,5 +1,5 @@ +import { expect, test } from "bun:test"; import WithStatic from "./export-default-with-static-initializer"; -import { test, expect } from "bun:test"; test("static initializer", () => { expect(WithStatic.boop).toBe("boop"); diff --git a/test/transpiler/handlebars.hbs b/test/bundler/transpiler/handlebars.hbs similarity index 100% rename from test/transpiler/handlebars.hbs rename to test/bundler/transpiler/handlebars.hbs diff --git a/test/bundler/inline.macro.js b/test/bundler/transpiler/inline.macro.js similarity index 100% rename from test/bundler/inline.macro.js rename to test/bundler/transpiler/inline.macro.js diff --git a/test/bundler/macro-check.js b/test/bundler/transpiler/macro-check.js similarity index 100% rename from test/bundler/macro-check.js rename to test/bundler/transpiler/macro-check.js diff --git a/test/transpiler/macro-test.test.ts b/test/bundler/transpiler/macro-test.test.ts similarity index 100% rename from test/transpiler/macro-test.test.ts rename to test/bundler/transpiler/macro-test.test.ts index 8404ab4b1568e..5c95553400f5c 100644 --- a/test/transpiler/macro-test.test.ts +++ b/test/bundler/transpiler/macro-test.test.ts @@ -1,6 +1,6 @@ +import { escapeHTML } from "bun" assert { type: "macro" }; import { expect, test } from "bun:test"; import { addStrings, addStringsUTF16, escape, identity } from "./macro.ts" assert { type: "macro" }; -import { escapeHTML } from "bun" assert { type: "macro" }; test("bun builtins can be used in macros", async () => { expect(escapeHTML("abc!")).toBe("abc!"); diff --git a/test/transpiler/macro.ts b/test/bundler/transpiler/macro.ts similarity index 100% rename from test/transpiler/macro.ts rename to test/bundler/transpiler/macro.ts diff --git a/test/bundler/transpiler/preserve-use-strict-cjs.test.ts b/test/bundler/transpiler/preserve-use-strict-cjs.test.ts new file mode 100644 index 0000000000000..fc459cdc6bf7d --- /dev/null +++ b/test/bundler/transpiler/preserve-use-strict-cjs.test.ts @@ -0,0 +1,10 @@ +import { expect, test } from "bun:test"; +import path from "path"; + +test(`"use strict'; preserves strict mode in CJS`, async () => { + expect([path.join(import.meta.dir, "strict-mode-fixture.ts")]).toRun(); +}); + +test(`sloppy mode by default in CJS`, async () => { + expect([path.join(import.meta.dir, "sloppy-mode-fixture.ts")]).toRun(); +}); diff --git a/test/transpiler/property-non-ascii-fixture.js b/test/bundler/transpiler/property-non-ascii-fixture.js similarity index 100% rename from test/transpiler/property-non-ascii-fixture.js rename to test/bundler/transpiler/property-non-ascii-fixture.js diff --git a/test/transpiler/property.test.ts b/test/bundler/transpiler/property.test.ts similarity index 95% rename from test/transpiler/property.test.ts rename to test/bundler/transpiler/property.test.ts index df02790da441c..10786513ab04f 100644 --- a/test/transpiler/property.test.ts +++ b/test/bundler/transpiler/property.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; // See https://github.com/oven-sh/bun/pull/2939 diff --git a/test/transpiler/runtime-transpiler-fixture-duplicate-keys.json b/test/bundler/transpiler/runtime-transpiler-fixture-duplicate-keys.json similarity index 100% rename from test/transpiler/runtime-transpiler-fixture-duplicate-keys.json rename to test/bundler/transpiler/runtime-transpiler-fixture-duplicate-keys.json diff --git a/test/transpiler/runtime-transpiler-json-fixture.json b/test/bundler/transpiler/runtime-transpiler-json-fixture.json similarity index 100% rename from test/transpiler/runtime-transpiler-json-fixture.json rename to test/bundler/transpiler/runtime-transpiler-json-fixture.json diff --git a/test/transpiler/runtime-transpiler.test.ts b/test/bundler/transpiler/runtime-transpiler.test.ts similarity index 100% rename from test/transpiler/runtime-transpiler.test.ts rename to test/bundler/transpiler/runtime-transpiler.test.ts diff --git a/test/bundler/transpiler/sloppy-mode-fixture.ts b/test/bundler/transpiler/sloppy-mode-fixture.ts new file mode 100644 index 0000000000000..bf79cea9ebb26 --- /dev/null +++ b/test/bundler/transpiler/sloppy-mode-fixture.ts @@ -0,0 +1,11 @@ +function checkThis() { + if (this !== globalThis) { + throw new Error("this is not globalThis"); + } +} + +checkThis(); + +module.exports = { + FORCE_COMMON_JS: true, +}; diff --git a/test/bundler/transpiler/strict-mode-fixture.ts b/test/bundler/transpiler/strict-mode-fixture.ts new file mode 100644 index 0000000000000..89ddf31bb25a6 --- /dev/null +++ b/test/bundler/transpiler/strict-mode-fixture.ts @@ -0,0 +1,13 @@ +"use strict"; + +function checkThis() { + if (this !== undefined) { + throw new Error("this is not undefined"); + } +} + +checkThis(); + +module.exports = { + FORCE_COMMON_JS: true, +}; diff --git a/test/transpiler/template-literal-fixture-test.js b/test/bundler/transpiler/template-literal-fixture-test.js similarity index 100% rename from test/transpiler/template-literal-fixture-test.js rename to test/bundler/transpiler/template-literal-fixture-test.js diff --git a/test/transpiler/template-literal.test.ts b/test/bundler/transpiler/template-literal.test.ts similarity index 94% rename from test/transpiler/template-literal.test.ts rename to test/bundler/transpiler/template-literal.test.ts index a0fd2170f32ae..aa17d4839547c 100644 --- a/test/transpiler/template-literal.test.ts +++ b/test/bundler/transpiler/template-literal.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; import { join } from "path"; diff --git a/test/transpiler/transpiler-stack-overflow.test.ts b/test/bundler/transpiler/transpiler-stack-overflow.test.ts similarity index 88% rename from test/transpiler/transpiler-stack-overflow.test.ts rename to test/bundler/transpiler/transpiler-stack-overflow.test.ts index 7db3d7f3cff44..73835e8f788ba 100644 --- a/test/transpiler/transpiler-stack-overflow.test.ts +++ b/test/bundler/transpiler/transpiler-stack-overflow.test.ts @@ -1,7 +1,7 @@ -import { test, expect } from "bun:test"; -import { writeFileSync, mkdirSync } from "node:fs"; -import { join } from "path"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe, tmpdirSync } from "harness"; +import { mkdirSync, writeFileSync } from "node:fs"; +import { join } from "path"; test("long chain of expressions does not cause stack overflow", () => { const chain = `globalThis.a = {};` + "\n" + `globalThis.a + globalThis.a +`.repeat(1000000) + `globalThis.a` + "\n"; diff --git a/test/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js similarity index 96% rename from test/transpiler/transpiler.test.js rename to test/bundler/transpiler/transpiler.test.js index f02f71a371c53..8e8fd6afe4381 100644 --- a/test/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -1,4 +1,4 @@ -import { expect, it, describe } from "bun:test"; +import { describe, expect, it } from "bun:test"; import { hideFromStackTrace } from "harness"; describe("Bun.Transpiler", () => { @@ -18,14 +18,31 @@ describe("Bun.Transpiler", () => { }, platform: "browser", }); + const transpilerMinifySyntax = new Bun.Transpiler({ + loader: "tsx", + define: { + "process.env.NODE_ENV": JSON.stringify("development"), + user_undefined: "undefined", + user_nested: "location.origin", + "hello.earth": "hello.mars", + "Math.log": "console.error", + }, + macro: { + react: { + bacon: `${import.meta.dir}/macro-check.js`, + }, + }, + minify: { syntax: true }, + platform: "browser", + }); const ts = { - parsed: (code, trim = true, autoExport = false) => { + parsed: (code, trim = true, autoExport = false, minify = false) => { if (autoExport) { code = "export default (" + code + ")"; } - var out = transpiler.transformSync(code, "ts"); + var out = (minify ? transpilerMinifySyntax : transpiler).transformSync(code, "ts"); if (autoExport && out.startsWith("export default ")) { out = out.substring("export default ".length); } @@ -43,6 +60,10 @@ describe("Bun.Transpiler", () => { return out; }, + parsedMin: (code, trim = true, autoExport = false) => { + return ts.parsed(code, trim, autoExport, true); + }, + expectPrinted: (code, out) => { expect(ts.parsed(code, true, true)).toBe(out); }, @@ -51,6 +72,10 @@ describe("Bun.Transpiler", () => { expect(ts.parsed(code, !out.endsWith(";\n"), false)).toBe(out); }, + expectPrintedMin_: (code, out) => { + expect(ts.parsedMin(code, !out.endsWith(";\n"), false)).toBe(out); + }, + expectParseError: (code, message) => { try { ts.parsed(code, false, false); @@ -77,29 +102,31 @@ describe("Bun.Transpiler", () => { it("doesn't hang indefinitely #2746", () => { // this test passes by not hanging - expect(() => - transpiler.transformSync(` - class Test { - test() { - - } - `), - ).toThrow(); + expect(() => { + console.log('1'); + const y = transpiler.transformSync(` + class Test { + test() { + + } + `); + console.error(y); + }).toThrow(); }); describe("property access inlining", () => { it("bails out with spread", () => { - ts.expectPrinted_("const a = [...b][0];", "const a = [...b][0]"); - ts.expectPrinted_("const a = {...b}[0];", "const a = { ...b }[0]"); + ts.expectPrintedMin_("const a = [...b][0];", "const a = [...b][0]"); + ts.expectPrintedMin_("const a = {...b}[0];", "const a = { ...b }[0]"); }); it("bails out with multiple items", () => { - ts.expectPrinted_("const a = [b, c][0];", "const a = [b, c][0]"); + ts.expectPrintedMin_("const a = [b, c][0];", "const a = [b, c][0]"); }); it("works", () => { - ts.expectPrinted_('const a = ["hey"][0];', 'const a = "hey"'); + ts.expectPrintedMin_('const a = ["hey"][0];', 'const a = "hey"'); }); it("works nested", () => { - ts.expectPrinted_('const a = ["hey"][0][0];', 'const a = "h"'); + ts.expectPrintedMin_('const a = ["hey"][0][0];', 'const a = "h"'); }); }); @@ -592,7 +619,7 @@ describe("Bun.Transpiler", () => { exp("class Foo {}", "class Foo {\n}"); exp("Foo = class {}", "Foo = class {\n}"); exp("Foo = class Bar {}", "Foo = class Bar {\n}"); - exp("function foo() {}", "let foo = function() {\n}"); + exp("function foo() {}", "function foo() {\n}"); exp("foo = function () {}", "foo = function() {\n}"); exp("foo = function bar() {}", "foo = function bar() {\n}"); exp("class Foo { bar() {} }", "class Foo {\n bar() {\n }\n}"); @@ -905,12 +932,12 @@ export default class { export enum x { y } }`; const output1 = `var test; -(function(test) { +((test) => { let x; - (function(x) { + ((x) => { x[x["y"] = 0] = "y"; - })(x = test.x || (test.x = {})); -})(test || (test = {}))`; + })(x = test.x ||= {}); +})(test ||= {})`; it("namespace with exported enum", () => { ts.expectPrinted_(input1, output1); @@ -920,12 +947,12 @@ export default class { export enum x { y } }`; const output2 = `export var test; -(function(test) { +((test) => { let x; - (function(x) { + ((x) => { x[x["y"] = 0] = "y"; - })(x = test.x || (test.x = {})); -})(test || (test = {}))`; + })(x = test.x ||= {}); +})(test ||= {})`; it("exported namespace with exported enum", () => { ts.expectPrinted_(input2, output2); @@ -937,15 +964,15 @@ export default class { } }`; const output3 = `var first; -(function(first) { +((first) => { let second; - (function(second) { + ((second) => { let x; - (function(x) { + ((x) => { x[x["y"] = 0] = "y"; - })(x || (x = {})); - })(second = first.second || (first.second = {})); -})(first || (first = {}))`; + })(x ||= {}); + })(second = first.second ||= {}); +})(first ||= {})`; it("exported inner namespace", () => { ts.expectPrinted_(input3, output3); @@ -953,9 +980,9 @@ export default class { const input4 = `export enum x { y }`; const output4 = `export var x; -(function(x) { +((x) => { x[x["y"] = 0] = "y"; -})(x || (x = {}))`; +})(x ||= {})`; it("exported enum", () => { ts.expectPrinted_(input4, output4); @@ -1386,101 +1413,6 @@ console.log(
);`), ); }); - describe("inline JSX", () => { - const inliner = new Bun.Transpiler({ - loader: "tsx", - define: { - "process.env.NODE_ENV": JSON.stringify("production"), - user_undefined: "undefined", - }, - platform: "bun", - jsxOptimizationInline: true, - treeShaking: false, - inline: true, - deadCodeElimination: true, - allowBunRuntime: true, - - target: "bun", - tsconfig: JSON.stringify({ - compilerOptions: { - jsxImportSource: "react", - }, - }), - }); - - it("inlines static JSX into object literals", () => { - expect( - inliner - .transformSync( - ` -export var hi =
{123}
-export var hiWithKey =
{123}
-export var hiWithRef =
{123}
- -export var ComponentThatChecksDefaultProps = -export var ComponentThatChecksDefaultPropsAndHasChildren = my child -export var ComponentThatHasSpreadCausesDeopt = - -`.trim(), - ) - .replaceAll("\n", "") - .replaceAll(" ", "") - .trim(), - ).toBe( - // TODO: figure out why its using jsxDEV() here. It doesn't do that with NODE_ENV=production at runtime. - ` - import { - $$typeof as $$typeof_4ad651bb3f5de058, - __merge as __merge_e79ebbbc0cc1f55b - } from "bun:wrap"; - export var hi = { - $$typeof: $$typeof_4ad651bb3f5de058, - type: "div", - key: null, - ref: null, - props: { - children: 123 - }, - _owner: null - }, hiWithKey = { - $$typeof: $$typeof_4ad651bb3f5de058, - type: "div", - key: "hey", - ref: null, - props: { - children: 123 - }, - _owner: null - }, hiWithRef = jsxDEV("div", { - ref: foo, - children: 123 - }, void 0, !1, void 0, this), ComponentThatChecksDefaultProps = { - $$typeof: $$typeof_4ad651bb3f5de058, - type: Hello, - key: null, - ref: null, - props: Hello.defaultProps || {}, - _owner: null - }, ComponentThatChecksDefaultPropsAndHasChildren = { - $$typeof: $$typeof_4ad651bb3f5de058, - type: Hello, - key: null, - ref: null, - props: __merge_e79ebbbc0cc1f55b({ - children: "my child" - }, Hello.defaultProps), - _owner: null - }, ComponentThatHasSpreadCausesDeopt = jsxDEV(Hello, { - ...spread - }, void 0, !1, void 0, this); - ` - .replaceAll("\n", "") - .replaceAll(" ", "") - .trim(), - ); - }); - }); - it("JSX spread children", () => { var bun = new Bun.Transpiler({ loader: "jsx", @@ -1512,8 +1444,10 @@ export var ComponentThatHasSpreadCausesDeopt = }); it("CommonJS", () => { - var nodeTranspiler = new Bun.Transpiler({ platform: "node" }); - expect(nodeTranspiler.transformSync("module.require('hi' + 123)")).toBe('require("hi" + 123);\n'); + var nodeTranspiler = new Bun.Transpiler({ platform: "node", minify: { syntax: false } }); + + // note: even if minify syntax is off, constant folding must happen within require calls + expect(nodeTranspiler.transformSync("module.require('hi' + 123)")).toBe('require("hi123");\n'); expect(nodeTranspiler.transformSync("module.require(1 ? 'foo' : 'bar')")).toBe('require("foo");\n'); expect(nodeTranspiler.transformSync("require(1 ? 'foo' : 'bar')")).toBe('require("foo");\n'); @@ -1598,6 +1532,10 @@ export var ComponentThatHasSpreadCausesDeopt = expect(parsed(code, !out.endsWith(";\n"), false)).toBe(out); }; + const expectPrintedMin_ = (code, out) => { + expect(parsed(code, !out.endsWith(";\n"), false, transpilerMinifySyntax)).toBe(out); + }; + const expectPrintedNoTrim = (code, out) => { expect(parsed(code, false, false)).toBe(out); }; @@ -1731,7 +1669,7 @@ export var ComponentThatHasSpreadCausesDeopt = }); it("import with unicode escape", () => { - expectPrinted_(`import { name } from 'mod\\u1011';`, `import {name} from "mod\\u1011"`); + expectPrinted_(`import { name } from 'mod\\u1011';`, `import { name } from "mod\\u1011"`); }); it("fold string addition", () => { @@ -1748,14 +1686,14 @@ console.log(a) `.trim(), ); - expectPrinted_(`export const foo = "a" + "b";`, `export const foo = "ab"`); - expectPrinted_( + expectPrintedMin_(`export const foo = "a" + "b";`, `export const foo = "ab"`); + expectPrintedMin_( `export const foo = "F" + "0" + "F" + "0123456789" + "ABCDEF" + "0123456789ABCDEFF0123456789ABCDEF00" + "b";`, `export const foo = "F0F0123456789ABCDEF0123456789ABCDEFF0123456789ABCDEF00b"`, ); - expectPrinted_(`export const foo = "a" + 1 + "b";`, `export const foo = "a" + 1 + "b"`); - expectPrinted_(`export const foo = "a" + "b" + 1 + "b";`, `export const foo = "ab" + 1 + "b"`); - expectPrinted_(`export const foo = "a" + "b" + 1 + "b" + "c";`, `export const foo = "ab" + 1 + "bc"`); + expectPrintedMin_(`export const foo = "a" + 1 + "b";`, `export const foo = "a1b"`); + expectPrintedMin_(`export const foo = "a" + "b" + 1 + "b";`, `export const foo = "ab1b"`); + expectPrintedMin_(`export const foo = "a" + "b" + 1 + "b" + "c";`, `export const foo = "ab1bc"`); }); it("numeric constants", () => { @@ -2866,6 +2804,10 @@ console.log(foo, array); }); it("constant folding", () => { + const expectPrinted = (code, out) => { + expect(parsed(code, true, true, transpilerMinifySyntax)).toBe(out); + }; + // we have an optimization for numbers 0 - 100, -0 - -100 so we must test those specifically // https://github.com/oven-sh/bun/issues/2810 for (let i = 1; i < 120; i++) { @@ -2970,7 +2912,7 @@ console.log(foo, array); expectPrinted("x + 'a' + 'b'", 'x + "ab"'); expectPrinted("x + 'a' + 'bc'", 'x + "abc"'); expectPrinted("x + 'ab' + 'c'", 'x + "abc"'); - expectPrinted("'a' + 1", '"a" + 1'); + expectPrinted("'a' + 1", '"a1"'); expectPrinted("x * 'a' + 'b'", 'x * "a" + "b"'); // rope string push another rope string @@ -3197,7 +3139,7 @@ console.log(foo, array); import {ɵtest} from 'foo' `); - expect(out).toBe('import {ɵtest} from "foo";\n'); + expect(out).toBe('import { ɵtest } from "foo";\n'); }); const importLines = ["import {createElement, bacon} from 'react';", "import {bacon, createElement} from 'react';"]; @@ -3273,10 +3215,10 @@ console.log(foo, array); }); it('`str` + "``"', () => { - expectPrinted_('const x = `str` + "``";', "const x = `str\\`\\``"); - expectPrinted_('const x = `` + "`";', "const x = `\\``"); - expectPrinted_('const x = `` + "``";', "const x = `\\`\\``"); - expectPrinted_('const x = "``" + ``;', "const x = `\\`\\``"); + expectPrintedMin_('const x = `str` + "``";', 'const x = "str``"'); + expectPrintedMin_('const x = `` + "`";', 'const x = "`"'); + expectPrintedMin_('const x = `` + "``";', 'const x = "``"'); + expectPrintedMin_('const x = "``" + ``;', 'const x = "``"'); }); }); diff --git a/test/transpiler/tsconfig.is-just-a-number.json b/test/bundler/transpiler/tsconfig.is-just-a-number.json similarity index 100% rename from test/transpiler/tsconfig.is-just-a-number.json rename to test/bundler/transpiler/tsconfig.is-just-a-number.json diff --git a/test/transpiler/tsconfig.with-commas.json b/test/bundler/transpiler/tsconfig.with-commas.json similarity index 100% rename from test/transpiler/tsconfig.with-commas.json rename to test/bundler/transpiler/tsconfig.with-commas.json diff --git a/test/transpiler/with-statement-works.js b/test/bundler/transpiler/with-statement-works.js similarity index 100% rename from test/transpiler/with-statement-works.js rename to test/bundler/transpiler/with-statement-works.js diff --git a/test/bunfig.toml b/test/bunfig.toml new file mode 100644 index 0000000000000..3dbe4143f947d --- /dev/null +++ b/test/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = "./preload.ts" diff --git a/test/cli/bun.test.ts b/test/cli/bun.test.ts index 9b48eeba3e4d0..c6dd62858fb56 100644 --- a/test/cli/bun.test.ts +++ b/test/cli/bun.test.ts @@ -1,8 +1,8 @@ -import { describe, test, expect } from "bun:test"; import { spawnSync } from "bun"; +import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; -import { tmpdir } from "node:os"; import fs from "node:fs"; +import { tmpdir } from "node:os"; describe("bun", () => { describe("NO_COLOR", () => { diff --git a/test/cli/hot/hot-file-loader.css b/test/cli/hot/hot-file-loader.css new file mode 100644 index 0000000000000..1e30d2166c07b --- /dev/null +++ b/test/cli/hot/hot-file-loader.css @@ -0,0 +1,3 @@ +* { + background-color: red; +} diff --git a/test/cli/hot/hot-file-loader.file b/test/cli/hot/hot-file-loader.file new file mode 100644 index 0000000000000..f2ba8f84ab5c1 --- /dev/null +++ b/test/cli/hot/hot-file-loader.file @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/test/cli/hot/hot-runner.js b/test/cli/hot/hot-runner.js index ab58fa21a7566..1023740e9d9a1 100644 --- a/test/cli/hot/hot-runner.js +++ b/test/cli/hot/hot-runner.js @@ -1,3 +1,5 @@ +import "./hot-file-loader.css"; +import "./hot-file-loader.file"; import "./hot-runner-imported"; globalThis.counter ??= 0; diff --git a/test/cli/hot/hot.test.ts b/test/cli/hot/hot.test.ts index c2bc6d4f31862..b819cdff2f035 100644 --- a/test/cli/hot/hot.test.ts +++ b/test/cli/hot/hot.test.ts @@ -1,9 +1,12 @@ import { spawn } from "bun"; import { beforeEach, expect, it } from "bun:test"; -import { bunExe, bunEnv, tmpdirSync, isDebug, isWindows } from "harness"; -import { cpSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, copyFileSync } from "fs"; +import { copyFileSync, cpSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync } from "fs"; +import { bunEnv, bunExe, isDebug, tmpdirSync, waitForFileToExist } from "harness"; import { join } from "path"; +const timeout = isDebug ? Infinity : 10_000; +const longTimeout = isDebug ? Infinity : 30_000; + let hotRunnerRoot: string = "", cwd = ""; beforeEach(() => { @@ -14,311 +17,387 @@ beforeEach(() => { cwd = hotPath; }); -it("should hot reload when file is overwritten", async () => { - const root = hotRunnerRoot; - try { - var runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - cwd, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); - - var reloadCounter = 0; - - async function onReload() { - writeFileSync(root, readFileSync(root, "utf-8")); - } - - var str = ""; - for await (const line of runner.stdout) { - str += new TextDecoder().decode(line); - var any = false; - if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; +it( + "should hot reload when file is overwritten", + async () => { + const root = hotRunnerRoot; + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); + + var reloadCounter = 0; + + async function onReload() { + writeFileSync(root, readFileSync(root, "utf-8")); + } - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - str = ""; + var str = ""; + for await (const line of runner.stdout) { + str += new TextDecoder().decode(line); + var any = false; + if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; + + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + str = ""; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; } - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + if (any) await onReload(); } - if (any) await onReload(); + expect(reloadCounter).toBeGreaterThanOrEqual(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } + }, + timeout, +); - expect(reloadCounter).toBe(3); - } finally { - // @ts-ignore - runner?.unref?.(); - // @ts-ignore - runner?.kill?.(9); - } -}); - -it("should recover from errors", async () => { - const root = hotRunnerRoot; - try { - var runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - cwd, - stdout: "pipe", - stderr: "pipe", - stdin: "ignore", - }); +it.each(["hot-file-loader.file", "hot-file-loader.css"])( + "should hot reload when `%s` is overwritten", + async (targetFilename: string) => { + const root = hotRunnerRoot; + const target = join(cwd, targetFilename); + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); + + var reloadCounter = 0; + + async function onReload() { + writeFileSync(target, readFileSync(target, "utf-8")); + } - let reloadCounter = 0; - const input = readFileSync(root, "utf-8"); - function onReloadGood() { - writeFileSync(root, input); - } + var str = ""; + for await (const line of runner.stdout) { + str += new TextDecoder().decode(line); + var any = false; + if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; + + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + str = ""; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } - function onReloadError() { - writeFileSync(root, "throw new Error('error');\n"); - } + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; + } - var queue = [onReloadError, onReloadGood, onReloadError, onReloadGood]; - var errors: string[] = []; - var onError: (...args: any[]) => void; - (async () => { - for await (let line of runner.stderr) { - var str = new TextDecoder().decode(line); - errors.push(str); - // @ts-ignore - onError && onError(str); + if (any) await onReload(); } - })(); - var str = ""; - for await (const line of runner.stdout) { - str += new TextDecoder().decode(line); - var any = false; - if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; - - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - str = ""; + expect(reloadCounter).toBeGreaterThanOrEqual(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); + } + }, + timeout, +); - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; - } +it( + "should recover from errors", + async () => { + const root = hotRunnerRoot; + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + let reloadCounter = 0; + const input = readFileSync(root, "utf-8"); + function onReloadGood() { + writeFileSync(root, input); + } - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + function onReloadError() { + writeFileSync(root, "throw new Error('error');\n"); } - if (any) { - queue.shift()!(); - await new Promise((resolve, reject) => { - if (errors.length > 0) { - errors.length = 0; - resolve(); - return; + var queue = [onReloadError, onReloadGood, onReloadError, onReloadGood]; + var errors: string[] = []; + var onError: (...args: any[]) => void; + (async () => { + for await (let line of runner.stderr) { + var str = new TextDecoder().decode(line); + errors.push(str); + // @ts-ignore + onError && onError(str); + } + })(); + + var str = ""; + for await (const line of runner.stdout) { + str += new TextDecoder().decode(line); + var any = false; + if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; + + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + str = ""; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; } - onError = resolve; - }); + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; + } - queue.shift()!(); - } - } + if (any) { + queue.shift()!(); + await new Promise((resolve, reject) => { + if (errors.length > 0) { + errors.length = 0; + resolve(); + return; + } - expect(reloadCounter).toBe(3); - } finally { - // @ts-ignore - runner?.unref?.(); - // @ts-ignore - runner?.kill?.(9); - } -}); + onError = resolve; + }); -it("should not hot reload when a random file is written", async () => { - const root = hotRunnerRoot; - try { - var runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - cwd, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + queue.shift()!(); + } + } - let reloadCounter = 0; - const code = readFileSync(root, "utf-8"); - async function onReload() { - writeFileSync(root + ".another.yet.js", code); - unlinkSync(root + ".another.yet.js"); + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } - var finished = false; - await Promise.race([ - Bun.sleep(200), - (async () => { - if (finished) { - return; - } - var str = ""; - for await (const line of runner.stdout) { + }, + timeout, +); + +it( + "should not hot reload when a random file is written", + async () => { + const root = hotRunnerRoot; + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); + + let reloadCounter = 0; + const code = readFileSync(root, "utf-8"); + async function onReload() { + writeFileSync(root + ".another.yet.js", code); + unlinkSync(root + ".another.yet.js"); + } + var finished = false; + await Promise.race([ + Bun.sleep(200), + (async () => { if (finished) { return; } - - str += new TextDecoder().decode(line); - if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; - - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; + var str = ""; + for await (const line of runner.stdout) { if (finished) { return; } - await onReload(); - reloadCounter++; - str = ""; - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - } - } - })(), - ]); - finished = true; - runner.kill(0); - runner.unref(); - - expect(reloadCounter).toBe(1); - } finally { - // @ts-ignore - runner?.unref?.(); - // @ts-ignore - runner?.kill?.(9); - } -}); + str += new TextDecoder().decode(line); + if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; -it("should hot reload when a file is deleted and rewritten", async () => { - try { - const root = hotRunnerRoot + ".tmp.js"; - copyFileSync(hotRunnerRoot, root); - var runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - cwd, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); - - var reloadCounter = 0; + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + if (finished) { + return; + } + await onReload(); - async function onReload() { - const contents = readFileSync(root, "utf-8"); - rmSync(root); - writeFileSync(root, contents); + reloadCounter++; + str = ""; + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + } + } + })(), + ]); + finished = true; + runner.kill(0); + runner.unref(); + + expect(reloadCounter).toBe(1); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } + }, + timeout, +); - var str = ""; - for await (const line of runner.stdout) { - str += new TextDecoder().decode(line); - var any = false; - if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; +it( + "should hot reload when a file is deleted and rewritten", + async () => { + try { + const root = hotRunnerRoot + ".tmp.js"; + copyFileSync(hotRunnerRoot, root); + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); + + var reloadCounter = 0; + + async function onReload() { + const contents = readFileSync(root, "utf-8"); + rmSync(root); + writeFileSync(root, contents); + } - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - str = ""; + var str = ""; + for await (const line of runner.stdout) { + str += new TextDecoder().decode(line); + var any = false; + if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; + + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + str = ""; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; } - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + if (any) await onReload(); } - - if (any) await onReload(); - } - rmSync(root); - expect(reloadCounter).toBe(3); - } finally { - // @ts-ignore - runner?.unref?.(); - // @ts-ignore - runner?.kill?.(9); - } -}); - -it("should hot reload when a file is renamed() into place", async () => { - const root = hotRunnerRoot + ".tmp.js"; - copyFileSync(hotRunnerRoot, root); - try { - var runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - cwd, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); - - var reloadCounter = 0; - - async function onReload() { - const contents = readFileSync(root, "utf-8"); - rmSync(root + ".tmpfile", { force: true }); - await 1; - writeFileSync(root + ".tmpfile", contents); - await 1; rmSync(root); - await 1; - renameSync(root + ".tmpfile", root); - await 1; + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } + }, + timeout, +); - var str = ""; - for await (const line of runner.stdout) { - str += new TextDecoder().decode(line); - var any = false; - if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; +it( + "should hot reload when a file is renamed() into place", + async () => { + const root = hotRunnerRoot + ".tmp.js"; + copyFileSync(hotRunnerRoot, root); + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); + + var reloadCounter = 0; + + async function onReload() { + const contents = readFileSync(root, "utf-8"); + rmSync(root + ".tmpfile", { force: true }); + await 1; + writeFileSync(root + ".tmpfile", contents); + await 1; + rmSync(root); + await 1; + renameSync(root + ".tmpfile", root); + await 1; + } - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - str = ""; + var str = ""; + for await (const line of runner.stdout) { + str += new TextDecoder().decode(line); + var any = false; + if (!/\[#!root\].*[0-9]\n/g.test(str)) continue; + + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + str = ""; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; } - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + if (any) await onReload(); } - - if (any) await onReload(); + rmSync(root); + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } - rmSync(root); - expect(reloadCounter).toBe(3); - } finally { - // @ts-ignore - runner?.unref?.(); - // @ts-ignore - runner?.kill?.(9); - } -}); + }, + timeout, +); const comment_spam = ("//" + "B".repeat(2000) + "\n").repeat(1000); it( @@ -385,81 +464,86 @@ ${" ".repeat(reloadCounter * 2)}throw new Error(${reloadCounter});`, await runner.exited; expect(reloadCounter).toBe(50); }, - isDebug ? Infinity : 10_000, + timeout, ); -it("should work with sourcemap loading", async () => { - let bundleIn = join(cwd, "bundle_in.ts"); - rmSync(hotRunnerRoot); - writeFileSync( - bundleIn, - `// source content -// -// -throw new Error('0');`, - ); - await using bundler = spawn({ - cmd: [bunExe(), "build", "--watch", bundleIn, "--target=bun", "--sourcemap", "--outfile", hotRunnerRoot], - env: bunEnv, - cwd, - stdout: "inherit", - stderr: "inherit", - stdin: "ignore", - }); - await using runner = spawn({ - cmd: [bunExe(), "--hot", "run", hotRunnerRoot], - env: bunEnv, - cwd, - stdout: "ignore", - stderr: "pipe", - stdin: "ignore", - }); - let reloadCounter = 0; - function onReload() { +it( + "should work with sourcemap loading", + async () => { + let bundleIn = join(cwd, "bundle_in.ts"); + rmSync(hotRunnerRoot); writeFileSync( bundleIn, `// source content +// +// +throw new Error('0');`, + ); + await using bundler = spawn({ + cmd: [bunExe(), "build", "--watch", bundleIn, "--target=bun", "--sourcemap", "--outfile", hotRunnerRoot], + env: bunEnv, + cwd, + stdout: "inherit", + stderr: "inherit", + stdin: "ignore", + }); + waitForFileToExist(hotRunnerRoot, 20); + await using runner = spawn({ + cmd: [bunExe(), "--hot", "run", hotRunnerRoot], + env: bunEnv, + cwd, + stdout: "ignore", + stderr: "pipe", + stdin: "ignore", + }); + let reloadCounter = 0; + function onReload() { + writeFileSync( + bundleIn, + `// source content // etc etc // etc etc ${" ".repeat(reloadCounter * 2)}throw new Error(${reloadCounter});`, - ); - } - let str = ""; - outer: for await (const chunk of runner.stderr) { - str += new TextDecoder().decode(chunk); - var any = false; - if (!/error: .*[0-9]\n.*?\n/g.test(str)) continue; - - let it = str.split("\n"); - let line; - while ((line = it.shift())) { - if (!line.includes("error")) continue; - str = ""; - - if (reloadCounter === 50) { - runner.kill(); - break; - } + ); + } + let str = ""; + outer: for await (const chunk of runner.stderr) { + str += new TextDecoder().decode(chunk); + var any = false; + if (!/error: .*[0-9]\n.*?\n/g.test(str)) continue; + + let it = str.split("\n"); + let line; + while ((line = it.shift())) { + if (!line.includes("error")) continue; + str = ""; - if (line.includes(`error: ${reloadCounter - 1}`)) { - onReload(); // re-save file to prevent deadlock - continue outer; + if (reloadCounter === 50) { + runner.kill(); + break; + } + + if (line.includes(`error: ${reloadCounter - 1}`)) { + onReload(); // re-save file to prevent deadlock + continue outer; + } + expect(line).toContain(`error: ${reloadCounter}`); + reloadCounter++; + + let next = it.shift()!; + expect(next).toInclude("bundle_in.ts"); + const col = next.match(/\s*at.*?:4:(\d+)$/)![1]; + expect(Number(col)).toBe(1 + "throw ".length + (reloadCounter - 1) * 2); + any = true; } - expect(line).toContain(`error: ${reloadCounter}`); - reloadCounter++; - - let next = it.shift()!; - expect(next).toInclude("bundle_in.ts"); - const col = next.match(/\s*at.*?:4:(\d+)$/)![1]; - expect(Number(col)).toBe(1 + "throw ".length + (reloadCounter - 1) * 2); - any = true; - } - if (any) await onReload(); - } - expect(reloadCounter).toBe(50); - bundler.kill(); -}); + if (any) await onReload(); + } + expect(reloadCounter).toBe(50); + bundler.kill(); + }, + timeout, +); const long_comment = "BBBB".repeat(100000); @@ -493,6 +577,7 @@ throw new Error('0');`, stderr: "ignore", stdin: "ignore", }); + waitForFileToExist(hotRunnerRoot, 20); await using runner = spawn({ cmd: [ // @@ -566,5 +651,5 @@ ${" ".repeat(reloadCounter * 2)}throw new Error(${reloadCounter});`, // TODO: bun has a memory leak when --hot is used on very large files // console.log({ sampleMemory10, sampleMemory100 }); }, - isDebug ? Infinity : 20_000, + longTimeout, ); diff --git a/test/cli/hot/watch.test.ts b/test/cli/hot/watch.test.ts index db664ad1e77c6..65c336c57d3c3 100644 --- a/test/cli/hot/watch.test.ts +++ b/test/cli/hot/watch.test.ts @@ -1,19 +1,19 @@ import { spawn } from "bun"; import { describe, expect, test } from "bun:test"; -import { writeFile } from "fs/promises"; import { bunEnv, bunExe, forEachLine, tempDirWithFiles } from "harness"; -import { join } from "path"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; describe("--watch works", async () => { - for (let watchedFile of ["tmp.js", "entry.js"]) { - test("with " + watchedFile, async () => { + for (const watchedFile of ["entry.js", "tmp.js"]) { + test(`with ${watchedFile}`, async () => { const tmpdir_ = tempDirWithFiles("watch-fixture", { "tmp.js": "console.log('hello #1')", "entry.js": "import './tmp.js'", "package.json": JSON.stringify({ name: "foo", version: "0.0.1" }), }); + await Bun.sleep(1000); const tmpfile = join(tmpdir_, "tmp.js"); - await writeFile(tmpfile, "console.log('hello #1')"); const process = spawn({ cmd: [bunExe(), "--watch", join(tmpdir_, watchedFile)], cwd: tmpdir_, @@ -22,7 +22,7 @@ describe("--watch works", async () => { }); const { stdout } = process; - let iter = forEachLine(stdout); + const iter = forEachLine(stdout); let { value: line, done } = await iter.next(); expect(done).toBe(false); expect(line).toBe("hello #1"); @@ -43,7 +43,7 @@ describe("--watch works", async () => { ({ value: line } = await iter.next()); expect(line).toBe("hello #5"); - process.kill(); + process.kill("SIGKILL"); await process.exited; }); } diff --git a/test/cli/init/init.test.ts b/test/cli/init/init.test.ts index a12889b88fcca..fb8f7c2ee82b8 100644 --- a/test/cli/init/init.test.ts +++ b/test/cli/init/init.test.ts @@ -1,7 +1,7 @@ +import { expect, test } from "bun:test"; import fs from "fs"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; import path from "path"; -import { bunExe, bunEnv, tmpdirSync } from "harness"; -import { test, expect } from "bun:test"; test("bun init works", () => { const temp = tmpdirSync(); diff --git a/test/cli/inspect/inspect.test.ts b/test/cli/inspect/inspect.test.ts index 30897ea619486..e648ad8c8a952 100644 --- a/test/cli/inspect/inspect.test.ts +++ b/test/cli/inspect/inspect.test.ts @@ -1,6 +1,6 @@ -import { test, expect, afterEach } from "bun:test"; -import { bunExe, bunEnv, randomPort } from "harness"; import { Subprocess, spawn } from "bun"; +import { afterEach, expect, test } from "bun:test"; +import { bunEnv, bunExe, randomPort } from "harness"; import { WebSocket } from "ws"; let inspectee: Subprocess; diff --git a/test/cli/install/__snapshots__/bun-install-dep.test.ts.snap b/test/cli/install/__snapshots__/bun-install-dep.test.ts.snap new file mode 100644 index 0000000000000..94cd72502d693 --- /dev/null +++ b/test/cli/install/__snapshots__/bun-install-dep.test.ts.snap @@ -0,0 +1,397 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`npa @scoped/package 1`] = ` +{ + "name": "@scoped/package", + "version": { + "name": "@scoped/package", + "tag": "latest", + "type": "dist_tag", + }, +} +`; + +exports[`npa @scoped/package@1.0.0 1`] = ` +{ + "name": "@scoped/package", + "version": { + "alias": false, + "name": "@scoped/package", + "type": "npm", + "version": "==1.0.0", + }, +} +`; + +exports[`npa @scoped/package@1.0.0-beta.1 1`] = ` +{ + "name": "@scoped/package", + "version": { + "alias": false, + "name": "@scoped/package", + "type": "npm", + "version": "==1.0.0-beta.1", + }, +} +`; + +exports[`npa @scoped/package@1.0.0-beta.1+build.123 1`] = ` +{ + "name": "@scoped/package", + "version": { + "alias": false, + "name": "@scoped/package", + "type": "npm", + "version": "==1.0.0-beta.1+build.123", + }, +} +`; + +exports[`npa package 1`] = ` +{ + "name": "package", + "version": { + "name": "package", + "tag": "latest", + "type": "dist_tag", + }, +} +`; + +exports[`npa package@1.0.0 1`] = ` +{ + "name": "package", + "version": { + "alias": false, + "name": "package", + "type": "npm", + "version": "==1.0.0", + }, +} +`; + +exports[`npa package@1.0.0-beta.1 1`] = ` +{ + "name": "package", + "version": { + "alias": false, + "name": "package", + "type": "npm", + "version": "==1.0.0-beta.1", + }, +} +`; + +exports[`npa package@1.0.0-beta.1+build.123 1`] = ` +{ + "name": "package", + "version": { + "alias": false, + "name": "package", + "type": "npm", + "version": "==1.0.0-beta.1+build.123", + }, +} +`; + +exports[`npa bitbucket:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "bitbucket:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa bitbucket.org:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "bitbucket.org:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa bitbucket.com:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "bitbucket.com:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa git@bitbucket.org:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "git@bitbucket.org:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa foo/bar 1`] = ` +{ + "name": "", + "version": { + "owner": "foo", + "ref": "", + "repo": "bar", + "type": "github", + }, +} +`; + +exports[`npa gitlab:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "gitlab:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa gitlab.com:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "gitlab.com:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa http://localhost:5000/no-deps/-/no-deps-2.0.0.tgz 1`] = ` +{ + "name": "", + "version": { + "name": "", + "type": "tarball", + "url": "http://localhost:5000/no-deps/-/no-deps-2.0.0.tgz", + }, +} +`; + +exports[`npa https://registry.npmjs.org/no-deps/-/no-deps-2.0.0.tgz 1`] = ` +{ + "name": "", + "version": { + "name": "", + "type": "tarball", + "url": "https://registry.npmjs.org/no-deps/-/no-deps-2.0.0.tgz", + }, +} +`; + +exports[`npa file:./path/to/tarball.tgz 1`] = ` +{ + "name": "", + "version": { + "name": "", + "path": "./path/to/tarball.tgz", + "type": "tarball", + }, +} +`; + +exports[`npa ./path/to/tarball.tgz 1`] = ` +{ + "name": "", + "version": { + "name": "", + "path": "./path/to/tarball.tgz", + "type": "tarball", + }, +} +`; + +exports[`npa foo/bar 2`] = ` +{ + "name": "", + "version": { + "owner": "foo", + "ref": "", + "repo": "bar", + "type": "github", + }, +} +`; + +exports[`npa github:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "dylan-conway", + "ref": "", + "repo": "public-install-test", + "type": "github", + }, +} +`; + +exports[`npa git@github.com:dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "", + "repo": "git@github.com:dylan-conway/public-install-test", + "type": "git", + }, +} +`; + +exports[`npa https://github.com/dylan-conway/public-install-test 1`] = ` +{ + "name": "", + "version": { + "owner": "dylan-conway", + "ref": "", + "repo": "public-install-test", + "type": "github", + }, +} +`; + +exports[`npa https://github.com/dylan-conway/public-install-test.git 1`] = ` +{ + "name": "", + "version": { + "owner": "dylan-conway", + "ref": "", + "repo": "public-install-test", + "type": "github", + }, +} +`; + +exports[`npa https://github.com/dylan-conway/public-install-test.git#semver:^1.0.0 1`] = ` +{ + "name": "", + "version": { + "owner": "", + "ref": "semver:^1.0.0", + "repo": "https://github.com/dylan-conway/public-install-test.git", + "type": "git", + }, +} +`; + +exports[`dependencies: {"foo": "1.2.3"} 1`] = ` +{ + "alias": false, + "name": "foo", + "type": "npm", + "version": "==1.2.3-foo", +} +`; + +exports[`dependencies: {"foo": "latest"} 1`] = ` +{ + "name": "foo", + "tag": "latest", + "type": "dist_tag", +} +`; + +exports[`dependencies: {"foo": "workspace:*"} 1`] = ` +{ + "name": "*foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"foo": "workspace:^1.0.0"} 1`] = ` +{ + "name": "^1.0.0foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"foo": "workspace:1.0.0"} 1`] = ` +{ + "name": "1.0.0foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"foo": "workspace:1.0.0-beta.1"} 1`] = ` +{ + "name": "1.0.0-beta.1foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"foo": "workspace:1.0.0-beta.1+build.123"} 1`] = ` +{ + "name": "1.0.0-beta.1+build.123foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"foo": "workspace:1.0.0-beta.1+build.123"} 2`] = ` +{ + "name": "1.0.0-beta.1+build.123foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"foo": "workspace:1.0.0-beta.1+build.123"} 3`] = ` +{ + "name": "1.0.0-beta.1+build.123foo", + "type": "workspace", +} +`; + +exports[`dependencies: {"bar": "^1.0.0"} 1`] = ` +{ + "alias": false, + "name": "bar", + "type": "npm", + "version": ">=1.0.0-bar <2.0.0", +} +`; + +exports[`dependencies: {"bar": "~1.0.0"} 1`] = ` +{ + "alias": false, + "name": "bar", + "type": "npm", + "version": ">=1.0.0-bar <1.1.0", +} +`; + +exports[`dependencies: {"bar": "> 1.0.0 < 2.0.0"} 1`] = ` +{ + "alias": false, + "name": "bar", + "type": "npm", + "version": ">1.0.0 && <2.0.0-bar", +} +`; + +exports[`dependencies: {"bar": "1.0.0 - 2.0.0"} 1`] = ` +{ + "alias": false, + "name": "bar", + "type": "npm", + "version": ">=1.0.0 <=2.0.0-bar", +} +`; diff --git a/test/cli/install/architecture-match.test.ts b/test/cli/install/architecture-match.test.ts new file mode 100644 index 0000000000000..17c9992b8b741 --- /dev/null +++ b/test/cli/install/architecture-match.test.ts @@ -0,0 +1,68 @@ +import { isArchitectureMatch, isOperatingSystemMatch } from "bun:internal-for-testing"; +import "harness"; + +import { describe, expect, test } from "bun:test"; + +describe("isArchitectureMatch", () => { + const trues = [ + ["wombo.com", "any"], + ["wombo.com", process.arch], + [], + ["any"], + ["any", process.arch], + [process.arch], + ["!ia32"], + ["!ia32", process.arch], + ["ia32", process.arch], + ["!mips", "!ia32"], + ]; + const falses = [ + ["wombo.com"], + ["ia32"], + ["any", "!" + process.arch], + ["!" + process.arch], + ["!ia32", "!" + process.arch], + ["!" + process.arch, process.arch], + ]; + for (let arch of trues) { + test(`${arch} === true`, () => { + expect(isArchitectureMatch(arch)).toBe(true); + }); + } + for (let arch of falses) { + test(`${arch} === false`, () => { + expect(isArchitectureMatch(arch)).toBe(false); + }); + } +}); + +describe("isOperatingSystemMatch", () => { + const trues = [ + [], + ["any"], + ["any", process.platform], + [process.platform], + ["!sunos"], + ["!sunos", process.platform], + ["sunos", process.platform], + ["!aix", "!sunos"], + ["wombo.com", "!aix"], + ]; + const falses = [ + ["aix"], + ["any", "!" + process.platform], + ["!" + process.platform], + ["!sunos", "!" + process.platform], + ["!" + process.platform, process.platform], + ]; + for (let os of trues) { + test(`${os} === true`, () => { + expect(isOperatingSystemMatch(os)).toBe(true); + }); + } + for (let os of falses) { + test(`${os} === false`, () => { + expect(isOperatingSystemMatch(os)).toBe(false); + }); + } +}); diff --git a/test/cli/install/bad-workspace.test.ts b/test/cli/install/bad-workspace.test.ts index c3d61e42502fb..36200df4e733b 100644 --- a/test/cli/install/bad-workspace.test.ts +++ b/test/cli/install/bad-workspace.test.ts @@ -1,7 +1,7 @@ import { spawnSync } from "bun"; -import { beforeEach, expect, test, beforeAll, setDefaultTimeout } from "bun:test"; +import { beforeAll, beforeEach, expect, setDefaultTimeout, test } from "bun:test"; import { writeFileSync } from "fs"; -import { bunExe, bunEnv, tmpdirSync } from "harness"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; let cwd: string; diff --git a/test/cli/install/bun-add.test.ts b/test/cli/install/bun-add.test.ts index 7c34d12d28d4d..385a14f28337c 100644 --- a/test/cli/install/bun-add.test.ts +++ b/test/cli/install/bun-add.test.ts @@ -1,7 +1,7 @@ import { file, spawn } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, expect, it, setDefaultTimeout } from "bun:test"; -import { bunExe, bunEnv as env, toHaveBins, toBeValidBin, toBeWorkspaceLink, tmpdirSync } from "harness"; -import { access, mkdir, readlink, rm, writeFile, copyFile, appendFile, readFile } from "fs/promises"; +import { access, appendFile, copyFile, mkdir, readlink, rm, writeFile } from "fs/promises"; +import { bunExe, bunEnv as env, tmpdirSync, toBeValidBin, toBeWorkspaceLink, toHaveBins } from "harness"; import { join, relative } from "path"; import { dummyAfterAll, @@ -202,11 +202,7 @@ it.each(["fileblah://"])("should reject invalid path without segfault: %s", asyn }); const err = await new Response(stderr).text(); expect(err).toContain("bun add"); - if (protocolPrefix === "file:///") { - expect(err).toContain("error: MissingPackageJSON"); - } else { - expect(err).toContain(`error: unrecognised dependency format: ${dep.replace(/\\\\/g, "/")}`); - } + expect(err).toContain(`error: unrecognised dependency format: ${dep}`); const out = await new Response(stdout).text(); expect(out).toBe(""); @@ -907,6 +903,35 @@ for (const { desc, dep } of gitNameTests) { }); } +it("git dep without package.json and with default branch", async () => { + await Bun.write( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + }), + ); + + const { stderr, exited } = spawn({ + cmd: [bunExe(), "add", "git@github.com:dylan-conway/install-test-no-packagejson"], + cwd: package_dir, + stdout: "ignore", + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + expect(await file(join(package_dir, "package.json")).json()).toEqual({ + name: "foo", + dependencies: { + "install-test-no-packagejson": "git@github.com:dylan-conway/install-test-no-packagejson", + }, + }); +}); + it("should let you add the same package twice", async () => { const urls: string[] = []; setHandler(dummyRegistry(urls, { "0.0.3": {} })); diff --git a/test/cli/install/bun-create.test.ts b/test/cli/install/bun-create.test.ts index b57d2e2faa577..1e8937a01451f 100644 --- a/test/cli/install/bun-create.test.ts +++ b/test/cli/install/bun-create.test.ts @@ -1,7 +1,7 @@ import { spawn, spawnSync } from "bun"; -import { beforeEach, expect, it, describe } from "bun:test"; +import { beforeEach, describe, expect, it } from "bun:test"; +import { exists, stat } from "fs/promises"; import { bunExe, bunEnv as env, tmpdirSync } from "harness"; -import { mkdir, stat, exists } from "fs/promises"; import { join } from "path"; let x_dir: string; diff --git a/test/cli/install/bun-install-dep.test.ts b/test/cli/install/bun-install-dep.test.ts new file mode 100644 index 0000000000000..03321aa867123 --- /dev/null +++ b/test/cli/install/bun-install-dep.test.ts @@ -0,0 +1,70 @@ +import { npa } from "bun:internal-for-testing"; +import { expect, test } from "bun:test"; + +const bitbucket = [ + "bitbucket:dylan-conway/public-install-test", + "bitbucket.org:dylan-conway/public-install-test", + "bitbucket.com:dylan-conway/public-install-test", + "git@bitbucket.org:dylan-conway/public-install-test", +]; + +const tarball_remote = [ + "http://localhost:5000/no-deps/-/no-deps-2.0.0.tgz", + "https://registry.npmjs.org/no-deps/-/no-deps-2.0.0.tgz", +]; + +const local_tarball = ["file:./path/to/tarball.tgz", "./path/to/tarball.tgz"]; +const github = ["foo/bar"]; +const folder = ["file:./path/to/folder"]; + +const gitlab = ["gitlab:dylan-conway/public-install-test", "gitlab.com:dylan-conway/public-install-test"]; + +const all = [ + "@scoped/package", + "@scoped/package@1.0.0", + "@scoped/package@1.0.0-beta.1", + "@scoped/package@1.0.0-beta.1+build.123", + "package", + "package@1.0.0", + "package@1.0.0-beta.1", + "package@1.0.0-beta.1+build.123", + ...bitbucket, + ...github, + ...gitlab, + ...tarball_remote, + ...local_tarball, + ...github, + "github:dylan-conway/public-install-test", + "git@github.com:dylan-conway/public-install-test", + "https://github.com/dylan-conway/public-install-test", + "https://github.com/dylan-conway/public-install-test.git", + "https://github.com/dylan-conway/public-install-test.git#semver:^1.0.0", +]; + +test.each(all)("npa %s", dep => { + expect(npa(dep)).toMatchSnapshot(); +}); + +const pkgJsonLike = [ + ["foo", "1.2.3"], + ["foo", "latest"], + ["foo", "workspace:*"], + ["foo", "workspace:^1.0.0"], + ["foo", "workspace:1.0.0"], + ["foo", "workspace:1.0.0-beta.1"], + ["foo", "workspace:1.0.0-beta.1+build.123"], + ["foo", "workspace:1.0.0-beta.1+build.123"], + ["foo", "workspace:1.0.0-beta.1+build.123"], + ["bar", "^1.0.0"], + ["bar", "~1.0.0"], + ["bar", "> 1.0.0 < 2.0.0"], + ["bar", "1.0.0 - 2.0.0"], +]; + +test.each(pkgJsonLike)('dependencies: {"%s": "%s"}', (name, version) => { + expect(npa(name, version)).toMatchSnapshot(); +}); + +test("bad", () => { + expect(() => npa("-123!}{P}{!P#$s")).toThrow(); +}); diff --git a/test/cli/install/bun-install-patch.test.ts b/test/cli/install/bun-install-patch.test.ts index 332aed1d1c5c8..1c47d0197dde2 100644 --- a/test/cli/install/bun-install-patch.test.ts +++ b/test/cli/install/bun-install-patch.test.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; -import { bunExe, bunEnv as env, toBeValidBin, toHaveBins, toBeWorkspaceLink, tempDirWithFiles, bunEnv } from "harness"; -import { afterAll, afterEach, beforeAll, beforeEach, expect, it, describe, test, setDefaultTimeout } from "bun:test"; +import { beforeAll, describe, expect, it, setDefaultTimeout, test } from "bun:test"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; beforeAll(() => { setDefaultTimeout(1000 * 60 * 5); diff --git a/test/cli/install/bun-install-pathname-trailing-slash.test.ts b/test/cli/install/bun-install-pathname-trailing-slash.test.ts index 68983ab312a1b..5f6e4b43db86a 100644 --- a/test/cli/install/bun-install-pathname-trailing-slash.test.ts +++ b/test/cli/install/bun-install-pathname-trailing-slash.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, expect, test } from "bun:test"; +import { beforeEach, expect, test } from "bun:test"; import { bunEnv, bunExe, tmpdirSync } from "harness"; import { join } from "path"; diff --git a/test/cli/install/bun-install-retry.test.ts b/test/cli/install/bun-install-retry.test.ts new file mode 100644 index 0000000000000..27f3f70522f2c --- /dev/null +++ b/test/cli/install/bun-install-retry.test.ts @@ -0,0 +1,111 @@ +import { file, spawn } from "bun"; +import { afterAll, afterEach, beforeAll, beforeEach, expect, it, setDefaultTimeout } from "bun:test"; +import { access, writeFile } from "fs/promises"; +import { bunExe, bunEnv as env, tmpdirSync, toBeValidBin, toBeWorkspaceLink, toHaveBins } from "harness"; +import { join } from "path"; +import { + dummyAfterAll, + dummyAfterEach, + dummyBeforeAll, + dummyBeforeEach, + dummyRegistry, + package_dir, + readdirSorted, + requested, + root_url, + setHandler, +} from "./dummy.registry"; + +beforeAll(dummyBeforeAll); +afterAll(dummyAfterAll); + +expect.extend({ + toHaveBins, + toBeValidBin, + toBeWorkspaceLink, +}); + +let port: string; +let add_dir: string; +beforeAll(() => { + setDefaultTimeout(1000 * 60 * 5); + port = new URL(root_url).port; +}); + +beforeEach(async () => { + add_dir = tmpdirSync(); + await dummyBeforeEach(); +}); +afterEach(async () => { + await dummyAfterEach(); +}); + +it("retries on 500", async () => { + const urls: string[] = []; + setHandler(dummyRegistry(urls, undefined, 4)); + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + version: "0.0.1", + }), + ); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "add", "BaR"], + cwd: package_dir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + const err = await new Response(stderr).text(); + expect(err).not.toContain("error:"); + expect(err).toContain("Saved lockfile"); + const out = await new Response(stdout).text(); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "installed BaR@0.0.2", + "", + "1 package installed", + ]); + expect(await exited).toBe(0); + expect(urls.sort()).toEqual([ + `${root_url}/BaR`, + `${root_url}/BaR`, + `${root_url}/BaR`, + `${root_url}/BaR`, + `${root_url}/BaR`, + `${root_url}/BaR`, + `${root_url}/BaR-0.0.2.tgz`, + `${root_url}/BaR-0.0.2.tgz`, + `${root_url}/BaR-0.0.2.tgz`, + `${root_url}/BaR-0.0.2.tgz`, + `${root_url}/BaR-0.0.2.tgz`, + `${root_url}/BaR-0.0.2.tgz`, + ]); + expect(requested).toBe(12); + await Promise.all([ + (async () => expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "BaR"]))(), + (async () => expect(await readdirSorted(join(package_dir, "node_modules", "BaR"))).toEqual(["package.json"]))(), + (async () => + expect(await file(join(package_dir, "node_modules", "BaR", "package.json")).json()).toEqual({ + name: "bar", + version: "0.0.2", + }))(), + (async () => + expect(await file(join(package_dir, "package.json")).text()).toEqual( + JSON.stringify( + { + name: "foo", + version: "0.0.1", + dependencies: { + BaR: "^0.0.2", + }, + }, + null, + 2, + ), + ))(), + async () => await access(join(package_dir, "bun.lockb")), + ]); +}); diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index 45c60f14651c5..072bcb22eea71 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -1,8 +1,7 @@ -import { $ } from "bun"; import { file, listen, Socket, spawn } from "bun"; -import { afterAll, afterEach, beforeAll, beforeEach, expect, it, describe, test, setDefaultTimeout } from "bun:test"; -import { bunExe, bunEnv as env, toBeValidBin, toHaveBins, toBeWorkspaceLink, tempDirWithFiles, bunEnv } from "harness"; -import { access, mkdir, readlink as readlink, realpath, rm, writeFile } from "fs/promises"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, setDefaultTimeout, test } from "bun:test"; +import { access, mkdir, readlink, rm, writeFile } from "fs/promises"; +import { bunEnv, bunExe, bunEnv as env, tempDirWithFiles, toBeValidBin, toBeWorkspaceLink, toHaveBins } from "harness"; import { join, sep } from "path"; import { dummyAfterAll, @@ -435,7 +434,7 @@ it("should handle @scoped authentication", async () => { } expect(await request.text()).toBeEmpty(); urls.push(request.url); - return new Response("Feeling lucky?", { status: 555 }); + return new Response("Feeling lucky?", { status: 422 }); }); // workaround against `writeFile(..., { flag: "a" })` await writeFile( @@ -454,7 +453,7 @@ foo = { token = "bar" } env, }); const err = await new Response(stderr).text(); - expect(err.split(/\r?\n/)).toContain(`error: GET ${url} - 555`); + expect(err.split(/\r?\n/)).toContain(`error: GET ${url} - 422`); expect(await new Response(stdout).text()).toBeEmpty(); expect(await exited).toBe(1); expect(urls.sort()).toEqual([url]); diff --git a/test/cli/install/bun-link.test.ts b/test/cli/install/bun-link.test.ts index 120e9ed976d63..713088fe04cca 100644 --- a/test/cli/install/bun-link.test.ts +++ b/test/cli/install/bun-link.test.ts @@ -1,7 +1,7 @@ -import { spawn, file } from "bun"; +import { file, spawn } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test"; -import { bunExe, bunEnv as env, runBunInstall, toBeValidBin, toHaveBins, tmpdirSync } from "harness"; -import { access, writeFile, mkdir } from "fs/promises"; +import { access, mkdir, writeFile } from "fs/promises"; +import { bunExe, bunEnv as env, runBunInstall, tmpdirSync, toBeValidBin, toHaveBins } from "harness"; import { basename, join } from "path"; import { dummyAfterAll, diff --git a/test/cli/install/bun-lockb.test.ts b/test/cli/install/bun-lockb.test.ts new file mode 100644 index 0000000000000..86ea34672fdf9 --- /dev/null +++ b/test/cli/install/bun-lockb.test.ts @@ -0,0 +1,58 @@ +import { file, listen, Socket, spawn } from "bun"; +import { tmpdirSync } from "harness"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, setDefaultTimeout, test } from "bun:test"; +import { access, mkdir, readlink, rm, writeFile, copyFile } from "fs/promises"; +import { bunEnv, bunExe, bunEnv as env, tempDirWithFiles, toBeValidBin, toBeWorkspaceLink, toHaveBins } from "harness"; +import { join, sep } from "path"; + +it("should not print anything to stderr when running bun.lockb", async () => { + const package_dir = tmpdirSync(); + + // copy bar-0.0.2.tgz to package_dir + await copyFile(join(__dirname, "bar-0.0.2.tgz"), join(package_dir, "bar-0.0.2.tgz")); + + // Create a simple package.json + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "test-package", + version: "1.0.0", + dependencies: { + "dummy-package": "file:./bar-0.0.2.tgz", + }, + }), + ); + + // Run 'bun install' to generate the lockfile + const installResult = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + env, + }); + await installResult.exited; + + // Ensure the lockfile was created + await access(join(package_dir, "bun.lockb")); + + // create a .env + await writeFile(join(package_dir, ".env"), "FOO=bar"); + + // Now test 'bun bun.lockb' + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "bun.lockb"], + cwd: package_dir, + stdout: "pipe", + stderr: "inherit", + env, + }); + + const stdoutOutput = await new Response(stdout).text(); + expect(stdoutOutput).toBe( + `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n# yarn lockfile v1\n# bun ./bun.lockb --hash: 8B7A1C2DA8966A48-f4830e6e283fffe9-DE5BD0E91FD9910F-f0bf88071b3f7ec9\n\n\n\"bar@file:./bar-0.0.2.tgz\":\n version \"./bar-0.0.2.tgz\"\n resolved \"./bar-0.0.2.tgz\"\n`, + ); + + const stderrOutput = await new Response(stderr).text(); + expect(stderrOutput).toBe(""); + + expect(await exited).toBe(0); +}); diff --git a/test/cli/install/bun-pack.test.ts b/test/cli/install/bun-pack.test.ts new file mode 100644 index 0000000000000..f9aa414179af6 --- /dev/null +++ b/test/cli/install/bun-pack.test.ts @@ -0,0 +1,1041 @@ +import { file, spawn, write } from "bun"; +import { readTarball } from "bun:internal-for-testing"; +import { beforeEach, describe, expect, test } from "bun:test"; +import { exists, mkdir, rm } from "fs/promises"; +import { bunEnv, bunExe, isWindows, runBunInstall, tmpdirSync } from "harness"; +import { join } from "path"; + +var packageDir: string; + +beforeEach(() => { + packageDir = tmpdirSync(); +}); + +async function pack(cwd: string, env: NodeJS.ProcessEnv, ...args: string[]) { + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "pm", "pack", ...args], + cwd, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).not.toContain("warning:"); + expect(err).not.toContain("failed"); + expect(err).not.toContain("panic:"); + + const out = await Bun.readableStreamToText(stdout); + + const exitCode = await exited; + expect(exitCode).toBe(0); + + return { out, err }; +} + +async function packExpectError(cwd: string, env: NodeJS.ProcessEnv, ...args: string[]) { + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "pm", "pack", ...args], + cwd, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("panic:"); + + const out = await Bun.readableStreamToText(stdout); + + const exitCode = await exited; + expect(exitCode).toBeGreaterThan(0); + + return { out, err }; +} + +test("basic", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-basic", + version: "1.2.3", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-basic-1.2.3.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }, { "pathname": "package/index.js" }]); +}); + +test("in subdirectory", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-from-subdir", + version: "7.7.7", + }), + ), + mkdir(join(packageDir, "subdir1", "subdir2"), { recursive: true }), + write(join(packageDir, "root.js"), "console.log(`hello ./root.js`);"), + write(join(packageDir, "subdir1", "subdir2", "index.js"), "console.log(`hello ./subdir1/subdir2/index.js`);"), + ]); + + await pack(join(packageDir, "subdir1", "subdir2"), bunEnv); + + const first = readTarball(join(packageDir, "pack-from-subdir-7.7.7.tgz")); + expect(first.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/root.js" }, + { "pathname": "package/subdir1/subdir2/index.js" }, + ]); + + await rm(join(packageDir, "pack-from-subdir-7.7.7.tgz")); + + await pack(join(packageDir, "subdir1"), bunEnv); + + const second = readTarball(join(packageDir, "pack-from-subdir-7.7.7.tgz")); + expect(first).toEqual(second); +}); + +describe("package.json names and versions", () => { + const tests = [ + { + desc: "missing name", + expectedError: "package.json must have `name` and `version` fields", + packageJson: { + version: "1.1.1", + }, + }, + { + desc: "missing version", + expectedError: "package.json must have `name` and `version` fields", + packageJson: { + name: "pack-invalid", + }, + }, + { + desc: "missing name and version", + expectedError: "package.json must have `name` and `version` fields", + packageJson: { + description: "ooops", + }, + }, + { + desc: "empty name", + expectedError: "package.json `name` and `version` fields must be non-empty strings", + packageJson: { + name: "", + version: "1.1.1", + }, + }, + { + desc: "empty version", + expectedError: "package.json `name` and `version` fields must be non-empty strings", + packageJson: { + name: "pack-invalid", + version: "", + }, + }, + { + desc: "empty name and version", + expectedError: "package.json `name` and `version` fields must be non-empty strings", + packageJson: { + name: "", + version: "", + }, + }, + ]; + + for (const { desc, expectedError, packageJson } of tests) { + test(desc, async () => { + await Promise.all([ + write(join(packageDir, "package.json"), JSON.stringify(packageJson)), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + const { err } = await packExpectError(packageDir, bunEnv); + expect(err).toContain(expectedError); + }); + } + + test("missing", async () => { + await write(join(packageDir, "index.js"), "console.log('hello ./index.js')"); + + const { err } = await packExpectError(packageDir, bunEnv); + expect(err).toContain(`error: No package.json was found for directory "${packageDir}`); + }); + + const scopedNames = [ + { + input: "@scoped/pkg", + output: "scoped-pkg-1.1.1.tgz", + }, + { + input: "@", + output: "-1.1.1.tgz", + }, + { + input: "@/", + output: "--1.1.1.tgz", + }, + { + input: "//", + output: "-1.1.1.tgz", + }, + { + input: "@//", + fail: true, + output: "", + }, + { + input: "@/s", + output: "-s-1.1.1.tgz", + }, + { + input: "@s", + output: "s-1.1.1.tgz", + }, + ]; + for (const { input, output, fail } of scopedNames) { + test(`scoped name: ${input}`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: input, + version: "1.1.1", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + fail ? await packExpectError(packageDir, bunEnv) : await pack(packageDir, bunEnv); + if (!fail) { + const tarball = readTarball(join(packageDir, output)); + expect(tarball.entries).toHaveLength(2); + } + }); + } +}); + +describe("flags", () => { + test("--dry-run", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-dry-run", + version: "1.1.1", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + const { out } = await pack(packageDir, bunEnv, "--dry-run"); + + expect(out).toContain("files: 2"); + + expect(await exists(join(packageDir, "pack-dry-run-1.1.1.tgz"))).toBeFalse(); + }); + test("--gzip", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-gzip-test", + version: "111111.1.11111111111111", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + for (const invalidGzipLevel of ["-1", "10", "kjefj"]) { + const { err } = await packExpectError(packageDir, bunEnv, `--gzip-level=${invalidGzipLevel}`); + expect(err).toContain(`error: compression level must be between 0 and 9, received ${invalidGzipLevel}\n`); + } + + await pack(packageDir, bunEnv, "--gzip-level=0"); + const largerTarball = readTarball(join(packageDir, "pack-gzip-test-111111.1.11111111111111.tgz")); + expect(largerTarball.entries).toHaveLength(2); + + await rm(join(packageDir, "pack-gzip-test-111111.1.11111111111111.tgz")); + + await pack(packageDir, bunEnv, "--gzip-level=9"); + const smallerTarball = readTarball(join(packageDir, "pack-gzip-test-111111.1.11111111111111.tgz")); + expect(smallerTarball.entries).toHaveLength(2); + + expect(smallerTarball.size).toBeLessThan(largerTarball.size); + }); + + const destinationTests = [ + { + "path": "", + }, + { + "path": "dest-dir", + }, + { + "path": "more/dir", + }, + ]; + + for (const { path } of destinationTests) { + test(`--destination="${path}"`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-dest-test", + version: "1.1.1", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + const dest = join(packageDir, path); + await pack(packageDir, bunEnv, `--destination=${dest}`); + + const tarball = readTarball(join(dest, "pack-dest-test-1.1.1.tgz")); + expect(tarball.entries).toHaveLength(2); + }); + } + + test("--ignore-scripts", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-ignore-scripts", + version: "1.1.1", + scripts: { + prepack: "touch prepack.txt", + postpack: "touch postpack.txt", + preprepare: "touch preprepare.txt", + prepare: "touch prepare.txt", + postprepare: "touch postprepare.txt", + }, + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + await pack(packageDir, bunEnv, "--ignore-scripts"); + + let results = await Promise.all([ + exists(join(packageDir, "prepack.txt")), + exists(join(packageDir, "postpack.txt")), + exists(join(packageDir, "preprepare.txt")), + exists(join(packageDir, "prepare.txt")), + exists(join(packageDir, "postprepare.txt")), + ]); + + expect(results).toEqual([false, false, false, false, false]); + + await pack(packageDir, bunEnv); + + results = await Promise.all([ + exists(join(packageDir, "prepack.txt")), + exists(join(packageDir, "postpack.txt")), + exists(join(packageDir, "preprepare.txt")), + exists(join(packageDir, "prepare.txt")), + exists(join(packageDir, "postprepare.txt")), + ]); + + expect(results).toEqual([true, true, false, true, false]); + }); +}); + +test("shasum and integrity are consistent", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-shasum", + version: "1.1.1", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + let { out } = await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-shasum-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([ + { + "pathname": "package/package.json", + }, + { + "pathname": "package/index.js", + }, + ]); + + expect(out).toContain(`Shasum: ${tarball.shasum}`); + + await rm(join(packageDir, "pack-shasum-1.1.1.tgz")); + + ({ out } = await pack(packageDir, bunEnv)); + + const secondTarball = readTarball(join(packageDir, "pack-shasum-1.1.1.tgz")); + expect(secondTarball.entries).toMatchObject([ + { + "pathname": "package/package.json", + }, + { + "pathname": "package/index.js", + }, + ]); + + expect(out).toContain(`Shasum: ${secondTarball.shasum}`); + expect(tarball.shasum).toBe(secondTarball.shasum); + expect(tarball.integrity).toBe(secondTarball.integrity); +}); + +describe("workspaces", () => { + async function createBasicWorkspace() { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-workspace", + version: "2.2.2", + workspaces: ["pkgs/*"], + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "pkgs", "pkg1", "package.json"), JSON.stringify({ name: "pkg1", version: "1.1.1" })), + write(join(packageDir, "pkgs", "pkg1", "index.js"), "console.log('hello ./index.js')"), + ]); + } + test("in a workspace", async () => { + await createBasicWorkspace(); + await pack(join(packageDir, "pkgs", "pkg1"), bunEnv); + + const tarball = readTarball(join(packageDir, "pkgs", "pkg1", "pkg1-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }, { "pathname": "package/index.js" }]); + }); + test("in a workspace subdirectory", async () => { + await createBasicWorkspace(); + await mkdir(join(packageDir, "pkgs", "pkg1", "subdir")); + + await pack(join(packageDir, "pkgs", "pkg1", "subdir"), bunEnv); + + const tarball = readTarball(join(packageDir, "pkgs", "pkg1", "pkg1-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }, { "pathname": "package/index.js" }]); + }); + test("replaces workspace: protocol without lockfile", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-workspace-protocol", + version: "2.3.4", + workspaces: ["pkgs/*"], + dependencies: { + "pkg1": "workspace:1.1.1", + }, + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "pkgs", "pkg1", "package.json"), JSON.stringify({ name: "pkg1", version: "1.1.1" })), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-workspace-protocol-2.3.4.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/pkgs/pkg1/package.json" }, + { "pathname": "package/root.js" }, + ]); + expect(JSON.parse(tarball.entries[0].contents)).toEqual({ + name: "pack-workspace-protocol", + version: "2.3.4", + workspaces: ["pkgs/*"], + dependencies: { + "pkg1": "1.1.1", + }, + }); + }); + + const withLockfileWorkspaceProtocolTests = [ + { input: "workspace:^", expected: "^1.1.1" }, + { input: "workspace:~", expected: "~1.1.1" }, + { input: "workspace:1.x", expected: "1.x" }, + { input: "workspace:1.1.x", expected: "1.1.x" }, + { input: "workspace:*", expected: "1.1.1" }, + { input: "workspace:-", expected: "-" }, + ]; + + for (const { input, expected } of withLockfileWorkspaceProtocolTests) { + test(`replaces workspace: protocol with lockfile: ${input}`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-workspace-protocol-with-lockfile", + version: "2.5.6", + workspaces: ["pkgs/*"], + dependencies: { + "pkg1": input, + }, + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "pkgs", "pkg1", "package.json"), JSON.stringify({ name: "pkg1", version: "1.1.1" })), + ]); + + await runBunInstall(bunEnv, packageDir); + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-workspace-protocol-with-lockfile-2.5.6.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/pkgs/pkg1/package.json" }, + { "pathname": "package/root.js" }, + ]); + expect(JSON.parse(tarball.entries[0].contents)).toEqual({ + name: "pack-workspace-protocol-with-lockfile", + version: "2.5.6", + workspaces: ["pkgs/*"], + dependencies: { + "pkg1": expected, + }, + }); + }); + } + + test("fails gracefully when workspace version fails to resolve", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-workspace-protocol-fail", + version: "2.2.3", + workspaces: ["pkgs/*"], + dependencies: { + "pkg1": "workspace:*", + }, + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "pkgs", "pkg1", "package.json"), JSON.stringify({ name: "pkg1", version: "1.1.1" })), + ]); + + const { err } = await packExpectError(packageDir, bunEnv); + expect(err).toContain( + 'error: Failed to resolve workspace version for "pkg1" in `dependencies`. Run `bun install` and try again.', + ); + + await rm(join(packageDir, "pack-workspace-protocol-fail-2.2.3.tgz")); + await runBunInstall(bunEnv, packageDir); + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-workspace-protocol-fail-2.2.3.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/pkgs/pkg1/package.json" }, + { "pathname": "package/root.js" }, + ]); + }); +}); + +test("lifecycle scripts execution order", async () => { + const script = `const fs = require("fs"); + fs.writeFileSync(\`\${process.argv[2]}.txt\`, \` +prepack: \${fs.existsSync("prepack.txt")} +prepare: \${fs.existsSync("prepare.txt")} +postpack: \${fs.existsSync("postpack.txt")} +tarball: \${fs.existsSync("pack-lifecycle-order-1.1.1.tgz")}\`)`; + + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-lifecycle-order", + version: "1.1.1", + scripts: { + prepack: `${bunExe()} script.js prepack`, + postpack: `${bunExe()} script.js postpack`, + prepare: `${bunExe()} script.js prepare`, + }, + }), + ), + write(join(packageDir, "script.js"), script), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-lifecycle-order-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/prepack.txt" }, + { "pathname": "package/prepare.txt" }, + { "pathname": "package/script.js" }, + ]); + + const results = await Promise.all([ + file(join(packageDir, "prepack.txt")).text(), + file(join(packageDir, "postpack.txt")).text(), + file(join(packageDir, "prepare.txt")).text(), + ]); + + expect(results).toEqual([ + "\nprepack: false\nprepare: false\npostpack: false\ntarball: false", + "\nprepack: true\nprepare: true\npostpack: false\ntarball: true", + "\nprepack: true\nprepare: false\npostpack: false\ntarball: false", + ]); +}); + +describe("bundledDependnecies", () => { + for (const bundledDependencies of ["bundledDependencies", "bundleDependencies"]) { + test(`basic (${bundledDependencies})`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-bundled", + version: "4.4.4", + dependencies: { + "dep1": "1.1.1", + }, + [bundledDependencies]: ["dep1"], + }), + ), + write( + join(packageDir, "node_modules", "dep1", "package.json"), + JSON.stringify({ + name: "dep1", + version: "1.1.1", + }), + ), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-bundled-4.4.4.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/node_modules/dep1/package.json" }, + ]); + }); + } + + test("resolve dep of bundled dep", async () => { + // Test that a bundled dep can have it's dependencies resolved without + // needing to add them to `bundledDependencies`. Also test that only + // the bundled deps are included, the other files in node_modules are excluded. + + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-resolved-bundled-dep", + version: "5.5.5", + dependencies: { + dep1: "1.1.1", + }, + bundledDependencies: ["dep1"], + }), + ), + write( + join(packageDir, "node_modules", "dep1", "package.json"), + JSON.stringify({ + name: "dep1", + version: "1.1.1", + dependencies: { + dep2: "2.2.2", + dep3: "3.3.3", + }, + }), + ), + write( + join(packageDir, "node_modules", "dep2", "package.json"), + JSON.stringify({ + name: "dep2", + version: "2.2.2", + }), + ), + write(join(packageDir, "node_modules", "dep1", "node_modules", "excluded.txt"), "do not add to tarball!"), + write( + join(packageDir, "node_modules", "dep1", "node_modules", "dep3", "package.json"), + JSON.stringify({ + name: "dep3", + version: "3.3.3", + }), + ), + ]); + + const { out } = await pack(packageDir, bunEnv); + expect(out).toContain("Total files: 4"); + expect(out).toContain("Bundled deps: 3"); + + const tarball = readTarball(join(packageDir, "pack-resolved-bundled-dep-5.5.5.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/node_modules/dep1/node_modules/dep3/package.json" }, + { "pathname": "package/node_modules/dep1/package.json" }, + { "pathname": "package/node_modules/dep2/package.json" }, + ]); + }); + + test.todo("scoped names", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-resolve-scoped", + version: "6.6.6", + dependencies: { + "@scoped/dep1": "1.1.1", + }, + bundledDependencies: ["@scoped/dep1"], + }), + ), + write( + join(packageDir, "node_modules", "@scoped", "dep1", "package.json"), + JSON.stringify({ + name: "@scoped/dep1", + version: "1.1.1", + dependencies: { + "@scoped/dep2": "2.2.2", + "@scoped/dep3": "3.3.3", + }, + }), + ), + write( + join(packageDir, "node_modules", "@scoped", "dep2", "package.json"), + JSON.stringify({ + name: "@scoped/dep2", + version: "2.2.2", + }), + ), + write( + join(packageDir, "node_modules", "@scoped", "dep1", "node_modules", "@scoped", "dep3", "package.json"), + JSON.stringify({ + name: "@scoped/dep3", + version: "3.3.3", + }), + ), + ]); + + const { out } = await pack(packageDir, bunEnv); + expect(out).toContain("Total files: 4"); + expect(out).toContain("Bundled deps: 3"); + + const tarball = readTarball(join(packageDir, "pack-resolve-scoped-6.6.6.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/node_modules/@scoped/dep1/node_modules/@scoped/dep3/package.json" }, + { "pathname": "package/node_modules/@scoped/dep1/package.json" }, + { "pathname": "package/node_modules/@scoped/dep2/package.json" }, + ]); + }); + + test("ignore deps that aren't directories", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-bundled-dep-not-dir", + version: "4.5.6", + dependencies: { + dep1: "1.1.1", + }, + }), + ), + write(join(packageDir, "node_modules", "dep1"), "hi. this is a file, not a directory"), + ]); + + const { out } = await pack(packageDir, bunEnv); + expect(out).toContain("Total files: 1"); + expect(out).not.toContain("Bundled deps"); + + const tarball = readTarball(join(packageDir, "pack-bundled-dep-not-dir-4.5.6.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }]); + }); +}); + +describe("files", () => { + test("CHANGELOG is not included by default", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-files-changelog", + version: "1.1.1", + files: ["lib"], + }), + ), + write(join(packageDir, "CHANGELOG.md"), "hello"), + write(join(packageDir, "lib", "index.js"), "console.log('hello ./lib/index.js')"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-files-changelog-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/lib/index.js" }, + ]); + }); + test("cannot exclude LICENSE", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-files-license", + version: "1.1.1", + files: ["lib", "!LICENSE"], + }), + ), + write(join(packageDir, "LICENSE"), "hello"), + write(join(packageDir, "lib", "index.js"), "console.log('hello ./lib/index.js')"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-files-license-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/LICENSE" }, + { "pathname": "package/lib/index.js" }, + ]); + }); + test("can include files and directories", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-files-1", + version: "1.1.1", + files: ["root.js", "subdir", "subdir2/subdir"], + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "subdir", "index.js"), "console.log('hello ./subdir/index.js')"), + write(join(packageDir, "subdir", "anotherdir", "index.js"), "console.log('hello ./subdir/anotherdir/index.js')"), + write(join(packageDir, "subdir2", "subdir", "index.js"), "console.log('hello ./subdir2/subdir/index.js')"), + + // should not be included + write(join(packageDir, "subdir2", "index.js"), "console.log('hello, dont include me!')"), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-files-1-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/root.js" }, + { "pathname": "package/subdir/anotherdir/index.js" }, + { "pathname": "package/subdir/index.js" }, + { "pathname": "package/subdir2/subdir/index.js" }, + ]); + }); + + test("matches relative to root by default", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-files-3", + version: "1.2.3", + files: ["index.js"], + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + write(join(packageDir, "subdir", "index.js"), "console.log('hello ./subdir/index.js')"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-files-3-1.2.3.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }, { "pathname": "package/index.js" }]); + }); + + test("recursive only if leading **/", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-files-2", + version: "1.2.123", + files: ["**/index.js"], + }), + ), + write(join(packageDir, "root.js"), "console.log('hello ./root.js')"), + write(join(packageDir, "subdir", "index.js"), "console.log('hello ./subdir/index.js')"), + write(join(packageDir, "subdir", "anotherdir", "index.js"), "console.log('hello ./subdir/anotherdir/index.js')"), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-files-2-1.2.123.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/index.js" }, + { "pathname": "package/subdir/anotherdir/index.js" }, + { "pathname": "package/subdir/index.js" }, + ]); + }); +}); + +describe(".gitignore/.npmignore", () => { + for (const ignoreFile of [".gitignore", ".npmignore"]) { + test(`can ignore and un-ignore a file (${ignoreFile})`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-ignore-1", + version: "0.0.0", + }), + ), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + write(join(packageDir, ignoreFile), "index.js"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-ignore-1-0.0.0.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }]); + + await Promise.all([ + rm(join(packageDir, "pack-ignore-1-0.0.0.tgz")), + write(join(packageDir, ignoreFile), "index.js\n!index.js"), + ]); + + await pack(packageDir, bunEnv); + const tarball2 = readTarball(join(packageDir, "pack-ignore-1-0.0.0.tgz")); + expect(tarball2.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/index.js" }, + ]); + + await Promise.all([ + rm(join(packageDir, "pack-ignore-1-0.0.0.tgz")), + write(join(packageDir, ignoreFile), "!index.js\nindex.js"), + ]); + + await pack(packageDir, bunEnv); + const tarball3 = readTarball(join(packageDir, "pack-ignore-1-0.0.0.tgz")); + expect(tarball3.entries).toMatchObject([{ "pathname": "package/package.json" }]); + }); + } + + test("excludes files recursively", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-ignore-2", + version: "1.2.1", + }), + ), + write(join(packageDir, ".npmignore"), "index.js"), + write(join(packageDir, "index.js"), "console.log('hello ./index.js')"), + write(join(packageDir, "subdir", "index.js"), "console.log('hello ./subdir/index.js')"), + write(join(packageDir, "subdir", "subsubdir", "index.js"), "console.log('hello ./subdir/subsubdir/index.js')"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-ignore-2-1.2.1.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }]); + }); +}); + +describe("bins", () => { + test("basic", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-bins", + version: "1.2.3", + bin: "bin.js", + }), + ), + write(join(packageDir, "bin.js"), `#!/usr/bin/env bun\n`), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-bins-1.2.3.tgz")); + expect(tarball.entries).toMatchObject([ + { + pathname: "package/package.json", + }, + { + pathname: "package/bin.js", + }, + ]); + + expect(tarball.entries[0].perm & 0o644).toBe(0o644); + if (isWindows) { + expect(tarball.entries[1].perm & 0o111).toBe(0); + } else { + expect(tarball.entries[1].perm & (0o644 | 0o111)).toBe(0o644 | 0o111); + } + }); + + test("directory", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-bins-dir", + version: "1.2.3", + directories: { + bin: "bins", + }, + }), + ), + write(join(packageDir, "bins", "bin1.js"), `#!/usr/bin/env bun\n`), + write(join(packageDir, "bins", "bin2.js"), `#!/usr/bin/env bun\n`), + ]); + + await pack(packageDir, bunEnv); + + const tarball = readTarball(join(packageDir, "pack-bins-dir-1.2.3.tgz")); + expect(tarball.entries).toMatchObject([ + { + pathname: "package/package.json", + }, + { + pathname: "package/bins/bin1.js", + }, + { + pathname: "package/bins/bin2.js", + }, + ]); + + expect(tarball.entries[0].perm & 0o644).toBe(0o644); + if (isWindows) { + expect(tarball.entries[1].perm & 0o111).toBe(0); + expect(tarball.entries[2].perm & 0o111).toBe(0); + } else { + expect(tarball.entries[1].perm & (0o644 | 0o111)).toBe(0o644 | 0o111); + expect(tarball.entries[2].perm & (0o644 | 0o111)).toBe(0o644 | 0o111); + } + }); +}); + +test("unicode", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-unicode", + version: "1.1.1", + }), + ), + write(join(packageDir, "äöüščří.js"), `console.log('hello ./äöüščří.js');`), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-unicode-1.1.1.tgz")); + expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }, { "pathname": "package/äöüščří.js" }]); +}); diff --git a/test/cli/install/bun-patch.test.ts b/test/cli/install/bun-patch.test.ts index e4655b7660c14..c81a74404eb2a 100644 --- a/test/cli/install/bun-patch.test.ts +++ b/test/cli/install/bun-patch.test.ts @@ -1,7 +1,7 @@ -import { $, ShellOutput, ShellPromise } from "bun"; -import { bunExe, bunEnv as env, toBeValidBin, toHaveBins, toBeWorkspaceLink, tempDirWithFiles, bunEnv } from "harness"; -import { afterAll, afterEach, beforeAll, beforeEach, expect, it, describe, test, setDefaultTimeout } from "bun:test"; -import { join, sep } from "path"; +import { $, ShellOutput } from "bun"; +import { beforeAll, describe, expect, setDefaultTimeout, test } from "bun:test"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { join } from "path"; const expectNoError = (o: ShellOutput) => expect(o.stderr.toString()).not.toContain("error"); // const platformPath = (path: string) => (process.platform === "win32" ? path.replaceAll("/", sep) : path); @@ -12,6 +12,324 @@ beforeAll(() => { }); describe("bun patch ", async () => { + describe("workspace interactions", async () => { + /** + * @repo/eslint-config and @repo/typescript-config both depend on @types/ws@8.5.4 + * so it should be hoisted to the root node_modules + */ + describe("inside workspace with hoisting", async () => { + const args = [ + ["node_modules/@types/ws", "node_modules/@types/ws"], + ["@types/ws@8.5.4", "node_modules/@types/ws"], + ]; + for (const [arg, path] of args) { + test(arg, async () => { + const tempdir = tempDirWithFiles("lol", { + "package.json": JSON.stringify({ + "name": "my-workspace", + private: "true", + version: "0.0.1", + "devDependencies": { + "@repo/ui": "*", + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + }, + workspaces: ["packages/*"], + }), + packages: { + "eslint-config": { + "package.json": JSON.stringify({ + name: "@repo/eslint-config", + "version": "0.0.0", + dependencies: { + "@types/ws": "8.5.4", + }, + private: "true", + }), + }, + "typescript-config": { + "package.json": JSON.stringify({ + "name": "@repo/typescript-config", + "version": "0.0.0", + dependencies: { + "@types/ws": "8.5.4", + }, + private: "true", + }), + }, + "ui": { + "package.json": JSON.stringify({ + name: "@repo/ui", + version: "0.0.0", + private: "true", + devDependencies: { + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + }, + }), + }, + }, + }); + + console.log("TEMPDIR", tempdir); + + await $`${bunExe()} i`.env(bunEnv).cwd(tempdir); + + let result = await $` ${bunExe()} patch ${arg}`.env(bunEnv).cwd(tempdir); + expect(result.stderr.toString()).not.toContain("error"); + expect(result.stdout.toString()).toContain(`To patch @types/ws, edit the following folder:\n\n ${path}\n`); + + await $`echo LOL > ${path}/index.d.ts`.env(bunEnv).cwd(tempdir); + + expectNoError(await $`${bunExe()} patch --commit ${arg}`.env(bunEnv).cwd(tempdir)); + + expect(await $`cat ${path}/index.d.ts`.env(bunEnv).cwd(tempdir).text()).toEqual("LOL\n"); + + expect( + (await $`cat package.json`.cwd(tempdir).env(bunEnv).json()).patchedDependencies["@types/ws@8.5.4"], + ).toEqual("patches/@types%2Fws@8.5.4.patch"); + }); + } + }); + + describe("inside workspace with multiple workspace packages with same dependency", async () => { + const args = [ + ["node_modules/@types/ws", "packages/eslint-config/node_modules/@types/ws"], + ["@types/ws@8.5.4", "node_modules/@repo/eslint-config/node_modules/@types/ws"], + ]; + for (const [arg, path] of args) { + test(arg, async () => { + const tempdir = tempDirWithFiles("lol", { + "package.json": JSON.stringify({ + "name": "my-workspace", + private: "true", + version: "0.0.1", + "devDependencies": { + "@repo/ui": "*", + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + "@types/ws": "7.4.7", + }, + workspaces: ["packages/*"], + }), + packages: { + "eslint-config": { + "package.json": JSON.stringify({ + name: "@repo/eslint-config", + "version": "0.0.0", + dependencies: { + "@types/ws": "8.5.4", + }, + private: "true", + }), + }, + "typescript-config": { + "package.json": JSON.stringify({ + "name": "@repo/typescript-config", + "version": "0.0.0", + dependencies: { + "@types/ws": "8.5.4", + }, + private: "true", + }), + }, + "ui": { + "package.json": JSON.stringify({ + name: "@repo/ui", + version: "0.0.0", + private: "true", + devDependencies: { + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + }, + }), + }, + }, + }); + + console.log("TEMPDIR", tempdir); + + await $`${bunExe()} i`.env(bunEnv).cwd(tempdir); + + let result = await $`cd packages/eslint-config; ${bunExe()} patch ${arg}`.env(bunEnv).cwd(tempdir); + expect(result.stderr.toString()).not.toContain("error"); + expect(result.stdout.toString()).toContain( + `To patch @types/ws, edit the following folder:\n\n ${tempdir}/${path}\n`, + ); + + await $`echo LOL > ${path}/index.d.ts`.env(bunEnv).cwd(tempdir); + + expectNoError(await $`cd packages/eslint-config; ${bunExe()} patch --commit ${arg}`.env(bunEnv).cwd(tempdir)); + + expect(await $`cat ${path}/index.d.ts`.env(bunEnv).cwd(tempdir).text()).toEqual("LOL\n"); + + expect( + (await $`cat package.json`.cwd(tempdir).env(bunEnv).json()).patchedDependencies["@types/ws@8.5.4"], + ).toEqual("patches/@types%2Fws@8.5.4.patch"); + }); + } + }); + + describe("inside workspace package", async () => { + const args = [ + ["node_modules/@types/ws", "packages/eslint-config/node_modules/@types/ws"], + ["@types/ws@8.5.4", "node_modules/@repo/eslint-config/node_modules/@types/ws"], + ]; + for (const [arg, path] of args) { + test(arg, async () => { + const tempdir = tempDirWithFiles("lol", { + "package.json": JSON.stringify({ + "name": "my-workspace", + private: "true", + version: "0.0.1", + "devDependencies": { + "@repo/ui": "*", + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + "@types/ws": "7.4.7", + }, + workspaces: ["packages/*"], + }), + packages: { + "eslint-config": { + "package.json": JSON.stringify({ + name: "@repo/eslint-config", + "version": "0.0.0", + dependencies: { + "@types/ws": "8.5.4", + }, + private: "true", + }), + }, + "typescript-config": { + "package.json": JSON.stringify({ + "name": "@repo/typescript-config", + "version": "0.0.0", + private: "true", + }), + }, + "ui": { + "package.json": JSON.stringify({ + name: "@repo/ui", + version: "0.0.0", + private: "true", + devDependencies: { + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + }, + }), + }, + }, + }); + + console.log("TEMPDIR", tempdir); + + await $`${bunExe()} i`.env(bunEnv).cwd(tempdir); + + let result = await $`cd packages/eslint-config; ${bunExe()} patch ${arg}`.env(bunEnv).cwd(tempdir); + expect(result.stderr.toString()).not.toContain("error"); + expect(result.stdout.toString()).toContain( + `To patch @types/ws, edit the following folder:\n\n ${tempdir}/${path}\n`, + ); + + await $`echo LOL > ${path}/index.js`.env(bunEnv).cwd(tempdir); + + expectNoError(await $`cd packages/eslint-config; ${bunExe()} patch --commit ${arg}`.env(bunEnv).cwd(tempdir)); + + expect(await $`cat ${path}/index.js`.env(bunEnv).cwd(tempdir).text()).toEqual("LOL\n"); + + expect( + (await $`cat package.json`.cwd(tempdir).env(bunEnv).json()).patchedDependencies["@types/ws@8.5.4"], + ).toEqual("patches/@types%2Fws@8.5.4.patch"); + }); + } + }); + + describe("inside ROOT workspace package", async () => { + const args = [ + [ + "packages/eslint-config/node_modules/@types/ws", + "packages/eslint-config/node_modules/@types/ws", + "@types/ws@8.5.4", + "patches/@types%2Fws@8.5.4.patch", + ], + [ + "@types/ws@8.5.4", + "node_modules/@repo/eslint-config/node_modules/@types/ws", + "@types/ws@8.5.4", + "patches/@types%2Fws@8.5.4.patch", + ], + ["@types/ws@7.4.7", "node_modules/@types/ws", "@types/ws@7.4.7", "patches/@types%2Fws@7.4.7.patch"], + ]; + for (const [arg, path, version, patch_path] of args) { + test(arg, async () => { + const tempdir = tempDirWithFiles("lol", { + "package.json": JSON.stringify({ + "name": "my-workspace", + private: "true", + version: "0.0.1", + "devDependencies": { + "@repo/ui": "*", + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + "@types/ws": "7.4.7", + }, + workspaces: ["packages/*"], + }), + packages: { + "eslint-config": { + "package.json": JSON.stringify({ + name: "@repo/eslint-config", + "version": "0.0.0", + dependencies: { + "@types/ws": "8.5.4", + }, + private: "true", + }), + }, + "typescript-config": { + "package.json": JSON.stringify({ + "name": "@repo/typescript-config", + "version": "0.0.0", + private: "true", + }), + }, + "ui": { + "package.json": JSON.stringify({ + name: "@repo/ui", + version: "0.0.0", + private: "true", + devDependencies: { + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + }, + }), + }, + }, + }); + + console.log("TEMPDIR", tempdir); + + await $`${bunExe()} i`.env(bunEnv).cwd(tempdir); + + let result = await $`${bunExe()} patch ${arg}`.env(bunEnv).cwd(tempdir); + expect(result.stderr.toString()).not.toContain("error"); + expect(result.stdout.toString()).toContain(`To patch @types/ws, edit the following folder:\n\n ${path}\n`); + + await $`echo LOL > ${path}/index.js`.env(bunEnv).cwd(tempdir); + + expectNoError(await $`${bunExe()} patch --commit ${arg}`.env(bunEnv).cwd(tempdir)); + + expect(await $`cat ${path}/index.js`.env(bunEnv).cwd(tempdir).text()).toEqual("LOL\n"); + + expect((await $`cat package.json`.cwd(tempdir).env(bunEnv).json()).patchedDependencies[version]).toEqual( + patch_path, + ); + }); + } + }); + }); + // Tests to make sure that patching describe("popular pkg", async () => { const dummyCode = /* ts */ ` @@ -431,10 +749,10 @@ module.exports = function isEven() { "index.ts": /* ts */ `import isEven from 'is-even'; console.log(isEven(420))`, }); - await $`${bunExe()} run index.ts` + await $`${bunExe()} i` .env(bunEnv) - .cwd(filedir) - .then(o => expect(o.stderr.toString()).toBe("")); + .cwd(tempdir) + .then(o => expect(o.stderr.toString()).not.toContain("error")); const { stdout, stderr } = await $`${bunExe()} run index.ts`.env(bunEnv).cwd(tempdir); expect(stderr.toString()).toBe(""); @@ -488,10 +806,10 @@ module.exports = function isOdd() { "index.ts": /* ts */ `import isEven from 'is-even'; console.log(isEven(420))`, }); - await $`${bunExe()} run index.ts` + await $`${bunExe()} i` .env(bunEnv) - .cwd(filedir) - .then(o => expect(o.stderr.toString()).toBe("")); + .cwd(tempdir) + .then(o => expect(o.stderr.toString()).not.toContain("error")); const { stdout, stderr } = await $`${bunExe()} run index.ts`.env(bunEnv).cwd(tempdir); expect(stderr.toString()).toBe(""); diff --git a/test/cli/install/bun-pm.test.ts b/test/cli/install/bun-pm.test.ts index 65420bd33c790..d1a6042f96fe0 100644 --- a/test/cli/install/bun-pm.test.ts +++ b/test/cli/install/bun-pm.test.ts @@ -1,7 +1,8 @@ -import { hash, spawn } from "bun"; +import { spawn } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test"; +import { exists, mkdir, writeFile } from "fs/promises"; import { bunEnv, bunExe, bunEnv as env, tmpdirSync } from "harness"; -import { mkdir, writeFile, exists } from "fs/promises"; +import { cpSync } from "node:fs"; import { join } from "path"; import { dummyAfterAll, @@ -15,7 +16,6 @@ import { root_url, setHandler, } from "./dummy.registry"; -import { cpSync, rmSync } from "js/node/fs/export-star-from"; beforeAll(dummyBeforeAll); afterAll(dummyAfterAll); diff --git a/test/cli/install/bun-remove.test.ts b/test/cli/install/bun-remove.test.ts index 1dd224d021d22..c7ae73618df91 100644 --- a/test/cli/install/bun-remove.test.ts +++ b/test/cli/install/bun-remove.test.ts @@ -1,10 +1,9 @@ -import { bunExe, bunEnv as env, tmpdirSync } from "harness"; +import { file, spawn } from "bun"; +import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test"; import { mkdir, writeFile } from "fs/promises"; +import { bunExe, bunEnv as env, tmpdirSync } from "harness"; import { join, relative } from "path"; -import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test"; import { dummyAfterAll, dummyAfterEach, dummyBeforeAll, dummyBeforeEach, package_dir } from "./dummy.registry"; -import { spawn } from "bun"; -import { file } from "bun"; beforeAll(dummyBeforeAll); afterAll(dummyAfterAll); diff --git a/test/cli/install/bun-repl.test.ts b/test/cli/install/bun-repl.test.ts new file mode 100644 index 0000000000000..7f95580d162ff --- /dev/null +++ b/test/cli/install/bun-repl.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from "bun:test"; +import "harness"; + +// https://github.com/oven-sh/bun/issues/12070 +test("bun repl", () => { + expect(["repl", "-e", "process.exit(0)"]).toRun(); +}); diff --git a/test/cli/install/bun-run-bunfig.test.ts b/test/cli/install/bun-run-bunfig.test.ts index 471c50775a3ae..a5890fb92d7ff 100644 --- a/test/cli/install/bun-run-bunfig.test.ts +++ b/test/cli/install/bun-run-bunfig.test.ts @@ -1,7 +1,6 @@ import { describe, expect, test } from "bun:test"; -import { realpathSync, chmodSync } from "fs"; +import { realpathSync } from "fs"; import { bunEnv, bunExe, isWindows, tempDirWithFiles, toTOMLString } from "harness"; -import { join } from "path"; describe.each(["bun run", "bun"])(`%s`, cmd => { const runCmd = cmd === "bun" ? ["-c=bunfig.toml", "run"] : ["-c=bunfig.toml"]; diff --git a/test/cli/install/bun-run.test.ts b/test/cli/install/bun-run.test.ts index 203f8f9676072..e20c2efda2948 100644 --- a/test/cli/install/bun-run.test.ts +++ b/test/cli/install/bun-run.test.ts @@ -1,7 +1,7 @@ import { file, spawn, spawnSync } from "bun"; -import { afterEach, beforeEach, expect, it, describe } from "bun:test"; -import { bunEnv, bunExe, bunEnv as env, isWindows, tmpdirSync } from "harness"; -import { rm, writeFile, exists, mkdir } from "fs/promises"; +import { beforeEach, describe, expect, it } from "bun:test"; +import { exists, mkdir, rm, writeFile } from "fs/promises"; +import { bunEnv, bunExe, bunEnv as env, isWindows, tempDirWithFiles, tmpdirSync } from "harness"; import { join } from "path"; import { readdirSorted } from "./dummy.registry"; @@ -437,3 +437,41 @@ it("should show the correct working directory when run with --cwd", async () => expect(await res.exited).toBe(0); expect(await Bun.readableStreamToText(res.stdout)).toMatch(/subdir/); }); + +it("DCE annotations are respected", () => { + const dir = tempDirWithFiles("test", { + "index.ts": ` + /* @__PURE__ */ console.log("Hello, world!"); + `, + }); + + const { stdout, stderr, exitCode } = spawnSync({ + cmd: [bunExe(), "run", "index.ts"], + cwd: dir, + env: bunEnv, + }); + + expect(exitCode).toBe(0); + + expect(stderr.toString()).toBe(""); + expect(stdout.toString()).toBe(""); +}); + +it("--ignore-dce-annotations ignores DCE annotations", () => { + const dir = tempDirWithFiles("test", { + "index.ts": ` + /* @__PURE__ */ console.log("Hello, world!"); + `, + }); + + const { stdout, stderr, exitCode } = spawnSync({ + cmd: [bunExe(), "--ignore-dce-annotations", "run", "index.ts"], + cwd: dir, + env: bunEnv, + }); + + expect(exitCode).toBe(0); + + expect(stderr.toString()).toBe(""); + expect(stdout.toString()).toBe("Hello, world!\n"); +}); diff --git a/test/cli/install/bun-update.test.ts b/test/cli/install/bun-update.test.ts index b15df2de3d368..9b6575a73b0d2 100644 --- a/test/cli/install/bun-update.test.ts +++ b/test/cli/install/bun-update.test.ts @@ -1,7 +1,7 @@ -import { file, listen, Socket, spawn } from "bun"; +import { file, spawn } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test"; +import { access, readFile, rm, writeFile } from "fs/promises"; import { bunExe, bunEnv as env, toBeValidBin, toHaveBins } from "harness"; -import { readFile, access, mkdir, readlink, realpath, rm, writeFile } from "fs/promises"; import { join } from "path"; import { dummyAfterAll, diff --git a/test/cli/install/bun-upgrade.test.ts b/test/cli/install/bun-upgrade.test.ts index 34e35072c91e2..4380313a317a6 100644 --- a/test/cli/install/bun-upgrade.test.ts +++ b/test/cli/install/bun-upgrade.test.ts @@ -1,27 +1,28 @@ import { spawn, spawnSync } from "bun"; -import { beforeEach, expect, it, setDefaultTimeout, beforeAll } from "bun:test"; -import { bunExe, bunEnv as env, tls, tmpdirSync } from "harness"; -import { join } from "path"; -import { copyFileSync } from "js/node/fs/export-star-from"; import { upgrade_test_helpers } from "bun:internal-for-testing"; +import { beforeAll, beforeEach, expect, it, setDefaultTimeout } from "bun:test"; +import { bunExe, bunEnv as env, tls, tmpdirSync } from "harness"; +import { copyFileSync } from "node:fs"; +import { basename, join } from "path"; const { openTempDirWithoutSharingDelete, closeTempDirHandle } = upgrade_test_helpers; -let run_dir: string; -let exe_name: string = "bun-debug" + (process.platform === "win32" ? ".exe" : ""); +let cwd: string; +let execPath: string; beforeAll(() => { setDefaultTimeout(1000 * 60 * 5); }); beforeEach(async () => { - run_dir = tmpdirSync(); - copyFileSync(bunExe(), join(run_dir, exe_name)); + cwd = tmpdirSync(); + execPath = join(cwd, basename(bunExe())); + copyFileSync(bunExe(), execPath); }); it("two invalid arguments, should display error message and suggest command", async () => { const { stderr } = spawn({ - cmd: [join(run_dir, exe_name), "upgrade", "bun-types", "--dev"], - cwd: run_dir, + cmd: [execPath, "upgrade", "bun-types", "--dev"], + cwd, stdout: null, stdin: "pipe", stderr: "pipe", @@ -35,8 +36,8 @@ it("two invalid arguments, should display error message and suggest command", as it("two invalid arguments flipped, should display error message and suggest command", async () => { const { stderr } = spawn({ - cmd: [join(run_dir, exe_name), "upgrade", "--dev", "bun-types"], - cwd: run_dir, + cmd: [execPath, "upgrade", "--dev", "bun-types"], + cwd, stdout: null, stdin: "pipe", stderr: "pipe", @@ -50,8 +51,8 @@ it("two invalid arguments flipped, should display error message and suggest comm it("one invalid argument, should display error message and suggest command", async () => { const { stderr } = spawn({ - cmd: [join(run_dir, exe_name), "upgrade", "bun-types"], - cwd: run_dir, + cmd: [execPath, "upgrade", "bun-types"], + cwd, stdout: null, stdin: "pipe", stderr: "pipe", @@ -65,8 +66,8 @@ it("one invalid argument, should display error message and suggest command", asy it("one valid argument, should succeed", async () => { const { stderr } = spawn({ - cmd: [join(run_dir, exe_name), "upgrade", "--help"], - cwd: run_dir, + cmd: [execPath, "upgrade", "--help"], + cwd, stdout: null, stdin: "pipe", stderr: "pipe", @@ -81,8 +82,8 @@ it("one valid argument, should succeed", async () => { it("two valid argument, should succeed", async () => { const { stderr } = spawn({ - cmd: [join(run_dir, exe_name), "upgrade", "--stable", "--profile"], - cwd: run_dir, + cmd: [execPath, "upgrade", "--stable", "--profile"], + cwd, stdout: null, stdin: "pipe", stderr: "pipe", @@ -96,55 +97,56 @@ it("two valid argument, should succeed", async () => { }); it("zero arguments, should succeed", async () => { + const tagName = bunExe().includes("-debug") ? "canary" : `bun-v${Bun.version}`; using server = Bun.serve({ tls: tls, port: 0, async fetch() { return new Response( JSON.stringify({ - "tag_name": "bun-v1.1.4", + "tag_name": tagName, "assets": [ { "url": "foo", "content_type": "application/zip", "name": "bun-windows-x64.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-windows-x64.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-windows-x64.zip`, }, { "url": "foo", "content_type": "application/zip", "name": "bun-windows-x64-baseline.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-windows-x64-baseline.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-windows-x64-baseline.zip`, }, { "url": "foo", "content_type": "application/zip", "name": "bun-linux-x64.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-linux-x64.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-linux-x64.zip`, }, { "url": "foo", "content_type": "application/zip", "name": "bun-linux-x64-baseline.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-linux-x64-baseline.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-linux-x64-baseline.zip`, }, { "url": "foo", "content_type": "application/zip", "name": "bun-darwin-x64.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-darwin-x64.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-darwin-x64.zip`, }, { "url": "foo", "content_type": "application/zip", "name": "bun-darwin-x64-baseline.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-darwin-x64-baseline.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-darwin-x64-baseline.zip`, }, { "url": "foo", "content_type": "application/zip", "name": "bun-darwin-aarch64.zip", - "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest/bun-darwin-aarch64.zip`, + "browser_download_url": `https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${tagName}/bun-darwin-aarch64.zip`, }, ], }), @@ -157,15 +159,15 @@ it("zero arguments, should succeed", async () => { openTempDirWithoutSharingDelete(); const { stderr } = spawnSync({ - cmd: [join(run_dir, exe_name), "upgrade"], - cwd: run_dir, + cmd: [execPath, "upgrade"], + cwd, stdout: null, stdin: "pipe", stderr: "pipe", env: { ...env, NODE_TLS_REJECT_UNAUTHORIZED: "0", - GITHUB_API_DOMAIN: `localhost:${server.port}`, + GITHUB_API_DOMAIN: `${server.hostname}:${server.port}`, }, }); diff --git a/test/cli/install/bun-workspaces.test.ts b/test/cli/install/bun-workspaces.test.ts index 98a10bed41fa8..318b563beae05 100644 --- a/test/cli/install/bun-workspaces.test.ts +++ b/test/cli/install/bun-workspaces.test.ts @@ -1,10 +1,9 @@ -import { spawnSync, write, file } from "bun"; +import { file, write } from "bun"; +import { install_test_helpers } from "bun:internal-for-testing"; +import { beforeEach, describe, expect, test } from "bun:test"; +import { mkdirSync, rmSync, writeFileSync } from "fs"; import { bunExe, bunEnv as env, runBunInstall, tmpdirSync, toMatchNodeModulesAt } from "harness"; import { join } from "path"; -import { writeFileSync, mkdirSync, rmSync } from "fs"; -import { writeFile, mkdir } from "fs/promises"; -import { beforeEach, test, expect, describe } from "bun:test"; -import { install_test_helpers } from "bun:internal-for-testing"; const { parseLockfile } = install_test_helpers; expect.extend({ toMatchNodeModulesAt }); @@ -459,3 +458,53 @@ describe("workspace aliases", async () => { }); } }); + +for (const glob of [true, false]) { + test(`does not crash when root package.json is in "workspaces"${glob ? " (glob)" : ""}`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: glob ? ["**"] : ["pkg1", "./*"], + }), + ), + write( + join(packageDir, "pkg1", "package.json"), + JSON.stringify({ + name: "pkg1", + }), + ), + ]); + + await runBunInstall(env, packageDir); + expect(await file(join(packageDir, "node_modules", "pkg1", "package.json")).json()).toEqual({ + name: "pkg1", + }); + }); +} + +test("cwd in workspace script is not the symlink path on windows", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["pkg1"], + }), + ), + write( + join(packageDir, "pkg1", "package.json"), + JSON.stringify({ + name: "pkg1", + scripts: { + postinstall: 'bun -e \'require("fs").writeFileSync("cwd", process.cwd())\'', + }, + }), + ), + ]); + + await runBunInstall(env, packageDir); + + expect(await file(join(packageDir, "node_modules", "pkg1", "cwd")).text()).toBe(join(packageDir, "pkg1")); +}); diff --git a/test/cli/install/bunx.test.ts b/test/cli/install/bunx.test.ts index d8302e0695431..87a26b0c7b15e 100644 --- a/test/cli/install/bunx.test.ts +++ b/test/cli/install/bunx.test.ts @@ -2,7 +2,7 @@ import { spawn } from "bun"; import { beforeAll, beforeEach, expect, it, setDefaultTimeout } from "bun:test"; import { rm, writeFile } from "fs/promises"; import { bunEnv, bunExe, isWindows, tmpdirSync } from "harness"; -import { readdirSync } from "fs"; +import { readdirSync } from "node:fs"; import { tmpdir } from "os"; import { join, resolve } from "path"; import { readdirSorted } from "./dummy.registry"; diff --git a/test/cli/install/dummy.registry.ts b/test/cli/install/dummy.registry.ts index 0858835b5d4d6..65b7db7c415df 100644 --- a/test/cli/install/dummy.registry.ts +++ b/test/cli/install/dummy.registry.ts @@ -4,11 +4,11 @@ * PACKAGE_DIR_TO_USE=(realpath .) bun test/cli/install/dummy.registry.ts */ import { file, Server } from "bun"; +import { tmpdirSync } from "harness"; let expect: (typeof import("bun:test"))["expect"]; -import { tmpdirSync } from "harness"; -import { readdir, rm, writeFile } from "fs/promises"; +import { readdir, writeFile } from "fs/promises"; import { basename, join } from "path"; type Handler = (req: Request) => Response | Promise; @@ -24,15 +24,28 @@ let server: Server; export let package_dir: string; export let requested: number; export let root_url: string; - -export function dummyRegistry(urls: string[], info: any = { "0.0.2": {} }) { +export function dummyRegistry(urls: string[], info: any = { "0.0.2": {} }, numberOfTimesTo500PerURL = 0) { + let retryCountsByURL = new Map(); const _handler: Handler = async request => { urls.push(request.url); const url = request.url.replaceAll("%2f", "/"); + let status = 200; + + if (numberOfTimesTo500PerURL > 0) { + let currentCount = retryCountsByURL.get(request.url); + if (currentCount === undefined) { + retryCountsByURL.set(request.url, numberOfTimesTo500PerURL); + status = 500; + } else { + retryCountsByURL.set(request.url, currentCount - 1); + status = currentCount > 0 ? 500 : 200; + } + } + expect(request.method).toBe("GET"); if (url.endsWith(".tgz")) { - return new Response(file(join(import.meta.dir, basename(url).toLowerCase()))); + return new Response(file(join(import.meta.dir, basename(url).toLowerCase())), { status }); } expect(request.headers.get("accept")).toBe( "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*", @@ -54,6 +67,7 @@ export function dummyRegistry(urls: string[], info: any = { "0.0.2": {} }) { ...info[version], }; } + return new Response( JSON.stringify({ name, @@ -62,6 +76,9 @@ export function dummyRegistry(urls: string[], info: any = { "0.0.2": {} }) { latest: info.latest ?? version, }, }), + { + status: status, + }, ); }; return _handler; diff --git a/test/cli/install/migration/complex-workspace.test.ts b/test/cli/install/migration/complex-workspace.test.ts index 4db23618b5935..094aef74d460a 100644 --- a/test/cli/install/migration/complex-workspace.test.ts +++ b/test/cli/install/migration/complex-workspace.test.ts @@ -1,7 +1,7 @@ +import { beforeAll, expect, setDefaultTimeout, test } from "bun:test"; import fs from "fs"; -import path from "path"; -import { test, expect, describe, beforeAll, setDefaultTimeout } from "bun:test"; import { bunEnv, bunExe, tmpdirSync } from "harness"; +import path from "path"; let cwd = tmpdirSync(); diff --git a/test/cli/install/migration/migrate.test.ts b/test/cli/install/migration/migrate.test.ts index 536311c981667..bd5d19b5fd9f5 100644 --- a/test/cli/install/migration/migrate.test.ts +++ b/test/cli/install/migration/migrate.test.ts @@ -1,5 +1,5 @@ +import { beforeAll, expect, setDefaultTimeout, test } from "bun:test"; import fs from "fs"; -import { test, expect, beforeAll, setDefaultTimeout } from "bun:test"; import { bunEnv, bunExe, tmpdirSync } from "harness"; import { join } from "path"; diff --git a/test/cli/install/migration/out-of-sync.test.ts b/test/cli/install/migration/out-of-sync.test.ts deleted file mode 100644 index bfee754b3056d..0000000000000 --- a/test/cli/install/migration/out-of-sync.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { test, expect } from "bun:test"; -import path from "node:path"; -import { bunEnv, bunExe, tempDirWithFiles } from "harness"; - -test("doesn't error when the migration is out of sync", async () => { - const cwd = tempDirWithFiles("out-of-sync-1", { - "package.json": JSON.stringify({ - "devDependencies": { - "lodash": "4.17.20", - }, - }), - "package-lock.json": JSON.stringify({ - "name": "reproo", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "lodash": "4.17.21", - }, - "devDependencies": { - "lodash": "4.17.20", - }, - }, - "node_modules/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": - "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true, - }, - }, - }), - }); - - const subprocess = Bun.spawn([bunExe(), "install"], { - env: bunEnv, - cwd, - stdio: ["ignore", "ignore", "inherit"], - }); - - await subprocess.exited; - - expect(subprocess.exitCode).toBe(0); - - let { stdout, exitCode } = Bun.spawnSync({ - cmd: [bunExe(), "pm", "ls"], - env: bunEnv, - cwd, - stdio: ["ignore", "pipe", "inherit"], - }); - let out = stdout.toString().trim(); - expect(out).toContain("lodash@4.17.20"); - // only one lodash is installed - expect(out.lastIndexOf("lodash")).toEqual(out.indexOf("lodash")); - expect(exitCode).toBe(0); - - expect(await Bun.file(path.join(cwd, "node_modules/lodash/package.json")).json()).toMatchObject({ - version: "4.17.20", - name: "lodash", - }); -}); diff --git a/test/cli/install/overrides.test.ts b/test/cli/install/overrides.test.ts index 46de6561554c5..71400fe8ca22c 100644 --- a/test/cli/install/overrides.test.ts +++ b/test/cli/install/overrides.test.ts @@ -1,7 +1,11 @@ -import { join } from "path"; +import { beforeAll, expect, setDefaultTimeout, test } from "bun:test"; import { readFileSync, writeFileSync } from "fs"; import { bunEnv, bunExe, tmpdirSync } from "harness"; -import { test, expect } from "bun:test"; +import { join } from "path"; + +beforeAll(() => { + setDefaultTimeout(1000 * 60 * 5); +}); function install(cwd: string, args: string[]) { const exec = Bun.spawnSync({ diff --git a/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap b/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap index 0111493eb12dd..0f33e6cd4f800 100644 --- a/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap +++ b/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap @@ -45,3 +45,84 @@ what-bin@1.0.0: integrity sha512-sa99On1k5aDqCvpni/TQ6rLzYprUWBlb8fNwWOzbjDlM24fRr7FKDOuaBO/Y9WEIcZuzoPkCW5EkBCpflj8REQ== " `; + +exports[`outdated normal dep, smaller than column title 1`] = ` +"┌──────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├──────────┼─────────┼────────┼────────┤ +│ no-deps │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +└──────────┴─────────┴────────┴────────┘ +" +`; + +exports[`outdated normal dep, larger than column title 1`] = ` +"┌───────────────┬────────────────┬────────────────┬────────────────┐ +│ Package │ Current │ Update │ Latest │ +├───────────────┼────────────────┼────────────────┼────────────────┤ +│ prereleases-1 │ 1.0.0-future.1 │ 1.0.0-future.1 │ 1.0.0-future.4 │ +└───────────────┴────────────────┴────────────────┴────────────────┘ +" +`; + +exports[`outdated dev dep, smaller than column title 1`] = ` +"┌───────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├───────────────┼─────────┼────────┼────────┤ +│ no-deps (dev) │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +└───────────────┴─────────┴────────┴────────┘ +" +`; + +exports[`outdated dev dep, larger than column title 1`] = ` +"┌─────────────────────┬────────────────┬────────────────┬────────────────┐ +│ Package │ Current │ Update │ Latest │ +├─────────────────────┼────────────────┼────────────────┼────────────────┤ +│ prereleases-1 (dev) │ 1.0.0-future.1 │ 1.0.0-future.1 │ 1.0.0-future.4 │ +└─────────────────────┴────────────────┴────────────────┴────────────────┘ +" +`; + +exports[`outdated peer dep, smaller than column title 1`] = ` +"┌────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────┼─────────┼────────┼────────┤ +│ no-deps (peer) │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +└────────────────┴─────────┴────────┴────────┘ +" +`; + +exports[`outdated peer dep, larger than column title 1`] = ` +"┌──────────────────────┬────────────────┬────────────────┬────────────────┐ +│ Package │ Current │ Update │ Latest │ +├──────────────────────┼────────────────┼────────────────┼────────────────┤ +│ prereleases-1 (peer) │ 1.0.0-future.1 │ 1.0.0-future.1 │ 1.0.0-future.4 │ +└──────────────────────┴────────────────┴────────────────┴────────────────┘ +" +`; + +exports[`outdated optional dep, smaller than column title 1`] = ` +"┌────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────┼─────────┼────────┼────────┤ +│ no-deps (optional) │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +└────────────────────┴─────────┴────────┴────────┘ +" +`; + +exports[`outdated optional dep, larger than column title 1`] = ` +"┌──────────────────────────┬────────────────┬────────────────┬────────────────┐ +│ Package │ Current │ Update │ Latest │ +├──────────────────────────┼────────────────┼────────────────┼────────────────┤ +│ prereleases-1 (optional) │ 1.0.0-future.1 │ 1.0.0-future.1 │ 1.0.0-future.4 │ +└──────────────────────────┴────────────────┴────────────────┴────────────────┘ +" +`; + +exports[`outdated NO_COLOR works 1`] = ` +"|--------------------------------------| +| Package | Current | Update | Latest | +|----------|---------|--------|--------| +| a-dep | 1.0.1 | 1.0.1 | 1.0.10 | +|--------------------------------------| +" +`; diff --git a/test/cli/install/registry/bun-install-registry.test.ts b/test/cli/install/registry/bun-install-registry.test.ts index c876c4b2afc3d..e33965d1a6e1a 100644 --- a/test/cli/install/registry/bun-install-registry.test.ts +++ b/test/cli/install/registry/bun-install-registry.test.ts @@ -1,24 +1,31 @@ import { file, spawn, write } from "bun"; +import { install_test_helpers } from "bun:internal-for-testing"; +import { afterAll, beforeAll, beforeEach, describe, expect, it, setDefaultTimeout, test } from "bun:test"; +import { ChildProcess, fork } from "child_process"; +import { copyFileSync, mkdirSync } from "fs"; +import { cp, exists, mkdir, readlink, rm, writeFile } from "fs/promises"; import { + assertManifestsPopulated, bunExe, bunEnv as env, isLinux, isWindows, + mergeWindowEnvs, + randomPort, + runBunInstall, + runBunUpdate, + tempDirWithFiles, + tmpdirSync, toBeValidBin, toHaveBins, - writeShebangScript, - tmpdirSync, toMatchNodeModulesAt, - runBunInstall, - runBunUpdate, + writeShebangScript, } from "harness"; -import { join, sep, resolve } from "path"; -import { rm, writeFile, mkdir, exists, cp, readlink } from "fs/promises"; +import { join, resolve, sep } from "path"; import { readdirSorted } from "../dummy.registry"; -import { fork, ChildProcess } from "child_process"; -import { beforeAll, afterAll, beforeEach, test, expect, describe, setDefaultTimeout } from "bun:test"; -import { install_test_helpers } from "bun:internal-for-testing"; const { parseLockfile } = install_test_helpers; +const { iniInternals } = require("bun:internal-for-testing"); +const { loadNpmrc } = iniInternals; expect.extend({ toBeValidBin, @@ -27,17 +34,40 @@ expect.extend({ }); var verdaccioServer: ChildProcess; -var port: number = 4873; +var port: number = randomPort(); var packageDir: string; +let users: Record = {}; + beforeAll(async () => { + console.log("STARTING VERDACCIO"); setDefaultTimeout(1000 * 60 * 5); verdaccioServer = fork( require.resolve("verdaccio/bin/verdaccio"), ["-c", join(import.meta.dir, "verdaccio.yaml"), "-l", `${port}`], - { silent: true, execPath: "bun" }, + { + silent: true, + // Prefer using a release build of Bun since it's faster + execPath: Bun.which("bun") || bunExe(), + }, ); + verdaccioServer.stderr?.on("data", data => { + console.error(`Error: ${data}`); + }); + + verdaccioServer.on("error", error => { + console.error(`Failed to start child process: ${error}`); + }); + + verdaccioServer.on("exit", (code, signal) => { + if (code !== 0) { + console.error(`Child process exited with code ${code} and signal ${signal}`); + } else { + console.log("Child process exited successfully"); + } + }); + await new Promise(done => { verdaccioServer.on("message", (msg: { verdaccio_started: boolean }) => { if (msg.verdaccio_started) { @@ -48,24 +78,395 @@ beforeAll(async () => { }); }); -afterAll(() => { - verdaccioServer.kill(); +afterAll(async () => { + await Bun.$`rm -f ${import.meta.dir}/htpasswd`.throws(false); + if (verdaccioServer) verdaccioServer.kill(); }); beforeEach(async () => { packageDir = tmpdirSync(); + await Bun.$`rm -f ${import.meta.dir}/htpasswd`.throws(false); + await Bun.$`rm -rf ${import.meta.dir}/packages/private-pkg-dont-touch`.throws(false); + users = {}; env.BUN_INSTALL_CACHE_DIR = join(packageDir, ".bun-cache"); env.BUN_TMPDIR = env.TMPDIR = env.TEMP = join(packageDir, ".bun-tmp"); await writeFile( join(packageDir, "bunfig.toml"), ` [install] -cache = false +cache = "${join(packageDir, ".bun-cache")}" registry = "http://localhost:${port}/" `, ); }); +function registryUrl() { + return `http://localhost:${port}/`; +} + +/** + * Returns auth token + */ +async function generateRegistryUser(username: string, password: string): Promise { + console.log("GENERATE REGISTRY USER"); + if (users[username]) { + throw new Error("that user already exists"); + } else users[username] = password; + + const url = `http://localhost:${port}/-/user/org.couchdb.user:${username}`; + const user = { + name: username, + password: password, + email: `${username}@example.com`, + }; + + const response = await fetch(url, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(user), + }); + + if (response.ok) { + const data = await response.json(); + console.log(`Token: ${data.token}`); + return data.token; + } else { + throw new Error("Failed to create user:", response.statusText); + } +} + +describe("npmrc", async () => { + const isBase64Encoded = (opt: string) => opt === "_auth" || opt === "_password"; + + it("works with empty file", async () => { + console.log("package dir", packageDir); + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const ini = /* ini */ ``; + + await Bun.$`echo ${ini} > ${packageDir}/.npmrc`; + await Bun.$`echo ${JSON.stringify({ + name: "foo", + dependencies: {}, + })} > package.json`.cwd(packageDir); + await Bun.$`${bunExe()} install`.cwd(packageDir).throws(true); + }); + + it("sets default registry", async () => { + console.log("package dir", packageDir); + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const ini = /* ini */ ` +registry = http://localhost:${port}/ +`; + + await Bun.$`echo ${ini} > ${packageDir}/.npmrc`; + await Bun.$`echo ${JSON.stringify({ + name: "foo", + dependencies: { + "no-deps": "1.0.0", + }, + })} > package.json`.cwd(packageDir); + await Bun.$`${bunExe()} install`.cwd(packageDir).throws(true); + }); + + it("sets scoped registry", async () => { + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const ini = /* ini */ ` + @types:registry=http://localhost:${port}/ + `; + + await Bun.$`echo ${ini} > ${packageDir}/.npmrc`; + await Bun.$`echo ${JSON.stringify({ + name: "foo", + dependencies: { + "@types/no-deps": "1.0.0", + }, + })} > package.json`.cwd(packageDir); + await Bun.$`${bunExe()} install`.cwd(packageDir).throws(true); + }); + + it("default registry from env variable", async () => { + const ini = /* ini */ ` +registry=\${LOL} + `; + + const result = loadNpmrc(ini, { LOL: `http://localhost:${port}/` }); + + expect(result.default_registry_url).toBe(`http://localhost:${port}/`); + }); + + it("default registry from env variable 2", async () => { + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const ini = /* ini */ ` +registry=http://localhost:\${PORT}/ + `; + + const result = loadNpmrc(ini, { ...env, PORT: port }); + + expect(result.default_registry_url).toEqual(`http://localhost:${port}/`); + }); + + async function makeTest( + options: [option: string, value: string][], + check: (result: { + default_registry_url: string; + default_registry_token: string; + default_registry_username: string; + default_registry_password: string; + }) => void, + ) { + const optionName = await Promise.all(options.map(async ([name, val]) => `${name} = ${val}`)); + test(optionName.join(" "), async () => { + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const iniInner = await Promise.all( + options.map(async ([option, value]) => { + let finalValue = value; + finalValue = isBase64Encoded(option) ? Buffer.from(finalValue).toString("base64") : finalValue; + return `//registry.npmjs.org/:${option}=${finalValue}`; + }), + ); + + const ini = /* ini */ ` +${iniInner.join("\n")} +`; + + await Bun.$`echo ${JSON.stringify({ + name: "hello", + main: "index.js", + version: "1.0.0", + dependencies: { + "is-even": "1.0.0", + }, + })} > package.json`.cwd(packageDir); + + await Bun.$`echo ${ini} > ${packageDir}/.npmrc`; + + const result = loadNpmrc(ini); + + check(result); + }); + } + + await makeTest([["_authToken", "skibidi"]], result => { + expect(result.default_registry_url).toEqual("https://registry.npmjs.org/"); + expect(result.default_registry_token).toEqual("skibidi"); + }); + + await makeTest( + [ + ["username", "zorp"], + ["_password", "skibidi"], + ], + result => { + expect(result.default_registry_url).toEqual("https://registry.npmjs.org/"); + expect(result.default_registry_username).toEqual("zorp"); + expect(result.default_registry_password).toEqual("skibidi"); + }, + ); + + it("authentication works", async () => { + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const ini = /* ini */ ` +registry = http://localhost:${port}/ +//localhost:${port}/:_authToken=${await generateRegistryUser("bilbo_swaggins", "verysecure")} +`; + + await Bun.$`echo ${ini} > ${packageDir}/.npmrc`; + await Bun.$`echo ${JSON.stringify({ + name: "hi", + main: "index.js", + version: "1.0.0", + dependencies: { + "@needs-auth/test-pkg": "1.0.0", + }, + "publishConfig": { + "registry": `http://localhost:${port}`, + }, + })} > package.json`.cwd(packageDir); + + await Bun.$`${bunExe()} install`.env(env).cwd(packageDir).throws(true); + }); + + type EnvMap = + | Omit< + { + [key: string]: string; + }, + "dotEnv" + > + | { dotEnv?: Record }; + + function registryConfigOptionTest( + name: string, + _opts: Record | (() => Promise>), + _env?: EnvMap | (() => Promise), + check?: (stdout: string, stderr: string) => void, + ) { + it(`sets scoped registry option: ${name}`, async () => { + console.log("PACKAGE DIR", packageDir); + await Bun.$`rm -rf ${packageDir}/bunfig.toml`; + + const { dotEnv, ...restOfEnv } = _env + ? typeof _env === "function" + ? await _env() + : _env + : { dotEnv: undefined }; + const opts = _opts ? (typeof _opts === "function" ? await _opts() : _opts) : {}; + const dotEnvInner = dotEnv + ? Object.entries(dotEnv) + .map(([k, v]) => `${k}=${k.includes("SECRET_") ? Buffer.from(v).toString("base64") : v}`) + .join("\n") + : ""; + + const ini = /* ini */ ` +registry = http://localhost:${port}/ +${Object.keys(opts) + .map( + k => + `//localhost:${port}/:${k}=${isBase64Encoded(k) && !opts[k].includes("${") ? Buffer.from(opts[k]).toString("base64") : opts[k]}`, + ) + .join("\n")} +`; + + if (dotEnvInner.length > 0) await Bun.$`echo ${dotEnvInner} > ${packageDir}/.env`; + await Bun.$`echo ${ini} > ${packageDir}/.npmrc`; + await Bun.$`echo ${JSON.stringify({ + name: "hi", + main: "index.js", + version: "1.0.0", + dependencies: { + "@needs-auth/test-pkg": "1.0.0", + }, + "publishConfig": { + "registry": `http://localhost:${port}`, + }, + })} > package.json`.cwd(packageDir); + + const { stdout, stderr } = await Bun.$`${bunExe()} install` + .env({ ...env, ...restOfEnv }) + .cwd(packageDir) + .throws(check === undefined); + + if (check) check(stdout.toString(), stderr.toString()); + }); + } + + registryConfigOptionTest("_authToken", async () => ({ + "_authToken": await generateRegistryUser("bilbo_baggins", "verysecure"), + })); + registryConfigOptionTest( + "_authToken with env variable value", + async () => ({ _authToken: "${SUPER_SECRET_TOKEN}" }), + async () => ({ SUPER_SECRET_TOKEN: await generateRegistryUser("bilbo_baggins420", "verysecure") }), + ); + registryConfigOptionTest("username and password", async () => { + await generateRegistryUser("gandalf429", "verysecure"); + return { username: "gandalf429", _password: "verysecure" }; + }); + registryConfigOptionTest( + "username and password with env variable password", + async () => { + await generateRegistryUser("gandalf422", "verysecure"); + return { username: "gandalf422", _password: "${SUPER_SECRET_PASSWORD}" }; + }, + { + SUPER_SECRET_PASSWORD: Buffer.from("verysecure").toString("base64"), + }, + ); + registryConfigOptionTest( + "username and password with .env variable password", + async () => { + await generateRegistryUser("gandalf421", "verysecure"); + return { username: "gandalf421", _password: "${SUPER_SECRET_PASSWORD}" }; + }, + { + dotEnv: { SUPER_SECRET_PASSWORD: "verysecure" }, + }, + ); + + registryConfigOptionTest("_auth", async () => { + await generateRegistryUser("linus", "verysecure"); + const _auth = "linus:verysecure"; + return { _auth }; + }); + + registryConfigOptionTest( + "_auth from .env variable", + async () => { + await generateRegistryUser("zack", "verysecure"); + return { _auth: "${SECRET_AUTH}" }; + }, + { + dotEnv: { SECRET_AUTH: "zack:verysecure" }, + }, + ); + + registryConfigOptionTest( + "_auth from .env variable with no value", + async () => { + await generateRegistryUser("zack420", "verysecure"); + return { _auth: "${SECRET_AUTH}" }; + }, + { + dotEnv: { SECRET_AUTH: "" }, + }, + (stdout: string, stderr: string) => { + expect(stderr).toContain("got an empty string"); + }, + ); +}); + +describe("package.json indentation", async () => { + test("works for root and workspace packages", async () => { + await Promise.all([ + // 5 space indentation + write(join(packageDir, "package.json"), `\n{\n\n "name": "foo",\n"workspaces": ["packages/*"]\n}`), + // 1 tab indentation + write(join(packageDir, "packages", "bar", "package.json"), `\n{\n\n\t"name": "bar",\n}`), + ]); + + let { exited } = spawn({ + cmd: [bunExe(), "add", "no-deps"], + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + }); + + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + const rootPackageJson = await file(join(packageDir, "package.json")).text(); + + expect(rootPackageJson).toBe( + `{\n "name": "foo",\n "workspaces": ["packages/*"],\n "dependencies": {\n "no-deps": "^2.0.0"\n }\n}`, + ); + + // now add to workspace. it should keep tab indentation + ({ exited } = spawn({ + cmd: [bunExe(), "add", "no-deps"], + cwd: join(packageDir, "packages", "bar"), + stdout: "inherit", + stderr: "inherit", + env, + })); + + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await file(join(packageDir, "package.json")).text()).toBe(rootPackageJson); + const workspacePackageJson = await file(join(packageDir, "packages", "bar", "package.json")).text(); + expect(workspacePackageJson).toBe(`{\n\t"name": "bar",\n\t"dependencies": {\n\t\t"no-deps": "^2.0.0"\n\t}\n}`); + }); +}); + describe("optionalDependencies", () => { for (const optional of [true, false]) { test(`exit code is ${optional ? 0 : 1} when ${optional ? "optional" : ""} dependency tarball is missing`, async () => { @@ -90,12 +491,8 @@ describe("optionalDependencies", () => { `${optional ? "warn" : "error"}: GET http://localhost:${port}/missing-tarball/-/missing-tarball-1.0.0.tgz - `, ); expect(await exited).toBe(optional ? 0 : 1); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".bin", - ".cache", - "uses-what-bin", - "what-bin", - ]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "uses-what-bin", "what-bin"]); expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeTrue(); }); } @@ -116,12 +513,37 @@ describe("optionalDependencies", () => { allowWarnings: true, savesLockfile: !rootOptional, }); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); - expect(err).toContain("warn: GET http://localhost:4873/this-package-does-not-exist-in-the-registry - "); + expect(err).toMatch(`warn: GET http://localhost:${port}/this-package-does-not-exist-in-the-registry - 404`); }); } }); +test("tarball override does not crash", async () => { + await write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + "two-range-deps": "||", + }, + overrides: { + "no-deps": `http://localhost:${port}/no-deps/-/no-deps-2.0.0.tgz`, + }, + }), + ); + + await runBunInstall(env, packageDir); + + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ + name: "no-deps", + version: "2.0.0", + }); +}); + describe.each(["--production", "without --production"])("%s", flag => { const prod = flag === "--production"; const order = ["devDependencies", "dependencies"]; @@ -155,6 +577,7 @@ describe.each(["--production", "without --production"])("%s", flag => { }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); const initialHash = Bun.hash(await file(join(packageDir, "bun.lockb")).arrayBuffer()); @@ -173,6 +596,7 @@ describe.each(["--production", "without --production"])("%s", flag => { }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "node_modules", "bin-change-dir", "package.json")).json()).toMatchObject({ name: "bin-change-dir", @@ -189,6 +613,7 @@ describe.each(["--production", "without --production"])("%s", flag => { }); expect(await exited).toBe(1); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); // We should not have saved bun.lockb expect(Bun.hash(await file(join(packageDir, "bun.lockb")).arrayBuffer())).toBe(initialHash); @@ -210,6 +635,7 @@ describe.each(["--production", "without --production"])("%s", flag => { }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); // We should not have saved bun.lockb expect(Bun.hash(await file(join(packageDir, "bun.lockb")).arrayBuffer())).toBe(initialHash); @@ -252,6 +678,7 @@ describe.each(["--production", "without --production"])("%s", flag => { env, }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await Promise.all([ (async () => @@ -286,6 +713,8 @@ describe.each(["--production", "without --production"])("%s", flag => { }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "bin-change-dir", "package.json")).json()).toMatchObject({ name: "bin-change-dir", version: prod ? "1.0.0" : "1.0.1", @@ -357,6 +786,7 @@ test("hardlinks on windows dont fail with long paths", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("basic 1", async () => { @@ -395,6 +825,8 @@ test("basic 1", async () => { } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); ({ stdout, stderr, exited } = spawn({ @@ -418,6 +850,7 @@ test("basic 1", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("manifest cache will invalidate when registry changes", async () => { @@ -449,7 +882,7 @@ registry = "http://localhost:${port}" const lockfile = await parseLockfile(packageDir); for (const pkg of Object.values(lockfile.packages) as any) { if (pkg.tag === "npm") { - expect(pkg.resolution.resolved).toContain("http://localhost:4873"); + expect(pkg.resolution.resolved).toContain(`http://localhost:${port}`); } } @@ -470,7 +903,7 @@ cache = "${cacheDir}" const npmLockfile = await parseLockfile(packageDir); for (const pkg of Object.values(npmLockfile.packages) as any) { if (pkg.tag === "npm") { - expect(pkg.resolution.resolved).not.toContain("http://localhost:4873"); + expect(pkg.resolution.resolved).not.toContain(`http://localhost:${port}`); } } }); @@ -514,6 +947,7 @@ test("dependency from root satisfies range from dependency", async () => { version: "1.0.0", } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); @@ -539,6 +973,7 @@ test("dependency from root satisfies range from dependency", async () => { "2 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("duplicate names and versions in a manifest do not install incorrect packages", async () => { @@ -571,6 +1006,8 @@ test("duplicate names and versions in a manifest do not install incorrect packag ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + const lockfile = parseLockfile(packageDir); expect(lockfile).toMatchNodeModulesAt(packageDir); const results = await Promise.all([ @@ -612,6 +1049,8 @@ describe("peerDependency index out of bounds", async () => { ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + const lockfile = parseLockfile(packageDir); expect(lockfile).toMatchNodeModulesAt(packageDir); const results = await Promise.all([ @@ -628,6 +1067,7 @@ describe("peerDependency index out of bounds", async () => { await Promise.all([ rm(join(packageDir, "node_modules"), { recursive: true, force: true }), + rm(join(packageDir, ".bun-cache"), { recursive: true, force: true }), write( join(packageDir, "package.json"), JSON.stringify({ @@ -640,6 +1080,8 @@ describe("peerDependency index out of bounds", async () => { ]); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + const newLockfile = parseLockfile(packageDir); expect(newLockfile).toMatchNodeModulesAt(packageDir); const newResults = await Promise.all([ @@ -656,6 +1098,50 @@ describe("peerDependency index out of bounds", async () => { }); } } + + // Install 2 dependencies, one is a normal dependency, the other is a dependency with a optional + // peer dependency on the first dependency. Delete node_modules and cache, then update the dependency + // with the optional peer to a new version. Doing this will cause the peer dependency to get enqueued + // internally, testing for index out of bounds. It's also important cache is deleted to ensure a tarball + // task is created for it. + test("optional", async () => { + await write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + "optional-peer-deps": "1.0.0", + "no-deps": "1.0.0", + }, + }), + ); + + await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + // update version and delete node_modules and cache + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + "optional-peer-deps": "1.0.1", + "no-deps": "1.0.0", + }, + }), + ), + rm(join(packageDir, "node_modules"), { recursive: true, force: true }), + rm(join(packageDir, ".bun-cache"), { recursive: true, force: true }), + ]); + + // this install would trigger the index out of bounds error + await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + const lockfile = parseLockfile(packageDir); + expect(lockfile).toMatchNodeModulesAt(packageDir); + }); }); test("peerDependency in child npm dependency should not maintain old version when package is upgraded", async () => { @@ -697,6 +1183,7 @@ test("peerDependency in child npm dependency should not maintain old version whe version: "1.0.0", } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await writeFile( join(packageDir, "package.json"), @@ -735,6 +1222,7 @@ test("peerDependency in child npm dependency should not maintain old version whe "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("package added after install", async () => { @@ -774,6 +1262,7 @@ test("package added after install", async () => { version: "1.1.0", } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); // add `no-deps` to root package.json with a smaller but still compatible // version for `one-range-dep`. @@ -820,6 +1309,7 @@ test("package added after install", async () => { version: "1.1.0", } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); @@ -845,6 +1335,7 @@ test("package added after install", async () => { "3 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("--production excludes devDependencies in workspaces", async () => { @@ -889,11 +1380,13 @@ test("--production excludes devDependencies in workspaces", async () => { // without lockfile const expectedResults = [ - [".cache", "a-dep", "no-deps", "pkg1", "pkg2"], + ["a-dep", "no-deps", "pkg1", "pkg2"], { name: "no-deps", version: "1.0.0" }, { name: "a-dep", version: "1.0.2" }, ]; let { out } = await runBunInstall(env, packageDir, { production: true }); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ no-deps@1.0.0", @@ -911,6 +1404,8 @@ test("--production excludes devDependencies in workspaces", async () => { // create non-production lockfile, then install with --production await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); ({ out } = await runBunInstall(env, packageDir)); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ a1@1.0.0", @@ -920,6 +1415,8 @@ test("--production excludes devDependencies in workspaces", async () => { ]); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); ({ out } = await runBunInstall(env, packageDir, { production: true })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ no-deps@1.0.0", @@ -967,152 +1464,332 @@ test("--production without a lockfile will install and not save lockfile", async "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await exists(join(packageDir, "node_modules", "no-deps", "index.js"))).toBeTrue(); }); -test("it should correctly link binaries after deleting node_modules", async () => { - const json: any = { - name: "foo", - version: "1.0.0", - dependencies: { - "what-bin": "1.0.0", - "uses-what-bin": "1.5.0", - }, - }; - await writeFile(join(packageDir, "package.json"), JSON.stringify(json)); +describe("binaries", () => { + for (const global of [false, true]) { + describe(`existing destinations${global ? " (global)" : ""}`, () => { + test("existing non-symlink", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + "what-bin": "1.0.0", + }, + }), + ), + write(join(packageDir, "node_modules", ".bin", "what-bin"), "hi"), + ]); - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, - }); + await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); - var err = await new Response(stderr).text(); - var out = await new Response(stdout).text(); - expect(err).toContain("Saved lockfile"); - expect(err).not.toContain("not found"); - expect(err).not.toContain("error:"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - "+ uses-what-bin@1.5.0", - "+ what-bin@1.0.0", - "", - expect.stringContaining("3 packages installed"), - "", - "Blocked 1 postinstall. Run `bun pm untrusted` for details.", - "", - ]); - expect(await exited).toBe(0); + expect(join(packageDir, "node_modules", ".bin", "what-bin")).toBeValidBin( + join("..", "what-bin", "what-bin.js"), + ); + }); + }); + } + test("it should correctly link binaries after deleting node_modules", async () => { + const json: any = { + name: "foo", + version: "1.0.0", + dependencies: { + "what-bin": "1.0.0", + "uses-what-bin": "1.5.0", + }, + }; + await writeFile(join(packageDir, "package.json"), JSON.stringify(json)); - await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); - ({ stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, - })); + var err = await new Response(stderr).text(); + var out = await new Response(stdout).text(); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("not found"); + expect(err).not.toContain("error:"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "+ uses-what-bin@1.5.0", + "+ what-bin@1.0.0", + "", + expect.stringContaining("3 packages installed"), + "", + "Blocked 1 postinstall. Run `bun pm untrusted` for details.", + "", + ]); + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); - err = await new Response(stderr).text(); - out = await new Response(stdout).text(); - expect(err).not.toContain("Saved lockfile"); - expect(err).not.toContain("not found"); - expect(err).not.toContain("error:"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - "+ uses-what-bin@1.5.0", - "+ what-bin@1.0.0", - "", - expect.stringContaining("3 packages installed"), - "", - "Blocked 1 postinstall. Run `bun pm untrusted` for details.", - "", - ]); - expect(await exited).toBe(0); -}); + await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); -test("it should re-symlink binaries that become invalid when updating package versions", async () => { - await writeFile( - join(packageDir, "package.json"), - JSON.stringify({ - name: "foo", - version: "1.0.0", - dependencies: { - "bin-change-dir": "1.0.0", - }, - scripts: { - postinstall: "bin-change-dir", - }, - }), - ); + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + })); - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, + err = await new Response(stderr).text(); + out = await new Response(stdout).text(); + expect(err).not.toContain("Saved lockfile"); + expect(err).not.toContain("not found"); + expect(err).not.toContain("error:"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "+ uses-what-bin@1.5.0", + "+ what-bin@1.0.0", + "", + expect.stringContaining("3 packages installed"), + "", + "Blocked 1 postinstall. Run `bun pm untrusted` for details.", + "", + ]); + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); - var err = await new Response(stderr).text(); - var out = await new Response(stdout).text(); - expect(err).toContain("Saved lockfile"); - expect(err).not.toContain("not found"); - expect(err).not.toContain("error:"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - "+ bin-change-dir@1.0.0", - "", - "1 package installed", - ]); - expect(await exited).toBe(0); - expect(await file(join(packageDir, "bin-1.0.0.txt")).text()).toEqual("success!"); - expect(await exists(join(packageDir, "bin-1.0.1.txt"))).toBeFalse(); + test("will link binaries for packages installed multiple times", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "uses-what-bin": "1.5.0", + }, + workspaces: ["packages/*"], + trustedDependencies: ["uses-what-bin"], + }), + ), + write( + join(packageDir, "packages", "pkg1", "package.json"), + JSON.stringify({ + name: "pkg1", + dependencies: { + "uses-what-bin": "1.0.0", + }, + }), + ), + write( + join(packageDir, "packages", "pkg2", "package.json"), + JSON.stringify({ + name: "pkg2", + dependencies: { + "uses-what-bin": "1.0.0", + }, + }), + ), + ]); - await writeFile( - join(packageDir, "package.json"), - JSON.stringify({ - name: "foo", - version: "1.0.0", - dependencies: { - "bin-change-dir": "1.0.1", - }, - scripts: { - postinstall: "bin-change-dir", - }, - }), - ); + // Root dependends on `uses-what-bin@1.5.0` and both packages depend on `uses-what-bin@1.0.0`. + // This test makes sure the binaries used by `pkg1` and `pkg2` are the correct version (`1.0.0`) + // instead of using the root version (`1.5.0`). - ({ stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, - })); + await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); - err = await new Response(stderr).text(); - out = await new Response(stdout).text(); - expect(err).toContain("Saved lockfile"); - expect(err).not.toContain("not found"); - expect(err).not.toContain("error:"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - "+ bin-change-dir@1.0.1", - "", - "1 package installed", - ]); - expect(await exited).toBe(0); - expect(await file(join(packageDir, "bin-1.0.0.txt")).text()).toEqual("success!"); - expect(await file(join(packageDir, "bin-1.0.1.txt")).text()).toEqual("success!"); + const results = await Promise.all([ + file(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt")).text(), + file(join(packageDir, "packages", "pkg1", "node_modules", "uses-what-bin", "what-bin.txt")).text(), + file(join(packageDir, "packages", "pkg2", "node_modules", "uses-what-bin", "what-bin.txt")).text(), + ]); + + expect(results).toEqual(["what-bin@1.5.0", "what-bin@1.0.0", "what-bin@1.0.0"]); + }); + + test("it should re-symlink binaries that become invalid when updating package versions", async () => { + await writeFile( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "bin-change-dir": "1.0.0", + }, + scripts: { + postinstall: "bin-change-dir", + }, + }), + ); + + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + + var err = await new Response(stderr).text(); + var out = await new Response(stdout).text(); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("not found"); + expect(err).not.toContain("error:"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "+ bin-change-dir@1.0.0", + "", + "1 package installed", + ]); + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await file(join(packageDir, "bin-1.0.0.txt")).text()).toEqual("success!"); + expect(await exists(join(packageDir, "bin-1.0.1.txt"))).toBeFalse(); + + await writeFile( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "bin-change-dir": "1.0.1", + }, + scripts: { + postinstall: "bin-change-dir", + }, + }), + ); + + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + })); + + err = await new Response(stderr).text(); + out = await new Response(stdout).text(); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("not found"); + expect(err).not.toContain("error:"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "+ bin-change-dir@1.0.1", + "", + "1 package installed", + ]); + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await file(join(packageDir, "bin-1.0.0.txt")).text()).toEqual("success!"); + expect(await file(join(packageDir, "bin-1.0.1.txt")).text()).toEqual("success!"); + }); + for (const global of [false, true]) { + test(`bin types${global ? " (global)" : ""}`, async () => { + if (global) { + await write( + join(packageDir, "bunfig.toml"), + ` + [install] + cache = false + registry = "http://localhost:${port}/" + globalBinDir = "${join(packageDir, "global-bin-dir").replace(/\\/g, "\\\\")}" + `, + ); + } else { + await write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + }), + ); + } + + const args = [ + bunExe(), + "install", + ...(global ? ["-g"] : []), + ...(global ? [`--config=${join(packageDir, "bunfig.toml")}`] : []), + "dep-with-file-bin", + "dep-with-single-entry-map-bin", + "dep-with-directory-bins", + "dep-with-map-bins", + ]; + const { stdout, stderr, exited } = spawn({ + cmd: args, + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env: global ? { ...env, BUN_INSTALL: join(packageDir, "global-install-dir") } : env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + + const out = await Bun.readableStreamToText(stdout); + expect(await exited).toBe(0); + + await runBin("dep-with-file-bin", "file-bin\n", global); + await runBin("single-entry-map-bin", "single-entry-map-bin\n", global); + await runBin("directory-bin-1", "directory-bin-1\n", global); + await runBin("directory-bin-2", "directory-bin-2\n", global); + await runBin("map-bin-1", "map-bin-1\n", global); + await runBin("map-bin-2", "map-bin-2\n", global); + }); + } + + async function runBin(binName: string, expected: string, global: boolean) { + const args = global ? [`./global-bin-dir/${binName}`] : [bunExe(), binName]; + const result = Bun.spawn({ + cmd: args, + stdout: "pipe", + stderr: "pipe", + cwd: packageDir, + env, + }); + + const out = await Bun.readableStreamToText(result.stdout); + expect(out).toEqual(expected); + const err = await Bun.readableStreamToText(result.stderr); + expect(err).toBeEmpty(); + expect(await result.exited).toBe(0); + } + + test("it will skip (without errors) if a folder from `directories.bin` does not exist", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + "missing-directory-bin": "file:missing-directory-bin-1.1.1.tgz", + }, + }), + ), + cp(join(import.meta.dir, "missing-directory-bin-1.1.1.tgz"), join(packageDir, "missing-directory-bin-1.1.1.tgz")), + ]); + + const { stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + }); + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(await exited).toBe(0); + }); }); test("it should install with missing bun.lockb, node_modules, and/or cache", async () => { @@ -1174,6 +1851,7 @@ test("it should install with missing bun.lockb, node_modules, and/or cache", asy "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); let lockfile = parseLockfile(packageDir); expect(lockfile).toMatchNodeModulesAt(packageDir); @@ -1216,6 +1894,7 @@ test("it should install with missing bun.lockb, node_modules, and/or cache", asy "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); lockfile = parseLockfile(packageDir); expect(lockfile).toMatchNodeModulesAt(packageDir); @@ -1256,10 +1935,11 @@ test("it should install with missing bun.lockb, node_modules, and/or cache", asy ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); } // delete cache - await rm(join(packageDir, "node_modules", ".cache"), { recursive: true, force: true }); + await rm(join(packageDir, ".bun-cache"), { recursive: true, force: true }); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install"], @@ -1280,10 +1960,11 @@ test("it should install with missing bun.lockb, node_modules, and/or cache", asy expect.stringContaining("Checked 19 installs across 23 packages (no changes)"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); // delete bun.lockb and cache await rm(join(packageDir, "bun.lockb"), { recursive: true, force: true }); - await rm(join(packageDir, "node_modules", ".cache"), { recursive: true, force: true }); + await rm(join(packageDir, ".bun-cache"), { recursive: true, force: true }); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install"], @@ -1295,6 +1976,7 @@ test("it should install with missing bun.lockb, node_modules, and/or cache", asy })); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); [err, out] = await Promise.all([new Response(stderr).text(), new Response(stdout).text()]); @@ -1415,6 +2097,8 @@ describe("hoisting", async () => { expect(out).toContain(`+ ${dep}@${dependencies[dep]}`); } expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).text()).toContain(expected); await rm(join(packageDir, "bun.lockb")); @@ -1573,6 +2257,8 @@ describe("hoisting", async () => { expect(out).toContain(`+ ${dep}@${dependencies[dep]}`); } expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).text()).toContain(expected); await rm(join(packageDir, "bun.lockb")); @@ -1596,6 +2282,8 @@ describe("hoisting", async () => { } expect(out).not.toContain("package installed"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).text()).toContain(expected); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); @@ -1616,6 +2304,8 @@ describe("hoisting", async () => { expect(err).not.toContain("error:"); expect(out).not.toContain("package installed"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).text()).toContain(expected); }); } @@ -1658,6 +2348,8 @@ describe("hoisting", async () => { ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", version: "1.0.0", @@ -1705,6 +2397,8 @@ describe("hoisting", async () => { ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", version: "2.0.0", @@ -1783,6 +2477,7 @@ describe("hoisting", async () => { version: "2.0.0", }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("hoisting/using incorrect peer dep on initial install", async () => { @@ -1822,6 +2517,8 @@ describe("hoisting", async () => { ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", version: "2.0.0", @@ -1869,6 +2566,8 @@ describe("hoisting", async () => { ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", version: "1.0.0", @@ -1918,6 +2617,7 @@ describe("hoisting", async () => { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", @@ -1980,6 +2680,7 @@ describe("hoisting", async () => { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", @@ -2040,6 +2741,7 @@ describe("hoisting", async () => { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", @@ -2091,6 +2793,7 @@ describe("hoisting", async () => { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", @@ -2141,6 +2844,8 @@ describe("workspaces", async () => { "2 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "root", workspaces: ["foo"], @@ -2166,6 +2871,8 @@ describe("workspaces", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "foo", "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -2192,13 +2899,9 @@ describe("workspaces", async () => { "3 packages installed", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".bin", - ".cache", - "foo", - "no-deps", - "what-bin", - ]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "foo", "no-deps", "what-bin"]); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); await rm(join(packageDir, "bun.lockb")); @@ -2218,13 +2921,9 @@ describe("workspaces", async () => { "3 packages installed", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".bin", - ".cache", - "foo", - "no-deps", - "what-bin", - ]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "foo", "no-deps", "what-bin"]); }); test("adding packages in workspaces", async () => { await writeFile( @@ -2274,6 +2973,8 @@ describe("workspaces", async () => { "3 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "bar"))).toBeTrue(); expect(await exists(join(packageDir, "node_modules", "boba"))).toBeTrue(); expect(await exists(join(packageDir, "node_modules", "pkg5"))).toBeTrue(); @@ -2295,6 +2996,8 @@ describe("workspaces", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", workspaces: ["packages/*"], @@ -2321,6 +3024,8 @@ describe("workspaces", async () => { "3 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "packages", "boba", "package.json")).json()).toEqual({ name: "boba", version: "1.0.0", @@ -2330,7 +3035,6 @@ describe("workspaces", async () => { }, }); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "@types", "bar", "boba", @@ -2356,6 +3060,8 @@ describe("workspaces", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "packages", "boba", "package.json")).json()).toEqual({ name: "boba", version: "1.0.0", @@ -2366,7 +3072,6 @@ describe("workspaces", async () => { }, }); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "@types", "bar", "boba", @@ -2480,6 +3185,7 @@ describe("workspaces", async () => { "2 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install"], @@ -2500,6 +3206,7 @@ describe("workspaces", async () => { "Checked 2 installs across 3 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); await rm(join(packageDir, "bun.lockb"), { recursive: true, force: true }); @@ -2525,6 +3232,7 @@ describe("workspaces", async () => { "2 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install"], @@ -2545,6 +3253,7 @@ describe("workspaces", async () => { "Checked 2 installs across 3 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); } } @@ -2595,6 +3304,8 @@ describe("workspaces", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "workspace-1", "package.json")).json()).toEqual({ name: "workspace-1", version: "1.0.0", @@ -2622,6 +3333,8 @@ describe("workspaces", async () => { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "workspace-1", "package.json")).json()).toEqual({ name: "workspace-1", version: "1.0.0", @@ -2677,6 +3390,8 @@ describe("workspaces", async () => { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "workspace-1", "package.json")).json()).toEqual({ name: "workspace-1", version: "1.0.0", @@ -2832,6 +3547,8 @@ describe("transitive file dependencies", () => { ]); var { out } = await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "14 packages installed"]); await checkHoistedFiles(); @@ -2841,11 +3558,15 @@ describe("transitive file dependencies", () => { // reinstall ({ out } = await runBunInstall(env, packageDir, { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "14 packages installed"]); await checkHoistedFiles(); ({ out } = await runBunInstall(env, packageDir, { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "1 package installed"]); await checkHoistedFiles(); @@ -2855,6 +3576,8 @@ describe("transitive file dependencies", () => { // install from workspace ({ out } = await runBunInstall(env, join(packageDir, "pkg1"))); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ @another-scope/file-dep@1.0.0", @@ -2872,11 +3595,15 @@ describe("transitive file dependencies", () => { expect(await exists(join(packageDir, "pkg1", "node_modules"))).toBeFalse(); ({ out } = await runBunInstall(env, join(packageDir, "pkg1"), { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "1 package installed"]); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); ({ out } = await runBunInstall(env, join(packageDir, "pkg1"), { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ @another-scope/file-dep@1.0.0", @@ -2936,6 +3663,8 @@ describe("transitive file dependencies", () => { ]); var { out } = await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ @another-scope/file-dep@1.0.1", @@ -2956,6 +3685,8 @@ describe("transitive file dependencies", () => { // reinstall ({ out } = await runBunInstall(env, packageDir, { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ @another-scope/file-dep@1.0.1", @@ -2972,6 +3703,8 @@ describe("transitive file dependencies", () => { await checkUnhoistedFiles(); ({ out } = await runBunInstall(env, packageDir, { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "1 package installed"]); await checkUnhoistedFiles(); @@ -2982,6 +3715,8 @@ describe("transitive file dependencies", () => { // install from workspace ({ out } = await runBunInstall(env, join(packageDir, "pkg1"))); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ @another-scope/file-dep@1.0.0", @@ -2998,12 +3733,16 @@ describe("transitive file dependencies", () => { await checkUnhoistedFiles(); ({ out } = await runBunInstall(env, join(packageDir, "pkg1"), { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "1 package installed"]); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); await rm(join(packageDir, "pkg1", "node_modules"), { recursive: true, force: true }); ({ out } = await runBunInstall(env, join(packageDir, "pkg1"), { savesLockfile: false })); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ "", "+ @another-scope/file-dep@1.0.0", @@ -3071,8 +3810,9 @@ describe("transitive file dependencies", () => { "13 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "@another-scope", "@scoped", "aliased-file-dep", @@ -3101,6 +3841,7 @@ describe("transitive file dependencies", () => { expect(err).not.toContain("panic:"); expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "1 package installed"]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await checkHoistedFiles(); @@ -3122,7 +3863,6 @@ describe("transitive file dependencies", () => { expect(err).not.toContain("error:"); expect(err).not.toContain("panic:"); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "@another-scope", "@scoped", "aliased-file-dep", @@ -3132,6 +3872,7 @@ describe("transitive file dependencies", () => { "self-file-dep", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await checkHoistedFiles(); }); @@ -3188,7 +3929,9 @@ describe("transitive file dependencies", () => { "2 packages installed", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".cache", "pkg0", "pkg1"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual(["pkg0", "pkg1"]); expect(await file(join(packageDir, "node_modules", "pkg0", "package.json")).json()).toEqual({ name: "pkg0", version: "1.1.1", @@ -3215,6 +3958,7 @@ test("name from manifest is scoped and url encoded", async () => { ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); const files = await Promise.all([ file(join(packageDir, "node_modules", "@url", "encoding.2", "package.json")).json(), @@ -3243,6 +3987,8 @@ describe("update", () => { ); await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3269,6 +4015,8 @@ describe("update", () => { ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).json()).toMatchObject({ name: "a-dep", version: "1.0.10", @@ -3276,6 +4024,8 @@ describe("update", () => { // Update without args, `latest` should stay await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3285,6 +4035,8 @@ describe("update", () => { // Update with `a-dep` and `--latest`, `latest` should be replaced with the installed version await runBunUpdate(env, packageDir, ["a-dep"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3292,6 +4044,8 @@ describe("update", () => { }, }); await runBunUpdate(env, packageDir, ["--latest"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3315,6 +4069,8 @@ describe("update", () => { }), ); async function check(version: string) { + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", dependency, "package.json")).json()).toMatchObject({ name: "a-dep", version: version.replace(/.*@/, ""), @@ -3356,12 +4112,16 @@ describe("update", () => { ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ name: "no-deps", version: "1.0.1", }); let { out } = await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "Checked 1 install across 2 packages (no changes)"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3372,6 +4132,8 @@ describe("update", () => { // another update does not change anything (previously the version would update because it was changed to `^1.0.1`) ({ out } = await runBunUpdate(env, packageDir)); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "Checked 1 install across 2 packages (no changes)"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3409,6 +4171,8 @@ describe("update", () => { if (latest) { await runBunUpdate(env, packageDir, ["--latest"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3431,6 +4195,8 @@ describe("update", () => { }); } else { await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3496,12 +4262,16 @@ describe("update", () => { ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ name: "no-deps", version: "1.0.1", }); let { out } = await runBunUpdate(env, packageDir, ["no-deps"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "installed no-deps@1.0.1", "", expect.stringContaining("done"), ""]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3513,6 +4283,8 @@ describe("update", () => { // update with --latest should only change the update request and keep `~` ({ out } = await runBunUpdate(env, packageDir, ["no-deps", "--latest"])); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "installed no-deps@2.0.0", "", "1 package installed"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3536,6 +4308,8 @@ describe("update", () => { ); await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3559,6 +4333,8 @@ describe("update", () => { ); await runBunUpdate(env, packageDir, ["aliased-dep"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3582,6 +4358,7 @@ describe("update", () => { ); await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "package.json")).json()).toMatchObject({ name: "foo", @@ -3596,6 +4373,8 @@ describe("update", () => { }); const { out } = await runBunUpdate(env, packageDir, ["--latest"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "^ aliased-dep 5.0.0-alpha.150 -> 5.0.0-alpha.153", "", "1 package installed"]); expect(await file(join(packageDir, "package.json")).json()).toMatchObject({ name: "foo", @@ -3617,6 +4396,8 @@ describe("update", () => { ); let { out } = await runBunUpdate(env, packageDir, ["--no-save"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "+ a-dep@1.0.1", "", "1 package installed"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3636,6 +4417,8 @@ describe("update", () => { ); ({ out } = await runBunUpdate(env, packageDir, ["--no-save"])); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "+ a-dep@1.0.10", "", "1 package installed"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3646,6 +4429,8 @@ describe("update", () => { // now save ({ out } = await runBunUpdate(env, packageDir)); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "Checked 1 install across 2 packages (no changes)"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3666,6 +4451,8 @@ describe("update", () => { ); await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3677,6 +4464,8 @@ describe("update", () => { }); // update with package name does not update beyond version range await runBunUpdate(env, packageDir, ["dep-with-tags"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3689,6 +4478,8 @@ describe("update", () => { // now update with a higher version range await runBunUpdate(env, packageDir, ["dep-with-tags@^2.0.0"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -3747,6 +4538,8 @@ describe("update", () => { // initial install, update root let { out } = await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual([ "", "+ a-dep@1.0.10", @@ -3763,7 +4556,8 @@ describe("update", () => { "+ uses-what-bin@1.5.0", "+ what-bin@1.5.0", "", - expect.stringContaining("20 packages installed"), + // Due to optional-native dependency, this can be either 20 or 19 packages + expect.stringMatching(/(?:20|19) packages installed/), "", "Blocked 1 postinstall. Run `bun pm untrusted` for details.", "", @@ -3800,6 +4594,8 @@ describe("update", () => { "uses-what-bin", "a-dep@1.0.5", ])); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual([ "", "installed what-bin@1.5.0 with binaries:", @@ -3844,6 +4640,8 @@ describe("update", () => { }); ({ out } = await runBunUpdate(env, join(packageDir, "packages", "pkg1"), ["a-dep@^1.0.5"])); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "installed a-dep@1.0.10", "", expect.stringMatching(/(\[\d+\.\d+m?s\])/), ""]); expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).json()).toMatchObject({ name: "a-dep", @@ -3881,6 +4679,8 @@ describe("update", () => { ); const { out } = args ? await runBunUpdate(env, packageDir, ["a-dep"]) : await runBunUpdate(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", args ? "installed a-dep@1.0.10" : "+ a-dep@1.0.10", "", "1 package installed"]); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -3929,6 +4729,7 @@ describe("update", () => { ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ version: "1.0.0", @@ -3942,6 +4743,8 @@ describe("update", () => { // update no-deps, no range, no change let { out } = await runBunUpdate(env, packageDir, ["no-deps"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "installed no-deps@1.0.0", "", expect.stringMatching(/(\[\d+\.\d+m?s\])/), ""]); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ version: "1.0.0", @@ -3949,6 +4752,8 @@ describe("update", () => { // update package that doesn't exist to workspace, should add to package.json ({ out } = await runBunUpdate(env, join(packageDir, "packages", "pkg1"), ["no-deps"])); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "installed no-deps@2.0.0", "", "1 package installed"]); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ version: "1.0.0", @@ -3975,6 +4780,8 @@ describe("update", () => { ); ({ out } = await runBunUpdate(env, packageDir, ["no-deps"])); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(out).toEqual(["", "installed no-deps@1.1.0", "", "1 package installed"]); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toMatchObject({ version: "1.1.0", @@ -3993,6 +4800,7 @@ describe("update", () => { ); await runBunUpdate(env, packageDir, ["no-deps", "--latest"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); const files = await Promise.all([ file(join(packageDir, "node_modules", "no-deps", "package.json")).json(), @@ -4016,6 +4824,8 @@ test("packages dependening on each other with aliases does not infinitely loop", ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + const files = await Promise.all([ file(join(packageDir, "node_modules", "alias-loop-1", "package.json")).json(), file(join(packageDir, "node_modules", "alias-loop-2", "package.json")).json(), @@ -4062,6 +4872,8 @@ test("it should re-populate .bin folder if package is reinstalled", async () => "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + const bin = process.platform === "win32" ? "what-bin.exe" : "what-bin"; expect(Bun.which("what-bin", { PATH: join(packageDir, "node_modules", ".bin") })).toBe( join(packageDir, "node_modules", ".bin", bin), @@ -4096,6 +4908,8 @@ test("it should re-populate .bin folder if package is reinstalled", async () => expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(Bun.which("what-bin", { PATH: join(packageDir, "node_modules", ".bin") })).toBe( join(packageDir, "node_modules", ".bin", bin), ); @@ -4137,6 +4951,7 @@ test("one version with binary map", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await readdirSorted(join(packageDir, "node_modules", ".bin"))).toHaveBins(["map-bin", "map_bin"]); expect(join(packageDir, "node_modules", ".bin", "map-bin")).toBeValidBin(join("..", "map-bin", "bin", "map-bin")); @@ -4175,6 +4990,7 @@ test("multiple versions with binary map", async () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await readdirSorted(join(packageDir, "node_modules", ".bin"))).toHaveBins(["map-bin", "map_bin"]); expect(join(packageDir, "node_modules", ".bin", "map-bin")).toBeValidBin( @@ -4200,6 +5016,7 @@ test("duplicate dependency in optionalDependencies maintains sort order", async ); await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); const lockfile = parseLockfile(packageDir); expect(lockfile).toMatchNodeModulesAt(packageDir); @@ -4217,7 +5034,7 @@ test("duplicate dependency in optionalDependencies maintains sort order", async }); const out = await Bun.readableStreamToText(stdout); - expect(out).toMatchSnapshot(); + expect(out.replaceAll(`${port}`, "4873")).toMatchSnapshot(); expect(await exited).toBe(0); }); @@ -4278,6 +5095,7 @@ test("missing package on reinstall, some with binaries", async () => { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await rm(join(packageDir, "node_modules", "native"), { recursive: true, force: true }); await rm(join(packageDir, "node_modules", "left-pad"), { recursive: true, force: true }); @@ -4320,6 +5138,7 @@ test("missing package on reinstall, some with binaries", async () => { expect.stringContaining("7 packages installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await exists(join(packageDir, "node_modules", "native", "package.json"))).toBe(true); expect(await exists(join(packageDir, "node_modules", "left-pad", "package.json"))).toBe(true); @@ -4341,9 +5160,9 @@ test("missing package on reinstall, some with binaries", async () => { // waiter thread is only a thing on Linux. for (const forceWaiterThread of isLinux ? [false, true] : [false]) { - const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; describe("lifecycle scripts" + (forceWaiterThread ? " (waiter thread)" : ""), async () => { test("root package with all lifecycle scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; const writeScript = async (name: string) => { const contents = ` import { writeFileSync, existsSync, rmSync } from "fs"; @@ -4397,6 +5216,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "preinstall.txt"))).toBeTrue(); expect(await exists(join(packageDir, "install.txt"))).toBeTrue(); expect(await exists(join(packageDir, "postinstall.txt"))).toBeTrue(); @@ -4452,6 +5273,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "preinstall.txt")).text()).toBe("preinstall exists!"); expect(await file(join(packageDir, "install.txt")).text()).toBe("install exists!"); expect(await file(join(packageDir, "postinstall.txt")).text()).toBe("postinstall exists!"); @@ -4492,6 +5315,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { env: testEnv, })); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + err = await new Response(stderr).text(); out = await new Response(stdout).text(); expect(err).toContain("Saved lockfile"); @@ -4518,6 +5343,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); test("workspace lifecycle scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -4585,6 +5412,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { var out = await new Response(stdout).text(); expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "2 packages installed"]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await exists(join(packageDir, "preinstall.txt"))).toBeTrue(); expect(await exists(join(packageDir, "install.txt"))).toBeTrue(); @@ -4607,6 +5435,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); test("dependency lifecycle scripts run before root lifecycle scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const script = '[[ -f "./node_modules/uses-what-bin-slow/what-bin.txt" ]]'; await writeFile( join(packageDir, "package.json"), @@ -4646,9 +5476,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("install a dependency with lifecycle scripts, then add to trusted dependencies and install again", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -4685,6 +5518,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); const depDir = join(packageDir, "node_modules", "all-lifecycle-scripts"); expect(await exists(join(depDir, "preinstall.txt"))).toBeFalse(); @@ -4727,6 +5561,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("Checked 1 install across 2 packages (no changes)"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await file(join(depDir, "preinstall.txt")).text()).toBe("preinstall!"); expect(await file(join(depDir, "install.txt")).text()).toBe("install!"); @@ -4737,6 +5572,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); test("adding a package without scripts to trustedDependencies", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -4770,7 +5607,9 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "1 package installed", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", ".cache", "what-bin"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "what-bin"]); const isWindows = process.platform === "win32"; const what_bin_bins = !isWindows ? ["what-bin"] : ["what-bin.bunx", "what-bin.exe"]; // prettier-ignore @@ -4795,6 +5634,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); await rm(join(packageDir, "bun.lockb")); @@ -4829,7 +5669,9 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "1 package installed", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", ".cache", "what-bin"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "what-bin"]); expect(await readdirSorted(join(packageDir, "node_modules", ".bin"))).toEqual(what_bin_bins); ({ stdout, stderr, exited } = spawn({ @@ -4851,7 +5693,9 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", ".cache", "what-bin"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "what-bin"]); expect(await readdirSorted(join(packageDir, "node_modules", ".bin"))).toEqual(what_bin_bins); // add it to trusted dependencies @@ -4886,11 +5730,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", ".cache", "what-bin"]); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".bin", "what-bin"]); expect(await readdirSorted(join(packageDir, "node_modules", ".bin"))).toEqual(what_bin_bins); }); test("lifecycle scripts run if node_modules is deleted", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -4924,7 +5772,10 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("error:"); expect(await exists(join(packageDir, "node_modules", "lifecycle-postinstall", "postinstall.txt"))).toBeTrue(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + await rm(join(packageDir, "node_modules"), { force: true, recursive: true }); + await rm(join(packageDir, ".bun-cache"), { recursive: true, force: true }); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install"], cwd: packageDir, @@ -4946,9 +5797,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("error:"); expect(await exists(join(packageDir, "node_modules", "lifecycle-postinstall", "postinstall.txt"))).toBeTrue(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("INIT_CWD is set to the correct directory", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5000,12 +5854,16 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "test.txt")).text()).toBe(packageDir); expect(await file(join(packageDir, "node_modules/lifecycle-init-cwd/test.txt")).text()).toBe(packageDir); expect(await file(join(packageDir, "node_modules/another-init-cwd/test.txt")).text()).toBe(packageDir); }); test("failing lifecycle script should print output", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5030,11 +5888,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { const err = await new Response(stderr).text(); expect(err).toContain("hello"); expect(await exited).toBe(1); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + const out = await new Response(stdout).text(); expect(out).toBeEmpty(); }); test("failing root lifecycle script should print output correctly", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5055,6 +5917,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); expect(await exited).toBe(1); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await Bun.readableStreamToText(stdout)).toBeEmpty(); const err = await Bun.readableStreamToText(stderr); expect(err).toContain("error: Oops!"); @@ -5062,6 +5926,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); test("exit 0 in lifecycle scripts works", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5095,9 +5961,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("--ignore-scripts should skip lifecycle scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5131,9 +6000,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should add `node-gyp rebuild` as the `install` script when `install` and `postinstall` don't exist and `binding.gyp` exists in the root of the package", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5167,10 +6039,14 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("2 packages installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules/binding-gyp-scripts/build.node"))).toBeTrue(); }); test("automatic node-gyp scripts should not run for untrusted dependencies, and should run after adding to `trustedDependencies`", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const packageJSON: any = { name: "foo", version: "1.0.0", @@ -5204,6 +6080,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "binding-gyp-scripts", "build.node"))).toBeFalse(); packageJSON.trustedDependencies = ["binding-gyp-scripts"]; @@ -5225,10 +6103,14 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("warn:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "binding-gyp-scripts", "build.node"))).toBeTrue(); }); test("automatic node-gyp scripts work in package root", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5263,6 +6145,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "build.node"))).toBeTrue(); await rm(join(packageDir, "build.node")); @@ -5277,10 +6161,14 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { })); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "build.node"))).toBeTrue(); }); test("auto node-gyp scripts work when scripts exists other than `install` and `preinstall`", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5320,11 +6208,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "build.node"))).toBeTrue(); }); for (const script of ["install", "preinstall"]) { test(`does not add auto node-gyp script when ${script} script exists`, async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const packageJSON: any = { name: "foo", version: "1.0.0", @@ -5359,11 +6251,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "build.node"))).toBeFalse(); }); } test("git dependencies also run `preprepare`, `prepare`, and `postprepare` scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5399,6 +6295,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "preprepare.txt"))).toBeFalse(); expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "prepare.txt"))).toBeFalse(); expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "postprepare.txt"))).toBeFalse(); @@ -5434,6 +6332,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("warn:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "preprepare.txt"))).toBeTrue(); expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "prepare.txt"))).toBeTrue(); expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "postprepare.txt"))).toBeTrue(); @@ -5443,6 +6343,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); test("root lifecycle scripts should wait for dependency lifecycle scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5484,6 +6386,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "2 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); async function createPackagesWithScripts( @@ -5526,6 +6429,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { } test("reach max concurrent scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const scripts = { "preinstall": `${bunExe()} -e 'Bun.sleepSync(500)'`, }; @@ -5554,9 +6459,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "4 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("stress test", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const dependenciesList = await createPackagesWithScripts(500, { "postinstall": `${bunExe()} --version`, }); @@ -5585,9 +6493,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should install and use correct binary version", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + // this should install `what-bin` in two places: // // - node_modules/.bin/what-bin@1.5.0 @@ -5630,6 +6541,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "what-bin", "what-bin.js")).text()).toContain( "what-bin@1.5.0", ); @@ -5672,6 +6585,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("warn:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "node_modules", "what-bin", "what-bin.js")).text()).toContain( "what-bin@1.0.0", ); @@ -5703,9 +6618,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("3 packages installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("node-gyp should always be available for lifecycle scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5734,10 +6652,13 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { // if node-gyp isn't available, it would return a non-zero exit code expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); // if this test fails, `electron` might be removed from the default list test("default trusted dependencies should work", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5772,9 +6693,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(out).not.toContain("Blocked"); expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("default trusted dependencies should not be used of trustedDependencies is populated", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5814,10 +6738,13 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse(); expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue(); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); + await rm(join(packageDir, ".bun-cache"), { recursive: true, force: true }); await rm(join(packageDir, "bun.lockb")); await writeFile( @@ -5860,12 +6787,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeTrue(); expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse(); }); test("does not run any scripts if trustedDependencies is an empty list", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5904,11 +6834,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse(); expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse(); }); test("will run default trustedDependencies after install that didn't include them", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -5947,6 +6881,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse(); await writeFile( @@ -5981,10 +6917,65 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue(); }); describe("--trust", async () => { + test("unhoisted untrusted scripts, none at root node_modules", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + // prevents real `uses-what-bin` from hoisting to root + "uses-what-bin": "npm:a-dep@1.0.3", + }, + workspaces: ["pkg1"], + }), + ), + write( + join(packageDir, "pkg1", "package.json"), + JSON.stringify({ + name: "pkg1", + dependencies: { + "uses-what-bin": "1.0.0", + }, + }), + ), + ]); + + await runBunInstall(testEnv, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + const results = await Promise.all([ + exists(join(packageDir, "node_modules", "pkg1", "node_modules", "uses-what-bin")), + exists(join(packageDir, "node_modules", "pkg1", "node_modules", "uses-what-bin", "what-bin.txt")), + ]); + + expect(results).toEqual([true, false]); + + const { stderr, exited } = spawn({ + cmd: [bunExe(), "pm", "trust", "--all"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env: testEnv, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + expect( + await exists(join(packageDir, "node_modules", "pkg1", "node_modules", "uses-what-bin", "what-bin.txt")), + ).toBeTrue(); + }); const trustTests = [ { label: "only name", @@ -6050,6 +7041,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { ]; for (const { label, packageJson } of trustTests) { test(label, async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile(join(packageDir, "package.json"), JSON.stringify(packageJson)); let { stdout, stderr, exited } = spawn({ @@ -6108,6 +7101,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { } describe("packages without lifecycle scripts", async () => { test("initial install", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6146,6 +7141,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); }); test("already installed", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6224,6 +7221,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { describe("updating trustedDependencies", async () => { test("existing trustedDependencies, unchanged trustedDependencies", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6257,6 +7256,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("2 packages installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeTrue(); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -6287,9 +7288,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 2 installs across 3 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("existing trustedDependencies, removing trustedDependencies", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6323,6 +7327,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("2 packages installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeTrue(); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -6365,6 +7371,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 2 installs across 3 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", dependencies: { @@ -6375,6 +7383,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); test("non-existent trustedDependencies, then adding it", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6407,6 +7417,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue(); expect(await file(join(packageDir, "package.json")).json()).toEqual({ name: "foo", @@ -6451,11 +7463,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "Checked 1 install across 2 packages (no changes)", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue(); }); }); test("node -p should work in postinstall scripts", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6487,11 +7503,14 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("error:"); expect(err).not.toContain("warn:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await exists(join(packageDir, "postinstall.txt"))).toBeTrue(); }); test("ensureTempNodeGypScript works", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6523,9 +7542,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { expect(err).not.toContain("error:"); expect(err).not.toContain("warn:"); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("bun pm trust and untrusted on missing package", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6560,6 +7582,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { ]); expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); // remove uses-what-bin from node_modules, bun pm trust and untrusted should handle missing package await rm(join(packageDir, "node_modules", "uses-what-bin"), { recursive: true, force: true }); @@ -6601,6 +7624,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { // for both cases, we need to update this test for (const withRm of [true, false]) { test(withRm ? "withRm" : "withoutRm", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6637,6 +7662,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse(); ({ stdout, stderr, exited } = spawn({ @@ -6775,6 +7802,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { describe.if(!forceWaiterThread || process.platform === "linux")("does not use 100% cpu", async () => { test("install", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + await writeFile( join(packageDir, "package.json"), JSON.stringify({ @@ -6796,12 +7825,15 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); expect(await proc.exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(proc.resourceUsage()?.cpuTime.total).toBeLessThan(750_000); }); // https://github.com/oven-sh/bun/issues/11252 test.todoIf(isWindows)("bun pm trust", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const dep = isWindows ? "uses-what-bin-slow-window" : "uses-what-bin-slow"; await writeFile( join(packageDir, "package.json"), @@ -6823,6 +7855,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { }); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); expect(await exists(join(packageDir, "node_modules", dep, "what-bin.txt"))).toBeFalse(); @@ -6845,6 +7878,8 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { describe("stdout/stderr is inherited from root scripts during install", async () => { test("without packages", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const exe = bunExe().replace(/\\/g, "\\\\"); await writeFile( join(packageDir, "package.json"), @@ -6889,9 +7924,12 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("with a package", async () => { + const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env; + const exe = bunExe().replace(/\\/g, "\\\\"); await writeFile( join(packageDir, "package.json"), @@ -6943,6 +7981,7 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) { "", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); }); } @@ -7103,6 +8142,8 @@ test("it should be able to find binary in node_modules/.bin from parent director expect.stringContaining("1 package installed"), ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + expect(await file(join(packageDir, "morePackageDir", "missing-bin.txt")).text()).toBe("missing-bin@WHAT"); }); @@ -7239,6 +8280,7 @@ describe("semver", () => { "1 package installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); } @@ -7254,20 +8296,82 @@ describe("semver", () => { }), ); - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, - }); + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + + var err = await new Response(stderr).text(); + var out = await new Response(stdout).text(); + expect(err).toContain('InvalidDependencyVersion parsing version "pre-1 || pre-2"'); + expect(await exited).toBe(1); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + expect(out).toBeEmpty(); + }); +}); + +test("doesn't error when the migration is out of sync", async () => { + const cwd = tempDirWithFiles("out-of-sync-1", { + "package.json": JSON.stringify({ + "devDependencies": { + "no-deps": "1.0.0", + }, + }), + "package-lock.json": JSON.stringify({ + "name": "reproo", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "reproo", + "dependencies": { + "no-deps": "2.0.0", + }, + "devDependencies": { + "no-deps": "1.0.0", + }, + }, + "node_modules/no-deps": { + "version": "1.0.0", + "resolved": `http://localhost:${port}/no-deps/-/no-deps-1.0.0.tgz`, + "integrity": + "sha512-v4w12JRjUGvfHDUP8vFDwu0gUWu04j0cv9hLb1Abf9VdaXu4XcrddYFTMVBVvmldKViGWH7jrb6xPJRF0wq6gw==", + "dev": true, + }, + }, + }), + }); + + const subprocess = Bun.spawn([bunExe(), "install"], { + env, + cwd, + stdio: ["ignore", "ignore", "inherit"], + }); + + await subprocess.exited; + + expect(subprocess.exitCode).toBe(0); + + let { stdout, exitCode } = Bun.spawnSync({ + cmd: [bunExe(), "pm", "ls"], + env, + cwd, + stdio: ["ignore", "pipe", "inherit"], + }); + let out = stdout.toString().trim(); + expect(out).toContain("no-deps@1.0.0"); + // only one no-deps is installed + expect(out.lastIndexOf("no-deps")).toEqual(out.indexOf("no-deps")); + expect(exitCode).toBe(0); - var err = await new Response(stderr).text(); - var out = await new Response(stdout).text(); - expect(err).toContain('InvalidDependencyVersion parsing version "pre-1 || pre-2"'); - expect(await exited).toBe(1); - expect(out).toBeEmpty(); + expect(await file(join(cwd, "node_modules/no-deps/package.json")).json()).toMatchObject({ + version: "1.0.0", + name: "no-deps", }); }); @@ -7405,6 +8509,7 @@ for (let i = 0; i < prereleaseTests.length; i++) { version: expected, } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); } }); @@ -7538,6 +8643,7 @@ for (let i = 0; i < prereleaseFailTests.length; i++) { expect(out).toBeEmpty(); expect(err).toContain(`No version matching "${depVersion}" found for specifier "${depName}"`); expect(await exited).toBe(1); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); } }); @@ -7579,7 +8685,6 @@ describe("yarn tests", () => { "6 packages installed", ]); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "dragon-test-1-a", "dragon-test-1-b", "dragon-test-1-c", @@ -7605,6 +8710,7 @@ describe("yarn tests", () => { }, } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 2", async () => { @@ -7667,7 +8773,6 @@ describe("yarn tests", () => { "3 packages installed", ]); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "dragon-test-2-a", "dragon-test-2-b", "no-deps", @@ -7678,6 +8783,7 @@ describe("yarn tests", () => { }); expect(await exists(join(packageDir, "dragon-test-2-a", "node_modules"))).toBeFalse(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 3", async () => { @@ -7713,7 +8819,6 @@ describe("yarn tests", () => { "3 packages installed", ]); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "dragon-test-3-a", "dragon-test-3-b", "no-deps", @@ -7729,6 +8834,7 @@ describe("yarn tests", () => { }, } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 4", async () => { @@ -7773,12 +8879,7 @@ describe("yarn tests", () => { expect(err).not.toContain("not found"); expect(err).not.toContain("error:"); expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "3 packages installed"]); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", - "my-workspace", - "no-deps", - "peer-deps", - ]); + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual(["my-workspace", "no-deps", "peer-deps"]); expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ name: "no-deps", version: "1.0.0", @@ -7791,6 +8892,7 @@ describe("yarn tests", () => { }, } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 5", async () => { @@ -7847,7 +8949,6 @@ describe("yarn tests", () => { expect(err).not.toContain("error:"); expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "5 packages installed"]); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "a", "b", "no-deps", @@ -7870,6 +8971,7 @@ describe("yarn tests", () => { version: "1.0.0", } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test.todo("dragon test 6", async () => { @@ -7985,6 +9087,7 @@ describe("yarn tests", () => { "7 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test.todo("dragon test 7", async () => { @@ -8026,6 +9129,7 @@ describe("yarn tests", () => { "7 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await writeFile( join(packageDir, "test.js"), @@ -8065,6 +9169,7 @@ describe("yarn tests", () => { ), ).toBeFalse(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 8", async () => { @@ -8106,6 +9211,7 @@ describe("yarn tests", () => { "4 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 9", async () => { @@ -8147,6 +9253,7 @@ describe("yarn tests", () => { await file(join(packageDir, "node_modules", "second", "package.json")).json(), ); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test.todo("dragon test 10", async () => { @@ -8220,6 +9327,7 @@ describe("yarn tests", () => { " packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("dragon test 12", async () => { @@ -8274,7 +9382,6 @@ describe("yarn tests", () => { expect(err).not.toContain("not found"); expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual(["", "4 packages installed"]); expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ - ".cache", "fake-peer-deps", "no-deps", "peer-deps", @@ -8289,6 +9396,7 @@ describe("yarn tests", () => { }, } as any); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should not warn when the peer dependency resolution is compatible", async () => { @@ -8326,8 +9434,9 @@ describe("yarn tests", () => { "", "2 packages installed", ]); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".cache", "no-deps", "peer-deps-fixed"]); + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual(["no-deps", "peer-deps-fixed"]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should warn when the peer dependency resolution is incompatible", async () => { @@ -8365,8 +9474,9 @@ describe("yarn tests", () => { "", "2 packages installed", ]); - expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".cache", "no-deps", "peer-deps-fixed"]); + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual(["no-deps", "peer-deps-fixed"]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should install in such a way that two identical packages with different peer dependencies are different instances", async () => { @@ -8405,6 +9515,7 @@ describe("yarn tests", () => { "5 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await writeFile( join(packageDir, "test.js"), @@ -8472,6 +9583,7 @@ describe("yarn tests", () => { expect(out).toBe("true\ntrue\nfalse\n"); expect(err).toBeEmpty(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should install in such a way that two identical packages with the same peer dependencies are the same instances (simple)", async () => { @@ -8510,6 +9622,7 @@ describe("yarn tests", () => { "4 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await writeFile( join(packageDir, "test.js"), @@ -8533,6 +9646,7 @@ describe("yarn tests", () => { expect(out).toBe("true\n"); expect(err).toBeEmpty(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it should install in such a way that two identical packages with the same peer dependencies are the same instances (complex)", async () => { @@ -8573,6 +9687,7 @@ describe("yarn tests", () => { "4 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await writeFile( join(packageDir, "test.js"), @@ -8596,6 +9711,7 @@ describe("yarn tests", () => { expect(out).toBe("true\n"); expect(err).toBeEmpty(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); }); test("it shouldn't deduplicate two packages with similar peer dependencies but different names", async () => { @@ -8636,6 +9752,7 @@ describe("yarn tests", () => { "3 packages installed", ]); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); await writeFile(join(packageDir, "test.js"), `console.log(require('peer-deps') === require('peer-deps-too'));`); @@ -8653,137 +9770,618 @@ describe("yarn tests", () => { expect(out).toBe("false\n"); expect(err).toBeEmpty(); expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + }); + + test("it should reinstall and rebuild dependencies deleted by the user on the next install", async () => { + await writeFile( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "no-deps-scripted": "1.0.0", + "one-dep-scripted": "1.5.0", + }, + trustedDependencies: ["no-deps-scripted", "one-dep-scripted"], + }), + ); + + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--dev"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + + var err = await new Response(stderr).text(); + var out = await new Response(stdout).text(); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + expect(err).not.toContain("not found"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "+ no-deps-scripted@1.0.0", + "+ one-dep-scripted@1.5.0", + "", + expect.stringContaining("4 packages installed"), + ]); + expect(await exists(join(packageDir, "node_modules/one-dep-scripted/success.txt"))).toBeTrue(); + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + await rm(join(packageDir, "node_modules/one-dep-scripted"), { recursive: true, force: true }); + + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--dev"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + })); + + err = await new Response(stderr).text(); + out = await new Response(stdout).text(); + expect(err).not.toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + expect(err).not.toContain("not found"); + expect(await exists(join(packageDir, "node_modules/one-dep-scripted/success.txt"))).toBeTrue(); + expect(await exited).toBe(0); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + }); +}); + +test("tarball `./` prefix, duplicate directory with file, and empty directory", async () => { + await write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + dependencies: { + "tarball-without-package-prefix": "1.0.0", + }, + }), + ); + + // Entries in this tarball: + // + // ./ + // ./package1000.js + // ./package2/ + // ./package3/ + // ./package4/ + // ./package.json + // ./package/ + // ./package1000/ + // ./package/index.js + // ./package4/package5/ + // ./package4/package.json + // ./package3/package6/ + // ./package3/package6/index.js + // ./package2/index.js + // package3/ + // package3/package6/ + // package3/package6/index.js + // + // The directory `package3` is added twice, but because one doesn't start + // with `./`, it is stripped from the path and a copy of `package6` is placed + // at the root of the output directory. Also `package1000` is not included in + // the output because it is an empty directory. + + await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + const prefix = join(packageDir, "node_modules", "tarball-without-package-prefix"); + const results = await Promise.all([ + file(join(prefix, "package.json")).json(), + file(join(prefix, "package1000.js")).text(), + file(join(prefix, "package", "index.js")).text(), + file(join(prefix, "package2", "index.js")).text(), + file(join(prefix, "package3", "package6", "index.js")).text(), + file(join(prefix, "package4", "package.json")).json(), + exists(join(prefix, "package4", "package5")), + exists(join(prefix, "package1000")), + file(join(prefix, "package6", "index.js")).text(), + ]); + expect(results).toEqual([ + { + name: "tarball-without-package-prefix", + version: "1.0.0", + }, + "hi", + "ooops", + "ooooops", + "oooooops", + { + "name": "tarball-without-package-prefix", + "version": "2.0.0", + }, + false, + false, + "oooooops", + ]); + expect(await file(join(packageDir, "node_modules", "tarball-without-package-prefix", "package.json")).json()).toEqual( + { + name: "tarball-without-package-prefix", + version: "1.0.0", + }, + ); +}); + +describe("outdated", () => { + const edgeCaseTests = [ + { + description: "normal dep, smaller than column title", + packageJson: { + dependencies: { + "no-deps": "1.0.0", + }, + }, + }, + { + description: "normal dep, larger than column title", + packageJson: { + dependencies: { + "prereleases-1": "1.0.0-future.1", + }, + }, + }, + { + description: "dev dep, smaller than column title", + packageJson: { + devDependencies: { + "no-deps": "1.0.0", + }, + }, + }, + { + description: "dev dep, larger than column title", + packageJson: { + devDependencies: { + "prereleases-1": "1.0.0-future.1", + }, + }, + }, + { + description: "peer dep, smaller than column title", + packageJson: { + peerDependencies: { + "no-deps": "1.0.0", + }, + }, + }, + { + description: "peer dep, larger than column title", + packageJson: { + peerDependencies: { + "prereleases-1": "1.0.0-future.1", + }, + }, + }, + { + description: "optional dep, smaller than column title", + packageJson: { + optionalDependencies: { + "no-deps": "1.0.0", + }, + }, + }, + { + description: "optional dep, larger than column title", + packageJson: { + optionalDependencies: { + "prereleases-1": "1.0.0-future.1", + }, + }, + }, + ]; + + for (const { description, packageJson } of edgeCaseTests) { + test(description, async () => { + await write(join(packageDir, "package.json"), JSON.stringify(packageJson)); + await runBunInstall(env, packageDir); + assertManifestsPopulated(join(packageDir, ".bun-cache"), registryUrl()); + + const testEnv = { ...env, FORCE_COLOR: "1" }; + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "outdated"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env: testEnv, + }); + + expect(await exited).toBe(0); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).not.toContain("panic:"); + const out = await Bun.readableStreamToText(stdout); + expect(out).toMatchSnapshot(); + }); + } + test("in workspace", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["pkg1"], + dependencies: { + "no-deps": "1.0.0", + }, + }), + ), + write( + join(packageDir, "pkg1", "package.json"), + JSON.stringify({ + name: "pkg1", + dependencies: { + "a-dep": "1.0.1", + }, + }), + ), + ]); + + await runBunInstall(env, packageDir); + + let { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "outdated"], + cwd: join(packageDir, "pkg1"), + stdout: "pipe", + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).not.toContain("panic:"); + let out = await Bun.readableStreamToText(stdout); + expect(out).toContain("a-dep"); + expect(out).not.toContain("no-deps"); + expect(await exited).toBe(0); + + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "outdated"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + })); + + const err2 = await Bun.readableStreamToText(stderr); + expect(err2).not.toContain("error:"); + expect(err2).not.toContain("panic:"); + let out2 = await Bun.readableStreamToText(stdout); + expect(out2).toContain("no-deps"); + expect(out2).not.toContain("a-dep"); + expect(await exited).toBe(0); }); - test("it should reinstall and rebuild dependencies deleted by the user on the next install", async () => { - await writeFile( + test("NO_COLOR works", async () => { + await write( join(packageDir, "package.json"), JSON.stringify({ name: "foo", - version: "1.0.0", dependencies: { - "no-deps-scripted": "1.0.0", - "one-dep-scripted": "1.5.0", + "a-dep": "1.0.1", }, - trustedDependencies: ["no-deps-scripted", "one-dep-scripted"], }), ); - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install", "--dev"], + await runBunInstall(env, packageDir); + + const testEnv = { ...env, NO_COLOR: "1" }; + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "outdated"], cwd: packageDir, stdout: "pipe", - stdin: "pipe", stderr: "pipe", - env, + env: testEnv, }); - var err = await new Response(stderr).text(); - var out = await new Response(stdout).text(); - expect(err).toContain("Saved lockfile"); + const err = await Bun.readableStreamToText(stderr); expect(err).not.toContain("error:"); - expect(err).not.toContain("not found"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - "+ no-deps-scripted@1.0.0", - "+ one-dep-scripted@1.5.0", - "", - expect.stringContaining("4 packages installed"), - ]); - expect(await exists(join(packageDir, "node_modules/one-dep-scripted/success.txt"))).toBeTrue(); + expect(err).not.toContain("panic:"); + + const out = await Bun.readableStreamToText(stdout); + expect(out).toContain("a-dep"); + expect(out).toMatchSnapshot(); + expect(await exited).toBe(0); + }); - await rm(join(packageDir, "node_modules/one-dep-scripted"), { recursive: true, force: true }); + async function setupWorkspace() { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["packages/*"], + dependencies: { + "no-deps": "1.0.0", + }, + }), + ), + write( + join(packageDir, "packages", "pkg1", "package.json"), + JSON.stringify({ + name: "pkg1", + dependencies: { + "a-dep": "1.0.1", + }, + }), + ), + write( + join(packageDir, "packages", "pkg2", "package.json"), + JSON.stringify({ + name: "pkg2222222222222", + dependencies: { + "prereleases-1": "1.0.0-future.1", + }, + }), + ), + ]); + } - ({ stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install", "--dev"], - cwd: packageDir, + async function runBunOutdated(env: any, cwd: string, ...args: string[]): Promise { + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "outdated", ...args], + cwd, stdout: "pipe", - stdin: "pipe", stderr: "pipe", env, - })); + }); - err = await new Response(stderr).text(); - out = await new Response(stdout).text(); - expect(err).not.toContain("Saved lockfile"); + const err = await Bun.readableStreamToText(stderr); expect(err).not.toContain("error:"); - expect(err).not.toContain("not found"); - expect(await exists(join(packageDir, "node_modules/one-dep-scripted/success.txt"))).toBeTrue(); - expect(await exited).toBe(0); + expect(err).not.toContain("panic:"); + const out = await Bun.readableStreamToText(stdout); + const exitCode = await exited; + expect(exitCode).toBe(0); + return out; + } + + test("--filter with workspace names and paths", async () => { + await setupWorkspace(); + await runBunInstall(env, packageDir); + + let out = await runBunOutdated(env, packageDir, "--filter", "*"); + expect(out).toContain("foo"); + expect(out).toContain("pkg1"); + expect(out).toContain("pkg2222222222222"); + + out = await runBunOutdated(env, join(packageDir, "packages", "pkg1"), "--filter", "./"); + expect(out).toContain("pkg1"); + expect(out).not.toContain("foo"); + expect(out).not.toContain("pkg2222222222222"); + + // in directory that isn't a workspace + out = await runBunOutdated(env, join(packageDir, "packages"), "--filter", "./*", "--filter", "!pkg1"); + expect(out).toContain("pkg2222222222222"); + expect(out).not.toContain("pkg1"); + expect(out).not.toContain("foo"); + + out = await runBunOutdated(env, join(packageDir, "packages", "pkg1"), "--filter", "../*"); + expect(out).not.toContain("foo"); + expect(out).toContain("pkg2222222222222"); + expect(out).toContain("pkg1"); + }); + + test("dependency pattern args", async () => { + await setupWorkspace(); + await runBunInstall(env, packageDir); + + let out = await runBunOutdated(env, packageDir, "no-deps", "--filter", "*"); + expect(out).toContain("no-deps"); + expect(out).not.toContain("a-dep"); + expect(out).not.toContain("prerelease-1"); + + out = await runBunOutdated(env, packageDir, "a-dep"); + expect(out).not.toContain("a-dep"); + expect(out).not.toContain("no-deps"); + expect(out).not.toContain("prerelease-1"); + + out = await runBunOutdated(env, packageDir, "*", "--filter", "*"); + expect(out).toContain("no-deps"); + expect(out).toContain("a-dep"); + expect(out).toContain("prereleases-1"); + }); + + test("scoped workspace names", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "@foo/bar", + workspaces: ["packages/*"], + dependencies: { + "no-deps": "1.0.0", + }, + }), + ), + write( + join(packageDir, "packages", "pkg1", "package.json"), + JSON.stringify({ + name: "@scope/pkg1", + dependencies: { + "a-dep": "1.0.1", + }, + }), + ), + ]); + + await runBunInstall(env, packageDir); + + let out = await runBunOutdated(env, packageDir, "--filter", "*"); + expect(out).toContain("@foo/bar"); + expect(out).toContain("@scope/pkg1"); + + out = await runBunOutdated(env, packageDir, "--filter", "*", "--filter", "!@foo/*"); + expect(out).not.toContain("@foo/bar"); + expect(out).toContain("@scope/pkg1"); }); }); -test("tarball `./` prefix, duplicate directory with file, and empty directory", async () => { - await write( +// TODO: setup verdaccio to run across multiple test files, then move this and a few other describe +// scopes (update, hoisting, ...) to other files +// +// test/cli/install/registry/bun-install-windowsshim.test.ts: +// +// This test is to verify that BinLinkingShim.zig creates correct shim files as +// well as bun_shim_impl.exe works in various edge cases. There are many fast +// paths for many many cases. +describe("windows bin linking shim should work", async () => { + if (!isWindows) return; + + const packageDir = tmpdirSync(); + + await writeFile( + join(packageDir, "bunfig.toml"), + ` +[install] +cache = false +registry = "http://localhost:${port}/" +`, + ); + + await writeFile( join(packageDir, "package.json"), JSON.stringify({ name: "foo", + version: "1.0.0", dependencies: { - "tarball-without-package-prefix": "1.0.0", + "bunx-bins": "*", }, }), ); + console.log(packageDir); - // Entries in this tarball: - // - // ./ - // ./package1000.js - // ./package2/ - // ./package3/ - // ./package4/ - // ./package.json - // ./package/ - // ./package1000/ - // ./package/index.js - // ./package4/package5/ - // ./package4/package.json - // ./package3/package6/ - // ./package3/package6/index.js - // ./package2/index.js - // package3/ - // package3/package6/ - // package3/package6/index.js - // - // The directory `package3` is added twice, but because one doesn't start - // with `./`, it is stripped from the path and a copy of `package6` is placed - // at the root of the output directory. Also `package1000` is not included in - // the output because it is an empty directory. + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--dev"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); - await runBunInstall(env, packageDir); - const prefix = join(packageDir, "node_modules", "tarball-without-package-prefix"); - const results = await Promise.all([ - file(join(prefix, "package.json")).json(), - file(join(prefix, "package1000.js")).text(), - file(join(prefix, "package", "index.js")).text(), - file(join(prefix, "package2", "index.js")).text(), - file(join(prefix, "package3", "package6", "index.js")).text(), - file(join(prefix, "package4", "package.json")).json(), - exists(join(prefix, "package4", "package5")), - exists(join(prefix, "package1000")), - file(join(prefix, "package6", "index.js")).text(), - ]); - expect(results).toEqual([ - { - name: "tarball-without-package-prefix", - version: "1.0.0", - }, - "hi", - "ooops", - "ooooops", - "oooooops", - { - "name": "tarball-without-package-prefix", - "version": "2.0.0", - }, - false, - false, - "oooooops", + var err = await new Response(stderr).text(); + var out = await new Response(stdout).text(); + console.log(err); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + expect(err).not.toContain("panic:"); + expect(err).not.toContain("not found"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + "+ bunx-bins@1.0.0", + "", + expect.stringContaining("1 package installed"), ]); - expect(await file(join(packageDir, "node_modules", "tarball-without-package-prefix", "package.json")).json()).toEqual( - { - name: "tarball-without-package-prefix", - version: "1.0.0", - }, - ); + expect(await exited).toBe(0); + + const temp_bin_dir = join(packageDir, "temp"); + mkdirSync(temp_bin_dir); + + for (let i = 1; i <= 7; i++) { + const target = join(temp_bin_dir, "a".repeat(i) + ".exe"); + copyFileSync(bunExe(), target); + } + + copyFileSync(join(packageDir, "node_modules\\bunx-bins\\native.exe"), join(temp_bin_dir, "native.exe")); + + const PATH = process.env.PATH + ";" + temp_bin_dir; + + const bins = [ + { bin: "bin1", name: "bin1" }, + { bin: "bin2", name: "bin2" }, + { bin: "bin3", name: "bin3" }, + { bin: "bin4", name: "bin4" }, + { bin: "bin5", name: "bin5" }, + { bin: "bin6", name: "bin6" }, + { bin: "bin7", name: "bin7" }, + { bin: "bin-node", name: "bin-node" }, + { bin: "bin-bun", name: "bin-bun" }, + { bin: "native", name: "exe" }, + { bin: "uses-native", name: `exe ${packageDir}\\node_modules\\bunx-bins\\uses-native.ts` }, + ]; + + for (const { bin, name } of bins) { + test(`bun run ${bin} arg1 arg2`, async () => { + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "run", bin, "arg1", "arg2"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env: mergeWindowEnvs([env, { PATH: PATH }]), + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err.trim()).toBe(""); + const out = await new Response(stdout).text(); + expect(out.trim()).toBe(`i am ${name} arg1 arg2`); + expect(await exited).toBe(0); + }); + } + + for (const { bin, name } of bins) { + test(`bun --bun run ${bin} arg1 arg2`, async () => { + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "--bun", "run", bin, "arg1", "arg2"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env: mergeWindowEnvs([env, { PATH: PATH }]), + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err.trim()).toBe(""); + const out = await new Response(stdout).text(); + expect(out.trim()).toBe(`i am ${name} arg1 arg2`); + expect(await exited).toBe(0); + }); + } + + for (const { bin, name } of bins) { + test(`bun --bun x ${bin} arg1 arg2`, async () => { + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "--bun", "x", bin, "arg1", "arg2"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env: mergeWindowEnvs([env, { PATH: PATH }]), + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err.trim()).toBe(""); + const out = await new Response(stdout).text(); + expect(out.trim()).toBe(`i am ${name} arg1 arg2`); + expect(await exited).toBe(0); + }); + } + + for (const { bin, name } of bins) { + test(`${bin} arg1 arg2`, async () => { + var { stdout, stderr, exited } = spawn({ + cmd: [join(packageDir, "node_modules", ".bin", bin + ".exe"), "arg1", "arg2"], + cwd: packageDir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env: mergeWindowEnvs([env, { PATH: PATH }]), + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err.trim()).toBe(""); + const out = await new Response(stdout).text(); + expect(out.trim()).toBe(`i am ${name} arg1 arg2`); + expect(await exited).toBe(0); + }); + } }); diff --git a/test/cli/install/registry/bun-install-windowsshim.test.ts b/test/cli/install/registry/bun-install-windowsshim.test.ts deleted file mode 100644 index b39fcdaa65700..0000000000000 --- a/test/cli/install/registry/bun-install-windowsshim.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { spawn } from "bun"; -import { bunExe, bunEnv as env, isWindows, mergeWindowEnvs, tmpdirSync } from "harness"; -import { join } from "path"; -import { copyFileSync, mkdirSync } from "fs"; -import { writeFile } from "fs/promises"; -import { test, expect, describe } from "bun:test"; - -// This test is to verify that BinLinkingShim.zig creates correct shim files as -// well as bun_shim_impl.exe works in various edge cases. There are many fast -// paths for many many cases. -describe("windows bin linking shim should work", async () => { - if (!isWindows) return; - - const packageDir = tmpdirSync(); - const port = 4873; - - await writeFile( - join(packageDir, "bunfig.toml"), - ` -[install] -cache = false -registry = "http://localhost:${port}/" -`, - ); - - await writeFile( - join(packageDir, "package.json"), - JSON.stringify({ - name: "foo", - version: "1.0.0", - dependencies: { - "bunx-bins": "*", - }, - }), - ); - console.log(packageDir); - - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install", "--dev"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, - }); - - var err = await new Response(stderr).text(); - var out = await new Response(stdout).text(); - console.log(err); - expect(err).toContain("Saved lockfile"); - expect(err).not.toContain("error:"); - expect(err).not.toContain("panic:"); - expect(err).not.toContain("not found"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - "+ bunx-bins@1.0.0", - "", - expect.stringContaining("1 package installed"), - ]); - expect(await exited).toBe(0); - - const temp_bin_dir = join(packageDir, "temp"); - mkdirSync(temp_bin_dir); - - for (let i = 1; i <= 7; i++) { - const target = join(temp_bin_dir, "a".repeat(i) + ".exe"); - copyFileSync(bunExe(), target); - } - - copyFileSync(join(packageDir, "node_modules\\bunx-bins\\native.exe"), join(temp_bin_dir, "native.exe")); - - const PATH = process.env.PATH + ";" + temp_bin_dir; - - const bins = [ - { bin: "bin1", name: "bin1" }, - { bin: "bin2", name: "bin2" }, - { bin: "bin3", name: "bin3" }, - { bin: "bin4", name: "bin4" }, - { bin: "bin5", name: "bin5" }, - { bin: "bin6", name: "bin6" }, - { bin: "bin7", name: "bin7" }, - { bin: "bin-node", name: "bin-node" }, - { bin: "bin-bun", name: "bin-bun" }, - { bin: "native", name: "exe" }, - { bin: "uses-native", name: `exe ${packageDir}\\node_modules\\bunx-bins\\uses-native.ts` }, - ]; - - for (const { bin, name } of bins) { - test(`bun run ${bin} arg1 arg2`, async () => { - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "run", bin, "arg1", "arg2"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env: mergeWindowEnvs([env, { PATH: PATH }]), - }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err.trim()).toBe(""); - const out = await new Response(stdout).text(); - expect(out.trim()).toBe(`i am ${name} arg1 arg2`); - expect(await exited).toBe(0); - }); - } - - for (const { bin, name } of bins) { - test(`bun --bun run ${bin} arg1 arg2`, async () => { - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "--bun", "run", bin, "arg1", "arg2"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env: mergeWindowEnvs([env, { PATH: PATH }]), - }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err.trim()).toBe(""); - const out = await new Response(stdout).text(); - expect(out.trim()).toBe(`i am ${name} arg1 arg2`); - expect(await exited).toBe(0); - }); - } - - for (const { bin, name } of bins) { - test(`bun --bun x ${bin} arg1 arg2`, async () => { - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "--bun", "x", bin, "arg1", "arg2"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env: mergeWindowEnvs([env, { PATH: PATH }]), - }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err.trim()).toBe(""); - const out = await new Response(stdout).text(); - expect(out.trim()).toBe(`i am ${name} arg1 arg2`); - expect(await exited).toBe(0); - }); - } - - for (const { bin, name } of bins) { - test(`${bin} arg1 arg2`, async () => { - var { stdout, stderr, exited } = spawn({ - cmd: [join(packageDir, "node_modules", ".bin", bin + ".exe"), "arg1", "arg2"], - cwd: packageDir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env: mergeWindowEnvs([env, { PATH: PATH }]), - }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err.trim()).toBe(""); - const out = await new Response(stdout).text(); - expect(out.trim()).toBe(`i am ${name} arg1 arg2`); - expect(await exited).toBe(0); - }); - } -}); diff --git a/test/cli/install/registry/missing-directory-bin-1.1.1.tgz b/test/cli/install/registry/missing-directory-bin-1.1.1.tgz new file mode 100644 index 0000000000000..beb806ab3a247 Binary files /dev/null and b/test/cli/install/registry/missing-directory-bin-1.1.1.tgz differ diff --git a/test/cli/install/registry/packages/@needs-auth/test-pkg/package.json b/test/cli/install/registry/packages/@needs-auth/test-pkg/package.json new file mode 100644 index 0000000000000..ba5f8449f2ed7 --- /dev/null +++ b/test/cli/install/registry/packages/@needs-auth/test-pkg/package.json @@ -0,0 +1,43 @@ +{ + "name": "@needs-auth/test-pkg", + "versions": { + "1.0.0": { + "name": "@needs-auth/test-pkg", + "main": "index.js", + "version": "1.0.0", + "dependencies": {}, + "publishConfig": { + "registry": "http://localhost:22115" + }, + "_id": "@needs-auth/test-pkg@1.0.0", + "_nodeVersion": "18.14.0", + "_npmVersion": "10.8.1", + "dist": { + "integrity": "sha512-7mQh/xL1CkoLei0Bc+sf0BzavEuJsY0dQxk3Zw3mBiuv3si91oOsE2Nz1SnUXpu1SHvqoqjMxwhhQEM9fpoHTQ==", + "shasum": "f7db88f438f712bd66b990e617f3a3fd5d396f17", + "tarball": "http://localhost:22115/@needs-auth/test-pkg/-/@needs-auth/test-pkg-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-07-06T01:49:26.660Z", + "created": "2024-07-06T01:49:26.660Z", + "1.0.0": "2024-07-06T01:49:26.660Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "test-pkg-1.0.0.tgz": { + "shasum": "f7db88f438f712bd66b990e617f3a3fd5d396f17", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "@needs-auth/test-pkg", + "readme": "ERROR: No README data found!" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/@needs-auth/test-pkg/test-pkg-1.0.0.tgz b/test/cli/install/registry/packages/@needs-auth/test-pkg/test-pkg-1.0.0.tgz new file mode 100644 index 0000000000000..27bde63d12c8f Binary files /dev/null and b/test/cli/install/registry/packages/@needs-auth/test-pkg/test-pkg-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/dep-with-directory-bins/dep-with-directory-bins-1.0.0.tgz b/test/cli/install/registry/packages/dep-with-directory-bins/dep-with-directory-bins-1.0.0.tgz new file mode 100644 index 0000000000000..435c6a6d35df6 Binary files /dev/null and b/test/cli/install/registry/packages/dep-with-directory-bins/dep-with-directory-bins-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/dep-with-directory-bins/package.json b/test/cli/install/registry/packages/dep-with-directory-bins/package.json new file mode 100644 index 0000000000000..f5c3df9439dcd --- /dev/null +++ b/test/cli/install/registry/packages/dep-with-directory-bins/package.json @@ -0,0 +1,41 @@ +{ + "name": "dep-with-directory-bins", + "versions": { + "1.0.0": { + "name": "dep-with-directory-bins", + "version": "1.0.0", + "directories": { + "bin": "./bins" + }, + "_id": "dep-with-directory-bins@1.0.0", + "_nodeVersion": "22.3.0", + "_npmVersion": "10.8.1", + "dist": { + "integrity": "sha512-uZLlUwT2HiX/YMXf/60hfKZ8VDaju26kUyiXohg6/PlevgYR0cLVd3a459tBPHip3TjX620TLW7kbBM306vJKQ==", + "shasum": "e15b26ce0ba04b009b5ebbea9476abe212756cc8", + "tarball": "http://localhost:4873/dep-with-directory-bins/-/dep-with-directory-bins-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-06-24T23:12:27.191Z", + "created": "2024-06-24T23:12:27.191Z", + "1.0.0": "2024-06-24T23:12:27.191Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "dep-with-directory-bins-1.0.0.tgz": { + "shasum": "e15b26ce0ba04b009b5ebbea9476abe212756cc8", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "dep-with-directory-bins", + "readme": "ERROR: No README data found!" +} diff --git a/test/cli/install/registry/packages/dep-with-file-bin/dep-with-file-bin-1.0.0.tgz b/test/cli/install/registry/packages/dep-with-file-bin/dep-with-file-bin-1.0.0.tgz new file mode 100644 index 0000000000000..e0e235bb77923 Binary files /dev/null and b/test/cli/install/registry/packages/dep-with-file-bin/dep-with-file-bin-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/dep-with-file-bin/package.json b/test/cli/install/registry/packages/dep-with-file-bin/package.json new file mode 100644 index 0000000000000..d5980f4e2c0fc --- /dev/null +++ b/test/cli/install/registry/packages/dep-with-file-bin/package.json @@ -0,0 +1,39 @@ +{ + "name": "dep-with-file-bin", + "versions": { + "1.0.0": { + "name": "dep-with-file-bin", + "version": "1.0.0", + "bin": "file-bin", + "_id": "dep-with-file-bin@1.0.0", + "_nodeVersion": "22.3.0", + "_npmVersion": "10.8.1", + "dist": { + "integrity": "sha512-0Wxd9twd0OSlNp7CaZUyz1Fv9utR/q/4BpxEl5VhBCK2yBHM8N0Kqvx4xQWL1DFu59WnvMnVLuQpUmmBivIHvQ==", + "shasum": "db0849d5b69675b3baafa7488a6b4bab06f9cd3d", + "tarball": "http://localhost:4873/dep-with-file-bin/-/dep-with-file-bin-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-06-24T23:02:42.992Z", + "created": "2024-06-24T23:02:42.992Z", + "1.0.0": "2024-06-24T23:02:42.992Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "dep-with-file-bin-1.0.0.tgz": { + "shasum": "db0849d5b69675b3baafa7488a6b4bab06f9cd3d", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "dep-with-file-bin", + "readme": "ERROR: No README data found!" +} diff --git a/test/cli/install/registry/packages/dep-with-map-bins/dep-with-map-bins-1.0.0.tgz b/test/cli/install/registry/packages/dep-with-map-bins/dep-with-map-bins-1.0.0.tgz new file mode 100644 index 0000000000000..c9624e2debc3b Binary files /dev/null and b/test/cli/install/registry/packages/dep-with-map-bins/dep-with-map-bins-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/dep-with-map-bins/package.json b/test/cli/install/registry/packages/dep-with-map-bins/package.json new file mode 100644 index 0000000000000..af813eb92526e --- /dev/null +++ b/test/cli/install/registry/packages/dep-with-map-bins/package.json @@ -0,0 +1,42 @@ +{ + "name": "dep-with-map-bins", + "versions": { + "1.0.0": { + "name": "dep-with-map-bins", + "version": "1.0.0", + "bin": { + "map-bin-1": "map-bin-1", + "map-bin-2": "map-bin-2" + }, + "_id": "dep-with-map-bins@1.0.0", + "_nodeVersion": "22.3.0", + "_npmVersion": "10.8.1", + "dist": { + "integrity": "sha512-nUqv0LNrwcQu6Lr4VGaYof8AaVLLbQV+wYXu7MkaYPUlTq4YoxhULmnXHzJvJ/WoaTtmAocJ6LNKPxL/WwlX9g==", + "shasum": "6a619a186f7782d7e2e2c2750ce7c72aa45887c7", + "tarball": "http://localhost:4873/dep-with-map-bins/-/dep-with-map-bins-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-06-24T23:27:57.311Z", + "created": "2024-06-24T23:27:57.311Z", + "1.0.0": "2024-06-24T23:27:57.311Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "dep-with-map-bins-1.0.0.tgz": { + "shasum": "6a619a186f7782d7e2e2c2750ce7c72aa45887c7", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "dep-with-map-bins", + "readme": "ERROR: No README data found!" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/dep-with-single-entry-map-bin/dep-with-single-entry-map-bin-1.0.0.tgz b/test/cli/install/registry/packages/dep-with-single-entry-map-bin/dep-with-single-entry-map-bin-1.0.0.tgz new file mode 100644 index 0000000000000..4f2bd4e101379 Binary files /dev/null and b/test/cli/install/registry/packages/dep-with-single-entry-map-bin/dep-with-single-entry-map-bin-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/dep-with-single-entry-map-bin/package.json b/test/cli/install/registry/packages/dep-with-single-entry-map-bin/package.json new file mode 100644 index 0000000000000..bd6096694c6ae --- /dev/null +++ b/test/cli/install/registry/packages/dep-with-single-entry-map-bin/package.json @@ -0,0 +1,41 @@ +{ + "name": "dep-with-single-entry-map-bin", + "versions": { + "1.0.0": { + "name": "dep-with-single-entry-map-bin", + "version": "1.0.0", + "bin": { + "single-entry-map-bin": "single-entry-map-bin" + }, + "_id": "dep-with-single-entry-map-bin@1.0.0", + "_nodeVersion": "22.3.0", + "_npmVersion": "10.8.1", + "dist": { + "integrity": "sha512-9hGEhDEiFwhCEE3rWRcRdJhYCSEL60JnDYbQq69OpQxcrIbPOrry0hEtie8oLWjgSdEtdRGqf3gGvHlWzF7Kjw==", + "shasum": "3e8beef599138fdeda8397dc4c12f4e8673223ca", + "tarball": "http://localhost:4873/dep-with-single-entry-map-bin/-/dep-with-single-entry-map-bin-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-06-24T23:10:55.988Z", + "created": "2024-06-24T23:10:55.988Z", + "1.0.0": "2024-06-24T23:10:55.988Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "dep-with-single-entry-map-bin-1.0.0.tgz": { + "shasum": "3e8beef599138fdeda8397dc4c12f4e8673223ca", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "dep-with-single-entry-map-bin", + "readme": "ERROR: No README data found!" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/optional-peer-deps/optional-peer-deps-1.0.1.tgz b/test/cli/install/registry/packages/optional-peer-deps/optional-peer-deps-1.0.1.tgz new file mode 100644 index 0000000000000..0ef3b402b5aa0 Binary files /dev/null and b/test/cli/install/registry/packages/optional-peer-deps/optional-peer-deps-1.0.1.tgz differ diff --git a/test/cli/install/registry/packages/optional-peer-deps/package.json b/test/cli/install/registry/packages/optional-peer-deps/package.json index 7a4310d5269dd..ff35f997a1a58 100644 --- a/test/cli/install/registry/packages/optional-peer-deps/package.json +++ b/test/cli/install/registry/packages/optional-peer-deps/package.json @@ -22,16 +22,38 @@ "tarball": "http://localhost:4873/optional-peer-deps/-/optional-peer-deps-1.0.0.tgz" }, "contributors": [] + }, + "1.0.1": { + "name": "optional-peer-deps", + "version": "1.0.1", + "peerDependencies": { + "no-deps": "*" + }, + "peerDependenciesMeta": { + "no-deps": { + "optional": true + } + }, + "_id": "optional-peer-deps@1.0.1", + "_nodeVersion": "22.3.0", + "_npmVersion": "10.8.1", + "dist": { + "integrity": "sha512-uOXnoNBSmmFwb8Jyesz6PgmhBKNi7tWRZucHUzqKso7zWVieVD99RM5UwzW2ar45KE9FBi7vtMA2s/MA+wXnlg==", + "shasum": "391af8b02376645200bb74ad7847bf74ded79a24", + "tarball": "http://localhost:4873/optional-peer-deps/-/optional-peer-deps-1.0.1.tgz" + }, + "contributors": [] } }, "time": { - "modified": "2023-11-01T22:05:27.238Z", + "modified": "2024-07-09T08:11:22.701Z", "created": "2023-11-01T22:05:27.238Z", - "1.0.0": "2023-11-01T22:05:27.238Z" + "1.0.0": "2023-11-01T22:05:27.238Z", + "1.0.1": "2024-07-09T08:11:22.701Z" }, "users": {}, "dist-tags": { - "latest": "1.0.0" + "latest": "1.0.1" }, "_uplinks": {}, "_distfiles": {}, @@ -39,6 +61,10 @@ "optional-peer-deps-1.0.0.tgz": { "shasum": "4a2be080620986b309ae061903aaa68edf73070b", "version": "1.0.0" + }, + "optional-peer-deps-1.0.1.tgz": { + "shasum": "391af8b02376645200bb74ad7847bf74ded79a24", + "version": "1.0.1" } }, "_rev": "3-62c621d1e44acf6e", diff --git a/test/cli/install/registry/packages/uses-what-bin-slow-window/package.json b/test/cli/install/registry/packages/uses-what-bin-slow-window/package.json index b4f500bcb2fb5..6b01c324c6d13 100644 --- a/test/cli/install/registry/packages/uses-what-bin-slow-window/package.json +++ b/test/cli/install/registry/packages/uses-what-bin-slow-window/package.json @@ -5,26 +5,27 @@ "name": "uses-what-bin-slow-window", "version": "1.0.0", "scripts": { - "install": "sleep 5 && what-bin" + "install": "bun sleep.js && what-bin" }, "dependencies": { "what-bin": "1.0.0" }, "_id": "uses-what-bin-slow-window@1.0.0", - "_nodeVersion": "21.7.1", - "_npmVersion": "10.5.0", + "gitHead": "9207d4123bb4116b8e19153b38fb9f2b58032ee4", + "_nodeVersion": "22.7.0", + "_npmVersion": "10.8.3", "dist": { - "integrity": "sha512-SVknEB1K9N8f3WldcidJMAwpIGkwdVJ3gwF/7lrqtToWdVs4l7g+6PoQk3VoCbyN6csPHVhr4m8oV/MwYZDiLw==", - "shasum": "898c8ed1ec91694ebeb9dc4ea4a4b2fbdad2f6d4", + "integrity": "sha512-u7EgbLjHj4a4EzZM3ed3aVBGKJBqhZ8yXdbOB2QsM4MfoLhjR/lxFBWbuca5kraC0PdtfpvVzhkT921UWPsauw==", + "shasum": "de6e837e6ca15e2400ebd2ce069dc384a1444ad8", "tarball": "http://localhost:4873/uses-what-bin-slow-window/-/uses-what-bin-slow-window-1.0.0.tgz" }, "contributors": [] } }, "time": { - "modified": "2024-04-25T06:58:20.908Z", - "created": "2024-04-25T06:58:20.908Z", - "1.0.0": "2024-04-25T06:58:20.908Z" + "modified": "2024-09-11T04:59:52.705Z", + "created": "2024-09-11T04:59:52.705Z", + "1.0.0": "2024-09-11T04:59:52.705Z" }, "users": {}, "dist-tags": { @@ -34,7 +35,7 @@ "_distfiles": {}, "_attachments": { "uses-what-bin-slow-window-1.0.0.tgz": { - "shasum": "898c8ed1ec91694ebeb9dc4ea4a4b2fbdad2f6d4", + "shasum": "de6e837e6ca15e2400ebd2ce069dc384a1444ad8", "version": "1.0.0" } }, diff --git a/test/cli/install/registry/packages/uses-what-bin-slow-window/uses-what-bin-slow-window-1.0.0.tgz b/test/cli/install/registry/packages/uses-what-bin-slow-window/uses-what-bin-slow-window-1.0.0.tgz index 3ca91cd81a7f6..a6569f689407e 100644 Binary files a/test/cli/install/registry/packages/uses-what-bin-slow-window/uses-what-bin-slow-window-1.0.0.tgz and b/test/cli/install/registry/packages/uses-what-bin-slow-window/uses-what-bin-slow-window-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/uses-what-bin-slow/package.json b/test/cli/install/registry/packages/uses-what-bin-slow/package.json index 4988307337fea..1dfe5707afd1a 100644 --- a/test/cli/install/registry/packages/uses-what-bin-slow/package.json +++ b/test/cli/install/registry/packages/uses-what-bin-slow/package.json @@ -5,26 +5,27 @@ "name": "uses-what-bin-slow", "version": "1.0.0", "scripts": { - "install": "sleep 1 && what-bin" + "install": "bun sleep.js && what-bin" }, "dependencies": { "what-bin": "1.0.0" }, "_id": "uses-what-bin-slow@1.0.0", - "_nodeVersion": "21.1.0", - "_npmVersion": "10.2.0", + "gitHead": "7e705b9d40fead6796575fd0df0d1ccfa124e95c", + "_nodeVersion": "22.7.0", + "_npmVersion": "10.8.3", "dist": { - "integrity": "sha512-/l5wILffL/epzl68C0NJPAxpTAd4P0Jyu911I2oI2XTNy8GzPdfHTNQ18GddYovPjaL+bQhopfgkmiHfF9/n2Q==", - "shasum": "af87d384ce8a007905c42d87989fd3ccd8fa9d6b", + "integrity": "sha512-sCuZz2/akHPvO9qh5IfQCIjAzcm0+WV0Wq9/IJUREgsMyf1+DcjDCwy6zq3tQuXJizLV0IkrPuFHEhzxxqJ1nA==", + "shasum": "16dbcb32b2add7bd26a724764cb58fd5c4e46e97", "tarball": "http://localhost:4873/uses-what-bin-slow/-/uses-what-bin-slow-1.0.0.tgz" }, "contributors": [] } }, "time": { - "modified": "2023-11-21T23:39:12.762Z", - "created": "2023-11-21T23:39:12.762Z", - "1.0.0": "2023-11-21T23:39:12.762Z" + "modified": "2024-09-11T04:55:31.365Z", + "created": "2024-09-11T04:55:31.365Z", + "1.0.0": "2024-09-11T04:55:31.365Z" }, "users": {}, "dist-tags": { @@ -34,7 +35,7 @@ "_distfiles": {}, "_attachments": { "uses-what-bin-slow-1.0.0.tgz": { - "shasum": "af87d384ce8a007905c42d87989fd3ccd8fa9d6b", + "shasum": "16dbcb32b2add7bd26a724764cb58fd5c4e46e97", "version": "1.0.0" } }, diff --git a/test/cli/install/registry/packages/uses-what-bin-slow/uses-what-bin-slow-1.0.0.tgz b/test/cli/install/registry/packages/uses-what-bin-slow/uses-what-bin-slow-1.0.0.tgz index 7b18d0984980f..d3b4080806773 100644 Binary files a/test/cli/install/registry/packages/uses-what-bin-slow/uses-what-bin-slow-1.0.0.tgz and b/test/cli/install/registry/packages/uses-what-bin-slow/uses-what-bin-slow-1.0.0.tgz differ diff --git a/test/cli/install/registry/verdaccio.yaml b/test/cli/install/registry/verdaccio.yaml index e0cad0015ae57..e46176c27ad2e 100644 --- a/test/cli/install/registry/verdaccio.yaml +++ b/test/cli/install/registry/verdaccio.yaml @@ -49,9 +49,9 @@ web: # publicPath: http://somedomain.org/ # https://verdaccio.org/docs/configuration#authentication -# auth: -# htpasswd: -# file: ./htpasswd +auth: + htpasswd: + file: ./htpasswd # Maximum amount of users allowed to register, defaults to "+inf". # You can set this to -1 to disable registration. # max_users: 1000 @@ -70,6 +70,11 @@ uplinks: # https://verdaccio.org/docs/protect-your-dependencies/ # https://verdaccio.org/docs/configuration#packages packages: + "@needs-auth/*": + access: $authenticated + publish: $authenticated + unpublish: $authenticated + "@*/*": # scoped packages access: $all @@ -87,7 +92,7 @@ packages: # allow all known users to publish/publish packages # (anyone can register by default, remember?) - publish: $all + publish: $authenticated unpublish: $all # if package is not available locally, proxy requests to 'npmjs' registry diff --git a/test/cli/run/as-node.test.ts b/test/cli/run/as-node.test.ts index 9881fed9c5cbb..871a1809d2fbc 100644 --- a/test/cli/run/as-node.test.ts +++ b/test/cli/run/as-node.test.ts @@ -1,6 +1,6 @@ -import { describe, test, expect } from "bun:test"; -import { bunExe, fakeNodeRun, tempDirWithFiles } from "../../harness"; +import { describe, expect, test } from "bun:test"; import { join } from "path"; +import { fakeNodeRun, tempDirWithFiles } from "../../harness"; describe("fake node cli", () => { test("the node cli actually works", () => { diff --git a/test/cli/run/cjs-defineProperty-arraylike.cjs b/test/cli/run/cjs-defineProperty-arraylike.cjs new file mode 100644 index 0000000000000..b9da6b5ae7c5c --- /dev/null +++ b/test/cli/run/cjs-defineProperty-arraylike.cjs @@ -0,0 +1,19 @@ +exports[0] = 0; +Object.defineProperty(exports, "1", { + value: 1, + enumerable: true, +}); +Object.defineProperty(exports, "2", { + get: () => { + return 3; + }, + enumerable: true, +}); + +exports[3] = 4; +Object.defineProperty(exports, "4", { + get() { + throw new Error("4"); + }, + enumerable: true, +}); diff --git a/test/cli/run/cjs-defineProperty-fixture.cjs b/test/cli/run/cjs-defineProperty-fixture.cjs new file mode 100644 index 0000000000000..b34639ea20096 --- /dev/null +++ b/test/cli/run/cjs-defineProperty-fixture.cjs @@ -0,0 +1,13 @@ +Object.defineProperty(exports, "a", { + value: 1, + enumerable: false, +}); +exports.b = 2; +let fn = function () { + return 3; +}; +// Node doesn't support non-enumerable getters/setters +Object.defineProperty(exports, "c", { + get: fn, + enumerable: false, +}); diff --git a/test/cli/run/commonjs-no-export.test.ts b/test/cli/run/commonjs-no-export.test.ts index b0bedd9d631b7..39b7e82781936 100644 --- a/test/cli/run/commonjs-no-export.test.ts +++ b/test/cli/run/commonjs-no-export.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; import { join } from "path"; diff --git a/test/cli/run/empty-file.test.ts b/test/cli/run/empty-file.test.ts index f29162bf7ad7f..e9f29757520f1 100644 --- a/test/cli/run/empty-file.test.ts +++ b/test/cli/run/empty-file.test.ts @@ -1,4 +1,4 @@ -import { it, expect } from "bun:test"; +import { expect, it } from "bun:test"; import { bunEnv, bunExe } from "harness"; import { join } from "path"; diff --git a/test/cli/run/env.test.ts b/test/cli/run/env.test.ts index 2837bfa5f8912..18dd4122ffb6f 100644 --- a/test/cli/run/env.test.ts +++ b/test/cli/run/env.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, test, beforeAll, afterAll } from "bun:test"; -import { bunRun, bunRunAsScript, bunTest, tempDirWithFiles, bunExe, bunEnv, isWindows } from "harness"; +import { beforeAll, describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, bunRun, bunRunAsScript, bunTest, isWindows, tempDirWithFiles } from "harness"; import path from "path"; function bunRunWithoutTrim(file: string, env?: Record) { diff --git a/test/cli/run/esm-defineProperty.test.ts b/test/cli/run/esm-defineProperty.test.ts new file mode 100644 index 0000000000000..d6289b49cc59b --- /dev/null +++ b/test/cli/run/esm-defineProperty.test.ts @@ -0,0 +1,36 @@ +import { expect, test } from "bun:test"; +import * as CJSArrayLike from "./cjs-defineProperty-arraylike.cjs"; +import * as CJS from "./cjs-defineProperty-fixture.cjs"; +// https://github.com/oven-sh/bun/issues/4432 +test("defineProperty", () => { + expect(CJS.a).toBe(1); + expect(CJS.b).toBe(2); + // non-enumerable getter/setter are not copied, matching node.js + expect(CJS.c).toBe(undefined); + + expect(Bun.inspect(CJS.default)).toBe(`{\n a: 1,\n b: 2,\n c: [Getter],\n}`); +}); + +test("arraylike", () => { + console.log(globalThis); + expect(CJSArrayLike[0]).toBe(0); + expect(CJSArrayLike[1]).toBe(1); + expect(CJSArrayLike[2]).toBe(3); + expect(CJSArrayLike[3]).toBe(4); + expect(CJSArrayLike[4]).toBe(undefined); + expect(CJSArrayLike).toHaveProperty("4"); + expect(Bun.inspect(CJSArrayLike)).toBe(`Module { + "0": 0, + "1": 1, + "2": 3, + "3": 4, + "4": undefined, + default: { + "0": 0, + "1": 1, + "2": [Getter], + "3": 4, + "4": [Getter], + }, +}`); +}); diff --git a/test/cli/run/filter-workspace.test.ts b/test/cli/run/filter-workspace.test.ts index 0ba2956191bbd..a6c402c8a69a4 100644 --- a/test/cli/run/filter-workspace.test.ts +++ b/test/cli/run/filter-workspace.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect, beforeAll } from "bun:test"; import { spawnSync } from "bun"; +import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; import { join } from "path"; diff --git a/test/cli/run/fixture-crash.js b/test/cli/run/fixture-crash.js index 9a56452ac7022..c90049da5d2db 100644 --- a/test/cli/run/fixture-crash.js +++ b/test/cli/run/fixture-crash.js @@ -11,5 +11,5 @@ const approach = process.argv[2]; if (approach in crash_handler) { crash_handler[approach](); } else { - console.error("usage: bun fixture-crash.js "); + console.error("usage: bun fixture-crash.js "); } diff --git a/test/cli/run/fixture-tty.js b/test/cli/run/fixture-tty.js index 1404b992128fe..fd9ca62a54c11 100644 --- a/test/cli/run/fixture-tty.js +++ b/test/cli/run/fixture-tty.js @@ -1,5 +1,5 @@ const onlyCheck = process.env.ONLY_CHECK_TTY === "0"; -import { dlopen, ptr } from "bun:ffi"; +import { dlopen } from "bun:ffi"; const suffix = process.platform === "darwin" ? "dylib" : "so.6"; const { tcgetattr, tcsetattr } = dlopen(`libc.${suffix}`, { diff --git a/test/cli/run/if-present.test.ts b/test/cli/run/if-present.test.ts index 96e587dc6d29b..824ee4a2398f8 100644 --- a/test/cli/run/if-present.test.ts +++ b/test/cli/run/if-present.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect, beforeAll } from "bun:test"; import { spawnSync } from "bun"; +import { beforeAll, describe, expect, test } from "bun:test"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; let cwd: string; diff --git a/test/cli/run/log-test.test.ts b/test/cli/run/log-test.test.ts index eb15dd59291d0..dd684b8e28e91 100644 --- a/test/cli/run/log-test.test.ts +++ b/test/cli/run/log-test.test.ts @@ -1,8 +1,8 @@ -import { it, expect } from "bun:test"; -import { basename, dirname, join } from "path"; +import { spawnSync } from "bun"; +import { expect, it } from "bun:test"; import * as fs from "fs"; -import { readableStreamToText, spawnSync } from "bun"; -import { bunExe, bunEnv } from "harness"; +import { bunEnv, bunExe } from "harness"; +import { dirname, join } from "path"; it("should not log .env when quiet", async () => { writeDirectoryTree("/tmp/log-test-silent", { diff --git a/test/cli/run/preload-test.test.js b/test/cli/run/preload-test.test.js index 3fa73956c9f7d..72f9ddd9538a4 100644 --- a/test/cli/run/preload-test.test.js +++ b/test/cli/run/preload-test.test.js @@ -1,9 +1,9 @@ import { spawnSync } from "bun"; import { describe, expect, test } from "bun:test"; import { mkdirSync, realpathSync } from "fs"; +import { bunEnv, bunExe } from "harness"; import { tmpdir } from "os"; import { join } from "path"; -import { bunEnv, bunExe } from "harness"; const preloadModule = ` import {plugin} from 'bun'; diff --git a/test/cli/run/require-and-import-trailing.test.ts b/test/cli/run/require-and-import-trailing.test.ts index 847756caccc6e..8b592ee7b14d3 100644 --- a/test/cli/run/require-and-import-trailing.test.ts +++ b/test/cli/run/require-and-import-trailing.test.ts @@ -1,6 +1,5 @@ -import { test, expect, describe } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDirWithFiles } from "harness"; -import { join } from "path"; +import { expect, test } from "bun:test"; +import { tempDirWithFiles } from "harness"; test("require() with trailing slash", () => { const requireDir = tempDirWithFiles("require-trailing", { diff --git a/test/cli/run/require-cache.test.ts b/test/cli/run/require-cache.test.ts index 1d79577c7bd71..9de45782b9cd1 100644 --- a/test/cli/run/require-cache.test.ts +++ b/test/cli/run/require-cache.test.ts @@ -1,5 +1,5 @@ -import { test, expect, describe } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, isWindows, tempDirWithFiles } from "harness"; import { join } from "path"; // This also tests __dirname and __filename @@ -26,6 +26,227 @@ test("require.cache does not include unevaluated modules", () => { expect(exitCode).toBe(0); }); +describe("files transpiled and loaded don't leak the output source code", () => { + test("via require() with a lot of long export names", () => { + let text = ""; + for (let i = 0; i < 10000; i++) { + text += `exports.superDuperExtraCrazyLongNameWowSuchNameLongYouveNeverSeenANameThisLongForACommonJSModuleExport${i} = 1;\n`; + } + + console.log("Text length:", text.length); + + const dir = tempDirWithFiles("require-cache-bug-leak-1", { + "index.js": text, + "require-cache-bug-leak-fixture.js": ` + const path = require.resolve("./index.js"); + const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + function bust() { + const mod = require.cache[path]; + if (mod) { + mod.parent = null; + mod.children = []; + delete require.cache[path]; + } + } + + for (let i = 0; i < 50; i++) { + require(path); + bust(); + } + gc(true); + const baseline = process.memoryUsage.rss(); + for (let i = 0; i < 500; i++) { + require(path); + bust(path); + console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB"); + } + gc(true); + const rss = process.memoryUsage.rss(); + const diff = rss - baseline; + console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); + console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); + if (diff > 100 * 1024 * 1024) { + // Bun v1.1.21 reported 844 MB here on macoS arm64. + throw new Error("Memory leak detected"); + } + + exports.abc = 123; + `, + }); + console.log({ dir }); + const { exitCode, resourceUsage } = Bun.spawnSync({ + cmd: [bunExe(), "run", "--smol", join(dir, "require-cache-bug-leak-fixture.js")], + env: bunEnv, + stdio: ["inherit", "inherit", "inherit"], + }); + + console.log(resourceUsage); + expect(exitCode).toBe(0); + }, 60000); + + test("via await import() with a lot of function calls", () => { + let text = "function i() { return 1; }\n"; + for (let i = 0; i < 20000; i++) { + text += `i();\n`; + } + text += "exports.forceCommonJS = true;\n"; + + console.log("Text length:", text.length); + + const dir = tempDirWithFiles("require-cache-bug-leak-3", { + "index.js": text, + "require-cache-bug-leak-fixture.js": ` + const path = require.resolve("./index.js"); + const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + function bust() { + delete require.cache[path]; + } + + for (let i = 0; i < 100; i++) { + await import(path); + bust(); + } + gc(true); + const baseline = process.memoryUsage.rss(); + for (let i = 0; i < 400; i++) { + await import(path); + bust(path); + console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB"); + } + gc(true); + const rss = process.memoryUsage.rss(); + const diff = rss - baseline; + console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); + console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); + if (diff > 64 * 1024 * 1024) { + // Bun v1.1.22 reported 1 MB here on macoS arm64. + // Bun v1.1.21 reported 257 MB here on macoS arm64. + throw new Error("Memory leak detected"); + } + + export default 123; + `, + }); + const { exitCode, resourceUsage } = Bun.spawnSync({ + cmd: [bunExe(), "run", "--smol", join(dir, "require-cache-bug-leak-fixture.js")], + env: bunEnv, + stdio: ["inherit", "inherit", "inherit"], + }); + + console.log(resourceUsage); + expect(exitCode).toBe(0); + }, 60000); // takes 4s on an M1 in release build + + test("via import() with a lot of long export names", () => { + let text = ""; + for (let i = 0; i < 10000; i++) { + text += `export const superDuperExtraCrazyLongNameWowSuchNameLongYouveNeverSeenANameThisLongForACommonJSModuleExport${i} = 1;\n`; + } + + const dir = tempDirWithFiles("require-cache-bug-leak-4", { + "index.js": text, + "require-cache-bug-leak-fixture.js": ` + const path = require.resolve("./index.js"); + const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + function bust() { + delete require.cache[path]; + } + + for (let i = 0; i < 50; i++) { + await import(path); + bust(); + } + gc(true); + const baseline = process.memoryUsage.rss(); + for (let i = 0; i < 250; i++) { + await import(path); + bust(path); + console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB"); + } + gc(true); + const rss = process.memoryUsage.rss(); + const diff = rss - baseline; + console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); + console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); + if (diff > 64 * 1024 * 1024) { + // Bun v1.1.21 reported 423 MB here on macoS arm64. + // Bun v1.1.22 reported 4 MB here on macoS arm64. + throw new Error("Memory leak detected"); + } + + export default 124; + `, + }); + console.log({ dir }); + const { exitCode, resourceUsage } = Bun.spawnSync({ + cmd: [bunExe(), "run", "--smol", join(dir, "require-cache-bug-leak-fixture.js")], + env: bunEnv, + stdio: ["inherit", "inherit", "inherit"], + }); + + console.log(resourceUsage); + expect(exitCode).toBe(0); + }, 60000); + + test("via require() with a lot of function calls", () => { + let text = "function i() { return 1; }\n"; + for (let i = 0; i < 20000; i++) { + text += `i();\n`; + } + text += "exports.forceCommonJS = true;\n"; + + console.log("Text length:", text.length); + + const dir = tempDirWithFiles("require-cache-bug-leak-2", { + "index.js": text, + "require-cache-bug-leak-fixture.js": ` + const path = require.resolve("./index.js"); + const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + function bust() { + const mod = require.cache[path]; + if (mod) { + mod.parent = null; + mod.children = []; + delete require.cache[path]; + } + } + + for (let i = 0; i < 100; i++) { + require(path); + bust(); + } + gc(true); + const baseline = process.memoryUsage.rss(); + for (let i = 0; i < 400; i++) { + require(path); + bust(path); + console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB"); + } + gc(true); + const rss = process.memoryUsage.rss(); + const diff = rss - baseline; + console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); + console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); + if (diff > 64 * 1024 * 1024) { + // Bun v1.1.22 reported 4 MB here on macoS arm64. + // Bun v1.1.21 reported 248 MB here on macoS arm64. + throw new Error("Memory leak detected"); + } + + exports.abc = 123; + `, + }); + const { exitCode, resourceUsage } = Bun.spawnSync({ + cmd: [bunExe(), "run", "--smol", join(dir, "require-cache-bug-leak-fixture.js")], + env: bunEnv, + stdio: ["inherit", "inherit", "inherit"], + }); + + console.log(resourceUsage); + expect(exitCode).toBe(0); + }, 60000); // takes 4s on an M1 in release build +}); + describe("files transpiled and loaded don't leak the AST", () => { test("via require()", () => { const { stdout, exitCode } = Bun.spawnSync({ diff --git a/test/cli/run/run-crash-handler.test.ts b/test/cli/run/run-crash-handler.test.ts index 515784db7ee58..7213cee4ccab7 100644 --- a/test/cli/run/run-crash-handler.test.ts +++ b/test/cli/run/run-crash-handler.test.ts @@ -1,7 +1,6 @@ import { crash_handler } from "bun:internal-for-testing"; -import { test, expect, describe } from "bun:test"; -import { bunExe, bunEnv, tempDirWithFiles, mergeWindowEnvs } from "harness"; -import { existsSync } from "js/node/fs/export-star-from"; +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, mergeWindowEnvs } from "harness"; import path from "path"; const { getMachOImageZeroOffset } = crash_handler; @@ -11,58 +10,84 @@ test.if(process.platform === "darwin")("macOS has the assumed image offset", () expect(getMachOImageZeroOffset()).toBe(0x100000000); }); +test("raise ignoring panic handler does not trigger the panic handler", async () => { + let sent = false; + const resolve_handler = Promise.withResolvers(); + + using server = Bun.serve({ + port: 0, + fetch(request, server) { + sent = true; + resolve_handler.resolve(); + return new Response("OK"); + }, + }); + + const proc = Bun.spawn({ + cmd: [bunExe(), path.join(import.meta.dir, "fixture-crash.js"), "raiseIgnoringPanicHandler"], + env: mergeWindowEnvs([ + bunEnv, + { + BUN_CRASH_REPORT_URL: server.url.toString(), + BUN_ENABLE_CRASH_REPORTING: "1", + }, + ]), + }); + + await proc.exited; + + /// Wait two seconds for a slow http request, or continue immediatly once the request is heard. + await Promise.race([resolve_handler.promise, Bun.sleep(2000)]); + + expect(proc.exited).resolves.not.toBe(0); + expect(sent).toBe(false); +}); + describe("automatic crash reporter", () => { - const has_reporting = process.platform !== "linux"; + for (const approach of ["panic", "segfault", "outOfMemory"]) { + test(`${approach} should report`, async () => { + let sent = false; + const resolve_handler = Promise.withResolvers(); - for (const should_report of has_reporting ? [true, false] : [false]) { - for (const approach of ["panic", "segfault"]) { - // TODO: this dependency injection no worky. fix later - test.todo(`${approach} ${should_report ? "should" : "should not"} report`, async () => { - const temp = tempDirWithFiles("crash-handler-path", { - "curl": ({ root }) => `#!/usr/bin/env bash -echo $@ > ${root}/request.out -`, - "powershell.cmd": ({ root }) => `echo true > ${root}\\request.out -`, - }); + // Self host the crash report backend. + using server = Bun.serve({ + port: 0, + fetch(request, server) { + expect(request.url).toEndWith("/ack"); + sent = true; + resolve_handler.resolve(); + return new Response("OK"); + }, + }); - const env: any = mergeWindowEnvs([ + const proc = Bun.spawn({ + cmd: [bunExe(), path.join(import.meta.dir, "fixture-crash.js"), approach], + env: mergeWindowEnvs([ + bunEnv, { - ...bunEnv, + BUN_CRASH_REPORT_URL: server.url.toString(), + BUN_ENABLE_CRASH_REPORTING: "1", GITHUB_ACTIONS: undefined, CI: undefined, }, - { - PATH: temp + path.delimiter + process.env.PATH, - }, - ]); - - if (!should_report) { - env.DO_NOT_TRACK = "1"; - } - - const result = Bun.spawnSync( - [ - bunExe(), - path.join(import.meta.dir, "fixture-crash.js"), - approach, - "--debug-crash-handler-use-trace-string", - ], - { env }, - ); - - console.log(result.stderr.toString("utf-8")); - try { - expect(result.stderr.toString("utf-8")).toInclude("https://bun.report/"); - } catch (e) { - throw e; - } + ]), + stdio: ["ignore", "pipe", "pipe"], + }); + const exitCode = await proc.exited; + const stderr = await Bun.readableStreamToText(proc.stderr); + console.log(stderr); - await Bun.sleep(1000); + await resolve_handler.promise; - const did_report = existsSync(path.join(temp, "request.out")); - expect(did_report).toBe(should_report); - }); - } + expect(exitCode).not.toBe(0); + expect(stderr).toContain(server.url.toString()); + if (approach !== "outOfMemory") { + expect(stderr).toContain("oh no: Bun has crashed. This indicates a bug in Bun, not your code"); + } else { + expect(stderr.toLowerCase()).toContain("out of memory"); + expect(stderr.toLowerCase()).not.toContain("panic"); + } + expect(sent).toBe(true); + }); } }); diff --git a/test/cli/run/run-eval.test.ts b/test/cli/run/run-eval.test.ts index b4631edce294e..23294d4223652 100644 --- a/test/cli/run/run-eval.test.ts +++ b/test/cli/run/run-eval.test.ts @@ -1,9 +1,9 @@ -import { SpawnOptions, Subprocess, SyncSubprocess } from "bun"; +import { SyncSubprocess } from "bun"; import { describe, expect, test } from "bun:test"; -import { writeFileSync, rmSync } from "fs"; +import { rmSync, writeFileSync } from "fs"; import { bunEnv, bunExe, tmpdirSync } from "harness"; import { tmpdir } from "os"; -import { join, sep, posix } from "path"; +import { join, sep } from "path"; for (const flag of ["-e", "--print"]) { describe(`bun ${flag}`, () => { diff --git a/test/cli/run/run-extensionless.test.ts b/test/cli/run/run-extensionless.test.ts index 4881a583825ea..3163c258e4960 100644 --- a/test/cli/run/run-extensionless.test.ts +++ b/test/cli/run/run-extensionless.test.ts @@ -1,7 +1,6 @@ import { expect, test } from "bun:test"; -import { mkdirSync } from "fs"; +import { mkdirSync, writeFileSync } from "fs"; import { bunEnv, bunExe, isWindows, tmpdirSync } from "harness"; -import { writeFileSync } from "fs"; import { join } from "path"; test("running extensionless file works", async () => { diff --git a/test/cli/run/run-importmetamain.ts b/test/cli/run/run-importmetamain.ts new file mode 100644 index 0000000000000..877f4ac56d211 --- /dev/null +++ b/test/cli/run/run-importmetamain.ts @@ -0,0 +1,38 @@ +import { expect, test } from "bun:test"; +import { mkdirSync } from "fs"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; +import { join } from "path"; + +test("import.meta.main", async () => { + const dir = tmpdirSync(); + mkdirSync(dir, { recursive: true }); + await Bun.write( + join(dir, "index1.js"), + `import "fs"; console.log(JSON.stringify([typeof require, import.meta.main, !import.meta.main, require.main === module, require.main !== module]));`, + ); + const { stdout } = Bun.spawnSync({ + cmd: [bunExe(), join(dir, "index1.js")], + cwd: dir, + env: bunEnv, + stderr: "inherit", + stdout: "pipe", + }); + expect(stdout.toString("utf8").trim()).toEqual(JSON.stringify(["function", true, false, true, false])); +}); + +test("import.meta.main in a common.js file", async () => { + const dir = tmpdirSync(); + mkdirSync(dir, { recursive: true }); + await Bun.write( + join(dir, "index1.js"), + `module.exports = {}; console.log(JSON.stringify([typeof require, import.meta.main, !import.meta.main, require.main === module, require.main !== module]));`, + ); + const { stdout } = Bun.spawnSync({ + cmd: [bunExe(), join(dir, "index1.js")], + cwd: dir, + env: bunEnv, + stderr: "inherit", + stdout: "pipe", + }); + expect(stdout.toString("utf8").trim()).toEqual(JSON.stringify(["function", true, false, true, false])); +}); diff --git a/test/cli/run/run_command.test.ts b/test/cli/run/run_command.test.ts index 7f63e1f47f080..2c036b55bb68d 100644 --- a/test/cli/run/run_command.test.ts +++ b/test/cli/run/run_command.test.ts @@ -1,7 +1,7 @@ -import { describe, test, expect } from "bun:test"; import { spawnSync } from "bun"; +import { describe, expect, test } from "bun:test"; +import { rmSync, writeFileSync } from "fs"; import { bunEnv, bunExe, bunRun, isWindows } from "harness"; -import { writeFileSync, rmSync } from "fs"; let cwd: string; diff --git a/test/cli/run/shell-keepalive-fixture-1.js b/test/cli/run/shell-keepalive-fixture-1.js new file mode 100644 index 0000000000000..8e7780ece9d40 --- /dev/null +++ b/test/cli/run/shell-keepalive-fixture-1.js @@ -0,0 +1,8 @@ +process.exitCode = 1; + +(async () => { + console.log("here 1"); + await Bun.$`ls .`; + console.log("here 2"); + process.exit(0); +})(); diff --git a/test/cli/run/shell-keepalive-fixture-2.js b/test/cli/run/shell-keepalive-fixture-2.js new file mode 100644 index 0000000000000..4a9497cdbd1d6 --- /dev/null +++ b/test/cli/run/shell-keepalive-fixture-2.js @@ -0,0 +1,6 @@ +process.exitCode = 1; + +(async () => { + await Bun.$`${process.execPath} -e "console.log('hi')"`; + process.exit(0); +})(); diff --git a/test/cli/run/shell-keepalive.test.ts b/test/cli/run/shell-keepalive.test.ts new file mode 100644 index 0000000000000..cc364b20ca17e --- /dev/null +++ b/test/cli/run/shell-keepalive.test.ts @@ -0,0 +1,11 @@ +import { expect, test } from "bun:test"; +import "harness"; +import { join } from "path"; + +test("shell should stay alive while a builtin command is in progress", async () => { + expect([join(import.meta.dir, "shell-keepalive-fixture-1.js")]).toRun(); +}); + +test("shell should stay alive while a non-builtin command is in progress", async () => { + expect([join(import.meta.dir, "shell-keepalive-fixture-2.js")]).toRun(); +}); diff --git a/test/cli/run/transpiler-cache.test.ts b/test/cli/run/transpiler-cache.test.ts index 133946b3ff71a..9a4d7c6a43805 100644 --- a/test/cli/run/transpiler-cache.test.ts +++ b/test/cli/run/transpiler-cache.test.ts @@ -1,8 +1,7 @@ import { Subprocess } from "bun"; import { beforeEach, describe, expect, test } from "bun:test"; -import { realpathSync, chmodSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs"; +import { chmodSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync, writeFileSync } from "fs"; import { bunEnv, bunExe, bunRun, tmpdirSync } from "harness"; -import { tmpdir } from "os"; import { join } from "path"; function dummyFile(size: number, cache_bust: string, value: string | { code: string }) { diff --git a/test/cli/test/__snapshots__/coverage.test.ts.snap b/test/cli/test/__snapshots__/coverage.test.ts.snap new file mode 100644 index 0000000000000..9250349865538 --- /dev/null +++ b/test/cli/test/__snapshots__/coverage.test.ts.snap @@ -0,0 +1,29 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`lcov coverage reporter 1`] = ` +"TN: +SF:demo1.ts +FNF:1 +FNH:0 +DA:2,19 +DA:3,16 +DA:4,1 +LF:5 +LH:3 +end_of_record +TN: +SF:demo2.ts +FNF:2 +FNH:1 +DA:2,28 +DA:4,10 +DA:6,10 +DA:9,0 +DA:10,0 +DA:11,1 +DA:14,9 +LF:15 +LH:5 +end_of_record +" +`; diff --git a/test/cli/test/bun-test.test.ts b/test/cli/test/bun-test.test.ts index 784bbfbd50347..71400823a9769 100644 --- a/test/cli/test/bun-test.test.ts +++ b/test/cli/test/bun-test.test.ts @@ -1,9 +1,8 @@ -import { join, resolve, dirname } from "node:path"; -import { tmpdir } from "node:os"; -import { writeFileSync, rmSync, mkdirSync, realpathSync } from "node:fs"; import { spawnSync } from "bun"; -import { describe, test, expect } from "bun:test"; -import { bunExe, bunEnv, tmpdirSync } from "harness"; +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; +import { mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; describe("bun test", () => { test("can provide no arguments", () => { @@ -891,7 +890,7 @@ function createTest(input?: string | (string | { filename: string; contents: str const inputs = Array.isArray(input) ? input : [input ?? ""]; for (const input of inputs) { const contents = typeof input === "string" ? input : input.contents; - const name = typeof input === "string" ? filename ?? `bun-test-${Math.random()}.test.ts` : input.filename; + const name = typeof input === "string" ? (filename ?? `bun-test-${Math.random()}.test.ts`) : input.filename; const path = join(cwd, name); try { diff --git a/test/cli/test/coverage.test.ts b/test/cli/test/coverage.test.ts index af72217d029f0..40f8db3dc7b39 100644 --- a/test/cli/test/coverage.test.ts +++ b/test/cli/test/coverage.test.ts @@ -1,5 +1,6 @@ -import { test, expect } from "bun:test"; -import { tempDirWithFiles, bunExe, bunEnv } from "harness"; +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { readFileSync } from "node:fs"; import path from "path"; test("coverage crash", () => { @@ -18,3 +19,61 @@ test("coverage crash", () => { expect(result.exitCode).toBe(0); expect(result.signalCode).toBeUndefined(); }); + +test("lcov coverage reporter", () => { + const dir = tempDirWithFiles("cov", { + "demo2.ts": ` +import { Y } from "./demo1"; + +export function covered() { + // this function IS covered + return Y; +} + +export function uncovered() { + // this function is not covered + return 42; +} + +covered(); +`, + "demo1.ts": ` +export class Y { +#hello; +}; + `, + }); + const result = Bun.spawnSync([bunExe(), "test", "--coverage", "--coverage-reporter", "lcov", "./demo2.ts"], { + cwd: dir, + env: { + ...bunEnv, + }, + stdio: ["inherit", "inherit", "inherit"], + }); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); + expect(readFileSync(path.join(dir, "coverage", "lcov.info"), "utf-8")).toMatchSnapshot(); +}); + +test("coverage excludes node_modules directory", () => { + const dir = tempDirWithFiles("cov", { + "node_modules/pi/index.js": ` + export const pi = 3.14; + `, + "demo.test.ts": ` + import { pi } from 'pi'; + console.log(pi); + `, + }); + const result = Bun.spawnSync([bunExe(), "test", "--coverage"], { + cwd: dir, + env: { + ...bunEnv, + }, + stdio: [null, null, "pipe"], + }); + expect(result.stderr.toString("utf-8")).toContain("demo.test.ts"); + expect(result.stderr.toString("utf-8")).not.toContain("node_modules"); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); +}); diff --git a/test/cli/watch/watch.test.ts b/test/cli/watch/watch.test.ts index 64c24a47e94b5..b30dfd14367c2 100644 --- a/test/cli/watch/watch.test.ts +++ b/test/cli/watch/watch.test.ts @@ -1,40 +1,43 @@ -import { it, expect, afterEach } from "bun:test"; import type { Subprocess } from "bun"; import { spawn } from "bun"; +import { afterEach, expect, it } from "bun:test"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; +import { rmSync } from "node:fs"; import { join } from "node:path"; -import { writeFileSync, rmSync } from "node:fs"; -import { bunExe, bunEnv, tmpdirSync } from "harness"; let watchee: Subprocess; -it("should watch files", async () => { - const cwd = tmpdirSync(); - const path = join(cwd, "watchee.js"); +for (const dir of ["dir", "©️"]) { + it(`should watch files ${dir === "dir" ? "" : "(non-ascii path)"}`, async () => { + const cwd = join(tmpdirSync(), dir); + const path = join(cwd, "watchee.js"); - const updateFile = (i: number) => { - writeFileSync(path, `console.log(${i});`); - }; + const updateFile = async (i: number) => { + await Bun.write(path, `console.log(${i}, __dirname);`); + }; - let i = 0; - updateFile(i); - watchee = spawn({ - cwd, - cmd: [bunExe(), "--watch", "watchee.js"], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + let i = 0; + await updateFile(i); + await Bun.sleep(1000); + watchee = spawn({ + cwd, + cmd: [bunExe(), "--watch", "watchee.js"], + env: bunEnv, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); - for await (const line of watchee.stdout) { - if (i == 10) break; - var str = new TextDecoder().decode(line); - expect(str).toContain(`${i}`); - i++; - updateFile(i); - } - rmSync(path); -}); + for await (const line of watchee.stdout) { + if (i == 10) break; + var str = new TextDecoder().decode(line); + expect(str).toContain(`${i} ${cwd}`); + i++; + await updateFile(i); + } + rmSync(path); + }, 10000); +} afterEach(() => { watchee?.kill(); diff --git a/test/harness.ts b/test/harness.ts index 1aae38b75fb8b..d28882adfb5ef 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -1,13 +1,15 @@ -import { gc as bunGC, unsafe, which } from "bun"; -import { describe, test, expect, afterAll, beforeAll } from "bun:test"; -import { readlink, readFile, writeFile } from "fs/promises"; -import { isAbsolute, join, dirname } from "path"; -import fs, { openSync, closeSync } from "node:fs"; -import os from "node:os"; +import { gc as bunGC, sleepSync, spawnSync, unsafe, which } from "bun"; import { heapStats } from "bun:jsc"; +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { readFile, readlink, writeFile } from "fs/promises"; +import fs, { closeSync, openSync } from "node:fs"; +import os from "node:os"; +import { dirname, isAbsolute, join } from "path"; type Awaitable = T | Promise; +export const BREAKING_CHANGES_BUN_1_2 = false; + export const isMacOS = process.platform === "darwin"; export const isLinux = process.platform === "linux"; export const isPosix = isMacOS || isLinux; @@ -15,6 +17,7 @@ export const isWindows = process.platform === "win32"; export const isIntelMacOS = isMacOS && process.arch === "x64"; export const isDebug = Bun.version.includes("debug"); export const isCI = process.env.CI !== undefined; +export const isBuildKite = process.env.BUILDKITE === "true"; export const bunEnv: NodeJS.ProcessEnv = { ...process.env, @@ -45,6 +48,12 @@ for (let key in bunEnv) { delete bunEnv.NODE_ENV; +if (isDebug) { + // This makes debug build memory leak tests more reliable. + // The code for dumping out the debug build transpiled source code has leaks. + bunEnv.BUN_DEBUG_NO_DUMP = "1"; +} + export function bunExe() { if (isWindows) return process.execPath.replaceAll("\\", "/"); return process.execPath; @@ -54,6 +63,10 @@ export function nodeExe(): string | null { return which("node") || null; } +export function shellExe(): string { + return isWindows ? "pwsh" : "bash"; +} + export function gc(force = true) { bunGC(force); } @@ -87,7 +100,7 @@ export async function expectMaxObjectTypeCount( await Bun.sleep(wait); gc(); } - expect(heapStats().objectTypeCounts[type]).toBeLessThanOrEqual(count); + expect(heapStats().objectTypeCounts[type] || 0).toBeLessThanOrEqual(count); } // we must ensure that finalizers are run @@ -135,9 +148,11 @@ export function tempDirWithFiles(basename: string, files: DirectoryTree): string const joined = join(base, name); if (name.includes("/")) { const dir = dirname(name); - fs.mkdirSync(join(base, dir), { recursive: true }); + if (dir !== name && dir !== ".") { + fs.mkdirSync(join(base, dir), { recursive: true }); + } } - if (typeof contents === "object" && contents && !Buffer.isBuffer(contents)) { + if (typeof contents === "object" && contents && typeof contents?.byteLength === "undefined") { fs.mkdirSync(joined); makeTree(joined, contents); continue; @@ -281,6 +296,7 @@ const binaryTypes = { "int8array": Int8Array, "int16array": Int16Array, "int32array": Int32Array, + "float16array": globalThis.Float16Array, "float32array": Float32Array, "float64array": Float64Array, } as const; @@ -350,21 +366,21 @@ expect.extend({ } } }, - toRun(cmds: string[], optionalStdout?: string) { + toRun(cmds: string[], optionalStdout?: string, expectedCode: number = 0) { const result = Bun.spawnSync({ cmd: [bunExe(), ...cmds], env: bunEnv, stdio: ["inherit", "pipe", "inherit"], }); - if (result.exitCode !== 0) { + if (result.exitCode !== expectedCode) { return { pass: false, message: () => `Command ${cmds.join(" ")} failed:` + "\n" + result.stdout.toString("utf-8"), }; } - if (optionalStdout) { + if (optionalStdout != null) { return { pass: result.stdout.toString("utf-8") === optionalStdout, message: () => @@ -377,6 +393,43 @@ expect.extend({ message: () => `Expected ${cmds.join(" ")} to fail`, }; }, + toThrowWithCode(fn: CallableFunction, cls: CallableFunction, code: string) { + try { + fn(); + return { + pass: false, + message: () => `Received function did not throw`, + }; + } catch (e) { + // expect(e).toBeInstanceOf(cls); + if (!(e instanceof cls)) { + return { + pass: false, + message: () => `Expected error to be instanceof ${cls.name}; got ${e.__proto__.constructor.name}`, + }; + } + + // expect(e).toHaveProperty("code"); + if (!("code" in e)) { + return { + pass: false, + message: () => `Expected error to have property 'code'; got ${e}`, + }; + } + + // expect(e.code).toEqual(code); + if (e.code !== code) { + return { + pass: false, + message: () => `Expected error to have code '${code}'; got ${e.code}`, + }; + } + + return { + pass: true, + }; + } + }, }); export function ospath(path: string) { @@ -916,7 +969,7 @@ export async function runBunInstall( }); expect(stdout).toBeDefined(); expect(stderr).toBeDefined(); - let err = (await new Response(stderr).text()).replace(/warn: Slow filesystem/g, ""); + let err = (await new Response(stderr).text()).replace(/warn: Slow filesystem.*/g, ""); expect(err).not.toContain("panic:"); if (!options?.allowErrors) { expect(err).not.toContain("error:"); @@ -1023,7 +1076,8 @@ interface BunHarnessTestMatchers { toBeUTF16String(): void; toHaveTestTimedOutAfter(expected: number): void; toBeBinaryType(expected: keyof typeof binaryTypes): void; - toRun(optionalStdout?: string): void; + toRun(optionalStdout?: string, expectedCode?: number): void; + toThrowWithCode(cls: CallableFunction, code: string): void; } declare module "bun:test" { @@ -1044,4 +1098,195 @@ export function rejectUnauthorizedScope(value: boolean) { }; } -export const BREAKING_CHANGES_BUN_1_2 = false; +let networkInterfaces: any; + +function isIP(type: "IPv4" | "IPv6") { + if (!networkInterfaces) { + networkInterfaces = os.networkInterfaces(); + } + for (const networkInterface of Object.values(networkInterfaces)) { + for (const { family } of networkInterface as any[]) { + if (family === type) { + return true; + } + } + } + return false; +} + +export function isIPv6() { + // FIXME: AWS instances on Linux for Buildkite are not setup with IPv6 + if (isBuildKite && isLinux) { + return false; + } + return isIP("IPv6"); +} + +export function isIPv4() { + return isIP("IPv4"); +} + +let glibcVersion: string | undefined; + +export function getGlibcVersion() { + if (glibcVersion || !isLinux) { + return glibcVersion; + } + try { + const { header } = process.report!.getReport() as any; + const { glibcVersionRuntime: version } = header; + if (typeof version === "string") { + return (glibcVersion = version); + } + } catch (error) { + console.warn("Failed to detect glibc version", error); + } +} + +export function isGlibcVersionAtLeast(version: string): boolean { + const glibcVersion = getGlibcVersion(); + if (!glibcVersion) { + return false; + } + return Bun.semver.satisfies(glibcVersion, `>=${version}`); +} + +let macOSVersion: string | undefined; + +export function getMacOSVersion(): string | undefined { + if (macOSVersion || !isMacOS) { + return macOSVersion; + } + try { + const { stdout } = Bun.spawnSync({ + cmd: ["sw_vers", "-productVersion"], + }); + return (macOSVersion = stdout.toString().trim()); + } catch (error) { + console.warn("Failed to detect macOS version:", error); + } +} + +export function isMacOSVersionAtLeast(minVersion: number): boolean { + const macOSVersion = getMacOSVersion(); + if (!macOSVersion) { + return false; + } + return parseFloat(macOSVersion) >= minVersion; +} + +export function readableStreamFromArray(array) { + return new ReadableStream({ + pull(controller) { + for (let entry of array) { + controller.enqueue(entry); + } + controller.close(); + }, + }); +} + +let hasGuardMalloc = -1; +export function forceGuardMalloc(env) { + if (process.platform !== "darwin") { + return; + } + + if (hasGuardMalloc === -1) { + hasGuardMalloc = Number(fs.existsSync("/usr/lib/libgmalloc.dylib")); + } + + if (hasGuardMalloc === 1) { + env.DYLD_INSERT_LIBRARIES = "/usr/lib/libgmalloc.dylib"; + env.MALLOC_PROTECT_BEFORE = "1"; + env.MallocScribble = "1"; + env.MallocGuardEdges = "1"; + env.MALLOC_FILL_SPACE = "1"; + env.MALLOC_STRICT_SIZE = "1"; + } else { + console.warn("Guard malloc is not available on this platform for some reason."); + } +} + +export function fileDescriptorLeakChecker() { + const initial = getMaxFD(); + return { + [Symbol.dispose]() { + const current = getMaxFD(); + if (current > initial) { + throw new Error(`File descriptor leak detected: ${current} (current) > ${initial} (initial)`); + } + }, + }; +} + +/** + * Gets a secret from the environment. + * + * In Buildkite, secrets must be retrieved using the `buildkite-agent secret get` command + * and are not available as an environment variable. + */ +export function getSecret(name: string): string | undefined { + let value = process.env[name]?.trim(); + + // When not running in CI, allow the secret to be missing. + if (!isCI) { + return value; + } + + // In Buildkite, secrets must be retrieved using the `buildkite-agent secret get` command + if (!value && isBuildKite) { + const { exitCode, stdout } = spawnSync({ + cmd: ["buildkite-agent", "secret", "get", name], + stdout: "pipe", + stderr: "inherit", + }); + if (exitCode === 0) { + value = stdout.toString().trim(); + } + } + + // Throw an error if the secret is not found, so the test fails in CI. + if (!value) { + let hint; + if (isBuildKite) { + hint = `Create a secret with the name "${name}" in the Buildkite UI. +https://buildkite.com/docs/pipelines/security/secrets/buildkite-secrets`; + } else { + hint = `Define an environment variable with the name "${name}".`; + } + + throw new Error(`Secret not found: ${name}\n${hint}`); + } + + // Set the secret in the environment so that it can be used in tests. + process.env[name] = value; + + return value; +} + +export function assertManifestsPopulated(absCachePath: string, registryUrl: string) { + const { npm_manifest_test_helpers } = require("bun:internal-for-testing"); + const { parseManifest } = npm_manifest_test_helpers; + + for (const file of fs.readdirSync(absCachePath)) { + if (!file.endsWith(".npm")) continue; + + const manifest = parseManifest(join(absCachePath, file), registryUrl); + expect(manifest.versions.length).toBeGreaterThan(0); + } +} + +// Make it easier to run some node tests. +Object.defineProperty(globalThis, "gc", { + value: Bun.gc, + writable: true, + enumerable: false, + configurable: true, +}); + +export function waitForFileToExist(path: string, interval: number) { + while (!fs.existsSync(path)) { + sleepSync(interval); + } +} diff --git a/test/integration/esbuild/esbuild.test.ts b/test/integration/esbuild/esbuild.test.ts index 9c6f172f50b09..74ce115609fb5 100644 --- a/test/integration/esbuild/esbuild.test.ts +++ b/test/integration/esbuild/esbuild.test.ts @@ -1,8 +1,8 @@ -import { describe, expect, test, beforeAll, setDefaultTimeout } from "bun:test"; -import { rm, writeFile, cp } from "fs/promises"; +import { spawn } from "bun"; +import { beforeAll, describe, expect, setDefaultTimeout, test } from "bun:test"; +import { cp, rm, writeFile } from "fs/promises"; import { bunExe, bunEnv as env, tmpdirSync } from "harness"; import { join } from "path"; -import { spawn } from "bun"; beforeAll(() => { setDefaultTimeout(1000 * 60 * 5); diff --git a/test/integration/expo-app/app/(tabs)/_layout.tsx b/test/integration/expo-app/app/(tabs)/_layout.tsx index 147b92e9046ad..b890dd8240922 100644 --- a/test/integration/expo-app/app/(tabs)/_layout.tsx +++ b/test/integration/expo-app/app/(tabs)/_layout.tsx @@ -1,5 +1,4 @@ import { Tabs } from "expo-router"; -import React from "react"; import { TabBarIcon } from "@/components/navigation/TabBarIcon"; import { Colors } from "@/constants/Colors"; diff --git a/test/integration/expo-app/app/(tabs)/explore.tsx b/test/integration/expo-app/app/(tabs)/explore.tsx index 4fdec99152fd8..1416505cbb51b 100644 --- a/test/integration/expo-app/app/(tabs)/explore.tsx +++ b/test/integration/expo-app/app/(tabs)/explore.tsx @@ -1,5 +1,5 @@ import Ionicons from "@expo/vector-icons/Ionicons"; -import { StyleSheet, Image, Platform } from "react-native"; +import { Image, Platform, StyleSheet } from "react-native"; import { Collapsible } from "@/components/Collapsible"; import { ExternalLink } from "@/components/ExternalLink"; diff --git a/test/integration/expo-app/app/(tabs)/index.tsx b/test/integration/expo-app/app/(tabs)/index.tsx index bb95d9a5ea2bc..b32d20fde71a6 100644 --- a/test/integration/expo-app/app/(tabs)/index.tsx +++ b/test/integration/expo-app/app/(tabs)/index.tsx @@ -1,4 +1,4 @@ -import { Image, StyleSheet, Platform } from "react-native"; +import { Image, Platform, StyleSheet } from "react-native"; import { HelloWave } from "@/components/HelloWave"; import ParallaxScrollView from "@/components/ParallaxScrollView"; diff --git a/test/integration/expo-app/bun.lockb b/test/integration/expo-app/bun.lockb new file mode 100644 index 0000000000000..0c4bcc5b4c044 Binary files /dev/null and b/test/integration/expo-app/bun.lockb differ diff --git a/test/integration/expo-app/components/HelloWave.tsx b/test/integration/expo-app/components/HelloWave.tsx index e3efecbfbdfaf..318cc2142b4d3 100644 --- a/test/integration/expo-app/components/HelloWave.tsx +++ b/test/integration/expo-app/components/HelloWave.tsx @@ -1,10 +1,10 @@ import { StyleSheet } from "react-native"; import Animated, { - useSharedValue, useAnimatedStyle, - withTiming, + useSharedValue, withRepeat, withSequence, + withTiming, } from "react-native-reanimated"; import { ThemedText } from "@/components/ThemedText"; diff --git a/test/integration/expo-app/components/ThemedText.tsx b/test/integration/expo-app/components/ThemedText.tsx index 201eeef3cb8f3..11f2a5beda79a 100644 --- a/test/integration/expo-app/components/ThemedText.tsx +++ b/test/integration/expo-app/components/ThemedText.tsx @@ -1,4 +1,4 @@ -import { Text, type TextProps, StyleSheet } from "react-native"; +import { StyleSheet, Text, type TextProps } from "react-native"; import { useThemeColor } from "@/hooks/useThemeColor"; diff --git a/test/integration/expo-app/expo.test.ts b/test/integration/expo-app/expo.test.ts index 7975896a48d06..b2191a347bf99 100644 --- a/test/integration/expo-app/expo.test.ts +++ b/test/integration/expo-app/expo.test.ts @@ -1,6 +1,6 @@ -import { test, beforeAll, expect, setDefaultTimeout } from "bun:test"; +import { beforeAll, expect, setDefaultTimeout, test } from "bun:test"; import fs from "fs/promises"; -import { bunExe, bunEnv, tmpdirSync } from "../../harness"; +import { bunEnv, bunExe, tmpdirSync } from "../../harness"; const tmpdir = tmpdirSync(); @@ -20,12 +20,15 @@ test("expo export works (no ajv issues)", async () => { }); expect(exitCode).toBe(0); - ({ exitCode } = Bun.spawnSync([bunExe(), "run", "export", "-p", "web"], { + ({ exitCode } = Bun.spawnSync([bunExe(), "run", "export"], { stdout: "inherit", stderr: "inherit", stdin: "inherit", cwd: tmpdir, - env: bunEnv, + env: { + ...bunEnv, + PORT: "0", + }, })); // just check exit code for now diff --git a/test/integration/expo-app/package.json b/test/integration/expo-app/package.json index 47db1252647b4..ad6a4a8902577 100644 --- a/test/integration/expo-app/package.json +++ b/test/integration/expo-app/package.json @@ -9,65 +9,65 @@ "ios": "expo start --ios", "web": "expo start --web", "lint": "expo lint", - "export": "expo export" + "export": "expo export -p web" }, "dependencies": { - "@expo/vector-icons": "14.0.0", - "@gorhom/bottom-sheet": "4", - "@hookform/resolvers": "2.9.8", - "@tanstack/react-query": "4.35.3", - "axios": "0.24.0", - "date-fns": "2.29.3", - "expo": "50.0.0", - "expo-checkbox": "2.7.0", - "expo-constants": "15.4.5", - "expo-font": "11.10.3", - "expo-image": "1.10.6", - "expo-image-picker": "14.7.1", - "expo-linear-gradient": "12.7.2", - "expo-linking": "6.2.2", - "expo-router": "3.4.8", - "expo-secure-store": "12.8.1", + "@expo/vector-icons": "14.0.2", + "@gorhom/bottom-sheet": "4.6.4", + "@hookform/resolvers": "3.9.0", + "@tanstack/react-query": "5.55.4", + "axios": "1.7.7", + "date-fns": "3.6.0", + "expo": "51.0.32", + "expo-checkbox": "3.0.0", + "expo-constants": "16.0.2", + "expo-font": "12.0.10", + "expo-image": "1.12.15", + "expo-image-picker": "15.0.7", + "expo-linear-gradient": "13.0.2", + "expo-linking": "6.3.1", + "expo-router": "3.5.23", + "expo-secure-store": "13.0.2", "expo-web-browser": "13.0.3", - "expo-status-bar": "1.11.1", - "expo-updates": "0.24.11", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-hook-form": "7.43.9", - "react-native": "0.73.4", - "react-native-dropdown-picker": "5.4.4", - "react-native-gesture-handler": "2.14.0", + "expo-status-bar": "1.12.1", + "expo-updates": "0.25.24", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-hook-form": "7.53.0", + "react-native": "0.75.2", + "react-native-dropdown-picker": "5.4.6", + "react-native-gesture-handler": "2.19.0", "react-native-masked-text": "1.13.0", "react-native-modal": "13.0.1", - "react-native-reanimated": "3.6.2", - "react-native-safe-area-context": "4.8.2", - "react-native-screens": "3.29.0", - "react-native-svg": "14.1.0", - "react-native-toast-message": "2.1.5", - "react-native-web": "0.19.6", - "styled-components": "5.3.5", - "yup": "0.32.11" + "react-native-reanimated": "3.15.1", + "react-native-safe-area-context": "4.11.0", + "react-native-screens": "3.34.0", + "react-native-svg": "15.6.0", + "react-native-toast-message": "2.2.0", + "react-native-web": "0.19.12", + "styled-components": "6.1.13", + "yup": "1.4.0" }, "devDependencies": { - "@babel/core": "7.21.0", - "@types/react": "18.2.45", - "@types/styled-components-react-native": "5.1.3", - "@typescript-eslint/eslint-plugin": "5.32.0", - "@typescript-eslint/parser": "5.32.0", - "babel-plugin-module-resolver": "4.1.0", - "eslint": "8.21.0", + "@babel/core": "7.25.2", + "@types/react": "18.3.5", + "@types/styled-components-react-native": "5.2.5", + "@typescript-eslint/eslint-plugin": "8.5.0", + "@typescript-eslint/parser": "8.5.0", + "babel-plugin-module-resolver": "5.0.2", + "eslint": "9.10.0", "eslint-config-airbnb": "19.0.4", - "eslint-config-prettier": "8.5.0", - "eslint-import-resolver-typescript": "3.4.0", - "eslint-plugin-import": "2.26.0", - "eslint-plugin-jsx-a11y": "6.6.1", - "eslint-plugin-prefer-arrow-functions": "3.1.4", - "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-react": "7.30.1", - "eslint-plugin-react-hooks": "4.6.0", - "prettier": "2.7.1", - "react-native-svg-transformer": "1.0.0", - "typescript": "5.3.2" + "eslint-config-prettier": "9.1.0", + "eslint-import-resolver-typescript": "3.6.3", + "eslint-plugin-import": "2.30.0", + "eslint-plugin-jsx-a11y": "6.10.0", + "eslint-plugin-prefer-arrow-functions": "3.4.1", + "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-react": "7.35.2", + "eslint-plugin-react-hooks": "4.6.2", + "prettier": "3.3.3", + "react-native-svg-transformer": "1.5.0", + "typescript": "5.6.2" }, "private": true -} \ No newline at end of file +} diff --git a/test/integration/mysql2/mysql2.test.ts b/test/integration/mysql2/mysql2.test.ts index cb0ef919f6398..9c8c383612ece 100644 --- a/test/integration/mysql2/mysql2.test.ts +++ b/test/integration/mysql2/mysql2.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { describeWithContainer } from "harness"; import type { Connection, ConnectionOptions } from "mysql2/promise"; import { createConnection } from "mysql2/promise"; diff --git a/test/integration/next-pages/bun.lockb b/test/integration/next-pages/bun.lockb index c7ad10320b2f5..683a48a517bf8 100755 Binary files a/test/integration/next-pages/bun.lockb and b/test/integration/next-pages/bun.lockb differ diff --git a/test/integration/next-pages/bunfig.toml b/test/integration/next-pages/bunfig.toml index 0a8f52769cffa..35a6b68216720 100644 --- a/test/integration/next-pages/bunfig.toml +++ b/test/integration/next-pages/bunfig.toml @@ -1,2 +1,2 @@ -[install] +[install] cache = false \ No newline at end of file diff --git a/test/integration/next-pages/package.json b/test/integration/next-pages/package.json index a6ac1698ed980..bb25ca4b5eaae 100644 --- a/test/integration/next-pages/package.json +++ b/test/integration/next-pages/package.json @@ -19,7 +19,7 @@ "eslint-config-next": "14.1.3", "next": "14.1.3", "postcss": "8.4.30", - "puppeteer": "22.4.1", + "puppeteer": "22.12.0", "react": "18.2.0", "react-dom": "18.2.0", "tailwindcss": "3.3.3", diff --git a/test/integration/next-pages/src/pages/_document.tsx b/test/integration/next-pages/src/pages/_document.tsx index b2fff8b4262dd..ffc3f3cccfc02 100644 --- a/test/integration/next-pages/src/pages/_document.tsx +++ b/test/integration/next-pages/src/pages/_document.tsx @@ -1,4 +1,4 @@ -import { Html, Head, Main, NextScript } from "next/document"; +import { Head, Html, Main, NextScript } from "next/document"; export default function Document() { return ( diff --git a/test/integration/next-pages/src/pages/index.tsx b/test/integration/next-pages/src/pages/index.tsx index 109f5e5e2735e..0bda34977a84d 100644 --- a/test/integration/next-pages/src/pages/index.tsx +++ b/test/integration/next-pages/src/pages/index.tsx @@ -1,7 +1,7 @@ -import Image from "next/image"; +import { Counter } from "@/Counter"; import { Inter } from "next/font/google"; import Head from "next/head"; -import { Counter } from "@/Counter"; +import Image from "next/image"; const inter = Inter({ subsets: ["latin"] }); @@ -122,7 +122,7 @@ export async function getStaticProps() { bunVersion: process.env.NODE_ENV === "production" ? "[production needs a constant string]" - : process.versions.bun ?? "not in bun", + : (process.versions.bun ?? "not in bun"), }, }; } diff --git a/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap b/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap index 839290caca28e..da10c802c8e29 100644 --- a/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap +++ b/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap @@ -14,7 +14,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "@types/node", "version": "==20.7.0", }, - "package_id": 456, + "package_id": 452, }, { "behavior": { @@ -27,7 +27,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "@types/react", "version": "==18.2.22", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { @@ -40,7 +40,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "@types/react-dom", "version": "==18.2.7", }, - "package_id": 451, + "package_id": 447, }, { "behavior": { @@ -53,7 +53,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "autoprefixer", "version": "==10.4.16", }, - "package_id": 444, + "package_id": 440, }, { "behavior": { @@ -66,7 +66,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "bun-types", "version": ">=1.0.3 <2.0.0", }, - "package_id": 442, + "package_id": 438, }, { "behavior": { @@ -79,7 +79,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "eslint", "version": "==8.50.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { @@ -92,7 +92,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "eslint-config-next", "version": "==14.1.3", }, - "package_id": 241, + "package_id": 237, }, { "behavior": { @@ -105,7 +105,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "next", "version": "==14.1.3", }, - "package_id": 223, + "package_id": 219, }, { "behavior": { @@ -125,11 +125,11 @@ exports[`ssr works for 100-ish requests 1`] = ` "normal": true, }, "id": 9, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer", "npm": { "name": "puppeteer", - "version": "==22.4.1", + "version": "==22.12.0", }, "package_id": 113, }, @@ -2008,221 +2008,234 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "cosmiconfig", "version": "==9.0.0", }, - "package_id": 201, + "package_id": 197, }, { "behavior": { "normal": true, }, "id": 154, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer-core", "npm": { "name": "puppeteer-core", - "version": "==22.4.1", + "version": "==22.12.0", }, - "package_id": 190, + "package_id": 191, }, { "behavior": { "normal": true, }, "id": 155, - "literal": "2.1.0", + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, "id": 156, + "literal": "0.0.1299070", + "name": "devtools-protocol", + "npm": { + "name": "devtools-protocol", + "version": "==0.0.1299070", + }, + "package_id": 114, + }, + { + "behavior": { + "normal": true, + }, + "id": 157, "literal": "4.3.4", "name": "debug", "npm": { "name": "debug", "version": "==4.3.4", }, - "package_id": 189, + "package_id": 190, }, { "behavior": { "normal": true, }, - "id": 157, + "id": 158, "literal": "2.0.1", "name": "extract-zip", "npm": { "name": "extract-zip", "version": "==2.0.1", }, - "package_id": 180, + "package_id": 181, }, { "behavior": { "normal": true, }, - "id": 158, + "id": 159, "literal": "2.0.3", "name": "progress", "npm": { "name": "progress", "version": "==2.0.3", }, - "package_id": 179, + "package_id": 180, }, { "behavior": { "normal": true, }, - "id": 159, + "id": 160, "literal": "6.4.0", "name": "proxy-agent", "npm": { "name": "proxy-agent", "version": "==6.4.0", }, - "package_id": 146, + "package_id": 147, }, { "behavior": { "normal": true, }, - "id": 160, + "id": 161, "literal": "3.0.5", "name": "tar-fs", "npm": { "name": "tar-fs", "version": "==3.0.5", }, - "package_id": 130, + "package_id": 131, }, { "behavior": { "normal": true, }, - "id": 161, + "id": 162, "literal": "1.4.3", "name": "unbzip2-stream", "npm": { "name": "unbzip2-stream", "version": "==1.4.3", }, - "package_id": 125, + "package_id": 126, }, { "behavior": { "normal": true, }, - "id": 162, + "id": 163, "literal": "17.7.2", "name": "yargs", "npm": { "name": "yargs", "version": "==17.7.2", }, - "package_id": 118, + "package_id": 119, }, { "behavior": { "normal": true, }, - "id": 163, + "id": 164, "literal": "7.6.0", "name": "semver", "npm": { "name": "semver", "version": "==7.6.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 164, + "id": 165, "literal": "^6.0.0", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=6.0.0 <7.0.0", }, - "package_id": 116, + "package_id": 117, }, { "behavior": { "normal": true, }, - "id": 165, + "id": 166, "literal": "^4.0.0", "name": "yallist", "npm": { "name": "yallist", "version": ">=4.0.0 <5.0.0", }, - "package_id": 117, + "package_id": 118, }, { "behavior": { "normal": true, }, - "id": 166, + "id": 167, "literal": "^8.0.1", "name": "cliui", "npm": { "name": "cliui", "version": ">=8.0.1 <9.0.0", }, - "package_id": 124, + "package_id": 125, }, { "behavior": { "normal": true, }, - "id": 167, + "id": 168, "literal": "^3.1.1", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.1 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 168, + "id": 169, "literal": "^2.0.5", "name": "get-caller-file", "npm": { "name": "get-caller-file", "version": ">=2.0.5 <3.0.0", }, - "package_id": 122, + "package_id": 123, }, { "behavior": { "normal": true, }, - "id": 169, + "id": 170, "literal": "^2.1.1", "name": "require-directory", "npm": { "name": "require-directory", "version": ">=2.1.1 <3.0.0", }, - "package_id": 121, + "package_id": 122, }, { "behavior": { "normal": true, }, - "id": 170, + "id": 171, "literal": "^4.2.3", "name": "string-width", "npm": { @@ -2235,33 +2248,33 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 171, + "id": 172, "literal": "^5.0.5", "name": "y18n", "npm": { "name": "y18n", "version": ">=5.0.5 <6.0.0", }, - "package_id": 120, + "package_id": 121, }, { "behavior": { "normal": true, }, - "id": 172, + "id": 173, "literal": "^21.1.1", "name": "yargs-parser", "npm": { "name": "yargs-parser", "version": ">=21.1.1 <22.0.0", }, - "package_id": 119, + "package_id": 120, }, { "behavior": { "normal": true, }, - "id": 173, + "id": 174, "literal": "^4.2.0", "name": "string-width", "npm": { @@ -2274,7 +2287,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 174, + "id": 175, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -2287,7 +2300,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 175, + "id": 176, "literal": "^7.0.0", "name": "wrap-ansi", "npm": { @@ -2300,1130 +2313,1117 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 176, + "id": 177, "literal": "^5.2.1", "name": "buffer", "npm": { "name": "buffer", "version": ">=5.2.1 <6.0.0", }, - "package_id": 127, + "package_id": 128, }, { "behavior": { "normal": true, }, - "id": 177, + "id": 178, "literal": "^2.3.8", "name": "through", "npm": { "name": "through", "version": ">=2.3.8 <3.0.0", }, - "package_id": 126, + "package_id": 127, }, { "behavior": { "normal": true, }, - "id": 178, + "id": 179, "literal": "^1.3.1", "name": "base64-js", "npm": { "name": "base64-js", "version": ">=1.3.1 <2.0.0", }, - "package_id": 129, + "package_id": 130, }, { "behavior": { "normal": true, }, - "id": 179, + "id": 180, "literal": "^1.1.13", "name": "ieee754", "npm": { "name": "ieee754", "version": ">=1.1.13 <2.0.0", }, - "package_id": 128, + "package_id": 129, }, { "behavior": { "normal": true, }, - "id": 180, + "id": 181, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 181, + "id": 182, "literal": "^3.1.5", "name": "tar-stream", "npm": { "name": "tar-stream", "version": ">=3.1.5 <4.0.0", }, - "package_id": 141, + "package_id": 142, }, { "behavior": { "optional": true, }, - "id": 182, + "id": 183, "literal": "^2.1.1", "name": "bare-fs", "npm": { "name": "bare-fs", "version": ">=2.1.1 <3.0.0", }, - "package_id": 133, + "package_id": 134, }, { "behavior": { "optional": true, }, - "id": 183, + "id": 184, "literal": "^2.1.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.1.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 184, + "id": 185, "literal": "^2.1.0", "name": "bare-os", "npm": { "name": "bare-os", "version": ">=2.1.0 <3.0.0", }, - "package_id": 132, + "package_id": 133, }, { "behavior": { "normal": true, }, - "id": 185, + "id": 186, "literal": "^2.0.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.0.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 186, + "id": 187, "literal": "^2.0.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.0.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 187, + "id": 188, "literal": "^2.0.0", "name": "bare-stream", "npm": { "name": "bare-stream", "version": ">=2.0.0 <3.0.0", }, - "package_id": 134, + "package_id": 135, }, { "behavior": { "normal": true, }, - "id": 188, + "id": 189, "literal": "^2.18.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.18.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 189, + "id": 190, "literal": "^1.3.2", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.3.2 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 190, + "id": 191, "literal": "^1.0.1", "name": "queue-tick", "npm": { "name": "queue-tick", "version": ">=1.0.1 <2.0.0", }, - "package_id": 139, + "package_id": 140, }, { "behavior": { "normal": true, }, - "id": 191, + "id": 192, "literal": "^1.1.0", "name": "text-decoder", "npm": { "name": "text-decoder", "version": ">=1.1.0 <2.0.0", }, - "package_id": 137, + "package_id": 138, }, { "behavior": { "optional": true, }, - "id": 192, + "id": 193, "literal": "^2.2.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.2.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 193, + "id": 194, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 194, + "id": 195, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 195, + "id": 196, "literal": "^1.2.0", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.2.0 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 196, + "id": 197, "literal": "^2.15.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.15.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 197, + "id": 198, "literal": "^1.1.0", "name": "end-of-stream", "npm": { "name": "end-of-stream", "version": ">=1.1.0 <2.0.0", }, - "package_id": 145, + "package_id": 146, }, { "behavior": { "normal": true, }, - "id": 198, + "id": 199, "literal": "^1.3.1", "name": "once", "npm": { "name": "once", "version": ">=1.3.1 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 199, + "id": 200, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 200, + "id": 201, "literal": "^1.4.0", "name": "once", "npm": { "name": "once", "version": ">=1.4.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 201, + "id": 202, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 202, + "id": 203, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 203, + "id": 204, "literal": "^7.0.1", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 204, + "id": 205, "literal": "^7.0.3", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.3 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 205, + "id": 206, "literal": "^7.14.1", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=7.14.1 <8.0.0", }, - "package_id": 178, + "package_id": 179, }, { "behavior": { "normal": true, }, - "id": 206, + "id": 207, "literal": "^7.0.1", "name": "pac-proxy-agent", "npm": { "name": "pac-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 157, + "package_id": 158, }, { "behavior": { "normal": true, }, - "id": 207, + "id": 208, "literal": "^1.1.0", "name": "proxy-from-env", "npm": { "name": "proxy-from-env", "version": ">=1.1.0 <2.0.0", }, - "package_id": 156, + "package_id": 157, }, { "behavior": { "normal": true, }, - "id": 208, + "id": 209, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 209, + "id": 210, "literal": "^7.1.1", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.1 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 210, + "id": 211, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 211, + "id": 212, "literal": "^2.7.1", "name": "socks", "npm": { "name": "socks", "version": ">=2.7.1 <3.0.0", }, - "package_id": 148, + "package_id": 149, }, { "behavior": { "normal": true, }, - "id": 212, + "id": 213, "literal": "^9.0.5", "name": "ip-address", "npm": { "name": "ip-address", "version": ">=9.0.5 <10.0.0", }, - "package_id": 150, + "package_id": 151, }, { "behavior": { "normal": true, }, - "id": 213, + "id": 214, "literal": "^4.2.0", "name": "smart-buffer", "npm": { "name": "smart-buffer", "version": ">=4.2.0 <5.0.0", }, - "package_id": 149, + "package_id": 150, }, { "behavior": { "normal": true, }, - "id": 214, + "id": 215, "literal": "1.1.0", "name": "jsbn", "npm": { "name": "jsbn", "version": "==1.1.0", }, - "package_id": 152, + "package_id": 153, }, { "behavior": { "normal": true, }, - "id": 215, + "id": 216, "literal": "^1.1.3", "name": "sprintf-js", "npm": { "name": "sprintf-js", "version": ">=1.1.3 <2.0.0", }, - "package_id": 151, + "package_id": 152, }, { "behavior": { "normal": true, }, - "id": 216, + "id": 217, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 217, + "id": 218, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 218, + "id": 219, "literal": "^0.23.0", "name": "@tootallnate/quickjs-emscripten", "npm": { "name": "@tootallnate/quickjs-emscripten", "version": ">=0.23.0 <0.24.0", }, - "package_id": 177, + "package_id": 178, }, { "behavior": { "normal": true, }, - "id": 219, + "id": 220, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 220, + "id": 221, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 221, + "id": 222, "literal": "^6.0.1", "name": "get-uri", "npm": { "name": "get-uri", "version": ">=6.0.1 <7.0.0", }, - "package_id": 170, + "package_id": 171, }, { "behavior": { "normal": true, }, - "id": 222, + "id": 223, "literal": "^7.0.0", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.0 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 223, + "id": 224, "literal": "^7.0.2", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.2 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 224, + "id": 225, "literal": "^7.0.0", "name": "pac-resolver", "npm": { "name": "pac-resolver", "version": ">=7.0.0 <8.0.0", }, - "package_id": 158, + "package_id": 159, }, { "behavior": { "normal": true, }, - "id": 225, + "id": 226, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 226, + "id": 227, "literal": "^5.0.0", "name": "degenerator", "npm": { "name": "degenerator", "version": ">=5.0.0 <6.0.0", }, - "package_id": 160, + "package_id": 161, }, { "behavior": { "normal": true, }, - "id": 227, + "id": 228, "literal": "^2.0.2", "name": "netmask", "npm": { "name": "netmask", "version": ">=2.0.2 <3.0.0", }, - "package_id": 159, + "package_id": 160, }, { "behavior": { "normal": true, }, - "id": 228, + "id": 229, "literal": "^0.13.4", "name": "ast-types", "npm": { "name": "ast-types", "version": ">=0.13.4 <0.14.0", }, - "package_id": 166, + "package_id": 167, }, { "behavior": { "normal": true, }, - "id": 229, + "id": 230, "literal": "^2.1.0", "name": "escodegen", "npm": { "name": "escodegen", "version": ">=2.1.0 <3.0.0", }, - "package_id": 162, + "package_id": 163, }, { "behavior": { "normal": true, }, - "id": 230, + "id": 231, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "normal": true, }, - "id": 231, + "id": 232, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 232, + "id": 233, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 233, + "id": 234, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "optional": true, }, - "id": 234, + "id": 235, "literal": "~0.6.1", "name": "source-map", "npm": { "name": "source-map", "version": ">=0.6.1 <0.7.0", }, - "package_id": 163, + "package_id": 164, }, { "behavior": { "normal": true, }, - "id": 235, + "id": 236, "literal": "^2.0.1", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.0.1 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 236, + "id": 237, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 237, + "id": 238, "literal": "4", "name": "debug", "npm": { "name": "debug", "version": "<5.0.0 >=4.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 238, + "id": 239, "literal": "^7.1.0", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.0 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 239, + "id": 240, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 240, + "id": 241, "literal": "^5.0.2", "name": "basic-ftp", "npm": { "name": "basic-ftp", "version": ">=5.0.2 <6.0.0", }, - "package_id": 176, + "package_id": 177, }, { "behavior": { "normal": true, }, - "id": 241, + "id": 242, "literal": "^6.0.2", "name": "data-uri-to-buffer", "npm": { "name": "data-uri-to-buffer", "version": ">=6.0.2 <7.0.0", }, - "package_id": 175, + "package_id": 176, }, { "behavior": { "normal": true, }, - "id": 242, + "id": 243, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 243, + "id": 244, "literal": "^11.2.0", "name": "fs-extra", "npm": { "name": "fs-extra", "version": ">=11.2.0 <12.0.0", }, - "package_id": 171, + "package_id": 172, }, { "behavior": { "normal": true, }, - "id": 244, + "id": 245, "literal": "^4.2.0", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.0 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 245, + "id": 246, "literal": "^6.0.1", "name": "jsonfile", "npm": { "name": "jsonfile", "version": ">=6.0.1 <7.0.0", }, - "package_id": 173, + "package_id": 174, }, { "behavior": { "normal": true, }, - "id": 246, + "id": 247, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "normal": true, }, - "id": 247, + "id": 248, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "optional": true, }, - "id": 248, + "id": 249, "literal": "^4.1.6", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.1.6 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 249, + "id": 250, "literal": "^4.1.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.1.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 250, + "id": 251, "literal": "^5.1.0", "name": "get-stream", "npm": { "name": "get-stream", "version": ">=5.1.0 <6.0.0", }, - "package_id": 188, + "package_id": 189, }, { "behavior": { "normal": true, }, - "id": 251, + "id": 252, "literal": "^2.10.0", "name": "yauzl", "npm": { "name": "yauzl", "version": ">=2.10.0 <3.0.0", }, - "package_id": 184, + "package_id": 185, }, { "behavior": { "optional": true, }, - "id": 252, + "id": 253, "literal": "^2.9.1", "name": "@types/yauzl", "npm": { "name": "@types/yauzl", "version": ">=2.9.1 <3.0.0", }, - "package_id": 181, + "package_id": 182, }, { "behavior": { "normal": true, }, - "id": 253, + "id": 254, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 254, + "id": 255, "literal": "~5.26.4", "name": "undici-types", "npm": { "name": "undici-types", "version": ">=5.26.4 <5.27.0", }, - "package_id": 183, + "package_id": 184, }, { "behavior": { "normal": true, }, - "id": 255, + "id": 256, "literal": "~1.1.0", "name": "fd-slicer", "npm": { "name": "fd-slicer", "version": ">=1.1.0 <1.2.0", }, - "package_id": 186, + "package_id": 187, }, { "behavior": { "normal": true, }, - "id": 256, + "id": 257, "literal": "~0.2.3", "name": "buffer-crc32", "npm": { "name": "buffer-crc32", "version": ">=0.2.3 <0.3.0", }, - "package_id": 185, + "package_id": 186, }, { "behavior": { "normal": true, }, - "id": 257, + "id": 258, "literal": "~1.2.0", "name": "pend", "npm": { "name": "pend", "version": ">=1.2.0 <1.3.0", }, - "package_id": 187, + "package_id": 188, }, { "behavior": { "normal": true, }, - "id": 258, + "id": 259, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 259, + "id": 260, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 260, - "literal": "2.1.0", + "id": 261, + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, - "id": 261, - "literal": "0.5.12", + "id": 262, + "literal": "0.5.24", "name": "chromium-bidi", "npm": { "name": "chromium-bidi", - "version": "==0.5.12", - }, - "package_id": 198, - }, - { - "behavior": { - "normal": true, - }, - "id": 262, - "literal": "4.0.0", - "name": "cross-fetch", - "npm": { - "name": "cross-fetch", - "version": "==4.0.0", + "version": "==0.5.24", }, "package_id": 193, }, @@ -3432,39 +3432,39 @@ exports[`ssr works for 100-ish requests 1`] = ` "normal": true, }, "id": 263, - "literal": "4.3.4", + "literal": "4.3.5", "name": "debug", "npm": { "name": "debug", - "version": "==4.3.4", + "version": "==4.3.5", }, - "package_id": 189, + "package_id": 154, }, { "behavior": { "normal": true, }, "id": 264, - "literal": "0.0.1249869", + "literal": "0.0.1299070", "name": "devtools-protocol", "npm": { "name": "devtools-protocol", - "version": "==0.0.1249869", + "version": "==0.0.1299070", }, - "package_id": 192, + "package_id": 114, }, { "behavior": { "normal": true, }, "id": 265, - "literal": "8.16.0", + "literal": "8.17.1", "name": "ws", "npm": { "name": "ws", - "version": "==8.16.0", + "version": "==8.17.1", }, - "package_id": 191, + "package_id": 192, }, { "behavior": { @@ -3499,164 +3499,111 @@ exports[`ssr works for 100-ish requests 1`] = ` "normal": true, }, "id": 268, - "literal": "^2.6.12", - "name": "node-fetch", + "literal": "3.0.1", + "name": "mitt", "npm": { - "name": "node-fetch", - "version": ">=2.6.12 <3.0.0", + "name": "mitt", + "version": "==3.0.1", }, - "package_id": 194, + "package_id": 196, }, { "behavior": { "normal": true, }, "id": 269, - "literal": "^5.0.0", - "name": "whatwg-url", + "literal": "10.0.0", + "name": "urlpattern-polyfill", "npm": { - "name": "whatwg-url", - "version": ">=5.0.0 <6.0.0", + "name": "urlpattern-polyfill", + "version": "==10.0.0", }, "package_id": 195, }, { "behavior": { - "optional": true, - "peer": true, + "normal": true, }, "id": 270, - "literal": "^0.1.0", - "name": "encoding", + "literal": "3.23.8", + "name": "zod", "npm": { - "name": "encoding", - "version": ">=0.1.0 <0.2.0", + "name": "zod", + "version": "==3.23.8", }, - "package_id": null, + "package_id": 194, }, { "behavior": { - "normal": true, + "peer": true, }, "id": 271, - "literal": "~0.0.3", - "name": "tr46", + "literal": "*", + "name": "devtools-protocol", "npm": { - "name": "tr46", - "version": ">=0.0.3 <0.1.0", + "name": "devtools-protocol", + "version": ">=0.0.0", }, - "package_id": 197, + "package_id": 114, }, { "behavior": { "normal": true, }, "id": 272, - "literal": "^3.0.0", - "name": "webidl-conversions", + "literal": "^2.2.1", + "name": "env-paths", "npm": { - "name": "webidl-conversions", - "version": ">=3.0.0 <4.0.0", + "name": "env-paths", + "version": ">=2.2.1 <3.0.0", }, - "package_id": 196, + "package_id": 218, }, { "behavior": { "normal": true, }, "id": 273, - "literal": "3.0.1", - "name": "mitt", + "literal": "^3.3.0", + "name": "import-fresh", "npm": { - "name": "mitt", - "version": "==3.0.1", + "name": "import-fresh", + "version": ">=3.3.0 <4.0.0", }, - "package_id": 200, + "package_id": 214, }, { "behavior": { "normal": true, }, "id": 274, - "literal": "10.0.0", - "name": "urlpattern-polyfill", - "npm": { - "name": "urlpattern-polyfill", - "version": "==10.0.0", - }, - "package_id": 199, - }, - { - "behavior": { - "peer": true, - }, - "id": 275, - "literal": "*", - "name": "devtools-protocol", - "npm": { - "name": "devtools-protocol", - "version": ">=0.0.0", - }, - "package_id": 192, - }, - { - "behavior": { - "normal": true, - }, - "id": 276, - "literal": "^2.2.1", - "name": "env-paths", - "npm": { - "name": "env-paths", - "version": ">=2.2.1 <3.0.0", - }, - "package_id": 222, - }, - { - "behavior": { - "normal": true, - }, - "id": 277, - "literal": "^3.3.0", - "name": "import-fresh", - "npm": { - "name": "import-fresh", - "version": ">=3.3.0 <4.0.0", - }, - "package_id": 218, - }, - { - "behavior": { - "normal": true, - }, - "id": 278, - "literal": "^4.1.0", - "name": "js-yaml", + "literal": "^4.1.0", + "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 279, + "id": 275, "literal": "^5.2.0", "name": "parse-json", "npm": { "name": "parse-json", "version": ">=5.2.0 <6.0.0", }, - "package_id": 202, + "package_id": 198, }, { "behavior": { "optional": true, "peer": true, }, - "id": 280, + "id": 276, "literal": ">=4.9.5", "name": "typescript", "npm": { @@ -3669,46 +3616,46 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 281, + "id": 277, "literal": "^7.0.0", "name": "@babel/code-frame", "npm": { "name": "@babel/code-frame", "version": ">=7.0.0 <8.0.0", }, - "package_id": 206, + "package_id": 202, }, { "behavior": { "normal": true, }, - "id": 282, + "id": 278, "literal": "^1.3.1", "name": "error-ex", "npm": { "name": "error-ex", "version": ">=1.3.1 <2.0.0", }, - "package_id": 204, + "package_id": 200, }, { "behavior": { "normal": true, }, - "id": 283, + "id": 279, "literal": "^2.3.0", "name": "json-parse-even-better-errors", "npm": { "name": "json-parse-even-better-errors", "version": ">=2.3.0 <3.0.0", }, - "package_id": 203, + "package_id": 199, }, { "behavior": { "normal": true, }, - "id": 284, + "id": 280, "literal": "^1.1.6", "name": "lines-and-columns", "npm": { @@ -3721,33 +3668,33 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 285, + "id": 281, "literal": "^0.2.1", "name": "is-arrayish", "npm": { "name": "is-arrayish", "version": ">=0.2.1 <0.3.0", }, - "package_id": 205, + "package_id": 201, }, { "behavior": { "normal": true, }, - "id": 286, + "id": 282, "literal": "^7.24.7", "name": "@babel/highlight", "npm": { "name": "@babel/highlight", "version": ">=7.24.7 <8.0.0", }, - "package_id": 207, + "package_id": 203, }, { "behavior": { "normal": true, }, - "id": 287, + "id": 283, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -3760,33 +3707,33 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 288, + "id": 284, "literal": "^7.24.7", "name": "@babel/helper-validator-identifier", "npm": { "name": "@babel/helper-validator-identifier", "version": ">=7.24.7 <8.0.0", }, - "package_id": 215, + "package_id": 211, }, { "behavior": { "normal": true, }, - "id": 289, + "id": 285, "literal": "^2.4.2", "name": "chalk", "npm": { "name": "chalk", "version": ">=2.4.2 <3.0.0", }, - "package_id": 208, + "package_id": 204, }, { "behavior": { "normal": true, }, - "id": 290, + "id": 286, "literal": "^4.0.0", "name": "js-tokens", "npm": { @@ -3799,7 +3746,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 291, + "id": 287, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -3812,346 +3759,346 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 292, + "id": 288, "literal": "^3.2.1", "name": "ansi-styles", "npm": { "name": "ansi-styles", "version": ">=3.2.1 <4.0.0", }, - "package_id": 212, + "package_id": 208, }, { "behavior": { "normal": true, }, - "id": 293, + "id": 289, "literal": "^1.0.5", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=1.0.5 <2.0.0", }, - "package_id": 211, + "package_id": 207, }, { "behavior": { "normal": true, }, - "id": 294, + "id": 290, "literal": "^5.3.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=5.3.0 <6.0.0", }, - "package_id": 209, + "package_id": 205, }, { "behavior": { "normal": true, }, - "id": 295, + "id": 291, "literal": "^3.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=3.0.0 <4.0.0", }, - "package_id": 210, + "package_id": 206, }, { "behavior": { "normal": true, }, - "id": 296, + "id": 292, "literal": "^1.9.0", "name": "color-convert", "npm": { "name": "color-convert", "version": ">=1.9.0 <2.0.0", }, - "package_id": 213, + "package_id": 209, }, { "behavior": { "normal": true, }, - "id": 297, + "id": 293, "literal": "1.1.3", "name": "color-name", "npm": { "name": "color-name", "version": "==1.1.3", }, - "package_id": 214, + "package_id": 210, }, { "behavior": { "normal": true, }, - "id": 298, + "id": 294, "literal": "^2.0.1", "name": "argparse", "npm": { "name": "argparse", "version": ">=2.0.1 <3.0.0", }, - "package_id": 217, + "package_id": 213, }, { "behavior": { "normal": true, }, - "id": 299, + "id": 295, "literal": "^1.0.0", "name": "parent-module", "npm": { "name": "parent-module", "version": ">=1.0.0 <2.0.0", }, - "package_id": 220, + "package_id": 216, }, { "behavior": { "normal": true, }, - "id": 300, + "id": 296, "literal": "^4.0.0", "name": "resolve-from", "npm": { "name": "resolve-from", "version": ">=4.0.0 <5.0.0", }, - "package_id": 219, + "package_id": 215, }, { "behavior": { "normal": true, }, - "id": 301, + "id": 297, "literal": "^3.0.0", "name": "callsites", "npm": { "name": "callsites", "version": ">=3.0.0 <4.0.0", }, - "package_id": 221, + "package_id": 217, }, { "behavior": { "normal": true, }, - "id": 302, + "id": 298, "literal": "1.6.0", "name": "busboy", "npm": { "name": "busboy", "version": "==1.6.0", }, - "package_id": 239, + "package_id": 235, }, { "behavior": { "normal": true, }, - "id": 303, + "id": 299, "literal": "8.4.31", "name": "postcss", "npm": { "name": "postcss", "version": "==8.4.31", }, - "package_id": 238, + "package_id": 234, }, { "behavior": { "normal": true, }, - "id": 304, + "id": 300, "literal": "14.1.3", "name": "@next/env", "npm": { "name": "@next/env", "version": "==14.1.3", }, - "package_id": 237, + "package_id": 233, }, { "behavior": { "normal": true, }, - "id": 305, + "id": 301, "literal": "5.1.1", "name": "styled-jsx", "npm": { "name": "styled-jsx", "version": "==5.1.1", }, - "package_id": 235, + "package_id": 231, }, { "behavior": { "normal": true, }, - "id": 306, + "id": 302, "literal": "^4.2.11", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.11 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 307, + "id": 303, "literal": "0.5.2", "name": "@swc/helpers", "npm": { "name": "@swc/helpers", "version": "==0.5.2", }, - "package_id": 234, + "package_id": 230, }, { "behavior": { "normal": true, }, - "id": 308, + "id": 304, "literal": "^1.0.30001579", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001579 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "optional": true, }, - "id": 309, + "id": 305, "literal": "14.1.3", "name": "@next/swc-darwin-x64", "npm": { "name": "@next/swc-darwin-x64", "version": "==14.1.3", }, - "package_id": 232, + "package_id": 228, }, { "behavior": { "optional": true, }, - "id": 310, + "id": 306, "literal": "14.1.3", "name": "@next/swc-darwin-arm64", "npm": { "name": "@next/swc-darwin-arm64", "version": "==14.1.3", }, - "package_id": 231, + "package_id": 227, }, { "behavior": { "optional": true, }, - "id": 311, + "id": 307, "literal": "14.1.3", "name": "@next/swc-linux-x64-gnu", "npm": { "name": "@next/swc-linux-x64-gnu", "version": "==14.1.3", }, - "package_id": 230, + "package_id": 226, }, { "behavior": { "optional": true, }, - "id": 312, + "id": 308, "literal": "14.1.3", "name": "@next/swc-linux-x64-musl", "npm": { "name": "@next/swc-linux-x64-musl", "version": "==14.1.3", }, - "package_id": 229, + "package_id": 225, }, { "behavior": { "optional": true, }, - "id": 313, + "id": 309, "literal": "14.1.3", "name": "@next/swc-win32-x64-msvc", "npm": { "name": "@next/swc-win32-x64-msvc", "version": "==14.1.3", }, - "package_id": 228, + "package_id": 224, }, { "behavior": { "optional": true, }, - "id": 314, + "id": 310, "literal": "14.1.3", "name": "@next/swc-linux-arm64-gnu", "npm": { "name": "@next/swc-linux-arm64-gnu", "version": "==14.1.3", }, - "package_id": 227, + "package_id": 223, }, { "behavior": { "optional": true, }, - "id": 315, + "id": 311, "literal": "14.1.3", "name": "@next/swc-win32-ia32-msvc", "npm": { "name": "@next/swc-win32-ia32-msvc", "version": "==14.1.3", }, - "package_id": 226, + "package_id": 222, }, { "behavior": { "optional": true, }, - "id": 316, + "id": 312, "literal": "14.1.3", "name": "@next/swc-linux-arm64-musl", "npm": { "name": "@next/swc-linux-arm64-musl", "version": "==14.1.3", }, - "package_id": 225, + "package_id": 221, }, { "behavior": { "optional": true, }, - "id": 317, + "id": 313, "literal": "14.1.3", "name": "@next/swc-win32-arm64-msvc", "npm": { "name": "@next/swc-win32-arm64-msvc", "version": "==14.1.3", }, - "package_id": 224, + "package_id": 220, }, { "behavior": { "optional": true, "peer": true, }, - "id": 318, + "id": 314, "literal": "^1.3.0", "name": "sass", "npm": { @@ -4165,7 +4112,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "optional": true, "peer": true, }, - "id": 319, + "id": 315, "literal": "^1.1.0", "name": "@opentelemetry/api", "npm": { @@ -4178,7 +4125,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "peer": true, }, - "id": 320, + "id": 316, "literal": "^18.2.0", "name": "react-dom", "npm": { @@ -4191,7 +4138,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "peer": true, }, - "id": 321, + "id": 317, "literal": "^18.2.0", "name": "react", "npm": { @@ -4204,33 +4151,33 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 322, + "id": 318, "literal": "^2.4.0", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.4.0 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 323, + "id": 319, "literal": "0.0.1", "name": "client-only", "npm": { "name": "client-only", "version": "==0.0.1", }, - "package_id": 236, + "package_id": 232, }, { "behavior": { "peer": true, }, - "id": 324, + "id": 320, "literal": ">= 16.8.0 || 17.x.x || ^18.0.0-0", "name": "react", "npm": { @@ -4243,7 +4190,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 325, + "id": 321, "literal": "^3.3.6", "name": "nanoid", "npm": { @@ -4256,7 +4203,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 326, + "id": 322, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -4269,7 +4216,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 327, + "id": 323, "literal": "^1.0.2", "name": "source-map-js", "npm": { @@ -4282,138 +4229,138 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 328, + "id": 324, "literal": "^1.1.0", "name": "streamsearch", "npm": { "name": "streamsearch", "version": ">=1.1.0 <2.0.0", }, - "package_id": 240, + "package_id": 236, }, { "behavior": { "normal": true, }, - "id": 329, + "id": 325, "literal": "^7.33.2", "name": "eslint-plugin-react", "npm": { "name": "eslint-plugin-react", "version": ">=7.33.2 <8.0.0", }, - "package_id": 433, + "package_id": 429, }, { "behavior": { "normal": true, }, - "id": 330, + "id": 326, "literal": "^2.28.1", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=2.28.1 <3.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 331, + "id": 327, "literal": "^6.7.1", "name": "eslint-plugin-jsx-a11y", "npm": { "name": "eslint-plugin-jsx-a11y", "version": ">=6.7.1 <7.0.0", }, - "package_id": 408, + "package_id": 404, }, { "behavior": { "normal": true, }, - "id": 332, + "id": 328, "literal": "^1.3.3", "name": "@rushstack/eslint-patch", "npm": { "name": "@rushstack/eslint-patch", "version": ">=1.3.3 <2.0.0", }, - "package_id": 407, + "package_id": 403, }, { "behavior": { "normal": true, }, - "id": 333, + "id": 329, "literal": "14.1.3", "name": "@next/eslint-plugin-next", "npm": { "name": "@next/eslint-plugin-next", "version": "==14.1.3", }, - "package_id": 406, + "package_id": 402, }, { "behavior": { "normal": true, }, - "id": 334, + "id": 330, "literal": "^5.4.2 || ^6.0.0", "name": "@typescript-eslint/parser", "npm": { "name": "@typescript-eslint/parser", "version": ">=5.4.2 <6.0.0 || >=6.0.0 <7.0.0 && >=6.0.0 <7.0.0", }, - "package_id": 394, + "package_id": 390, }, { "behavior": { "normal": true, }, - "id": 335, + "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", "name": "eslint-plugin-react-hooks", "npm": { "name": "eslint-plugin-react-hooks", "version": ">=4.5.0 <5.0.0 || ==5.0.0-canary-7118f5dd7-20230705 && ==5.0.0-canary-7118f5dd7-20230705", }, - "package_id": 393, + "package_id": 389, }, { "behavior": { "normal": true, }, - "id": 336, + "id": 332, "literal": "^0.3.6", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.6 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 337, + "id": 333, "literal": "^3.5.2", "name": "eslint-import-resolver-typescript", "npm": { "name": "eslint-import-resolver-typescript", "version": ">=3.5.2 <4.0.0", }, - "package_id": 306, + "package_id": 302, }, { "behavior": { "optional": true, "peer": true, }, - "id": 338, + "id": 334, "literal": ">=3.3.1", "name": "typescript", "npm": { @@ -4426,150 +4373,150 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "peer": true, }, - "id": 339, + "id": 335, "literal": "^7.23.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.23.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 340, + "id": 336, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 341, + "id": 337, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 342, + "id": 338, "literal": "^4.0.0", "name": "chalk", "npm": { "name": "chalk", "version": ">=4.0.0 <5.0.0", }, - "package_id": 303, + "package_id": 299, }, { "behavior": { "normal": true, }, - "id": 343, + "id": 339, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 344, + "id": 340, "literal": "^9.6.1", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.1 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 345, + "id": 341, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 346, + "id": 342, "literal": "^1.4.2", "name": "esquery", "npm": { "name": "esquery", "version": ">=1.4.2 <2.0.0", }, - "package_id": 302, + "package_id": 298, }, { "behavior": { "normal": true, }, - "id": 347, + "id": 343, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 348, + "id": 344, "literal": "^5.0.0", "name": "find-up", "npm": { "name": "find-up", "version": ">=5.0.0 <6.0.0", }, - "package_id": 296, + "package_id": 292, }, { "behavior": { "normal": true, }, - "id": 349, + "id": 345, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 350, + "id": 346, "literal": "^4.0.0", "name": "is-glob", "npm": { @@ -4582,85 +4529,85 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 351, + "id": 347, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 352, + "id": 348, "literal": "^3.0.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=3.0.0 <4.0.0", }, - "package_id": 295, + "package_id": 291, }, { "behavior": { "normal": true, }, - "id": 353, + "id": 349, "literal": "^1.4.0", "name": "graphemer", "npm": { "name": "graphemer", "version": ">=1.4.0 <2.0.0", }, - "package_id": 294, + "package_id": 290, }, { "behavior": { "normal": true, }, - "id": 354, + "id": 350, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 355, + "id": 351, "literal": "8.50.0", "name": "@eslint/js", "npm": { "name": "@eslint/js", "version": "==8.50.0", }, - "package_id": 293, + "package_id": 289, }, { "behavior": { "normal": true, }, - "id": 356, + "id": 352, "literal": "^0.9.3", "name": "optionator", "npm": { "name": "optionator", "version": ">=0.9.3 <0.10.0", }, - "package_id": 286, + "package_id": 282, }, { "behavior": { "normal": true, }, - "id": 357, + "id": 353, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -4673,20 +4620,20 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 358, + "id": 354, "literal": "^0.2.0", "name": "text-table", "npm": { "name": "text-table", "version": ">=0.2.0 <0.3.0", }, - "package_id": 285, + "package_id": 281, }, { "behavior": { "normal": true, }, - "id": 359, + "id": 355, "literal": "^7.0.2", "name": "cross-spawn", "npm": { @@ -4699,7 +4646,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 360, + "id": 356, "literal": "^6.0.2", "name": "glob-parent", "npm": { @@ -4712,98 +4659,98 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 361, + "id": 357, "literal": "^0.1.4", "name": "imurmurhash", "npm": { "name": "imurmurhash", "version": ">=0.1.4 <0.2.0", }, - "package_id": 284, + "package_id": 280, }, { "behavior": { "normal": true, }, - "id": 362, + "id": 358, "literal": "^7.2.2", "name": "eslint-scope", "npm": { "name": "eslint-scope", "version": ">=7.2.2 <8.0.0", }, - "package_id": 282, + "package_id": 278, }, { "behavior": { "normal": true, }, - "id": 363, + "id": 359, "literal": "^4.6.2", "name": "lodash.merge", "npm": { "name": "lodash.merge", "version": ">=4.6.2 <5.0.0", }, - "package_id": 281, + "package_id": 277, }, { "behavior": { "normal": true, }, - "id": 364, + "id": 360, "literal": "^3.0.3", "name": "is-path-inside", "npm": { "name": "is-path-inside", "version": ">=3.0.3 <4.0.0", }, - "package_id": 280, + "package_id": 276, }, { "behavior": { "normal": true, }, - "id": 365, + "id": 361, "literal": "^3.1.3", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.3 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 366, + "id": 362, "literal": "^1.4.0", "name": "natural-compare", "npm": { "name": "natural-compare", "version": ">=1.4.0 <2.0.0", }, - "package_id": 279, + "package_id": 275, }, { "behavior": { "normal": true, }, - "id": 367, + "id": 363, "literal": "^2.1.2", "name": "@eslint/eslintrc", "npm": { "name": "@eslint/eslintrc", "version": ">=2.1.2 <3.0.0", }, - "package_id": 265, + "package_id": 261, }, { "behavior": { "normal": true, }, - "id": 368, + "id": 364, "literal": "^1.2.8", "name": "@nodelib/fs.walk", "npm": { @@ -4816,189 +4763,189 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 369, + "id": 365, "literal": "^6.0.1", "name": "file-entry-cache", "npm": { "name": "file-entry-cache", "version": ">=6.0.1 <7.0.0", }, - "package_id": 254, + "package_id": 250, }, { "behavior": { "normal": true, }, - "id": 370, + "id": 366, "literal": "^3.4.3", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.3 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 371, + "id": 367, "literal": "^4.0.0", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=4.0.0 <5.0.0", }, - "package_id": 253, + "package_id": 249, }, { "behavior": { "normal": true, }, - "id": 372, + "id": 368, "literal": "^4.6.1", "name": "@eslint-community/regexpp", "npm": { "name": "@eslint-community/regexpp", "version": ">=4.6.1 <5.0.0", }, - "package_id": 252, + "package_id": 248, }, { "behavior": { "normal": true, }, - "id": 373, + "id": 369, "literal": "^0.11.11", "name": "@humanwhocodes/config-array", "npm": { "name": "@humanwhocodes/config-array", "version": ">=0.11.11 <0.12.0", }, - "package_id": 247, + "package_id": 243, }, { "behavior": { "normal": true, }, - "id": 374, + "id": 370, "literal": "^4.2.0", "name": "@eslint-community/eslint-utils", "npm": { "name": "@eslint-community/eslint-utils", "version": ">=4.2.0 <5.0.0", }, - "package_id": 245, + "package_id": 241, }, { "behavior": { "normal": true, }, - "id": 375, + "id": 371, "literal": "^1.0.1", "name": "@humanwhocodes/module-importer", "npm": { "name": "@humanwhocodes/module-importer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 244, + "package_id": 240, }, { "behavior": { "normal": true, }, - "id": 376, + "id": 372, "literal": "^1.0.1", "name": "json-stable-stringify-without-jsonify", "npm": { "name": "json-stable-stringify-without-jsonify", "version": ">=1.0.1 <2.0.0", }, - "package_id": 243, + "package_id": 239, }, { "behavior": { "normal": true, }, - "id": 377, + "id": 373, "literal": "^3.3.0", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.3.0 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 378, + "id": 374, "literal": "^6.0.0 || ^7.0.0 || >=8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 && >=8.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 379, + "id": 375, "literal": "^2.0.2", "name": "@humanwhocodes/object-schema", "npm": { "name": "@humanwhocodes/object-schema", "version": ">=2.0.2 <3.0.0", }, - "package_id": 251, + "package_id": 247, }, { "behavior": { "normal": true, }, - "id": 380, + "id": 376, "literal": "^4.3.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 381, + "id": 377, "literal": "^3.0.5", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.0.5 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 382, + "id": 378, "literal": "^1.1.7", "name": "brace-expansion", "npm": { "name": "brace-expansion", "version": ">=1.1.7 <2.0.0", }, - "package_id": 249, + "package_id": 245, }, { "behavior": { "normal": true, }, - "id": 383, + "id": 379, "literal": "^1.0.0", "name": "balanced-match", "npm": { @@ -5011,696 +4958,696 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 384, + "id": 380, "literal": "0.0.1", "name": "concat-map", "npm": { "name": "concat-map", "version": "==0.0.1", }, - "package_id": 250, + "package_id": 246, }, { "behavior": { "normal": true, }, - "id": 385, + "id": 381, "literal": "^3.0.4", "name": "flat-cache", "npm": { "name": "flat-cache", "version": ">=3.0.4 <4.0.0", }, - "package_id": 255, + "package_id": 251, }, { "behavior": { "normal": true, }, - "id": 386, + "id": 382, "literal": "^3.2.9", "name": "flatted", "npm": { "name": "flatted", "version": ">=3.2.9 <4.0.0", }, - "package_id": 264, + "package_id": 260, }, { "behavior": { "normal": true, }, - "id": 387, + "id": 383, "literal": "^4.5.3", "name": "keyv", "npm": { "name": "keyv", "version": ">=4.5.3 <5.0.0", }, - "package_id": 262, + "package_id": 258, }, { "behavior": { "normal": true, }, - "id": 388, + "id": 384, "literal": "^3.0.2", "name": "rimraf", "npm": { "name": "rimraf", "version": ">=3.0.2 <4.0.0", }, - "package_id": 256, + "package_id": 252, }, { "behavior": { "normal": true, }, - "id": 389, + "id": 385, "literal": "^7.1.3", "name": "glob", "npm": { "name": "glob", "version": ">=7.1.3 <8.0.0", }, - "package_id": 257, + "package_id": 253, }, { "behavior": { "normal": true, }, - "id": 390, + "id": 386, "literal": "^1.0.0", "name": "fs.realpath", "npm": { "name": "fs.realpath", "version": ">=1.0.0 <2.0.0", }, - "package_id": 261, + "package_id": 257, }, { "behavior": { "normal": true, }, - "id": 391, + "id": 387, "literal": "^1.0.4", "name": "inflight", "npm": { "name": "inflight", "version": ">=1.0.4 <2.0.0", }, - "package_id": 260, + "package_id": 256, }, { "behavior": { "normal": true, }, - "id": 392, + "id": 388, "literal": "2", "name": "inherits", "npm": { "name": "inherits", "version": "<3.0.0 >=2.0.0", }, - "package_id": 259, + "package_id": 255, }, { "behavior": { "normal": true, }, - "id": 393, + "id": 389, "literal": "^3.1.1", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.1 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 394, + "id": 390, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 395, + "id": 391, "literal": "^1.0.0", "name": "path-is-absolute", "npm": { "name": "path-is-absolute", "version": ">=1.0.0 <2.0.0", }, - "package_id": 258, + "package_id": 254, }, { "behavior": { "normal": true, }, - "id": 396, + "id": 392, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 397, + "id": 393, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 398, + "id": 394, "literal": "3.0.1", "name": "json-buffer", "npm": { "name": "json-buffer", "version": "==3.0.1", }, - "package_id": 263, + "package_id": 259, }, { "behavior": { "normal": true, }, - "id": 399, + "id": 395, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 400, + "id": 396, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 401, + "id": 397, "literal": "^9.6.0", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.0 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 402, + "id": 398, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 403, + "id": 399, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 404, + "id": 400, "literal": "^3.2.1", "name": "import-fresh", "npm": { "name": "import-fresh", "version": ">=3.2.1 <4.0.0", }, - "package_id": 218, + "package_id": 214, }, { "behavior": { "normal": true, }, - "id": 405, + "id": 401, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 406, + "id": 402, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 407, + "id": 403, "literal": "^3.1.1", "name": "strip-json-comments", "npm": { "name": "strip-json-comments", "version": ">=3.1.1 <4.0.0", }, - "package_id": 266, + "package_id": 262, }, { "behavior": { "normal": true, }, - "id": 408, + "id": 404, "literal": "^0.20.2", "name": "type-fest", "npm": { "name": "type-fest", "version": ">=0.20.2 <0.21.0", }, - "package_id": 269, + "package_id": 265, }, { "behavior": { "normal": true, }, - "id": 409, + "id": 405, "literal": "^8.9.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=8.9.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 410, + "id": 406, "literal": "^5.3.2", "name": "acorn-jsx", "npm": { "name": "acorn-jsx", "version": ">=5.3.2 <6.0.0", }, - "package_id": 271, + "package_id": 267, }, { "behavior": { "normal": true, }, - "id": 411, + "id": 407, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 412, + "id": 408, "literal": "^6.0.0 || ^7.0.0 || ^8.0.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 413, + "id": 409, "literal": "^3.1.1", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.1 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 414, + "id": 410, "literal": "^2.0.0", "name": "fast-json-stable-stringify", "npm": { "name": "fast-json-stable-stringify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 277, + "package_id": 273, }, { "behavior": { "normal": true, }, - "id": 415, + "id": 411, "literal": "^0.4.1", "name": "json-schema-traverse", "npm": { "name": "json-schema-traverse", "version": ">=0.4.1 <0.5.0", }, - "package_id": 276, + "package_id": 272, }, { "behavior": { "normal": true, }, - "id": 416, + "id": 412, "literal": "^4.2.2", "name": "uri-js", "npm": { "name": "uri-js", "version": ">=4.2.2 <5.0.0", }, - "package_id": 274, + "package_id": 270, }, { "behavior": { "normal": true, }, - "id": 417, + "id": 413, "literal": "^2.1.0", "name": "punycode", "npm": { "name": "punycode", "version": ">=2.1.0 <3.0.0", }, - "package_id": 275, + "package_id": 271, }, { "behavior": { "normal": true, }, - "id": 418, + "id": 414, "literal": "^4.3.0", "name": "esrecurse", "npm": { "name": "esrecurse", "version": ">=4.3.0 <5.0.0", }, - "package_id": 283, + "package_id": 279, }, { "behavior": { "normal": true, }, - "id": 419, + "id": 415, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 420, + "id": 416, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 421, + "id": 417, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 422, + "id": 418, "literal": "^0.1.3", "name": "deep-is", "npm": { "name": "deep-is", "version": ">=0.1.3 <0.2.0", }, - "package_id": 292, + "package_id": 288, }, { "behavior": { "normal": true, }, - "id": 423, + "id": 419, "literal": "^1.2.5", "name": "word-wrap", "npm": { "name": "word-wrap", "version": ">=1.2.5 <2.0.0", }, - "package_id": 291, + "package_id": 287, }, { "behavior": { "normal": true, }, - "id": 424, + "id": 420, "literal": "^0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 425, + "id": 421, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 426, + "id": 422, "literal": "^2.0.6", "name": "fast-levenshtein", "npm": { "name": "fast-levenshtein", "version": ">=2.0.6 <3.0.0", }, - "package_id": 287, + "package_id": 283, }, { "behavior": { "normal": true, }, - "id": 427, + "id": 423, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 428, + "id": 424, "literal": "~0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 429, + "id": 425, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 430, + "id": 426, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 431, + "id": 427, "literal": "^6.0.0", "name": "locate-path", "npm": { "name": "locate-path", "version": ">=6.0.0 <7.0.0", }, - "package_id": 298, + "package_id": 294, }, { "behavior": { "normal": true, }, - "id": 432, + "id": 428, "literal": "^4.0.0", "name": "path-exists", "npm": { "name": "path-exists", "version": ">=4.0.0 <5.0.0", }, - "package_id": 297, + "package_id": 293, }, { "behavior": { "normal": true, }, - "id": 433, + "id": 429, "literal": "^5.0.0", "name": "p-locate", "npm": { "name": "p-locate", "version": ">=5.0.0 <6.0.0", }, - "package_id": 299, + "package_id": 295, }, { "behavior": { "normal": true, }, - "id": 434, + "id": 430, "literal": "^3.0.2", "name": "p-limit", "npm": { "name": "p-limit", "version": ">=3.0.2 <4.0.0", }, - "package_id": 300, + "package_id": 296, }, { "behavior": { "normal": true, }, - "id": 435, + "id": 431, "literal": "^0.1.0", "name": "yocto-queue", "npm": { "name": "yocto-queue", "version": ">=0.1.0 <0.2.0", }, - "package_id": 301, + "package_id": 297, }, { "behavior": { "normal": true, }, - "id": 436, + "id": 432, "literal": "^5.1.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.1.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 437, + "id": 433, "literal": "^4.1.0", "name": "ansi-styles", "npm": { @@ -5713,72 +5660,72 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 438, + "id": 434, "literal": "^7.1.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=7.1.0 <8.0.0", }, - "package_id": 304, + "package_id": 300, }, { "behavior": { "normal": true, }, - "id": 439, + "id": 435, "literal": "^4.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=4.0.0 <5.0.0", }, - "package_id": 305, + "package_id": 301, }, { "behavior": { "normal": true, }, - "id": 440, + "id": 436, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 441, + "id": 437, "literal": "^5.12.0", "name": "enhanced-resolve", "npm": { "name": "enhanced-resolve", "version": ">=5.12.0 <6.0.0", }, - "package_id": 391, + "package_id": 387, }, { "behavior": { "normal": true, }, - "id": 442, + "id": 438, "literal": "^2.7.4", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.7.4 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 443, + "id": 439, "literal": "^3.3.1", "name": "fast-glob", "npm": { @@ -5791,20 +5738,20 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 444, + "id": 440, "literal": "^4.5.0", "name": "get-tsconfig", "npm": { "name": "get-tsconfig", "version": ">=4.5.0 <5.0.0", }, - "package_id": 389, + "package_id": 385, }, { "behavior": { "normal": true, }, - "id": 445, + "id": 441, "literal": "^2.11.0", "name": "is-core-module", "npm": { @@ -5817,7 +5764,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 446, + "id": 442, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -5830,137 +5777,137 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "peer": true, }, - "id": 447, + "id": 443, "literal": "*", "name": "eslint", "npm": { "name": "eslint", "version": ">=0.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "peer": true, }, - "id": 448, + "id": 444, "literal": "*", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=0.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 449, + "id": 445, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 450, + "id": 446, "literal": "^1.2.3", "name": "array.prototype.findlastindex", "npm": { "name": "array.prototype.findlastindex", "version": ">=1.2.3 <2.0.0", }, - "package_id": 387, + "package_id": 383, }, { "behavior": { "normal": true, }, - "id": 451, + "id": 447, "literal": "^1.3.2", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.2 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 452, + "id": 448, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 453, + "id": 449, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 454, + "id": 450, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 455, + "id": 451, "literal": "^0.3.9", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.9 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 456, + "id": 452, "literal": "^2.8.0", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.8.0 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 457, + "id": 453, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -5973,7 +5920,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 458, + "id": 454, "literal": "^2.13.1", "name": "is-core-module", "npm": { @@ -5986,7 +5933,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 459, + "id": 455, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -5999,293 +5946,293 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 460, + "id": 456, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 461, + "id": 457, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 462, + "id": 458, "literal": "^1.0.1", "name": "object.groupby", "npm": { "name": "object.groupby", "version": ">=1.0.1 <2.0.0", }, - "package_id": 328, + "package_id": 324, }, { "behavior": { "normal": true, }, - "id": 463, + "id": 459, "literal": "^1.1.7", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.7 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 464, + "id": 460, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 465, + "id": 461, "literal": "^3.15.0", "name": "tsconfig-paths", "npm": { "name": "tsconfig-paths", "version": ">=3.15.0 <4.0.0", }, - "package_id": 308, + "package_id": 304, }, { "behavior": { "peer": true, }, - "id": 466, + "id": 462, "literal": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=2.0.0 <3.0.0 || >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 467, + "id": 463, "literal": "^0.0.29", "name": "@types/json5", "npm": { "name": "@types/json5", "version": ">=0.0.29 <0.0.30", }, - "package_id": 312, + "package_id": 308, }, { "behavior": { "normal": true, }, - "id": 468, + "id": 464, "literal": "^1.0.2", "name": "json5", "npm": { "name": "json5", "version": ">=1.0.2 <2.0.0", }, - "package_id": 311, + "package_id": 307, }, { "behavior": { "normal": true, }, - "id": 469, + "id": 465, "literal": "^1.2.6", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.6 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 470, + "id": 466, "literal": "^3.0.0", "name": "strip-bom", "npm": { "name": "strip-bom", "version": ">=3.0.0 <4.0.0", }, - "package_id": 309, + "package_id": 305, }, { "behavior": { "normal": true, }, - "id": 471, + "id": 467, "literal": "^1.2.0", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.0 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 472, + "id": 468, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 473, + "id": 469, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 474, + "id": 470, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 475, + "id": 471, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 476, + "id": 472, "literal": "^1.0.1", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.0.1 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 477, + "id": 473, "literal": "^1.0.0", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.0 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 478, + "id": 474, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 479, + "id": 475, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 480, + "id": 476, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 481, + "id": 477, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 482, + "id": 478, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6298,33 +6245,33 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 483, + "id": 479, "literal": "^1.0.1", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.1 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 484, + "id": 480, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 485, + "id": 481, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -6337,85 +6284,85 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 486, + "id": 482, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 487, + "id": 483, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 488, + "id": 484, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 489, + "id": 485, "literal": "^1.1.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.1.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 490, + "id": 486, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 491, + "id": 487, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 492, + "id": 488, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6428,59 +6375,59 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 493, + "id": 489, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 494, + "id": 490, "literal": "^1.2.1", "name": "set-function-length", "npm": { "name": "set-function-length", "version": ">=1.2.1 <2.0.0", }, - "package_id": 327, + "package_id": 323, }, { "behavior": { "normal": true, }, - "id": 495, + "id": 491, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 496, + "id": 492, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 497, + "id": 493, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6493,345 +6440,345 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 498, + "id": 494, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 499, + "id": 495, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 500, + "id": 496, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 501, + "id": 497, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 502, + "id": 498, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 503, + "id": 499, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 504, + "id": 500, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 505, + "id": 501, "literal": "^1.0.3", "name": "arraybuffer.prototype.slice", "npm": { "name": "arraybuffer.prototype.slice", "version": ">=1.0.3 <2.0.0", }, - "package_id": 377, + "package_id": 373, }, { "behavior": { "normal": true, }, - "id": 506, + "id": 502, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 507, + "id": 503, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 508, + "id": 504, "literal": "^1.0.1", "name": "data-view-buffer", "npm": { "name": "data-view-buffer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 376, + "package_id": 372, }, { "behavior": { "normal": true, }, - "id": 509, + "id": 505, "literal": "^1.0.1", "name": "data-view-byte-length", "npm": { "name": "data-view-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 375, + "package_id": 371, }, { "behavior": { "normal": true, }, - "id": 510, + "id": 506, "literal": "^1.0.0", "name": "data-view-byte-offset", "npm": { "name": "data-view-byte-offset", "version": ">=1.0.0 <2.0.0", }, - "package_id": 374, + "package_id": 370, }, { "behavior": { "normal": true, }, - "id": 511, + "id": 507, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 512, + "id": 508, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 513, + "id": 509, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 514, + "id": 510, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 515, + "id": 511, "literal": "^1.2.1", "name": "es-to-primitive", "npm": { "name": "es-to-primitive", "version": ">=1.2.1 <2.0.0", }, - "package_id": 371, + "package_id": 367, }, { "behavior": { "normal": true, }, - "id": 516, + "id": 512, "literal": "^1.1.6", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.6 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 517, + "id": 513, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 518, + "id": 514, "literal": "^1.0.2", "name": "get-symbol-description", "npm": { "name": "get-symbol-description", "version": ">=1.0.2 <2.0.0", }, - "package_id": 369, + "package_id": 365, }, { "behavior": { "normal": true, }, - "id": 519, + "id": 515, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 520, + "id": 516, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 521, + "id": 517, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 522, + "id": 518, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 523, + "id": 519, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 524, + "id": 520, "literal": "^2.0.2", "name": "hasown", "npm": { @@ -6844,1385 +6791,1385 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 525, + "id": 521, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 526, + "id": 522, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 527, + "id": 523, "literal": "^1.2.7", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.2.7 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 528, + "id": 524, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 529, + "id": 525, "literal": "^2.0.3", "name": "is-negative-zero", "npm": { "name": "is-negative-zero", "version": ">=2.0.3 <3.0.0", }, - "package_id": 363, + "package_id": 359, }, { "behavior": { "normal": true, }, - "id": 530, + "id": 526, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 531, + "id": 527, "literal": "^1.0.3", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.3 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 532, + "id": 528, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 533, + "id": 529, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 534, + "id": 530, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 535, + "id": 531, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 536, + "id": 532, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 537, + "id": 533, "literal": "^4.1.5", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.5 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 538, + "id": 534, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 539, + "id": 535, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 540, + "id": 536, "literal": "^1.0.3", "name": "safe-regex-test", "npm": { "name": "safe-regex-test", "version": ">=1.0.3 <2.0.0", }, - "package_id": 352, + "package_id": 348, }, { "behavior": { "normal": true, }, - "id": 541, + "id": 537, "literal": "^1.2.9", "name": "string.prototype.trim", "npm": { "name": "string.prototype.trim", "version": ">=1.2.9 <2.0.0", }, - "package_id": 351, + "package_id": 347, }, { "behavior": { "normal": true, }, - "id": 542, + "id": 538, "literal": "^1.0.8", "name": "string.prototype.trimend", "npm": { "name": "string.prototype.trimend", "version": ">=1.0.8 <2.0.0", }, - "package_id": 350, + "package_id": 346, }, { "behavior": { "normal": true, }, - "id": 543, + "id": 539, "literal": "^1.0.8", "name": "string.prototype.trimstart", "npm": { "name": "string.prototype.trimstart", "version": ">=1.0.8 <2.0.0", }, - "package_id": 349, + "package_id": 345, }, { "behavior": { "normal": true, }, - "id": 544, + "id": 540, "literal": "^1.0.2", "name": "typed-array-buffer", "npm": { "name": "typed-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 348, + "package_id": 344, }, { "behavior": { "normal": true, }, - "id": 545, + "id": 541, "literal": "^1.0.1", "name": "typed-array-byte-length", "npm": { "name": "typed-array-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 347, + "package_id": 343, }, { "behavior": { "normal": true, }, - "id": 546, + "id": 542, "literal": "^1.0.2", "name": "typed-array-byte-offset", "npm": { "name": "typed-array-byte-offset", "version": ">=1.0.2 <2.0.0", }, - "package_id": 346, + "package_id": 342, }, { "behavior": { "normal": true, }, - "id": 547, + "id": 543, "literal": "^1.0.6", "name": "typed-array-length", "npm": { "name": "typed-array-length", "version": ">=1.0.6 <2.0.0", }, - "package_id": 344, + "package_id": 340, }, { "behavior": { "normal": true, }, - "id": 548, + "id": 544, "literal": "^1.0.2", "name": "unbox-primitive", "npm": { "name": "unbox-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 336, + "package_id": 332, }, { "behavior": { "normal": true, }, - "id": 549, + "id": 545, "literal": "^1.1.15", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.15 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 550, + "id": 546, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 551, + "id": 547, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 552, + "id": 548, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 553, + "id": 549, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 554, + "id": 550, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 555, + "id": 551, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 556, + "id": 552, "literal": "^1.1.3", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.3 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 557, + "id": 553, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 558, + "id": 554, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 559, + "id": 555, "literal": "^1.0.2", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.2 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 560, + "id": 556, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 561, + "id": 557, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 562, + "id": 558, "literal": "^1.0.1", "name": "is-bigint", "npm": { "name": "is-bigint", "version": ">=1.0.1 <2.0.0", }, - "package_id": 342, + "package_id": 338, }, { "behavior": { "normal": true, }, - "id": 563, + "id": 559, "literal": "^1.1.0", "name": "is-boolean-object", "npm": { "name": "is-boolean-object", "version": ">=1.1.0 <2.0.0", }, - "package_id": 341, + "package_id": 337, }, { "behavior": { "normal": true, }, - "id": 564, + "id": 560, "literal": "^1.0.4", "name": "is-number-object", "npm": { "name": "is-number-object", "version": ">=1.0.4 <2.0.0", }, - "package_id": 340, + "package_id": 336, }, { "behavior": { "normal": true, }, - "id": 565, + "id": 561, "literal": "^1.0.5", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.5 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 566, + "id": 562, "literal": "^1.0.3", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.3 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 567, + "id": 563, "literal": "^1.0.2", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.2 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 568, + "id": 564, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 569, + "id": 565, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 570, + "id": 566, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 571, + "id": 567, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 572, + "id": 568, "literal": "^1.0.1", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.1 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 573, + "id": 569, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 574, + "id": 570, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 575, + "id": 571, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 576, + "id": 572, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 577, + "id": 573, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 578, + "id": 574, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 579, + "id": 575, "literal": "^1.1.14", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.14 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 580, + "id": 576, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 581, + "id": 577, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 582, + "id": 578, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 583, + "id": 579, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 584, + "id": 580, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 585, + "id": 581, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 586, + "id": 582, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 587, + "id": 583, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 588, + "id": 584, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 589, + "id": 585, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 590, + "id": 586, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 591, + "id": 587, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 592, + "id": 588, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 593, + "id": 589, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 594, + "id": 590, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 595, + "id": 591, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 596, + "id": 592, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 597, + "id": 593, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 598, + "id": 594, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 599, + "id": 595, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 600, + "id": 596, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 601, + "id": 597, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 602, + "id": 598, "literal": "^1.23.0", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.0 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 603, + "id": 599, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 604, + "id": 600, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 605, + "id": 601, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 606, + "id": 602, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 607, + "id": 603, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 608, + "id": 604, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 609, + "id": 605, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 610, + "id": 606, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 611, + "id": 607, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 612, + "id": 608, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 613, + "id": 609, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 614, + "id": 610, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 615, + "id": 611, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 616, + "id": 612, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 617, + "id": 613, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 618, + "id": 614, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 619, + "id": 615, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 620, + "id": 616, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 621, + "id": 617, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 622, + "id": 618, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 623, + "id": 619, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 624, + "id": 620, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 625, + "id": 621, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 626, + "id": 622, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 627, + "id": 623, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 628, + "id": 624, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 629, + "id": 625, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 630, + "id": 626, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 631, + "id": 627, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -8235,267 +8182,267 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 632, + "id": 628, "literal": "^1.0.4", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.4 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 633, + "id": 629, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 634, + "id": 630, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 635, + "id": 631, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 636, + "id": 632, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 637, + "id": 633, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 638, + "id": 634, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 639, + "id": 635, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 640, + "id": 636, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 641, + "id": 637, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 642, + "id": 638, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 643, + "id": 639, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 644, + "id": 640, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 645, + "id": 641, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 646, + "id": 642, "literal": "^1.1.4", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.4 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 647, + "id": 643, "literal": "^1.0.1", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.1 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 648, + "id": 644, "literal": "^1.0.2", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.2 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 649, + "id": 645, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 650, + "id": 646, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 651, + "id": 647, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 652, + "id": 648, "literal": "^2.0.1", "name": "hasown", "npm": { @@ -8508,345 +8455,345 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 653, + "id": 649, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 654, + "id": 650, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 655, + "id": 651, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 656, + "id": 652, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 657, + "id": 653, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 658, + "id": 654, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 659, + "id": 655, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 660, + "id": 656, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 661, + "id": 657, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 662, + "id": 658, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 663, + "id": 659, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 664, + "id": 660, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 665, + "id": 661, "literal": "^1.22.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 666, + "id": 662, "literal": "^1.2.1", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.2.1 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 667, + "id": 663, "literal": "^1.2.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 668, + "id": 664, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 669, + "id": 665, "literal": "^1.0.2", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 670, + "id": 666, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 671, + "id": 667, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 672, + "id": 668, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 673, + "id": 669, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 674, + "id": 670, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 675, + "id": 671, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 676, + "id": 672, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 677, + "id": 673, "literal": "^2.1.1", "name": "ms", "npm": { "name": "ms", "version": ">=2.1.1 <3.0.0", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 678, + "id": 674, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 679, + "id": 675, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -8859,7 +8806,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 680, + "id": 676, "literal": "^1.22.4", "name": "resolve", "npm": { @@ -8872,72 +8819,72 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 681, + "id": 677, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 682, + "id": 678, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 683, + "id": 679, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 684, + "id": 680, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 685, + "id": 681, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 686, + "id": 682, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -8950,384 +8897,384 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 687, + "id": 683, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 688, + "id": 684, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 689, + "id": 685, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 690, + "id": 686, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 691, + "id": 687, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 692, + "id": 688, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 693, + "id": 689, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 694, + "id": 690, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 695, + "id": 691, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 696, + "id": 692, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 697, + "id": 693, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 698, + "id": 694, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 699, + "id": 695, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 700, + "id": 696, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 701, + "id": 697, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 702, + "id": 698, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 703, + "id": 699, "literal": "^1.0.0", "name": "resolve-pkg-maps", "npm": { "name": "resolve-pkg-maps", "version": ">=1.0.0 <2.0.0", }, - "package_id": 390, + "package_id": 386, }, { "behavior": { "normal": true, }, - "id": 704, + "id": 700, "literal": "^4.2.4", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.4 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 705, + "id": 701, "literal": "^2.2.0", "name": "tapable", "npm": { "name": "tapable", "version": ">=2.2.0 <3.0.0", }, - "package_id": 392, + "package_id": 388, }, { "behavior": { "peer": true, }, - "id": 706, + "id": 702, "literal": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=8.0.0-0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 707, + "id": 703, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 708, + "id": 704, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 709, + "id": 705, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 710, + "id": 706, "literal": "6.21.0", "name": "@typescript-eslint/scope-manager", "npm": { "name": "@typescript-eslint/scope-manager", "version": "==6.21.0", }, - "package_id": 405, + "package_id": 401, }, { "behavior": { "normal": true, }, - "id": 711, + "id": 707, "literal": "6.21.0", "name": "@typescript-eslint/typescript-estree", "npm": { "name": "@typescript-eslint/typescript-estree", "version": "==6.21.0", }, - "package_id": 395, + "package_id": 391, }, { "behavior": { "peer": true, }, - "id": 712, + "id": 708, "literal": "^7.0.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 713, + "id": 709, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 714, + "id": 710, "literal": "^11.1.0", "name": "globby", "npm": { "name": "globby", "version": ">=11.1.0 <12.0.0", }, - "package_id": 400, + "package_id": 396, }, { "behavior": { "normal": true, }, - "id": 715, + "id": 711, "literal": "^7.5.4", "name": "semver", "npm": { "name": "semver", "version": ">=7.5.4 <8.0.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 716, + "id": 712, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -9340,85 +9287,85 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 717, + "id": 713, "literal": "9.0.3", "name": "minimatch", "npm": { "name": "minimatch", "version": "==9.0.3", }, - "package_id": 399, + "package_id": 395, }, { "behavior": { "normal": true, }, - "id": 718, + "id": 714, "literal": "^1.0.1", "name": "ts-api-utils", "npm": { "name": "ts-api-utils", "version": ">=1.0.1 <2.0.0", }, - "package_id": 398, + "package_id": 394, }, { "behavior": { "normal": true, }, - "id": 719, + "id": 715, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 720, + "id": 716, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 721, + "id": 717, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 722, + "id": 718, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "peer": true, }, - "id": 723, + "id": 719, "literal": ">=4.2.0", "name": "typescript", "npm": { @@ -9431,7 +9378,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 724, + "id": 720, "literal": "^2.0.1", "name": "brace-expansion", "npm": { @@ -9444,33 +9391,33 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 725, + "id": 721, "literal": "^2.1.0", "name": "array-union", "npm": { "name": "array-union", "version": ">=2.1.0 <3.0.0", }, - "package_id": 404, + "package_id": 400, }, { "behavior": { "normal": true, }, - "id": 726, + "id": 722, "literal": "^3.0.1", "name": "dir-glob", "npm": { "name": "dir-glob", "version": ">=3.0.1 <4.0.0", }, - "package_id": 402, + "package_id": 398, }, { "behavior": { "normal": true, }, - "id": 727, + "id": 723, "literal": "^3.2.9", "name": "fast-glob", "npm": { @@ -9483,20 +9430,20 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 728, + "id": 724, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 729, + "id": 725, "literal": "^1.4.1", "name": "merge2", "npm": { @@ -9509,59 +9456,59 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 730, + "id": 726, "literal": "^3.0.0", "name": "slash", "npm": { "name": "slash", "version": ">=3.0.0 <4.0.0", }, - "package_id": 401, + "package_id": 397, }, { "behavior": { "normal": true, }, - "id": 731, + "id": 727, "literal": "^4.0.0", "name": "path-type", "npm": { "name": "path-type", "version": ">=4.0.0 <5.0.0", }, - "package_id": 403, + "package_id": 399, }, { "behavior": { "normal": true, }, - "id": 732, + "id": 728, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 733, + "id": 729, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 734, + "id": 730, "literal": "10.3.10", "name": "glob", "npm": { @@ -9574,111 +9521,111 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 735, + "id": 731, "literal": "^7.23.2", "name": "@babel/runtime", "npm": { "name": "@babel/runtime", "version": ">=7.23.2 <8.0.0", }, - "package_id": 431, + "package_id": 427, }, { "behavior": { "normal": true, }, - "id": 736, + "id": 732, "literal": "^5.3.0", "name": "aria-query", "npm": { "name": "aria-query", "version": ">=5.3.0 <6.0.0", }, - "package_id": 430, + "package_id": 426, }, { "behavior": { "normal": true, }, - "id": 737, + "id": 733, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 738, + "id": 734, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 739, + "id": 735, "literal": "^0.0.8", "name": "ast-types-flow", "npm": { "name": "ast-types-flow", "version": ">=0.0.8 <0.0.9", }, - "package_id": 429, + "package_id": 425, }, { "behavior": { "normal": true, }, - "id": 740, + "id": 736, "literal": "=4.7.0", "name": "axe-core", "npm": { "name": "axe-core", "version": "==4.7.0", }, - "package_id": 428, + "package_id": 424, }, { "behavior": { "normal": true, }, - "id": 741, + "id": 737, "literal": "^3.2.1", "name": "axobject-query", "npm": { "name": "axobject-query", "version": ">=3.2.1 <4.0.0", }, - "package_id": 426, + "package_id": 422, }, { "behavior": { "normal": true, }, - "id": 742, + "id": 738, "literal": "^1.0.8", "name": "damerau-levenshtein", "npm": { "name": "damerau-levenshtein", "version": ">=1.0.8 <2.0.0", }, - "package_id": 425, + "package_id": 421, }, { "behavior": { "normal": true, }, - "id": 743, + "id": 739, "literal": "^9.2.2", "name": "emoji-regex", "npm": { @@ -9691,20 +9638,20 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 744, + "id": 740, "literal": "^1.0.15", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.15 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 745, + "id": 741, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -9717,254 +9664,254 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 746, + "id": 742, "literal": "^3.3.5", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=3.3.5 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 747, + "id": 743, "literal": "^1.0.9", "name": "language-tags", "npm": { "name": "language-tags", "version": ">=1.0.9 <2.0.0", }, - "package_id": 410, + "package_id": 406, }, { "behavior": { "normal": true, }, - "id": 748, + "id": 744, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 749, + "id": 745, "literal": "^1.1.7", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.7 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 750, + "id": 746, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "peer": true, }, - "id": 751, + "id": 747, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 752, + "id": 748, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 753, + "id": 749, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 754, + "id": 750, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 755, + "id": 751, "literal": "^0.3.20", "name": "language-subtag-registry", "npm": { "name": "language-subtag-registry", "version": ">=0.3.20 <0.4.0", }, - "package_id": 411, + "package_id": 407, }, { "behavior": { "normal": true, }, - "id": 756, + "id": 752, "literal": "^3.1.6", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.6 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 757, + "id": 753, "literal": "^1.3.1", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.1 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 758, + "id": 754, "literal": "^4.1.4", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.4 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 759, + "id": 755, "literal": "^1.1.6", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.6 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 760, + "id": 756, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 761, + "id": 757, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 762, + "id": 758, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 763, + "id": 759, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 764, + "id": 760, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 765, + "id": 761, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -9977,982 +9924,982 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 766, + "id": 762, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 767, + "id": 763, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 768, + "id": 764, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 769, + "id": 765, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 770, + "id": 766, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 771, + "id": 767, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 772, + "id": 768, "literal": "^1.1.2", "name": "iterator.prototype", "npm": { "name": "iterator.prototype", "version": ">=1.1.2 <2.0.0", }, - "package_id": 414, + "package_id": 410, }, { "behavior": { "normal": true, }, - "id": 773, + "id": 769, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 774, + "id": 770, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 775, + "id": 771, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 776, + "id": 772, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 777, + "id": 773, "literal": "^1.0.4", "name": "reflect.getprototypeof", "npm": { "name": "reflect.getprototypeof", "version": ">=1.0.4 <2.0.0", }, - "package_id": 415, + "package_id": 411, }, { "behavior": { "normal": true, }, - "id": 778, + "id": 774, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 779, + "id": 775, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 780, + "id": 776, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 781, + "id": 777, "literal": "^1.23.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 782, + "id": 778, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 783, + "id": 779, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 784, + "id": 780, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 785, + "id": 781, "literal": "^1.1.3", "name": "which-builtin-type", "npm": { "name": "which-builtin-type", "version": ">=1.1.3 <2.0.0", }, - "package_id": 416, + "package_id": 412, }, { "behavior": { "normal": true, }, - "id": 786, + "id": 782, "literal": "^1.1.5", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.5 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 787, + "id": 783, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 788, + "id": 784, "literal": "^2.0.0", "name": "is-async-function", "npm": { "name": "is-async-function", "version": ">=2.0.0 <3.0.0", }, - "package_id": 424, + "package_id": 420, }, { "behavior": { "normal": true, }, - "id": 789, + "id": 785, "literal": "^1.0.5", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.5 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 790, + "id": 786, "literal": "^1.0.2", "name": "is-finalizationregistry", "npm": { "name": "is-finalizationregistry", "version": ">=1.0.2 <2.0.0", }, - "package_id": 423, + "package_id": 419, }, { "behavior": { "normal": true, }, - "id": 791, + "id": 787, "literal": "^1.0.10", "name": "is-generator-function", "npm": { "name": "is-generator-function", "version": ">=1.0.10 <2.0.0", }, - "package_id": 422, + "package_id": 418, }, { "behavior": { "normal": true, }, - "id": 792, + "id": 788, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 793, + "id": 789, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 794, + "id": 790, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 795, + "id": 791, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 796, + "id": 792, "literal": "^1.0.1", "name": "which-collection", "npm": { "name": "which-collection", "version": ">=1.0.1 <2.0.0", }, - "package_id": 417, + "package_id": 413, }, { "behavior": { "normal": true, }, - "id": 797, + "id": 793, "literal": "^1.1.9", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.9 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 798, + "id": 794, "literal": "^2.0.3", "name": "is-map", "npm": { "name": "is-map", "version": ">=2.0.3 <3.0.0", }, - "package_id": 421, + "package_id": 417, }, { "behavior": { "normal": true, }, - "id": 799, + "id": 795, "literal": "^2.0.3", "name": "is-set", "npm": { "name": "is-set", "version": ">=2.0.3 <3.0.0", }, - "package_id": 420, + "package_id": 416, }, { "behavior": { "normal": true, }, - "id": 800, + "id": 796, "literal": "^2.0.2", "name": "is-weakmap", "npm": { "name": "is-weakmap", "version": ">=2.0.2 <3.0.0", }, - "package_id": 419, + "package_id": 415, }, { "behavior": { "normal": true, }, - "id": 801, + "id": 797, "literal": "^2.0.3", "name": "is-weakset", "npm": { "name": "is-weakset", "version": ">=2.0.3 <3.0.0", }, - "package_id": 418, + "package_id": 414, }, { "behavior": { "normal": true, }, - "id": 802, + "id": 798, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 803, + "id": 799, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 804, + "id": 800, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 805, + "id": 801, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 806, + "id": 802, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 807, + "id": 803, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 808, + "id": 804, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 809, + "id": 805, "literal": "^0.14.0", "name": "regenerator-runtime", "npm": { "name": "regenerator-runtime", "version": ">=0.14.0 <0.15.0", }, - "package_id": 432, + "package_id": 428, }, { "behavior": { "normal": true, }, - "id": 810, + "id": 806, "literal": "^3.1.8", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.8 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 811, + "id": 807, "literal": "^1.2.5", "name": "array.prototype.findlast", "npm": { "name": "array.prototype.findlast", "version": ">=1.2.5 <2.0.0", }, - "package_id": 441, + "package_id": 437, }, { "behavior": { "normal": true, }, - "id": 812, + "id": 808, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 813, + "id": 809, "literal": "^1.1.2", "name": "array.prototype.toreversed", "npm": { "name": "array.prototype.toreversed", "version": ">=1.1.2 <2.0.0", }, - "package_id": 440, + "package_id": 436, }, { "behavior": { "normal": true, }, - "id": 814, + "id": 810, "literal": "^1.1.3", "name": "array.prototype.tosorted", "npm": { "name": "array.prototype.tosorted", "version": ">=1.1.3 <2.0.0", }, - "package_id": 439, + "package_id": 435, }, { "behavior": { "normal": true, }, - "id": 815, + "id": 811, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 816, + "id": 812, "literal": "^1.0.19", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.19 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 817, + "id": 813, "literal": "^5.3.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.3.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 818, + "id": 814, "literal": "^2.4.1 || ^3.0.0", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=2.4.1 <3.0.0 || >=3.0.0 <4.0.0 && >=3.0.0 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 819, + "id": 815, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 820, + "id": 816, "literal": "^1.1.8", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.8 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 821, + "id": 817, "literal": "^2.0.8", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.8 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 822, + "id": 818, "literal": "^1.1.4", "name": "object.hasown", "npm": { "name": "object.hasown", "version": ">=1.1.4 <2.0.0", }, - "package_id": 438, + "package_id": 434, }, { "behavior": { "normal": true, }, - "id": 823, + "id": 819, "literal": "^1.2.0", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.2.0 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 824, + "id": 820, "literal": "^15.8.1", "name": "prop-types", "npm": { "name": "prop-types", "version": ">=15.8.1 <16.0.0", }, - "package_id": 436, + "package_id": 432, }, { "behavior": { "normal": true, }, - "id": 825, + "id": 821, "literal": "^2.0.0-next.5", "name": "resolve", "npm": { "name": "resolve", "version": ">=2.0.0-next.5 <3.0.0", }, - "package_id": 435, + "package_id": 431, }, { "behavior": { "normal": true, }, - "id": 826, + "id": 822, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 827, + "id": 823, "literal": "^4.0.11", "name": "string.prototype.matchall", "npm": { "name": "string.prototype.matchall", "version": ">=4.0.11 <5.0.0", }, - "package_id": 434, + "package_id": 430, }, { "behavior": { "peer": true, }, - "id": 828, + "id": 824, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 829, + "id": 825, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 830, + "id": 826, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 831, + "id": 827, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 832, + "id": 828, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 833, + "id": 829, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 834, + "id": 830, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 835, + "id": 831, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 836, + "id": 832, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 837, + "id": 833, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 838, + "id": 834, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 839, + "id": 835, "literal": "^2.0.2", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.2 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 840, + "id": 836, "literal": "^1.0.6", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.6 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 841, + "id": 837, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -10965,7 +10912,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 842, + "id": 838, "literal": "^1.0.7", "name": "path-parse", "npm": { @@ -10978,7 +10925,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 843, + "id": 839, "literal": "^1.0.0", "name": "supports-preserve-symlinks-flag", "npm": { @@ -10991,7 +10938,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 844, + "id": 840, "literal": "^1.4.0", "name": "loose-envify", "npm": { @@ -11004,7 +10951,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 845, + "id": 841, "literal": "^4.1.1", "name": "object-assign", "npm": { @@ -11017,345 +10964,345 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 846, + "id": 842, "literal": "^16.13.1", "name": "react-is", "npm": { "name": "react-is", "version": ">=16.13.1 <17.0.0", }, - "package_id": 437, + "package_id": 433, }, { "behavior": { "normal": true, }, - "id": 847, + "id": 843, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 848, + "id": 844, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 849, + "id": 845, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 850, + "id": 846, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 851, + "id": 847, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 852, + "id": 848, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 853, + "id": 849, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 854, + "id": 850, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 855, + "id": 851, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 856, + "id": 852, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 857, + "id": 853, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 858, + "id": 854, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 859, + "id": 855, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 860, + "id": 856, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 861, + "id": 857, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 862, + "id": 858, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 863, + "id": 859, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 864, + "id": 860, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 865, + "id": 861, "literal": "~8.5.10", "name": "@types/ws", "npm": { "name": "@types/ws", "version": ">=8.5.10 <8.6.0", }, - "package_id": 443, + "package_id": 439, }, { "behavior": { "normal": true, }, - "id": 866, + "id": 862, "literal": "~20.12.8", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=20.12.8 <20.13.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 867, + "id": 863, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 868, + "id": 864, "literal": "^4.21.10", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.10 <5.0.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 869, + "id": 865, "literal": "^1.0.30001538", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001538 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 870, + "id": 866, "literal": "^4.3.6", "name": "fraction.js", "npm": { "name": "fraction.js", "version": ">=4.3.6 <5.0.0", }, - "package_id": 446, + "package_id": 442, }, { "behavior": { "normal": true, }, - "id": 871, + "id": 867, "literal": "^0.1.2", "name": "normalize-range", "npm": { "name": "normalize-range", "version": ">=0.1.2 <0.2.0", }, - "package_id": 445, + "package_id": 441, }, { "behavior": { "normal": true, }, - "id": 872, + "id": 868, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -11368,7 +11315,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 873, + "id": 869, "literal": "^4.2.0", "name": "postcss-value-parser", "npm": { @@ -11381,7 +11328,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "peer": true, }, - "id": 874, + "id": 870, "literal": "^8.1.0", "name": "postcss", "npm": { @@ -11394,72 +11341,72 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "normal": true, }, - "id": 875, + "id": 871, "literal": "^1.0.30001587", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001587 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 876, + "id": 872, "literal": "^1.4.668", "name": "electron-to-chromium", "npm": { "name": "electron-to-chromium", "version": ">=1.4.668 <2.0.0", }, - "package_id": 450, + "package_id": 446, }, { "behavior": { "normal": true, }, - "id": 877, + "id": 873, "literal": "^2.0.14", "name": "node-releases", "npm": { "name": "node-releases", "version": ">=2.0.14 <3.0.0", }, - "package_id": 449, + "package_id": 445, }, { "behavior": { "normal": true, }, - "id": 878, + "id": 874, "literal": "^1.0.13", "name": "update-browserslist-db", "npm": { "name": "update-browserslist-db", "version": ">=1.0.13 <2.0.0", }, - "package_id": 448, + "package_id": 444, }, { "behavior": { "normal": true, }, - "id": 879, + "id": 875, "literal": "^3.1.2", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.2 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 880, + "id": 876, "literal": "^1.0.1", "name": "picocolors", "npm": { @@ -11472,128 +11419,128 @@ exports[`ssr works for 100-ish requests 1`] = ` "behavior": { "peer": true, }, - "id": 881, + "id": 877, "literal": ">= 4.21.0", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 882, + "id": 878, "literal": "*", "name": "@types/react", "npm": { "name": "@types/react", "version": ">=0.0.0", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { "normal": true, }, - "id": 883, + "id": 879, "literal": "*", "name": "@types/prop-types", "npm": { "name": "@types/prop-types", "version": ">=0.0.0", }, - "package_id": 455, + "package_id": 451, }, { "behavior": { "normal": true, }, - "id": 884, + "id": 880, "literal": "*", "name": "@types/scheduler", "npm": { "name": "@types/scheduler", "version": ">=0.0.0", }, - "package_id": 454, + "package_id": 450, }, { "behavior": { "normal": true, }, - "id": 885, + "id": 881, "literal": "^3.0.2", "name": "csstype", "npm": { "name": "csstype", "version": ">=3.0.2 <4.0.0", }, - "package_id": 453, + "package_id": 449, }, ], "format": "v2", - "meta_hash": "4688315a50aab25bb1d5fe41e445b346f9c0c71bf75f43ebbc91db59253d9026", + "meta_hash": "632a4f7405ad36643df0c844e942395e7c61cf79c7738eb128eba03ebdd1e094", "package_index": { "@alloc/quick-lru": 13, - "@babel/code-frame": 206, - "@babel/helper-validator-identifier": 215, - "@babel/highlight": 207, - "@babel/runtime": 431, - "@eslint-community/eslint-utils": 245, - "@eslint-community/regexpp": 252, - "@eslint/eslintrc": 265, - "@eslint/js": 293, - "@humanwhocodes/config-array": 247, - "@humanwhocodes/module-importer": 244, - "@humanwhocodes/object-schema": 251, + "@babel/code-frame": 202, + "@babel/helper-validator-identifier": 211, + "@babel/highlight": 203, + "@babel/runtime": 427, + "@eslint-community/eslint-utils": 241, + "@eslint-community/regexpp": 248, + "@eslint/eslintrc": 261, + "@eslint/js": 289, + "@humanwhocodes/config-array": 243, + "@humanwhocodes/module-importer": 240, + "@humanwhocodes/object-schema": 247, "@isaacs/cliui": 74, "@jridgewell/gen-mapping": 100, "@jridgewell/resolve-uri": 103, "@jridgewell/set-array": 104, "@jridgewell/sourcemap-codec": 102, "@jridgewell/trace-mapping": 101, - "@next/env": 237, - "@next/eslint-plugin-next": 406, - "@next/swc-darwin-arm64": 231, - "@next/swc-darwin-x64": 232, - "@next/swc-linux-arm64-gnu": 227, - "@next/swc-linux-arm64-musl": 225, - "@next/swc-linux-x64-gnu": 230, - "@next/swc-linux-x64-musl": 229, - "@next/swc-win32-arm64-msvc": 224, - "@next/swc-win32-ia32-msvc": 226, - "@next/swc-win32-x64-msvc": 228, + "@next/env": 233, + "@next/eslint-plugin-next": 402, + "@next/swc-darwin-arm64": 227, + "@next/swc-darwin-x64": 228, + "@next/swc-linux-arm64-gnu": 223, + "@next/swc-linux-arm64-musl": 221, + "@next/swc-linux-x64-gnu": 226, + "@next/swc-linux-x64-musl": 225, + "@next/swc-win32-arm64-msvc": 220, + "@next/swc-win32-ia32-msvc": 222, + "@next/swc-win32-x64-msvc": 224, "@nodelib/fs.scandir": 46, "@nodelib/fs.stat": 49, "@nodelib/fs.walk": 43, "@pkgjs/parseargs": 73, - "@puppeteer/browsers": 114, - "@rushstack/eslint-patch": 407, - "@swc/helpers": 234, - "@tootallnate/quickjs-emscripten": 177, - "@types/json5": 312, + "@puppeteer/browsers": 115, + "@rushstack/eslint-patch": 403, + "@swc/helpers": 230, + "@tootallnate/quickjs-emscripten": 178, + "@types/json5": 308, "@types/node": [ - 182, - 456, + 183, + 452, ], - "@types/prop-types": 455, - "@types/react": 452, - "@types/react-dom": 451, - "@types/scheduler": 454, - "@types/ws": 443, - "@types/yauzl": 181, - "@typescript-eslint/parser": 394, - "@typescript-eslint/scope-manager": 405, - "@typescript-eslint/types": 397, - "@typescript-eslint/typescript-estree": 395, - "@typescript-eslint/visitor-keys": 396, - "acorn": 272, - "acorn-jsx": 271, - "agent-base": 155, - "ajv": 273, + "@types/prop-types": 451, + "@types/react": 448, + "@types/react-dom": 447, + "@types/scheduler": 450, + "@types/ws": 439, + "@types/yauzl": 182, + "@typescript-eslint/parser": 390, + "@typescript-eslint/scope-manager": 401, + "@typescript-eslint/types": 393, + "@typescript-eslint/typescript-estree": 391, + "@typescript-eslint/visitor-keys": 392, + "acorn": 268, + "acorn-jsx": 267, + "agent-base": 156, + "ajv": 269, "ansi-regex": [ 86, 77, @@ -11601,316 +11548,314 @@ exports[`ssr works for 100-ish requests 1`] = ` "ansi-styles": [ 90, 81, - 212, + 208, ], "any-promise": 62, "anymatch": 55, "arg": 107, - "argparse": 217, - "aria-query": 430, - "array-buffer-byte-length": 378, - "array-includes": 388, - "array-union": 404, - "array.prototype.findlast": 441, - "array.prototype.findlastindex": 387, - "array.prototype.flat": 386, - "array.prototype.flatmap": 384, - "array.prototype.toreversed": 440, - "array.prototype.tosorted": 439, - "arraybuffer.prototype.slice": 377, - "ast-types": 166, - "ast-types-flow": 429, - "autoprefixer": 444, - "available-typed-arrays": 334, - "axe-core": 428, - "axobject-query": 426, - "b4a": 138, + "argparse": 213, + "aria-query": 426, + "array-buffer-byte-length": 374, + "array-includes": 384, + "array-union": 400, + "array.prototype.findlast": 437, + "array.prototype.findlastindex": 383, + "array.prototype.flat": 382, + "array.prototype.flatmap": 380, + "array.prototype.toreversed": 436, + "array.prototype.tosorted": 435, + "arraybuffer.prototype.slice": 373, + "ast-types": 167, + "ast-types-flow": 425, + "autoprefixer": 440, + "available-typed-arrays": 330, + "axe-core": 424, + "axobject-query": 422, + "b4a": 139, "balanced-match": 71, - "bare-events": 136, - "bare-fs": 133, - "bare-os": 132, - "bare-path": 131, - "bare-stream": 134, - "base64-js": 129, - "basic-ftp": 176, + "bare-events": 137, + "bare-fs": 134, + "bare-os": 133, + "bare-path": 132, + "bare-stream": 135, + "base64-js": 130, + "basic-ftp": 177, "binary-extensions": 54, "brace-expansion": [ 70, - 249, + 245, ], "braces": 34, - "browserslist": 447, - "buffer": 127, - "buffer-crc32": 185, - "bun-types": 442, - "busboy": 239, - "call-bind": 326, - "callsites": 221, + "browserslist": 443, + "buffer": 128, + "buffer-crc32": 186, + "bun-types": 438, + "busboy": 235, + "call-bind": 322, + "callsites": 217, "camelcase-css": 31, - "caniuse-lite": 233, + "caniuse-lite": 229, "chalk": [ - 303, - 208, + 299, + 204, ], "chokidar": 50, - "chromium-bidi": 198, - "client-only": 236, - "cliui": 124, + "chromium-bidi": 193, + "client-only": 232, + "cliui": 125, "color-convert": [ 82, - 213, + 209, ], "color-name": [ 83, - 214, + 210, ], "commander": 99, - "concat-map": 250, - "cosmiconfig": 201, - "cross-fetch": 193, + "concat-map": 246, + "cosmiconfig": 197, "cross-spawn": 93, "cssesc": 5, - "csstype": 453, - "damerau-levenshtein": 425, - "data-uri-to-buffer": 175, - "data-view-buffer": 376, - "data-view-byte-length": 375, - "data-view-byte-offset": 374, + "csstype": 449, + "damerau-levenshtein": 421, + "data-uri-to-buffer": 176, + "data-view-buffer": 372, + "data-view-byte-length": 371, + "data-view-byte-offset": 370, "debug": [ - 153, - 189, - 381, + 154, + 190, + 377, ], - "deep-is": 292, + "deep-is": 288, "default-create-template": 0, - "define-data-property": 324, - "define-properties": 317, - "degenerator": 160, - "dequal": 427, - "devtools-protocol": 192, + "define-data-property": 320, + "define-properties": 313, + "degenerator": 161, + "dequal": 423, + "devtools-protocol": 114, "didyoumean": 38, - "dir-glob": 402, + "dir-glob": 398, "dlv": 106, "doctrine": [ - 295, - 383, + 291, + 379, ], "eastasianwidth": 89, - "electron-to-chromium": 450, + "electron-to-chromium": 446, "emoji-regex": [ 88, 80, ], - "end-of-stream": 145, - "enhanced-resolve": 391, - "env-paths": 222, - "error-ex": 204, - "es-abstract": 329, - "es-define-property": 320, - "es-errors": 316, - "es-iterator-helpers": 413, - "es-object-atoms": 315, - "es-set-tostringtag": 373, - "es-shim-unscopables": 385, - "es-to-primitive": 371, - "escalade": 123, + "end-of-stream": 146, + "enhanced-resolve": 387, + "env-paths": 218, + "error-ex": 200, + "es-abstract": 325, + "es-define-property": 316, + "es-errors": 312, + "es-iterator-helpers": 409, + "es-object-atoms": 311, + "es-set-tostringtag": 369, + "es-shim-unscopables": 381, + "es-to-primitive": 367, + "escalade": 124, "escape-string-regexp": [ - 253, - 211, + 249, + 207, ], - "escodegen": 162, - "eslint": 242, - "eslint-config-next": 241, - "eslint-import-resolver-node": 382, - "eslint-import-resolver-typescript": 306, - "eslint-module-utils": 380, - "eslint-plugin-import": 307, - "eslint-plugin-jsx-a11y": 408, - "eslint-plugin-react": 433, - "eslint-plugin-react-hooks": 393, - "eslint-scope": 282, - "eslint-visitor-keys": 246, - "espree": 270, - "esprima": 161, - "esquery": 302, - "esrecurse": 283, - "estraverse": 165, - "esutils": 164, - "extract-zip": 180, - "fast-deep-equal": 278, - "fast-fifo": 140, + "escodegen": 163, + "eslint": 238, + "eslint-config-next": 237, + "eslint-import-resolver-node": 378, + "eslint-import-resolver-typescript": 302, + "eslint-module-utils": 376, + "eslint-plugin-import": 303, + "eslint-plugin-jsx-a11y": 404, + "eslint-plugin-react": 429, + "eslint-plugin-react-hooks": 389, + "eslint-scope": 278, + "eslint-visitor-keys": 242, + "espree": 266, + "esprima": 162, + "esquery": 298, + "esrecurse": 279, + "estraverse": 166, + "esutils": 165, + "extract-zip": 181, + "fast-deep-equal": 274, + "fast-fifo": 141, "fast-glob": 40, - "fast-json-stable-stringify": 277, - "fast-levenshtein": 287, + "fast-json-stable-stringify": 273, + "fast-levenshtein": 283, "fastq": 44, - "fd-slicer": 186, - "file-entry-cache": 254, + "fd-slicer": 187, + "file-entry-cache": 250, "fill-range": 35, - "find-up": 296, - "flat-cache": 255, - "flatted": 264, - "for-each": 332, + "find-up": 292, + "flat-cache": 251, + "flatted": 260, + "for-each": 328, "foreground-child": 91, - "fraction.js": 446, - "fs-extra": 171, - "fs.realpath": 261, + "fraction.js": 442, + "fs-extra": 172, + "fs.realpath": 257, "fsevents": 51, "function-bind": 21, - "function.prototype.name": 370, - "functions-have-names": 358, - "get-caller-file": 122, - "get-intrinsic": 321, - "get-stream": 188, - "get-symbol-description": 369, - "get-tsconfig": 389, - "get-uri": 170, + "function.prototype.name": 366, + "functions-have-names": 354, + "get-caller-file": 123, + "get-intrinsic": 317, + "get-stream": 189, + "get-symbol-description": 365, + "get-tsconfig": 385, + "get-uri": 171, "glob": [ 65, - 257, + 253, ], "glob-parent": [ 27, 42, ], - "globals": 268, - "globalthis": 368, - "globby": 400, - "gopd": 325, - "graceful-fs": 174, - "graphemer": 294, - "has-bigints": 343, + "globals": 264, + "globalthis": 364, + "globby": 396, + "gopd": 321, + "graceful-fs": 175, + "graphemer": 290, + "has-bigints": 339, "has-flag": [ - 305, - 210, + 301, + 206, ], - "has-property-descriptors": 319, - "has-proto": 323, - "has-symbols": 322, - "has-tostringtag": 331, + "has-property-descriptors": 315, + "has-proto": 319, + "has-symbols": 318, + "has-tostringtag": 327, "hasown": 20, - "http-proxy-agent": 169, - "https-proxy-agent": 168, - "ieee754": 128, - "ignore": 267, - "import-fresh": 218, - "imurmurhash": 284, - "inflight": 260, - "inherits": 259, - "internal-slot": 366, - "ip-address": 150, - "is-array-buffer": 365, - "is-arrayish": 205, - "is-async-function": 424, - "is-bigint": 342, + "http-proxy-agent": 170, + "https-proxy-agent": 169, + "ieee754": 129, + "ignore": 263, + "import-fresh": 214, + "imurmurhash": 280, + "inflight": 256, + "inherits": 255, + "internal-slot": 362, + "ip-address": 151, + "is-array-buffer": 361, + "is-arrayish": 201, + "is-async-function": 420, + "is-bigint": 338, "is-binary-path": 53, - "is-boolean-object": 341, - "is-callable": 333, + "is-boolean-object": 337, + "is-callable": 329, "is-core-module": 19, - "is-data-view": 364, - "is-date-object": 372, + "is-data-view": 360, + "is-date-object": 368, "is-extglob": 29, - "is-finalizationregistry": 423, + "is-finalizationregistry": 419, "is-fullwidth-code-point": 79, - "is-generator-function": 422, + "is-generator-function": 418, "is-glob": 28, - "is-map": 421, - "is-negative-zero": 363, + "is-map": 417, + "is-negative-zero": 359, "is-number": 37, - "is-number-object": 340, - "is-path-inside": 280, - "is-regex": 353, - "is-set": 420, - "is-shared-array-buffer": 362, - "is-string": 339, - "is-symbol": 338, - "is-typed-array": 345, - "is-weakmap": 419, - "is-weakref": 361, - "is-weakset": 418, - "isarray": 355, + "is-number-object": 336, + "is-path-inside": 276, + "is-regex": 349, + "is-set": 416, + "is-shared-array-buffer": 358, + "is-string": 335, + "is-symbol": 334, + "is-typed-array": 341, + "is-weakmap": 415, + "is-weakref": 357, + "is-weakset": 414, + "isarray": 351, "isexe": 95, - "iterator.prototype": 414, + "iterator.prototype": 410, "jackspeak": 72, "jiti": 105, "js-tokens": 111, - "js-yaml": 216, - "jsbn": 152, - "json-buffer": 263, - "json-parse-even-better-errors": 203, - "json-schema-traverse": 276, - "json-stable-stringify-without-jsonify": 243, - "json5": 311, - "jsonfile": 173, - "jsx-ast-utils": 412, - "keyv": 262, - "language-subtag-registry": 411, - "language-tags": 410, - "levn": 288, + "js-yaml": 212, + "jsbn": 153, + "json-buffer": 259, + "json-parse-even-better-errors": 199, + "json-schema-traverse": 272, + "json-stable-stringify-without-jsonify": 239, + "json5": 307, + "jsonfile": 174, + "jsx-ast-utils": 408, + "keyv": 258, + "language-subtag-registry": 407, + "language-tags": 406, + "levn": 284, "lilconfig": [ 11, 39, ], "lines-and-columns": 64, - "locate-path": 298, - "lodash.merge": 281, + "locate-path": 294, + "lodash.merge": 277, "loose-envify": 110, "lru-cache": [ 68, - 178, - 116, + 179, + 117, ], "merge2": 41, "micromatch": 32, "minimatch": [ 69, - 399, - 248, + 395, + 244, ], - "minimist": 310, + "minimist": 306, "minipass": 67, - "mitt": 200, - "ms": 154, + "mitt": 196, + "ms": 155, "mz": 59, "nanoid": 10, - "natural-compare": 279, - "netmask": 159, - "next": 223, - "node-fetch": 194, - "node-releases": 449, + "natural-compare": 275, + "netmask": 160, + "next": 219, + "node-releases": 445, "normalize-path": 25, - "normalize-range": 445, + "normalize-range": 441, "object-assign": 63, "object-hash": 26, - "object-inspect": 360, - "object-keys": 318, - "object.assign": 359, - "object.entries": 409, - "object.fromentries": 379, - "object.groupby": 328, - "object.hasown": 438, - "object.values": 314, - "once": 143, - "optionator": 286, - "p-limit": 300, - "p-locate": 299, - "pac-proxy-agent": 157, - "pac-resolver": 158, - "parent-module": 220, - "parse-json": 202, - "path-exists": 297, - "path-is-absolute": 258, + "object-inspect": 356, + "object-keys": 314, + "object.assign": 355, + "object.entries": 405, + "object.fromentries": 375, + "object.groupby": 324, + "object.hasown": 434, + "object.values": 310, + "once": 144, + "optionator": 282, + "p-limit": 296, + "p-locate": 295, + "pac-proxy-agent": 158, + "pac-resolver": 159, + "parent-module": 216, + "parse-json": 198, + "path-exists": 293, + "path-is-absolute": 254, "path-key": 98, "path-parse": 18, "path-scurry": 66, - "path-type": 403, - "pend": 187, + "path-type": 399, + "pend": 188, "picocolors": 9, "picomatch": 33, "pify": 23, "pirates": 58, - "possible-typed-array-names": 335, + "possible-typed-array-names": 331, "postcss": [ - 238, + 234, 7, ], "postcss-import": 15, @@ -11919,129 +11864,127 @@ exports[`ssr works for 100-ish requests 1`] = ` "postcss-nested": 14, "postcss-selector-parser": 3, "postcss-value-parser": 24, - "prelude-ls": 290, - "progress": 179, - "prop-types": 436, - "proxy-agent": 146, - "proxy-from-env": 156, - "pump": 142, - "punycode": 275, + "prelude-ls": 286, + "progress": 180, + "prop-types": 432, + "proxy-agent": 147, + "proxy-from-env": 157, + "pump": 143, + "punycode": 271, "puppeteer": 113, - "puppeteer-core": 190, + "puppeteer-core": 191, "queue-microtask": 48, - "queue-tick": 139, + "queue-tick": 140, "react": 109, "react-dom": 108, - "react-is": 437, + "react-is": 433, "read-cache": 22, "readdirp": 52, - "reflect.getprototypeof": 415, - "regenerator-runtime": 432, - "regexp.prototype.flags": 356, - "require-directory": 121, + "reflect.getprototypeof": 411, + "regenerator-runtime": 428, + "regexp.prototype.flags": 352, + "require-directory": 122, "resolve": [ - 435, + 431, 16, ], - "resolve-from": 219, - "resolve-pkg-maps": 390, + "resolve-from": 215, + "resolve-pkg-maps": 386, "reusify": 45, - "rimraf": 256, + "rimraf": 252, "run-parallel": 47, - "safe-array-concat": 354, - "safe-regex-test": 352, + "safe-array-concat": 350, + "safe-regex-test": 348, "scheduler": 112, "semver": [ - 115, - 313, + 116, + 309, ], - "set-function-length": 327, - "set-function-name": 357, + "set-function-length": 323, + "set-function-name": 353, "shebang-command": 96, "shebang-regex": 97, - "side-channel": 367, + "side-channel": 363, "signal-exit": 92, - "slash": 401, - "smart-buffer": 149, - "socks": 148, - "socks-proxy-agent": 147, - "source-map": 163, + "slash": 397, + "smart-buffer": 150, + "socks": 149, + "socks-proxy-agent": 148, + "source-map": 164, "source-map-js": 8, - "sprintf-js": 151, - "streamsearch": 240, - "streamx": 135, + "sprintf-js": 152, + "streamsearch": 236, + "streamx": 136, "string-width": [ 87, 78, ], - "string.prototype.matchall": 434, - "string.prototype.trim": 351, - "string.prototype.trimend": 350, - "string.prototype.trimstart": 349, + "string.prototype.matchall": 430, + "string.prototype.trim": 347, + "string.prototype.trimend": 346, + "string.prototype.trimstart": 345, "strip-ansi": [ 85, 76, ], - "strip-bom": 309, - "strip-json-comments": 266, - "styled-jsx": 235, + "strip-bom": 305, + "strip-json-comments": 262, + "styled-jsx": 231, "sucrase": 56, "supports-color": [ - 304, - 209, + 300, + 205, ], "supports-preserve-symlinks-flag": 17, "tailwindcss": 2, - "tapable": 392, - "tar-fs": 130, - "tar-stream": 141, - "text-decoder": 137, - "text-table": 285, + "tapable": 388, + "tar-fs": 131, + "tar-stream": 142, + "text-decoder": 138, + "text-table": 281, "thenify": 61, "thenify-all": 60, - "through": 126, + "through": 127, "to-regex-range": 36, - "tr46": 197, - "ts-api-utils": 398, + "ts-api-utils": 394, "ts-interface-checker": 57, - "tsconfig-paths": 308, - "tslib": 167, - "type-check": 289, - "type-fest": 269, - "typed-array-buffer": 348, - "typed-array-byte-length": 347, - "typed-array-byte-offset": 346, - "typed-array-length": 344, + "tsconfig-paths": 304, + "tslib": 168, + "type-check": 285, + "type-fest": 265, + "typed-array-buffer": 344, + "typed-array-byte-length": 343, + "typed-array-byte-offset": 342, + "typed-array-length": 340, "typescript": 1, - "unbox-primitive": 336, - "unbzip2-stream": 125, - "undici-types": 183, - "universalify": 172, - "update-browserslist-db": 448, - "uri-js": 274, - "urlpattern-polyfill": 199, + "unbox-primitive": 332, + "unbzip2-stream": 126, + "undici-types": 184, + "universalify": 173, + "update-browserslist-db": 444, + "uri-js": 270, + "urlpattern-polyfill": 195, "util-deprecate": 4, - "webidl-conversions": 196, - "whatwg-url": 195, "which": 94, - "which-boxed-primitive": 337, - "which-builtin-type": 416, - "which-collection": 417, - "which-typed-array": 330, - "word-wrap": 291, + "which-boxed-primitive": 333, + "which-builtin-type": 412, + "which-collection": 413, + "which-typed-array": 326, + "word-wrap": 287, "wrap-ansi": [ 84, 75, ], - "wrappy": 144, - "ws": 191, - "y18n": 120, - "yallist": 117, + "wrappy": 145, + "ws": 192, + "y18n": 121, + "yallist": 118, "yaml": 12, - "yargs": 118, - "yargs-parser": 119, - "yauzl": 184, - "yocto-queue": 301, + "yargs": 119, + "yargs-parser": 120, + "yauzl": 185, + "yocto-queue": 297, + "zod": 194, }, "packages": [ { @@ -14108,17 +14051,34 @@ exports[`ssr works for 100-ish requests 1`] = ` 153, 154, 155, + 156, ], "id": 113, - "integrity": "sha512-Mag1wRLanzwS4yEUyrDRBUgsKlH3dpL6oAfVwNHG09oxd0+ySsatMvYj7HwjynWy/S+Hg+XHLgjyC/F6CsL/lg==", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "man_dir": "", "name": "puppeteer", "name_hash": "13072297456933147981", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", + "tag": "npm", + "value": "22.12.0", + }, + "scripts": {}, + }, + { + "bin": null, + "dependencies": [], + "id": 114, + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==", + "man_dir": "", + "name": "devtools-protocol", + "name_hash": "12159960943916763407", + "origin": "npm", + "resolution": { + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", "tag": "npm", - "value": "22.4.1", + "value": "0.0.1299070", }, "scripts": {}, }, @@ -14128,7 +14088,6 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "browsers", }, "dependencies": [ - 156, 157, 158, 159, @@ -14136,17 +14095,18 @@ exports[`ssr works for 100-ish requests 1`] = ` 161, 162, 163, + 164, ], - "id": 114, - "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "id": 115, + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "man_dir": "", "name": "@puppeteer/browsers", "name_hash": "6318517029770692415", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", "tag": "npm", - "value": "2.1.0", + "value": "2.2.3", }, "scripts": {}, }, @@ -14156,9 +14116,9 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "semver", }, "dependencies": [ - 164, + 165, ], - "id": 115, + "id": 116, "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "man_dir": "", "name": "semver", @@ -14174,9 +14134,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 165, + 166, ], - "id": 116, + "id": 117, "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "man_dir": "", "name": "lru-cache", @@ -14192,7 +14152,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 117, + "id": 118, "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "man_dir": "", "name": "yallist", @@ -14208,15 +14168,15 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 166, 167, 168, 169, 170, 171, 172, + 173, ], - "id": 118, + "id": 119, "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "man_dir": "", "name": "yargs", @@ -14232,7 +14192,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 119, + "id": 120, "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "man_dir": "", "name": "yargs-parser", @@ -14248,7 +14208,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 120, + "id": 121, "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "man_dir": "", "name": "y18n", @@ -14264,7 +14224,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 121, + "id": 122, "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "man_dir": "", "name": "require-directory", @@ -14280,7 +14240,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 122, + "id": 123, "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "man_dir": "", "name": "get-caller-file", @@ -14296,7 +14256,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 123, + "id": 124, "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "man_dir": "", "name": "escalade", @@ -14312,11 +14272,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 173, 174, 175, + 176, ], - "id": 124, + "id": 125, "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "man_dir": "", "name": "cliui", @@ -14332,10 +14292,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 176, 177, + 178, ], - "id": 125, + "id": 126, "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "man_dir": "", "name": "unbzip2-stream", @@ -14351,7 +14311,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 126, + "id": 127, "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "man_dir": "", "name": "through", @@ -14367,10 +14327,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 178, 179, + 180, ], - "id": 127, + "id": 128, "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "man_dir": "", "name": "buffer", @@ -14386,7 +14346,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 128, + "id": 129, "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "man_dir": "", "name": "ieee754", @@ -14402,7 +14362,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 129, + "id": 130, "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "man_dir": "", "name": "base64-js", @@ -14418,12 +14378,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 180, 181, 182, 183, + 184, ], - "id": 130, + "id": 131, "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "man_dir": "", "name": "tar-fs", @@ -14439,9 +14399,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 184, + 185, ], - "id": 131, + "id": 132, "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "man_dir": "", "name": "bare-path", @@ -14457,7 +14417,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 132, + "id": 133, "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", "man_dir": "", "name": "bare-os", @@ -14473,11 +14433,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 185, 186, 187, + 188, ], - "id": 133, + "id": 134, "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", "man_dir": "", "name": "bare-fs", @@ -14493,9 +14453,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 188, + 189, ], - "id": 134, + "id": 135, "integrity": "sha512-ubLyoDqPnUf5o0kSFp709HC0WRZuxVuh4pbte5eY95Xvx5bdvz07c2JFmXBfqqe60q+9PJ8S4X5GRvmcNSKMxg==", "man_dir": "", "name": "bare-stream", @@ -14511,12 +14471,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 189, 190, 191, 192, + 193, ], - "id": 135, + "id": 136, "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", "man_dir": "", "name": "streamx", @@ -14532,7 +14492,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 136, + "id": 137, "integrity": "sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA==", "man_dir": "", "name": "bare-events", @@ -14548,9 +14508,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 193, + 194, ], - "id": 137, + "id": 138, "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", "man_dir": "", "name": "text-decoder", @@ -14566,7 +14526,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 138, + "id": 139, "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "man_dir": "", "name": "b4a", @@ -14582,7 +14542,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 139, + "id": 140, "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "man_dir": "", "name": "queue-tick", @@ -14598,7 +14558,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 140, + "id": 141, "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "man_dir": "", "name": "fast-fifo", @@ -14614,11 +14574,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 194, 195, 196, + 197, ], - "id": 141, + "id": 142, "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "man_dir": "", "name": "tar-stream", @@ -14634,10 +14594,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 197, 198, + 199, ], - "id": 142, + "id": 143, "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "man_dir": "", "name": "pump", @@ -14653,9 +14613,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 199, + 200, ], - "id": 143, + "id": 144, "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "man_dir": "", "name": "once", @@ -14671,7 +14631,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 144, + "id": 145, "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "man_dir": "", "name": "wrappy", @@ -14687,9 +14647,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 200, + 201, ], - "id": 145, + "id": 146, "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "man_dir": "", "name": "end-of-stream", @@ -14705,7 +14665,6 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 201, 202, 203, 204, @@ -14713,8 +14672,9 @@ exports[`ssr works for 100-ish requests 1`] = ` 206, 207, 208, + 209, ], - "id": 146, + "id": 147, "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "man_dir": "", "name": "proxy-agent", @@ -14730,11 +14690,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 209, 210, 211, + 212, ], - "id": 147, + "id": 148, "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "man_dir": "", "name": "socks-proxy-agent", @@ -14750,10 +14710,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 212, 213, + 214, ], - "id": 148, + "id": 149, "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "man_dir": "", "name": "socks", @@ -14769,7 +14729,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 149, + "id": 150, "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "man_dir": "", "name": "smart-buffer", @@ -14785,10 +14745,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 214, 215, + 216, ], - "id": 150, + "id": 151, "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "man_dir": "", "name": "ip-address", @@ -14804,7 +14764,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 151, + "id": 152, "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "man_dir": "", "name": "sprintf-js", @@ -14820,7 +14780,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 152, + "id": 153, "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "man_dir": "", "name": "jsbn", @@ -14836,9 +14796,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 216, + 217, ], - "id": 153, + "id": 154, "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "man_dir": "", "name": "debug", @@ -14854,7 +14814,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 154, + "id": 155, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "man_dir": "", "name": "ms", @@ -14870,9 +14830,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 217, + 218, ], - "id": 155, + "id": 156, "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "man_dir": "", "name": "agent-base", @@ -14888,7 +14848,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 156, + "id": 157, "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "man_dir": "", "name": "proxy-from-env", @@ -14904,7 +14864,6 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 218, 219, 220, 221, @@ -14912,8 +14871,9 @@ exports[`ssr works for 100-ish requests 1`] = ` 223, 224, 225, + 226, ], - "id": 157, + "id": 158, "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "man_dir": "", "name": "pac-proxy-agent", @@ -14929,10 +14889,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 226, 227, + 228, ], - "id": 158, + "id": 159, "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "man_dir": "", "name": "pac-resolver", @@ -14948,7 +14908,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 159, + "id": 160, "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "man_dir": "", "name": "netmask", @@ -14964,11 +14924,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 228, 229, 230, + 231, ], - "id": 160, + "id": 161, "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "man_dir": "", "name": "degenerator", @@ -14987,7 +14947,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "esvalidate": "./bin/esvalidate.js", }, "dependencies": [], - "id": 161, + "id": 162, "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "man_dir": "", "name": "esprima", @@ -15006,12 +14966,12 @@ exports[`ssr works for 100-ish requests 1`] = ` "esgenerate": "bin/esgenerate.js", }, "dependencies": [ - 231, 232, 233, 234, + 235, ], - "id": 162, + "id": 163, "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "man_dir": "", "name": "escodegen", @@ -15027,7 +14987,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 163, + "id": 164, "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "man_dir": "", "name": "source-map", @@ -15043,7 +15003,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 164, + "id": 165, "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "man_dir": "", "name": "esutils", @@ -15059,7 +15019,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 165, + "id": 166, "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "man_dir": "", "name": "estraverse", @@ -15075,9 +15035,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 235, + 236, ], - "id": 166, + "id": 167, "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "man_dir": "", "name": "ast-types", @@ -15093,7 +15053,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 167, + "id": 168, "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "man_dir": "", "name": "tslib", @@ -15109,10 +15069,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 236, 237, + 238, ], - "id": 168, + "id": 169, "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "man_dir": "", "name": "https-proxy-agent", @@ -15128,10 +15088,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 238, 239, + 240, ], - "id": 169, + "id": 170, "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "man_dir": "", "name": "http-proxy-agent", @@ -15147,12 +15107,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 240, 241, 242, 243, + 244, ], - "id": 170, + "id": 171, "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "man_dir": "", "name": "get-uri", @@ -15168,11 +15128,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 244, 245, 246, + 247, ], - "id": 171, + "id": 172, "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "man_dir": "", "name": "fs-extra", @@ -15188,7 +15148,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 172, + "id": 173, "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "man_dir": "", "name": "universalify", @@ -15204,10 +15164,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 247, 248, + 249, ], - "id": 173, + "id": 174, "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "man_dir": "", "name": "jsonfile", @@ -15223,7 +15183,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 174, + "id": 175, "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "man_dir": "", "name": "graceful-fs", @@ -15239,7 +15199,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 175, + "id": 176, "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "man_dir": "", "name": "data-uri-to-buffer", @@ -15255,7 +15215,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 176, + "id": 177, "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "man_dir": "", "name": "basic-ftp", @@ -15271,7 +15231,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 177, + "id": 178, "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "man_dir": "", "name": "@tootallnate/quickjs-emscripten", @@ -15287,7 +15247,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 178, + "id": 179, "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "man_dir": "", "name": "lru-cache", @@ -15303,7 +15263,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 179, + "id": 180, "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "man_dir": "", "name": "progress", @@ -15322,12 +15282,12 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "extract-zip", }, "dependencies": [ - 249, 250, 251, 252, + 253, ], - "id": 180, + "id": 181, "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "man_dir": "", "name": "extract-zip", @@ -15343,9 +15303,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 253, + 254, ], - "id": 181, + "id": 182, "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "man_dir": "", "name": "@types/yauzl", @@ -15361,9 +15321,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 254, + 255, ], - "id": 182, + "id": 183, "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", "man_dir": "", "name": "@types/node", @@ -15379,7 +15339,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 183, + "id": 184, "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "man_dir": "", "name": "undici-types", @@ -15395,10 +15355,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 255, 256, + 257, ], - "id": 184, + "id": 185, "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "man_dir": "", "name": "yauzl", @@ -15414,7 +15374,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 185, + "id": 186, "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "man_dir": "", "name": "buffer-crc32", @@ -15430,9 +15390,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 257, + 258, ], - "id": 186, + "id": 187, "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "man_dir": "", "name": "fd-slicer", @@ -15448,7 +15408,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 187, + "id": 188, "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "man_dir": "", "name": "pend", @@ -15464,9 +15424,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 258, + 259, ], - "id": 188, + "id": 189, "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "man_dir": "", "name": "get-stream", @@ -15482,9 +15442,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 259, + 260, ], - "id": 189, + "id": 190, "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "man_dir": "", "name": "debug", @@ -15500,23 +15460,22 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 260, 261, 262, 263, 264, 265, ], - "id": 190, - "integrity": "sha512-l9nf8NcirYOHdID12CIMWyy7dqcJCVtgVS+YAiJuUJHg8+9yjgPiG2PcNhojIEEpCkvw3FxvnyITVfKVmkWpjA==", + "id": 191, + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", "man_dir": "", "name": "puppeteer-core", "name_hash": "10954685796294859150", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", "tag": "npm", - "value": "22.4.1", + "value": "22.12.0", }, "scripts": {}, }, @@ -15526,32 +15485,16 @@ exports[`ssr works for 100-ish requests 1`] = ` 266, 267, ], - "id": 191, - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "id": 192, + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "man_dir": "", "name": "ws", "name_hash": "14644737011329074183", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "tag": "npm", - "value": "8.16.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 192, - "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", - "man_dir": "", - "name": "devtools-protocol", - "name_hash": "12159960943916763407", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "tag": "npm", - "value": "0.0.1249869", + "value": "8.17.1", }, "scripts": {}, }, @@ -15559,114 +15502,43 @@ exports[`ssr works for 100-ish requests 1`] = ` "bin": null, "dependencies": [ 268, - ], - "id": 193, - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "man_dir": "", - "name": "cross-fetch", - "name_hash": "5665307032371542913", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "tag": "npm", - "value": "4.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 269, 270, - ], - "id": 194, - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "man_dir": "", - "name": "node-fetch", - "name_hash": "9368364337257117328", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "tag": "npm", - "value": "2.7.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 271, - 272, ], - "id": 195, - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "man_dir": "", - "name": "whatwg-url", - "name_hash": "15436316526856444177", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "tag": "npm", - "value": "5.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 196, - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "id": 193, + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "man_dir": "", - "name": "webidl-conversions", - "name_hash": "5343883202058398372", + "name": "chromium-bidi", + "name_hash": "17738832193826713561", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", "tag": "npm", - "value": "3.0.1", + "value": "0.5.24", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 197, - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "man_dir": "", - "name": "tr46", - "name_hash": "4865213169840252474", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "tag": "npm", - "value": "0.0.3", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ - 273, - 274, - 275, - ], - "id": 198, - "integrity": "sha512-sZMgEBWKbupD0Q7lyFu8AWkrE+rs5ycE12jFkGwIgD/VS8lDPtelPlXM7LYaq4zrkZ/O2L3f4afHUHL0ICdKog==", + "id": 194, + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "man_dir": "", - "name": "chromium-bidi", - "name_hash": "17738832193826713561", + "name": "zod", + "name_hash": "13942938047053248045", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.12.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "tag": "npm", - "value": "0.5.12", + "value": "3.23.8", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 199, + "id": 195, "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "man_dir": "", "name": "urlpattern-polyfill", @@ -15682,7 +15554,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 200, + "id": 196, "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "man_dir": "", "name": "mitt", @@ -15698,13 +15570,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 272, + 273, + 274, + 275, 276, - 277, - 278, - 279, - 280, ], - "id": 201, + "id": 197, "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "man_dir": "", "name": "cosmiconfig", @@ -15720,12 +15592,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 281, - 282, - 283, - 284, + 277, + 278, + 279, + 280, ], - "id": 202, + "id": 198, "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "man_dir": "", "name": "parse-json", @@ -15741,7 +15613,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 203, + "id": 199, "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "man_dir": "", "name": "json-parse-even-better-errors", @@ -15757,9 +15629,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 285, + 281, ], - "id": 204, + "id": 200, "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "man_dir": "", "name": "error-ex", @@ -15775,7 +15647,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 205, + "id": 201, "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "man_dir": "", "name": "is-arrayish", @@ -15791,10 +15663,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 286, - 287, + 282, + 283, ], - "id": 206, + "id": 202, "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "man_dir": "", "name": "@babel/code-frame", @@ -15810,12 +15682,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 288, - 289, - 290, - 291, + 284, + 285, + 286, + 287, ], - "id": 207, + "id": 203, "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "man_dir": "", "name": "@babel/highlight", @@ -15831,11 +15703,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 292, - 293, - 294, + 288, + 289, + 290, ], - "id": 208, + "id": 204, "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "man_dir": "", "name": "chalk", @@ -15851,9 +15723,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 295, + 291, ], - "id": 209, + "id": 205, "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "man_dir": "", "name": "supports-color", @@ -15869,7 +15741,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 210, + "id": 206, "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "man_dir": "", "name": "has-flag", @@ -15885,7 +15757,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 211, + "id": 207, "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "man_dir": "", "name": "escape-string-regexp", @@ -15901,9 +15773,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 296, + 292, ], - "id": 212, + "id": 208, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "man_dir": "", "name": "ansi-styles", @@ -15919,9 +15791,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 297, + 293, ], - "id": 213, + "id": 209, "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "man_dir": "", "name": "color-convert", @@ -15937,7 +15809,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 214, + "id": 210, "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "man_dir": "", "name": "color-name", @@ -15953,7 +15825,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 215, + "id": 211, "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "man_dir": "", "name": "@babel/helper-validator-identifier", @@ -15972,9 +15844,9 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "js-yaml", }, "dependencies": [ - 298, + 294, ], - "id": 216, + "id": 212, "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "man_dir": "", "name": "js-yaml", @@ -15990,7 +15862,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 217, + "id": 213, "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "man_dir": "", "name": "argparse", @@ -16006,10 +15878,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 299, - 300, + 295, + 296, ], - "id": 218, + "id": 214, "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "man_dir": "", "name": "import-fresh", @@ -16025,7 +15897,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 219, + "id": 215, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "man_dir": "", "name": "resolve-from", @@ -16041,9 +15913,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 301, + 297, ], - "id": 220, + "id": 216, "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "man_dir": "", "name": "parent-module", @@ -16059,7 +15931,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 221, + "id": 217, "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "man_dir": "", "name": "callsites", @@ -16075,7 +15947,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 222, + "id": 218, "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "man_dir": "", "name": "env-paths", @@ -16094,6 +15966,10 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "next", }, "dependencies": [ + 298, + 299, + 300, + 301, 302, 303, 304, @@ -16110,12 +15986,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 315, 316, 317, - 318, - 319, - 320, - 321, ], - "id": 223, + "id": 219, "integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==", "man_dir": "", "name": "next", @@ -16134,7 +16006,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 224, + "id": 220, "integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==", "man_dir": "", "name": "@next/swc-win32-arm64-msvc", @@ -16156,7 +16028,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 225, + "id": 221, "integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==", "man_dir": "", "name": "@next/swc-linux-arm64-musl", @@ -16178,7 +16050,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 226, + "id": 222, "integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==", "man_dir": "", "name": "@next/swc-win32-ia32-msvc", @@ -16200,7 +16072,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 227, + "id": 223, "integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==", "man_dir": "", "name": "@next/swc-linux-arm64-gnu", @@ -16222,7 +16094,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 228, + "id": 224, "integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==", "man_dir": "", "name": "@next/swc-win32-x64-msvc", @@ -16244,7 +16116,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 229, + "id": 225, "integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==", "man_dir": "", "name": "@next/swc-linux-x64-musl", @@ -16266,7 +16138,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 230, + "id": 226, "integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==", "man_dir": "", "name": "@next/swc-linux-x64-gnu", @@ -16288,7 +16160,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 231, + "id": 227, "integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==", "man_dir": "", "name": "@next/swc-darwin-arm64", @@ -16310,7 +16182,7 @@ exports[`ssr works for 100-ish requests 1`] = ` ], "bin": null, "dependencies": [], - "id": 232, + "id": 228, "integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==", "man_dir": "", "name": "@next/swc-darwin-x64", @@ -16329,7 +16201,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 233, + "id": 229, "integrity": "sha512-S3BnR4Kh26TBxbi5t5kpbcUlLJb9lhtDXISDPwOfI+JoC+ik0QksvkZtUVyikw3hjnkgkMPSJ8oIM9yMm9vflA==", "man_dir": "", "name": "caniuse-lite", @@ -16345,9 +16217,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 322, + 318, ], - "id": 234, + "id": 230, "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "man_dir": "", "name": "@swc/helpers", @@ -16363,10 +16235,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 323, - 324, + 319, + 320, ], - "id": 235, + "id": 231, "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "man_dir": "", "name": "styled-jsx", @@ -16382,7 +16254,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 236, + "id": 232, "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "man_dir": "", "name": "client-only", @@ -16398,7 +16270,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 237, + "id": 233, "integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==", "man_dir": "", "name": "@next/env", @@ -16414,11 +16286,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 325, - 326, - 327, + 321, + 322, + 323, ], - "id": 238, + "id": 234, "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "man_dir": "", "name": "postcss", @@ -16434,9 +16306,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 328, + 324, ], - "id": 239, + "id": 235, "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "man_dir": "", "name": "busboy", @@ -16452,7 +16324,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 240, + "id": 236, "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "man_dir": "", "name": "streamsearch", @@ -16468,6 +16340,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 325, + 326, + 327, + 328, 329, 330, 331, @@ -16475,12 +16351,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 333, 334, 335, - 336, - 337, - 338, - 339, ], - "id": 241, + "id": 237, "integrity": "sha512-sUCpWlGuHpEhI0pIT0UtdSLJk5Z8E2DYinPTwsBiWaSYQomchdl0i60pjynY48+oXvtyWMQ7oE+G3m49yrfacg==", "man_dir": "", "name": "eslint-config-next", @@ -16499,6 +16371,10 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "eslint", }, "dependencies": [ + 336, + 337, + 338, + 339, 340, 341, 342, @@ -16532,12 +16408,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 370, 371, 372, - 373, - 374, - 375, - 376, ], - "id": 242, + "id": 238, "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "man_dir": "", "name": "eslint", @@ -16553,7 +16425,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 243, + "id": 239, "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "man_dir": "", "name": "json-stable-stringify-without-jsonify", @@ -16569,7 +16441,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 244, + "id": 240, "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "man_dir": "", "name": "@humanwhocodes/module-importer", @@ -16585,10 +16457,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 377, - 378, + 373, + 374, ], - "id": 245, + "id": 241, "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "man_dir": "", "name": "@eslint-community/eslint-utils", @@ -16604,7 +16476,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 246, + "id": 242, "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "man_dir": "", "name": "eslint-visitor-keys", @@ -16620,11 +16492,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 379, - 380, - 381, + 375, + 376, + 377, ], - "id": 247, + "id": 243, "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "man_dir": "", "name": "@humanwhocodes/config-array", @@ -16640,9 +16512,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 382, + 378, ], - "id": 248, + "id": 244, "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "man_dir": "", "name": "minimatch", @@ -16658,10 +16530,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 383, - 384, + 379, + 380, ], - "id": 249, + "id": 245, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "man_dir": "", "name": "brace-expansion", @@ -16677,7 +16549,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 250, + "id": 246, "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "man_dir": "", "name": "concat-map", @@ -16693,7 +16565,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 251, + "id": 247, "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "man_dir": "", "name": "@humanwhocodes/object-schema", @@ -16709,7 +16581,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 252, + "id": 248, "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "man_dir": "", "name": "@eslint-community/regexpp", @@ -16725,7 +16597,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 253, + "id": 249, "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "man_dir": "", "name": "escape-string-regexp", @@ -16741,9 +16613,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 385, + 381, ], - "id": 254, + "id": 250, "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "man_dir": "", "name": "file-entry-cache", @@ -16759,11 +16631,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 386, - 387, - 388, + 382, + 383, + 384, ], - "id": 255, + "id": 251, "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "man_dir": "", "name": "flat-cache", @@ -16782,9 +16654,9 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "rimraf", }, "dependencies": [ - 389, + 385, ], - "id": 256, + "id": 252, "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "man_dir": "", "name": "rimraf", @@ -16800,14 +16672,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 386, + 387, + 388, + 389, 390, 391, - 392, - 393, - 394, - 395, ], - "id": 257, + "id": 253, "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "man_dir": "", "name": "glob", @@ -16823,7 +16695,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 258, + "id": 254, "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "man_dir": "", "name": "path-is-absolute", @@ -16839,7 +16711,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 259, + "id": 255, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "man_dir": "", "name": "inherits", @@ -16855,10 +16727,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 396, - 397, + 392, + 393, ], - "id": 260, + "id": 256, "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "man_dir": "", "name": "inflight", @@ -16874,7 +16746,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 261, + "id": 257, "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "man_dir": "", "name": "fs.realpath", @@ -16890,9 +16762,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 398, + 394, ], - "id": 262, + "id": 258, "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "man_dir": "", "name": "keyv", @@ -16908,7 +16780,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 263, + "id": 259, "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "man_dir": "", "name": "json-buffer", @@ -16924,7 +16796,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 264, + "id": 260, "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "man_dir": "", "name": "flatted", @@ -16940,17 +16812,17 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 395, + 396, + 397, + 398, 399, 400, 401, 402, 403, - 404, - 405, - 406, - 407, ], - "id": 265, + "id": 261, "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "man_dir": "", "name": "@eslint/eslintrc", @@ -16966,7 +16838,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 266, + "id": 262, "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "man_dir": "", "name": "strip-json-comments", @@ -16982,7 +16854,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 267, + "id": 263, "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "man_dir": "", "name": "ignore", @@ -16998,9 +16870,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 408, + 404, ], - "id": 268, + "id": 264, "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "man_dir": "", "name": "globals", @@ -17016,7 +16888,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 269, + "id": 265, "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "man_dir": "", "name": "type-fest", @@ -17032,11 +16904,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 409, - 410, - 411, + 405, + 406, + 407, ], - "id": 270, + "id": 266, "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "man_dir": "", "name": "espree", @@ -17052,9 +16924,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 412, + 408, ], - "id": 271, + "id": 267, "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "man_dir": "", "name": "acorn-jsx", @@ -17073,7 +16945,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "acorn", }, "dependencies": [], - "id": 272, + "id": 268, "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "man_dir": "", "name": "acorn", @@ -17089,12 +16961,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 413, - 414, - 415, - 416, + 409, + 410, + 411, + 412, ], - "id": 273, + "id": 269, "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "man_dir": "", "name": "ajv", @@ -17110,9 +16982,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 417, + 413, ], - "id": 274, + "id": 270, "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "man_dir": "", "name": "uri-js", @@ -17128,7 +17000,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 275, + "id": 271, "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "man_dir": "", "name": "punycode", @@ -17144,7 +17016,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 276, + "id": 272, "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "man_dir": "", "name": "json-schema-traverse", @@ -17160,7 +17032,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 277, + "id": 273, "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "man_dir": "", "name": "fast-json-stable-stringify", @@ -17176,7 +17048,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 278, + "id": 274, "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "man_dir": "", "name": "fast-deep-equal", @@ -17192,7 +17064,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 279, + "id": 275, "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "man_dir": "", "name": "natural-compare", @@ -17208,7 +17080,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 280, + "id": 276, "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "man_dir": "", "name": "is-path-inside", @@ -17224,7 +17096,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 281, + "id": 277, "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "man_dir": "", "name": "lodash.merge", @@ -17240,10 +17112,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 418, - 419, + 414, + 415, ], - "id": 282, + "id": 278, "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "man_dir": "", "name": "eslint-scope", @@ -17259,9 +17131,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 420, + 416, ], - "id": 283, + "id": 279, "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "man_dir": "", "name": "esrecurse", @@ -17277,7 +17149,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 284, + "id": 280, "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "man_dir": "", "name": "imurmurhash", @@ -17293,7 +17165,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 285, + "id": 281, "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "man_dir": "", "name": "text-table", @@ -17309,14 +17181,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 417, + 418, + 419, + 420, 421, 422, - 423, - 424, - 425, - 426, ], - "id": 286, + "id": 282, "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "man_dir": "", "name": "optionator", @@ -17332,7 +17204,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 287, + "id": 283, "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "man_dir": "", "name": "fast-levenshtein", @@ -17348,10 +17220,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 427, - 428, + 423, + 424, ], - "id": 288, + "id": 284, "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "man_dir": "", "name": "levn", @@ -17367,9 +17239,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 429, + 425, ], - "id": 289, + "id": 285, "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "man_dir": "", "name": "type-check", @@ -17385,7 +17257,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 290, + "id": 286, "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "man_dir": "", "name": "prelude-ls", @@ -17401,7 +17273,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 291, + "id": 287, "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "man_dir": "", "name": "word-wrap", @@ -17417,7 +17289,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 292, + "id": 288, "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "man_dir": "", "name": "deep-is", @@ -17433,7 +17305,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 293, + "id": 289, "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "man_dir": "", "name": "@eslint/js", @@ -17449,7 +17321,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 294, + "id": 290, "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "man_dir": "", "name": "graphemer", @@ -17465,9 +17337,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 430, + 426, ], - "id": 295, + "id": 291, "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "man_dir": "", "name": "doctrine", @@ -17483,10 +17355,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 431, - 432, + 427, + 428, ], - "id": 296, + "id": 292, "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "man_dir": "", "name": "find-up", @@ -17502,7 +17374,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 297, + "id": 293, "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "man_dir": "", "name": "path-exists", @@ -17518,9 +17390,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 433, + 429, ], - "id": 298, + "id": 294, "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "man_dir": "", "name": "locate-path", @@ -17536,9 +17408,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 434, + 430, ], - "id": 299, + "id": 295, "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "man_dir": "", "name": "p-locate", @@ -17554,9 +17426,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 435, + 431, ], - "id": 300, + "id": 296, "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "man_dir": "", "name": "p-limit", @@ -17572,7 +17444,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 301, + "id": 297, "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "man_dir": "", "name": "yocto-queue", @@ -17588,9 +17460,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 436, + 432, ], - "id": 302, + "id": 298, "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "man_dir": "", "name": "esquery", @@ -17606,10 +17478,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 437, - 438, + 433, + 434, ], - "id": 303, + "id": 299, "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "man_dir": "", "name": "chalk", @@ -17625,9 +17497,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 439, + 435, ], - "id": 304, + "id": 300, "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "man_dir": "", "name": "supports-color", @@ -17643,7 +17515,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 305, + "id": 301, "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "man_dir": "", "name": "has-flag", @@ -17659,17 +17531,17 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 436, + 437, + 438, + 439, 440, 441, 442, 443, 444, - 445, - 446, - 447, - 448, ], - "id": 306, + "id": 302, "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", "man_dir": "", "name": "eslint-import-resolver-typescript", @@ -17685,6 +17557,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 445, + 446, + 447, + 448, 449, 450, 451, @@ -17699,12 +17575,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 460, 461, 462, - 463, - 464, - 465, - 466, ], - "id": 307, + "id": 303, "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "man_dir": "", "name": "eslint-plugin-import", @@ -17720,12 +17592,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 467, - 468, - 469, - 470, + 463, + 464, + 465, + 466, ], - "id": 308, + "id": 304, "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "man_dir": "", "name": "tsconfig-paths", @@ -17741,7 +17613,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 309, + "id": 305, "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "man_dir": "", "name": "strip-bom", @@ -17757,7 +17629,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 310, + "id": 306, "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "man_dir": "", "name": "minimist", @@ -17776,9 +17648,9 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "json5", }, "dependencies": [ - 471, + 467, ], - "id": 311, + "id": 307, "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "man_dir": "", "name": "json5", @@ -17794,7 +17666,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 312, + "id": 308, "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "man_dir": "", "name": "@types/json5", @@ -17813,7 +17685,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "semver", }, "dependencies": [], - "id": 313, + "id": 309, "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "man_dir": "", "name": "semver", @@ -17829,11 +17701,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 472, - 473, - 474, + 468, + 469, + 470, ], - "id": 314, + "id": 310, "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "man_dir": "", "name": "object.values", @@ -17849,9 +17721,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 475, + 471, ], - "id": 315, + "id": 311, "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "man_dir": "", "name": "es-object-atoms", @@ -17867,7 +17739,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 316, + "id": 312, "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "man_dir": "", "name": "es-errors", @@ -17883,11 +17755,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 476, - 477, - 478, + 472, + 473, + 474, ], - "id": 317, + "id": 313, "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "man_dir": "", "name": "define-properties", @@ -17903,7 +17775,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 318, + "id": 314, "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "man_dir": "", "name": "object-keys", @@ -17919,9 +17791,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 479, + 475, ], - "id": 319, + "id": 315, "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "man_dir": "", "name": "has-property-descriptors", @@ -17937,9 +17809,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 480, + 476, ], - "id": 320, + "id": 316, "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "man_dir": "", "name": "es-define-property", @@ -17955,13 +17827,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 477, + 478, + 479, + 480, 481, - 482, - 483, - 484, - 485, ], - "id": 321, + "id": 317, "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "man_dir": "", "name": "get-intrinsic", @@ -17977,7 +17849,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 322, + "id": 318, "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "man_dir": "", "name": "has-symbols", @@ -17993,7 +17865,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 323, + "id": 319, "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "man_dir": "", "name": "has-proto", @@ -18009,11 +17881,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 486, - 487, - 488, + 482, + 483, + 484, ], - "id": 324, + "id": 320, "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "man_dir": "", "name": "define-data-property", @@ -18029,9 +17901,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 489, + 485, ], - "id": 325, + "id": 321, "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "man_dir": "", "name": "gopd", @@ -18047,13 +17919,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 486, + 487, + 488, + 489, 490, - 491, - 492, - 493, - 494, ], - "id": 326, + "id": 322, "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "man_dir": "", "name": "call-bind", @@ -18069,14 +17941,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 491, + 492, + 493, + 494, 495, 496, - 497, - 498, - 499, - 500, ], - "id": 327, + "id": 323, "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "man_dir": "", "name": "set-function-length", @@ -18092,11 +17964,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 501, - 502, - 503, + 497, + 498, + 499, ], - "id": 328, + "id": 324, "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "man_dir": "", "name": "object.groupby", @@ -18112,6 +17984,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 500, + 501, + 502, + 503, 504, 505, 506, @@ -18154,12 +18030,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 543, 544, 545, - 546, - 547, - 548, - 549, ], - "id": 329, + "id": 325, "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "man_dir": "", "name": "es-abstract", @@ -18175,13 +18047,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 546, + 547, + 548, + 549, 550, - 551, - 552, - 553, - 554, ], - "id": 330, + "id": 326, "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "man_dir": "", "name": "which-typed-array", @@ -18197,9 +18069,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 555, + 551, ], - "id": 331, + "id": 327, "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "man_dir": "", "name": "has-tostringtag", @@ -18215,9 +18087,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 556, + 552, ], - "id": 332, + "id": 328, "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "man_dir": "", "name": "for-each", @@ -18233,7 +18105,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 333, + "id": 329, "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "man_dir": "", "name": "is-callable", @@ -18249,9 +18121,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 557, + 553, ], - "id": 334, + "id": 330, "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "man_dir": "", "name": "available-typed-arrays", @@ -18267,7 +18139,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 335, + "id": 331, "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "man_dir": "", "name": "possible-typed-array-names", @@ -18283,12 +18155,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 558, - 559, - 560, - 561, + 554, + 555, + 556, + 557, ], - "id": 336, + "id": 332, "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "man_dir": "", "name": "unbox-primitive", @@ -18304,13 +18176,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 558, + 559, + 560, + 561, 562, - 563, - 564, - 565, - 566, ], - "id": 337, + "id": 333, "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "man_dir": "", "name": "which-boxed-primitive", @@ -18326,9 +18198,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 567, + 563, ], - "id": 338, + "id": 334, "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "man_dir": "", "name": "is-symbol", @@ -18344,9 +18216,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 568, + 564, ], - "id": 339, + "id": 335, "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "man_dir": "", "name": "is-string", @@ -18362,9 +18234,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 569, + 565, ], - "id": 340, + "id": 336, "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "man_dir": "", "name": "is-number-object", @@ -18380,10 +18252,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 570, - 571, + 566, + 567, ], - "id": 341, + "id": 337, "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "man_dir": "", "name": "is-boolean-object", @@ -18399,9 +18271,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 572, + 568, ], - "id": 342, + "id": 338, "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "man_dir": "", "name": "is-bigint", @@ -18417,7 +18289,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 343, + "id": 339, "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "man_dir": "", "name": "has-bigints", @@ -18433,14 +18305,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 569, + 570, + 571, + 572, 573, 574, - 575, - 576, - 577, - 578, ], - "id": 344, + "id": 340, "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "man_dir": "", "name": "typed-array-length", @@ -18456,9 +18328,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 579, + 575, ], - "id": 345, + "id": 341, "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "man_dir": "", "name": "is-typed-array", @@ -18474,14 +18346,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 576, + 577, + 578, + 579, 580, 581, - 582, - 583, - 584, - 585, ], - "id": 346, + "id": 342, "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "man_dir": "", "name": "typed-array-byte-offset", @@ -18497,13 +18369,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 582, + 583, + 584, + 585, 586, - 587, - 588, - 589, - 590, ], - "id": 347, + "id": 343, "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "man_dir": "", "name": "typed-array-byte-length", @@ -18519,11 +18391,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 591, - 592, - 593, + 587, + 588, + 589, ], - "id": 348, + "id": 344, "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "man_dir": "", "name": "typed-array-buffer", @@ -18539,11 +18411,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 594, - 595, - 596, + 590, + 591, + 592, ], - "id": 349, + "id": 345, "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "man_dir": "", "name": "string.prototype.trimstart", @@ -18559,11 +18431,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 597, - 598, - 599, + 593, + 594, + 595, ], - "id": 350, + "id": 346, "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "man_dir": "", "name": "string.prototype.trimend", @@ -18579,12 +18451,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 600, - 601, - 602, - 603, + 596, + 597, + 598, + 599, ], - "id": 351, + "id": 347, "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "man_dir": "", "name": "string.prototype.trim", @@ -18600,11 +18472,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 604, - 605, - 606, + 600, + 601, + 602, ], - "id": 352, + "id": 348, "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "man_dir": "", "name": "safe-regex-test", @@ -18620,10 +18492,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 607, - 608, + 603, + 604, ], - "id": 353, + "id": 349, "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "man_dir": "", "name": "is-regex", @@ -18639,12 +18511,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 609, - 610, - 611, - 612, + 605, + 606, + 607, + 608, ], - "id": 354, + "id": 350, "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "man_dir": "", "name": "safe-array-concat", @@ -18660,7 +18532,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 355, + "id": 351, "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "man_dir": "", "name": "isarray", @@ -18676,12 +18548,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 613, - 614, - 615, - 616, + 609, + 610, + 611, + 612, ], - "id": 356, + "id": 352, "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "man_dir": "", "name": "regexp.prototype.flags", @@ -18697,12 +18569,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 617, - 618, - 619, - 620, + 613, + 614, + 615, + 616, ], - "id": 357, + "id": 353, "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "man_dir": "", "name": "set-function-name", @@ -18718,7 +18590,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 358, + "id": 354, "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "man_dir": "", "name": "functions-have-names", @@ -18734,12 +18606,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 621, - 622, - 623, - 624, + 617, + 618, + 619, + 620, ], - "id": 359, + "id": 355, "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "man_dir": "", "name": "object.assign", @@ -18755,7 +18627,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 360, + "id": 356, "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "man_dir": "", "name": "object-inspect", @@ -18771,9 +18643,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 625, + 621, ], - "id": 361, + "id": 357, "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "man_dir": "", "name": "is-weakref", @@ -18789,9 +18661,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 626, + 622, ], - "id": 362, + "id": 358, "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "man_dir": "", "name": "is-shared-array-buffer", @@ -18807,7 +18679,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 363, + "id": 359, "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "man_dir": "", "name": "is-negative-zero", @@ -18823,9 +18695,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 627, + 623, ], - "id": 364, + "id": 360, "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "man_dir": "", "name": "is-data-view", @@ -18841,10 +18713,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 628, - 629, + 624, + 625, ], - "id": 365, + "id": 361, "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "man_dir": "", "name": "is-array-buffer", @@ -18860,11 +18732,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 630, - 631, - 632, + 626, + 627, + 628, ], - "id": 366, + "id": 362, "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "man_dir": "", "name": "internal-slot", @@ -18880,12 +18752,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 633, - 634, - 635, - 636, + 629, + 630, + 631, + 632, ], - "id": 367, + "id": 363, "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "man_dir": "", "name": "side-channel", @@ -18901,10 +18773,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 637, - 638, + 633, + 634, ], - "id": 368, + "id": 364, "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "man_dir": "", "name": "globalthis", @@ -18920,11 +18792,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 639, - 640, - 641, + 635, + 636, + 637, ], - "id": 369, + "id": 365, "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "man_dir": "", "name": "get-symbol-description", @@ -18940,12 +18812,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 642, - 643, - 644, - 645, + 638, + 639, + 640, + 641, ], - "id": 370, + "id": 366, "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "man_dir": "", "name": "function.prototype.name", @@ -18961,11 +18833,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 646, - 647, - 648, + 642, + 643, + 644, ], - "id": 371, + "id": 367, "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "man_dir": "", "name": "es-to-primitive", @@ -18981,9 +18853,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 649, + 645, ], - "id": 372, + "id": 368, "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "man_dir": "", "name": "is-date-object", @@ -18999,11 +18871,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 650, - 651, - 652, + 646, + 647, + 648, ], - "id": 373, + "id": 369, "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "man_dir": "", "name": "es-set-tostringtag", @@ -19019,11 +18891,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 653, - 654, - 655, + 649, + 650, + 651, ], - "id": 374, + "id": 370, "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "man_dir": "", "name": "data-view-byte-offset", @@ -19039,11 +18911,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 656, - 657, - 658, + 652, + 653, + 654, ], - "id": 375, + "id": 371, "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "man_dir": "", "name": "data-view-byte-length", @@ -19059,11 +18931,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 659, - 660, - 661, + 655, + 656, + 657, ], - "id": 376, + "id": 372, "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "man_dir": "", "name": "data-view-buffer", @@ -19079,16 +18951,16 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 658, + 659, + 660, + 661, 662, 663, 664, 665, - 666, - 667, - 668, - 669, ], - "id": 377, + "id": 373, "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "man_dir": "", "name": "arraybuffer.prototype.slice", @@ -19104,10 +18976,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 670, - 671, + 666, + 667, ], - "id": 378, + "id": 374, "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "man_dir": "", "name": "array-buffer-byte-length", @@ -19123,12 +18995,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 672, - 673, - 674, - 675, + 668, + 669, + 670, + 671, ], - "id": 379, + "id": 375, "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "man_dir": "", "name": "object.fromentries", @@ -19144,9 +19016,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 676, + 672, ], - "id": 380, + "id": 376, "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "man_dir": "", "name": "eslint-module-utils", @@ -19162,9 +19034,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 677, + 673, ], - "id": 381, + "id": 377, "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "man_dir": "", "name": "debug", @@ -19180,11 +19052,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 678, - 679, - 680, + 674, + 675, + 676, ], - "id": 382, + "id": 378, "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "man_dir": "", "name": "eslint-import-resolver-node", @@ -19200,9 +19072,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 681, + 677, ], - "id": 383, + "id": 379, "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "man_dir": "", "name": "doctrine", @@ -19218,12 +19090,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 682, - 683, - 684, - 685, + 678, + 679, + 680, + 681, ], - "id": 384, + "id": 380, "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "man_dir": "", "name": "array.prototype.flatmap", @@ -19239,9 +19111,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 686, + 682, ], - "id": 385, + "id": 381, "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "man_dir": "", "name": "es-shim-unscopables", @@ -19257,12 +19129,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 687, - 688, - 689, - 690, + 683, + 684, + 685, + 686, ], - "id": 386, + "id": 382, "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "man_dir": "", "name": "array.prototype.flat", @@ -19278,14 +19150,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 687, + 688, + 689, + 690, 691, 692, - 693, - 694, - 695, - 696, ], - "id": 387, + "id": 383, "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "man_dir": "", "name": "array.prototype.findlastindex", @@ -19301,14 +19173,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 693, + 694, + 695, + 696, 697, 698, - 699, - 700, - 701, - 702, ], - "id": 388, + "id": 384, "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "man_dir": "", "name": "array-includes", @@ -19324,9 +19196,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 703, + 699, ], - "id": 389, + "id": 385, "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "man_dir": "", "name": "get-tsconfig", @@ -19342,7 +19214,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 390, + "id": 386, "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "man_dir": "", "name": "resolve-pkg-maps", @@ -19358,10 +19230,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 704, - 705, + 700, + 701, ], - "id": 391, + "id": 387, "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "man_dir": "", "name": "enhanced-resolve", @@ -19377,7 +19249,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 392, + "id": 388, "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "man_dir": "", "name": "tapable", @@ -19393,9 +19265,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 706, + 702, ], - "id": 393, + "id": 389, "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "man_dir": "", "name": "eslint-plugin-react-hooks", @@ -19411,14 +19283,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 703, + 704, + 705, + 706, 707, 708, - 709, - 710, - 711, - 712, ], - "id": 394, + "id": 390, "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "man_dir": "", "name": "@typescript-eslint/parser", @@ -19434,16 +19306,16 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 709, + 710, + 711, + 712, 713, 714, 715, 716, - 717, - 718, - 719, - 720, ], - "id": 395, + "id": 391, "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "man_dir": "", "name": "@typescript-eslint/typescript-estree", @@ -19459,10 +19331,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 721, - 722, + 717, + 718, ], - "id": 396, + "id": 392, "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "man_dir": "", "name": "@typescript-eslint/visitor-keys", @@ -19478,7 +19350,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 397, + "id": 393, "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "man_dir": "", "name": "@typescript-eslint/types", @@ -19494,9 +19366,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 723, + 719, ], - "id": 398, + "id": 394, "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "man_dir": "", "name": "ts-api-utils", @@ -19512,9 +19384,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 724, + 720, ], - "id": 399, + "id": 395, "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "man_dir": "", "name": "minimatch", @@ -19530,14 +19402,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 721, + 722, + 723, + 724, 725, 726, - 727, - 728, - 729, - 730, ], - "id": 400, + "id": 396, "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "man_dir": "", "name": "globby", @@ -19553,7 +19425,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 401, + "id": 397, "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "man_dir": "", "name": "slash", @@ -19569,9 +19441,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 731, + 727, ], - "id": 402, + "id": 398, "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "man_dir": "", "name": "dir-glob", @@ -19587,7 +19459,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 403, + "id": 399, "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "man_dir": "", "name": "path-type", @@ -19603,7 +19475,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 404, + "id": 400, "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "man_dir": "", "name": "array-union", @@ -19619,10 +19491,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 732, - 733, + 728, + 729, ], - "id": 405, + "id": 401, "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "man_dir": "", "name": "@typescript-eslint/scope-manager", @@ -19638,9 +19510,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 734, + 730, ], - "id": 406, + "id": 402, "integrity": "sha512-VCnZI2cy77Yaj3L7Uhs3+44ikMM1VD/fBMwvTBb3hIaTIuqa+DmG4dhUDq+MASu3yx97KhgsVJbsas0XuiKyww==", "man_dir": "", "name": "@next/eslint-plugin-next", @@ -19656,7 +19528,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 407, + "id": 403, "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "man_dir": "", "name": "@rushstack/eslint-patch", @@ -19672,6 +19544,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 731, + 732, + 733, + 734, 735, 736, 737, @@ -19685,12 +19561,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 745, 746, 747, - 748, - 749, - 750, - 751, ], - "id": 408, + "id": 404, "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", "man_dir": "", "name": "eslint-plugin-jsx-a11y", @@ -19706,11 +19578,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 752, - 753, - 754, + 748, + 749, + 750, ], - "id": 409, + "id": 405, "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "man_dir": "", "name": "object.entries", @@ -19726,9 +19598,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 755, + 751, ], - "id": 410, + "id": 406, "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "man_dir": "", "name": "language-tags", @@ -19744,7 +19616,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 411, + "id": 407, "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "man_dir": "", "name": "language-subtag-registry", @@ -19760,12 +19632,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 756, - 757, - 758, - 759, + 752, + 753, + 754, + 755, ], - "id": 412, + "id": 408, "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "man_dir": "", "name": "jsx-ast-utils", @@ -19781,6 +19653,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 756, + 757, + 758, + 759, 760, 761, 762, @@ -19791,12 +19667,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 767, 768, 769, - 770, - 771, - 772, - 773, ], - "id": 413, + "id": 409, "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "man_dir": "", "name": "es-iterator-helpers", @@ -19812,13 +19684,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 770, + 771, + 772, + 773, 774, - 775, - 776, - 777, - 778, ], - "id": 414, + "id": 410, "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "man_dir": "", "name": "iterator.prototype", @@ -19834,15 +19706,15 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 775, + 776, + 777, + 778, 779, 780, 781, - 782, - 783, - 784, - 785, ], - "id": 415, + "id": 411, "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "man_dir": "", "name": "reflect.getprototypeof", @@ -19858,6 +19730,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 782, + 783, + 784, + 785, 786, 787, 788, @@ -19866,12 +19742,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 791, 792, 793, - 794, - 795, - 796, - 797, ], - "id": 416, + "id": 412, "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", "man_dir": "", "name": "which-builtin-type", @@ -19887,12 +19759,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 798, - 799, - 800, - 801, + 794, + 795, + 796, + 797, ], - "id": 417, + "id": 413, "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "man_dir": "", "name": "which-collection", @@ -19908,10 +19780,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 802, - 803, + 798, + 799, ], - "id": 418, + "id": 414, "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "man_dir": "", "name": "is-weakset", @@ -19927,7 +19799,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 419, + "id": 415, "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "man_dir": "", "name": "is-weakmap", @@ -19943,7 +19815,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 420, + "id": 416, "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "man_dir": "", "name": "is-set", @@ -19959,7 +19831,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 421, + "id": 417, "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "man_dir": "", "name": "is-map", @@ -19975,9 +19847,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 804, + 800, ], - "id": 422, + "id": 418, "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "man_dir": "", "name": "is-generator-function", @@ -19993,9 +19865,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 805, + 801, ], - "id": 423, + "id": 419, "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "man_dir": "", "name": "is-finalizationregistry", @@ -20011,9 +19883,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 806, + 802, ], - "id": 424, + "id": 420, "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "man_dir": "", "name": "is-async-function", @@ -20029,7 +19901,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 425, + "id": 421, "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "man_dir": "", "name": "damerau-levenshtein", @@ -20045,9 +19917,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 807, + 803, ], - "id": 426, + "id": 422, "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "man_dir": "", "name": "axobject-query", @@ -20063,7 +19935,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 427, + "id": 423, "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "man_dir": "", "name": "dequal", @@ -20079,7 +19951,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 428, + "id": 424, "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "man_dir": "", "name": "axe-core", @@ -20095,7 +19967,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 429, + "id": 425, "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "man_dir": "", "name": "ast-types-flow", @@ -20111,9 +19983,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 808, + 804, ], - "id": 430, + "id": 426, "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "man_dir": "", "name": "aria-query", @@ -20129,9 +20001,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 809, + 805, ], - "id": 431, + "id": 427, "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "man_dir": "", "name": "@babel/runtime", @@ -20147,7 +20019,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 432, + "id": 428, "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "man_dir": "", "name": "regenerator-runtime", @@ -20163,6 +20035,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 806, + 807, + 808, + 809, 810, 811, 812, @@ -20178,12 +20054,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 822, 823, 824, - 825, - 826, - 827, - 828, ], - "id": 433, + "id": 429, "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", "man_dir": "", "name": "eslint-plugin-react", @@ -20199,6 +20071,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 825, + 826, + 827, + 828, 829, 830, 831, @@ -20207,12 +20083,8 @@ exports[`ssr works for 100-ish requests 1`] = ` 834, 835, 836, - 837, - 838, - 839, - 840, ], - "id": 434, + "id": 430, "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "man_dir": "", "name": "string.prototype.matchall", @@ -20231,11 +20103,11 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "resolve", }, "dependencies": [ - 841, - 842, - 843, + 837, + 838, + 839, ], - "id": 435, + "id": 431, "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "man_dir": "", "name": "resolve", @@ -20251,11 +20123,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 844, - 845, - 846, + 840, + 841, + 842, ], - "id": 436, + "id": 432, "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "man_dir": "", "name": "prop-types", @@ -20271,7 +20143,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 437, + "id": 433, "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "man_dir": "", "name": "react-is", @@ -20287,11 +20159,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 847, - 848, - 849, + 843, + 844, + 845, ], - "id": 438, + "id": 434, "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "man_dir": "", "name": "object.hasown", @@ -20307,13 +20179,13 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 846, + 847, + 848, + 849, 850, - 851, - 852, - 853, - 854, ], - "id": 439, + "id": 435, "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "man_dir": "", "name": "array.prototype.tosorted", @@ -20329,12 +20201,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 855, - 856, - 857, - 858, + 851, + 852, + 853, + 854, ], - "id": 440, + "id": 436, "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", "man_dir": "", "name": "array.prototype.toreversed", @@ -20350,14 +20222,14 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ + 855, + 856, + 857, + 858, 859, 860, - 861, - 862, - 863, - 864, ], - "id": 441, + "id": 437, "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "man_dir": "", "name": "array.prototype.findlast", @@ -20373,10 +20245,10 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 865, - 866, + 861, + 862, ], - "id": 442, + "id": 438, "integrity": "sha512-DIM2C9qCECwhck9nLsCDeTv943VmGMCkwX3KljjprSRDXaK2CSiUDVGbUit80Er38ukgxuESJgYPAys4FsNCdg==", "man_dir": "", "name": "bun-types", @@ -20392,9 +20264,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 867, + 863, ], - "id": 443, + "id": 439, "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "man_dir": "", "name": "@types/ws", @@ -20413,15 +20285,15 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "autoprefixer", }, "dependencies": [ + 864, + 865, + 866, + 867, 868, 869, 870, - 871, - 872, - 873, - 874, ], - "id": 444, + "id": 440, "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "man_dir": "", "name": "autoprefixer", @@ -20437,7 +20309,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 445, + "id": 441, "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "man_dir": "", "name": "normalize-range", @@ -20453,7 +20325,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 446, + "id": 442, "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "man_dir": "", "name": "fraction.js", @@ -20472,12 +20344,12 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "browserslist", }, "dependencies": [ - 875, - 876, - 877, - 878, + 871, + 872, + 873, + 874, ], - "id": 447, + "id": 443, "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "man_dir": "", "name": "browserslist", @@ -20496,11 +20368,11 @@ exports[`ssr works for 100-ish requests 1`] = ` "name": "update-browserslist-db", }, "dependencies": [ - 879, - 880, - 881, + 875, + 876, + 877, ], - "id": 448, + "id": 444, "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "man_dir": "", "name": "update-browserslist-db", @@ -20516,7 +20388,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 449, + "id": 445, "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "man_dir": "", "name": "node-releases", @@ -20532,7 +20404,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 450, + "id": 446, "integrity": "sha512-eVGeQxpaBYbomDBa/Mehrs28MdvCXfJmEFzaMFsv8jH/MJDLIylJN81eTJ5kvx7B7p18OiPK0BkC06lydEy63A==", "man_dir": "", "name": "electron-to-chromium", @@ -20548,9 +20420,9 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 882, + 878, ], - "id": 451, + "id": 447, "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "man_dir": "", "name": "@types/react-dom", @@ -20566,11 +20438,11 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [ - 883, - 884, - 885, + 879, + 880, + 881, ], - "id": 452, + "id": 448, "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", "man_dir": "", "name": "@types/react", @@ -20586,7 +20458,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 453, + "id": 449, "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "man_dir": "", "name": "csstype", @@ -20602,7 +20474,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 454, + "id": 450, "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==", "man_dir": "", "name": "@types/scheduler", @@ -20618,7 +20490,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 455, + "id": 451, "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "man_dir": "", "name": "@types/prop-types", @@ -20634,7 +20506,7 @@ exports[`ssr works for 100-ish requests 1`] = ` { "bin": null, "dependencies": [], - "id": 456, + "id": 452, "integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==", "man_dir": "", "name": "@types/node", @@ -20656,48 +20528,48 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 13, }, "@babel/code-frame": { - "id": 281, - "package_id": 206, + "id": 277, + "package_id": 202, }, "@babel/helper-validator-identifier": { - "id": 288, - "package_id": 215, + "id": 284, + "package_id": 211, }, "@babel/highlight": { - "id": 286, - "package_id": 207, + "id": 282, + "package_id": 203, }, "@babel/runtime": { - "id": 735, - "package_id": 431, + "id": 731, + "package_id": 427, }, "@eslint-community/eslint-utils": { - "id": 374, - "package_id": 245, + "id": 370, + "package_id": 241, }, "@eslint-community/regexpp": { - "id": 372, - "package_id": 252, + "id": 368, + "package_id": 248, }, "@eslint/eslintrc": { - "id": 367, - "package_id": 265, + "id": 363, + "package_id": 261, }, "@eslint/js": { - "id": 355, - "package_id": 293, + "id": 351, + "package_id": 289, }, "@humanwhocodes/config-array": { - "id": 373, - "package_id": 247, + "id": 369, + "package_id": 243, }, "@humanwhocodes/module-importer": { - "id": 375, - "package_id": 244, + "id": 371, + "package_id": 240, }, "@humanwhocodes/object-schema": { - "id": 379, - "package_id": 251, + "id": 375, + "package_id": 247, }, "@isaacs/cliui": { "id": 111, @@ -20724,48 +20596,48 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 101, }, "@next/env": { - "id": 304, - "package_id": 237, + "id": 300, + "package_id": 233, }, "@next/eslint-plugin-next": { - "id": 333, - "package_id": 406, + "id": 329, + "package_id": 402, }, "@next/swc-darwin-arm64": { - "id": 310, - "package_id": 231, + "id": 306, + "package_id": 227, }, "@next/swc-darwin-x64": { - "id": 309, - "package_id": 232, + "id": 305, + "package_id": 228, }, "@next/swc-linux-arm64-gnu": { - "id": 314, - "package_id": 227, + "id": 310, + "package_id": 223, }, "@next/swc-linux-arm64-musl": { - "id": 316, - "package_id": 225, + "id": 312, + "package_id": 221, }, "@next/swc-linux-x64-gnu": { - "id": 311, - "package_id": 230, + "id": 307, + "package_id": 226, }, "@next/swc-linux-x64-musl": { - "id": 312, - "package_id": 229, + "id": 308, + "package_id": 225, }, "@next/swc-win32-arm64-msvc": { - "id": 317, - "package_id": 224, + "id": 313, + "package_id": 220, }, "@next/swc-win32-ia32-msvc": { - "id": 315, - "package_id": 226, + "id": 311, + "package_id": 222, }, "@next/swc-win32-x64-msvc": { - "id": 313, - "package_id": 228, + "id": 309, + "package_id": 224, }, "@nodelib/fs.scandir": { "id": 72, @@ -20776,7 +20648,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 49, }, "@nodelib/fs.walk": { - "id": 368, + "id": 364, "package_id": 43, }, "@pkgjs/parseargs": { @@ -20785,94 +20657,94 @@ exports[`ssr works for 100-ish requests 1`] = ` }, "@puppeteer/browsers": { "id": 155, - "package_id": 114, + "package_id": 115, }, "@rushstack/eslint-patch": { - "id": 332, - "package_id": 407, + "id": 328, + "package_id": 403, }, "@swc/helpers": { - "id": 307, - "package_id": 234, + "id": 303, + "package_id": 230, }, "@tootallnate/quickjs-emscripten": { - "id": 218, - "package_id": 177, + "id": 219, + "package_id": 178, }, "@types/json5": { - "id": 467, - "package_id": 312, + "id": 463, + "package_id": 308, }, "@types/node": { "id": 0, - "package_id": 456, + "package_id": 452, }, "@types/prop-types": { - "id": 883, - "package_id": 455, + "id": 879, + "package_id": 451, }, "@types/react": { "id": 1, - "package_id": 452, + "package_id": 448, }, "@types/react-dom": { "id": 2, - "package_id": 451, + "package_id": 447, }, "@types/scheduler": { - "id": 884, - "package_id": 454, + "id": 880, + "package_id": 450, }, "@types/ws": { - "id": 865, - "package_id": 443, + "id": 861, + "package_id": 439, }, "@types/yauzl": { - "id": 252, - "package_id": 181, + "id": 253, + "package_id": 182, }, "@typescript-eslint/parser": { - "id": 334, - "package_id": 394, + "id": 330, + "package_id": 390, }, "@typescript-eslint/scope-manager": { - "id": 710, - "package_id": 405, + "id": 706, + "package_id": 401, }, "@typescript-eslint/types": { - "id": 708, - "package_id": 397, + "id": 704, + "package_id": 393, }, "@typescript-eslint/typescript-estree": { - "id": 711, - "package_id": 395, + "id": 707, + "package_id": 391, }, "@typescript-eslint/visitor-keys": { - "id": 709, - "package_id": 396, + "id": 705, + "package_id": 392, }, "acorn": { - "id": 409, - "package_id": 272, + "id": 405, + "package_id": 268, }, "acorn-jsx": { - "id": 410, - "package_id": 271, + "id": 406, + "package_id": 267, }, "agent-base": { - "id": 201, - "package_id": 155, + "id": 202, + "package_id": 156, }, "ajv": { - "id": 340, - "package_id": 273, + "id": 336, + "package_id": 269, }, "ansi-regex": { "id": 122, "package_id": 77, }, "ansi-styles": { - "id": 437, + "id": 433, "package_id": 81, }, "any-promise": { @@ -20888,180 +20760,180 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 107, }, "argparse": { - "id": 298, - "package_id": 217, + "id": 294, + "package_id": 213, }, "aria-query": { - "id": 736, - "package_id": 430, + "id": 732, + "package_id": 426, }, "array-buffer-byte-length": { - "id": 504, - "package_id": 378, + "id": 500, + "package_id": 374, }, "array-includes": { - "id": 810, - "package_id": 388, + "id": 806, + "package_id": 384, }, "array-union": { - "id": 725, - "package_id": 404, + "id": 721, + "package_id": 400, }, "array.prototype.findlast": { - "id": 811, - "package_id": 441, + "id": 807, + "package_id": 437, }, "array.prototype.findlastindex": { - "id": 450, - "package_id": 387, + "id": 446, + "package_id": 383, }, "array.prototype.flat": { - "id": 451, - "package_id": 386, + "id": 447, + "package_id": 382, }, "array.prototype.flatmap": { - "id": 812, - "package_id": 384, + "id": 808, + "package_id": 380, }, "array.prototype.toreversed": { - "id": 813, - "package_id": 440, + "id": 809, + "package_id": 436, }, "array.prototype.tosorted": { - "id": 814, - "package_id": 439, + "id": 810, + "package_id": 435, }, "arraybuffer.prototype.slice": { - "id": 505, - "package_id": 377, + "id": 501, + "package_id": 373, }, "ast-types": { - "id": 228, - "package_id": 166, + "id": 229, + "package_id": 167, }, "ast-types-flow": { - "id": 739, - "package_id": 429, + "id": 735, + "package_id": 425, }, "autoprefixer": { "id": 3, - "package_id": 444, + "package_id": 440, }, "available-typed-arrays": { - "id": 506, - "package_id": 334, + "id": 502, + "package_id": 330, }, "axe-core": { - "id": 740, - "package_id": 428, + "id": 736, + "package_id": 424, }, "axobject-query": { - "id": 741, - "package_id": 426, + "id": 737, + "package_id": 422, }, "b4a": { - "id": 194, - "package_id": 138, + "id": 195, + "package_id": 139, }, "balanced-match": { - "id": 383, + "id": 379, "package_id": 71, }, "bare-events": { - "id": 185, - "package_id": 136, + "id": 186, + "package_id": 137, }, "bare-fs": { - "id": 182, - "package_id": 133, + "id": 183, + "package_id": 134, }, "bare-os": { - "id": 184, - "package_id": 132, + "id": 185, + "package_id": 133, }, "bare-path": { - "id": 183, - "package_id": 131, + "id": 184, + "package_id": 132, }, "bare-stream": { - "id": 187, - "package_id": 134, + "id": 188, + "package_id": 135, }, "base64-js": { - "id": 178, - "package_id": 129, + "id": 179, + "package_id": 130, }, "basic-ftp": { - "id": 240, - "package_id": 176, + "id": 241, + "package_id": 177, }, "binary-extensions": { "id": 87, "package_id": 54, }, "brace-expansion": { - "id": 382, - "package_id": 249, + "id": 378, + "package_id": 245, }, "braces": { "id": 79, "package_id": 34, }, "browserslist": { - "id": 868, - "package_id": 447, + "id": 864, + "package_id": 443, }, "buffer": { - "id": 176, - "package_id": 127, + "id": 177, + "package_id": 128, }, "buffer-crc32": { - "id": 256, - "package_id": 185, + "id": 257, + "package_id": 186, }, "bun-types": { "id": 4, - "package_id": 442, + "package_id": 438, }, "busboy": { - "id": 302, - "package_id": 239, + "id": 298, + "package_id": 235, }, "call-bind": { - "id": 697, - "package_id": 326, + "id": 693, + "package_id": 322, }, "callsites": { - "id": 301, - "package_id": 221, + "id": 297, + "package_id": 217, }, "camelcase-css": { "id": 59, "package_id": 31, }, "caniuse-lite": { - "id": 869, - "package_id": 233, + "id": 865, + "package_id": 229, }, "chalk": { - "id": 342, - "package_id": 303, + "id": 338, + "package_id": 299, }, "chokidar": { "id": 21, "package_id": 50, }, "chromium-bidi": { - "id": 261, - "package_id": 198, + "id": 262, + "package_id": 193, }, "client-only": { - "id": 323, - "package_id": 236, + "id": 319, + "package_id": 232, }, "cliui": { - "id": 166, - "package_id": 124, + "id": 167, + "package_id": 125, }, "color-convert": { "id": 126, @@ -21076,19 +20948,15 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 99, }, "concat-map": { - "id": 384, - "package_id": 250, + "id": 380, + "package_id": 246, }, "cosmiconfig": { "id": 153, - "package_id": 201, - }, - "cross-fetch": { - "id": 262, - "package_id": 193, + "package_id": 197, }, "cross-spawn": { - "id": 359, + "id": 355, "package_id": 93, }, "cssesc": { @@ -21096,552 +20964,552 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 5, }, "csstype": { - "id": 885, - "package_id": 453, + "id": 881, + "package_id": 449, }, "damerau-levenshtein": { - "id": 742, - "package_id": 425, + "id": 738, + "package_id": 421, }, "data-uri-to-buffer": { - "id": 241, - "package_id": 175, + "id": 242, + "package_id": 176, }, "data-view-buffer": { - "id": 508, - "package_id": 376, + "id": 504, + "package_id": 372, }, "data-view-byte-length": { - "id": 509, - "package_id": 375, + "id": 505, + "package_id": 371, }, "data-view-byte-offset": { - "id": 510, - "package_id": 374, + "id": 506, + "package_id": 370, }, "debug": { - "id": 343, - "package_id": 153, + "id": 339, + "package_id": 154, }, "deep-is": { - "id": 422, - "package_id": 292, + "id": 418, + "package_id": 288, }, "define-data-property": { - "id": 476, - "package_id": 324, + "id": 472, + "package_id": 320, }, "define-properties": { - "id": 698, - "package_id": 317, + "id": 694, + "package_id": 313, }, "degenerator": { - "id": 226, - "package_id": 160, + "id": 227, + "package_id": 161, }, "dequal": { - "id": 808, - "package_id": 427, + "id": 804, + "package_id": 423, }, "devtools-protocol": { - "id": 264, - "package_id": 192, + "id": 156, + "package_id": 114, }, "didyoumean": { "id": 24, "package_id": 38, }, "dir-glob": { - "id": 726, - "package_id": 402, + "id": 722, + "package_id": 398, }, "dlv": { "id": 15, "package_id": 106, }, "doctrine": { - "id": 352, - "package_id": 295, + "id": 348, + "package_id": 291, }, "eastasianwidth": { "id": 132, "package_id": 89, }, "electron-to-chromium": { - "id": 876, - "package_id": 450, + "id": 872, + "package_id": 446, }, "emoji-regex": { - "id": 743, + "id": 739, "package_id": 88, }, "end-of-stream": { - "id": 197, - "package_id": 145, + "id": 198, + "package_id": 146, }, "enhanced-resolve": { - "id": 441, - "package_id": 391, + "id": 437, + "package_id": 387, }, "env-paths": { - "id": 276, - "package_id": 222, + "id": 272, + "package_id": 218, }, "error-ex": { - "id": 282, - "package_id": 204, + "id": 278, + "package_id": 200, }, "es-abstract": { - "id": 699, - "package_id": 329, + "id": 695, + "package_id": 325, }, "es-define-property": { - "id": 490, - "package_id": 320, + "id": 486, + "package_id": 316, }, "es-errors": { - "id": 862, - "package_id": 316, + "id": 858, + "package_id": 312, }, "es-iterator-helpers": { - "id": 816, - "package_id": 413, + "id": 812, + "package_id": 409, }, "es-object-atoms": { - "id": 700, - "package_id": 315, + "id": 696, + "package_id": 311, }, "es-set-tostringtag": { - "id": 764, - "package_id": 373, + "id": 760, + "package_id": 369, }, "es-shim-unscopables": { - "id": 864, - "package_id": 385, + "id": 860, + "package_id": 381, }, "es-to-primitive": { - "id": 515, - "package_id": 371, + "id": 511, + "package_id": 367, }, "escalade": { - "id": 879, - "package_id": 123, + "id": 875, + "package_id": 124, }, "escape-string-regexp": { - "id": 371, - "package_id": 253, + "id": 367, + "package_id": 249, }, "escodegen": { - "id": 229, - "package_id": 162, + "id": 230, + "package_id": 163, }, "eslint": { "id": 5, - "package_id": 242, + "package_id": 238, }, "eslint-config-next": { "id": 6, - "package_id": 241, + "package_id": 237, }, "eslint-import-resolver-node": { - "id": 336, - "package_id": 382, + "id": 332, + "package_id": 378, }, "eslint-import-resolver-typescript": { - "id": 337, - "package_id": 306, + "id": 333, + "package_id": 302, }, "eslint-module-utils": { - "id": 456, - "package_id": 380, + "id": 452, + "package_id": 376, }, "eslint-plugin-import": { - "id": 330, - "package_id": 307, + "id": 326, + "package_id": 303, }, "eslint-plugin-jsx-a11y": { - "id": 331, - "package_id": 408, + "id": 327, + "package_id": 404, }, "eslint-plugin-react": { - "id": 329, - "package_id": 433, + "id": 325, + "package_id": 429, }, "eslint-plugin-react-hooks": { - "id": 335, - "package_id": 393, + "id": 331, + "package_id": 389, }, "eslint-scope": { - "id": 362, - "package_id": 282, + "id": 358, + "package_id": 278, }, "eslint-visitor-keys": { - "id": 370, - "package_id": 246, + "id": 366, + "package_id": 242, }, "espree": { - "id": 344, - "package_id": 270, + "id": 340, + "package_id": 266, }, "esprima": { - "id": 230, - "package_id": 161, + "id": 231, + "package_id": 162, }, "esquery": { - "id": 346, - "package_id": 302, + "id": 342, + "package_id": 298, }, "esrecurse": { - "id": 418, - "package_id": 283, + "id": 414, + "package_id": 279, }, "estraverse": { - "id": 436, - "package_id": 165, + "id": 432, + "package_id": 166, }, "esutils": { - "id": 347, - "package_id": 164, + "id": 343, + "package_id": 165, }, "extract-zip": { - "id": 157, - "package_id": 180, + "id": 158, + "package_id": 181, }, "fast-deep-equal": { - "id": 365, - "package_id": 278, + "id": 361, + "package_id": 274, }, "fast-fifo": { - "id": 195, - "package_id": 140, + "id": 196, + "package_id": 141, }, "fast-glob": { "id": 22, "package_id": 40, }, "fast-json-stable-stringify": { - "id": 414, - "package_id": 277, + "id": 410, + "package_id": 273, }, "fast-levenshtein": { - "id": 426, - "package_id": 287, + "id": 422, + "package_id": 283, }, "fastq": { "id": 73, "package_id": 44, }, "fd-slicer": { - "id": 255, - "package_id": 186, + "id": 256, + "package_id": 187, }, "file-entry-cache": { - "id": 369, - "package_id": 254, + "id": 365, + "package_id": 250, }, "fill-range": { "id": 63, "package_id": 35, }, "find-up": { - "id": 348, - "package_id": 296, + "id": 344, + "package_id": 292, }, "flat-cache": { - "id": 385, - "package_id": 255, + "id": 381, + "package_id": 251, }, "flatted": { - "id": 386, - "package_id": 264, + "id": 382, + "package_id": 260, }, "for-each": { - "id": 587, - "package_id": 332, + "id": 583, + "package_id": 328, }, "foreground-child": { "id": 102, "package_id": 91, }, "fraction.js": { - "id": 870, - "package_id": 446, + "id": 866, + "package_id": 442, }, "fs-extra": { - "id": 243, - "package_id": 171, + "id": 244, + "package_id": 172, }, "fs.realpath": { - "id": 390, - "package_id": 261, + "id": 386, + "package_id": 257, }, "fsevents": { "id": 85, "package_id": 51, }, "function-bind": { - "id": 765, + "id": 761, "package_id": 21, }, "function.prototype.name": { - "id": 516, - "package_id": 370, + "id": 512, + "package_id": 366, }, "functions-have-names": { - "id": 619, - "package_id": 358, + "id": 615, + "package_id": 354, }, "get-caller-file": { - "id": 168, - "package_id": 122, + "id": 169, + "package_id": 123, }, "get-intrinsic": { - "id": 701, - "package_id": 321, + "id": 697, + "package_id": 317, }, "get-stream": { - "id": 250, - "package_id": 188, + "id": 251, + "package_id": 189, }, "get-symbol-description": { - "id": 518, - "package_id": 369, + "id": 514, + "package_id": 365, }, "get-tsconfig": { - "id": 444, - "package_id": 389, + "id": 440, + "package_id": 385, }, "get-uri": { - "id": 221, - "package_id": 170, + "id": 222, + "package_id": 171, }, "glob": { - "id": 734, + "id": 730, "package_id": 65, }, "glob-parent": { - "id": 360, + "id": 356, "package_id": 27, }, "globals": { - "id": 349, - "package_id": 268, + "id": 345, + "package_id": 264, }, "globalthis": { - "id": 767, - "package_id": 368, + "id": 763, + "package_id": 364, }, "globby": { - "id": 714, - "package_id": 400, + "id": 710, + "package_id": 396, }, "gopd": { - "id": 835, - "package_id": 325, + "id": 831, + "package_id": 321, }, "graceful-fs": { - "id": 306, - "package_id": 174, + "id": 302, + "package_id": 175, }, "graphemer": { - "id": 353, - "package_id": 294, + "id": 349, + "package_id": 290, }, "has-bigints": { - "id": 559, - "package_id": 343, + "id": 555, + "package_id": 339, }, "has-flag": { - "id": 439, - "package_id": 305, + "id": 435, + "package_id": 301, }, "has-property-descriptors": { - "id": 768, - "package_id": 319, + "id": 764, + "package_id": 315, }, "has-proto": { - "id": 769, - "package_id": 323, + "id": 765, + "package_id": 319, }, "has-symbols": { - "id": 770, - "package_id": 322, + "id": 766, + "package_id": 318, }, "has-tostringtag": { - "id": 568, - "package_id": 331, + "id": 564, + "package_id": 327, }, "hasown": { - "id": 457, + "id": 453, "package_id": 20, }, "http-proxy-agent": { - "id": 203, - "package_id": 169, + "id": 204, + "package_id": 170, }, "https-proxy-agent": { - "id": 204, - "package_id": 168, + "id": 205, + "package_id": 169, }, "ieee754": { - "id": 179, - "package_id": 128, + "id": 180, + "package_id": 129, }, "ignore": { - "id": 345, - "package_id": 267, + "id": 341, + "package_id": 263, }, "import-fresh": { - "id": 404, - "package_id": 218, + "id": 400, + "package_id": 214, }, "imurmurhash": { - "id": 361, - "package_id": 284, + "id": 357, + "package_id": 280, }, "inflight": { - "id": 391, - "package_id": 260, + "id": 387, + "package_id": 256, }, "inherits": { - "id": 392, - "package_id": 259, + "id": 388, + "package_id": 255, }, "internal-slot": { - "id": 771, - "package_id": 366, + "id": 767, + "package_id": 362, }, "ip-address": { - "id": 212, - "package_id": 150, + "id": 213, + "package_id": 151, }, "is-array-buffer": { - "id": 526, - "package_id": 365, + "id": 522, + "package_id": 361, }, "is-arrayish": { - "id": 285, - "package_id": 205, + "id": 281, + "package_id": 201, }, "is-async-function": { - "id": 788, - "package_id": 424, + "id": 784, + "package_id": 420, }, "is-bigint": { - "id": 562, - "package_id": 342, + "id": 558, + "package_id": 338, }, "is-binary-path": { "id": 81, "package_id": 53, }, "is-boolean-object": { - "id": 563, - "package_id": 341, + "id": 559, + "package_id": 337, }, "is-callable": { - "id": 527, - "package_id": 333, + "id": 523, + "package_id": 329, }, "is-core-module": { - "id": 458, + "id": 454, "package_id": 19, }, "is-data-view": { - "id": 528, - "package_id": 364, + "id": 524, + "package_id": 360, }, "is-date-object": { - "id": 647, - "package_id": 372, + "id": 643, + "package_id": 368, }, "is-extglob": { "id": 58, "package_id": 29, }, "is-finalizationregistry": { - "id": 790, - "package_id": 423, + "id": 786, + "package_id": 419, }, "is-fullwidth-code-point": { "id": 124, "package_id": 79, }, "is-generator-function": { - "id": 791, - "package_id": 422, + "id": 787, + "package_id": 418, }, "is-glob": { - "id": 350, + "id": 346, "package_id": 28, }, "is-map": { - "id": 798, - "package_id": 421, + "id": 794, + "package_id": 417, }, "is-negative-zero": { - "id": 529, - "package_id": 363, + "id": 525, + "package_id": 359, }, "is-number": { "id": 65, "package_id": 37, }, "is-number-object": { - "id": 564, - "package_id": 340, + "id": 560, + "package_id": 336, }, "is-path-inside": { - "id": 364, - "package_id": 280, + "id": 360, + "package_id": 276, }, "is-regex": { - "id": 530, - "package_id": 353, + "id": 526, + "package_id": 349, }, "is-set": { - "id": 799, - "package_id": 420, + "id": 795, + "package_id": 416, }, "is-shared-array-buffer": { - "id": 531, - "package_id": 362, + "id": 527, + "package_id": 358, }, "is-string": { - "id": 702, - "package_id": 339, + "id": 698, + "package_id": 335, }, "is-symbol": { - "id": 648, - "package_id": 338, + "id": 644, + "package_id": 334, }, "is-typed-array": { - "id": 533, - "package_id": 345, + "id": 529, + "package_id": 341, }, "is-weakmap": { - "id": 800, - "package_id": 419, + "id": 796, + "package_id": 415, }, "is-weakref": { - "id": 534, - "package_id": 361, + "id": 530, + "package_id": 357, }, "is-weakset": { - "id": 801, - "package_id": 418, + "id": 797, + "package_id": 414, }, "isarray": { - "id": 612, - "package_id": 355, + "id": 608, + "package_id": 351, }, "isexe": { "id": 140, "package_id": 95, }, "iterator.prototype": { - "id": 772, - "package_id": 414, + "id": 768, + "package_id": 410, }, "jackspeak": { "id": 103, @@ -21656,56 +21524,56 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 111, }, "js-yaml": { - "id": 351, - "package_id": 216, + "id": 347, + "package_id": 212, }, "jsbn": { - "id": 214, - "package_id": 152, + "id": 215, + "package_id": 153, }, "json-buffer": { - "id": 398, - "package_id": 263, + "id": 394, + "package_id": 259, }, "json-parse-even-better-errors": { - "id": 283, - "package_id": 203, + "id": 279, + "package_id": 199, }, "json-schema-traverse": { - "id": 415, - "package_id": 276, + "id": 411, + "package_id": 272, }, "json-stable-stringify-without-jsonify": { - "id": 376, - "package_id": 243, + "id": 372, + "package_id": 239, }, "json5": { - "id": 468, - "package_id": 311, + "id": 464, + "package_id": 307, }, "jsonfile": { - "id": 245, - "package_id": 173, + "id": 246, + "package_id": 174, }, "jsx-ast-utils": { - "id": 818, - "package_id": 412, + "id": 814, + "package_id": 408, }, "keyv": { - "id": 387, - "package_id": 262, + "id": 383, + "package_id": 258, }, "language-subtag-registry": { - "id": 755, - "package_id": 411, + "id": 751, + "package_id": 407, }, "language-tags": { - "id": 747, - "package_id": 410, + "id": 743, + "package_id": 406, }, "levn": { - "id": 341, - "package_id": 288, + "id": 337, + "package_id": 284, }, "lilconfig": { "id": 23, @@ -21716,20 +21584,20 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 64, }, "locate-path": { - "id": 431, - "package_id": 298, + "id": 427, + "package_id": 294, }, "lodash.merge": { - "id": 363, - "package_id": 281, + "id": 359, + "package_id": 277, }, "loose-envify": { "id": 150, "package_id": 110, }, "lru-cache": { - "id": 205, - "package_id": 178, + "id": 206, + "package_id": 179, }, "merge2": { "id": 69, @@ -21740,24 +21608,24 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 32, }, "minimatch": { - "id": 354, - "package_id": 248, + "id": 350, + "package_id": 244, }, "minimist": { - "id": 469, - "package_id": 310, + "id": 465, + "package_id": 306, }, "minipass": { "id": 105, "package_id": 67, }, "mitt": { - "id": 273, - "package_id": 200, + "id": 268, + "package_id": 196, }, "ms": { - "id": 216, - "package_id": 154, + "id": 217, + "package_id": 155, }, "mz": { "id": 94, @@ -21768,35 +21636,31 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 10, }, "natural-compare": { - "id": 366, - "package_id": 279, + "id": 362, + "package_id": 275, }, "netmask": { - "id": 227, - "package_id": 159, + "id": 228, + "package_id": 160, }, "next": { "id": 7, - "package_id": 223, - }, - "node-fetch": { - "id": 268, - "package_id": 194, + "package_id": 219, }, "node-releases": { - "id": 877, - "package_id": 449, + "id": 873, + "package_id": 445, }, "normalize-path": { "id": 30, "package_id": 25, }, "normalize-range": { - "id": 871, - "package_id": 445, + "id": 867, + "package_id": 441, }, "object-assign": { - "id": 845, + "id": 841, "package_id": 63, }, "object-hash": { @@ -21804,76 +21668,76 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 26, }, "object-inspect": { - "id": 535, - "package_id": 360, + "id": 531, + "package_id": 356, }, "object-keys": { - "id": 478, - "package_id": 318, + "id": 474, + "package_id": 314, }, "object.assign": { - "id": 758, - "package_id": 359, + "id": 754, + "package_id": 355, }, "object.entries": { - "id": 820, - "package_id": 409, + "id": 816, + "package_id": 405, }, "object.fromentries": { - "id": 821, - "package_id": 379, + "id": 817, + "package_id": 375, }, "object.groupby": { - "id": 462, - "package_id": 328, + "id": 458, + "package_id": 324, }, "object.hasown": { - "id": 822, - "package_id": 438, + "id": 818, + "package_id": 434, }, "object.values": { - "id": 823, - "package_id": 314, + "id": 819, + "package_id": 310, }, "once": { - "id": 198, - "package_id": 143, + "id": 199, + "package_id": 144, }, "optionator": { - "id": 356, - "package_id": 286, + "id": 352, + "package_id": 282, }, "p-limit": { - "id": 434, - "package_id": 300, + "id": 430, + "package_id": 296, }, "p-locate": { - "id": 433, - "package_id": 299, + "id": 429, + "package_id": 295, }, "pac-proxy-agent": { - "id": 206, - "package_id": 157, + "id": 207, + "package_id": 158, }, "pac-resolver": { - "id": 224, - "package_id": 158, + "id": 225, + "package_id": 159, }, "parent-module": { - "id": 299, - "package_id": 220, + "id": 295, + "package_id": 216, }, "parse-json": { - "id": 279, - "package_id": 202, + "id": 275, + "package_id": 198, }, "path-exists": { - "id": 432, - "package_id": 297, + "id": 428, + "package_id": 293, }, "path-is-absolute": { - "id": 395, - "package_id": 258, + "id": 391, + "package_id": 254, }, "path-key": { "id": 137, @@ -21888,15 +21752,15 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 66, }, "path-type": { - "id": 731, - "package_id": 403, + "id": 727, + "package_id": 399, }, "pend": { - "id": 257, - "package_id": 187, + "id": 258, + "package_id": 188, }, "picocolors": { - "id": 872, + "id": 868, "package_id": 9, }, "picomatch": { @@ -21912,8 +21776,8 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 58, }, "possible-typed-array-names": { - "id": 557, - "package_id": 335, + "id": 553, + "package_id": 331, }, "postcss": { "id": 8, @@ -21940,36 +21804,36 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 3, }, "postcss-value-parser": { - "id": 873, + "id": 869, "package_id": 24, }, "prelude-ls": { - "id": 427, - "package_id": 290, + "id": 423, + "package_id": 286, }, "progress": { - "id": 158, - "package_id": 179, + "id": 159, + "package_id": 180, }, "prop-types": { - "id": 824, - "package_id": 436, + "id": 820, + "package_id": 432, }, "proxy-agent": { - "id": 159, - "package_id": 146, + "id": 160, + "package_id": 147, }, "proxy-from-env": { - "id": 207, - "package_id": 156, + "id": 208, + "package_id": 157, }, "pump": { - "id": 180, - "package_id": 142, + "id": 181, + "package_id": 143, }, "punycode": { - "id": 417, - "package_id": 275, + "id": 413, + "package_id": 271, }, "puppeteer": { "id": 9, @@ -21977,15 +21841,15 @@ exports[`ssr works for 100-ish requests 1`] = ` }, "puppeteer-core": { "id": 154, - "package_id": 190, + "package_id": 191, }, "queue-microtask": { "id": 77, "package_id": 48, }, "queue-tick": { - "id": 190, - "package_id": 139, + "id": 191, + "package_id": 140, }, "react": { "id": 10, @@ -21996,8 +21860,8 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 108, }, "react-is": { - "id": 846, - "package_id": 437, + "id": 842, + "package_id": 433, }, "read-cache": { "id": 48, @@ -22008,68 +21872,68 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 52, }, "reflect.getprototypeof": { - "id": 777, - "package_id": 415, + "id": 773, + "package_id": 411, }, "regenerator-runtime": { - "id": 809, - "package_id": 432, + "id": 805, + "package_id": 428, }, "regexp.prototype.flags": { - "id": 838, - "package_id": 356, + "id": 834, + "package_id": 352, }, "require-directory": { - "id": 169, - "package_id": 121, + "id": 170, + "package_id": 122, }, "resolve": { "id": 19, "package_id": 16, }, "resolve-from": { - "id": 300, - "package_id": 219, + "id": 296, + "package_id": 215, }, "resolve-pkg-maps": { - "id": 703, - "package_id": 390, + "id": 699, + "package_id": 386, }, "reusify": { "id": 74, "package_id": 45, }, "rimraf": { - "id": 388, - "package_id": 256, + "id": 384, + "package_id": 252, }, "run-parallel": { "id": 76, "package_id": 47, }, "safe-array-concat": { - "id": 773, - "package_id": 354, + "id": 769, + "package_id": 350, }, "safe-regex-test": { - "id": 540, - "package_id": 352, + "id": 536, + "package_id": 348, }, "scheduler": { "id": 147, "package_id": 112, }, "semver": { - "id": 826, - "package_id": 313, + "id": 822, + "package_id": 309, }, "set-function-length": { - "id": 494, - "package_id": 327, + "id": 490, + "package_id": 323, }, "set-function-name": { - "id": 839, - "package_id": 357, + "id": 835, + "package_id": 353, }, "shebang-command": { "id": 138, @@ -22080,51 +21944,51 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 97, }, "side-channel": { - "id": 840, - "package_id": 367, + "id": 836, + "package_id": 363, }, "signal-exit": { "id": 136, "package_id": 92, }, "slash": { - "id": 730, - "package_id": 401, + "id": 726, + "package_id": 397, }, "smart-buffer": { - "id": 213, - "package_id": 149, + "id": 214, + "package_id": 150, }, "socks": { - "id": 211, - "package_id": 148, + "id": 212, + "package_id": 149, }, "socks-proxy-agent": { - "id": 208, - "package_id": 147, + "id": 209, + "package_id": 148, }, "source-map": { - "id": 234, - "package_id": 163, + "id": 235, + "package_id": 164, }, "source-map-js": { "id": 44, "package_id": 8, }, "sprintf-js": { - "id": 215, - "package_id": 151, + "id": 216, + "package_id": 152, }, "streamsearch": { - "id": 328, - "package_id": 240, + "id": 324, + "package_id": 236, }, "streamx": { - "id": 196, - "package_id": 135, + "id": 197, + "package_id": 136, }, "string-width": { - "id": 170, + "id": 171, "package_id": 78, }, "string-width-cjs": { @@ -22132,23 +21996,23 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 78, }, "string.prototype.matchall": { - "id": 827, - "package_id": 434, + "id": 823, + "package_id": 430, }, "string.prototype.trim": { - "id": 541, - "package_id": 351, + "id": 537, + "package_id": 347, }, "string.prototype.trimend": { - "id": 542, - "package_id": 350, + "id": 538, + "package_id": 346, }, "string.prototype.trimstart": { - "id": 543, - "package_id": 349, + "id": 539, + "package_id": 345, }, "strip-ansi": { - "id": 357, + "id": 353, "package_id": 76, }, "strip-ansi-cjs": { @@ -22156,24 +22020,24 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 76, }, "strip-bom": { - "id": 470, - "package_id": 309, + "id": 466, + "package_id": 305, }, "strip-json-comments": { - "id": 407, - "package_id": 266, + "id": 403, + "package_id": 262, }, "styled-jsx": { - "id": 305, - "package_id": 235, + "id": 301, + "package_id": 231, }, "sucrase": { "id": 20, "package_id": 56, }, "supports-color": { - "id": 438, - "package_id": 304, + "id": 434, + "package_id": 300, }, "supports-preserve-symlinks-flag": { "id": 53, @@ -22184,24 +22048,24 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 2, }, "tapable": { - "id": 705, - "package_id": 392, + "id": 701, + "package_id": 388, }, "tar-fs": { - "id": 160, - "package_id": 130, + "id": 161, + "package_id": 131, }, "tar-stream": { - "id": 181, - "package_id": 141, + "id": 182, + "package_id": 142, }, "text-decoder": { - "id": 191, - "package_id": 137, + "id": 192, + "package_id": 138, }, "text-table": { - "id": 358, - "package_id": 285, + "id": 354, + "package_id": 281, }, "thenify": { "id": 100, @@ -22212,127 +22076,115 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 60, }, "through": { - "id": 177, - "package_id": 126, + "id": 178, + "package_id": 127, }, "to-regex-range": { "id": 64, "package_id": 36, }, - "tr46": { - "id": 271, - "package_id": 197, - }, "ts-api-utils": { - "id": 718, - "package_id": 398, + "id": 714, + "package_id": 394, }, "ts-interface-checker": { "id": 96, "package_id": 57, }, "tsconfig-paths": { - "id": 465, - "package_id": 308, + "id": 461, + "package_id": 304, }, "tslib": { - "id": 322, - "package_id": 167, + "id": 318, + "package_id": 168, }, "type-check": { - "id": 428, - "package_id": 289, + "id": 424, + "package_id": 285, }, "type-fest": { - "id": 408, - "package_id": 269, + "id": 404, + "package_id": 265, }, "typed-array-buffer": { - "id": 544, - "package_id": 348, + "id": 540, + "package_id": 344, }, "typed-array-byte-length": { - "id": 545, - "package_id": 347, + "id": 541, + "package_id": 343, }, "typed-array-byte-offset": { - "id": 546, - "package_id": 346, + "id": 542, + "package_id": 342, }, "typed-array-length": { - "id": 547, - "package_id": 344, + "id": 543, + "package_id": 340, }, "typescript": { "id": 13, "package_id": 1, }, "unbox-primitive": { - "id": 548, - "package_id": 336, + "id": 544, + "package_id": 332, }, "unbzip2-stream": { - "id": 161, - "package_id": 125, + "id": 162, + "package_id": 126, }, "undici-types": { - "id": 254, - "package_id": 183, + "id": 255, + "package_id": 184, }, "universalify": { - "id": 246, - "package_id": 172, + "id": 247, + "package_id": 173, }, "update-browserslist-db": { - "id": 878, - "package_id": 448, + "id": 874, + "package_id": 444, }, "uri-js": { - "id": 416, - "package_id": 274, + "id": 412, + "package_id": 270, }, "urlpattern-polyfill": { - "id": 274, - "package_id": 199, + "id": 269, + "package_id": 195, }, "util-deprecate": { "id": 37, "package_id": 4, }, - "webidl-conversions": { - "id": 272, - "package_id": 196, - }, - "whatwg-url": { - "id": 269, - "package_id": 195, - }, "which": { "id": 139, "package_id": 94, }, "which-boxed-primitive": { - "id": 561, - "package_id": 337, + "id": 557, + "package_id": 333, }, "which-builtin-type": { - "id": 785, - "package_id": 416, + "id": 781, + "package_id": 412, }, "which-collection": { - "id": 796, - "package_id": 417, + "id": 792, + "package_id": 413, }, "which-typed-array": { - "id": 549, - "package_id": 330, + "id": 545, + "package_id": 326, }, "word-wrap": { - "id": 423, - "package_id": 291, + "id": 419, + "package_id": 287, }, "wrap-ansi": { - "id": 175, + "id": 176, "package_id": 75, }, "wrap-ansi-cjs": { @@ -22340,40 +22192,44 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 75, }, "wrappy": { - "id": 199, - "package_id": 144, + "id": 200, + "package_id": 145, }, "ws": { "id": 265, - "package_id": 191, + "package_id": 192, }, "y18n": { - "id": 171, - "package_id": 120, + "id": 172, + "package_id": 121, }, "yallist": { - "id": 165, - "package_id": 117, + "id": 166, + "package_id": 118, }, "yaml": { "id": 38, "package_id": 12, }, "yargs": { - "id": 162, - "package_id": 118, + "id": 163, + "package_id": 119, }, "yargs-parser": { - "id": 172, - "package_id": 119, + "id": 173, + "package_id": 120, }, "yauzl": { - "id": 251, - "package_id": 184, + "id": 252, + "package_id": 185, }, "yocto-queue": { - "id": 435, - "package_id": 301, + "id": 431, + "package_id": 297, + }, + "zod": { + "id": 270, + "package_id": 194, }, }, "depth": 0, @@ -22383,8 +22239,8 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "@types/node": { - "id": 866, - "package_id": 182, + "id": 862, + "package_id": 183, }, }, "depth": 1, @@ -22394,8 +22250,8 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "postcss": { - "id": 303, - "package_id": 238, + "id": 299, + "package_id": 234, }, }, "depth": 1, @@ -22405,8 +22261,8 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "@types/node": { - "id": 867, - "package_id": 182, + "id": 863, + "package_id": 183, }, }, "depth": 1, @@ -22416,12 +22272,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "doctrine": { - "id": 815, - "package_id": 383, + "id": 811, + "package_id": 379, }, "resolve": { - "id": 825, - "package_id": 435, + "id": 821, + "package_id": 431, }, }, "depth": 1, @@ -22431,12 +22287,12 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "debug": { - "id": 453, - "package_id": 381, + "id": 449, + "package_id": 377, }, "doctrine": { - "id": 454, - "package_id": 383, + "id": 450, + "package_id": 379, }, }, "depth": 1, @@ -22446,8 +22302,8 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "debug": { - "id": 678, - "package_id": 381, + "id": 674, + "package_id": 377, }, }, "depth": 1, @@ -22457,27 +22313,16 @@ exports[`ssr works for 100-ish requests 1`] = ` { "dependencies": { "debug": { - "id": 263, - "package_id": 189, - }, - }, - "depth": 1, - "id": 7, - "path": "node_modules/puppeteer-core/node_modules", - }, - { - "dependencies": { - "debug": { - "id": 156, - "package_id": 189, + "id": 157, + "package_id": 190, }, "semver": { - "id": 163, - "package_id": 115, + "id": 164, + "package_id": 116, }, }, "depth": 1, - "id": 8, + "id": 7, "path": "node_modules/@puppeteer/browsers/node_modules", }, { @@ -22488,7 +22333,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 9, + "id": 8, "path": "node_modules/chokidar/node_modules", }, { @@ -22499,7 +22344,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 10, + "id": 9, "path": "node_modules/fast-glob/node_modules", }, { @@ -22510,18 +22355,18 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 11, + "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, { "dependencies": { "debug": { - "id": 676, - "package_id": 381, + "id": 672, + "package_id": 377, }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/eslint-module-utils/node_modules", }, { @@ -22532,44 +22377,44 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/glob/node_modules", }, { "dependencies": { "minimatch": { - "id": 717, - "package_id": 399, + "id": 713, + "package_id": 395, }, "semver": { - "id": 715, - "package_id": 115, + "id": 711, + "package_id": 116, }, }, "depth": 1, - "id": 14, + "id": 13, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 15, + "id": 14, "path": "node_modules/@puppeteer/browsers/node_modules/semver/node_modules", }, { "dependencies": { "glob": { - "id": 389, - "package_id": 257, + "id": 385, + "package_id": 253, }, }, "depth": 1, - "id": 16, + "id": 15, "path": "node_modules/rimraf/node_modules", }, { @@ -22580,7 +22425,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 2, - "id": 17, + "id": 16, "path": "node_modules/glob/node_modules/minimatch/node_modules", }, { @@ -22591,40 +22436,40 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 18, + "id": 17, "path": "node_modules/path-scurry/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", }, { "dependencies": { "brace-expansion": { - "id": 724, + "id": 720, "package_id": 70, }, }, "depth": 2, - "id": 20, + "id": 19, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, { "dependencies": { "@types/node": { - "id": 253, - "package_id": 182, + "id": 254, + "package_id": 183, }, }, "depth": 1, - "id": 21, + "id": 20, "path": "node_modules/@types/yauzl/node_modules", }, { @@ -22635,7 +22480,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 22, + "id": 21, "path": "node_modules/string-width/node_modules", }, { @@ -22654,18 +22499,18 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 23, + "id": 22, "path": "node_modules/@isaacs/cliui/node_modules", }, { "dependencies": { "chalk": { - "id": 289, - "package_id": 208, + "id": 285, + "package_id": 204, }, }, "depth": 1, - "id": 24, + "id": 23, "path": "node_modules/@babel/highlight/node_modules", }, { @@ -22676,7 +22521,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 25, + "id": 24, "path": "node_modules/string-width-cjs/node_modules", }, { @@ -22687,7 +22532,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 2, - "id": 26, + "id": 25, "path": "node_modules/@isaacs/cliui/node_modules/strip-ansi/node_modules", }, { @@ -22698,59 +22543,59 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 2, - "id": 27, + "id": 26, "path": "node_modules/@isaacs/cliui/node_modules/wrap-ansi/node_modules", }, { "dependencies": { "ansi-styles": { - "id": 292, - "package_id": 212, + "id": 288, + "package_id": 208, }, "escape-string-regexp": { - "id": 293, - "package_id": 211, + "id": 289, + "package_id": 207, }, "supports-color": { - "id": 294, - "package_id": 209, + "id": 290, + "package_id": 205, }, }, "depth": 2, - "id": 28, + "id": 27, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules", }, { "dependencies": { "color-convert": { - "id": 296, - "package_id": 213, + "id": 292, + "package_id": 209, }, }, "depth": 3, - "id": 29, + "id": 28, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules", }, { "dependencies": { "has-flag": { - "id": 295, - "package_id": 210, + "id": 291, + "package_id": 206, }, }, "depth": 3, - "id": 30, + "id": 29, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/supports-color/node_modules", }, { "dependencies": { "color-name": { - "id": 297, - "package_id": 214, + "id": 293, + "package_id": 210, }, }, "depth": 4, - "id": 31, + "id": 30, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/node_modules", }, ], diff --git a/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap b/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap index 691918922236a..7b40a27d78c94 100644 --- a/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap +++ b/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap @@ -14,7 +14,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "@types/node", "version": "==20.7.0", }, - "package_id": 456, + "package_id": 452, }, { "behavior": { @@ -27,7 +27,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "@types/react", "version": "==18.2.22", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { @@ -40,7 +40,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "@types/react-dom", "version": "==18.2.7", }, - "package_id": 451, + "package_id": 447, }, { "behavior": { @@ -53,7 +53,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "autoprefixer", "version": "==10.4.16", }, - "package_id": 444, + "package_id": 440, }, { "behavior": { @@ -66,7 +66,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "bun-types", "version": ">=1.0.3 <2.0.0", }, - "package_id": 442, + "package_id": 438, }, { "behavior": { @@ -79,7 +79,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "eslint", "version": "==8.50.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { @@ -92,7 +92,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "eslint-config-next", "version": "==14.1.3", }, - "package_id": 241, + "package_id": 237, }, { "behavior": { @@ -105,7 +105,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "next", "version": "==14.1.3", }, - "package_id": 223, + "package_id": 219, }, { "behavior": { @@ -125,11 +125,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "normal": true, }, "id": 9, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer", "npm": { "name": "puppeteer", - "version": "==22.4.1", + "version": "==22.12.0", }, "package_id": 113, }, @@ -2008,221 +2008,234 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "cosmiconfig", "version": "==9.0.0", }, - "package_id": 201, + "package_id": 197, }, { "behavior": { "normal": true, }, "id": 154, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer-core", "npm": { "name": "puppeteer-core", - "version": "==22.4.1", + "version": "==22.12.0", }, - "package_id": 190, + "package_id": 191, }, { "behavior": { "normal": true, }, "id": 155, - "literal": "2.1.0", + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, "id": 156, + "literal": "0.0.1299070", + "name": "devtools-protocol", + "npm": { + "name": "devtools-protocol", + "version": "==0.0.1299070", + }, + "package_id": 114, + }, + { + "behavior": { + "normal": true, + }, + "id": 157, "literal": "4.3.4", "name": "debug", "npm": { "name": "debug", "version": "==4.3.4", }, - "package_id": 189, + "package_id": 190, }, { "behavior": { "normal": true, }, - "id": 157, + "id": 158, "literal": "2.0.1", "name": "extract-zip", "npm": { "name": "extract-zip", "version": "==2.0.1", }, - "package_id": 180, + "package_id": 181, }, { "behavior": { "normal": true, }, - "id": 158, + "id": 159, "literal": "2.0.3", "name": "progress", "npm": { "name": "progress", "version": "==2.0.3", }, - "package_id": 179, + "package_id": 180, }, { "behavior": { "normal": true, }, - "id": 159, + "id": 160, "literal": "6.4.0", "name": "proxy-agent", "npm": { "name": "proxy-agent", "version": "==6.4.0", }, - "package_id": 146, + "package_id": 147, }, { "behavior": { "normal": true, }, - "id": 160, + "id": 161, "literal": "3.0.5", "name": "tar-fs", "npm": { "name": "tar-fs", "version": "==3.0.5", }, - "package_id": 130, + "package_id": 131, }, { "behavior": { "normal": true, }, - "id": 161, + "id": 162, "literal": "1.4.3", "name": "unbzip2-stream", "npm": { "name": "unbzip2-stream", "version": "==1.4.3", }, - "package_id": 125, + "package_id": 126, }, { "behavior": { "normal": true, }, - "id": 162, + "id": 163, "literal": "17.7.2", "name": "yargs", "npm": { "name": "yargs", "version": "==17.7.2", }, - "package_id": 118, + "package_id": 119, }, { "behavior": { "normal": true, }, - "id": 163, + "id": 164, "literal": "7.6.0", "name": "semver", "npm": { "name": "semver", "version": "==7.6.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 164, + "id": 165, "literal": "^6.0.0", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=6.0.0 <7.0.0", }, - "package_id": 116, + "package_id": 117, }, { "behavior": { "normal": true, }, - "id": 165, + "id": 166, "literal": "^4.0.0", "name": "yallist", "npm": { "name": "yallist", "version": ">=4.0.0 <5.0.0", }, - "package_id": 117, + "package_id": 118, }, { "behavior": { "normal": true, }, - "id": 166, + "id": 167, "literal": "^8.0.1", "name": "cliui", "npm": { "name": "cliui", "version": ">=8.0.1 <9.0.0", }, - "package_id": 124, + "package_id": 125, }, { "behavior": { "normal": true, }, - "id": 167, + "id": 168, "literal": "^3.1.1", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.1 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 168, + "id": 169, "literal": "^2.0.5", "name": "get-caller-file", "npm": { "name": "get-caller-file", "version": ">=2.0.5 <3.0.0", }, - "package_id": 122, + "package_id": 123, }, { "behavior": { "normal": true, }, - "id": 169, + "id": 170, "literal": "^2.1.1", "name": "require-directory", "npm": { "name": "require-directory", "version": ">=2.1.1 <3.0.0", }, - "package_id": 121, + "package_id": 122, }, { "behavior": { "normal": true, }, - "id": 170, + "id": 171, "literal": "^4.2.3", "name": "string-width", "npm": { @@ -2235,33 +2248,33 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 171, + "id": 172, "literal": "^5.0.5", "name": "y18n", "npm": { "name": "y18n", "version": ">=5.0.5 <6.0.0", }, - "package_id": 120, + "package_id": 121, }, { "behavior": { "normal": true, }, - "id": 172, + "id": 173, "literal": "^21.1.1", "name": "yargs-parser", "npm": { "name": "yargs-parser", "version": ">=21.1.1 <22.0.0", }, - "package_id": 119, + "package_id": 120, }, { "behavior": { "normal": true, }, - "id": 173, + "id": 174, "literal": "^4.2.0", "name": "string-width", "npm": { @@ -2274,7 +2287,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 174, + "id": 175, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -2287,7 +2300,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 175, + "id": 176, "literal": "^7.0.0", "name": "wrap-ansi", "npm": { @@ -2300,1130 +2313,1117 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 176, + "id": 177, "literal": "^5.2.1", "name": "buffer", "npm": { "name": "buffer", "version": ">=5.2.1 <6.0.0", }, - "package_id": 127, + "package_id": 128, }, { "behavior": { "normal": true, }, - "id": 177, + "id": 178, "literal": "^2.3.8", "name": "through", "npm": { "name": "through", "version": ">=2.3.8 <3.0.0", }, - "package_id": 126, + "package_id": 127, }, { "behavior": { "normal": true, }, - "id": 178, + "id": 179, "literal": "^1.3.1", "name": "base64-js", "npm": { "name": "base64-js", "version": ">=1.3.1 <2.0.0", }, - "package_id": 129, + "package_id": 130, }, { "behavior": { "normal": true, }, - "id": 179, + "id": 180, "literal": "^1.1.13", "name": "ieee754", "npm": { "name": "ieee754", "version": ">=1.1.13 <2.0.0", }, - "package_id": 128, + "package_id": 129, }, { "behavior": { "normal": true, }, - "id": 180, + "id": 181, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 181, + "id": 182, "literal": "^3.1.5", "name": "tar-stream", "npm": { "name": "tar-stream", "version": ">=3.1.5 <4.0.0", }, - "package_id": 141, + "package_id": 142, }, { "behavior": { "optional": true, }, - "id": 182, + "id": 183, "literal": "^2.1.1", "name": "bare-fs", "npm": { "name": "bare-fs", "version": ">=2.1.1 <3.0.0", }, - "package_id": 133, + "package_id": 134, }, { "behavior": { "optional": true, }, - "id": 183, + "id": 184, "literal": "^2.1.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.1.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 184, + "id": 185, "literal": "^2.1.0", "name": "bare-os", "npm": { "name": "bare-os", "version": ">=2.1.0 <3.0.0", }, - "package_id": 132, + "package_id": 133, }, { "behavior": { "normal": true, }, - "id": 185, + "id": 186, "literal": "^2.0.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.0.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 186, + "id": 187, "literal": "^2.0.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.0.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 187, + "id": 188, "literal": "^2.0.0", "name": "bare-stream", "npm": { "name": "bare-stream", "version": ">=2.0.0 <3.0.0", }, - "package_id": 134, + "package_id": 135, }, { "behavior": { "normal": true, }, - "id": 188, + "id": 189, "literal": "^2.18.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.18.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 189, + "id": 190, "literal": "^1.3.2", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.3.2 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 190, + "id": 191, "literal": "^1.0.1", "name": "queue-tick", "npm": { "name": "queue-tick", "version": ">=1.0.1 <2.0.0", }, - "package_id": 139, + "package_id": 140, }, { "behavior": { "normal": true, }, - "id": 191, + "id": 192, "literal": "^1.1.0", "name": "text-decoder", "npm": { "name": "text-decoder", "version": ">=1.1.0 <2.0.0", }, - "package_id": 137, + "package_id": 138, }, { "behavior": { "optional": true, }, - "id": 192, + "id": 193, "literal": "^2.2.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.2.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 193, + "id": 194, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 194, + "id": 195, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 195, + "id": 196, "literal": "^1.2.0", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.2.0 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 196, + "id": 197, "literal": "^2.15.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.15.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 197, + "id": 198, "literal": "^1.1.0", "name": "end-of-stream", "npm": { "name": "end-of-stream", "version": ">=1.1.0 <2.0.0", }, - "package_id": 145, + "package_id": 146, }, { "behavior": { "normal": true, }, - "id": 198, + "id": 199, "literal": "^1.3.1", "name": "once", "npm": { "name": "once", "version": ">=1.3.1 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 199, + "id": 200, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 200, + "id": 201, "literal": "^1.4.0", "name": "once", "npm": { "name": "once", "version": ">=1.4.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 201, + "id": 202, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 202, + "id": 203, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 203, + "id": 204, "literal": "^7.0.1", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 204, + "id": 205, "literal": "^7.0.3", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.3 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 205, + "id": 206, "literal": "^7.14.1", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=7.14.1 <8.0.0", }, - "package_id": 178, + "package_id": 179, }, { "behavior": { "normal": true, }, - "id": 206, + "id": 207, "literal": "^7.0.1", "name": "pac-proxy-agent", "npm": { "name": "pac-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 157, + "package_id": 158, }, { "behavior": { "normal": true, }, - "id": 207, + "id": 208, "literal": "^1.1.0", "name": "proxy-from-env", "npm": { "name": "proxy-from-env", "version": ">=1.1.0 <2.0.0", }, - "package_id": 156, + "package_id": 157, }, { "behavior": { "normal": true, }, - "id": 208, + "id": 209, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 209, + "id": 210, "literal": "^7.1.1", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.1 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 210, + "id": 211, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 211, + "id": 212, "literal": "^2.7.1", "name": "socks", "npm": { "name": "socks", "version": ">=2.7.1 <3.0.0", }, - "package_id": 148, + "package_id": 149, }, { "behavior": { "normal": true, }, - "id": 212, + "id": 213, "literal": "^9.0.5", "name": "ip-address", "npm": { "name": "ip-address", "version": ">=9.0.5 <10.0.0", }, - "package_id": 150, + "package_id": 151, }, { "behavior": { "normal": true, }, - "id": 213, + "id": 214, "literal": "^4.2.0", "name": "smart-buffer", "npm": { "name": "smart-buffer", "version": ">=4.2.0 <5.0.0", }, - "package_id": 149, + "package_id": 150, }, { "behavior": { "normal": true, }, - "id": 214, + "id": 215, "literal": "1.1.0", "name": "jsbn", "npm": { "name": "jsbn", "version": "==1.1.0", }, - "package_id": 152, + "package_id": 153, }, { "behavior": { "normal": true, }, - "id": 215, + "id": 216, "literal": "^1.1.3", "name": "sprintf-js", "npm": { "name": "sprintf-js", "version": ">=1.1.3 <2.0.0", }, - "package_id": 151, + "package_id": 152, }, { "behavior": { "normal": true, }, - "id": 216, + "id": 217, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 217, + "id": 218, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 218, + "id": 219, "literal": "^0.23.0", "name": "@tootallnate/quickjs-emscripten", "npm": { "name": "@tootallnate/quickjs-emscripten", "version": ">=0.23.0 <0.24.0", }, - "package_id": 177, + "package_id": 178, }, { "behavior": { "normal": true, }, - "id": 219, + "id": 220, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 220, + "id": 221, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 221, + "id": 222, "literal": "^6.0.1", "name": "get-uri", "npm": { "name": "get-uri", "version": ">=6.0.1 <7.0.0", }, - "package_id": 170, + "package_id": 171, }, { "behavior": { "normal": true, }, - "id": 222, + "id": 223, "literal": "^7.0.0", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.0 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 223, + "id": 224, "literal": "^7.0.2", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.2 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 224, + "id": 225, "literal": "^7.0.0", "name": "pac-resolver", "npm": { "name": "pac-resolver", "version": ">=7.0.0 <8.0.0", }, - "package_id": 158, + "package_id": 159, }, { "behavior": { "normal": true, }, - "id": 225, + "id": 226, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 226, + "id": 227, "literal": "^5.0.0", "name": "degenerator", "npm": { "name": "degenerator", "version": ">=5.0.0 <6.0.0", }, - "package_id": 160, + "package_id": 161, }, { "behavior": { "normal": true, }, - "id": 227, + "id": 228, "literal": "^2.0.2", "name": "netmask", "npm": { "name": "netmask", "version": ">=2.0.2 <3.0.0", }, - "package_id": 159, + "package_id": 160, }, { "behavior": { "normal": true, }, - "id": 228, + "id": 229, "literal": "^0.13.4", "name": "ast-types", "npm": { "name": "ast-types", "version": ">=0.13.4 <0.14.0", }, - "package_id": 166, + "package_id": 167, }, { "behavior": { "normal": true, }, - "id": 229, + "id": 230, "literal": "^2.1.0", "name": "escodegen", "npm": { "name": "escodegen", "version": ">=2.1.0 <3.0.0", }, - "package_id": 162, + "package_id": 163, }, { "behavior": { "normal": true, }, - "id": 230, + "id": 231, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "normal": true, }, - "id": 231, + "id": 232, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 232, + "id": 233, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 233, + "id": 234, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "optional": true, }, - "id": 234, + "id": 235, "literal": "~0.6.1", "name": "source-map", "npm": { "name": "source-map", "version": ">=0.6.1 <0.7.0", }, - "package_id": 163, + "package_id": 164, }, { "behavior": { "normal": true, }, - "id": 235, + "id": 236, "literal": "^2.0.1", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.0.1 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 236, + "id": 237, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 237, + "id": 238, "literal": "4", "name": "debug", "npm": { "name": "debug", "version": "<5.0.0 >=4.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 238, + "id": 239, "literal": "^7.1.0", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.0 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 239, + "id": 240, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 240, + "id": 241, "literal": "^5.0.2", "name": "basic-ftp", "npm": { "name": "basic-ftp", "version": ">=5.0.2 <6.0.0", }, - "package_id": 176, + "package_id": 177, }, { "behavior": { "normal": true, }, - "id": 241, + "id": 242, "literal": "^6.0.2", "name": "data-uri-to-buffer", "npm": { "name": "data-uri-to-buffer", "version": ">=6.0.2 <7.0.0", }, - "package_id": 175, + "package_id": 176, }, { "behavior": { "normal": true, }, - "id": 242, + "id": 243, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 243, + "id": 244, "literal": "^11.2.0", "name": "fs-extra", "npm": { "name": "fs-extra", "version": ">=11.2.0 <12.0.0", }, - "package_id": 171, + "package_id": 172, }, { "behavior": { "normal": true, }, - "id": 244, + "id": 245, "literal": "^4.2.0", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.0 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 245, + "id": 246, "literal": "^6.0.1", "name": "jsonfile", "npm": { "name": "jsonfile", "version": ">=6.0.1 <7.0.0", }, - "package_id": 173, + "package_id": 174, }, { "behavior": { "normal": true, }, - "id": 246, + "id": 247, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "normal": true, }, - "id": 247, + "id": 248, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "optional": true, }, - "id": 248, + "id": 249, "literal": "^4.1.6", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.1.6 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 249, + "id": 250, "literal": "^4.1.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.1.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 250, + "id": 251, "literal": "^5.1.0", "name": "get-stream", "npm": { "name": "get-stream", "version": ">=5.1.0 <6.0.0", }, - "package_id": 188, + "package_id": 189, }, { "behavior": { "normal": true, }, - "id": 251, + "id": 252, "literal": "^2.10.0", "name": "yauzl", "npm": { "name": "yauzl", "version": ">=2.10.0 <3.0.0", }, - "package_id": 184, + "package_id": 185, }, { "behavior": { "optional": true, }, - "id": 252, + "id": 253, "literal": "^2.9.1", "name": "@types/yauzl", "npm": { "name": "@types/yauzl", "version": ">=2.9.1 <3.0.0", }, - "package_id": 181, + "package_id": 182, }, { "behavior": { "normal": true, }, - "id": 253, + "id": 254, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 254, + "id": 255, "literal": "~5.26.4", "name": "undici-types", "npm": { "name": "undici-types", "version": ">=5.26.4 <5.27.0", }, - "package_id": 183, + "package_id": 184, }, { "behavior": { "normal": true, }, - "id": 255, + "id": 256, "literal": "~1.1.0", "name": "fd-slicer", "npm": { "name": "fd-slicer", "version": ">=1.1.0 <1.2.0", }, - "package_id": 186, + "package_id": 187, }, { "behavior": { "normal": true, }, - "id": 256, + "id": 257, "literal": "~0.2.3", "name": "buffer-crc32", "npm": { "name": "buffer-crc32", "version": ">=0.2.3 <0.3.0", }, - "package_id": 185, + "package_id": 186, }, { "behavior": { "normal": true, }, - "id": 257, + "id": 258, "literal": "~1.2.0", "name": "pend", "npm": { "name": "pend", "version": ">=1.2.0 <1.3.0", }, - "package_id": 187, + "package_id": 188, }, { "behavior": { "normal": true, }, - "id": 258, + "id": 259, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 259, + "id": 260, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 260, - "literal": "2.1.0", + "id": 261, + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, - "id": 261, - "literal": "0.5.12", + "id": 262, + "literal": "0.5.24", "name": "chromium-bidi", "npm": { "name": "chromium-bidi", - "version": "==0.5.12", - }, - "package_id": 198, - }, - { - "behavior": { - "normal": true, - }, - "id": 262, - "literal": "4.0.0", - "name": "cross-fetch", - "npm": { - "name": "cross-fetch", - "version": "==4.0.0", + "version": "==0.5.24", }, "package_id": 193, }, @@ -3432,39 +3432,39 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "normal": true, }, "id": 263, - "literal": "4.3.4", + "literal": "4.3.5", "name": "debug", "npm": { "name": "debug", - "version": "==4.3.4", + "version": "==4.3.5", }, - "package_id": 189, + "package_id": 154, }, { "behavior": { "normal": true, }, "id": 264, - "literal": "0.0.1249869", + "literal": "0.0.1299070", "name": "devtools-protocol", "npm": { "name": "devtools-protocol", - "version": "==0.0.1249869", + "version": "==0.0.1299070", }, - "package_id": 192, + "package_id": 114, }, { "behavior": { "normal": true, }, "id": 265, - "literal": "8.16.0", + "literal": "8.17.1", "name": "ws", "npm": { "name": "ws", - "version": "==8.16.0", + "version": "==8.17.1", }, - "package_id": 191, + "package_id": 192, }, { "behavior": { @@ -3499,164 +3499,111 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "normal": true, }, "id": 268, - "literal": "^2.6.12", - "name": "node-fetch", + "literal": "3.0.1", + "name": "mitt", "npm": { - "name": "node-fetch", - "version": ">=2.6.12 <3.0.0", + "name": "mitt", + "version": "==3.0.1", }, - "package_id": 194, + "package_id": 196, }, { "behavior": { "normal": true, }, "id": 269, - "literal": "^5.0.0", - "name": "whatwg-url", + "literal": "10.0.0", + "name": "urlpattern-polyfill", "npm": { - "name": "whatwg-url", - "version": ">=5.0.0 <6.0.0", + "name": "urlpattern-polyfill", + "version": "==10.0.0", }, "package_id": 195, }, { "behavior": { - "optional": true, - "peer": true, + "normal": true, }, "id": 270, - "literal": "^0.1.0", - "name": "encoding", + "literal": "3.23.8", + "name": "zod", "npm": { - "name": "encoding", - "version": ">=0.1.0 <0.2.0", + "name": "zod", + "version": "==3.23.8", }, - "package_id": null, + "package_id": 194, }, { "behavior": { - "normal": true, + "peer": true, }, "id": 271, - "literal": "~0.0.3", - "name": "tr46", + "literal": "*", + "name": "devtools-protocol", "npm": { - "name": "tr46", - "version": ">=0.0.3 <0.1.0", + "name": "devtools-protocol", + "version": ">=0.0.0", }, - "package_id": 197, + "package_id": 114, }, { "behavior": { "normal": true, }, "id": 272, - "literal": "^3.0.0", - "name": "webidl-conversions", + "literal": "^2.2.1", + "name": "env-paths", "npm": { - "name": "webidl-conversions", - "version": ">=3.0.0 <4.0.0", + "name": "env-paths", + "version": ">=2.2.1 <3.0.0", }, - "package_id": 196, + "package_id": 218, }, { "behavior": { "normal": true, }, "id": 273, - "literal": "3.0.1", - "name": "mitt", + "literal": "^3.3.0", + "name": "import-fresh", "npm": { - "name": "mitt", - "version": "==3.0.1", + "name": "import-fresh", + "version": ">=3.3.0 <4.0.0", }, - "package_id": 200, + "package_id": 214, }, { "behavior": { "normal": true, }, "id": 274, - "literal": "10.0.0", - "name": "urlpattern-polyfill", - "npm": { - "name": "urlpattern-polyfill", - "version": "==10.0.0", - }, - "package_id": 199, - }, - { - "behavior": { - "peer": true, - }, - "id": 275, - "literal": "*", - "name": "devtools-protocol", - "npm": { - "name": "devtools-protocol", - "version": ">=0.0.0", - }, - "package_id": 192, - }, - { - "behavior": { - "normal": true, - }, - "id": 276, - "literal": "^2.2.1", - "name": "env-paths", - "npm": { - "name": "env-paths", - "version": ">=2.2.1 <3.0.0", - }, - "package_id": 222, - }, - { - "behavior": { - "normal": true, - }, - "id": 277, - "literal": "^3.3.0", - "name": "import-fresh", - "npm": { - "name": "import-fresh", - "version": ">=3.3.0 <4.0.0", - }, - "package_id": 218, - }, - { - "behavior": { - "normal": true, - }, - "id": 278, - "literal": "^4.1.0", - "name": "js-yaml", + "literal": "^4.1.0", + "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 279, + "id": 275, "literal": "^5.2.0", "name": "parse-json", "npm": { "name": "parse-json", "version": ">=5.2.0 <6.0.0", }, - "package_id": 202, + "package_id": 198, }, { "behavior": { "optional": true, "peer": true, }, - "id": 280, + "id": 276, "literal": ">=4.9.5", "name": "typescript", "npm": { @@ -3669,46 +3616,46 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 281, + "id": 277, "literal": "^7.0.0", "name": "@babel/code-frame", "npm": { "name": "@babel/code-frame", "version": ">=7.0.0 <8.0.0", }, - "package_id": 206, + "package_id": 202, }, { "behavior": { "normal": true, }, - "id": 282, + "id": 278, "literal": "^1.3.1", "name": "error-ex", "npm": { "name": "error-ex", "version": ">=1.3.1 <2.0.0", }, - "package_id": 204, + "package_id": 200, }, { "behavior": { "normal": true, }, - "id": 283, + "id": 279, "literal": "^2.3.0", "name": "json-parse-even-better-errors", "npm": { "name": "json-parse-even-better-errors", "version": ">=2.3.0 <3.0.0", }, - "package_id": 203, + "package_id": 199, }, { "behavior": { "normal": true, }, - "id": 284, + "id": 280, "literal": "^1.1.6", "name": "lines-and-columns", "npm": { @@ -3721,33 +3668,33 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 285, + "id": 281, "literal": "^0.2.1", "name": "is-arrayish", "npm": { "name": "is-arrayish", "version": ">=0.2.1 <0.3.0", }, - "package_id": 205, + "package_id": 201, }, { "behavior": { "normal": true, }, - "id": 286, + "id": 282, "literal": "^7.24.7", "name": "@babel/highlight", "npm": { "name": "@babel/highlight", "version": ">=7.24.7 <8.0.0", }, - "package_id": 207, + "package_id": 203, }, { "behavior": { "normal": true, }, - "id": 287, + "id": 283, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -3760,33 +3707,33 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 288, + "id": 284, "literal": "^7.24.7", "name": "@babel/helper-validator-identifier", "npm": { "name": "@babel/helper-validator-identifier", "version": ">=7.24.7 <8.0.0", }, - "package_id": 215, + "package_id": 211, }, { "behavior": { "normal": true, }, - "id": 289, + "id": 285, "literal": "^2.4.2", "name": "chalk", "npm": { "name": "chalk", "version": ">=2.4.2 <3.0.0", }, - "package_id": 208, + "package_id": 204, }, { "behavior": { "normal": true, }, - "id": 290, + "id": 286, "literal": "^4.0.0", "name": "js-tokens", "npm": { @@ -3799,7 +3746,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 291, + "id": 287, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -3812,346 +3759,346 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 292, + "id": 288, "literal": "^3.2.1", "name": "ansi-styles", "npm": { "name": "ansi-styles", "version": ">=3.2.1 <4.0.0", }, - "package_id": 212, + "package_id": 208, }, { "behavior": { "normal": true, }, - "id": 293, + "id": 289, "literal": "^1.0.5", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=1.0.5 <2.0.0", }, - "package_id": 211, + "package_id": 207, }, { "behavior": { "normal": true, }, - "id": 294, + "id": 290, "literal": "^5.3.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=5.3.0 <6.0.0", }, - "package_id": 209, + "package_id": 205, }, { "behavior": { "normal": true, }, - "id": 295, + "id": 291, "literal": "^3.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=3.0.0 <4.0.0", }, - "package_id": 210, + "package_id": 206, }, { "behavior": { "normal": true, }, - "id": 296, + "id": 292, "literal": "^1.9.0", "name": "color-convert", "npm": { "name": "color-convert", "version": ">=1.9.0 <2.0.0", }, - "package_id": 213, + "package_id": 209, }, { "behavior": { "normal": true, }, - "id": 297, + "id": 293, "literal": "1.1.3", "name": "color-name", "npm": { "name": "color-name", "version": "==1.1.3", }, - "package_id": 214, + "package_id": 210, }, { "behavior": { "normal": true, }, - "id": 298, + "id": 294, "literal": "^2.0.1", "name": "argparse", "npm": { "name": "argparse", "version": ">=2.0.1 <3.0.0", }, - "package_id": 217, + "package_id": 213, }, { "behavior": { "normal": true, }, - "id": 299, + "id": 295, "literal": "^1.0.0", "name": "parent-module", "npm": { "name": "parent-module", "version": ">=1.0.0 <2.0.0", }, - "package_id": 220, + "package_id": 216, }, { "behavior": { "normal": true, }, - "id": 300, + "id": 296, "literal": "^4.0.0", "name": "resolve-from", "npm": { "name": "resolve-from", "version": ">=4.0.0 <5.0.0", }, - "package_id": 219, + "package_id": 215, }, { "behavior": { "normal": true, }, - "id": 301, + "id": 297, "literal": "^3.0.0", "name": "callsites", "npm": { "name": "callsites", "version": ">=3.0.0 <4.0.0", }, - "package_id": 221, + "package_id": 217, }, { "behavior": { "normal": true, }, - "id": 302, + "id": 298, "literal": "1.6.0", "name": "busboy", "npm": { "name": "busboy", "version": "==1.6.0", }, - "package_id": 239, + "package_id": 235, }, { "behavior": { "normal": true, }, - "id": 303, + "id": 299, "literal": "8.4.31", "name": "postcss", "npm": { "name": "postcss", "version": "==8.4.31", }, - "package_id": 238, + "package_id": 234, }, { "behavior": { "normal": true, }, - "id": 304, + "id": 300, "literal": "14.1.3", "name": "@next/env", "npm": { "name": "@next/env", "version": "==14.1.3", }, - "package_id": 237, + "package_id": 233, }, { "behavior": { "normal": true, }, - "id": 305, + "id": 301, "literal": "5.1.1", "name": "styled-jsx", "npm": { "name": "styled-jsx", "version": "==5.1.1", }, - "package_id": 235, + "package_id": 231, }, { "behavior": { "normal": true, }, - "id": 306, + "id": 302, "literal": "^4.2.11", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.11 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 307, + "id": 303, "literal": "0.5.2", "name": "@swc/helpers", "npm": { "name": "@swc/helpers", "version": "==0.5.2", }, - "package_id": 234, + "package_id": 230, }, { "behavior": { "normal": true, }, - "id": 308, + "id": 304, "literal": "^1.0.30001579", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001579 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "optional": true, }, - "id": 309, + "id": 305, "literal": "14.1.3", "name": "@next/swc-darwin-x64", "npm": { "name": "@next/swc-darwin-x64", "version": "==14.1.3", }, - "package_id": 232, + "package_id": 228, }, { "behavior": { "optional": true, }, - "id": 310, + "id": 306, "literal": "14.1.3", "name": "@next/swc-darwin-arm64", "npm": { "name": "@next/swc-darwin-arm64", "version": "==14.1.3", }, - "package_id": 231, + "package_id": 227, }, { "behavior": { "optional": true, }, - "id": 311, + "id": 307, "literal": "14.1.3", "name": "@next/swc-linux-x64-gnu", "npm": { "name": "@next/swc-linux-x64-gnu", "version": "==14.1.3", }, - "package_id": 230, + "package_id": 226, }, { "behavior": { "optional": true, }, - "id": 312, + "id": 308, "literal": "14.1.3", "name": "@next/swc-linux-x64-musl", "npm": { "name": "@next/swc-linux-x64-musl", "version": "==14.1.3", }, - "package_id": 229, + "package_id": 225, }, { "behavior": { "optional": true, }, - "id": 313, + "id": 309, "literal": "14.1.3", "name": "@next/swc-win32-x64-msvc", "npm": { "name": "@next/swc-win32-x64-msvc", "version": "==14.1.3", }, - "package_id": 228, + "package_id": 224, }, { "behavior": { "optional": true, }, - "id": 314, + "id": 310, "literal": "14.1.3", "name": "@next/swc-linux-arm64-gnu", "npm": { "name": "@next/swc-linux-arm64-gnu", "version": "==14.1.3", }, - "package_id": 227, + "package_id": 223, }, { "behavior": { "optional": true, }, - "id": 315, + "id": 311, "literal": "14.1.3", "name": "@next/swc-win32-ia32-msvc", "npm": { "name": "@next/swc-win32-ia32-msvc", "version": "==14.1.3", }, - "package_id": 226, + "package_id": 222, }, { "behavior": { "optional": true, }, - "id": 316, + "id": 312, "literal": "14.1.3", "name": "@next/swc-linux-arm64-musl", "npm": { "name": "@next/swc-linux-arm64-musl", "version": "==14.1.3", }, - "package_id": 225, + "package_id": 221, }, { "behavior": { "optional": true, }, - "id": 317, + "id": 313, "literal": "14.1.3", "name": "@next/swc-win32-arm64-msvc", "npm": { "name": "@next/swc-win32-arm64-msvc", "version": "==14.1.3", }, - "package_id": 224, + "package_id": 220, }, { "behavior": { "optional": true, "peer": true, }, - "id": 318, + "id": 314, "literal": "^1.3.0", "name": "sass", "npm": { @@ -4165,7 +4112,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "optional": true, "peer": true, }, - "id": 319, + "id": 315, "literal": "^1.1.0", "name": "@opentelemetry/api", "npm": { @@ -4178,7 +4125,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "peer": true, }, - "id": 320, + "id": 316, "literal": "^18.2.0", "name": "react-dom", "npm": { @@ -4191,7 +4138,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "peer": true, }, - "id": 321, + "id": 317, "literal": "^18.2.0", "name": "react", "npm": { @@ -4204,33 +4151,33 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 322, + "id": 318, "literal": "^2.4.0", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.4.0 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 323, + "id": 319, "literal": "0.0.1", "name": "client-only", "npm": { "name": "client-only", "version": "==0.0.1", }, - "package_id": 236, + "package_id": 232, }, { "behavior": { "peer": true, }, - "id": 324, + "id": 320, "literal": ">= 16.8.0 || 17.x.x || ^18.0.0-0", "name": "react", "npm": { @@ -4243,7 +4190,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 325, + "id": 321, "literal": "^3.3.6", "name": "nanoid", "npm": { @@ -4256,7 +4203,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 326, + "id": 322, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -4269,7 +4216,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 327, + "id": 323, "literal": "^1.0.2", "name": "source-map-js", "npm": { @@ -4282,138 +4229,138 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 328, + "id": 324, "literal": "^1.1.0", "name": "streamsearch", "npm": { "name": "streamsearch", "version": ">=1.1.0 <2.0.0", }, - "package_id": 240, + "package_id": 236, }, { "behavior": { "normal": true, }, - "id": 329, + "id": 325, "literal": "^7.33.2", "name": "eslint-plugin-react", "npm": { "name": "eslint-plugin-react", "version": ">=7.33.2 <8.0.0", }, - "package_id": 433, + "package_id": 429, }, { "behavior": { "normal": true, }, - "id": 330, + "id": 326, "literal": "^2.28.1", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=2.28.1 <3.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 331, + "id": 327, "literal": "^6.7.1", "name": "eslint-plugin-jsx-a11y", "npm": { "name": "eslint-plugin-jsx-a11y", "version": ">=6.7.1 <7.0.0", }, - "package_id": 408, + "package_id": 404, }, { "behavior": { "normal": true, }, - "id": 332, + "id": 328, "literal": "^1.3.3", "name": "@rushstack/eslint-patch", "npm": { "name": "@rushstack/eslint-patch", "version": ">=1.3.3 <2.0.0", }, - "package_id": 407, + "package_id": 403, }, { "behavior": { "normal": true, }, - "id": 333, + "id": 329, "literal": "14.1.3", "name": "@next/eslint-plugin-next", "npm": { "name": "@next/eslint-plugin-next", "version": "==14.1.3", }, - "package_id": 406, + "package_id": 402, }, { "behavior": { "normal": true, }, - "id": 334, + "id": 330, "literal": "^5.4.2 || ^6.0.0", "name": "@typescript-eslint/parser", "npm": { "name": "@typescript-eslint/parser", "version": ">=5.4.2 <6.0.0 || >=6.0.0 <7.0.0 && >=6.0.0 <7.0.0", }, - "package_id": 394, + "package_id": 390, }, { "behavior": { "normal": true, }, - "id": 335, + "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", "name": "eslint-plugin-react-hooks", "npm": { "name": "eslint-plugin-react-hooks", "version": ">=4.5.0 <5.0.0 || ==5.0.0-canary-7118f5dd7-20230705 && ==5.0.0-canary-7118f5dd7-20230705", }, - "package_id": 393, + "package_id": 389, }, { "behavior": { "normal": true, }, - "id": 336, + "id": 332, "literal": "^0.3.6", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.6 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 337, + "id": 333, "literal": "^3.5.2", "name": "eslint-import-resolver-typescript", "npm": { "name": "eslint-import-resolver-typescript", "version": ">=3.5.2 <4.0.0", }, - "package_id": 306, + "package_id": 302, }, { "behavior": { "optional": true, "peer": true, }, - "id": 338, + "id": 334, "literal": ">=3.3.1", "name": "typescript", "npm": { @@ -4426,150 +4373,150 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "peer": true, }, - "id": 339, + "id": 335, "literal": "^7.23.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.23.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 340, + "id": 336, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 341, + "id": 337, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 342, + "id": 338, "literal": "^4.0.0", "name": "chalk", "npm": { "name": "chalk", "version": ">=4.0.0 <5.0.0", }, - "package_id": 303, + "package_id": 299, }, { "behavior": { "normal": true, }, - "id": 343, + "id": 339, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 344, + "id": 340, "literal": "^9.6.1", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.1 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 345, + "id": 341, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 346, + "id": 342, "literal": "^1.4.2", "name": "esquery", "npm": { "name": "esquery", "version": ">=1.4.2 <2.0.0", }, - "package_id": 302, + "package_id": 298, }, { "behavior": { "normal": true, }, - "id": 347, + "id": 343, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 348, + "id": 344, "literal": "^5.0.0", "name": "find-up", "npm": { "name": "find-up", "version": ">=5.0.0 <6.0.0", }, - "package_id": 296, + "package_id": 292, }, { "behavior": { "normal": true, }, - "id": 349, + "id": 345, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 350, + "id": 346, "literal": "^4.0.0", "name": "is-glob", "npm": { @@ -4582,85 +4529,85 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 351, + "id": 347, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 352, + "id": 348, "literal": "^3.0.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=3.0.0 <4.0.0", }, - "package_id": 295, + "package_id": 291, }, { "behavior": { "normal": true, }, - "id": 353, + "id": 349, "literal": "^1.4.0", "name": "graphemer", "npm": { "name": "graphemer", "version": ">=1.4.0 <2.0.0", }, - "package_id": 294, + "package_id": 290, }, { "behavior": { "normal": true, }, - "id": 354, + "id": 350, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 355, + "id": 351, "literal": "8.50.0", "name": "@eslint/js", "npm": { "name": "@eslint/js", "version": "==8.50.0", }, - "package_id": 293, + "package_id": 289, }, { "behavior": { "normal": true, }, - "id": 356, + "id": 352, "literal": "^0.9.3", "name": "optionator", "npm": { "name": "optionator", "version": ">=0.9.3 <0.10.0", }, - "package_id": 286, + "package_id": 282, }, { "behavior": { "normal": true, }, - "id": 357, + "id": 353, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -4673,20 +4620,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 358, + "id": 354, "literal": "^0.2.0", "name": "text-table", "npm": { "name": "text-table", "version": ">=0.2.0 <0.3.0", }, - "package_id": 285, + "package_id": 281, }, { "behavior": { "normal": true, }, - "id": 359, + "id": 355, "literal": "^7.0.2", "name": "cross-spawn", "npm": { @@ -4699,7 +4646,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 360, + "id": 356, "literal": "^6.0.2", "name": "glob-parent", "npm": { @@ -4712,98 +4659,98 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 361, + "id": 357, "literal": "^0.1.4", "name": "imurmurhash", "npm": { "name": "imurmurhash", "version": ">=0.1.4 <0.2.0", }, - "package_id": 284, + "package_id": 280, }, { "behavior": { "normal": true, }, - "id": 362, + "id": 358, "literal": "^7.2.2", "name": "eslint-scope", "npm": { "name": "eslint-scope", "version": ">=7.2.2 <8.0.0", }, - "package_id": 282, + "package_id": 278, }, { "behavior": { "normal": true, }, - "id": 363, + "id": 359, "literal": "^4.6.2", "name": "lodash.merge", "npm": { "name": "lodash.merge", "version": ">=4.6.2 <5.0.0", }, - "package_id": 281, + "package_id": 277, }, { "behavior": { "normal": true, }, - "id": 364, + "id": 360, "literal": "^3.0.3", "name": "is-path-inside", "npm": { "name": "is-path-inside", "version": ">=3.0.3 <4.0.0", }, - "package_id": 280, + "package_id": 276, }, { "behavior": { "normal": true, }, - "id": 365, + "id": 361, "literal": "^3.1.3", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.3 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 366, + "id": 362, "literal": "^1.4.0", "name": "natural-compare", "npm": { "name": "natural-compare", "version": ">=1.4.0 <2.0.0", }, - "package_id": 279, + "package_id": 275, }, { "behavior": { "normal": true, }, - "id": 367, + "id": 363, "literal": "^2.1.2", "name": "@eslint/eslintrc", "npm": { "name": "@eslint/eslintrc", "version": ">=2.1.2 <3.0.0", }, - "package_id": 265, + "package_id": 261, }, { "behavior": { "normal": true, }, - "id": 368, + "id": 364, "literal": "^1.2.8", "name": "@nodelib/fs.walk", "npm": { @@ -4816,189 +4763,189 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 369, + "id": 365, "literal": "^6.0.1", "name": "file-entry-cache", "npm": { "name": "file-entry-cache", "version": ">=6.0.1 <7.0.0", }, - "package_id": 254, + "package_id": 250, }, { "behavior": { "normal": true, }, - "id": 370, + "id": 366, "literal": "^3.4.3", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.3 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 371, + "id": 367, "literal": "^4.0.0", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=4.0.0 <5.0.0", }, - "package_id": 253, + "package_id": 249, }, { "behavior": { "normal": true, }, - "id": 372, + "id": 368, "literal": "^4.6.1", "name": "@eslint-community/regexpp", "npm": { "name": "@eslint-community/regexpp", "version": ">=4.6.1 <5.0.0", }, - "package_id": 252, + "package_id": 248, }, { "behavior": { "normal": true, }, - "id": 373, + "id": 369, "literal": "^0.11.11", "name": "@humanwhocodes/config-array", "npm": { "name": "@humanwhocodes/config-array", "version": ">=0.11.11 <0.12.0", }, - "package_id": 247, + "package_id": 243, }, { "behavior": { "normal": true, }, - "id": 374, + "id": 370, "literal": "^4.2.0", "name": "@eslint-community/eslint-utils", "npm": { "name": "@eslint-community/eslint-utils", "version": ">=4.2.0 <5.0.0", }, - "package_id": 245, + "package_id": 241, }, { "behavior": { "normal": true, }, - "id": 375, + "id": 371, "literal": "^1.0.1", "name": "@humanwhocodes/module-importer", "npm": { "name": "@humanwhocodes/module-importer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 244, + "package_id": 240, }, { "behavior": { "normal": true, }, - "id": 376, + "id": 372, "literal": "^1.0.1", "name": "json-stable-stringify-without-jsonify", "npm": { "name": "json-stable-stringify-without-jsonify", "version": ">=1.0.1 <2.0.0", }, - "package_id": 243, + "package_id": 239, }, { "behavior": { "normal": true, }, - "id": 377, + "id": 373, "literal": "^3.3.0", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.3.0 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 378, + "id": 374, "literal": "^6.0.0 || ^7.0.0 || >=8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 && >=8.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 379, + "id": 375, "literal": "^2.0.2", "name": "@humanwhocodes/object-schema", "npm": { "name": "@humanwhocodes/object-schema", "version": ">=2.0.2 <3.0.0", }, - "package_id": 251, + "package_id": 247, }, { "behavior": { "normal": true, }, - "id": 380, + "id": 376, "literal": "^4.3.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 381, + "id": 377, "literal": "^3.0.5", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.0.5 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 382, + "id": 378, "literal": "^1.1.7", "name": "brace-expansion", "npm": { "name": "brace-expansion", "version": ">=1.1.7 <2.0.0", }, - "package_id": 249, + "package_id": 245, }, { "behavior": { "normal": true, }, - "id": 383, + "id": 379, "literal": "^1.0.0", "name": "balanced-match", "npm": { @@ -5011,696 +4958,696 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 384, + "id": 380, "literal": "0.0.1", "name": "concat-map", "npm": { "name": "concat-map", "version": "==0.0.1", }, - "package_id": 250, + "package_id": 246, }, { "behavior": { "normal": true, }, - "id": 385, + "id": 381, "literal": "^3.0.4", "name": "flat-cache", "npm": { "name": "flat-cache", "version": ">=3.0.4 <4.0.0", }, - "package_id": 255, + "package_id": 251, }, { "behavior": { "normal": true, }, - "id": 386, + "id": 382, "literal": "^3.2.9", "name": "flatted", "npm": { "name": "flatted", "version": ">=3.2.9 <4.0.0", }, - "package_id": 264, + "package_id": 260, }, { "behavior": { "normal": true, }, - "id": 387, + "id": 383, "literal": "^4.5.3", "name": "keyv", "npm": { "name": "keyv", "version": ">=4.5.3 <5.0.0", }, - "package_id": 262, + "package_id": 258, }, { "behavior": { "normal": true, }, - "id": 388, + "id": 384, "literal": "^3.0.2", "name": "rimraf", "npm": { "name": "rimraf", "version": ">=3.0.2 <4.0.0", }, - "package_id": 256, + "package_id": 252, }, { "behavior": { "normal": true, }, - "id": 389, + "id": 385, "literal": "^7.1.3", "name": "glob", "npm": { "name": "glob", "version": ">=7.1.3 <8.0.0", }, - "package_id": 257, + "package_id": 253, }, { "behavior": { "normal": true, }, - "id": 390, + "id": 386, "literal": "^1.0.0", "name": "fs.realpath", "npm": { "name": "fs.realpath", "version": ">=1.0.0 <2.0.0", }, - "package_id": 261, + "package_id": 257, }, { "behavior": { "normal": true, }, - "id": 391, + "id": 387, "literal": "^1.0.4", "name": "inflight", "npm": { "name": "inflight", "version": ">=1.0.4 <2.0.0", }, - "package_id": 260, + "package_id": 256, }, { "behavior": { "normal": true, }, - "id": 392, + "id": 388, "literal": "2", "name": "inherits", "npm": { "name": "inherits", "version": "<3.0.0 >=2.0.0", }, - "package_id": 259, + "package_id": 255, }, { "behavior": { "normal": true, }, - "id": 393, + "id": 389, "literal": "^3.1.1", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.1 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 394, + "id": 390, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 395, + "id": 391, "literal": "^1.0.0", "name": "path-is-absolute", "npm": { "name": "path-is-absolute", "version": ">=1.0.0 <2.0.0", }, - "package_id": 258, + "package_id": 254, }, { "behavior": { "normal": true, }, - "id": 396, + "id": 392, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 397, + "id": 393, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 398, + "id": 394, "literal": "3.0.1", "name": "json-buffer", "npm": { "name": "json-buffer", "version": "==3.0.1", }, - "package_id": 263, + "package_id": 259, }, { "behavior": { "normal": true, }, - "id": 399, + "id": 395, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 400, + "id": 396, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 401, + "id": 397, "literal": "^9.6.0", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.0 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 402, + "id": 398, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 403, + "id": 399, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 404, + "id": 400, "literal": "^3.2.1", "name": "import-fresh", "npm": { "name": "import-fresh", "version": ">=3.2.1 <4.0.0", }, - "package_id": 218, + "package_id": 214, }, { "behavior": { "normal": true, }, - "id": 405, + "id": 401, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 406, + "id": 402, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 407, + "id": 403, "literal": "^3.1.1", "name": "strip-json-comments", "npm": { "name": "strip-json-comments", "version": ">=3.1.1 <4.0.0", }, - "package_id": 266, + "package_id": 262, }, { "behavior": { "normal": true, }, - "id": 408, + "id": 404, "literal": "^0.20.2", "name": "type-fest", "npm": { "name": "type-fest", "version": ">=0.20.2 <0.21.0", }, - "package_id": 269, + "package_id": 265, }, { "behavior": { "normal": true, }, - "id": 409, + "id": 405, "literal": "^8.9.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=8.9.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 410, + "id": 406, "literal": "^5.3.2", "name": "acorn-jsx", "npm": { "name": "acorn-jsx", "version": ">=5.3.2 <6.0.0", }, - "package_id": 271, + "package_id": 267, }, { "behavior": { "normal": true, }, - "id": 411, + "id": 407, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 412, + "id": 408, "literal": "^6.0.0 || ^7.0.0 || ^8.0.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 413, + "id": 409, "literal": "^3.1.1", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.1 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 414, + "id": 410, "literal": "^2.0.0", "name": "fast-json-stable-stringify", "npm": { "name": "fast-json-stable-stringify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 277, + "package_id": 273, }, { "behavior": { "normal": true, }, - "id": 415, + "id": 411, "literal": "^0.4.1", "name": "json-schema-traverse", "npm": { "name": "json-schema-traverse", "version": ">=0.4.1 <0.5.0", }, - "package_id": 276, + "package_id": 272, }, { "behavior": { "normal": true, }, - "id": 416, + "id": 412, "literal": "^4.2.2", "name": "uri-js", "npm": { "name": "uri-js", "version": ">=4.2.2 <5.0.0", }, - "package_id": 274, + "package_id": 270, }, { "behavior": { "normal": true, }, - "id": 417, + "id": 413, "literal": "^2.1.0", "name": "punycode", "npm": { "name": "punycode", "version": ">=2.1.0 <3.0.0", }, - "package_id": 275, + "package_id": 271, }, { "behavior": { "normal": true, }, - "id": 418, + "id": 414, "literal": "^4.3.0", "name": "esrecurse", "npm": { "name": "esrecurse", "version": ">=4.3.0 <5.0.0", }, - "package_id": 283, + "package_id": 279, }, { "behavior": { "normal": true, }, - "id": 419, + "id": 415, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 420, + "id": 416, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 421, + "id": 417, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 422, + "id": 418, "literal": "^0.1.3", "name": "deep-is", "npm": { "name": "deep-is", "version": ">=0.1.3 <0.2.0", }, - "package_id": 292, + "package_id": 288, }, { "behavior": { "normal": true, }, - "id": 423, + "id": 419, "literal": "^1.2.5", "name": "word-wrap", "npm": { "name": "word-wrap", "version": ">=1.2.5 <2.0.0", }, - "package_id": 291, + "package_id": 287, }, { "behavior": { "normal": true, }, - "id": 424, + "id": 420, "literal": "^0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 425, + "id": 421, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 426, + "id": 422, "literal": "^2.0.6", "name": "fast-levenshtein", "npm": { "name": "fast-levenshtein", "version": ">=2.0.6 <3.0.0", }, - "package_id": 287, + "package_id": 283, }, { "behavior": { "normal": true, }, - "id": 427, + "id": 423, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 428, + "id": 424, "literal": "~0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 429, + "id": 425, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 430, + "id": 426, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 431, + "id": 427, "literal": "^6.0.0", "name": "locate-path", "npm": { "name": "locate-path", "version": ">=6.0.0 <7.0.0", }, - "package_id": 298, + "package_id": 294, }, { "behavior": { "normal": true, }, - "id": 432, + "id": 428, "literal": "^4.0.0", "name": "path-exists", "npm": { "name": "path-exists", "version": ">=4.0.0 <5.0.0", }, - "package_id": 297, + "package_id": 293, }, { "behavior": { "normal": true, }, - "id": 433, + "id": 429, "literal": "^5.0.0", "name": "p-locate", "npm": { "name": "p-locate", "version": ">=5.0.0 <6.0.0", }, - "package_id": 299, + "package_id": 295, }, { "behavior": { "normal": true, }, - "id": 434, + "id": 430, "literal": "^3.0.2", "name": "p-limit", "npm": { "name": "p-limit", "version": ">=3.0.2 <4.0.0", }, - "package_id": 300, + "package_id": 296, }, { "behavior": { "normal": true, }, - "id": 435, + "id": 431, "literal": "^0.1.0", "name": "yocto-queue", "npm": { "name": "yocto-queue", "version": ">=0.1.0 <0.2.0", }, - "package_id": 301, + "package_id": 297, }, { "behavior": { "normal": true, }, - "id": 436, + "id": 432, "literal": "^5.1.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.1.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 437, + "id": 433, "literal": "^4.1.0", "name": "ansi-styles", "npm": { @@ -5713,72 +5660,72 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 438, + "id": 434, "literal": "^7.1.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=7.1.0 <8.0.0", }, - "package_id": 304, + "package_id": 300, }, { "behavior": { "normal": true, }, - "id": 439, + "id": 435, "literal": "^4.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=4.0.0 <5.0.0", }, - "package_id": 305, + "package_id": 301, }, { "behavior": { "normal": true, }, - "id": 440, + "id": 436, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 441, + "id": 437, "literal": "^5.12.0", "name": "enhanced-resolve", "npm": { "name": "enhanced-resolve", "version": ">=5.12.0 <6.0.0", }, - "package_id": 391, + "package_id": 387, }, { "behavior": { "normal": true, }, - "id": 442, + "id": 438, "literal": "^2.7.4", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.7.4 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 443, + "id": 439, "literal": "^3.3.1", "name": "fast-glob", "npm": { @@ -5791,20 +5738,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 444, + "id": 440, "literal": "^4.5.0", "name": "get-tsconfig", "npm": { "name": "get-tsconfig", "version": ">=4.5.0 <5.0.0", }, - "package_id": 389, + "package_id": 385, }, { "behavior": { "normal": true, }, - "id": 445, + "id": 441, "literal": "^2.11.0", "name": "is-core-module", "npm": { @@ -5817,7 +5764,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 446, + "id": 442, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -5830,137 +5777,137 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "peer": true, }, - "id": 447, + "id": 443, "literal": "*", "name": "eslint", "npm": { "name": "eslint", "version": ">=0.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "peer": true, }, - "id": 448, + "id": 444, "literal": "*", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=0.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 449, + "id": 445, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 450, + "id": 446, "literal": "^1.2.3", "name": "array.prototype.findlastindex", "npm": { "name": "array.prototype.findlastindex", "version": ">=1.2.3 <2.0.0", }, - "package_id": 387, + "package_id": 383, }, { "behavior": { "normal": true, }, - "id": 451, + "id": 447, "literal": "^1.3.2", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.2 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 452, + "id": 448, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 453, + "id": 449, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 454, + "id": 450, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 455, + "id": 451, "literal": "^0.3.9", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.9 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 456, + "id": 452, "literal": "^2.8.0", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.8.0 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 457, + "id": 453, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -5973,7 +5920,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 458, + "id": 454, "literal": "^2.13.1", "name": "is-core-module", "npm": { @@ -5986,7 +5933,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 459, + "id": 455, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -5999,293 +5946,293 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 460, + "id": 456, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 461, + "id": 457, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 462, + "id": 458, "literal": "^1.0.1", "name": "object.groupby", "npm": { "name": "object.groupby", "version": ">=1.0.1 <2.0.0", }, - "package_id": 328, + "package_id": 324, }, { "behavior": { "normal": true, }, - "id": 463, + "id": 459, "literal": "^1.1.7", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.7 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 464, + "id": 460, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 465, + "id": 461, "literal": "^3.15.0", "name": "tsconfig-paths", "npm": { "name": "tsconfig-paths", "version": ">=3.15.0 <4.0.0", }, - "package_id": 308, + "package_id": 304, }, { "behavior": { "peer": true, }, - "id": 466, + "id": 462, "literal": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=2.0.0 <3.0.0 || >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 467, + "id": 463, "literal": "^0.0.29", "name": "@types/json5", "npm": { "name": "@types/json5", "version": ">=0.0.29 <0.0.30", }, - "package_id": 312, + "package_id": 308, }, { "behavior": { "normal": true, }, - "id": 468, + "id": 464, "literal": "^1.0.2", "name": "json5", "npm": { "name": "json5", "version": ">=1.0.2 <2.0.0", }, - "package_id": 311, + "package_id": 307, }, { "behavior": { "normal": true, }, - "id": 469, + "id": 465, "literal": "^1.2.6", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.6 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 470, + "id": 466, "literal": "^3.0.0", "name": "strip-bom", "npm": { "name": "strip-bom", "version": ">=3.0.0 <4.0.0", }, - "package_id": 309, + "package_id": 305, }, { "behavior": { "normal": true, }, - "id": 471, + "id": 467, "literal": "^1.2.0", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.0 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 472, + "id": 468, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 473, + "id": 469, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 474, + "id": 470, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 475, + "id": 471, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 476, + "id": 472, "literal": "^1.0.1", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.0.1 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 477, + "id": 473, "literal": "^1.0.0", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.0 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 478, + "id": 474, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 479, + "id": 475, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 480, + "id": 476, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 481, + "id": 477, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 482, + "id": 478, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6298,33 +6245,33 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 483, + "id": 479, "literal": "^1.0.1", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.1 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 484, + "id": 480, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 485, + "id": 481, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -6337,85 +6284,85 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 486, + "id": 482, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 487, + "id": 483, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 488, + "id": 484, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 489, + "id": 485, "literal": "^1.1.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.1.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 490, + "id": 486, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 491, + "id": 487, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 492, + "id": 488, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6428,59 +6375,59 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 493, + "id": 489, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 494, + "id": 490, "literal": "^1.2.1", "name": "set-function-length", "npm": { "name": "set-function-length", "version": ">=1.2.1 <2.0.0", }, - "package_id": 327, + "package_id": 323, }, { "behavior": { "normal": true, }, - "id": 495, + "id": 491, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 496, + "id": 492, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 497, + "id": 493, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6493,345 +6440,345 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 498, + "id": 494, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 499, + "id": 495, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 500, + "id": 496, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 501, + "id": 497, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 502, + "id": 498, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 503, + "id": 499, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 504, + "id": 500, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 505, + "id": 501, "literal": "^1.0.3", "name": "arraybuffer.prototype.slice", "npm": { "name": "arraybuffer.prototype.slice", "version": ">=1.0.3 <2.0.0", }, - "package_id": 377, + "package_id": 373, }, { "behavior": { "normal": true, }, - "id": 506, + "id": 502, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 507, + "id": 503, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 508, + "id": 504, "literal": "^1.0.1", "name": "data-view-buffer", "npm": { "name": "data-view-buffer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 376, + "package_id": 372, }, { "behavior": { "normal": true, }, - "id": 509, + "id": 505, "literal": "^1.0.1", "name": "data-view-byte-length", "npm": { "name": "data-view-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 375, + "package_id": 371, }, { "behavior": { "normal": true, }, - "id": 510, + "id": 506, "literal": "^1.0.0", "name": "data-view-byte-offset", "npm": { "name": "data-view-byte-offset", "version": ">=1.0.0 <2.0.0", }, - "package_id": 374, + "package_id": 370, }, { "behavior": { "normal": true, }, - "id": 511, + "id": 507, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 512, + "id": 508, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 513, + "id": 509, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 514, + "id": 510, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 515, + "id": 511, "literal": "^1.2.1", "name": "es-to-primitive", "npm": { "name": "es-to-primitive", "version": ">=1.2.1 <2.0.0", }, - "package_id": 371, + "package_id": 367, }, { "behavior": { "normal": true, }, - "id": 516, + "id": 512, "literal": "^1.1.6", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.6 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 517, + "id": 513, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 518, + "id": 514, "literal": "^1.0.2", "name": "get-symbol-description", "npm": { "name": "get-symbol-description", "version": ">=1.0.2 <2.0.0", }, - "package_id": 369, + "package_id": 365, }, { "behavior": { "normal": true, }, - "id": 519, + "id": 515, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 520, + "id": 516, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 521, + "id": 517, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 522, + "id": 518, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 523, + "id": 519, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 524, + "id": 520, "literal": "^2.0.2", "name": "hasown", "npm": { @@ -6844,1385 +6791,1385 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 525, + "id": 521, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 526, + "id": 522, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 527, + "id": 523, "literal": "^1.2.7", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.2.7 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 528, + "id": 524, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 529, + "id": 525, "literal": "^2.0.3", "name": "is-negative-zero", "npm": { "name": "is-negative-zero", "version": ">=2.0.3 <3.0.0", }, - "package_id": 363, + "package_id": 359, }, { "behavior": { "normal": true, }, - "id": 530, + "id": 526, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 531, + "id": 527, "literal": "^1.0.3", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.3 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 532, + "id": 528, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 533, + "id": 529, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 534, + "id": 530, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 535, + "id": 531, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 536, + "id": 532, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 537, + "id": 533, "literal": "^4.1.5", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.5 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 538, + "id": 534, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 539, + "id": 535, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 540, + "id": 536, "literal": "^1.0.3", "name": "safe-regex-test", "npm": { "name": "safe-regex-test", "version": ">=1.0.3 <2.0.0", }, - "package_id": 352, + "package_id": 348, }, { "behavior": { "normal": true, }, - "id": 541, + "id": 537, "literal": "^1.2.9", "name": "string.prototype.trim", "npm": { "name": "string.prototype.trim", "version": ">=1.2.9 <2.0.0", }, - "package_id": 351, + "package_id": 347, }, { "behavior": { "normal": true, }, - "id": 542, + "id": 538, "literal": "^1.0.8", "name": "string.prototype.trimend", "npm": { "name": "string.prototype.trimend", "version": ">=1.0.8 <2.0.0", }, - "package_id": 350, + "package_id": 346, }, { "behavior": { "normal": true, }, - "id": 543, + "id": 539, "literal": "^1.0.8", "name": "string.prototype.trimstart", "npm": { "name": "string.prototype.trimstart", "version": ">=1.0.8 <2.0.0", }, - "package_id": 349, + "package_id": 345, }, { "behavior": { "normal": true, }, - "id": 544, + "id": 540, "literal": "^1.0.2", "name": "typed-array-buffer", "npm": { "name": "typed-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 348, + "package_id": 344, }, { "behavior": { "normal": true, }, - "id": 545, + "id": 541, "literal": "^1.0.1", "name": "typed-array-byte-length", "npm": { "name": "typed-array-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 347, + "package_id": 343, }, { "behavior": { "normal": true, }, - "id": 546, + "id": 542, "literal": "^1.0.2", "name": "typed-array-byte-offset", "npm": { "name": "typed-array-byte-offset", "version": ">=1.0.2 <2.0.0", }, - "package_id": 346, + "package_id": 342, }, { "behavior": { "normal": true, }, - "id": 547, + "id": 543, "literal": "^1.0.6", "name": "typed-array-length", "npm": { "name": "typed-array-length", "version": ">=1.0.6 <2.0.0", }, - "package_id": 344, + "package_id": 340, }, { "behavior": { "normal": true, }, - "id": 548, + "id": 544, "literal": "^1.0.2", "name": "unbox-primitive", "npm": { "name": "unbox-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 336, + "package_id": 332, }, { "behavior": { "normal": true, }, - "id": 549, + "id": 545, "literal": "^1.1.15", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.15 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 550, + "id": 546, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 551, + "id": 547, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 552, + "id": 548, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 553, + "id": 549, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 554, + "id": 550, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 555, + "id": 551, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 556, + "id": 552, "literal": "^1.1.3", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.3 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 557, + "id": 553, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 558, + "id": 554, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 559, + "id": 555, "literal": "^1.0.2", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.2 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 560, + "id": 556, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 561, + "id": 557, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 562, + "id": 558, "literal": "^1.0.1", "name": "is-bigint", "npm": { "name": "is-bigint", "version": ">=1.0.1 <2.0.0", }, - "package_id": 342, + "package_id": 338, }, { "behavior": { "normal": true, }, - "id": 563, + "id": 559, "literal": "^1.1.0", "name": "is-boolean-object", "npm": { "name": "is-boolean-object", "version": ">=1.1.0 <2.0.0", }, - "package_id": 341, + "package_id": 337, }, { "behavior": { "normal": true, }, - "id": 564, + "id": 560, "literal": "^1.0.4", "name": "is-number-object", "npm": { "name": "is-number-object", "version": ">=1.0.4 <2.0.0", }, - "package_id": 340, + "package_id": 336, }, { "behavior": { "normal": true, }, - "id": 565, + "id": 561, "literal": "^1.0.5", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.5 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 566, + "id": 562, "literal": "^1.0.3", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.3 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 567, + "id": 563, "literal": "^1.0.2", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.2 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 568, + "id": 564, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 569, + "id": 565, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 570, + "id": 566, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 571, + "id": 567, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 572, + "id": 568, "literal": "^1.0.1", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.1 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 573, + "id": 569, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 574, + "id": 570, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 575, + "id": 571, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 576, + "id": 572, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 577, + "id": 573, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 578, + "id": 574, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 579, + "id": 575, "literal": "^1.1.14", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.14 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 580, + "id": 576, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 581, + "id": 577, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 582, + "id": 578, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 583, + "id": 579, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 584, + "id": 580, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 585, + "id": 581, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 586, + "id": 582, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 587, + "id": 583, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 588, + "id": 584, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 589, + "id": 585, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 590, + "id": 586, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 591, + "id": 587, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 592, + "id": 588, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 593, + "id": 589, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 594, + "id": 590, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 595, + "id": 591, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 596, + "id": 592, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 597, + "id": 593, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 598, + "id": 594, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 599, + "id": 595, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 600, + "id": 596, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 601, + "id": 597, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 602, + "id": 598, "literal": "^1.23.0", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.0 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 603, + "id": 599, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 604, + "id": 600, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 605, + "id": 601, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 606, + "id": 602, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 607, + "id": 603, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 608, + "id": 604, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 609, + "id": 605, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 610, + "id": 606, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 611, + "id": 607, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 612, + "id": 608, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 613, + "id": 609, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 614, + "id": 610, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 615, + "id": 611, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 616, + "id": 612, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 617, + "id": 613, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 618, + "id": 614, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 619, + "id": 615, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 620, + "id": 616, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 621, + "id": 617, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 622, + "id": 618, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 623, + "id": 619, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 624, + "id": 620, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 625, + "id": 621, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 626, + "id": 622, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 627, + "id": 623, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 628, + "id": 624, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 629, + "id": 625, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 630, + "id": 626, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 631, + "id": 627, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -8235,267 +8182,267 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 632, + "id": 628, "literal": "^1.0.4", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.4 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 633, + "id": 629, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 634, + "id": 630, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 635, + "id": 631, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 636, + "id": 632, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 637, + "id": 633, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 638, + "id": 634, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 639, + "id": 635, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 640, + "id": 636, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 641, + "id": 637, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 642, + "id": 638, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 643, + "id": 639, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 644, + "id": 640, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 645, + "id": 641, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 646, + "id": 642, "literal": "^1.1.4", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.4 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 647, + "id": 643, "literal": "^1.0.1", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.1 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 648, + "id": 644, "literal": "^1.0.2", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.2 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 649, + "id": 645, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 650, + "id": 646, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 651, + "id": 647, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 652, + "id": 648, "literal": "^2.0.1", "name": "hasown", "npm": { @@ -8508,345 +8455,345 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 653, + "id": 649, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 654, + "id": 650, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 655, + "id": 651, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 656, + "id": 652, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 657, + "id": 653, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 658, + "id": 654, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 659, + "id": 655, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 660, + "id": 656, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 661, + "id": 657, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 662, + "id": 658, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 663, + "id": 659, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 664, + "id": 660, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 665, + "id": 661, "literal": "^1.22.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 666, + "id": 662, "literal": "^1.2.1", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.2.1 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 667, + "id": 663, "literal": "^1.2.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 668, + "id": 664, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 669, + "id": 665, "literal": "^1.0.2", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 670, + "id": 666, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 671, + "id": 667, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 672, + "id": 668, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 673, + "id": 669, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 674, + "id": 670, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 675, + "id": 671, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 676, + "id": 672, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 677, + "id": 673, "literal": "^2.1.1", "name": "ms", "npm": { "name": "ms", "version": ">=2.1.1 <3.0.0", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 678, + "id": 674, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 679, + "id": 675, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -8859,7 +8806,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 680, + "id": 676, "literal": "^1.22.4", "name": "resolve", "npm": { @@ -8872,72 +8819,72 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 681, + "id": 677, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 682, + "id": 678, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 683, + "id": 679, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 684, + "id": 680, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 685, + "id": 681, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 686, + "id": 682, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -8950,384 +8897,384 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 687, + "id": 683, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 688, + "id": 684, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 689, + "id": 685, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 690, + "id": 686, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 691, + "id": 687, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 692, + "id": 688, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 693, + "id": 689, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 694, + "id": 690, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 695, + "id": 691, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 696, + "id": 692, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 697, + "id": 693, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 698, + "id": 694, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 699, + "id": 695, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 700, + "id": 696, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 701, + "id": 697, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 702, + "id": 698, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 703, + "id": 699, "literal": "^1.0.0", "name": "resolve-pkg-maps", "npm": { "name": "resolve-pkg-maps", "version": ">=1.0.0 <2.0.0", }, - "package_id": 390, + "package_id": 386, }, { "behavior": { "normal": true, }, - "id": 704, + "id": 700, "literal": "^4.2.4", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.4 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 705, + "id": 701, "literal": "^2.2.0", "name": "tapable", "npm": { "name": "tapable", "version": ">=2.2.0 <3.0.0", }, - "package_id": 392, + "package_id": 388, }, { "behavior": { "peer": true, }, - "id": 706, + "id": 702, "literal": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=8.0.0-0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 707, + "id": 703, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 708, + "id": 704, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 709, + "id": 705, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 710, + "id": 706, "literal": "6.21.0", "name": "@typescript-eslint/scope-manager", "npm": { "name": "@typescript-eslint/scope-manager", "version": "==6.21.0", }, - "package_id": 405, + "package_id": 401, }, { "behavior": { "normal": true, }, - "id": 711, + "id": 707, "literal": "6.21.0", "name": "@typescript-eslint/typescript-estree", "npm": { "name": "@typescript-eslint/typescript-estree", "version": "==6.21.0", }, - "package_id": 395, + "package_id": 391, }, { "behavior": { "peer": true, }, - "id": 712, + "id": 708, "literal": "^7.0.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 713, + "id": 709, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 714, + "id": 710, "literal": "^11.1.0", "name": "globby", "npm": { "name": "globby", "version": ">=11.1.0 <12.0.0", }, - "package_id": 400, + "package_id": 396, }, { "behavior": { "normal": true, }, - "id": 715, + "id": 711, "literal": "^7.5.4", "name": "semver", "npm": { "name": "semver", "version": ">=7.5.4 <8.0.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 716, + "id": 712, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -9340,85 +9287,85 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 717, + "id": 713, "literal": "9.0.3", "name": "minimatch", "npm": { "name": "minimatch", "version": "==9.0.3", }, - "package_id": 399, + "package_id": 395, }, { "behavior": { "normal": true, }, - "id": 718, + "id": 714, "literal": "^1.0.1", "name": "ts-api-utils", "npm": { "name": "ts-api-utils", "version": ">=1.0.1 <2.0.0", }, - "package_id": 398, + "package_id": 394, }, { "behavior": { "normal": true, }, - "id": 719, + "id": 715, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 720, + "id": 716, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 721, + "id": 717, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 722, + "id": 718, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "peer": true, }, - "id": 723, + "id": 719, "literal": ">=4.2.0", "name": "typescript", "npm": { @@ -9431,7 +9378,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 724, + "id": 720, "literal": "^2.0.1", "name": "brace-expansion", "npm": { @@ -9444,33 +9391,33 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 725, + "id": 721, "literal": "^2.1.0", "name": "array-union", "npm": { "name": "array-union", "version": ">=2.1.0 <3.0.0", }, - "package_id": 404, + "package_id": 400, }, { "behavior": { "normal": true, }, - "id": 726, + "id": 722, "literal": "^3.0.1", "name": "dir-glob", "npm": { "name": "dir-glob", "version": ">=3.0.1 <4.0.0", }, - "package_id": 402, + "package_id": 398, }, { "behavior": { "normal": true, }, - "id": 727, + "id": 723, "literal": "^3.2.9", "name": "fast-glob", "npm": { @@ -9483,20 +9430,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 728, + "id": 724, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 729, + "id": 725, "literal": "^1.4.1", "name": "merge2", "npm": { @@ -9509,59 +9456,59 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 730, + "id": 726, "literal": "^3.0.0", "name": "slash", "npm": { "name": "slash", "version": ">=3.0.0 <4.0.0", }, - "package_id": 401, + "package_id": 397, }, { "behavior": { "normal": true, }, - "id": 731, + "id": 727, "literal": "^4.0.0", "name": "path-type", "npm": { "name": "path-type", "version": ">=4.0.0 <5.0.0", }, - "package_id": 403, + "package_id": 399, }, { "behavior": { "normal": true, }, - "id": 732, + "id": 728, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 733, + "id": 729, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 734, + "id": 730, "literal": "10.3.10", "name": "glob", "npm": { @@ -9574,111 +9521,111 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 735, + "id": 731, "literal": "^7.23.2", "name": "@babel/runtime", "npm": { "name": "@babel/runtime", "version": ">=7.23.2 <8.0.0", }, - "package_id": 431, + "package_id": 427, }, { "behavior": { "normal": true, }, - "id": 736, + "id": 732, "literal": "^5.3.0", "name": "aria-query", "npm": { "name": "aria-query", "version": ">=5.3.0 <6.0.0", }, - "package_id": 430, + "package_id": 426, }, { "behavior": { "normal": true, }, - "id": 737, + "id": 733, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 738, + "id": 734, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 739, + "id": 735, "literal": "^0.0.8", "name": "ast-types-flow", "npm": { "name": "ast-types-flow", "version": ">=0.0.8 <0.0.9", }, - "package_id": 429, + "package_id": 425, }, { "behavior": { "normal": true, }, - "id": 740, + "id": 736, "literal": "=4.7.0", "name": "axe-core", "npm": { "name": "axe-core", "version": "==4.7.0", }, - "package_id": 428, + "package_id": 424, }, { "behavior": { "normal": true, }, - "id": 741, + "id": 737, "literal": "^3.2.1", "name": "axobject-query", "npm": { "name": "axobject-query", "version": ">=3.2.1 <4.0.0", }, - "package_id": 426, + "package_id": 422, }, { "behavior": { "normal": true, }, - "id": 742, + "id": 738, "literal": "^1.0.8", "name": "damerau-levenshtein", "npm": { "name": "damerau-levenshtein", "version": ">=1.0.8 <2.0.0", }, - "package_id": 425, + "package_id": 421, }, { "behavior": { "normal": true, }, - "id": 743, + "id": 739, "literal": "^9.2.2", "name": "emoji-regex", "npm": { @@ -9691,20 +9638,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 744, + "id": 740, "literal": "^1.0.15", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.15 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 745, + "id": 741, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -9717,254 +9664,254 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 746, + "id": 742, "literal": "^3.3.5", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=3.3.5 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 747, + "id": 743, "literal": "^1.0.9", "name": "language-tags", "npm": { "name": "language-tags", "version": ">=1.0.9 <2.0.0", }, - "package_id": 410, + "package_id": 406, }, { "behavior": { "normal": true, }, - "id": 748, + "id": 744, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 749, + "id": 745, "literal": "^1.1.7", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.7 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 750, + "id": 746, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "peer": true, }, - "id": 751, + "id": 747, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 752, + "id": 748, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 753, + "id": 749, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 754, + "id": 750, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 755, + "id": 751, "literal": "^0.3.20", "name": "language-subtag-registry", "npm": { "name": "language-subtag-registry", "version": ">=0.3.20 <0.4.0", }, - "package_id": 411, + "package_id": 407, }, { "behavior": { "normal": true, }, - "id": 756, + "id": 752, "literal": "^3.1.6", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.6 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 757, + "id": 753, "literal": "^1.3.1", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.1 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 758, + "id": 754, "literal": "^4.1.4", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.4 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 759, + "id": 755, "literal": "^1.1.6", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.6 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 760, + "id": 756, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 761, + "id": 757, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 762, + "id": 758, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 763, + "id": 759, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 764, + "id": 760, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 765, + "id": 761, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -9977,982 +9924,982 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 766, + "id": 762, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 767, + "id": 763, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 768, + "id": 764, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 769, + "id": 765, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 770, + "id": 766, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 771, + "id": 767, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 772, + "id": 768, "literal": "^1.1.2", "name": "iterator.prototype", "npm": { "name": "iterator.prototype", "version": ">=1.1.2 <2.0.0", }, - "package_id": 414, + "package_id": 410, }, { "behavior": { "normal": true, }, - "id": 773, + "id": 769, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 774, + "id": 770, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 775, + "id": 771, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 776, + "id": 772, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 777, + "id": 773, "literal": "^1.0.4", "name": "reflect.getprototypeof", "npm": { "name": "reflect.getprototypeof", "version": ">=1.0.4 <2.0.0", }, - "package_id": 415, + "package_id": 411, }, { "behavior": { "normal": true, }, - "id": 778, + "id": 774, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 779, + "id": 775, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 780, + "id": 776, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 781, + "id": 777, "literal": "^1.23.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 782, + "id": 778, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 783, + "id": 779, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 784, + "id": 780, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 785, + "id": 781, "literal": "^1.1.3", "name": "which-builtin-type", "npm": { "name": "which-builtin-type", "version": ">=1.1.3 <2.0.0", }, - "package_id": 416, + "package_id": 412, }, { "behavior": { "normal": true, }, - "id": 786, + "id": 782, "literal": "^1.1.5", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.5 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 787, + "id": 783, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 788, + "id": 784, "literal": "^2.0.0", "name": "is-async-function", "npm": { "name": "is-async-function", "version": ">=2.0.0 <3.0.0", }, - "package_id": 424, + "package_id": 420, }, { "behavior": { "normal": true, }, - "id": 789, + "id": 785, "literal": "^1.0.5", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.5 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 790, + "id": 786, "literal": "^1.0.2", "name": "is-finalizationregistry", "npm": { "name": "is-finalizationregistry", "version": ">=1.0.2 <2.0.0", }, - "package_id": 423, + "package_id": 419, }, { "behavior": { "normal": true, }, - "id": 791, + "id": 787, "literal": "^1.0.10", "name": "is-generator-function", "npm": { "name": "is-generator-function", "version": ">=1.0.10 <2.0.0", }, - "package_id": 422, + "package_id": 418, }, { "behavior": { "normal": true, }, - "id": 792, + "id": 788, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 793, + "id": 789, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 794, + "id": 790, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 795, + "id": 791, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 796, + "id": 792, "literal": "^1.0.1", "name": "which-collection", "npm": { "name": "which-collection", "version": ">=1.0.1 <2.0.0", }, - "package_id": 417, + "package_id": 413, }, { "behavior": { "normal": true, }, - "id": 797, + "id": 793, "literal": "^1.1.9", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.9 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 798, + "id": 794, "literal": "^2.0.3", "name": "is-map", "npm": { "name": "is-map", "version": ">=2.0.3 <3.0.0", }, - "package_id": 421, + "package_id": 417, }, { "behavior": { "normal": true, }, - "id": 799, + "id": 795, "literal": "^2.0.3", "name": "is-set", "npm": { "name": "is-set", "version": ">=2.0.3 <3.0.0", }, - "package_id": 420, + "package_id": 416, }, { "behavior": { "normal": true, }, - "id": 800, + "id": 796, "literal": "^2.0.2", "name": "is-weakmap", "npm": { "name": "is-weakmap", "version": ">=2.0.2 <3.0.0", }, - "package_id": 419, + "package_id": 415, }, { "behavior": { "normal": true, }, - "id": 801, + "id": 797, "literal": "^2.0.3", "name": "is-weakset", "npm": { "name": "is-weakset", "version": ">=2.0.3 <3.0.0", }, - "package_id": 418, + "package_id": 414, }, { "behavior": { "normal": true, }, - "id": 802, + "id": 798, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 803, + "id": 799, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 804, + "id": 800, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 805, + "id": 801, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 806, + "id": 802, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 807, + "id": 803, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 808, + "id": 804, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 809, + "id": 805, "literal": "^0.14.0", "name": "regenerator-runtime", "npm": { "name": "regenerator-runtime", "version": ">=0.14.0 <0.15.0", }, - "package_id": 432, + "package_id": 428, }, { "behavior": { "normal": true, }, - "id": 810, + "id": 806, "literal": "^3.1.8", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.8 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 811, + "id": 807, "literal": "^1.2.5", "name": "array.prototype.findlast", "npm": { "name": "array.prototype.findlast", "version": ">=1.2.5 <2.0.0", }, - "package_id": 441, + "package_id": 437, }, { "behavior": { "normal": true, }, - "id": 812, + "id": 808, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 813, + "id": 809, "literal": "^1.1.2", "name": "array.prototype.toreversed", "npm": { "name": "array.prototype.toreversed", "version": ">=1.1.2 <2.0.0", }, - "package_id": 440, + "package_id": 436, }, { "behavior": { "normal": true, }, - "id": 814, + "id": 810, "literal": "^1.1.3", "name": "array.prototype.tosorted", "npm": { "name": "array.prototype.tosorted", "version": ">=1.1.3 <2.0.0", }, - "package_id": 439, + "package_id": 435, }, { "behavior": { "normal": true, }, - "id": 815, + "id": 811, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 816, + "id": 812, "literal": "^1.0.19", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.19 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 817, + "id": 813, "literal": "^5.3.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.3.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 818, + "id": 814, "literal": "^2.4.1 || ^3.0.0", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=2.4.1 <3.0.0 || >=3.0.0 <4.0.0 && >=3.0.0 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 819, + "id": 815, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 820, + "id": 816, "literal": "^1.1.8", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.8 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 821, + "id": 817, "literal": "^2.0.8", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.8 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 822, + "id": 818, "literal": "^1.1.4", "name": "object.hasown", "npm": { "name": "object.hasown", "version": ">=1.1.4 <2.0.0", }, - "package_id": 438, + "package_id": 434, }, { "behavior": { "normal": true, }, - "id": 823, + "id": 819, "literal": "^1.2.0", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.2.0 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 824, + "id": 820, "literal": "^15.8.1", "name": "prop-types", "npm": { "name": "prop-types", "version": ">=15.8.1 <16.0.0", }, - "package_id": 436, + "package_id": 432, }, { "behavior": { "normal": true, }, - "id": 825, + "id": 821, "literal": "^2.0.0-next.5", "name": "resolve", "npm": { "name": "resolve", "version": ">=2.0.0-next.5 <3.0.0", }, - "package_id": 435, + "package_id": 431, }, { "behavior": { "normal": true, }, - "id": 826, + "id": 822, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 827, + "id": 823, "literal": "^4.0.11", "name": "string.prototype.matchall", "npm": { "name": "string.prototype.matchall", "version": ">=4.0.11 <5.0.0", }, - "package_id": 434, + "package_id": 430, }, { "behavior": { "peer": true, }, - "id": 828, + "id": 824, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 829, + "id": 825, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 830, + "id": 826, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 831, + "id": 827, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 832, + "id": 828, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 833, + "id": 829, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 834, + "id": 830, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 835, + "id": 831, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 836, + "id": 832, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 837, + "id": 833, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 838, + "id": 834, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 839, + "id": 835, "literal": "^2.0.2", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.2 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 840, + "id": 836, "literal": "^1.0.6", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.6 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 841, + "id": 837, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -10965,7 +10912,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 842, + "id": 838, "literal": "^1.0.7", "name": "path-parse", "npm": { @@ -10978,7 +10925,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 843, + "id": 839, "literal": "^1.0.0", "name": "supports-preserve-symlinks-flag", "npm": { @@ -10991,7 +10938,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 844, + "id": 840, "literal": "^1.4.0", "name": "loose-envify", "npm": { @@ -11004,7 +10951,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 845, + "id": 841, "literal": "^4.1.1", "name": "object-assign", "npm": { @@ -11017,345 +10964,345 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 846, + "id": 842, "literal": "^16.13.1", "name": "react-is", "npm": { "name": "react-is", "version": ">=16.13.1 <17.0.0", }, - "package_id": 437, + "package_id": 433, }, { "behavior": { "normal": true, }, - "id": 847, + "id": 843, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 848, + "id": 844, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 849, + "id": 845, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 850, + "id": 846, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 851, + "id": 847, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 852, + "id": 848, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 853, + "id": 849, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 854, + "id": 850, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 855, + "id": 851, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 856, + "id": 852, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 857, + "id": 853, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 858, + "id": 854, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 859, + "id": 855, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 860, + "id": 856, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 861, + "id": 857, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 862, + "id": 858, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 863, + "id": 859, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 864, + "id": 860, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 865, + "id": 861, "literal": "~8.5.10", "name": "@types/ws", "npm": { "name": "@types/ws", "version": ">=8.5.10 <8.6.0", }, - "package_id": 443, + "package_id": 439, }, { "behavior": { "normal": true, }, - "id": 866, + "id": 862, "literal": "~20.12.8", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=20.12.8 <20.13.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 867, + "id": 863, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 868, + "id": 864, "literal": "^4.21.10", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.10 <5.0.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 869, + "id": 865, "literal": "^1.0.30001538", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001538 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 870, + "id": 866, "literal": "^4.3.6", "name": "fraction.js", "npm": { "name": "fraction.js", "version": ">=4.3.6 <5.0.0", }, - "package_id": 446, + "package_id": 442, }, { "behavior": { "normal": true, }, - "id": 871, + "id": 867, "literal": "^0.1.2", "name": "normalize-range", "npm": { "name": "normalize-range", "version": ">=0.1.2 <0.2.0", }, - "package_id": 445, + "package_id": 441, }, { "behavior": { "normal": true, }, - "id": 872, + "id": 868, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -11368,7 +11315,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 873, + "id": 869, "literal": "^4.2.0", "name": "postcss-value-parser", "npm": { @@ -11381,7 +11328,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "peer": true, }, - "id": 874, + "id": 870, "literal": "^8.1.0", "name": "postcss", "npm": { @@ -11394,72 +11341,72 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "normal": true, }, - "id": 875, + "id": 871, "literal": "^1.0.30001587", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001587 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 876, + "id": 872, "literal": "^1.4.668", "name": "electron-to-chromium", "npm": { "name": "electron-to-chromium", "version": ">=1.4.668 <2.0.0", }, - "package_id": 450, + "package_id": 446, }, { "behavior": { "normal": true, }, - "id": 877, + "id": 873, "literal": "^2.0.14", "name": "node-releases", "npm": { "name": "node-releases", "version": ">=2.0.14 <3.0.0", }, - "package_id": 449, + "package_id": 445, }, { "behavior": { "normal": true, }, - "id": 878, + "id": 874, "literal": "^1.0.13", "name": "update-browserslist-db", "npm": { "name": "update-browserslist-db", "version": ">=1.0.13 <2.0.0", }, - "package_id": 448, + "package_id": 444, }, { "behavior": { "normal": true, }, - "id": 879, + "id": 875, "literal": "^3.1.2", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.2 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 880, + "id": 876, "literal": "^1.0.1", "name": "picocolors", "npm": { @@ -11472,128 +11419,128 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "behavior": { "peer": true, }, - "id": 881, + "id": 877, "literal": ">= 4.21.0", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 882, + "id": 878, "literal": "*", "name": "@types/react", "npm": { "name": "@types/react", "version": ">=0.0.0", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { "normal": true, }, - "id": 883, + "id": 879, "literal": "*", "name": "@types/prop-types", "npm": { "name": "@types/prop-types", "version": ">=0.0.0", }, - "package_id": 455, + "package_id": 451, }, { "behavior": { "normal": true, }, - "id": 884, + "id": 880, "literal": "*", "name": "@types/scheduler", "npm": { "name": "@types/scheduler", "version": ">=0.0.0", }, - "package_id": 454, + "package_id": 450, }, { "behavior": { "normal": true, }, - "id": 885, + "id": 881, "literal": "^3.0.2", "name": "csstype", "npm": { "name": "csstype", "version": ">=3.0.2 <4.0.0", }, - "package_id": 453, + "package_id": 449, }, ], "format": "v2", - "meta_hash": "4688315a50aab25bb1d5fe41e445b346f9c0c71bf75f43ebbc91db59253d9026", + "meta_hash": "632a4f7405ad36643df0c844e942395e7c61cf79c7738eb128eba03ebdd1e094", "package_index": { "@alloc/quick-lru": 13, - "@babel/code-frame": 206, - "@babel/helper-validator-identifier": 215, - "@babel/highlight": 207, - "@babel/runtime": 431, - "@eslint-community/eslint-utils": 245, - "@eslint-community/regexpp": 252, - "@eslint/eslintrc": 265, - "@eslint/js": 293, - "@humanwhocodes/config-array": 247, - "@humanwhocodes/module-importer": 244, - "@humanwhocodes/object-schema": 251, + "@babel/code-frame": 202, + "@babel/helper-validator-identifier": 211, + "@babel/highlight": 203, + "@babel/runtime": 427, + "@eslint-community/eslint-utils": 241, + "@eslint-community/regexpp": 248, + "@eslint/eslintrc": 261, + "@eslint/js": 289, + "@humanwhocodes/config-array": 243, + "@humanwhocodes/module-importer": 240, + "@humanwhocodes/object-schema": 247, "@isaacs/cliui": 74, "@jridgewell/gen-mapping": 100, "@jridgewell/resolve-uri": 103, "@jridgewell/set-array": 104, "@jridgewell/sourcemap-codec": 102, "@jridgewell/trace-mapping": 101, - "@next/env": 237, - "@next/eslint-plugin-next": 406, - "@next/swc-darwin-arm64": 231, - "@next/swc-darwin-x64": 232, - "@next/swc-linux-arm64-gnu": 227, - "@next/swc-linux-arm64-musl": 225, - "@next/swc-linux-x64-gnu": 230, - "@next/swc-linux-x64-musl": 229, - "@next/swc-win32-arm64-msvc": 224, - "@next/swc-win32-ia32-msvc": 226, - "@next/swc-win32-x64-msvc": 228, + "@next/env": 233, + "@next/eslint-plugin-next": 402, + "@next/swc-darwin-arm64": 227, + "@next/swc-darwin-x64": 228, + "@next/swc-linux-arm64-gnu": 223, + "@next/swc-linux-arm64-musl": 221, + "@next/swc-linux-x64-gnu": 226, + "@next/swc-linux-x64-musl": 225, + "@next/swc-win32-arm64-msvc": 220, + "@next/swc-win32-ia32-msvc": 222, + "@next/swc-win32-x64-msvc": 224, "@nodelib/fs.scandir": 46, "@nodelib/fs.stat": 49, "@nodelib/fs.walk": 43, "@pkgjs/parseargs": 73, - "@puppeteer/browsers": 114, - "@rushstack/eslint-patch": 407, - "@swc/helpers": 234, - "@tootallnate/quickjs-emscripten": 177, - "@types/json5": 312, + "@puppeteer/browsers": 115, + "@rushstack/eslint-patch": 403, + "@swc/helpers": 230, + "@tootallnate/quickjs-emscripten": 178, + "@types/json5": 308, "@types/node": [ - 182, - 456, + 183, + 452, ], - "@types/prop-types": 455, - "@types/react": 452, - "@types/react-dom": 451, - "@types/scheduler": 454, - "@types/ws": 443, - "@types/yauzl": 181, - "@typescript-eslint/parser": 394, - "@typescript-eslint/scope-manager": 405, - "@typescript-eslint/types": 397, - "@typescript-eslint/typescript-estree": 395, - "@typescript-eslint/visitor-keys": 396, - "acorn": 272, - "acorn-jsx": 271, - "agent-base": 155, - "ajv": 273, + "@types/prop-types": 451, + "@types/react": 448, + "@types/react-dom": 447, + "@types/scheduler": 450, + "@types/ws": 439, + "@types/yauzl": 182, + "@typescript-eslint/parser": 390, + "@typescript-eslint/scope-manager": 401, + "@typescript-eslint/types": 393, + "@typescript-eslint/typescript-estree": 391, + "@typescript-eslint/visitor-keys": 392, + "acorn": 268, + "acorn-jsx": 267, + "agent-base": 156, + "ajv": 269, "ansi-regex": [ 86, 77, @@ -11601,316 +11548,314 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "ansi-styles": [ 90, 81, - 212, + 208, ], "any-promise": 62, "anymatch": 55, "arg": 107, - "argparse": 217, - "aria-query": 430, - "array-buffer-byte-length": 378, - "array-includes": 388, - "array-union": 404, - "array.prototype.findlast": 441, - "array.prototype.findlastindex": 387, - "array.prototype.flat": 386, - "array.prototype.flatmap": 384, - "array.prototype.toreversed": 440, - "array.prototype.tosorted": 439, - "arraybuffer.prototype.slice": 377, - "ast-types": 166, - "ast-types-flow": 429, - "autoprefixer": 444, - "available-typed-arrays": 334, - "axe-core": 428, - "axobject-query": 426, - "b4a": 138, + "argparse": 213, + "aria-query": 426, + "array-buffer-byte-length": 374, + "array-includes": 384, + "array-union": 400, + "array.prototype.findlast": 437, + "array.prototype.findlastindex": 383, + "array.prototype.flat": 382, + "array.prototype.flatmap": 380, + "array.prototype.toreversed": 436, + "array.prototype.tosorted": 435, + "arraybuffer.prototype.slice": 373, + "ast-types": 167, + "ast-types-flow": 425, + "autoprefixer": 440, + "available-typed-arrays": 330, + "axe-core": 424, + "axobject-query": 422, + "b4a": 139, "balanced-match": 71, - "bare-events": 136, - "bare-fs": 133, - "bare-os": 132, - "bare-path": 131, - "bare-stream": 134, - "base64-js": 129, - "basic-ftp": 176, + "bare-events": 137, + "bare-fs": 134, + "bare-os": 133, + "bare-path": 132, + "bare-stream": 135, + "base64-js": 130, + "basic-ftp": 177, "binary-extensions": 54, "brace-expansion": [ 70, - 249, + 245, ], "braces": 34, - "browserslist": 447, - "buffer": 127, - "buffer-crc32": 185, - "bun-types": 442, - "busboy": 239, - "call-bind": 326, - "callsites": 221, + "browserslist": 443, + "buffer": 128, + "buffer-crc32": 186, + "bun-types": 438, + "busboy": 235, + "call-bind": 322, + "callsites": 217, "camelcase-css": 31, - "caniuse-lite": 233, + "caniuse-lite": 229, "chalk": [ - 303, - 208, + 299, + 204, ], "chokidar": 50, - "chromium-bidi": 198, - "client-only": 236, - "cliui": 124, + "chromium-bidi": 193, + "client-only": 232, + "cliui": 125, "color-convert": [ 82, - 213, + 209, ], "color-name": [ 83, - 214, + 210, ], "commander": 99, - "concat-map": 250, - "cosmiconfig": 201, - "cross-fetch": 193, + "concat-map": 246, + "cosmiconfig": 197, "cross-spawn": 93, "cssesc": 5, - "csstype": 453, - "damerau-levenshtein": 425, - "data-uri-to-buffer": 175, - "data-view-buffer": 376, - "data-view-byte-length": 375, - "data-view-byte-offset": 374, + "csstype": 449, + "damerau-levenshtein": 421, + "data-uri-to-buffer": 176, + "data-view-buffer": 372, + "data-view-byte-length": 371, + "data-view-byte-offset": 370, "debug": [ - 153, - 189, - 381, + 154, + 190, + 377, ], - "deep-is": 292, + "deep-is": 288, "default-create-template": 0, - "define-data-property": 324, - "define-properties": 317, - "degenerator": 160, - "dequal": 427, - "devtools-protocol": 192, + "define-data-property": 320, + "define-properties": 313, + "degenerator": 161, + "dequal": 423, + "devtools-protocol": 114, "didyoumean": 38, - "dir-glob": 402, + "dir-glob": 398, "dlv": 106, "doctrine": [ - 295, - 383, + 291, + 379, ], "eastasianwidth": 89, - "electron-to-chromium": 450, + "electron-to-chromium": 446, "emoji-regex": [ 88, 80, ], - "end-of-stream": 145, - "enhanced-resolve": 391, - "env-paths": 222, - "error-ex": 204, - "es-abstract": 329, - "es-define-property": 320, - "es-errors": 316, - "es-iterator-helpers": 413, - "es-object-atoms": 315, - "es-set-tostringtag": 373, - "es-shim-unscopables": 385, - "es-to-primitive": 371, - "escalade": 123, + "end-of-stream": 146, + "enhanced-resolve": 387, + "env-paths": 218, + "error-ex": 200, + "es-abstract": 325, + "es-define-property": 316, + "es-errors": 312, + "es-iterator-helpers": 409, + "es-object-atoms": 311, + "es-set-tostringtag": 369, + "es-shim-unscopables": 381, + "es-to-primitive": 367, + "escalade": 124, "escape-string-regexp": [ - 253, - 211, + 249, + 207, ], - "escodegen": 162, - "eslint": 242, - "eslint-config-next": 241, - "eslint-import-resolver-node": 382, - "eslint-import-resolver-typescript": 306, - "eslint-module-utils": 380, - "eslint-plugin-import": 307, - "eslint-plugin-jsx-a11y": 408, - "eslint-plugin-react": 433, - "eslint-plugin-react-hooks": 393, - "eslint-scope": 282, - "eslint-visitor-keys": 246, - "espree": 270, - "esprima": 161, - "esquery": 302, - "esrecurse": 283, - "estraverse": 165, - "esutils": 164, - "extract-zip": 180, - "fast-deep-equal": 278, - "fast-fifo": 140, + "escodegen": 163, + "eslint": 238, + "eslint-config-next": 237, + "eslint-import-resolver-node": 378, + "eslint-import-resolver-typescript": 302, + "eslint-module-utils": 376, + "eslint-plugin-import": 303, + "eslint-plugin-jsx-a11y": 404, + "eslint-plugin-react": 429, + "eslint-plugin-react-hooks": 389, + "eslint-scope": 278, + "eslint-visitor-keys": 242, + "espree": 266, + "esprima": 162, + "esquery": 298, + "esrecurse": 279, + "estraverse": 166, + "esutils": 165, + "extract-zip": 181, + "fast-deep-equal": 274, + "fast-fifo": 141, "fast-glob": 40, - "fast-json-stable-stringify": 277, - "fast-levenshtein": 287, + "fast-json-stable-stringify": 273, + "fast-levenshtein": 283, "fastq": 44, - "fd-slicer": 186, - "file-entry-cache": 254, + "fd-slicer": 187, + "file-entry-cache": 250, "fill-range": 35, - "find-up": 296, - "flat-cache": 255, - "flatted": 264, - "for-each": 332, + "find-up": 292, + "flat-cache": 251, + "flatted": 260, + "for-each": 328, "foreground-child": 91, - "fraction.js": 446, - "fs-extra": 171, - "fs.realpath": 261, + "fraction.js": 442, + "fs-extra": 172, + "fs.realpath": 257, "fsevents": 51, "function-bind": 21, - "function.prototype.name": 370, - "functions-have-names": 358, - "get-caller-file": 122, - "get-intrinsic": 321, - "get-stream": 188, - "get-symbol-description": 369, - "get-tsconfig": 389, - "get-uri": 170, + "function.prototype.name": 366, + "functions-have-names": 354, + "get-caller-file": 123, + "get-intrinsic": 317, + "get-stream": 189, + "get-symbol-description": 365, + "get-tsconfig": 385, + "get-uri": 171, "glob": [ 65, - 257, + 253, ], "glob-parent": [ 27, 42, ], - "globals": 268, - "globalthis": 368, - "globby": 400, - "gopd": 325, - "graceful-fs": 174, - "graphemer": 294, - "has-bigints": 343, + "globals": 264, + "globalthis": 364, + "globby": 396, + "gopd": 321, + "graceful-fs": 175, + "graphemer": 290, + "has-bigints": 339, "has-flag": [ - 305, - 210, + 301, + 206, ], - "has-property-descriptors": 319, - "has-proto": 323, - "has-symbols": 322, - "has-tostringtag": 331, + "has-property-descriptors": 315, + "has-proto": 319, + "has-symbols": 318, + "has-tostringtag": 327, "hasown": 20, - "http-proxy-agent": 169, - "https-proxy-agent": 168, - "ieee754": 128, - "ignore": 267, - "import-fresh": 218, - "imurmurhash": 284, - "inflight": 260, - "inherits": 259, - "internal-slot": 366, - "ip-address": 150, - "is-array-buffer": 365, - "is-arrayish": 205, - "is-async-function": 424, - "is-bigint": 342, + "http-proxy-agent": 170, + "https-proxy-agent": 169, + "ieee754": 129, + "ignore": 263, + "import-fresh": 214, + "imurmurhash": 280, + "inflight": 256, + "inherits": 255, + "internal-slot": 362, + "ip-address": 151, + "is-array-buffer": 361, + "is-arrayish": 201, + "is-async-function": 420, + "is-bigint": 338, "is-binary-path": 53, - "is-boolean-object": 341, - "is-callable": 333, + "is-boolean-object": 337, + "is-callable": 329, "is-core-module": 19, - "is-data-view": 364, - "is-date-object": 372, + "is-data-view": 360, + "is-date-object": 368, "is-extglob": 29, - "is-finalizationregistry": 423, + "is-finalizationregistry": 419, "is-fullwidth-code-point": 79, - "is-generator-function": 422, + "is-generator-function": 418, "is-glob": 28, - "is-map": 421, - "is-negative-zero": 363, + "is-map": 417, + "is-negative-zero": 359, "is-number": 37, - "is-number-object": 340, - "is-path-inside": 280, - "is-regex": 353, - "is-set": 420, - "is-shared-array-buffer": 362, - "is-string": 339, - "is-symbol": 338, - "is-typed-array": 345, - "is-weakmap": 419, - "is-weakref": 361, - "is-weakset": 418, - "isarray": 355, + "is-number-object": 336, + "is-path-inside": 276, + "is-regex": 349, + "is-set": 416, + "is-shared-array-buffer": 358, + "is-string": 335, + "is-symbol": 334, + "is-typed-array": 341, + "is-weakmap": 415, + "is-weakref": 357, + "is-weakset": 414, + "isarray": 351, "isexe": 95, - "iterator.prototype": 414, + "iterator.prototype": 410, "jackspeak": 72, "jiti": 105, "js-tokens": 111, - "js-yaml": 216, - "jsbn": 152, - "json-buffer": 263, - "json-parse-even-better-errors": 203, - "json-schema-traverse": 276, - "json-stable-stringify-without-jsonify": 243, - "json5": 311, - "jsonfile": 173, - "jsx-ast-utils": 412, - "keyv": 262, - "language-subtag-registry": 411, - "language-tags": 410, - "levn": 288, + "js-yaml": 212, + "jsbn": 153, + "json-buffer": 259, + "json-parse-even-better-errors": 199, + "json-schema-traverse": 272, + "json-stable-stringify-without-jsonify": 239, + "json5": 307, + "jsonfile": 174, + "jsx-ast-utils": 408, + "keyv": 258, + "language-subtag-registry": 407, + "language-tags": 406, + "levn": 284, "lilconfig": [ 11, 39, ], "lines-and-columns": 64, - "locate-path": 298, - "lodash.merge": 281, + "locate-path": 294, + "lodash.merge": 277, "loose-envify": 110, "lru-cache": [ 68, - 178, - 116, + 179, + 117, ], "merge2": 41, "micromatch": 32, "minimatch": [ 69, - 399, - 248, + 395, + 244, ], - "minimist": 310, + "minimist": 306, "minipass": 67, - "mitt": 200, - "ms": 154, + "mitt": 196, + "ms": 155, "mz": 59, "nanoid": 10, - "natural-compare": 279, - "netmask": 159, - "next": 223, - "node-fetch": 194, - "node-releases": 449, + "natural-compare": 275, + "netmask": 160, + "next": 219, + "node-releases": 445, "normalize-path": 25, - "normalize-range": 445, + "normalize-range": 441, "object-assign": 63, "object-hash": 26, - "object-inspect": 360, - "object-keys": 318, - "object.assign": 359, - "object.entries": 409, - "object.fromentries": 379, - "object.groupby": 328, - "object.hasown": 438, - "object.values": 314, - "once": 143, - "optionator": 286, - "p-limit": 300, - "p-locate": 299, - "pac-proxy-agent": 157, - "pac-resolver": 158, - "parent-module": 220, - "parse-json": 202, - "path-exists": 297, - "path-is-absolute": 258, + "object-inspect": 356, + "object-keys": 314, + "object.assign": 355, + "object.entries": 405, + "object.fromentries": 375, + "object.groupby": 324, + "object.hasown": 434, + "object.values": 310, + "once": 144, + "optionator": 282, + "p-limit": 296, + "p-locate": 295, + "pac-proxy-agent": 158, + "pac-resolver": 159, + "parent-module": 216, + "parse-json": 198, + "path-exists": 293, + "path-is-absolute": 254, "path-key": 98, "path-parse": 18, "path-scurry": 66, - "path-type": 403, - "pend": 187, + "path-type": 399, + "pend": 188, "picocolors": 9, "picomatch": 33, "pify": 23, "pirates": 58, - "possible-typed-array-names": 335, + "possible-typed-array-names": 331, "postcss": [ - 238, + 234, 7, ], "postcss-import": 15, @@ -11919,129 +11864,127 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "postcss-nested": 14, "postcss-selector-parser": 3, "postcss-value-parser": 24, - "prelude-ls": 290, - "progress": 179, - "prop-types": 436, - "proxy-agent": 146, - "proxy-from-env": 156, - "pump": 142, - "punycode": 275, + "prelude-ls": 286, + "progress": 180, + "prop-types": 432, + "proxy-agent": 147, + "proxy-from-env": 157, + "pump": 143, + "punycode": 271, "puppeteer": 113, - "puppeteer-core": 190, + "puppeteer-core": 191, "queue-microtask": 48, - "queue-tick": 139, + "queue-tick": 140, "react": 109, "react-dom": 108, - "react-is": 437, + "react-is": 433, "read-cache": 22, "readdirp": 52, - "reflect.getprototypeof": 415, - "regenerator-runtime": 432, - "regexp.prototype.flags": 356, - "require-directory": 121, + "reflect.getprototypeof": 411, + "regenerator-runtime": 428, + "regexp.prototype.flags": 352, + "require-directory": 122, "resolve": [ - 435, + 431, 16, ], - "resolve-from": 219, - "resolve-pkg-maps": 390, + "resolve-from": 215, + "resolve-pkg-maps": 386, "reusify": 45, - "rimraf": 256, + "rimraf": 252, "run-parallel": 47, - "safe-array-concat": 354, - "safe-regex-test": 352, + "safe-array-concat": 350, + "safe-regex-test": 348, "scheduler": 112, "semver": [ - 115, - 313, + 116, + 309, ], - "set-function-length": 327, - "set-function-name": 357, + "set-function-length": 323, + "set-function-name": 353, "shebang-command": 96, "shebang-regex": 97, - "side-channel": 367, + "side-channel": 363, "signal-exit": 92, - "slash": 401, - "smart-buffer": 149, - "socks": 148, - "socks-proxy-agent": 147, - "source-map": 163, + "slash": 397, + "smart-buffer": 150, + "socks": 149, + "socks-proxy-agent": 148, + "source-map": 164, "source-map-js": 8, - "sprintf-js": 151, - "streamsearch": 240, - "streamx": 135, + "sprintf-js": 152, + "streamsearch": 236, + "streamx": 136, "string-width": [ 87, 78, ], - "string.prototype.matchall": 434, - "string.prototype.trim": 351, - "string.prototype.trimend": 350, - "string.prototype.trimstart": 349, + "string.prototype.matchall": 430, + "string.prototype.trim": 347, + "string.prototype.trimend": 346, + "string.prototype.trimstart": 345, "strip-ansi": [ 85, 76, ], - "strip-bom": 309, - "strip-json-comments": 266, - "styled-jsx": 235, + "strip-bom": 305, + "strip-json-comments": 262, + "styled-jsx": 231, "sucrase": 56, "supports-color": [ - 304, - 209, + 300, + 205, ], "supports-preserve-symlinks-flag": 17, "tailwindcss": 2, - "tapable": 392, - "tar-fs": 130, - "tar-stream": 141, - "text-decoder": 137, - "text-table": 285, + "tapable": 388, + "tar-fs": 131, + "tar-stream": 142, + "text-decoder": 138, + "text-table": 281, "thenify": 61, "thenify-all": 60, - "through": 126, + "through": 127, "to-regex-range": 36, - "tr46": 197, - "ts-api-utils": 398, + "ts-api-utils": 394, "ts-interface-checker": 57, - "tsconfig-paths": 308, - "tslib": 167, - "type-check": 289, - "type-fest": 269, - "typed-array-buffer": 348, - "typed-array-byte-length": 347, - "typed-array-byte-offset": 346, - "typed-array-length": 344, + "tsconfig-paths": 304, + "tslib": 168, + "type-check": 285, + "type-fest": 265, + "typed-array-buffer": 344, + "typed-array-byte-length": 343, + "typed-array-byte-offset": 342, + "typed-array-length": 340, "typescript": 1, - "unbox-primitive": 336, - "unbzip2-stream": 125, - "undici-types": 183, - "universalify": 172, - "update-browserslist-db": 448, - "uri-js": 274, - "urlpattern-polyfill": 199, + "unbox-primitive": 332, + "unbzip2-stream": 126, + "undici-types": 184, + "universalify": 173, + "update-browserslist-db": 444, + "uri-js": 270, + "urlpattern-polyfill": 195, "util-deprecate": 4, - "webidl-conversions": 196, - "whatwg-url": 195, "which": 94, - "which-boxed-primitive": 337, - "which-builtin-type": 416, - "which-collection": 417, - "which-typed-array": 330, - "word-wrap": 291, + "which-boxed-primitive": 333, + "which-builtin-type": 412, + "which-collection": 413, + "which-typed-array": 326, + "word-wrap": 287, "wrap-ansi": [ 84, 75, ], - "wrappy": 144, - "ws": 191, - "y18n": 120, - "yallist": 117, + "wrappy": 145, + "ws": 192, + "y18n": 121, + "yallist": 118, "yaml": 12, - "yargs": 118, - "yargs-parser": 119, - "yauzl": 184, - "yocto-queue": 301, + "yargs": 119, + "yargs-parser": 120, + "yauzl": 185, + "yocto-queue": 297, + "zod": 194, }, "packages": [ { @@ -14108,17 +14051,34 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 153, 154, 155, + 156, ], "id": 113, - "integrity": "sha512-Mag1wRLanzwS4yEUyrDRBUgsKlH3dpL6oAfVwNHG09oxd0+ySsatMvYj7HwjynWy/S+Hg+XHLgjyC/F6CsL/lg==", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "man_dir": "", "name": "puppeteer", "name_hash": "13072297456933147981", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", + "tag": "npm", + "value": "22.12.0", + }, + "scripts": {}, + }, + { + "bin": null, + "dependencies": [], + "id": 114, + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==", + "man_dir": "", + "name": "devtools-protocol", + "name_hash": "12159960943916763407", + "origin": "npm", + "resolution": { + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", "tag": "npm", - "value": "22.4.1", + "value": "0.0.1299070", }, "scripts": {}, }, @@ -14128,7 +14088,6 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "browsers", }, "dependencies": [ - 156, 157, 158, 159, @@ -14136,17 +14095,18 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 161, 162, 163, + 164, ], - "id": 114, - "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "id": 115, + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "man_dir": "", "name": "@puppeteer/browsers", "name_hash": "6318517029770692415", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", "tag": "npm", - "value": "2.1.0", + "value": "2.2.3", }, "scripts": {}, }, @@ -14156,9 +14116,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "semver", }, "dependencies": [ - 164, + 165, ], - "id": 115, + "id": 116, "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "man_dir": "", "name": "semver", @@ -14174,9 +14134,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 165, + 166, ], - "id": 116, + "id": 117, "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "man_dir": "", "name": "lru-cache", @@ -14192,7 +14152,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 117, + "id": 118, "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "man_dir": "", "name": "yallist", @@ -14208,15 +14168,15 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 166, 167, 168, 169, 170, 171, 172, + 173, ], - "id": 118, + "id": 119, "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "man_dir": "", "name": "yargs", @@ -14232,7 +14192,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 119, + "id": 120, "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "man_dir": "", "name": "yargs-parser", @@ -14248,7 +14208,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 120, + "id": 121, "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "man_dir": "", "name": "y18n", @@ -14264,7 +14224,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 121, + "id": 122, "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "man_dir": "", "name": "require-directory", @@ -14280,7 +14240,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 122, + "id": 123, "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "man_dir": "", "name": "get-caller-file", @@ -14296,7 +14256,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 123, + "id": 124, "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "man_dir": "", "name": "escalade", @@ -14312,11 +14272,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 173, 174, 175, + 176, ], - "id": 124, + "id": 125, "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "man_dir": "", "name": "cliui", @@ -14332,10 +14292,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 176, 177, + 178, ], - "id": 125, + "id": 126, "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "man_dir": "", "name": "unbzip2-stream", @@ -14351,7 +14311,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 126, + "id": 127, "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "man_dir": "", "name": "through", @@ -14367,10 +14327,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 178, 179, + 180, ], - "id": 127, + "id": 128, "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "man_dir": "", "name": "buffer", @@ -14386,7 +14346,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 128, + "id": 129, "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "man_dir": "", "name": "ieee754", @@ -14402,7 +14362,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 129, + "id": 130, "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "man_dir": "", "name": "base64-js", @@ -14418,12 +14378,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 180, 181, 182, 183, + 184, ], - "id": 130, + "id": 131, "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "man_dir": "", "name": "tar-fs", @@ -14439,9 +14399,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 184, + 185, ], - "id": 131, + "id": 132, "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "man_dir": "", "name": "bare-path", @@ -14457,7 +14417,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 132, + "id": 133, "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", "man_dir": "", "name": "bare-os", @@ -14473,11 +14433,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 185, 186, 187, + 188, ], - "id": 133, + "id": 134, "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", "man_dir": "", "name": "bare-fs", @@ -14493,9 +14453,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 188, + 189, ], - "id": 134, + "id": 135, "integrity": "sha512-ubLyoDqPnUf5o0kSFp709HC0WRZuxVuh4pbte5eY95Xvx5bdvz07c2JFmXBfqqe60q+9PJ8S4X5GRvmcNSKMxg==", "man_dir": "", "name": "bare-stream", @@ -14511,12 +14471,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 189, 190, 191, 192, + 193, ], - "id": 135, + "id": 136, "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", "man_dir": "", "name": "streamx", @@ -14532,7 +14492,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 136, + "id": 137, "integrity": "sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA==", "man_dir": "", "name": "bare-events", @@ -14548,9 +14508,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 193, + 194, ], - "id": 137, + "id": 138, "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", "man_dir": "", "name": "text-decoder", @@ -14566,7 +14526,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 138, + "id": 139, "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "man_dir": "", "name": "b4a", @@ -14582,7 +14542,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 139, + "id": 140, "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "man_dir": "", "name": "queue-tick", @@ -14598,7 +14558,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 140, + "id": 141, "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "man_dir": "", "name": "fast-fifo", @@ -14614,11 +14574,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 194, 195, 196, + 197, ], - "id": 141, + "id": 142, "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "man_dir": "", "name": "tar-stream", @@ -14634,10 +14594,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 197, 198, + 199, ], - "id": 142, + "id": 143, "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "man_dir": "", "name": "pump", @@ -14653,9 +14613,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 199, + 200, ], - "id": 143, + "id": 144, "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "man_dir": "", "name": "once", @@ -14671,7 +14631,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 144, + "id": 145, "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "man_dir": "", "name": "wrappy", @@ -14687,9 +14647,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 200, + 201, ], - "id": 145, + "id": 146, "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "man_dir": "", "name": "end-of-stream", @@ -14705,7 +14665,6 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 201, 202, 203, 204, @@ -14713,8 +14672,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 206, 207, 208, + 209, ], - "id": 146, + "id": 147, "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "man_dir": "", "name": "proxy-agent", @@ -14730,11 +14690,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 209, 210, 211, + 212, ], - "id": 147, + "id": 148, "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "man_dir": "", "name": "socks-proxy-agent", @@ -14750,10 +14710,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 212, 213, + 214, ], - "id": 148, + "id": 149, "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "man_dir": "", "name": "socks", @@ -14769,7 +14729,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 149, + "id": 150, "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "man_dir": "", "name": "smart-buffer", @@ -14785,10 +14745,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 214, 215, + 216, ], - "id": 150, + "id": 151, "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "man_dir": "", "name": "ip-address", @@ -14804,7 +14764,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 151, + "id": 152, "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "man_dir": "", "name": "sprintf-js", @@ -14820,7 +14780,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 152, + "id": 153, "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "man_dir": "", "name": "jsbn", @@ -14836,9 +14796,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 216, + 217, ], - "id": 153, + "id": 154, "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "man_dir": "", "name": "debug", @@ -14854,7 +14814,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 154, + "id": 155, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "man_dir": "", "name": "ms", @@ -14870,9 +14830,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 217, + 218, ], - "id": 155, + "id": 156, "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "man_dir": "", "name": "agent-base", @@ -14888,7 +14848,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 156, + "id": 157, "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "man_dir": "", "name": "proxy-from-env", @@ -14904,7 +14864,6 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 218, 219, 220, 221, @@ -14912,8 +14871,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 223, 224, 225, + 226, ], - "id": 157, + "id": 158, "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "man_dir": "", "name": "pac-proxy-agent", @@ -14929,10 +14889,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 226, 227, + 228, ], - "id": 158, + "id": 159, "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "man_dir": "", "name": "pac-resolver", @@ -14948,7 +14908,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 159, + "id": 160, "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "man_dir": "", "name": "netmask", @@ -14964,11 +14924,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 228, 229, 230, + 231, ], - "id": 160, + "id": 161, "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "man_dir": "", "name": "degenerator", @@ -14987,7 +14947,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "esvalidate": "./bin/esvalidate.js", }, "dependencies": [], - "id": 161, + "id": 162, "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "man_dir": "", "name": "esprima", @@ -15006,12 +14966,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "esgenerate": "bin/esgenerate.js", }, "dependencies": [ - 231, 232, 233, 234, + 235, ], - "id": 162, + "id": 163, "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "man_dir": "", "name": "escodegen", @@ -15027,7 +14987,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 163, + "id": 164, "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "man_dir": "", "name": "source-map", @@ -15043,7 +15003,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 164, + "id": 165, "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "man_dir": "", "name": "esutils", @@ -15059,7 +15019,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 165, + "id": 166, "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "man_dir": "", "name": "estraverse", @@ -15075,9 +15035,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 235, + 236, ], - "id": 166, + "id": 167, "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "man_dir": "", "name": "ast-types", @@ -15093,7 +15053,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 167, + "id": 168, "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "man_dir": "", "name": "tslib", @@ -15109,10 +15069,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 236, 237, + 238, ], - "id": 168, + "id": 169, "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "man_dir": "", "name": "https-proxy-agent", @@ -15128,10 +15088,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 238, 239, + 240, ], - "id": 169, + "id": 170, "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "man_dir": "", "name": "http-proxy-agent", @@ -15147,12 +15107,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 240, 241, 242, 243, + 244, ], - "id": 170, + "id": 171, "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "man_dir": "", "name": "get-uri", @@ -15168,11 +15128,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 244, 245, 246, + 247, ], - "id": 171, + "id": 172, "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "man_dir": "", "name": "fs-extra", @@ -15188,7 +15148,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 172, + "id": 173, "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "man_dir": "", "name": "universalify", @@ -15204,10 +15164,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 247, 248, + 249, ], - "id": 173, + "id": 174, "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "man_dir": "", "name": "jsonfile", @@ -15223,7 +15183,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 174, + "id": 175, "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "man_dir": "", "name": "graceful-fs", @@ -15239,7 +15199,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 175, + "id": 176, "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "man_dir": "", "name": "data-uri-to-buffer", @@ -15255,7 +15215,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 176, + "id": 177, "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "man_dir": "", "name": "basic-ftp", @@ -15271,7 +15231,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 177, + "id": 178, "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "man_dir": "", "name": "@tootallnate/quickjs-emscripten", @@ -15287,7 +15247,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 178, + "id": 179, "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "man_dir": "", "name": "lru-cache", @@ -15303,7 +15263,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 179, + "id": 180, "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "man_dir": "", "name": "progress", @@ -15322,12 +15282,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "extract-zip", }, "dependencies": [ - 249, 250, 251, 252, + 253, ], - "id": 180, + "id": 181, "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "man_dir": "", "name": "extract-zip", @@ -15343,9 +15303,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 253, + 254, ], - "id": 181, + "id": 182, "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "man_dir": "", "name": "@types/yauzl", @@ -15361,9 +15321,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 254, + 255, ], - "id": 182, + "id": 183, "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", "man_dir": "", "name": "@types/node", @@ -15379,7 +15339,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 183, + "id": 184, "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "man_dir": "", "name": "undici-types", @@ -15395,10 +15355,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 255, 256, + 257, ], - "id": 184, + "id": 185, "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "man_dir": "", "name": "yauzl", @@ -15414,7 +15374,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 185, + "id": 186, "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "man_dir": "", "name": "buffer-crc32", @@ -15430,9 +15390,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 257, + 258, ], - "id": 186, + "id": 187, "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "man_dir": "", "name": "fd-slicer", @@ -15448,7 +15408,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 187, + "id": 188, "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "man_dir": "", "name": "pend", @@ -15464,9 +15424,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 258, + 259, ], - "id": 188, + "id": 189, "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "man_dir": "", "name": "get-stream", @@ -15482,9 +15442,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 259, + 260, ], - "id": 189, + "id": 190, "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "man_dir": "", "name": "debug", @@ -15500,23 +15460,22 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 260, 261, 262, 263, 264, 265, ], - "id": 190, - "integrity": "sha512-l9nf8NcirYOHdID12CIMWyy7dqcJCVtgVS+YAiJuUJHg8+9yjgPiG2PcNhojIEEpCkvw3FxvnyITVfKVmkWpjA==", + "id": 191, + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", "man_dir": "", "name": "puppeteer-core", "name_hash": "10954685796294859150", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", "tag": "npm", - "value": "22.4.1", + "value": "22.12.0", }, "scripts": {}, }, @@ -15526,32 +15485,16 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 266, 267, ], - "id": 191, - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "id": 192, + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "man_dir": "", "name": "ws", "name_hash": "14644737011329074183", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "tag": "npm", - "value": "8.16.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 192, - "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", - "man_dir": "", - "name": "devtools-protocol", - "name_hash": "12159960943916763407", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "tag": "npm", - "value": "0.0.1249869", + "value": "8.17.1", }, "scripts": {}, }, @@ -15559,114 +15502,43 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "bin": null, "dependencies": [ 268, - ], - "id": 193, - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "man_dir": "", - "name": "cross-fetch", - "name_hash": "5665307032371542913", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "tag": "npm", - "value": "4.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 269, 270, - ], - "id": 194, - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "man_dir": "", - "name": "node-fetch", - "name_hash": "9368364337257117328", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "tag": "npm", - "value": "2.7.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 271, - 272, ], - "id": 195, - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "man_dir": "", - "name": "whatwg-url", - "name_hash": "15436316526856444177", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "tag": "npm", - "value": "5.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 196, - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "id": 193, + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "man_dir": "", - "name": "webidl-conversions", - "name_hash": "5343883202058398372", + "name": "chromium-bidi", + "name_hash": "17738832193826713561", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", "tag": "npm", - "value": "3.0.1", + "value": "0.5.24", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 197, - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "man_dir": "", - "name": "tr46", - "name_hash": "4865213169840252474", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "tag": "npm", - "value": "0.0.3", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ - 273, - 274, - 275, - ], - "id": 198, - "integrity": "sha512-sZMgEBWKbupD0Q7lyFu8AWkrE+rs5ycE12jFkGwIgD/VS8lDPtelPlXM7LYaq4zrkZ/O2L3f4afHUHL0ICdKog==", + "id": 194, + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "man_dir": "", - "name": "chromium-bidi", - "name_hash": "17738832193826713561", + "name": "zod", + "name_hash": "13942938047053248045", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.12.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "tag": "npm", - "value": "0.5.12", + "value": "3.23.8", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 199, + "id": 195, "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "man_dir": "", "name": "urlpattern-polyfill", @@ -15682,7 +15554,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 200, + "id": 196, "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "man_dir": "", "name": "mitt", @@ -15698,13 +15570,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 272, + 273, + 274, + 275, 276, - 277, - 278, - 279, - 280, ], - "id": 201, + "id": 197, "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "man_dir": "", "name": "cosmiconfig", @@ -15720,12 +15592,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 281, - 282, - 283, - 284, + 277, + 278, + 279, + 280, ], - "id": 202, + "id": 198, "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "man_dir": "", "name": "parse-json", @@ -15741,7 +15613,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 203, + "id": 199, "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "man_dir": "", "name": "json-parse-even-better-errors", @@ -15757,9 +15629,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 285, + 281, ], - "id": 204, + "id": 200, "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "man_dir": "", "name": "error-ex", @@ -15775,7 +15647,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 205, + "id": 201, "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "man_dir": "", "name": "is-arrayish", @@ -15791,10 +15663,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 286, - 287, + 282, + 283, ], - "id": 206, + "id": 202, "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "man_dir": "", "name": "@babel/code-frame", @@ -15810,12 +15682,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 288, - 289, - 290, - 291, + 284, + 285, + 286, + 287, ], - "id": 207, + "id": 203, "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "man_dir": "", "name": "@babel/highlight", @@ -15831,11 +15703,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 292, - 293, - 294, + 288, + 289, + 290, ], - "id": 208, + "id": 204, "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "man_dir": "", "name": "chalk", @@ -15851,9 +15723,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 295, + 291, ], - "id": 209, + "id": 205, "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "man_dir": "", "name": "supports-color", @@ -15869,7 +15741,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 210, + "id": 206, "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "man_dir": "", "name": "has-flag", @@ -15885,7 +15757,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 211, + "id": 207, "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "man_dir": "", "name": "escape-string-regexp", @@ -15901,9 +15773,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 296, + 292, ], - "id": 212, + "id": 208, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "man_dir": "", "name": "ansi-styles", @@ -15919,9 +15791,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 297, + 293, ], - "id": 213, + "id": 209, "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "man_dir": "", "name": "color-convert", @@ -15937,7 +15809,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 214, + "id": 210, "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "man_dir": "", "name": "color-name", @@ -15953,7 +15825,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 215, + "id": 211, "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "man_dir": "", "name": "@babel/helper-validator-identifier", @@ -15972,9 +15844,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "js-yaml", }, "dependencies": [ - 298, + 294, ], - "id": 216, + "id": 212, "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "man_dir": "", "name": "js-yaml", @@ -15990,7 +15862,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 217, + "id": 213, "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "man_dir": "", "name": "argparse", @@ -16006,10 +15878,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 299, - 300, + 295, + 296, ], - "id": 218, + "id": 214, "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "man_dir": "", "name": "import-fresh", @@ -16025,7 +15897,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 219, + "id": 215, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "man_dir": "", "name": "resolve-from", @@ -16041,9 +15913,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 301, + 297, ], - "id": 220, + "id": 216, "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "man_dir": "", "name": "parent-module", @@ -16059,7 +15931,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 221, + "id": 217, "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "man_dir": "", "name": "callsites", @@ -16075,7 +15947,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 222, + "id": 218, "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "man_dir": "", "name": "env-paths", @@ -16094,6 +15966,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "next", }, "dependencies": [ + 298, + 299, + 300, + 301, 302, 303, 304, @@ -16110,12 +15986,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 315, 316, 317, - 318, - 319, - 320, - 321, ], - "id": 223, + "id": 219, "integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==", "man_dir": "", "name": "next", @@ -16134,7 +16006,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 224, + "id": 220, "integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==", "man_dir": "", "name": "@next/swc-win32-arm64-msvc", @@ -16156,7 +16028,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 225, + "id": 221, "integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==", "man_dir": "", "name": "@next/swc-linux-arm64-musl", @@ -16178,7 +16050,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 226, + "id": 222, "integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==", "man_dir": "", "name": "@next/swc-win32-ia32-msvc", @@ -16200,7 +16072,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 227, + "id": 223, "integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==", "man_dir": "", "name": "@next/swc-linux-arm64-gnu", @@ -16222,7 +16094,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 228, + "id": 224, "integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==", "man_dir": "", "name": "@next/swc-win32-x64-msvc", @@ -16244,7 +16116,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 229, + "id": 225, "integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==", "man_dir": "", "name": "@next/swc-linux-x64-musl", @@ -16266,7 +16138,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 230, + "id": 226, "integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==", "man_dir": "", "name": "@next/swc-linux-x64-gnu", @@ -16288,7 +16160,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 231, + "id": 227, "integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==", "man_dir": "", "name": "@next/swc-darwin-arm64", @@ -16310,7 +16182,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` ], "bin": null, "dependencies": [], - "id": 232, + "id": 228, "integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==", "man_dir": "", "name": "@next/swc-darwin-x64", @@ -16329,7 +16201,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 233, + "id": 229, "integrity": "sha512-S3BnR4Kh26TBxbi5t5kpbcUlLJb9lhtDXISDPwOfI+JoC+ik0QksvkZtUVyikw3hjnkgkMPSJ8oIM9yMm9vflA==", "man_dir": "", "name": "caniuse-lite", @@ -16345,9 +16217,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 322, + 318, ], - "id": 234, + "id": 230, "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "man_dir": "", "name": "@swc/helpers", @@ -16363,10 +16235,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 323, - 324, + 319, + 320, ], - "id": 235, + "id": 231, "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "man_dir": "", "name": "styled-jsx", @@ -16382,7 +16254,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 236, + "id": 232, "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "man_dir": "", "name": "client-only", @@ -16398,7 +16270,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 237, + "id": 233, "integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==", "man_dir": "", "name": "@next/env", @@ -16414,11 +16286,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 325, - 326, - 327, + 321, + 322, + 323, ], - "id": 238, + "id": 234, "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "man_dir": "", "name": "postcss", @@ -16434,9 +16306,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 328, + 324, ], - "id": 239, + "id": 235, "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "man_dir": "", "name": "busboy", @@ -16452,7 +16324,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 240, + "id": 236, "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "man_dir": "", "name": "streamsearch", @@ -16468,6 +16340,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 325, + 326, + 327, + 328, 329, 330, 331, @@ -16475,12 +16351,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 333, 334, 335, - 336, - 337, - 338, - 339, ], - "id": 241, + "id": 237, "integrity": "sha512-sUCpWlGuHpEhI0pIT0UtdSLJk5Z8E2DYinPTwsBiWaSYQomchdl0i60pjynY48+oXvtyWMQ7oE+G3m49yrfacg==", "man_dir": "", "name": "eslint-config-next", @@ -16499,6 +16371,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "eslint", }, "dependencies": [ + 336, + 337, + 338, + 339, 340, 341, 342, @@ -16532,12 +16408,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 370, 371, 372, - 373, - 374, - 375, - 376, ], - "id": 242, + "id": 238, "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "man_dir": "", "name": "eslint", @@ -16553,7 +16425,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 243, + "id": 239, "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "man_dir": "", "name": "json-stable-stringify-without-jsonify", @@ -16569,7 +16441,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 244, + "id": 240, "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "man_dir": "", "name": "@humanwhocodes/module-importer", @@ -16585,10 +16457,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 377, - 378, + 373, + 374, ], - "id": 245, + "id": 241, "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "man_dir": "", "name": "@eslint-community/eslint-utils", @@ -16604,7 +16476,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 246, + "id": 242, "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "man_dir": "", "name": "eslint-visitor-keys", @@ -16620,11 +16492,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 379, - 380, - 381, + 375, + 376, + 377, ], - "id": 247, + "id": 243, "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "man_dir": "", "name": "@humanwhocodes/config-array", @@ -16640,9 +16512,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 382, + 378, ], - "id": 248, + "id": 244, "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "man_dir": "", "name": "minimatch", @@ -16658,10 +16530,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 383, - 384, + 379, + 380, ], - "id": 249, + "id": 245, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "man_dir": "", "name": "brace-expansion", @@ -16677,7 +16549,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 250, + "id": 246, "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "man_dir": "", "name": "concat-map", @@ -16693,7 +16565,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 251, + "id": 247, "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "man_dir": "", "name": "@humanwhocodes/object-schema", @@ -16709,7 +16581,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 252, + "id": 248, "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "man_dir": "", "name": "@eslint-community/regexpp", @@ -16725,7 +16597,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 253, + "id": 249, "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "man_dir": "", "name": "escape-string-regexp", @@ -16741,9 +16613,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 385, + 381, ], - "id": 254, + "id": 250, "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "man_dir": "", "name": "file-entry-cache", @@ -16759,11 +16631,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 386, - 387, - 388, + 382, + 383, + 384, ], - "id": 255, + "id": 251, "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "man_dir": "", "name": "flat-cache", @@ -16782,9 +16654,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "rimraf", }, "dependencies": [ - 389, + 385, ], - "id": 256, + "id": 252, "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "man_dir": "", "name": "rimraf", @@ -16800,14 +16672,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 386, + 387, + 388, + 389, 390, 391, - 392, - 393, - 394, - 395, ], - "id": 257, + "id": 253, "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "man_dir": "", "name": "glob", @@ -16823,7 +16695,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 258, + "id": 254, "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "man_dir": "", "name": "path-is-absolute", @@ -16839,7 +16711,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 259, + "id": 255, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "man_dir": "", "name": "inherits", @@ -16855,10 +16727,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 396, - 397, + 392, + 393, ], - "id": 260, + "id": 256, "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "man_dir": "", "name": "inflight", @@ -16874,7 +16746,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 261, + "id": 257, "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "man_dir": "", "name": "fs.realpath", @@ -16890,9 +16762,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 398, + 394, ], - "id": 262, + "id": 258, "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "man_dir": "", "name": "keyv", @@ -16908,7 +16780,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 263, + "id": 259, "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "man_dir": "", "name": "json-buffer", @@ -16924,7 +16796,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 264, + "id": 260, "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "man_dir": "", "name": "flatted", @@ -16940,17 +16812,17 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 395, + 396, + 397, + 398, 399, 400, 401, 402, 403, - 404, - 405, - 406, - 407, ], - "id": 265, + "id": 261, "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "man_dir": "", "name": "@eslint/eslintrc", @@ -16966,7 +16838,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 266, + "id": 262, "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "man_dir": "", "name": "strip-json-comments", @@ -16982,7 +16854,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 267, + "id": 263, "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "man_dir": "", "name": "ignore", @@ -16998,9 +16870,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 408, + 404, ], - "id": 268, + "id": 264, "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "man_dir": "", "name": "globals", @@ -17016,7 +16888,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 269, + "id": 265, "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "man_dir": "", "name": "type-fest", @@ -17032,11 +16904,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 409, - 410, - 411, + 405, + 406, + 407, ], - "id": 270, + "id": 266, "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "man_dir": "", "name": "espree", @@ -17052,9 +16924,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 412, + 408, ], - "id": 271, + "id": 267, "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "man_dir": "", "name": "acorn-jsx", @@ -17073,7 +16945,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "acorn", }, "dependencies": [], - "id": 272, + "id": 268, "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "man_dir": "", "name": "acorn", @@ -17089,12 +16961,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 413, - 414, - 415, - 416, + 409, + 410, + 411, + 412, ], - "id": 273, + "id": 269, "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "man_dir": "", "name": "ajv", @@ -17110,9 +16982,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 417, + 413, ], - "id": 274, + "id": 270, "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "man_dir": "", "name": "uri-js", @@ -17128,7 +17000,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 275, + "id": 271, "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "man_dir": "", "name": "punycode", @@ -17144,7 +17016,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 276, + "id": 272, "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "man_dir": "", "name": "json-schema-traverse", @@ -17160,7 +17032,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 277, + "id": 273, "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "man_dir": "", "name": "fast-json-stable-stringify", @@ -17176,7 +17048,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 278, + "id": 274, "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "man_dir": "", "name": "fast-deep-equal", @@ -17192,7 +17064,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 279, + "id": 275, "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "man_dir": "", "name": "natural-compare", @@ -17208,7 +17080,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 280, + "id": 276, "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "man_dir": "", "name": "is-path-inside", @@ -17224,7 +17096,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 281, + "id": 277, "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "man_dir": "", "name": "lodash.merge", @@ -17240,10 +17112,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 418, - 419, + 414, + 415, ], - "id": 282, + "id": 278, "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "man_dir": "", "name": "eslint-scope", @@ -17259,9 +17131,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 420, + 416, ], - "id": 283, + "id": 279, "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "man_dir": "", "name": "esrecurse", @@ -17277,7 +17149,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 284, + "id": 280, "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "man_dir": "", "name": "imurmurhash", @@ -17293,7 +17165,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 285, + "id": 281, "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "man_dir": "", "name": "text-table", @@ -17309,14 +17181,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 417, + 418, + 419, + 420, 421, 422, - 423, - 424, - 425, - 426, ], - "id": 286, + "id": 282, "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "man_dir": "", "name": "optionator", @@ -17332,7 +17204,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 287, + "id": 283, "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "man_dir": "", "name": "fast-levenshtein", @@ -17348,10 +17220,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 427, - 428, + 423, + 424, ], - "id": 288, + "id": 284, "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "man_dir": "", "name": "levn", @@ -17367,9 +17239,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 429, + 425, ], - "id": 289, + "id": 285, "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "man_dir": "", "name": "type-check", @@ -17385,7 +17257,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 290, + "id": 286, "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "man_dir": "", "name": "prelude-ls", @@ -17401,7 +17273,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 291, + "id": 287, "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "man_dir": "", "name": "word-wrap", @@ -17417,7 +17289,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 292, + "id": 288, "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "man_dir": "", "name": "deep-is", @@ -17433,7 +17305,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 293, + "id": 289, "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "man_dir": "", "name": "@eslint/js", @@ -17449,7 +17321,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 294, + "id": 290, "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "man_dir": "", "name": "graphemer", @@ -17465,9 +17337,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 430, + 426, ], - "id": 295, + "id": 291, "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "man_dir": "", "name": "doctrine", @@ -17483,10 +17355,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 431, - 432, + 427, + 428, ], - "id": 296, + "id": 292, "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "man_dir": "", "name": "find-up", @@ -17502,7 +17374,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 297, + "id": 293, "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "man_dir": "", "name": "path-exists", @@ -17518,9 +17390,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 433, + 429, ], - "id": 298, + "id": 294, "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "man_dir": "", "name": "locate-path", @@ -17536,9 +17408,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 434, + 430, ], - "id": 299, + "id": 295, "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "man_dir": "", "name": "p-locate", @@ -17554,9 +17426,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 435, + 431, ], - "id": 300, + "id": 296, "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "man_dir": "", "name": "p-limit", @@ -17572,7 +17444,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 301, + "id": 297, "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "man_dir": "", "name": "yocto-queue", @@ -17588,9 +17460,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 436, + 432, ], - "id": 302, + "id": 298, "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "man_dir": "", "name": "esquery", @@ -17606,10 +17478,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 437, - 438, + 433, + 434, ], - "id": 303, + "id": 299, "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "man_dir": "", "name": "chalk", @@ -17625,9 +17497,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 439, + 435, ], - "id": 304, + "id": 300, "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "man_dir": "", "name": "supports-color", @@ -17643,7 +17515,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 305, + "id": 301, "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "man_dir": "", "name": "has-flag", @@ -17659,17 +17531,17 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 436, + 437, + 438, + 439, 440, 441, 442, 443, 444, - 445, - 446, - 447, - 448, ], - "id": 306, + "id": 302, "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", "man_dir": "", "name": "eslint-import-resolver-typescript", @@ -17685,6 +17557,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 445, + 446, + 447, + 448, 449, 450, 451, @@ -17699,12 +17575,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 460, 461, 462, - 463, - 464, - 465, - 466, ], - "id": 307, + "id": 303, "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "man_dir": "", "name": "eslint-plugin-import", @@ -17720,12 +17592,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 467, - 468, - 469, - 470, + 463, + 464, + 465, + 466, ], - "id": 308, + "id": 304, "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "man_dir": "", "name": "tsconfig-paths", @@ -17741,7 +17613,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 309, + "id": 305, "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "man_dir": "", "name": "strip-bom", @@ -17757,7 +17629,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 310, + "id": 306, "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "man_dir": "", "name": "minimist", @@ -17776,9 +17648,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "json5", }, "dependencies": [ - 471, + 467, ], - "id": 311, + "id": 307, "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "man_dir": "", "name": "json5", @@ -17794,7 +17666,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 312, + "id": 308, "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "man_dir": "", "name": "@types/json5", @@ -17813,7 +17685,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "semver", }, "dependencies": [], - "id": 313, + "id": 309, "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "man_dir": "", "name": "semver", @@ -17829,11 +17701,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 472, - 473, - 474, + 468, + 469, + 470, ], - "id": 314, + "id": 310, "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "man_dir": "", "name": "object.values", @@ -17849,9 +17721,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 475, + 471, ], - "id": 315, + "id": 311, "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "man_dir": "", "name": "es-object-atoms", @@ -17867,7 +17739,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 316, + "id": 312, "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "man_dir": "", "name": "es-errors", @@ -17883,11 +17755,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 476, - 477, - 478, + 472, + 473, + 474, ], - "id": 317, + "id": 313, "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "man_dir": "", "name": "define-properties", @@ -17903,7 +17775,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 318, + "id": 314, "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "man_dir": "", "name": "object-keys", @@ -17919,9 +17791,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 479, + 475, ], - "id": 319, + "id": 315, "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "man_dir": "", "name": "has-property-descriptors", @@ -17937,9 +17809,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 480, + 476, ], - "id": 320, + "id": 316, "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "man_dir": "", "name": "es-define-property", @@ -17955,13 +17827,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 477, + 478, + 479, + 480, 481, - 482, - 483, - 484, - 485, ], - "id": 321, + "id": 317, "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "man_dir": "", "name": "get-intrinsic", @@ -17977,7 +17849,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 322, + "id": 318, "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "man_dir": "", "name": "has-symbols", @@ -17993,7 +17865,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 323, + "id": 319, "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "man_dir": "", "name": "has-proto", @@ -18009,11 +17881,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 486, - 487, - 488, + 482, + 483, + 484, ], - "id": 324, + "id": 320, "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "man_dir": "", "name": "define-data-property", @@ -18029,9 +17901,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 489, + 485, ], - "id": 325, + "id": 321, "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "man_dir": "", "name": "gopd", @@ -18047,13 +17919,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 486, + 487, + 488, + 489, 490, - 491, - 492, - 493, - 494, ], - "id": 326, + "id": 322, "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "man_dir": "", "name": "call-bind", @@ -18069,14 +17941,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 491, + 492, + 493, + 494, 495, 496, - 497, - 498, - 499, - 500, ], - "id": 327, + "id": 323, "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "man_dir": "", "name": "set-function-length", @@ -18092,11 +17964,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 501, - 502, - 503, + 497, + 498, + 499, ], - "id": 328, + "id": 324, "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "man_dir": "", "name": "object.groupby", @@ -18112,6 +17984,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 500, + 501, + 502, + 503, 504, 505, 506, @@ -18154,12 +18030,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 543, 544, 545, - 546, - 547, - 548, - 549, ], - "id": 329, + "id": 325, "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "man_dir": "", "name": "es-abstract", @@ -18175,13 +18047,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 546, + 547, + 548, + 549, 550, - 551, - 552, - 553, - 554, ], - "id": 330, + "id": 326, "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "man_dir": "", "name": "which-typed-array", @@ -18197,9 +18069,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 555, + 551, ], - "id": 331, + "id": 327, "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "man_dir": "", "name": "has-tostringtag", @@ -18215,9 +18087,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 556, + 552, ], - "id": 332, + "id": 328, "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "man_dir": "", "name": "for-each", @@ -18233,7 +18105,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 333, + "id": 329, "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "man_dir": "", "name": "is-callable", @@ -18249,9 +18121,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 557, + 553, ], - "id": 334, + "id": 330, "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "man_dir": "", "name": "available-typed-arrays", @@ -18267,7 +18139,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 335, + "id": 331, "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "man_dir": "", "name": "possible-typed-array-names", @@ -18283,12 +18155,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 558, - 559, - 560, - 561, + 554, + 555, + 556, + 557, ], - "id": 336, + "id": 332, "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "man_dir": "", "name": "unbox-primitive", @@ -18304,13 +18176,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 558, + 559, + 560, + 561, 562, - 563, - 564, - 565, - 566, ], - "id": 337, + "id": 333, "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "man_dir": "", "name": "which-boxed-primitive", @@ -18326,9 +18198,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 567, + 563, ], - "id": 338, + "id": 334, "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "man_dir": "", "name": "is-symbol", @@ -18344,9 +18216,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 568, + 564, ], - "id": 339, + "id": 335, "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "man_dir": "", "name": "is-string", @@ -18362,9 +18234,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 569, + 565, ], - "id": 340, + "id": 336, "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "man_dir": "", "name": "is-number-object", @@ -18380,10 +18252,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 570, - 571, + 566, + 567, ], - "id": 341, + "id": 337, "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "man_dir": "", "name": "is-boolean-object", @@ -18399,9 +18271,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 572, + 568, ], - "id": 342, + "id": 338, "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "man_dir": "", "name": "is-bigint", @@ -18417,7 +18289,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 343, + "id": 339, "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "man_dir": "", "name": "has-bigints", @@ -18433,14 +18305,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 569, + 570, + 571, + 572, 573, 574, - 575, - 576, - 577, - 578, ], - "id": 344, + "id": 340, "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "man_dir": "", "name": "typed-array-length", @@ -18456,9 +18328,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 579, + 575, ], - "id": 345, + "id": 341, "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "man_dir": "", "name": "is-typed-array", @@ -18474,14 +18346,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 576, + 577, + 578, + 579, 580, 581, - 582, - 583, - 584, - 585, ], - "id": 346, + "id": 342, "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "man_dir": "", "name": "typed-array-byte-offset", @@ -18497,13 +18369,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 582, + 583, + 584, + 585, 586, - 587, - 588, - 589, - 590, ], - "id": 347, + "id": 343, "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "man_dir": "", "name": "typed-array-byte-length", @@ -18519,11 +18391,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 591, - 592, - 593, + 587, + 588, + 589, ], - "id": 348, + "id": 344, "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "man_dir": "", "name": "typed-array-buffer", @@ -18539,11 +18411,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 594, - 595, - 596, + 590, + 591, + 592, ], - "id": 349, + "id": 345, "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "man_dir": "", "name": "string.prototype.trimstart", @@ -18559,11 +18431,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 597, - 598, - 599, + 593, + 594, + 595, ], - "id": 350, + "id": 346, "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "man_dir": "", "name": "string.prototype.trimend", @@ -18579,12 +18451,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 600, - 601, - 602, - 603, + 596, + 597, + 598, + 599, ], - "id": 351, + "id": 347, "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "man_dir": "", "name": "string.prototype.trim", @@ -18600,11 +18472,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 604, - 605, - 606, + 600, + 601, + 602, ], - "id": 352, + "id": 348, "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "man_dir": "", "name": "safe-regex-test", @@ -18620,10 +18492,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 607, - 608, + 603, + 604, ], - "id": 353, + "id": 349, "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "man_dir": "", "name": "is-regex", @@ -18639,12 +18511,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 609, - 610, - 611, - 612, + 605, + 606, + 607, + 608, ], - "id": 354, + "id": 350, "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "man_dir": "", "name": "safe-array-concat", @@ -18660,7 +18532,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 355, + "id": 351, "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "man_dir": "", "name": "isarray", @@ -18676,12 +18548,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 613, - 614, - 615, - 616, + 609, + 610, + 611, + 612, ], - "id": 356, + "id": 352, "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "man_dir": "", "name": "regexp.prototype.flags", @@ -18697,12 +18569,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 617, - 618, - 619, - 620, + 613, + 614, + 615, + 616, ], - "id": 357, + "id": 353, "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "man_dir": "", "name": "set-function-name", @@ -18718,7 +18590,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 358, + "id": 354, "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "man_dir": "", "name": "functions-have-names", @@ -18734,12 +18606,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 621, - 622, - 623, - 624, + 617, + 618, + 619, + 620, ], - "id": 359, + "id": 355, "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "man_dir": "", "name": "object.assign", @@ -18755,7 +18627,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 360, + "id": 356, "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "man_dir": "", "name": "object-inspect", @@ -18771,9 +18643,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 625, + 621, ], - "id": 361, + "id": 357, "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "man_dir": "", "name": "is-weakref", @@ -18789,9 +18661,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 626, + 622, ], - "id": 362, + "id": 358, "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "man_dir": "", "name": "is-shared-array-buffer", @@ -18807,7 +18679,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 363, + "id": 359, "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "man_dir": "", "name": "is-negative-zero", @@ -18823,9 +18695,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 627, + 623, ], - "id": 364, + "id": 360, "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "man_dir": "", "name": "is-data-view", @@ -18841,10 +18713,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 628, - 629, + 624, + 625, ], - "id": 365, + "id": 361, "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "man_dir": "", "name": "is-array-buffer", @@ -18860,11 +18732,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 630, - 631, - 632, + 626, + 627, + 628, ], - "id": 366, + "id": 362, "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "man_dir": "", "name": "internal-slot", @@ -18880,12 +18752,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 633, - 634, - 635, - 636, + 629, + 630, + 631, + 632, ], - "id": 367, + "id": 363, "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "man_dir": "", "name": "side-channel", @@ -18901,10 +18773,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 637, - 638, + 633, + 634, ], - "id": 368, + "id": 364, "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "man_dir": "", "name": "globalthis", @@ -18920,11 +18792,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 639, - 640, - 641, + 635, + 636, + 637, ], - "id": 369, + "id": 365, "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "man_dir": "", "name": "get-symbol-description", @@ -18940,12 +18812,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 642, - 643, - 644, - 645, + 638, + 639, + 640, + 641, ], - "id": 370, + "id": 366, "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "man_dir": "", "name": "function.prototype.name", @@ -18961,11 +18833,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 646, - 647, - 648, + 642, + 643, + 644, ], - "id": 371, + "id": 367, "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "man_dir": "", "name": "es-to-primitive", @@ -18981,9 +18853,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 649, + 645, ], - "id": 372, + "id": 368, "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "man_dir": "", "name": "is-date-object", @@ -18999,11 +18871,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 650, - 651, - 652, + 646, + 647, + 648, ], - "id": 373, + "id": 369, "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "man_dir": "", "name": "es-set-tostringtag", @@ -19019,11 +18891,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 653, - 654, - 655, + 649, + 650, + 651, ], - "id": 374, + "id": 370, "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "man_dir": "", "name": "data-view-byte-offset", @@ -19039,11 +18911,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 656, - 657, - 658, + 652, + 653, + 654, ], - "id": 375, + "id": 371, "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "man_dir": "", "name": "data-view-byte-length", @@ -19059,11 +18931,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 659, - 660, - 661, + 655, + 656, + 657, ], - "id": 376, + "id": 372, "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "man_dir": "", "name": "data-view-buffer", @@ -19079,16 +18951,16 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 658, + 659, + 660, + 661, 662, 663, 664, 665, - 666, - 667, - 668, - 669, ], - "id": 377, + "id": 373, "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "man_dir": "", "name": "arraybuffer.prototype.slice", @@ -19104,10 +18976,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 670, - 671, + 666, + 667, ], - "id": 378, + "id": 374, "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "man_dir": "", "name": "array-buffer-byte-length", @@ -19123,12 +18995,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 672, - 673, - 674, - 675, + 668, + 669, + 670, + 671, ], - "id": 379, + "id": 375, "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "man_dir": "", "name": "object.fromentries", @@ -19144,9 +19016,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 676, + 672, ], - "id": 380, + "id": 376, "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "man_dir": "", "name": "eslint-module-utils", @@ -19162,9 +19034,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 677, + 673, ], - "id": 381, + "id": 377, "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "man_dir": "", "name": "debug", @@ -19180,11 +19052,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 678, - 679, - 680, + 674, + 675, + 676, ], - "id": 382, + "id": 378, "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "man_dir": "", "name": "eslint-import-resolver-node", @@ -19200,9 +19072,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 681, + 677, ], - "id": 383, + "id": 379, "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "man_dir": "", "name": "doctrine", @@ -19218,12 +19090,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 682, - 683, - 684, - 685, + 678, + 679, + 680, + 681, ], - "id": 384, + "id": 380, "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "man_dir": "", "name": "array.prototype.flatmap", @@ -19239,9 +19111,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 686, + 682, ], - "id": 385, + "id": 381, "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "man_dir": "", "name": "es-shim-unscopables", @@ -19257,12 +19129,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 687, - 688, - 689, - 690, + 683, + 684, + 685, + 686, ], - "id": 386, + "id": 382, "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "man_dir": "", "name": "array.prototype.flat", @@ -19278,14 +19150,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 687, + 688, + 689, + 690, 691, 692, - 693, - 694, - 695, - 696, ], - "id": 387, + "id": 383, "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "man_dir": "", "name": "array.prototype.findlastindex", @@ -19301,14 +19173,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 693, + 694, + 695, + 696, 697, 698, - 699, - 700, - 701, - 702, ], - "id": 388, + "id": 384, "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "man_dir": "", "name": "array-includes", @@ -19324,9 +19196,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 703, + 699, ], - "id": 389, + "id": 385, "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "man_dir": "", "name": "get-tsconfig", @@ -19342,7 +19214,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 390, + "id": 386, "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "man_dir": "", "name": "resolve-pkg-maps", @@ -19358,10 +19230,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 704, - 705, + 700, + 701, ], - "id": 391, + "id": 387, "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "man_dir": "", "name": "enhanced-resolve", @@ -19377,7 +19249,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 392, + "id": 388, "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "man_dir": "", "name": "tapable", @@ -19393,9 +19265,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 706, + 702, ], - "id": 393, + "id": 389, "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "man_dir": "", "name": "eslint-plugin-react-hooks", @@ -19411,14 +19283,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 703, + 704, + 705, + 706, 707, 708, - 709, - 710, - 711, - 712, ], - "id": 394, + "id": 390, "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "man_dir": "", "name": "@typescript-eslint/parser", @@ -19434,16 +19306,16 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 709, + 710, + 711, + 712, 713, 714, 715, 716, - 717, - 718, - 719, - 720, ], - "id": 395, + "id": 391, "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "man_dir": "", "name": "@typescript-eslint/typescript-estree", @@ -19459,10 +19331,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 721, - 722, + 717, + 718, ], - "id": 396, + "id": 392, "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "man_dir": "", "name": "@typescript-eslint/visitor-keys", @@ -19478,7 +19350,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 397, + "id": 393, "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "man_dir": "", "name": "@typescript-eslint/types", @@ -19494,9 +19366,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 723, + 719, ], - "id": 398, + "id": 394, "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "man_dir": "", "name": "ts-api-utils", @@ -19512,9 +19384,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 724, + 720, ], - "id": 399, + "id": 395, "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "man_dir": "", "name": "minimatch", @@ -19530,14 +19402,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 721, + 722, + 723, + 724, 725, 726, - 727, - 728, - 729, - 730, ], - "id": 400, + "id": 396, "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "man_dir": "", "name": "globby", @@ -19553,7 +19425,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 401, + "id": 397, "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "man_dir": "", "name": "slash", @@ -19569,9 +19441,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 731, + 727, ], - "id": 402, + "id": 398, "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "man_dir": "", "name": "dir-glob", @@ -19587,7 +19459,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 403, + "id": 399, "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "man_dir": "", "name": "path-type", @@ -19603,7 +19475,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 404, + "id": 400, "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "man_dir": "", "name": "array-union", @@ -19619,10 +19491,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 732, - 733, + 728, + 729, ], - "id": 405, + "id": 401, "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "man_dir": "", "name": "@typescript-eslint/scope-manager", @@ -19638,9 +19510,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 734, + 730, ], - "id": 406, + "id": 402, "integrity": "sha512-VCnZI2cy77Yaj3L7Uhs3+44ikMM1VD/fBMwvTBb3hIaTIuqa+DmG4dhUDq+MASu3yx97KhgsVJbsas0XuiKyww==", "man_dir": "", "name": "@next/eslint-plugin-next", @@ -19656,7 +19528,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 407, + "id": 403, "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "man_dir": "", "name": "@rushstack/eslint-patch", @@ -19672,6 +19544,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 731, + 732, + 733, + 734, 735, 736, 737, @@ -19685,12 +19561,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 745, 746, 747, - 748, - 749, - 750, - 751, ], - "id": 408, + "id": 404, "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", "man_dir": "", "name": "eslint-plugin-jsx-a11y", @@ -19706,11 +19578,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 752, - 753, - 754, + 748, + 749, + 750, ], - "id": 409, + "id": 405, "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "man_dir": "", "name": "object.entries", @@ -19726,9 +19598,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 755, + 751, ], - "id": 410, + "id": 406, "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "man_dir": "", "name": "language-tags", @@ -19744,7 +19616,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 411, + "id": 407, "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "man_dir": "", "name": "language-subtag-registry", @@ -19760,12 +19632,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 756, - 757, - 758, - 759, + 752, + 753, + 754, + 755, ], - "id": 412, + "id": 408, "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "man_dir": "", "name": "jsx-ast-utils", @@ -19781,6 +19653,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 756, + 757, + 758, + 759, 760, 761, 762, @@ -19791,12 +19667,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 767, 768, 769, - 770, - 771, - 772, - 773, ], - "id": 413, + "id": 409, "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "man_dir": "", "name": "es-iterator-helpers", @@ -19812,13 +19684,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 770, + 771, + 772, + 773, 774, - 775, - 776, - 777, - 778, ], - "id": 414, + "id": 410, "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "man_dir": "", "name": "iterator.prototype", @@ -19834,15 +19706,15 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 775, + 776, + 777, + 778, 779, 780, 781, - 782, - 783, - 784, - 785, ], - "id": 415, + "id": 411, "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "man_dir": "", "name": "reflect.getprototypeof", @@ -19858,6 +19730,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 782, + 783, + 784, + 785, 786, 787, 788, @@ -19866,12 +19742,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 791, 792, 793, - 794, - 795, - 796, - 797, ], - "id": 416, + "id": 412, "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", "man_dir": "", "name": "which-builtin-type", @@ -19887,12 +19759,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 798, - 799, - 800, - 801, + 794, + 795, + 796, + 797, ], - "id": 417, + "id": 413, "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "man_dir": "", "name": "which-collection", @@ -19908,10 +19780,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 802, - 803, + 798, + 799, ], - "id": 418, + "id": 414, "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "man_dir": "", "name": "is-weakset", @@ -19927,7 +19799,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 419, + "id": 415, "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "man_dir": "", "name": "is-weakmap", @@ -19943,7 +19815,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 420, + "id": 416, "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "man_dir": "", "name": "is-set", @@ -19959,7 +19831,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 421, + "id": 417, "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "man_dir": "", "name": "is-map", @@ -19975,9 +19847,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 804, + 800, ], - "id": 422, + "id": 418, "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "man_dir": "", "name": "is-generator-function", @@ -19993,9 +19865,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 805, + 801, ], - "id": 423, + "id": 419, "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "man_dir": "", "name": "is-finalizationregistry", @@ -20011,9 +19883,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 806, + 802, ], - "id": 424, + "id": 420, "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "man_dir": "", "name": "is-async-function", @@ -20029,7 +19901,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 425, + "id": 421, "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "man_dir": "", "name": "damerau-levenshtein", @@ -20045,9 +19917,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 807, + 803, ], - "id": 426, + "id": 422, "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "man_dir": "", "name": "axobject-query", @@ -20063,7 +19935,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 427, + "id": 423, "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "man_dir": "", "name": "dequal", @@ -20079,7 +19951,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 428, + "id": 424, "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "man_dir": "", "name": "axe-core", @@ -20095,7 +19967,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 429, + "id": 425, "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "man_dir": "", "name": "ast-types-flow", @@ -20111,9 +19983,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 808, + 804, ], - "id": 430, + "id": 426, "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "man_dir": "", "name": "aria-query", @@ -20129,9 +20001,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 809, + 805, ], - "id": 431, + "id": 427, "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "man_dir": "", "name": "@babel/runtime", @@ -20147,7 +20019,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 432, + "id": 428, "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "man_dir": "", "name": "regenerator-runtime", @@ -20163,6 +20035,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 806, + 807, + 808, + 809, 810, 811, 812, @@ -20178,12 +20054,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 822, 823, 824, - 825, - 826, - 827, - 828, ], - "id": 433, + "id": 429, "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", "man_dir": "", "name": "eslint-plugin-react", @@ -20199,6 +20071,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 825, + 826, + 827, + 828, 829, 830, 831, @@ -20207,12 +20083,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` 834, 835, 836, - 837, - 838, - 839, - 840, ], - "id": 434, + "id": 430, "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "man_dir": "", "name": "string.prototype.matchall", @@ -20231,11 +20103,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "resolve", }, "dependencies": [ - 841, - 842, - 843, + 837, + 838, + 839, ], - "id": 435, + "id": 431, "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "man_dir": "", "name": "resolve", @@ -20251,11 +20123,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 844, - 845, - 846, + 840, + 841, + 842, ], - "id": 436, + "id": 432, "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "man_dir": "", "name": "prop-types", @@ -20271,7 +20143,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 437, + "id": 433, "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "man_dir": "", "name": "react-is", @@ -20287,11 +20159,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 847, - 848, - 849, + 843, + 844, + 845, ], - "id": 438, + "id": 434, "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "man_dir": "", "name": "object.hasown", @@ -20307,13 +20179,13 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 846, + 847, + 848, + 849, 850, - 851, - 852, - 853, - 854, ], - "id": 439, + "id": 435, "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "man_dir": "", "name": "array.prototype.tosorted", @@ -20329,12 +20201,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 855, - 856, - 857, - 858, + 851, + 852, + 853, + 854, ], - "id": 440, + "id": 436, "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", "man_dir": "", "name": "array.prototype.toreversed", @@ -20350,14 +20222,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ + 855, + 856, + 857, + 858, 859, 860, - 861, - 862, - 863, - 864, ], - "id": 441, + "id": 437, "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "man_dir": "", "name": "array.prototype.findlast", @@ -20373,10 +20245,10 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 865, - 866, + 861, + 862, ], - "id": 442, + "id": 438, "integrity": "sha512-DIM2C9qCECwhck9nLsCDeTv943VmGMCkwX3KljjprSRDXaK2CSiUDVGbUit80Er38ukgxuESJgYPAys4FsNCdg==", "man_dir": "", "name": "bun-types", @@ -20392,9 +20264,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 867, + 863, ], - "id": 443, + "id": 439, "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "man_dir": "", "name": "@types/ws", @@ -20413,15 +20285,15 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "autoprefixer", }, "dependencies": [ + 864, + 865, + 866, + 867, 868, 869, 870, - 871, - 872, - 873, - 874, ], - "id": 444, + "id": 440, "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "man_dir": "", "name": "autoprefixer", @@ -20437,7 +20309,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 445, + "id": 441, "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "man_dir": "", "name": "normalize-range", @@ -20453,7 +20325,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 446, + "id": 442, "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "man_dir": "", "name": "fraction.js", @@ -20472,12 +20344,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "browserslist", }, "dependencies": [ - 875, - 876, - 877, - 878, + 871, + 872, + 873, + 874, ], - "id": 447, + "id": 443, "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "man_dir": "", "name": "browserslist", @@ -20496,11 +20368,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "name": "update-browserslist-db", }, "dependencies": [ - 879, - 880, - 881, + 875, + 876, + 877, ], - "id": 448, + "id": 444, "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "man_dir": "", "name": "update-browserslist-db", @@ -20516,7 +20388,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 449, + "id": 445, "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "man_dir": "", "name": "node-releases", @@ -20532,7 +20404,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 450, + "id": 446, "integrity": "sha512-eVGeQxpaBYbomDBa/Mehrs28MdvCXfJmEFzaMFsv8jH/MJDLIylJN81eTJ5kvx7B7p18OiPK0BkC06lydEy63A==", "man_dir": "", "name": "electron-to-chromium", @@ -20548,9 +20420,9 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 882, + 878, ], - "id": 451, + "id": 447, "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "man_dir": "", "name": "@types/react-dom", @@ -20566,11 +20438,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [ - 883, - 884, - 885, + 879, + 880, + 881, ], - "id": 452, + "id": 448, "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", "man_dir": "", "name": "@types/react", @@ -20586,7 +20458,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 453, + "id": 449, "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "man_dir": "", "name": "csstype", @@ -20602,7 +20474,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 454, + "id": 450, "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==", "man_dir": "", "name": "@types/scheduler", @@ -20618,7 +20490,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 455, + "id": 451, "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "man_dir": "", "name": "@types/prop-types", @@ -20634,7 +20506,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "bin": null, "dependencies": [], - "id": 456, + "id": 452, "integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==", "man_dir": "", "name": "@types/node", @@ -20656,48 +20528,48 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 13, }, "@babel/code-frame": { - "id": 281, - "package_id": 206, + "id": 277, + "package_id": 202, }, "@babel/helper-validator-identifier": { - "id": 288, - "package_id": 215, + "id": 284, + "package_id": 211, }, "@babel/highlight": { - "id": 286, - "package_id": 207, + "id": 282, + "package_id": 203, }, "@babel/runtime": { - "id": 735, - "package_id": 431, + "id": 731, + "package_id": 427, }, "@eslint-community/eslint-utils": { - "id": 374, - "package_id": 245, + "id": 370, + "package_id": 241, }, "@eslint-community/regexpp": { - "id": 372, - "package_id": 252, + "id": 368, + "package_id": 248, }, "@eslint/eslintrc": { - "id": 367, - "package_id": 265, + "id": 363, + "package_id": 261, }, "@eslint/js": { - "id": 355, - "package_id": 293, + "id": 351, + "package_id": 289, }, "@humanwhocodes/config-array": { - "id": 373, - "package_id": 247, + "id": 369, + "package_id": 243, }, "@humanwhocodes/module-importer": { - "id": 375, - "package_id": 244, + "id": 371, + "package_id": 240, }, "@humanwhocodes/object-schema": { - "id": 379, - "package_id": 251, + "id": 375, + "package_id": 247, }, "@isaacs/cliui": { "id": 111, @@ -20724,48 +20596,48 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 101, }, "@next/env": { - "id": 304, - "package_id": 237, + "id": 300, + "package_id": 233, }, "@next/eslint-plugin-next": { - "id": 333, - "package_id": 406, + "id": 329, + "package_id": 402, }, "@next/swc-darwin-arm64": { - "id": 310, - "package_id": 231, + "id": 306, + "package_id": 227, }, "@next/swc-darwin-x64": { - "id": 309, - "package_id": 232, + "id": 305, + "package_id": 228, }, "@next/swc-linux-arm64-gnu": { - "id": 314, - "package_id": 227, + "id": 310, + "package_id": 223, }, "@next/swc-linux-arm64-musl": { - "id": 316, - "package_id": 225, + "id": 312, + "package_id": 221, }, "@next/swc-linux-x64-gnu": { - "id": 311, - "package_id": 230, + "id": 307, + "package_id": 226, }, "@next/swc-linux-x64-musl": { - "id": 312, - "package_id": 229, + "id": 308, + "package_id": 225, }, "@next/swc-win32-arm64-msvc": { - "id": 317, - "package_id": 224, + "id": 313, + "package_id": 220, }, "@next/swc-win32-ia32-msvc": { - "id": 315, - "package_id": 226, + "id": 311, + "package_id": 222, }, "@next/swc-win32-x64-msvc": { - "id": 313, - "package_id": 228, + "id": 309, + "package_id": 224, }, "@nodelib/fs.scandir": { "id": 72, @@ -20776,7 +20648,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 49, }, "@nodelib/fs.walk": { - "id": 368, + "id": 364, "package_id": 43, }, "@pkgjs/parseargs": { @@ -20785,94 +20657,94 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, "@puppeteer/browsers": { "id": 155, - "package_id": 114, + "package_id": 115, }, "@rushstack/eslint-patch": { - "id": 332, - "package_id": 407, + "id": 328, + "package_id": 403, }, "@swc/helpers": { - "id": 307, - "package_id": 234, + "id": 303, + "package_id": 230, }, "@tootallnate/quickjs-emscripten": { - "id": 218, - "package_id": 177, + "id": 219, + "package_id": 178, }, "@types/json5": { - "id": 467, - "package_id": 312, + "id": 463, + "package_id": 308, }, "@types/node": { "id": 0, - "package_id": 456, + "package_id": 452, }, "@types/prop-types": { - "id": 883, - "package_id": 455, + "id": 879, + "package_id": 451, }, "@types/react": { "id": 1, - "package_id": 452, + "package_id": 448, }, "@types/react-dom": { "id": 2, - "package_id": 451, + "package_id": 447, }, "@types/scheduler": { - "id": 884, - "package_id": 454, + "id": 880, + "package_id": 450, }, "@types/ws": { - "id": 865, - "package_id": 443, + "id": 861, + "package_id": 439, }, "@types/yauzl": { - "id": 252, - "package_id": 181, + "id": 253, + "package_id": 182, }, "@typescript-eslint/parser": { - "id": 334, - "package_id": 394, + "id": 330, + "package_id": 390, }, "@typescript-eslint/scope-manager": { - "id": 710, - "package_id": 405, + "id": 706, + "package_id": 401, }, "@typescript-eslint/types": { - "id": 708, - "package_id": 397, + "id": 704, + "package_id": 393, }, "@typescript-eslint/typescript-estree": { - "id": 711, - "package_id": 395, + "id": 707, + "package_id": 391, }, "@typescript-eslint/visitor-keys": { - "id": 709, - "package_id": 396, + "id": 705, + "package_id": 392, }, "acorn": { - "id": 409, - "package_id": 272, + "id": 405, + "package_id": 268, }, "acorn-jsx": { - "id": 410, - "package_id": 271, + "id": 406, + "package_id": 267, }, "agent-base": { - "id": 201, - "package_id": 155, + "id": 202, + "package_id": 156, }, "ajv": { - "id": 340, - "package_id": 273, + "id": 336, + "package_id": 269, }, "ansi-regex": { "id": 122, "package_id": 77, }, "ansi-styles": { - "id": 437, + "id": 433, "package_id": 81, }, "any-promise": { @@ -20888,180 +20760,180 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 107, }, "argparse": { - "id": 298, - "package_id": 217, + "id": 294, + "package_id": 213, }, "aria-query": { - "id": 736, - "package_id": 430, + "id": 732, + "package_id": 426, }, "array-buffer-byte-length": { - "id": 504, - "package_id": 378, + "id": 500, + "package_id": 374, }, "array-includes": { - "id": 810, - "package_id": 388, + "id": 806, + "package_id": 384, }, "array-union": { - "id": 725, - "package_id": 404, + "id": 721, + "package_id": 400, }, "array.prototype.findlast": { - "id": 811, - "package_id": 441, + "id": 807, + "package_id": 437, }, "array.prototype.findlastindex": { - "id": 450, - "package_id": 387, + "id": 446, + "package_id": 383, }, "array.prototype.flat": { - "id": 451, - "package_id": 386, + "id": 447, + "package_id": 382, }, "array.prototype.flatmap": { - "id": 812, - "package_id": 384, + "id": 808, + "package_id": 380, }, "array.prototype.toreversed": { - "id": 813, - "package_id": 440, + "id": 809, + "package_id": 436, }, "array.prototype.tosorted": { - "id": 814, - "package_id": 439, + "id": 810, + "package_id": 435, }, "arraybuffer.prototype.slice": { - "id": 505, - "package_id": 377, + "id": 501, + "package_id": 373, }, "ast-types": { - "id": 228, - "package_id": 166, + "id": 229, + "package_id": 167, }, "ast-types-flow": { - "id": 739, - "package_id": 429, + "id": 735, + "package_id": 425, }, "autoprefixer": { "id": 3, - "package_id": 444, + "package_id": 440, }, "available-typed-arrays": { - "id": 506, - "package_id": 334, + "id": 502, + "package_id": 330, }, "axe-core": { - "id": 740, - "package_id": 428, + "id": 736, + "package_id": 424, }, "axobject-query": { - "id": 741, - "package_id": 426, + "id": 737, + "package_id": 422, }, "b4a": { - "id": 194, - "package_id": 138, + "id": 195, + "package_id": 139, }, "balanced-match": { - "id": 383, + "id": 379, "package_id": 71, }, "bare-events": { - "id": 185, - "package_id": 136, + "id": 186, + "package_id": 137, }, "bare-fs": { - "id": 182, - "package_id": 133, + "id": 183, + "package_id": 134, }, "bare-os": { - "id": 184, - "package_id": 132, + "id": 185, + "package_id": 133, }, "bare-path": { - "id": 183, - "package_id": 131, + "id": 184, + "package_id": 132, }, "bare-stream": { - "id": 187, - "package_id": 134, + "id": 188, + "package_id": 135, }, "base64-js": { - "id": 178, - "package_id": 129, + "id": 179, + "package_id": 130, }, "basic-ftp": { - "id": 240, - "package_id": 176, + "id": 241, + "package_id": 177, }, "binary-extensions": { "id": 87, "package_id": 54, }, "brace-expansion": { - "id": 382, - "package_id": 249, + "id": 378, + "package_id": 245, }, "braces": { "id": 79, "package_id": 34, }, "browserslist": { - "id": 868, - "package_id": 447, + "id": 864, + "package_id": 443, }, "buffer": { - "id": 176, - "package_id": 127, + "id": 177, + "package_id": 128, }, "buffer-crc32": { - "id": 256, - "package_id": 185, + "id": 257, + "package_id": 186, }, "bun-types": { "id": 4, - "package_id": 442, + "package_id": 438, }, "busboy": { - "id": 302, - "package_id": 239, + "id": 298, + "package_id": 235, }, "call-bind": { - "id": 697, - "package_id": 326, + "id": 693, + "package_id": 322, }, "callsites": { - "id": 301, - "package_id": 221, + "id": 297, + "package_id": 217, }, "camelcase-css": { "id": 59, "package_id": 31, }, "caniuse-lite": { - "id": 869, - "package_id": 233, + "id": 865, + "package_id": 229, }, "chalk": { - "id": 342, - "package_id": 303, + "id": 338, + "package_id": 299, }, "chokidar": { "id": 21, "package_id": 50, }, "chromium-bidi": { - "id": 261, - "package_id": 198, + "id": 262, + "package_id": 193, }, "client-only": { - "id": 323, - "package_id": 236, + "id": 319, + "package_id": 232, }, "cliui": { - "id": 166, - "package_id": 124, + "id": 167, + "package_id": 125, }, "color-convert": { "id": 126, @@ -21076,19 +20948,15 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 99, }, "concat-map": { - "id": 384, - "package_id": 250, + "id": 380, + "package_id": 246, }, "cosmiconfig": { "id": 153, - "package_id": 201, - }, - "cross-fetch": { - "id": 262, - "package_id": 193, + "package_id": 197, }, "cross-spawn": { - "id": 359, + "id": 355, "package_id": 93, }, "cssesc": { @@ -21096,552 +20964,552 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 5, }, "csstype": { - "id": 885, - "package_id": 453, + "id": 881, + "package_id": 449, }, "damerau-levenshtein": { - "id": 742, - "package_id": 425, + "id": 738, + "package_id": 421, }, "data-uri-to-buffer": { - "id": 241, - "package_id": 175, + "id": 242, + "package_id": 176, }, "data-view-buffer": { - "id": 508, - "package_id": 376, + "id": 504, + "package_id": 372, }, "data-view-byte-length": { - "id": 509, - "package_id": 375, + "id": 505, + "package_id": 371, }, "data-view-byte-offset": { - "id": 510, - "package_id": 374, + "id": 506, + "package_id": 370, }, "debug": { - "id": 343, - "package_id": 153, + "id": 339, + "package_id": 154, }, "deep-is": { - "id": 422, - "package_id": 292, + "id": 418, + "package_id": 288, }, "define-data-property": { - "id": 476, - "package_id": 324, + "id": 472, + "package_id": 320, }, "define-properties": { - "id": 698, - "package_id": 317, + "id": 694, + "package_id": 313, }, "degenerator": { - "id": 226, - "package_id": 160, + "id": 227, + "package_id": 161, }, "dequal": { - "id": 808, - "package_id": 427, + "id": 804, + "package_id": 423, }, "devtools-protocol": { - "id": 264, - "package_id": 192, + "id": 156, + "package_id": 114, }, "didyoumean": { "id": 24, "package_id": 38, }, "dir-glob": { - "id": 726, - "package_id": 402, + "id": 722, + "package_id": 398, }, "dlv": { "id": 15, "package_id": 106, }, "doctrine": { - "id": 352, - "package_id": 295, + "id": 348, + "package_id": 291, }, "eastasianwidth": { "id": 132, "package_id": 89, }, "electron-to-chromium": { - "id": 876, - "package_id": 450, + "id": 872, + "package_id": 446, }, "emoji-regex": { - "id": 743, + "id": 739, "package_id": 88, }, "end-of-stream": { - "id": 197, - "package_id": 145, + "id": 198, + "package_id": 146, }, "enhanced-resolve": { - "id": 441, - "package_id": 391, + "id": 437, + "package_id": 387, }, "env-paths": { - "id": 276, - "package_id": 222, + "id": 272, + "package_id": 218, }, "error-ex": { - "id": 282, - "package_id": 204, + "id": 278, + "package_id": 200, }, "es-abstract": { - "id": 699, - "package_id": 329, + "id": 695, + "package_id": 325, }, "es-define-property": { - "id": 490, - "package_id": 320, + "id": 486, + "package_id": 316, }, "es-errors": { - "id": 862, - "package_id": 316, + "id": 858, + "package_id": 312, }, "es-iterator-helpers": { - "id": 816, - "package_id": 413, + "id": 812, + "package_id": 409, }, "es-object-atoms": { - "id": 700, - "package_id": 315, + "id": 696, + "package_id": 311, }, "es-set-tostringtag": { - "id": 764, - "package_id": 373, + "id": 760, + "package_id": 369, }, "es-shim-unscopables": { - "id": 864, - "package_id": 385, + "id": 860, + "package_id": 381, }, "es-to-primitive": { - "id": 515, - "package_id": 371, + "id": 511, + "package_id": 367, }, "escalade": { - "id": 879, - "package_id": 123, + "id": 875, + "package_id": 124, }, "escape-string-regexp": { - "id": 371, - "package_id": 253, + "id": 367, + "package_id": 249, }, "escodegen": { - "id": 229, - "package_id": 162, + "id": 230, + "package_id": 163, }, "eslint": { "id": 5, - "package_id": 242, + "package_id": 238, }, "eslint-config-next": { "id": 6, - "package_id": 241, + "package_id": 237, }, "eslint-import-resolver-node": { - "id": 336, - "package_id": 382, + "id": 332, + "package_id": 378, }, "eslint-import-resolver-typescript": { - "id": 337, - "package_id": 306, + "id": 333, + "package_id": 302, }, "eslint-module-utils": { - "id": 456, - "package_id": 380, + "id": 452, + "package_id": 376, }, "eslint-plugin-import": { - "id": 330, - "package_id": 307, + "id": 326, + "package_id": 303, }, "eslint-plugin-jsx-a11y": { - "id": 331, - "package_id": 408, + "id": 327, + "package_id": 404, }, "eslint-plugin-react": { - "id": 329, - "package_id": 433, + "id": 325, + "package_id": 429, }, "eslint-plugin-react-hooks": { - "id": 335, - "package_id": 393, + "id": 331, + "package_id": 389, }, "eslint-scope": { - "id": 362, - "package_id": 282, + "id": 358, + "package_id": 278, }, "eslint-visitor-keys": { - "id": 370, - "package_id": 246, + "id": 366, + "package_id": 242, }, "espree": { - "id": 344, - "package_id": 270, + "id": 340, + "package_id": 266, }, "esprima": { - "id": 230, - "package_id": 161, + "id": 231, + "package_id": 162, }, "esquery": { - "id": 346, - "package_id": 302, + "id": 342, + "package_id": 298, }, "esrecurse": { - "id": 418, - "package_id": 283, + "id": 414, + "package_id": 279, }, "estraverse": { - "id": 436, - "package_id": 165, + "id": 432, + "package_id": 166, }, "esutils": { - "id": 347, - "package_id": 164, + "id": 343, + "package_id": 165, }, "extract-zip": { - "id": 157, - "package_id": 180, + "id": 158, + "package_id": 181, }, "fast-deep-equal": { - "id": 365, - "package_id": 278, + "id": 361, + "package_id": 274, }, "fast-fifo": { - "id": 195, - "package_id": 140, + "id": 196, + "package_id": 141, }, "fast-glob": { "id": 22, "package_id": 40, }, "fast-json-stable-stringify": { - "id": 414, - "package_id": 277, + "id": 410, + "package_id": 273, }, "fast-levenshtein": { - "id": 426, - "package_id": 287, + "id": 422, + "package_id": 283, }, "fastq": { "id": 73, "package_id": 44, }, "fd-slicer": { - "id": 255, - "package_id": 186, + "id": 256, + "package_id": 187, }, "file-entry-cache": { - "id": 369, - "package_id": 254, + "id": 365, + "package_id": 250, }, "fill-range": { "id": 63, "package_id": 35, }, "find-up": { - "id": 348, - "package_id": 296, + "id": 344, + "package_id": 292, }, "flat-cache": { - "id": 385, - "package_id": 255, + "id": 381, + "package_id": 251, }, "flatted": { - "id": 386, - "package_id": 264, + "id": 382, + "package_id": 260, }, "for-each": { - "id": 587, - "package_id": 332, + "id": 583, + "package_id": 328, }, "foreground-child": { "id": 102, "package_id": 91, }, "fraction.js": { - "id": 870, - "package_id": 446, + "id": 866, + "package_id": 442, }, "fs-extra": { - "id": 243, - "package_id": 171, + "id": 244, + "package_id": 172, }, "fs.realpath": { - "id": 390, - "package_id": 261, + "id": 386, + "package_id": 257, }, "fsevents": { "id": 85, "package_id": 51, }, "function-bind": { - "id": 765, + "id": 761, "package_id": 21, }, "function.prototype.name": { - "id": 516, - "package_id": 370, + "id": 512, + "package_id": 366, }, "functions-have-names": { - "id": 619, - "package_id": 358, + "id": 615, + "package_id": 354, }, "get-caller-file": { - "id": 168, - "package_id": 122, + "id": 169, + "package_id": 123, }, "get-intrinsic": { - "id": 701, - "package_id": 321, + "id": 697, + "package_id": 317, }, "get-stream": { - "id": 250, - "package_id": 188, + "id": 251, + "package_id": 189, }, "get-symbol-description": { - "id": 518, - "package_id": 369, + "id": 514, + "package_id": 365, }, "get-tsconfig": { - "id": 444, - "package_id": 389, + "id": 440, + "package_id": 385, }, "get-uri": { - "id": 221, - "package_id": 170, + "id": 222, + "package_id": 171, }, "glob": { - "id": 734, + "id": 730, "package_id": 65, }, "glob-parent": { - "id": 360, + "id": 356, "package_id": 27, }, "globals": { - "id": 349, - "package_id": 268, + "id": 345, + "package_id": 264, }, "globalthis": { - "id": 767, - "package_id": 368, + "id": 763, + "package_id": 364, }, "globby": { - "id": 714, - "package_id": 400, + "id": 710, + "package_id": 396, }, "gopd": { - "id": 835, - "package_id": 325, + "id": 831, + "package_id": 321, }, "graceful-fs": { - "id": 306, - "package_id": 174, + "id": 302, + "package_id": 175, }, "graphemer": { - "id": 353, - "package_id": 294, + "id": 349, + "package_id": 290, }, "has-bigints": { - "id": 559, - "package_id": 343, + "id": 555, + "package_id": 339, }, "has-flag": { - "id": 439, - "package_id": 305, + "id": 435, + "package_id": 301, }, "has-property-descriptors": { - "id": 768, - "package_id": 319, + "id": 764, + "package_id": 315, }, "has-proto": { - "id": 769, - "package_id": 323, + "id": 765, + "package_id": 319, }, "has-symbols": { - "id": 770, - "package_id": 322, + "id": 766, + "package_id": 318, }, "has-tostringtag": { - "id": 568, - "package_id": 331, + "id": 564, + "package_id": 327, }, "hasown": { - "id": 457, + "id": 453, "package_id": 20, }, "http-proxy-agent": { - "id": 203, - "package_id": 169, + "id": 204, + "package_id": 170, }, "https-proxy-agent": { - "id": 204, - "package_id": 168, + "id": 205, + "package_id": 169, }, "ieee754": { - "id": 179, - "package_id": 128, + "id": 180, + "package_id": 129, }, "ignore": { - "id": 345, - "package_id": 267, + "id": 341, + "package_id": 263, }, "import-fresh": { - "id": 404, - "package_id": 218, + "id": 400, + "package_id": 214, }, "imurmurhash": { - "id": 361, - "package_id": 284, + "id": 357, + "package_id": 280, }, "inflight": { - "id": 391, - "package_id": 260, + "id": 387, + "package_id": 256, }, "inherits": { - "id": 392, - "package_id": 259, + "id": 388, + "package_id": 255, }, "internal-slot": { - "id": 771, - "package_id": 366, + "id": 767, + "package_id": 362, }, "ip-address": { - "id": 212, - "package_id": 150, + "id": 213, + "package_id": 151, }, "is-array-buffer": { - "id": 526, - "package_id": 365, + "id": 522, + "package_id": 361, }, "is-arrayish": { - "id": 285, - "package_id": 205, + "id": 281, + "package_id": 201, }, "is-async-function": { - "id": 788, - "package_id": 424, + "id": 784, + "package_id": 420, }, "is-bigint": { - "id": 562, - "package_id": 342, + "id": 558, + "package_id": 338, }, "is-binary-path": { "id": 81, "package_id": 53, }, "is-boolean-object": { - "id": 563, - "package_id": 341, + "id": 559, + "package_id": 337, }, "is-callable": { - "id": 527, - "package_id": 333, + "id": 523, + "package_id": 329, }, "is-core-module": { - "id": 458, + "id": 454, "package_id": 19, }, "is-data-view": { - "id": 528, - "package_id": 364, + "id": 524, + "package_id": 360, }, "is-date-object": { - "id": 647, - "package_id": 372, + "id": 643, + "package_id": 368, }, "is-extglob": { "id": 58, "package_id": 29, }, "is-finalizationregistry": { - "id": 790, - "package_id": 423, + "id": 786, + "package_id": 419, }, "is-fullwidth-code-point": { "id": 124, "package_id": 79, }, "is-generator-function": { - "id": 791, - "package_id": 422, + "id": 787, + "package_id": 418, }, "is-glob": { - "id": 350, + "id": 346, "package_id": 28, }, "is-map": { - "id": 798, - "package_id": 421, + "id": 794, + "package_id": 417, }, "is-negative-zero": { - "id": 529, - "package_id": 363, + "id": 525, + "package_id": 359, }, "is-number": { "id": 65, "package_id": 37, }, "is-number-object": { - "id": 564, - "package_id": 340, + "id": 560, + "package_id": 336, }, "is-path-inside": { - "id": 364, - "package_id": 280, + "id": 360, + "package_id": 276, }, "is-regex": { - "id": 530, - "package_id": 353, + "id": 526, + "package_id": 349, }, "is-set": { - "id": 799, - "package_id": 420, + "id": 795, + "package_id": 416, }, "is-shared-array-buffer": { - "id": 531, - "package_id": 362, + "id": 527, + "package_id": 358, }, "is-string": { - "id": 702, - "package_id": 339, + "id": 698, + "package_id": 335, }, "is-symbol": { - "id": 648, - "package_id": 338, + "id": 644, + "package_id": 334, }, "is-typed-array": { - "id": 533, - "package_id": 345, + "id": 529, + "package_id": 341, }, "is-weakmap": { - "id": 800, - "package_id": 419, + "id": 796, + "package_id": 415, }, "is-weakref": { - "id": 534, - "package_id": 361, + "id": 530, + "package_id": 357, }, "is-weakset": { - "id": 801, - "package_id": 418, + "id": 797, + "package_id": 414, }, "isarray": { - "id": 612, - "package_id": 355, + "id": 608, + "package_id": 351, }, "isexe": { "id": 140, "package_id": 95, }, "iterator.prototype": { - "id": 772, - "package_id": 414, + "id": 768, + "package_id": 410, }, "jackspeak": { "id": 103, @@ -21656,56 +21524,56 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 111, }, "js-yaml": { - "id": 351, - "package_id": 216, + "id": 347, + "package_id": 212, }, "jsbn": { - "id": 214, - "package_id": 152, + "id": 215, + "package_id": 153, }, "json-buffer": { - "id": 398, - "package_id": 263, + "id": 394, + "package_id": 259, }, "json-parse-even-better-errors": { - "id": 283, - "package_id": 203, + "id": 279, + "package_id": 199, }, "json-schema-traverse": { - "id": 415, - "package_id": 276, + "id": 411, + "package_id": 272, }, "json-stable-stringify-without-jsonify": { - "id": 376, - "package_id": 243, + "id": 372, + "package_id": 239, }, "json5": { - "id": 468, - "package_id": 311, + "id": 464, + "package_id": 307, }, "jsonfile": { - "id": 245, - "package_id": 173, + "id": 246, + "package_id": 174, }, "jsx-ast-utils": { - "id": 818, - "package_id": 412, + "id": 814, + "package_id": 408, }, "keyv": { - "id": 387, - "package_id": 262, + "id": 383, + "package_id": 258, }, "language-subtag-registry": { - "id": 755, - "package_id": 411, + "id": 751, + "package_id": 407, }, "language-tags": { - "id": 747, - "package_id": 410, + "id": 743, + "package_id": 406, }, "levn": { - "id": 341, - "package_id": 288, + "id": 337, + "package_id": 284, }, "lilconfig": { "id": 23, @@ -21716,20 +21584,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 64, }, "locate-path": { - "id": 431, - "package_id": 298, + "id": 427, + "package_id": 294, }, "lodash.merge": { - "id": 363, - "package_id": 281, + "id": 359, + "package_id": 277, }, "loose-envify": { "id": 150, "package_id": 110, }, "lru-cache": { - "id": 205, - "package_id": 178, + "id": 206, + "package_id": 179, }, "merge2": { "id": 69, @@ -21740,24 +21608,24 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 32, }, "minimatch": { - "id": 354, - "package_id": 248, + "id": 350, + "package_id": 244, }, "minimist": { - "id": 469, - "package_id": 310, + "id": 465, + "package_id": 306, }, "minipass": { "id": 105, "package_id": 67, }, "mitt": { - "id": 273, - "package_id": 200, + "id": 268, + "package_id": 196, }, "ms": { - "id": 216, - "package_id": 154, + "id": 217, + "package_id": 155, }, "mz": { "id": 94, @@ -21768,35 +21636,31 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 10, }, "natural-compare": { - "id": 366, - "package_id": 279, + "id": 362, + "package_id": 275, }, "netmask": { - "id": 227, - "package_id": 159, + "id": 228, + "package_id": 160, }, "next": { "id": 7, - "package_id": 223, - }, - "node-fetch": { - "id": 268, - "package_id": 194, + "package_id": 219, }, "node-releases": { - "id": 877, - "package_id": 449, + "id": 873, + "package_id": 445, }, "normalize-path": { "id": 30, "package_id": 25, }, "normalize-range": { - "id": 871, - "package_id": 445, + "id": 867, + "package_id": 441, }, "object-assign": { - "id": 845, + "id": 841, "package_id": 63, }, "object-hash": { @@ -21804,76 +21668,76 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 26, }, "object-inspect": { - "id": 535, - "package_id": 360, + "id": 531, + "package_id": 356, }, "object-keys": { - "id": 478, - "package_id": 318, + "id": 474, + "package_id": 314, }, "object.assign": { - "id": 758, - "package_id": 359, + "id": 754, + "package_id": 355, }, "object.entries": { - "id": 820, - "package_id": 409, + "id": 816, + "package_id": 405, }, "object.fromentries": { - "id": 821, - "package_id": 379, + "id": 817, + "package_id": 375, }, "object.groupby": { - "id": 462, - "package_id": 328, + "id": 458, + "package_id": 324, }, "object.hasown": { - "id": 822, - "package_id": 438, + "id": 818, + "package_id": 434, }, "object.values": { - "id": 823, - "package_id": 314, + "id": 819, + "package_id": 310, }, "once": { - "id": 198, - "package_id": 143, + "id": 199, + "package_id": 144, }, "optionator": { - "id": 356, - "package_id": 286, + "id": 352, + "package_id": 282, }, "p-limit": { - "id": 434, - "package_id": 300, + "id": 430, + "package_id": 296, }, "p-locate": { - "id": 433, - "package_id": 299, + "id": 429, + "package_id": 295, }, "pac-proxy-agent": { - "id": 206, - "package_id": 157, + "id": 207, + "package_id": 158, }, "pac-resolver": { - "id": 224, - "package_id": 158, + "id": 225, + "package_id": 159, }, "parent-module": { - "id": 299, - "package_id": 220, + "id": 295, + "package_id": 216, }, "parse-json": { - "id": 279, - "package_id": 202, + "id": 275, + "package_id": 198, }, "path-exists": { - "id": 432, - "package_id": 297, + "id": 428, + "package_id": 293, }, "path-is-absolute": { - "id": 395, - "package_id": 258, + "id": 391, + "package_id": 254, }, "path-key": { "id": 137, @@ -21888,15 +21752,15 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 66, }, "path-type": { - "id": 731, - "package_id": 403, + "id": 727, + "package_id": 399, }, "pend": { - "id": 257, - "package_id": 187, + "id": 258, + "package_id": 188, }, "picocolors": { - "id": 872, + "id": 868, "package_id": 9, }, "picomatch": { @@ -21912,8 +21776,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 58, }, "possible-typed-array-names": { - "id": 557, - "package_id": 335, + "id": 553, + "package_id": 331, }, "postcss": { "id": 8, @@ -21940,36 +21804,36 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 3, }, "postcss-value-parser": { - "id": 873, + "id": 869, "package_id": 24, }, "prelude-ls": { - "id": 427, - "package_id": 290, + "id": 423, + "package_id": 286, }, "progress": { - "id": 158, - "package_id": 179, + "id": 159, + "package_id": 180, }, "prop-types": { - "id": 824, - "package_id": 436, + "id": 820, + "package_id": 432, }, "proxy-agent": { - "id": 159, - "package_id": 146, + "id": 160, + "package_id": 147, }, "proxy-from-env": { - "id": 207, - "package_id": 156, + "id": 208, + "package_id": 157, }, "pump": { - "id": 180, - "package_id": 142, + "id": 181, + "package_id": 143, }, "punycode": { - "id": 417, - "package_id": 275, + "id": 413, + "package_id": 271, }, "puppeteer": { "id": 9, @@ -21977,15 +21841,15 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, "puppeteer-core": { "id": 154, - "package_id": 190, + "package_id": 191, }, "queue-microtask": { "id": 77, "package_id": 48, }, "queue-tick": { - "id": 190, - "package_id": 139, + "id": 191, + "package_id": 140, }, "react": { "id": 10, @@ -21996,8 +21860,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 108, }, "react-is": { - "id": 846, - "package_id": 437, + "id": 842, + "package_id": 433, }, "read-cache": { "id": 48, @@ -22008,68 +21872,68 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 52, }, "reflect.getprototypeof": { - "id": 777, - "package_id": 415, + "id": 773, + "package_id": 411, }, "regenerator-runtime": { - "id": 809, - "package_id": 432, + "id": 805, + "package_id": 428, }, "regexp.prototype.flags": { - "id": 838, - "package_id": 356, + "id": 834, + "package_id": 352, }, "require-directory": { - "id": 169, - "package_id": 121, + "id": 170, + "package_id": 122, }, "resolve": { "id": 19, "package_id": 16, }, "resolve-from": { - "id": 300, - "package_id": 219, + "id": 296, + "package_id": 215, }, "resolve-pkg-maps": { - "id": 703, - "package_id": 390, + "id": 699, + "package_id": 386, }, "reusify": { "id": 74, "package_id": 45, }, "rimraf": { - "id": 388, - "package_id": 256, + "id": 384, + "package_id": 252, }, "run-parallel": { "id": 76, "package_id": 47, }, "safe-array-concat": { - "id": 773, - "package_id": 354, + "id": 769, + "package_id": 350, }, "safe-regex-test": { - "id": 540, - "package_id": 352, + "id": 536, + "package_id": 348, }, "scheduler": { "id": 147, "package_id": 112, }, "semver": { - "id": 826, - "package_id": 313, + "id": 822, + "package_id": 309, }, "set-function-length": { - "id": 494, - "package_id": 327, + "id": 490, + "package_id": 323, }, "set-function-name": { - "id": 839, - "package_id": 357, + "id": 835, + "package_id": 353, }, "shebang-command": { "id": 138, @@ -22080,51 +21944,51 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 97, }, "side-channel": { - "id": 840, - "package_id": 367, + "id": 836, + "package_id": 363, }, "signal-exit": { "id": 136, "package_id": 92, }, "slash": { - "id": 730, - "package_id": 401, + "id": 726, + "package_id": 397, }, "smart-buffer": { - "id": 213, - "package_id": 149, + "id": 214, + "package_id": 150, }, "socks": { - "id": 211, - "package_id": 148, + "id": 212, + "package_id": 149, }, "socks-proxy-agent": { - "id": 208, - "package_id": 147, + "id": 209, + "package_id": 148, }, "source-map": { - "id": 234, - "package_id": 163, + "id": 235, + "package_id": 164, }, "source-map-js": { "id": 44, "package_id": 8, }, "sprintf-js": { - "id": 215, - "package_id": 151, + "id": 216, + "package_id": 152, }, "streamsearch": { - "id": 328, - "package_id": 240, + "id": 324, + "package_id": 236, }, "streamx": { - "id": 196, - "package_id": 135, + "id": 197, + "package_id": 136, }, "string-width": { - "id": 170, + "id": 171, "package_id": 78, }, "string-width-cjs": { @@ -22132,23 +21996,23 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 78, }, "string.prototype.matchall": { - "id": 827, - "package_id": 434, + "id": 823, + "package_id": 430, }, "string.prototype.trim": { - "id": 541, - "package_id": 351, + "id": 537, + "package_id": 347, }, "string.prototype.trimend": { - "id": 542, - "package_id": 350, + "id": 538, + "package_id": 346, }, "string.prototype.trimstart": { - "id": 543, - "package_id": 349, + "id": 539, + "package_id": 345, }, "strip-ansi": { - "id": 357, + "id": 353, "package_id": 76, }, "strip-ansi-cjs": { @@ -22156,24 +22020,24 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 76, }, "strip-bom": { - "id": 470, - "package_id": 309, + "id": 466, + "package_id": 305, }, "strip-json-comments": { - "id": 407, - "package_id": 266, + "id": 403, + "package_id": 262, }, "styled-jsx": { - "id": 305, - "package_id": 235, + "id": 301, + "package_id": 231, }, "sucrase": { "id": 20, "package_id": 56, }, "supports-color": { - "id": 438, - "package_id": 304, + "id": 434, + "package_id": 300, }, "supports-preserve-symlinks-flag": { "id": 53, @@ -22184,24 +22048,24 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 2, }, "tapable": { - "id": 705, - "package_id": 392, + "id": 701, + "package_id": 388, }, "tar-fs": { - "id": 160, - "package_id": 130, + "id": 161, + "package_id": 131, }, "tar-stream": { - "id": 181, - "package_id": 141, + "id": 182, + "package_id": 142, }, "text-decoder": { - "id": 191, - "package_id": 137, + "id": 192, + "package_id": 138, }, "text-table": { - "id": 358, - "package_id": 285, + "id": 354, + "package_id": 281, }, "thenify": { "id": 100, @@ -22212,127 +22076,115 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 60, }, "through": { - "id": 177, - "package_id": 126, + "id": 178, + "package_id": 127, }, "to-regex-range": { "id": 64, "package_id": 36, }, - "tr46": { - "id": 271, - "package_id": 197, - }, "ts-api-utils": { - "id": 718, - "package_id": 398, + "id": 714, + "package_id": 394, }, "ts-interface-checker": { "id": 96, "package_id": 57, }, "tsconfig-paths": { - "id": 465, - "package_id": 308, + "id": 461, + "package_id": 304, }, "tslib": { - "id": 322, - "package_id": 167, + "id": 318, + "package_id": 168, }, "type-check": { - "id": 428, - "package_id": 289, + "id": 424, + "package_id": 285, }, "type-fest": { - "id": 408, - "package_id": 269, + "id": 404, + "package_id": 265, }, "typed-array-buffer": { - "id": 544, - "package_id": 348, + "id": 540, + "package_id": 344, }, "typed-array-byte-length": { - "id": 545, - "package_id": 347, + "id": 541, + "package_id": 343, }, "typed-array-byte-offset": { - "id": 546, - "package_id": 346, + "id": 542, + "package_id": 342, }, "typed-array-length": { - "id": 547, - "package_id": 344, + "id": 543, + "package_id": 340, }, "typescript": { "id": 13, "package_id": 1, }, "unbox-primitive": { - "id": 548, - "package_id": 336, + "id": 544, + "package_id": 332, }, "unbzip2-stream": { - "id": 161, - "package_id": 125, + "id": 162, + "package_id": 126, }, "undici-types": { - "id": 254, - "package_id": 183, + "id": 255, + "package_id": 184, }, "universalify": { - "id": 246, - "package_id": 172, + "id": 247, + "package_id": 173, }, "update-browserslist-db": { - "id": 878, - "package_id": 448, + "id": 874, + "package_id": 444, }, "uri-js": { - "id": 416, - "package_id": 274, + "id": 412, + "package_id": 270, }, "urlpattern-polyfill": { - "id": 274, - "package_id": 199, + "id": 269, + "package_id": 195, }, "util-deprecate": { "id": 37, "package_id": 4, }, - "webidl-conversions": { - "id": 272, - "package_id": 196, - }, - "whatwg-url": { - "id": 269, - "package_id": 195, - }, "which": { "id": 139, "package_id": 94, }, "which-boxed-primitive": { - "id": 561, - "package_id": 337, + "id": 557, + "package_id": 333, }, "which-builtin-type": { - "id": 785, - "package_id": 416, + "id": 781, + "package_id": 412, }, "which-collection": { - "id": 796, - "package_id": 417, + "id": 792, + "package_id": 413, }, "which-typed-array": { - "id": 549, - "package_id": 330, + "id": 545, + "package_id": 326, }, "word-wrap": { - "id": 423, - "package_id": 291, + "id": 419, + "package_id": 287, }, "wrap-ansi": { - "id": 175, + "id": 176, "package_id": 75, }, "wrap-ansi-cjs": { @@ -22340,40 +22192,44 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 75, }, "wrappy": { - "id": 199, - "package_id": 144, + "id": 200, + "package_id": 145, }, "ws": { "id": 265, - "package_id": 191, + "package_id": 192, }, "y18n": { - "id": 171, - "package_id": 120, + "id": 172, + "package_id": 121, }, "yallist": { - "id": 165, - "package_id": 117, + "id": 166, + "package_id": 118, }, "yaml": { "id": 38, "package_id": 12, }, "yargs": { - "id": 162, - "package_id": 118, + "id": 163, + "package_id": 119, }, "yargs-parser": { - "id": 172, - "package_id": 119, + "id": 173, + "package_id": 120, }, "yauzl": { - "id": 251, - "package_id": 184, + "id": 252, + "package_id": 185, }, "yocto-queue": { - "id": 435, - "package_id": 301, + "id": 431, + "package_id": 297, + }, + "zod": { + "id": 270, + "package_id": 194, }, }, "depth": 0, @@ -22383,8 +22239,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "@types/node": { - "id": 866, - "package_id": 182, + "id": 862, + "package_id": 183, }, }, "depth": 1, @@ -22394,8 +22250,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "postcss": { - "id": 303, - "package_id": 238, + "id": 299, + "package_id": 234, }, }, "depth": 1, @@ -22405,8 +22261,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "@types/node": { - "id": 867, - "package_id": 182, + "id": 863, + "package_id": 183, }, }, "depth": 1, @@ -22416,12 +22272,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "doctrine": { - "id": 815, - "package_id": 383, + "id": 811, + "package_id": 379, }, "resolve": { - "id": 825, - "package_id": 435, + "id": 821, + "package_id": 431, }, }, "depth": 1, @@ -22431,12 +22287,12 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "debug": { - "id": 453, - "package_id": 381, + "id": 449, + "package_id": 377, }, "doctrine": { - "id": 454, - "package_id": 383, + "id": 450, + "package_id": 379, }, }, "depth": 1, @@ -22446,8 +22302,8 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "debug": { - "id": 678, - "package_id": 381, + "id": 674, + "package_id": 377, }, }, "depth": 1, @@ -22457,27 +22313,16 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` { "dependencies": { "debug": { - "id": 263, - "package_id": 189, - }, - }, - "depth": 1, - "id": 7, - "path": "node_modules/puppeteer-core/node_modules", - }, - { - "dependencies": { - "debug": { - "id": 156, - "package_id": 189, + "id": 157, + "package_id": 190, }, "semver": { - "id": 163, - "package_id": 115, + "id": 164, + "package_id": 116, }, }, "depth": 1, - "id": 8, + "id": 7, "path": "node_modules/@puppeteer/browsers/node_modules", }, { @@ -22488,7 +22333,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 9, + "id": 8, "path": "node_modules/chokidar/node_modules", }, { @@ -22499,7 +22344,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 10, + "id": 9, "path": "node_modules/fast-glob/node_modules", }, { @@ -22510,18 +22355,18 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 11, + "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, { "dependencies": { "debug": { - "id": 676, - "package_id": 381, + "id": 672, + "package_id": 377, }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/eslint-module-utils/node_modules", }, { @@ -22532,44 +22377,44 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/glob/node_modules", }, { "dependencies": { "minimatch": { - "id": 717, - "package_id": 399, + "id": 713, + "package_id": 395, }, "semver": { - "id": 715, - "package_id": 115, + "id": 711, + "package_id": 116, }, }, "depth": 1, - "id": 14, + "id": 13, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 15, + "id": 14, "path": "node_modules/@puppeteer/browsers/node_modules/semver/node_modules", }, { "dependencies": { "glob": { - "id": 389, - "package_id": 257, + "id": 385, + "package_id": 253, }, }, "depth": 1, - "id": 16, + "id": 15, "path": "node_modules/rimraf/node_modules", }, { @@ -22580,7 +22425,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 2, - "id": 17, + "id": 16, "path": "node_modules/glob/node_modules/minimatch/node_modules", }, { @@ -22591,40 +22436,40 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 18, + "id": 17, "path": "node_modules/path-scurry/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", }, { "dependencies": { "brace-expansion": { - "id": 724, + "id": 720, "package_id": 70, }, }, "depth": 2, - "id": 20, + "id": 19, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, { "dependencies": { "@types/node": { - "id": 253, - "package_id": 182, + "id": 254, + "package_id": 183, }, }, "depth": 1, - "id": 21, + "id": 20, "path": "node_modules/@types/yauzl/node_modules", }, { @@ -22635,7 +22480,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 22, + "id": 21, "path": "node_modules/string-width/node_modules", }, { @@ -22654,18 +22499,18 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 23, + "id": 22, "path": "node_modules/@isaacs/cliui/node_modules", }, { "dependencies": { "chalk": { - "id": 289, - "package_id": 208, + "id": 285, + "package_id": 204, }, }, "depth": 1, - "id": 24, + "id": 23, "path": "node_modules/@babel/highlight/node_modules", }, { @@ -22676,7 +22521,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 25, + "id": 24, "path": "node_modules/string-width-cjs/node_modules", }, { @@ -22687,7 +22532,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 2, - "id": 26, + "id": 25, "path": "node_modules/@isaacs/cliui/node_modules/strip-ansi/node_modules", }, { @@ -22698,59 +22543,59 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 2, - "id": 27, + "id": 26, "path": "node_modules/@isaacs/cliui/node_modules/wrap-ansi/node_modules", }, { "dependencies": { "ansi-styles": { - "id": 292, - "package_id": 212, + "id": 288, + "package_id": 208, }, "escape-string-regexp": { - "id": 293, - "package_id": 211, + "id": 289, + "package_id": 207, }, "supports-color": { - "id": 294, - "package_id": 209, + "id": 290, + "package_id": 205, }, }, "depth": 2, - "id": 28, + "id": 27, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules", }, { "dependencies": { "color-convert": { - "id": 296, - "package_id": 213, + "id": 292, + "package_id": 209, }, }, "depth": 3, - "id": 29, + "id": 28, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules", }, { "dependencies": { "has-flag": { - "id": 295, - "package_id": 210, + "id": 291, + "package_id": 206, }, }, "depth": 3, - "id": 30, + "id": 29, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/supports-color/node_modules", }, { "dependencies": { "color-name": { - "id": 297, - "package_id": 214, + "id": 293, + "package_id": 210, }, }, "depth": 4, - "id": 31, + "id": 30, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/node_modules", }, ], diff --git a/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap b/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap index e42b3f01872d6..c23871f444a62 100644 --- a/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap +++ b/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap @@ -14,7 +14,7 @@ exports[`next build works: bun 1`] = ` "name": "@types/node", "version": "==20.7.0", }, - "package_id": 456, + "package_id": 452, }, { "behavior": { @@ -27,7 +27,7 @@ exports[`next build works: bun 1`] = ` "name": "@types/react", "version": "==18.2.22", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { @@ -40,7 +40,7 @@ exports[`next build works: bun 1`] = ` "name": "@types/react-dom", "version": "==18.2.7", }, - "package_id": 451, + "package_id": 447, }, { "behavior": { @@ -53,7 +53,7 @@ exports[`next build works: bun 1`] = ` "name": "autoprefixer", "version": "==10.4.16", }, - "package_id": 444, + "package_id": 440, }, { "behavior": { @@ -66,7 +66,7 @@ exports[`next build works: bun 1`] = ` "name": "bun-types", "version": ">=1.0.3 <2.0.0", }, - "package_id": 442, + "package_id": 438, }, { "behavior": { @@ -79,7 +79,7 @@ exports[`next build works: bun 1`] = ` "name": "eslint", "version": "==8.50.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { @@ -92,7 +92,7 @@ exports[`next build works: bun 1`] = ` "name": "eslint-config-next", "version": "==14.1.3", }, - "package_id": 241, + "package_id": 237, }, { "behavior": { @@ -105,7 +105,7 @@ exports[`next build works: bun 1`] = ` "name": "next", "version": "==14.1.3", }, - "package_id": 223, + "package_id": 219, }, { "behavior": { @@ -125,11 +125,11 @@ exports[`next build works: bun 1`] = ` "normal": true, }, "id": 9, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer", "npm": { "name": "puppeteer", - "version": "==22.4.1", + "version": "==22.12.0", }, "package_id": 113, }, @@ -2008,221 +2008,234 @@ exports[`next build works: bun 1`] = ` "name": "cosmiconfig", "version": "==9.0.0", }, - "package_id": 201, + "package_id": 197, }, { "behavior": { "normal": true, }, "id": 154, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer-core", "npm": { "name": "puppeteer-core", - "version": "==22.4.1", + "version": "==22.12.0", }, - "package_id": 190, + "package_id": 191, }, { "behavior": { "normal": true, }, "id": 155, - "literal": "2.1.0", + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, "id": 156, + "literal": "0.0.1299070", + "name": "devtools-protocol", + "npm": { + "name": "devtools-protocol", + "version": "==0.0.1299070", + }, + "package_id": 114, + }, + { + "behavior": { + "normal": true, + }, + "id": 157, "literal": "4.3.4", "name": "debug", "npm": { "name": "debug", "version": "==4.3.4", }, - "package_id": 189, + "package_id": 190, }, { "behavior": { "normal": true, }, - "id": 157, + "id": 158, "literal": "2.0.1", "name": "extract-zip", "npm": { "name": "extract-zip", "version": "==2.0.1", }, - "package_id": 180, + "package_id": 181, }, { "behavior": { "normal": true, }, - "id": 158, + "id": 159, "literal": "2.0.3", "name": "progress", "npm": { "name": "progress", "version": "==2.0.3", }, - "package_id": 179, + "package_id": 180, }, { "behavior": { "normal": true, }, - "id": 159, + "id": 160, "literal": "6.4.0", "name": "proxy-agent", "npm": { "name": "proxy-agent", "version": "==6.4.0", }, - "package_id": 146, + "package_id": 147, }, { "behavior": { "normal": true, }, - "id": 160, + "id": 161, "literal": "3.0.5", "name": "tar-fs", "npm": { "name": "tar-fs", "version": "==3.0.5", }, - "package_id": 130, + "package_id": 131, }, { "behavior": { "normal": true, }, - "id": 161, + "id": 162, "literal": "1.4.3", "name": "unbzip2-stream", "npm": { "name": "unbzip2-stream", "version": "==1.4.3", }, - "package_id": 125, + "package_id": 126, }, { "behavior": { "normal": true, }, - "id": 162, + "id": 163, "literal": "17.7.2", "name": "yargs", "npm": { "name": "yargs", "version": "==17.7.2", }, - "package_id": 118, + "package_id": 119, }, { "behavior": { "normal": true, }, - "id": 163, + "id": 164, "literal": "7.6.0", "name": "semver", "npm": { "name": "semver", "version": "==7.6.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 164, + "id": 165, "literal": "^6.0.0", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=6.0.0 <7.0.0", }, - "package_id": 116, + "package_id": 117, }, { "behavior": { "normal": true, }, - "id": 165, + "id": 166, "literal": "^4.0.0", "name": "yallist", "npm": { "name": "yallist", "version": ">=4.0.0 <5.0.0", }, - "package_id": 117, + "package_id": 118, }, { "behavior": { "normal": true, }, - "id": 166, + "id": 167, "literal": "^8.0.1", "name": "cliui", "npm": { "name": "cliui", "version": ">=8.0.1 <9.0.0", }, - "package_id": 124, + "package_id": 125, }, { "behavior": { "normal": true, }, - "id": 167, + "id": 168, "literal": "^3.1.1", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.1 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 168, + "id": 169, "literal": "^2.0.5", "name": "get-caller-file", "npm": { "name": "get-caller-file", "version": ">=2.0.5 <3.0.0", }, - "package_id": 122, + "package_id": 123, }, { "behavior": { "normal": true, }, - "id": 169, + "id": 170, "literal": "^2.1.1", "name": "require-directory", "npm": { "name": "require-directory", "version": ">=2.1.1 <3.0.0", }, - "package_id": 121, + "package_id": 122, }, { "behavior": { "normal": true, }, - "id": 170, + "id": 171, "literal": "^4.2.3", "name": "string-width", "npm": { @@ -2235,33 +2248,33 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 171, + "id": 172, "literal": "^5.0.5", "name": "y18n", "npm": { "name": "y18n", "version": ">=5.0.5 <6.0.0", }, - "package_id": 120, + "package_id": 121, }, { "behavior": { "normal": true, }, - "id": 172, + "id": 173, "literal": "^21.1.1", "name": "yargs-parser", "npm": { "name": "yargs-parser", "version": ">=21.1.1 <22.0.0", }, - "package_id": 119, + "package_id": 120, }, { "behavior": { "normal": true, }, - "id": 173, + "id": 174, "literal": "^4.2.0", "name": "string-width", "npm": { @@ -2274,7 +2287,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 174, + "id": 175, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -2287,7 +2300,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 175, + "id": 176, "literal": "^7.0.0", "name": "wrap-ansi", "npm": { @@ -2300,1130 +2313,1117 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 176, + "id": 177, "literal": "^5.2.1", "name": "buffer", "npm": { "name": "buffer", "version": ">=5.2.1 <6.0.0", }, - "package_id": 127, + "package_id": 128, }, { "behavior": { "normal": true, }, - "id": 177, + "id": 178, "literal": "^2.3.8", "name": "through", "npm": { "name": "through", "version": ">=2.3.8 <3.0.0", }, - "package_id": 126, + "package_id": 127, }, { "behavior": { "normal": true, }, - "id": 178, + "id": 179, "literal": "^1.3.1", "name": "base64-js", "npm": { "name": "base64-js", "version": ">=1.3.1 <2.0.0", }, - "package_id": 129, + "package_id": 130, }, { "behavior": { "normal": true, }, - "id": 179, + "id": 180, "literal": "^1.1.13", "name": "ieee754", "npm": { "name": "ieee754", "version": ">=1.1.13 <2.0.0", }, - "package_id": 128, + "package_id": 129, }, { "behavior": { "normal": true, }, - "id": 180, + "id": 181, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 181, + "id": 182, "literal": "^3.1.5", "name": "tar-stream", "npm": { "name": "tar-stream", "version": ">=3.1.5 <4.0.0", }, - "package_id": 141, + "package_id": 142, }, { "behavior": { "optional": true, }, - "id": 182, + "id": 183, "literal": "^2.1.1", "name": "bare-fs", "npm": { "name": "bare-fs", "version": ">=2.1.1 <3.0.0", }, - "package_id": 133, + "package_id": 134, }, { "behavior": { "optional": true, }, - "id": 183, + "id": 184, "literal": "^2.1.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.1.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 184, + "id": 185, "literal": "^2.1.0", "name": "bare-os", "npm": { "name": "bare-os", "version": ">=2.1.0 <3.0.0", }, - "package_id": 132, + "package_id": 133, }, { "behavior": { "normal": true, }, - "id": 185, + "id": 186, "literal": "^2.0.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.0.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 186, + "id": 187, "literal": "^2.0.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.0.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 187, + "id": 188, "literal": "^2.0.0", "name": "bare-stream", "npm": { "name": "bare-stream", "version": ">=2.0.0 <3.0.0", }, - "package_id": 134, + "package_id": 135, }, { "behavior": { "normal": true, }, - "id": 188, + "id": 189, "literal": "^2.18.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.18.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 189, + "id": 190, "literal": "^1.3.2", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.3.2 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 190, + "id": 191, "literal": "^1.0.1", "name": "queue-tick", "npm": { "name": "queue-tick", "version": ">=1.0.1 <2.0.0", }, - "package_id": 139, + "package_id": 140, }, { "behavior": { "normal": true, }, - "id": 191, + "id": 192, "literal": "^1.1.0", "name": "text-decoder", "npm": { "name": "text-decoder", "version": ">=1.1.0 <2.0.0", }, - "package_id": 137, + "package_id": 138, }, { "behavior": { "optional": true, }, - "id": 192, + "id": 193, "literal": "^2.2.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.2.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 193, + "id": 194, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 194, + "id": 195, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 195, + "id": 196, "literal": "^1.2.0", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.2.0 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 196, + "id": 197, "literal": "^2.15.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.15.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 197, + "id": 198, "literal": "^1.1.0", "name": "end-of-stream", "npm": { "name": "end-of-stream", "version": ">=1.1.0 <2.0.0", }, - "package_id": 145, + "package_id": 146, }, { "behavior": { "normal": true, }, - "id": 198, + "id": 199, "literal": "^1.3.1", "name": "once", "npm": { "name": "once", "version": ">=1.3.1 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 199, + "id": 200, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 200, + "id": 201, "literal": "^1.4.0", "name": "once", "npm": { "name": "once", "version": ">=1.4.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 201, + "id": 202, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 202, + "id": 203, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 203, + "id": 204, "literal": "^7.0.1", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 204, + "id": 205, "literal": "^7.0.3", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.3 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 205, + "id": 206, "literal": "^7.14.1", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=7.14.1 <8.0.0", }, - "package_id": 178, + "package_id": 179, }, { "behavior": { "normal": true, }, - "id": 206, + "id": 207, "literal": "^7.0.1", "name": "pac-proxy-agent", "npm": { "name": "pac-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 157, + "package_id": 158, }, { "behavior": { "normal": true, }, - "id": 207, + "id": 208, "literal": "^1.1.0", "name": "proxy-from-env", "npm": { "name": "proxy-from-env", "version": ">=1.1.0 <2.0.0", }, - "package_id": 156, + "package_id": 157, }, { "behavior": { "normal": true, }, - "id": 208, + "id": 209, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 209, + "id": 210, "literal": "^7.1.1", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.1 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 210, + "id": 211, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 211, + "id": 212, "literal": "^2.7.1", "name": "socks", "npm": { "name": "socks", "version": ">=2.7.1 <3.0.0", }, - "package_id": 148, + "package_id": 149, }, { "behavior": { "normal": true, }, - "id": 212, + "id": 213, "literal": "^9.0.5", "name": "ip-address", "npm": { "name": "ip-address", "version": ">=9.0.5 <10.0.0", }, - "package_id": 150, + "package_id": 151, }, { "behavior": { "normal": true, }, - "id": 213, + "id": 214, "literal": "^4.2.0", "name": "smart-buffer", "npm": { "name": "smart-buffer", "version": ">=4.2.0 <5.0.0", }, - "package_id": 149, + "package_id": 150, }, { "behavior": { "normal": true, }, - "id": 214, + "id": 215, "literal": "1.1.0", "name": "jsbn", "npm": { "name": "jsbn", "version": "==1.1.0", }, - "package_id": 152, + "package_id": 153, }, { "behavior": { "normal": true, }, - "id": 215, + "id": 216, "literal": "^1.1.3", "name": "sprintf-js", "npm": { "name": "sprintf-js", "version": ">=1.1.3 <2.0.0", }, - "package_id": 151, + "package_id": 152, }, { "behavior": { "normal": true, }, - "id": 216, + "id": 217, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 217, + "id": 218, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 218, + "id": 219, "literal": "^0.23.0", "name": "@tootallnate/quickjs-emscripten", "npm": { "name": "@tootallnate/quickjs-emscripten", "version": ">=0.23.0 <0.24.0", }, - "package_id": 177, + "package_id": 178, }, { "behavior": { "normal": true, }, - "id": 219, + "id": 220, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 220, + "id": 221, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 221, + "id": 222, "literal": "^6.0.1", "name": "get-uri", "npm": { "name": "get-uri", "version": ">=6.0.1 <7.0.0", }, - "package_id": 170, + "package_id": 171, }, { "behavior": { "normal": true, }, - "id": 222, + "id": 223, "literal": "^7.0.0", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.0 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 223, + "id": 224, "literal": "^7.0.2", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.2 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 224, + "id": 225, "literal": "^7.0.0", "name": "pac-resolver", "npm": { "name": "pac-resolver", "version": ">=7.0.0 <8.0.0", }, - "package_id": 158, + "package_id": 159, }, { "behavior": { "normal": true, }, - "id": 225, + "id": 226, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 226, + "id": 227, "literal": "^5.0.0", "name": "degenerator", "npm": { "name": "degenerator", "version": ">=5.0.0 <6.0.0", }, - "package_id": 160, + "package_id": 161, }, { "behavior": { "normal": true, }, - "id": 227, + "id": 228, "literal": "^2.0.2", "name": "netmask", "npm": { "name": "netmask", "version": ">=2.0.2 <3.0.0", }, - "package_id": 159, + "package_id": 160, }, { "behavior": { "normal": true, }, - "id": 228, + "id": 229, "literal": "^0.13.4", "name": "ast-types", "npm": { "name": "ast-types", "version": ">=0.13.4 <0.14.0", }, - "package_id": 166, + "package_id": 167, }, { "behavior": { "normal": true, }, - "id": 229, + "id": 230, "literal": "^2.1.0", "name": "escodegen", "npm": { "name": "escodegen", "version": ">=2.1.0 <3.0.0", }, - "package_id": 162, + "package_id": 163, }, { "behavior": { "normal": true, }, - "id": 230, + "id": 231, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "normal": true, }, - "id": 231, + "id": 232, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 232, + "id": 233, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 233, + "id": 234, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "optional": true, }, - "id": 234, + "id": 235, "literal": "~0.6.1", "name": "source-map", "npm": { "name": "source-map", "version": ">=0.6.1 <0.7.0", }, - "package_id": 163, + "package_id": 164, }, { "behavior": { "normal": true, }, - "id": 235, + "id": 236, "literal": "^2.0.1", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.0.1 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 236, + "id": 237, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 237, + "id": 238, "literal": "4", "name": "debug", "npm": { "name": "debug", "version": "<5.0.0 >=4.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 238, + "id": 239, "literal": "^7.1.0", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.0 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 239, + "id": 240, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 240, + "id": 241, "literal": "^5.0.2", "name": "basic-ftp", "npm": { "name": "basic-ftp", "version": ">=5.0.2 <6.0.0", }, - "package_id": 176, + "package_id": 177, }, { "behavior": { "normal": true, }, - "id": 241, + "id": 242, "literal": "^6.0.2", "name": "data-uri-to-buffer", "npm": { "name": "data-uri-to-buffer", "version": ">=6.0.2 <7.0.0", }, - "package_id": 175, + "package_id": 176, }, { "behavior": { "normal": true, }, - "id": 242, + "id": 243, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 243, + "id": 244, "literal": "^11.2.0", "name": "fs-extra", "npm": { "name": "fs-extra", "version": ">=11.2.0 <12.0.0", }, - "package_id": 171, + "package_id": 172, }, { "behavior": { "normal": true, }, - "id": 244, + "id": 245, "literal": "^4.2.0", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.0 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 245, + "id": 246, "literal": "^6.0.1", "name": "jsonfile", "npm": { "name": "jsonfile", "version": ">=6.0.1 <7.0.0", }, - "package_id": 173, + "package_id": 174, }, { "behavior": { "normal": true, }, - "id": 246, + "id": 247, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "normal": true, }, - "id": 247, + "id": 248, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "optional": true, }, - "id": 248, + "id": 249, "literal": "^4.1.6", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.1.6 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 249, + "id": 250, "literal": "^4.1.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.1.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 250, + "id": 251, "literal": "^5.1.0", "name": "get-stream", "npm": { "name": "get-stream", "version": ">=5.1.0 <6.0.0", }, - "package_id": 188, + "package_id": 189, }, { "behavior": { "normal": true, }, - "id": 251, + "id": 252, "literal": "^2.10.0", "name": "yauzl", "npm": { "name": "yauzl", "version": ">=2.10.0 <3.0.0", }, - "package_id": 184, + "package_id": 185, }, { "behavior": { "optional": true, }, - "id": 252, + "id": 253, "literal": "^2.9.1", "name": "@types/yauzl", "npm": { "name": "@types/yauzl", "version": ">=2.9.1 <3.0.0", }, - "package_id": 181, + "package_id": 182, }, { "behavior": { "normal": true, }, - "id": 253, + "id": 254, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 254, + "id": 255, "literal": "~5.26.4", "name": "undici-types", "npm": { "name": "undici-types", "version": ">=5.26.4 <5.27.0", }, - "package_id": 183, + "package_id": 184, }, { "behavior": { "normal": true, }, - "id": 255, + "id": 256, "literal": "~1.1.0", "name": "fd-slicer", "npm": { "name": "fd-slicer", "version": ">=1.1.0 <1.2.0", }, - "package_id": 186, + "package_id": 187, }, { "behavior": { "normal": true, }, - "id": 256, + "id": 257, "literal": "~0.2.3", "name": "buffer-crc32", "npm": { "name": "buffer-crc32", "version": ">=0.2.3 <0.3.0", }, - "package_id": 185, + "package_id": 186, }, { "behavior": { "normal": true, }, - "id": 257, + "id": 258, "literal": "~1.2.0", "name": "pend", "npm": { "name": "pend", "version": ">=1.2.0 <1.3.0", }, - "package_id": 187, + "package_id": 188, }, { "behavior": { "normal": true, }, - "id": 258, + "id": 259, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 259, + "id": 260, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 260, - "literal": "2.1.0", + "id": 261, + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, - "id": 261, - "literal": "0.5.12", + "id": 262, + "literal": "0.5.24", "name": "chromium-bidi", "npm": { "name": "chromium-bidi", - "version": "==0.5.12", - }, - "package_id": 198, - }, - { - "behavior": { - "normal": true, - }, - "id": 262, - "literal": "4.0.0", - "name": "cross-fetch", - "npm": { - "name": "cross-fetch", - "version": "==4.0.0", + "version": "==0.5.24", }, "package_id": 193, }, @@ -3432,39 +3432,39 @@ exports[`next build works: bun 1`] = ` "normal": true, }, "id": 263, - "literal": "4.3.4", + "literal": "4.3.5", "name": "debug", "npm": { "name": "debug", - "version": "==4.3.4", + "version": "==4.3.5", }, - "package_id": 189, + "package_id": 154, }, { "behavior": { "normal": true, }, "id": 264, - "literal": "0.0.1249869", + "literal": "0.0.1299070", "name": "devtools-protocol", "npm": { "name": "devtools-protocol", - "version": "==0.0.1249869", + "version": "==0.0.1299070", }, - "package_id": 192, + "package_id": 114, }, { "behavior": { "normal": true, }, "id": 265, - "literal": "8.16.0", + "literal": "8.17.1", "name": "ws", "npm": { "name": "ws", - "version": "==8.16.0", + "version": "==8.17.1", }, - "package_id": 191, + "package_id": 192, }, { "behavior": { @@ -3499,164 +3499,111 @@ exports[`next build works: bun 1`] = ` "normal": true, }, "id": 268, - "literal": "^2.6.12", - "name": "node-fetch", + "literal": "3.0.1", + "name": "mitt", "npm": { - "name": "node-fetch", - "version": ">=2.6.12 <3.0.0", + "name": "mitt", + "version": "==3.0.1", }, - "package_id": 194, + "package_id": 196, }, { "behavior": { "normal": true, }, "id": 269, - "literal": "^5.0.0", - "name": "whatwg-url", + "literal": "10.0.0", + "name": "urlpattern-polyfill", "npm": { - "name": "whatwg-url", - "version": ">=5.0.0 <6.0.0", + "name": "urlpattern-polyfill", + "version": "==10.0.0", }, "package_id": 195, }, - { - "behavior": { - "optional": true, - "peer": true, - }, - "id": 270, - "literal": "^0.1.0", - "name": "encoding", - "npm": { - "name": "encoding", - "version": ">=0.1.0 <0.2.0", - }, - "package_id": null, - }, - { - "behavior": { - "normal": true, - }, - "id": 271, - "literal": "~0.0.3", - "name": "tr46", - "npm": { - "name": "tr46", - "version": ">=0.0.3 <0.1.0", - }, - "package_id": 197, - }, - { - "behavior": { - "normal": true, - }, - "id": 272, - "literal": "^3.0.0", - "name": "webidl-conversions", - "npm": { - "name": "webidl-conversions", - "version": ">=3.0.0 <4.0.0", - }, - "package_id": 196, - }, - { - "behavior": { - "normal": true, - }, - "id": 273, - "literal": "3.0.1", - "name": "mitt", - "npm": { - "name": "mitt", - "version": "==3.0.1", - }, - "package_id": 200, - }, { "behavior": { "normal": true, }, - "id": 274, - "literal": "10.0.0", - "name": "urlpattern-polyfill", + "id": 270, + "literal": "3.23.8", + "name": "zod", "npm": { - "name": "urlpattern-polyfill", - "version": "==10.0.0", + "name": "zod", + "version": "==3.23.8", }, - "package_id": 199, + "package_id": 194, }, { "behavior": { "peer": true, }, - "id": 275, + "id": 271, "literal": "*", "name": "devtools-protocol", "npm": { "name": "devtools-protocol", "version": ">=0.0.0", }, - "package_id": 192, + "package_id": 114, }, { "behavior": { "normal": true, }, - "id": 276, + "id": 272, "literal": "^2.2.1", "name": "env-paths", "npm": { "name": "env-paths", "version": ">=2.2.1 <3.0.0", }, - "package_id": 222, + "package_id": 218, }, { "behavior": { "normal": true, }, - "id": 277, + "id": 273, "literal": "^3.3.0", "name": "import-fresh", "npm": { "name": "import-fresh", "version": ">=3.3.0 <4.0.0", }, - "package_id": 218, + "package_id": 214, }, { "behavior": { "normal": true, }, - "id": 278, + "id": 274, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 279, + "id": 275, "literal": "^5.2.0", "name": "parse-json", "npm": { "name": "parse-json", "version": ">=5.2.0 <6.0.0", }, - "package_id": 202, + "package_id": 198, }, { "behavior": { "optional": true, "peer": true, }, - "id": 280, + "id": 276, "literal": ">=4.9.5", "name": "typescript", "npm": { @@ -3669,46 +3616,46 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 281, + "id": 277, "literal": "^7.0.0", "name": "@babel/code-frame", "npm": { "name": "@babel/code-frame", "version": ">=7.0.0 <8.0.0", }, - "package_id": 206, + "package_id": 202, }, { "behavior": { "normal": true, }, - "id": 282, + "id": 278, "literal": "^1.3.1", "name": "error-ex", "npm": { "name": "error-ex", "version": ">=1.3.1 <2.0.0", }, - "package_id": 204, + "package_id": 200, }, { "behavior": { "normal": true, }, - "id": 283, + "id": 279, "literal": "^2.3.0", "name": "json-parse-even-better-errors", "npm": { "name": "json-parse-even-better-errors", "version": ">=2.3.0 <3.0.0", }, - "package_id": 203, + "package_id": 199, }, { "behavior": { "normal": true, }, - "id": 284, + "id": 280, "literal": "^1.1.6", "name": "lines-and-columns", "npm": { @@ -3721,33 +3668,33 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 285, + "id": 281, "literal": "^0.2.1", "name": "is-arrayish", "npm": { "name": "is-arrayish", "version": ">=0.2.1 <0.3.0", }, - "package_id": 205, + "package_id": 201, }, { "behavior": { "normal": true, }, - "id": 286, + "id": 282, "literal": "^7.24.7", "name": "@babel/highlight", "npm": { "name": "@babel/highlight", "version": ">=7.24.7 <8.0.0", }, - "package_id": 207, + "package_id": 203, }, { "behavior": { "normal": true, }, - "id": 287, + "id": 283, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -3760,33 +3707,33 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 288, + "id": 284, "literal": "^7.24.7", "name": "@babel/helper-validator-identifier", "npm": { "name": "@babel/helper-validator-identifier", "version": ">=7.24.7 <8.0.0", }, - "package_id": 215, + "package_id": 211, }, { "behavior": { "normal": true, }, - "id": 289, + "id": 285, "literal": "^2.4.2", "name": "chalk", "npm": { "name": "chalk", "version": ">=2.4.2 <3.0.0", }, - "package_id": 208, + "package_id": 204, }, { "behavior": { "normal": true, }, - "id": 290, + "id": 286, "literal": "^4.0.0", "name": "js-tokens", "npm": { @@ -3799,7 +3746,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 291, + "id": 287, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -3812,346 +3759,346 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 292, + "id": 288, "literal": "^3.2.1", "name": "ansi-styles", "npm": { "name": "ansi-styles", "version": ">=3.2.1 <4.0.0", }, - "package_id": 212, + "package_id": 208, }, { "behavior": { "normal": true, }, - "id": 293, + "id": 289, "literal": "^1.0.5", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=1.0.5 <2.0.0", }, - "package_id": 211, + "package_id": 207, }, { "behavior": { "normal": true, }, - "id": 294, + "id": 290, "literal": "^5.3.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=5.3.0 <6.0.0", }, - "package_id": 209, + "package_id": 205, }, { "behavior": { "normal": true, }, - "id": 295, + "id": 291, "literal": "^3.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=3.0.0 <4.0.0", }, - "package_id": 210, + "package_id": 206, }, { "behavior": { "normal": true, }, - "id": 296, + "id": 292, "literal": "^1.9.0", "name": "color-convert", "npm": { "name": "color-convert", "version": ">=1.9.0 <2.0.0", }, - "package_id": 213, + "package_id": 209, }, { "behavior": { "normal": true, }, - "id": 297, + "id": 293, "literal": "1.1.3", "name": "color-name", "npm": { "name": "color-name", "version": "==1.1.3", }, - "package_id": 214, + "package_id": 210, }, { "behavior": { "normal": true, }, - "id": 298, + "id": 294, "literal": "^2.0.1", "name": "argparse", "npm": { "name": "argparse", "version": ">=2.0.1 <3.0.0", }, - "package_id": 217, + "package_id": 213, }, { "behavior": { "normal": true, }, - "id": 299, + "id": 295, "literal": "^1.0.0", "name": "parent-module", "npm": { "name": "parent-module", "version": ">=1.0.0 <2.0.0", }, - "package_id": 220, + "package_id": 216, }, { "behavior": { "normal": true, }, - "id": 300, + "id": 296, "literal": "^4.0.0", "name": "resolve-from", "npm": { "name": "resolve-from", "version": ">=4.0.0 <5.0.0", }, - "package_id": 219, + "package_id": 215, }, { "behavior": { "normal": true, }, - "id": 301, + "id": 297, "literal": "^3.0.0", "name": "callsites", "npm": { "name": "callsites", "version": ">=3.0.0 <4.0.0", }, - "package_id": 221, + "package_id": 217, }, { "behavior": { "normal": true, }, - "id": 302, + "id": 298, "literal": "1.6.0", "name": "busboy", "npm": { "name": "busboy", "version": "==1.6.0", }, - "package_id": 239, + "package_id": 235, }, { "behavior": { "normal": true, }, - "id": 303, + "id": 299, "literal": "8.4.31", "name": "postcss", "npm": { "name": "postcss", "version": "==8.4.31", }, - "package_id": 238, + "package_id": 234, }, { "behavior": { "normal": true, }, - "id": 304, + "id": 300, "literal": "14.1.3", "name": "@next/env", "npm": { "name": "@next/env", "version": "==14.1.3", }, - "package_id": 237, + "package_id": 233, }, { "behavior": { "normal": true, }, - "id": 305, + "id": 301, "literal": "5.1.1", "name": "styled-jsx", "npm": { "name": "styled-jsx", "version": "==5.1.1", }, - "package_id": 235, + "package_id": 231, }, { "behavior": { "normal": true, }, - "id": 306, + "id": 302, "literal": "^4.2.11", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.11 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 307, + "id": 303, "literal": "0.5.2", "name": "@swc/helpers", "npm": { "name": "@swc/helpers", "version": "==0.5.2", }, - "package_id": 234, + "package_id": 230, }, { "behavior": { "normal": true, }, - "id": 308, + "id": 304, "literal": "^1.0.30001579", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001579 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "optional": true, }, - "id": 309, + "id": 305, "literal": "14.1.3", "name": "@next/swc-darwin-x64", "npm": { "name": "@next/swc-darwin-x64", "version": "==14.1.3", }, - "package_id": 232, + "package_id": 228, }, { "behavior": { "optional": true, }, - "id": 310, + "id": 306, "literal": "14.1.3", "name": "@next/swc-darwin-arm64", "npm": { "name": "@next/swc-darwin-arm64", "version": "==14.1.3", }, - "package_id": 231, + "package_id": 227, }, { "behavior": { "optional": true, }, - "id": 311, + "id": 307, "literal": "14.1.3", "name": "@next/swc-linux-x64-gnu", "npm": { "name": "@next/swc-linux-x64-gnu", "version": "==14.1.3", }, - "package_id": 230, + "package_id": 226, }, { "behavior": { "optional": true, }, - "id": 312, + "id": 308, "literal": "14.1.3", "name": "@next/swc-linux-x64-musl", "npm": { "name": "@next/swc-linux-x64-musl", "version": "==14.1.3", }, - "package_id": 229, + "package_id": 225, }, { "behavior": { "optional": true, }, - "id": 313, + "id": 309, "literal": "14.1.3", "name": "@next/swc-win32-x64-msvc", "npm": { "name": "@next/swc-win32-x64-msvc", "version": "==14.1.3", }, - "package_id": 228, + "package_id": 224, }, { "behavior": { "optional": true, }, - "id": 314, + "id": 310, "literal": "14.1.3", "name": "@next/swc-linux-arm64-gnu", "npm": { "name": "@next/swc-linux-arm64-gnu", "version": "==14.1.3", }, - "package_id": 227, + "package_id": 223, }, { "behavior": { "optional": true, }, - "id": 315, + "id": 311, "literal": "14.1.3", "name": "@next/swc-win32-ia32-msvc", "npm": { "name": "@next/swc-win32-ia32-msvc", "version": "==14.1.3", }, - "package_id": 226, + "package_id": 222, }, { "behavior": { "optional": true, }, - "id": 316, + "id": 312, "literal": "14.1.3", "name": "@next/swc-linux-arm64-musl", "npm": { "name": "@next/swc-linux-arm64-musl", "version": "==14.1.3", }, - "package_id": 225, + "package_id": 221, }, { "behavior": { "optional": true, }, - "id": 317, + "id": 313, "literal": "14.1.3", "name": "@next/swc-win32-arm64-msvc", "npm": { "name": "@next/swc-win32-arm64-msvc", "version": "==14.1.3", }, - "package_id": 224, + "package_id": 220, }, { "behavior": { "optional": true, "peer": true, }, - "id": 318, + "id": 314, "literal": "^1.3.0", "name": "sass", "npm": { @@ -4165,7 +4112,7 @@ exports[`next build works: bun 1`] = ` "optional": true, "peer": true, }, - "id": 319, + "id": 315, "literal": "^1.1.0", "name": "@opentelemetry/api", "npm": { @@ -4178,7 +4125,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "peer": true, }, - "id": 320, + "id": 316, "literal": "^18.2.0", "name": "react-dom", "npm": { @@ -4191,7 +4138,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "peer": true, }, - "id": 321, + "id": 317, "literal": "^18.2.0", "name": "react", "npm": { @@ -4204,33 +4151,33 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 322, + "id": 318, "literal": "^2.4.0", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.4.0 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 323, + "id": 319, "literal": "0.0.1", "name": "client-only", "npm": { "name": "client-only", "version": "==0.0.1", }, - "package_id": 236, + "package_id": 232, }, { "behavior": { "peer": true, }, - "id": 324, + "id": 320, "literal": ">= 16.8.0 || 17.x.x || ^18.0.0-0", "name": "react", "npm": { @@ -4243,7 +4190,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 325, + "id": 321, "literal": "^3.3.6", "name": "nanoid", "npm": { @@ -4256,7 +4203,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 326, + "id": 322, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -4269,7 +4216,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 327, + "id": 323, "literal": "^1.0.2", "name": "source-map-js", "npm": { @@ -4282,138 +4229,138 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 328, + "id": 324, "literal": "^1.1.0", "name": "streamsearch", "npm": { "name": "streamsearch", "version": ">=1.1.0 <2.0.0", }, - "package_id": 240, + "package_id": 236, }, { "behavior": { "normal": true, }, - "id": 329, + "id": 325, "literal": "^7.33.2", "name": "eslint-plugin-react", "npm": { "name": "eslint-plugin-react", "version": ">=7.33.2 <8.0.0", }, - "package_id": 433, + "package_id": 429, }, { "behavior": { "normal": true, }, - "id": 330, + "id": 326, "literal": "^2.28.1", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=2.28.1 <3.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 331, + "id": 327, "literal": "^6.7.1", "name": "eslint-plugin-jsx-a11y", "npm": { "name": "eslint-plugin-jsx-a11y", "version": ">=6.7.1 <7.0.0", }, - "package_id": 408, + "package_id": 404, }, { "behavior": { "normal": true, }, - "id": 332, + "id": 328, "literal": "^1.3.3", "name": "@rushstack/eslint-patch", "npm": { "name": "@rushstack/eslint-patch", "version": ">=1.3.3 <2.0.0", }, - "package_id": 407, + "package_id": 403, }, { "behavior": { "normal": true, }, - "id": 333, + "id": 329, "literal": "14.1.3", "name": "@next/eslint-plugin-next", "npm": { "name": "@next/eslint-plugin-next", "version": "==14.1.3", }, - "package_id": 406, + "package_id": 402, }, { "behavior": { "normal": true, }, - "id": 334, + "id": 330, "literal": "^5.4.2 || ^6.0.0", "name": "@typescript-eslint/parser", "npm": { "name": "@typescript-eslint/parser", "version": ">=5.4.2 <6.0.0 || >=6.0.0 <7.0.0 && >=6.0.0 <7.0.0", }, - "package_id": 394, + "package_id": 390, }, { "behavior": { "normal": true, }, - "id": 335, + "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", "name": "eslint-plugin-react-hooks", "npm": { "name": "eslint-plugin-react-hooks", "version": ">=4.5.0 <5.0.0 || ==5.0.0-canary-7118f5dd7-20230705 && ==5.0.0-canary-7118f5dd7-20230705", }, - "package_id": 393, + "package_id": 389, }, { "behavior": { "normal": true, }, - "id": 336, + "id": 332, "literal": "^0.3.6", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.6 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 337, + "id": 333, "literal": "^3.5.2", "name": "eslint-import-resolver-typescript", "npm": { "name": "eslint-import-resolver-typescript", "version": ">=3.5.2 <4.0.0", }, - "package_id": 306, + "package_id": 302, }, { "behavior": { "optional": true, "peer": true, }, - "id": 338, + "id": 334, "literal": ">=3.3.1", "name": "typescript", "npm": { @@ -4426,150 +4373,150 @@ exports[`next build works: bun 1`] = ` "behavior": { "peer": true, }, - "id": 339, + "id": 335, "literal": "^7.23.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.23.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 340, + "id": 336, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 341, + "id": 337, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 342, + "id": 338, "literal": "^4.0.0", "name": "chalk", "npm": { "name": "chalk", "version": ">=4.0.0 <5.0.0", }, - "package_id": 303, + "package_id": 299, }, { "behavior": { "normal": true, }, - "id": 343, + "id": 339, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 344, + "id": 340, "literal": "^9.6.1", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.1 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 345, + "id": 341, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 346, + "id": 342, "literal": "^1.4.2", "name": "esquery", "npm": { "name": "esquery", "version": ">=1.4.2 <2.0.0", }, - "package_id": 302, + "package_id": 298, }, { "behavior": { "normal": true, }, - "id": 347, + "id": 343, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 348, + "id": 344, "literal": "^5.0.0", "name": "find-up", "npm": { "name": "find-up", "version": ">=5.0.0 <6.0.0", }, - "package_id": 296, + "package_id": 292, }, { "behavior": { "normal": true, }, - "id": 349, + "id": 345, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 350, + "id": 346, "literal": "^4.0.0", "name": "is-glob", "npm": { @@ -4582,85 +4529,85 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 351, + "id": 347, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 352, + "id": 348, "literal": "^3.0.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=3.0.0 <4.0.0", }, - "package_id": 295, + "package_id": 291, }, { "behavior": { "normal": true, }, - "id": 353, + "id": 349, "literal": "^1.4.0", "name": "graphemer", "npm": { "name": "graphemer", "version": ">=1.4.0 <2.0.0", }, - "package_id": 294, + "package_id": 290, }, { "behavior": { "normal": true, }, - "id": 354, + "id": 350, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 355, + "id": 351, "literal": "8.50.0", "name": "@eslint/js", "npm": { "name": "@eslint/js", "version": "==8.50.0", }, - "package_id": 293, + "package_id": 289, }, { "behavior": { "normal": true, }, - "id": 356, + "id": 352, "literal": "^0.9.3", "name": "optionator", "npm": { "name": "optionator", "version": ">=0.9.3 <0.10.0", }, - "package_id": 286, + "package_id": 282, }, { "behavior": { "normal": true, }, - "id": 357, + "id": 353, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -4673,20 +4620,20 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 358, + "id": 354, "literal": "^0.2.0", "name": "text-table", "npm": { "name": "text-table", "version": ">=0.2.0 <0.3.0", }, - "package_id": 285, + "package_id": 281, }, { "behavior": { "normal": true, }, - "id": 359, + "id": 355, "literal": "^7.0.2", "name": "cross-spawn", "npm": { @@ -4699,7 +4646,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 360, + "id": 356, "literal": "^6.0.2", "name": "glob-parent", "npm": { @@ -4712,98 +4659,98 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 361, + "id": 357, "literal": "^0.1.4", "name": "imurmurhash", "npm": { "name": "imurmurhash", "version": ">=0.1.4 <0.2.0", }, - "package_id": 284, + "package_id": 280, }, { "behavior": { "normal": true, }, - "id": 362, + "id": 358, "literal": "^7.2.2", "name": "eslint-scope", "npm": { "name": "eslint-scope", "version": ">=7.2.2 <8.0.0", }, - "package_id": 282, + "package_id": 278, }, { "behavior": { "normal": true, }, - "id": 363, + "id": 359, "literal": "^4.6.2", "name": "lodash.merge", "npm": { "name": "lodash.merge", "version": ">=4.6.2 <5.0.0", }, - "package_id": 281, + "package_id": 277, }, { "behavior": { "normal": true, }, - "id": 364, + "id": 360, "literal": "^3.0.3", "name": "is-path-inside", "npm": { "name": "is-path-inside", "version": ">=3.0.3 <4.0.0", }, - "package_id": 280, + "package_id": 276, }, { "behavior": { "normal": true, }, - "id": 365, + "id": 361, "literal": "^3.1.3", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.3 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 366, + "id": 362, "literal": "^1.4.0", "name": "natural-compare", "npm": { "name": "natural-compare", "version": ">=1.4.0 <2.0.0", }, - "package_id": 279, + "package_id": 275, }, { "behavior": { "normal": true, }, - "id": 367, + "id": 363, "literal": "^2.1.2", "name": "@eslint/eslintrc", "npm": { "name": "@eslint/eslintrc", "version": ">=2.1.2 <3.0.0", }, - "package_id": 265, + "package_id": 261, }, { "behavior": { "normal": true, }, - "id": 368, + "id": 364, "literal": "^1.2.8", "name": "@nodelib/fs.walk", "npm": { @@ -4816,189 +4763,189 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 369, + "id": 365, "literal": "^6.0.1", "name": "file-entry-cache", "npm": { "name": "file-entry-cache", "version": ">=6.0.1 <7.0.0", }, - "package_id": 254, + "package_id": 250, }, { "behavior": { "normal": true, }, - "id": 370, + "id": 366, "literal": "^3.4.3", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.3 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 371, + "id": 367, "literal": "^4.0.0", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=4.0.0 <5.0.0", }, - "package_id": 253, + "package_id": 249, }, { "behavior": { "normal": true, }, - "id": 372, + "id": 368, "literal": "^4.6.1", "name": "@eslint-community/regexpp", "npm": { "name": "@eslint-community/regexpp", "version": ">=4.6.1 <5.0.0", }, - "package_id": 252, + "package_id": 248, }, { "behavior": { "normal": true, }, - "id": 373, + "id": 369, "literal": "^0.11.11", "name": "@humanwhocodes/config-array", "npm": { "name": "@humanwhocodes/config-array", "version": ">=0.11.11 <0.12.0", }, - "package_id": 247, + "package_id": 243, }, { "behavior": { "normal": true, }, - "id": 374, + "id": 370, "literal": "^4.2.0", "name": "@eslint-community/eslint-utils", "npm": { "name": "@eslint-community/eslint-utils", "version": ">=4.2.0 <5.0.0", }, - "package_id": 245, + "package_id": 241, }, { "behavior": { "normal": true, }, - "id": 375, + "id": 371, "literal": "^1.0.1", "name": "@humanwhocodes/module-importer", "npm": { "name": "@humanwhocodes/module-importer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 244, + "package_id": 240, }, { "behavior": { "normal": true, }, - "id": 376, + "id": 372, "literal": "^1.0.1", "name": "json-stable-stringify-without-jsonify", "npm": { "name": "json-stable-stringify-without-jsonify", "version": ">=1.0.1 <2.0.0", }, - "package_id": 243, + "package_id": 239, }, { "behavior": { "normal": true, }, - "id": 377, + "id": 373, "literal": "^3.3.0", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.3.0 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 378, + "id": 374, "literal": "^6.0.0 || ^7.0.0 || >=8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 && >=8.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 379, + "id": 375, "literal": "^2.0.2", "name": "@humanwhocodes/object-schema", "npm": { "name": "@humanwhocodes/object-schema", "version": ">=2.0.2 <3.0.0", }, - "package_id": 251, + "package_id": 247, }, { "behavior": { "normal": true, }, - "id": 380, + "id": 376, "literal": "^4.3.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 381, + "id": 377, "literal": "^3.0.5", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.0.5 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 382, + "id": 378, "literal": "^1.1.7", "name": "brace-expansion", "npm": { "name": "brace-expansion", "version": ">=1.1.7 <2.0.0", }, - "package_id": 249, + "package_id": 245, }, { "behavior": { "normal": true, }, - "id": 383, + "id": 379, "literal": "^1.0.0", "name": "balanced-match", "npm": { @@ -5011,696 +4958,696 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 384, + "id": 380, "literal": "0.0.1", "name": "concat-map", "npm": { "name": "concat-map", "version": "==0.0.1", }, - "package_id": 250, + "package_id": 246, }, { "behavior": { "normal": true, }, - "id": 385, + "id": 381, "literal": "^3.0.4", "name": "flat-cache", "npm": { "name": "flat-cache", "version": ">=3.0.4 <4.0.0", }, - "package_id": 255, + "package_id": 251, }, { "behavior": { "normal": true, }, - "id": 386, + "id": 382, "literal": "^3.2.9", "name": "flatted", "npm": { "name": "flatted", "version": ">=3.2.9 <4.0.0", }, - "package_id": 264, + "package_id": 260, }, { "behavior": { "normal": true, }, - "id": 387, + "id": 383, "literal": "^4.5.3", "name": "keyv", "npm": { "name": "keyv", "version": ">=4.5.3 <5.0.0", }, - "package_id": 262, + "package_id": 258, }, { "behavior": { "normal": true, }, - "id": 388, + "id": 384, "literal": "^3.0.2", "name": "rimraf", "npm": { "name": "rimraf", "version": ">=3.0.2 <4.0.0", }, - "package_id": 256, + "package_id": 252, }, { "behavior": { "normal": true, }, - "id": 389, + "id": 385, "literal": "^7.1.3", "name": "glob", "npm": { "name": "glob", "version": ">=7.1.3 <8.0.0", }, - "package_id": 257, + "package_id": 253, }, { "behavior": { "normal": true, }, - "id": 390, + "id": 386, "literal": "^1.0.0", "name": "fs.realpath", "npm": { "name": "fs.realpath", "version": ">=1.0.0 <2.0.0", }, - "package_id": 261, + "package_id": 257, }, { "behavior": { "normal": true, }, - "id": 391, + "id": 387, "literal": "^1.0.4", "name": "inflight", "npm": { "name": "inflight", "version": ">=1.0.4 <2.0.0", }, - "package_id": 260, + "package_id": 256, }, { "behavior": { "normal": true, }, - "id": 392, + "id": 388, "literal": "2", "name": "inherits", "npm": { "name": "inherits", "version": "<3.0.0 >=2.0.0", }, - "package_id": 259, + "package_id": 255, }, { "behavior": { "normal": true, }, - "id": 393, + "id": 389, "literal": "^3.1.1", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.1 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 394, + "id": 390, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 395, + "id": 391, "literal": "^1.0.0", "name": "path-is-absolute", "npm": { "name": "path-is-absolute", "version": ">=1.0.0 <2.0.0", }, - "package_id": 258, + "package_id": 254, }, { "behavior": { "normal": true, }, - "id": 396, + "id": 392, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 397, + "id": 393, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 398, + "id": 394, "literal": "3.0.1", "name": "json-buffer", "npm": { "name": "json-buffer", "version": "==3.0.1", }, - "package_id": 263, + "package_id": 259, }, { "behavior": { "normal": true, }, - "id": 399, + "id": 395, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 400, + "id": 396, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 401, + "id": 397, "literal": "^9.6.0", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.0 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 402, + "id": 398, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 403, + "id": 399, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 404, + "id": 400, "literal": "^3.2.1", "name": "import-fresh", "npm": { "name": "import-fresh", "version": ">=3.2.1 <4.0.0", }, - "package_id": 218, + "package_id": 214, }, { "behavior": { "normal": true, }, - "id": 405, + "id": 401, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 406, + "id": 402, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 407, + "id": 403, "literal": "^3.1.1", "name": "strip-json-comments", "npm": { "name": "strip-json-comments", "version": ">=3.1.1 <4.0.0", }, - "package_id": 266, + "package_id": 262, }, { "behavior": { "normal": true, }, - "id": 408, + "id": 404, "literal": "^0.20.2", "name": "type-fest", "npm": { "name": "type-fest", "version": ">=0.20.2 <0.21.0", }, - "package_id": 269, + "package_id": 265, }, { "behavior": { "normal": true, }, - "id": 409, + "id": 405, "literal": "^8.9.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=8.9.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 410, + "id": 406, "literal": "^5.3.2", "name": "acorn-jsx", "npm": { "name": "acorn-jsx", "version": ">=5.3.2 <6.0.0", }, - "package_id": 271, + "package_id": 267, }, { "behavior": { "normal": true, }, - "id": 411, + "id": 407, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 412, + "id": 408, "literal": "^6.0.0 || ^7.0.0 || ^8.0.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 413, + "id": 409, "literal": "^3.1.1", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.1 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 414, + "id": 410, "literal": "^2.0.0", "name": "fast-json-stable-stringify", "npm": { "name": "fast-json-stable-stringify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 277, + "package_id": 273, }, { "behavior": { "normal": true, }, - "id": 415, + "id": 411, "literal": "^0.4.1", "name": "json-schema-traverse", "npm": { "name": "json-schema-traverse", "version": ">=0.4.1 <0.5.0", }, - "package_id": 276, + "package_id": 272, }, { "behavior": { "normal": true, }, - "id": 416, + "id": 412, "literal": "^4.2.2", "name": "uri-js", "npm": { "name": "uri-js", "version": ">=4.2.2 <5.0.0", }, - "package_id": 274, + "package_id": 270, }, { "behavior": { "normal": true, }, - "id": 417, + "id": 413, "literal": "^2.1.0", "name": "punycode", "npm": { "name": "punycode", "version": ">=2.1.0 <3.0.0", }, - "package_id": 275, + "package_id": 271, }, { "behavior": { "normal": true, }, - "id": 418, + "id": 414, "literal": "^4.3.0", "name": "esrecurse", "npm": { "name": "esrecurse", "version": ">=4.3.0 <5.0.0", }, - "package_id": 283, + "package_id": 279, }, { "behavior": { "normal": true, }, - "id": 419, + "id": 415, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 420, + "id": 416, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 421, + "id": 417, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 422, + "id": 418, "literal": "^0.1.3", "name": "deep-is", "npm": { "name": "deep-is", "version": ">=0.1.3 <0.2.0", }, - "package_id": 292, + "package_id": 288, }, { "behavior": { "normal": true, }, - "id": 423, + "id": 419, "literal": "^1.2.5", "name": "word-wrap", "npm": { "name": "word-wrap", "version": ">=1.2.5 <2.0.0", }, - "package_id": 291, + "package_id": 287, }, { "behavior": { "normal": true, }, - "id": 424, + "id": 420, "literal": "^0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 425, + "id": 421, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 426, + "id": 422, "literal": "^2.0.6", "name": "fast-levenshtein", "npm": { "name": "fast-levenshtein", "version": ">=2.0.6 <3.0.0", }, - "package_id": 287, + "package_id": 283, }, { "behavior": { "normal": true, }, - "id": 427, + "id": 423, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 428, + "id": 424, "literal": "~0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 429, + "id": 425, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 430, + "id": 426, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 431, + "id": 427, "literal": "^6.0.0", "name": "locate-path", "npm": { "name": "locate-path", "version": ">=6.0.0 <7.0.0", }, - "package_id": 298, + "package_id": 294, }, { "behavior": { "normal": true, }, - "id": 432, + "id": 428, "literal": "^4.0.0", "name": "path-exists", "npm": { "name": "path-exists", "version": ">=4.0.0 <5.0.0", }, - "package_id": 297, + "package_id": 293, }, { "behavior": { "normal": true, }, - "id": 433, + "id": 429, "literal": "^5.0.0", "name": "p-locate", "npm": { "name": "p-locate", "version": ">=5.0.0 <6.0.0", }, - "package_id": 299, + "package_id": 295, }, { "behavior": { "normal": true, }, - "id": 434, + "id": 430, "literal": "^3.0.2", "name": "p-limit", "npm": { "name": "p-limit", "version": ">=3.0.2 <4.0.0", }, - "package_id": 300, + "package_id": 296, }, { "behavior": { "normal": true, }, - "id": 435, + "id": 431, "literal": "^0.1.0", "name": "yocto-queue", "npm": { "name": "yocto-queue", "version": ">=0.1.0 <0.2.0", }, - "package_id": 301, + "package_id": 297, }, { "behavior": { "normal": true, }, - "id": 436, + "id": 432, "literal": "^5.1.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.1.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 437, + "id": 433, "literal": "^4.1.0", "name": "ansi-styles", "npm": { @@ -5713,72 +5660,72 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 438, + "id": 434, "literal": "^7.1.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=7.1.0 <8.0.0", }, - "package_id": 304, + "package_id": 300, }, { "behavior": { "normal": true, }, - "id": 439, + "id": 435, "literal": "^4.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=4.0.0 <5.0.0", }, - "package_id": 305, + "package_id": 301, }, { "behavior": { "normal": true, }, - "id": 440, + "id": 436, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 441, + "id": 437, "literal": "^5.12.0", "name": "enhanced-resolve", "npm": { "name": "enhanced-resolve", "version": ">=5.12.0 <6.0.0", }, - "package_id": 391, + "package_id": 387, }, { "behavior": { "normal": true, }, - "id": 442, + "id": 438, "literal": "^2.7.4", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.7.4 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 443, + "id": 439, "literal": "^3.3.1", "name": "fast-glob", "npm": { @@ -5791,20 +5738,20 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 444, + "id": 440, "literal": "^4.5.0", "name": "get-tsconfig", "npm": { "name": "get-tsconfig", "version": ">=4.5.0 <5.0.0", }, - "package_id": 389, + "package_id": 385, }, { "behavior": { "normal": true, }, - "id": 445, + "id": 441, "literal": "^2.11.0", "name": "is-core-module", "npm": { @@ -5817,7 +5764,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 446, + "id": 442, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -5830,137 +5777,137 @@ exports[`next build works: bun 1`] = ` "behavior": { "peer": true, }, - "id": 447, + "id": 443, "literal": "*", "name": "eslint", "npm": { "name": "eslint", "version": ">=0.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "peer": true, }, - "id": 448, + "id": 444, "literal": "*", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=0.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 449, + "id": 445, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 450, + "id": 446, "literal": "^1.2.3", "name": "array.prototype.findlastindex", "npm": { "name": "array.prototype.findlastindex", "version": ">=1.2.3 <2.0.0", }, - "package_id": 387, + "package_id": 383, }, { "behavior": { "normal": true, }, - "id": 451, + "id": 447, "literal": "^1.3.2", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.2 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 452, + "id": 448, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 453, + "id": 449, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 454, + "id": 450, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 455, + "id": 451, "literal": "^0.3.9", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.9 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 456, + "id": 452, "literal": "^2.8.0", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.8.0 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 457, + "id": 453, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -5973,7 +5920,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 458, + "id": 454, "literal": "^2.13.1", "name": "is-core-module", "npm": { @@ -5986,7 +5933,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 459, + "id": 455, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -5999,293 +5946,293 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 460, + "id": 456, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 461, + "id": 457, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 462, + "id": 458, "literal": "^1.0.1", "name": "object.groupby", "npm": { "name": "object.groupby", "version": ">=1.0.1 <2.0.0", }, - "package_id": 328, + "package_id": 324, }, { "behavior": { "normal": true, }, - "id": 463, + "id": 459, "literal": "^1.1.7", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.7 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 464, + "id": 460, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 465, + "id": 461, "literal": "^3.15.0", "name": "tsconfig-paths", "npm": { "name": "tsconfig-paths", "version": ">=3.15.0 <4.0.0", }, - "package_id": 308, + "package_id": 304, }, { "behavior": { "peer": true, }, - "id": 466, + "id": 462, "literal": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=2.0.0 <3.0.0 || >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 467, + "id": 463, "literal": "^0.0.29", "name": "@types/json5", "npm": { "name": "@types/json5", "version": ">=0.0.29 <0.0.30", }, - "package_id": 312, + "package_id": 308, }, { "behavior": { "normal": true, }, - "id": 468, + "id": 464, "literal": "^1.0.2", "name": "json5", "npm": { "name": "json5", "version": ">=1.0.2 <2.0.0", }, - "package_id": 311, + "package_id": 307, }, { "behavior": { "normal": true, }, - "id": 469, + "id": 465, "literal": "^1.2.6", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.6 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 470, + "id": 466, "literal": "^3.0.0", "name": "strip-bom", "npm": { "name": "strip-bom", "version": ">=3.0.0 <4.0.0", }, - "package_id": 309, + "package_id": 305, }, { "behavior": { "normal": true, }, - "id": 471, + "id": 467, "literal": "^1.2.0", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.0 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 472, + "id": 468, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 473, + "id": 469, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 474, + "id": 470, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 475, + "id": 471, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 476, + "id": 472, "literal": "^1.0.1", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.0.1 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 477, + "id": 473, "literal": "^1.0.0", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.0 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 478, + "id": 474, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 479, + "id": 475, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 480, + "id": 476, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 481, + "id": 477, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 482, + "id": 478, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6298,33 +6245,33 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 483, + "id": 479, "literal": "^1.0.1", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.1 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 484, + "id": 480, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 485, + "id": 481, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -6337,85 +6284,85 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 486, + "id": 482, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 487, + "id": 483, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 488, + "id": 484, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 489, + "id": 485, "literal": "^1.1.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.1.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 490, + "id": 486, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 491, + "id": 487, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 492, + "id": 488, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6428,59 +6375,59 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 493, + "id": 489, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 494, + "id": 490, "literal": "^1.2.1", "name": "set-function-length", "npm": { "name": "set-function-length", "version": ">=1.2.1 <2.0.0", }, - "package_id": 327, + "package_id": 323, }, { "behavior": { "normal": true, }, - "id": 495, + "id": 491, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 496, + "id": 492, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 497, + "id": 493, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -6493,345 +6440,345 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 498, + "id": 494, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 499, + "id": 495, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 500, + "id": 496, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 501, + "id": 497, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 502, + "id": 498, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 503, + "id": 499, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 504, + "id": 500, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 505, + "id": 501, "literal": "^1.0.3", "name": "arraybuffer.prototype.slice", "npm": { "name": "arraybuffer.prototype.slice", "version": ">=1.0.3 <2.0.0", }, - "package_id": 377, + "package_id": 373, }, { "behavior": { "normal": true, }, - "id": 506, + "id": 502, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 507, + "id": 503, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 508, + "id": 504, "literal": "^1.0.1", "name": "data-view-buffer", "npm": { "name": "data-view-buffer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 376, + "package_id": 372, }, { "behavior": { "normal": true, }, - "id": 509, + "id": 505, "literal": "^1.0.1", "name": "data-view-byte-length", "npm": { "name": "data-view-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 375, + "package_id": 371, }, { "behavior": { "normal": true, }, - "id": 510, + "id": 506, "literal": "^1.0.0", "name": "data-view-byte-offset", "npm": { "name": "data-view-byte-offset", "version": ">=1.0.0 <2.0.0", }, - "package_id": 374, + "package_id": 370, }, { "behavior": { "normal": true, }, - "id": 511, + "id": 507, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 512, + "id": 508, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 513, + "id": 509, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 514, + "id": 510, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 515, + "id": 511, "literal": "^1.2.1", "name": "es-to-primitive", "npm": { "name": "es-to-primitive", "version": ">=1.2.1 <2.0.0", }, - "package_id": 371, + "package_id": 367, }, { "behavior": { "normal": true, }, - "id": 516, + "id": 512, "literal": "^1.1.6", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.6 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 517, + "id": 513, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 518, + "id": 514, "literal": "^1.0.2", "name": "get-symbol-description", "npm": { "name": "get-symbol-description", "version": ">=1.0.2 <2.0.0", }, - "package_id": 369, + "package_id": 365, }, { "behavior": { "normal": true, }, - "id": 519, + "id": 515, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 520, + "id": 516, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 521, + "id": 517, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 522, + "id": 518, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 523, + "id": 519, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 524, + "id": 520, "literal": "^2.0.2", "name": "hasown", "npm": { @@ -6844,1385 +6791,1385 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 525, + "id": 521, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 526, + "id": 522, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 527, + "id": 523, "literal": "^1.2.7", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.2.7 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 528, + "id": 524, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 529, + "id": 525, "literal": "^2.0.3", "name": "is-negative-zero", "npm": { "name": "is-negative-zero", "version": ">=2.0.3 <3.0.0", }, - "package_id": 363, + "package_id": 359, }, { "behavior": { "normal": true, }, - "id": 530, + "id": 526, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 531, + "id": 527, "literal": "^1.0.3", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.3 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 532, + "id": 528, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 533, + "id": 529, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 534, + "id": 530, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 535, + "id": 531, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 536, + "id": 532, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 537, + "id": 533, "literal": "^4.1.5", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.5 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 538, + "id": 534, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 539, + "id": 535, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 540, + "id": 536, "literal": "^1.0.3", "name": "safe-regex-test", "npm": { "name": "safe-regex-test", "version": ">=1.0.3 <2.0.0", }, - "package_id": 352, + "package_id": 348, }, { "behavior": { "normal": true, }, - "id": 541, + "id": 537, "literal": "^1.2.9", "name": "string.prototype.trim", "npm": { "name": "string.prototype.trim", "version": ">=1.2.9 <2.0.0", }, - "package_id": 351, + "package_id": 347, }, { "behavior": { "normal": true, }, - "id": 542, + "id": 538, "literal": "^1.0.8", "name": "string.prototype.trimend", "npm": { "name": "string.prototype.trimend", "version": ">=1.0.8 <2.0.0", }, - "package_id": 350, + "package_id": 346, }, { "behavior": { "normal": true, }, - "id": 543, + "id": 539, "literal": "^1.0.8", "name": "string.prototype.trimstart", "npm": { "name": "string.prototype.trimstart", "version": ">=1.0.8 <2.0.0", }, - "package_id": 349, + "package_id": 345, }, { "behavior": { "normal": true, }, - "id": 544, + "id": 540, "literal": "^1.0.2", "name": "typed-array-buffer", "npm": { "name": "typed-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 348, + "package_id": 344, }, { "behavior": { "normal": true, }, - "id": 545, + "id": 541, "literal": "^1.0.1", "name": "typed-array-byte-length", "npm": { "name": "typed-array-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 347, + "package_id": 343, }, { "behavior": { "normal": true, }, - "id": 546, + "id": 542, "literal": "^1.0.2", "name": "typed-array-byte-offset", "npm": { "name": "typed-array-byte-offset", "version": ">=1.0.2 <2.0.0", }, - "package_id": 346, + "package_id": 342, }, { "behavior": { "normal": true, }, - "id": 547, + "id": 543, "literal": "^1.0.6", "name": "typed-array-length", "npm": { "name": "typed-array-length", "version": ">=1.0.6 <2.0.0", }, - "package_id": 344, + "package_id": 340, }, { "behavior": { "normal": true, }, - "id": 548, + "id": 544, "literal": "^1.0.2", "name": "unbox-primitive", "npm": { "name": "unbox-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 336, + "package_id": 332, }, { "behavior": { "normal": true, }, - "id": 549, + "id": 545, "literal": "^1.1.15", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.15 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 550, + "id": 546, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 551, + "id": 547, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 552, + "id": 548, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 553, + "id": 549, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 554, + "id": 550, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 555, + "id": 551, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 556, + "id": 552, "literal": "^1.1.3", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.3 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 557, + "id": 553, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 558, + "id": 554, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 559, + "id": 555, "literal": "^1.0.2", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.2 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 560, + "id": 556, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 561, + "id": 557, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 562, + "id": 558, "literal": "^1.0.1", "name": "is-bigint", "npm": { "name": "is-bigint", "version": ">=1.0.1 <2.0.0", }, - "package_id": 342, + "package_id": 338, }, { "behavior": { "normal": true, }, - "id": 563, + "id": 559, "literal": "^1.1.0", "name": "is-boolean-object", "npm": { "name": "is-boolean-object", "version": ">=1.1.0 <2.0.0", }, - "package_id": 341, + "package_id": 337, }, { "behavior": { "normal": true, }, - "id": 564, + "id": 560, "literal": "^1.0.4", "name": "is-number-object", "npm": { "name": "is-number-object", "version": ">=1.0.4 <2.0.0", }, - "package_id": 340, + "package_id": 336, }, { "behavior": { "normal": true, }, - "id": 565, + "id": 561, "literal": "^1.0.5", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.5 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 566, + "id": 562, "literal": "^1.0.3", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.3 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 567, + "id": 563, "literal": "^1.0.2", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.2 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 568, + "id": 564, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 569, + "id": 565, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 570, + "id": 566, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 571, + "id": 567, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 572, + "id": 568, "literal": "^1.0.1", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.1 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 573, + "id": 569, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 574, + "id": 570, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 575, + "id": 571, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 576, + "id": 572, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 577, + "id": 573, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 578, + "id": 574, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 579, + "id": 575, "literal": "^1.1.14", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.14 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 580, + "id": 576, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 581, + "id": 577, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 582, + "id": 578, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 583, + "id": 579, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 584, + "id": 580, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 585, + "id": 581, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 586, + "id": 582, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 587, + "id": 583, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 588, + "id": 584, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 589, + "id": 585, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 590, + "id": 586, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 591, + "id": 587, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 592, + "id": 588, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 593, + "id": 589, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 594, + "id": 590, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 595, + "id": 591, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 596, + "id": 592, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 597, + "id": 593, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 598, + "id": 594, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 599, + "id": 595, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 600, + "id": 596, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 601, + "id": 597, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 602, + "id": 598, "literal": "^1.23.0", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.0 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 603, + "id": 599, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 604, + "id": 600, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 605, + "id": 601, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 606, + "id": 602, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 607, + "id": 603, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 608, + "id": 604, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 609, + "id": 605, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 610, + "id": 606, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 611, + "id": 607, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 612, + "id": 608, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 613, + "id": 609, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 614, + "id": 610, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 615, + "id": 611, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 616, + "id": 612, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 617, + "id": 613, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 618, + "id": 614, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 619, + "id": 615, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 620, + "id": 616, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 621, + "id": 617, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 622, + "id": 618, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 623, + "id": 619, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 624, + "id": 620, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 625, + "id": 621, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 626, + "id": 622, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 627, + "id": 623, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 628, + "id": 624, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 629, + "id": 625, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 630, + "id": 626, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 631, + "id": 627, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -8235,267 +8182,267 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 632, + "id": 628, "literal": "^1.0.4", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.4 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 633, + "id": 629, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 634, + "id": 630, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 635, + "id": 631, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 636, + "id": 632, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 637, + "id": 633, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 638, + "id": 634, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 639, + "id": 635, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 640, + "id": 636, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 641, + "id": 637, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 642, + "id": 638, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 643, + "id": 639, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 644, + "id": 640, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 645, + "id": 641, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 646, + "id": 642, "literal": "^1.1.4", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.4 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 647, + "id": 643, "literal": "^1.0.1", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.1 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 648, + "id": 644, "literal": "^1.0.2", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.2 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 649, + "id": 645, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 650, + "id": 646, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 651, + "id": 647, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 652, + "id": 648, "literal": "^2.0.1", "name": "hasown", "npm": { @@ -8508,345 +8455,345 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 653, + "id": 649, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 654, + "id": 650, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 655, + "id": 651, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 656, + "id": 652, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 657, + "id": 653, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 658, + "id": 654, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 659, + "id": 655, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 660, + "id": 656, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 661, + "id": 657, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 662, + "id": 658, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 663, + "id": 659, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 664, + "id": 660, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 665, + "id": 661, "literal": "^1.22.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 666, + "id": 662, "literal": "^1.2.1", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.2.1 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 667, + "id": 663, "literal": "^1.2.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 668, + "id": 664, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 669, + "id": 665, "literal": "^1.0.2", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 670, + "id": 666, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 671, + "id": 667, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 672, + "id": 668, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 673, + "id": 669, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 674, + "id": 670, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 675, + "id": 671, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 676, + "id": 672, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 677, + "id": 673, "literal": "^2.1.1", "name": "ms", "npm": { "name": "ms", "version": ">=2.1.1 <3.0.0", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 678, + "id": 674, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 679, + "id": 675, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -8859,7 +8806,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 680, + "id": 676, "literal": "^1.22.4", "name": "resolve", "npm": { @@ -8872,72 +8819,72 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 681, + "id": 677, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 682, + "id": 678, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 683, + "id": 679, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 684, + "id": 680, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 685, + "id": 681, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 686, + "id": 682, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -8950,384 +8897,384 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 687, + "id": 683, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 688, + "id": 684, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 689, + "id": 685, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 690, + "id": 686, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 691, + "id": 687, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 692, + "id": 688, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 693, + "id": 689, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 694, + "id": 690, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 695, + "id": 691, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 696, + "id": 692, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 697, + "id": 693, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 698, + "id": 694, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 699, + "id": 695, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 700, + "id": 696, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 701, + "id": 697, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 702, + "id": 698, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 703, + "id": 699, "literal": "^1.0.0", "name": "resolve-pkg-maps", "npm": { "name": "resolve-pkg-maps", "version": ">=1.0.0 <2.0.0", }, - "package_id": 390, + "package_id": 386, }, { "behavior": { "normal": true, }, - "id": 704, + "id": 700, "literal": "^4.2.4", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.4 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 705, + "id": 701, "literal": "^2.2.0", "name": "tapable", "npm": { "name": "tapable", "version": ">=2.2.0 <3.0.0", }, - "package_id": 392, + "package_id": 388, }, { "behavior": { "peer": true, }, - "id": 706, + "id": 702, "literal": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=8.0.0-0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 707, + "id": 703, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 708, + "id": 704, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 709, + "id": 705, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 710, + "id": 706, "literal": "6.21.0", "name": "@typescript-eslint/scope-manager", "npm": { "name": "@typescript-eslint/scope-manager", "version": "==6.21.0", }, - "package_id": 405, + "package_id": 401, }, { "behavior": { "normal": true, }, - "id": 711, + "id": 707, "literal": "6.21.0", "name": "@typescript-eslint/typescript-estree", "npm": { "name": "@typescript-eslint/typescript-estree", "version": "==6.21.0", }, - "package_id": 395, + "package_id": 391, }, { "behavior": { "peer": true, }, - "id": 712, + "id": 708, "literal": "^7.0.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 713, + "id": 709, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 714, + "id": 710, "literal": "^11.1.0", "name": "globby", "npm": { "name": "globby", "version": ">=11.1.0 <12.0.0", }, - "package_id": 400, + "package_id": 396, }, { "behavior": { "normal": true, }, - "id": 715, + "id": 711, "literal": "^7.5.4", "name": "semver", "npm": { "name": "semver", "version": ">=7.5.4 <8.0.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 716, + "id": 712, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -9340,85 +9287,85 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 717, + "id": 713, "literal": "9.0.3", "name": "minimatch", "npm": { "name": "minimatch", "version": "==9.0.3", }, - "package_id": 399, + "package_id": 395, }, { "behavior": { "normal": true, }, - "id": 718, + "id": 714, "literal": "^1.0.1", "name": "ts-api-utils", "npm": { "name": "ts-api-utils", "version": ">=1.0.1 <2.0.0", }, - "package_id": 398, + "package_id": 394, }, { "behavior": { "normal": true, }, - "id": 719, + "id": 715, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 720, + "id": 716, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 721, + "id": 717, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 722, + "id": 718, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "peer": true, }, - "id": 723, + "id": 719, "literal": ">=4.2.0", "name": "typescript", "npm": { @@ -9431,7 +9378,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 724, + "id": 720, "literal": "^2.0.1", "name": "brace-expansion", "npm": { @@ -9444,33 +9391,33 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 725, + "id": 721, "literal": "^2.1.0", "name": "array-union", "npm": { "name": "array-union", "version": ">=2.1.0 <3.0.0", }, - "package_id": 404, + "package_id": 400, }, { "behavior": { "normal": true, }, - "id": 726, + "id": 722, "literal": "^3.0.1", "name": "dir-glob", "npm": { "name": "dir-glob", "version": ">=3.0.1 <4.0.0", }, - "package_id": 402, + "package_id": 398, }, { "behavior": { "normal": true, }, - "id": 727, + "id": 723, "literal": "^3.2.9", "name": "fast-glob", "npm": { @@ -9483,20 +9430,20 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 728, + "id": 724, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 729, + "id": 725, "literal": "^1.4.1", "name": "merge2", "npm": { @@ -9509,59 +9456,59 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 730, + "id": 726, "literal": "^3.0.0", "name": "slash", "npm": { "name": "slash", "version": ">=3.0.0 <4.0.0", }, - "package_id": 401, + "package_id": 397, }, { "behavior": { "normal": true, }, - "id": 731, + "id": 727, "literal": "^4.0.0", "name": "path-type", "npm": { "name": "path-type", "version": ">=4.0.0 <5.0.0", }, - "package_id": 403, + "package_id": 399, }, { "behavior": { "normal": true, }, - "id": 732, + "id": 728, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 733, + "id": 729, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 734, + "id": 730, "literal": "10.3.10", "name": "glob", "npm": { @@ -9574,111 +9521,111 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 735, + "id": 731, "literal": "^7.23.2", "name": "@babel/runtime", "npm": { "name": "@babel/runtime", "version": ">=7.23.2 <8.0.0", }, - "package_id": 431, + "package_id": 427, }, { "behavior": { "normal": true, }, - "id": 736, + "id": 732, "literal": "^5.3.0", "name": "aria-query", "npm": { "name": "aria-query", "version": ">=5.3.0 <6.0.0", }, - "package_id": 430, + "package_id": 426, }, { "behavior": { "normal": true, }, - "id": 737, + "id": 733, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 738, + "id": 734, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 739, + "id": 735, "literal": "^0.0.8", "name": "ast-types-flow", "npm": { "name": "ast-types-flow", "version": ">=0.0.8 <0.0.9", }, - "package_id": 429, + "package_id": 425, }, { "behavior": { "normal": true, }, - "id": 740, + "id": 736, "literal": "=4.7.0", "name": "axe-core", "npm": { "name": "axe-core", "version": "==4.7.0", }, - "package_id": 428, + "package_id": 424, }, { "behavior": { "normal": true, }, - "id": 741, + "id": 737, "literal": "^3.2.1", "name": "axobject-query", "npm": { "name": "axobject-query", "version": ">=3.2.1 <4.0.0", }, - "package_id": 426, + "package_id": 422, }, { "behavior": { "normal": true, }, - "id": 742, + "id": 738, "literal": "^1.0.8", "name": "damerau-levenshtein", "npm": { "name": "damerau-levenshtein", "version": ">=1.0.8 <2.0.0", }, - "package_id": 425, + "package_id": 421, }, { "behavior": { "normal": true, }, - "id": 743, + "id": 739, "literal": "^9.2.2", "name": "emoji-regex", "npm": { @@ -9691,20 +9638,20 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 744, + "id": 740, "literal": "^1.0.15", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.15 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 745, + "id": 741, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -9717,254 +9664,254 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 746, + "id": 742, "literal": "^3.3.5", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=3.3.5 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 747, + "id": 743, "literal": "^1.0.9", "name": "language-tags", "npm": { "name": "language-tags", "version": ">=1.0.9 <2.0.0", }, - "package_id": 410, + "package_id": 406, }, { "behavior": { "normal": true, }, - "id": 748, + "id": 744, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 749, + "id": 745, "literal": "^1.1.7", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.7 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 750, + "id": 746, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "peer": true, }, - "id": 751, + "id": 747, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 752, + "id": 748, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 753, + "id": 749, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 754, + "id": 750, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 755, + "id": 751, "literal": "^0.3.20", "name": "language-subtag-registry", "npm": { "name": "language-subtag-registry", "version": ">=0.3.20 <0.4.0", }, - "package_id": 411, + "package_id": 407, }, { "behavior": { "normal": true, }, - "id": 756, + "id": 752, "literal": "^3.1.6", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.6 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 757, + "id": 753, "literal": "^1.3.1", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.1 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 758, + "id": 754, "literal": "^4.1.4", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.4 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 759, + "id": 755, "literal": "^1.1.6", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.6 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 760, + "id": 756, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 761, + "id": 757, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 762, + "id": 758, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 763, + "id": 759, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 764, + "id": 760, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 765, + "id": 761, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -9977,982 +9924,982 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 766, + "id": 762, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 767, + "id": 763, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 768, + "id": 764, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 769, + "id": 765, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 770, + "id": 766, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 771, + "id": 767, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 772, + "id": 768, "literal": "^1.1.2", "name": "iterator.prototype", "npm": { "name": "iterator.prototype", "version": ">=1.1.2 <2.0.0", }, - "package_id": 414, + "package_id": 410, }, { "behavior": { "normal": true, }, - "id": 773, + "id": 769, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 774, + "id": 770, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 775, + "id": 771, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 776, + "id": 772, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 777, + "id": 773, "literal": "^1.0.4", "name": "reflect.getprototypeof", "npm": { "name": "reflect.getprototypeof", "version": ">=1.0.4 <2.0.0", }, - "package_id": 415, + "package_id": 411, }, { "behavior": { "normal": true, }, - "id": 778, + "id": 774, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 779, + "id": 775, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 780, + "id": 776, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 781, + "id": 777, "literal": "^1.23.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 782, + "id": 778, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 783, + "id": 779, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 784, + "id": 780, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 785, + "id": 781, "literal": "^1.1.3", "name": "which-builtin-type", "npm": { "name": "which-builtin-type", "version": ">=1.1.3 <2.0.0", }, - "package_id": 416, + "package_id": 412, }, { "behavior": { "normal": true, }, - "id": 786, + "id": 782, "literal": "^1.1.5", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.5 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 787, + "id": 783, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 788, + "id": 784, "literal": "^2.0.0", "name": "is-async-function", "npm": { "name": "is-async-function", "version": ">=2.0.0 <3.0.0", }, - "package_id": 424, + "package_id": 420, }, { "behavior": { "normal": true, }, - "id": 789, + "id": 785, "literal": "^1.0.5", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.5 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 790, + "id": 786, "literal": "^1.0.2", "name": "is-finalizationregistry", "npm": { "name": "is-finalizationregistry", "version": ">=1.0.2 <2.0.0", }, - "package_id": 423, + "package_id": 419, }, { "behavior": { "normal": true, }, - "id": 791, + "id": 787, "literal": "^1.0.10", "name": "is-generator-function", "npm": { "name": "is-generator-function", "version": ">=1.0.10 <2.0.0", }, - "package_id": 422, + "package_id": 418, }, { "behavior": { "normal": true, }, - "id": 792, + "id": 788, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 793, + "id": 789, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 794, + "id": 790, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 795, + "id": 791, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 796, + "id": 792, "literal": "^1.0.1", "name": "which-collection", "npm": { "name": "which-collection", "version": ">=1.0.1 <2.0.0", }, - "package_id": 417, + "package_id": 413, }, { "behavior": { "normal": true, }, - "id": 797, + "id": 793, "literal": "^1.1.9", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.9 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 798, + "id": 794, "literal": "^2.0.3", "name": "is-map", "npm": { "name": "is-map", "version": ">=2.0.3 <3.0.0", }, - "package_id": 421, + "package_id": 417, }, { "behavior": { "normal": true, }, - "id": 799, + "id": 795, "literal": "^2.0.3", "name": "is-set", "npm": { "name": "is-set", "version": ">=2.0.3 <3.0.0", }, - "package_id": 420, + "package_id": 416, }, { "behavior": { "normal": true, }, - "id": 800, + "id": 796, "literal": "^2.0.2", "name": "is-weakmap", "npm": { "name": "is-weakmap", "version": ">=2.0.2 <3.0.0", }, - "package_id": 419, + "package_id": 415, }, { "behavior": { "normal": true, }, - "id": 801, + "id": 797, "literal": "^2.0.3", "name": "is-weakset", "npm": { "name": "is-weakset", "version": ">=2.0.3 <3.0.0", }, - "package_id": 418, + "package_id": 414, }, { "behavior": { "normal": true, }, - "id": 802, + "id": 798, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 803, + "id": 799, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 804, + "id": 800, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 805, + "id": 801, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 806, + "id": 802, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 807, + "id": 803, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 808, + "id": 804, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 809, + "id": 805, "literal": "^0.14.0", "name": "regenerator-runtime", "npm": { "name": "regenerator-runtime", "version": ">=0.14.0 <0.15.0", }, - "package_id": 432, + "package_id": 428, }, { "behavior": { "normal": true, }, - "id": 810, + "id": 806, "literal": "^3.1.8", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.8 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 811, + "id": 807, "literal": "^1.2.5", "name": "array.prototype.findlast", "npm": { "name": "array.prototype.findlast", "version": ">=1.2.5 <2.0.0", }, - "package_id": 441, + "package_id": 437, }, { "behavior": { "normal": true, }, - "id": 812, + "id": 808, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 813, + "id": 809, "literal": "^1.1.2", "name": "array.prototype.toreversed", "npm": { "name": "array.prototype.toreversed", "version": ">=1.1.2 <2.0.0", }, - "package_id": 440, + "package_id": 436, }, { "behavior": { "normal": true, }, - "id": 814, + "id": 810, "literal": "^1.1.3", "name": "array.prototype.tosorted", "npm": { "name": "array.prototype.tosorted", "version": ">=1.1.3 <2.0.0", }, - "package_id": 439, + "package_id": 435, }, { "behavior": { "normal": true, }, - "id": 815, + "id": 811, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 816, + "id": 812, "literal": "^1.0.19", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.19 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 817, + "id": 813, "literal": "^5.3.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.3.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 818, + "id": 814, "literal": "^2.4.1 || ^3.0.0", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=2.4.1 <3.0.0 || >=3.0.0 <4.0.0 && >=3.0.0 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 819, + "id": 815, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 820, + "id": 816, "literal": "^1.1.8", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.8 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 821, + "id": 817, "literal": "^2.0.8", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.8 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 822, + "id": 818, "literal": "^1.1.4", "name": "object.hasown", "npm": { "name": "object.hasown", "version": ">=1.1.4 <2.0.0", }, - "package_id": 438, + "package_id": 434, }, { "behavior": { "normal": true, }, - "id": 823, + "id": 819, "literal": "^1.2.0", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.2.0 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 824, + "id": 820, "literal": "^15.8.1", "name": "prop-types", "npm": { "name": "prop-types", "version": ">=15.8.1 <16.0.0", }, - "package_id": 436, + "package_id": 432, }, { "behavior": { "normal": true, }, - "id": 825, + "id": 821, "literal": "^2.0.0-next.5", "name": "resolve", "npm": { "name": "resolve", "version": ">=2.0.0-next.5 <3.0.0", }, - "package_id": 435, + "package_id": 431, }, { "behavior": { "normal": true, }, - "id": 826, + "id": 822, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 827, + "id": 823, "literal": "^4.0.11", "name": "string.prototype.matchall", "npm": { "name": "string.prototype.matchall", "version": ">=4.0.11 <5.0.0", }, - "package_id": 434, + "package_id": 430, }, { "behavior": { "peer": true, }, - "id": 828, + "id": 824, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 829, + "id": 825, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 830, + "id": 826, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 831, + "id": 827, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 832, + "id": 828, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 833, + "id": 829, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 834, + "id": 830, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 835, + "id": 831, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 836, + "id": 832, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 837, + "id": 833, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 838, + "id": 834, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 839, + "id": 835, "literal": "^2.0.2", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.2 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 840, + "id": 836, "literal": "^1.0.6", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.6 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 841, + "id": 837, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -10965,7 +10912,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 842, + "id": 838, "literal": "^1.0.7", "name": "path-parse", "npm": { @@ -10978,7 +10925,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 843, + "id": 839, "literal": "^1.0.0", "name": "supports-preserve-symlinks-flag", "npm": { @@ -10991,7 +10938,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 844, + "id": 840, "literal": "^1.4.0", "name": "loose-envify", "npm": { @@ -11004,7 +10951,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 845, + "id": 841, "literal": "^4.1.1", "name": "object-assign", "npm": { @@ -11017,345 +10964,345 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 846, + "id": 842, "literal": "^16.13.1", "name": "react-is", "npm": { "name": "react-is", "version": ">=16.13.1 <17.0.0", }, - "package_id": 437, + "package_id": 433, }, { "behavior": { "normal": true, }, - "id": 847, + "id": 843, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 848, + "id": 844, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 849, + "id": 845, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 850, + "id": 846, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 851, + "id": 847, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 852, + "id": 848, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 853, + "id": 849, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 854, + "id": 850, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 855, + "id": 851, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 856, + "id": 852, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 857, + "id": 853, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 858, + "id": 854, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 859, + "id": 855, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 860, + "id": 856, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 861, + "id": 857, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 862, + "id": 858, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 863, + "id": 859, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 864, + "id": 860, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 865, + "id": 861, "literal": "~8.5.10", "name": "@types/ws", "npm": { "name": "@types/ws", "version": ">=8.5.10 <8.6.0", }, - "package_id": 443, + "package_id": 439, }, { "behavior": { "normal": true, }, - "id": 866, + "id": 862, "literal": "~20.12.8", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=20.12.8 <20.13.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 867, + "id": 863, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 868, + "id": 864, "literal": "^4.21.10", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.10 <5.0.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 869, + "id": 865, "literal": "^1.0.30001538", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001538 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 870, + "id": 866, "literal": "^4.3.6", "name": "fraction.js", "npm": { "name": "fraction.js", "version": ">=4.3.6 <5.0.0", }, - "package_id": 446, + "package_id": 442, }, { "behavior": { "normal": true, }, - "id": 871, + "id": 867, "literal": "^0.1.2", "name": "normalize-range", "npm": { "name": "normalize-range", "version": ">=0.1.2 <0.2.0", }, - "package_id": 445, + "package_id": 441, }, { "behavior": { "normal": true, }, - "id": 872, + "id": 868, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -11368,7 +11315,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 873, + "id": 869, "literal": "^4.2.0", "name": "postcss-value-parser", "npm": { @@ -11381,7 +11328,7 @@ exports[`next build works: bun 1`] = ` "behavior": { "peer": true, }, - "id": 874, + "id": 870, "literal": "^8.1.0", "name": "postcss", "npm": { @@ -11394,72 +11341,72 @@ exports[`next build works: bun 1`] = ` "behavior": { "normal": true, }, - "id": 875, + "id": 871, "literal": "^1.0.30001587", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001587 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 876, + "id": 872, "literal": "^1.4.668", "name": "electron-to-chromium", "npm": { "name": "electron-to-chromium", "version": ">=1.4.668 <2.0.0", }, - "package_id": 450, + "package_id": 446, }, { "behavior": { "normal": true, }, - "id": 877, + "id": 873, "literal": "^2.0.14", "name": "node-releases", "npm": { "name": "node-releases", "version": ">=2.0.14 <3.0.0", }, - "package_id": 449, + "package_id": 445, }, { "behavior": { "normal": true, }, - "id": 878, + "id": 874, "literal": "^1.0.13", "name": "update-browserslist-db", "npm": { "name": "update-browserslist-db", "version": ">=1.0.13 <2.0.0", }, - "package_id": 448, + "package_id": 444, }, { "behavior": { "normal": true, }, - "id": 879, + "id": 875, "literal": "^3.1.2", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.2 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 880, + "id": 876, "literal": "^1.0.1", "name": "picocolors", "npm": { @@ -11472,128 +11419,128 @@ exports[`next build works: bun 1`] = ` "behavior": { "peer": true, }, - "id": 881, + "id": 877, "literal": ">= 4.21.0", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 882, + "id": 878, "literal": "*", "name": "@types/react", "npm": { "name": "@types/react", "version": ">=0.0.0", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { "normal": true, }, - "id": 883, + "id": 879, "literal": "*", "name": "@types/prop-types", "npm": { "name": "@types/prop-types", "version": ">=0.0.0", }, - "package_id": 455, + "package_id": 451, }, { "behavior": { "normal": true, }, - "id": 884, + "id": 880, "literal": "*", "name": "@types/scheduler", "npm": { "name": "@types/scheduler", "version": ">=0.0.0", }, - "package_id": 454, + "package_id": 450, }, { "behavior": { "normal": true, }, - "id": 885, + "id": 881, "literal": "^3.0.2", "name": "csstype", "npm": { "name": "csstype", "version": ">=3.0.2 <4.0.0", }, - "package_id": 453, + "package_id": 449, }, ], "format": "v2", - "meta_hash": "4688315a50aab25bb1d5fe41e445b346f9c0c71bf75f43ebbc91db59253d9026", + "meta_hash": "632a4f7405ad36643df0c844e942395e7c61cf79c7738eb128eba03ebdd1e094", "package_index": { "@alloc/quick-lru": 13, - "@babel/code-frame": 206, - "@babel/helper-validator-identifier": 215, - "@babel/highlight": 207, - "@babel/runtime": 431, - "@eslint-community/eslint-utils": 245, - "@eslint-community/regexpp": 252, - "@eslint/eslintrc": 265, - "@eslint/js": 293, - "@humanwhocodes/config-array": 247, - "@humanwhocodes/module-importer": 244, - "@humanwhocodes/object-schema": 251, + "@babel/code-frame": 202, + "@babel/helper-validator-identifier": 211, + "@babel/highlight": 203, + "@babel/runtime": 427, + "@eslint-community/eslint-utils": 241, + "@eslint-community/regexpp": 248, + "@eslint/eslintrc": 261, + "@eslint/js": 289, + "@humanwhocodes/config-array": 243, + "@humanwhocodes/module-importer": 240, + "@humanwhocodes/object-schema": 247, "@isaacs/cliui": 74, "@jridgewell/gen-mapping": 100, "@jridgewell/resolve-uri": 103, "@jridgewell/set-array": 104, "@jridgewell/sourcemap-codec": 102, "@jridgewell/trace-mapping": 101, - "@next/env": 237, - "@next/eslint-plugin-next": 406, - "@next/swc-darwin-arm64": 231, - "@next/swc-darwin-x64": 232, - "@next/swc-linux-arm64-gnu": 227, - "@next/swc-linux-arm64-musl": 225, - "@next/swc-linux-x64-gnu": 230, - "@next/swc-linux-x64-musl": 229, - "@next/swc-win32-arm64-msvc": 224, - "@next/swc-win32-ia32-msvc": 226, - "@next/swc-win32-x64-msvc": 228, + "@next/env": 233, + "@next/eslint-plugin-next": 402, + "@next/swc-darwin-arm64": 227, + "@next/swc-darwin-x64": 228, + "@next/swc-linux-arm64-gnu": 223, + "@next/swc-linux-arm64-musl": 221, + "@next/swc-linux-x64-gnu": 226, + "@next/swc-linux-x64-musl": 225, + "@next/swc-win32-arm64-msvc": 220, + "@next/swc-win32-ia32-msvc": 222, + "@next/swc-win32-x64-msvc": 224, "@nodelib/fs.scandir": 46, "@nodelib/fs.stat": 49, "@nodelib/fs.walk": 43, "@pkgjs/parseargs": 73, - "@puppeteer/browsers": 114, - "@rushstack/eslint-patch": 407, - "@swc/helpers": 234, - "@tootallnate/quickjs-emscripten": 177, - "@types/json5": 312, + "@puppeteer/browsers": 115, + "@rushstack/eslint-patch": 403, + "@swc/helpers": 230, + "@tootallnate/quickjs-emscripten": 178, + "@types/json5": 308, "@types/node": [ - 182, - 456, + 183, + 452, ], - "@types/prop-types": 455, - "@types/react": 452, - "@types/react-dom": 451, - "@types/scheduler": 454, - "@types/ws": 443, - "@types/yauzl": 181, - "@typescript-eslint/parser": 394, - "@typescript-eslint/scope-manager": 405, - "@typescript-eslint/types": 397, - "@typescript-eslint/typescript-estree": 395, - "@typescript-eslint/visitor-keys": 396, - "acorn": 272, - "acorn-jsx": 271, - "agent-base": 155, - "ajv": 273, + "@types/prop-types": 451, + "@types/react": 448, + "@types/react-dom": 447, + "@types/scheduler": 450, + "@types/ws": 439, + "@types/yauzl": 182, + "@typescript-eslint/parser": 390, + "@typescript-eslint/scope-manager": 401, + "@typescript-eslint/types": 393, + "@typescript-eslint/typescript-estree": 391, + "@typescript-eslint/visitor-keys": 392, + "acorn": 268, + "acorn-jsx": 267, + "agent-base": 156, + "ajv": 269, "ansi-regex": [ 86, 77, @@ -11601,316 +11548,314 @@ exports[`next build works: bun 1`] = ` "ansi-styles": [ 90, 81, - 212, + 208, ], "any-promise": 62, "anymatch": 55, "arg": 107, - "argparse": 217, - "aria-query": 430, - "array-buffer-byte-length": 378, - "array-includes": 388, - "array-union": 404, - "array.prototype.findlast": 441, - "array.prototype.findlastindex": 387, - "array.prototype.flat": 386, - "array.prototype.flatmap": 384, - "array.prototype.toreversed": 440, - "array.prototype.tosorted": 439, - "arraybuffer.prototype.slice": 377, - "ast-types": 166, - "ast-types-flow": 429, - "autoprefixer": 444, - "available-typed-arrays": 334, - "axe-core": 428, - "axobject-query": 426, - "b4a": 138, + "argparse": 213, + "aria-query": 426, + "array-buffer-byte-length": 374, + "array-includes": 384, + "array-union": 400, + "array.prototype.findlast": 437, + "array.prototype.findlastindex": 383, + "array.prototype.flat": 382, + "array.prototype.flatmap": 380, + "array.prototype.toreversed": 436, + "array.prototype.tosorted": 435, + "arraybuffer.prototype.slice": 373, + "ast-types": 167, + "ast-types-flow": 425, + "autoprefixer": 440, + "available-typed-arrays": 330, + "axe-core": 424, + "axobject-query": 422, + "b4a": 139, "balanced-match": 71, - "bare-events": 136, - "bare-fs": 133, - "bare-os": 132, - "bare-path": 131, - "bare-stream": 134, - "base64-js": 129, - "basic-ftp": 176, + "bare-events": 137, + "bare-fs": 134, + "bare-os": 133, + "bare-path": 132, + "bare-stream": 135, + "base64-js": 130, + "basic-ftp": 177, "binary-extensions": 54, "brace-expansion": [ 70, - 249, + 245, ], "braces": 34, - "browserslist": 447, - "buffer": 127, - "buffer-crc32": 185, - "bun-types": 442, - "busboy": 239, - "call-bind": 326, - "callsites": 221, + "browserslist": 443, + "buffer": 128, + "buffer-crc32": 186, + "bun-types": 438, + "busboy": 235, + "call-bind": 322, + "callsites": 217, "camelcase-css": 31, - "caniuse-lite": 233, + "caniuse-lite": 229, "chalk": [ - 303, - 208, + 299, + 204, ], "chokidar": 50, - "chromium-bidi": 198, - "client-only": 236, - "cliui": 124, + "chromium-bidi": 193, + "client-only": 232, + "cliui": 125, "color-convert": [ 82, - 213, + 209, ], "color-name": [ 83, - 214, + 210, ], "commander": 99, - "concat-map": 250, - "cosmiconfig": 201, - "cross-fetch": 193, + "concat-map": 246, + "cosmiconfig": 197, "cross-spawn": 93, "cssesc": 5, - "csstype": 453, - "damerau-levenshtein": 425, - "data-uri-to-buffer": 175, - "data-view-buffer": 376, - "data-view-byte-length": 375, - "data-view-byte-offset": 374, + "csstype": 449, + "damerau-levenshtein": 421, + "data-uri-to-buffer": 176, + "data-view-buffer": 372, + "data-view-byte-length": 371, + "data-view-byte-offset": 370, "debug": [ - 153, - 189, - 381, + 154, + 190, + 377, ], - "deep-is": 292, + "deep-is": 288, "default-create-template": 0, - "define-data-property": 324, - "define-properties": 317, - "degenerator": 160, - "dequal": 427, - "devtools-protocol": 192, + "define-data-property": 320, + "define-properties": 313, + "degenerator": 161, + "dequal": 423, + "devtools-protocol": 114, "didyoumean": 38, - "dir-glob": 402, + "dir-glob": 398, "dlv": 106, "doctrine": [ - 295, - 383, + 291, + 379, ], "eastasianwidth": 89, - "electron-to-chromium": 450, + "electron-to-chromium": 446, "emoji-regex": [ 88, 80, ], - "end-of-stream": 145, - "enhanced-resolve": 391, - "env-paths": 222, - "error-ex": 204, - "es-abstract": 329, - "es-define-property": 320, - "es-errors": 316, - "es-iterator-helpers": 413, - "es-object-atoms": 315, - "es-set-tostringtag": 373, - "es-shim-unscopables": 385, - "es-to-primitive": 371, - "escalade": 123, + "end-of-stream": 146, + "enhanced-resolve": 387, + "env-paths": 218, + "error-ex": 200, + "es-abstract": 325, + "es-define-property": 316, + "es-errors": 312, + "es-iterator-helpers": 409, + "es-object-atoms": 311, + "es-set-tostringtag": 369, + "es-shim-unscopables": 381, + "es-to-primitive": 367, + "escalade": 124, "escape-string-regexp": [ - 253, - 211, + 249, + 207, ], - "escodegen": 162, - "eslint": 242, - "eslint-config-next": 241, - "eslint-import-resolver-node": 382, - "eslint-import-resolver-typescript": 306, - "eslint-module-utils": 380, - "eslint-plugin-import": 307, - "eslint-plugin-jsx-a11y": 408, - "eslint-plugin-react": 433, - "eslint-plugin-react-hooks": 393, - "eslint-scope": 282, - "eslint-visitor-keys": 246, - "espree": 270, - "esprima": 161, - "esquery": 302, - "esrecurse": 283, - "estraverse": 165, - "esutils": 164, - "extract-zip": 180, - "fast-deep-equal": 278, - "fast-fifo": 140, + "escodegen": 163, + "eslint": 238, + "eslint-config-next": 237, + "eslint-import-resolver-node": 378, + "eslint-import-resolver-typescript": 302, + "eslint-module-utils": 376, + "eslint-plugin-import": 303, + "eslint-plugin-jsx-a11y": 404, + "eslint-plugin-react": 429, + "eslint-plugin-react-hooks": 389, + "eslint-scope": 278, + "eslint-visitor-keys": 242, + "espree": 266, + "esprima": 162, + "esquery": 298, + "esrecurse": 279, + "estraverse": 166, + "esutils": 165, + "extract-zip": 181, + "fast-deep-equal": 274, + "fast-fifo": 141, "fast-glob": 40, - "fast-json-stable-stringify": 277, - "fast-levenshtein": 287, + "fast-json-stable-stringify": 273, + "fast-levenshtein": 283, "fastq": 44, - "fd-slicer": 186, - "file-entry-cache": 254, + "fd-slicer": 187, + "file-entry-cache": 250, "fill-range": 35, - "find-up": 296, - "flat-cache": 255, - "flatted": 264, - "for-each": 332, + "find-up": 292, + "flat-cache": 251, + "flatted": 260, + "for-each": 328, "foreground-child": 91, - "fraction.js": 446, - "fs-extra": 171, - "fs.realpath": 261, + "fraction.js": 442, + "fs-extra": 172, + "fs.realpath": 257, "fsevents": 51, "function-bind": 21, - "function.prototype.name": 370, - "functions-have-names": 358, - "get-caller-file": 122, - "get-intrinsic": 321, - "get-stream": 188, - "get-symbol-description": 369, - "get-tsconfig": 389, - "get-uri": 170, + "function.prototype.name": 366, + "functions-have-names": 354, + "get-caller-file": 123, + "get-intrinsic": 317, + "get-stream": 189, + "get-symbol-description": 365, + "get-tsconfig": 385, + "get-uri": 171, "glob": [ 65, - 257, + 253, ], "glob-parent": [ 27, 42, ], - "globals": 268, - "globalthis": 368, - "globby": 400, - "gopd": 325, - "graceful-fs": 174, - "graphemer": 294, - "has-bigints": 343, + "globals": 264, + "globalthis": 364, + "globby": 396, + "gopd": 321, + "graceful-fs": 175, + "graphemer": 290, + "has-bigints": 339, "has-flag": [ - 305, - 210, + 301, + 206, ], - "has-property-descriptors": 319, - "has-proto": 323, - "has-symbols": 322, - "has-tostringtag": 331, + "has-property-descriptors": 315, + "has-proto": 319, + "has-symbols": 318, + "has-tostringtag": 327, "hasown": 20, - "http-proxy-agent": 169, - "https-proxy-agent": 168, - "ieee754": 128, - "ignore": 267, - "import-fresh": 218, - "imurmurhash": 284, - "inflight": 260, - "inherits": 259, - "internal-slot": 366, - "ip-address": 150, - "is-array-buffer": 365, - "is-arrayish": 205, - "is-async-function": 424, - "is-bigint": 342, + "http-proxy-agent": 170, + "https-proxy-agent": 169, + "ieee754": 129, + "ignore": 263, + "import-fresh": 214, + "imurmurhash": 280, + "inflight": 256, + "inherits": 255, + "internal-slot": 362, + "ip-address": 151, + "is-array-buffer": 361, + "is-arrayish": 201, + "is-async-function": 420, + "is-bigint": 338, "is-binary-path": 53, - "is-boolean-object": 341, - "is-callable": 333, + "is-boolean-object": 337, + "is-callable": 329, "is-core-module": 19, - "is-data-view": 364, - "is-date-object": 372, + "is-data-view": 360, + "is-date-object": 368, "is-extglob": 29, - "is-finalizationregistry": 423, + "is-finalizationregistry": 419, "is-fullwidth-code-point": 79, - "is-generator-function": 422, + "is-generator-function": 418, "is-glob": 28, - "is-map": 421, - "is-negative-zero": 363, + "is-map": 417, + "is-negative-zero": 359, "is-number": 37, - "is-number-object": 340, - "is-path-inside": 280, - "is-regex": 353, - "is-set": 420, - "is-shared-array-buffer": 362, - "is-string": 339, - "is-symbol": 338, - "is-typed-array": 345, - "is-weakmap": 419, - "is-weakref": 361, - "is-weakset": 418, - "isarray": 355, + "is-number-object": 336, + "is-path-inside": 276, + "is-regex": 349, + "is-set": 416, + "is-shared-array-buffer": 358, + "is-string": 335, + "is-symbol": 334, + "is-typed-array": 341, + "is-weakmap": 415, + "is-weakref": 357, + "is-weakset": 414, + "isarray": 351, "isexe": 95, - "iterator.prototype": 414, + "iterator.prototype": 410, "jackspeak": 72, "jiti": 105, "js-tokens": 111, - "js-yaml": 216, - "jsbn": 152, - "json-buffer": 263, - "json-parse-even-better-errors": 203, - "json-schema-traverse": 276, - "json-stable-stringify-without-jsonify": 243, - "json5": 311, - "jsonfile": 173, - "jsx-ast-utils": 412, - "keyv": 262, - "language-subtag-registry": 411, - "language-tags": 410, - "levn": 288, + "js-yaml": 212, + "jsbn": 153, + "json-buffer": 259, + "json-parse-even-better-errors": 199, + "json-schema-traverse": 272, + "json-stable-stringify-without-jsonify": 239, + "json5": 307, + "jsonfile": 174, + "jsx-ast-utils": 408, + "keyv": 258, + "language-subtag-registry": 407, + "language-tags": 406, + "levn": 284, "lilconfig": [ 11, 39, ], "lines-and-columns": 64, - "locate-path": 298, - "lodash.merge": 281, + "locate-path": 294, + "lodash.merge": 277, "loose-envify": 110, "lru-cache": [ 68, - 178, - 116, + 179, + 117, ], "merge2": 41, "micromatch": 32, "minimatch": [ 69, - 399, - 248, + 395, + 244, ], - "minimist": 310, + "minimist": 306, "minipass": 67, - "mitt": 200, - "ms": 154, + "mitt": 196, + "ms": 155, "mz": 59, "nanoid": 10, - "natural-compare": 279, - "netmask": 159, - "next": 223, - "node-fetch": 194, - "node-releases": 449, + "natural-compare": 275, + "netmask": 160, + "next": 219, + "node-releases": 445, "normalize-path": 25, - "normalize-range": 445, + "normalize-range": 441, "object-assign": 63, "object-hash": 26, - "object-inspect": 360, - "object-keys": 318, - "object.assign": 359, - "object.entries": 409, - "object.fromentries": 379, - "object.groupby": 328, - "object.hasown": 438, - "object.values": 314, - "once": 143, - "optionator": 286, - "p-limit": 300, - "p-locate": 299, - "pac-proxy-agent": 157, - "pac-resolver": 158, - "parent-module": 220, - "parse-json": 202, - "path-exists": 297, - "path-is-absolute": 258, + "object-inspect": 356, + "object-keys": 314, + "object.assign": 355, + "object.entries": 405, + "object.fromentries": 375, + "object.groupby": 324, + "object.hasown": 434, + "object.values": 310, + "once": 144, + "optionator": 282, + "p-limit": 296, + "p-locate": 295, + "pac-proxy-agent": 158, + "pac-resolver": 159, + "parent-module": 216, + "parse-json": 198, + "path-exists": 293, + "path-is-absolute": 254, "path-key": 98, "path-parse": 18, "path-scurry": 66, - "path-type": 403, - "pend": 187, + "path-type": 399, + "pend": 188, "picocolors": 9, "picomatch": 33, "pify": 23, "pirates": 58, - "possible-typed-array-names": 335, + "possible-typed-array-names": 331, "postcss": [ - 238, + 234, 7, ], "postcss-import": 15, @@ -11919,129 +11864,127 @@ exports[`next build works: bun 1`] = ` "postcss-nested": 14, "postcss-selector-parser": 3, "postcss-value-parser": 24, - "prelude-ls": 290, - "progress": 179, - "prop-types": 436, - "proxy-agent": 146, - "proxy-from-env": 156, - "pump": 142, - "punycode": 275, + "prelude-ls": 286, + "progress": 180, + "prop-types": 432, + "proxy-agent": 147, + "proxy-from-env": 157, + "pump": 143, + "punycode": 271, "puppeteer": 113, - "puppeteer-core": 190, + "puppeteer-core": 191, "queue-microtask": 48, - "queue-tick": 139, + "queue-tick": 140, "react": 109, "react-dom": 108, - "react-is": 437, + "react-is": 433, "read-cache": 22, "readdirp": 52, - "reflect.getprototypeof": 415, - "regenerator-runtime": 432, - "regexp.prototype.flags": 356, - "require-directory": 121, + "reflect.getprototypeof": 411, + "regenerator-runtime": 428, + "regexp.prototype.flags": 352, + "require-directory": 122, "resolve": [ - 435, + 431, 16, ], - "resolve-from": 219, - "resolve-pkg-maps": 390, + "resolve-from": 215, + "resolve-pkg-maps": 386, "reusify": 45, - "rimraf": 256, + "rimraf": 252, "run-parallel": 47, - "safe-array-concat": 354, - "safe-regex-test": 352, + "safe-array-concat": 350, + "safe-regex-test": 348, "scheduler": 112, "semver": [ - 115, - 313, + 116, + 309, ], - "set-function-length": 327, - "set-function-name": 357, + "set-function-length": 323, + "set-function-name": 353, "shebang-command": 96, "shebang-regex": 97, - "side-channel": 367, + "side-channel": 363, "signal-exit": 92, - "slash": 401, - "smart-buffer": 149, - "socks": 148, - "socks-proxy-agent": 147, - "source-map": 163, + "slash": 397, + "smart-buffer": 150, + "socks": 149, + "socks-proxy-agent": 148, + "source-map": 164, "source-map-js": 8, - "sprintf-js": 151, - "streamsearch": 240, - "streamx": 135, + "sprintf-js": 152, + "streamsearch": 236, + "streamx": 136, "string-width": [ 87, 78, ], - "string.prototype.matchall": 434, - "string.prototype.trim": 351, - "string.prototype.trimend": 350, - "string.prototype.trimstart": 349, + "string.prototype.matchall": 430, + "string.prototype.trim": 347, + "string.prototype.trimend": 346, + "string.prototype.trimstart": 345, "strip-ansi": [ 85, 76, ], - "strip-bom": 309, - "strip-json-comments": 266, - "styled-jsx": 235, + "strip-bom": 305, + "strip-json-comments": 262, + "styled-jsx": 231, "sucrase": 56, "supports-color": [ - 304, - 209, + 300, + 205, ], "supports-preserve-symlinks-flag": 17, "tailwindcss": 2, - "tapable": 392, - "tar-fs": 130, - "tar-stream": 141, - "text-decoder": 137, - "text-table": 285, + "tapable": 388, + "tar-fs": 131, + "tar-stream": 142, + "text-decoder": 138, + "text-table": 281, "thenify": 61, "thenify-all": 60, - "through": 126, + "through": 127, "to-regex-range": 36, - "tr46": 197, - "ts-api-utils": 398, + "ts-api-utils": 394, "ts-interface-checker": 57, - "tsconfig-paths": 308, - "tslib": 167, - "type-check": 289, - "type-fest": 269, - "typed-array-buffer": 348, - "typed-array-byte-length": 347, - "typed-array-byte-offset": 346, - "typed-array-length": 344, + "tsconfig-paths": 304, + "tslib": 168, + "type-check": 285, + "type-fest": 265, + "typed-array-buffer": 344, + "typed-array-byte-length": 343, + "typed-array-byte-offset": 342, + "typed-array-length": 340, "typescript": 1, - "unbox-primitive": 336, - "unbzip2-stream": 125, - "undici-types": 183, - "universalify": 172, - "update-browserslist-db": 448, - "uri-js": 274, - "urlpattern-polyfill": 199, + "unbox-primitive": 332, + "unbzip2-stream": 126, + "undici-types": 184, + "universalify": 173, + "update-browserslist-db": 444, + "uri-js": 270, + "urlpattern-polyfill": 195, "util-deprecate": 4, - "webidl-conversions": 196, - "whatwg-url": 195, "which": 94, - "which-boxed-primitive": 337, - "which-builtin-type": 416, - "which-collection": 417, - "which-typed-array": 330, - "word-wrap": 291, + "which-boxed-primitive": 333, + "which-builtin-type": 412, + "which-collection": 413, + "which-typed-array": 326, + "word-wrap": 287, "wrap-ansi": [ 84, 75, ], - "wrappy": 144, - "ws": 191, - "y18n": 120, - "yallist": 117, + "wrappy": 145, + "ws": 192, + "y18n": 121, + "yallist": 118, "yaml": 12, - "yargs": 118, - "yargs-parser": 119, - "yauzl": 184, - "yocto-queue": 301, + "yargs": 119, + "yargs-parser": 120, + "yauzl": 185, + "yocto-queue": 297, + "zod": 194, }, "packages": [ { @@ -14108,17 +14051,34 @@ exports[`next build works: bun 1`] = ` 153, 154, 155, + 156, ], "id": 113, - "integrity": "sha512-Mag1wRLanzwS4yEUyrDRBUgsKlH3dpL6oAfVwNHG09oxd0+ySsatMvYj7HwjynWy/S+Hg+XHLgjyC/F6CsL/lg==", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "man_dir": "", "name": "puppeteer", "name_hash": "13072297456933147981", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", "tag": "npm", - "value": "22.4.1", + "value": "22.12.0", + }, + "scripts": {}, + }, + { + "bin": null, + "dependencies": [], + "id": 114, + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==", + "man_dir": "", + "name": "devtools-protocol", + "name_hash": "12159960943916763407", + "origin": "npm", + "resolution": { + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", + "tag": "npm", + "value": "0.0.1299070", }, "scripts": {}, }, @@ -14128,7 +14088,6 @@ exports[`next build works: bun 1`] = ` "name": "browsers", }, "dependencies": [ - 156, 157, 158, 159, @@ -14136,17 +14095,18 @@ exports[`next build works: bun 1`] = ` 161, 162, 163, + 164, ], - "id": 114, - "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "id": 115, + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "man_dir": "", "name": "@puppeteer/browsers", "name_hash": "6318517029770692415", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", "tag": "npm", - "value": "2.1.0", + "value": "2.2.3", }, "scripts": {}, }, @@ -14156,9 +14116,9 @@ exports[`next build works: bun 1`] = ` "name": "semver", }, "dependencies": [ - 164, + 165, ], - "id": 115, + "id": 116, "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "man_dir": "", "name": "semver", @@ -14174,9 +14134,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 165, + 166, ], - "id": 116, + "id": 117, "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "man_dir": "", "name": "lru-cache", @@ -14192,7 +14152,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 117, + "id": 118, "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "man_dir": "", "name": "yallist", @@ -14208,15 +14168,15 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 166, 167, 168, 169, 170, 171, 172, + 173, ], - "id": 118, + "id": 119, "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "man_dir": "", "name": "yargs", @@ -14232,7 +14192,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 119, + "id": 120, "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "man_dir": "", "name": "yargs-parser", @@ -14248,7 +14208,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 120, + "id": 121, "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "man_dir": "", "name": "y18n", @@ -14264,7 +14224,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 121, + "id": 122, "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "man_dir": "", "name": "require-directory", @@ -14280,7 +14240,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 122, + "id": 123, "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "man_dir": "", "name": "get-caller-file", @@ -14296,7 +14256,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 123, + "id": 124, "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "man_dir": "", "name": "escalade", @@ -14312,11 +14272,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 173, 174, 175, + 176, ], - "id": 124, + "id": 125, "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "man_dir": "", "name": "cliui", @@ -14332,10 +14292,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 176, 177, + 178, ], - "id": 125, + "id": 126, "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "man_dir": "", "name": "unbzip2-stream", @@ -14351,7 +14311,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 126, + "id": 127, "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "man_dir": "", "name": "through", @@ -14367,10 +14327,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 178, 179, + 180, ], - "id": 127, + "id": 128, "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "man_dir": "", "name": "buffer", @@ -14386,7 +14346,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 128, + "id": 129, "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "man_dir": "", "name": "ieee754", @@ -14402,7 +14362,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 129, + "id": 130, "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "man_dir": "", "name": "base64-js", @@ -14418,12 +14378,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 180, 181, 182, 183, + 184, ], - "id": 130, + "id": 131, "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "man_dir": "", "name": "tar-fs", @@ -14439,9 +14399,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 184, + 185, ], - "id": 131, + "id": 132, "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "man_dir": "", "name": "bare-path", @@ -14457,7 +14417,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 132, + "id": 133, "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", "man_dir": "", "name": "bare-os", @@ -14473,11 +14433,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 185, 186, 187, + 188, ], - "id": 133, + "id": 134, "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", "man_dir": "", "name": "bare-fs", @@ -14493,9 +14453,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 188, + 189, ], - "id": 134, + "id": 135, "integrity": "sha512-ubLyoDqPnUf5o0kSFp709HC0WRZuxVuh4pbte5eY95Xvx5bdvz07c2JFmXBfqqe60q+9PJ8S4X5GRvmcNSKMxg==", "man_dir": "", "name": "bare-stream", @@ -14511,12 +14471,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 189, 190, 191, 192, + 193, ], - "id": 135, + "id": 136, "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", "man_dir": "", "name": "streamx", @@ -14532,7 +14492,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 136, + "id": 137, "integrity": "sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA==", "man_dir": "", "name": "bare-events", @@ -14548,9 +14508,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 193, + 194, ], - "id": 137, + "id": 138, "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", "man_dir": "", "name": "text-decoder", @@ -14566,7 +14526,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 138, + "id": 139, "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "man_dir": "", "name": "b4a", @@ -14582,7 +14542,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 139, + "id": 140, "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "man_dir": "", "name": "queue-tick", @@ -14598,7 +14558,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 140, + "id": 141, "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "man_dir": "", "name": "fast-fifo", @@ -14614,11 +14574,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 194, 195, 196, + 197, ], - "id": 141, + "id": 142, "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "man_dir": "", "name": "tar-stream", @@ -14634,10 +14594,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 197, 198, + 199, ], - "id": 142, + "id": 143, "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "man_dir": "", "name": "pump", @@ -14653,9 +14613,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 199, + 200, ], - "id": 143, + "id": 144, "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "man_dir": "", "name": "once", @@ -14671,7 +14631,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 144, + "id": 145, "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "man_dir": "", "name": "wrappy", @@ -14687,9 +14647,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 200, + 201, ], - "id": 145, + "id": 146, "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "man_dir": "", "name": "end-of-stream", @@ -14705,7 +14665,6 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 201, 202, 203, 204, @@ -14713,8 +14672,9 @@ exports[`next build works: bun 1`] = ` 206, 207, 208, + 209, ], - "id": 146, + "id": 147, "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "man_dir": "", "name": "proxy-agent", @@ -14730,11 +14690,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 209, 210, 211, + 212, ], - "id": 147, + "id": 148, "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "man_dir": "", "name": "socks-proxy-agent", @@ -14750,10 +14710,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 212, 213, + 214, ], - "id": 148, + "id": 149, "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "man_dir": "", "name": "socks", @@ -14769,7 +14729,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 149, + "id": 150, "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "man_dir": "", "name": "smart-buffer", @@ -14785,10 +14745,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 214, 215, + 216, ], - "id": 150, + "id": 151, "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "man_dir": "", "name": "ip-address", @@ -14804,7 +14764,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 151, + "id": 152, "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "man_dir": "", "name": "sprintf-js", @@ -14820,7 +14780,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 152, + "id": 153, "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "man_dir": "", "name": "jsbn", @@ -14836,9 +14796,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 216, + 217, ], - "id": 153, + "id": 154, "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "man_dir": "", "name": "debug", @@ -14854,7 +14814,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 154, + "id": 155, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "man_dir": "", "name": "ms", @@ -14870,9 +14830,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 217, + 218, ], - "id": 155, + "id": 156, "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "man_dir": "", "name": "agent-base", @@ -14888,7 +14848,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 156, + "id": 157, "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "man_dir": "", "name": "proxy-from-env", @@ -14904,7 +14864,6 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 218, 219, 220, 221, @@ -14912,8 +14871,9 @@ exports[`next build works: bun 1`] = ` 223, 224, 225, + 226, ], - "id": 157, + "id": 158, "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "man_dir": "", "name": "pac-proxy-agent", @@ -14929,10 +14889,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 226, 227, + 228, ], - "id": 158, + "id": 159, "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "man_dir": "", "name": "pac-resolver", @@ -14948,7 +14908,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 159, + "id": 160, "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "man_dir": "", "name": "netmask", @@ -14964,11 +14924,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 228, 229, 230, + 231, ], - "id": 160, + "id": 161, "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "man_dir": "", "name": "degenerator", @@ -14987,7 +14947,7 @@ exports[`next build works: bun 1`] = ` "esvalidate": "./bin/esvalidate.js", }, "dependencies": [], - "id": 161, + "id": 162, "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "man_dir": "", "name": "esprima", @@ -15006,12 +14966,12 @@ exports[`next build works: bun 1`] = ` "esgenerate": "bin/esgenerate.js", }, "dependencies": [ - 231, 232, 233, 234, + 235, ], - "id": 162, + "id": 163, "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "man_dir": "", "name": "escodegen", @@ -15027,7 +14987,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 163, + "id": 164, "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "man_dir": "", "name": "source-map", @@ -15043,7 +15003,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 164, + "id": 165, "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "man_dir": "", "name": "esutils", @@ -15059,7 +15019,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 165, + "id": 166, "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "man_dir": "", "name": "estraverse", @@ -15075,9 +15035,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 235, + 236, ], - "id": 166, + "id": 167, "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "man_dir": "", "name": "ast-types", @@ -15093,7 +15053,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 167, + "id": 168, "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "man_dir": "", "name": "tslib", @@ -15109,10 +15069,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 236, 237, + 238, ], - "id": 168, + "id": 169, "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "man_dir": "", "name": "https-proxy-agent", @@ -15128,10 +15088,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 238, 239, + 240, ], - "id": 169, + "id": 170, "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "man_dir": "", "name": "http-proxy-agent", @@ -15147,12 +15107,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 240, 241, 242, 243, + 244, ], - "id": 170, + "id": 171, "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "man_dir": "", "name": "get-uri", @@ -15168,11 +15128,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 244, 245, 246, + 247, ], - "id": 171, + "id": 172, "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "man_dir": "", "name": "fs-extra", @@ -15188,7 +15148,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 172, + "id": 173, "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "man_dir": "", "name": "universalify", @@ -15204,10 +15164,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 247, 248, + 249, ], - "id": 173, + "id": 174, "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "man_dir": "", "name": "jsonfile", @@ -15223,7 +15183,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 174, + "id": 175, "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "man_dir": "", "name": "graceful-fs", @@ -15239,7 +15199,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 175, + "id": 176, "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "man_dir": "", "name": "data-uri-to-buffer", @@ -15255,7 +15215,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 176, + "id": 177, "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "man_dir": "", "name": "basic-ftp", @@ -15271,7 +15231,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 177, + "id": 178, "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "man_dir": "", "name": "@tootallnate/quickjs-emscripten", @@ -15287,7 +15247,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 178, + "id": 179, "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "man_dir": "", "name": "lru-cache", @@ -15303,7 +15263,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 179, + "id": 180, "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "man_dir": "", "name": "progress", @@ -15322,12 +15282,12 @@ exports[`next build works: bun 1`] = ` "name": "extract-zip", }, "dependencies": [ - 249, 250, 251, 252, + 253, ], - "id": 180, + "id": 181, "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "man_dir": "", "name": "extract-zip", @@ -15343,9 +15303,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 253, + 254, ], - "id": 181, + "id": 182, "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "man_dir": "", "name": "@types/yauzl", @@ -15361,9 +15321,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 254, + 255, ], - "id": 182, + "id": 183, "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", "man_dir": "", "name": "@types/node", @@ -15379,7 +15339,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 183, + "id": 184, "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "man_dir": "", "name": "undici-types", @@ -15395,10 +15355,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 255, 256, + 257, ], - "id": 184, + "id": 185, "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "man_dir": "", "name": "yauzl", @@ -15414,7 +15374,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 185, + "id": 186, "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "man_dir": "", "name": "buffer-crc32", @@ -15430,9 +15390,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 257, + 258, ], - "id": 186, + "id": 187, "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "man_dir": "", "name": "fd-slicer", @@ -15448,7 +15408,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 187, + "id": 188, "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "man_dir": "", "name": "pend", @@ -15464,9 +15424,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 258, + 259, ], - "id": 188, + "id": 189, "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "man_dir": "", "name": "get-stream", @@ -15482,9 +15442,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 259, + 260, ], - "id": 189, + "id": 190, "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "man_dir": "", "name": "debug", @@ -15500,23 +15460,22 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 260, 261, 262, 263, 264, 265, ], - "id": 190, - "integrity": "sha512-l9nf8NcirYOHdID12CIMWyy7dqcJCVtgVS+YAiJuUJHg8+9yjgPiG2PcNhojIEEpCkvw3FxvnyITVfKVmkWpjA==", + "id": 191, + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", "man_dir": "", "name": "puppeteer-core", "name_hash": "10954685796294859150", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", "tag": "npm", - "value": "22.4.1", + "value": "22.12.0", }, "scripts": {}, }, @@ -15526,32 +15485,16 @@ exports[`next build works: bun 1`] = ` 266, 267, ], - "id": 191, - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "id": 192, + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "man_dir": "", "name": "ws", "name_hash": "14644737011329074183", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "tag": "npm", - "value": "8.16.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 192, - "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", - "man_dir": "", - "name": "devtools-protocol", - "name_hash": "12159960943916763407", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "tag": "npm", - "value": "0.0.1249869", + "value": "8.17.1", }, "scripts": {}, }, @@ -15559,114 +15502,43 @@ exports[`next build works: bun 1`] = ` "bin": null, "dependencies": [ 268, - ], - "id": 193, - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "man_dir": "", - "name": "cross-fetch", - "name_hash": "5665307032371542913", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "tag": "npm", - "value": "4.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 269, 270, - ], - "id": 194, - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "man_dir": "", - "name": "node-fetch", - "name_hash": "9368364337257117328", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "tag": "npm", - "value": "2.7.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 271, - 272, ], - "id": 195, - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "id": 193, + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "man_dir": "", - "name": "whatwg-url", - "name_hash": "15436316526856444177", + "name": "chromium-bidi", + "name_hash": "17738832193826713561", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", "tag": "npm", - "value": "5.0.0", + "value": "0.5.24", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 196, - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "id": 194, + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "man_dir": "", - "name": "webidl-conversions", - "name_hash": "5343883202058398372", + "name": "zod", + "name_hash": "13942938047053248045", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "tag": "npm", - "value": "3.0.1", + "value": "3.23.8", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 197, - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "man_dir": "", - "name": "tr46", - "name_hash": "4865213169840252474", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "tag": "npm", - "value": "0.0.3", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ - 273, - 274, - 275, - ], - "id": 198, - "integrity": "sha512-sZMgEBWKbupD0Q7lyFu8AWkrE+rs5ycE12jFkGwIgD/VS8lDPtelPlXM7LYaq4zrkZ/O2L3f4afHUHL0ICdKog==", - "man_dir": "", - "name": "chromium-bidi", - "name_hash": "17738832193826713561", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.12.tgz", - "tag": "npm", - "value": "0.5.12", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 199, + "id": 195, "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "man_dir": "", "name": "urlpattern-polyfill", @@ -15682,7 +15554,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 200, + "id": 196, "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "man_dir": "", "name": "mitt", @@ -15698,13 +15570,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 272, + 273, + 274, + 275, 276, - 277, - 278, - 279, - 280, ], - "id": 201, + "id": 197, "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "man_dir": "", "name": "cosmiconfig", @@ -15720,12 +15592,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 281, - 282, - 283, - 284, + 277, + 278, + 279, + 280, ], - "id": 202, + "id": 198, "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "man_dir": "", "name": "parse-json", @@ -15741,7 +15613,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 203, + "id": 199, "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "man_dir": "", "name": "json-parse-even-better-errors", @@ -15757,9 +15629,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 285, + 281, ], - "id": 204, + "id": 200, "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "man_dir": "", "name": "error-ex", @@ -15775,7 +15647,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 205, + "id": 201, "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "man_dir": "", "name": "is-arrayish", @@ -15791,10 +15663,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 286, - 287, + 282, + 283, ], - "id": 206, + "id": 202, "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "man_dir": "", "name": "@babel/code-frame", @@ -15810,12 +15682,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 288, - 289, - 290, - 291, + 284, + 285, + 286, + 287, ], - "id": 207, + "id": 203, "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "man_dir": "", "name": "@babel/highlight", @@ -15831,11 +15703,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 292, - 293, - 294, + 288, + 289, + 290, ], - "id": 208, + "id": 204, "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "man_dir": "", "name": "chalk", @@ -15851,9 +15723,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 295, + 291, ], - "id": 209, + "id": 205, "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "man_dir": "", "name": "supports-color", @@ -15869,7 +15741,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 210, + "id": 206, "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "man_dir": "", "name": "has-flag", @@ -15885,7 +15757,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 211, + "id": 207, "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "man_dir": "", "name": "escape-string-regexp", @@ -15901,9 +15773,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 296, + 292, ], - "id": 212, + "id": 208, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "man_dir": "", "name": "ansi-styles", @@ -15919,9 +15791,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 297, + 293, ], - "id": 213, + "id": 209, "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "man_dir": "", "name": "color-convert", @@ -15937,7 +15809,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 214, + "id": 210, "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "man_dir": "", "name": "color-name", @@ -15953,7 +15825,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 215, + "id": 211, "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "man_dir": "", "name": "@babel/helper-validator-identifier", @@ -15972,9 +15844,9 @@ exports[`next build works: bun 1`] = ` "name": "js-yaml", }, "dependencies": [ - 298, + 294, ], - "id": 216, + "id": 212, "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "man_dir": "", "name": "js-yaml", @@ -15990,7 +15862,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 217, + "id": 213, "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "man_dir": "", "name": "argparse", @@ -16006,10 +15878,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 299, - 300, + 295, + 296, ], - "id": 218, + "id": 214, "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "man_dir": "", "name": "import-fresh", @@ -16025,7 +15897,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 219, + "id": 215, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "man_dir": "", "name": "resolve-from", @@ -16041,9 +15913,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 301, + 297, ], - "id": 220, + "id": 216, "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "man_dir": "", "name": "parent-module", @@ -16059,7 +15931,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 221, + "id": 217, "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "man_dir": "", "name": "callsites", @@ -16075,7 +15947,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 222, + "id": 218, "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "man_dir": "", "name": "env-paths", @@ -16094,6 +15966,10 @@ exports[`next build works: bun 1`] = ` "name": "next", }, "dependencies": [ + 298, + 299, + 300, + 301, 302, 303, 304, @@ -16110,12 +15986,8 @@ exports[`next build works: bun 1`] = ` 315, 316, 317, - 318, - 319, - 320, - 321, ], - "id": 223, + "id": 219, "integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==", "man_dir": "", "name": "next", @@ -16134,7 +16006,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 224, + "id": 220, "integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==", "man_dir": "", "name": "@next/swc-win32-arm64-msvc", @@ -16156,7 +16028,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 225, + "id": 221, "integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==", "man_dir": "", "name": "@next/swc-linux-arm64-musl", @@ -16178,7 +16050,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 226, + "id": 222, "integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==", "man_dir": "", "name": "@next/swc-win32-ia32-msvc", @@ -16200,7 +16072,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 227, + "id": 223, "integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==", "man_dir": "", "name": "@next/swc-linux-arm64-gnu", @@ -16222,7 +16094,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 228, + "id": 224, "integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==", "man_dir": "", "name": "@next/swc-win32-x64-msvc", @@ -16244,7 +16116,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 229, + "id": 225, "integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==", "man_dir": "", "name": "@next/swc-linux-x64-musl", @@ -16266,7 +16138,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 230, + "id": 226, "integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==", "man_dir": "", "name": "@next/swc-linux-x64-gnu", @@ -16288,7 +16160,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 231, + "id": 227, "integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==", "man_dir": "", "name": "@next/swc-darwin-arm64", @@ -16310,7 +16182,7 @@ exports[`next build works: bun 1`] = ` ], "bin": null, "dependencies": [], - "id": 232, + "id": 228, "integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==", "man_dir": "", "name": "@next/swc-darwin-x64", @@ -16329,7 +16201,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 233, + "id": 229, "integrity": "sha512-S3BnR4Kh26TBxbi5t5kpbcUlLJb9lhtDXISDPwOfI+JoC+ik0QksvkZtUVyikw3hjnkgkMPSJ8oIM9yMm9vflA==", "man_dir": "", "name": "caniuse-lite", @@ -16345,9 +16217,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 322, + 318, ], - "id": 234, + "id": 230, "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "man_dir": "", "name": "@swc/helpers", @@ -16363,10 +16235,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 323, - 324, + 319, + 320, ], - "id": 235, + "id": 231, "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "man_dir": "", "name": "styled-jsx", @@ -16382,7 +16254,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 236, + "id": 232, "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "man_dir": "", "name": "client-only", @@ -16398,7 +16270,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 237, + "id": 233, "integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==", "man_dir": "", "name": "@next/env", @@ -16414,11 +16286,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 325, - 326, - 327, + 321, + 322, + 323, ], - "id": 238, + "id": 234, "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "man_dir": "", "name": "postcss", @@ -16434,9 +16306,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 328, + 324, ], - "id": 239, + "id": 235, "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "man_dir": "", "name": "busboy", @@ -16452,7 +16324,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 240, + "id": 236, "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "man_dir": "", "name": "streamsearch", @@ -16468,6 +16340,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 325, + 326, + 327, + 328, 329, 330, 331, @@ -16475,12 +16351,8 @@ exports[`next build works: bun 1`] = ` 333, 334, 335, - 336, - 337, - 338, - 339, ], - "id": 241, + "id": 237, "integrity": "sha512-sUCpWlGuHpEhI0pIT0UtdSLJk5Z8E2DYinPTwsBiWaSYQomchdl0i60pjynY48+oXvtyWMQ7oE+G3m49yrfacg==", "man_dir": "", "name": "eslint-config-next", @@ -16499,6 +16371,10 @@ exports[`next build works: bun 1`] = ` "name": "eslint", }, "dependencies": [ + 336, + 337, + 338, + 339, 340, 341, 342, @@ -16532,12 +16408,8 @@ exports[`next build works: bun 1`] = ` 370, 371, 372, - 373, - 374, - 375, - 376, ], - "id": 242, + "id": 238, "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "man_dir": "", "name": "eslint", @@ -16553,7 +16425,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 243, + "id": 239, "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "man_dir": "", "name": "json-stable-stringify-without-jsonify", @@ -16569,7 +16441,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 244, + "id": 240, "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "man_dir": "", "name": "@humanwhocodes/module-importer", @@ -16585,10 +16457,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 377, - 378, + 373, + 374, ], - "id": 245, + "id": 241, "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "man_dir": "", "name": "@eslint-community/eslint-utils", @@ -16604,7 +16476,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 246, + "id": 242, "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "man_dir": "", "name": "eslint-visitor-keys", @@ -16620,11 +16492,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 379, - 380, - 381, + 375, + 376, + 377, ], - "id": 247, + "id": 243, "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "man_dir": "", "name": "@humanwhocodes/config-array", @@ -16640,9 +16512,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 382, + 378, ], - "id": 248, + "id": 244, "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "man_dir": "", "name": "minimatch", @@ -16658,10 +16530,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 383, - 384, + 379, + 380, ], - "id": 249, + "id": 245, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "man_dir": "", "name": "brace-expansion", @@ -16677,7 +16549,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 250, + "id": 246, "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "man_dir": "", "name": "concat-map", @@ -16693,7 +16565,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 251, + "id": 247, "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "man_dir": "", "name": "@humanwhocodes/object-schema", @@ -16709,7 +16581,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 252, + "id": 248, "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "man_dir": "", "name": "@eslint-community/regexpp", @@ -16725,7 +16597,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 253, + "id": 249, "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "man_dir": "", "name": "escape-string-regexp", @@ -16741,9 +16613,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 385, + 381, ], - "id": 254, + "id": 250, "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "man_dir": "", "name": "file-entry-cache", @@ -16759,11 +16631,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 386, - 387, - 388, + 382, + 383, + 384, ], - "id": 255, + "id": 251, "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "man_dir": "", "name": "flat-cache", @@ -16782,9 +16654,9 @@ exports[`next build works: bun 1`] = ` "name": "rimraf", }, "dependencies": [ - 389, + 385, ], - "id": 256, + "id": 252, "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "man_dir": "", "name": "rimraf", @@ -16800,14 +16672,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 386, + 387, + 388, + 389, 390, 391, - 392, - 393, - 394, - 395, ], - "id": 257, + "id": 253, "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "man_dir": "", "name": "glob", @@ -16823,7 +16695,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 258, + "id": 254, "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "man_dir": "", "name": "path-is-absolute", @@ -16839,7 +16711,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 259, + "id": 255, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "man_dir": "", "name": "inherits", @@ -16855,10 +16727,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 396, - 397, + 392, + 393, ], - "id": 260, + "id": 256, "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "man_dir": "", "name": "inflight", @@ -16874,7 +16746,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 261, + "id": 257, "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "man_dir": "", "name": "fs.realpath", @@ -16890,9 +16762,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 398, + 394, ], - "id": 262, + "id": 258, "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "man_dir": "", "name": "keyv", @@ -16908,7 +16780,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 263, + "id": 259, "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "man_dir": "", "name": "json-buffer", @@ -16924,7 +16796,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 264, + "id": 260, "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "man_dir": "", "name": "flatted", @@ -16940,17 +16812,17 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 395, + 396, + 397, + 398, 399, 400, 401, 402, 403, - 404, - 405, - 406, - 407, ], - "id": 265, + "id": 261, "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "man_dir": "", "name": "@eslint/eslintrc", @@ -16966,7 +16838,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 266, + "id": 262, "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "man_dir": "", "name": "strip-json-comments", @@ -16982,7 +16854,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 267, + "id": 263, "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "man_dir": "", "name": "ignore", @@ -16998,9 +16870,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 408, + 404, ], - "id": 268, + "id": 264, "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "man_dir": "", "name": "globals", @@ -17016,7 +16888,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 269, + "id": 265, "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "man_dir": "", "name": "type-fest", @@ -17032,11 +16904,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 409, - 410, - 411, + 405, + 406, + 407, ], - "id": 270, + "id": 266, "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "man_dir": "", "name": "espree", @@ -17052,9 +16924,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 412, + 408, ], - "id": 271, + "id": 267, "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "man_dir": "", "name": "acorn-jsx", @@ -17073,7 +16945,7 @@ exports[`next build works: bun 1`] = ` "name": "acorn", }, "dependencies": [], - "id": 272, + "id": 268, "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "man_dir": "", "name": "acorn", @@ -17089,12 +16961,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 413, - 414, - 415, - 416, + 409, + 410, + 411, + 412, ], - "id": 273, + "id": 269, "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "man_dir": "", "name": "ajv", @@ -17110,9 +16982,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 417, + 413, ], - "id": 274, + "id": 270, "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "man_dir": "", "name": "uri-js", @@ -17128,7 +17000,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 275, + "id": 271, "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "man_dir": "", "name": "punycode", @@ -17144,7 +17016,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 276, + "id": 272, "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "man_dir": "", "name": "json-schema-traverse", @@ -17160,7 +17032,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 277, + "id": 273, "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "man_dir": "", "name": "fast-json-stable-stringify", @@ -17176,7 +17048,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 278, + "id": 274, "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "man_dir": "", "name": "fast-deep-equal", @@ -17192,7 +17064,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 279, + "id": 275, "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "man_dir": "", "name": "natural-compare", @@ -17208,7 +17080,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 280, + "id": 276, "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "man_dir": "", "name": "is-path-inside", @@ -17224,7 +17096,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 281, + "id": 277, "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "man_dir": "", "name": "lodash.merge", @@ -17240,10 +17112,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 418, - 419, + 414, + 415, ], - "id": 282, + "id": 278, "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "man_dir": "", "name": "eslint-scope", @@ -17259,9 +17131,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 420, + 416, ], - "id": 283, + "id": 279, "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "man_dir": "", "name": "esrecurse", @@ -17277,7 +17149,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 284, + "id": 280, "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "man_dir": "", "name": "imurmurhash", @@ -17293,7 +17165,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 285, + "id": 281, "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "man_dir": "", "name": "text-table", @@ -17309,14 +17181,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 417, + 418, + 419, + 420, 421, 422, - 423, - 424, - 425, - 426, ], - "id": 286, + "id": 282, "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "man_dir": "", "name": "optionator", @@ -17332,7 +17204,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 287, + "id": 283, "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "man_dir": "", "name": "fast-levenshtein", @@ -17348,10 +17220,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 427, - 428, + 423, + 424, ], - "id": 288, + "id": 284, "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "man_dir": "", "name": "levn", @@ -17367,9 +17239,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 429, + 425, ], - "id": 289, + "id": 285, "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "man_dir": "", "name": "type-check", @@ -17385,7 +17257,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 290, + "id": 286, "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "man_dir": "", "name": "prelude-ls", @@ -17401,7 +17273,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 291, + "id": 287, "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "man_dir": "", "name": "word-wrap", @@ -17417,7 +17289,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 292, + "id": 288, "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "man_dir": "", "name": "deep-is", @@ -17433,7 +17305,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 293, + "id": 289, "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "man_dir": "", "name": "@eslint/js", @@ -17449,7 +17321,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 294, + "id": 290, "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "man_dir": "", "name": "graphemer", @@ -17465,9 +17337,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 430, + 426, ], - "id": 295, + "id": 291, "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "man_dir": "", "name": "doctrine", @@ -17483,10 +17355,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 431, - 432, + 427, + 428, ], - "id": 296, + "id": 292, "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "man_dir": "", "name": "find-up", @@ -17502,7 +17374,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 297, + "id": 293, "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "man_dir": "", "name": "path-exists", @@ -17518,9 +17390,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 433, + 429, ], - "id": 298, + "id": 294, "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "man_dir": "", "name": "locate-path", @@ -17536,9 +17408,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 434, + 430, ], - "id": 299, + "id": 295, "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "man_dir": "", "name": "p-locate", @@ -17554,9 +17426,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 435, + 431, ], - "id": 300, + "id": 296, "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "man_dir": "", "name": "p-limit", @@ -17572,7 +17444,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 301, + "id": 297, "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "man_dir": "", "name": "yocto-queue", @@ -17588,9 +17460,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 436, + 432, ], - "id": 302, + "id": 298, "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "man_dir": "", "name": "esquery", @@ -17606,10 +17478,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 437, - 438, + 433, + 434, ], - "id": 303, + "id": 299, "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "man_dir": "", "name": "chalk", @@ -17625,9 +17497,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 439, + 435, ], - "id": 304, + "id": 300, "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "man_dir": "", "name": "supports-color", @@ -17643,7 +17515,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 305, + "id": 301, "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "man_dir": "", "name": "has-flag", @@ -17659,17 +17531,17 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 436, + 437, + 438, + 439, 440, 441, 442, 443, 444, - 445, - 446, - 447, - 448, ], - "id": 306, + "id": 302, "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", "man_dir": "", "name": "eslint-import-resolver-typescript", @@ -17685,6 +17557,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 445, + 446, + 447, + 448, 449, 450, 451, @@ -17699,12 +17575,8 @@ exports[`next build works: bun 1`] = ` 460, 461, 462, - 463, - 464, - 465, - 466, ], - "id": 307, + "id": 303, "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "man_dir": "", "name": "eslint-plugin-import", @@ -17720,12 +17592,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 467, - 468, - 469, - 470, + 463, + 464, + 465, + 466, ], - "id": 308, + "id": 304, "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "man_dir": "", "name": "tsconfig-paths", @@ -17741,7 +17613,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 309, + "id": 305, "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "man_dir": "", "name": "strip-bom", @@ -17757,7 +17629,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 310, + "id": 306, "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "man_dir": "", "name": "minimist", @@ -17776,9 +17648,9 @@ exports[`next build works: bun 1`] = ` "name": "json5", }, "dependencies": [ - 471, + 467, ], - "id": 311, + "id": 307, "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "man_dir": "", "name": "json5", @@ -17794,7 +17666,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 312, + "id": 308, "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "man_dir": "", "name": "@types/json5", @@ -17813,7 +17685,7 @@ exports[`next build works: bun 1`] = ` "name": "semver", }, "dependencies": [], - "id": 313, + "id": 309, "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "man_dir": "", "name": "semver", @@ -17829,11 +17701,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 472, - 473, - 474, + 468, + 469, + 470, ], - "id": 314, + "id": 310, "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "man_dir": "", "name": "object.values", @@ -17849,9 +17721,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 475, + 471, ], - "id": 315, + "id": 311, "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "man_dir": "", "name": "es-object-atoms", @@ -17867,7 +17739,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 316, + "id": 312, "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "man_dir": "", "name": "es-errors", @@ -17883,11 +17755,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 476, - 477, - 478, + 472, + 473, + 474, ], - "id": 317, + "id": 313, "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "man_dir": "", "name": "define-properties", @@ -17903,7 +17775,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 318, + "id": 314, "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "man_dir": "", "name": "object-keys", @@ -17919,9 +17791,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 479, + 475, ], - "id": 319, + "id": 315, "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "man_dir": "", "name": "has-property-descriptors", @@ -17937,9 +17809,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 480, + 476, ], - "id": 320, + "id": 316, "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "man_dir": "", "name": "es-define-property", @@ -17955,13 +17827,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 477, + 478, + 479, + 480, 481, - 482, - 483, - 484, - 485, ], - "id": 321, + "id": 317, "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "man_dir": "", "name": "get-intrinsic", @@ -17977,7 +17849,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 322, + "id": 318, "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "man_dir": "", "name": "has-symbols", @@ -17993,7 +17865,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 323, + "id": 319, "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "man_dir": "", "name": "has-proto", @@ -18009,11 +17881,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 486, - 487, - 488, + 482, + 483, + 484, ], - "id": 324, + "id": 320, "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "man_dir": "", "name": "define-data-property", @@ -18029,9 +17901,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 489, + 485, ], - "id": 325, + "id": 321, "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "man_dir": "", "name": "gopd", @@ -18047,13 +17919,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 486, + 487, + 488, + 489, 490, - 491, - 492, - 493, - 494, ], - "id": 326, + "id": 322, "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "man_dir": "", "name": "call-bind", @@ -18069,14 +17941,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 491, + 492, + 493, + 494, 495, 496, - 497, - 498, - 499, - 500, ], - "id": 327, + "id": 323, "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "man_dir": "", "name": "set-function-length", @@ -18092,11 +17964,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 501, - 502, - 503, + 497, + 498, + 499, ], - "id": 328, + "id": 324, "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "man_dir": "", "name": "object.groupby", @@ -18112,6 +17984,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 500, + 501, + 502, + 503, 504, 505, 506, @@ -18154,12 +18030,8 @@ exports[`next build works: bun 1`] = ` 543, 544, 545, - 546, - 547, - 548, - 549, ], - "id": 329, + "id": 325, "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "man_dir": "", "name": "es-abstract", @@ -18175,13 +18047,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 546, + 547, + 548, + 549, 550, - 551, - 552, - 553, - 554, ], - "id": 330, + "id": 326, "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "man_dir": "", "name": "which-typed-array", @@ -18197,9 +18069,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 555, + 551, ], - "id": 331, + "id": 327, "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "man_dir": "", "name": "has-tostringtag", @@ -18215,9 +18087,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 556, + 552, ], - "id": 332, + "id": 328, "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "man_dir": "", "name": "for-each", @@ -18233,7 +18105,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 333, + "id": 329, "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "man_dir": "", "name": "is-callable", @@ -18249,9 +18121,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 557, + 553, ], - "id": 334, + "id": 330, "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "man_dir": "", "name": "available-typed-arrays", @@ -18267,7 +18139,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 335, + "id": 331, "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "man_dir": "", "name": "possible-typed-array-names", @@ -18283,12 +18155,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 558, - 559, - 560, - 561, + 554, + 555, + 556, + 557, ], - "id": 336, + "id": 332, "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "man_dir": "", "name": "unbox-primitive", @@ -18304,13 +18176,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 558, + 559, + 560, + 561, 562, - 563, - 564, - 565, - 566, ], - "id": 337, + "id": 333, "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "man_dir": "", "name": "which-boxed-primitive", @@ -18326,9 +18198,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 567, + 563, ], - "id": 338, + "id": 334, "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "man_dir": "", "name": "is-symbol", @@ -18344,9 +18216,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 568, + 564, ], - "id": 339, + "id": 335, "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "man_dir": "", "name": "is-string", @@ -18362,9 +18234,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 569, + 565, ], - "id": 340, + "id": 336, "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "man_dir": "", "name": "is-number-object", @@ -18380,10 +18252,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 570, - 571, + 566, + 567, ], - "id": 341, + "id": 337, "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "man_dir": "", "name": "is-boolean-object", @@ -18399,9 +18271,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 572, + 568, ], - "id": 342, + "id": 338, "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "man_dir": "", "name": "is-bigint", @@ -18417,7 +18289,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 343, + "id": 339, "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "man_dir": "", "name": "has-bigints", @@ -18433,14 +18305,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 569, + 570, + 571, + 572, 573, 574, - 575, - 576, - 577, - 578, ], - "id": 344, + "id": 340, "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "man_dir": "", "name": "typed-array-length", @@ -18456,9 +18328,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 579, + 575, ], - "id": 345, + "id": 341, "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "man_dir": "", "name": "is-typed-array", @@ -18474,14 +18346,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 576, + 577, + 578, + 579, 580, 581, - 582, - 583, - 584, - 585, ], - "id": 346, + "id": 342, "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "man_dir": "", "name": "typed-array-byte-offset", @@ -18497,13 +18369,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 582, + 583, + 584, + 585, 586, - 587, - 588, - 589, - 590, ], - "id": 347, + "id": 343, "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "man_dir": "", "name": "typed-array-byte-length", @@ -18519,11 +18391,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 591, - 592, - 593, + 587, + 588, + 589, ], - "id": 348, + "id": 344, "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "man_dir": "", "name": "typed-array-buffer", @@ -18539,11 +18411,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 594, - 595, - 596, + 590, + 591, + 592, ], - "id": 349, + "id": 345, "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "man_dir": "", "name": "string.prototype.trimstart", @@ -18559,11 +18431,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 597, - 598, - 599, + 593, + 594, + 595, ], - "id": 350, + "id": 346, "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "man_dir": "", "name": "string.prototype.trimend", @@ -18579,12 +18451,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 600, - 601, - 602, - 603, + 596, + 597, + 598, + 599, ], - "id": 351, + "id": 347, "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "man_dir": "", "name": "string.prototype.trim", @@ -18600,11 +18472,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 604, - 605, - 606, + 600, + 601, + 602, ], - "id": 352, + "id": 348, "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "man_dir": "", "name": "safe-regex-test", @@ -18620,10 +18492,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 607, - 608, + 603, + 604, ], - "id": 353, + "id": 349, "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "man_dir": "", "name": "is-regex", @@ -18639,12 +18511,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 609, - 610, - 611, - 612, + 605, + 606, + 607, + 608, ], - "id": 354, + "id": 350, "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "man_dir": "", "name": "safe-array-concat", @@ -18660,7 +18532,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 355, + "id": 351, "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "man_dir": "", "name": "isarray", @@ -18676,12 +18548,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 613, - 614, - 615, - 616, + 609, + 610, + 611, + 612, ], - "id": 356, + "id": 352, "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "man_dir": "", "name": "regexp.prototype.flags", @@ -18697,12 +18569,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 617, - 618, - 619, - 620, + 613, + 614, + 615, + 616, ], - "id": 357, + "id": 353, "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "man_dir": "", "name": "set-function-name", @@ -18718,7 +18590,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 358, + "id": 354, "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "man_dir": "", "name": "functions-have-names", @@ -18734,12 +18606,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 621, - 622, - 623, - 624, + 617, + 618, + 619, + 620, ], - "id": 359, + "id": 355, "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "man_dir": "", "name": "object.assign", @@ -18755,7 +18627,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 360, + "id": 356, "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "man_dir": "", "name": "object-inspect", @@ -18771,9 +18643,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 625, + 621, ], - "id": 361, + "id": 357, "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "man_dir": "", "name": "is-weakref", @@ -18789,9 +18661,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 626, + 622, ], - "id": 362, + "id": 358, "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "man_dir": "", "name": "is-shared-array-buffer", @@ -18807,7 +18679,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 363, + "id": 359, "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "man_dir": "", "name": "is-negative-zero", @@ -18823,9 +18695,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 627, + 623, ], - "id": 364, + "id": 360, "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "man_dir": "", "name": "is-data-view", @@ -18841,10 +18713,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 628, - 629, + 624, + 625, ], - "id": 365, + "id": 361, "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "man_dir": "", "name": "is-array-buffer", @@ -18860,11 +18732,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 630, - 631, - 632, + 626, + 627, + 628, ], - "id": 366, + "id": 362, "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "man_dir": "", "name": "internal-slot", @@ -18880,12 +18752,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 633, - 634, - 635, - 636, + 629, + 630, + 631, + 632, ], - "id": 367, + "id": 363, "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "man_dir": "", "name": "side-channel", @@ -18901,10 +18773,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 637, - 638, + 633, + 634, ], - "id": 368, + "id": 364, "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "man_dir": "", "name": "globalthis", @@ -18920,11 +18792,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 639, - 640, - 641, + 635, + 636, + 637, ], - "id": 369, + "id": 365, "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "man_dir": "", "name": "get-symbol-description", @@ -18940,12 +18812,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 642, - 643, - 644, - 645, + 638, + 639, + 640, + 641, ], - "id": 370, + "id": 366, "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "man_dir": "", "name": "function.prototype.name", @@ -18961,11 +18833,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 646, - 647, - 648, + 642, + 643, + 644, ], - "id": 371, + "id": 367, "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "man_dir": "", "name": "es-to-primitive", @@ -18981,9 +18853,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 649, + 645, ], - "id": 372, + "id": 368, "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "man_dir": "", "name": "is-date-object", @@ -18999,11 +18871,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 650, - 651, - 652, + 646, + 647, + 648, ], - "id": 373, + "id": 369, "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "man_dir": "", "name": "es-set-tostringtag", @@ -19019,11 +18891,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 653, - 654, - 655, + 649, + 650, + 651, ], - "id": 374, + "id": 370, "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "man_dir": "", "name": "data-view-byte-offset", @@ -19039,11 +18911,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 656, - 657, - 658, + 652, + 653, + 654, ], - "id": 375, + "id": 371, "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "man_dir": "", "name": "data-view-byte-length", @@ -19059,11 +18931,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 659, - 660, - 661, + 655, + 656, + 657, ], - "id": 376, + "id": 372, "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "man_dir": "", "name": "data-view-buffer", @@ -19079,16 +18951,16 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 658, + 659, + 660, + 661, 662, 663, 664, 665, - 666, - 667, - 668, - 669, ], - "id": 377, + "id": 373, "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "man_dir": "", "name": "arraybuffer.prototype.slice", @@ -19104,10 +18976,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 670, - 671, + 666, + 667, ], - "id": 378, + "id": 374, "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "man_dir": "", "name": "array-buffer-byte-length", @@ -19123,12 +18995,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 672, - 673, - 674, - 675, + 668, + 669, + 670, + 671, ], - "id": 379, + "id": 375, "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "man_dir": "", "name": "object.fromentries", @@ -19144,9 +19016,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 676, + 672, ], - "id": 380, + "id": 376, "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "man_dir": "", "name": "eslint-module-utils", @@ -19162,9 +19034,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 677, + 673, ], - "id": 381, + "id": 377, "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "man_dir": "", "name": "debug", @@ -19180,11 +19052,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 678, - 679, - 680, + 674, + 675, + 676, ], - "id": 382, + "id": 378, "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "man_dir": "", "name": "eslint-import-resolver-node", @@ -19200,9 +19072,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 681, + 677, ], - "id": 383, + "id": 379, "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "man_dir": "", "name": "doctrine", @@ -19218,12 +19090,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 682, - 683, - 684, - 685, + 678, + 679, + 680, + 681, ], - "id": 384, + "id": 380, "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "man_dir": "", "name": "array.prototype.flatmap", @@ -19239,9 +19111,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 686, + 682, ], - "id": 385, + "id": 381, "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "man_dir": "", "name": "es-shim-unscopables", @@ -19257,12 +19129,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 687, - 688, - 689, - 690, + 683, + 684, + 685, + 686, ], - "id": 386, + "id": 382, "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "man_dir": "", "name": "array.prototype.flat", @@ -19278,14 +19150,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 687, + 688, + 689, + 690, 691, 692, - 693, - 694, - 695, - 696, ], - "id": 387, + "id": 383, "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "man_dir": "", "name": "array.prototype.findlastindex", @@ -19301,14 +19173,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 693, + 694, + 695, + 696, 697, 698, - 699, - 700, - 701, - 702, ], - "id": 388, + "id": 384, "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "man_dir": "", "name": "array-includes", @@ -19324,9 +19196,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 703, + 699, ], - "id": 389, + "id": 385, "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "man_dir": "", "name": "get-tsconfig", @@ -19342,7 +19214,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 390, + "id": 386, "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "man_dir": "", "name": "resolve-pkg-maps", @@ -19358,10 +19230,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 704, - 705, + 700, + 701, ], - "id": 391, + "id": 387, "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "man_dir": "", "name": "enhanced-resolve", @@ -19377,7 +19249,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 392, + "id": 388, "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "man_dir": "", "name": "tapable", @@ -19393,9 +19265,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 706, + 702, ], - "id": 393, + "id": 389, "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "man_dir": "", "name": "eslint-plugin-react-hooks", @@ -19411,14 +19283,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 703, + 704, + 705, + 706, 707, 708, - 709, - 710, - 711, - 712, ], - "id": 394, + "id": 390, "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "man_dir": "", "name": "@typescript-eslint/parser", @@ -19434,16 +19306,16 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 709, + 710, + 711, + 712, 713, 714, 715, 716, - 717, - 718, - 719, - 720, ], - "id": 395, + "id": 391, "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "man_dir": "", "name": "@typescript-eslint/typescript-estree", @@ -19459,10 +19331,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 721, - 722, + 717, + 718, ], - "id": 396, + "id": 392, "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "man_dir": "", "name": "@typescript-eslint/visitor-keys", @@ -19478,7 +19350,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 397, + "id": 393, "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "man_dir": "", "name": "@typescript-eslint/types", @@ -19494,9 +19366,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 723, + 719, ], - "id": 398, + "id": 394, "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "man_dir": "", "name": "ts-api-utils", @@ -19512,9 +19384,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 724, + 720, ], - "id": 399, + "id": 395, "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "man_dir": "", "name": "minimatch", @@ -19530,14 +19402,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 721, + 722, + 723, + 724, 725, 726, - 727, - 728, - 729, - 730, ], - "id": 400, + "id": 396, "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "man_dir": "", "name": "globby", @@ -19553,7 +19425,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 401, + "id": 397, "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "man_dir": "", "name": "slash", @@ -19569,9 +19441,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 731, + 727, ], - "id": 402, + "id": 398, "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "man_dir": "", "name": "dir-glob", @@ -19587,7 +19459,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 403, + "id": 399, "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "man_dir": "", "name": "path-type", @@ -19603,7 +19475,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 404, + "id": 400, "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "man_dir": "", "name": "array-union", @@ -19619,10 +19491,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 732, - 733, + 728, + 729, ], - "id": 405, + "id": 401, "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "man_dir": "", "name": "@typescript-eslint/scope-manager", @@ -19638,9 +19510,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 734, + 730, ], - "id": 406, + "id": 402, "integrity": "sha512-VCnZI2cy77Yaj3L7Uhs3+44ikMM1VD/fBMwvTBb3hIaTIuqa+DmG4dhUDq+MASu3yx97KhgsVJbsas0XuiKyww==", "man_dir": "", "name": "@next/eslint-plugin-next", @@ -19656,7 +19528,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 407, + "id": 403, "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "man_dir": "", "name": "@rushstack/eslint-patch", @@ -19672,6 +19544,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 731, + 732, + 733, + 734, 735, 736, 737, @@ -19685,12 +19561,8 @@ exports[`next build works: bun 1`] = ` 745, 746, 747, - 748, - 749, - 750, - 751, ], - "id": 408, + "id": 404, "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", "man_dir": "", "name": "eslint-plugin-jsx-a11y", @@ -19706,11 +19578,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 752, - 753, - 754, + 748, + 749, + 750, ], - "id": 409, + "id": 405, "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "man_dir": "", "name": "object.entries", @@ -19726,9 +19598,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 755, + 751, ], - "id": 410, + "id": 406, "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "man_dir": "", "name": "language-tags", @@ -19744,7 +19616,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 411, + "id": 407, "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "man_dir": "", "name": "language-subtag-registry", @@ -19760,12 +19632,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 756, - 757, - 758, - 759, + 752, + 753, + 754, + 755, ], - "id": 412, + "id": 408, "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "man_dir": "", "name": "jsx-ast-utils", @@ -19781,6 +19653,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 756, + 757, + 758, + 759, 760, 761, 762, @@ -19791,12 +19667,8 @@ exports[`next build works: bun 1`] = ` 767, 768, 769, - 770, - 771, - 772, - 773, ], - "id": 413, + "id": 409, "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "man_dir": "", "name": "es-iterator-helpers", @@ -19812,13 +19684,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 770, + 771, + 772, + 773, 774, - 775, - 776, - 777, - 778, ], - "id": 414, + "id": 410, "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "man_dir": "", "name": "iterator.prototype", @@ -19834,15 +19706,15 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 775, + 776, + 777, + 778, 779, 780, 781, - 782, - 783, - 784, - 785, ], - "id": 415, + "id": 411, "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "man_dir": "", "name": "reflect.getprototypeof", @@ -19858,6 +19730,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 782, + 783, + 784, + 785, 786, 787, 788, @@ -19866,12 +19742,8 @@ exports[`next build works: bun 1`] = ` 791, 792, 793, - 794, - 795, - 796, - 797, ], - "id": 416, + "id": 412, "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", "man_dir": "", "name": "which-builtin-type", @@ -19887,12 +19759,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 798, - 799, - 800, - 801, + 794, + 795, + 796, + 797, ], - "id": 417, + "id": 413, "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "man_dir": "", "name": "which-collection", @@ -19908,10 +19780,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 802, - 803, + 798, + 799, ], - "id": 418, + "id": 414, "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "man_dir": "", "name": "is-weakset", @@ -19927,7 +19799,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 419, + "id": 415, "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "man_dir": "", "name": "is-weakmap", @@ -19943,7 +19815,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 420, + "id": 416, "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "man_dir": "", "name": "is-set", @@ -19959,7 +19831,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 421, + "id": 417, "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "man_dir": "", "name": "is-map", @@ -19975,9 +19847,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 804, + 800, ], - "id": 422, + "id": 418, "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "man_dir": "", "name": "is-generator-function", @@ -19993,9 +19865,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 805, + 801, ], - "id": 423, + "id": 419, "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "man_dir": "", "name": "is-finalizationregistry", @@ -20011,9 +19883,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 806, + 802, ], - "id": 424, + "id": 420, "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "man_dir": "", "name": "is-async-function", @@ -20029,7 +19901,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 425, + "id": 421, "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "man_dir": "", "name": "damerau-levenshtein", @@ -20045,9 +19917,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 807, + 803, ], - "id": 426, + "id": 422, "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "man_dir": "", "name": "axobject-query", @@ -20063,7 +19935,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 427, + "id": 423, "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "man_dir": "", "name": "dequal", @@ -20079,7 +19951,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 428, + "id": 424, "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "man_dir": "", "name": "axe-core", @@ -20095,7 +19967,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 429, + "id": 425, "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "man_dir": "", "name": "ast-types-flow", @@ -20111,9 +19983,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 808, + 804, ], - "id": 430, + "id": 426, "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "man_dir": "", "name": "aria-query", @@ -20129,9 +20001,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 809, + 805, ], - "id": 431, + "id": 427, "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "man_dir": "", "name": "@babel/runtime", @@ -20147,7 +20019,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 432, + "id": 428, "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "man_dir": "", "name": "regenerator-runtime", @@ -20163,6 +20035,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 806, + 807, + 808, + 809, 810, 811, 812, @@ -20178,12 +20054,8 @@ exports[`next build works: bun 1`] = ` 822, 823, 824, - 825, - 826, - 827, - 828, ], - "id": 433, + "id": 429, "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", "man_dir": "", "name": "eslint-plugin-react", @@ -20199,6 +20071,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 825, + 826, + 827, + 828, 829, 830, 831, @@ -20207,12 +20083,8 @@ exports[`next build works: bun 1`] = ` 834, 835, 836, - 837, - 838, - 839, - 840, ], - "id": 434, + "id": 430, "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "man_dir": "", "name": "string.prototype.matchall", @@ -20231,11 +20103,11 @@ exports[`next build works: bun 1`] = ` "name": "resolve", }, "dependencies": [ - 841, - 842, - 843, + 837, + 838, + 839, ], - "id": 435, + "id": 431, "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "man_dir": "", "name": "resolve", @@ -20251,11 +20123,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 844, - 845, - 846, + 840, + 841, + 842, ], - "id": 436, + "id": 432, "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "man_dir": "", "name": "prop-types", @@ -20271,7 +20143,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 437, + "id": 433, "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "man_dir": "", "name": "react-is", @@ -20287,11 +20159,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 847, - 848, - 849, + 843, + 844, + 845, ], - "id": 438, + "id": 434, "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "man_dir": "", "name": "object.hasown", @@ -20307,13 +20179,13 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 846, + 847, + 848, + 849, 850, - 851, - 852, - 853, - 854, ], - "id": 439, + "id": 435, "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "man_dir": "", "name": "array.prototype.tosorted", @@ -20329,12 +20201,12 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 855, - 856, - 857, - 858, + 851, + 852, + 853, + 854, ], - "id": 440, + "id": 436, "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", "man_dir": "", "name": "array.prototype.toreversed", @@ -20350,14 +20222,14 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ + 855, + 856, + 857, + 858, 859, 860, - 861, - 862, - 863, - 864, ], - "id": 441, + "id": 437, "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "man_dir": "", "name": "array.prototype.findlast", @@ -20373,10 +20245,10 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 865, - 866, + 861, + 862, ], - "id": 442, + "id": 438, "integrity": "sha512-DIM2C9qCECwhck9nLsCDeTv943VmGMCkwX3KljjprSRDXaK2CSiUDVGbUit80Er38ukgxuESJgYPAys4FsNCdg==", "man_dir": "", "name": "bun-types", @@ -20392,9 +20264,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 867, + 863, ], - "id": 443, + "id": 439, "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "man_dir": "", "name": "@types/ws", @@ -20413,15 +20285,15 @@ exports[`next build works: bun 1`] = ` "name": "autoprefixer", }, "dependencies": [ + 864, + 865, + 866, + 867, 868, 869, 870, - 871, - 872, - 873, - 874, ], - "id": 444, + "id": 440, "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "man_dir": "", "name": "autoprefixer", @@ -20437,7 +20309,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 445, + "id": 441, "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "man_dir": "", "name": "normalize-range", @@ -20453,7 +20325,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 446, + "id": 442, "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "man_dir": "", "name": "fraction.js", @@ -20472,12 +20344,12 @@ exports[`next build works: bun 1`] = ` "name": "browserslist", }, "dependencies": [ - 875, - 876, - 877, - 878, + 871, + 872, + 873, + 874, ], - "id": 447, + "id": 443, "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "man_dir": "", "name": "browserslist", @@ -20496,11 +20368,11 @@ exports[`next build works: bun 1`] = ` "name": "update-browserslist-db", }, "dependencies": [ - 879, - 880, - 881, + 875, + 876, + 877, ], - "id": 448, + "id": 444, "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "man_dir": "", "name": "update-browserslist-db", @@ -20516,7 +20388,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 449, + "id": 445, "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "man_dir": "", "name": "node-releases", @@ -20532,7 +20404,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 450, + "id": 446, "integrity": "sha512-eVGeQxpaBYbomDBa/Mehrs28MdvCXfJmEFzaMFsv8jH/MJDLIylJN81eTJ5kvx7B7p18OiPK0BkC06lydEy63A==", "man_dir": "", "name": "electron-to-chromium", @@ -20548,9 +20420,9 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 882, + 878, ], - "id": 451, + "id": 447, "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "man_dir": "", "name": "@types/react-dom", @@ -20566,11 +20438,11 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [ - 883, - 884, - 885, + 879, + 880, + 881, ], - "id": 452, + "id": 448, "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", "man_dir": "", "name": "@types/react", @@ -20586,7 +20458,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 453, + "id": 449, "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "man_dir": "", "name": "csstype", @@ -20602,7 +20474,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 454, + "id": 450, "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==", "man_dir": "", "name": "@types/scheduler", @@ -20618,7 +20490,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 455, + "id": 451, "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "man_dir": "", "name": "@types/prop-types", @@ -20634,7 +20506,7 @@ exports[`next build works: bun 1`] = ` { "bin": null, "dependencies": [], - "id": 456, + "id": 452, "integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==", "man_dir": "", "name": "@types/node", @@ -20656,48 +20528,48 @@ exports[`next build works: bun 1`] = ` "package_id": 13, }, "@babel/code-frame": { - "id": 281, - "package_id": 206, + "id": 277, + "package_id": 202, }, "@babel/helper-validator-identifier": { - "id": 288, - "package_id": 215, + "id": 284, + "package_id": 211, }, "@babel/highlight": { - "id": 286, - "package_id": 207, + "id": 282, + "package_id": 203, }, "@babel/runtime": { - "id": 735, - "package_id": 431, + "id": 731, + "package_id": 427, }, "@eslint-community/eslint-utils": { - "id": 374, - "package_id": 245, + "id": 370, + "package_id": 241, }, "@eslint-community/regexpp": { - "id": 372, - "package_id": 252, + "id": 368, + "package_id": 248, }, "@eslint/eslintrc": { - "id": 367, - "package_id": 265, + "id": 363, + "package_id": 261, }, "@eslint/js": { - "id": 355, - "package_id": 293, + "id": 351, + "package_id": 289, }, "@humanwhocodes/config-array": { - "id": 373, - "package_id": 247, + "id": 369, + "package_id": 243, }, "@humanwhocodes/module-importer": { - "id": 375, - "package_id": 244, + "id": 371, + "package_id": 240, }, "@humanwhocodes/object-schema": { - "id": 379, - "package_id": 251, + "id": 375, + "package_id": 247, }, "@isaacs/cliui": { "id": 111, @@ -20724,48 +20596,48 @@ exports[`next build works: bun 1`] = ` "package_id": 101, }, "@next/env": { - "id": 304, - "package_id": 237, + "id": 300, + "package_id": 233, }, "@next/eslint-plugin-next": { - "id": 333, - "package_id": 406, + "id": 329, + "package_id": 402, }, "@next/swc-darwin-arm64": { - "id": 310, - "package_id": 231, + "id": 306, + "package_id": 227, }, "@next/swc-darwin-x64": { - "id": 309, - "package_id": 232, + "id": 305, + "package_id": 228, }, "@next/swc-linux-arm64-gnu": { - "id": 314, - "package_id": 227, + "id": 310, + "package_id": 223, }, "@next/swc-linux-arm64-musl": { - "id": 316, - "package_id": 225, + "id": 312, + "package_id": 221, }, "@next/swc-linux-x64-gnu": { - "id": 311, - "package_id": 230, + "id": 307, + "package_id": 226, }, "@next/swc-linux-x64-musl": { - "id": 312, - "package_id": 229, + "id": 308, + "package_id": 225, }, "@next/swc-win32-arm64-msvc": { - "id": 317, - "package_id": 224, + "id": 313, + "package_id": 220, }, "@next/swc-win32-ia32-msvc": { - "id": 315, - "package_id": 226, + "id": 311, + "package_id": 222, }, "@next/swc-win32-x64-msvc": { - "id": 313, - "package_id": 228, + "id": 309, + "package_id": 224, }, "@nodelib/fs.scandir": { "id": 72, @@ -20776,7 +20648,7 @@ exports[`next build works: bun 1`] = ` "package_id": 49, }, "@nodelib/fs.walk": { - "id": 368, + "id": 364, "package_id": 43, }, "@pkgjs/parseargs": { @@ -20785,94 +20657,94 @@ exports[`next build works: bun 1`] = ` }, "@puppeteer/browsers": { "id": 155, - "package_id": 114, + "package_id": 115, }, "@rushstack/eslint-patch": { - "id": 332, - "package_id": 407, + "id": 328, + "package_id": 403, }, "@swc/helpers": { - "id": 307, - "package_id": 234, + "id": 303, + "package_id": 230, }, "@tootallnate/quickjs-emscripten": { - "id": 218, - "package_id": 177, + "id": 219, + "package_id": 178, }, "@types/json5": { - "id": 467, - "package_id": 312, + "id": 463, + "package_id": 308, }, "@types/node": { "id": 0, - "package_id": 456, + "package_id": 452, }, "@types/prop-types": { - "id": 883, - "package_id": 455, + "id": 879, + "package_id": 451, }, "@types/react": { "id": 1, - "package_id": 452, + "package_id": 448, }, "@types/react-dom": { "id": 2, - "package_id": 451, + "package_id": 447, }, "@types/scheduler": { - "id": 884, - "package_id": 454, + "id": 880, + "package_id": 450, }, "@types/ws": { - "id": 865, - "package_id": 443, + "id": 861, + "package_id": 439, }, "@types/yauzl": { - "id": 252, - "package_id": 181, + "id": 253, + "package_id": 182, }, "@typescript-eslint/parser": { - "id": 334, - "package_id": 394, + "id": 330, + "package_id": 390, }, "@typescript-eslint/scope-manager": { - "id": 710, - "package_id": 405, + "id": 706, + "package_id": 401, }, "@typescript-eslint/types": { - "id": 708, - "package_id": 397, + "id": 704, + "package_id": 393, }, "@typescript-eslint/typescript-estree": { - "id": 711, - "package_id": 395, + "id": 707, + "package_id": 391, }, "@typescript-eslint/visitor-keys": { - "id": 709, - "package_id": 396, + "id": 705, + "package_id": 392, }, "acorn": { - "id": 409, - "package_id": 272, + "id": 405, + "package_id": 268, }, "acorn-jsx": { - "id": 410, - "package_id": 271, + "id": 406, + "package_id": 267, }, "agent-base": { - "id": 201, - "package_id": 155, + "id": 202, + "package_id": 156, }, "ajv": { - "id": 340, - "package_id": 273, + "id": 336, + "package_id": 269, }, "ansi-regex": { "id": 122, "package_id": 77, }, "ansi-styles": { - "id": 437, + "id": 433, "package_id": 81, }, "any-promise": { @@ -20888,180 +20760,180 @@ exports[`next build works: bun 1`] = ` "package_id": 107, }, "argparse": { - "id": 298, - "package_id": 217, + "id": 294, + "package_id": 213, }, "aria-query": { - "id": 736, - "package_id": 430, + "id": 732, + "package_id": 426, }, "array-buffer-byte-length": { - "id": 504, - "package_id": 378, + "id": 500, + "package_id": 374, }, "array-includes": { - "id": 810, - "package_id": 388, + "id": 806, + "package_id": 384, }, "array-union": { - "id": 725, - "package_id": 404, + "id": 721, + "package_id": 400, }, "array.prototype.findlast": { - "id": 811, - "package_id": 441, + "id": 807, + "package_id": 437, }, "array.prototype.findlastindex": { - "id": 450, - "package_id": 387, + "id": 446, + "package_id": 383, }, "array.prototype.flat": { - "id": 451, - "package_id": 386, + "id": 447, + "package_id": 382, }, "array.prototype.flatmap": { - "id": 812, - "package_id": 384, + "id": 808, + "package_id": 380, }, "array.prototype.toreversed": { - "id": 813, - "package_id": 440, + "id": 809, + "package_id": 436, }, "array.prototype.tosorted": { - "id": 814, - "package_id": 439, + "id": 810, + "package_id": 435, }, "arraybuffer.prototype.slice": { - "id": 505, - "package_id": 377, + "id": 501, + "package_id": 373, }, "ast-types": { - "id": 228, - "package_id": 166, + "id": 229, + "package_id": 167, }, "ast-types-flow": { - "id": 739, - "package_id": 429, + "id": 735, + "package_id": 425, }, "autoprefixer": { "id": 3, - "package_id": 444, + "package_id": 440, }, "available-typed-arrays": { - "id": 506, - "package_id": 334, + "id": 502, + "package_id": 330, }, "axe-core": { - "id": 740, - "package_id": 428, + "id": 736, + "package_id": 424, }, "axobject-query": { - "id": 741, - "package_id": 426, + "id": 737, + "package_id": 422, }, "b4a": { - "id": 194, - "package_id": 138, + "id": 195, + "package_id": 139, }, "balanced-match": { - "id": 383, + "id": 379, "package_id": 71, }, "bare-events": { - "id": 185, - "package_id": 136, + "id": 186, + "package_id": 137, }, "bare-fs": { - "id": 182, - "package_id": 133, + "id": 183, + "package_id": 134, }, "bare-os": { - "id": 184, - "package_id": 132, + "id": 185, + "package_id": 133, }, "bare-path": { - "id": 183, - "package_id": 131, + "id": 184, + "package_id": 132, }, "bare-stream": { - "id": 187, - "package_id": 134, + "id": 188, + "package_id": 135, }, "base64-js": { - "id": 178, - "package_id": 129, + "id": 179, + "package_id": 130, }, "basic-ftp": { - "id": 240, - "package_id": 176, + "id": 241, + "package_id": 177, }, "binary-extensions": { "id": 87, "package_id": 54, }, "brace-expansion": { - "id": 382, - "package_id": 249, + "id": 378, + "package_id": 245, }, "braces": { "id": 79, "package_id": 34, }, "browserslist": { - "id": 868, - "package_id": 447, + "id": 864, + "package_id": 443, }, "buffer": { - "id": 176, - "package_id": 127, + "id": 177, + "package_id": 128, }, "buffer-crc32": { - "id": 256, - "package_id": 185, + "id": 257, + "package_id": 186, }, "bun-types": { "id": 4, - "package_id": 442, + "package_id": 438, }, "busboy": { - "id": 302, - "package_id": 239, + "id": 298, + "package_id": 235, }, "call-bind": { - "id": 697, - "package_id": 326, + "id": 693, + "package_id": 322, }, "callsites": { - "id": 301, - "package_id": 221, + "id": 297, + "package_id": 217, }, "camelcase-css": { "id": 59, "package_id": 31, }, "caniuse-lite": { - "id": 869, - "package_id": 233, + "id": 865, + "package_id": 229, }, "chalk": { - "id": 342, - "package_id": 303, + "id": 338, + "package_id": 299, }, "chokidar": { "id": 21, "package_id": 50, }, "chromium-bidi": { - "id": 261, - "package_id": 198, + "id": 262, + "package_id": 193, }, "client-only": { - "id": 323, - "package_id": 236, + "id": 319, + "package_id": 232, }, "cliui": { - "id": 166, - "package_id": 124, + "id": 167, + "package_id": 125, }, "color-convert": { "id": 126, @@ -21076,19 +20948,15 @@ exports[`next build works: bun 1`] = ` "package_id": 99, }, "concat-map": { - "id": 384, - "package_id": 250, + "id": 380, + "package_id": 246, }, "cosmiconfig": { "id": 153, - "package_id": 201, - }, - "cross-fetch": { - "id": 262, - "package_id": 193, + "package_id": 197, }, "cross-spawn": { - "id": 359, + "id": 355, "package_id": 93, }, "cssesc": { @@ -21096,552 +20964,552 @@ exports[`next build works: bun 1`] = ` "package_id": 5, }, "csstype": { - "id": 885, - "package_id": 453, + "id": 881, + "package_id": 449, }, "damerau-levenshtein": { - "id": 742, - "package_id": 425, + "id": 738, + "package_id": 421, }, "data-uri-to-buffer": { - "id": 241, - "package_id": 175, + "id": 242, + "package_id": 176, }, "data-view-buffer": { - "id": 508, - "package_id": 376, + "id": 504, + "package_id": 372, }, "data-view-byte-length": { - "id": 509, - "package_id": 375, + "id": 505, + "package_id": 371, }, "data-view-byte-offset": { - "id": 510, - "package_id": 374, + "id": 506, + "package_id": 370, }, "debug": { - "id": 343, - "package_id": 153, + "id": 339, + "package_id": 154, }, "deep-is": { - "id": 422, - "package_id": 292, + "id": 418, + "package_id": 288, }, "define-data-property": { - "id": 476, - "package_id": 324, + "id": 472, + "package_id": 320, }, "define-properties": { - "id": 698, - "package_id": 317, + "id": 694, + "package_id": 313, }, "degenerator": { - "id": 226, - "package_id": 160, + "id": 227, + "package_id": 161, }, "dequal": { - "id": 808, - "package_id": 427, + "id": 804, + "package_id": 423, }, "devtools-protocol": { - "id": 264, - "package_id": 192, + "id": 156, + "package_id": 114, }, "didyoumean": { "id": 24, "package_id": 38, }, "dir-glob": { - "id": 726, - "package_id": 402, + "id": 722, + "package_id": 398, }, "dlv": { "id": 15, "package_id": 106, }, "doctrine": { - "id": 352, - "package_id": 295, + "id": 348, + "package_id": 291, }, "eastasianwidth": { "id": 132, "package_id": 89, }, "electron-to-chromium": { - "id": 876, - "package_id": 450, + "id": 872, + "package_id": 446, }, "emoji-regex": { - "id": 743, + "id": 739, "package_id": 88, }, "end-of-stream": { - "id": 197, - "package_id": 145, + "id": 198, + "package_id": 146, }, "enhanced-resolve": { - "id": 441, - "package_id": 391, + "id": 437, + "package_id": 387, }, "env-paths": { - "id": 276, - "package_id": 222, + "id": 272, + "package_id": 218, }, "error-ex": { - "id": 282, - "package_id": 204, + "id": 278, + "package_id": 200, }, "es-abstract": { - "id": 699, - "package_id": 329, + "id": 695, + "package_id": 325, }, "es-define-property": { - "id": 490, - "package_id": 320, + "id": 486, + "package_id": 316, }, "es-errors": { - "id": 862, - "package_id": 316, + "id": 858, + "package_id": 312, }, "es-iterator-helpers": { - "id": 816, - "package_id": 413, + "id": 812, + "package_id": 409, }, "es-object-atoms": { - "id": 700, - "package_id": 315, + "id": 696, + "package_id": 311, }, "es-set-tostringtag": { - "id": 764, - "package_id": 373, + "id": 760, + "package_id": 369, }, "es-shim-unscopables": { - "id": 864, - "package_id": 385, + "id": 860, + "package_id": 381, }, "es-to-primitive": { - "id": 515, - "package_id": 371, + "id": 511, + "package_id": 367, }, "escalade": { - "id": 879, - "package_id": 123, + "id": 875, + "package_id": 124, }, "escape-string-regexp": { - "id": 371, - "package_id": 253, + "id": 367, + "package_id": 249, }, "escodegen": { - "id": 229, - "package_id": 162, + "id": 230, + "package_id": 163, }, "eslint": { "id": 5, - "package_id": 242, + "package_id": 238, }, "eslint-config-next": { "id": 6, - "package_id": 241, + "package_id": 237, }, "eslint-import-resolver-node": { - "id": 336, - "package_id": 382, + "id": 332, + "package_id": 378, }, "eslint-import-resolver-typescript": { - "id": 337, - "package_id": 306, + "id": 333, + "package_id": 302, }, "eslint-module-utils": { - "id": 456, - "package_id": 380, + "id": 452, + "package_id": 376, }, "eslint-plugin-import": { - "id": 330, - "package_id": 307, + "id": 326, + "package_id": 303, }, "eslint-plugin-jsx-a11y": { - "id": 331, - "package_id": 408, + "id": 327, + "package_id": 404, }, "eslint-plugin-react": { - "id": 329, - "package_id": 433, + "id": 325, + "package_id": 429, }, "eslint-plugin-react-hooks": { - "id": 335, - "package_id": 393, + "id": 331, + "package_id": 389, }, "eslint-scope": { - "id": 362, - "package_id": 282, + "id": 358, + "package_id": 278, }, "eslint-visitor-keys": { - "id": 370, - "package_id": 246, + "id": 366, + "package_id": 242, }, "espree": { - "id": 344, - "package_id": 270, + "id": 340, + "package_id": 266, }, "esprima": { - "id": 230, - "package_id": 161, + "id": 231, + "package_id": 162, }, "esquery": { - "id": 346, - "package_id": 302, + "id": 342, + "package_id": 298, }, "esrecurse": { - "id": 418, - "package_id": 283, + "id": 414, + "package_id": 279, }, "estraverse": { - "id": 436, - "package_id": 165, + "id": 432, + "package_id": 166, }, "esutils": { - "id": 347, - "package_id": 164, + "id": 343, + "package_id": 165, }, "extract-zip": { - "id": 157, - "package_id": 180, + "id": 158, + "package_id": 181, }, "fast-deep-equal": { - "id": 365, - "package_id": 278, + "id": 361, + "package_id": 274, }, "fast-fifo": { - "id": 195, - "package_id": 140, + "id": 196, + "package_id": 141, }, "fast-glob": { "id": 22, "package_id": 40, }, "fast-json-stable-stringify": { - "id": 414, - "package_id": 277, + "id": 410, + "package_id": 273, }, "fast-levenshtein": { - "id": 426, - "package_id": 287, + "id": 422, + "package_id": 283, }, "fastq": { "id": 73, "package_id": 44, }, "fd-slicer": { - "id": 255, - "package_id": 186, + "id": 256, + "package_id": 187, }, "file-entry-cache": { - "id": 369, - "package_id": 254, + "id": 365, + "package_id": 250, }, "fill-range": { "id": 63, "package_id": 35, }, "find-up": { - "id": 348, - "package_id": 296, + "id": 344, + "package_id": 292, }, "flat-cache": { - "id": 385, - "package_id": 255, + "id": 381, + "package_id": 251, }, "flatted": { - "id": 386, - "package_id": 264, + "id": 382, + "package_id": 260, }, "for-each": { - "id": 587, - "package_id": 332, + "id": 583, + "package_id": 328, }, "foreground-child": { "id": 102, "package_id": 91, }, "fraction.js": { - "id": 870, - "package_id": 446, + "id": 866, + "package_id": 442, }, "fs-extra": { - "id": 243, - "package_id": 171, + "id": 244, + "package_id": 172, }, "fs.realpath": { - "id": 390, - "package_id": 261, + "id": 386, + "package_id": 257, }, "fsevents": { "id": 85, "package_id": 51, }, "function-bind": { - "id": 765, + "id": 761, "package_id": 21, }, "function.prototype.name": { - "id": 516, - "package_id": 370, + "id": 512, + "package_id": 366, }, "functions-have-names": { - "id": 619, - "package_id": 358, + "id": 615, + "package_id": 354, }, "get-caller-file": { - "id": 168, - "package_id": 122, + "id": 169, + "package_id": 123, }, "get-intrinsic": { - "id": 701, - "package_id": 321, + "id": 697, + "package_id": 317, }, "get-stream": { - "id": 250, - "package_id": 188, + "id": 251, + "package_id": 189, }, "get-symbol-description": { - "id": 518, - "package_id": 369, + "id": 514, + "package_id": 365, }, "get-tsconfig": { - "id": 444, - "package_id": 389, + "id": 440, + "package_id": 385, }, "get-uri": { - "id": 221, - "package_id": 170, + "id": 222, + "package_id": 171, }, "glob": { - "id": 734, + "id": 730, "package_id": 65, }, "glob-parent": { - "id": 360, + "id": 356, "package_id": 27, }, "globals": { - "id": 349, - "package_id": 268, + "id": 345, + "package_id": 264, }, "globalthis": { - "id": 767, - "package_id": 368, + "id": 763, + "package_id": 364, }, "globby": { - "id": 714, - "package_id": 400, + "id": 710, + "package_id": 396, }, "gopd": { - "id": 835, - "package_id": 325, + "id": 831, + "package_id": 321, }, "graceful-fs": { - "id": 306, - "package_id": 174, + "id": 302, + "package_id": 175, }, "graphemer": { - "id": 353, - "package_id": 294, + "id": 349, + "package_id": 290, }, "has-bigints": { - "id": 559, - "package_id": 343, + "id": 555, + "package_id": 339, }, "has-flag": { - "id": 439, - "package_id": 305, + "id": 435, + "package_id": 301, }, "has-property-descriptors": { - "id": 768, - "package_id": 319, + "id": 764, + "package_id": 315, }, "has-proto": { - "id": 769, - "package_id": 323, + "id": 765, + "package_id": 319, }, "has-symbols": { - "id": 770, - "package_id": 322, + "id": 766, + "package_id": 318, }, "has-tostringtag": { - "id": 568, - "package_id": 331, + "id": 564, + "package_id": 327, }, "hasown": { - "id": 457, + "id": 453, "package_id": 20, }, "http-proxy-agent": { - "id": 203, - "package_id": 169, + "id": 204, + "package_id": 170, }, "https-proxy-agent": { - "id": 204, - "package_id": 168, + "id": 205, + "package_id": 169, }, "ieee754": { - "id": 179, - "package_id": 128, + "id": 180, + "package_id": 129, }, "ignore": { - "id": 345, - "package_id": 267, + "id": 341, + "package_id": 263, }, "import-fresh": { - "id": 404, - "package_id": 218, + "id": 400, + "package_id": 214, }, "imurmurhash": { - "id": 361, - "package_id": 284, + "id": 357, + "package_id": 280, }, "inflight": { - "id": 391, - "package_id": 260, + "id": 387, + "package_id": 256, }, "inherits": { - "id": 392, - "package_id": 259, + "id": 388, + "package_id": 255, }, "internal-slot": { - "id": 771, - "package_id": 366, + "id": 767, + "package_id": 362, }, "ip-address": { - "id": 212, - "package_id": 150, + "id": 213, + "package_id": 151, }, "is-array-buffer": { - "id": 526, - "package_id": 365, + "id": 522, + "package_id": 361, }, "is-arrayish": { - "id": 285, - "package_id": 205, + "id": 281, + "package_id": 201, }, "is-async-function": { - "id": 788, - "package_id": 424, + "id": 784, + "package_id": 420, }, "is-bigint": { - "id": 562, - "package_id": 342, + "id": 558, + "package_id": 338, }, "is-binary-path": { "id": 81, "package_id": 53, }, "is-boolean-object": { - "id": 563, - "package_id": 341, + "id": 559, + "package_id": 337, }, "is-callable": { - "id": 527, - "package_id": 333, + "id": 523, + "package_id": 329, }, "is-core-module": { - "id": 458, + "id": 454, "package_id": 19, }, "is-data-view": { - "id": 528, - "package_id": 364, + "id": 524, + "package_id": 360, }, "is-date-object": { - "id": 647, - "package_id": 372, + "id": 643, + "package_id": 368, }, "is-extglob": { "id": 58, "package_id": 29, }, "is-finalizationregistry": { - "id": 790, - "package_id": 423, + "id": 786, + "package_id": 419, }, "is-fullwidth-code-point": { "id": 124, "package_id": 79, }, "is-generator-function": { - "id": 791, - "package_id": 422, + "id": 787, + "package_id": 418, }, "is-glob": { - "id": 350, + "id": 346, "package_id": 28, }, "is-map": { - "id": 798, - "package_id": 421, + "id": 794, + "package_id": 417, }, "is-negative-zero": { - "id": 529, - "package_id": 363, + "id": 525, + "package_id": 359, }, "is-number": { "id": 65, "package_id": 37, }, "is-number-object": { - "id": 564, - "package_id": 340, + "id": 560, + "package_id": 336, }, "is-path-inside": { - "id": 364, - "package_id": 280, + "id": 360, + "package_id": 276, }, "is-regex": { - "id": 530, - "package_id": 353, + "id": 526, + "package_id": 349, }, "is-set": { - "id": 799, - "package_id": 420, + "id": 795, + "package_id": 416, }, "is-shared-array-buffer": { - "id": 531, - "package_id": 362, + "id": 527, + "package_id": 358, }, "is-string": { - "id": 702, - "package_id": 339, + "id": 698, + "package_id": 335, }, "is-symbol": { - "id": 648, - "package_id": 338, + "id": 644, + "package_id": 334, }, "is-typed-array": { - "id": 533, - "package_id": 345, + "id": 529, + "package_id": 341, }, "is-weakmap": { - "id": 800, - "package_id": 419, + "id": 796, + "package_id": 415, }, "is-weakref": { - "id": 534, - "package_id": 361, + "id": 530, + "package_id": 357, }, "is-weakset": { - "id": 801, - "package_id": 418, + "id": 797, + "package_id": 414, }, "isarray": { - "id": 612, - "package_id": 355, + "id": 608, + "package_id": 351, }, "isexe": { "id": 140, "package_id": 95, }, "iterator.prototype": { - "id": 772, - "package_id": 414, + "id": 768, + "package_id": 410, }, "jackspeak": { "id": 103, @@ -21656,56 +21524,56 @@ exports[`next build works: bun 1`] = ` "package_id": 111, }, "js-yaml": { - "id": 351, - "package_id": 216, + "id": 347, + "package_id": 212, }, "jsbn": { - "id": 214, - "package_id": 152, + "id": 215, + "package_id": 153, }, "json-buffer": { - "id": 398, - "package_id": 263, + "id": 394, + "package_id": 259, }, "json-parse-even-better-errors": { - "id": 283, - "package_id": 203, + "id": 279, + "package_id": 199, }, "json-schema-traverse": { - "id": 415, - "package_id": 276, + "id": 411, + "package_id": 272, }, "json-stable-stringify-without-jsonify": { - "id": 376, - "package_id": 243, + "id": 372, + "package_id": 239, }, "json5": { - "id": 468, - "package_id": 311, + "id": 464, + "package_id": 307, }, "jsonfile": { - "id": 245, - "package_id": 173, + "id": 246, + "package_id": 174, }, "jsx-ast-utils": { - "id": 818, - "package_id": 412, + "id": 814, + "package_id": 408, }, "keyv": { - "id": 387, - "package_id": 262, + "id": 383, + "package_id": 258, }, "language-subtag-registry": { - "id": 755, - "package_id": 411, + "id": 751, + "package_id": 407, }, "language-tags": { - "id": 747, - "package_id": 410, + "id": 743, + "package_id": 406, }, "levn": { - "id": 341, - "package_id": 288, + "id": 337, + "package_id": 284, }, "lilconfig": { "id": 23, @@ -21716,20 +21584,20 @@ exports[`next build works: bun 1`] = ` "package_id": 64, }, "locate-path": { - "id": 431, - "package_id": 298, + "id": 427, + "package_id": 294, }, "lodash.merge": { - "id": 363, - "package_id": 281, + "id": 359, + "package_id": 277, }, "loose-envify": { "id": 150, "package_id": 110, }, "lru-cache": { - "id": 205, - "package_id": 178, + "id": 206, + "package_id": 179, }, "merge2": { "id": 69, @@ -21740,24 +21608,24 @@ exports[`next build works: bun 1`] = ` "package_id": 32, }, "minimatch": { - "id": 354, - "package_id": 248, + "id": 350, + "package_id": 244, }, "minimist": { - "id": 469, - "package_id": 310, + "id": 465, + "package_id": 306, }, "minipass": { "id": 105, "package_id": 67, }, "mitt": { - "id": 273, - "package_id": 200, + "id": 268, + "package_id": 196, }, "ms": { - "id": 216, - "package_id": 154, + "id": 217, + "package_id": 155, }, "mz": { "id": 94, @@ -21768,35 +21636,31 @@ exports[`next build works: bun 1`] = ` "package_id": 10, }, "natural-compare": { - "id": 366, - "package_id": 279, + "id": 362, + "package_id": 275, }, "netmask": { - "id": 227, - "package_id": 159, + "id": 228, + "package_id": 160, }, "next": { "id": 7, - "package_id": 223, - }, - "node-fetch": { - "id": 268, - "package_id": 194, + "package_id": 219, }, "node-releases": { - "id": 877, - "package_id": 449, + "id": 873, + "package_id": 445, }, "normalize-path": { "id": 30, "package_id": 25, }, "normalize-range": { - "id": 871, - "package_id": 445, + "id": 867, + "package_id": 441, }, "object-assign": { - "id": 845, + "id": 841, "package_id": 63, }, "object-hash": { @@ -21804,76 +21668,76 @@ exports[`next build works: bun 1`] = ` "package_id": 26, }, "object-inspect": { - "id": 535, - "package_id": 360, + "id": 531, + "package_id": 356, }, "object-keys": { - "id": 478, - "package_id": 318, + "id": 474, + "package_id": 314, }, "object.assign": { - "id": 758, - "package_id": 359, + "id": 754, + "package_id": 355, }, "object.entries": { - "id": 820, - "package_id": 409, + "id": 816, + "package_id": 405, }, "object.fromentries": { - "id": 821, - "package_id": 379, + "id": 817, + "package_id": 375, }, "object.groupby": { - "id": 462, - "package_id": 328, + "id": 458, + "package_id": 324, }, "object.hasown": { - "id": 822, - "package_id": 438, + "id": 818, + "package_id": 434, }, "object.values": { - "id": 823, - "package_id": 314, + "id": 819, + "package_id": 310, }, "once": { - "id": 198, - "package_id": 143, + "id": 199, + "package_id": 144, }, "optionator": { - "id": 356, - "package_id": 286, + "id": 352, + "package_id": 282, }, "p-limit": { - "id": 434, - "package_id": 300, + "id": 430, + "package_id": 296, }, "p-locate": { - "id": 433, - "package_id": 299, + "id": 429, + "package_id": 295, }, "pac-proxy-agent": { - "id": 206, - "package_id": 157, + "id": 207, + "package_id": 158, }, "pac-resolver": { - "id": 224, - "package_id": 158, + "id": 225, + "package_id": 159, }, "parent-module": { - "id": 299, - "package_id": 220, + "id": 295, + "package_id": 216, }, "parse-json": { - "id": 279, - "package_id": 202, + "id": 275, + "package_id": 198, }, "path-exists": { - "id": 432, - "package_id": 297, + "id": 428, + "package_id": 293, }, "path-is-absolute": { - "id": 395, - "package_id": 258, + "id": 391, + "package_id": 254, }, "path-key": { "id": 137, @@ -21888,15 +21752,15 @@ exports[`next build works: bun 1`] = ` "package_id": 66, }, "path-type": { - "id": 731, - "package_id": 403, + "id": 727, + "package_id": 399, }, "pend": { - "id": 257, - "package_id": 187, + "id": 258, + "package_id": 188, }, "picocolors": { - "id": 872, + "id": 868, "package_id": 9, }, "picomatch": { @@ -21912,8 +21776,8 @@ exports[`next build works: bun 1`] = ` "package_id": 58, }, "possible-typed-array-names": { - "id": 557, - "package_id": 335, + "id": 553, + "package_id": 331, }, "postcss": { "id": 8, @@ -21940,36 +21804,36 @@ exports[`next build works: bun 1`] = ` "package_id": 3, }, "postcss-value-parser": { - "id": 873, + "id": 869, "package_id": 24, }, "prelude-ls": { - "id": 427, - "package_id": 290, + "id": 423, + "package_id": 286, }, "progress": { - "id": 158, - "package_id": 179, + "id": 159, + "package_id": 180, }, "prop-types": { - "id": 824, - "package_id": 436, + "id": 820, + "package_id": 432, }, "proxy-agent": { - "id": 159, - "package_id": 146, + "id": 160, + "package_id": 147, }, "proxy-from-env": { - "id": 207, - "package_id": 156, + "id": 208, + "package_id": 157, }, "pump": { - "id": 180, - "package_id": 142, + "id": 181, + "package_id": 143, }, "punycode": { - "id": 417, - "package_id": 275, + "id": 413, + "package_id": 271, }, "puppeteer": { "id": 9, @@ -21977,15 +21841,15 @@ exports[`next build works: bun 1`] = ` }, "puppeteer-core": { "id": 154, - "package_id": 190, + "package_id": 191, }, "queue-microtask": { "id": 77, "package_id": 48, }, "queue-tick": { - "id": 190, - "package_id": 139, + "id": 191, + "package_id": 140, }, "react": { "id": 10, @@ -21996,8 +21860,8 @@ exports[`next build works: bun 1`] = ` "package_id": 108, }, "react-is": { - "id": 846, - "package_id": 437, + "id": 842, + "package_id": 433, }, "read-cache": { "id": 48, @@ -22008,68 +21872,68 @@ exports[`next build works: bun 1`] = ` "package_id": 52, }, "reflect.getprototypeof": { - "id": 777, - "package_id": 415, + "id": 773, + "package_id": 411, }, "regenerator-runtime": { - "id": 809, - "package_id": 432, + "id": 805, + "package_id": 428, }, "regexp.prototype.flags": { - "id": 838, - "package_id": 356, + "id": 834, + "package_id": 352, }, "require-directory": { - "id": 169, - "package_id": 121, + "id": 170, + "package_id": 122, }, "resolve": { "id": 19, "package_id": 16, }, "resolve-from": { - "id": 300, - "package_id": 219, + "id": 296, + "package_id": 215, }, "resolve-pkg-maps": { - "id": 703, - "package_id": 390, + "id": 699, + "package_id": 386, }, "reusify": { "id": 74, "package_id": 45, }, "rimraf": { - "id": 388, - "package_id": 256, + "id": 384, + "package_id": 252, }, "run-parallel": { "id": 76, "package_id": 47, }, "safe-array-concat": { - "id": 773, - "package_id": 354, + "id": 769, + "package_id": 350, }, "safe-regex-test": { - "id": 540, - "package_id": 352, + "id": 536, + "package_id": 348, }, "scheduler": { "id": 147, "package_id": 112, }, "semver": { - "id": 826, - "package_id": 313, + "id": 822, + "package_id": 309, }, "set-function-length": { - "id": 494, - "package_id": 327, + "id": 490, + "package_id": 323, }, "set-function-name": { - "id": 839, - "package_id": 357, + "id": 835, + "package_id": 353, }, "shebang-command": { "id": 138, @@ -22080,51 +21944,51 @@ exports[`next build works: bun 1`] = ` "package_id": 97, }, "side-channel": { - "id": 840, - "package_id": 367, + "id": 836, + "package_id": 363, }, "signal-exit": { "id": 136, "package_id": 92, }, "slash": { - "id": 730, - "package_id": 401, + "id": 726, + "package_id": 397, }, "smart-buffer": { - "id": 213, - "package_id": 149, + "id": 214, + "package_id": 150, }, "socks": { - "id": 211, - "package_id": 148, + "id": 212, + "package_id": 149, }, "socks-proxy-agent": { - "id": 208, - "package_id": 147, + "id": 209, + "package_id": 148, }, "source-map": { - "id": 234, - "package_id": 163, + "id": 235, + "package_id": 164, }, "source-map-js": { "id": 44, "package_id": 8, }, "sprintf-js": { - "id": 215, - "package_id": 151, + "id": 216, + "package_id": 152, }, "streamsearch": { - "id": 328, - "package_id": 240, + "id": 324, + "package_id": 236, }, "streamx": { - "id": 196, - "package_id": 135, + "id": 197, + "package_id": 136, }, "string-width": { - "id": 170, + "id": 171, "package_id": 78, }, "string-width-cjs": { @@ -22132,23 +21996,23 @@ exports[`next build works: bun 1`] = ` "package_id": 78, }, "string.prototype.matchall": { - "id": 827, - "package_id": 434, + "id": 823, + "package_id": 430, }, "string.prototype.trim": { - "id": 541, - "package_id": 351, + "id": 537, + "package_id": 347, }, "string.prototype.trimend": { - "id": 542, - "package_id": 350, + "id": 538, + "package_id": 346, }, "string.prototype.trimstart": { - "id": 543, - "package_id": 349, + "id": 539, + "package_id": 345, }, "strip-ansi": { - "id": 357, + "id": 353, "package_id": 76, }, "strip-ansi-cjs": { @@ -22156,24 +22020,24 @@ exports[`next build works: bun 1`] = ` "package_id": 76, }, "strip-bom": { - "id": 470, - "package_id": 309, + "id": 466, + "package_id": 305, }, "strip-json-comments": { - "id": 407, - "package_id": 266, + "id": 403, + "package_id": 262, }, "styled-jsx": { - "id": 305, - "package_id": 235, + "id": 301, + "package_id": 231, }, "sucrase": { "id": 20, "package_id": 56, }, "supports-color": { - "id": 438, - "package_id": 304, + "id": 434, + "package_id": 300, }, "supports-preserve-symlinks-flag": { "id": 53, @@ -22184,24 +22048,24 @@ exports[`next build works: bun 1`] = ` "package_id": 2, }, "tapable": { - "id": 705, - "package_id": 392, + "id": 701, + "package_id": 388, }, "tar-fs": { - "id": 160, - "package_id": 130, + "id": 161, + "package_id": 131, }, "tar-stream": { - "id": 181, - "package_id": 141, + "id": 182, + "package_id": 142, }, "text-decoder": { - "id": 191, - "package_id": 137, + "id": 192, + "package_id": 138, }, "text-table": { - "id": 358, - "package_id": 285, + "id": 354, + "package_id": 281, }, "thenify": { "id": 100, @@ -22212,127 +22076,115 @@ exports[`next build works: bun 1`] = ` "package_id": 60, }, "through": { - "id": 177, - "package_id": 126, + "id": 178, + "package_id": 127, }, "to-regex-range": { "id": 64, "package_id": 36, }, - "tr46": { - "id": 271, - "package_id": 197, - }, "ts-api-utils": { - "id": 718, - "package_id": 398, + "id": 714, + "package_id": 394, }, "ts-interface-checker": { "id": 96, "package_id": 57, }, "tsconfig-paths": { - "id": 465, - "package_id": 308, + "id": 461, + "package_id": 304, }, "tslib": { - "id": 322, - "package_id": 167, + "id": 318, + "package_id": 168, }, "type-check": { - "id": 428, - "package_id": 289, + "id": 424, + "package_id": 285, }, "type-fest": { - "id": 408, - "package_id": 269, + "id": 404, + "package_id": 265, }, "typed-array-buffer": { - "id": 544, - "package_id": 348, + "id": 540, + "package_id": 344, }, "typed-array-byte-length": { - "id": 545, - "package_id": 347, + "id": 541, + "package_id": 343, }, "typed-array-byte-offset": { - "id": 546, - "package_id": 346, + "id": 542, + "package_id": 342, }, "typed-array-length": { - "id": 547, - "package_id": 344, + "id": 543, + "package_id": 340, }, "typescript": { "id": 13, "package_id": 1, }, "unbox-primitive": { - "id": 548, - "package_id": 336, + "id": 544, + "package_id": 332, }, "unbzip2-stream": { - "id": 161, - "package_id": 125, + "id": 162, + "package_id": 126, }, "undici-types": { - "id": 254, - "package_id": 183, + "id": 255, + "package_id": 184, }, "universalify": { - "id": 246, - "package_id": 172, + "id": 247, + "package_id": 173, }, "update-browserslist-db": { - "id": 878, - "package_id": 448, + "id": 874, + "package_id": 444, }, "uri-js": { - "id": 416, - "package_id": 274, + "id": 412, + "package_id": 270, }, "urlpattern-polyfill": { - "id": 274, - "package_id": 199, + "id": 269, + "package_id": 195, }, "util-deprecate": { "id": 37, "package_id": 4, }, - "webidl-conversions": { - "id": 272, - "package_id": 196, - }, - "whatwg-url": { - "id": 269, - "package_id": 195, - }, "which": { "id": 139, "package_id": 94, }, "which-boxed-primitive": { - "id": 561, - "package_id": 337, + "id": 557, + "package_id": 333, }, "which-builtin-type": { - "id": 785, - "package_id": 416, + "id": 781, + "package_id": 412, }, "which-collection": { - "id": 796, - "package_id": 417, + "id": 792, + "package_id": 413, }, "which-typed-array": { - "id": 549, - "package_id": 330, + "id": 545, + "package_id": 326, }, "word-wrap": { - "id": 423, - "package_id": 291, + "id": 419, + "package_id": 287, }, "wrap-ansi": { - "id": 175, + "id": 176, "package_id": 75, }, "wrap-ansi-cjs": { @@ -22340,40 +22192,44 @@ exports[`next build works: bun 1`] = ` "package_id": 75, }, "wrappy": { - "id": 199, - "package_id": 144, + "id": 200, + "package_id": 145, }, "ws": { "id": 265, - "package_id": 191, + "package_id": 192, }, "y18n": { - "id": 171, - "package_id": 120, + "id": 172, + "package_id": 121, }, "yallist": { - "id": 165, - "package_id": 117, + "id": 166, + "package_id": 118, }, "yaml": { "id": 38, "package_id": 12, }, "yargs": { - "id": 162, - "package_id": 118, + "id": 163, + "package_id": 119, }, "yargs-parser": { - "id": 172, - "package_id": 119, + "id": 173, + "package_id": 120, }, "yauzl": { - "id": 251, - "package_id": 184, + "id": 252, + "package_id": 185, }, "yocto-queue": { - "id": 435, - "package_id": 301, + "id": 431, + "package_id": 297, + }, + "zod": { + "id": 270, + "package_id": 194, }, }, "depth": 0, @@ -22383,8 +22239,8 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "@types/node": { - "id": 866, - "package_id": 182, + "id": 862, + "package_id": 183, }, }, "depth": 1, @@ -22394,8 +22250,8 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "postcss": { - "id": 303, - "package_id": 238, + "id": 299, + "package_id": 234, }, }, "depth": 1, @@ -22405,8 +22261,8 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "@types/node": { - "id": 867, - "package_id": 182, + "id": 863, + "package_id": 183, }, }, "depth": 1, @@ -22416,12 +22272,12 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "doctrine": { - "id": 815, - "package_id": 383, + "id": 811, + "package_id": 379, }, "resolve": { - "id": 825, - "package_id": 435, + "id": 821, + "package_id": 431, }, }, "depth": 1, @@ -22431,12 +22287,12 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "debug": { - "id": 453, - "package_id": 381, + "id": 449, + "package_id": 377, }, "doctrine": { - "id": 454, - "package_id": 383, + "id": 450, + "package_id": 379, }, }, "depth": 1, @@ -22446,8 +22302,8 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "debug": { - "id": 678, - "package_id": 381, + "id": 674, + "package_id": 377, }, }, "depth": 1, @@ -22457,27 +22313,16 @@ exports[`next build works: bun 1`] = ` { "dependencies": { "debug": { - "id": 263, - "package_id": 189, - }, - }, - "depth": 1, - "id": 7, - "path": "node_modules/puppeteer-core/node_modules", - }, - { - "dependencies": { - "debug": { - "id": 156, - "package_id": 189, + "id": 157, + "package_id": 190, }, "semver": { - "id": 163, - "package_id": 115, + "id": 164, + "package_id": 116, }, }, "depth": 1, - "id": 8, + "id": 7, "path": "node_modules/@puppeteer/browsers/node_modules", }, { @@ -22488,7 +22333,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 9, + "id": 8, "path": "node_modules/chokidar/node_modules", }, { @@ -22499,7 +22344,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 10, + "id": 9, "path": "node_modules/fast-glob/node_modules", }, { @@ -22510,18 +22355,18 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 11, + "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, { "dependencies": { "debug": { - "id": 676, - "package_id": 381, + "id": 672, + "package_id": 377, }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/eslint-module-utils/node_modules", }, { @@ -22532,44 +22377,44 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/glob/node_modules", }, { "dependencies": { "minimatch": { - "id": 717, - "package_id": 399, + "id": 713, + "package_id": 395, }, "semver": { - "id": 715, - "package_id": 115, + "id": 711, + "package_id": 116, }, }, "depth": 1, - "id": 14, + "id": 13, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 15, + "id": 14, "path": "node_modules/@puppeteer/browsers/node_modules/semver/node_modules", }, { "dependencies": { "glob": { - "id": 389, - "package_id": 257, + "id": 385, + "package_id": 253, }, }, "depth": 1, - "id": 16, + "id": 15, "path": "node_modules/rimraf/node_modules", }, { @@ -22580,7 +22425,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 2, - "id": 17, + "id": 16, "path": "node_modules/glob/node_modules/minimatch/node_modules", }, { @@ -22591,40 +22436,40 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 18, + "id": 17, "path": "node_modules/path-scurry/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", }, { "dependencies": { "brace-expansion": { - "id": 724, + "id": 720, "package_id": 70, }, }, "depth": 2, - "id": 20, + "id": 19, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, { "dependencies": { "@types/node": { - "id": 253, - "package_id": 182, + "id": 254, + "package_id": 183, }, }, "depth": 1, - "id": 21, + "id": 20, "path": "node_modules/@types/yauzl/node_modules", }, { @@ -22635,7 +22480,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 22, + "id": 21, "path": "node_modules/string-width/node_modules", }, { @@ -22654,18 +22499,18 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 23, + "id": 22, "path": "node_modules/@isaacs/cliui/node_modules", }, { "dependencies": { "chalk": { - "id": 289, - "package_id": 208, + "id": 285, + "package_id": 204, }, }, "depth": 1, - "id": 24, + "id": 23, "path": "node_modules/@babel/highlight/node_modules", }, { @@ -22676,7 +22521,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 25, + "id": 24, "path": "node_modules/string-width-cjs/node_modules", }, { @@ -22687,7 +22532,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 2, - "id": 26, + "id": 25, "path": "node_modules/@isaacs/cliui/node_modules/strip-ansi/node_modules", }, { @@ -22698,59 +22543,59 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 2, - "id": 27, + "id": 26, "path": "node_modules/@isaacs/cliui/node_modules/wrap-ansi/node_modules", }, { "dependencies": { "ansi-styles": { - "id": 292, - "package_id": 212, + "id": 288, + "package_id": 208, }, "escape-string-regexp": { - "id": 293, - "package_id": 211, + "id": 289, + "package_id": 207, }, "supports-color": { - "id": 294, - "package_id": 209, + "id": 290, + "package_id": 205, }, }, "depth": 2, - "id": 28, + "id": 27, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules", }, { "dependencies": { "color-convert": { - "id": 296, - "package_id": 213, + "id": 292, + "package_id": 209, }, }, "depth": 3, - "id": 29, + "id": 28, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules", }, { "dependencies": { "has-flag": { - "id": 295, - "package_id": 210, + "id": 291, + "package_id": 206, }, }, "depth": 3, - "id": 30, + "id": 29, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/supports-color/node_modules", }, { "dependencies": { "color-name": { - "id": 297, - "package_id": 214, + "id": 293, + "package_id": 210, }, }, "depth": 4, - "id": 31, + "id": 30, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/node_modules", }, ], @@ -22773,7 +22618,7 @@ exports[`next build works: node 1`] = ` "name": "@types/node", "version": "==20.7.0", }, - "package_id": 456, + "package_id": 452, }, { "behavior": { @@ -22786,7 +22631,7 @@ exports[`next build works: node 1`] = ` "name": "@types/react", "version": "==18.2.22", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { @@ -22799,7 +22644,7 @@ exports[`next build works: node 1`] = ` "name": "@types/react-dom", "version": "==18.2.7", }, - "package_id": 451, + "package_id": 447, }, { "behavior": { @@ -22812,7 +22657,7 @@ exports[`next build works: node 1`] = ` "name": "autoprefixer", "version": "==10.4.16", }, - "package_id": 444, + "package_id": 440, }, { "behavior": { @@ -22825,7 +22670,7 @@ exports[`next build works: node 1`] = ` "name": "bun-types", "version": ">=1.0.3 <2.0.0", }, - "package_id": 442, + "package_id": 438, }, { "behavior": { @@ -22838,7 +22683,7 @@ exports[`next build works: node 1`] = ` "name": "eslint", "version": "==8.50.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { @@ -22851,7 +22696,7 @@ exports[`next build works: node 1`] = ` "name": "eslint-config-next", "version": "==14.1.3", }, - "package_id": 241, + "package_id": 237, }, { "behavior": { @@ -22864,7 +22709,7 @@ exports[`next build works: node 1`] = ` "name": "next", "version": "==14.1.3", }, - "package_id": 223, + "package_id": 219, }, { "behavior": { @@ -22884,11 +22729,11 @@ exports[`next build works: node 1`] = ` "normal": true, }, "id": 9, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer", "npm": { "name": "puppeteer", - "version": "==22.4.1", + "version": "==22.12.0", }, "package_id": 113, }, @@ -24767,221 +24612,234 @@ exports[`next build works: node 1`] = ` "name": "cosmiconfig", "version": "==9.0.0", }, - "package_id": 201, + "package_id": 197, }, { "behavior": { "normal": true, }, "id": 154, - "literal": "22.4.1", + "literal": "22.12.0", "name": "puppeteer-core", "npm": { "name": "puppeteer-core", - "version": "==22.4.1", + "version": "==22.12.0", }, - "package_id": 190, + "package_id": 191, }, { "behavior": { "normal": true, }, "id": 155, - "literal": "2.1.0", + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, "id": 156, + "literal": "0.0.1299070", + "name": "devtools-protocol", + "npm": { + "name": "devtools-protocol", + "version": "==0.0.1299070", + }, + "package_id": 114, + }, + { + "behavior": { + "normal": true, + }, + "id": 157, "literal": "4.3.4", "name": "debug", "npm": { "name": "debug", "version": "==4.3.4", }, - "package_id": 189, + "package_id": 190, }, { "behavior": { "normal": true, }, - "id": 157, + "id": 158, "literal": "2.0.1", "name": "extract-zip", "npm": { "name": "extract-zip", "version": "==2.0.1", }, - "package_id": 180, + "package_id": 181, }, { "behavior": { "normal": true, }, - "id": 158, + "id": 159, "literal": "2.0.3", "name": "progress", "npm": { "name": "progress", "version": "==2.0.3", }, - "package_id": 179, + "package_id": 180, }, { "behavior": { "normal": true, }, - "id": 159, + "id": 160, "literal": "6.4.0", "name": "proxy-agent", "npm": { "name": "proxy-agent", "version": "==6.4.0", }, - "package_id": 146, + "package_id": 147, }, { "behavior": { "normal": true, }, - "id": 160, + "id": 161, "literal": "3.0.5", "name": "tar-fs", "npm": { "name": "tar-fs", "version": "==3.0.5", }, - "package_id": 130, + "package_id": 131, }, { "behavior": { "normal": true, }, - "id": 161, + "id": 162, "literal": "1.4.3", "name": "unbzip2-stream", "npm": { "name": "unbzip2-stream", "version": "==1.4.3", }, - "package_id": 125, + "package_id": 126, }, { "behavior": { "normal": true, }, - "id": 162, + "id": 163, "literal": "17.7.2", "name": "yargs", "npm": { "name": "yargs", "version": "==17.7.2", }, - "package_id": 118, + "package_id": 119, }, { "behavior": { "normal": true, }, - "id": 163, + "id": 164, "literal": "7.6.0", "name": "semver", "npm": { "name": "semver", "version": "==7.6.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 164, + "id": 165, "literal": "^6.0.0", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=6.0.0 <7.0.0", }, - "package_id": 116, + "package_id": 117, }, { "behavior": { "normal": true, }, - "id": 165, + "id": 166, "literal": "^4.0.0", "name": "yallist", "npm": { "name": "yallist", "version": ">=4.0.0 <5.0.0", }, - "package_id": 117, + "package_id": 118, }, { "behavior": { "normal": true, }, - "id": 166, + "id": 167, "literal": "^8.0.1", "name": "cliui", "npm": { "name": "cliui", "version": ">=8.0.1 <9.0.0", }, - "package_id": 124, + "package_id": 125, }, { "behavior": { "normal": true, }, - "id": 167, + "id": 168, "literal": "^3.1.1", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.1 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 168, + "id": 169, "literal": "^2.0.5", "name": "get-caller-file", "npm": { "name": "get-caller-file", "version": ">=2.0.5 <3.0.0", }, - "package_id": 122, + "package_id": 123, }, { "behavior": { "normal": true, }, - "id": 169, + "id": 170, "literal": "^2.1.1", "name": "require-directory", "npm": { "name": "require-directory", "version": ">=2.1.1 <3.0.0", }, - "package_id": 121, + "package_id": 122, }, { "behavior": { "normal": true, }, - "id": 170, + "id": 171, "literal": "^4.2.3", "name": "string-width", "npm": { @@ -24994,33 +24852,33 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 171, + "id": 172, "literal": "^5.0.5", "name": "y18n", "npm": { "name": "y18n", "version": ">=5.0.5 <6.0.0", }, - "package_id": 120, + "package_id": 121, }, { "behavior": { "normal": true, }, - "id": 172, + "id": 173, "literal": "^21.1.1", "name": "yargs-parser", "npm": { "name": "yargs-parser", "version": ">=21.1.1 <22.0.0", }, - "package_id": 119, + "package_id": 120, }, { "behavior": { "normal": true, }, - "id": 173, + "id": 174, "literal": "^4.2.0", "name": "string-width", "npm": { @@ -25033,7 +24891,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 174, + "id": 175, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -25046,7 +24904,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 175, + "id": 176, "literal": "^7.0.0", "name": "wrap-ansi", "npm": { @@ -25059,1130 +24917,1117 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 176, + "id": 177, "literal": "^5.2.1", "name": "buffer", "npm": { "name": "buffer", "version": ">=5.2.1 <6.0.0", }, - "package_id": 127, + "package_id": 128, }, { "behavior": { "normal": true, }, - "id": 177, + "id": 178, "literal": "^2.3.8", "name": "through", "npm": { "name": "through", "version": ">=2.3.8 <3.0.0", }, - "package_id": 126, + "package_id": 127, }, { "behavior": { "normal": true, }, - "id": 178, + "id": 179, "literal": "^1.3.1", "name": "base64-js", "npm": { "name": "base64-js", "version": ">=1.3.1 <2.0.0", }, - "package_id": 129, + "package_id": 130, }, { "behavior": { "normal": true, }, - "id": 179, + "id": 180, "literal": "^1.1.13", "name": "ieee754", "npm": { "name": "ieee754", "version": ">=1.1.13 <2.0.0", }, - "package_id": 128, + "package_id": 129, }, { "behavior": { "normal": true, }, - "id": 180, + "id": 181, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 181, + "id": 182, "literal": "^3.1.5", "name": "tar-stream", "npm": { "name": "tar-stream", "version": ">=3.1.5 <4.0.0", }, - "package_id": 141, + "package_id": 142, }, { "behavior": { "optional": true, }, - "id": 182, + "id": 183, "literal": "^2.1.1", "name": "bare-fs", "npm": { "name": "bare-fs", "version": ">=2.1.1 <3.0.0", }, - "package_id": 133, + "package_id": 134, }, { "behavior": { "optional": true, }, - "id": 183, + "id": 184, "literal": "^2.1.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.1.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 184, + "id": 185, "literal": "^2.1.0", "name": "bare-os", "npm": { "name": "bare-os", "version": ">=2.1.0 <3.0.0", }, - "package_id": 132, + "package_id": 133, }, { "behavior": { "normal": true, }, - "id": 185, + "id": 186, "literal": "^2.0.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.0.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 186, + "id": 187, "literal": "^2.0.0", "name": "bare-path", "npm": { "name": "bare-path", "version": ">=2.0.0 <3.0.0", }, - "package_id": 131, + "package_id": 132, }, { "behavior": { "normal": true, }, - "id": 187, + "id": 188, "literal": "^2.0.0", "name": "bare-stream", "npm": { "name": "bare-stream", "version": ">=2.0.0 <3.0.0", }, - "package_id": 134, + "package_id": 135, }, { "behavior": { "normal": true, }, - "id": 188, + "id": 189, "literal": "^2.18.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.18.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 189, + "id": 190, "literal": "^1.3.2", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.3.2 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 190, + "id": 191, "literal": "^1.0.1", "name": "queue-tick", "npm": { "name": "queue-tick", "version": ">=1.0.1 <2.0.0", }, - "package_id": 139, + "package_id": 140, }, { "behavior": { "normal": true, }, - "id": 191, + "id": 192, "literal": "^1.1.0", "name": "text-decoder", "npm": { "name": "text-decoder", "version": ">=1.1.0 <2.0.0", }, - "package_id": 137, + "package_id": 138, }, { "behavior": { "optional": true, }, - "id": 192, + "id": 193, "literal": "^2.2.0", "name": "bare-events", "npm": { "name": "bare-events", "version": ">=2.2.0 <3.0.0", }, - "package_id": 136, + "package_id": 137, }, { "behavior": { "normal": true, }, - "id": 193, + "id": 194, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 194, + "id": 195, "literal": "^1.6.4", "name": "b4a", "npm": { "name": "b4a", "version": ">=1.6.4 <2.0.0", }, - "package_id": 138, + "package_id": 139, }, { "behavior": { "normal": true, }, - "id": 195, + "id": 196, "literal": "^1.2.0", "name": "fast-fifo", "npm": { "name": "fast-fifo", "version": ">=1.2.0 <2.0.0", }, - "package_id": 140, + "package_id": 141, }, { "behavior": { "normal": true, }, - "id": 196, + "id": 197, "literal": "^2.15.0", "name": "streamx", "npm": { "name": "streamx", "version": ">=2.15.0 <3.0.0", }, - "package_id": 135, + "package_id": 136, }, { "behavior": { "normal": true, }, - "id": 197, + "id": 198, "literal": "^1.1.0", "name": "end-of-stream", "npm": { "name": "end-of-stream", "version": ">=1.1.0 <2.0.0", }, - "package_id": 145, + "package_id": 146, }, { "behavior": { "normal": true, }, - "id": 198, + "id": 199, "literal": "^1.3.1", "name": "once", "npm": { "name": "once", "version": ">=1.3.1 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 199, + "id": 200, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 200, + "id": 201, "literal": "^1.4.0", "name": "once", "npm": { "name": "once", "version": ">=1.4.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 201, + "id": 202, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 202, + "id": 203, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 203, + "id": 204, "literal": "^7.0.1", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 204, + "id": 205, "literal": "^7.0.3", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.3 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 205, + "id": 206, "literal": "^7.14.1", "name": "lru-cache", "npm": { "name": "lru-cache", "version": ">=7.14.1 <8.0.0", }, - "package_id": 178, + "package_id": 179, }, { "behavior": { "normal": true, }, - "id": 206, + "id": 207, "literal": "^7.0.1", "name": "pac-proxy-agent", "npm": { "name": "pac-proxy-agent", "version": ">=7.0.1 <8.0.0", }, - "package_id": 157, + "package_id": 158, }, { "behavior": { "normal": true, }, - "id": 207, + "id": 208, "literal": "^1.1.0", "name": "proxy-from-env", "npm": { "name": "proxy-from-env", "version": ">=1.1.0 <2.0.0", }, - "package_id": 156, + "package_id": 157, }, { "behavior": { "normal": true, }, - "id": 208, + "id": 209, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 209, + "id": 210, "literal": "^7.1.1", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.1 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 210, + "id": 211, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 211, + "id": 212, "literal": "^2.7.1", "name": "socks", "npm": { "name": "socks", "version": ">=2.7.1 <3.0.0", }, - "package_id": 148, + "package_id": 149, }, { "behavior": { "normal": true, }, - "id": 212, + "id": 213, "literal": "^9.0.5", "name": "ip-address", "npm": { "name": "ip-address", "version": ">=9.0.5 <10.0.0", }, - "package_id": 150, + "package_id": 151, }, { "behavior": { "normal": true, }, - "id": 213, + "id": 214, "literal": "^4.2.0", "name": "smart-buffer", "npm": { "name": "smart-buffer", "version": ">=4.2.0 <5.0.0", }, - "package_id": 149, + "package_id": 150, }, { "behavior": { "normal": true, }, - "id": 214, + "id": 215, "literal": "1.1.0", "name": "jsbn", "npm": { "name": "jsbn", "version": "==1.1.0", }, - "package_id": 152, + "package_id": 153, }, { "behavior": { "normal": true, }, - "id": 215, + "id": 216, "literal": "^1.1.3", "name": "sprintf-js", "npm": { "name": "sprintf-js", "version": ">=1.1.3 <2.0.0", }, - "package_id": 151, + "package_id": 152, }, { "behavior": { "normal": true, }, - "id": 216, + "id": 217, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 217, + "id": 218, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 218, + "id": 219, "literal": "^0.23.0", "name": "@tootallnate/quickjs-emscripten", "npm": { "name": "@tootallnate/quickjs-emscripten", "version": ">=0.23.0 <0.24.0", }, - "package_id": 177, + "package_id": 178, }, { "behavior": { "normal": true, }, - "id": 219, + "id": 220, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 220, + "id": 221, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 221, + "id": 222, "literal": "^6.0.1", "name": "get-uri", "npm": { "name": "get-uri", "version": ">=6.0.1 <7.0.0", }, - "package_id": 170, + "package_id": 171, }, { "behavior": { "normal": true, }, - "id": 222, + "id": 223, "literal": "^7.0.0", "name": "http-proxy-agent", "npm": { "name": "http-proxy-agent", "version": ">=7.0.0 <8.0.0", }, - "package_id": 169, + "package_id": 170, }, { "behavior": { "normal": true, }, - "id": 223, + "id": 224, "literal": "^7.0.2", "name": "https-proxy-agent", "npm": { "name": "https-proxy-agent", "version": ">=7.0.2 <8.0.0", }, - "package_id": 168, + "package_id": 169, }, { "behavior": { "normal": true, }, - "id": 224, + "id": 225, "literal": "^7.0.0", "name": "pac-resolver", "npm": { "name": "pac-resolver", "version": ">=7.0.0 <8.0.0", }, - "package_id": 158, + "package_id": 159, }, { "behavior": { "normal": true, }, - "id": 225, + "id": 226, "literal": "^8.0.2", "name": "socks-proxy-agent", "npm": { "name": "socks-proxy-agent", "version": ">=8.0.2 <9.0.0", }, - "package_id": 147, + "package_id": 148, }, { "behavior": { "normal": true, }, - "id": 226, + "id": 227, "literal": "^5.0.0", "name": "degenerator", "npm": { "name": "degenerator", "version": ">=5.0.0 <6.0.0", }, - "package_id": 160, + "package_id": 161, }, { "behavior": { "normal": true, }, - "id": 227, + "id": 228, "literal": "^2.0.2", "name": "netmask", "npm": { "name": "netmask", "version": ">=2.0.2 <3.0.0", }, - "package_id": 159, + "package_id": 160, }, { "behavior": { "normal": true, }, - "id": 228, + "id": 229, "literal": "^0.13.4", "name": "ast-types", "npm": { "name": "ast-types", "version": ">=0.13.4 <0.14.0", }, - "package_id": 166, + "package_id": 167, }, { "behavior": { "normal": true, }, - "id": 229, + "id": 230, "literal": "^2.1.0", "name": "escodegen", "npm": { "name": "escodegen", "version": ">=2.1.0 <3.0.0", }, - "package_id": 162, + "package_id": 163, }, { "behavior": { "normal": true, }, - "id": 230, + "id": 231, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "normal": true, }, - "id": 231, + "id": 232, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 232, + "id": 233, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 233, + "id": 234, "literal": "^4.0.1", "name": "esprima", "npm": { "name": "esprima", "version": ">=4.0.1 <5.0.0", }, - "package_id": 161, + "package_id": 162, }, { "behavior": { "optional": true, }, - "id": 234, + "id": 235, "literal": "~0.6.1", "name": "source-map", "npm": { "name": "source-map", "version": ">=0.6.1 <0.7.0", }, - "package_id": 163, + "package_id": 164, }, { "behavior": { "normal": true, }, - "id": 235, + "id": 236, "literal": "^2.0.1", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.0.1 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 236, + "id": 237, "literal": "^7.0.2", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.0.2 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 237, + "id": 238, "literal": "4", "name": "debug", "npm": { "name": "debug", "version": "<5.0.0 >=4.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 238, + "id": 239, "literal": "^7.1.0", "name": "agent-base", "npm": { "name": "agent-base", "version": ">=7.1.0 <8.0.0", }, - "package_id": 155, + "package_id": 156, }, { "behavior": { "normal": true, }, - "id": 239, + "id": 240, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 240, + "id": 241, "literal": "^5.0.2", "name": "basic-ftp", "npm": { "name": "basic-ftp", "version": ">=5.0.2 <6.0.0", }, - "package_id": 176, + "package_id": 177, }, { "behavior": { "normal": true, }, - "id": 241, + "id": 242, "literal": "^6.0.2", "name": "data-uri-to-buffer", "npm": { "name": "data-uri-to-buffer", "version": ">=6.0.2 <7.0.0", }, - "package_id": 175, + "package_id": 176, }, { "behavior": { "normal": true, }, - "id": 242, + "id": 243, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 243, + "id": 244, "literal": "^11.2.0", "name": "fs-extra", "npm": { "name": "fs-extra", "version": ">=11.2.0 <12.0.0", }, - "package_id": 171, + "package_id": 172, }, { "behavior": { "normal": true, }, - "id": 244, + "id": 245, "literal": "^4.2.0", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.0 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 245, + "id": 246, "literal": "^6.0.1", "name": "jsonfile", "npm": { "name": "jsonfile", "version": ">=6.0.1 <7.0.0", }, - "package_id": 173, + "package_id": 174, }, { "behavior": { "normal": true, }, - "id": 246, + "id": 247, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "normal": true, }, - "id": 247, + "id": 248, "literal": "^2.0.0", "name": "universalify", "npm": { "name": "universalify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 172, + "package_id": 173, }, { "behavior": { "optional": true, }, - "id": 248, + "id": 249, "literal": "^4.1.6", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.1.6 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 249, + "id": 250, "literal": "^4.1.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.1.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 250, + "id": 251, "literal": "^5.1.0", "name": "get-stream", "npm": { "name": "get-stream", "version": ">=5.1.0 <6.0.0", }, - "package_id": 188, + "package_id": 189, }, { "behavior": { "normal": true, }, - "id": 251, + "id": 252, "literal": "^2.10.0", "name": "yauzl", "npm": { "name": "yauzl", "version": ">=2.10.0 <3.0.0", }, - "package_id": 184, + "package_id": 185, }, { "behavior": { "optional": true, }, - "id": 252, + "id": 253, "literal": "^2.9.1", "name": "@types/yauzl", "npm": { "name": "@types/yauzl", "version": ">=2.9.1 <3.0.0", }, - "package_id": 181, + "package_id": 182, }, { "behavior": { "normal": true, }, - "id": 253, + "id": 254, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 254, + "id": 255, "literal": "~5.26.4", "name": "undici-types", "npm": { "name": "undici-types", "version": ">=5.26.4 <5.27.0", }, - "package_id": 183, + "package_id": 184, }, { "behavior": { "normal": true, }, - "id": 255, + "id": 256, "literal": "~1.1.0", "name": "fd-slicer", "npm": { "name": "fd-slicer", "version": ">=1.1.0 <1.2.0", }, - "package_id": 186, + "package_id": 187, }, { "behavior": { "normal": true, }, - "id": 256, + "id": 257, "literal": "~0.2.3", "name": "buffer-crc32", "npm": { "name": "buffer-crc32", "version": ">=0.2.3 <0.3.0", }, - "package_id": 185, + "package_id": 186, }, { "behavior": { "normal": true, }, - "id": 257, + "id": 258, "literal": "~1.2.0", "name": "pend", "npm": { "name": "pend", "version": ">=1.2.0 <1.3.0", }, - "package_id": 187, + "package_id": 188, }, { "behavior": { "normal": true, }, - "id": 258, + "id": 259, "literal": "^3.0.0", "name": "pump", "npm": { "name": "pump", "version": ">=3.0.0 <4.0.0", }, - "package_id": 142, + "package_id": 143, }, { "behavior": { "normal": true, }, - "id": 259, + "id": 260, "literal": "2.1.2", "name": "ms", "npm": { "name": "ms", "version": "==2.1.2", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 260, - "literal": "2.1.0", + "id": 261, + "literal": "2.2.3", "name": "@puppeteer/browsers", "npm": { "name": "@puppeteer/browsers", - "version": "==2.1.0", + "version": "==2.2.3", }, - "package_id": 114, + "package_id": 115, }, { "behavior": { "normal": true, }, - "id": 261, - "literal": "0.5.12", + "id": 262, + "literal": "0.5.24", "name": "chromium-bidi", "npm": { "name": "chromium-bidi", - "version": "==0.5.12", - }, - "package_id": 198, - }, - { - "behavior": { - "normal": true, - }, - "id": 262, - "literal": "4.0.0", - "name": "cross-fetch", - "npm": { - "name": "cross-fetch", - "version": "==4.0.0", + "version": "==0.5.24", }, "package_id": 193, }, @@ -26191,39 +26036,39 @@ exports[`next build works: node 1`] = ` "normal": true, }, "id": 263, - "literal": "4.3.4", + "literal": "4.3.5", "name": "debug", "npm": { "name": "debug", - "version": "==4.3.4", + "version": "==4.3.5", }, - "package_id": 189, + "package_id": 154, }, { "behavior": { "normal": true, }, "id": 264, - "literal": "0.0.1249869", + "literal": "0.0.1299070", "name": "devtools-protocol", "npm": { "name": "devtools-protocol", - "version": "==0.0.1249869", + "version": "==0.0.1299070", }, - "package_id": 192, + "package_id": 114, }, { "behavior": { "normal": true, }, "id": 265, - "literal": "8.16.0", + "literal": "8.17.1", "name": "ws", "npm": { "name": "ws", - "version": "==8.16.0", + "version": "==8.17.1", }, - "package_id": 191, + "package_id": 192, }, { "behavior": { @@ -26258,164 +26103,111 @@ exports[`next build works: node 1`] = ` "normal": true, }, "id": 268, - "literal": "^2.6.12", - "name": "node-fetch", + "literal": "3.0.1", + "name": "mitt", "npm": { - "name": "node-fetch", - "version": ">=2.6.12 <3.0.0", + "name": "mitt", + "version": "==3.0.1", }, - "package_id": 194, + "package_id": 196, }, { "behavior": { "normal": true, }, "id": 269, - "literal": "^5.0.0", - "name": "whatwg-url", + "literal": "10.0.0", + "name": "urlpattern-polyfill", "npm": { - "name": "whatwg-url", - "version": ">=5.0.0 <6.0.0", + "name": "urlpattern-polyfill", + "version": "==10.0.0", }, "package_id": 195, }, - { - "behavior": { - "optional": true, - "peer": true, - }, - "id": 270, - "literal": "^0.1.0", - "name": "encoding", - "npm": { - "name": "encoding", - "version": ">=0.1.0 <0.2.0", - }, - "package_id": null, - }, - { - "behavior": { - "normal": true, - }, - "id": 271, - "literal": "~0.0.3", - "name": "tr46", - "npm": { - "name": "tr46", - "version": ">=0.0.3 <0.1.0", - }, - "package_id": 197, - }, - { - "behavior": { - "normal": true, - }, - "id": 272, - "literal": "^3.0.0", - "name": "webidl-conversions", - "npm": { - "name": "webidl-conversions", - "version": ">=3.0.0 <4.0.0", - }, - "package_id": 196, - }, { "behavior": { "normal": true, }, - "id": 273, - "literal": "3.0.1", - "name": "mitt", - "npm": { - "name": "mitt", - "version": "==3.0.1", - }, - "package_id": 200, - }, - { - "behavior": { - "normal": true, - }, - "id": 274, - "literal": "10.0.0", - "name": "urlpattern-polyfill", + "id": 270, + "literal": "3.23.8", + "name": "zod", "npm": { - "name": "urlpattern-polyfill", - "version": "==10.0.0", + "name": "zod", + "version": "==3.23.8", }, - "package_id": 199, + "package_id": 194, }, { "behavior": { "peer": true, }, - "id": 275, + "id": 271, "literal": "*", "name": "devtools-protocol", "npm": { "name": "devtools-protocol", "version": ">=0.0.0", }, - "package_id": 192, + "package_id": 114, }, { "behavior": { "normal": true, }, - "id": 276, + "id": 272, "literal": "^2.2.1", "name": "env-paths", "npm": { "name": "env-paths", "version": ">=2.2.1 <3.0.0", }, - "package_id": 222, + "package_id": 218, }, { "behavior": { "normal": true, }, - "id": 277, + "id": 273, "literal": "^3.3.0", "name": "import-fresh", "npm": { "name": "import-fresh", "version": ">=3.3.0 <4.0.0", }, - "package_id": 218, + "package_id": 214, }, { "behavior": { "normal": true, }, - "id": 278, + "id": 274, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 279, + "id": 275, "literal": "^5.2.0", "name": "parse-json", "npm": { "name": "parse-json", "version": ">=5.2.0 <6.0.0", }, - "package_id": 202, + "package_id": 198, }, { "behavior": { "optional": true, "peer": true, }, - "id": 280, + "id": 276, "literal": ">=4.9.5", "name": "typescript", "npm": { @@ -26428,46 +26220,46 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 281, + "id": 277, "literal": "^7.0.0", "name": "@babel/code-frame", "npm": { "name": "@babel/code-frame", "version": ">=7.0.0 <8.0.0", }, - "package_id": 206, + "package_id": 202, }, { "behavior": { "normal": true, }, - "id": 282, + "id": 278, "literal": "^1.3.1", "name": "error-ex", "npm": { "name": "error-ex", "version": ">=1.3.1 <2.0.0", }, - "package_id": 204, + "package_id": 200, }, { "behavior": { "normal": true, }, - "id": 283, + "id": 279, "literal": "^2.3.0", "name": "json-parse-even-better-errors", "npm": { "name": "json-parse-even-better-errors", "version": ">=2.3.0 <3.0.0", }, - "package_id": 203, + "package_id": 199, }, { "behavior": { "normal": true, }, - "id": 284, + "id": 280, "literal": "^1.1.6", "name": "lines-and-columns", "npm": { @@ -26480,33 +26272,33 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 285, + "id": 281, "literal": "^0.2.1", "name": "is-arrayish", "npm": { "name": "is-arrayish", "version": ">=0.2.1 <0.3.0", }, - "package_id": 205, + "package_id": 201, }, { "behavior": { "normal": true, }, - "id": 286, + "id": 282, "literal": "^7.24.7", "name": "@babel/highlight", "npm": { "name": "@babel/highlight", "version": ">=7.24.7 <8.0.0", }, - "package_id": 207, + "package_id": 203, }, { "behavior": { "normal": true, }, - "id": 287, + "id": 283, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -26519,33 +26311,33 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 288, + "id": 284, "literal": "^7.24.7", "name": "@babel/helper-validator-identifier", "npm": { "name": "@babel/helper-validator-identifier", "version": ">=7.24.7 <8.0.0", }, - "package_id": 215, + "package_id": 211, }, { "behavior": { "normal": true, }, - "id": 289, + "id": 285, "literal": "^2.4.2", "name": "chalk", "npm": { "name": "chalk", "version": ">=2.4.2 <3.0.0", }, - "package_id": 208, + "package_id": 204, }, { "behavior": { "normal": true, }, - "id": 290, + "id": 286, "literal": "^4.0.0", "name": "js-tokens", "npm": { @@ -26558,7 +26350,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 291, + "id": 287, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -26571,346 +26363,346 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 292, + "id": 288, "literal": "^3.2.1", "name": "ansi-styles", "npm": { "name": "ansi-styles", "version": ">=3.2.1 <4.0.0", }, - "package_id": 212, + "package_id": 208, }, { "behavior": { "normal": true, }, - "id": 293, + "id": 289, "literal": "^1.0.5", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=1.0.5 <2.0.0", }, - "package_id": 211, + "package_id": 207, }, { "behavior": { "normal": true, }, - "id": 294, + "id": 290, "literal": "^5.3.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=5.3.0 <6.0.0", }, - "package_id": 209, + "package_id": 205, }, { "behavior": { "normal": true, }, - "id": 295, + "id": 291, "literal": "^3.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=3.0.0 <4.0.0", }, - "package_id": 210, + "package_id": 206, }, { "behavior": { "normal": true, }, - "id": 296, + "id": 292, "literal": "^1.9.0", "name": "color-convert", "npm": { "name": "color-convert", "version": ">=1.9.0 <2.0.0", }, - "package_id": 213, + "package_id": 209, }, { "behavior": { "normal": true, }, - "id": 297, + "id": 293, "literal": "1.1.3", "name": "color-name", "npm": { "name": "color-name", "version": "==1.1.3", }, - "package_id": 214, + "package_id": 210, }, { "behavior": { "normal": true, }, - "id": 298, + "id": 294, "literal": "^2.0.1", "name": "argparse", "npm": { "name": "argparse", "version": ">=2.0.1 <3.0.0", }, - "package_id": 217, + "package_id": 213, }, { "behavior": { "normal": true, }, - "id": 299, + "id": 295, "literal": "^1.0.0", "name": "parent-module", "npm": { "name": "parent-module", "version": ">=1.0.0 <2.0.0", }, - "package_id": 220, + "package_id": 216, }, { "behavior": { "normal": true, }, - "id": 300, + "id": 296, "literal": "^4.0.0", "name": "resolve-from", "npm": { "name": "resolve-from", "version": ">=4.0.0 <5.0.0", }, - "package_id": 219, + "package_id": 215, }, { "behavior": { "normal": true, }, - "id": 301, + "id": 297, "literal": "^3.0.0", "name": "callsites", "npm": { "name": "callsites", "version": ">=3.0.0 <4.0.0", }, - "package_id": 221, + "package_id": 217, }, { "behavior": { "normal": true, }, - "id": 302, + "id": 298, "literal": "1.6.0", "name": "busboy", "npm": { "name": "busboy", "version": "==1.6.0", }, - "package_id": 239, + "package_id": 235, }, { "behavior": { "normal": true, }, - "id": 303, + "id": 299, "literal": "8.4.31", "name": "postcss", "npm": { "name": "postcss", "version": "==8.4.31", }, - "package_id": 238, + "package_id": 234, }, { "behavior": { "normal": true, }, - "id": 304, + "id": 300, "literal": "14.1.3", "name": "@next/env", "npm": { "name": "@next/env", "version": "==14.1.3", }, - "package_id": 237, + "package_id": 233, }, { "behavior": { "normal": true, }, - "id": 305, + "id": 301, "literal": "5.1.1", "name": "styled-jsx", "npm": { "name": "styled-jsx", "version": "==5.1.1", }, - "package_id": 235, + "package_id": 231, }, { "behavior": { "normal": true, }, - "id": 306, + "id": 302, "literal": "^4.2.11", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.11 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 307, + "id": 303, "literal": "0.5.2", "name": "@swc/helpers", "npm": { "name": "@swc/helpers", "version": "==0.5.2", }, - "package_id": 234, + "package_id": 230, }, { "behavior": { "normal": true, }, - "id": 308, + "id": 304, "literal": "^1.0.30001579", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001579 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "optional": true, }, - "id": 309, + "id": 305, "literal": "14.1.3", "name": "@next/swc-darwin-x64", "npm": { "name": "@next/swc-darwin-x64", "version": "==14.1.3", }, - "package_id": 232, + "package_id": 228, }, { "behavior": { "optional": true, }, - "id": 310, + "id": 306, "literal": "14.1.3", "name": "@next/swc-darwin-arm64", "npm": { "name": "@next/swc-darwin-arm64", "version": "==14.1.3", }, - "package_id": 231, + "package_id": 227, }, { "behavior": { "optional": true, }, - "id": 311, + "id": 307, "literal": "14.1.3", "name": "@next/swc-linux-x64-gnu", "npm": { "name": "@next/swc-linux-x64-gnu", "version": "==14.1.3", }, - "package_id": 230, + "package_id": 226, }, { "behavior": { "optional": true, }, - "id": 312, + "id": 308, "literal": "14.1.3", "name": "@next/swc-linux-x64-musl", "npm": { "name": "@next/swc-linux-x64-musl", "version": "==14.1.3", }, - "package_id": 229, + "package_id": 225, }, { "behavior": { "optional": true, }, - "id": 313, + "id": 309, "literal": "14.1.3", "name": "@next/swc-win32-x64-msvc", "npm": { "name": "@next/swc-win32-x64-msvc", "version": "==14.1.3", }, - "package_id": 228, + "package_id": 224, }, { "behavior": { "optional": true, }, - "id": 314, + "id": 310, "literal": "14.1.3", "name": "@next/swc-linux-arm64-gnu", "npm": { "name": "@next/swc-linux-arm64-gnu", "version": "==14.1.3", }, - "package_id": 227, + "package_id": 223, }, { "behavior": { "optional": true, }, - "id": 315, + "id": 311, "literal": "14.1.3", "name": "@next/swc-win32-ia32-msvc", "npm": { "name": "@next/swc-win32-ia32-msvc", "version": "==14.1.3", }, - "package_id": 226, + "package_id": 222, }, { "behavior": { "optional": true, }, - "id": 316, + "id": 312, "literal": "14.1.3", "name": "@next/swc-linux-arm64-musl", "npm": { "name": "@next/swc-linux-arm64-musl", "version": "==14.1.3", }, - "package_id": 225, + "package_id": 221, }, { "behavior": { "optional": true, }, - "id": 317, + "id": 313, "literal": "14.1.3", "name": "@next/swc-win32-arm64-msvc", "npm": { "name": "@next/swc-win32-arm64-msvc", "version": "==14.1.3", }, - "package_id": 224, + "package_id": 220, }, { "behavior": { "optional": true, "peer": true, }, - "id": 318, + "id": 314, "literal": "^1.3.0", "name": "sass", "npm": { @@ -26924,7 +26716,7 @@ exports[`next build works: node 1`] = ` "optional": true, "peer": true, }, - "id": 319, + "id": 315, "literal": "^1.1.0", "name": "@opentelemetry/api", "npm": { @@ -26937,7 +26729,7 @@ exports[`next build works: node 1`] = ` "behavior": { "peer": true, }, - "id": 320, + "id": 316, "literal": "^18.2.0", "name": "react-dom", "npm": { @@ -26950,7 +26742,7 @@ exports[`next build works: node 1`] = ` "behavior": { "peer": true, }, - "id": 321, + "id": 317, "literal": "^18.2.0", "name": "react", "npm": { @@ -26963,33 +26755,33 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 322, + "id": 318, "literal": "^2.4.0", "name": "tslib", "npm": { "name": "tslib", "version": ">=2.4.0 <3.0.0", }, - "package_id": 167, + "package_id": 168, }, { "behavior": { "normal": true, }, - "id": 323, + "id": 319, "literal": "0.0.1", "name": "client-only", "npm": { "name": "client-only", "version": "==0.0.1", }, - "package_id": 236, + "package_id": 232, }, { "behavior": { "peer": true, }, - "id": 324, + "id": 320, "literal": ">= 16.8.0 || 17.x.x || ^18.0.0-0", "name": "react", "npm": { @@ -27002,7 +26794,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 325, + "id": 321, "literal": "^3.3.6", "name": "nanoid", "npm": { @@ -27015,7 +26807,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 326, + "id": 322, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -27028,7 +26820,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 327, + "id": 323, "literal": "^1.0.2", "name": "source-map-js", "npm": { @@ -27041,138 +26833,138 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 328, + "id": 324, "literal": "^1.1.0", "name": "streamsearch", "npm": { "name": "streamsearch", "version": ">=1.1.0 <2.0.0", }, - "package_id": 240, + "package_id": 236, }, { "behavior": { "normal": true, }, - "id": 329, + "id": 325, "literal": "^7.33.2", "name": "eslint-plugin-react", "npm": { "name": "eslint-plugin-react", "version": ">=7.33.2 <8.0.0", }, - "package_id": 433, + "package_id": 429, }, { "behavior": { "normal": true, }, - "id": 330, + "id": 326, "literal": "^2.28.1", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=2.28.1 <3.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 331, + "id": 327, "literal": "^6.7.1", "name": "eslint-plugin-jsx-a11y", "npm": { "name": "eslint-plugin-jsx-a11y", "version": ">=6.7.1 <7.0.0", }, - "package_id": 408, + "package_id": 404, }, { "behavior": { "normal": true, }, - "id": 332, + "id": 328, "literal": "^1.3.3", "name": "@rushstack/eslint-patch", "npm": { "name": "@rushstack/eslint-patch", "version": ">=1.3.3 <2.0.0", }, - "package_id": 407, + "package_id": 403, }, { "behavior": { "normal": true, }, - "id": 333, + "id": 329, "literal": "14.1.3", "name": "@next/eslint-plugin-next", "npm": { "name": "@next/eslint-plugin-next", "version": "==14.1.3", }, - "package_id": 406, + "package_id": 402, }, { "behavior": { "normal": true, }, - "id": 334, + "id": 330, "literal": "^5.4.2 || ^6.0.0", "name": "@typescript-eslint/parser", "npm": { "name": "@typescript-eslint/parser", "version": ">=5.4.2 <6.0.0 || >=6.0.0 <7.0.0 && >=6.0.0 <7.0.0", }, - "package_id": 394, + "package_id": 390, }, { "behavior": { "normal": true, }, - "id": 335, + "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", "name": "eslint-plugin-react-hooks", "npm": { "name": "eslint-plugin-react-hooks", "version": ">=4.5.0 <5.0.0 || ==5.0.0-canary-7118f5dd7-20230705 && ==5.0.0-canary-7118f5dd7-20230705", }, - "package_id": 393, + "package_id": 389, }, { "behavior": { "normal": true, }, - "id": 336, + "id": 332, "literal": "^0.3.6", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.6 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 337, + "id": 333, "literal": "^3.5.2", "name": "eslint-import-resolver-typescript", "npm": { "name": "eslint-import-resolver-typescript", "version": ">=3.5.2 <4.0.0", }, - "package_id": 306, + "package_id": 302, }, { "behavior": { "optional": true, "peer": true, }, - "id": 338, + "id": 334, "literal": ">=3.3.1", "name": "typescript", "npm": { @@ -27185,150 +26977,150 @@ exports[`next build works: node 1`] = ` "behavior": { "peer": true, }, - "id": 339, + "id": 335, "literal": "^7.23.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.23.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 340, + "id": 336, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 341, + "id": 337, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 342, + "id": 338, "literal": "^4.0.0", "name": "chalk", "npm": { "name": "chalk", "version": ">=4.0.0 <5.0.0", }, - "package_id": 303, + "package_id": 299, }, { "behavior": { "normal": true, }, - "id": 343, + "id": 339, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 344, + "id": 340, "literal": "^9.6.1", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.1 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 345, + "id": 341, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 346, + "id": 342, "literal": "^1.4.2", "name": "esquery", "npm": { "name": "esquery", "version": ">=1.4.2 <2.0.0", }, - "package_id": 302, + "package_id": 298, }, { "behavior": { "normal": true, }, - "id": 347, + "id": 343, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 348, + "id": 344, "literal": "^5.0.0", "name": "find-up", "npm": { "name": "find-up", "version": ">=5.0.0 <6.0.0", }, - "package_id": 296, + "package_id": 292, }, { "behavior": { "normal": true, }, - "id": 349, + "id": 345, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 350, + "id": 346, "literal": "^4.0.0", "name": "is-glob", "npm": { @@ -27341,85 +27133,85 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 351, + "id": 347, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 352, + "id": 348, "literal": "^3.0.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=3.0.0 <4.0.0", }, - "package_id": 295, + "package_id": 291, }, { "behavior": { "normal": true, }, - "id": 353, + "id": 349, "literal": "^1.4.0", "name": "graphemer", "npm": { "name": "graphemer", "version": ">=1.4.0 <2.0.0", }, - "package_id": 294, + "package_id": 290, }, { "behavior": { "normal": true, }, - "id": 354, + "id": 350, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 355, + "id": 351, "literal": "8.50.0", "name": "@eslint/js", "npm": { "name": "@eslint/js", "version": "==8.50.0", }, - "package_id": 293, + "package_id": 289, }, { "behavior": { "normal": true, }, - "id": 356, + "id": 352, "literal": "^0.9.3", "name": "optionator", "npm": { "name": "optionator", "version": ">=0.9.3 <0.10.0", }, - "package_id": 286, + "package_id": 282, }, { "behavior": { "normal": true, }, - "id": 357, + "id": 353, "literal": "^6.0.1", "name": "strip-ansi", "npm": { @@ -27432,20 +27224,20 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 358, + "id": 354, "literal": "^0.2.0", "name": "text-table", "npm": { "name": "text-table", "version": ">=0.2.0 <0.3.0", }, - "package_id": 285, + "package_id": 281, }, { "behavior": { "normal": true, }, - "id": 359, + "id": 355, "literal": "^7.0.2", "name": "cross-spawn", "npm": { @@ -27458,7 +27250,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 360, + "id": 356, "literal": "^6.0.2", "name": "glob-parent", "npm": { @@ -27471,98 +27263,98 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 361, + "id": 357, "literal": "^0.1.4", "name": "imurmurhash", "npm": { "name": "imurmurhash", "version": ">=0.1.4 <0.2.0", }, - "package_id": 284, + "package_id": 280, }, { "behavior": { "normal": true, }, - "id": 362, + "id": 358, "literal": "^7.2.2", "name": "eslint-scope", "npm": { "name": "eslint-scope", "version": ">=7.2.2 <8.0.0", }, - "package_id": 282, + "package_id": 278, }, { "behavior": { "normal": true, }, - "id": 363, + "id": 359, "literal": "^4.6.2", "name": "lodash.merge", "npm": { "name": "lodash.merge", "version": ">=4.6.2 <5.0.0", }, - "package_id": 281, + "package_id": 277, }, { "behavior": { "normal": true, }, - "id": 364, + "id": 360, "literal": "^3.0.3", "name": "is-path-inside", "npm": { "name": "is-path-inside", "version": ">=3.0.3 <4.0.0", }, - "package_id": 280, + "package_id": 276, }, { "behavior": { "normal": true, }, - "id": 365, + "id": 361, "literal": "^3.1.3", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.3 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 366, + "id": 362, "literal": "^1.4.0", "name": "natural-compare", "npm": { "name": "natural-compare", "version": ">=1.4.0 <2.0.0", }, - "package_id": 279, + "package_id": 275, }, { "behavior": { "normal": true, }, - "id": 367, + "id": 363, "literal": "^2.1.2", "name": "@eslint/eslintrc", "npm": { "name": "@eslint/eslintrc", "version": ">=2.1.2 <3.0.0", }, - "package_id": 265, + "package_id": 261, }, { "behavior": { "normal": true, }, - "id": 368, + "id": 364, "literal": "^1.2.8", "name": "@nodelib/fs.walk", "npm": { @@ -27575,189 +27367,189 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 369, + "id": 365, "literal": "^6.0.1", "name": "file-entry-cache", "npm": { "name": "file-entry-cache", "version": ">=6.0.1 <7.0.0", }, - "package_id": 254, + "package_id": 250, }, { "behavior": { "normal": true, }, - "id": 370, + "id": 366, "literal": "^3.4.3", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.3 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 371, + "id": 367, "literal": "^4.0.0", "name": "escape-string-regexp", "npm": { "name": "escape-string-regexp", "version": ">=4.0.0 <5.0.0", }, - "package_id": 253, + "package_id": 249, }, { "behavior": { "normal": true, }, - "id": 372, + "id": 368, "literal": "^4.6.1", "name": "@eslint-community/regexpp", "npm": { "name": "@eslint-community/regexpp", "version": ">=4.6.1 <5.0.0", }, - "package_id": 252, + "package_id": 248, }, { "behavior": { "normal": true, }, - "id": 373, + "id": 369, "literal": "^0.11.11", "name": "@humanwhocodes/config-array", "npm": { "name": "@humanwhocodes/config-array", "version": ">=0.11.11 <0.12.0", }, - "package_id": 247, + "package_id": 243, }, { "behavior": { "normal": true, }, - "id": 374, + "id": 370, "literal": "^4.2.0", "name": "@eslint-community/eslint-utils", "npm": { "name": "@eslint-community/eslint-utils", "version": ">=4.2.0 <5.0.0", }, - "package_id": 245, + "package_id": 241, }, { "behavior": { "normal": true, }, - "id": 375, + "id": 371, "literal": "^1.0.1", "name": "@humanwhocodes/module-importer", "npm": { "name": "@humanwhocodes/module-importer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 244, + "package_id": 240, }, { "behavior": { "normal": true, }, - "id": 376, + "id": 372, "literal": "^1.0.1", "name": "json-stable-stringify-without-jsonify", "npm": { "name": "json-stable-stringify-without-jsonify", "version": ">=1.0.1 <2.0.0", }, - "package_id": 243, + "package_id": 239, }, { "behavior": { "normal": true, }, - "id": 377, + "id": 373, "literal": "^3.3.0", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.3.0 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 378, + "id": 374, "literal": "^6.0.0 || ^7.0.0 || >=8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 && >=8.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 379, + "id": 375, "literal": "^2.0.2", "name": "@humanwhocodes/object-schema", "npm": { "name": "@humanwhocodes/object-schema", "version": ">=2.0.2 <3.0.0", }, - "package_id": 251, + "package_id": 247, }, { "behavior": { "normal": true, }, - "id": 380, + "id": 376, "literal": "^4.3.1", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.1 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 381, + "id": 377, "literal": "^3.0.5", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.0.5 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 382, + "id": 378, "literal": "^1.1.7", "name": "brace-expansion", "npm": { "name": "brace-expansion", "version": ">=1.1.7 <2.0.0", }, - "package_id": 249, + "package_id": 245, }, { "behavior": { "normal": true, }, - "id": 383, + "id": 379, "literal": "^1.0.0", "name": "balanced-match", "npm": { @@ -27770,696 +27562,696 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 384, + "id": 380, "literal": "0.0.1", "name": "concat-map", "npm": { "name": "concat-map", "version": "==0.0.1", }, - "package_id": 250, + "package_id": 246, }, { "behavior": { "normal": true, }, - "id": 385, + "id": 381, "literal": "^3.0.4", "name": "flat-cache", "npm": { "name": "flat-cache", "version": ">=3.0.4 <4.0.0", }, - "package_id": 255, + "package_id": 251, }, { "behavior": { "normal": true, }, - "id": 386, + "id": 382, "literal": "^3.2.9", "name": "flatted", "npm": { "name": "flatted", "version": ">=3.2.9 <4.0.0", }, - "package_id": 264, + "package_id": 260, }, { "behavior": { "normal": true, }, - "id": 387, + "id": 383, "literal": "^4.5.3", "name": "keyv", "npm": { "name": "keyv", "version": ">=4.5.3 <5.0.0", }, - "package_id": 262, + "package_id": 258, }, { "behavior": { "normal": true, }, - "id": 388, + "id": 384, "literal": "^3.0.2", "name": "rimraf", "npm": { "name": "rimraf", "version": ">=3.0.2 <4.0.0", }, - "package_id": 256, + "package_id": 252, }, { "behavior": { "normal": true, }, - "id": 389, + "id": 385, "literal": "^7.1.3", "name": "glob", "npm": { "name": "glob", "version": ">=7.1.3 <8.0.0", }, - "package_id": 257, + "package_id": 253, }, { "behavior": { "normal": true, }, - "id": 390, + "id": 386, "literal": "^1.0.0", "name": "fs.realpath", "npm": { "name": "fs.realpath", "version": ">=1.0.0 <2.0.0", }, - "package_id": 261, + "package_id": 257, }, { "behavior": { "normal": true, }, - "id": 391, + "id": 387, "literal": "^1.0.4", "name": "inflight", "npm": { "name": "inflight", "version": ">=1.0.4 <2.0.0", }, - "package_id": 260, + "package_id": 256, }, { "behavior": { "normal": true, }, - "id": 392, + "id": 388, "literal": "2", "name": "inherits", "npm": { "name": "inherits", "version": "<3.0.0 >=2.0.0", }, - "package_id": 259, + "package_id": 255, }, { "behavior": { "normal": true, }, - "id": 393, + "id": 389, "literal": "^3.1.1", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.1 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 394, + "id": 390, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 395, + "id": 391, "literal": "^1.0.0", "name": "path-is-absolute", "npm": { "name": "path-is-absolute", "version": ">=1.0.0 <2.0.0", }, - "package_id": 258, + "package_id": 254, }, { "behavior": { "normal": true, }, - "id": 396, + "id": 392, "literal": "^1.3.0", "name": "once", "npm": { "name": "once", "version": ">=1.3.0 <2.0.0", }, - "package_id": 143, + "package_id": 144, }, { "behavior": { "normal": true, }, - "id": 397, + "id": 393, "literal": "1", "name": "wrappy", "npm": { "name": "wrappy", "version": "<2.0.0 >=1.0.0", }, - "package_id": 144, + "package_id": 145, }, { "behavior": { "normal": true, }, - "id": 398, + "id": 394, "literal": "3.0.1", "name": "json-buffer", "npm": { "name": "json-buffer", "version": "==3.0.1", }, - "package_id": 263, + "package_id": 259, }, { "behavior": { "normal": true, }, - "id": 399, + "id": 395, "literal": "^6.12.4", "name": "ajv", "npm": { "name": "ajv", "version": ">=6.12.4 <7.0.0", }, - "package_id": 273, + "package_id": 269, }, { "behavior": { "normal": true, }, - "id": 400, + "id": 396, "literal": "^4.3.2", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.2 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 401, + "id": 397, "literal": "^9.6.0", "name": "espree", "npm": { "name": "espree", "version": ">=9.6.0 <10.0.0", }, - "package_id": 270, + "package_id": 266, }, { "behavior": { "normal": true, }, - "id": 402, + "id": 398, "literal": "^13.19.0", "name": "globals", "npm": { "name": "globals", "version": ">=13.19.0 <14.0.0", }, - "package_id": 268, + "package_id": 264, }, { "behavior": { "normal": true, }, - "id": 403, + "id": 399, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 404, + "id": 400, "literal": "^3.2.1", "name": "import-fresh", "npm": { "name": "import-fresh", "version": ">=3.2.1 <4.0.0", }, - "package_id": 218, + "package_id": 214, }, { "behavior": { "normal": true, }, - "id": 405, + "id": 401, "literal": "^4.1.0", "name": "js-yaml", "npm": { "name": "js-yaml", "version": ">=4.1.0 <5.0.0", }, - "package_id": 216, + "package_id": 212, }, { "behavior": { "normal": true, }, - "id": 406, + "id": 402, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 407, + "id": 403, "literal": "^3.1.1", "name": "strip-json-comments", "npm": { "name": "strip-json-comments", "version": ">=3.1.1 <4.0.0", }, - "package_id": 266, + "package_id": 262, }, { "behavior": { "normal": true, }, - "id": 408, + "id": 404, "literal": "^0.20.2", "name": "type-fest", "npm": { "name": "type-fest", "version": ">=0.20.2 <0.21.0", }, - "package_id": 269, + "package_id": 265, }, { "behavior": { "normal": true, }, - "id": 409, + "id": 405, "literal": "^8.9.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=8.9.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 410, + "id": 406, "literal": "^5.3.2", "name": "acorn-jsx", "npm": { "name": "acorn-jsx", "version": ">=5.3.2 <6.0.0", }, - "package_id": 271, + "package_id": 267, }, { "behavior": { "normal": true, }, - "id": 411, + "id": 407, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "peer": true, }, - "id": 412, + "id": 408, "literal": "^6.0.0 || ^7.0.0 || ^8.0.0", "name": "acorn", "npm": { "name": "acorn", "version": ">=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 272, + "package_id": 268, }, { "behavior": { "normal": true, }, - "id": 413, + "id": 409, "literal": "^3.1.1", "name": "fast-deep-equal", "npm": { "name": "fast-deep-equal", "version": ">=3.1.1 <4.0.0", }, - "package_id": 278, + "package_id": 274, }, { "behavior": { "normal": true, }, - "id": 414, + "id": 410, "literal": "^2.0.0", "name": "fast-json-stable-stringify", "npm": { "name": "fast-json-stable-stringify", "version": ">=2.0.0 <3.0.0", }, - "package_id": 277, + "package_id": 273, }, { "behavior": { "normal": true, }, - "id": 415, + "id": 411, "literal": "^0.4.1", "name": "json-schema-traverse", "npm": { "name": "json-schema-traverse", "version": ">=0.4.1 <0.5.0", }, - "package_id": 276, + "package_id": 272, }, { "behavior": { "normal": true, }, - "id": 416, + "id": 412, "literal": "^4.2.2", "name": "uri-js", "npm": { "name": "uri-js", "version": ">=4.2.2 <5.0.0", }, - "package_id": 274, + "package_id": 270, }, { "behavior": { "normal": true, }, - "id": 417, + "id": 413, "literal": "^2.1.0", "name": "punycode", "npm": { "name": "punycode", "version": ">=2.1.0 <3.0.0", }, - "package_id": 275, + "package_id": 271, }, { "behavior": { "normal": true, }, - "id": 418, + "id": 414, "literal": "^4.3.0", "name": "esrecurse", "npm": { "name": "esrecurse", "version": ">=4.3.0 <5.0.0", }, - "package_id": 283, + "package_id": 279, }, { "behavior": { "normal": true, }, - "id": 419, + "id": 415, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 420, + "id": 416, "literal": "^5.2.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.2.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 421, + "id": 417, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 422, + "id": 418, "literal": "^0.1.3", "name": "deep-is", "npm": { "name": "deep-is", "version": ">=0.1.3 <0.2.0", }, - "package_id": 292, + "package_id": 288, }, { "behavior": { "normal": true, }, - "id": 423, + "id": 419, "literal": "^1.2.5", "name": "word-wrap", "npm": { "name": "word-wrap", "version": ">=1.2.5 <2.0.0", }, - "package_id": 291, + "package_id": 287, }, { "behavior": { "normal": true, }, - "id": 424, + "id": 420, "literal": "^0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 425, + "id": 421, "literal": "^0.4.1", "name": "levn", "npm": { "name": "levn", "version": ">=0.4.1 <0.5.0", }, - "package_id": 288, + "package_id": 284, }, { "behavior": { "normal": true, }, - "id": 426, + "id": 422, "literal": "^2.0.6", "name": "fast-levenshtein", "npm": { "name": "fast-levenshtein", "version": ">=2.0.6 <3.0.0", }, - "package_id": 287, + "package_id": 283, }, { "behavior": { "normal": true, }, - "id": 427, + "id": 423, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 428, + "id": 424, "literal": "~0.4.0", "name": "type-check", "npm": { "name": "type-check", "version": ">=0.4.0 <0.5.0", }, - "package_id": 289, + "package_id": 285, }, { "behavior": { "normal": true, }, - "id": 429, + "id": 425, "literal": "^1.2.1", "name": "prelude-ls", "npm": { "name": "prelude-ls", "version": ">=1.2.1 <2.0.0", }, - "package_id": 290, + "package_id": 286, }, { "behavior": { "normal": true, }, - "id": 430, + "id": 426, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 431, + "id": 427, "literal": "^6.0.0", "name": "locate-path", "npm": { "name": "locate-path", "version": ">=6.0.0 <7.0.0", }, - "package_id": 298, + "package_id": 294, }, { "behavior": { "normal": true, }, - "id": 432, + "id": 428, "literal": "^4.0.0", "name": "path-exists", "npm": { "name": "path-exists", "version": ">=4.0.0 <5.0.0", }, - "package_id": 297, + "package_id": 293, }, { "behavior": { "normal": true, }, - "id": 433, + "id": 429, "literal": "^5.0.0", "name": "p-locate", "npm": { "name": "p-locate", "version": ">=5.0.0 <6.0.0", }, - "package_id": 299, + "package_id": 295, }, { "behavior": { "normal": true, }, - "id": 434, + "id": 430, "literal": "^3.0.2", "name": "p-limit", "npm": { "name": "p-limit", "version": ">=3.0.2 <4.0.0", }, - "package_id": 300, + "package_id": 296, }, { "behavior": { "normal": true, }, - "id": 435, + "id": 431, "literal": "^0.1.0", "name": "yocto-queue", "npm": { "name": "yocto-queue", "version": ">=0.1.0 <0.2.0", }, - "package_id": 301, + "package_id": 297, }, { "behavior": { "normal": true, }, - "id": 436, + "id": 432, "literal": "^5.1.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.1.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 437, + "id": 433, "literal": "^4.1.0", "name": "ansi-styles", "npm": { @@ -28472,72 +28264,72 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 438, + "id": 434, "literal": "^7.1.0", "name": "supports-color", "npm": { "name": "supports-color", "version": ">=7.1.0 <8.0.0", }, - "package_id": 304, + "package_id": 300, }, { "behavior": { "normal": true, }, - "id": 439, + "id": 435, "literal": "^4.0.0", "name": "has-flag", "npm": { "name": "has-flag", "version": ">=4.0.0 <5.0.0", }, - "package_id": 305, + "package_id": 301, }, { "behavior": { "normal": true, }, - "id": 440, + "id": 436, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 441, + "id": 437, "literal": "^5.12.0", "name": "enhanced-resolve", "npm": { "name": "enhanced-resolve", "version": ">=5.12.0 <6.0.0", }, - "package_id": 391, + "package_id": 387, }, { "behavior": { "normal": true, }, - "id": 442, + "id": 438, "literal": "^2.7.4", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.7.4 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 443, + "id": 439, "literal": "^3.3.1", "name": "fast-glob", "npm": { @@ -28550,20 +28342,20 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 444, + "id": 440, "literal": "^4.5.0", "name": "get-tsconfig", "npm": { "name": "get-tsconfig", "version": ">=4.5.0 <5.0.0", }, - "package_id": 389, + "package_id": 385, }, { "behavior": { "normal": true, }, - "id": 445, + "id": 441, "literal": "^2.11.0", "name": "is-core-module", "npm": { @@ -28576,7 +28368,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 446, + "id": 442, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -28589,137 +28381,137 @@ exports[`next build works: node 1`] = ` "behavior": { "peer": true, }, - "id": 447, + "id": 443, "literal": "*", "name": "eslint", "npm": { "name": "eslint", "version": ">=0.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "peer": true, }, - "id": 448, + "id": 444, "literal": "*", "name": "eslint-plugin-import", "npm": { "name": "eslint-plugin-import", "version": ">=0.0.0", }, - "package_id": 307, + "package_id": 303, }, { "behavior": { "normal": true, }, - "id": 449, + "id": 445, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 450, + "id": 446, "literal": "^1.2.3", "name": "array.prototype.findlastindex", "npm": { "name": "array.prototype.findlastindex", "version": ">=1.2.3 <2.0.0", }, - "package_id": 387, + "package_id": 383, }, { "behavior": { "normal": true, }, - "id": 451, + "id": 447, "literal": "^1.3.2", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.2 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 452, + "id": 448, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 453, + "id": 449, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 454, + "id": 450, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 455, + "id": 451, "literal": "^0.3.9", "name": "eslint-import-resolver-node", "npm": { "name": "eslint-import-resolver-node", "version": ">=0.3.9 <0.4.0", }, - "package_id": 382, + "package_id": 378, }, { "behavior": { "normal": true, }, - "id": 456, + "id": 452, "literal": "^2.8.0", "name": "eslint-module-utils", "npm": { "name": "eslint-module-utils", "version": ">=2.8.0 <3.0.0", }, - "package_id": 380, + "package_id": 376, }, { "behavior": { "normal": true, }, - "id": 457, + "id": 453, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -28732,7 +28524,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 458, + "id": 454, "literal": "^2.13.1", "name": "is-core-module", "npm": { @@ -28745,7 +28537,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 459, + "id": 455, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -28758,293 +28550,293 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 460, + "id": 456, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 461, + "id": 457, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 462, + "id": 458, "literal": "^1.0.1", "name": "object.groupby", "npm": { "name": "object.groupby", "version": ">=1.0.1 <2.0.0", }, - "package_id": 328, + "package_id": 324, }, { "behavior": { "normal": true, }, - "id": 463, + "id": 459, "literal": "^1.1.7", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.7 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 464, + "id": 460, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 465, + "id": 461, "literal": "^3.15.0", "name": "tsconfig-paths", "npm": { "name": "tsconfig-paths", "version": ">=3.15.0 <4.0.0", }, - "package_id": 308, + "package_id": 304, }, { "behavior": { "peer": true, }, - "id": 466, + "id": 462, "literal": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=2.0.0 <3.0.0 || >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.2.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 467, + "id": 463, "literal": "^0.0.29", "name": "@types/json5", "npm": { "name": "@types/json5", "version": ">=0.0.29 <0.0.30", }, - "package_id": 312, + "package_id": 308, }, { "behavior": { "normal": true, }, - "id": 468, + "id": 464, "literal": "^1.0.2", "name": "json5", "npm": { "name": "json5", "version": ">=1.0.2 <2.0.0", }, - "package_id": 311, + "package_id": 307, }, { "behavior": { "normal": true, }, - "id": 469, + "id": 465, "literal": "^1.2.6", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.6 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 470, + "id": 466, "literal": "^3.0.0", "name": "strip-bom", "npm": { "name": "strip-bom", "version": ">=3.0.0 <4.0.0", }, - "package_id": 309, + "package_id": 305, }, { "behavior": { "normal": true, }, - "id": 471, + "id": 467, "literal": "^1.2.0", "name": "minimist", "npm": { "name": "minimist", "version": ">=1.2.0 <2.0.0", }, - "package_id": 310, + "package_id": 306, }, { "behavior": { "normal": true, }, - "id": 472, + "id": 468, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 473, + "id": 469, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 474, + "id": 470, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 475, + "id": 471, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 476, + "id": 472, "literal": "^1.0.1", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.0.1 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 477, + "id": 473, "literal": "^1.0.0", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.0 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 478, + "id": 474, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 479, + "id": 475, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 480, + "id": 476, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 481, + "id": 477, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 482, + "id": 478, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -29057,33 +28849,33 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 483, + "id": 479, "literal": "^1.0.1", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.1 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 484, + "id": 480, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 485, + "id": 481, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -29096,85 +28888,85 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 486, + "id": 482, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 487, + "id": 483, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 488, + "id": 484, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 489, + "id": 485, "literal": "^1.1.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.1.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 490, + "id": 486, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 491, + "id": 487, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 492, + "id": 488, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -29187,59 +28979,59 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 493, + "id": 489, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 494, + "id": 490, "literal": "^1.2.1", "name": "set-function-length", "npm": { "name": "set-function-length", "version": ">=1.2.1 <2.0.0", }, - "package_id": 327, + "package_id": 323, }, { "behavior": { "normal": true, }, - "id": 495, + "id": 491, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 496, + "id": 492, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 497, + "id": 493, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -29252,345 +29044,345 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 498, + "id": 494, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 499, + "id": 495, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 500, + "id": 496, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 501, + "id": 497, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 502, + "id": 498, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 503, + "id": 499, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 504, + "id": 500, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 505, + "id": 501, "literal": "^1.0.3", "name": "arraybuffer.prototype.slice", "npm": { "name": "arraybuffer.prototype.slice", "version": ">=1.0.3 <2.0.0", }, - "package_id": 377, + "package_id": 373, }, { "behavior": { "normal": true, }, - "id": 506, + "id": 502, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 507, + "id": 503, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 508, + "id": 504, "literal": "^1.0.1", "name": "data-view-buffer", "npm": { "name": "data-view-buffer", "version": ">=1.0.1 <2.0.0", }, - "package_id": 376, + "package_id": 372, }, { "behavior": { "normal": true, }, - "id": 509, + "id": 505, "literal": "^1.0.1", "name": "data-view-byte-length", "npm": { "name": "data-view-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 375, + "package_id": 371, }, { "behavior": { "normal": true, }, - "id": 510, + "id": 506, "literal": "^1.0.0", "name": "data-view-byte-offset", "npm": { "name": "data-view-byte-offset", "version": ">=1.0.0 <2.0.0", }, - "package_id": 374, + "package_id": 370, }, { "behavior": { "normal": true, }, - "id": 511, + "id": 507, "literal": "^1.0.0", "name": "es-define-property", "npm": { "name": "es-define-property", "version": ">=1.0.0 <2.0.0", }, - "package_id": 320, + "package_id": 316, }, { "behavior": { "normal": true, }, - "id": 512, + "id": 508, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 513, + "id": 509, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 514, + "id": 510, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 515, + "id": 511, "literal": "^1.2.1", "name": "es-to-primitive", "npm": { "name": "es-to-primitive", "version": ">=1.2.1 <2.0.0", }, - "package_id": 371, + "package_id": 367, }, { "behavior": { "normal": true, }, - "id": 516, + "id": 512, "literal": "^1.1.6", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.6 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 517, + "id": 513, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 518, + "id": 514, "literal": "^1.0.2", "name": "get-symbol-description", "npm": { "name": "get-symbol-description", "version": ">=1.0.2 <2.0.0", }, - "package_id": 369, + "package_id": 365, }, { "behavior": { "normal": true, }, - "id": 519, + "id": 515, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 520, + "id": 516, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 521, + "id": 517, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 522, + "id": 518, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 523, + "id": 519, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 524, + "id": 520, "literal": "^2.0.2", "name": "hasown", "npm": { @@ -29603,1385 +29395,1385 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 525, + "id": 521, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 526, + "id": 522, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 527, + "id": 523, "literal": "^1.2.7", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.2.7 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 528, + "id": 524, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 529, + "id": 525, "literal": "^2.0.3", "name": "is-negative-zero", "npm": { "name": "is-negative-zero", "version": ">=2.0.3 <3.0.0", }, - "package_id": 363, + "package_id": 359, }, { "behavior": { "normal": true, }, - "id": 530, + "id": 526, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 531, + "id": 527, "literal": "^1.0.3", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.3 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 532, + "id": 528, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 533, + "id": 529, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 534, + "id": 530, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 535, + "id": 531, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 536, + "id": 532, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 537, + "id": 533, "literal": "^4.1.5", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.5 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 538, + "id": 534, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 539, + "id": 535, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 540, + "id": 536, "literal": "^1.0.3", "name": "safe-regex-test", "npm": { "name": "safe-regex-test", "version": ">=1.0.3 <2.0.0", }, - "package_id": 352, + "package_id": 348, }, { "behavior": { "normal": true, }, - "id": 541, + "id": 537, "literal": "^1.2.9", "name": "string.prototype.trim", "npm": { "name": "string.prototype.trim", "version": ">=1.2.9 <2.0.0", }, - "package_id": 351, + "package_id": 347, }, { "behavior": { "normal": true, }, - "id": 542, + "id": 538, "literal": "^1.0.8", "name": "string.prototype.trimend", "npm": { "name": "string.prototype.trimend", "version": ">=1.0.8 <2.0.0", }, - "package_id": 350, + "package_id": 346, }, { "behavior": { "normal": true, }, - "id": 543, + "id": 539, "literal": "^1.0.8", "name": "string.prototype.trimstart", "npm": { "name": "string.prototype.trimstart", "version": ">=1.0.8 <2.0.0", }, - "package_id": 349, + "package_id": 345, }, { "behavior": { "normal": true, }, - "id": 544, + "id": 540, "literal": "^1.0.2", "name": "typed-array-buffer", "npm": { "name": "typed-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 348, + "package_id": 344, }, { "behavior": { "normal": true, }, - "id": 545, + "id": 541, "literal": "^1.0.1", "name": "typed-array-byte-length", "npm": { "name": "typed-array-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 347, + "package_id": 343, }, { "behavior": { "normal": true, }, - "id": 546, + "id": 542, "literal": "^1.0.2", "name": "typed-array-byte-offset", "npm": { "name": "typed-array-byte-offset", "version": ">=1.0.2 <2.0.0", }, - "package_id": 346, + "package_id": 342, }, { "behavior": { "normal": true, }, - "id": 547, + "id": 543, "literal": "^1.0.6", "name": "typed-array-length", "npm": { "name": "typed-array-length", "version": ">=1.0.6 <2.0.0", }, - "package_id": 344, + "package_id": 340, }, { "behavior": { "normal": true, }, - "id": 548, + "id": 544, "literal": "^1.0.2", "name": "unbox-primitive", "npm": { "name": "unbox-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 336, + "package_id": 332, }, { "behavior": { "normal": true, }, - "id": 549, + "id": 545, "literal": "^1.1.15", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.15 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 550, + "id": 546, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 551, + "id": 547, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 552, + "id": 548, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 553, + "id": 549, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 554, + "id": 550, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 555, + "id": 551, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 556, + "id": 552, "literal": "^1.1.3", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.3 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 557, + "id": 553, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 558, + "id": 554, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 559, + "id": 555, "literal": "^1.0.2", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.2 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 560, + "id": 556, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 561, + "id": 557, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 562, + "id": 558, "literal": "^1.0.1", "name": "is-bigint", "npm": { "name": "is-bigint", "version": ">=1.0.1 <2.0.0", }, - "package_id": 342, + "package_id": 338, }, { "behavior": { "normal": true, }, - "id": 563, + "id": 559, "literal": "^1.1.0", "name": "is-boolean-object", "npm": { "name": "is-boolean-object", "version": ">=1.1.0 <2.0.0", }, - "package_id": 341, + "package_id": 337, }, { "behavior": { "normal": true, }, - "id": 564, + "id": 560, "literal": "^1.0.4", "name": "is-number-object", "npm": { "name": "is-number-object", "version": ">=1.0.4 <2.0.0", }, - "package_id": 340, + "package_id": 336, }, { "behavior": { "normal": true, }, - "id": 565, + "id": 561, "literal": "^1.0.5", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.5 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 566, + "id": 562, "literal": "^1.0.3", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.3 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 567, + "id": 563, "literal": "^1.0.2", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.2 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 568, + "id": 564, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 569, + "id": 565, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 570, + "id": 566, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 571, + "id": 567, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 572, + "id": 568, "literal": "^1.0.1", "name": "has-bigints", "npm": { "name": "has-bigints", "version": ">=1.0.1 <2.0.0", }, - "package_id": 343, + "package_id": 339, }, { "behavior": { "normal": true, }, - "id": 573, + "id": 569, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 574, + "id": 570, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 575, + "id": 571, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 576, + "id": 572, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 577, + "id": 573, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 578, + "id": 574, "literal": "^1.0.0", "name": "possible-typed-array-names", "npm": { "name": "possible-typed-array-names", "version": ">=1.0.0 <2.0.0", }, - "package_id": 335, + "package_id": 331, }, { "behavior": { "normal": true, }, - "id": 579, + "id": 575, "literal": "^1.1.14", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.14 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 580, + "id": 576, "literal": "^1.0.7", "name": "available-typed-arrays", "npm": { "name": "available-typed-arrays", "version": ">=1.0.7 <2.0.0", }, - "package_id": 334, + "package_id": 330, }, { "behavior": { "normal": true, }, - "id": 581, + "id": 577, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 582, + "id": 578, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 583, + "id": 579, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 584, + "id": 580, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 585, + "id": 581, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 586, + "id": 582, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 587, + "id": 583, "literal": "^0.3.3", "name": "for-each", "npm": { "name": "for-each", "version": ">=0.3.3 <0.4.0", }, - "package_id": 332, + "package_id": 328, }, { "behavior": { "normal": true, }, - "id": 588, + "id": 584, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 589, + "id": 585, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 590, + "id": 586, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 591, + "id": 587, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 592, + "id": 588, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 593, + "id": 589, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 594, + "id": 590, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 595, + "id": 591, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 596, + "id": 592, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 597, + "id": 593, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 598, + "id": 594, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 599, + "id": 595, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 600, + "id": 596, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 601, + "id": 597, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 602, + "id": 598, "literal": "^1.23.0", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.0 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 603, + "id": 599, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 604, + "id": 600, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 605, + "id": 601, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 606, + "id": 602, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 607, + "id": 603, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 608, + "id": 604, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 609, + "id": 605, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 610, + "id": 606, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 611, + "id": 607, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 612, + "id": 608, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 613, + "id": 609, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 614, + "id": 610, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 615, + "id": 611, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 616, + "id": 612, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 617, + "id": 613, "literal": "^1.1.4", "name": "define-data-property", "npm": { "name": "define-data-property", "version": ">=1.1.4 <2.0.0", }, - "package_id": 324, + "package_id": 320, }, { "behavior": { "normal": true, }, - "id": 618, + "id": 614, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 619, + "id": 615, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 620, + "id": 616, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 621, + "id": 617, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 622, + "id": 618, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 623, + "id": 619, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 624, + "id": 620, "literal": "^1.1.1", "name": "object-keys", "npm": { "name": "object-keys", "version": ">=1.1.1 <2.0.0", }, - "package_id": 318, + "package_id": 314, }, { "behavior": { "normal": true, }, - "id": 625, + "id": 621, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 626, + "id": 622, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 627, + "id": 623, "literal": "^1.1.13", "name": "is-typed-array", "npm": { "name": "is-typed-array", "version": ">=1.1.13 <2.0.0", }, - "package_id": 345, + "package_id": 341, }, { "behavior": { "normal": true, }, - "id": 628, + "id": 624, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 629, + "id": 625, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 630, + "id": 626, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 631, + "id": 627, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -30994,267 +30786,267 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 632, + "id": 628, "literal": "^1.0.4", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.4 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 633, + "id": 629, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 634, + "id": 630, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 635, + "id": 631, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 636, + "id": 632, "literal": "^1.13.1", "name": "object-inspect", "npm": { "name": "object-inspect", "version": ">=1.13.1 <2.0.0", }, - "package_id": 360, + "package_id": 356, }, { "behavior": { "normal": true, }, - "id": 637, + "id": 633, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 638, + "id": 634, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 639, + "id": 635, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 640, + "id": 636, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 641, + "id": 637, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 642, + "id": 638, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 643, + "id": 639, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 644, + "id": 640, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 645, + "id": 641, "literal": "^1.2.3", "name": "functions-have-names", "npm": { "name": "functions-have-names", "version": ">=1.2.3 <2.0.0", }, - "package_id": 358, + "package_id": 354, }, { "behavior": { "normal": true, }, - "id": 646, + "id": 642, "literal": "^1.1.4", "name": "is-callable", "npm": { "name": "is-callable", "version": ">=1.1.4 <2.0.0", }, - "package_id": 333, + "package_id": 329, }, { "behavior": { "normal": true, }, - "id": 647, + "id": 643, "literal": "^1.0.1", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.1 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 648, + "id": 644, "literal": "^1.0.2", "name": "is-symbol", "npm": { "name": "is-symbol", "version": ">=1.0.2 <2.0.0", }, - "package_id": 338, + "package_id": 334, }, { "behavior": { "normal": true, }, - "id": 649, + "id": 645, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 650, + "id": 646, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 651, + "id": 647, "literal": "^1.0.2", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.2 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 652, + "id": 648, "literal": "^2.0.1", "name": "hasown", "npm": { @@ -31267,345 +31059,345 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 653, + "id": 649, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 654, + "id": 650, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 655, + "id": 651, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 656, + "id": 652, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 657, + "id": 653, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 658, + "id": 654, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 659, + "id": 655, "literal": "^1.0.6", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.6 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 660, + "id": 656, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 661, + "id": 657, "literal": "^1.0.1", "name": "is-data-view", "npm": { "name": "is-data-view", "version": ">=1.0.1 <2.0.0", }, - "package_id": 364, + "package_id": 360, }, { "behavior": { "normal": true, }, - "id": 662, + "id": 658, "literal": "^1.0.1", "name": "array-buffer-byte-length", "npm": { "name": "array-buffer-byte-length", "version": ">=1.0.1 <2.0.0", }, - "package_id": 378, + "package_id": 374, }, { "behavior": { "normal": true, }, - "id": 663, + "id": 659, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 664, + "id": 660, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 665, + "id": 661, "literal": "^1.22.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 666, + "id": 662, "literal": "^1.2.1", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.2.1 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 667, + "id": 663, "literal": "^1.2.3", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.3 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 668, + "id": 664, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 669, + "id": 665, "literal": "^1.0.2", "name": "is-shared-array-buffer", "npm": { "name": "is-shared-array-buffer", "version": ">=1.0.2 <2.0.0", }, - "package_id": 362, + "package_id": 358, }, { "behavior": { "normal": true, }, - "id": 670, + "id": 666, "literal": "^1.0.5", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.5 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 671, + "id": 667, "literal": "^3.0.4", "name": "is-array-buffer", "npm": { "name": "is-array-buffer", "version": ">=3.0.4 <4.0.0", }, - "package_id": 365, + "package_id": 361, }, { "behavior": { "normal": true, }, - "id": 672, + "id": 668, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 673, + "id": 669, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 674, + "id": 670, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 675, + "id": 671, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 676, + "id": 672, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 677, + "id": 673, "literal": "^2.1.1", "name": "ms", "npm": { "name": "ms", "version": ">=2.1.1 <3.0.0", }, - "package_id": 154, + "package_id": 155, }, { "behavior": { "normal": true, }, - "id": 678, + "id": 674, "literal": "^3.2.7", "name": "debug", "npm": { "name": "debug", "version": ">=3.2.7 <4.0.0", }, - "package_id": 381, + "package_id": 377, }, { "behavior": { "normal": true, }, - "id": 679, + "id": 675, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -31618,7 +31410,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 680, + "id": 676, "literal": "^1.22.4", "name": "resolve", "npm": { @@ -31631,72 +31423,72 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 681, + "id": 677, "literal": "^2.0.2", "name": "esutils", "npm": { "name": "esutils", "version": ">=2.0.2 <3.0.0", }, - "package_id": 164, + "package_id": 165, }, { "behavior": { "normal": true, }, - "id": 682, + "id": 678, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 683, + "id": 679, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 684, + "id": 680, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 685, + "id": 681, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 686, + "id": 682, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -31709,384 +31501,384 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 687, + "id": 683, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 688, + "id": 684, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 689, + "id": 685, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 690, + "id": 686, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 691, + "id": 687, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 692, + "id": 688, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 693, + "id": 689, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 694, + "id": 690, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 695, + "id": 691, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 696, + "id": 692, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 697, + "id": 693, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 698, + "id": 694, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 699, + "id": 695, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 700, + "id": 696, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 701, + "id": 697, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 702, + "id": 698, "literal": "^1.0.7", "name": "is-string", "npm": { "name": "is-string", "version": ">=1.0.7 <2.0.0", }, - "package_id": 339, + "package_id": 335, }, { "behavior": { "normal": true, }, - "id": 703, + "id": 699, "literal": "^1.0.0", "name": "resolve-pkg-maps", "npm": { "name": "resolve-pkg-maps", "version": ">=1.0.0 <2.0.0", }, - "package_id": 390, + "package_id": 386, }, { "behavior": { "normal": true, }, - "id": 704, + "id": 700, "literal": "^4.2.4", "name": "graceful-fs", "npm": { "name": "graceful-fs", "version": ">=4.2.4 <5.0.0", }, - "package_id": 174, + "package_id": 175, }, { "behavior": { "normal": true, }, - "id": 705, + "id": 701, "literal": "^2.2.0", "name": "tapable", "npm": { "name": "tapable", "version": ">=2.2.0 <3.0.0", }, - "package_id": 392, + "package_id": 388, }, { "behavior": { "peer": true, }, - "id": 706, + "id": 702, "literal": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0-0 <9.0.0 && >=8.0.0-0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 707, + "id": 703, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 708, + "id": 704, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 709, + "id": 705, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 710, + "id": 706, "literal": "6.21.0", "name": "@typescript-eslint/scope-manager", "npm": { "name": "@typescript-eslint/scope-manager", "version": "==6.21.0", }, - "package_id": 405, + "package_id": 401, }, { "behavior": { "normal": true, }, - "id": 711, + "id": 707, "literal": "6.21.0", "name": "@typescript-eslint/typescript-estree", "npm": { "name": "@typescript-eslint/typescript-estree", "version": "==6.21.0", }, - "package_id": 395, + "package_id": 391, }, { "behavior": { "peer": true, }, - "id": 712, + "id": 708, "literal": "^7.0.0 || ^8.0.0", "name": "eslint", "npm": { "name": "eslint", "version": ">=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 713, + "id": 709, "literal": "^4.3.4", "name": "debug", "npm": { "name": "debug", "version": ">=4.3.4 <5.0.0", }, - "package_id": 153, + "package_id": 154, }, { "behavior": { "normal": true, }, - "id": 714, + "id": 710, "literal": "^11.1.0", "name": "globby", "npm": { "name": "globby", "version": ">=11.1.0 <12.0.0", }, - "package_id": 400, + "package_id": 396, }, { "behavior": { "normal": true, }, - "id": 715, + "id": 711, "literal": "^7.5.4", "name": "semver", "npm": { "name": "semver", "version": ">=7.5.4 <8.0.0", }, - "package_id": 115, + "package_id": 116, }, { "behavior": { "normal": true, }, - "id": 716, + "id": 712, "literal": "^4.0.3", "name": "is-glob", "npm": { @@ -32099,85 +31891,85 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 717, + "id": 713, "literal": "9.0.3", "name": "minimatch", "npm": { "name": "minimatch", "version": "==9.0.3", }, - "package_id": 399, + "package_id": 395, }, { "behavior": { "normal": true, }, - "id": 718, + "id": 714, "literal": "^1.0.1", "name": "ts-api-utils", "npm": { "name": "ts-api-utils", "version": ">=1.0.1 <2.0.0", }, - "package_id": 398, + "package_id": 394, }, { "behavior": { "normal": true, }, - "id": 719, + "id": 715, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 720, + "id": 716, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 721, + "id": 717, "literal": "^3.4.1", "name": "eslint-visitor-keys", "npm": { "name": "eslint-visitor-keys", "version": ">=3.4.1 <4.0.0", }, - "package_id": 246, + "package_id": 242, }, { "behavior": { "normal": true, }, - "id": 722, + "id": 718, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "peer": true, }, - "id": 723, + "id": 719, "literal": ">=4.2.0", "name": "typescript", "npm": { @@ -32190,7 +31982,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 724, + "id": 720, "literal": "^2.0.1", "name": "brace-expansion", "npm": { @@ -32203,33 +31995,33 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 725, + "id": 721, "literal": "^2.1.0", "name": "array-union", "npm": { "name": "array-union", "version": ">=2.1.0 <3.0.0", }, - "package_id": 404, + "package_id": 400, }, { "behavior": { "normal": true, }, - "id": 726, + "id": 722, "literal": "^3.0.1", "name": "dir-glob", "npm": { "name": "dir-glob", "version": ">=3.0.1 <4.0.0", }, - "package_id": 402, + "package_id": 398, }, { "behavior": { "normal": true, }, - "id": 727, + "id": 723, "literal": "^3.2.9", "name": "fast-glob", "npm": { @@ -32242,20 +32034,20 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 728, + "id": 724, "literal": "^5.2.0", "name": "ignore", "npm": { "name": "ignore", "version": ">=5.2.0 <6.0.0", }, - "package_id": 267, + "package_id": 263, }, { "behavior": { "normal": true, }, - "id": 729, + "id": 725, "literal": "^1.4.1", "name": "merge2", "npm": { @@ -32268,59 +32060,59 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 730, + "id": 726, "literal": "^3.0.0", "name": "slash", "npm": { "name": "slash", "version": ">=3.0.0 <4.0.0", }, - "package_id": 401, + "package_id": 397, }, { "behavior": { "normal": true, }, - "id": 731, + "id": 727, "literal": "^4.0.0", "name": "path-type", "npm": { "name": "path-type", "version": ">=4.0.0 <5.0.0", }, - "package_id": 403, + "package_id": 399, }, { "behavior": { "normal": true, }, - "id": 732, + "id": 728, "literal": "6.21.0", "name": "@typescript-eslint/types", "npm": { "name": "@typescript-eslint/types", "version": "==6.21.0", }, - "package_id": 397, + "package_id": 393, }, { "behavior": { "normal": true, }, - "id": 733, + "id": 729, "literal": "6.21.0", "name": "@typescript-eslint/visitor-keys", "npm": { "name": "@typescript-eslint/visitor-keys", "version": "==6.21.0", }, - "package_id": 396, + "package_id": 392, }, { "behavior": { "normal": true, }, - "id": 734, + "id": 730, "literal": "10.3.10", "name": "glob", "npm": { @@ -32333,111 +32125,111 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 735, + "id": 731, "literal": "^7.23.2", "name": "@babel/runtime", "npm": { "name": "@babel/runtime", "version": ">=7.23.2 <8.0.0", }, - "package_id": 431, + "package_id": 427, }, { "behavior": { "normal": true, }, - "id": 736, + "id": 732, "literal": "^5.3.0", "name": "aria-query", "npm": { "name": "aria-query", "version": ">=5.3.0 <6.0.0", }, - "package_id": 430, + "package_id": 426, }, { "behavior": { "normal": true, }, - "id": 737, + "id": 733, "literal": "^3.1.7", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.7 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 738, + "id": 734, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 739, + "id": 735, "literal": "^0.0.8", "name": "ast-types-flow", "npm": { "name": "ast-types-flow", "version": ">=0.0.8 <0.0.9", }, - "package_id": 429, + "package_id": 425, }, { "behavior": { "normal": true, }, - "id": 740, + "id": 736, "literal": "=4.7.0", "name": "axe-core", "npm": { "name": "axe-core", "version": "==4.7.0", }, - "package_id": 428, + "package_id": 424, }, { "behavior": { "normal": true, }, - "id": 741, + "id": 737, "literal": "^3.2.1", "name": "axobject-query", "npm": { "name": "axobject-query", "version": ">=3.2.1 <4.0.0", }, - "package_id": 426, + "package_id": 422, }, { "behavior": { "normal": true, }, - "id": 742, + "id": 738, "literal": "^1.0.8", "name": "damerau-levenshtein", "npm": { "name": "damerau-levenshtein", "version": ">=1.0.8 <2.0.0", }, - "package_id": 425, + "package_id": 421, }, { "behavior": { "normal": true, }, - "id": 743, + "id": 739, "literal": "^9.2.2", "name": "emoji-regex", "npm": { @@ -32450,20 +32242,20 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 744, + "id": 740, "literal": "^1.0.15", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.15 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 745, + "id": 741, "literal": "^2.0.0", "name": "hasown", "npm": { @@ -32476,254 +32268,254 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 746, + "id": 742, "literal": "^3.3.5", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=3.3.5 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 747, + "id": 743, "literal": "^1.0.9", "name": "language-tags", "npm": { "name": "language-tags", "version": ">=1.0.9 <2.0.0", }, - "package_id": 410, + "package_id": 406, }, { "behavior": { "normal": true, }, - "id": 748, + "id": 744, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 749, + "id": 745, "literal": "^1.1.7", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.7 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 750, + "id": 746, "literal": "^2.0.7", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.7 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "peer": true, }, - "id": 751, + "id": 747, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 752, + "id": 748, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 753, + "id": 749, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 754, + "id": 750, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 755, + "id": 751, "literal": "^0.3.20", "name": "language-subtag-registry", "npm": { "name": "language-subtag-registry", "version": ">=0.3.20 <0.4.0", }, - "package_id": 411, + "package_id": 407, }, { "behavior": { "normal": true, }, - "id": 756, + "id": 752, "literal": "^3.1.6", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.6 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 757, + "id": 753, "literal": "^1.3.1", "name": "array.prototype.flat", "npm": { "name": "array.prototype.flat", "version": ">=1.3.1 <2.0.0", }, - "package_id": 386, + "package_id": 382, }, { "behavior": { "normal": true, }, - "id": 758, + "id": 754, "literal": "^4.1.4", "name": "object.assign", "npm": { "name": "object.assign", "version": ">=4.1.4 <5.0.0", }, - "package_id": 359, + "package_id": 355, }, { "behavior": { "normal": true, }, - "id": 759, + "id": 755, "literal": "^1.1.6", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.1.6 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 760, + "id": 756, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 761, + "id": 757, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 762, + "id": 758, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 763, + "id": 759, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 764, + "id": 760, "literal": "^2.0.3", "name": "es-set-tostringtag", "npm": { "name": "es-set-tostringtag", "version": ">=2.0.3 <3.0.0", }, - "package_id": 373, + "package_id": 369, }, { "behavior": { "normal": true, }, - "id": 765, + "id": 761, "literal": "^1.1.2", "name": "function-bind", "npm": { @@ -32736,982 +32528,982 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 766, + "id": 762, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 767, + "id": 763, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 768, + "id": 764, "literal": "^1.0.2", "name": "has-property-descriptors", "npm": { "name": "has-property-descriptors", "version": ">=1.0.2 <2.0.0", }, - "package_id": 319, + "package_id": 315, }, { "behavior": { "normal": true, }, - "id": 769, + "id": 765, "literal": "^1.0.3", "name": "has-proto", "npm": { "name": "has-proto", "version": ">=1.0.3 <2.0.0", }, - "package_id": 323, + "package_id": 319, }, { "behavior": { "normal": true, }, - "id": 770, + "id": 766, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 771, + "id": 767, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 772, + "id": 768, "literal": "^1.1.2", "name": "iterator.prototype", "npm": { "name": "iterator.prototype", "version": ">=1.1.2 <2.0.0", }, - "package_id": 414, + "package_id": 410, }, { "behavior": { "normal": true, }, - "id": 773, + "id": 769, "literal": "^1.1.2", "name": "safe-array-concat", "npm": { "name": "safe-array-concat", "version": ">=1.1.2 <2.0.0", }, - "package_id": 354, + "package_id": 350, }, { "behavior": { "normal": true, }, - "id": 774, + "id": 770, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 775, + "id": 771, "literal": "^1.2.1", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.1 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 776, + "id": 772, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 777, + "id": 773, "literal": "^1.0.4", "name": "reflect.getprototypeof", "npm": { "name": "reflect.getprototypeof", "version": ">=1.0.4 <2.0.0", }, - "package_id": 415, + "package_id": 411, }, { "behavior": { "normal": true, }, - "id": 778, + "id": 774, "literal": "^2.0.1", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.1 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 779, + "id": 775, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 780, + "id": 776, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 781, + "id": 777, "literal": "^1.23.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 782, + "id": 778, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 783, + "id": 779, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 784, + "id": 780, "literal": "^1.0.3", "name": "globalthis", "npm": { "name": "globalthis", "version": ">=1.0.3 <2.0.0", }, - "package_id": 368, + "package_id": 364, }, { "behavior": { "normal": true, }, - "id": 785, + "id": 781, "literal": "^1.1.3", "name": "which-builtin-type", "npm": { "name": "which-builtin-type", "version": ">=1.1.3 <2.0.0", }, - "package_id": 416, + "package_id": 412, }, { "behavior": { "normal": true, }, - "id": 786, + "id": 782, "literal": "^1.1.5", "name": "function.prototype.name", "npm": { "name": "function.prototype.name", "version": ">=1.1.5 <2.0.0", }, - "package_id": 370, + "package_id": 366, }, { "behavior": { "normal": true, }, - "id": 787, + "id": 783, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 788, + "id": 784, "literal": "^2.0.0", "name": "is-async-function", "npm": { "name": "is-async-function", "version": ">=2.0.0 <3.0.0", }, - "package_id": 424, + "package_id": 420, }, { "behavior": { "normal": true, }, - "id": 789, + "id": 785, "literal": "^1.0.5", "name": "is-date-object", "npm": { "name": "is-date-object", "version": ">=1.0.5 <2.0.0", }, - "package_id": 372, + "package_id": 368, }, { "behavior": { "normal": true, }, - "id": 790, + "id": 786, "literal": "^1.0.2", "name": "is-finalizationregistry", "npm": { "name": "is-finalizationregistry", "version": ">=1.0.2 <2.0.0", }, - "package_id": 423, + "package_id": 419, }, { "behavior": { "normal": true, }, - "id": 791, + "id": 787, "literal": "^1.0.10", "name": "is-generator-function", "npm": { "name": "is-generator-function", "version": ">=1.0.10 <2.0.0", }, - "package_id": 422, + "package_id": 418, }, { "behavior": { "normal": true, }, - "id": 792, + "id": 788, "literal": "^1.1.4", "name": "is-regex", "npm": { "name": "is-regex", "version": ">=1.1.4 <2.0.0", }, - "package_id": 353, + "package_id": 349, }, { "behavior": { "normal": true, }, - "id": 793, + "id": 789, "literal": "^1.0.2", "name": "is-weakref", "npm": { "name": "is-weakref", "version": ">=1.0.2 <2.0.0", }, - "package_id": 361, + "package_id": 357, }, { "behavior": { "normal": true, }, - "id": 794, + "id": 790, "literal": "^2.0.5", "name": "isarray", "npm": { "name": "isarray", "version": ">=2.0.5 <3.0.0", }, - "package_id": 355, + "package_id": 351, }, { "behavior": { "normal": true, }, - "id": 795, + "id": 791, "literal": "^1.0.2", "name": "which-boxed-primitive", "npm": { "name": "which-boxed-primitive", "version": ">=1.0.2 <2.0.0", }, - "package_id": 337, + "package_id": 333, }, { "behavior": { "normal": true, }, - "id": 796, + "id": 792, "literal": "^1.0.1", "name": "which-collection", "npm": { "name": "which-collection", "version": ">=1.0.1 <2.0.0", }, - "package_id": 417, + "package_id": 413, }, { "behavior": { "normal": true, }, - "id": 797, + "id": 793, "literal": "^1.1.9", "name": "which-typed-array", "npm": { "name": "which-typed-array", "version": ">=1.1.9 <2.0.0", }, - "package_id": 330, + "package_id": 326, }, { "behavior": { "normal": true, }, - "id": 798, + "id": 794, "literal": "^2.0.3", "name": "is-map", "npm": { "name": "is-map", "version": ">=2.0.3 <3.0.0", }, - "package_id": 421, + "package_id": 417, }, { "behavior": { "normal": true, }, - "id": 799, + "id": 795, "literal": "^2.0.3", "name": "is-set", "npm": { "name": "is-set", "version": ">=2.0.3 <3.0.0", }, - "package_id": 420, + "package_id": 416, }, { "behavior": { "normal": true, }, - "id": 800, + "id": 796, "literal": "^2.0.2", "name": "is-weakmap", "npm": { "name": "is-weakmap", "version": ">=2.0.2 <3.0.0", }, - "package_id": 419, + "package_id": 415, }, { "behavior": { "normal": true, }, - "id": 801, + "id": 797, "literal": "^2.0.3", "name": "is-weakset", "npm": { "name": "is-weakset", "version": ">=2.0.3 <3.0.0", }, - "package_id": 418, + "package_id": 414, }, { "behavior": { "normal": true, }, - "id": 802, + "id": 798, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 803, + "id": 799, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 804, + "id": 800, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 805, + "id": 801, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 806, + "id": 802, "literal": "^1.0.0", "name": "has-tostringtag", "npm": { "name": "has-tostringtag", "version": ">=1.0.0 <2.0.0", }, - "package_id": 331, + "package_id": 327, }, { "behavior": { "normal": true, }, - "id": 807, + "id": 803, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 808, + "id": 804, "literal": "^2.0.3", "name": "dequal", "npm": { "name": "dequal", "version": ">=2.0.3 <3.0.0", }, - "package_id": 427, + "package_id": 423, }, { "behavior": { "normal": true, }, - "id": 809, + "id": 805, "literal": "^0.14.0", "name": "regenerator-runtime", "npm": { "name": "regenerator-runtime", "version": ">=0.14.0 <0.15.0", }, - "package_id": 432, + "package_id": 428, }, { "behavior": { "normal": true, }, - "id": 810, + "id": 806, "literal": "^3.1.8", "name": "array-includes", "npm": { "name": "array-includes", "version": ">=3.1.8 <4.0.0", }, - "package_id": 388, + "package_id": 384, }, { "behavior": { "normal": true, }, - "id": 811, + "id": 807, "literal": "^1.2.5", "name": "array.prototype.findlast", "npm": { "name": "array.prototype.findlast", "version": ">=1.2.5 <2.0.0", }, - "package_id": 441, + "package_id": 437, }, { "behavior": { "normal": true, }, - "id": 812, + "id": 808, "literal": "^1.3.2", "name": "array.prototype.flatmap", "npm": { "name": "array.prototype.flatmap", "version": ">=1.3.2 <2.0.0", }, - "package_id": 384, + "package_id": 380, }, { "behavior": { "normal": true, }, - "id": 813, + "id": 809, "literal": "^1.1.2", "name": "array.prototype.toreversed", "npm": { "name": "array.prototype.toreversed", "version": ">=1.1.2 <2.0.0", }, - "package_id": 440, + "package_id": 436, }, { "behavior": { "normal": true, }, - "id": 814, + "id": 810, "literal": "^1.1.3", "name": "array.prototype.tosorted", "npm": { "name": "array.prototype.tosorted", "version": ">=1.1.3 <2.0.0", }, - "package_id": 439, + "package_id": 435, }, { "behavior": { "normal": true, }, - "id": 815, + "id": 811, "literal": "^2.1.0", "name": "doctrine", "npm": { "name": "doctrine", "version": ">=2.1.0 <3.0.0", }, - "package_id": 383, + "package_id": 379, }, { "behavior": { "normal": true, }, - "id": 816, + "id": 812, "literal": "^1.0.19", "name": "es-iterator-helpers", "npm": { "name": "es-iterator-helpers", "version": ">=1.0.19 <2.0.0", }, - "package_id": 413, + "package_id": 409, }, { "behavior": { "normal": true, }, - "id": 817, + "id": 813, "literal": "^5.3.0", "name": "estraverse", "npm": { "name": "estraverse", "version": ">=5.3.0 <6.0.0", }, - "package_id": 165, + "package_id": 166, }, { "behavior": { "normal": true, }, - "id": 818, + "id": 814, "literal": "^2.4.1 || ^3.0.0", "name": "jsx-ast-utils", "npm": { "name": "jsx-ast-utils", "version": ">=2.4.1 <3.0.0 || >=3.0.0 <4.0.0 && >=3.0.0 <4.0.0", }, - "package_id": 412, + "package_id": 408, }, { "behavior": { "normal": true, }, - "id": 819, + "id": 815, "literal": "^3.1.2", "name": "minimatch", "npm": { "name": "minimatch", "version": ">=3.1.2 <4.0.0", }, - "package_id": 248, + "package_id": 244, }, { "behavior": { "normal": true, }, - "id": 820, + "id": 816, "literal": "^1.1.8", "name": "object.entries", "npm": { "name": "object.entries", "version": ">=1.1.8 <2.0.0", }, - "package_id": 409, + "package_id": 405, }, { "behavior": { "normal": true, }, - "id": 821, + "id": 817, "literal": "^2.0.8", "name": "object.fromentries", "npm": { "name": "object.fromentries", "version": ">=2.0.8 <3.0.0", }, - "package_id": 379, + "package_id": 375, }, { "behavior": { "normal": true, }, - "id": 822, + "id": 818, "literal": "^1.1.4", "name": "object.hasown", "npm": { "name": "object.hasown", "version": ">=1.1.4 <2.0.0", }, - "package_id": 438, + "package_id": 434, }, { "behavior": { "normal": true, }, - "id": 823, + "id": 819, "literal": "^1.2.0", "name": "object.values", "npm": { "name": "object.values", "version": ">=1.2.0 <2.0.0", }, - "package_id": 314, + "package_id": 310, }, { "behavior": { "normal": true, }, - "id": 824, + "id": 820, "literal": "^15.8.1", "name": "prop-types", "npm": { "name": "prop-types", "version": ">=15.8.1 <16.0.0", }, - "package_id": 436, + "package_id": 432, }, { "behavior": { "normal": true, }, - "id": 825, + "id": 821, "literal": "^2.0.0-next.5", "name": "resolve", "npm": { "name": "resolve", "version": ">=2.0.0-next.5 <3.0.0", }, - "package_id": 435, + "package_id": 431, }, { "behavior": { "normal": true, }, - "id": 826, + "id": 822, "literal": "^6.3.1", "name": "semver", "npm": { "name": "semver", "version": ">=6.3.1 <7.0.0", }, - "package_id": 313, + "package_id": 309, }, { "behavior": { "normal": true, }, - "id": 827, + "id": 823, "literal": "^4.0.11", "name": "string.prototype.matchall", "npm": { "name": "string.prototype.matchall", "version": ">=4.0.11 <5.0.0", }, - "package_id": 434, + "package_id": 430, }, { "behavior": { "peer": true, }, - "id": 828, + "id": 824, "literal": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "name": "eslint", "npm": { "name": "eslint", "version": ">=3.0.0 <4.0.0 || >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=4.0.0 <5.0.0 || >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=5.0.0 <6.0.0 || >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=6.0.0 <7.0.0 || >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=7.0.0 <8.0.0 || >=8.0.0 <9.0.0 && >=8.0.0 <9.0.0", }, - "package_id": 242, + "package_id": 238, }, { "behavior": { "normal": true, }, - "id": 829, + "id": 825, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 830, + "id": 826, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 831, + "id": 827, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 832, + "id": 828, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 833, + "id": 829, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 834, + "id": 830, "literal": "^1.2.4", "name": "get-intrinsic", "npm": { "name": "get-intrinsic", "version": ">=1.2.4 <2.0.0", }, - "package_id": 321, + "package_id": 317, }, { "behavior": { "normal": true, }, - "id": 835, + "id": 831, "literal": "^1.0.1", "name": "gopd", "npm": { "name": "gopd", "version": ">=1.0.1 <2.0.0", }, - "package_id": 325, + "package_id": 321, }, { "behavior": { "normal": true, }, - "id": 836, + "id": 832, "literal": "^1.0.3", "name": "has-symbols", "npm": { "name": "has-symbols", "version": ">=1.0.3 <2.0.0", }, - "package_id": 322, + "package_id": 318, }, { "behavior": { "normal": true, }, - "id": 837, + "id": 833, "literal": "^1.0.7", "name": "internal-slot", "npm": { "name": "internal-slot", "version": ">=1.0.7 <2.0.0", }, - "package_id": 366, + "package_id": 362, }, { "behavior": { "normal": true, }, - "id": 838, + "id": 834, "literal": "^1.5.2", "name": "regexp.prototype.flags", "npm": { "name": "regexp.prototype.flags", "version": ">=1.5.2 <2.0.0", }, - "package_id": 356, + "package_id": 352, }, { "behavior": { "normal": true, }, - "id": 839, + "id": 835, "literal": "^2.0.2", "name": "set-function-name", "npm": { "name": "set-function-name", "version": ">=2.0.2 <3.0.0", }, - "package_id": 357, + "package_id": 353, }, { "behavior": { "normal": true, }, - "id": 840, + "id": 836, "literal": "^1.0.6", "name": "side-channel", "npm": { "name": "side-channel", "version": ">=1.0.6 <2.0.0", }, - "package_id": 367, + "package_id": 363, }, { "behavior": { "normal": true, }, - "id": 841, + "id": 837, "literal": "^2.13.0", "name": "is-core-module", "npm": { @@ -33724,7 +33516,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 842, + "id": 838, "literal": "^1.0.7", "name": "path-parse", "npm": { @@ -33737,7 +33529,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 843, + "id": 839, "literal": "^1.0.0", "name": "supports-preserve-symlinks-flag", "npm": { @@ -33750,7 +33542,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 844, + "id": 840, "literal": "^1.4.0", "name": "loose-envify", "npm": { @@ -33763,7 +33555,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 845, + "id": 841, "literal": "^4.1.1", "name": "object-assign", "npm": { @@ -33776,345 +33568,345 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 846, + "id": 842, "literal": "^16.13.1", "name": "react-is", "npm": { "name": "react-is", "version": ">=16.13.1 <17.0.0", }, - "package_id": 437, + "package_id": 433, }, { "behavior": { "normal": true, }, - "id": 847, + "id": 843, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 848, + "id": 844, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 849, + "id": 845, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 850, + "id": 846, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 851, + "id": 847, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 852, + "id": 848, "literal": "^1.23.3", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.3 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 853, + "id": 849, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 854, + "id": 850, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 855, + "id": 851, "literal": "^1.0.2", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.2 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 856, + "id": 852, "literal": "^1.2.0", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.0 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 857, + "id": 853, "literal": "^1.22.1", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.22.1 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 858, + "id": 854, "literal": "^1.0.0", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.0 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 859, + "id": 855, "literal": "^1.0.7", "name": "call-bind", "npm": { "name": "call-bind", "version": ">=1.0.7 <2.0.0", }, - "package_id": 326, + "package_id": 322, }, { "behavior": { "normal": true, }, - "id": 860, + "id": 856, "literal": "^1.2.1", "name": "define-properties", "npm": { "name": "define-properties", "version": ">=1.2.1 <2.0.0", }, - "package_id": 317, + "package_id": 313, }, { "behavior": { "normal": true, }, - "id": 861, + "id": 857, "literal": "^1.23.2", "name": "es-abstract", "npm": { "name": "es-abstract", "version": ">=1.23.2 <2.0.0", }, - "package_id": 329, + "package_id": 325, }, { "behavior": { "normal": true, }, - "id": 862, + "id": 858, "literal": "^1.3.0", "name": "es-errors", "npm": { "name": "es-errors", "version": ">=1.3.0 <2.0.0", }, - "package_id": 316, + "package_id": 312, }, { "behavior": { "normal": true, }, - "id": 863, + "id": 859, "literal": "^1.0.0", "name": "es-object-atoms", "npm": { "name": "es-object-atoms", "version": ">=1.0.0 <2.0.0", }, - "package_id": 315, + "package_id": 311, }, { "behavior": { "normal": true, }, - "id": 864, + "id": 860, "literal": "^1.0.2", "name": "es-shim-unscopables", "npm": { "name": "es-shim-unscopables", "version": ">=1.0.2 <2.0.0", }, - "package_id": 385, + "package_id": 381, }, { "behavior": { "normal": true, }, - "id": 865, + "id": 861, "literal": "~8.5.10", "name": "@types/ws", "npm": { "name": "@types/ws", "version": ">=8.5.10 <8.6.0", }, - "package_id": 443, + "package_id": 439, }, { "behavior": { "normal": true, }, - "id": 866, + "id": 862, "literal": "~20.12.8", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=20.12.8 <20.13.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 867, + "id": 863, "literal": "*", "name": "@types/node", "npm": { "name": "@types/node", "version": ">=0.0.0", }, - "package_id": 182, + "package_id": 183, }, { "behavior": { "normal": true, }, - "id": 868, + "id": 864, "literal": "^4.21.10", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.10 <5.0.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 869, + "id": 865, "literal": "^1.0.30001538", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001538 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 870, + "id": 866, "literal": "^4.3.6", "name": "fraction.js", "npm": { "name": "fraction.js", "version": ">=4.3.6 <5.0.0", }, - "package_id": 446, + "package_id": 442, }, { "behavior": { "normal": true, }, - "id": 871, + "id": 867, "literal": "^0.1.2", "name": "normalize-range", "npm": { "name": "normalize-range", "version": ">=0.1.2 <0.2.0", }, - "package_id": 445, + "package_id": 441, }, { "behavior": { "normal": true, }, - "id": 872, + "id": 868, "literal": "^1.0.0", "name": "picocolors", "npm": { @@ -34127,7 +33919,7 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 873, + "id": 869, "literal": "^4.2.0", "name": "postcss-value-parser", "npm": { @@ -34140,7 +33932,7 @@ exports[`next build works: node 1`] = ` "behavior": { "peer": true, }, - "id": 874, + "id": 870, "literal": "^8.1.0", "name": "postcss", "npm": { @@ -34153,72 +33945,72 @@ exports[`next build works: node 1`] = ` "behavior": { "normal": true, }, - "id": 875, + "id": 871, "literal": "^1.0.30001587", "name": "caniuse-lite", "npm": { "name": "caniuse-lite", "version": ">=1.0.30001587 <2.0.0", }, - "package_id": 233, + "package_id": 229, }, { "behavior": { "normal": true, }, - "id": 876, + "id": 872, "literal": "^1.4.668", "name": "electron-to-chromium", "npm": { "name": "electron-to-chromium", "version": ">=1.4.668 <2.0.0", }, - "package_id": 450, + "package_id": 446, }, { "behavior": { "normal": true, }, - "id": 877, + "id": 873, "literal": "^2.0.14", "name": "node-releases", "npm": { "name": "node-releases", "version": ">=2.0.14 <3.0.0", }, - "package_id": 449, + "package_id": 445, }, { "behavior": { "normal": true, }, - "id": 878, + "id": 874, "literal": "^1.0.13", "name": "update-browserslist-db", "npm": { "name": "update-browserslist-db", "version": ">=1.0.13 <2.0.0", }, - "package_id": 448, + "package_id": 444, }, { "behavior": { "normal": true, }, - "id": 879, + "id": 875, "literal": "^3.1.2", "name": "escalade", "npm": { "name": "escalade", "version": ">=3.1.2 <4.0.0", }, - "package_id": 123, + "package_id": 124, }, { "behavior": { "normal": true, }, - "id": 880, + "id": 876, "literal": "^1.0.1", "name": "picocolors", "npm": { @@ -34231,128 +34023,128 @@ exports[`next build works: node 1`] = ` "behavior": { "peer": true, }, - "id": 881, + "id": 877, "literal": ">= 4.21.0", "name": "browserslist", "npm": { "name": "browserslist", "version": ">=4.21.0", }, - "package_id": 447, + "package_id": 443, }, { "behavior": { "normal": true, }, - "id": 882, + "id": 878, "literal": "*", "name": "@types/react", "npm": { "name": "@types/react", "version": ">=0.0.0", }, - "package_id": 452, + "package_id": 448, }, { "behavior": { "normal": true, }, - "id": 883, + "id": 879, "literal": "*", "name": "@types/prop-types", "npm": { "name": "@types/prop-types", "version": ">=0.0.0", }, - "package_id": 455, + "package_id": 451, }, { "behavior": { "normal": true, }, - "id": 884, + "id": 880, "literal": "*", "name": "@types/scheduler", "npm": { "name": "@types/scheduler", "version": ">=0.0.0", }, - "package_id": 454, + "package_id": 450, }, { "behavior": { "normal": true, }, - "id": 885, + "id": 881, "literal": "^3.0.2", "name": "csstype", "npm": { "name": "csstype", "version": ">=3.0.2 <4.0.0", }, - "package_id": 453, + "package_id": 449, }, ], "format": "v2", - "meta_hash": "4688315a50aab25bb1d5fe41e445b346f9c0c71bf75f43ebbc91db59253d9026", + "meta_hash": "632a4f7405ad36643df0c844e942395e7c61cf79c7738eb128eba03ebdd1e094", "package_index": { "@alloc/quick-lru": 13, - "@babel/code-frame": 206, - "@babel/helper-validator-identifier": 215, - "@babel/highlight": 207, - "@babel/runtime": 431, - "@eslint-community/eslint-utils": 245, - "@eslint-community/regexpp": 252, - "@eslint/eslintrc": 265, - "@eslint/js": 293, - "@humanwhocodes/config-array": 247, - "@humanwhocodes/module-importer": 244, - "@humanwhocodes/object-schema": 251, + "@babel/code-frame": 202, + "@babel/helper-validator-identifier": 211, + "@babel/highlight": 203, + "@babel/runtime": 427, + "@eslint-community/eslint-utils": 241, + "@eslint-community/regexpp": 248, + "@eslint/eslintrc": 261, + "@eslint/js": 289, + "@humanwhocodes/config-array": 243, + "@humanwhocodes/module-importer": 240, + "@humanwhocodes/object-schema": 247, "@isaacs/cliui": 74, "@jridgewell/gen-mapping": 100, "@jridgewell/resolve-uri": 103, "@jridgewell/set-array": 104, "@jridgewell/sourcemap-codec": 102, "@jridgewell/trace-mapping": 101, - "@next/env": 237, - "@next/eslint-plugin-next": 406, - "@next/swc-darwin-arm64": 231, - "@next/swc-darwin-x64": 232, - "@next/swc-linux-arm64-gnu": 227, - "@next/swc-linux-arm64-musl": 225, - "@next/swc-linux-x64-gnu": 230, - "@next/swc-linux-x64-musl": 229, - "@next/swc-win32-arm64-msvc": 224, - "@next/swc-win32-ia32-msvc": 226, - "@next/swc-win32-x64-msvc": 228, + "@next/env": 233, + "@next/eslint-plugin-next": 402, + "@next/swc-darwin-arm64": 227, + "@next/swc-darwin-x64": 228, + "@next/swc-linux-arm64-gnu": 223, + "@next/swc-linux-arm64-musl": 221, + "@next/swc-linux-x64-gnu": 226, + "@next/swc-linux-x64-musl": 225, + "@next/swc-win32-arm64-msvc": 220, + "@next/swc-win32-ia32-msvc": 222, + "@next/swc-win32-x64-msvc": 224, "@nodelib/fs.scandir": 46, "@nodelib/fs.stat": 49, "@nodelib/fs.walk": 43, "@pkgjs/parseargs": 73, - "@puppeteer/browsers": 114, - "@rushstack/eslint-patch": 407, - "@swc/helpers": 234, - "@tootallnate/quickjs-emscripten": 177, - "@types/json5": 312, + "@puppeteer/browsers": 115, + "@rushstack/eslint-patch": 403, + "@swc/helpers": 230, + "@tootallnate/quickjs-emscripten": 178, + "@types/json5": 308, "@types/node": [ - 182, - 456, + 183, + 452, ], - "@types/prop-types": 455, - "@types/react": 452, - "@types/react-dom": 451, - "@types/scheduler": 454, - "@types/ws": 443, - "@types/yauzl": 181, - "@typescript-eslint/parser": 394, - "@typescript-eslint/scope-manager": 405, - "@typescript-eslint/types": 397, - "@typescript-eslint/typescript-estree": 395, - "@typescript-eslint/visitor-keys": 396, - "acorn": 272, - "acorn-jsx": 271, - "agent-base": 155, - "ajv": 273, + "@types/prop-types": 451, + "@types/react": 448, + "@types/react-dom": 447, + "@types/scheduler": 450, + "@types/ws": 439, + "@types/yauzl": 182, + "@typescript-eslint/parser": 390, + "@typescript-eslint/scope-manager": 401, + "@typescript-eslint/types": 393, + "@typescript-eslint/typescript-estree": 391, + "@typescript-eslint/visitor-keys": 392, + "acorn": 268, + "acorn-jsx": 267, + "agent-base": 156, + "ajv": 269, "ansi-regex": [ 86, 77, @@ -34360,316 +34152,314 @@ exports[`next build works: node 1`] = ` "ansi-styles": [ 90, 81, - 212, + 208, ], "any-promise": 62, "anymatch": 55, "arg": 107, - "argparse": 217, - "aria-query": 430, - "array-buffer-byte-length": 378, - "array-includes": 388, - "array-union": 404, - "array.prototype.findlast": 441, - "array.prototype.findlastindex": 387, - "array.prototype.flat": 386, - "array.prototype.flatmap": 384, - "array.prototype.toreversed": 440, - "array.prototype.tosorted": 439, - "arraybuffer.prototype.slice": 377, - "ast-types": 166, - "ast-types-flow": 429, - "autoprefixer": 444, - "available-typed-arrays": 334, - "axe-core": 428, - "axobject-query": 426, - "b4a": 138, + "argparse": 213, + "aria-query": 426, + "array-buffer-byte-length": 374, + "array-includes": 384, + "array-union": 400, + "array.prototype.findlast": 437, + "array.prototype.findlastindex": 383, + "array.prototype.flat": 382, + "array.prototype.flatmap": 380, + "array.prototype.toreversed": 436, + "array.prototype.tosorted": 435, + "arraybuffer.prototype.slice": 373, + "ast-types": 167, + "ast-types-flow": 425, + "autoprefixer": 440, + "available-typed-arrays": 330, + "axe-core": 424, + "axobject-query": 422, + "b4a": 139, "balanced-match": 71, - "bare-events": 136, - "bare-fs": 133, - "bare-os": 132, - "bare-path": 131, - "bare-stream": 134, - "base64-js": 129, - "basic-ftp": 176, + "bare-events": 137, + "bare-fs": 134, + "bare-os": 133, + "bare-path": 132, + "bare-stream": 135, + "base64-js": 130, + "basic-ftp": 177, "binary-extensions": 54, "brace-expansion": [ 70, - 249, + 245, ], "braces": 34, - "browserslist": 447, - "buffer": 127, - "buffer-crc32": 185, - "bun-types": 442, - "busboy": 239, - "call-bind": 326, - "callsites": 221, + "browserslist": 443, + "buffer": 128, + "buffer-crc32": 186, + "bun-types": 438, + "busboy": 235, + "call-bind": 322, + "callsites": 217, "camelcase-css": 31, - "caniuse-lite": 233, + "caniuse-lite": 229, "chalk": [ - 303, - 208, + 299, + 204, ], "chokidar": 50, - "chromium-bidi": 198, - "client-only": 236, - "cliui": 124, + "chromium-bidi": 193, + "client-only": 232, + "cliui": 125, "color-convert": [ 82, - 213, + 209, ], "color-name": [ 83, - 214, + 210, ], "commander": 99, - "concat-map": 250, - "cosmiconfig": 201, - "cross-fetch": 193, + "concat-map": 246, + "cosmiconfig": 197, "cross-spawn": 93, "cssesc": 5, - "csstype": 453, - "damerau-levenshtein": 425, - "data-uri-to-buffer": 175, - "data-view-buffer": 376, - "data-view-byte-length": 375, - "data-view-byte-offset": 374, + "csstype": 449, + "damerau-levenshtein": 421, + "data-uri-to-buffer": 176, + "data-view-buffer": 372, + "data-view-byte-length": 371, + "data-view-byte-offset": 370, "debug": [ - 153, - 189, - 381, + 154, + 190, + 377, ], - "deep-is": 292, + "deep-is": 288, "default-create-template": 0, - "define-data-property": 324, - "define-properties": 317, - "degenerator": 160, - "dequal": 427, - "devtools-protocol": 192, + "define-data-property": 320, + "define-properties": 313, + "degenerator": 161, + "dequal": 423, + "devtools-protocol": 114, "didyoumean": 38, - "dir-glob": 402, + "dir-glob": 398, "dlv": 106, "doctrine": [ - 295, - 383, + 291, + 379, ], "eastasianwidth": 89, - "electron-to-chromium": 450, + "electron-to-chromium": 446, "emoji-regex": [ 88, 80, ], - "end-of-stream": 145, - "enhanced-resolve": 391, - "env-paths": 222, - "error-ex": 204, - "es-abstract": 329, - "es-define-property": 320, - "es-errors": 316, - "es-iterator-helpers": 413, - "es-object-atoms": 315, - "es-set-tostringtag": 373, - "es-shim-unscopables": 385, - "es-to-primitive": 371, - "escalade": 123, + "end-of-stream": 146, + "enhanced-resolve": 387, + "env-paths": 218, + "error-ex": 200, + "es-abstract": 325, + "es-define-property": 316, + "es-errors": 312, + "es-iterator-helpers": 409, + "es-object-atoms": 311, + "es-set-tostringtag": 369, + "es-shim-unscopables": 381, + "es-to-primitive": 367, + "escalade": 124, "escape-string-regexp": [ - 253, - 211, + 249, + 207, ], - "escodegen": 162, - "eslint": 242, - "eslint-config-next": 241, - "eslint-import-resolver-node": 382, - "eslint-import-resolver-typescript": 306, - "eslint-module-utils": 380, - "eslint-plugin-import": 307, - "eslint-plugin-jsx-a11y": 408, - "eslint-plugin-react": 433, - "eslint-plugin-react-hooks": 393, - "eslint-scope": 282, - "eslint-visitor-keys": 246, - "espree": 270, - "esprima": 161, - "esquery": 302, - "esrecurse": 283, - "estraverse": 165, - "esutils": 164, - "extract-zip": 180, - "fast-deep-equal": 278, - "fast-fifo": 140, + "escodegen": 163, + "eslint": 238, + "eslint-config-next": 237, + "eslint-import-resolver-node": 378, + "eslint-import-resolver-typescript": 302, + "eslint-module-utils": 376, + "eslint-plugin-import": 303, + "eslint-plugin-jsx-a11y": 404, + "eslint-plugin-react": 429, + "eslint-plugin-react-hooks": 389, + "eslint-scope": 278, + "eslint-visitor-keys": 242, + "espree": 266, + "esprima": 162, + "esquery": 298, + "esrecurse": 279, + "estraverse": 166, + "esutils": 165, + "extract-zip": 181, + "fast-deep-equal": 274, + "fast-fifo": 141, "fast-glob": 40, - "fast-json-stable-stringify": 277, - "fast-levenshtein": 287, + "fast-json-stable-stringify": 273, + "fast-levenshtein": 283, "fastq": 44, - "fd-slicer": 186, - "file-entry-cache": 254, + "fd-slicer": 187, + "file-entry-cache": 250, "fill-range": 35, - "find-up": 296, - "flat-cache": 255, - "flatted": 264, - "for-each": 332, + "find-up": 292, + "flat-cache": 251, + "flatted": 260, + "for-each": 328, "foreground-child": 91, - "fraction.js": 446, - "fs-extra": 171, - "fs.realpath": 261, + "fraction.js": 442, + "fs-extra": 172, + "fs.realpath": 257, "fsevents": 51, "function-bind": 21, - "function.prototype.name": 370, - "functions-have-names": 358, - "get-caller-file": 122, - "get-intrinsic": 321, - "get-stream": 188, - "get-symbol-description": 369, - "get-tsconfig": 389, - "get-uri": 170, + "function.prototype.name": 366, + "functions-have-names": 354, + "get-caller-file": 123, + "get-intrinsic": 317, + "get-stream": 189, + "get-symbol-description": 365, + "get-tsconfig": 385, + "get-uri": 171, "glob": [ 65, - 257, + 253, ], "glob-parent": [ 27, 42, ], - "globals": 268, - "globalthis": 368, - "globby": 400, - "gopd": 325, - "graceful-fs": 174, - "graphemer": 294, - "has-bigints": 343, + "globals": 264, + "globalthis": 364, + "globby": 396, + "gopd": 321, + "graceful-fs": 175, + "graphemer": 290, + "has-bigints": 339, "has-flag": [ - 305, - 210, + 301, + 206, ], - "has-property-descriptors": 319, - "has-proto": 323, - "has-symbols": 322, - "has-tostringtag": 331, + "has-property-descriptors": 315, + "has-proto": 319, + "has-symbols": 318, + "has-tostringtag": 327, "hasown": 20, - "http-proxy-agent": 169, - "https-proxy-agent": 168, - "ieee754": 128, - "ignore": 267, - "import-fresh": 218, - "imurmurhash": 284, - "inflight": 260, - "inherits": 259, - "internal-slot": 366, - "ip-address": 150, - "is-array-buffer": 365, - "is-arrayish": 205, - "is-async-function": 424, - "is-bigint": 342, + "http-proxy-agent": 170, + "https-proxy-agent": 169, + "ieee754": 129, + "ignore": 263, + "import-fresh": 214, + "imurmurhash": 280, + "inflight": 256, + "inherits": 255, + "internal-slot": 362, + "ip-address": 151, + "is-array-buffer": 361, + "is-arrayish": 201, + "is-async-function": 420, + "is-bigint": 338, "is-binary-path": 53, - "is-boolean-object": 341, - "is-callable": 333, + "is-boolean-object": 337, + "is-callable": 329, "is-core-module": 19, - "is-data-view": 364, - "is-date-object": 372, + "is-data-view": 360, + "is-date-object": 368, "is-extglob": 29, - "is-finalizationregistry": 423, + "is-finalizationregistry": 419, "is-fullwidth-code-point": 79, - "is-generator-function": 422, + "is-generator-function": 418, "is-glob": 28, - "is-map": 421, - "is-negative-zero": 363, + "is-map": 417, + "is-negative-zero": 359, "is-number": 37, - "is-number-object": 340, - "is-path-inside": 280, - "is-regex": 353, - "is-set": 420, - "is-shared-array-buffer": 362, - "is-string": 339, - "is-symbol": 338, - "is-typed-array": 345, - "is-weakmap": 419, - "is-weakref": 361, - "is-weakset": 418, - "isarray": 355, + "is-number-object": 336, + "is-path-inside": 276, + "is-regex": 349, + "is-set": 416, + "is-shared-array-buffer": 358, + "is-string": 335, + "is-symbol": 334, + "is-typed-array": 341, + "is-weakmap": 415, + "is-weakref": 357, + "is-weakset": 414, + "isarray": 351, "isexe": 95, - "iterator.prototype": 414, + "iterator.prototype": 410, "jackspeak": 72, "jiti": 105, "js-tokens": 111, - "js-yaml": 216, - "jsbn": 152, - "json-buffer": 263, - "json-parse-even-better-errors": 203, - "json-schema-traverse": 276, - "json-stable-stringify-without-jsonify": 243, - "json5": 311, - "jsonfile": 173, - "jsx-ast-utils": 412, - "keyv": 262, - "language-subtag-registry": 411, - "language-tags": 410, - "levn": 288, + "js-yaml": 212, + "jsbn": 153, + "json-buffer": 259, + "json-parse-even-better-errors": 199, + "json-schema-traverse": 272, + "json-stable-stringify-without-jsonify": 239, + "json5": 307, + "jsonfile": 174, + "jsx-ast-utils": 408, + "keyv": 258, + "language-subtag-registry": 407, + "language-tags": 406, + "levn": 284, "lilconfig": [ 11, 39, ], "lines-and-columns": 64, - "locate-path": 298, - "lodash.merge": 281, + "locate-path": 294, + "lodash.merge": 277, "loose-envify": 110, "lru-cache": [ 68, - 178, - 116, + 179, + 117, ], "merge2": 41, "micromatch": 32, "minimatch": [ 69, - 399, - 248, + 395, + 244, ], - "minimist": 310, + "minimist": 306, "minipass": 67, - "mitt": 200, - "ms": 154, + "mitt": 196, + "ms": 155, "mz": 59, "nanoid": 10, - "natural-compare": 279, - "netmask": 159, - "next": 223, - "node-fetch": 194, - "node-releases": 449, + "natural-compare": 275, + "netmask": 160, + "next": 219, + "node-releases": 445, "normalize-path": 25, - "normalize-range": 445, + "normalize-range": 441, "object-assign": 63, "object-hash": 26, - "object-inspect": 360, - "object-keys": 318, - "object.assign": 359, - "object.entries": 409, - "object.fromentries": 379, - "object.groupby": 328, - "object.hasown": 438, - "object.values": 314, - "once": 143, - "optionator": 286, - "p-limit": 300, - "p-locate": 299, - "pac-proxy-agent": 157, - "pac-resolver": 158, - "parent-module": 220, - "parse-json": 202, - "path-exists": 297, - "path-is-absolute": 258, + "object-inspect": 356, + "object-keys": 314, + "object.assign": 355, + "object.entries": 405, + "object.fromentries": 375, + "object.groupby": 324, + "object.hasown": 434, + "object.values": 310, + "once": 144, + "optionator": 282, + "p-limit": 296, + "p-locate": 295, + "pac-proxy-agent": 158, + "pac-resolver": 159, + "parent-module": 216, + "parse-json": 198, + "path-exists": 293, + "path-is-absolute": 254, "path-key": 98, "path-parse": 18, "path-scurry": 66, - "path-type": 403, - "pend": 187, + "path-type": 399, + "pend": 188, "picocolors": 9, "picomatch": 33, "pify": 23, "pirates": 58, - "possible-typed-array-names": 335, + "possible-typed-array-names": 331, "postcss": [ - 238, + 234, 7, ], "postcss-import": 15, @@ -34678,129 +34468,127 @@ exports[`next build works: node 1`] = ` "postcss-nested": 14, "postcss-selector-parser": 3, "postcss-value-parser": 24, - "prelude-ls": 290, - "progress": 179, - "prop-types": 436, - "proxy-agent": 146, - "proxy-from-env": 156, - "pump": 142, - "punycode": 275, + "prelude-ls": 286, + "progress": 180, + "prop-types": 432, + "proxy-agent": 147, + "proxy-from-env": 157, + "pump": 143, + "punycode": 271, "puppeteer": 113, - "puppeteer-core": 190, + "puppeteer-core": 191, "queue-microtask": 48, - "queue-tick": 139, + "queue-tick": 140, "react": 109, "react-dom": 108, - "react-is": 437, + "react-is": 433, "read-cache": 22, "readdirp": 52, - "reflect.getprototypeof": 415, - "regenerator-runtime": 432, - "regexp.prototype.flags": 356, - "require-directory": 121, + "reflect.getprototypeof": 411, + "regenerator-runtime": 428, + "regexp.prototype.flags": 352, + "require-directory": 122, "resolve": [ - 435, + 431, 16, ], - "resolve-from": 219, - "resolve-pkg-maps": 390, + "resolve-from": 215, + "resolve-pkg-maps": 386, "reusify": 45, - "rimraf": 256, + "rimraf": 252, "run-parallel": 47, - "safe-array-concat": 354, - "safe-regex-test": 352, + "safe-array-concat": 350, + "safe-regex-test": 348, "scheduler": 112, "semver": [ - 115, - 313, + 116, + 309, ], - "set-function-length": 327, - "set-function-name": 357, + "set-function-length": 323, + "set-function-name": 353, "shebang-command": 96, "shebang-regex": 97, - "side-channel": 367, + "side-channel": 363, "signal-exit": 92, - "slash": 401, - "smart-buffer": 149, - "socks": 148, - "socks-proxy-agent": 147, - "source-map": 163, + "slash": 397, + "smart-buffer": 150, + "socks": 149, + "socks-proxy-agent": 148, + "source-map": 164, "source-map-js": 8, - "sprintf-js": 151, - "streamsearch": 240, - "streamx": 135, + "sprintf-js": 152, + "streamsearch": 236, + "streamx": 136, "string-width": [ 87, 78, ], - "string.prototype.matchall": 434, - "string.prototype.trim": 351, - "string.prototype.trimend": 350, - "string.prototype.trimstart": 349, + "string.prototype.matchall": 430, + "string.prototype.trim": 347, + "string.prototype.trimend": 346, + "string.prototype.trimstart": 345, "strip-ansi": [ 85, 76, ], - "strip-bom": 309, - "strip-json-comments": 266, - "styled-jsx": 235, + "strip-bom": 305, + "strip-json-comments": 262, + "styled-jsx": 231, "sucrase": 56, "supports-color": [ - 304, - 209, + 300, + 205, ], "supports-preserve-symlinks-flag": 17, "tailwindcss": 2, - "tapable": 392, - "tar-fs": 130, - "tar-stream": 141, - "text-decoder": 137, - "text-table": 285, + "tapable": 388, + "tar-fs": 131, + "tar-stream": 142, + "text-decoder": 138, + "text-table": 281, "thenify": 61, "thenify-all": 60, - "through": 126, + "through": 127, "to-regex-range": 36, - "tr46": 197, - "ts-api-utils": 398, + "ts-api-utils": 394, "ts-interface-checker": 57, - "tsconfig-paths": 308, - "tslib": 167, - "type-check": 289, - "type-fest": 269, - "typed-array-buffer": 348, - "typed-array-byte-length": 347, - "typed-array-byte-offset": 346, - "typed-array-length": 344, + "tsconfig-paths": 304, + "tslib": 168, + "type-check": 285, + "type-fest": 265, + "typed-array-buffer": 344, + "typed-array-byte-length": 343, + "typed-array-byte-offset": 342, + "typed-array-length": 340, "typescript": 1, - "unbox-primitive": 336, - "unbzip2-stream": 125, - "undici-types": 183, - "universalify": 172, - "update-browserslist-db": 448, - "uri-js": 274, - "urlpattern-polyfill": 199, + "unbox-primitive": 332, + "unbzip2-stream": 126, + "undici-types": 184, + "universalify": 173, + "update-browserslist-db": 444, + "uri-js": 270, + "urlpattern-polyfill": 195, "util-deprecate": 4, - "webidl-conversions": 196, - "whatwg-url": 195, "which": 94, - "which-boxed-primitive": 337, - "which-builtin-type": 416, - "which-collection": 417, - "which-typed-array": 330, - "word-wrap": 291, + "which-boxed-primitive": 333, + "which-builtin-type": 412, + "which-collection": 413, + "which-typed-array": 326, + "word-wrap": 287, "wrap-ansi": [ 84, 75, ], - "wrappy": 144, - "ws": 191, - "y18n": 120, - "yallist": 117, + "wrappy": 145, + "ws": 192, + "y18n": 121, + "yallist": 118, "yaml": 12, - "yargs": 118, - "yargs-parser": 119, - "yauzl": 184, - "yocto-queue": 301, + "yargs": 119, + "yargs-parser": 120, + "yauzl": 185, + "yocto-queue": 297, + "zod": 194, }, "packages": [ { @@ -36867,17 +36655,34 @@ exports[`next build works: node 1`] = ` 153, 154, 155, + 156, ], "id": 113, - "integrity": "sha512-Mag1wRLanzwS4yEUyrDRBUgsKlH3dpL6oAfVwNHG09oxd0+ySsatMvYj7HwjynWy/S+Hg+XHLgjyC/F6CsL/lg==", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "man_dir": "", "name": "puppeteer", "name_hash": "13072297456933147981", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", + "tag": "npm", + "value": "22.12.0", + }, + "scripts": {}, + }, + { + "bin": null, + "dependencies": [], + "id": 114, + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==", + "man_dir": "", + "name": "devtools-protocol", + "name_hash": "12159960943916763407", + "origin": "npm", + "resolution": { + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", "tag": "npm", - "value": "22.4.1", + "value": "0.0.1299070", }, "scripts": {}, }, @@ -36887,7 +36692,6 @@ exports[`next build works: node 1`] = ` "name": "browsers", }, "dependencies": [ - 156, 157, 158, 159, @@ -36895,17 +36699,18 @@ exports[`next build works: node 1`] = ` 161, 162, 163, + 164, ], - "id": 114, - "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "id": 115, + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "man_dir": "", "name": "@puppeteer/browsers", "name_hash": "6318517029770692415", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", "tag": "npm", - "value": "2.1.0", + "value": "2.2.3", }, "scripts": {}, }, @@ -36915,9 +36720,9 @@ exports[`next build works: node 1`] = ` "name": "semver", }, "dependencies": [ - 164, + 165, ], - "id": 115, + "id": 116, "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "man_dir": "", "name": "semver", @@ -36933,9 +36738,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 165, + 166, ], - "id": 116, + "id": 117, "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "man_dir": "", "name": "lru-cache", @@ -36951,7 +36756,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 117, + "id": 118, "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "man_dir": "", "name": "yallist", @@ -36967,15 +36772,15 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 166, 167, 168, 169, 170, 171, 172, + 173, ], - "id": 118, + "id": 119, "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "man_dir": "", "name": "yargs", @@ -36991,7 +36796,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 119, + "id": 120, "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "man_dir": "", "name": "yargs-parser", @@ -37007,7 +36812,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 120, + "id": 121, "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "man_dir": "", "name": "y18n", @@ -37023,7 +36828,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 121, + "id": 122, "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "man_dir": "", "name": "require-directory", @@ -37039,7 +36844,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 122, + "id": 123, "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "man_dir": "", "name": "get-caller-file", @@ -37055,7 +36860,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 123, + "id": 124, "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "man_dir": "", "name": "escalade", @@ -37071,11 +36876,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 173, 174, 175, + 176, ], - "id": 124, + "id": 125, "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "man_dir": "", "name": "cliui", @@ -37091,10 +36896,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 176, 177, + 178, ], - "id": 125, + "id": 126, "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "man_dir": "", "name": "unbzip2-stream", @@ -37110,7 +36915,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 126, + "id": 127, "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "man_dir": "", "name": "through", @@ -37126,10 +36931,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 178, 179, + 180, ], - "id": 127, + "id": 128, "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "man_dir": "", "name": "buffer", @@ -37145,7 +36950,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 128, + "id": 129, "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "man_dir": "", "name": "ieee754", @@ -37161,7 +36966,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 129, + "id": 130, "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "man_dir": "", "name": "base64-js", @@ -37177,12 +36982,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 180, 181, 182, 183, + 184, ], - "id": 130, + "id": 131, "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "man_dir": "", "name": "tar-fs", @@ -37198,9 +37003,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 184, + 185, ], - "id": 131, + "id": 132, "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "man_dir": "", "name": "bare-path", @@ -37216,7 +37021,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 132, + "id": 133, "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", "man_dir": "", "name": "bare-os", @@ -37232,11 +37037,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 185, 186, 187, + 188, ], - "id": 133, + "id": 134, "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", "man_dir": "", "name": "bare-fs", @@ -37252,9 +37057,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 188, + 189, ], - "id": 134, + "id": 135, "integrity": "sha512-ubLyoDqPnUf5o0kSFp709HC0WRZuxVuh4pbte5eY95Xvx5bdvz07c2JFmXBfqqe60q+9PJ8S4X5GRvmcNSKMxg==", "man_dir": "", "name": "bare-stream", @@ -37270,12 +37075,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 189, 190, 191, 192, + 193, ], - "id": 135, + "id": 136, "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", "man_dir": "", "name": "streamx", @@ -37291,7 +37096,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 136, + "id": 137, "integrity": "sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA==", "man_dir": "", "name": "bare-events", @@ -37307,9 +37112,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 193, + 194, ], - "id": 137, + "id": 138, "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", "man_dir": "", "name": "text-decoder", @@ -37325,7 +37130,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 138, + "id": 139, "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "man_dir": "", "name": "b4a", @@ -37341,7 +37146,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 139, + "id": 140, "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "man_dir": "", "name": "queue-tick", @@ -37357,7 +37162,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 140, + "id": 141, "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "man_dir": "", "name": "fast-fifo", @@ -37373,11 +37178,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 194, 195, 196, + 197, ], - "id": 141, + "id": 142, "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "man_dir": "", "name": "tar-stream", @@ -37393,10 +37198,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 197, 198, + 199, ], - "id": 142, + "id": 143, "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "man_dir": "", "name": "pump", @@ -37412,9 +37217,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 199, + 200, ], - "id": 143, + "id": 144, "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "man_dir": "", "name": "once", @@ -37430,7 +37235,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 144, + "id": 145, "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "man_dir": "", "name": "wrappy", @@ -37446,9 +37251,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 200, + 201, ], - "id": 145, + "id": 146, "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "man_dir": "", "name": "end-of-stream", @@ -37464,7 +37269,6 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 201, 202, 203, 204, @@ -37472,8 +37276,9 @@ exports[`next build works: node 1`] = ` 206, 207, 208, + 209, ], - "id": 146, + "id": 147, "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "man_dir": "", "name": "proxy-agent", @@ -37489,11 +37294,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 209, 210, 211, + 212, ], - "id": 147, + "id": 148, "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "man_dir": "", "name": "socks-proxy-agent", @@ -37509,10 +37314,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 212, 213, + 214, ], - "id": 148, + "id": 149, "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "man_dir": "", "name": "socks", @@ -37528,7 +37333,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 149, + "id": 150, "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "man_dir": "", "name": "smart-buffer", @@ -37544,10 +37349,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 214, 215, + 216, ], - "id": 150, + "id": 151, "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "man_dir": "", "name": "ip-address", @@ -37563,7 +37368,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 151, + "id": 152, "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "man_dir": "", "name": "sprintf-js", @@ -37579,7 +37384,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 152, + "id": 153, "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "man_dir": "", "name": "jsbn", @@ -37595,9 +37400,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 216, + 217, ], - "id": 153, + "id": 154, "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "man_dir": "", "name": "debug", @@ -37613,7 +37418,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 154, + "id": 155, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "man_dir": "", "name": "ms", @@ -37629,9 +37434,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 217, + 218, ], - "id": 155, + "id": 156, "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "man_dir": "", "name": "agent-base", @@ -37647,7 +37452,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 156, + "id": 157, "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "man_dir": "", "name": "proxy-from-env", @@ -37663,7 +37468,6 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 218, 219, 220, 221, @@ -37671,8 +37475,9 @@ exports[`next build works: node 1`] = ` 223, 224, 225, + 226, ], - "id": 157, + "id": 158, "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "man_dir": "", "name": "pac-proxy-agent", @@ -37688,10 +37493,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 226, 227, + 228, ], - "id": 158, + "id": 159, "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "man_dir": "", "name": "pac-resolver", @@ -37707,7 +37512,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 159, + "id": 160, "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "man_dir": "", "name": "netmask", @@ -37723,11 +37528,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 228, 229, 230, + 231, ], - "id": 160, + "id": 161, "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "man_dir": "", "name": "degenerator", @@ -37746,7 +37551,7 @@ exports[`next build works: node 1`] = ` "esvalidate": "./bin/esvalidate.js", }, "dependencies": [], - "id": 161, + "id": 162, "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "man_dir": "", "name": "esprima", @@ -37765,12 +37570,12 @@ exports[`next build works: node 1`] = ` "esgenerate": "bin/esgenerate.js", }, "dependencies": [ - 231, 232, 233, 234, + 235, ], - "id": 162, + "id": 163, "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "man_dir": "", "name": "escodegen", @@ -37786,7 +37591,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 163, + "id": 164, "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "man_dir": "", "name": "source-map", @@ -37802,7 +37607,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 164, + "id": 165, "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "man_dir": "", "name": "esutils", @@ -37818,7 +37623,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 165, + "id": 166, "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "man_dir": "", "name": "estraverse", @@ -37834,9 +37639,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 235, + 236, ], - "id": 166, + "id": 167, "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "man_dir": "", "name": "ast-types", @@ -37852,7 +37657,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 167, + "id": 168, "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "man_dir": "", "name": "tslib", @@ -37868,10 +37673,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 236, 237, + 238, ], - "id": 168, + "id": 169, "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "man_dir": "", "name": "https-proxy-agent", @@ -37887,10 +37692,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 238, 239, + 240, ], - "id": 169, + "id": 170, "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "man_dir": "", "name": "http-proxy-agent", @@ -37906,12 +37711,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 240, 241, 242, 243, + 244, ], - "id": 170, + "id": 171, "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "man_dir": "", "name": "get-uri", @@ -37927,11 +37732,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 244, 245, 246, + 247, ], - "id": 171, + "id": 172, "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "man_dir": "", "name": "fs-extra", @@ -37947,7 +37752,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 172, + "id": 173, "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "man_dir": "", "name": "universalify", @@ -37963,10 +37768,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 247, 248, + 249, ], - "id": 173, + "id": 174, "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "man_dir": "", "name": "jsonfile", @@ -37982,7 +37787,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 174, + "id": 175, "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "man_dir": "", "name": "graceful-fs", @@ -37998,7 +37803,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 175, + "id": 176, "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "man_dir": "", "name": "data-uri-to-buffer", @@ -38014,7 +37819,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 176, + "id": 177, "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "man_dir": "", "name": "basic-ftp", @@ -38030,7 +37835,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 177, + "id": 178, "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "man_dir": "", "name": "@tootallnate/quickjs-emscripten", @@ -38046,7 +37851,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 178, + "id": 179, "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "man_dir": "", "name": "lru-cache", @@ -38062,7 +37867,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 179, + "id": 180, "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "man_dir": "", "name": "progress", @@ -38081,12 +37886,12 @@ exports[`next build works: node 1`] = ` "name": "extract-zip", }, "dependencies": [ - 249, 250, 251, 252, + 253, ], - "id": 180, + "id": 181, "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "man_dir": "", "name": "extract-zip", @@ -38102,9 +37907,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 253, + 254, ], - "id": 181, + "id": 182, "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "man_dir": "", "name": "@types/yauzl", @@ -38120,9 +37925,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 254, + 255, ], - "id": 182, + "id": 183, "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", "man_dir": "", "name": "@types/node", @@ -38138,7 +37943,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 183, + "id": 184, "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "man_dir": "", "name": "undici-types", @@ -38154,10 +37959,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 255, 256, + 257, ], - "id": 184, + "id": 185, "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "man_dir": "", "name": "yauzl", @@ -38173,7 +37978,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 185, + "id": 186, "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "man_dir": "", "name": "buffer-crc32", @@ -38189,9 +37994,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 257, + 258, ], - "id": 186, + "id": 187, "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "man_dir": "", "name": "fd-slicer", @@ -38207,7 +38012,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 187, + "id": 188, "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "man_dir": "", "name": "pend", @@ -38223,9 +38028,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 258, + 259, ], - "id": 188, + "id": 189, "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "man_dir": "", "name": "get-stream", @@ -38241,9 +38046,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 259, + 260, ], - "id": 189, + "id": 190, "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "man_dir": "", "name": "debug", @@ -38259,23 +38064,22 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 260, 261, 262, 263, 264, 265, ], - "id": 190, - "integrity": "sha512-l9nf8NcirYOHdID12CIMWyy7dqcJCVtgVS+YAiJuUJHg8+9yjgPiG2PcNhojIEEpCkvw3FxvnyITVfKVmkWpjA==", + "id": 191, + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", "man_dir": "", "name": "puppeteer-core", "name_hash": "10954685796294859150", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.4.1.tgz", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", "tag": "npm", - "value": "22.4.1", + "value": "22.12.0", }, "scripts": {}, }, @@ -38285,32 +38089,16 @@ exports[`next build works: node 1`] = ` 266, 267, ], - "id": 191, - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "id": 192, + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "man_dir": "", "name": "ws", "name_hash": "14644737011329074183", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "tag": "npm", - "value": "8.16.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 192, - "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", - "man_dir": "", - "name": "devtools-protocol", - "name_hash": "12159960943916763407", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "tag": "npm", - "value": "0.0.1249869", + "value": "8.17.1", }, "scripts": {}, }, @@ -38318,114 +38106,43 @@ exports[`next build works: node 1`] = ` "bin": null, "dependencies": [ 268, - ], - "id": 193, - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "man_dir": "", - "name": "cross-fetch", - "name_hash": "5665307032371542913", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "tag": "npm", - "value": "4.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 269, 270, - ], - "id": 194, - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "man_dir": "", - "name": "node-fetch", - "name_hash": "9368364337257117328", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "tag": "npm", - "value": "2.7.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ 271, - 272, ], - "id": 195, - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "man_dir": "", - "name": "whatwg-url", - "name_hash": "15436316526856444177", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "tag": "npm", - "value": "5.0.0", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [], - "id": 196, - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "id": 193, + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "man_dir": "", - "name": "webidl-conversions", - "name_hash": "5343883202058398372", + "name": "chromium-bidi", + "name_hash": "17738832193826713561", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", "tag": "npm", - "value": "3.0.1", + "value": "0.5.24", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 197, - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "man_dir": "", - "name": "tr46", - "name_hash": "4865213169840252474", - "origin": "npm", - "resolution": { - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "tag": "npm", - "value": "0.0.3", - }, - "scripts": {}, - }, - { - "bin": null, - "dependencies": [ - 273, - 274, - 275, - ], - "id": 198, - "integrity": "sha512-sZMgEBWKbupD0Q7lyFu8AWkrE+rs5ycE12jFkGwIgD/VS8lDPtelPlXM7LYaq4zrkZ/O2L3f4afHUHL0ICdKog==", + "id": 194, + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "man_dir": "", - "name": "chromium-bidi", - "name_hash": "17738832193826713561", + "name": "zod", + "name_hash": "13942938047053248045", "origin": "npm", "resolution": { - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.12.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "tag": "npm", - "value": "0.5.12", + "value": "3.23.8", }, "scripts": {}, }, { "bin": null, "dependencies": [], - "id": 199, + "id": 195, "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "man_dir": "", "name": "urlpattern-polyfill", @@ -38441,7 +38158,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 200, + "id": 196, "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "man_dir": "", "name": "mitt", @@ -38457,13 +38174,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 272, + 273, + 274, + 275, 276, - 277, - 278, - 279, - 280, ], - "id": 201, + "id": 197, "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "man_dir": "", "name": "cosmiconfig", @@ -38479,12 +38196,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 281, - 282, - 283, - 284, + 277, + 278, + 279, + 280, ], - "id": 202, + "id": 198, "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "man_dir": "", "name": "parse-json", @@ -38500,7 +38217,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 203, + "id": 199, "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "man_dir": "", "name": "json-parse-even-better-errors", @@ -38516,9 +38233,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 285, + 281, ], - "id": 204, + "id": 200, "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "man_dir": "", "name": "error-ex", @@ -38534,7 +38251,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 205, + "id": 201, "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "man_dir": "", "name": "is-arrayish", @@ -38550,10 +38267,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 286, - 287, + 282, + 283, ], - "id": 206, + "id": 202, "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "man_dir": "", "name": "@babel/code-frame", @@ -38569,12 +38286,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 288, - 289, - 290, - 291, + 284, + 285, + 286, + 287, ], - "id": 207, + "id": 203, "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "man_dir": "", "name": "@babel/highlight", @@ -38590,11 +38307,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 292, - 293, - 294, + 288, + 289, + 290, ], - "id": 208, + "id": 204, "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "man_dir": "", "name": "chalk", @@ -38610,9 +38327,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 295, + 291, ], - "id": 209, + "id": 205, "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "man_dir": "", "name": "supports-color", @@ -38628,7 +38345,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 210, + "id": 206, "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "man_dir": "", "name": "has-flag", @@ -38644,7 +38361,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 211, + "id": 207, "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "man_dir": "", "name": "escape-string-regexp", @@ -38660,9 +38377,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 296, + 292, ], - "id": 212, + "id": 208, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "man_dir": "", "name": "ansi-styles", @@ -38678,9 +38395,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 297, + 293, ], - "id": 213, + "id": 209, "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "man_dir": "", "name": "color-convert", @@ -38696,7 +38413,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 214, + "id": 210, "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "man_dir": "", "name": "color-name", @@ -38712,7 +38429,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 215, + "id": 211, "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "man_dir": "", "name": "@babel/helper-validator-identifier", @@ -38731,9 +38448,9 @@ exports[`next build works: node 1`] = ` "name": "js-yaml", }, "dependencies": [ - 298, + 294, ], - "id": 216, + "id": 212, "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "man_dir": "", "name": "js-yaml", @@ -38749,7 +38466,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 217, + "id": 213, "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "man_dir": "", "name": "argparse", @@ -38765,10 +38482,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 299, - 300, + 295, + 296, ], - "id": 218, + "id": 214, "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "man_dir": "", "name": "import-fresh", @@ -38784,7 +38501,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 219, + "id": 215, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "man_dir": "", "name": "resolve-from", @@ -38800,9 +38517,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 301, + 297, ], - "id": 220, + "id": 216, "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "man_dir": "", "name": "parent-module", @@ -38818,7 +38535,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 221, + "id": 217, "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "man_dir": "", "name": "callsites", @@ -38834,7 +38551,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 222, + "id": 218, "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "man_dir": "", "name": "env-paths", @@ -38853,6 +38570,10 @@ exports[`next build works: node 1`] = ` "name": "next", }, "dependencies": [ + 298, + 299, + 300, + 301, 302, 303, 304, @@ -38869,12 +38590,8 @@ exports[`next build works: node 1`] = ` 315, 316, 317, - 318, - 319, - 320, - 321, ], - "id": 223, + "id": 219, "integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==", "man_dir": "", "name": "next", @@ -38893,7 +38610,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 224, + "id": 220, "integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==", "man_dir": "", "name": "@next/swc-win32-arm64-msvc", @@ -38915,7 +38632,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 225, + "id": 221, "integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==", "man_dir": "", "name": "@next/swc-linux-arm64-musl", @@ -38937,7 +38654,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 226, + "id": 222, "integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==", "man_dir": "", "name": "@next/swc-win32-ia32-msvc", @@ -38959,7 +38676,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 227, + "id": 223, "integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==", "man_dir": "", "name": "@next/swc-linux-arm64-gnu", @@ -38981,7 +38698,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 228, + "id": 224, "integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==", "man_dir": "", "name": "@next/swc-win32-x64-msvc", @@ -39003,7 +38720,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 229, + "id": 225, "integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==", "man_dir": "", "name": "@next/swc-linux-x64-musl", @@ -39025,7 +38742,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 230, + "id": 226, "integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==", "man_dir": "", "name": "@next/swc-linux-x64-gnu", @@ -39047,7 +38764,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 231, + "id": 227, "integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==", "man_dir": "", "name": "@next/swc-darwin-arm64", @@ -39069,7 +38786,7 @@ exports[`next build works: node 1`] = ` ], "bin": null, "dependencies": [], - "id": 232, + "id": 228, "integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==", "man_dir": "", "name": "@next/swc-darwin-x64", @@ -39088,7 +38805,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 233, + "id": 229, "integrity": "sha512-S3BnR4Kh26TBxbi5t5kpbcUlLJb9lhtDXISDPwOfI+JoC+ik0QksvkZtUVyikw3hjnkgkMPSJ8oIM9yMm9vflA==", "man_dir": "", "name": "caniuse-lite", @@ -39104,9 +38821,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 322, + 318, ], - "id": 234, + "id": 230, "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "man_dir": "", "name": "@swc/helpers", @@ -39122,10 +38839,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 323, - 324, + 319, + 320, ], - "id": 235, + "id": 231, "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "man_dir": "", "name": "styled-jsx", @@ -39141,7 +38858,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 236, + "id": 232, "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "man_dir": "", "name": "client-only", @@ -39157,7 +38874,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 237, + "id": 233, "integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==", "man_dir": "", "name": "@next/env", @@ -39173,11 +38890,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 325, - 326, - 327, + 321, + 322, + 323, ], - "id": 238, + "id": 234, "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "man_dir": "", "name": "postcss", @@ -39193,9 +38910,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 328, + 324, ], - "id": 239, + "id": 235, "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "man_dir": "", "name": "busboy", @@ -39211,7 +38928,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 240, + "id": 236, "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "man_dir": "", "name": "streamsearch", @@ -39227,6 +38944,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 325, + 326, + 327, + 328, 329, 330, 331, @@ -39234,12 +38955,8 @@ exports[`next build works: node 1`] = ` 333, 334, 335, - 336, - 337, - 338, - 339, ], - "id": 241, + "id": 237, "integrity": "sha512-sUCpWlGuHpEhI0pIT0UtdSLJk5Z8E2DYinPTwsBiWaSYQomchdl0i60pjynY48+oXvtyWMQ7oE+G3m49yrfacg==", "man_dir": "", "name": "eslint-config-next", @@ -39258,6 +38975,10 @@ exports[`next build works: node 1`] = ` "name": "eslint", }, "dependencies": [ + 336, + 337, + 338, + 339, 340, 341, 342, @@ -39291,12 +39012,8 @@ exports[`next build works: node 1`] = ` 370, 371, 372, - 373, - 374, - 375, - 376, ], - "id": 242, + "id": 238, "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "man_dir": "", "name": "eslint", @@ -39312,7 +39029,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 243, + "id": 239, "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "man_dir": "", "name": "json-stable-stringify-without-jsonify", @@ -39328,7 +39045,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 244, + "id": 240, "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "man_dir": "", "name": "@humanwhocodes/module-importer", @@ -39344,10 +39061,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 377, - 378, + 373, + 374, ], - "id": 245, + "id": 241, "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "man_dir": "", "name": "@eslint-community/eslint-utils", @@ -39363,7 +39080,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 246, + "id": 242, "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "man_dir": "", "name": "eslint-visitor-keys", @@ -39379,11 +39096,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 379, - 380, - 381, + 375, + 376, + 377, ], - "id": 247, + "id": 243, "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "man_dir": "", "name": "@humanwhocodes/config-array", @@ -39399,9 +39116,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 382, + 378, ], - "id": 248, + "id": 244, "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "man_dir": "", "name": "minimatch", @@ -39417,10 +39134,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 383, - 384, + 379, + 380, ], - "id": 249, + "id": 245, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "man_dir": "", "name": "brace-expansion", @@ -39436,7 +39153,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 250, + "id": 246, "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "man_dir": "", "name": "concat-map", @@ -39452,7 +39169,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 251, + "id": 247, "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "man_dir": "", "name": "@humanwhocodes/object-schema", @@ -39468,7 +39185,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 252, + "id": 248, "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "man_dir": "", "name": "@eslint-community/regexpp", @@ -39484,7 +39201,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 253, + "id": 249, "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "man_dir": "", "name": "escape-string-regexp", @@ -39500,9 +39217,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 385, + 381, ], - "id": 254, + "id": 250, "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "man_dir": "", "name": "file-entry-cache", @@ -39518,11 +39235,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 386, - 387, - 388, + 382, + 383, + 384, ], - "id": 255, + "id": 251, "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "man_dir": "", "name": "flat-cache", @@ -39541,9 +39258,9 @@ exports[`next build works: node 1`] = ` "name": "rimraf", }, "dependencies": [ - 389, + 385, ], - "id": 256, + "id": 252, "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "man_dir": "", "name": "rimraf", @@ -39559,14 +39276,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 386, + 387, + 388, + 389, 390, 391, - 392, - 393, - 394, - 395, ], - "id": 257, + "id": 253, "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "man_dir": "", "name": "glob", @@ -39582,7 +39299,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 258, + "id": 254, "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "man_dir": "", "name": "path-is-absolute", @@ -39598,7 +39315,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 259, + "id": 255, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "man_dir": "", "name": "inherits", @@ -39614,10 +39331,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 396, - 397, + 392, + 393, ], - "id": 260, + "id": 256, "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "man_dir": "", "name": "inflight", @@ -39633,7 +39350,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 261, + "id": 257, "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "man_dir": "", "name": "fs.realpath", @@ -39649,9 +39366,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 398, + 394, ], - "id": 262, + "id": 258, "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "man_dir": "", "name": "keyv", @@ -39667,7 +39384,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 263, + "id": 259, "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "man_dir": "", "name": "json-buffer", @@ -39683,7 +39400,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 264, + "id": 260, "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "man_dir": "", "name": "flatted", @@ -39699,17 +39416,17 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 395, + 396, + 397, + 398, 399, 400, 401, 402, 403, - 404, - 405, - 406, - 407, ], - "id": 265, + "id": 261, "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "man_dir": "", "name": "@eslint/eslintrc", @@ -39725,7 +39442,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 266, + "id": 262, "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "man_dir": "", "name": "strip-json-comments", @@ -39741,7 +39458,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 267, + "id": 263, "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "man_dir": "", "name": "ignore", @@ -39757,9 +39474,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 408, + 404, ], - "id": 268, + "id": 264, "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "man_dir": "", "name": "globals", @@ -39775,7 +39492,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 269, + "id": 265, "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "man_dir": "", "name": "type-fest", @@ -39791,11 +39508,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 409, - 410, - 411, + 405, + 406, + 407, ], - "id": 270, + "id": 266, "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "man_dir": "", "name": "espree", @@ -39811,9 +39528,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 412, + 408, ], - "id": 271, + "id": 267, "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "man_dir": "", "name": "acorn-jsx", @@ -39832,7 +39549,7 @@ exports[`next build works: node 1`] = ` "name": "acorn", }, "dependencies": [], - "id": 272, + "id": 268, "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "man_dir": "", "name": "acorn", @@ -39848,12 +39565,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 413, - 414, - 415, - 416, + 409, + 410, + 411, + 412, ], - "id": 273, + "id": 269, "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "man_dir": "", "name": "ajv", @@ -39869,9 +39586,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 417, + 413, ], - "id": 274, + "id": 270, "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "man_dir": "", "name": "uri-js", @@ -39887,7 +39604,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 275, + "id": 271, "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "man_dir": "", "name": "punycode", @@ -39903,7 +39620,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 276, + "id": 272, "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "man_dir": "", "name": "json-schema-traverse", @@ -39919,7 +39636,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 277, + "id": 273, "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "man_dir": "", "name": "fast-json-stable-stringify", @@ -39935,7 +39652,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 278, + "id": 274, "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "man_dir": "", "name": "fast-deep-equal", @@ -39951,7 +39668,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 279, + "id": 275, "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "man_dir": "", "name": "natural-compare", @@ -39967,7 +39684,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 280, + "id": 276, "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "man_dir": "", "name": "is-path-inside", @@ -39983,7 +39700,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 281, + "id": 277, "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "man_dir": "", "name": "lodash.merge", @@ -39999,10 +39716,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 418, - 419, + 414, + 415, ], - "id": 282, + "id": 278, "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "man_dir": "", "name": "eslint-scope", @@ -40018,9 +39735,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 420, + 416, ], - "id": 283, + "id": 279, "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "man_dir": "", "name": "esrecurse", @@ -40036,7 +39753,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 284, + "id": 280, "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "man_dir": "", "name": "imurmurhash", @@ -40052,7 +39769,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 285, + "id": 281, "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "man_dir": "", "name": "text-table", @@ -40068,14 +39785,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 417, + 418, + 419, + 420, 421, 422, - 423, - 424, - 425, - 426, ], - "id": 286, + "id": 282, "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "man_dir": "", "name": "optionator", @@ -40091,7 +39808,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 287, + "id": 283, "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "man_dir": "", "name": "fast-levenshtein", @@ -40107,10 +39824,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 427, - 428, + 423, + 424, ], - "id": 288, + "id": 284, "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "man_dir": "", "name": "levn", @@ -40126,9 +39843,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 429, + 425, ], - "id": 289, + "id": 285, "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "man_dir": "", "name": "type-check", @@ -40144,7 +39861,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 290, + "id": 286, "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "man_dir": "", "name": "prelude-ls", @@ -40160,7 +39877,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 291, + "id": 287, "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "man_dir": "", "name": "word-wrap", @@ -40176,7 +39893,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 292, + "id": 288, "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "man_dir": "", "name": "deep-is", @@ -40192,7 +39909,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 293, + "id": 289, "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "man_dir": "", "name": "@eslint/js", @@ -40208,7 +39925,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 294, + "id": 290, "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "man_dir": "", "name": "graphemer", @@ -40224,9 +39941,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 430, + 426, ], - "id": 295, + "id": 291, "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "man_dir": "", "name": "doctrine", @@ -40242,10 +39959,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 431, - 432, + 427, + 428, ], - "id": 296, + "id": 292, "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "man_dir": "", "name": "find-up", @@ -40261,7 +39978,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 297, + "id": 293, "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "man_dir": "", "name": "path-exists", @@ -40277,9 +39994,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 433, + 429, ], - "id": 298, + "id": 294, "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "man_dir": "", "name": "locate-path", @@ -40295,9 +40012,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 434, + 430, ], - "id": 299, + "id": 295, "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "man_dir": "", "name": "p-locate", @@ -40313,9 +40030,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 435, + 431, ], - "id": 300, + "id": 296, "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "man_dir": "", "name": "p-limit", @@ -40331,7 +40048,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 301, + "id": 297, "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "man_dir": "", "name": "yocto-queue", @@ -40347,9 +40064,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 436, + 432, ], - "id": 302, + "id": 298, "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "man_dir": "", "name": "esquery", @@ -40365,10 +40082,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 437, - 438, + 433, + 434, ], - "id": 303, + "id": 299, "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "man_dir": "", "name": "chalk", @@ -40384,9 +40101,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 439, + 435, ], - "id": 304, + "id": 300, "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "man_dir": "", "name": "supports-color", @@ -40402,7 +40119,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 305, + "id": 301, "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "man_dir": "", "name": "has-flag", @@ -40418,17 +40135,17 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 436, + 437, + 438, + 439, 440, 441, 442, 443, 444, - 445, - 446, - 447, - 448, ], - "id": 306, + "id": 302, "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", "man_dir": "", "name": "eslint-import-resolver-typescript", @@ -40444,6 +40161,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 445, + 446, + 447, + 448, 449, 450, 451, @@ -40458,12 +40179,8 @@ exports[`next build works: node 1`] = ` 460, 461, 462, - 463, - 464, - 465, - 466, ], - "id": 307, + "id": 303, "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "man_dir": "", "name": "eslint-plugin-import", @@ -40479,12 +40196,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 467, - 468, - 469, - 470, + 463, + 464, + 465, + 466, ], - "id": 308, + "id": 304, "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "man_dir": "", "name": "tsconfig-paths", @@ -40500,7 +40217,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 309, + "id": 305, "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "man_dir": "", "name": "strip-bom", @@ -40516,7 +40233,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 310, + "id": 306, "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "man_dir": "", "name": "minimist", @@ -40535,9 +40252,9 @@ exports[`next build works: node 1`] = ` "name": "json5", }, "dependencies": [ - 471, + 467, ], - "id": 311, + "id": 307, "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "man_dir": "", "name": "json5", @@ -40553,7 +40270,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 312, + "id": 308, "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "man_dir": "", "name": "@types/json5", @@ -40572,7 +40289,7 @@ exports[`next build works: node 1`] = ` "name": "semver", }, "dependencies": [], - "id": 313, + "id": 309, "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "man_dir": "", "name": "semver", @@ -40588,11 +40305,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 472, - 473, - 474, + 468, + 469, + 470, ], - "id": 314, + "id": 310, "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "man_dir": "", "name": "object.values", @@ -40608,9 +40325,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 475, + 471, ], - "id": 315, + "id": 311, "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "man_dir": "", "name": "es-object-atoms", @@ -40626,7 +40343,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 316, + "id": 312, "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "man_dir": "", "name": "es-errors", @@ -40642,11 +40359,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 476, - 477, - 478, + 472, + 473, + 474, ], - "id": 317, + "id": 313, "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "man_dir": "", "name": "define-properties", @@ -40662,7 +40379,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 318, + "id": 314, "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "man_dir": "", "name": "object-keys", @@ -40678,9 +40395,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 479, + 475, ], - "id": 319, + "id": 315, "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "man_dir": "", "name": "has-property-descriptors", @@ -40696,9 +40413,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 480, + 476, ], - "id": 320, + "id": 316, "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "man_dir": "", "name": "es-define-property", @@ -40714,13 +40431,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 477, + 478, + 479, + 480, 481, - 482, - 483, - 484, - 485, ], - "id": 321, + "id": 317, "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "man_dir": "", "name": "get-intrinsic", @@ -40736,7 +40453,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 322, + "id": 318, "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "man_dir": "", "name": "has-symbols", @@ -40752,7 +40469,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 323, + "id": 319, "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "man_dir": "", "name": "has-proto", @@ -40768,11 +40485,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 486, - 487, - 488, + 482, + 483, + 484, ], - "id": 324, + "id": 320, "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "man_dir": "", "name": "define-data-property", @@ -40788,9 +40505,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 489, + 485, ], - "id": 325, + "id": 321, "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "man_dir": "", "name": "gopd", @@ -40806,13 +40523,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 486, + 487, + 488, + 489, 490, - 491, - 492, - 493, - 494, ], - "id": 326, + "id": 322, "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "man_dir": "", "name": "call-bind", @@ -40828,14 +40545,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 491, + 492, + 493, + 494, 495, 496, - 497, - 498, - 499, - 500, ], - "id": 327, + "id": 323, "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "man_dir": "", "name": "set-function-length", @@ -40851,11 +40568,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 501, - 502, - 503, + 497, + 498, + 499, ], - "id": 328, + "id": 324, "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "man_dir": "", "name": "object.groupby", @@ -40871,6 +40588,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 500, + 501, + 502, + 503, 504, 505, 506, @@ -40913,12 +40634,8 @@ exports[`next build works: node 1`] = ` 543, 544, 545, - 546, - 547, - 548, - 549, ], - "id": 329, + "id": 325, "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "man_dir": "", "name": "es-abstract", @@ -40934,13 +40651,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 546, + 547, + 548, + 549, 550, - 551, - 552, - 553, - 554, ], - "id": 330, + "id": 326, "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "man_dir": "", "name": "which-typed-array", @@ -40956,9 +40673,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 555, + 551, ], - "id": 331, + "id": 327, "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "man_dir": "", "name": "has-tostringtag", @@ -40974,9 +40691,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 556, + 552, ], - "id": 332, + "id": 328, "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "man_dir": "", "name": "for-each", @@ -40992,7 +40709,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 333, + "id": 329, "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "man_dir": "", "name": "is-callable", @@ -41008,9 +40725,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 557, + 553, ], - "id": 334, + "id": 330, "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "man_dir": "", "name": "available-typed-arrays", @@ -41026,7 +40743,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 335, + "id": 331, "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "man_dir": "", "name": "possible-typed-array-names", @@ -41042,12 +40759,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 558, - 559, - 560, - 561, + 554, + 555, + 556, + 557, ], - "id": 336, + "id": 332, "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "man_dir": "", "name": "unbox-primitive", @@ -41063,13 +40780,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 558, + 559, + 560, + 561, 562, - 563, - 564, - 565, - 566, ], - "id": 337, + "id": 333, "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "man_dir": "", "name": "which-boxed-primitive", @@ -41085,9 +40802,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 567, + 563, ], - "id": 338, + "id": 334, "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "man_dir": "", "name": "is-symbol", @@ -41103,9 +40820,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 568, + 564, ], - "id": 339, + "id": 335, "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "man_dir": "", "name": "is-string", @@ -41121,9 +40838,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 569, + 565, ], - "id": 340, + "id": 336, "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "man_dir": "", "name": "is-number-object", @@ -41139,10 +40856,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 570, - 571, + 566, + 567, ], - "id": 341, + "id": 337, "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "man_dir": "", "name": "is-boolean-object", @@ -41158,9 +40875,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 572, + 568, ], - "id": 342, + "id": 338, "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "man_dir": "", "name": "is-bigint", @@ -41176,7 +40893,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 343, + "id": 339, "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "man_dir": "", "name": "has-bigints", @@ -41192,14 +40909,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 569, + 570, + 571, + 572, 573, 574, - 575, - 576, - 577, - 578, ], - "id": 344, + "id": 340, "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "man_dir": "", "name": "typed-array-length", @@ -41215,9 +40932,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 579, + 575, ], - "id": 345, + "id": 341, "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "man_dir": "", "name": "is-typed-array", @@ -41233,14 +40950,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 576, + 577, + 578, + 579, 580, 581, - 582, - 583, - 584, - 585, ], - "id": 346, + "id": 342, "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "man_dir": "", "name": "typed-array-byte-offset", @@ -41256,13 +40973,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 582, + 583, + 584, + 585, 586, - 587, - 588, - 589, - 590, ], - "id": 347, + "id": 343, "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "man_dir": "", "name": "typed-array-byte-length", @@ -41278,11 +40995,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 591, - 592, - 593, + 587, + 588, + 589, ], - "id": 348, + "id": 344, "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "man_dir": "", "name": "typed-array-buffer", @@ -41298,11 +41015,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 594, - 595, - 596, + 590, + 591, + 592, ], - "id": 349, + "id": 345, "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "man_dir": "", "name": "string.prototype.trimstart", @@ -41318,11 +41035,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 597, - 598, - 599, + 593, + 594, + 595, ], - "id": 350, + "id": 346, "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "man_dir": "", "name": "string.prototype.trimend", @@ -41338,12 +41055,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 600, - 601, - 602, - 603, + 596, + 597, + 598, + 599, ], - "id": 351, + "id": 347, "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "man_dir": "", "name": "string.prototype.trim", @@ -41359,11 +41076,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 604, - 605, - 606, + 600, + 601, + 602, ], - "id": 352, + "id": 348, "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "man_dir": "", "name": "safe-regex-test", @@ -41379,10 +41096,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 607, - 608, + 603, + 604, ], - "id": 353, + "id": 349, "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "man_dir": "", "name": "is-regex", @@ -41398,12 +41115,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 609, - 610, - 611, - 612, + 605, + 606, + 607, + 608, ], - "id": 354, + "id": 350, "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "man_dir": "", "name": "safe-array-concat", @@ -41419,7 +41136,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 355, + "id": 351, "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "man_dir": "", "name": "isarray", @@ -41435,12 +41152,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 613, - 614, - 615, - 616, + 609, + 610, + 611, + 612, ], - "id": 356, + "id": 352, "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "man_dir": "", "name": "regexp.prototype.flags", @@ -41456,12 +41173,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 617, - 618, - 619, - 620, + 613, + 614, + 615, + 616, ], - "id": 357, + "id": 353, "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "man_dir": "", "name": "set-function-name", @@ -41477,7 +41194,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 358, + "id": 354, "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "man_dir": "", "name": "functions-have-names", @@ -41493,12 +41210,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 621, - 622, - 623, - 624, + 617, + 618, + 619, + 620, ], - "id": 359, + "id": 355, "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "man_dir": "", "name": "object.assign", @@ -41514,7 +41231,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 360, + "id": 356, "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "man_dir": "", "name": "object-inspect", @@ -41530,9 +41247,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 625, + 621, ], - "id": 361, + "id": 357, "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "man_dir": "", "name": "is-weakref", @@ -41548,9 +41265,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 626, + 622, ], - "id": 362, + "id": 358, "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "man_dir": "", "name": "is-shared-array-buffer", @@ -41566,7 +41283,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 363, + "id": 359, "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "man_dir": "", "name": "is-negative-zero", @@ -41582,9 +41299,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 627, + 623, ], - "id": 364, + "id": 360, "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "man_dir": "", "name": "is-data-view", @@ -41600,10 +41317,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 628, - 629, + 624, + 625, ], - "id": 365, + "id": 361, "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "man_dir": "", "name": "is-array-buffer", @@ -41619,11 +41336,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 630, - 631, - 632, + 626, + 627, + 628, ], - "id": 366, + "id": 362, "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "man_dir": "", "name": "internal-slot", @@ -41639,12 +41356,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 633, - 634, - 635, - 636, + 629, + 630, + 631, + 632, ], - "id": 367, + "id": 363, "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "man_dir": "", "name": "side-channel", @@ -41660,10 +41377,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 637, - 638, + 633, + 634, ], - "id": 368, + "id": 364, "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "man_dir": "", "name": "globalthis", @@ -41679,11 +41396,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 639, - 640, - 641, + 635, + 636, + 637, ], - "id": 369, + "id": 365, "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "man_dir": "", "name": "get-symbol-description", @@ -41699,12 +41416,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 642, - 643, - 644, - 645, + 638, + 639, + 640, + 641, ], - "id": 370, + "id": 366, "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "man_dir": "", "name": "function.prototype.name", @@ -41720,11 +41437,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 646, - 647, - 648, + 642, + 643, + 644, ], - "id": 371, + "id": 367, "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "man_dir": "", "name": "es-to-primitive", @@ -41740,9 +41457,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 649, + 645, ], - "id": 372, + "id": 368, "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "man_dir": "", "name": "is-date-object", @@ -41758,11 +41475,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 650, - 651, - 652, + 646, + 647, + 648, ], - "id": 373, + "id": 369, "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "man_dir": "", "name": "es-set-tostringtag", @@ -41778,11 +41495,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 653, - 654, - 655, + 649, + 650, + 651, ], - "id": 374, + "id": 370, "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "man_dir": "", "name": "data-view-byte-offset", @@ -41798,11 +41515,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 656, - 657, - 658, + 652, + 653, + 654, ], - "id": 375, + "id": 371, "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "man_dir": "", "name": "data-view-byte-length", @@ -41818,11 +41535,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 659, - 660, - 661, + 655, + 656, + 657, ], - "id": 376, + "id": 372, "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "man_dir": "", "name": "data-view-buffer", @@ -41838,16 +41555,16 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 658, + 659, + 660, + 661, 662, 663, 664, 665, - 666, - 667, - 668, - 669, ], - "id": 377, + "id": 373, "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "man_dir": "", "name": "arraybuffer.prototype.slice", @@ -41863,10 +41580,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 670, - 671, + 666, + 667, ], - "id": 378, + "id": 374, "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "man_dir": "", "name": "array-buffer-byte-length", @@ -41882,12 +41599,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 672, - 673, - 674, - 675, + 668, + 669, + 670, + 671, ], - "id": 379, + "id": 375, "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "man_dir": "", "name": "object.fromentries", @@ -41903,9 +41620,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 676, + 672, ], - "id": 380, + "id": 376, "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "man_dir": "", "name": "eslint-module-utils", @@ -41921,9 +41638,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 677, + 673, ], - "id": 381, + "id": 377, "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "man_dir": "", "name": "debug", @@ -41939,11 +41656,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 678, - 679, - 680, + 674, + 675, + 676, ], - "id": 382, + "id": 378, "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "man_dir": "", "name": "eslint-import-resolver-node", @@ -41959,9 +41676,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 681, + 677, ], - "id": 383, + "id": 379, "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "man_dir": "", "name": "doctrine", @@ -41977,12 +41694,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 682, - 683, - 684, - 685, + 678, + 679, + 680, + 681, ], - "id": 384, + "id": 380, "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "man_dir": "", "name": "array.prototype.flatmap", @@ -41998,9 +41715,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 686, + 682, ], - "id": 385, + "id": 381, "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "man_dir": "", "name": "es-shim-unscopables", @@ -42016,12 +41733,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 687, - 688, - 689, - 690, + 683, + 684, + 685, + 686, ], - "id": 386, + "id": 382, "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "man_dir": "", "name": "array.prototype.flat", @@ -42037,14 +41754,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 687, + 688, + 689, + 690, 691, 692, - 693, - 694, - 695, - 696, ], - "id": 387, + "id": 383, "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "man_dir": "", "name": "array.prototype.findlastindex", @@ -42060,14 +41777,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 693, + 694, + 695, + 696, 697, 698, - 699, - 700, - 701, - 702, ], - "id": 388, + "id": 384, "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "man_dir": "", "name": "array-includes", @@ -42083,9 +41800,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 703, + 699, ], - "id": 389, + "id": 385, "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "man_dir": "", "name": "get-tsconfig", @@ -42101,7 +41818,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 390, + "id": 386, "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "man_dir": "", "name": "resolve-pkg-maps", @@ -42117,10 +41834,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 704, - 705, + 700, + 701, ], - "id": 391, + "id": 387, "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "man_dir": "", "name": "enhanced-resolve", @@ -42136,7 +41853,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 392, + "id": 388, "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "man_dir": "", "name": "tapable", @@ -42152,9 +41869,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 706, + 702, ], - "id": 393, + "id": 389, "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "man_dir": "", "name": "eslint-plugin-react-hooks", @@ -42170,14 +41887,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 703, + 704, + 705, + 706, 707, 708, - 709, - 710, - 711, - 712, ], - "id": 394, + "id": 390, "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "man_dir": "", "name": "@typescript-eslint/parser", @@ -42193,16 +41910,16 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 709, + 710, + 711, + 712, 713, 714, 715, 716, - 717, - 718, - 719, - 720, ], - "id": 395, + "id": 391, "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "man_dir": "", "name": "@typescript-eslint/typescript-estree", @@ -42218,10 +41935,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 721, - 722, + 717, + 718, ], - "id": 396, + "id": 392, "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "man_dir": "", "name": "@typescript-eslint/visitor-keys", @@ -42237,7 +41954,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 397, + "id": 393, "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "man_dir": "", "name": "@typescript-eslint/types", @@ -42253,9 +41970,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 723, + 719, ], - "id": 398, + "id": 394, "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "man_dir": "", "name": "ts-api-utils", @@ -42271,9 +41988,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 724, + 720, ], - "id": 399, + "id": 395, "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "man_dir": "", "name": "minimatch", @@ -42289,14 +42006,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 721, + 722, + 723, + 724, 725, 726, - 727, - 728, - 729, - 730, ], - "id": 400, + "id": 396, "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "man_dir": "", "name": "globby", @@ -42312,7 +42029,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 401, + "id": 397, "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "man_dir": "", "name": "slash", @@ -42328,9 +42045,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 731, + 727, ], - "id": 402, + "id": 398, "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "man_dir": "", "name": "dir-glob", @@ -42346,7 +42063,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 403, + "id": 399, "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "man_dir": "", "name": "path-type", @@ -42362,7 +42079,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 404, + "id": 400, "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "man_dir": "", "name": "array-union", @@ -42378,10 +42095,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 732, - 733, + 728, + 729, ], - "id": 405, + "id": 401, "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "man_dir": "", "name": "@typescript-eslint/scope-manager", @@ -42397,9 +42114,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 734, + 730, ], - "id": 406, + "id": 402, "integrity": "sha512-VCnZI2cy77Yaj3L7Uhs3+44ikMM1VD/fBMwvTBb3hIaTIuqa+DmG4dhUDq+MASu3yx97KhgsVJbsas0XuiKyww==", "man_dir": "", "name": "@next/eslint-plugin-next", @@ -42415,7 +42132,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 407, + "id": 403, "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "man_dir": "", "name": "@rushstack/eslint-patch", @@ -42431,6 +42148,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 731, + 732, + 733, + 734, 735, 736, 737, @@ -42444,12 +42165,8 @@ exports[`next build works: node 1`] = ` 745, 746, 747, - 748, - 749, - 750, - 751, ], - "id": 408, + "id": 404, "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", "man_dir": "", "name": "eslint-plugin-jsx-a11y", @@ -42465,11 +42182,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 752, - 753, - 754, + 748, + 749, + 750, ], - "id": 409, + "id": 405, "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "man_dir": "", "name": "object.entries", @@ -42485,9 +42202,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 755, + 751, ], - "id": 410, + "id": 406, "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "man_dir": "", "name": "language-tags", @@ -42503,7 +42220,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 411, + "id": 407, "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "man_dir": "", "name": "language-subtag-registry", @@ -42519,12 +42236,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 756, - 757, - 758, - 759, + 752, + 753, + 754, + 755, ], - "id": 412, + "id": 408, "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "man_dir": "", "name": "jsx-ast-utils", @@ -42540,6 +42257,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 756, + 757, + 758, + 759, 760, 761, 762, @@ -42550,12 +42271,8 @@ exports[`next build works: node 1`] = ` 767, 768, 769, - 770, - 771, - 772, - 773, ], - "id": 413, + "id": 409, "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "man_dir": "", "name": "es-iterator-helpers", @@ -42571,13 +42288,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 770, + 771, + 772, + 773, 774, - 775, - 776, - 777, - 778, ], - "id": 414, + "id": 410, "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "man_dir": "", "name": "iterator.prototype", @@ -42593,15 +42310,15 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 775, + 776, + 777, + 778, 779, 780, 781, - 782, - 783, - 784, - 785, ], - "id": 415, + "id": 411, "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "man_dir": "", "name": "reflect.getprototypeof", @@ -42617,6 +42334,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 782, + 783, + 784, + 785, 786, 787, 788, @@ -42625,12 +42346,8 @@ exports[`next build works: node 1`] = ` 791, 792, 793, - 794, - 795, - 796, - 797, ], - "id": 416, + "id": 412, "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", "man_dir": "", "name": "which-builtin-type", @@ -42646,12 +42363,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 798, - 799, - 800, - 801, + 794, + 795, + 796, + 797, ], - "id": 417, + "id": 413, "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "man_dir": "", "name": "which-collection", @@ -42667,10 +42384,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 802, - 803, + 798, + 799, ], - "id": 418, + "id": 414, "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "man_dir": "", "name": "is-weakset", @@ -42686,7 +42403,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 419, + "id": 415, "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "man_dir": "", "name": "is-weakmap", @@ -42702,7 +42419,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 420, + "id": 416, "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "man_dir": "", "name": "is-set", @@ -42718,7 +42435,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 421, + "id": 417, "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "man_dir": "", "name": "is-map", @@ -42734,9 +42451,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 804, + 800, ], - "id": 422, + "id": 418, "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "man_dir": "", "name": "is-generator-function", @@ -42752,9 +42469,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 805, + 801, ], - "id": 423, + "id": 419, "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "man_dir": "", "name": "is-finalizationregistry", @@ -42770,9 +42487,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 806, + 802, ], - "id": 424, + "id": 420, "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "man_dir": "", "name": "is-async-function", @@ -42788,7 +42505,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 425, + "id": 421, "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "man_dir": "", "name": "damerau-levenshtein", @@ -42804,9 +42521,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 807, + 803, ], - "id": 426, + "id": 422, "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "man_dir": "", "name": "axobject-query", @@ -42822,7 +42539,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 427, + "id": 423, "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "man_dir": "", "name": "dequal", @@ -42838,7 +42555,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 428, + "id": 424, "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "man_dir": "", "name": "axe-core", @@ -42854,7 +42571,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 429, + "id": 425, "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "man_dir": "", "name": "ast-types-flow", @@ -42870,9 +42587,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 808, + 804, ], - "id": 430, + "id": 426, "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "man_dir": "", "name": "aria-query", @@ -42888,9 +42605,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 809, + 805, ], - "id": 431, + "id": 427, "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "man_dir": "", "name": "@babel/runtime", @@ -42906,7 +42623,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 432, + "id": 428, "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "man_dir": "", "name": "regenerator-runtime", @@ -42922,6 +42639,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 806, + 807, + 808, + 809, 810, 811, 812, @@ -42937,12 +42658,8 @@ exports[`next build works: node 1`] = ` 822, 823, 824, - 825, - 826, - 827, - 828, ], - "id": 433, + "id": 429, "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", "man_dir": "", "name": "eslint-plugin-react", @@ -42958,6 +42675,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 825, + 826, + 827, + 828, 829, 830, 831, @@ -42966,12 +42687,8 @@ exports[`next build works: node 1`] = ` 834, 835, 836, - 837, - 838, - 839, - 840, ], - "id": 434, + "id": 430, "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "man_dir": "", "name": "string.prototype.matchall", @@ -42990,11 +42707,11 @@ exports[`next build works: node 1`] = ` "name": "resolve", }, "dependencies": [ - 841, - 842, - 843, + 837, + 838, + 839, ], - "id": 435, + "id": 431, "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "man_dir": "", "name": "resolve", @@ -43010,11 +42727,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 844, - 845, - 846, + 840, + 841, + 842, ], - "id": 436, + "id": 432, "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "man_dir": "", "name": "prop-types", @@ -43030,7 +42747,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 437, + "id": 433, "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "man_dir": "", "name": "react-is", @@ -43046,11 +42763,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 847, - 848, - 849, + 843, + 844, + 845, ], - "id": 438, + "id": 434, "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "man_dir": "", "name": "object.hasown", @@ -43066,13 +42783,13 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 846, + 847, + 848, + 849, 850, - 851, - 852, - 853, - 854, ], - "id": 439, + "id": 435, "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "man_dir": "", "name": "array.prototype.tosorted", @@ -43088,12 +42805,12 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 855, - 856, - 857, - 858, + 851, + 852, + 853, + 854, ], - "id": 440, + "id": 436, "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", "man_dir": "", "name": "array.prototype.toreversed", @@ -43109,14 +42826,14 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ + 855, + 856, + 857, + 858, 859, 860, - 861, - 862, - 863, - 864, ], - "id": 441, + "id": 437, "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "man_dir": "", "name": "array.prototype.findlast", @@ -43132,10 +42849,10 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 865, - 866, + 861, + 862, ], - "id": 442, + "id": 438, "integrity": "sha512-DIM2C9qCECwhck9nLsCDeTv943VmGMCkwX3KljjprSRDXaK2CSiUDVGbUit80Er38ukgxuESJgYPAys4FsNCdg==", "man_dir": "", "name": "bun-types", @@ -43151,9 +42868,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 867, + 863, ], - "id": 443, + "id": 439, "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "man_dir": "", "name": "@types/ws", @@ -43172,15 +42889,15 @@ exports[`next build works: node 1`] = ` "name": "autoprefixer", }, "dependencies": [ + 864, + 865, + 866, + 867, 868, 869, 870, - 871, - 872, - 873, - 874, ], - "id": 444, + "id": 440, "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "man_dir": "", "name": "autoprefixer", @@ -43196,7 +42913,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 445, + "id": 441, "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "man_dir": "", "name": "normalize-range", @@ -43212,7 +42929,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 446, + "id": 442, "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "man_dir": "", "name": "fraction.js", @@ -43231,12 +42948,12 @@ exports[`next build works: node 1`] = ` "name": "browserslist", }, "dependencies": [ - 875, - 876, - 877, - 878, + 871, + 872, + 873, + 874, ], - "id": 447, + "id": 443, "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "man_dir": "", "name": "browserslist", @@ -43255,11 +42972,11 @@ exports[`next build works: node 1`] = ` "name": "update-browserslist-db", }, "dependencies": [ - 879, - 880, - 881, + 875, + 876, + 877, ], - "id": 448, + "id": 444, "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "man_dir": "", "name": "update-browserslist-db", @@ -43275,7 +42992,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 449, + "id": 445, "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "man_dir": "", "name": "node-releases", @@ -43291,7 +43008,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 450, + "id": 446, "integrity": "sha512-eVGeQxpaBYbomDBa/Mehrs28MdvCXfJmEFzaMFsv8jH/MJDLIylJN81eTJ5kvx7B7p18OiPK0BkC06lydEy63A==", "man_dir": "", "name": "electron-to-chromium", @@ -43307,9 +43024,9 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 882, + 878, ], - "id": 451, + "id": 447, "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "man_dir": "", "name": "@types/react-dom", @@ -43325,11 +43042,11 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [ - 883, - 884, - 885, + 879, + 880, + 881, ], - "id": 452, + "id": 448, "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", "man_dir": "", "name": "@types/react", @@ -43345,7 +43062,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 453, + "id": 449, "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "man_dir": "", "name": "csstype", @@ -43361,7 +43078,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 454, + "id": 450, "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==", "man_dir": "", "name": "@types/scheduler", @@ -43377,7 +43094,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 455, + "id": 451, "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "man_dir": "", "name": "@types/prop-types", @@ -43393,7 +43110,7 @@ exports[`next build works: node 1`] = ` { "bin": null, "dependencies": [], - "id": 456, + "id": 452, "integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==", "man_dir": "", "name": "@types/node", @@ -43415,48 +43132,48 @@ exports[`next build works: node 1`] = ` "package_id": 13, }, "@babel/code-frame": { - "id": 281, - "package_id": 206, + "id": 277, + "package_id": 202, }, "@babel/helper-validator-identifier": { - "id": 288, - "package_id": 215, + "id": 284, + "package_id": 211, }, "@babel/highlight": { - "id": 286, - "package_id": 207, + "id": 282, + "package_id": 203, }, "@babel/runtime": { - "id": 735, - "package_id": 431, + "id": 731, + "package_id": 427, }, "@eslint-community/eslint-utils": { - "id": 374, - "package_id": 245, + "id": 370, + "package_id": 241, }, "@eslint-community/regexpp": { - "id": 372, - "package_id": 252, + "id": 368, + "package_id": 248, }, "@eslint/eslintrc": { - "id": 367, - "package_id": 265, + "id": 363, + "package_id": 261, }, "@eslint/js": { - "id": 355, - "package_id": 293, + "id": 351, + "package_id": 289, }, "@humanwhocodes/config-array": { - "id": 373, - "package_id": 247, + "id": 369, + "package_id": 243, }, "@humanwhocodes/module-importer": { - "id": 375, - "package_id": 244, + "id": 371, + "package_id": 240, }, "@humanwhocodes/object-schema": { - "id": 379, - "package_id": 251, + "id": 375, + "package_id": 247, }, "@isaacs/cliui": { "id": 111, @@ -43483,48 +43200,48 @@ exports[`next build works: node 1`] = ` "package_id": 101, }, "@next/env": { - "id": 304, - "package_id": 237, + "id": 300, + "package_id": 233, }, "@next/eslint-plugin-next": { - "id": 333, - "package_id": 406, + "id": 329, + "package_id": 402, }, "@next/swc-darwin-arm64": { - "id": 310, - "package_id": 231, + "id": 306, + "package_id": 227, }, "@next/swc-darwin-x64": { - "id": 309, - "package_id": 232, + "id": 305, + "package_id": 228, }, "@next/swc-linux-arm64-gnu": { - "id": 314, - "package_id": 227, + "id": 310, + "package_id": 223, }, "@next/swc-linux-arm64-musl": { - "id": 316, - "package_id": 225, + "id": 312, + "package_id": 221, }, "@next/swc-linux-x64-gnu": { - "id": 311, - "package_id": 230, + "id": 307, + "package_id": 226, }, "@next/swc-linux-x64-musl": { - "id": 312, - "package_id": 229, + "id": 308, + "package_id": 225, }, "@next/swc-win32-arm64-msvc": { - "id": 317, - "package_id": 224, + "id": 313, + "package_id": 220, }, "@next/swc-win32-ia32-msvc": { - "id": 315, - "package_id": 226, + "id": 311, + "package_id": 222, }, "@next/swc-win32-x64-msvc": { - "id": 313, - "package_id": 228, + "id": 309, + "package_id": 224, }, "@nodelib/fs.scandir": { "id": 72, @@ -43535,7 +43252,7 @@ exports[`next build works: node 1`] = ` "package_id": 49, }, "@nodelib/fs.walk": { - "id": 368, + "id": 364, "package_id": 43, }, "@pkgjs/parseargs": { @@ -43544,94 +43261,94 @@ exports[`next build works: node 1`] = ` }, "@puppeteer/browsers": { "id": 155, - "package_id": 114, + "package_id": 115, }, "@rushstack/eslint-patch": { - "id": 332, - "package_id": 407, + "id": 328, + "package_id": 403, }, "@swc/helpers": { - "id": 307, - "package_id": 234, + "id": 303, + "package_id": 230, }, "@tootallnate/quickjs-emscripten": { - "id": 218, - "package_id": 177, + "id": 219, + "package_id": 178, }, "@types/json5": { - "id": 467, - "package_id": 312, + "id": 463, + "package_id": 308, }, "@types/node": { "id": 0, - "package_id": 456, + "package_id": 452, }, "@types/prop-types": { - "id": 883, - "package_id": 455, + "id": 879, + "package_id": 451, }, "@types/react": { "id": 1, - "package_id": 452, + "package_id": 448, }, "@types/react-dom": { "id": 2, - "package_id": 451, + "package_id": 447, }, "@types/scheduler": { - "id": 884, - "package_id": 454, + "id": 880, + "package_id": 450, }, "@types/ws": { - "id": 865, - "package_id": 443, + "id": 861, + "package_id": 439, }, "@types/yauzl": { - "id": 252, - "package_id": 181, + "id": 253, + "package_id": 182, }, "@typescript-eslint/parser": { - "id": 334, - "package_id": 394, + "id": 330, + "package_id": 390, }, "@typescript-eslint/scope-manager": { - "id": 710, - "package_id": 405, + "id": 706, + "package_id": 401, }, "@typescript-eslint/types": { - "id": 708, - "package_id": 397, + "id": 704, + "package_id": 393, }, "@typescript-eslint/typescript-estree": { - "id": 711, - "package_id": 395, + "id": 707, + "package_id": 391, }, "@typescript-eslint/visitor-keys": { - "id": 709, - "package_id": 396, + "id": 705, + "package_id": 392, }, "acorn": { - "id": 409, - "package_id": 272, + "id": 405, + "package_id": 268, }, "acorn-jsx": { - "id": 410, - "package_id": 271, + "id": 406, + "package_id": 267, }, "agent-base": { - "id": 201, - "package_id": 155, + "id": 202, + "package_id": 156, }, "ajv": { - "id": 340, - "package_id": 273, + "id": 336, + "package_id": 269, }, "ansi-regex": { "id": 122, "package_id": 77, }, "ansi-styles": { - "id": 437, + "id": 433, "package_id": 81, }, "any-promise": { @@ -43647,180 +43364,180 @@ exports[`next build works: node 1`] = ` "package_id": 107, }, "argparse": { - "id": 298, - "package_id": 217, + "id": 294, + "package_id": 213, }, "aria-query": { - "id": 736, - "package_id": 430, + "id": 732, + "package_id": 426, }, "array-buffer-byte-length": { - "id": 504, - "package_id": 378, + "id": 500, + "package_id": 374, }, "array-includes": { - "id": 810, - "package_id": 388, + "id": 806, + "package_id": 384, }, "array-union": { - "id": 725, - "package_id": 404, + "id": 721, + "package_id": 400, }, "array.prototype.findlast": { - "id": 811, - "package_id": 441, + "id": 807, + "package_id": 437, }, "array.prototype.findlastindex": { - "id": 450, - "package_id": 387, + "id": 446, + "package_id": 383, }, "array.prototype.flat": { - "id": 451, - "package_id": 386, + "id": 447, + "package_id": 382, }, "array.prototype.flatmap": { - "id": 812, - "package_id": 384, + "id": 808, + "package_id": 380, }, "array.prototype.toreversed": { - "id": 813, - "package_id": 440, + "id": 809, + "package_id": 436, }, "array.prototype.tosorted": { - "id": 814, - "package_id": 439, + "id": 810, + "package_id": 435, }, "arraybuffer.prototype.slice": { - "id": 505, - "package_id": 377, + "id": 501, + "package_id": 373, }, "ast-types": { - "id": 228, - "package_id": 166, + "id": 229, + "package_id": 167, }, "ast-types-flow": { - "id": 739, - "package_id": 429, + "id": 735, + "package_id": 425, }, "autoprefixer": { "id": 3, - "package_id": 444, + "package_id": 440, }, "available-typed-arrays": { - "id": 506, - "package_id": 334, + "id": 502, + "package_id": 330, }, "axe-core": { - "id": 740, - "package_id": 428, + "id": 736, + "package_id": 424, }, "axobject-query": { - "id": 741, - "package_id": 426, + "id": 737, + "package_id": 422, }, "b4a": { - "id": 194, - "package_id": 138, + "id": 195, + "package_id": 139, }, "balanced-match": { - "id": 383, + "id": 379, "package_id": 71, }, "bare-events": { - "id": 185, - "package_id": 136, + "id": 186, + "package_id": 137, }, "bare-fs": { - "id": 182, - "package_id": 133, + "id": 183, + "package_id": 134, }, "bare-os": { - "id": 184, - "package_id": 132, + "id": 185, + "package_id": 133, }, "bare-path": { - "id": 183, - "package_id": 131, + "id": 184, + "package_id": 132, }, "bare-stream": { - "id": 187, - "package_id": 134, + "id": 188, + "package_id": 135, }, "base64-js": { - "id": 178, - "package_id": 129, + "id": 179, + "package_id": 130, }, "basic-ftp": { - "id": 240, - "package_id": 176, + "id": 241, + "package_id": 177, }, "binary-extensions": { "id": 87, "package_id": 54, }, "brace-expansion": { - "id": 382, - "package_id": 249, + "id": 378, + "package_id": 245, }, "braces": { "id": 79, "package_id": 34, }, "browserslist": { - "id": 868, - "package_id": 447, + "id": 864, + "package_id": 443, }, "buffer": { - "id": 176, - "package_id": 127, + "id": 177, + "package_id": 128, }, "buffer-crc32": { - "id": 256, - "package_id": 185, + "id": 257, + "package_id": 186, }, "bun-types": { "id": 4, - "package_id": 442, + "package_id": 438, }, "busboy": { - "id": 302, - "package_id": 239, + "id": 298, + "package_id": 235, }, "call-bind": { - "id": 697, - "package_id": 326, + "id": 693, + "package_id": 322, }, "callsites": { - "id": 301, - "package_id": 221, + "id": 297, + "package_id": 217, }, "camelcase-css": { "id": 59, "package_id": 31, }, "caniuse-lite": { - "id": 869, - "package_id": 233, + "id": 865, + "package_id": 229, }, "chalk": { - "id": 342, - "package_id": 303, + "id": 338, + "package_id": 299, }, "chokidar": { "id": 21, "package_id": 50, }, "chromium-bidi": { - "id": 261, - "package_id": 198, + "id": 262, + "package_id": 193, }, "client-only": { - "id": 323, - "package_id": 236, + "id": 319, + "package_id": 232, }, "cliui": { - "id": 166, - "package_id": 124, + "id": 167, + "package_id": 125, }, "color-convert": { "id": 126, @@ -43835,19 +43552,15 @@ exports[`next build works: node 1`] = ` "package_id": 99, }, "concat-map": { - "id": 384, - "package_id": 250, + "id": 380, + "package_id": 246, }, "cosmiconfig": { "id": 153, - "package_id": 201, - }, - "cross-fetch": { - "id": 262, - "package_id": 193, + "package_id": 197, }, "cross-spawn": { - "id": 359, + "id": 355, "package_id": 93, }, "cssesc": { @@ -43855,552 +43568,552 @@ exports[`next build works: node 1`] = ` "package_id": 5, }, "csstype": { - "id": 885, - "package_id": 453, + "id": 881, + "package_id": 449, }, "damerau-levenshtein": { - "id": 742, - "package_id": 425, + "id": 738, + "package_id": 421, }, "data-uri-to-buffer": { - "id": 241, - "package_id": 175, + "id": 242, + "package_id": 176, }, "data-view-buffer": { - "id": 508, - "package_id": 376, + "id": 504, + "package_id": 372, }, "data-view-byte-length": { - "id": 509, - "package_id": 375, + "id": 505, + "package_id": 371, }, "data-view-byte-offset": { - "id": 510, - "package_id": 374, + "id": 506, + "package_id": 370, }, "debug": { - "id": 343, - "package_id": 153, + "id": 339, + "package_id": 154, }, "deep-is": { - "id": 422, - "package_id": 292, + "id": 418, + "package_id": 288, }, "define-data-property": { - "id": 476, - "package_id": 324, + "id": 472, + "package_id": 320, }, "define-properties": { - "id": 698, - "package_id": 317, + "id": 694, + "package_id": 313, }, "degenerator": { - "id": 226, - "package_id": 160, + "id": 227, + "package_id": 161, }, "dequal": { - "id": 808, - "package_id": 427, + "id": 804, + "package_id": 423, }, "devtools-protocol": { - "id": 264, - "package_id": 192, + "id": 156, + "package_id": 114, }, "didyoumean": { "id": 24, "package_id": 38, }, "dir-glob": { - "id": 726, - "package_id": 402, + "id": 722, + "package_id": 398, }, "dlv": { "id": 15, "package_id": 106, }, "doctrine": { - "id": 352, - "package_id": 295, + "id": 348, + "package_id": 291, }, "eastasianwidth": { "id": 132, "package_id": 89, }, "electron-to-chromium": { - "id": 876, - "package_id": 450, + "id": 872, + "package_id": 446, }, "emoji-regex": { - "id": 743, + "id": 739, "package_id": 88, }, "end-of-stream": { - "id": 197, - "package_id": 145, + "id": 198, + "package_id": 146, }, "enhanced-resolve": { - "id": 441, - "package_id": 391, + "id": 437, + "package_id": 387, }, "env-paths": { - "id": 276, - "package_id": 222, + "id": 272, + "package_id": 218, }, "error-ex": { - "id": 282, - "package_id": 204, + "id": 278, + "package_id": 200, }, "es-abstract": { - "id": 699, - "package_id": 329, + "id": 695, + "package_id": 325, }, "es-define-property": { - "id": 490, - "package_id": 320, + "id": 486, + "package_id": 316, }, "es-errors": { - "id": 862, - "package_id": 316, + "id": 858, + "package_id": 312, }, "es-iterator-helpers": { - "id": 816, - "package_id": 413, + "id": 812, + "package_id": 409, }, "es-object-atoms": { - "id": 700, - "package_id": 315, + "id": 696, + "package_id": 311, }, "es-set-tostringtag": { - "id": 764, - "package_id": 373, + "id": 760, + "package_id": 369, }, "es-shim-unscopables": { - "id": 864, - "package_id": 385, + "id": 860, + "package_id": 381, }, "es-to-primitive": { - "id": 515, - "package_id": 371, + "id": 511, + "package_id": 367, }, "escalade": { - "id": 879, - "package_id": 123, + "id": 875, + "package_id": 124, }, "escape-string-regexp": { - "id": 371, - "package_id": 253, + "id": 367, + "package_id": 249, }, "escodegen": { - "id": 229, - "package_id": 162, + "id": 230, + "package_id": 163, }, "eslint": { "id": 5, - "package_id": 242, + "package_id": 238, }, "eslint-config-next": { "id": 6, - "package_id": 241, + "package_id": 237, }, "eslint-import-resolver-node": { - "id": 336, - "package_id": 382, + "id": 332, + "package_id": 378, }, "eslint-import-resolver-typescript": { - "id": 337, - "package_id": 306, + "id": 333, + "package_id": 302, }, "eslint-module-utils": { - "id": 456, - "package_id": 380, + "id": 452, + "package_id": 376, }, "eslint-plugin-import": { - "id": 330, - "package_id": 307, + "id": 326, + "package_id": 303, }, "eslint-plugin-jsx-a11y": { - "id": 331, - "package_id": 408, + "id": 327, + "package_id": 404, }, "eslint-plugin-react": { - "id": 329, - "package_id": 433, + "id": 325, + "package_id": 429, }, "eslint-plugin-react-hooks": { - "id": 335, - "package_id": 393, + "id": 331, + "package_id": 389, }, "eslint-scope": { - "id": 362, - "package_id": 282, + "id": 358, + "package_id": 278, }, "eslint-visitor-keys": { - "id": 370, - "package_id": 246, + "id": 366, + "package_id": 242, }, "espree": { - "id": 344, - "package_id": 270, + "id": 340, + "package_id": 266, }, "esprima": { - "id": 230, - "package_id": 161, + "id": 231, + "package_id": 162, }, "esquery": { - "id": 346, - "package_id": 302, + "id": 342, + "package_id": 298, }, "esrecurse": { - "id": 418, - "package_id": 283, + "id": 414, + "package_id": 279, }, "estraverse": { - "id": 436, - "package_id": 165, + "id": 432, + "package_id": 166, }, "esutils": { - "id": 347, - "package_id": 164, + "id": 343, + "package_id": 165, }, "extract-zip": { - "id": 157, - "package_id": 180, + "id": 158, + "package_id": 181, }, "fast-deep-equal": { - "id": 365, - "package_id": 278, + "id": 361, + "package_id": 274, }, "fast-fifo": { - "id": 195, - "package_id": 140, + "id": 196, + "package_id": 141, }, "fast-glob": { "id": 22, "package_id": 40, }, "fast-json-stable-stringify": { - "id": 414, - "package_id": 277, + "id": 410, + "package_id": 273, }, "fast-levenshtein": { - "id": 426, - "package_id": 287, + "id": 422, + "package_id": 283, }, "fastq": { "id": 73, "package_id": 44, }, "fd-slicer": { - "id": 255, - "package_id": 186, + "id": 256, + "package_id": 187, }, "file-entry-cache": { - "id": 369, - "package_id": 254, + "id": 365, + "package_id": 250, }, "fill-range": { "id": 63, "package_id": 35, }, "find-up": { - "id": 348, - "package_id": 296, + "id": 344, + "package_id": 292, }, "flat-cache": { - "id": 385, - "package_id": 255, + "id": 381, + "package_id": 251, }, "flatted": { - "id": 386, - "package_id": 264, + "id": 382, + "package_id": 260, }, "for-each": { - "id": 587, - "package_id": 332, + "id": 583, + "package_id": 328, }, "foreground-child": { "id": 102, "package_id": 91, }, "fraction.js": { - "id": 870, - "package_id": 446, + "id": 866, + "package_id": 442, }, "fs-extra": { - "id": 243, - "package_id": 171, + "id": 244, + "package_id": 172, }, "fs.realpath": { - "id": 390, - "package_id": 261, + "id": 386, + "package_id": 257, }, "fsevents": { "id": 85, "package_id": 51, }, "function-bind": { - "id": 765, + "id": 761, "package_id": 21, }, "function.prototype.name": { - "id": 516, - "package_id": 370, + "id": 512, + "package_id": 366, }, "functions-have-names": { - "id": 619, - "package_id": 358, + "id": 615, + "package_id": 354, }, "get-caller-file": { - "id": 168, - "package_id": 122, + "id": 169, + "package_id": 123, }, "get-intrinsic": { - "id": 701, - "package_id": 321, + "id": 697, + "package_id": 317, }, "get-stream": { - "id": 250, - "package_id": 188, + "id": 251, + "package_id": 189, }, "get-symbol-description": { - "id": 518, - "package_id": 369, + "id": 514, + "package_id": 365, }, "get-tsconfig": { - "id": 444, - "package_id": 389, + "id": 440, + "package_id": 385, }, "get-uri": { - "id": 221, - "package_id": 170, + "id": 222, + "package_id": 171, }, "glob": { - "id": 734, + "id": 730, "package_id": 65, }, "glob-parent": { - "id": 360, + "id": 356, "package_id": 27, }, "globals": { - "id": 349, - "package_id": 268, + "id": 345, + "package_id": 264, }, "globalthis": { - "id": 767, - "package_id": 368, + "id": 763, + "package_id": 364, }, "globby": { - "id": 714, - "package_id": 400, + "id": 710, + "package_id": 396, }, "gopd": { - "id": 835, - "package_id": 325, + "id": 831, + "package_id": 321, }, "graceful-fs": { - "id": 306, - "package_id": 174, + "id": 302, + "package_id": 175, }, "graphemer": { - "id": 353, - "package_id": 294, + "id": 349, + "package_id": 290, }, "has-bigints": { - "id": 559, - "package_id": 343, + "id": 555, + "package_id": 339, }, "has-flag": { - "id": 439, - "package_id": 305, + "id": 435, + "package_id": 301, }, "has-property-descriptors": { - "id": 768, - "package_id": 319, + "id": 764, + "package_id": 315, }, "has-proto": { - "id": 769, - "package_id": 323, + "id": 765, + "package_id": 319, }, "has-symbols": { - "id": 770, - "package_id": 322, + "id": 766, + "package_id": 318, }, "has-tostringtag": { - "id": 568, - "package_id": 331, + "id": 564, + "package_id": 327, }, "hasown": { - "id": 457, + "id": 453, "package_id": 20, }, "http-proxy-agent": { - "id": 203, - "package_id": 169, + "id": 204, + "package_id": 170, }, "https-proxy-agent": { - "id": 204, - "package_id": 168, + "id": 205, + "package_id": 169, }, "ieee754": { - "id": 179, - "package_id": 128, + "id": 180, + "package_id": 129, }, "ignore": { - "id": 345, - "package_id": 267, + "id": 341, + "package_id": 263, }, "import-fresh": { - "id": 404, - "package_id": 218, + "id": 400, + "package_id": 214, }, "imurmurhash": { - "id": 361, - "package_id": 284, + "id": 357, + "package_id": 280, }, "inflight": { - "id": 391, - "package_id": 260, + "id": 387, + "package_id": 256, }, "inherits": { - "id": 392, - "package_id": 259, + "id": 388, + "package_id": 255, }, "internal-slot": { - "id": 771, - "package_id": 366, + "id": 767, + "package_id": 362, }, "ip-address": { - "id": 212, - "package_id": 150, + "id": 213, + "package_id": 151, }, "is-array-buffer": { - "id": 526, - "package_id": 365, + "id": 522, + "package_id": 361, }, "is-arrayish": { - "id": 285, - "package_id": 205, + "id": 281, + "package_id": 201, }, "is-async-function": { - "id": 788, - "package_id": 424, + "id": 784, + "package_id": 420, }, "is-bigint": { - "id": 562, - "package_id": 342, + "id": 558, + "package_id": 338, }, "is-binary-path": { "id": 81, "package_id": 53, }, "is-boolean-object": { - "id": 563, - "package_id": 341, + "id": 559, + "package_id": 337, }, "is-callable": { - "id": 527, - "package_id": 333, + "id": 523, + "package_id": 329, }, "is-core-module": { - "id": 458, + "id": 454, "package_id": 19, }, "is-data-view": { - "id": 528, - "package_id": 364, + "id": 524, + "package_id": 360, }, "is-date-object": { - "id": 647, - "package_id": 372, + "id": 643, + "package_id": 368, }, "is-extglob": { "id": 58, "package_id": 29, }, "is-finalizationregistry": { - "id": 790, - "package_id": 423, + "id": 786, + "package_id": 419, }, "is-fullwidth-code-point": { "id": 124, "package_id": 79, }, "is-generator-function": { - "id": 791, - "package_id": 422, + "id": 787, + "package_id": 418, }, "is-glob": { - "id": 350, + "id": 346, "package_id": 28, }, "is-map": { - "id": 798, - "package_id": 421, + "id": 794, + "package_id": 417, }, "is-negative-zero": { - "id": 529, - "package_id": 363, + "id": 525, + "package_id": 359, }, "is-number": { "id": 65, "package_id": 37, }, "is-number-object": { - "id": 564, - "package_id": 340, + "id": 560, + "package_id": 336, }, "is-path-inside": { - "id": 364, - "package_id": 280, + "id": 360, + "package_id": 276, }, "is-regex": { - "id": 530, - "package_id": 353, + "id": 526, + "package_id": 349, }, "is-set": { - "id": 799, - "package_id": 420, + "id": 795, + "package_id": 416, }, "is-shared-array-buffer": { - "id": 531, - "package_id": 362, + "id": 527, + "package_id": 358, }, "is-string": { - "id": 702, - "package_id": 339, + "id": 698, + "package_id": 335, }, "is-symbol": { - "id": 648, - "package_id": 338, + "id": 644, + "package_id": 334, }, "is-typed-array": { - "id": 533, - "package_id": 345, + "id": 529, + "package_id": 341, }, "is-weakmap": { - "id": 800, - "package_id": 419, + "id": 796, + "package_id": 415, }, "is-weakref": { - "id": 534, - "package_id": 361, + "id": 530, + "package_id": 357, }, "is-weakset": { - "id": 801, - "package_id": 418, + "id": 797, + "package_id": 414, }, "isarray": { - "id": 612, - "package_id": 355, + "id": 608, + "package_id": 351, }, "isexe": { "id": 140, "package_id": 95, }, "iterator.prototype": { - "id": 772, - "package_id": 414, + "id": 768, + "package_id": 410, }, "jackspeak": { "id": 103, @@ -44415,56 +44128,56 @@ exports[`next build works: node 1`] = ` "package_id": 111, }, "js-yaml": { - "id": 351, - "package_id": 216, + "id": 347, + "package_id": 212, }, "jsbn": { - "id": 214, - "package_id": 152, + "id": 215, + "package_id": 153, }, "json-buffer": { - "id": 398, - "package_id": 263, + "id": 394, + "package_id": 259, }, "json-parse-even-better-errors": { - "id": 283, - "package_id": 203, + "id": 279, + "package_id": 199, }, "json-schema-traverse": { - "id": 415, - "package_id": 276, + "id": 411, + "package_id": 272, }, "json-stable-stringify-without-jsonify": { - "id": 376, - "package_id": 243, + "id": 372, + "package_id": 239, }, "json5": { - "id": 468, - "package_id": 311, + "id": 464, + "package_id": 307, }, "jsonfile": { - "id": 245, - "package_id": 173, + "id": 246, + "package_id": 174, }, "jsx-ast-utils": { - "id": 818, - "package_id": 412, + "id": 814, + "package_id": 408, }, "keyv": { - "id": 387, - "package_id": 262, + "id": 383, + "package_id": 258, }, "language-subtag-registry": { - "id": 755, - "package_id": 411, + "id": 751, + "package_id": 407, }, "language-tags": { - "id": 747, - "package_id": 410, + "id": 743, + "package_id": 406, }, "levn": { - "id": 341, - "package_id": 288, + "id": 337, + "package_id": 284, }, "lilconfig": { "id": 23, @@ -44475,20 +44188,20 @@ exports[`next build works: node 1`] = ` "package_id": 64, }, "locate-path": { - "id": 431, - "package_id": 298, + "id": 427, + "package_id": 294, }, "lodash.merge": { - "id": 363, - "package_id": 281, + "id": 359, + "package_id": 277, }, "loose-envify": { "id": 150, "package_id": 110, }, "lru-cache": { - "id": 205, - "package_id": 178, + "id": 206, + "package_id": 179, }, "merge2": { "id": 69, @@ -44499,24 +44212,24 @@ exports[`next build works: node 1`] = ` "package_id": 32, }, "minimatch": { - "id": 354, - "package_id": 248, + "id": 350, + "package_id": 244, }, "minimist": { - "id": 469, - "package_id": 310, + "id": 465, + "package_id": 306, }, "minipass": { "id": 105, "package_id": 67, }, "mitt": { - "id": 273, - "package_id": 200, + "id": 268, + "package_id": 196, }, "ms": { - "id": 216, - "package_id": 154, + "id": 217, + "package_id": 155, }, "mz": { "id": 94, @@ -44527,35 +44240,31 @@ exports[`next build works: node 1`] = ` "package_id": 10, }, "natural-compare": { - "id": 366, - "package_id": 279, + "id": 362, + "package_id": 275, }, "netmask": { - "id": 227, - "package_id": 159, + "id": 228, + "package_id": 160, }, "next": { "id": 7, - "package_id": 223, - }, - "node-fetch": { - "id": 268, - "package_id": 194, + "package_id": 219, }, "node-releases": { - "id": 877, - "package_id": 449, + "id": 873, + "package_id": 445, }, "normalize-path": { "id": 30, "package_id": 25, }, "normalize-range": { - "id": 871, - "package_id": 445, + "id": 867, + "package_id": 441, }, "object-assign": { - "id": 845, + "id": 841, "package_id": 63, }, "object-hash": { @@ -44563,76 +44272,76 @@ exports[`next build works: node 1`] = ` "package_id": 26, }, "object-inspect": { - "id": 535, - "package_id": 360, + "id": 531, + "package_id": 356, }, "object-keys": { - "id": 478, - "package_id": 318, + "id": 474, + "package_id": 314, }, "object.assign": { - "id": 758, - "package_id": 359, + "id": 754, + "package_id": 355, }, "object.entries": { - "id": 820, - "package_id": 409, + "id": 816, + "package_id": 405, }, "object.fromentries": { - "id": 821, - "package_id": 379, + "id": 817, + "package_id": 375, }, "object.groupby": { - "id": 462, - "package_id": 328, + "id": 458, + "package_id": 324, }, "object.hasown": { - "id": 822, - "package_id": 438, + "id": 818, + "package_id": 434, }, "object.values": { - "id": 823, - "package_id": 314, + "id": 819, + "package_id": 310, }, "once": { - "id": 198, - "package_id": 143, + "id": 199, + "package_id": 144, }, "optionator": { - "id": 356, - "package_id": 286, + "id": 352, + "package_id": 282, }, "p-limit": { - "id": 434, - "package_id": 300, + "id": 430, + "package_id": 296, }, "p-locate": { - "id": 433, - "package_id": 299, + "id": 429, + "package_id": 295, }, "pac-proxy-agent": { - "id": 206, - "package_id": 157, + "id": 207, + "package_id": 158, }, "pac-resolver": { - "id": 224, - "package_id": 158, + "id": 225, + "package_id": 159, }, "parent-module": { - "id": 299, - "package_id": 220, + "id": 295, + "package_id": 216, }, "parse-json": { - "id": 279, - "package_id": 202, + "id": 275, + "package_id": 198, }, "path-exists": { - "id": 432, - "package_id": 297, + "id": 428, + "package_id": 293, }, "path-is-absolute": { - "id": 395, - "package_id": 258, + "id": 391, + "package_id": 254, }, "path-key": { "id": 137, @@ -44647,15 +44356,15 @@ exports[`next build works: node 1`] = ` "package_id": 66, }, "path-type": { - "id": 731, - "package_id": 403, + "id": 727, + "package_id": 399, }, "pend": { - "id": 257, - "package_id": 187, + "id": 258, + "package_id": 188, }, "picocolors": { - "id": 872, + "id": 868, "package_id": 9, }, "picomatch": { @@ -44671,8 +44380,8 @@ exports[`next build works: node 1`] = ` "package_id": 58, }, "possible-typed-array-names": { - "id": 557, - "package_id": 335, + "id": 553, + "package_id": 331, }, "postcss": { "id": 8, @@ -44699,36 +44408,36 @@ exports[`next build works: node 1`] = ` "package_id": 3, }, "postcss-value-parser": { - "id": 873, + "id": 869, "package_id": 24, }, "prelude-ls": { - "id": 427, - "package_id": 290, + "id": 423, + "package_id": 286, }, "progress": { - "id": 158, - "package_id": 179, + "id": 159, + "package_id": 180, }, "prop-types": { - "id": 824, - "package_id": 436, + "id": 820, + "package_id": 432, }, "proxy-agent": { - "id": 159, - "package_id": 146, + "id": 160, + "package_id": 147, }, "proxy-from-env": { - "id": 207, - "package_id": 156, + "id": 208, + "package_id": 157, }, "pump": { - "id": 180, - "package_id": 142, + "id": 181, + "package_id": 143, }, "punycode": { - "id": 417, - "package_id": 275, + "id": 413, + "package_id": 271, }, "puppeteer": { "id": 9, @@ -44736,15 +44445,15 @@ exports[`next build works: node 1`] = ` }, "puppeteer-core": { "id": 154, - "package_id": 190, + "package_id": 191, }, "queue-microtask": { "id": 77, "package_id": 48, }, "queue-tick": { - "id": 190, - "package_id": 139, + "id": 191, + "package_id": 140, }, "react": { "id": 10, @@ -44755,8 +44464,8 @@ exports[`next build works: node 1`] = ` "package_id": 108, }, "react-is": { - "id": 846, - "package_id": 437, + "id": 842, + "package_id": 433, }, "read-cache": { "id": 48, @@ -44767,68 +44476,68 @@ exports[`next build works: node 1`] = ` "package_id": 52, }, "reflect.getprototypeof": { - "id": 777, - "package_id": 415, + "id": 773, + "package_id": 411, }, "regenerator-runtime": { - "id": 809, - "package_id": 432, + "id": 805, + "package_id": 428, }, "regexp.prototype.flags": { - "id": 838, - "package_id": 356, + "id": 834, + "package_id": 352, }, "require-directory": { - "id": 169, - "package_id": 121, + "id": 170, + "package_id": 122, }, "resolve": { "id": 19, "package_id": 16, }, "resolve-from": { - "id": 300, - "package_id": 219, + "id": 296, + "package_id": 215, }, "resolve-pkg-maps": { - "id": 703, - "package_id": 390, + "id": 699, + "package_id": 386, }, "reusify": { "id": 74, "package_id": 45, }, "rimraf": { - "id": 388, - "package_id": 256, + "id": 384, + "package_id": 252, }, "run-parallel": { "id": 76, "package_id": 47, }, "safe-array-concat": { - "id": 773, - "package_id": 354, + "id": 769, + "package_id": 350, }, "safe-regex-test": { - "id": 540, - "package_id": 352, + "id": 536, + "package_id": 348, }, "scheduler": { "id": 147, "package_id": 112, }, "semver": { - "id": 826, - "package_id": 313, + "id": 822, + "package_id": 309, }, "set-function-length": { - "id": 494, - "package_id": 327, + "id": 490, + "package_id": 323, }, "set-function-name": { - "id": 839, - "package_id": 357, + "id": 835, + "package_id": 353, }, "shebang-command": { "id": 138, @@ -44839,51 +44548,51 @@ exports[`next build works: node 1`] = ` "package_id": 97, }, "side-channel": { - "id": 840, - "package_id": 367, + "id": 836, + "package_id": 363, }, "signal-exit": { "id": 136, "package_id": 92, }, "slash": { - "id": 730, - "package_id": 401, + "id": 726, + "package_id": 397, }, "smart-buffer": { - "id": 213, - "package_id": 149, + "id": 214, + "package_id": 150, }, "socks": { - "id": 211, - "package_id": 148, + "id": 212, + "package_id": 149, }, "socks-proxy-agent": { - "id": 208, - "package_id": 147, + "id": 209, + "package_id": 148, }, "source-map": { - "id": 234, - "package_id": 163, + "id": 235, + "package_id": 164, }, "source-map-js": { "id": 44, "package_id": 8, }, "sprintf-js": { - "id": 215, - "package_id": 151, + "id": 216, + "package_id": 152, }, "streamsearch": { - "id": 328, - "package_id": 240, + "id": 324, + "package_id": 236, }, "streamx": { - "id": 196, - "package_id": 135, + "id": 197, + "package_id": 136, }, "string-width": { - "id": 170, + "id": 171, "package_id": 78, }, "string-width-cjs": { @@ -44891,23 +44600,23 @@ exports[`next build works: node 1`] = ` "package_id": 78, }, "string.prototype.matchall": { - "id": 827, - "package_id": 434, + "id": 823, + "package_id": 430, }, "string.prototype.trim": { - "id": 541, - "package_id": 351, + "id": 537, + "package_id": 347, }, "string.prototype.trimend": { - "id": 542, - "package_id": 350, + "id": 538, + "package_id": 346, }, "string.prototype.trimstart": { - "id": 543, - "package_id": 349, + "id": 539, + "package_id": 345, }, "strip-ansi": { - "id": 357, + "id": 353, "package_id": 76, }, "strip-ansi-cjs": { @@ -44915,24 +44624,24 @@ exports[`next build works: node 1`] = ` "package_id": 76, }, "strip-bom": { - "id": 470, - "package_id": 309, + "id": 466, + "package_id": 305, }, "strip-json-comments": { - "id": 407, - "package_id": 266, + "id": 403, + "package_id": 262, }, "styled-jsx": { - "id": 305, - "package_id": 235, + "id": 301, + "package_id": 231, }, "sucrase": { "id": 20, "package_id": 56, }, "supports-color": { - "id": 438, - "package_id": 304, + "id": 434, + "package_id": 300, }, "supports-preserve-symlinks-flag": { "id": 53, @@ -44943,24 +44652,24 @@ exports[`next build works: node 1`] = ` "package_id": 2, }, "tapable": { - "id": 705, - "package_id": 392, + "id": 701, + "package_id": 388, }, "tar-fs": { - "id": 160, - "package_id": 130, + "id": 161, + "package_id": 131, }, "tar-stream": { - "id": 181, - "package_id": 141, + "id": 182, + "package_id": 142, }, "text-decoder": { - "id": 191, - "package_id": 137, + "id": 192, + "package_id": 138, }, "text-table": { - "id": 358, - "package_id": 285, + "id": 354, + "package_id": 281, }, "thenify": { "id": 100, @@ -44971,127 +44680,115 @@ exports[`next build works: node 1`] = ` "package_id": 60, }, "through": { - "id": 177, - "package_id": 126, + "id": 178, + "package_id": 127, }, "to-regex-range": { "id": 64, "package_id": 36, }, - "tr46": { - "id": 271, - "package_id": 197, - }, "ts-api-utils": { - "id": 718, - "package_id": 398, + "id": 714, + "package_id": 394, }, "ts-interface-checker": { "id": 96, "package_id": 57, }, "tsconfig-paths": { - "id": 465, - "package_id": 308, + "id": 461, + "package_id": 304, }, "tslib": { - "id": 322, - "package_id": 167, + "id": 318, + "package_id": 168, }, "type-check": { - "id": 428, - "package_id": 289, + "id": 424, + "package_id": 285, }, "type-fest": { - "id": 408, - "package_id": 269, + "id": 404, + "package_id": 265, }, "typed-array-buffer": { - "id": 544, - "package_id": 348, + "id": 540, + "package_id": 344, }, "typed-array-byte-length": { - "id": 545, - "package_id": 347, + "id": 541, + "package_id": 343, }, "typed-array-byte-offset": { - "id": 546, - "package_id": 346, + "id": 542, + "package_id": 342, }, "typed-array-length": { - "id": 547, - "package_id": 344, + "id": 543, + "package_id": 340, }, "typescript": { "id": 13, "package_id": 1, }, "unbox-primitive": { - "id": 548, - "package_id": 336, + "id": 544, + "package_id": 332, }, "unbzip2-stream": { - "id": 161, - "package_id": 125, + "id": 162, + "package_id": 126, }, "undici-types": { - "id": 254, - "package_id": 183, + "id": 255, + "package_id": 184, }, "universalify": { - "id": 246, - "package_id": 172, + "id": 247, + "package_id": 173, }, "update-browserslist-db": { - "id": 878, - "package_id": 448, + "id": 874, + "package_id": 444, }, "uri-js": { - "id": 416, - "package_id": 274, + "id": 412, + "package_id": 270, }, "urlpattern-polyfill": { - "id": 274, - "package_id": 199, + "id": 269, + "package_id": 195, }, "util-deprecate": { "id": 37, "package_id": 4, }, - "webidl-conversions": { - "id": 272, - "package_id": 196, - }, - "whatwg-url": { - "id": 269, - "package_id": 195, - }, "which": { "id": 139, "package_id": 94, }, "which-boxed-primitive": { - "id": 561, - "package_id": 337, + "id": 557, + "package_id": 333, }, "which-builtin-type": { - "id": 785, - "package_id": 416, + "id": 781, + "package_id": 412, }, "which-collection": { - "id": 796, - "package_id": 417, + "id": 792, + "package_id": 413, }, "which-typed-array": { - "id": 549, - "package_id": 330, + "id": 545, + "package_id": 326, }, "word-wrap": { - "id": 423, - "package_id": 291, + "id": 419, + "package_id": 287, }, "wrap-ansi": { - "id": 175, + "id": 176, "package_id": 75, }, "wrap-ansi-cjs": { @@ -45099,40 +44796,44 @@ exports[`next build works: node 1`] = ` "package_id": 75, }, "wrappy": { - "id": 199, - "package_id": 144, + "id": 200, + "package_id": 145, }, "ws": { "id": 265, - "package_id": 191, + "package_id": 192, }, "y18n": { - "id": 171, - "package_id": 120, + "id": 172, + "package_id": 121, }, "yallist": { - "id": 165, - "package_id": 117, + "id": 166, + "package_id": 118, }, "yaml": { "id": 38, "package_id": 12, }, "yargs": { - "id": 162, - "package_id": 118, + "id": 163, + "package_id": 119, }, "yargs-parser": { - "id": 172, - "package_id": 119, + "id": 173, + "package_id": 120, }, "yauzl": { - "id": 251, - "package_id": 184, + "id": 252, + "package_id": 185, }, "yocto-queue": { - "id": 435, - "package_id": 301, + "id": 431, + "package_id": 297, + }, + "zod": { + "id": 270, + "package_id": 194, }, }, "depth": 0, @@ -45142,8 +44843,8 @@ exports[`next build works: node 1`] = ` { "dependencies": { "@types/node": { - "id": 866, - "package_id": 182, + "id": 862, + "package_id": 183, }, }, "depth": 1, @@ -45153,8 +44854,8 @@ exports[`next build works: node 1`] = ` { "dependencies": { "postcss": { - "id": 303, - "package_id": 238, + "id": 299, + "package_id": 234, }, }, "depth": 1, @@ -45164,8 +44865,8 @@ exports[`next build works: node 1`] = ` { "dependencies": { "@types/node": { - "id": 867, - "package_id": 182, + "id": 863, + "package_id": 183, }, }, "depth": 1, @@ -45175,12 +44876,12 @@ exports[`next build works: node 1`] = ` { "dependencies": { "doctrine": { - "id": 815, - "package_id": 383, + "id": 811, + "package_id": 379, }, "resolve": { - "id": 825, - "package_id": 435, + "id": 821, + "package_id": 431, }, }, "depth": 1, @@ -45190,12 +44891,12 @@ exports[`next build works: node 1`] = ` { "dependencies": { "debug": { - "id": 453, - "package_id": 381, + "id": 449, + "package_id": 377, }, "doctrine": { - "id": 454, - "package_id": 383, + "id": 450, + "package_id": 379, }, }, "depth": 1, @@ -45205,8 +44906,8 @@ exports[`next build works: node 1`] = ` { "dependencies": { "debug": { - "id": 678, - "package_id": 381, + "id": 674, + "package_id": 377, }, }, "depth": 1, @@ -45216,27 +44917,16 @@ exports[`next build works: node 1`] = ` { "dependencies": { "debug": { - "id": 263, - "package_id": 189, - }, - }, - "depth": 1, - "id": 7, - "path": "node_modules/puppeteer-core/node_modules", - }, - { - "dependencies": { - "debug": { - "id": 156, - "package_id": 189, + "id": 157, + "package_id": 190, }, "semver": { - "id": 163, - "package_id": 115, + "id": 164, + "package_id": 116, }, }, "depth": 1, - "id": 8, + "id": 7, "path": "node_modules/@puppeteer/browsers/node_modules", }, { @@ -45247,7 +44937,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 9, + "id": 8, "path": "node_modules/chokidar/node_modules", }, { @@ -45258,7 +44948,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 10, + "id": 9, "path": "node_modules/fast-glob/node_modules", }, { @@ -45269,18 +44959,18 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 11, + "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, { "dependencies": { "debug": { - "id": 676, - "package_id": 381, + "id": 672, + "package_id": 377, }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/eslint-module-utils/node_modules", }, { @@ -45291,44 +44981,44 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/glob/node_modules", }, { "dependencies": { "minimatch": { - "id": 717, - "package_id": 399, + "id": 713, + "package_id": 395, }, "semver": { - "id": 715, - "package_id": 115, + "id": 711, + "package_id": 116, }, }, "depth": 1, - "id": 14, + "id": 13, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 15, + "id": 14, "path": "node_modules/@puppeteer/browsers/node_modules/semver/node_modules", }, { "dependencies": { "glob": { - "id": 389, - "package_id": 257, + "id": 385, + "package_id": 253, }, }, "depth": 1, - "id": 16, + "id": 15, "path": "node_modules/rimraf/node_modules", }, { @@ -45339,7 +45029,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 2, - "id": 17, + "id": 16, "path": "node_modules/glob/node_modules/minimatch/node_modules", }, { @@ -45350,40 +45040,40 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 18, + "id": 17, "path": "node_modules/path-scurry/node_modules", }, { "dependencies": { "lru-cache": { - "id": 164, - "package_id": 116, + "id": 165, + "package_id": 117, }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", }, { "dependencies": { "brace-expansion": { - "id": 724, + "id": 720, "package_id": 70, }, }, "depth": 2, - "id": 20, + "id": 19, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, { "dependencies": { "@types/node": { - "id": 253, - "package_id": 182, + "id": 254, + "package_id": 183, }, }, "depth": 1, - "id": 21, + "id": 20, "path": "node_modules/@types/yauzl/node_modules", }, { @@ -45394,7 +45084,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 22, + "id": 21, "path": "node_modules/string-width/node_modules", }, { @@ -45413,18 +45103,18 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 23, + "id": 22, "path": "node_modules/@isaacs/cliui/node_modules", }, { "dependencies": { "chalk": { - "id": 289, - "package_id": 208, + "id": 285, + "package_id": 204, }, }, "depth": 1, - "id": 24, + "id": 23, "path": "node_modules/@babel/highlight/node_modules", }, { @@ -45435,7 +45125,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 25, + "id": 24, "path": "node_modules/string-width-cjs/node_modules", }, { @@ -45446,7 +45136,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 2, - "id": 26, + "id": 25, "path": "node_modules/@isaacs/cliui/node_modules/strip-ansi/node_modules", }, { @@ -45457,59 +45147,59 @@ exports[`next build works: node 1`] = ` }, }, "depth": 2, - "id": 27, + "id": 26, "path": "node_modules/@isaacs/cliui/node_modules/wrap-ansi/node_modules", }, { "dependencies": { "ansi-styles": { - "id": 292, - "package_id": 212, + "id": 288, + "package_id": 208, }, "escape-string-regexp": { - "id": 293, - "package_id": 211, + "id": 289, + "package_id": 207, }, "supports-color": { - "id": 294, - "package_id": 209, + "id": 290, + "package_id": 205, }, }, "depth": 2, - "id": 28, + "id": 27, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules", }, { "dependencies": { "color-convert": { - "id": 296, - "package_id": 213, + "id": 292, + "package_id": 209, }, }, "depth": 3, - "id": 29, + "id": 28, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules", }, { "dependencies": { "has-flag": { - "id": 295, - "package_id": 210, + "id": 291, + "package_id": 206, }, }, "depth": 3, - "id": 30, + "id": 29, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/supports-color/node_modules", }, { "dependencies": { "color-name": { - "id": 297, - "package_id": 214, + "id": 293, + "package_id": 210, }, }, "depth": 4, - "id": 31, + "id": 30, "path": "node_modules/@babel/highlight/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/node_modules", }, ], diff --git a/test/integration/next-pages/test/dev-server-puppeteer.ts b/test/integration/next-pages/test/dev-server-puppeteer.ts index faf620e5bfba8..8fbe1924501e9 100644 --- a/test/integration/next-pages/test/dev-server-puppeteer.ts +++ b/test/integration/next-pages/test/dev-server-puppeteer.ts @@ -1,7 +1,7 @@ -import { ConsoleMessage, Page, launch } from "puppeteer"; import assert from "assert"; import { copyFileSync } from "fs"; import { join } from "path"; +import { ConsoleMessage, Page, launch } from "puppeteer"; const root = join(import.meta.dir, "../"); @@ -13,8 +13,28 @@ if (process.argv.length > 2) { } const b = await launch({ - headless: true, + // While puppeteer is migrating to their new headless: `true` mode, + // this causes strange issues on macOS in the cloud (AWS and MacStadium). + // + // There is a GitHub issue, but the discussion is unhelpful: + // https://github.com/puppeteer/puppeteer/issues/10153 + // + // Fixes: 'TargetCloseError: Protocol error (Target.setAutoAttach): Target closed' + headless: "shell", dumpio: true, + pipe: true, + args: [ + // Fixes: 'dock_plist is not an NSDictionary' + "--no-sandbox", + "--single-process", + "--disable-setuid-sandbox", + "--disable-dev-shm-usage", + // Fixes: 'Navigating frame was detached' + "--disable-features=site-per-process", + // Uncomment if you want debug logs from Chromium: + // "--enable-logging=stderr", + // "--v=1", + ], }); async function main() { diff --git a/test/integration/next-pages/test/dev-server-ssr-100.test.ts b/test/integration/next-pages/test/dev-server-ssr-100.test.ts index 1e186d5a7e74e..269b88867dce7 100644 --- a/test/integration/next-pages/test/dev-server-ssr-100.test.ts +++ b/test/integration/next-pages/test/dev-server-ssr-100.test.ts @@ -1,11 +1,11 @@ -import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, tmpdirSync, toMatchNodeModulesAt } from "../../../harness"; import { Subprocess } from "bun"; -import { copyFileSync, rmSync } from "fs"; +import { install_test_helpers } from "bun:internal-for-testing"; +import { afterAll, beforeAll, expect, test } from "bun:test"; +import { copyFileSync } from "fs"; +import { cp, rm } from "fs/promises"; import { join } from "path"; import { StringDecoder } from "string_decoder"; -import { cp, rm } from "fs/promises"; -import { install_test_helpers } from "bun:internal-for-testing"; +import { bunEnv, bunExe, tmpdirSync, toMatchNodeModulesAt } from "../../../harness"; const { parseLockfile } = install_test_helpers; expect.extend({ toMatchNodeModulesAt }); @@ -96,7 +96,8 @@ beforeAll(async () => { stdin: "inherit", }); if (!install.success) { - throw new Error("Failed to install dependencies"); + const reason = install.signalCode || `code ${install.exitCode}`; + throw new Error(`Failed to install dependencies: ${reason}`); } try { diff --git a/test/integration/next-pages/test/dev-server.test.ts b/test/integration/next-pages/test/dev-server.test.ts index 0c51b810dc2d6..ae7fadb38dcb6 100644 --- a/test/integration/next-pages/test/dev-server.test.ts +++ b/test/integration/next-pages/test/dev-server.test.ts @@ -1,11 +1,11 @@ -import { afterAll, beforeAll, expect, test } from "bun:test"; -import { bunEnv, bunExe, isCI, isWindows, tmpdirSync, toMatchNodeModulesAt } from "../../../harness"; import { Subprocess } from "bun"; +import { install_test_helpers } from "bun:internal-for-testing"; +import { afterAll, beforeAll, expect, test } from "bun:test"; import { copyFileSync } from "fs"; +import { cp, rm } from "fs/promises"; import { join } from "path"; import { StringDecoder } from "string_decoder"; -import { cp, rm } from "fs/promises"; -import { install_test_helpers } from "bun:internal-for-testing"; +import { bunEnv, bunExe, isCI, isWindows, tmpdirSync, toMatchNodeModulesAt } from "../../../harness"; const { parseLockfile } = install_test_helpers; expect.extend({ toMatchNodeModulesAt }); @@ -96,7 +96,8 @@ beforeAll(async () => { stdin: "inherit", }); if (!install.success) { - throw new Error("Failed to install dependencies"); + const reason = install.signalCode || `code ${install.exitCode}`; + throw new Error(`Failed to install dependencies: ${reason}`); } try { diff --git a/test/integration/next-pages/test/next-build.test.ts b/test/integration/next-pages/test/next-build.test.ts index aac96e7f890e5..a94e50dc5c5fc 100644 --- a/test/integration/next-pages/test/next-build.test.ts +++ b/test/integration/next-pages/test/next-build.test.ts @@ -1,9 +1,9 @@ +import { install_test_helpers } from "bun:internal-for-testing"; import { expect, test } from "bun:test"; -import { bunEnv, bunExe, tmpdirSync, toMatchNodeModulesAt } from "../../../harness"; -import { copyFileSync, cpSync, readFileSync, rmSync, promises as fs } from "fs"; -import { join } from "path"; +import { copyFileSync, cpSync, promises as fs, readFileSync, rmSync } from "fs"; import { cp } from "fs/promises"; -import { install_test_helpers } from "bun:internal-for-testing"; +import { join } from "path"; +import { bunEnv, bunExe, isDebug, tmpdirSync, toMatchNodeModulesAt } from "../../../harness"; const { parseLockfile } = install_test_helpers; expect.extend({ toMatchNodeModulesAt }); @@ -27,15 +27,16 @@ async function tempDirToBuildIn() { cpSync(join(root, "src/Counter1.txt"), join(dir, "src/Counter.tsx")); cpSync(join(root, "tsconfig_for_build.json"), join(dir, "tsconfig.json")); - const install = Bun.spawn([bunExe(), "i"], { + const install = Bun.spawnSync([bunExe(), "i"], { cwd: dir, env: bunEnv, stdin: "inherit", stdout: "inherit", stderr: "inherit", }); - if ((await install.exited) !== 0) { - throw new Error("Failed to install dependencies"); + if (!install.success) { + const reason = install.signalCode || `code ${install.exitCode}`; + throw new Error(`Failed to install dependencies: ${reason}`); } return dir; @@ -82,107 +83,111 @@ function normalizeOutput(stdout: string) { ); } -test("next build works", async () => { - rmSync(join(root, ".next"), { recursive: true, force: true }); - copyFileSync(join(root, "src/Counter1.txt"), join(root, "src/Counter.tsx")); - - const bunDir = await tempDirToBuildIn(); - let lockfile = parseLockfile(bunDir); - expect(lockfile).toMatchNodeModulesAt(bunDir); - expect(parseLockfile(bunDir)).toMatchSnapshot("bun"); - - const nodeDir = await tempDirToBuildIn(); - lockfile = parseLockfile(nodeDir); - expect(lockfile).toMatchNodeModulesAt(nodeDir); - expect(lockfile).toMatchSnapshot("node"); - - console.log("Bun Dir: " + bunDir); - console.log("Node Dir: " + nodeDir); - - const nextPath = "node_modules/next/dist/bin/next"; - - console.time("[bun] next build"); - const bunBuild = Bun.spawn([bunExe(), "--bun", nextPath, "build"], { - cwd: bunDir, - stdio: ["ignore", "pipe", "inherit"], - env: { - ...bunEnv, - NODE_ENV: "production", - }, - }); - - console.time("[node] next build"); - const nodeBuild = Bun.spawn(["node", nextPath, "build"], { - cwd: nodeDir, - env: { ...bunEnv, NODE_NO_WARNINGS: "1", NODE_ENV: "production" }, - stdio: ["ignore", "pipe", "inherit"], - }); - await Promise.all([ - bunBuild.exited.then(a => { - console.timeEnd("[bun] next build"); - return a; - }), - nodeBuild.exited.then(a => { - console.timeEnd("[node] next build"); - return a; - }), - ]); - expect(nodeBuild.exitCode).toBe(0); - expect(bunBuild.exitCode).toBe(0); - - const bunCliOutput = normalizeOutput(await new Response(bunBuild.stdout).text()); - const nodeCliOutput = normalizeOutput(await new Response(nodeBuild.stdout).text()); - - console.log("bun", bunCliOutput); - console.log("node", nodeCliOutput); - - expect(bunCliOutput).toBe(nodeCliOutput); - - const bunBuildDir = join(bunDir, ".next"); - const nodeBuildDir = join(nodeDir, ".next"); - - // Remove some build files that Next.js does not make deterministic. - const toRemove = [ - // these have timestamps and absolute paths in them - "trace", - "cache", - "required-server-files.json", - // these have "signing keys", not sure what they are tbh - "prerender-manifest.json", - "prerender-manifest.js", - // these are similar but i feel like there might be something we can fix to make them the same - "next-minimal-server.js.nft.json", - "next-server.js.nft.json", - // this file is not deterministically sorted - "server/pages-manifest.json", - ]; - for (const key of toRemove) { - rmSync(join(bunBuildDir, key), { recursive: true }); - rmSync(join(nodeBuildDir, key), { recursive: true }); - } +test( + "next build works", + async () => { + rmSync(join(root, ".next"), { recursive: true, force: true }); + copyFileSync(join(root, "src/Counter1.txt"), join(root, "src/Counter.tsx")); + + const bunDir = await tempDirToBuildIn(); + let lockfile = parseLockfile(bunDir); + expect(lockfile).toMatchNodeModulesAt(bunDir); + expect(parseLockfile(bunDir)).toMatchSnapshot("bun"); + + const nodeDir = await tempDirToBuildIn(); + lockfile = parseLockfile(nodeDir); + expect(lockfile).toMatchNodeModulesAt(nodeDir); + expect(lockfile).toMatchSnapshot("node"); + + console.log("Bun Dir: " + bunDir); + console.log("Node Dir: " + nodeDir); + + const nextPath = "node_modules/next/dist/bin/next"; + + console.time("[bun] next build"); + const bunBuild = Bun.spawn([bunExe(), "--bun", nextPath, "build"], { + cwd: bunDir, + stdio: ["ignore", "pipe", "inherit"], + env: { + ...bunEnv, + NODE_ENV: "production", + }, + }); + + console.time("[node] next build"); + const nodeBuild = Bun.spawn(["node", nextPath, "build"], { + cwd: nodeDir, + env: { ...bunEnv, NODE_NO_WARNINGS: "1", NODE_ENV: "production" }, + stdio: ["ignore", "pipe", "inherit"], + }); + await Promise.all([ + bunBuild.exited.then(a => { + console.timeEnd("[bun] next build"); + return a; + }), + nodeBuild.exited.then(a => { + console.timeEnd("[node] next build"); + return a; + }), + ]); + expect(nodeBuild.exitCode).toBe(0); + expect(bunBuild.exitCode).toBe(0); + + const bunCliOutput = normalizeOutput(await new Response(bunBuild.stdout).text()); + const nodeCliOutput = normalizeOutput(await new Response(nodeBuild.stdout).text()); + + console.log("bun", bunCliOutput); + console.log("node", nodeCliOutput); + + expect(bunCliOutput).toBe(nodeCliOutput); + + const bunBuildDir = join(bunDir, ".next"); + const nodeBuildDir = join(nodeDir, ".next"); + + // Remove some build files that Next.js does not make deterministic. + const toRemove = [ + // these have timestamps and absolute paths in them + "trace", + "cache", + "required-server-files.json", + // these have "signing keys", not sure what they are tbh + "prerender-manifest.json", + "prerender-manifest.js", + // these are similar but i feel like there might be something we can fix to make them the same + "next-minimal-server.js.nft.json", + "next-server.js.nft.json", + // this file is not deterministically sorted + "server/pages-manifest.json", + ]; + for (const key of toRemove) { + rmSync(join(bunBuildDir, key), { recursive: true }); + rmSync(join(nodeBuildDir, key), { recursive: true }); + } - console.log("Hashing files..."); - const [bunBuildHash, nodeBuildHash] = await Promise.all([hashAllFiles(bunBuildDir), hashAllFiles(nodeBuildDir)]); - - try { - expect(bunBuildHash).toEqual(nodeBuildHash); - } catch (error) { - console.log("bunBuildDir", bunBuildDir); - console.log("nodeBuildDir", nodeBuildDir); - - // print diffs for every file if not the same - for (const key in bunBuildHash) { - if (bunBuildHash[key] !== nodeBuildHash[key]) { - console.log(key + ":"); - try { - expect(readFileSync(join(bunBuildDir, key)).toString()).toBe( - readFileSync(join(nodeBuildDir, key)).toString(), - ); - } catch (error) { - console.error(error); + console.log("Hashing files..."); + const [bunBuildHash, nodeBuildHash] = await Promise.all([hashAllFiles(bunBuildDir), hashAllFiles(nodeBuildDir)]); + + try { + expect(bunBuildHash).toEqual(nodeBuildHash); + } catch (error) { + console.log("bunBuildDir", bunBuildDir); + console.log("nodeBuildDir", nodeBuildDir); + + // print diffs for every file if not the same + for (const key in bunBuildHash) { + if (bunBuildHash[key] !== nodeBuildHash[key]) { + console.log(key + ":"); + try { + expect(readFileSync(join(bunBuildDir, key)).toString()).toBe( + readFileSync(join(nodeBuildDir, key)).toString(), + ); + } catch (error) { + console.error(error); + } } } + throw error; } - throw error; - } -}, 60_0000); + }, + isDebug ? Infinity : 60_0000, +); diff --git a/test/internal/highlighter.test.ts b/test/internal/highlighter.test.ts new file mode 100644 index 0000000000000..e45e73ca9fc3a --- /dev/null +++ b/test/internal/highlighter.test.ts @@ -0,0 +1,7 @@ +import { quickAndDirtyJavaScriptSyntaxHighlighter as highlighter } from "bun:internal-for-testing"; +import { expect, test } from "bun:test"; + +test("highlighter", () => { + expect(highlighter("`can do ${123} ${'123'} ${`123`}`").length).toBeLessThan(150); + expect(highlighter("`can do ${123} ${'123'} ${`123`}`123").length).toBeLessThan(150); +}); diff --git a/test/internal/powershell-escape.test.ts b/test/internal/powershell-escape.test.ts new file mode 100644 index 0000000000000..e81c02ed3eaea --- /dev/null +++ b/test/internal/powershell-escape.test.ts @@ -0,0 +1,10 @@ +import { escapePowershell } from "bun:internal-for-testing"; + +it("powershell escaping rules", () => { + // This formatter does not include quotes around the string intentionally + expect(escapePowershell("foo")).toBe("foo"); + expect(escapePowershell("foo bar")).toBe("foo bar"); + expect(escapePowershell('foo" bar')).toBe('foo`" bar'); + expect(escapePowershell('foo" `bar')).toBe('foo`" ``bar'); + expect(escapePowershell('foo" ``"bar')).toBe('foo`" `````"bar'); +}); diff --git a/test/js/bun/console/console-iterator.test.ts b/test/js/bun/console/console-iterator.test.ts index 48c8c418f1c3d..5b93b8c20201b 100644 --- a/test/js/bun/console/console-iterator.test.ts +++ b/test/js/bun/console/console-iterator.test.ts @@ -1,4 +1,4 @@ -import { spawnSync, spawn } from "bun"; +import { spawn, spawnSync } from "bun"; import { describe, expect, it } from "bun:test"; import { bunEnv, bunExe } from "harness"; diff --git a/test/js/bun/console/console-table.test.ts b/test/js/bun/console/console-table.test.ts index ca251c0024bf5..22d780ac82b04 100644 --- a/test/js/bun/console/console-table.test.ts +++ b/test/js/bun/console/console-table.test.ts @@ -1,6 +1,6 @@ -import { describe, expect, test } from "bun:test"; import { spawnSync } from "bun"; -import { bunExe, bunEnv } from "harness"; +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; describe("console.table", () => { test("throws when second arg is invalid", () => { diff --git a/test/js/bun/crypto/cipheriv-decipheriv.test.ts b/test/js/bun/crypto/cipheriv-decipheriv.test.ts index 0c220730c7464..01d94a7b0ba72 100644 --- a/test/js/bun/crypto/cipheriv-decipheriv.test.ts +++ b/test/js/bun/crypto/cipheriv-decipheriv.test.ts @@ -1,5 +1,5 @@ -import { BinaryLike, DecipherGCM, CipherGCM, createCipheriv, createDecipheriv, randomBytes } from "crypto"; -import { it, expect } from "bun:test"; +import { expect, it } from "bun:test"; +import { BinaryLike, CipherGCM, createCipheriv, createDecipheriv, DecipherGCM, randomBytes } from "crypto"; /** * Perform a sample encryption and decryption diff --git a/test/js/bun/dns/resolve-dns.test.ts b/test/js/bun/dns/resolve-dns.test.ts index 48352278278c4..85edc371301a8 100644 --- a/test/js/bun/dns/resolve-dns.test.ts +++ b/test/js/bun/dns/resolve-dns.test.ts @@ -1,12 +1,12 @@ import { SystemError, dns } from "bun"; -import { describe, expect, it, test } from "bun:test"; +import { describe, expect, test } from "bun:test"; import { withoutAggressiveGC } from "harness"; import { isIP, isIPv4, isIPv6 } from "node:net"; const backends = ["system", "libc", "c-ares"]; const validHostnames = ["localhost", "example.com"]; const invalidHostnames = ["adsfa.asdfasdf.asdf.com"]; // known invalid -const malformedHostnames = ["", " ", ".", " .", "localhost:80", "this is not a hostname"]; +const malformedHostnames = [" ", ".", " .", "localhost:80", "this is not a hostname"]; const isWindows = process.platform === "win32"; describe("dns", () => { describe.each(backends)("lookup() [backend: %s]", backend => { @@ -100,14 +100,16 @@ describe("dns", () => { // @ts-expect-error expect(dns.lookup(hostname, { backend })).rejects.toMatchObject({ code: "DNS_ENOTFOUND", + name: "DNSException", + }); + }); + + test.each(malformedHostnames)("'%s'", hostname => { + // @ts-expect-error + expect(dns.lookup(hostname, { backend })).rejects.toMatchObject({ + code: expect.stringMatching(/^DNS_ENOTFOUND|DNS_ESERVFAIL|DNS_ENOTIMP$/), + name: "DNSException", }); }); - // TODO: causes segfaults - // test.each(malformedHostnames)("%s", (hostname) => { - // // @ts-expect-error - // expect(dns.lookup(hostname, { backend })).rejects.toMatchObject({ - // code: "DNS_ENOTFOUND", - // }); - // }); }); }); diff --git a/test/js/bun/ffi/__snapshots__/cc.test.ts.snap b/test/js/bun/ffi/__snapshots__/cc.test.ts.snap new file mode 100644 index 0000000000000..7f08a1de50080 --- /dev/null +++ b/test/js/bun/ffi/__snapshots__/cc.test.ts.snap @@ -0,0 +1,10 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`can run a .c file: cc-fixture-stderr 1`] = `"Hello, World!"`; + +exports[`can run a .c file: cc-fixture-stdout 1`] = ` +"Hello, World! +Hello, World! +Hi!, 123 == 123 +bool true = 1, bool false = 0" +`; diff --git a/test/js/bun/ffi/cc-fixture.c b/test/js/bun/ffi/cc-fixture.c new file mode 100644 index 0000000000000..e39a15c784796 --- /dev/null +++ b/test/js/bun/ffi/cc-fixture.c @@ -0,0 +1,49 @@ +// Ensure we can include builtin headers. +#include +#include +#include +#include +#include +#if __has_include() +#include +#endif + +#if __has_include() + +#include + +napi_value napi_main(napi_env env) { + napi_value result; + napi_create_string_utf8(env, "Hello, Napi!", NAPI_AUTO_LENGTH, &result); + return result; +} + +#endif + +int main() { + +#if __has_include() + // Check fprint stdout and stderr. + fprintf(stdout, "Hello, World!\n"); + fprintf(stderr, "Hello, World!\n"); + + // Verify printf doesn't crash. + printf("Hello, World!\n"); + printf("Hi!, 123 == %d\n", 123); +#endif + + // Verify stdbool.h works. + bool g = true; + bool h = false; +#if __has_include() + printf("bool true = %d, bool false = %d\n", (int)g, (int)h); +#endif + +#ifdef HAS_MY_DEFINE +#if (__has_include()) + printf("HAS_MY_DEFINE is defined as %s\n", HAS_MY_DEFINE); +#endif +#endif + + return 42; +} \ No newline at end of file diff --git a/test/js/bun/ffi/cc-fixture.js b/test/js/bun/ffi/cc-fixture.js new file mode 100644 index 0000000000000..b3d4d642c4f72 --- /dev/null +++ b/test/js/bun/ffi/cc-fixture.js @@ -0,0 +1,29 @@ +import { cc } from "bun:ffi"; +import fixture from "./cc-fixture.c" with { type: "file" }; +const { + symbols: { napi_main, main }, +} = cc({ + source: fixture, + define: { + "HAS_MY_DEFINE": '"my value"', + }, + + symbols: { + "napi_main": { + args: ["napi_env"], + returns: "napi_value", + }, + "main": { + args: [], + returns: "int", + }, + }, +}); + +if (main() !== 42) { + throw new Error("main() !== 42"); +} + +if (napi_main(null) !== "Hello, Napi!") { + throw new Error("napi_main() !== Hello, Napi!"); +} diff --git a/test/js/bun/ffi/cc.test.ts b/test/js/bun/ffi/cc.test.ts new file mode 100644 index 0000000000000..8e90b8ec52183 --- /dev/null +++ b/test/js/bun/ffi/cc.test.ts @@ -0,0 +1,16 @@ +import { expect, it } from "bun:test"; +import { bunEnv, bunExe, isWindows } from "harness"; +import path from "path"; + +// TODO: we need to install build-essential and apple SDK in CI. +// it can't find includes. It can on machiens with that enabled. +it.todoIf(isWindows)("can run a .c file", () => { + const result = Bun.spawnSync({ + cmd: [bunExe(), path.join(__dirname, "cc-fixture.js")], + cwd: __dirname, + env: bunEnv, + stdio: ["inherit", "inherit", "inherit"], + }); + + expect(result.exitCode).toBe(0); +}); diff --git a/test/js/bun/ffi/ffi-test.c b/test/js/bun/ffi/ffi-test.c index 0fe227385267d..a71021423d7c7 100644 --- a/test/js/bun/ffi/ffi-test.c +++ b/test/js/bun/ffi/ffi-test.c @@ -3,60 +3,67 @@ #include #include -bool returns_true(); -bool returns_false(); -char returns_42_char(); -float returns_42_float(); -double returns_42_double(); -uint8_t returns_42_uint8_t(); -int8_t returns_neg_42_int8_t(); -uint16_t returns_42_uint16_t(); -uint32_t returns_42_uint32_t(); -uint64_t returns_42_uint64_t(); -int16_t returns_neg_42_int16_t(); -int32_t returns_neg_42_int32_t(); -int64_t returns_neg_42_int64_t(); +#ifdef _WIN32 +#define FFI_EXPORT __declspec(dllexport) +#else +#define FFI_EXPORT __attribute__((visibility("default"))) +#endif -bool cb_identity_true(bool (*cb)()); -bool cb_identity_false(bool (*cb)()); -char cb_identity_42_char(char (*cb)()); -float cb_identity_42_float(float (*cb)()); -double cb_identity_42_double(double (*cb)()); -uint8_t cb_identity_42_uint8_t(uint8_t (*cb)()); -int8_t cb_identity_neg_42_int8_t(int8_t (*cb)()); -uint16_t cb_identity_42_uint16_t(uint16_t (*cb)()); -uint32_t cb_identity_42_uint32_t(uint32_t (*cb)()); -uint64_t cb_identity_42_uint64_t(uint64_t (*cb)()); -int16_t cb_identity_neg_42_int16_t(int16_t (*cb)()); -int32_t cb_identity_neg_42_int32_t(int32_t (*cb)()); -int64_t cb_identity_neg_42_int64_t(int64_t (*cb)()); +FFI_EXPORT bool returns_true(); +FFI_EXPORT bool returns_false(); +FFI_EXPORT char returns_42_char(); +FFI_EXPORT float returns_42_float(); +FFI_EXPORT double returns_42_double(); +FFI_EXPORT uint8_t returns_42_uint8_t(); +FFI_EXPORT int8_t returns_neg_42_int8_t(); +FFI_EXPORT uint16_t returns_42_uint16_t(); +FFI_EXPORT uint32_t returns_42_uint32_t(); +FFI_EXPORT uint64_t returns_42_uint64_t(); +FFI_EXPORT int16_t returns_neg_42_int16_t(); +FFI_EXPORT int32_t returns_neg_42_int32_t(); +FFI_EXPORT int64_t returns_neg_42_int64_t(); -bool identity_bool_true(); -bool identity_bool_false(); -char identity_char(char a); -float identity_float(float a); -bool identity_bool(bool ident); -double identity_double(double a); -int8_t identity_int8_t(int8_t a); -int16_t identity_int16_t(int16_t a); -int32_t identity_int32_t(int32_t a); -int64_t identity_int64_t(int64_t a); -uint8_t identity_uint8_t(uint8_t a); -uint16_t identity_uint16_t(uint16_t a); -uint32_t identity_uint32_t(uint32_t a); -uint64_t identity_uint64_t(uint64_t a); +FFI_EXPORT bool cb_identity_true(bool (*cb)()); +FFI_EXPORT bool cb_identity_false(bool (*cb)()); +FFI_EXPORT char cb_identity_42_char(char (*cb)()); +FFI_EXPORT float cb_identity_42_float(float (*cb)()); +FFI_EXPORT double cb_identity_42_double(double (*cb)()); +FFI_EXPORT uint8_t cb_identity_42_uint8_t(uint8_t (*cb)()); +FFI_EXPORT int8_t cb_identity_neg_42_int8_t(int8_t (*cb)()); +FFI_EXPORT uint16_t cb_identity_42_uint16_t(uint16_t (*cb)()); +FFI_EXPORT uint32_t cb_identity_42_uint32_t(uint32_t (*cb)()); +FFI_EXPORT uint64_t cb_identity_42_uint64_t(uint64_t (*cb)()); +FFI_EXPORT int16_t cb_identity_neg_42_int16_t(int16_t (*cb)()); +FFI_EXPORT int32_t cb_identity_neg_42_int32_t(int32_t (*cb)()); +FFI_EXPORT int64_t cb_identity_neg_42_int64_t(int64_t (*cb)()); -char add_char(char a, char b); -float add_float(float a, float b); -double add_double(double a, double b); -int8_t add_int8_t(int8_t a, int8_t b); -int16_t add_int16_t(int16_t a, int16_t b); -int32_t add_int32_t(int32_t a, int32_t b); -int64_t add_int64_t(int64_t a, int64_t b); -uint8_t add_uint8_t(uint8_t a, uint8_t b); -uint16_t add_uint16_t(uint16_t a, uint16_t b); -uint32_t add_uint32_t(uint32_t a, uint32_t b); -uint64_t add_uint64_t(uint64_t a, uint64_t b); +FFI_EXPORT bool identity_bool_true(); +FFI_EXPORT bool identity_bool_false(); +FFI_EXPORT char identity_char(char a); +FFI_EXPORT float identity_float(float a); +FFI_EXPORT bool identity_bool(bool ident); +FFI_EXPORT double identity_double(double a); +FFI_EXPORT int8_t identity_int8_t(int8_t a); +FFI_EXPORT int16_t identity_int16_t(int16_t a); +FFI_EXPORT int32_t identity_int32_t(int32_t a); +FFI_EXPORT int64_t identity_int64_t(int64_t a); +FFI_EXPORT uint8_t identity_uint8_t(uint8_t a); +FFI_EXPORT uint16_t identity_uint16_t(uint16_t a); +FFI_EXPORT uint32_t identity_uint32_t(uint32_t a); +FFI_EXPORT uint64_t identity_uint64_t(uint64_t a); +FFI_EXPORT void *identity_ptr(void *ident); + +FFI_EXPORT char add_char(char a, char b); +FFI_EXPORT float add_float(float a, float b); +FFI_EXPORT double add_double(double a, double b); +FFI_EXPORT int8_t add_int8_t(int8_t a, int8_t b); +FFI_EXPORT int16_t add_int16_t(int16_t a, int16_t b); +FFI_EXPORT int32_t add_int32_t(int32_t a, int32_t b); +FFI_EXPORT int64_t add_int64_t(int64_t a, int64_t b); +FFI_EXPORT uint8_t add_uint8_t(uint8_t a, uint8_t b); +FFI_EXPORT uint16_t add_uint16_t(uint16_t a, uint16_t b); +FFI_EXPORT uint32_t add_uint32_t(uint32_t a, uint32_t b); +FFI_EXPORT uint64_t add_uint64_t(uint64_t a, uint64_t b); bool returns_false() { return false; } bool returns_true() { return true; } @@ -98,7 +105,8 @@ uint16_t add_uint16_t(uint16_t a, uint16_t b) { return a + b; } uint32_t add_uint32_t(uint32_t a, uint32_t b) { return a + b; } uint64_t add_uint64_t(uint64_t a, uint64_t b) { return a + b; } -void *ptr_should_point_to_42_as_int32_t(); +FFI_EXPORT void *ptr_should_point_to_42_as_int32_t(); + void *ptr_should_point_to_42_as_int32_t() { int32_t *ptr = malloc(sizeof(int32_t)); *ptr = 42; @@ -107,37 +115,37 @@ void *ptr_should_point_to_42_as_int32_t() { static uint8_t buffer_with_deallocator[128]; static int deallocatorCalled; -void deallocator(void *ptr, void *userData) { deallocatorCalled++; } -void *getDeallocatorCallback() { +FFI_EXPORT void deallocator(void *ptr, void *userData) { deallocatorCalled++; } +FFI_EXPORT void *getDeallocatorCallback() { deallocatorCalled = 0; return &deallocator; } -void *getDeallocatorBuffer() { +FFI_EXPORT void *getDeallocatorBuffer() { deallocatorCalled = 0; return &buffer_with_deallocator; } -int getDeallocatorCalledCount() { return deallocatorCalled; } +FFI_EXPORT int getDeallocatorCalledCount() { return deallocatorCalled; } -bool is_null(int32_t *ptr) { return ptr == NULL; } -bool does_pointer_equal_42_as_int32_t(int32_t *ptr); +FFI_EXPORT bool is_null(int32_t *ptr) { return ptr == NULL; } +FFI_EXPORT bool does_pointer_equal_42_as_int32_t(int32_t *ptr); bool does_pointer_equal_42_as_int32_t(int32_t *ptr) { return *ptr == 42; } -void *return_a_function_ptr_to_function_that_returns_true(); +FFI_EXPORT void *return_a_function_ptr_to_function_that_returns_true(); void *return_a_function_ptr_to_function_that_returns_true() { return (void *)&returns_true; } -bool cb_identity_true(bool (*cb)()) { return cb(); } +FFI_EXPORT bool cb_identity_true(bool (*cb)()) { return cb(); } -bool cb_identity_false(bool (*cb)()) { return cb(); } -char cb_identity_42_char(char (*cb)()) { return cb(); } -float cb_identity_42_float(float (*cb)()) { return cb(); } -double cb_identity_42_double(double (*cb)()) { return cb(); } -uint8_t cb_identity_42_uint8_t(uint8_t (*cb)()) { return cb(); } -int8_t cb_identity_neg_42_int8_t(int8_t (*cb)()) { return cb(); } -uint16_t cb_identity_42_uint16_t(uint16_t (*cb)()) { return cb(); } -uint32_t cb_identity_42_uint32_t(uint32_t (*cb)()) { return cb(); } -uint64_t cb_identity_42_uint64_t(uint64_t (*cb)()) { return cb(); } -int16_t cb_identity_neg_42_int16_t(int16_t (*cb)()) { return cb(); } -int32_t cb_identity_neg_42_int32_t(int32_t (*cb)()) { return cb(); } -int64_t cb_identity_neg_42_int64_t(int64_t (*cb)()) { return cb(); } \ No newline at end of file +FFI_EXPORT bool cb_identity_false(bool (*cb)()) { return cb(); } +FFI_EXPORT char cb_identity_42_char(char (*cb)()) { return cb(); } +FFI_EXPORT float cb_identity_42_float(float (*cb)()) { return cb(); } +FFI_EXPORT double cb_identity_42_double(double (*cb)()) { return cb(); } +FFI_EXPORT uint8_t cb_identity_42_uint8_t(uint8_t (*cb)()) { return cb(); } +FFI_EXPORT int8_t cb_identity_neg_42_int8_t(int8_t (*cb)()) { return cb(); } +FFI_EXPORT uint16_t cb_identity_42_uint16_t(uint16_t (*cb)()) { return cb(); } +FFI_EXPORT uint32_t cb_identity_42_uint32_t(uint32_t (*cb)()) { return cb(); } +FFI_EXPORT uint64_t cb_identity_42_uint64_t(uint64_t (*cb)()) { return cb(); } +FFI_EXPORT int16_t cb_identity_neg_42_int16_t(int16_t (*cb)()) { return cb(); } +FFI_EXPORT int32_t cb_identity_neg_42_int32_t(int32_t (*cb)()) { return cb(); } +FFI_EXPORT int64_t cb_identity_neg_42_int64_t(int64_t (*cb)()) { return cb(); } \ No newline at end of file diff --git a/test/js/bun/ffi/ffi.test.fixture.callback.c b/test/js/bun/ffi/ffi.test.fixture.callback.c index 1b04ffcc0fc3f..3807160af40ff 100644 --- a/test/js/bun/ffi/ffi.test.fixture.callback.c +++ b/test/js/bun/ffi/ffi.test.fixture.callback.c @@ -2,12 +2,12 @@ #define IS_CALLBACK 1 // This file is part of Bun! // You can find the original source: -// https://github.com/oven-sh/bun/blob/main/src/bun.js/api/FFI.h#L2 +// https://github.com/oven-sh/bun/blob/main/src/bun.js/api/FFI.h // // clang-format off // This file is only compatible with 64 bit CPUs // It must be kept in sync with JSCJSValue.h -// https://github.com/oven-sh/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458 +// https://github.com/oven-sh/WebKit/blob/main/Source/JavaScriptCore/runtime/JSCJSValue.h #ifdef IS_CALLBACK #define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call #endif @@ -292,9 +292,6 @@ ZIG_REPR_TYPE JSFunctionCall(void* jsGlobalObject, void* callFrame); /* --- The Callback Function */ -/* --- The Callback Function */ -bool my_callback_function(void* arg0); - bool my_callback_function(void* arg0) { ZIG_REPR_TYPE arguments[1]; arguments[0] = PTR_TO_JSVALUE(arg0).asZigRepr; diff --git a/test/js/bun/ffi/ffi.test.fixture.receiver.c b/test/js/bun/ffi/ffi.test.fixture.receiver.c index efbb3490cc654..6d448c806dc70 100644 --- a/test/js/bun/ffi/ffi.test.fixture.receiver.c +++ b/test/js/bun/ffi/ffi.test.fixture.receiver.c @@ -2,12 +2,12 @@ #define USES_FLOAT 1 // This file is part of Bun! // You can find the original source: -// https://github.com/oven-sh/bun/blob/main/src/bun.js/api/FFI.h#L2 +// https://github.com/oven-sh/bun/blob/main/src/bun.js/api/FFI.h // // clang-format off // This file is only compatible with 64 bit CPUs // It must be kept in sync with JSCJSValue.h -// https://github.com/oven-sh/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458 +// https://github.com/oven-sh/WebKit/blob/main/Source/JavaScriptCore/runtime/JSCJSValue.h #ifdef IS_CALLBACK #define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call #endif diff --git a/test/js/bun/ffi/ffi.test.js b/test/js/bun/ffi/ffi.test.js index 7a6c00ca18134..39782240bfadb 100644 --- a/test/js/bun/ffi/ffi.test.js +++ b/test/js/bun/ffi/ffi.test.js @@ -1,18 +1,19 @@ import { afterAll, describe, expect, it } from "bun:test"; import { existsSync } from "fs"; +import { isGlibcVersionAtLeast } from "harness"; import { platform } from "os"; import { + dlopen as _dlopen, CFunction, CString, - dlopen as _dlopen, JSCallback, ptr, read, + suffix, toArrayBuffer, toBuffer, viewSource, - suffix, } from "bun:ffi"; const dlopen = (...args) => { @@ -674,304 +675,304 @@ it(".ptr is not leaked", () => { } }); -const lib_path = +const libPath = platform() === "darwin" ? "/usr/lib/libSystem.B.dylib" - : existsSync("/lib/x86_64-linux-gnu/libc.so.6") + : existsSync("/lib/x86_64-linux-gnu/libc.so.6") && isGlibcVersionAtLeast("2.36.0") ? "/lib/x86_64-linux-gnu/libc.so.6" : null; -const test = lib_path ? describe : describe.skip; -test("can open more than 63 symbols via", () => { - if (lib_path) { - for (const [libPath, description] of [ - // For file: URLs since one might do import.meta.resolve() - [Bun.pathToFileURL(lib_path), "URL"], - - // file: URLs as a string - [Bun.pathToFileURL(lib_path).href, "file: URL"], - - // For embedding files since one might do Bun.file(embeddedFile) - [Bun.file(lib_path), "Bun.file"], - - // For file path strings - [lib_path, "string"], - ]) { - it(description, () => { - // test file: URL works - const lib = dlopen(libPath, { - memchr: { - returns: "ptr", - args: ["ptr", "int", "usize"], - }, - strcpy: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - strcat: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - strncat: { - returns: "ptr", - args: ["ptr", "ptr", "usize"], - }, - strcmp: { - returns: "int", - args: ["ptr", "ptr"], - }, - strncmp: { - returns: "int", - args: ["ptr", "ptr", "usize"], - }, - strcoll: { - returns: "int", - args: ["ptr", "ptr"], - }, - strxfrm: { - returns: "int", - args: ["ptr", "ptr", "usize"], - }, - strchr: { - returns: "ptr", - args: ["ptr", "int"], - }, - strrchr: { - returns: "ptr", - args: ["ptr", "int"], - }, - strcspn: { - returns: "usize", - args: ["ptr", "ptr"], - }, - strspn: { - returns: "usize", - args: ["ptr", "ptr"], - }, - strpbrk: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - strstr: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - strtok: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - strerror: { - returns: "ptr", - args: ["int"], - }, - strerror_r: { - returns: "ptr", - args: ["int", "ptr", "usize"], - }, - strsep: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - strsignal: { - returns: "ptr", - args: ["int"], - }, - stpcpy: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - stpncpy: { - returns: "ptr", - args: ["ptr", "ptr", "usize"], - }, - basename: { - returns: "ptr", - args: ["ptr"], - }, - bcmp: { - returns: "int", - args: ["ptr", "ptr", "usize"], - }, - getdate: { - returns: "ptr", - args: ["ptr"], - }, - gmtime: { - returns: "ptr", - args: ["ptr"], - }, - localtime: { - returns: "ptr", - args: ["ptr"], - }, - ctime: { - returns: "ptr", - args: ["ptr"], - }, - asctime: { - returns: "ptr", - args: ["ptr"], - }, - strftime: { - returns: "usize", - args: ["ptr", "usize", "ptr", "ptr"], - }, - strptime: { - returns: "ptr", - args: ["ptr", "ptr", "ptr"], - }, - asctime_r: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - ctime_r: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - gmtime_r: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - localtime_r: { - returns: "ptr", - args: ["ptr", "ptr"], - }, - bcopy: { - returns: "int", - args: ["ptr", "ptr", "usize"], - }, - bzero: { - returns: "void", - args: ["ptr", "usize"], - }, - index: { - returns: "ptr", - args: ["ptr", "int"], - }, - rindex: { - returns: "ptr", - args: ["ptr", "int"], - }, - ffs: { - returns: "int", - args: ["int"], - }, - strcasecmp: { - returns: "int", - args: ["ptr", "ptr"], - }, - strncasecmp: { - returns: "int", - args: ["ptr", "ptr", "usize"], - }, - pthread_attr_init: { - returns: "int", - args: ["ptr"], - }, - pthread_attr_destroy: { - returns: "int", - args: ["ptr"], - }, - pthread_attr_getdetachstate: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setdetachstate: { - returns: "int", - args: ["ptr", "int"], - }, - pthread_attr_getguardsize: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setguardsize: { - returns: "int", - args: ["ptr", "usize"], - }, - pthread_attr_getschedparam: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setschedparam: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_getschedpolicy: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setschedpolicy: { - returns: "int", - args: ["ptr", "int"], - }, - pthread_attr_getinheritsched: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setinheritsched: { - returns: "int", - args: ["ptr", "int"], - }, - pthread_attr_getscope: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setscope: { - returns: "int", - args: ["ptr", "int"], - }, - pthread_attr_getstackaddr: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setstackaddr: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_getstacksize: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setstacksize: { - returns: "int", - args: ["ptr", "usize"], - }, - pthread_attr_getstack: { - returns: "int", - args: ["ptr", "ptr", "ptr"], - }, - pthread_attr_setstack: { - returns: "int", - args: ["ptr", "ptr", "usize"], - }, - pthread_attr_getguardsize: { - returns: "int", - args: ["ptr", "ptr"], - }, - pthread_attr_setguardsize: { - returns: "int", - args: ["ptr", "usize"], - }, - login_tty: { - returns: "int", - args: ["int"], - }, - login: { - returns: "int", - args: ["ptr"], - }, - logout: { - returns: "int", - args: ["ptr"], - }, - strlen: { - returns: "usize", - args: ["ptr"], - }, - }); - expect(Object.keys(lib.symbols).length).toBe(65); - expect(lib.symbols.strcasecmp(Buffer.from("ciro\0"), Buffer.from("CIRO\0"))).toBe(0); - expect(lib.symbols.strlen(Buffer.from("bunbun\0", "ascii"))).toBe(6n); - }); - } + +const libSymbols = { + memchr: { + returns: "ptr", + args: ["ptr", "int", "usize"], + }, + strcpy: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + strcat: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + strncat: { + returns: "ptr", + args: ["ptr", "ptr", "usize"], + }, + strcmp: { + returns: "int", + args: ["ptr", "ptr"], + }, + strncmp: { + returns: "int", + args: ["ptr", "ptr", "usize"], + }, + strcoll: { + returns: "int", + args: ["ptr", "ptr"], + }, + strxfrm: { + returns: "int", + args: ["ptr", "ptr", "usize"], + }, + strchr: { + returns: "ptr", + args: ["ptr", "int"], + }, + strrchr: { + returns: "ptr", + args: ["ptr", "int"], + }, + strcspn: { + returns: "usize", + args: ["ptr", "ptr"], + }, + strspn: { + returns: "usize", + args: ["ptr", "ptr"], + }, + strpbrk: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + strstr: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + strtok: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + strerror: { + returns: "ptr", + args: ["int"], + }, + strerror_r: { + returns: "ptr", + args: ["int", "ptr", "usize"], + }, + strsep: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + strsignal: { + returns: "ptr", + args: ["int"], + }, + stpcpy: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + stpncpy: { + returns: "ptr", + args: ["ptr", "ptr", "usize"], + }, + basename: { + returns: "ptr", + args: ["ptr"], + }, + bcmp: { + returns: "int", + args: ["ptr", "ptr", "usize"], + }, + getdate: { + returns: "ptr", + args: ["ptr"], + }, + gmtime: { + returns: "ptr", + args: ["ptr"], + }, + localtime: { + returns: "ptr", + args: ["ptr"], + }, + ctime: { + returns: "ptr", + args: ["ptr"], + }, + asctime: { + returns: "ptr", + args: ["ptr"], + }, + strftime: { + returns: "usize", + args: ["ptr", "usize", "ptr", "ptr"], + }, + strptime: { + returns: "ptr", + args: ["ptr", "ptr", "ptr"], + }, + asctime_r: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + ctime_r: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + gmtime_r: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + localtime_r: { + returns: "ptr", + args: ["ptr", "ptr"], + }, + bcopy: { + returns: "int", + args: ["ptr", "ptr", "usize"], + }, + bzero: { + returns: "void", + args: ["ptr", "usize"], + }, + index: { + returns: "ptr", + args: ["ptr", "int"], + }, + rindex: { + returns: "ptr", + args: ["ptr", "int"], + }, + ffs: { + returns: "int", + args: ["int"], + }, + strcasecmp: { + returns: "int", + args: ["ptr", "ptr"], + }, + strncasecmp: { + returns: "int", + args: ["ptr", "ptr", "usize"], + }, + pthread_attr_init: { + returns: "int", + args: ["ptr"], + }, + pthread_attr_destroy: { + returns: "int", + args: ["ptr"], + }, + pthread_attr_getdetachstate: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setdetachstate: { + returns: "int", + args: ["ptr", "int"], + }, + pthread_attr_getguardsize: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setguardsize: { + returns: "int", + args: ["ptr", "usize"], + }, + pthread_attr_getschedparam: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setschedparam: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_getschedpolicy: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setschedpolicy: { + returns: "int", + args: ["ptr", "int"], + }, + pthread_attr_getinheritsched: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setinheritsched: { + returns: "int", + args: ["ptr", "int"], + }, + pthread_attr_getscope: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setscope: { + returns: "int", + args: ["ptr", "int"], + }, + pthread_attr_getstackaddr: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setstackaddr: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_getstacksize: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setstacksize: { + returns: "int", + args: ["ptr", "usize"], + }, + pthread_attr_getstack: { + returns: "int", + args: ["ptr", "ptr", "ptr"], + }, + pthread_attr_setstack: { + returns: "int", + args: ["ptr", "ptr", "usize"], + }, + pthread_attr_getguardsize: { + returns: "int", + args: ["ptr", "ptr"], + }, + pthread_attr_setguardsize: { + returns: "int", + args: ["ptr", "usize"], + }, + login_tty: { + returns: "int", + args: ["int"], + }, + login: { + returns: "int", + args: ["ptr"], + }, + logout: { + returns: "int", + args: ["ptr"], + }, + strlen: { + returns: "usize", + args: ["ptr"], + }, +}; + +describe.if(!!libPath)("can open more than 63 symbols via", () => { + for (const [description, libFn] of [ + // For file: URLs since one might do import.meta.resolve() + ["URL", () => Bun.pathToFileURL(libPath)], + + // file: URLs as a string + ["file: URL", () => Bun.pathToFileURL(libPath).href], + + // For embedding files since one might do Bun.file(embeddedFile) + ["Bun.file", () => Bun.file(libPath)], + + // For file path strings + ["string", () => libPath], + ]) { + it(description, () => { + const libPath = libFn(); + const lib = dlopen(libPath, libSymbols); + expect(Object.keys(lib.symbols).length).toBe(Object.keys(libSymbols).length); + expect(lib.symbols.strcasecmp(Buffer.from("ciro\0"), Buffer.from("CIRO\0"))).toBe(0); + expect(lib.symbols.strlen(Buffer.from("bunbun\0", "ascii"))).toBe(6n); + }); } }); diff --git a/test/js/bun/glob/__snapshots__/scan.test.ts.snap b/test/js/bun/glob/__snapshots__/scan.test.ts.snap index e756e12afb8a2..db6f6a636a9b6 100644 --- a/test/js/bun/glob/__snapshots__/scan.test.ts.snap +++ b/test/js/bun/glob/__snapshots__/scan.test.ts.snap @@ -972,7 +972,7 @@ exports[`fast-glob e2e tests (absolute) patterns absolute cwd *: absolute: * 1`] exports[`fast-glob e2e tests patterns absolute cwd *: absolute: * 1`] = ` [ - "", + "file.md", ] `; @@ -993,16 +993,16 @@ exports[`fast-glob e2e tests (absolute) patterns absolute cwd **: absolute: ** 1 exports[`fast-glob e2e tests patterns absolute cwd **: absolute: ** 1`] = ` [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", + "file.md", + "first/file.md", + "first/nested/directory/file.json", + "first/nested/directory/file.md", + "first/nested/file.md", + "second/file.md", + "second/nested/directory/file.md", + "second/nested/file.md", + "third/library/a/book.md", + "third/library/b/book.md", ] `; @@ -1023,16 +1023,16 @@ exports[`fast-glob e2e tests (absolute) patterns absolute cwd **/*: absolute: ** exports[`fast-glob e2e tests patterns absolute cwd **/*: absolute: **/* 1`] = ` [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", + "file.md", + "first/file.md", + "first/nested/directory/file.json", + "first/nested/directory/file.md", + "first/nested/file.md", + "second/file.md", + "second/nested/directory/file.md", + "second/nested/file.md", + "third/library/a/book.md", + "third/library/b/book.md", ] `; diff --git a/test/js/bun/glob/leak.test.ts b/test/js/bun/glob/leak.test.ts index 96db6523eee2a..7296e6f357fda 100644 --- a/test/js/bun/glob/leak.test.ts +++ b/test/js/bun/glob/leak.test.ts @@ -1,5 +1,4 @@ -import { expect, test, describe, beforeAll, afterAll } from "bun:test"; -import { Glob, GlobScanOptions } from "bun"; +import { describe, expect, test } from "bun:test"; describe("leaks", () => { const bun = process.argv[0]; diff --git a/test/js/bun/glob/match.test.ts b/test/js/bun/glob/match.test.ts index d3925990b2f31..c09f8b7cd0ea5 100644 --- a/test/js/bun/glob/match.test.ts +++ b/test/js/bun/glob/match.test.ts @@ -22,8 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import { expect, test, describe } from "bun:test"; import { Glob } from "bun"; +import { describe, expect, test } from "bun:test"; describe("Glob.match", () => { test("single wildcard", () => { diff --git a/test/js/bun/glob/scan.test.ts b/test/js/bun/glob/scan.test.ts index 2ba0a18091b99..db92acf36b6ba 100644 --- a/test/js/bun/glob/scan.test.ts +++ b/test/js/bun/glob/scan.test.ts @@ -23,11 +23,10 @@ import { Glob, GlobScanOptions } from "bun"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import fg from "fast-glob"; -import * as path from "path"; -import { tempFixturesDir, createTempDirectoryWithBrokenSymlinks, prepareEntries } from "./util"; import { tempDirWithFiles, tmpdirSync } from "harness"; -import * as os from "node:os"; import * as fs from "node:fs"; +import * as path from "path"; +import { createTempDirectoryWithBrokenSymlinks, prepareEntries, tempFixturesDir } from "./util"; let origAggressiveGC = Bun.unsafe.gcAggressionLevel(); let tempBrokenSymlinksDir: string; @@ -410,9 +409,9 @@ describe("fast-glob e2e tests", async () => { ? fg.globSync(pattern, { cwd: testCwd, absolute: true }) : Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true, absolute: true })); - entries = entries.sort().map(entry => entry.slice(absoluteCwd.length + 1)); + entries = entries.sort().map(entry => entry.slice(testCwd.length + 1)); entries = prepareEntries(entries); - expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + expect(entries).toMatchSnapshot(`absolute: ${pattern}`); }); }); diff --git a/test/js/bun/glob/stress.test.ts b/test/js/bun/glob/stress.test.ts index 43be74444f579..7fdde0819a586 100644 --- a/test/js/bun/glob/stress.test.ts +++ b/test/js/bun/glob/stress.test.ts @@ -1,7 +1,7 @@ -import { expect, test, describe, beforeAll } from "bun:test"; import { Glob } from "bun"; -import { tempFixturesDir } from "./util"; +import { beforeAll, test } from "bun:test"; import path from "path"; +import { tempFixturesDir } from "./util"; const paths = [ path.join(import.meta.dir, "fixtures/file.md"), path.join(import.meta.dir, "fixtures/second/file.md"), diff --git a/test/js/bun/glob/util.ts b/test/js/bun/glob/util.ts index 3fac0f7b06a2e..48ae7d4789b8d 100644 --- a/test/js/bun/glob/util.ts +++ b/test/js/bun/glob/util.ts @@ -1,6 +1,6 @@ import { symlinkSync } from "fs"; -import path from "path"; import { tmpdirSync } from "harness"; +import path from "path"; export function createTempDirectoryWithBrokenSymlinks() { // Create a temporary directory diff --git a/test/js/bun/globals.test.js b/test/js/bun/globals.test.js index 1a0f7b2ef9a10..554f9d7b42033 100644 --- a/test/js/bun/globals.test.js +++ b/test/js/bun/globals.test.js @@ -1,7 +1,45 @@ -import { expect, it, describe } from "bun:test"; +import { describe, expect, it } from "bun:test"; import { bunEnv, bunExe } from "harness"; import path from "path"; +it("ERR_INVALID_THIS", () => { + try { + Request.prototype.formData.call(undefined); + expect.unreachable(); + } catch (e) { + expect(e.code).toBe("ERR_INVALID_THIS"); + expect(e.name).toBe("TypeError"); + expect(e.message).toBe("Expected this to be instanceof Request"); + } + + try { + Request.prototype.formData.call(null); + expect.unreachable(); + } catch (e) { + expect(e.code).toBe("ERR_INVALID_THIS"); + expect(e.name).toBe("TypeError"); + expect(e.message).toBe("Expected this to be instanceof Request, but received null"); + } + + try { + Request.prototype.formData.call(new (class Boop {})()); + expect.unreachable(); + } catch (e) { + expect(e.code).toBe("ERR_INVALID_THIS"); + expect(e.name).toBe("TypeError"); + expect(e.message).toBe("Expected this to be instanceof Request, but received Boop"); + } + + try { + Request.prototype.formData.call("hellooo"); + expect.unreachable(); + } catch (e) { + expect(e.code).toBe("ERR_INVALID_THIS"); + expect(e.name).toBe("TypeError"); + expect(e.message).toBe("Expected this to be instanceof Request, but received a string"); + } +}); + it("extendable", () => { const classes = [Blob, TextDecoder, TextEncoder, Request, Response, Headers, HTMLRewriter, Bun.Transpiler, Buffer]; for (let Class of classes) { diff --git a/test/js/bun/http/async-iterator-stream.test.ts b/test/js/bun/http/async-iterator-stream.test.ts index e2784d8eb6f20..c247055f50433 100644 --- a/test/js/bun/http/async-iterator-stream.test.ts +++ b/test/js/bun/http/async-iterator-stream.test.ts @@ -1,6 +1,6 @@ -import { describe, expect, test, afterAll, mock } from "bun:test"; import { spawn } from "bun"; -import { bunExe, bunEnv } from "harness"; +import { afterAll, describe, expect, mock, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; describe("Streaming body via", () => { test("async generator function", async () => { diff --git a/test/js/bun/http/body-leak-test-fixture.ts b/test/js/bun/http/body-leak-test-fixture.ts index c1e1a495b904d..f9702c54e1906 100644 --- a/test/js/bun/http/body-leak-test-fixture.ts +++ b/test/js/bun/http/body-leak-test-fixture.ts @@ -1,7 +1,8 @@ const server = Bun.serve({ port: 0, async fetch(req: Request) { - if (req.url.endsWith("/report")) { + const url = req.url; + if (url.endsWith("/report")) { Bun.gc(true); await Bun.sleep(10); return new Response(JSON.stringify(process.memoryUsage.rss()), { @@ -10,22 +11,25 @@ const server = Bun.serve({ }, }); } - if (req.url.endsWith("/buffering")) { + if (url.endsWith("/buffering")) { await req.text(); - } else if (req.url.endsWith("/streaming")) { - const reader = req.body?.getReader(); + } else if (url.endsWith("/buffering+body-getter")) { + req.body; + await req.text(); + } else if (url.endsWith("/streaming")) { + const reader = req.body.getReader(); while (reader) { const { done, value } = await reader?.read(); if (done) { break; } } - } else if (req.url.endsWith("/incomplete-streaming")) { + } else if (url.endsWith("/incomplete-streaming")) { const reader = req.body?.getReader(); if (!reader) { reader?.read(); } - } else if (req.url.endsWith("/streaming-echo")) { + } else if (url.endsWith("/streaming-echo")) { return new Response(req.body, { headers: { "Content-Type": "application/octet-stream", @@ -36,3 +40,4 @@ const server = Bun.serve({ }, }); console.log(server.url.href); +process?.send?.(server.url.href); diff --git a/test/js/bun/http/bun-serve-body-json-async.test.ts b/test/js/bun/http/bun-serve-body-json-async.test.ts index feef851d6976a..4bdccc906eba4 100644 --- a/test/js/bun/http/bun-serve-body-json-async.test.ts +++ b/test/js/bun/http/bun-serve-body-json-async.test.ts @@ -1,5 +1,4 @@ -import { serve, sleep, $ } from "bun"; -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; import { join } from "path"; diff --git a/test/js/bun/http/bun-serve-headers.test.ts b/test/js/bun/http/bun-serve-headers.test.ts index e5b340cab22f0..9fa8902d86794 100644 --- a/test/js/bun/http/bun-serve-headers.test.ts +++ b/test/js/bun/http/bun-serve-headers.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; // https://github.com/oven-sh/bun/issues/9180 test("weird headers", async () => { diff --git a/test/js/bun/http/bun-serve-propagate-errors.test.ts b/test/js/bun/http/bun-serve-propagate-errors.test.ts index e64d8da94ea47..88b6ef5ecc2d0 100644 --- a/test/js/bun/http/bun-serve-propagate-errors.test.ts +++ b/test/js/bun/http/bun-serve-propagate-errors.test.ts @@ -1,5 +1,5 @@ import { spawnSync } from "bun"; -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; test("Bun.serve() propagates errors to the parent fixture", async () => { diff --git a/test/js/bun/http/bun-serve-static.test.ts b/test/js/bun/http/bun-serve-static.test.ts new file mode 100644 index 0000000000000..2f2e96992bf55 --- /dev/null +++ b/test/js/bun/http/bun-serve-static.test.ts @@ -0,0 +1,196 @@ +import { afterAll, beforeAll, describe, expect, it, mock, test } from "bun:test"; +import { fillRepeating, isWindows } from "harness"; + +const routes = { + "/foo": new Response("foo", { + headers: { + "Content-Type": "text/plain", + "X-Foo": "bar", + }, + }), + "/big": new Response( + (() => { + const buf = Buffer.alloc(1024 * 1024 * 4); + const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_*^!@#$%^&*()+=?><:;{}[]|\\ \n"; + + function randomAnyCaseLetter() { + return alphabet[(Math.random() * alphabet.length) | 0]; + } + + for (let i = 0; i < 1024; i++) { + buf[i] = randomAnyCaseLetter(); + } + fillRepeating(buf, 0, 1024); + return buf; + })(), + ), + "/redirect": Response.redirect("/foo/bar", 302), + "/foo/bar": new Response("/foo/bar", { + headers: { + "Content-Type": "text/plain", + "X-Foo": "bar", + }, + }), + "/redirect/fallback": Response.redirect("/foo/bar/fallback", 302), +}; +const static_responses = {}; +for (const [path, response] of Object.entries(routes)) { + static_responses[path] = await response.clone().blob(); +} + +describe("static", () => { + let server: Server; + let handler = mock(req => { + return new Response(req.url, { + headers: { + ...req.headers, + Location: undefined, + }, + }); + }); + afterAll(() => { + server.stop(true); + }); + + beforeAll(async () => { + server = Bun.serve({ + static: routes, + port: 0, + fetch: handler, + }); + server.unref(); + }); + + it("reload", async () => { + const modified = { ...routes }; + modified["/foo"] = new Response("modified", { + headers: { + "Content-Type": "text/plain", + }, + }); + server.reload({ + static: modified, + + fetch: handler, + }); + + const res = await fetch(`${server.url}foo`); + expect(res.status).toBe(200); + expect(await res.text()).toBe("modified"); + server.reload({ + static: routes, + fetch: handler, + }); + }); + + describe.each(["/foo", "/big", "/foo/bar"])("%s", path => { + it("GET", async () => { + const previousCallCount = handler.mock.calls.length; + + const res = await fetch(`${server.url}${path}`); + expect(res.status).toBe(200); + expect(await res.bytes()).toEqual(await static_responses[path].bytes()); + expect(handler.mock.calls.length, "Handler should not be called").toBe(previousCallCount); + }); + + it("HEAD", async () => { + const previousCallCount = handler.mock.calls.length; + + const res = await fetch(`${server.url}${path}`, { method: "HEAD" }); + expect(res.status).toBe(200); + expect(await res.bytes()).toHaveLength(0); + expect(res.headers.get("Content-Length")).toBe(static_responses[path].size.toString()); + expect(handler.mock.calls.length, "Handler should not be called").toBe(previousCallCount); + }); + + describe.each(["access .body", "don't access .body"])("stress (%s)", label => { + test.each(["arrayBuffer", "blob", "bytes", "text"])( + "%s", + async method => { + const byteSize = static_responses[path][method]?.size; + + const bytes = method === "blob" ? static_responses[path] : await static_responses[path][method](); + + // macOS limits backlog to 128. + // When we do the big request, reduce number of connections but increase number of iterations + const batchSize = Math.ceil((byteSize > 1024 * 1024 ? 48 : 64) / (isWindows ? 8 : 1)); + const iterations = Math.ceil((byteSize > 1024 * 1024 ? 10 : 12) / (isWindows ? 8 : 1)); + + async function iterate() { + let array = new Array(batchSize); + const route = `${server.url}${path.substring(1)}`; + for (let i = 0; i < batchSize; i++) { + array[i] = fetch(route) + .then(res => { + expect(res.status).toBe(200); + expect(res.url).toBe(route); + if (label === "access .body") { + res.body; + } + return res[method](); + }) + .then(output => { + expect(output).toStrictEqual(bytes); + }); + } + + await Promise.all(array); + + Bun.gc(); + } + + for (let i = 0; i < iterations; i++) { + await iterate(); + } + + Bun.gc(true); + const baseline = (process.memoryUsage.rss() / 1024 / 1024) | 0; + let lastRSS = baseline; + console.log("Start RSS", baseline); + for (let i = 0; i < iterations; i++) { + await iterate(); + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + if (lastRSS + 50 < rss) { + console.log("RSS Growth", rss - lastRSS); + } + lastRSS = rss; + } + Bun.gc(true); + + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + expect(rss).toBeLessThan(4092); + const delta = rss - baseline; + console.log("Final RSS", rss); + console.log("Delta RSS", delta); + }, + 40 * 1000, + ); + }); + }); + + it("/redirect", async () => { + const previousCallCount = handler.mock.calls.length; + const res = await fetch(`${server.url}/redirect`, { redirect: "manual" }); + expect(res.status).toBe(302); + expect(res.headers.get("Location")).toBe("/foo/bar"); + expect(handler.mock.calls.length, "Handler should not be called").toBe(previousCallCount); + }); + + it("/redirect (follow)", async () => { + const previousCallCount = handler.mock.calls.length; + const res = await fetch(`${server.url}/redirect`); + expect(res.status).toBe(200); + expect(res.url).toBe(`${server.url}foo/bar`); + expect(await res.text()).toBe("/foo/bar"); + expect(handler.mock.calls.length, "Handler should not be called").toBe(previousCallCount); + expect(res.redirected).toBeTrue(); + }); + + it("/redirect/fallback", async () => { + const previousCallCount = handler.mock.calls.length; + const res = await fetch(`${server.url}/redirect/fallback`); + expect(res.status).toBe(200); + expect(await res.text()).toBe(`${server.url}foo/bar/fallback`); + expect(handler.mock.calls.length, "Handler should be called").toBe(previousCallCount + 1); + }); +}); diff --git a/test/js/bun/http/bun-server.test.ts b/test/js/bun/http/bun-server.test.ts index 4d4cc571ea7c6..638f141214c85 100644 --- a/test/js/bun/http/bun-server.test.ts +++ b/test/js/bun/http/bun-server.test.ts @@ -1,6 +1,6 @@ -import type { ServerWebSocket, Server } from "bun"; +import type { Server, ServerWebSocket, Socket } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunExe, bunEnv, rejectUnauthorizedScope } from "harness"; +import { bunEnv, bunExe, rejectUnauthorizedScope } from "harness"; import path from "path"; describe("Server", () => { @@ -517,6 +517,7 @@ test("Bun should be able to handle utf16 inside Content-Type header #11316", asy test("should be able to async upgrade using custom protocol", async () => { const { promise, resolve } = Promise.withResolvers<{ code: number; reason: string } | boolean>(); using server = Bun.serve({ + port: 0, async fetch(req: Request, server: Server) { await Bun.sleep(1); @@ -543,3 +544,134 @@ test("should be able to async upgrade using custom protocol", async () => { expect(await promise).toBe(true); }); + +test("should be able to abrubtly close a upload request", async () => { + const { promise, resolve } = Promise.withResolvers(); + const { promise: promise2, resolve: resolve2 } = Promise.withResolvers(); + using server = Bun.serve({ + port: 0, + hostname: "localhost", + maxRequestBodySize: 1024 * 1024 * 1024 * 16, + async fetch(req) { + let total_size = 0; + req.signal.addEventListener("abort", resolve); + try { + for await (const chunk of req.body as ReadableStream) { + total_size += chunk.length; + if (total_size > 1024 * 1024 * 1024) { + return new Response("too big", { status: 413 }); + } + } + } catch (e) { + expect((e as Error)?.name).toBe("AbortError"); + } finally { + resolve2(); + } + + return new Response("Received " + total_size); + }, + }); + // ~100KB + const chunk = Buffer.alloc(1024 * 100, "a"); + // ~1GB + const MAX_PAYLOAD = 1024 * 1024 * 1024; + const request = Buffer.from( + `POST / HTTP/1.1\r\nHost: ${server.hostname}:${server.port}\r\nContent-Length: ${MAX_PAYLOAD}\r\n\r\n`, + ); + + type SocketInfo = { state: number; pending: Buffer | null }; + function tryWritePending(socket: Socket) { + if (socket.data.pending === null) { + // first write + socket.data.pending = request; + } + const data = socket.data.pending as Buffer; + const written = socket.write(data); + if (written < data.byteLength) { + // partial write + socket.data.pending = data.slice(0, written); + return false; + } + + // full write got to next state + if (socket.data.state === 0) { + // request sent -> send chunk + socket.data.pending = chunk; + } else { + // chunk sent -> delay shutdown + setTimeout(() => socket.shutdown(), 100); + } + socket.data.state++; + socket.flush(); + return true; + } + + function trySend(socket: Socket) { + while (socket.data.state < 2) { + if (!tryWritePending(socket)) { + return; + } + } + return; + } + await Bun.connect({ + hostname: server.hostname, + port: server.port, + data: { + state: 0, + pending: null, + } as SocketInfo, + socket: { + open: trySend, + drain: trySend, + data(socket, data) {}, + }, + }); + await Promise.all([promise, promise2]); + expect().pass(); +}); + +// This test is disabled because it can OOM the CI +test.skip("should be able to stream huge amounts of data", async () => { + const buf = Buffer.alloc(1024 * 1024 * 256); + const CONTENT_LENGTH = 3 * 1024 * 1024 * 1024; + let received = 0; + let written = 0; + using server = Bun.serve({ + port: 0, + fetch() { + return new Response( + new ReadableStream({ + type: "direct", + async pull(controller) { + while (written < CONTENT_LENGTH) { + written += buf.byteLength; + await controller.write(buf); + } + controller.close(); + }, + }), + { + headers: { + "Content-Type": "text/plain", + "Content-Length": CONTENT_LENGTH.toString(), + }, + }, + ); + }, + }); + + const response = await fetch(server.url); + expect(response.status).toBe(200); + expect(response.headers.get("content-type")).toBe("text/plain"); + const reader = (response.body as ReadableStream).getReader(); + while (true) { + const { done, value } = await reader.read(); + received += value ? value.byteLength : 0; + if (done) { + break; + } + } + expect(written).toBe(CONTENT_LENGTH); + expect(received).toBe(CONTENT_LENGTH); +}, 30_000); diff --git a/test/js/bun/http/error-response.js b/test/js/bun/http/error-response.js index 3284c146bb015..9283161b3f658 100644 --- a/test/js/bun/http/error-response.js +++ b/test/js/bun/http/error-response.js @@ -1,8 +1,8 @@ -const s = Bun.serve({ +using s = Bun.serve({ fetch(req, res) { - s.stop(true); throw new Error("1"); }, port: 0, }); -fetch(`http://${s.hostname}:${s.port}`).then(res => console.log(res.status)); + +await fetch(`http://${s.hostname}:${s.port}`).then(res => console.log(res.status)); diff --git a/test/js/bun/http/form-data-set-append.test.js b/test/js/bun/http/form-data-set-append.test.js new file mode 100644 index 0000000000000..7123dd34685a2 --- /dev/null +++ b/test/js/bun/http/form-data-set-append.test.js @@ -0,0 +1,63 @@ +import { expect, test } from "bun:test"; + +// https://github.com/oven-sh/bun/issues/12325 + +test("formdata set with File works as expected", async () => { + const expected = ["617580375", "text-notes1.txt"]; + + using server = Bun.serve({ + port: 0, + fetch: async req => { + const data = await req.formData(); + const chat_id = data.get("chat_id"); + const document = data.get("document"); + expect(chat_id).toEqual(expected[0]); + expect(document.name).toEqual(expected[1]); + return new Response(""); + }, + }); + + async function sendDocument(body) { + const response = await fetch(server.url, { + method: "POST", + body: body, + }); + const text = await response.text(); + return text; + } + + const formDataSet = new FormData(); + formDataSet.set("chat_id", expected[0]); + formDataSet.set("document", new File(["some text notes 1"], expected[1])); + await sendDocument(formDataSet); +}); + +test("formdata apppend with File works as expected", async () => { + const expected = ["617580376", "text-notes2.txt"]; + + using server = Bun.serve({ + port: 0, + fetch: async req => { + const data = await req.formData(); + const chat_id = data.get("chat_id"); + const document = data.get("document"); + expect(chat_id).toEqual(expected[0]); + expect(document.name).toEqual(expected[1]); + return new Response(""); + }, + }); + + async function sendDocument(body) { + const response = await fetch(server.url, { + method: "POST", + body: body, + }); + const text = await response.text(); + return text; + } + + const formDataSet = new FormData(); + formDataSet.append("chat_id", expected[0]); + formDataSet.append("document", new File(["some text notes 2"], expected[1])); + await sendDocument(formDataSet); +}); diff --git a/test/js/bun/http/js-sink-sourmap-fixture/chunks/stream.mjs b/test/js/bun/http/js-sink-sourmap-fixture/chunks/stream.mjs index 9b2d87af4eff8..063f4ab4692f2 100644 --- a/test/js/bun/http/js-sink-sourmap-fixture/chunks/stream.mjs +++ b/test/js/bun/http/js-sink-sourmap-fixture/chunks/stream.mjs @@ -1,9 +1,9 @@ -import { e as eventHandler } from "../index.mjs"; import "fs"; -import "path"; import "node:async_hooks"; import "node:fs"; import "node:url"; +import "path"; +import { e as eventHandler } from "../index.mjs"; const stream = eventHandler(() => { const encoder = new TextEncoder(); diff --git a/test/js/bun/http/js-sink-sourmap-fixture/index.mjs b/test/js/bun/http/js-sink-sourmap-fixture/index.mjs index 66a1f66267fcb..11d5025db0c7d 100644 --- a/test/js/bun/http/js-sink-sourmap-fixture/index.mjs +++ b/test/js/bun/http/js-sink-sourmap-fixture/index.mjs @@ -1,9 +1,9 @@ globalThis._importMeta_ = { url: import.meta.url, env: process.env }; -import { promises, existsSync } from "fs"; -import { dirname as dirname$1, resolve as resolve$1, join } from "path"; +import { existsSync, promises } from "fs"; import { AsyncLocalStorage } from "node:async_hooks"; import { promises as promises$1 } from "node:fs"; import { fileURLToPath } from "node:url"; +import { dirname as dirname$1, join, resolve as resolve$1 } from "path"; const HASH_RE = /#/g; const AMPERSAND_RE = /&/g; diff --git a/test/js/bun/http/leaks-test.test.ts b/test/js/bun/http/leaks-test.test.ts new file mode 100644 index 0000000000000..ac0a9fc664e01 --- /dev/null +++ b/test/js/bun/http/leaks-test.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from "bun:test"; +import "harness"; +import { join } from "path"; + +// This test was never leaking, as far as i can tell. +test("request error doesn't leak", async () => { + expect([join(import.meta.dir, "request-constructor-leak-fixture.js")]).toRun(); +}); + +test("response error doesn't leak", async () => { + expect([join(import.meta.dir, "response-constructor-leak-fixture.js")]).toRun(); +}); diff --git a/test/js/bun/http/proxy.test.js b/test/js/bun/http/proxy.test.js index 433adfb831684..6faa0f4f07609 100644 --- a/test/js/bun/http/proxy.test.js +++ b/test/js/bun/http/proxy.test.js @@ -1,12 +1,10 @@ -import { afterAll, beforeAll, describe, expect, it } from "bun:test"; -import { gc } from "harness"; +import { afterAll, beforeAll, expect, it } from "bun:test"; import fs from "fs"; +import { bunExe, gc } from "harness"; import { tmpdir } from "os"; import path from "path"; -import { bunExe } from "harness"; let proxy, auth_proxy, server; - beforeAll(() => { proxy = Bun.serve({ port: 0, diff --git a/test/js/bun/http/proxy.test.ts b/test/js/bun/http/proxy.test.ts new file mode 100644 index 0000000000000..6ae0d4e189f30 --- /dev/null +++ b/test/js/bun/http/proxy.test.ts @@ -0,0 +1,242 @@ +import type { Server } from "bun"; +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { tls as tlsCert } from "harness"; +import { once } from "node:events"; +import net from "node:net"; +import tls from "node:tls"; +async function createProxyServer(is_tls: boolean) { + const serverArgs = []; + if (is_tls) { + serverArgs.push({ + ...tlsCert, + rejectUnauthorized: false, + }); + } + serverArgs.push((clientSocket: net.Socket | tls.TLSSocket) => { + clientSocket.once("data", data => { + const request = data.toString(); + const [method, path] = request.split(" "); + let host: string; + let port: number | string = 0; + let request_path: string; + if (path.indexOf("http") !== -1) { + const url = new URL(path); + host = url.hostname; + port = url.port; + request_path = url.pathname + (url.search || ""); + } else { + // Extract the host and port from the CONNECT request + [host, port] = path.split(":"); + } + const destinationPort = Number.parseInt((port || (method === "CONNECT" ? "443" : "80")).toString(), 10); + const destinationHost = host || ""; + // Establish a connection to the destination server + const serverSocket = net.connect(destinationPort, destinationHost, () => { + if (method === "CONNECT") { + // 220 OK with host so the client knows the connection was successful + clientSocket.write("HTTP/1.1 200 OK\r\nHost: localhost\r\n\r\n"); + + // Pipe data between client and server + clientSocket.pipe(serverSocket); + serverSocket.pipe(clientSocket); + } else { + serverSocket.write(`${method} ${request_path} HTTP/1.1\r\n`); + // Send the request to the destination server + serverSocket.write(data.slice(request.indexOf("\r\n") + 2)); + serverSocket.pipe(clientSocket); + } + }); + + serverSocket.on("error", err => { + clientSocket.end(); + }); + }); + }); + // Create a server to listen for incoming HTTPS connections + //@ts-ignore + const server = (is_tls ? tls : net).createServer(...serverArgs); + + server.listen(0); + await once(server, "listening"); + const port = server.address().port; + const url = `http${is_tls ? "s" : ""}://localhost:${port}`; + return { server, url }; +} + +let httpServer: Server; +let httpsServer: Server; +let httpProxyServer: { server: net.Server; url: string }; +let httpsProxyServer: { server: net.Server; url: string }; + +beforeAll(async () => { + httpServer = Bun.serve({ + port: 0, + async fetch(req) { + if (req.method === "POST") { + const text = await req.text(); + return new Response(text, { status: 200 }); + } + return new Response("", { status: 200 }); + }, + }); + + httpsServer = Bun.serve({ + port: 0, + tls: tlsCert, + async fetch(req) { + if (req.method === "POST") { + const text = await req.text(); + return new Response(text, { status: 200 }); + } + return new Response("", { status: 200 }); + }, + }); + + httpProxyServer = await createProxyServer(false); + httpsProxyServer = await createProxyServer(true); +}); + +afterAll(() => { + httpServer.stop(); + httpsServer.stop(); + httpProxyServer.server.close(); + httpsProxyServer.server.close(); +}); + +for (const proxy_tls of [false, true]) { + for (const target_tls of [false, true]) { + for (const body of [undefined, "Hello, World"]) { + test(`${body === undefined ? "GET" : "POST"} ${proxy_tls ? "TLS" : "non-TLS"} proxy -> ${target_tls ? "TLS" : "non-TLS"} body type ${typeof body}`, async () => { + const response = await fetch(target_tls ? httpsServer.url : httpServer.url, { + method: body === undefined ? "GET" : "POST", + proxy: proxy_tls ? httpsProxyServer.url : httpProxyServer.url, + headers: { + "Content-Type": "plain/text", + }, + keepalive: false, + body: body, + tls: { + ca: tlsCert.cert, + rejectUnauthorized: false, + }, + }); + expect(response.ok).toBe(true); + expect(response.status).toBe(200); + expect(response.statusText).toBe("OK"); + const result = await response.text(); + + expect(result).toBe(body || ""); + }); + } + } +} + +for (const server_tls of [false, true]) { + describe(`proxy can handle redirects with ${server_tls ? "TLS" : "non-TLS"} server`, () => { + test("with empty body #12007", async () => { + using server = Bun.serve({ + tls: server_tls ? tlsCert : undefined, + port: 0, + async fetch(req) { + if (req.url.endsWith("/bunbun")) { + return Response.redirect("/bun", 302); + } + if (req.url.endsWith("/bun")) { + return Response.redirect("/", 302); + } + return new Response("", { status: 403 }); + }, + }); + const response = await fetch(`${server.url.origin}/bunbun`, { + proxy: httpsProxyServer.url, + tls: { + cert: tlsCert.cert, + rejectUnauthorized: false, + }, + }); + expect(response.ok).toBe(false); + expect(response.status).toBe(403); + expect(response.statusText).toBe("Forbidden"); + }); + + test("with body #12007", async () => { + using server = Bun.serve({ + tls: server_tls ? tlsCert : undefined, + port: 0, + async fetch(req) { + if (req.url.endsWith("/bunbun")) { + return new Response("Hello, bunbun", { status: 302, headers: { Location: "/bun" } }); + } + if (req.url.endsWith("/bun")) { + return new Response("Hello, bun", { status: 302, headers: { Location: "/" } }); + } + return new Response("BUN!", { status: 200 }); + }, + }); + const response = await fetch(`${server.url.origin}/bunbun`, { + proxy: httpsProxyServer.url, + tls: { + cert: tlsCert.cert, + rejectUnauthorized: false, + }, + }); + expect(response.ok).toBe(true); + expect(response.status).toBe(200); + expect(response.statusText).toBe("OK"); + + const result = await response.text(); + expect(result).toBe("BUN!"); + }); + + test("with chunked body #12007", async () => { + using server = Bun.serve({ + tls: server_tls ? tlsCert : undefined, + port: 0, + async fetch(req) { + async function* body() { + await Bun.sleep(100); + yield "bun"; + await Bun.sleep(100); + yield "bun"; + await Bun.sleep(100); + yield "bun"; + await Bun.sleep(100); + yield "bun"; + } + if (req.url.endsWith("/bunbun")) { + return new Response(body, { status: 302, headers: { Location: "/bun" } }); + } + if (req.url.endsWith("/bun")) { + return new Response(body, { status: 302, headers: { Location: "/" } }); + } + return new Response(body, { status: 200 }); + }, + }); + const response = await fetch(`${server.url.origin}/bunbun`, { + proxy: httpsProxyServer.url, + tls: { + cert: tlsCert.cert, + rejectUnauthorized: false, + }, + }); + expect(response.ok).toBe(true); + expect(response.status).toBe(200); + expect(response.statusText).toBe("OK"); + + const result = await response.text(); + expect(result).toBe("bunbunbunbun"); + }); + }); +} + +test("unsupported protocol", async () => { + expect( + fetch("https://httpbin.org/get", { + proxy: "ftp://asdf.com", + }), + ).rejects.toThrowError( + expect.objectContaining({ + code: "UnsupportedProxyProtocol", + }), + ); +}); diff --git a/test/js/bun/http/request-constructor-leak-fixture.js b/test/js/bun/http/request-constructor-leak-fixture.js new file mode 100644 index 0000000000000..eff0eb03ccca3 --- /dev/null +++ b/test/js/bun/http/request-constructor-leak-fixture.js @@ -0,0 +1,27 @@ +// This test is meant to cause OOM if either: +// +// - the request body leaks +// - the headers leak +// - the url leaks +// +const buf = new Uint8Array(1024 * 1024 * 16); + +for (var i = 0; i < 1000; i++) { + try { + new Request("http://" + "superduperlongurlwowsuchlengthicant".repeat(1024) + ".com/" + i, { + body: buf, + signal: Symbol("leaky-error"), + headers: { + // That means the string needs to be long enough to otherwise show up with a 0-length body. + ["Content-Type"]: + "yo de lay yo de lay yo de lay yo de lay yo de lay yo de lay ".repeat(1024) + Math.random(), + "Invalid-Header-Name-☺️": "1", + }, + }); + } catch (e) {} +} +Bun.gc(true); +console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB"); +if (process.memoryUsage.rss() > 1024 * 1024 * 1024) { + process.exit(1); +} diff --git a/test/js/bun/http/response-constructor-leak-fixture.js b/test/js/bun/http/response-constructor-leak-fixture.js new file mode 100644 index 0000000000000..c61544cd95f73 --- /dev/null +++ b/test/js/bun/http/response-constructor-leak-fixture.js @@ -0,0 +1,28 @@ +// This test is meant to cause OOM if either: +// +// - the response body leaks +// - the headers leak +// + +const buf = new Uint8Array(1024 * 1024 * 32); + +for (var i = 0; i < 1000; i++) { + try { + new Response(buf, { + // This causes the response constructor to throw an error + statusText: Symbol("leaky-error"), + + status: 200, + headers: { + // That means the string needs to be long enough to otherwise show up with a 0-length body. + ["Content-Type"]: + "yo de lay yo de lay yo de lay yo de lay yo de lay yo de lay ".repeat(1024) + Math.random(), + }, + }); + } catch (e) {} +} +Bun.gc(true); +console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB"); +if (process.memoryUsage.rss() > 1024 * 1024 * 1024) { + process.exit(1); +} diff --git a/test/js/bun/http/serve-body-leak.test.ts b/test/js/bun/http/serve-body-leak.test.ts index 1e27f1d48ea3d..5c2b7bad65824 100644 --- a/test/js/bun/http/serve-body-leak.test.ts +++ b/test/js/bun/http/serve-body-leak.test.ts @@ -1,7 +1,7 @@ -import { join } from "path"; -import { it, expect, beforeAll, afterAll } from "bun:test"; -import { bunExe, bunEnv, isDebug } from "harness"; import type { Subprocess } from "bun"; +import { afterEach, beforeEach, expect, it } from "bun:test"; +import { bunEnv, bunExe, isDebug } from "harness"; +import { join } from "path"; const payload = Buffer.alloc(512 * 1024, "1").toString("utf-8"); // decent size payload to test memory leak const batchSize = 40; @@ -10,20 +10,26 @@ const zeroCopyPayload = new Blob([payload]); let url: URL; let process: Subprocess<"ignore", "pipe", "inherit"> | null = null; -beforeAll(async () => { +beforeEach(async () => { + if (process) { + process?.kill(); + } + + let defer = Promise.withResolvers(); process = Bun.spawn([bunExe(), "--smol", join(import.meta.dirname, "body-leak-test-fixture.ts")], { env: bunEnv, - stdout: "pipe", + stdout: "inherit", stderr: "inherit", stdin: "ignore", + ipc(message) { + defer.resolve(message); + }, }); - const { value } = await process.stdout.getReader().read(); - url = new URL(new TextDecoder().decode(value)); + url = new URL(await defer.promise); process.unref(); - await warmup(); }); -afterAll(() => { +afterEach(() => { process?.kill(); }); @@ -57,6 +63,14 @@ async function callBuffering() { }).then(res => res.text()); expect(result).toBe("Ok"); } + +async function callBufferingBodyGetter() { + const result = await fetch(`${url.origin}/buffering+body-getter`, { + method: "POST", + body: zeroCopyPayload, + }).then(res => res.text()); + expect(result).toBe("Ok"); +} async function callStreaming() { const result = await fetch(`${url.origin}/streaming`, { method: "POST", @@ -126,23 +140,24 @@ async function calculateMemoryLeak(fn: () => Promise) { // If it was leaking the body, the memory usage would be at least 512 KB * 10_000 = 5 GB // If it ends up around 280 MB, it's probably not leaking the body. for (const test_info of [ - ["#10265 should not leak memory when ignoring the body", callIgnore, false, 48], - ["should not leak memory when buffering the body", callBuffering, false, 48], - ["should not leak memory when streaming the body", callStreaming, false, 48], + ["#10265 should not leak memory when ignoring the body", callIgnore, false, 64], + ["should not leak memory when buffering the body", callBuffering, false, 64], + ["should not leak memory when buffering the body and accessing req.body", callBufferingBodyGetter, false, 64], + ["should not leak memory when streaming the body", callStreaming, false, 64], ["should not leak memory when streaming the body incompletely", callIncompleteStreaming, false, 64], ["should not leak memory when streaming the body and echoing it back", callStreamingEcho, false, 64], ] as const) { const [testName, fn, skip, maxMemoryGrowth] = test_info; - it.todoIf(skip)( + it( testName, async () => { const report = await calculateMemoryLeak(fn); // peak memory is too high - expect(report.peak_memory > report.start_memory * 2).toBe(false); + expect(report.peak_memory).not.toBeGreaterThan(report.start_memory * 2.5); // acceptable memory leak expect(report.leak).toBeLessThanOrEqual(maxMemoryGrowth); expect(report.end_memory).toBeLessThanOrEqual(512 * 1024 * 1024); }, - isDebug ? 60_000 : 30_000, + isDebug ? 60_000 : 40_000, ); } diff --git a/test/js/bun/http/serve-listen.test.ts b/test/js/bun/http/serve-listen.test.ts index 031496710d48c..6402b78a0c2c7 100644 --- a/test/js/bun/http/serve-listen.test.ts +++ b/test/js/bun/http/serve-listen.test.ts @@ -1,9 +1,9 @@ -import { describe, test, expect } from "bun:test"; import { file, serve } from "bun"; +import { describe, expect, test } from "bun:test"; +import { isWindows, tmpdirSync } from "harness"; import type { NetworkInterfaceInfo } from "node:os"; import { networkInterfaces } from "node:os"; import { join } from "node:path"; -import { isWindows, tmpdirSync } from "harness"; const networks = Object.values(networkInterfaces()).flat() as NetworkInterfaceInfo[]; const hasIPv4 = networks.some(({ family }) => family === "IPv4"); diff --git a/test/js/bun/http/serve.test.ts b/test/js/bun/http/serve.test.ts index ea3fc3941a618..1376c0a1c1f61 100644 --- a/test/js/bun/http/serve.test.ts +++ b/test/js/bun/http/serve.test.ts @@ -1,13 +1,14 @@ import { file, gc, Serve, serve, Server } from "bun"; -import { afterEach, describe, it, expect, afterAll, mock } from "bun:test"; +import { afterAll, afterEach, describe, expect, it, mock } from "bun:test"; import { readFileSync, writeFileSync } from "fs"; +import { bunEnv, bunExe, dumpStats, isIPv4, isIPv6, isPosix, tls, tmpdirSync } from "harness"; import { join, resolve } from "path"; -import { bunExe, bunEnv, dumpStats } from "harness"; // import { renderToReadableStream } from "react-dom/server"; // import app_jsx from "./app.jsx"; +import { heapStats } from "bun:jsc"; import { spawn } from "child_process"; +import net from "node:net"; import { tmpdir } from "os"; -import { heapStats } from "bun:jsc"; let renderToReadableStream: any = null; let app_jsx: any = null; @@ -27,7 +28,6 @@ async function runTest({ port, ...serverOptions }: Serve, test: (server: Se while (!server) { try { server = serve({ ...serverOptions, port: 0 }); - console.log(`Server: ${server.url}`); break; } catch (e: any) { console.log("catch:", e); @@ -48,6 +48,171 @@ afterAll(() => { } }); +it("should be able to abruptly stop the server many times", async () => { + async function run() { + const stopped = Promise.withResolvers(); + const server = Bun.serve({ + port: 0, + error() { + return new Response("Error", { status: 500 }); + }, + async fetch(req, server) { + await Bun.sleep(50); + server.stop(true); + await Bun.sleep(50); + server = undefined; + if (stopped.resolve) { + stopped.resolve(); + stopped.resolve = undefined; + } + + return new Response("Hello, World!"); + }, + }); + const url = server.url; + + async function request() { + try { + await fetch(url, { keepalive: true }).then(res => res.text()); + expect.unreachable(); + } catch (e) { + expect(["ConnectionClosed", "ConnectionRefused"]).toContain(e.code); + } + } + + const requests = new Array(20); + for (let i = 0; i < 20; i++) { + requests[i] = request(); + } + await Promise.all(requests); + await stopped.promise; + Bun.gc(true); + } + const runs = new Array(10); + for (let i = 0; i < 10; i++) { + runs[i] = run(); + } + + await Promise.all(runs); + Bun.gc(true); +}); + +// This test reproduces a crash in Bun v1.1.18 and earlier +it("should be able to abruptly stop the server", async () => { + for (let i = 0; i < 2; i++) { + const controller = new AbortController(); + + using server = Bun.serve({ + port: 0, + error() { + return new Response("Error", { status: 500 }); + }, + async fetch(req, server) { + server.stop(true); + await Bun.sleep(10); + return new Response(); + }, + }); + + await fetch(server.url, { + signal: controller.signal, + }) + .then(res => { + return res.blob(); + }) + .catch(() => {}); + } +}); + +// https://github.com/oven-sh/bun/issues/6758 +// https://github.com/oven-sh/bun/issues/4517 +it("should call cancel() on ReadableStream when the Request is aborted", async () => { + let waitForCancel = Promise.withResolvers(); + const abortedFn = mock(() => { + console.log("'abort' event fired", new Date()); + }); + const cancelledFn = mock(() => { + console.log("'cancel' function called", new Date()); + waitForCancel.resolve(); + }); + let onIncomingRequest = Promise.withResolvers(); + await runTest( + { + async fetch(req) { + req.signal.addEventListener("abort", abortedFn); + // Give it a chance to start the stream so that the cancel function can be called. + setTimeout(() => { + console.log("'onIncomingRequest' function called", new Date()); + onIncomingRequest.resolve(); + }, 0); + return new Response( + new ReadableStream({ + async pull(controller) { + await waitForCancel.promise; + }, + cancel: cancelledFn, + }), + ); + }, + }, + async server => { + const controller = new AbortController(); + const signal = controller.signal; + const request = fetch(server.url, { signal }); + await onIncomingRequest.promise; + controller.abort(); + expect(async () => await request).toThrow(); + // Delay for one run of the event loop. + await Bun.sleep(1); + + expect(abortedFn).toHaveBeenCalled(); + expect(cancelledFn).toHaveBeenCalled(); + }, + ); +}); +for (let withDelay of [true, false]) { + for (let connectionHeader of ["keepalive", "not keepalive"] as const) { + it(`should NOT call cancel() on ReadableStream that finished normally for ${connectionHeader} request and ${withDelay ? "with" : "without"} delay`, async () => { + const cancelledFn = mock(() => { + console.log("'cancel' function called", new Date()); + }); + let onIncomingRequest = Promise.withResolvers(); + await runTest( + { + async fetch(req) { + return new Response( + new ReadableStream({ + async pull(controller) { + controller.enqueue(new Uint8Array([1, 2, 3])); + if (withDelay) await Bun.sleep(1); + controller.close(); + }, + cancel: cancelledFn, + }), + ); + }, + }, + async server => { + const resp = await fetch( + server.url, + connectionHeader === "keepalive" + ? {} + : { + headers: { + "Connection": "close", + }, + keepalive: false, + }, + ); + await resp.blob(); + // Delay for one run of the event loop. + await Bun.sleep(1); + expect(cancelledFn).not.toHaveBeenCalled(); + }, + ); + }); + } +} describe("1000 uploads & downloads in batches of 64 do not leak ReadableStream", () => { for (let isDirect of [true, false] as const) { it( @@ -91,7 +256,10 @@ describe("1000 uploads & downloads in batches of 64 do not leak ReadableStream", async server => { const count = 1000; async function callback() { - const response = await fetch(server.url, { body: blob, method: "POST" }); + const response = await fetch(server.url, { + body: blob, + method: "POST", + }); // We are testing for ReadableStream leaks, so we use the ReadableStream here. const chunks = []; @@ -203,7 +371,9 @@ it("request.signal works in trivial case", async () => { }, async server => { expect(async () => { - const response = await fetch(server.url.origin, { signal: aborty.signal }); + const response = await fetch(server.url.origin, { + signal: aborty.signal, + }); await signaler.promise; await response.blob(); }).toThrow("The operation was aborted."); @@ -1006,7 +1176,9 @@ describe("should support Content-Range with Bun.file()", () => { const end = Number(searchParams.get("end")); const file = Bun.file(fixture); return new Response(file.slice(start, end), { - headers: { "Content-Range": "bytes " + start + "-" + end + "/" + file.size }, + headers: { + "Content-Range": "bytes " + start + "-" + end + "/" + file.size, + }, }); }, }); @@ -1097,16 +1269,27 @@ describe("should support Content-Range with Bun.file()", () => { }); it("formats error responses correctly", async () => { - const c = spawn(bunExe(), ["./error-response.js"], { cwd: import.meta.dir, env: bunEnv }); + const { promise, resolve, reject } = Promise.withResolvers(); + const c = spawn(bunExe(), ["./error-response.js"], { + cwd: import.meta.dir, + env: bunEnv, + }); var output = ""; c.stderr.on("data", chunk => { output += chunk.toString(); }); c.stderr.on("end", () => { - expect(output).toContain('throw new Error("1");'); - c.kill(); + try { + expect(output).toContain('throw new Error("1");'); + resolve(); + } catch (e) { + reject(e); + } finally { + c.kill(); + } }); + await promise; }); it("request body and signal life cycle", async () => { @@ -1256,9 +1439,10 @@ it("#5859 json", async () => { port: 0, async fetch(req) { try { - await req.json(); + const json = await req.json(); + console.log({ json }); } catch (e) { - return new Response("FAIL", { status: 500 }); + return new Response(e?.message!, { status: 500 }); } return new Response("SHOULD'VE FAILED", {}); @@ -1270,16 +1454,17 @@ it("#5859 json", async () => { body: new Uint8Array([0xfd]), }); + expect(await response.text()).toBe("Failed to parse JSON"); expect(response.ok).toBeFalse(); - expect(await response.text()).toBe("FAIL"); }); it("#5859 arrayBuffer", async () => { - await Bun.write("/tmp/bad", new Uint8Array([0xfd])); - expect(async () => await Bun.file("/tmp/bad").json()).toThrow(); + const tmp = join(tmpdirSync(), "bad"); + await Bun.write(tmp, new Uint8Array([0xfd])); + expect(async () => await Bun.file(tmp).json()).toThrow(); }); -it("server.requestIP (v4)", async () => { +it.if(isIPv4())("server.requestIP (v4)", async () => { using server = Bun.serve({ port: 0, fetch(req, server) { @@ -1296,7 +1481,7 @@ it("server.requestIP (v4)", async () => { }); }); -it("server.requestIP (v6)", async () => { +it.if(isIPv6())("server.requestIP (v6)", async () => { using server = Bun.serve({ port: 0, fetch(req, server) { @@ -1313,8 +1498,8 @@ it("server.requestIP (v6)", async () => { }); }); -it("server.requestIP (unix)", async () => { - const unix = "/tmp/bun-serve.sock"; +it.if(isPosix)("server.requestIP (unix)", async () => { + const unix = join(tmpdirSync(), "serve.sock"); using server = Bun.serve({ unix, fetch(req, server) { @@ -1478,7 +1663,7 @@ it("should resolve pending promise if requested ended with pending read", async { fetch(req) { // @ts-ignore - req.body?.getReader().read().catch(shouldError).then(shouldMarkDone); + req.body?.getReader().read().then(shouldMarkDone).catch(shouldError); return new Response("OK"); }, }, @@ -1489,8 +1674,9 @@ it("should resolve pending promise if requested ended with pending read", async }); const text = await response.text(); expect(text).toContain("OK"); - expect(is_done).toBe(true); - expect(error).toBeUndefined(); + expect(is_done).toBe(false); + expect(error).toBeDefined(); + expect(error.name).toContain("AbortError"); }, ); }); @@ -1509,3 +1695,363 @@ it("should work with dispose keyword", async () => { } expect(fetch(url)).rejects.toThrow(); }); + +// prettier-ignore +it("should be able to stop in the middle of a file response", async () => { + async function doRequest(url: string) { + try { + const response = await fetch(url, { signal: AbortSignal.timeout(10) }); + const read = (response.body as ReadableStream).getReader(); + while (true) { + const { value, done } = await read.read(); + if (done) break; + } + expect(response.status).toBe(200); + } catch {} + } + const fixture = join(import.meta.dir, "server-bigfile-send.fixture.js"); + for (let i = 0; i < 3; i++) { + const process = Bun.spawn([bunExe(), fixture], { + env: bunEnv, + stderr: "inherit", + stdout: "pipe", + stdin: "ignore", + }); + const { value } = await process.stdout.getReader().read(); + const url = new TextDecoder().decode(value).trim(); + const requests = []; + for (let j = 0; j < 5_000; j++) { + requests.push(doRequest(url)); + } + // only await for 1k requests (and kill the process) + await Promise.all(requests.slice(0, 1_000)); + expect(process.exitCode || 0).toBe(0); + process.kill(); + } +}, 60_000); + +it("should be able to abrupt stop the server", async () => { + for (let i = 0; i < 10; i++) { + using server = Bun.serve({ + port: 0, + error() { + return new Response("Error", { status: 500 }); + }, + async fetch(req, server) { + server.stop(true); + await Bun.sleep(100); + return new Response("Hello, World!"); + }, + }); + + try { + await fetch(server.url).then(res => res.text()); + expect.unreachable(); + } catch (e) { + expect(e.code).toBe("ConnectionClosed"); + } + } +}); + +it("should not instanciate error instances in each request", async () => { + const startErrorCount = heapStats().objectTypeCounts.Error || 0; + using server = Bun.serve({ + port: 0, + async fetch(req, server) { + return new Response("bun"); + }, + }); + const batchSize = 100; + const batch = new Array(batchSize); + for (let i = 0; i < 1000; i++) { + batch[i % batchSize] = await fetch(server.url, { + method: "POST", + body: "bun", + }); + if (i % batchSize === batchSize - 1) { + await Promise.all(batch); + } + } + expect(heapStats().objectTypeCounts.Error || 0).toBeLessThanOrEqual(startErrorCount); +}); + +it("should be able to abort a sendfile response and streams", async () => { + const bigfile = join(import.meta.dir, "../../web/encoding/utf8-encoding-fixture.bin"); + using server = serve({ + port: 0, + tls, + hostname: "localhost", + async fetch() { + return new Response(file(bigfile), { + headers: { "Content-Type": "text/html" }, + }); + }, + }); + + async function doRequest() { + try { + const controller = new AbortController(); + const res = await fetch(server.url, { + signal: controller.signal, + tls: { rejectUnauthorized: false }, + }); + res.body + ?.getReader() + .read() + .catch(() => {}); + controller.abort(); + } catch {} + } + const batchSize = 20; + const batch = []; + + for (let i = 0; i < 500; i++) { + batch.push(doRequest()); + if (batch.length === batchSize) { + await Promise.all(batch); + batch.length = 0; + } + } + await Promise.all(batch); + expect().pass(); +}, 10_000); + +it("should not send extra bytes when using sendfile", async () => { + const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + const tmpFile = join(tmpdirSync(), "test.bin"); + await Bun.write(tmpFile, payload); + using serve = Bun.serve({ + port: 0, + fetch(req) { + const pathname = new URL(req.url).pathname; + if (pathname === "/file") { + return new Response(Bun.file(tmpFile), { + headers: { + "Content-Type": "plain/text", + }, + }); + } + return new Response("Not Found", { + status: 404, + }); + }, + }); + + // manually fetch the file using sockets, and get the whole content + const { promise, resolve, reject } = Promise.withResolvers(); + const socket = net.connect(serve.port, "localhost", () => { + socket.write("GET /file HTTP/1.1\r\nHost: localhost\r\n\r\n"); + setTimeout(() => { + socket.end(); // wait a bit before closing the connection so we get the whole content + }, 100); + }); + + let body: Buffer | null = null; + let content_length = 0; + let headers = ""; + + socket.on("data", data => { + if (body) { + body = Buffer.concat([body as Buffer, data]); + + return; + } + // parse headers + const str = data.toString("utf8"); + const index = str.indexOf("\r\n\r\n"); + if (index === -1) { + headers += str; + return; + } + headers += str.slice(0, index); + const lines = headers.split("\r\n"); + for (const line of lines) { + const [key, value] = line.split(": "); + if (key.toLowerCase() === "content-length") { + content_length = Number.parseInt(value, 10); + } + } + body = data.subarray(index + 4); + }); + socket.on("error", reject); + socket.on("close", () => { + resolve(body); + }); + + expect(await promise).toEqual(Buffer.from(payload)); + expect(content_length).toBe(payload.byteLength); +}); + +it("we should always send date", async () => { + const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + const tmpFile = join(tmpdirSync(), "test.bin"); + await Bun.write(tmpFile, payload); + using serve = Bun.serve({ + port: 0, + fetch(req) { + const pathname = new URL(req.url).pathname; + if (pathname === "/file") { + return new Response(Bun.file(tmpFile), { + headers: { + "Content-Type": "plain/text", + }, + }); + } + if (pathname === "/file2") { + return new Response(Bun.file(tmpFile)); + } + if (pathname === "/stream") { + return new Response( + new ReadableStream({ + async pull(controller) { + await Bun.sleep(10); + controller.enqueue(payload); + await Bun.sleep(10); + controller.close(); + }, + }), + ); + } + return new Response("Hello, World!"); + }, + }); + + { + const res = await fetch(new URL("/file", serve.url.origin)); + expect(res.headers.has("Date")).toBeTrue(); + } + { + const res = await fetch(new URL("/file2", serve.url.origin)); + expect(res.headers.has("Date")).toBeTrue(); + } + + { + const res = await fetch(new URL("/", serve.url.origin)); + expect(res.headers.has("Date")).toBeTrue(); + } + { + const res = await fetch(new URL("/stream", serve.url.origin)); + expect(res.headers.has("Date")).toBeTrue(); + } +}); + +it("should allow use of custom timeout", async () => { + using server = Bun.serve({ + port: 0, + idleTimeout: 8, // uws precision is in seconds, and lower than 4 seconds is not reliable its timer is not that accurate + async fetch(req) { + const url = new URL(req.url); + return new Response( + new ReadableStream({ + async pull(controller) { + controller.enqueue("Hello,"); + if (url.pathname === "/timeout") { + await Bun.sleep(10000); + } else { + await Bun.sleep(10); + } + controller.enqueue(" World!"); + + controller.close(); + }, + }), + { headers: { "Content-Type": "text/plain" } }, + ); + }, + }); + async function testTimeout(pathname: string, success: boolean) { + const res = await fetch(new URL(pathname, server.url.origin)); + expect(res.status).toBe(200); + if (success) { + expect(res.text()).resolves.toBe("Hello, World!"); + } else { + expect(res.text()).rejects.toThrow(/The socket connection was closed unexpectedly./); + } + } + await Promise.all([testTimeout("/ok", true), testTimeout("/timeout", false)]); +}, 15_000); + +it("should reset timeout after writes", async () => { + // the default is 10s so we send 15 + // this test should take 15s at most + const CHUNKS = 15; + const payload = Buffer.from(`data: ${Date.now()}\n\n`); + using server = Bun.serve({ + idleTimeout: 5, + port: 0, + fetch(request, server) { + let controller!: ReadableStreamDefaultController; + let count = CHUNKS; + let interval = setInterval(() => { + controller.enqueue(payload); + count--; + if (count == 0) { + clearInterval(interval); + interval = null; + controller.close(); + return; + } + }, 1000); + return new Response( + new ReadableStream({ + start(_controller) { + controller = _controller; + }, + cancel(controller) { + if (interval) clearInterval(interval); + }, + }), + { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + }, + }, + ); + }, + }); + let received = 0; + const response = await fetch(server.url); + const stream = response.body.getReader(); + const decoder = new TextDecoder(); + while (true) { + const { done, value } = await stream.read(); + received += value?.length || 0; + if (done) break; + } + + expect(received).toBe(CHUNKS * payload.byteLength); +}, 20_000); + +it("allow requestIP after async operation", async () => { + using server = Bun.serve({ + port: 0, + async fetch(req, server) { + await Bun.sleep(1); + return new Response(JSON.stringify(server.requestIP(req))); + }, + }); + + const ip = await fetch(server.url).then(res => res.json()); + expect(ip).not.toBeNull(); + expect(ip.port).toBeInteger(); + expect(ip.address).toBeString(); + expect(ip.family).toBeString(); +}); + +it("allow custom timeout per request", async () => { + using server = Bun.serve({ + idleTimeout: 1, + port: 0, + async fetch(req, server) { + server.timeout(req, 60); + await Bun.sleep(10000); //uWS precision is not great + + return new Response("Hello, World!"); + }, + }); + expect(server.timeout).toBeFunction(); + const res = await fetch(new URL("/long-timeout", server.url.origin)); + expect(res.status).toBe(200); + expect(res.text()).resolves.toBe("Hello, World!"); +}, 20_000); diff --git a/test/js/bun/http/server-bigfile-send.fixture.js b/test/js/bun/http/server-bigfile-send.fixture.js new file mode 100644 index 0000000000000..265ab4a32d014 --- /dev/null +++ b/test/js/bun/http/server-bigfile-send.fixture.js @@ -0,0 +1,13 @@ +import { file, serve } from "bun"; +import { join } from "node:path"; +const bigfile = join(import.meta.dir, "../../web/encoding/utf8-encoding-fixture.bin"); +const server = serve({ + port: 0, + async fetch() { + return new Response(file(bigfile), { + headers: { "Content-Type": "text/html" }, + }); + }, +}); + +console.log(server.url.href); diff --git a/test/js/bun/ini/foo.ini b/test/js/bun/ini/foo.ini new file mode 100644 index 0000000000000..528f166c353a4 --- /dev/null +++ b/test/js/bun/ini/foo.ini @@ -0,0 +1,95 @@ +o = p + + a with spaces = b c + +; wrap in quotes to JSON-decode and preserve spaces +" xa n p " = "\"\r\nyoyoyo\r\r\n" + +; wrap in quotes to get a key with a bracket, not a section. +"[disturbing]" = hey you never know + +; Test single quotes +s = 'something' + +; Test mixing quotes + +s1 = "something' + +; Test double quotes +s2 = "something else" + +; Test blank value +s3 = + +; Test value with only spaces +s4 = + +; Test quoted value with only spaces +s5 = ' ' + +; Test quoted value with leading and trailing spaces +s6 = ' a ' + +; Test no equal sign +s7 + +; Test bool(true) +true = true + +; Test bool(false) +false = false + +; Test null +null = null + +; Test undefined +undefined = undefined + +; Test arrays +zr[] = deedee +ar[] = one +ar[] = three +; This should be included in the array +ar = this is included + +; Test resetting of a value (and not turn it into an array) +br = cold +br = warm + +eq = "eq=eq" + +; a section +[a] +av = a val +e = { o: p, a: { av: a val, b: { c: { e: "this [value]" } } } } +j = "{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }" +"[]" = a square? + +; Nested array +cr[] = four +cr[] = eight + +; b section with a space after its title +[b] + +; nested child without middle parent +; should create otherwise-empty a.b +[a.b.c] +e = 1 +j = 2 + +; dots in the section name should be literally interpreted +[x\.y\.z] +x.y.z = xyz + +[x\.y\.z.a\.b\.c] +a.b.c = abc + +; this next one is not a comment! it's escaped! +nocomment = this\; this is not a comment + +# Support the use of the number sign (#) as an alternative to the semicolon for indicating comments. +# http://en.wikipedia.org/wiki/INI_file#Comments + +# this next one is not a comment! it's escaped! +noHashComment = this\# this is not a comment diff --git a/test/js/bun/ini/ini.test.ts b/test/js/bun/ini/ini.test.ts new file mode 100644 index 0000000000000..7cb732829cd33 --- /dev/null +++ b/test/js/bun/ini/ini.test.ts @@ -0,0 +1,321 @@ +const { iniInternals } = require("bun:internal-for-testing"); +const { parse } = iniInternals; +import { describe, expect, it, test } from "bun:test"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; + +describe("parse ini", () => { + test("weird section", () => { + const ini = /* ini */ ` +[foo\\]] +lol = true +`; + + expect(parse(ini)).toEqual({ "[foo\\]]": true, "lol": true }); + }); + + test("really long input", () => { + const ini = /* ini */ ` +[${Array(1024).fill("a").join("")}.lol.this.be.long] +wow = 'hi' +`; + + expect(parse(ini)).toEqual({ + [`${Array(1024).fill("a").join("")}`]: { + lol: { + this: { + be: { + long: { + wow: "hi", + }, + }, + }, + }, + }, + }); + }); + describe("env vars", () => { + envVarTest({ + name: "escaped", + ini: "hi = \\${NODE_ENV}", + env: { NODE_ENV: "production" }, + expected: { hi: "${NODE_ENV}" }, + }); + + envVarTest({ + name: "escaped2", + ini: "hi = \\\\${NODE_ENV}", + env: { NODE_ENV: "production" }, + expected: { hi: "\\production" }, + }); + + envVarTest({ + name: "basic", + ini: /* ini */ ` +hello = \${LOL} + `, + env: { LOL: "hi" }, + expected: { hello: "hi" }, + }); + + envVarTest({ + name: "no val", + ini: /* ini */ ` +hello = \${oooooooooooooooogaboga} + `, + env: {}, + expected: { hello: "" }, + }); + + envVarTest({ + name: "concat", + ini: /* ini */ ` +hello = greeting: \${LOL} + `, + env: { LOL: "hi" }, + expected: { hello: "greeting: hi" }, + }); + + envVarTest({ + name: "nesting selects the inner most", + ini: /* ini */ ` +hello = greeting: \${what\${LOL}lol} + `, + env: { LOL: "hi" }, + expected: { hello: "greeting: ${whathilol}" }, + }); + + envVarTest({ + name: "nesting 2 selects the inner most", + ini: /* ini */ ` +hello = greeting: \${what\${omg\${LOL}why}lol} + `, + env: { LOL: "hi" }, + expected: { hello: "greeting: ${what${omghiwhy}lol}" }, + }); + + envVarTest({ + name: "unclosed", + ini: /* ini */ ` +hello = greeting: \${LOL + `, + env: { LOL: "hi" }, + expected: { hello: "greeting: ${LOL" }, + }); + + function envVarTest(args: { name: string; ini: string; env: Record; expected: any }) { + const { name, ini, env, expected } = args; + test(name, async () => { + const tempdir = tempDirWithFiles("hi", { "foo.ini": ini }); + const inipath = `${tempdir}/foo.ini`.replaceAll("\\", "/"); + const code = /* ts */ ` +const { iniInternals } = require("bun:internal-for-testing"); +const { parse } = iniInternals; + +const ini = await Bun.$\`cat ${inipath}\`.text() + +console.log(JSON.stringify(parse(ini))) + `; + + const result = await Bun.$`${bunExe()} -e ${code}`.env({ ...bunEnv, ...env }).json(); + expect(result).toEqual(expected); + }); + } + }); + + it("works with unicode in the .ini file", () => { + let ini /* ini */ = ` +hi👋lol = 'lol hi 👋' +`; + + expect(parse(ini)).toEqual({ + "hi👋lol": "lol hi 👋", + }); + + ini = /* ini */ ` +[😎.🫒.🤦‍♀️] +lol = 'wtf' + `; + + expect(parse(ini)).toEqual({ + "😎": { + "🫒": { + "🤦‍♀️": { + lol: "wtf", + }, + }, + }, + }); + }); + + it("matches stupid npm/ini behavior", () => { + let ini /* ini */ = ` +'{ "what": "is this" }' = seriously? +`; + + let result = parse(ini); + expect(result).toEqual({ + "[Object object]": "seriously?", + }); + + ini = /* ini */ ` +'[1, 2, 3]' = cmon man +`; + + result = parse(ini); + expect(result).toEqual({ + "1,2,3": "cmon man", + }); + }); + + test("basic", () => { + const ini = /* ini */ ` + hello = 'friends' + `; + + expect(parse(ini)).toEqual({ + hello: "friends", + }); + }); + + test("basic sections", () => { + const ini = /* ini */ ` +hello = 'friends' + +[foo] +bar = 'baz' + `; + + expect(parse(ini)).toEqual({ + hello: "friends", + foo: { + bar: "baz", + }, + }); + }); + + test("key and then section edgecase", () => { + const ini = /* ini */ ` +foo = 'hihihi' + +[foo] +isbar = 'lol' + `; + + expect(parse(ini)).toEqual({ + foo: "hihihi", + }); + }); + + describe("duplicate properties", () => { + test("decode with duplicate properties", () => { + const ini = /* ini */ ` +zr[] = deedee +zr=123 +ar[] = one +ar[] = three +str = 3 +brr = 1 +brr = 2 +brr = 3 +brr = 3 +`; + + expect(parse(ini)).toEqual({ + zr: ["deedee", "123"], + ar: ["one", "three"], + str: "3", + brr: "3", + }); + }); + }); + + test("bigboi", async () => { + const foo = await Bun.$`cat ${__dirname}/foo.ini`.text(); + const result = parse(foo); + console.log(JSON.stringify(result)); + expect(result).toEqual({ + " xa n p ": '"\r\nyoyoyo\r\r\n', + "[disturbing]": "hey you never know", + "a": { + "[]": "a square?", + "av": "a val", + "b": { + "c": { + "e": "1", + "j": "2", + }, + }, + "cr": ["four", "eight"], + "e": '{ o: p, a: { av: a val, b: { c: { e: "this [value]" } } } }', + "j": '"{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }"', + }, + "a with spaces": "b c", + "ar": ["one", "three", "this is included"], + "b": {}, + "br": "warm", + "eq": "eq=eq", + "false": false, + "null": null, + "o": "p", + "s": "something", + "s1": "\"something'", + "s2": "something else", + "s3": "", + "s4": "", + "s5": " ", + "s6": " a ", + "s7": true, + "true": true, + "undefined": "undefined", + "x.y.z": { + "a.b.c": { + "a.b.c": "abc", + "nocomment": "this; this is not a comment", + "noHashComment": "this# this is not a comment", + }, + "x.y.z": "xyz", + }, + "zr": ["deedee"], + }); + }); +}); + +const wtf = { + "o": "p", + "a with spaces": "b c", + " xa n p ": '"\r\nyoyoyo\r\r\n', + "[disturbing]": "hey you never know", + "s": "something", + "s1": "\"something'", + "s2": "something else", + "s3": true, + "s4": true, + "s5": " ", + "s6": " a ", + "s7": true, + "true": true, + "false": false, + "null": null, + "undefined": "undefined", + "zr": ["deedee"], + "ar": [["one"], "three", "this is included"], + "br": "warm", + "eq": "eq=eq", + "a": { + "av": "a val", + "e": '{ o: p, a: { av: a val, b: { c: { e: "this [value]" } } } }', + "j": '"{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }"', + "[]": "a square?", + "cr": [["four"], "eight"], + "b": { "c": { "e": "1", "j": "2" } }, + }, + "b": {}, + "x.y.z": { + "x.y.z": "xyz", + "a.b.c": { + "a.b.c": "abc", + "nocomment": "this; this is not a comment", + "noHashComment": "this# this is not a comment", + }, + }, +}; diff --git a/test/js/bun/io/bun-write-leak.test.ts b/test/js/bun/io/bun-write-leak.test.ts index f12c25c1584e6..f0b3b5551a647 100644 --- a/test/js/bun/io/bun-write-leak.test.ts +++ b/test/js/bun/io/bun-write-leak.test.ts @@ -1,4 +1,4 @@ -import { test, expect, describe } from "bun:test"; +import { expect, test } from "bun:test"; import path from "node:path"; import "harness"; diff --git a/test/js/bun/io/bun-write.test.js b/test/js/bun/io/bun-write.test.js index 7ab28c887ca86..18a744c3366f5 100644 --- a/test/js/bun/io/bun-write.test.js +++ b/test/js/bun/io/bun-write.test.js @@ -1,10 +1,13 @@ +import { describe, expect, it, test } from "bun:test"; import fs, { mkdirSync } from "fs"; -import { it, expect, describe, test } from "bun:test"; -import path, { join } from "path"; -import { gcTick, withoutAggressiveGC, bunExe, bunEnv, isWindows } from "harness"; +import { bunEnv, bunExe, gcTick, isWindows, withoutAggressiveGC } from "harness"; import { tmpdir } from "os"; +import path, { join } from "path"; const tmpbase = tmpdir() + path.sep; +const IS_UV_FS_COPYFILE_DISABLED = + process.platform === "win32" && process.env.BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE === "1"; + it("Bun.write blob", async () => { await Bun.write( Bun.file(join(tmpdir(), "response-file.test.txt")), @@ -95,7 +98,9 @@ it("Bun.write file not found returns ENOENT, issue#6336", async () => { expect.unreachable(); } catch (exception) { expect(exception.code).toBe("ENOENT"); - expect(exception.path).toBe(dst.name); + if (!IS_UV_FS_COPYFILE_DISABLED) { + expect(exception.path).toBe(dst.name); + } } const src = Bun.file(path.join(tmpdir(), `test-bun-write-${Date.now()}.txt`)); @@ -107,7 +112,9 @@ it("Bun.write file not found returns ENOENT, issue#6336", async () => { await gcTick(); } catch (exception) { expect(exception.code).toBe("ENOENT"); - expect(exception.path).toBe(dst.name); + if (!IS_UV_FS_COPYFILE_DISABLED) { + expect(exception.path).toBe(dst.name); + } } finally { fs.unlinkSync(src.name); } @@ -310,6 +317,15 @@ it("Bun.write(Bun.stderr, 'new TextEncoder().encode(Bun.write STDERR TEST'))", a expect(await Bun.write(Bun.stderr, new TextEncoder().encode("\nBun.write STDERR TEST\n\n"))).toBe(24); }); +// These tests pass by not throwing: +it("Bun.write(Bun.stdout, Bun.file(path))", async () => { + await Bun.write(Bun.stdout, Bun.file(path.join(import.meta.dir, "hello-world.txt"))); +}); + +it("Bun.write(Bun.stderr, Bun.file(path))", async () => { + await Bun.write(Bun.stderr, Bun.file(path.join(import.meta.dir, "hello-world.txt"))); +}); + it("Bun.file(0) survives GC", async () => { for (let i = 0; i < 10; i++) { let f = Bun.file(0); @@ -495,4 +511,19 @@ test("timed output should work", async () => { await Bun.sleep(1000); } expect(text).toBe("0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n"); -}); +}, 25000); + +if (isWindows && !IS_UV_FS_COPYFILE_DISABLED) { + it("Bun.write() without uv_fs_copyfile", async () => { + const { exited } = Bun.spawn({ + cmd: [bunExe(), "test", import.meta.path], + env: { + ...bunEnv, + BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE: "1", + }, + stdio: ["inherit", "inherit", "inherit"], + }); + + expect(await exited).toBe(0); + }, 10000); +} diff --git a/test/js/bun/io/hello-world.txt b/test/js/bun/io/hello-world.txt new file mode 100644 index 0000000000000..4429471b7814a --- /dev/null +++ b/test/js/bun/io/hello-world.txt @@ -0,0 +1 @@ +Hello World! diff --git a/test/js/bun/jsc/bun-jsc.test.ts b/test/js/bun/jsc/bun-jsc.test.ts index 5da25c5bc94d2..f43aca7404b4a 100644 --- a/test/js/bun/jsc/bun-jsc.test.ts +++ b/test/js/bun/jsc/bun-jsc.test.ts @@ -1,31 +1,30 @@ -import { describe, expect, it } from "bun:test"; import { - describe as jscDescribe, + callerSourceOrigin, describeArray, - serialize, deserialize, - gcAndSweep, - fullGC, + drainMicrotasks, edenGC, + fullGC, + gcAndSweep, + getProtectedObjects, + getRandomSeed, heapSize, heapStats, - memoryUsage, - getRandomSeed, - setRandomSeed, isRope, - callerSourceOrigin, - noFTL, - noOSRExitFuzzing, - optimizeNextInvocation, + describe as jscDescribe, + memoryUsage, numberOfDFGCompiles, + optimizeNextInvocation, + profile, releaseWeakRefs, - totalCompileTime, - getProtectedObjects, reoptimizationRetryCount, - drainMicrotasks, - startRemoteDebugger, + serialize, + setRandomSeed, setTimeZone, + totalCompileTime, } from "bun:jsc"; +import { describe, expect, it } from "bun:test"; +import { isBuildKite, isWindows } from "harness"; describe("bun:jsc", () => { function count() { @@ -73,7 +72,10 @@ describe("bun:jsc", () => { expect(setRandomSeed(2)).toBeUndefined(); }); it("isRope", () => { - expect(isRope("a" + 123 + "b")).toBe(true); + // https://twitter.com/bunjavascript/status/1806921203644571685 + let y; + y = 123; + expect(isRope("a" + y + "b")).toBe(true); expect(isRope("abcdefgh")).toBe(false); }); it("callerSourceOrigin", () => { @@ -88,7 +90,7 @@ describe("bun:jsc", () => { }); it("numberOfDFGCompiles", async () => { await Bun.sleep(5); // this failed once and i suspect it is because the query was done too fast - expect(numberOfDFGCompiles(count)).toBeGreaterThan(0); + expect(numberOfDFGCompiles(count)).toBeGreaterThanOrEqual(0); }); it("releaseWeakRefs", () => { expect(releaseWeakRefs()).toBeUndefined(); @@ -167,4 +169,18 @@ describe("bun:jsc", () => { } Bun.gc(true); }); + + it.todoIf(isBuildKite && isWindows)("profile async", async () => { + const { promise, resolve } = Promise.withResolvers(); + const result = await profile( + async function hey(arg1: number) { + await Bun.sleep(10).then(() => resolve(arguments)); + return arg1; + }, + 1, + 2, + ); + const input = await promise; + expect({ ...input }).toStrictEqual({ "0": 2 }); + }); }); diff --git a/test/js/bun/jsc/domjit.test.ts b/test/js/bun/jsc/domjit.test.ts index fe38b3cc91f09..f26cd5ec87e24 100644 --- a/test/js/bun/jsc/domjit.test.ts +++ b/test/js/bun/jsc/domjit.test.ts @@ -1,15 +1,17 @@ // test functions that use DOMJIT -import { describe, test, expect } from "bun:test"; +import { describe, expect, test } from "bun:test"; +import { ptr, read } from "bun:ffi"; import crypto from "crypto"; import { statSync } from "fs"; -import { read, ptr } from "bun:ffi"; +import vm from "node:vm"; const dirStats = statSync(import.meta.dir); const buffer = new BigInt64Array(16); describe("DOMJIT", () => { + const buf = new Uint8Array(4); for (let iter of [1000, 10000, 100000, 1000000]) { test("Buffer.alloc", () => { for (let i = 0; i < iter; i++) { @@ -43,13 +45,13 @@ describe("DOMJIT", () => { }); test("TextEncoder.encodeInto", () => { for (let i = 0; i < iter; i++) { - new TextEncoder().encodeInto("test", new Uint8Array(4)); + new TextEncoder().encodeInto("test", buf); } expect(true).toBe(true); }); test("Crypto.timingSafeEqual", () => { for (let i = 0; i < iter; i++) { - crypto.timingSafeEqual(new Uint8Array(4), new Uint8Array(4)); + crypto.timingSafeEqual(buf, buf); } expect(true).toBe(true); }); @@ -61,13 +63,13 @@ describe("DOMJIT", () => { }); test("Crypto.getRandomValues", () => { for (let i = 0; i < iter; i++) { - crypto.getRandomValues(new Uint8Array(4)); + crypto.getRandomValues(buf); } expect(true).toBe(true); }); test("TextDecoder.decode", () => { for (let i = 0; i < iter; i++) { - new TextDecoder().decode(new Uint8Array(4)); + new TextDecoder().decode(buf); } expect(true).toBe(true); }); @@ -100,4 +102,54 @@ describe("DOMJIT", () => { expect(true).toBe(true); }); } + + describe("in NodeVM", () => { + const code = ` + const buf = new Uint8Array(4); + const encoder = new TextEncoder(); + for (let iter of [100000]) { + for (let i = 0; i < iter; i++) { + performance.now(); + } + for (let i = 0; i < iter; i++) { + new TextEncoder().encode("test"); + } + const str = "a".repeat(1030); + for (let i = 0; i < 1000000; i++) { + const result = encoder.encode(str); + } + for (let i = 0; i < iter; i++) { + new TextEncoder().encodeInto("test", buf); + } + for (let i = 0; i < iter; i++) { + crypto.timingSafeEqual(buf, buf); + } + for (let i = 0; i < iter; i++) { + crypto.randomUUID(); + } + for (let i = 0; i < iter; i++) { + crypto.getRandomValues(buf); + } + for (let i = 0; i < iter; i++) { + new TextDecoder().decode(buf); + } + for (let i = 0; i < iter; i++) { + dirStats.isSymbolicLink(); + dirStats.isSocket(); + dirStats.isFile(); + dirStats.isFIFO(); + dirStats.isDirectory(); + dirStats.isCharacterDevice(); + dirStats.isBlockDevice(); + } + } + "success";`; + test("Script.runInNewContext", () => { + const script = new vm.Script(code); + expect(script.runInNewContext({ crypto, performance, TextEncoder, TextDecoder, dirStats })).toBe("success"); + }, 20_000); + test("vm.runInNewContext", () => { + expect(vm.runInNewContext(code, { crypto, performance, TextEncoder, TextDecoder, dirStats })).toBe("success"); + }, 20_000); + }); }); diff --git a/test/js/bun/jsc/shadow.test.js b/test/js/bun/jsc/shadow.test.js index 3fffcac9024c8..3fdca53914131 100644 --- a/test/js/bun/jsc/shadow.test.js +++ b/test/js/bun/jsc/shadow.test.js @@ -1,4 +1,4 @@ -import { describe, it, expect } from "bun:test"; +import { expect, it } from "bun:test"; it("shadow realm works", () => { const red = new ShadowRealm(); diff --git a/test/js/bun/net/connect-returns-socket-unix.js b/test/js/bun/net/connect-returns-socket-unix.js index 1c6c805458a79..6b1e43190711c 100644 --- a/test/js/bun/net/connect-returns-socket-unix.js +++ b/test/js/bun/net/connect-returns-socket-unix.js @@ -1,4 +1,3 @@ -import fs from "fs"; import os from "os"; let resolve; diff --git a/test/js/bun/net/socket-leak-fixture.js b/test/js/bun/net/socket-leak-fixture.js index e029f9732e446..5182771c058ff 100644 --- a/test/js/bun/net/socket-leak-fixture.js +++ b/test/js/bun/net/socket-leak-fixture.js @@ -1,5 +1,5 @@ -import { openSync, closeSync } from "node:fs"; import { expect } from "bun:test"; +import { closeSync, openSync } from "node:fs"; const server = Bun.listen({ port: 0, @@ -14,30 +14,38 @@ const server = Bun.listen({ let connected = 0; async function callback() { + const { promise, resolve } = Promise.withResolvers(); await Bun.connect({ port: server.port, - hostname: "localhost", + hostname: server.hostname, socket: { open(socket) { connected += 1; }, data(socket, data) {}, + close() { + connected -= 1; + resolve(); + }, }, }); + return promise; } +// warmup +await Promise.all(new Array(10).fill(0).map(callback)); + const fd_before = openSync("/dev/null", "w"); closeSync(fd_before); // start 100 connections -const connections = await Promise.all(new Array(100).fill(0).map(callback)); +await Promise.all(new Array(100).fill(0).map(callback)); -expect(connected).toBe(100); +expect(connected).toBe(0); const fd = openSync("/dev/null", "w"); closeSync(fd); // ensure that we don't leak sockets when we initiate multiple connections expect(fd - fd_before).toBeLessThan(5); - -server.stop(); +server.stop(true); diff --git a/test/js/bun/net/socket.test.ts b/test/js/bun/net/socket.test.ts index 18486dbb90396..f3d114667d7cf 100644 --- a/test/js/bun/net/socket.test.ts +++ b/test/js/bun/net/socket.test.ts @@ -1,7 +1,8 @@ -import { expect, it } from "bun:test"; -import { bunEnv, bunExe, expectMaxObjectTypeCount, isWindows } from "harness"; -import { connect, fileURLToPath, SocketHandler, spawn } from "bun"; import type { Socket } from "bun"; +import { connect, fileURLToPath, SocketHandler, spawn } from "bun"; +import { heapStats } from "bun:jsc"; +import { expect, it, jest } from "bun:test"; +import { bunEnv, bunExe, expectMaxObjectTypeCount, isWindows, tls } from "harness"; it("should coerce '0' to 0", async () => { const listener = Bun.listen({ // @ts-expect-error @@ -354,7 +355,7 @@ it("it should not crash when returning a Error on client socket open", async () }); it("it should only call open once", async () => { - const server = Bun.listen({ + using server = Bun.listen({ port: 0, hostname: "localhost", socket: { @@ -381,7 +382,6 @@ it("it should only call open once", async () => { expect().fail("connectError should not be called"); }, close(socket) { - server.stop(); resolve(); }, data(socket, data) {}, @@ -397,7 +397,7 @@ it.skipIf(isWindows)("should not leak file descriptors when connecting", async ( }); it("should not call open if the connection had an error", async () => { - const server = Bun.listen({ + using server = Bun.listen({ port: 0, hostname: "0.0.0.0", socket: { @@ -435,12 +435,11 @@ it("should not call open if the connection had an error", async () => { await Bun.sleep(50); await promise; - server.stop(); expect(hadError).toBe(true); }); it("should connect directly when using an ip address", async () => { - const server = Bun.listen({ + using server = Bun.listen({ port: 0, hostname: "127.0.0.1", socket: { @@ -467,7 +466,6 @@ it("should connect directly when using an ip address", async () => { expect().fail("connectError should not be called"); }, close(socket) { - server.stop(); resolve(); }, data(socket, data) {}, @@ -498,3 +496,183 @@ it("should not call drain before handshake", async () => { await promise; expect(socket.authorized).toBe(true); }); +it("upgradeTLS handles errors", async () => { + using server = Bun.serve({ + tls, + async fetch(req) { + return new Response("Hello World"); + }, + }); + let body = ""; + let rawBody = Buffer.alloc(0); + + for (let i = 0; i < 100; i++) { + const socket = await Bun.connect({ + hostname: "localhost", + port: server.port, + socket: { + data(socket, data) { + rawBody = Buffer.concat([rawBody, data]); + }, + close() {}, + error(err) {}, + }, + }); + + const handlers = { + data: Buffer.from("GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n"), + socket: { + data: jest.fn(), + close: jest.fn(), + drain: jest.fn(), + error: jest.fn(), + open: jest.fn(), + }, + }; + expect(() => + socket.upgradeTLS({ + ...handlers, + tls: { + ca: "invalid certificate!", + }, + }), + ).toThrow( + expect.objectContaining({ + code: "ERR_BORINGSSL", + }), + ); + + expect(() => + socket.upgradeTLS({ + ...handlers, + tls: { + cert: "invalid certificate!", + }, + }), + ).toThrow( + expect.objectContaining({ + code: "ERR_BORINGSSL", + }), + ); + + expect(() => + socket.upgradeTLS({ + ...handlers, + tls: { + ...tls, + key: "invalid key!", + }, + }), + ).toThrow( + expect.objectContaining({ + code: "ERR_BORINGSSL", + }), + ); + + expect(() => + socket.upgradeTLS({ + ...handlers, + tls: { + ...tls, + key: "invalid key!", + cert: "invalid cert!", + }, + }), + ).toThrow( + expect.objectContaining({ + code: "ERR_BORINGSSL", + }), + ); + + expect(() => + socket.upgradeTLS({ + ...handlers, + tls: {}, + }), + ).toThrow(); + + expect(handlers.socket.close).not.toHaveBeenCalled(); + expect(handlers.socket.error).not.toHaveBeenCalled(); + expect(handlers.socket.data).not.toHaveBeenCalled(); + expect(handlers.socket.drain).not.toHaveBeenCalled(); + expect(handlers.socket.open).not.toHaveBeenCalled(); + socket.end(); + } + Bun.gc(true); +}); +it("should be able to upgrade to TLS", async () => { + using server = Bun.serve({ + tls, + async fetch(req) { + return new Response("Hello World"); + }, + }); + for (let i = 0; i < 50; i++) { + const { promise: tlsSocketPromise, resolve, reject } = Promise.withResolvers(); + const { promise: rawSocketPromise, resolve: rawSocketResolve, reject: rawSocketReject } = Promise.withResolvers(); + { + let body = ""; + let rawBody = Buffer.alloc(0); + const socket = await Bun.connect({ + hostname: "localhost", + port: server.port, + socket: { + data(socket, data) { + rawBody = Buffer.concat([rawBody, data]); + }, + close() { + rawSocketResolve(rawBody); + }, + error(err) { + rawSocketReject(err); + }, + }, + }); + const result = socket.upgradeTLS({ + data: Buffer.from("GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n"), + tls, + socket: { + data(socket, data) { + body += data.toString("utf8"); + if (body.includes("\r\n\r\n")) { + socket.end(); + } + }, + close() { + resolve(body); + }, + drain(socket) { + while (socket.data.byteLength > 0) { + const written = socket.write(socket.data); + if (written === 0) { + break; + } + socket.data = socket.data.slice(written); + } + socket.flush(); + }, + error(err) { + reject(err); + }, + }, + }); + + const [raw, tls_socket] = result; + expect(raw).toBeDefined(); + expect(tls_socket).toBeDefined(); + } + const [tlsData, rawData] = await Promise.all([tlsSocketPromise, rawSocketPromise]); + expect(tlsData).toContain("HTTP/1.1 200 OK"); + expect(tlsData).toContain("Content-Length: 11"); + expect(tlsData).toContain("\r\nHello World"); + expect(rawData.byteLength).toBeGreaterThanOrEqual(1980); + } +}); + +it("should not leak memory", async () => { + // assert we don't leak the sockets + // we expect 1 or 2 because that's the prototype / structure + await expectMaxObjectTypeCount(expect, "Listener", 2); + await expectMaxObjectTypeCount(expect, "TCPSocket", 2); + await expectMaxObjectTypeCount(expect, "TLSSocket", 2); +}); diff --git a/test/js/bun/net/tcp-server.test.ts b/test/js/bun/net/tcp-server.test.ts index fb6d46b8ac05c..c5a2edf5daf52 100644 --- a/test/js/bun/net/tcp-server.test.ts +++ b/test/js/bun/net/tcp-server.test.ts @@ -1,4 +1,4 @@ -import { listen, connect, TCPSocketListener, SocketHandler } from "bun"; +import { connect, listen, SocketHandler, TCPSocketListener } from "bun"; import { describe, expect, it } from "bun:test"; import { expectMaxObjectTypeCount } from "harness"; @@ -15,7 +15,7 @@ it("remoteAddress works", async () => { }; reject = reject1; }); - let server = Bun.listen({ + using server = Bun.listen({ socket: { open(ws) { try { @@ -25,8 +25,6 @@ it("remoteAddress works", async () => { reject(e); return; - } finally { - setTimeout(() => server.stop(true), 0); } }, close() {}, @@ -63,7 +61,7 @@ it("should not allow invalid tls option", () => { [1, "string", Symbol("symbol")].forEach(value => { expect(() => { // @ts-ignore - const server = Bun.listen({ + using server = Bun.listen({ socket: { open(ws) {}, close() {}, @@ -73,7 +71,6 @@ it("should not allow invalid tls option", () => { hostname: "localhost", tls: value, }); - server.stop(true); }).toThrow("tls option expects an object"); }); }); @@ -82,7 +79,7 @@ it("should allow using false, null or undefined tls option", () => { [false, null, undefined].forEach(value => { expect(() => { // @ts-ignore - const server = Bun.listen({ + using server = Bun.listen({ socket: { open(ws) {}, close() {}, @@ -92,7 +89,6 @@ it("should allow using false, null or undefined tls option", () => { hostname: "localhost", tls: value, }); - server.stop(true); }).not.toThrow("tls option expects an object"); }); }); @@ -167,7 +163,7 @@ it("echo server 1 on 1", async () => { }, } as SocketHandler; - var server: TCPSocketListener | undefined = listen({ + using server: TCPSocketListener | undefined = listen({ socket: handlers, hostname: "localhost", port: 0, @@ -186,8 +182,6 @@ it("echo server 1 on 1", async () => { }, }); await Promise.all([prom, clientProm, serverProm]); - server.stop(true); - server = serverData = clientData = undefined; })(); }); @@ -276,7 +270,7 @@ describe("tcp socket binaryType", () => { binaryType: type, } as SocketHandler; - var server: TCPSocketListener | undefined = listen({ + using server: TCPSocketListener | undefined = listen({ socket: handlers, hostname: "localhost", port: 0, @@ -296,8 +290,6 @@ describe("tcp socket binaryType", () => { }); await Promise.all([prom, clientProm, serverProm]); - server.stop(true); - server = serverData = clientData = undefined; })(); }); } diff --git a/test/js/bun/patch/patch.test.ts b/test/js/bun/patch/patch.test.ts index e11c1f076e8d3..2a0afdbca37f5 100644 --- a/test/js/bun/patch/patch.test.ts +++ b/test/js/bun/patch/patch.test.ts @@ -1,9 +1,9 @@ import { $ } from "bun"; -import { describe, test, expect, it } from "bun:test"; import { patchInternals } from "bun:internal-for-testing"; +import { describe, expect, test } from "bun:test"; +import fs from "fs/promises"; import { tempDirWithFiles as __tempDirWithFiles } from "harness"; import { join as __join } from "node:path"; -import fs from "fs/promises"; const { parse, apply, makeDiff } = patchInternals; const makeDiffJs = async (aFolder: string, bFolder: string, cwd: string): Promise => { diff --git a/test/js/bun/plugin/plugins.test.ts b/test/js/bun/plugin/plugins.test.ts index 50da7df730bc2..1b2013c534002 100644 --- a/test/js/bun/plugin/plugins.test.ts +++ b/test/js/bun/plugin/plugins.test.ts @@ -1,7 +1,7 @@ /// import { plugin } from "bun"; import { describe, expect, it } from "bun:test"; -import { resolve, dirname } from "path"; +import { resolve } from "path"; declare global { var failingObject: any; diff --git a/test/js/bun/resolve/esModule-annotation.test.js b/test/js/bun/resolve/esModule-annotation.test.js index 33c84be5d52a5..bb7b8a6861684 100644 --- a/test/js/bun/resolve/esModule-annotation.test.js +++ b/test/js/bun/resolve/esModule-annotation.test.js @@ -1,4 +1,4 @@ -import { test, expect, describe } from "bun:test"; +import { describe, expect, test } from "bun:test"; import * as WithTypeModuleExportEsModuleAnnotationMissingDefault from "./with-type-module/export-esModule-annotation-empty.cjs"; import * as WithTypeModuleExportEsModuleAnnotationNoDefault from "./with-type-module/export-esModule-annotation-no-default.cjs"; import * as WithTypeModuleExportEsModuleAnnotation from "./with-type-module/export-esModule-annotation.cjs"; diff --git a/test/js/bun/resolve/import-custom-condition.test.ts b/test/js/bun/resolve/import-custom-condition.test.ts index 77b4110980458..fa753b5a0b54a 100644 --- a/test/js/bun/resolve/import-custom-condition.test.ts +++ b/test/js/bun/resolve/import-custom-condition.test.ts @@ -1,18 +1,20 @@ -import { it, expect, beforeAll } from "bun:test"; +import { beforeAll, expect, it } from "bun:test"; import { writeFileSync } from "fs"; -import { bunExe, bunEnv, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; let dir: string; beforeAll(() => { dir = tempDirWithFiles("customcondition", { "./node_modules/custom/index.js": "export const foo = 1;", + "./node_modules/custom/browser.js": "export const foo = 2;", "./node_modules/custom/not_allow.js": "throw new Error('should not be imported')", "./node_modules/custom/package.json": JSON.stringify({ name: "custom", exports: { "./test": { first: "./index.js", + browser: "./browser.js", default: "./not_allow.js", }, }, @@ -54,6 +56,7 @@ beforeAll(() => { }); writeFileSync(`${dir}/test.js`, `import {foo} from 'custom/test';\nconsole.log(foo);`); + writeFileSync(`${dir}/test.test.js`, `import {foo} from 'custom/test';\nconsole.log(foo);`); writeFileSync(`${dir}/test.cjs`, `const {foo} = require("custom2/test");\nconsole.log(foo);`); writeFileSync( `${dir}/multiple-conditions.js`, @@ -87,6 +90,39 @@ it("custom condition 'import' in package.json resolves", async () => { expect(stdout.toString("utf8")).toBe("1\n"); }); +it("custom condition 'import' in package.json resolves with browser condition", async () => { + const { exitCode, stdout } = Bun.spawnSync({ + cmd: [bunExe(), "--conditions=browser", `${dir}/test.js`], + env: bunEnv, + cwd: import.meta.dir, + }); + + expect(exitCode).toBe(0); + expect(stdout.toString("utf8")).toBe("2\n"); +}); + +it("custom condition 'import' in package.json resolves in bun test", async () => { + const { exitCode, stdout } = Bun.spawnSync({ + cmd: [bunExe(), "test", "--conditions=first", `${dir}/test.test.js`], + env: bunEnv, + cwd: import.meta.dir, + }); + + expect(exitCode).toBe(0); + expect(stdout.toString("utf8")).toBe("1\n"); +}); + +it("custom condition 'import' in package.json resolves in bun test with browser condition", async () => { + const { exitCode, stdout } = Bun.spawnSync({ + cmd: [bunExe(), "test", "--conditions=browser", `${dir}/test.test.js`], + env: bunEnv, + cwd: import.meta.dir, + }); + + expect(exitCode).toBe(0); + expect(stdout.toString("utf8")).toBe("2\n"); +}); + it("custom condition 'require' in package.json resolves", async () => { const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), "--conditions=first", `${dir}/test.cjs`], diff --git a/test/js/bun/resolve/import-meta.test.js b/test/js/bun/resolve/import-meta.test.js index 10775c55ef2d0..668e3ca8e9687 100644 --- a/test/js/bun/resolve/import-meta.test.js +++ b/test/js/bun/resolve/import-meta.test.js @@ -1,11 +1,11 @@ import { spawnSync } from "bun"; -import { describe, expect, it } from "bun:test"; +import { expect, it } from "bun:test"; import { bunEnv, bunExe, ospath } from "harness"; import { mkdirSync, rmSync, writeFileSync } from "node:fs"; import * as Module from "node:module"; +import { tmpdir } from "node:os"; import { join, sep } from "node:path"; import sync from "./require-json.json"; -import { tmpdir } from "node:os"; const { path, dir, dirname, filename } = import.meta; @@ -190,7 +190,7 @@ it("import.meta.require (javascript, live bindings)", () => { }); it("import.meta.dir", () => { - expect(dir).toEndWith(ospath("/bun/test/js/bun/resolve")); + expect(dir).toEndWith(ospath("/test/js/bun/resolve")); }); it("import.meta.dirname", () => { @@ -202,7 +202,7 @@ it("import.meta.filename", () => { }); it("import.meta.path", () => { - expect(path).toEndWith(ospath("/bun/test/js/bun/resolve/import-meta.test.js")); + expect(path).toEndWith(ospath("/test/js/bun/resolve/import-meta.test.js")); }); it('require("bun") works', () => { diff --git a/test/js/bun/resolve/import-require-tla.js b/test/js/bun/resolve/import-require-tla.js index 732fc34dd6621..55cad59d22b6d 100644 --- a/test/js/bun/resolve/import-require-tla.js +++ b/test/js/bun/resolve/import-require-tla.js @@ -1,3 +1,4 @@ +// organize-imports-ignore const Fs = require("fs"); const DirEnt = Fs.Dirent; diff --git a/test/js/bun/resolve/import.live.rexport-require.js b/test/js/bun/resolve/import.live.rexport-require.js index 10c993e089029..fbef4687db202 100644 --- a/test/js/bun/resolve/import.live.rexport-require.js +++ b/test/js/bun/resolve/import.live.rexport-require.js @@ -1 +1,2 @@ +// organize-imports-ignore export const Namespace = import.meta.require("./import.live.decl.js"); diff --git a/test/js/bun/resolve/import.live.rexport.js b/test/js/bun/resolve/import.live.rexport.js index 6709c34660306..0ad3868c36a14 100644 --- a/test/js/bun/resolve/import.live.rexport.js +++ b/test/js/bun/resolve/import.live.rexport.js @@ -1,2 +1,3 @@ +// organize-imports-ignore export { foo, setFoo } from "./import.live.decl.js"; import { foo as bar } from "./import.live.decl.js"; diff --git a/test/js/bun/resolve/jsonc.test.ts b/test/js/bun/resolve/jsonc.test.ts new file mode 100644 index 0000000000000..d2571644c8d6d --- /dev/null +++ b/test/js/bun/resolve/jsonc.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from "bun:test"; +import { tempDirWithFiles } from "harness"; +import { join } from "path"; +test("empty jsonc - package.json", async () => { + const dir = tempDirWithFiles("jsonc", { + "package.json": ``, + "index.ts": ` + import pkg from './package.json'; + if (JSON.stringify(pkg) !== '{}') throw new Error('package.json should be empty'); + `, + }); + expect([join(dir, "index.ts")]).toRun(); +}); + +test("empty jsonc - tsconfig.json", async () => { + const dir = tempDirWithFiles("jsonc", { + "tsconfig.json": ``, + "index.ts": ` + import tsconfig from './tsconfig.json'; + if (JSON.stringify(tsconfig) !== '{}') throw new Error('tsconfig.json should be empty'); + `, + }); + expect([join(dir, "index.ts")]).toRun(); +}); diff --git a/test/js/bun/resolve/load-file-loader-a-lot.test.ts b/test/js/bun/resolve/load-file-loader-a-lot.test.ts index a640f34166728..55beab120bc93 100644 --- a/test/js/bun/resolve/load-file-loader-a-lot.test.ts +++ b/test/js/bun/resolve/load-file-loader-a-lot.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test"; +import { test } from "bun:test"; test("a file: loader file can be imported 10,000 times", async () => { const prev = Bun.unsafe.gcAggressionLevel(); diff --git a/test/js/bun/resolve/load-same-js-file-a-lot.test.ts b/test/js/bun/resolve/load-same-js-file-a-lot.test.ts index 9f9d864373f0d..face20c5c0614 100644 --- a/test/js/bun/resolve/load-same-js-file-a-lot.test.ts +++ b/test/js/bun/resolve/load-same-js-file-a-lot.test.ts @@ -1,5 +1,4 @@ -import { test, expect } from "bun:test"; -import { withoutAggressiveGC } from "harness"; +import { expect, test } from "bun:test"; test("load the same file 10,000 times", async () => { const meta = { diff --git a/test/js/bun/resolve/non-english-import.test.js b/test/js/bun/resolve/non-english-import.test.js index 9e58869bcf5d3..cbe01da23ae73 100644 --- a/test/js/bun/resolve/non-english-import.test.js +++ b/test/js/bun/resolve/non-english-import.test.js @@ -1,6 +1,6 @@ // We do not make these files imports in the codebase because non-ascii file paths can cause issues with git // Instead, we put them into a temporary directory and run them from there -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; import { mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/test/js/bun/resolve/resolve-error.test.ts b/test/js/bun/resolve/resolve-error.test.ts index 124546a30c903..513269e67f685 100644 --- a/test/js/bun/resolve/resolve-error.test.ts +++ b/test/js/bun/resolve/resolve-error.test.ts @@ -1,4 +1,4 @@ -import { expect, it, describe } from "bun:test"; +import { describe, expect, it } from "bun:test"; describe("ResolveMessage", () => { it("position object does not segfault", async () => { diff --git a/test/js/bun/resolve/resolve-test.js b/test/js/bun/resolve/resolve-test.js index 4bc86c1786b73..a5a8f94a172fd 100644 --- a/test/js/bun/resolve/resolve-test.js +++ b/test/js/bun/resolve/resolve-test.js @@ -1,5 +1,4 @@ -import { it, expect } from "bun:test"; -import { ospath } from "harness"; +import { expect, it } from "bun:test"; import { join, resolve } from "path"; function resolveFrom(from) { diff --git a/test/js/bun/resolve/resolve-ts.test.ts b/test/js/bun/resolve/resolve-ts.test.ts index 6c130a59ebbdc..f0e121307bd35 100644 --- a/test/js/bun/resolve/resolve-ts.test.ts +++ b/test/js/bun/resolve/resolve-ts.test.ts @@ -1,4 +1,4 @@ -import { test, expect, describe } from "bun:test"; +import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; import * as Chooses from "./chooses-ts"; diff --git a/test/js/bun/resolve/resolve.test.ts b/test/js/bun/resolve/resolve.test.ts index 1a46b049cdabb..6491d75c2ab3f 100644 --- a/test/js/bun/resolve/resolve.test.ts +++ b/test/js/bun/resolve/resolve.test.ts @@ -1,9 +1,8 @@ -import { it, expect } from "bun:test"; -import { mkdirSync, writeFileSync } from "fs"; -import { join } from "path"; -import { bunExe, bunEnv, tempDirWithFiles } from "harness"; import { pathToFileURL } from "bun"; -import { sep } from "path"; +import { expect, it } from "bun:test"; +import { mkdirSync, writeFileSync } from "fs"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { join, sep } from "path"; it("spawn test file", () => { writePackageJSONImportsFixture(); diff --git a/test/js/bun/resolve/second-child.mjs b/test/js/bun/resolve/second-child.mjs index 5fb06ed452815..6f99ac5fb1689 100644 --- a/test/js/bun/resolve/second-child.mjs +++ b/test/js/bun/resolve/second-child.mjs @@ -1,4 +1,4 @@ -import { start, end } from "./startEnd.mjs"; +import { end, start } from "./startEnd.mjs"; start("Second (nested import)"); diff --git a/test/js/bun/resolve/second.mjs b/test/js/bun/resolve/second.mjs index 888eb11b9f1fa..479d652d1470a 100644 --- a/test/js/bun/resolve/second.mjs +++ b/test/js/bun/resolve/second.mjs @@ -1,4 +1,4 @@ -import { start, end } from "./startEnd.mjs"; +import { end, start } from "./startEnd.mjs"; start("Second"); diff --git a/test/js/node/fs/test.txt b/test/js/bun/resolve/toml/toml-empty.toml similarity index 100% rename from test/js/node/fs/test.txt rename to test/js/bun/resolve/toml/toml-empty.toml diff --git a/test/js/bun/resolve/toml/toml-fixture.toml b/test/js/bun/resolve/toml/toml-fixture.toml index 5b7df33af2b5a..bed28762b3e91 100644 --- a/test/js/bun/resolve/toml/toml-fixture.toml +++ b/test/js/bun/resolve/toml/toml-fixture.toml @@ -1,42 +1,42 @@ - -framework = "next" -origin = "http://localhost:5000" -inline.array = [1234, 4, 5, 6] - - -[macros] -react-relay = { "graphql" = "node_modules/bun-macro-relay/bun-macro-relay.tsx" } - -[install.scopes] -"@mybigcompany2" = { "token" = "123456", "url" = "https://registry.mybigcompany.com" } -"@mybigcompany3" = { "token" = "123456", "url" = "https://registry.mybigcompany.com", "three" = 4 } - - -[install.scopes."@mybigcompany"] -token = "123456" -url = "https://registry.mybigcompany.com" - -[bundle.packages] -"@emotion/react" = true - -[install.cache] -dir = "C:\\Windows\\System32" -dir2 = "C:\\Windows\\System32\\🏳️‍🌈" - -[dev] -foo = 123 -"foo.bar" = "baz" -"abba.baba" = "baba" -dabba = -123 -doo = 123.456 -one.two.three = 4 - -[[array]] -entry_one = "one" -entry_two = "two" - -[[array]] -entry_one = "three" - -[[array.nested]] -entry_one = "four" + +framework = "next" +origin = "http://localhost:5000" +inline.array = [1234, 4, 5, 6] + + +[macros] +react-relay = { "graphql" = "node_modules/bun-macro-relay/bun-macro-relay.tsx" } + +[install.scopes] +"@mybigcompany2" = { "token" = "123456", "url" = "https://registry.mybigcompany.com" } +"@mybigcompany3" = { "token" = "123456", "url" = "https://registry.mybigcompany.com", "three" = 4 } + + +[install.scopes."@mybigcompany"] +token = "123456" +url = "https://registry.mybigcompany.com" + +[bundle.packages] +"@emotion/react" = true + +[install.cache] +dir = "C:\\Windows\\System32" +dir2 = "C:\\Windows\\System32\\🏳️‍🌈" + +[dev] +foo = 123 +"foo.bar" = "baz" +"abba.baba" = "baba" +dabba = -123 +doo = 123.456 +one.two.three = 4 + +[[array]] +entry_one = "one" +entry_two = "two" + +[[array]] +entry_one = "three" + +[[array.nested]] +entry_one = "four" diff --git a/test/js/bun/resolve/toml/toml.test.js b/test/js/bun/resolve/toml/toml.test.js index ce5cb2923b351..d9fcaaaaf0bfd 100644 --- a/test/js/bun/resolve/toml/toml.test.js +++ b/test/js/bun/resolve/toml/toml.test.js @@ -1,4 +1,5 @@ import { expect, it } from "bun:test"; +import emptyToml from "./toml-empty.toml"; import tomlFromCustomTypeAttribute from "./toml-fixture.toml.txt" with { type: "toml" }; function checkToml(toml) { @@ -35,3 +36,7 @@ it("via dynamic import with type attribute", async () => { const toml = (await import("./toml-fixture.toml.txt", { with: { type: "toml" } })).default; checkToml(toml); }); + +it("empty via import statement", () => { + expect(emptyToml).toEqual({}); +}); diff --git a/test/js/bun/shell/brace.test.ts b/test/js/bun/shell/brace.test.ts index 97a1c6429e58b..93788ba800f62 100644 --- a/test/js/bun/shell/brace.test.ts +++ b/test/js/bun/shell/brace.test.ts @@ -1,5 +1,5 @@ import { $ } from "bun"; -import { test, expect, describe } from "bun:test"; +import { describe, expect, test } from "bun:test"; describe("$.braces", () => { test("no-op", () => { diff --git a/test/js/bun/shell/bunshell-default.test.ts b/test/js/bun/shell/bunshell-default.test.ts index 6266b603e86c3..6c5b6b9ce9f81 100644 --- a/test/js/bun/shell/bunshell-default.test.ts +++ b/test/js/bun/shell/bunshell-default.test.ts @@ -1,6 +1,5 @@ -import { $ } from "bun"; -import { bunExe, createTestBuilder } from "./test_builder"; import { bunEnv } from "harness"; +import { bunExe, createTestBuilder } from "./test_builder"; const TestBuilder = createTestBuilder(import.meta.path); test("default throw on command failure", async () => { diff --git a/test/js/bun/shell/bunshell-file.test.ts b/test/js/bun/shell/bunshell-file.test.ts index 294259298b77d..0a0c264e09474 100644 --- a/test/js/bun/shell/bunshell-file.test.ts +++ b/test/js/bun/shell/bunshell-file.test.ts @@ -1,5 +1,5 @@ import { $ } from "bun"; -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; test("$ with Bun.file prints the path", async () => { expect(await $`echo ${Bun.file(import.meta.path)}`.text()).toBe(`${import.meta.path}\n`); diff --git a/test/js/bun/shell/bunshell-instance.test.ts b/test/js/bun/shell/bunshell-instance.test.ts index 12f7ee2f76ac9..553e7d86dbe04 100644 --- a/test/js/bun/shell/bunshell-instance.test.ts +++ b/test/js/bun/shell/bunshell-instance.test.ts @@ -1,4 +1,4 @@ -import { test, expect, describe } from "bun:test"; +import { describe, expect, test } from "bun:test"; import { $ } from "bun"; diff --git a/test/js/bun/shell/bunshell.test.ts b/test/js/bun/shell/bunshell.test.ts index bd9e174f277b0..31b59ffa41ac9 100644 --- a/test/js/bun/shell/bunshell.test.ts +++ b/test/js/bun/shell/bunshell.test.ts @@ -7,7 +7,7 @@ import { $ } from "bun"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { mkdir, rm, stat } from "fs/promises"; -import { bunEnv as __bunEnv, bunExe, isWindows, runWithErrorPromise, tempDirWithFiles, tmpdirSync } from "harness"; +import { bunExe, isWindows, runWithErrorPromise, tempDirWithFiles, tmpdirSync } from "harness"; import { join, sep } from "path"; import { createTestBuilder, sortedShellOutput } from "./util"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/basename.test.ts b/test/js/bun/shell/commands/basename.test.ts index 5ffe4f3ec7d7a..3f4b95e5071c8 100644 --- a/test/js/bun/shell/commands/basename.test.ts +++ b/test/js/bun/shell/commands/basename.test.ts @@ -1,5 +1,4 @@ -import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; +import { describe } from "bun:test"; import { createTestBuilder } from "../test_builder"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/cp.test.ts b/test/js/bun/shell/commands/cp.test.ts index 2c4a3e8429f6f..500eb11bc7d72 100644 --- a/test/js/bun/shell/commands/cp.test.ts +++ b/test/js/bun/shell/commands/cp.test.ts @@ -1,10 +1,9 @@ import { $ } from "bun"; +import { shellInternals } from "bun:internal-for-testing"; +import { describe, expect } from "bun:test"; +import { tempDirWithFiles } from "harness"; import { bunExe, createTestBuilder } from "../test_builder"; -import { beforeAll, describe, test, expect, beforeEach } from "bun:test"; import { sortedShellOutput } from "../util"; -import { tempDirWithFiles } from "harness"; -import fs from "fs"; -import { shellInternals } from "bun:internal-for-testing"; const { builtinDisabled } = shellInternals; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/dirname.test.ts b/test/js/bun/shell/commands/dirname.test.ts index 4a2fe0d3217b8..e10293795a640 100644 --- a/test/js/bun/shell/commands/dirname.test.ts +++ b/test/js/bun/shell/commands/dirname.test.ts @@ -1,5 +1,4 @@ -import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; +import { describe } from "bun:test"; import { createTestBuilder } from "../test_builder"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/exit.test.ts b/test/js/bun/shell/commands/exit.test.ts index 5e757a9c80a4d..5a3239d0f49dd 100644 --- a/test/js/bun/shell/commands/exit.test.ts +++ b/test/js/bun/shell/commands/exit.test.ts @@ -1,9 +1,6 @@ -import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; +import { describe } from "bun:test"; import { createTestBuilder } from "../test_builder"; const TestBuilder = createTestBuilder(import.meta.path); -import { sortedShellOutput } from "../util"; -import { join } from "path"; describe("exit", async () => { TestBuilder.command`exit`.exitCode(0).runAsTest("works"); diff --git a/test/js/bun/shell/commands/false.test.ts b/test/js/bun/shell/commands/false.test.ts index 785a495ee99c2..8513fb044d097 100644 --- a/test/js/bun/shell/commands/false.test.ts +++ b/test/js/bun/shell/commands/false.test.ts @@ -1,5 +1,4 @@ -import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; +import { describe } from "bun:test"; import { createTestBuilder } from "../test_builder"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/mv.test.ts b/test/js/bun/shell/commands/mv.test.ts index 0a4fdec5201b5..1a7236788f0b6 100644 --- a/test/js/bun/shell/commands/mv.test.ts +++ b/test/js/bun/shell/commands/mv.test.ts @@ -1,9 +1,9 @@ import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; +import { describe, expect } from "bun:test"; +import { join } from "path"; import { createTestBuilder } from "../test_builder"; -const TestBuilder = createTestBuilder(import.meta.path); import { sortedShellOutput } from "../util"; -import { join } from "path"; +const TestBuilder = createTestBuilder(import.meta.path); $.nothrow(); diff --git a/test/js/bun/shell/commands/rm.test.ts b/test/js/bun/shell/commands/rm.test.ts index df639939693ce..114b4a9ecefe5 100644 --- a/test/js/bun/shell/commands/rm.test.ts +++ b/test/js/bun/shell/commands/rm.test.ts @@ -4,12 +4,11 @@ * * This code is licensed under the MIT License: https://opensource.org/licenses/MIT */ -import { tempDirWithFiles } from "harness"; -import { describe, test, afterAll, beforeAll, expect, setDefaultTimeout } from "bun:test"; import { $ } from "bun"; -import path from "path"; +import { beforeAll, describe, expect, setDefaultTimeout, test } from "bun:test"; +import { tempDirWithFiles } from "harness"; import { mkdirSync, writeFileSync } from "node:fs"; -import { ShellOutput } from "bun"; +import path from "path"; import { createTestBuilder, sortedShellOutput } from "../util"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/seq.test.ts b/test/js/bun/shell/commands/seq.test.ts index f728425123fa1..87f11b15e5eae 100644 --- a/test/js/bun/shell/commands/seq.test.ts +++ b/test/js/bun/shell/commands/seq.test.ts @@ -1,4 +1,3 @@ -import { $ } from "bun"; import { describe } from "bun:test"; import { createTestBuilder } from "../test_builder"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/true.test.ts b/test/js/bun/shell/commands/true.test.ts index 4dc491713e63c..a66fa9b27d9b5 100644 --- a/test/js/bun/shell/commands/true.test.ts +++ b/test/js/bun/shell/commands/true.test.ts @@ -1,5 +1,4 @@ -import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; +import { describe } from "bun:test"; import { createTestBuilder } from "../test_builder"; const TestBuilder = createTestBuilder(import.meta.path); diff --git a/test/js/bun/shell/commands/which.test.ts b/test/js/bun/shell/commands/which.test.ts index c613573a7ba4a..720493685251a 100644 --- a/test/js/bun/shell/commands/which.test.ts +++ b/test/js/bun/shell/commands/which.test.ts @@ -1,5 +1,5 @@ import { $ } from "bun"; -import { test, expect } from "bun:test"; +import { expect, test } from "bun:test"; test("which rlly long", async () => { const longstr = "a".repeat(100000); diff --git a/test/js/bun/shell/commands/yes.test.ts b/test/js/bun/shell/commands/yes.test.ts index 69e31889d9111..fb301a5b37b18 100644 --- a/test/js/bun/shell/commands/yes.test.ts +++ b/test/js/bun/shell/commands/yes.test.ts @@ -1,5 +1,5 @@ -import { $, ShellOutput } from "bun"; -import { describe, test, expect, beforeEach } from "bun:test"; +import { $ } from "bun"; +import { describe, expect, test } from "bun:test"; $.throws(false); diff --git a/test/js/bun/shell/env.positionals.test.ts b/test/js/bun/shell/env.positionals.test.ts index 93846badab3af..f122aa34157bb 100644 --- a/test/js/bun/shell/env.positionals.test.ts +++ b/test/js/bun/shell/env.positionals.test.ts @@ -1,9 +1,9 @@ import { $, spawn } from "bun"; -import { describe, test, expect } from "bun:test"; -import { createTestBuilder } from "./test_builder"; -const TestBuilder = createTestBuilder(import.meta.path); +import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; import * as path from "node:path"; +import { createTestBuilder } from "./test_builder"; +const TestBuilder = createTestBuilder(import.meta.path); $.nothrow(); describe("$ argv", async () => { diff --git a/test/js/bun/shell/exec.test.ts b/test/js/bun/shell/exec.test.ts index 2fb108ddcf8a6..be3a333c3186b 100644 --- a/test/js/bun/shell/exec.test.ts +++ b/test/js/bun/shell/exec.test.ts @@ -1,9 +1,9 @@ import { $ } from "bun"; -import { describe, test, expect } from "bun:test"; -import { createTestBuilder } from "./test_builder"; -const TestBuilder = createTestBuilder(import.meta.path); +import { describe, expect, test } from "bun:test"; import { bunEnv, tmpdirSync } from "harness"; import { join } from "path"; +import { createTestBuilder } from "./test_builder"; +const TestBuilder = createTestBuilder(import.meta.path); const BUN = process.argv0; @@ -19,7 +19,7 @@ describe("bun exec", () => { TestBuilder.command`${BUN} exec` .env(bunEnv) .stdout( - 'Usage: bun exec